diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index f9f5fbe15..73eb8d9a0 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "modernuoschemagenerator": { - "version": "4.0.0", + "version": "2.12.18", "commands": [ "ModernUOSchemaGenerator" ] diff --git a/.cursorrules b/.cursorrules deleted file mode 100644 index 2fe47ff90..000000000 --- a/.cursorrules +++ /dev/null @@ -1 +0,0 @@ -Read and follow all instructions in CLAUDE.md in this repository's root. diff --git a/.github/COPILOT-INSTRUCTIONS.md b/.github/COPILOT-INSTRUCTIONS.md deleted file mode 100644 index 2fe47ff90..000000000 --- a/.github/COPILOT-INSTRUCTIONS.md +++ /dev/null @@ -1 +0,0 @@ -Read and follow all instructions in CLAUDE.md in this repository's root. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 393946bf7..e4ca311d0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,7 +9,3 @@ updates: directory: "/" # Location of package manifests schedule: interval: "daily" - - package-ecosystem: "github-actions" - directory: "/" # Watches .github/workflows/** - schedule: - interval: "weekly" diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index eb74c39a4..cbc6c6a13 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -3,179 +3,94 @@ name: Build on: push: branches: [main] - paths: - - '.config/dotnet-tools.json' - - 'Projects/**' - - 'Directory.Build.props' - - 'global.json' - - 'version.json' - - '*.slnx' pull_request: branches: [main] - paths: - - '.config/dotnet-tools.json' - - 'Projects/**' - - 'Directory.Build.props' - - 'global.json' - - 'version.json' - - '*.slnx' jobs: build-macos: runs-on: ${{ matrix.os }} - # A hung run otherwise bills the full 360-minute default before GitHub kills it. - timeout-minutes: 30 name: Build (${{ matrix.name }}) strategy: fail-fast: false matrix: include: - - os: macos-15 - name: MacOS 15 - - os: macos-26 - name: MacOS 26 + - os: macos-13 + name: MacOS 13 + - os: macos-14 + name: MacOS 14 steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@v4 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - - name: Install .NET - uses: actions/setup-dotnet@v6 + - name: Install .NET 9 + uses: actions/setup-dotnet@v4 with: global-json-file: global.json - name: Install Prerequisites run: | brew update - brew install icu4c libdeflate argon2 + brew install icu4c libdeflate zstd argon2 - name: Set Library Path - run: echo "DYLD_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV + run: echo "DYLD_LIBRARY_PATH=/opt/homebrew/lib:\$DYLD_LIBRARY_PATH" >> $GITHUB_ENV - name: Build - run: dotnet run --project Projects/BuildTool -- --config Release --skip-prereqs + run: ./publish.cmd Release - name: Migration Changes run: git diff --exit-code ./**/Migrations/*.v*.json - name: Test - # blame-hang kills a stuck test host after 10 minutes and reports the in-flight - # tests plus a process dump instead of hanging until the job timeout. - run: | - dotnet test --logger trx --results-directory ./TestResults --blame-hang --blame-hang-timeout 10m --blame-hang-dump-type full - if [ -z "$(find ./TestResults -name '*.trx' 2>/dev/null)" ]; then - echo "::error::No test result files were produced - no test projects ran. Failing to avoid masking failures." - exit 1 - fi - - name: Upload test results on failure - if: failure() - uses: actions/upload-artifact@v7 - with: - name: TestResults-${{ matrix.name }} - path: ./TestResults - if-no-files-found: ignore + run: dotnet test --no-restore build-linux: runs-on: ubuntu-latest - # A hung run otherwise bills the full 360-minute default before GitHub kills it. - timeout-minutes: 30 - container: - image: ${{ matrix.container }} - options: --security-opt seccomp=unconfined + container: ${{ matrix.container }} name: Build (${{ matrix.name }}) strategy: fail-fast: false matrix: include: - - container: ubuntu:26.04 - name: Ubuntu 26 - packageManager: apt - container: ubuntu:noble name: Ubuntu 24 packageManager: apt - container: ubuntu:jammy name: Ubuntu 22 packageManager: apt - - container: debian:trixie - name: Debian 13 + - container: ubuntu:focal + name: Ubuntu 20 packageManager: apt - container: debian:bookworm name: Debian 12 packageManager: apt - - container: fedora:44 - name: Fedora 44 + - container: debian:bullseye + name: Debian 11 + packageManager: apt + - container: fedora:39 + name: Fedora 39 + packageManager: dnf + - container: fedora:40 + name: Fedora 40 packageManager: dnf - container: quay.io/centos/centos:stream9 name: CentOS 9 Stream packageManager: dnf - epel: true - - container: quay.io/centos/centos:stream10 - name: CentOS 10 Stream - packageManager: dnf - epel: true - - container: almalinux:10 - name: AlmaLinux 10 - packageManager: dnf - epel: true steps: - # Enable CRB before EPEL, per the EPEL quickstart. epel-next is not installed: - # none of the prerequisites need it, EPEL 10 does not have it, and it is one more - # mirrorlist to fetch. - - name: Enable EPEL and CRB - run: | - dnf upgrade --refresh -y - dnf install -y dnf-plugins-core - dnf config-manager --set-enabled crb - dnf install -y epel-release - if: ${{ matrix.epel }} - # Runtime packages only, deliberately. Installing the -dev packages here would add the - # unversioned .so symlink and mask the very thing the binding packages now probe for, so a - # regression in versioned-SONAME resolution would sail through CI. + - name: Enable EPEL + run: dnf upgrade --refresh -y && dnf install -y epel-release epel-next-release + if: ${{ startsWith(matrix.name, 'CentOS') }} - name: Install Prerequisites using dnf - run: dnf makecache --refresh && dnf install -y findutils libicu libdeflate libargon2 tzdata + run: dnf makecache --refresh && dnf install -y findutils libicu libdeflate-devel zstd libargon2-devel if: ${{ matrix.packageManager == 'dnf' }} - # ICU's runtime package carries the ABI version in its name (libicu70 on jammy, libicu76 on - # trixie) and has no stable alias, so match it by pattern. libicu-dev was the old way to stay - # version-independent, but it drags in the unversioned symlink and defeats the check below. - name: Install Prerequisites using apt - run: apt-get update -y && apt-get install -y curl '^libicu[0-9]+$' libdeflate0 libargon2-1 tzdata + run: apt-get update -y && apt-get install -y curl libicu-dev libdeflate-dev zstd libargon2-dev if: ${{ matrix.packageManager == 'apt' }} - # Versioned-SONAME resolution is only under test while the unversioned symlink is absent. If a - # base image or a package ever starts shipping it, every probe would succeed on the first try - # and a regression in the fallback would sail through CI, so fail loudly instead of silently - # testing nothing. - - name: Assert the unversioned .so symlinks are absent - run: | - found="" - for lib in libicuuc libicui18n libdeflate libargon2; do - hit=$(ls /usr/lib/*/"$lib".so /usr/lib64/"$lib".so 2>/dev/null || true) - if [ -n "$hit" ]; then - found="$found $hit" - fi - done - if [ -n "$found" ]; then - echo "::error::Unversioned symlinks present, so CI is no longer exercising versioned SONAME resolution:$found" - exit 1 - fi - echo "No unversioned symlinks present; versioned SONAME resolution is under test." - - uses: actions/checkout@v7 + - uses: actions/checkout@v4 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - - name: Install .NET - uses: actions/setup-dotnet@v6 + - name: Install .NET 9 + uses: actions/setup-dotnet@v4 with: global-json-file: global.json - name: Build - run: dotnet run --project Projects/BuildTool -- --config Release --skip-prereqs + run: ./publish.cmd Release - name: Test - # blame-hang kills a stuck test host after 10 minutes and reports the in-flight - # tests plus a process dump instead of hanging until the job timeout. - run: | - dotnet test --logger trx --results-directory ./TestResults --blame-hang --blame-hang-timeout 10m --blame-hang-dump-type full - if [ -z "$(find ./TestResults -name '*.trx' 2>/dev/null)" ]; then - echo "::error::No test result files were produced - no test projects ran. Failing to avoid masking failures." - exit 1 - fi - - name: Upload test results on failure - if: failure() - uses: actions/upload-artifact@v7 - with: - name: TestResults-${{ matrix.name }} - path: ./TestResults - if-no-files-found: ignore + run: dotnet test --no-restore diff --git a/.github/workflows/build-tool-release.yml b/.github/workflows/build-tool-release.yml deleted file mode 100644 index 696a825dc..000000000 --- a/.github/workflows/build-tool-release.yml +++ /dev/null @@ -1,163 +0,0 @@ -name: Build Tool Release - -on: - push: - branches: [main] - paths: - - 'Projects/BuildTool/**' - workflow_dispatch: - -jobs: - build: - runs-on: ${{ matrix.os }} - name: Build (${{ matrix.rid }}) - strategy: - fail-fast: false - matrix: - include: - - os: windows-latest - rid: win-x64 - artifact: build-tool-win-x64.exe - - os: windows-latest - rid: win-arm64 - artifact: build-tool-win-arm64.exe - - os: macos-15 - rid: osx-arm64 - artifact: build-tool-osx-arm64 - - os: ubuntu-latest - rid: linux-x64 - artifact: build-tool-linux-x64 - - os: ubuntu-24.04-arm - rid: linux-arm64 - artifact: build-tool-linux-arm64 - - steps: - - uses: actions/checkout@v7 - with: - fetch-depth: 0 # Full clone required for Nerdbank.GitVersioning - - - name: Install .NET - uses: actions/setup-dotnet@v6 - with: - global-json-file: global.json - - - name: Publish NativeAOT - run: dotnet publish Projects/BuildTool/BuildTool.csproj -c Release -r ${{ matrix.rid }} -o publish/ - - - name: Rename artifact (Unix) - if: runner.os != 'Windows' - run: mv publish/build-tool publish/${{ matrix.artifact }} - - - name: Rename artifact (Windows) - if: runner.os == 'Windows' - run: mv publish/build-tool.exe publish/${{ matrix.artifact }} - shell: bash - - - name: Upload artifact - uses: actions/upload-artifact@v7 - with: - name: ${{ matrix.artifact }} - path: publish/${{ matrix.artifact }} - - sign: - needs: build - if: github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - name: Sign (${{ matrix.artifact }}) - permissions: - actions: read - contents: read - id-token: write - strategy: - matrix: - include: - - artifact: build-tool-win-x64.exe - - artifact: build-tool-win-arm64.exe - - steps: - - name: Download unsigned artifact - uses: actions/download-artifact@v8 - with: - name: ${{ matrix.artifact }} - path: unsigned/ - - - name: Upload for signing - id: upload-for-signing - uses: actions/upload-artifact@v7 - with: - name: ${{ matrix.artifact }}-unsigned - path: unsigned/${{ matrix.artifact }} - - - name: Submit signing request - id: submit - uses: signpath/github-action-submit-signing-request@v2 - with: - api-token: '${{ secrets.SIGNPATH_API_TOKEN }}' - organization-id: '${{ secrets.SIGNPATH_ORGANIZATION_ID }}' - project-slug: 'modernuo' - signing-policy-slug: 'release-signing' - artifact-configuration-slug: 'build-tool' - github-artifact-id: '${{ steps.upload-for-signing.outputs.artifact-id }}' - wait-for-completion: true - output-artifact-directory: signed/ - - - name: Upload signed artifact - uses: actions/upload-artifact@v7 - with: - name: ${{ matrix.artifact }}-signed - path: signed/${{ matrix.artifact }} - - release: - needs: [build, sign] - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' - permissions: - contents: write - - steps: - - uses: actions/checkout@v7 - - - name: Move tag to current commit - run: | - git tag -f build-tool-latest - git push origin build-tool-latest --force - - - name: Download signed Windows artifacts - uses: actions/download-artifact@v8 - with: - pattern: '*-signed' - path: artifacts/ - merge-multiple: true - - - name: Download macOS artifacts - uses: actions/download-artifact@v8 - with: - pattern: 'build-tool-osx-*' - path: artifacts/ - merge-multiple: true - - - name: Download Linux artifacts - uses: actions/download-artifact@v8 - with: - pattern: 'build-tool-linux-*' - path: artifacts/ - merge-multiple: true - - - name: Generate checksums - run: | - cd artifacts - sha256sum build-tool-* > checksums-sha256.txt - - - name: Create or update release - uses: softprops/action-gh-release@v3 - with: - tag_name: build-tool-latest - name: Build Tool (Latest) - body: | - Latest NativeAOT-compiled build tool binaries. - These are automatically downloaded by `publish.cmd` / `publish.sh`. - prerelease: true - files: | - artifacts/build-tool-* - artifacts/checksums-sha256.txt - make_latest: false diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml new file mode 100644 index 000000000..ae9f3e078 --- /dev/null +++ b/.github/workflows/code_quality.yml @@ -0,0 +1,24 @@ +name: Qodana +on: + workflow_dispatch: + pull_request: + push: + branches: # Specify your branches here + - main # The 'main' branch + +jobs: + qodana: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + checks: write + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit + fetch-depth: 0 # a full history is required for pull request analysis + - name: 'Qodana Scan' + uses: JetBrains/qodana-action@v2024.1 + env: + QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 3c0d182a3..0e8334e59 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -10,26 +10,27 @@ jobs: name: Create Release runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@v4 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - - name: Install .NET - uses: actions/setup-dotnet@v6 + - name: Install .NET 9 + uses: actions/setup-dotnet@v4 with: global-json-file: global.json - - name: Compute version - uses: dotnet/nbgv@v0.5.2 + - name: Install NGBV + uses: dotnet/nbgv@master id: nbgv - - name: Push version tag - run: | - git tag ${{ steps.nbgv.outputs.Version }} - git push origin ${{ steps.nbgv.outputs.Version }} - env: - GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - - name: Create Release - uses: softprops/action-gh-release@v3 + - name: Push git changes + uses: ad-m/github-push-action@master with: + github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + tags: true + - name: Create Release + id: create_release + uses: softprops/action-gh-release@v2 + with: + body: ${{ steps.changelog.outputs.clean_changelog }} tag_name: ${{ steps.nbgv.outputs.Version }} name: ${{ steps.nbgv.outputs.Version }} draft: false diff --git a/.github/workflows/post-release-discord.yml b/.github/workflows/post-release-discord.yml index 2bcf8b2a9..77e4fc02a 100644 --- a/.github/workflows/post-release-discord.yml +++ b/.github/workflows/post-release-discord.yml @@ -9,8 +9,12 @@ jobs: post-release-discord: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # avoid shallow clone so nbgv can do its work. + token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: Post Release on Discord - uses: SethCohen/github-releases-to-discord@v1.20.0 + uses: SethCohen/github-releases-to-discord@v1.15.0 with: webhook_url: ${{ secrets.WEBHOOK_URL }} username: "Release Changelog" diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml new file mode 100644 index 000000000..3595a112a --- /dev/null +++ b/.github/workflows/update-docs.yml @@ -0,0 +1,27 @@ +name: Updates Docs + +on: + repository_dispatch: + types: [docs] + workflow_dispatch: + +jobs: + update-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - run: pip install mkdocs-material + - run: mkdocs gh-deploy --force + - name: Extract commands web file + run: | + 7z x ./docs/commands/commands.7z -o./docs/commands + rm ./docs/commands/commands.7z + - name: Deploy commands file + uses: JamesIves/github-pages-deploy-action@v4 + with: + folder: ./docs/commands + clean: false + force: false diff --git a/.gitignore b/.gitignore index d5b26e268..d4c5e3b5f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,30 +9,16 @@ /Distribution/bsdtar /Distribution/Configuration/antimacro.json /Distribution/Configuration/assistants.json -/Distribution/Configuration/auto-denylist.json -/Distribution/Configuration/bans.json -/Distribution/Configuration/blocklist.json -/Distribution/Configuration/crowdsec.json /Distribution/Configuration/expansion.json -/Distribution/Configuration/firewall.json -/Distribution/Configuration/ip-allowlist*.txt -/Distribution/Configuration/ip-allowlist*.txt.tmp -/Distribution/Configuration/ip-blocklist.txt -/Distribution/Configuration/ip-blocklist.txt.tmp -/Distribution/Configuration/login-allowlist.json -/Distribution/Configuration/login-allowlist.txt -/Distribution/Configuration/login-allowlist.txt.tmp /Distribution/Configuration/modernuo.json /Distribution/Configuration/email-settings.json /Distribution/Configuration/throttles.json /Distribution/Configuration/tot.json -/Distribution/Data/Pathfinding /Distribution/Logs /Distribution/Archives /Distribution/Backups /Distribution/Saves /Distribution/docs -/docs/ /Distribution/temp /Distribution/*.dylib /Distribution/*.so @@ -53,14 +39,8 @@ /.idea /.vs /.vscode -/.claude .DS_Store /packages/* /Distribution/Configuration/server-access.json - -# BuildTool native binaries (downloaded from GitHub Releases). -# Ignore everything under tools/ except the operator scripts checked in below. -/tools/* -!/tools/Export-IpBlocklist.ps1 diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 2fe47ff90..000000000 --- a/AGENTS.md +++ /dev/null @@ -1 +0,0 @@ -Read and follow all instructions in CLAUDE.md in this repository's root. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index c77d39e19..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,117 +0,0 @@ -# ModernUO - -.NET 10 Ultima Online server emulator. Single-threaded game loop. All game logic runs on one thread. - -- **Server engine**: `Projects/Server/` — do NOT modify without explicit request -- **Game content**: `Projects/UOContent/` — primary editing target -- **Build**: `dotnet build` from repo root - -## Code Audit Rules - -Apply these when writing or reviewing `.cs` files under `Projects/`. - -1. **LINQ** — Tier 1 (zero-cost patterns) free on hot paths; Tier 2 (low overhead) OK on warm paths; Tier 3 (allocating) forbidden on hot paths → `dev-docs/code-standards.md` -2. **No `Console.WriteLine`** — use `LogFactory.GetLogger(typeof(MyClass))` → `logger.Information(...)` (requires `using Server.Logging;`) -3. **Threading policy** — game logic runs only on the main loop; **never** touch game state (`World`, mobiles, items, maps, timers) from a background thread. Heavy work that *needs* game state must be **chunked** across ticks, not threaded. Heavy work that does *not* need game state (large-file parse, external I/O) **must** run on a background thread **and must yield to world saves** (defer while `World.Saving`/`WorldState.PendingSave`). Publish results back to the loop as an immutable snapshot swapped via a single `volatile` reference — the only sanctioned `volatile`. No `lock`/`Mutex`/`ConcurrentDictionary` in game logic. Rule #10 covers how background work hands results back to the loop → `dev-docs/threading-model.md` -4. **No `World.Mobiles`/`World.Items` iteration** — use spatial queries: `map.GetMobilesInRange()`, `map.GetItemsInRange()` -5. **Clean up refs in `OnDelete()`/`OnAfterDelete()`** — null out `Item`/`Mobile` references -6. **Cancel timers in `OnDelete()`/`OnAfterDelete()`** — call `_token.Cancel()` or `_timer?.Stop()` -7. **`STArrayPool.Shared`** not `ArrayPool.Shared` — single-threaded optimized, no locks -8. **`PooledRefList`** not `new List()` on hot paths — zero GC pressure, stack-allocated ref struct -9. **Serialization** — class must be `partial`, constructor needs `[Constructible]`, `TimerExecutionToken` must NOT have `[SerializableField]`. New classes: use `[SerializationGenerator(version)]` (omit `encoded`). Setters that coerce/veto/run side effects: use `[SerializableField]` args `allowFieldChange: nameof(BoolRefMethod)` / `fieldChanged: nameof(OldNewMethod)` — reserve `[SerializableProperty]` for custom getters. Serializable `Timer` members declare `[DeserializeTimer(nameof(Method))]` on the field (anchored by default — downtime preserves remaining delay; `wallClock: true` = absolute; method runs only when a timer was running at save). Conditional writes: `[SaveFlag(nameof(Should), nameof(Default))]` on the field. When bumping versions, add `MigrateFrom(VXContent)` (X = previous version). Never modify `Deserialize(reader, version)` for version bumps — that method is only for pre-codegen legacy saves. When migrating from pre-codegen Serialize/Deserialize: pass `false` if old code used `reader.ReadInt()`, bump version +1, and keep old logic as `private void Deserialize(IGenericReader reader, int version)` → `dev-docs/serialization.md`, `dev-docs/runuo-migration-docs/02-serialization.md` -10. **No `Task.Run`/`new Thread()` for game logic** (tandem with rule #3) — game logic is the single-threaded event loop. Backgrounding is allowed only for work that does not itself touch game state (external service calls, large-file parse). **Prove the need before adding a thread**: measure **on-loop** time, not wall-clock (frozen world is the cost, player latency is not), and gate on `Environment.ProcessorCount` — off-loading creates no CPU and buys nothing on 1–2 cores. New workers go in the vetted table in `dev-docs/threading-model.md` with their measurement. When such work must *feed* game logic: run the heavy/I/O part off-loop and `ConfigureAwait(false)` its awaits so a continuation never resumes on the loop and silently foregrounds heavy work; then hand the result back **explicitly** — publish an immutable snapshot swapped via a `volatile` reference (the loop reads it lock-free), or marshal the apply step with `Core.LoopContext.Post(() => …)`, re-validating in the continuation whatever may have changed while it ran. Never touch game state off-thread; never let the scheduler decide where the heavy work runs → `dev-docs/threading-model.md` -11. **Never assume era** — if code uses `Core.AOS`/`Core.SE`/etc., ask which expansion to target -12. **Naming** — `_camelCase` private fields, `PascalCase` properties/methods/classes; don't flag legacy `m_` but use `_` for new code -13. **No empty gumps** — every gump must produce visual elements. An empty gump leaks on client+server (no way to close it). Use static `DisplayTo()` to validate before constructing → `dev-docs/gump-system.md` -14. **PropertyList string literals must be holes** — `$"{"Map"}\t{value}"` not `$"Map\t{value}"`. The handler treats bare text as delimiters, `{}` holes as arguments. Only `\t` should be a bare literal → `dev-docs/property-lists.md` -15. **Braces required on all control flow** — `if`, `else`, `for`, `foreach`, `while`, `do`, `switch` must always have braces, even for single-line bodies → `dev-docs/code-standards.md` -16. **Prefer switch expressions and switch-when** — use switch expressions for value mapping and switch-when for pattern matching where they improve readability. Exception: skip if unreadable or cold path → `dev-docs/code-standards.md` -17. **No `System.Text.StringBuilder`** — use `ValueStringBuilder` with `stackalloc` (bounded output) or `ValueStringBuilder.Create()` (unbounded). Supports `$"..."` interpolation directly. Always use `using var` for disposal. Use `Reset()` instead of reassigning → `dev-docs/string-handling.md` -18. **Interpolation anti-patterns on handler-aware APIs** — `Send*`/`Say`/`Emote`/`PublicOverhead*`/`IPropertyList.Add`/gump `AddLabel`/`AddHtml`/`Html.Center`/`SpanWriter.Write*` all have `ref RawInterpolatedStringHandler` overloads that allocate zero strings, but only when the call-site argument is a `$"..."` literal directly. Avoid: ternaries with interpolated branches (`Send(c ? $"a" : $"b")`), switch expressions with interpolated arms, pre-built `var s = $"..."` locals (single-use), `.ToString()` / `.String()` / `string.Format` inside holes, string concat (`{a + b}`), LINQ string ops in holes. Use `:L` format spec for lowercase (`{rank:L}` not `rank.ToString().ToLowerInvariant()`) → `dev-docs/string-handling.md` § Interpolation Anti-Patterns -19. **No `InvalidateProperties()` from inside `GetProperties`** — every property a `GetProperties` override reads must be a pure read. `InvalidateProperties()` rebuilds the list in place (`Reset()` + rebuild), and `Reset()` returns the pooled interpolation buffer — which the compiler rents for the whole `$"..."` expression, so every hole is evaluated while it is live — and rewinds the packet cursor. A getter that invalidates therefore throws `ArgumentNullException` (parameter `"array"`) out of `GetProperties` from an unrelated-looking line, or silently corrupts the tooltip. The engine refuses and logs an error; `DEBUG` throws. Lazy recomputation in a getter is fine — the *notification* is not. Invalidate in the setter that changes the value, or defer with `Timer.DelayCall(InvalidateProperties)` → `dev-docs/property-lists.md` § Never Invalidate From Inside `GetProperties` -20. **Tick-count math must be wraparound-safe** — compare `Core.TickCount`/`GetTimestamp()` values only by subtraction (`a - b < 0`, never `a < b`), no zero/sign sentinels on tick fields, seed deadline fields from a real tick (never rely on the 0 default). Cloud hypervisors (GCP) pass through the host's never-resetting counter: ticks start enormous and can wrap negative. Linux affected in production; Windows not so far → `dev-docs/tick-counts.md` - -## Dev-Docs Reference - -| Topic | File | -|---|---| -| Code standards & LINQ tiers | `dev-docs/code-standards.md` | -| Serialization system | `dev-docs/serialization.md` | -| Content patterns (Items, Mobiles, Creatures) | `dev-docs/content-patterns.md` | -| Era & expansion handling | `dev-docs/era-expansion.md` | -| Timer system | `dev-docs/timers.md` | -| Event scheduler (wall-clock/calendar) | `dev-docs/event-scheduler.md` | -| Object property lists (tooltips) | `dev-docs/property-lists.md` | -| Gump (UI dialog) system | `dev-docs/gump-system.md` | -| Commands & targeting | `dev-docs/commands-targeting.md` | -| Event system | `dev-docs/events.md` | -| Threading model | `dev-docs/threading-model.md` | -| Server hardware requirements | `dev-docs/server-requirements.md` | -| Debugging event-loop performance (profiling build, decomposition, GC/RAM) | `dev-docs/debugging-event-loop.md` | -| Tick-count overflow rules (subtraction comparisons; GCP pass-through counters) | `dev-docs/tick-counts.md` | -| Server lifecycle & bootstrap phases (Configure/ConfigurePrompts/Initialize) | `dev-docs/server-lifecycle.md` | -| Platform prerequisites (ICU, tzdata, native libs per distro) | `dev-docs/platform-prerequisites.md` | -| Configuration system | `dev-docs/configuration.md` | -| Networking & packets | `dev-docs/networking-packets.md` | -| IP bans, blocklists & allowlists (incl. unblocking a player) | `dev-docs/ip-bans-and-allowlists.md` | -| Region system | `dev-docs/regions.md` | -| String handling & ValueStringBuilder | `dev-docs/string-handling.md` | -| RunUO migration (overview) | `dev-docs/runuo-migration-docs/00-overview.md` | -| RunUO migration (all docs) | `dev-docs/runuo-migration-docs/` | - -## Claude Skills (Opt-In) - -Detailed Claude Code skills live in `dev-docs/claude-skills/`. They are **not auto-loaded** — they must be copied to `.claude/skills/` to activate. - -**When to offer**: If the user is building complex content (new items, creatures, spells, gumps, quests, packets, serialization work, etc.), ask: - -> I have detailed Claude Code skills for this kind of work. Want me to enable them? -> I'll copy the relevant files from `dev-docs/claude-skills/` to `.claude/skills/`. - -Then copy only the relevant skill files based on the task: - -| Task | Skills to enable | -|---|---| -| New Item or Mobile | `modernuo-content-patterns`, `modernuo-serialization`, `modernuo-property-lists` | -| Creature / spawn | `modernuo-content-patterns`, `modernuo-serialization`, `modernuo-timers` | -| Spell or ability | `modernuo-content-patterns`, `modernuo-serialization`, `modernuo-timers`, `modernuo-era-expansion` | -| Gump / UI dialog | `modernuo-gump-system`, `modernuo-commands-targeting` | -| Quest or event system | `modernuo-events`, `modernuo-content-patterns`, `modernuo-configuration` | -| Scheduled / seasonal / holiday events | `modernuo-event-scheduler`, `modernuo-timers` | -| Custom regions / dynamic areas | `modernuo-regions`, `modernuo-content-patterns` | -| Packet / networking | `modernuo-networking`, `modernuo-threading` | -| Commands | `modernuo-commands-targeting` | -| Timer work | `modernuo-timers`, `modernuo-serialization` | -| Config system | `modernuo-configuration` | -| Era-conditional code | `modernuo-era-expansion` | -| String building / formatting | `modernuo-string-handling` | -| Code review / audit | `modernuo-code-audit` | -| Any `.cs` file edit | `modernuo-code-audit` (always offer for code changes) | -| **RunUO Migration** | | -| Migrate any RunUO script | `migrate-from-runuo/migrate-foundation` (always), plus system-specific skills below | -| Migrate Item/Mobile/Creature | `migrate-from-runuo/migrate-foundation`, `migrate-from-runuo/migrate-serialization`, `migrate-from-runuo/migrate-items-mobiles` | -| Migrate serialization | `migrate-from-runuo/migrate-serialization` | -| Migrate timers | `migrate-from-runuo/migrate-timers` | -| Migrate gumps | `migrate-from-runuo/migrate-gumps` | -| Migrate packets | `migrate-from-runuo/migrate-packets` | -| Migrate property lists | `migrate-from-runuo/migrate-property-lists` | -| Migrate events/commands | `migrate-from-runuo/migrate-commands-events` | -| Migrate persistence (WorldSave) | `migrate-from-runuo/migrate-persistence` | -| Migrate multi-file system | `migrate-from-runuo/migrate-systems` | - -To enable a skill — Claude Code loads `.claude/skills//SKILL.md`; a bare `.md` dropped -directly into `.claude/skills/` is **not** picked up, and newly installed skills appear in the -*next* session: - -```sh -# Standard skills (modernuo-*) -mkdir -p .claude/skills/ && cp dev-docs/claude-skills/.md .claude/skills//SKILL.md - -# Migration skills — sources live in the migrate-from-runuo/ subfolder, but install under the -# bare skill name (the table's "migrate-from-runuo/" is the source path, not the name): -mkdir -p .claude/skills/ && cp dev-docs/claude-skills/migrate-from-runuo/.md .claude/skills//SKILL.md -``` - -Migration skills reference the deep docs in `dev-docs/runuo-migration-docs/` and point to existing ModernUO skills for best practices. - -The `modernuo-code-audit` skill auto-triggers on `.cs` file edits and flags convention violations (warnings only, asks before fixing). diff --git a/Directory.Build.props b/Directory.Build.props index c832f1006..43a8a6044 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,9 +3,9 @@ Kamron Batman ModernUO - 2019-2026 - net10.0 - 14 + 2019-2023 + net9.0 + 12 true true NU1603 @@ -31,12 +31,10 @@ $(SolutionDir) false - - true - true false + true false @@ -63,19 +61,12 @@ ..\..\Rules.ruleset latest - - - $(DefineConstants);EVENT_LOOP_PROFILING - - + - + - 3.10.91 + 3.7.112 all diff --git a/Distribution/Data/Binary/Bounds.bin b/Distribution/Data/Binary/Bounds.bin new file mode 100644 index 000000000..11431362e Binary files /dev/null and b/Distribution/Data/Binary/Bounds.bin differ diff --git a/Distribution/Data/Decoration/BountyBoards/bountyboards.cfg b/Distribution/Data/Decoration/BountyBoards/bountyboards.cfg deleted file mode 100644 index 32fb378eb..000000000 --- a/Distribution/Data/Decoration/BountyBoards/bountyboards.cfg +++ /dev/null @@ -1,25 +0,0 @@ -# Bounty Boards — placed when murderSystem.bountiesEnabled is true -# 0x1E5E = east-facing, 0x1E5F = south-facing - -BountyBoard 0x1E5E -2887 3482 17 -2734 2184 0 -2891 680 0 -5668 3123 13 - -BountyBoard 0x1E5F -5278 3981 37 -1442 1687 0 -2512 551 0 -600 2147 0 - -# Guards — one per board, standing in front -Spawner 0x1F13 (Spawn=WarriorGuard; Count=1; HomeRange=0; WalkingRange=5; Name=Bounty Board Guard Spawner) -2887 3487 17 -2734 2189 0 -2891 685 0 -5668 3128 13 -5283 3981 37 -1447 1687 0 -2517 551 0 -605 2147 0 diff --git a/Distribution/Data/Decoration/Trammel/haven.cfg b/Distribution/Data/Decoration/Trammel/haven.cfg index 921eb141a..bba5db5c6 100644 --- a/Distribution/Data/Decoration/Trammel/haven.cfg +++ b/Distribution/Data/Decoration/Trammel/haven.cfg @@ -331,13 +331,4 @@ FlourMillSouthAddon 0x192C # bellows LargeForgeEastAddon 0x1986 3644 2619 0 -3596 2601 0 - -# New Haven Mine Teleporters -# Rope(0x14FA) teleporter -InteractionTeleporter 0x14FA (Name=a rope leading down;PointDest=(5953, 319, 0)) -5994 319 0 - -# Rope(0x14FA) teleporter -InteractionTeleporter 0x14FA (Name=a rope leading up;PointDest=(5994, 319, 0)) -5953 319 0 \ No newline at end of file +3596 2601 0 \ No newline at end of file diff --git a/Distribution/Data/Items/ItemBounds.bin b/Distribution/Data/Items/ItemBounds.bin deleted file mode 100644 index 1e0214cbf..000000000 Binary files a/Distribution/Data/Items/ItemBounds.bin and /dev/null differ diff --git a/Distribution/Data/Spawns/shared/felucca/BlightedGrove.json b/Distribution/Data/Spawns/post-uoml/felucca/BlightedGrove.json similarity index 89% rename from Distribution/Data/Spawns/shared/felucca/BlightedGrove.json rename to Distribution/Data/Spawns/post-uoml/felucca/BlightedGrove.json index a7dca8fe5..17a579783 100644 --- a/Distribution/Data/Spawns/shared/felucca/BlightedGrove.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/BlightedGrove.json @@ -1,38 +1,27 @@ [ { - "$type": "Spawner", - "guid": "163c83bd-e63d-4654-8c0e-231486150f45", + "type": "Spawner", + "guid": "c6dedce5-b3a9-4510-b8a4-4e1be285305c", "name": "Spawner (201)", - "location": [6540, 872, 0], + "location": [6513, 871, 0], "map": "Felucca", - "count": 1, + "count": 17, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, + "homeRange": 35, + "walkingRange": 35, "entries": [ - { "name": "InsaneDryad", "maxCount": 1, "probability": 100 } + { "name": "GiantSerpent", "maxCount": 17, "probability": 100 }, + { "name": "GiantToad", "maxCount": 17, "probability": 100 }, + { "name": "Harpy", "maxCount": 17, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 17, "probability": 100 }, + { "name": "Snake", "maxCount": 17, "probability": 100 }, + { "name": "Bogling", "maxCount": 17, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "196961bc-de35-4e5c-9fbf-079ab5092d5f", - "name": "Spawner (201)", - "location": [6518, 879, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "InsaneDryad", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "3bd66ec7-4d6b-46cb-8913-5c62344510c0", "name": "Spawner (201)", "location": [6507, 843, 0], @@ -54,7 +43,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "687f8c07-43c4-4724-8ede-078862373736", "name": "Spawner (201)", "location": [6535, 848, 0], @@ -72,44 +61,7 @@ ] }, { - "$type": "Spawner", - "guid": "9b045049-0b03-4f18-9148-b0bd1fa3600b", - "name": "Spawner (201)", - "location": [6489, 897, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "InsaneDryad", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c6dedce5-b3a9-4510-b8a4-4e1be285305c", - "name": "Spawner (201)", - "location": [6513, 871, 0], - "map": "Felucca", - "count": 17, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 35, - "walkingRange": 35, - "entries": [ - { "name": "GiantSerpent", "maxCount": 17, "probability": 100 }, - { "name": "GiantToad", "maxCount": 17, "probability": 100 }, - { "name": "Harpy", "maxCount": 17, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 17, "probability": 100 }, - { "name": "Snake", "maxCount": 17, "probability": 100 }, - { "name": "Bogling", "maxCount": 17, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "d4583e64-cac2-49f5-bb09-86c9481e0147", "name": "Spawner (201)", "location": [6518, 870, 0], @@ -131,7 +83,21 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "163c83bd-e63d-4654-8c0e-231486150f45", + "name": "Spawner (201)", + "location": [6540, 872, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "f708c05a-1da4-4452-8d8b-1965849d6e39", "name": "Spawner (201)", "location": [6585, 878, 0], @@ -147,5 +113,33 @@ { "name": "Saliva", "maxCount": 7, "probability": 100 }, { "name": "Changeling", "maxCount": 7, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "9b045049-0b03-4f18-9148-b0bd1fa3600b", + "name": "Spawner (201)", + "location": [6489, 897, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "196961bc-de35-4e5c-9fbf-079ab5092d5f", + "name": "Spawner (201)", + "location": [6518, 879, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/BritainSewer.json b/Distribution/Data/Spawns/post-uoml/felucca/BritainSewer.json similarity index 89% rename from Distribution/Data/Spawns/shared/felucca/BritainSewer.json rename to Distribution/Data/Spawns/post-uoml/felucca/BritainSewer.json index 1e3e4c45e..36ab04868 100644 --- a/Distribution/Data/Spawns/shared/felucca/BritainSewer.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/BritainSewer.json @@ -1,230 +1,6 @@ [ { - "$type": "Spawner", - "guid": "07b03a23-af40-4539-be8b-6834573838df", - "name": "Spawner (202)", - "location": [6065, 1459, 5], - "map": "Felucca", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "099f6832-75ef-4f46-91b2-0d64509897c2", - "name": "Spawner (202)", - "location": [6052, 1463, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2fd77ead-e282-4216-aa6c-0273391bdd4e", - "name": "Spawner (202)", - "location": [6090, 1483, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3237bbc2-29a2-488c-a918-d8db1f5e7f60", - "name": "Spawner (202)", - "location": [6087, 1443, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Alligator", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "371c3545-e908-4d18-8dbb-3b86c422566a", - "name": "Spawner (202)", - "location": [6112, 1462, 5], - "map": "Felucca", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5da0bc6b-dfdb-47b8-a0df-0ff3eaf5c212", - "name": "Spawner (202)", - "location": [6048, 1487, 5], - "map": "Felucca", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "62ba5343-d337-495e-aaf4-9dfb4d46c8b3", - "name": "Spawner (202)", - "location": [6061, 1490, 5], - "map": "Felucca", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75adb4cb-b0a7-44db-a5fe-d4e9b139ce7b", - "name": "Spawner (202)", - "location": [6059, 1438, 4], - "map": "Felucca", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75e04bc3-078d-40c2-9ca8-d3a11a97965f", - "name": "Spawner (202)", - "location": [6108, 1471, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Alligator", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "77bf6da3-ca32-446e-8d0b-239bb4520afd", - "name": "Spawner (202)", - "location": [6037, 1494, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Alligator", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8674d509-c36a-4ab8-9399-6329f1379984", - "name": "Spawner (202)", - "location": [6091, 1491, 10], - "map": "Felucca", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8680e6d-12db-4ef9-8107-9df6850893db", - "name": "Spawner (202)", - "location": [6115, 1491, -15], - "map": "Felucca", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a9a6ccfe-d9b7-40f3-bfa8-d00994214056", - "name": "Spawner (202)", - "location": [6039, 1438, 4], - "map": "Felucca", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "aca1fcd4-cc2b-4472-ab6e-89e0322f9fec", "name": "Spawner (202)", "location": [6049, 1447, 4], @@ -242,7 +18,61 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "8674d509-c36a-4ab8-9399-6329f1379984", + "name": "Spawner (202)", + "location": [6091, 1491, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5da0bc6b-dfdb-47b8-a0df-0ff3eaf5c212", + "name": "Spawner (202)", + "location": [6048, 1487, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07b03a23-af40-4539-be8b-6834573838df", + "name": "Spawner (202)", + "location": [6065, 1459, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "b8273eff-be93-4ab9-8642-c1e9a92ddbbc", "name": "Spawner (202)", "location": [6092, 1448, 5], @@ -260,10 +90,10 @@ ] }, { - "$type": "Spawner", - "guid": "ba6cecc1-f3c1-4e95-a624-ac4d5d668265", + "type": "Spawner", + "guid": "ebace9c4-5941-4346-9ebf-6ca7ff7e33c5", "name": "Spawner (202)", - "location": [6034, 1467, 5], + "location": [6107, 1452, 25], "map": "Felucca", "count": 3, "minDelay": "00:00:00", @@ -278,10 +108,10 @@ ] }, { - "$type": "Spawner", - "guid": "c93d730a-8c5a-4580-8952-14008d80b395", + "type": "Spawner", + "guid": "371c3545-e908-4d18-8dbb-3b86c422566a", "name": "Spawner (202)", - "location": [6049, 1470, 5], + "location": [6112, 1462, 5], "map": "Felucca", "count": 3, "minDelay": "00:00:00", @@ -296,10 +126,10 @@ ] }, { - "$type": "Spawner", - "guid": "d0d1dbda-9b50-4646-9509-d5d32eb9f081", + "type": "Spawner", + "guid": "a9a6ccfe-d9b7-40f3-bfa8-d00994214056", "name": "Spawner (202)", - "location": [6041, 1486, 5], + "location": [6039, 1438, 4], "map": "Felucca", "count": 3, "minDelay": "00:00:00", @@ -314,7 +144,25 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "a8680e6d-12db-4ef9-8107-9df6850893db", + "name": "Spawner (202)", + "location": [6115, 1491, -15], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "da9f7dcc-c64d-4a72-ba8a-b5846258a093", "name": "Spawner (202)", "location": [6084, 1470, 5], @@ -332,26 +180,10 @@ ] }, { - "$type": "Spawner", - "guid": "e8b1dc3e-cc13-4253-b228-12abe499061b", + "type": "Spawner", + "guid": "c93d730a-8c5a-4580-8952-14008d80b395", "name": "Spawner (202)", - "location": [6077, 1492, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Alligator", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ebace9c4-5941-4346-9ebf-6ca7ff7e33c5", - "name": "Spawner (202)", - "location": [6107, 1452, 25], + "location": [6049, 1470, 5], "map": "Felucca", "count": 3, "minDelay": "00:00:00", @@ -364,5 +196,161 @@ { "name": "Rat", "maxCount": 2, "probability": 100 }, { "name": "BullFrog", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "ba6cecc1-f3c1-4e95-a624-ac4d5d668265", + "name": "Spawner (202)", + "location": [6034, 1467, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75adb4cb-b0a7-44db-a5fe-d4e9b139ce7b", + "name": "Spawner (202)", + "location": [6059, 1438, 4], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62ba5343-d337-495e-aaf4-9dfb4d46c8b3", + "name": "Spawner (202)", + "location": [6061, 1490, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d0d1dbda-9b50-4646-9509-d5d32eb9f081", + "name": "Spawner (202)", + "location": [6041, 1486, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2fd77ead-e282-4216-aa6c-0273391bdd4e", + "name": "Spawner (202)", + "location": [6090, 1483, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "Alligator", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e8b1dc3e-cc13-4253-b228-12abe499061b", + "name": "Spawner (202)", + "location": [6077, 1492, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "75e04bc3-078d-40c2-9ca8-d3a11a97965f", + "name": "Spawner (202)", + "location": [6108, 1471, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3237bbc2-29a2-488c-a918-d8db1f5e7f60", + "name": "Spawner (202)", + "location": [6087, 1443, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "099f6832-75ef-4f46-91b2-0d64509897c2", + "name": "Spawner (202)", + "location": [6052, 1463, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "Alligator", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "77bf6da3-ca32-446e-8d0b-239bb4520afd", + "name": "Spawner (202)", + "location": [6037, 1494, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Covetous.json b/Distribution/Data/Spawns/post-uoml/felucca/Covetous.json similarity index 87% rename from Distribution/Data/Spawns/shared/felucca/Covetous.json rename to Distribution/Data/Spawns/post-uoml/felucca/Covetous.json index 633bfd777..b67938d6a 100644 --- a/Distribution/Data/Spawns/shared/felucca/Covetous.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Covetous.json @@ -1,39 +1,161 @@ [ { - "$type": "Spawner", - "guid": "04a59b8a-4697-4c8a-b057-a7846a5cfe97", + "type": "Spawner", + "guid": "85fdc7f9-7c5b-4d42-ba34-f0470a3470c9", "name": "Spawner (203)", - "location": [5460, 1975, 0], + "location": [5472, 1877, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Harpy", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b4252b3-0f5e-4af8-b8c7-6dc2352924f5", + "name": "Spawner (203)", + "location": [5453, 1905, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "HeadlessOne", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "938f10be-c2ce-4786-a151-5a946533d688", + "name": "Spawner (203)", + "location": [5443, 1918, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 30, + "entries": [{ "name": "StoneHarpy", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e0b760fb-d7aa-45d3-8bf9-48011b0cf4d5", + "name": "Spawner (203)", + "location": [5418, 1876, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Gazer", "maxCount": 4, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb869b8b-507c-427b-879d-4fe7b790a3a2", + "name": "Spawner (203)", + "location": [5411, 1901, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Gazer", "maxCount": 2, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d06af3f-76ac-4beb-b7bb-c65efeb63a2d", + "name": "Spawner (203)", + "location": [5393, 1921, 0], "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 7, - "walkingRange": 7, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dd7e6d31-3034-4600-a6b9-ca42a8842c80", + "name": "Spawner (203)", + "location": [5400, 1937, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7ea55605-a5c0-4993-a383-accfc14ac29a", + "name": "Spawner (203)", + "location": [5452, 1891, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3bfaa0a-3984-451a-940c-3fd6890e0d94", + "name": "Spawner (203)", + "location": [5399, 1859, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, "entries": [ { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "05c027e9-5cef-4b03-b4e2-b7f70c0f34cb", + "type": "Spawner", + "guid": "50cbf26b-8516-4000-869e-08fee55f37c8", "name": "Spawner (203)", - "location": [5469, 2019, 0], + "location": [5387, 1911, 0], "map": "Felucca", - "count": 5, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 25, + "homeRange": 2, + "walkingRange": 2, "entries": [ - { "name": "Slime", "maxCount": 5, "probability": 100 } + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "076ead03-f463-46d8-9eae-962520cb986f", "name": "Spawner (203)", "location": [5411, 1932, 0], @@ -51,55 +173,21 @@ ] }, { - "$type": "Spawner", - "guid": "081c0be6-7cd4-4ab6-95bd-b4a3ac8bc5eb", + "type": "Spawner", + "guid": "46c6bb22-25f4-44cc-a42a-15ec120268bb", "name": "Spawner (203)", - "location": [5436, 1972, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Corpser", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11ad3aa3-a368-4ae4-834f-873a14b82a5c", - "name": "Spawner (203)", - "location": [2513, 862, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12837eff-77e0-423d-9308-aa8e4b92afc0", - "name": "Spawner (203)", - "location": [5388, 2012, 0], + "location": [5577, 2006, 0], "map": "Felucca", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Gazer", "maxCount": 2, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "18614d1c-4026-4618-9066-f8369e353c0b", "name": "Spawner (203)", "location": [5593, 2017, 0], @@ -110,67 +198,24 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "18b6eb68-8acb-4269-8d51-e1e52df02f52", + "type": "Spawner", + "guid": "b1325960-b3f5-4e08-bf86-4971d893a03b", "name": "Spawner (203)", - "location": [5596, 1903, 0], + "location": [5558, 2025, 0], "map": "Felucca", - "count": 7, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Mummy", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "1d06af3f-76ac-4beb-b7bb-c65efeb63a2d", - "name": "Spawner (203)", - "location": [5393, 1921, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Corpser", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21535b77-5477-4334-98a1-2fb854f492a2", - "name": "Spawner (203)", - "location": [5429, 1989, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Gazer", "maxCount": 2, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "44351b8c-77f3-4eca-b121-b692abfefe34", "name": "Spawner (203)", "location": [5498, 1998, 0], @@ -187,40 +232,86 @@ ] }, { - "$type": "Spawner", - "guid": "46c6bb22-25f4-44cc-a42a-15ec120268bb", + "type": "Spawner", + "guid": "57532ad5-1402-49c2-ac15-8b40fbe30210", "name": "Spawner (203)", - "location": [5577, 2006, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50cbf26b-8516-4000-869e-08fee55f37c8", - "name": "Spawner (203)", - "location": [5387, 1911, 0], + "location": [5474, 2035, 0], "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 2, - "walkingRange": 2, + "homeRange": 10, + "walkingRange": 25, "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "05c027e9-5cef-4b03-b4e2-b7f70c0f34cb", + "name": "Spawner (203)", + "location": [5469, 2019, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Slime", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "610daa60-c1c5-4a85-be92-2bd7cec2d8d3", + "name": "Spawner (203)", + "location": [5445, 2030, -3], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "WaterElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c7a0ddb7-7ad8-4f5e-aa17-6d4cb62deebe", + "name": "Spawner (203)", + "location": [5440, 2023, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dede0e05-f13e-4e1f-bd7d-3a5ad86ae730", + "name": "Spawner (203)", + "location": [5473, 1991, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 9, + "walkingRange": 25, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "55ea4b3a-f7f0-4e41-8a13-4b839a657671", "name": "Spawner (203)", "location": [5457, 1971, 0], @@ -238,41 +329,80 @@ ] }, { - "$type": "Spawner", - "guid": "57532ad5-1402-49c2-ac15-8b40fbe30210", + "type": "Spawner", + "guid": "081c0be6-7cd4-4ab6-95bd-b4a3ac8bc5eb", "name": "Spawner (203)", - "location": [5474, 2035, 0], + "location": [5436, 1972, 0], "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "87a1cb9f-e49a-407e-a015-f52c4449902b", + "name": "Spawner (203)", + "location": [5417, 1975, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21535b77-5477-4334-98a1-2fb854f492a2", + "name": "Spawner (203)", + "location": [5429, 1989, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, "walkingRange": 25, "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "DreadSpider", "maxCount": 1, "probability": 100 } + { "name": "Gazer", "maxCount": 2, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "5d292834-991e-49ac-b1f8-9b19816f6619", + "type": "Spawner", + "guid": "7b58b7a8-e6b5-4353-82b2-787c87a6469d", "name": "Spawner (203)", - "location": [5448, 2027, 0], + "location": [5425, 1992, 0], "map": "Felucca", - "count": 3, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "12837eff-77e0-423d-9308-aa8e4b92afc0", + "name": "Spawner (203)", + "location": [5388, 2012, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Gazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "5e88cb7f-eff4-43c8-8137-ec7efbef249e", "name": "Spawner (203)", "location": [5503, 2002, 0], @@ -289,94 +419,7 @@ ] }, { - "$type": "Spawner", - "guid": "610daa60-c1c5-4a85-be92-2bd7cec2d8d3", - "name": "Spawner (203)", - "location": [5445, 2030, -3], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "WaterElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bef2f7e-e2ca-444e-a1a9-04f03ce7b12c", - "name": "Spawner (203)", - "location": [5579, 1859, 0], - "map": "Felucca", - "count": 11, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 11, - "walkingRange": 25, - "entries": [ - { "name": "Lich", "maxCount": 7, "probability": 100 }, - { "name": "Spectre", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "78da4a22-a348-4c15-80c1-90a649a8551c", - "name": "Spawner (203)", - "location": [5615, 1838, 0], - "map": "Felucca", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 25, - "entries": [ - { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b4252b3-0f5e-4af8-b8c7-6dc2352924f5", - "name": "Spawner (203)", - "location": [5453, 1905, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "HeadlessOne", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b58b7a8-e6b5-4353-82b2-787c87a6469d", - "name": "Spawner (203)", - "location": [5425, 1992, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Corpser", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "7b69d58b-9222-46b1-9833-4d9c5d73785e", "name": "Spawner (203)", "location": [5413, 2000, 0], @@ -393,59 +436,44 @@ ] }, { - "$type": "Spawner", - "guid": "7ea55605-a5c0-4993-a383-accfc14ac29a", + "type": "Spawner", + "guid": "04a59b8a-4697-4c8a-b057-a7846a5cfe97", "name": "Spawner (203)", - "location": [5452, 1891, 0], + "location": [5460, 1975, 0], "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 2, - "walkingRange": 2, + "homeRange": 7, + "walkingRange": 7, "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "85fdc7f9-7c5b-4d42-ba34-f0470a3470c9", + "type": "Spawner", + "guid": "5d292834-991e-49ac-b1f8-9b19816f6619", "name": "Spawner (203)", - "location": [5472, 1877, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Harpy", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "87a1cb9f-e49a-407e-a015-f52c4449902b", - "name": "Spawner (203)", - "location": [5417, 1975, 0], + "location": [5448, 2027, 0], "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, + "homeRange": 8, + "walkingRange": 8, "entries": [ - { "name": "Corpser", "maxCount": 3, "probability": 100 } + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "9274c0ec-c477-4846-b170-b903efdbba05", + "type": "Spawner", + "guid": "e2c5902f-4e3a-413c-b938-f8e7bf3bef13", "name": "Spawner (203)", - "location": [2493, 883, 0], + "location": [2456, 861, 0], "map": "Felucca", "count": 4, "minDelay": "00:05:00", @@ -453,28 +481,10 @@ "team": 0, "homeRange": 10, "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "938f10be-c2ce-4786-a151-5a946533d688", - "name": "Spawner (203)", - "location": [5443, 1918, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 30, - "entries": [ - { "name": "StoneHarpy", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a6cd9b62-1f5a-4c7b-81b8-4df8e73a1c88", "name": "Spawner (203)", "location": [2480, 863, 0], @@ -485,65 +495,38 @@ "team": 0, "homeRange": 10, "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "a733418d-d49e-4938-842b-cc7ffed4523a", + "type": "Spawner", + "guid": "9274c0ec-c477-4846-b170-b903efdbba05", "name": "Spawner (203)", - "location": [5504, 1808, 0], + "location": [2493, 883, 0], "map": "Felucca", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 3, - "walkingRange": 12, - "entries": [ - { "name": "RottingCorpse", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "b1325960-b3f5-4e08-bf86-4971d893a03b", + "type": "Spawner", + "guid": "11ad3aa3-a368-4ae4-834f-873a14b82a5c", "name": "Spawner (203)", - "location": [5558, 2025, 0], + "location": [2513, 862, 0], "map": "Felucca", - "count": 2, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b661a309-8991-4fb9-9360-47dda815fae5", - "name": "Spawner (203)", - "location": [5617, 1861, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, "walkingRange": 25, - "entries": [ - { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bd99f0d0-a90c-409b-be4c-91898afed157", "name": "Spawner (203)", "location": [5560, 1903, 0], @@ -565,17 +548,17 @@ ] }, { - "$type": "Spawner", - "guid": "c2ebc056-295d-46f8-9a00-5dde61c30859", + "type": "Spawner", + "guid": "18b6eb68-8acb-4269-8d51-e1e52df02f52", "name": "Spawner (203)", - "location": [5560, 1843, 0], + "location": [5596, 1903, 0], "map": "Felucca", "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 15, - "walkingRange": 40, + "walkingRange": 25, "entries": [ { "name": "Mummy", "maxCount": 1, "probability": 100 }, { "name": "Shade", "maxCount": 1, "probability": 100 }, @@ -587,58 +570,7 @@ ] }, { - "$type": "Spawner", - "guid": "c7a0ddb7-7ad8-4f5e-aa17-6d4cb62deebe", - "name": "Spawner (203)", - "location": [5440, 2023, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "DreadSpider", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ca2ebd92-7ae7-43ab-a1bd-7bf52783f77f", - "name": "Spawner (203)", - "location": [5461, 1818, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cb869b8b-507c-427b-879d-4fe7b790a3a2", - "name": "Spawner (203)", - "location": [5411, 1901, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gazer", "maxCount": 2, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "cc9968d8-52b1-415b-b5c7-05129358b666", "name": "Spawner (203)", "location": [5560, 1876, 0], @@ -660,7 +592,106 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "f0fae8d5-a1e1-41f4-a0ec-4469b9ca6648", + "name": "Spawner (203)", + "location": [5603, 1875, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b661a309-8991-4fb9-9360-47dda815fae5", + "name": "Spawner (203)", + "location": [5617, 1861, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78da4a22-a348-4c15-80c1-90a649a8551c", + "name": "Spawner (203)", + "location": [5615, 1838, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bef2f7e-e2ca-444e-a1a9-04f03ce7b12c", + "name": "Spawner (203)", + "location": [5579, 1859, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 11, + "walkingRange": 25, + "entries": [ + { "name": "Lich", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2ebc056-295d-46f8-9a00-5dde61c30859", + "name": "Spawner (203)", + "location": [5560, 1843, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 40, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "dcb4ea5c-002a-4589-be5c-f09de15f5d4e", "name": "Spawner (203)", "location": [5528, 1808, 0], @@ -682,109 +713,25 @@ ] }, { - "$type": "Spawner", - "guid": "dd7e6d31-3034-4600-a6b9-ca42a8842c80", + "type": "Spawner", + "guid": "a733418d-d49e-4938-842b-cc7ffed4523a", "name": "Spawner (203)", - "location": [5400, 1937, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Corpser", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dede0e05-f13e-4e1f-bd7d-3a5ad86ae730", - "name": "Spawner (203)", - "location": [5473, 1991, 0], + "location": [5504, 1808, 0], "map": "Felucca", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 9, - "walkingRange": 25, + "homeRange": 3, + "walkingRange": 12, "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "DreadSpider", "maxCount": 2, "probability": 100 } + { "name": "RottingCorpse", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e0b760fb-d7aa-45d3-8bf9-48011b0cf4d5", - "name": "Spawner (203)", - "location": [5418, 1876, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "Gazer", "maxCount": 4, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e2c5902f-4e3a-413c-b938-f8e7bf3bef13", - "name": "Spawner (203)", - "location": [2456, 861, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e3bfaa0a-3984-451a-940c-3fd6890e0d94", - "name": "Spawner (203)", - "location": [5399, 1859, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f0fae8d5-a1e1-41f4-a0ec-4469b9ca6648", - "name": "Spawner (203)", - "location": [5603, 1875, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 25, - "entries": [ - { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f8fc4307-f4ce-4883-b41d-b8dca969991a", "name": "Spawner (203)", "location": [5452, 1799, 0], @@ -799,5 +746,22 @@ { "name": "Dragon", "maxCount": 1, "probability": 100 }, { "name": "Drake", "maxCount": 2, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "ca2ebd92-7ae7-43ab-a1bd-7bf52783f77f", + "name": "Spawner (203)", + "location": [5461, 1818, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Deceit.json b/Distribution/Data/Spawns/post-uoml/felucca/Deceit.json similarity index 91% rename from Distribution/Data/Spawns/shared/felucca/Deceit.json rename to Distribution/Data/Spawns/post-uoml/felucca/Deceit.json index c7b146387..98600c715 100644 --- a/Distribution/Data/Spawns/shared/felucca/Deceit.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Deceit.json @@ -1,61 +1,52 @@ [ { - "$type": "Spawner", - "guid": "05d2885b-2b4d-4c4e-907e-2fdbe4d54e69", + "type": "Spawner", + "guid": "edd28bb3-5aeb-487a-8571-9e4e4aea0783", "name": "Spawner (204)", - "location": [5212, 674, -20], + "location": [5187, 603, 0], "map": "Felucca", - "count": 4, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, - "walkingRange": 25, + "homeRange": 18, + "walkingRange": 35, "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0b6e0a59-f918-4f77-8bc8-f5b5d944d3a5", - "name": "Spawner (204)", - "location": [5219, 573, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 3, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, { "name": "Zombie", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "0be3858f-bd81-4a27-9a8d-3846c0df0603", + "type": "Spawner", + "guid": "8507dbe4-8220-4439-adbe-5ef6ebb0fec3", "name": "Spawner (204)", - "location": [5192, 695, 0], + "location": [5186, 575, 0], "map": "Felucca", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } - ] + "homeRange": 8, + "walkingRange": 12, + "entries": [{ "name": "WaterElemental", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "fb409b68-fe6b-422f-bf2d-95c4a92f5e21", + "name": "Spawner (204)", + "location": [5187, 545, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "19d96c26-d7f8-4209-9071-00e0117ed1d8", "name": "Spawner (204)", "location": [5163, 567, 0], @@ -73,136 +64,92 @@ ] }, { - "$type": "Spawner", - "guid": "1cbfae9c-7cc7-45cc-842f-0e75a158cd8a", + "type": "Spawner", + "guid": "9862d38e-e12f-4e04-932f-0020f3121d9a", "name": "Spawner (204)", - "location": [5307, 546, 0], + "location": [5145, 553, -40], "map": "Felucca", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "25b2761d-dc06-443e-b979-049c7f50abf6", - "name": "Spawner (204)", - "location": [5267, 690, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, + "homeRange": 8, "walkingRange": 35, "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, - { "name": "Lich", "maxCount": 2, "probability": 100 } + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "2a7f9024-7d0f-43a0-8721-52b6a3c27c8f", + "type": "Spawner", + "guid": "d8ba7cc3-c1f6-4701-b684-850203cb9e37", "name": "Spawner (204)", - "location": [5307, 675, -20], + "location": [5147, 579, -50], "map": "Felucca", - "count": 3, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, + "homeRange": 7, "walkingRange": 25, "entries": [ - { "name": "Ghoul", "maxCount": 2, "probability": 100 }, - { "name": "Lich", "maxCount": 1, "probability": 100 } + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "350cfc3c-1c89-463d-a93a-f2ab22149d19", + "type": "Spawner", + "guid": "ab35ec53-2a16-4cdd-a2a1-f1b84922e36c", "name": "Spawner (204)", - "location": [5323, 571, 0], + "location": [5147, 598, -50], "map": "Felucca", - "count": 3, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 2, - "walkingRange": 2, + "homeRange": 7, + "walkingRange": 25, "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "35acdddb-eef4-4687-b6d7-6dea19535b71", + "type": "Spawner", + "guid": "d9f4d863-626f-480c-968a-9630e6a9a83d", "name": "Spawner (204)", - "location": [5314, 748, -20], + "location": [5145, 622, -50], "map": "Felucca", - "count": 9, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 8, "walkingRange": 25, "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }, - { "name": "Lich", "maxCount": 3, "probability": 100 }, - { "name": "LichLord", "maxCount": 1, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d25517a-5b90-4468-9e23-4041f88774e2", - "name": "Spawner (204)", - "location": [5299, 603, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } + { "name": "Zombie", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "56939d4e-8a5d-47da-a117-e7f56ff6671b", + "type": "Spawner", + "guid": "cb79321c-1a80-4881-8961-1997174d0458", "name": "Spawner (204)", - "location": [5151, 718, 0], + "location": [5212, 531, 0], "map": "Felucca", - "count": 6, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, + "homeRange": 8, "walkingRange": 25, "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Lich", "maxCount": 3, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "684234a4-21ea-4f94-897b-e92d88a17ced", "name": "Spawner (204)", "location": [5219, 552, 0], @@ -221,7 +168,117 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "0b6e0a59-f918-4f77-8bc8-f5b5d944d3a5", + "name": "Spawner (204)", + "location": [5219, 573, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f6b68da6-e7f9-4cb1-870c-079ebff4787b", + "name": "Spawner (204)", + "location": [5145, 618, -50], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1cbfae9c-7cc7-45cc-842f-0e75a158cd8a", + "name": "Spawner (204)", + "location": [5307, 546, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4857128-1876-473c-951f-418107fad15c", + "name": "Spawner (204)", + "location": [5327, 542, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b00a63e-cf2d-495b-8bd9-61cef122f168", + "name": "Spawner (204)", + "location": [5286, 542, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2fc19bd-75c9-4f68-b59d-6efc65381167", + "name": "Spawner (204)", + "location": [5283, 583, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "7016863d-26dc-4d2e-beda-68031a966ebe", "name": "Spawner (204)", "location": [5318, 578, 0], @@ -241,23 +298,7 @@ ] }, { - "$type": "Spawner", - "guid": "750ebeff-4327-4bf0-b285-8c6749530985", - "name": "Spawner (204)", - "location": [5190, 655, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "79dfd73d-ede7-44e2-8651-94cfe5f77314", "name": "Spawner (204)", "location": [5342, 582, 0], @@ -274,266 +315,40 @@ ] }, { - "$type": "Spawner", - "guid": "818d1f8c-a5ba-4ef1-a823-2b04bca2f2e2", + "type": "Spawner", + "guid": "3d25517a-5b90-4468-9e23-4041f88774e2", "name": "Spawner (204)", - "location": [5320, 708, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8507dbe4-8220-4439-adbe-5ef6ebb0fec3", - "name": "Spawner (204)", - "location": [5186, 575, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 12, - "entries": [ - { "name": "WaterElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "88b5fff8-7124-4a19-83ad-465e8aaf072b", - "name": "Spawner (204)", - "location": [5150, 742, 0], + "location": [5299, 603, 0], "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 3, - "walkingRange": 15, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9862d38e-e12f-4e04-932f-0020f3121d9a", - "name": "Spawner (204)", - "location": [5145, 553, -40], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 35, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b00a63e-cf2d-495b-8bd9-61cef122f168", - "name": "Spawner (204)", - "location": [5286, 542, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a2d6e670-8282-4b60-bd5f-933e45d3d7a8", - "name": "Spawner (204)", - "location": [5170, 678, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, "homeRange": 10, "walkingRange": 25, "entries": [ { "name": "Ghoul", "maxCount": 1, "probability": 100 }, { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, { "name": "Wraith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ab35ec53-2a16-4cdd-a2a1-f1b84922e36c", + "type": "Spawner", + "guid": "fa668f86-9786-48bc-a676-5a879e77bee2", "name": "Spawner (204)", - "location": [5147, 598, -50], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c69344fe-718b-4e69-83b8-d12141250707", - "name": "Spawner (204)", - "location": [5219, 734, -20], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Lich", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cacb456a-f52d-4168-9d59-8df80779a1d6", - "name": "Spawner (204)", - "location": [5183, 727, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cb79321c-1a80-4881-8961-1997174d0458", - "name": "Spawner (204)", - "location": [5212, 531, 0], + "location": [5295, 623, -5], "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 8, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 } - ] + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "BoneMagi", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "d4857128-1876-473c-951f-418107fad15c", - "name": "Spawner (204)", - "location": [5327, 542, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d8ba7cc3-c1f6-4701-b684-850203cb9e37", - "name": "Spawner (204)", - "location": [5147, 579, -50], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9f4d863-626f-480c-968a-9630e6a9a83d", - "name": "Spawner (204)", - "location": [5145, 622, -50], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dbb8562b-7557-4347-9faa-a5facf1ba1d4", - "name": "Spawner (204)", - "location": [5280, 675, 8], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "edd28bb3-5aeb-487a-8571-9e4e4aea0783", - "name": "Spawner (204)", - "location": [5187, 603, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 18, - "walkingRange": 35, - "entries": [ - { "name": "Mummy", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 3, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "efc2564d-a6df-40a6-ad5e-6e6e81d066e9", "name": "Spawner (204)", "location": [5322, 626, 0], @@ -553,24 +368,24 @@ ] }, { - "$type": "Spawner", - "guid": "f2fc19bd-75c9-4f68-b59d-6efc65381167", + "type": "Spawner", + "guid": "350cfc3c-1c89-463d-a93a-f2ab22149d19", "name": "Spawner (204)", - "location": [5283, 583, 0], + "location": [5323, 571, 0], "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 25, + "homeRange": 2, + "walkingRange": 2, "entries": [ - { "name": "Ghoul", "maxCount": 2, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f5e81dcf-fa17-4d36-8aa1-e73496aad012", "name": "Spawner (204)", "location": [5139, 661, 0], @@ -589,52 +404,223 @@ ] }, { - "$type": "Spawner", - "guid": "f6b68da6-e7f9-4cb1-870c-079ebff4787b", + "type": "Spawner", + "guid": "750ebeff-4327-4bf0-b285-8c6749530985", "name": "Spawner (204)", - "location": [5145, 618, -50], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fa668f86-9786-48bc-a676-5a879e77bee2", - "name": "Spawner (204)", - "location": [5295, 623, -5], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 10, - "entries": [ - { "name": "BoneMagi", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fb409b68-fe6b-422f-bf2d-95c4a92f5e21", - "name": "Spawner (204)", - "location": [5187, 545, 0], + "location": [5190, 655, 0], "map": "Felucca", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 6, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a2d6e670-8282-4b60-bd5f-933e45d3d7a8", + "name": "Spawner (204)", + "location": [5170, 678, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, "walkingRange": 25, "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 } + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56939d4e-8a5d-47da-a117-e7f56ff6671b", + "name": "Spawner (204)", + "location": [5151, 718, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0be3858f-bd81-4a27-9a8d-3846c0df0603", + "name": "Spawner (204)", + "location": [5192, 695, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cacb456a-f52d-4168-9d59-8df80779a1d6", + "name": "Spawner (204)", + "location": [5183, 727, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05d2885b-2b4d-4c4e-907e-2fdbe4d54e69", + "name": "Spawner (204)", + "location": [5212, 674, -20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c69344fe-718b-4e69-83b8-d12141250707", + "name": "Spawner (204)", + "location": [5219, 734, -20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "88b5fff8-7124-4a19-83ad-465e8aaf072b", + "name": "Spawner (204)", + "location": [5150, 742, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a7f9024-7d0f-43a0-8721-52b6a3c27c8f", + "name": "Spawner (204)", + "location": [5307, 675, -20], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbb8562b-7557-4347-9faa-a5facf1ba1d4", + "name": "Spawner (204)", + "location": [5280, 675, 8], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "818d1f8c-a5ba-4ef1-a823-2b04bca2f2e2", + "name": "Spawner (204)", + "location": [5320, 708, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "35acdddb-eef4-4687-b6d7-6dea19535b71", + "name": "Spawner (204)", + "location": [5314, 748, -20], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "25b2761d-dc06-443e-b979-049c7f50abf6", + "name": "Spawner (204)", + "location": [5267, 690, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 35, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Despise.json b/Distribution/Data/Spawns/post-uoml/felucca/Despise.json similarity index 77% rename from Distribution/Data/Spawns/shared/felucca/Despise.json rename to Distribution/Data/Spawns/post-uoml/felucca/Despise.json index b0aede9f3..b4aa9c7b2 100644 --- a/Distribution/Data/Spawns/shared/felucca/Despise.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Despise.json @@ -1,443 +1,6 @@ [ { - "$type": "Spawner", - "guid": "04207c29-fe38-4d43-a08d-8645a3a312e8", - "name": "Spawner (205)", - "location": [5441, 680, 20], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "EarthElemental", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "05844644-3d42-4021-bb28-8114d8f7f064", - "name": "Spawner (205)", - "location": [5420, 746, 5], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 18, - "walkingRange": 25, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Ettin", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a6bf903-a9a3-4252-a4a0-652d89fb9381", - "name": "Spawner (205)", - "location": [5528, 902, 30], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ogre", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11e8cbb5-45dd-4109-8606-54eb9bada453", - "name": "Spawner (205)", - "location": [5460, 526, 60], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1855dd2e-81b1-4326-bc97-f3e50884e09a", - "name": "Spawner (205)", - "location": [5391, 905, 30], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1adcf3d5-536f-473d-af18-4ae23415ebba", - "name": "Spawner (205)", - "location": [5505, 747, 5], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 18, - "walkingRange": 25, - "entries": [ - { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21b9e0f9-7627-4967-b7bf-5453be3e0266", - "name": "Spawner (205)", - "location": [5470, 860, 45], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "310b722e-fe5d-4692-97c7-956d15b76472", - "name": "Spawner (205)", - "location": [5482, 791, 60], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "38967322-06dc-48bc-bed1-6dd60661b1c0", - "name": "Spawner (205)", - "location": [5387, 697, 15], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "38fe4fe8-2f5d-441f-8f4b-8f679a955e96", - "name": "Spawner (205)", - "location": [5546, 888, 30], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d6315d9-3968-4b92-b71a-99a12bab509e", - "name": "Spawner (205)", - "location": [5590, 871, 45], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 35, - "entries": [ - { "name": "Ogre", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "441cff6e-d90b-4a74-bd0a-b10165fd6eb4", - "name": "Spawner (205)", - "location": [5426, 943, 20], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4780e4d1-5a3a-4519-a8db-3cc6e77648c7", - "name": "Spawner (205)", - "location": [5398, 996, 5], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d00942a-33e2-4714-8d03-9d6a40778772", - "name": "Spawner (205)", - "location": [5390, 587, 45], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "51d69140-0708-491e-9dbd-9c5d58349172", - "name": "Spawner (205)", - "location": [5504, 939, 20], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5c5b2844-5792-434b-9d9e-e5f607f32b6b", - "name": "Spawner (205)", - "location": [5439, 851, 45], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6a01c304-d63d-4a59-8b00-d004329d1af7", - "name": "Spawner (205)", - "location": [5483, 711, 15], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b94a3e0-7977-459b-bc5f-6c13ce0062ff", - "name": "Spawner (205)", - "location": [5405, 576, 61], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "70c7a94c-7535-432d-917a-640d0a6f47ff", - "name": "Spawner (205)", - "location": [5605, 799, 60], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "74032d64-a50f-4f99-ad45-3d9ad9f1d718", - "name": "Spawner (205)", - "location": [5410, 611, 45], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "74fc3f59-8ee2-4ee5-b246-ddf07c415ca5", - "name": "Spawner (205)", - "location": [5557, 828, 45], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7573ab39-270e-45f0-99db-4304837ef7bb", - "name": "Spawner (205)", - "location": [5487, 902, 30], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c71f2a8-2d6b-40b8-8dca-9eca3aa54d47", - "name": "Spawner (205)", - "location": [5397, 995, 5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7ec31075-3cef-4d6b-a20e-2c55540052fc", - "name": "Spawner (205)", - "location": [5391, 660, 20], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "831675ca-f594-4e7e-bd6f-b0f15ed60bf0", - "name": "Spawner (205)", - "location": [5504, 597, 45], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8e65ea3f-2bca-43cd-b6e2-916b8625d69b", - "name": "Spawner (205)", - "location": [5398, 995, 5], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93356ad6-8774-42cd-ab96-705c2d55e109", - "name": "Spawner (205)", - "location": [5604, 789, 60], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ogre", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "9dbd8c5c-40d7-4908-8fbd-86cd1f149be0", "name": "Spawner (205)", "location": [5503, 529, 60], @@ -455,40 +18,63 @@ ] }, { - "$type": "Spawner", - "guid": "9df386c1-05f8-4290-960b-8d0b6f0393ce", + "type": "Spawner", + "guid": "831675ca-f594-4e7e-bd6f-b0f15ed60bf0", "name": "Spawner (205)", - "location": [5593, 794, 60], + "location": [5504, 597, 45], "map": "Felucca", - "count": 1, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "9e9bcade-4455-4c85-b7f2-4d9dc6e6c6f4", + "type": "Spawner", + "guid": "fb3ee679-fc60-4abb-b6b0-a809f77ca53b", "name": "Spawner (205)", - "location": [5394, 525, 65], + "location": [5464, 600, 45], "map": "Felucca", - "count": 3, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } - ] + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "f0922369-d73d-4c7e-a669-28812e0a0559", + "name": "Spawner (205)", + "location": [5435, 589, 47], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "74032d64-a50f-4f99-ad45-3d9ad9f1d718", + "name": "Spawner (205)", + "location": [5410, 611, 45], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "9f576fea-09aa-47fb-a3e4-a1b8369b650e", "name": "Spawner (205)", "location": [5386, 612, 45], @@ -506,40 +92,21 @@ ] }, { - "$type": "Spawner", - "guid": "b328679f-d212-49e8-8900-af2cd1f77b04", + "type": "Spawner", + "guid": "6b94a3e0-7977-459b-bc5f-6c13ce0062ff", "name": "Spawner (205)", - "location": [5564, 821, 45], + "location": [5405, 576, 61], "map": "Felucca", - "count": 2, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "b453194c-94e8-4d58-87cc-3870d6292247", - "name": "Spawner (205)", - "location": [5507, 656, 20], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "ba7fd9a9-bffc-4f04-8606-ae79b4bb830b", "name": "Spawner (205)", "location": [5411, 541, 60], @@ -550,28 +117,103 @@ "team": 0, "homeRange": 12, "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11e8cbb5-45dd-4109-8606-54eb9bada453", + "name": "Spawner (205)", + "location": [5460, 526, 60], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9e9bcade-4455-4c85-b7f2-4d9dc6e6c6f4", + "name": "Spawner (205)", + "location": [5394, 525, 65], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "bb7fc3f7-d80d-44c5-8a63-743a970f5842", + "type": "Spawner", + "guid": "4d00942a-33e2-4714-8d03-9d6a40778772", "name": "Spawner (205)", - "location": [5385, 703, 15], + "location": [5390, 587, 45], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c99bb2a1-1690-4656-95d7-d61b0c5640c8", + "name": "Spawner (205)", + "location": [5490, 676, 20], "map": "Felucca", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6a01c304-d63d-4a59-8b00-d004329d1af7", + "name": "Spawner (205)", + "location": [5483, 711, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1adcf3d5-536f-473d-af18-4ae23415ebba", + "name": "Spawner (205)", + "location": [5505, 747, 5], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, "walkingRange": 25, "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c045d499-1fe2-4afa-be89-e8e5f3c6f4f8", "name": "Spawner (205)", "location": [5475, 748, 5], @@ -588,26 +230,41 @@ ] }, { - "$type": "Spawner", - "guid": "c739ce37-7e4b-4a3a-886b-4f45a049cde6", + "type": "Spawner", + "guid": "05844644-3d42-4021-bb28-8114d8f7f064", "name": "Spawner (205)", - "location": [5558, 824, 45], + "location": [5420, 746, 5], "map": "Felucca", - "count": 5, + "count": 10, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, + "homeRange": 18, "walkingRange": 25, "entries": [ - { "name": "OgreLord", "maxCount": 5, "probability": 100 } + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ettin", "maxCount": 6, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "c99bb2a1-1690-4656-95d7-d61b0c5640c8", + "type": "Spawner", + "guid": "bb7fc3f7-d80d-44c5-8a63-743a970f5842", "name": "Spawner (205)", - "location": [5490, 676, 20], + "location": [5385, 703, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7ec31075-3cef-4d6b-a20e-2c55540052fc", + "name": "Spawner (205)", + "location": [5391, 660, 20], "map": "Felucca", "count": 4, "minDelay": "00:05:00", @@ -615,60 +272,86 @@ "team": 0, "homeRange": 12, "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "cb91b20f-4aee-45c7-b70b-de22c9adcdae", + "type": "Spawner", + "guid": "04207c29-fe38-4d43-a08d-8645a3a312e8", "name": "Spawner (205)", - "location": [5471, 973, 15], + "location": [5441, 680, 20], "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 18, - "walkingRange": 25, - "entries": [ - { "name": "Cyclops", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d58f315e-8fcf-4282-a4e4-5aaed9c19cd5", - "name": "Spawner (205)", - "location": [5532, 787, 60], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ogre", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f0922369-d73d-4c7e-a669-28812e0a0559", - "name": "Spawner (205)", - "location": [5435, 589, 47], - "map": "Felucca", - "count": 5, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 12, "walkingRange": 25, + "entries": [{ "name": "EarthElemental", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b453194c-94e8-4d58-87cc-3870d6292247", + "name": "Spawner (205)", + "location": [5507, 656, 20], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "38967322-06dc-48bc-bed1-6dd60661b1c0", + "name": "Spawner (205)", + "location": [5387, 697, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5c5b2844-5792-434b-9d9e-e5f607f32b6b", + "name": "Spawner (205)", + "location": [5439, 851, 45], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21b9e0f9-7627-4967-b7bf-5453be3e0266", + "name": "Spawner (205)", + "location": [5470, 860, 45], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "f8f62c01-5bf8-41dc-ac7a-37cc7a97134b", "name": "Spawner (205)", "location": [5426, 791, 60], @@ -679,15 +362,55 @@ "team": 0, "homeRange": 12, "walkingRange": 25, - "entries": [ - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "fb3ee679-fc60-4abb-b6b0-a809f77ca53b", + "type": "Spawner", + "guid": "310b722e-fe5d-4692-97c7-956d15b76472", "name": "Spawner (205)", - "location": [5464, 600, 45], + "location": [5482, 791, 60], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d58f315e-8fcf-4282-a4e4-5aaed9c19cd5", + "name": "Spawner (205)", + "location": [5532, 787, 60], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93356ad6-8774-42cd-ab96-705c2d55e109", + "name": "Spawner (205)", + "location": [5604, 789, 60], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c739ce37-7e4b-4a3a-886b-4f45a049cde6", + "name": "Spawner (205)", + "location": [5558, 824, 45], "map": "Felucca", "count": 5, "minDelay": "00:05:00", @@ -695,8 +418,219 @@ "team": 0, "homeRange": 12, "walkingRange": 25, + "entries": [{ "name": "OgreLord", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3d6315d9-3968-4b92-b71a-99a12bab509e", + "name": "Spawner (205)", + "location": [5590, 871, 45], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0a6bf903-a9a3-4252-a4a0-652d89fb9381", + "name": "Spawner (205)", + "location": [5528, 902, 30], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7573ab39-270e-45f0-99db-4304837ef7bb", + "name": "Spawner (205)", + "location": [5487, 902, 30], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "51d69140-0708-491e-9dbd-9c5d58349172", + "name": "Spawner (205)", + "location": [5504, 939, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb91b20f-4aee-45c7-b70b-de22c9adcdae", + "name": "Spawner (205)", + "location": [5471, 973, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 25, + "entries": [{ "name": "Cyclops", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "441cff6e-d90b-4a74-bd0a-b10165fd6eb4", + "name": "Spawner (205)", + "location": [5426, 943, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1855dd2e-81b1-4326-bc97-f3e50884e09a", + "name": "Spawner (205)", + "location": [5391, 905, 30], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4780e4d1-5a3a-4519-a8db-3cc6e77648c7", + "name": "Spawner (205)", + "location": [5398, 996, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "AcidElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9df386c1-05f8-4290-960b-8d0b6f0393ce", + "name": "Spawner (205)", + "location": [5593, 794, 60], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "38fe4fe8-2f5d-441f-8f4b-8f679a955e96", + "name": "Spawner (205)", + "location": [5546, 888, 30], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "7c71f2a8-2d6b-40b8-8dca-9eca3aa54d47", + "name": "Spawner (205)", + "location": [5397, 995, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70c7a94c-7535-432d-917a-640d0a6f47ff", + "name": "Spawner (205)", + "location": [5605, 799, 60], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "74fc3f59-8ee2-4ee5-b246-ddf07c415ca5", + "name": "Spawner (205)", + "location": [5557, 828, 45], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e65ea3f-2bca-43cd-b6e2-916b8625d69b", + "name": "Spawner (205)", + "location": [5398, 995, 5], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b328679f-d212-49e8-8900-af2cd1f77b04", + "name": "Spawner (205)", + "location": [5564, 821, 45], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Destard.json b/Distribution/Data/Spawns/post-uoml/felucca/Destard.json similarity index 89% rename from Distribution/Data/Spawns/shared/felucca/Destard.json rename to Distribution/Data/Spawns/post-uoml/felucca/Destard.json index 7559f0397..e72f521e7 100644 --- a/Distribution/Data/Spawns/shared/felucca/Destard.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Destard.json @@ -1,399 +1,6 @@ [ { - "$type": "Spawner", - "guid": "0fbce341-ca59-4ec4-a718-cc8f7b3ed34e", - "name": "Spawner (206)", - "location": [5247, 956, -40], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "171e7677-5e0b-4da4-8599-9c3c16200e7a", - "name": "Spawner (206)", - "location": [5141, 968, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "326c64f4-34d3-414d-a79c-c7bb9d441e96", - "name": "Spawner (206)", - "location": [5149, 907, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "Wyvern", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e318f8e-aec0-4b16-b435-43dc4fde4372", - "name": "Spawner (206)", - "location": [5241, 945, -40], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "48b37663-ddee-4f04-978d-35f4c085f785", - "name": "Spawner (206)", - "location": [5269, 811, 7], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4f8eb77e-64cb-4846-8c0f-0816f16c94b3", - "name": "Spawner (206)", - "location": [5352, 934, -5], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "WaterElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "506ce404-853e-4168-9b6b-1296d40c90e3", - "name": "Spawner (206)", - "location": [5183, 1007, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "59c245e9-e59f-4f6c-b3cb-2add65aba939", - "name": "Spawner (206)", - "location": [5203, 787, 5], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "WaterElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "79707431-23e6-4347-a89e-7bc970162938", - "name": "Spawner (206)", - "location": [5280, 955, -40], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7dfd256b-c903-4a4f-abf1-bdce7ff2217b", - "name": "Spawner (206)", - "location": [5284, 926, -40], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "831f9d08-398c-42b0-bc53-72e1c0685e34", - "name": "Spawner (206)", - "location": [5294, 842, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "WaterElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "87114b5e-ac9d-4d7f-90c1-c6db7a570c2f", - "name": "Spawner (206)", - "location": [5226, 826, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "889ccd57-8eda-4910-9790-2dc3bc80ca99", - "name": "Spawner (206)", - "location": [5214, 917, -40], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8b681dd7-a815-4513-85e6-901967f4e6ed", - "name": "Spawner (206)", - "location": [5143, 986, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "936bde4a-dce6-44bb-859c-81a3e1f0a87b", - "name": "Spawner (206)", - "location": [5307, 815, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9641ac5e-6eec-4f35-8536-226265c67ace", - "name": "Spawner (206)", - "location": [5205, 778, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad446354-3f49-4c9d-ab1d-dea29378794e", - "name": "Spawner (206)", - "location": [5157, 997, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 50, - "entries": [ - { "name": "Wyvern", "maxCount": 3, "probability": 100 }, - { "name": "Wyvern", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b6125064-8514-4c32-953d-c28eca82fa4e", - "name": "Spawner (206)", - "location": [5229, 841, 1], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "be7b487d-c201-413a-94c8-8201dc3a1c4d", - "name": "Spawner (206)", - "location": [5141, 834, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "ShadowWyrm", "maxCount": 1, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d045c909-34f1-46a6-8d61-7275dc6e45c9", - "name": "Spawner (206)", - "location": [5317, 980, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d8aceeb2-7644-4b3d-96de-4e172f41abd6", - "name": "Spawner (206)", - "location": [5153, 842, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 4, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e2bea874-3646-4dec-8fc9-cbc0c67f2a0a", - "name": "Spawner (206)", - "location": [5250, 869, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e6703676-d851-4394-b09f-51286d5d85f2", - "name": "Spawner (206)", - "location": [5185, 1006, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 50, - "entries": [ - { "name": "AncientWyrm", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e8017d92-b455-4ab2-b484-f95b2957b3be", "name": "Spawner (206)", "location": [5209, 965, -40], @@ -411,10 +18,10 @@ ] }, { - "$type": "Spawner", - "guid": "edd4aa65-92a9-491c-97e8-d9906e31fbea", + "type": "Spawner", + "guid": "0fbce341-ca59-4ec4-a718-cc8f7b3ed34e", "name": "Spawner (206)", - "location": [5169, 836, 0], + "location": [5247, 956, -40], "map": "Felucca", "count": 3, "minDelay": "00:05:00", @@ -423,12 +30,115 @@ "homeRange": 30, "walkingRange": 50, "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "EvilMage", "maxCount": 1, "probability": 100 } + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "79707431-23e6-4347-a89e-7bc970162938", + "name": "Spawner (206)", + "location": [5280, 955, -40], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7dfd256b-c903-4a4f-abf1-bdce7ff2217b", + "name": "Spawner (206)", + "location": [5284, 926, -40], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "889ccd57-8eda-4910-9790-2dc3bc80ca99", + "name": "Spawner (206)", + "location": [5214, 917, -40], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e318f8e-aec0-4b16-b435-43dc4fde4372", + "name": "Spawner (206)", + "location": [5241, 945, -40], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5973fd2-89b7-405a-bdea-4b1f2fafa92d", + "name": "Spawner (206)", + "location": [5161, 940, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [{ "name": "GiantSerpent", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "326c64f4-34d3-414d-a79c-c7bb9d441e96", + "name": "Spawner (206)", + "location": [5149, 907, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "ef6d6675-0683-47e4-b236-bfc890a12227", "name": "Spawner (206)", "location": [5141, 915, 0], @@ -445,7 +155,199 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "4f8eb77e-64cb-4846-8c0f-0816f16c94b3", + "name": "Spawner (206)", + "location": [5352, 934, -5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d045c909-34f1-46a6-8d61-7275dc6e45c9", + "name": "Spawner (206)", + "location": [5317, 980, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2bea874-3646-4dec-8fc9-cbc0c67f2a0a", + "name": "Spawner (206)", + "location": [5250, 869, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "GiantSerpent", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "87114b5e-ac9d-4d7f-90c1-c6db7a570c2f", + "name": "Spawner (206)", + "location": [5226, 826, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "48b37663-ddee-4f04-978d-35f4c085f785", + "name": "Spawner (206)", + "location": [5269, 811, 7], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "936bde4a-dce6-44bb-859c-81a3e1f0a87b", + "name": "Spawner (206)", + "location": [5307, 815, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "831f9d08-398c-42b0-bc53-72e1c0685e34", + "name": "Spawner (206)", + "location": [5294, 842, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "59c245e9-e59f-4f6c-b3cb-2add65aba939", + "name": "Spawner (206)", + "location": [5203, 787, 5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b6125064-8514-4c32-953d-c28eca82fa4e", + "name": "Spawner (206)", + "location": [5229, 841, 1], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9641ac5e-6eec-4f35-8536-226265c67ace", + "name": "Spawner (206)", + "location": [5205, 778, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "be7b487d-c201-413a-94c8-8201dc3a1c4d", + "name": "Spawner (206)", + "location": [5141, 834, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "ShadowWyrm", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "edd4aa65-92a9-491c-97e8-d9906e31fbea", + "name": "Spawner (206)", + "location": [5169, 836, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "EvilMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f2aa7cb3-ff27-4ea9-a2a4-fe62a0e78b76", "name": "Spawner (206)", "location": [5152, 869, 0], @@ -463,19 +365,101 @@ ] }, { - "$type": "Spawner", - "guid": "f5973fd2-89b7-405a-bdea-4b1f2fafa92d", + "type": "Spawner", + "guid": "d8aceeb2-7644-4b3d-96de-4e172f41abd6", "name": "Spawner (206)", - "location": [5161, 940, 0], + "location": [5153, 842, 0], "map": "Felucca", - "count": 6, + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "TreasureChestLevel4", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "171e7677-5e0b-4da4-8599-9c3c16200e7a", + "name": "Spawner (206)", + "location": [5141, 968, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ad446354-3f49-4c9d-ab1d-dea29378794e", + "name": "Spawner (206)", + "location": [5157, 997, 0], + "map": "Felucca", + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 20, "walkingRange": 50, "entries": [ - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 } + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6703676-d851-4394-b09f-51286d5d85f2", + "name": "Spawner (206)", + "location": [5185, 1006, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [{ "name": "AncientWyrm", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8b681dd7-a815-4513-85e6-901967f4e6ed", + "name": "Spawner (206)", + "location": [5143, 986, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "506ce404-853e-4168-9b6b-1296d40c90e3", + "name": "Spawner (206)", + "location": [5183, 1007, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/post-uoml/felucca/FelCropsLS.json b/Distribution/Data/Spawns/post-uoml/felucca/FelCropsLS.json new file mode 100644 index 000000000..2a31049f3 --- /dev/null +++ b/Distribution/Data/Spawns/post-uoml/felucca/FelCropsLS.json @@ -0,0 +1,290 @@ +[ + { + "type": "RegionSpawner", + "location": [375, 1191, 0], + "map": "Felucca", + "region": "A Wheatfield in Yew 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "bba64d97-11eb-4a87-b326-e41e3c9b355d", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [567, 1239, 0], + "map": "Felucca", + "region": "A Wheatfield in Yew 1", + "count": 5, + "entries": [{ "maxCount": 5, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "e37b4d74-c87e-4102-8c87-8f80c728fe0c", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [571, 1099, 0], + "map": "Felucca", + "region": "A Farm in Yew", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "aed692cc-2dac-4c80-931b-35c73ca8d092", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [675, 939, 0], + "map": "Felucca", + "region": "A Field of Sheep in Yew 1", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "425ac125-1597-41b4-9e6d-72b91801230b", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [675, 1179, 0], + "map": "Felucca", + "region": "A Field of Sheep in Yew 2", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "3d7925bf-2642-4185-bc03-fa314fb75a7f", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [813, 2163, 0], + "map": "Felucca", + "region": "A Wheatfield in Skara Brae 1", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "57da6436-6b16-4697-add5-f4b45776ccbc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2254, 0], + "map": "Felucca", + "region": "A Carrot Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "427451dd-93fa-43c9-85c6-01536066c8cc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2264, 0], + "map": "Felucca", + "region": "An Onion Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableOnion", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "640fe3f8-9917-4dfa-986d-038923163add", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2264, 0], + "map": "Felucca", + "region": "A Cabbage Field in Skara Brae 1", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "f4bccdc2-c663-4ccd-b7df-31656fa2ae09", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2274, 0], + "map": "Felucca", + "region": "A Cabbage Field in Skara Brae 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "0c48edf9-a88e-45f1-9ba3-cd8c919aefbc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2355, 0], + "map": "Felucca", + "region": "A Cotton Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCotton", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "e7b34039-ec1f-43c8-b64f-d7462ce7ae9d", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [843, 2352, 0], + "map": "Felucca", + "region": "A Wheatfield in Skara Brae 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "2bf378f0-77ed-44cd-8dfb-181d068f401e", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1120, 1624, 0], + "map": "Felucca", + "region": "A Wheatfield in Britain 4", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "e6a4aede-5a76-473d-8331-4bb73701ea1a", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1135, 1791, 0], + "map": "Felucca", + "region": "A Wheatfield in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "b29b54ab-5ae3-4ce3-93ac-326f324a34f6", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1152, 1576, 0], + "map": "Felucca", + "region": "A Wheatfield in Britain 5", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "e416e9fa-5a9a-426b-962e-50cdce64ca7f", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1183, 1683, 0], + "map": "Felucca", + "region": "A Cabbage Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "8536c19c-2036-495a-a3c5-e82c9f32f5ad", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1199, 1683, 0], + "map": "Felucca", + "region": "A Turnip Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableTurnip", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "d8a2e54e-4951-468f-93c5-376460070cba", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1199, 1823, 0], + "map": "Felucca", + "region": "A Wheatfield in Britain 2", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "58b14343-2032-4507-a913-6b536507c2e5", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1215, 1723, 0], + "map": "Felucca", + "region": "A Carrot Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "9805ef58-355f-4352-bfc4-43c6710ff409", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1216, 1604, 0], + "map": "Felucca", + "region": "A Turnip Field in Britain 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableTurnip", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "0c6481ab-3341-4113-ac1a-6523c19c881d", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1231, 1723, 0], + "map": "Felucca", + "region": "An Onion Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableOnion", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "57f955bb-49e1-4a66-a214-4fbddf8d572b", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1231, 1887, 0], + "map": "Felucca", + "region": "A Wheatfield in Britain 3", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "f68b6bc8-f822-4c7a-9d01-37fad25bc95b", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1232, 1604, 0], + "map": "Felucca", + "region": "A Carrot Field in Britain 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "7b09d4b5-fe63-4f95-8525-3c970c513adc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [4567, 1475, 0], + "map": "Felucca", + "region": "A Cotton Field in Moonglow", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCotton", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "4b4e3973-d836-4f7c-b9f7-5fb7d468da28", + "name": "Spawner" + } +] diff --git a/Distribution/Data/Spawns/shared/felucca/Fire.json b/Distribution/Data/Spawns/post-uoml/felucca/Fire.json similarity index 77% rename from Distribution/Data/Spawns/shared/felucca/Fire.json rename to Distribution/Data/Spawns/post-uoml/felucca/Fire.json index 12b6c7d33..2404569f9 100644 --- a/Distribution/Data/Spawns/shared/felucca/Fire.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Fire.json @@ -1,78 +1,52 @@ [ { - "$type": "Spawner", - "guid": "06b7ba24-826f-4130-b3fb-f367ac6e9a82", + "type": "Spawner", + "guid": "9df65008-f599-43d4-b788-df7138e37320", "name": "Spawner (207)", - "location": [5835, 1304, 0], + "location": [5693, 1415, 38], "map": "Felucca", - "count": 6, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 30, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "FireElemental", "maxCount": 4, "probability": 100 }, - { "name": "Efreet", "maxCount": 1, "probability": 100 }, - { "name": "LavaSerpent", "maxCount": 1, "probability": 100 } + { "name": "Slime", "maxCount": 1, "probability": 100 }, + { "name": "GiantRat", "maxCount": 2, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "09685cb4-df4f-4bd2-98fd-4ca2f401d65b", + "type": "Spawner", + "guid": "d77719be-10a6-40f5-97a0-95dc2e3137b4", "name": "Spawner (207)", - "location": [5643, 1435, 0], + "location": [5695, 1434, -1], "map": "Felucca", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 3, - "walkingRange": 5, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Slime", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "11550ec6-9ce8-4c50-a310-38383b51f230", + "type": "Spawner", + "guid": "a08f5c78-d35f-4755-a0f9-009660acb8c9", "name": "Spawner (207)", - "location": [5679, 1438, 0], + "location": [5680, 1438, 0], "map": "Felucca", - "count": 1, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "EvilMageLord", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "1d357111-cd98-4c2d-ab92-ead67f9da0e9", - "name": "Spawner (207)", - "location": [5651, 1363, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 5648, "y": 1360, "z": 0 }, - "end": { "x": 5654, "y": 1366, "z": 15 } - }, - "walkingRange": 3, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 }, - { "name": "HellHound", "maxCount": 5, "probability": 100 }, - { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "1da36d4f-d8c9-4202-8ce0-b8456969fbe7", "name": "Spawner (207)", "location": [5733, 1433, 22], @@ -90,26 +64,10 @@ ] }, { - "$type": "Spawner", - "guid": "1e8b71e5-2c17-4c4d-b933-b887a9b830b4", + "type": "Spawner", + "guid": "93908c67-1ffd-4196-bceb-b2ca2ec98e5d", "name": "Spawner (207)", - "location": [5852, 1468, 1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 30, - "entries": [ - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f24afdd-09b5-406c-90d6-c714c1811f4e", - "name": "Spawner (207)", - "location": [5859, 1387, 0], + "location": [5787, 1427, 17], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -117,212 +75,10 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "LavaLizard", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "LavaLizard", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "23349d09-37ca-4f61-ba48-b1a593d90c6a", - "name": "Spawner (207)", - "location": [5643, 1435, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "25415970-01ad-463e-b9f0-053c077a209f", - "name": "Spawner (207)", - "location": [5732, 1297, -1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 20, - "entries": [ - { "name": "HellHound", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2834e29b-b0b5-4f09-80ea-c0cc6cbdb5a6", - "name": "Spawner (207)", - "location": [5638, 1439, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 40, - "entries": [ - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e619be6-7384-415e-a23a-d7327b39a05e", - "name": "Spawner (207)", - "location": [5859, 1341, -1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a206916-74e5-44ec-8c78-bf01a8916f53", - "name": "Spawner (207)", - "location": [5731, 1379, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 7, - "entries": [ - { "name": "Lich", "maxCount": 3, "probability": 100 }, - { "name": "LichLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3bac45c2-6a16-4892-b25c-f6c02788a5f4", - "name": "Spawner (207)", - "location": [5723, 1291, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "436c4d7e-5dac-4f1f-a6bf-397e2e768683", - "name": "Spawner (207)", - "location": [5717, 1333, 6], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4f164c53-9758-4970-91ab-c4de29e3bfc2", - "name": "Spawner (207)", - "location": [5836, 1364, 1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "55d34b4f-fbf6-43a0-b8bb-5215ae74c2a6", - "name": "Spawner (207)", - "location": [5678, 1340, -2], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "HellHound", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5655a639-c25c-446b-9de9-30fb4d294143", - "name": "Spawner (207)", - "location": [5747, 1320, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "LavaSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5bb1395f-d446-42de-bcdf-d257d7ed953a", - "name": "Spawner (207)", - "location": [5667, 1315, 5], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 5647, "y": 1295, "z": -128 }, - "end": { "x": 5687, "y": 1335, "z": 20 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Efreet", "maxCount": 2, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6569aa86-8a7e-4a7d-8b0b-f46badca59c2", - "name": "Spawner (207)", - "location": [5656, 1412, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 10, - "entries": [ - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "7b6bd2f4-2d4f-4144-ac9a-e34b9d2f88b1", "name": "Spawner (207)", "location": [5717, 1465, -2], @@ -333,12 +89,29 @@ "team": 0, "homeRange": 10, "walkingRange": 15, + "entries": [{ "name": "FireElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b8f9386a-456c-44ff-9baa-0b8234e0c0c4", + "name": "Spawner (207)", + "location": [5744, 1471, 2], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, "entries": [ - { "name": "FireElemental", "maxCount": 3, "probability": 100 } + { "name": "FireElemental", "maxCount": 3, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "8524d599-a31d-4fb0-977e-0cff77d2d297", "name": "Spawner (207)", "location": [5790, 1451, 7], @@ -357,153 +130,7 @@ ] }, { - "$type": "Spawner", - "guid": "88f058cc-ebdb-48bd-908a-cbb49142fd11", - "name": "Spawner (207)", - "location": [5709, 1364, -1], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "HellHound", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8f08a4ce-78dd-48ca-81e4-1e6d4d35d15e", - "name": "Spawner (207)", - "location": [5714, 1465, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "90609dc2-cd78-4057-9b87-6023fc1fa263", - "name": "Spawner (207)", - "location": [5643, 1403, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9157324f-0d09-409e-8ace-f0c4dcb44683", - "name": "Spawner (207)", - "location": [5699, 1302, 1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "934c223f-19e3-401c-aaf9-494c751d8c1c", - "name": "Spawner (207)", - "location": [5689, 1387, 1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93908c67-1ffd-4196-bceb-b2ca2ec98e5d", - "name": "Spawner (207)", - "location": [5787, 1427, 17], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "LavaLizard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9df65008-f599-43d4-b788-df7138e37320", - "name": "Spawner (207)", - "location": [5693, 1415, 38], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Slime", "maxCount": 1, "probability": 100 }, - { "name": "GiantRat", "maxCount": 2, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a08f5c78-d35f-4755-a0f9-009660acb8c9", - "name": "Spawner (207)", - "location": [5680, 1438, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EvilMageLord", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a874fb64-451c-4f30-ab6f-e56a9eb71604", - "name": "Spawner (207)", - "location": [5787, 1386, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "HellCat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "b4c87574-6489-4e20-8642-61051697b2e1", "name": "Spawner (207)", "location": [5852, 1468, 1], @@ -525,139 +152,49 @@ ] }, { - "$type": "Spawner", - "guid": "b8f9386a-456c-44ff-9baa-0b8234e0c0c4", + "type": "Spawner", + "guid": "1e8b71e5-2c17-4c4d-b933-b887a9b830b4", "name": "Spawner (207)", - "location": [5744, 1471, 2], + "location": [5852, 1468, 1], "map": "Felucca", - "count": 6, + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f24afdd-09b5-406c-90d6-c714c1811f4e", + "name": "Spawner (207)", + "location": [5859, 1387, 0], + "map": "Felucca", + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "FireElemental", "maxCount": 3, "probability": 100 }, - { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 }, - { "name": "HellHound", "maxCount": 1, "probability": 100 } - ] + "walkingRange": 20, + "entries": [{ "name": "LavaLizard", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "bb0ec9be-567e-46d8-9045-c498fc51972d", + "type": "Spawner", + "guid": "a874fb64-451c-4f30-ab6f-e56a9eb71604", "name": "Spawner (207)", - "location": [5723, 1390, 2], + "location": [5787, 1386, -2], "map": "Felucca", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HellCat", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "be64cf9e-3d3a-4e25-9162-50fa7a777f44", - "name": "Spawner (207)", - "location": [5722, 1390, 1], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2f7684e-9246-4b09-b224-f993c15173b9", - "name": "Spawner (207)", - "location": [5650, 1434, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c3250304-df94-4c9d-8a15-e9efa6845b65", - "name": "Spawner (207)", - "location": [5679, 1439, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c60b60b7-79f5-4a77-b9af-c4a9b49d87f1", - "name": "Spawner (207)", - "location": [5651, 1408, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 3, - "entries": [ - { "name": "HellHound", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c701efc3-54d7-40d9-b3d0-909ded6a74d6", - "name": "Spawner (207)", - "location": [5651, 1418, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 3, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c729c34e-7162-442e-a0bc-0e8b1ca89720", - "name": "Spawner (207)", - "location": [5651, 1435, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 5, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "ce7b5441-9c5e-4426-9ff8-09085c48f899", "name": "Spawner (207)", "location": [5818, 1379, 0], @@ -668,12 +205,24 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "4f164c53-9758-4970-91ab-c4de29e3bfc2", + "name": "Spawner (207)", + "location": [5836, 1364, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "d009fa2f-ee2e-4adf-828d-917c991f5c8d", "name": "Spawner (207)", "location": [5809, 1332, 0], @@ -684,63 +233,13 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "d00cb0e2-efb1-47c3-a65f-079f2677219c", + "type": "Spawner", + "guid": "2e619be6-7384-415e-a23a-d7327b39a05e", "name": "Spawner (207)", - "location": [5654, 1380, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d114e342-f07c-46b9-9164-43bafbd74988", - "name": "Spawner (207)", - "location": [5651, 1402, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 3, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d11e7abf-7396-4fa4-a7a1-8dd69b700907", - "name": "Spawner (207)", - "location": [5691, 1347, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d77719be-10a6-40f5-97a0-95dc2e3137b4", - "name": "Spawner (207)", - "location": [5695, 1434, -1], + "location": [5859, 1341, -1], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -748,31 +247,28 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "Slime", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "d7e70c50-8b14-4153-b44f-a9f6764762f0", + "type": "Spawner", + "guid": "06b7ba24-826f-4130-b3fb-f367ac6e9a82", "name": "Spawner (207)", - "location": [5650, 1419, 22], + "location": [5835, 1304, 0], "map": "Felucca", - "count": 1, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 5648, "y": 1417, "z": 22 }, - "end": { "x": 5652, "y": 1421, "z": 40 } - }, - "walkingRange": 5, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "Efreet", "maxCount": 1, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "dfe6b8e9-4b85-4f76-a60f-47ae91fd38fa", "name": "Spawner (207)", "location": [5867, 1309, 0], @@ -789,7 +285,296 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "9157324f-0d09-409e-8ace-f0c4dcb44683", + "name": "Spawner (207)", + "location": [5699, 1302, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3bac45c2-6a16-4892-b25c-f6c02788a5f4", + "name": "Spawner (207)", + "location": [5723, 1291, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "25415970-01ad-463e-b9f0-053c077a209f", + "name": "Spawner (207)", + "location": [5732, 1297, -1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 20, + "entries": [{ "name": "HellHound", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5bb1395f-d446-42de-bcdf-d257d7ed953a", + "name": "Spawner (207)", + "location": [5667, 1315, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Efreet", "maxCount": 2, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5655a639-c25c-446b-9de9-30fb4d294143", + "name": "Spawner (207)", + "location": [5747, 1320, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "436c4d7e-5dac-4f1f-a6bf-397e2e768683", + "name": "Spawner (207)", + "location": [5717, 1333, 6], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a206916-74e5-44ec-8c78-bf01a8916f53", + "name": "Spawner (207)", + "location": [5731, 1379, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [ + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88f058cc-ebdb-48bd-908a-cbb49142fd11", + "name": "Spawner (207)", + "location": [5709, 1364, -1], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HellHound", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d11e7abf-7396-4fa4-a7a1-8dd69b700907", + "name": "Spawner (207)", + "location": [5691, 1347, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Daemon", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "55d34b4f-fbf6-43a0-b8bb-5215ae74c2a6", + "name": "Spawner (207)", + "location": [5678, 1340, -2], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d357111-cd98-4c2d-ab92-ead67f9da0e9", + "name": "Spawner (207)", + "location": [5651, 1363, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 5, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d00cb0e2-efb1-47c3-a65f-079f2677219c", + "name": "Spawner (207)", + "location": [5654, 1380, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "934c223f-19e3-401c-aaf9-494c751d8c1c", + "name": "Spawner (207)", + "location": [5689, 1387, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6569aa86-8a7e-4a7d-8b0b-f46badca59c2", + "name": "Spawner (207)", + "location": [5656, 1412, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 10, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d114e342-f07c-46b9-9164-43bafbd74988", + "name": "Spawner (207)", + "location": [5651, 1402, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "90609dc2-cd78-4057-9b87-6023fc1fa263", + "name": "Spawner (207)", + "location": [5643, 1403, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c60b60b7-79f5-4a77-b9af-c4a9b49d87f1", + "name": "Spawner (207)", + "location": [5651, 1408, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [{ "name": "HellHound", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c701efc3-54d7-40d9-b3d0-909ded6a74d6", + "name": "Spawner (207)", + "location": [5651, 1418, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d7e70c50-8b14-4153-b44f-a9f6764762f0", + "name": "Spawner (207)", + "location": [5650, 1419, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "e6a0e881-cdbe-4fe2-98dd-1d63debf8e1b", "name": "Spawner (207)", "location": [5647, 1439, 22], @@ -800,12 +585,10 @@ "team": 0, "homeRange": 1, "walkingRange": 15, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f17e6176-bbdd-4861-bd51-8aa58d4de7f9", "name": "Spawner (207)", "location": [5651, 1443, 0], @@ -816,12 +599,94 @@ "team": 0, "homeRange": 3, "walkingRange": 5, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "c729c34e-7162-442e-a0bc-0e8b1ca89720", + "name": "Spawner (207)", + "location": [5651, 1435, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "09685cb4-df4f-4bd2-98fd-4ca2f401d65b", + "name": "Spawner (207)", + "location": [5643, 1435, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2834e29b-b0b5-4f09-80ea-c0cc6cbdb5a6", + "name": "Spawner (207)", + "location": [5638, 1439, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 40, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8f08a4ce-78dd-48ca-81e4-1e6d4d35d15e", + "name": "Spawner (207)", + "location": [5714, 1465, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bb0ec9be-567e-46d8-9045-c498fc51972d", + "name": "Spawner (207)", + "location": [5723, 1390, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11550ec6-9ce8-4c50-a310-38383b51f230", + "name": "Spawner (207)", + "location": [5679, 1438, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "f3828caa-6ea3-40b5-86a0-21808b5eb4ea", "name": "Spawner (207)", "location": [5715, 1465, -1], @@ -832,8 +697,62 @@ "team": 0, "homeRange": 7, "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "be64cf9e-3d3a-4e25-9162-50fa7a777f44", + "name": "Spawner (207)", + "location": [5722, 1390, 1], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c3250304-df94-4c9d-8a15-e9efa6845b65", + "name": "Spawner (207)", + "location": [5679, 1439, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2f7684e-9246-4b09-b224-f993c15173b9", + "name": "Spawner (207)", + "location": [5650, 1434, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "23349d09-37ca-4f61-ba48-b1a593d90c6a", + "name": "Spawner (207)", + "location": [5643, 1435, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Graveyards.json b/Distribution/Data/Spawns/post-uoml/felucca/Graveyards.json similarity index 79% rename from Distribution/Data/Spawns/shared/felucca/Graveyards.json rename to Distribution/Data/Spawns/post-uoml/felucca/Graveyards.json index 5d1bb99a3..f5a133e71 100644 --- a/Distribution/Data/Spawns/shared/felucca/Graveyards.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Graveyards.json @@ -1,110 +1,6 @@ [ { - "$type": "Spawner", - "guid": "04856160-7e89-4672-a7d9-468b8634c3d6", - "name": "Spawner (208)", - "location": [4545, 1317, 8], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 4535, "y": 1307, "z": -128 }, - "end": { "x": 4555, "y": 1327, "z": 25 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "14b45cdf-987e-4dff-9e9f-10e90e3662e6", - "name": "Spawner (208)", - "location": [2758, 867, 0], - "map": "Felucca", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 2738, "y": 847, "z": -128 }, - "end": { "x": 2778, "y": 887, "z": 15 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Spectre", "maxCount": 2, "probability": 100 }, - { "name": "Wraith", "maxCount": 2, "probability": 100 }, - { "name": "Skeleton", "maxCount": 3, "probability": 100 }, - { "name": "Zombie", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "43d53b7d-95f9-4332-8d22-33e5f5a1fcf6", - "name": "Spawner (208)", - "location": [722, 1119, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47aa8449-5b66-401f-ac38-c5b6538627b6", - "name": "Spawner (208)", - "location": [4543, 1314, 8], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 4533, "y": 1304, "z": -128 }, - "end": { "x": 4553, "y": 1324, "z": 25 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "522530f4-50ea-4fbf-9119-b783bd955678", - "name": "Spawner (208)", - "location": [2438, 1100, 8], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 2428, "y": 1090, "z": -128 }, - "end": { "x": 2448, "y": 1110, "z": 30 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Lich", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "7dfee8fd-49f0-457d-a88c-f38c65bf2474", "name": "Spawner (208)", "location": [1369, 1475, 10], @@ -123,7 +19,115 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "b3ba766a-4852-4c5e-adf1-0276e193317f", + "name": "Spawner (208)", + "location": [1285, 3731, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Spectre", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "Shade", "maxCount": 2, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "522530f4-50ea-4fbf-9119-b783bd955678", + "name": "Spawner (208)", + "location": [2438, 1100, 8], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04856160-7e89-4672-a7d9-468b8634c3d6", + "name": "Spawner (208)", + "location": [4545, 1317, 8], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47aa8449-5b66-401f-ac38-c5b6538627b6", + "name": "Spawner (208)", + "location": [4543, 1314, 8], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "14b45cdf-987e-4dff-9e9f-10e90e3662e6", + "name": "Spawner (208)", + "location": [2758, 867, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Spectre", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "43d53b7d-95f9-4332-8d22-33e5f5a1fcf6", + "name": "Spawner (208)", + "location": [722, 1119, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "8bdfe6b3-df80-48b6-8d1e-8e052efde18b", "name": "Spawner (208)", "location": [3407, 2652, 48], @@ -138,28 +142,5 @@ { "name": "Zombie", "maxCount": 4, "probability": 100 }, { "name": "Skeleton", "maxCount": 4, "probability": 100 } ] - }, - { - "$type": "Spawner", - "guid": "b3ba766a-4852-4c5e-adf1-0276e193317f", - "name": "Spawner (208)", - "location": [1285, 3731, 0], - "map": "Felucca", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 1275, "y": 3721, "z": -128 }, - "end": { "x": 1295, "y": 3741, "z": 15 } - }, - "walkingRange": 15, - "entries": [ - { "name": "Spectre", "maxCount": 2, "probability": 100 }, - { "name": "Wraith", "maxCount": 2, "probability": 100 }, - { "name": "Shade", "maxCount": 2, "probability": 100 }, - { "name": "Skeleton", "maxCount": 3, "probability": 100 }, - { "name": "Zombie", "maxCount": 4, "probability": 100 } - ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Hythloth.json b/Distribution/Data/Spawns/post-uoml/felucca/Hythloth.json similarity index 78% rename from Distribution/Data/Spawns/shared/felucca/Hythloth.json rename to Distribution/Data/Spawns/post-uoml/felucca/Hythloth.json index 8f5b1a501..80dc937fe 100644 --- a/Distribution/Data/Spawns/shared/felucca/Hythloth.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Hythloth.json @@ -1,514 +1,6 @@ [ { - "$type": "Spawner", - "guid": "07267c9b-1455-47ec-885c-418388fe5df6", - "name": "Spawner (209)", - "location": [6125, 221, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0e5f0f20-5cc7-4e18-87d9-93736f1d71b7", - "name": "Spawner (209)", - "location": [6116, 86, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "FireGargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15eda36a-c00b-49dc-bfe3-a50d92123c2f", - "name": "Spawner (209)", - "location": [5929, 77, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "225cdfdd-2f24-4c49-87ef-0448507da17f", - "name": "Spawner (209)", - "location": [5997, 145, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "26ac95c2-5558-4077-9998-c5beeb7e8046", - "name": "Spawner (209)", - "location": [5914, 195, 22], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2acf18bc-021c-4853-a78e-fa673dbdd76b", - "name": "Spawner (209)", - "location": [5967, 80, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 6, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "317df32a-2992-4f4a-b978-87eee963d561", - "name": "Spawner (209)", - "location": [5918, 235, 44], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32f0f7d1-6145-48d7-a6bb-6a3015964780", - "name": "Spawner (209)", - "location": [6124, 159, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3302304b-f8a4-4638-9218-4bc883fb2811", - "name": "Spawner (209)", - "location": [6099, 51, 22], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33301994-da3d-4178-ae8c-f7db535784a0", - "name": "Spawner (209)", - "location": [5983, 169, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3421a293-1957-4a23-b050-910aaf9f211c", - "name": "Spawner (209)", - "location": [6033, 199, 22], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "349a17a0-eeeb-4b70-bd73-277509e44ac1", - "name": "Spawner (209)", - "location": [6081, 44, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37cd9071-b0bb-41f1-93e1-ee50487edc74", - "name": "Spawner (209)", - "location": [6119, 219, 22], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, - { "name": "Gazer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "39726a71-b66c-4013-b625-b32a8ba2d721", - "name": "Spawner (209)", - "location": [6041, 230, 44], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ba4ffaa-796e-4b04-a9d5-6884f33928ef", - "name": "Spawner (209)", - "location": [6064, 96, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3c0ee712-4025-43a7-9ec1-3821d758bfe5", - "name": "Spawner (209)", - "location": [6106, 67, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ce75ae6-093f-4f10-b5c1-272a9776c2d2", - "name": "Spawner (209)", - "location": [5978, 185, 44], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40e9cae3-14ce-4132-be18-0879d45cd22c", - "name": "Spawner (209)", - "location": [6086, 45, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4815a5f0-aca1-4f0e-88b5-ef8aa4df08c2", - "name": "Spawner (209)", - "location": [6033, 205, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d5d1ad7-e8ec-4072-aaa5-60148b6b1633", - "name": "Spawner (209)", - "location": [5911, 55, 22], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "TreasureChestLevel1", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50edd29c-ed4b-4321-a8d7-0b7561cc923b", - "name": "Spawner (209)", - "location": [6092, 187, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "53b614b7-1d3f-42dd-8be6-713057bcd618", - "name": "Spawner (209)", - "location": [6097, 36, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "53c930b6-9da5-4543-be7e-3fc576ef5dd2", - "name": "Spawner (209)", - "location": [5970, 80, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "55c08276-33f5-4a81-b29d-69f5c6fd6eb6", - "name": "Spawner (209)", - "location": [5954, 26, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "572ca10c-4892-4945-b7d4-2b9df1d51d13", - "name": "Spawner (209)", - "location": [6052, 156, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a00adac-4b86-4cde-8520-eb6b51d271ec", - "name": "Spawner (209)", - "location": [6060, 158, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5dfa30dc-1590-4e52-a339-0ead51e7dd8e", - "name": "Spawner (209)", - "location": [6113, 92, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5fdc3c1f-8471-405d-82f1-06f97369c994", - "name": "Spawner (209)", - "location": [5940, 107, 22], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6468fabe-a889-451f-abe7-932bee576a28", - "name": "Spawner (209)", - "location": [5958, 65, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "70564f6b-1725-414c-9ef4-d2402e6cfde8", - "name": "Spawner (209)", - "location": [6087, 186, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72a58090-313e-407c-88f3-4589706ce720", - "name": "Spawner (209)", - "location": [5948, 49, 22], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 1, "probability": 100 }, - { "name": "HellHound", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "73a4bf99-0ec3-40f5-b6c6-86abb6b5b574", "name": "Spawner (209)", "location": [5923, 48, 22], @@ -525,10 +17,27 @@ ] }, { - "$type": "Spawner", - "guid": "74881cf0-b5c7-4405-bcda-94b3d3baeb31", + "type": "Spawner", + "guid": "4d5d1ad7-e8ec-4072-aaa5-60148b6b1633", "name": "Spawner (209)", - "location": [5995, 62, 22], + "location": [5911, 55, 22], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel1", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72a58090-313e-407c-88f3-4589706ce720", + "name": "Spawner (209)", + "location": [5948, 49, 22], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -537,126 +46,29 @@ "homeRange": 10, "walkingRange": 20, "entries": [ - { "name": "FireGargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "74da3036-3d76-47ba-92ef-e2bf3b9e0ef4", - "name": "Spawner (209)", - "location": [5994, 96, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 }, { "name": "HellHound", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "7672fa03-92ce-4855-90ae-66082355c179", + "type": "Spawner", + "guid": "55c08276-33f5-4a81-b29d-69f5c6fd6eb6", "name": "Spawner (209)", - "location": [6117, 81, 0], + "location": [5954, 26, 0], "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7a2d0ba5-68fe-4672-88da-ecc1a8978c02", - "name": "Spawner (209)", - "location": [6107, 70, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b394cdd-416f-4d19-b797-2bfa1b7bcab6", - "name": "Spawner (209)", - "location": [5990, 36, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b88586b-18c3-45a4-a6de-900e6ceaa864", - "name": "Spawner (209)", - "location": [6108, 66, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7f5c5979-7038-41ed-b055-2d022c8b06e5", - "name": "Spawner (209)", - "location": [6054, 45, 0], - "map": "Felucca", - "count": 3, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 20, "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "Imp", "maxCount": 1, "probability": 100 }, - { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "80419ca6-262a-4219-852b-bebc9d4ed947", - "name": "Spawner (209)", - "location": [5989, 25, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "8193feec-c25b-484d-ae5e-ed08011bdd4f", "name": "Spawner (209)", "location": [5977, 49, 22], @@ -673,26 +85,10 @@ ] }, { - "$type": "Spawner", - "guid": "85e48021-924e-49bd-90e0-85b01b1021bc", + "type": "Spawner", + "guid": "74881cf0-b5c7-4405-bcda-94b3d3baeb31", "name": "Spawner (209)", - "location": [5989, 186, 44], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ee96e4d-2c92-49fa-b01a-40e19ece56d3", - "name": "Spawner (209)", - "location": [6081, 83, 21], + "location": [5995, 62, 22], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -700,49 +96,13 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "FireGargoyle", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "90cd0867-831d-46d8-8d07-8ae5d65b1e47", + "type": "Spawner", + "guid": "53c930b6-9da5-4543-be7e-3fc576ef5dd2", "name": "Spawner (209)", - "location": [5956, 145, 22], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "92a7ffa1-3c11-424a-a7af-bbb2dc49ac08", - "name": "Spawner (209)", - "location": [6064, 96, 22], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 1, "probability": 100 }, - { "name": "Imp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "92be9f7c-c84e-453f-8dc9-fe0080e20789", - "name": "Spawner (209)", - "location": [5915, 91, 0], + "location": [5970, 80, 0], "map": "Felucca", "count": 4, "minDelay": "00:05:00", @@ -756,318 +116,24 @@ ] }, { - "$type": "Spawner", - "guid": "93fdf44f-9a95-4a11-b7c2-2d558ca2d437", + "type": "Spawner", + "guid": "74da3036-3d76-47ba-92ef-e2bf3b9e0ef4", "name": "Spawner (209)", - "location": [6122, 170, 0], + "location": [5994, 96, 0], "map": "Felucca", - "count": 6, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 20, "entries": [ - { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9453078d-36c6-46c3-a5d7-7086780707b8", - "name": "Spawner (209)", - "location": [6041, 222, 44], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98d3590c-691c-41df-a0a5-9306c5fcb9f1", - "name": "Spawner (209)", - "location": [5985, 36, 22], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9bfd8f39-d27c-48e1-bfeb-0162f4881a2e", - "name": "Spawner (209)", - "location": [5986, 203, 44], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Gazer", "maxCount": 3, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a29d1eb0-e07b-4c26-99c9-27baf3edb014", - "name": "Spawner (209)", - "location": [5937, 65, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a2b3facc-dd13-4ecd-9a59-5d7cc88565a3", - "name": "Spawner (209)", - "location": [6108, 36, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a404b892-ba90-4ac7-be93-43460c5e53f0", - "name": "Spawner (209)", - "location": [5917, 93, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a7569f5b-dad0-419e-a784-af9b8bfc60b0", - "name": "Spawner (209)", - "location": [6086, 178, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, - { "name": "Gazer", "maxCount": 2, "probability": 100 } + { "name": "HellHound", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "a782d10c-59bd-4bd3-8eb9-2a78719fd9b4", - "name": "Spawner (209)", - "location": [5970, 169, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ab5876e4-9cba-4a92-afbe-9030402dcbf3", - "name": "Spawner (209)", - "location": [5945, 175, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b0687b35-95d6-48e1-b075-55fd1faa5063", - "name": "Spawner (209)", - "location": [5926, 230, 44], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b06bc9bb-e877-4958-a427-4df43a0748b9", - "name": "Spawner (209)", - "location": [5981, 84, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b34b407a-7f63-4257-bdc1-78f3163a5f75", - "name": "Spawner (209)", - "location": [5954, 225, 22], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3ea2162-f70f-40ba-92e0-0d1820c4f479", - "name": "Spawner (209)", - "location": [6046, 199, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b496ff0c-e786-4745-a5f2-15be500def57", - "name": "Spawner (209)", - "location": [5983, 25, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b90a3c30-9749-4223-9e81-9188a30c727d", - "name": "Spawner (209)", - "location": [5962, 217, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba8ccb47-e78a-441e-b626-7bd423077d3c", - "name": "Spawner (209)", - "location": [6058, 153, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0b89eb0-7bc5-47b2-9ff6-32f16b236742", - "name": "Spawner (209)", - "location": [6058, 92, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0c1350a-0f7f-4638-aa4f-04652af2a7da", - "name": "Spawner (209)", - "location": [5979, 26, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "cc62bdf5-4b80-4d0f-b505-da1072a83a35", "name": "Spawner (209)", "location": [5946, 75, 22], @@ -1084,10 +150,24 @@ ] }, { - "$type": "Spawner", - "guid": "ccd087df-54f2-4696-bc15-a3be48470aa5", + "type": "Spawner", + "guid": "5fdc3c1f-8471-405d-82f1-06f97369c994", "name": "Spawner (209)", - "location": [5918, 223, 44], + "location": [5940, 107, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92be9f7c-c84e-453f-8dc9-fe0080e20789", + "name": "Spawner (209)", + "location": [5915, 91, 0], "map": "Felucca", "count": 4, "minDelay": "00:05:00", @@ -1096,14 +176,15 @@ "homeRange": 10, "walkingRange": 20, "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 } + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "cd40cd10-9531-4ccf-bdfe-c92c83bc3bc3", + "type": "Spawner", + "guid": "7b394cdd-416f-4d19-b797-2bfa1b7bcab6", "name": "Spawner (209)", - "location": [5945, 78, 22], + "location": [5990, 36, 22], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1111,47 +192,27 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "d0d0c487-cfd5-4e29-a45d-f814967797fa", + "type": "Spawner", + "guid": "98d3590c-691c-41df-a0a5-9306c5fcb9f1", "name": "Spawner (209)", - "location": [5917, 156, 22], + "location": [5985, 36, 22], "map": "Felucca", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "da0917f7-f30c-453c-b767-6f825428eb59", + "type": "Spawner", + "guid": "80419ca6-262a-4219-852b-bebc9d4ed947", "name": "Spawner (209)", - "location": [6107, 37, 22], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "db6a7440-9c85-400f-9fbc-9ea9c8c69164", - "name": "Spawner (209)", - "location": [6121, 166, 0], + "location": [5989, 25, 22], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1159,15 +220,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "dd36a876-49a9-43df-b35a-a4ce32499d3d", + "type": "Spawner", + "guid": "b496ff0c-e786-4745-a5f2-15be500def57", "name": "Spawner (209)", - "location": [6120, 229, 22], + "location": [5983, 25, 22], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1175,12 +234,94 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "c0c1350a-0f7f-4638-aa4f-04652af2a7da", + "name": "Spawner (209)", + "location": [5979, 26, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b06bc9bb-e877-4958-a427-4df43a0748b9", + "name": "Spawner (209)", + "location": [5981, 84, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e5f7287c-0731-4b03-b76e-0970507df18d", + "name": "Spawner (209)", + "location": [5966, 70, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6468fabe-a889-451f-abe7-932bee576a28", + "name": "Spawner (209)", + "location": [5958, 65, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a29d1eb0-e07b-4c26-99c9-27baf3edb014", + "name": "Spawner (209)", + "location": [5937, 65, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e76e56fd-8b68-4535-b03d-4bed039daa55", + "name": "Spawner (209)", + "location": [5921, 81, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "de879ff4-9c86-4eec-b48c-959301ff83f3", "name": "Spawner (209)", "location": [5972, 86, 0], @@ -1191,12 +332,69 @@ "team": 0, "homeRange": 1, "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a404b892-ba90-4ac7-be93-43460c5e53f0", + "name": "Spawner (209)", + "location": [5917, 93, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "15eda36a-c00b-49dc-bfe3-a50d92123c2f", + "name": "Spawner (209)", + "location": [5929, 77, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cd40cd10-9531-4ccf-bdfe-c92c83bc3bc3", + "name": "Spawner (209)", + "location": [5945, 78, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2acf18bc-021c-4853-a78e-fa673dbdd76b", + "name": "Spawner (209)", + "location": [5967, 80, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e3d52e48-5710-4ba5-b239-e09a0cd5e872", "name": "Spawner (209)", "location": [5961, 169, 0], @@ -1213,7 +411,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e4ef1d65-a740-496e-a5b4-f85312633052", "name": "Spawner (209)", "location": [5992, 146, 0], @@ -1224,15 +422,30 @@ "team": 0, "homeRange": 10, "walkingRange": 20, + "entries": [{ "name": "Daemon", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab5876e4-9cba-4a92-afbe-9030402dcbf3", + "name": "Spawner (209)", + "location": [5945, 175, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 } + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e5b8d01c-2293-42c4-8ed0-4fffcc1e0aff", + "type": "Spawner", + "guid": "90cd0867-831d-46d8-8d07-8ae5d65b1e47", "name": "Spawner (209)", - "location": [6058, 193, 22], + "location": [5956, 145, 22], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -1240,15 +453,109 @@ "team": 0, "homeRange": 10, "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d0d0c487-cfd5-4e29-a45d-f814967797fa", + "name": "Spawner (209)", + "location": [5917, 156, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "26ac95c2-5558-4077-9998-c5beeb7e8046", + "name": "Spawner (209)", + "location": [5914, 195, 22], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e5f7287c-0731-4b03-b76e-0970507df18d", + "type": "Spawner", + "guid": "ccd087df-54f2-4696-bc15-a3be48470aa5", "name": "Spawner (209)", - "location": [5966, 70, 0], + "location": [5918, 223, 44], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "317df32a-2992-4f4a-b978-87eee963d561", + "name": "Spawner (209)", + "location": [5918, 235, 44], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b34b407a-7f63-4257-bdc1-78f3163a5f75", + "name": "Spawner (209)", + "location": [5954, 225, 22], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 20, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9bfd8f39-d27c-48e1-bfeb-0162f4881a2e", + "name": "Spawner (209)", + "location": [5986, 203, 44], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33301994-da3d-4178-ae8c-f7db535784a0", + "name": "Spawner (209)", + "location": [5983, 169, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1256,15 +563,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e76e56fd-8b68-4535-b03d-4bed039daa55", + "type": "Spawner", + "guid": "fb0d4918-8977-44d0-92d9-7e6267b62a7f", "name": "Spawner (209)", - "location": [5921, 81, 0], + "location": [5992, 147, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1272,12 +577,94 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "a782d10c-59bd-4bd3-8eb9-2a78719fd9b4", + "name": "Spawner (209)", + "location": [5970, 169, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "225cdfdd-2f24-4c49-87ef-0448507da17f", + "name": "Spawner (209)", + "location": [5997, 145, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ce75ae6-093f-4f10-b5c1-272a9776c2d2", + "name": "Spawner (209)", + "location": [5978, 185, 44], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b90a3c30-9749-4223-9e81-9188a30c727d", + "name": "Spawner (209)", + "location": [5962, 217, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "85e48021-924e-49bd-90e0-85b01b1021bc", + "name": "Spawner (209)", + "location": [5989, 186, 44], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b0687b35-95d6-48e1-b075-55fd1faa5063", + "name": "Spawner (209)", + "location": [5926, 230, 44], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "e978a387-2727-4c39-85c8-16b3c6f2969e", "name": "Spawner (209)", "location": [6094, 154, -22], @@ -1295,7 +682,310 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "93fdf44f-9a95-4a11-b7c2-2d558ca2d437", + "name": "Spawner (209)", + "location": [6122, 170, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7569f5b-dad0-419e-a784-af9b8bfc60b0", + "name": "Spawner (209)", + "location": [6086, 178, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37cd9071-b0bb-41f1-93e1-ee50487edc74", + "name": "Spawner (209)", + "location": [6119, 219, 22], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5b8d01c-2293-42c4-8ed0-4fffcc1e0aff", + "name": "Spawner (209)", + "location": [6058, 193, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3421a293-1957-4a23-b050-910aaf9f211c", + "name": "Spawner (209)", + "location": [6033, 199, 22], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "572ca10c-4892-4945-b7d4-2b9df1d51d13", + "name": "Spawner (209)", + "location": [6052, 156, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db6a7440-9c85-400f-9fbc-9ea9c8c69164", + "name": "Spawner (209)", + "location": [6121, 166, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "32f0f7d1-6145-48d7-a6bb-6a3015964780", + "name": "Spawner (209)", + "location": [6124, 159, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70564f6b-1725-414c-9ef4-d2402e6cfde8", + "name": "Spawner (209)", + "location": [6087, 186, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5a00adac-4b86-4cde-8520-eb6b51d271ec", + "name": "Spawner (209)", + "location": [6060, 158, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b3ea2162-f70f-40ba-92e0-0d1820c4f479", + "name": "Spawner (209)", + "location": [6046, 199, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9453078d-36c6-46c3-a5d7-7086780707b8", + "name": "Spawner (209)", + "location": [6041, 222, 44], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "07267c9b-1455-47ec-885c-418388fe5df6", + "name": "Spawner (209)", + "location": [6125, 221, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dd36a876-49a9-43df-b35a-a4ce32499d3d", + "name": "Spawner (209)", + "location": [6120, 229, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50edd29c-ed4b-4321-a8d7-0b7561cc923b", + "name": "Spawner (209)", + "location": [6092, 187, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "39726a71-b66c-4013-b625-b32a8ba2d721", + "name": "Spawner (209)", + "location": [6041, 230, 44], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4815a5f0-aca1-4f0e-88b5-ef8aa4df08c2", + "name": "Spawner (209)", + "location": [6033, 205, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ba8ccb47-e78a-441e-b626-7bd423077d3c", + "name": "Spawner (209)", + "location": [6058, 153, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92a7ffa1-3c11-424a-a7af-bbb2dc49ac08", + "name": "Spawner (209)", + "location": [6064, 96, 22], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ee96e4d-2c92-49fa-b01a-40e19ece56d3", + "name": "Spawner (209)", + "location": [6081, 83, 21], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "f67bfb20-8128-4699-936b-37a792b7086c", "name": "Spawner (209)", "location": [6084, 66, 0], @@ -1306,15 +996,87 @@ "team": 0, "homeRange": 10, "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3c0ee712-4025-43a7-9ec1-3821d758bfe5", + "name": "Spawner (209)", + "location": [6106, 67, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0e5f0f20-5cc7-4e18-87d9-93736f1d71b7", + "name": "Spawner (209)", + "location": [6116, 86, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireGargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3302304b-f8a4-4638-9218-4bc883fb2811", + "name": "Spawner (209)", + "location": [6099, 51, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da0917f7-f30c-453c-b767-6f825428eb59", + "name": "Spawner (209)", + "location": [6107, 37, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7f5c5979-7038-41ed-b055-2d022c8b06e5", + "name": "Spawner (209)", + "location": [6054, 45, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fb0d4918-8977-44d0-92d9-7e6267b62a7f", + "type": "Spawner", + "guid": "7b88586b-18c3-45a4-a6de-900e6ceaa864", "name": "Spawner (209)", - "location": [5992, 147, 0], + "location": [6108, 66, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1322,8 +1084,132 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7672fa03-92ce-4855-90ae-66082355c179", + "name": "Spawner (209)", + "location": [6117, 81, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c0b89eb0-7bc5-47b2-9ff6-32f16b236742", + "name": "Spawner (209)", + "location": [6058, 92, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5dfa30dc-1590-4e52-a339-0ead51e7dd8e", + "name": "Spawner (209)", + "location": [6113, 92, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7a2d0ba5-68fe-4672-88da-ecc1a8978c02", + "name": "Spawner (209)", + "location": [6107, 70, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "349a17a0-eeeb-4b70-bd73-277509e44ac1", + "name": "Spawner (209)", + "location": [6081, 44, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "53b614b7-1d3f-42dd-8be6-713057bcd618", + "name": "Spawner (209)", + "location": [6097, 36, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ba4ffaa-796e-4b04-a9d5-6884f33928ef", + "name": "Spawner (209)", + "location": [6064, 96, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40e9cae3-14ce-4132-be18-0879d45cd22c", + "name": "Spawner (209)", + "location": [6086, 45, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a2b3facc-dd13-4ecd-9a59-5d7cc88565a3", + "name": "Spawner (209)", + "location": [6108, 36, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Ice.json b/Distribution/Data/Spawns/post-uoml/felucca/Ice.json similarity index 78% rename from Distribution/Data/Spawns/shared/felucca/Ice.json rename to Distribution/Data/Spawns/post-uoml/felucca/Ice.json index 30723f477..5d34f26f3 100644 --- a/Distribution/Data/Spawns/shared/felucca/Ice.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Ice.json @@ -1,6 +1,100 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "5a62f325-4e00-49c3-8576-fdd9fcf330e5", + "name": "Spawner (210)", + "location": [5771, 224, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3d3f7414-5249-4ee4-9602-c842af0da38c", + "name": "Spawner (210)", + "location": [5799, 188, -6], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8a2691a-996a-4c38-834f-897b972bfe2f", + "name": "Spawner (210)", + "location": [5867, 218, -4], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "FrostOoze", "maxCount": 2, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d31d21b1-8768-4d45-a962-182e67f5a5c0", + "name": "Spawner (210)", + "location": [5850, 219, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RatmanMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e111cb63-9804-4eac-96ad-290a0b963c8f", + "name": "Spawner (210)", + "location": [5814, 237, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d7d771c1-605a-4253-965e-dec048f72502", + "name": "Spawner (210)", + "location": [5752, 144, 9], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "WhiteWyrm", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "03498749-3218-4779-8864-b854b36c79f7", "name": "Spawner (210)", "location": [5839, 159, 0], @@ -21,7 +115,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0614ccac-5ffc-4223-8348-cbb5a0f0cc2d", "name": "Spawner (210)", "location": [5785, 161, -6], @@ -42,173 +136,7 @@ ] }, { - "$type": "Spawner", - "guid": "1cf83838-f5d3-42f9-8390-f445a01ac889", - "name": "Spawner (210)", - "location": [5754, 211, -6], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33003603-a1fc-43a4-b9d7-2408eb2e5e50", - "name": "Spawner (210)", - "location": [5848, 226, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3bf7b937-9eb6-45fc-81e6-d907910ca62a", - "name": "Spawner (210)", - "location": [5758, 139, 5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d3f7414-5249-4ee4-9602-c842af0da38c", - "name": "Spawner (210)", - "location": [5799, 188, -6], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 1, "probability": 100 }, - { "name": "FrostSpider", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "479095c0-02ab-4651-9edc-362d77a65a65", - "name": "Spawner (210)", - "location": [5763, 187, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4ab984eb-913e-4b21-a9b2-5242726ae03f", - "name": "Spawner (210)", - "location": [5731, 176, -5], - "map": "Felucca", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 35, - "entries": [ - { "name": "IceSerpent", "maxCount": 9, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 9, "probability": 100 }, - { "name": "IceElemental", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b8dd28c-f72f-45c5-afc4-84dc637c1e0a", - "name": "Spawner (210)", - "location": [5684, 181, -5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "51445c30-3ef8-4c0e-95bc-b12e643e39ba", - "name": "Spawner (210)", - "location": [5669, 330, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "IceFiend", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "57c38552-9c17-4451-9e53-3a8d2048d2f6", - "name": "Spawner (210)", - "location": [5666, 331, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a62f325-4e00-49c3-8576-fdd9fcf330e5", - "name": "Spawner (210)", - "location": [5771, 224, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "61e8233d-f991-4429-9e4e-c7e0d24201d0", "name": "Spawner (210)", "location": [5757, 213, -7], @@ -228,235 +156,7 @@ ] }, { - "$type": "Spawner", - "guid": "72473a5f-3d9b-4f69-b3f9-0d266291468a", - "name": "Spawner (210)", - "location": [5824, 363, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b080104-77b8-491b-b8f9-44cec8ecbb65", - "name": "Spawner (210)", - "location": [5680, 306, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "IceFiend", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "89792e6b-89bb-4c0a-8f45-259ab52f6844", - "name": "Spawner (210)", - "location": [5676, 331, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8aba961b-8ce4-49ba-9ec8-7861204815c4", - "name": "Spawner (210)", - "location": [5831, 358, -1], - "map": "Felucca", - "count": 24, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ratman", "maxCount": 12, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 7, "probability": 100 }, - { "name": "RatmanMage", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b93a6e4-67e5-4d22-aa51-ff84ea98bb72", - "name": "Spawner (210)", - "location": [5751, 140, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a3c447a5-c7bb-4826-a5ae-81d8b3863130", - "name": "Spawner (210)", - "location": [5854, 227, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b8a2691a-996a-4c38-834f-897b972bfe2f", - "name": "Spawner (210)", - "location": [5867, 218, -4], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, - { "name": "FrostOoze", "maxCount": 2, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b96eb341-ef4c-4b7e-ba01-6fb487be56a7", - "name": "Spawner (210)", - "location": [5721, 147, -1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bca4ab21-15b1-4b04-b698-05d4f8df1f6c", - "name": "Spawner (210)", - "location": [5838, 355, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cb37f510-fff9-4c76-8c51-2d59ddd8f715", - "name": "Spawner (210)", - "location": [5683, 197, -4], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cdb2ea29-4ee5-48c3-9444-018c5003afbd", - "name": "Spawner (210)", - "location": [5837, 245, -5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d31d21b1-8768-4d45-a962-182e67f5a5c0", - "name": "Spawner (210)", - "location": [5850, 219, -3], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "RatmanMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d5ca5e49-cbb2-4079-81f1-c8679fcecb46", - "name": "Spawner (210)", - "location": [5696, 303, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d7d771c1-605a-4253-965e-dec048f72502", - "name": "Spawner (210)", - "location": [5752, 144, 9], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "WhiteWyrm", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "d8d3fc0f-231c-4d04-b0a3-7191cc4512b2", "name": "Spawner (210)", "location": [5681, 191, -6], @@ -476,26 +176,28 @@ ] }, { - "$type": "Spawner", - "guid": "e111cb63-9804-4eac-96ad-290a0b963c8f", + "type": "Spawner", + "guid": "4ab984eb-913e-4b21-a9b2-5242726ae03f", "name": "Spawner (210)", - "location": [5814, 237, 0], + "location": [5731, 176, -5], "map": "Felucca", - "count": 1, + "count": 18, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 15, + "homeRange": 25, + "walkingRange": 35, "entries": [ - { "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 } + { "name": "IceSerpent", "maxCount": 9, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 9, "probability": 100 }, + { "name": "IceElemental", "maxCount": 9, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e89bcb09-65c3-4474-bded-50e62ce3c153", + "type": "Spawner", + "guid": "33003603-a1fc-43a4-b9d7-2408eb2e5e50", "name": "Spawner (210)", - "location": [5712, 145, -34], + "location": [5848, 226, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -503,15 +205,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ed18e76c-c568-42fe-9c98-8482c467ff61", + "type": "Spawner", + "guid": "cb37f510-fff9-4c76-8c51-2d59ddd8f715", "name": "Spawner (210)", - "location": [5756, 203, -2], + "location": [5683, 197, -4], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -519,15 +219,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f2eb3f17-d5f4-4c58-a248-825e46a8a0f8", + "type": "Spawner", + "guid": "b96eb341-ef4c-4b7e-ba01-6fb487be56a7", "name": "Spawner (210)", - "location": [5772, 188, -3], + "location": [5721, 147, -1], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -535,15 +233,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f522aee5-3ff7-427c-a3f1-31b162a65fbe", + "type": "Spawner", + "guid": "3bf7b937-9eb6-45fc-81e6-d907910ca62a", "name": "Spawner (210)", - "location": [5682, 314, 0], + "location": [5758, 139, 5], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -551,12 +247,24 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "1cf83838-f5d3-42f9-8390-f445a01ac889", + "name": "Spawner (210)", + "location": [5754, 211, -6], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "fe1657a5-a11c-4fe6-9544-a7ed0d2059b2", "name": "Spawner (210)", "location": [5833, 242, -2], @@ -567,8 +275,248 @@ "team": 0, "homeRange": 1, "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a3c447a5-c7bb-4826-a5ae-81d8b3863130", + "name": "Spawner (210)", + "location": [5854, 227, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f2eb3f17-d5f4-4c58-a248-825e46a8a0f8", + "name": "Spawner (210)", + "location": [5772, 188, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cdb2ea29-4ee5-48c3-9444-018c5003afbd", + "name": "Spawner (210)", + "location": [5837, 245, -5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9b93a6e4-67e5-4d22-aa51-ff84ea98bb72", + "name": "Spawner (210)", + "location": [5751, 140, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "479095c0-02ab-4651-9edc-362d77a65a65", + "name": "Spawner (210)", + "location": [5763, 187, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed18e76c-c568-42fe-9c98-8482c467ff61", + "name": "Spawner (210)", + "location": [5756, 203, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e89bcb09-65c3-4474-bded-50e62ce3c153", + "name": "Spawner (210)", + "location": [5712, 145, -34], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4b8dd28c-f72f-45c5-afc4-84dc637c1e0a", + "name": "Spawner (210)", + "location": [5684, 181, -5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8aba961b-8ce4-49ba-9ec8-7861204815c4", + "name": "Spawner (210)", + "location": [5831, 358, -1], + "map": "Felucca", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + { "name": "Ratman", "maxCount": 12, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 7, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 5, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "bca4ab21-15b1-4b04-b698-05d4f8df1f6c", + "name": "Spawner (210)", + "location": [5838, 355, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72473a5f-3d9b-4f69-b3f9-0d266291468a", + "name": "Spawner (210)", + "location": [5824, 363, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b080104-77b8-491b-b8f9-44cec8ecbb65", + "name": "Spawner (210)", + "location": [5680, 306, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "IceFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "51445c30-3ef8-4c0e-95bc-b12e643e39ba", + "name": "Spawner (210)", + "location": [5669, 330, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "IceFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d5ca5e49-cbb2-4079-81f1-c8679fcecb46", + "name": "Spawner (210)", + "location": [5696, 303, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f522aee5-3ff7-427c-a3f1-31b162a65fbe", + "name": "Spawner (210)", + "location": [5682, 314, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "57c38552-9c17-4451-9e53-3a8d2048d2f6", + "name": "Spawner (210)", + "location": [5666, 331, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "89792e6b-89bb-4c0a-8f45-259ab52f6844", + "name": "Spawner (210)", + "location": [5676, 331, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Khaldun.json b/Distribution/Data/Spawns/post-uoml/felucca/Khaldun.json similarity index 70% rename from Distribution/Data/Spawns/shared/felucca/Khaldun.json rename to Distribution/Data/Spawns/post-uoml/felucca/Khaldun.json index a87b03f1f..f28275f0a 100644 --- a/Distribution/Data/Spawns/shared/felucca/Khaldun.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Khaldun.json @@ -1,326 +1,6 @@ [ { - "$type": "Spawner", - "guid": "013c84aa-40c1-44d8-b8e9-f6745b1c6b35", - "name": "Spawner (228)", - "location": [5475, 1402, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "SpectralArmour", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "04591799-f2f2-446c-beee-df4ef3a88f1b", - "name": "Spawner (228)", - "location": [5415, 1312, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "ShadowFiend", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0b253bce-6be4-4602-9ddc-0f0d03e9e788", - "name": "Spawner (228)", - "location": [5442, 1411, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f6b8570-c002-4a2f-b3ce-745882fe08eb", - "name": "Spawner (228)", - "location": [5491, 1370, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12a4f785-0e30-4d1d-8bac-9030e34043f2", - "name": "Spawner (228)", - "location": [5505, 1316, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Cursed", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12feef7c-3ac2-4474-b767-c980db063b56", - "name": "Spawner (228)", - "location": [5499, 1411, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "ShadowFiend", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3c57a7a8-4376-47c4-bbd0-79c14dfa1e9d", - "name": "Spawner (228)", - "location": [5492, 1297, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "46062a97-dd3a-4ba6-9b9e-233ebf3bc148", - "name": "Spawner (228)", - "location": [5465, 1449, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "SkeletalMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b95ed14-7d77-47ec-b8f0-f8b9775cff31", - "name": "Spawner (228)", - "location": [5536, 1345, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "KhaldunZealot", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e913247-f30e-4a61-8c3d-b00b42f70f36", - "name": "Spawner (228)", - "location": [5605, 1343, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 20, - "entries": [ - { "name": "HarrowerTentacles", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "66ab0f0e-6e24-4639-bce2-93bb86188979", - "name": "Spawner (228)", - "location": [5433, 1485, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6738216d-c615-4f86-b8b0-433804be6d2e", - "name": "Spawner (228)", - "location": [5418, 1450, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6c6e1194-471d-480d-9e8a-1dc63c0abbf3", - "name": "Spawner (228)", - "location": [5403, 1332, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6e56c148-f415-4bfe-b722-8b4482b9b782", - "name": "Spawner (228)", - "location": [5521, 1397, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "ShadowFiend", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "71bad24b-147d-4001-b0c6-5d9a00c3ff95", - "name": "Spawner (228)", - "location": [5459, 1449, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "KhaldunSummoner", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7bfe01e3-1ba0-460e-b6e5-7a813bb616dc", - "name": "Spawner (228)", - "location": [5412, 1330, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "BoneKnight", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "86ef240b-47f8-4918-803a-f043dd0b2e4e", - "name": "Spawner (228)", - "location": [5400, 1397, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "KhaldunZealot", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "86fa9698-3b65-416e-b947-55eb169a65b8", - "name": "Spawner (228)", - "location": [5492, 1294, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "89edcd60-e1d3-42d8-b992-03aaec09a0ef", - "name": "Spawner (228)", - "location": [5395, 1370, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Cursed", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ccf110d-809e-4016-ad67-06d151088844", - "name": "Spawner (228)", - "location": [5536, 1351, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "KhaldunSummoner", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "92f3597c-2d16-4f72-b8f0-c2e8e855b334", "name": "Spawner (228)", "location": [5569, 1306, 0], @@ -331,252 +11,10 @@ "team": 0, "homeRange": 5, "walkingRange": 10, - "entries": [ - { "name": "ShadowFiend", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "9d0c1a06-557d-4eec-83fe-6c6f1340e5a6", - "name": "Spawner (228)", - "location": [5415, 1331, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a22f7aa4-357d-4b0e-80bf-46363acc2b42", - "name": "Spawner (228)", - "location": [5549, 1324, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Cursed", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a541dc54-0708-4584-8df2-aa8cd9e41efb", - "name": "Spawner (228)", - "location": [5399, 1298, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a6b68b32-e386-4456-ad05-125d9cba058f", - "name": "Spawner (228)", - "location": [5435, 1346, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "ShadowFiend", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a6e622ec-a297-4c44-89e4-17fc7b127588", - "name": "Spawner (228)", - "location": [5396, 1371, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a7c42c91-71d1-434e-97a2-6be2419da639", - "name": "Spawner (228)", - "location": [5425, 1451, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "afb085cd-4efa-47da-aec4-494463708012", - "name": "Spawner (228)", - "location": [5460, 1451, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "KhaldunZealot", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b647c6fc-c8cf-4ac4-8862-3f6a288ac886", - "name": "Spawner (228)", - "location": [5444, 1410, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf42c575-292a-4cd2-babf-39233d4f540a", - "name": "Spawner (228)", - "location": [5458, 1445, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "AncientLich", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf7e1e29-9832-4df4-adc7-fa167afdbfa6", - "name": "Spawner (228)", - "location": [5402, 1430, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d1fa73c3-fc93-4630-a81c-eb272cd40185", - "name": "Spawner (228)", - "location": [5420, 1355, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "TavaraSewel", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d29cf8da-75e3-4e20-b1b8-51ecbde5942a", - "name": "Spawner (228)", - "location": [5483, 1446, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "ShadowFiend", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d37ef7a2-76ff-4c00-b104-059f764f76ff", - "name": "Spawner (228)", - "location": [5422, 1355, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d440099e-f38e-4f45-85ce-e0bbd4c50e7d", - "name": "Spawner (228)", - "location": [5485, 1434, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ddbab85a-3d5b-4417-b289-d7a95aeafff2", - "name": "Spawner (228)", - "location": [5549, 1332, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "AncientLich", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e1b1ac2b-578c-4fcd-8882-4e78047852f2", "name": "Spawner (228)", "location": [5539, 1301, 0], @@ -587,140 +25,10 @@ "team": 0, "homeRange": 20, "walkingRange": 30, - "entries": [ - { "name": "Cursed", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Cursed", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e34e42ae-4fba-428d-9460-8627acb32b8e", - "name": "Spawner (228)", - "location": [5400, 1428, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "KhaldunSummoner", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e568c255-b944-4f6d-a246-245d16aac43b", - "name": "Spawner (228)", - "location": [5452, 1366, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "GrimmochDrummel", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e6542e93-a203-4d2e-b2e6-0b93f695c863", - "name": "Spawner (228)", - "location": [5430, 1485, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "ShadowFiend", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e9d26f46-ca9e-4ef6-b946-1ad646d11bb7", - "name": "Spawner (228)", - "location": [5458, 1303, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "LysanderGathenwale", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eb722e41-4267-48cb-a817-1d0851c7895b", - "name": "Spawner (228)", - "location": [5425, 1484, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "BoneMagi", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ee38e2d0-0c75-483e-9503-03634907cab5", - "name": "Spawner (228)", - "location": [5404, 1431, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "ShadowFiend", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eec23c16-31c0-4bed-887b-e5fa15891696", - "name": "Spawner (228)", - "location": [5393, 1369, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "ShadowFiend", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f53982c9-8d7c-460b-becf-2653818a5939", - "name": "Spawner (228)", - "location": [5490, 1367, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "SpectralArmour", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "fb1469d0-67e5-44cc-868c-7879e3d04603", "name": "Spawner (228)", "location": [5525, 1296, 0], @@ -731,12 +39,80 @@ "team": 0, "homeRange": 20, "walkingRange": 20, - "entries": [ - { "name": "ShadowFiend", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "ShadowFiend", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "86fa9698-3b65-416e-b947-55eb169a65b8", + "name": "Spawner (228)", + "location": [5492, 1294, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3c57a7a8-4376-47c4-bbd0-79c14dfa1e9d", + "name": "Spawner (228)", + "location": [5492, 1297, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e9d26f46-ca9e-4ef6-b946-1ad646d11bb7", + "name": "Spawner (228)", + "location": [5458, 1303, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "LysanderGathenwale", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04591799-f2f2-446c-beee-df4ef3a88f1b", + "name": "Spawner (228)", + "location": [5415, 1312, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a541dc54-0708-4584-8df2-aa8cd9e41efb", + "name": "Spawner (228)", + "location": [5399, 1298, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "ff670d55-158a-40f9-b228-7ea17fd81559", "name": "Spawner (228)", "location": [5398, 1299, 0], @@ -747,8 +123,538 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "MorgBergen", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "MorgBergen", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7bfe01e3-1ba0-460e-b6e5-7a813bb616dc", + "name": "Spawner (228)", + "location": [5412, 1330, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "BoneKnight", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9d0c1a06-557d-4eec-83fe-6c6f1340e5a6", + "name": "Spawner (228)", + "location": [5415, 1331, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6c6e1194-471d-480d-9e8a-1dc63c0abbf3", + "name": "Spawner (228)", + "location": [5403, 1332, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d1fa73c3-fc93-4630-a81c-eb272cd40185", + "name": "Spawner (228)", + "location": [5420, 1355, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "TavaraSewel", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d37ef7a2-76ff-4c00-b104-059f764f76ff", + "name": "Spawner (228)", + "location": [5422, 1355, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a6b68b32-e386-4456-ad05-125d9cba058f", + "name": "Spawner (228)", + "location": [5435, 1346, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a6e622ec-a297-4c44-89e4-17fc7b127588", + "name": "Spawner (228)", + "location": [5396, 1371, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "89edcd60-e1d3-42d8-b992-03aaec09a0ef", + "name": "Spawner (228)", + "location": [5395, 1370, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Cursed", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eec23c16-31c0-4bed-887b-e5fa15891696", + "name": "Spawner (228)", + "location": [5393, 1369, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "86ef240b-47f8-4918-803a-f043dd0b2e4e", + "name": "Spawner (228)", + "location": [5400, 1397, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "KhaldunZealot", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b647c6fc-c8cf-4ac4-8862-3f6a288ac886", + "name": "Spawner (228)", + "location": [5444, 1410, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0b253bce-6be4-4602-9ddc-0f0d03e9e788", + "name": "Spawner (228)", + "location": [5442, 1411, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e34e42ae-4fba-428d-9460-8627acb32b8e", + "name": "Spawner (228)", + "location": [5400, 1428, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "KhaldunSummoner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf7e1e29-9832-4df4-adc7-fa167afdbfa6", + "name": "Spawner (228)", + "location": [5402, 1430, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ee38e2d0-0c75-483e-9503-03634907cab5", + "name": "Spawner (228)", + "location": [5404, 1431, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a7c42c91-71d1-434e-97a2-6be2419da639", + "name": "Spawner (228)", + "location": [5425, 1451, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6738216d-c615-4f86-b8b0-433804be6d2e", + "name": "Spawner (228)", + "location": [5418, 1450, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eb722e41-4267-48cb-a817-1d0851c7895b", + "name": "Spawner (228)", + "location": [5425, 1484, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "BoneMagi", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e6542e93-a203-4d2e-b2e6-0b93f695c863", + "name": "Spawner (228)", + "location": [5430, 1485, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "ShadowFiend", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "66ab0f0e-6e24-4639-bce2-93bb86188979", + "name": "Spawner (228)", + "location": [5433, 1485, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf42c575-292a-4cd2-babf-39233d4f540a", + "name": "Spawner (228)", + "location": [5458, 1445, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "AncientLich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "71bad24b-147d-4001-b0c6-5d9a00c3ff95", + "name": "Spawner (228)", + "location": [5459, 1449, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "KhaldunSummoner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "afb085cd-4efa-47da-aec4-494463708012", + "name": "Spawner (228)", + "location": [5460, 1451, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "KhaldunZealot", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "46062a97-dd3a-4ba6-9b9e-233ebf3bc148", + "name": "Spawner (228)", + "location": [5465, 1449, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "SkeletalMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d29cf8da-75e3-4e20-b1b8-51ecbde5942a", + "name": "Spawner (228)", + "location": [5483, 1446, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "ShadowFiend", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d440099e-f38e-4f45-85ce-e0bbd4c50e7d", + "name": "Spawner (228)", + "location": [5485, 1434, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12feef7c-3ac2-4474-b767-c980db063b56", + "name": "Spawner (228)", + "location": [5499, 1411, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "ShadowFiend", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "013c84aa-40c1-44d8-b8e9-f6745b1c6b35", + "name": "Spawner (228)", + "location": [5475, 1402, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "SpectralArmour", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0f6b8570-c002-4a2f-b3ce-745882fe08eb", + "name": "Spawner (228)", + "location": [5491, 1370, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f53982c9-8d7c-460b-becf-2653818a5939", + "name": "Spawner (228)", + "location": [5490, 1367, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "SpectralArmour", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6e56c148-f415-4bfe-b722-8b4482b9b782", + "name": "Spawner (228)", + "location": [5521, 1397, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5e913247-f30e-4a61-8c3d-b00b42f70f36", + "name": "Spawner (228)", + "location": [5605, 1343, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 20, + "entries": [{ "name": "HarrowerTentacles", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8ccf110d-809e-4016-ad67-06d151088844", + "name": "Spawner (228)", + "location": [5536, 1351, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "KhaldunSummoner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4b95ed14-7d77-47ec-b8f0-f8b9775cff31", + "name": "Spawner (228)", + "location": [5536, 1345, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "KhaldunZealot", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a22f7aa4-357d-4b0e-80bf-46363acc2b42", + "name": "Spawner (228)", + "location": [5549, 1324, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Cursed", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ddbab85a-3d5b-4417-b289-d7a95aeafff2", + "name": "Spawner (228)", + "location": [5549, 1332, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "AncientLich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12a4f785-0e30-4d1d-8bac-9030e34043f2", + "name": "Spawner (228)", + "location": [5505, 1316, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Cursed", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e568c255-b944-4f6d-a246-245d16aac43b", + "name": "Spawner (228)", + "location": [5452, 1366, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "GrimmochDrummel", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/LostLands.json b/Distribution/Data/Spawns/post-uoml/felucca/LostLands.json similarity index 95% rename from Distribution/Data/Spawns/shared/felucca/LostLands.json rename to Distribution/Data/Spawns/post-uoml/felucca/LostLands.json index 97cabd097..a02c293d6 100644 --- a/Distribution/Data/Spawns/shared/felucca/LostLands.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/LostLands.json @@ -1,1345 +1,6 @@ [ { - "$type": "Spawner", - "guid": "0051ef48-a9fa-49be-8307-04ed26c3f8fc", - "name": "Spawner (211)", - "location": [5501, 2361, 27], - "map": "Felucca", - "count": 30, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 75, - "entries": [ - { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, - { "name": "IceSnake", "maxCount": 8, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, - { "name": "Walrus", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "00b71fd9-b553-4846-8cc6-5ceaf42ba4b3", - "name": "Spawner (211)", - "location": [5467, 3078, -3], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "048db5e6-adcb-4fec-91e7-3d12011d05b0", - "name": "Spawner (211)", - "location": [5347, 3449, 17], - "map": "Felucca", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 4, "probability": 100 }, - { "name": "Hind", "maxCount": 16, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "04c56ada-2f70-4d20-a168-217066a7920d", - "name": "Spawner (211)", - "location": [5351, 2359, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 75, - "entries": [ - { "name": "PolarBear", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "04ebb8af-b6c7-488c-816c-b37805270c49", - "name": "Spawner (211)", - "location": [6022, 2874, 16], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "050b5b91-6840-4ec9-adca-c73658982e58", - "name": "Spawner (211)", - "location": [5918, 3975, 1], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "07a9c362-3e90-470f-8020-52320f19ef71", - "name": "Spawner (211)", - "location": [5724, 2949, 31], - "map": "Felucca", - "count": 44, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 65, - "walkingRange": 75, - "entries": [ - { "name": "HellHound", "maxCount": 8, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 8, "probability": 100 }, - { "name": "FireElemental", "maxCount": 10, "probability": 100 }, - { "name": "Efreet", "maxCount": 4, "probability": 100 }, - { "name": "HellCat", "maxCount": 6, "probability": 100 }, - { "name": "LavaSerpent", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "092875e2-4178-4845-97e9-1d70ab565933", - "name": "Spawner (211)", - "location": [5919, 3875, 16], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0e69642d-acde-40f0-bc27-0e07d9292977", - "name": "Spawner (211)", - "location": [5251, 3301, -2], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Llama", "maxCount": 2, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f0fa954-8f4e-4a76-aa93-5e5bf5cb1735", - "name": "Spawner (211)", - "location": [5711, 2494, 46], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 80, - "entries": [ - { "name": "Imp", "maxCount": 3, "probability": 100 }, - { "name": "Scorpion", "maxCount": 3, "probability": 100 }, - { "name": "DesertOstard", "maxCount": 1, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0fe27512-76eb-4830-939d-36ab2aafaa42", - "name": "Spawner (211)", - "location": [5722, 3776, 32], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12d2b037-bfa7-4ea1-9580-28e0623a2671", - "name": "Spawner (211)", - "location": [5697, 2626, -2], - "map": "Felucca", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 30, - "entries": [ - { "name": "Wyvern", "maxCount": 2, "probability": 100 }, - { "name": "Drake", "maxCount": 4, "probability": 100 }, - { "name": "Imp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "13efbe43-2edd-44f9-8282-5b3274f79912", - "name": "Spawner (211)", - "location": [5570, 3153, 14], - "map": "Felucca", - "count": 25, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 60, - "walkingRange": 80, - "entries": [ - { "name": "Gorilla", "maxCount": 6, "probability": 100 }, - { "name": "Panther", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "150865b5-2285-49ea-94d4-42554405d181", - "name": "Spawner (211)", - "location": [5201, 3162, 53], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Llama", "maxCount": 2, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15908c1e-bc66-4149-9c00-c11fe35e5a17", - "name": "Spawner (211)", - "location": [6022, 3773, 18], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15e3c862-5e27-47f3-ba4d-8a9ee45c3623", - "name": "Spawner (211)", - "location": [5251, 3301, -2], - "map": "Felucca", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 6, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, - { "name": "BlackBear", "maxCount": 6, "probability": 100 }, - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, - { "name": "Rabbit", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Cougar", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "18e0751c-cee9-4120-8873-04f87bfe4772", - "name": "Spawner (211)", - "location": [5478, 3587, 3], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1a30ea72-ec30-49a4-be3f-1f7a4a0217b2", - "name": "Spawner (211)", - "location": [5478, 3587, 3], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d35c378-e89b-4bcc-9074-bd08850092de", - "name": "Spawner (211)", - "location": [5521, 3775, 8], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1eee493b-6795-4b24-b390-72c7cd5b2b9a", - "name": "Spawner (211)", - "location": [5394, 2530, 6], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Cyclops", "maxCount": 5, "probability": 100 }, - { "name": "Titan", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20911691-e6c3-4c40-87d1-ee67a34d2669", - "name": "Spawner (211)", - "location": [5921, 3777, 28], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21157414-21f7-4eb1-84e8-cb9ffb3b0b1b", - "name": "Spawner (211)", - "location": [5521, 3775, 8], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21cba4e8-69f4-49b4-bdc2-1b5893001823", - "name": "Spawner (211)", - "location": [5623, 3875, 15], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21f61927-4b6f-4b7d-8c37-4e0dbab8a750", - "name": "Spawner (211)", - "location": [5721, 3876, -3], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "224ddc6f-f8ea-4752-8209-4a8484eeca66", - "name": "Spawner (211)", - "location": [5521, 3875, -1], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23eb1938-b1a7-400d-a9dd-12d6f514a3c5", - "name": "Spawner (211)", - "location": [5521, 3875, -1], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2694242b-1d3f-4dfc-96c0-892202e3b950", - "name": "Spawner (211)", - "location": [5721, 3675, 22], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "26b4a453-57c9-4bcf-b19b-a5caace69ace", - "name": "Spawner (211)", - "location": [5756, 3453, -2], - "map": "Felucca", - "count": 42, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 130, - "walkingRange": 130, - "entries": [ - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "Panther", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 2, "probability": 100 }, - { "name": "Mongbat", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "270bb58a-fbba-4af0-98b2-3cece2112336", - "name": "Spawner (211)", - "location": [5349, 3547, -1], - "map": "Felucca", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 4, "probability": 100 }, - { "name": "Hind", "maxCount": 16, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "273c3edf-416a-4113-b68a-fe94b128b63d", - "name": "Spawner (211)", - "location": [5218, 3786, 1], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "273dd9e4-983d-4029-8b2b-3e2ea1178a95", - "name": "Spawner (211)", - "location": [5351, 3401, 30], - "map": "Felucca", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 6, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, - { "name": "BlackBear", "maxCount": 6, "probability": 100 }, - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, - { "name": "Rabbit", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Cougar", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "27c7c466-0a83-4356-8b5d-7aab73b1dde1", - "name": "Spawner (211)", - "location": [6091, 2379, 25], - "map": "Felucca", - "count": 28, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 60, - "entries": [ - { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, - { "name": "IceSnake", "maxCount": 10, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, - { "name": "Walrus", "maxCount": 3, "probability": 100 }, - { "name": "PolarBear", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2a5e6b73-0af4-4461-96af-e980adc67771", - "name": "Spawner (211)", - "location": [5624, 3975, -3], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "30f49430-b98b-4da2-89b6-cfbf3f69182e", - "name": "Spawner (211)", - "location": [5820, 3675, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33c93a22-4e4d-4b81-a122-0fc480c300a2", - "name": "Spawner (211)", - "location": [5200, 3895, 2], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "354f9453-76f6-4cc8-8255-873544d38355", - "name": "Spawner (211)", - "location": [5621, 3775, 2], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "35afdfbe-5809-4afe-bf7a-b2de7ef3e6a4", - "name": "Spawner (211)", - "location": [5624, 3975, -3], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3612f07e-f714-4ad8-bec2-e7bbca18b0ea", - "name": "Spawner (211)", - "location": [5371, 3820, -2], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3781051c-c4bb-4e65-b4f5-5e09c44b9a31", - "name": "Spawner (211)", - "location": [5218, 3786, 1], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rabbit", "maxCount": 20, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, - { "name": "Bird", "maxCount": 15, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 }, - { "name": "Hind", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "38fd8a6b-4deb-4f8f-87be-70dcfd5b4055", - "name": "Spawner (211)", - "location": [5521, 3775, 8], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a9fe475-26ea-4f9c-a73b-e33ab2674130", - "name": "Spawner (211)", - "location": [5820, 3775, 48], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3b2a1a28-2c30-483f-833c-1a9ad40bd153", - "name": "Spawner (211)", - "location": [5819, 3974, 16], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "413f3751-fb8f-4519-bc77-47bb4c6cb964", - "name": "Spawner (211)", - "location": [5520, 2890, 33], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Wyvern", "maxCount": 4, "probability": 100 }, - { "name": "Mummy", "maxCount": 2, "probability": 100 }, - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41d9a9e4-66be-4a71-a1ea-b8dec95e6258", - "name": "Spawner (211)", - "location": [5623, 3676, -3], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "421bbe19-6e35-4125-a521-0a5294553b06", - "name": "Spawner (211)", - "location": [5818, 3876, 18], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "42d4b8fa-99ad-412e-9df7-bd3f92c023ac", - "name": "Spawner (211)", - "location": [5721, 3675, 22], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "440d81ae-8e54-44db-b6de-909abe0e75b8", - "name": "Spawner (211)", - "location": [5432, 3079, -4], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4438dccc-cae8-4b97-8079-baee38c36b7a", - "name": "Spawner (211)", - "location": [5521, 3775, 8], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4bc10a0b-643a-434c-af08-7b789cfcd954", - "name": "Spawner (211)", - "location": [5250, 3401, 1], - "map": "Felucca", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 6, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, - { "name": "BlackBear", "maxCount": 6, "probability": 100 }, - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, - { "name": "Rabbit", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Cougar", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4c010aa0-3974-42d1-ace5-aefc3014eda1", - "name": "Spawner (211)", - "location": [5623, 3676, -3], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4c6585a9-e298-4753-a981-b77e13fec541", - "name": "Spawner (211)", - "location": [5967, 2370, 44], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 75, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d1cecc3-832e-4898-ae75-bce89940fb02", - "name": "Spawner (211)", - "location": [5722, 3776, 32], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d59b4f4-57c3-43ef-9fc2-0364e61a718c", - "name": "Spawner (211)", - "location": [5920, 3675, 12], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4da8db24-068c-406f-a561-6128621f78e6", - "name": "Spawner (211)", - "location": [6022, 3971, 17], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "507fcaa7-e8ed-4ce1-93db-f6f6d4bc5abc", - "name": "Spawner (211)", - "location": [5212, 3619, 0], - "map": "Felucca", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "SavageRider", "maxCount": 3, "probability": 100 }, - { "name": "Savage", "maxCount": 9, "probability": 100 }, - { "name": "SavageShaman", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50aa6367-2c0f-475a-a0f7-81ee98f66244", - "name": "Spawner (211)", - "location": [5818, 3876, 18], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54cb046e-98e1-4971-8b9b-0b62d687c1ab", - "name": "Spawner (211)", - "location": [5250, 3449, 16], - "map": "Felucca", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 4, "probability": 100 }, - { "name": "Hind", "maxCount": 16, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54cc6589-3bd8-4258-a88c-834c928a2257", - "name": "Spawner (211)", - "location": [5722, 3776, 32], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "55a13cb2-cc95-4d1b-8498-2babc070ce78", - "name": "Spawner (211)", - "location": [5918, 3975, 1], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5702bcbc-9e12-44b5-a382-d2a56acda20f", - "name": "Spawner (211)", - "location": [5623, 3875, 15], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5821f460-0bda-4984-8a75-10c711c09f64", - "name": "Spawner (211)", - "location": [5368, 3887, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "585f8ed2-b655-4de7-b9c8-e937bd858d43", - "name": "Spawner (211)", - "location": [5721, 3876, -3], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a102eab-82dd-45ca-9071-d38e2d759ce6", - "name": "Spawner (211)", - "location": [5351, 3401, 30], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Llama", "maxCount": 2, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a31f2cf-8495-4789-b1d7-3a480f373f73", - "name": "Spawner (211)", - "location": [5721, 3975, 21], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5bc4fc91-8bea-4601-990a-f5bfac3d090e", - "name": "Spawner (211)", - "location": [5159, 3913, 1], - "map": "Felucca", - "count": 11, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 10, - "entries": [ - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 1, "probability": 100 }, - { "name": "Bull", "maxCount": 1, "probability": 100 }, - { "name": "Chicken", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "5c677abc-6f32-45bb-a1cb-c0a826f8e95f", "name": "Spawner (211)", "location": [5728, 3209, -3], @@ -1350,1882 +11,10 @@ "team": 0, "homeRange": 100, "walkingRange": 100, - "entries": [ - { "name": "SwampDragon", "maxCount": 7, "probability": 100 } - ] + "entries": [{ "name": "SwampDragon", "maxCount": 7, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "5d4a0fe3-6d82-42e1-83a8-8eea8de98aee", - "name": "Spawner (211)", - "location": [5523, 3676, 45], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "616334e1-53ea-49c0-8901-856c483a69e5", - "name": "Spawner (211)", - "location": [5920, 3675, 12], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "620213e0-2aad-4795-9d8a-1c326bb59212", - "name": "Spawner (211)", - "location": [6018, 3673, 18], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "62947b20-aa82-480c-887f-bbacd25e698f", - "name": "Spawner (211)", - "location": [5523, 3676, 45], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6488c8bb-5ced-4f2f-9f3a-6f80958d1ae6", - "name": "Spawner (211)", - "location": [5621, 3775, 2], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "64a11ac5-1b77-4d46-beed-78d63b227f89", - "name": "Spawner (211)", - "location": [5624, 3975, -3], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6686565b-fadf-4063-9125-c8fe0a138653", - "name": "Spawner (211)", - "location": [5478, 3587, 3], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "66e10caf-9bd5-4f1c-8e03-6c0b8ef97cda", - "name": "Spawner (211)", - "location": [6022, 3773, 18], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "673c07e2-de3f-4228-9c93-fcaa22aae807", - "name": "Spawner (211)", - "location": [5250, 3401, 1], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Llama", "maxCount": 2, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "675ac5d1-85b0-4fe0-89c3-011c4ee3be55", - "name": "Spawner (211)", - "location": [5520, 3939, 42], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6ad3a966-1fcf-45e8-b36a-f3f2c5cbd374", - "name": "Spawner (211)", - "location": [5371, 3820, -2], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rabbit", "maxCount": 20, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, - { "name": "Bird", "maxCount": 15, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 }, - { "name": "Hind", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b8e8c6a-2fb2-443a-a5ac-6ba23572d23c", - "name": "Spawner (211)", - "location": [5624, 3975, -3], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bd68fd6-cde9-45a8-8a99-8173b0e61e54", - "name": "Spawner (211)", - "location": [5173, 2368, 21], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "PolarBear", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6e1001c3-4e95-479b-a49a-850853fd7e59", - "name": "Spawner (211)", - "location": [5729, 2767, -1], - "map": "Felucca", - "count": 30, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 60, - "walkingRange": 70, - "entries": [ - { "name": "Imp", "maxCount": 8, "probability": 100 }, - { "name": "Scorpion", "maxCount": 8, "probability": 100 }, - { "name": "DesertOstard", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, - { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6ef79240-6f81-42f3-ae82-873af735b246", - "name": "Spawner (211)", - "location": [5351, 2359, 0], - "map": "Felucca", - "count": 29, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 75, - "entries": [ - { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, - { "name": "IceSnake", "maxCount": 7, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, - { "name": "Walrus", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6fae27d7-64f5-4e31-8a41-381a6344a96d", - "name": "Spawner (211)", - "location": [5863, 2470, 46], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Wyvern", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6ff7fdd5-e4c7-41ea-958f-8b030f8aca55", - "name": "Spawner (211)", - "location": [5523, 3676, 45], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "738a7321-91fd-4205-97a0-cb9ef78e5ad0", - "name": "Spawner (211)", - "location": [5820, 3775, 48], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "741769ff-9984-44cf-a8b0-bdc393beffc2", - "name": "Spawner (211)", - "location": [5820, 3675, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76d30438-3240-4879-aae2-083e9eecfd1f", - "name": "Spawner (211)", - "location": [5724, 2949, 31], - "map": "Felucca", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 65, - "walkingRange": 75, - "entries": [ - { "name": "LavaSnake", "maxCount": 15, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "775ba293-8e3a-4d62-bf10-7e002b3b2ec5", - "name": "Spawner (211)", - "location": [5918, 3975, 1], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "783e28bf-94fe-445d-b357-a4009b10eab0", - "name": "Spawner (211)", - "location": [5621, 3775, 2], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7a43ad68-7a8b-43f6-b8e4-2f5bce9b3c1e", - "name": "Spawner (211)", - "location": [5523, 3676, 45], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7a81959d-4cbc-487c-a41c-867dfa194ca0", - "name": "Spawner (211)", - "location": [5817, 2367, 42], - "map": "Felucca", - "count": 23, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 75, - "entries": [ - { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, - { "name": "IceSnake", "maxCount": 3, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 3, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c05974e-e051-411c-8e9d-991adc1b11d7", - "name": "Spawner (211)", - "location": [5368, 3887, 0], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rabbit", "maxCount": 20, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, - { "name": "Bird", "maxCount": 15, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 }, - { "name": "Hind", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "80c6a933-7596-4e44-9ecb-006e36b52a4c", - "name": "Spawner (211)", - "location": [5721, 3675, 22], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "833ac732-4f3f-4863-8b88-f1b52673d063", - "name": "Spawner (211)", - "location": [5784, 3951, 20], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 35, - "walkingRange": 35, - "entries": [ - { "name": "FrenziedOstard", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84a12c7e-b2f6-4713-8d14-5c768f38358b", - "name": "Spawner (211)", - "location": [5566, 3292, 4], - "map": "Felucca", - "count": 21, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 56, - "walkingRange": 62, - "entries": [ - { "name": "Gorilla", "maxCount": 5, "probability": 100 }, - { "name": "Panther", "maxCount": 5, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "872072bf-8139-497f-8338-76cc9b06d93e", - "name": "Spawner (211)", - "location": [5818, 3876, 18], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "88937169-f283-4c78-9d28-7f03ac03872c", - "name": "Spawner (211)", - "location": [5721, 3876, -3], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ac377e6-e430-4412-8fcd-8ed365924d58", - "name": "Spawner (211)", - "location": [6018, 3673, 18], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8bbb0aca-5066-44cc-80b6-2a6e4862d6df", - "name": "Spawner (211)", - "location": [5721, 3975, 21], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8be2f678-bffa-4aa1-acf4-d4e3d2cfd92d", - "name": "Spawner (211)", - "location": [5521, 3875, -1], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d68603f-69e3-4d13-b972-2648fa6e6f5b", - "name": "Spawner (211)", - "location": [5457, 2651, 45], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Wyvern", "maxCount": 6, "probability": 100 }, - { "name": "Mummy", "maxCount": 4, "probability": 100 }, - { "name": "Ettin", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8e896c88-1cc3-4b00-a042-41b7daa309dc", - "name": "Spawner (211)", - "location": [5173, 2368, 21], - "map": "Felucca", - "count": 28, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, - { "name": "IceSnake", "maxCount": 10, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, - { "name": "Walrus", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "91259235-0484-4698-906e-bf63a077d599", - "name": "Spawner (211)", - "location": [5454, 3136, -60], - "map": "Felucca", - "count": 26, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "TerathanMatriarch", "maxCount": 2, "probability": 100 }, - { "name": "TerathanAvenger", "maxCount": 4, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 10, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93c70268-f78d-436e-b3c0-3dd39fbadad3", - "name": "Spawner (211)", - "location": [5819, 3974, 16], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "942c695f-f39f-4c28-8704-9714152cdefd", - "name": "Spawner (211)", - "location": [5921, 3777, 28], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95f3b7c6-e49c-483f-8598-6a5c71f6e959", - "name": "Spawner (211)", - "location": [5623, 3676, -3], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b991a2d-71f9-42d1-9d66-7a7a4d6ba42e", - "name": "Spawner (211)", - "location": [5478, 3587, 3], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9cf77fad-29ee-4dc8-a174-7adf1ba52831", - "name": "Spawner (211)", - "location": [6018, 3673, 18], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9d0907cc-7569-4001-9ef3-6c95a3273262", - "name": "Spawner (211)", - "location": [6018, 3673, 18], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9d472611-6a0e-4aca-ba28-e3b6f3e2bb1e", - "name": "Spawner (211)", - "location": [5623, 3676, -3], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9fcbab64-2922-4c73-b1a1-8c7304252e57", - "name": "Spawner (211)", - "location": [5967, 2370, 44], - "map": "Felucca", - "count": 26, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 75, - "entries": [ - { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, - { "name": "IceSnake", "maxCount": 4, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a98d669c-759b-45b5-96ad-3a1153f38d9d", - "name": "Spawner (211)", - "location": [5820, 3675, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa42da05-496c-43fd-8178-a7fdb9ebfe63", - "name": "Spawner (211)", - "location": [5919, 3875, 16], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ab1c20f0-33bf-4b17-9d3e-b4a1bf3a7dc9", - "name": "Spawner (211)", - "location": [5985, 3441, 17], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 90, - "entries": [ - { "name": "GiantToad", "maxCount": 8, "probability": 100 }, - { "name": "SwampDragon", "maxCount": 5, "probability": 100 }, - { "name": "GiantRat", "maxCount": 12, "probability": 100 }, - { "name": "Alligator", "maxCount": 12, "probability": 100 }, - { "name": "SewerRat", "maxCount": 11, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae2d2521-6a54-46cc-8bd6-4e4ff10242e8", - "name": "Spawner (211)", - "location": [5918, 3975, 1], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b35c2359-f228-4ede-ab15-7677b251219d", - "name": "Spawner (211)", - "location": [5243, 3548, 14], - "map": "Felucca", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 4, "probability": 100 }, - { "name": "Hind", "maxCount": 16, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b424a6e3-dd2b-4140-a335-fa3916397531", - "name": "Spawner (211)", - "location": [5466, 4005, -1], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rabbit", "maxCount": 20, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, - { "name": "Bird", "maxCount": 15, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 }, - { "name": "Hind", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4562d6b-3ea3-46c0-9a75-d102a5da6fef", - "name": "Spawner (211)", - "location": [5721, 3975, 21], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4f901b5-6562-4689-9d5a-3ad3e3ad8a3f", - "name": "Spawner (211)", - "location": [5919, 3875, 16], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4fdfdb8-b446-4cad-a4d5-700f6e387112", - "name": "Spawner (211)", - "location": [5220, 3664, -1], - "map": "Felucca", - "count": 23, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 25, - "entries": [ - { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 3, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 6, "probability": 100 }, - { "name": "Wraith", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b532fb5d-8a9a-4ee4-8358-e7e2ef4095d9", - "name": "Spawner (211)", - "location": [5921, 3777, 28], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b5d11127-71e9-4521-88ae-739adb829541", - "name": "Spawner (211)", - "location": [5466, 4005, -1], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b831bbb7-b063-4039-9d54-bdaf8c0a4cbf", - "name": "Spawner (211)", - "location": [5820, 3775, 48], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9b9dd7d-c27f-48e4-99b3-23cd89fc19cb", - "name": "Spawner (211)", - "location": [6022, 3773, 18], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba775ba5-f4a7-462e-88f1-c9962147118a", - "name": "Spawner (211)", - "location": [5557, 2625, 0], - "map": "Felucca", - "count": 21, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 3, "probability": 100 }, - { "name": "Troll", "maxCount": 3, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bc3fc7a6-6646-4c86-8830-883c67dd0938", - "name": "Spawner (211)", - "location": [6022, 3971, 17], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bdfb73eb-b35a-4701-afe9-0487da724239", - "name": "Spawner (211)", - "location": [5820, 3775, 48], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf46ef5b-0f23-4bd9-93ce-b6bf724fd816", - "name": "Spawner (211)", - "location": [5819, 3974, 16], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf8af0ff-9824-45cb-b254-bb762019c7ca", - "name": "Spawner (211)", - "location": [5818, 3876, 18], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2962e5e-fa41-4e95-8870-66cf709d80c5", - "name": "Spawner (211)", - "location": [5623, 3875, 15], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c3108d12-e98e-4525-aea1-18bcdce8cb3f", - "name": "Spawner (211)", - "location": [5722, 3776, 32], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c3c7e0c0-82d2-4fa6-b8d8-ceb9131c811f", - "name": "Spawner (211)", - "location": [6022, 2874, 16], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c86b6986-06f2-4bfd-83a9-eb305dffd798", - "name": "Spawner (211)", - "location": [5985, 3441, 17], - "map": "Felucca", - "count": 36, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 90, - "entries": [ - { "name": "BullFrog", "maxCount": 15, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 5, "probability": 100 }, - { "name": "Bogling", "maxCount": 13, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c9cf3ac9-5078-48a6-8d1d-8a622fa60024", - "name": "Spawner (211)", - "location": [5580, 2885, 6], - "map": "Felucca", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Gazer", "maxCount": 3, "probability": 100 }, - { "name": "Reaper", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c9d49bec-96ad-41ab-8397-20f8cb54ab0f", - "name": "Spawner (211)", - "location": [6022, 3773, 18], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cc748df2-8d72-4078-a6a4-5a25406770a3", - "name": "Spawner (211)", - "location": [5729, 2767, -1], - "map": "Felucca", - "count": 14, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 60, - "walkingRange": 70, - "entries": [ - { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Wyvern", "maxCount": 3, "probability": 100 }, - { "name": "Mummy", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cc8c9793-e6fe-48b5-9b83-28d3178fa5a0", - "name": "Spawner (211)", - "location": [5367, 4003, 19], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d03b6997-0732-409a-a2ec-f92413ac6a08", - "name": "Spawner (211)", - "location": [5440, 3292, -4], - "map": "Felucca", - "count": 21, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 56, - "walkingRange": 62, - "entries": [ - { "name": "Gorilla", "maxCount": 5, "probability": 100 }, - { "name": "Panther", "maxCount": 5, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d2665bd3-8dfc-4e5d-aa4b-0810ebf249a3", - "name": "Spawner (211)", - "location": [5920, 3675, 12], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d2e9a141-6f17-4844-bbfa-a8e9adacbc50", - "name": "Spawner (211)", - "location": [5490, 3071, 15], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d41bb4a9-6e4c-4959-88ce-0f7cc1e518c3", - "name": "Spawner (211)", - "location": [5194, 3885, -1], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 32, - "walkingRange": 45, - "entries": [ - { "name": "Bull", "maxCount": 10, "probability": 100 }, - { "name": "GreatHart", "maxCount": 6, "probability": 100 }, - { "name": "Cow", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d51c7215-68f4-4c9f-aa0c-c15f8c0555ed", - "name": "Spawner (211)", - "location": [5819, 3974, 16], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d7e46421-f97f-427e-885c-2e3092bbbb23", - "name": "Spawner (211)", - "location": [5621, 3775, 2], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d8ac67eb-7ed1-4f41-80de-549bc900fccd", - "name": "Spawner (211)", - "location": [5521, 3875, -1], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d8f34e41-4ecb-4747-8300-cabad11db9f6", - "name": "Spawner (211)", - "location": [5501, 2361, 27], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 75, - "entries": [ - { "name": "PolarBear", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "db44f2a2-5df8-4de6-94c9-cbe8f792f3e5", - "name": "Spawner (211)", - "location": [5446, 3074, 3], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dbad34f7-3dad-4a18-8143-dfae7052b6a8", - "name": "Spawner (211)", - "location": [5520, 2890, 33], - "map": "Felucca", - "count": 37, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Imp", "maxCount": 10, "probability": 100 }, - { "name": "Scorpion", "maxCount": 10, "probability": 100 }, - { "name": "DesertOstard", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, - { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd202c62-aa55-4c9c-b3f6-1a0a6d44c32e", - "name": "Spawner (211)", - "location": [5367, 4003, 19], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rabbit", "maxCount": 20, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, - { "name": "Bird", "maxCount": 15, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 }, - { "name": "Hind", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e118382e-b3f3-4e8c-ae91-52fcb3eb6e79", - "name": "Spawner (211)", - "location": [5764, 2585, -4], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 75, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e14998fb-042e-4d70-809a-d5ac583494b0", - "name": "Spawner (211)", - "location": [5921, 3777, 28], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e1af1466-54a7-4db4-a6ba-d8867ab87512", - "name": "Spawner (211)", - "location": [5721, 3876, -3], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e6336daa-16af-48dd-ad9a-04b2aea717ca", - "name": "Spawner (211)", - "location": [5920, 3675, 12], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e646890b-dfcb-4572-bc2b-c4683711befa", - "name": "Spawner (211)", - "location": [6022, 2874, 16], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e64d60d3-0d19-4878-a69c-90548a99ef6e", - "name": "Spawner (211)", - "location": [5721, 3675, 22], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e79036f2-c56f-4467-8312-c95bf651af1c", - "name": "Spawner (211)", - "location": [5721, 3975, 21], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ed54effc-7e93-4d1e-be86-690a39878733", - "name": "Spawner (211)", - "location": [6022, 3971, 17], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ef9989f7-1231-48a7-b6ff-428d1df26bce", - "name": "Spawner (211)", - "location": [5838, 2651, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 75, - "entries": [ - { "name": "Imp", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Scorpion", "maxCount": 1, "probability": 100 }, - { "name": "StoneHarpy", "maxCount": 1, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f04320b3-02e3-457c-aa3d-85944e1987e1", "name": "Spawner (211)", "location": [5457, 2651, 45], @@ -3246,48 +35,145 @@ ] }, { - "$type": "Spawner", - "guid": "f19e0ef2-66a2-48b9-a6df-83027370fd1d", + "type": "Spawner", + "guid": "8d68603f-69e3-4d13-b972-2648fa6e6f5b", "name": "Spawner (211)", - "location": [5408, 3077, 16], + "location": [5457, 2651, 45], "map": "Felucca", - "count": 4, + "count": 19, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 6, "probability": 100 }, + { "name": "Mummy", "maxCount": 4, "probability": 100 }, + { "name": "Ettin", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbad34f7-3dad-4a18-8143-dfae7052b6a8", + "name": "Spawner (211)", + "location": [5520, 2890, 33], + "map": "Felucca", + "count": 37, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Imp", "maxCount": 10, "probability": 100 }, + { "name": "Scorpion", "maxCount": 10, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "413f3751-fb8f-4519-bc77-47bb4c6cb964", + "name": "Spawner (211)", + "location": [5520, 2890, 33], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 4, "probability": 100 }, + { "name": "Mummy", "maxCount": 2, "probability": 100 }, + { "name": "Ettin", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9cf3ac9-5078-48a6-8d1d-8a622fa60024", + "name": "Spawner (211)", + "location": [5580, 2885, 6], + "map": "Felucca", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "Reaper", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba775ba5-f4a7-462e-88f1-c9962147118a", + "name": "Spawner (211)", + "location": [5557, 2625, 0], + "map": "Felucca", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 3, "probability": 100 }, + { "name": "Troll", "maxCount": 3, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1eee493b-6795-4b24-b390-72c7cd5b2b9a", + "name": "Spawner (211)", + "location": [5394, 2530, 6], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, "walkingRange": 25, "entries": [ - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + { "name": "Cyclops", "maxCount": 5, "probability": 100 }, + { "name": "Titan", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f31f301e-888f-4493-a562-b05219ff599d", + "type": "Spawner", + "guid": "0f0fa954-8f4e-4a76-aa93-5e5bf5cb1735", "name": "Spawner (211)", - "location": [6022, 3971, 17], + "location": [5711, 2494, 46], "map": "Felucca", - "count": 7, + "count": 13, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, + "homeRange": 80, + "walkingRange": 80, "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + { "name": "Imp", "maxCount": 3, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f56b7a46-f8e5-4e85-aa15-9f30cf0c409d", "name": "Spawner (211)", "location": [5711, 2494, 46], @@ -3298,74 +184,10 @@ "team": 0, "homeRange": 80, "walkingRange": 80, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Ettin", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f706d02f-df96-4378-8e75-697f931d9eeb", - "name": "Spawner (211)", - "location": [5817, 2367, 42], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 75, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f72695d4-8b46-4aef-acd3-39aedce54767", - "name": "Spawner (211)", - "location": [5820, 3675, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f8499d9a-396d-46a9-abba-b78ce9de9e74", - "name": "Spawner (211)", - "location": [6022, 2874, 16], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "fa987bdc-474a-44da-91be-4b0bcd8cb807", "name": "Spawner (211)", "location": [5765, 2637, 39], @@ -3385,7 +207,494 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "12d2b037-bfa7-4ea1-9580-28e0623a2671", + "name": "Spawner (211)", + "location": [5697, 2626, -2], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "Wyvern", "maxCount": 2, "probability": 100 }, + { "name": "Drake", "maxCount": 4, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6e1001c3-4e95-479b-a49a-850853fd7e59", + "name": "Spawner (211)", + "location": [5729, 2767, -1], + "map": "Felucca", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 70, + "entries": [ + { "name": "Imp", "maxCount": 8, "probability": 100 }, + { "name": "Scorpion", "maxCount": 8, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc748df2-8d72-4078-a6a4-5a25406770a3", + "name": "Spawner (211)", + "location": [5729, 2767, -1], + "map": "Felucca", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 70, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "Mummy", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07a9c362-3e90-470f-8020-52320f19ef71", + "name": "Spawner (211)", + "location": [5724, 2949, 31], + "map": "Felucca", + "count": 44, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 65, + "walkingRange": 75, + "entries": [ + { "name": "HellHound", "maxCount": 8, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 8, "probability": 100 }, + { "name": "FireElemental", "maxCount": 10, "probability": 100 }, + { "name": "Efreet", "maxCount": 4, "probability": 100 }, + { "name": "HellCat", "maxCount": 6, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76d30438-3240-4879-aae2-083e9eecfd1f", + "name": "Spawner (211)", + "location": [5724, 2949, 31], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 65, + "walkingRange": 75, + "entries": [{ "name": "LavaSnake", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6fae27d7-64f5-4e31-8a41-381a6344a96d", + "name": "Spawner (211)", + "location": [5863, 2470, 46], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Wyvern", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0051ef48-a9fa-49be-8307-04ed26c3f8fc", + "name": "Spawner (211)", + "location": [5501, 2361, 27], + "map": "Felucca", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 8, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, + { "name": "Walrus", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8f34e41-4ecb-4747-8300-cabad11db9f6", + "name": "Spawner (211)", + "location": [5501, 2361, 27], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6ef79240-6f81-42f3-ae82-873af735b246", + "name": "Spawner (211)", + "location": [5351, 2359, 0], + "map": "Felucca", + "count": 29, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 7, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, + { "name": "Walrus", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04c56ada-2f70-4d20-a168-217066a7920d", + "name": "Spawner (211)", + "location": [5351, 2359, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e896c88-1cc3-4b00-a042-41b7daa309dc", + "name": "Spawner (211)", + "location": [5173, 2368, 21], + "map": "Felucca", + "count": 28, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 10, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, + { "name": "Walrus", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bd68fd6-cde9-45a8-8a99-8173b0e61e54", + "name": "Spawner (211)", + "location": [5173, 2368, 21], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "PolarBear", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "27c7c466-0a83-4356-8b5d-7aab73b1dde1", + "name": "Spawner (211)", + "location": [6091, 2379, 25], + "map": "Felucca", + "count": 28, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 60, + "entries": [ + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 10, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, + { "name": "Walrus", "maxCount": 3, "probability": 100 }, + { "name": "PolarBear", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9fcbab64-2922-4c73-b1a1-8c7304252e57", + "name": "Spawner (211)", + "location": [5967, 2370, 44], + "map": "Felucca", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 4, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c6585a9-e298-4753-a981-b77e13fec541", + "name": "Spawner (211)", + "location": [5967, 2370, 44], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7a81959d-4cbc-487c-a41c-867dfa194ca0", + "name": "Spawner (211)", + "location": [5817, 2367, 42], + "map": "Felucca", + "count": 23, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 3, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 3, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f706d02f-df96-4378-8e75-697f931d9eeb", + "name": "Spawner (211)", + "location": [5817, 2367, 42], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "26b4a453-57c9-4bcf-b19b-a5caace69ace", + "name": "Spawner (211)", + "location": [5756, 3453, -2], + "map": "Felucca", + "count": 42, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "Panther", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ab1c20f0-33bf-4b17-9d3e-b4a1bf3a7dc9", + "name": "Spawner (211)", + "location": [5985, 3441, 17], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 90, + "entries": [ + { "name": "GiantToad", "maxCount": 8, "probability": 100 }, + { "name": "SwampDragon", "maxCount": 5, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "Alligator", "maxCount": 12, "probability": 100 }, + { "name": "SewerRat", "maxCount": 11, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c86b6986-06f2-4bfd-83a9-eb305dffd798", + "name": "Spawner (211)", + "location": [5985, 3441, 17], + "map": "Felucca", + "count": 36, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 90, + "entries": [ + { "name": "BullFrog", "maxCount": 15, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 5, "probability": 100 }, + { "name": "Bogling", "maxCount": 13, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4fdfdb8-b446-4cad-a4d5-700f6e387112", + "name": "Spawner (211)", + "location": [5220, 3664, -1], + "map": "Felucca", + "count": 23, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 3, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 6, "probability": 100 }, + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "507fcaa7-e8ed-4ce1-93db-f6f6d4bc5abc", + "name": "Spawner (211)", + "location": [5212, 3619, 0], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "SavageRider", "maxCount": 3, "probability": 100 }, + { "name": "Savage", "maxCount": 9, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "048db5e6-adcb-4fec-91e7-3d12011d05b0", + "name": "Spawner (211)", + "location": [5347, 3449, 17], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b35c2359-f228-4ede-ab15-7677b251219d", + "name": "Spawner (211)", + "location": [5243, 3548, 14], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54cb046e-98e1-4971-8b9b-0b62d687c1ab", + "name": "Spawner (211)", + "location": [5250, 3449, 16], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "270bb58a-fbba-4af0-98b2-3cece2112336", + "name": "Spawner (211)", + "location": [5349, 3547, -1], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fba20373-697a-475b-8db9-52501d460fce", "name": "Spawner (211)", "location": [5201, 3162, 53], @@ -3410,7 +719,1769 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "150865b5-2285-49ea-94d4-42554405d181", + "name": "Spawner (211)", + "location": [5201, 3162, 53], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15e3c862-5e27-47f3-ba4d-8a9ee45c3623", + "name": "Spawner (211)", + "location": [5251, 3301, -2], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e69642d-acde-40f0-bc27-0e07d9292977", + "name": "Spawner (211)", + "location": [5251, 3301, -2], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4bc10a0b-643a-434c-af08-7b789cfcd954", + "name": "Spawner (211)", + "location": [5250, 3401, 1], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "673c07e2-de3f-4228-9c93-fcaa22aae807", + "name": "Spawner (211)", + "location": [5250, 3401, 1], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "273dd9e4-983d-4029-8b2b-3e2ea1178a95", + "name": "Spawner (211)", + "location": [5351, 3401, 30], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a102eab-82dd-45ca-9071-d38e2d759ce6", + "name": "Spawner (211)", + "location": [5351, 3401, 30], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "620213e0-2aad-4795-9d8a-1c326bb59212", + "name": "Spawner (211)", + "location": [6018, 3673, 18], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9d0907cc-7569-4001-9ef3-6c95a3273262", + "name": "Spawner (211)", + "location": [6018, 3673, 18], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9cf77fad-29ee-4dc8-a174-7adf1ba52831", + "name": "Spawner (211)", + "location": [6018, 3673, 18], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ac377e6-e430-4412-8fcd-8ed365924d58", + "name": "Spawner (211)", + "location": [6018, 3673, 18], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "66e10caf-9bd5-4f1c-8e03-6c0b8ef97cda", + "name": "Spawner (211)", + "location": [6022, 3773, 18], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15908c1e-bc66-4149-9c00-c11fe35e5a17", + "name": "Spawner (211)", + "location": [6022, 3773, 18], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9d49bec-96ad-41ab-8397-20f8cb54ab0f", + "name": "Spawner (211)", + "location": [6022, 3773, 18], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9b9dd7d-c27f-48e4-99b3-23cd89fc19cb", + "name": "Spawner (211)", + "location": [6022, 3773, 18], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f8499d9a-396d-46a9-abba-b78ce9de9e74", + "name": "Spawner (211)", + "location": [6022, 2874, 16], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3c7e0c0-82d2-4fa6-b8d8-ceb9131c811f", + "name": "Spawner (211)", + "location": [6022, 2874, 16], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e646890b-dfcb-4572-bc2b-c4683711befa", + "name": "Spawner (211)", + "location": [6022, 2874, 16], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04ebb8af-b6c7-488c-816c-b37805270c49", + "name": "Spawner (211)", + "location": [6022, 2874, 16], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc3fc7a6-6646-4c86-8830-883c67dd0938", + "name": "Spawner (211)", + "location": [6022, 3971, 17], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f31f301e-888f-4493-a562-b05219ff599d", + "name": "Spawner (211)", + "location": [6022, 3971, 17], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4da8db24-068c-406f-a561-6128621f78e6", + "name": "Spawner (211)", + "location": [6022, 3971, 17], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ed54effc-7e93-4d1e-be86-690a39878733", + "name": "Spawner (211)", + "location": [6022, 3971, 17], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d59b4f4-57c3-43ef-9fc2-0364e61a718c", + "name": "Spawner (211)", + "location": [5920, 3675, 12], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2665bd3-8dfc-4e5d-aa4b-0810ebf249a3", + "name": "Spawner (211)", + "location": [5920, 3675, 12], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6336daa-16af-48dd-ad9a-04b2aea717ca", + "name": "Spawner (211)", + "location": [5920, 3675, 12], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "616334e1-53ea-49c0-8901-856c483a69e5", + "name": "Spawner (211)", + "location": [5920, 3675, 12], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "942c695f-f39f-4c28-8704-9714152cdefd", + "name": "Spawner (211)", + "location": [5921, 3777, 28], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b532fb5d-8a9a-4ee4-8358-e7e2ef4095d9", + "name": "Spawner (211)", + "location": [5921, 3777, 28], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20911691-e6c3-4c40-87d1-ee67a34d2669", + "name": "Spawner (211)", + "location": [5921, 3777, 28], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e14998fb-042e-4d70-809a-d5ac583494b0", + "name": "Spawner (211)", + "location": [5921, 3777, 28], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa42da05-496c-43fd-8178-a7fdb9ebfe63", + "name": "Spawner (211)", + "location": [5919, 3875, 16], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4f901b5-6562-4689-9d5a-3ad3e3ad8a3f", + "name": "Spawner (211)", + "location": [5919, 3875, 16], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "092875e2-4178-4845-97e9-1d70ab565933", + "name": "Spawner (211)", + "location": [5919, 3875, 16], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fde4de4d-8369-49e1-944f-b76d4a2e42d2", + "name": "Spawner (211)", + "location": [5919, 3875, 16], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "775ba293-8e3a-4d62-bf10-7e002b3b2ec5", + "name": "Spawner (211)", + "location": [5918, 3975, 1], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55a13cb2-cc95-4d1b-8498-2babc070ce78", + "name": "Spawner (211)", + "location": [5918, 3975, 1], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "050b5b91-6840-4ec9-adca-c73658982e58", + "name": "Spawner (211)", + "location": [5918, 3975, 1], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae2d2521-6a54-46cc-8bd6-4e4ff10242e8", + "name": "Spawner (211)", + "location": [5918, 3975, 1], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a98d669c-759b-45b5-96ad-3a1153f38d9d", + "name": "Spawner (211)", + "location": [5820, 3675, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "741769ff-9984-44cf-a8b0-bdc393beffc2", + "name": "Spawner (211)", + "location": [5820, 3675, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f72695d4-8b46-4aef-acd3-39aedce54767", + "name": "Spawner (211)", + "location": [5820, 3675, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "30f49430-b98b-4da2-89b6-cfbf3f69182e", + "name": "Spawner (211)", + "location": [5820, 3675, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "738a7321-91fd-4205-97a0-cb9ef78e5ad0", + "name": "Spawner (211)", + "location": [5820, 3775, 48], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a9fe475-26ea-4f9c-a73b-e33ab2674130", + "name": "Spawner (211)", + "location": [5820, 3775, 48], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdfb73eb-b35a-4701-afe9-0487da724239", + "name": "Spawner (211)", + "location": [5820, 3775, 48], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b831bbb7-b063-4039-9d54-bdaf8c0a4cbf", + "name": "Spawner (211)", + "location": [5820, 3775, 48], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "421bbe19-6e35-4125-a521-0a5294553b06", + "name": "Spawner (211)", + "location": [5818, 3876, 18], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50aa6367-2c0f-475a-a0f7-81ee98f66244", + "name": "Spawner (211)", + "location": [5818, 3876, 18], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "872072bf-8139-497f-8338-76cc9b06d93e", + "name": "Spawner (211)", + "location": [5818, 3876, 18], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf8af0ff-9824-45cb-b254-bb762019c7ca", + "name": "Spawner (211)", + "location": [5818, 3876, 18], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93c70268-f78d-436e-b3c0-3dd39fbadad3", + "name": "Spawner (211)", + "location": [5819, 3974, 16], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d51c7215-68f4-4c9f-aa0c-c15f8c0555ed", + "name": "Spawner (211)", + "location": [5819, 3974, 16], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3b2a1a28-2c30-483f-833c-1a9ad40bd153", + "name": "Spawner (211)", + "location": [5819, 3974, 16], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf46ef5b-0f23-4bd9-93ce-b6bf724fd816", + "name": "Spawner (211)", + "location": [5819, 3974, 16], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e64d60d3-0d19-4878-a69c-90548a99ef6e", + "name": "Spawner (211)", + "location": [5721, 3675, 22], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2694242b-1d3f-4dfc-96c0-892202e3b950", + "name": "Spawner (211)", + "location": [5721, 3675, 22], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "42d4b8fa-99ad-412e-9df7-bd3f92c023ac", + "name": "Spawner (211)", + "location": [5721, 3675, 22], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "80c6a933-7596-4e44-9ecb-006e36b52a4c", + "name": "Spawner (211)", + "location": [5721, 3675, 22], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54cc6589-3bd8-4258-a88c-834c928a2257", + "name": "Spawner (211)", + "location": [5722, 3776, 32], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0fe27512-76eb-4830-939d-36ab2aafaa42", + "name": "Spawner (211)", + "location": [5722, 3776, 32], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3108d12-e98e-4525-aea1-18bcdce8cb3f", + "name": "Spawner (211)", + "location": [5722, 3776, 32], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d1cecc3-832e-4898-ae75-bce89940fb02", + "name": "Spawner (211)", + "location": [5722, 3776, 32], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21f61927-4b6f-4b7d-8c37-4e0dbab8a750", + "name": "Spawner (211)", + "location": [5721, 3876, -3], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1af1466-54a7-4db4-a6ba-d8867ab87512", + "name": "Spawner (211)", + "location": [5721, 3876, -3], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88937169-f283-4c78-9d28-7f03ac03872c", + "name": "Spawner (211)", + "location": [5721, 3876, -3], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "585f8ed2-b655-4de7-b9c8-e937bd858d43", + "name": "Spawner (211)", + "location": [5721, 3876, -3], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8bbb0aca-5066-44cc-80b6-2a6e4862d6df", + "name": "Spawner (211)", + "location": [5721, 3975, 21], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e79036f2-c56f-4467-8312-c95bf651af1c", + "name": "Spawner (211)", + "location": [5721, 3975, 21], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4562d6b-3ea3-46c0-9a75-d102a5da6fef", + "name": "Spawner (211)", + "location": [5721, 3975, 21], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a31f2cf-8495-4789-b1d7-3a480f373f73", + "name": "Spawner (211)", + "location": [5721, 3975, 21], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41d9a9e4-66be-4a71-a1ea-b8dec95e6258", + "name": "Spawner (211)", + "location": [5623, 3676, -3], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c010aa0-3974-42d1-ace5-aefc3014eda1", + "name": "Spawner (211)", + "location": [5623, 3676, -3], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9d472611-6a0e-4aca-ba28-e3b6f3e2bb1e", + "name": "Spawner (211)", + "location": [5623, 3676, -3], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95f3b7c6-e49c-483f-8598-6a5c71f6e959", + "name": "Spawner (211)", + "location": [5623, 3676, -3], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "783e28bf-94fe-445d-b357-a4009b10eab0", + "name": "Spawner (211)", + "location": [5621, 3775, 2], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d7e46421-f97f-427e-885c-2e3092bbbb23", + "name": "Spawner (211)", + "location": [5621, 3775, 2], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "354f9453-76f6-4cc8-8255-873544d38355", + "name": "Spawner (211)", + "location": [5621, 3775, 2], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6488c8bb-5ced-4f2f-9f3a-6f80958d1ae6", + "name": "Spawner (211)", + "location": [5621, 3775, 2], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5702bcbc-9e12-44b5-a382-d2a56acda20f", + "name": "Spawner (211)", + "location": [5623, 3875, 15], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2962e5e-fa41-4e95-8870-66cf709d80c5", + "name": "Spawner (211)", + "location": [5623, 3875, 15], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21cba4e8-69f4-49b4-bdc2-1b5893001823", + "name": "Spawner (211)", + "location": [5623, 3875, 15], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fdc76541-5d27-4dcf-ab0e-fb96e4beb7e8", "name": "Spawner (211)", "location": [5623, 3875, 15], @@ -3429,10 +2500,77 @@ ] }, { - "$type": "Spawner", - "guid": "fde4de4d-8369-49e1-944f-b76d4a2e42d2", + "type": "Spawner", + "guid": "6b8e8c6a-2fb2-443a-a5ac-6ba23572d23c", "name": "Spawner (211)", - "location": [5919, 3875, 16], + "location": [5624, 3975, -3], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "64a11ac5-1b77-4d46-beed-78d63b227f89", + "name": "Spawner (211)", + "location": [5624, 3975, -3], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "35afdfbe-5809-4afe-bf7a-b2de7ef3e6a4", + "name": "Spawner (211)", + "location": [5624, 3975, -3], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a5e6b73-0af4-4461-96af-e980adc67771", + "name": "Spawner (211)", + "location": [5624, 3975, -3], "map": "Felucca", "count": 5, "minDelay": "00:05:00", @@ -3446,5 +2584,847 @@ { "name": "Reaper", "maxCount": 1, "probability": 100 }, { "name": "Troll", "maxCount": 2, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "62947b20-aa82-480c-887f-bbacd25e698f", + "name": "Spawner (211)", + "location": [5523, 3676, 45], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7a43ad68-7a8b-43f6-b8e4-2f5bce9b3c1e", + "name": "Spawner (211)", + "location": [5523, 3676, 45], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d4a0fe3-6d82-42e1-83a8-8eea8de98aee", + "name": "Spawner (211)", + "location": [5523, 3676, 45], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6ff7fdd5-e4c7-41ea-958f-8b030f8aca55", + "name": "Spawner (211)", + "location": [5523, 3676, 45], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21157414-21f7-4eb1-84e8-cb9ffb3b0b1b", + "name": "Spawner (211)", + "location": [5521, 3775, 8], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d35c378-e89b-4bcc-9074-bd08850092de", + "name": "Spawner (211)", + "location": [5521, 3775, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "38fd8a6b-4deb-4f8f-87be-70dcfd5b4055", + "name": "Spawner (211)", + "location": [5521, 3775, 8], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4438dccc-cae8-4b97-8079-baee38c36b7a", + "name": "Spawner (211)", + "location": [5521, 3775, 8], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8ac67eb-7ed1-4f41-80de-549bc900fccd", + "name": "Spawner (211)", + "location": [5521, 3875, -1], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23eb1938-b1a7-400d-a9dd-12d6f514a3c5", + "name": "Spawner (211)", + "location": [5521, 3875, -1], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "224ddc6f-f8ea-4752-8209-4a8484eeca66", + "name": "Spawner (211)", + "location": [5521, 3875, -1], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8be2f678-bffa-4aa1-acf4-d4e3d2cfd92d", + "name": "Spawner (211)", + "location": [5521, 3875, -1], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b991a2d-71f9-42d1-9d66-7a7a4d6ba42e", + "name": "Spawner (211)", + "location": [5478, 3587, 3], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6686565b-fadf-4063-9125-c8fe0a138653", + "name": "Spawner (211)", + "location": [5478, 3587, 3], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "18e0751c-cee9-4120-8873-04f87bfe4772", + "name": "Spawner (211)", + "location": [5478, 3587, 3], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a30ea72-ec30-49a4-be3f-1f7a4a0217b2", + "name": "Spawner (211)", + "location": [5478, 3587, 3], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "675ac5d1-85b0-4fe0-89c3-011c4ee3be55", + "name": "Spawner (211)", + "location": [5520, 3939, 42], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "833ac732-4f3f-4863-8b88-f1b52673d063", + "name": "Spawner (211)", + "location": [5784, 3951, 20], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [{ "name": "FrenziedOstard", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e118382e-b3f3-4e8c-ae91-52fcb3eb6e79", + "name": "Spawner (211)", + "location": [5764, 2585, -4], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 75, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef9989f7-1231-48a7-b6ff-428d1df26bce", + "name": "Spawner (211)", + "location": [5838, 2651, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 75, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3781051c-c4bb-4e65-b4f5-5e09c44b9a31", + "name": "Spawner (211)", + "location": [5218, 3786, 1], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "273c3edf-416a-4113-b68a-fe94b128b63d", + "name": "Spawner (211)", + "location": [5218, 3786, 1], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6ad3a966-1fcf-45e8-b36a-f3f2c5cbd374", + "name": "Spawner (211)", + "location": [5371, 3820, -2], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3612f07e-f714-4ad8-bec2-e7bbca18b0ea", + "name": "Spawner (211)", + "location": [5371, 3820, -2], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c05974e-e051-411c-8e9d-991adc1b11d7", + "name": "Spawner (211)", + "location": [5368, 3887, 0], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5821f460-0bda-4984-8a75-10c711c09f64", + "name": "Spawner (211)", + "location": [5368, 3887, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd202c62-aa55-4c9c-b3f6-1a0a6d44c32e", + "name": "Spawner (211)", + "location": [5367, 4003, 19], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc8c9793-e6fe-48b5-9b83-28d3178fa5a0", + "name": "Spawner (211)", + "location": [5367, 4003, 19], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b424a6e3-dd2b-4140-a335-fa3916397531", + "name": "Spawner (211)", + "location": [5466, 4005, -1], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5d11127-71e9-4521-88ae-739adb829541", + "name": "Spawner (211)", + "location": [5466, 4005, -1], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d41bb4a9-6e4c-4959-88ce-0f7cc1e518c3", + "name": "Spawner (211)", + "location": [5194, 3885, -1], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 32, + "walkingRange": 45, + "entries": [ + { "name": "Bull", "maxCount": 10, "probability": 100 }, + { "name": "GreatHart", "maxCount": 6, "probability": 100 }, + { "name": "Cow", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33c93a22-4e4d-4b81-a122-0fc480c300a2", + "name": "Spawner (211)", + "location": [5200, 3895, 2], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bc4fc91-8bea-4601-990a-f5bfac3d090e", + "name": "Spawner (211)", + "location": [5159, 3913, 1], + "map": "Felucca", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 10, + "entries": [ + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 1, "probability": 100 }, + { "name": "Bull", "maxCount": 1, "probability": 100 }, + { "name": "Chicken", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13efbe43-2edd-44f9-8282-5b3274f79912", + "name": "Spawner (211)", + "location": [5570, 3153, 14], + "map": "Felucca", + "count": 25, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 80, + "entries": [ + { "name": "Gorilla", "maxCount": 6, "probability": 100 }, + { "name": "Panther", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d03b6997-0732-409a-a2ec-f92413ac6a08", + "name": "Spawner (211)", + "location": [5440, 3292, -4], + "map": "Felucca", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 56, + "walkingRange": 62, + "entries": [ + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "Panther", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84a12c7e-b2f6-4713-8d14-5c768f38358b", + "name": "Spawner (211)", + "location": [5566, 3292, 4], + "map": "Felucca", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 56, + "walkingRange": 62, + "entries": [ + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "Panther", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91259235-0484-4698-906e-bf63a077d599", + "name": "Spawner (211)", + "location": [5454, 3136, -60], + "map": "Felucca", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "TerathanMatriarch", "maxCount": 2, "probability": 100 }, + { "name": "TerathanAvenger", "maxCount": 4, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 10, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2e9a141-6f17-4844-bbfa-a8e9adacbc50", + "name": "Spawner (211)", + "location": [5490, 3071, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "00b71fd9-b553-4846-8cc6-5ceaf42ba4b3", + "name": "Spawner (211)", + "location": [5467, 3078, -3], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db44f2a2-5df8-4de6-94c9-cbe8f792f3e5", + "name": "Spawner (211)", + "location": [5446, 3074, 3], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "440d81ae-8e54-44db-b6de-909abe0e75b8", + "name": "Spawner (211)", + "location": [5432, 3079, -4], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f19e0ef2-66a2-48b9-a6df-83027370fd1d", + "name": "Spawner (211)", + "location": [5408, 3077, 16], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/OrcCaves.json b/Distribution/Data/Spawns/post-uoml/felucca/OrcCaves.json similarity index 84% rename from Distribution/Data/Spawns/shared/felucca/OrcCaves.json rename to Distribution/Data/Spawns/post-uoml/felucca/OrcCaves.json index da3bf0e2d..bafbd9be2 100644 --- a/Distribution/Data/Spawns/shared/felucca/OrcCaves.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/OrcCaves.json @@ -1,24 +1,24 @@ [ { - "$type": "Spawner", - "guid": "01ae830b-c7f9-4857-8a6b-4bacca6758ad", + "type": "Spawner", + "guid": "572a2fac-11c7-4583-837a-1b03eae15f85", "name": "Spawner (212)", - "location": [5356, 1304, 0], + "location": [5146, 1995, 0], "map": "Felucca", - "count": 4, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 8, - "walkingRange": 10, + "walkingRange": 8, "entries": [ - { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 1, "probability": 100 }, { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } + { "name": "OrcishLord", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "03b59250-db54-481c-85f4-1d0b8b433252", "name": "Spawner (212)", "location": [5153, 1971, 0], @@ -29,12 +29,66 @@ "team": 0, "homeRange": 8, "walkingRange": 8, - "entries": [ - { "name": "Orc", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Orc", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "a1dbbaf6-03e3-42f2-ba42-35d88349fd0c", + "name": "Spawner (212)", + "location": [5153, 1961, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "DireWolf", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5145b262-72b3-4bf2-8a73-91efe509ce18", + "name": "Spawner (212)", + "location": [5144, 1960, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "OrcishLord", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fe0be93b-5f45-40b0-9169-16df378c0e6e", + "name": "Spawner (212)", + "location": [5141, 1968, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "OrcCaptain", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ee800978-f2bf-47e0-8e19-c7f5fe0b33bc", + "name": "Spawner (212)", + "location": [5332, 1365, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "0e858157-adc4-4963-a34b-101fefb109d1", "name": "Spawner (212)", "location": [5308, 1372, 0], @@ -45,12 +99,27 @@ "team": 0, "homeRange": 8, "walkingRange": 8, + "entries": [{ "name": "Noble", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "593ee3b4-df42-426d-8976-faced7cd2435", + "name": "Spawner (212)", + "location": [5302, 1355, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 12, "entries": [ - { "name": "Noble", "maxCount": 1, "probability": 100 } + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1a6833ee-9e32-4900-ad6e-87926e8fb427", "name": "Spawner (212)", "location": [5316, 1332, 0], @@ -61,46 +130,24 @@ "team": 0, "homeRange": 8, "walkingRange": 8, - "entries": [ - { "name": "GiantRat", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "GiantRat", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "1bff583d-3643-4e60-9a44-e3dea6882a02", + "type": "Spawner", + "guid": "70960aad-63ab-4bc2-9784-0ae5db883fe1", "name": "Spawner (212)", - "location": [5308, 2005, 0], + "location": [5331, 1346, 0], "map": "Felucca", - "count": 5, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "OrcBrute", "maxCount": 1, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 } - ] + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "DireWolf", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "2d24f1f9-20ff-4ee1-8f31-8304e2e4822d", - "name": "Spawner (212)", - "location": [5310, 1969, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "OrcBomber", "maxCount": 2, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "40c5eacc-9b5d-49f4-b13d-c605a32f6816", "name": "Spawner (212)", "location": [5354, 1332, 0], @@ -119,7 +166,25 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "01ae830b-c7f9-4857-8a6b-4bacca6758ad", + "name": "Spawner (212)", + "location": [5356, 1304, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [ + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "471bb8b3-f768-4654-a3ae-9517ecc8dd45", "name": "Spawner (212)", "location": [5317, 1309, 0], @@ -138,139 +203,7 @@ ] }, { - "$type": "Spawner", - "guid": "5145b262-72b3-4bf2-8a73-91efe509ce18", - "name": "Spawner (212)", - "location": [5144, 1960, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "OrcishLord", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "572a2fac-11c7-4583-837a-1b03eae15f85", - "name": "Spawner (212)", - "location": [5146, 1995, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Orc", "maxCount": 1, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "593ee3b4-df42-426d-8976-faced7cd2435", - "name": "Spawner (212)", - "location": [5302, 1355, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 12, - "entries": [ - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "70960aad-63ab-4bc2-9784-0ae5db883fe1", - "name": "Spawner (212)", - "location": [5331, 1346, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "DireWolf", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81a40400-c93d-42f3-8f19-0e9492198a82", - "name": "Spawner (212)", - "location": [5292, 1316, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Corpser", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a1dbbaf6-03e3-42f2-ba42-35d88349fd0c", - "name": "Spawner (212)", - "location": [5153, 1961, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "DireWolf", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a70f186a-94a1-4f54-963f-5c039c7d4006", - "name": "Spawner (212)", - "location": [5348, 2011, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d01dca90-9fac-438e-9660-a93e9df7fc58", - "name": "Spawner (212)", - "location": [5281, 2029, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "d313d866-1199-45fc-a72c-018d146bbe8c", "name": "Spawner (212)", "location": [5300, 1314, 0], @@ -288,10 +221,24 @@ ] }, { - "$type": "Spawner", - "guid": "ee800978-f2bf-47e0-8e19-c7f5fe0b33bc", + "type": "Spawner", + "guid": "81a40400-c93d-42f3-8f19-0e9492198a82", "name": "Spawner (212)", - "location": [5332, 1365, 0], + "location": [5292, 1316, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Corpser", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d01dca90-9fac-438e-9660-a93e9df7fc58", + "name": "Spawner (212)", + "location": [5281, 2029, 0], "map": "Felucca", "count": 3, "minDelay": "00:05:00", @@ -299,24 +246,57 @@ "team": 0, "homeRange": 8, "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1bff583d-3643-4e60-9a44-e3dea6882a02", + "name": "Spawner (212)", + "location": [5308, 2005, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, "entries": [ - { "name": "Orc", "maxCount": 3, "probability": 100 } + { "name": "OrcBrute", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fe0be93b-5f45-40b0-9169-16df378c0e6e", + "type": "Spawner", + "guid": "2d24f1f9-20ff-4ee1-8f31-8304e2e4822d", "name": "Spawner (212)", - "location": [5141, 1968, 0], + "location": [5310, 1969, 0], "map": "Felucca", - "count": 3, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 8, - "walkingRange": 8, + "homeRange": 12, + "walkingRange": 12, "entries": [ - { "name": "OrcCaptain", "maxCount": 3, "probability": 100 } + { "name": "OrcBomber", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a70f186a-94a1-4f54-963f-5c039c7d4006", + "name": "Spawner (212)", + "location": [5348, 2011, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 4, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Outdoors.json b/Distribution/Data/Spawns/post-uoml/felucca/Outdoors.json similarity index 95% rename from Distribution/Data/Spawns/shared/felucca/Outdoors.json rename to Distribution/Data/Spawns/post-uoml/felucca/Outdoors.json index a472b5cfa..29586316a 100644 --- a/Distribution/Data/Spawns/shared/felucca/Outdoors.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Outdoors.json @@ -1,25 +1,310 @@ [ { - "$type": "Spawner", - "guid": "001a5320-820c-4300-96f9-676e428b55be", + "type": "Spawner", + "guid": "33c2dd22-28c7-4365-8d2a-61a1594edcc5", "name": "Spawner (213)", - "location": [4066, 569, 0], + "location": [3619, 2814, 21], + "map": "Felucca", + "count": 17, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "HordeMinion", "maxCount": 17, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a9fffefd-9314-4e62-b029-0b55d7a51418", + "name": "Spawner (213)", + "location": [3619, 2814, 21], + "map": "Felucca", + "count": 17, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Mongbat", "maxCount": 17, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "38809d23-8eee-4351-a9fd-337ee44ceb7c", + "name": "Spawner (213)", + "location": [1147, 3524, 0], "map": "Felucca", "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "minDelay": "01:00:00", + "maxDelay": "01:00:00", "team": 0, - "homeRange": 80, - "walkingRange": 80, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "PolarBear", "maxCount": 8, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 8, "probability": 100 }, - { "name": "Walrus", "maxCount": 8, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 8, "probability": 100 } + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "216f74af-209f-4943-ac78-cb2703ba7f06", + "name": "Spawner (213)", + "location": [1266, 1982, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f886f7b8-60a5-45c0-bf80-05032137df23", + "name": "Spawner (213)", + "location": [1249, 2030, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "74a9ed57-25f8-4fe2-8286-2696225764f3", + "name": "Spawner (213)", + "location": [1232, 2093, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3f9d6f0d-92a3-4b72-a6aa-d8bfba12e7f7", + "name": "Spawner (213)", + "location": [1140, 2087, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55af7b2a-3f27-4bf0-9d5e-bcedbdb3b76b", + "name": "Spawner (213)", + "location": [1089, 2033, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10667ef8-7144-444e-a63b-8bfe0ff5c55d", + "name": "Spawner (213)", + "location": [1067, 2117, 5], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c51a903d-62e2-4963-81f0-c6a4a2efb2d0", + "name": "Spawner (213)", + "location": [666, 1825, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "156f17d7-f242-4657-87fd-48b947ad6f65", + "name": "Spawner (213)", + "location": [741, 1838, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f63dafa0-bd09-499c-a34d-e3555d08fdc9", + "name": "Spawner (213)", + "location": [733, 1937, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "70cee935-0141-46ca-a61d-693c873af487", + "name": "Spawner (213)", + "location": [798, 1935, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "def51564-1874-418a-972b-13e052b9d80b", + "name": "Spawner (213)", + "location": [819, 1841, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3199ad8-a84a-4919-8353-b3950ebbd19f", + "name": "Spawner (213)", + "location": [893, 1861, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd52d8c8-0b49-4626-94c2-55a0aba0d970", + "name": "Spawner (213)", + "location": [879, 1956, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "004df2b4-e99d-4946-8dec-4b088fa75ea3", "name": "Spawner (213)", "location": [809, 1615, 0], @@ -36,78 +321,7 @@ ] }, { - "$type": "Spawner", - "guid": "004eb6c0-e616-488d-8331-baaca386890e", - "name": "Spawner (213)", - "location": [1231, 192, 6], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "005e6436-1cae-4ece-a97a-a95fae8949ce", - "name": "Spawner (213)", - "location": [899, 1851, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "006eec8a-3a67-4461-9688-1d7d0bfa0f1d", - "name": "Spawner (213)", - "location": [1055, 1442, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "024c3335-3bed-4475-bbc8-cda05fca9d6e", "name": "Spawner (213)", "location": [4461, 3106, 0], @@ -118,12 +332,1168 @@ "team": 0, "homeRange": 30, "walkingRange": 30, + "entries": [{ "name": "OrcishMage", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "890ffbb5-4dab-44d2-b7b0-7d11528f7bb0", + "name": "Spawner (213)", + "location": [4729, 3485, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc0d728c-fa4a-4913-8834-555178746625", + "name": "Spawner (213)", + "location": [4519, 3097, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "381a50a4-7600-462c-b95e-9c79f96e610e", + "name": "Spawner (213)", + "location": [4613, 3272, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0d4101a9-dd3b-4e5e-b95b-f538addb8324", + "name": "Spawner (213)", + "location": [4576, 3356, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "860745e5-2133-46bb-bf8b-b8cd1273b204", + "name": "Spawner (213)", + "location": [4658, 3376, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6fad7f26-0faf-4aa1-a25f-c050cc1e0143", + "name": "Spawner (213)", + "location": [4607, 3480, 29], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cbf48240-08c5-4939-9f2b-ec6e972b25a9", + "name": "Spawner (213)", + "location": [3485, 2595, 12], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "OrcishMage", "maxCount": 5, "probability": 100 } + { "name": "Brigand", "maxCount": 3, "probability": 100 }, + { "name": "Harpy", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Troll", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "4124fe40-7dde-4264-8393-5f92e87bdd80", + "name": "Spawner (213)", + "location": [3446, 2741, 49], + "map": "Felucca", + "count": 7, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Brigand", "maxCount": 7, "probability": 100 }, + { "name": "Harpy", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21abb157-384a-4001-9ba6-743cd407fe3f", + "name": "Spawner (213)", + "location": [3459, 2655, 9], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Brigand", "maxCount": 3, "probability": 100 }, + { "name": "Harpy", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Troll", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8211671-8686-48e8-ae56-66a5d815ecda", + "name": "Spawner (213)", + "location": [3379, 2693, 35], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Brigand", "maxCount": 3, "probability": 100 }, + { "name": "Harpy", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Troll", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "769cc51f-efed-48b2-ba94-013f544fcb5a", + "name": "Spawner (213)", + "location": [3411, 2592, 44], + "map": "Felucca", + "count": 6, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Brigand", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Spectre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2e12985-afee-4b4d-8981-d50e99e1a1d6", + "name": "Spawner (213)", + "location": [3464, 2503, 49], + "map": "Felucca", + "count": 7, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Brigand", "maxCount": 7, "probability": 100 }, + { "name": "Harpy", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dcd101fb-c10a-4fe2-a5b2-9e5d7ac965ca", + "name": "Spawner (213)", + "location": [3168, 629, 8], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa67c8fa-91bc-4db4-9db9-ce9ba0440401", + "name": "Spawner (213)", + "location": [3172, 700, 8], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "324e278d-d03c-4a19-9f7c-90983f0f7372", + "name": "Spawner (213)", + "location": [3173, 771, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f69aa349-7dd7-4a24-945e-f75cd274cfba", + "name": "Spawner (213)", + "location": [1473, 229, 22], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68e9fc19-85e7-41d6-9791-d22fdad0e11b", + "name": "Spawner (213)", + "location": [1337, 293, 22], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5630cd94-517e-4ddf-856d-fe59b8d2fc7a", + "name": "Spawner (213)", + "location": [1184, 217, 27], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8aa3fee7-2653-4bb2-828b-c1952fa27afe", + "name": "Spawner (213)", + "location": [1284, 433, 6], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47035c00-635d-410e-9208-3d07ee948ff6", + "name": "Spawner (213)", + "location": [1203, 502, 30], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a5ccea8-791c-4a24-a5b8-1107fbd32673", + "name": "Spawner (213)", + "location": [1300, 607, 15], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12281c4e-e6de-4abf-8313-db1b6895eaee", + "name": "Spawner (213)", + "location": [1200, 633, 6], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46abf595-aaae-4e1c-85e6-8cb7b463409c", + "name": "Spawner (213)", + "location": [1162, 704, 6], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "64615b94-7440-4c4b-873a-4b530cfd29e5", + "name": "Spawner (213)", + "location": [1130, 830, 9], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd655b49-23cd-410b-b7a4-8c959b5bd7d5", + "name": "Spawner (213)", + "location": [1013, 980, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "499d9dab-ec87-4739-8055-7ffd7a54cecf", + "name": "Spawner (213)", + "location": [848, 917, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47fe6452-2b6d-486b-850c-86cb477c5e9c", + "name": "Spawner (213)", + "location": [840, 1063, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ffe958cd-13e8-4caf-9694-af4cb65b8842", + "name": "Spawner (213)", + "location": [1038, 1149, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13d7ad11-9375-4baf-8a4e-27993057790e", + "name": "Spawner (213)", + "location": [844, 1235, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3177e9c8-ff1d-48b8-8280-3a66a202526b", + "name": "Spawner (213)", + "location": [617, 1326, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c25e54a0-e617-4cae-b5d0-0aeffa888d1a", + "name": "Spawner (213)", + "location": [1831, 449, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85b3d37c-21b5-484e-841c-a2344d3a911b", + "name": "Spawner (213)", + "location": [1832, 517, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07aff726-fbd5-4a47-a768-3ec347652b7f", + "name": "Spawner (213)", + "location": [1749, 474, 6], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60caf162-33d5-4a0d-a754-04c8327bf10c", + "name": "Spawner (213)", + "location": [1636, 532, 22], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1687b6e3-dbd1-4203-83e6-3b42459a20c5", + "name": "Spawner (213)", + "location": [1743, 589, 16], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23af0fb5-5bba-41f3-8caa-1f8f5018f42f", + "name": "Spawner (213)", + "location": [1887, 591, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9436808-f34f-438e-8b11-31f6470faf91", + "name": "Spawner (213)", + "location": [1810, 647, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb8b69ec-7762-4c73-82fa-cd4c6ab12311", + "name": "Spawner (213)", + "location": [1631, 650, 16], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b782447-4c8b-4dbf-aad3-2d537084e961", + "name": "Spawner (213)", + "location": [1723, 683, 6], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bdd2249-7e2c-44b8-9ae3-0d4eb5f5f224", + "name": "Spawner (213)", + "location": [1980, 832, -1], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f13df549-7ce0-4283-87e7-ef2971f81d03", + "name": "Spawner (213)", + "location": [1905, 883, -1], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c079015-240e-4b1c-bf6f-9802277d8148", + "name": "Spawner (213)", + "location": [1849, 953, 7], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "06c5aa59-1beb-4100-abea-13c6d2079025", + "name": "Spawner (213)", + "location": [1001, 787, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65846901-1333-4a0a-ab1d-85e2424e0b46", + "name": "Spawner (213)", + "location": [987, 725, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ee6239d0-b83b-434d-831f-b5b533dfbb7d", + "name": "Spawner (213)", + "location": [1912, 1283, 0], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c119e387-73ae-4d7f-8104-a4eb9618a34b", + "name": "Spawner (213)", + "location": [1787, 1280, 0], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc9e19b0-3e29-4bab-b86f-980379e3a230", + "name": "Spawner (213)", + "location": [1799, 1394, 0], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5c244d89-f413-49e1-abf4-70086903d0bd", + "name": "Spawner (213)", + "location": [1652, 1400, 0], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "48847927-65aa-4abd-8423-86546faa0c80", + "name": "Spawner (213)", + "location": [1755, 1474, 0], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "409e129c-f95e-4a05-aff8-e2d577aede77", + "name": "Spawner (213)", + "location": [1325, 1071, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Troll", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "401fe491-1378-4d69-ae3e-4cbfbe471991", + "name": "Spawner (213)", + "location": [2369, 3430, 3], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "11bc853d-5908-4b28-9578-8c556ee3d7e1", + "name": "Spawner (213)", + "location": [2354, 3503, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "181514b8-e4b3-442a-920c-27b56e3df585", + "name": "Spawner (213)", + "location": [2503, 3593, 3], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b8e488f-bcb3-4037-a201-fce494c31f3d", + "name": "Spawner (213)", + "location": [2520, 3983, 3], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5988167-2735-4863-a380-20a2ab27f70c", + "name": "Spawner (213)", + "location": [2438, 3914, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03cdfce6-9d4a-4ce4-b09b-fccff27ba52e", + "name": "Spawner (213)", + "location": [2143, 3953, 3], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aec49b53-770f-44dd-899a-3447bcf40650", + "name": "Spawner (213)", + "location": [868, 488, 0], + "map": "Felucca", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "198ec5be-f20d-4f44-a5c2-36c153751cb6", + "name": "Spawner (213)", + "location": [805, 483, 0], + "map": "Felucca", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02e35c83-6913-4607-a063-677964db0928", + "name": "Spawner (213)", + "location": [839, 406, 0], + "map": "Felucca", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "343b53bd-40ed-499b-93cf-60b7c854e810", + "name": "Spawner (213)", + "location": [841, 344, 0], + "map": "Felucca", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d842bcc-2ef7-4842-a73a-0bbd654b6056", + "name": "Spawner (213)", + "location": [1131, 3415, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4c5eb40-bd81-4de8-ba47-459319c56d4f", + "name": "Spawner (213)", + "location": [1158, 3457, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5438491f-692d-4434-9731-0bd0e7cf96c0", + "name": "Spawner (213)", + "location": [1094, 3482, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "02650c18-4ae6-4d51-affd-69bc2244fb28", "name": "Spawner (213)", "location": [1312, 613, 10], @@ -150,5243 +1520,7 @@ ] }, { - "$type": "Spawner", - "guid": "02e35c83-6913-4607-a063-677964db0928", - "name": "Spawner (213)", - "location": [839, 406, 0], - "map": "Felucca", - "count": 2, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "02ea296d-d8b7-4ecd-ba51-56469126e50b", - "name": "Spawner (213)", - "location": [2485, 191, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "03cdfce6-9d4a-4ce4-b09b-fccff27ba52e", - "name": "Spawner (213)", - "location": [2143, 3953, 3], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "043c3e4b-4a7f-4cf4-b5f7-54064d1c0aeb", - "name": "Spawner (213)", - "location": [1015, 706, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "067a213b-da9a-43e9-baac-1c2628a6e58e", - "name": "Spawner (213)", - "location": [1226, 449, 1], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "06c5aa59-1beb-4100-abea-13c6d2079025", - "name": "Spawner (213)", - "location": [1001, 787, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 3, "probability": 100 }, - { "name": "Lich", "maxCount": 3, "probability": 100 }, - { "name": "LichLord", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 }, - { "name": "Zombie", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "070b08c1-489a-4446-a535-8ba4828a641b", - "name": "Spawner (213)", - "location": [1915, 2385, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "071f9e9b-26c7-4bc6-a5a0-7867a5049939", - "name": "Spawner (213)", - "location": [4493, 1364, 23], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Boar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 }, - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0731f9aa-2216-4b25-97f1-bcfa2c54688d", - "name": "Spawner (213)", - "location": [1474, 1297, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "07aff726-fbd5-4a47-a768-3ec347652b7f", - "name": "Spawner (213)", - "location": [1749, 474, 6], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "08f49123-9ff1-456d-90ec-e5aa91908226", - "name": "Spawner (213)", - "location": [2549, 136, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "094d699e-d6ad-4322-b6eb-4a6883da6fe2", - "name": "Spawner (213)", - "location": [2177, 3034, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Spectre", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "09c20c39-441d-4c33-8089-aab62d941c8a", - "name": "Spawner (213)", - "location": [3382, 280, 9], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0bfb8c5f-2695-41d7-8978-7e1155117e57", - "name": "Spawner (213)", - "location": [1930, 864, -1], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0c68cb1c-7662-4ebc-a9e9-166ce2574f9e", - "name": "Spawner (213)", - "location": [4531, 1363, 23], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 15, - "entries": [ - { "name": "Mummy", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "Scorpion", "maxCount": 1, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0cd04146-09d2-4f8c-8faf-dc0e19d551a0", - "name": "Spawner (213)", - "location": [4153, 601, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0d4101a9-dd3b-4e5e-b95b-f538addb8324", - "name": "Spawner (213)", - "location": [4576, 3356, 0], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0e34837e-aee8-4001-872a-373502c74cdb", - "name": "Spawner (213)", - "location": [1969, 1409, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0e6a3af0-b37b-44d0-a920-d1ea5053f2b9", - "name": "Spawner (213)", - "location": [2963, 3617, 3], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 7, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0eca02f5-d149-4460-aed5-98695fa760be", - "name": "Spawner (213)", - "location": [3360, 218, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0edde836-7b37-4de8-8fc1-6336dac33b51", - "name": "Spawner (213)", - "location": [1191, 288, 11], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f01ea1e-7189-495d-b62f-53698c79b009", - "name": "Spawner (213)", - "location": [728, 1242, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f42e18d-e212-4379-868b-48f40b9aab76", - "name": "Spawner (213)", - "location": [1004, 1369, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f713474-29c7-4dff-865f-a57be332f44e", - "name": "Spawner (213)", - "location": [2345, 676, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10667ef8-7144-444e-a63b-8bfe0ff5c55d", - "name": "Spawner (213)", - "location": [1067, 2117, 5], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10a9fb84-e7e7-47b1-ac1d-0bdba65b7d5f", - "name": "Spawner (213)", - "location": [1640, 1431, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1171adb5-c351-4aff-a15b-96e90a53b975", - "name": "Spawner (213)", - "location": [3963, 508, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11bc853d-5908-4b28-9578-8c556ee3d7e1", - "name": "Spawner (213)", - "location": [2354, 3503, 0], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12281c4e-e6de-4abf-8313-db1b6895eaee", - "name": "Spawner (213)", - "location": [1200, 633, 6], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12609af5-f153-4afa-96da-970e1be4afd0", - "name": "Spawner (213)", - "location": [1445, 1365, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "132293a7-fc22-467d-b65d-6c6fa8609304", - "name": "Spawner (213)", - "location": [2079, 1018, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "13359902-1ded-4453-9330-765325c87e2b", - "name": "Spawner (213)", - "location": [1525, 2053, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "136fc430-d130-446c-bfd0-a0daf0d69e50", - "name": "Spawner (213)", - "location": [634, 1504, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "13d177ea-c83f-4d80-82a0-bbdaea29decf", - "name": "Spawner (213)", - "location": [2077, 2305, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "13d7ad11-9375-4baf-8a4e-27993057790e", - "name": "Spawner (213)", - "location": [844, 1235, 0], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "14080e7e-45ac-4e42-aa16-5c423a4563df", - "name": "Spawner (213)", - "location": [1037, 1325, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1501e046-7baa-43b3-8b46-a9b978b0460f", - "name": "Spawner (213)", - "location": [2171, 1356, 0], - "map": "Felucca", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, - { "name": "Savage", "maxCount": 3, "probability": 100 }, - { "name": "SavageRider", "maxCount": 2, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "156f17d7-f242-4657-87fd-48b947ad6f65", - "name": "Spawner (213)", - "location": [741, 1838, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15b7f75c-58c0-4a76-9e29-97b123985326", - "name": "Spawner (213)", - "location": [1877, 1515, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1687b6e3-dbd1-4203-83e6-3b42459a20c5", - "name": "Spawner (213)", - "location": [1743, 589, 16], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "181514b8-e4b3-442a-920c-27b56e3df585", - "name": "Spawner (213)", - "location": [2503, 3593, 3], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1824a86e-4c8d-48fd-900d-e70e7127667a", - "name": "Spawner (213)", - "location": [4455, 1434, 10], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1878494f-e3bd-44a8-9a78-e3c2de43cf0a", - "name": "Spawner (213)", - "location": [240, 1443, 0], - "map": "Felucca", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1973fede-bee5-4d12-9620-4ac08754278c", - "name": "Spawner (213)", - "location": [4596, 3607, 30], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 50, - "entries": [ - { "name": "Daemon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "198ec5be-f20d-4f44-a5c2-36c153751cb6", - "name": "Spawner (213)", - "location": [805, 483, 0], - "map": "Felucca", - "count": 2, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1a02a3c7-3e58-44d8-98e3-6abb2f2b099c", - "name": "Spawner (213)", - "location": [516, 1585, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 }, - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Reaper", "maxCount": 5, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1adc37c9-0eea-45ec-adf2-6825f0af9b46", - "name": "Spawner (213)", - "location": [1534, 1941, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d296a28-acda-437c-82f8-b0dfb62be85d", - "name": "Spawner (213)", - "location": [4618, 3570, 30], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d32dbb7-e905-45c1-b8fe-a73bfb6d5806", - "name": "Spawner (213)", - "location": [4571, 3572, 31], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d35fcf9-851c-4b60-8c51-ccd62e38d1c2", - "name": "Spawner (213)", - "location": [1726, 1362, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d5b7b60-a289-434e-be7a-e3f538e1d9d9", - "name": "Spawner (213)", - "location": [1361, 1392, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1ecc2c95-94e1-4859-bdae-5b6e9f6d6094", - "name": "Spawner (213)", - "location": [1956, 48, 4], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, - { "name": "Gazer", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f59609c-ffa6-4a3c-ae8f-92caf32391a1", - "name": "Spawner (213)", - "location": [4512, 1380, 23], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 10, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Jwilson", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1ff6bf9e-dcdf-4967-a32c-8b542a45ce6e", - "name": "Spawner (213)", - "location": [4013, 239, 1], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "215f7ae1-42f8-46a6-a35b-bebd67373a40", - "name": "Spawner (213)", - "location": [909, 1175, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "216f74af-209f-4943-ac78-cb2703ba7f06", - "name": "Spawner (213)", - "location": [1266, 1982, 0], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21abb157-384a-4001-9ba6-743cd407fe3f", - "name": "Spawner (213)", - "location": [3459, 2655, 9], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Brigand", "maxCount": 3, "probability": 100 }, - { "name": "Harpy", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "Spectre", "maxCount": 3, "probability": 100 }, - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22a47de7-ed67-461f-830e-d7d395e638c4", - "name": "Spawner (213)", - "location": [1812, 3036, 5], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2319882c-d94a-4f58-97e7-6dca32e890e2", - "name": "Spawner (213)", - "location": [1244, 1374, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23347175-c9e6-4776-a97c-04c85507e131", - "name": "Spawner (213)", - "location": [1990, 976, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23af0fb5-5bba-41f3-8caa-1f8f5018f42f", - "name": "Spawner (213)", - "location": [1887, 591, 0], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "27b05c45-ec38-45db-b3ac-127bac6b8e99", - "name": "Spawner (213)", - "location": [1021, 1415, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "28724275-f05c-4b50-8e05-60aa69bb3e65", - "name": "Spawner (213)", - "location": [418, 1665, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 }, - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Reaper", "maxCount": 5, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "28d181e5-edb4-4d16-900d-b4a9b8a336f4", - "name": "Spawner (213)", - "location": [971, 1308, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "28dec03a-120b-40ca-a03e-482d3f2de528", - "name": "Spawner (213)", - "location": [1640, 1431, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2a64edbb-d1e3-4ec8-9cf3-7d8993ebbb7f", - "name": "Spawner (213)", - "location": [1416, 1992, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2b2e285f-e9ae-40a8-a1f8-95a8fe906e13", - "name": "Spawner (213)", - "location": [4552, 1447, 10], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2c079015-240e-4b1c-bf6f-9802277d8148", - "name": "Spawner (213)", - "location": [1849, 953, 7], - "map": "Felucca", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Ratman", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2c730966-efe7-4079-a56d-f836444b2b4d", - "name": "Spawner (213)", - "location": [4090, 325, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ca9d939-38bc-404a-b3f6-04727259fb8b", - "name": "Spawner (213)", - "location": [2203, 1379, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e0715c9-f838-4d37-be39-3c8a0f1e2580", - "name": "Spawner (213)", - "location": [1947, 912, -1], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2f215ba7-0215-4501-a61a-dfc7bf9d549d", - "name": "Spawner (213)", - "location": [805, 463, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 }, - { "name": "Reaper", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ff06e09-3cbe-48fe-99d3-4f5753b9dabc", - "name": "Spawner (213)", - "location": [2468, 144, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3177e9c8-ff1d-48b8-8280-3a66a202526b", - "name": "Spawner (213)", - "location": [617, 1326, 0], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "317f0495-b38f-4f35-90f3-05033507c676", - "name": "Spawner (213)", - "location": [4615, 1469, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32260d9a-6ff8-417d-88e9-b08e0c04fa2c", - "name": "Spawner (213)", - "location": [1055, 1442, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "324e278d-d03c-4a19-9f7c-90983f0f7372", - "name": "Spawner (213)", - "location": [3173, 771, 0], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 5, "probability": 100 }, - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33035953-5de9-4a8d-9f5e-05b56a5b7a87", - "name": "Spawner (213)", - "location": [1891, 226, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33879016-b02f-4b25-bc21-dcbb882c5fff", - "name": "Spawner (213)", - "location": [1875, 906, 7], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33c2dd22-28c7-4365-8d2a-61a1594edcc5", - "name": "Spawner (213)", - "location": [3619, 2814, 21], - "map": "Felucca", - "count": 17, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "HordeMinion", "maxCount": 17, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "34134639-abeb-476b-9cd6-c0ad81f3615f", - "name": "Spawner (213)", - "location": [1849, 2915, 30], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "343b53bd-40ed-499b-93cf-60b7c854e810", - "name": "Spawner (213)", - "location": [841, 344, 0], - "map": "Felucca", - "count": 2, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3467c2e3-9fc0-45ba-97b6-207bddb5beb9", - "name": "Spawner (213)", - "location": [1862, 1434, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "34d6cb84-92a2-4e06-a314-e54bddd11403", - "name": "Spawner (213)", - "location": [1866, 3040, 15], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "354c29c0-32e9-448f-b17c-ef47ef6f1c41", - "name": "Spawner (213)", - "location": [1967, 1296, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "362c3cc5-8548-43be-8fab-4d981cab5ed1", - "name": "Spawner (213)", - "location": [1877, 1515, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "381a50a4-7600-462c-b95e-9c79f96e610e", - "name": "Spawner (213)", - "location": [4613, 3272, 0], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "383c7654-67b0-4024-a455-f433ea40d522", - "name": "Spawner (213)", - "location": [423, 1595, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "386f438a-1ba3-4f0d-b7e8-b7b95789f518", - "name": "Spawner (213)", - "location": [1829, 874, -1], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "38809d23-8eee-4351-a9fd-337ee44ceb7c", - "name": "Spawner (213)", - "location": [1147, 3524, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "399f4337-1564-4f34-b22c-5d1ec5b8a7ba", - "name": "Spawner (213)", - "location": [1898, 1265, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "39c6fca8-585d-419a-ad33-fdeecff5278e", - "name": "Spawner (213)", - "location": [1619, 2725, 10], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a4bf08f-1f3d-438b-a995-b638b1cb09f2", - "name": "Spawner (213)", - "location": [2080, 400, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3b09d5b9-f1e1-4628-92bb-7592ee885f6c", - "name": "Spawner (213)", - "location": [2089, 3017, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3bdf6642-bec9-4d20-b880-37381726f5b9", - "name": "Spawner (213)", - "location": [1803, 2981, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3c8c991b-9b97-404c-8a16-b2501645d88a", - "name": "Spawner (213)", - "location": [1866, 938, -1], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3cdb18b5-9aa3-42bc-9978-136a1d1263aa", - "name": "Spawner (213)", - "location": [1944, 102, 4], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, - { "name": "Gazer", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3f9d6f0d-92a3-4b72-a6aa-d8bfba12e7f7", - "name": "Spawner (213)", - "location": [1140, 2087, 0], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ffe57e9-1807-40db-bbfc-3b9d7776d0f0", - "name": "Spawner (213)", - "location": [2146, 178, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "401fe491-1378-4d69-ae3e-4cbfbe471991", - "name": "Spawner (213)", - "location": [2369, 3430, 3], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "409e129c-f95e-4a05-aff8-e2d577aede77", - "name": "Spawner (213)", - "location": [1325, 1071, 0], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Troll", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4124fe40-7dde-4264-8393-5f92e87bdd80", - "name": "Spawner (213)", - "location": [3446, 2741, 49], - "map": "Felucca", - "count": 7, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Brigand", "maxCount": 7, "probability": 100 }, - { "name": "Harpy", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Spectre", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41bf401b-72da-46c0-b246-04c1fce86c37", - "name": "Spawner (213)", - "location": [2107, 358, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41dd143d-0df5-41eb-a23e-bc65a7255c49", - "name": "Spawner (213)", - "location": [1862, 1434, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41fe89de-bbed-4af2-ba54-2d33bb39a64d", - "name": "Spawner (213)", - "location": [1541, 1895, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "42663241-fc0a-4fcc-aaa8-8fd9ab4bb3a4", - "name": "Spawner (213)", - "location": [1435, 2261, 5], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "42a92b9b-a8ac-498c-b528-94ff6f10b775", - "name": "Spawner (213)", - "location": [634, 1462, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4337a9d3-98f2-4eec-9459-c10f70f60319", - "name": "Spawner (213)", - "location": [4455, 1358, 10], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "440f3392-8081-4185-b1d4-62ebb6e90b5b", - "name": "Spawner (213)", - "location": [837, 361, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 }, - { "name": "Reaper", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "45008529-dc3f-4fcc-8810-520775c4e411", - "name": "Spawner (213)", - "location": [1915, 3050, 15], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "456e985f-4c8d-4f71-b998-d6efc19957f6", - "name": "Spawner (213)", - "location": [4493, 1364, 23], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Horse", "maxCount": 1, "probability": 100 }, - { "name": "Rat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "460ad9dc-158f-4e2e-a988-b52dcfe37839", - "name": "Spawner (213)", - "location": [1060, 1375, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "46abf595-aaae-4e1c-85e6-8cb7b463409c", - "name": "Spawner (213)", - "location": [1162, 704, 6], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47035c00-635d-410e-9208-3d07ee948ff6", - "name": "Spawner (213)", - "location": [1203, 502, 30], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47242747-b6e8-49d1-82ff-e3ddede86281", - "name": "Spawner (213)", - "location": [4605, 1365, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47fe6452-2b6d-486b-850c-86cb477c5e9c", - "name": "Spawner (213)", - "location": [840, 1063, 0], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "48847927-65aa-4abd-8423-86546faa0c80", - "name": "Spawner (213)", - "location": [1755, 1474, 0], - "map": "Felucca", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49282da8-3f46-4b69-aab2-670dda1c7117", - "name": "Spawner (213)", - "location": [2966, 3591, 11], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 7, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "499d9dab-ec87-4739-8055-7ffd7a54cecf", - "name": "Spawner (213)", - "location": [848, 917, 0], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49a48c0e-c34c-464b-b5b2-5374b434aab5", - "name": "Spawner (213)", - "location": [1021, 1415, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4a7b60db-3c30-449e-b20a-f85336dd5446", - "name": "Spawner (213)", - "location": [2044, 2323, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4a88078e-0413-44ec-add5-83f6d71d84a2", - "name": "Spawner (213)", - "location": [1004, 1369, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4ae1f335-1c4e-4be3-b495-209990644141", - "name": "Spawner (213)", - "location": [4595, 3570, 30], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b35e54f-77d1-4fe8-80d0-217df211d4e2", - "name": "Spawner (213)", - "location": [1042, 1920, 0], - "map": "Felucca", - "count": 9, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Spectre", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4c9fe5d1-3732-466e-a113-3be9fbc93e4c", - "name": "Spawner (213)", - "location": [2659, 147, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d68ece9-2d7e-4792-b92d-71424147068b", - "name": "Spawner (213)", - "location": [2184, 1376, 0], - "map": "Felucca", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, - { "name": "Savage", "maxCount": 3, "probability": 100 }, - { "name": "SavageRider", "maxCount": 2, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4e653da6-2748-4c30-b242-e97125e5be12", - "name": "Spawner (213)", - "location": [3042, 3590, 5], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 7, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4f3369eb-d269-4cb8-b74a-96641125adb0", - "name": "Spawner (213)", - "location": [815, 1603, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Spectre", "maxCount": 5, "probability": 100 }, - { "name": "Zombie", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4f619a76-b1b8-43dd-83a3-e3303ec767c3", - "name": "Spawner (213)", - "location": [2058, 87, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4fbb2008-7fa3-4f3a-8694-2fab575d41df", - "name": "Spawner (213)", - "location": [2533, 240, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50f349ae-dfc6-4c38-b241-9055670b9a37", - "name": "Spawner (213)", - "location": [1361, 522, 19], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "513b093a-a623-4073-b89c-44c8417c4c90", - "name": "Spawner (213)", - "location": [4504, 1458, 8], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "516fd39d-d4d2-4916-8c90-49902582100b", - "name": "Spawner (213)", - "location": [1222, 1418, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "519391cf-1cdd-424c-83c2-d646ea48343e", - "name": "Spawner (213)", - "location": [516, 1585, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "528a7172-954d-48e9-a482-391304d6cd59", - "name": "Spawner (213)", - "location": [2034, 46, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5349e354-eb19-4207-a2e0-831fda4d1850", - "name": "Spawner (213)", - "location": [480, 1641, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 }, - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Reaper", "maxCount": 5, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "53d1a2d3-250d-4eb4-bc6f-bba8133b27c1", - "name": "Spawner (213)", - "location": [232, 1350, 0], - "map": "Felucca", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5438491f-692d-4434-9731-0bd0e7cf96c0", - "name": "Spawner (213)", - "location": [1094, 3482, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54caf961-eba0-450a-a510-b078d9011faa", - "name": "Spawner (213)", - "location": [2069, 1028, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "55af7b2a-3f27-4bf0-9d5e-bcedbdb3b76b", - "name": "Spawner (213)", - "location": [1089, 2033, 0], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "55ca4007-514b-4b20-89e5-408cccf0924a", - "name": "Spawner (213)", - "location": [1192, 958, 5], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5630cd94-517e-4ddf-856d-fe59b8d2fc7a", - "name": "Spawner (213)", - "location": [1184, 217, 27], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5685505d-66f4-42c3-96b4-f71b071b40af", - "name": "Spawner (213)", - "location": [189, 1344, 0], - "map": "Felucca", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "579f51aa-6bce-480c-98e0-b29d2c0fcdd1", - "name": "Spawner (213)", - "location": [955, 707, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 947, "y": 699, "z": -128 }, - "end": { "x": 963, "y": 715, "z": 15 } - }, - "walkingRange": 12, - "entries": [ - { "name": "Wraith", "maxCount": 4, "probability": 100 }, - { "name": "Shade", "maxCount": 4, "probability": 100 }, - { "name": "Spectre", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "57e9ff50-ef57-4bfe-bd82-399ec6db0def", - "name": "Spawner (213)", - "location": [1375, 1330, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "57f5c71c-e443-4f41-a4a0-dac167db544d", - "name": "Spawner (213)", - "location": [4519, 1358, 23], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 15, - "entries": [ - { "name": "Alligator", "maxCount": 1, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 }, - { "name": "Gorilla", "maxCount": 1, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, - { "name": "GiantToad", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5821b10d-c56f-4f82-8a8c-576bde50184d", - "name": "Spawner (213)", - "location": [1947, 2294, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a5ccea8-791c-4a24-a5b8-1107fbd32673", - "name": "Spawner (213)", - "location": [1300, 607, 15], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5b7f6dd7-59c1-4ef1-8b7f-1e4b87c1f873", - "name": "Spawner (213)", - "location": [2429, 690, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5bd6347d-424f-4e25-80ce-a08fc96edc05", - "name": "Spawner (213)", - "location": [672, 1825, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5bf4bb35-2ec4-4a39-92d3-68c249f5b09a", - "name": "Spawner (213)", - "location": [1759, 259, 21], - "map": "Felucca", - "count": 13, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, - { "name": "Gazer", "maxCount": 11, "probability": 100 }, - { "name": "Harpy", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5c244d89-f413-49e1-abf4-70086903d0bd", - "name": "Spawner (213)", - "location": [1652, 1400, 0], - "map": "Felucca", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5c94feb2-ffa2-45f5-8392-bef88c324f87", - "name": "Spawner (213)", - "location": [821, 1938, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5cfe198e-679e-4311-b464-825debc44f00", - "name": "Spawner (213)", - "location": [1491, 238, 22], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d15df44-cca8-413c-aead-64e7fa9e6c7b", - "name": "Spawner (213)", - "location": [1867, 2962, 7], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d2d0626-1d6c-4338-bb96-16281daaf313", - "name": "Spawner (213)", - "location": [541, 1645, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d55517f-6927-4afd-b28c-123e332166ac", - "name": "Spawner (213)", - "location": [1635, 3253, 0], - "map": "Felucca", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e287924-f6b6-4a1f-b718-d3a8ab42d3f3", - "name": "Spawner (213)", - "location": [2326, 656, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5ef1c873-6465-4e41-bc69-4b09c5a527b9", - "name": "Spawner (213)", - "location": [1804, 247, 0], - "map": "Felucca", - "count": 13, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, - { "name": "Gazer", "maxCount": 11, "probability": 100 }, - { "name": "Harpy", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60046b73-487d-41d2-aa57-a137e9b1dfe9", - "name": "Spawner (213)", - "location": [964, 365, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 10, "probability": 100 }, - { "name": "DireWolf", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, - { "name": "Gazer", "maxCount": 10, "probability": 100 }, - { "name": "Harpy", "maxCount": 10, "probability": 100 }, - { "name": "Lizardman", "maxCount": 10, "probability": 100 }, - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "602665fa-b49a-4a81-8d2a-763665a13bb4", - "name": "Spawner (213)", - "location": [1626, 266, 16], - "map": "Felucca", - "count": 13, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, - { "name": "Gazer", "maxCount": 11, "probability": 100 }, - { "name": "Harpy", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "602c87dc-2586-4281-805c-948364d6fcd7", - "name": "Spawner (213)", - "location": [1968, 2346, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "606d97e6-78a1-48c6-bece-8cfa117906cf", - "name": "Spawner (213)", - "location": [2621, 99, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60caf162-33d5-4a0d-a754-04c8327bf10c", - "name": "Spawner (213)", - "location": [1636, 532, 22], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "615a5cba-d2f6-44dc-95a6-4cfa0b679aa4", - "name": "Spawner (213)", - "location": [2000, 141, 1], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6375273a-4a07-4c39-9a5c-5af034151feb", - "name": "Spawner (213)", - "location": [1387, 3109, 0], - "map": "Felucca", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "64615b94-7440-4c4b-873a-4b530cfd29e5", - "name": "Spawner (213)", - "location": [1130, 830, 9], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "647ff862-b266-4923-bdeb-78d411d4f0fd", - "name": "Spawner (213)", - "location": [854, 1686, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "Brigand", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "653465ba-f89c-4ecf-ac58-20f45a4b1055", - "name": "Spawner (213)", - "location": [1305, 1326, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "65846901-1333-4a0a-ab1d-85e2424e0b46", - "name": "Spawner (213)", - "location": [987, 725, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 3, "probability": 100 }, - { "name": "Lich", "maxCount": 3, "probability": 100 }, - { "name": "LichLord", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 }, - { "name": "Zombie", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "65c5883b-2663-411b-9820-28499977c4d8", - "name": "Spawner (213)", - "location": [892, 1963, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6629f955-9f48-46ed-affe-6ef6bb823646", - "name": "Spawner (213)", - "location": [4480, 1439, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6779c53c-467b-424f-86a7-5007798c4b47", - "name": "Spawner (213)", - "location": [1970, 3052, 15], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "67ff897a-73a2-4138-a65e-266bc2553d42", - "name": "Spawner (213)", - "location": [659, 1318, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "68e27fa5-b64e-4298-bf75-1df29d3cf1e1", - "name": "Spawner (213)", - "location": [1912, 1302, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "68e9fc19-85e7-41d6-9791-d22fdad0e11b", - "name": "Spawner (213)", - "location": [1337, 293, 22], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "69d4b822-5483-487f-9578-8cb790fc9e49", - "name": "Spawner (213)", - "location": [541, 1645, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 }, - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Reaper", "maxCount": 5, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6a159a3d-7dc5-441f-917b-c58af12fed0d", - "name": "Spawner (213)", - "location": [736, 1938, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b364efa-4774-4c6a-afc7-a44d234328fb", - "name": "Spawner (213)", - "location": [1060, 1375, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b782447-4c8b-4dbf-aad3-2d537084e961", - "name": "Spawner (213)", - "location": [1723, 683, 6], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b8e488f-bcb3-4037-a201-fce494c31f3d", - "name": "Spawner (213)", - "location": [2520, 3983, 3], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bdf87d2-daea-4576-984d-5463bc82d5cd", - "name": "Spawner (213)", - "location": [1883, 827, -1], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6d5c1996-6320-4d37-a6e2-527c3eef0be6", - "name": "Spawner (213)", - "location": [2095, 2352, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6dc07357-369f-413b-9acc-acf5540ea8cb", - "name": "Spawner (213)", - "location": [2115, 48, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6edd0c08-b27b-4bc3-a3bb-603b6359d3ac", - "name": "Spawner (213)", - "location": [1427, 2525, 0], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f39acaf-9dac-46d6-aca4-54e66f4280ef", - "name": "Spawner (213)", - "location": [1166, 818, 5], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f56546f-246a-491b-8b19-a8c668ea73fb", - "name": "Spawner (213)", - "location": [1898, 1265, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6fad7f26-0faf-4aa1-a25f-c050cc1e0143", - "name": "Spawner (213)", - "location": [4607, 3480, 29], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "70cee935-0141-46ca-a61d-693c873af487", - "name": "Spawner (213)", - "location": [798, 1935, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "713534d6-6d42-4846-95e2-e43b5aaa340f", - "name": "Spawner (213)", - "location": [1939, 1310, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "714bf0dd-48b0-488e-ad59-432e8477b9d5", - "name": "Spawner (213)", - "location": [1475, 1958, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7211f788-6a3c-4d59-809d-dc4f5440e1f6", - "name": "Spawner (213)", - "location": [851, 495, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 }, - { "name": "Reaper", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "731e7d09-59ed-48a4-b1d7-f2827a2d05e5", - "name": "Spawner (213)", - "location": [1504, 2007, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "74a9ed57-25f8-4fe2-8286-2696225764f3", - "name": "Spawner (213)", - "location": [1232, 2093, 0], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75aa2658-e0f1-4003-93e6-28da55db063e", - "name": "Spawner (213)", - "location": [1651, 2413, 5], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75c2c949-73c9-4a97-8688-79c5cbcf4cd8", - "name": "Spawner (213)", - "location": [423, 1595, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 }, - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Reaper", "maxCount": 5, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "769cc51f-efed-48b2-ba94-013f544fcb5a", - "name": "Spawner (213)", - "location": [3411, 2592, 44], - "map": "Felucca", - "count": 6, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Brigand", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Spectre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76ef18b2-8642-45f5-99e0-e206048ed383", - "name": "Spawner (213)", - "location": [4511, 1394, 23], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "IceSerpent", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "FrostOoze", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "77e4e362-a87a-46eb-ac22-87b392a705f6", - "name": "Spawner (213)", - "location": [1826, 2250, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "79dcafe6-4ca2-4715-ab2c-80951c18fad7", - "name": "Spawner (213)", - "location": [1000, 330, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 10, "probability": 100 }, - { "name": "DireWolf", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, - { "name": "Gazer", "maxCount": 10, "probability": 100 }, - { "name": "Harpy", "maxCount": 10, "probability": 100 }, - { "name": "Lizardman", "maxCount": 10, "probability": 100 }, - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7a444146-d8d7-4ba6-af15-1a5f440826c9", - "name": "Spawner (213)", - "location": [4550, 1409, 8], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b61ae14-8163-4511-97f7-bf81e83b4efe", - "name": "Spawner (213)", - "location": [757, 1401, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lich", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7ba962b8-dc8a-44c3-9535-16f076cc14aa", - "name": "Spawner (213)", - "location": [3434, 316, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7bdd2249-7e2c-44b8-9ae3-0d4eb5f5f224", - "name": "Spawner (213)", - "location": [1980, 832, -1], - "map": "Felucca", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Ratman", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d1ffadb-f06d-4166-b4e1-549f742c05f3", - "name": "Spawner (213)", - "location": [1037, 1325, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d35912d-be04-44d1-97b2-f6a77882c755", - "name": "Spawner (213)", - "location": [1827, 3429, 10], - "map": "Felucca", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7e183556-fb83-4823-96e9-9dba1cc69dd9", - "name": "Spawner (213)", - "location": [1240, 1317, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7e9e81b9-071c-4b46-adf9-f3e39b7e73d1", - "name": "Spawner (213)", - "location": [2372, 210, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 }, - { "name": "Zombie", "maxCount": 9, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "818950cd-6dbc-4e5d-8836-7c32103b28fa", - "name": "Spawner (213)", - "location": [4206, 471, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81aec24a-c7b8-46a7-a104-518c2bdd479c", - "name": "Spawner (213)", - "location": [1083, 783, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81cab405-c429-4e92-b73a-27f944780ae4", - "name": "Spawner (213)", - "location": [1833, 1245, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81e26950-3b2a-4d34-b8ea-e6bc4431a202", - "name": "Spawner (213)", - "location": [141, 1367, 0], - "map": "Felucca", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "82a86397-6318-4f4d-aad4-7f846018a9e0", - "name": "Spawner (213)", - "location": [2189, 298, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "83a2d33b-b85c-48a2-88d6-bf2e2e3a90b8", - "name": "Spawner (213)", - "location": [1915, 928, -1], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84565ecf-6dc3-4c85-a6d8-9959325383af", - "name": "Spawner (213)", - "location": [3440, 405, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8458a597-4941-4335-8537-6f89cd177920", - "name": "Spawner (213)", - "location": [1097, 970, 2], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84b2cd30-182a-4251-b95d-3904e80ee9ee", - "name": "Spawner (213)", - "location": [1064, 2488, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84e221a0-eef4-4399-a87e-385b21ee014d", - "name": "Spawner (213)", - "location": [3929, 235, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "850d750e-203e-44c3-8437-b0784ecdd1e8", - "name": "Spawner (213)", - "location": [1059, 2461, 0], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "85637171-e2f4-4170-b0a1-cb685aea8962", - "name": "Spawner (213)", - "location": [1180, 737, 1], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "85b3d37c-21b5-484e-841c-a2344d3a911b", - "name": "Spawner (213)", - "location": [1832, 517, 0], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "860745e5-2133-46bb-bf8b-b8cd1273b204", - "name": "Spawner (213)", - "location": [4658, 3376, 0], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "865c8dd1-b8f3-4fbb-8d00-d4188cb4b630", - "name": "Spawner (213)", - "location": [3013, 3591, 15], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 7, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "87b90902-f43c-4892-963c-c8b5a3097b5f", - "name": "Spawner (213)", - "location": [2155, 1370, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "87bb1dc1-dc09-44cf-b2cc-1b72e55ac804", - "name": "Spawner (213)", - "location": [3602, 2815, 28], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "87e84f7b-43c8-41fc-9891-000423b0afb8", - "name": "Spawner (213)", - "location": [1595, 3045, 0], - "map": "Felucca", - "count": 30, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 60, - "walkingRange": 100, - "entries": [ - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, - { "name": "Gorilla", "maxCount": 5, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "88c9fa5d-b9ee-4060-8e14-24961c30cf76", - "name": "Spawner (213)", - "location": [823, 960, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "890ffbb5-4dab-44d2-b7b0-7d11528f7bb0", - "name": "Spawner (213)", - "location": [4729, 3485, 0], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8993ccc9-a37b-4023-8e61-5cb2ebd7e2da", - "name": "Spawner (213)", - "location": [2137, 271, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8a225256-b5b2-4e7a-8150-e4223df853a9", - "name": "Spawner (213)", - "location": [947, 418, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 10, "probability": 100 }, - { "name": "DireWolf", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, - { "name": "Gazer", "maxCount": 10, "probability": 100 }, - { "name": "Harpy", "maxCount": 10, "probability": 100 }, - { "name": "Lizardman", "maxCount": 10, "probability": 100 }, - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8a406878-badb-424f-b754-f4ca946ecd46", - "name": "Spawner (213)", - "location": [1243, 2437, 5], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8aa3fee7-2653-4bb2-828b-c1952fa27afe", - "name": "Spawner (213)", - "location": [1284, 433, 6], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8af8ecde-099a-4fbe-9047-af0b8d3f383f", - "name": "Spawner (213)", - "location": [1383, 1949, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8bff60da-cfb3-4fb7-9f6a-b1b406597762", - "name": "Spawner (213)", - "location": [2067, 989, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8c3a3d09-11df-451e-b796-b0ec4fc0849f", - "name": "Spawner (213)", - "location": [849, 1262, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8c77216f-786f-4f37-b97d-3aebb21fe1a1", - "name": "Spawner (213)", - "location": [1479, 2094, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8cd055a5-7d48-4bc2-9909-37a3617c35d7", - "name": "Spawner (213)", - "location": [1908, 214, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d481b37-cf17-44b3-964e-3c5a49eb3273", - "name": "Spawner (213)", - "location": [1891, 2331, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d842bcc-2ef7-4842-a73a-0bbd654b6056", - "name": "Spawner (213)", - "location": [1131, 3415, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8e1dfcc8-d717-4623-a37a-ded401bb0816", - "name": "Spawner (213)", - "location": [1812, 1314, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ed47473-6f84-449c-ab34-5d75e71d3335", - "name": "Spawner (213)", - "location": [2328, 67, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 }, - { "name": "Zombie", "maxCount": 9, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8efd16fe-fa70-4bc3-ab54-988dd78cda62", - "name": "Spawner (213)", - "location": [1954, 2903, 30], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9169bc6c-ef7f-48ed-94aa-18469b88e35e", - "name": "Spawner (213)", - "location": [1965, 160, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "919eb0cd-ae28-41e9-8f0a-c27da48a14c1", - "name": "Spawner (213)", - "location": [579, 1305, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9237e86f-8498-4384-aa4d-0cddc7f78012", - "name": "Spawner (213)", - "location": [1946, 186, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9267b97a-6678-4f62-a2f3-099e8de35e35", - "name": "Spawner (213)", - "location": [1145, 2231, 20], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Daemon", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "929d7fd7-0b40-4937-a62c-86b180c63f66", - "name": "Spawner (213)", - "location": [2379, 654, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93f4c0ad-2380-488f-87a4-24d765cc9082", - "name": "Spawner (213)", - "location": [4566, 1359, 8], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "948f6864-11e8-41da-a67a-c7b6dcc2e9a1", - "name": "Spawner (213)", - "location": [1682, 252, 6], - "map": "Felucca", - "count": 13, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, - { "name": "Gazer", "maxCount": 11, "probability": 100 }, - { "name": "Harpy", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94f97606-ded4-4f36-9529-008c94c705be", - "name": "Spawner (213)", - "location": [1340, 1268, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95c40856-e7b8-4d17-b7d4-9600c9f0670b", - "name": "Spawner (213)", - "location": [204, 1483, 0], - "map": "Felucca", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "96a38541-94a9-4607-adaa-1bd6347bf66a", - "name": "Spawner (213)", - "location": [974, 1186, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "96afa046-b31d-4bda-8839-026e8e3fd86f", - "name": "Spawner (213)", - "location": [1152, 3471, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "96b77dd9-7c8a-4a6b-b3a3-18fbbde09fdb", - "name": "Spawner (213)", - "location": [4595, 3574, 75], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 50, - "entries": [ - { "name": "Daemon", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b3a4772-0805-44ff-b7bd-2ec95092ded2", - "name": "Spawner (213)", - "location": [2355, 179, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 }, - { "name": "Zombie", "maxCount": 9, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c96876a-9456-4e48-b8e7-151973a5fe0f", - "name": "Spawner (213)", - "location": [1692, 1463, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9d026080-bc0b-4dff-a9aa-eb948024a256", - "name": "Spawner (213)", - "location": [1987, 822, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9df57404-f990-46d9-b66f-dfbbd3496caf", - "name": "Spawner (213)", - "location": [1986, 1463, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "9e690a14-3014-4267-8fa9-87cc82f26077", "name": "Spawner (213)", "location": [1214, 617, 13], @@ -5413,7 +1547,61 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "50f349ae-dfc6-4c38-b241-9055670b9a37", + "name": "Spawner (213)", + "location": [1361, 522, 19], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "067a213b-da9a-43e9-baac-1c2628a6e58e", + "name": "Spawner (213)", + "location": [1226, 449, 1], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "9e804689-212a-45f1-b593-eda7f9efdd48", "name": "Spawner (213)", "location": [1276, 431, 16], @@ -5440,28 +1628,115 @@ ] }, { - "$type": "Spawner", - "guid": "9f0ee982-f3db-44cb-93a5-b8d337332dde", + "type": "Spawner", + "guid": "004eb6c0-e616-488d-8331-baaca386890e", "name": "Spawner (213)", - "location": [1837, 2334, 0], + "location": [1231, 192, 6], "map": "Felucca", - "count": 6, + "count": 12, "minDelay": "00:20:00", "maxDelay": "00:20:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "d3d68f12-966b-42ab-aa8f-6b035d9fb485", + "name": "Spawner (213)", + "location": [1143, 180, 14], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0edde836-7b37-4de8-8fc1-6336dac33b51", + "name": "Spawner (213)", + "location": [1191, 288, 11], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da43570c-3e93-4a98-847a-1480345bd5b2", + "name": "Spawner (213)", + "location": [1295, 328, 22], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "a0e5df46-d8d1-4c20-b25a-34e315646b5c", "name": "Spawner (213)", "location": [1361, 275, 16], @@ -5488,28 +1763,4474 @@ ] }, { - "$type": "Spawner", - "guid": "a1a5cffa-a389-4f0d-866b-84416c3e8eac", + "type": "Spawner", + "guid": "5cfe198e-679e-4311-b464-825debc44f00", "name": "Spawner (213)", - "location": [2091, 3413, 0], + "location": [1491, 238, 22], "map": "Felucca", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", "team": 0, - "homeRange": 150, - "walkingRange": 150, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "84565ecf-6dc3-4c85-a6d8-9959325383af", + "name": "Spawner (213)", + "location": [3440, 405, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6c0e2b6-b91d-4c81-a410-dc9eb6c05647", + "name": "Spawner (213)", + "location": [3382, 375, 4], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cada760c-ea76-4c03-a520-f54419dbb958", + "name": "Spawner (213)", + "location": [3327, 344, 4], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7ba962b8-dc8a-44c3-9535-16f076cc14aa", + "name": "Spawner (213)", + "location": [3434, 316, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09c20c39-441d-4c33-8089-aab62d941c8a", + "name": "Spawner (213)", + "location": [3382, 280, 9], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aad6f620-4709-4f67-9a6f-305ad3aff10b", + "name": "Spawner (213)", + "location": [3322, 271, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0eca02f5-d149-4460-aed5-98695fa760be", + "name": "Spawner (213)", + "location": [3360, 218, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e8501154-9dab-4c4c-a39f-b9628b3bbb76", + "name": "Spawner (213)", + "location": [3390, 259, 4], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eaf8f756-84ca-4a4b-8192-b12cbc585c06", + "name": "Spawner (213)", + "location": [3446, 213, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8535646-4ceb-4c18-8381-815cacb69db3", + "name": "Spawner (213)", + "location": [3462, 136, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d55e8962-d269-430e-9b9e-3be35bc38f62", + "name": "Spawner (213)", + "location": [2589, 204, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08f49123-9ff1-456d-90ec-e5aa91908226", + "name": "Spawner (213)", + "location": [2549, 136, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c9fe5d1-3732-466e-a113-3be9fbc93e4c", + "name": "Spawner (213)", + "location": [2659, 147, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "606d97e6-78a1-48c6-bece-8cfa117906cf", + "name": "Spawner (213)", + "location": [2621, 99, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c662fb39-3f09-4671-82f3-7aff006a304f", + "name": "Spawner (213)", + "location": [2549, 86, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe3d76ed-5b62-4db7-b8bd-ab6778bdbcc1", + "name": "Spawner (213)", + "location": [2478, 100, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ff06e09-3cbe-48fe-99d3-4f5753b9dabc", + "name": "Spawner (213)", + "location": [2468, 144, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02ea296d-d8b7-4ecd-ba51-56469126e50b", + "name": "Spawner (213)", + "location": [2485, 191, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eb2156a6-745b-4c10-be4c-ebf1204d237f", + "name": "Spawner (213)", + "location": [2454, 248, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fbb2008-7fa3-4f3a-8694-2fab575d41df", + "name": "Spawner (213)", + "location": [2533, 240, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec92432d-3a21-4e09-8b18-c40643973bb3", + "name": "Spawner (213)", + "location": [2077, 434, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a4bf08f-1f3d-438b-a995-b638b1cb09f2", + "name": "Spawner (213)", + "location": [2080, 400, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41bf401b-72da-46c0-b246-04c1fce86c37", + "name": "Spawner (213)", + "location": [2107, 358, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c351d958-abbf-4a43-879d-486e488ed804", + "name": "Spawner (213)", + "location": [2138, 323, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "82a86397-6318-4f4d-aad4-7f846018a9e0", + "name": "Spawner (213)", + "location": [2189, 298, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8993ccc9-a37b-4023-8e61-5cb2ebd7e2da", + "name": "Spawner (213)", + "location": [2137, 271, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec21b7e4-dc3e-4852-a4b0-88bab20a85bc", + "name": "Spawner (213)", + "location": [2113, 221, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ffe57e9-1807-40db-bbfc-3b9d7776d0f0", + "name": "Spawner (213)", + "location": [2146, 178, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af6f5396-9061-4a3f-9a80-74e737f07633", + "name": "Spawner (213)", + "location": [2093, 147, 3], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "adc2a006-48b8-4997-bd27-a61a4a6f4135", + "name": "Spawner (213)", + "location": [2109, 109, 3], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6dc07357-369f-413b-9acc-acf5540ea8cb", + "name": "Spawner (213)", + "location": [2115, 48, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea181e6f-6d69-462b-95a0-7c92a7a15736", + "name": "Spawner (213)", + "location": [1996, 73, 8], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ecc2c95-94e1-4859-bdae-5b6e9f6d6094", + "name": "Spawner (213)", + "location": [1956, 48, 4], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3cdb18b5-9aa3-42bc-9978-136a1d1263aa", + "name": "Spawner (213)", + "location": [1944, 102, 4], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "528a7172-954d-48e9-a482-391304d6cd59", + "name": "Spawner (213)", + "location": [2034, 46, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cf7782fd-55ea-4b33-8cfd-6ace1c591fb1", + "name": "Spawner (213)", + "location": [2033, 74, 1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f619a76-b1b8-43dd-83a3-e3303ec767c3", + "name": "Spawner (213)", + "location": [2058, 87, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dddfb4b7-f386-46f5-a7da-7aa2b4ee0315", + "name": "Spawner (213)", + "location": [2034, 108, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "615a5cba-d2f6-44dc-95a6-4cfa0b679aa4", + "name": "Spawner (213)", + "location": [2000, 141, 1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9169bc6c-ef7f-48ed-94aa-18469b88e35e", + "name": "Spawner (213)", + "location": [1965, 160, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9237e86f-8498-4384-aa4d-0cddc7f78012", + "name": "Spawner (213)", + "location": [1946, 186, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dfea35bb-7743-4f04-8ae3-17f91eddcc80", + "name": "Spawner (213)", + "location": [1922, 173, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be9fc521-2647-453c-8311-0129a0d79172", + "name": "Spawner (213)", + "location": [1894, 197, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8cd055a5-7d48-4bc2-9909-37a3617c35d7", + "name": "Spawner (213)", + "location": [1908, 214, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33035953-5de9-4a8d-9f5e-05b56a5b7a87", + "name": "Spawner (213)", + "location": [1891, 226, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c53659a8-543b-485d-b470-e5ed6acdb52f", + "name": "Spawner (213)", + "location": [1869, 265, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "602665fa-b49a-4a81-8d2a-763665a13bb4", + "name": "Spawner (213)", + "location": [1626, 266, 16], + "map": "Felucca", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "948f6864-11e8-41da-a67a-c7b6dcc2e9a1", + "name": "Spawner (213)", + "location": [1682, 252, 6], + "map": "Felucca", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae693c99-8cb9-438a-87fe-86b5d033aca3", + "name": "Spawner (213)", + "location": [1723, 252, 16], + "map": "Felucca", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bf4bb35-2ec4-4a39-92d3-68c249f5b09a", + "name": "Spawner (213)", + "location": [1759, 259, 21], + "map": "Felucca", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ef1c873-6465-4e41-bc69-4b09c5a527b9", + "name": "Spawner (213)", + "location": [1804, 247, 0], + "map": "Felucca", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33879016-b02f-4b25-bc21-dcbb882c5fff", + "name": "Spawner (213)", + "location": [1875, 906, 7], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "386f438a-1ba3-4f0d-b7e8-b7b95789f518", + "name": "Spawner (213)", + "location": [1829, 874, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd268637-d96f-4ec9-b6cc-a70144dc3165", + "name": "Spawner (213)", + "location": [1852, 841, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0bfb8c5f-2695-41d7-8978-7e1155117e57", + "name": "Spawner (213)", + "location": [1930, 864, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bdf87d2-daea-4576-984d-5463bc82d5cd", + "name": "Spawner (213)", + "location": [1883, 827, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f09c9e65-1864-434c-bc3f-d767fa80e788", + "name": "Spawner (213)", + "location": [1949, 824, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9d026080-bc0b-4dff-a9aa-eb948024a256", + "name": "Spawner (213)", + "location": [1987, 822, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f41e03cd-3997-4d0d-bb42-49adf8ae92e9", + "name": "Spawner (213)", + "location": [2013, 839, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1779892-5c27-47db-aa94-a4d59bc1b1e9", + "name": "Spawner (213)", + "location": [1979, 885, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e0715c9-f838-4d37-be39-3c8a0f1e2580", + "name": "Spawner (213)", + "location": [1947, 912, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "83a2d33b-b85c-48a2-88d6-bf2e2e3a90b8", + "name": "Spawner (213)", + "location": [1915, 928, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c8c991b-9b97-404c-8a16-b2501645d88a", + "name": "Spawner (213)", + "location": [1866, 938, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad3ac281-793d-43a4-82ca-b7c04c4e3bbd", + "name": "Spawner (213)", + "location": [1838, 957, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd671a3d-e780-4f6f-993b-daa7dc80457b", + "name": "Spawner (213)", + "location": [1802, 1506, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c96876a-9456-4e48-b8e7-151973a5fe0f", + "name": "Spawner (213)", + "location": [1692, 1463, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc4f1b34-7c58-4161-9674-50b983dd4162", + "name": "Spawner (213)", + "location": [1833, 1245, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a26ea05c-1690-4f53-b10c-b44cc66607ed", + "name": "Spawner (213)", + "location": [1726, 1362, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28dec03a-120b-40ca-a03e-482d3f2de528", + "name": "Spawner (213)", + "location": [1640, 1431, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9802f03-9dbb-422c-91f0-6aba14af525e", + "name": "Spawner (213)", + "location": [1912, 1302, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f0797410-53f9-4af5-976e-332f8d4db4d8", + "name": "Spawner (213)", + "location": [1967, 1296, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f56546f-246a-491b-8b19-a8c668ea73fb", + "name": "Spawner (213)", + "location": [1898, 1265, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af167225-49e3-4b16-89e9-d9ec5c1a7dcf", + "name": "Spawner (213)", + "location": [1872, 1347, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9e1a43f-932f-4501-861d-ab721a87d572", + "name": "Spawner (213)", + "location": [1812, 1314, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5349db7-c725-494b-9350-8b108ef5a1a1", + "name": "Spawner (213)", + "location": [1797, 1393, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41dd143d-0df5-41eb-a23e-bc65a7255c49", + "name": "Spawner (213)", + "location": [1862, 1434, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15b7f75c-58c0-4a76-9e29-97b123985326", + "name": "Spawner (213)", + "location": [1877, 1515, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d5b7b60-a289-434e-be7a-e3f538e1d9d9", + "name": "Spawner (213)", + "location": [1361, 1392, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12609af5-f153-4afa-96da-970e1be4afd0", + "name": "Spawner (213)", + "location": [1445, 1365, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "57e9ff50-ef57-4bfe-bd82-399ec6db0def", + "name": "Spawner (213)", + "location": [1375, 1330, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0731f9aa-2216-4b25-97f1-bcfa2c54688d", + "name": "Spawner (213)", + "location": [1474, 1297, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc0604fa-8a01-4f09-bcce-86e207e4a514", + "name": "Spawner (213)", + "location": [1407, 1281, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e410df9e-2140-4972-aaed-8e1366658bb4", + "name": "Spawner (213)", + "location": [1225, 1477, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "516fd39d-d4d2-4916-8c90-49902582100b", + "name": "Spawner (213)", + "location": [1222, 1418, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2319882c-d94a-4f58-97e7-6dca32e890e2", + "name": "Spawner (213)", + "location": [1244, 1374, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d5e8e17c-a9b3-420d-b15c-c7ba5057c876", + "name": "Spawner (213)", + "location": [1180, 1354, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e183556-fb83-4823-96e9-9dba1cc69dd9", + "name": "Spawner (213)", + "location": [1240, 1317, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94f97606-ded4-4f36-9529-008c94c705be", + "name": "Spawner (213)", + "location": [1340, 1268, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "653465ba-f89c-4ecf-ac58-20f45a4b1055", + "name": "Spawner (213)", + "location": [1305, 1326, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8bb9b2d-6144-4bc2-82c9-0479a8d336af", + "name": "Spawner (213)", + "location": [1304, 1413, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bff6e844-976b-4433-9a12-653c267de42f", + "name": "Spawner (213)", + "location": [1300, 1481, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41fe89de-bbed-4af2-ba54-2d33bb39a64d", + "name": "Spawner (213)", + "location": [1541, 1895, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1adc37c9-0eea-45ec-adf2-6825f0af9b46", + "name": "Spawner (213)", + "location": [1534, 1941, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "714bf0dd-48b0-488e-ad59-432e8477b9d5", + "name": "Spawner (213)", + "location": [1475, 1958, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da41ea67-2124-4dbe-9f22-66e678652e59", + "name": "Spawner (213)", + "location": [1435, 1917, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8af8ecde-099a-4fbe-9047-af0b8d3f383f", + "name": "Spawner (213)", + "location": [1383, 1949, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a64edbb-d1e3-4ec8-9cf3-7d8993ebbb7f", + "name": "Spawner (213)", + "location": [1416, 1992, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "731e7d09-59ed-48a4-b1d7-f2827a2d05e5", + "name": "Spawner (213)", + "location": [1504, 2007, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a309b246-ca7e-4a68-b57e-5a50c9c7d0b1", + "name": "Spawner (213)", + "location": [1447, 2038, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c77216f-786f-4f37-b97d-3aebb21fe1a1", + "name": "Spawner (213)", + "location": [1479, 2094, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13359902-1ded-4453-9330-765325c87e2b", + "name": "Spawner (213)", + "location": [1525, 2053, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d5c1996-6320-4d37-a6e2-527c3eef0be6", + "name": "Spawner (213)", + "location": [2095, 2352, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13d177ea-c83f-4d80-82a0-bbdaea29decf", + "name": "Spawner (213)", + "location": [2077, 2305, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fabc9f7f-e030-43da-adf3-7c8065e0eeec", + "name": "Spawner (213)", + "location": [2021, 2313, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "602c87dc-2586-4281-805c-948364d6fcd7", + "name": "Spawner (213)", + "location": [1968, 2346, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5821b10d-c56f-4f82-8a8c-576bde50184d", + "name": "Spawner (213)", + "location": [1947, 2294, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "070b08c1-489a-4446-a535-8ba4828a641b", + "name": "Spawner (213)", + "location": [1915, 2385, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d481b37-cf17-44b3-964e-3c5a49eb3273", + "name": "Spawner (213)", + "location": [1891, 2331, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b99597c2-8732-450d-a851-27fbef18abfa", + "name": "Spawner (213)", + "location": [1890, 2270, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9ae149f-f3a4-41a1-87bf-19c32f436d18", + "name": "Spawner (213)", + "location": [1873, 2426, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f0ee982-f3db-44cb-93a5-b8d337332dde", + "name": "Spawner (213)", + "location": [1837, 2334, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "77e4e362-a87a-46eb-ac22-87b392a705f6", + "name": "Spawner (213)", + "location": [1826, 2250, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f950f3e2-4656-41d1-a46d-6941b77d6ebf", + "name": "Spawner (213)", + "location": [2109, 2989, 30], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3b09d5b9-f1e1-4628-92bb-7592ee885f6c", + "name": "Spawner (213)", + "location": [2089, 3017, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db1e8042-1bba-4828-8444-1bd0404753f5", + "name": "Spawner (213)", + "location": [2074, 3069, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe99c6b3-b023-46ff-8559-7c4b26728027", + "name": "Spawner (213)", + "location": [2033, 3041, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6779c53c-467b-424f-86a7-5007798c4b47", + "name": "Spawner (213)", + "location": [1970, 3052, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "45008529-dc3f-4fcc-8810-520775c4e411", + "name": "Spawner (213)", + "location": [1915, 3050, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "34d6cb84-92a2-4e06-a314-e54bddd11403", + "name": "Spawner (213)", + "location": [1866, 3040, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22a47de7-ed67-461f-830e-d7d395e638c4", + "name": "Spawner (213)", + "location": [1812, 3036, 5], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eff8645a-0a22-4a43-b54e-919edba83505", + "name": "Spawner (213)", + "location": [1841, 3000, 10], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3bdf6642-bec9-4d20-b880-37381726f5b9", + "name": "Spawner (213)", + "location": [1803, 2981, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d15df44-cca8-413c-aead-64e7fa9e6c7b", + "name": "Spawner (213)", + "location": [1867, 2962, 7], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "34134639-abeb-476b-9cd6-c0ad81f3615f", + "name": "Spawner (213)", + "location": [1849, 2915, 30], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8292593-a88b-4eb5-8cb6-b028fc4baf7f", + "name": "Spawner (213)", + "location": [1911, 2934, 10], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8efd16fe-fa70-4bc3-ab54-988dd78cda62", + "name": "Spawner (213)", + "location": [1954, 2903, 30], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7211f788-6a3c-4d59-809d-dc4f5440e1f6", + "name": "Spawner (213)", + "location": [851, 495, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f215ba7-0215-4501-a61a-dfc7bf9d549d", + "name": "Spawner (213)", + "location": [805, 463, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "440f3392-8081-4185-b1d4-62ebb6e90b5b", + "name": "Spawner (213)", + "location": [837, 361, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad6a2169-b01e-47ab-a323-fab15c6fabb6", + "name": "Spawner (213)", + "location": [1029, 1969, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e26d2e1a-806c-4929-9f9c-606e861532cc", + "name": "Spawner (213)", + "location": [1080, 1963, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b35e54f-77d1-4fe8-80d0-217df211d4e2", + "name": "Spawner (213)", + "location": [1042, 1920, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5c94feb2-ffa2-45f5-8392-bef88c324f87", + "name": "Spawner (213)", + "location": [821, 1938, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a159a3d-7dc5-441f-917b-c58af12fed0d", + "name": "Spawner (213)", + "location": [736, 1938, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "005e6436-1cae-4ece-a97a-a95fae8949ce", + "name": "Spawner (213)", + "location": [899, 1851, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef74d098-27d0-4c3d-8733-e2312c1b8de3", + "name": "Spawner (213)", + "location": [759, 1828, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bd6347d-424f-4e25-80ce-a08fc96edc05", + "name": "Spawner (213)", + "location": [672, 1825, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65c5883b-2663-411b-9820-28499977c4d8", + "name": "Spawner (213)", + "location": [892, 1963, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f3369eb-d269-4cb8-b74a-96641125adb0", + "name": "Spawner (213)", + "location": [815, 1603, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Spectre", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "006eec8a-3a67-4461-9688-1d7d0bfa0f1d", + "name": "Spawner (213)", + "location": [1055, 1442, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "27b05c45-ec38-45db-b3ac-127bac6b8e99", + "name": "Spawner (213)", + "location": [1021, 1415, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "460ad9dc-158f-4e2e-a988-b52dcfe37839", + "name": "Spawner (213)", + "location": [1060, 1375, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f42e18d-e212-4379-868b-48f40b9aab76", + "name": "Spawner (213)", + "location": [1004, 1369, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d1ffadb-f06d-4166-b4e1-549f742c05f3", + "name": "Spawner (213)", + "location": [1037, 1325, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe612581-49b6-43f5-bc62-a245821a3041", + "name": "Spawner (213)", + "location": [971, 1308, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79dcafe6-4ca2-4715-ab2c-80951c18fad7", + "name": "Spawner (213)", + "location": [1000, 330, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60046b73-487d-41d2-aa57-a137e9b1dfe9", + "name": "Spawner (213)", + "location": [964, 365, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a225256-b5b2-4e7a-8150-e4223df853a9", + "name": "Spawner (213)", + "location": [947, 418, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e073944b-3ed8-4649-9a2e-861f8ed0f1d3", + "name": "Spawner (213)", + "location": [956, 452, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75c2c949-73c9-4a97-8688-79c5cbcf4cd8", + "name": "Spawner (213)", + "location": [423, 1595, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28724275-f05c-4b50-8e05-60aa69bb3e65", + "name": "Spawner (213)", + "location": [418, 1665, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5349e354-eb19-4207-a2e0-831fda4d1850", + "name": "Spawner (213)", + "location": [480, 1641, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a02a3c7-3e58-44d8-98e3-6abb2f2b099c", + "name": "Spawner (213)", + "location": [516, 1585, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69d4b822-5483-487f-9578-8cb790fc9e49", + "name": "Spawner (213)", + "location": [541, 1645, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81e26950-3b2a-4d34-b8ea-e6bc4431a202", + "name": "Spawner (213)", + "location": [141, 1367, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5685505d-66f4-42c3-96b4-f71b071b40af", + "name": "Spawner (213)", + "location": [189, 1344, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "53d1a2d3-250d-4eb4-bc6f-bba8133b27c1", + "name": "Spawner (213)", + "location": [232, 1350, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2bf21be-ee5d-433d-bb96-ba564a604f03", + "name": "Spawner (213)", + "location": [278, 1355, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cefea663-b05f-4d86-8d4a-b09f1358f866", + "name": "Spawner (213)", + "location": [229, 1419, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5cc15e9-9e9b-4787-8003-004bac948bcc", + "name": "Spawner (213)", + "location": [182, 1428, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fbfa536c-3e04-4609-953f-e77bb2a48bb4", + "name": "Spawner (213)", + "location": [157, 1487, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95c40856-e7b8-4d17-b7d4-9600c9f0670b", + "name": "Spawner (213)", + "location": [204, 1483, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1878494f-e3bd-44a8-9a78-e3c2de43cf0a", + "name": "Spawner (213)", + "location": [240, 1443, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1e53b99-22b0-4072-a3f5-d82f9ac90cf1", + "name": "Spawner (213)", + "location": [269, 1474, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "faa69ada-cbbc-4c81-849d-ad4f66bb02ce", + "name": "Spawner (213)", + "location": [4057, 300, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ff6bf9e-dcdf-4967-a32c-8b542a45ce6e", + "name": "Spawner (213)", + "location": [4013, 239, 1], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84e221a0-eef4-4399-a87e-385b21ee014d", + "name": "Spawner (213)", + "location": [3929, 235, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca397470-069c-4a2e-8669-259cfa3a8b07", + "name": "Spawner (213)", + "location": [3920, 287, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c730966-efe7-4079-a56d-f836444b2b4d", + "name": "Spawner (213)", + "location": [4090, 325, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3b59c9a-cc31-461a-96da-58a9916f1b6f", + "name": "Spawner (213)", + "location": [4162, 395, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "818950cd-6dbc-4e5d-8836-7c32103b28fa", + "name": "Spawner (213)", + "location": [4206, 471, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c7b6e2b9-38c4-4b1b-b8b0-72f87b2f66a9", + "name": "Spawner (213)", + "location": [3928, 393, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1171adb5-c351-4aff-a15b-96e90a53b975", + "name": "Spawner (213)", + "location": [3963, 508, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "001a5320-820c-4300-96f9-676e428b55be", + "name": "Spawner (213)", + "location": [4066, 569, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "PolarBear", "maxCount": 8, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 8, "probability": 100 }, + { "name": "Walrus", "maxCount": 8, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0cd04146-09d2-4f8c-8faf-dc0e19d551a0", + "name": "Spawner (213)", + "location": [4153, 601, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8b50362-2413-4aff-9efe-90c40aefd329", + "name": "Spawner (213)", + "location": [4214, 655, 1], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a4883925-84fb-4f21-9f3e-9a31ef9821cd", + "name": "Spawner (213)", + "location": [4264, 725, 1], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e6a3af0-b37b-44d0-a920-d1ea5053f2b9", + "name": "Spawner (213)", + "location": [2963, 3617, 3], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49282da8-3f46-4b69-aab2-670dda1c7117", + "name": "Spawner (213)", + "location": [2966, 3591, 11], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "865c8dd1-b8f3-4fbb-8d00-d4188cb4b630", + "name": "Spawner (213)", + "location": [3013, 3591, 15], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aefc9ed6-d9e5-4094-97e8-cfcd27d232ee", + "name": "Spawner (213)", + "location": [3024, 3566, 15], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4e653da6-2748-4c30-b242-e97125e5be12", + "name": "Spawner (213)", + "location": [3042, 3590, 5], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c14d9101-3d93-4198-98c9-1ba34b4b8a5b", + "name": "Spawner (213)", + "location": [2462, 725, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b894e46c-2304-482f-a36e-b80e8ff7c26f", + "name": "Spawner (213)", + "location": [2428, 722, 8], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a43206c7-8096-4742-8975-2fe1e93a9c7a", + "name": "Spawner (213)", + "location": [2375, 725, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f713474-29c7-4dff-865f-a57be332f44e", + "name": "Spawner (213)", + "location": [2345, 676, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b7f6dd7-59c1-4ef1-8b7f-1e4b87c1f873", + "name": "Spawner (213)", + "location": [2429, 690, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "929d7fd7-0b40-4937-a62c-86b180c63f66", + "name": "Spawner (213)", + "location": [2379, 654, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e287924-f6b6-4a1f-b718-d3a8ab42d3f3", + "name": "Spawner (213)", + "location": [2326, 656, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a37f96c3-c51a-47b2-b472-2d4a495ed755", + "name": "Spawner (213)", + "location": [2319, 591, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "67ff897a-73a2-4138-a65e-266bc2553d42", + "name": "Spawner (213)", + "location": [659, 1318, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "919eb0cd-ae28-41e9-8f0a-c27da48a14c1", + "name": "Spawner (213)", + "location": [579, 1305, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f01ea1e-7189-495d-b62f-53698c79b009", + "name": "Spawner (213)", + "location": [728, 1242, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b80b4982-0838-4dc8-b433-d8158860b3f0", + "name": "Spawner (213)", + "location": [793, 1088, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c3a3d09-11df-451e-b796-b0ec4fc0849f", + "name": "Spawner (213)", + "location": [849, 1262, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "215f7ae1-42f8-46a6-a35b-bebd67373a40", + "name": "Spawner (213)", + "location": [909, 1175, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96a38541-94a9-4607-adaa-1bd6347bf66a", + "name": "Spawner (213)", + "location": [974, 1186, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9873f87-4f01-4e8d-920b-2834ee78aa98", + "name": "Spawner (213)", + "location": [1059, 1166, 5], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88c9fa5d-b9ee-4060-8e14-24961c30cf76", + "name": "Spawner (213)", + "location": [823, 960, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bec45065-1a08-4933-96e5-e014ff2cad8e", + "name": "Spawner (213)", + "location": [960, 910, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5ffd85a-c19d-49ed-812d-a3aa1f014a1a", + "name": "Spawner (213)", + "location": [1042, 1024, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae2034e9-7884-4677-9137-68707037298d", + "name": "Spawner (213)", + "location": [1147, 1048, 5], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8458a597-4941-4335-8537-6f89cd177920", + "name": "Spawner (213)", + "location": [1097, 970, 2], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55ca4007-514b-4b20-89e5-408cccf0924a", + "name": "Spawner (213)", + "location": [1192, 958, 5], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f39acaf-9dac-46d6-aca4-54e66f4280ef", + "name": "Spawner (213)", + "location": [1166, 818, 5], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81aec24a-c7b8-46a7-a104-518c2bdd479c", + "name": "Spawner (213)", + "location": [1083, 783, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a431c89a-9532-4cee-9ad2-843618cba4fd", + "name": "Spawner (213)", + "location": [1268, 774, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85637171-e2f4-4170-b0a1-cb685aea8962", + "name": "Spawner (213)", + "location": [1180, 737, 1], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "a1e173c7-f153-4d33-a8d8-056c97f97179", "name": "Spawner (213)", "location": [1122, 718, 1], @@ -5536,75 +6257,923 @@ ] }, { - "$type": "Spawner", - "guid": "a1e53b99-22b0-4072-a3f5-d82f9ac90cf1", + "type": "Spawner", + "guid": "383c7654-67b0-4024-a455-f433ea40d522", "name": "Spawner (213)", - "location": [269, 1474, 0], + "location": [423, 1595, 0], "map": "Felucca", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "a26ea05c-1690-4f53-b10c-b44cc66607ed", + "type": "Spawner", + "guid": "ae89bef0-87ec-40c4-85cf-29f8e2a8aa9f", "name": "Spawner (213)", - "location": [1726, 1362, 0], + "location": [418, 1665, 0], "map": "Felucca", "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9013421-1e07-4736-b861-4ae0e0eb5f1f", + "name": "Spawner (213)", + "location": [480, 1641, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "519391cf-1cdd-424c-83c2-d646ea48343e", + "name": "Spawner (213)", + "location": [516, 1585, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d2d0626-1d6c-4338-bb96-16281daaf313", + "name": "Spawner (213)", + "location": [541, 1645, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "513b093a-a623-4073-b89c-44c8417c4c90", + "name": "Spawner (213)", + "location": [4504, 1458, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf84bf7c-5e4e-4df0-841c-951a130df943", + "name": "Spawner (213)", + "location": [4494, 1426, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1824a86e-4c8d-48fd-900d-e70e7127667a", + "name": "Spawner (213)", + "location": [4455, 1434, 10], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c037ed10-c9d1-48d9-9b78-238fae407643", + "name": "Spawner (213)", + "location": [4456, 1405, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4337a9d3-98f2-4eec-9459-c10f70f60319", + "name": "Spawner (213)", + "location": [4455, 1358, 10], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7a444146-d8d7-4ba6-af15-1a5f440826c9", + "name": "Spawner (213)", + "location": [4550, 1409, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2b2e285f-e9ae-40a8-a1f8-95a8fe906e13", + "name": "Spawner (213)", + "location": [4552, 1447, 10], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "317f0495-b38f-4f35-90f3-05033507c676", + "name": "Spawner (213)", + "location": [4615, 1469, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8cec028-8686-4f1e-ae36-007c7074cf39", + "name": "Spawner (213)", + "location": [4612, 1418, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47242747-b6e8-49d1-82ff-e3ddede86281", + "name": "Spawner (213)", + "location": [4605, 1365, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93f4c0ad-2380-488f-87a4-24d765cc9082", + "name": "Spawner (213)", + "location": [4566, 1359, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e9e81b9-071c-4b46-adf9-f3e39b7e73d1", + "name": "Spawner (213)", + "location": [2372, 210, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b3a4772-0805-44ff-b7bd-2ec95092ded2", + "name": "Spawner (213)", + "location": [2355, 179, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec24e1a4-87be-4d55-8565-38292ab737b6", + "name": "Spawner (213)", + "location": [2351, 148, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d76ec9df-9494-4cb4-af37-e257ae4ffdf4", + "name": "Spawner (213)", + "location": [2345, 112, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ed47473-6f84-449c-ab34-5d75e71d3335", + "name": "Spawner (213)", + "location": [2328, 67, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "094d699e-d6ad-4322-b6eb-4a6883da6fe2", + "name": "Spawner (213)", + "location": [2177, 3034, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, { "name": "Ettin", "maxCount": 7, "probability": 100 }, { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, { "name": "Zombie", "maxCount": 7, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "a309b246-ca7e-4a68-b57e-5a50c9c7d0b1", + "type": "Spawner", + "guid": "e1955cb3-e55a-4c69-a173-60d937009596", "name": "Spawner (213)", - "location": [1447, 2038, 0], + "location": [2143, 3068, 20], "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "e9e020c2-1a4f-4a3f-9f91-4811c7f0ba13", + "name": "Spawner (213)", + "location": [1983, 1517, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9df57404-f990-46d9-b66f-dfbbd3496caf", + "name": "Spawner (213)", + "location": [1986, 1463, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e34837e-aee8-4001-872a-373502c74cdb", + "name": "Spawner (213)", + "location": [1969, 1409, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68e27fa5-b64e-4298-bf75-1df29d3cf1e1", + "name": "Spawner (213)", + "location": [1912, 1302, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "354c29c0-32e9-448f-b17c-ef47ef6f1c41", + "name": "Spawner (213)", + "location": [1967, 1296, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "399f4337-1564-4f34-b22c-5d1ec5b8a7ba", + "name": "Spawner (213)", + "location": [1898, 1265, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b745f7ae-402e-4da9-b424-31712b0c8b4c", + "name": "Spawner (213)", + "location": [1872, 1347, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8e1dfcc8-d717-4623-a37a-ded401bb0816", + "name": "Spawner (213)", + "location": [1812, 1314, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2a94db5-73b3-4012-be1e-ac122f9ebed2", + "name": "Spawner (213)", + "location": [1797, 1393, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3467c2e3-9fc0-45ba-97b6-207bddb5beb9", + "name": "Spawner (213)", + "location": [1862, 1434, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "362c3cc5-8548-43be-8fab-4d981cab5ed1", + "name": "Spawner (213)", + "location": [1877, 1515, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cdf5d19a-75c9-4b79-bcd1-4dcee392a090", + "name": "Spawner (213)", + "location": [1802, 1506, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9276ac3-f4a9-4614-bcd5-ff08d76b1a27", + "name": "Spawner (213)", + "location": [1692, 1463, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81cab405-c429-4e92-b73a-27f944780ae4", + "name": "Spawner (213)", + "location": [1833, 1245, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d35fcf9-851c-4b60-8c51-ccd62e38d1c2", + "name": "Spawner (213)", + "location": [1726, 1362, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10a9fb84-e7e7-47b1-ac1d-0bdba65b7d5f", + "name": "Spawner (213)", + "location": [1640, 1431, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32260d9a-6ff8-417d-88e9-b08e0c04fa2c", + "name": "Spawner (213)", + "location": [1055, 1442, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49a48c0e-c34c-464b-b5b2-5374b434aab5", + "name": "Spawner (213)", + "location": [1021, 1415, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b364efa-4774-4c6a-afc7-a44d234328fb", + "name": "Spawner (213)", + "location": [1060, 1375, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a88078e-0413-44ec-add5-83f6d71d84a2", + "name": "Spawner (213)", + "location": [1004, 1369, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "14080e7e-45ac-4e42-aa16-5c423a4563df", + "name": "Spawner (213)", + "location": [1037, 1325, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28d181e5-edb4-4d16-900d-b4a9b8a336f4", + "name": "Spawner (213)", + "location": [971, 1308, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1501e046-7baa-43b3-8b46-a9b978b0460f", + "name": "Spawner (213)", + "location": [2171, 1356, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, + { "name": "Savage", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d68ece9-2d7e-4792-b92d-71424147068b", + "name": "Spawner (213)", + "location": [2184, 1376, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, + { "name": "Savage", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "87b90902-f43c-4892-963c-c8b5a3097b5f", + "name": "Spawner (213)", + "location": [2155, 1370, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ca9d939-38bc-404a-b3f6-04727259fb8b", + "name": "Spawner (213)", + "location": [2203, 1379, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "a363c624-4c20-44dd-abb1-99a6f779c68b", "name": "Spawner (213)", "location": [2038, 1023, 0], @@ -5634,122 +7203,10 @@ ] }, { - "$type": "Spawner", - "guid": "a37f96c3-c51a-47b2-b472-2d4a495ed755", + "type": "Spawner", + "guid": "132293a7-fc22-467d-b65d-6c6fa8609304", "name": "Spawner (213)", - "location": [2319, 591, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a431c89a-9532-4cee-9ad2-843618cba4fd", - "name": "Spawner (213)", - "location": [1268, 774, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a43206c7-8096-4742-8975-2fe1e93a9c7a", - "name": "Spawner (213)", - "location": [2375, 725, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a4883925-84fb-4f21-9f3e-9a31ef9821cd", - "name": "Spawner (213)", - "location": [4264, 725, 1], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a608396f-9595-4436-b5a9-75a2947a948d", - "name": "Spawner (213)", - "location": [633, 1484, 0], - "map": "Felucca", - "count": 14, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a610c3f7-9200-4609-bbec-1f8e9fa3d46b", - "name": "Spawner (213)", - "location": [2023, 1052, 0], + "location": [2079, 1018, 0], "map": "Felucca", "count": 7, "minDelay": "00:05:00", @@ -5776,214 +7233,10 @@ ] }, { - "$type": "Spawner", - "guid": "a9bd52d6-d0bb-4999-8a8b-e5168463c718", + "type": "Spawner", + "guid": "8bff60da-cfb3-4fb7-9f6a-b1b406597762", "name": "Spawner (213)", - "location": [2548, 1173, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a9fffefd-9314-4e62-b029-0b55d7a51418", - "name": "Spawner (213)", - "location": [3619, 2814, 21], - "map": "Felucca", - "count": 17, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 17, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa67c8fa-91bc-4db4-9db9-ce9ba0440401", - "name": "Spawner (213)", - "location": [3172, 700, 8], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 5, "probability": 100 }, - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aad6f620-4709-4f67-9a6f-305ad3aff10b", - "name": "Spawner (213)", - "location": [3322, 271, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ac2a868d-5ee3-40a2-b50a-ca2b98717487", - "name": "Spawner (213)", - "location": [4511, 1394, 23], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad3ac281-793d-43a4-82ca-b7c04c4e3bbd", - "name": "Spawner (213)", - "location": [1838, 957, -1], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad6a2169-b01e-47ab-a323-fab15c6fabb6", - "name": "Spawner (213)", - "location": [1029, 1969, 0], - "map": "Felucca", - "count": 9, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Spectre", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "adc2a006-48b8-4997-bd27-a61a4a6f4135", - "name": "Spawner (213)", - "location": [2109, 109, 3], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae2034e9-7884-4677-9137-68707037298d", - "name": "Spawner (213)", - "location": [1147, 1048, 5], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae693c99-8cb9-438a-87fe-86b5d033aca3", - "name": "Spawner (213)", - "location": [1723, 252, 16], - "map": "Felucca", - "count": 13, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, - { "name": "Gazer", "maxCount": 11, "probability": 100 }, - { "name": "Harpy", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae89bef0-87ec-40c4-85cf-29f8e2a8aa9f", - "name": "Spawner (213)", - "location": [418, 1665, 0], + "location": [2067, 989, 0], "map": "Felucca", "count": 7, "minDelay": "00:05:00", @@ -5992,440 +7245,25 @@ "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "AirElemental", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "aec49b53-770f-44dd-899a-3447bcf40650", - "name": "Spawner (213)", - "location": [868, 488, 0], - "map": "Felucca", - "count": 2, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aefc9ed6-d9e5-4094-97e8-cfcd27d232ee", - "name": "Spawner (213)", - "location": [3024, 3566, 15], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 7, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "af167225-49e3-4b16-89e9-d9ec5c1a7dcf", - "name": "Spawner (213)", - "location": [1872, 1347, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "af6f5396-9061-4a3f-9a80-74e737f07633", - "name": "Spawner (213)", - "location": [2093, 147, 3], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3e704fd-0a49-4396-a196-b56afeea5ffc", - "name": "Spawner (213)", - "location": [1871, 1076, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b5988167-2735-4863-a380-20a2ab27f70c", - "name": "Spawner (213)", - "location": [2438, 3914, 0], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b5ffd85a-c19d-49ed-812d-a3aa1f014a1a", - "name": "Spawner (213)", - "location": [1042, 1024, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b745f7ae-402e-4da9-b424-31712b0c8b4c", - "name": "Spawner (213)", - "location": [1872, 1347, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b80b4982-0838-4dc8-b433-d8158860b3f0", - "name": "Spawner (213)", - "location": [793, 1088, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b8292593-a88b-4eb5-8cb6-b028fc4baf7f", - "name": "Spawner (213)", - "location": [1911, 2934, 10], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b894e46c-2304-482f-a36e-b80e8ff7c26f", - "name": "Spawner (213)", - "location": [2428, 722, 8], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b8b50362-2413-4aff-9efe-90c40aefd329", - "name": "Spawner (213)", - "location": [4214, 655, 1], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9436808-f34f-438e-8b11-31f6470faf91", - "name": "Spawner (213)", - "location": [1810, 647, 0], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b99597c2-8732-450d-a851-27fbef18abfa", - "name": "Spawner (213)", - "location": [1890, 2270, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bb22f21a-37bb-4e7a-b2a8-06db80a58e3a", - "name": "Spawner (213)", - "location": [1016, 821, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 10, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bb340a5d-bc6e-4bf1-b384-5e3c4b1edf2b", - "name": "Spawner (213)", - "location": [4498, 1394, 23], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "DireWolf", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, - { "name": "Rabbit", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "Panther", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Eagle", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bbbaf085-fb59-4fa5-a525-860584b9f84d", - "name": "Spawner (213)", - "location": [962, 692, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 5, - "entries": [ - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bc0604fa-8a01-4f09-bcce-86e207e4a514", - "name": "Spawner (213)", - "location": [1407, 1281, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bc9e19b0-3e29-4bab-b86f-980379e3a230", - "name": "Spawner (213)", - "location": [1799, 1394, 0], - "map": "Felucca", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd268637-d96f-4ec9-b6cc-a70144dc3165", - "name": "Spawner (213)", - "location": [1852, 841, -1], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd52d8c8-0b49-4626-94c2-55a0aba0d970", - "name": "Spawner (213)", - "location": [879, 1956, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "bdb6c75c-66db-4c72-aece-0d8bfe4ae4c1", "name": "Spawner (213)", "location": [2028, 987, 0], @@ -6455,1340 +7293,7 @@ ] }, { - "$type": "Spawner", - "guid": "bdc7678b-7586-468a-8fd2-185a10a2190d", - "name": "Spawner (213)", - "location": [1017, 826, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "be52369b-69c9-435f-b280-bc04927624aa", - "name": "Spawner (213)", - "location": [608, 1702, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Wisp", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "be9fc521-2647-453c-8311-0129a0d79172", - "name": "Spawner (213)", - "location": [1894, 197, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bec45065-1a08-4933-96e5-e014ff2cad8e", - "name": "Spawner (213)", - "location": [960, 910, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf84bf7c-5e4e-4df0-841c-951a130df943", - "name": "Spawner (213)", - "location": [4494, 1426, 8], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bff6e844-976b-4433-9a12-653c267de42f", - "name": "Spawner (213)", - "location": [1300, 1481, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c037ed10-c9d1-48d9-9b78-238fae407643", - "name": "Spawner (213)", - "location": [4456, 1405, 8], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c067f57d-067c-4cfb-bf77-8d8c6cce1ae6", - "name": "Spawner (213)", - "location": [1216, 2259, 5], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c119e387-73ae-4d7f-8104-a4eb9618a34b", - "name": "Spawner (213)", - "location": [1787, 1280, 0], - "map": "Felucca", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c14d9101-3d93-4198-98c9-1ba34b4b8a5b", - "name": "Spawner (213)", - "location": [2462, 725, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c158877f-aa51-4f38-9e0f-0b9792b6603c", - "name": "Spawner (213)", - "location": [4530, 1387, 23], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "GiantRat", "maxCount": 1, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Jwilson", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c25e54a0-e617-4cae-b5d0-0aeffa888d1a", - "name": "Spawner (213)", - "location": [1831, 449, 0], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c29f73f4-b7d1-4d83-ba23-d995a5c99085", - "name": "Spawner (213)", - "location": [1110, 2672, 0], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c351d958-abbf-4a43-879d-486e488ed804", - "name": "Spawner (213)", - "location": [2138, 323, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c51a903d-62e2-4963-81f0-c6a4a2efb2d0", - "name": "Spawner (213)", - "location": [666, 1825, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c5349db7-c725-494b-9350-8b108ef5a1a1", - "name": "Spawner (213)", - "location": [1797, 1393, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c53659a8-543b-485d-b470-e5ed6acdb52f", - "name": "Spawner (213)", - "location": [1869, 265, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c662fb39-3f09-4671-82f3-7aff006a304f", - "name": "Spawner (213)", - "location": [2549, 86, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c7b6e2b9-38c4-4b1b-b8b0-72f87b2f66a9", - "name": "Spawner (213)", - "location": [3928, 393, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c8211671-8686-48e8-ae56-66a5d815ecda", - "name": "Spawner (213)", - "location": [3379, 2693, 35], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Brigand", "maxCount": 3, "probability": 100 }, - { "name": "Harpy", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "Spectre", "maxCount": 3, "probability": 100 }, - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c8bb9b2d-6144-4bc2-82c9-0479a8d336af", - "name": "Spawner (213)", - "location": [1304, 1413, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c8cec028-8686-4f1e-ae36-007c7074cf39", - "name": "Spawner (213)", - "location": [4612, 1418, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c9873f87-4f01-4e8d-920b-2834ee78aa98", - "name": "Spawner (213)", - "location": [1059, 1166, 5], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c9ae149f-f3a4-41a1-87bf-19c32f436d18", - "name": "Spawner (213)", - "location": [1873, 2426, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ca397470-069c-4a2e-8669-259cfa3a8b07", - "name": "Spawner (213)", - "location": [3920, 287, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cac38c89-0f20-4066-b946-6cd4876723ab", - "name": "Spawner (213)", - "location": [2048, 1066, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cada760c-ea76-4c03-a520-f54419dbb958", - "name": "Spawner (213)", - "location": [3327, 344, 4], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cb8b69ec-7762-4c73-82fa-cd4c6ab12311", - "name": "Spawner (213)", - "location": [1631, 650, 16], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cbf48240-08c5-4939-9f2b-ec6e972b25a9", - "name": "Spawner (213)", - "location": [3485, 2595, 12], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Brigand", "maxCount": 3, "probability": 100 }, - { "name": "Harpy", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "Spectre", "maxCount": 3, "probability": 100 }, - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cc4f1b34-7c58-4161-9674-50b983dd4162", - "name": "Spawner (213)", - "location": [1833, 1245, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd655b49-23cd-410b-b7a4-8c959b5bd7d5", - "name": "Spawner (213)", - "location": [1013, 980, 0], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cdf5d19a-75c9-4b79-bcd1-4dcee392a090", - "name": "Spawner (213)", - "location": [1802, 1506, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ce5519e9-137c-4db7-b7a6-4dadcbdaa9ad", - "name": "Spawner (213)", - "location": [1530, 999, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cefea663-b05f-4d86-8d4a-b09f1358f866", - "name": "Spawner (213)", - "location": [229, 1419, 0], - "map": "Felucca", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cf7782fd-55ea-4b33-8cfd-6ace1c591fb1", - "name": "Spawner (213)", - "location": [2033, 74, 1], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d26b49c8-1b3f-425e-8f29-87b0ae3073f1", - "name": "Spawner (213)", - "location": [1072, 2195, 5], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3cd2585-5346-47a3-840c-3b4ccbdd34ab", - "name": "Spawner (213)", - "location": [821, 675, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3d68f12-966b-42ab-aa8f-6b035d9fb485", - "name": "Spawner (213)", - "location": [1143, 180, 14], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d4c5eb40-bd81-4de8-ba47-459319c56d4f", - "name": "Spawner (213)", - "location": [1158, 3457, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d532ee9e-fafc-4432-bc06-76cd59789b9c", - "name": "Spawner (213)", - "location": [1931, 3205, 0], - "map": "Felucca", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d55e8962-d269-430e-9b9e-3be35bc38f62", - "name": "Spawner (213)", - "location": [2589, 204, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d5e8e17c-a9b3-420d-b15c-c7ba5057c876", - "name": "Spawner (213)", - "location": [1180, 1354, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d6432a74-1126-4214-b9a0-6dec45594bd7", - "name": "Spawner (213)", - "location": [2103, 3371, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d6c0e2b6-b91d-4c81-a410-dc9eb6c05647", - "name": "Spawner (213)", - "location": [3382, 375, 4], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d76ec9df-9494-4cb4-af37-e257ae4ffdf4", - "name": "Spawner (213)", - "location": [2345, 112, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 }, - { "name": "Zombie", "maxCount": 9, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d8535646-4ceb-4c18-8381-815cacb69db3", - "name": "Spawner (213)", - "location": [3462, 136, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9276ac3-f4a9-4614-bcd5-ff08d76b1a27", - "name": "Spawner (213)", - "location": [1692, 1463, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9802f03-9dbb-422c-91f0-6aba14af525e", - "name": "Spawner (213)", - "location": [1912, 1302, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da41ea67-2124-4dbe-9f22-66e678652e59", - "name": "Spawner (213)", - "location": [1435, 1917, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da43570c-3e93-4a98-847a-1480345bd5b2", - "name": "Spawner (213)", - "location": [1295, 328, 22], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "db1e8042-1bba-4828-8444-1bd0404753f5", - "name": "Spawner (213)", - "location": [2074, 3069, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dcd101fb-c10a-4fe2-a5b2-9e5d7ac965ca", - "name": "Spawner (213)", - "location": [3168, 629, 8], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 5, "probability": 100 }, - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd67249a-421d-4479-b771-3083f7d9e036", - "name": "Spawner (213)", - "location": [2035, 1018, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dddfb4b7-f386-46f5-a7da-7aa2b4ee0315", - "name": "Spawner (213)", - "location": [2034, 108, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "def51564-1874-418a-972b-13e052b9d80b", - "name": "Spawner (213)", - "location": [819, 1841, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df065cb1-c48e-426b-a4df-f2ac9f1c585d", - "name": "Spawner (213)", - "location": [1042, 1394, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dfea35bb-7743-4f04-8ae3-17f91eddcc80", - "name": "Spawner (213)", - "location": [1922, 173, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e073944b-3ed8-4649-9a2e-861f8ed0f1d3", - "name": "Spawner (213)", - "location": [956, 452, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 10, "probability": 100 }, - { "name": "DireWolf", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, - { "name": "Gazer", "maxCount": 10, "probability": 100 }, - { "name": "Harpy", "maxCount": 10, "probability": 100 }, - { "name": "Lizardman", "maxCount": 10, "probability": 100 }, - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e1955cb3-e55a-4c69-a173-60d937009596", - "name": "Spawner (213)", - "location": [2143, 3068, 20], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Spectre", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e260d7d5-b8e5-49da-97c3-0ea924088649", - "name": "Spawner (213)", - "location": [965, 757, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 960, "y": 752, "z": -128 }, - "end": { "x": 970, "y": 762, "z": 15 } - }, - "walkingRange": 10, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e26d2e1a-806c-4929-9f9c-606e861532cc", - "name": "Spawner (213)", - "location": [1080, 1963, 0], - "map": "Felucca", - "count": 9, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Spectre", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e2a94db5-73b3-4012-be1e-ac122f9ebed2", - "name": "Spawner (213)", - "location": [1797, 1393, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e2bf21be-ee5d-433d-bb96-ba564a604f03", - "name": "Spawner (213)", - "location": [278, 1355, 0], - "map": "Felucca", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e3895c37-a997-421f-a67e-808c23a3ebe3", - "name": "Spawner (213)", - "location": [1158, 2872, 0], - "map": "Felucca", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 130, - "walkingRange": 130, - "entries": [ - { "name": "AcidElemental", "maxCount": 24, "probability": 100 }, - { "name": "Alligator", "maxCount": 24, "probability": 100 }, - { "name": "BogThing", "maxCount": 24, "probability": 100 }, - { "name": "Bogling", "maxCount": 24, "probability": 100 }, - { "name": "Corpser", "maxCount": 24, "probability": 100 }, - { "name": "GiantRat", "maxCount": 24, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 24, "probability": 100 }, - { "name": "Lizardman", "maxCount": 23, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 23, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 23, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 23, "probability": 100 }, - { "name": "Ratman", "maxCount": 23, "probability": 100 }, - { "name": "SewerRat", "maxCount": 23, "probability": 100 }, - { "name": "Snake", "maxCount": 23, "probability": 100 }, - { "name": "Wisp", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e3b59c9a-cc31-461a-96da-58a9916f1b6f", - "name": "Spawner (213)", - "location": [4162, 395, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e3bc5021-baec-4097-8279-a50d34d8d905", "name": "Spawner (213)", "location": [2033, 936, 0], @@ -7818,111 +7323,10 @@ ] }, { - "$type": "Spawner", - "guid": "e410df9e-2140-4972-aaed-8e1366658bb4", + "type": "Spawner", + "guid": "23347175-c9e6-4776-a97c-04c85507e131", "name": "Spawner (213)", - "location": [1225, 1477, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e5cc15e9-9e9b-4787-8003-004bac948bcc", - "name": "Spawner (213)", - "location": [182, 1428, 0], - "map": "Felucca", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e783706f-f00b-4bc1-9833-b3cafa22425a", - "name": "Spawner (213)", - "location": [1141, 2959, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Savage", "maxCount": 2, "probability": 100 }, - { "name": "SavageRider", "maxCount": 1, "probability": 100 }, - { "name": "SavageShaman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e8501154-9dab-4c4c-a39f-b9628b3bbb76", - "name": "Spawner (213)", - "location": [3390, 259, 4], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e89c2bad-96b7-475d-a5a7-d759364bde62", - "name": "Spawner (213)", - "location": [2349, 722, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e9e020c2-1a4f-4a3f-9f91-4811c7f0ba13", - "name": "Spawner (213)", - "location": [1983, 1517, 0], + "location": [1990, 976, 0], "map": "Felucca", "count": 7, "minDelay": "00:05:00", @@ -7931,214 +7335,25 @@ "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e9e1a43f-932f-4501-861d-ab721a87d572", - "name": "Spawner (213)", - "location": [1812, 1314, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ea181e6f-6d69-462b-95a0-7c92a7a15736", - "name": "Spawner (213)", - "location": [1996, 73, 8], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, - { "name": "Gazer", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eab22a55-6437-4024-a9e0-b351b03c0042", - "name": "Spawner (213)", - "location": [2540, 130, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eaf8f756-84ca-4a4b-8192-b12cbc585c06", - "name": "Spawner (213)", - "location": [3446, 213, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eb2156a6-745b-4c10-be4c-ebf1204d237f", - "name": "Spawner (213)", - "location": [2454, 248, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec21b7e4-dc3e-4852-a4b0-88bab20a85bc", - "name": "Spawner (213)", - "location": [2113, 221, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec24e1a4-87be-4d55-8565-38292ab737b6", - "name": "Spawner (213)", - "location": [2351, 148, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 }, - { "name": "Zombie", "maxCount": 9, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec92432d-3a21-4e09-8b18-c40643973bb3", - "name": "Spawner (213)", - "location": [2077, 434, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ee6239d0-b83b-434d-831f-b5b533dfbb7d", - "name": "Spawner (213)", - "location": [1912, 1283, 0], - "map": "Felucca", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eed75f19-218d-4bfe-8312-953669844813", - "name": "Spawner (213)", - "location": [788, 1894, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "ef1a52fa-ba6d-48c0-9817-03492eed0ad1", "name": "Spawner (213)", "location": [1993, 1030, 0], @@ -8168,185 +7383,968 @@ ] }, { - "$type": "Spawner", - "guid": "ef74d098-27d0-4c3d-8733-e2312c1b8de3", + "type": "Spawner", + "guid": "dd67249a-421d-4479-b771-3083f7d9e036", "name": "Spawner (213)", - "location": [759, 1828, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eff8645a-0a22-4a43-b54e-919edba83505", - "name": "Spawner (213)", - "location": [1841, 3000, 10], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f0797410-53f9-4af5-976e-332f8d4db4d8", - "name": "Spawner (213)", - "location": [1967, 1296, 0], + "location": [2035, 1018, 0], "map": "Felucca", "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f09c9e65-1864-434c-bc3f-d767fa80e788", + "type": "Spawner", + "guid": "a610c3f7-9200-4609-bbec-1f8e9fa3d46b", "name": "Spawner (213)", - "location": [1949, 824, -1], + "location": [2023, 1052, 0], "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f13df549-7ce0-4283-87e7-ef2971f81d03", + "type": "Spawner", + "guid": "54caf961-eba0-450a-a510-b078d9011faa", "name": "Spawner (213)", - "location": [1905, 883, -1], + "location": [2069, 1028, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cac38c89-0f20-4066-b946-6cd4876723ab", + "name": "Spawner (213)", + "location": [2048, 1066, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "42663241-fc0a-4fcc-aaa8-8fd9ab4bb3a4", + "name": "Spawner (213)", + "location": [1435, 2261, 5], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75aa2658-e0f1-4003-93e6-28da55db063e", + "name": "Spawner (213)", + "location": [1651, 2413, 5], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39c6fca8-585d-419a-ad33-fdeecff5278e", + "name": "Spawner (213)", + "location": [1619, 2725, 10], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6edd0c08-b27b-4bc3-a3bb-603b6359d3ac", + "name": "Spawner (213)", + "location": [1427, 2525, 0], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "850d750e-203e-44c3-8437-b0784ecdd1e8", + "name": "Spawner (213)", + "location": [1059, 2461, 0], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a406878-badb-424f-b754-f4ca946ecd46", + "name": "Spawner (213)", + "location": [1243, 2437, 5], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9267b97a-6678-4f62-a2f3-099e8de35e35", + "name": "Spawner (213)", + "location": [1145, 2231, 20], "map": "Felucca", "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, + "homeRange": 20, "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Ratman", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Daemon", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f1779892-5c27-47db-aa94-a4d59bc1b1e9", + "type": "Spawner", + "guid": "d26b49c8-1b3f-425e-8f29-87b0ae3073f1", "name": "Spawner (213)", - "location": [1979, 885, -1], + "location": [1072, 2195, 5], "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, + "homeRange": 20, "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f866bbb8-35ac-4ff7-a190-de8ad06b77e7", + "name": "Spawner (213)", + "location": [1070, 2256, 5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f4f2c223-5517-4b4b-99f1-8fef5d1e4397", + "name": "Spawner (213)", + "location": [1215, 2195, 5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c067f57d-067c-4cfb-bf77-8d8c6cce1ae6", + "name": "Spawner (213)", + "location": [1216, 2259, 5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "87e84f7b-43c8-41fc-9891-000423b0afb8", + "name": "Spawner (213)", + "location": [1595, 3045, 0], + "map": "Felucca", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 100, "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f2e12985-afee-4b4d-8981-d50e99e1a1d6", + "type": "Spawner", + "guid": "a1a5cffa-a389-4f0d-866b-84416c3e8eac", "name": "Spawner (213)", - "location": [3464, 2503, 49], + "location": [2091, 3413, 0], + "map": "Felucca", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d532ee9e-fafc-4432-bc06-76cd59789b9c", + "name": "Spawner (213)", + "location": [1931, 3205, 0], + "map": "Felucca", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d35912d-be04-44d1-97b2-f6a77882c755", + "name": "Spawner (213)", + "location": [1827, 3429, 10], + "map": "Felucca", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d55517f-6927-4afd-b28c-123e332166ac", + "name": "Spawner (213)", + "location": [1635, 3253, 0], + "map": "Felucca", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6375273a-4a07-4c39-9a5c-5af034151feb", + "name": "Spawner (213)", + "location": [1387, 3109, 0], + "map": "Felucca", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c29f73f4-b7d1-4d83-ba23-d995a5c99085", + "name": "Spawner (213)", + "location": [1110, 2672, 0], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3895c37-a997-421f-a67e-808c23a3ebe3", + "name": "Spawner (213)", + "location": [1158, 2872, 0], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "AcidElemental", "maxCount": 24, "probability": 100 }, + { "name": "Alligator", "maxCount": 24, "probability": 100 }, + { "name": "BogThing", "maxCount": 24, "probability": 100 }, + { "name": "Bogling", "maxCount": 24, "probability": 100 }, + { "name": "Corpser", "maxCount": 24, "probability": 100 }, + { "name": "GiantRat", "maxCount": 24, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 24, "probability": 100 }, + { "name": "Lizardman", "maxCount": 23, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 23, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 23, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 23, "probability": 100 }, + { "name": "Ratman", "maxCount": 23, "probability": 100 }, + { "name": "SewerRat", "maxCount": 23, "probability": 100 }, + { "name": "Snake", "maxCount": 23, "probability": 100 }, + { "name": "Wisp", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e783706f-f00b-4bc1-9833-b3cafa22425a", + "name": "Spawner (213)", + "location": [1141, 2959, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Savage", "maxCount": 2, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb66a7cc-2b6c-4930-90e6-f5bd2da4ec33", + "name": "Spawner (213)", + "location": [1166, 2958, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Savage", "maxCount": 2, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "647ff862-b266-4923-bdeb-78d411d4f0fd", + "name": "Spawner (213)", + "location": [854, 1686, 0], "map": "Felucca", "count": 7, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Brigand", "maxCount": 7, "probability": 100 }, - { "name": "Harpy", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Spectre", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 } - ] + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "Brigand", "maxCount": 7, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f3199ad8-a84a-4919-8353-b3950ebbd19f", + "type": "Spawner", + "guid": "7b61ae14-8163-4511-97f7-bf81e83b4efe", "name": "Spawner (213)", - "location": [893, 1861, 0], + "location": [757, 1401, 0], "map": "Felucca", "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] + "entries": [{ "name": "Lich", "maxCount": 8, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f3330409-2d73-4dd3-88e5-449d92789a28", + "type": "Spawner", + "guid": "136fc430-d130-446c-bfd0-a0daf0d69e50", "name": "Spawner (213)", - "location": [4500, 1382, 23], + "location": [634, 1504, 0], "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 10, + "homeRange": 10, + "walkingRange": 15, "entries": [ - { "name": "Cat", "maxCount": 1, "probability": 100 }, - { "name": "Chicken", "maxCount": 1, "probability": 100 }, - { "name": "Dog", "maxCount": 1, "probability": 100 }, - { "name": "Pig", "maxCount": 1, "probability": 100 }, - { "name": "Sheep", "maxCount": 1, "probability": 100 } + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "42a92b9b-a8ac-498c-b528-94ff6f10b775", + "name": "Spawner (213)", + "location": [634, 1462, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a608396f-9595-4436-b5a9-75a2947a948d", + "name": "Spawner (213)", + "location": [633, 1484, 0], + "map": "Felucca", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e260d7d5-b8e5-49da-97c3-0ea924088649", + "name": "Spawner (213)", + "location": [965, 757, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "579f51aa-6bce-480c-98e0-b29d2c0fcdd1", + "name": "Spawner (213)", + "location": [955, 707, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 12, + "entries": [ + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbbaf085-fb59-4fa5-a525-860584b9f84d", + "name": "Spawner (213)", + "location": [962, 692, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 5, + "entries": [ + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa3003a8-2ab1-4b80-a6d4-eae803b8ba80", + "name": "Spawner (213)", + "location": [948, 703, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 5, + "entries": [ + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb22f21a-37bb-4e7a-b2a8-06db80a58e3a", + "name": "Spawner (213)", + "location": [1016, 821, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "043c3e4b-4a7f-4cf4-b5f7-54064d1c0aeb", + "name": "Spawner (213)", + "location": [1015, 706, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdc7678b-7586-468a-8fd2-185a10a2190d", + "name": "Spawner (213)", + "location": [1017, 826, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1973fede-bee5-4d12-9620-4ac08754278c", + "name": "Spawner (213)", + "location": [4596, 3607, 30], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Daemon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d32dbb7-e905-45c1-b8fe-a73bfb6d5806", + "name": "Spawner (213)", + "location": [4571, 3572, 31], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96b77dd9-7c8a-4a6b-b3a3-18fbbde09fdb", + "name": "Spawner (213)", + "location": [4595, 3574, 75], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [{ "name": "Daemon", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d296a28-acda-437c-82f8-b0dfb62be85d", + "name": "Spawner (213)", + "location": [4618, 3570, 30], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4ae1f335-1c4e-4be3-b495-209990644141", + "name": "Spawner (213)", + "location": [4595, 3570, 30], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be52369b-69c9-435f-b280-bc04927624aa", + "name": "Spawner (213)", + "location": [608, 1702, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Wisp", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c158877f-aa51-4f38-9e0f-0b9792b6603c", + "name": "Spawner (213)", + "location": [4530, 1387, 23], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "GiantRat", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Jwilson", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76ef18b2-8642-45f5-99e0-e206048ed383", + "name": "Spawner (213)", + "location": [4511, 1394, 23], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "IceSerpent", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "FrostOoze", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ac2a868d-5ee3-40a2-b50a-ca2b98717487", + "name": "Spawner (213)", + "location": [4511, 1394, 23], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb340a5d-bc6e-4bf1-b384-5e3c4b1edf2b", + "name": "Spawner (213)", + "location": [4498, 1394, 23], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Rabbit", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "Panther", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Eagle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f490fa81-fb97-460f-bc0f-5ab6db6fa9e4", + "name": "Spawner (213)", + "location": [4485, 1387, 23], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Alligator", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "071f9e9b-26c7-4bc6-a5a0-7867a5049939", + "name": "Spawner (213)", + "location": [4493, 1364, 23], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Boar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "456e985f-4c8d-4f71-b998-d6efc19957f6", + "name": "Spawner (213)", + "location": [4493, 1364, 23], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f372a717-67d5-4f65-a903-6bf06a8107a4", "name": "Spawner (213)", "location": [4506, 1358, 23], @@ -8371,96 +8369,46 @@ ] }, { - "$type": "Spawner", - "guid": "f41e03cd-3997-4d0d-bb42-49adf8ae92e9", + "type": "Spawner", + "guid": "57f5c71c-e443-4f41-a4a0-dac167db544d", "name": "Spawner (213)", - "location": [2013, 839, -1], + "location": [4519, 1358, 23], "map": "Felucca", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f490fa81-fb97-460f-bc0f-5ab6db6fa9e4", - "name": "Spawner (213)", - "location": [4485, 1387, 23], - "map": "Felucca", - "count": 7, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 4, "walkingRange": 15, "entries": [ - { "name": "Alligator", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 1, "probability": 100 } + { "name": "Alligator", "maxCount": 1, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 }, + { "name": "Gorilla", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GiantToad", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f4f2c223-5517-4b4b-99f1-8fef5d1e4397", + "type": "Spawner", + "guid": "0c68cb1c-7662-4ebc-a9e9-166ce2574f9e", "name": "Spawner (213)", - "location": [1215, 2195, 5], + "location": [4531, 1363, 23], "map": "Felucca", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 30, + "homeRange": 4, + "walkingRange": 15, "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f63dafa0-bd09-499c-a34d-e3555d08fdc9", - "name": "Spawner (213)", - "location": [733, 1937, 0], - "map": "Felucca", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f69aa349-7dd7-4a24-945e-f75cd274cfba", - "name": "Spawner (213)", - "location": [1473, 229, 22], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f71bc29c-8c95-4531-8bdf-51f27e350b39", "name": "Spawner (213)", "location": [4512, 1372, 23], @@ -8480,147 +8428,98 @@ ] }, { - "$type": "Spawner", - "guid": "f866bbb8-35ac-4ff7-a190-de8ad06b77e7", + "type": "Spawner", + "guid": "1f59609c-ffa6-4a3c-ae8f-92caf32391a1", "name": "Spawner (213)", - "location": [1070, 2256, 5], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f886f7b8-60a5-45c0-bf80-05032137df23", - "name": "Spawner (213)", - "location": [1249, 2030, 0], - "map": "Felucca", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f9013421-1e07-4736-b861-4ae0e0eb5f1f", - "name": "Spawner (213)", - "location": [480, 1641, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f950f3e2-4656-41d1-a46d-6941b77d6ebf", - "name": "Spawner (213)", - "location": [2109, 2989, 30], + "location": [4512, 1380, 23], "map": "Felucca", "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fa3003a8-2ab1-4b80-a6d4-eae803b8ba80", - "name": "Spawner (213)", - "location": [948, 703, 0], - "map": "Felucca", - "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 946, "y": 701, "z": -128 }, - "end": { "x": 950, "y": 705, "z": 15 } - }, - "walkingRange": 5, + "homeRange": 3, + "walkingRange": 10, "entries": [ - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 } + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Jwilson", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "faa69ada-cbbc-4c81-849d-ad4f66bb02ce", + "type": "Spawner", + "guid": "f3330409-2d73-4dd3-88e5-449d92789a28", "name": "Spawner (213)", - "location": [4057, 300, 0], + "location": [4500, 1382, 23], "map": "Felucca", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Chicken", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Pig", "maxCount": 1, "probability": 100 }, + { "name": "Sheep", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fabc9f7f-e030-43da-adf3-7c8065e0eeec", + "type": "Spawner", + "guid": "d3cd2585-5346-47a3-840c-3b4ccbdd34ab", "name": "Spawner (213)", - "location": [2021, 2313, 0], + "location": [821, 675, 0], "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "df065cb1-c48e-426b-a4df-f2ac9f1c585d", + "name": "Spawner (213)", + "location": [1042, 1394, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eab22a55-6437-4024-a9e0-b351b03c0042", + "name": "Spawner (213)", + "location": [2540, 130, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "faea3534-a9c2-4dd8-af03-3f231ad976e3", "name": "Spawner (213)", "location": [3170, 672, 0], @@ -8637,167 +8536,207 @@ ] }, { - "$type": "Spawner", - "guid": "fb66a7cc-2b6c-4930-90e6-f5bd2da4ec33", + "type": "Spawner", + "guid": "a9bd52d6-d0bb-4999-8a8b-e5168463c718", "name": "Spawner (213)", - "location": [1166, 2958, 0], + "location": [2548, 1173, 0], "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Savage", "maxCount": 2, "probability": 100 }, - { "name": "SavageRider", "maxCount": 1, "probability": 100 }, - { "name": "SavageShaman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fbfa536c-3e04-4609-953f-e77bb2a48bb4", - "name": "Spawner (213)", - "location": [157, 1487, 0], - "map": "Felucca", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fc0d728c-fa4a-4913-8834-555178746625", - "name": "Spawner (213)", - "location": [4519, 3097, 0], - "map": "Felucca", - "count": 10, - "minDelay": "01:00:00", + "count": 1, + "minDelay": "00:45:00", "maxDelay": "01:00:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fd671a3d-e780-4f6f-993b-daa7dc80457b", + "type": "Spawner", + "guid": "87bb1dc1-dc09-44cf-b2cc-1b72e55ac804", "name": "Spawner (213)", - "location": [1802, 1506, 0], + "location": [3602, 2815, 28], "map": "Felucca", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fe3d76ed-5b62-4db7-b8bd-ab6778bdbcc1", - "name": "Spawner (213)", - "location": [2478, 100, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fe612581-49b6-43f5-bc62-a245821a3041", - "name": "Spawner (213)", - "location": [971, 1308, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fe99c6b3-b023-46ff-8559-7c4b26728027", - "name": "Spawner (213)", - "location": [2033, 3041, 15], - "map": "Felucca", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ffe958cd-13e8-4caf-9694-af4cb65b8842", - "name": "Spawner (213)", - "location": [1038, 1149, 0], - "map": "Felucca", - "count": 3, - "minDelay": "01:00:00", + "count": 1, + "minDelay": "00:45:00", "maxDelay": "01:00:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6629f955-9f48-46ed-affe-6ef6bb823646", + "name": "Spawner (213)", + "location": [4480, 1439, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96afa046-b31d-4bda-8839-026e8e3fd86f", + "name": "Spawner (213)", + "location": [1152, 3471, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce5519e9-137c-4db7-b7a6-4dadcbdaa9ad", + "name": "Spawner (213)", + "location": [1530, 999, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3e704fd-0a49-4396-a196-b56afeea5ffc", + "name": "Spawner (213)", + "location": [1871, 1076, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eed75f19-218d-4bfe-8312-953669844813", + "name": "Spawner (213)", + "location": [788, 1894, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84b2cd30-182a-4251-b95d-3904e80ee9ee", + "name": "Spawner (213)", + "location": [1064, 2488, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6432a74-1126-4214-b9a0-6dec45594bd7", + "name": "Spawner (213)", + "location": [2103, 3371, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a7b60db-3c30-449e-b20a-f85336dd5446", + "name": "Spawner (213)", + "location": [2044, 2323, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "713534d6-6d42-4846-95e2-e43b5aaa340f", + "name": "Spawner (213)", + "location": [1939, 1310, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e89c2bad-96b7-475d-a5a7-d759364bde62", + "name": "Spawner (213)", + "location": [2349, 722, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/PaintedCaves.json b/Distribution/Data/Spawns/post-uoml/felucca/PaintedCaves.json similarity index 96% rename from Distribution/Data/Spawns/shared/felucca/PaintedCaves.json rename to Distribution/Data/Spawns/post-uoml/felucca/PaintedCaves.json index 419a37945..742de6497 100644 --- a/Distribution/Data/Spawns/shared/felucca/PaintedCaves.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/PaintedCaves.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "2733b112-93bf-47cc-b071-02e87c3150ab", "name": "Spawner (214)", "location": [6279, 879, -1], @@ -21,7 +21,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bc6475e8-885e-4e5e-bf83-49c3e408ac06", "name": "Spawner (214)", "location": [6275, 879, -2], diff --git a/Distribution/Data/Spawns/shared/felucca/PalaceOfParoxysmus.json b/Distribution/Data/Spawns/post-uoml/felucca/PalaceOfParoxysmus.json similarity index 94% rename from Distribution/Data/Spawns/shared/felucca/PalaceOfParoxysmus.json rename to Distribution/Data/Spawns/post-uoml/felucca/PalaceOfParoxysmus.json index a30e2596c..a683111c6 100644 --- a/Distribution/Data/Spawns/shared/felucca/PalaceOfParoxysmus.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/PalaceOfParoxysmus.json @@ -1,61 +1,30 @@ [ { - "$type": "Spawner", - "guid": "04a75e0b-4499-4fe6-8bd6-49e3dbf0d528", + "type": "Spawner", + "guid": "604b87ed-6718-4802-b1b6-f572ed7d9935", "name": "Spawner (215)", - "location": [6416, 447, -40], + "location": [6295, 616, -50], "map": "Felucca", - "count": 15, + "count": 27, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, + "homeRange": 50, + "walkingRange": 50, "entries": [ - { "name": "PlagueBeast", "maxCount": 8, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 8, "probability": 100 }, - { "name": "AcidElemental", "maxCount": 7, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 7, "probability": 100 } + { "name": "Daemon", "maxCount": 6, "probability": 100 }, + { "name": "Succubus", "maxCount": 6, "probability": 100 }, + { "name": "InterredGrizzle", "maxCount": 6, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 6, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 6, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 6, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 3, "probability": 100 }, + { "name": "Putrefier", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeastLord", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "04cc01ab-f7da-4992-8913-4db4ea63149e", - "name": "Spawner (215)", - "location": [6298, 395, 60], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a0d9e92-6661-4ad8-a38e-dcf7516e05de", - "name": "Spawner (215)", - "location": [6354, 455, -40], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "PlagueBeast", "maxCount": 5, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 5, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 5, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "5a586bde-b6a0-4f4d-b147-1e1886aae735", "name": "Spawner (215)", "location": [6384, 580, -50], @@ -79,7 +48,139 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "e695b242-221c-49b5-853a-9e943afe8355", + "name": "Spawner (215)", + "location": [6328, 517, -50], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "PoisonElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a0d9e92-6661-4ad8-a38e-dcf7516e05de", + "name": "Spawner (215)", + "location": [6354, 455, -40], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "PlagueBeast", "maxCount": 5, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 5, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 5, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04a75e0b-4499-4fe6-8bd6-49e3dbf0d528", + "name": "Spawner (215)", + "location": [6416, 447, -40], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "PlagueBeast", "maxCount": 8, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 8, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 7, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad3fbc20-7914-4651-a7c1-994d9b88ed22", + "name": "Spawner (215)", + "location": [6466, 543, -50], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Balron", "maxCount": 6, "probability": 100 }, + { "name": "Succubus", "maxCount": 6, "probability": 100 }, + { "name": "Daemon", "maxCount": 6, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeastLord", "maxCount": 8, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 8, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 8, "probability": 100 }, + { "name": "InterredGrizzle", "maxCount": 5, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b7da2a27-b13e-4113-b8c8-e5f2e7ac2e3a", + "name": "Spawner (215)", + "location": [6292, 462, -50], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "ChaosDaemon", "maxCount": 7, "probability": 100 }, + { "name": "Moloch", "maxCount": 7, "probability": 100 }, + { "name": "Succubus", "maxCount": 7, "probability": 100 }, + { "name": "Daemon", "maxCount": 7, "probability": 100 }, + { "name": "Balron", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04cc01ab-f7da-4992-8913-4db4ea63149e", + "name": "Spawner (215)", + "location": [6298, 395, 60], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "74269ae8-5e94-4496-ad04-611e81b14168", + "name": "Spawner (215)", + "location": [6404, 378, -40], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 18, + "entries": [ + { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 2, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "5ef37566-7b9c-47fb-a88c-d6c0177d92f3", "name": "Spawner (215)", "location": [6418, 358, -40], @@ -104,148 +205,7 @@ ] }, { - "$type": "Spawner", - "guid": "604b87ed-6718-4802-b1b6-f572ed7d9935", - "name": "Spawner (215)", - "location": [6295, 616, -50], - "map": "Felucca", - "count": 27, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Daemon", "maxCount": 6, "probability": 100 }, - { "name": "Succubus", "maxCount": 6, "probability": 100 }, - { "name": "InterredGrizzle", "maxCount": 6, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 6, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 6, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 6, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 3, "probability": 100 }, - { "name": "Putrefier", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeastLord", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "74269ae8-5e94-4496-ad04-611e81b14168", - "name": "Spawner (215)", - "location": [6404, 378, -40], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 18, - "walkingRange": 18, - "entries": [ - { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 2, "probability": 100 }, - { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c1149a5-ac2a-42e6-b6e2-296e6ca05602", - "name": "Spawner (215)", - "location": [6249, 352, 60], - "map": "Felucca", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "AcidElemental", "maxCount": 6, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 6, "probability": 100 }, - { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad3fbc20-7914-4651-a7c1-994d9b88ed22", - "name": "Spawner (215)", - "location": [6466, 543, -50], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Balron", "maxCount": 6, "probability": 100 }, - { "name": "Succubus", "maxCount": 6, "probability": 100 }, - { "name": "Daemon", "maxCount": 6, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeastLord", "maxCount": 8, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 8, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 8, "probability": 100 }, - { "name": "InterredGrizzle", "maxCount": 5, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "af6ef316-bb00-417f-852c-14e9c98ca74c", - "name": "Spawner (215)", - "location": [6285, 353, 60], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b7da2a27-b13e-4113-b8c8-e5f2e7ac2e3a", - "name": "Spawner (215)", - "location": [6292, 462, -50], - "map": "Felucca", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "ChaosDaemon", "maxCount": 7, "probability": 100 }, - { "name": "Moloch", "maxCount": 7, "probability": 100 }, - { "name": "Succubus", "maxCount": 7, "probability": 100 }, - { "name": "Daemon", "maxCount": 7, "probability": 100 }, - { "name": "Balron", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bbed003d-43d6-4690-b93e-763f42a4187f", - "name": "Spawner (215)", - "location": [6353, 400, 60], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "d3ba2375-fc73-4996-9f54-3a09dc39c653", "name": "Spawner (215)", "location": [6319, 348, 60], @@ -268,10 +228,45 @@ ] }, { - "$type": "Spawner", - "guid": "e695b242-221c-49b5-853a-9e943afe8355", + "type": "Spawner", + "guid": "9c1149a5-ac2a-42e6-b6e2-296e6ca05602", "name": "Spawner (215)", - "location": [6328, 517, -50], + "location": [6249, 352, 60], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "AcidElemental", "maxCount": 6, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 6, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af6ef316-bb00-417f-852c-14e9c98ca74c", + "name": "Spawner (215)", + "location": [6285, 353, 60], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "PoisonElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bbed003d-43d6-4690-b93e-763f42a4187f", + "name": "Spawner (215)", + "location": [6353, 400, 60], "map": "Felucca", "count": 4, "minDelay": "00:05:00", @@ -280,7 +275,8 @@ "homeRange": 10, "walkingRange": 10, "entries": [ - { "name": "PoisonElemental", "maxCount": 4, "probability": 100 } + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 4, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/PrismOfLight.json b/Distribution/Data/Spawns/post-uoml/felucca/PrismOfLight.json similarity index 77% rename from Distribution/Data/Spawns/shared/felucca/PrismOfLight.json rename to Distribution/Data/Spawns/post-uoml/felucca/PrismOfLight.json index e5e74a13a..f32330467 100644 --- a/Distribution/Data/Spawns/shared/felucca/PrismOfLight.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/PrismOfLight.json @@ -1,234 +1,6 @@ [ { - "$type": "Spawner", - "guid": "3ccfb405-2b18-4397-9d18-5b37302d62e4", - "name": "Spawner (216)", - "location": [6521, 139, -20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49f09e2e-fb04-42a9-96e2-d930c04f729f", - "name": "Spawner (216)", - "location": [6530, 139, -20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4e01097c-b1f4-4d22-ad9b-dcd117eeaeec", - "name": "Spawner (216)", - "location": [6566, 120, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "CrystalVortex", "maxCount": 7, "probability": 100 }, - { "name": "IceElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50809274-807a-49ee-b9f9-6c13693a56d0", - "name": "Spawner (216)", - "location": [6572, 89, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "CrystalHydra", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "539ddfd6-d1ad-4e3f-b9cf-414a6dc0553a", - "name": "Spawner (216)", - "location": [6509, 176, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "UnfrozenMummy", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6912561a-5610-4441-8c64-16f4bd9f73f8", - "name": "Spawner (216)", - "location": [6510, 171, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Protector", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84ae0146-70ef-44e4-9159-db9a045c14c9", - "name": "Spawner (216)", - "location": [6536, 79, -10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "CrystalDaemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8f88efe1-68b8-478e-8166-b03984e956e1", - "name": "Spawner (216)", - "location": [6504, 181, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8fe6c0c3-9fcb-4725-b6b2-ee89df036d80", - "name": "Spawner (216)", - "location": [6532, 79, -10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "CrystalDaemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93922052-811a-473e-b3b2-eddf5380829a", - "name": "Spawner (216)", - "location": [6475, 101, -44], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "ShadowWisp", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c7afc30-19c5-4034-a344-8b75e52b6b40", - "name": "Spawner (216)", - "location": [6547, 91, -10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "CrystalDaemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9ce6b636-4bca-44d7-adf2-5ef841d08a61", - "name": "Spawner (216)", - "location": [6535, 179, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "CrystalWisp", "maxCount": 5, "probability": 100 }, - { "name": "CrystalLatticeSeeker", "maxCount": 5, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 5, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9dc74009-4df1-4872-874f-fcc908ec105b", - "name": "Spawner (216)", - "location": [6534, 178, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "CrystalWisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "adf86a17-213e-408b-be55-3cb37b9a1a36", - "name": "Spawner (216)", - "location": [6502, 120, -10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 0, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "b87b2fe5-b1dc-46b1-9c35-f1627c33ad04", "name": "Spawner (216)", "location": [6553, 157, 0], @@ -239,12 +11,10 @@ "team": 0, "homeRange": 8, "walkingRange": 8, - "entries": [ - { "name": "CrystalLatticeSeeker", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "CrystalLatticeSeeker", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c04125c7-bad1-4747-8e55-7664e9797a6d", "name": "Spawner (216)", "location": [6474, 74, -34], @@ -261,56 +31,7 @@ ] }, { - "$type": "Spawner", - "guid": "c66d4bde-6308-44bb-80fe-f6f0070beda7", - "name": "Spawner (216)", - "location": [6478, 174, 4], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Wisp", "maxCount": 6, "probability": 100 }, - { "name": "CrystalWisp", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e82c7115-8675-4d30-b321-c5e6a3b43ea0", - "name": "Spawner (216)", - "location": [6507, 151, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Protector", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ed613a4e-b19f-422e-943c-c94a29411ff8", - "name": "Spawner (216)", - "location": [6578, 183, 31], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Ferret", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f083b3bb-c6c8-4774-a9d7-65d6b2cfc831", "name": "Spawner (216)", "location": [6552, 155, 0], @@ -329,7 +50,35 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "3ccfb405-2b18-4397-9d18-5b37302d62e4", + "name": "Spawner (216)", + "location": [6521, 139, -20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Wisp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49f09e2e-fb04-42a9-96e2-d930c04f729f", + "name": "Spawner (216)", + "location": [6530, 139, -20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Wisp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "f620b501-c242-4301-a813-b4d7a44e141f", "name": "Spawner (216)", "location": [6576, 91, 0], @@ -340,8 +89,227 @@ "team": 0, "homeRange": 4, "walkingRange": 4, + "entries": [{ "name": "CrystalSeaSerpent", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "539ddfd6-d1ad-4e3f-b9cf-414a6dc0553a", + "name": "Spawner (216)", + "location": [6509, 176, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "UnfrozenMummy", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84ae0146-70ef-44e4-9159-db9a045c14c9", + "name": "Spawner (216)", + "location": [6536, 79, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9c7afc30-19c5-4034-a344-8b75e52b6b40", + "name": "Spawner (216)", + "location": [6547, 91, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50809274-807a-49ee-b9f9-6c13693a56d0", + "name": "Spawner (216)", + "location": [6572, 89, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalHydra", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8f88efe1-68b8-478e-8166-b03984e956e1", + "name": "Spawner (216)", + "location": [6504, 181, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93922052-811a-473e-b3b2-eddf5380829a", + "name": "Spawner (216)", + "location": [6475, 101, -44], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "ShadowWisp", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c66d4bde-6308-44bb-80fe-f6f0070beda7", + "name": "Spawner (216)", + "location": [6478, 174, 4], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, "entries": [ - { "name": "CrystalSeaSerpent", "maxCount": 2, "probability": 100 } + { "name": "Wisp", "maxCount": 6, "probability": 100 }, + { "name": "CrystalWisp", "maxCount": 6, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "4e01097c-b1f4-4d22-ad9b-dcd117eeaeec", + "name": "Spawner (216)", + "location": [6566, 120, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "CrystalVortex", "maxCount": 7, "probability": 100 }, + { "name": "IceElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9dc74009-4df1-4872-874f-fcc908ec105b", + "name": "Spawner (216)", + "location": [6534, 178, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalWisp", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ce6b636-4bca-44d7-adf2-5ef841d08a61", + "name": "Spawner (216)", + "location": [6535, 179, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "CrystalWisp", "maxCount": 5, "probability": 100 }, + { "name": "CrystalLatticeSeeker", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "adf86a17-213e-408b-be55-3cb37b9a1a36", + "name": "Spawner (216)", + "location": [6502, 120, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e82c7115-8675-4d30-b321-c5e6a3b43ea0", + "name": "Spawner (216)", + "location": [6507, 151, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Protector", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8fe6c0c3-9fcb-4725-b6b2-ee89df036d80", + "name": "Spawner (216)", + "location": [6532, 79, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed613a4e-b19f-422e-943c-c94a29411ff8", + "name": "Spawner (216)", + "location": [6578, 183, 31], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Ferret", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6912561a-5610-4441-8c64-16f4bd9f73f8", + "name": "Spawner (216)", + "location": [6510, 171, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Protector", "maxCount": 3, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Reagents.json b/Distribution/Data/Spawns/post-uoml/felucca/Reagents.json similarity index 97% rename from Distribution/Data/Spawns/shared/felucca/Reagents.json rename to Distribution/Data/Spawns/post-uoml/felucca/Reagents.json index dddc04df8..5a52d0dce 100644 --- a/Distribution/Data/Spawns/shared/felucca/Reagents.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Reagents.json @@ -1,213 +1,6 @@ [ { - "$type": "Spawner", - "guid": "0034e4c4-162c-4569-bd5d-9faef725ecd5", - "name": "Spawner (229)", - "location": [2054, 516, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "046e36ec-faf1-4f5f-8f8f-4cf50e2ba3ef", - "name": "Spawner (229)", - "location": [1938, 1408, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "065cdc7b-b2ce-4339-b45c-c96f4a734723", - "name": "Spawner (229)", - "location": [5618, 2744, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0b3ccf5f-b1e6-4cd7-a1f7-b4662fe4ff9a", - "name": "Spawner (229)", - "location": [1070, 2320, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0b82556d-2f5d-4fa0-b53e-fc95ed035b3f", - "name": "Spawner (229)", - "location": [1286, 504, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0d10ddc0-6866-4c69-945e-830fe9fa18c5", - "name": "Spawner (229)", - "location": [5674, 3120, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11579f1f-1437-48f9-9061-a409f8468750", - "name": "Spawner (229)", - "location": [1566, 1140, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "16af2efe-3319-4812-9089-2fe793653f21", - "name": "Spawner (229)", - "location": [1114, 1416, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1853a130-1967-4711-bb38-522e045b5820", - "name": "Spawner (229)", - "location": [1514, 1584, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "1bafedf2-97d4-484d-9ef3-4597666765ce", "name": "Spawner (229)", "location": [2854, 816, 0], @@ -230,260 +23,7 @@ ] }, { - "$type": "Spawner", - "guid": "21879949-0b91-42cc-ad2d-7b616b27ad05", - "name": "Spawner (229)", - "location": [342, 1464, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2fe931f6-6f60-411c-a235-dcc4b9cb9fc2", - "name": "Spawner (229)", - "location": [1934, 2328, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "36a65d30-bbb1-4476-9336-302ca764c41b", - "name": "Spawner (229)", - "location": [1658, 236, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "39aee7f8-373f-4191-b349-f5c9bb367abd", - "name": "Spawner (229)", - "location": [2930, 3476, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a6221c9-7105-4707-aaa1-8e9005016a6b", - "name": "Spawner (229)", - "location": [718, 1180, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3c91dce8-087c-4dff-96bf-6d2f32ff57b9", - "name": "Spawner (229)", - "location": [2750, 368, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3fe88456-6018-41e4-b9af-a129a1025651", - "name": "Spawner (229)", - "location": [3602, 2948, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40ea761c-a1da-48b7-9663-0571df9a28cc", - "name": "Spawner (229)", - "location": [1078, 2772, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4144faa9-61d4-47fa-be5c-665e94989f9c", - "name": "Spawner (229)", - "location": [1466, 3032, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4394742a-5799-4d8c-9d29-11753415b5b5", - "name": "Spawner (229)", - "location": [1122, 3524, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "450db427-aea5-48a7-95f4-36f2e64d4aec", - "name": "Spawner (229)", - "location": [2374, 652, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "472b629a-65dc-4e26-a728-19a1dc715740", "name": "Spawner (229)", "location": [4590, 1448, 0], @@ -506,605 +46,7 @@ ] }, { - "$type": "Spawner", - "guid": "4f547881-9057-4530-bacd-f2d8b8d3918a", - "name": "Spawner (229)", - "location": [2522, 84, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50c090e2-807c-4a99-a467-a0430a8cc232", - "name": "Spawner (229)", - "location": [2734, 2168, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "51190b84-1742-4bea-ab6e-9387c4ff0efa", - "name": "Spawner (229)", - "location": [4426, 1028, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54855ff6-f02b-4bc4-a803-455719541850", - "name": "Spawner (229)", - "location": [5934, 3436, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54bb9a76-f991-425d-9b3a-20310659b1dd", - "name": "Spawner (229)", - "location": [702, 1752, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "59cfb257-99a9-4a1d-9220-8aeb3712dd60", - "name": "Spawner (229)", - "location": [3538, 2536, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6813adda-8875-4603-9a78-57aab7688e56", - "name": "Spawner (229)", - "location": [5326, 2492, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6a796506-6692-420c-bc95-cd7fcf99d1b9", - "name": "Spawner (229)", - "location": [2062, 964, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7a28f716-9fa0-4686-ad8c-ca1e7ab1a9e3", - "name": "Spawner (229)", - "location": [1582, 2028, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7eaf59ab-e769-4772-aaa3-06d741e424ae", - "name": "Spawner (229)", - "location": [1490, 2472, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "821fbdbf-ae39-4073-adb0-17e2e9ee87aa", - "name": "Spawner (229)", - "location": [2082, 60, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8255d504-90e7-4ca3-a2bd-1cdfc72983f7", - "name": "Spawner (229)", - "location": [1846, 3260, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8553a096-7a8e-4f69-accf-401bfef947fb", - "name": "Spawner (229)", - "location": [5474, 3904, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "870cd346-44a3-45cd-b8f2-b65543021467", - "name": "Spawner (229)", - "location": [3198, 504, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8c69fc76-ecf7-4282-84c1-9fa5cecda213", - "name": "Spawner (229)", - "location": [2474, 3956, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "96d35be0-ba86-4f2e-8c27-2ef75a684c93", - "name": "Spawner (229)", - "location": [5926, 3888, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b42e3cb-2224-4516-9fe2-165a19bcda3d", - "name": "Spawner (229)", - "location": [366, 900, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9f1da2ac-4a31-415d-99c6-76921de812b0", - "name": "Spawner (229)", - "location": [4282, 3700, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ab7cf0fc-e53d-4f1b-a6f6-7c9bc21df8df", - "name": "Spawner (229)", - "location": [1694, 688, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abb85eaf-f92e-41a0-a6cf-8c0bfab71e30", - "name": "Spawner (229)", - "location": [642, 2216, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b0d2917b-3296-4811-bf68-c9eb6fac7f2b", - "name": "Spawner (229)", - "location": [5234, 3068, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b229c260-ee7d-4aab-bacf-3e974e2e8934", - "name": "Spawner (229)", - "location": [4166, 584, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b745dd5f-b8ef-4950-baef-569e80994036", - "name": "Spawner (229)", - "location": [2226, 3532, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b89e1c55-2a8a-4e42-aa92-5718f3699c0f", - "name": "Spawner (229)", - "location": [746, 636, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c012739f-6a97-413c-b2bd-249fd2b7e51f", - "name": "Spawner (229)", - "location": [1410, 3796, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c4d5ac68-9b53-488f-94bd-6e6d1dec3493", - "name": "Spawner (229)", - "location": [5462, 3452, 0], - "map": "Felucca", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "cd38b465-2ed5-4634-ae0a-e8b2ab38c2d4", "name": "Spawner (229)", "location": [2490, 1096, 0], @@ -1127,10 +69,10 @@ ] }, { - "$type": "Spawner", - "guid": "cdd876a8-1378-4ccd-8f12-9cdac9c3487a", + "type": "Spawner", + "guid": "11579f1f-1437-48f9-9061-a409f8468750", "name": "Spawner (229)", - "location": [4570, 3340, 0], + "location": [1566, 1140, 0], "map": "Felucca", "count": 160, "minDelay": "00:11:00", @@ -1150,7 +92,145 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "1853a130-1967-4711-bb38-522e045b5820", + "name": "Spawner (229)", + "location": [1514, 1584, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "046e36ec-faf1-4f5f-8f8f-4cf50e2ba3ef", + "name": "Spawner (229)", + "location": [1938, 1408, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "450db427-aea5-48a7-95f4-36f2e64d4aec", + "name": "Spawner (229)", + "location": [2374, 652, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0034e4c4-162c-4569-bd5d-9faef725ecd5", + "name": "Spawner (229)", + "location": [2054, 516, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b82556d-2f5d-4fa0-b53e-fc95ed035b3f", + "name": "Spawner (229)", + "location": [1286, 504, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b89e1c55-2a8a-4e42-aa92-5718f3699c0f", + "name": "Spawner (229)", + "location": [746, 636, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "d233e91c-82d3-4130-a747-2be35a3c7e55", "name": "Spawner (229)", "location": [1134, 964, 0], @@ -1173,10 +253,10 @@ ] }, { - "$type": "Spawner", - "guid": "d276b701-845a-4648-ba54-fde4f1a7d725", + "type": "Spawner", + "guid": "3a6221c9-7105-4707-aaa1-8e9005016a6b", "name": "Spawner (229)", - "location": [3994, 328, 0], + "location": [718, 1180, 0], "map": "Felucca", "count": 160, "minDelay": "00:11:00", @@ -1196,10 +276,10 @@ ] }, { - "$type": "Spawner", - "guid": "d3bf2b6e-3947-4a69-9600-04527ef88ff1", + "type": "Spawner", + "guid": "21879949-0b91-42cc-ad2d-7b616b27ad05", "name": "Spawner (229)", - "location": [3130, 116, 0], + "location": [342, 1464, 0], "map": "Felucca", "count": 160, "minDelay": "00:11:00", @@ -1219,10 +299,10 @@ ] }, { - "$type": "Spawner", - "guid": "dbc82493-6b0c-46a7-a243-e0cb875f4bcf", + "type": "Spawner", + "guid": "54bb9a76-f991-425d-9b3a-20310659b1dd", "name": "Spawner (229)", - "location": [5966, 2456, 0], + "location": [702, 1752, 0], "map": "Felucca", "count": 160, "minDelay": "00:11:00", @@ -1242,10 +322,10 @@ ] }, { - "$type": "Spawner", - "guid": "dd4c385d-b00e-47ab-a6bc-f2cb490c9567", + "type": "Spawner", + "guid": "16af2efe-3319-4812-9089-2fe793653f21", "name": "Spawner (229)", - "location": [6042, 2916, 0], + "location": [1114, 1416, 0], "map": "Felucca", "count": 160, "minDelay": "00:11:00", @@ -1265,7 +345,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e0199ccf-a3a0-4888-9939-42b7a5ad553a", "name": "Spawner (229)", "location": [1150, 1876, 0], @@ -1288,10 +368,10 @@ ] }, { - "$type": "Spawner", - "guid": "e17f7124-9255-4f70-94b4-c6716544873d", + "type": "Spawner", + "guid": "7a28f716-9fa0-4686-ad8c-ca1e7ab1a9e3", "name": "Spawner (229)", - "location": [3674, 1216, 0], + "location": [1582, 2028, 0], "map": "Felucca", "count": 160, "minDelay": "00:11:00", @@ -1311,10 +391,10 @@ ] }, { - "$type": "Spawner", - "guid": "f229444f-13ea-4dba-aa0e-d0a4bd0887ee", + "type": "Spawner", + "guid": "2fe931f6-6f60-411c-a235-dcc4b9cb9fc2", "name": "Spawner (229)", - "location": [1850, 2796, 0], + "location": [1934, 2328, 0], "map": "Felucca", "count": 160, "minDelay": "00:11:00", @@ -1334,7 +414,145 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "7eaf59ab-e769-4772-aaa3-06d741e424ae", + "name": "Spawner (229)", + "location": [1490, 2472, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b3ccf5f-b1e6-4cd7-a1f7-b4662fe4ff9a", + "name": "Spawner (229)", + "location": [1070, 2320, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4144faa9-61d4-47fa-be5c-665e94989f9c", + "name": "Spawner (229)", + "location": [1466, 3032, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a796506-6692-420c-bc95-cd7fcf99d1b9", + "name": "Spawner (229)", + "location": [2062, 964, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8255d504-90e7-4ca3-a2bd-1cdfc72983f7", + "name": "Spawner (229)", + "location": [1846, 3260, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40ea761c-a1da-48b7-9663-0571df9a28cc", + "name": "Spawner (229)", + "location": [1078, 2772, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f5544628-f982-40f3-b44a-f5db0507a62e", "name": "Spawner (229)", "location": [4710, 3796, 0], @@ -1357,7 +575,306 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "96d35be0-ba86-4f2e-8c27-2ef75a684c93", + "name": "Spawner (229)", + "location": [5926, 3888, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8553a096-7a8e-4f69-accf-401bfef947fb", + "name": "Spawner (229)", + "location": [5474, 3904, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54855ff6-f02b-4bc4-a803-455719541850", + "name": "Spawner (229)", + "location": [5934, 3436, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4d5ac68-9b53-488f-94bd-6e6d1dec3493", + "name": "Spawner (229)", + "location": [5462, 3452, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d10ddc0-6866-4c69-945e-830fe9fa18c5", + "name": "Spawner (229)", + "location": [5674, 3120, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "065cdc7b-b2ce-4339-b45c-c96f4a734723", + "name": "Spawner (229)", + "location": [5618, 2744, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6813adda-8875-4603-9a78-57aab7688e56", + "name": "Spawner (229)", + "location": [5326, 2492, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbc82493-6b0c-46a7-a243-e0cb875f4bcf", + "name": "Spawner (229)", + "location": [5966, 2456, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0d2917b-3296-4811-bf68-c9eb6fac7f2b", + "name": "Spawner (229)", + "location": [5234, 3068, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd4c385d-b00e-47ab-a6bc-f2cb490c9567", + "name": "Spawner (229)", + "location": [6042, 2916, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fe88456-6018-41e4-b9af-a129a1025651", + "name": "Spawner (229)", + "location": [3602, 2948, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59cfb257-99a9-4a1d-9220-8aeb3712dd60", + "name": "Spawner (229)", + "location": [3538, 2536, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50c090e2-807c-4a99-a467-a0430a8cc232", + "name": "Spawner (229)", + "location": [2734, 2168, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f55cc26e-f8f2-47cc-9af0-ccd026d9a18d", "name": "Spawner (229)", "location": [3714, 2152, 0], @@ -1378,5 +895,488 @@ { "name": "Nightshade", "maxCount": 160, "probability": 100 }, { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "e17f7124-9255-4f70-94b4-c6716544873d", + "name": "Spawner (229)", + "location": [3674, 1216, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d276b701-845a-4648-ba54-fde4f1a7d725", + "name": "Spawner (229)", + "location": [3994, 328, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b229c260-ee7d-4aab-bacf-3e974e2e8934", + "name": "Spawner (229)", + "location": [4166, 584, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "870cd346-44a3-45cd-b8f2-b65543021467", + "name": "Spawner (229)", + "location": [3198, 504, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c91dce8-087c-4dff-96bf-6d2f32ff57b9", + "name": "Spawner (229)", + "location": [2750, 368, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ab7cf0fc-e53d-4f1b-a6f6-7c9bc21df8df", + "name": "Spawner (229)", + "location": [1694, 688, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b42e3cb-2224-4516-9fe2-165a19bcda3d", + "name": "Spawner (229)", + "location": [366, 900, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abb85eaf-f92e-41a0-a6cf-8c0bfab71e30", + "name": "Spawner (229)", + "location": [642, 2216, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b745dd5f-b8ef-4950-baef-569e80994036", + "name": "Spawner (229)", + "location": [2226, 3532, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f229444f-13ea-4dba-aa0e-d0a4bd0887ee", + "name": "Spawner (229)", + "location": [1850, 2796, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36a65d30-bbb1-4476-9336-302ca764c41b", + "name": "Spawner (229)", + "location": [1658, 236, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "51190b84-1742-4bea-ab6e-9387c4ff0efa", + "name": "Spawner (229)", + "location": [4426, 1028, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f547881-9057-4530-bacd-f2d8b8d3918a", + "name": "Spawner (229)", + "location": [2522, 84, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3bf2b6e-3947-4a69-9600-04527ef88ff1", + "name": "Spawner (229)", + "location": [3130, 116, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "821fbdbf-ae39-4073-adb0-17e2e9ee87aa", + "name": "Spawner (229)", + "location": [2082, 60, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39aee7f8-373f-4191-b349-f5c9bb367abd", + "name": "Spawner (229)", + "location": [2930, 3476, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c012739f-6a97-413c-b2bd-249fd2b7e51f", + "name": "Spawner (229)", + "location": [1410, 3796, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4394742a-5799-4d8c-9d29-11753415b5b5", + "name": "Spawner (229)", + "location": [1122, 3524, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c69fc76-ecf7-4282-84c1-9fa5cecda213", + "name": "Spawner (229)", + "location": [2474, 3956, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cdd876a8-1378-4ccd-8f12-9cdac9c3487a", + "name": "Spawner (229)", + "location": [4570, 3340, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f1da2ac-4a31-415d-99c6-76921de812b0", + "name": "Spawner (229)", + "location": [4282, 3700, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Sanctuary.json b/Distribution/Data/Spawns/post-uoml/felucca/Sanctuary.json similarity index 97% rename from Distribution/Data/Spawns/shared/felucca/Sanctuary.json rename to Distribution/Data/Spawns/post-uoml/felucca/Sanctuary.json index 66279bb49..7c9079e14 100644 --- a/Distribution/Data/Spawns/shared/felucca/Sanctuary.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Sanctuary.json @@ -1,6 +1,29 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "1511b1c9-952f-4b34-a527-40c8458fd839", + "name": "Spawner (217)", + "location": [6263, 118, -10], + "map": "Felucca", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Chicken", "maxCount": 6, "probability": 100 }, + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 3, "probability": 100 }, + { "name": "Cat", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 6, "probability": 100 }, + { "name": "Bull", "maxCount": 6, "probability": 100 }, + { "name": "Pig", "maxCount": 3, "probability": 100 }, + { "name": "Boar", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "072beafe-a629-4c48-b5ff-b984254a5d46", "name": "Spawner (217)", "location": [6349, 89, -20], @@ -27,30 +50,110 @@ ] }, { - "$type": "Spawner", - "guid": "1511b1c9-952f-4b34-a527-40c8458fd839", + "type": "Spawner", + "guid": "f96a5381-773a-4b5a-af6a-4cc8d2d08811", "name": "Spawner (217)", - "location": [6263, 118, -10], + "location": [6168, 117, 0], "map": "Felucca", - "count": 24, + "count": 34, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "Chicken", "maxCount": 6, "probability": 100 }, - { "name": "Bird", "maxCount": 6, "probability": 100 }, - { "name": "Dog", "maxCount": 3, "probability": 100 }, - { "name": "Cat", "maxCount": 3, "probability": 100 }, - { "name": "Cow", "maxCount": 6, "probability": 100 }, - { "name": "Bull", "maxCount": 6, "probability": 100 }, - { "name": "Pig", "maxCount": 3, "probability": 100 }, - { "name": "Boar", "maxCount": 3, "probability": 100 } + { "name": "Ratman", "maxCount": 11, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 11, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 11, "probability": 100 }, + { "name": "Changeling", "maxCount": 11, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 11, "probability": 100 }, + { "name": "Dog", "maxCount": 9, "probability": 100 }, + { "name": "Chicken", "maxCount": 9, "probability": 100 }, + { "name": "Cat", "maxCount": 9, "probability": 100 }, + { "name": "Boar", "maxCount": 9, "probability": 100 }, + { "name": "Bird", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Titan", "maxCount": 3, "probability": 100 }, + { "name": "Cyclops", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 5, "probability": 100 }, + { "name": "orcscout", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "a03d9ae7-cc7d-41c7-83ad-fd30bdb1b5be", + "name": "Spawner (217)", + "location": [6189, 71, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "OgreLord", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cow", "maxCount": 4, "probability": 100 }, + { "name": "Bull", "maxCount": 4, "probability": 100 }, + { "name": "Pig", "maxCount": 4, "probability": 100 }, + { "name": "Chicken", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "584c49ef-3604-4c2a-a385-7fca422c604e", + "name": "Spawner (217)", + "location": [6187, 101, 0], + "map": "Felucca", + "count": 34, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Titan", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 10, "probability": 100 }, + { "name": "MougGuur", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Chicken", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 }, + { "name": "Cow", "maxCount": 10, "probability": 100 }, + { "name": "Bull", "maxCount": 10, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 7, "probability": 100 }, + { "name": "OrcBrute", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36e62443-3909-4f76-b267-0ffe2138c841", + "name": "Spawner (217)", + "location": [6184, 29, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Chicken", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "1be8d7aa-7b8d-48b2-a99e-7e8aadda47ae", "name": "Spawner (217)", "location": [6265, 48, -10], @@ -87,80 +190,7 @@ ] }, { - "$type": "Spawner", - "guid": "36e62443-3909-4f76-b267-0ffe2138c841", - "name": "Spawner (217)", - "location": [6184, 29, 0], - "map": "Felucca", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Bird", "maxCount": 6, "probability": 100 }, - { "name": "Chicken", "maxCount": 6, "probability": 100 }, - { "name": "Rat", "maxCount": 6, "probability": 100 }, - { "name": "Dog", "maxCount": 6, "probability": 100 }, - { "name": "Doppleganger", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "584c49ef-3604-4c2a-a385-7fca422c604e", - "name": "Spawner (217)", - "location": [6187, 101, 0], - "map": "Felucca", - "count": 34, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Titan", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Doppleganger", "maxCount": 10, "probability": 100 }, - { "name": "MougGuur", "maxCount": 10, "probability": 100 }, - { "name": "Dog", "maxCount": 10, "probability": 100 }, - { "name": "Chicken", "maxCount": 10, "probability": 100 }, - { "name": "Rat", "maxCount": 10, "probability": 100 }, - { "name": "Cow", "maxCount": 10, "probability": 100 }, - { "name": "Bull", "maxCount": 10, "probability": 100 }, - { "name": "OrcBomber", "maxCount": 7, "probability": 100 }, - { "name": "OrcBrute", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a03d9ae7-cc7d-41c7-83ad-fd30bdb1b5be", - "name": "Spawner (217)", - "location": [6189, 71, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "OgreLord", "maxCount": 8, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Cow", "maxCount": 4, "probability": 100 }, - { "name": "Bull", "maxCount": 4, "probability": 100 }, - { "name": "Pig", "maxCount": 4, "probability": 100 }, - { "name": "Chicken", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a976bf7f-b394-4429-92e9-b3dd47eae839", "name": "Spawner (217)", "location": [6191, 155, 0], @@ -188,35 +218,5 @@ { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, { "name": "Snake", "maxCount": 3, "probability": 100 } ] - }, - { - "$type": "Spawner", - "guid": "f96a5381-773a-4b5a-af6a-4cc8d2d08811", - "name": "Spawner (217)", - "location": [6168, 117, 0], - "map": "Felucca", - "count": 34, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ratman", "maxCount": 11, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 11, "probability": 100 }, - { "name": "RatmanMage", "maxCount": 11, "probability": 100 }, - { "name": "Changeling", "maxCount": 11, "probability": 100 }, - { "name": "Doppleganger", "maxCount": 11, "probability": 100 }, - { "name": "Dog", "maxCount": 9, "probability": 100 }, - { "name": "Chicken", "maxCount": 9, "probability": 100 }, - { "name": "Cat", "maxCount": 9, "probability": 100 }, - { "name": "Boar", "maxCount": 9, "probability": 100 }, - { "name": "Bird", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Titan", "maxCount": 3, "probability": 100 }, - { "name": "Cyclops", "maxCount": 3, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 5, "probability": 100 }, - { "name": "orcscout", "maxCount": 5, "probability": 100 } - ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/SeaLife.json b/Distribution/Data/Spawns/post-uoml/felucca/SeaLife.json similarity index 96% rename from Distribution/Data/Spawns/shared/felucca/SeaLife.json rename to Distribution/Data/Spawns/post-uoml/felucca/SeaLife.json index 9c0983728..e2cd44f2e 100644 --- a/Distribution/Data/Spawns/shared/felucca/SeaLife.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/SeaLife.json @@ -1,552 +1,6 @@ [ { - "$type": "Spawner", - "guid": "0014d520-3474-4486-86ec-00a967fb2a5e", - "name": "Spawner (218)", - "location": [4896, 3215, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0071f2f5-305e-4819-b388-4ffbf15f7fb1", - "name": "Spawner (218)", - "location": [1792, 79, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "01985344-d2d2-4fa8-ab4c-c1e3bd4e1426", - "name": "Spawner (218)", - "location": [4896, 1519, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "01b29f02-2bec-4d96-8d9a-ccb1957acff3", - "name": "Spawner (218)", - "location": [2368, 2799, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "058ccec6-cf0a-4246-97d1-5397c1557502", - "name": "Spawner (218)", - "location": [3568, 3215, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "05f1490c-6e6c-4905-8d78-01f2367c5f58", - "name": "Spawner (218)", - "location": [4307, 3965, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0627e5fe-58d2-432f-9864-9d4bc05e187d", - "name": "Spawner (218)", - "location": [4904, 1127, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "07342ec8-7e6f-4d0c-877f-743f3143d265", - "name": "Spawner (218)", - "location": [3344, 3479, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "07ce9fc5-0714-4583-bd08-329c1997ed4d", - "name": "Spawner (218)", - "location": [147, 3213, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "08ccd285-5416-4099-8edf-371c61bccbd2", - "name": "Spawner (218)", - "location": [4112, 791, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a9ddbf8-0673-47fa-b250-08395ca453af", - "name": "Spawner (218)", - "location": [544, 3719, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0c6c585b-1213-4135-bab3-6f15861f7aa0", - "name": "Spawner (218)", - "location": [4152, 3415, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f18cdfa-c425-4525-9056-54476d4bc98d", - "name": "Spawner (218)", - "location": [4731, 3997, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10d5ae06-e475-4d16-93ca-a1deee44e78e", - "name": "Spawner (218)", - "location": [3560, 3871, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "17574264-babd-4f28-92ed-1fcb3f07ee28", - "name": "Spawner (218)", - "location": [496, 527, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "176e52b8-2ac0-496f-9647-fc42ea72ee8a", - "name": "Spawner (218)", - "location": [592, 223, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1bc905f9-1691-4a2b-afca-738e6b22fc47", - "name": "Spawner (218)", - "location": [4048, 63, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d557f82-a5f6-46f1-b459-fb3096e5852c", - "name": "Spawner (218)", - "location": [840, 2863, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1e652609-1c8b-4f88-bd10-de2445856391", - "name": "Spawner (218)", - "location": [496, 1879, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f070052-489a-4be0-90b1-a4ac42a0c367", - "name": "Spawner (218)", - "location": [3576, 823, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "201a3c66-f161-43f2-a6d0-0ca3597a6678", - "name": "Spawner (218)", - "location": [3968, 3895, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20fdaa4f-d544-4a36-925b-cc0dfb565abc", - "name": "Spawner (218)", - "location": [4008, 2767, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23ad7990-6434-4baa-8ced-32317059b195", - "name": "Spawner (218)", - "location": [208, 2503, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2910bc91-c850-493b-b157-d37b8a4dfde8", - "name": "Spawner (218)", - "location": [4192, 2975, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "296f03e2-d85d-4743-8fd2-0428b9390c89", - "name": "Spawner (218)", - "location": [2971, 1605, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2abdef39-47e2-4145-9dd5-9af7e078d53c", - "name": "Spawner (218)", - "location": [4032, 2039, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "2d7cf932-ce8c-4a83-bd73-6371f3448072", "name": "Spawner (218)", "location": [1395, 3421, -5], @@ -567,133 +21,7 @@ ] }, { - "$type": "Spawner", - "guid": "2f2b2fca-8ffe-4bb5-95d3-94a5ee0b72a8", - "name": "Spawner (218)", - "location": [2608, 3295, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "319bf1d1-606a-4b25-be88-11da80afe4fd", - "name": "Spawner (218)", - "location": [2400, 2399, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33e0f8af-c16e-4dc4-a240-5c0533ff3375", - "name": "Spawner (218)", - "location": [4392, 823, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "372bca4b-5296-48b9-9207-7782d129aa21", - "name": "Spawner (218)", - "location": [2931, 3245, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3867ead7-d062-44a3-a284-ba67c14b2b3b", - "name": "Spawner (218)", - "location": [139, 1173, -5], - "map": "Felucca", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 130, - "walkingRange": 130, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, - { "name": "Kraken", "maxCount": 13, "probability": 100 }, - { "name": "Dolphin", "maxCount": 4, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "395aeb43-6d36-4856-b6f5-19790d555189", - "name": "Spawner (218)", - "location": [896, 3695, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "3dcee48d-ec06-4315-9abc-ef03d4c03e4a", "name": "Spawner (218)", "location": [4888, 375, -5], @@ -714,385 +42,7 @@ ] }, { - "$type": "Spawner", - "guid": "3e3b9e43-1b6c-407b-9c3f-325e87738c26", - "name": "Spawner (218)", - "location": [4864, 2831, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "445d35c5-b9a8-4269-ab27-c8b294156811", - "name": "Spawner (218)", - "location": [3891, 733, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "44c78479-90c2-44fe-92a8-23cb6de892fa", - "name": "Spawner (218)", - "location": [176, 3551, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47efe73c-82a4-4117-b43b-e5d15136c149", - "name": "Spawner (218)", - "location": [3776, 3519, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4de0d475-b6de-4676-ae8c-715b17e0f44a", - "name": "Spawner (218)", - "location": [2952, 1239, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5114e22e-6538-4d21-b276-aa9a00a88a54", - "name": "Spawner (218)", - "location": [1187, 3885, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "52a8d52f-aec5-4ea8-8964-3a769c403cc2", - "name": "Spawner (218)", - "location": [1336, 79, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5491c069-ef4f-4b5c-bc5b-5913c4339f3f", - "name": "Spawner (218)", - "location": [491, 4013, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54d209e5-3a8c-476b-bc51-e3ebb3449c6c", - "name": "Spawner (218)", - "location": [2040, 3751, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54fc0bab-2105-444b-8155-804624544ffd", - "name": "Spawner (218)", - "location": [3992, 1447, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a7be8f4-75a8-495b-9ec7-18ca5a146566", - "name": "Spawner (218)", - "location": [979, 3997, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5b9bde41-61e8-486c-8893-f5de1e36bdf2", - "name": "Spawner (218)", - "location": [3603, 125, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e50039d-cd33-4802-bc94-d472eb3fc521", - "name": "Spawner (218)", - "location": [3256, 1031, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e5967d0-64f1-46b3-a8c9-c85e60e2498f", - "name": "Spawner (218)", - "location": [512, 2919, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5fe352af-61a0-464a-8ded-bd840ddc69c8", - "name": "Spawner (218)", - "location": [3168, 3871, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60abd3c2-eba6-4e07-aae5-1d988ade2d99", - "name": "Spawner (218)", - "location": [4275, 1309, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "61eb0c80-5734-4aaa-8639-26c61907aa70", - "name": "Spawner (218)", - "location": [1866, 1798, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "667c9bca-f47b-4a12-b00e-55db4657cb46", - "name": "Spawner (218)", - "location": [1891, 3597, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "670934bb-5897-4cdd-bbd3-1c90df939a21", "name": "Spawner (218)", "location": [4352, 215, -5], @@ -1113,1036 +63,7 @@ ] }, { - "$type": "Spawner", - "guid": "696ab0e9-99f4-49ca-b79c-4d87f37fa250", - "name": "Spawner (218)", - "location": [4016, 1031, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6c993910-33aa-4227-b37d-e3cdec1ed456", - "name": "Spawner (218)", - "location": [480, 3351, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "760de2a1-81b0-44b0-a936-749b378086cf", - "name": "Spawner (218)", - "location": [3408, 1407, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "79aefaf1-568d-41d2-bed6-bd670ecce526", - "name": "Spawner (218)", - "location": [4624, 2199, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7e4ba525-1583-4aed-a7b7-783f37239a5a", - "name": "Spawner (218)", - "location": [1720, 3879, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "817895c6-8683-41e6-b08d-01a5ff038f94", - "name": "Spawner (218)", - "location": [2320, 4007, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "85204ae4-6b37-4f1e-9490-c081e806e26f", - "name": "Spawner (218)", - "location": [3088, 2759, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8964dcc7-930d-44fb-85a8-f2f85e4fc458", - "name": "Spawner (218)", - "location": [4984, 3567, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "89971651-6235-411e-b939-9d42fa08539a", - "name": "Spawner (218)", - "location": [4896, 2119, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8a301447-cfe7-45e3-b30a-b9573379b108", - "name": "Spawner (218)", - "location": [2291, 3181, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8faaf35e-e1d9-4bf5-8ddb-403d6628dc91", - "name": "Spawner (218)", - "location": [2392, 2031, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8fb38b44-6776-4902-a7ec-ad918c7d58cc", - "name": "Spawner (218)", - "location": [3008, 1895, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8fe15958-527f-4480-98d6-6d8c9e2653cd", - "name": "Spawner (218)", - "location": [4232, 1655, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "956a87d3-17ff-4f04-9165-ac12e0954594", - "name": "Spawner (218)", - "location": [824, 3287, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "96ea0189-bdc2-40de-8660-f07b8f3c6d51", - "name": "Spawner (218)", - "location": [2227, 3533, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9703d8ff-da28-4ea8-9071-d9f9a4c6d836", - "name": "Spawner (218)", - "location": [3616, 1807, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b0dcb48-2f80-4503-8f25-ddbc29221473", - "name": "Spawner (218)", - "location": [3984, 2423, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9be3514d-643b-4008-b316-aa0f47902fe8", - "name": "Spawner (218)", - "location": [2744, 2567, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a3c1a4b2-df5b-4492-a0c5-0e9e5a0c399f", - "name": "Spawner (218)", - "location": [4912, 2471, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a408764b-0975-45e6-87dc-031e05747605", - "name": "Spawner (218)", - "location": [4995, 1829, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a4a476f6-ad25-4619-a34e-285bffea1f55", - "name": "Spawner (218)", - "location": [3520, 2263, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a5a6b13c-50d2-4aa4-ba70-9b2abb9841b6", - "name": "Spawner (218)", - "location": [931, 141, -5], - "map": "Felucca", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 130, - "walkingRange": 130, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, - { "name": "Kraken", "maxCount": 13, "probability": 100 }, - { "name": "Dolphin", "maxCount": 4, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a798b4a7-f027-4e9f-86d0-a93f721ee033", - "name": "Spawner (218)", - "location": [2208, 1703, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a88ec077-c435-47e0-92b3-548e27f2e603", - "name": "Spawner (218)", - "location": [4363, 1981, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8e4212a-a3d5-4e62-937a-3118350ea07d", - "name": "Spawner (218)", - "location": [731, 3989, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa54f319-d255-4aea-b782-d505cce89754", - "name": "Spawner (218)", - "location": [3328, 2063, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "add4463a-f94f-45b8-8504-98007a319caf", - "name": "Spawner (218)", - "location": [2072, 2527, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b096bc10-2a6c-42a6-bd3c-1835e6ba57e3", - "name": "Spawner (218)", - "location": [3176, 2383, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3e6d001-7ec1-48c0-a243-df5a177d56a7", - "name": "Spawner (218)", - "location": [4283, 2221, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b90733e7-882e-42a3-ab1c-852611eec585", - "name": "Spawner (218)", - "location": [4600, 1751, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bdd432ee-e61f-4df2-ab8b-d1296838dd90", - "name": "Spawner (218)", - "location": [2008, 4007, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bea968d2-ac27-494b-b59f-a3ed10e48402", - "name": "Spawner (218)", - "location": [200, 3879, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c02a7e0f-a448-45c3-b8e8-532f0441089d", - "name": "Spawner (218)", - "location": [3267, 1717, -5], - "map": "Felucca", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 130, - "walkingRange": 130, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, - { "name": "Kraken", "maxCount": 13, "probability": 100 }, - { "name": "Dolphin", "maxCount": 4, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0738ba5-30cc-4819-9f84-3df391ddb250", - "name": "Spawner (218)", - "location": [107, 909, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c3938e64-993f-45c6-886e-dcdde4e43ee9", - "name": "Spawner (218)", - "location": [600, 2511, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c86a16f5-01d1-4a6b-8967-6e35ba6d4f30", - "name": "Spawner (218)", - "location": [192, 2895, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd863ac3-ff02-4719-a235-9f8403f1f6b9", - "name": "Spawner (218)", - "location": [4400, 2527, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d01dce21-9d3a-4b51-85ce-e4589e03776f", - "name": "Spawner (218)", - "location": [200, 2103, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d2bdb1a3-d96b-4a44-95e7-099f39ff38cc", - "name": "Spawner (218)", - "location": [2395, 1405, -5], - "map": "Felucca", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 130, - "walkingRange": 130, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, - { "name": "Kraken", "maxCount": 13, "probability": 100 }, - { "name": "Dolphin", "maxCount": 4, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d45f2c8d-d661-4a91-bc0a-b04f901b64b2", - "name": "Spawner (218)", - "location": [5003, 101, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d8eb15e7-f507-4850-a1fd-9bc9f1ce1e07", - "name": "Spawner (218)", - "location": [2768, 3847, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d98fd38f-c4f3-4933-9ea3-53a81c886e8f", - "name": "Spawner (218)", - "location": [3888, 3167, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da152e88-97ad-4c9e-b27d-c4d4385e8455", - "name": "Spawner (218)", - "location": [3931, 1757, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df48afce-46f8-4d69-bae7-b843e1185554", - "name": "Spawner (218)", - "location": [2752, 1455, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df7a4d12-8fbd-4547-8f92-bb4dfc720ac2", - "name": "Spawner (218)", - "location": [3811, 109, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e131f5dc-1b9d-480e-b759-85af56822993", - "name": "Spawner (218)", - "location": [3123, 1485, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e19fdd70-0246-43dd-bde0-a65e5324f684", - "name": "Spawner (218)", - "location": [200, 559, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e35061d7-cfbd-4383-b75b-065c37982be4", - "name": "Spawner (218)", - "location": [3672, 1535, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e5bd5536-fac7-43f2-a074-01d548139c56", - "name": "Spawner (218)", - "location": [184, 191, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e602a618-789c-4b14-84d6-fc4eba7fff99", "name": "Spawner (218)", "location": [3688, 439, -5], @@ -2163,7 +84,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e6115838-1fe9-4d50-845e-758c36abdccb", "name": "Spawner (218)", "location": [4512, 583, -5], @@ -2184,112 +105,7 @@ ] }, { - "$type": "Spawner", - "guid": "e6f4428d-db11-4d83-837f-4b92a3bee9e8", - "name": "Spawner (218)", - "location": [2792, 2951, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec7aedaf-b518-4f7e-9d05-b864db54eac2", - "name": "Spawner (218)", - "location": [4955, 3997, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec9b98b4-d409-4aaf-823c-a5cf9503f461", - "name": "Spawner (218)", - "location": [2624, 1775, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ef767a86-1b77-4ddb-a52b-d4c6599b42c0", - "name": "Spawner (218)", - "location": [168, 1743, -5], - "map": "Felucca", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f61ab42b-c23e-462f-980d-ea4d236f4796", - "name": "Spawner (218)", - "location": [4667, 109, -5], - "map": "Felucca", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "fac96160-fbae-4833-8946-e315521ffb8e", "name": "Spawner (218)", "location": [4904, 759, -5], @@ -2310,10 +126,10 @@ ] }, { - "$type": "Spawner", - "guid": "fb98801b-da33-4aab-b21a-8f4f0df5ee8c", + "type": "Spawner", + "guid": "0627e5fe-58d2-432f-9864-9d4bc05e187d", "name": "Spawner (218)", - "location": [3240, 3151, -5], + "location": [4904, 1127, -5], "map": "Felucca", "count": 22, "minDelay": "00:05:00", @@ -2331,49 +147,301 @@ ] }, { - "$type": "Spawner", - "guid": "fc7ef1ce-76f7-4239-b044-7f30a4c32a42", + "type": "Spawner", + "guid": "1f070052-489a-4be0-90b1-a4ac42a0c367", "name": "Spawner (218)", - "location": [1580, 75, -5], + "location": [3576, 823, -5], "map": "Felucca", - "count": 13, + "count": 22, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 100, - "walkingRange": 100, + "homeRange": 175, + "walkingRange": 175, "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, { "name": "SeaHorse", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fd9175ae-f576-4ffd-bae8-2710ae3e07e1", + "type": "Spawner", + "guid": "696ab0e9-99f4-49ca-b79c-4d87f37fa250", "name": "Spawner (218)", - "location": [2288, 3767, -5], + "location": [4016, 1031, -5], "map": "Felucca", - "count": 13, + "count": 22, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 100, - "walkingRange": 100, + "homeRange": 175, + "walkingRange": 175, "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, { "name": "SeaHorse", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "54fc0bab-2105-444b-8155-804624544ffd", + "name": "Spawner (218)", + "location": [3992, 1447, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8fe15958-527f-4480-98d6-6d8c9e2653cd", + "name": "Spawner (218)", + "location": [4232, 1655, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b90733e7-882e-42a3-ab1c-852611eec585", + "name": "Spawner (218)", + "location": [4600, 1751, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01985344-d2d2-4fa8-ab4c-c1e3bd4e1426", + "name": "Spawner (218)", + "location": [4896, 1519, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9703d8ff-da28-4ea8-9071-d9f9a4c6d836", + "name": "Spawner (218)", + "location": [3616, 1807, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2abdef39-47e2-4145-9dd5-9af7e078d53c", + "name": "Spawner (218)", + "location": [4032, 2039, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79aefaf1-568d-41d2-bed6-bd670ecce526", + "name": "Spawner (218)", + "location": [4624, 2199, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd863ac3-ff02-4719-a235-9f8403f1f6b9", + "name": "Spawner (218)", + "location": [4400, 2527, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b0dcb48-2f80-4503-8f25-ddbc29221473", + "name": "Spawner (218)", + "location": [3984, 2423, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "89971651-6235-411e-b939-9d42fa08539a", + "name": "Spawner (218)", + "location": [4896, 2119, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a3c1a4b2-df5b-4492-a0c5-0e9e5a0c399f", + "name": "Spawner (218)", + "location": [4912, 2471, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e3b9e43-1b6c-407b-9c3f-325e87738c26", + "name": "Spawner (218)", + "location": [4864, 2831, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fe2bf6f9-ab40-4e8d-b7e9-ef01688462cb", "name": "Spawner (218)", "location": [4536, 2847, -5], @@ -2394,7 +462,1834 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "0014d520-3474-4486-86ec-00a967fb2a5e", + "name": "Spawner (218)", + "location": [4896, 3215, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2910bc91-c850-493b-b157-d37b8a4dfde8", + "name": "Spawner (218)", + "location": [4192, 2975, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20fdaa4f-d544-4a36-925b-cc0dfb565abc", + "name": "Spawner (218)", + "location": [4008, 2767, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa54f319-d255-4aea-b782-d505cce89754", + "name": "Spawner (218)", + "location": [3328, 2063, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b096bc10-2a6c-42a6-bd3c-1835e6ba57e3", + "name": "Spawner (218)", + "location": [3176, 2383, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85204ae4-6b37-4f1e-9490-c081e806e26f", + "name": "Spawner (218)", + "location": [3088, 2759, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d98fd38f-c4f3-4933-9ea3-53a81c886e8f", + "name": "Spawner (218)", + "location": [3888, 3167, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c6c585b-1213-4135-bab3-6f15861f7aa0", + "name": "Spawner (218)", + "location": [4152, 3415, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "201a3c66-f161-43f2-a6d0-0ca3597a6678", + "name": "Spawner (218)", + "location": [3968, 3895, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb98801b-da33-4aab-b21a-8f4f0df5ee8c", + "name": "Spawner (218)", + "location": [3240, 3151, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47efe73c-82a4-4117-b43b-e5d15136c149", + "name": "Spawner (218)", + "location": [3776, 3519, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07342ec8-7e6f-4d0c-877f-743f3143d265", + "name": "Spawner (218)", + "location": [3344, 3479, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6f4428d-db11-4d83-837f-4b92a3bee9e8", + "name": "Spawner (218)", + "location": [2792, 2951, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f2b2fca-8ffe-4bb5-95d3-94a5ee0b72a8", + "name": "Spawner (218)", + "location": [2608, 3295, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9be3514d-643b-4008-b316-aa0f47902fe8", + "name": "Spawner (218)", + "location": [2744, 2567, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01b29f02-2bec-4d96-8d9a-ccb1957acff3", + "name": "Spawner (218)", + "location": [2368, 2799, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10d5ae06-e475-4d16-93ca-a1deee44e78e", + "name": "Spawner (218)", + "location": [3560, 3871, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5fe352af-61a0-464a-8ded-bd840ddc69c8", + "name": "Spawner (218)", + "location": [3168, 3871, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8eb15e7-f507-4850-a1fd-9bc9f1ce1e07", + "name": "Spawner (218)", + "location": [2768, 3847, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e4ba525-1583-4aed-a7b7-783f37239a5a", + "name": "Spawner (218)", + "location": [1720, 3879, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bea968d2-ac27-494b-b59f-a3ed10e48402", + "name": "Spawner (218)", + "location": [200, 3879, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a9ddbf8-0673-47fa-b250-08395ca453af", + "name": "Spawner (218)", + "location": [544, 3719, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "395aeb43-6d36-4856-b6f5-19790d555189", + "name": "Spawner (218)", + "location": [896, 3695, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "956a87d3-17ff-4f04-9165-ac12e0954594", + "name": "Spawner (218)", + "location": [824, 3287, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "44c78479-90c2-44fe-92a8-23cb6de892fa", + "name": "Spawner (218)", + "location": [176, 3551, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c993910-33aa-4227-b37d-e3cdec1ed456", + "name": "Spawner (218)", + "location": [480, 3351, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e5967d0-64f1-46b3-a8c9-c85e60e2498f", + "name": "Spawner (218)", + "location": [512, 2919, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c86a16f5-01d1-4a6b-8967-6e35ba6d4f30", + "name": "Spawner (218)", + "location": [192, 2895, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23ad7990-6434-4baa-8ced-32317059b195", + "name": "Spawner (218)", + "location": [208, 2503, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d557f82-a5f6-46f1-b459-fb3096e5852c", + "name": "Spawner (218)", + "location": [840, 2863, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3938e64-993f-45c6-886e-dcdde4e43ee9", + "name": "Spawner (218)", + "location": [600, 2511, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d01dce21-9d3a-4b51-85ce-e4589e03776f", + "name": "Spawner (218)", + "location": [200, 2103, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef767a86-1b77-4ddb-a52b-d4c6599b42c0", + "name": "Spawner (218)", + "location": [168, 1743, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e19fdd70-0246-43dd-bde0-a65e5324f684", + "name": "Spawner (218)", + "location": [200, 559, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5bd5536-fac7-43f2-a074-01d548139c56", + "name": "Spawner (218)", + "location": [184, 191, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "176e52b8-2ac0-496f-9647-fc42ea72ee8a", + "name": "Spawner (218)", + "location": [592, 223, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "17574264-babd-4f28-92ed-1fcb3f07ee28", + "name": "Spawner (218)", + "location": [496, 527, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "319bf1d1-606a-4b25-be88-11da80afe4fd", + "name": "Spawner (218)", + "location": [2400, 2399, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8fb38b44-6776-4902-a7ec-ad918c7d58cc", + "name": "Spawner (218)", + "location": [3008, 1895, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8faaf35e-e1d9-4bf5-8ddb-403d6628dc91", + "name": "Spawner (218)", + "location": [2392, 2031, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec9b98b4-d409-4aaf-823c-a5cf9503f461", + "name": "Spawner (218)", + "location": [2624, 1775, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "760de2a1-81b0-44b0-a936-749b378086cf", + "name": "Spawner (218)", + "location": [3408, 1407, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e50039d-cd33-4802-bc94-d472eb3fc521", + "name": "Spawner (218)", + "location": [3256, 1031, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4de0d475-b6de-4676-ae8c-715b17e0f44a", + "name": "Spawner (218)", + "location": [2952, 1239, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df48afce-46f8-4d69-bae7-b843e1185554", + "name": "Spawner (218)", + "location": [2752, 1455, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a798b4a7-f027-4e9f-86d0-a93f721ee033", + "name": "Spawner (218)", + "location": [2208, 1703, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61eb0c80-5734-4aaa-8639-26c61907aa70", + "name": "Spawner (218)", + "location": [1866, 1798, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5a6b13c-50d2-4aa4-ba70-9b2abb9841b6", + "name": "Spawner (218)", + "location": [931, 141, -5], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c02a7e0f-a448-45c3-b8e8-532f0441089d", + "name": "Spawner (218)", + "location": [3267, 1717, -5], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2bdb1a3-d96b-4a44-95e7-099f39ff38cc", + "name": "Spawner (218)", + "location": [2395, 1405, -5], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3867ead7-d062-44a3-a284-ba67c14b2b3b", + "name": "Spawner (218)", + "location": [139, 1173, -5], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e131f5dc-1b9d-480e-b759-85af56822993", + "name": "Spawner (218)", + "location": [3123, 1485, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df7a4d12-8fbd-4547-8f92-bb4dfc720ac2", + "name": "Spawner (218)", + "location": [3811, 109, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da152e88-97ad-4c9e-b27d-c4d4385e8455", + "name": "Spawner (218)", + "location": [3931, 1757, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "372bca4b-5296-48b9-9207-7782d129aa21", + "name": "Spawner (218)", + "location": [2931, 3245, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a301447-cfe7-45e3-b30a-b9573379b108", + "name": "Spawner (218)", + "location": [2291, 3181, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a7be8f4-75a8-495b-9ec7-18ca5a146566", + "name": "Spawner (218)", + "location": [979, 3997, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07ce9fc5-0714-4583-bd08-329c1997ed4d", + "name": "Spawner (218)", + "location": [147, 3213, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "667c9bca-f47b-4a12-b00e-55db4657cb46", + "name": "Spawner (218)", + "location": [1891, 3597, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "296f03e2-d85d-4743-8fd2-0428b9390c89", + "name": "Spawner (218)", + "location": [2971, 1605, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fc7ef1ce-76f7-4239-b044-7f30a4c32a42", + "name": "Spawner (218)", + "location": [1580, 75, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60abd3c2-eba6-4e07-aae5-1d988ade2d99", + "name": "Spawner (218)", + "location": [4275, 1309, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f61ab42b-c23e-462f-980d-ea4d236f4796", + "name": "Spawner (218)", + "location": [4667, 109, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d45f2c8d-d661-4a91-bc0a-b04f901b64b2", + "name": "Spawner (218)", + "location": [5003, 101, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b9bde41-61e8-486c-8893-f5de1e36bdf2", + "name": "Spawner (218)", + "location": [3603, 125, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08ccd285-5416-4099-8edf-371c61bccbd2", + "name": "Spawner (218)", + "location": [4112, 791, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "445d35c5-b9a8-4269-ab27-c8b294156811", + "name": "Spawner (218)", + "location": [3891, 733, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e35061d7-cfbd-4383-b75b-065c37982be4", + "name": "Spawner (218)", + "location": [3672, 1535, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a88ec077-c435-47e0-92b3-548e27f2e603", + "name": "Spawner (218)", + "location": [4363, 1981, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a408764b-0975-45e6-87dc-031e05747605", + "name": "Spawner (218)", + "location": [4995, 1829, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3e6d001-7ec1-48c0-a243-df5a177d56a7", + "name": "Spawner (218)", + "location": [4283, 2221, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdd432ee-e61f-4df2-ab8b-d1296838dd90", + "name": "Spawner (218)", + "location": [2008, 4007, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "817895c6-8683-41e6-b08d-01a5ff038f94", + "name": "Spawner (218)", + "location": [2320, 4007, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec7aedaf-b518-4f7e-9d05-b864db54eac2", + "name": "Spawner (218)", + "location": [4955, 3997, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f18cdfa-c425-4525-9056-54476d4bc98d", + "name": "Spawner (218)", + "location": [4731, 3997, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05f1490c-6e6c-4905-8d78-01f2367c5f58", + "name": "Spawner (218)", + "location": [4307, 3965, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96ea0189-bdc2-40de-8660-f07b8f3c6d51", + "name": "Spawner (218)", + "location": [2227, 3533, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5114e22e-6538-4d21-b276-aa9a00a88a54", + "name": "Spawner (218)", + "location": [1187, 3885, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8e4212a-a3d5-4e62-937a-3118350ea07d", + "name": "Spawner (218)", + "location": [731, 3989, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5491c069-ef4f-4b5c-bc5b-5913c4339f3f", + "name": "Spawner (218)", + "location": [491, 4013, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0738ba5-30cc-4819-9f84-3df391ddb250", + "name": "Spawner (218)", + "location": [107, 909, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0071f2f5-305e-4819-b388-4ffbf15f7fb1", + "name": "Spawner (218)", + "location": [1792, 79, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "52a8d52f-aec5-4ea8-8964-3a769c403cc2", + "name": "Spawner (218)", + "location": [1336, 79, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1bc905f9-1691-4a2b-afca-738e6b22fc47", + "name": "Spawner (218)", + "location": [4048, 63, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33e0f8af-c16e-4dc4-a240-5c0533ff3375", + "name": "Spawner (218)", + "location": [4392, 823, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a4a476f6-ad25-4619-a34e-285bffea1f55", + "name": "Spawner (218)", + "location": [3520, 2263, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "058ccec6-cf0a-4246-97d1-5397c1557502", + "name": "Spawner (218)", + "location": [3568, 3215, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fec6b06d-982d-45b5-aa46-a6334f5ece5e", "name": "Spawner (218)", "location": [4992, 3791, -5], @@ -2413,5 +2308,110 @@ { "name": "Dolphin", "maxCount": 2, "probability": 100 }, { "name": "SeaHorse", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "8964dcc7-930d-44fb-85a8-f2f85e4fc458", + "name": "Spawner (218)", + "location": [4984, 3567, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "add4463a-f94f-45b8-8504-98007a319caf", + "name": "Spawner (218)", + "location": [2072, 2527, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54d209e5-3a8c-476b-bc51-e3ebb3449c6c", + "name": "Spawner (218)", + "location": [2040, 3751, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd9175ae-f576-4ffd-bae8-2710ae3e07e1", + "name": "Spawner (218)", + "location": [2288, 3767, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1e652609-1c8b-4f88-bd10-de2445856391", + "name": "Spawner (218)", + "location": [496, 1879, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/Shame.json b/Distribution/Data/Spawns/post-uoml/felucca/Shame.json similarity index 76% rename from Distribution/Data/Spawns/shared/felucca/Shame.json rename to Distribution/Data/Spawns/post-uoml/felucca/Shame.json index a820332a0..64d1847f4 100644 --- a/Distribution/Data/Spawns/shared/felucca/Shame.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Shame.json @@ -1,1100 +1,6 @@ [ { - "$type": "Spawner", - "guid": "00b4ffd2-c3fe-42e0-b913-9dc7f244bad1", - "name": "Spawner (219)", - "location": [5595, 147, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "06a27dea-d6cf-4668-aaf5-11b92f37dac2", - "name": "Spawner (219)", - "location": [5403, 235, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "099f8534-bad4-4baa-a701-6f52e856b767", - "name": "Spawner (219)", - "location": [5615, 18, 10], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "DullCopperElemental", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0acf386b-aab2-42da-9852-bbab5e17235c", - "name": "Spawner (219)", - "location": [5649, 101, 10], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0b0f079f-a068-4b35-9c6a-13a2a0eefa41", - "name": "Spawner (219)", - "location": [5394, 230, 10], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11f83b98-6f66-43cd-bc2d-c8435a753629", - "name": "Spawner (219)", - "location": [5390, 145, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1333be8f-f542-4131-a842-f5419ea6690b", - "name": "Spawner (219)", - "location": [5405, 200, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "13410a04-955c-4e3b-b88a-c59e17ec343c", - "name": "Spawner (219)", - "location": [5708, 43, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "16036a13-721a-447b-ae6b-a5df60d9c9ad", - "name": "Spawner (219)", - "location": [5717, 60, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "17349861-3ab6-4103-bd27-07d40be823d0", - "name": "Spawner (219)", - "location": [5817, 83, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1bb3ab96-96dd-47df-8e4b-c788960b039f", - "name": "Spawner (219)", - "location": [5817, 78, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1c97ea8d-413a-4925-8507-6c004a16ccd6", - "name": "Spawner (219)", - "location": [5536, 119, -5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2284227d-bd9b-4b23-99c7-e96eaeacb970", - "name": "Spawner (219)", - "location": [5553, 146, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22b4ddba-b4dc-46ba-b8ca-ee5c6ab5538b", - "name": "Spawner (219)", - "location": [5445, 12, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "24bb9e8a-3124-4984-89be-39ff6cdd3e34", - "name": "Spawner (219)", - "location": [5615, 173, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "25a4f4e6-8d5b-4e50-87c5-65961ef02649", - "name": "Spawner (219)", - "location": [5412, 200, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, - { "name": "Scorpion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "27ef1488-f84c-4729-a3fc-6ad074a90495", - "name": "Spawner (219)", - "location": [5557, 222, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 2, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "28a61b6b-d21f-4610-aaf2-98f0714ee469", - "name": "Spawner (219)", - "location": [5412, 164, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "29921605-0cc3-4a0f-8fcb-d5088c40eb8b", - "name": "Spawner (219)", - "location": [5412, 201, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e200a23-7f5e-4b50-b15b-72543331cbdd", - "name": "Spawner (219)", - "location": [5733, 91, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e980b6d-486f-45a7-ad54-477a816dda3a", - "name": "Spawner (219)", - "location": [5527, 210, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "DullCopperElemental", "maxCount": 4, "probability": 100 }, - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33b7379d-d169-43b4-a75a-2c550fb45d2e", - "name": "Spawner (219)", - "location": [5711, 97, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "340a9686-1ac8-47ea-9052-31167add0da8", - "name": "Spawner (219)", - "location": [5605, 202, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3498612c-357d-42f3-a1fc-89ee565569d2", - "name": "Spawner (219)", - "location": [5437, 11, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "36e8b3e2-c177-4b5f-a9b8-e21ac99d93e5", - "name": "Spawner (219)", - "location": [5572, 113, 5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37cbc800-73b6-4348-a6ed-f19dea8ff7f7", - "name": "Spawner (219)", - "location": [5549, 60, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "38198440-cbb9-4ddb-8808-59977415fa28", - "name": "Spawner (219)", - "location": [5717, 58, 5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3962bad0-4c7f-4fed-b4f2-e69020569bcc", - "name": "Spawner (219)", - "location": [5410, 170, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3986c149-5d04-4234-9194-25d9ad0daeef", - "name": "Spawner (219)", - "location": [5698, 57, 5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a545713-a7cb-4e80-b41f-9eccfb8d5f35", - "name": "Spawner (219)", - "location": [5730, 90, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3bc0b686-d31e-4ed6-b1bc-6236301c0d04", - "name": "Spawner (219)", - "location": [5661, 16, -10], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3cf4908c-dbae-4cb8-b48f-fdf387ecc501", - "name": "Spawner (219)", - "location": [5571, 158, -10], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3f6e1f05-7dcb-4952-8342-2c6386f057e5", - "name": "Spawner (219)", - "location": [5468, 109, 35], - "map": "Felucca", - "count": 26, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, - { "name": "Scorpion", "maxCount": 20, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49a30e76-7efb-42df-a85b-f40fd1546cee", - "name": "Spawner (219)", - "location": [5448, 33, -10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49dbe727-8641-49ac-8372-a3f2f8fa5cf7", - "name": "Spawner (219)", - "location": [5608, 190, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4a851206-2957-4090-931d-54ed78cea448", - "name": "Spawner (219)", - "location": [5474, 139, 20], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b71965f-2829-4d76-8b31-9c6d55aa7b11", - "name": "Spawner (219)", - "location": [5545, 153, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4bbcb759-e08d-45a9-b7c2-76855e283b0f", - "name": "Spawner (219)", - "location": [5456, 143, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4be1fbed-a047-4092-9ed8-e5ee308123de", - "name": "Spawner (219)", - "location": [5406, 177, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4fe7e9e3-3d65-433f-9c20-584fa7647e12", - "name": "Spawner (219)", - "location": [5439, 137, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5036ca70-e3f4-4e5e-989e-cddf68363ace", - "name": "Spawner (219)", - "location": [5595, 82, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5073e41f-2bd8-4e2b-a73a-e7ecf76a92c5", - "name": "Spawner (219)", - "location": [5568, 36, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54cfd78d-c062-45de-9691-fc08b8225353", - "name": "Spawner (219)", - "location": [5425, 58, 10], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 7, "probability": 100 }, - { "name": "Scorpion", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "56bcaa4f-deef-4ca3-983e-461ab73c81ca", - "name": "Spawner (219)", - "location": [5818, 80, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "ElderGazer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "56eb182b-e842-46da-b44c-cee5290561ac", - "name": "Spawner (219)", - "location": [5684, 117, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "BloodElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "584599e2-fdf8-423b-b7b7-9e6e2cdcf45b", - "name": "Spawner (219)", - "location": [5723, 77, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a80cf5e-c29f-46d5-83e4-c622fbeba8ad", - "name": "Spawner (219)", - "location": [5441, 187, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 5426, "y": 172, "z": 0 }, - "end": { "x": 5456, "y": 202, "z": 43 } - }, - "walkingRange": 30, - "entries": [ - { "name": "EvilMage", "maxCount": 5, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5cf37618-77d8-450d-b847-3ee1f14efcc1", - "name": "Spawner (219)", - "location": [5546, 118, -5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d5036ac-c3f2-48f0-8183-daca945d0523", - "name": "Spawner (219)", - "location": [5398, 148, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "61800760-7e33-48a1-970a-5c1c973e7398", - "name": "Spawner (219)", - "location": [5468, 106, 35], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "62098bc9-596a-4f31-80a0-74190ea65a91", - "name": "Spawner (219)", - "location": [5405, 185, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "622a0111-2226-43de-bea6-a6e50b1a1a67", - "name": "Spawner (219)", - "location": [5730, 93, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "63e94588-f42b-42a6-a046-a9d4fe7a62b8", - "name": "Spawner (219)", - "location": [5701, 60, 5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "663e5aa7-2204-42a6-8b47-b4a5cdd8382e", - "name": "Spawner (219)", - "location": [5492, 56, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6786e744-3a8a-4d00-8bec-213325b489b6", - "name": "Spawner (219)", - "location": [5715, 61, 5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "694e60e0-1db8-457f-b48c-d11372471f5f", - "name": "Spawner (219)", - "location": [5828, 42, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b367539-626c-44b0-a97e-8795ee947c6e", - "name": "Spawner (219)", - "location": [5616, 184, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "70127b13-674d-4def-86fe-bc5b90a6e88e", - "name": "Spawner (219)", - "location": [5723, 73, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72cfdb5a-89de-4277-9de9-66e2c36e48fc", - "name": "Spawner (219)", - "location": [5392, 10, 30], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72d5ae48-41aa-478e-b063-23a3840b3255", - "name": "Spawner (219)", - "location": [5421, 117, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "746760c5-2c14-4d2c-80fe-9c1afe9a2692", - "name": "Spawner (219)", - "location": [5603, 116, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "DullCopperElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7540f782-bcf5-4a5f-b2e6-ba385859a340", - "name": "Spawner (219)", - "location": [5505, 180, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "ElderGazer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7aaac8e3-5868-41ec-b4ae-fea008e39ba5", - "name": "Spawner (219)", - "location": [5575, 16, 10], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b10ba4a-f6a2-4164-827a-c7f98da29341", - "name": "Spawner (219)", - "location": [5477, 183, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7f5f240f-d211-412b-b76d-eec1723c79fd", - "name": "Spawner (219)", - "location": [5474, 20, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "811e333f-7aa9-481f-9edf-8c61e6f984a5", - "name": "Spawner (219)", - "location": [5493, 51, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "86dc2f0d-baf0-4acc-8b9f-62a1bbbcca31", - "name": "Spawner (219)", - "location": [5593, 141, 10], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "8d894292-aaba-4fd7-834a-68aea0d50e0a", "name": "Spawner (219)", "location": [5404, 93, 10], @@ -1111,10 +17,106 @@ ] }, { - "$type": "Spawner", - "guid": "90a2b066-3a4d-4627-bcd8-649434051203", + "type": "Spawner", + "guid": "54cfd78d-c062-45de-9691-fc08b8225353", "name": "Spawner (219)", - "location": [5594, 139, 10], + "location": [5425, 58, 10], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 7, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6921911-2c96-4f33-96e9-d40efa57561c", + "name": "Spawner (219)", + "location": [5387, 41, 20], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2a61736-5a76-4421-8178-deea7aabc615", + "name": "Spawner (219)", + "location": [5394, 11, 30], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7f5f240f-d211-412b-b76d-eec1723c79fd", + "name": "Spawner (219)", + "location": [5474, 20, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b907c1ba-e6aa-4ec8-81a2-8ebc40131a7a", + "name": "Spawner (219)", + "location": [5477, 67, 20], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3f6e1f05-7dcb-4952-8342-2c6386f057e5", + "name": "Spawner (219)", + "location": [5468, 109, 35], + "map": "Felucca", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, + { "name": "Scorpion", "maxCount": 20, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f21c56d6-5653-4bec-95de-58b70a981ff0", + "name": "Spawner (219)", + "location": [5423, 112, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1122,15 +124,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "91118d48-78d5-4fce-a97d-bd662a0bb903", + "type": "Spawner", + "guid": "61800760-7e33-48a1-970a-5c1c973e7398", "name": "Spawner (219)", - "location": [5715, 58, 2], + "location": [5468, 106, 35], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1138,15 +138,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "9303d7b0-a873-47f4-b4e3-1d70d547e4b1", + "type": "Spawner", + "guid": "b283b315-485a-422a-a822-446e200a0080", "name": "Spawner (219)", - "location": [5443, 36, -10], + "location": [5434, 100, 20], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1154,12 +152,80 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "663e5aa7-2204-42a6-8b47-b4a5cdd8382e", + "name": "Spawner (219)", + "location": [5492, 56, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b27e56f7-9157-4b01-8ddd-0aec26e185df", + "name": "Spawner (219)", + "location": [5416, 59, -15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2eb38b8-d316-4f83-95f5-4c5dc6b43113", + "name": "Spawner (219)", + "location": [5400, 45, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49a30e76-7efb-42df-a85b-f40fd1546cee", + "name": "Spawner (219)", + "location": [5448, 33, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3498612c-357d-42f3-a1fc-89ee565569d2", + "name": "Spawner (219)", + "location": [5437, 11, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "9a246364-18c8-46c3-9fac-17025f9ab620", "name": "Spawner (219)", "location": [5399, 11, 30], @@ -1170,15 +236,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "9add65b7-3729-4ab6-be5f-53fc13b3fbcb", + "type": "Spawner", + "guid": "72d5ae48-41aa-478e-b063-23a3840b3255", "name": "Spawner (219)", - "location": [5475, 190, 0], + "location": [5421, 117, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1186,12 +250,285 @@ "team": 0, "homeRange": 1, "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ba178ecf-0529-4540-9edd-710d6deb748b", + "name": "Spawner (219)", + "location": [5468, 100, 35], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72cfdb5a-89de-4277-9de9-66e2c36e48fc", + "name": "Spawner (219)", + "location": [5392, 10, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "22b4ddba-b4dc-46ba-b8ca-ee5c6ab5538b", + "name": "Spawner (219)", + "location": [5445, 12, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cad7fb39-f2aa-4f55-9899-64967860d07e", + "name": "Spawner (219)", + "location": [5404, 43, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9303d7b0-a873-47f4-b4e3-1d70d547e4b1", + "name": "Spawner (219)", + "location": [5443, 36, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ddce5aba-7b2b-4d1f-b055-d242db0509c1", + "name": "Spawner (219)", + "location": [5402, 58, -20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "811e333f-7aa9-481f-9edf-8c61e6f984a5", + "name": "Spawner (219)", + "location": [5493, 51, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b9c97892-db29-41e5-9478-0f6d9e28e8f7", + "name": "Spawner (219)", + "location": [5434, 89, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7aaac8e3-5868-41ec-b4ae-fea008e39ba5", + "name": "Spawner (219)", + "location": [5575, 16, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "099f8534-bad4-4baa-a701-6f52e856b767", + "name": "Spawner (219)", + "location": [5615, 18, 10], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "DullCopperElemental", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5073e41f-2bd8-4e2b-a73a-e7ecf76a92c5", + "name": "Spawner (219)", + "location": [5568, 36, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "37cbc800-73b6-4348-a6ed-f19dea8ff7f7", + "name": "Spawner (219)", + "location": [5549, 60, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "d1eed89e-ef04-4d60-812d-fea412921d76", + "name": "Spawner (219)", + "location": [5549, 79, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5036ca70-e3f4-4e5e-989e-cddf68363ace", + "name": "Spawner (219)", + "location": [5595, 82, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d1898122-15d6-4523-a6e6-84ae0cda8c6a", + "name": "Spawner (219)", + "location": [5603, 93, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "DullCopperElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "746760c5-2c14-4d2c-80fe-9c1afe9a2692", + "name": "Spawner (219)", + "location": [5603, 116, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "DullCopperElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1c97ea8d-413a-4925-8507-6c004a16ccd6", + "name": "Spawner (219)", + "location": [5536, 119, -5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5cf37618-77d8-450d-b847-3ee1f14efcc1", + "name": "Spawner (219)", + "location": [5546, 118, -5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a3d232dd-36fe-40f3-90b3-8e40d23c75e5", + "name": "Spawner (219)", + "location": [5606, 25, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "9b58940a-490b-4c30-8b87-69ff988cacd6", "name": "Spawner (219)", "location": [5570, 118, 5], @@ -1202,15 +539,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "a01e8912-3b82-4214-a8b7-13c598cb23e0", + "type": "Spawner", + "guid": "a7f36503-7139-4ef4-adef-0cc2889d0e90", "name": "Spawner (219)", - "location": [5701, 58, 2], + "location": [5620, 21, 10], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1218,12 +553,69 @@ "team": 0, "homeRange": 1, "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "36e8b3e2-c177-4b5f-a9b8-e21ac99d93e5", + "name": "Spawner (219)", + "location": [5572, 113, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c327247c-da8a-412c-9e92-d960c1b91ec2", + "name": "Spawner (219)", + "location": [5546, 113, -5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4a851206-2957-4090-931d-54ed78cea448", + "name": "Spawner (219)", + "location": [5474, 139, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b10ba4a-f6a2-4164-827a-c7f98da29341", + "name": "Spawner (219)", + "location": [5477, 183, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a0c7b16e-fe26-4e43-ab3a-789041878480", "name": "Spawner (219)", "location": [5469, 211, 0], @@ -1241,336 +633,10 @@ ] }, { - "$type": "Spawner", - "guid": "a3d232dd-36fe-40f3-90b3-8e40d23c75e5", + "type": "Spawner", + "guid": "5a80cf5e-c29f-46d5-83e4-c622fbeba8ad", "name": "Spawner (219)", - "location": [5606, 25, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a40b89dc-77a6-4b5a-9f53-c7c6b75fe44b", - "name": "Spawner (219)", - "location": [5415, 189, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a59d8de3-e39e-4d3e-84dd-b76c900d4f1f", - "name": "Spawner (219)", - "location": [5845, 57, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a7f36503-7139-4ef4-adef-0cc2889d0e90", - "name": "Spawner (219)", - "location": [5620, 21, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a980490d-cb4e-43dc-ad4f-1cd8c1f8846a", - "name": "Spawner (219)", - "location": [5804, 12, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b0e7336b-58cf-43d5-bb4c-d91d0d696b72", - "name": "Spawner (219)", - "location": [5725, 78, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b1bc56ef-b214-4b53-b49b-fe725b9d4d9f", - "name": "Spawner (219)", - "location": [5733, 93, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b27e56f7-9157-4b01-8ddd-0aec26e185df", - "name": "Spawner (219)", - "location": [5416, 59, -15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b283b315-485a-422a-a822-446e200a0080", - "name": "Spawner (219)", - "location": [5434, 100, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b907c1ba-e6aa-4ec8-81a2-8ebc40131a7a", - "name": "Spawner (219)", - "location": [5477, 67, 20], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9c97892-db29-41e5-9478-0f6d9e28e8f7", - "name": "Spawner (219)", - "location": [5434, 89, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba178ecf-0529-4540-9edd-710d6deb748b", - "name": "Spawner (219)", - "location": [5468, 100, 35], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bdfae837-0641-4ad9-ac46-23113dbe07e0", - "name": "Spawner (219)", - "location": [5875, 44, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bffaf353-dbe8-44a7-9468-acd13b4430b7", - "name": "Spawner (219)", - "location": [5752, 44, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c164e1f3-3128-4087-a1f5-ffcc45e5e300", - "name": "Spawner (219)", - "location": [5819, 50, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c23758c7-2a5d-4607-b09e-de24fed379ee", - "name": "Spawner (219)", - "location": [5760, 99, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "Scorpion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2a61736-5a76-4421-8178-deea7aabc615", - "name": "Spawner (219)", - "location": [5394, 11, 30], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2eb38b8-d316-4f83-95f5-4c5dc6b43113", - "name": "Spawner (219)", - "location": [5400, 45, 30], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c327247c-da8a-412c-9e92-d960c1b91ec2", - "name": "Spawner (219)", - "location": [5546, 113, -5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c3b25137-7ec8-4274-8af1-7a30f4cfaef3", - "name": "Spawner (219)", - "location": [5721, 12, 10], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "BloodElemental", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ca52e3b4-b46b-49ec-96ca-132c5ff0b4d6", - "name": "Spawner (219)", - "location": [5577, 194, 0], + "location": [5441, 187, 0], "map": "Felucca", "count": 10, "minDelay": "00:05:00", @@ -1584,142 +650,10 @@ ] }, { - "$type": "Spawner", - "guid": "cad7fb39-f2aa-4f55-9899-64967860d07e", + "type": "Spawner", + "guid": "28a61b6b-d21f-4610-aaf2-98f0714ee469", "name": "Spawner (219)", - "location": [5404, 43, 30], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cc00b741-ad6a-46f6-8398-62ad6e66700a", - "name": "Spawner (219)", - "location": [5856, 107, 10], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ceb6c395-5f88-41cd-8d10-8a6dd7683cce", - "name": "Spawner (219)", - "location": [5594, 182, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d1898122-15d6-4523-a6e6-84ae0cda8c6a", - "name": "Spawner (219)", - "location": [5603, 93, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "DullCopperElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d1eed89e-ef04-4d60-812d-fea412921d76", - "name": "Spawner (219)", - "location": [5549, 79, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d4a17766-a790-4a7b-a7d2-22d095feb615", - "name": "Spawner (219)", - "location": [5456, 157, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d6921911-2c96-4f33-96e9-d40efa57561c", - "name": "Spawner (219)", - "location": [5387, 41, 20], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Scorpion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d92657cf-0eff-4aa3-8d6b-b11164fe85f2", - "name": "Spawner (219)", - "location": [5604, 166, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da26e6d2-e197-479f-a3a0-2f3cd436539c", - "name": "Spawner (219)", - "location": [5724, 118, 0], + "location": [5412, 164, 0], "map": "Felucca", "count": 4, "minDelay": "00:05:00", @@ -1728,76 +662,12 @@ "homeRange": 15, "walkingRange": 30, "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 2, "probability": 100 } + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ddce5aba-7b2b-4d1f-b055-d242db0509c1", - "name": "Spawner (219)", - "location": [5402, 58, -20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e468b283-ac85-4c27-a6e8-462a971f007c", - "name": "Spawner (219)", - "location": [5699, 61, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f11035e3-c112-444e-9227-79c2254265b0", - "name": "Spawner (219)", - "location": [5432, 158, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f14bf474-ef89-4f01-b3a7-0ec8aaf1f35a", - "name": "Spawner (219)", - "location": [5727, 72, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f1a6bec6-dc10-4658-bceb-bd9ecf6970cf", "name": "Spawner (219)", "location": [5411, 163, 0], @@ -1815,10 +685,177 @@ ] }, { - "$type": "Spawner", - "guid": "f21c56d6-5653-4bec-95de-58b70a981ff0", + "type": "Spawner", + "guid": "29921605-0cc3-4a0f-8fcb-d5088c40eb8b", "name": "Spawner (219)", - "location": [5423, 112, 0], + "location": [5412, 201, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "25a4f4e6-8d5b-4e50-87c5-65961ef02649", + "name": "Spawner (219)", + "location": [5412, 200, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b0f079f-a068-4b35-9c6a-13a2a0eefa41", + "name": "Spawner (219)", + "location": [5394, 230, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7540f782-bcf5-4a5f-b2e6-ba385859a340", + "name": "Spawner (219)", + "location": [5505, 180, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "ElderGazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e980b6d-486f-45a7-ad54-477a816dda3a", + "name": "Spawner (219)", + "location": [5527, 210, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "DullCopperElemental", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "27ef1488-f84c-4729-a3fc-6ad074a90495", + "name": "Spawner (219)", + "location": [5557, 222, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 2, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca52e3b4-b46b-49ec-96ca-132c5ff0b4d6", + "name": "Spawner (219)", + "location": [5577, 194, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EvilMage", "maxCount": 5, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ceb6c395-5f88-41cd-8d10-8a6dd7683cce", + "name": "Spawner (219)", + "location": [5594, 182, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3cf4908c-dbae-4cb8-b48f-fdf387ecc501", + "name": "Spawner (219)", + "location": [5571, 158, -10], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "86dc2f0d-baf0-4acc-8b9f-62a1bbbcca31", + "name": "Spawner (219)", + "location": [5593, 141, 10], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d5036ac-c3f2-48f0-8183-daca945d0523", + "name": "Spawner (219)", + "location": [5398, 148, 20], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1826,15 +863,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "fb19bd5b-21cf-475e-bd85-4c990c2d6596", + "type": "Spawner", + "guid": "4b71965f-2829-4d76-8b31-9c6d55aa7b11", "name": "Spawner (219)", - "location": [5474, 176, 0], + "location": [5545, 153, 20], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -1842,12 +877,136 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "90a2b066-3a4d-4627-bcd8-649434051203", + "name": "Spawner (219)", + "location": [5594, 139, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d92657cf-0eff-4aa3-8d6b-b11164fe85f2", + "name": "Spawner (219)", + "location": [5604, 166, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49dbe727-8641-49ac-8372-a3f2f8fa5cf7", + "name": "Spawner (219)", + "location": [5608, 190, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "340a9686-1ac8-47ea-9052-31167add0da8", + "name": "Spawner (219)", + "location": [5605, 202, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4fe7e9e3-3d65-433f-9c20-584fa7647e12", + "name": "Spawner (219)", + "location": [5439, 137, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11f83b98-6f66-43cd-bc2d-c8435a753629", + "name": "Spawner (219)", + "location": [5390, 145, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d4a17766-a790-4a7b-a7d2-22d095feb615", + "name": "Spawner (219)", + "location": [5456, 157, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3962bad0-4c7f-4fed-b4f2-e69020569bcc", + "name": "Spawner (219)", + "location": [5410, 170, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a40b89dc-77a6-4b5a-9f53-c7c6b75fe44b", + "name": "Spawner (219)", + "location": [5415, 189, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "fc63948f-de6d-43de-a205-6e11d44249f1", "name": "Spawner (219)", "location": [5396, 226, 10], @@ -1858,8 +1017,674 @@ "team": 0, "homeRange": 1, "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2284227d-bd9b-4b23-99c7-e96eaeacb970", + "name": "Spawner (219)", + "location": [5553, 146, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "00b4ffd2-c3fe-42e0-b913-9dc7f244bad1", + "name": "Spawner (219)", + "location": [5595, 147, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "24bb9e8a-3124-4984-89be-39ff6cdd3e34", + "name": "Spawner (219)", + "location": [5615, 173, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6b367539-626c-44b0-a97e-8795ee947c6e", + "name": "Spawner (219)", + "location": [5616, 184, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4bbcb759-e08d-45a9-b7c2-76855e283b0f", + "name": "Spawner (219)", + "location": [5456, 143, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f11035e3-c112-444e-9227-79c2254265b0", + "name": "Spawner (219)", + "location": [5432, 158, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb19bd5b-21cf-475e-bd85-4c990c2d6596", + "name": "Spawner (219)", + "location": [5474, 176, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4be1fbed-a047-4092-9ed8-e5ee308123de", + "name": "Spawner (219)", + "location": [5406, 177, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "62098bc9-596a-4f31-80a0-74190ea65a91", + "name": "Spawner (219)", + "location": [5405, 185, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1333be8f-f542-4131-a842-f5419ea6690b", + "name": "Spawner (219)", + "location": [5405, 200, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "06a27dea-d6cf-4668-aaf5-11b92f37dac2", + "name": "Spawner (219)", + "location": [5403, 235, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9add65b7-3729-4ab6-be5f-53fc13b3fbcb", + "name": "Spawner (219)", + "location": [5475, 190, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bdfae837-0641-4ad9-ac46-23113dbe07e0", + "name": "Spawner (219)", + "location": [5875, 44, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cc00b741-ad6a-46f6-8398-62ad6e66700a", + "name": "Spawner (219)", + "location": [5856, 107, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a59d8de3-e39e-4d3e-84dd-b76c900d4f1f", + "name": "Spawner (219)", + "location": [5845, 57, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "694e60e0-1db8-457f-b48c-d11372471f5f", + "name": "Spawner (219)", + "location": [5828, 42, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "c164e1f3-3128-4087-a1f5-ffcc45e5e300", + "name": "Spawner (219)", + "location": [5819, 50, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56bcaa4f-deef-4ca3-983e-461ab73c81ca", + "name": "Spawner (219)", + "location": [5818, 80, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "ElderGazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a980490d-cb4e-43dc-ad4f-1cd8c1f8846a", + "name": "Spawner (219)", + "location": [5804, 12, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c3b25137-7ec8-4274-8af1-7a30f4cfaef3", + "name": "Spawner (219)", + "location": [5721, 12, 10], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "BloodElemental", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3bc0b686-d31e-4ed6-b1bc-6236301c0d04", + "name": "Spawner (219)", + "location": [5661, 16, -10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0acf386b-aab2-42da-9852-bbab5e17235c", + "name": "Spawner (219)", + "location": [5649, 101, 10], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "56eb182b-e842-46da-b44c-cee5290561ac", + "name": "Spawner (219)", + "location": [5684, 117, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "BloodElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da26e6d2-e197-479f-a3a0-2f3cd436539c", + "name": "Spawner (219)", + "location": [5724, 118, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c23758c7-2a5d-4607-b09e-de24fed379ee", + "name": "Spawner (219)", + "location": [5760, 99, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33b7379d-d169-43b4-a75a-2c550fb45d2e", + "name": "Spawner (219)", + "location": [5711, 97, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bffaf353-dbe8-44a7-9468-acd13b4430b7", + "name": "Spawner (219)", + "location": [5752, 44, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13410a04-955c-4e3b-b88a-c59e17ec343c", + "name": "Spawner (219)", + "location": [5708, 43, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a545713-a7cb-4e80-b41f-9eccfb8d5f35", + "name": "Spawner (219)", + "location": [5730, 90, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70127b13-674d-4def-86fe-bc5b90a6e88e", + "name": "Spawner (219)", + "location": [5723, 73, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6786e744-3a8a-4d00-8bec-213325b489b6", + "name": "Spawner (219)", + "location": [5715, 61, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "63e94588-f42b-42a6-a046-a9d4fe7a62b8", + "name": "Spawner (219)", + "location": [5701, 60, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "17349861-3ab6-4103-bd27-07d40be823d0", + "name": "Spawner (219)", + "location": [5817, 83, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "622a0111-2226-43de-bea6-a6e50b1a1a67", + "name": "Spawner (219)", + "location": [5730, 93, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "584599e2-fdf8-423b-b7b7-9e6e2cdcf45b", + "name": "Spawner (219)", + "location": [5723, 77, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "16036a13-721a-447b-ae6b-a5df60d9c9ad", + "name": "Spawner (219)", + "location": [5717, 60, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e468b283-ac85-4c27-a6e8-462a971f007c", + "name": "Spawner (219)", + "location": [5699, 61, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1bb3ab96-96dd-47df-8e4b-c788960b039f", + "name": "Spawner (219)", + "location": [5817, 78, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e200a23-7f5e-4b50-b15b-72543331cbdd", + "name": "Spawner (219)", + "location": [5733, 91, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b0e7336b-58cf-43d5-bb4c-d91d0d696b72", + "name": "Spawner (219)", + "location": [5725, 78, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "91118d48-78d5-4fce-a97d-bd662a0bb903", + "name": "Spawner (219)", + "location": [5715, 58, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3986c149-5d04-4234-9194-25d9ad0daeef", + "name": "Spawner (219)", + "location": [5698, 57, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b1bc56ef-b214-4b53-b49b-fe725b9d4d9f", + "name": "Spawner (219)", + "location": [5733, 93, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f14bf474-ef89-4f01-b3a7-0ec8aaf1f35a", + "name": "Spawner (219)", + "location": [5727, 72, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "38198440-cbb9-4ddb-8808-59977415fa28", + "name": "Spawner (219)", + "location": [5717, 58, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a01e8912-3b82-4214-a8b7-13c598cb23e0", + "name": "Spawner (219)", + "location": [5701, 58, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/SolenHive.json b/Distribution/Data/Spawns/post-uoml/felucca/SolenHive.json similarity index 73% rename from Distribution/Data/Spawns/shared/felucca/SolenHive.json rename to Distribution/Data/Spawns/post-uoml/felucca/SolenHive.json index 3a6d5704f..c97f6a76a 100644 --- a/Distribution/Data/Spawns/shared/felucca/SolenHive.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/SolenHive.json @@ -1,119 +1,20 @@ [ { - "$type": "Spawner", - "guid": "00f9e4a9-d9a7-4d44-aecc-c2bb20a79c9c", + "type": "Spawner", + "guid": "c6783361-a68d-4811-a162-86af961cf503", "name": "Spawner (220)", - "location": [5920, 1842, 1], + "location": [5670, 1807, 4], "map": "Felucca", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, + "homeRange": 25, "walkingRange": 30, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "022918a5-aa50-4cf1-b286-bae8de9c9537", - "name": "Spawner (220)", - "location": [5884, 1920, -15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "AntLion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "04a12d97-707d-4bc9-ad99-b32f0e88e158", - "name": "Spawner (220)", - "location": [5671, 1979, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0876305c-d6ce-46ff-bb97-e34d5fb772a9", - "name": "Spawner (220)", - "location": [5798, 1963, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 35, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0b57c719-bf1c-4cbe-ac70-5aaabda64779", - "name": "Spawner (220)", - "location": [5681, 1886, 2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 50, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0cda81be-bbe3-4312-af53-dcf5b33a1be8", - "name": "Spawner (220)", - "location": [5699, 1921, 2], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "164e28e4-c77f-450f-916a-493904e79d9b", - "name": "Spawner (220)", - "location": [3218, 502, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "RedSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, - { "name": "RedSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "292f941a-a9c4-4ec2-8957-0252fe7be988", "name": "Spawner (220)", "location": [5673, 1846, 1], @@ -124,140 +25,10 @@ "team": 0, "homeRange": 6, "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "308e3de7-9304-466e-9cc0-e71367242d75", - "name": "Spawner (220)", - "location": [5815, 1881, 2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "315f85b8-ba97-4ca0-b2c4-92b7cd539c5b", - "name": "Spawner (220)", - "location": [5847, 1819, 1], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "39cbbb5e-744c-4179-8646-fb1290698ad1", - "name": "Spawner (220)", - "location": [5829, 1798, 1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3bdddcd1-a470-49d7-bc08-41a4e9049e3d", - "name": "Spawner (220)", - "location": [5767, 1962, 2], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 50, - "entries": [ - { "name": "DreadSpider", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40a36fd0-161f-42c1-9cca-9db682d9c50e", - "name": "Spawner (220)", - "location": [5703, 1991, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 35, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "451c7289-0639-4054-879d-fd66e27e866c", - "name": "Spawner (220)", - "location": [5912, 1800, 1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "487e7cad-478d-4ffb-b109-fe2f398f2980", - "name": "Spawner (220)", - "location": [5825, 1968, 4], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4adcd151-0741-4ae0-88ef-f4b01523826c", - "name": "Spawner (220)", - "location": [5838, 2019, 3], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "4e878d51-e601-4e8c-b034-fcc572f4a840", "name": "Spawner (220)", "location": [5693, 1834, 0], @@ -268,78 +39,10 @@ "team": 0, "homeRange": 4, "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "4fc74f26-5c3b-4122-8a88-9581e5d02e5a", - "name": "Spawner (220)", - "location": [5910, 1991, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "616e54a8-e1fd-4877-8f77-92f3361b266e", - "name": "Spawner (220)", - "location": [5722, 1820, 5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f5ddf7f-8608-44e9-841d-b1b6248e54bf", - "name": "Spawner (220)", - "location": [5736, 1939, 7], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BlackSolenQueen", "maxCount": 1, "probability": 100 }, - { "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76303de9-d601-4f18-b341-ed8741315132", - "name": "Spawner (220)", - "location": [1940, 3261, 1], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "RedSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, - { "name": "RedSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "7841012e-e50b-44c7-80e5-9f0901304abd", "name": "Spawner (220)", "location": [5704, 1807, 4], @@ -350,28 +53,24 @@ "team": 0, "homeRange": 6, "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "818ebcff-8a3d-4e6d-a8ac-fbd782899100", + "type": "Spawner", + "guid": "616e54a8-e1fd-4877-8f77-92f3361b266e", "name": "Spawner (220)", - "location": [5799, 1857, 5], + "location": [5722, 1820, 5], "map": "Felucca", - "count": 2, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "81d29a4d-f337-46b8-971f-402bce72c1a3", "name": "Spawner (220)", "location": [5709, 1874, 1], @@ -382,76 +81,66 @@ "team": 0, "homeRange": 10, "walkingRange": 50, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "8bfb6db7-bd95-4b69-9274-330a3806885b", + "type": "Spawner", + "guid": "0b57c719-bf1c-4cbe-ac70-5aaabda64779", "name": "Spawner (220)", - "location": [5782, 2021, -11], + "location": [5681, 1886, 2], "map": "Felucca", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "AntLion", "maxCount": 1, "probability": 100 } - ] + "walkingRange": 50, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "92c760f1-55bf-44cd-b51f-b3425595c3f4", + "type": "Spawner", + "guid": "fe17cd01-9207-4132-b25a-b23b6d0a21cd", "name": "Spawner (220)", - "location": [5776, 2019, 2], + "location": [5756, 1857, 4], "map": "Felucca", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 } - ] + "walkingRange": 50, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "9c112ab3-aaed-483c-a895-12d56c3da76c", + "type": "Spawner", + "guid": "451c7289-0639-4054-879d-fd66e27e866c", "name": "Spawner (220)", - "location": [5831, 1910, 3], + "location": [5912, 1800, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "00f9e4a9-d9a7-4d44-aecc-c2bb20a79c9c", + "name": "Spawner (220)", + "location": [5920, 1842, 1], "map": "Felucca", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Beetle", "maxCount": 2, "probability": 100 } - ] + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "9fccb18d-7d55-428d-9a0e-6975b0f2f213", - "name": "Spawner (220)", - "location": [5837, 1992, -2], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Beetle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a130822d-7738-4410-a154-7860ca71cba2", "name": "Spawner (220)", "location": [5887, 1840, -18], @@ -462,60 +151,38 @@ "team": 0, "homeRange": 10, "walkingRange": 25, - "entries": [ - { "name": "AntLion", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "a2fd7f95-2496-4594-9ed9-ab55fa31d08f", + "type": "Spawner", + "guid": "315f85b8-ba97-4ca0-b2c4-92b7cd539c5b", "name": "Spawner (220)", - "location": [5707, 1843, -15], + "location": [5847, 1819, 1], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "39cbbb5e-744c-4179-8646-fb1290698ad1", + "name": "Spawner (220)", + "location": [5829, 1798, 1], "map": "Felucca", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "AntLion", "maxCount": 1, "probability": 100 } - ] + "walkingRange": 15, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "abaf512d-5839-439d-964e-888303a40d48", - "name": "Spawner (220)", - "location": [5782, 1793, 1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b280605e-0ebe-492d-a0c0-f57d1e05e5a4", - "name": "Spawner (220)", - "location": [5665, 1868, 11], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "AntLion", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "c02ed532-4fdd-4e4a-83c4-f2f8a0e025e1", "name": "Spawner (220)", "location": [5873, 1798, 0], @@ -532,7 +199,206 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "abaf512d-5839-439d-964e-888303a40d48", + "name": "Spawner (220)", + "location": [5782, 1793, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "818ebcff-8a3d-4e6d-a8ac-fbd782899100", + "name": "Spawner (220)", + "location": [5799, 1857, 5], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "308e3de7-9304-466e-9cc0-e71367242d75", + "name": "Spawner (220)", + "location": [5815, 1881, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9c112ab3-aaed-483c-a895-12d56c3da76c", + "name": "Spawner (220)", + "location": [5831, 1910, 3], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Beetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f7c24d72-6e8d-4e78-9cd2-fd6bf7e427e3", + "name": "Spawner (220)", + "location": [5718, 2024, 4], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92c760f1-55bf-44cd-b51f-b3425595c3f4", + "name": "Spawner (220)", + "location": [5776, 2019, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8bfb6db7-bd95-4b69-9274-330a3806885b", + "name": "Spawner (220)", + "location": [5782, 2021, -11], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3bdddcd1-a470-49d7-bc08-41a4e9049e3d", + "name": "Spawner (220)", + "location": [5767, 1962, 2], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 50, + "entries": [{ "name": "DreadSpider", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04a12d97-707d-4bc9-ad99-b32f0e88e158", + "name": "Spawner (220)", + "location": [5671, 1979, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40a36fd0-161f-42c1-9cca-9db682d9c50e", + "name": "Spawner (220)", + "location": [5703, 1991, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0cda81be-bbe3-4312-af53-dcf5b33a1be8", + "name": "Spawner (220)", + "location": [5699, 1921, 2], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f9cdf3b9-37a7-456b-a24a-a9fc19153cda", + "name": "Spawner (220)", + "location": [5748, 1923, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6f5ddf7f-8608-44e9-841d-b1b6248e54bf", + "name": "Spawner (220)", + "location": [5736, 1939, 7], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "BlackSolenQueen", "maxCount": 1, "probability": 100 }, + { "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d94b6b1f-efd6-427f-aa69-88b326e256fd", + "name": "Spawner (220)", + "location": [5876, 2018, 3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "c1cfb2f5-31c9-4686-8478-7e23db2e9ab0", "name": "Spawner (220)", "location": [5856, 2018, 0], @@ -543,28 +409,184 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "c6783361-a68d-4811-a162-86af961cf503", + "type": "Spawner", + "guid": "4adcd151-0741-4ae0-88ef-f4b01523826c", "name": "Spawner (220)", - "location": [5670, 1807, 4], + "location": [5838, 2019, 3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4fc74f26-5c3b-4122-8a88-9581e5d02e5a", + "name": "Spawner (220)", + "location": [5910, 1991, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9fccb18d-7d55-428d-9a0e-6975b0f2f213", + "name": "Spawner (220)", + "location": [5837, 1992, -2], "map": "Felucca", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 25, - "walkingRange": 30, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Beetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e5998826-a85a-4fff-81d0-382fa35bf4ae", + "name": "Spawner (220)", + "location": [5863, 1958, 2], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "022918a5-aa50-4cf1-b286-bae8de9c9537", + "name": "Spawner (220)", + "location": [5884, 1920, -15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "487e7cad-478d-4ffb-b109-fe2f398f2980", + "name": "Spawner (220)", + "location": [5825, 1968, 4], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e82f829e-9f6f-494c-afe4-98c2cb4f1e33", + "name": "Spawner (220)", + "location": [5855, 1932, 3], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0876305c-d6ce-46ff-bb97-e34d5fb772a9", + "name": "Spawner (220)", + "location": [5798, 1963, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 35, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b280605e-0ebe-492d-a0c0-f57d1e05e5a4", + "name": "Spawner (220)", + "location": [5665, 1868, 11], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "AntLion", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a2fd7f95-2496-4594-9ed9-ab55fa31d08f", + "name": "Spawner (220)", + "location": [5707, 1843, -15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "164e28e4-c77f-450f-916a-493904e79d9b", + "name": "Spawner (220)", + "location": [3218, 502, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, "entries": [ - { "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 } + { "name": "RedSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "RedSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "76303de9-d601-4f18-b341-ed8741315132", + "name": "Spawner (220)", + "location": [1940, 3261, 1], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "RedSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "RedSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "d1b08137-55ba-4356-8927-30febaac045b", "name": "Spawner (220)", "location": [1361, 894, 0], @@ -579,101 +601,5 @@ { "name": "RedSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, { "name": "RedSolenInfiltratorWarrior", "maxCount": 1, "probability": 100 } ] - }, - { - "$type": "Spawner", - "guid": "d94b6b1f-efd6-427f-aa69-88b326e256fd", - "name": "Spawner (220)", - "location": [5876, 2018, 3], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e5998826-a85a-4fff-81d0-382fa35bf4ae", - "name": "Spawner (220)", - "location": [5863, 1958, 2], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e82f829e-9f6f-494c-afe4-98c2cb4f1e33", - "name": "Spawner (220)", - "location": [5855, 1932, 3], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f7c24d72-6e8d-4e78-9cd2-fd6bf7e427e3", - "name": "Spawner (220)", - "location": [5718, 2024, 4], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f9cdf3b9-37a7-456b-a24a-a9fc19153cda", - "name": "Spawner (220)", - "location": [5748, 1923, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 20, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fe17cd01-9207-4132-b25a-b23b6d0a21cd", - "name": "Spawner (220)", - "location": [5756, 1857, 4], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 50, - "entries": [ - { "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 } - ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/TerathanKeep.json b/Distribution/Data/Spawns/post-uoml/felucca/TerathanKeep.json similarity index 94% rename from Distribution/Data/Spawns/shared/felucca/TerathanKeep.json rename to Distribution/Data/Spawns/post-uoml/felucca/TerathanKeep.json index 873518a6b..154869d4f 100644 --- a/Distribution/Data/Spawns/shared/felucca/TerathanKeep.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/TerathanKeep.json @@ -1,9 +1,28 @@ [ { - "$type": "Spawner", - "guid": "05f81bd1-8432-458d-973a-85449805e567", + "type": "Spawner", + "guid": "ec99841b-c28c-4b7c-a4d3-15f2753a4184", "name": "Spawner (221)", - "location": [5163, 1585, -15], + "location": [5296, 1561, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fee1141d-1c6e-479e-9cfc-d92fed242b0a", + "name": "Spawner (221)", + "location": [5296, 1562, 0], "map": "Felucca", "count": 5, "minDelay": "00:05:00", @@ -20,10 +39,10 @@ ] }, { - "$type": "Spawner", - "guid": "0c8d7085-680b-49cf-a8c3-be20a79f94be", + "type": "Spawner", + "guid": "b3b5c6cf-915f-427b-97b1-89d49bdc440a", "name": "Spawner (221)", - "location": [5161, 1584, -15], + "location": [5297, 1565, 0], "map": "Felucca", "count": 4, "minDelay": "00:05:00", @@ -39,43 +58,26 @@ ] }, { - "$type": "Spawner", - "guid": "0e911183-4be3-4c7d-878b-a7b4d97d8f14", + "type": "Spawner", + "guid": "6db88e49-590f-4fc9-9ff2-484c0caba22d", "name": "Spawner (221)", - "location": [5201, 1566, 0], + "location": [5342, 1553, 0], "map": "Felucca", - "count": 5, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 20, "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "140da661-2c38-4502-a8d3-a093fb672c3e", - "name": "Spawner (221)", - "location": [5363, 1709, -75], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "14d9a777-99d2-4e2a-92e4-98eb9ed2cc2a", "name": "Spawner (221)", "location": [5342, 1552, 0], @@ -95,83 +97,7 @@ ] }, { - "$type": "Spawner", - "guid": "1613210d-f099-406c-8dad-b3e6c4978ea4", - "name": "Spawner (221)", - "location": [5134, 1625, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "19a3a2ba-4466-440e-9eb8-69c30087faf0", - "name": "Spawner (221)", - "location": [5160, 1586, -15], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "56ff9b13-646b-4f60-be0b-b5aaa730d17d", - "name": "Spawner (221)", - "location": [5336, 1708, -70], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "578848ef-81b8-4528-b3e5-7819296f1441", - "name": "Spawner (221)", - "location": [5200, 1568, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "5a472619-a1fd-43b6-8ed3-876689a8b7a4", "name": "Spawner (221)", "location": [5343, 1551, 0], @@ -190,270 +116,7 @@ ] }, { - "$type": "Spawner", - "guid": "628166d6-c981-4d20-bee3-f9e68ca672e4", - "name": "Spawner (221)", - "location": [5345, 1551, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b1ed67b-dcfd-41a6-9b27-9f815db3e48f", - "name": "Spawner (221)", - "location": [5200, 1565, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6db88e49-590f-4fc9-9ff2-484c0caba22d", - "name": "Spawner (221)", - "location": [5342, 1553, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7045923d-0e54-48e2-8c61-25f4e748bb62", - "name": "Spawner (221)", - "location": [5176, 1702, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "78eb98fb-eb53-4956-919c-9b075b3a02ad", - "name": "Spawner (221)", - "location": [5348, 1552, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "877895b3-0048-42fa-943b-4246c6d0dbd1", - "name": "Spawner (221)", - "location": [5176, 1701, 2], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8cddc6ca-33de-411d-9d6d-03f61c427acc", - "name": "Spawner (221)", - "location": [5335, 1707, -70], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "97e91add-b1d5-48e6-ae53-6dd25cb088bc", - "name": "Spawner (221)", - "location": [5270, 1695, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c464287-4f2e-40c2-b6cd-7110d89097eb", - "name": "Spawner (221)", - "location": [5132, 1624, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a720dbb7-0f4e-45c8-9bac-c9b76e028c02", - "name": "Spawner (221)", - "location": [5132, 1623, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ada2362f-c030-42f1-86ce-5d983ff64a38", - "name": "Spawner (221)", - "location": [5346, 1756, -125], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "adcadb01-80d3-40a1-acf8-6a7493aeef84", - "name": "Spawner (221)", - "location": [5271, 1693, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b1a245ac-73e5-4f51-a9b0-72a7e0aeb999", - "name": "Spawner (221)", - "location": [5177, 1700, 1], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3b5c6cf-915f-427b-97b1-89d49bdc440a", - "name": "Spawner (221)", - "location": [5297, 1565, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "ce1ce8c5-583f-4f67-84b6-126bc30487ff", "name": "Spawner (221)", "location": [5334, 1613, 0], @@ -472,27 +135,7 @@ ] }, { - "$type": "Spawner", - "guid": "d3a73b6d-ffa1-4448-9308-6431b4f51d4b", - "name": "Spawner (221)", - "location": [5345, 1757, -125], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "d6b68ad4-a9c2-4982-9477-31a7cb202a69", "name": "Spawner (221)", "location": [5334, 1614, 0], @@ -512,26 +155,7 @@ ] }, { - "$type": "Spawner", - "guid": "da8f850f-a715-4ae6-9c10-47db89b3d732", - "name": "Spawner (221)", - "location": [5344, 1757, -125], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "dfb3c171-67af-4dea-8b11-1d49762b2fb2", "name": "Spawner (221)", "location": [5335, 1615, 0], @@ -550,10 +174,10 @@ ] }, { - "$type": "Spawner", - "guid": "ebca1c72-604f-4cba-b019-c5de4a5b55c8", + "type": "Spawner", + "guid": "78eb98fb-eb53-4956-919c-9b075b3a02ad", "name": "Spawner (221)", - "location": [5365, 1707, -71], + "location": [5348, 1552, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -561,15 +185,27 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ec99841b-c28c-4b7c-a4d3-15f2753a4184", + "type": "Spawner", + "guid": "628166d6-c981-4d20-bee3-f9e68ca672e4", "name": "Spawner (221)", - "location": [5296, 1561, 0], + "location": [5345, 1551, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da8f850f-a715-4ae6-9c10-47db89b3d732", + "name": "Spawner (221)", + "location": [5344, 1757, -125], "map": "Felucca", "count": 4, "minDelay": "00:05:00", @@ -585,7 +221,46 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "d3a73b6d-ffa1-4448-9308-6431b4f51d4b", + "name": "Spawner (221)", + "location": [5345, 1757, -125], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ada2362f-c030-42f1-86ce-5d983ff64a38", + "name": "Spawner (221)", + "location": [5346, 1756, -125], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f9a936c9-3c10-4029-a46d-dbbdb5494b48", "name": "Spawner (221)", "location": [5335, 1706, -70], @@ -604,7 +279,65 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "8cddc6ca-33de-411d-9d6d-03f61c427acc", + "name": "Spawner (221)", + "location": [5335, 1707, -70], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56ff9b13-646b-4f60-be0b-b5aaa730d17d", + "name": "Spawner (221)", + "location": [5336, 1708, -70], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "97e91add-b1d5-48e6-ae53-6dd25cb088bc", + "name": "Spawner (221)", + "location": [5270, 1695, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fbc340d4-cf84-410e-9764-5987bdacc4fc", "name": "Spawner (221)", "location": [5270, 1694, 0], @@ -624,10 +357,76 @@ ] }, { - "$type": "Spawner", - "guid": "fee1141d-1c6e-479e-9cfc-d92fed242b0a", + "type": "Spawner", + "guid": "adcadb01-80d3-40a1-acf8-6a7493aeef84", "name": "Spawner (221)", - "location": [5296, 1562, 0], + "location": [5271, 1693, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebca1c72-604f-4cba-b019-c5de4a5b55c8", + "name": "Spawner (221)", + "location": [5365, 1707, -71], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "140da661-2c38-4502-a8d3-a093fb672c3e", + "name": "Spawner (221)", + "location": [5363, 1709, -75], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7045923d-0e54-48e2-8c61-25f4e748bb62", + "name": "Spawner (221)", + "location": [5176, 1702, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "877895b3-0048-42fa-943b-4246c6d0dbd1", + "name": "Spawner (221)", + "location": [5176, 1701, 2], "map": "Felucca", "count": 5, "minDelay": "00:05:00", @@ -642,5 +441,198 @@ { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "b1a245ac-73e5-4f51-a9b0-72a7e0aeb999", + "name": "Spawner (221)", + "location": [5177, 1700, 1], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c464287-4f2e-40c2-b6cd-7110d89097eb", + "name": "Spawner (221)", + "location": [5132, 1624, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a720dbb7-0f4e-45c8-9bac-c9b76e028c02", + "name": "Spawner (221)", + "location": [5132, 1623, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1613210d-f099-406c-8dad-b3e6c4978ea4", + "name": "Spawner (221)", + "location": [5134, 1625, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "19a3a2ba-4466-440e-9eb8-69c30087faf0", + "name": "Spawner (221)", + "location": [5160, 1586, -15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05f81bd1-8432-458d-973a-85449805e567", + "name": "Spawner (221)", + "location": [5163, 1585, -15], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c8d7085-680b-49cf-a8c3-be20a79f94be", + "name": "Spawner (221)", + "location": [5161, 1584, -15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b1ed67b-dcfd-41a6-9b27-9f815db3e48f", + "name": "Spawner (221)", + "location": [5200, 1565, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e911183-4be3-4c7d-878b-a7b4d97d8f14", + "name": "Spawner (221)", + "location": [5201, 1566, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "578848ef-81b8-4528-b3e5-7819296f1441", + "name": "Spawner (221)", + "location": [5200, 1568, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/TownsLife.json b/Distribution/Data/Spawns/post-uoml/felucca/TownsLife.json similarity index 86% rename from Distribution/Data/Spawns/shared/felucca/TownsLife.json rename to Distribution/Data/Spawns/post-uoml/felucca/TownsLife.json index 28182ee4a..665f78309 100644 --- a/Distribution/Data/Spawns/shared/felucca/TownsLife.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/TownsLife.json @@ -1,113 +1,54 @@ [ { - "$type": "Spawner", - "guid": "004f420b-8d77-487d-9864-cc4879439ee5", + "type": "Spawner", + "guid": "97b2dcc3-dbbf-4bf5-89a2-47a112b9e4af", "name": "Spawner (222)", - "location": [1183, 3607, 0], + "location": [5249, 228, 15], "map": "Felucca", - "count": 5, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 }, - { "name": "Cow", "maxCount": 5, "probability": 100 }, - { "name": "Sheep", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "016fd8f8-82f3-4109-9246-336705000145", - "name": "Spawner (222)", - "location": [675, 939, 0], - "map": "Felucca", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Sheep", "maxCount": 15, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "05602f83-caa0-4822-b699-f4427d787aee", - "name": "Spawner (222)", - "location": [2566, 622, 0], - "map": "Felucca", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 }, - { "name": "Cat", "maxCount": 3, "probability": 100 }, - { "name": "Dog", "maxCount": 3, "probability": 100 }, - { "name": "Rat", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a13d9f3-e379-4e9d-887f-2be3ed304042", - "name": "Spawner (222)", - "location": [1435, 4000, 0], - "map": "Felucca", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 }, - { "name": "Cat", "maxCount": 5, "probability": 100 }, - { "name": "Dog", "maxCount": 5, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a813be3-6c2d-4a5e-a5a2-d7fd2e90abdf", - "name": "Spawner (222)", - "location": [1400, 3775, 0], - "map": "Felucca", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Bird", "maxCount": 10, "probability": 100 }, - { "name": "Cat", "maxCount": 10, "probability": 100 }, - { "name": "Dog", "maxCount": 10, "probability": 100 }, - { "name": "Rat", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0aa73630-f7ee-48c5-b61a-24b0d6a44941", - "name": "Spawner (222)", - "location": [7002, 382, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, + "homeRange": 20, "walkingRange": 20, "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 } + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "91ddce58-c241-4606-81e8-1d08cfb7de58", + "name": "Spawner (222)", + "location": [5164, 205, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13543563-3b10-42fa-9e48-94856ed4b3c8", + "name": "Spawner (222)", + "location": [5133, 150, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Daemon", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "0b4c7ad8-a59f-4328-8630-817246fd7cd1", "name": "Spawner (222)", "location": [1550, 1658, 26], @@ -126,177 +67,7 @@ ] }, { - "$type": "Spawner", - "guid": "0e4fee28-b3d1-42c8-8f95-4103c53f31e1", - "name": "Spawner (222)", - "location": [830, 2353, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Chicken", "maxCount": 3, "probability": 100 }, - { "name": "Sheep", "maxCount": 3, "probability": 100 }, - { "name": "Cow", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "107a8d24-1749-4236-a32a-e1e0c286360e", - "name": "Spawner (222)", - "location": [536, 962, 0], - "map": "Felucca", - "count": 80, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 160, - "walkingRange": 160, - "entries": [ - { "name": "Bird", "maxCount": 20, "probability": 100 }, - { "name": "Cat", "maxCount": 20, "probability": 100 }, - { "name": "Dog", "maxCount": 20, "probability": 100 }, - { "name": "Rat", "maxCount": 20, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11532176-a402-41bb-801d-b2f4fedff8ce", - "name": "Spawner (222)", - "location": [1185, 1632, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 80, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "13543563-3b10-42fa-9e48-94856ed4b3c8", - "name": "Spawner (222)", - "location": [5133, 150, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Daemon", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1ad9e818-3743-43df-8834-2bef8d35d69f", - "name": "Spawner (222)", - "location": [1216, 1823, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 80, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1c30b2f9-fd07-4a84-9d98-21a9e0bf814e", - "name": "Spawner (222)", - "location": [7063, 410, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21be647c-e373-43a4-90c9-f24eeafafeda", - "name": "Spawner (222)", - "location": [2242, 1197, 0], - "map": "Felucca", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22cd06b3-f02b-44f8-80cf-60c95ee4fcc4", - "name": "Spawner (222)", - "location": [1319, 1821, 0], - "map": "Felucca", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "Cow", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "270c974f-1266-4c2e-80f1-d883e392c8c3", - "name": "Spawner (222)", - "location": [560, 1239, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2db897d0-401a-4efe-8f40-ed186c60d376", - "name": "Spawner (222)", - "location": [1127, 3583, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 }, - { "name": "Cow", "maxCount": 5, "probability": 100 }, - { "name": "Sheep", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "2e1c6a6c-b2b4-4946-ad62-a319330cef19", "name": "Spawner (222)", "location": [1319, 1810, 0], @@ -307,186 +78,24 @@ "team": 0, "homeRange": 5, "walkingRange": 10, - "entries": [ - { "name": "Cow", "maxCount": 7, "probability": 100 } - ] + "entries": [{ "name": "Cow", "maxCount": 7, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "2ed2bfa6-08b9-4b93-bd1c-aa67f2ebde6a", + "type": "Spawner", + "guid": "22cd06b3-f02b-44f8-80cf-60c95ee4fcc4", "name": "Spawner (222)", - "location": [3726, 2647, 0], + "location": [1319, 1821, 0], "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Chicken", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "34b6ad1f-4ab4-4eea-a295-36e62593cecf", - "name": "Spawner (222)", - "location": [7044, 429, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3c8b3124-16ab-436f-9687-7ecf1c533563", - "name": "Spawner (222)", - "location": [7036, 409, 12], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "425dfc37-404d-455d-8bf7-23a3f9857cad", - "name": "Spawner (222)", - "location": [5255, 4000, 37], - "map": "Felucca", - "count": 24, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 80, - "entries": [ - { "name": "Bird", "maxCount": 6, "probability": 100 }, - { "name": "Cat", "maxCount": 6, "probability": 100 }, - { "name": "Dog", "maxCount": 6, "probability": 100 }, - { "name": "Rat", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "472a9125-a41e-4796-9eb5-63fff737a52a", - "name": "Spawner (222)", - "location": [2687, 2218, 0], - "map": "Felucca", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "48afa381-95b4-4fca-98be-b6ff50b46106", - "name": "Spawner (222)", - "location": [1154, 3639, 0], - "map": "Felucca", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "490ddc6d-d111-427d-b5cf-8e9f510d191f", - "name": "Spawner (222)", - "location": [2223, 1151, 0], - "map": "Felucca", - "count": 1, + "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Chicken", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4c4303ac-98c9-41ae-a4ed-a286510232a9", - "name": "Spawner (222)", - "location": [4634, 1302, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 }, - { "name": "Cow", "maxCount": 5, "probability": 100 }, - { "name": "Sheep", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Cow", "maxCount": 7, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "53d267c1-a427-4e36-be80-7a5cb96f4669", - "name": "Spawner (222)", - "location": [7081, 381, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60755e38-7c07-4745-a61e-bd21ddfa841a", - "name": "Spawner (222)", - "location": [3714, 2205, 20], - "map": "Felucca", - "count": 24, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 6, "probability": 100 }, - { "name": "Cat", "maxCount": 6, "probability": 100 }, - { "name": "Dog", "maxCount": 6, "probability": 100 }, - { "name": "Rat", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "6434f6cc-a8b9-4667-a3e7-5109384d6c75", "name": "Spawner (222)", "location": [1340, 1790, 15], @@ -505,23 +114,7 @@ ] }, { - "$type": "Spawner", - "guid": "65aa9d17-8718-4d62-9dea-53f965b6a798", - "name": "Spawner (222)", - "location": [7052, 386, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "6826cac3-d751-442e-9712-e56579d34015", "name": "Spawner (222)", "location": [1339, 1788, 15], @@ -532,366 +125,10 @@ "team": 0, "homeRange": 80, "walkingRange": 80, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "6f6053e2-9f9b-4e29-b3f6-f4675c82e68b", - "name": "Spawner (222)", - "location": [7007, 360, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76958af8-18c3-4fcc-8624-be8ccfac900e", - "name": "Spawner (222)", - "location": [4422, 1451, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Chicken", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "79c882ff-dab2-497a-9534-d3971b71734c", - "name": "Spawner (222)", - "location": [7007, 382, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7e7d8786-aeec-4481-acf8-7c65d6afa71c", - "name": "Spawner (222)", - "location": [3699, 1238, 0], - "map": "Felucca", - "count": 72, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 160, - "walkingRange": 160, - "entries": [ - { "name": "Bird", "maxCount": 18, "probability": 100 }, - { "name": "Cat", "maxCount": 18, "probability": 100 }, - { "name": "Dog", "maxCount": 18, "probability": 100 }, - { "name": "Rat", "maxCount": 18, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7ebf2c6a-3ecf-4da8-84dc-d0fe5bb38264", - "name": "Spawner (222)", - "location": [2682, 2118, 0], - "map": "Felucca", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 }, - { "name": "Cat", "maxCount": 5, "probability": 100 }, - { "name": "Dog", "maxCount": 5, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "832487b1-6929-4cfd-bf10-8f8901a6af24", - "name": "Spawner (222)", - "location": [7087, 382, 1], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "88b89736-19fe-4265-a878-ae30fb1aa685", - "name": "Spawner (222)", - "location": [1937, 2760, 10], - "map": "Felucca", - "count": 48, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 120, - "walkingRange": 120, - "entries": [ - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "Cat", "maxCount": 12, "probability": 100 }, - { "name": "Dog", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ff8d2d7-f679-42d4-b616-7e3006e18695", - "name": "Spawner (222)", - "location": [2904, 906, 0], - "map": "Felucca", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Bird", "maxCount": 10, "probability": 100 }, - { "name": "Cat", "maxCount": 10, "probability": 100 }, - { "name": "Dog", "maxCount": 10, "probability": 100 }, - { "name": "Rat", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9132b08c-8598-4130-9779-b2eeaf3b0df0", - "name": "Spawner (222)", - "location": [688, 1007, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "91ddce58-c241-4606-81e8-1d08cfb7de58", - "name": "Spawner (222)", - "location": [5164, 205, 15], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "LichLord", "maxCount": 1, "probability": 100 }, - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "96704675-d8b7-4772-9d44-1f334fa1d5bd", - "name": "Spawner (222)", - "location": [2972, 3436, 15], - "map": "Felucca", - "count": 48, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "Cat", "maxCount": 12, "probability": 100 }, - { "name": "Dog", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "97b2dcc3-dbbf-4bf5-89a2-47a112b9e4af", - "name": "Spawner (222)", - "location": [5249, 228, 15], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a5704357-3d9e-4895-ba9b-1148f0440001", - "name": "Spawner (222)", - "location": [2939, 746, 2], - "map": "Felucca", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Bird", "maxCount": 10, "probability": 100 }, - { "name": "Cat", "maxCount": 10, "probability": 100 }, - { "name": "Dog", "maxCount": 10, "probability": 100 }, - { "name": "Rat", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8636c7c-90b3-4c44-8a8d-739728e3b44b", - "name": "Spawner (222)", - "location": [608, 2195, 0], - "map": "Felucca", - "count": 32, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 70, - "walkingRange": 70, - "entries": [ - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Cat", "maxCount": 8, "probability": 100 }, - { "name": "Dog", "maxCount": 8, "probability": 100 }, - { "name": "Rat", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8a3de25-676d-4d86-9400-9a656c62dec1", - "name": "Spawner (222)", - "location": [4438, 1115, 0], - "map": "Felucca", - "count": 48, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 70, - "walkingRange": 70, - "entries": [ - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "Cat", "maxCount": 12, "probability": 100 }, - { "name": "Dog", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8c66161-cf43-47a9-a3a3-c5dcbfa4fbd9", - "name": "Spawner (222)", - "location": [382, 1192, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b09a16a0-6abb-420c-8995-24671b9183c6", - "name": "Spawner (222)", - "location": [5726, 3211, -2], - "map": "Felucca", - "count": 48, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "Cat", "maxCount": 12, "probability": 100 }, - { "name": "Dog", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3d3fb31-99cc-4fa9-a5a3-0dd641081add", - "name": "Spawner (222)", - "location": [3717, 2111, 20], - "map": "Felucca", - "count": 24, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 6, "probability": 100 }, - { "name": "Cat", "maxCount": 6, "probability": 100 }, - { "name": "Dog", "maxCount": 6, "probability": 100 }, - { "name": "Rat", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba72b0e5-a33d-437d-9028-5c4b71eea90c", - "name": "Spawner (222)", - "location": [818, 2269, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Chicken", "maxCount": 3, "probability": 100 }, - { "name": "Sheep", "maxCount": 3, "probability": 100 }, - { "name": "Cow", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf55508a-adf0-4dca-9e67-37c947b28064", - "name": "Spawner (222)", - "location": [6986, 384, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "c73dd6cb-2264-4b89-829e-717f741a58b0", "name": "Spawner (222)", "location": [1217, 1825, 0], @@ -910,208 +147,92 @@ ] }, { - "$type": "Spawner", - "guid": "cb2d9013-b557-43c3-ab94-f69c92c68a89", + "type": "Spawner", + "guid": "1ad9e818-3743-43df-8834-2bef8d35d69f", "name": "Spawner (222)", - "location": [7033, 385, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd07faea-cb76-402c-a145-cc4ca8277359", - "name": "Spawner (222)", - "location": [3652, 2511, 0], - "map": "Felucca", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d1f89bea-7fed-494e-a03a-ebb7cc8fa3d2", - "name": "Spawner (222)", - "location": [4567, 1476, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "Chicken", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d233e758-f2b9-4f55-8e65-1441e452f34e", - "name": "Spawner (222)", - "location": [6992, 345, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d287da59-9954-4e44-b743-f859fdc73d1c", - "name": "Spawner (222)", - "location": [7032, 411, 7], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d4aab4aa-ae2e-4686-b282-39e05a6e0855", - "name": "Spawner (222)", - "location": [7035, 387, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d53c7e28-e23d-48e5-a3cf-8cdd942ad988", - "name": "Spawner (222)", - "location": [7048, 424, 0], + "location": [1216, 1823, 0], "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 } - ] + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "d8016c7e-7b98-4c9b-b7ae-4247f0e578d4", + "type": "Spawner", + "guid": "f9528415-54f7-4b5b-9390-d0cc6e9ffb1f", "name": "Spawner (222)", - "location": [6999, 352, 0], + "location": [1186, 1633, 0], "map": "Felucca", - "count": 3, + "count": 28, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, - "walkingRange": 12, + "homeRange": 80, + "walkingRange": 80, "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 } + { "name": "Bird", "maxCount": 7, "probability": 100 }, + { "name": "Cat", "maxCount": 7, "probability": 100 }, + { "name": "Dog", "maxCount": 7, "probability": 100 }, + { "name": "Rat", "maxCount": 7, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "d9f8f4ae-ed8f-4888-88ba-14e7e0ab54e1", + "type": "Spawner", + "guid": "11532176-a402-41bb-801d-b2f4fedff8ce", "name": "Spawner (222)", - "location": [2472, 434, 15], - "map": "Felucca", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 }, - { "name": "Cat", "maxCount": 5, "probability": 100 }, - { "name": "Dog", "maxCount": 5, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da82a0d4-2ddc-44cf-9f7e-227dbb1a0bdc", - "name": "Spawner (222)", - "location": [7047, 382, 0], + "location": [1185, 1632, 0], "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 } - ] + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "dcfdc32c-876a-4432-97ec-9d733f76578f", + "type": "Spawner", + "guid": "88b89736-19fe-4265-a878-ae30fb1aa685", "name": "Spawner (222)", - "location": [5267, 4012, 40], + "location": [1937, 2760, 10], "map": "Felucca", - "count": 5, + "count": 48, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 15, + "homeRange": 120, + "walkingRange": 120, "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 }, - { "name": "Sheep", "maxCount": 5, "probability": 100 }, - { "name": "Cow", "maxCount": 5, "probability": 100 }, - { "name": "Bull", "maxCount": 5, "probability": 100 } + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e1129f66-8c08-407c-bcfa-56010a3a4519", + "type": "Spawner", + "guid": "a8636c7c-90b3-4c44-8a8d-739728e3b44b", "name": "Spawner (222)", - "location": [676, 1178, 0], + "location": [608, 2195, 0], "map": "Felucca", - "count": 15, + "count": 32, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 15, + "homeRange": 70, + "walkingRange": 70, "entries": [ - { "name": "Sheep", "maxCount": 15, "probability": 100 } + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Cat", "maxCount": 8, "probability": 100 }, + { "name": "Dog", "maxCount": 8, "probability": 100 }, + { "name": "Rat", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e7061f73-946b-4058-be40-de5593cbd075", "name": "Spawner (222)", "location": [813, 2164, 0], @@ -1129,17 +250,189 @@ ] }, { - "$type": "Spawner", - "guid": "ec2ab142-9d54-4cd1-9fd9-5918ddc5593f", + "type": "Spawner", + "guid": "ba72b0e5-a33d-437d-9028-5c4b71eea90c", "name": "Spawner (222)", - "location": [3674, 2624, 0], + "location": [818, 2269, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e4fee28-b3d1-42c8-8f95-4103c53f31e1", + "name": "Spawner (222)", + "location": [830, 2353, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "107a8d24-1749-4236-a32a-e1e0c286360e", + "name": "Spawner (222)", + "location": [536, 962, 0], + "map": "Felucca", + "count": 80, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 160, + "walkingRange": 160, + "entries": [ + { "name": "Bird", "maxCount": 20, "probability": 100 }, + { "name": "Cat", "maxCount": 20, "probability": 100 }, + { "name": "Dog", "maxCount": 20, "probability": 100 }, + { "name": "Rat", "maxCount": 20, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1129f66-8c08-407c-bcfa-56010a3a4519", + "name": "Spawner (222)", + "location": [676, 1178, 0], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f7e85f44-f68b-419a-859a-6c11cd6cabb6", + "name": "Spawner (222)", + "location": [570, 1099, 0], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "016fd8f8-82f3-4109-9246-336705000145", + "name": "Spawner (222)", + "location": [675, 939, 0], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a8c66161-cf43-47a9-a3a3-c5dcbfa4fbd9", + "name": "Spawner (222)", + "location": [382, 1192, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "270c974f-1266-4c2e-80f1-d883e392c8c3", + "name": "Spawner (222)", + "location": [560, 1239, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9132b08c-8598-4130-9779-b2eeaf3b0df0", + "name": "Spawner (222)", + "location": [688, 1007, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21be647c-e373-43a4-90c9-f24eeafafeda", + "name": "Spawner (222)", + "location": [2242, 1197, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "490ddc6d-d111-427d-b5cf-8e9f510d191f", + "name": "Spawner (222)", + "location": [2223, 1151, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Chicken", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a5704357-3d9e-4895-ba9b-1148f0440001", + "name": "Spawner (222)", + "location": [2939, 746, 2], "map": "Felucca", "count": 40, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 80, - "walkingRange": 80, + "homeRange": 100, + "walkingRange": 100, "entries": [ { "name": "Bird", "maxCount": 10, "probability": 100 }, { "name": "Cat", "maxCount": 10, "probability": 100 }, @@ -1148,7 +441,26 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "8ff8d2d7-f679-42d4-b616-7e3006e18695", + "name": "Spawner (222)", + "location": [2904, 906, 0], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f392c11b-1a43-4647-952c-99783a72e0fd", "name": "Spawner (222)", "location": [2481, 542, 0], @@ -1167,38 +479,660 @@ ] }, { - "$type": "Spawner", - "guid": "f7e85f44-f68b-419a-859a-6c11cd6cabb6", + "type": "Spawner", + "guid": "d9f8f4ae-ed8f-4888-88ba-14e7e0ab54e1", "name": "Spawner (222)", - "location": [570, 1099, 0], + "location": [2472, 434, 15], "map": "Felucca", - "count": 15, + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05602f83-caa0-4822-b699-f4427d787aee", + "name": "Spawner (222)", + "location": [2566, 622, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "Cat", "maxCount": 3, "probability": 100 }, + { "name": "Dog", "maxCount": 3, "probability": 100 }, + { "name": "Rat", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "48afa381-95b4-4fca-98be-b6ff50b46106", + "name": "Spawner (222)", + "location": [1154, 3639, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2db897d0-401a-4efe-8f40-ed186c60d376", + "name": "Spawner (222)", + "location": [1127, 3583, 0], + "map": "Felucca", + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 15, + "walkingRange": 10, "entries": [ - { "name": "Sheep", "maxCount": 15, "probability": 100 } + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f9528415-54f7-4b5b-9390-d0cc6e9ffb1f", + "type": "Spawner", + "guid": "004f420b-8d77-487d-9864-cc4879439ee5", "name": "Spawner (222)", - "location": [1186, 1633, 0], + "location": [1183, 3607, 0], "map": "Felucca", - "count": 28, + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a813be3-6c2d-4a5e-a5a2-d7fd2e90abdf", + "name": "Spawner (222)", + "location": [1400, 3775, 0], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a13d9f3-e379-4e9d-887f-2be3ed304042", + "name": "Spawner (222)", + "location": [1435, 4000, 0], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96704675-d8b7-4772-9d44-1f334fa1d5bd", + "name": "Spawner (222)", + "location": [2972, 3436, 15], + "map": "Felucca", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec2ab142-9d54-4cd1-9fd9-5918ddc5593f", + "name": "Spawner (222)", + "location": [3674, 2624, 0], + "map": "Felucca", + "count": 40, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 80, "walkingRange": 80, "entries": [ - { "name": "Bird", "maxCount": 7, "probability": 100 }, - { "name": "Cat", "maxCount": 7, "probability": 100 }, - { "name": "Dog", "maxCount": 7, "probability": 100 }, - { "name": "Rat", "maxCount": 7, "probability": 100 } + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ed2bfa6-08b9-4b93-bd1c-aa67f2ebde6a", + "name": "Spawner (222)", + "location": [3726, 2647, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Chicken", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cd07faea-cb76-402c-a145-cc4ca8277359", + "name": "Spawner (222)", + "location": [3652, 2511, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60755e38-7c07-4745-a61e-bd21ddfa841a", + "name": "Spawner (222)", + "location": [3714, 2205, 20], + "map": "Felucca", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3d3fb31-99cc-4fa9-a5a3-0dd641081add", + "name": "Spawner (222)", + "location": [3717, 2111, 20], + "map": "Felucca", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7ebf2c6a-3ecf-4da8-84dc-d0fe5bb38264", + "name": "Spawner (222)", + "location": [2682, 2118, 0], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "472a9125-a41e-4796-9eb5-63fff737a52a", + "name": "Spawner (222)", + "location": [2687, 2218, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e7d8786-aeec-4481-acf8-7c65d6afa71c", + "name": "Spawner (222)", + "location": [3699, 1238, 0], + "map": "Felucca", + "count": 72, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 160, + "walkingRange": 160, + "entries": [ + { "name": "Bird", "maxCount": 18, "probability": 100 }, + { "name": "Cat", "maxCount": 18, "probability": 100 }, + { "name": "Dog", "maxCount": 18, "probability": 100 }, + { "name": "Rat", "maxCount": 18, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8a3de25-676d-4d86-9400-9a656c62dec1", + "name": "Spawner (222)", + "location": [4438, 1115, 0], + "map": "Felucca", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c4303ac-98c9-41ae-a4ed-a286510232a9", + "name": "Spawner (222)", + "location": [4634, 1302, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d1f89bea-7fed-494e-a03a-ebb7cc8fa3d2", + "name": "Spawner (222)", + "location": [4567, 1476, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76958af8-18c3-4fcc-8624-be8ccfac900e", + "name": "Spawner (222)", + "location": [4422, 1451, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Chicken", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3c8b3124-16ab-436f-9687-7ecf1c533563", + "name": "Spawner (222)", + "location": [7036, 409, 12], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d53c7e28-e23d-48e5-a3cf-8cdd942ad988", + "name": "Spawner (222)", + "location": [7048, 424, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "53d267c1-a427-4e36-be80-7a5cb96f4669", + "name": "Spawner (222)", + "location": [7081, 381, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0aa73630-f7ee-48c5-b61a-24b0d6a44941", + "name": "Spawner (222)", + "location": [7002, 382, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d8016c7e-7b98-4c9b-b7ae-4247f0e578d4", + "name": "Spawner (222)", + "location": [6999, 352, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da82a0d4-2ddc-44cf-9f7e-227dbb1a0bdc", + "name": "Spawner (222)", + "location": [7047, 382, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb2d9013-b557-43c3-ab94-f69c92c68a89", + "name": "Spawner (222)", + "location": [7033, 385, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Bird", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d287da59-9954-4e44-b743-f859fdc73d1c", + "name": "Spawner (222)", + "location": [7032, 411, 7], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "34b6ad1f-4ab4-4eea-a295-36e62593cecf", + "name": "Spawner (222)", + "location": [7044, 429, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf55508a-adf0-4dca-9e67-37c947b28064", + "name": "Spawner (222)", + "location": [6986, 384, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1c30b2f9-fd07-4a84-9d98-21a9e0bf814e", + "name": "Spawner (222)", + "location": [7063, 410, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "65aa9d17-8718-4d62-9dea-53f965b6a798", + "name": "Spawner (222)", + "location": [7052, 386, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d4aab4aa-ae2e-4686-b282-39e05a6e0855", + "name": "Spawner (222)", + "location": [7035, 387, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "79c882ff-dab2-497a-9534-d3971b71734c", + "name": "Spawner (222)", + "location": [7007, 382, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "832487b1-6929-4cfd-bf10-8f8901a6af24", + "name": "Spawner (222)", + "location": [7087, 382, 1], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d233e758-f2b9-4f55-8e65-1441e452f34e", + "name": "Spawner (222)", + "location": [6992, 345, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6f6053e2-9f9b-4e29-b3f6-f4675c82e68b", + "name": "Spawner (222)", + "location": [7007, 360, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b09a16a0-6abb-420c-8995-24671b9183c6", + "name": "Spawner (222)", + "location": [5726, 3211, -2], + "map": "Felucca", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "425dfc37-404d-455d-8bf7-23a3f9857cad", + "name": "Spawner (222)", + "location": [5255, 4000, 37], + "map": "Felucca", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dcfdc32c-876a-4432-97ec-9d733f76578f", + "name": "Spawner (222)", + "location": [5267, 4012, 40], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Bull", "maxCount": 5, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/TownsPeople.json b/Distribution/Data/Spawns/post-uoml/felucca/TownsPeople.json similarity index 84% rename from Distribution/Data/Spawns/shared/felucca/TownsPeople.json rename to Distribution/Data/Spawns/post-uoml/felucca/TownsPeople.json index dc3ff19b3..ba001a47a 100644 --- a/Distribution/Data/Spawns/shared/felucca/TownsPeople.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/TownsPeople.json @@ -1,293 +1,6 @@ [ { - "$type": "Spawner", - "guid": "01057a94-5e22-4609-8162-aa947144da30", - "name": "Spawner (223)", - "location": [7031, 409, 7], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Alelle", "maxCount": 1, "probability": 100 }, - { "name": "Daelas", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "017be0ee-b224-4daf-9626-6bcb46a4b433", - "name": "Spawner (223)", - "location": [3815, 1217, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f1fbc08-b932-424b-956a-f78082db8525", - "name": "Spawner (223)", - "location": [627, 864, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f9c22dd-0738-4b96-b652-3171ed867cbd", - "name": "Spawner (223)", - "location": [1829, 2811, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d916eb1-ed25-4d3b-a9cd-995e97630ed8", - "name": "Spawner (223)", - "location": [1662, 1611, 19], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23e949aa-940a-4b5a-9ce9-762db82b58a6", - "name": "Spawner (223)", - "location": [2884, 692, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "259cc674-14e4-4fe2-b8fa-10af4610ffbc", - "name": "Spawner (223)", - "location": [4398, 1049, -2], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "34e31c96-47b7-4760-8446-d0a1f816d315", - "name": "Spawner (223)", - "location": [3652, 2619, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "380cd112-1791-4b59-8910-9ec257750d3e", - "name": "Spawner (223)", - "location": [2068, 2854, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "409f20ec-24eb-4ac4-9768-ee6467476f0c", - "name": "Spawner (223)", - "location": [1421, 1698, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "478d5bf8-5043-4d76-9a45-802677f0c30f", - "name": "Spawner (223)", - "location": [7010, 363, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "athialon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4de7c0e5-3203-4a68-a2d6-8e78652191fc", - "name": "Spawner (223)", - "location": [4451, 1153, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5ed933bb-802f-444d-855d-c499926302ab", - "name": "Spawner (223)", - "location": [1813, 2834, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6301492f-d4ed-4f24-afb2-cbbedb68afe6", - "name": "Spawner (223)", - "location": [3645, 2661, -2], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "63416791-9505-4a1e-ab72-91c9438612df", - "name": "Spawner (223)", - "location": [1483, 1760, -2], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "70250667-d0b4-488e-95e3-c660bcab2428", - "name": "Spawner (223)", - "location": [2254, 1207, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "710c1e19-1ae1-4131-99c6-184cb4636f03", - "name": "Spawner (223)", - "location": [3041, 827, -3], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "81cb310e-ae13-4fca-bd6e-3bb693d0ef0a", "name": "Spawner (223)", "location": [1400, 1623, 28], @@ -298,12 +11,136 @@ "team": 0, "homeRange": 0, "walkingRange": 1, - "entries": [ - { "name": "OrderGuard", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "b902cab2-6165-44ca-b583-814d9d3849a0", + "name": "Spawner (223)", + "location": [1400, 1628, 28], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e284edef-ae7d-4ed1-8620-441dfb4618c9", + "name": "Spawner (223)", + "location": [1521, 1457, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "ChaosGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e97a4cd6-1170-4ab3-abf2-58b0c8e241bd", + "name": "Spawner (223)", + "location": [1525, 1457, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "ChaosGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d916eb1-ed25-4d3b-a9cd-995e97630ed8", + "name": "Spawner (223)", + "location": [1662, 1611, 19], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "409f20ec-24eb-4ac4-9768-ee6467476f0c", + "name": "Spawner (223)", + "location": [1421, 1698, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70250667-d0b4-488e-95e3-c660bcab2428", + "name": "Spawner (223)", + "location": [2254, 1207, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4bb5474-dfc9-46a7-ba0d-9b117110cb0b", + "name": "Spawner (223)", + "location": [5290, 3987, 37], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "34e31c96-47b7-4760-8446-d0a1f816d315", + "name": "Spawner (223)", + "location": [3652, 2619, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ee365839-216c-4690-89cc-f5d83845f669", + "name": "Spawner (223)", + "location": [1330, 3778, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "84f9a05f-e99b-4ece-9156-1978b789c47f", "name": "Spawner (223)", "location": [3721, 2161, 20], @@ -314,12 +151,52 @@ "team": 0, "homeRange": 1, "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "9c4d2d6b-56e8-4473-959d-9cc9199c11e1", + "name": "Spawner (223)", + "location": [2515, 557, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4de7c0e5-3203-4a68-a2d6-8e78652191fc", + "name": "Spawner (223)", + "location": [4451, 1153, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ba1a481e-d5da-4445-af80-eee525b3e3c9", + "name": "Spawner (223)", + "location": [3773, 1308, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "8517bfc1-8190-42fb-bfec-f5a0a03488cc", "name": "Spawner (223)", "location": [5685, 3148, -4], @@ -330,80 +207,94 @@ "team": 0, "homeRange": 1, "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "87354a42-802a-4607-92e9-cd39a5bac46d", + "type": "Spawner", + "guid": "92425d73-e98b-46d3-b391-77582cda9302", "name": "Spawner (223)", - "location": [3749, 1404, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "892bd9fc-6385-46b9-9c20-dd9eeb4dcdc8", - "name": "Spawner (223)", - "location": [7082, 367, 0], + "location": [2891, 3474, 15], "map": "Felucca", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Olaeni", "maxCount": 1, "probability": 100 } - ] + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "8d5e0ca7-10d5-4475-9aa3-23bdc96210de", + "type": "Spawner", + "guid": "fa72d822-a3e5-43f5-9eb6-f800b3e49553", "name": "Spawner (223)", - "location": [7053, 338, 0], + "location": [603, 2141, 0], "map": "Felucca", - "count": 2, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 6, - "walkingRange": 6, - "entries": [ - { "name": "aneen", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 } - ] + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "8e6761b5-a94e-4a95-90da-c08d5966aeb5", + "type": "Spawner", + "guid": "c5d53f85-e6b8-4791-a77e-d241fab79d05", "name": "Spawner (223)", - "location": [7063, 350, 0], + "location": [1914, 2687, 0], "map": "Felucca", - "count": 3, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Bolaevin", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 } - ] + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "0f9c22dd-0738-4b96-b652-3171ed867cbd", + "name": "Spawner (223)", + "location": [1829, 2811, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a65b533b-cb58-4d72-8c9b-5375a7e7ad02", + "name": "Spawner (223)", + "location": [2895, 686, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0f1fbc08-b932-424b-956a-f78082db8525", + "name": "Spawner (223)", + "location": [627, 864, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "9119d23e-0857-4d0c-a03e-c345aafdd7fe", "name": "Spawner (223)", "location": [1399, 3743, -21], @@ -428,23 +319,25 @@ ] }, { - "$type": "Spawner", - "guid": "92425d73-e98b-46d3-b391-77582cda9302", + "type": "Spawner", + "guid": "63416791-9505-4a1e-ab72-91c9438612df", "name": "Spawner (223)", - "location": [2891, 3474, 15], + "location": [1483, 1760, -2], "map": "Felucca", - "count": 1, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 5, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9522412c-95ef-4067-9b3c-fd814717f9b4", "name": "Spawner (223)", "location": [1372, 3916, -2], @@ -462,23 +355,25 @@ ] }, { - "$type": "Spawner", - "guid": "9c4d2d6b-56e8-4473-959d-9cc9199c11e1", + "type": "Spawner", + "guid": "cb794f47-6ae2-4f92-809d-27c34fbd0b7b", "name": "Spawner (223)", - "location": [2515, 557, 0], + "location": [1126, 3688, -2], "map": "Felucca", - "count": 1, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 5, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a39b544b-76ab-424d-9f6d-a91cd55e3ed6", "name": "Spawner (223)", "location": [3674, 2291, -2], @@ -496,23 +391,79 @@ ] }, { - "$type": "Spawner", - "guid": "a65b533b-cb58-4d72-8c9b-5375a7e7ad02", + "type": "Spawner", + "guid": "259cc674-14e4-4fe2-b8fa-10af4610ffbc", "name": "Spawner (223)", - "location": [2895, 686, 0], + "location": [4398, 1049, -2], "map": "Felucca", - "count": 1, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 5, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "017be0ee-b224-4daf-9626-6bcb46a4b433", + "name": "Spawner (223)", + "location": [3815, 1217, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "87354a42-802a-4607-92e9-cd39a5bac46d", + "name": "Spawner (223)", + "location": [3749, 1404, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6301492f-d4ed-4f24-afb2-cbbedb68afe6", + "name": "Spawner (223)", + "location": [3645, 2661, -2], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "aac6fef9-46e2-44b2-9297-cd812c3b2c7b", "name": "Spawner (223)", "location": [2994, 3451, 16], @@ -530,89 +481,25 @@ ] }, { - "$type": "Spawner", - "guid": "adee85c7-830f-4ef1-8539-77b5006da4f9", + "type": "Spawner", + "guid": "ee30f8c4-8c52-4aaf-a974-2e4df4d0e28e", "name": "Spawner (223)", - "location": [2874, 692, 0], + "location": [673, 2235, -3], "map": "Felucca", - "count": 2, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "b4bb5474-dfc9-46a7-ba0d-9b117110cb0b", - "name": "Spawner (223)", - "location": [5290, 3987, 37], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b902cab2-6165-44ca-b583-814d9d3849a0", - "name": "Spawner (223)", - "location": [1400, 1628, 28], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 1, - "entries": [ - { "name": "OrderGuard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9f512c6-5b66-4f2a-9a03-6c0c99d7863f", - "name": "Spawner (223)", - "location": [7090, 387, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Aluniol", "maxCount": 1, "probability": 100 }, - { "name": "Rebinil", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba1a481e-d5da-4445-af80-eee525b3e3c9", - "name": "Spawner (223)", - "location": [3773, 1308, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "bba7781a-c2fd-486f-8bfa-587c809347d5", "name": "Spawner (223)", "location": [573, 2246, 0], @@ -630,23 +517,171 @@ ] }, { - "$type": "Spawner", - "guid": "c5d53f85-e6b8-4791-a77e-d241fab79d05", + "type": "Spawner", + "guid": "380cd112-1791-4b59-8910-9ec257750d3e", "name": "Spawner (223)", - "location": [1914, 2687, 0], + "location": [2068, 2854, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "710c1e19-1ae1-4131-99c6-184cb4636f03", + "name": "Spawner (223)", + "location": [3041, 827, -3], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d95bb2d0-7f44-4ff3-9c87-93d0ba887e86", + "name": "Spawner (223)", + "location": [631, 883, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ed933bb-802f-444d-855d-c499926302ab", + "name": "Spawner (223)", + "location": [1813, 2834, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e134aa6f-68a5-48c8-819f-64e2806f8877", + "name": "Spawner (223)", + "location": [2875, 675, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adee85c7-830f-4ef1-8539-77b5006da4f9", + "name": "Spawner (223)", + "location": [2874, 692, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23e949aa-940a-4b5a-9ce9-762db82b58a6", + "name": "Spawner (223)", + "location": [2884, 692, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "892bd9fc-6385-46b9-9c20-dd9eeb4dcdc8", + "name": "Spawner (223)", + "location": [7082, 367, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 5, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Olaeni", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e6761b5-a94e-4a95-90da-c08d5966aeb5", + "name": "Spawner (223)", + "location": [7063, 350, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } + { "name": "Bolaevin", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "478d5bf8-5043-4d76-9a45-802677f0c30f", + "name": "Spawner (223)", + "location": [7010, 363, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "athialon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "c7251678-03dd-4030-8316-2529b932fecb", "name": "Spawner (223)", "location": [7048, 382, 0], @@ -667,7 +702,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c9a63304-f0da-4144-9982-f8ce6d4c1914", "name": "Spawner (223)", "location": [6989, 342, 0], @@ -678,142 +713,57 @@ "team": 0, "homeRange": 8, "walkingRange": 8, + "entries": [{ "name": "Tyeelor", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d5e0ca7-10d5-4475-9aa3-23bdc96210de", + "name": "Spawner (223)", + "location": [7053, 338, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, "entries": [ - { "name": "Tyeelor", "maxCount": 1, "probability": 100 } + { "name": "aneen", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "cb794f47-6ae2-4f92-809d-27c34fbd0b7b", + "type": "Spawner", + "guid": "01057a94-5e22-4609-8162-aa947144da30", "name": "Spawner (223)", - "location": [1126, 3688, -2], + "location": [7031, 409, 7], "map": "Felucca", - "count": 3, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 20, + "walkingRange": 10, "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } + { "name": "Alelle", "maxCount": 1, "probability": 100 }, + { "name": "Daelas", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "d95bb2d0-7f44-4ff3-9c87-93d0ba887e86", + "type": "Spawner", + "guid": "b9f512c6-5b66-4f2a-9a03-6c0c99d7863f", "name": "Spawner (223)", - "location": [631, 883, 0], + "location": [7090, 387, 0], "map": "Felucca", - "count": 3, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 20, + "homeRange": 8, + "walkingRange": 8, "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e134aa6f-68a5-48c8-819f-64e2806f8877", - "name": "Spawner (223)", - "location": [2875, 675, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e284edef-ae7d-4ed1-8620-441dfb4618c9", - "name": "Spawner (223)", - "location": [1521, 1457, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 1, - "entries": [ - { "name": "ChaosGuard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e97a4cd6-1170-4ab3-abf2-58b0c8e241bd", - "name": "Spawner (223)", - "location": [1525, 1457, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 1, - "entries": [ - { "name": "ChaosGuard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ee30f8c4-8c52-4aaf-a974-2e4df4d0e28e", - "name": "Spawner (223)", - "location": [673, 2235, -3], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ee365839-216c-4690-89cc-f5d83845f669", - "name": "Spawner (223)", - "location": [1330, 3778, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fa72d822-a3e5-43f5-9eb6-f800b3e49553", - "name": "Spawner (223)", - "location": [603, 2141, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } + { "name": "Aluniol", "maxCount": 1, "probability": 100 }, + { "name": "Rebinil", "maxCount": 1, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/TrinsicPassage.json b/Distribution/Data/Spawns/post-uoml/felucca/TrinsicPassage.json similarity index 96% rename from Distribution/Data/Spawns/shared/felucca/TrinsicPassage.json rename to Distribution/Data/Spawns/post-uoml/felucca/TrinsicPassage.json index 944c38b7e..2832c2bb9 100644 --- a/Distribution/Data/Spawns/shared/felucca/TrinsicPassage.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/TrinsicPassage.json @@ -1,83 +1,6 @@ [ { - "$type": "Spawner", - "guid": "047a7695-7bd9-46ad-852d-f84b2895b843", - "name": "Spawner (224)", - "location": [5912, 1326, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37ea1620-9487-40df-8035-e76b2443a7b2", - "name": "Spawner (224)", - "location": [5915, 1302, 2], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8190243a-f39d-48b2-9df3-9b00b6e418d4", - "name": "Spawner (224)", - "location": [5956, 1310, -2], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0481877-f573-4c00-af1f-c1f5873f4bbb", - "name": "Spawner (224)", - "location": [5940, 1356, -2], - "map": "Felucca", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "c08391fb-b609-4c8d-be85-2d3986f1639a", "name": "Spawner (224)", "location": [5976, 1387, -22], @@ -97,7 +20,67 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "c0481877-f573-4c00-af1f-c1f5873f4bbb", + "name": "Spawner (224)", + "location": [5940, 1356, -2], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "047a7695-7bd9-46ad-852d-f84b2895b843", + "name": "Spawner (224)", + "location": [5912, 1326, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8190243a-f39d-48b2-9df3-9b00b6e418d4", + "name": "Spawner (224)", + "location": [5956, 1310, -2], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "d661a6de-3520-464f-994f-77ff738ff5bd", "name": "Spawner (224)", "location": [5999, 1327, 10], @@ -115,5 +98,22 @@ { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, { "name": "LavaSnake", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "37ea1620-9487-40df-8035-e76b2443a7b2", + "name": "Spawner (224)", + "location": [5915, 1302, 2], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/post-uoml/felucca/Vendors.json b/Distribution/Data/Spawns/post-uoml/felucca/Vendors.json index 84104b52e..906c4f4a4 100644 --- a/Distribution/Data/Spawns/post-uoml/felucca/Vendors.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Vendors.json @@ -1,6 +1,5209 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "da1bd33e-e6d5-4311-8177-9443fe6c1c63", + "name": "Spawner (225)", + "location": [1685, 2985, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Herbalist", "maxCount": 2, "probability": 100 }, + { "name": "Mage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0df23a73-3ee4-4f00-87ce-576069c5ce66", + "name": "Spawner (225)", + "location": [1451, 1679, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d4cb40b-fc42-4aac-8aaa-29f5c98a2e2e", + "name": "Spawner (225)", + "location": [1602, 1712, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fa1e658-1d2d-4d6b-8b45-49c4e941c223", + "name": "Spawner (225)", + "location": [1470, 1578, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7aa8ce1b-2fda-4a02-a725-62342dc96905", + "name": "Spawner (225)", + "location": [1547, 1659, 26], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "30a5dc4e-3f06-4dd4-b3aa-d66d55905b62", + "name": "Spawner (225)", + "location": [1620, 1586, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88bafb3d-aa60-4138-8d7d-2e7c9e733efd", + "name": "Spawner (225)", + "location": [1430, 1597, 20], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "143a413e-2a69-4e60-a5b2-892becd26dac", + "name": "Spawner (225)", + "location": [1485, 1550, 30], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ced5124a-83f8-4d38-926b-ba8372a19275", + "name": "Spawner (225)", + "location": [1296, 1759, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "87de110f-213d-4275-b7f0-82e7991742ff", + "name": "Spawner (225)", + "location": [1481, 1584, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e59444cb-2d90-4137-85bc-e4e15e18e6a9", + "name": "Spawner (225)", + "location": [1493, 1616, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0f3b0c94-b2e2-4c22-99d5-c559758a9043", + "name": "Spawner (225)", + "location": [1422, 1654, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ac62908-35f4-43c6-a40a-dba3a693666f", + "name": "Spawner (225)", + "location": [1547, 1768, 10], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7ff0fc47-17c9-4073-a0e0-d1c7520cf9bb", + "name": "Spawner (225)", + "location": [1341, 1733, 20], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "hirefighter", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4381baac-5e70-48a1-bacb-99ff2a41663a", + "name": "Spawner (225)", + "location": [1585, 1591, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "afe473da-540a-477d-ad3b-233b8e7e4a89", + "name": "Spawner (225)", + "location": [1637, 1693, 37], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10283e6b-f2de-47b8-b6c8-21ee93bbbaf7", + "name": "Spawner (225)", + "location": [1431, 1612, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "347d6ccf-8681-4dc3-b6be-0b0edecb9d03", + "name": "Spawner (225)", + "location": [1498, 1691, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6abb0c5-a18d-4f82-bcf8-77c5d00edf24", + "name": "Spawner (225)", + "location": [1470, 1765, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "546c63ee-9eca-4c1c-8405-108d7eb1df12", + "name": "Spawner (225)", + "location": [1409, 1590, 42], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "42f7a139-1c12-457b-bfd7-5f50fc33a57e", + "name": "Spawner (225)", + "location": [1349, 1778, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Blacksmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f29e3b5a-eb24-477a-adb1-3d1c7cfda113", + "name": "Spawner (225)", + "location": [1469, 1668, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22117025-4e5b-4bc6-a65c-3803cea02917", + "name": "Spawner (225)", + "location": [1510, 1543, 25], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9240f638-d05a-4e70-93bc-8be1c163e508", + "name": "Spawner (225)", + "location": [1427, 1716, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81f5c4b0-e089-4a51-b311-56a700b2845d", + "name": "Spawner (225)", + "location": [1449, 1723, 6], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "59739d59-4955-4749-b91e-0dc56587d9d4", + "name": "Spawner (225)", + "location": [1650, 1608, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "06054f54-b0fa-4f88-9306-92972f4dcca4", + "name": "Spawner (225)", + "location": [1498, 1659, 27], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3757ec1-febf-4562-9ae6-62181a418b8a", + "name": "Spawner (225)", + "location": [1425, 1690, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3535310b-bd5f-4938-92a8-07969bc0cdf5", + "name": "Spawner (225)", + "location": [1494, 1715, 6], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adb68e3c-d5a4-45f0-9f22-afa40bd604c6", + "name": "Spawner (225)", + "location": [1650, 1642, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "82487046-2e78-49ae-ba4b-0a79b8247eb5", + "name": "Spawner (225)", + "location": [1450, 1617, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "098512b2-9b9c-4dd1-95aa-631d5c79c220", + "name": "Spawner (225)", + "location": [1418, 1547, 30], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f769d5d8-1f95-4671-be31-0082fd16c61c", + "name": "Spawner (225)", + "location": [1471, 1611, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd54442b-2a68-421a-b113-f38a66849e77", + "name": "Spawner (225)", + "location": [1447, 1647, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ac519e78-a9e7-4c70-b9e2-ec7ed9d6fbf7", + "name": "Spawner (225)", + "location": [1590, 1654, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2eac5045-fa01-46a1-aab1-19d09c2bd246", + "name": "Spawner (225)", + "location": [1455, 1557, 30], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Bard", "maxCount": 1, "probability": 100 }, + { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3b4c2e06-e89a-484e-9afe-d1c285a46db6", + "name": "Spawner (225)", + "location": [1467, 1686, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec68d28f-8601-4346-a801-0d1ff604d008", + "name": "Spawner (225)", + "location": [1474, 1597, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cab9dba3-559b-4ff0-9d85-2b134c813ca5", + "name": "Spawner (225)", + "location": [1417, 1578, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4d83000-0cd1-4bca-bcfd-4f5c3e7fa942", + "name": "Spawner (225)", + "location": [1458, 1525, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c778c01a-96fe-49fe-af2c-6788ccfb36d0", + "name": "Spawner (225)", + "location": [1416, 1754, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50c3d9c7-0a08-46b4-af3c-9ef791c012ee", + "name": "Spawner (225)", + "location": [1405, 1810, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Mage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eef4c3f5-0a7b-49da-a496-5f20882af7ad", + "name": "Spawner (225)", + "location": [1482, 1761, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "298aca7b-3047-4808-8b9e-e972a0bc4b08", + "name": "Spawner (225)", + "location": [1228, 1572, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9b34f6d-b58d-4106-85d0-c38fa2cd608d", + "name": "Spawner (225)", + "location": [1355, 1754, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 14, + "entries": [ + { "name": "OrderGuard", "maxCount": 1, "probability": 100 }, + { "name": "hirefighter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c55470a0-bfa8-4290-8ec0-c0c642e251e0", + "name": "Spawner (225)", + "location": [1243, 1699, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1a128fa-7b72-403f-9bb3-14a4f26d2c99", + "name": "Spawner (225)", + "location": [1351, 1816, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "EscortableMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fd5e6859-407b-4ec2-bee4-88f1b88f0fbb", + "name": "Spawner (225)", + "location": [1306, 1804, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 6, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3327ba9f-5ab2-4203-81c3-10ed191fe8a8", + "name": "Spawner (225)", + "location": [1304, 1826, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "Peasant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "56c75ca9-bdbc-4709-bfae-6bb56308f2da", + "name": "Spawner (225)", + "location": [1362, 1825, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 12, + "entries": [{ "name": "Artist", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f8fb6522-0e64-4dd6-8ef2-d6d5c9dcc3b5", + "name": "Spawner (225)", + "location": [1333, 1808, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "escortablewanderinghealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2ffb4f73-598f-47b4-b4e9-e40c7a7ab424", + "name": "Spawner (225)", + "location": [1150, 1619, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59b02f45-35c8-4b36-a55b-431fbb8d3254", + "name": "Spawner (225)", + "location": [1149, 1547, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "67acd17b-db93-4bb1-9f67-f4c9015a1888", + "name": "Spawner (225)", + "location": [1295, 1773, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "BrideGroom", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "456fbee0-848e-4ea7-b528-a7124166beb7", + "name": "Spawner (225)", + "location": [1316, 1747, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "hirefighter", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b9ebd4f7-d90e-4664-be98-a53c91926c79", + "name": "Spawner (225)", + "location": [1231, 1714, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "hirepeasant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8018ee5a-2f60-4dad-8ac7-8678c56000ac", + "name": "Spawner (225)", + "location": [1190, 1707, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 8, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "35225c44-2e25-43d6-a2f3-82ef04806cbd", + "name": "Spawner (225)", + "location": [1427, 1726, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "hirebard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d6a16c9-b172-40da-9573-4c979fa74ab3", + "name": "Spawner (225)", + "location": [1438, 1761, -2], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [ + { "name": "hiresailor", "maxCount": 1, "probability": 100 }, + { "name": "Fisherman", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "667b1e85-dea9-4c92-a796-9c8fe4958ac0", + "name": "Spawner (225)", + "location": [1477, 1741, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "hiresailor", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b060374a-df96-4f0b-a861-d2f50c651018", + "name": "Spawner (225)", + "location": [1436, 1752, 14], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "HarborMaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2dc8250b-995a-4eae-a615-2c7b0037fd64", + "name": "Spawner (225)", + "location": [1434, 1717, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Merchant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1002eb76-82f2-4dc6-863b-8c1720c1294c", + "name": "Spawner (225)", + "location": [1388, 1655, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a48c8b3d-d87d-4036-846f-e54593690566", + "name": "Spawner (225)", + "location": [1362, 1574, 30], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8972d5d6-a75e-4dcb-85f4-565dd1a644e6", + "name": "Spawner (225)", + "location": [1388, 1588, 30], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "14284f7f-ef5e-4dff-a0aa-bf84616d15a8", + "name": "Spawner (225)", + "location": [2715, 2098, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44c610bd-fae7-481c-9174-9a3e34068786", + "name": "Spawner (225)", + "location": [2659, 2194, 4], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "ThiefGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cc8af24a-c3f4-4029-b238-a76b951d104e", + "name": "Spawner (225)", + "location": [2680, 2238, 2], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce01eeba-f0ab-459c-8c0c-f998f5caaf30", + "name": "Spawner (225)", + "location": [2737, 2250, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13a55ac1-98b8-4c8e-ae53-5408dc9aa8bb", + "name": "Spawner (225)", + "location": [2731, 2192, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d74a89d-c5fa-49d6-8a1b-a52aa1484f5e", + "name": "Spawner (225)", + "location": [2627, 2100, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad85e0e4-4493-4c09-8fa7-61e108db38ab", + "name": "Spawner (225)", + "location": [2752, 2155, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "37ea3966-1157-4d85-a26e-1a9f3008dd75", + "name": "Spawner (225)", + "location": [2707, 2178, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75b0732c-4590-4031-88c0-2ad4750e7445", + "name": "Spawner (225)", + "location": [2634, 2083, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc9225d4-8e01-4c04-ac86-0de58d5e6efc", + "name": "Spawner (225)", + "location": [2709, 2130, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0834f2e-139b-4b00-b68a-b46b66301b5c", + "name": "Spawner (225)", + "location": [2216, 1167, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "25c1334d-8fb1-442a-9315-f549ed3c5254", + "name": "Spawner (225)", + "location": [2256, 1181, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1aa3d9a8-9ace-49cb-969e-6a4350b5ba08", + "name": "Spawner (225)", + "location": [2216, 1192, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62ab8dc9-0f3e-411a-aba1-d2449a7d882c", + "name": "Spawner (225)", + "location": [2245, 1231, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f865ca2-1ac4-4612-9079-18dd99f2f42d", + "name": "Spawner (225)", + "location": [1512, 3989, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab871081-1d9a-4195-b251-88bd12201f99", + "name": "Spawner (225)", + "location": [1504, 3693, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ef4209ee-affa-476d-a190-9121f8cd3e18", + "name": "Spawner (225)", + "location": [1125, 3687, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d9ab6c79-9b10-49f4-ae60-78022c502dff", + "name": "Spawner (225)", + "location": [1447, 3981, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "51c50c2c-1ad2-4b38-bfd3-e3680b82624e", + "name": "Spawner (225)", + "location": [1452, 3747, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf5b64a0-83e5-4959-851a-990e7173c143", + "name": "Spawner (225)", + "location": [1441, 3719, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3b6c12ad-854f-409f-a281-f04dce59330f", + "name": "Spawner (225)", + "location": [1419, 3859, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f32b1627-e330-440e-b8da-982a8b308935", + "name": "Spawner (225)", + "location": [1356, 3780, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8521a0ed-ffd6-430e-9cd6-b1e9dc43713f", + "name": "Spawner (225)", + "location": [1364, 3732, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8ed2be48-c634-4ef3-82a4-c335d9b2e8f4", + "name": "Spawner (225)", + "location": [1475, 3865, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a28db63a-c3b0-44e8-a446-7d33f63c081e", + "name": "Spawner (225)", + "location": [1354, 3754, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6602517d-493c-41c3-8bfb-2738aa2a2dcd", + "name": "Spawner (225)", + "location": [1386, 3825, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b37905b7-bed7-4376-bead-f9b7bdba21c7", + "name": "Spawner (225)", + "location": [1450, 4026, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2dd708a7-fa32-46ad-a558-56d4464a6442", + "name": "Spawner (225)", + "location": [1395, 3705, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3825dd92-a301-4bf0-9d73-c29fdc1b0043", + "name": "Spawner (225)", + "location": [1435, 3820, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5037c29-a88d-4d7c-bfd0-7a98d12e3b81", + "name": "Spawner (225)", + "location": [1404, 3802, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd42cc6c-c177-4292-b22f-8e150bc833d7", + "name": "Spawner (225)", + "location": [1452, 3770, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "053c4263-9e79-4067-ab30-8576df3e9b00", + "name": "Spawner (225)", + "location": [1456, 3850, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfc4ea82-efcb-455c-b01a-482e43313f65", + "name": "Spawner (225)", + "location": [1317, 3773, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03bc089d-0286-40d7-a5e1-dc425e0c8f53", + "name": "Spawner (225)", + "location": [1372, 3908, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e51c1b00-e54a-4e00-837e-e0d86f596722", + "name": "Spawner (225)", + "location": [1416, 3779, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bae0089-ee4c-4961-9b8b-a04cb40f3fff", + "name": "Spawner (225)", + "location": [1387, 3771, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4427c2d2-613b-4859-a8e0-c14df5bd0f52", + "name": "Spawner (225)", + "location": [1425, 3981, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69144e2b-49b0-4027-ad34-f12811dae9a9", + "name": "Spawner (225)", + "location": [1454, 4003, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d673deb5-d4ef-4f50-8b5b-9b55a00bd7b3", + "name": "Spawner (225)", + "location": [1360, 3815, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eb3be177-f027-491b-85d2-099a52c52d14", + "name": "Spawner (225)", + "location": [1442, 3802, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ef25d3d-9926-4bcb-b14e-b043e23982af", + "name": "Spawner (225)", + "location": [1437, 3750, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "333fe588-e295-4247-b8c6-c0b492f161cc", + "name": "Spawner (225)", + "location": [3699, 2163, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c688221a-a1d6-4c2c-80e2-5d1bd06c6c81", + "name": "Spawner (225)", + "location": [3666, 2235, 20], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b3d26b3-c709-4ed0-bd77-a257a8ca84e2", + "name": "Spawner (225)", + "location": [3720, 2126, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39db05f3-b04e-4ff7-911c-49a9cd081aa5", + "name": "Spawner (225)", + "location": [3755, 2227, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5cc88170-da30-4eb5-a8c7-19e8f29caa86", + "name": "Spawner (225)", + "location": [3734, 2149, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "de27e5cc-f1d9-45b2-ab09-e09b520dcd28", + "name": "Spawner (225)", + "location": [3683, 2171, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "778b3fd0-9f0f-4e70-9923-917b2b268fd1", + "name": "Spawner (225)", + "location": [3661, 2183, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d19e8b0e-5ee0-4e21-b092-b4f5de33cb69", + "name": "Spawner (225)", + "location": [3683, 2252, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "390c60c0-4fd2-43b8-a9a2-5c194dc235a7", + "name": "Spawner (225)", + "location": [3667, 2252, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebf29920-68ce-4aee-8a6d-84c0576c4c0d", + "name": "Spawner (225)", + "location": [3727, 2224, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a3d9a50a-d415-4249-a6e0-c4c6cbe4d6de", + "name": "Spawner (225)", + "location": [3687, 2227, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "19957abb-f841-4c9a-b52a-ac8c19cd0ab0", + "name": "Spawner (225)", + "location": [3674, 2289, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4cc10b14-a086-4781-84ff-f656af24f44e", + "name": "Spawner (225)", + "location": [3665, 2140, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f22df3f4-b9f0-4cf5-b444-c01f001f01bb", + "name": "Spawner (225)", + "location": [3699, 2218, 20], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec953969-18e0-4b71-949e-3b37f5a97c1d", + "name": "Spawner (225)", + "location": [3703, 2249, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ff08692-d784-4f17-8291-8249aaf2dc6e", + "name": "Spawner (225)", + "location": [2450, 428, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Provisioner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "453d3fec-1f00-4ae4-9633-5c71510d64f8", + "name": "Spawner (225)", + "location": [2450, 428, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Cobbler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "05a68019-c07b-428f-8e99-90f5ace263b1", + "name": "Spawner (225)", + "location": [2438, 410, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "16fdba4a-b4ce-4017-9f6d-6b535ae8350a", + "name": "Spawner (225)", + "location": [2526, 375, 23], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d6634bfd-e77d-4595-b836-4ef3ff685e5a", + "name": "Spawner (225)", + "location": [2522, 524, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6101131e-6247-4e30-b693-0c7a26b09cec", + "name": "Spawner (225)", + "location": [2526, 546, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5fec0502-6f90-4e48-b0ef-fd58b3c8ab5e", + "name": "Spawner (225)", + "location": [2513, 477, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Architect", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f98a9a64-b87c-4f79-ad89-f1fbe0854cfc", + "name": "Spawner (225)", + "location": [2533, 572, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95195876-4c1c-422a-962e-31ce7ea8e3f8", + "name": "Spawner (225)", + "location": [2503, 552, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "89aa4fc3-f834-4499-bbc6-ac47b395ce13", + "name": "Spawner (225)", + "location": [2475, 397, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e65f0cdb-3a59-43a5-9e14-6088e448dd92", + "name": "Spawner (225)", + "location": [2471, 564, 5], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "902148e8-f197-478a-95e5-25e0f3227606", + "name": "Spawner (225)", + "location": [2461, 457, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "997c3a7c-c590-4bd7-a2a7-f3b839ad0fd6", + "name": "Spawner (225)", + "location": [2505, 432, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bbe4e681-6b3d-4ecd-912c-d3fca9eba8d2", + "name": "Spawner (225)", + "location": [2577, 599, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a8fd99e-04b8-4dba-9350-410c0db296b0", + "name": "Spawner (225)", + "location": [2455, 487, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9027faa4-bbd1-4feb-8ba7-59f0c4b9c0be", + "name": "Spawner (225)", + "location": [2424, 555, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Bard", "maxCount": 1, "probability": 100 }, + { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32955c0b-65f3-4a59-ad71-de6854ec16b9", + "name": "Spawner (225)", + "location": [2478, 427, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f10e0fef-f74c-41c5-857f-00d429c69d60", + "name": "Spawner (225)", + "location": [2513, 477, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1fc3ffc-4245-429f-a3cb-7e3d46a150bc", + "name": "Spawner (225)", + "location": [4458, 1060, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af98980d-fb5a-44a9-961a-b32c6295132d", + "name": "Spawner (225)", + "location": [4417, 1064, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04c6be93-3c01-460a-9035-cc4a78774b5c", + "name": "Spawner (225)", + "location": [4401, 1165, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4493f83f-d73b-4691-b01e-cda4b8046621", + "name": "Spawner (225)", + "location": [4440, 1162, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8f7e0a08-6898-4aaa-b655-e58bb3c88042", + "name": "Spawner (225)", + "location": [4545, 860, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "27f0fb46-5d81-4688-8e10-7cdebe46fc32", + "name": "Spawner (225)", + "location": [4481, 1085, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "51487317-ca09-48ac-a372-7acae8ca4ca4", + "name": "Spawner (225)", + "location": [4395, 1137, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "43be54dd-c46c-4d7b-819e-a792890ff138", + "name": "Spawner (225)", + "location": [4471, 1156, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dea7fe8f-3b6f-4679-9650-4cee301d7b71", + "name": "Spawner (225)", + "location": [4416, 1133, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bcc5e683-0c16-470d-ac15-e9a597e0e1b6", + "name": "Spawner (225)", + "location": [4392, 1068, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9d559c33-8b55-426e-8ebc-e767487c32f0", + "name": "Spawner (225)", + "location": [4416, 1085, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8fb2794d-96c6-4293-bfd6-4a24e772dbd2", + "name": "Spawner (225)", + "location": [4392, 1117, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cf06d28e-ba5f-47dd-85ad-f49dddb548ef", + "name": "Spawner (225)", + "location": [4406, 1038, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e62476e-5f70-49e7-ae16-f8ed9ddd4c53", + "name": "Spawner (225)", + "location": [4394, 1083, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c826160-cc14-4c24-91ac-8fac400a9d4c", + "name": "Spawner (225)", + "location": [4409, 1111, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "039694fe-6a0b-41bf-a561-1391dfb2cb14", + "name": "Spawner (225)", + "location": [4484, 1064, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "74bc4555-d00a-4214-bb20-9c37ddffa4d6", + "name": "Spawner (225)", + "location": [4448, 1090, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a4e55c2b-7487-4425-b114-f517851ddaab", + "name": "Spawner (225)", + "location": [3554, 1202, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "701c4bc8-b92a-4d2e-9f31-bb0e73157bc7", + "name": "Spawner (225)", + "location": [3731, 1320, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f4d6fd78-de3c-47bf-aa20-3e784327e814", + "name": "Spawner (225)", + "location": [3700, 1214, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 0, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1411c07d-db5c-45db-aef9-5ca1c7f9c769", + "name": "Spawner (225)", + "location": [3729, 1258, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "134687d6-0adb-4dac-a522-1bf22a1c76c7", + "name": "Spawner (225)", + "location": [3774, 1265, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4437c2af-dd86-4295-9b55-3d45746cd99a", + "name": "Spawner (225)", + "location": [3545, 1178, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Tanner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "64afa061-436e-4816-acc8-b4b932a2f31f", + "name": "Spawner (225)", + "location": [3764, 1317, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59707794-b18e-4101-9c64-f28940881270", + "name": "Spawner (225)", + "location": [3740, 1197, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "BardGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "267e1e11-d6b0-44cb-a161-c8c3b962d1af", + "name": "Spawner (225)", + "location": [3778, 1172, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8883ad81-f99d-4ccb-8d1d-65ada7848c65", + "name": "Spawner (225)", + "location": [3769, 1218, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "caff4a2a-380d-4672-b915-74bd60aab37e", + "name": "Spawner (225)", + "location": [3546, 1185, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e363885b-3519-4ff2-b737-f7da5a04aab4", + "name": "Spawner (225)", + "location": [3546, 1194, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "faaed6d7-6878-44d7-a893-1c0d12dc9e0e", + "name": "Spawner (225)", + "location": [3555, 1171, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d14b9830-d3cc-4717-af11-51f9bdd77aef", + "name": "Spawner (225)", + "location": [3545, 1178, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Furtrader", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "46c5c4a0-48d4-4e36-88ae-cbf18533e56b", + "name": "Spawner (225)", + "location": [3647, 2681, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "992f2215-28dd-466b-aa19-25b5d0089c1a", + "name": "Spawner (225)", + "location": [3612, 2466, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "912580fa-084c-4ad0-ae90-e3dec3330cad", + "name": "Spawner (225)", + "location": [3628, 2541, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47601b12-bda9-4dab-92db-13ba9022ebaf", + "name": "Spawner (225)", + "location": [3667, 2586, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56b03849-fb73-4883-8368-9f0a01df0403", + "name": "Spawner (225)", + "location": [3708, 2649, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "22666a8e-0c05-4ebd-b93e-92411a48d9c9", + "name": "Spawner (225)", + "location": [3672, 2654, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed469ec1-25ae-4d8c-ab82-32d94428571c", + "name": "Spawner (225)", + "location": [3632, 2640, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "99dbe037-b50c-43b1-b490-94940fbe4cf9", + "name": "Spawner (225)", + "location": [3695, 2511, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7acbc3b8-8176-40ce-8d76-848b7ce1bedc", + "name": "Spawner (225)", + "location": [3665, 2531, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "BardGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e558f44a-c896-4b7a-ac0e-d45a0c1672b3", + "name": "Spawner (225)", + "location": [3602, 2615, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "279cfcf8-24e9-4390-a4e2-a507e02e89ef", + "name": "Spawner (225)", + "location": [3673, 2614, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9808a90b-2ba1-4951-a011-a5ca40a24c8b", + "name": "Spawner (225)", + "location": [3632, 2595, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "19fad4db-4751-4907-8561-8ef0a0d22959", + "name": "Spawner (225)", + "location": [3629, 2608, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5a5d0c9-fa97-4709-9804-097f6bb733ce", + "name": "Spawner (225)", + "location": [3636, 2565, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdb81c0f-d54b-4054-91a3-485614472f6f", + "name": "Spawner (225)", + "location": [3610, 2577, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6f32b48f-e661-4e16-a5c6-1755a1c77e57", + "name": "Spawner (225)", + "location": [2975, 3353, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4a3ad267-fe69-4133-8021-6a46265945e8", + "name": "Spawner (225)", + "location": [2906, 3485, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "31475343-bb81-44f0-9ece-86234032a185", + "name": "Spawner (225)", + "location": [3031, 3350, 35], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21ea5650-c7b3-48d0-b78b-1195290b17f8", + "name": "Spawner (225)", + "location": [3018, 3426, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfcb2873-b661-44a8-b500-8cab42fefcf1", + "name": "Spawner (225)", + "location": [2880, 3472, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "80a970ea-b163-4589-a626-c0fc6f875350", + "name": "Spawner (225)", + "location": [2997, 3428, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41bac877-ca53-4a5f-aa2d-12de82f8b3f4", + "name": "Spawner (225)", + "location": [3036, 3464, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c55b75f2-c734-40c2-b4a7-43a5218bf9e5", + "name": "Spawner (225)", + "location": [3003, 3355, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61506934-e743-4d80-8c62-daf3fa9c6648", + "name": "Spawner (225)", + "location": [2905, 3517, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "56c50bc0-b5de-45d5-b9ed-3da67c3efe73", + "name": "Spawner (225)", + "location": [3051, 3367, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab9d83de-4639-45e3-be08-04a7536fc2be", + "name": "Spawner (225)", + "location": [2883, 3502, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e677e3f4-5885-4dfe-baeb-ebe4650a94be", + "name": "Spawner (225)", + "location": [2992, 3451, 16], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fd956d04-c3b5-4033-99c9-d3ce4f16fbf6", + "name": "Spawner (225)", + "location": [3008, 3389, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b1a005b4-d2a1-4b4d-a4e9-ca5adfda51ac", + "name": "Spawner (225)", + "location": [2940, 3500, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f707815-9566-4210-a8e6-51f5affda55b", + "name": "Spawner (225)", + "location": [3058, 3400, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bedd9806-6e44-4d6b-98e2-e776ed3fb16b", + "name": "Spawner (225)", + "location": [3007, 3408, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7cbf932d-ba9f-4a37-8ff3-068952fcbe6c", + "name": "Spawner (225)", + "location": [3013, 3353, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9bfb07a-33e5-41c1-8e89-40a429ef8d7c", + "name": "Spawner (225)", + "location": [2967, 3408, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3037b09f-4d00-4b90-b77f-4a9c77d446bd", + "name": "Spawner (225)", + "location": [2972, 3425, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60ff9933-37fb-44e0-8794-2dfc53969609", + "name": "Spawner (225)", + "location": [3009, 3455, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bbc2f39-f8bf-4ff8-8cce-8128c08ae64b", + "name": "Spawner (225)", + "location": [2940, 3410, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "24029208-9dc1-44d6-829c-f60e05b7089e", + "name": "Spawner (225)", + "location": [630, 2194, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b6efd78-0c41-4bd1-b51c-35e870f89a5c", + "name": "Spawner (225)", + "location": [562, 2148, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "RangerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ca51a670-1297-4de7-b01a-ed82119ceb90", + "name": "Spawner (225)", + "location": [554, 2178, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "058219b9-095c-409b-87b0-ffff99c4e0c9", + "name": "Spawner (225)", + "location": [650, 2178, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af2d2a82-247d-4cba-a657-6fb4e2b798f4", + "name": "Spawner (225)", + "location": [656, 2235, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "34a7b4b8-cdad-46e2-b1c7-5aebe2fa41d1", + "name": "Spawner (225)", + "location": [578, 2227, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f19cc32a-4597-4070-8e43-f322ce4aa618", + "name": "Spawner (225)", + "location": [592, 2277, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "017536ca-0a2b-45c1-90a2-e6279f1a2e79", + "name": "Spawner (225)", + "location": [587, 2146, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8996375-dc5d-4fd9-a1bf-9931c0a1fe58", + "name": "Spawner (225)", + "location": [570, 2121, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc7a6537-745e-4573-8b1f-611e5de6b834", + "name": "Spawner (225)", + "location": [590, 2205, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e4959103-c32c-4f31-8668-3d02188253eb", + "name": "Spawner (225)", + "location": [582, 2186, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fe70d7f3-4e67-44ab-9be0-c714538b3db2", + "name": "Spawner (225)", + "location": [627, 2163, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bacf79a8-939c-416a-b7f3-56f5c4b4f247", + "name": "Spawner (225)", + "location": [601, 2235, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d40d06e-44aa-40b5-8e01-5f965c00bb67", + "name": "Spawner (225)", + "location": [611, 2157, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dab5532c-bfaa-4e06-b2e9-9181a087402a", + "name": "Spawner (225)", + "location": [650, 2161, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22214376-1f4d-4e5a-a3c5-0041c4302fa3", + "name": "Spawner (225)", + "location": [620, 2218, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2be191ab-063f-49a5-8ecc-71c7b52e05b2", + "name": "Spawner (225)", + "location": [602, 2180, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8de04b9c-7762-426e-b7c6-79b531837288", + "name": "Spawner (225)", + "location": [586, 2170, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "757854f4-8a3a-48bf-a288-3cf416c80b0c", + "name": "Spawner (225)", + "location": [659, 2141, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8be1dd4d-eda1-42c7-a107-8f523ec06814", + "name": "Spawner (225)", + "location": [2002, 2721, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "02c8aec2-f3b8-4626-9127-c3685d861a3b", + "name": "Spawner (225)", + "location": [1897, 2805, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2d2440ab-0e7c-4fa1-a078-688127e2feb5", + "name": "Spawner (225)", + "location": [1852, 2831, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31cbfeeb-3e9c-440e-a424-2524ca287d1b", + "name": "Spawner (225)", + "location": [2033, 2801, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98d30ebe-3d65-4427-a8cb-84ce8b3a9f27", + "name": "Spawner (225)", + "location": [1895, 2653, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "064da568-3987-4ae8-90b0-592f6e49acd3", + "name": "Spawner (225)", + "location": [2026, 2845, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b84b5ea-61db-496c-9804-f90ecae123bc", + "name": "Spawner (225)", + "location": [1848, 2680, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "048d0dc3-9b09-4997-825b-e316da48d60f", + "name": "Spawner (225)", + "location": [1990, 2889, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4d903014-9897-4a01-8ca1-4dcddadaf629", + "name": "Spawner (225)", + "location": [2072, 2856, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cc0b834c-14d8-4b2d-937a-fc4dcf6d6220", + "name": "Spawner (225)", + "location": [1991, 2867, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c02625b1-ced5-4100-8c61-2820fbcaf129", + "name": "Spawner (225)", + "location": [1911, 2805, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d2fdc26-5ecb-44f8-a59f-a04aa21f7f29", + "name": "Spawner (225)", + "location": [1897, 2684, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "825d89ed-d1f3-45e5-b7c0-0b86434ec759", + "name": "Spawner (225)", + "location": [1822, 2739, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9e7b7d17-aaa1-45c9-85f5-48f0e5c7f6cc", + "name": "Spawner (225)", + "location": [2019, 2748, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d66334a0-15b9-4362-8afc-0bb44e1a9f1c", + "name": "Spawner (225)", + "location": [1844, 2735, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "01e09394-d02b-4fad-9ee9-511454e49140", + "name": "Spawner (225)", + "location": [1981, 2838, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7b2c31b7-20c9-46b8-9cd1-6b624a505c01", + "name": "Spawner (225)", + "location": [1880, 2802, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2ce77da7-68d6-4c82-a3f4-8fdc1161ab7f", + "name": "Spawner (225)", + "location": [1813, 2825, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc4349ab-4016-43e9-8f41-f09733d7c0d4", + "name": "Spawner (225)", + "location": [1850, 2796, -8], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05b65869-ea89-4ac6-9233-aef3768a2796", + "name": "Spawner (225)", + "location": [1939, 2739, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "95936f14-8718-4f24-9236-d5e92d4fcc41", + "name": "Spawner (225)", + "location": [1846, 2715, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df027fa7-1a26-4c66-a7da-13e67012c84f", + "name": "Spawner (225)", + "location": [2011, 2804, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d4728bf-f926-42cc-b670-37b8c74e5dbb", + "name": "Spawner (225)", + "location": [1935, 2766, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15b48a52-8c02-438e-bd97-aae13dada523", + "name": "Spawner (225)", + "location": [1935, 2796, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8e0eaa2-df1d-478d-9ae1-85bc28647027", + "name": "Spawner (225)", + "location": [2855, 734, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff67542d-65a0-4592-9df9-856ee2d1e834", + "name": "Spawner (225)", + "location": [2861, 812, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "43cb9a14-49b9-4f88-885d-d98eef7479b9", + "name": "Spawner (225)", + "location": [2882, 723, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ef0e9deb-1a5f-4e48-86db-18c1bb206530", + "name": "Spawner (225)", + "location": [2961, 621, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6fa80ffa-14af-4633-9e2f-fcc64b96c946", + "name": "Spawner (225)", + "location": [2840, 885, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41d788c5-5cc4-4cd1-9110-67560560dade", + "name": "Spawner (225)", + "location": [2860, 999, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c351eb79-682e-4e9c-a193-0e5434f44634", + "name": "Spawner (225)", + "location": [2998, 760, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d3f60ee0-06c4-4e53-81d8-0abbbc0383a3", + "name": "Spawner (225)", + "location": [3034, 827, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "86011460-6b38-45da-8706-cbfad5250fe0", + "name": "Spawner (225)", + "location": [2962, 885, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a125d56-a7a0-4e00-a7c6-b0f7fea301a3", + "name": "Spawner (225)", + "location": [2835, 805, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd2e9db5-e5b2-4195-9587-76654566de99", + "name": "Spawner (225)", + "location": [2986, 637, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2761adf7-e265-438a-a2a8-284e00c4b605", + "name": "Spawner (225)", + "location": [2890, 651, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d7d6dc0-179c-41c3-88c3-26d47e2b66a8", + "name": "Spawner (225)", + "location": [2954, 705, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Beekeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "612b7282-50f3-48c0-8172-4d6ed40f47ff", + "name": "Spawner (225)", + "location": [2987, 777, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "147578da-6cae-4d44-ad6e-3b01f3e2d670", + "name": "Spawner (225)", + "location": [2841, 907, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "66bba18a-084f-4657-909d-a2c7e06e4e49", + "name": "Spawner (225)", + "location": [3017, 761, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2325bd37-aa70-467d-a132-c77447fa333d", + "name": "Spawner (225)", + "location": [2963, 813, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c0166342-30ea-4fb2-b95d-387c4a5ebc39", + "name": "Spawner (225)", + "location": [3012, 780, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbea519d-d3d7-4287-9bd2-3231430294d8", + "name": "Spawner (225)", + "location": [2899, 790, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad38d744-523e-425d-995c-6e954c9dbcc8", + "name": "Spawner (225)", + "location": [2917, 798, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4723e0e9-a7e0-4657-9e74-0610afdf5a24", + "name": "Spawner (225)", + "location": [2920, 856, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ff79ff29-6a36-42e4-9017-beb51a60290e", + "name": "Spawner (225)", + "location": [2782, 969, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b1af2e4f-16db-41d7-a4ae-fcce5350d51f", + "name": "Spawner (225)", + "location": [2865, 852, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4dd996e0-2cf5-4d04-8b3b-3264c0bed46a", + "name": "Spawner (225)", + "location": [2918, 672, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a14b366d-e223-4ef7-9715-298df09962b1", + "name": "Spawner (225)", + "location": [2992, 844, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "676fa552-31ef-4384-8dd1-e68d0d434f0c", + "name": "Spawner (225)", + "location": [2995, 815, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a027a96b-cea5-4bfb-b048-2d7fcda9edd7", + "name": "Spawner (225)", + "location": [2901, 908, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9af91c7-daae-47de-958d-0169069fabde", + "name": "Spawner (225)", + "location": [2881, 684, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5382e8d9-4883-46b4-9f57-f7eedea8f0b3", + "name": "Spawner (225)", + "location": [5242, 180, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "96956b69-4920-400c-8ca9-6effd0202fa5", + "name": "Spawner (225)", + "location": [5161, 32, 17], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ba190817-c467-4702-83f4-f13272bd748a", + "name": "Spawner (225)", + "location": [5148, 60, 25], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ff657041-f430-4a17-9995-279fdbb05ff1", + "name": "Spawner (225)", + "location": [5347, 76, 25], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1b458a3-908e-42c6-8e88-9f9a013f098c", + "name": "Spawner (225)", + "location": [5263, 129, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a4163176-dab9-4a56-83ea-e97cd802579e", + "name": "Spawner (225)", + "location": [5203, 86, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f79dae4-98e1-489e-9f04-b11d20f34897", + "name": "Spawner (225)", + "location": [5218, 175, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98952a90-a316-4809-a85f-d5840e2f6716", + "name": "Spawner (225)", + "location": [5300, 90, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e19650f-6ec9-4170-8aed-3ff6cb384366", + "name": "Spawner (225)", + "location": [5306, 37, 40], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "77ea211d-a375-4f9f-a2ff-31a864070728", + "name": "Spawner (225)", + "location": [5236, 142, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0caf1231-8fd1-4873-b525-29c1b9a4751c", + "name": "Spawner (225)", + "location": [5351, 56, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bea16b73-7772-41e1-868e-7a6de8007a93", + "name": "Spawner (225)", + "location": [5154, 97, 5], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "00c709c3-bd1f-464f-9851-6222d10bf35b", + "name": "Spawner (225)", + "location": [5215, 117, 1], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2efce74-1331-43af-9713-6b1c75fb6225", + "name": "Spawner (225)", + "location": [570, 969, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "358552dd-07fb-4fc4-ad49-366f4cbd7105", + "name": "Spawner (225)", + "location": [517, 986, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1bf5b7f8-c166-419f-87f5-199254245758", + "name": "Spawner (225)", + "location": [529, 1008, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f439eb85-35d6-487c-884f-a7a71716748f", + "name": "Spawner (225)", + "location": [652, 820, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6fb8b91e-1bb9-4d0a-8ffe-ddfb552cabf5", + "name": "Spawner (225)", + "location": [610, 810, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d75fe7bf-4215-402e-9d5c-24bd7a8801bb", + "name": "Spawner (225)", + "location": [535, 867, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03132a28-66bf-4d70-ad03-efc8590f21fe", + "name": "Spawner (225)", + "location": [564, 1011, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "38a24240-c538-40cf-93a6-65e6c31455c5", + "name": "Spawner (225)", + "location": [643, 1083, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef25c059-a51f-4e6d-80f6-a39eb60c9c27", + "name": "Spawner (225)", + "location": [624, 1145, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e16bd6c9-e916-4616-9273-c5c3d1ab0115", + "name": "Spawner (225)", + "location": [631, 1034, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aedc8ecd-8443-4c79-a7ab-05da8844c493", + "name": "Spawner (225)", + "location": [540, 966, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "173141ce-120f-451c-8d7e-781b07e8482a", + "name": "Spawner (225)", + "location": [553, 985, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a76b7074-3933-477d-849a-99833d78d89a", + "name": "Spawner (225)", + "location": [5219, 4012, 37], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "acbb59a0-32ac-4add-9b8b-479cf4be15a3", + "name": "Spawner (225)", + "location": [5275, 3977, 37], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61bb7c02-bbc3-497f-9d42-bc82a6378c3a", + "name": "Spawner (225)", + "location": [5225, 4000, 38], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef1db5ca-06dd-4a31-bfce-5bb4c75a1df8", + "name": "Spawner (225)", + "location": [5197, 4060, 37], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "add1a992-7c4e-419d-9342-29642aac96af", + "name": "Spawner (225)", + "location": [5297, 4007, 40], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9acf583a-2654-4f9c-8be4-11f121aa97ab", + "name": "Spawner (225)", + "location": [5234, 4025, 37], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d922542-dbaa-45a6-b70f-491621ed4509", + "name": "Spawner (225)", + "location": [5191, 3989, 37], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9dbabd5d-fd49-490f-86f1-e07e76420332", + "name": "Spawner (225)", + "location": [5296, 3978, 37], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81f52dcd-ccb6-470c-a58a-9141b90be9c3", + "name": "Spawner (225)", + "location": [5669, 3131, 14], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Minter", "maxCount": 1, "probability": 100 }, + { "name": "Banker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "87f9a58b-fa94-4899-b3de-e2e344641494", + "name": "Spawner (225)", + "location": [5731, 3192, 9], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "Mage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01605fa2-174e-473c-b83b-7b2edb42c693", + "name": "Spawner (225)", + "location": [5726, 3243, 16], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Tinker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "472774c8-7b94-461a-a9e1-4d3004062592", + "name": "Spawner (225)", + "location": [5662, 3150, 13], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "591c4553-36ec-4e20-8f04-1b7d2f056c7a", + "name": "Spawner (225)", + "location": [5671, 3287, 11], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e6689d71-5784-4c9c-a0ed-90bcdde2eb2b", + "name": "Spawner (225)", + "location": [5698, 3282, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "95b1c31c-4fb0-46b2-8b18-8d4a8f7aaeb7", + "name": "Spawner (225)", + "location": [5805, 3300, 11], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 }, + { "name": "Armorer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3f935f7e-4454-44fa-9222-58e667345c4d", + "name": "Spawner (225)", + "location": [5714, 3202, 12], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "Herbalist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0024932-683f-457b-b4ca-fe0e60d8b42c", + "name": "Spawner (225)", + "location": [5805, 3270, 11], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mapmaker", "maxCount": 1, "probability": 100 }, + { "name": "Shipwright", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a9450d4e-81d6-4963-9afd-fd2da7c143eb", + "name": "Spawner (225)", + "location": [5752, 3272, 16], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "Tailor", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "553b4277-f7f3-44a0-8d8a-b1cf97b10152", + "name": "Spawner (225)", + "location": [5839, 3256, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8080d16f-4ee6-4d7b-b017-cebc7113ce25", + "name": "Spawner (225)", + "location": [5746, 3200, 16], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5f377cf6-2581-4b00-95ac-e8804ab76415", + "name": "Spawner (225)", + "location": [5775, 3161, 14], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6908fde5-9dd0-4385-8202-d07d88bfba2c", + "name": "Spawner (225)", + "location": [5737, 3222, 11], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Healer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4ea855b-be4f-46f8-9b3a-6bac5d6a1c5a", + "name": "Spawner (225)", + "location": [5691, 3209, 11], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "Carpenter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b201992b-e843-4249-82a4-2122cc01f090", + "name": "Spawner (225)", + "location": [5736, 3263, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Cobbler", "maxCount": 1, "probability": 100 }, + { "name": "Provisioner", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "3df0543a-373c-4673-a98b-8191686f4ab3", "name": "Spawner (225)", "location": [4571, 2303, -2], @@ -11,12 +5214,10 @@ "team": 0, "homeRange": 0, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "72828840-4db8-4607-9227-fcfd2f6ac819", "name": "Spawner (225)", "location": [4553, 2318, -2], @@ -27,12 +5228,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Tanner", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3e40c9ba-b486-47e2-98c3-1547ed17236f", "name": "Spawner (225)", "location": [4550, 2307, -2], @@ -49,7 +5248,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "602fb6c4-16b8-42cc-ad4d-eaec24989698", "name": "Spawner (225)", "location": [4557, 2307, -2], @@ -60,12 +5259,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "056fee00-19c9-459b-8062-bb8d32aa9684", "name": "Spawner (225)", "location": [4540, 2301, -1], @@ -82,7 +5279,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "957a0aac-b3b4-4565-a36c-80223d09f884", "name": "Spawner (225)", "location": [4547, 2321, -2], @@ -93,12 +5290,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "bodysculptor", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "bodysculptor", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e5204866-5351-4ea0-b2b3-5ebae2b5a70b", "name": "Spawner (225)", "location": [4551, 2334, -2], @@ -109,12 +5304,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Shipwright", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fc335c32-274c-406a-af4d-359e4b36bc4b", "name": "Spawner (225)", "location": [4556, 2330, -2], @@ -125,12 +5318,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e2a30c0b-02e7-4f98-9093-db9f2c69a338", "name": "Spawner (225)", "location": [4533, 2333, -2], @@ -148,7 +5339,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a6b874c9-ffc6-41c3-b57d-505e1cc0b127", "name": "Spawner (225)", "location": [4569, 2304, -2], @@ -159,12 +5350,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "dockmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "dockmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9174aac6-9b43-4a79-a29a-bfd85e66a4c4", "name": "Spawner (225)", "location": [4549, 2346, -2], @@ -175,12 +5364,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5b6c1209-b817-4852-8a1c-d378ea9cb0b6", "name": "Spawner (225)", "location": [4562, 2349, -2], @@ -191,12 +5378,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "hiresailor", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "hiresailor", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "12dab98a-eac6-4729-9d04-42b0e16271a5", "name": "Spawner (225)", "location": [4570, 2400, -2], @@ -213,7 +5398,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c1330899-bfe5-4dd5-9add-8708c6564101", "name": "Spawner (225)", "location": [4551, 2327, -2], @@ -224,12 +5409,10 @@ "team": 0, "homeRange": 7, "walkingRange": -1, - "entries": [ - { "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4dca1873-97db-40ae-9206-3da39bf12eb9", "name": "Spawner (225)", "location": [4530, 2382, -2], @@ -240,12 +5423,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9c5549c8-0ac4-48c5-8dcd-53fea6ab3fc1", "name": "Spawner (225)", "location": [4551, 2320, -2], @@ -256,12 +5437,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "boatpainter", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "boatpainter", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "8f87e0c7-f32f-4c15-8fef-f35249144384", "name": "Spawner (225)", "location": [4573, 2364, -2], @@ -272,12 +5451,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "91aad75f-c75f-4ac2-9cd0-78b44f1e7e99", "name": "Spawner (225)", "location": [4553, 2351, -2], @@ -288,12 +5465,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f054eaef-7c05-4422-b92d-38de870a606f", "name": "Spawner (225)", "location": [4537, 2348, -2], @@ -304,12 +5479,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0d40622f-69f2-4d5b-8375-40fa1e17f429", "name": "Spawner (225)", "location": [4549, 2301, -2], @@ -320,12 +5493,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2a6c3d42-36b5-4f96-b477-ddbafbcb22da", "name": "Spawner (225)", "location": [4553, 2343, -2], @@ -336,12 +5507,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "94b31286-98ac-4832-9cc9-f6b95e8a21d4", "name": "Spawner (225)", "location": [4544, 2299, -1], @@ -358,7 +5527,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "aadd2ea6-a598-4805-b4dd-737fff405f6b", "name": "Spawner (225)", "location": [4561, 2328, -2], @@ -369,12 +5538,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Tinker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "90268f42-c87d-4ab2-8f88-1bd135f0a463", "name": "Spawner (225)", "location": [4551, 2381, -2], @@ -385,12 +5552,10 @@ "team": 0, "homeRange": 15, "walkingRange": -1, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "251b9d65-ab66-42b4-ad7f-6fff5e477b14", "name": "Spawner (225)", "location": [4552, 2349, -2], @@ -401,12 +5566,10 @@ "team": 0, "homeRange": 5, "walkingRange": -1, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3d6c13d4-d1b1-46e5-82ea-2aea918057ab", "name": "Spawner (225)", "location": [4552, 2390, -2], @@ -417,12 +5580,10 @@ "team": 0, "homeRange": 7, "walkingRange": -1, - "entries": [ - { "name": "hiresailor", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "hiresailor", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "35d48eae-3966-4f92-966e-e7f40e7be19b", "name": "Spawner (225)", "location": [4542, 2329, -2], @@ -433,12 +5594,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Alchemist", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Alchemist", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a5f81c37-59df-4510-bac7-e4809bf4c71d", "name": "Spawner (225)", "location": [4551, 2323, -2], @@ -449,12 +5608,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Banker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b40aaca3-893a-4d86-a3bc-a8dcfd929794", "name": "Spawner (225)", "location": [4564, 2298, -2], @@ -465,12 +5622,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Shipwright", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "643cc355-1279-47c8-a088-48cb02f214bf", "name": "Spawner (225)", "location": [4547, 2376, -2], @@ -481,12 +5636,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "91ee98c3-bf4f-46b6-b844-c4539c8a91a7", "name": "Spawner (225)", "location": [4540, 2306, -1], @@ -497,12 +5650,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "Waiter", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Waiter", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "173fb37f-69be-40b5-99f1-eeaade1956c3", "name": "Spawner (225)", "location": [4554, 2389, -2], @@ -513,12 +5664,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0f374a41-ce2d-4eb6-8bcd-5ecc9f87ba20", "name": "Spawner (225)", "location": [4534, 2349, -2], @@ -529,12 +5678,10 @@ "team": 0, "homeRange": 0, "walkingRange": -1, - "entries": [ - { "name": "treasurelevel1h", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "treasurelevel1h", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3faeebac-5ea9-4262-bf9b-a6764358547b", "name": "Spawner (225)", "location": [4535, 2349, -2], @@ -545,8 +5692,6 @@ "team": 0, "homeRange": 0, "walkingRange": -1, - "entries": [ - { "name": "treasurelevel1h", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "treasurelevel1h", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/felucca/WildLife.json b/Distribution/Data/Spawns/post-uoml/felucca/WildLife.json similarity index 98% rename from Distribution/Data/Spawns/shared/felucca/WildLife.json rename to Distribution/Data/Spawns/post-uoml/felucca/WildLife.json index 20d7c945b..d1dd75dd1 100644 --- a/Distribution/Data/Spawns/shared/felucca/WildLife.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/WildLife.json @@ -1,3984 +1,6 @@ [ { - "$type": "Spawner", - "guid": "01728819-da08-4e42-9763-39cdf14b7707", - "name": "Spawner (226)", - "location": [846, 1071, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "02a348ee-7285-4741-b23e-61a6ef88a7c0", - "name": "Spawner (226)", - "location": [2512, 1066, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "03ab5a78-b854-4676-bad5-4a37e96c1529", - "name": "Spawner (226)", - "location": [1315, 2192, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "04263280-45f5-494d-9d8d-d36726bde577", - "name": "Spawner (226)", - "location": [1695, 809, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "05fcd31c-d53e-417b-8798-65d1e7c2b5de", - "name": "Spawner (226)", - "location": [1666, 1290, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "09d26956-a96f-4cce-b0e9-37f5bc2afffa", - "name": "Spawner (226)", - "location": [2188, 1986, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0acdd078-559c-40e8-bc74-2490d5c85d71", - "name": "Spawner (226)", - "location": [1087, 2401, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0e87997b-c05c-429a-a632-a9076a5afd84", - "name": "Spawner (226)", - "location": [1582, 2722, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11834407-82df-4241-bf81-5ea9687c955a", - "name": "Spawner (226)", - "location": [4510, 1262, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1230cda9-3dbe-4268-8e2f-cf765dccd339", - "name": "Spawner (226)", - "location": [4692, 3494, 1], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12eea555-de46-4ae2-ad79-fb7b8a3f9053", - "name": "Spawner (226)", - "location": [1798, 1471, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "14c3e5bd-aae9-410c-9d47-19ead3ef7c10", - "name": "Spawner (226)", - "location": [1867, 1244, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15fc7f5b-6386-400e-9490-2d3fbfebf96d", - "name": "Spawner (226)", - "location": [887, 1223, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1616a270-482b-465c-b9a8-f8103ba729b4", - "name": "Spawner (226)", - "location": [2735, 417, 15], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "18abaf3e-2dd3-4f3c-afcd-21feb5e65b44", - "name": "Spawner (226)", - "location": [2367, 748, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "18bb4b32-dbcd-416e-aae6-547bb1b13fb1", - "name": "Spawner (226)", - "location": [1387, 2023, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1956dff5-b7f6-477e-8fb2-5fd6840f0c8e", - "name": "Spawner (226)", - "location": [725, 1483, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1a1a9dd9-0073-47da-8ea9-f7b932cad724", - "name": "Spawner (226)", - "location": [1459, 1963, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1ef9fd31-972f-43c0-aa33-ed94fab149c2", - "name": "Spawner (226)", - "location": [782, 1156, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21609f91-35f1-483c-9d4f-970c07c7499b", - "name": "Spawner (226)", - "location": [2672, 814, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2248238d-727b-42d6-a39b-267d253d3c1e", - "name": "Spawner (226)", - "location": [1172, 974, 2], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "226ea014-96c5-4281-8c3c-6a1742392ac9", - "name": "Spawner (226)", - "location": [951, 542, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23cb3cb9-b698-4de3-a49f-bd3ed42cac7c", - "name": "Spawner (226)", - "location": [1229, 1655, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "260110a5-7142-4fb3-b18b-002b831bc2fa", - "name": "Spawner (226)", - "location": [895, 830, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "27a803b9-9e76-4bcd-8d3f-d55d65c8b721", - "name": "Spawner (226)", - "location": [1527, 2609, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "29780577-caca-4fcc-8586-0d7d9f9ea4fc", - "name": "Spawner (226)", - "location": [1426, 2615, 10], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2a7a1122-3dd4-49c7-b8f4-d0a47d53a32e", - "name": "Spawner (226)", - "location": [4336, 1130, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2a91ed63-e48f-4b5c-8f67-68e1b0519944", - "name": "Spawner (226)", - "location": [2654, 2286, 9], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2b1d8e55-62da-4a75-a626-bc24e0415a5e", - "name": "Spawner (226)", - "location": [1715, 2307, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ba37ae4-8aa1-4631-a39e-4d727282f435", - "name": "Spawner (226)", - "location": [1602, 2426, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2c3aaabb-253c-4de4-bc92-3ae604a8213e", - "name": "Spawner (226)", - "location": [4612, 3424, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2d6b8996-a8c3-4110-9973-14084c70d0b9", - "name": "Spawner (226)", - "location": [2338, 989, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2d7e7d28-893b-406a-8f44-b8fc3c7e55af", - "name": "Spawner (226)", - "location": [3555, 2525, 13], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e2f4cce-2302-427b-8a93-ca5c43466bfc", - "name": "Spawner (226)", - "location": [1173, 2103, 2], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2f61bc16-a435-4de0-9aaa-223f3930c57c", - "name": "Spawner (226)", - "location": [4384, 1218, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2f638b8b-dd33-49f6-8bbf-c7e638a912fe", - "name": "Spawner (226)", - "location": [1222, 2378, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "345821d1-8277-47c0-8b9f-a722e65dd2e7", - "name": "Spawner (226)", - "location": [4632, 1164, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "35bbcb04-b782-4975-90b0-fe6118462b3e", - "name": "Spawner (226)", - "location": [977, 1638, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3713dbd8-18ac-4d4f-b278-5375fc33a7eb", - "name": "Spawner (226)", - "location": [3546, 2812, 13], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "374ade5f-0b52-4d90-9695-394ffa268943", - "name": "Spawner (226)", - "location": [1011, 1945, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3864b7b7-57d3-43ac-9105-d330ba4b32c9", - "name": "Spawner (226)", - "location": [1092, 1010, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a58ab96-dff2-424c-9486-829b5e1d0df8", - "name": "Spawner (226)", - "location": [1699, 2560, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a81fadc-a778-41a9-aea0-76fe8ec168cf", - "name": "Spawner (226)", - "location": [3654, 2866, 61], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e4e5017-002b-4c43-b113-3ae91958fee4", - "name": "Spawner (226)", - "location": [1724, 2781, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ef19957-7ff2-4d70-8ada-ad57fcbda8a7", - "name": "Spawner (226)", - "location": [2888, 391, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3fa4af6f-bbc5-4dd0-b6df-20f4c7971725", - "name": "Spawner (226)", - "location": [2786, 2282, 13], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3fe6b62d-c81a-4440-a608-26c0b40a8f08", - "name": "Spawner (226)", - "location": [1498, 2040, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3fe8f267-3962-4c07-a7dd-23dc702c4830", - "name": "Spawner (226)", - "location": [2814, 492, 15], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "438632c6-7114-4c25-9ab4-ec3a0883720d", - "name": "Spawner (226)", - "location": [559, 2081, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "441009bc-1c05-4252-bac0-4dfd6d1b57a6", - "name": "Spawner (226)", - "location": [940, 1028, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "450d4409-e114-42c1-84a7-9cd9e0c5f69e", - "name": "Spawner (226)", - "location": [485, 1398, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "476014f7-4611-401d-bfc1-b5ca816ddd7d", - "name": "Spawner (226)", - "location": [851, 1770, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47c42859-f7e9-4eac-bc14-7ca42eba61b9", - "name": "Spawner (226)", - "location": [1958, 1305, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4954f31e-56af-4be6-82de-5d909a232b8f", - "name": "Spawner (226)", - "location": [1563, 2277, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4be9e0ff-5157-493c-90c2-9c1a1a45e3b1", - "name": "Spawner (226)", - "location": [4590, 1326, 8], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4c42f7ec-a06f-492b-b19a-4c887a2057b2", - "name": "Spawner (226)", - "location": [3392, 424, 9], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4e149dd8-9b15-43fb-9b8e-c7fd5974db3e", - "name": "Spawner (226)", - "location": [1865, 1379, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4e211be3-6af6-4035-a6f0-72704f498f6d", - "name": "Spawner (226)", - "location": [1894, 2942, 19], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4fcd2029-3cf4-442c-8977-e3f21a4fb8f5", - "name": "Spawner (226)", - "location": [4544, 1128, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "51fc697b-edf6-413f-acda-11c084092eb7", - "name": "Spawner (226)", - "location": [2946, 2168, 44], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54a0d275-e0fe-4565-bc4b-a36330991e7a", - "name": "Spawner (226)", - "location": [3506, 2477, 28], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "576c5f8a-deef-4600-aabc-b0494de12d9c", - "name": "Spawner (226)", - "location": [1057, 765, 1], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "579099c7-42d6-4f8c-843c-a0c993cc3430", - "name": "Spawner (226)", - "location": [1222, 2499, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "58861404-11fe-4f82-b962-70196d0d5ff6", - "name": "Spawner (226)", - "location": [4446, 3706, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5b654652-e5e1-4cbb-8404-e2760b2f180f", - "name": "Spawner (226)", - "location": [1634, 2645, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5de7027a-07d5-405f-aba7-ffa1edb10d69", - "name": "Spawner (226)", - "location": [1709, 1992, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5f0a0d30-52e5-4e38-9853-06806f9bf957", - "name": "Spawner (226)", - "location": [1503, 2207, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6193d4d1-91f8-4237-b7eb-28d1217335bc", - "name": "Spawner (226)", - "location": [2495, 978, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "622a57d0-5378-4a17-aee9-2203afc201ce", - "name": "Spawner (226)", - "location": [1388, 784, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "63a8dea7-3158-4ccf-8312-d60bab954514", - "name": "Spawner (226)", - "location": [999, 1839, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "63a9ae0f-6d62-4169-8494-e14fe4fa8539", - "name": "Spawner (226)", - "location": [3650, 2080, 21], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6499e448-465f-440e-8425-d8866179eb24", - "name": "Spawner (226)", - "location": [1152, 812, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "689f30bd-7852-427e-8327-332dcfc9cbc3", - "name": "Spawner (226)", - "location": [2583, 993, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "69f2606e-f9c8-4110-8031-ed1889e5985d", - "name": "Spawner (226)", - "location": [3449, 2737, 49], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6aab7562-4d73-4806-b582-f8c32251e4fd", - "name": "Spawner (226)", - "location": [2643, 728, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6adc698e-936e-4d69-8870-13a39fb1baa5", - "name": "Spawner (226)", - "location": [1334, 2311, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b573305-001f-487c-9dc0-8411bb238161", - "name": "Spawner (226)", - "location": [2418, 999, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bb381bc-3862-4e05-84e8-2e13c62451b8", - "name": "Spawner (226)", - "location": [2748, 660, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6d98da32-7abe-4c6e-bbed-71e5a727f5a9", - "name": "Spawner (226)", - "location": [2668, 1020, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f79d72b-455f-483f-b33f-eec6d1c50bc2", - "name": "Spawner (226)", - "location": [805, 576, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "710644f8-df91-4fe2-948a-376700d3afcd", - "name": "Spawner (226)", - "location": [4560, 3504, 2], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "717379e5-9754-4a5f-8083-2a7042b9b403", - "name": "Spawner (226)", - "location": [3172, 664, 8], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7305a62d-7a0b-4d9e-88e4-a2d3b0d25462", - "name": "Spawner (226)", - "location": [805, 1570, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76aadaf7-2302-4077-96f3-e6d122b18000", - "name": "Spawner (226)", - "location": [748, 669, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76f11cc1-4ca0-4357-a659-026ea69ae181", - "name": "Spawner (226)", - "location": [3206, 366, 9], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "79cda4aa-4199-4530-afd9-a41ce93c93b5", - "name": "Spawner (226)", - "location": [876, 1639, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7bd58b24-3bf1-48b8-9d6f-d8ff61a2e2c4", - "name": "Spawner (226)", - "location": [2864, 150, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7dc555e2-daca-49fe-bde0-a49d5e3eaa51", - "name": "Spawner (226)", - "location": [3607, 2417, 45], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7f279f10-c41b-43f4-97ee-66aa3cda4c07", - "name": "Spawner (226)", - "location": [1545, 813, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7f778ca1-e3e1-411e-8029-e62ad6783492", - "name": "Spawner (226)", - "location": [1650, 2796, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "821ef012-218a-4691-882b-799f3eef1307", - "name": "Spawner (226)", - "location": [802, 1280, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "833a7019-688c-4996-93af-a5cdc6501324", - "name": "Spawner (226)", - "location": [3718, 2736, 38], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8464904f-68c6-4900-a548-1ee24a9605f2", - "name": "Spawner (226)", - "location": [4540, 1454, 8], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84f26198-6d50-4e80-a0fb-5f47b0a200b0", - "name": "Spawner (226)", - "location": [3452, 2563, 32], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "85b70538-e38a-4e69-b0c9-fe1774848e03", - "name": "Spawner (226)", - "location": [4326, 1046, 8], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "87498c2d-dbda-448c-8822-993811da1899", - "name": "Spawner (226)", - "location": [1628, 2334, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "88c34d44-6f28-4353-bdea-ed04a0bf415a", - "name": "Spawner (226)", - "location": [1824, 3027, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "892408a1-3f82-4854-ba77-7f37a15582a1", - "name": "Spawner (226)", - "location": [1932, 3125, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8b45e7a5-6dad-4ddf-9d4e-75953242e058", - "name": "Spawner (226)", - "location": [958, 2074, 1], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8b8e890c-1f24-4b07-b0a8-3c0160b64932", - "name": "Spawner (226)", - "location": [1973, 751, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8bf5c14b-8f9c-4fa5-96fb-0b8521ffb0b2", - "name": "Spawner (226)", - "location": [883, 2129, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8f29ae6e-ad1a-4e16-9ce6-5ddeec18003f", - "name": "Spawner (226)", - "location": [1769, 1330, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "90a10ef6-dca6-4f41-9f9a-3ac84050e227", - "name": "Spawner (226)", - "location": [4592, 1424, 3], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9228d1aa-d550-4239-a8d1-df8c69125d44", - "name": "Spawner (226)", - "location": [2416, 1172, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9252566d-4ebe-4f24-84da-fd9b05dd8694", - "name": "Spawner (226)", - "location": [974, 374, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "92ff3311-a6f6-47a8-8069-9738fb0264d6", - "name": "Spawner (226)", - "location": [1024, 1244, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93f49e99-b7cc-427f-a04e-93aa82f9e20a", - "name": "Spawner (226)", - "location": [1526, 960, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94f6e27d-8653-4b55-86d0-cc131a38aa4b", - "name": "Spawner (226)", - "location": [1017, 1726, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9a232c41-76f0-4175-bb91-8dfcc2dfd800", - "name": "Spawner (226)", - "location": [623, 1328, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9ace0130-cad4-4d06-956a-f8a1fbd4c88f", - "name": "Spawner (226)", - "location": [3429, 2650, 43], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9f4ab748-bee7-48f9-820b-230e2b2c2f65", - "name": "Spawner (226)", - "location": [4470, 1430, 8], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9fe7ab6c-c783-431b-b05e-65f5ccb275cb", - "name": "Spawner (226)", - "location": [4448, 1334, 3], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a0a3a37e-6c7f-4636-9680-fed8a5144063", - "name": "Spawner (226)", - "location": [1268, 2018, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a1fd0dfb-3904-4170-b3a9-e85c2aef2021", - "name": "Spawner (226)", - "location": [2085, 658, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a2de2354-f225-4181-a7ce-bd709041b5fa", - "name": "Spawner (226)", - "location": [464, 2059, 8], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a54dc067-1553-42d8-9c2b-d4029bd44f24", - "name": "Spawner (226)", - "location": [764, 822, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a96f7041-620d-4f55-8856-106b2d8ecb5a", - "name": "Spawner (226)", - "location": [1047, 1071, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "acb8201d-8566-4625-ac64-54c9b24ec285", "name": "Spawner (226)", "location": [3394, 628, 5], @@ -4012,1435 +34,7 @@ ] }, { - "$type": "Spawner", - "guid": "ace2e1ec-f2fe-48b9-8cdd-1cf2482169ea", - "name": "Spawner (226)", - "location": [398, 1305, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "adaf3214-a59c-4c6b-86b8-466ce4e53018", - "name": "Spawner (226)", - "location": [3608, 2140, 79], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "adcf2529-b297-4c4e-b1b9-8319df439c20", - "name": "Spawner (226)", - "location": [2513, 782, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b214fa51-5b5f-466a-8c69-02bfb22af5c7", - "name": "Spawner (226)", - "location": [1486, 1301, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b40de864-46c6-49c8-9be6-7ee491a8c7f6", - "name": "Spawner (226)", - "location": [4468, 1006, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4a6db11-d166-4762-869e-9d2a74168a90", - "name": "Spawner (226)", - "location": [1018, 629, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b67aaf83-665d-4d9d-95e4-af3072de86cc", - "name": "Spawner (226)", - "location": [919, 1718, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b6895418-072d-48b0-92f8-cf2eb6b9d085", - "name": "Spawner (226)", - "location": [1505, 2445, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b8106158-5efa-4600-8c35-ee6a1178c0b3", - "name": "Spawner (226)", - "location": [937, 743, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b814727e-e122-4a40-af91-bfa4632efc20", - "name": "Spawner (226)", - "location": [2554, 1142, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9d1f842-8619-4a7f-93ec-2125ef399296", - "name": "Spawner (226)", - "location": [1020, 1360, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba4b69f8-15fe-4ca3-9c03-9a9551510113", - "name": "Spawner (226)", - "location": [890, 657, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba6fdb72-4770-4b79-96d9-2f8ea6cbf65a", - "name": "Spawner (226)", - "location": [3662, 2802, 51], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bb35b29d-953d-4d35-88f9-becfbcd3c6d6", - "name": "Spawner (226)", - "location": [1728, 691, 6], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bb5eb63b-330a-4d9b-9e78-a22466bf7a50", - "name": "Spawner (226)", - "location": [3596, 2804, 51], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bcd487fc-33c6-47ea-b8ab-ba10f86db14f", - "name": "Spawner (226)", - "location": [3292, 570, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd99393a-7ba7-49cd-8d52-b36eda439249", - "name": "Spawner (226)", - "location": [4478, 3218, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "be547090-d9da-4bc6-89bc-4dc712ef42af", - "name": "Spawner (226)", - "location": [210, 1447, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0632e64-37a8-4ea1-9c44-189db60d2285", - "name": "Spawner (226)", - "location": [1231, 1462, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0f15020-27c0-4fd5-ab31-9055a7578081", - "name": "Spawner (226)", - "location": [4526, 986, 10], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2ec7572-57d8-4788-878c-8b9b0ac1f6a3", - "name": "Spawner (226)", - "location": [727, 1394, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c4140697-856a-4860-ab7d-30180de15320", - "name": "Spawner (226)", - "location": [2765, 754, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c67ab1cc-072c-4579-9c12-beb4b562aaac", - "name": "Spawner (226)", - "location": [2914, 547, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c7aee851-a76e-47a0-a0c6-e55ee5d71208", - "name": "Spawner (226)", - "location": [1011, 896, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c88821a4-e46f-4867-9994-f1583d90db1c", - "name": "Spawner (226)", - "location": [1439, 2104, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cb3efa23-3330-4812-aa08-ee1a5d9b6b61", - "name": "Spawner (226)", - "location": [1351, 1359, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d1921647-6d81-41cc-8956-618e06170bad", - "name": "Spawner (226)", - "location": [1063, 541, 16], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3badeb6-8bf0-41a6-b6e0-6c77ad3c6ba8", - "name": "Spawner (226)", - "location": [2074, 1972, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d44d4945-508a-4a08-a281-8ed15b4c0d6b", - "name": "Spawner (226)", - "location": [964, 2214, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d4ca5a94-fee0-447a-bed4-4bf70b4a7a8f", - "name": "Spawner (226)", - "location": [3092, 542, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d720e7e8-da12-4afe-b297-164dcaa76804", - "name": "Spawner (226)", - "location": [2289, 917, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d906a602-3187-4a61-84f3-ef94a6371a4f", - "name": "Spawner (226)", - "location": [916, 1159, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9dd37bd-c5f8-4525-b3b8-b06585d7ddaf", - "name": "Spawner (226)", - "location": [2122, 2108, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc6aeac6-caef-47bd-9f88-91e5852f81fd", - "name": "Spawner (226)", - "location": [1727, 2702, 5], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc8cd7ae-3f79-400b-b3b6-3e3c630f6198", - "name": "Spawner (226)", - "location": [1631, 640, 22], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df86c69d-e7c3-40e3-b819-8d425587d46b", - "name": "Spawner (226)", - "location": [993, 1130, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e030b63a-f358-4458-adf2-34e91b171761", - "name": "Spawner (226)", - "location": [4682, 3662, 77], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e0caa10e-e991-444f-94e5-66eca46e3520", - "name": "Spawner (226)", - "location": [1070, 2069, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e12e7bc4-f8df-4f78-bd8e-84ccec108eec", - "name": "Spawner (226)", - "location": [1857, 600, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e5355f9c-f463-4132-9204-550d80ed45c5", - "name": "Spawner (226)", - "location": [2744, 2014, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e68886a7-ef39-46d9-ac84-bbc0f201c5a1", - "name": "Spawner (226)", - "location": [2669, 922, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e80aa0d0-bfd3-42cd-b3e7-3f341aaed7a4", - "name": "Spawner (226)", - "location": [1884, 735, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e8375f72-dc08-4ec1-92ae-4eab98618ceb", "name": "Spawner (226)", "location": [3232, 510, 0], @@ -5474,10 +68,10 @@ ] }, { - "$type": "Spawner", - "guid": "e93c6ddc-5771-42f2-8e50-fc5ca03b13fd", + "type": "Spawner", + "guid": "bd99393a-7ba7-49cd-8d52-b36eda439249", "name": "Spawner (226)", - "location": [855, 934, 0], + "location": [4478, 3218, 0], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5508,10 +102,10 @@ ] }, { - "$type": "Spawner", - "guid": "e9a18ee1-4484-4c93-8fb9-7a996468639e", + "type": "Spawner", + "guid": "58861404-11fe-4f82-b962-70196d0d5ff6", "name": "Spawner (226)", - "location": [1377, 1911, 0], + "location": [4446, 3706, 0], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5542,10 +136,10 @@ ] }, { - "$type": "Spawner", - "guid": "ebd28e0c-ea07-4ec1-b767-1ab08f5d8a0a", + "type": "Spawner", + "guid": "e030b63a-f358-4458-adf2-34e91b171761", "name": "Spawner (226)", - "location": [968, 2476, 0], + "location": [4682, 3662, 77], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5576,10 +170,10 @@ ] }, { - "$type": "Spawner", - "guid": "edaec946-a62e-487f-82af-b30ccc8f0ccf", + "type": "Spawner", + "guid": "1230cda9-3dbe-4268-8e2f-cf765dccd339", "name": "Spawner (226)", - "location": [1725, 1447, 0], + "location": [4692, 3494, 1], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5610,10 +204,10 @@ ] }, { - "$type": "Spawner", - "guid": "f1017559-b6f9-44fc-b632-30a61d15f505", + "type": "Spawner", + "guid": "710644f8-df91-4fe2-948a-376700d3afcd", "name": "Spawner (226)", - "location": [994, 2361, 0], + "location": [4560, 3504, 2], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5644,10 +238,10 @@ ] }, { - "$type": "Spawner", - "guid": "f2d5617a-b4c1-43dc-bed5-1258240f91b2", + "type": "Spawner", + "guid": "2c3aaabb-253c-4de4-bc92-3ae604a8213e", "name": "Spawner (226)", - "location": [771, 1013, 0], + "location": [4612, 3424, 0], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5678,10 +272,10 @@ ] }, { - "$type": "Spawner", - "guid": "f40f3fdb-8bf3-41c7-b8c4-4375c91c4c94", + "type": "Spawner", + "guid": "7dc555e2-daca-49fe-bde0-a49d5e3eaa51", "name": "Spawner (226)", - "location": [2998, 3590, 15], + "location": [3607, 2417, 45], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5712,10 +306,10 @@ ] }, { - "$type": "Spawner", - "guid": "f44df805-ac2e-4331-b14f-2ddcee0e34c6", + "type": "Spawner", + "guid": "2d7e7d28-893b-406a-8f44-b8fc3c7e55af", "name": "Spawner (226)", - "location": [2814, 3500, 0], + "location": [3555, 2525, 13], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5746,10 +340,10 @@ ] }, { - "$type": "Spawner", - "guid": "f4e006fc-cd03-4c67-9622-2b5d4199a265", + "type": "Spawner", + "guid": "54a0d275-e0fe-4565-bc4b-a36330991e7a", "name": "Spawner (226)", - "location": [1531, 2351, 5], + "location": [3506, 2477, 28], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5780,10 +374,10 @@ ] }, { - "$type": "Spawner", - "guid": "f5cfeda8-987a-4c07-a165-dace4a70402d", + "type": "Spawner", + "guid": "84f26198-6d50-4e80-a0fb-5f47b0a200b0", "name": "Spawner (226)", - "location": [689, 753, 0], + "location": [3452, 2563, 32], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5814,10 +408,10 @@ ] }, { - "$type": "Spawner", - "guid": "f64e4426-d22e-4091-bb29-227354ba124a", + "type": "Spawner", + "guid": "9ace0130-cad4-4d06-956a-f8a1fbd4c88f", "name": "Spawner (226)", - "location": [2688, 524, 11], + "location": [3429, 2650, 43], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5848,10 +442,10 @@ ] }, { - "$type": "Spawner", - "guid": "f6f3201e-4f38-486d-b797-81a09db9f3bf", + "type": "Spawner", + "guid": "69f2606e-f9c8-4110-8031-ed1889e5985d", "name": "Spawner (226)", - "location": [1612, 2526, 0], + "location": [3449, 2737, 49], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5882,41 +476,7 @@ ] }, { - "$type": "Spawner", - "guid": "f7ac9eaa-2936-48d1-a3a7-e7de0d1e9293", - "name": "Spawner (226)", - "location": [834, 751, 0], - "map": "Felucca", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f8585600-951d-4c74-9533-db771d36d4e8", "name": "Spawner (226)", "location": [3736, 2512, 35], @@ -5950,10 +510,10 @@ ] }, { - "$type": "Spawner", - "guid": "fcdb223f-fd95-481f-82a7-b8f7d29ebfed", + "type": "Spawner", + "guid": "833a7019-688c-4996-93af-a5cdc6501324", "name": "Spawner (226)", - "location": [1802, 495, 0], + "location": [3718, 2736, 38], "map": "Felucca", "count": 19, "minDelay": "00:05:00", @@ -5984,7 +544,1435 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "3a81fadc-a778-41a9-aea0-76fe8ec168cf", + "name": "Spawner (226)", + "location": [3654, 2866, 61], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba6fdb72-4770-4b79-96d9-2f8ea6cbf65a", + "name": "Spawner (226)", + "location": [3662, 2802, 51], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb5eb63b-330a-4d9b-9e78-a22466bf7a50", + "name": "Spawner (226)", + "location": [3596, 2804, 51], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3713dbd8-18ac-4d4f-b278-5375fc33a7eb", + "name": "Spawner (226)", + "location": [3546, 2812, 13], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63a9ae0f-6d62-4169-8494-e14fe4fa8539", + "name": "Spawner (226)", + "location": [3650, 2080, 21], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "adaf3214-a59c-4c6b-86b8-466ce4e53018", + "name": "Spawner (226)", + "location": [3608, 2140, 79], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f61bc16-a435-4de0-9aaa-223f3930c57c", + "name": "Spawner (226)", + "location": [4384, 1218, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b40de864-46c6-49c8-9be6-7ee491a8c7f6", + "name": "Spawner (226)", + "location": [4468, 1006, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85b70538-e38a-4e69-b0c9-fe1774848e03", + "name": "Spawner (226)", + "location": [4326, 1046, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fcd2029-3cf4-442c-8977-e3f21a4fb8f5", + "name": "Spawner (226)", + "location": [4544, 1128, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "11834407-82df-4241-bf81-5ea9687c955a", + "name": "Spawner (226)", + "location": [4510, 1262, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4be9e0ff-5157-493c-90c2-9c1a1a45e3b1", + "name": "Spawner (226)", + "location": [4590, 1326, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0f15020-27c0-4fd5-ab31-9055a7578081", + "name": "Spawner (226)", + "location": [4526, 986, 10], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a7a1122-3dd4-49c7-b8f4-d0a47d53a32e", + "name": "Spawner (226)", + "location": [4336, 1130, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9fe7ab6c-c783-431b-b05e-65f5ccb275cb", + "name": "Spawner (226)", + "location": [4448, 1334, 3], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8464904f-68c6-4900-a548-1ee24a9605f2", + "name": "Spawner (226)", + "location": [4540, 1454, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "345821d1-8277-47c0-8b9f-a722e65dd2e7", + "name": "Spawner (226)", + "location": [4632, 1164, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f4ab748-bee7-48f9-820b-230e2b2c2f65", + "name": "Spawner (226)", + "location": [4470, 1430, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "90a10ef6-dca6-4f41-9f9a-3ac84050e227", + "name": "Spawner (226)", + "location": [4592, 1424, 3], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f44df805-ac2e-4331-b14f-2ddcee0e34c6", + "name": "Spawner (226)", + "location": [2814, 3500, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f40f3fdb-8bf3-41c7-b8c4-4375c91c4c94", + "name": "Spawner (226)", + "location": [2998, 3590, 15], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bd58b24-3bf1-48b8-9d6f-d8ff61a2e2c4", + "name": "Spawner (226)", + "location": [2864, 150, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bcd487fc-33c6-47ea-b8ab-ba10f86db14f", + "name": "Spawner (226)", + "location": [3292, 570, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76f11cc1-4ca0-4357-a659-026ea69ae181", + "name": "Spawner (226)", + "location": [3206, 366, 9], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "717379e5-9754-4a5f-8083-2a7042b9b403", + "name": "Spawner (226)", + "location": [3172, 664, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c42f7ec-a06f-492b-b19a-4c887a2057b2", + "name": "Spawner (226)", + "location": [3392, 424, 9], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fff42bf6-596f-4059-943f-bf76af7660b8", + "name": "Spawner (226)", + "location": [3076, 150, 1], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4ca5a94-fee0-447a-bed4-4bf70b4a7a8f", + "name": "Spawner (226)", + "location": [3092, 542, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02a348ee-7285-4741-b23e-61a6ef88a7c0", + "name": "Spawner (226)", + "location": [2512, 1066, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b814727e-e122-4a40-af91-bfa4632efc20", + "name": "Spawner (226)", + "location": [2554, 1142, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9228d1aa-d550-4239-a8d1-df8c69125d44", + "name": "Spawner (226)", + "location": [2416, 1172, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09d26956-a96f-4cce-b0e9-37f5bc2afffa", + "name": "Spawner (226)", + "location": [2188, 1986, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a91ed63-e48f-4b5c-8f67-68e1b0519944", + "name": "Spawner (226)", + "location": [2654, 2286, 9], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "51fc697b-edf6-413f-acda-11c084092eb7", + "name": "Spawner (226)", + "location": [2946, 2168, 44], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5355f9c-f463-4132-9204-550d80ed45c5", + "name": "Spawner (226)", + "location": [2744, 2014, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fa4af6f-bbc5-4dd0-b6df-20f4c7971725", + "name": "Spawner (226)", + "location": [2786, 2282, 13], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3badeb6-8bf0-41a6-b6e0-6c77ad3c6ba8", + "name": "Spawner (226)", + "location": [2074, 1972, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9dd37bd-c5f8-4525-b3b8-b06585d7ddaf", + "name": "Spawner (226)", + "location": [2122, 2108, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "892408a1-3f82-4854-ba77-7f37a15582a1", + "name": "Spawner (226)", + "location": [1932, 3125, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88c34d44-6f28-4353-bdea-ed04a0bf415a", + "name": "Spawner (226)", + "location": [1824, 3027, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4e211be3-6af6-4035-a6f0-72704f498f6d", + "name": "Spawner (226)", + "location": [1894, 2942, 19], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "579099c7-42d6-4f8c-843c-a0c993cc3430", + "name": "Spawner (226)", + "location": [1222, 2499, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fd418550-6b49-45a5-9c4a-6896b7fc2a6d", "name": "Spawner (226)", "location": [1005, 2647, 0], @@ -6018,7 +2006,687 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "a2de2354-f225-4181-a7ce-bd709041b5fa", + "name": "Spawner (226)", + "location": [464, 2059, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "438632c6-7114-4c25-9ab4-ec3a0883720d", + "name": "Spawner (226)", + "location": [559, 2081, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8bf5c14b-8f9c-4fa5-96fb-0b8521ffb0b2", + "name": "Spawner (226)", + "location": [883, 2129, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebd28e0c-ea07-4ec1-b767-1ab08f5d8a0a", + "name": "Spawner (226)", + "location": [968, 2476, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1017559-b6f9-44fc-b632-30a61d15f505", + "name": "Spawner (226)", + "location": [994, 2361, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d44d4945-508a-4a08-a281-8ed15b4c0d6b", + "name": "Spawner (226)", + "location": [964, 2214, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0acdd078-559c-40e8-bc74-2490d5c85d71", + "name": "Spawner (226)", + "location": [1087, 2401, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f638b8b-dd33-49f6-8bbf-c7e638a912fe", + "name": "Spawner (226)", + "location": [1222, 2378, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6adc698e-936e-4d69-8870-13a39fb1baa5", + "name": "Spawner (226)", + "location": [1334, 2311, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03ab5a78-b854-4676-bad5-4a37e96c1529", + "name": "Spawner (226)", + "location": [1315, 2192, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "29780577-caca-4fcc-8586-0d7d9f9ea4fc", + "name": "Spawner (226)", + "location": [1426, 2615, 10], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "27a803b9-9e76-4bcd-8d3f-d55d65c8b721", + "name": "Spawner (226)", + "location": [1527, 2609, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e87997b-c05c-429a-a632-a9076a5afd84", + "name": "Spawner (226)", + "location": [1582, 2722, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7f778ca1-e3e1-411e-8029-e62ad6783492", + "name": "Spawner (226)", + "location": [1650, 2796, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e4e5017-002b-4c43-b113-3ae91958fee4", + "name": "Spawner (226)", + "location": [1724, 2781, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc6aeac6-caef-47bd-9f88-91e5852f81fd", + "name": "Spawner (226)", + "location": [1727, 2702, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b654652-e5e1-4cbb-8404-e2760b2f180f", + "name": "Spawner (226)", + "location": [1634, 2645, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b6895418-072d-48b0-92f8-cf2eb6b9d085", + "name": "Spawner (226)", + "location": [1505, 2445, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a58ab96-dff2-424c-9486-829b5e1d0df8", + "name": "Spawner (226)", + "location": [1699, 2560, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f6f3201e-4f38-486d-b797-81a09db9f3bf", + "name": "Spawner (226)", + "location": [1612, 2526, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "ff3449e7-8000-450f-bfb5-af18d60aced2", "name": "Spawner (226)", "location": [1722, 2435, 10], @@ -6052,10 +2720,3342 @@ ] }, { - "$type": "Spawner", - "guid": "fff42bf6-596f-4059-943f-bf76af7660b8", + "type": "Spawner", + "guid": "2b1d8e55-62da-4a75-a626-bc24e0415a5e", "name": "Spawner (226)", - "location": [3076, 150, 1], + "location": [1715, 2307, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4e006fc-cd03-4c67-9622-2b5d4199a265", + "name": "Spawner (226)", + "location": [1531, 2351, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ba37ae4-8aa1-4631-a39e-4d727282f435", + "name": "Spawner (226)", + "location": [1602, 2426, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "87498c2d-dbda-448c-8822-993811da1899", + "name": "Spawner (226)", + "location": [1628, 2334, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4954f31e-56af-4be6-82de-5d909a232b8f", + "name": "Spawner (226)", + "location": [1563, 2277, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f0a0d30-52e5-4e38-9853-06806f9bf957", + "name": "Spawner (226)", + "location": [1503, 2207, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5de7027a-07d5-405f-aba7-ffa1edb10d69", + "name": "Spawner (226)", + "location": [1709, 1992, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fe6b62d-c81a-4440-a608-26c0b40a8f08", + "name": "Spawner (226)", + "location": [1498, 2040, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c88821a4-e46f-4867-9994-f1583d90db1c", + "name": "Spawner (226)", + "location": [1439, 2104, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "18bb4b32-dbcd-416e-aae6-547bb1b13fb1", + "name": "Spawner (226)", + "location": [1387, 2023, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9a18ee1-4484-4c93-8fb9-7a996468639e", + "name": "Spawner (226)", + "location": [1377, 1911, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a1a9dd9-0073-47da-8ea9-f7b932cad724", + "name": "Spawner (226)", + "location": [1459, 1963, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a0a3a37e-6c7f-4636-9680-fed8a5144063", + "name": "Spawner (226)", + "location": [1268, 2018, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e2f4cce-2302-427b-8a93-ca5c43466bfc", + "name": "Spawner (226)", + "location": [1173, 2103, 2], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0caa10e-e991-444f-94e5-66eca46e3520", + "name": "Spawner (226)", + "location": [1070, 2069, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b45e7a5-6dad-4ddf-9d4e-75953242e058", + "name": "Spawner (226)", + "location": [958, 2074, 1], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63a8dea7-3158-4ccf-8312-d60bab954514", + "name": "Spawner (226)", + "location": [999, 1839, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "374ade5f-0b52-4d90-9695-394ffa268943", + "name": "Spawner (226)", + "location": [1011, 1945, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23cb3cb9-b698-4de3-a49f-bd3ed42cac7c", + "name": "Spawner (226)", + "location": [1229, 1655, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "edaec946-a62e-487f-82af-b30ccc8f0ccf", + "name": "Spawner (226)", + "location": [1725, 1447, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12eea555-de46-4ae2-ad79-fb7b8a3f9053", + "name": "Spawner (226)", + "location": [1798, 1471, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47c42859-f7e9-4eac-bc14-7ca42eba61b9", + "name": "Spawner (226)", + "location": [1958, 1305, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "14c3e5bd-aae9-410c-9d47-19ead3ef7c10", + "name": "Spawner (226)", + "location": [1867, 1244, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4e149dd8-9b15-43fb-9b8e-c7fd5974db3e", + "name": "Spawner (226)", + "location": [1865, 1379, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8f29ae6e-ad1a-4e16-9ce6-5ddeec18003f", + "name": "Spawner (226)", + "location": [1769, 1330, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05fcd31c-d53e-417b-8798-65d1e7c2b5de", + "name": "Spawner (226)", + "location": [1666, 1290, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b214fa51-5b5f-466a-8c69-02bfb22af5c7", + "name": "Spawner (226)", + "location": [1486, 1301, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb3efa23-3330-4812-aa08-ee1a5d9b6b61", + "name": "Spawner (226)", + "location": [1351, 1359, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0632e64-37a8-4ea1-9c44-189db60d2285", + "name": "Spawner (226)", + "location": [1231, 1462, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94f6e27d-8653-4b55-86d0-cc131a38aa4b", + "name": "Spawner (226)", + "location": [1017, 1726, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ace2e1ec-f2fe-48b9-8cdd-1cf2482169ea", + "name": "Spawner (226)", + "location": [398, 1305, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "450d4409-e114-42c1-84a7-9cd9e0c5f69e", + "name": "Spawner (226)", + "location": [485, 1398, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be547090-d9da-4bc6-89bc-4dc712ef42af", + "name": "Spawner (226)", + "location": [210, 1447, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "476014f7-4611-401d-bfc1-b5ca816ddd7d", + "name": "Spawner (226)", + "location": [851, 1770, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "35bbcb04-b782-4975-90b0-fe6118462b3e", + "name": "Spawner (226)", + "location": [977, 1638, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b67aaf83-665d-4d9d-95e4-af3072de86cc", + "name": "Spawner (226)", + "location": [919, 1718, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79cda4aa-4199-4530-afd9-a41ce93c93b5", + "name": "Spawner (226)", + "location": [876, 1639, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7305a62d-7a0b-4d9e-88e4-a2d3b0d25462", + "name": "Spawner (226)", + "location": [805, 1570, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1956dff5-b7f6-477e-8fb2-5fd6840f0c8e", + "name": "Spawner (226)", + "location": [725, 1483, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a232c41-76f0-4175-bb91-8dfcc2dfd800", + "name": "Spawner (226)", + "location": [623, 1328, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2ec7572-57d8-4788-878c-8b9b0ac1f6a3", + "name": "Spawner (226)", + "location": [727, 1394, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9d1f842-8619-4a7f-93ec-2125ef399296", + "name": "Spawner (226)", + "location": [1020, 1360, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "92ff3311-a6f6-47a8-8069-9738fb0264d6", + "name": "Spawner (226)", + "location": [1024, 1244, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "821ef012-218a-4691-882b-799f3eef1307", + "name": "Spawner (226)", + "location": [802, 1280, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e93c6ddc-5771-42f2-8e50-fc5ca03b13fd", + "name": "Spawner (226)", + "location": [855, 934, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2d5617a-b4c1-43dc-bed5-1258240f91b2", + "name": "Spawner (226)", + "location": [771, 1013, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01728819-da08-4e42-9763-39cdf14b7707", + "name": "Spawner (226)", + "location": [846, 1071, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "441009bc-1c05-4252-bac0-4dfd6d1b57a6", + "name": "Spawner (226)", + "location": [940, 1028, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6499e448-465f-440e-8425-d8866179eb24", + "name": "Spawner (226)", + "location": [1152, 812, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c7aee851-a76e-47a0-a0c6-e55ee5d71208", + "name": "Spawner (226)", + "location": [1011, 896, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "576c5f8a-deef-4600-aabc-b0494de12d9c", + "name": "Spawner (226)", + "location": [1057, 765, 1], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9252566d-4ebe-4f24-84da-fd9b05dd8694", + "name": "Spawner (226)", + "location": [974, 374, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d1921647-6d81-41cc-8956-618e06170bad", + "name": "Spawner (226)", + "location": [1063, 541, 16], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4a6db11-d166-4762-869e-9d2a74168a90", + "name": "Spawner (226)", + "location": [1018, 629, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "226ea014-96c5-4281-8c3c-6a1742392ac9", + "name": "Spawner (226)", + "location": [951, 542, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f79d72b-455f-483f-b33f-eec6d1c50bc2", + "name": "Spawner (226)", + "location": [805, 576, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8106158-5efa-4600-8c35-ee6a1178c0b3", + "name": "Spawner (226)", + "location": [937, 743, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba4b69f8-15fe-4ca3-9c03-9a9551510113", + "name": "Spawner (226)", + "location": [890, 657, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "260110a5-7142-4fb3-b18b-002b831bc2fa", + "name": "Spawner (226)", + "location": [895, 830, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5cfeda8-987a-4c07-a165-dace4a70402d", + "name": "Spawner (226)", + "location": [689, 753, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76aadaf7-2302-4077-96f3-e6d122b18000", + "name": "Spawner (226)", + "location": [748, 669, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a54dc067-1553-42d8-9c2b-d4029bd44f24", + "name": "Spawner (226)", + "location": [764, 822, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f7ac9eaa-2936-48d1-a3a7-e7de0d1e9293", + "name": "Spawner (226)", + "location": [834, 751, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ef9fd31-972f-43c0-aa33-ed94fab149c2", + "name": "Spawner (226)", + "location": [782, 1156, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15fc7f5b-6386-400e-9490-2d3fbfebf96d", + "name": "Spawner (226)", + "location": [887, 1223, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d906a602-3187-4a61-84f3-ef94a6371a4f", + "name": "Spawner (226)", + "location": [916, 1159, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df86c69d-e7c3-40e3-b819-8d425587d46b", + "name": "Spawner (226)", + "location": [993, 1130, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a96f7041-620d-4f55-8856-106b2d8ecb5a", + "name": "Spawner (226)", + "location": [1047, 1071, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3864b7b7-57d3-43ac-9105-d330ba4b32c9", + "name": "Spawner (226)", + "location": [1092, 1010, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2248238d-727b-42d6-a39b-267d253d3c1e", + "name": "Spawner (226)", + "location": [1172, 974, 2], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "622a57d0-5378-4a17-aee9-2203afc201ce", + "name": "Spawner (226)", + "location": [1388, 784, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc8cd7ae-3f79-400b-b3b6-3e3c630f6198", + "name": "Spawner (226)", + "location": [1631, 640, 22], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93f49e99-b7cc-427f-a04e-93aa82f9e20a", + "name": "Spawner (226)", + "location": [1526, 960, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7f279f10-c41b-43f4-97ee-66aa3cda4c07", + "name": "Spawner (226)", + "location": [1545, 813, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04263280-45f5-494d-9d8d-d36726bde577", + "name": "Spawner (226)", + "location": [1695, 809, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb35b29d-953d-4d35-88f9-becfbcd3c6d6", + "name": "Spawner (226)", + "location": [1728, 691, 6], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fcdb223f-fd95-481f-82a7-b8f7d29ebfed", + "name": "Spawner (226)", + "location": [1802, 495, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e12e7bc4-f8df-4f78-bd8e-84ccec108eec", + "name": "Spawner (226)", + "location": [1857, 600, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e80aa0d0-bfd3-42cd-b3e7-3f341aaed7a4", + "name": "Spawner (226)", + "location": [1884, 735, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b8e890c-1f24-4b07-b0a8-3c0160b64932", + "name": "Spawner (226)", + "location": [1973, 751, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1fd0dfb-3904-4170-b3a9-e85c2aef2021", + "name": "Spawner (226)", + "location": [2085, 658, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "18abaf3e-2dd3-4f3c-afcd-21feb5e65b44", + "name": "Spawner (226)", + "location": [2367, 748, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d720e7e8-da12-4afe-b297-164dcaa76804", + "name": "Spawner (226)", + "location": [2289, 917, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d6b8996-a8c3-4110-9973-14084c70d0b9", + "name": "Spawner (226)", + "location": [2338, 989, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b573305-001f-487c-9dc0-8411bb238161", + "name": "Spawner (226)", + "location": [2418, 999, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6193d4d1-91f8-4237-b7eb-28d1217335bc", + "name": "Spawner (226)", + "location": [2495, 978, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "689f30bd-7852-427e-8327-332dcfc9cbc3", + "name": "Spawner (226)", + "location": [2583, 993, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "adcf2529-b297-4c4e-b1b9-8319df439c20", + "name": "Spawner (226)", + "location": [2513, 782, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bb381bc-3862-4e05-84e8-2e13c62451b8", + "name": "Spawner (226)", + "location": [2748, 660, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4140697-856a-4860-ab7d-30180de15320", + "name": "Spawner (226)", + "location": [2765, 754, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d98da32-7abe-4c6e-bbed-71e5a727f5a9", + "name": "Spawner (226)", + "location": [2668, 1020, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e68886a7-ef39-46d9-ac84-bbc0f201c5a1", + "name": "Spawner (226)", + "location": [2669, 922, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21609f91-35f1-483c-9d4f-970c07c7499b", + "name": "Spawner (226)", + "location": [2672, 814, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6aab7562-4d73-4806-b582-f8c32251e4fd", + "name": "Spawner (226)", + "location": [2643, 728, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ef19957-7ff2-4d70-8ada-ad57fcbda8a7", + "name": "Spawner (226)", + "location": [2888, 391, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1616a270-482b-465c-b9a8-f8103ba729b4", + "name": "Spawner (226)", + "location": [2735, 417, 15], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c67ab1cc-072c-4579-9c12-beb4b562aaac", + "name": "Spawner (226)", + "location": [2914, 547, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fe8f267-3962-4c07-a7dd-23dc702c4830", + "name": "Spawner (226)", + "location": [2814, 492, 15], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f64e4426-d22e-4091-bb29-227354ba124a", + "name": "Spawner (226)", + "location": [2688, 524, 11], "map": "Felucca", "count": 19, "minDelay": "00:05:00", diff --git a/Distribution/Data/Spawns/shared/felucca/Wrong.json b/Distribution/Data/Spawns/post-uoml/felucca/Wrong.json similarity index 93% rename from Distribution/Data/Spawns/shared/felucca/Wrong.json rename to Distribution/Data/Spawns/post-uoml/felucca/Wrong.json index 6f7253813..f441a08c8 100644 --- a/Distribution/Data/Spawns/shared/felucca/Wrong.json +++ b/Distribution/Data/Spawns/post-uoml/felucca/Wrong.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "29168b55-1571-4743-9178-01314586cbe5", "name": "Spawner (227)", "location": [5819, 591, 0], @@ -11,12 +11,99 @@ "team": 0, "homeRange": 5, "walkingRange": 25, + "entries": [{ "name": "JukaWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "419487ab-4c4b-4726-8f93-0cd1b49aed0d", + "name": "Spawner (227)", + "location": [5808, 588, 12], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "JukaWarrior", "maxCount": 2, "probability": 100 } + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "84d09a21-c001-44ed-9c8a-2e7791fa4b1e", + "name": "Spawner (227)", + "location": [5792, 544, 10], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 30, + "entries": [ + { "name": "GolemController", "maxCount": 4, "probability": 100 }, + { "name": "Golem", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63285a8f-9f0d-4966-9f0e-a7fa33fe3a45", + "name": "Spawner (227)", + "location": [5857, 586, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dfb2dd7e-c751-49e6-9059-25b892efce67", + "name": "Spawner (227)", + "location": [5857, 562, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41bd23d3-0a22-41fd-a2d2-5d6d809b2018", + "name": "Spawner (227)", + "location": [5828, 530, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "346ff700-516b-4788-85a2-9ee7146726f9", "name": "Spawner (227)", "location": [5659, 563, 20], @@ -35,96 +122,7 @@ ] }, { - "$type": "Spawner", - "guid": "3ce378dd-3952-4fac-a5fe-ad12a7b09f20", - "name": "Spawner (227)", - "location": [5690, 534, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 30, - "entries": [ - { "name": "JukaLord", "maxCount": 1, "probability": 100 }, - { "name": "JukaMage", "maxCount": 1, "probability": 100 }, - { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "419487ab-4c4b-4726-8f93-0cd1b49aed0d", - "name": "Spawner (227)", - "location": [5808, 588, 12], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "JukaLord", "maxCount": 1, "probability": 100 }, - { "name": "JukaMage", "maxCount": 1, "probability": 100 }, - { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41bd23d3-0a22-41fd-a2d2-5d6d809b2018", - "name": "Spawner (227)", - "location": [5828, 530, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 30, - "entries": [ - { "name": "JukaLord", "maxCount": 1, "probability": 100 }, - { "name": "JukaMage", "maxCount": 1, "probability": 100 }, - { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "63285a8f-9f0d-4966-9f0e-a7fa33fe3a45", - "name": "Spawner (227)", - "location": [5857, 586, 15], - "map": "Felucca", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 30, - "entries": [ - { "name": "JukaLord", "maxCount": 1, "probability": 100 }, - { "name": "JukaMage", "maxCount": 1, "probability": 100 }, - { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84d09a21-c001-44ed-9c8a-2e7791fa4b1e", - "name": "Spawner (227)", - "location": [5792, 544, 10], - "map": "Felucca", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 30, - "entries": [ - { "name": "GolemController", "maxCount": 4, "probability": 100 }, - { "name": "Golem", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "87ed7838-1bc6-4987-bd1e-0cc16bd54f32", "name": "Spawner (227)", "location": [5724, 561, 20], @@ -143,16 +141,16 @@ ] }, { - "$type": "Spawner", - "guid": "dfb2dd7e-c751-49e6-9059-25b892efce67", + "type": "Spawner", + "guid": "3ce378dd-3952-4fac-a5fe-ad12a7b09f20", "name": "Spawner (227)", - "location": [5857, 562, 15], + "location": [5690, 534, 0], "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 7, + "homeRange": 8, "walkingRange": 30, "entries": [ { "name": "JukaLord", "maxCount": 1, "probability": 100 }, diff --git a/Distribution/Data/Spawns/shared/ilshenar/Ancientlair.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Ancientlair.json similarity index 91% rename from Distribution/Data/Spawns/shared/ilshenar/Ancientlair.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Ancientlair.json index 48884cf58..ba2480b60 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Ancientlair.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Ancientlair.json @@ -1,40 +1,20 @@ [ { - "$type": "Spawner", - "guid": "113fa6c7-b58a-48eb-8cd8-18332e92e22e", + "type": "Spawner", + "guid": "cbb2e5a6-ad0e-4bf3-b53d-528756994f1b", "name": "Spawner (301)", - "location": [79, 687, -28], + "location": [50, 689, -28], "map": "Ilshenar", "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", + "minDelay": "00:10:00", + "maxDelay": "00:20:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 1, "probability": 100 } - ] + "homeRange": 30, + "walkingRange": 15, + "entries": [{ "name": "AncientWyrm", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "60859b12-15e4-4d3f-bbc6-b48c940c4080", - "name": "Spawner (301)", - "location": [54, 718, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "6c0fa0c8-c4fb-4c89-a310-528a89dc22e9", "name": "Spawner (301)", "location": [115, 716, -28], @@ -51,7 +31,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9f1df533-f0b6-4d42-8c80-ebf852a0830b", "name": "Spawner (301)", "location": [131, 78, 0], @@ -68,23 +48,24 @@ ] }, { - "$type": "Spawner", - "guid": "cbb2e5a6-ad0e-4bf3-b53d-528756994f1b", + "type": "Spawner", + "guid": "60859b12-15e4-4d3f-bbc6-b48c940c4080", "name": "Spawner (301)", - "location": [50, 689, -28], + "location": [54, 718, -28], "map": "Ilshenar", "count": 1, - "minDelay": "00:10:00", - "maxDelay": "00:20:00", + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, - "walkingRange": 15, + "homeRange": 10, + "walkingRange": 10, "entries": [ - { "name": "AncientWyrm", "maxCount": 1, "probability": 100 } + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fdfe8209-e586-454c-aa1e-fdabf9b43125", "name": "Spawner (301)", "location": [82, 718, -28], @@ -99,5 +80,22 @@ { "name": "FireElemental", "maxCount": 2, "probability": 100 }, { "name": "LavaLizard", "maxCount": 2, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "113fa6c7-b58a-48eb-8cd8-18332e92e22e", + "name": "Spawner (301)", + "location": [79, 687, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Ankh.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Ankh.json similarity index 76% rename from Distribution/Data/Spawns/shared/ilshenar/Ankh.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Ankh.json index 8a9eba8ac..24f22d567 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Ankh.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Ankh.json @@ -1,363 +1,6 @@ [ { - "$type": "Spawner", - "guid": "01be3c7a-f159-4721-aa38-5861f526d3b8", - "name": "Spawner (302)", - "location": [45, 905, -29], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1146a823-b233-4508-89f4-802dc5f67112", - "name": "Spawner (302)", - "location": [66, 1171, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12ed93c0-e81b-4de2-afe6-0c5c34975a41", - "name": "Spawner (302)", - "location": [49, 846, -30], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "192e009f-b329-47a3-b290-cd1af4504db6", - "name": "Spawner (302)", - "location": [148, 953, -29], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Kirin", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "250608fe-f32f-472e-a726-d5e1e3924160", - "name": "Spawner (302)", - "location": [15, 1503, -27], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2de1d85a-a211-4c0d-8cdb-3820a2f4cde3", - "name": "Spawner (302)", - "location": [62, 1446, -28], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "EvilMage", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32d38b45-94a0-429d-b06d-da2941222110", - "name": "Spawner (302)", - "location": [39, 1491, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Daemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40c9f7b2-9ce5-4c7c-ba9c-d78cbd9a7eab", - "name": "Spawner (302)", - "location": [131, 1331, -28], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "LichLord", "maxCount": 1, "probability": 100 }, - { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5644d83b-87a0-46aa-a23d-5166d9d4f2f7", - "name": "Spawner (302)", - "location": [55, 1303, -28], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Skeleton", "maxCount": 3, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5c977889-217c-4562-bc51-d549a3ce616d", - "name": "Spawner (302)", - "location": [29, 1016, -27], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 35, - "entries": [ - { "name": "Drake", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "675f68f6-58e7-4a9c-adef-31aa0ecdbc00", - "name": "Spawner (302)", - "location": [86, 1447, -28], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 15, - "entries": [ - { "name": "Mummy", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6fa3458d-ad0c-4ec0-a058-1d831df7b112", - "name": "Spawner (302)", - "location": [71, 1521, -28], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 40, - "entries": [ - { "name": "HellHound", "maxCount": 2, "probability": 100 }, - { "name": "Imp", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94a6eb04-35cb-49c0-8697-ac3caf736e1a", - "name": "Spawner (302)", - "location": [124, 1435, -16], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "SkeletalMage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94b96b34-65e4-4af5-a3cd-56c2c8e8478f", - "name": "Spawner (302)", - "location": [473, 1552, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "SerpentineDragon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9a01a01c-2e9f-46ad-b9a9-0d1af7d3b211", - "name": "Spawner (302)", - "location": [102, 959, -43], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "PoisonElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9bca331b-b283-436c-b46b-446d69e18593", - "name": "Spawner (302)", - "location": [108, 1110, -27], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9debe6c0-b766-4c9a-94c7-2f4db1d1a7fd", - "name": "Spawner (302)", - "location": [34, 1462, -28], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "BloodElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a474135e-6997-45bf-a865-77ca9c5c854b", - "name": "Spawner (302)", - "location": [29, 1009, -27], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 35, - "entries": [ - { "name": "Dragon", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b88fa850-e3c3-468e-b8e4-afc1430e5ad6", - "name": "Spawner (302)", - "location": [87, 1051, -29], - "map": "Ilshenar", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 35, - "entries": [ - { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c37e0854-914d-46a7-850f-4b8072add151", - "name": "Spawner (302)", - "location": [87, 1471, -22], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "EvilMage", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c5dbed97-1066-4bcc-8b21-cffeb33843ad", - "name": "Spawner (302)", - "location": [99, 1158, -23], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "AcidElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cb063844-675e-4177-9da9-7e3c1d2704af", - "name": "Spawner (302)", - "location": [63, 1471, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "EvilMage", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e3b4ec7a-974a-42ef-b9be-80b93317c858", "name": "Spawner (302)", "location": [129, 1530, -28], @@ -375,39 +18,108 @@ ] }, { - "$type": "Spawner", - "guid": "e4279b1a-4375-4a93-9ed5-df2a8f9ade4a", + "type": "Spawner", + "guid": "6fa3458d-ad0c-4ec0-a058-1d831df7b112", "name": "Spawner (302)", - "location": [438, 1552, -28], + "location": [71, 1521, -28], "map": "Ilshenar", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 20, + "homeRange": 15, + "walkingRange": 40, "entries": [ - { "name": "Pixie", "maxCount": 5, "probability": 100 } + { "name": "HellHound", "maxCount": 2, "probability": 100 }, + { "name": "Imp", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e73b0e45-8071-4f7a-8126-d3b05a6ac95e", + "type": "Spawner", + "guid": "c37e0854-914d-46a7-850f-4b8072add151", "name": "Spawner (302)", - "location": [114, 908, -26], + "location": [87, 1471, -22], "map": "Ilshenar", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "EarthElemental", "maxCount": 3, "probability": 100 } - ] + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "EvilMage", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "cb063844-675e-4177-9da9-7e3c1d2704af", + "name": "Spawner (302)", + "location": [63, 1471, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "EvilMage", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "32d38b45-94a0-429d-b06d-da2941222110", + "name": "Spawner (302)", + "location": [39, 1491, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Daemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9debe6c0-b766-4c9a-94c7-2f4db1d1a7fd", + "name": "Spawner (302)", + "location": [34, 1462, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "BloodElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "250608fe-f32f-472e-a726-d5e1e3924160", + "name": "Spawner (302)", + "location": [15, 1503, -27], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2de1d85a-a211-4c0d-8cdb-3820a2f4cde3", + "name": "Spawner (302)", + "location": [62, 1446, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "EvilMage", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "e87e63f3-2c4e-4a95-ad84-7cae33a9071b", "name": "Spawner (302)", "location": [31, 1410, -28], @@ -418,44 +130,38 @@ "team": 0, "homeRange": 7, "walkingRange": 11, - "entries": [ - { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "SkeletalKnight", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ed586ce9-3e23-403c-b92a-013f2e88fb3c", + "type": "Spawner", + "guid": "675f68f6-58e7-4a9c-adef-31aa0ecdbc00", "name": "Spawner (302)", - "location": [107, 1390, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f444295e-dedd-4070-8386-54b35d4ac4bb", - "name": "Spawner (302)", - "location": [451, 1552, -27], + "location": [86, 1447, -28], "map": "Ilshenar", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Wisp", "maxCount": 3, "probability": 100 } - ] + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "Mummy", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "94a6eb04-35cb-49c0-8697-ac3caf736e1a", + "name": "Spawner (302)", + "location": [124, 1435, -16], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "SkeletalMage", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "fc88bc1a-1266-4ae7-ad00-50ecb32896ff", "name": "Spawner (302)", "location": [124, 1436, -16], @@ -466,12 +172,41 @@ "team": 0, "homeRange": 7, "walkingRange": 15, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed586ce9-3e23-403c-b92a-013f2e88fb3c", + "name": "Spawner (302)", + "location": [107, 1390, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40c9f7b2-9ce5-4c7c-ba9c-d78cbd9a7eab", + "name": "Spawner (302)", + "location": [131, 1331, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fd6c1c50-ee38-412c-8957-3f74936e8020", "name": "Spawner (302)", "location": [132, 1332, -28], @@ -482,8 +217,223 @@ "team": 0, "homeRange": 20, "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5644d83b-87a0-46aa-a23d-5166d9d4f2f7", + "name": "Spawner (302)", + "location": [55, 1303, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, "entries": [ - { "name": "Zombie", "maxCount": 4, "probability": 100 } + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "01be3c7a-f159-4721-aa38-5861f526d3b8", + "name": "Spawner (302)", + "location": [45, 905, -29], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12ed93c0-e81b-4de2-afe6-0c5c34975a41", + "name": "Spawner (302)", + "location": [49, 846, -30], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "PoisonElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e73b0e45-8071-4f7a-8126-d3b05a6ac95e", + "name": "Spawner (302)", + "location": [114, 908, -26], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "EarthElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9a01a01c-2e9f-46ad-b9a9-0d1af7d3b211", + "name": "Spawner (302)", + "location": [102, 959, -43], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "PoisonElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "192e009f-b329-47a3-b290-cd1af4504db6", + "name": "Spawner (302)", + "location": [148, 953, -29], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Kirin", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a474135e-6997-45bf-a865-77ca9c5c854b", + "name": "Spawner (302)", + "location": [29, 1009, -27], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 35, + "entries": [{ "name": "Dragon", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5c977889-217c-4562-bc51-d549a3ce616d", + "name": "Spawner (302)", + "location": [29, 1016, -27], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 35, + "entries": [{ "name": "Drake", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b88fa850-e3c3-468e-b8e4-afc1430e5ad6", + "name": "Spawner (302)", + "location": [87, 1051, -29], + "map": "Ilshenar", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [ + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9bca331b-b283-436c-b46b-446d69e18593", + "name": "Spawner (302)", + "location": [108, 1110, -27], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c5dbed97-1066-4bcc-8b21-cffeb33843ad", + "name": "Spawner (302)", + "location": [99, 1158, -23], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "AcidElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1146a823-b233-4508-89f4-802dc5f67112", + "name": "Spawner (302)", + "location": [66, 1171, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Nightmare", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e4279b1a-4375-4a93-9ed5-df2a8f9ade4a", + "name": "Spawner (302)", + "location": [438, 1552, -28], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Pixie", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f444295e-dedd-4070-8386-54b35d4ac4bb", + "name": "Spawner (302)", + "location": [451, 1552, -27], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Wisp", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "94b96b34-65e4-4af5-a3cd-56c2c8e8478f", + "name": "Spawner (302)", + "location": [473, 1552, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "SerpentineDragon", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Blood.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Blood.json similarity index 73% rename from Distribution/Data/Spawns/shared/ilshenar/Blood.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Blood.json index 8ad44ab88..d33444bb9 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Blood.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Blood.json @@ -1,6 +1,20 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "b36243ef-5201-4922-972a-8e0dd78ffe11", + "name": "Spawner (303)", + "location": [2114, 846, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "1a119f51-d328-4a35-bda3-d63ca1d2f3f0", "name": "Spawner (303)", "location": [2051, 837, -28], @@ -11,60 +25,24 @@ "team": 0, "homeRange": 7, "walkingRange": 15, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "1e39d497-d9f3-48ba-8c6a-af1bf7a9ba3d", + "type": "Spawner", + "guid": "8540d8e1-53c7-416b-b611-94127a44d56c", "name": "Spawner (303)", - "location": [2145, 911, -28], + "location": [2050, 859, -28], "map": "Ilshenar", - "count": 1, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f6a7fef-6d82-4d17-8505-0927a56a3302", - "name": "Spawner (303)", - "location": [2083, 911, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2dbb818a-3f08-4752-bfb0-414d433fa5d3", - "name": "Spawner (303)", - "location": [2146, 951, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, + "homeRange": 7, "walkingRange": 15, - "entries": [ - { "name": "Daemon", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "HellCat", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2e60ce70-cd00-4d8d-82fb-0733b5b06c32", "name": "Spawner (303)", "location": [2090, 868, -14], @@ -75,12 +53,108 @@ "team": 0, "homeRange": 7, "walkingRange": 15, - "entries": [ - { "name": "HellCat", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "HellCat", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "d0c57916-16d8-4135-ba77-c43c93947a9d", + "name": "Spawner (303)", + "location": [2177, 836, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d083d0d-b124-4567-96ec-6a35ac8cbc06", + "name": "Spawner (303)", + "location": [2177, 858, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b73a5bca-9065-495e-8801-7b776732c00b", + "name": "Spawner (303)", + "location": [2138, 868, -14], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49e5b4ac-570c-43eb-85cc-8c113c5bea7b", + "name": "Spawner (303)", + "location": [2138, 868, -14], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f6a7fef-6d82-4d17-8505-0927a56a3302", + "name": "Spawner (303)", + "location": [2083, 911, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a670a008-155a-4420-ab3c-e15e3e951f83", + "name": "Spawner (303)", + "location": [2115, 917, -23], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1e39d497-d9f3-48ba-8c6a-af1bf7a9ba3d", + "name": "Spawner (303)", + "location": [2145, 911, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "2eada9f7-16d7-4d0e-9418-9e013208bf4f", "name": "Spawner (303)", "location": [2170, 920, -28], @@ -91,12 +165,80 @@ "team": 0, "homeRange": 10, "walkingRange": 32, - "entries": [ - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "7f72864a-7c9b-41ce-abda-a205be287234", + "name": "Spawner (303)", + "location": [2058, 907, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d08b49a2-1291-4d12-a97f-50aee1c289ff", + "name": "Spawner (303)", + "location": [2058, 932, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8b88f31e-d1c6-4a32-b719-2062d630872f", + "name": "Spawner (303)", + "location": [2083, 951, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Daemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ac12b1c2-8db2-4664-91db-7381d677eb02", + "name": "Spawner (303)", + "location": [2114, 951, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Daemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2dbb818a-3f08-4752-bfb0-414d433fa5d3", + "name": "Spawner (303)", + "location": [2146, 951, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Daemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "3a8ab857-7a3d-4cd4-9466-567fd9d0b3ea", "name": "Spawner (303)", "location": [2113, 1017, -28], @@ -111,181 +253,5 @@ { "name": "Succubus", "maxCount": 2, "probability": 100 }, { "name": "HellCat", "maxCount": 4, "probability": 100 } ] - }, - { - "$type": "Spawner", - "guid": "49e5b4ac-570c-43eb-85cc-8c113c5bea7b", - "name": "Spawner (303)", - "location": [2138, 868, -14], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d083d0d-b124-4567-96ec-6a35ac8cbc06", - "name": "Spawner (303)", - "location": [2177, 858, -28], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 15, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7f72864a-7c9b-41ce-abda-a205be287234", - "name": "Spawner (303)", - "location": [2058, 907, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8540d8e1-53c7-416b-b611-94127a44d56c", - "name": "Spawner (303)", - "location": [2050, 859, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 15, - "entries": [ - { "name": "HellCat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8b88f31e-d1c6-4a32-b719-2062d630872f", - "name": "Spawner (303)", - "location": [2083, 951, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Daemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a670a008-155a-4420-ab3c-e15e3e951f83", - "name": "Spawner (303)", - "location": [2115, 917, -23], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ac12b1c2-8db2-4664-91db-7381d677eb02", - "name": "Spawner (303)", - "location": [2114, 951, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Daemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b36243ef-5201-4922-972a-8e0dd78ffe11", - "name": "Spawner (303)", - "location": [2114, 846, -28], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b73a5bca-9065-495e-8801-7b776732c00b", - "name": "Spawner (303)", - "location": [2138, 868, -14], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 15, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d08b49a2-1291-4d12-a97f-50aee1c289ff", - "name": "Spawner (303)", - "location": [2058, 932, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d0c57916-16d8-4135-ba77-c43c93947a9d", - "name": "Spawner (303)", - "location": [2177, 836, -28], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 15, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Exodus.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Exodus.json similarity index 82% rename from Distribution/Data/Spawns/shared/ilshenar/Exodus.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Exodus.json index 7476c42b9..1691ae1ab 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Exodus.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Exodus.json @@ -1,172 +1,6 @@ [ { - "$type": "Spawner", - "guid": "1156d090-d270-4d2c-99b5-d9801dc4b411", - "name": "Spawner (304)", - "location": [1932, 196, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "GolemController", "maxCount": 2, "probability": 100 }, - { "name": "Golem", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21934889-6573-468b-b7e9-40d090d3e9e3", - "name": "Spawner (304)", - "location": [1995, 67, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "GolemController", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "224edb01-38bc-4e79-abee-c1754f857c1a", - "name": "Spawner (304)", - "location": [2063, 183, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "GargoyleEnforcer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2704244a-7822-45bf-bc70-120c145fc097", - "name": "Spawner (304)", - "location": [1979, 192, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "ExodusOverseer", "maxCount": 2, "probability": 100 }, - { "name": "ExodusMinion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2cddb981-ac94-4967-9c20-2cc3de1a52c9", - "name": "Spawner (304)", - "location": [1980, 39, -28], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "ExodusOverseer", "maxCount": 3, "probability": 100 }, - { "name": "ExodusMinion", "maxCount": 3, "probability": 100 }, - { "name": "GolemController", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "35394aa1-6039-446f-b247-51d9f7926f6c", - "name": "Spawner (304)", - "location": [1894, 116, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "GolemController", "maxCount": 2, "probability": 100 }, - { "name": "ExodusOverseer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a54ee30-73d0-4f81-8889-ace39a951514", - "name": "Spawner (304)", - "location": [2032, 184, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "GargoyleEnforcer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "412a318b-d693-413c-86e6-92996375f16e", - "name": "Spawner (304)", - "location": [1896, 72, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "GargoyleDestroyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75b1d8fc-c98d-42c5-9445-12024a38f167", - "name": "Spawner (304)", - "location": [1895, 160, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "GargoyleDestroyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8c609c03-ea54-4cb3-8e79-ae67bbe813a8", - "name": "Spawner (304)", - "location": [2044, 87, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "ExodusMinion", "maxCount": 2, "probability": 100 }, - { "name": "Golem", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "8e098781-936b-42cc-adfa-806111b9a9f2", "name": "Spawner (304)", "location": [1980, 116, -28], @@ -183,7 +17,52 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "d39e9374-f30b-488c-8a12-5f3a6315f391", + "name": "Spawner (304)", + "location": [1939, 116, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "ExodusMinion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "35394aa1-6039-446f-b247-51d9f7926f6c", + "name": "Spawner (304)", + "location": [1894, 116, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [ + { "name": "GolemController", "maxCount": 2, "probability": 100 }, + { "name": "ExodusOverseer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a35646cf-0460-48d2-a4ea-5e9cc33ad9ab", + "name": "Spawner (304)", + "location": [1964, 164, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "918f3735-8e70-42c3-a633-2fe03730f22d", "name": "Spawner (304)", "location": [1932, 164, -28], @@ -194,15 +73,103 @@ "team": 0, "homeRange": 10, "walkingRange": 15, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1156d090-d270-4d2c-99b5-d9801dc4b411", + "name": "Spawner (304)", + "location": [1932, 196, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, "entries": [ - { "name": "GolemController", "maxCount": 1, "probability": 100 } + { "name": "GolemController", "maxCount": 2, "probability": 100 }, + { "name": "Golem", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "98c1814f-9347-402a-93aa-0d3fad5ca8e5", + "type": "Spawner", + "guid": "75b1d8fc-c98d-42c5-9445-12024a38f167", "name": "Spawner (304)", - "location": [2071, 116, -28], + "location": [1895, 160, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleDestroyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2704244a-7822-45bf-bc70-120c145fc097", + "name": "Spawner (304)", + "location": [1979, 192, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "ExodusOverseer", "maxCount": 2, "probability": 100 }, + { "name": "ExodusMinion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f7bef168-af9c-4bc5-9928-348891d481c0", + "name": "Spawner (304)", + "location": [1996, 164, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a54ee30-73d0-4f81-8889-ace39a951514", + "name": "Spawner (304)", + "location": [2032, 184, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleEnforcer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "224edb01-38bc-4e79-abee-c1754f857c1a", + "name": "Spawner (304)", + "location": [2063, 183, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleEnforcer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dddffe92-2c45-4677-9690-2e782e597530", + "name": "Spawner (304)", + "location": [2058, 163, -28], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", @@ -211,12 +178,12 @@ "homeRange": 10, "walkingRange": 15, "entries": [ - { "name": "GolemController", "maxCount": 2, "probability": 100 }, - { "name": "ExodusOverseer", "maxCount": 2, "probability": 100 } + { "name": "GargoyleEnforcer", "maxCount": 2, "probability": 100 }, + { "name": "GargoyleDestroyer", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a2ff6be0-f521-42fa-8cda-0ca0436133d6", "name": "Spawner (304)", "location": [2044, 143, -28], @@ -234,10 +201,44 @@ ] }, { - "$type": "Spawner", - "guid": "a35646cf-0460-48d2-a4ea-5e9cc33ad9ab", + "type": "Spawner", + "guid": "98c1814f-9347-402a-93aa-0d3fad5ca8e5", "name": "Spawner (304)", - "location": [1964, 164, -28], + "location": [2071, 116, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "GolemController", "maxCount": 2, "probability": 100 }, + { "name": "ExodusOverseer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c609c03-ea54-4cb3-8e79-ae67bbe813a8", + "name": "Spawner (304)", + "location": [2044, 87, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "ExodusMinion", "maxCount": 2, "probability": 100 }, + { "name": "Golem", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3d9b4cc-ec56-4fed-a26c-330d2bd4edac", + "name": "Spawner (304)", + "location": [2031, 48, -28], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -245,12 +246,56 @@ "team": 0, "homeRange": 10, "walkingRange": 15, + "entries": [{ "name": "GargoyleEnforcer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f226735c-6233-4aba-989b-d6f90cd48e2f", + "name": "Spawner (304)", + "location": [2038, 67, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleDestroyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21934889-6573-468b-b7e9-40d090d3e9e3", + "name": "Spawner (304)", + "location": [1995, 67, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2cddb981-ac94-4967-9c20-2cc3de1a52c9", + "name": "Spawner (304)", + "location": [1980, 39, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, "entries": [ - { "name": "GolemController", "maxCount": 1, "probability": 100 } + { "name": "ExodusOverseer", "maxCount": 3, "probability": 100 }, + { "name": "ExodusMinion", "maxCount": 3, "probability": 100 }, + { "name": "GolemController", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b44ae0e1-0a06-4385-b550-c72c58b6b41f", "name": "Spawner (304)", "location": [1931, 67, -28], @@ -261,12 +306,24 @@ "team": 0, "homeRange": 10, "walkingRange": 15, - "entries": [ - { "name": "GolemController", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "412a318b-d693-413c-86e6-92996375f16e", + "name": "Spawner (304)", + "location": [1896, 72, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleDestroyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "c6d64523-ac82-4ad3-9f39-f2d80731a5a3", "name": "Spawner (304)", "location": [1932, 35, -28], @@ -281,86 +338,5 @@ { "name": "GolemController", "maxCount": 2, "probability": 100 }, { "name": "Golem", "maxCount": 2, "probability": 100 } ] - }, - { - "$type": "Spawner", - "guid": "d39e9374-f30b-488c-8a12-5f3a6315f391", - "name": "Spawner (304)", - "location": [1939, 116, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "ExodusMinion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dddffe92-2c45-4677-9690-2e782e597530", - "name": "Spawner (304)", - "location": [2058, 163, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "GargoyleEnforcer", "maxCount": 2, "probability": 100 }, - { "name": "GargoyleDestroyer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e3d9b4cc-ec56-4fed-a26c-330d2bd4edac", - "name": "Spawner (304)", - "location": [2031, 48, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "GargoyleEnforcer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f226735c-6233-4aba-989b-d6f90cd48e2f", - "name": "Spawner (304)", - "location": [2038, 67, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "GargoyleDestroyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f7bef168-af9c-4bc5-9928-348891d481c0", - "name": "Spawner (304)", - "location": [1996, 164, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "GolemController", "maxCount": 1, "probability": 100 } - ] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Mushroom.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Mushroom.json similarity index 70% rename from Distribution/Data/Spawns/shared/ilshenar/Mushroom.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Mushroom.json index f7ccfa7b4..894d63e31 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Mushroom.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Mushroom.json @@ -1,22 +1,6 @@ [ { - "$type": "Spawner", - "guid": "3e3f2805-df30-4d29-abdd-e8a53002b1d2", - "name": "Spawner (305)", - "location": [1415, 1498, -27], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:02:00", - "maxDelay": "00:05:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "MushroomTrap", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "46803838-2abe-45af-8c77-4deccf2e99d7", "name": "Spawner (305)", "location": [1444, 1519, -28], @@ -27,28 +11,10 @@ "team": 0, "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "MushroomTrap", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "MushroomTrap", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e7588959-6148-4bf7-a0b1-829b3dca6e1b", - "name": "Spawner (305)", - "location": [1464, 1495, -28], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:02:00", - "maxDelay": "00:05:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MushroomTrap", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f5a9bc72-4489-4b3a-83ee-7c476d4c9396", "name": "Spawner (305)", "location": [1441, 1532, -28], @@ -59,8 +25,34 @@ "team": 0, "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "MushroomTrap", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "MushroomTrap", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3e3f2805-df30-4d29-abdd-e8a53002b1d2", + "name": "Spawner (305)", + "location": [1415, 1498, -27], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:02:00", + "maxDelay": "00:05:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "MushroomTrap", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e7588959-6148-4bf7-a0b1-829b3dca6e1b", + "name": "Spawner (305)", + "location": [1464, 1495, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:02:00", + "maxDelay": "00:05:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MushroomTrap", "maxCount": 4, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/post-uoml/ilshenar/Outdoors.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Outdoors.json index d4d54d323..5e2ea4035 100644 --- a/Distribution/Data/Spawns/post-uoml/ilshenar/Outdoors.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Outdoors.json @@ -1,6 +1,731 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "bb3daf66-4926-47b9-9113-9bf6e30303f3", + "name": "Spawner (306)", + "location": [1763, 344, 52], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Dragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fca9b079-0c6f-4836-9860-39e260c7896d", + "name": "Spawner (306)", + "location": [1790, 435, -10], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [{ "name": "EarthElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40d88f7b-2b85-4fb5-a207-7ce74ecf3f3a", + "name": "Spawner (306)", + "location": [1805, 469, -9], + "map": "Ilshenar", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Scorpion", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "58561047-30f5-4a7d-9aa9-6430566a554a", + "name": "Spawner (306)", + "location": [1852, 484, -13], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c73818ae-1121-46ae-972e-b60e6a670889", + "name": "Spawner (306)", + "location": [1856, 517, -14], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Brigand", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "82fd9f36-5fca-4deb-bbdf-7fe85fba6344", + "name": "Spawner (306)", + "location": [1775, 530, 66], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "AirElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "82d08b20-2c54-4d66-b165-832eb2d9eefd", + "name": "Spawner (306)", + "location": [1828, 601, 38], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Wyvern", "maxCount": 2, "probability": 100 }, + { "name": "Mummy", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fbd4d134-ec9e-4642-be28-e1b392df4d52", + "name": "Spawner (306)", + "location": [1717, 606, 17], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "OrcCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c02c8708-ba44-46e1-9373-c0cce877084f", + "name": "Spawner (306)", + "location": [1647, 602, -14], + "map": "Ilshenar", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DesertOstard", "maxCount": 5, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 2, "probability": 100 }, + { "name": "Efreet", "maxCount": 1, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fda17c85-19c4-4c90-b848-0c1c135ac5e2", + "name": "Spawner (306)", + "location": [1593, 602, 7], + "map": "Ilshenar", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DesertOstard", "maxCount": 5, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "Efreet", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1b1ec95-54f3-4f98-a891-4dfd576a0ac6", + "name": "Spawner (306)", + "location": [1700, 546, 5], + "map": "Ilshenar", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 14, "probability": 100 }, + { "name": "Scorpion", "maxCount": 14, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cebde1e6-ed55-4160-87b6-acf8934a3778", + "name": "Spawner (306)", + "location": [1580, 538, -14], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 7, + "entries": [ + { "name": "Ratman", "maxCount": 2, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da798d63-b7b0-484f-a345-8f4849216de3", + "name": "Spawner (306)", + "location": [1580, 554, -13], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 7, + "entries": [ + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bfef4953-b3bc-4ea4-a2f6-0d9885c7a2da", + "name": "Spawner (306)", + "location": [1534, 631, -14], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "88063e8a-ea72-4a69-945a-274e4cd2f6a2", + "name": "Spawner (306)", + "location": [1530, 667, -14], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb648bc8-9c84-4454-9770-4d21d69c0774", + "name": "Spawner (306)", + "location": [1534, 600, -14], + "map": "Ilshenar", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Shade", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Wraith", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eac70a7f-7a65-43b1-9292-599632f696ae", + "name": "Spawner (306)", + "location": [1445, 681, -1], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Imp", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "628ed55b-7593-471e-8676-3a017e26ea43", + "name": "Spawner (306)", + "location": [1400, 687, 32], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Harpy", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1df0bbb8-c293-4d0b-8ddc-080926fa4246", + "name": "Spawner (306)", + "location": [1352, 667, 111], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 22, + "walkingRange": 22, + "entries": [ + { "name": "FrostTroll", "maxCount": 3, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, + { "name": "IceElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3585e152-9781-42d6-8524-88edff5ca30e", + "name": "Spawner (306)", + "location": [1372, 623, 111], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "IceElemental", "maxCount": 1, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c699c4f-00f9-4e38-b62a-b94f5cfbcffb", + "name": "Spawner (306)", + "location": [1316, 633, 106], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "FrostTroll", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, + { "name": "IceElemental", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20d82817-a691-4340-b341-3aadd53f0471", + "name": "Spawner (306)", + "location": [1442, 576, -11], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Shade", "maxCount": 6, "probability": 100 }, + { "name": "Spectre", "maxCount": 6, "probability": 100 }, + { "name": "Wraith", "maxCount": 6, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d92c3d5-24c0-4caa-b732-0acaa35b5a4b", + "name": "Spawner (306)", + "location": [1400, 572, -13], + "map": "Ilshenar", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Efreet", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ba57b1c-8c9a-4b21-9d44-44a36804e6c5", + "name": "Spawner (306)", + "location": [1421, 517, 1], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a3db698-ab8a-4794-b798-79dcb81d2229", + "name": "Spawner (306)", + "location": [1358, 498, 3], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "HealerCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1e5b54c0-d694-4a14-ade1-0da78a1829b8", + "name": "Spawner (306)", + "location": [1319, 560, -4], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Gargoyle", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9320cae4-3837-4cb8-8ef7-9238f5ca2eae", + "name": "Spawner (306)", + "location": [1237, 473, -4], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7de1e641-1ffc-4161-b38f-a3730aeab3a2", + "name": "Spawner (306)", + "location": [1275, 401, -1], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 40, + "entries": [ + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Harpy", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1abdf777-6592-4e3e-8ce2-9863e7975f8d", + "name": "Spawner (306)", + "location": [1445, 384, 22], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Troll", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9cdf8453-6a90-4b5b-baf9-9698ab361f4a", + "name": "Spawner (306)", + "location": [1416, 317, 74], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "AncientWyrm", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8ccefff3-a611-4d68-856d-c55d3f0f1c68", + "name": "Spawner (306)", + "location": [1496, 280, -10], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Imp", "maxCount": 3, "probability": 100 }, + { "name": "Daemon", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a4f9334f-bdb9-411b-b848-4b41043b7a5c", + "name": "Spawner (306)", + "location": [1464, 241, 2], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Ettin", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b158181c-8e65-42d1-ad74-5f8755e9e21e", + "name": "Spawner (306)", + "location": [1503, 239, -7], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Ettin", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "38b2eaed-8ee0-4992-bc2d-b0b916e515b2", + "name": "Spawner (306)", + "location": [1571, 253, 14], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Imp", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04bb184e-3ba8-40ea-90db-59479a0e21a2", + "name": "Spawner (306)", + "location": [1070, 750, -80], + "map": "Ilshenar", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SewerRat", "maxCount": 4, "probability": 100 }, + { "name": "Corpser", "maxCount": 2, "probability": 100 }, + { "name": "Bogling", "maxCount": 2, "probability": 100 }, + { "name": "GiantRat", "maxCount": 2, "probability": 100 }, + { "name": "Alligator", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1bbb08b1-5a8f-49fa-923e-9e1a7c1a2758", + "name": "Spawner (306)", + "location": [1070, 750, -80], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 4, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 2, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0bc9fdae-c152-4e2e-a117-f7664d7a7f30", + "name": "Spawner (306)", + "location": [1135, 743, -80], + "map": "Ilshenar", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SewerRat", "maxCount": 4, "probability": 100 }, + { "name": "Corpser", "maxCount": 2, "probability": 100 }, + { "name": "Bogling", "maxCount": 2, "probability": 100 }, + { "name": "GiantRat", "maxCount": 2, "probability": 100 }, + { "name": "Alligator", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50392cce-759c-41f2-8ceb-926f00d1a455", + "name": "Spawner (306)", + "location": [1135, 743, -80], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 4, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 2, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "BogThing", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "16b1d51f-a63a-402c-baf0-fccb22964b64", + "name": "Spawner (306)", + "location": [1160, 681, -80], + "map": "Ilshenar", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SewerRat", "maxCount": 4, "probability": 100 }, + { "name": "Corpser", "maxCount": 2, "probability": 100 }, + { "name": "Bogling", "maxCount": 1, "probability": 100 }, + { "name": "GiantRat", "maxCount": 2, "probability": 100 }, + { "name": "Alligator", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "209ff492-213a-4e4f-bc0f-e00be14de63a", + "name": "Spawner (306)", + "location": [1160, 681, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2078f000-de9a-492b-a35f-fa4a8cdff17c", + "name": "Spawner (306)", + "location": [1097, 702, -79], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 7, + "entries": [ + { "name": "GolemController", "maxCount": 1, "probability": 100 }, + { "name": "Golem", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6753c19-0c02-4974-8f9a-ec93fb28116e", + "name": "Spawner (306)", + "location": [1087, 698, -80], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 7, + "entries": [ + { "name": "GolemController", "maxCount": 1, "probability": 100 }, + { "name": "Golem", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e15d8287-9350-468d-8144-dd3baa906a53", + "name": "Spawner (306)", + "location": [1086, 702, -60], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "GolemController", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "7431d589-5e24-4f05-b479-30dc17c7e942", "name": "Spawner (306)", "location": [1098, 698, -60], @@ -17,23 +742,7 @@ ] }, { - "$type": "Spawner", - "guid": "8ed26f0f-de07-4412-bc99-6cee204f1f40", - "name": "Spawner (306)", - "location": [1072, 651, -60], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 6, - "entries": [ - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "d7d89534-d330-4b6a-a6b0-83187bf9ff54", "name": "Spawner (306)", "location": [1091, 702, -40], @@ -44,24 +753,2414 @@ "team": 0, "homeRange": 5, "walkingRange": 10, - "entries": [ - { "name": "Golem", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Golem", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e15d8287-9350-468d-8144-dd3baa906a53", + "type": "Spawner", + "guid": "126847de-2d7c-49ce-baba-7939b13f5751", "name": "Spawner (306)", - "location": [1086, 702, -60], + "location": [1084, 675, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [{ "name": "JukaMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "78364853-0fe1-4bc7-b3e9-2c8cb742514c", + "name": "Spawner (306)", + "location": [1059, 675, -79], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9066bb92-197e-4a1d-bc36-61ead0f50a79", + "name": "Spawner (306)", + "location": [1060, 659, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4fbf340-bb61-47b8-8e2f-ac6ed604214a", + "name": "Spawner (306)", + "location": [1060, 643, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6a7e29f4-5aa1-4106-94df-76d248d282f4", + "name": "Spawner (306)", + "location": [1075, 628, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [{ "name": "JukaMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "85ff24c5-747c-429c-ac5b-288a259572f4", + "name": "Spawner (306)", + "location": [1092, 628, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b30f735e-cc37-4cae-b3a2-4bd44bf5441e", + "name": "Spawner (306)", + "location": [1083, 638, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "caf251aa-ecef-47a2-9671-c040818d4ea6", + "name": "Spawner (306)", + "location": [1095, 638, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "JukaMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ec64bd53-d037-42d7-941a-aa2a0da52ce7", + "name": "Spawner (306)", + "location": [1095, 664, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0636eb85-29c6-4f6c-b3fa-56930c3ab4b0", + "name": "Spawner (306)", + "location": [1083, 664, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "JukaLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1bf4cbee-2835-4c1e-aa54-49f61260836e", + "name": "Spawner (306)", + "location": [1070, 639, -79], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "JukaLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ace0f33f-dea2-40b1-a1d8-5d8404cdc903", + "name": "Spawner (306)", + "location": [1070, 650, -79], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 5, - "walkingRange": 5, + "walkingRange": 7, "entries": [ - { "name": "GolemController", "maxCount": 2, "probability": 100 } + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ed26f0f-de07-4412-bc99-6cee204f1f40", + "name": "Spawner (306)", + "location": [1072, 651, -60], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "87acf4d6-d8a3-4302-82e7-da7e4bc8b040", + "name": "Spawner (306)", + "location": [1083, 628, -60], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4f9a9db0-6b48-4134-be58-16590bbcd28d", + "name": "Spawner (306)", + "location": [1107, 627, -59], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [{ "name": "JukaMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dc6d3f64-16bf-4643-af4d-d4806ec1fc80", + "name": "Spawner (306)", + "location": [1083, 675, -60], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [{ "name": "Betrayer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "37253fe5-959b-45cf-88ab-8b6551fbbab7", + "name": "Spawner (306)", + "location": [1088, 661, -60], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 9, + "walkingRange": 10, + "entries": [ + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 1, "probability": 100 }, + { "name": "Juggernaut", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f2b8727-6b0d-4ce1-97f5-6893895a89bd", + "name": "Spawner (306)", + "location": [1090, 646, -60], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 10, + "entries": [ + { "name": "Betrayer", "maxCount": 1, "probability": 100 }, + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd17296a-77d8-4eef-9e9d-c042d03c928b", + "name": "Spawner (306)", + "location": [1088, 637, -60], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "JukaMage", "maxCount": 2, "probability": 100 }, + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "efa8acca-e72d-4672-882b-c27f2506ed03", + "name": "Spawner (306)", + "location": [1060, 652, -40], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [{ "name": "Betrayer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fab56f4a-1933-48ac-adb6-d1771228cb00", + "name": "Spawner (306)", + "location": [1083, 651, -40], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Betrayer", "maxCount": 1, "probability": 100 }, + { "name": "GolemController", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "92d313e6-e98e-4803-b61b-faea999cd89f", + "name": "Spawner (306)", + "location": [1083, 651, -40], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Juggernaut", "maxCount": 3, "probability": 100 }, + { "name": "Golem", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ccbd8610-d346-4117-a518-9df86604bde4", + "name": "Spawner (306)", + "location": [1084, 652, -20], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "GolemController", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "99def11e-ee3f-440e-bafb-2a0b3c89d0c9", + "name": "Spawner (306)", + "location": [1772, 67, -28], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "MeerWarrior", "maxCount": 3, "probability": 100 }, + { "name": "MeerMage", "maxCount": 2, "probability": 100 }, + { "name": "MeerCaptain", "maxCount": 2, "probability": 100 }, + { "name": "MeerEternal", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b7a6b349-9426-4332-8fca-238370a60f3b", + "name": "Spawner (306)", + "location": [1803, 66, -28], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "MeerWarrior", "maxCount": 4, "probability": 100 }, + { "name": "MeerMage", "maxCount": 2, "probability": 100 }, + { "name": "MeerCaptain", "maxCount": 1, "probability": 100 }, + { "name": "MeerEternal", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0a1a3db-b946-47b0-9be9-26093c01cbef", + "name": "Spawner (306)", + "location": [855, 464, -2], + "map": "Ilshenar", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 6, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 }, + { "name": "Zombie", "maxCount": 3, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d5cf478e-ed02-4bfb-943c-7b22d9d28b1b", + "name": "Spawner (306)", + "location": [1041, 272, 56], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "413d50ce-9d24-4ed3-ae07-77b1fe16f29a", + "name": "Spawner (306)", + "location": [1041, 272, 56], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Wraith", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Ghoul", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6e75609b-a0f2-4f77-8ea2-63bdbb1b24a8", + "name": "Spawner (306)", + "location": [1000, 272, 56], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "DreadSpider", "maxCount": 1, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 1, "probability": 100 }, + { "name": "ElderGazer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e4a4532-780a-4b55-ac97-e1dc5f3f816a", + "name": "Spawner (306)", + "location": [1000, 272, 56], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Wraith", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Ghoul", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "16fb0dfe-0333-467f-a29e-395067847379", + "name": "Spawner (306)", + "location": [748, 492, -64], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "649e26f7-54c4-42ce-a8f4-7a755cc361f3", + "name": "Spawner (306)", + "location": [783, 448, -47], + "map": "Ilshenar", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 2, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0ab9d3c1-154c-4ceb-9fcc-cb2142950550", + "name": "Spawner (306)", + "location": [783, 448, -47], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abd36839-1bf9-4879-b41e-6857521a0a28", + "name": "Spawner (306)", + "location": [742, 440, -59], + "map": "Ilshenar", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 2, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "089d463b-27b6-4f55-84f7-e72bb08fcb58", + "name": "Spawner (306)", + "location": [742, 440, -59], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a3d283e-539d-4a54-8f31-ccfba1a1712e", + "name": "Spawner (306)", + "location": [756, 208, -40], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Cyclops", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3845912b-63e1-42e8-8574-63bf5c8f3401", + "name": "Spawner (306)", + "location": [739, 264, -54], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [ + { "name": "Cyclops", "maxCount": 1, "probability": 100 }, + { "name": "Titan", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a38f9e6-156d-4af4-a732-eceae98cb9c1", + "name": "Spawner (306)", + "location": [778, 264, -38], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [ + { "name": "Cyclops", "maxCount": 1, "probability": 100 }, + { "name": "Titan", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6633838e-e038-4494-a3b9-2b7cb570c675", + "name": "Spawner (306)", + "location": [511, 545, -60], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "Brigand", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09d2520c-8083-4cd9-ac10-7e8c67cbb01b", + "name": "Spawner (306)", + "location": [638, 555, -75], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Wyvern", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ac49b12d-1a92-429d-9434-b88fe0ae6bcb", + "name": "Spawner (306)", + "location": [565, 343, -77], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Wyvern", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0c2ea734-6dea-4648-8c9d-37a5e91eea2b", + "name": "Spawner (306)", + "location": [489, 488, -62], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3410cc2-aabd-4257-ade9-6866bb429349", + "name": "Spawner (306)", + "location": [385, 421, -62], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Dragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44dd1dfd-d45f-41a4-a7bb-bd44c257a4f3", + "name": "Spawner (306)", + "location": [447, 425, -60], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Dragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c6da5392-5d37-43f5-8c50-0331d0bc4190", + "name": "Spawner (306)", + "location": [445, 366, -66], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Dragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2877d30d-dff4-449d-927f-2c30146d5060", + "name": "Spawner (306)", + "location": [445, 318, -64], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Dragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0b49d91f-2e00-4a51-bfe4-887dc0648c2f", + "name": "Spawner (306)", + "location": [370, 264, -52], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2ffa9ab-3ee2-4cea-8a60-b1c14c51a703", + "name": "Spawner (306)", + "location": [409, 264, -44], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6260f64-4918-4f57-a202-75c21906ebb9", + "name": "Spawner (306)", + "location": [449, 264, -57], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d561eeb-d2ca-49c5-9abd-02b48dd5d7b4", + "name": "Spawner (306)", + "location": [964, 638, -80], + "map": "Ilshenar", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 5, "probability": 100 }, + { "name": "Scorpion", "maxCount": 5, "probability": 100 }, + { "name": "SandVortex", "maxCount": 5, "probability": 100 }, + { "name": "Ridgeback", "maxCount": 1, "probability": 100 }, + { "name": "Efreet", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76827ffe-c3f1-4bcf-84fc-b3e2bc796c2e", + "name": "Spawner (306)", + "location": [981, 470, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3846e700-72a4-4d94-84e5-06b5d9b044c5", + "name": "Spawner (306)", + "location": [978, 471, -80], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "GolemController", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91d2b43c-a0fe-4613-8cc3-cf9d67f3e5ca", + "name": "Spawner (306)", + "location": [973, 460, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "LavaLizard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "13bc99c3-a51d-4ca0-b6bc-4c5698870ad0", + "name": "Spawner (306)", + "location": [964, 456, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e671ad18-2aa3-4188-8cbb-71fc3edcf9fb", + "name": "Spawner (306)", + "location": [963, 456, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "ExodusOverseer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "42dd61c7-6bec-4781-b24e-6db7864b72a4", + "name": "Spawner (306)", + "location": [941, 471, -38], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Imp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "757b03a1-ee65-4fa4-8465-cffc7754f9af", + "name": "Spawner (306)", + "location": [946, 430, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Gargoyle", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff667187-7d72-4573-aa08-064bbe619989", + "name": "Spawner (306)", + "location": [878, 527, -77], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da3f6bf3-177d-4280-bc57-e0f5b0adb651", + "name": "Spawner (306)", + "location": [938, 453, -80], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bb89d203-e35e-4b3c-87a0-83ac03e2822f", + "name": "Spawner (306)", + "location": [960, 524, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2679185-4de7-41dd-a304-27da19abffb2", + "name": "Spawner (306)", + "location": [911, 502, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "697f7eba-a9f6-4703-800a-44b754370384", + "name": "Spawner (306)", + "location": [930, 519, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2927de7-9e4a-4874-bcb4-857d20ce731b", + "name": "Spawner (306)", + "location": [808, 781, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0094215e-9411-4fa1-a091-dbfd46540289", + "name": "Spawner (306)", + "location": [853, 762, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 17, + "walkingRange": 17, + "entries": [{ "name": "Quagmire", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7513fe51-3c38-4bdd-9462-71b8442352ae", + "name": "Spawner (306)", + "location": [821, 758, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 17, + "walkingRange": 17, + "entries": [ + { "name": "ChaosDaemon", "maxCount": 1, "probability": 100 }, + { "name": "Moloch", "maxCount": 1, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e4773cbb-3189-4e17-9f1d-5f0828466171", + "name": "Spawner (306)", + "location": [821, 802, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 17, + "walkingRange": 17, + "entries": [ + { "name": "Quagmire", "maxCount": 1, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 1, "probability": 100 }, + { "name": "ArcaneDaemon", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9addf88a-7b37-49e1-aadc-5779d6eb586d", + "name": "Spawner (306)", + "location": [858, 799, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 17, + "walkingRange": 17, + "entries": [ + { "name": "ChaosDaemon", "maxCount": 2, "probability": 100 }, + { "name": "Moloch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05ea0169-d712-439a-bbbc-decb66204c70", + "name": "Spawner (306)", + "location": [879, 781, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "ChaosDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f1220ee4-7be3-40f0-b7eb-11e681dea5ae", + "name": "Spawner (306)", + "location": [907, 780, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5d4c8c7a-0513-4f06-b84e-85896cd5c5c9", + "name": "Spawner (306)", + "location": [987, 791, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "63b04be0-c512-4985-b0b2-30ba0b282d84", + "name": "Spawner (306)", + "location": [275, 453, -53], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "AirElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e1700d3c-9672-4e00-951d-feb0e20c80db", + "name": "Spawner (306)", + "location": [652, 555, -72], + "map": "Ilshenar", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [ + { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, + { "name": "Gorilla", "maxCount": 3, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca1241f9-a5d2-432f-a4f5-23505d765970", + "name": "Spawner (306)", + "location": [685, 340, -64], + "map": "Ilshenar", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 125, + "walkingRange": 170, + "entries": [ + { "name": "TropicalBird", "maxCount": 15, "probability": 100 }, + { "name": "Boar", "maxCount": 9, "probability": 100 }, + { "name": "Snake", "maxCount": 9, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 9, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 9, "probability": 100 }, + { "name": "Gorilla", "maxCount": 9, "probability": 100 }, + { "name": "Panther", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "827416b4-3e01-4d6a-9901-2b0cdc57ef3b", + "name": "Spawner (306)", + "location": [460, 469, -49], + "map": "Ilshenar", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [ + { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, + { "name": "Gorilla", "maxCount": 3, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5056885c-d7a0-4cff-9e18-b2edb0f507fe", + "name": "Spawner (306)", + "location": [377, 340, -14], + "map": "Ilshenar", + "count": 35, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 150, + "entries": [ + { "name": "TropicalBird", "maxCount": 9, "probability": 100 }, + { "name": "Boar", "maxCount": 5, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 5, "probability": 100 }, + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "Panther", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e13fea92-e3ac-4e7d-bf3a-05d99b66b057", + "name": "Spawner (306)", + "location": [1083, 968, -38], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "WanderingHealer", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f683a2f2-fab0-4a35-aeed-1d50b9dcf273", + "name": "Spawner (306)", + "location": [1059, 1153, -24], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "RatmanMage", "maxCount": 2, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 2, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae1cec6c-8c74-4b4b-849f-f482df430969", + "name": "Spawner (306)", + "location": [1129, 1236, -4], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Brigand", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9a9045a4-e6a0-4c29-85af-6f0f497b6c21", + "name": "Spawner (306)", + "location": [1186, 1223, -20], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "BankerCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "135c1626-1ac5-4fe6-970c-3bf9d19f588c", + "name": "Spawner (306)", + "location": [1519, 1019, -6], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "HealerCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb21b810-7b2c-4fd3-9a87-4dfe0671bb1d", + "name": "Spawner (306)", + "location": [1592, 1191, -20], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Pixie", "maxCount": 5, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "14c41ba9-6903-43dc-bfb0-30b6c10df12f", + "name": "Spawner (306)", + "location": [1592, 1191, -20], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Unicorn", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c565a927-decf-4326-8213-bcd7efb5fc21", + "name": "Spawner (306)", + "location": [1480, 1207, -18], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Pixie", "maxCount": 5, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c2b6978-ce19-490d-8d71-7ed2aa368bfd", + "name": "Spawner (306)", + "location": [1480, 1207, -18], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Unicorn", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aa6761b5-dd89-4700-8c1c-7fafcefcb035", + "name": "Spawner (306)", + "location": [1318, 1246, -13], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 50, + "entries": [ + { "name": "Pixie", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "53db67c5-be50-4250-bdaa-14d7684f9ee2", + "name": "Spawner (306)", + "location": [1183, 1287, -30], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [ + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 3, "probability": 100 }, + { "name": "Ghoul", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fbf30316-ff51-4001-84f3-d63501306a7f", + "name": "Spawner (306)", + "location": [1319, 1331, -14], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Sheep", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49a3f002-3ed6-4e08-b8de-d6ffa7dae59f", + "name": "Spawner (306)", + "location": [1733, 1161, -5], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Skeleton", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "798e730a-c923-4159-abf5-0c1492331a67", + "name": "Spawner (306)", + "location": [1747, 1221, -1], + "map": "Ilshenar", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 4, "probability": 100 }, + { "name": "Imp", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cbb2778d-3936-46ea-97eb-f93214859600", + "name": "Spawner (306)", + "location": [1678, 971, 10], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Harpy", "maxCount": 3, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3a2fccf-7467-45b6-8c92-b18ec3463312", + "name": "Spawner (306)", + "location": [1674, 1074, 32], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7566467d-86a1-4e77-8d7e-cd2c876f4af2", + "name": "Spawner (306)", + "location": [1585, 990, 20], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 10, + "entries": [{ "name": "GiantRat", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eaa928d7-cdd8-47cf-97b8-ff8daa37c6f7", + "name": "Spawner (306)", + "location": [1436, 960, -22], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "Reaper", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1f641e7-bd1c-4706-bc21-1af65e71bbee", + "name": "Spawner (306)", + "location": [1249, 957, -14], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Pixie", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dd6a1f71-e45b-40c9-ac4c-75cb8a7c2ae3", + "name": "Spawner (306)", + "location": [1360, 888, -21], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 40, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d12c9543-0c01-47e6-b619-4cdddaba9f1b", + "name": "Spawner (306)", + "location": [1817, 976, -28], + "map": "Ilshenar", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 45, + "walkingRange": 45, + "entries": [ + { "name": "DreadSpider", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 13, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08173e6f-aebd-4ba2-85ae-6b9390f2d26c", + "name": "Spawner (306)", + "location": [1504, 878, 10], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "GiantSpider", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5c8e9fc9-aef3-4248-aa1d-829108d3f59b", + "name": "Spawner (306)", + "location": [664, 668, 5], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 10, + "entries": [ + { "name": "RatmanMage", "maxCount": 1, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f341a81-ed10-448a-8986-9b6fe3f7f42e", + "name": "Spawner (306)", + "location": [664, 665, -15], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 10, + "entries": [ + { "name": "RatmanArcher", "maxCount": 2, "probability": 100 }, + { "name": "Ratman", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33d32b88-b234-4f29-911b-db8f5008fe7d", + "name": "Spawner (306)", + "location": [664, 665, -35], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "RatmanMage", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b70b8b97-55cc-4666-b5c0-5088913de58d", + "name": "Spawner (306)", + "location": [644, 819, -44], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "RatmanMage", "maxCount": 3, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ddb61039-cb35-4e01-92f4-fa49f273db3e", + "name": "Spawner (306)", + "location": [961, 924, -37], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "BankerCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "286d344f-fbf6-4cea-bd98-66a6ae098622", + "name": "Spawner (306)", + "location": [567, 807, -74], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "MageCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f5b5861e-4eb1-4ea6-8013-e04ecf78d1d2", + "name": "Spawner (306)", + "location": [830, 1206, -70], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "MageCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6428c823-54f9-4675-9bb9-8c5d4b0a5db0", + "name": "Spawner (306)", + "location": [485, 891, -82], + "map": "Ilshenar", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 1, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 }, + { "name": "Slime", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9bbfa6b-b223-4fb1-828c-100ca2ceb176", + "name": "Spawner (306)", + "location": [487, 890, -82], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Jwilson", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4345b3d1-292e-4c01-af9c-e4abe339b8c5", + "name": "Spawner (306)", + "location": [330, 881, -73], + "map": "Ilshenar", + "count": 17, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 5, "probability": 100 }, + { "name": "Shade", "maxCount": 5, "probability": 100 }, + { "name": "Wraith", "maxCount": 5, "probability": 100 }, + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "38b098ef-adb7-4187-9c46-2266659838b7", + "name": "Spawner (306)", + "location": [325, 893, -72], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "251d70b0-01cd-4817-b072-1dd7dd5ba417", + "name": "Spawner (306)", + "location": [366, 881, -73], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8148df05-3fd6-4b7e-a052-bc8830e173e5", + "name": "Spawner (306)", + "location": [338, 863, -72], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "370cf0c4-a6d5-4940-b3bc-5e911aad3b67", + "name": "Spawner (306)", + "location": [353, 1140, -54], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Shade", "maxCount": 3, "probability": 100 }, + { "name": "Wraith", "maxCount": 3, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f26425c-ee86-48f4-84e8-96c85410fdf7", + "name": "Spawner (306)", + "location": [354, 1140, -52], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Shade", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4d8998df-e3b7-41c8-9e15-7147a530e05c", + "name": "Spawner (306)", + "location": [430, 1176, -68], + "map": "Ilshenar", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "SwampTentacle", "maxCount": 4, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 }, + { "name": "Alligator", "maxCount": 14, "probability": 100 }, + { "name": "Reaper", "maxCount": 4, "probability": 100 }, + { "name": "Corpser", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ce8e092-abb1-4db0-ad42-c3cb2d4ab28d", + "name": "Spawner (306)", + "location": [350, 1204, -38], + "map": "Ilshenar", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "SwampTentacle", "maxCount": 2, "probability": 100 }, + { "name": "GiantToad", "maxCount": 5, "probability": 100 }, + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "Reaper", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abe02eba-8c77-43a4-b81a-88b366c1bbbb", + "name": "Spawner (306)", + "location": [400, 1334, -37], + "map": "Ilshenar", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "SwampTentacle", "maxCount": 5, "probability": 100 }, + { "name": "GiantToad", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Alligator", "maxCount": 0, "probability": 100 }, + { "name": "Reaper", "maxCount": 0, "probability": 100 }, + { "name": "Corpser", "maxCount": 0, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfb042de-f9a6-49a0-a18d-7ff3067c9c0a", + "name": "Spawner (306)", + "location": [291, 1313, -25], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Lizardman", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11402edf-4c62-4179-a86a-1f49db2c8b4d", + "name": "Spawner (306)", + "location": [295, 1348, -26], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Lizardman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cadd8d7c-9d3a-4121-83ae-e2613840894a", + "name": "Spawner (306)", + "location": [302, 1580, -29], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Lizardman", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cae7914f-420c-4bd4-a325-604479484886", + "name": "Spawner (306)", + "location": [325, 1576, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [{ "name": "Lizardman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "462ee8dd-f60c-438e-90a7-0bd1b7b2f137", + "name": "Spawner (306)", + "location": [223, 1360, -15], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "AncientWyrm", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 2, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d26042c9-0119-45e9-a006-0a59d84f338b", + "name": "Spawner (306)", + "location": [278, 1440, 15], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Efreet", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e733d3be-2373-466b-a904-c90c1f87156c", + "name": "Spawner (306)", + "location": [314, 1436, 16], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc7cac1d-5957-4d5f-b459-c7379c118384", + "name": "Spawner (306)", + "location": [350, 1441, 17], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4fa9bb9-65f4-4e28-a0df-c30a910b37bc", + "name": "Spawner (306)", + "location": [438, 1431, 15], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 2, "probability": 100 }, + { "name": "AirElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60e90560-1527-4f63-b097-6a4ec6c38f8e", + "name": "Spawner (306)", + "location": [648, 1321, -57], + "map": "Ilshenar", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 }, + { "name": "Imp", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d977c28c-0fde-46fb-ae30-07c36d70f077", + "name": "Spawner (306)", + "location": [722, 1354, -61], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91bf1d10-57ae-4e79-a7db-66b06c5e1320", + "name": "Spawner (306)", + "location": [913, 935, -34], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "WaterElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "48fa1520-858d-40ea-b7f0-a1d47fb0ebbc", + "name": "Spawner (306)", + "location": [963, 1028, -31], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "Reaper", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "Imp", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd8c40d5-7d9f-46f2-86f9-ab58c0ec5d31", + "name": "Spawner (306)", + "location": [935, 1159, 13], + "map": "Ilshenar", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Imp", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc71a047-fd22-43e8-96bb-953581239b63", + "name": "Spawner (306)", + "location": [822, 1324, -80], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93695f7d-f83a-4797-a125-6ff28e94c368", + "name": "Spawner (306)", + "location": [902, 1308, -71], + "map": "Ilshenar", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 40, + "entries": [ + { "name": "Titan", "maxCount": 2, "probability": 100 }, + { "name": "Cyclops", "maxCount": 4, "probability": 100 }, + { "name": "Ettin", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08c9e8fa-503a-4590-ae93-498397e9f798", + "name": "Spawner (306)", + "location": [646, 1209, -93], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 50, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aabcb1f9-f76a-49db-a0f2-617e9abd2d66", + "name": "Spawner (306)", + "location": [605, 1061, -84], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 45, + "walkingRange": 55, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a037d753-db69-4c16-8c67-4c1405a4aa0f", + "name": "Spawner (306)", + "location": [564, 1142, -99], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 45, + "walkingRange": 65, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0608e0a7-41e3-416a-b283-c4c7548ff433", + "name": "Spawner (306)", + "location": [681, 1125, -73], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 65, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a0bfc1f-d6f8-47a2-9551-62bdea09f4d5", + "name": "Spawner (306)", + "location": [703, 984, -68], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 65, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "603df694-0c2b-450a-8c18-64b6dbbcbacf", + "name": "Spawner (306)", + "location": [605, 973, -82], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 65, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d513dab-cb9d-42bc-8e86-ffd715ffc3e5", + "name": "Spawner (306)", + "location": [514, 1225, -66], + "map": "Ilshenar", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 1, "probability": 100 }, + { "name": "Snake", "maxCount": 2, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 2, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 1, "probability": 100 }, + { "name": "Gorilla", "maxCount": 1, "probability": 100 }, + { "name": "Panther", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1e695260-4946-433b-91c3-0e67eb3434ef", + "name": "Spawner (306)", + "location": [733, 1238, -68], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 65, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b4c3c4c-418e-46f7-be0f-d39d51e8843a", + "name": "Spawner (306)", + "location": [397, 958, -80], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 70, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "749fd6cf-bc7c-476e-b125-e980ab8d540e", + "name": "Spawner (306)", + "location": [468, 874, -80], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 50, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01b06a3d-d447-4ada-aa1e-c0ed77f20988", + "name": "Spawner (306)", + "location": [630, 828, -55], + "map": "Ilshenar", + "count": 25, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 85, + "walkingRange": 110, + "entries": [ + { "name": "TropicalBird", "maxCount": 7, "probability": 100 }, + { "name": "Boar", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 4, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 4, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 4, "probability": 100 }, + { "name": "Gorilla", "maxCount": 4, "probability": 100 }, + { "name": "Panther", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "51c640e7-ecaf-4949-8de8-37b31daaa1b8", + "name": "Spawner (306)", + "location": [588, 674, -60], + "map": "Ilshenar", + "count": 25, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 75, + "walkingRange": 100, + "entries": [ + { "name": "TropicalBird", "maxCount": 7, "probability": 100 }, + { "name": "Boar", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 4, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 4, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 4, "probability": 100 }, + { "name": "Gorilla", "maxCount": 4, "probability": 100 }, + { "name": "Panther", "maxCount": 3, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Ratmancave.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Ratmancave.json similarity index 91% rename from Distribution/Data/Spawns/shared/ilshenar/Ratmancave.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Ratmancave.json index ee725d9c3..9514fad48 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Ratmancave.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Ratmancave.json @@ -1,41 +1,6 @@ [ { - "$type": "Spawner", - "guid": "0182a6f9-bdde-4ca6-95c8-a7c146b1a0e1", - "name": "Spawner (307)", - "location": [1224, 1494, -28], - "map": "Ilshenar", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "SkeletalKnight", "maxCount": 5, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 5, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2694470a-4c93-488c-96a3-87938993d306", - "name": "Spawner (307)", - "location": [1171, 1511, -68], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "46e1d6c5-ebe2-4575-beeb-3e35754caef7", "name": "Spawner (307)", "location": [1310, 1519, -28], @@ -53,7 +18,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a96346f4-9aca-4dd6-b1ff-e823d8741a19", "name": "Spawner (307)", "location": [1224, 1524, -28], @@ -71,7 +36,25 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "0182a6f9-bdde-4ca6-95c8-a7c146b1a0e1", + "name": "Spawner (307)", + "location": [1224, 1494, -28], + "map": "Ilshenar", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SkeletalKnight", "maxCount": 5, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 5, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "c6766cd8-89ac-4169-a2d9-8216a74369a4", "name": "Spawner (307)", "location": [1181, 1512, -68], @@ -82,8 +65,23 @@ "team": 0, "homeRange": 10, "walkingRange": 10, + "entries": [{ "name": "SkeletalDragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2694470a-4c93-488c-96a3-87938993d306", + "name": "Spawner (307)", + "location": [1171, 1511, -68], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, "entries": [ - { "name": "SkeletalDragon", "maxCount": 1, "probability": 100 } + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Rock.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Rock.json similarity index 84% rename from Distribution/Data/Spawns/shared/ilshenar/Rock.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Rock.json index 48c11c660..a2bb47cd7 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Rock.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Rock.json @@ -1,55 +1,6 @@ [ { - "$type": "Spawner", - "guid": "027c989e-9d77-4dfd-85d8-b7c83f722bde", - "name": "Spawner (308)", - "location": [2094, 56, -32], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "Executioner", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "148839f1-8a5b-44bd-b6f5-3b310de5c951", - "name": "Spawner (308)", - "location": [2155, 24, -32], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "PoisonElemental", "maxCount": 2, "probability": 100 }, - { "name": "LichLord", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "151afe47-a4c7-42d9-999b-6be7ab8ed901", - "name": "Spawner (308)", - "location": [2125, 137, -32], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "2481376c-f54f-453e-aa09-b0ae25a2da13", "name": "Spawner (308)", "location": [2188, 310, -7], @@ -67,10 +18,10 @@ ] }, { - "$type": "Spawner", - "guid": "3a6c8beb-1840-4e1a-b55e-b44158315f59", + "type": "Spawner", + "guid": "93052936-70f8-43b4-a20c-d68adab714c6", "name": "Spawner (308)", - "location": [2138, 97, -27], + "location": [2220, 75, -27], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -78,45 +29,10 @@ "team": 0, "homeRange": 5, "walkingRange": 10, - "entries": [ - { "name": "ElderGazer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "AcidElemental", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "4f5851f5-110c-4155-91a4-5d6715b52c2f", - "name": "Spawner (308)", - "location": [2183, 85, -32], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5c78812b-3143-4ab4-a3e2-950a74181fb1", - "name": "Spawner (308)", - "location": [2185, 168, -32], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "6d9c7f2b-dc74-4ca3-9396-fb2a38fa2d80", "name": "Spawner (308)", "location": [2207, 113, -27], @@ -127,12 +43,24 @@ "team": 0, "homeRange": 5, "walkingRange": 10, - "entries": [ - { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "StoneGargoyle", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "5c78812b-3143-4ab4-a3e2-950a74181fb1", + "name": "Spawner (308)", + "location": [2185, 168, -32], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "8d713f41-daf3-40c4-9d90-bbf4befb1b08", "name": "Spawner (308)", "location": [2229, 143, -32], @@ -149,7 +77,100 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "bdba50d6-07c1-40ad-ba33-ac67631712cc", + "name": "Spawner (308)", + "location": [2154, 163, -27], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc19f651-1312-4542-80df-b9f2362974da", + "name": "Spawner (308)", + "location": [2125, 165, -32], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [ + { "name": "EvilMage", "maxCount": 2, "probability": 100 }, + { "name": "Executioner", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "151afe47-a4c7-42d9-999b-6be7ab8ed901", + "name": "Spawner (308)", + "location": [2125, 137, -32], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a6c8beb-1840-4e1a-b55e-b44158315f59", + "name": "Spawner (308)", + "location": [2138, 97, -27], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "ElderGazer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4f5851f5-110c-4155-91a4-5d6715b52c2f", + "name": "Spawner (308)", + "location": [2183, 85, -32], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "148839f1-8a5b-44bd-b6f5-3b310de5c951", + "name": "Spawner (308)", + "location": [2155, 24, -32], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [ + { "name": "PoisonElemental", "maxCount": 2, "probability": 100 }, + { "name": "LichLord", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "92ee0621-2fc1-434c-a2d8-11b9359e01bc", "name": "Spawner (308)", "location": [2124, 96, -32], @@ -167,10 +188,10 @@ ] }, { - "$type": "Spawner", - "guid": "93052936-70f8-43b4-a20c-d68adab714c6", + "type": "Spawner", + "guid": "027c989e-9d77-4dfd-85d8-b7c83f722bde", "name": "Spawner (308)", - "location": [2220, 75, -27], + "location": [2094, 56, -32], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -178,28 +199,10 @@ "team": 0, "homeRange": 5, "walkingRange": 10, - "entries": [ - { "name": "AcidElemental", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Executioner", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "bdba50d6-07c1-40ad-ba33-ac67631712cc", - "name": "Spawner (308)", - "location": [2154, 163, -27], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f8cc122d-1672-4b14-a6bf-4dcc6178efdc", "name": "Spawner (308)", "location": [2127, 29, -32], @@ -214,22 +217,5 @@ { "name": "HellHound", "maxCount": 1, "probability": 100 }, { "name": "LichLord", "maxCount": 1, "probability": 100 } ] - }, - { - "$type": "Spawner", - "guid": "fc19f651-1312-4542-80df-b9f2362974da", - "name": "Spawner (308)", - "location": [2125, 165, -32], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "EvilMage", "maxCount": 2, "probability": 100 }, - { "name": "Executioner", "maxCount": 2, "probability": 100 } - ] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Sorcerers.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Sorcerers.json similarity index 84% rename from Distribution/Data/Spawns/shared/ilshenar/Sorcerers.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Sorcerers.json index 1533d49a4..ccb04b6d1 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Sorcerers.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Sorcerers.json @@ -1,188 +1,20 @@ [ { - "$type": "Spawner", - "guid": "091c33d5-ee75-471b-aafc-78ea45d3dd66", + "type": "Spawner", + "guid": "fc22ceb7-01e2-4389-a356-ffc788071f0f", "name": "Spawner (309)", - "location": [336, 48, -28], + "location": [427, 67, -28], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 8, - "walkingRange": 10, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10f6b4be-fd86-480d-b285-d00f0f55b59c", - "name": "Spawner (309)", - "location": [155, 21, -28], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Balron", "maxCount": 2, "probability": 100 }, - { "name": "ElderGazer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1fb85d78-33ef-42c5-8145-e0af0ec5bd33", - "name": "Spawner (309)", - "location": [336, 37, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 10, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20ea3ede-9733-4b69-a2a9-44312ad782df", - "name": "Spawner (309)", - "location": [254, 20, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "271dd12c-8c89-4962-8766-27c6e23ab039", - "name": "Spawner (309)", - "location": [427, 95, -28], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "27540523-3186-413d-99da-3fd4ef5be98f", - "name": "Spawner (309)", - "location": [281, 20, -28], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "29b9ab63-8072-4a0e-b8b2-cbdbe54a74c6", - "name": "Spawner (309)", - "location": [462, 16, -28], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 15, - "entries": [ - { "name": "HeadlessOne", "maxCount": 2, "probability": 100 }, - { "name": "Mongbat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37598c17-946c-4a0a-8e86-20d1329e4bca", - "name": "Spawner (309)", - "location": [133, 114, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, + "homeRange": 25, "walkingRange": 25, - "entries": [ - { "name": "BoneKnight", "maxCount": 1, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Zombie", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "3aad6c47-815b-4713-93f2-c57156aded30", - "name": "Spawner (309)", - "location": [305, 20, -28], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "DullCopperElemental", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "492af6a5-b034-4a8b-aa7b-923b906321e2", - "name": "Spawner (309)", - "location": [133, 114, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5bceb873-73d8-4326-8ec1-46bb48c65d60", - "name": "Spawner (309)", - "location": [214, 67, -28], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 40, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "5e285fc0-52a0-4f23-ad8f-bc7e90b2fcec", "name": "Spawner (309)", "location": [455, 70, -28], @@ -200,7 +32,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6433de3a-a4f6-4d51-9249-0dc37a3fe107", "name": "Spawner (309)", "location": [394, 69, -28], @@ -217,25 +49,7 @@ ] }, { - "$type": "Spawner", - "guid": "65f92121-fb34-4f86-9722-4ee64ec64b61", - "name": "Spawner (309)", - "location": [109, 44, -28], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 4, "probability": 100 }, - { "name": "Mummy", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "72c10d82-2c9a-489b-8bad-28dac3e794c9", "name": "Spawner (309)", "location": [447, 19, -28], @@ -254,23 +68,7 @@ ] }, { - "$type": "Spawner", - "guid": "73b4ab03-1cab-4034-a3c0-9b8ba61d3600", - "name": "Spawner (309)", - "location": [160, 59, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "73efa56f-434e-4fc0-8822-c028c9a83e6c", "name": "Spawner (309)", "location": [431, 16, -28], @@ -287,7 +85,101 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "29b9ab63-8072-4a0e-b8b2-cbdbe54a74c6", + "name": "Spawner (309)", + "location": [462, 16, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [ + { "name": "HeadlessOne", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "86f27a56-95cc-49ed-8483-38c07cd31de2", + "name": "Spawner (309)", + "location": [462, 16, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9cebea0f-1699-45af-a9b1-0d79d99d290e", + "name": "Spawner (309)", + "location": [383, 29, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "271dd12c-8c89-4962-8766-27c6e23ab039", + "name": "Spawner (309)", + "location": [427, 95, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20ea3ede-9733-4b69-a2a9-44312ad782df", + "name": "Spawner (309)", + "location": [254, 20, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "27540523-3186-413d-99da-3fd4ef5be98f", + "name": "Spawner (309)", + "location": [281, 20, -28], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "807fd985-4f3a-4d1f-a7be-942294f1d782", "name": "Spawner (309)", "location": [238, 48, -28], @@ -304,205 +196,21 @@ ] }, { - "$type": "Spawner", - "guid": "818d30b9-0568-4e69-9b00-a8567e0f63b4", + "type": "Spawner", + "guid": "5bceb873-73d8-4326-8ec1-46bb48c65d60", "name": "Spawner (309)", - "location": [143, 70, -28], + "location": [214, 67, -28], "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "846a8862-f62b-4814-856b-88a6f9cb0a27", - "name": "Spawner (309)", - "location": [350, 42, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "86f27a56-95cc-49ed-8483-38c07cd31de2", - "name": "Spawner (309)", - "location": [462, 16, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 15, - "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94e38cf9-78c0-498b-927d-f7151001f811", - "name": "Spawner (309)", - "location": [67, 70, -28], - "map": "Ilshenar", - "count": 6, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "LichLord", "maxCount": 2, "probability": 100 }, - { "name": "Mummy", "maxCount": 4, "probability": 100 } - ] + "walkingRange": 40, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "99674640-e132-4cdd-a69e-1607e28dc5f3", - "name": "Spawner (309)", - "location": [310, 92, -13], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "LichLord", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9ce0e4a2-0318-4e8d-83a6-48933b302425", - "name": "Spawner (309)", - "location": [336, 42, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 15, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9cebea0f-1699-45af-a9b1-0d79d99d290e", - "name": "Spawner (309)", - "location": [383, 29, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a1b6deba-e340-4851-bba7-4f5ad2d03353", - "name": "Spawner (309)", - "location": [146, 83, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a304cb5f-12b3-401a-96d3-104ee9f33544", - "name": "Spawner (309)", - "location": [311, 91, -13], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b6f7c48e-faa6-44a6-b59f-d0b47313a4f6", - "name": "Spawner (309)", - "location": [278, 53, -28], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "HellHound", "maxCount": 6, "probability": 100 }, - { "name": "BloodElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd576d60-5525-4ee3-b85f-8439e6dd38fd", - "name": "Spawner (309)", - "location": [160, 9, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 5, - "entries": [ - { "name": "Efreet", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bed076f5-fb2b-4342-9423-6a3228cc1978", - "name": "Spawner (309)", - "location": [262, 84, -28], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 }, - { "name": "EvilMage", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f4631bce-3338-4d0e-88c9-9faa4ba968ef", "name": "Spawner (309)", "location": [262, 84, -28], @@ -520,19 +228,281 @@ ] }, { - "$type": "Spawner", - "guid": "fc22ceb7-01e2-4389-a356-ffc788071f0f", + "type": "Spawner", + "guid": "bed076f5-fb2b-4342-9423-6a3228cc1978", "name": "Spawner (309)", - "location": [427, 67, -28], + "location": [262, 84, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "EvilMage", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b6f7c48e-faa6-44a6-b59f-d0b47313a4f6", + "name": "Spawner (309)", + "location": [278, 53, -28], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "HellHound", "maxCount": 6, "probability": 100 }, + { "name": "BloodElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3aad6c47-815b-4713-93f2-c57156aded30", + "name": "Spawner (309)", + "location": [305, 20, -28], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "DullCopperElemental", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "99674640-e132-4cdd-a69e-1607e28dc5f3", + "name": "Spawner (309)", + "location": [310, 92, -13], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 25, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "LichLord", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a304cb5f-12b3-401a-96d3-104ee9f33544", + "name": "Spawner (309)", + "location": [311, 91, -13], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, + { "name": "BloodElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1fb85d78-33ef-42c5-8145-e0af0ec5bd33", + "name": "Spawner (309)", + "location": [336, 37, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "091c33d5-ee75-471b-aafc-78ea45d3dd66", + "name": "Spawner (309)", + "location": [336, 48, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ce0e4a2-0318-4e8d-83a6-48933b302425", + "name": "Spawner (309)", + "location": [336, 42, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 15, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "846a8862-f62b-4814-856b-88a6f9cb0a27", + "name": "Spawner (309)", + "location": [350, 42, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a1b6deba-e340-4851-bba7-4f5ad2d03353", + "name": "Spawner (309)", + "location": [146, 83, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "818d30b9-0568-4e69-9b00-a8567e0f63b4", + "name": "Spawner (309)", + "location": [143, 70, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "73b4ab03-1cab-4034-a3c0-9b8ba61d3600", + "name": "Spawner (309)", + "location": [160, 59, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "10f6b4be-fd86-480d-b285-d00f0f55b59c", + "name": "Spawner (309)", + "location": [155, 21, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Balron", "maxCount": 2, "probability": 100 }, + { "name": "ElderGazer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd576d60-5525-4ee3-b85f-8439e6dd38fd", + "name": "Spawner (309)", + "location": [160, 9, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 5, + "entries": [{ "name": "Efreet", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "65f92121-fb34-4f86-9722-4ee64ec64b61", + "name": "Spawner (309)", + "location": [109, 44, -28], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 4, "probability": 100 }, + { "name": "Mummy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94e38cf9-78c0-498b-927d-f7151001f811", + "name": "Spawner (309)", + "location": [67, 70, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "LichLord", "maxCount": 2, "probability": 100 }, + { "name": "Mummy", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37598c17-946c-4a0a-8e86-20d1329e4bca", + "name": "Spawner (309)", + "location": [133, 114, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, "walkingRange": 25, "entries": [ - { "name": "Zombie", "maxCount": 2, "probability": 100 } + { "name": "BoneKnight", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "492af6a5-b034-4a8b-aa7b-923b906321e2", + "name": "Spawner (309)", + "location": [133, 114, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Spectre.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Spectre.json similarity index 96% rename from Distribution/Data/Spawns/shared/ilshenar/Spectre.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Spectre.json index 234036f77..98437e578 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Spectre.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Spectre.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "70e1e800-e6dc-4f5c-a610-269930e6c327", "name": "Spawner (310)", "location": [1981, 1059, -28], diff --git a/Distribution/Data/Spawns/shared/ilshenar/Towns.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Towns.json similarity index 83% rename from Distribution/Data/Spawns/shared/ilshenar/Towns.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Towns.json index ac7d0eb99..1da62e31b 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Towns.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Towns.json @@ -1,91 +1,6 @@ [ { - "$type": "Spawner", - "guid": "04236803-3dc7-426b-8a8d-d927b1bd0086", - "name": "Spawner (311)", - "location": [1279, 781, -80], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Ridgeback", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "063240fa-7f32-4bad-b79e-bf9c8ca7b9b9", - "name": "Spawner (311)", - "location": [1108, 492, -80], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "DireWolf", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "149dfca3-652c-4777-b005-a0cc95f30d9d", - "name": "Spawner (311)", - "location": [1517, 541, 11], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "165f16c6-b66f-474a-aed6-0d6c2ee57123", - "name": "Spawner (311)", - "location": [1715, 228, 78], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "StoneHarpy", "maxCount": 3, "probability": 100 }, - { "name": "StoneGargoyle", "maxCount": 2, "probability": 100 }, - { "name": "Balron", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "17243bc5-68af-437f-bb95-963913a05306", - "name": "Spawner (311)", - "location": [1191, 696, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "SavageRider", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "1b101011-42fa-41b1-b15c-f15fdcaaf6ec", "name": "Spawner (311)", "location": [1517, 558, 5], @@ -96,229 +11,156 @@ "team": 0, "homeRange": 20, "walkingRange": 35, - "entries": [ - { "name": "Skeleton", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Skeleton", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "1e1dd613-72d1-4715-b899-1086679533ab", + "type": "Spawner", + "guid": "824d3710-eb67-4f27-8917-6debd7c8e2ca", "name": "Spawner (311)", - "location": [1665, 244, 93], + "location": [1517, 552, 10], "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1e2bfa6b-abe9-4dcd-b996-bc4dcdd17f9f", - "name": "Spawner (311)", - "location": [1234, 760, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "SavageShaman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f0236b1-b26e-4572-b03a-d53d94ab1a6a", - "name": "Spawner (311)", - "location": [1110, 542, -77], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "DireWolf", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "26a0cc2e-3dfc-49d7-9274-d993fc0371db", - "name": "Spawner (311)", - "location": [1292, 755, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Savage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "34ad74fe-d7eb-4427-8c7e-e7658bd63156", - "name": "Spawner (311)", - "location": [1363, 1046, -13], - "map": "Ilshenar", - "count": 4, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 15, "walkingRange": 30, "entries": [ - { "name": "EtherealWarrior", "maxCount": 4, "probability": 100 } + { "name": "Spectre", "maxCount": 6, "probability": 100 }, + { "name": "Shade", "maxCount": 6, "probability": 100 }, + { "name": "Wraith", "maxCount": 6, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "38614de6-1ed9-41a0-b30b-59ae8191316f", + "type": "Spawner", + "guid": "9d3ed0a0-e458-4fa9-bc94-008304706893", "name": "Spawner (311)", - "location": [1213, 719, -80], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Savage", "maxCount": 3, "probability": 100 }, - { "name": "SavageRider", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3bfd2dc7-d6d7-4604-858f-246ed9b25d35", - "name": "Spawner (311)", - "location": [1203, 762, -80], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Savage", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4befab4e-1b1a-4290-ae76-6c176b48a18a", - "name": "Spawner (311)", - "location": [1046, 517, -80], + "location": [1516, 557, 4], "map": "Ilshenar", "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 15, + "homeRange": 20, + "walkingRange": 35, "entries": [ - { "name": "SavageRider", "maxCount": 3, "probability": 100 }, - { "name": "Savage", "maxCount": 4, "probability": 100 } + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "56b2d766-8057-4400-913f-6c10a5155f98", + "type": "Spawner", + "guid": "c4c688bb-cf62-4b3d-84bd-2633333c0242", "name": "Spawner (311)", - "location": [1251, 703, -80], + "location": [1517, 552, 10], "map": "Ilshenar", - "count": 3, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "Savage", "maxCount": 3, "probability": 100 } + { "name": "SkeletalKnight", "maxCount": 3, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 3, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "59ae00b4-1b69-42de-8faa-670350014ad7", + "type": "Spawner", + "guid": "149dfca3-652c-4777-b005-a0cc95f30d9d", "name": "Spawner (311)", - "location": [1116, 527, -80], + "location": [1517, 541, 11], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Savage", "maxCount": 1, "probability": 100 }, - { "name": "SavageRidgeback", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a5f79c6-3813-428e-a5fe-00893d247556", - "name": "Spawner (311)", - "location": [1161, 668, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Savage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5f1d23bd-e6c8-422e-b69e-a88d61fa61c0", - "name": "Spawner (311)", - "location": [1243, 732, -80], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Savage", "maxCount": 1, "probability": 100 }, - { "name": "SavageShaman", "maxCount": 3, "probability": 100 }, - { "name": "SavageRider", "maxCount": 1, "probability": 100 } - ] + "walkingRange": 20, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "64e33b76-ba88-4906-a91f-4b56dea52a81", + "type": "Spawner", + "guid": "c671596f-69b6-45df-a106-b8fce71aa156", "name": "Spawner (311)", - "location": [1181, 691, -80], + "location": [1517, 541, 61], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 4, - "walkingRange": 4, + "walkingRange": 10, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb1de953-cfdd-46d6-aab9-68cbdfe60780", + "name": "Spawner (311)", + "location": [1203, 1138, -25], + "map": "Ilshenar", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 55, "entries": [ - { "name": "Savage", "maxCount": 1, "probability": 100 } + { "name": "MeerWarrior", "maxCount": 16, "probability": 100 }, + { "name": "MeerCaptain", "maxCount": 16, "probability": 100 }, + { "name": "MeerMage", "maxCount": 16, "probability": 100 }, + { "name": "MeerEternal", "maxCount": 16, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "ef56cf38-1547-41ad-adc4-ba72aa156f87", + "name": "Spawner (311)", + "location": [1127, 1112, -34], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 35, + "entries": [ + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7f13f3fa-2330-4dd5-9985-164d8e240659", + "name": "Spawner (311)", + "location": [1180, 1188, -25], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Snake", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93476fd0-4859-4358-90ab-4371f8c1ac40", + "name": "Spawner (311)", + "location": [1181, 1189, -25], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "GiantSerpent", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "6ac6b235-2d7c-4058-bc8d-d7f73cd17d5a", "name": "Spawner (311)", "location": [817, 1065, -30], @@ -338,222 +180,7 @@ ] }, { - "$type": "Spawner", - "guid": "6c8f4381-39be-4530-bfc3-1478035c1aa5", - "name": "Spawner (311)", - "location": [1108, 492, -80], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72ce5891-f2dd-4a3c-8019-199928c49dcc", - "name": "Spawner (311)", - "location": [1231, 694, -80], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Savage", "maxCount": 2, "probability": 100 }, - { "name": "SavageShaman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7f13f3fa-2330-4dd5-9985-164d8e240659", - "name": "Spawner (311)", - "location": [1180, 1188, -25], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Snake", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "824d3710-eb67-4f27-8917-6debd7c8e2ca", - "name": "Spawner (311)", - "location": [1517, 552, 10], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "Spectre", "maxCount": 6, "probability": 100 }, - { "name": "Shade", "maxCount": 6, "probability": 100 }, - { "name": "Wraith", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "86ff8b56-5d0c-4218-80d4-749414e3f568", - "name": "Spawner (311)", - "location": [1191, 696, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 7, - "entries": [ - { "name": "Savage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "88f2279d-e351-4611-8255-206a456394d6", - "name": "Spawner (311)", - "location": [1110, 542, -77], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "917aa704-acc1-49ce-81c6-401f0604b260", - "name": "Spawner (311)", - "location": [1274, 728, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Savage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93476fd0-4859-4358-90ab-4371f8c1ac40", - "name": "Spawner (311)", - "location": [1181, 1189, -25], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b71629a-ede8-4fba-bd86-63a895c4c422", - "name": "Spawner (311)", - "location": [1214, 744, -80], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Savage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9d3ed0a0-e458-4fa9-bc94-008304706893", - "name": "Spawner (311)", - "location": [1516, 557, 4], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 35, - "entries": [ - { "name": "Ghoul", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9fce7b2d-3671-4d8e-bf22-19c10ec5709f", - "name": "Spawner (311)", - "location": [1280, 770, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "Savage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c4c688bb-cf62-4b3d-84bd-2633333c0242", - "name": "Spawner (311)", - "location": [1517, 552, 10], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "SkeletalKnight", "maxCount": 3, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 3, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c671596f-69b6-45df-a106-b8fce71aa156", - "name": "Spawner (311)", - "location": [1517, 541, 61], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "LichLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e0710f4e-9d44-4e1e-be0d-3f560d5f52c6", "name": "Spawner (311)", "location": [1643, 310, 48], @@ -574,56 +201,7 @@ ] }, { - "$type": "Spawner", - "guid": "eb0f7637-62d3-483f-835e-248e48ffdc44", - "name": "Spawner (311)", - "location": [1187, 685, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Savage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ed8774de-7f1f-4f40-bb03-0ff9e7d1ff31", - "name": "Spawner (311)", - "location": [1262, 758, -80], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Savage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ef56cf38-1547-41ad-adc4-ba72aa156f87", - "name": "Spawner (311)", - "location": [1127, 1112, -34], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 35, - "entries": [ - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f216ee7b-bb49-415a-aace-924f1778e3cc", "name": "Spawner (311)", "location": [1643, 310, 48], @@ -640,7 +218,73 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "1e1dd613-72d1-4715-b899-1086679533ab", + "name": "Spawner (311)", + "location": [1665, 244, 93], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "165f16c6-b66f-474a-aed6-0d6c2ee57123", + "name": "Spawner (311)", + "location": [1715, 228, 78], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Balron", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "34ad74fe-d7eb-4427-8c7e-e7658bd63156", + "name": "Spawner (311)", + "location": [1363, 1046, -13], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "EtherealWarrior", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4befab4e-1b1a-4290-ae76-6c176b48a18a", + "name": "Spawner (311)", + "location": [1046, 517, -80], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "SavageRider", "maxCount": 3, "probability": 100 }, + { "name": "Savage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fb0b433c-9d26-4a0f-b9a0-4ddb16ea1904", "name": "Spawner (311)", "location": [1046, 517, -80], @@ -651,27 +295,337 @@ "team": 0, "homeRange": 7, "walkingRange": 10, - "entries": [ - { "name": "SavageShaman", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "SavageShaman", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "fb1de953-cfdd-46d6-aab9-68cbdfe60780", + "type": "Spawner", + "guid": "59ae00b4-1b69-42de-8faa-670350014ad7", "name": "Spawner (311)", - "location": [1203, 1138, -25], + "location": [1116, 527, -80], "map": "Ilshenar", - "count": 16, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 40, - "walkingRange": 55, + "homeRange": 15, + "walkingRange": 15, "entries": [ - { "name": "MeerWarrior", "maxCount": 16, "probability": 100 }, - { "name": "MeerCaptain", "maxCount": 16, "probability": 100 }, - { "name": "MeerMage", "maxCount": 16, "probability": 100 }, - { "name": "MeerEternal", "maxCount": 16, "probability": 100 } + { "name": "Savage", "maxCount": 1, "probability": 100 }, + { "name": "SavageRidgeback", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "88f2279d-e351-4611-8255-206a456394d6", + "name": "Spawner (311)", + "location": [1110, 542, -77], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6c8f4381-39be-4530-bfc3-1478035c1aa5", + "name": "Spawner (311)", + "location": [1108, 492, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f0236b1-b26e-4572-b03a-d53d94ab1a6a", + "name": "Spawner (311)", + "location": [1110, 542, -77], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "063240fa-7f32-4bad-b79e-bf9c8ca7b9b9", + "name": "Spawner (311)", + "location": [1108, 492, -80], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a5f79c6-3813-428e-a5fe-00893d247556", + "name": "Spawner (311)", + "location": [1161, 668, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "64e33b76-ba88-4906-a91f-4b56dea52a81", + "name": "Spawner (311)", + "location": [1181, 691, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eb0f7637-62d3-483f-835e-248e48ffdc44", + "name": "Spawner (311)", + "location": [1187, 685, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "86ff8b56-5d0c-4218-80d4-749414e3f568", + "name": "Spawner (311)", + "location": [1191, 696, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 7, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "17243bc5-68af-437f-bb95-963913a05306", + "name": "Spawner (311)", + "location": [1191, 696, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "SavageRider", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3bfd2dc7-d6d7-4604-858f-246ed9b25d35", + "name": "Spawner (311)", + "location": [1203, 762, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Savage", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1e2bfa6b-abe9-4dcd-b996-bc4dcdd17f9f", + "name": "Spawner (311)", + "location": [1234, 760, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "SavageShaman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "38614de6-1ed9-41a0-b30b-59ae8191316f", + "name": "Spawner (311)", + "location": [1213, 719, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Savage", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b71629a-ede8-4fba-bd86-63a895c4c422", + "name": "Spawner (311)", + "location": [1214, 744, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Savage", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72ce5891-f2dd-4a3c-8019-199928c49dcc", + "name": "Spawner (311)", + "location": [1231, 694, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Savage", "maxCount": 2, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56b2d766-8057-4400-913f-6c10a5155f98", + "name": "Spawner (311)", + "location": [1251, 703, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Savage", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "917aa704-acc1-49ce-81c6-401f0604b260", + "name": "Spawner (311)", + "location": [1274, 728, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "26a0cc2e-3dfc-49d7-9274-d993fc0371db", + "name": "Spawner (311)", + "location": [1292, 755, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9fce7b2d-3671-4d8e-bf22-19c10ec5709f", + "name": "Spawner (311)", + "location": [1280, 770, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04236803-3dc7-426b-8a8d-d927b1bd0086", + "name": "Spawner (311)", + "location": [1279, 781, -80], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Ridgeback", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5f1d23bd-e6c8-422e-b69e-a88d61fa61c0", + "name": "Spawner (311)", + "location": [1243, 732, -80], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Savage", "maxCount": 1, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ed8774de-7f1f-4f40-bb03-0ff9e7d1ff31", + "name": "Spawner (311)", + "location": [1262, 758, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Savage", "maxCount": 4, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/TwistedWeald.json b/Distribution/Data/Spawns/post-uoml/ilshenar/TwistedWeald.json similarity index 85% rename from Distribution/Data/Spawns/shared/ilshenar/TwistedWeald.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/TwistedWeald.json index e7844b8a4..0181aff24 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/TwistedWeald.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/TwistedWeald.json @@ -1,23 +1,6 @@ [ { - "$type": "Spawner", - "guid": "418a0be2-0604-4ff8-b39d-1fb570fa7b7e", - "name": "Spawner (314)", - "location": [2177, 1210, -60], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "LadyLissith", "maxCount": 2, "probability": 100 }, - { "name": "LadySabrix", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "46dc7fc7-d062-4be9-8134-f960352fbca3", "name": "Spawner (314)", "location": [2171, 1181, -44], @@ -37,23 +20,42 @@ ] }, { - "$type": "Spawner", - "guid": "5a47595b-b894-4d45-b35d-5714de0c8c51", + "type": "Spawner", + "guid": "418a0be2-0604-4ff8-b39d-1fb570fa7b7e", "name": "Spawner (314)", - "location": [2206, 1220, -8], + "location": [2177, 1210, -60], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "LadyLissith", "maxCount": 2, "probability": 100 }, + { "name": "LadySabrix", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd6dab60-126f-4877-b9dd-f786cbfad49b", + "name": "Spawner (314)", + "location": [2138, 1212, -60], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, "homeRange": 20, "walkingRange": 20, "entries": [ - { "name": "CuSidhe", "maxCount": 2, "probability": 100 } + { "name": "Quagmire", "maxCount": 8, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 8, "probability": 100 }, + { "name": "Irk", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5cb54b29-10c4-448f-a036-5ba23dcb350a", "name": "Spawner (314)", "location": [2224, 1218, 13], @@ -64,28 +66,90 @@ "team": 0, "homeRange": 8, "walkingRange": 8, - "entries": [ - { "name": "Swoop", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Swoop", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "68b1853c-f9bd-4bd1-852f-dcc9977a3049", + "type": "Spawner", + "guid": "aa8d4c55-b124-40ae-a0db-31b873c7a549", "name": "Spawner (314)", - "location": [2167, 1266, -60], + "location": [2134, 1174, -46], "map": "Ilshenar", - "count": 3, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 5, "walkingRange": 5, "entries": [ - { "name": "Malefic", "maxCount": 3, "probability": 100 } + { "name": "Gnaw", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "5a47595b-b894-4d45-b35d-5714de0c8c51", + "name": "Spawner (314)", + "location": [2206, 1220, -8], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "CuSidhe", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fe2db064-ee88-4f04-8c49-b3d7a56fd9a2", + "name": "Spawner (314)", + "location": [2159, 1223, -60], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "Spite", "maxCount": 5, "probability": 100 }, + { "name": "Guile", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eea8e238-78e9-4d84-a91b-95360f6f2fd6", + "name": "Spawner (314)", + "location": [2170, 1247, -60], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Malefic", "maxCount": 4, "probability": 100 }, + { "name": "Virulent", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "852fa8b9-931f-493c-9ee0-200a3f229110", + "name": "Spawner (314)", + "location": [2163, 1189, -48], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Changeling", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "6eee069d-8662-415f-bdcc-78975f1a54ae", "name": "Spawner (314)", "location": [2207, 1180, -36], @@ -105,7 +169,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "76ef21c0-5e48-410e-84f5-57023fa7efb4", "name": "Spawner (314)", "location": [2137, 1216, -60], @@ -116,61 +180,10 @@ "team": 0, "homeRange": 5, "walkingRange": 5, - "entries": [ - { "name": "Silk", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Silk", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "7e991368-4fad-4e2b-af3b-88d360b88627", - "name": "Spawner (314)", - "location": [2157, 1206, -60], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Virulent", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "852fa8b9-931f-493c-9ee0-200a3f229110", - "name": "Spawner (314)", - "location": [2163, 1189, -48], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Changeling", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa8d4c55-b124-40ae-a0db-31b873c7a549", - "name": "Spawner (314)", - "location": [2134, 1174, -46], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Gnaw", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "b5b1118b-161e-43fb-80dd-f890dec2be53", "name": "Spawner (314)", "location": [2177, 1229, -60], @@ -181,61 +194,34 @@ "team": 0, "homeRange": 5, "walkingRange": 5, - "entries": [ - { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "cd6dab60-126f-4877-b9dd-f786cbfad49b", + "type": "Spawner", + "guid": "7e991368-4fad-4e2b-af3b-88d360b88627", "name": "Spawner (314)", - "location": [2138, 1212, -60], + "location": [2157, 1206, -60], "map": "Ilshenar", - "count": 8, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Quagmire", "maxCount": 8, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 8, "probability": 100 }, - { "name": "Irk", "maxCount": 8, "probability": 100 } - ] + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Virulent", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "eea8e238-78e9-4d84-a91b-95360f6f2fd6", + "type": "Spawner", + "guid": "68b1853c-f9bd-4bd1-852f-dcc9977a3049", "name": "Spawner (314)", - "location": [2170, 1247, -60], + "location": [2167, 1266, -60], "map": "Ilshenar", - "count": 4, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Malefic", "maxCount": 4, "probability": 100 }, - { "name": "Virulent", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fe2db064-ee88-4f04-8c49-b3d7a56fd9a2", - "name": "Spawner (314)", - "location": [2159, 1223, -60], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "Spite", "maxCount": 5, "probability": 100 }, - { "name": "Guile", "maxCount": 5, "probability": 100 } - ] + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Malefic", "maxCount": 3, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Vendors.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Vendors.json similarity index 85% rename from Distribution/Data/Spawns/shared/ilshenar/Vendors.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Vendors.json index e54f7c4dc..510ddae32 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Vendors.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Vendors.json @@ -1,26 +1,9 @@ [ { - "$type": "Spawner", - "guid": "0457d1c2-1736-4b97-96be-0625a8a08c91", + "type": "Spawner", + "guid": "826d7ee8-c78b-47ea-9ead-b30014afbe4d", "name": "Spawner (312)", - "location": [1497, 614, -14], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "07dcfcfd-1f11-4fe7-8cc5-d1981fb1e74f", - "name": "Spawner (312)", - "location": [868, 657, -40], + "location": [791, 618, 0], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -28,396 +11,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "StoneCrafter", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "12d3edd5-4ebb-41a7-82ff-2d2ab3c4adef", - "name": "Spawner (312)", - "location": [1318, 1328, -14], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "14094381-ef68-4f89-b700-0d824d8968b2", - "name": "Spawner (312)", - "location": [1261, 582, -18], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20c754a1-ce9b-4068-89ae-b50d95a64c82", - "name": "Spawner (312)", - "location": [854, 680, -40], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2b7c425b-7a52-414a-a76a-cb0734c3165c", - "name": "Spawner (312)", - "location": [1388, 428, -23], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "GypsyAnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2d27bf3f-df46-48bb-a611-a0f17e8d1051", - "name": "Spawner (312)", - "location": [1226, 554, -12], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "GypsyBanker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "305ecea0-47a1-4561-bf25-5d879a152003", - "name": "Spawner (312)", - "location": [859, 698, -40], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32050353-d0d9-4b06-afee-a65fae031a98", - "name": "Spawner (312)", - "location": [896, 610, -40], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33de0f8c-f6ba-4e9b-a09b-dc36c96db2ea", - "name": "Spawner (312)", - "location": [1394, 441, 13], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "FortuneTeller", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37693d18-5f7b-4461-b8a3-49202500e8da", - "name": "Spawner (312)", - "location": [1235, 543, -15], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "FortuneTeller", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ba1aca3-914a-4c73-91b0-12e42056659e", - "name": "Spawner (312)", - "location": [1499, 619, -19], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d6ef24f-8252-459b-a03b-d6fdaeb96597", - "name": "Spawner (312)", - "location": [872, 586, -40], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4e5b8807-7ced-4f40-ae49-c4507b82fb78", - "name": "Spawner (312)", - "location": [1253, 588, -19], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5053b0d7-ca6f-4156-bb35-28f937b1db6b", - "name": "Spawner (312)", - "location": [824, 602, -40], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "52da389e-73f7-493e-b830-88dc0c4525f4", - "name": "Spawner (312)", - "location": [1598, 539, -19], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5568a6cc-21a3-4550-9cc2-3ad836c7983b", - "name": "Spawner (312)", - "location": [1600, 552, -16], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d0135b3-074b-49ae-b850-25281059b466", - "name": "Spawner (312)", - "location": [575, 537, -69], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6670bd00-6ae7-43fd-a102-f77c877c6107", - "name": "Spawner (312)", - "location": [1518, 619, -19], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "68189f98-29d3-49e2-a415-c57862584b26", - "name": "Spawner (312)", - "location": [1628, 548, -11], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "69fd06d1-04cb-457e-bbd9-e02475f6c4a8", - "name": "Spawner (312)", - "location": [896, 610, -40], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6ce1b420-27ed-4adf-9ba2-0eeb9b0600fe", - "name": "Spawner (312)", - "location": [826, 679, -40], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "724e9d36-13e4-4abc-ac60-18aa45fd712f", - "name": "Spawner (312)", - "location": [792, 665, 0], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "751c4f29-04b6-4c50-b914-591862159429", - "name": "Spawner (312)", - "location": [808, 586, -40], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c9b9466-17bc-4285-8c04-294fa9b2b885", - "name": "Spawner (312)", - "location": [1400, 434, -11], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "GypsyMaiden", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "81fc796d-04ed-48fb-a783-ad6ce619b116", "name": "Spawner (312)", "location": [808, 697, -40], @@ -434,10 +31,27 @@ ] }, { - "$type": "Spawner", - "guid": "826d7ee8-c78b-47ea-9ead-b30014afbe4d", + "type": "Spawner", + "guid": "751c4f29-04b6-4c50-b914-591862159429", "name": "Spawner (312)", - "location": [791, 618, 0], + "location": [808, 586, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bce87417-e533-491a-8a6a-734227db69db", + "name": "Spawner (312)", + "location": [1225, 563, -18], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -445,15 +59,13 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "StoneCrafter", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "GypsyAnimalTrainer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "84e30990-a039-4283-9bf3-00d8e70489de", + "type": "Spawner", + "guid": "2b7c425b-7a52-414a-a76a-cb0734c3165c", "name": "Spawner (312)", - "location": [840, 707, 0], + "location": [1388, 428, -23], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -461,28 +73,44 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "GypsyAnimalTrainer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "93dc7896-715b-44ad-b6e5-01a37dd42f81", + "type": "Spawner", + "guid": "0457d1c2-1736-4b97-96be-0625a8a08c91", "name": "Spawner (312)", - "location": [1405, 439, 2], + "location": [1497, 614, -14], "map": "Ilshenar", - "count": 1, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "GypsyBanker", "maxCount": 1, "probability": 100 } + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "20c754a1-ce9b-4068-89ae-b50d95a64c82", + "name": "Spawner (312)", + "location": [854, 680, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "9a08c2d8-b7b6-4b2f-b550-008e42f3a68b", "name": "Spawner (312)", "location": [855, 603, -40], @@ -499,10 +127,27 @@ ] }, { - "$type": "Spawner", - "guid": "9c4eb8ed-3851-4ff2-80a6-a0c6f6c3f948", + "type": "Spawner", + "guid": "fd5a6f5e-87cf-40c3-bbf6-d42f59e8f730", "name": "Spawner (312)", - "location": [774, 1144, -30], + "location": [766, 641, 0], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d27bf3f-df46-48bb-a611-a0f17e8d1051", + "name": "Spawner (312)", + "location": [1226, 554, -12], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -510,12 +155,334 @@ "team": 0, "homeRange": 0, "walkingRange": 5, + "entries": [{ "name": "GypsyBanker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b523ffff-8496-40a1-b051-f2e81e74263c", + "name": "Spawner (312)", + "location": [1610, 556, -19], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "6ce1b420-27ed-4adf-9ba2-0eeb9b0600fe", + "name": "Spawner (312)", + "location": [826, 679, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5053b0d7-ca6f-4156-bb35-28f937b1db6b", + "name": "Spawner (312)", + "location": [824, 602, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c9b9466-17bc-4285-8c04-294fa9b2b885", + "name": "Spawner (312)", + "location": [1400, 434, -11], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "GypsyMaiden", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f4a51238-7658-469e-9ff3-eec8ba035f95", + "name": "Spawner (312)", + "location": [665, 665, -35], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e9c58794-1e68-4a12-8e96-fe9695224e36", + "name": "Spawner (312)", + "location": [1364, 1051, -13], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "14094381-ef68-4f89-b700-0d824d8968b2", + "name": "Spawner (312)", + "location": [1261, 582, -18], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d10ff188-6468-434f-b032-dc92db16a183", + "name": "Spawner (312)", + "location": [284, 536, -24], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37693d18-5f7b-4461-b8a3-49202500e8da", + "name": "Spawner (312)", + "location": [1235, 543, -15], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FortuneTeller", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff91a9cc-14c5-4a0d-a138-20f24c30cee0", + "name": "Spawner (312)", + "location": [1407, 428, -8], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FortuneTeller", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "68189f98-29d3-49e2-a415-c57862584b26", + "name": "Spawner (312)", + "location": [1628, 548, -11], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6670bd00-6ae7-43fd-a102-f77c877c6107", + "name": "Spawner (312)", + "location": [1518, 619, -19], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba48a4cb-841e-4be5-8ef9-1f06970d1829", + "name": "Spawner (312)", + "location": [1093, 955, -38], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d6ef24f-8252-459b-a03b-d6fdaeb96597", + "name": "Spawner (312)", + "location": [872, 586, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3f80bac-4411-4382-8781-582340ff4f03", + "name": "Spawner (312)", + "location": [1620, 554, -20], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c8e54c5c-c4fb-4aa2-8da2-40d6c7c23d72", + "name": "Spawner (312)", + "location": [1605, 541, -13], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "305ecea0-47a1-4561-bf25-5d879a152003", + "name": "Spawner (312)", + "location": [859, 698, -40], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "07dcfcfd-1f11-4fe7-8cc5-d1981fb1e74f", + "name": "Spawner (312)", + "location": [868, 657, -40], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "de233679-dda4-4826-be6f-66a66bc3ed0c", + "name": "Spawner (312)", + "location": [869, 625, -40], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93dc7896-715b-44ad-b6e5-01a37dd42f81", + "name": "Spawner (312)", + "location": [1405, 439, 2], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "GypsyBanker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "33de0f8c-f6ba-4e9b-a09b-dc36c96db2ea", + "name": "Spawner (312)", + "location": [1394, 441, 13], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FortuneTeller", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "9fffcfc7-2091-4013-b643-205c9567c311", "name": "Spawner (312)", "location": [1505, 611, -19], @@ -533,10 +500,10 @@ ] }, { - "$type": "Spawner", - "guid": "b40eb968-c112-4c66-a331-b96a7d08622b", + "type": "Spawner", + "guid": "9c4eb8ed-3851-4ff2-80a6-a0c6f6c3f948", "name": "Spawner (312)", - "location": [1514, 611, -14], + "location": [774, 1144, -30], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -544,15 +511,13 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "b523ffff-8496-40a1-b051-f2e81e74263c", + "type": "Spawner", + "guid": "52da389e-73f7-493e-b830-88dc0c4525f4", "name": "Spawner (312)", - "location": [1610, 556, -19], + "location": [1598, 539, -19], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", @@ -561,15 +526,15 @@ "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ba48a4cb-841e-4be5-8ef9-1f06970d1829", + "type": "Spawner", + "guid": "f38481b6-0a95-44e0-af74-8cc94195e282", "name": "Spawner (312)", - "location": [1093, 955, -38], + "location": [369, 1432, 15], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", @@ -583,10 +548,44 @@ ] }, { - "$type": "Spawner", - "guid": "bae2beb6-304b-4002-b459-680d881199e5", + "type": "Spawner", + "guid": "12d3edd5-4ebb-41a7-82ff-2d2ab3c4adef", "name": "Spawner (312)", - "location": [1566, 1049, -8], + "location": [1318, 1328, -14], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69fd06d1-04cb-457e-bbd9-e02475f6c4a8", + "name": "Spawner (312)", + "location": [896, 610, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32050353-d0d9-4b06-afee-a65fae031a98", + "name": "Spawner (312)", + "location": [896, 610, -40], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -594,15 +593,13 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "bce87417-e533-491a-8a6a-734227db69db", + "type": "Spawner", + "guid": "3ba1aca3-914a-4c73-91b0-12e42056659e", "name": "Spawner (312)", - "location": [1225, 563, -18], + "location": [1499, 619, -19], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -610,28 +607,28 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "GypsyAnimalTrainer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "c8e54c5c-c4fb-4aa2-8da2-40d6c7c23d72", + "type": "Spawner", + "guid": "5568a6cc-21a3-4550-9cc2-3ad836c7983b", "name": "Spawner (312)", - "location": [1605, 541, -13], + "location": [1600, 552, -16], "map": "Ilshenar", - "count": 1, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ce3b9e5a-5028-4768-86ca-ebc7591eb49d", "name": "Spawner (312)", "location": [840, 571, 0], @@ -649,10 +646,72 @@ ] }, { - "$type": "Spawner", - "guid": "d10ff188-6468-434f-b032-dc92db16a183", + "type": "Spawner", + "guid": "84e30990-a039-4283-9bf3-00d8e70489de", "name": "Spawner (312)", - "location": [284, 536, -24], + "location": [840, 707, 0], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4e5b8807-7ced-4f40-ae49-c4507b82fb78", + "name": "Spawner (312)", + "location": [1253, 588, -19], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bae2beb6-304b-4002-b459-680d881199e5", + "name": "Spawner (312)", + "location": [1566, 1049, -8], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "724e9d36-13e4-4abc-ac60-18aa45fd712f", + "name": "Spawner (312)", + "location": [792, 665, 0], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d0135b3-074b-49ae-b850-25281059b466", + "name": "Spawner (312)", + "location": [575, 537, -69], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", @@ -666,56 +725,7 @@ ] }, { - "$type": "Spawner", - "guid": "d3f80bac-4411-4382-8781-582340ff4f03", - "name": "Spawner (312)", - "location": [1620, 554, -20], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "de233679-dda4-4826-be6f-66a66bc3ed0c", - "name": "Spawner (312)", - "location": [869, 625, -40], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e9c58794-1e68-4a12-8e96-fe9695224e36", - "name": "Spawner (312)", - "location": [1364, 1051, -13], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "ee8d0d83-c586-49d8-8de2-038e1d6f2848", "name": "Spawner (312)", "location": [682, 297, -41], @@ -732,27 +742,10 @@ ] }, { - "$type": "Spawner", - "guid": "f38481b6-0a95-44e0-af74-8cc94195e282", + "type": "Spawner", + "guid": "b40eb968-c112-4c66-a331-b96a7d08622b", "name": "Spawner (312)", - "location": [369, 1432, 15], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f4a51238-7658-469e-9ff3-eec8ba035f95", - "name": "Spawner (312)", - "location": [665, 665, -35], + "location": [1514, 611, -14], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", @@ -760,41 +753,6 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fd5a6f5e-87cf-40c3-bbf6-d42f59e8f730", - "name": "Spawner (312)", - "location": [766, 641, 0], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ff91a9cc-14c5-4a0d-a138-20f24c30cee0", - "name": "Spawner (312)", - "location": [1407, 428, -8], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "FortuneTeller", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Wisp.json b/Distribution/Data/Spawns/post-uoml/ilshenar/Wisp.json similarity index 81% rename from Distribution/Data/Spawns/shared/ilshenar/Wisp.json rename to Distribution/Data/Spawns/post-uoml/ilshenar/Wisp.json index df59cec46..0db759881 100644 --- a/Distribution/Data/Spawns/shared/ilshenar/Wisp.json +++ b/Distribution/Data/Spawns/post-uoml/ilshenar/Wisp.json @@ -1,172 +1,20 @@ [ { - "$type": "Spawner", - "guid": "0d68da82-d86a-4c40-8085-174a01c65f55", + "type": "Spawner", + "guid": "ae956108-5118-4793-94d7-ac885617030f", "name": "Spawner (313)", - "location": [775, 1479, -28], + "location": [681, 1535, -28], "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Balron", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0fe99fe0-a203-4563-994b-5e02d70f1b3f", - "name": "Spawner (313)", - "location": [888, 1568, -28], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "14b1989d-ec45-42db-af1b-92f49023b981", - "name": "Spawner (313)", - "location": [891, 1537, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 10, - "entries": [ - { "name": "RottingCorpse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ef49b9b-97d6-431a-946d-ffd2f1615697", - "name": "Spawner (313)", - "location": [855, 1566, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 10, - "entries": [ - { "name": "Shade", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33185d9f-afa2-43d7-a7aa-7d221c1a309a", - "name": "Spawner (313)", - "location": [964, 1554, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 10, - "entries": [ - { "name": "LichLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b9e3103-e83e-48a4-88b3-c72acbe5e173", - "name": "Spawner (313)", - "location": [936, 1559, -22], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "516473d8-b5d1-4924-ad74-556649f98470", - "name": "Spawner (313)", - "location": [939, 1492, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "Imp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "530dcd87-a132-4cdb-a9c9-0f78758e4ad0", - "name": "Spawner (313)", - "location": [962, 1480, -28], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Wraith", "maxCount": 4, "probability": 100 }, - { "name": "Spectre", "maxCount": 4, "probability": 100 }, - { "name": "Shade", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5ce9d857-549d-4bf0-8e70-2dd6902c4ccf", - "name": "Spawner (313)", - "location": [775, 1480, -28], - "map": "Ilshenar", - "count": 5, + "count": 20, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Imp", "maxCount": 5, "probability": 100 } - ] + "walkingRange": 60, + "entries": [{ "name": "Wisp", "maxCount": 20, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "649b7645-25f8-4c0c-ac2d-896e558b2767", - "name": "Spawner (313)", - "location": [776, 1480, -28], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 9, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 9, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 6, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "67d46705-d56a-4098-bafb-ede2658118a8", "name": "Spawner (313)", "location": [855, 1567, -28], @@ -177,12 +25,38 @@ "team": 0, "homeRange": 6, "walkingRange": 6, - "entries": [ - { "name": "SkeletalMage", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "SkeletalMage", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "2ef49b9b-97d6-431a-946d-ffd2f1615697", + "name": "Spawner (313)", + "location": [855, 1566, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "Shade", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a90852cd-b672-4c4f-83bb-99dcc861ead0", + "name": "Spawner (313)", + "location": [851, 1498, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 20, + "entries": [{ "name": "RottingCorpse", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "6cf2cd79-9f0f-4f25-af2b-333a9317171f", "name": "Spawner (313)", "location": [827, 1479, -28], @@ -200,7 +74,161 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "add779de-8361-4edb-b214-900500ca81f0", + "name": "Spawner (313)", + "location": [888, 1471, -28], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 20, + "entries": [ + { "name": "Shade", "maxCount": 3, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4f8aa84-982a-4bd5-a708-9724d8598dd7", + "name": "Spawner (313)", + "location": [901, 1507, 2], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "EvilMageLord", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "79272788-c6c6-4216-84e0-985b942af71b", + "name": "Spawner (313)", + "location": [871, 1531, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "EvilMage", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "14b1989d-ec45-42db-af1b-92f49023b981", + "name": "Spawner (313)", + "location": [891, 1537, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "RottingCorpse", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0fe99fe0-a203-4563-994b-5e02d70f1b3f", + "name": "Spawner (313)", + "location": [888, 1568, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be42e727-e04f-47a2-b07c-49046fdafcb9", + "name": "Spawner (313)", + "location": [939, 1531, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Imp", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "516473d8-b5d1-4924-ad74-556649f98470", + "name": "Spawner (313)", + "location": [939, 1492, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Imp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc4fedc9-9489-4385-bf85-9ec569081f4a", + "name": "Spawner (313)", + "location": [961, 1502, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "Ettin", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "530dcd87-a132-4cdb-a9c9-0f78758e4ad0", + "name": "Spawner (313)", + "location": [962, 1480, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b19163ea-9376-48e7-9bf7-b0a6a2e75d38", + "name": "Spawner (313)", + "location": [1000, 1512, 0], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Titan", "maxCount": 2, "probability": 100 }, + { "name": "Cyclops", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "74498688-9c91-4ea3-8abd-adcd2beb3b8d", "name": "Spawner (313)", "location": [959, 1530, -28], @@ -219,134 +247,78 @@ ] }, { - "$type": "Spawner", - "guid": "79272788-c6c6-4216-84e0-985b942af71b", + "type": "Spawner", + "guid": "4b9e3103-e83e-48a4-88b3-c72acbe5e173", "name": "Spawner (313)", - "location": [871, 1531, -28], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 10, - "entries": [ - { "name": "EvilMage", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a90852cd-b672-4c4f-83bb-99dcc861ead0", - "name": "Spawner (313)", - "location": [851, 1498, -28], + "location": [936, 1559, -22], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 20, - "entries": [ - { "name": "RottingCorpse", "maxCount": 1, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "add779de-8361-4edb-b214-900500ca81f0", + "type": "Spawner", + "guid": "33185d9f-afa2-43d7-a7aa-7d221c1a309a", "name": "Spawner (313)", - "location": [888, 1471, -28], + "location": [964, 1554, -28], "map": "Ilshenar", - "count": 8, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, - "walkingRange": 20, - "entries": [ - { "name": "Shade", "maxCount": 3, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 2, "probability": 100 } - ] + "homeRange": 7, + "walkingRange": 10, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ae956108-5118-4793-94d7-ac885617030f", + "type": "Spawner", + "guid": "0d68da82-d86a-4c40-8085-174a01c65f55", "name": "Spawner (313)", - "location": [681, 1535, -28], + "location": [775, 1479, -28], "map": "Ilshenar", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 60, - "entries": [ - { "name": "Wisp", "maxCount": 20, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b19163ea-9376-48e7-9bf7-b0a6a2e75d38", - "name": "Spawner (313)", - "location": [1000, 1512, 0], - "map": "Ilshenar", - "count": 5, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 15, "walkingRange": 15, - "entries": [ - { "name": "Titan", "maxCount": 2, "probability": 100 }, - { "name": "Cyclops", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "Balron", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "be42e727-e04f-47a2-b07c-49046fdafcb9", + "type": "Spawner", + "guid": "5ce9d857-549d-4bf0-8e70-2dd6902c4ccf", "name": "Spawner (313)", - "location": [939, 1531, -28], + "location": [775, 1480, -28], "map": "Ilshenar", - "count": 2, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 } - ] + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Imp", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f4f8aa84-982a-4bd5-a708-9724d8598dd7", + "type": "Spawner", + "guid": "649b7645-25f8-4c0c-ac2d-896e558b2767", "name": "Spawner (313)", - "location": [901, 1507, 2], + "location": [776, 1480, -28], "map": "Ilshenar", - "count": 2, + "count": 15, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, + "homeRange": 3, + "walkingRange": 3, "entries": [ - { "name": "EvilMageLord", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fc4fedc9-9489-4385-bf85-9ec569081f4a", - "name": "Spawner (313)", - "location": [961, 1502, -28], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 10, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 } + { "name": "TreasureChestLevel1", "maxCount": 9, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 9, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 6, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 6, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/malas/Bedlam.json b/Distribution/Data/Spawns/post-uoml/malas/Bedlam.json similarity index 89% rename from Distribution/Data/Spawns/shared/malas/Bedlam.json rename to Distribution/Data/Spawns/post-uoml/malas/Bedlam.json index 2180f341e..639f36084 100644 --- a/Distribution/Data/Spawns/shared/malas/Bedlam.json +++ b/Distribution/Data/Spawns/post-uoml/malas/Bedlam.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "8c2124f4-e175-4198-8cf8-9cf64804fc22", "name": "Spawner (408)", "location": [98, 1656, 0], @@ -24,7 +24,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "eaa69fa3-4076-4fc5-950a-fe175713bbd3", "name": "Spawner (408)", "location": [104, 1655, 0], @@ -35,8 +35,6 @@ "team": 0, "homeRange": 15, "walkingRange": 15, - "entries": [ - { "name": "RedDeath", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "RedDeath", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/malas/Citadel.json b/Distribution/Data/Spawns/post-uoml/malas/Citadel.json similarity index 92% rename from Distribution/Data/Spawns/shared/malas/Citadel.json rename to Distribution/Data/Spawns/post-uoml/malas/Citadel.json index 3fe03ac03..1b3e105c5 100644 --- a/Distribution/Data/Spawns/shared/malas/Citadel.json +++ b/Distribution/Data/Spawns/post-uoml/malas/Citadel.json @@ -1,39 +1,6 @@ [ { - "$type": "Spawner", - "guid": "114d80f2-07e6-49e3-9418-6e7c30291028", - "name": "Spawner (406)", - "location": [142, 1921, 0], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "tigersclawthief", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15b169f9-fe9e-4ef7-973b-cb0619b9937d", - "name": "Spawner (406)", - "location": [82, 1875, 0], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "serpentsfangassassin", "maxCount": 4, "probability": 100 }, - { "name": "eliteninjawarrior", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "17abd9d2-3b7a-44ec-a724-57c202236788", "name": "Spawner (406)", "location": [179, 1869, 0], @@ -50,7 +17,38 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "856c181e-183a-4231-acc8-5146301c56c6", + "name": "Spawner (406)", + "location": [185, 1920, 0], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "magedragonsflamemage", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4854c18d-7bf2-4f8c-a69f-7679204b6d7d", + "name": "Spawner (406)", + "location": [155, 1869, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 4, "probability": 100 }, + { "name": "dragonsflamemage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "28535caf-b979-4745-8a87-e62691d7cb11", "name": "Spawner (406)", "location": [145, 1898, 0], @@ -68,7 +66,25 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "b5fed5a1-35db-4f09-b4b6-8017146a8bf3", + "name": "Spawner (406)", + "location": [174, 1900, 0], + "map": "Malas", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 5, "probability": 100 }, + { "name": "dragonsflamemage", "maxCount": 5, "probability": 100 }, + { "name": "eliteninjawarrior", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "2d629e57-63db-4610-b6d2-cf3bfd34f0b0", "name": "Spawner (406)", "location": [181, 1883, 0], @@ -87,7 +103,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "347e85d2-c752-4abf-86c7-bda2a2051033", "name": "Spawner (406)", "location": [183, 1961, 0], @@ -104,92 +120,7 @@ ] }, { - "$type": "Spawner", - "guid": "364cc553-16d0-428d-bf97-061c3ddfd120", - "name": "Spawner (406)", - "location": [170, 1974, 0], - "map": "Malas", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "serpentsfangassassin", "maxCount": 3, "probability": 100 }, - { "name": "serpentsfanghighexecutioner", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "435ce67b-aad4-4ed0-b621-73ab4b6b0042", - "name": "Spawner (406)", - "location": [81, 1892, 0], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "eliteninjawarrior", "maxCount": 4, "probability": 100 }, - { "name": "serpentsfangassassin", "maxCount": 4, "probability": 100 }, - { "name": "dragonsflamemage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4854c18d-7bf2-4f8c-a69f-7679204b6d7d", - "name": "Spawner (406)", - "location": [155, 1869, 0], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "serpentsfangassassin", "maxCount": 4, "probability": 100 }, - { "name": "dragonsflamemage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50c87975-6021-46cb-bbe1-58c45ba776b7", - "name": "Spawner (406)", - "location": [77, 1883, 0], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "dragonsflamemage", "maxCount": 4, "probability": 100 }, - { "name": "eliteninjawarrior", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "856c181e-183a-4231-acc8-5146301c56c6", - "name": "Spawner (406)", - "location": [185, 1920, 0], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "magedragonsflamemage", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "9f02ef97-7699-4a65-8cd6-615aa50cc318", "name": "Spawner (406)", "location": [139, 1868, 0], @@ -209,25 +140,24 @@ ] }, { - "$type": "Spawner", - "guid": "b5fed5a1-35db-4f09-b4b6-8017146a8bf3", + "type": "Spawner", + "guid": "364cc553-16d0-428d-bf97-061c3ddfd120", "name": "Spawner (406)", - "location": [174, 1900, 0], + "location": [170, 1974, 0], "map": "Malas", - "count": 5, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, + "homeRange": 5, + "walkingRange": 5, "entries": [ - { "name": "serpentsfangassassin", "maxCount": 5, "probability": 100 }, - { "name": "dragonsflamemage", "maxCount": 5, "probability": 100 }, - { "name": "eliteninjawarrior", "maxCount": 5, "probability": 100 } + { "name": "serpentsfangassassin", "maxCount": 3, "probability": 100 }, + { "name": "serpentsfanghighexecutioner", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "dccae74d-bafa-40ff-9984-7a2a0af5d8de", "name": "Spawner (406)", "location": [140, 1974, 0], @@ -244,7 +174,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fb6ee275-c5c2-4780-9180-76e3a4ac1a97", "name": "Spawner (406)", "location": [134, 1947, 0], @@ -259,5 +189,71 @@ { "name": "magedragonsflamemage", "maxCount": 2, "probability": 100 }, { "name": "dragonsflamemage", "maxCount": 2, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "435ce67b-aad4-4ed0-b621-73ab4b6b0042", + "name": "Spawner (406)", + "location": [81, 1892, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "eliteninjawarrior", "maxCount": 4, "probability": 100 }, + { "name": "serpentsfangassassin", "maxCount": 4, "probability": 100 }, + { "name": "dragonsflamemage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "114d80f2-07e6-49e3-9418-6e7c30291028", + "name": "Spawner (406)", + "location": [142, 1921, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "tigersclawthief", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50c87975-6021-46cb-bbe1-58c45ba776b7", + "name": "Spawner (406)", + "location": [77, 1883, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "dragonsflamemage", "maxCount": 4, "probability": 100 }, + { "name": "eliteninjawarrior", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15b169f9-fe9e-4ef7-973b-cb0619b9937d", + "name": "Spawner (406)", + "location": [82, 1875, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 4, "probability": 100 }, + { "name": "eliteninjawarrior", "maxCount": 4, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/shared/malas/Doom.json b/Distribution/Data/Spawns/post-uoml/malas/Doom.json similarity index 80% rename from Distribution/Data/Spawns/shared/malas/Doom.json rename to Distribution/Data/Spawns/post-uoml/malas/Doom.json index 3db6e02a8..9754fef3b 100644 --- a/Distribution/Data/Spawns/shared/malas/Doom.json +++ b/Distribution/Data/Spawns/post-uoml/malas/Doom.json @@ -1,332 +1,6 @@ [ { - "$type": "Spawner", - "guid": "0bb98d0c-0614-431b-a6ce-c30718f447c8", - "name": "Spawner (401)", - "location": [447, 25, -1], - "map": "Malas", - "count": 6, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Skeleton", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0e708303-8a2d-4207-bdc3-d7f732e04efe", - "name": "Spawner (401)", - "location": [496, 75, 0], - "map": "Malas", - "count": 3, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "PatchworkSkeleton", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1210b304-4b1a-47be-a503-9f490764d069", - "name": "Spawner (401)", - "location": [287, 81, -1], - "map": "Malas", - "count": 6, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Skeleton", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "13e0051b-bff0-4826-9e89-73ea9ec64e7a", - "name": "Spawner (401)", - "location": [320, 207, -1], - "map": "Malas", - "count": 2, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "182c0603-13fa-414f-849c-dcbde31a739a", - "name": "Spawner (401)", - "location": [479, 168, -1], - "map": "Malas", - "count": 4, - "minDelay": "00:02:00", - "maxDelay": "00:03:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Devourer", "maxCount": 3, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20977621-9e81-4c25-b802-19fa12fc3633", - "name": "Spawner (401)", - "location": [323, 90, -1], - "map": "Malas", - "count": 9, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 }, - { "name": "PatchworkSkeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "218aaa26-c02b-4605-8c79-c9879523ea0b", - "name": "Spawner (401)", - "location": [356, 40, -1], - "map": "Malas", - "count": 6, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "VampireBat", "maxCount": 4, "probability": 100 }, - { "name": "Skeleton", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "298afdb1-74fb-4668-9bc2-5f625a61caed", - "name": "Spawner (401)", - "location": [478, 206, -1], - "map": "Malas", - "count": 2, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2a98c34c-cb3d-4d28-8c12-1a3cf7cc58ad", - "name": "Spawner (401)", - "location": [330, 136, 0], - "map": "Malas", - "count": 2, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2be25342-c032-406a-af47-8eb5994c0875", - "name": "Spawner (401)", - "location": [427, 256, -1], - "map": "Malas", - "count": 2, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32a2e0d8-8246-42ff-8e13-c15b51a9f73f", - "name": "Spawner (401)", - "location": [470, 30, -2], - "map": "Malas", - "count": 6, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "PatchworkSkeleton", "maxCount": 4, "probability": 100 }, - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "42bbf95d-6b39-4df6-a4f4-7a8d385b49ef", - "name": "Spawner (401)", - "location": [442, 205, -1], - "map": "Malas", - "count": 1, - "minDelay": "00:20:00", - "maxDelay": "00:40:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BoneDemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "48538f93-9afa-44c9-baab-b1c3612bdde5", - "name": "Spawner (401)", - "location": [356, 234, -1], - "map": "Malas", - "count": 3, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Devourer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5110399b-8443-448e-a68c-f59ac2303716", - "name": "Spawner (401)", - "location": [441, 78, -1], - "map": "Malas", - "count": 6, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PatchworkSkeleton", "maxCount": 4, "probability": 100 }, - { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "55898d74-f2cf-4c30-953e-e5f3d9a3b5bf", - "name": "Spawner (401)", - "location": [278, 233, 0], - "map": "Malas", - "count": 4, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PatchworkSkeleton", "maxCount": 2, "probability": 100 }, - { "name": "VampireBat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "59819649-f460-4934-ba36-a89a98aa13f2", - "name": "Spawner (401)", - "location": [320, 114, -1], - "map": "Malas", - "count": 7, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 35, - "walkingRange": 35, - "entries": [ - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "59d35721-49b5-4ff9-9c91-fad1b3d4b953", - "name": "Spawner (401)", - "location": [395, 33, -1], - "map": "Malas", - "count": 4, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "VampireBat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5bc9de9b-c7d7-4152-9fd1-19e57598100e", - "name": "Spawner (401)", - "location": [320, 114, -1], - "map": "Malas", - "count": 3, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Mummy", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5c28d40c-81f7-428d-b661-6e272aeabf3d", - "name": "Spawner (401)", - "location": [480, 227, -1], - "map": "Malas", - "count": 2, - "minDelay": "00:03:00", - "maxDelay": "00:05:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "613d79a6-10e0-4465-9937-142ee7e701b0", - "name": "Spawner (401)", - "location": [289, 154, 5], - "map": "Malas", - "count": 1, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "LichLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "646d5241-162f-477e-a05c-17ba7eb95a83", "name": "Spawner (401)", "location": [379, 147, -1], @@ -343,136 +17,142 @@ ] }, { - "$type": "Spawner", - "guid": "6492e163-beb3-4abc-a674-921cbf5ba5df", + "type": "Spawner", + "guid": "c85f6fd0-ad43-4e05-a43e-b49002b7f22c", "name": "Spawner (401)", - "location": [356, 234, -1], + "location": [372, 201, -1], "map": "Malas", - "count": 3, + "count": 4, "minDelay": "00:01:00", "maxDelay": "00:02:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, + "homeRange": 20, + "walkingRange": 20, "entries": [ - { "name": "Skeleton", "maxCount": 3, "probability": 100 } + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "693a953a-75cf-43df-9708-b781c8a04439", + "type": "Spawner", + "guid": "42bbf95d-6b39-4df6-a4f4-7a8d385b49ef", "name": "Spawner (401)", - "location": [325, 172, -1], - "map": "Malas", - "count": 6, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 35, - "walkingRange": 35, - "entries": [ - { "name": "PatchworkSkeleton", "maxCount": 3, "probability": 100 }, - { "name": "VampireBat", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "74182f58-a286-4576-9615-f92bbd4e7b71", - "name": "Spawner (401)", - "location": [378, 236, -1], - "map": "Malas", - "count": 2, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Devourer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "92450262-dc59-4666-b8bd-233fa481ddcb", - "name": "Spawner (401)", - "location": [400, 174, -1], + "location": [442, 205, -1], "map": "Malas", "count": 1, + "minDelay": "00:20:00", + "maxDelay": "00:40:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "BoneDemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dcade1a7-1652-4eb7-a550-d9ed7a7449ac", + "name": "Spawner (401)", + "location": [442, 205, -1], + "map": "Malas", + "count": 11, "minDelay": "00:01:00", "maxDelay": "00:02:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 } + { "name": "PatchworkSkeleton", "maxCount": 6, "probability": 100 }, + { "name": "VampireBat", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "93ce2586-d403-49a2-acbb-034ef0ee797a", + "type": "Spawner", + "guid": "5c28d40c-81f7-428d-b661-6e272aeabf3d", "name": "Spawner (401)", - "location": [278, 233, 0], + "location": [480, 227, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:03:00", + "maxDelay": "00:05:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff72dd04-bba5-4a6f-8899-d851b4c1fe88", + "name": "Spawner (401)", + "location": [480, 227, -1], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d5daa218-49a1-446c-a871-a7ebc77cacf9", + "name": "Spawner (401)", + "location": [480, 227, -1], "map": "Malas", "count": 3, "minDelay": "00:01:00", "maxDelay": "00:02:00", "team": 0, - "homeRange": 35, - "walkingRange": 35, - "entries": [ - { "name": "Devourer", "maxCount": 3, "probability": 100 } - ] + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "968ff190-b606-41f5-8cf0-36e15d3fd7d4", + "type": "Spawner", + "guid": "c2ca4b73-4847-47ab-83de-c3f0abb570a9", "name": "Spawner (401)", - "location": [382, 82, -1], + "location": [493, 202, -1], "map": "Malas", "count": 6, "minDelay": "00:01:00", "maxDelay": "00:02:00", "team": 0, - "homeRange": 40, - "walkingRange": 40, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Skeleton", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "182c0603-13fa-414f-849c-dcbde31a739a", + "name": "Spawner (401)", + "location": [479, 168, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:02:00", + "maxDelay": "00:03:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, "entries": [ - { "name": "VampireBat", "maxCount": 6, "probability": 100 } + { "name": "Devourer", "maxCount": 3, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "9e641eb9-2f8a-41ae-afb9-24a4b5879a0a", + "type": "Spawner", + "guid": "f909d2a4-b3bf-4d0c-b07c-f36839c99209", "name": "Spawner (401)", - "location": [305, 28, 0], + "location": [479, 168, -1], "map": "Malas", - "count": 4, + "count": 5, "minDelay": "00:01:00", "maxDelay": "00:02:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Gibberling", "maxCount": 4, "probability": 100 } - ] + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "a1434ffe-19f2-4076-b1ce-28e59dc9f627", - "name": "Spawner (401)", - "location": [496, 75, 0], - "map": "Malas", - "count": 4, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "VampireBat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a42b1d2c-dcd4-4d43-aada-f5707a5f9e9f", "name": "Spawner (401)", "location": [468, 141, -1], @@ -483,29 +163,190 @@ "team": 0, "homeRange": 6, "walkingRange": 6, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cf26832d-8e20-4277-8491-91461dd2995e", + "name": "Spawner (401)", + "location": [487, 118, -1], + "map": "Malas", + "count": 5, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Mummy", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "a81f4ff2-ee77-4e1f-968d-335a0ecbe7b9", + "type": "Spawner", + "guid": "0e708303-8a2d-4207-bdc3-d7f732e04efe", "name": "Spawner (401)", - "location": [427, 256, -1], + "location": [496, 75, 0], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "PatchworkSkeleton", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a1434ffe-19f2-4076-b1ce-28e59dc9f627", + "name": "Spawner (401)", + "location": [496, 75, 0], "map": "Malas", "count": 4, "minDelay": "00:01:00", "maxDelay": "00:02:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "VampireBat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0bb98d0c-0614-431b-a6ce-c30718f447c8", + "name": "Spawner (401)", + "location": [447, 25, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Skeleton", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f20c08f3-28e9-47a3-b24e-b31370ce2c18", + "name": "Spawner (401)", + "location": [447, 25, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "32a2e0d8-8246-42ff-8e13-c15b51a9f73f", + "name": "Spawner (401)", + "location": [470, 30, -2], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, "entries": [ - { "name": "LichLord", "maxCount": 1, "probability": 100 }, - { "name": "RestlessSoul", "maxCount": 3, "probability": 100 } + { "name": "PatchworkSkeleton", "maxCount": 4, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "bbfc66e4-a4fd-486a-960e-5cb7c64c2e72", + "name": "Spawner (401)", + "location": [456, 48, -1], + "map": "Malas", + "count": 12, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "FleshGolem", "maxCount": 6, "probability": 100 }, + { "name": "Devourer", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59d35721-49b5-4ff9-9c91-fad1b3d4b953", + "name": "Spawner (401)", + "location": [395, 33, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "VampireBat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "218aaa26-c02b-4605-8c79-c9879523ea0b", + "name": "Spawner (401)", + "location": [356, 40, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "VampireBat", "maxCount": 4, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e641eb9-2f8a-41ae-afb9-24a4b5879a0a", + "name": "Spawner (401)", + "location": [305, 28, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Gibberling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e12bb836-80f3-4ce1-b068-9d2485922b0f", + "name": "Spawner (401)", + "location": [290, 28, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "GoreFiend", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f9c8b1af-1ea5-466d-90a1-e956f8c8de31", + "name": "Spawner (401)", + "location": [266, 26, 0], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 5, + "walkingRange": 8, + "entries": [{ "name": "LichLord", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "b0f74ae9-9e85-4525-9903-6e69c2f818e4", "name": "Spawner (401)", "location": [309, 15, 0], @@ -523,74 +364,7 @@ ] }, { - "$type": "Spawner", - "guid": "bbfc66e4-a4fd-486a-960e-5cb7c64c2e72", - "name": "Spawner (401)", - "location": [456, 48, -1], - "map": "Malas", - "count": 12, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "FleshGolem", "maxCount": 6, "probability": 100 }, - { "name": "Devourer", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2ca4b73-4847-47ab-83de-c3f0abb570a9", - "name": "Spawner (401)", - "location": [493, 202, -1], - "map": "Malas", - "count": 6, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "Skeleton", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c85f6fd0-ad43-4e05-a43e-b49002b7f22c", - "name": "Spawner (401)", - "location": [372, 201, -1], - "map": "Malas", - "count": 4, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 3, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cf26832d-8e20-4277-8491-91461dd2995e", - "name": "Spawner (401)", - "location": [487, 118, -1], - "map": "Malas", - "count": 5, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Skeleton", "maxCount": 3, "probability": 100 }, - { "name": "Mummy", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "d03c82ab-0be4-4818-8708-14afba5d8eee", "name": "Spawner (401)", "location": [366, 13, -1], @@ -601,28 +375,69 @@ "team": 0, "homeRange": 10, "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "20977621-9e81-4c25-b802-19fa12fc3633", + "name": "Spawner (401)", + "location": [323, 90, -1], + "map": "Malas", + "count": 9, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, "entries": [ - { "name": "Lich", "maxCount": 3, "probability": 100 } + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "PatchworkSkeleton", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "d5daa218-49a1-446c-a871-a7ebc77cacf9", + "type": "Spawner", + "guid": "59819649-f460-4934-ba36-a89a98aa13f2", "name": "Spawner (401)", - "location": [480, 227, -1], + "location": [320, 114, -1], + "map": "Malas", + "count": 7, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [{ "name": "Zombie", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5bc9de9b-c7d7-4152-9fd1-19e57598100e", + "name": "Spawner (401)", + "location": [320, 114, -1], "map": "Malas", "count": 3, "minDelay": "00:01:00", "maxDelay": "00:02:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 3, "probability": 100 } - ] + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Mummy", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "2a98c34c-cb3d-4d28-8c12-1a3cf7cc58ad", + "name": "Spawner (401)", + "location": [330, 136, 0], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "d8a6cfe1-0e71-423a-94e9-4ad92ef8d73d", "name": "Spawner (401)", "location": [330, 136, 0], @@ -633,12 +448,173 @@ "team": 0, "homeRange": 4, "walkingRange": 4, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "693a953a-75cf-43df-9708-b781c8a04439", + "name": "Spawner (401)", + "location": [325, 172, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, "entries": [ - { "name": "LichLord", "maxCount": 1, "probability": 100 } + { "name": "PatchworkSkeleton", "maxCount": 3, "probability": 100 }, + { "name": "VampireBat", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "613d79a6-10e0-4465-9937-142ee7e701b0", + "name": "Spawner (401)", + "location": [289, 154, 5], + "map": "Malas", + "count": 1, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93ce2586-d403-49a2-acbb-034ef0ee797a", + "name": "Spawner (401)", + "location": [278, 233, 0], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [{ "name": "Devourer", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "55898d74-f2cf-4c30-953e-e5f3d9a3b5bf", + "name": "Spawner (401)", + "location": [278, 233, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PatchworkSkeleton", "maxCount": 2, "probability": 100 }, + { "name": "VampireBat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13e0051b-bff0-4826-9e89-73ea9ec64e7a", + "name": "Spawner (401)", + "location": [320, 207, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6492e163-beb3-4abc-a674-921cbf5ba5df", + "name": "Spawner (401)", + "location": [356, 234, -1], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Skeleton", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "48538f93-9afa-44c9-baab-b1c3612bdde5", + "name": "Spawner (401)", + "location": [356, 234, -1], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Devourer", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "74182f58-a286-4576-9615-f92bbd4e7b71", + "name": "Spawner (401)", + "location": [378, 236, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Devourer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a81f4ff2-ee77-4e1f-968d-335a0ecbe7b9", + "name": "Spawner (401)", + "location": [427, 256, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "RestlessSoul", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2be25342-c032-406a-af47-8eb5994c0875", + "name": "Spawner (401)", + "location": [427, 256, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "RottingCorpse", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "298afdb1-74fb-4668-9bc2-5f625a61caed", + "name": "Spawner (401)", + "location": [478, 206, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "RottingCorpse", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "d99b3aa7-013c-4dd9-8258-ec68e91e6bf5", "name": "Spawner (401)", "location": [443, 128, -1], @@ -656,40 +632,35 @@ ] }, { - "$type": "Spawner", - "guid": "dcade1a7-1652-4eb7-a550-d9ed7a7449ac", + "type": "Spawner", + "guid": "92450262-dc59-4666-b8bd-233fa481ddcb", "name": "Spawner (401)", - "location": [442, 205, -1], + "location": [400, 174, -1], "map": "Malas", - "count": 11, + "count": 1, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1210b304-4b1a-47be-a503-9f490764d069", + "name": "Spawner (401)", + "location": [287, 81, -1], + "map": "Malas", + "count": 6, "minDelay": "00:01:00", "maxDelay": "00:02:00", "team": 0, "homeRange": 30, "walkingRange": 30, - "entries": [ - { "name": "PatchworkSkeleton", "maxCount": 6, "probability": 100 }, - { "name": "VampireBat", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Skeleton", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e12bb836-80f3-4ce1-b068-9d2485922b0f", - "name": "Spawner (401)", - "location": [290, 28, 0], - "map": "Malas", - "count": 4, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "GoreFiend", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e764f652-de2c-420f-8419-7d29a2d6cd9c", "name": "Spawner (401)", "location": [287, 81, -1], @@ -706,67 +677,34 @@ ] }, { - "$type": "Spawner", - "guid": "f20c08f3-28e9-47a3-b24e-b31370ce2c18", + "type": "Spawner", + "guid": "5110399b-8443-448e-a68c-f59ac2303716", "name": "Spawner (401)", - "location": [447, 25, -1], + "location": [441, 78, -1], "map": "Malas", - "count": 2, + "count": 6, "minDelay": "00:01:00", "maxDelay": "00:02:00", "team": 0, - "homeRange": 15, - "walkingRange": 15, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } + { "name": "PatchworkSkeleton", "maxCount": 4, "probability": 100 }, + { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f909d2a4-b3bf-4d0c-b07c-f36839c99209", + "type": "Spawner", + "guid": "968ff190-b606-41f5-8cf0-36e15d3fd7d4", "name": "Spawner (401)", - "location": [479, 168, -1], + "location": [382, 82, -1], "map": "Malas", - "count": 5, + "count": 6, "minDelay": "00:01:00", "maxDelay": "00:02:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f9c8b1af-1ea5-466d-90a1-e956f8c8de31", - "name": "Spawner (401)", - "location": [266, 26, 0], - "map": "Malas", - "count": 2, - "minDelay": "00:01:00", - "maxDelay": "00:02:00", - "team": 0, - "homeRange": 5, - "walkingRange": 8, - "entries": [ - { "name": "LichLord", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ff72dd04-bba5-4a6f-8899-d851b4c1fe88", - "name": "Spawner (401)", - "location": [480, 227, -1], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "LichLord", "maxCount": 1, "probability": 100 } - ] + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "VampireBat", "maxCount": 6, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/malas/Labyrinth.json b/Distribution/Data/Spawns/post-uoml/malas/Labyrinth.json similarity index 90% rename from Distribution/Data/Spawns/shared/malas/Labyrinth.json rename to Distribution/Data/Spawns/post-uoml/malas/Labyrinth.json index d65dd5491..df33e5cfc 100644 --- a/Distribution/Data/Spawns/shared/malas/Labyrinth.json +++ b/Distribution/Data/Spawns/post-uoml/malas/Labyrinth.json @@ -1,22 +1,6 @@ [ { - "$type": "Spawner", - "guid": "0b34e749-7a4e-4dc8-8b84-6d6444d80525", - "name": "Spawner (407)", - "location": [335, 1920, 0], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Miasma", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "3ba58850-4bed-4935-8a37-e0c703f3b768", "name": "Spawner (407)", "location": [366, 1928, 0], @@ -35,43 +19,86 @@ ] }, { - "$type": "Spawner", - "guid": "3d9f4f90-3f61-4dc1-9715-0d614c34c99c", + "type": "Spawner", + "guid": "ebe544f9-9a54-4822-bb37-ee11ba1059db", "name": "Spawner (407)", - "location": [382, 1913, 0], + "location": [384, 1932, 10], "map": "Malas", - "count": 0, + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Minotaur", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b34e749-7a4e-4dc8-8b84-6d6444d80525", + "name": "Spawner (407)", + "location": [335, 1920, 0], + "map": "Malas", + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 5, "walkingRange": 5, - "entries": [ - { "name": "Meraktus", "maxCount": 0, "probability": 100 } - ] + "entries": [{ "name": "Miasma", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "46f54372-6b9c-4e33-9f35-99fc01204b59", + "type": "Spawner", + "guid": "eea9dc24-23e1-4198-8dbc-b22bddb5a71c", "name": "Spawner (407)", - "location": [427, 1923, 5], + "location": [1740, 990, -80], "map": "Malas", - "count": 10, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 15, "walkingRange": 15, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b9da636f-3e98-4924-a7da-8ac3d0141e5f", + "name": "Spawner (407)", + "location": [1742, 990, -84], + "map": "Malas", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, "entries": [ - { "name": "MinotaurCaptain", "maxCount": 6, "probability": 100 }, - { "name": "Minotaur", "maxCount": 6, "probability": 100 }, - { "name": "MinotaurScout", "maxCount": 6, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 4, "probability": 100 } + { "name": "Scorpion", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "b8196e8f-9f8f-4362-b188-6bb06dbfea9d", + "name": "Spawner (407)", + "location": [336, 1891, 0], + "map": "Malas", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [ + { "name": "Minotaur", "maxCount": 5, "probability": 100 }, + { "name": "MinotaurScout", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "4a9e68d9-d730-402c-96cc-979f6d08fcfa", "name": "Spawner (407)", "location": [357, 1898, 5], @@ -82,83 +109,27 @@ "team": 0, "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "Rend", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Rend", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "50f241e9-15cf-4561-9a1c-0fe8522d01cd", + "type": "Spawner", + "guid": "f5972024-9852-4b8d-96a2-bf1afc05c588", "name": "Spawner (407)", - "location": [415, 1897, 5], + "location": [346, 1959, 0], "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Pyre", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "613eb20c-bb8e-4f16-8561-bd839851b463", - "name": "Spawner (407)", - "location": [390, 1883, 0], - "map": "Malas", - "count": 8, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 10, "entries": [ - { "name": "Eagle", "maxCount": 8, "probability": 100 }, - { "name": "Cat", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 8, "probability": 100 } + { "name": "Squirrel", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "a451b30c-88b8-411d-99b6-3a66cdfd0cd5", - "name": "Spawner (407)", - "location": [355, 1918, 0], - "map": "Malas", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Reptalon", "maxCount": 6, "probability": 100 }, - { "name": "Minotaur", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aaca3ab0-5ea8-4af5-ac5f-c037d41b00fa", - "name": "Spawner (407)", - "location": [347, 1888, 5], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "flurry", "maxCount": 4, "probability": 100 }, - { "name": "grim", "maxCount": 4, "probability": 100 }, - { "name": "mistral", "maxCount": 4, "probability": 100 }, - { "name": "tempest", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "af7b61de-563e-4559-9905-d5113f0e7c02", "name": "Spawner (407)", "location": [392, 1917, 0], @@ -180,41 +151,43 @@ ] }, { - "$type": "Spawner", - "guid": "b8196e8f-9f8f-4362-b188-6bb06dbfea9d", + "type": "Spawner", + "guid": "613eb20c-bb8e-4f16-8561-bd839851b463", "name": "Spawner (407)", - "location": [336, 1891, 0], + "location": [390, 1883, 0], "map": "Malas", - "count": 5, + "count": 8, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, - "walkingRange": 12, + "homeRange": 10, + "walkingRange": 10, "entries": [ - { "name": "Minotaur", "maxCount": 5, "probability": 100 }, - { "name": "MinotaurScout", "maxCount": 5, "probability": 100 } + { "name": "Eagle", "maxCount": 8, "probability": 100 }, + { "name": "Cat", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "b9da636f-3e98-4924-a7da-8ac3d0141e5f", + "type": "Spawner", + "guid": "a451b30c-88b8-411d-99b6-3a66cdfd0cd5", "name": "Spawner (407)", - "location": [1742, 990, -84], + "location": [355, 1918, 0], "map": "Malas", - "count": 10, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, - "walkingRange": 12, + "homeRange": 20, + "walkingRange": 20, "entries": [ - { "name": "Scorpion", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 4, "probability": 100 } + { "name": "Reptalon", "maxCount": 6, "probability": 100 }, + { "name": "Minotaur", "maxCount": 6, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c1e202ec-c6ba-4c76-8a26-29767ae27907", "name": "Spawner (407)", "location": [346, 1910, 0], @@ -231,40 +204,27 @@ ] }, { - "$type": "Spawner", - "guid": "ebe544f9-9a54-4822-bb37-ee11ba1059db", + "type": "Spawner", + "guid": "46f54372-6b9c-4e33-9f35-99fc01204b59", "name": "Spawner (407)", - "location": [384, 1932, 10], + "location": [427, 1923, 5], "map": "Malas", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Minotaur", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eea9dc24-23e1-4198-8dbc-b22bddb5a71c", - "name": "Spawner (407)", - "location": [1740, 990, -80], - "map": "Malas", - "count": 1, + "count": 10, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 15, "walkingRange": 15, "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } + { "name": "MinotaurCaptain", "maxCount": 6, "probability": 100 }, + { "name": "Minotaur", "maxCount": 6, "probability": 100 }, + { "name": "MinotaurScout", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ef581668-95b6-4e1b-b43f-06e805e2e646", "name": "Spawner (407)", "location": [348, 1948, 0], @@ -284,20 +244,50 @@ ] }, { - "$type": "Spawner", - "guid": "f5972024-9852-4b8d-96a2-bf1afc05c588", + "type": "Spawner", + "guid": "50f241e9-15cf-4561-9a1c-0fe8522d01cd", "name": "Spawner (407)", - "location": [346, 1959, 0], + "location": [415, 1897, 5], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Pyre", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aaca3ab0-5ea8-4af5-ac5f-c037d41b00fa", + "name": "Spawner (407)", + "location": [347, 1888, 5], "map": "Malas", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, + "homeRange": 20, + "walkingRange": 20, "entries": [ - { "name": "Squirrel", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 } + { "name": "flurry", "maxCount": 4, "probability": 100 }, + { "name": "grim", "maxCount": 4, "probability": 100 }, + { "name": "mistral", "maxCount": 4, "probability": 100 }, + { "name": "tempest", "maxCount": 4, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "3d9f4f90-3f61-4dc1-9715-0d614c34c99c", + "name": "Spawner (407)", + "location": [382, 1913, 0], + "map": "Malas", + "count": 0, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Meraktus", "maxCount": 0, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/post-uoml/malas/North.json b/Distribution/Data/Spawns/post-uoml/malas/North.json index e81a2a547..4bf4a8c06 100644 --- a/Distribution/Data/Spawns/post-uoml/malas/North.json +++ b/Distribution/Data/Spawns/post-uoml/malas/North.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "b37c3d38-2d53-4d2d-a8aa-a2e4926a29c6", "name": "Spawner (402)", "location": [1119, 530, -90], @@ -19,7 +19,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e1ba3ada-d1fd-44fb-83b8-82e0e1fcf94f", "name": "Spawner (402)", "location": [1674, 659, -84], @@ -40,7 +40,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "099efd1a-520c-4e6b-bb39-552def69e2df", "name": "Spawner (402)", "location": [1434, 607, -89], @@ -61,7 +61,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c9c80a39-40db-4cb9-a3c7-243957ecdd39", "name": "Spawner (402)", "location": [1194, 638, -84], @@ -82,7 +82,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0335054c-d38b-4c5a-9fc6-c04c42ba9de1", "name": "Spawner (402)", "location": [920, 356, -85], @@ -101,5 +101,348 @@ { "name": "GreatHart", "maxCount": 1, "probability": 100 }, { "name": "JackRabbit", "maxCount": 4, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "6ef4d890-5038-4d39-977c-5eb15448543a", + "name": "Spawner (402)", + "location": [1659, 112, -80], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7be1139-619a-4d84-a98a-8ea04c550827", + "name": "Spawner (402)", + "location": [1869, 480, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41a269ac-0492-4a16-bbc3-449c7efa9ef9", + "name": "Spawner (402)", + "location": [2121, 597, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1e3857a-952a-47c2-9311-d6bcc3806c45", + "name": "Spawner (402)", + "location": [1980, 660, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2af6c55-d320-4255-a85f-c694f8d9886b", + "name": "Spawner (402)", + "location": [2233, 555, -90], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b85a0fee-612e-4bd9-8489-28fbcdc60ef5", + "name": "Spawner (402)", + "location": [1137, 223, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b48ebb7a-8bfc-4c48-a0ab-f82ca41e38ac", + "name": "Spawner (402)", + "location": [1381, 340, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "67b52f00-d808-4db4-91ca-9313330d0b40", + "name": "Spawner (402)", + "location": [1710, 403, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63d80004-20f7-48d8-bcc8-639a8d83c099", + "name": "Spawner (402)", + "location": [766, 114, -90], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be2f76e8-a03b-42b9-b39c-c641f03ee091", + "name": "Spawner (402)", + "location": [1206, 191, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abaa8a2d-9997-4bff-b033-a9dfa78d46f9", + "name": "Spawner (402)", + "location": [2030, 500, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 70, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "135a20a6-0885-4a61-9a55-a01593f3e874", + "name": "Spawner (402)", + "location": [1482, 347, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 60, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3f940e1-3225-416f-a9ba-717b28fb944a", + "name": "Spawner (402)", + "location": [1023, 192, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2db6869c-b590-4f82-a5eb-edb7afdd18e4", + "name": "Spawner (402)", + "location": [1274, 300, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dec6f03f-a79d-414a-8b8b-c46af723371a", + "name": "Spawner (402)", + "location": [1587, 386, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c628980-8a6f-45e1-bdc8-ed7ea16765fd", + "name": "Spawner (402)", + "location": [2205, 145, -90], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Unicorn", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2efda65d-ebc3-4251-b6a3-356d42cca581", + "name": "Spawner (402)", + "location": [2291, 295, -90], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [{ "name": "GoreFiend", "maxCount": 6, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/malas/OrcForts.json b/Distribution/Data/Spawns/post-uoml/malas/OrcForts.json similarity index 92% rename from Distribution/Data/Spawns/shared/malas/OrcForts.json rename to Distribution/Data/Spawns/post-uoml/malas/OrcForts.json index 3197730a0..d0c3cd6a3 100644 --- a/Distribution/Data/Spawns/shared/malas/OrcForts.json +++ b/Distribution/Data/Spawns/post-uoml/malas/OrcForts.json @@ -1,6 +1,34 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "f4837b68-40f5-42db-b907-f4fe65248cf9", + "name": "Spawner (403)", + "location": [1343, 1248, -90], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Orc", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9a42ced3-e2d2-4d65-bb78-3903a4092c9f", + "name": "Spawner (403)", + "location": [1343, 1196, -90], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Orc", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "0cb005f6-2405-41d4-b9b6-9ad8c75a6851", "name": "Spawner (403)", "location": [1372, 1225, -90], @@ -20,131 +48,7 @@ ] }, { - "$type": "Spawner", - "guid": "2abcbbd3-3842-46f6-95f1-41b743bb7083", - "name": "Spawner (403)", - "location": [1364, 596, -86], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:15:00", - "team": 0, - "homeRange": 4, - "walkingRange": 15, - "entries": [ - { "name": "Orc", "maxCount": 2, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "415080ef-9b67-4529-8651-f82d4e0fba5c", - "name": "Spawner (403)", - "location": [1205, 700, -88], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:15:00", - "team": 0, - "homeRange": 4, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 2, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "778c0ad2-e39e-4c61-9b1c-e45b6a9b554e", - "name": "Spawner (403)", - "location": [1257, 1326, -90], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:15:00", - "team": 0, - "homeRange": 4, - "walkingRange": 15, - "entries": [ - { "name": "Orc", "maxCount": 2, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9a42ced3-e2d2-4d65-bb78-3903a4092c9f", - "name": "Spawner (403)", - "location": [1343, 1196, -90], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:15:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b187d6bd-fe00-43c8-906d-f18ee330f432", - "name": "Spawner (403)", - "location": [911, 194, -79], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:15:00", - "team": 0, - "homeRange": 4, - "walkingRange": 15, - "entries": [ - { "name": "Orc", "maxCount": 2, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c4a2ba68-4d62-480c-a9a9-b893f56a1a5d", - "name": "Spawner (403)", - "location": [1598, 1819, -110], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:15:00", - "team": 0, - "homeRange": 4, - "walkingRange": 15, - "entries": [ - { "name": "Orc", "maxCount": 2, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c93264b1-19bd-47d9-ae8d-261e8d36fc30", - "name": "Spawner (403)", - "location": [1665, 356, -50], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:15:00", - "team": 0, - "homeRange": 4, - "walkingRange": 25, - "entries": [ - { "name": "Orc", "maxCount": 2, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "ea44d8fe-27b9-4650-9c22-fed0630e5cc3", "name": "Spawner (403)", "location": [1322, 1225, -90], @@ -164,19 +68,111 @@ ] }, { - "$type": "Spawner", - "guid": "f4837b68-40f5-42db-b907-f4fe65248cf9", + "type": "Spawner", + "guid": "b187d6bd-fe00-43c8-906d-f18ee330f432", "name": "Spawner (403)", - "location": [1343, 1248, -90], + "location": [911, 194, -79], "map": "Malas", - "count": 2, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:15:00", "team": 0, "homeRange": 4, - "walkingRange": 4, + "walkingRange": 15, "entries": [ - { "name": "Orc", "maxCount": 2, "probability": 100 } + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c93264b1-19bd-47d9-ae8d-261e8d36fc30", + "name": "Spawner (403)", + "location": [1665, 356, -50], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 25, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2abcbbd3-3842-46f6-95f1-41b743bb7083", + "name": "Spawner (403)", + "location": [1364, 596, -86], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "415080ef-9b67-4529-8651-f82d4e0fba5c", + "name": "Spawner (403)", + "location": [1205, 700, -88], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "778c0ad2-e39e-4c61-9b1c-e45b6a9b554e", + "name": "Spawner (403)", + "location": [1257, 1326, -90], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4a2ba68-4d62-480c-a9a9-b893f56a1a5d", + "name": "Spawner (403)", + "location": [1598, 1819, -110], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/post-uoml/malas/South.json b/Distribution/Data/Spawns/post-uoml/malas/South.json index a5c303d5e..6a2e0ce7d 100644 --- a/Distribution/Data/Spawns/post-uoml/malas/South.json +++ b/Distribution/Data/Spawns/post-uoml/malas/South.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "27bdb175-d7c0-49c9-b389-b26fe028e58a", "name": "Spawner (404)", "location": [1920, 1124, -90], @@ -23,7 +23,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "56c3146a-f634-4ee8-9dd8-65e7fb98f8e2", "name": "Spawner (404)", "location": [1920, 1124, -90], @@ -52,7 +52,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5286c49e-21e5-407a-a363-8ecf2ff5eafe", "name": "Spawner (404)", "location": [2266, 1218, -85], @@ -74,7 +74,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "695dcc16-4e4d-4b93-be25-05cf6ddf199c", "name": "Spawner (404)", "location": [2267, 1218, -85], @@ -103,7 +103,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bb4e40ab-96e4-4061-85e9-8011b42ed020", "name": "Spawner (404)", "location": [2303, 1258, -86], @@ -125,7 +125,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3e41feda-8300-4fa4-ba72-6752d37486cd", "name": "Spawner (404)", "location": [2304, 1258, -86], @@ -146,7 +146,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4b59baa2-7ce0-40a6-9c68-c6f2210f0869", "name": "Spawner (404)", "location": [1641, 1797, -110], @@ -165,7 +165,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9aa39353-19ab-480a-9e96-6a7ba1485251", "name": "Spawner (404)", "location": [1899, 1609, -110], @@ -184,7 +184,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6216b5d7-8c6f-420b-b615-54edf74cb173", "name": "Spawner (404)", "location": [1741, 1487, -110], @@ -203,7 +203,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3f641570-9b47-44a3-859a-e9ab15dd4da8", "name": "Spawner (404)", "location": [1837, 1803, -110], @@ -222,7 +222,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "18a04411-aa44-46c4-9476-a77365090afa", "name": "Spawner (404)", "location": [1823, 1799, -90], @@ -231,10 +231,7 @@ "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 1819, "y": 1795, "z": -90 }, - "end": { "x": 1827, "y": 1803, "z": -75 } - }, + "homeRange": 4, "walkingRange": 15, "entries": [ { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, @@ -246,7 +243,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4a2b88ce-b146-439d-917c-3f056775012e", "name": "Spawner (404)", "location": [1821, 1798, -110], @@ -255,10 +252,7 @@ "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 1817, "y": 1794, "z": -110 }, - "end": { "x": 1825, "y": 1802, "z": -95 } - }, + "homeRange": 4, "walkingRange": 15, "entries": [ { "name": "GiantRat", "maxCount": 4, "probability": 100 }, @@ -268,7 +262,27 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "6bbb82e4-b0d1-4a23-a9da-5d35911ce762", + "name": "Spawner (404)", + "location": [2231, 1605, -95], + "map": "Malas", + "count": 33, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 160, + "walkingRange": 160, + "entries": [ + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "SandVortex", "maxCount": 6, "probability": 100 }, + { "name": "Scorpion", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "d91c089c-551a-4333-adbb-4ff82e82908f", "name": "Spawner (404)", "location": [1466, 1418, -90], @@ -288,7 +302,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "01e68ebc-8521-44bd-ada3-a96cf6dc7b8e", "name": "Spawner (404)", "location": [1477, 1241, -85], @@ -308,7 +322,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "7a14417b-5b9c-4c46-978d-4f9aaf4ab2d8", "name": "Spawner (404)", "location": [1347, 1739, -110], @@ -328,7 +342,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c5036af7-93ad-41b3-a29b-48b1e60393fe", "name": "Spawner (404)", "location": [1327, 1146, -90], @@ -347,7 +361,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6d49d6f6-4054-4b02-a319-8be526aaefcf", "name": "Spawner (404)", "location": [1256, 1221, -90], @@ -366,7 +380,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "aa9e1655-d9ed-4fdc-99c9-ef04a9234b9b", "name": "Spawner (404)", "location": [1345, 1314, -85], @@ -385,7 +399,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fad094bd-915f-49d1-a0c6-486029d25506", "name": "Spawner (404)", "location": [943, 1150, -90], @@ -396,12 +410,10 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "Brigand", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Brigand", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c2cdce00-aaf5-40cc-b1f5-28c0d1364eff", "name": "Spawner (404)", "location": [900, 1494, -90], @@ -420,7 +432,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c5657dd4-3bbc-4557-a5bb-9a111c7e6763", "name": "Spawner (404)", "location": [829, 1474, -85], @@ -439,7 +451,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "eb36fa8a-c49d-42b2-ac5a-246391bdb716", "name": "Spawner (404)", "location": [989, 1419, -82], @@ -458,7 +470,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e9f2158f-8b22-46cd-94ce-924a22b6dd65", "name": "Spawner (404)", "location": [913, 1335, -90], @@ -477,7 +489,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "20e95aca-18d5-4862-a0cd-72e65f188dc4", "name": "Spawner (404)", "location": [881, 927, -90], @@ -488,12 +500,10 @@ "team": 0, "homeRange": 15, "walkingRange": 30, - "entries": [ - { "name": "Pixie", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Pixie", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "51a28e73-74fd-454b-8c02-5aeacd7f041b", "name": "Spawner (404)", "location": [882, 1000, -90], @@ -504,12 +514,10 @@ "team": 0, "homeRange": 15, "walkingRange": 40, - "entries": [ - { "name": "Pixie", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Pixie", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "83ded31b-46a4-4d33-b340-abb593f01ea9", "name": "Spawner (404)", "location": [989, 1420, -82], @@ -520,12 +528,10 @@ "team": 0, "homeRange": 15, "walkingRange": 40, - "entries": [ - { "name": "Pixie", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Pixie", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0109ed70-dc93-4c76-9647-ef3565315a1b", "name": "Spawner (404)", "location": [1455, 1445, -90], @@ -536,12 +542,10 @@ "team": 0, "homeRange": 10, "walkingRange": 30, - "entries": [ - { "name": "Pixie", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Pixie", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c3fa83a1-72e9-440b-9ddf-d8733e54e59f", "name": "Spawner (404)", "location": [1429, 1549, -110], @@ -552,12 +556,10 @@ "team": 0, "homeRange": 15, "walkingRange": 25, - "entries": [ - { "name": "Pixie", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Pixie", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f18bf6f0-7dde-43f7-81f5-ac26393e48c0", "name": "Spawner (404)", "location": [1305, 1095, -90], @@ -568,12 +570,10 @@ "team": 0, "homeRange": 15, "walkingRange": 30, - "entries": [ - { "name": "Pixie", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Pixie", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b49f301b-9378-489a-b999-51785a3307e9", "name": "Spawner (404)", "location": [790, 948, -90], @@ -592,7 +592,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "74e793f9-ab88-4c51-b713-203fe3ec3828", "name": "Spawner (404)", "location": [766, 1364, -90], @@ -611,7 +611,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d5aa4b2a-02bd-4fab-ad78-6e0a2cd7c575", "name": "Spawner (404)", "location": [656, 1431, -90], @@ -630,7 +630,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ff590783-b876-429c-aaad-e6096a86ea7e", "name": "Spawner (404)", "location": [805, 1232, -90], @@ -649,7 +649,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "eeaacf1f-a83f-4ba0-86f3-1f39aff6a3ec", "name": "Spawner (404)", "location": [672, 1237, -90], @@ -668,7 +668,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d0fe5086-d802-43d1-b62f-a81dbc603ca1", "name": "Spawner (404)", "location": [684, 1109, -90], @@ -687,7 +687,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c30d468f-b35f-4aa5-a13f-773518810151", "name": "Spawner (404)", "location": [1079, 1440, -90], @@ -706,7 +706,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "cf2df3ea-792b-4d9e-9723-b094b5654dcf", "name": "Spawner (404)", "location": [966, 1213, -90], @@ -725,7 +725,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6651b803-1285-427b-93a5-c127957915bc", "name": "Spawner (404)", "location": [925, 1105, -90], @@ -744,7 +744,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bfcc0baf-f539-440f-96b6-911679afc732", "name": "Spawner (404)", "location": [1049, 1332, -90], @@ -763,7 +763,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "445e307b-72d4-4d2e-9284-14938e4dab15", "name": "Spawner (404)", "location": [1086, 1067, -90], @@ -782,7 +782,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "375e8bd1-6823-442a-a125-2e2acf53edd9", "name": "Spawner (404)", "location": [1062, 988, -90], diff --git a/Distribution/Data/Spawns/post-uoml/malas/Vendors.json b/Distribution/Data/Spawns/post-uoml/malas/Vendors.json index 4efdc8afa..f51e7f37e 100644 --- a/Distribution/Data/Spawns/post-uoml/malas/Vendors.json +++ b/Distribution/Data/Spawns/post-uoml/malas/Vendors.json @@ -1,6 +1,470 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "99cde945-5b9c-4cff-924f-a59142bcbdb9", + "name": "Spawner (405)", + "location": [989, 520, -50], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bd799ab-c7fb-446e-b6cb-580d1fe2f7fb", + "name": "Spawner (405)", + "location": [976, 512, -50], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f62df843-74a7-4bb0-a101-53d1dc4cb806", + "name": "Spawner (405)", + "location": [976, 527, -50], + "map": "Malas", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79aa68b8-d2bd-4a4d-ad84-422588aeaaff", + "name": "Spawner (405)", + "location": [1016, 514, -70], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "KeeperOfChivalry", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e08eb38c-0b25-4387-8999-f9d8a0a877aa", + "name": "Spawner (405)", + "location": [961, 517, -70], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "KeeperOfChivalry", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a4200c02-8035-48bc-b79d-8b45225ffd4f", + "name": "Spawner (405)", + "location": [1004, 527, -50], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "HolyMage", "maxCount": 1, "probability": 100 }, + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1bda852-fee4-45ee-aa79-630980bc44e9", + "name": "Spawner (405)", + "location": [1003, 512, -50], + "map": "Malas", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df531ac9-d2b0-48ae-bc19-dc043d6af3de", + "name": "Spawner (405)", + "location": [993, 511, -50], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 }, + { "name": "Jeweler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54deea77-bc1e-446b-8274-ea23bddb369e", + "name": "Spawner (405)", + "location": [989, 527, -50], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7a10d380-53e3-48c9-9c5b-07a3c215032a", + "name": "Spawner (405)", + "location": [1027, 494, -70], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3d301c77-1ade-4823-b2ff-f1d4e0ed2bf0", + "name": "Spawner (405)", + "location": [1029, 520, -55], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d9c6323-7da9-4e12-861a-76fd30b3e3dc", + "name": "Spawner (405)", + "location": [950, 520, -55], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55fab330-6d2a-4823-a666-3dfa33dceeb6", + "name": "Spawner (405)", + "location": [1977, 1365, -80], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81e4af4e-3310-4864-8517-a032d487716f", + "name": "Spawner (405)", + "location": [1992, 1315, -90], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ad9f47f-17bc-4790-9a84-1e9aee66478f", + "name": "Spawner (405)", + "location": [2011, 1326, -80], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa9dbd50-e0ca-4206-a197-0fe85a349900", + "name": "Spawner (405)", + "location": [2017, 1356, -90], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4f71d7e-8c76-47db-a1c1-b81c4484741b", + "name": "Spawner (405)", + "location": [2027, 1353, -90], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dcbbc92-14ac-4798-8753-41aa3b6b2450", + "name": "Spawner (405)", + "location": [2023, 1379, -80], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a0c83d13-2354-494f-9535-d4e587cc7a6b", + "name": "Spawner (405)", + "location": [2025, 1387, -80], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d69c950-70d4-48c5-8729-be8848330c1a", + "name": "Spawner (405)", + "location": [2045, 1397, -90], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a263ccfc-65e1-4181-93b6-6e1c7914f5b8", + "name": "Spawner (405)", + "location": [2048, 1343, -85], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9347985d-23a4-4044-9a5c-79939c9f7df0", + "name": "Spawner (405)", + "location": [2037, 1311, -85], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eb50b13f-dd98-42c1-bfa0-2599bdc85de3", + "name": "Spawner (405)", + "location": [2066, 1282, -80], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ed338dc-51c1-40b3-a91a-7ebd8d91131b", + "name": "Spawner (405)", + "location": [2060, 1283, -80], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6de9f30-1ee9-4914-853c-be075bd119ce", + "name": "Spawner (405)", + "location": [2083, 1322, -80], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72a7f47d-d4cb-4530-9ba6-3878482279b2", + "name": "Spawner (405)", + "location": [2078, 1327, -80], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0db7d5e9-beaa-43a2-ac46-2802ac962fcb", + "name": "Spawner (405)", + "location": [2068, 1372, -75], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b7226aea-9fd0-4079-8e21-70a7a443d0b2", + "name": "Spawner (405)", + "location": [1056, 1434, -85], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "befc0201-8bec-432e-8ced-3ee5ceb57d09", "name": "Spawner (405)", "location": [1050, 1434, -85], @@ -18,7 +482,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d8e03ce8-cd76-433e-af76-0c0767290705", "name": "Spawner (405)", "location": [1042, 1444, -90], diff --git a/Distribution/Data/Spawns/post-uoml/termur/Abyss.json b/Distribution/Data/Spawns/post-uoml/termur/Abyss.json index 237b4da35..c1ab986d1 100644 --- a/Distribution/Data/Spawns/post-uoml/termur/Abyss.json +++ b/Distribution/Data/Spawns/post-uoml/termur/Abyss.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "35fb2ebe-b2e6-4a64-af3a-ce1eaacd9a54", "name": "Spawner (601)", "location": [795, 753, 53], @@ -11,12 +11,10 @@ "team": 0, "homeRange": 15, "walkingRange": 15, - "entries": [ - { "name": "Hydra", "maxCount": 8, "probability": 100 } - ] + "entries": [{ "name": "Hydra", "maxCount": 8, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "8d0bc27a-c900-4d7f-8811-a52f386b6d0c", "name": "Spawner (601)", "location": [745, 739, 16], @@ -27,12 +25,10 @@ "team": 0, "homeRange": 5, "walkingRange": 5, - "entries": [ - { "name": "MaddeningHorror", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "MaddeningHorror", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f7e9ab99-b970-4be1-881b-9e7ef1af0d01", "name": "Spawner (601)", "location": [749, 475, -17], @@ -43,12 +39,10 @@ "team": 0, "homeRange": 20, "walkingRange": 20, - "entries": [ - { "name": "SlasherOfVeils", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "SlasherOfVeils", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "dd5aea6e-c13b-40f4-949f-9f41eac96589", "name": "Spawner (601)", "location": [326, 159, 20], @@ -59,12 +53,10 @@ "team": 0, "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "StygianDragon", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "StygianDragon", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5c81b759-1add-4e8a-b151-420aa778e236", "name": "Spawner (601)", "location": [716, 720, -11], @@ -84,7 +76,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a02bf178-9eb0-48ff-ae18-d06958f798a8", "name": "Spawner (601)", "location": [992, 337, 9], @@ -104,7 +96,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3929df23-08d5-4650-89a1-6cb025fddbac", "name": "Spawner (601)", "location": [982, 491, -12], @@ -122,7 +114,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "941cbed7-bf9a-448d-ae6a-f741459f50d1", "name": "Spawner (601)", "location": [981, 491, -12], @@ -133,12 +125,10 @@ "team": 0, "homeRange": 18, "walkingRange": 18, - "entries": [ - { "name": "ClockworkScorpion", "maxCount": 16, "probability": 100 } - ] + "entries": [{ "name": "ClockworkScorpion", "maxCount": 16, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "517bfdd3-f625-4e1c-af4b-8467810e6e46", "name": "Spawner (601)", "location": [919, 502, -12], @@ -156,7 +146,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "592811d2-8196-4006-a774-5658b4df3ecb", "name": "Spawner (601)", "location": [918, 502, -12], @@ -167,12 +157,10 @@ "team": 0, "homeRange": 20, "walkingRange": 20, - "entries": [ - { "name": "ClanRibbonPlagueRat", "maxCount": 15, "probability": 100 } - ] + "entries": [{ "name": "ClanRibbonPlagueRat", "maxCount": 15, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "8ff19011-9cc1-4aaf-a795-1867c6667123", "name": "Spawner (601)", "location": [949, 555, -14], @@ -190,7 +178,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1b0b5739-68fc-4048-b2be-10da4dee13b8", "name": "Spawner (601)", "location": [950, 555, -14], @@ -201,12 +189,10 @@ "team": 0, "homeRange": 20, "walkingRange": 20, - "entries": [ - { "name": "ClanScratchSavageWolf", "maxCount": 18, "probability": 100 } - ] + "entries": [{ "name": "ClanScratchSavageWolf", "maxCount": 18, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c33dadb8-e340-4fc6-9944-627054cf96f6", "name": "Spawner (601)", "location": [974, 161, -11], @@ -229,7 +215,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "dcc3ba35-0e14-48fe-8a1b-8a7e5b3e4c03", "name": "Spawner (601)", "location": [581, 815, -45], @@ -252,7 +238,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6dfe8446-6623-4133-8990-6d11813b2b0d", "name": "Spawner (601)", "location": [526, 766, -92], @@ -276,7 +262,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6d71c735-e0cb-414d-b687-10c1a4192856", "name": "Spawner (601)", "location": [536, 657, 8], @@ -298,7 +284,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "29d4421f-924d-47c3-abe5-45fe5e1be9a5", "name": "Spawner (601)", "location": [601, 905, -60], @@ -321,7 +307,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "7dac51b3-7c0a-4f22-8652-7e29ba875fee", "name": "Spawner (601)", "location": [818, 927, -15], @@ -332,12 +318,10 @@ "team": 0, "homeRange": 15, "walkingRange": 15, - "entries": [ - { "name": "Medusa", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Medusa", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c64053fc-272a-4fdd-9c6c-4f05a1ec02be", "name": "Spawner (601)", "location": [684, 579, -15], @@ -357,7 +341,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a72aa352-96b2-4163-83c0-de180a8fb7e8", "name": "Spawner (601)", "location": [440, 708, 24], @@ -376,7 +360,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "875d5be5-9156-4e42-a41a-973aef764aee", "name": "Spawner (601)", "location": [674, 828, -109], @@ -399,7 +383,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "11551b29-0ee5-4fae-8d9b-2fd60465b032", "name": "Spawner (601)", "location": [887, 277, 3], diff --git a/Distribution/Data/Spawns/post-uoml/termur/TerMur.json b/Distribution/Data/Spawns/post-uoml/termur/TerMur.json index 609bc2e94..734239b0b 100644 --- a/Distribution/Data/Spawns/post-uoml/termur/TerMur.json +++ b/Distribution/Data/Spawns/post-uoml/termur/TerMur.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "910e07be-fd79-4608-9990-5b860c14f95f", "name": "Spawner (602)", "location": [1109, 3744, -34], @@ -17,7 +17,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e93d2519-a992-406d-84db-c246d4c9e68c", "name": "Spawner (602)", "location": [1088, 3732, -43], @@ -28,12 +28,10 @@ "team": 0, "homeRange": 5, "walkingRange": 5, - "entries": [ - { "name": "TrapdoorSpider", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "TrapdoorSpider", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5de9c1d3-274e-418f-8d1f-01357fa82fa4", "name": "Spawner (602)", "location": [1126, 3714, -43], @@ -44,12 +42,10 @@ "team": 0, "homeRange": 5, "walkingRange": 5, - "entries": [ - { "name": "TrapdoorSpider", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "TrapdoorSpider", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "88ee3368-50a1-458c-9c99-8fb7ceddf079", "name": "Spawner (602)", "location": [1134, 3483, -42], @@ -71,7 +67,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "dd1b50db-06cc-4ab8-b1ae-7eb6d7ebeb03", "name": "Spawner (602)", "location": [1118, 3416, -42], @@ -92,7 +88,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "17ea295b-36bd-43e1-aa63-81d61bb7db98", "name": "Spawner (602)", "location": [1112, 3624, -45], @@ -109,7 +105,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "38fa4d5b-ab23-455a-9b32-3cfae24dacf1", "name": "Spawner (602)", "location": [1036, 3670, 0], @@ -126,7 +122,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "08c6d785-3e69-4343-a48e-54e876e09863", "name": "Spawner (602)", "location": [1012, 3652, -30], @@ -143,7 +139,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ae84b446-e1a6-4df6-84b7-487218c2ffc0", "name": "Spawner (602)", "location": [996, 3406, -43], @@ -168,7 +164,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f6d1d4f5-6a84-430b-bbef-8b248d6b7c34", "name": "Spawner (602)", "location": [851, 3642, -43], @@ -193,7 +189,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1faffedd-40e4-4dcf-b944-418dbd7f1300", "name": "Spawner (602)", "location": [934, 3564, -43], @@ -218,7 +214,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f76c2cff-e0c9-4b37-a7f8-9463975f34ac", "name": "Spawner (602)", "location": [965, 3489, -43], @@ -243,7 +239,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "56b271ca-0fcc-4ea4-9c2d-7b5aeec0d532", "name": "Spawner (602)", "location": [858, 3532, -43], @@ -260,7 +256,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3b6298d3-c9f3-40df-af03-20af1ed36ad5", "name": "Spawner (602)", "location": [981, 3318, -42], @@ -277,7 +273,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "869aeec5-52eb-4041-ab43-5d943d05055f", "name": "Spawner (602)", "location": [881, 3344, -43], @@ -294,7 +290,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "46785472-bc5c-4563-8a5b-2ea1c0f9f465", "name": "Spawner (602)", "location": [1073, 3322, -42], @@ -317,7 +313,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5f076796-65bf-4d41-88f3-78dacfab04b8", "name": "Spawner (602)", "location": [981, 3574, -43], @@ -334,7 +330,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "55507c03-3ae8-43ba-a2c5-7e8ecd7a3457", "name": "Spawner (602)", "location": [909, 3235, 38], @@ -357,7 +353,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6d62246c-b5a5-4a2a-8f8a-3a9d54387e8a", "name": "Spawner (602)", "location": [802, 3231, 40], @@ -380,7 +376,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "572149ff-9542-42c1-92b9-99b71d8f2579", "name": "Spawner (602)", "location": [679, 3248, 38], @@ -400,7 +396,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c05a8e56-f166-414e-babc-dcc1d14221c4", "name": "Spawner (602)", "location": [588, 3267, 37], @@ -423,7 +419,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1a1cad72-ebdf-4491-a623-66d87bc45538", "name": "Spawner (602)", "location": [521, 3355, 38], @@ -444,7 +440,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e23908dc-858c-4ec1-a41a-474fd7e9730d", "name": "Spawner (602)", "location": [511, 3273, 36], @@ -462,7 +458,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "955417f2-d918-4c43-93a6-346ca6fb4bbc", "name": "Spawner (602)", "location": [547, 3421, 37], @@ -483,7 +479,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ebbbc910-1d0f-4180-8e7a-544d819906b8", "name": "Spawner (602)", "location": [571, 3506, 38], @@ -501,7 +497,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "153e658a-818b-4ed3-80d0-8b5f99193b6e", "name": "Spawner (602)", "location": [1169, 3338, -42], @@ -523,7 +519,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "18cef512-871f-4ff0-b21d-54e22e6197bc", "name": "Spawner (602)", "location": [689, 3711, -43], @@ -543,7 +539,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "8745bf7c-2b8b-45ae-b992-7de9c58d14fc", "name": "Spawner (602)", "location": [761, 3650, -43], @@ -563,7 +559,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "97c2ed7b-69c7-43db-8ccb-8591ecd90316", "name": "Spawner (602)", "location": [616, 3684, -43], @@ -581,7 +577,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "990f3875-b9e4-4060-8e14-7f2c4b9a69a2", "name": "Spawner (602)", "location": [668, 3831, -31], @@ -600,7 +596,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0b5d810e-182e-4d7c-9a79-2dfb3ba8e5f3", "name": "Spawner (602)", "location": [680, 3788, -43], @@ -611,12 +607,10 @@ "team": 0, "homeRange": 5, "walkingRange": 5, - "entries": [ - { "name": "TrapdoorSpider", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "TrapdoorSpider", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "8151255d-cede-4c52-bacc-fc3a1a434c45", "name": "Spawner (602)", "location": [760, 3810, -42], @@ -637,7 +631,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1017634b-fad7-41ee-a13c-ec15dc0e5ae9", "name": "Spawner (602)", "location": [826, 3762, -43], @@ -659,7 +653,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e2817ac3-8c77-4901-af5f-d51f0ceb7f7d", "name": "Spawner (602)", "location": [795, 3827, -19], @@ -670,12 +664,10 @@ "team": 0, "homeRange": 5, "walkingRange": 5, - "entries": [ - { "name": "TrapdoorSpider", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "TrapdoorSpider", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "54228f45-e935-4df1-8d50-e15ec557d110", "name": "Spawner (602)", "location": [799, 3863, 48], @@ -697,7 +689,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "91a9c0fa-51f3-4726-aeb1-25a03911fffa", "name": "Spawner (602)", "location": [763, 3991, -42], @@ -718,7 +710,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "866bde45-dd02-4eef-b6c4-143bf3b6c31a", "name": "Spawner (602)", "location": [1129, 3163, -42], @@ -738,7 +730,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "08ce21d5-67cc-46c7-a827-68048f38e3ef", "name": "Spawner (602)", "location": [687, 3917, -43], @@ -749,12 +741,10 @@ "team": 0, "homeRange": 5, "walkingRange": 5, - "entries": [ - { "name": "TrapdoorSpider", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "TrapdoorSpider", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "63140eed-b7f0-4ca9-87f5-5caeff3b6607", "name": "Spawner (602)", "location": [641, 3922, -43], @@ -775,7 +765,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5216666c-6c47-4d72-9818-510a88ea6edc", "name": "Spawner (602)", "location": [1065, 2996, 74], @@ -795,7 +785,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "089d3262-1c31-4f80-aa13-d2d5383f0535", "name": "Spawner (602)", "location": [1099, 3010, 50], @@ -806,12 +796,10 @@ "team": 0, "homeRange": 3, "walkingRange": 3, - "entries": [ - { "name": "ToxicSlith", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "ToxicSlith", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c1db00c8-9759-4f55-8ac4-f7557a6b9242", "name": "Spawner (602)", "location": [1053, 2971, 50], @@ -822,12 +810,10 @@ "team": 0, "homeRange": 3, "walkingRange": 3, - "entries": [ - { "name": "ToxicSlith", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "ToxicSlith", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a7437a00-ed2e-4acc-9375-b234ceffd92a", "name": "Spawner (602)", "location": [1062, 3026, 50], @@ -838,12 +824,10 @@ "team": 0, "homeRange": 3, "walkingRange": 3, - "entries": [ - { "name": "ToxicSlith", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "ToxicSlith", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d0e61272-a938-4b64-8713-3bac2356f1c7", "name": "Spawner (602)", "location": [762, 3073, 105], @@ -866,7 +850,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "27b77304-78cf-43bd-9ea3-076c491369a4", "name": "Spawner (602)", "location": [917, 3088, 37], @@ -889,7 +873,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "150f4f85-b244-48a4-a46f-d5b3c56951c6", "name": "Spawner (602)", "location": [839, 3086, 83], @@ -912,7 +896,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9f18de9c-4862-4940-a2b2-ea02d78c2a69", "name": "Spawner (602)", "location": [817, 2960, 76], @@ -932,7 +916,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4b7fcf89-dc47-4a59-8da2-bda0f810beeb", "name": "Spawner (602)", "location": [1106, 3138, -43], @@ -949,7 +933,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "743c46bb-a95f-4a7b-8a7f-4c4c0473fe38", "name": "Spawner (602)", "location": [861, 2923, 38], @@ -969,7 +953,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "de473023-c4ad-43f6-b9bd-b84d281234e8", "name": "Spawner (602)", "location": [648, 3131, 38], @@ -991,7 +975,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "7ad064f2-fb54-47c6-ba74-7d36d9425ea6", "name": "Spawner (602)", "location": [622, 3045, 35], @@ -1008,7 +992,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "10d3d73f-7f91-4eed-bd56-fc5805653892", "name": "Spawner (602)", "location": [685, 2901, 36], @@ -1027,7 +1011,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f1c797af-4863-4b66-8169-795330712f08", "name": "Spawner (602)", "location": [562, 2942, 36], @@ -1046,7 +1030,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fc314f2e-f697-4a0b-8bb0-f7dd51c1ed2a", "name": "Spawner (602)", "location": [486, 3211, 38], @@ -1064,7 +1048,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4182343a-a4c0-4bc5-b07a-2e71cb1d3d97", "name": "Spawner (602)", "location": [426, 3190, 38], @@ -1084,7 +1068,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a340eb44-bdd5-4ae8-a316-570d801cc0d3", "name": "Spawner (602)", "location": [354, 3283, 57], @@ -1101,7 +1085,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f5ea0039-e107-461c-8091-21561b83f1d8", "name": "Spawner (602)", "location": [453, 3632, 37], @@ -1118,7 +1102,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "80ecee75-df9e-462a-ac4c-695f0941b8fb", "name": "Spawner (602)", "location": [387, 3524, 38], @@ -1135,7 +1119,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0b95e3c9-d94c-478c-9179-0ee10faad038", "name": "Spawner (602)", "location": [1130, 3240, -42], @@ -1156,7 +1140,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6eff3e1c-0886-4c54-b7d6-0b01477deca7", "name": "Spawner (602)", "location": [1054, 3215, 38], @@ -1175,7 +1159,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b928e0bb-64e8-442d-bd4f-c058e9bde1c2", "name": "Spawner (602)", "location": [1057, 3167, 38], @@ -1195,7 +1179,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "979cdd74-3b3d-419b-9cf2-d7721332883d", "name": "Spawner (602)", "location": [1186, 3558, -42], @@ -1214,7 +1198,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4383a8fc-ec81-4ae6-b57e-f28ef51d26a1", "name": "Spawner (602)", "location": [35, 99, -5], @@ -1231,7 +1215,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2841721d-dcb8-41ce-84ed-ec8fca926424", "name": "Spawner (602)", "location": [35, 159, 0], @@ -1249,7 +1233,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "600fd870-3e98-44cf-9650-3e5e80977bc5", "name": "Spawner (602)", "location": [143, 158, -20], @@ -1260,8 +1244,6 @@ "team": 0, "homeRange": 5, "walkingRange": 5, - "entries": [ - { "name": "Niporailem", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Niporailem", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/post-uoml/termur/Underworld.json b/Distribution/Data/Spawns/post-uoml/termur/Underworld.json index b22ff5a29..c6ec0fc38 100644 --- a/Distribution/Data/Spawns/post-uoml/termur/Underworld.json +++ b/Distribution/Data/Spawns/post-uoml/termur/Underworld.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "1cd4979b-aac9-4287-b8af-9a31bb5526bc", "name": "Spawner (603)", "location": [1127, 1202, -2], @@ -11,12 +11,10 @@ "team": 0, "homeRange": 0, "walkingRange": -1, - "entries": [ - { "name": "genericguard", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "genericguard", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "568aaef8-af43-44f6-8212-a50e96dc51d8", "name": "Spawner (603)", "location": [1130, 1202, -2], @@ -27,12 +25,10 @@ "team": 0, "homeRange": 0, "walkingRange": -1, - "entries": [ - { "name": "genericguard", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "genericguard", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c7124c0d-db07-4d72-92e4-ff8ebb54ce89", "name": "Spawner (603)", "location": [1128, 1165, -12], @@ -43,12 +39,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "Garamon", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Garamon", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "29ebc79d-9f58-4ab8-9017-44a71fe17440", "name": "Spawner (603)", "location": [1108, 1153, -22], @@ -59,12 +53,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "gremlin", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "gremlin", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "81c4ea7e-4927-4bba-a31b-62ec12092f66", "name": "Spawner (603)", "location": [1110, 1169, -23], @@ -75,12 +67,10 @@ "team": 0, "homeRange": 7, "walkingRange": 10, - "entries": [ - { "name": "Slime", "maxCount": 10, "probability": 100 } - ] + "entries": [{ "name": "Slime", "maxCount": 10, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4eddff06-5db8-4a97-ad02-9284c3437410", "name": "Spawner (603)", "location": [1097, 1189, -38], @@ -91,12 +81,10 @@ "team": 0, "homeRange": 7, "walkingRange": 10, - "entries": [ - { "name": "acidslug", "maxCount": 25, "probability": 100 } - ] + "entries": [{ "name": "acidslug", "maxCount": 25, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bf730c77-6395-4ba6-80a4-8b004a7a78a1", "name": "Spawner (603)", "location": [1039, 1185, -56], @@ -113,7 +101,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f6324bc0-73ac-4936-805a-875b46164c54", "name": "Spawner (603)", "location": [1058, 1135, -37], @@ -124,12 +112,10 @@ "team": 0, "homeRange": 10, "walkingRange": 15, - "entries": [ - { "name": "rotworm", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "rotworm", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "dc88d319-4808-474a-8897-8bf076d951f9", "name": "Spawner (603)", "location": [1064, 1106, -65], @@ -140,12 +126,10 @@ "team": 0, "homeRange": 10, "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Kraken", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bcc44632-3114-4d86-86c5-a9b1e05a9c98", "name": "Spawner (603)", "location": [1153, 1187, -32], @@ -156,12 +140,10 @@ "team": 0, "homeRange": 14, "walkingRange": 20, - "entries": [ - { "name": "rotworm", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "rotworm", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "84dd7320-c5a2-4b62-a959-83b749d88285", "name": "Spawner (603)", "location": [1187, 1186, -40], @@ -172,12 +154,10 @@ "team": 0, "homeRange": 7, "walkingRange": 20, - "entries": [ - { "name": "WaterElemental", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "WaterElemental", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bd06c2cc-090f-47c9-98af-119591c20f30", "name": "Spawner (603)", "location": [1063, 1159, -42], @@ -195,7 +175,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0784ac30-eae0-4d6e-8953-f2217416f2a8", "name": "Spawner (603)", "location": [1152, 1130, -42], @@ -206,12 +186,10 @@ "team": 0, "homeRange": 12, "walkingRange": 20, - "entries": [ - { "name": "rotworm", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "rotworm", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1485dfcd-1dea-412f-a8fe-06a3f6a076cb", "name": "Spawner (603)", "location": [1121, 1132, -42], @@ -222,12 +200,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "FiddlingTobin", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "FiddlingTobin", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fd834a4b-778b-4f9c-9a86-c92de20ff2fe", "name": "Spawner (603)", "location": [1150, 963, -43], @@ -238,12 +214,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "NevilleBrightwhistle", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "NevilleBrightwhistle", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f94a912f-46d6-45b9-81c0-910a92f2aa87", "name": "Spawner (603)", "location": [1108, 1086, -60], @@ -254,12 +228,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "tanglingroots", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "tanglingroots", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0829e9d8-84e7-41d2-9587-2d61b117f473", "name": "Spawner (603)", "location": [1095, 1076, -71], @@ -270,12 +242,10 @@ "team": 0, "homeRange": 10, "walkingRange": -1, - "entries": [ - { "name": "tanglingroots", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "tanglingroots", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "587467d9-8b59-4257-a242-9890ec058b00", "name": "Spawner (603)", "location": [1089, 1128, -42], @@ -286,12 +256,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "Gretchen", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Gretchen", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9e60f208-6e14-4d77-83f0-fb067d02e804", "name": "Spawner (603)", "location": [1104, 1128, -52], @@ -302,12 +270,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "ElderDugan", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "ElderDugan", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "66d7c6f5-eeb7-4b34-af75-c82d38330ee1", "name": "Spawner (603)", "location": [1135, 1132, -42], @@ -318,12 +284,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "QuartermasterFlint", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "QuartermasterFlint", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2affc9d3-8b16-4c2e-947f-45d362a4a0f4", "name": "Spawner (603)", "location": [1015, 1118, -42], @@ -348,7 +312,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "382e2dee-6150-4d26-960b-c07b96638749", "name": "Spawner (603)", "location": [1022, 1087, -42], @@ -373,7 +337,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d072d262-08f3-4da3-9dcc-3983fa81d186", "name": "Spawner (603)", "location": [1039, 1072, -32], @@ -384,12 +348,10 @@ "team": 0, "homeRange": 5, "walkingRange": 10, - "entries": [ - { "name": "GreaterPoisonElemental", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "GreaterPoisonElemental", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9beef510-d580-44fc-95a6-d5ba9100cc34", "name": "Spawner (603)", "location": [1191, 1022, -42], @@ -406,7 +368,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "afed6e34-b8ee-4c64-b7d1-1b66629ccb97", "name": "Spawner (603)", "location": [1177, 995, -27], @@ -423,7 +385,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5caafbc5-1310-408c-8a26-ec00335494c2", "name": "Spawner (603)", "location": [1218, 1049, -42], @@ -440,7 +402,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a7f77781-8fcb-4aa0-b398-8f9b87b59eb2", "name": "Spawner (603)", "location": [1178, 971, -27], @@ -451,12 +413,10 @@ "team": 0, "homeRange": 10, "walkingRange": 15, - "entries": [ - { "name": "GrayGoblin", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "GrayGoblin", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6b2a7732-7ee9-480e-abd4-db501214cb05", "name": "Spawner (603)", "location": [1214, 1026, -22], @@ -467,12 +427,10 @@ "team": 0, "homeRange": 5, "walkingRange": 15, - "entries": [ - { "name": "GrayGoblin", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "GrayGoblin", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b00555a0-97e8-4eb3-b609-2bb9bc5d21bd", "name": "Spawner (603)", "location": [1199, 1053, -42], @@ -489,7 +447,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "23b17a4f-d7e9-421c-8581-ba8eb8aff487", "name": "Spawner (603)", "location": [1204, 1041, -42], @@ -500,12 +458,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "Jaacar", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Jaacar", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "25d8cb74-d701-4e5a-9074-e7c20f7cff39", "name": "Spawner (603)", "location": [1189, 989, -27], @@ -516,12 +472,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "Barreraak", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Barreraak", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "95edace5-c3dc-4582-b559-1c3f9905f6f5", "name": "Spawner (603)", "location": [1208, 984, -1], @@ -538,7 +492,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "975d89b2-f693-48c0-b88b-9796d8e39ee8", "name": "Spawner (603)", "location": [1217, 1074, -52], @@ -560,7 +514,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1b91d22a-24a2-4aee-88fa-36304f40dfdc", "name": "Spawner (603)", "location": [1053, 861, -32], @@ -571,12 +525,10 @@ "team": 0, "homeRange": 15, "walkingRange": 20, - "entries": [ - { "name": "NavreyNightEyes", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "NavreyNightEyes", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a37dcfad-6094-4074-8fc4-ead7d8dd889c", "name": "Spawner (603)", "location": [1094, 1002, -16], @@ -593,7 +545,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5a3f2d8b-8ed7-43fe-977b-a082ad4fd243", "name": "Spawner (603)", "location": [1124, 1008, -43], @@ -610,7 +562,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "31bf6eb1-fe34-4d25-b7cc-d0f395b2370a", "name": "Spawner (603)", "location": [1147, 993, -41], @@ -621,12 +573,10 @@ "team": 0, "homeRange": 7, "walkingRange": 7, - "entries": [ - { "name": "IronBeetle", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "IronBeetle", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "399bf1fe-0c47-4945-8c14-5ee8a1d3b6b3", "name": "Spawner (603)", "location": [1034, 1028, -43], @@ -644,7 +594,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "df3d7a25-190a-4f00-968c-a6def4cfa15e", "name": "Spawner (603)", "location": [1015, 1022, -43], @@ -655,12 +605,10 @@ "team": 0, "homeRange": 3, "walkingRange": 10, - "entries": [ - { "name": "GreenGoblinAlchemist", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "GreenGoblinAlchemist", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4bdd2d24-47f0-4678-b220-921df1e1c213", "name": "Spawner (603)", "location": [1014, 1000, -43], @@ -677,7 +625,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b1225d9f-1777-4ceb-9e03-2566fb2dc314", "name": "Spawner (603)", "location": [1034, 1004, -43], @@ -694,7 +642,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "af6a0287-46a1-490c-88ae-15172ec414f8", "name": "Spawner (603)", "location": [1024, 1018, -42], @@ -705,12 +653,10 @@ "team": 0, "homeRange": 4, "walkingRange": 10, - "entries": [ - { "name": "rotworm", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "rotworm", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "332360f9-69c2-4c45-b36b-0325b7c6650e", "name": "Spawner (603)", "location": [1015, 976, -27], @@ -728,7 +674,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4bb2f7ea-9131-47be-83a6-0f13831e76b1", "name": "Spawner (603)", "location": [1044, 976, -30], diff --git a/Distribution/Data/Spawns/post-uoml/termur/Vendors.json b/Distribution/Data/Spawns/post-uoml/termur/Vendors.json index 0133533d2..f884cf802 100644 --- a/Distribution/Data/Spawns/post-uoml/termur/Vendors.json +++ b/Distribution/Data/Spawns/post-uoml/termur/Vendors.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "7bd2b3c7-3c39-40c5-9c3b-98b73d060c65", "name": "Spawner (604)", "location": [976, 3881, -42], @@ -11,12 +11,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a80c5b72-e181-4266-8109-9c7a5eaf6a10", "name": "Spawner (604)", "location": [1010, 3883, -42], @@ -33,7 +31,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "99c47ee2-bbb0-4d0b-9294-18104ebb0f6b", "name": "Spawner (604)", "location": [1013, 3913, -42], @@ -50,7 +48,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6b95fe1b-1c63-479d-b481-1b4d4c929a79", "name": "Spawner (604)", "location": [977, 3909, -42], @@ -67,7 +65,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9c632527-c639-469c-9ded-ca859e4a8859", "name": "Spawner (604)", "location": [943, 3924, -42], @@ -78,12 +76,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b317c56d-c032-4e81-aa3a-78988500a740", "name": "Spawner (604)", "location": [1020, 3884, -42], @@ -100,7 +96,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "556fdb4d-f7f2-4ba0-9d01-d94a76ee7a28", "name": "Spawner (604)", "location": [740, 3466, -19], @@ -111,12 +107,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "65b19c79-0f40-4772-9037-1cad0769456d", "name": "Spawner (604)", "location": [772, 3467, -20], @@ -127,12 +121,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fd3342ae-de6c-436e-964a-e410ba1f983b", "name": "Spawner (604)", "location": [773, 3478, -20], @@ -150,7 +142,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fcffbb1c-fb73-4c59-9257-52e1d6ebadb4", "name": "Spawner (604)", "location": [772, 3490, -20], @@ -168,7 +160,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bb731ab1-950e-4e4b-8e55-6378eeb2c3b0", "name": "Spawner (604)", "location": [783, 3491, -20], @@ -185,7 +177,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "36f1b1bc-0754-4605-929a-d7c8e030a80b", "name": "Spawner (604)", "location": [797, 3494, 0], @@ -202,7 +194,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "28cda9f4-bbd1-451d-acb0-f568361a7d6f", "name": "Spawner (604)", "location": [805, 3491, -20], @@ -213,12 +205,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0bd1f793-51e9-4dbe-ab5c-dbd452e85d8c", "name": "Spawner (604)", "location": [817, 3456, -10], @@ -229,12 +219,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5f573458-bb63-4617-b2c4-c73762718dae", "name": "Spawner (604)", "location": [817, 3438, 0], @@ -252,7 +240,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "37378f9d-5e71-46a8-8d15-0b98d48a3d04", "name": "Spawner (604)", "location": [817, 3426, 0], @@ -269,7 +257,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2088913c-0ec7-4225-8f9d-1112e817542b", "name": "Spawner (604)", "location": [816, 3419, 0], @@ -286,7 +274,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "094d18b0-4b1a-491d-ae33-2463d400904b", "name": "Spawner (604)", "location": [805, 3397, 0], @@ -304,7 +292,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b9c41d27-d185-41dc-aa13-7258c018abc1", "name": "Spawner (604)", "location": [804, 3387, 0], @@ -321,7 +309,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "720f9c6d-c0e6-4f66-aca9-cca448a5d02d", "name": "Spawner (604)", "location": [851, 3404, -20], @@ -332,12 +320,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0197b002-1c6d-45ac-8d7d-21706a854586", "name": "Spawner (604)", "location": [833, 3439, -20], @@ -354,7 +340,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3e2450a8-7aa3-434a-a144-dfc36f784d9e", "name": "Spawner (604)", "location": [843, 3439, -20], @@ -371,7 +357,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f2b8fe59-6e8b-4db1-a20c-9663efe3b725", "name": "Spawner (604)", "location": [702, 3435, -20], @@ -388,7 +374,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "59459615-255f-449a-b4ee-c6adf848c09a", "name": "Spawner (604)", "location": [727, 3425, -20], @@ -405,7 +391,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fa9d60df-aa99-4934-bfab-35066f170481", "name": "Spawner (604)", "location": [715, 3446, -20], @@ -422,7 +408,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "27644d88-d3d5-458f-902d-78b7b59eaf95", "name": "Spawner (604)", "location": [713, 3438, -20], @@ -440,7 +426,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2a0e5133-2658-44f4-95ca-bcc5d68ddaeb", "name": "Spawner (604)", "location": [794, 3446, -10], @@ -457,7 +443,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6d91f1ff-12e5-4d75-aa5a-033ff00b9a24", "name": "Spawner (604)", "location": [779, 3431, -10], @@ -468,12 +454,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a1ba61c1-0fc5-46a4-b7de-1350606fb0fc", "name": "Spawner (604)", "location": [842, 3453, -18], @@ -484,8 +468,6 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/tokuno/FanDancersDojo.json b/Distribution/Data/Spawns/post-uoml/tokuno/FanDancersDojo.json similarity index 89% rename from Distribution/Data/Spawns/shared/tokuno/FanDancersDojo.json rename to Distribution/Data/Spawns/post-uoml/tokuno/FanDancersDojo.json index c0fb9438d..cb3648c61 100644 --- a/Distribution/Data/Spawns/shared/tokuno/FanDancersDojo.json +++ b/Distribution/Data/Spawns/post-uoml/tokuno/FanDancersDojo.json @@ -1,6 +1,56 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "42b06210-4cd3-49d6-a5d1-74e5ab0cc77a", + "name": "Spawner (501)", + "location": [977, 218, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "FanDancer", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47256f4f-b709-4c19-866b-2d7bdd896fd6", + "name": "Spawner (501)", + "location": [78, 546, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Daemon", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 }, + { "name": "Succubus", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "66d98c69-5788-471a-b93c-d82e252471b2", + "name": "Spawner (501)", + "location": [110, 540, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Daemon", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 }, + { "name": "Succubus", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "005c0fd4-1893-489e-9a09-14c53b246567", "name": "Spawner (501)", "location": [96, 540, -1], @@ -17,7 +67,169 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "f093fd90-d7b6-48a7-b5f3-c2524f4ca2ca", + "name": "Spawner (501)", + "location": [100, 499, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "24b2ba02-2289-4cb3-a957-e3ec7629c0b1", + "name": "Spawner (501)", + "location": [64, 488, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 2, "probability": 100 }, + { "name": "HordeMinion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3743807-ec35-485a-82b5-f3cd3ba27bc0", + "name": "Spawner (501)", + "location": [70, 376, -1], + "map": "Malas", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "FanDancer", "maxCount": 3, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, + { "name": "HordeMinion", "maxCount": 3, "probability": 100 }, + { "name": "Daemon", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "06d87817-7b93-45c4-9696-35c26ca4218d", + "name": "Spawner (501)", + "location": [120, 415, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "HordeMinion", "maxCount": 2, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd077bc0-b36c-41b4-9e4c-e3f067d6846e", + "name": "Spawner (501)", + "location": [99, 387, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "HeadlessOne", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4849d20d-77fc-4f0d-a513-a352a5849467", + "name": "Spawner (501)", + "location": [123, 387, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "HeadlessOne", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "183ea3fc-7ed8-4f54-bd05-513be50f48d6", + "name": "Spawner (501)", + "location": [79, 521, -1], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "HeadlessOne", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "624d9055-be69-4b42-9aa8-f8876a6c3cbf", + "name": "Spawner (501)", + "location": [88, 332, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "FanDancer", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cdd374da-1e88-4389-ac42-a6c63ba30d0b", + "name": "Spawner (501)", + "location": [97, 467, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "HordeMinion", "maxCount": 2, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Succubus", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "752b7dfa-4f8c-4bb6-875e-2f18385881b5", + "name": "Spawner (501)", + "location": [111, 352, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "HordeMinion", "maxCount": 2, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "016a082b-0696-40c3-815b-c3dbef373d50", "name": "Spawner (501)", "location": [173, 615, -1], @@ -36,58 +248,7 @@ ] }, { - "$type": "Spawner", - "guid": "06d87817-7b93-45c4-9696-35c26ca4218d", - "name": "Spawner (501)", - "location": [120, 415, -1], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "HordeMinion", "maxCount": 2, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12d3edf5-87f3-4fb4-9ca5-8462b94a81d8", - "name": "Spawner (501)", - "location": [134, 651, -1], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "FanDancer", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "183ea3fc-7ed8-4f54-bd05-513be50f48d6", - "name": "Spawner (501)", - "location": [79, 521, -1], - "map": "Malas", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "HeadlessOne", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "1a384aa9-3d4c-4df7-82df-58e658f4bf5f", "name": "Spawner (501)", "location": [147, 646, -1], @@ -104,144 +265,7 @@ ] }, { - "$type": "Spawner", - "guid": "24b2ba02-2289-4cb3-a957-e3ec7629c0b1", - "name": "Spawner (501)", - "location": [64, 488, -1], - "map": "Malas", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 2, "probability": 100 }, - { "name": "HordeMinion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "42b06210-4cd3-49d6-a5d1-74e5ab0cc77a", - "name": "Spawner (501)", - "location": [977, 218, 23], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "FanDancer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47256f4f-b709-4c19-866b-2d7bdd896fd6", - "name": "Spawner (501)", - "location": [78, 546, -1], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Daemon", "maxCount": 1, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 }, - { "name": "Succubus", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "477ff85e-e7ac-4a85-a99f-75d1b3e9eb74", - "name": "Spawner (501)", - "location": [110, 649, -1], - "map": "Malas", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "FanDancer", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 }, - { "name": "Balron", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4849d20d-77fc-4f0d-a513-a352a5849467", - "name": "Spawner (501)", - "location": [123, 387, -1], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "HeadlessOne", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "624d9055-be69-4b42-9aa8-f8876a6c3cbf", - "name": "Spawner (501)", - "location": [88, 332, -1], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "FanDancer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "66d98c69-5788-471a-b93c-d82e252471b2", - "name": "Spawner (501)", - "location": [110, 540, -1], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Daemon", "maxCount": 1, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 }, - { "name": "Succubus", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "752b7dfa-4f8c-4bb6-875e-2f18385881b5", - "name": "Spawner (501)", - "location": [111, 352, -1], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "HordeMinion", "maxCount": 2, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "9a661087-c1ec-4503-9451-8873a8c24bd9", "name": "Spawner (501)", "location": [175, 660, -1], @@ -259,7 +283,21 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "e3fd2f65-4f1e-4af5-8cab-26493d9c5350", + "name": "Spawner (501)", + "location": [142, 692, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "FanDancer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "a4392a5c-8729-40c2-bd31-4b145cba2f1a", "name": "Spawner (501)", "location": [106, 685, -1], @@ -276,89 +314,39 @@ ] }, { - "$type": "Spawner", - "guid": "bd077bc0-b36c-41b4-9e4c-e3f067d6846e", + "type": "Spawner", + "guid": "477ff85e-e7ac-4a85-a99f-75d1b3e9eb74", "name": "Spawner (501)", - "location": [99, 387, -1], + "location": [110, 649, -1], "map": "Malas", - "count": 4, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 10, "entries": [ - { "name": "HeadlessOne", "maxCount": 4, "probability": 100 } + { "name": "FanDancer", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "Balron", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "cdd374da-1e88-4389-ac42-a6c63ba30d0b", + "type": "Spawner", + "guid": "12d3edf5-87f3-4fb4-9ca5-8462b94a81d8", "name": "Spawner (501)", - "location": [97, 467, -1], + "location": [134, 651, -1], "map": "Malas", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, + "homeRange": 5, + "walkingRange": 5, "entries": [ - { "name": "HordeMinion", "maxCount": 2, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Succubus", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e3fd2f65-4f1e-4af5-8cab-26493d9c5350", - "name": "Spawner (501)", - "location": [142, 692, -1], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "FanDancer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f093fd90-d7b6-48a7-b5f3-c2524f4ca2ca", - "name": "Spawner (501)", - "location": [100, 499, -1], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f3743807-ec35-485a-82b5-f3cd3ba27bc0", - "name": "Spawner (501)", - "location": [70, 376, -1], - "map": "Malas", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 30, - "entries": [ - { "name": "FanDancer", "maxCount": 3, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, - { "name": "HordeMinion", "maxCount": 3, "probability": 100 }, - { "name": "Daemon", "maxCount": 1, "probability": 100 } + { "name": "FanDancer", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/tokuno/Outdoors.json b/Distribution/Data/Spawns/post-uoml/tokuno/Outdoors.json similarity index 84% rename from Distribution/Data/Spawns/shared/tokuno/Outdoors.json rename to Distribution/Data/Spawns/post-uoml/tokuno/Outdoors.json index 5e3698049..f8327b7cf 100644 --- a/Distribution/Data/Spawns/shared/tokuno/Outdoors.json +++ b/Distribution/Data/Spawns/post-uoml/tokuno/Outdoors.json @@ -1,9 +1,385 @@ [ { - "$type": "Spawner", - "guid": "005a9ed1-16b4-49a2-8f12-ee718ea97d4d", + "type": "Spawner", + "guid": "db468eba-47a3-46c1-b47a-c2179f0b2a5f", "name": "Spawner (502)", - "location": [212, 646, 31], + "location": [1215, 1086, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f388571-783f-4271-ad85-19979804caed", + "name": "Spawner (502)", + "location": [643, 540, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BakeKitsune", "maxCount": 4, "probability": 100 }, + { "name": "HellCat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6218636-c822-4e08-9b7c-1355ccae6ccf", + "name": "Spawner (502)", + "location": [615, 556, 37], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, + { "name": "HellCat", "maxCount": 8, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4421adb-275f-4462-95dc-2d8bcd981471", + "name": "Spawner (502)", + "location": [1184, 837, 36], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5906f708-87ed-4315-9849-69799e96a1fe", + "name": "Spawner (502)", + "location": [499, 530, 32], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "LesserHiryu", "maxCount": 6, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 6, "probability": 100 }, + { "name": "Oni", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3042275-6dc0-4a32-b09f-01d26bbb2c29", + "name": "Spawner (502)", + "location": [1197, 813, 27], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Hiryu", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b0318874-b52d-4005-aaf0-540bce7fc3e9", + "name": "Spawner (502)", + "location": [1247, 791, 6], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2facb999-7cc7-4e29-9a29-d77bb5a2d6fa", + "name": "Spawner (502)", + "location": [1345, 881, 13], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a8f16a28-29cc-41c2-a79e-be9e10a32363", + "name": "Spawner (502)", + "location": [1377, 882, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "LavaSerpent", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93a819a9-3b0f-40bd-b923-20ed865b6d0f", + "name": "Spawner (502)", + "location": [208, 641, 36], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6bc3be1f-048d-44a1-99d1-416c753c9468", + "name": "Spawner (502)", + "location": [582, 505, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, + { "name": "HellCat", "maxCount": 8, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 8, "probability": 100 }, + { "name": "HellHound", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9149d3f-2c41-4774-9758-1f8f71b1598e", + "name": "Spawner (502)", + "location": [1311, 776, 10], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Hiryu", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e2ab6de1-7751-4971-b901-3fb6f9ad2a2c", + "name": "Spawner (502)", + "location": [554, 476, 32], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "BakeKitsune", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "55d6194b-1117-472c-b276-543f6234d504", + "name": "Spawner (502)", + "location": [571, 550, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60281adb-b53d-4daf-9ce8-84c89c800e56", + "name": "Spawner (502)", + "location": [526, 556, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, + { "name": "HellCat", "maxCount": 8, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a1bebc3-fa84-492d-9b65-7a1e95a18fe1", + "name": "Spawner (502)", + "location": [1336, 810, 9], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EliteNinja", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a8390df1-8ea6-42e6-9d13-e09159f03a0e", + "name": "Spawner (502)", + "location": [1344, 774, 19], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98f4d240-c7db-4d14-ad8b-9b8fdb381d82", + "name": "Spawner (502)", + "location": [528, 552, 32], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "LesserHiryu", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c80f169f-7335-426e-a51c-ca1771920137", + "name": "Spawner (502)", + "location": [502, 553, 32], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Oni", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4bf88f4-306f-49f5-a2ab-ef5412ede34e", + "name": "Spawner (502)", + "location": [517, 513, 32], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "BakeKitsune", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "402e05e6-7371-4469-ab9b-32ac67e0dbf0", + "name": "Spawner (502)", + "location": [1291, 695, 19], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "LesserHiryu", "maxCount": 2, "probability": 100 }, + { "name": "RevenantLion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1fa14d5-38a8-41b5-8deb-d256e37f49d7", + "name": "Spawner (502)", + "location": [1354, 700, 33], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EliteNinja", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7f962e07-3668-48c6-889e-d2afab115c85", + "name": "Spawner (502)", + "location": [273, 606, 7], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "038fac93-3c13-44ef-aba9-491c8ad45a2b", + "name": "Spawner (502)", + "location": [518, 477, 32], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "BakeKitsune", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "99dd39c0-83e8-4f4d-a661-eb6bee0601b3", + "name": "Spawner (502)", + "location": [159, 688, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "52415a9d-22d9-4e34-af5a-f5f995a08d02", + "name": "Spawner (502)", + "location": [157, 685, 25], "map": "Tokuno", "count": 8, "minDelay": "00:05:00", @@ -21,56 +397,38 @@ ] }, { - "$type": "Spawner", - "guid": "01d0fd73-f752-4375-9fa8-46a112f9cf01", + "type": "Spawner", + "guid": "742dfb62-1a8b-4abe-89d2-2aa5b3eccd8c", "name": "Spawner (502)", - "location": [1036, 366, 7], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "02267402-1f6a-40ba-82fc-1b3bec08fcd6", - "name": "Spawner (502)", - "location": [248, 525, 41], + "location": [246, 768, 55], "map": "Tokuno", "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 6, "probability": 100 }, - { "name": "Kappa", "maxCount": 6, "probability": 100 } + { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "038fac93-3c13-44ef-aba9-491c8ad45a2b", + "type": "Spawner", + "guid": "c1966d23-2f5c-4a49-bc2c-b18756f292c3", "name": "Spawner (502)", - "location": [518, 477, 32], + "location": [186, 534, 41], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BakeKitsune", "maxCount": 4, "probability": 100 } - ] + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "05dffa7d-7472-4317-82f0-35e8d6c53fc8", "name": "Spawner (502)", "location": [374, 820, 34], @@ -81,48 +439,59 @@ "team": 0, "homeRange": 20, "walkingRange": 20, - "entries": [ - { "name": "RaiJu", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "RaiJu", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "07e15b51-9db0-4924-8c8e-5626e94a0eec", + "type": "Spawner", + "guid": "34706834-7851-4da5-928a-c74d6b0c84c3", "name": "Spawner (502)", - "location": [185, 1046, 10], + "location": [258, 709, 41], "map": "Tokuno", - "count": 12, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 12, "probability": 100 }, - { "name": "Lizardman", "maxCount": 12, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 } - ] + "entries": [{ "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "0a66af15-e45e-41be-a474-91e6ff754ae5", + "type": "Spawner", + "guid": "a5616fa0-d8b0-4711-bab9-34bacc355ed0", "name": "Spawner (502)", - "location": [980, 278, 30], + "location": [124, 646, 37], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e531f0c-3c40-4567-bc25-d433936fcf2a", + "name": "Spawner (502)", + "location": [123, 647, 36], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 }, - { "name": "FanDancer", "maxCount": 4, "probability": 100 } + { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, + { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0af3775d-7a5c-458c-920d-9147d2490ac4", "name": "Spawner (502)", "location": [356, 722, 54], @@ -133,132 +502,24 @@ "team": 0, "homeRange": 30, "walkingRange": 30, - "entries": [ - { "name": "YomotsuWarrior", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "YomotsuWarrior", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "0c8687ee-6ffe-4243-81ba-5938dc3c80c3", + "type": "Spawner", + "guid": "ae8aaaa8-249c-4994-b84d-c27860c04b12", "name": "Spawner (502)", - "location": [192, 1014, 10], + "location": [201, 731, 30], "map": "Tokuno", - "count": 8, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 8, "probability": 100 } - ] + "entries": [{ "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "0eef3fac-cdf6-4f36-bfca-b979d8f936af", - "name": "Spawner (502)", - "location": [540, 343, 8], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TsukiWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f013060-e752-4f42-972f-9b5638b67aa4", - "name": "Spawner (502)", - "location": [1062, 564, 22], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "Reaper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10592fab-f09a-450d-999b-b35b30fe5cb0", - "name": "Spawner (502)", - "location": [932, 364, 3], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1382b18e-ee1d-4364-8fe0-604a01e29711", - "name": "Spawner (502)", - "location": [210, 1085, 10], - "map": "Tokuno", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 12, "probability": 100 }, - { "name": "Lizardman", "maxCount": 12, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "149dab9c-9eba-43fc-922d-6f0c001552a4", - "name": "Spawner (502)", - "location": [472, 433, 27], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "150fd272-8a8e-4313-9751-5b5e9a7b9c74", - "name": "Spawner (502)", - "location": [1258, 531, 31], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Reaper", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "1763036e-1279-413f-93f8-447989b5b34c", "name": "Spawner (502)", "location": [181, 588, 45], @@ -269,62 +530,10 @@ "team": 0, "homeRange": 30, "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "18a2424f-1bd0-4745-b96d-07275a0b28cd", - "name": "Spawner (502)", - "location": [1282, 898, 12], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Oni", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "19703e47-77ae-4ee1-97ba-199e119d633d", - "name": "Spawner (502)", - "location": [760, 941, 37], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "19a680ca-e843-4941-8443-572b8299cccf", - "name": "Spawner (502)", - "location": [1076, 864, 56], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, - { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "19d3293a-39da-428f-b8f0-64d1aa2ca711", "name": "Spawner (502)", "location": [180, 586, 45], @@ -345,10 +554,69 @@ ] }, { - "$type": "Spawner", - "guid": "19e63dc7-82b4-4a67-94e4-5f016bf5be36", + "type": "Spawner", + "guid": "84d7c14d-faf3-4902-a569-f1426b2f16c3", "name": "Spawner (502)", - "location": [1034, 293, 21], + "location": [200, 803, 62], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ec646b02-cb01-4fba-b336-7d617095b3d3", + "name": "Spawner (502)", + "location": [166, 583, 49], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Yamandon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9b5e2d60-bf93-418d-a447-fa42e903a4e4", + "name": "Spawner (502)", + "location": [190, 856, 52], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e396724d-7481-4cb6-9302-77ed6dfb8b98", + "name": "Spawner (502)", + "location": [206, 891, 56], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6f788eb2-7306-4b06-9368-c6ff57e09ff7", + "name": "Spawner (502)", + "location": [1008, 246, 37], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", @@ -362,42 +630,31 @@ ] }, { - "$type": "Spawner", - "guid": "1a53a4f7-a748-48c5-b72c-40315e1b6cc8", + "type": "Spawner", + "guid": "005a9ed1-16b4-49a2-8f12-ee718ea97d4d", "name": "Spawner (502)", - "location": [874, 971, 45], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1a9c5dc9-b415-4da6-83f1-eab7300e39bf", - "name": "Spawner (502)", - "location": [358, 396, 32], + "location": [212, 646, 31], "map": "Tokuno", "count": 8, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, - "walkingRange": 40, + "walkingRange": 30, "entries": [ - { "name": "Ronin", "maxCount": 8, "probability": 100 } + { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, + { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "1d54520b-6357-4a09-a9dd-024c82a9dc3d", + "type": "Spawner", + "guid": "b6be5e10-9a08-4766-beb3-e9a445d4ae57", "name": "Spawner (502)", - "location": [662, 495, 28], + "location": [208, 936, 43], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", @@ -405,12 +662,59 @@ "team": 0, "homeRange": 30, "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9d0b815f-1f4b-499f-9c0d-4cfc20a62983", + "name": "Spawner (502)", + "location": [248, 928, 36], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "BakeKitsune", "maxCount": 4, "probability": 100 } + { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 6, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "5865a64e-b788-493a-9334-43fcec0bab98", + "name": "Spawner (502)", + "location": [221, 968, 34], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0c8687ee-6ffe-4243-81ba-5938dc3c80c3", + "name": "Spawner (502)", + "location": [192, 1014, 10], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "1dc1ab31-c521-4fdd-8248-fcf3c97717c6", "name": "Spawner (502)", "location": [1016, 292, 30], @@ -427,244 +731,7 @@ ] }, { - "$type": "Spawner", - "guid": "1eabd7ef-f919-48c9-a2d9-bd0953eca101", - "name": "Spawner (502)", - "location": [962, 203, 23], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 }, - { "name": "FanDancer", "maxCount": 4, "probability": 100 }, - { "name": "EliteNinja", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f22b0f9-3a7d-4520-935a-7d3e965708d4", - "name": "Spawner (502)", - "location": [588, 1012, 62], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f388571-783f-4271-ad85-19979804caed", - "name": "Spawner (502)", - "location": [643, 540, 24], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BakeKitsune", "maxCount": 4, "probability": 100 }, - { "name": "HellCat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22d610e3-5455-4ad6-ba5a-0894e1a78d2e", - "name": "Spawner (502)", - "location": [1055, 887, 38], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "243f4da3-3b86-46e7-afd0-66b9e67a506e", - "name": "Spawner (502)", - "location": [709, 877, 18], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "26a8d09d-b64b-40ab-a798-4df18a9b37a9", - "name": "Spawner (502)", - "location": [789, 276, 56], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, - { "name": "EvilMage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "270fa492-ee22-44dd-9f22-b83bcd5ce6eb", - "name": "Spawner (502)", - "location": [649, 409, 36], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "RaiJu", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2a2f5196-88ac-4cc7-9f02-d08890632f18", - "name": "Spawner (502)", - "location": [914, 55, 36], - "map": "Tokuno", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "IceElemental", "maxCount": 2, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, - { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, - { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2c226f07-9334-453c-b888-63f6f7bffeb4", - "name": "Spawner (502)", - "location": [680, 956, 44], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2cb5338d-2c87-40b8-bd7a-f6ed6162ecc8", - "name": "Spawner (502)", - "location": [798, 377, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, - { "name": "EvilMage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e030c4f-6f79-41b9-add4-785e95c7d016", - "name": "Spawner (502)", - "location": [1172, 535, 23], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Reaper", "maxCount": 4, "probability": 100 }, - { "name": "KazeKemono", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2facb999-7cc7-4e29-9a29-d77bb5a2d6fa", - "name": "Spawner (502)", - "location": [1345, 881, 13], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2fb8bd06-f301-46d7-bc73-b79ea25bc9a7", - "name": "Spawner (502)", - "location": [899, 271, 34], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 }, - { "name": "EvilMage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "318bacdf-f3b3-4619-95b6-dc8ce45e2f67", - "name": "Spawner (502)", - "location": [415, 351, 28], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "31ab5811-6361-44eb-9bda-c31fb4444295", "name": "Spawner (502)", "location": [232, 1116, 10], @@ -684,7 +751,227 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "3f8ba73c-44f5-41b9-affa-72a4bf38bee9", + "name": "Spawner (502)", + "location": [948, 247, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 }, + { "name": "EliteNinja", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65bb731d-dfb2-4eeb-9858-03d58c785b2a", + "name": "Spawner (502)", + "location": [285, 1156, 13], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61f7f65b-b91f-4e51-8551-e72d045d225f", + "name": "Spawner (502)", + "location": [261, 1186, 20], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "73714dcc-c107-4e57-98e3-800002a6ba8b", + "name": "Spawner (502)", + "location": [251, 1074, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1eabd7ef-f919-48c9-a2d9-bd0953eca101", + "name": "Spawner (502)", + "location": [962, 203, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 }, + { "name": "EliteNinja", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "818696a6-799b-49c6-855a-0b4efd4dcfda", + "name": "Spawner (502)", + "location": [916, 225, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 }, + { "name": "EliteNinja", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2fb8bd06-f301-46d7-bc73-b79ea25bc9a7", + "name": "Spawner (502)", + "location": [899, 271, 34], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ac2fd03-8350-4d2a-8c44-a2a7a126356e", + "name": "Spawner (502)", + "location": [819, 208, 38], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02267402-1f6a-40ba-82fc-1b3bec08fcd6", + "name": "Spawner (502)", + "location": [248, 525, 41], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 6, "probability": 100 }, + { "name": "Kappa", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "90b5cbd2-ec33-4b76-ab24-9b3dace6724b", + "name": "Spawner (502)", + "location": [824, 280, 33], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc472613-2b05-41aa-af9a-db4ef9e40fd0", + "name": "Spawner (502)", + "location": [284, 423, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d34f4cf7-eecf-4074-82e9-065d8aa94c1a", + "name": "Spawner (502)", + "location": [265, 406, 22], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "EliteNinja", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb71c80f-279b-4f05-8037-e53f05fef9f4", + "name": "Spawner (502)", + "location": [289, 477, 32], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "EliteNinja", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "31e9bebd-7db7-46c5-a143-e540ca8ccd98", "name": "Spawner (502)", "location": [326, 454, 32], @@ -695,69 +982,386 @@ "team": 0, "homeRange": 30, "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 8, "probability": 100 } - ] + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "3247cd95-f365-49a5-8a8d-efa7b29def12", + "type": "Spawner", + "guid": "a4c44311-a4b2-4bc4-8533-2723551964b5", "name": "Spawner (502)", - "location": [700, 826, 36], + "location": [765, 189, 42], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3416dcdb-aebf-4f59-8158-bd16a36d460c", - "name": "Spawner (502)", - "location": [976, 144, 35], - "map": "Tokuno", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "IceElemental", "maxCount": 2, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, - { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, - { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "34706834-7851-4da5-928a-c74d6b0c84c3", + "type": "Spawner", + "guid": "a83437ce-696c-4d66-93e7-90777f276afb", "name": "Spawner (502)", - "location": [258, 709, 41], + "location": [360, 466, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aa9544d7-3f07-4e3f-b2e1-0a37763cd99b", + "name": "Spawner (502)", + "location": [384, 460, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e6108ac2-0a40-4c99-a006-ee8b9d3950b9", + "name": "Spawner (502)", + "location": [418, 463, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "516cda7d-9e69-4aba-9e5d-9fded4599601", + "name": "Spawner (502)", + "location": [425, 443, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "149dab9c-9eba-43fc-922d-6f0c001552a4", + "name": "Spawner (502)", + "location": [472, 433, 27], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4dfbc791-7f39-44f7-99e9-4558e98c5055", + "name": "Spawner (502)", + "location": [407, 399, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1a9c5dc9-b415-4da6-83f1-eab7300e39bf", + "name": "Spawner (502)", + "location": [358, 396, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "896fbcbf-72c3-4280-bc24-c65df790c56e", + "name": "Spawner (502)", + "location": [725, 246, 36], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa3b9c76-9b9c-44f7-94cf-43c7b90277a7", + "name": "Spawner (502)", + "location": [769, 152, 12], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f48b4b8-cd8e-48fd-815b-04539b2e7295", + "name": "Spawner (502)", + "location": [323, 374, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adafc69d-7044-4319-8e4a-6e729aabfba0", + "name": "Spawner (502)", + "location": [773, 229, 64], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "318bacdf-f3b3-4619-95b6-dc8ce45e2f67", + "name": "Spawner (502)", + "location": [415, 351, 28], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "26a8d09d-b64b-40ab-a798-4df18a9b37a9", + "name": "Spawner (502)", + "location": [789, 276, 56], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "796af405-0888-48d6-945b-2da6c9f5578d", + "name": "Spawner (502)", + "location": [493, 378, 45], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cba2353c-6c72-4abe-b04e-5dbc6fc24027", + "name": "Spawner (502)", + "location": [454, 374, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2cb5338d-2c87-40b8-bd7a-f6ed6162ecc8", + "name": "Spawner (502)", + "location": [798, 377, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d344dbc9-d8ce-4502-bd9d-09be8e5e1286", + "name": "Spawner (502)", + "location": [472, 336, 38], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "435eff18-25d3-4e71-849c-6e8c69c05c9f", + "name": "Spawner (502)", + "location": [841, 326, 32], "map": "Tokuno", "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, - "walkingRange": 30, + "walkingRange": 40, "entries": [ - { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 } + { "name": "RuneBeetle", "maxCount": 6, "probability": 100 }, + { "name": "EvilMage", "maxCount": 6, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 6, "probability": 100 }, + { "name": "FanDancer", "maxCount": 6, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "37a35bdc-a33a-47ec-b7d3-e520093ebe29", + "type": "Spawner", + "guid": "c1afa53a-2806-4717-9ce7-04b6ac4afeab", "name": "Spawner (502)", - "location": [951, 409, 9], + "location": [907, 292, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d0734d58-9c91-45c2-8b51-69d73217e9d7", + "name": "Spawner (502)", + "location": [526, 299, 26], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Yamandon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0eef3fac-cdf6-4f36-bfca-b979d8f936af", + "name": "Spawner (502)", + "location": [540, 343, 8], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "436deca6-7510-45d7-84b4-9d5749246ca3", + "name": "Spawner (502)", + "location": [527, 393, 53], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a25ae4f2-e3d4-4c6c-ae56-10c00f828436", + "name": "Spawner (502)", + "location": [566, 430, 35], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98e06265-c66b-426c-87f1-fa8bcfbfbd18", + "name": "Spawner (502)", + "location": [892, 391, 14], "map": "Tokuno", "count": 8, "minDelay": "00:05:00", @@ -772,7 +1376,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3946cdc1-9fcf-40fd-91ab-2f52f6c97592", "name": "Spawner (502)", "location": [873, 326, 37], @@ -790,23 +1394,853 @@ ] }, { - "$type": "Spawner", - "guid": "3a1bebc3-fa84-492d-9b65-7a1e95a18fe1", + "type": "Spawner", + "guid": "919574b9-7243-4843-94d0-cb4f5b4d0f1b", "name": "Spawner (502)", - "location": [1336, 810, 9], + "location": [594, 362, 22], "map": "Tokuno", - "count": 1, + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47a5ecc2-c805-4edf-a622-3f9cc0655911", + "name": "Spawner (502)", + "location": [861, 335, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Kappa", "maxCount": 4, "probability": 100 }, + { "name": "Ronin", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d7641cb5-bb06-4d5a-9b9d-24413ae09e8c", + "name": "Spawner (502)", + "location": [853, 386, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Kappa", "maxCount": 4, "probability": 100 }, + { "name": "Ronin", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "270fa492-ee22-44dd-9f22-b83bcd5ce6eb", + "name": "Spawner (502)", + "location": [649, 409, 36], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "RaiJu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e84fef60-0b86-48d0-8e63-b754a89b1579", + "name": "Spawner (502)", + "location": [677, 446, 31], + "map": "Tokuno", + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "EliteNinja", "maxCount": 1, "probability": 100 } + { "name": "RevenantLion", "maxCount": 4, "probability": 100 }, + { "name": "BakeKitsune", "maxCount": 4, "probability": 100 }, + { "name": "Oni", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "4980823a-bcbe-431e-8ad2-594bf7e28aaf", + "name": "Spawner (502)", + "location": [907, 394, 10], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9bf85cd-f6ef-4207-852e-0c8128711512", + "name": "Spawner (502)", + "location": [927, 451, 14], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10592fab-f09a-450d-999b-b35b30fe5cb0", + "name": "Spawner (502)", + "location": [932, 364, 3], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e60bef67-7439-47c5-950a-4d6ba75f9456", + "name": "Spawner (502)", + "location": [930, 405, 11], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "97c87b67-b804-4135-8bbf-80e2e1595bf2", + "name": "Spawner (502)", + "location": [992, 383, 5], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b504e238-76e5-4634-a3cc-deb3b3ce3db2", + "name": "Spawner (502)", + "location": [997, 407, -2], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37a35bdc-a33a-47ec-b7d3-e520093ebe29", + "name": "Spawner (502)", + "location": [951, 409, 9], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01d0fd73-f752-4375-9fa8-46a112f9cf01", + "name": "Spawner (502)", + "location": [1036, 366, 7], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19e63dc7-82b4-4a67-94e4-5f016bf5be36", + "name": "Spawner (502)", + "location": [1034, 293, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a66af15-e45e-41be-a474-91e6ff754ae5", + "name": "Spawner (502)", + "location": [980, 278, 30], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7142d685-eb27-4e84-92cb-4344d63d739e", + "name": "Spawner (502)", + "location": [948, 522, 7], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9cc378fc-7158-4e04-83ad-895160e6d1ec", + "name": "Spawner (502)", + "location": [918, 439, 14], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "602fd66a-e62a-4963-a028-bbdc0d8f9401", + "name": "Spawner (502)", + "location": [951, 467, 12], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62fb0bce-3f04-4d27-9341-14e8e9ea6497", + "name": "Spawner (502)", + "location": [1150, 1072, 14], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "Kappa", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46e8f0bc-a094-423d-bd18-23a05efa801d", + "name": "Spawner (502)", + "location": [979, 477, 2], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c61fe6f-8b81-40ed-acef-188199111f3c", + "name": "Spawner (502)", + "location": [983, 448, -1], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91372a1f-eb92-49f7-b7ae-25c8fb221855", + "name": "Spawner (502)", + "location": [1051, 473, 7], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b818a6a6-2295-402a-912f-a3c7b680d71a", + "name": "Spawner (502)", + "location": [1062, 469, 8], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DireWolf", "maxCount": 4, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9bbad6f-cb96-4a0d-90dd-5711a4b19ec7", + "name": "Spawner (502)", + "location": [1086, 452, 14], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 }, + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "Lizardman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c79a8c2-cbae-49ae-87ca-3f32e5c1eda0", + "name": "Spawner (502)", + "location": [1084, 464, 15], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Yamandon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "866db38c-6643-486a-973b-ed4eb3855e05", + "name": "Spawner (502)", + "location": [999, 580, 14], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "KazeKemono", "maxCount": 4, "probability": 100 }, + { "name": "TsukiWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d935aa73-025f-4c79-92df-f46b642cda74", + "name": "Spawner (502)", + "location": [921, 593, 20], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7ddcace7-c82a-4f05-9855-3c4c413e4602", + "name": "Spawner (502)", + "location": [1042, 559, 22], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Reaper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0f013060-e752-4f42-972f-9b5638b67aa4", + "name": "Spawner (502)", + "location": [1062, 564, 22], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Reaper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d3d6ff67-6800-4108-9039-071f96f48955", + "name": "Spawner (502)", + "location": [1092, 563, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e7895e4-b062-48a8-bf1e-11bd423cf49f", + "name": "Spawner (502)", + "location": [1146, 516, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9d0a3d34-a633-4d61-9d84-b2df29ae7051", + "name": "Spawner (502)", + "location": [192, 770, 56], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Mongbat", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4615040f-530b-4719-bd2f-2a201281cd44", + "name": "Spawner (502)", + "location": [1214, 531, 28], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e030c4f-6f79-41b9-add4-785e95c7d016", + "name": "Spawner (502)", + "location": [1172, 535, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Reaper", "maxCount": 4, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ace8cee6-59ca-4e3c-90a7-1285a1758f14", + "name": "Spawner (502)", + "location": [1184, 616, 19], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "LavaSerpent", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "59600e4f-219e-4f5b-baa3-6714be5b9f7e", + "name": "Spawner (502)", + "location": [1058, 799, 13], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3cfcea02-60a9-463b-a5a3-67bae5267235", + "name": "Spawner (502)", + "location": [1181, 484, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "97ce7efd-4bea-4256-aec4-dde1404c559b", + "name": "Spawner (502)", + "location": [1196, 507, 26], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Reaper", "maxCount": 4, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "150fd272-8a8e-4313-9751-5b5e9a7b9c74", + "name": "Spawner (502)", + "location": [1258, 531, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Reaper", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c0f9cdb5-34f3-470e-9080-e375959d95e1", + "name": "Spawner (502)", + "location": [1285, 573, 30], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bbf25923-04a3-4b44-9e43-6199ea5a5dd1", + "name": "Spawner (502)", + "location": [1321, 619, 38], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f7d75f4-e71b-474c-8711-00345b91e83d", + "name": "Spawner (502)", + "location": [1214, 652, 90], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5228c3d-ef03-46e5-9d44-8b2e223eef4d", + "name": "Spawner (502)", + "location": [1176, 701, 72], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b5ace63-c337-4f3b-b82b-7901942fb71a", + "name": "Spawner (502)", + "location": [1144, 667, 70], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5316c66-ce00-4cd1-9151-7508e8d0b6e8", + "name": "Spawner (502)", + "location": [1134, 705, 72], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 4, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa991041-da28-4f4e-980c-8034ec03aa57", + "name": "Spawner (502)", + "location": [1105, 753, 44], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "HellCat", "maxCount": 4, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9314780-e6f4-4589-a64b-cd470dc5bb32", + "name": "Spawner (502)", + "location": [1021, 802, 0], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, + { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd76c01b-91e2-4a35-b2d2-c30a7ddfe78a", + "name": "Spawner (502)", + "location": [931, 833, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7fdc0616-9830-43aa-9d9c-a51bda258191", + "name": "Spawner (502)", + "location": [1006, 857, 15], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, + { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d393499a-3788-4887-b582-2903c0ae3c16", + "name": "Spawner (502)", + "location": [980, 815, 15], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, + { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4354759f-5548-41f0-acb1-fbc9c7a9694a", + "name": "Spawner (502)", + "location": [892, 652, 17], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "KazeKemono", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "67b16a8e-dbf3-46f2-8c1c-89f4601d9f56", + "name": "Spawner (502)", + "location": [968, 753, 3], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "3c541073-7877-418c-af03-7864f952a710", "name": "Spawner (502)", "location": [938, 722, 10], @@ -817,12 +2251,530 @@ "team": 0, "homeRange": 30, "walkingRange": 30, + "entries": [{ "name": "KazeKemono", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f314eaf0-1752-4943-82f0-89288cf6b903", + "name": "Spawner (502)", + "location": [932, 685, 12], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "KazeKemono", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7ebea3a2-31d2-4ff1-b858-42ce1945f701", + "name": "Spawner (502)", + "location": [1120, 1099, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "KazeKemono", "maxCount": 4, "probability": 100 } + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, + { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "5da7254b-6b8b-4ad4-b253-e9a3f8b84350", + "name": "Spawner (502)", + "location": [840, 714, 16], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "KazeKemono", "maxCount": 8, "probability": 100 }, + { "name": "EliteNinja", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65e659e7-d71c-472d-9552-755fd0b7d187", + "name": "Spawner (502)", + "location": [883, 604, 39], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Hiryu", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a30ef889-c94e-4870-a1c7-6bc38738919d", + "name": "Spawner (502)", + "location": [1012, 631, 11], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19a680ca-e843-4941-8443-572b8299cccf", + "name": "Spawner (502)", + "location": [1076, 864, 56], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, + { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22d610e3-5455-4ad6-ba5a-0894e1a78d2e", + "name": "Spawner (502)", + "location": [1055, 887, 38], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb28888e-536f-4588-87ed-32e0151c3557", + "name": "Spawner (502)", + "location": [1052, 976, 26], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "497466b2-aa4f-4a36-b431-53c89e5c6afe", + "name": "Spawner (502)", + "location": [1064, 1029, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfdb9550-70ee-48ef-a165-4dd2b82a5ace", + "name": "Spawner (502)", + "location": [1092, 980, 33], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e5ec5310-19bc-428f-92d3-b6e972280758", + "name": "Spawner (502)", + "location": [1086, 910, 61], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 4, "probability": 100 }, + { "name": "EliteNinja", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e8727ee4-86b2-4ac0-bff7-bd503904199f", + "name": "Spawner (502)", + "location": [637, 357, 32], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "RevenantLion", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "18a2424f-1bd0-4745-b96d-07275a0b28cd", + "name": "Spawner (502)", + "location": [1282, 898, 12], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Oni", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cabd6472-2187-4078-85e1-437ab1d2d0b8", + "name": "Spawner (502)", + "location": [243, 1031, 13], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1c5f957-9c22-4839-9b22-1be66ab4a078", + "name": "Spawner (502)", + "location": [742, 1012, 33], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d54520b-6357-4a09-a9dd-024c82a9dc3d", + "name": "Spawner (502)", + "location": [662, 495, 28], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "BakeKitsune", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47a74e20-3a5b-43ee-b381-d945b34ddc8a", + "name": "Spawner (502)", + "location": [1293, 864, -1], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7043408d-2dc5-4c52-b192-2e95d93195c5", + "name": "Spawner (502)", + "location": [1321, 847, 13], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EliteNinja", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d46fba23-4a24-4736-8701-daa96189ff9f", + "name": "Spawner (502)", + "location": [1343, 654, 35], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eb40a1dc-8905-430d-a5b6-180d5629d0a2", + "name": "Spawner (502)", + "location": [294, 748, 55], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e9f063b7-3bd6-4490-804e-1dcedcd98cc0", + "name": "Spawner (502)", + "location": [198, 924, 42], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07e15b51-9db0-4924-8c8e-5626e94a0eec", + "name": "Spawner (502)", + "location": [185, 1046, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1382b18e-ee1d-4364-8fe0-604a01e29711", + "name": "Spawner (502)", + "location": [210, 1085, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "deb29408-0b0a-4bbe-bfee-962353f80e94", + "name": "Spawner (502)", + "location": [581, 953, 33], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "997db593-15d5-4963-9659-0a372b565d86", + "name": "Spawner (502)", + "location": [533, 991, 53], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b2ac0fff-06ad-438a-a365-0bb7ad3502a6", + "name": "Spawner (502)", + "location": [562, 984, 43], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "defa7e90-33db-45c3-8ef7-6f5b153bf4cd", + "name": "Spawner (502)", + "location": [511, 956, 27], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4540125e-93b0-4491-bdee-fd09145a4a6c", + "name": "Spawner (502)", + "location": [524, 934, 19], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5bf51fd3-f2c9-49f3-b680-549db2d662fe", + "name": "Spawner (502)", + "location": [535, 918, 14], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2d2e12f-2919-4097-81b2-69c88f8d4f63", + "name": "Spawner (502)", + "location": [526, 889, 11], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ee8bcfb4-c8aa-46d8-a29c-3350388df282", + "name": "Spawner (502)", + "location": [573, 933, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ceff3a89-0382-4aa9-b03d-e5935edcafe7", + "name": "Spawner (502)", + "location": [612, 888, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4526aff8-ef6d-4c5b-b1ea-0754687af7bb", + "name": "Spawner (502)", + "location": [570, 840, 0], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "3cc7a85a-0c2e-4368-8912-0c52aef08729", "name": "Spawner (502)", "location": [601, 930, 32], @@ -839,10 +2791,421 @@ ] }, { - "$type": "Spawner", - "guid": "3cfcea02-60a9-463b-a5a3-67bae5267235", + "type": "Spawner", + "guid": "8dcd72ef-b063-43c7-ae83-84dd5d2389c2", "name": "Spawner (502)", - "location": [1181, 484, 22], + "location": [638, 866, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9723d0f8-bdca-4e26-a59c-750042c787a3", + "name": "Spawner (502)", + "location": [666, 896, 28], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3247cd95-f365-49a5-8a8d-efa7b29def12", + "name": "Spawner (502)", + "location": [700, 826, 36], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Orc", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ddecebbf-fc04-45ad-9eea-328790b80fc0", + "name": "Spawner (502)", + "location": [715, 831, 36], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Orc", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a2b02241-de57-4e42-908a-d89b90ae45d3", + "name": "Spawner (502)", + "location": [725, 823, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "de26b10f-8b26-4f72-92aa-058a19600dd2", + "name": "Spawner (502)", + "location": [771, 824, 16], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "73b7620d-f5f9-4fdb-9379-831522388d84", + "name": "Spawner (502)", + "location": [737, 853, 35], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "243f4da3-3b86-46e7-afd0-66b9e67a506e", + "name": "Spawner (502)", + "location": [709, 877, 18], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "462cc023-8496-4574-8963-5577ce18642d", + "name": "Spawner (502)", + "location": [683, 857, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Orc", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "67cd30f3-8ec3-4cbb-91b5-a2074fab67f4", + "name": "Spawner (502)", + "location": [667, 943, 43], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c41c2c47-c049-41b3-b4cf-b48cc130a9e0", + "name": "Spawner (502)", + "location": [619, 981, 45], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f22b0f9-3a7d-4520-935a-7d3e965708d4", + "name": "Spawner (502)", + "location": [588, 1012, 62], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7537f1f7-5755-4e45-ba02-7f4a79ccb98b", + "name": "Spawner (502)", + "location": [644, 1005, 55], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9ed6e580-23db-415f-a93d-4a1841495166", + "name": "Spawner (502)", + "location": [668, 986, 46], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd996179-baa6-48a6-b065-5371b0592cfd", + "name": "Spawner (502)", + "location": [688, 979, 44], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c226f07-9334-453c-b888-63f6f7bffeb4", + "name": "Spawner (502)", + "location": [680, 956, 44], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "692aae7e-d475-458e-8d26-a15b280edd8f", + "name": "Spawner (502)", + "location": [722, 916, 40], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8fb6a683-1f7b-4364-9538-2db139f0288e", + "name": "Spawner (502)", + "location": [729, 934, 42], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "990e1da9-704d-4e3d-b1a6-9180cea7dabe", + "name": "Spawner (502)", + "location": [771, 979, 29], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "904d1a0d-b3b7-43b4-b37f-5c8a61bffbdb", + "name": "Spawner (502)", + "location": [803, 961, 14], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76c890d5-23df-453d-bb6b-d1e6789384c3", + "name": "Spawner (502)", + "location": [819, 944, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "86222a57-5629-47c3-8a8d-977853e8081c", + "name": "Spawner (502)", + "location": [792, 920, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed3f775c-f53d-46b2-8a53-bc325a2cb088", + "name": "Spawner (502)", + "location": [794, 890, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b332005a-d3b2-4d5a-a527-6858e5197c0b", + "name": "Spawner (502)", + "location": [819, 902, 27], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eb58e0a4-29a4-4f28-a54b-226136752d59", + "name": "Spawner (502)", + "location": [770, 875, 30], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19703e47-77ae-4ee1-97ba-199e119d633d", + "name": "Spawner (502)", + "location": [760, 941, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a53a4f7-a748-48c5-b72c-40315e1b6cc8", + "name": "Spawner (502)", + "location": [874, 971, 45], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "414e159b-efdc-4c32-be33-60f677efc824", + "name": "Spawner (502)", + "location": [1105, 855, 50], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", @@ -851,11 +3214,12 @@ "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "TsukiWolf", "maxCount": 4, "probability": 100 } + { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, + { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3e4489f8-0e90-420b-a546-8628a6b39871", "name": "Spawner (502)", "location": [926, 145, 49], @@ -877,1170 +3241,7 @@ ] }, { - "$type": "Spawner", - "guid": "3f8ba73c-44f5-41b9-affa-72a4bf38bee9", - "name": "Spawner (502)", - "location": [948, 247, 19], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 }, - { "name": "FanDancer", "maxCount": 4, "probability": 100 }, - { "name": "EliteNinja", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "402e05e6-7371-4469-ab9b-32ac67e0dbf0", - "name": "Spawner (502)", - "location": [1291, 695, 19], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "LesserHiryu", "maxCount": 2, "probability": 100 }, - { "name": "RevenantLion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "414e159b-efdc-4c32-be33-60f677efc824", - "name": "Spawner (502)", - "location": [1105, 855, 50], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, - { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4354759f-5548-41f0-acb1-fbc9c7a9694a", - "name": "Spawner (502)", - "location": [892, 652, 17], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "KazeKemono", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "435eff18-25d3-4e71-849c-6e8c69c05c9f", - "name": "Spawner (502)", - "location": [841, 326, 32], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "RuneBeetle", "maxCount": 6, "probability": 100 }, - { "name": "EvilMage", "maxCount": 6, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 6, "probability": 100 }, - { "name": "FanDancer", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "436deca6-7510-45d7-84b4-9d5749246ca3", - "name": "Spawner (502)", - "location": [527, 393, 53], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 8, "probability": 100 }, - { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4526aff8-ef6d-4c5b-b1ea-0754687af7bb", - "name": "Spawner (502)", - "location": [570, 840, 0], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4540125e-93b0-4491-bdee-fd09145a4a6c", - "name": "Spawner (502)", - "location": [524, 934, 19], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4615040f-530b-4719-bd2f-2a201281cd44", - "name": "Spawner (502)", - "location": [1214, 531, 28], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TsukiWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "462cc023-8496-4574-8963-5577ce18642d", - "name": "Spawner (502)", - "location": [683, 857, 31], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "46e8f0bc-a094-423d-bd18-23a05efa801d", - "name": "Spawner (502)", - "location": [979, 477, 2], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47a5ecc2-c805-4edf-a622-3f9cc0655911", - "name": "Spawner (502)", - "location": [861, 335, 37], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Kappa", "maxCount": 4, "probability": 100 }, - { "name": "Ronin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47a74e20-3a5b-43ee-b381-d945b34ddc8a", - "name": "Spawner (502)", - "location": [1293, 864, -1], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "LesserHiryu", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "497466b2-aa4f-4a36-b431-53c89e5c6afe", - "name": "Spawner (502)", - "location": [1064, 1029, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4980823a-bcbe-431e-8ad2-594bf7e28aaf", - "name": "Spawner (502)", - "location": [907, 394, 10], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4c61fe6f-8b81-40ed-acef-188199111f3c", - "name": "Spawner (502)", - "location": [983, 448, -1], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4dfbc791-7f39-44f7-99e9-4558e98c5055", - "name": "Spawner (502)", - "location": [407, 399, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4f48b4b8-cd8e-48fd-815b-04539b2e7295", - "name": "Spawner (502)", - "location": [323, 374, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "516cda7d-9e69-4aba-9e5d-9fded4599601", - "name": "Spawner (502)", - "location": [425, 443, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "52415a9d-22d9-4e34-af5a-f5f995a08d02", - "name": "Spawner (502)", - "location": [157, 685, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, - { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "55d6194b-1117-472c-b276-543f6234d504", - "name": "Spawner (502)", - "location": [571, 550, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, - { "name": "KazeKemono", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5865a64e-b788-493a-9334-43fcec0bab98", - "name": "Spawner (502)", - "location": [221, 968, 34], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5906f708-87ed-4315-9849-69799e96a1fe", - "name": "Spawner (502)", - "location": [499, 530, 32], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "LesserHiryu", "maxCount": 6, "probability": 100 }, - { "name": "KazeKemono", "maxCount": 6, "probability": 100 }, - { "name": "Oni", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "59600e4f-219e-4f5b-baa3-6714be5b9f7e", - "name": "Spawner (502)", - "location": [1058, 799, 13], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5b5ace63-c337-4f3b-b82b-7901942fb71a", - "name": "Spawner (502)", - "location": [1144, 667, 70], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "HellHound", "maxCount": 4, "probability": 100 }, - { "name": "FireElemental", "maxCount": 4, "probability": 100 }, - { "name": "LavaSerpent", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5bf51fd3-f2c9-49f3-b680-549db2d662fe", - "name": "Spawner (502)", - "location": [535, 918, 14], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5da7254b-6b8b-4ad4-b253-e9a3f8b84350", - "name": "Spawner (502)", - "location": [840, 714, 16], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "KazeKemono", "maxCount": 8, "probability": 100 }, - { "name": "EliteNinja", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60281adb-b53d-4daf-9ce8-84c89c800e56", - "name": "Spawner (502)", - "location": [526, 556, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, - { "name": "HellCat", "maxCount": 8, "probability": 100 }, - { "name": "KazeKemono", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "602fd66a-e62a-4963-a028-bbdc0d8f9401", - "name": "Spawner (502)", - "location": [951, 467, 12], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "61f7f65b-b91f-4e51-8551-e72d045d225f", - "name": "Spawner (502)", - "location": [261, 1186, 20], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "62fb0bce-3f04-4d27-9341-14e8e9ea6497", - "name": "Spawner (502)", - "location": [1150, 1072, 14], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "Kappa", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "63477a04-4f55-4c09-b594-0031d9d4e25d", - "name": "Spawner (502)", - "location": [947, 103, 46], - "map": "Tokuno", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "IceElemental", "maxCount": 2, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, - { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, - { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "65bb731d-dfb2-4eeb-9858-03d58c785b2a", - "name": "Spawner (502)", - "location": [285, 1156, 13], - "map": "Tokuno", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 12, "probability": 100 }, - { "name": "Lizardman", "maxCount": 12, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "65e659e7-d71c-472d-9552-755fd0b7d187", - "name": "Spawner (502)", - "location": [883, 604, 39], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Hiryu", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "67b16a8e-dbf3-46f2-8c1c-89f4601d9f56", - "name": "Spawner (502)", - "location": [968, 753, 3], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TsukiWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "67cd30f3-8ec3-4cbb-91b5-a2074fab67f4", - "name": "Spawner (502)", - "location": [667, 943, 43], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "692aae7e-d475-458e-8d26-a15b280edd8f", - "name": "Spawner (502)", - "location": [722, 916, 40], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bc3be1f-048d-44a1-99d1-416c753c9468", - "name": "Spawner (502)", - "location": [582, 505, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, - { "name": "HellCat", "maxCount": 8, "probability": 100 }, - { "name": "KazeKemono", "maxCount": 8, "probability": 100 }, - { "name": "HellHound", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6c79a8c2-cbae-49ae-87ca-3f32e5c1eda0", - "name": "Spawner (502)", - "location": [1084, 464, 15], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Yamandon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f788eb2-7306-4b06-9368-c6ff57e09ff7", - "name": "Spawner (502)", - "location": [1008, 246, 37], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 }, - { "name": "FanDancer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f7d75f4-e71b-474c-8711-00345b91e83d", - "name": "Spawner (502)", - "location": [1214, 652, 90], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "HellHound", "maxCount": 4, "probability": 100 }, - { "name": "FireElemental", "maxCount": 4, "probability": 100 }, - { "name": "LavaSerpent", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7043408d-2dc5-4c52-b192-2e95d93195c5", - "name": "Spawner (502)", - "location": [1321, 847, 13], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EliteNinja", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7142d685-eb27-4e84-92cb-4344d63d739e", - "name": "Spawner (502)", - "location": [948, 522, 7], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "73714dcc-c107-4e57-98e3-800002a6ba8b", - "name": "Spawner (502)", - "location": [251, 1074, 10], - "map": "Tokuno", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 12, "probability": 100 }, - { "name": "Lizardman", "maxCount": 12, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "73b7620d-f5f9-4fdb-9379-831522388d84", - "name": "Spawner (502)", - "location": [737, 853, 35], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "742dfb62-1a8b-4abe-89d2-2aa5b3eccd8c", - "name": "Spawner (502)", - "location": [246, 768, 55], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, - { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7537f1f7-5755-4e45-ba02-7f4a79ccb98b", - "name": "Spawner (502)", - "location": [644, 1005, 55], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76c890d5-23df-453d-bb6b-d1e6789384c3", - "name": "Spawner (502)", - "location": [819, 944, 22], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "796af405-0888-48d6-945b-2da6c9f5578d", - "name": "Spawner (502)", - "location": [493, 378, 45], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 8, "probability": 100 }, - { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7ddcace7-c82a-4f05-9855-3c4c413e4602", - "name": "Spawner (502)", - "location": [1042, 559, 22], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "Reaper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7ebea3a2-31d2-4ff1-b858-42ce1945f701", - "name": "Spawner (502)", - "location": [1120, 1099, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, - { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7f962e07-3668-48c6-889e-d2afab115c85", - "name": "Spawner (502)", - "location": [273, 606, 7], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7fdc0616-9830-43aa-9d9c-a51bda258191", - "name": "Spawner (502)", - "location": [1006, 857, 15], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, - { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "818696a6-799b-49c6-855a-0b4efd4dcfda", - "name": "Spawner (502)", - "location": [916, 225, 23], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 }, - { "name": "FanDancer", "maxCount": 4, "probability": 100 }, - { "name": "EliteNinja", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84d7c14d-faf3-4902-a569-f1426b2f16c3", - "name": "Spawner (502)", - "location": [200, 803, 62], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "86222a57-5629-47c3-8a8d-977853e8081c", - "name": "Spawner (502)", - "location": [792, 920, 31], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "866db38c-6643-486a-973b-ed4eb3855e05", - "name": "Spawner (502)", - "location": [999, 580, 14], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "KazeKemono", "maxCount": 4, "probability": 100 }, - { "name": "TsukiWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "896fbcbf-72c3-4280-bc24-c65df790c56e", - "name": "Spawner (502)", - "location": [725, 246, 36], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, - { "name": "EvilMage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ac2fd03-8350-4d2a-8c44-a2a7a126356e", - "name": "Spawner (502)", - "location": [819, 208, 38], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, - { "name": "EvilMage", "maxCount": 4, "probability": 100 }, - { "name": "FanDancer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8dcd72ef-b063-43c7-ae83-84dd5d2389c2", - "name": "Spawner (502)", - "location": [638, 866, 23], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8e531f0c-3c40-4567-bc25-d433936fcf2a", - "name": "Spawner (502)", - "location": [123, 647, 36], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, - { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8e7895e4-b062-48a8-bf1e-11bd423cf49f", - "name": "Spawner (502)", - "location": [1146, 516, 21], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TsukiWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8fb6a683-1f7b-4364-9538-2db139f0288e", - "name": "Spawner (502)", - "location": [729, 934, 42], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "904d1a0d-b3b7-43b4-b37f-5c8a61bffbdb", - "name": "Spawner (502)", - "location": [803, 961, 14], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "90b5cbd2-ec33-4b76-ab24-9b3dace6724b", - "name": "Spawner (502)", - "location": [824, 280, 33], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, - { "name": "EvilMage", "maxCount": 4, "probability": 100 }, - { "name": "FanDancer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "91372a1f-eb92-49f7-b7ae-25c8fb221855", - "name": "Spawner (502)", - "location": [1051, 473, 7], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "919574b9-7243-4843-94d0-cb4f5b4d0f1b", - "name": "Spawner (502)", - "location": [594, 362, 22], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93a819a9-3b0f-40bd-b923-20ed865b6d0f", - "name": "Spawner (502)", - "location": [208, 641, 36], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "9412516d-cb2a-4ea7-a03d-9ea7354b5437", "name": "Spawner (502)", "location": [879, 128, 44], @@ -2062,1478 +3263,69 @@ ] }, { - "$type": "Spawner", - "guid": "9723d0f8-bdca-4e26-a59c-750042c787a3", + "type": "Spawner", + "guid": "2a2f5196-88ac-4cc7-9f02-d08890632f18", "name": "Spawner (502)", - "location": [666, 896, 28], + "location": [914, 55, 36], "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "97c87b67-b804-4135-8bbf-80e2e1595bf2", - "name": "Spawner (502)", - "location": [992, 383, 5], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "97ce7efd-4bea-4256-aec4-dde1404c559b", - "name": "Spawner (502)", - "location": [1196, 507, 26], - "map": "Tokuno", - "count": 4, + "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Reaper", "maxCount": 4, "probability": 100 }, - { "name": "KazeKemono", "maxCount": 4, "probability": 100 } + { "name": "IceElemental", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "98e06265-c66b-426c-87f1-fa8bcfbfbd18", + "type": "Spawner", + "guid": "63477a04-4f55-4c09-b594-0031d9d4e25d", "name": "Spawner (502)", - "location": [892, 391, 14], + "location": [947, 103, 46], "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98f4d240-c7db-4d14-ad8b-9b8fdb381d82", - "name": "Spawner (502)", - "location": [528, 552, 32], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "LesserHiryu", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "990e1da9-704d-4e3d-b1a6-9180cea7dabe", - "name": "Spawner (502)", - "location": [771, 979, 29], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "997db593-15d5-4963-9659-0a372b565d86", - "name": "Spawner (502)", - "location": [533, 991, 53], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "99dd39c0-83e8-4f4d-a661-eb6bee0601b3", - "name": "Spawner (502)", - "location": [159, 688, 24], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b5e2d60-bf93-418d-a447-fa42e903a4e4", - "name": "Spawner (502)", - "location": [190, 856, 52], - "map": "Tokuno", - "count": 6, + "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }, - { "name": "KazeKemono", "maxCount": 6, "probability": 100 } + { "name": "IceElemental", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "9cc378fc-7158-4e04-83ad-895160e6d1ec", + "type": "Spawner", + "guid": "3416dcdb-aebf-4f59-8158-bd16a36d460c", "name": "Spawner (502)", - "location": [918, 439, 14], + "location": [976, 144, 35], "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9d0a3d34-a633-4d61-9d84-b2df29ae7051", - "name": "Spawner (502)", - "location": [192, 770, 56], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Mongbat", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9d0b815f-1f4b-499f-9c0d-4cfc20a62983", - "name": "Spawner (502)", - "location": [248, 928, 36], - "map": "Tokuno", - "count": 6, + "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }, - { "name": "KazeKemono", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9ed6e580-23db-415f-a93d-4a1841495166", - "name": "Spawner (502)", - "location": [668, 986, 46], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a25ae4f2-e3d4-4c6c-ae56-10c00f828436", - "name": "Spawner (502)", - "location": [566, 430, 35], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a2b02241-de57-4e42-908a-d89b90ae45d3", - "name": "Spawner (502)", - "location": [725, 823, 24], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a30ef889-c94e-4870-a1c7-6bc38738919d", - "name": "Spawner (502)", - "location": [1012, 631, 11], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "LesserHiryu", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a4c44311-a4b2-4bc4-8533-2723551964b5", - "name": "Spawner (502)", - "location": [765, 189, 42], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, - { "name": "EvilMage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a5228c3d-ef03-46e5-9d44-8b2e223eef4d", - "name": "Spawner (502)", - "location": [1176, 701, 72], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "HellHound", "maxCount": 4, "probability": 100 }, - { "name": "FireElemental", "maxCount": 4, "probability": 100 }, - { "name": "LavaSerpent", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a5316c66-ce00-4cd1-9151-7508e8d0b6e8", - "name": "Spawner (502)", - "location": [1134, 705, 72], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "HellHound", "maxCount": 4, "probability": 100 }, - { "name": "FireElemental", "maxCount": 4, "probability": 100 }, - { "name": "LavaSerpent", "maxCount": 4, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a5616fa0-d8b0-4711-bab9-34bacc355ed0", - "name": "Spawner (502)", - "location": [124, 646, 37], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a83437ce-696c-4d66-93e7-90777f276afb", - "name": "Spawner (502)", - "location": [360, 466, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8390df1-8ea6-42e6-9d13-e09159f03a0e", - "name": "Spawner (502)", - "location": [1344, 774, 19], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "LesserHiryu", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8f16a28-29cc-41c2-a79e-be9e10a32363", - "name": "Spawner (502)", - "location": [1377, 882, 21], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "LavaSerpent", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa3b9c76-9b9c-44f7-94cf-43c7b90277a7", - "name": "Spawner (502)", - "location": [769, 152, 12], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, - { "name": "EvilMage", "maxCount": 4, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa9544d7-3f07-4e3f-b2e1-0a37763cd99b", - "name": "Spawner (502)", - "location": [384, 460, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ace8cee6-59ca-4e3c-90a7-1285a1758f14", - "name": "Spawner (502)", - "location": [1184, 616, 19], - "map": "Tokuno", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "LavaSerpent", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "adafc69d-7044-4319-8e4a-6e729aabfba0", - "name": "Spawner (502)", - "location": [773, 229, 64], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, - { "name": "EvilMage", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae8aaaa8-249c-4994-b84d-c27860c04b12", - "name": "Spawner (502)", - "location": [201, 731, 30], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b0318874-b52d-4005-aaf0-540bce7fc3e9", - "name": "Spawner (502)", - "location": [1247, 791, 6], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "LesserHiryu", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b2ac0fff-06ad-438a-a365-0bb7ad3502a6", - "name": "Spawner (502)", - "location": [562, 984, 43], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b332005a-d3b2-4d5a-a527-6858e5197c0b", - "name": "Spawner (502)", - "location": [819, 902, 27], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4421adb-275f-4462-95dc-2d8bcd981471", - "name": "Spawner (502)", - "location": [1184, 837, 36], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "LesserHiryu", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4bf88f4-306f-49f5-a2ab-ef5412ede34e", - "name": "Spawner (502)", - "location": [517, 513, 32], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BakeKitsune", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b504e238-76e5-4634-a3cc-deb3b3ce3db2", - "name": "Spawner (502)", - "location": [997, 407, -2], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b6be5e10-9a08-4766-beb3-e9a445d4ae57", - "name": "Spawner (502)", - "location": [208, 936, 43], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b818a6a6-2295-402a-912f-a3c7b680d71a", - "name": "Spawner (502)", - "location": [1062, 469, 8], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DireWolf", "maxCount": 4, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bbf25923-04a3-4b44-9e43-6199ea5a5dd1", - "name": "Spawner (502)", - "location": [1321, 619, 38], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bc472613-2b05-41aa-af9a-db4ef9e40fd0", - "name": "Spawner (502)", - "location": [284, 423, 31], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd996179-baa6-48a6-b065-5371b0592cfd", - "name": "Spawner (502)", - "location": [688, 979, 44], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0f9cdb5-34f3-470e-9080-e375959d95e1", - "name": "Spawner (502)", - "location": [1285, 573, 30], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c1966d23-2f5c-4a49-bc2c-b18756f292c3", - "name": "Spawner (502)", - "location": [186, 534, 41], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Ronin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c1afa53a-2806-4717-9ce7-04b6ac4afeab", - "name": "Spawner (502)", - "location": [907, 292, 19], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, - { "name": "Ronin", "maxCount": 4, "probability": 100 }, - { "name": "FanDancer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c1c5f957-9c22-4839-9b22-1be66ab4a078", - "name": "Spawner (502)", - "location": [742, 1012, 33], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2d2e12f-2919-4097-81b2-69c88f8d4f63", - "name": "Spawner (502)", - "location": [526, 889, 11], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c41c2c47-c049-41b3-b4cf-b48cc130a9e0", - "name": "Spawner (502)", - "location": [619, 981, 45], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c80f169f-7335-426e-a51c-ca1771920137", - "name": "Spawner (502)", - "location": [502, 553, 32], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Oni", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cabd6472-2187-4078-85e1-437ab1d2d0b8", - "name": "Spawner (502)", - "location": [243, 1031, 13], - "map": "Tokuno", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 12, "probability": 100 }, - { "name": "Lizardman", "maxCount": 12, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cb28888e-536f-4588-87ed-32e0151c3557", - "name": "Spawner (502)", - "location": [1052, 976, 26], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cba2353c-6c72-4abe-b04e-5dbc6fc24027", - "name": "Spawner (502)", - "location": [454, 374, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 8, "probability": 100 }, - { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd76c01b-91e2-4a35-b2d2-c30a7ddfe78a", - "name": "Spawner (502)", - "location": [931, 833, 23], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ceff3a89-0382-4aa9-b03d-e5935edcafe7", - "name": "Spawner (502)", - "location": [612, 888, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 }, - { "name": "Shade", "maxCount": 4, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cfdb9550-70ee-48ef-a165-4dd2b82a5ace", - "name": "Spawner (502)", - "location": [1092, 980, 33], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d0734d58-9c91-45c2-8b51-69d73217e9d7", - "name": "Spawner (502)", - "location": [526, 299, 26], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Yamandon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3042275-6dc0-4a32-b09f-01d26bbb2c29", - "name": "Spawner (502)", - "location": [1197, 813, 27], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Hiryu", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d344dbc9-d8ce-4502-bd9d-09be8e5e1286", - "name": "Spawner (502)", - "location": [472, 336, 38], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d34f4cf7-eecf-4074-82e9-065d8aa94c1a", - "name": "Spawner (502)", - "location": [265, 406, 22], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "EliteNinja", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d393499a-3788-4887-b582-2903c0ae3c16", - "name": "Spawner (502)", - "location": [980, 815, 15], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, - { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 }, - { "name": "DreadSpider", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3d6ff67-6800-4108-9039-071f96f48955", - "name": "Spawner (502)", - "location": [1092, 563, 22], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TsukiWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d46fba23-4a24-4736-8701-daa96189ff9f", - "name": "Spawner (502)", - "location": [1343, 654, 35], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "LesserHiryu", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d7641cb5-bb06-4d5a-9b9d-24413ae09e8c", - "name": "Spawner (502)", - "location": [853, 386, 19], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Kappa", "maxCount": 4, "probability": 100 }, - { "name": "Ronin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d935aa73-025f-4c79-92df-f46b642cda74", - "name": "Spawner (502)", - "location": [921, 593, 20], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, - { "name": "Sheep", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "db468eba-47a3-46c1-b47a-c2179f0b2a5f", - "name": "Spawner (502)", - "location": [1215, 1086, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ddecebbf-fc04-45ad-9eea-328790b80fc0", - "name": "Spawner (502)", - "location": [715, 831, 36], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "de26b10f-8b26-4f72-92aa-058a19600dd2", - "name": "Spawner (502)", - "location": [771, 824, 16], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "deb29408-0b0a-4bbe-bfee-962353f80e94", - "name": "Spawner (502)", - "location": [581, 953, 33], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 }, - { "name": "Shade", "maxCount": 4, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "defa7e90-33db-45c3-8ef7-6f5b153bf4cd", - "name": "Spawner (502)", - "location": [511, 956, 27], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e2ab6de1-7751-4971-b901-3fb6f9ad2a2c", - "name": "Spawner (502)", - "location": [554, 476, 32], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BakeKitsune", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e396724d-7481-4cb6-9302-77ed6dfb8b98", - "name": "Spawner (502)", - "location": [206, 891, 56], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e5ec5310-19bc-428f-92d3-b6e972280758", - "name": "Spawner (502)", - "location": [1086, 910, 61], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Kappa", "maxCount": 4, "probability": 100 }, - { "name": "EliteNinja", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e60bef67-7439-47c5-950a-4d6ba75f9456", - "name": "Spawner (502)", - "location": [930, 405, 11], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e6108ac2-0a40-4c99-a006-ee8b9d3950b9", - "name": "Spawner (502)", - "location": [418, 463, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e6218636-c822-4e08-9b7c-1355ccae6ccf", - "name": "Spawner (502)", - "location": [615, 556, 37], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, - { "name": "HellCat", "maxCount": 8, "probability": 100 }, - { "name": "KazeKemono", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e84fef60-0b86-48d0-8e63-b754a89b1579", - "name": "Spawner (502)", - "location": [677, 446, 31], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "RevenantLion", "maxCount": 4, "probability": 100 }, - { "name": "BakeKitsune", "maxCount": 4, "probability": 100 }, - { "name": "Oni", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e8727ee4-86b2-4ac0-bff7-bd503904199f", - "name": "Spawner (502)", - "location": [637, 357, 32], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "RevenantLion", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e9149d3f-2c41-4774-9758-1f8f71b1598e", - "name": "Spawner (502)", - "location": [1311, 776, 10], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Hiryu", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e9314780-e6f4-4589-a64b-cd470dc5bb32", - "name": "Spawner (502)", - "location": [1021, 802, 0], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, - { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e9bf85cd-f6ef-4207-852e-0c8128711512", - "name": "Spawner (502)", - "location": [927, 451, 14], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e9f063b7-3bd6-4490-804e-1dcedcd98cc0", - "name": "Spawner (502)", - "location": [198, 924, 42], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }, - { "name": "KazeKemono", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eb40a1dc-8905-430d-a5b6-180d5629d0a2", - "name": "Spawner (502)", - "location": [294, 748, 55], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eb58e0a4-29a4-4f28-a54b-226136752d59", - "name": "Spawner (502)", - "location": [770, 875, 30], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec646b02-cb01-4fba-b336-7d617095b3d3", - "name": "Spawner (502)", - "location": [166, 583, 49], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Yamandon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ed3f775c-f53d-46b2-8a53-bc325a2cb088", - "name": "Spawner (502)", - "location": [794, 890, 31], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ee8bcfb4-c8aa-46d8-a29c-3350388df282", - "name": "Spawner (502)", - "location": [573, 933, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 }, - { "name": "Shade", "maxCount": 4, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f1fa14d5-38a8-41b5-8deb-d256e37f49d7", - "name": "Spawner (502)", - "location": [1354, 700, 33], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EliteNinja", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f314eaf0-1752-4943-82f0-89288cf6b903", - "name": "Spawner (502)", - "location": [932, 685, 12], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "KazeKemono", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f9bbad6f-cb96-4a0d-90dd-5711a4b19ec7", - "name": "Spawner (502)", - "location": [1086, 452, 14], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 }, - { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, - { "name": "Lizardman", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fa991041-da28-4f4e-980c-8034ec03aa57", - "name": "Spawner (502)", - "location": [1105, 753, 44], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "HellCat", "maxCount": 4, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fb71c80f-279b-4f05-8037-e53f05fef9f4", - "name": "Spawner (502)", - "location": [289, 477, 32], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "EliteNinja", "maxCount": 1, "probability": 100 } + { "name": "IceElemental", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/tokuno/TownsLife.json b/Distribution/Data/Spawns/post-uoml/tokuno/TownsLife.json similarity index 88% rename from Distribution/Data/Spawns/shared/tokuno/TownsLife.json rename to Distribution/Data/Spawns/post-uoml/tokuno/TownsLife.json index 95e18ac99..f8dfd18ae 100644 --- a/Distribution/Data/Spawns/shared/tokuno/TownsLife.json +++ b/Distribution/Data/Spawns/post-uoml/tokuno/TownsLife.json @@ -1,221 +1,6 @@ [ { - "$type": "Spawner", - "guid": "03f1632a-1fc5-4132-92a7-288b4baf9ae9", - "name": "Spawner (503)", - "location": [744, 1347, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Sheep", "maxCount": 1, "probability": 100 }, - { "name": "Dog", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Rat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "217af4ba-df3f-4134-b360-8f8f921e8a01", - "name": "Spawner (503)", - "location": [721, 1274, 25], - "map": "Tokuno", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Dog", "maxCount": 1, "probability": 100 }, - { "name": "Cat", "maxCount": 1, "probability": 100 }, - { "name": "Rat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "366108fc-b366-4620-b8bb-4d04014d40b7", - "name": "Spawner (503)", - "location": [625, 1317, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 2, "probability": 100 }, - { "name": "Goat", "maxCount": 1, "probability": 100 }, - { "name": "Rabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3cc46920-b82d-4c3b-b622-611f7b416b66", - "name": "Spawner (503)", - "location": [803, 1338, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3eced8bf-e4d6-484b-ae5f-6519e827a787", - "name": "Spawner (503)", - "location": [679, 1224, 25], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40d3a681-f9b5-4866-b6c2-93aa8d211660", - "name": "Spawner (503)", - "location": [834, 1351, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40d40ab7-a774-4443-8225-790b81878d3a", - "name": "Spawner (503)", - "location": [768, 1192, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Cat", "maxCount": 1, "probability": 100 }, - { "name": "Dog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "42f392a2-faec-48fe-b8fa-44c394ce4f31", - "name": "Spawner (503)", - "location": [844, 1318, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Crane", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, - { "name": "Dog", "maxCount": 1, "probability": 100 }, - { "name": "Pig", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4400a7f9-33b7-4f12-993e-6370e8422577", - "name": "Spawner (503)", - "location": [812, 1222, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Cat", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "59135bf6-67a7-42db-ae76-2d9d53ee8d6b", - "name": "Spawner (503)", - "location": [628, 1310, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a5e3558-be1a-49cc-a5cc-201c160e6c7b", - "name": "Spawner (503)", - "location": [711, 1322, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Dog", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Crane", "maxCount": 2, "probability": 100 }, - { "name": "Sheep", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "63828858-74cb-4daa-a2e3-d79928cd539d", - "name": "Spawner (503)", - "location": [630, 1225, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 }, - { "name": "Crane", "maxCount": 3, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "660e512a-833e-42d2-b13d-8d9aee2b8e68", "name": "Spawner (503)", "location": [667, 1343, 25], @@ -226,28 +11,10 @@ "team": 0, "homeRange": 20, "walkingRange": 20, - "entries": [ - { "name": "Rabbit", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Rabbit", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "787cabab-33ea-4938-a32d-5f3443af22c4", - "name": "Spawner (503)", - "location": [712, 1238, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Cat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "7c22ce54-d85a-48c0-8388-25f26d1c7904", "name": "Spawner (503)", "location": [756, 1236, 25], @@ -266,7 +33,21 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "787cabab-33ea-4938-a32d-5f3443af22c4", + "name": "Spawner (503)", + "location": [712, 1238, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Cat", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "8065872f-41df-44a9-b382-d5d16e9f31da", "name": "Spawner (503)", "location": [683, 1231, 25], @@ -277,12 +58,170 @@ "team": 0, "homeRange": 20, "walkingRange": 20, + "entries": [{ "name": "Rat", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3eced8bf-e4d6-484b-ae5f-6519e827a787", + "name": "Spawner (503)", + "location": [679, 1224, 25], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dbffb36b-2e05-4968-96f6-c8f53ddeb50a", + "name": "Spawner (503)", + "location": [706, 1208, 25], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "217af4ba-df3f-4134-b360-8f8f921e8a01", + "name": "Spawner (503)", + "location": [721, 1274, 25], + "map": "Tokuno", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, { "name": "Rat", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "9780bf47-bc15-4bdd-8dc6-0a5ec2abddea", + "name": "Spawner (503)", + "location": [740, 1284, 25], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ae3ea0e1-e260-4f24-a0f2-7a617cbe7bbe", + "name": "Spawner (503)", + "location": [713, 1351, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 2, "probability": 100 }, + { "name": "Cat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9dd4287-6d11-4e50-b4b1-834c431ee60b", + "name": "Spawner (503)", + "location": [710, 1350, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb8699ff-ca4c-44b1-9dd8-422e6e31fbbf", + "name": "Spawner (503)", + "location": [636, 1331, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "Pig", "maxCount": 1, "probability": 100 }, + { "name": "Crane", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59135bf6-67a7-42db-ae76-2d9d53ee8d6b", + "name": "Spawner (503)", + "location": [628, 1310, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "366108fc-b366-4620-b8bb-4d04014d40b7", + "name": "Spawner (503)", + "location": [625, 1317, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 2, "probability": 100 }, + { "name": "Goat", "maxCount": 1, "probability": 100 }, + { "name": "Rabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63828858-74cb-4daa-a2e3-d79928cd539d", + "name": "Spawner (503)", + "location": [630, 1225, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "Crane", "maxCount": 3, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "8ca277a3-a47d-4c33-8470-60d67d1fb2c7", "name": "Spawner (503)", "location": [628, 1272, 25], @@ -300,7 +239,81 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "40d40ab7-a774-4443-8225-790b81878d3a", + "name": "Spawner (503)", + "location": [768, 1192, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3688f98-8e8b-4100-8952-4f90020ab8ae", + "name": "Spawner (503)", + "location": [857, 1166, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Sheep", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c7bce2da-6a6c-4d0f-9c79-70c5a2895962", + "name": "Spawner (503)", + "location": [853, 1287, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Sheep", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e828c6c6-2867-4d26-a77a-ab581c4a7dcc", + "name": "Spawner (503)", + "location": [843, 1247, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Sheep", "maxCount": 2, "probability": 100 }, + { "name": "Pig", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "962520ff-e7ec-4187-b385-5550af5428cb", "name": "Spawner (503)", "location": [844, 1198, 25], @@ -320,23 +333,25 @@ ] }, { - "$type": "Spawner", - "guid": "9780bf47-bc15-4bdd-8dc6-0a5ec2abddea", + "type": "Spawner", + "guid": "4400a7f9-33b7-4f12-993e-6370e8422577", "name": "Spawner (503)", - "location": [740, 1284, 25], + "location": [812, 1222, 25], "map": "Tokuno", - "count": 2, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 20, "walkingRange": 20, "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 } + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9bf1fbe7-640a-44fe-89c5-b8771032d64b", "name": "Spawner (503)", "location": [807, 1287, 25], @@ -355,30 +370,10 @@ ] }, { - "$type": "Spawner", - "guid": "ae3ea0e1-e260-4f24-a0f2-7a617cbe7bbe", + "type": "Spawner", + "guid": "42f392a2-faec-48fe-b8fa-44c394ce4f31", "name": "Spawner (503)", - "location": [713, 1351, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 693, "y": 1331, "z": -128 }, - "end": { "x": 733, "y": 1371, "z": 40 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 2, "probability": 100 }, - { "name": "Cat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c7bce2da-6a6c-4d0f-9c79-70c5a2895962", - "name": "Spawner (503)", - "location": [853, 1287, 25], + "location": [844, 1318, 25], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", @@ -387,17 +382,32 @@ "homeRange": 20, "walkingRange": 20, "entries": [ - { "name": "Sheep", "maxCount": 1, "probability": 100 }, - { "name": "Rat", "maxCount": 1, "probability": 100 }, { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Dog", "maxCount": 1, "probability": 100 } + { "name": "Crane", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Pig", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "cb8699ff-ca4c-44b1-9dd8-422e6e31fbbf", + "type": "Spawner", + "guid": "40d3a681-f9b5-4866-b6c2-93aa8d211660", "name": "Spawner (503)", - "location": [636, 1331, 25], + "location": [834, 1351, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3cc46920-b82d-4c3b-b622-611f7b416b66", + "name": "Spawner (503)", + "location": [803, 1338, 25], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", @@ -406,48 +416,12 @@ "homeRange": 20, "walkingRange": 20, "entries": [ - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "Pig", "maxCount": 1, "probability": 100 }, - { "name": "Crane", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9dd4287-6d11-4e50-b4b1-834c431ee60b", - "name": "Spawner (503)", - "location": [710, 1350, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 690, "y": 1330, "z": -128 }, - "end": { "x": 730, "y": 1370, "z": 40 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dbffb36b-2e05-4968-96f6-c8f53ddeb50a", - "name": "Spawner (503)", - "location": [706, 1208, 25], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ + { "name": "Rat", "maxCount": 2, "probability": 100 }, { "name": "Bird", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "df568bbb-9e8c-4594-a32b-85dd93540f10", "name": "Spawner (503)", "location": [770, 1344, 25], @@ -466,27 +440,10 @@ ] }, { - "$type": "Spawner", - "guid": "e828c6c6-2867-4d26-a77a-ab581c4a7dcc", + "type": "Spawner", + "guid": "03f1632a-1fc5-4132-92a7-288b4baf9ae9", "name": "Spawner (503)", - "location": [843, 1247, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Sheep", "maxCount": 2, "probability": 100 }, - { "name": "Pig", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f3688f98-8e8b-4100-8952-4f90020ab8ae", - "name": "Spawner (503)", - "location": [857, 1166, 25], + "location": [744, 1347, 25], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", @@ -496,13 +453,32 @@ "walkingRange": 20, "entries": [ { "name": "Sheep", "maxCount": 1, "probability": 100 }, - { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Dog", "maxCount": 1, "probability": 100 } + { "name": "Rat", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "5a5e3558-be1a-49cc-a5cc-201c160e6c7b", + "name": "Spawner (503)", + "location": [711, 1322, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Dog", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Crane", "maxCount": 2, "probability": 100 }, + { "name": "Sheep", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f4d88eea-fd00-4029-bf05-960a4a03a082", "name": "Spawner (503)", "location": [690, 1288, 25], diff --git a/Distribution/Data/Spawns/shared/tokuno/Vendors.json b/Distribution/Data/Spawns/post-uoml/tokuno/Vendors.json similarity index 72% rename from Distribution/Data/Spawns/shared/tokuno/Vendors.json rename to Distribution/Data/Spawns/post-uoml/tokuno/Vendors.json index 4521e62a4..ab443c4b0 100644 --- a/Distribution/Data/Spawns/shared/tokuno/Vendors.json +++ b/Distribution/Data/Spawns/post-uoml/tokuno/Vendors.json @@ -1,25 +1,9 @@ [ { - "$type": "Spawner", - "guid": "03112703-010a-4495-90de-3dceafabf6bb", + "type": "Spawner", + "guid": "698d030e-cf3f-40fd-be6e-4a92866dbfb1", "name": "Spawner (504)", - "location": [502, 589, 24], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0360875a-9408-4f4a-a42c-200f359baccb", - "name": "Spawner (504)", - "location": [756, 1302, 25], + "location": [668, 1254, 25], "map": "Tokuno", "count": 1, "minDelay": "00:05:00", @@ -27,92 +11,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BardGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "1159a610-a01a-4cac-841f-ca37fcf2ab21", - "name": "Spawner (504)", - "location": [1177, 985, 38], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "117e79c9-e92c-4094-89af-ceef02b6d875", - "name": "Spawner (504)", - "location": [724, 1297, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11f9b7fa-b1ea-4a93-848a-5339c852afa5", - "name": "Spawner (504)", - "location": [695, 1297, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1457ebfc-398e-415e-b091-20427af4fb70", - "name": "Spawner (504)", - "location": [691, 1264, 47], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "152e7156-8c9c-4aae-a560-7972868cbc57", - "name": "Spawner (504)", - "location": [789, 1278, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "15590b9b-8815-4906-8e9e-d08a54b6a2f1", "name": "Spawner (504)", "location": [773, 1248, 26], @@ -123,15 +25,13 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "16523597-999a-4504-aa59-7b01c881978d", + "type": "Spawner", + "guid": "f44766ae-31e3-46fa-94ce-869a53621012", "name": "Spawner (504)", - "location": [689, 1255, 25], + "location": [662, 1210, 25], "map": "Tokuno", "count": 1, "minDelay": "00:05:00", @@ -139,15 +39,31 @@ "team": 0, "homeRange": 0, "walkingRange": 4, + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bdc0117b-2e5b-458b-90ca-0f3bfa5489e8", + "name": "Spawner (504)", + "location": [703, 1262, 25], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 10, "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } + { "name": "Baker", "maxCount": 1, "probability": 100 }, + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "1990cbea-3f71-44da-aec2-0af4e07e3618", + "type": "Spawner", + "guid": "840e2e09-7a7e-43cc-a5d0-a5be3d4e00d6", "name": "Spawner (504)", - "location": [702, 1298, 25], + "location": [669, 1266, 47], "map": "Tokuno", "count": 1, "minDelay": "00:05:00", @@ -155,15 +71,13 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Alchemist", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Shipwright", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "1b386724-c26f-4714-80b3-edaaa46b3ab4", + "type": "Spawner", + "guid": "97d04faf-bf2f-4085-bdb9-2a6538c4a97a", "name": "Spawner (504)", - "location": [683, 1298, 25], + "location": [720, 1211, 25], "map": "Tokuno", "count": 1, "minDelay": "00:05:00", @@ -171,15 +85,27 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Tanner", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "1bef801b-5ac8-4cde-a3bd-71fff904d997", + "type": "Spawner", + "guid": "a4b62432-203b-4b3e-894c-651e62d68567", "name": "Spawner (504)", - "location": [679, 1207, 25], + "location": [739, 1223, 45], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Carpenter", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8c714386-6b61-4f4e-9eec-f75cd78010a6", + "name": "Spawner (504)", + "location": [732, 1256, 30], "map": "Tokuno", "count": 1, "minDelay": "00:05:00", @@ -187,12 +113,52 @@ "team": 0, "homeRange": 0, "walkingRange": 0, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "f6481ae3-b345-40ab-aaa2-2eea59aee211", + "name": "Spawner (504)", + "location": [781, 1301, 26], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "152e7156-8c9c-4aae-a560-7972868cbc57", + "name": "Spawner (504)", + "location": [789, 1278, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0360875a-9408-4f4a-a42c-200f359baccb", + "name": "Spawner (504)", + "location": [756, 1302, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Armorer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "1f9e3288-f988-4ea3-95e8-251aa38d064c", "name": "Spawner (504)", "location": [692, 1213, 25], @@ -203,12 +169,150 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TavernKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "1bef801b-5ac8-4cde-a3bd-71fff904d997", + "name": "Spawner (504)", + "location": [679, 1207, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "33f10364-0097-42d5-95b1-c3f73750fda0", + "name": "Spawner (504)", + "location": [720, 1213, 45], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Tinker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e081b4f9-5c48-4808-82e7-f8bb95309a19", + "name": "Spawner (504)", + "location": [744, 1219, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Tailor", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1457ebfc-398e-415e-b091-20427af4fb70", + "name": "Spawner (504)", + "location": [691, 1264, 47], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Mapmaker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f50b6789-7be4-43f0-a1be-c53e85a6997b", + "name": "Spawner (504)", + "location": [675, 1265, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Bard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "16523597-999a-4504-aa59-7b01c881978d", + "name": "Spawner (504)", + "location": [689, 1255, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1b386724-c26f-4714-80b3-edaaa46b3ab4", + "name": "Spawner (504)", + "location": [683, 1298, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Herbalist", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1990cbea-3f71-44da-aec2-0af4e07e3618", + "name": "Spawner (504)", + "location": [702, 1298, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Alchemist", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11f9b7fa-b1ea-4a93-848a-5339c852afa5", + "name": "Spawner (504)", + "location": [695, 1297, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Mage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "117e79c9-e92c-4094-89af-ceef02b6d875", + "name": "Spawner (504)", + "location": [724, 1297, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "21467215-aae1-4e1c-bf72-1137b91b71eb", "name": "Spawner (504)", "location": [730, 1282, 25], @@ -219,28 +323,125 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Provisioner", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "294130b1-fd5a-4cef-9c68-a5491f5e59c6", + "type": "Spawner", + "guid": "5ada367e-0bcb-487e-9e44-ef259bfec2c7", "name": "Spawner (504)", - "location": [226, 786, 57], + "location": [781, 1219, 25], "map": "Tokuno", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 3, - "walkingRange": 3, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Healer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d001030a-99d7-4146-83f9-b5e1aff453b0", + "name": "Spawner (504)", + "location": [729, 1249, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Banker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "448a5e8c-0682-488b-ba85-4a457ed8329c", + "name": "Spawner (504)", + "location": [742, 1304, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Armorer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d64f79d1-bb69-4aab-8099-04cc92244b60", + "name": "Spawner (504)", + "location": [806, 1308, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Weaponsmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7dad1cc2-dfab-490e-915f-8d00ed39f14c", + "name": "Spawner (504)", + "location": [749, 1303, 25], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "67fe5dc9-67d2-4e5f-afab-03691f497fed", + "name": "Spawner (504)", + "location": [661, 1209, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dc6b9648-9165-491e-87b2-f5950d8e38ad", + "name": "Spawner (504)", + "location": [744, 1210, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Weaver", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4b940cb-17ef-4234-842c-2bdcbd7827f0", + "name": "Spawner (504)", + "location": [779, 1259, 26], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Weaponsmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "30f1eaf5-633e-494a-aadb-2997f3e8d0a4", "name": "Spawner (504)", "location": [735, 1256, 30], @@ -260,490 +461,7 @@ ] }, { - "$type": "Spawner", - "guid": "33f10364-0097-42d5-95b1-c3f73750fda0", - "name": "Spawner (504)", - "location": [720, 1213, 45], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3df1960a-2e81-4997-ab85-3f1062065c7d", - "name": "Spawner (504)", - "location": [763, 247, 71], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "448a5e8c-0682-488b-ba85-4a457ed8329c", - "name": "Spawner (504)", - "location": [742, 1304, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4830fbdc-9c83-4e22-af7a-cd868807eb9c", - "name": "Spawner (504)", - "location": [297, 637, 15], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "57f4d4ba-3841-4d12-bec6-37b059c49840", - "name": "Spawner (504)", - "location": [248, 1089, 10], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5ada367e-0bcb-487e-9e44-ef259bfec2c7", - "name": "Spawner (504)", - "location": [781, 1219, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "67fe5dc9-67d2-4e5f-afab-03691f497fed", - "name": "Spawner (504)", - "location": [661, 1209, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "698d030e-cf3f-40fd-be6e-4a92866dbfb1", - "name": "Spawner (504)", - "location": [668, 1254, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7dad1cc2-dfab-490e-915f-8d00ed39f14c", - "name": "Spawner (504)", - "location": [749, 1303, 25], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "80122c8e-85c4-41a7-a347-d8c0074e6031", - "name": "Spawner (504)", - "location": [371, 431, 32], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "840e2e09-7a7e-43cc-a5d0-a5be3d4e00d6", - "name": "Spawner (504)", - "location": [669, 1266, 47], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "876d5b73-9f59-4446-8144-dd997bf93916", - "name": "Spawner (504)", - "location": [1152, 936, 65], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8c714386-6b61-4f4e-9eec-f75cd78010a6", - "name": "Spawner (504)", - "location": [732, 1256, 30], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 0, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "901b6857-11f1-4c9f-bb1f-1399406dc3b6", - "name": "Spawner (504)", - "location": [399, 549, 15], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95447e64-a9d3-45ca-81c7-318441ca97d4", - "name": "Spawner (504)", - "location": [1111, 495, 18], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "97d04faf-bf2f-4085-bdb9-2a6538c4a97a", - "name": "Spawner (504)", - "location": [720, 1211, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a4b62432-203b-4b3e-894c-651e62d68567", - "name": "Spawner (504)", - "location": [739, 1223, 45], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "af820fc2-57d8-4b19-9a54-9bb57f9736cb", - "name": "Spawner (504)", - "location": [1221, 494, 27], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4b940cb-17ef-4234-842c-2bdcbd7827f0", - "name": "Spawner (504)", - "location": [779, 1259, 26], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b5b2efe1-d96b-4d37-ac13-ec6251ad23f9", - "name": "Spawner (504)", - "location": [895, 519, 53], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b65649fc-1b3a-4124-b063-0f0e7b35d3d2", - "name": "Spawner (504)", - "location": [979, 238, 21], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bdc0117b-2e5b-458b-90ca-0f3bfa5489e8", - "name": "Spawner (504)", - "location": [703, 1262, 25], - "map": "Tokuno", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 10, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 }, - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cfc1714e-8851-4cf8-a9a5-bb1d4d9236b0", - "name": "Spawner (504)", - "location": [558, 534, 32], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d001030a-99d7-4146-83f9-b5e1aff453b0", - "name": "Spawner (504)", - "location": [729, 1249, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d64f79d1-bb69-4aab-8099-04cc92244b60", - "name": "Spawner (504)", - "location": [806, 1308, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc6b9648-9165-491e-87b2-f5950d8e38ad", - "name": "Spawner (504)", - "location": [744, 1210, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Weaver", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e081b4f9-5c48-4808-82e7-f8bb95309a19", - "name": "Spawner (504)", - "location": [744, 1219, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f44766ae-31e3-46fa-94ce-869a53621012", - "name": "Spawner (504)", - "location": [662, 1210, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f50b6789-7be4-43f0-a1be-c53e85a6997b", - "name": "Spawner (504)", - "location": [675, 1265, 25], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Bard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f6481ae3-b345-40ab-aaa2-2eea59aee211", - "name": "Spawner (504)", - "location": [781, 1301, 26], - "map": "Tokuno", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "fb70140d-b0d9-41c7-9975-c8136c4ee686", "name": "Spawner (504)", "location": [1166, 802, 27], @@ -754,8 +472,202 @@ "team": 0, "homeRange": 3, "walkingRange": 3, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cfc1714e-8851-4cf8-a9a5-bb1d4d9236b0", + "name": "Spawner (504)", + "location": [558, 534, 32], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "03112703-010a-4495-90de-3dceafabf6bb", + "name": "Spawner (504)", + "location": [502, 589, 24], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1159a610-a01a-4cac-841f-ca37fcf2ab21", + "name": "Spawner (504)", + "location": [1177, 985, 38], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "901b6857-11f1-4c9f-bb1f-1399406dc3b6", + "name": "Spawner (504)", + "location": [399, 549, 15], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "294130b1-fd5a-4cef-9c68-a5491f5e59c6", + "name": "Spawner (504)", + "location": [226, 786, 57], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4830fbdc-9c83-4e22-af7a-cd868807eb9c", + "name": "Spawner (504)", + "location": [297, 637, 15], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "80122c8e-85c4-41a7-a347-d8c0074e6031", + "name": "Spawner (504)", + "location": [371, 431, 32], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3df1960a-2e81-4997-ab85-3f1062065c7d", + "name": "Spawner (504)", + "location": [763, 247, 71], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "95447e64-a9d3-45ca-81c7-318441ca97d4", + "name": "Spawner (504)", + "location": [1111, 495, 18], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "af820fc2-57d8-4b19-9a54-9bb57f9736cb", + "name": "Spawner (504)", + "location": [1221, 494, 27], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b5b2efe1-d96b-4d37-ac13-ec6251ad23f9", + "name": "Spawner (504)", + "location": [895, 519, 53], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "876d5b73-9f59-4446-8144-dd997bf93916", + "name": "Spawner (504)", + "location": [1152, 936, 65], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b65649fc-1b3a-4124-b063-0f0e7b35d3d2", + "name": "Spawner (504)", + "location": [979, 238, 21], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "57f4d4ba-3841-4d12-bec6-37b059c49840", + "name": "Spawner (504)", + "location": [248, 1089, 10], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/tokuno/WildLife.json b/Distribution/Data/Spawns/post-uoml/tokuno/WildLife.json similarity index 92% rename from Distribution/Data/Spawns/shared/tokuno/WildLife.json rename to Distribution/Data/Spawns/post-uoml/tokuno/WildLife.json index c19c8a14e..929271c01 100644 --- a/Distribution/Data/Spawns/shared/tokuno/WildLife.json +++ b/Distribution/Data/Spawns/post-uoml/tokuno/WildLife.json @@ -1,481 +1,26 @@ [ { - "$type": "Spawner", - "guid": "025966e3-5dff-48d2-8101-47bb942642d1", + "type": "Spawner", + "guid": "7b7eb7c3-f465-4f75-b739-52557290bd17", "name": "Spawner (505)", - "location": [612, 857, 15], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "07015515-1c9d-4bfd-9199-5a8eeccce91a", - "name": "Spawner (505)", - "location": [580, 1170, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Llama", "maxCount": 8, "probability": 100 }, - { "name": "Rabbit", "maxCount": 8, "probability": 100 }, - { "name": "Dog", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "073bd67c-3055-4bf5-8879-4bca7908de15", - "name": "Spawner (505)", - "location": [874, 971, 45], + "location": [1177, 1002, 34], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0773f05d-07e5-426e-a2e1-85c61ed07de5", - "name": "Spawner (505)", - "location": [472, 336, 38], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 8, "probability": 100 }, - { "name": "Crane", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "09a9d3ff-55d0-4e8c-9fe7-d8458206ec1b", - "name": "Spawner (505)", - "location": [418, 463, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "Mongbat", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 }, - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a15f08c-dfd3-4184-8b1e-e37833f41bc2", - "name": "Spawner (505)", - "location": [856, 324, 37], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a5ac06e-23f0-40c6-a1f4-9961465b6ddc", - "name": "Spawner (505)", - "location": [1176, 701, 72], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "MountainGoat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0c1322bf-5ebb-464a-99e8-c137e14ff1d4", - "name": "Spawner (505)", - "location": [1011, 509, 26], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0c20bc6c-30d5-4b2d-97e3-0db6146a7226", - "name": "Spawner (505)", - "location": [883, 1121, 19], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f2ecc77-7a9d-483c-b2d2-28b021b69767", - "name": "Spawner (505)", - "location": [381, 511, 22], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Gaman", "maxCount": 2, "probability": 100 }, - { "name": "BrownBear", "maxCount": 2, "probability": 100 }, - { "name": "BlackBear", "maxCount": 2, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 } + { "name": "JackRabbit", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "109decf7-c5fa-4839-ab48-51d6e1a0a984", + "type": "Spawner", + "guid": "6177f745-08f2-4d37-a5ae-2f2e3129b903", "name": "Spawner (505)", - "location": [988, 335, 17], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10d55af6-cce8-498b-9c29-4e2713f79d9b", - "name": "Spawner (505)", - "location": [1091, 897, 69], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1155a3a8-591d-47ed-9672-30b5d0d10e5f", - "name": "Spawner (505)", - "location": [572, 1211, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Goat", "maxCount": 8, "probability": 100 }, - { "name": "Sheep", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Rabbit", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11f32a2d-41af-40ea-b076-14452d79a9c9", - "name": "Spawner (505)", - "location": [1092, 1024, 34], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12723266-85ed-413a-bab7-c825593b42bb", - "name": "Spawner (505)", - "location": [504, 1042, 24], - "map": "Tokuno", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15fd3cea-416b-449a-925f-c571ada88282", - "name": "Spawner (505)", - "location": [600, 1198, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Goat", "maxCount": 8, "probability": 100 }, - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "Rabbit", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1882a00b-e0b5-44b2-9a53-ad4faabbd6d8", - "name": "Spawner (505)", - "location": [596, 1289, 21], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Crane", "maxCount": 8, "probability": 100 }, - { "name": "Pig", "maxCount": 8, "probability": 100 }, - { "name": "Goat", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1a38a3b3-ecad-4c71-b4af-6a862e5f1e38", - "name": "Spawner (505)", - "location": [724, 831, 34], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1c39d957-43f3-4e45-9fca-0f82fcd80baf", - "name": "Spawner (505)", - "location": [1074, 806, 23], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "BrownBear", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d903bb8-480f-4d62-b957-ae627809d9d5", - "name": "Spawner (505)", - "location": [190, 856, 52], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "208e7ae6-ff8a-4482-aad8-88fac46e3821", - "name": "Spawner (505)", - "location": [887, 1072, 20], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2112cd3e-2459-4ced-b190-dc6ce7d36d3a", - "name": "Spawner (505)", - "location": [916, 1120, 17], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "Pig", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "221b2448-0059-4522-8cdc-c1f0ea38145a", - "name": "Spawner (505)", - "location": [907, 1143, 11], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Sheep", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "259e2b9a-187b-4009-8e43-277c10cff6b5", - "name": "Spawner (505)", - "location": [210, 1085, 10], - "map": "Tokuno", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 }, - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "GiantRat", "maxCount": 12, "probability": 100 }, - { "name": "BullFrog", "maxCount": 12, "probability": 100 }, - { "name": "GiantToad", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2be2ad47-84cf-4567-abe9-a0eea8a95ee4", - "name": "Spawner (505)", - "location": [232, 1116, 10], - "map": "Tokuno", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 }, - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "GiantRat", "maxCount": 12, "probability": 100 }, - { "name": "BullFrog", "maxCount": 12, "probability": 100 }, - { "name": "GiantToad", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2c461fc3-195d-4f3e-961b-c46b24a68048", - "name": "Spawner (505)", - "location": [307, 572, 33], + "location": [629, 510, 32], "map": "Tokuno", "count": 8, "minDelay": "00:05:00", @@ -484,519 +29,79 @@ "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Cat", "maxCount": 1, "probability": 100 }, - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 1, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a445a5be-f116-48cf-a9bc-0b083796ba9e", + "name": "Spawner (505)", + "location": [1198, 941, 36], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, { "name": "JackRabbit", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "2d1b64ad-e246-4c2f-b1b1-e2b25c6e53e2", + "type": "Spawner", + "guid": "f7998dee-c474-49e9-8833-279d375db898", "name": "Spawner (505)", - "location": [1144, 667, 70], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "MountainGoat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2d8b101e-4530-4396-98db-3b3f0a0e6a23", - "name": "Spawner (505)", - "location": [775, 1127, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, - { "name": "BlackBear", "maxCount": 4, "probability": 100 }, - { "name": "BrownBear", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2dfdc1dd-5220-4169-86e7-4342c692e2f6", - "name": "Spawner (505)", - "location": [192, 1014, 10], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 8, "probability": 100 }, - { "name": "Rat", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e0d9671-9a22-41db-98ef-754e21134ec4", - "name": "Spawner (505)", - "location": [770, 875, 30], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32ba9655-ba7d-4112-9e6a-3f36b34102bd", - "name": "Spawner (505)", - "location": [1122, 501, 19], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "35aa8796-06bb-4243-840f-44d4ce68b110", - "name": "Spawner (505)", - "location": [1181, 547, 23], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "365b6b36-18ef-4ad8-8a0a-896cfdd1ba1b", - "name": "Spawner (505)", - "location": [1133, 1036, 31], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3692b482-e864-4cc3-92f2-02bbbf69c448", - "name": "Spawner (505)", - "location": [534, 1189, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Horse", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "377c039b-1370-49ed-ac1f-b5453a0f98ae", - "name": "Spawner (505)", - "location": [722, 916, 40], + "location": [1141, 894, 57], "map": "Tokuno", "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 6, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3c6b8543-bd53-4cf8-ac52-a20347b89cad", - "name": "Spawner (505)", - "location": [1088, 980, 30], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3dc8cf0c-106b-49a9-92aa-82758885387d", - "name": "Spawner (505)", - "location": [443, 402, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 }, - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e0c0300-cb5b-4a45-a97a-12b40100f047", - "name": "Spawner (505)", - "location": [1039, 952, 24], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e8d2eac-e0f8-484a-8316-ecb5720962ef", - "name": "Spawner (505)", - "location": [454, 374, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 8, "probability": 100 }, - { "name": "Crane", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40d60aa8-583e-474f-a59f-8b86a0233014", - "name": "Spawner (505)", - "location": [206, 891, 56], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ { "name": "Gaman", "maxCount": 1, "probability": 100 }, { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "DireWolf", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a39a0058-8f78-43a4-9028-f509c65ffa7e", + "name": "Spawner (505)", + "location": [623, 551, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, { "name": "BlackBear", "maxCount": 1, "probability": 100 }, { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "Horse", "maxCount": 1, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 1, "probability": 100 } + { "name": "Bird", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "44acca90-d356-438d-9a80-a27d07ecac27", + "type": "Spawner", + "guid": "614d3af0-78c4-4099-9a35-0add14ce3e3f", "name": "Spawner (505)", - "location": [251, 1074, 10], - "map": "Tokuno", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 }, - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "GiantRat", "maxCount": 12, "probability": 100 }, - { "name": "BullFrog", "maxCount": 12, "probability": 100 }, - { "name": "GiantToad", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47507391-0312-43b8-a52f-1da5ef65d134", - "name": "Spawner (505)", - "location": [685, 891, 22], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4943e08b-e268-4035-a091-6f3d05e5aab1", - "name": "Spawner (505)", - "location": [285, 1156, 13], - "map": "Tokuno", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 }, - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "GiantRat", "maxCount": 12, "probability": 100 }, - { "name": "BullFrog", "maxCount": 12, "probability": 100 }, - { "name": "GiantToad", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49dc8190-fb53-4f7a-85c1-4bb0645f7da8", - "name": "Spawner (505)", - "location": [1139, 999, 48], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Horse", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b8f1c02-d05a-4bc9-a013-57e201cca765", - "name": "Spawner (505)", - "location": [960, 293, 29], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4c7e0f56-72ac-454c-9392-b00a2ca81ff0", - "name": "Spawner (505)", - "location": [683, 857, 31], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "JackRabbit", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "503c5b72-b18c-4f8e-bf90-9b161a77db25", - "name": "Spawner (505)", - "location": [364, 565, 15], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 3, "probability": 100 }, - { "name": "Dog", "maxCount": 3, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "504e34ec-555a-4638-aa23-440d042f355a", - "name": "Spawner (505)", - "location": [833, 908, 15], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "512c0d13-c23a-4f39-9dd1-584f708660fb", - "name": "Spawner (505)", - "location": [561, 1101, 33], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "Pig", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "52b4ffec-21bb-4847-ae83-65f2001d581d", - "name": "Spawner (505)", - "location": [1345, 775, 19], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "53507cb5-dd64-4105-8f65-3535910989ea", - "name": "Spawner (505)", - "location": [905, 331, 8], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "536320cb-cd64-4a9b-9f44-6b2c3a6b4770", - "name": "Spawner (505)", - "location": [1255, 866, 12], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54b64e89-5437-4c61-81ee-2adc677a3195", - "name": "Spawner (505)", - "location": [1230, 822, 21], + "location": [1197, 851, 32], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", @@ -1008,91 +113,12 @@ { "name": "Gaman", "maxCount": 1, "probability": 100 }, { "name": "Bird", "maxCount": 1, "probability": 100 }, { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, { "name": "Horse", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "55ccd405-6e56-4289-a671-c1521f8b1b4e", - "name": "Spawner (505)", - "location": [956, 705, 16], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "581859e8-1196-47e2-bc24-1359c385fecb", - "name": "Spawner (505)", - "location": [857, 376, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a91c9b1-2c2c-4e5d-b3ab-98b110013172", - "name": "Spawner (505)", - "location": [566, 1287, 22], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Crane", "maxCount": 8, "probability": 100 }, - { "name": "Rabbit", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5b1a526f-836a-4c63-a353-066cf22828bf", - "name": "Spawner (505)", - "location": [771, 824, 16], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "5ded03e2-db27-4a82-88ec-7274d5521db0", "name": "Spawner (505)", "location": [1230, 895, 22], @@ -1112,7 +138,225 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "536320cb-cd64-4a9b-9f44-6b2c3a6b4770", + "name": "Spawner (505)", + "location": [1255, 866, 12], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54b64e89-5437-4c61-81ee-2adc677a3195", + "name": "Spawner (505)", + "location": [1230, 822, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ced8bd2c-f463-445a-ad5b-90a5b43e0aa6", + "name": "Spawner (505)", + "location": [1289, 843, 4], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e22936fe-88bf-42b6-8563-b4d85f37af0d", + "name": "Spawner (505)", + "location": [1377, 882, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8835e0c4-4f91-493e-a9a5-474c0e5db0b6", + "name": "Spawner (505)", + "location": [1299, 814, 9], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d629be0-95e5-4dec-bb25-5cfdc8339993", + "name": "Spawner (505)", + "location": [518, 329, 15], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Gaman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9306a516-f374-4a37-8188-8ba412962f12", + "name": "Spawner (505)", + "location": [1263, 754, -1], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c444b27-ab3c-4376-aa20-5f39a9623573", + "name": "Spawner (505)", + "location": [1311, 777, 9], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d97602d0-7bee-4c1b-ab0c-be5b52de898c", + "name": "Spawner (505)", + "location": [551, 544, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aaba65e1-c0ef-4788-9167-b7cddede3aa5", + "name": "Spawner (505)", + "location": [502, 532, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "52b4ffec-21bb-4847-ae83-65f2001d581d", + "name": "Spawner (505)", + "location": [1345, 775, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ec9ed72-e359-46ad-afa1-10a09818b0bb", + "name": "Spawner (505)", + "location": [1312, 704, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "5f1c63ab-a484-4556-88c2-2f513b6a73ff", "name": "Spawner (505)", "location": [273, 606, 7], @@ -1134,10 +378,10 @@ ] }, { - "$type": "Spawner", - "guid": "5f4e6a55-2723-4d2e-b192-a8881e2b7c96", + "type": "Spawner", + "guid": "dacf4f2f-fe97-46a9-9dd9-48b3eed8ba3c", "name": "Spawner (505)", - "location": [356, 403, 32], + "location": [505, 474, 31], "map": "Tokuno", "count": 8, "minDelay": "00:05:00", @@ -1146,60 +390,17 @@ "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 }, - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 } + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 2, "probability": 100 }, + { "name": "BrownBear", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "5fddebe2-5f66-4157-a95e-50f1d376ab67", + "type": "Spawner", + "guid": "503c5b72-b18c-4f8e-bf90-9b161a77db25", "name": "Spawner (505)", - "location": [644, 1142, 28], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DireWolf", "maxCount": 6, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 6, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "614d3af0-78c4-4099-9a35-0add14ce3e3f", - "name": "Spawner (505)", - "location": [1197, 851, 32], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "Horse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6177f745-08f2-4d37-a5ae-2f2e3129b903", - "name": "Spawner (505)", - "location": [629, 510, 32], + "location": [364, 565, 15], "map": "Tokuno", "count": 8, "minDelay": "00:05:00", @@ -1208,813 +409,13 @@ "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "MountainGoat", "maxCount": 1, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "62196314-f7e7-42e8-a131-db562b52bee0", - "name": "Spawner (505)", - "location": [879, 1038, 16], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Gaman", "maxCount": 4, "probability": 100 }, - { "name": "Goat", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "651e3dfe-31b6-4a95-9110-5861aac65171", - "name": "Spawner (505)", - "location": [1101, 832, 37], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "BrownBear", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "65473288-20d0-40f6-9da4-ab3c1213c9e1", - "name": "Spawner (505)", - "location": [563, 1133, 43], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b0b9c5e-f925-4f24-97f9-d9c9915d8d8f", - "name": "Spawner (505)", - "location": [687, 439, 26], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Sheep", "maxCount": 8, "probability": 100 }, - { "name": "MountainGoat", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Crane", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6d84bf13-4790-4286-a8dd-ad78174f533a", - "name": "Spawner (505)", - "location": [771, 228, 63], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6ef86062-ac38-4916-ae0c-8e9b7f02b400", - "name": "Spawner (505)", - "location": [565, 447, 31], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Sheep", "maxCount": 8, "probability": 100 }, - { "name": "MountainGoat", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f71ccad-9194-41b5-8af2-819e8d5ff14e", - "name": "Spawner (505)", - "location": [419, 529, 15], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 8, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "783c5a65-45be-49b2-978e-839ee3d8ad42", - "name": "Spawner (505)", - "location": [746, 814, 16], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b655fa8-4c47-4101-8a05-e138b0eb8e14", - "name": "Spawner (505)", - "location": [934, 1233, 19], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b7eb7c3-f465-4f75-b739-52557290bd17", - "name": "Spawner (505)", - "location": [1177, 1002, 34], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Gaman", "maxCount": 3, "probability": 100 }, + { "name": "Dog", "maxCount": 3, "probability": 100 }, { "name": "JackRabbit", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "7bbee32e-82df-4a88-b864-dda360fed83e", - "name": "Spawner (505)", - "location": [143, 131, 0], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c2e0be2-cc73-4518-9c76-3e960cf286b6", - "name": "Spawner (505)", - "location": [547, 1240, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Dog", "maxCount": 8, "probability": 100 }, - { "name": "Cat", "maxCount": 8, "probability": 100 }, - { "name": "Rabbit", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c67a873-1b6e-422d-bce9-5cc93722eecc", - "name": "Spawner (505)", - "location": [243, 1031, 13], - "map": "Tokuno", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 }, - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "GiantRat", "maxCount": 12, "probability": 100 }, - { "name": "BullFrog", "maxCount": 12, "probability": 100 }, - { "name": "GiantToad", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d5217d9-a132-4859-ba9e-af45db26bc7a", - "name": "Spawner (505)", - "location": [146, 109, 0], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Cat", "maxCount": 1, "probability": 100 }, - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "Dog", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7f3e95c9-608e-4e32-95fa-5830bd9f5372", - "name": "Spawner (505)", - "location": [942, 1170, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Sheep", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7faed2d6-3375-4d3d-a635-921e06255853", - "name": "Spawner (505)", - "location": [525, 1263, 24], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 8, "probability": 100 }, - { "name": "Rabbit", "maxCount": 8, "probability": 100 }, - { "name": "Horse", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "811da3e8-b320-416d-936e-505e490d503e", - "name": "Spawner (505)", - "location": [955, 828, 27], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81e3bdc8-9415-461f-b1ab-91824c47f65e", - "name": "Spawner (505)", - "location": [812, 218, 41], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "83a06690-beab-4182-a040-4466d61e8bd3", - "name": "Spawner (505)", - "location": [550, 1092, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8406e7c2-5200-4cc1-b87e-0455f0b66efc", - "name": "Spawner (505)", - "location": [1062, 469, 8], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DireWolf", "maxCount": 4, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84e49574-5215-480c-93e0-98a4320678a1", - "name": "Spawner (505)", - "location": [221, 968, 34], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84e8cdc1-226f-4a5c-9732-628e95c380a8", - "name": "Spawner (505)", - "location": [889, 516, 47], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Sheep", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "855c2b06-17c2-452b-bbd6-bd081a79a64b", - "name": "Spawner (505)", - "location": [533, 991, 53], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8651cb79-bd9b-4294-aedb-a4ca96bae64a", - "name": "Spawner (505)", - "location": [356, 705, 50], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "DireWolf", "maxCount": 1, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Llama", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8835e0c4-4f91-493e-a9a5-474c0e5db0b6", - "name": "Spawner (505)", - "location": [1299, 814, 9], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8be2d00c-c9fb-4563-bf7e-619326a17121", - "name": "Spawner (505)", - "location": [1133, 957, 64], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d149ddd-a7b8-4045-9b98-144298a83710", - "name": "Spawner (505)", - "location": [1207, 1037, 25], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d3dd1fa-c7a9-4de7-b7c2-9ab56d5118b8", - "name": "Spawner (505)", - "location": [526, 889, 11], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d629be0-95e5-4dec-bb25-5cfdc8339993", - "name": "Spawner (505)", - "location": [518, 329, 15], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8dfd0e1e-dc0e-45a9-bea3-622dde6659d4", - "name": "Spawner (505)", - "location": [967, 755, 3], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ec9ed72-e359-46ad-afa1-10a09818b0bb", - "name": "Spawner (505)", - "location": [1312, 704, 21], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8edf5c3c-7085-4c94-8e30-7a5d41e355fd", - "name": "Spawner (505)", - "location": [998, 656, 11], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8f58b5f2-f80e-458b-9eaf-a8042b0c8c61", - "name": "Spawner (505)", - "location": [1235, 506, 28], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9306a516-f374-4a37-8188-8ba412962f12", - "name": "Spawner (505)", - "location": [1263, 754, -1], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93f967ad-747c-4a5b-b39c-4693af13a61e", - "name": "Spawner (505)", - "location": [1027, 562, 29], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "974b9a03-2f13-4f89-9c95-02da87ffde75", - "name": "Spawner (505)", - "location": [635, 966, 44], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98e287de-8406-4373-8955-829652114180", - "name": "Spawner (505)", - "location": [1056, 904, 23], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c207158-3448-4593-bd8a-390f278dc088", - "name": "Spawner (505)", - "location": [727, 1071, 39], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Horse", "maxCount": 6, "probability": 100 }, - { "name": "Dog", "maxCount": 6, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c444b27-ab3c-4376-aa20-5f39a9623573", - "name": "Spawner (505)", - "location": [1311, 777, 9], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c58ae7c-5335-4da7-88f4-e0f4d9de498e", - "name": "Spawner (505)", - "location": [497, 453, 17], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9e7d1a09-96fd-4d52-a8c7-26be104f1050", - "name": "Spawner (505)", - "location": [823, 279, 33], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9fefec2c-5373-4ac5-8c36-400eff03d46d", - "name": "Spawner (505)", - "location": [1227, 1082, 24], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "Horse", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a1330307-1cab-42c3-9047-d07dcdb05571", "name": "Spawner (505)", "location": [404, 592, 26], @@ -2033,115 +434,7 @@ ] }, { - "$type": "Spawner", - "guid": "a23effc8-f42c-4893-a20a-0813bf1bd503", - "name": "Spawner (505)", - "location": [558, 862, 6], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a335efbd-4bde-4687-afaa-b44445efea4e", - "name": "Spawner (505)", - "location": [1134, 705, 72], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a39a0058-8f78-43a4-9028-f509c65ffa7e", - "name": "Spawner (505)", - "location": [623, 551, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "MountainGoat", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a3eb5fcc-51b8-4116-8762-6fd6758595b4", - "name": "Spawner (505)", - "location": [567, 1160, 25], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Rabbit", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a445a5be-f116-48cf-a9bc-0b083796ba9e", - "name": "Spawner (505)", - "location": [1198, 941, 36], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a5adf0ce-96eb-410d-9052-57b63b4fa2e6", - "name": "Spawner (505)", - "location": [913, 1055, 19], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a63fd209-3706-4242-8004-48d9b24e87f7", "name": "Spawner (505)", "location": [377, 635, 50], @@ -2165,313 +458,7 @@ ] }, { - "$type": "Spawner", - "guid": "a730d8a0-de99-42bc-8c4b-06bbb07de2bd", - "name": "Spawner (505)", - "location": [243, 563, 42], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Crane", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a7e0bbcc-6499-4720-8db9-edc7b5ab8b35", - "name": "Spawner (505)", - "location": [1258, 531, 31], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a81ef6e2-3c78-41f6-a98b-c6e169a3e67c", - "name": "Spawner (505)", - "location": [911, 1025, 16], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aaba65e1-c0ef-4788-9167-b7cddede3aa5", - "name": "Spawner (505)", - "location": [502, 532, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "MountainGoat", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aac6e8bc-1ab7-4b96-a84a-0f4fcc720cd0", - "name": "Spawner (505)", - "location": [861, 335, 37], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aadcbe2d-6fca-4527-a5ac-6ce004224916", - "name": "Spawner (505)", - "location": [1052, 1030, 24], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad0e593d-294e-4bd2-aa48-91a130447bc2", - "name": "Spawner (505)", - "location": [921, 593, 20], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, - { "name": "Sheep", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aebda4c6-8377-4938-a945-4da8600bb792", - "name": "Spawner (505)", - "location": [1265, 927, 19], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b8ae605c-db83-4c3c-9c6b-3ca1ecac20fb", - "name": "Spawner (505)", - "location": [591, 1010, 58], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b96c9765-f852-40d2-b621-ecf427dffa93", - "name": "Spawner (505)", - "location": [887, 1286, 13], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Goat", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba085b7c-aa9a-4138-b480-6dd562a9bb52", - "name": "Spawner (505)", - "location": [1028, 818, 13], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bbb6df60-7185-44d4-87d4-7ba60d7bd95b", - "name": "Spawner (505)", - "location": [192, 770, 56], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Horse", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bcbabf9f-03b8-427a-895f-06d963e636f7", - "name": "Spawner (505)", - "location": [1105, 539, 22], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bccad70b-6bc6-4f7d-b2c8-510036a4b9bc", - "name": "Spawner (505)", - "location": [656, 411, 33], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Sheep", "maxCount": 8, "probability": 100 }, - { "name": "MountainGoat", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bcfae829-1bba-48df-96e4-90bcf0664ad1", - "name": "Spawner (505)", - "location": [792, 920, 31], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "JackRabbit", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "be2a0d5d-dd14-422c-89c2-4885993f8776", - "name": "Spawner (505)", - "location": [566, 430, 35], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Sheep", "maxCount": 8, "probability": 100 }, - { "name": "Gaman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bed998a1-f2a1-46dc-a006-a4e4f49e7b51", - "name": "Spawner (505)", - "location": [955, 391, 4], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "c091a0bd-f518-4132-9879-039057bc949b", "name": "Spawner (505)", "location": [58, 104, 0], @@ -2495,77 +482,10 @@ ] }, { - "$type": "Spawner", - "guid": "c10dca38-deb2-48a7-87f4-487e862ede2f", + "type": "Spawner", + "guid": "8651cb79-bd9b-4294-aedb-a4ca96bae64a", "name": "Spawner (505)", - "location": [785, 303, 41], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2b38eeb-aff3-40aa-a028-09b3b7ad2196", - "name": "Spawner (505)", - "location": [1074, 529, 22], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c6024e89-5dcf-4bbb-8a3c-7ecdc07fecd2", - "name": "Spawner (505)", - "location": [949, 589, 7], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "BrownBear", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ca287a73-5914-4d4f-a338-9db48f48e9b9", - "name": "Spawner (505)", - "location": [1008, 260, 36], + "location": [356, 705, 50], "map": "Tokuno", "count": 8, "minDelay": "00:05:00", @@ -2574,416 +494,33 @@ "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 } + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "cd53dbe3-1a65-4629-8fbd-fe54f458741b", + "type": "Spawner", + "guid": "1d903bb8-480f-4d62-b957-ae627809d9d5", "name": "Spawner (505)", - "location": [710, 1115, 41], + "location": [190, 856, 52], "map": "Tokuno", "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "GreyWolf", "maxCount": 6, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd7c3147-b193-4867-9912-9e8cf19ccde5", - "name": "Spawner (505)", - "location": [901, 264, 31], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, "homeRange": 30, "walkingRange": 30, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] + "entries": [{ "name": "Gaman", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ced8bd2c-f463-445a-ad5b-90a5b43e0aa6", - "name": "Spawner (505)", - "location": [1289, 843, 4], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d4ebe134-af3c-4210-b8c6-687f6d4d7859", - "name": "Spawner (505)", - "location": [1184, 616, 19], - "map": "Tokuno", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MountainGoat", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d612093e-9a8c-4400-ac9f-7e7db6183ed9", - "name": "Spawner (505)", - "location": [868, 660, 7], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d65b2115-acc5-4d5b-9f25-4fe8b7fc3d92", - "name": "Spawner (505)", - "location": [493, 378, 45], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 8, "probability": 100 }, - { "name": "Crane", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d848d73f-350b-4b9a-8e5f-896386d076bb", - "name": "Spawner (505)", - "location": [1178, 1076, 19], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d88e96d0-39ff-46fe-86be-9b06a791891f", - "name": "Spawner (505)", - "location": [459, 483, 21], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d8978752-311f-4eef-86ff-3a85cf17c09d", - "name": "Spawner (505)", - "location": [1021, 297, 28], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d97602d0-7bee-4c1b-ab0c-be5b52de898c", - "name": "Spawner (505)", - "location": [551, 544, 32], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "MountainGoat", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dacf4f2f-fe97-46a9-9dd9-48b3eed8ba3c", - "name": "Spawner (505)", - "location": [505, 474, 31], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Sheep", "maxCount": 3, "probability": 100 }, - { "name": "Bird", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 2, "probability": 100 }, - { "name": "BrownBear", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc8e7cfd-1a8c-4f6a-ad02-a571e65e8222", - "name": "Spawner (505)", - "location": [862, 490, 54], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Sheep", "maxCount": 4, "probability": 100 }, - { "name": "MountainGoat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dcf2c166-cc37-459d-9ce7-be185248d6bb", - "name": "Spawner (505)", - "location": [208, 936, 43], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "DireWolf", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "Horse", "maxCount": 1, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd3a1f7e-e3da-4c36-8195-3aa7b42d299a", - "name": "Spawner (505)", - "location": [939, 1199, 22], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df75557f-0588-4342-8138-89d5a4c76e36", - "name": "Spawner (505)", - "location": [884, 1152, 29], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Sheep", "maxCount": 4, "probability": 100 }, - { "name": "Pig", "maxCount": 4, "probability": 100 }, - { "name": "Rabbit", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e1a61bd2-63af-4d3d-beaf-ffa0f49bd279", - "name": "Spawner (505)", - "location": [527, 393, 53], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e1b8a9d9-eb0c-4de4-aa22-f8ebe4e6cd65", - "name": "Spawner (505)", - "location": [261, 1186, 20], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e22936fe-88bf-42b6-8563-b4d85f37af0d", - "name": "Spawner (505)", - "location": [1377, 882, 21], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "Llama", "maxCount": 2, "probability": 100 }, - { "name": "MountainGoat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e42281b9-4fde-4a3e-8d04-a01f8459d320", - "name": "Spawner (505)", - "location": [1255, 804, 5], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gaman", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Horse", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e6b48855-1dec-4cfc-8630-055f969403df", - "name": "Spawner (505)", - "location": [702, 419, 18], - "map": "Tokuno", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Crane", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e89bf8f1-8463-4d29-89f0-89965fd42d44", - "name": "Spawner (505)", - "location": [941, 206, 17], - "map": "Tokuno", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Horse", "maxCount": 8, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "BlackBear", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Llama", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e9c32da2-2d36-45a6-9808-94bb89477d71", - "name": "Spawner (505)", - "location": [1065, 708, 17], - "map": "Tokuno", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "MountainGoat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "ec7098d8-a0eb-43da-8b23-43032cb8b93a", "name": "Spawner (505)", "location": [324, 664, 29], @@ -3007,23 +544,7 @@ ] }, { - "$type": "Spawner", - "guid": "edc05e21-90b2-4bf2-9539-929f0b217be7", - "name": "Spawner (505)", - "location": [794, 890, 31], - "map": "Tokuno", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f0ecf0a7-871d-4d75-8fbb-487dbe7ef934", "name": "Spawner (505)", "location": [327, 604, 15], @@ -3042,26 +563,185 @@ ] }, { - "$type": "Spawner", - "guid": "f21407d4-22ef-4284-94cb-ec01f21513c8", + "type": "Spawner", + "guid": "0f2ecc77-7a9d-483c-b2d2-28b021b69767", "name": "Spawner (505)", - "location": [680, 987, 47], + "location": [381, 511, 22], "map": "Tokuno", - "count": 2, + "count": 8, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 } + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "BrownBear", "maxCount": 2, "probability": 100 }, + { "name": "BlackBear", "maxCount": 2, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f5b24c6b-5308-461b-a977-f9bc646b451d", + "type": "Spawner", + "guid": "2c461fc3-195d-4f3e-961b-c46b24a68048", "name": "Spawner (505)", - "location": [185, 1046, 10], + "location": [307, 572, 33], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d5217d9-a132-4859-ba9e-af45db26bc7a", + "name": "Spawner (505)", + "location": [146, 109, 0], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40d60aa8-583e-474f-a59f-8b86a0233014", + "name": "Spawner (505)", + "location": [206, 891, 56], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dcf2c166-cc37-459d-9ce7-be185248d6bb", + "name": "Spawner (505)", + "location": [208, 936, 43], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84e49574-5215-480c-93e0-98a4320678a1", + "name": "Spawner (505)", + "location": [221, 968, 34], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2dfdc1dd-5220-4169-86e7-4342c692e2f6", + "name": "Spawner (505)", + "location": [192, 1014, 10], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 8, "probability": 100 }, + { "name": "Rat", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca287a73-5914-4d4f-a338-9db48f48e9b9", + "name": "Spawner (505)", + "location": [1008, 260, 36], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2be2ad47-84cf-4567-abe9-a0eea8a95ee4", + "name": "Spawner (505)", + "location": [232, 1116, 10], "map": "Tokuno", "count": 12, "minDelay": "00:05:00", @@ -3073,58 +753,66 @@ { "name": "Crane", "maxCount": 12, "probability": 100 }, { "name": "Rat", "maxCount": 12, "probability": 100 }, { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "GiantRat", "maxCount": 12, "probability": 100 } + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "BullFrog", "maxCount": 12, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f72385df-b6f1-4ecf-a2cd-6b712deb7068", + "type": "Spawner", + "guid": "d8978752-311f-4eef-86ff-3a85cf17c09d", "name": "Spawner (505)", - "location": [288, 652, 15], + "location": [1021, 297, 28], "map": "Tokuno", "count": 8, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Gaman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f7998dee-c474-49e9-8833-279d375db898", - "name": "Spawner (505)", - "location": [1141, 894, 57], - "map": "Tokuno", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Gaman", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, - { "name": "Horse", "maxCount": 1, "probability": 100 }, - { "name": "Cat", "maxCount": 1, "probability": 100 }, - { "name": "Dog", "maxCount": 1, "probability": 100 } + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f9552423-f109-40b1-a654-5e44bfd7e688", + "type": "Spawner", + "guid": "4943e08b-e268-4035-a091-6f3d05e5aab1", "name": "Spawner (505)", - "location": [793, 368, 31], + "location": [285, 1156, 13], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "BullFrog", "maxCount": 12, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e89bf8f1-8463-4d29-89f0-89965fd42d44", + "name": "Spawner (505)", + "location": [941, 206, 17], "map": "Tokuno", "count": 8, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 30, - "walkingRange": 40, + "walkingRange": 30, "entries": [ { "name": "Horse", "maxCount": 8, "probability": 100 }, { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, @@ -3137,7 +825,102 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "e1b8a9d9-eb0c-4de4-aa22-f8ebe4e6cd65", + "name": "Spawner (505)", + "location": [261, 1186, 20], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44acca90-d356-438d-9a80-a27d07ecac27", + "name": "Spawner (505)", + "location": [251, 1074, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "BullFrog", "maxCount": 12, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd7c3147-b193-4867-9912-9e8cf19ccde5", + "name": "Spawner (505)", + "location": [901, 264, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a730d8a0-de99-42bc-8c4b-06bbb07de2bd", + "name": "Spawner (505)", + "location": [243, 563, 42], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Crane", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81e3bdc8-9415-461f-b1ab-91824c47f65e", + "name": "Spawner (505)", + "location": [812, 218, 41], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fb021200-15d6-43f6-9d1f-92c5da81b194", "name": "Spawner (505)", "location": [384, 460, 32], @@ -3162,7 +945,813 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "09a9d3ff-55d0-4e8c-9fe7-d8458206ec1b", + "name": "Spawner (505)", + "location": [418, 463, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "Mongbat", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f4e6a55-2723-4d2e-b192-a8881e2b7c96", + "name": "Spawner (505)", + "location": [356, 403, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c1322bf-5ebb-464a-99e8-c137e14ff1d4", + "name": "Spawner (505)", + "location": [1011, 509, 26], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d65b2115-acc5-4d5b-9f25-4fe8b7fc3d92", + "name": "Spawner (505)", + "location": [493, 378, 45], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c10dca38-deb2-48a7-87f4-487e862ede2f", + "name": "Spawner (505)", + "location": [785, 303, 41], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e8d2eac-e0f8-484a-8316-ecb5720962ef", + "name": "Spawner (505)", + "location": [454, 374, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9552423-f109-40b1-a654-5e44bfd7e688", + "name": "Spawner (505)", + "location": [793, 368, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0773f05d-07e5-426e-a2e1-85c61ed07de5", + "name": "Spawner (505)", + "location": [472, 336, 38], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a15f08c-dfd3-4184-8b1e-e37833f41bc2", + "name": "Spawner (505)", + "location": [856, 324, 37], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "53507cb5-dd64-4105-8f65-3535910989ea", + "name": "Spawner (505)", + "location": [905, 331, 8], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1a61bd2-63af-4d3d-beaf-ffa0f49bd279", + "name": "Spawner (505)", + "location": [527, 393, 53], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Gaman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "be2a0d5d-dd14-422c-89c2-4885993f8776", + "name": "Spawner (505)", + "location": [566, 430, 35], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Sheep", "maxCount": 8, "probability": 100 }, + { "name": "Gaman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c58ae7c-5335-4da7-88f4-e0f4d9de498e", + "name": "Spawner (505)", + "location": [497, 453, 17], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aac6e8bc-1ab7-4b96-a84a-0f4fcc720cd0", + "name": "Spawner (505)", + "location": [861, 335, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6ef86062-ac38-4916-ae0c-8e9b7f02b400", + "name": "Spawner (505)", + "location": [565, 447, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Sheep", "maxCount": 8, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bccad70b-6bc6-4f7d-b2c8-510036a4b9bc", + "name": "Spawner (505)", + "location": [656, 411, 33], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Sheep", "maxCount": 8, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "581859e8-1196-47e2-bc24-1359c385fecb", + "name": "Spawner (505)", + "location": [857, 376, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6b48855-1dec-4cfc-8630-055f969403df", + "name": "Spawner (505)", + "location": [702, 419, 18], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Crane", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bed998a1-f2a1-46dc-a006-a4e4f49e7b51", + "name": "Spawner (505)", + "location": [955, 391, 4], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fdfab629-3712-4720-bfb3-3d06b510f089", + "name": "Spawner (505)", + "location": [935, 521, 16], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2b38eeb-aff3-40aa-a028-09b3b7ad2196", + "name": "Spawner (505)", + "location": [1074, 529, 22], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8406e7c2-5200-4cc1-b87e-0455f0b66efc", + "name": "Spawner (505)", + "location": [1062, 469, 8], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DireWolf", "maxCount": 4, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32ba9655-ba7d-4112-9e6a-3f36b34102bd", + "name": "Spawner (505)", + "location": [1122, 501, 19], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93f967ad-747c-4a5b-b39c-4693af13a61e", + "name": "Spawner (505)", + "location": [1027, 562, 29], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c6024e89-5dcf-4bbb-8a3c-7ecdc07fecd2", + "name": "Spawner (505)", + "location": [949, 589, 7], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bcbabf9f-03b8-427a-895f-06d963e636f7", + "name": "Spawner (505)", + "location": [1105, 539, 22], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad0e593d-294e-4bd2-aa48-91a130447bc2", + "name": "Spawner (505)", + "location": [921, 593, 20], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbb6df60-7185-44d4-87d4-7ba60d7bd95b", + "name": "Spawner (505)", + "location": [192, 770, 56], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "35aa8796-06bb-4243-840f-44d4ce68b110", + "name": "Spawner (505)", + "location": [1181, 547, 23], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4ebe134-af3c-4210-b8c6-687f6d4d7859", + "name": "Spawner (505)", + "location": [1184, 616, 19], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MountainGoat", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8f58b5f2-f80e-458b-9eaf-a8042b0c8c61", + "name": "Spawner (505)", + "location": [1235, 506, 28], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7e0bbcc-6499-4720-8db9-edc7b5ab8b35", + "name": "Spawner (505)", + "location": [1258, 531, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc5b8bce-6c63-4d77-97ac-7661248770fc", + "name": "Spawner (505)", + "location": [1307, 559, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a5ac06e-23f0-40c6-a1f4-9961465b6ddc", + "name": "Spawner (505)", + "location": [1176, 701, 72], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "MountainGoat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2d1b64ad-e246-4c2f-b1b1-e2b25c6e53e2", + "name": "Spawner (505)", + "location": [1144, 667, 70], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "MountainGoat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a335efbd-4bde-4687-afaa-b44445efea4e", + "name": "Spawner (505)", + "location": [1134, 705, 72], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1c39d957-43f3-4e45-9fca-0f82fcd80baf", + "name": "Spawner (505)", + "location": [1074, 806, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "BrownBear", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "811da3e8-b320-416d-936e-505e490d503e", + "name": "Spawner (505)", + "location": [955, 828, 27], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba085b7c-aa9a-4138-b480-6dd562a9bb52", + "name": "Spawner (505)", + "location": [1028, 818, 13], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "651e3dfe-31b6-4a95-9110-5861aac65171", + "name": "Spawner (505)", + "location": [1101, 832, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "BrownBear", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8dfd0e1e-dc0e-45a9-bea3-622dde6659d4", + "name": "Spawner (505)", + "location": [967, 755, 3], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55ccd405-6e56-4289-a671-c1521f8b1b4e", + "name": "Spawner (505)", + "location": [956, 705, 16], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fb4ff5b1-2124-4bce-8b53-3b48dcf0af29", "name": "Spawner (505)", "location": [911, 643, 15], @@ -3185,26 +1774,61 @@ ] }, { - "$type": "Spawner", - "guid": "fc35baac-54c5-442a-9808-686341d564e4", + "type": "Spawner", + "guid": "d612093e-9a8c-4400-ac9f-7e7db6183ed9", "name": "Spawner (505)", - "location": [837, 1075, 16], + "location": [868, 660, 7], "map": "Tokuno", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84e8cdc1-226f-4a5c-9732-628e95c380a8", + "name": "Spawner (505)", + "location": [889, 516, 47], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "Crane", "maxCount": 4, "probability": 100 } + { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fc5b8bce-6c63-4d77-97ac-7661248770fc", + "type": "Spawner", + "guid": "dc8e7cfd-1a8c-4f6a-ad02-a571e65e8222", "name": "Spawner (505)", - "location": [1307, 559, 31], + "location": [862, 490, 54], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8edf5c3c-7085-4c94-8e30-7a5d41e355fd", + "name": "Spawner (505)", + "location": [998, 656, 11], "map": "Tokuno", "count": 8, "minDelay": "00:05:00", @@ -3213,13 +1837,679 @@ "homeRange": 30, "walkingRange": 40, "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9c32da2-2d36-45a6-9808-94bb89477d71", + "name": "Spawner (505)", + "location": [1065, 708, 17], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "MountainGoat", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9fefec2c-5373-4ac5-8c36-400eff03d46d", + "name": "Spawner (505)", + "location": [1227, 1082, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e0c0300-cb5b-4a45-a97a-12b40100f047", + "name": "Spawner (505)", + "location": [1039, 952, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aadcbe2d-6fca-4527-a5ac-6ce004224916", + "name": "Spawner (505)", + "location": [1052, 1030, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11f32a2d-41af-40ea-b076-14452d79a9c9", + "name": "Spawner (505)", + "location": [1092, 1024, 34], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8be2d00c-c9fb-4563-bf7e-619326a17121", + "name": "Spawner (505)", + "location": [1133, 957, 64], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c6b8543-bd53-4cf8-ac52-a20347b89cad", + "name": "Spawner (505)", + "location": [1088, 980, 30], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10d55af6-cce8-498b-9c29-4e2713f79d9b", + "name": "Spawner (505)", + "location": [1091, 897, 69], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98e287de-8406-4373-8955-829652114180", + "name": "Spawner (505)", + "location": [1056, 904, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7bbee32e-82df-4a88-b864-dda360fed83e", + "name": "Spawner (505)", + "location": [143, 131, 0], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Gaman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6b0b9c5e-f925-4f24-97f9-d9c9915d8d8f", + "name": "Spawner (505)", + "location": [687, 439, 26], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Sheep", "maxCount": 8, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e7d1a09-96fd-4d52-a8c7-26be104f1050", + "name": "Spawner (505)", + "location": [823, 279, 33], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aebda4c6-8377-4938-a945-4da8600bb792", + "name": "Spawner (505)", + "location": [1265, 927, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f71ccad-9194-41b5-8af2-819e8d5ff14e", + "name": "Spawner (505)", + "location": [419, 529, 15], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 8, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b8f1c02-d05a-4bc9-a013-57e201cca765", + "name": "Spawner (505)", + "location": [960, 293, 29], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c67a873-1b6e-422d-bce9-5cc93722eecc", + "name": "Spawner (505)", + "location": [243, 1031, 13], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "BullFrog", "maxCount": 12, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "109decf7-c5fa-4839-ab48-51d6e1a0a984", + "name": "Spawner (505)", + "location": [988, 335, 17], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d84bf13-4790-4286-a8dd-ad78174f533a", + "name": "Spawner (505)", + "location": [771, 228, 63], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dc8cf0c-106b-49a9-92aa-82758885387d", + "name": "Spawner (505)", + "location": [443, 402, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e42281b9-4fde-4a3e-8d04-a01f8459d320", + "name": "Spawner (505)", + "location": [1255, 804, 5], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f72385df-b6f1-4ecf-a2cd-6b712deb7068", + "name": "Spawner (505)", + "location": [288, 652, 15], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Gaman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d88e96d0-39ff-46fe-86be-9b06a791891f", + "name": "Spawner (505)", + "location": [459, 483, 21], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Gaman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f5b24c6b-5308-461b-a977-f9bc646b451d", + "name": "Spawner (505)", + "location": [185, 1046, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "259e2b9a-187b-4009-8e43-277c10cff6b5", + "name": "Spawner (505)", + "location": [210, 1085, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "BullFrog", "maxCount": 12, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1882a00b-e0b5-44b2-9a53-ad4faabbd6d8", + "name": "Spawner (505)", + "location": [596, 1289, 21], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 }, + { "name": "Pig", "maxCount": 8, "probability": 100 }, + { "name": "Goat", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a91c9b1-2c2c-4e5d-b3ab-98b110013172", + "name": "Spawner (505)", + "location": [566, 1287, 22], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7faed2d6-3375-4d3d-a635-921e06255853", + "name": "Spawner (505)", + "location": [525, 1263, 24], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c2e0be2-cc73-4518-9c76-3e960cf286b6", + "name": "Spawner (505)", + "location": [547, 1240, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Dog", "maxCount": 8, "probability": 100 }, + { "name": "Cat", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3692b482-e864-4cc3-92f2-02bbbf69c448", + "name": "Spawner (505)", + "location": [534, 1189, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1155a3a8-591d-47ed-9672-30b5d0d10e5f", + "name": "Spawner (505)", + "location": [572, 1211, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Goat", "maxCount": 8, "probability": 100 }, + { "name": "Sheep", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15fd3cea-416b-449a-925f-c571ada88282", + "name": "Spawner (505)", + "location": [600, 1198, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Goat", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07015515-1c9d-4bfd-9199-5a8eeccce91a", + "name": "Spawner (505)", + "location": [580, 1170, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 }, + { "name": "Dog", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a3eb5fcc-51b8-4116-8762-6fd6758595b4", + "name": "Spawner (505)", + "location": [567, 1160, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65473288-20d0-40f6-9da4-ab3c1213c9e1", + "name": "Spawner (505)", + "location": [563, 1133, 43], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, { "name": "Bird", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "512c0d13-c23a-4f39-9dd1-584f708660fb", + "name": "Spawner (505)", + "location": [561, 1101, 33], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "Pig", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "83a06690-beab-4182-a040-4466d61e8bd3", + "name": "Spawner (505)", + "location": [550, 1092, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fd5426bf-46f4-494f-9818-bd66d8b8f4f9", "name": "Spawner (505)", "location": [524, 1054, 18], @@ -3230,15 +2520,13 @@ "team": 0, "homeRange": 20, "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "fdfab629-3712-4720-bfb3-3d06b510f089", + "type": "Spawner", + "guid": "12723266-85ed-413a-bab7-c825593b42bb", "name": "Spawner (505)", - "location": [935, 521, 16], + "location": [504, 1042, 24], "map": "Tokuno", "count": 3, "minDelay": "00:05:00", @@ -3246,9 +2534,645 @@ "team": 0, "homeRange": 20, "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "855c2b06-17c2-452b-bbd6-bd081a79a64b", + "name": "Spawner (505)", + "location": [533, 991, 53], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d3dd1fa-c7a9-4de7-b7c2-9ab56d5118b8", + "name": "Spawner (505)", + "location": [526, 889, 11], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "025966e3-5dff-48d2-8101-47bb942642d1", + "name": "Spawner (505)", + "location": [612, 857, 15], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a23effc8-f42c-4893-a20a-0813bf1bd503", + "name": "Spawner (505)", + "location": [558, 862, 6], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 }, - { "name": "Sheep", "maxCount": 3, "probability": 100 } + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "1a38a3b3-ecad-4c71-b4af-6a862e5f1e38", + "name": "Spawner (505)", + "location": [724, 831, 34], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b1a526f-836a-4c63-a353-066cf22828bf", + "name": "Spawner (505)", + "location": [771, 824, 16], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "783c5a65-45be-49b2-978e-839ee3d8ad42", + "name": "Spawner (505)", + "location": [746, 814, 16], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4c7e0f56-72ac-454c-9392-b00a2ca81ff0", + "name": "Spawner (505)", + "location": [683, 857, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "JackRabbit", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47507391-0312-43b8-a52f-1da5ef65d134", + "name": "Spawner (505)", + "location": [685, 891, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "974b9a03-2f13-4f89-9c95-02da87ffde75", + "name": "Spawner (505)", + "location": [635, 966, 44], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8ae605c-db83-4c3c-9c6b-3ca1ecac20fb", + "name": "Spawner (505)", + "location": [591, 1010, 58], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f21407d4-22ef-4284-94cb-ec01f21513c8", + "name": "Spawner (505)", + "location": [680, 987, 47], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "377c039b-1370-49ed-ac1f-b5453a0f98ae", + "name": "Spawner (505)", + "location": [722, 916, 40], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 6, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c207158-3448-4593-bd8a-390f278dc088", + "name": "Spawner (505)", + "location": [727, 1071, 39], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Horse", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd53dbe3-1a65-4629-8fbd-fe54f458741b", + "name": "Spawner (505)", + "location": [710, 1115, 41], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "GreyWolf", "maxCount": 6, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5fddebe2-5f66-4157-a95e-50f1d376ab67", + "name": "Spawner (505)", + "location": [644, 1142, 28], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DireWolf", "maxCount": 6, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 6, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d8b101e-4530-4396-98db-3b3f0a0e6a23", + "name": "Spawner (505)", + "location": [775, 1127, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "BlackBear", "maxCount": 4, "probability": 100 }, + { "name": "BrownBear", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bcfae829-1bba-48df-96e4-90bcf0664ad1", + "name": "Spawner (505)", + "location": [792, 920, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "JackRabbit", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "edc05e21-90b2-4bf2-9539-929f0b217be7", + "name": "Spawner (505)", + "location": [794, 890, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "504e34ec-555a-4638-aa23-440d042f355a", + "name": "Spawner (505)", + "location": [833, 908, 15], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e0d9671-9a22-41db-98ef-754e21134ec4", + "name": "Spawner (505)", + "location": [770, 875, 30], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "073bd67c-3055-4bf5-8879-4bca7908de15", + "name": "Spawner (505)", + "location": [874, 971, 45], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a81ef6e2-3c78-41f6-a98b-c6e169a3e67c", + "name": "Spawner (505)", + "location": [911, 1025, 16], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Crane", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62196314-f7e7-42e8-a131-db562b52bee0", + "name": "Spawner (505)", + "location": [879, 1038, 16], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Goat", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "208e7ae6-ff8a-4482-aad8-88fac46e3821", + "name": "Spawner (505)", + "location": [887, 1072, 20], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a5adf0ce-96eb-410d-9052-57b63b4fa2e6", + "name": "Spawner (505)", + "location": [913, 1055, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2112cd3e-2459-4ced-b190-dc6ce7d36d3a", + "name": "Spawner (505)", + "location": [916, 1120, 17], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Pig", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c20bc6c-30d5-4b2d-97e3-0db6146a7226", + "name": "Spawner (505)", + "location": [883, 1121, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fc35baac-54c5-442a-9808-686341d564e4", + "name": "Spawner (505)", + "location": [837, 1075, 16], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "df75557f-0588-4342-8138-89d5a4c76e36", + "name": "Spawner (505)", + "location": [884, 1152, 29], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Sheep", "maxCount": 4, "probability": 100 }, + { "name": "Pig", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "221b2448-0059-4522-8cdc-c1f0ea38145a", + "name": "Spawner (505)", + "location": [907, 1143, 11], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7f3e95c9-608e-4e32-95fa-5830bd9f5372", + "name": "Spawner (505)", + "location": [942, 1170, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd3a1f7e-e3da-4c36-8195-3aa7b42d299a", + "name": "Spawner (505)", + "location": [939, 1199, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b655fa8-4c47-4101-8a05-e138b0eb8e14", + "name": "Spawner (505)", + "location": [934, 1233, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b96c9765-f852-40d2-b621-ecf427dffa93", + "name": "Spawner (505)", + "location": [887, 1286, 13], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Goat", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49dc8190-fb53-4f7a-85c1-4bb0645f7da8", + "name": "Spawner (505)", + "location": [1139, 999, 48], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "365b6b36-18ef-4ad8-8a0a-896cfdd1ba1b", + "name": "Spawner (505)", + "location": [1133, 1036, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Crane", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d149ddd-a7b8-4045-9b98-144298a83710", + "name": "Spawner (505)", + "location": [1207, 1037, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Crane", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d848d73f-350b-4b9a-8e5f-896386d076bb", + "name": "Spawner (505)", + "location": [1178, 1076, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/tokuno/YomutsoMines.json b/Distribution/Data/Spawns/post-uoml/tokuno/YomutsoMines.json similarity index 94% rename from Distribution/Data/Spawns/shared/tokuno/YomutsoMines.json rename to Distribution/Data/Spawns/post-uoml/tokuno/YomutsoMines.json index 50b8c9314..beeec296b 100644 --- a/Distribution/Data/Spawns/shared/tokuno/YomutsoMines.json +++ b/Distribution/Data/Spawns/post-uoml/tokuno/YomutsoMines.json @@ -1,79 +1,6 @@ [ { - "$type": "Spawner", - "guid": "115246e3-d444-4ad4-ad93-ba5f98ade944", - "name": "Spawner (506)", - "location": [71, 29, -1], - "map": "Malas", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, - { "name": "DullCopperElemental", "maxCount": 2, "probability": 100 }, - { "name": "YomotsuWarrior", "maxCount": 1, "probability": 100 }, - { "name": "YomotsuPriest", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6561df12-d971-4504-9a84-fba65d6e3339", - "name": "Spawner (506)", - "location": [111, 20, -1], - "map": "Malas", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, - { "name": "DullCopperElemental", "maxCount": 1, "probability": 100 }, - { "name": "YomotsuWarrior", "maxCount": 2, "probability": 100 }, - { "name": "YomotsuPriest", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "694ea01f-680b-4054-9f13-bf9e7317a6d8", - "name": "Spawner (506)", - "location": [107, 23, -1], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "FireBeetle", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "922f0ff6-6efc-4787-aab1-32b8c41831ab", - "name": "Spawner (506)", - "location": [54, 67, -1], - "map": "Malas", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, - { "name": "DullCopperElemental", "maxCount": 1, "probability": 100 }, - { "name": "YomotsuWarrior", "maxCount": 2, "probability": 100 }, - { "name": "YomotsuPriest", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "9cbd85a7-68f8-401d-a8c3-b2a220b2e431", "name": "Spawner (506)", "location": [16, 56, -1], @@ -92,10 +19,10 @@ ] }, { - "$type": "Spawner", - "guid": "a0525c92-565a-4b15-b6b0-61c6be263ae7", + "type": "Spawner", + "guid": "ee2e7f73-a95f-40e6-8493-80dbe4c2ea65", "name": "Spawner (506)", - "location": [102, 84, -1], + "location": [25, 19, -1], "map": "Malas", "count": 6, "minDelay": "00:05:00", @@ -111,7 +38,45 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "115246e3-d444-4ad4-ad93-ba5f98ade944", + "name": "Spawner (506)", + "location": [71, 29, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "922f0ff6-6efc-4787-aab1-32b8c41831ab", + "name": "Spawner (506)", + "location": [54, 67, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "ba3f54b0-b977-476e-be97-2ad5ba39dc19", "name": "Spawner (506)", "location": [83, 114, -1], @@ -130,7 +95,26 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "a0525c92-565a-4b15-b6b0-61c6be263ae7", + "name": "Spawner (506)", + "location": [102, 84, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "dc1fe934-3641-4ddd-8b6c-207ccf0861ef", "name": "Spawner (506)", "location": [116, 61, -1], @@ -149,10 +133,10 @@ ] }, { - "$type": "Spawner", - "guid": "ee2e7f73-a95f-40e6-8493-80dbe4c2ea65", + "type": "Spawner", + "guid": "6561df12-d971-4504-9a84-fba65d6e3339", "name": "Spawner (506)", - "location": [25, 19, -1], + "location": [111, 20, -1], "map": "Malas", "count": 6, "minDelay": "00:05:00", @@ -166,5 +150,19 @@ { "name": "YomotsuWarrior", "maxCount": 2, "probability": 100 }, { "name": "YomotsuPriest", "maxCount": 2, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "694ea01f-680b-4054-9f13-bf9e7317a6d8", + "name": "Spawner (506)", + "location": [107, 23, -1], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "FireBeetle", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/BlightedGrove.json b/Distribution/Data/Spawns/post-uoml/trammel/BlightedGrove.json similarity index 89% rename from Distribution/Data/Spawns/shared/trammel/BlightedGrove.json rename to Distribution/Data/Spawns/post-uoml/trammel/BlightedGrove.json index c4eb637ae..9a38fe774 100644 --- a/Distribution/Data/Spawns/shared/trammel/BlightedGrove.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/BlightedGrove.json @@ -1,99 +1,6 @@ [ { - "$type": "Spawner", - "guid": "025ff0bc-ff5b-4175-b963-1e393ee70543", - "name": "Spawner (101)", - "location": [6535, 848, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Abscess", "maxCount": 4, "probability": 100 }, - { "name": "Hydra", "maxCount": 4, "probability": 100 }, - { "name": "BogThing", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "04ebab40-c487-455f-8bea-211ae849bf2a", - "name": "Spawner (101)", - "location": [6518, 879, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "InsaneDryad", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3dbad35b-a4c0-4d6d-b1d7-ad4cb792da01", - "name": "Spawner (101)", - "location": [6489, 897, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "InsaneDryad", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "43cff389-a041-49a6-89fb-cd5111466e70", - "name": "Spawner (101)", - "location": [6518, 870, 0], - "map": "Trammel", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Thrasher", "maxCount": 9, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Bogling", "maxCount": 9, "probability": 100 }, - { "name": "Alligator", "maxCount": 9, "probability": 100 }, - { "name": "Coil", "maxCount": 9, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "713c3e56-33d2-430c-b182-364da7607c83", - "name": "Spawner (101)", - "location": [6513, 871, 0], - "map": "Trammel", - "count": 17, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 35, - "walkingRange": 35, - "entries": [ - { "name": "GiantSerpent", "maxCount": 17, "probability": 100 }, - { "name": "GiantToad", "maxCount": 17, "probability": 100 }, - { "name": "Harpy", "maxCount": 17, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 17, "probability": 100 }, - { "name": "Snake", "maxCount": 17, "probability": 100 }, - { "name": "Bogling", "maxCount": 17, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "b04cfa39-6091-49da-bdba-1b737c266a0a", "name": "Spawner (101)", "location": [6507, 843, 0], @@ -115,7 +22,28 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "713c3e56-33d2-430c-b182-364da7607c83", + "name": "Spawner (101)", + "location": [6513, 871, 0], + "map": "Trammel", + "count": 17, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [ + { "name": "GiantSerpent", "maxCount": 17, "probability": 100 }, + { "name": "GiantToad", "maxCount": 17, "probability": 100 }, + { "name": "Harpy", "maxCount": 17, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 17, "probability": 100 }, + { "name": "Snake", "maxCount": 17, "probability": 100 }, + { "name": "Bogling", "maxCount": 17, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "c6215ac8-c0e3-4c83-9394-984deb29c773", "name": "Spawner (101)", "location": [6585, 878, 0], @@ -133,7 +61,25 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "025ff0bc-ff5b-4175-b963-1e393ee70543", + "name": "Spawner (101)", + "location": [6535, 848, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Abscess", "maxCount": 4, "probability": 100 }, + { "name": "Hydra", "maxCount": 4, "probability": 100 }, + { "name": "BogThing", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "e840761f-1c5b-45f2-ba91-2a273e9739c2", "name": "Spawner (101)", "location": [6540, 872, 0], @@ -144,8 +90,56 @@ "team": 0, "homeRange": 5, "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "43cff389-a041-49a6-89fb-cd5111466e70", + "name": "Spawner (101)", + "location": [6518, 870, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, "entries": [ - { "name": "InsaneDryad", "maxCount": 1, "probability": 100 } + { "name": "Thrasher", "maxCount": 9, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Bogling", "maxCount": 9, "probability": 100 }, + { "name": "Alligator", "maxCount": 9, "probability": 100 }, + { "name": "Coil", "maxCount": 9, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 9, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "3dbad35b-a4c0-4d6d-b1d7-ad4cb792da01", + "name": "Spawner (101)", + "location": [6489, 897, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04ebab40-c487-455f-8bea-211ae849bf2a", + "name": "Spawner (101)", + "location": [6518, 879, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/BritainSewer.json b/Distribution/Data/Spawns/post-uoml/trammel/BritainSewer.json similarity index 89% rename from Distribution/Data/Spawns/shared/trammel/BritainSewer.json rename to Distribution/Data/Spawns/post-uoml/trammel/BritainSewer.json index fd7621ba0..b1c013d1a 100644 --- a/Distribution/Data/Spawns/shared/trammel/BritainSewer.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/BritainSewer.json @@ -1,58 +1,6 @@ [ { - "$type": "Spawner", - "guid": "16798caa-188d-4b2d-8494-c3b56cbaa4c7", - "name": "Spawner (102)", - "location": [6049, 1470, 5], - "map": "Trammel", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1fb937a2-b8d8-4796-8f7e-62dc86db4541", - "name": "Spawner (102)", - "location": [6052, 1463, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2059e438-3ea0-4553-9d80-2c4b3e179956", - "name": "Spawner (102)", - "location": [6092, 1448, 5], - "map": "Trammel", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "308b2558-7402-46d1-a300-11eb22feaf0b", "name": "Spawner (102)", "location": [6049, 1447, 4], @@ -70,41 +18,7 @@ ] }, { - "$type": "Spawner", - "guid": "31142c48-f400-4b04-a235-4df3d184432a", - "name": "Spawner (102)", - "location": [6115, 1491, -15], - "map": "Trammel", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "424591d5-810a-4248-b002-51d2d993b09e", - "name": "Spawner (102)", - "location": [6108, 1471, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Alligator", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "4502169a-d510-4779-a2d4-7ecff61c32a1", "name": "Spawner (102)", "location": [6091, 1491, 10], @@ -122,75 +36,7 @@ ] }, { - "$type": "Spawner", - "guid": "551ba232-804a-48e2-a289-048ac2c5952c", - "name": "Spawner (102)", - "location": [6087, 1443, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Alligator", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5873288f-b3d0-4ffc-b937-1a938b0ab0a3", - "name": "Spawner (102)", - "location": [6039, 1438, 4], - "map": "Trammel", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e47e277-8d43-46c0-b43a-a15ce3623ceb", - "name": "Spawner (102)", - "location": [6112, 1462, 5], - "map": "Trammel", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "639f6230-75e9-4633-8e2b-cfd6f35f4fdc", - "name": "Spawner (102)", - "location": [6077, 1492, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Alligator", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "70842ac5-c2f9-490b-8fca-603396002f94", "name": "Spawner (102)", "location": [6048, 1487, 5], @@ -208,7 +54,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "73483117-862e-472d-b856-13fbe0f2cb04", "name": "Spawner (102)", "location": [6065, 1459, 5], @@ -226,26 +72,10 @@ ] }, { - "$type": "Spawner", - "guid": "80883952-a77b-466d-b01e-f810fca42c11", + "type": "Spawner", + "guid": "2059e438-3ea0-4553-9d80-2c4b3e179956", "name": "Spawner (102)", - "location": [6090, 1483, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "82cbfa7a-4ea4-476b-ac8c-a3963746fffd", - "name": "Spawner (102)", - "location": [6061, 1490, 5], + "location": [6092, 1448, 5], "map": "Trammel", "count": 3, "minDelay": "00:00:00", @@ -260,25 +90,7 @@ ] }, { - "$type": "Spawner", - "guid": "bda24adb-c409-4c12-a0c0-8aacd36ab3d9", - "name": "Spawner (102)", - "location": [6034, 1467, 5], - "map": "Trammel", - "count": 3, - "minDelay": "00:00:00", - "maxDelay": "00:01:00", - "team": 0, - "homeRange": 2, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "cb41f5b8-add1-42be-8bbe-48b76f9a4dd9", "name": "Spawner (102)", "location": [6107, 1452, 25], @@ -296,7 +108,115 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "5e47e277-8d43-46c0-b43a-a15ce3623ceb", + "name": "Spawner (102)", + "location": [6112, 1462, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5873288f-b3d0-4ffc-b937-1a938b0ab0a3", + "name": "Spawner (102)", + "location": [6039, 1438, 4], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31142c48-f400-4b04-a235-4df3d184432a", + "name": "Spawner (102)", + "location": [6115, 1491, -15], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ed8f2188-17cd-4235-86d8-54d62209ef93", + "name": "Spawner (102)", + "location": [6084, 1470, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 1, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "16798caa-188d-4b2d-8494-c3b56cbaa4c7", + "name": "Spawner (102)", + "location": [6049, 1470, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bda24adb-c409-4c12-a0c0-8aacd36ab3d9", + "name": "Spawner (102)", + "location": [6034, 1467, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "dd1b022d-98d5-4f06-aac1-7751d2416304", "name": "Spawner (102)", "location": [6059, 1438, 4], @@ -314,23 +234,25 @@ ] }, { - "$type": "Spawner", - "guid": "e40a3f76-4701-425c-a806-06cb36504593", + "type": "Spawner", + "guid": "82cbfa7a-4ea4-476b-ac8c-a3963746fffd", "name": "Spawner (102)", - "location": [6037, 1494, 0], + "location": [6061, 1490, 5], "map": "Trammel", - "count": 2, + "count": 3, "minDelay": "00:00:00", "maxDelay": "00:01:00", "team": 0, - "homeRange": 4, - "walkingRange": 10, + "homeRange": 2, + "walkingRange": 15, "entries": [ - { "name": "Alligator", "maxCount": 2, "probability": 100 } + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e5f0bff7-c610-4923-8c17-b32a00911628", "name": "Spawner (102)", "location": [6041, 1486, 5], @@ -348,21 +270,87 @@ ] }, { - "$type": "Spawner", - "guid": "ed8f2188-17cd-4235-86d8-54d62209ef93", + "type": "Spawner", + "guid": "80883952-a77b-466d-b01e-f810fca42c11", "name": "Spawner (102)", - "location": [6084, 1470, 5], + "location": [6090, 1483, 0], "map": "Trammel", - "count": 3, + "count": 8, "minDelay": "00:00:00", "maxDelay": "00:01:00", "team": 0, - "homeRange": 1, - "walkingRange": 15, - "entries": [ - { "name": "SewerRat", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 } - ] + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "Alligator", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "639f6230-75e9-4633-8e2b-cfd6f35f4fdc", + "name": "Spawner (102)", + "location": [6077, 1492, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "424591d5-810a-4248-b002-51d2d993b09e", + "name": "Spawner (102)", + "location": [6108, 1471, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "551ba232-804a-48e2-a289-048ac2c5952c", + "name": "Spawner (102)", + "location": [6087, 1443, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1fb937a2-b8d8-4796-8f7e-62dc86db4541", + "name": "Spawner (102)", + "location": [6052, 1463, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "Alligator", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e40a3f76-4701-425c-a806-06cb36504593", + "name": "Spawner (102)", + "location": [6037, 1494, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/Covetous.json b/Distribution/Data/Spawns/post-uoml/trammel/Covetous.json similarity index 87% rename from Distribution/Data/Spawns/shared/trammel/Covetous.json rename to Distribution/Data/Spawns/post-uoml/trammel/Covetous.json index 450299122..2f3d1c6b7 100644 --- a/Distribution/Data/Spawns/shared/trammel/Covetous.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Covetous.json @@ -1,22 +1,127 @@ [ { - "$type": "Spawner", - "guid": "03fd7675-5b18-4bd1-b886-549fff24a24e", + "type": "Spawner", + "guid": "234f3c85-84fb-4f3d-bb0e-c90cc95c16b0", "name": "Spawner (103)", - "location": [2513, 862, 0], + "location": [5472, 1877, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Harpy", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "41f90fd5-f4fe-47f3-b05f-4e00f83dd415", + "name": "Spawner (103)", + "location": [5453, 1905, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "HeadlessOne", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5cb18cd8-da7a-414d-9f4d-f9b20709b796", + "name": "Spawner (103)", + "location": [5443, 1918, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 30, + "entries": [{ "name": "StoneHarpy", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bc45c254-4539-4c60-a37f-2711c2e2ddec", + "name": "Spawner (103)", + "location": [5418, 1876, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Gazer", "maxCount": 4, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be80b563-6951-4564-869e-e64efe2b116a", + "name": "Spawner (103)", + "location": [5411, 1901, 0], "map": "Trammel", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 25, + "walkingRange": 20, "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 } + { "name": "Gazer", "maxCount": 2, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "af277400-a7ad-4048-bdb1-fdcc10b20756", + "name": "Spawner (103)", + "location": [5393, 1921, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84ba2d57-7801-4461-8ed9-c7d45c95f473", + "name": "Spawner (103)", + "location": [5400, 1937, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3828dfe6-a240-4615-bf09-4fd7eccd4b90", + "name": "Spawner (103)", + "location": [5452, 1891, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "156f3558-d5ea-4117-bcdb-f6f2f2ff25bd", "name": "Spawner (103)", "location": [5399, 1859, 0], @@ -33,29 +138,24 @@ ] }, { - "$type": "Spawner", - "guid": "15e19e7f-7de5-412c-873e-e38fd7f4c99d", + "type": "Spawner", + "guid": "4755ff86-849d-443e-aaf9-4da5a523f28c", "name": "Spawner (103)", - "location": [5528, 1808, 0], + "location": [5387, 1911, 0], "map": "Trammel", - "count": 8, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 30, + "homeRange": 2, + "walkingRange": 2, "entries": [ - { "name": "Mummy", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 3, "probability": 100 }, - { "name": "Zombie", "maxCount": 3, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "16f79bca-0bcf-4440-a5df-ac2a73ece818", "name": "Spawner (103)", "location": [5411, 1932, 0], @@ -73,112 +173,66 @@ ] }, { - "$type": "Spawner", - "guid": "1b287d2c-4992-4071-a577-23a460b7af73", + "type": "Spawner", + "guid": "ec453c02-9ef8-40bb-8e12-06c74c0ce96e", "name": "Spawner (103)", - "location": [5560, 1843, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 40, - "entries": [ - { "name": "Mummy", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1fc3857a-ad6d-467c-9feb-df23f3192492", - "name": "Spawner (103)", - "location": [5460, 1975, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22bd9b72-f463-431e-bd87-17e25e1c077f", - "name": "Spawner (103)", - "location": [5473, 1991, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 9, - "walkingRange": 25, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "DreadSpider", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "234f3c85-84fb-4f3d-bb0e-c90cc95c16b0", - "name": "Spawner (103)", - "location": [5472, 1877, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Harpy", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e2d21b4-67af-4720-b7ff-d8aa07544362", - "name": "Spawner (103)", - "location": [5445, 2030, -3], + "location": [5577, 2006, 0], "map": "Trammel", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "WaterElemental", "maxCount": 2, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "2e3bb584-e75f-47d2-b3ca-9926aef55326", + "type": "Spawner", + "guid": "b180d203-9331-4077-86a1-5cec055f0fc8", "name": "Spawner (103)", - "location": [5448, 2027, 0], + "location": [5593, 2017, 0], "map": "Trammel", - "count": 3, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 8, - "walkingRange": 8, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "df30762f-98e0-4753-97ef-d2e548612633", + "name": "Spawner (103)", + "location": [5558, 2025, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d56c22bc-e344-47d6-ae5f-d65acdea6940", + "name": "Spawner (103)", + "location": [5498, 1998, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "368ab9e9-2c6c-4cc2-9418-8d978ddaea67", "name": "Spawner (103)", "location": [5474, 2035, 0], @@ -195,180 +249,69 @@ ] }, { - "$type": "Spawner", - "guid": "3828dfe6-a240-4615-bf09-4fd7eccd4b90", + "type": "Spawner", + "guid": "cdffe875-e196-4511-b07b-bafe2547279b", "name": "Spawner (103)", - "location": [5452, 1891, 0], + "location": [5469, 2019, 0], "map": "Trammel", - "count": 3, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Slime", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "41f90fd5-f4fe-47f3-b05f-4e00f83dd415", + "type": "Spawner", + "guid": "2e2d21b4-67af-4720-b7ff-d8aa07544362", "name": "Spawner (103)", - "location": [5453, 1905, 0], + "location": [5445, 2030, -3], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "WaterElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b005fb7e-e674-496d-89f1-ee8d3b8961e0", + "name": "Spawner (103)", + "location": [5440, 2023, 0], "map": "Trammel", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "HeadlessOne", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "43148509-fa06-41d1-9ae9-f6cbf2b9cd63", - "name": "Spawner (103)", - "location": [5560, 1876, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, "homeRange": 15, "walkingRange": 25, "entries": [ - { "name": "Mummy", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "437c19eb-a47b-4477-88f2-90e3f8171a94", + "type": "Spawner", + "guid": "22bd9b72-f463-431e-bd87-17e25e1c077f", "name": "Spawner (103)", - "location": [2493, 883, 0], + "location": [5473, 1991, 0], "map": "Trammel", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, + "homeRange": 9, "walkingRange": 25, "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 } + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "4755ff86-849d-443e-aaf9-4da5a523f28c", - "name": "Spawner (103)", - "location": [5387, 1911, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49d2dedd-e659-40b1-8faa-92ec79d5ebc6", - "name": "Spawner (103)", - "location": [5413, 2000, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d0223bc-6ca5-4794-ae48-16a6969f44e3", - "name": "Spawner (103)", - "location": [5461, 1818, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "58e61f09-1d8e-4eb0-bdee-18678d4d7ca4", - "name": "Spawner (103)", - "location": [5503, 2002, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5cb18cd8-da7a-414d-9f4d-f9b20709b796", - "name": "Spawner (103)", - "location": [5443, 1918, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 30, - "entries": [ - { "name": "StoneHarpy", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60678191-e2a8-48ee-9dc7-56ff237b3ddb", - "name": "Spawner (103)", - "location": [5579, 1859, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 11, - "walkingRange": 25, - "entries": [ - { "name": "Lich", "maxCount": 7, "probability": 100 }, - { "name": "Spectre", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "6e4df3f9-6915-4733-9efb-f999f6725e3e", "name": "Spawner (103)", "location": [5457, 1971, 0], @@ -386,67 +329,35 @@ ] }, { - "$type": "Spawner", - "guid": "6e763f3e-fd66-40af-9c05-e3bd8604b646", + "type": "Spawner", + "guid": "ca031256-07f4-4a12-8303-54c2c2606afc", "name": "Spawner (103)", - "location": [5452, 1799, 0], + "location": [5436, 1972, 0], "map": "Trammel", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "792c60a4-bf05-4088-a0f1-295a8cbcc26d", + "type": "Spawner", + "guid": "824e3fe5-d203-4b2c-82b1-4c6af496a131", "name": "Spawner (103)", - "location": [5596, 1903, 0], + "location": [5417, 1975, 0], "map": "Trammel", - "count": 7, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Mummy", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } - ] + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "7a96822d-8531-469e-b32b-42ca4c914e5c", - "name": "Spawner (103)", - "location": [5615, 1838, 0], - "map": "Trammel", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 25, - "entries": [ - { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "7d782c02-bb11-4a7b-a864-c6aa0d9372b6", "name": "Spawner (103)", "location": [5429, 1989, 0], @@ -463,7 +374,103 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "fce765ed-6ec5-45ae-94e6-762879f661a9", + "name": "Spawner (103)", + "location": [5425, 1992, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc2ab61a-331b-4b29-8639-552cb33a9a7b", + "name": "Spawner (103)", + "location": [5388, 2012, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Gazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "58e61f09-1d8e-4eb0-bdee-18678d4d7ca4", + "name": "Spawner (103)", + "location": [5503, 2002, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49d2dedd-e659-40b1-8faa-92ec79d5ebc6", + "name": "Spawner (103)", + "location": [5413, 2000, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1fc3857a-ad6d-467c-9feb-df23f3192492", + "name": "Spawner (103)", + "location": [5460, 1975, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e3bb584-e75f-47d2-b3ca-9926aef55326", + "name": "Spawner (103)", + "location": [5448, 2027, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "807182c4-b6fd-4053-a8a3-14b83dff0d1e", "name": "Spawner (103)", "location": [2456, 861, 0], @@ -474,44 +481,52 @@ "team": 0, "homeRange": 10, "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "824e3fe5-d203-4b2c-82b1-4c6af496a131", + "type": "Spawner", + "guid": "de81051f-f31d-4254-b09b-3a629e29f763", "name": "Spawner (103)", - "location": [5417, 1975, 0], + "location": [2480, 863, 0], "map": "Trammel", - "count": 3, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Corpser", "maxCount": 3, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "84ba2d57-7801-4461-8ed9-c7d45c95f473", + "type": "Spawner", + "guid": "437c19eb-a47b-4477-88f2-90e3f8171a94", "name": "Spawner (103)", - "location": [5400, 1937, 0], + "location": [2493, 883, 0], "map": "Trammel", - "count": 3, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Corpser", "maxCount": 3, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "03fd7675-5b18-4bd1-b886-549fff24a24e", + "name": "Spawner (103)", + "location": [2513, 862, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "8a7ef603-1a81-4834-afba-20cbe04710ba", "name": "Spawner (103)", "location": [5560, 1903, 0], @@ -533,26 +548,51 @@ ] }, { - "$type": "Spawner", - "guid": "94f3b7ec-a856-4779-8ad6-0f90895790a0", + "type": "Spawner", + "guid": "792c60a4-bf05-4088-a0f1-295a8cbcc26d", "name": "Spawner (103)", - "location": [5617, 1861, 0], + "location": [5596, 1903, 0], "map": "Trammel", - "count": 6, + "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 8, + "homeRange": 15, "walkingRange": 25, "entries": [ - { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 } + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "43148509-fa06-41d1-9ae9-f6cbf2b9cd63", + "name": "Spawner (103)", + "location": [5560, 1876, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "a1bc2e58-de79-4851-9b49-119ff6a2609a", "name": "Spawner (103)", "location": [5603, 1875, 0], @@ -571,7 +611,109 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "94f3b7ec-a856-4779-8ad6-0f90895790a0", + "name": "Spawner (103)", + "location": [5617, 1861, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7a96822d-8531-469e-b32b-42ca4c914e5c", + "name": "Spawner (103)", + "location": [5615, 1838, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60678191-e2a8-48ee-9dc7-56ff237b3ddb", + "name": "Spawner (103)", + "location": [5579, 1859, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 11, + "walkingRange": 25, + "entries": [ + { "name": "Lich", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1b287d2c-4992-4071-a577-23a460b7af73", + "name": "Spawner (103)", + "location": [5560, 1843, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 40, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15e19e7f-7de5-412c-873e-e38fd7f4c99d", + "name": "Spawner (103)", + "location": [5528, 1808, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "a827c58c-f729-41d8-a600-7e85d4fa2fb5", "name": "Spawner (103)", "location": [5504, 1808, 0], @@ -589,215 +731,37 @@ ] }, { - "$type": "Spawner", - "guid": "af277400-a7ad-4048-bdb1-fdcc10b20756", + "type": "Spawner", + "guid": "6e763f3e-fd66-40af-9c05-e3bd8604b646", "name": "Spawner (103)", - "location": [5393, 1921, 0], + "location": [5452, 1799, 0], "map": "Trammel", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Corpser", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b005fb7e-e674-496d-89f1-ee8d3b8961e0", - "name": "Spawner (103)", - "location": [5440, 2023, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "DreadSpider", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b180d203-9331-4077-86a1-5cec055f0fc8", - "name": "Spawner (103)", - "location": [5593, 2017, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bc45c254-4539-4c60-a37f-2711c2e2ddec", - "name": "Spawner (103)", - "location": [5418, 1876, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, + "homeRange": 20, "walkingRange": 30, "entries": [ - { "name": "Gazer", "maxCount": 4, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "be80b563-6951-4564-869e-e64efe2b116a", + "type": "Spawner", + "guid": "4d0223bc-6ca5-4794-ae48-16a6969f44e3", "name": "Spawner (103)", - "location": [5411, 1901, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gazer", "maxCount": 2, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ca031256-07f4-4a12-8303-54c2c2606afc", - "name": "Spawner (103)", - "location": [5436, 1972, 0], + "location": [5461, 1818, 0], "map": "Trammel", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, + "homeRange": 8, + "walkingRange": 8, "entries": [ - { "name": "Corpser", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cdffe875-e196-4511-b07b-bafe2547279b", - "name": "Spawner (103)", - "location": [5469, 2019, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Slime", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d56c22bc-e344-47d6-ae5f-d65acdea6940", - "name": "Spawner (103)", - "location": [5498, 1998, 0], - "map": "Trammel", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "StoneHarpy", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "de81051f-f31d-4254-b09b-3a629e29f763", - "name": "Spawner (103)", - "location": [2480, 863, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df30762f-98e0-4753-97ef-d2e548612633", - "name": "Spawner (103)", - "location": [5558, 2025, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec453c02-9ef8-40bb-8e12-06c74c0ce96e", - "name": "Spawner (103)", - "location": [5577, 2006, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fc2ab61a-331b-4b29-8639-552cb33a9a7b", - "name": "Spawner (103)", - "location": [5388, 2012, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Gazer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fce765ed-6ec5-45ae-94e6-762879f661a9", - "name": "Spawner (103)", - "location": [5425, 1992, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Corpser", "maxCount": 2, "probability": 100 } + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/Deceit.json b/Distribution/Data/Spawns/post-uoml/trammel/Deceit.json similarity index 91% rename from Distribution/Data/Spawns/shared/trammel/Deceit.json rename to Distribution/Data/Spawns/post-uoml/trammel/Deceit.json index d470122c6..c7964280f 100644 --- a/Distribution/Data/Spawns/shared/trammel/Deceit.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Deceit.json @@ -1,61 +1,6 @@ [ { - "$type": "Spawner", - "guid": "20f986bd-7976-415c-bf34-2df8bde4025e", - "name": "Spawner (104)", - "location": [5150, 742, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 15, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "28f3eccc-1c12-4576-ae46-e9cfb11fb19f", - "name": "Spawner (104)", - "location": [5163, 567, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 25, - "entries": [ - { "name": "Mummy", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 3, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3dfec3c5-2cc6-4908-b975-51182b394218", - "name": "Spawner (104)", - "location": [5286, 542, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "4563aa47-639e-47f3-a301-c430a6e1777a", "name": "Spawner (104)", "location": [5187, 603, 0], @@ -73,7 +18,70 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "94066b8f-d45a-4273-b649-ab593fbe55e8", + "name": "Spawner (104)", + "location": [5186, 575, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 12, + "entries": [{ "name": "WaterElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f4c26861-33c2-4f4b-af35-62f556e385cb", + "name": "Spawner (104)", + "location": [5187, 545, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "28f3eccc-1c12-4576-ae46-e9cfb11fb19f", + "name": "Spawner (104)", + "location": [5163, 567, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 25, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b83d71f9-6539-4a90-a43a-4feb8ee48678", + "name": "Spawner (104)", + "location": [5145, 553, -40], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 35, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "462bded2-9162-4b79-a1a3-7d35a889f899", "name": "Spawner (104)", "location": [5147, 579, -50], @@ -90,23 +98,132 @@ ] }, { - "$type": "Spawner", - "guid": "4afcb2a6-cecf-4d42-a449-de7e061ccd6c", + "type": "Spawner", + "guid": "5da72c5b-46fd-4a13-962f-3accc945f462", "name": "Spawner (104)", - "location": [5190, 655, 0], + "location": [5147, 598, -50], "map": "Trammel", - "count": 2, + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5c3582a8-fa9c-4839-aff6-82457f87f023", + "name": "Spawner (104)", + "location": [5145, 622, -50], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5018fced-3b89-4654-88ac-143ceed1e566", + "name": "Spawner (104)", + "location": [5212, 531, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d729f42a-87ef-4a42-8a3b-b65ae177ab40", + "name": "Spawner (104)", + "location": [5219, 552, 0], + "map": "Trammel", + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 25, "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "f2dac00d-2532-4fc4-8ae2-bde51687b3e1", + "name": "Spawner (104)", + "location": [5219, 573, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b1e99f8-bde4-41f5-a76d-78b9dca9844c", + "name": "Spawner (104)", + "location": [5145, 618, -50], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d846b9eb-23d3-4b86-8a61-20331e2cf8db", + "name": "Spawner (104)", + "location": [5307, 546, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "4fb75ec8-f949-4480-b573-064a299b34c0", "name": "Spawner (104)", "location": [5327, 542, 0], @@ -125,127 +242,26 @@ ] }, { - "$type": "Spawner", - "guid": "5018fced-3b89-4654-88ac-143ceed1e566", + "type": "Spawner", + "guid": "3dfec3c5-2cc6-4908-b975-51182b394218", "name": "Spawner (104)", - "location": [5212, 531, 0], + "location": [5286, 542, 0], "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "564f98cb-f8fe-43dd-a180-9da4652a4f1e", - "name": "Spawner (104)", - "location": [5342, 582, 0], - "map": "Trammel", - "count": 8, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 25, "entries": [ - { "name": "BoneKnight", "maxCount": 4, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "591e02f3-9855-42a6-ae25-d11d0fbe788b", - "name": "Spawner (104)", - "location": [5323, 571, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5b1e99f8-bde4-41f5-a76d-78b9dca9844c", - "name": "Spawner (104)", - "location": [5145, 618, -50], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5c3582a8-fa9c-4839-aff6-82457f87f023", - "name": "Spawner (104)", - "location": [5145, 622, -50], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5da72c5b-46fd-4a13-962f-3accc945f462", - "name": "Spawner (104)", - "location": [5147, 598, -50], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6006bae6-ddb4-4c35-9c38-c1378f71e584", - "name": "Spawner (104)", - "location": [5267, 690, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 35, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "6ac0a6d7-ac00-48c9-bb46-827c103b44ef", "name": "Spawner (104)", "location": [5283, 583, 0], @@ -262,168 +278,7 @@ ] }, { - "$type": "Spawner", - "guid": "7038def8-3dd9-481a-aff9-b344e58e0122", - "name": "Spawner (104)", - "location": [5151, 718, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Lich", "maxCount": 3, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7a2d79d7-d842-4977-8449-5d7735e73c6f", - "name": "Spawner (104)", - "location": [5280, 675, 8], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7f6975e8-9689-441b-b484-e0c803cf29db", - "name": "Spawner (104)", - "location": [5170, 678, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8a63e78b-f33d-4502-9760-81c36f272658", - "name": "Spawner (104)", - "location": [5299, 603, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8f579b88-cb8e-4c17-b795-fd4d1d3d69d2", - "name": "Spawner (104)", - "location": [5320, 708, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Lich", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94066b8f-d45a-4273-b649-ab593fbe55e8", - "name": "Spawner (104)", - "location": [5186, 575, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 12, - "entries": [ - { "name": "WaterElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9f4cf647-abba-4181-aa0a-b477d21800c4", - "name": "Spawner (104)", - "location": [5322, 626, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a3e5c4ff-6bd0-4380-aebd-47ee3c1c1421", - "name": "Spawner (104)", - "location": [5314, 748, -20], - "map": "Trammel", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 25, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }, - { "name": "Lich", "maxCount": 3, "probability": 100 }, - { "name": "LichLord", "maxCount": 1, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a44b5c06-0cda-4edc-8468-fa4a00a52b1e", - "name": "Spawner (104)", - "location": [5295, 623, -5], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 10, - "entries": [ - { "name": "BoneMagi", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "b094c982-2cfd-42ff-ab54-b175a134de30", "name": "Spawner (104)", "location": [5318, 578, 0], @@ -443,76 +298,94 @@ ] }, { - "$type": "Spawner", - "guid": "b0cf1788-6671-41ad-aa47-4e8287fbac5e", + "type": "Spawner", + "guid": "564f98cb-f8fe-43dd-a180-9da4652a4f1e", "name": "Spawner (104)", - "location": [5212, 674, -20], + "location": [5342, 582, 0], "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b83d71f9-6539-4a90-a43a-4feb8ee48678", - "name": "Spawner (104)", - "location": [5145, 553, -40], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 35, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf4c7226-e5d1-4d90-b70c-cc4d3ad472cb", - "name": "Spawner (104)", - "location": [5307, 675, -20], - "map": "Trammel", - "count": 3, + "count": 8, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 25, "entries": [ - { "name": "Ghoul", "maxCount": 2, "probability": 100 }, - { "name": "Lich", "maxCount": 1, "probability": 100 } + { "name": "BoneKnight", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "cd67b487-cfbb-436c-9623-b05e87602698", + "type": "Spawner", + "guid": "8a63e78b-f33d-4502-9760-81c36f272658", "name": "Spawner (104)", - "location": [5219, 734, -20], + "location": [5299, 603, 0], "map": "Trammel", - "count": 4, + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a44b5c06-0cda-4edc-8468-fa4a00a52b1e", + "name": "Spawner (104)", + "location": [5295, 623, -5], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "BoneMagi", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9f4cf647-abba-4181-aa0a-b477d21800c4", + "name": "Spawner (104)", + "location": [5322, 626, 0], + "map": "Trammel", + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 15, "walkingRange": 25, "entries": [ - { "name": "Lich", "maxCount": 4, "probability": 100 } + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "591e02f3-9855-42a6-ae25-d11d0fbe788b", + "name": "Spawner (104)", + "location": [5323, 571, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "d2d34809-99ba-41b9-9f74-ed1a65248ca0", "name": "Spawner (104)", "location": [5139, 661, 0], @@ -531,29 +404,24 @@ ] }, { - "$type": "Spawner", - "guid": "d729f42a-87ef-4a42-8a3b-b65ae177ab40", + "type": "Spawner", + "guid": "4afcb2a6-cecf-4d42-a449-de7e061ccd6c", "name": "Spawner (104)", - "location": [5219, 552, 0], + "location": [5190, 655, 0], "map": "Trammel", - "count": 6, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 25, - "entries": [ - { "name": "Ghoul", "maxCount": 3, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "d846b9eb-23d3-4b86-8a61-20331e2cf8db", + "type": "Spawner", + "guid": "7f6975e8-9689-441b-b484-e0c803cf29db", "name": "Spawner (104)", - "location": [5307, 546, 0], + "location": [5170, 678, 0], "map": "Trammel", "count": 4, "minDelay": "00:05:00", @@ -563,16 +431,16 @@ "walkingRange": 25, "entries": [ { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f2dac00d-2532-4fc4-8ae2-bde51687b3e1", + "type": "Spawner", + "guid": "7038def8-3dd9-481a-aff9-b344e58e0122", "name": "Spawner (104)", - "location": [5219, 573, 0], + "location": [5151, 718, 0], "map": "Trammel", "count": 6, "minDelay": "00:05:00", @@ -581,14 +449,31 @@ "homeRange": 10, "walkingRange": 25, "entries": [ - { "name": "Ghoul", "maxCount": 3, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 } + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "f6f496d1-7f72-4e0a-be1f-e8249bc31633", + "name": "Spawner (104)", + "location": [5192, 695, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f3f74896-465e-4cc7-bb83-63d508157390", "name": "Spawner (104)", "location": [5183, 727, 0], @@ -605,36 +490,137 @@ ] }, { - "$type": "Spawner", - "guid": "f4c26861-33c2-4f4b-af35-62f556e385cb", + "type": "Spawner", + "guid": "b0cf1788-6671-41ad-aa47-4e8287fbac5e", "name": "Spawner (104)", - "location": [5187, 545, 0], + "location": [5212, 674, -20], "map": "Trammel", - "count": 2, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 6, + "homeRange": 12, "walkingRange": 25, "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 } + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f6f496d1-7f72-4e0a-be1f-e8249bc31633", + "type": "Spawner", + "guid": "cd67b487-cfbb-436c-9623-b05e87602698", "name": "Spawner (104)", - "location": [5192, 695, 0], + "location": [5219, 734, -20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "20f986bd-7976-415c-bf34-2df8bde4025e", + "name": "Spawner (104)", + "location": [5150, 742, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf4c7226-e5d1-4d90-b70c-cc4d3ad472cb", + "name": "Spawner (104)", + "location": [5307, 675, -20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7a2d79d7-d842-4977-8449-5d7735e73c6f", + "name": "Spawner (104)", + "location": [5280, 675, 8], "map": "Trammel", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 20, + "walkingRange": 25, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8f579b88-cb8e-4c17-b795-fd4d1d3d69d2", + "name": "Spawner (104)", + "location": [5320, 708, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a3e5c4ff-6bd0-4380-aebd-47ee3c1c1421", + "name": "Spawner (104)", + "location": [5314, 748, -20], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, "entries": [ - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6006bae6-ddb4-4c35-9c38-c1378f71e584", + "name": "Spawner (104)", + "location": [5267, 690, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 35, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/Despise.json b/Distribution/Data/Spawns/post-uoml/trammel/Despise.json similarity index 77% rename from Distribution/Data/Spawns/shared/trammel/Despise.json rename to Distribution/Data/Spawns/post-uoml/trammel/Despise.json index ef181ac5d..73276ed2a 100644 --- a/Distribution/Data/Spawns/shared/trammel/Despise.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Despise.json @@ -1,345 +1,6 @@ [ { - "$type": "Spawner", - "guid": "0c1a2c73-e451-4e78-8fff-e320bb67ea7d", - "name": "Spawner (105)", - "location": [5420, 746, 5], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 18, - "walkingRange": 25, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Ettin", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0c46651c-0760-476e-8ebe-d51ca00006bf", - "name": "Spawner (105)", - "location": [5426, 791, 60], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0de2bfd4-ccb8-4e8c-88b7-e234eb2f5122", - "name": "Spawner (105)", - "location": [5483, 711, 15], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "193bc157-aeb6-4e74-88e4-11e980858cec", - "name": "Spawner (105)", - "location": [5564, 821, 45], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1eb2840b-690f-4f10-80d0-00a7e8d22a07", - "name": "Spawner (105)", - "location": [5475, 748, 5], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20ae1a8a-90cd-4046-819f-f7eae50c6b24", - "name": "Spawner (105)", - "location": [5405, 576, 61], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20f0fb49-eaea-4b6b-a14d-0868d204ceaf", - "name": "Spawner (105)", - "location": [5441, 680, 20], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "EarthElemental", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "25b5c0ec-29bf-4618-b857-9e68e23fa088", - "name": "Spawner (105)", - "location": [5398, 996, 5], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2fbd19bb-2caa-494d-ac09-70784915cc6e", - "name": "Spawner (105)", - "location": [5590, 871, 45], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 35, - "entries": [ - { "name": "Ogre", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "306d0efd-88fc-4880-a46d-d2f9e5b04ec7", - "name": "Spawner (105)", - "location": [5487, 902, 30], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "312b70e0-4afc-411d-8f1a-d902ea0f9e16", - "name": "Spawner (105)", - "location": [5504, 597, 45], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "36aa83bc-3e2a-42f8-bdeb-0ee9cb004a64", - "name": "Spawner (105)", - "location": [5490, 676, 20], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "39d6eab1-27c8-4f51-b3d7-4befdbd17478", - "name": "Spawner (105)", - "location": [5410, 611, 45], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e8b1378-fe18-4c3b-9e86-5b35a30a1c6f", - "name": "Spawner (105)", - "location": [5482, 791, 60], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41a2d01b-14a5-4b87-a4fc-85053f7ae31a", - "name": "Spawner (105)", - "location": [5460, 526, 60], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "462a3304-fa2a-4473-99f4-438e9f55886e", - "name": "Spawner (105)", - "location": [5439, 851, 45], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4f3cb4cd-569f-4ad1-9343-e7fe4eb4775a", - "name": "Spawner (105)", - "location": [5464, 600, 45], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5329add2-7d3f-4884-8b4c-61dd7b78d29b", - "name": "Spawner (105)", - "location": [5593, 794, 60], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e82bdcb-378c-4879-af67-03ecc96f3ff0", - "name": "Spawner (105)", - "location": [5391, 905, 30], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60ec080d-a3a9-44a8-b00c-e1d1bb81818f", - "name": "Spawner (105)", - "location": [5394, 525, 65], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "68d435c5-668c-4af0-a9fd-5e9c3e2002d0", - "name": "Spawner (105)", - "location": [5532, 787, 60], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ogre", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "6c3fc410-6d7c-4e7e-80fb-62d05f146e28", "name": "Spawner (105)", "location": [5503, 529, 60], @@ -357,7 +18,63 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "312b70e0-4afc-411d-8f1a-d902ea0f9e16", + "name": "Spawner (105)", + "location": [5504, 597, 45], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4f3cb4cd-569f-4ad1-9343-e7fe4eb4775a", + "name": "Spawner (105)", + "location": [5464, 600, 45], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff5edaf2-353f-4e22-8d77-75ba2cb489be", + "name": "Spawner (105)", + "location": [5435, 589, 47], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "39d6eab1-27c8-4f51-b3d7-4befdbd17478", + "name": "Spawner (105)", + "location": [5410, 611, 45], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "6eb7ccc4-5d54-4e56-b637-4726b2606957", "name": "Spawner (105)", "location": [5386, 612, 45], @@ -375,7 +92,21 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "20ae1a8a-90cd-4046-819f-f7eae50c6b24", + "name": "Spawner (105)", + "location": [5405, 576, 61], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "77392dd3-f72b-43fa-b5b4-9ce81858fe4f", "name": "Spawner (105)", "location": [5411, 541, 60], @@ -386,29 +117,41 @@ "team": 0, "homeRange": 12, "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "8ecf5903-3e52-48af-8a3e-3deececd5314", + "type": "Spawner", + "guid": "41a2d01b-14a5-4b87-a4fc-85053f7ae31a", "name": "Spawner (105)", - "location": [5546, 888, 30], + "location": [5460, 526, 60], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "60ec080d-a3a9-44a8-b00c-e1d1bb81818f", + "name": "Spawner (105)", + "location": [5394, 525, 65], "map": "Trammel", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 8, - "walkingRange": 8, + "homeRange": 5, + "walkingRange": 5, "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "90882c2e-a2b4-4c37-905e-b87f5ed61a60", "name": "Spawner (105)", "location": [5390, 587, 45], @@ -425,40 +168,35 @@ ] }, { - "$type": "Spawner", - "guid": "9a3db886-d50c-4e9c-b241-c3a0c5ee9e98", + "type": "Spawner", + "guid": "36aa83bc-3e2a-42f8-bdeb-0ee9cb004a64", "name": "Spawner (105)", - "location": [5470, 860, 45], + "location": [5490, 676, 20], "map": "Trammel", - "count": 3, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 12, "walkingRange": 25, - "entries": [ - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "9ed82ee8-dd07-43ef-a454-a1dd5e964fa7", + "type": "Spawner", + "guid": "0de2bfd4-ccb8-4e8c-88b7-e234eb2f5122", "name": "Spawner (105)", - "location": [5507, 656, 20], + "location": [5483, 711, 15], "map": "Trammel", - "count": 3, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9fcd5ea5-3722-49ef-8d15-8c6f86f1d10c", "name": "Spawner (105)", "location": [5505, 747, 5], @@ -475,7 +213,41 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "1eb2840b-690f-4f10-80d0-00a7e8d22a07", + "name": "Spawner (105)", + "location": [5475, 748, 5], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c1a2c73-e451-4e78-8fff-e320bb67ea7d", + "name": "Spawner (105)", + "location": [5420, 746, 5], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 25, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ettin", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "ac09482f-cf78-4b67-849e-81d969044806", "name": "Spawner (105)", "location": [5385, 703, 15], @@ -486,76 +258,55 @@ "team": 0, "homeRange": 15, "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "b7c5f3ca-e60d-4623-b31c-86583932aa77", + "type": "Spawner", + "guid": "d710fdd3-1600-4436-a76b-7062ab33aa72", "name": "Spawner (105)", - "location": [5426, 943, 20], + "location": [5391, 660, 20], "map": "Trammel", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, + "homeRange": 12, "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "cab315f4-6eaf-4772-a991-c2f4ee6c5284", + "type": "Spawner", + "guid": "20f0fb49-eaea-4b6b-a14d-0868d204ceaf", "name": "Spawner (105)", - "location": [5471, 973, 15], + "location": [5441, 680, 20], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "EarthElemental", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ed82ee8-dd07-43ef-a454-a1dd5e964fa7", + "name": "Spawner (105)", + "location": [5507, 656, 20], "map": "Trammel", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 18, - "walkingRange": 25, + "homeRange": 5, + "walkingRange": 5, "entries": [ - { "name": "Cyclops", "maxCount": 3, "probability": 100 } + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "cd736cc2-c0d3-4259-bbe2-949af88f94e7", - "name": "Spawner (105)", - "location": [5504, 939, 20], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cf63c3f7-751e-498b-bc4f-3e185280593b", - "name": "Spawner (105)", - "location": [5557, 828, 45], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "d35770ae-714d-4f38-b2ea-ce1e4320a82e", "name": "Spawner (105)", "location": [5387, 697, 15], @@ -572,55 +323,77 @@ ] }, { - "$type": "Spawner", - "guid": "d710fdd3-1600-4436-a76b-7062ab33aa72", + "type": "Spawner", + "guid": "462a3304-fa2a-4473-99f4-438e9f55886e", "name": "Spawner (105)", - "location": [5391, 660, 20], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d88f3a9b-067f-48cc-ba7f-636f89d9445e", - "name": "Spawner (105)", - "location": [5605, 799, 60], + "location": [5439, 851, 45], "map": "Trammel", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 } - ] + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "de0083f1-6b11-411b-8875-f94bbd11846b", + "type": "Spawner", + "guid": "9a3db886-d50c-4e9c-b241-c3a0c5ee9e98", "name": "Spawner (105)", - "location": [5558, 824, 45], + "location": [5470, 860, 45], "map": "Trammel", - "count": 5, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 12, "walkingRange": 25, - "entries": [ - { "name": "OgreLord", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "0c46651c-0760-476e-8ebe-d51ca00006bf", + "name": "Spawner (105)", + "location": [5426, 791, 60], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3e8b1378-fe18-4c3b-9e86-5b35a30a1c6f", + "name": "Spawner (105)", + "location": [5482, 791, 60], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "68d435c5-668c-4af0-a9fd-5e9c3e2002d0", + "name": "Spawner (105)", + "location": [5532, 787, 60], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "e6a67238-55d8-4735-8ac3-7efcc25d3193", "name": "Spawner (105)", "location": [5604, 789, 60], @@ -631,12 +404,38 @@ "team": 0, "homeRange": 15, "walkingRange": 25, - "entries": [ - { "name": "Ogre", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "de0083f1-6b11-411b-8875-f94bbd11846b", + "name": "Spawner (105)", + "location": [5558, 824, 45], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "OgreLord", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2fbd19bb-2caa-494d-ac09-70784915cc6e", + "name": "Spawner (105)", + "location": [5590, 871, 45], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "e6b0982f-8c36-4bc7-be61-f5fb371fd66f", "name": "Spawner (105)", "location": [5528, 902, 30], @@ -647,12 +446,125 @@ "team": 0, "homeRange": 15, "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "306d0efd-88fc-4880-a46d-d2f9e5b04ec7", + "name": "Spawner (105)", + "location": [5487, 902, 30], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cd736cc2-c0d3-4259-bbe2-949af88f94e7", + "name": "Spawner (105)", + "location": [5504, 939, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cab315f4-6eaf-4772-a991-c2f4ee6c5284", + "name": "Spawner (105)", + "location": [5471, 973, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 25, + "entries": [{ "name": "Cyclops", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b7c5f3ca-e60d-4623-b31c-86583932aa77", + "name": "Spawner (105)", + "location": [5426, 943, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5e82bdcb-378c-4879-af67-03ecc96f3ff0", + "name": "Spawner (105)", + "location": [5391, 905, 30], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "25b5c0ec-29bf-4618-b857-9e68e23fa088", + "name": "Spawner (105)", + "location": [5398, 996, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "AcidElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5329add2-7d3f-4884-8b4c-61dd7b78d29b", + "name": "Spawner (105)", + "location": [5593, 794, 60], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8ecf5903-3e52-48af-8a3e-3deececd5314", + "name": "Spawner (105)", + "location": [5546, 888, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, "entries": [ - { "name": "Ogre", "maxCount": 3, "probability": 100 } + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e9e70e19-7819-454d-8641-1fab82224c65", "name": "Spawner (105)", "location": [5397, 995, 5], @@ -663,12 +575,38 @@ "team": 0, "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "d88f3a9b-067f-48cc-ba7f-636f89d9445e", + "name": "Spawner (105)", + "location": [5605, 799, 60], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cf63c3f7-751e-498b-bc4f-3e185280593b", + "name": "Spawner (105)", + "location": [5557, 828, 45], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "f9357ebd-0256-47bb-b849-2add953fab65", "name": "Spawner (105)", "location": [5398, 995, 5], @@ -679,24 +617,20 @@ "team": 0, "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ff5edaf2-353f-4e22-8d77-75ba2cb489be", + "type": "Spawner", + "guid": "193bc157-aeb6-4e74-88e4-11e980858cec", "name": "Spawner (105)", - "location": [5435, 589, 47], + "location": [5564, 821, 45], "map": "Trammel", - "count": 5, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/Destard.json b/Distribution/Data/Spawns/post-uoml/trammel/Destard.json similarity index 89% rename from Distribution/Data/Spawns/shared/trammel/Destard.json rename to Distribution/Data/Spawns/post-uoml/trammel/Destard.json index a4f48b6a4..bf7dfd3ce 100644 --- a/Distribution/Data/Spawns/shared/trammel/Destard.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Destard.json @@ -1,76 +1,6 @@ [ { - "$type": "Spawner", - "guid": "01f5635b-645f-43a3-b8e1-de505a9805f4", - "name": "Spawner (106)", - "location": [5269, 811, 7], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "039faa13-d838-4ccf-8790-13500ac30d28", - "name": "Spawner (106)", - "location": [5141, 834, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "ShadowWyrm", "maxCount": 1, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f5e5a38-ef2b-4e49-8574-b5349c21dd06", - "name": "Spawner (106)", - "location": [5352, 934, -5], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "WaterElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22700190-506d-4f55-be3b-4ecf63f097a1", - "name": "Spawner (106)", - "location": [5152, 869, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 50, - "entries": [ - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "ShadowWyrm", "maxCount": 1, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "2d985bfc-95b3-40be-8a3e-f1919da38b19", "name": "Spawner (106)", "location": [5209, 965, -40], @@ -88,23 +18,7 @@ ] }, { - "$type": "Spawner", - "guid": "31627f88-cd2c-42f2-8aa2-df019970100f", - "name": "Spawner (106)", - "location": [5294, 842, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "WaterElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "34426acb-8319-4eb6-8760-d0acb86670f9", "name": "Spawner (106)", "location": [5247, 956, -40], @@ -121,145 +35,10 @@ ] }, { - "$type": "Spawner", - "guid": "45519d94-93ec-4b71-9fcd-0693de58f4e8", + "type": "Spawner", + "guid": "fce8877c-f374-4e24-9d3f-65205427600e", "name": "Spawner (106)", - "location": [5229, 841, 1], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4a321325-9193-4861-a25e-c6f075636a07", - "name": "Spawner (106)", - "location": [5241, 945, -40], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4dea2c35-b11b-43cd-9916-6ee8d03105c7", - "name": "Spawner (106)", - "location": [5153, 842, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 4, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "58412982-34fe-41e4-9c85-cd0d5691d62c", - "name": "Spawner (106)", - "location": [5143, 986, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "68815cec-f528-4033-be41-04c6d75415e1", - "name": "Spawner (106)", - "location": [5161, 940, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 50, - "entries": [ - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "699dfc8f-e891-4978-9c73-a74cb1762849", - "name": "Spawner (106)", - "location": [5185, 1006, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 50, - "entries": [ - { "name": "AncientWyrm", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b05c0d4-7c45-4d29-9f15-adbb0b12686f", - "name": "Spawner (106)", - "location": [5141, 915, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "73ef3d35-2c97-40c0-8c9d-f949bedf4bf0", - "name": "Spawner (106)", - "location": [5169, 836, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "89faa9ac-5c83-4145-a239-0f1bbc3088a6", - "name": "Spawner (106)", - "location": [5226, 826, 0], + "location": [5280, 955, -40], "map": "Trammel", "count": 4, "minDelay": "00:05:00", @@ -274,23 +53,7 @@ ] }, { - "$type": "Spawner", - "guid": "8a097ab1-3372-4f64-bb50-b18470eed97f", - "name": "Spawner (106)", - "location": [5250, 869, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "95541af6-6526-41ad-8329-a9819a3a1f8d", "name": "Spawner (106)", "location": [5284, 926, -40], @@ -308,93 +71,7 @@ ] }, { - "$type": "Spawner", - "guid": "9f0b2731-3658-43bd-9fe4-3bca1a7c5212", - "name": "Spawner (106)", - "location": [5317, 980, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b2eb4c30-1f29-4ef5-b80a-851c68af76f9", - "name": "Spawner (106)", - "location": [5157, 997, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 50, - "entries": [ - { "name": "Wyvern", "maxCount": 3, "probability": 100 }, - { "name": "Wyvern", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b705ea99-1d95-42ff-8287-fb6588d0ed24", - "name": "Spawner (106)", - "location": [5149, 907, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "Wyvern", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cde84676-d0c3-44e7-8be3-9335dc9c5b1b", - "name": "Spawner (106)", - "location": [5203, 787, 5], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "WaterElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d6da5d02-55ae-4e14-8aec-e02b2e7cf188", - "name": "Spawner (106)", - "location": [5307, 815, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "da94b96f-7cbc-4016-91b5-6ec3df649e23", "name": "Spawner (106)", "location": [5214, 917, -40], @@ -412,7 +89,364 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "4a321325-9193-4861-a25e-c6f075636a07", + "name": "Spawner (106)", + "location": [5241, 945, -40], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68815cec-f528-4033-be41-04c6d75415e1", + "name": "Spawner (106)", + "location": [5161, 940, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [{ "name": "GiantSerpent", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b705ea99-1d95-42ff-8287-fb6588d0ed24", + "name": "Spawner (106)", + "location": [5149, 907, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b05c0d4-7c45-4d29-9f15-adbb0b12686f", + "name": "Spawner (106)", + "location": [5141, 915, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f5e5a38-ef2b-4e49-8574-b5349c21dd06", + "name": "Spawner (106)", + "location": [5352, 934, -5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9f0b2731-3658-43bd-9fe4-3bca1a7c5212", + "name": "Spawner (106)", + "location": [5317, 980, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a097ab1-3372-4f64-bb50-b18470eed97f", + "name": "Spawner (106)", + "location": [5250, 869, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "GiantSerpent", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "89faa9ac-5c83-4145-a239-0f1bbc3088a6", + "name": "Spawner (106)", + "location": [5226, 826, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01f5635b-645f-43a3-b8e1-de505a9805f4", + "name": "Spawner (106)", + "location": [5269, 811, 7], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6da5d02-55ae-4e14-8aec-e02b2e7cf188", + "name": "Spawner (106)", + "location": [5307, 815, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31627f88-cd2c-42f2-8aa2-df019970100f", + "name": "Spawner (106)", + "location": [5294, 842, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cde84676-d0c3-44e7-8be3-9335dc9c5b1b", + "name": "Spawner (106)", + "location": [5203, 787, 5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "45519d94-93ec-4b71-9fcd-0693de58f4e8", + "name": "Spawner (106)", + "location": [5229, 841, 1], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "def6615c-5bbd-4b7c-ab81-4dc4327c97d8", + "name": "Spawner (106)", + "location": [5205, 778, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "039faa13-d838-4ccf-8790-13500ac30d28", + "name": "Spawner (106)", + "location": [5141, 834, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "ShadowWyrm", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73ef3d35-2c97-40c0-8c9d-f949bedf4bf0", + "name": "Spawner (106)", + "location": [5169, 836, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "EvilMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22700190-506d-4f55-be3b-4ecf63f097a1", + "name": "Spawner (106)", + "location": [5152, 869, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "ShadowWyrm", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4dea2c35-b11b-43cd-9916-6ee8d03105c7", + "name": "Spawner (106)", + "location": [5153, 842, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "TreasureChestLevel4", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb1e3f19-4276-4b1f-b87f-0b4e38ebd70b", + "name": "Spawner (106)", + "location": [5141, 968, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b2eb4c30-1f29-4ef5-b80a-851c68af76f9", + "name": "Spawner (106)", + "location": [5157, 997, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [ + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "699dfc8f-e891-4978-9c73-a74cb1762849", + "name": "Spawner (106)", + "location": [5185, 1006, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [{ "name": "AncientWyrm", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "58412982-34fe-41e4-9c85-cd0d5691d62c", + "name": "Spawner (106)", + "location": [5143, 986, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "db505150-0a76-44ac-8b27-841ef6b87321", "name": "Spawner (106)", "location": [5183, 1007, 0], @@ -427,55 +461,5 @@ { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } ] - }, - { - "$type": "Spawner", - "guid": "def6615c-5bbd-4b7c-ab81-4dc4327c97d8", - "name": "Spawner (106)", - "location": [5205, 778, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fb1e3f19-4276-4b1f-b87f-0b4e38ebd70b", - "name": "Spawner (106)", - "location": [5141, 968, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fce8877c-f374-4e24-9d3f-65205427600e", - "name": "Spawner (106)", - "location": [5280, 955, -40], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/Fire.json b/Distribution/Data/Spawns/post-uoml/trammel/Fire.json similarity index 78% rename from Distribution/Data/Spawns/shared/trammel/Fire.json rename to Distribution/Data/Spawns/post-uoml/trammel/Fire.json index b2133ff89..43f8113b2 100644 --- a/Distribution/Data/Spawns/shared/trammel/Fire.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Fire.json @@ -1,71 +1,6 @@ [ { - "$type": "Spawner", - "guid": "0077d996-0107-433c-868d-32757f57d4ec", - "name": "Spawner (107)", - "location": [5722, 1390, 1], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "017e2ed5-fb4e-4776-9c76-cff6f836f32d", - "name": "Spawner (107)", - "location": [5787, 1386, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "HellCat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "06eeaf2c-c00f-42fa-8c42-539a81928818", - "name": "Spawner (107)", - "location": [5656, 1412, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 10, - "entries": [ - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "07e0ca08-16a9-4ea3-9349-bc11d1d86995", - "name": "Spawner (107)", - "location": [5717, 1333, 6], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "0eb73fc0-e467-4792-ad60-4d3102e26915", "name": "Spawner (107)", "location": [5693, 1415, 38], @@ -83,60 +18,10 @@ ] }, { - "$type": "Spawner", - "guid": "193c201f-41f0-4dad-a2d7-0bcf80e30199", + "type": "Spawner", + "guid": "df760fe6-6df1-45ac-a3c2-0f27637dd457", "name": "Spawner (107)", - "location": [5867, 1309, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "HellCat", "maxCount": 1, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "19c7452e-c000-43d9-97f9-7e969d89bda8", - "name": "Spawner (107)", - "location": [5638, 1439, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 40, - "entries": [ - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f8ce435-dfce-4ebc-ac13-34516a8067a7", - "name": "Spawner (107)", - "location": [5731, 1379, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 7, - "entries": [ - { "name": "Lich", "maxCount": 3, "probability": 100 }, - { "name": "LichLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20407dd7-05e5-4af7-8771-bcf92a833e0d", - "name": "Spawner (107)", - "location": [5859, 1341, -1], + "location": [5695, 1434, -1], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -144,275 +29,10 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Slime", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "263d2d5e-da10-4537-ac29-3010728ca3b4", - "name": "Spawner (107)", - "location": [5699, 1302, 1], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2a2291bc-7a04-4d90-be90-c9541aec5498", - "name": "Spawner (107)", - "location": [5678, 1340, -2], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "HellHound", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a2c62cd-5e59-4da4-a2ce-9a0c5c93febd", - "name": "Spawner (107)", - "location": [5643, 1403, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3c4823a0-17b4-43bc-8033-ee7cde4874fd", - "name": "Spawner (107)", - "location": [5654, 1380, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3dd210f5-6903-45b5-874a-1213f20378aa", - "name": "Spawner (107)", - "location": [5667, 1315, 5], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 5647, "y": 1295, "z": -128 }, - "end": { "x": 5687, "y": 1335, "z": 20 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Efreet", "maxCount": 2, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3f1fa8e4-0461-43c5-915d-1f8d6243c984", - "name": "Spawner (107)", - "location": [5651, 1443, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 5, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40fc9fd2-ec56-4328-ba07-79287c997be5", - "name": "Spawner (107)", - "location": [5714, 1465, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "456851f3-5839-4c42-b5e2-5bebcb146b2b", - "name": "Spawner (107)", - "location": [5643, 1435, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b7d444d-5a83-4a4a-9f9b-f268080c00c6", - "name": "Spawner (107)", - "location": [5647, 1439, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 15, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "56b8cca4-7325-4d9f-ae81-dfbe47bf02b5", - "name": "Spawner (107)", - "location": [5852, 1468, 1], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 30, - "entries": [ - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "580578d9-a4f1-4054-8d68-ca645c60d4c4", - "name": "Spawner (107)", - "location": [5650, 1419, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 5, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "63aa9c1a-6933-4ee4-bbf5-fbaed82df249", - "name": "Spawner (107)", - "location": [5689, 1387, 1], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "641ecc65-2a7a-4d79-8d6d-09064de015b9", - "name": "Spawner (107)", - "location": [5691, 1347, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6ac34bad-d1e3-4d92-b379-d07ad3e49439", - "name": "Spawner (107)", - "location": [5818, 1379, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6e37e3a4-0e7e-474f-8f47-762a96993d74", - "name": "Spawner (107)", - "location": [5679, 1438, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72406438-fb9d-4ffb-8daa-a290a93efba5", - "name": "Spawner (107)", - "location": [5835, 1304, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "FireElemental", "maxCount": 4, "probability": 100 }, - { "name": "Efreet", "maxCount": 1, "probability": 100 }, - { "name": "LavaSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "7e6d1ab1-e613-416e-98c1-943cfdc638ea", "name": "Spawner (107)", "location": [5680, 1438, 0], @@ -423,31 +43,31 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "EvilMageLord", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "EvilMageLord", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "82c4dd19-052a-4f08-a8d7-540d994eee6e", + "type": "Spawner", + "guid": "c474fe51-44fd-4dc4-8a50-2a35fb00bfe8", "name": "Spawner (107)", - "location": [5723, 1291, 0], + "location": [5733, 1433, 22], "map": "Trammel", - "count": 2, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 3, - "walkingRange": 3, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "FireElemental", "maxCount": 2, "probability": 100 } + { "name": "GiantRat", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, + { "name": "EvilMage", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "835ef9b2-6755-46e7-bf92-e2004cd6b3f9", + "type": "Spawner", + "guid": "d402bb06-3c2f-4151-bd55-a97f1459aa8c", "name": "Spawner (107)", - "location": [5809, 1332, 0], + "location": [5787, 1427, 17], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -455,44 +75,10 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "LavaLizard", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "849224e3-2e29-427b-80eb-0a151e93e199", - "name": "Spawner (107)", - "location": [5651, 1408, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 3, - "entries": [ - { "name": "HellHound", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "85f5476d-1709-4dc9-b429-dadbc4f40506", - "name": "Spawner (107)", - "location": [5715, 1465, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "9253307c-4dbe-44bb-9e7f-7177e491daf7", "name": "Spawner (107)", "location": [5717, 1465, -2], @@ -503,31 +89,10 @@ "team": 0, "homeRange": 10, "walkingRange": 15, - "entries": [ - { "name": "FireElemental", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "FireElemental", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "93d283d5-a55c-47a8-b022-cb2807cd6864", - "name": "Spawner (107)", - "location": [5790, 1451, 7], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 15, - "entries": [ - { "name": "Efreet", "maxCount": 1, "probability": 100 }, - { "name": "LavaSerpent", "maxCount": 1, "probability": 100 }, - { "name": "HellHound", "maxCount": 1, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a3a036ef-e0c6-4c5c-9573-2117548224e9", "name": "Spawner (107)", "location": [5744, 1471, 2], @@ -546,144 +111,26 @@ ] }, { - "$type": "Spawner", - "guid": "a5f9ec55-e754-40f7-a83c-dfd7b78efc93", + "type": "Spawner", + "guid": "93d283d5-a55c-47a8-b022-cb2807cd6864", "name": "Spawner (107)", - "location": [5651, 1435, 0], + "location": [5790, 1451, 7], "map": "Trammel", - "count": 1, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 3, - "walkingRange": 5, + "homeRange": 5, + "walkingRange": 15, "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } + { "name": "Efreet", "maxCount": 1, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ab04e5e0-3592-4288-b924-ac2f2b78aac7", - "name": "Spawner (107)", - "location": [5650, 1434, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c15a4f6b-9710-4def-b8d0-09c117d33575", - "name": "Spawner (107)", - "location": [5651, 1418, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 3, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c474fe51-44fd-4dc4-8a50-2a35fb00bfe8", - "name": "Spawner (107)", - "location": [5733, 1433, 22], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "GiantRat", "maxCount": 1, "probability": 100 }, - { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c4aa67ce-4174-42c6-a18c-54d72771a68e", - "name": "Spawner (107)", - "location": [5643, 1435, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 5, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c6978e2b-eb18-46b6-8809-1dba551a5efc", - "name": "Spawner (107)", - "location": [5651, 1363, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 5648, "y": 1360, "z": 0 }, - "end": { "x": 5654, "y": 1366, "z": 15 } - }, - "walkingRange": 3, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 }, - { "name": "HellHound", "maxCount": 5, "probability": 100 }, - { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c7d55c1f-b262-4f11-a264-c731516e29a4", - "name": "Spawner (107)", - "location": [5859, 1387, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "LavaLizard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c8ed6c9a-fa12-4cd8-8a14-e3eb1f07154a", - "name": "Spawner (107)", - "location": [5836, 1364, 1], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "cf42e1f4-a588-42f6-a9ec-f25f181ca6ae", "name": "Spawner (107)", "location": [5852, 1468, 1], @@ -705,10 +152,24 @@ ] }, { - "$type": "Spawner", - "guid": "d402bb06-3c2f-4151-bd55-a97f1459aa8c", + "type": "Spawner", + "guid": "56b8cca4-7325-4d9f-ae81-dfbe47bf02b5", "name": "Spawner (107)", - "location": [5787, 1427, 17], + "location": [5852, 1468, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c7d55c1f-b262-4f11-a264-c731516e29a4", + "name": "Spawner (107)", + "location": [5859, 1387, 0], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -716,12 +177,143 @@ "team": 0, "homeRange": 10, "walkingRange": 20, + "entries": [{ "name": "LavaLizard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "017e2ed5-fb4e-4776-9c76-cff6f836f32d", + "name": "Spawner (107)", + "location": [5787, 1386, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HellCat", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6ac34bad-d1e3-4d92-b379-d07ad3e49439", + "name": "Spawner (107)", + "location": [5818, 1379, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c8ed6c9a-fa12-4cd8-8a14-e3eb1f07154a", + "name": "Spawner (107)", + "location": [5836, 1364, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "835ef9b2-6755-46e7-bf92-e2004cd6b3f9", + "name": "Spawner (107)", + "location": [5809, 1332, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "20407dd7-05e5-4af7-8771-bcf92a833e0d", + "name": "Spawner (107)", + "location": [5859, 1341, -1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72406438-fb9d-4ffb-8daa-a290a93efba5", + "name": "Spawner (107)", + "location": [5835, 1304, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "LavaLizard", "maxCount": 1, "probability": 100 } + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "Efreet", "maxCount": 1, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "193c201f-41f0-4dad-a2d7-0bcf80e30199", + "name": "Spawner (107)", + "location": [5867, 1309, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "263d2d5e-da10-4537-ac29-3010728ca3b4", + "name": "Spawner (107)", + "location": [5699, 1302, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "82c4dd19-052a-4f08-a8d7-540d994eee6e", + "name": "Spawner (107)", + "location": [5723, 1291, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "df5837df-c256-4851-9863-5c9338442d8e", "name": "Spawner (107)", "location": [5732, 1297, -1], @@ -732,44 +324,27 @@ "team": 0, "homeRange": 5, "walkingRange": 20, - "entries": [ - { "name": "HellHound", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "HellHound", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "df760fe6-6df1-45ac-a3c2-0f27637dd457", + "type": "Spawner", + "guid": "3dd210f5-6903-45b5-874a-1213f20378aa", "name": "Spawner (107)", - "location": [5695, 1434, -1], + "location": [5667, 1315, 5], "map": "Trammel", - "count": 1, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, + "homeRange": 20, "walkingRange": 20, "entries": [ - { "name": "Slime", "maxCount": 1, "probability": 100 } + { "name": "Efreet", "maxCount": 2, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e56c3640-05b4-407f-b362-11a83dfc9670", - "name": "Spawner (107)", - "location": [5651, 1402, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 3, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "eb42506a-19bf-470c-a4b6-4e96d6ede07f", "name": "Spawner (107)", "location": [5747, 1320, 0], @@ -786,7 +361,41 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "07e0ca08-16a9-4ea3-9349-bc11d1d86995", + "name": "Spawner (107)", + "location": [5717, 1333, 6], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f8ce435-dfce-4ebc-ac13-34516a8067a7", + "name": "Spawner (107)", + "location": [5731, 1379, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [ + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f3c05932-b64d-45a7-aaf0-98bae95935e7", "name": "Spawner (107)", "location": [5709, 1364, -1], @@ -797,28 +406,259 @@ "team": 0, "homeRange": 10, "walkingRange": 20, - "entries": [ - { "name": "HellHound", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "HellHound", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f8a27029-7a4d-4bc6-99c7-1e691e0e6c45", + "type": "Spawner", + "guid": "641ecc65-2a7a-4d79-8d6d-09064de015b9", "name": "Spawner (107)", - "location": [5679, 1439, 0], + "location": [5691, 1347, 0], "map": "Trammel", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, - "walkingRange": 5, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Daemon", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2a2291bc-7a04-4d90-be90-c9541aec5498", + "name": "Spawner (107)", + "location": [5678, 1340, -2], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "c6978e2b-eb18-46b6-8809-1dba551a5efc", + "name": "Spawner (107)", + "location": [5651, 1363, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 5, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c4823a0-17b4-43bc-8033-ee7cde4874fd", + "name": "Spawner (107)", + "location": [5654, 1380, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "63aa9c1a-6933-4ee4-bbf5-fbaed82df249", + "name": "Spawner (107)", + "location": [5689, 1387, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "06eeaf2c-c00f-42fa-8c42-539a81928818", + "name": "Spawner (107)", + "location": [5656, 1412, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 10, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e56c3640-05b4-407f-b362-11a83dfc9670", + "name": "Spawner (107)", + "location": [5651, 1402, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a2c62cd-5e59-4da4-a2ce-9a0c5c93febd", + "name": "Spawner (107)", + "location": [5643, 1403, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "849224e3-2e29-427b-80eb-0a151e93e199", + "name": "Spawner (107)", + "location": [5651, 1408, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [{ "name": "HellHound", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c15a4f6b-9710-4def-b8d0-09c117d33575", + "name": "Spawner (107)", + "location": [5651, 1418, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "580578d9-a4f1-4054-8d68-ca645c60d4c4", + "name": "Spawner (107)", + "location": [5650, 1419, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4b7d444d-5a83-4a4a-9f9b-f268080c00c6", + "name": "Spawner (107)", + "location": [5647, 1439, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 15, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3f1fa8e4-0461-43c5-915d-1f8d6243c984", + "name": "Spawner (107)", + "location": [5651, 1443, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a5f9ec55-e754-40f7-a83c-dfd7b78efc93", + "name": "Spawner (107)", + "location": [5651, 1435, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c4aa67ce-4174-42c6-a18c-54d72771a68e", + "name": "Spawner (107)", + "location": [5643, 1435, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19c7452e-c000-43d9-97f9-7e969d89bda8", + "name": "Spawner (107)", + "location": [5638, 1439, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 40, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40fc9fd2-ec56-4328-ba07-79287c997be5", + "name": "Spawner (107)", + "location": [5714, 1465, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "fc9a02b7-3d5f-4609-b586-907c4ca497ee", "name": "Spawner (107)", "location": [5723, 1390, 2], @@ -829,8 +669,90 @@ "team": 0, "homeRange": 7, "walkingRange": 7, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6e37e3a4-0e7e-474f-8f47-762a96993d74", + "name": "Spawner (107)", + "location": [5679, 1438, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "85f5476d-1709-4dc9-b429-dadbc4f40506", + "name": "Spawner (107)", + "location": [5715, 1465, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0077d996-0107-433c-868d-32757f57d4ec", + "name": "Spawner (107)", + "location": [5722, 1390, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f8a27029-7a4d-4bc6-99c7-1e691e0e6c45", + "name": "Spawner (107)", + "location": [5679, 1439, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab04e5e0-3592-4288-b924-ac2f2b78aac7", + "name": "Spawner (107)", + "location": [5650, 1434, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "456851f3-5839-4c42-b5e2-5bebcb146b2b", + "name": "Spawner (107)", + "location": [5643, 1435, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/Graveyards.json b/Distribution/Data/Spawns/post-uoml/trammel/Graveyards.json similarity index 82% rename from Distribution/Data/Spawns/shared/trammel/Graveyards.json rename to Distribution/Data/Spawns/post-uoml/trammel/Graveyards.json index 1ef15a683..986322790 100644 --- a/Distribution/Data/Spawns/shared/trammel/Graveyards.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Graveyards.json @@ -1,45 +1,6 @@ [ { - "$type": "Spawner", - "guid": "003c2e17-0f7e-4af8-9b25-de6ccc4c6e24", - "name": "Spawner (108)", - "location": [4545, 1317, 8], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 4535, "y": 1307, "z": -128 }, - "end": { "x": 4555, "y": 1327, "z": 25 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Spectre", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "18ac21c4-da65-4449-997e-6e3952e53d46", - "name": "Spawner (108)", - "location": [722, 1119, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "1b1194dd-0699-4c9e-9cbe-1332a477a90d", "name": "Spawner (108)", "location": [1369, 1475, 10], @@ -58,29 +19,27 @@ ] }, { - "$type": "Spawner", - "guid": "69dd4fe9-7f47-466e-b290-963f9bf240d8", + "type": "Spawner", + "guid": "f40bd4f4-6ff1-40ba-84c3-296ade50ab51", "name": "Spawner (108)", - "location": [2758, 867, 0], + "location": [1285, 3731, 0], "map": "Trammel", "count": 9, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 2738, "y": 847, "z": -128 }, - "end": { "x": 2778, "y": 887, "z": 15 } - }, - "walkingRange": 20, + "homeRange": 10, + "walkingRange": 15, "entries": [ { "name": "Spectre", "maxCount": 2, "probability": 100 }, { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "Shade", "maxCount": 2, "probability": 100 }, { "name": "Skeleton", "maxCount": 3, "probability": 100 }, { "name": "Zombie", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "eb0eb81f-7aea-4162-bf8d-1a0e614ecbcd", "name": "Spawner (108)", "location": [2438, 1100, 8], @@ -89,10 +48,7 @@ "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 2428, "y": 1090, "z": -128 }, - "end": { "x": 2448, "y": 1110, "z": 30 } - }, + "homeRange": 10, "walkingRange": 20, "entries": [ { "name": "Spectre", "maxCount": 1, "probability": 100 }, @@ -104,30 +60,60 @@ ] }, { - "$type": "Spawner", - "guid": "f40bd4f4-6ff1-40ba-84c3-296ade50ab51", + "type": "Spawner", + "guid": "003c2e17-0f7e-4af8-9b25-de6ccc4c6e24", "name": "Spawner (108)", - "location": [1285, 3731, 0], + "location": [4545, 1317, 8], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69dd4fe9-7f47-466e-b290-963f9bf240d8", + "name": "Spawner (108)", + "location": [2758, 867, 0], "map": "Trammel", "count": 9, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 1275, "y": 3721, "z": -128 }, - "end": { "x": 1295, "y": 3741, "z": 15 } - }, - "walkingRange": 15, + "homeRange": 20, + "walkingRange": 20, "entries": [ { "name": "Spectre", "maxCount": 2, "probability": 100 }, { "name": "Wraith", "maxCount": 2, "probability": 100 }, - { "name": "Shade", "maxCount": 2, "probability": 100 }, { "name": "Skeleton", "maxCount": 3, "probability": 100 }, { "name": "Zombie", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "18ac21c4-da65-4449-997e-6e3952e53d46", + "name": "Spawner (108)", + "location": [722, 1119, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "fd6f9733-1638-485e-a3fd-3d6f9550591e", "name": "Spawner (108)", "location": [3407, 2652, 48], diff --git a/Distribution/Data/Spawns/shared/trammel/Hythloth.json b/Distribution/Data/Spawns/post-uoml/trammel/Hythloth.json similarity index 78% rename from Distribution/Data/Spawns/shared/trammel/Hythloth.json rename to Distribution/Data/Spawns/post-uoml/trammel/Hythloth.json index 2a68d5114..35f111e95 100644 --- a/Distribution/Data/Spawns/shared/trammel/Hythloth.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Hythloth.json @@ -1,187 +1,6 @@ [ { - "$type": "Spawner", - "guid": "04d3a931-3af8-4863-8989-58f6d1cae959", - "name": "Spawner (109)", - "location": [5966, 70, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0d2d46b6-5b3a-4668-b064-34d8035b7854", - "name": "Spawner (109)", - "location": [5962, 217, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "132536ba-9f7b-47a7-bf31-be4487936235", - "name": "Spawner (109)", - "location": [6033, 199, 22], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "159531c4-89e0-48c6-ac04-b4d67b078cae", - "name": "Spawner (109)", - "location": [5945, 78, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "176b940f-621c-490e-baee-3c2816004e14", - "name": "Spawner (109)", - "location": [5954, 225, 22], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "177607bb-ea2f-484c-8e06-262edf56f6d6", - "name": "Spawner (109)", - "location": [6108, 36, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "185cd08c-716f-4255-a8aa-b9e2dbe06e44", - "name": "Spawner (109)", - "location": [6124, 159, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1deb5cae-7cab-4b3b-91ef-a933a9836369", - "name": "Spawner (109)", - "location": [6033, 205, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2179be82-2867-4f8e-b797-4102cc967c4c", - "name": "Spawner (109)", - "location": [5915, 91, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23a59d14-b35b-4776-9e39-c2b32a16262d", - "name": "Spawner (109)", - "location": [6108, 66, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23ec32d7-3f5f-4d4c-b89c-2449ba45aa6e", - "name": "Spawner (109)", - "location": [6052, 156, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "276dda12-6154-456f-9cc9-ac111160fcb0", "name": "Spawner (109)", "location": [5923, 48, 22], @@ -198,10 +17,174 @@ ] }, { - "$type": "Spawner", - "guid": "2f802063-b694-457e-8662-8c56600b28d1", + "type": "Spawner", + "guid": "bf3d2161-79d8-402f-9e07-26ebcab8fdc2", "name": "Spawner (109)", - "location": [6058, 153, 0], + "location": [5911, 55, 22], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel1", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0b1509d-4dd2-4b7a-bd21-84c8c93347aa", + "name": "Spawner (109)", + "location": [5948, 49, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8afc042c-474c-4b81-bcc8-c05eb2d8f24e", + "name": "Spawner (109)", + "location": [5954, 26, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ff350d79-2434-441a-85e9-254afcdafe5a", + "name": "Spawner (109)", + "location": [5977, 49, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8db6fca-406c-4fcd-951d-5108bb4ed3d2", + "name": "Spawner (109)", + "location": [5995, 62, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireGargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92736344-898e-4a70-aa23-229c2b92024e", + "name": "Spawner (109)", + "location": [5970, 80, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94814adf-f077-409f-bd7d-84ca6a68e389", + "name": "Spawner (109)", + "location": [5994, 96, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "772b0b8b-373b-4859-aeb9-46d2958b0cd8", + "name": "Spawner (109)", + "location": [5946, 75, 22], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4244ac16-86b4-4f39-aa2a-7474103986f5", + "name": "Spawner (109)", + "location": [5940, 107, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2179be82-2867-4f8e-b797-4102cc967c4c", + "name": "Spawner (109)", + "location": [5915, 91, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d182489e-af6b-449d-a4d9-fd345ee98ce1", + "name": "Spawner (109)", + "location": [5990, 36, 22], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -209,12 +192,316 @@ "team": 0, "homeRange": 1, "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93525eb9-32ec-49c8-8565-e9a1728d098b", + "name": "Spawner (109)", + "location": [5985, 36, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a6d37bb4-cfe5-4a9c-a35b-c8d8a11b8ab7", + "name": "Spawner (109)", + "location": [5989, 25, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9e850a41-0f8a-4411-94e0-7c6df352f6fc", + "name": "Spawner (109)", + "location": [5983, 25, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7e0d9ef3-4a24-44f4-bf1f-cc2c98ef3fa6", + "name": "Spawner (109)", + "location": [5979, 26, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3f509b00-f24b-4d5a-b609-ec88fe0e025d", + "name": "Spawner (109)", + "location": [5981, 84, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04d3a931-3af8-4863-8989-58f6d1cae959", + "name": "Spawner (109)", + "location": [5966, 70, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "969dad27-c44e-43c2-8456-a94c292253e1", + "name": "Spawner (109)", + "location": [5958, 65, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d2957c0-20a9-43d6-8bc9-f1c46585f525", + "name": "Spawner (109)", + "location": [5937, 65, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5b6e1b73-46ac-46b3-b416-c1a4b5e147f1", + "name": "Spawner (109)", + "location": [5921, 81, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ad9f1c34-b053-4f75-bcca-67b8d0749726", + "name": "Spawner (109)", + "location": [5972, 86, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5a2b323b-7048-480c-bd22-b8c557dd7571", + "name": "Spawner (109)", + "location": [5917, 93, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "545e00c0-3dfb-485b-9f32-dc003ad34c1c", + "name": "Spawner (109)", + "location": [5929, 77, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "159531c4-89e0-48c6-ac04-b4d67b078cae", + "name": "Spawner (109)", + "location": [5945, 78, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6598121a-66ed-4839-b985-cca9e8cb789f", + "name": "Spawner (109)", + "location": [5967, 80, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "798fd763-c76c-4656-86c3-988524cd12b6", + "name": "Spawner (109)", + "location": [5961, 169, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f90467c3-88f7-43eb-81c2-fa882c5b62a9", + "name": "Spawner (109)", + "location": [5992, 146, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Daemon", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fdf36d81-05fc-4c57-8b01-09f8de27484f", + "name": "Spawner (109)", + "location": [5945, 175, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bd13d82-5c43-47a4-9610-882a8dbb5800", + "name": "Spawner (109)", + "location": [5956, 145, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f80c3502-c704-4632-ae0d-322f3f7ff388", + "name": "Spawner (109)", + "location": [5917, 156, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d56d6379-f8d7-4adc-bd09-0bdb75059548", + "name": "Spawner (109)", + "location": [5914, 195, 22], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b63bde3-f24f-407e-9d11-0eb2c04305b3", + "name": "Spawner (109)", + "location": [5918, 223, 44], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "350f7c02-4640-4932-89b6-2f4c4c4bc1b2", "name": "Spawner (109)", "location": [5918, 235, 44], @@ -231,124 +518,44 @@ ] }, { - "$type": "Spawner", - "guid": "35fc4240-bcd9-4335-b930-b2140d486cc2", + "type": "Spawner", + "guid": "176b940f-621c-490e-baee-3c2816004e14", "name": "Spawner (109)", - "location": [5978, 185, 44], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "36a58f71-fb57-48db-b111-d17ec727d1aa", - "name": "Spawner (109)", - "location": [6081, 44, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3da1531b-38a3-42f5-a69e-1f7a1aa2cb89", - "name": "Spawner (109)", - "location": [6097, 36, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e2dd9b5-5fc4-4308-9ae0-a8817d4fac63", - "name": "Spawner (109)", - "location": [6125, 221, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3f509b00-f24b-4d5a-b609-ec88fe0e025d", - "name": "Spawner (109)", - "location": [5981, 84, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4244ac16-86b4-4f39-aa2a-7474103986f5", - "name": "Spawner (109)", - "location": [5940, 107, 22], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "443e9b08-9415-41e4-8e4e-07c813d1f687", - "name": "Spawner (109)", - "location": [6086, 178, 0], + "location": [5954, 225, 22], "map": "Trammel", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, + "homeRange": 12, "walkingRange": 20, "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, - { "name": "Gazer", "maxCount": 2, "probability": 100 } + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "4653daba-0405-4fb3-b89c-b712cf560782", + "type": "Spawner", + "guid": "c84dd874-d913-4a50-b681-ebe45af83d6a", "name": "Spawner (109)", - "location": [6041, 230, 44], + "location": [5986, 203, 44], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb68f3ce-29a6-47ea-ad5d-c74a7bfe7ad0", + "name": "Spawner (109)", + "location": [5983, 169, 0], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -356,28 +563,24 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "4b63bde3-f24f-407e-9d11-0eb2c04305b3", + "type": "Spawner", + "guid": "f2fcd059-3b7c-4855-a406-c419a3b46036", "name": "Spawner (109)", - "location": [5918, 223, 44], + "location": [5992, 147, 0], "map": "Trammel", - "count": 4, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 } - ] + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "52f85430-2e62-4e62-b548-46c1a184cf9e", "name": "Spawner (109)", "location": [5970, 169, 0], @@ -388,15 +591,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "545e00c0-3dfb-485b-9f32-dc003ad34c1c", + "type": "Spawner", + "guid": "72de652f-c027-4100-ab8b-43e229bc527b", "name": "Spawner (109)", - "location": [5929, 77, 0], + "location": [5997, 145, 0], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -404,15 +605,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "5a2b323b-7048-480c-bd22-b8c557dd7571", + "type": "Spawner", + "guid": "35fc4240-bcd9-4335-b930-b2140d486cc2", "name": "Spawner (109)", - "location": [5917, 93, 0], + "location": [5978, 185, 44], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -420,12 +619,52 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "0d2d46b6-5b3a-4668-b064-34d8035b7854", + "name": "Spawner (109)", + "location": [5962, 217, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "760cb112-5308-4e18-9fd5-2e7dabc74fb5", + "name": "Spawner (109)", + "location": [5989, 186, 44], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "88c01a9f-467c-4183-9e4a-ef7c47b8e5ce", + "name": "Spawner (109)", + "location": [5926, 230, 44], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "5a73a11a-55be-42d7-b6d4-9db6b6e0762f", "name": "Spawner (109)", "location": [6094, 154, -22], @@ -443,709 +682,7 @@ ] }, { - "$type": "Spawner", - "guid": "5b6e1b73-46ac-46b3-b416-c1a4b5e147f1", - "name": "Spawner (109)", - "location": [5921, 81, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d613a12-4519-421f-afd7-dfe2d8dfc2ed", - "name": "Spawner (109)", - "location": [6099, 51, 22], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6524efb1-e7c2-47e4-91ad-393224529ad8", - "name": "Spawner (109)", - "location": [6060, 158, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6598121a-66ed-4839-b985-cca9e8cb789f", - "name": "Spawner (109)", - "location": [5967, 80, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 6, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6638a784-d1d4-4ceb-b913-0d19c3876bd1", - "name": "Spawner (109)", - "location": [6046, 199, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72de652f-c027-4100-ab8b-43e229bc527b", - "name": "Spawner (109)", - "location": [5997, 145, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75f93255-3c6f-47dc-b862-3d4a61bf454a", - "name": "Spawner (109)", - "location": [6064, 96, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "760cb112-5308-4e18-9fd5-2e7dabc74fb5", - "name": "Spawner (109)", - "location": [5989, 186, 44], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76399213-8099-47d2-b5e5-6f12d7e3ffde", - "name": "Spawner (109)", - "location": [6106, 67, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "772b0b8b-373b-4859-aeb9-46d2958b0cd8", - "name": "Spawner (109)", - "location": [5946, 75, 22], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "798fd763-c76c-4656-86c3-988524cd12b6", - "name": "Spawner (109)", - "location": [5961, 169, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7bd13d82-5c43-47a4-9610-882a8dbb5800", - "name": "Spawner (109)", - "location": [5956, 145, 22], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d5def22-5137-48d5-ba95-54fb10dae441", - "name": "Spawner (109)", - "location": [6113, 92, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7e0d9ef3-4a24-44f4-bf1f-cc2c98ef3fa6", - "name": "Spawner (109)", - "location": [5979, 26, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8254a266-263f-4f5f-b8ac-7646b42b484a", - "name": "Spawner (109)", - "location": [6054, 45, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "Imp", "maxCount": 1, "probability": 100 }, - { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84e31077-87fd-482c-ad78-6c7a8857b572", - "name": "Spawner (109)", - "location": [6116, 86, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "FireGargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "88c01a9f-467c-4183-9e4a-ef7c47b8e5ce", - "name": "Spawner (109)", - "location": [5926, 230, 44], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8afc042c-474c-4b81-bcc8-c05eb2d8f24e", - "name": "Spawner (109)", - "location": [5954, 26, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d2957c0-20a9-43d6-8bc9-f1c46585f525", - "name": "Spawner (109)", - "location": [5937, 65, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8eeb526f-8a8e-40ab-9f37-79d211549bef", - "name": "Spawner (109)", - "location": [6086, 45, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "92736344-898e-4a70-aa23-229c2b92024e", - "name": "Spawner (109)", - "location": [5970, 80, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9339a8f1-6dd9-444e-a105-3fda18569885", - "name": "Spawner (109)", - "location": [6041, 222, 44], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93525eb9-32ec-49c8-8565-e9a1728d098b", - "name": "Spawner (109)", - "location": [5985, 36, 22], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94814adf-f077-409f-bd7d-84ca6a68e389", - "name": "Spawner (109)", - "location": [5994, 96, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "969dad27-c44e-43c2-8456-a94c292253e1", - "name": "Spawner (109)", - "location": [5958, 65, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9e850a41-0f8a-4411-94e0-7c6df352f6fc", - "name": "Spawner (109)", - "location": [5983, 25, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a6d37bb4-cfe5-4a9c-a35b-c8d8a11b8ab7", - "name": "Spawner (109)", - "location": [5989, 25, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8ce27ea-3cc0-49b9-afd4-6f404432bec5", - "name": "Spawner (109)", - "location": [6117, 81, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ab1c29a8-d843-4ba3-84b9-b5ef0ef2f524", - "name": "Spawner (109)", - "location": [6064, 96, 22], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 1, "probability": 100 }, - { "name": "Imp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad9f1c34-b053-4f75-bcca-67b8d0749726", - "name": "Spawner (109)", - "location": [5972, 86, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b0f29c64-4887-41c8-ab48-463a37a2f599", - "name": "Spawner (109)", - "location": [6081, 83, 21], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3501f38-bdac-418b-969f-4e0e9f7d9c42", - "name": "Spawner (109)", - "location": [6121, 166, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b538b4b4-eba9-4854-9c66-411d622b3ed8", - "name": "Spawner (109)", - "location": [6107, 70, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b8db6fca-406c-4fcd-951d-5108bb4ed3d2", - "name": "Spawner (109)", - "location": [5995, 62, 22], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "FireGargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bdf775bc-e899-44df-b7aa-36759f438e22", - "name": "Spawner (109)", - "location": [6058, 92, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf3d2161-79d8-402f-9e07-26ebcab8fdc2", - "name": "Spawner (109)", - "location": [5911, 55, 22], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "TreasureChestLevel1", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0b1509d-4dd2-4b7a-bd21-84c8c93347aa", - "name": "Spawner (109)", - "location": [5948, 49, 22], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 1, "probability": 100 }, - { "name": "HellHound", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c84dd874-d913-4a50-b681-ebe45af83d6a", - "name": "Spawner (109)", - "location": [5986, 203, 44], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "Gazer", "maxCount": 3, "probability": 100 }, - { "name": "GazerLarva", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cb68f3ce-29a6-47ea-ad5d-c74a7bfe7ad0", - "name": "Spawner (109)", - "location": [5983, 169, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d182489e-af6b-449d-a4d9-fd345ee98ce1", - "name": "Spawner (109)", - "location": [5990, 36, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3e6e31a-5547-47b2-afaa-f7f64512bdb7", - "name": "Spawner (109)", - "location": [6107, 37, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d56d6379-f8d7-4adc-bd09-0bdb75059548", - "name": "Spawner (109)", - "location": [5914, 195, 22], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d5b56fe5-edf3-4e8a-933c-692ff3bc752c", - "name": "Spawner (109)", - "location": [6120, 229, 22], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "df4026a4-516d-4f35-bbdd-10fb253627c1", "name": "Spawner (109)", "location": [6122, 170, 0], @@ -1163,103 +700,25 @@ ] }, { - "$type": "Spawner", - "guid": "e141c079-d2a8-43ad-a226-1cb79f87cc7d", + "type": "Spawner", + "guid": "443e9b08-9415-41e4-8e4e-07c813d1f687", "name": "Spawner (109)", - "location": [6092, 187, 0], + "location": [6086, 178, 0], "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e59bd560-153a-46a4-b083-8d4278b0ef2c", - "name": "Spawner (109)", - "location": [6087, 186, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e94b0430-2565-4cdd-a7df-f87d3cb61887", - "name": "Spawner (109)", - "location": [6084, 66, 0], - "map": "Trammel", - "count": 2, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 20, "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f2fcd059-3b7c-4855-a406-c419a3b46036", - "name": "Spawner (109)", - "location": [5992, 147, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f80c3502-c704-4632-ae0d-322f3f7ff388", - "name": "Spawner (109)", - "location": [5917, 156, 22], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f90467c3-88f7-43eb-81c2-fa882c5b62a9", - "name": "Spawner (109)", - "location": [5992, 146, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Daemon", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f9a143ac-b466-4e8f-a283-146f9cba767f", "name": "Spawner (109)", "location": [6119, 219, 22], @@ -1277,7 +736,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fab57726-33f0-45bb-9aa3-28650b92d61b", "name": "Spawner (109)", "location": [6058, 193, 22], @@ -1288,15 +747,216 @@ "team": 0, "homeRange": 10, "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "132536ba-9f7b-47a7-bf31-be4487936235", + "name": "Spawner (109)", + "location": [6033, 199, 22], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fdf36d81-05fc-4c57-8b01-09f8de27484f", + "type": "Spawner", + "guid": "23ec32d7-3f5f-4d4c-b89c-2449ba45aa6e", "name": "Spawner (109)", - "location": [5945, 175, 0], + "location": [6052, 156, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3501f38-bdac-418b-969f-4e0e9f7d9c42", + "name": "Spawner (109)", + "location": [6121, 166, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "185cd08c-716f-4255-a8aa-b9e2dbe06e44", + "name": "Spawner (109)", + "location": [6124, 159, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e59bd560-153a-46a4-b083-8d4278b0ef2c", + "name": "Spawner (109)", + "location": [6087, 186, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6524efb1-e7c2-47e4-91ad-393224529ad8", + "name": "Spawner (109)", + "location": [6060, 158, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6638a784-d1d4-4ceb-b913-0d19c3876bd1", + "name": "Spawner (109)", + "location": [6046, 199, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9339a8f1-6dd9-444e-a105-3fda18569885", + "name": "Spawner (109)", + "location": [6041, 222, 44], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3e2dd9b5-5fc4-4308-9ae0-a8817d4fac63", + "name": "Spawner (109)", + "location": [6125, 221, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d5b56fe5-edf3-4e8a-933c-692ff3bc752c", + "name": "Spawner (109)", + "location": [6120, 229, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e141c079-d2a8-43ad-a226-1cb79f87cc7d", + "name": "Spawner (109)", + "location": [6092, 187, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4653daba-0405-4fb3-b89c-b712cf560782", + "name": "Spawner (109)", + "location": [6041, 230, 44], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1deb5cae-7cab-4b3b-91ef-a933a9836369", + "name": "Spawner (109)", + "location": [6033, 205, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2f802063-b694-457e-8662-8c56600b28d1", + "name": "Spawner (109)", + "location": [6058, 153, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab1c29a8-d843-4ba3-84b9-b5ef0ef2f524", + "name": "Spawner (109)", + "location": [6064, 96, 22], "map": "Trammel", "count": 4, "minDelay": "00:05:00", @@ -1305,15 +965,16 @@ "homeRange": 10, "walkingRange": 20, "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 } + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ff350d79-2434-441a-85e9-254afcdafe5a", + "type": "Spawner", + "guid": "b0f29c64-4887-41c8-ab48-463a37a2f599", "name": "Spawner (109)", - "location": [5977, 49, 22], + "location": [6081, 83, 21], "map": "Trammel", "count": 2, "minDelay": "00:05:00", @@ -1321,9 +982,234 @@ "team": 0, "homeRange": 10, "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e94b0430-2565-4cdd-a7df-f87d3cb61887", + "name": "Spawner (109)", + "location": [6084, 66, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76399213-8099-47d2-b5e5-6f12d7e3ffde", + "name": "Spawner (109)", + "location": [6106, 67, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84e31077-87fd-482c-ad78-6c7a8857b572", + "name": "Spawner (109)", + "location": [6116, 86, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireGargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5d613a12-4519-421f-afd7-dfe2d8dfc2ed", + "name": "Spawner (109)", + "location": [6099, 51, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d3e6e31a-5547-47b2-afaa-f7f64512bdb7", + "name": "Spawner (109)", + "location": [6107, 37, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8254a266-263f-4f5f-b8ac-7646b42b484a", + "name": "Spawner (109)", + "location": [6054, 45, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, { "name": "Imp", "maxCount": 1, "probability": 100 }, - { "name": "HellHound", "maxCount": 1, "probability": 100 } + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "23a59d14-b35b-4776-9e39-c2b32a16262d", + "name": "Spawner (109)", + "location": [6108, 66, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a8ce27ea-3cc0-49b9-afd4-6f404432bec5", + "name": "Spawner (109)", + "location": [6117, 81, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bdf775bc-e899-44df-b7aa-36759f438e22", + "name": "Spawner (109)", + "location": [6058, 92, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d5def22-5137-48d5-ba95-54fb10dae441", + "name": "Spawner (109)", + "location": [6113, 92, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b538b4b4-eba9-4854-9c66-411d622b3ed8", + "name": "Spawner (109)", + "location": [6107, 70, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "36a58f71-fb57-48db-b111-d17ec727d1aa", + "name": "Spawner (109)", + "location": [6081, 44, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3da1531b-38a3-42f5-a69e-1f7a1aa2cb89", + "name": "Spawner (109)", + "location": [6097, 36, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "75f93255-3c6f-47dc-b862-3d4a61bf454a", + "name": "Spawner (109)", + "location": [6064, 96, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8eeb526f-8a8e-40ab-9f37-79d211549bef", + "name": "Spawner (109)", + "location": [6086, 45, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "177607bb-ea2f-484c-8e06-262edf56f6d6", + "name": "Spawner (109)", + "location": [6108, 36, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/Ice.json b/Distribution/Data/Spawns/post-uoml/trammel/Ice.json similarity index 78% rename from Distribution/Data/Spawns/shared/trammel/Ice.json rename to Distribution/Data/Spawns/post-uoml/trammel/Ice.json index e3f4e5a5a..7ea233445 100644 --- a/Distribution/Data/Spawns/shared/trammel/Ice.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Ice.json @@ -1,75 +1,86 @@ [ { - "$type": "Spawner", - "guid": "03886f2b-1e0e-48fd-8b92-35a1a60a95c5", + "type": "Spawner", + "guid": "dee07fb6-4dcc-43d5-bd0a-706c36801e8d", "name": "Spawner (110)", - "location": [5837, 245, -5], + "location": [5771, 224, -2], "map": "Trammel", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "106c22c9-9d01-43a0-8983-bea255146fd5", - "name": "Spawner (110)", - "location": [5833, 242, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "308a4f85-5d3f-4f44-af64-371951608f97", - "name": "Spawner (110)", - "location": [5785, 161, -6], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 18, + "homeRange": 10, "walkingRange": 20, + "entries": [{ "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9cbfc951-e954-4e8d-b1af-cf9457216b53", + "name": "Spawner (110)", + "location": [5799, 188, -6], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, "entries": [ - { "name": "FrostOoze", "maxCount": 1, "probability": 100 }, - { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, - { "name": "IceElemental", "maxCount": 1, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 1, "probability": 100 } + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "3cfd9a1b-000a-430f-b44c-e64720d69237", + "type": "Spawner", + "guid": "a74f4f2c-7906-46c3-bd5f-8ce30e43417c", "name": "Spawner (110)", - "location": [5772, 188, -3], + "location": [5867, 218, -4], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "FrostOoze", "maxCount": 2, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84103371-1387-4ebe-af6a-91faf5d3a5f2", + "name": "Spawner (110)", + "location": [5850, 219, -3], "map": "Trammel", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RatmanMage", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "ada3263b-aece-4dee-a69b-fe3ef96a3550", + "name": "Spawner (110)", + "location": [5814, 237, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "3def478d-d193-49d2-96e0-b36023034db7", "name": "Spawner (110)", "location": [5752, 144, 9], @@ -80,80 +91,10 @@ "team": 0, "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "WhiteWyrm", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "WhiteWyrm", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "3ec99717-4da8-4d7c-9fb2-dd9ba4c845bc", - "name": "Spawner (110)", - "location": [5731, 176, -5], - "map": "Trammel", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 35, - "entries": [ - { "name": "IceSerpent", "maxCount": 9, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 9, "probability": 100 }, - { "name": "IceElemental", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3f420eb7-724d-47e0-89fa-1e844feee72c", - "name": "Spawner (110)", - "location": [5666, 331, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4133a659-4bcb-4b38-a3fe-097866c8aa4e", - "name": "Spawner (110)", - "location": [5680, 306, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "IceFiend", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "44394753-6925-4e7f-95d0-d1998f5bb036", - "name": "Spawner (110)", - "location": [5831, 358, -1], - "map": "Trammel", - "count": 24, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Ratman", "maxCount": 12, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 7, "probability": 100 }, - { "name": "RatmanMage", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "49c552fa-1934-405a-8d30-4fb7db89313f", "name": "Spawner (110)", "location": [5839, 159, 0], @@ -174,203 +115,28 @@ ] }, { - "$type": "Spawner", - "guid": "4df74fc7-0443-4fdc-a6ad-57d9a4d6bc7f", + "type": "Spawner", + "guid": "308a4f85-5d3f-4f44-af64-371951608f97", "name": "Spawner (110)", - "location": [5684, 181, -5], + "location": [5785, 161, -6], "map": "Trammel", - "count": 1, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6502c7e1-5c7a-43b7-a0de-33f73f78e6a3", - "name": "Spawner (110)", - "location": [5838, 355, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "66e9c531-1fee-42e4-910a-cdf71b212b95", - "name": "Spawner (110)", - "location": [5754, 211, -6], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "722d603b-dddb-4b71-88c4-9e965918d6e9", - "name": "Spawner (110)", - "location": [5763, 187, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76706bc3-f4c4-4040-b206-650aa04e041d", - "name": "Spawner (110)", - "location": [5824, 363, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7ebb7a60-b45f-469c-ad0b-86490c855be1", - "name": "Spawner (110)", - "location": [5756, 203, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84103371-1387-4ebe-af6a-91faf5d3a5f2", - "name": "Spawner (110)", - "location": [5850, 219, -3], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, + "homeRange": 18, "walkingRange": 20, "entries": [ - { "name": "RatmanMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8855a9a7-aed4-4c54-94da-a94024783d51", - "name": "Spawner (110)", - "location": [5854, 227, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9456469a-1f9f-43a1-9c7b-639ca4e367f3", - "name": "Spawner (110)", - "location": [5712, 145, -34], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "947f998d-a84b-49c8-8619-852fff3e5295", - "name": "Spawner (110)", - "location": [5669, 330, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "IceFiend", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9cbfc951-e954-4e8d-b1af-cf9457216b53", - "name": "Spawner (110)", - "location": [5799, 188, -6], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "FrostOoze", "maxCount": 1, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "IceElemental", "maxCount": 1, "probability": 100 }, { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 1, "probability": 100 }, - { "name": "FrostSpider", "maxCount": 1, "probability": 100 } + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "9f689286-dcc8-44ac-9d6b-c34c002d5ff5", - "name": "Spawner (110)", - "location": [5682, 314, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a6faf1f3-65fa-47a8-a597-1bd61d7dc350", "name": "Spawner (110)", "location": [5757, 213, -7], @@ -390,25 +156,7 @@ ] }, { - "$type": "Spawner", - "guid": "a74f4f2c-7906-46c3-bd5f-8ce30e43417c", - "name": "Spawner (110)", - "location": [5867, 218, -4], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, - { "name": "FrostOoze", "maxCount": 2, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a9adcc2c-c67d-4f0d-8241-7aa8d73d0fdd", "name": "Spawner (110)", "location": [5681, 191, -6], @@ -428,55 +176,25 @@ ] }, { - "$type": "Spawner", - "guid": "ada3263b-aece-4dee-a69b-fe3ef96a3550", + "type": "Spawner", + "guid": "3ec99717-4da8-4d7c-9fb2-dd9ba4c845bc", "name": "Spawner (110)", - "location": [5814, 237, 0], + "location": [5731, 176, -5], "map": "Trammel", - "count": 1, + "count": 18, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 15, + "homeRange": 25, + "walkingRange": 35, "entries": [ - { "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 } + { "name": "IceSerpent", "maxCount": 9, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 9, "probability": 100 }, + { "name": "IceElemental", "maxCount": 9, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "b4e94abe-5829-4d51-acb5-e112d8b33ce1", - "name": "Spawner (110)", - "location": [5676, 331, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b5f90c32-64bd-4524-bfc6-84716f186595", - "name": "Spawner (110)", - "location": [5683, 197, -4], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "b69a34f7-3ff9-4b19-b38e-817c98a7c93f", "name": "Spawner (110)", "location": [5848, 226, 0], @@ -487,15 +205,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "bc1b0fa0-397d-400e-a8a1-46c6980d3ed7", + "type": "Spawner", + "guid": "b5f90c32-64bd-4524-bfc6-84716f186595", "name": "Spawner (110)", - "location": [5751, 140, 10], + "location": [5683, 197, -4], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -503,60 +219,10 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "c1a3b5ae-9c59-488f-8d7f-24d25e0c7673", - "name": "Spawner (110)", - "location": [5758, 139, 5], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ca52ec08-1f4f-400a-ab1f-9e05892dbdd2", - "name": "Spawner (110)", - "location": [5696, 303, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dee07fb6-4dcc-43d5-bd0a-706c36801e8d", - "name": "Spawner (110)", - "location": [5771, 224, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f7a22de1-cc80-4060-aa02-9728d7e241a0", "name": "Spawner (110)", "location": [5721, 147, -1], @@ -567,8 +233,290 @@ "team": 0, "homeRange": 1, "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c1a3b5ae-9c59-488f-8d7f-24d25e0c7673", + "name": "Spawner (110)", + "location": [5758, 139, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "66e9c531-1fee-42e4-910a-cdf71b212b95", + "name": "Spawner (110)", + "location": [5754, 211, -6], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "106c22c9-9d01-43a0-8983-bea255146fd5", + "name": "Spawner (110)", + "location": [5833, 242, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8855a9a7-aed4-4c54-94da-a94024783d51", + "name": "Spawner (110)", + "location": [5854, 227, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3cfd9a1b-000a-430f-b44c-e64720d69237", + "name": "Spawner (110)", + "location": [5772, 188, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "03886f2b-1e0e-48fd-8b92-35a1a60a95c5", + "name": "Spawner (110)", + "location": [5837, 245, -5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bc1b0fa0-397d-400e-a8a1-46c6980d3ed7", + "name": "Spawner (110)", + "location": [5751, 140, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "722d603b-dddb-4b71-88c4-9e965918d6e9", + "name": "Spawner (110)", + "location": [5763, 187, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7ebb7a60-b45f-469c-ad0b-86490c855be1", + "name": "Spawner (110)", + "location": [5756, 203, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9456469a-1f9f-43a1-9c7b-639ca4e367f3", + "name": "Spawner (110)", + "location": [5712, 145, -34], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4df74fc7-0443-4fdc-a6ad-57d9a4d6bc7f", + "name": "Spawner (110)", + "location": [5684, 181, -5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44394753-6925-4e7f-95d0-d1998f5bb036", + "name": "Spawner (110)", + "location": [5831, 358, -1], + "map": "Trammel", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + { "name": "Ratman", "maxCount": 12, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 7, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 5, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "6502c7e1-5c7a-43b7-a0de-33f73f78e6a3", + "name": "Spawner (110)", + "location": [5838, 355, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76706bc3-f4c4-4040-b206-650aa04e041d", + "name": "Spawner (110)", + "location": [5824, 363, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4133a659-4bcb-4b38-a3fe-097866c8aa4e", + "name": "Spawner (110)", + "location": [5680, 306, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "IceFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "947f998d-a84b-49c8-8619-852fff3e5295", + "name": "Spawner (110)", + "location": [5669, 330, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "IceFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ca52ec08-1f4f-400a-ab1f-9e05892dbdd2", + "name": "Spawner (110)", + "location": [5696, 303, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9f689286-dcc8-44ac-9d6b-c34c002d5ff5", + "name": "Spawner (110)", + "location": [5682, 314, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3f420eb7-724d-47e0-89fa-1e844feee72c", + "name": "Spawner (110)", + "location": [5666, 331, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4e94abe-5829-4d51-acb5-e112d8b33ce1", + "name": "Spawner (110)", + "location": [5676, 331, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/LostLands.json b/Distribution/Data/Spawns/post-uoml/trammel/LostLands.json similarity index 95% rename from Distribution/Data/Spawns/shared/trammel/LostLands.json rename to Distribution/Data/Spawns/post-uoml/trammel/LostLands.json index a65542792..36bad6b8f 100644 --- a/Distribution/Data/Spawns/shared/trammel/LostLands.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/LostLands.json @@ -1,212 +1,232 @@ [ { - "$type": "Spawner", - "guid": "020cf0fc-c7b4-4735-b113-a5f076bfd189", + "type": "Spawner", + "guid": "12cb6fcd-2072-4003-885b-76821f1993cf", "name": "Spawner (111)", - "location": [5251, 3301, -2], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Llama", "maxCount": 2, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "034a2452-0f15-48f2-9bc3-3a1fc3deed2e", - "name": "Spawner (111)", - "location": [5250, 3401, 1], - "map": "Trammel", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 6, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, - { "name": "BlackBear", "maxCount": 6, "probability": 100 }, - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, - { "name": "Rabbit", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Cougar", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "036ad518-7a5a-4805-9d06-6deb52839f4f", - "name": "Spawner (111)", - "location": [5623, 3875, 15], + "location": [5728, 3209, -3], "map": "Trammel", "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] + "homeRange": 100, + "walkingRange": 100, + "entries": [{ "name": "SwampDragon", "maxCount": 7, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "03829553-0468-42d4-b617-02116bca3200", + "type": "Spawner", + "guid": "efc4c627-fca8-4e56-ae35-f66d3552c8bd", "name": "Spawner (111)", - "location": [5918, 3975, 1], + "location": [5457, 2651, 45], "map": "Trammel", - "count": 5, + "count": 54, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, + "homeRange": 150, + "walkingRange": 150, "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 15, "probability": 100 }, + { "name": "Scorpion", "maxCount": 15, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 6, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cefa6e21-5391-467f-818c-5da5eb3ab833", + "name": "Spawner (111)", + "location": [5457, 2651, 45], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 6, "probability": 100 }, + { "name": "Mummy", "maxCount": 4, "probability": 100 }, + { "name": "Ettin", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef5cab61-3a77-4439-9342-d0834b31b96a", + "name": "Spawner (111)", + "location": [5520, 2890, 33], + "map": "Trammel", + "count": 37, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Imp", "maxCount": 10, "probability": 100 }, + { "name": "Scorpion", "maxCount": 10, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec56459e-8667-4086-8068-699bbe693b8d", + "name": "Spawner (111)", + "location": [5520, 2890, 33], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 4, "probability": 100 }, + { "name": "Mummy", "maxCount": 2, "probability": 100 }, + { "name": "Ettin", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84c8a1d7-5dfd-4115-a610-95e853d19ff8", + "name": "Spawner (111)", + "location": [5580, 2885, 6], + "map": "Trammel", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "Reaper", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 2, "probability": 100 }, { "name": "Troll", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "057290d8-05ed-4d10-8b1b-b6ff72396554", + "type": "Spawner", + "guid": "2071c33b-72f7-4fb4-b2b3-923940d3ab47", "name": "Spawner (111)", - "location": [5863, 2470, 46], + "location": [5557, 2625, 0], "map": "Trammel", - "count": 6, + "count": 21, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 25, - "walkingRange": 25, + "homeRange": 40, + "walkingRange": 40, "entries": [ - { "name": "Wyvern", "maxCount": 6, "probability": 100 } + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 3, "probability": 100 }, + { "name": "Troll", "maxCount": 3, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "05f8720b-51de-4359-96c4-edee763473a8", + "type": "Spawner", + "guid": "527e7c9f-a712-4fb2-956f-cb48ed793b21", "name": "Spawner (111)", - "location": [5820, 3675, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "07bf9106-e8c2-4f1c-8d4b-1344e90624a3", - "name": "Spawner (111)", - "location": [5838, 2651, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 75, - "entries": [ - { "name": "Imp", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Scorpion", "maxCount": 1, "probability": 100 }, - { "name": "StoneHarpy", "maxCount": 1, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "09c49d83-9516-43f4-b947-545a44f71516", - "name": "Spawner (111)", - "location": [5523, 3676, 45], + "location": [5394, 2530, 6], "map": "Trammel", "count": 8, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, + "homeRange": 15, + "walkingRange": 25, "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } + { "name": "Cyclops", "maxCount": 5, "probability": 100 }, + { "name": "Titan", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "0a34689d-a46f-4d99-a9d3-96eb175dcf83", + "type": "Spawner", + "guid": "f8909944-1483-4ba8-9387-6706bbf6aa41", "name": "Spawner (111)", - "location": [5820, 3775, 48], + "location": [5711, 2494, 46], "map": "Trammel", - "count": 5, + "count": 13, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, + "homeRange": 80, + "walkingRange": 80, "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } + { "name": "Imp", "maxCount": 3, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "0ae04c79-0948-4ebf-a7e8-2241db34b661", + "type": "Spawner", + "guid": "562a1a95-4a94-4fde-8bca-5e7f53561a76", "name": "Spawner (111)", - "location": [5251, 3301, -2], + "location": [5711, 2494, 46], "map": "Trammel", - "count": 20, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Ettin", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f9f0921-b571-4713-b9e0-b61ea7b54b01", + "name": "Spawner (111)", + "location": [5765, 2637, 39], + "map": "Trammel", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, "entries": [ - { "name": "BrownBear", "maxCount": 6, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, - { "name": "BlackBear", "maxCount": 6, "probability": 100 }, - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, - { "name": "Rabbit", "maxCount": 4, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Cougar", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 } + { "name": "OphidianArchmage", "maxCount": 3, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 6, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 3, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 4, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 10, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "48e240ce-cc40-4668-8ea9-a1238cc8f812", + "name": "Spawner (111)", + "location": [5697, 2626, -2], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "Wyvern", "maxCount": 2, "probability": 100 }, + { "name": "Drake", "maxCount": 4, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "0eea03f4-90bc-445e-b9d2-a5c563620af3", "name": "Spawner (111)", "location": [5729, 2767, -1], @@ -227,153 +247,271 @@ ] }, { - "$type": "Spawner", - "guid": "0eeef349-4bf3-414a-8a66-b0a1de50f17c", + "type": "Spawner", + "guid": "abd01cf8-e978-4a3b-9368-8004894052df", "name": "Spawner (111)", - "location": [5624, 3975, -3], + "location": [5729, 2767, -1], "map": "Trammel", - "count": 7, + "count": 14, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, + "homeRange": 60, + "walkingRange": 70, "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "Mummy", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "11d43b66-9a30-4c18-801b-a93222314461", + "type": "Spawner", + "guid": "5a51c39e-d65f-4ae3-bef0-7b35a7b6f278", "name": "Spawner (111)", - "location": [5919, 3875, 16], + "location": [5724, 2949, 31], "map": "Trammel", - "count": 7, + "count": 44, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, + "homeRange": 65, + "walkingRange": 75, "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + { "name": "HellHound", "maxCount": 8, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 8, "probability": 100 }, + { "name": "FireElemental", "maxCount": 10, "probability": 100 }, + { "name": "Efreet", "maxCount": 4, "probability": 100 }, + { "name": "HellCat", "maxCount": 6, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "12b8818a-8be6-47c4-bdc6-27a68420bc60", + "type": "Spawner", + "guid": "521f89eb-db58-4e5c-8633-f435c40ab6eb", "name": "Spawner (111)", - "location": [5921, 3777, 28], + "location": [5724, 2949, 31], "map": "Trammel", - "count": 10, + "count": 15, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] + "homeRange": 65, + "walkingRange": 75, + "entries": [{ "name": "LavaSnake", "maxCount": 15, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "12cb6fcd-2072-4003-885b-76821f1993cf", + "type": "Spawner", + "guid": "057290d8-05ed-4d10-8b1b-b6ff72396554", "name": "Spawner (111)", - "location": [5728, 3209, -3], + "location": [5863, 2470, 46], "map": "Trammel", - "count": 7, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 100, - "walkingRange": 100, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Wyvern", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aa8093ad-4207-4986-9724-1fae5cf6e257", + "name": "Spawner (111)", + "location": [5501, 2361, 27], + "map": "Trammel", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, "entries": [ - { "name": "SwampDragon", "maxCount": 7, "probability": 100 } + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 8, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, + { "name": "Walrus", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "1315e61d-169b-47e9-9281-395b54cd4fbb", + "type": "Spawner", + "guid": "f6e752bd-6e0e-4436-9b9e-7598b356993b", "name": "Spawner (111)", - "location": [5218, 3786, 1], + "location": [5501, 2361, 27], "map": "Trammel", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, + "homeRange": 40, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b5a3d231-4dca-41de-92e9-8f8eb2e23cf9", + "name": "Spawner (111)", + "location": [5351, 2359, 0], + "map": "Trammel", + "count": 29, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 7, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, + { "name": "Walrus", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "1524f465-d2dd-4315-b98f-e54e03000f27", + "type": "Spawner", + "guid": "8efa8f7f-e693-4967-86be-e19bb93893c0", "name": "Spawner (111)", - "location": [5721, 3876, -3], + "location": [5351, 2359, 0], "map": "Trammel", - "count": 10, + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "894ddf46-b0fd-43c4-8fc0-a2bf626e2a53", + "name": "Spawner (111)", + "location": [5173, 2368, 21], + "map": "Trammel", + "count": 28, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 10, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, + { "name": "Walrus", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "230b1817-c355-4413-a899-3d1dcd5087c9", + "name": "Spawner (111)", + "location": [5173, 2368, 21], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "PolarBear", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c142b48f-bf5a-4953-9af2-8f72be636600", + "name": "Spawner (111)", + "location": [6091, 2379, 25], + "map": "Trammel", + "count": 28, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 60, + "entries": [ + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 10, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, + { "name": "Walrus", "maxCount": 3, "probability": 100 }, + { "name": "PolarBear", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa842995-ea24-4be7-9594-6632e33971c3", + "name": "Spawner (111)", + "location": [5967, 2370, 44], + "map": "Trammel", + "count": 26, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 50, - "walkingRange": 50, + "walkingRange": 75, "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 4, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "162b114a-bf25-4054-8124-1ed834e586b1", + "type": "Spawner", + "guid": "b3d1dec7-ad92-4678-926f-6addcb325ebc", "name": "Spawner (111)", - "location": [5521, 3875, -1], + "location": [5967, 2370, 44], "map": "Trammel", - "count": 7, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 50, - "walkingRange": 50, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8c3d2529-fd5d-4c3a-8071-4df7504d621e", + "name": "Spawner (111)", + "location": [5817, 2367, 42], + "map": "Trammel", + "count": 23, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 3, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 3, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "20ee3b9f-9776-4cf4-aee9-faa751edca21", + "name": "Spawner (111)", + "location": [5817, 2367, 42], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "17dfff61-8b8f-400c-b93a-1e15c55b42db", "name": "Spawner (111)", "location": [5756, 3453, -2], @@ -393,7 +531,170 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "b9dfdd93-a5e2-4db4-b98c-9da90a65101f", + "name": "Spawner (111)", + "location": [5985, 3441, 17], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 90, + "entries": [ + { "name": "GiantToad", "maxCount": 8, "probability": 100 }, + { "name": "SwampDragon", "maxCount": 5, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "Alligator", "maxCount": 12, "probability": 100 }, + { "name": "SewerRat", "maxCount": 11, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf87c030-7319-45ba-bf07-e086dda7f7f3", + "name": "Spawner (111)", + "location": [5985, 3441, 17], + "map": "Trammel", + "count": 36, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 90, + "entries": [ + { "name": "BullFrog", "maxCount": 15, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 5, "probability": 100 }, + { "name": "Bogling", "maxCount": 13, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa220a4b-57d9-4b63-b224-bd411a926045", + "name": "Spawner (111)", + "location": [5220, 3664, -1], + "map": "Trammel", + "count": 23, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 3, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 6, "probability": 100 }, + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe198cdf-57f9-40d8-8878-de34a8c84298", + "name": "Spawner (111)", + "location": [5212, 3619, 0], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "SavageRider", "maxCount": 3, "probability": 100 }, + { "name": "Savage", "maxCount": 9, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b13fd88-7eec-4422-9b79-bd314a1bba2f", + "name": "Spawner (111)", + "location": [5347, 3449, 17], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "218c75c8-acf3-4239-b72e-4ac850c8918a", + "name": "Spawner (111)", + "location": [5243, 3548, 14], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9cb57aa-0183-4149-af6b-a47499a2ff72", + "name": "Spawner (111)", + "location": [5250, 3449, 16], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3b32813c-00d7-479a-b5e5-1570dd11e90e", + "name": "Spawner (111)", + "location": [5349, 3547, -1], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "17e80af9-7b12-4515-99af-8a61e7ba5d44", "name": "Spawner (111)", "location": [5201, 3162, 53], @@ -418,1497 +719,7 @@ ] }, { - "$type": "Spawner", - "guid": "1f5d2b74-9714-4a11-8f0d-b88413e22556", - "name": "Spawner (111)", - "location": [6022, 2874, 16], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f9f0921-b571-4713-b9e0-b61ea7b54b01", - "name": "Spawner (111)", - "location": [5765, 2637, 39], - "map": "Trammel", - "count": 26, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 30, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 3, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 6, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 3, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 4, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2071c33b-72f7-4fb4-b2b3-923940d3ab47", - "name": "Spawner (111)", - "location": [5557, 2625, 0], - "map": "Trammel", - "count": 21, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 3, "probability": 100 }, - { "name": "Troll", "maxCount": 3, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20ee3b9f-9776-4cf4-aee9-faa751edca21", - "name": "Spawner (111)", - "location": [5817, 2367, 42], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 75, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "218c75c8-acf3-4239-b72e-4ac850c8918a", - "name": "Spawner (111)", - "location": [5243, 3548, 14], - "map": "Trammel", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 4, "probability": 100 }, - { "name": "Hind", "maxCount": 16, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "227477e6-a891-4b3e-b43b-305c6b6541f4", - "name": "Spawner (111)", - "location": [5523, 3676, 45], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "230b1817-c355-4413-a899-3d1dcd5087c9", - "name": "Spawner (111)", - "location": [5173, 2368, 21], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "PolarBear", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "27ac5355-03f9-44a1-9d04-78db0e9b9d25", - "name": "Spawner (111)", - "location": [6022, 3971, 17], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "28a1961d-3ff6-4890-a603-c5fb5512eb3e", - "name": "Spawner (111)", - "location": [5920, 3675, 12], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2b1377f8-2453-4797-8f16-121a5da7dd1d", - "name": "Spawner (111)", - "location": [5918, 3975, 1], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2b552925-08cd-4be2-b96f-85701200ab9a", - "name": "Spawner (111)", - "location": [5918, 3975, 1], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2c914773-48a0-4129-ae65-87ee6707e4f7", - "name": "Spawner (111)", - "location": [5819, 3974, 16], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "318ff931-f450-4626-b97f-13af5c0e41e3", - "name": "Spawner (111)", - "location": [5721, 3876, -3], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "34888307-f095-4c8b-afe0-e5f3c45c5a24", - "name": "Spawner (111)", - "location": [5820, 3675, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37ba19f6-eb60-4539-8f17-b87529148607", - "name": "Spawner (111)", - "location": [5921, 3777, 28], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37c219c7-139b-4604-acd9-34e10d961cd9", - "name": "Spawner (111)", - "location": [5920, 3675, 12], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3b32813c-00d7-479a-b5e5-1570dd11e90e", - "name": "Spawner (111)", - "location": [5349, 3547, -1], - "map": "Trammel", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 4, "probability": 100 }, - { "name": "Hind", "maxCount": 16, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3bb22e15-16d5-4f29-b41a-051fa2a5bf56", - "name": "Spawner (111)", - "location": [5820, 3775, 48], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40332222-33e9-4df1-bf72-15f818f2c5f2", - "name": "Spawner (111)", - "location": [5432, 3079, -4], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4306a87f-e65c-43ba-96d2-276065502e18", - "name": "Spawner (111)", - "location": [5721, 3975, 21], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "46191d14-a2b4-4995-b761-4d91195932b0", - "name": "Spawner (111)", - "location": [5466, 4005, -1], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rabbit", "maxCount": 20, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, - { "name": "Bird", "maxCount": 15, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 }, - { "name": "Hind", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "48e240ce-cc40-4668-8ea9-a1238cc8f812", - "name": "Spawner (111)", - "location": [5697, 2626, -2], - "map": "Trammel", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 30, - "entries": [ - { "name": "Wyvern", "maxCount": 2, "probability": 100 }, - { "name": "Drake", "maxCount": 4, "probability": 100 }, - { "name": "Imp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b7e25a1-2cca-4a59-823e-ec421274efc1", - "name": "Spawner (111)", - "location": [5566, 3292, 4], - "map": "Trammel", - "count": 21, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 56, - "walkingRange": 62, - "entries": [ - { "name": "Gorilla", "maxCount": 5, "probability": 100 }, - { "name": "Panther", "maxCount": 5, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4be24872-6eab-4638-85db-fca18bf64762", - "name": "Spawner (111)", - "location": [5921, 3777, 28], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "502dd43b-fe21-4851-83cc-0479b0fd3f99", - "name": "Spawner (111)", - "location": [5921, 3777, 28], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50ccc29d-4ee3-4606-b7e2-c03156afb9ba", - "name": "Spawner (111)", - "location": [5721, 3675, 22], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "521f89eb-db58-4e5c-8633-f435c40ab6eb", - "name": "Spawner (111)", - "location": [5724, 2949, 31], - "map": "Trammel", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 65, - "walkingRange": 75, - "entries": [ - { "name": "LavaSnake", "maxCount": 15, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "527e7c9f-a712-4fb2-956f-cb48ed793b21", - "name": "Spawner (111)", - "location": [5394, 2530, 6], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Cyclops", "maxCount": 5, "probability": 100 }, - { "name": "Titan", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "53071c27-4eb5-4101-b20f-0008810bf616", - "name": "Spawner (111)", - "location": [5621, 3775, 2], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54dcd3be-256f-48ca-900d-f585ae0a721a", - "name": "Spawner (111)", - "location": [5722, 3776, 32], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "562a1a95-4a94-4fde-8bca-5e7f53561a76", - "name": "Spawner (111)", - "location": [5711, 2494, 46], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 80, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "57a3231a-5b0e-45dd-bcfc-3340878eb9cf", - "name": "Spawner (111)", - "location": [5784, 3951, 20], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 35, - "walkingRange": 35, - "entries": [ - { "name": "FrenziedOstard", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5812b2ff-56e7-47ee-937f-040e60498ca1", - "name": "Spawner (111)", - "location": [5624, 3975, -3], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a51c39e-d65f-4ae3-bef0-7b35a7b6f278", - "name": "Spawner (111)", - "location": [5724, 2949, 31], - "map": "Trammel", - "count": 44, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 65, - "walkingRange": 75, - "entries": [ - { "name": "HellHound", "maxCount": 8, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 8, "probability": 100 }, - { "name": "FireElemental", "maxCount": 10, "probability": 100 }, - { "name": "Efreet", "maxCount": 4, "probability": 100 }, - { "name": "HellCat", "maxCount": 6, "probability": 100 }, - { "name": "LavaSerpent", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5b655971-59b1-4016-b9d2-c2d4c2a11e50", - "name": "Spawner (111)", - "location": [5621, 3775, 2], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e007014-f2b1-4454-8fc7-66065189e313", - "name": "Spawner (111)", - "location": [5819, 3974, 16], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5f584b25-d87b-49e8-94ae-0405a3d3cc45", - "name": "Spawner (111)", - "location": [6018, 3673, 18], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6087c54a-11de-4326-8ec5-cd5b5cde47a4", - "name": "Spawner (111)", - "location": [5623, 3875, 15], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "65753d38-25f4-4282-9dab-3fa55cba90f4", - "name": "Spawner (111)", - "location": [5721, 3876, -3], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "68e6f042-92f2-45a9-ae8d-0752b3b4b944", - "name": "Spawner (111)", - "location": [5623, 3875, 15], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b13fd88-7eec-4422-9b79-bd314a1bba2f", - "name": "Spawner (111)", - "location": [5347, 3449, 17], - "map": "Trammel", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 4, "probability": 100 }, - { "name": "Hind", "maxCount": 16, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bb402f0-2399-410d-ac65-eed098e522ca", - "name": "Spawner (111)", - "location": [5721, 3876, -3], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6e6ba9f1-8c4c-4142-b251-141d2ea7a7f0", - "name": "Spawner (111)", - "location": [5722, 3776, 32], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f64b0f6-5882-4551-9a27-ca031288fbae", - "name": "Spawner (111)", - "location": [5159, 3913, 1], - "map": "Trammel", - "count": 11, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 10, - "entries": [ - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 1, "probability": 100 }, - { "name": "Bull", "maxCount": 1, "probability": 100 }, - { "name": "Chicken", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f6ce1e0-6056-4ae7-b976-ea650d4a0a9c", - "name": "Spawner (111)", - "location": [6022, 3773, 18], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7181d30e-02d5-4a40-9d4a-d856732d05e9", - "name": "Spawner (111)", - "location": [5624, 3975, -3], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "723998f0-08d5-4124-84d3-df5401888a32", - "name": "Spawner (111)", - "location": [5368, 3887, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72d79fc1-9984-4107-9c30-f4dc77c2976f", - "name": "Spawner (111)", - "location": [6018, 3673, 18], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "78272d11-7b2e-4e49-b533-9f82138558d1", - "name": "Spawner (111)", - "location": [5623, 3676, -3], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "79cc7028-11d1-49df-b0a5-75066e5a6559", - "name": "Spawner (111)", - "location": [5521, 3775, 8], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "79f39847-4344-4381-8b37-8eaa8c2ffef7", - "name": "Spawner (111)", - "location": [5722, 3776, 32], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7aa1e207-3ea8-4aca-b0dc-a0a585466eed", - "name": "Spawner (111)", - "location": [5521, 3775, 8], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7dca1dbd-9eb3-4a64-b276-02d6205baf2a", - "name": "Spawner (111)", - "location": [5478, 3587, 3], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7e42b948-0bd7-4f93-8303-2d7723a1bb1d", - "name": "Spawner (111)", - "location": [5820, 3775, 48], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7ff8cd5c-784b-4c77-b20a-de86c8fafa76", - "name": "Spawner (111)", - "location": [5250, 3401, 1], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Llama", "maxCount": 2, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "832c6d54-6e88-4e70-ba0f-9731b9ecc3cc", - "name": "Spawner (111)", - "location": [5521, 3875, -1], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84c8a1d7-5dfd-4115-a610-95e853d19ff8", - "name": "Spawner (111)", - "location": [5580, 2885, 6], - "map": "Trammel", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Gazer", "maxCount": 3, "probability": 100 }, - { "name": "Reaper", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84c9128d-15f3-4670-ab03-a6472bb66f26", - "name": "Spawner (111)", - "location": [5919, 3875, 16], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "86e1ffa4-247e-40e8-9b24-069df5468fc4", - "name": "Spawner (111)", - "location": [5623, 3676, -3], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "894ddf46-b0fd-43c4-8fc0-a2bf626e2a53", - "name": "Spawner (111)", - "location": [5173, 2368, 21], - "map": "Trammel", - "count": 28, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, - { "name": "IceSnake", "maxCount": 10, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, - { "name": "Walrus", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "896be635-414c-4750-8e26-742fea9d795c", - "name": "Spawner (111)", - "location": [5623, 3875, 15], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ad6c839-2bd5-4b9e-8ce6-3e55b1d2de83", - "name": "Spawner (111)", - "location": [6022, 2874, 16], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8add37de-2b09-4215-9568-e8586520f1ce", - "name": "Spawner (111)", - "location": [5820, 3675, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8b4617e7-da4a-42b4-9f4b-7d2e5e1fad7f", - "name": "Spawner (111)", - "location": [5454, 3136, -60], - "map": "Trammel", - "count": 26, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "TerathanMatriarch", "maxCount": 2, "probability": 100 }, - { "name": "TerathanAvenger", "maxCount": 4, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 10, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8bfa4c3d-f412-4707-a48f-e358295e31e1", - "name": "Spawner (111)", - "location": [5218, 3786, 1], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rabbit", "maxCount": 20, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, - { "name": "Bird", "maxCount": 15, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 }, - { "name": "Hind", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8c3d2529-fd5d-4c3a-8071-4df7504d621e", - "name": "Spawner (111)", - "location": [5817, 2367, 42], - "map": "Trammel", - "count": 23, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 75, - "entries": [ - { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, - { "name": "IceSnake", "maxCount": 3, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 3, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8cdfed0c-8b67-4041-9923-82af57871539", - "name": "Spawner (111)", - "location": [5351, 3401, 30], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Llama", "maxCount": 2, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d4b41e7-0405-43b3-babf-4855aa1222aa", - "name": "Spawner (111)", - "location": [6022, 2874, 16], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8df84079-fd67-48ff-85d2-c2aab5b08d89", - "name": "Spawner (111)", - "location": [5570, 3153, 14], - "map": "Trammel", - "count": 25, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 60, - "walkingRange": 80, - "entries": [ - { "name": "Gorilla", "maxCount": 6, "probability": 100 }, - { "name": "Panther", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8efa8f7f-e693-4967-86be-e19bb93893c0", - "name": "Spawner (111)", - "location": [5351, 2359, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 75, - "entries": [ - { "name": "PolarBear", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8fce4e75-131b-4255-b270-41bcaf47d4b4", - "name": "Spawner (111)", - "location": [5721, 3675, 22], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "917b5953-f939-46c0-863e-17a1ca293dd8", - "name": "Spawner (111)", - "location": [5478, 3587, 3], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94b422df-ea7d-4c7f-816f-1f89ad5c9e21", - "name": "Spawner (111)", - "location": [5478, 3587, 3], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "94ea8ab3-3e9d-4d64-8c52-c9a96347b234", "name": "Spawner (111)", "location": [5201, 3162, 53], @@ -1926,177 +737,93 @@ ] }, { - "$type": "Spawner", - "guid": "98af7987-041e-4589-aa4a-259b5faf9057", + "type": "Spawner", + "guid": "0ae04c79-0948-4ebf-a7e8-2241db34b661", "name": "Spawner (111)", - "location": [6022, 3773, 18], + "location": [5251, 3301, -2], "map": "Trammel", - "count": 7, + "count": 20, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 50, "walkingRange": 50, "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "9a5e4485-6df8-4fae-9288-90ac1e3a871d", + "type": "Spawner", + "guid": "020cf0fc-c7b4-4735-b113-a5f076bfd189", "name": "Spawner (111)", - "location": [5523, 3676, 45], + "location": [5251, 3301, -2], "map": "Trammel", - "count": 10, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 50, "walkingRange": 50, "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "9e2ede8e-0dab-4f8e-b6c2-e84626c0acb3", + "type": "Spawner", + "guid": "034a2452-0f15-48f2-9bc3-3a1fc3deed2e", "name": "Spawner (111)", - "location": [5194, 3885, -1], + "location": [5250, 3401, 1], "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 32, - "walkingRange": 45, - "entries": [ - { "name": "Bull", "maxCount": 10, "probability": 100 }, - { "name": "GreatHart", "maxCount": 6, "probability": 100 }, - { "name": "Cow", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9e889357-16df-4213-bc1e-e19a942f58ad", - "name": "Spawner (111)", - "location": [5818, 3876, 18], - "map": "Trammel", - "count": 5, + "count": 20, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 50, "walkingRange": 50, "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "a02dc119-74bf-4864-92ab-94c29aefc6be", + "type": "Spawner", + "guid": "7ff8cd5c-784b-4c77-b20a-de86c8fafa76", "name": "Spawner (111)", - "location": [5490, 3071, 15], + "location": [5250, 3401, 1], "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a268bfc5-82da-4d3a-aac5-ca114f7f2c9a", - "name": "Spawner (111)", - "location": [5920, 3675, 12], - "map": "Trammel", - "count": 8, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 50, "walkingRange": 50, "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "a2e8331c-2368-4014-ba4c-afa7e63c86e3", - "name": "Spawner (111)", - "location": [5820, 3675, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a43ee478-1a1b-4c04-85b9-0c9fe436e7db", - "name": "Spawner (111)", - "location": [5521, 3875, -1], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a49e3c21-9ea2-426f-a94d-f36324e96751", "name": "Spawner (111)", "location": [5351, 3401, 30], @@ -2121,910 +848,50 @@ ] }, { - "$type": "Spawner", - "guid": "a5dd609e-9369-4d3c-b3e0-2d74ce2d204a", + "type": "Spawner", + "guid": "8cdfed0c-8b67-4041-9923-82af57871539", "name": "Spawner (111)", - "location": [5919, 3875, 16], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a91547bb-7e0f-4518-92f8-dbaadb86bdfb", - "name": "Spawner (111)", - "location": [5819, 3974, 16], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa220a4b-57d9-4b63-b224-bd411a926045", - "name": "Spawner (111)", - "location": [5220, 3664, -1], - "map": "Trammel", - "count": 23, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 25, - "entries": [ - { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 3, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 6, "probability": 100 }, - { "name": "Wraith", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa8093ad-4207-4986-9724-1fae5cf6e257", - "name": "Spawner (111)", - "location": [5501, 2361, 27], - "map": "Trammel", - "count": 30, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 75, - "entries": [ - { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, - { "name": "IceSnake", "maxCount": 8, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, - { "name": "Walrus", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa842995-ea24-4be7-9594-6632e33971c3", - "name": "Spawner (111)", - "location": [5967, 2370, 44], - "map": "Trammel", - "count": 26, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 75, - "entries": [ - { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, - { "name": "IceSnake", "maxCount": 4, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abd01cf8-e978-4a3b-9368-8004894052df", - "name": "Spawner (111)", - "location": [5729, 2767, -1], - "map": "Trammel", - "count": 14, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 60, - "walkingRange": 70, - "entries": [ - { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Wyvern", "maxCount": 3, "probability": 100 }, - { "name": "Mummy", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abe4fbf3-2c4d-4e8d-be6c-d1fda9d7bab6", - "name": "Spawner (111)", - "location": [5367, 4003, 19], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad3477fd-221f-4a59-ba0d-e120e09a9146", - "name": "Spawner (111)", - "location": [5721, 3975, 21], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "afb0dc5f-fdc5-441a-a5dc-288767a16e69", - "name": "Spawner (111)", - "location": [6022, 3773, 18], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b0213ce1-2ab9-4aa0-899c-8a9c5aed26f7", - "name": "Spawner (111)", - "location": [5523, 3676, 45], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b100d6ba-9d60-4454-9657-4b4b5a84b528", - "name": "Spawner (111)", - "location": [5918, 3975, 1], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3d1dec7-ad92-4678-926f-6addcb325ebc", - "name": "Spawner (111)", - "location": [5967, 2370, 44], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 75, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b5a3d231-4dca-41de-92e9-8f8eb2e23cf9", - "name": "Spawner (111)", - "location": [5351, 2359, 0], - "map": "Trammel", - "count": 29, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 75, - "entries": [ - { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, - { "name": "IceSnake", "maxCount": 7, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, - { "name": "Walrus", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b606b627-c425-445c-9bc1-0e8e0a502c0d", - "name": "Spawner (111)", - "location": [5446, 3074, 3], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b614a58d-1a96-4cc1-b136-30bc2ee50291", - "name": "Spawner (111)", - "location": [5721, 3675, 22], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b7087528-b615-4aba-8544-e4904472600f", - "name": "Spawner (111)", - "location": [5818, 3876, 18], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b7df3e65-2706-4ad9-89e4-20e50c6f83ba", - "name": "Spawner (111)", - "location": [5764, 2585, -4], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 75, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9afd457-3945-403c-a0de-fb2b0ab73b8e", - "name": "Spawner (111)", - "location": [5819, 3974, 16], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9dfdd93-a5e2-4db4-b98c-9da90a65101f", - "name": "Spawner (111)", - "location": [5985, 3441, 17], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 90, - "entries": [ - { "name": "GiantToad", "maxCount": 8, "probability": 100 }, - { "name": "SwampDragon", "maxCount": 5, "probability": 100 }, - { "name": "GiantRat", "maxCount": 12, "probability": 100 }, - { "name": "Alligator", "maxCount": 12, "probability": 100 }, - { "name": "SewerRat", "maxCount": 11, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bbd8cc70-dbcc-477c-bbe3-9865e30453da", - "name": "Spawner (111)", - "location": [5521, 3775, 8], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf87c030-7319-45ba-bf07-e086dda7f7f3", - "name": "Spawner (111)", - "location": [5985, 3441, 17], - "map": "Trammel", - "count": 36, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 90, - "entries": [ - { "name": "BullFrog", "maxCount": 15, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 5, "probability": 100 }, - { "name": "Bogling", "maxCount": 13, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c07ac1f6-f0bd-483f-ac86-db1e8cc1914f", - "name": "Spawner (111)", - "location": [5623, 3676, -3], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c142b48f-bf5a-4953-9af2-8f72be636600", - "name": "Spawner (111)", - "location": [6091, 2379, 25], - "map": "Trammel", - "count": 28, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 60, - "entries": [ - { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, - { "name": "IceSnake", "maxCount": 10, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, - { "name": "Walrus", "maxCount": 3, "probability": 100 }, - { "name": "PolarBear", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c339abe3-345c-4881-aa87-f0c360517450", - "name": "Spawner (111)", - "location": [5624, 3975, -3], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c4ea0559-6a74-406b-a53a-aa0aa9338d4d", - "name": "Spawner (111)", - "location": [5478, 3587, 3], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c6882bff-77fb-4555-8369-c948185411a3", - "name": "Spawner (111)", - "location": [5467, 3078, -3], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c8d068d0-cdb5-4976-a3af-bff4443a1e0c", - "name": "Spawner (111)", - "location": [5440, 3292, -4], - "map": "Trammel", - "count": 21, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 56, - "walkingRange": 62, - "entries": [ - { "name": "Gorilla", "maxCount": 5, "probability": 100 }, - { "name": "Panther", "maxCount": 5, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "caf23365-239f-47ea-a5df-e469a64d10f9", - "name": "Spawner (111)", - "location": [5371, 3820, -2], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rabbit", "maxCount": 20, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, - { "name": "Bird", "maxCount": 15, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 }, - { "name": "Hind", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cbf0e551-7d26-4227-b14e-9ae59fbfafd1", - "name": "Spawner (111)", - "location": [6022, 3971, 17], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ce64bca0-53f1-4f9a-a4ff-2da36a7bdcb6", - "name": "Spawner (111)", - "location": [5621, 3775, 2], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cefa6e21-5391-467f-818c-5da5eb3ab833", - "name": "Spawner (111)", - "location": [5457, 2651, 45], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Wyvern", "maxCount": 6, "probability": 100 }, - { "name": "Mummy", "maxCount": 4, "probability": 100 }, - { "name": "Ettin", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d055b36c-22ea-42c2-b862-493a9a0ba34e", - "name": "Spawner (111)", - "location": [5621, 3775, 2], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rat", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "Reaper", "maxCount": 1, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d0844af0-b9fd-4500-8751-8d4e0f913ee2", - "name": "Spawner (111)", - "location": [6022, 3971, 17], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d463133b-6448-4ead-95a3-6cbfe8ce50cd", - "name": "Spawner (111)", - "location": [6022, 2874, 16], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d5989a1c-98df-44fd-8c77-1fecf310a3d2", - "name": "Spawner (111)", - "location": [5367, 4003, 19], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rabbit", "maxCount": 20, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, - { "name": "Bird", "maxCount": 15, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 }, - { "name": "Hind", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d89f6fd3-bc3e-4d39-a5b2-6d0b186d2a44", - "name": "Spawner (111)", - "location": [5820, 3775, 48], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9cb57aa-0183-4149-af6b-a47499a2ff72", - "name": "Spawner (111)", - "location": [5250, 3449, 16], - "map": "Trammel", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Horse", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 4, "probability": 100 }, - { "name": "Hind", "maxCount": 16, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "db56d1fb-cd8c-4a7d-a029-1170f3bd78dc", - "name": "Spawner (111)", - "location": [5721, 3675, 22], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "db7d0ea8-885d-460c-95dc-08a5870b6b32", - "name": "Spawner (111)", - "location": [5371, 3820, -2], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd4ffdc9-a292-4a4b-a765-1f3a1ec0e9c4", - "name": "Spawner (111)", - "location": [5623, 3676, -3], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd535de1-dc4b-46a9-9018-feedf4c80edb", - "name": "Spawner (111)", - "location": [5200, 3895, 2], + "location": [5351, 3401, 30], "map": "Trammel", "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 20, + "homeRange": 50, + "walkingRange": 50, "entries": [ - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "df38e40e-4233-447c-9faa-0eda376e975d", + "type": "Spawner", + "guid": "72d79fc1-9984-4107-9c30-f4dc77c2976f", "name": "Spawner (111)", - "location": [5521, 3775, 8], + "location": [6018, 3673, 18], "map": "Trammel", - "count": 7, + "count": 10, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 50, "walkingRange": 50, "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e1308141-2a7f-45ea-8514-38453ba30742", - "name": "Spawner (111)", - "location": [5520, 3939, 42], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e23f7c11-dffc-4ba8-bae8-af7acae97635", - "name": "Spawner (111)", - "location": [5818, 3876, 18], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e30e600c-0ae2-4521-8721-ec8a952f47b4", "name": "Spawner (111)", "location": [6018, 3673, 18], @@ -3045,65 +912,7 @@ ] }, { - "$type": "Spawner", - "guid": "e44775de-291a-43dd-9103-11c3a2063c7c", - "name": "Spawner (111)", - "location": [5408, 3077, 16], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e58b638c-9203-4d48-925a-0129f612fae4", - "name": "Spawner (111)", - "location": [5466, 4005, -1], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e64232f7-0ccb-4c81-9a36-0c96fdd8e00a", - "name": "Spawner (111)", - "location": [5721, 3975, 21], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e6f3017b-f7d2-4957-8de4-c415f99c7a6c", "name": "Spawner (111)", "location": [6018, 3673, 18], @@ -3124,10 +933,10 @@ ] }, { - "$type": "Spawner", - "guid": "ea9eae49-ae57-49b2-8a41-c956d8e8aafe", + "type": "Spawner", + "guid": "5f584b25-d87b-49e8-94ae-0405a3d3cc45", "name": "Spawner (111)", - "location": [5521, 3875, -1], + "location": [6018, 3673, 18], "map": "Trammel", "count": 5, "minDelay": "00:05:00", @@ -3143,244 +952,7 @@ ] }, { - "$type": "Spawner", - "guid": "ebb689ed-d6b6-4395-b26b-ef1fecd26093", - "name": "Spawner (111)", - "location": [6022, 3971, 17], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec1caee7-0d96-49ac-8b1f-f759b03efdf8", - "name": "Spawner (111)", - "location": [5818, 3876, 18], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 1, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec56459e-8667-4086-8068-699bbe693b8d", - "name": "Spawner (111)", - "location": [5520, 2890, 33], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Wyvern", "maxCount": 4, "probability": 100 }, - { "name": "Mummy", "maxCount": 2, "probability": 100 }, - { "name": "Ettin", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ef5cab61-3a77-4439-9342-d0834b31b96a", - "name": "Spawner (111)", - "location": [5520, 2890, 33], - "map": "Trammel", - "count": 37, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Imp", "maxCount": 10, "probability": 100 }, - { "name": "Scorpion", "maxCount": 10, "probability": 100 }, - { "name": "DesertOstard", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, - { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "efc4c627-fca8-4e56-ae35-f66d3552c8bd", - "name": "Spawner (111)", - "location": [5457, 2651, 45], - "map": "Trammel", - "count": 54, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Imp", "maxCount": 15, "probability": 100 }, - { "name": "Scorpion", "maxCount": 15, "probability": 100 }, - { "name": "DesertOstard", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 6, "probability": 100 }, - { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f10cdd8b-b8ea-44ad-9b02-9bb147f5c85d", - "name": "Spawner (111)", - "location": [5919, 3875, 16], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Gazer", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Harpy", "maxCount": 1, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f4d538e7-9d37-4707-81ff-db9eef73be4e", - "name": "Spawner (111)", - "location": [5920, 3675, 12], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f6e752bd-6e0e-4436-9b9e-7598b356993b", - "name": "Spawner (111)", - "location": [5501, 2361, 27], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 75, - "entries": [ - { "name": "PolarBear", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f77b4431-b6bf-47bc-9353-1ed0fa25dc0e", - "name": "Spawner (111)", - "location": [5368, 3887, 0], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Rabbit", "maxCount": 20, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, - { "name": "Bird", "maxCount": 15, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 }, - { "name": "Hind", "maxCount": 4, "probability": 100 }, - { "name": "Llama", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f8909944-1483-4ba8-9387-6706bbf6aa41", - "name": "Spawner (111)", - "location": [5711, 2494, 46], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 80, - "entries": [ - { "name": "Imp", "maxCount": 3, "probability": 100 }, - { "name": "Scorpion", "maxCount": 3, "probability": 100 }, - { "name": "DesertOstard", "maxCount": 1, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fa8905ce-91db-4d17-92c1-ae5b04738dea", - "name": "Spawner (111)", - "location": [5722, 3776, 32], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "BrownBear", "maxCount": 3, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, - { "name": "BlackBear", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 2, "probability": 100 }, - { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, - { "name": "Rabbit", "maxCount": 2, "probability": 100 }, - { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "fd0e2342-a6e7-4b41-acb2-8d0d6f316062", "name": "Spawner (111)", "location": [6022, 3773, 18], @@ -3405,7 +977,1186 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "98af7987-041e-4589-aa4a-259b5faf9057", + "name": "Spawner (111)", + "location": [6022, 3773, 18], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f6ce1e0-6056-4ae7-b976-ea650d4a0a9c", + "name": "Spawner (111)", + "location": [6022, 3773, 18], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "afb0dc5f-fdc5-441a-a5dc-288767a16e69", + "name": "Spawner (111)", + "location": [6022, 3773, 18], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f5d2b74-9714-4a11-8f0d-b88413e22556", + "name": "Spawner (111)", + "location": [6022, 2874, 16], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d463133b-6448-4ead-95a3-6cbfe8ce50cd", + "name": "Spawner (111)", + "location": [6022, 2874, 16], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d4b41e7-0405-43b3-babf-4855aa1222aa", + "name": "Spawner (111)", + "location": [6022, 2874, 16], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ad6c839-2bd5-4b9e-8ce6-3e55b1d2de83", + "name": "Spawner (111)", + "location": [6022, 2874, 16], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebb689ed-d6b6-4395-b26b-ef1fecd26093", + "name": "Spawner (111)", + "location": [6022, 3971, 17], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d0844af0-b9fd-4500-8751-8d4e0f913ee2", + "name": "Spawner (111)", + "location": [6022, 3971, 17], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cbf0e551-7d26-4227-b14e-9ae59fbfafd1", + "name": "Spawner (111)", + "location": [6022, 3971, 17], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "27ac5355-03f9-44a1-9d04-78db0e9b9d25", + "name": "Spawner (111)", + "location": [6022, 3971, 17], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4d538e7-9d37-4707-81ff-db9eef73be4e", + "name": "Spawner (111)", + "location": [5920, 3675, 12], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37c219c7-139b-4604-acd9-34e10d961cd9", + "name": "Spawner (111)", + "location": [5920, 3675, 12], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a268bfc5-82da-4d3a-aac5-ca114f7f2c9a", + "name": "Spawner (111)", + "location": [5920, 3675, 12], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28a1961d-3ff6-4890-a603-c5fb5512eb3e", + "name": "Spawner (111)", + "location": [5920, 3675, 12], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12b8818a-8be6-47c4-bdc6-27a68420bc60", + "name": "Spawner (111)", + "location": [5921, 3777, 28], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4be24872-6eab-4638-85db-fca18bf64762", + "name": "Spawner (111)", + "location": [5921, 3777, 28], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37ba19f6-eb60-4539-8f17-b87529148607", + "name": "Spawner (111)", + "location": [5921, 3777, 28], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "502dd43b-fe21-4851-83cc-0479b0fd3f99", + "name": "Spawner (111)", + "location": [5921, 3777, 28], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84c9128d-15f3-4670-ab03-a6472bb66f26", + "name": "Spawner (111)", + "location": [5919, 3875, 16], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "11d43b66-9a30-4c18-801b-a93222314461", + "name": "Spawner (111)", + "location": [5919, 3875, 16], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f10cdd8b-b8ea-44ad-9b02-9bb147f5c85d", + "name": "Spawner (111)", + "location": [5919, 3875, 16], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5dd609e-9369-4d3c-b3e0-2d74ce2d204a", + "name": "Spawner (111)", + "location": [5919, 3875, 16], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2b552925-08cd-4be2-b96f-85701200ab9a", + "name": "Spawner (111)", + "location": [5918, 3975, 1], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2b1377f8-2453-4797-8f16-121a5da7dd1d", + "name": "Spawner (111)", + "location": [5918, 3975, 1], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b100d6ba-9d60-4454-9657-4b4b5a84b528", + "name": "Spawner (111)", + "location": [5918, 3975, 1], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03829553-0468-42d4-b617-02116bca3200", + "name": "Spawner (111)", + "location": [5918, 3975, 1], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2e8331c-2368-4014-ba4c-afa7e63c86e3", + "name": "Spawner (111)", + "location": [5820, 3675, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05f8720b-51de-4359-96c4-edee763473a8", + "name": "Spawner (111)", + "location": [5820, 3675, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "34888307-f095-4c8b-afe0-e5f3c45c5a24", + "name": "Spawner (111)", + "location": [5820, 3675, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8add37de-2b09-4215-9568-e8586520f1ce", + "name": "Spawner (111)", + "location": [5820, 3675, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d89f6fd3-bc3e-4d39-a5b2-6d0b186d2a44", + "name": "Spawner (111)", + "location": [5820, 3775, 48], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e42b948-0bd7-4f93-8303-2d7723a1bb1d", + "name": "Spawner (111)", + "location": [5820, 3775, 48], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3bb22e15-16d5-4f29-b41a-051fa2a5bf56", + "name": "Spawner (111)", + "location": [5820, 3775, 48], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a34689d-a46f-4d99-a9d3-96eb175dcf83", + "name": "Spawner (111)", + "location": [5820, 3775, 48], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b7087528-b615-4aba-8544-e4904472600f", + "name": "Spawner (111)", + "location": [5818, 3876, 18], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec1caee7-0d96-49ac-8b1f-f759b03efdf8", + "name": "Spawner (111)", + "location": [5818, 3876, 18], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e23f7c11-dffc-4ba8-bae8-af7acae97635", + "name": "Spawner (111)", + "location": [5818, 3876, 18], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e889357-16df-4213-bc1e-e19a942f58ad", + "name": "Spawner (111)", + "location": [5818, 3876, 18], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a91547bb-7e0f-4518-92f8-dbaadb86bdfb", + "name": "Spawner (111)", + "location": [5819, 3974, 16], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9afd457-3945-403c-a0de-fb2b0ab73b8e", + "name": "Spawner (111)", + "location": [5819, 3974, 16], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e007014-f2b1-4454-8fc7-66065189e313", + "name": "Spawner (111)", + "location": [5819, 3974, 16], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c914773-48a0-4129-ae65-87ee6707e4f7", + "name": "Spawner (111)", + "location": [5819, 3974, 16], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b614a58d-1a96-4cc1-b136-30bc2ee50291", + "name": "Spawner (111)", + "location": [5721, 3675, 22], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8fce4e75-131b-4255-b270-41bcaf47d4b4", + "name": "Spawner (111)", + "location": [5721, 3675, 22], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db56d1fb-cd8c-4a7d-a029-1170f3bd78dc", + "name": "Spawner (111)", + "location": [5721, 3675, 22], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50ccc29d-4ee3-4606-b7e2-c03156afb9ba", + "name": "Spawner (111)", + "location": [5721, 3675, 22], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa8905ce-91db-4d17-92c1-ae5b04738dea", + "name": "Spawner (111)", + "location": [5722, 3776, 32], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79f39847-4344-4381-8b37-8eaa8c2ffef7", + "name": "Spawner (111)", + "location": [5722, 3776, 32], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6e6ba9f1-8c4c-4142-b251-141d2ea7a7f0", + "name": "Spawner (111)", + "location": [5722, 3776, 32], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54dcd3be-256f-48ca-900d-f585ae0a721a", + "name": "Spawner (111)", + "location": [5722, 3776, 32], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1524f465-d2dd-4315-b98f-e54e03000f27", + "name": "Spawner (111)", + "location": [5721, 3876, -3], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bb402f0-2399-410d-ac65-eed098e522ca", + "name": "Spawner (111)", + "location": [5721, 3876, -3], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "318ff931-f450-4626-b97f-13af5c0e41e3", + "name": "Spawner (111)", + "location": [5721, 3876, -3], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65753d38-25f4-4282-9dab-3fa55cba90f4", + "name": "Spawner (111)", + "location": [5721, 3876, -3], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fdde56d8-75b8-4488-8f25-3768fe5b8b14", "name": "Spawner (111)", "location": [5721, 3975, 21], @@ -3430,21 +2181,1250 @@ ] }, { - "$type": "Spawner", - "guid": "fe198cdf-57f9-40d8-8878-de34a8c84298", + "type": "Spawner", + "guid": "ad3477fd-221f-4a59-ba0d-e120e09a9146", "name": "Spawner (111)", - "location": [5212, 3619, 0], + "location": [5721, 3975, 21], "map": "Trammel", - "count": 15, + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e64232f7-0ccb-4c81-9a36-0c96fdd8e00a", + "name": "Spawner (111)", + "location": [5721, 3975, 21], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4306a87f-e65c-43ba-96d2-276065502e18", + "name": "Spawner (111)", + "location": [5721, 3975, 21], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78272d11-7b2e-4e49-b533-9f82138558d1", + "name": "Spawner (111)", + "location": [5623, 3676, -3], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd4ffdc9-a292-4a4b-a765-1f3a1ec0e9c4", + "name": "Spawner (111)", + "location": [5623, 3676, -3], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c07ac1f6-f0bd-483f-ac86-db1e8cc1914f", + "name": "Spawner (111)", + "location": [5623, 3676, -3], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "86e1ffa4-247e-40e8-9b24-069df5468fc4", + "name": "Spawner (111)", + "location": [5623, 3676, -3], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b655971-59b1-4016-b9d2-c2d4c2a11e50", + "name": "Spawner (111)", + "location": [5621, 3775, 2], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce64bca0-53f1-4f9a-a4ff-2da36a7bdcb6", + "name": "Spawner (111)", + "location": [5621, 3775, 2], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "53071c27-4eb5-4101-b20f-0008810bf616", + "name": "Spawner (111)", + "location": [5621, 3775, 2], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d055b36c-22ea-42c2-b862-493a9a0ba34e", + "name": "Spawner (111)", + "location": [5621, 3775, 2], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "896be635-414c-4750-8e26-742fea9d795c", + "name": "Spawner (111)", + "location": [5623, 3875, 15], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "036ad518-7a5a-4805-9d06-6deb52839f4f", + "name": "Spawner (111)", + "location": [5623, 3875, 15], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68e6f042-92f2-45a9-ae8d-0752b3b4b944", + "name": "Spawner (111)", + "location": [5623, 3875, 15], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6087c54a-11de-4326-8ec5-cd5b5cde47a4", + "name": "Spawner (111)", + "location": [5623, 3875, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c339abe3-345c-4881-aa87-f0c360517450", + "name": "Spawner (111)", + "location": [5624, 3975, -3], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0eeef349-4bf3-414a-8a66-b0a1de50f17c", + "name": "Spawner (111)", + "location": [5624, 3975, -3], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5812b2ff-56e7-47ee-937f-040e60498ca1", + "name": "Spawner (111)", + "location": [5624, 3975, -3], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7181d30e-02d5-4a40-9d4a-d856732d05e9", + "name": "Spawner (111)", + "location": [5624, 3975, -3], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a5e4485-6df8-4fae-9288-90ac1e3a871d", + "name": "Spawner (111)", + "location": [5523, 3676, 45], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0213ce1-2ab9-4aa0-899c-8a9c5aed26f7", + "name": "Spawner (111)", + "location": [5523, 3676, 45], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09c49d83-9516-43f4-b947-545a44f71516", + "name": "Spawner (111)", + "location": [5523, 3676, 45], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "227477e6-a891-4b3e-b43b-305c6b6541f4", + "name": "Spawner (111)", + "location": [5523, 3676, 45], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79cc7028-11d1-49df-b0a5-75066e5a6559", + "name": "Spawner (111)", + "location": [5521, 3775, 8], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df38e40e-4233-447c-9faa-0eda376e975d", + "name": "Spawner (111)", + "location": [5521, 3775, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7aa1e207-3ea8-4aca-b0dc-a0a585466eed", + "name": "Spawner (111)", + "location": [5521, 3775, 8], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbd8cc70-dbcc-477c-bbe3-9865e30453da", + "name": "Spawner (111)", + "location": [5521, 3775, 8], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "832c6d54-6e88-4e70-ba0f-9731b9ecc3cc", + "name": "Spawner (111)", + "location": [5521, 3875, -1], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "162b114a-bf25-4054-8124-1ed834e586b1", + "name": "Spawner (111)", + "location": [5521, 3875, -1], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a43ee478-1a1b-4c04-85b9-0c9fe436e7db", + "name": "Spawner (111)", + "location": [5521, 3875, -1], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea9eae49-ae57-49b2-8a41-c956d8e8aafe", + "name": "Spawner (111)", + "location": [5521, 3875, -1], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94b422df-ea7d-4c7f-816f-1f89ad5c9e21", + "name": "Spawner (111)", + "location": [5478, 3587, 3], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7dca1dbd-9eb3-4a64-b276-02d6205baf2a", + "name": "Spawner (111)", + "location": [5478, 3587, 3], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "917b5953-f939-46c0-863e-17a1ca293dd8", + "name": "Spawner (111)", + "location": [5478, 3587, 3], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4ea0559-6a74-406b-a53a-aa0aa9338d4d", + "name": "Spawner (111)", + "location": [5478, 3587, 3], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1308141-2a7f-45ea-8514-38453ba30742", + "name": "Spawner (111)", + "location": [5520, 3939, 42], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "57a3231a-5b0e-45dd-bcfc-3340878eb9cf", + "name": "Spawner (111)", + "location": [5784, 3951, 20], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [{ "name": "FrenziedOstard", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b7df3e65-2706-4ad9-89e4-20e50c6f83ba", + "name": "Spawner (111)", + "location": [5764, 2585, -4], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 75, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07bf9106-e8c2-4f1c-8d4b-1344e90624a3", + "name": "Spawner (111)", + "location": [5838, 2651, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 75, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8bfa4c3d-f412-4707-a48f-e358295e31e1", + "name": "Spawner (111)", + "location": [5218, 3786, 1], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1315e61d-169b-47e9-9281-395b54cd4fbb", + "name": "Spawner (111)", + "location": [5218, 3786, 1], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "caf23365-239f-47ea-a5df-e469a64d10f9", + "name": "Spawner (111)", + "location": [5371, 3820, -2], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db7d0ea8-885d-460c-95dc-08a5870b6b32", + "name": "Spawner (111)", + "location": [5371, 3820, -2], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f77b4431-b6bf-47bc-9353-1ed0fa25dc0e", + "name": "Spawner (111)", + "location": [5368, 3887, 0], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "723998f0-08d5-4124-84d3-df5401888a32", + "name": "Spawner (111)", + "location": [5368, 3887, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d5989a1c-98df-44fd-8c77-1fecf310a3d2", + "name": "Spawner (111)", + "location": [5367, 4003, 19], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abe4fbf3-2c4d-4e8d-be6c-d1fda9d7bab6", + "name": "Spawner (111)", + "location": [5367, 4003, 19], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46191d14-a2b4-4995-b761-4d91195932b0", + "name": "Spawner (111)", + "location": [5466, 4005, -1], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e58b638c-9203-4d48-925a-0129f612fae4", + "name": "Spawner (111)", + "location": [5466, 4005, -1], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e2ede8e-0dab-4f8e-b6c2-e84626c0acb3", + "name": "Spawner (111)", + "location": [5194, 3885, -1], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 32, + "walkingRange": 45, + "entries": [ + { "name": "Bull", "maxCount": 10, "probability": 100 }, + { "name": "GreatHart", "maxCount": 6, "probability": 100 }, + { "name": "Cow", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd535de1-dc4b-46a9-9018-feedf4c80edb", + "name": "Spawner (111)", + "location": [5200, 3895, 2], + "map": "Trammel", + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 15, + "walkingRange": 20, "entries": [ - { "name": "SavageRider", "maxCount": 3, "probability": 100 }, - { "name": "Savage", "maxCount": 9, "probability": 100 }, - { "name": "SavageShaman", "maxCount": 3, "probability": 100 } + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f64b0f6-5882-4551-9a27-ca031288fbae", + "name": "Spawner (111)", + "location": [5159, 3913, 1], + "map": "Trammel", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 10, + "entries": [ + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 1, "probability": 100 }, + { "name": "Bull", "maxCount": 1, "probability": 100 }, + { "name": "Chicken", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8df84079-fd67-48ff-85d2-c2aab5b08d89", + "name": "Spawner (111)", + "location": [5570, 3153, 14], + "map": "Trammel", + "count": 25, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 80, + "entries": [ + { "name": "Gorilla", "maxCount": 6, "probability": 100 }, + { "name": "Panther", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8d068d0-cdb5-4976-a3af-bff4443a1e0c", + "name": "Spawner (111)", + "location": [5440, 3292, -4], + "map": "Trammel", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 56, + "walkingRange": 62, + "entries": [ + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "Panther", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b7e25a1-2cca-4a59-823e-ec421274efc1", + "name": "Spawner (111)", + "location": [5566, 3292, 4], + "map": "Trammel", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 56, + "walkingRange": 62, + "entries": [ + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "Panther", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b4617e7-da4a-42b4-9f4b-7d2e5e1fad7f", + "name": "Spawner (111)", + "location": [5454, 3136, -60], + "map": "Trammel", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "TerathanMatriarch", "maxCount": 2, "probability": 100 }, + { "name": "TerathanAvenger", "maxCount": 4, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 10, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a02dc119-74bf-4864-92ab-94c29aefc6be", + "name": "Spawner (111)", + "location": [5490, 3071, 15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c6882bff-77fb-4555-8369-c948185411a3", + "name": "Spawner (111)", + "location": [5467, 3078, -3], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b606b627-c425-445c-9bc1-0e8e0a502c0d", + "name": "Spawner (111)", + "location": [5446, 3074, 3], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40332222-33e9-4df1-bf72-15f818f2c5f2", + "name": "Spawner (111)", + "location": [5432, 3079, -4], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e44775de-291a-43dd-9103-11c3a2063c7c", + "name": "Spawner (111)", + "location": [5408, 3077, 16], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/OrcCaves.json b/Distribution/Data/Spawns/post-uoml/trammel/OrcCaves.json similarity index 84% rename from Distribution/Data/Spawns/shared/trammel/OrcCaves.json rename to Distribution/Data/Spawns/post-uoml/trammel/OrcCaves.json index dec261b24..c35101c09 100644 --- a/Distribution/Data/Spawns/shared/trammel/OrcCaves.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/OrcCaves.json @@ -1,6 +1,153 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "cd4b9397-58f2-42bf-ac91-033ad8396eda", + "name": "Spawner (112)", + "location": [5146, 1995, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "Orc", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04dd6b85-c84e-49e1-8e6a-fbea6734f30b", + "name": "Spawner (112)", + "location": [5153, 1971, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "264b83e9-092d-4d4b-9dac-8f717b8a21a6", + "name": "Spawner (112)", + "location": [5153, 1961, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "DireWolf", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4abbaeaf-4d90-4eef-bca1-55bbc1b0b631", + "name": "Spawner (112)", + "location": [5144, 1960, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "OrcishLord", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "20658391-8218-48bd-bdab-fd04bbafad31", + "name": "Spawner (112)", + "location": [5141, 1968, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "OrcCaptain", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c343a444-ed8b-4bc7-9c5f-4ac06bcb18f8", + "name": "Spawner (112)", + "location": [5332, 1365, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "17e3d0fd-73d1-435c-9d32-ea6716977e80", + "name": "Spawner (112)", + "location": [5308, 1372, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Noble", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "48b68bf1-d9b1-4fac-af9e-9c51c90fe891", + "name": "Spawner (112)", + "location": [5302, 1355, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 12, + "entries": [ + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f128f5b2-ca75-465f-87b2-d5bbf8c933cf", + "name": "Spawner (112)", + "location": [5316, 1332, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "GiantRat", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "02f05175-47b3-49ce-adfd-6838f0bb93a7", + "name": "Spawner (112)", + "location": [5331, 1346, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "DireWolf", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "0076bc53-87a0-493c-9251-8b689c0aac25", "name": "Spawner (112)", "location": [5354, 1332, 0], @@ -19,171 +166,25 @@ ] }, { - "$type": "Spawner", - "guid": "02f05175-47b3-49ce-adfd-6838f0bb93a7", + "type": "Spawner", + "guid": "ebff4db1-6752-4457-b629-109818ec0294", "name": "Spawner (112)", - "location": [5331, 1346, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "DireWolf", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "03496f57-4e3d-4a83-a12c-8ee3523f4efb", - "name": "Spawner (112)", - "location": [5348, 2011, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "04dd6b85-c84e-49e1-8e6a-fbea6734f30b", - "name": "Spawner (112)", - "location": [5153, 1971, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Orc", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "17e3d0fd-73d1-435c-9d32-ea6716977e80", - "name": "Spawner (112)", - "location": [5308, 1372, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Noble", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20658391-8218-48bd-bdab-fd04bbafad31", - "name": "Spawner (112)", - "location": [5141, 1968, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "OrcCaptain", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "264b83e9-092d-4d4b-9dac-8f717b8a21a6", - "name": "Spawner (112)", - "location": [5153, 1961, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "DireWolf", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "434454b5-2093-4125-8b2c-59c57d6bd98b", - "name": "Spawner (112)", - "location": [5292, 1316, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Corpser", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "48b68bf1-d9b1-4fac-af9e-9c51c90fe891", - "name": "Spawner (112)", - "location": [5302, 1355, 0], + "location": [5356, 1304, 0], "map": "Trammel", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 8, - "walkingRange": 12, - "entries": [ - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4abbaeaf-4d90-4eef-bca1-55bbc1b0b631", - "name": "Spawner (112)", - "location": [5144, 1960, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "OrcishLord", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "646f189b-b834-4e7d-9e96-dc8b5d71412d", - "name": "Spawner (112)", - "location": [5300, 1314, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, "walkingRange": 10, "entries": [ - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 2, "probability": 100 } + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6d6544ae-63b4-4c8b-8be3-7b52b505f7f9", "name": "Spawner (112)", "location": [5317, 1309, 0], @@ -202,7 +203,53 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "646f189b-b834-4e7d-9e96-dc8b5d71412d", + "name": "Spawner (112)", + "location": [5300, 1314, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "434454b5-2093-4125-8b2c-59c57d6bd98b", + "name": "Spawner (112)", + "location": [5292, 1316, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Corpser", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "def6d26d-288a-4659-9ccc-b7556ba93dfe", + "name": "Spawner (112)", + "location": [5281, 2029, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "c13bf083-3691-486a-b5af-7779a398b1f8", "name": "Spawner (112)", "location": [5308, 2005, 0], @@ -219,91 +266,7 @@ ] }, { - "$type": "Spawner", - "guid": "c343a444-ed8b-4bc7-9c5f-4ac06bcb18f8", - "name": "Spawner (112)", - "location": [5332, 1365, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd4b9397-58f2-42bf-ac91-033ad8396eda", - "name": "Spawner (112)", - "location": [5146, 1995, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Orc", "maxCount": 1, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "def6d26d-288a-4659-9ccc-b7556ba93dfe", - "name": "Spawner (112)", - "location": [5281, 2029, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ebff4db1-6752-4457-b629-109818ec0294", - "name": "Spawner (112)", - "location": [5356, 1304, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 10, - "entries": [ - { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f128f5b2-ca75-465f-87b2-d5bbf8c933cf", - "name": "Spawner (112)", - "location": [5316, 1332, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "GiantRat", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f769344d-9b4e-4573-b61a-5d1e4d66361d", "name": "Spawner (112)", "location": [5310, 1969, 0], @@ -318,5 +281,22 @@ { "name": "OrcBomber", "maxCount": 2, "probability": 100 }, { "name": "Orc", "maxCount": 2, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "03496f57-4e3d-4a83-a12c-8ee3523f4efb", + "name": "Spawner (112)", + "location": [5348, 2011, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 4, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/post-uoml/trammel/Outdoors.json b/Distribution/Data/Spawns/post-uoml/trammel/Outdoors.json index aa077d9ab..39c77d4ff 100644 --- a/Distribution/Data/Spawns/post-uoml/trammel/Outdoors.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Outdoors.json @@ -1,6 +1,8563 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "62ff4954-fb25-4416-8ac1-3120144c4c14", + "name": "Spawner (113)", + "location": [1147, 3524, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2c12823-bb40-49bb-93ab-87fec1ebda5f", + "name": "Spawner (113)", + "location": [1266, 1982, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "566cfa8d-c4be-4e84-9779-19fd29a5c4af", + "name": "Spawner (113)", + "location": [1249, 2030, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6092a82-aeba-47d2-81a2-e51332742aa4", + "name": "Spawner (113)", + "location": [1232, 2093, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95bd45a8-6d9d-4663-a009-90bbfdb6c783", + "name": "Spawner (113)", + "location": [1140, 2087, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fc7f9d6e-d0ce-4879-b674-ba5ed365450a", + "name": "Spawner (113)", + "location": [1089, 2033, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cf631a13-97b5-4714-87db-23ad2e1a5ed4", + "name": "Spawner (113)", + "location": [1067, 2117, 5], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2b03adb-c492-487f-bdc1-4dc7f70818cd", + "name": "Spawner (113)", + "location": [666, 1825, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75118095-d436-46b3-8e73-d2cc56c66ce9", + "name": "Spawner (113)", + "location": [741, 1838, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba037fbd-9398-4438-bee8-e6fce7d07c01", + "name": "Spawner (113)", + "location": [733, 1937, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ee147b0d-5a5f-4ad0-9d7a-974230bb3f53", + "name": "Spawner (113)", + "location": [798, 1935, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da8f61e2-5aed-4bb9-a59e-c3c01cf2f4da", + "name": "Spawner (113)", + "location": [819, 1841, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0042c352-ea0e-4d42-860c-5f3462b43c61", + "name": "Spawner (113)", + "location": [893, 1861, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb7f5bad-4b07-4c56-bd44-8fb33a088fa5", + "name": "Spawner (113)", + "location": [879, 1956, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a99fcecd-571e-4151-8c6d-b3d0611e4f5a", + "name": "Spawner (113)", + "location": [809, 1615, 0], + "map": "Trammel", + "count": 6, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7abf83f6-4989-40d9-8b25-ee6299f59786", + "name": "Spawner (113)", + "location": [4461, 3106, 0], + "map": "Trammel", + "count": 5, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "OrcishMage", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3e3414ca-ff05-479b-ab44-3dd7f6c46470", + "name": "Spawner (113)", + "location": [4729, 3485, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12e72748-84ff-4d28-bfbe-39b7d5232ceb", + "name": "Spawner (113)", + "location": [4519, 3097, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2aa2c25-88b6-44d5-af14-e47c7b2a798a", + "name": "Spawner (113)", + "location": [4613, 3272, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f62d079f-93a0-412b-a5a2-f088592be535", + "name": "Spawner (113)", + "location": [4576, 3356, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "792fd336-4cdb-41aa-91f2-ea5a748bf4f3", + "name": "Spawner (113)", + "location": [4658, 3376, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3366bb0e-f7cf-475b-968a-25da2580d36b", + "name": "Spawner (113)", + "location": [4607, 3480, 29], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9b819902-07ef-442c-bff5-d8201a95eb92", + "name": "Spawner (113)", + "location": [3168, 629, 8], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e72e6b3-e644-4850-8a8d-b5586ac9cf55", + "name": "Spawner (113)", + "location": [3172, 700, 8], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "583032d0-fa06-4e3c-ba13-1054ca5c022f", + "name": "Spawner (113)", + "location": [3173, 771, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3cfc616f-5f47-49de-88e6-0aea4dcd0fa6", + "name": "Spawner (113)", + "location": [1473, 229, 22], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9956e808-d58e-42bd-8c6d-3a22c652480d", + "name": "Spawner (113)", + "location": [1337, 293, 22], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a94c7436-52ec-4820-9c38-88fcada332a9", + "name": "Spawner (113)", + "location": [1184, 217, 27], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2ad3f8c-d318-42b4-8a19-20a025ee838d", + "name": "Spawner (113)", + "location": [1284, 433, 6], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f8ff142-0e90-4ee2-bb29-c6960d4dc308", + "name": "Spawner (113)", + "location": [1203, 502, 30], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a9bdd33d-5beb-4e5f-b2a7-25bbe6de9194", + "name": "Spawner (113)", + "location": [1300, 607, 15], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d14a61d-ea19-473b-8d74-b57ac8fd7f89", + "name": "Spawner (113)", + "location": [1200, 633, 6], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4cb1e2e1-c235-4ea9-ac7a-86497d31fe93", + "name": "Spawner (113)", + "location": [1162, 704, 6], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8791d5da-aabf-4ef8-9e50-bd73ebf233d3", + "name": "Spawner (113)", + "location": [1130, 830, 9], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b5a9736-d1d3-4492-9674-e786e8ea6b55", + "name": "Spawner (113)", + "location": [1013, 980, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbc84d48-e44c-404a-9912-3f08fd80c2a8", + "name": "Spawner (113)", + "location": [848, 917, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62abe17a-ffc3-43f4-b71d-281ff6e875bb", + "name": "Spawner (113)", + "location": [840, 1063, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "97faf684-9350-46c3-8da6-c3892f7f604d", + "name": "Spawner (113)", + "location": [1038, 1149, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b518dc40-be46-4410-9296-164bc7f5d6db", + "name": "Spawner (113)", + "location": [844, 1235, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c9f16f2-a241-42ad-bf26-7fed6e7b0693", + "name": "Spawner (113)", + "location": [617, 1326, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a71a8ce9-b6a6-40b5-ba55-ae04b8940322", + "name": "Spawner (113)", + "location": [1831, 449, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0bc4302-a584-4339-b0b4-ebd374568aa7", + "name": "Spawner (113)", + "location": [1832, 517, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "51ec0cce-de67-476d-abb7-7882f5add66e", + "name": "Spawner (113)", + "location": [1749, 474, 6], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "16d441e6-2418-4ffb-97c8-6c1b05f6d1a4", + "name": "Spawner (113)", + "location": [1636, 532, 22], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6901fe1c-8be5-4cba-949a-8401a0cb640d", + "name": "Spawner (113)", + "location": [1743, 589, 16], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1af8afa4-3937-4745-9a39-4fb67f03f640", + "name": "Spawner (113)", + "location": [1887, 591, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75631a7a-9d4e-4464-9197-1e2ce34369cd", + "name": "Spawner (113)", + "location": [1810, 647, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32e86bfa-a1f9-4b2e-bf35-9c83335710a9", + "name": "Spawner (113)", + "location": [1631, 650, 16], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61e4635f-38a1-4bed-9d85-40665f917751", + "name": "Spawner (113)", + "location": [1723, 683, 6], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d3feafc-3dc2-441d-a9ab-18c468552742", + "name": "Spawner (113)", + "location": [1980, 832, -1], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9e6dac6-5887-4a6e-a321-7166ad16b575", + "name": "Spawner (113)", + "location": [1905, 883, -1], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7ea4508-795e-4bb9-97c3-41fe28f1f214", + "name": "Spawner (113)", + "location": [1849, 953, 7], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9fd2dbc-cb36-489c-8149-b815c16f8d09", + "name": "Spawner (113)", + "location": [1001, 787, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c710dd46-a5f7-4b86-8a96-a787c08e03cb", + "name": "Spawner (113)", + "location": [987, 725, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47544599-de5d-4da5-9fec-857b970a1ce5", + "name": "Spawner (113)", + "location": [1912, 1283, 0], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "38e273a0-f54c-4f47-a468-379b40ac1fe2", + "name": "Spawner (113)", + "location": [1787, 1280, 0], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54106890-a301-41d2-99ed-475fa5ebce54", + "name": "Spawner (113)", + "location": [1799, 1394, 0], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ba259c9-3aba-4651-a0f8-9fb5ad4c203b", + "name": "Spawner (113)", + "location": [1652, 1400, 0], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbd652ce-e378-4c20-84c3-42788aac59b2", + "name": "Spawner (113)", + "location": [1755, 1474, 0], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d8ac8b6-9e7c-42d8-8dc5-bc38d61e535b", + "name": "Spawner (113)", + "location": [1325, 1071, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Troll", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "28aa4986-a824-4960-b415-73d72a3c2c6b", + "name": "Spawner (113)", + "location": [2369, 3430, 3], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e085923c-489c-4b7b-b9bf-c6a4ca1b60c1", + "name": "Spawner (113)", + "location": [2354, 3503, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2df0361e-29e1-454f-970b-3e153ff01794", + "name": "Spawner (113)", + "location": [2503, 3593, 3], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "402644f1-43d6-4f2a-ba97-7f86c9010bed", + "name": "Spawner (113)", + "location": [2520, 3983, 3], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "056b2cc1-56cd-45e3-bae8-a8749a1ddc06", + "name": "Spawner (113)", + "location": [2438, 3914, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0758b1c-baa8-4216-989d-000515e5227d", + "name": "Spawner (113)", + "location": [2143, 3953, 3], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad938586-87a9-4dd7-a5b1-59cd95cd6d46", + "name": "Spawner (113)", + "location": [868, 488, 0], + "map": "Trammel", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72083877-79f3-49cf-a915-5ffb89bf1e48", + "name": "Spawner (113)", + "location": [805, 483, 0], + "map": "Trammel", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "066c7e0c-e833-422b-9eab-6ce558dba618", + "name": "Spawner (113)", + "location": [839, 406, 0], + "map": "Trammel", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a546dd6-47b1-48e5-ba06-a2c594caa9b2", + "name": "Spawner (113)", + "location": [841, 344, 0], + "map": "Trammel", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6cc36c7c-cfd0-42f4-9e04-9ff5dbd963c5", + "name": "Spawner (113)", + "location": [1131, 3415, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf514878-4b14-4e01-acd6-ccff87fe9980", + "name": "Spawner (113)", + "location": [1158, 3457, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "538691b1-1499-4081-96bd-2c2dd54a4599", + "name": "Spawner (113)", + "location": [1094, 3482, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1b3fe9d8-e194-42e9-8316-aa67a21b887d", + "name": "Spawner (113)", + "location": [1312, 613, 10], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "837610d0-3ae0-49e0-aa44-a02a4a3e379f", + "name": "Spawner (113)", + "location": [1214, 617, 13], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a48df719-c919-4066-b8a2-6c679032f463", + "name": "Spawner (113)", + "location": [1361, 522, 19], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47abac64-ba65-4d28-8d46-a2b9c6872407", + "name": "Spawner (113)", + "location": [1226, 449, 1], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "efe392b2-baea-4098-9549-0439983f0e4e", + "name": "Spawner (113)", + "location": [1276, 431, 16], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4e4aa09e-a831-4f56-b374-8d39f3977e30", + "name": "Spawner (113)", + "location": [1231, 192, 6], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75b4dbbf-f0d3-4f4e-8148-520aba12abaa", + "name": "Spawner (113)", + "location": [1143, 180, 14], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d6e79e4-c6c2-456c-9bf3-629536077417", + "name": "Spawner (113)", + "location": [1191, 288, 11], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73e77785-b460-4d5d-840c-b13c96c1ae54", + "name": "Spawner (113)", + "location": [1295, 328, 22], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "afde0aad-5648-43b2-b77b-08270970f7b5", + "name": "Spawner (113)", + "location": [1361, 275, 16], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55959a0a-7dac-4bea-9689-01d82dcb1212", + "name": "Spawner (113)", + "location": [1491, 238, 22], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc56ca14-97aa-4dc3-99a3-f684078e007e", + "name": "Spawner (113)", + "location": [3440, 405, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c79e8b6-595f-4914-9372-be6a16e42bf8", + "name": "Spawner (113)", + "location": [3382, 375, 4], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39d7b2f6-d423-4d6c-8068-985bbd4865ef", + "name": "Spawner (113)", + "location": [3327, 344, 4], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ab664229-d260-4bfd-9948-f2bb2b433504", + "name": "Spawner (113)", + "location": [3434, 316, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6aab8758-a4ee-4a8a-bc76-33cdbdd297c0", + "name": "Spawner (113)", + "location": [3382, 280, 9], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "688f208d-8821-4c70-9b98-28f8594741bd", + "name": "Spawner (113)", + "location": [3322, 271, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e9214fe-1512-458b-8302-1820c027c565", + "name": "Spawner (113)", + "location": [3360, 218, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6987ffa4-6e58-48b7-a2a4-12d2eef6b86d", + "name": "Spawner (113)", + "location": [3390, 259, 4], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "198d8d75-87a7-4c77-abc3-c45fb558825b", + "name": "Spawner (113)", + "location": [3446, 213, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54e24a3b-cd43-4265-9f30-d73326dd45a9", + "name": "Spawner (113)", + "location": [3462, 136, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b84a2533-eb93-46ba-8053-dc0c04146704", + "name": "Spawner (113)", + "location": [2589, 204, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "457024f7-fdc7-4080-858e-b578a99cc7d5", + "name": "Spawner (113)", + "location": [2549, 136, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa7aa472-6b9e-4cc5-b138-720d5d413925", + "name": "Spawner (113)", + "location": [2659, 147, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c7570b9-067e-4887-8309-b653be55bd69", + "name": "Spawner (113)", + "location": [2621, 99, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2acf5f1c-d200-4621-9cbf-9d013a9e553b", + "name": "Spawner (113)", + "location": [2549, 86, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "543e6d36-efb0-4382-b526-b615fd7d0cbf", + "name": "Spawner (113)", + "location": [2478, 100, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4feab07-a874-4ac6-a248-7a292a021674", + "name": "Spawner (113)", + "location": [2468, 144, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10f682e2-a5bd-42f8-b228-ae66d1a9a4c5", + "name": "Spawner (113)", + "location": [2485, 191, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "748028eb-c0ef-4c6f-bbb9-e43359901099", + "name": "Spawner (113)", + "location": [2454, 248, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0efa850b-b21d-4835-a4ff-08015747427a", + "name": "Spawner (113)", + "location": [2533, 240, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37b5c086-ef17-49b8-ad0b-125ec80df443", + "name": "Spawner (113)", + "location": [2077, 434, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1c9e3733-5669-4f75-85fb-0d024e01cdb5", + "name": "Spawner (113)", + "location": [2080, 400, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e4c86bb-8737-41c8-924c-f226b9aedfde", + "name": "Spawner (113)", + "location": [2107, 358, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d3e5300-d499-4a9b-b4e0-f6daa25ac14b", + "name": "Spawner (113)", + "location": [2138, 323, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5436a84f-0593-4be2-bead-26e7960898a7", + "name": "Spawner (113)", + "location": [2189, 298, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e52d805-c99f-49b2-8a7a-6c5e4e33038a", + "name": "Spawner (113)", + "location": [2137, 271, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f05dbd6-1d96-4f1c-ad4e-7808de1dcbe0", + "name": "Spawner (113)", + "location": [2113, 221, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ea552d7-eb71-4a77-9508-fd3e69b4870a", + "name": "Spawner (113)", + "location": [2146, 178, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f66d1ea-1745-45de-bbd3-bbf8032b53bc", + "name": "Spawner (113)", + "location": [2093, 147, 3], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28cd7bd8-2a62-421d-8a48-3409ae908a33", + "name": "Spawner (113)", + "location": [2109, 109, 3], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3cc3559-e015-4904-b37e-d464bcd6d7ef", + "name": "Spawner (113)", + "location": [2115, 48, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d739d7a9-b14c-49ed-ad4d-6765d520bec5", + "name": "Spawner (113)", + "location": [1996, 73, 8], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc87ef42-d3a3-4a6b-8d44-ac0d38d75739", + "name": "Spawner (113)", + "location": [1956, 48, 4], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75764c8f-2910-43c9-9fde-d03748e5361e", + "name": "Spawner (113)", + "location": [1944, 102, 4], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "387b8a0f-f657-418e-a8a4-0713e3bf18bb", + "name": "Spawner (113)", + "location": [2034, 46, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41cb561e-ed07-4766-a993-ea37d3480d8b", + "name": "Spawner (113)", + "location": [2033, 74, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "249b097d-dd14-4d6b-9ddf-f29eea89a011", + "name": "Spawner (113)", + "location": [2058, 87, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61b07a04-4d35-4af2-9fef-2575e7aa81f6", + "name": "Spawner (113)", + "location": [2034, 108, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bed7e28-21ce-4afc-b05e-ab91bc2a9b0a", + "name": "Spawner (113)", + "location": [2000, 141, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40d4b2f8-9cb0-4b8f-b46e-d460c0aba610", + "name": "Spawner (113)", + "location": [1965, 160, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "847e76b7-5a08-438c-80a3-56285b0f4696", + "name": "Spawner (113)", + "location": [1946, 186, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b2274f08-2f99-4bed-b485-e6dd3228564f", + "name": "Spawner (113)", + "location": [1922, 173, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "056a5d12-e5e6-4906-9fe1-5e8fb605cfb7", + "name": "Spawner (113)", + "location": [1894, 197, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0eab19f2-96ae-41b9-a0d7-5dc82506af6a", + "name": "Spawner (113)", + "location": [1908, 214, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "483c1680-2f1b-4e6f-9d15-2665c8ac264f", + "name": "Spawner (113)", + "location": [1891, 226, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4cc2ab2c-5acf-4063-92e1-db91e44a9870", + "name": "Spawner (113)", + "location": [1869, 265, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91340fd5-c54d-4502-9253-adaf4569c7a3", + "name": "Spawner (113)", + "location": [1626, 266, 16], + "map": "Trammel", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c76cdb5d-42cb-4ab4-a7ec-6db8582f8f14", + "name": "Spawner (113)", + "location": [1682, 252, 6], + "map": "Trammel", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebbc9fcf-7284-4d3c-9d01-045bf8f31a5d", + "name": "Spawner (113)", + "location": [1723, 252, 16], + "map": "Trammel", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6eefb00-5655-4f27-9d1b-9baa28ab2ffc", + "name": "Spawner (113)", + "location": [1759, 259, 21], + "map": "Trammel", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd5e98b7-cddd-4087-b1f4-c28350797537", + "name": "Spawner (113)", + "location": [1804, 247, 0], + "map": "Trammel", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03e8ed27-82da-4ecf-8d02-126f4b8bc35d", + "name": "Spawner (113)", + "location": [1875, 906, 7], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ab5fd7a4-8a79-4ee4-8369-b9ed647a5fe6", + "name": "Spawner (113)", + "location": [1829, 874, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b950c826-32fc-4800-92c3-04292c5ab38b", + "name": "Spawner (113)", + "location": [1852, 841, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8c9dac4-eb05-4d17-9741-c566db9f045f", + "name": "Spawner (113)", + "location": [1930, 864, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4cc9345c-fe2a-47b7-ab4c-bf9097b047d6", + "name": "Spawner (113)", + "location": [1883, 827, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fd0a082-52c5-460c-b284-52893bddcc32", + "name": "Spawner (113)", + "location": [1949, 824, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9737161-8220-4189-ade1-d48a07a04d7a", + "name": "Spawner (113)", + "location": [1987, 822, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2bac5389-5a5b-45a2-bcad-03e258bcbf5c", + "name": "Spawner (113)", + "location": [2013, 839, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a559cad1-a142-4969-bef2-8e154d658ec6", + "name": "Spawner (113)", + "location": [1979, 885, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "11dcec12-bab6-4595-aa20-ef64eb749c0e", + "name": "Spawner (113)", + "location": [1947, 912, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bb0725a-0be8-4432-9256-9d4d41d7c168", + "name": "Spawner (113)", + "location": [1915, 928, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba9ff3a4-d363-4fac-b113-bab612b9614f", + "name": "Spawner (113)", + "location": [1866, 938, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0ed841ed-3e0c-4c9a-aef2-1952720a260b", + "name": "Spawner (113)", + "location": [1838, 957, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61a0bee6-034f-4e72-851a-0069ea03c769", + "name": "Spawner (113)", + "location": [1802, 1506, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7bf87e5-4f5b-4b8c-9710-4e4ab3343720", + "name": "Spawner (113)", + "location": [1692, 1463, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4eedbefe-4446-443f-a5be-6b1c56c842cb", + "name": "Spawner (113)", + "location": [1833, 1245, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "30b6c30f-49a6-4cc7-ae52-78107281393a", + "name": "Spawner (113)", + "location": [1726, 1362, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "372bf2eb-9738-4465-8da3-64c980dfc01e", + "name": "Spawner (113)", + "location": [1640, 1431, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "80b26b9b-afc9-40a9-b6e7-47e04586711e", + "name": "Spawner (113)", + "location": [1912, 1302, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6ffb380-e7b3-4b1c-a89e-49a68626b151", + "name": "Spawner (113)", + "location": [1967, 1296, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebb84eb3-cfb1-4925-9e17-ea98ad5df79e", + "name": "Spawner (113)", + "location": [1898, 1265, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "17b36c45-5c86-4ede-8d59-8c9031e72a79", + "name": "Spawner (113)", + "location": [1872, 1347, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "476be46e-7393-4617-afc5-7d03ce670c16", + "name": "Spawner (113)", + "location": [1812, 1314, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1e6337c-a7e4-4011-a777-d5e32ab30ace", + "name": "Spawner (113)", + "location": [1797, 1393, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2aba6ad-8726-41c2-92bb-73f1176d0422", + "name": "Spawner (113)", + "location": [1862, 1434, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "17f64e17-61cb-4e36-aaa9-8f0f94218781", + "name": "Spawner (113)", + "location": [1877, 1515, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8103dd4b-9100-4c4b-bcb1-4b0b5bf054f8", + "name": "Spawner (113)", + "location": [1361, 1392, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9bfb3456-0e22-429d-bf41-8b597f0c8170", + "name": "Spawner (113)", + "location": [1445, 1365, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "467e3ecc-401c-48d9-b064-f260e6891b6f", + "name": "Spawner (113)", + "location": [1375, 1330, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c656301-ba27-4061-b13c-67e32ca73351", + "name": "Spawner (113)", + "location": [1474, 1297, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68c72e6d-a7fd-431c-8685-37f47afc4b0b", + "name": "Spawner (113)", + "location": [1407, 1281, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4bc9831-73ab-4b9b-a6b9-21a02bf60b8c", + "name": "Spawner (113)", + "location": [1225, 1477, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bb510ff-11c6-4ed6-88b2-28786db5e331", + "name": "Spawner (113)", + "location": [1222, 1418, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e2c18bc-c2c6-4a16-bda2-1d4bd4d5574d", + "name": "Spawner (113)", + "location": [1244, 1374, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c113386f-3b9c-40bf-a0bd-a45027fc8201", + "name": "Spawner (113)", + "location": [1180, 1354, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d80fc757-5a54-44c3-9700-ad91a1cee771", + "name": "Spawner (113)", + "location": [1240, 1317, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5b60719-c3a1-4169-8070-a3d98da16df3", + "name": "Spawner (113)", + "location": [1340, 1268, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "acf731fa-53ee-48da-b0ef-356f9d61c38f", + "name": "Spawner (113)", + "location": [1305, 1326, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08534eb5-4011-42d7-9006-169b3d002ca1", + "name": "Spawner (113)", + "location": [1304, 1413, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "83e06193-d371-4d06-93b6-9a4147fddee3", + "name": "Spawner (113)", + "location": [1300, 1481, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10e3df7f-ffc8-4bd7-a216-0689f4decabc", + "name": "Spawner (113)", + "location": [1541, 1895, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c00f933-5883-4c21-aac6-0e9cb9565b56", + "name": "Spawner (113)", + "location": [1534, 1941, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f430ad0e-4341-455b-9f4e-230f9f461670", + "name": "Spawner (113)", + "location": [1475, 1958, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0ada6680-a6f8-4468-a941-912dbef13276", + "name": "Spawner (113)", + "location": [1435, 1917, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c33edf4-3f59-4b73-bfee-9d57b2a4e7be", + "name": "Spawner (113)", + "location": [1383, 1949, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4245c498-a5a6-436c-91db-68989f925ce7", + "name": "Spawner (113)", + "location": [1416, 1992, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dc3563c-c729-4217-b57b-535caa81fdb2", + "name": "Spawner (113)", + "location": [1504, 2007, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "528890d0-376a-43c3-a832-69efcd0d56c1", + "name": "Spawner (113)", + "location": [1447, 2038, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ad1ac6d-989f-4590-8d0e-0b1894c5c216", + "name": "Spawner (113)", + "location": [1479, 2094, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "729cb568-ac02-4737-b9e7-4abd87c5f66a", + "name": "Spawner (113)", + "location": [1525, 2053, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ce82610-b4ea-41c1-abc5-e2bbce8dfa46", + "name": "Spawner (113)", + "location": [2095, 2352, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "668b691e-8bdc-4002-93b8-ccc8d3ef1882", + "name": "Spawner (113)", + "location": [2077, 2305, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1365e61d-0ffa-43f0-b926-45316a2a8048", + "name": "Spawner (113)", + "location": [2021, 2313, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8792576e-488e-47bc-a672-01aee138d62e", + "name": "Spawner (113)", + "location": [1968, 2346, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d941b3f-588d-49fe-a879-d50c6005b8c9", + "name": "Spawner (113)", + "location": [1947, 2294, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f8dcb56-8c3f-49fd-b778-75d243d770c6", + "name": "Spawner (113)", + "location": [1915, 2385, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6cf68fd8-cc90-4d19-b6cd-cc14fddfa5b9", + "name": "Spawner (113)", + "location": [1891, 2331, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08b4da27-33a7-4cfa-be11-f0eca92c6aa2", + "name": "Spawner (113)", + "location": [1890, 2270, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68d63e2a-4dc7-49a8-9876-f6bbe9704568", + "name": "Spawner (113)", + "location": [1873, 2426, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "313bfee9-788e-4860-8506-48c6e3a2c9b9", + "name": "Spawner (113)", + "location": [1837, 2334, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15f17b62-41e8-4db8-a62f-eb04db9c7606", + "name": "Spawner (113)", + "location": [1826, 2250, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "482755b3-dd05-4836-8a75-6f71edc3098f", + "name": "Spawner (113)", + "location": [2109, 2989, 30], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c1c9bf1-535e-4e13-a6ab-714d3439cd95", + "name": "Spawner (113)", + "location": [2089, 3017, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "769fdfa1-dd5c-4b03-89a2-2e86fb41888f", + "name": "Spawner (113)", + "location": [2074, 3069, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68ef188a-6290-4a22-a06b-089287e867ad", + "name": "Spawner (113)", + "location": [2033, 3041, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7b2a0a94-8536-4656-8b41-311715aabe6a", + "name": "Spawner (113)", + "location": [1970, 3052, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0dea7015-1783-4b93-9ded-b5b52be021cb", + "name": "Spawner (113)", + "location": [1915, 3050, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bd439c3-5ce9-4971-a8a8-45a432089787", + "name": "Spawner (113)", + "location": [1866, 3040, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b7f54d4-bf98-4c1c-9c79-c0b6b6fe0082", + "name": "Spawner (113)", + "location": [1812, 3036, 5], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5df49ed-42f4-4533-bd84-0c6d38a134a6", + "name": "Spawner (113)", + "location": [1841, 3000, 10], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b3ea5f3-1cf1-4b1f-85e5-97deb2d2741b", + "name": "Spawner (113)", + "location": [1803, 2981, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5da854a2-bca1-46e3-88fc-98d5dd640339", + "name": "Spawner (113)", + "location": [1867, 2962, 7], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5677568-fa43-430c-9f73-0ed52edd08c9", + "name": "Spawner (113)", + "location": [1849, 2915, 30], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9ac79ca6-546f-4634-8048-5a9c59b662c2", + "name": "Spawner (113)", + "location": [1911, 2934, 10], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a40f806a-f922-415f-b88f-63dde478379b", + "name": "Spawner (113)", + "location": [1954, 2903, 30], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "52640d62-3b31-4a7a-819a-518a6f3fae3e", + "name": "Spawner (113)", + "location": [851, 495, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd06b247-c98e-4218-93b9-b96ff85ba165", + "name": "Spawner (113)", + "location": [805, 463, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03369351-f3c2-4651-907d-2e9ec6fcfb6b", + "name": "Spawner (113)", + "location": [837, 361, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad8627b0-4cf5-4650-96aa-7bbc32e9e18b", + "name": "Spawner (113)", + "location": [1029, 1969, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5175dc34-0726-4304-bab5-bc9533355114", + "name": "Spawner (113)", + "location": [1080, 1963, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d0cd619-9e1a-4d4e-a3d1-2af50eea591e", + "name": "Spawner (113)", + "location": [1042, 1920, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36d83036-0fe8-4652-aecd-8f1dbe8a1388", + "name": "Spawner (113)", + "location": [821, 1938, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73a9fc66-3b63-4478-a41b-07df4bc6be50", + "name": "Spawner (113)", + "location": [736, 1938, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a5702d5-741d-4142-876f-f5446948c3ce", + "name": "Spawner (113)", + "location": [899, 1851, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f608b229-cfdb-4397-9908-249713f11b1f", + "name": "Spawner (113)", + "location": [759, 1828, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df7d496c-92b3-487a-a49c-92ef77818fd0", + "name": "Spawner (113)", + "location": [672, 1825, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d425d30-7a90-46b3-99ee-1be6e09e2ea4", + "name": "Spawner (113)", + "location": [892, 1963, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a93c7a8-af80-45e5-8f20-920eb51c09ff", + "name": "Spawner (113)", + "location": [815, 1603, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Spectre", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfca73f2-a84f-402f-b50f-52b3fa851d6e", + "name": "Spawner (113)", + "location": [1055, 1442, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec76be04-eeb8-4466-9328-008c0a24ded9", + "name": "Spawner (113)", + "location": [1021, 1415, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81e040fc-9b61-42ca-acfb-5d96eadca6a4", + "name": "Spawner (113)", + "location": [1060, 1375, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73b21795-cf2b-49a5-9ee8-9e25a136cb0b", + "name": "Spawner (113)", + "location": [1004, 1369, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "24d72ad1-069e-480c-93db-58654afde815", + "name": "Spawner (113)", + "location": [1037, 1325, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1aebb4f5-9b3c-42b8-bb18-77dc7b26b98f", + "name": "Spawner (113)", + "location": [971, 1308, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe495dbf-3e33-4998-a844-3fdba286070b", + "name": "Spawner (113)", + "location": [1000, 330, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "577bcfed-a871-4aa1-b989-189a320eba2f", + "name": "Spawner (113)", + "location": [964, 365, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4848785f-07e3-4e86-b62a-916774bf18e1", + "name": "Spawner (113)", + "location": [947, 418, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8642c7b8-1bbe-4e19-a85a-29df23ca9944", + "name": "Spawner (113)", + "location": [956, 452, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3947c59c-9e6f-44fb-938f-d1770f47b622", + "name": "Spawner (113)", + "location": [423, 1595, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4d67563-65b3-45b4-9cb9-02ecaf27d37e", + "name": "Spawner (113)", + "location": [418, 1665, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "799df1e3-5107-4858-8b7c-2e271b9756f1", + "name": "Spawner (113)", + "location": [480, 1641, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "697858f2-61ad-43d6-8cbb-97a5b872d8f4", + "name": "Spawner (113)", + "location": [516, 1585, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1707f1c-a8ae-4c20-9ae8-a8efea2f92dd", + "name": "Spawner (113)", + "location": [541, 1645, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36b8e938-4f69-4df8-a64b-6bb43c48bbb8", + "name": "Spawner (113)", + "location": [141, 1367, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78613e32-9f25-4c0d-a566-f63f4b57607d", + "name": "Spawner (113)", + "location": [189, 1344, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4054a1d-96e5-47dc-8d44-853d05e4d9c7", + "name": "Spawner (113)", + "location": [232, 1350, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2691312-c437-4d3b-bb69-8e2a26a432cf", + "name": "Spawner (113)", + "location": [278, 1355, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c2beb8c-e9fd-4484-9395-d13dcec9502b", + "name": "Spawner (113)", + "location": [229, 1419, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "087f6b77-2393-4dac-8543-50a14a7ae6f1", + "name": "Spawner (113)", + "location": [182, 1428, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "66634507-bcd9-4a0a-b5c9-5b6dd0c9adc9", + "name": "Spawner (113)", + "location": [157, 1487, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5219997-2933-414d-b9c1-8a38c7d7169a", + "name": "Spawner (113)", + "location": [204, 1483, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c133b0ca-429c-4f25-909f-36ce88862d76", + "name": "Spawner (113)", + "location": [240, 1443, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1cab8e17-97d9-4873-8f47-b79eb03e8038", + "name": "Spawner (113)", + "location": [269, 1474, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ffa2abe9-90a6-4b35-ae74-538bd30c2419", + "name": "Spawner (113)", + "location": [4057, 300, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "67a947e4-5f60-4065-9469-43a878cb1bfc", + "name": "Spawner (113)", + "location": [4013, 239, 1], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5d51f12-ef63-4187-ad60-00f961a67f30", + "name": "Spawner (113)", + "location": [3929, 235, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b48075d3-37f2-4471-bd77-272fd0de644b", + "name": "Spawner (113)", + "location": [3920, 287, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "66594f81-5b6b-4cfc-ab1c-48805b3ca936", + "name": "Spawner (113)", + "location": [4090, 325, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "620249c5-7ec3-4f73-bab8-52dd3f89d0c0", + "name": "Spawner (113)", + "location": [4162, 395, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49c9739c-a70f-4687-859d-56e5f72d59dc", + "name": "Spawner (113)", + "location": [4206, 471, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cbbe09b8-6b83-4d65-90b7-985dfa8335ad", + "name": "Spawner (113)", + "location": [3928, 393, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "193a49e9-ea5c-40f5-8118-d7f369008887", + "name": "Spawner (113)", + "location": [3963, 508, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ff6852c-8cd0-434b-9eb8-9afd5940805c", + "name": "Spawner (113)", + "location": [4066, 569, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "PolarBear", "maxCount": 8, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 8, "probability": 100 }, + { "name": "Walrus", "maxCount": 8, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2dfe455c-76ff-4ce4-84dc-46d1c84ad63f", + "name": "Spawner (113)", + "location": [4153, 601, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "640d4b0e-9a86-4035-8e71-c37fadb6a717", + "name": "Spawner (113)", + "location": [4214, 655, 1], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a33ee66a-277f-492a-940d-51c2654e1804", + "name": "Spawner (113)", + "location": [4264, 725, 1], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba2ae76c-dcca-4df2-9274-fa8136a61efb", + "name": "Spawner (113)", + "location": [2963, 3617, 3], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6c232b6-7b8e-41b4-b958-3aa5080efaa4", + "name": "Spawner (113)", + "location": [2966, 3591, 11], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "48328575-a500-4ebc-afcd-de93ed7209bc", + "name": "Spawner (113)", + "location": [3013, 3591, 15], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e53312f-8367-4864-b655-db9b4b3b652d", + "name": "Spawner (113)", + "location": [3024, 3566, 15], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d61a295a-c615-4af8-a057-bf82c9c753c7", + "name": "Spawner (113)", + "location": [3042, 3590, 5], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b487a09f-6222-4251-a712-839233ccf85d", + "name": "Spawner (113)", + "location": [2462, 725, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12e896ea-da73-4645-a010-70d1cf7145c8", + "name": "Spawner (113)", + "location": [2428, 722, 8], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e252043e-aff1-4bea-b8bc-32e668d5dd59", + "name": "Spawner (113)", + "location": [2375, 725, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d65bd05-bec9-44cc-8027-564ab2597d52", + "name": "Spawner (113)", + "location": [2345, 676, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2310c1b9-bc6e-4752-be08-751194837081", + "name": "Spawner (113)", + "location": [2429, 690, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f952e99-08cc-47bb-a84e-b814533856ba", + "name": "Spawner (113)", + "location": [2379, 654, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03d53510-89c6-400b-84cc-058b3a747a9f", + "name": "Spawner (113)", + "location": [2326, 656, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e62f1551-be29-4561-9de2-8029f7d167ea", + "name": "Spawner (113)", + "location": [2319, 591, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5127a1ad-b427-472b-aa36-8239cdf04b15", + "name": "Spawner (113)", + "location": [659, 1318, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "934bc4f3-d414-4cb1-8809-cdceca368616", + "name": "Spawner (113)", + "location": [579, 1305, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8a9dfe2-9620-4fe5-8d99-d1533d2f7da9", + "name": "Spawner (113)", + "location": [728, 1242, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6349924-e5bd-4073-9b11-4836e15f9f91", + "name": "Spawner (113)", + "location": [793, 1088, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "889af6a9-6809-4fdf-a28b-cc38170df5de", + "name": "Spawner (113)", + "location": [849, 1262, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4ca25be-16e8-4b4a-acc1-b87e77f4953e", + "name": "Spawner (113)", + "location": [909, 1175, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad79f7ea-b8bd-4821-8b80-d5d4fbe72c24", + "name": "Spawner (113)", + "location": [974, 1186, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15701d78-3752-44f6-b158-ddca2746390e", + "name": "Spawner (113)", + "location": [1059, 1166, 5], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "afd3e919-f63c-437a-bd36-b3431b394683", + "name": "Spawner (113)", + "location": [823, 960, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2b1d17be-9b13-46ea-88ff-89d1c0ddfa18", + "name": "Spawner (113)", + "location": [960, 910, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ecfe434-8acd-4f36-b873-626fd9337937", + "name": "Spawner (113)", + "location": [1042, 1024, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81bfcb26-842e-4e50-91c2-572c3aee4bee", + "name": "Spawner (113)", + "location": [1147, 1048, 5], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9cb6e3ec-d48c-416e-9e5e-4ce284e350b8", + "name": "Spawner (113)", + "location": [1097, 970, 2], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a9a67290-a828-48fa-a3f7-a0b7f10fbd45", + "name": "Spawner (113)", + "location": [1192, 958, 5], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc6d7ab6-b963-4216-85bd-99e6a7234b5d", + "name": "Spawner (113)", + "location": [1166, 818, 5], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3415104e-2a61-4225-9d64-be6609276576", + "name": "Spawner (113)", + "location": [1083, 783, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5fa9a115-ce2a-4213-b48d-c9744e848d12", + "name": "Spawner (113)", + "location": [1268, 774, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fcd562e-e69c-4792-9375-7df489650310", + "name": "Spawner (113)", + "location": [1180, 737, 1], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9ef0197-6591-49f6-ae85-2a94712d64a6", + "name": "Spawner (113)", + "location": [1122, 718, 1], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6357f444-0d29-4154-8256-71dde67c0337", + "name": "Spawner (113)", + "location": [423, 1595, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ab6b426-1743-4857-9784-498e4156521c", + "name": "Spawner (113)", + "location": [418, 1665, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "24cd546e-c1a8-4e63-9a2f-a10e04391e45", + "name": "Spawner (113)", + "location": [480, 1641, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "26e6a133-1816-47f1-af9a-c65e04e37cf8", + "name": "Spawner (113)", + "location": [516, 1585, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a7de24f-2da7-4229-8f6f-fbaf1adcecdd", + "name": "Spawner (113)", + "location": [541, 1645, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e263026b-c626-4692-9a46-67aafc5bc4c0", + "name": "Spawner (113)", + "location": [4504, 1458, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aa2759b8-efff-4868-97d0-674c377f63f3", + "name": "Spawner (113)", + "location": [4494, 1426, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50c40fb1-1e3d-4d2e-9d95-6bfd7d57fa3f", + "name": "Spawner (113)", + "location": [4455, 1434, 10], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "13ea30a4-a229-4926-8cc6-45d85d3680e2", + "name": "Spawner (113)", + "location": [4456, 1405, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1fd94ac8-c5bb-4bcf-8ce9-511bd67357ce", + "name": "Spawner (113)", + "location": [4455, 1358, 10], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "27db6adc-96c4-4afd-987f-269fccd23635", + "name": "Spawner (113)", + "location": [4550, 1409, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1c19f0de-4a64-471b-b656-9eb3d7a7d09e", + "name": "Spawner (113)", + "location": [4552, 1447, 10], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d05dae7-686d-4ca8-82c2-3b67c57adc30", + "name": "Spawner (113)", + "location": [4615, 1469, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4925ff65-5ca5-4f85-9d9b-6a228d778618", + "name": "Spawner (113)", + "location": [4612, 1418, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49369990-5bfb-4c5a-b64a-2dddbb892391", + "name": "Spawner (113)", + "location": [4605, 1365, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94502e42-4b90-4a37-8c20-6a8988b7857b", + "name": "Spawner (113)", + "location": [4566, 1359, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e7ba00e8-d827-4435-8455-690aa3f2482d", + "name": "Spawner (113)", + "location": [2372, 210, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c517cdb-1ddc-43fc-a8f1-2929874413a1", + "name": "Spawner (113)", + "location": [2355, 179, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "57956924-7db0-48b3-b993-d4284974093a", + "name": "Spawner (113)", + "location": [2351, 148, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37715bfb-2a0c-45ac-b416-45a3c3302b42", + "name": "Spawner (113)", + "location": [2345, 112, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10cf4b5b-9477-402b-ac6c-57245b73756c", + "name": "Spawner (113)", + "location": [2328, 67, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa392213-96a7-4165-8c45-f5a96988b74e", + "name": "Spawner (113)", + "location": [2177, 3034, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9258088-60b5-4ded-a22a-280d4eee8573", + "name": "Spawner (113)", + "location": [2143, 3068, 20], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6681af59-d2bd-46ee-96f7-15f9e48cf967", + "name": "Spawner (113)", + "location": [1983, 1517, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fd5313b-815f-4ec8-96e1-4ceb46497e78", + "name": "Spawner (113)", + "location": [1986, 1463, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49b84769-1509-4e93-8bfb-6f952dc6ba84", + "name": "Spawner (113)", + "location": [1969, 1409, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7b163acc-acd7-462a-a048-8f9a4ffc5cb8", + "name": "Spawner (113)", + "location": [1912, 1302, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84124f00-8aa4-491d-a4d9-53e65b98036f", + "name": "Spawner (113)", + "location": [1967, 1296, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e97ce87e-cb97-493c-a692-d48ebf51a4a3", + "name": "Spawner (113)", + "location": [1898, 1265, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "793c4766-d5b6-42f0-83ff-d0a267c26846", + "name": "Spawner (113)", + "location": [1872, 1347, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d77d3fc0-43bc-47da-9a91-5c0f82f285aa", + "name": "Spawner (113)", + "location": [1812, 1314, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c673f9b8-1ae1-4de9-8d6f-b907c7b758a1", + "name": "Spawner (113)", + "location": [1797, 1393, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8129ab7-03a2-498f-b96e-cd45b8913271", + "name": "Spawner (113)", + "location": [1862, 1434, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "563ed9d1-69ea-4407-8bcb-db6cdf69ca81", + "name": "Spawner (113)", + "location": [1877, 1515, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8829fbf2-d914-48fc-bdf4-7286f71acb4c", + "name": "Spawner (113)", + "location": [1802, 1506, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f8d690f4-c3ea-4da8-973b-7e442dd4fc2d", + "name": "Spawner (113)", + "location": [1692, 1463, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02eea69a-a67a-4244-b57d-d32b4d80510c", + "name": "Spawner (113)", + "location": [1833, 1245, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6701bc25-8a58-4dc8-aba0-5be8467a0c96", + "name": "Spawner (113)", + "location": [1726, 1362, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f87b3cc-3bec-4233-a193-faf5d766c8b1", + "name": "Spawner (113)", + "location": [1640, 1431, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5eca3a0-ab6e-4a09-aea0-e5c02573761a", + "name": "Spawner (113)", + "location": [1055, 1442, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5029ee0-ff6b-4336-bd11-196cfba8392c", + "name": "Spawner (113)", + "location": [1021, 1415, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fc005a29-add5-4782-9fa3-5a15644b9d97", + "name": "Spawner (113)", + "location": [1060, 1375, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca9bfc78-2835-4a5b-bc0d-6fe008b79b5c", + "name": "Spawner (113)", + "location": [1004, 1369, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "00b85da5-f02c-4ddb-88c0-11c677a799c1", + "name": "Spawner (113)", + "location": [1037, 1325, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f26c8e00-9f91-478b-8d8e-089f164d81cf", + "name": "Spawner (113)", + "location": [971, 1308, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2676738-8f89-44ba-bd49-531b4a48c13a", + "name": "Spawner (113)", + "location": [2171, 1356, 0], + "map": "Trammel", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, + { "name": "Savage", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "034b0848-3841-4c7a-befa-b47a3c32c96a", + "name": "Spawner (113)", + "location": [2184, 1376, 0], + "map": "Trammel", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, + { "name": "Savage", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f689f7d9-5600-4199-86fd-e4bfc33e670f", + "name": "Spawner (113)", + "location": [2155, 1370, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a4006aa-ef63-42db-9a08-cc7df6515351", + "name": "Spawner (113)", + "location": [2203, 1379, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a4e77a5-dd20-465a-a274-4a6dedad51b8", + "name": "Spawner (113)", + "location": [2038, 1023, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61c1a7de-68ec-4a55-b6d7-b71c315d93d1", + "name": "Spawner (113)", + "location": [2079, 1018, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b3c0d48-b7c1-4282-8233-b5bef002ab2a", + "name": "Spawner (113)", + "location": [2067, 989, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6dfcbb5-788b-4b78-b87b-d3587f7ccc94", + "name": "Spawner (113)", + "location": [2028, 987, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4bbc1a4a-e118-4531-aaae-b7c8ea90206f", + "name": "Spawner (113)", + "location": [2033, 936, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3a4bb67-71eb-420c-b965-8dec687d5b66", + "name": "Spawner (113)", + "location": [1990, 976, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5a204bf-a47d-47be-9f0b-9182bc4ad39f", + "name": "Spawner (113)", + "location": [1993, 1030, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3db40c33-f93d-4707-a22e-b56e2170744f", + "name": "Spawner (113)", + "location": [2035, 1018, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e87d826-1451-4347-b6b1-e272bcd135cc", + "name": "Spawner (113)", + "location": [2023, 1052, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1812fd23-b8b5-4af6-b601-b12e3acd5de3", + "name": "Spawner (113)", + "location": [2069, 1028, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c184c7e-21c7-46f1-b437-5e4e9091f0b9", + "name": "Spawner (113)", + "location": [2048, 1066, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6781029f-3b4a-4030-ad2d-83948df6f257", + "name": "Spawner (113)", + "location": [1435, 2261, 5], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93468ee6-bb6d-43d0-b4b7-eb86de5bcbc0", + "name": "Spawner (113)", + "location": [1651, 2413, 5], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "547bc7d5-edce-4e93-967c-3430f125219f", + "name": "Spawner (113)", + "location": [1619, 2725, 10], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eac45551-873f-48bb-9296-121cbd96318c", + "name": "Spawner (113)", + "location": [1427, 2525, 0], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a756d7c7-629c-45cb-970e-deee0190baa3", + "name": "Spawner (113)", + "location": [1059, 2461, 0], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a50eb7ab-718b-4cd9-b418-2a55ef6c8eb6", + "name": "Spawner (113)", + "location": [1243, 2437, 5], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ec3e3a0-8c69-4620-8e2c-7f6b2d550787", + "name": "Spawner (113)", + "location": [1145, 2231, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Daemon", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d9fa1621-0c99-4082-8075-0e4a1f8e25f7", + "name": "Spawner (113)", + "location": [1072, 2195, 5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d4c137a-0a24-456a-837f-cfd51ab71a3a", + "name": "Spawner (113)", + "location": [1070, 2256, 5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a4e772e0-b570-46da-a6b8-4d84ad26dfcb", + "name": "Spawner (113)", + "location": [1215, 2195, 5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f019e22a-5265-452b-ac1f-9f1cd8018041", + "name": "Spawner (113)", + "location": [1216, 2259, 5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d1a8c12d-2186-49a2-81ae-6f14ea963ca1", + "name": "Spawner (113)", + "location": [1595, 3045, 0], + "map": "Trammel", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 100, + "entries": [ + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09a60df9-a7f8-4964-9e24-dc43558b7c96", + "name": "Spawner (113)", + "location": [2091, 3413, 0], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d38fbcb-a573-405a-94cd-1cda898164a1", + "name": "Spawner (113)", + "location": [1931, 3205, 0], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "30a11e6d-014f-427b-a461-63d0653fa3b8", + "name": "Spawner (113)", + "location": [1827, 3429, 10], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3effe468-2b2d-4d56-952e-9c378e1d9a84", + "name": "Spawner (113)", + "location": [1635, 3253, 0], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a08f71d7-7504-448f-814c-c7c34216019c", + "name": "Spawner (113)", + "location": [1387, 3109, 0], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "090b4a9b-ed1e-4276-94db-cf708d34f9d6", + "name": "Spawner (113)", + "location": [1110, 2672, 0], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04e28c4b-9b75-46a7-b932-3ce92bcc7d85", + "name": "Spawner (113)", + "location": [1158, 2872, 0], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "AcidElemental", "maxCount": 24, "probability": 100 }, + { "name": "Alligator", "maxCount": 24, "probability": 100 }, + { "name": "BogThing", "maxCount": 24, "probability": 100 }, + { "name": "Bogling", "maxCount": 24, "probability": 100 }, + { "name": "Corpser", "maxCount": 24, "probability": 100 }, + { "name": "GiantRat", "maxCount": 24, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 24, "probability": 100 }, + { "name": "Lizardman", "maxCount": 23, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 23, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 23, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 23, "probability": 100 }, + { "name": "Ratman", "maxCount": 23, "probability": 100 }, + { "name": "SewerRat", "maxCount": 23, "probability": 100 }, + { "name": "Snake", "maxCount": 23, "probability": 100 }, + { "name": "Wisp", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "079a77cb-460d-4537-bd99-7ffe833a7648", + "name": "Spawner (113)", + "location": [1141, 2959, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Savage", "maxCount": 2, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5eb5bfe-0848-489f-9717-a8891ae7a1e9", + "name": "Spawner (113)", + "location": [1166, 2958, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Savage", "maxCount": 2, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "00f29d73-3ef1-4c12-bdd7-ff19293fecef", + "name": "Spawner (113)", + "location": [854, 1686, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "Brigand", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "77395a8a-4c2d-4f5f-9692-58c84c4146a7", + "name": "Spawner (113)", + "location": [757, 1401, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Lich", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "88e2ec5e-fbd6-4459-9128-74e244d4b01f", + "name": "Spawner (113)", + "location": [634, 1504, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c326969-58f1-4463-83a1-980619b49133", + "name": "Spawner (113)", + "location": [634, 1462, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "decca3ad-554d-4411-b3eb-ee2535528d04", + "name": "Spawner (113)", + "location": [633, 1484, 0], + "map": "Trammel", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f1a08a0-b47a-4299-8733-94a2923e3d42", + "name": "Spawner (113)", + "location": [965, 757, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2169b133-512b-45c3-aead-50a931bd972a", + "name": "Spawner (113)", + "location": [955, 707, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 12, + "entries": [ + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "574f956f-5ff2-4a72-a157-6ffb296b52b2", + "name": "Spawner (113)", + "location": [962, 692, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 5, + "entries": [ + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c7248e18-5065-4a1f-8278-cd383e39a7ad", + "name": "Spawner (113)", + "location": [948, 703, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 5, + "entries": [ + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85053a68-d91a-4502-841e-697dde7fdbf5", + "name": "Spawner (113)", + "location": [1016, 821, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "637a3aaf-fa96-4bdc-a770-ec273932916f", + "name": "Spawner (113)", + "location": [1015, 706, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20d0534e-aa34-4f34-9615-5e37cb005052", + "name": "Spawner (113)", + "location": [1017, 826, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abdfd7cf-7042-4881-b6bd-16231d352cb3", + "name": "Spawner (113)", + "location": [4596, 3607, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Daemon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f0a8c785-3472-41f4-b59b-e3a0252e6962", + "name": "Spawner (113)", + "location": [4571, 3572, 31], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "caeb038e-ab75-41e9-bce1-c5be5d25551e", + "name": "Spawner (113)", + "location": [4595, 3574, 75], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [{ "name": "Daemon", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cd80c05e-08c8-411f-aecd-0b030c095094", + "name": "Spawner (113)", + "location": [4618, 3570, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d3e512e-32f2-412f-a9de-4140e4e97a9e", + "name": "Spawner (113)", + "location": [4595, 3570, 30], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f67516b1-35ef-4231-890f-5626fccbfef1", + "name": "Spawner (113)", + "location": [608, 1702, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Wisp", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "71d63638-7d38-4e09-a599-2b6eace4ffff", + "name": "Spawner (113)", + "location": [4530, 1387, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "GiantRat", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Jwilson", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b280374f-3913-422f-a8fb-67a5b5774cbd", + "name": "Spawner (113)", + "location": [4511, 1394, 23], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "IceSerpent", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "FrostOoze", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e146ad1f-0e5e-4018-956e-faddb83059ca", + "name": "Spawner (113)", + "location": [4511, 1394, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2162b8e-47c1-449a-bca4-8b1e29a7fa5b", + "name": "Spawner (113)", + "location": [4498, 1394, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Rabbit", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "Panther", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Eagle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88ec5fa0-837d-41e1-9b72-864f82f40070", + "name": "Spawner (113)", + "location": [4485, 1387, 23], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Alligator", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "081de1cf-9c2c-4ac8-9b21-ba2593487543", + "name": "Spawner (113)", + "location": [4493, 1364, 23], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Boar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33d5a886-8aea-47d2-910d-067fdacd1def", + "name": "Spawner (113)", + "location": [4493, 1364, 23], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d956977-3c38-429d-bf32-32968f930d34", + "name": "Spawner (113)", + "location": [4506, 1358, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "Panther", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d68cd11-ae7b-45c4-944a-6e58a6e65684", + "name": "Spawner (113)", + "location": [4519, 1358, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Alligator", "maxCount": 1, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 }, + { "name": "Gorilla", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GiantToad", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "700be867-c251-4ecc-a320-867ba65b024c", + "name": "Spawner (113)", + "location": [4531, 1363, 23], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb69e358-5119-4573-a663-19bb5ab7bdcd", + "name": "Spawner (113)", + "location": [4512, 1372, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 10, + "entries": [ + { "name": "RottingCorpse", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Slime", "maxCount": 1, "probability": 100 }, + { "name": "Snake", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2b4059a-7a86-4b8d-aac4-d98f8ac842b5", + "name": "Spawner (113)", + "location": [4512, 1380, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 10, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Jwilson", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "efb1438c-3ee7-466f-80b8-629a1e319b19", + "name": "Spawner (113)", + "location": [4500, 1382, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Chicken", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Pig", "maxCount": 1, "probability": 100 }, + { "name": "Sheep", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "686118b7-c5ff-48d2-9035-55a6eabee085", + "name": "Spawner (113)", + "location": [821, 675, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fa601f2-2e8d-4170-8c1f-1960889ef12d", + "name": "Spawner (113)", + "location": [1042, 1394, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78c65c1b-b461-4203-9a8a-ad62146c81ba", + "name": "Spawner (113)", + "location": [2540, 130, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d5de5b0d-fffe-496d-8167-85ff964f45d0", + "name": "Spawner (113)", + "location": [3170, 672, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b6c7f37-04ff-4f95-9167-8f299dd5ab5f", + "name": "Spawner (113)", + "location": [2548, 1173, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af95e8e2-8d1f-43a8-ab45-cd79154fd947", + "name": "Spawner (113)", + "location": [4480, 1439, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "574d5350-83ca-4a90-8494-1234476d9004", + "name": "Spawner (113)", + "location": [1152, 3471, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41daa032-dfaf-437c-aea8-5ebd5ae28f8d", + "name": "Spawner (113)", + "location": [1530, 999, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b77a44c2-c523-4718-ba7d-ace36df8fde5", + "name": "Spawner (113)", + "location": [1871, 1076, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc54c094-335f-4eb0-835b-df374aa73808", + "name": "Spawner (113)", + "location": [788, 1894, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1c790a35-f7b6-4ee1-8d94-4e6076f94ed3", + "name": "Spawner (113)", + "location": [1064, 2488, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1b36b219-b882-4af5-a52b-5097ee26680f", + "name": "Spawner (113)", + "location": [2103, 3371, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ebc9621-1061-4ab5-aed9-c6d72a4c1514", + "name": "Spawner (113)", + "location": [2044, 2323, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd48c66c-dbf3-4729-9b05-896d93615858", + "name": "Spawner (113)", + "location": [1939, 1310, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e4825b9-1cbe-4a27-a4d2-e2552901924f", + "name": "Spawner (113)", + "location": [2349, 722, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "1af71e9c-8891-43d9-940a-bdbd94560583", "name": "Spawner (113)", "location": [3650, 2509, 0], @@ -18,7 +8575,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f2443544-bfe4-460a-819c-17df28a96007", "name": "Spawner (113)", "location": [3598, 2450, 16], @@ -36,7 +8593,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "04d3a2ac-5bcc-46c5-93a6-46a19856d317", "name": "Spawner (113)", "location": [3635, 2625, 0], @@ -47,12 +8604,10 @@ "team": 0, "homeRange": 6, "walkingRange": 15, - "entries": [ - { "name": "Skeleton", "maxCount": 7, "probability": 100 } - ] + "entries": [{ "name": "Skeleton", "maxCount": 7, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a77b05f7-790b-4ff2-9e9f-f2198d7ea8ac", "name": "Spawner (113)", "location": [3651, 2627, 0], @@ -63,12 +8618,10 @@ "team": 0, "homeRange": 8, "walkingRange": 15, - "entries": [ - { "name": "Skeleton", "maxCount": 10, "probability": 100 } - ] + "entries": [{ "name": "Skeleton", "maxCount": 10, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c81666eb-207b-4b23-8d17-3e502b94e5b9", "name": "Spawner (113)", "location": [3625, 2594, 0], @@ -79,12 +8632,10 @@ "team": 0, "homeRange": 8, "walkingRange": 15, - "entries": [ - { "name": "Zombie", "maxCount": 10, "probability": 100 } - ] + "entries": [{ "name": "Zombie", "maxCount": 10, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "aa86f56c-710e-431a-8347-03d24de8ba35", "name": "Spawner (113)", "location": [3649, 2591, 0], @@ -95,12 +8646,10 @@ "team": 0, "homeRange": 8, "walkingRange": 15, - "entries": [ - { "name": "Zombie", "maxCount": 10, "probability": 100 } - ] + "entries": [{ "name": "Zombie", "maxCount": 10, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "02fb2a03-26a3-4fd6-b1b9-dcff4f1a81f1", "name": "Spawner (113)", "location": [3618, 2614, 0], @@ -111,12 +8660,10 @@ "team": 0, "homeRange": 4, "walkingRange": 4, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Healer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "780ce0e0-1358-451d-a224-02a36e50ad15", "name": "Spawner (113)", "location": [3725, 2670, 35], @@ -127,12 +8674,10 @@ "team": 0, "homeRange": 40, "walkingRange": 40, - "entries": [ - { "name": "Sheep", "maxCount": 10, "probability": 100 } - ] + "entries": [{ "name": "Sheep", "maxCount": 10, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2a750344-a3b9-4cfe-bbe4-a036a2d382cd", "name": "Spawner (113)", "location": [3727, 2603, 40], @@ -143,12 +8688,10 @@ "team": 0, "homeRange": 40, "walkingRange": 40, - "entries": [ - { "name": "Sheep", "maxCount": 10, "probability": 100 } - ] + "entries": [{ "name": "Sheep", "maxCount": 10, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e4e819fc-7d1d-40df-9a17-4bcc44f6a600", "name": "Spawner (113)", "location": [3738, 2586, 40], @@ -159,12 +8702,10 @@ "team": 0, "homeRange": 12, "walkingRange": 12, - "entries": [ - { "name": "Pumpkin", "maxCount": 40, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 40, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "58878acf-f253-4422-aa77-4cca67211798", "name": "Spawner (113)", "location": [3711, 2571, 20], @@ -175,12 +8716,10 @@ "team": 0, "homeRange": 6, "walkingRange": 6, - "entries": [ - { "name": "Pumpkin", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "527e71d1-6bd4-4382-b1ab-df6a7607d5b6", "name": "Spawner (113)", "location": [3738, 2610, 40], @@ -191,12 +8730,10 @@ "team": 0, "homeRange": 12, "walkingRange": 12, - "entries": [ - { "name": "Pumpkin", "maxCount": 40, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 40, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4bb02df5-05ad-4772-b673-5d7a2e606453", "name": "Spawner (113)", "location": [3716, 2612, 20], @@ -207,12 +8744,10 @@ "team": 0, "homeRange": 5, "walkingRange": 6, - "entries": [ - { "name": "Pumpkin", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "62ae0cad-51cf-4c49-90d7-67cb6ec90215", "name": "Spawner (113)", "location": [3716, 2617, 20], @@ -223,12 +8758,10 @@ "team": 0, "homeRange": 5, "walkingRange": 6, - "entries": [ - { "name": "Pumpkin", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "70e55af3-0e24-4075-82ee-4eb6e666e36e", "name": "Spawner (113)", "location": [3716, 2624, 21], @@ -239,12 +8772,10 @@ "team": 0, "homeRange": 5, "walkingRange": 6, - "entries": [ - { "name": "Pumpkin", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9fdc6901-f1ec-4f1d-a8b5-4db1261dcf0b", "name": "Spawner (113)", "location": [3744, 2660, 40], @@ -255,12 +8786,10 @@ "team": 0, "homeRange": 12, "walkingRange": 12, - "entries": [ - { "name": "Pumpkin", "maxCount": 50, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 50, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a6f9edee-6056-438d-a64d-6af232087c5f", "name": "Spawner (113)", "location": [3716, 2632, 20], @@ -271,12 +8800,10 @@ "team": 0, "homeRange": 5, "walkingRange": 6, - "entries": [ - { "name": "Pumpkin", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "8c9f0ebe-5cb1-4b50-b72b-a4220150b88e", "name": "Spawner (113)", "location": [3751, 2704, 39], @@ -287,12 +8814,10 @@ "team": 0, "homeRange": 6, "walkingRange": 6, - "entries": [ - { "name": "Pumpkin", "maxCount": 12, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 12, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9be72b3f-7a97-4f8f-b4e7-594502ff3222", "name": "Spawner (113)", "location": [3711, 2669, 20], @@ -303,12 +8828,10 @@ "team": 0, "homeRange": 6, "walkingRange": 6, - "entries": [ - { "name": "Pumpkin", "maxCount": 8, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 8, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3866d15e-8c29-4c90-88b6-eb32df6ef598", "name": "Spawner (113)", "location": [3711, 2690, 20], @@ -319,12 +8842,10 @@ "team": 0, "homeRange": 6, "walkingRange": 6, - "entries": [ - { "name": "Pumpkin", "maxCount": 8, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 8, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a2b0dc3d-9a2f-4614-bd7a-81f6ebb1dfe6", "name": "Spawner (113)", "location": [3711, 2679, 20], @@ -335,12 +8856,10 @@ "team": 0, "homeRange": 6, "walkingRange": 6, - "entries": [ - { "name": "Pumpkin", "maxCount": 8, "probability": 100 } - ] + "entries": [{ "name": "Pumpkin", "maxCount": 8, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5ea95508-0bcf-45cb-97f1-fa8c7a59d604", "name": "Spawner (113)", "location": [3772, 2771, 56], @@ -357,7 +8876,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6527a09f-7352-45a1-929e-bce8398c09b9", "name": "Spawner (113)", "location": [3396, 2645, 48], @@ -390,7 +8909,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "62925efa-f113-4201-a4f9-3a93e358e103", "name": "Spawner (113)", "location": [3742, 2520, 47], @@ -422,7 +8941,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "caeaff80-1da6-4546-998a-f629d8b849be", "name": "Spawner (113)", "location": [3403, 2572, 21], @@ -454,7 +8973,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fe0cce54-9a71-4c02-a1b7-e2dc495dd29e", "name": "Spawner (113)", "location": [3690, 2422, 40], @@ -486,7 +9005,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6cced58c-2759-4614-bc35-bdd0cc98d6cf", "name": "Spawner (113)", "location": [3615, 2410, 47], @@ -518,7 +9037,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d80a73e2-7862-4b9d-baba-a1dd20b132b1", "name": "Spawner (113)", "location": [3549, 2409, 16], @@ -550,7 +9069,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "aa2bfefb-2642-4c4e-a7ae-0ed83dd451e5", "name": "Spawner (113)", "location": [3483, 2389, 17], @@ -582,7 +9101,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a94d5e4b-e59a-4138-9c9c-6e92f3495861", "name": "Spawner (113)", "location": [3575, 2523, 0], @@ -614,7 +9133,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "754ae235-0db8-454e-afca-3ae1e96814ee", "name": "Spawner (113)", "location": [3524, 2459, 28], @@ -646,7 +9165,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c33dcd57-a372-45e6-a84e-5ec5be59961e", "name": "Spawner (113)", "location": [3482, 2459, 35], @@ -678,7 +9197,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d4654660-6f3b-4548-8237-95178ffc922d", "name": "Spawner (113)", "location": [3436, 2486, 27], @@ -710,7 +9229,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "49d1330e-77d3-4b7b-8e8d-0ca096a86687", "name": "Spawner (113)", "location": [3450, 2734, 49], @@ -744,7 +9263,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "855e3df1-166e-41c3-9e1e-08bbde898559", "name": "Spawner (113)", "location": [3700, 2699, 20], @@ -755,12 +9274,10 @@ "team": 0, "homeRange": 4, "walkingRange": 4, - "entries": [ - { "name": "BrigandCamp", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BrigandCamp", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "eeb2089b-90c4-4415-92bd-f9ac17231245", "name": "Spawner (113)", "location": [3753, 2705, 40], @@ -771,12 +9288,10 @@ "team": 0, "homeRange": 4, "walkingRange": 4, - "entries": [ - { "name": "BrigandCamp", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BrigandCamp", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "aab3ef35-c62d-459e-bd83-d3c70189caa2", "name": "Spawner (113)", "location": [3764, 2662, 62], @@ -787,8 +9302,6 @@ "team": 0, "homeRange": 0, "walkingRange": 0, - "entries": [ - { "name": "BrigandCamp", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BrigandCamp", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/PaintedCaves.json b/Distribution/Data/Spawns/post-uoml/trammel/PaintedCaves.json similarity index 96% rename from Distribution/Data/Spawns/shared/trammel/PaintedCaves.json rename to Distribution/Data/Spawns/post-uoml/trammel/PaintedCaves.json index e5549d9dd..e5d70de82 100644 --- a/Distribution/Data/Spawns/shared/trammel/PaintedCaves.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/PaintedCaves.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "3cf3a839-be51-4f1c-8592-9a1f89b02b87", "name": "Spawner (114)", "location": [6279, 880, 1], @@ -21,7 +21,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "67d9fa9b-5bc4-4f2b-8f19-fc31af6f2333", "name": "Spawner (114)", "location": [6275, 879, -2], diff --git a/Distribution/Data/Spawns/shared/trammel/PalaceOfParoxysmus.json b/Distribution/Data/Spawns/post-uoml/trammel/PalaceOfParoxysmus.json similarity index 94% rename from Distribution/Data/Spawns/shared/trammel/PalaceOfParoxysmus.json rename to Distribution/Data/Spawns/post-uoml/trammel/PalaceOfParoxysmus.json index bb6828072..5fc0f17b4 100644 --- a/Distribution/Data/Spawns/shared/trammel/PalaceOfParoxysmus.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/PalaceOfParoxysmus.json @@ -1,118 +1,6 @@ [ { - "$type": "Spawner", - "guid": "02cf36b9-6188-4bda-9e7f-66d0aac930b8", - "name": "Spawner (115)", - "location": [6416, 447, -40], - "map": "Trammel", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "PlagueBeast", "maxCount": 8, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 8, "probability": 100 }, - { "name": "AcidElemental", "maxCount": 7, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b986151-c607-40ec-b8b2-3b8a09e1e828", - "name": "Spawner (115)", - "location": [6292, 462, -50], - "map": "Trammel", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "ChaosDaemon", "maxCount": 7, "probability": 100 }, - { "name": "Moloch", "maxCount": 7, "probability": 100 }, - { "name": "Succubus", "maxCount": 7, "probability": 100 }, - { "name": "Daemon", "maxCount": 7, "probability": 100 }, - { "name": "Balron", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "546847b9-bd52-4a7b-be03-bd9b9f26cb17", - "name": "Spawner (115)", - "location": [6249, 352, 60], - "map": "Trammel", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "AcidElemental", "maxCount": 6, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 6, "probability": 100 }, - { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "676cc139-9378-4935-9667-0cbd86e0b2b1", - "name": "Spawner (115)", - "location": [6285, 353, 60], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "851856f8-49a9-4dfd-95bc-e969d748bd9c", - "name": "Spawner (115)", - "location": [6404, 378, -40], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 18, - "walkingRange": 18, - "entries": [ - { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 2, "probability": 100 }, - { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8bbbe927-ffbd-4f1c-aa89-4982088f1dca", - "name": "Spawner (115)", - "location": [6298, 395, 60], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "9599a1e0-f678-49f5-8ec6-c4f01998f96f", "name": "Spawner (115)", "location": [6295, 616, -50], @@ -136,30 +24,7 @@ ] }, { - "$type": "Spawner", - "guid": "ad7e7828-7b85-416c-8ca6-6a9e7de99d95", - "name": "Spawner (115)", - "location": [6319, 348, 60], - "map": "Trammel", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "EarthElemental", "maxCount": 5, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 5, "probability": 100 }, - { "name": "AcidElemental", "maxCount": 5, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 5, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 1, "probability": 100 }, - { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "bae60d5d-cbc7-4fed-ae27-75eb68d79870", "name": "Spawner (115)", "location": [6384, 580, -50], @@ -183,10 +48,10 @@ ] }, { - "$type": "Spawner", - "guid": "c32b93fc-8549-4d80-8d8a-de99e885a477", + "type": "Spawner", + "guid": "cb00720f-c41e-413f-8cec-b90e2fe72b4a", "name": "Spawner (115)", - "location": [6353, 400, 60], + "location": [6328, 517, -50], "map": "Trammel", "count": 4, "minDelay": "00:05:00", @@ -194,13 +59,10 @@ "team": 0, "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "CorrosiveSlime", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "PoisonElemental", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c3c0fc01-0e9e-420e-a994-ce64bc57a96e", "name": "Spawner (115)", "location": [6354, 455, -40], @@ -219,7 +81,26 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "02cf36b9-6188-4bda-9e7f-66d0aac930b8", + "name": "Spawner (115)", + "location": [6416, 447, -40], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "PlagueBeast", "maxCount": 8, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 8, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 7, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "c68cc7ec-9447-455e-ac60-ab1d1f319bc5", "name": "Spawner (115)", "location": [6466, 543, -50], @@ -243,23 +124,63 @@ ] }, { - "$type": "Spawner", - "guid": "cb00720f-c41e-413f-8cec-b90e2fe72b4a", + "type": "Spawner", + "guid": "4b986151-c607-40ec-b8b2-3b8a09e1e828", "name": "Spawner (115)", - "location": [6328, 517, -50], + "location": [6292, 462, -50], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "ChaosDaemon", "maxCount": 7, "probability": 100 }, + { "name": "Moloch", "maxCount": 7, "probability": 100 }, + { "name": "Succubus", "maxCount": 7, "probability": 100 }, + { "name": "Daemon", "maxCount": 7, "probability": 100 }, + { "name": "Balron", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8bbbe927-ffbd-4f1c-aa89-4982088f1dca", + "name": "Spawner (115)", + "location": [6298, 395, 60], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "851856f8-49a9-4dfd-95bc-e969d748bd9c", + "name": "Spawner (115)", + "location": [6404, 378, -40], "map": "Trammel", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, + "homeRange": 18, + "walkingRange": 18, "entries": [ - { "name": "PoisonElemental", "maxCount": 4, "probability": 100 } + { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 2, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "dcc7984a-1911-4368-81df-344fa7b0a2b5", "name": "Spawner (115)", "location": [6418, 358, -40], @@ -282,5 +203,80 @@ { "name": "CorrosiveSlime", "maxCount": 2, "probability": 100 }, { "name": "PoisonElemental", "maxCount": 2, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "ad7e7828-7b85-416c-8ca6-6a9e7de99d95", + "name": "Spawner (115)", + "location": [6319, 348, 60], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "EarthElemental", "maxCount": 5, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 5, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 5, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 5, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 1, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "546847b9-bd52-4a7b-be03-bd9b9f26cb17", + "name": "Spawner (115)", + "location": [6249, 352, 60], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "AcidElemental", "maxCount": 6, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 6, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "676cc139-9378-4935-9667-0cbd86e0b2b1", + "name": "Spawner (115)", + "location": [6285, 353, 60], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "PoisonElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c32b93fc-8549-4d80-8d8a-de99e885a477", + "name": "Spawner (115)", + "location": [6353, 400, 60], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 4, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/PrismOfLight.json b/Distribution/Data/Spawns/post-uoml/trammel/PrismOfLight.json similarity index 77% rename from Distribution/Data/Spawns/shared/trammel/PrismOfLight.json rename to Distribution/Data/Spawns/post-uoml/trammel/PrismOfLight.json index a70a7dcde..32f83fe9d 100644 --- a/Distribution/Data/Spawns/shared/trammel/PrismOfLight.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/PrismOfLight.json @@ -1,87 +1,37 @@ [ { - "$type": "Spawner", - "guid": "19d77ed8-3a64-4317-9570-c8a19dc885ed", + "type": "Spawner", + "guid": "4664466b-b753-46bf-8d9a-4c66246ed060", "name": "Spawner (116)", - "location": [6536, 79, -10], + "location": [6553, 157, 0], "map": "Trammel", - "count": 1, + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "CrystalLatticeSeeker", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70250ff2-f75d-4f6a-adcd-e32d3f8c05e4", + "name": "Spawner (116)", + "location": [6474, 74, -34], + "map": "Trammel", + "count": 9, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 4, "walkingRange": 4, "entries": [ - { "name": "CrystalDaemon", "maxCount": 1, "probability": 100 } + { "name": "Rat", "maxCount": 9, "probability": 100 }, + { "name": "IceSnake", "maxCount": 9, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "2b5f366f-9197-4fb0-842c-d7e50f9d3395", - "name": "Spawner (116)", - "location": [6478, 174, 4], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Wisp", "maxCount": 6, "probability": 100 }, - { "name": "CrystalWisp", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2bdadfea-e542-43dd-8391-a0a55761bd09", - "name": "Spawner (116)", - "location": [6509, 176, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "UnfrozenMummy", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ad2e27a-0fe6-44eb-9a7e-1840e2333414", - "name": "Spawner (116)", - "location": [6572, 89, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "CrystalHydra", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ce801dd-1e54-4eb6-b908-d095fc3ac3d6", - "name": "Spawner (116)", - "location": [6521, 139, -20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "44ede0db-e4a9-4fc2-bbd4-41741dc3a138", "name": "Spawner (116)", "location": [6552, 155, 0], @@ -100,23 +50,35 @@ ] }, { - "$type": "Spawner", - "guid": "4664466b-b753-46bf-8d9a-4c66246ed060", + "type": "Spawner", + "guid": "3ce801dd-1e54-4eb6-b908-d095fc3ac3d6", "name": "Spawner (116)", - "location": [6553, 157, 0], + "location": [6521, 139, -20], "map": "Trammel", - "count": 3, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "CrystalLatticeSeeker", "maxCount": 3, "probability": 100 } - ] + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Wisp", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "d4e26c54-212c-4dc9-bbb8-a6e2ecd02d55", + "name": "Spawner (116)", + "location": [6530, 139, -20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Wisp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "500789a1-6c13-4c8c-94c4-d7290a6d9238", "name": "Spawner (116)", "location": [6576, 91, 0], @@ -127,28 +89,111 @@ "team": 0, "homeRange": 4, "walkingRange": 4, - "entries": [ - { "name": "CrystalSeaSerpent", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "CrystalSeaSerpent", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "57899529-d774-4c8f-a5b3-263237a5cb74", + "type": "Spawner", + "guid": "2bdadfea-e542-43dd-8391-a0a55761bd09", "name": "Spawner (116)", - "location": [6507, 151, 0], + "location": [6509, 176, 0], "map": "Trammel", - "count": 3, + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "UnfrozenMummy", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19d77ed8-3a64-4317-9570-c8a19dc885ed", + "name": "Spawner (116)", + "location": [6536, 79, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "68fe4e5a-136b-4f9d-8b23-1289f26cbfb3", + "name": "Spawner (116)", + "location": [6547, 91, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ad2e27a-0fe6-44eb-9a7e-1840e2333414", + "name": "Spawner (116)", + "location": [6572, 89, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalHydra", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6c1abed9-6d57-4f20-a0e6-1598ab7b0602", + "name": "Spawner (116)", + "location": [6504, 181, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb0719bf-0812-4551-a287-3fe92e6dd66b", + "name": "Spawner (116)", + "location": [6475, 101, -44], + "map": "Trammel", + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 5, "walkingRange": 5, + "entries": [{ "name": "ShadowWisp", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2b5f366f-9197-4fb0-842c-d7e50f9d3395", + "name": "Spawner (116)", + "location": [6478, 174, 4], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, "entries": [ - { "name": "Protector", "maxCount": 3, "probability": 100 } + { "name": "Wisp", "maxCount": 6, "probability": 100 }, + { "name": "CrystalWisp", "maxCount": 6, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5a151ef4-7184-49a1-b516-e2e5ba7973f4", "name": "Spawner (116)", "location": [6566, 120, 0], @@ -165,7 +210,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5e2e128a-5312-4afc-858f-4ee70783f6c8", "name": "Spawner (116)", "location": [6534, 178, 0], @@ -176,125 +221,10 @@ "team": 0, "homeRange": 4, "walkingRange": 4, - "entries": [ - { "name": "CrystalWisp", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "CrystalWisp", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "68fe4e5a-136b-4f9d-8b23-1289f26cbfb3", - "name": "Spawner (116)", - "location": [6547, 91, -10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "CrystalDaemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6c1abed9-6d57-4f20-a0e6-1598ab7b0602", - "name": "Spawner (116)", - "location": [6504, 181, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6d47bfa4-0820-4805-865d-7a7f3e54e159", - "name": "Spawner (116)", - "location": [6510, 171, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Protector", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "70250ff2-f75d-4f6a-adcd-e32d3f8c05e4", - "name": "Spawner (116)", - "location": [6474, 74, -34], - "map": "Trammel", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Rat", "maxCount": 9, "probability": 100 }, - { "name": "IceSnake", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9e39061c-b9fa-4988-abef-2eac1f2344f3", - "name": "Spawner (116)", - "location": [6578, 183, 31], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Ferret", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cb0719bf-0812-4551-a287-3fe92e6dd66b", - "name": "Spawner (116)", - "location": [6475, 101, -44], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "ShadowWisp", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d4e26c54-212c-4dc9-bbb8-a6e2ecd02d55", - "name": "Spawner (116)", - "location": [6530, 139, -20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "d8d2b685-7458-494d-a596-90baea5763c8", "name": "Spawner (116)", "location": [6535, 179, 0], @@ -313,23 +243,7 @@ ] }, { - "$type": "Spawner", - "guid": "dbdcaa33-dad3-447b-be16-737b98732315", - "name": "Spawner (116)", - "location": [6532, 79, -10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "CrystalDaemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "ed761287-3375-4732-be78-2277dec45e2e", "name": "Spawner (116)", "location": [6502, 120, -10], @@ -340,8 +254,62 @@ "team": 0, "homeRange": 0, "walkingRange": 0, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "57899529-d774-4c8f-a5b3-263237a5cb74", + "name": "Spawner (116)", + "location": [6507, 151, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Protector", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dbdcaa33-dad3-447b-be16-737b98732315", + "name": "Spawner (116)", + "location": [6532, 79, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9e39061c-b9fa-4988-abef-2eac1f2344f3", + "name": "Spawner (116)", + "location": [6578, 183, 31], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Ferret", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6d47bfa4-0820-4805-865d-7a7f3e54e159", + "name": "Spawner (116)", + "location": [6510, 171, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Protector", "maxCount": 3, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/Reagents.json b/Distribution/Data/Spawns/post-uoml/trammel/Reagents.json similarity index 97% rename from Distribution/Data/Spawns/shared/trammel/Reagents.json rename to Distribution/Data/Spawns/post-uoml/trammel/Reagents.json index 7aff0872d..48b1a076a 100644 --- a/Distribution/Data/Spawns/shared/trammel/Reagents.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Reagents.json @@ -1,765 +1,6 @@ [ { - "$type": "Spawner", - "guid": "01bb3555-b7f0-4e46-b0ff-0b7266390cf3", - "name": "Spawner (129)", - "location": [4282, 3700, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a64e927-c349-447b-930c-c4de5d8d257d", - "name": "Spawner (129)", - "location": [2750, 368, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0c8ebc0f-8be3-45a7-85bf-51960355ac9b", - "name": "Spawner (129)", - "location": [3130, 116, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "125b2482-f852-4e28-b180-1718ebb4d062", - "name": "Spawner (129)", - "location": [1070, 2320, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "159fe79b-9b72-475b-8a75-7da4f5cca213", - "name": "Spawner (129)", - "location": [2082, 60, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15b9837c-a21b-46c8-9855-ac45895bdd07", - "name": "Spawner (129)", - "location": [5674, 3120, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1bba845c-2dc5-4395-b547-932b73723320", - "name": "Spawner (129)", - "location": [2474, 3956, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1fe40850-5b0c-4b89-880f-a64de82f0651", - "name": "Spawner (129)", - "location": [2054, 516, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23205131-5ad2-41a7-a07a-bb7fe741e100", - "name": "Spawner (129)", - "location": [1694, 688, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23902d00-71f8-4e7d-95d8-a237c88a77bd", - "name": "Spawner (129)", - "location": [1566, 1140, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2634bcca-5534-4964-97f3-e7f949d7f428", - "name": "Spawner (129)", - "location": [2226, 3532, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "29f7b655-522d-449b-a528-0bf2f4458aac", - "name": "Spawner (129)", - "location": [5926, 3888, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2bba2ae7-487a-434b-be26-4a3b8a33cbdf", - "name": "Spawner (129)", - "location": [2374, 652, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "30a36df2-0426-4686-b7b7-abff8e0db0fe", - "name": "Spawner (129)", - "location": [746, 636, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "31e3fd64-5ee4-43cb-95e5-10558cb9dfbe", - "name": "Spawner (129)", - "location": [1582, 2028, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3212484a-1a7f-4939-9704-f251885c3958", - "name": "Spawner (129)", - "location": [5934, 3436, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d68c680-4113-41e3-8d15-bda7b4f8631b", - "name": "Spawner (129)", - "location": [702, 1752, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41a38d34-c7a4-40b6-b95f-821fc2a8b77d", - "name": "Spawner (129)", - "location": [1134, 964, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4df7d7dd-b748-4680-9564-a561bd44a1ba", - "name": "Spawner (129)", - "location": [4710, 3796, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "53399cd4-2e2c-468d-9509-c794caa5a693", - "name": "Spawner (129)", - "location": [1410, 3796, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "546305f1-85c7-4072-864c-1a8c146b3b55", - "name": "Spawner (129)", - "location": [5618, 2744, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d8b589d-c874-4358-88f7-581e4a9f5c04", - "name": "Spawner (129)", - "location": [718, 1180, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60e9fe4d-adfa-43a4-b237-b07d751c58bb", - "name": "Spawner (129)", - "location": [3674, 1216, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6136db46-6bdb-40ca-8b33-9299b082585d", - "name": "Spawner (129)", - "location": [3714, 2152, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6a3759d1-5599-4a4d-9d3d-bb9dfd92bbf4", - "name": "Spawner (129)", - "location": [1934, 2328, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6c0091b8-6f09-437c-9e2f-7be1fbc92a9b", - "name": "Spawner (129)", - "location": [5474, 3904, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6d2e8680-ffd9-43ff-bc25-dc0d1b290e7d", - "name": "Spawner (129)", - "location": [4570, 3340, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6e42372d-6d54-44c8-8e89-af54e9a086f4", - "name": "Spawner (129)", - "location": [1846, 3260, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81761c3a-8870-44aa-a753-2730826c047e", - "name": "Spawner (129)", - "location": [1150, 1876, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "820cbf36-79e2-4d81-9e18-d6fe00e18223", - "name": "Spawner (129)", - "location": [3602, 2948, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84464230-dfb3-41cf-acba-ff69341c5099", - "name": "Spawner (129)", - "location": [1466, 3032, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "97319bac-f1df-456d-bf1c-9819cf6b1fdc", - "name": "Spawner (129)", - "location": [1490, 2472, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98e1387c-8ef9-4fb5-b816-eaa040824d11", - "name": "Spawner (129)", - "location": [1114, 1416, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "a3c31fb0-0ee3-4582-8fc1-9b80dca33840", "name": "Spawner (129)", "location": [2854, 816, 0], @@ -782,329 +23,7 @@ ] }, { - "$type": "Spawner", - "guid": "a78242f1-ab8f-46d6-b5d9-7652c2b01a86", - "name": "Spawner (129)", - "location": [366, 900, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a95d1ad3-6006-422c-8457-86b455be3589", - "name": "Spawner (129)", - "location": [5966, 2456, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abbe6e65-1093-417c-8cb5-57314087f26b", - "name": "Spawner (129)", - "location": [342, 1464, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad7ee5e1-f8ef-418f-a08e-7208a7f1e6db", - "name": "Spawner (129)", - "location": [5462, 3452, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae94454e-1590-489c-801b-cf6a1bf4942f", - "name": "Spawner (129)", - "location": [3538, 2536, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "af3c879d-6b46-424a-85a2-8178306e3ed0", - "name": "Spawner (129)", - "location": [1078, 2772, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b59164a9-46d0-4751-89b6-4f4cd3059f2e", - "name": "Spawner (129)", - "location": [1658, 236, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b6ca6516-11a1-4797-8dc5-9895256a30c2", - "name": "Spawner (129)", - "location": [1286, 504, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bbe4df4d-2e1d-47da-97e4-ef89fe7a85e6", - "name": "Spawner (129)", - "location": [4426, 1028, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd345daf-05c6-4a88-9294-23ededf39f0e", - "name": "Spawner (129)", - "location": [5234, 3068, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "be631686-1638-4057-89af-f53b90b3c57f", - "name": "Spawner (129)", - "location": [5326, 2492, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bed21e3a-c8fa-44c0-a584-8d95e8c12cb8", - "name": "Spawner (129)", - "location": [2734, 2168, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bfd11968-9616-4213-922f-b645b97863e6", - "name": "Spawner (129)", - "location": [3198, 504, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c1b8bcfb-3ee9-4a4a-8ae3-1a8d955cd210", - "name": "Spawner (129)", - "location": [2490, 1096, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "c230fcb8-e618-4853-9a08-1ea490cf6f9d", "name": "Spawner (129)", "location": [4590, 1448, 0], @@ -1127,10 +46,10 @@ ] }, { - "$type": "Spawner", - "guid": "c714dac4-dbfb-4a12-94ea-4536cd915ca7", + "type": "Spawner", + "guid": "c1b8bcfb-3ee9-4a4a-8ae3-1a8d955cd210", "name": "Spawner (129)", - "location": [2930, 3476, 0], + "location": [2490, 1096, 0], "map": "Trammel", "count": 160, "minDelay": "00:11:00", @@ -1150,10 +69,10 @@ ] }, { - "$type": "Spawner", - "guid": "c90c7638-9e53-460d-85c3-52002d27f48f", + "type": "Spawner", + "guid": "23902d00-71f8-4e7d-95d8-a237c88a77bd", "name": "Spawner (129)", - "location": [6042, 2916, 0], + "location": [1566, 1140, 0], "map": "Trammel", "count": 160, "minDelay": "00:11:00", @@ -1173,168 +92,7 @@ ] }, { - "$type": "Spawner", - "guid": "c9dc0305-842f-4328-b377-a50189c056db", - "name": "Spawner (129)", - "location": [1938, 1408, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d2e9abe0-41c7-48e3-a9a7-45433525764f", - "name": "Spawner (129)", - "location": [3994, 328, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d69ca3b5-5114-4aa8-9ff9-fa1b32848294", - "name": "Spawner (129)", - "location": [1122, 3524, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d69d8667-b6eb-46b6-a480-e67290f6b12e", - "name": "Spawner (129)", - "location": [2522, 84, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "de6f1dd7-f7f4-449b-96c6-8c4e93df7ddc", - "name": "Spawner (129)", - "location": [2062, 964, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ea100cd7-2afd-4001-a2db-aa9e7de29957", - "name": "Spawner (129)", - "location": [642, 2216, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f73e2029-5699-40d8-9c3e-71fee49654fa", - "name": "Spawner (129)", - "location": [4166, 584, 0], - "map": "Trammel", - "count": 160, - "minDelay": "00:11:00", - "maxDelay": "00:23:00", - "team": 0, - "homeRange": 200, - "walkingRange": 200, - "entries": [ - { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, - { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, - { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, - { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, - { "name": "Garlic", "maxCount": 160, "probability": 100 }, - { "name": "Ginseng", "maxCount": 160, "probability": 100 }, - { "name": "Nightshade", "maxCount": 160, "probability": 100 }, - { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "fe92f02b-a3b1-4bf2-add0-ca9a447f6982", "name": "Spawner (129)", "location": [1514, 1584, 0], @@ -1357,7 +115,996 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "c9dc0305-842f-4328-b377-a50189c056db", + "name": "Spawner (129)", + "location": [1938, 1408, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2bba2ae7-487a-434b-be26-4a3b8a33cbdf", + "name": "Spawner (129)", + "location": [2374, 652, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1fe40850-5b0c-4b89-880f-a64de82f0651", + "name": "Spawner (129)", + "location": [2054, 516, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b6ca6516-11a1-4797-8dc5-9895256a30c2", + "name": "Spawner (129)", + "location": [1286, 504, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "30a36df2-0426-4686-b7b7-abff8e0db0fe", + "name": "Spawner (129)", + "location": [746, 636, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41a38d34-c7a4-40b6-b95f-821fc2a8b77d", + "name": "Spawner (129)", + "location": [1134, 964, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d8b589d-c874-4358-88f7-581e4a9f5c04", + "name": "Spawner (129)", + "location": [718, 1180, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abbe6e65-1093-417c-8cb5-57314087f26b", + "name": "Spawner (129)", + "location": [342, 1464, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d68c680-4113-41e3-8d15-bda7b4f8631b", + "name": "Spawner (129)", + "location": [702, 1752, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98e1387c-8ef9-4fb5-b816-eaa040824d11", + "name": "Spawner (129)", + "location": [1114, 1416, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81761c3a-8870-44aa-a753-2730826c047e", + "name": "Spawner (129)", + "location": [1150, 1876, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31e3fd64-5ee4-43cb-95e5-10558cb9dfbe", + "name": "Spawner (129)", + "location": [1582, 2028, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a3759d1-5599-4a4d-9d3d-bb9dfd92bbf4", + "name": "Spawner (129)", + "location": [1934, 2328, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "97319bac-f1df-456d-bf1c-9819cf6b1fdc", + "name": "Spawner (129)", + "location": [1490, 2472, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "125b2482-f852-4e28-b180-1718ebb4d062", + "name": "Spawner (129)", + "location": [1070, 2320, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84464230-dfb3-41cf-acba-ff69341c5099", + "name": "Spawner (129)", + "location": [1466, 3032, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "de6f1dd7-f7f4-449b-96c6-8c4e93df7ddc", + "name": "Spawner (129)", + "location": [2062, 964, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6e42372d-6d54-44c8-8e89-af54e9a086f4", + "name": "Spawner (129)", + "location": [1846, 3260, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af3c879d-6b46-424a-85a2-8178306e3ed0", + "name": "Spawner (129)", + "location": [1078, 2772, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4df7d7dd-b748-4680-9564-a561bd44a1ba", + "name": "Spawner (129)", + "location": [4710, 3796, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "29f7b655-522d-449b-a528-0bf2f4458aac", + "name": "Spawner (129)", + "location": [5926, 3888, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c0091b8-6f09-437c-9e2f-7be1fbc92a9b", + "name": "Spawner (129)", + "location": [5474, 3904, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3212484a-1a7f-4939-9704-f251885c3958", + "name": "Spawner (129)", + "location": [5934, 3436, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad7ee5e1-f8ef-418f-a08e-7208a7f1e6db", + "name": "Spawner (129)", + "location": [5462, 3452, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15b9837c-a21b-46c8-9855-ac45895bdd07", + "name": "Spawner (129)", + "location": [5674, 3120, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "546305f1-85c7-4072-864c-1a8c146b3b55", + "name": "Spawner (129)", + "location": [5618, 2744, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be631686-1638-4057-89af-f53b90b3c57f", + "name": "Spawner (129)", + "location": [5326, 2492, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a95d1ad3-6006-422c-8457-86b455be3589", + "name": "Spawner (129)", + "location": [5966, 2456, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd345daf-05c6-4a88-9294-23ededf39f0e", + "name": "Spawner (129)", + "location": [5234, 3068, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c90c7638-9e53-460d-85c3-52002d27f48f", + "name": "Spawner (129)", + "location": [6042, 2916, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "820cbf36-79e2-4d81-9e18-d6fe00e18223", + "name": "Spawner (129)", + "location": [3602, 2948, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae94454e-1590-489c-801b-cf6a1bf4942f", + "name": "Spawner (129)", + "location": [3538, 2536, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bed21e3a-c8fa-44c0-a584-8d95e8c12cb8", + "name": "Spawner (129)", + "location": [2734, 2168, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6136db46-6bdb-40ca-8b33-9299b082585d", + "name": "Spawner (129)", + "location": [3714, 2152, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60e9fe4d-adfa-43a4-b237-b07d751c58bb", + "name": "Spawner (129)", + "location": [3674, 1216, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2e9abe0-41c7-48e3-a9a7-45433525764f", + "name": "Spawner (129)", + "location": [3994, 328, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f73e2029-5699-40d8-9c3e-71fee49654fa", + "name": "Spawner (129)", + "location": [4166, 584, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bfd11968-9616-4213-922f-b645b97863e6", + "name": "Spawner (129)", + "location": [3198, 504, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a64e927-c349-447b-930c-c4de5d8d257d", + "name": "Spawner (129)", + "location": [2750, 368, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23205131-5ad2-41a7-a07a-bb7fe741e100", + "name": "Spawner (129)", + "location": [1694, 688, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a78242f1-ab8f-46d6-b5d9-7652c2b01a86", + "name": "Spawner (129)", + "location": [366, 900, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea100cd7-2afd-4001-a2db-aa9e7de29957", + "name": "Spawner (129)", + "location": [642, 2216, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2634bcca-5534-4964-97f3-e7f949d7f428", + "name": "Spawner (129)", + "location": [2226, 3532, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fedbe286-f965-4ce8-95d2-0e78001edb80", "name": "Spawner (129)", "location": [1850, 2796, 0], @@ -1378,5 +1125,258 @@ { "name": "Nightshade", "maxCount": 160, "probability": 100 }, { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "b59164a9-46d0-4751-89b6-4f4cd3059f2e", + "name": "Spawner (129)", + "location": [1658, 236, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbe4df4d-2e1d-47da-97e4-ef89fe7a85e6", + "name": "Spawner (129)", + "location": [4426, 1028, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d69d8667-b6eb-46b6-a480-e67290f6b12e", + "name": "Spawner (129)", + "location": [2522, 84, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c8ebc0f-8be3-45a7-85bf-51960355ac9b", + "name": "Spawner (129)", + "location": [3130, 116, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "159fe79b-9b72-475b-8a75-7da4f5cca213", + "name": "Spawner (129)", + "location": [2082, 60, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c714dac4-dbfb-4a12-94ea-4536cd915ca7", + "name": "Spawner (129)", + "location": [2930, 3476, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "53399cd4-2e2c-468d-9509-c794caa5a693", + "name": "Spawner (129)", + "location": [1410, 3796, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d69ca3b5-5114-4aa8-9ff9-fa1b32848294", + "name": "Spawner (129)", + "location": [1122, 3524, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1bba845c-2dc5-4395-b547-932b73723320", + "name": "Spawner (129)", + "location": [2474, 3956, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d2e8680-ffd9-43ff-bc25-dc0d1b290e7d", + "name": "Spawner (129)", + "location": [4570, 3340, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01bb3555-b7f0-4e46-b0ff-0b7266390cf3", + "name": "Spawner (129)", + "location": [4282, 3700, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/Sanctuary.json b/Distribution/Data/Spawns/post-uoml/trammel/Sanctuary.json similarity index 97% rename from Distribution/Data/Spawns/shared/trammel/Sanctuary.json rename to Distribution/Data/Spawns/post-uoml/trammel/Sanctuary.json index 1af2f9e18..869987b1c 100644 --- a/Distribution/Data/Spawns/shared/trammel/Sanctuary.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Sanctuary.json @@ -1,6 +1,56 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "499d7a5c-d73c-4e43-889f-f88bc94cf40e", + "name": "Spawner (117)", + "location": [6263, 118, -10], + "map": "Trammel", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Chicken", "maxCount": 6, "probability": 100 }, + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 3, "probability": 100 }, + { "name": "Cat", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 6, "probability": 100 }, + { "name": "Bull", "maxCount": 6, "probability": 100 }, + { "name": "Pig", "maxCount": 3, "probability": 100 }, + { "name": "Boar", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "18850a42-5089-43d9-9f0c-1adca014ed3e", + "name": "Spawner (117)", + "location": [6349, 89, -20], + "map": "Trammel", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Ratman", "maxCount": 18, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 18, "probability": 100 }, + { "name": "Chiikkaha", "maxCount": 18, "probability": 100 }, + { "name": "Titan", "maxCount": 18, "probability": 100 }, + { "name": "Cyclops", "maxCount": 18, "probability": 100 }, + { "name": "Rat", "maxCount": 18, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 4, "probability": 100 }, + { "name": "Chiikkaha", "maxCount": 2, "probability": 100 }, + { "name": "Titan", "maxCount": 2, "probability": 100 }, + { "name": "Cyclops", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "01891c2e-7b0f-4f3e-81a6-199eb062ec9c", "name": "Spawner (117)", "location": [6168, 117, 0], @@ -30,7 +80,60 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "e35998b5-c9fa-4547-b793-3efeeca0253f", + "name": "Spawner (117)", + "location": [6189, 71, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "OgreLord", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cow", "maxCount": 4, "probability": 100 }, + { "name": "Bull", "maxCount": 4, "probability": 100 }, + { "name": "Pig", "maxCount": 4, "probability": 100 }, + { "name": "Chicken", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28f37a01-81c7-4090-a181-5cd3f670ef72", + "name": "Spawner (117)", + "location": [6187, 101, 0], + "map": "Trammel", + "count": 34, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Titan", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 10, "probability": 100 }, + { "name": "MougGuur", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Chicken", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 }, + { "name": "Cow", "maxCount": 10, "probability": 100 }, + { "name": "Bull", "maxCount": 10, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 7, "probability": 100 }, + { "name": "OrcBrute", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "0983061e-4deb-4750-8f2b-ee10756735ec", "name": "Spawner (117)", "location": [6184, 29, 0], @@ -50,34 +153,7 @@ ] }, { - "$type": "Spawner", - "guid": "18850a42-5089-43d9-9f0c-1adca014ed3e", - "name": "Spawner (117)", - "location": [6349, 89, -20], - "map": "Trammel", - "count": 30, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 70, - "walkingRange": 70, - "entries": [ - { "name": "Ratman", "maxCount": 18, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 18, "probability": 100 }, - { "name": "Chiikkaha", "maxCount": 18, "probability": 100 }, - { "name": "Titan", "maxCount": 18, "probability": 100 }, - { "name": "Cyclops", "maxCount": 18, "probability": 100 }, - { "name": "Rat", "maxCount": 18, "probability": 100 }, - { "name": "Ratman", "maxCount": 4, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 4, "probability": 100 }, - { "name": "Chiikkaha", "maxCount": 2, "probability": 100 }, - { "name": "Titan", "maxCount": 2, "probability": 100 }, - { "name": "Cyclops", "maxCount": 2, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "2862d617-a6ad-449e-b063-3a11f39753d7", "name": "Spawner (117)", "location": [6265, 48, -10], @@ -114,60 +190,7 @@ ] }, { - "$type": "Spawner", - "guid": "28f37a01-81c7-4090-a181-5cd3f670ef72", - "name": "Spawner (117)", - "location": [6187, 101, 0], - "map": "Trammel", - "count": 34, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Titan", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Doppleganger", "maxCount": 10, "probability": 100 }, - { "name": "MougGuur", "maxCount": 10, "probability": 100 }, - { "name": "Dog", "maxCount": 10, "probability": 100 }, - { "name": "Chicken", "maxCount": 10, "probability": 100 }, - { "name": "Rat", "maxCount": 10, "probability": 100 }, - { "name": "Cow", "maxCount": 10, "probability": 100 }, - { "name": "Bull", "maxCount": 10, "probability": 100 }, - { "name": "OrcBomber", "maxCount": 7, "probability": 100 }, - { "name": "OrcBrute", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "499d7a5c-d73c-4e43-889f-f88bc94cf40e", - "name": "Spawner (117)", - "location": [6263, 118, -10], - "map": "Trammel", - "count": 24, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Chicken", "maxCount": 6, "probability": 100 }, - { "name": "Bird", "maxCount": 6, "probability": 100 }, - { "name": "Dog", "maxCount": 3, "probability": 100 }, - { "name": "Cat", "maxCount": 3, "probability": 100 }, - { "name": "Cow", "maxCount": 6, "probability": 100 }, - { "name": "Bull", "maxCount": 6, "probability": 100 }, - { "name": "Pig", "maxCount": 3, "probability": 100 }, - { "name": "Boar", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "5f510865-94bd-49b7-90bf-c24131ef1151", "name": "Spawner (117)", "location": [6191, 155, 0], @@ -195,28 +218,5 @@ { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, { "name": "Snake", "maxCount": 3, "probability": 100 } ] - }, - { - "$type": "Spawner", - "guid": "e35998b5-c9fa-4547-b793-3efeeca0253f", - "name": "Spawner (117)", - "location": [6189, 71, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "OgreLord", "maxCount": 8, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 }, - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Cow", "maxCount": 4, "probability": 100 }, - { "name": "Bull", "maxCount": 4, "probability": 100 }, - { "name": "Pig", "maxCount": 4, "probability": 100 }, - { "name": "Chicken", "maxCount": 4, "probability": 100 } - ] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/SeaLife.json b/Distribution/Data/Spawns/post-uoml/trammel/SeaLife.json similarity index 96% rename from Distribution/Data/Spawns/shared/trammel/SeaLife.json rename to Distribution/Data/Spawns/post-uoml/trammel/SeaLife.json index 4bffb5ecc..5cc832297 100644 --- a/Distribution/Data/Spawns/shared/trammel/SeaLife.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/SeaLife.json @@ -1,2148 +1,6 @@ [ { - "$type": "Spawner", - "guid": "01b080a0-d726-4fd8-92b2-fe774c8c5646", - "name": "Spawner (118)", - "location": [840, 2863, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "062057b0-ded5-4aed-8ed7-1ea3204bbc27", - "name": "Spawner (118)", - "location": [2368, 2799, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0725865a-b679-4d54-ad18-8238b2eb6f38", - "name": "Spawner (118)", - "location": [4112, 791, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "07b89a5e-cd89-4448-8b46-56bdd1d989a8", - "name": "Spawner (118)", - "location": [3267, 1717, -5], - "map": "Trammel", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 130, - "walkingRange": 130, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, - { "name": "Kraken", "maxCount": 13, "probability": 100 }, - { "name": "Dolphin", "maxCount": 4, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a3d212e-7f85-4ed1-84f6-e0cab9db1504", - "name": "Spawner (118)", - "location": [107, 909, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0e68a1d8-740d-4fef-b2a6-204c774cbc0a", - "name": "Spawner (118)", - "location": [2792, 2951, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "103196d1-0c89-4383-9e0e-a0b01aaf3c20", - "name": "Spawner (118)", - "location": [3408, 1407, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10b4a195-d28e-4534-b058-ce0d08ad7ae4", - "name": "Spawner (118)", - "location": [2395, 1405, -5], - "map": "Trammel", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 130, - "walkingRange": 130, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, - { "name": "Kraken", "maxCount": 13, "probability": 100 }, - { "name": "Dolphin", "maxCount": 4, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1313a5ba-08e2-450f-850e-e3b8dd09d2ac", - "name": "Spawner (118)", - "location": [2392, 2031, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "144aa07d-868c-4d0a-b4b8-fb127d1f9651", - "name": "Spawner (118)", - "location": [3240, 3151, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15221ce6-7198-4de6-b750-6610f6680d51", - "name": "Spawner (118)", - "location": [931, 141, -5], - "map": "Trammel", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 130, - "walkingRange": 130, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, - { "name": "Kraken", "maxCount": 13, "probability": 100 }, - { "name": "Dolphin", "maxCount": 4, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1a02156e-b816-4c27-8dd0-f377e6378524", - "name": "Spawner (118)", - "location": [2291, 3181, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1b48857b-fc9d-42ef-8d0b-e372a256d37b", - "name": "Spawner (118)", - "location": [3688, 439, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1ce072bf-6016-4902-bb6c-7cf6626eeb1f", - "name": "Spawner (118)", - "location": [168, 1743, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d2e0d12-3e70-4572-8cae-ccc6f7bfd585", - "name": "Spawner (118)", - "location": [2208, 1703, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1e293bc0-3f8c-44b5-b5d3-e13f6c03353f", - "name": "Spawner (118)", - "location": [184, 191, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2191b9c5-c69e-4a83-bc9f-f17cced550b3", - "name": "Spawner (118)", - "location": [3811, 109, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23637b7b-52d1-4ae2-95ff-e08be730be14", - "name": "Spawner (118)", - "location": [979, 3997, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23fac61f-7a20-4329-8a8e-6136e0fab29e", - "name": "Spawner (118)", - "location": [4888, 375, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2640ac8d-34c1-4869-8f3e-7533872fc883", - "name": "Spawner (118)", - "location": [4008, 2767, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ecb155b-60d2-4de3-9c43-330d492797ac", - "name": "Spawner (118)", - "location": [4955, 3997, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2f9961f3-4539-4558-a08d-e55c0ffabb57", - "name": "Spawner (118)", - "location": [2752, 1455, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2fb5cbd9-1d4b-4e04-a3e3-a90e53dc60ba", - "name": "Spawner (118)", - "location": [3328, 2063, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "301c54c5-7fe3-4538-903c-396311156077", - "name": "Spawner (118)", - "location": [147, 3213, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "306cfd71-75e0-43de-b0b0-16c5271b8da8", - "name": "Spawner (118)", - "location": [4896, 2119, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33bc2f0f-243c-40d2-bf2d-dad2466fa1dc", - "name": "Spawner (118)", - "location": [896, 3695, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "39e15c45-9188-4dbf-aab7-17a8c633911c", - "name": "Spawner (118)", - "location": [200, 3879, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3f604920-6da5-4233-ac70-c870acfa2c9a", - "name": "Spawner (118)", - "location": [208, 2503, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "405e5843-eff4-41c2-b17a-f3d9468fa0b2", - "name": "Spawner (118)", - "location": [3088, 2759, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40854e37-49b5-485d-b2e4-c514f7e04657", - "name": "Spawner (118)", - "location": [4904, 759, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40a637b6-5c6b-4a77-885a-da2491d97e5f", - "name": "Spawner (118)", - "location": [4400, 2527, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4209e2ed-53c1-45f1-9987-49f5ba958ec8", - "name": "Spawner (118)", - "location": [544, 3719, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4707f57e-e053-4515-8905-ead9aab81c34", - "name": "Spawner (118)", - "location": [4731, 3997, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4a76c8c9-5e59-4915-9f25-d9503f076b0d", - "name": "Spawner (118)", - "location": [200, 2103, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4c974c21-e97f-4ab5-a375-8d24a2b25f8f", - "name": "Spawner (118)", - "location": [4032, 2039, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4f5e480b-e325-405d-9250-55050cb07bab", - "name": "Spawner (118)", - "location": [824, 3287, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4f7e8b07-f31c-418d-a11f-a45c0b4096e1", - "name": "Spawner (118)", - "location": [600, 2511, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4fa56369-c24e-46da-872d-f2a43d43ceee", - "name": "Spawner (118)", - "location": [192, 2895, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4fbde8d9-735a-41d0-a2a9-e2d1a0a201e0", - "name": "Spawner (118)", - "location": [4392, 823, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "517db203-cdaa-41f6-93f2-906280e1344d", - "name": "Spawner (118)", - "location": [4864, 2831, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54c24f2b-a9b4-4a6b-a005-4974491cb4b8", - "name": "Spawner (118)", - "location": [2320, 4007, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "55c44ca9-d830-4a74-bb0f-e529edef11e1", - "name": "Spawner (118)", - "location": [3672, 1535, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5af749ec-cde5-4635-a69c-8d9df732688c", - "name": "Spawner (118)", - "location": [4363, 1981, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5af7b6a7-f921-4188-9f09-c7d45bf22b58", - "name": "Spawner (118)", - "location": [3344, 3479, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5b4931fa-70ca-4abc-b62a-4496f8d6bd3b", - "name": "Spawner (118)", - "location": [3891, 733, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d9c9755-604e-4cad-b35c-f8f910ff42f1", - "name": "Spawner (118)", - "location": [4896, 1519, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5db1f5df-d7c7-4d1b-a938-a0008044eba8", - "name": "Spawner (118)", - "location": [4016, 1031, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e4f0bf4-79c5-463f-8c3e-7dc5c38c9e0f", - "name": "Spawner (118)", - "location": [2768, 3847, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bee85a9-3484-41ed-bf30-bc94accd887c", - "name": "Spawner (118)", - "location": [4307, 3965, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6cdb8fc8-8406-4535-a2e1-7f663e171f39", - "name": "Spawner (118)", - "location": [3123, 1485, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6d3d6b34-3a34-4b08-afaf-2d7d27071c5a", - "name": "Spawner (118)", - "location": [3984, 2423, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6ef0ce60-872f-47a8-b8c7-288de410fd37", - "name": "Spawner (118)", - "location": [1580, 75, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6ffba6b0-025d-4465-9bf8-1c7e22f8dea1", - "name": "Spawner (118)", - "location": [4995, 1829, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "71147c8d-bc3f-4d7f-a2e8-04b3cd99fc21", - "name": "Spawner (118)", - "location": [3568, 3215, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "71890746-952f-4b24-a473-76515b23e7e6", - "name": "Spawner (118)", - "location": [1336, 79, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "769bcacc-1b82-4198-b3c5-6f19c298f770", - "name": "Spawner (118)", - "location": [1187, 3885, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "796cf3aa-e1ce-4249-b39a-97246dbd2a82", - "name": "Spawner (118)", - "location": [3888, 3167, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7be74f9e-3d46-49c1-8845-f2891a31472b", - "name": "Spawner (118)", - "location": [139, 1173, -5], - "map": "Trammel", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 130, - "walkingRange": 130, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, - { "name": "Kraken", "maxCount": 13, "probability": 100 }, - { "name": "Dolphin", "maxCount": 4, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c9ea5dd-8743-4733-990c-44ba4af4c3b0", - "name": "Spawner (118)", - "location": [4536, 2847, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7cceffb4-478d-44b1-a66f-c932e0c8d7e0", - "name": "Spawner (118)", - "location": [1792, 79, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d4b5771-7c1e-4bf1-a7ff-40815ccc6e43", - "name": "Spawner (118)", - "location": [3256, 1031, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8158ece9-f497-4858-a0a2-ce68e1bd9b55", - "name": "Spawner (118)", - "location": [2400, 2399, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d36c3ef-0186-4aa2-8248-e32fa525c5d1", - "name": "Spawner (118)", - "location": [3603, 125, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8df655e8-c716-4ad9-8ed9-4a5fc03ae90e", - "name": "Spawner (118)", - "location": [4912, 2471, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8f431e71-483c-4493-9f2f-80e89d6f0554", - "name": "Spawner (118)", - "location": [4352, 215, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "934908c0-3860-4133-a090-ea95e09e10a0", - "name": "Spawner (118)", - "location": [512, 2919, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95014bb0-76a6-4bc9-8b10-79296fa33ad6", - "name": "Spawner (118)", - "location": [2744, 2567, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9576fcbe-ac4f-4382-931a-18d85664a5c4", - "name": "Spawner (118)", - "location": [2971, 1605, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98a90cb4-d8a4-46cb-a791-3767989189a7", - "name": "Spawner (118)", - "location": [3576, 823, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9a4c154b-5279-42ee-bdd6-98873ee0bcec", - "name": "Spawner (118)", - "location": [3176, 2383, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9aac32c6-3e67-4413-9ded-df193f4cd205", - "name": "Spawner (118)", - "location": [4667, 109, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9ab3a044-b6f4-4fda-b048-e4da84173f15", - "name": "Spawner (118)", - "location": [4275, 1309, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9cf6fa1b-6813-491f-ab42-5308c43fe9f5", - "name": "Spawner (118)", - "location": [731, 3989, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a1d3c81c-4063-4c1b-a855-bc98dc5f1c6d", - "name": "Spawner (118)", - "location": [2624, 1775, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a432cdec-33eb-492d-93a8-6c8557cf643c", - "name": "Spawner (118)", - "location": [3560, 3871, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a62d0aec-b3d0-4ef7-b81a-304bc4e317e5", - "name": "Spawner (118)", - "location": [4192, 2975, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a84139ff-b09e-4b56-b69c-5a2a19ae0cab", - "name": "Spawner (118)", - "location": [3968, 3895, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8f72a6c-ac20-4b11-9f88-d7ac1fcc16d3", - "name": "Spawner (118)", - "location": [4283, 2221, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa8fc3ef-70ac-4310-aca2-44e2f5756e2f", - "name": "Spawner (118)", - "location": [3776, 3519, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aacc2b81-5061-4474-af3a-3128191b5515", - "name": "Spawner (118)", - "location": [2952, 1239, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abe19479-98b3-4b2d-a030-b0719a2016e4", - "name": "Spawner (118)", - "location": [2072, 2527, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abfabdf0-bf96-4776-8036-8bd8449fa042", - "name": "Spawner (118)", - "location": [592, 223, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ac69ff02-6f53-401b-830d-e5cd4b8cfb6d", - "name": "Spawner (118)", - "location": [2608, 3295, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b258c234-07e5-4a5e-8083-e95cee0aa7c2", - "name": "Spawner (118)", - "location": [4048, 63, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b70aa657-8435-4c48-bae7-38c62fd87d02", - "name": "Spawner (118)", - "location": [4904, 1127, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b85b2928-f90d-4814-aee9-099661bb58bc", - "name": "Spawner (118)", - "location": [4984, 3567, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bee3753d-3d3b-4a69-94b7-a9396767b655", - "name": "Spawner (118)", - "location": [4600, 1751, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bee4ac0d-312f-4bed-8690-545f989cbf2b", - "name": "Spawner (118)", - "location": [3992, 1447, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf8e1268-6e29-4feb-8ad5-4f7f2e0b81d0", - "name": "Spawner (118)", - "location": [3931, 1757, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c00499fb-21a8-48a6-9140-72e1b964da3d", - "name": "Spawner (118)", - "location": [1866, 1798, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c5a35e9a-e886-4acb-be41-17fbfc5c28d2", - "name": "Spawner (118)", - "location": [2227, 3533, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ce84448b-cd5c-4db2-9623-807a216f2e65", - "name": "Spawner (118)", - "location": [3520, 2263, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ce95bb5c-8753-4ae6-a109-5abcf47d36c9", - "name": "Spawner (118)", - "location": [3168, 3871, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d7e454a9-f662-438f-aedf-1f7813c34536", - "name": "Spawner (118)", - "location": [4992, 3791, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da0f30cb-55c4-42a9-896a-1327277ed67e", - "name": "Spawner (118)", - "location": [200, 559, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc9c68d1-115b-4d9f-a75a-1c7b914f865d", - "name": "Spawner (118)", - "location": [5003, 101, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dcb85ac4-7906-49f5-9e13-223112e63dc3", - "name": "Spawner (118)", - "location": [4512, 583, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd078e33-7679-49ae-bc54-4be36e1e3c61", - "name": "Spawner (118)", - "location": [1891, 3597, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "def01738-95eb-4463-9025-57f58ab767c8", - "name": "Spawner (118)", - "location": [3008, 1895, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df266a1d-632f-4a87-a31d-1e5dc6bccf9f", - "name": "Spawner (118)", - "location": [4896, 3215, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e68e3874-1188-4c5b-9909-4539e8119a58", - "name": "Spawner (118)", - "location": [2931, 3245, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eac92a71-5b24-4bb0-9518-d07ea06e1deb", - "name": "Spawner (118)", - "location": [1720, 3879, -5], - "map": "Trammel", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 175, - "walkingRange": 175, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, - { "name": "Kraken", "maxCount": 15, "probability": 100 }, - { "name": "Dolphin", "maxCount": 6, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "ebfc8a09-79e6-4af0-9436-4728c1c8f90f", "name": "Spawner (118)", "location": [1395, 3421, -5], @@ -2163,10 +21,10 @@ ] }, { - "$type": "Spawner", - "guid": "ef51ae2c-fc05-45af-8b7c-c8db93d36a3d", + "type": "Spawner", + "guid": "23fac61f-7a20-4329-8a8e-6136e0fab29e", "name": "Spawner (118)", - "location": [480, 3351, -5], + "location": [4888, 375, -5], "map": "Trammel", "count": 22, "minDelay": "00:05:00", @@ -2184,52 +42,10 @@ ] }, { - "$type": "Spawner", - "guid": "ef98baa5-fd8a-4c7e-abe6-2f9e2972e0e6", + "type": "Spawner", + "guid": "8f431e71-483c-4493-9f2f-80e89d6f0554", "name": "Spawner (118)", - "location": [496, 1879, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f1cbdc49-7c36-4f0a-9c71-0c7ac05f6519", - "name": "Spawner (118)", - "location": [2288, 3767, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f295d667-2c49-4737-be40-1e50874b6308", - "name": "Spawner (118)", - "location": [4624, 2199, -5], + "location": [4352, 215, -5], "map": "Trammel", "count": 22, "minDelay": "00:05:00", @@ -2247,10 +63,10 @@ ] }, { - "$type": "Spawner", - "guid": "f2d164cb-e8de-4998-b1d2-457408ba7d68", + "type": "Spawner", + "guid": "1b48857b-fc9d-42ef-8d0b-e372a256d37b", "name": "Spawner (118)", - "location": [176, 3551, -5], + "location": [3688, 439, -5], "map": "Trammel", "count": 22, "minDelay": "00:05:00", @@ -2268,31 +84,10 @@ ] }, { - "$type": "Spawner", - "guid": "f334a29c-42e6-4c51-a19a-9bdbd57efb70", + "type": "Spawner", + "guid": "dcb85ac4-7906-49f5-9e13-223112e63dc3", "name": "Spawner (118)", - "location": [2008, 4007, -5], - "map": "Trammel", - "count": 13, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, - { "name": "Kraken", "maxCount": 10, "probability": 100 }, - { "name": "Dolphin", "maxCount": 2, "probability": 100 }, - { "name": "SeaHorse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f3473140-bfcf-4672-b602-5a26d6273ff9", - "name": "Spawner (118)", - "location": [3616, 1807, -5], + "location": [4512, 583, -5], "map": "Trammel", "count": 22, "minDelay": "00:05:00", @@ -2310,7 +105,112 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "40854e37-49b5-485d-b2e4-c514f7e04657", + "name": "Spawner (118)", + "location": [4904, 759, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b70aa657-8435-4c48-bae7-38c62fd87d02", + "name": "Spawner (118)", + "location": [4904, 1127, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98a90cb4-d8a4-46cb-a791-3767989189a7", + "name": "Spawner (118)", + "location": [3576, 823, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5db1f5df-d7c7-4d1b-a938-a0008044eba8", + "name": "Spawner (118)", + "location": [4016, 1031, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bee4ac0d-312f-4bed-8690-545f989cbf2b", + "name": "Spawner (118)", + "location": [3992, 1447, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fa433125-925e-4c08-b036-6d42c8c2ac42", "name": "Spawner (118)", "location": [4232, 1655, -5], @@ -2331,7 +231,973 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "bee3753d-3d3b-4a69-94b7-a9396767b655", + "name": "Spawner (118)", + "location": [4600, 1751, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d9c9755-604e-4cad-b35c-f8f910ff42f1", + "name": "Spawner (118)", + "location": [4896, 1519, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3473140-bfcf-4672-b602-5a26d6273ff9", + "name": "Spawner (118)", + "location": [3616, 1807, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c974c21-e97f-4ab5-a375-8d24a2b25f8f", + "name": "Spawner (118)", + "location": [4032, 2039, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f295d667-2c49-4737-be40-1e50874b6308", + "name": "Spawner (118)", + "location": [4624, 2199, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40a637b6-5c6b-4a77-885a-da2491d97e5f", + "name": "Spawner (118)", + "location": [4400, 2527, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d3d6b34-3a34-4b08-afaf-2d7d27071c5a", + "name": "Spawner (118)", + "location": [3984, 2423, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "306cfd71-75e0-43de-b0b0-16c5271b8da8", + "name": "Spawner (118)", + "location": [4896, 2119, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8df655e8-c716-4ad9-8ed9-4a5fc03ae90e", + "name": "Spawner (118)", + "location": [4912, 2471, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "517db203-cdaa-41f6-93f2-906280e1344d", + "name": "Spawner (118)", + "location": [4864, 2831, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c9ea5dd-8743-4733-990c-44ba4af4c3b0", + "name": "Spawner (118)", + "location": [4536, 2847, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df266a1d-632f-4a87-a31d-1e5dc6bccf9f", + "name": "Spawner (118)", + "location": [4896, 3215, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a62d0aec-b3d0-4ef7-b81a-304bc4e317e5", + "name": "Spawner (118)", + "location": [4192, 2975, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2640ac8d-34c1-4869-8f3e-7533872fc883", + "name": "Spawner (118)", + "location": [4008, 2767, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2fb5cbd9-1d4b-4e04-a3e3-a90e53dc60ba", + "name": "Spawner (118)", + "location": [3328, 2063, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a4c154b-5279-42ee-bdd6-98873ee0bcec", + "name": "Spawner (118)", + "location": [3176, 2383, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "405e5843-eff4-41c2-b17a-f3d9468fa0b2", + "name": "Spawner (118)", + "location": [3088, 2759, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "796cf3aa-e1ce-4249-b39a-97246dbd2a82", + "name": "Spawner (118)", + "location": [3888, 3167, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a84139ff-b09e-4b56-b69c-5a2a19ae0cab", + "name": "Spawner (118)", + "location": [3968, 3895, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "144aa07d-868c-4d0a-b4b8-fb127d1f9651", + "name": "Spawner (118)", + "location": [3240, 3151, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa8fc3ef-70ac-4310-aca2-44e2f5756e2f", + "name": "Spawner (118)", + "location": [3776, 3519, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5af7b6a7-f921-4188-9f09-c7d45bf22b58", + "name": "Spawner (118)", + "location": [3344, 3479, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e68a1d8-740d-4fef-b2a6-204c774cbc0a", + "name": "Spawner (118)", + "location": [2792, 2951, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ac69ff02-6f53-401b-830d-e5cd4b8cfb6d", + "name": "Spawner (118)", + "location": [2608, 3295, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95014bb0-76a6-4bc9-8b10-79296fa33ad6", + "name": "Spawner (118)", + "location": [2744, 2567, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "062057b0-ded5-4aed-8ed7-1ea3204bbc27", + "name": "Spawner (118)", + "location": [2368, 2799, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a432cdec-33eb-492d-93a8-6c8557cf643c", + "name": "Spawner (118)", + "location": [3560, 3871, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce95bb5c-8753-4ae6-a109-5abcf47d36c9", + "name": "Spawner (118)", + "location": [3168, 3871, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e4f0bf4-79c5-463f-8c3e-7dc5c38c9e0f", + "name": "Spawner (118)", + "location": [2768, 3847, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eac92a71-5b24-4bb0-9518-d07ea06e1deb", + "name": "Spawner (118)", + "location": [1720, 3879, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39e15c45-9188-4dbf-aab7-17a8c633911c", + "name": "Spawner (118)", + "location": [200, 3879, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4209e2ed-53c1-45f1-9987-49f5ba958ec8", + "name": "Spawner (118)", + "location": [544, 3719, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33bc2f0f-243c-40d2-bf2d-dad2466fa1dc", + "name": "Spawner (118)", + "location": [896, 3695, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f5e480b-e325-405d-9250-55050cb07bab", + "name": "Spawner (118)", + "location": [824, 3287, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2d164cb-e8de-4998-b1d2-457408ba7d68", + "name": "Spawner (118)", + "location": [176, 3551, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef51ae2c-fc05-45af-8b7c-c8db93d36a3d", + "name": "Spawner (118)", + "location": [480, 3351, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "934908c0-3860-4133-a090-ea95e09e10a0", + "name": "Spawner (118)", + "location": [512, 2919, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fa56369-c24e-46da-872d-f2a43d43ceee", + "name": "Spawner (118)", + "location": [192, 2895, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3f604920-6da5-4233-ac70-c870acfa2c9a", + "name": "Spawner (118)", + "location": [208, 2503, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01b080a0-d726-4fd8-92b2-fe774c8c5646", + "name": "Spawner (118)", + "location": [840, 2863, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f7e8b07-f31c-418d-a11f-a45c0b4096e1", + "name": "Spawner (118)", + "location": [600, 2511, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a76c8c9-5e59-4915-9f25-d9503f076b0d", + "name": "Spawner (118)", + "location": [200, 2103, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ce072bf-6016-4902-bb6c-7cf6626eeb1f", + "name": "Spawner (118)", + "location": [168, 1743, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da0f30cb-55c4-42a9-896a-1327277ed67e", + "name": "Spawner (118)", + "location": [200, 559, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1e293bc0-3f8c-44b5-b5d3-e13f6c03353f", + "name": "Spawner (118)", + "location": [184, 191, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abfabdf0-bf96-4776-8036-8bd8449fa042", + "name": "Spawner (118)", + "location": [592, 223, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fafb6838-5696-495e-8633-ae7909d2607b", "name": "Spawner (118)", "location": [496, 527, -5], @@ -2352,7 +1218,1120 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "8158ece9-f497-4858-a0a2-ce68e1bd9b55", + "name": "Spawner (118)", + "location": [2400, 2399, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "def01738-95eb-4463-9025-57f58ab767c8", + "name": "Spawner (118)", + "location": [3008, 1895, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1313a5ba-08e2-450f-850e-e3b8dd09d2ac", + "name": "Spawner (118)", + "location": [2392, 2031, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1d3c81c-4063-4c1b-a855-bc98dc5f1c6d", + "name": "Spawner (118)", + "location": [2624, 1775, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "103196d1-0c89-4383-9e0e-a0b01aaf3c20", + "name": "Spawner (118)", + "location": [3408, 1407, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d4b5771-7c1e-4bf1-a7ff-40815ccc6e43", + "name": "Spawner (118)", + "location": [3256, 1031, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aacc2b81-5061-4474-af3a-3128191b5515", + "name": "Spawner (118)", + "location": [2952, 1239, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f9961f3-4539-4558-a08d-e55c0ffabb57", + "name": "Spawner (118)", + "location": [2752, 1455, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d2e0d12-3e70-4572-8cae-ccc6f7bfd585", + "name": "Spawner (118)", + "location": [2208, 1703, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c00499fb-21a8-48a6-9140-72e1b964da3d", + "name": "Spawner (118)", + "location": [1866, 1798, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15221ce6-7198-4de6-b750-6610f6680d51", + "name": "Spawner (118)", + "location": [931, 141, -5], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07b89a5e-cd89-4448-8b46-56bdd1d989a8", + "name": "Spawner (118)", + "location": [3267, 1717, -5], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10b4a195-d28e-4534-b058-ce0d08ad7ae4", + "name": "Spawner (118)", + "location": [2395, 1405, -5], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7be74f9e-3d46-49c1-8845-f2891a31472b", + "name": "Spawner (118)", + "location": [139, 1173, -5], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6cdb8fc8-8406-4535-a2e1-7f663e171f39", + "name": "Spawner (118)", + "location": [3123, 1485, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2191b9c5-c69e-4a83-bc9f-f17cced550b3", + "name": "Spawner (118)", + "location": [3811, 109, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf8e1268-6e29-4feb-8ad5-4f7f2e0b81d0", + "name": "Spawner (118)", + "location": [3931, 1757, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e68e3874-1188-4c5b-9909-4539e8119a58", + "name": "Spawner (118)", + "location": [2931, 3245, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a02156e-b816-4c27-8dd0-f377e6378524", + "name": "Spawner (118)", + "location": [2291, 3181, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23637b7b-52d1-4ae2-95ff-e08be730be14", + "name": "Spawner (118)", + "location": [979, 3997, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "301c54c5-7fe3-4538-903c-396311156077", + "name": "Spawner (118)", + "location": [147, 3213, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd078e33-7679-49ae-bc54-4be36e1e3c61", + "name": "Spawner (118)", + "location": [1891, 3597, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9576fcbe-ac4f-4382-931a-18d85664a5c4", + "name": "Spawner (118)", + "location": [2971, 1605, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6ef0ce60-872f-47a8-b8c7-288de410fd37", + "name": "Spawner (118)", + "location": [1580, 75, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9ab3a044-b6f4-4fda-b048-e4da84173f15", + "name": "Spawner (118)", + "location": [4275, 1309, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9aac32c6-3e67-4413-9ded-df193f4cd205", + "name": "Spawner (118)", + "location": [4667, 109, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc9c68d1-115b-4d9f-a75a-1c7b914f865d", + "name": "Spawner (118)", + "location": [5003, 101, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d36c3ef-0186-4aa2-8248-e32fa525c5d1", + "name": "Spawner (118)", + "location": [3603, 125, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0725865a-b679-4d54-ad18-8238b2eb6f38", + "name": "Spawner (118)", + "location": [4112, 791, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b4931fa-70ca-4abc-b62a-4496f8d6bd3b", + "name": "Spawner (118)", + "location": [3891, 733, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55c44ca9-d830-4a74-bb0f-e529edef11e1", + "name": "Spawner (118)", + "location": [3672, 1535, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5af749ec-cde5-4635-a69c-8d9df732688c", + "name": "Spawner (118)", + "location": [4363, 1981, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6ffba6b0-025d-4465-9bf8-1c7e22f8dea1", + "name": "Spawner (118)", + "location": [4995, 1829, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8f72a6c-ac20-4b11-9f88-d7ac1fcc16d3", + "name": "Spawner (118)", + "location": [4283, 2221, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f334a29c-42e6-4c51-a19a-9bdbd57efb70", + "name": "Spawner (118)", + "location": [2008, 4007, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54c24f2b-a9b4-4a6b-a005-4974491cb4b8", + "name": "Spawner (118)", + "location": [2320, 4007, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ecb155b-60d2-4de3-9c43-330d492797ac", + "name": "Spawner (118)", + "location": [4955, 3997, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4707f57e-e053-4515-8905-ead9aab81c34", + "name": "Spawner (118)", + "location": [4731, 3997, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bee85a9-3484-41ed-bf30-bc94accd887c", + "name": "Spawner (118)", + "location": [4307, 3965, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5a35e9a-e886-4acb-be41-17fbfc5c28d2", + "name": "Spawner (118)", + "location": [2227, 3533, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "769bcacc-1b82-4198-b3c5-6f19c298f770", + "name": "Spawner (118)", + "location": [1187, 3885, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9cf6fa1b-6813-491f-ab42-5308c43fe9f5", + "name": "Spawner (118)", + "location": [731, 3989, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe923a99-b8c8-443c-993d-2925273bcb35", + "name": "Spawner (118)", + "location": [491, 4013, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a3d212e-7f85-4ed1-84f6-e0cab9db1504", + "name": "Spawner (118)", + "location": [107, 909, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7cceffb4-478d-44b1-a66f-c932e0c8d7e0", + "name": "Spawner (118)", + "location": [1792, 79, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "71890746-952f-4b24-a473-76515b23e7e6", + "name": "Spawner (118)", + "location": [1336, 79, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b258c234-07e5-4a5e-8083-e95cee0aa7c2", + "name": "Spawner (118)", + "location": [4048, 63, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fbde8d9-735a-41d0-a2a9-e2d1a0a201e0", + "name": "Spawner (118)", + "location": [4392, 823, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce84448b-cd5c-4db2-9623-807a216f2e65", + "name": "Spawner (118)", + "location": [3520, 2263, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "71147c8d-bc3f-4d7f-a2e8-04b3cd99fc21", + "name": "Spawner (118)", + "location": [3568, 3215, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d7e454a9-f662-438f-aedf-1f7813c34536", + "name": "Spawner (118)", + "location": [4992, 3791, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b85b2928-f90d-4814-aee9-099661bb58bc", + "name": "Spawner (118)", + "location": [4984, 3567, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abe19479-98b3-4b2d-a030-b0719a2016e4", + "name": "Spawner (118)", + "location": [2072, 2527, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "fe6e7d88-7bb3-4492-9aec-2d002562c69a", "name": "Spawner (118)", "location": [2040, 3751, -5], @@ -2373,10 +2352,31 @@ ] }, { - "$type": "Spawner", - "guid": "fe923a99-b8c8-443c-993d-2925273bcb35", + "type": "Spawner", + "guid": "f1cbdc49-7c36-4f0a-9c71-0c7ac05f6519", "name": "Spawner (118)", - "location": [491, 4013, -5], + "location": [2288, 3767, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef98baa5-fd8a-4c7e-abe6-2f9e2972e0e6", + "name": "Spawner (118)", + "location": [496, 1879, -5], "map": "Trammel", "count": 13, "minDelay": "00:05:00", diff --git a/Distribution/Data/Spawns/shared/trammel/Shame.json b/Distribution/Data/Spawns/post-uoml/trammel/Shame.json similarity index 76% rename from Distribution/Data/Spawns/shared/trammel/Shame.json rename to Distribution/Data/Spawns/post-uoml/trammel/Shame.json index cbc86099a..4fdc5d9c4 100644 --- a/Distribution/Data/Spawns/shared/trammel/Shame.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Shame.json @@ -1,70 +1,6 @@ [ { - "$type": "Spawner", - "guid": "001ee924-4432-49b8-b36b-b617d376c561", - "name": "Spawner (119)", - "location": [5603, 93, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 25, - "entries": [ - { "name": "DullCopperElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "008c5583-538b-4a11-a7c0-3fbd453b9a25", - "name": "Spawner (119)", - "location": [5733, 91, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "079c93fb-f739-40a5-a21d-0a4aba793bc8", - "name": "Spawner (119)", - "location": [5575, 16, 10], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "08ada2f5-25a8-4964-a067-c74348006ff5", - "name": "Spawner (119)", - "location": [5604, 166, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "09ded4e4-5965-4d2b-b5bd-d19dd22d1d78", "name": "Spawner (119)", "location": [5404, 93, 10], @@ -81,73 +17,86 @@ ] }, { - "$type": "Spawner", - "guid": "0f0f2698-3569-4115-8e04-3792d50dfabc", + "type": "Spawner", + "guid": "b6cce94b-7e83-4d5e-aa5a-620dd839887f", "name": "Spawner (119)", - "location": [5594, 182, 0], + "location": [5425, 58, 10], "map": "Trammel", - "count": 3, + "count": 10, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 25, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + { "name": "EarthElemental", "maxCount": 7, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "10bd98c5-918f-4da9-9073-5bd147f09113", + "type": "Spawner", + "guid": "9b565f16-387d-48d3-b5c9-bb230a1f2175", "name": "Spawner (119)", - "location": [5475, 190, 0], + "location": [5387, 41, 20], "map": "Trammel", - "count": 1, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "18fc907b-29dc-48b4-ad1c-7dbc4d94c838", + "type": "Spawner", + "guid": "32fbcd32-5e8c-4061-b8d3-760447f9d436", "name": "Spawner (119)", - "location": [5730, 93, 0], + "location": [5394, 11, 30], "map": "Trammel", - "count": 1, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "19050742-7cad-48c2-b1bd-1a6da29d076c", + "type": "Spawner", + "guid": "9ae367fa-1674-4515-874c-639dc46b0983", "name": "Spawner (119)", - "location": [5817, 83, 0], + "location": [5474, 20, 0], "map": "Trammel", - "count": 1, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cd11b1a6-10bb-4c7f-9eb9-fb89c49f1cff", + "name": "Spawner (119)", + "location": [5477, 67, 20], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1d32e77e-c8dd-4626-81e5-ef5e71cc2c4d", "name": "Spawner (119)", "location": [5468, 109, 35], @@ -164,10 +113,10 @@ ] }, { - "$type": "Spawner", - "guid": "1d390a29-becb-4f59-b28d-a07fec3f5f60", + "type": "Spawner", + "guid": "f61afcad-3fea-45bb-8cb9-b7e278859277", "name": "Spawner (119)", - "location": [5727, 72, 0], + "location": [5423, 112, 0], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -175,28 +124,52 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "21d404c6-8dcc-49a1-b03c-ae4ef9898b55", + "type": "Spawner", + "guid": "f73d08aa-ffec-4132-accc-5eacbe40e8a8", "name": "Spawner (119)", - "location": [5804, 12, 0], + "location": [5468, 106, 35], "map": "Trammel", - "count": 5, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 5, "probability": 100 } - ] + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "72ee6353-de85-4b30-bd7e-5b3bb8274c8e", + "name": "Spawner (119)", + "location": [5434, 100, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "888398f2-e222-44e8-aa2c-85fee9df40b7", + "name": "Spawner (119)", + "location": [5492, 56, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "2237b125-9c62-49d2-8969-0a408f480409", "name": "Spawner (119)", "location": [5416, 59, -15], @@ -207,15 +180,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "22645272-781c-4559-9897-622f9c8c78cf", + "type": "Spawner", + "guid": "6f6ac8eb-4d03-40b4-9a85-ecfadeae978d", "name": "Spawner (119)", - "location": [5698, 57, 5], + "location": [5400, 45, 30], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -223,152 +194,254 @@ "team": 0, "homeRange": 1, "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d4a93223-fe95-4565-824d-a75653d2e466", + "name": "Spawner (119)", + "location": [5448, 33, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "82bf7919-cc1a-43ba-b4cd-f8dc919ce842", + "name": "Spawner (119)", + "location": [5437, 11, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ddd573db-303c-4e55-8b3d-276b8185ddc7", + "name": "Spawner (119)", + "location": [5399, 11, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e740c0d1-2ab2-43c6-acda-fe9874666ac4", + "name": "Spawner (119)", + "location": [5421, 117, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9633f2a9-53e9-477f-900d-ec62e3ad68d8", + "name": "Spawner (119)", + "location": [5468, 100, 35], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7661fbb1-6302-4c11-9ee6-14cc5f24b79e", + "name": "Spawner (119)", + "location": [5392, 10, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c18e446d-8c84-4f2d-8b78-a67767b70920", + "name": "Spawner (119)", + "location": [5445, 12, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6a7ec713-9962-445d-90c4-78bb4424b61c", + "name": "Spawner (119)", + "location": [5404, 43, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d79079a-6f9c-479b-86fc-46e2a5361c83", + "name": "Spawner (119)", + "location": [5443, 36, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4ec81dd8-9d60-4705-81bd-8cd7e50ba6c6", + "name": "Spawner (119)", + "location": [5402, 58, -20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ac52c146-adc0-4501-84c0-c9d38f301dce", + "name": "Spawner (119)", + "location": [5493, 51, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f1141039-567b-499e-b3e9-4749a47f6ced", + "name": "Spawner (119)", + "location": [5434, 89, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "079c93fb-f739-40a5-a21d-0a4aba793bc8", + "name": "Spawner (119)", + "location": [5575, 16, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5b5cc1fc-cbae-4895-b1f8-f05ee581851c", + "name": "Spawner (119)", + "location": [5615, 18, 10], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "DullCopperElemental", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d1a66cc8-5ebf-4a29-9c4d-7724f82d2db4", + "name": "Spawner (119)", + "location": [5568, 36, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bd0003c3-3bd7-43e1-a8aa-f0518996d54a", + "name": "Spawner (119)", + "location": [5549, 60, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "2771037f-84f2-43da-98d9-db67d02d1431", + "type": "Spawner", + "guid": "9a838c4b-48f3-4181-832e-c77b2067e8c2", "name": "Spawner (119)", - "location": [5557, 222, 0], + "location": [5549, 79, 0], "map": "Trammel", - "count": 7, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 25, + "walkingRange": 30, "entries": [ - { "name": "Kraken", "maxCount": 2, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 2, "probability": 100 }, + { "name": "Kraken", "maxCount": 1, "probability": 100 }, { "name": "WaterElemental", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "29b1dfff-7701-4436-9bf1-9b31e8ee667c", - "name": "Spawner (119)", - "location": [5711, 97, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2d37c623-d85a-4986-878b-3db1485a3932", - "name": "Spawner (119)", - "location": [5760, 99, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "Scorpion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "30d586e5-c20c-44c1-af6c-5b73b505fdf6", - "name": "Spawner (119)", - "location": [5723, 73, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "314487e0-1198-40a4-b0c9-d37c010700b4", - "name": "Spawner (119)", - "location": [5411, 163, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, - { "name": "Scorpion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32fbcd32-5e8c-4061-b8d3-760447f9d436", - "name": "Spawner (119)", - "location": [5394, 11, 30], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3b03d43c-d591-4845-bf47-815b88c21c29", - "name": "Spawner (119)", - "location": [5441, 187, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 5426, "y": 172, "z": 0 }, - "end": { "x": 5456, "y": 202, "z": 43 } - }, - "walkingRange": 30, - "entries": [ - { "name": "EvilMage", "maxCount": 5, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3bed0f7f-ea30-4440-8a18-93377ac6ce62", - "name": "Spawner (119)", - "location": [5456, 157, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "3ed65348-422d-4f40-86e9-14b3cb8fa14b", "name": "Spawner (119)", "location": [5595, 82, 0], @@ -385,183 +458,21 @@ ] }, { - "$type": "Spawner", - "guid": "46f4e0e9-6b99-4e2e-a7ca-2a4c8a429f52", + "type": "Spawner", + "guid": "001ee924-4432-49b8-b36b-b617d376c561", "name": "Spawner (119)", - "location": [5605, 202, 0], + "location": [5603, 93, 0], "map": "Trammel", - "count": 1, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "DullCopperElemental", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "481e0770-ef9b-4f8c-975f-06f954ede9d8", - "name": "Spawner (119)", - "location": [5474, 176, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "490f88ed-d12a-419f-9c25-02766dd7acbe", - "name": "Spawner (119)", - "location": [5725, 78, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4cf19ca1-b747-42ff-b6ba-647eb155a930", - "name": "Spawner (119)", - "location": [5615, 173, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4df550f5-2395-4415-bce2-780cef42016e", - "name": "Spawner (119)", - "location": [5546, 118, -5], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4ec81dd8-9d60-4705-81bd-8cd7e50ba6c6", - "name": "Spawner (119)", - "location": [5402, 58, -20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "509eea60-a86e-406f-80dd-32310ad518cd", - "name": "Spawner (119)", - "location": [5405, 185, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "521558b3-b945-413e-b3fb-3d8ec3498ce2", - "name": "Spawner (119)", - "location": [5410, 170, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54528337-9b35-44af-80d4-ef8176ac81d2", - "name": "Spawner (119)", - "location": [5701, 60, 5], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "56012eb5-81d3-4912-9910-4a08a69ac977", - "name": "Spawner (119)", - "location": [5717, 58, 5], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5b5cc1fc-cbae-4895-b1f8-f05ee581851c", - "name": "Spawner (119)", - "location": [5615, 18, 10], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "DullCopperElemental", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "67c32364-17d1-4910-9dc2-0c5f849e3c3a", "name": "Spawner (119)", "location": [5603, 116, 0], @@ -572,15 +483,13 @@ "team": 0, "homeRange": 15, "walkingRange": 30, - "entries": [ - { "name": "DullCopperElemental", "maxCount": 4, "probability": 100 } - ] + "entries": [{ "name": "DullCopperElemental", "maxCount": 4, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "69104c32-673c-44d2-9326-7d4d03633df3", + "type": "Spawner", + "guid": "6e6b46bf-7fdd-44c9-a7be-4f5d4a5ad13f", "name": "Spawner (119)", - "location": [5730, 90, 0], + "location": [5536, 119, -5], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -588,12 +497,125 @@ "team": 0, "homeRange": 1, "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4df550f5-2395-4415-bce2-780cef42016e", + "name": "Spawner (119)", + "location": [5546, 118, -5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "edc5910f-203a-4888-83a4-cf67cea2cc43", + "name": "Spawner (119)", + "location": [5606, 25, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "953785e9-e0b7-4464-bd7d-9fb1b2f6b535", + "name": "Spawner (119)", + "location": [5570, 118, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "97536e41-0cd7-4692-884c-25dd30d94b99", + "name": "Spawner (119)", + "location": [5620, 21, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ad47daba-e2ba-49ba-a625-23f6b5803519", + "name": "Spawner (119)", + "location": [5572, 113, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e7ae4e7b-0861-4ff2-b25d-bf84299ac1e0", + "name": "Spawner (119)", + "location": [5546, 113, -5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d9160a0d-2b7d-479e-a737-6dd383189d54", + "name": "Spawner (119)", + "location": [5474, 139, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "baadf71e-a920-4903-8852-0c06cb1cf279", + "name": "Spawner (119)", + "location": [5477, 183, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "69e9d6d7-5fa3-40e6-bae3-5eb077de6a00", "name": "Spawner (119)", "location": [5469, 211, 0], @@ -611,23 +633,24 @@ ] }, { - "$type": "Spawner", - "guid": "6a7ec713-9962-445d-90c4-78bb4424b61c", + "type": "Spawner", + "guid": "3b03d43c-d591-4845-bf47-815b88c21c29", "name": "Spawner (119)", - "location": [5404, 43, 30], + "location": [5441, 187, 0], "map": "Trammel", - "count": 1, + "count": 10, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + { "name": "EvilMage", "maxCount": 5, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6cd6d9d8-95d2-4517-bce2-bc0172947842", "name": "Spawner (119)", "location": [5412, 164, 0], @@ -644,855 +667,28 @@ ] }, { - "$type": "Spawner", - "guid": "6d97533a-8934-4eff-95f5-1ade2f51a092", + "type": "Spawner", + "guid": "314487e0-1198-40a4-b0c9-d37c010700b4", "name": "Spawner (119)", - "location": [5708, 43, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6e6b46bf-7fdd-44c9-a7be-4f5d4a5ad13f", - "name": "Spawner (119)", - "location": [5536, 119, -5], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f6ac8eb-4d03-40b4-9a85-ecfadeae978d", - "name": "Spawner (119)", - "location": [5400, 45, 30], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "719a5843-5c2b-4fe2-9606-5cfe1bea7fcc", - "name": "Spawner (119)", - "location": [5439, 137, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "725c89d8-4f6d-429b-ac19-f467eaab6f3d", - "name": "Spawner (119)", - "location": [5819, 50, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "EvilMage", "maxCount": 1, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72ee6353-de85-4b30-bd7e-5b3bb8274c8e", - "name": "Spawner (119)", - "location": [5434, 100, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7492a94d-efd9-4505-a057-1e260cc4adbb", - "name": "Spawner (119)", - "location": [5828, 42, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7661fbb1-6302-4c11-9ee6-14cc5f24b79e", - "name": "Spawner (119)", - "location": [5392, 10, 30], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "773e4496-e32b-4ab8-aac2-969f0c927494", - "name": "Spawner (119)", - "location": [5577, 194, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "EvilMage", "maxCount": 5, "probability": 100 }, - { "name": "EvilMageLord", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d79079a-6f9c-479b-86fc-46e2a5361c83", - "name": "Spawner (119)", - "location": [5443, 36, -10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7e839e78-3211-4cac-9773-fa08866d4026", - "name": "Spawner (119)", - "location": [5403, 235, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8115af0b-b4a8-4c92-8fd6-a3e6353257e8", - "name": "Spawner (119)", - "location": [5505, 180, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "ElderGazer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "82bf7919-cc1a-43ba-b4cd-f8dc919ce842", - "name": "Spawner (119)", - "location": [5437, 11, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "885a80e9-bfa3-4078-8ae6-a97eea5abb1e", - "name": "Spawner (119)", - "location": [5723, 77, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "888398f2-e222-44e8-aa2c-85fee9df40b7", - "name": "Spawner (119)", - "location": [5492, 56, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8a352d4f-c130-4c91-b8a1-0beded1ed79a", - "name": "Spawner (119)", - "location": [5415, 189, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ad8e084-ea10-46e2-9767-fa71ee0c5add", - "name": "Spawner (119)", - "location": [5616, 184, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d197175-de78-46d5-a9ce-23947f16d376", - "name": "Spawner (119)", - "location": [5818, 80, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "ElderGazer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8e8968c9-b416-4792-9b0c-3670a4b23695", - "name": "Spawner (119)", - "location": [5845, 57, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "953785e9-e0b7-4464-bd7d-9fb1b2f6b535", - "name": "Spawner (119)", - "location": [5570, 118, 5], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9633f2a9-53e9-477f-900d-ec62e3ad68d8", - "name": "Spawner (119)", - "location": [5468, 100, 35], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "97536e41-0cd7-4692-884c-25dd30d94b99", - "name": "Spawner (119)", - "location": [5620, 21, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "991c1f24-db8a-4a4e-8620-1d07266240fb", - "name": "Spawner (119)", - "location": [5721, 12, 10], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "BloodElemental", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9a838c4b-48f3-4181-832e-c77b2067e8c2", - "name": "Spawner (119)", - "location": [5549, 79, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9ae367fa-1674-4515-874c-639dc46b0983", - "name": "Spawner (119)", - "location": [5474, 20, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b565f16-387d-48d3-b5c9-bb230a1f2175", - "name": "Spawner (119)", - "location": [5387, 41, 20], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Scorpion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a0d04624-3586-4efd-b7d3-2ec4258544bd", - "name": "Spawner (119)", - "location": [5699, 61, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a1cd5afc-94a1-4674-ab44-62265f6978dd", - "name": "Spawner (119)", - "location": [5752, 44, 0], + "location": [5411, 163, 0], "map": "Trammel", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Kraken", "maxCount": 1, "probability": 100 }, - { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a1ff514a-58c1-4173-9577-188a2a41b3d5", - "name": "Spawner (119)", - "location": [5724, 118, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a30b9f6a-dcb9-4e6b-8968-61dca09727f8", - "name": "Spawner (119)", - "location": [5456, 143, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a5729af1-b157-4483-a1c3-6ec1ca5c057c", - "name": "Spawner (119)", - "location": [5608, 190, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a96bff59-e6a9-4ab9-872d-bfe06a6d55fd", - "name": "Spawner (119)", - "location": [5649, 101, 10], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a9e6fa28-2c45-4b1c-bcda-19a702017cc2", - "name": "Spawner (119)", - "location": [5405, 200, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aafa33d0-8132-46b6-88d5-fc65bef565ef", - "name": "Spawner (119)", - "location": [5875, 44, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ab9458c7-cafb-4752-813c-822bb1dbc3a6", - "name": "Spawner (119)", - "location": [5595, 147, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ac1ca0f0-ce5f-4aca-b1b5-6b80ea586a4e", - "name": "Spawner (119)", - "location": [5545, 153, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ac52c146-adc0-4501-84c0-c9d38f301dce", - "name": "Spawner (119)", - "location": [5493, 51, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad47daba-e2ba-49ba-a625-23f6b5803519", - "name": "Spawner (119)", - "location": [5572, 113, 5], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "adbef39f-4d4f-4b24-ac09-16b247e1aa60", - "name": "Spawner (119)", - "location": [5553, 146, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3a3061a-1a09-41cf-bb40-2d6503a76301", - "name": "Spawner (119)", - "location": [5817, 78, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b6cce94b-7e83-4d5e-aa5a-620dd839887f", - "name": "Spawner (119)", - "location": [5425, 58, 10], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 7, "probability": 100 }, - { "name": "Scorpion", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9b42bc5-e1f5-4b91-924c-88c12a7ae994", - "name": "Spawner (119)", - "location": [5733, 93, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "baadf71e-a920-4903-8852-0c06cb1cf279", - "name": "Spawner (119)", - "location": [5477, 183, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, "homeRange": 15, "walkingRange": 30, "entries": [ { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 4, "probability": 100 } + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "bd0003c3-3bd7-43e1-a8aa-f0518996d54a", + "type": "Spawner", + "guid": "fd5f3ab6-1ba5-4db0-8598-6b7d91486ac3", "name": "Spawner (119)", - "location": [5549, 60, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "be48657e-5a30-4917-9f02-9eeb8e55430e", - "name": "Spawner (119)", - "location": [5715, 58, 2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bed0b85f-695c-441c-ba0e-dcb292dc561f", - "name": "Spawner (119)", - "location": [5701, 58, 2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c18e446d-8c84-4f2d-8b78-a67767b70920", - "name": "Spawner (119)", - "location": [5445, 12, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c8b77b20-a7ba-4791-b5c3-feda828d6c7f", - "name": "Spawner (119)", - "location": [5594, 139, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd11b1a6-10bb-4c7f-9eb9-fb89c49f1cff", - "name": "Spawner (119)", - "location": [5477, 67, 20], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d07b5d4b-bf25-46aa-b179-37265ef7c5e5", - "name": "Spawner (119)", - "location": [5398, 148, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d1a66cc8-5ebf-4a29-9c4d-7724f82d2db4", - "name": "Spawner (119)", - "location": [5568, 36, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d4a93223-fe95-4565-824d-a75653d2e466", - "name": "Spawner (119)", - "location": [5448, 33, -10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d750d4b4-310a-461d-9288-ddc0658fe807", - "name": "Spawner (119)", - "location": [5593, 141, 10], + "location": [5412, 201, 0], "map": "Trammel", "count": 4, "minDelay": "00:05:00", @@ -1501,77 +697,12 @@ "homeRange": 15, "walkingRange": 30, "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9160a0d-2b7d-479e-a737-6dd383189d54", - "name": "Spawner (119)", - "location": [5474, 139, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, { "name": "EarthElemental", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "da517822-6aee-42d2-955e-61f32f64248c", - "name": "Spawner (119)", - "location": [5717, 60, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da924d39-dbec-4eab-87de-90db6d447919", - "name": "Spawner (119)", - "location": [5527, 210, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "DullCopperElemental", "maxCount": 4, "probability": 100 }, - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ddd573db-303c-4e55-8b3d-276b8185ddc7", - "name": "Spawner (119)", - "location": [5399, 11, 30], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e0e358ad-1171-4102-a7fc-d6c934dca6af", "name": "Spawner (119)", "location": [5412, 200, 0], @@ -1589,26 +720,10 @@ ] }, { - "$type": "Spawner", - "guid": "e1baaf02-08a2-48f0-995a-e0f605750a9c", + "type": "Spawner", + "guid": "ffbfcdb1-28bb-472c-9886-e2f86381c699", "name": "Spawner (119)", - "location": [5406, 177, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e2e06115-367a-4fde-9f22-fbcbea8f9bb8", - "name": "Spawner (119)", - "location": [5684, 117, 0], + "location": [5394, 230, 10], "map": "Trammel", "count": 2, "minDelay": "00:05:00", @@ -1616,76 +731,94 @@ "team": 0, "homeRange": 15, "walkingRange": 30, - "entries": [ - { "name": "BloodElemental", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e740c0d1-2ab2-43c6-acda-fe9874666ac4", + "type": "Spawner", + "guid": "8115af0b-b4a8-4c92-8fd6-a3e6353257e8", "name": "Spawner (119)", - "location": [5421, 117, 0], + "location": [5505, 180, 0], "map": "Trammel", - "count": 1, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "ElderGazer", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e7ae4e7b-0861-4ff2-b25d-bf84299ac1e0", + "type": "Spawner", + "guid": "da924d39-dbec-4eab-87de-90db6d447919", "name": "Spawner (119)", - "location": [5546, 113, -5], + "location": [5527, 210, 0], "map": "Trammel", - "count": 1, + "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + { "name": "DullCopperElemental", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e7f17883-f57f-40fd-a1dd-140010fb9699", + "type": "Spawner", + "guid": "2771037f-84f2-43da-98d9-db67d02d1431", "name": "Spawner (119)", - "location": [5396, 226, 10], + "location": [5557, 222, 0], "map": "Trammel", - "count": 1, + "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, + "homeRange": 10, + "walkingRange": 25, "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + { "name": "Kraken", "maxCount": 2, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e9fbf301-1258-4a79-b9e7-4d63acc98240", + "type": "Spawner", + "guid": "773e4496-e32b-4ab8-aac2-969f0c927494", "name": "Spawner (119)", - "location": [5715, 61, 5], + "location": [5577, 194, 0], "map": "Trammel", - "count": 1, + "count": 10, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 1, + "homeRange": 15, + "walkingRange": 30, "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } + { "name": "EvilMage", "maxCount": 5, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "0f0f2698-3569-4115-8e04-3792d50dfabc", + "name": "Spawner (119)", + "location": [5594, 182, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "eb4b7903-2927-4029-b565-8ebdc5da5b44", "name": "Spawner (119)", "location": [5571, 158, -10], @@ -1702,10 +835,27 @@ ] }, { - "$type": "Spawner", - "guid": "edc5910f-203a-4888-83a4-cf67cea2cc43", + "type": "Spawner", + "guid": "d750d4b4-310a-461d-9288-ddc0658fe807", "name": "Spawner (119)", - "location": [5606, 25, 10], + "location": [5593, 141, 10], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d07b5d4b-bf25-46aa-b179-37265ef7c5e5", + "name": "Spawner (119)", + "location": [5398, 148, 20], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -1713,12 +863,94 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "ac1ca0f0-ce5f-4aca-b1b5-6b80ea586a4e", + "name": "Spawner (119)", + "location": [5545, 153, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c8b77b20-a7ba-4791-b5c3-feda828d6c7f", + "name": "Spawner (119)", + "location": [5594, 139, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "08ada2f5-25a8-4964-a067-c74348006ff5", + "name": "Spawner (119)", + "location": [5604, 166, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a5729af1-b157-4483-a1c3-6ec1ca5c057c", + "name": "Spawner (119)", + "location": [5608, 190, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "46f4e0e9-6b99-4e2e-a7ca-2a4c8a429f52", + "name": "Spawner (119)", + "location": [5605, 202, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "719a5843-5c2b-4fe2-9606-5cfe1bea7fcc", + "name": "Spawner (119)", + "location": [5439, 137, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "eeed29d5-5317-4534-ad38-3c0992c39684", "name": "Spawner (119)", "location": [5390, 145, 20], @@ -1729,12 +961,136 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "3bed0f7f-ea30-4440-8a18-93377ac6ce62", + "name": "Spawner (119)", + "location": [5456, 157, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "521558b3-b945-413e-b3fb-3d8ec3498ce2", + "name": "Spawner (119)", + "location": [5410, 170, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8a352d4f-c130-4c91-b8a1-0beded1ed79a", + "name": "Spawner (119)", + "location": [5415, 189, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e7f17883-f57f-40fd-a1dd-140010fb9699", + "name": "Spawner (119)", + "location": [5396, 226, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adbef39f-4d4f-4b24-ac09-16b247e1aa60", + "name": "Spawner (119)", + "location": [5553, 146, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab9458c7-cafb-4752-813c-822bb1dbc3a6", + "name": "Spawner (119)", + "location": [5595, 147, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4cf19ca1-b747-42ff-b6ba-647eb155a930", + "name": "Spawner (119)", + "location": [5615, 173, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8ad8e084-ea10-46e2-9767-fa71ee0c5add", + "name": "Spawner (119)", + "location": [5616, 184, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a30b9f6a-dcb9-4e6b-8968-61dca09727f8", + "name": "Spawner (119)", + "location": [5456, 143, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "f026769e-09f2-46db-bc0a-e5294bee1910", "name": "Spawner (119)", "location": [5432, 158, 0], @@ -1745,15 +1101,13 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f1141039-567b-499e-b3e9-4749a47f6ced", + "type": "Spawner", + "guid": "481e0770-ef9b-4f8c-975f-06f954ede9d8", "name": "Spawner (119)", - "location": [5434, 89, 20], + "location": [5474, 176, 0], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -1761,15 +1115,83 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f216166d-1cad-4be4-9bc0-37d09a0a2d6a", + "type": "Spawner", + "guid": "e1baaf02-08a2-48f0-995a-e0f605750a9c", "name": "Spawner (119)", - "location": [5661, 16, -10], + "location": [5406, 177, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "509eea60-a86e-406f-80dd-32310ad518cd", + "name": "Spawner (119)", + "location": [5405, 185, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a9e6fa28-2c45-4b1c-bcda-19a702017cc2", + "name": "Spawner (119)", + "location": [5405, 200, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7e839e78-3211-4cac-9773-fa08866d4026", + "name": "Spawner (119)", + "location": [5403, 235, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "10bd98c5-918f-4da9-9073-5bd147f09113", + "name": "Spawner (119)", + "location": [5475, 190, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aafa33d0-8132-46b6-88d5-fc65bef565ef", + "name": "Spawner (119)", + "location": [5875, 44, 0], "map": "Trammel", "count": 2, "minDelay": "00:05:00", @@ -1777,44 +1199,10 @@ "team": 0, "homeRange": 15, "walkingRange": 30, - "entries": [ - { "name": "FireElemental", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f61afcad-3fea-45bb-8cb9-b7e278859277", - "name": "Spawner (119)", - "location": [5423, 112, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f73d08aa-ffec-4132-accc-5eacbe40e8a8", - "name": "Spawner (119)", - "location": [5468, 106, 35], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f97a9dbc-694a-4fce-8ff9-5f6bb478274c", "name": "Spawner (119)", "location": [5856, 107, 10], @@ -1825,15 +1213,145 @@ "team": 0, "homeRange": 15, "walkingRange": 30, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e8968c9-b416-4792-9b0c-3670a4b23695", + "name": "Spawner (119)", + "location": [5845, 57, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7492a94d-efd9-4505-a057-1e260cc4adbb", + "name": "Spawner (119)", + "location": [5828, 42, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, "entries": [ - { "name": "FireElemental", "maxCount": 2, "probability": 100 } + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fd5f3ab6-1ba5-4db0-8598-6b7d91486ac3", + "type": "Spawner", + "guid": "725c89d8-4f6d-429b-ac19-f467eaab6f3d", "name": "Spawner (119)", - "location": [5412, 201, 0], + "location": [5819, 50, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d197175-de78-46d5-a9ce-23947f16d376", + "name": "Spawner (119)", + "location": [5818, 80, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "ElderGazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21d404c6-8dcc-49a1-b03c-ae4ef9898b55", + "name": "Spawner (119)", + "location": [5804, 12, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "991c1f24-db8a-4a4e-8620-1d07266240fb", + "name": "Spawner (119)", + "location": [5721, 12, 10], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "BloodElemental", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f216166d-1cad-4be4-9bc0-37d09a0a2d6a", + "name": "Spawner (119)", + "location": [5661, 16, -10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a96bff59-e6a9-4ab9-872d-bfe06a6d55fd", + "name": "Spawner (119)", + "location": [5649, 101, 10], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e2e06115-367a-4fde-9f22-fbcbea8f9bb8", + "name": "Spawner (119)", + "location": [5684, 117, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "BloodElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a1ff514a-58c1-4173-9577-188a2a41b3d5", + "name": "Spawner (119)", + "location": [5724, 118, 0], "map": "Trammel", "count": 4, "minDelay": "00:05:00", @@ -1842,24 +1360,331 @@ "homeRange": 15, "walkingRange": 30, "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 2, "probability": 100 } + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ffbfcdb1-28bb-472c-9886-e2f86381c699", + "type": "Spawner", + "guid": "2d37c623-d85a-4986-878b-3db1485a3932", "name": "Spawner (119)", - "location": [5394, 230, 10], + "location": [5760, 99, 0], "map": "Trammel", - "count": 2, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 15, "walkingRange": 30, "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 } + { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "29b1dfff-7701-4436-9bf1-9b31e8ee667c", + "name": "Spawner (119)", + "location": [5711, 97, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1cd5afc-94a1-4674-ab44-62265f6978dd", + "name": "Spawner (119)", + "location": [5752, 44, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d97533a-8934-4eff-95f5-1ade2f51a092", + "name": "Spawner (119)", + "location": [5708, 43, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69104c32-673c-44d2-9326-7d4d03633df3", + "name": "Spawner (119)", + "location": [5730, 90, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "30d586e5-c20c-44c1-af6c-5b73b505fdf6", + "name": "Spawner (119)", + "location": [5723, 73, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e9fbf301-1258-4a79-b9e7-4d63acc98240", + "name": "Spawner (119)", + "location": [5715, 61, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "54528337-9b35-44af-80d4-ef8176ac81d2", + "name": "Spawner (119)", + "location": [5701, 60, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19050742-7cad-48c2-b1bd-1a6da29d076c", + "name": "Spawner (119)", + "location": [5817, 83, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "18fc907b-29dc-48b4-ad1c-7dbc4d94c838", + "name": "Spawner (119)", + "location": [5730, 93, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "885a80e9-bfa3-4078-8ae6-a97eea5abb1e", + "name": "Spawner (119)", + "location": [5723, 77, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da517822-6aee-42d2-955e-61f32f64248c", + "name": "Spawner (119)", + "location": [5717, 60, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a0d04624-3586-4efd-b7d3-2ec4258544bd", + "name": "Spawner (119)", + "location": [5699, 61, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b3a3061a-1a09-41cf-bb40-2d6503a76301", + "name": "Spawner (119)", + "location": [5817, 78, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "008c5583-538b-4a11-a7c0-3fbd453b9a25", + "name": "Spawner (119)", + "location": [5733, 91, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "490f88ed-d12a-419f-9c25-02766dd7acbe", + "name": "Spawner (119)", + "location": [5725, 78, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "be48657e-5a30-4917-9f02-9eeb8e55430e", + "name": "Spawner (119)", + "location": [5715, 58, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "22645272-781c-4559-9897-622f9c8c78cf", + "name": "Spawner (119)", + "location": [5698, 57, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b9b42bc5-e1f5-4b91-924c-88c12a7ae994", + "name": "Spawner (119)", + "location": [5733, 93, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d390a29-becb-4f59-b28d-a07fec3f5f60", + "name": "Spawner (119)", + "location": [5727, 72, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "56012eb5-81d3-4912-9910-4a08a69ac977", + "name": "Spawner (119)", + "location": [5717, 58, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bed0b85f-695c-441c-ba0e-dcb292dc561f", + "name": "Spawner (119)", + "location": [5701, 58, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/SolenHive.json b/Distribution/Data/Spawns/post-uoml/trammel/SolenHive.json similarity index 73% rename from Distribution/Data/Spawns/shared/trammel/SolenHive.json rename to Distribution/Data/Spawns/post-uoml/trammel/SolenHive.json index 83c2653b1..0ce8cac29 100644 --- a/Distribution/Data/Spawns/shared/trammel/SolenHive.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/SolenHive.json @@ -1,375 +1,6 @@ [ { - "$type": "Spawner", - "guid": "16d57106-d22a-48c5-a704-96675ab47670", - "name": "Spawner (120)", - "location": [5837, 1992, -2], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Beetle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "196acab9-0c1f-491f-a11e-7aabb7c56648", - "name": "Spawner (120)", - "location": [5681, 1886, 2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 50, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "204955db-7ad2-40bc-a4e4-09a07d091c72", - "name": "Spawner (120)", - "location": [5831, 1910, 3], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Beetle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21746fe5-d247-4351-b3fe-e58cfd3db9b2", - "name": "Spawner (120)", - "location": [5671, 1979, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22b0f532-20f7-4b1e-9807-a8ffcd5732d7", - "name": "Spawner (120)", - "location": [5748, 1923, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23a8fa37-4b98-4d53-b5f8-df35c72b6ff2", - "name": "Spawner (120)", - "location": [5722, 1820, 5], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "245aab6c-8a39-4f1b-9aaf-eaafc8117f58", - "name": "Spawner (120)", - "location": [5693, 1834, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "274ebd5a-249f-4dd9-92ad-25dcb95d8d06", - "name": "Spawner (120)", - "location": [5912, 1800, 1], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2c2b2ec3-aabe-4bce-b80a-21292660b372", - "name": "Spawner (120)", - "location": [5855, 1932, 3], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "373d83af-8b04-46f3-9602-7199c792fc9f", - "name": "Spawner (120)", - "location": [5863, 1958, 2], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3db4e70c-3423-4aaf-b80c-656c9b657c69", - "name": "Spawner (120)", - "location": [5815, 1881, 2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e0b4f9c-116a-44ad-9cf5-fc8f9179c02c", - "name": "Spawner (120)", - "location": [5703, 1991, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 35, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "43fe7167-b65e-4e21-ae5f-d52b5c58607c", - "name": "Spawner (120)", - "location": [5718, 2024, 4], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "495718b4-baad-4872-8d48-07cec97e5fda", - "name": "Spawner (120)", - "location": [5910, 1991, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4ecaeb0d-e3c7-4680-9859-c2879f71cd2c", - "name": "Spawner (120)", - "location": [5856, 2018, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50b74a27-fb2a-4890-a4cf-6309a7c1a2f2", - "name": "Spawner (120)", - "location": [5884, 1920, -15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "AntLion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5dd054bd-aabe-4f16-8029-1c766ba83308", - "name": "Spawner (120)", - "location": [5709, 1874, 1], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 50, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "71a71fe3-081c-49d4-9eed-8a280eb9fe95", - "name": "Spawner (120)", - "location": [5876, 2018, 3], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72980a1c-b916-4b7f-95d3-5a66b4f47fca", - "name": "Spawner (120)", - "location": [5825, 1968, 4], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72ee4a95-09bc-4efa-baf4-4a9c75c7b397", - "name": "Spawner (120)", - "location": [5704, 1807, 4], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75a0efe5-1432-4b69-abeb-69058db48d31", - "name": "Spawner (120)", - "location": [5767, 1962, 2], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 50, - "entries": [ - { "name": "DreadSpider", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b61d8d6-669b-45e7-b478-7df484b954d6", - "name": "Spawner (120)", - "location": [5847, 1819, 1], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c89c785-3d76-4b10-a309-20ff87727f55", - "name": "Spawner (120)", - "location": [3218, 502, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "BlackSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, - { "name": "BlackSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "80a9c1da-dd8f-490b-9742-3f044df6342a", "name": "Spawner (120)", "location": [5670, 1807, 4], @@ -380,207 +11,10 @@ "team": 0, "homeRange": 25, "walkingRange": 30, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "821a5e93-b1a5-4fef-9ef0-45f6833fa68d", - "name": "Spawner (120)", - "location": [5887, 1840, -18], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "AntLion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8b45fed3-db59-49c8-b477-b5d76f1c5fd2", - "name": "Spawner (120)", - "location": [5838, 2019, 3], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "97e600cc-7679-4998-aa88-d6124f061421", - "name": "Spawner (120)", - "location": [5829, 1798, 1], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b557083-7599-44ef-87f4-f8dff1b09c7e", - "name": "Spawner (120)", - "location": [5665, 1868, 11], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "AntLion", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ab7b16cd-6111-412a-9a29-27d1da389076", - "name": "Spawner (120)", - "location": [5756, 1857, 4], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 50, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bcf0b17e-8f7c-4c2e-b13b-a979643031fc", - "name": "Spawner (120)", - "location": [1361, 894, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 8, - "entries": [ - { "name": "BlackSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, - { "name": "BlackSolenInfiltratorWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "beafc393-f412-4d90-8816-c00a26814f92", - "name": "Spawner (120)", - "location": [5736, 1939, 7], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenQueen", "maxCount": 1, "probability": 100 }, - { "name": "RedSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c49c6762-912b-4609-9df3-c33cb865e2b6", - "name": "Spawner (120)", - "location": [5782, 2021, -11], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "AntLion", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "caba66b7-9425-4fd5-b2f0-576ef17354da", - "name": "Spawner (120)", - "location": [5920, 1842, 1], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 30, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d1fce2c8-e0e7-454e-9546-d648f27a2bc3", - "name": "Spawner (120)", - "location": [5798, 1963, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 35, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d325fbe4-9db7-4583-8eb3-dbabaefba8fe", - "name": "Spawner (120)", - "location": [5782, 1793, 1], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d575dc9a-eb61-43f3-a8c1-758e59010f50", - "name": "Spawner (120)", - "location": [1940, 3261, 1], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "BlackSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, - { "name": "BlackSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e2dfbc83-aee1-4e9d-9320-df1cea768140", "name": "Spawner (120)", "location": [5673, 1846, 1], @@ -591,60 +25,164 @@ "team": 0, "homeRange": 6, "walkingRange": 20, - "entries": [ - { "name": "RedSolenWorker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e5564c6c-b83e-46be-8af8-c35024c13df3", + "type": "Spawner", + "guid": "245aab6c-8a39-4f1b-9aaf-eaafc8117f58", "name": "Spawner (120)", - "location": [5799, 1857, 5], + "location": [5693, 1834, 0], "map": "Trammel", - "count": 2, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, + "homeRange": 4, "walkingRange": 20, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ed1dd6c0-4260-415d-8ef1-4185452bdfa3", + "type": "Spawner", + "guid": "72ee4a95-09bc-4efa-baf4-4a9c75c7b397", "name": "Spawner (120)", - "location": [5699, 1921, 2], + "location": [5704, 1807, 4], "map": "Trammel", - "count": 2, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 } - ] + "homeRange": 6, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ed6f6b69-45b4-4591-9865-f7d03509aa8a", + "type": "Spawner", + "guid": "23a8fa37-4b98-4d53-b5f8-df35c72b6ff2", "name": "Spawner (120)", - "location": [5707, 1843, -15], + "location": [5722, 1820, 5], "map": "Trammel", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "AntLion", "maxCount": 1, "probability": 100 } - ] + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "5dd054bd-aabe-4f16-8029-1c766ba83308", + "name": "Spawner (120)", + "location": [5709, 1874, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 50, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "196acab9-0c1f-491f-a11e-7aabb7c56648", + "name": "Spawner (120)", + "location": [5681, 1886, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 50, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab7b16cd-6111-412a-9a29-27d1da389076", + "name": "Spawner (120)", + "location": [5756, 1857, 4], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 50, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "274ebd5a-249f-4dd9-92ad-25dcb95d8d06", + "name": "Spawner (120)", + "location": [5912, 1800, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "caba66b7-9425-4fd5-b2f0-576ef17354da", + "name": "Spawner (120)", + "location": [5920, 1842, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "821a5e93-b1a5-4fef-9ef0-45f6833fa68d", + "name": "Spawner (120)", + "location": [5887, 1840, -18], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b61d8d6-669b-45e7-b478-7df484b954d6", + "name": "Spawner (120)", + "location": [5847, 1819, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "97e600cc-7679-4998-aa88-d6124f061421", + "name": "Spawner (120)", + "location": [5829, 1798, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "f5300b36-01bc-48bd-842d-a8d57f03cdd1", "name": "Spawner (120)", "location": [5873, 1798, 0], @@ -661,7 +199,77 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "d325fbe4-9db7-4583-8eb3-dbabaefba8fe", + "name": "Spawner (120)", + "location": [5782, 1793, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e5564c6c-b83e-46be-8af8-c35024c13df3", + "name": "Spawner (120)", + "location": [5799, 1857, 5], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3db4e70c-3423-4aaf-b80c-656c9b657c69", + "name": "Spawner (120)", + "location": [5815, 1881, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "204955db-7ad2-40bc-a4e4-09a07d091c72", + "name": "Spawner (120)", + "location": [5831, 1910, 3], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Beetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "43fe7167-b65e-4e21-ae5f-d52b5c58607c", + "name": "Spawner (120)", + "location": [5718, 2024, 4], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "f780f0c8-a550-427a-99a8-1834f747a867", "name": "Spawner (120)", "location": [5776, 2019, 2], @@ -672,8 +280,326 @@ "team": 0, "homeRange": 10, "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c49c6762-912b-4609-9df3-c33cb865e2b6", + "name": "Spawner (120)", + "location": [5782, 2021, -11], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "75a0efe5-1432-4b69-abeb-69058db48d31", + "name": "Spawner (120)", + "location": [5767, 1962, 2], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 50, + "entries": [{ "name": "DreadSpider", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21746fe5-d247-4351-b3fe-e58cfd3db9b2", + "name": "Spawner (120)", + "location": [5671, 1979, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3e0b4f9c-116a-44ad-9cf5-fc8f9179c02c", + "name": "Spawner (120)", + "location": [5703, 1991, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed1dd6c0-4260-415d-8ef1-4185452bdfa3", + "name": "Spawner (120)", + "location": [5699, 1921, 2], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "22b0f532-20f7-4b1e-9807-a8ffcd5732d7", + "name": "Spawner (120)", + "location": [5748, 1923, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "beafc393-f412-4d90-8816-c00a26814f92", + "name": "Spawner (120)", + "location": [5736, 1939, 7], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "RedSolenWorker", "maxCount": 1, "probability": 100 } + { "name": "RedSolenQueen", "maxCount": 1, "probability": 100 }, + { "name": "RedSolenWorker", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "71a71fe3-081c-49d4-9eed-8a280eb9fe95", + "name": "Spawner (120)", + "location": [5876, 2018, 3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4ecaeb0d-e3c7-4680-9859-c2879f71cd2c", + "name": "Spawner (120)", + "location": [5856, 2018, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8b45fed3-db59-49c8-b477-b5d76f1c5fd2", + "name": "Spawner (120)", + "location": [5838, 2019, 3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "495718b4-baad-4872-8d48-07cec97e5fda", + "name": "Spawner (120)", + "location": [5910, 1991, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "RedSolenWorker", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "16d57106-d22a-48c5-a704-96675ab47670", + "name": "Spawner (120)", + "location": [5837, 1992, -2], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Beetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "373d83af-8b04-46f3-9602-7199c792fc9f", + "name": "Spawner (120)", + "location": [5863, 1958, 2], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50b74a27-fb2a-4890-a4cf-6309a7c1a2f2", + "name": "Spawner (120)", + "location": [5884, 1920, -15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72980a1c-b916-4b7f-95d3-5a66b4f47fca", + "name": "Spawner (120)", + "location": [5825, 1968, 4], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2c2b2ec3-aabe-4bce-b80a-21292660b372", + "name": "Spawner (120)", + "location": [5855, 1932, 3], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d1fce2c8-e0e7-454e-9546-d648f27a2bc3", + "name": "Spawner (120)", + "location": [5798, 1963, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 35, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9b557083-7599-44ef-87f4-f8dff1b09c7e", + "name": "Spawner (120)", + "location": [5665, 1868, 11], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "AntLion", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed6f6b69-45b4-4591-9865-f7d03509aa8a", + "name": "Spawner (120)", + "location": [5707, 1843, -15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7c89c785-3d76-4b10-a309-20ff87727f55", + "name": "Spawner (120)", + "location": [3218, 502, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "BlackSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "BlackSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d575dc9a-eb61-43f3-a8c1-758e59010f50", + "name": "Spawner (120)", + "location": [1940, 3261, 1], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "BlackSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "BlackSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bcf0b17e-8f7c-4c2e-b13b-a979643031fc", + "name": "Spawner (120)", + "location": [1361, 894, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 8, + "entries": [ + { "name": "BlackSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "BlackSolenInfiltratorWarrior", "maxCount": 1, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/TerathanKeep.json b/Distribution/Data/Spawns/post-uoml/trammel/TerathanKeep.json similarity index 94% rename from Distribution/Data/Spawns/shared/trammel/TerathanKeep.json rename to Distribution/Data/Spawns/post-uoml/trammel/TerathanKeep.json index a921a7535..080b038d0 100644 --- a/Distribution/Data/Spawns/shared/trammel/TerathanKeep.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/TerathanKeep.json @@ -1,45 +1,6 @@ [ { - "$type": "Spawner", - "guid": "031eb48a-ab35-4358-b90b-4c6f50b96a38", - "name": "Spawner (121)", - "location": [5334, 1614, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0945ec2d-d81a-4b3e-9c49-4d67c615a056", - "name": "Spawner (121)", - "location": [5342, 1553, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "0b2133ab-a3f1-4857-80a2-c41233c499fe", "name": "Spawner (121)", "location": [5296, 1561, 0], @@ -58,324 +19,7 @@ ] }, { - "$type": "Spawner", - "guid": "1a9ae4d0-2900-4a34-a4b0-72d5ed252665", - "name": "Spawner (121)", - "location": [5200, 1565, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "245b8698-8502-4d28-a477-04532306bfd3", - "name": "Spawner (121)", - "location": [5271, 1693, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "27edcd48-0444-44f7-a6d2-dbf5e2bb08f1", - "name": "Spawner (121)", - "location": [5132, 1623, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "286c764e-4d51-42fa-97d5-86fc2f4902f8", - "name": "Spawner (121)", - "location": [5161, 1584, -15], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ce3592b-1ef9-4c42-8731-660451045829", - "name": "Spawner (121)", - "location": [5363, 1709, -75], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e3c0553-182a-40da-b633-ef4bf2c42425", - "name": "Spawner (121)", - "location": [5345, 1757, -125], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3f984984-b04b-4635-8685-0e4e2eae07a7", - "name": "Spawner (121)", - "location": [5348, 1552, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "45a4e9ce-7994-4faa-bca4-adaf4e330450", - "name": "Spawner (121)", - "location": [5177, 1700, 1], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4cc200d3-c1ba-4433-92ba-fa48868ee0f8", - "name": "Spawner (121)", - "location": [5336, 1708, -70], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50432a43-ccb4-4064-8336-06ea08e4b61e", - "name": "Spawner (121)", - "location": [5335, 1706, -70], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "52c5db98-5de5-4d55-9a26-85f3bc461810", - "name": "Spawner (121)", - "location": [5345, 1551, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5f285bad-aaa5-40d3-add2-5b550f38e408", - "name": "Spawner (121)", - "location": [5342, 1552, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "656b11cd-88d3-40fb-b9dc-f830f7e22ae5", - "name": "Spawner (121)", - "location": [5335, 1615, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6fe29b7c-0452-4412-a1b9-d0c30c0721fc", - "name": "Spawner (121)", - "location": [5270, 1695, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c337921-7949-4be3-bd19-9353b66560ae", - "name": "Spawner (121)", - "location": [5132, 1624, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c3950c7-e1df-4308-905f-f987fe5c9b99", - "name": "Spawner (121)", - "location": [5176, 1702, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81220dc9-b072-44ff-8b29-3257236c2b25", - "name": "Spawner (121)", - "location": [5343, 1551, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "88aac11e-5718-4d06-be82-14b956899e5c", "name": "Spawner (121)", "location": [5296, 1562, 0], @@ -395,10 +39,10 @@ ] }, { - "$type": "Spawner", - "guid": "8af3286d-17b3-4447-8292-ee407d69f826", + "type": "Spawner", + "guid": "fab17e79-c410-496f-8cf8-b7b07363b973", "name": "Spawner (121)", - "location": [5200, 1568, 0], + "location": [5297, 1565, 0], "map": "Trammel", "count": 4, "minDelay": "00:05:00", @@ -414,7 +58,65 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "0945ec2d-d81a-4b3e-9c49-4d67c615a056", + "name": "Spawner (121)", + "location": [5342, 1553, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f285bad-aaa5-40d3-add2-5b550f38e408", + "name": "Spawner (121)", + "location": [5342, 1552, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81220dc9-b072-44ff-8b29-3257236c2b25", + "name": "Spawner (121)", + "location": [5343, 1551, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "9fb21a92-1084-4819-a2b2-15e4887c701c", "name": "Spawner (121)", "location": [5334, 1613, 0], @@ -433,10 +135,10 @@ ] }, { - "$type": "Spawner", - "guid": "b0fde845-3212-41e0-8f65-c457b0570a53", + "type": "Spawner", + "guid": "031eb48a-ab35-4358-b90b-4c6f50b96a38", "name": "Spawner (121)", - "location": [5201, 1566, 0], + "location": [5334, 1614, 0], "map": "Trammel", "count": 5, "minDelay": "00:05:00", @@ -453,69 +155,10 @@ ] }, { - "$type": "Spawner", - "guid": "b116f136-d3a6-43fa-b111-ef38b6ff3efa", + "type": "Spawner", + "guid": "656b11cd-88d3-40fb-b9dc-f830f7e22ae5", "name": "Spawner (121)", - "location": [5270, 1694, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bdcd9c84-b005-4f1e-8c9f-0af0f8800d38", - "name": "Spawner (121)", - "location": [5160, 1586, -15], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Balron", "maxCount": 1, "probability": 100 }, - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 }, - { "name": "Nightmare", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf601676-5f93-4b75-ae89-ac1b1a4774f6", - "name": "Spawner (121)", - "location": [5176, 1701, 2], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c3d9ecd4-8f8e-42a2-9029-b585a881657f", - "name": "Spawner (121)", - "location": [5134, 1625, 0], + "location": [5335, 1615, 0], "map": "Trammel", "count": 4, "minDelay": "00:05:00", @@ -531,10 +174,10 @@ ] }, { - "$type": "Spawner", - "guid": "c9238348-7288-4d4f-97c2-9df88a2981b3", + "type": "Spawner", + "guid": "3f984984-b04b-4635-8685-0e4e2eae07a7", "name": "Spawner (121)", - "location": [5365, 1707, -71], + "location": [5348, 1552, 0], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -542,31 +185,24 @@ "team": 0, "homeRange": 1, "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "cff18fb7-c66e-4c05-bb2e-01f883d660ce", + "type": "Spawner", + "guid": "52c5db98-5de5-4d55-9a26-85f3bc461810", "name": "Spawner (121)", - "location": [5346, 1756, -125], + "location": [5345, 1551, 0], "map": "Trammel", - "count": 4, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, - { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, - { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, - { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } - ] + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d6923469-ec40-4861-a6e7-ef21b07024a7", "name": "Spawner (121)", "location": [5344, 1757, -125], @@ -585,7 +221,65 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "3e3c0553-182a-40da-b633-ef4bf2c42425", + "name": "Spawner (121)", + "location": [5345, 1757, -125], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cff18fb7-c66e-4c05-bb2e-01f883d660ce", + "name": "Spawner (121)", + "location": [5346, 1756, -125], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50432a43-ccb4-4064-8336-06ea08e4b61e", + "name": "Spawner (121)", + "location": [5335, 1706, -70], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f1bd125f-c580-49ef-be95-922862466bad", "name": "Spawner (121)", "location": [5335, 1707, -70], @@ -605,7 +299,247 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "4cc200d3-c1ba-4433-92ba-fa48868ee0f8", + "name": "Spawner (121)", + "location": [5336, 1708, -70], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6fe29b7c-0452-4412-a1b9-d0c30c0721fc", + "name": "Spawner (121)", + "location": [5270, 1695, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b116f136-d3a6-43fa-b111-ef38b6ff3efa", + "name": "Spawner (121)", + "location": [5270, 1694, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "245b8698-8502-4d28-a477-04532306bfd3", + "name": "Spawner (121)", + "location": [5271, 1693, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9238348-7288-4d4f-97c2-9df88a2981b3", + "name": "Spawner (121)", + "location": [5365, 1707, -71], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2ce3592b-1ef9-4c42-8731-660451045829", + "name": "Spawner (121)", + "location": [5363, 1709, -75], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7c3950c7-e1df-4308-905f-f987fe5c9b99", + "name": "Spawner (121)", + "location": [5176, 1702, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf601676-5f93-4b75-ae89-ac1b1a4774f6", + "name": "Spawner (121)", + "location": [5176, 1701, 2], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "45a4e9ce-7994-4faa-bca4-adaf4e330450", + "name": "Spawner (121)", + "location": [5177, 1700, 1], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c337921-7949-4be3-bd19-9353b66560ae", + "name": "Spawner (121)", + "location": [5132, 1624, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "27edcd48-0444-44f7-a6d2-dbf5e2bb08f1", + "name": "Spawner (121)", + "location": [5132, 1623, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3d9ecd4-8f8e-42a2-9029-b585a881657f", + "name": "Spawner (121)", + "location": [5134, 1625, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdcd9c84-b005-4f1e-8c9f-0af0f8800d38", + "name": "Spawner (121)", + "location": [5160, 1586, -15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "f27e8d0a-6a88-40e1-b02b-97b2c550065c", "name": "Spawner (121)", "location": [5163, 1585, -15], @@ -625,10 +559,68 @@ ] }, { - "$type": "Spawner", - "guid": "fab17e79-c410-496f-8cf8-b7b07363b973", + "type": "Spawner", + "guid": "286c764e-4d51-42fa-97d5-86fc2f4902f8", "name": "Spawner (121)", - "location": [5297, 1565, 0], + "location": [5161, 1584, -15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a9ae4d0-2900-4a34-a4b0-72d5ed252665", + "name": "Spawner (121)", + "location": [5200, 1565, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0fde845-3212-41e0-8f65-c457b0570a53", + "name": "Spawner (121)", + "location": [5201, 1566, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8af3286d-17b3-4447-8292-ee407d69f826", + "name": "Spawner (121)", + "location": [5200, 1568, 0], "map": "Trammel", "count": 4, "minDelay": "00:05:00", diff --git a/Distribution/Data/Spawns/post-uoml/trammel/TownsLife.json b/Distribution/Data/Spawns/post-uoml/trammel/TownsLife.json index 5390d1239..119554543 100644 --- a/Distribution/Data/Spawns/post-uoml/trammel/TownsLife.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/TownsLife.json @@ -1,6 +1,635 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "bb7d8052-56aa-48cc-8d9a-31d4d85c0d81", + "name": "Spawner (122)", + "location": [5249, 228, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1218cc8e-4200-42cc-99ee-b27dd396e3b4", + "name": "Spawner (122)", + "location": [5164, 205, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f474b7be-1817-4b1d-a492-42df4790cedb", + "name": "Spawner (122)", + "location": [5133, 150, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Daemon", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d5e571c-c1e3-45d4-a374-04b465b36460", + "name": "Spawner (122)", + "location": [1550, 1658, 26], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 120, + "walkingRange": 120, + "entries": [ + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Cat", "maxCount": 15, "probability": 100 }, + { "name": "Dog", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 15, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d7f7b008-ad02-4eae-8d4a-8f9163288631", + "name": "Spawner (122)", + "location": [1319, 1810, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Cow", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b3d2bb40-d3d1-4ec9-9a5c-b20b737de066", + "name": "Spawner (122)", + "location": [1319, 1821, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Cow", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "86f2e9e8-9ebb-44dc-8372-dc6edfb41847", + "name": "Spawner (122)", + "location": [1340, 1790, 15], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ee6ae89b-6de4-489c-bc6c-62e43810a6a9", + "name": "Spawner (122)", + "location": [1339, 1788, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4dd3ea55-9eb8-4e5c-b31a-04a071ae46ab", + "name": "Spawner (122)", + "location": [1217, 1825, 0], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12aababc-881a-4724-a9f5-b99ed51f3fe0", + "name": "Spawner (122)", + "location": [1216, 1823, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5396da5f-3f83-46e9-bd36-541a1af0972e", + "name": "Spawner (122)", + "location": [1186, 1633, 0], + "map": "Trammel", + "count": 28, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Bird", "maxCount": 7, "probability": 100 }, + { "name": "Cat", "maxCount": 7, "probability": 100 }, + { "name": "Dog", "maxCount": 7, "probability": 100 }, + { "name": "Rat", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3afe2f59-7cc4-482f-8d58-341c919c8fab", + "name": "Spawner (122)", + "location": [1185, 1632, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84e06a55-fdcb-4448-9560-9a71ae7778fb", + "name": "Spawner (122)", + "location": [1937, 2760, 10], + "map": "Trammel", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 120, + "walkingRange": 120, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "66a2657f-2e25-46fb-aca9-585773bd5646", + "name": "Spawner (122)", + "location": [608, 2195, 0], + "map": "Trammel", + "count": 32, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Cat", "maxCount": 8, "probability": 100 }, + { "name": "Dog", "maxCount": 8, "probability": 100 }, + { "name": "Rat", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "290150ac-103d-4117-a891-c09072cfb116", + "name": "Spawner (122)", + "location": [813, 2164, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd871bd2-e5c3-49ed-9b4e-73021199efe9", + "name": "Spawner (122)", + "location": [818, 2269, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa850e39-3bd1-43cf-9b34-c8756a3eeb2f", + "name": "Spawner (122)", + "location": [830, 2353, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c74c0ea9-7dae-43fd-99ab-1de27e40d2f8", + "name": "Spawner (122)", + "location": [536, 962, 0], + "map": "Trammel", + "count": 80, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 160, + "walkingRange": 160, + "entries": [ + { "name": "Bird", "maxCount": 20, "probability": 100 }, + { "name": "Cat", "maxCount": 20, "probability": 100 }, + { "name": "Dog", "maxCount": 20, "probability": 100 }, + { "name": "Rat", "maxCount": 20, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "52df17e8-b859-401f-b3ad-d49035f45428", + "name": "Spawner (122)", + "location": [676, 1178, 0], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "519581dd-727c-42fe-9b48-854998e02194", + "name": "Spawner (122)", + "location": [570, 1099, 0], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98ed6d30-11e2-4a50-9ca8-4e00cc255d17", + "name": "Spawner (122)", + "location": [675, 939, 0], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "75e5fd43-c65f-4862-b96b-be5fa6e722aa", + "name": "Spawner (122)", + "location": [382, 1192, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "97c5b1a1-5028-44ee-914e-c30b62486126", + "name": "Spawner (122)", + "location": [560, 1239, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81a21d4b-6f8a-44ae-a9e4-aca7eda1afa7", + "name": "Spawner (122)", + "location": [688, 1007, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c73117b7-d4f3-4fee-926d-b09a412dfa40", + "name": "Spawner (122)", + "location": [2242, 1197, 0], + "map": "Trammel", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ac7bee3c-854c-4da1-8676-87cfc0f100e6", + "name": "Spawner (122)", + "location": [2223, 1151, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Chicken", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "66b8967e-4a25-462a-bac2-70b7c6ee2b08", + "name": "Spawner (122)", + "location": [2939, 746, 2], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "253c3269-5141-4306-ab24-984c74677fa3", + "name": "Spawner (122)", + "location": [2904, 906, 0], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "827b225e-094a-4fe0-80ef-e4ae2e2057c6", + "name": "Spawner (122)", + "location": [2481, 542, 0], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be3e5a0a-6330-47c5-a254-0e0270696f3c", + "name": "Spawner (122)", + "location": [2472, 434, 15], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "45807a8e-eb86-4b7a-a592-a4d04b1c024f", + "name": "Spawner (122)", + "location": [2566, 622, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "Cat", "maxCount": 3, "probability": 100 }, + { "name": "Dog", "maxCount": 3, "probability": 100 }, + { "name": "Rat", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb452f73-c524-4b3b-ac31-861a5ae7c30e", + "name": "Spawner (122)", + "location": [1154, 3639, 0], + "map": "Trammel", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b058d9e8-aa3b-4410-a984-736338f185e5", + "name": "Spawner (122)", + "location": [1127, 3583, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ac8567f-97e0-4553-aefd-04404e6fa403", + "name": "Spawner (122)", + "location": [1183, 3607, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0596ff5d-53a1-4384-978a-11ec521c44ec", + "name": "Spawner (122)", + "location": [1400, 3775, 0], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8beee950-860e-45be-af5d-437fdd2b5eeb", + "name": "Spawner (122)", + "location": [1435, 4000, 0], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0dc3b704-8d6e-468e-ad7e-61b6097c289b", + "name": "Spawner (122)", + "location": [2972, 3436, 15], + "map": "Trammel", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "076e8845-e42b-434d-8df6-380aaf7a3383", "name": "Spawner (122)", "location": [3444, 2556, 30], @@ -11,12 +640,10 @@ "team": 0, "homeRange": 3, "walkingRange": 3, - "entries": [ - { "name": "Cat", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "Cat", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "af813f31-3e6a-4657-bd6d-640e0d74c328", "name": "Spawner (122)", "location": [3487, 2549, 16], @@ -32,5 +659,460 @@ { "name": "Cat", "maxCount": 5, "probability": 100 }, { "name": "Bird", "maxCount": 20, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "562f9c3f-4db0-4c6c-831d-83daf19d53ce", + "name": "Spawner (122)", + "location": [3714, 2205, 20], + "map": "Trammel", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0651efe3-7bee-409d-9934-7d686aabac52", + "name": "Spawner (122)", + "location": [3717, 2111, 20], + "map": "Trammel", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1c4cdc18-06dc-4ecf-9f82-e5a723220ee3", + "name": "Spawner (122)", + "location": [2682, 2118, 0], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "145f69d9-8fb7-4130-85d1-591799b5b309", + "name": "Spawner (122)", + "location": [2687, 2218, 0], + "map": "Trammel", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95de32e9-060f-4776-ac71-d112526ec4b6", + "name": "Spawner (122)", + "location": [3699, 1238, 0], + "map": "Trammel", + "count": 72, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 160, + "walkingRange": 160, + "entries": [ + { "name": "Bird", "maxCount": 18, "probability": 100 }, + { "name": "Cat", "maxCount": 18, "probability": 100 }, + { "name": "Dog", "maxCount": 18, "probability": 100 }, + { "name": "Rat", "maxCount": 18, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c7f550f6-bf86-41ff-86fe-406006dc60bb", + "name": "Spawner (122)", + "location": [4438, 1115, 0], + "map": "Trammel", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e763cbc-b631-4c4c-8594-1472369cd6f7", + "name": "Spawner (122)", + "location": [4634, 1302, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1208aae8-e36f-4170-97bf-76ebb6bebf09", + "name": "Spawner (122)", + "location": [4567, 1476, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1378a58c-09d4-4940-81fa-209f75870791", + "name": "Spawner (122)", + "location": [4422, 1451, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Chicken", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "74e17b0b-5fe4-4965-b55c-787441371f56", + "name": "Spawner (122)", + "location": [7036, 409, 12], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ae9c74fb-d8eb-4df7-aa22-412058583e8b", + "name": "Spawner (122)", + "location": [7048, 424, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4c814d05-058a-44a7-ad86-3e920797bc9e", + "name": "Spawner (122)", + "location": [7081, 381, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "42c9b654-4a10-4c16-8d34-487646e4c064", + "name": "Spawner (122)", + "location": [7002, 382, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e3d55bfe-9e18-4d68-ba3a-691cc402a410", + "name": "Spawner (122)", + "location": [6999, 352, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dd5c1a95-b5d7-4afa-8d8d-97a460e840b7", + "name": "Spawner (122)", + "location": [7047, 382, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f7b05c2-d4a9-4828-be39-b1f1de6c3861", + "name": "Spawner (122)", + "location": [7033, 385, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Bird", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "405031eb-1ebb-4793-91f7-82fd2c8c4590", + "name": "Spawner (122)", + "location": [7032, 411, 7], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "561930a3-2f73-4447-b49f-e4637f3fefdf", + "name": "Spawner (122)", + "location": [7044, 429, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "18bfc417-d6d9-4495-8215-979ef88c2e96", + "name": "Spawner (122)", + "location": [6986, 384, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12f99d2c-3a40-451c-a75c-28b31f86c35a", + "name": "Spawner (122)", + "location": [7063, 410, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0a4a9b61-4c34-441d-82b1-6e822fa8ceb4", + "name": "Spawner (122)", + "location": [7052, 386, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "39a9c600-a998-4cc2-b1a8-ee242efadaf2", + "name": "Spawner (122)", + "location": [7035, 387, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "089ed81a-4b3d-4a1a-aca7-10dd211255dc", + "name": "Spawner (122)", + "location": [7007, 382, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "446d1aec-f4ab-4692-b0a1-f05455ade58e", + "name": "Spawner (122)", + "location": [7087, 382, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "80dd3733-74a7-4a58-baf5-88b1eb228a36", + "name": "Spawner (122)", + "location": [6992, 345, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d1437b0a-56bb-440c-9625-8313955cdde6", + "name": "Spawner (122)", + "location": [7007, 360, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1b035b3b-80f4-4486-8571-9138925425f2", + "name": "Spawner (122)", + "location": [5726, 3211, -2], + "map": "Trammel", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "156e6770-5cef-4a8e-abc0-10ae90fe8331", + "name": "Spawner (122)", + "location": [5255, 4000, 37], + "map": "Trammel", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c14d6a08-a6b8-486d-bffe-2129d734437b", + "name": "Spawner (122)", + "location": [5267, 4012, 40], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Bull", "maxCount": 5, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/TownsPeople.json b/Distribution/Data/Spawns/post-uoml/trammel/TownsPeople.json similarity index 84% rename from Distribution/Data/Spawns/shared/trammel/TownsPeople.json rename to Distribution/Data/Spawns/post-uoml/trammel/TownsPeople.json index f04f33a9d..5f89175e5 100644 --- a/Distribution/Data/Spawns/shared/trammel/TownsPeople.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/TownsPeople.json @@ -1,6 +1,34 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "a1fec8b0-aeb0-4cfe-bedb-a630f60f483b", + "name": "Spawner (123)", + "location": [1400, 1623, 28], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d7c52fa-6f53-40ae-8f24-1daf0a7ce571", + "name": "Spawner (123)", + "location": [1400, 1628, 28], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "0cd9c91b-ab80-4dbd-8594-ace0132a0fc8", "name": "Spawner (123)", "location": [1521, 1457, 15], @@ -11,30 +39,192 @@ "team": 0, "homeRange": 0, "walkingRange": 1, - "entries": [ - { "name": "ChaosGuard", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "ChaosGuard", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "112087fa-8961-4d69-a729-19390bb722c5", + "type": "Spawner", + "guid": "fc87ea0f-7cd3-413a-a409-d1b304ec5299", "name": "Spawner (123)", - "location": [1126, 3688, -2], + "location": [1525, 1457, 15], "map": "Trammel", - "count": 3, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "ChaosGuard", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "81ee4607-e452-45ca-ac26-7c5a8d47f201", + "name": "Spawner (123)", + "location": [1662, 1611, 19], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d351ad8d-c82c-43b5-a10c-b7d42d47cc9f", + "name": "Spawner (123)", + "location": [1421, 1698, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81d50c82-2e5a-41d8-b572-f50a66302d6e", + "name": "Spawner (123)", + "location": [2254, 1207, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "faa84300-0e93-40c1-a328-863f8ed209fe", + "name": "Spawner (123)", + "location": [5290, 3987, 37], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8b75bcf8-7b83-4d94-8573-92aff88cd41f", + "name": "Spawner (123)", + "location": [3652, 2619, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c26e2162-6a56-4eeb-b6ec-300b0ef5ed0f", + "name": "Spawner (123)", + "location": [1330, 3778, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "56ff4f2b-88cc-44ce-bc5c-cadd416e069d", + "name": "Spawner (123)", + "location": [3721, 2161, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93c7a59d-449b-4993-b89d-8261edf96f2d", + "name": "Spawner (123)", + "location": [2515, 557, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ffaf3446-ee45-4f0f-9020-7508409e7da0", + "name": "Spawner (123)", + "location": [4451, 1153, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "34473f72-e1ae-4968-97aa-0388ead486b9", + "name": "Spawner (123)", + "location": [3773, 1308, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d746f23-5e37-467c-9b8c-2af3b1ba0882", + "name": "Spawner (123)", + "location": [5685, 3148, -4], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "756f5e4b-690b-4d80-a775-3ff67397e700", + "name": "Spawner (123)", + "location": [2891, 3474, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "17960040-e115-49e8-a1f3-f14583d9517f", "name": "Spawner (123)", "location": [603, 2141, 0], @@ -45,30 +235,66 @@ "team": 0, "homeRange": 1, "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "1de05315-2afe-4cc6-8b83-22fe3ebab00e", + "type": "Spawner", + "guid": "76bb7e24-7ad9-4dc2-97c8-f575ff05a259", "name": "Spawner (123)", - "location": [2994, 3451, 16], + "location": [1914, 2687, 0], "map": "Trammel", - "count": 3, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "2b35dd52-619d-448f-86d1-8aaa6f146ac7", + "name": "Spawner (123)", + "location": [1829, 2811, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "65e4866a-41f4-47ec-8f26-aa92f48aac80", + "name": "Spawner (123)", + "location": [2895, 686, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "36bf73a1-cc52-4776-8b8b-99088d86d03c", + "name": "Spawner (123)", + "location": [627, 864, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "26a2621e-6b2c-47ef-b957-4d5630091661", "name": "Spawner (123)", "location": [1399, 3743, -21], @@ -93,323 +319,7 @@ ] }, { - "$type": "Spawner", - "guid": "2b35dd52-619d-448f-86d1-8aaa6f146ac7", - "name": "Spawner (123)", - "location": [1829, 2811, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e006ab7-3370-4b4e-8fbc-cfa849b3d0c5", - "name": "Spawner (123)", - "location": [7082, 367, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Olaeni", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "31d1057e-9886-428e-82ef-4c08bf202117", - "name": "Spawner (123)", - "location": [3815, 1217, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3203a756-0011-4741-9fae-8a1c92166c45", - "name": "Spawner (123)", - "location": [3749, 1404, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "34473f72-e1ae-4968-97aa-0388ead486b9", - "name": "Spawner (123)", - "location": [3773, 1308, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "36bf73a1-cc52-4776-8b8b-99088d86d03c", - "name": "Spawner (123)", - "location": [627, 864, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37d7e7c7-3ef0-4b63-bc92-87b172a02533", - "name": "Spawner (123)", - "location": [7053, 338, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 6, - "entries": [ - { "name": "aneen", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4258947f-59d2-4fbb-98e7-11b65f16d8a9", - "name": "Spawner (123)", - "location": [7063, 350, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Bolaevin", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "46dc4d43-91c1-4d09-a0e0-9d2e89804977", - "name": "Spawner (123)", - "location": [7090, 387, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "Aluniol", "maxCount": 1, "probability": 100 }, - { "name": "Rebinil", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "56ff4f2b-88cc-44ce-bc5c-cadd416e069d", - "name": "Spawner (123)", - "location": [3721, 2161, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "64802440-be2d-4b3e-babb-d8612ae85e90", - "name": "Spawner (123)", - "location": [2875, 675, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "65e4866a-41f4-47ec-8f26-aa92f48aac80", - "name": "Spawner (123)", - "location": [2895, 686, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "756f5e4b-690b-4d80-a775-3ff67397e700", - "name": "Spawner (123)", - "location": [2891, 3474, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76bb7e24-7ad9-4dc2-97c8-f575ff05a259", - "name": "Spawner (123)", - "location": [1914, 2687, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "79236906-51cd-4390-86c5-36b5ea71f943", - "name": "Spawner (123)", - "location": [2874, 692, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81d50c82-2e5a-41d8-b572-f50a66302d6e", - "name": "Spawner (123)", - "location": [2254, 1207, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81e6cbf7-1e74-4547-b10f-024a75b64d40", - "name": "Spawner (123)", - "location": [2068, 2854, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81ee4607-e452-45ca-ac26-7c5a8d47f201", - "name": "Spawner (123)", - "location": [1662, 1611, 19], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "82760905-485e-4bbe-83b6-56ccc903665a", - "name": "Spawner (123)", - "location": [1372, 3916, -2], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "852fdf43-a6c6-4fb9-acd9-35b1943f4d11", "name": "Spawner (123)", "location": [1483, 1760, -2], @@ -427,24 +337,151 @@ ] }, { - "$type": "Spawner", - "guid": "86739b8d-2726-43ff-aeaa-562873cee364", + "type": "Spawner", + "guid": "82760905-485e-4bbe-83b6-56ccc903665a", "name": "Spawner (123)", - "location": [1813, 2834, 0], + "location": [1372, 3916, -2], "map": "Trammel", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 2, - "walkingRange": 2, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "112087fa-8961-4d69-a729-19390bb722c5", + "name": "Spawner (123)", + "location": [1126, 3688, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc65ad16-fde0-4172-b5c9-df90020f5ed5", + "name": "Spawner (123)", + "location": [3674, 2291, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e077b7d-aea5-4e45-9435-4cce0aaa6962", + "name": "Spawner (123)", + "location": [4398, 1049, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31d1057e-9886-428e-82ef-4c08bf202117", + "name": "Spawner (123)", + "location": [3815, 1217, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3203a756-0011-4741-9fae-8a1c92166c45", + "name": "Spawner (123)", + "location": [3749, 1404, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce3d7ff4-6411-4a30-914a-6b2bd1b9c752", + "name": "Spawner (123)", + "location": [3645, 2661, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1de05315-2afe-4cc6-8b83-22fe3ebab00e", + "name": "Spawner (123)", + "location": [2994, 3451, 16], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "88ba3663-c0bb-4d49-bcb5-cc4b6490b9cf", "name": "Spawner (123)", "location": [673, 2235, -3], @@ -462,74 +499,178 @@ ] }, { - "$type": "Spawner", - "guid": "8b75bcf8-7b83-4d94-8573-92aff88cd41f", + "type": "Spawner", + "guid": "d9dbef7c-e798-47c8-8939-7bbb74d297a9", "name": "Spawner (123)", - "location": [3652, 2619, 0], + "location": [573, 2246, 0], "map": "Trammel", - "count": 1, + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81e6cbf7-1e74-4547-b10f-024a75b64d40", + "name": "Spawner (123)", + "location": [2068, 2854, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea87d9c8-52d9-492a-9a5f-9b32bf2ee832", + "name": "Spawner (123)", + "location": [3041, 827, -3], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1071149-0c52-45c8-a0db-75da33c0a77e", + "name": "Spawner (123)", + "location": [631, 883, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "86739b8d-2726-43ff-aeaa-562873cee364", + "name": "Spawner (123)", + "location": [1813, 2834, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "64802440-be2d-4b3e-babb-d8612ae85e90", + "name": "Spawner (123)", + "location": [2875, 675, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "79236906-51cd-4390-86c5-36b5ea71f943", + "name": "Spawner (123)", + "location": [2874, 692, 0], + "map": "Trammel", + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d746f23-5e37-467c-9b8c-2af3b1ba0882", - "name": "Spawner (123)", - "location": [5685, 3148, -4], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d7c52fa-6f53-40ae-8f24-1daf0a7ce571", - "name": "Spawner (123)", - "location": [1400, 1628, 28], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, "walkingRange": 1, "entries": [ - { "name": "OrderGuard", "maxCount": 1, "probability": 100 } + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "93c7a59d-449b-4993-b89d-8261edf96f2d", + "type": "Spawner", + "guid": "f539d13c-6972-46fe-bce6-a09531c3473a", "name": "Spawner (123)", - "location": [2515, 557, 0], + "location": [2884, 692, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e006ab7-3370-4b4e-8fbc-cfa849b3d0c5", + "name": "Spawner (123)", + "location": [7082, 367, 0], "map": "Trammel", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 1, - "walkingRange": 5, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Olaeni", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4258947f-59d2-4fbb-98e7-11b65f16d8a9", + "name": "Spawner (123)", + "location": [7063, 350, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } + { "name": "Bolaevin", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "995f4053-0947-4757-8665-98cdac36ca2b", + "type": "Spawner", + "guid": "fd8c35db-5081-4db5-9859-2873a070fbac", "name": "Spawner (123)", - "location": [6989, 342, 0], + "location": [7010, 363, 0], "map": "Trammel", "count": 1, "minDelay": "00:05:00", @@ -537,12 +678,10 @@ "team": 0, "homeRange": 8, "walkingRange": 8, - "entries": [ - { "name": "Tyeelor", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "athialon", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "99c8e802-f37a-4f2e-b5d5-23239e0ba219", "name": "Spawner (123)", "location": [7048, 382, 0], @@ -563,25 +702,38 @@ ] }, { - "$type": "Spawner", - "guid": "9e077b7d-aea5-4e45-9435-4cce0aaa6962", + "type": "Spawner", + "guid": "995f4053-0947-4757-8665-98cdac36ca2b", "name": "Spawner (123)", - "location": [4398, 1049, -2], + "location": [6989, 342, 0], "map": "Trammel", - "count": 3, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 20, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Tyeelor", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "37d7e7c7-3ef0-4b63-bc92-87b172a02533", + "name": "Spawner (123)", + "location": [7053, 338, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } + { "name": "aneen", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9f7c934c-8e02-433d-a88f-4c8769a02081", "name": "Spawner (123)", "location": [7031, 409, 7], @@ -598,222 +750,20 @@ ] }, { - "$type": "Spawner", - "guid": "a1fec8b0-aeb0-4cfe-bedb-a630f60f483b", + "type": "Spawner", + "guid": "46dc4d43-91c1-4d09-a0e0-9d2e89804977", "name": "Spawner (123)", - "location": [1400, 1623, 28], + "location": [7090, 387, 0], "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 1, - "entries": [ - { "name": "OrderGuard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bc65ad16-fde0-4172-b5c9-df90020f5ed5", - "name": "Spawner (123)", - "location": [3674, 2291, -2], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c26e2162-6a56-4eeb-b6ec-300b0ef5ed0f", - "name": "Spawner (123)", - "location": [1330, 3778, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ce3d7ff4-6411-4a30-914a-6b2bd1b9c752", - "name": "Spawner (123)", - "location": [3645, 2661, -2], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d351ad8d-c82c-43b5-a10c-b7d42d47cc9f", - "name": "Spawner (123)", - "location": [1421, 1698, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9dbef7c-e798-47c8-8939-7bbb74d297a9", - "name": "Spawner (123)", - "location": [573, 2246, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e1071149-0c52-45c8-a0db-75da33c0a77e", - "name": "Spawner (123)", - "location": [631, 883, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ea87d9c8-52d9-492a-9a5f-9b32bf2ee832", - "name": "Spawner (123)", - "location": [3041, 827, -3], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, - { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, - { "name": "Noble", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f539d13c-6972-46fe-bce6-a09531c3473a", - "name": "Spawner (123)", - "location": [2884, 692, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "faa84300-0e93-40c1-a328-863f8ed209fe", - "name": "Spawner (123)", - "location": [5290, 3987, 37], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fc87ea0f-7cd3-413a-a409-d1b304ec5299", - "name": "Spawner (123)", - "location": [1525, 1457, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 1, - "entries": [ - { "name": "ChaosGuard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fd8c35db-5081-4db5-9859-2873a070fbac", - "name": "Spawner (123)", - "location": [7010, 363, 0], - "map": "Trammel", - "count": 1, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 8, "walkingRange": 8, "entries": [ - { "name": "athialon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ffaf3446-ee45-4f0f-9020-7508409e7da0", - "name": "Spawner (123)", - "location": [4451, 1153, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } + { "name": "Aluniol", "maxCount": 1, "probability": 100 }, + { "name": "Rebinil", "maxCount": 1, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/post-uoml/trammel/TramCropsLS.json b/Distribution/Data/Spawns/post-uoml/trammel/TramCropsLS.json new file mode 100644 index 000000000..ba95364c1 --- /dev/null +++ b/Distribution/Data/Spawns/post-uoml/trammel/TramCropsLS.json @@ -0,0 +1,290 @@ +[ + { + "type": "RegionSpawner", + "location": [375, 1191, 0], + "map": "Trammel", + "region": "A Wheatfield in Yew 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "fb826650-9fad-43ff-8ba1-9d848f1f26c5", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [567, 1239, 0], + "map": "Trammel", + "region": "A Wheatfield in Yew 1", + "count": 5, + "entries": [{ "maxCount": 5, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "79af53a5-33e2-4fdb-95dd-278a050e54fc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [571, 1099, 0], + "map": "Trammel", + "region": "A Farm in Yew", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "8408baaa-0de1-4cb0-ad89-973782e3a565", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [675, 939, 0], + "map": "Trammel", + "region": "A Field of Sheep in Yew 1", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "b17f2f9c-9d36-4bea-87db-69f8effc4926", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [675, 1179, 0], + "map": "Trammel", + "region": "A Field of Sheep in Yew 2", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "690ccc1c-2ac8-4f05-8066-695aa53c98aa", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [813, 2163, 0], + "map": "Trammel", + "region": "A Wheatfield in Skara Brae 1", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "ee6692af-82cf-477e-96ec-375be7a23db9", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2254, 0], + "map": "Trammel", + "region": "A Carrot Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "c052a5e0-493d-483e-a98a-665cf05c5c14", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2264, 0], + "map": "Trammel", + "region": "An Onion Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableOnion", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "41a583ac-749c-40a7-8658-fb3deb1b222a", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2264, 0], + "map": "Trammel", + "region": "A Cabbage Field in Skara Brae 1", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "df4c5918-b490-4257-9aeb-541b44980b27", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2274, 0], + "map": "Trammel", + "region": "A Cabbage Field in Skara Brae 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "1025906e-047c-460b-9bb8-581f57ff1357", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2355, 0], + "map": "Trammel", + "region": "A Cotton Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCotton", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "185e475b-36cf-4dad-8776-537e84b15e42", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [843, 2352, 0], + "map": "Trammel", + "region": "A Wheatfield in Skara Brae 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "59e99d3b-b727-491b-b0f3-03a441b10c04", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1120, 1624, 0], + "map": "Trammel", + "region": "A Wheatfield in Britain 4", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "85cc6fca-4f66-4428-aa3a-347a88523a4b", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1135, 1791, 0], + "map": "Trammel", + "region": "A Wheatfield in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "afdfec82-dac3-43df-bd7b-cd2cfad5f265", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1152, 1576, 0], + "map": "Trammel", + "region": "A Wheatfield in Britain 5", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "34b0579c-1fc6-4868-82c5-09138c5e5878", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1183, 1683, 0], + "map": "Trammel", + "region": "A Cabbage Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "997d9ff5-ad2b-421b-ae76-6794e7e88150", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1199, 1683, 0], + "map": "Trammel", + "region": "A Turnip Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableTurnip", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "bfb677f7-e824-46f6-95ef-ee5602f4f9c3", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1199, 1823, 0], + "map": "Trammel", + "region": "A Wheatfield in Britain 2", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "c42ef172-78c8-4c5e-b26c-fcc3fc3da1cc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1215, 1723, 0], + "map": "Trammel", + "region": "A Carrot Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "57551e80-0018-4fcf-8b83-9d39ef19b0a6", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1216, 1604, 0], + "map": "Trammel", + "region": "A Turnip Field in Britain 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableTurnip", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "0da29e36-7bff-4fcf-803d-79dd16212653", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1231, 1723, 0], + "map": "Trammel", + "region": "An Onion Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableOnion", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "4656ad9d-a455-4217-9316-ec10d10cf68c", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1231, 1887, 0], + "map": "Trammel", + "region": "A Wheatfield in Britain 3", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "d5b08634-5c5d-4bc2-b6cd-74cb06132423", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1232, 1604, 0], + "map": "Trammel", + "region": "A Carrot Field in Britain 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "ab0cb920-b25f-4811-8ef6-938549dbfa27", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [4567, 1475, 0], + "map": "Trammel", + "region": "A Cotton Field in Moonglow", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCotton", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "69dfbe70-b46c-42b0-9ff1-351fd9651884", + "name": "Spawner" + } +] diff --git a/Distribution/Data/Spawns/shared/trammel/TrinsicPassage.json b/Distribution/Data/Spawns/post-uoml/trammel/TrinsicPassage.json similarity index 96% rename from Distribution/Data/Spawns/shared/trammel/TrinsicPassage.json rename to Distribution/Data/Spawns/post-uoml/trammel/TrinsicPassage.json index 131c04797..3b4d30297 100644 --- a/Distribution/Data/Spawns/shared/trammel/TrinsicPassage.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/TrinsicPassage.json @@ -1,66 +1,6 @@ [ { - "$type": "Spawner", - "guid": "01aee8cd-fc2b-4e00-8d21-991131290d9b", - "name": "Spawner (124)", - "location": [5999, 1327, 10], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "098f3928-ea97-4278-ad09-63a202b30fea", - "name": "Spawner (124)", - "location": [5956, 1310, -2], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1e21e474-6f3f-4e15-b16a-da201c371cfe", - "name": "Spawner (124)", - "location": [5912, 1326, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "HellCat", "maxCount": 1, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, - { "name": "LavaSnake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "54cb95e8-ceb7-42de-872f-6c2208f075c1", "name": "Spawner (124)", "location": [5976, 1387, -22], @@ -80,7 +20,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bdc0fed3-7087-4713-9690-92e8878992cb", "name": "Spawner (124)", "location": [5940, 1356, -2], @@ -100,7 +40,67 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "1e21e474-6f3f-4e15-b16a-da201c371cfe", + "name": "Spawner (124)", + "location": [5912, 1326, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "098f3928-ea97-4278-ad09-63a202b30fea", + "name": "Spawner (124)", + "location": [5956, 1310, -2], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01aee8cd-fc2b-4e00-8d21-991131290d9b", + "name": "Spawner (124)", + "location": [5999, 1327, 10], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "faefe911-578d-4e50-b1b5-918119689237", "name": "Spawner (124)", "location": [5915, 1302, 2], diff --git a/Distribution/Data/Spawns/post-uoml/trammel/Vendors.json b/Distribution/Data/Spawns/post-uoml/trammel/Vendors.json index adc106250..ab42e8ed5 100644 --- a/Distribution/Data/Spawns/post-uoml/trammel/Vendors.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Vendors.json @@ -1,6 +1,2695 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "558ec561-9e56-4d94-8dc0-c86161bdb6a4", + "name": "Spawner (125)", + "location": [1685, 2985, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Herbalist", "maxCount": 2, "probability": 100 }, + { "name": "Mage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0339e1b7-4167-4d40-bc12-1cb62da96006", + "name": "Spawner (125)", + "location": [1650, 1642, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "71f6509c-73fc-4b66-97e0-73117fa77c0c", + "name": "Spawner (125)", + "location": [1450, 1617, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d3a3af04-33a1-478f-856d-210fdbb5a9ce", + "name": "Spawner (125)", + "location": [1418, 1547, 30], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d1dbe32d-b712-4b26-8c3c-f34e94ffeeb2", + "name": "Spawner (125)", + "location": [1471, 1611, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "643c0e84-5e3f-40db-ac86-3c17c38a316f", + "name": "Spawner (125)", + "location": [1447, 1647, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dced8f77-228f-4246-9b0a-96539575a2b0", + "name": "Spawner (125)", + "location": [1590, 1654, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ced2b237-e589-49f6-9468-9c6b5bbb8ca4", + "name": "Spawner (125)", + "location": [1455, 1557, 30], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Bard", "maxCount": 1, "probability": 100 }, + { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98ab2774-ef8a-402a-805b-fd3f035f0458", + "name": "Spawner (125)", + "location": [1388, 1588, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d69a466-479b-4419-98df-ad54be6244cc", + "name": "Spawner (125)", + "location": [1362, 1574, 30], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2aebb1a5-2e39-424d-9e9b-e8254a5e307d", + "name": "Spawner (125)", + "location": [1467, 1686, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "42088948-8d4a-4745-8ecc-b0f6a6f5f5db", + "name": "Spawner (125)", + "location": [1474, 1597, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8562df8d-5b04-4426-8f62-4125b0bb81c8", + "name": "Spawner (125)", + "location": [1417, 1578, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1647a7af-7960-4b9e-87aa-99e10a89920d", + "name": "Spawner (125)", + "location": [1458, 1525, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "62c4349d-d176-4b3d-8df5-c8b624a55423", + "name": "Spawner (125)", + "location": [1416, 1754, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbd51949-6b2b-4973-8e5f-67aa1504d28b", + "name": "Spawner (125)", + "location": [1451, 1679, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "327a996b-05f3-4523-b029-90d7c4984a31", + "name": "Spawner (125)", + "location": [1602, 1712, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5e06b77-5783-4d11-8468-2759e524524d", + "name": "Spawner (125)", + "location": [1470, 1578, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1a9355e3-e45f-43b3-bc0d-2afb86873a1c", + "name": "Spawner (125)", + "location": [1547, 1659, 26], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41161b38-3f7c-478f-92c2-53f0dd3e9dad", + "name": "Spawner (125)", + "location": [1620, 1586, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a301e23d-1bfd-46ef-a2fc-dd4ca9756255", + "name": "Spawner (125)", + "location": [1430, 1597, 20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "29a793dc-b980-4c98-a362-f90f50121b3b", + "name": "Spawner (125)", + "location": [1485, 1550, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c937726b-6b62-4b10-8598-9393b597ef90", + "name": "Spawner (125)", + "location": [1296, 1759, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "991ca64d-4d48-49d8-a2a0-71a9d23fb2b3", + "name": "Spawner (125)", + "location": [1388, 1655, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8c6bc394-02b5-4830-8848-55fbb6623a7a", + "name": "Spawner (125)", + "location": [1481, 1584, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40f86110-4700-404e-865a-0624ee3e88df", + "name": "Spawner (125)", + "location": [1493, 1616, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b6c600be-26e6-4553-bd5b-17986a50c0ef", + "name": "Spawner (125)", + "location": [1422, 1654, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2bb96113-3a2f-4092-b055-1883f7842b22", + "name": "Spawner (125)", + "location": [1547, 1768, 10], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0435d4c7-1502-4434-b152-9ed1f6c77cf0", + "name": "Spawner (125)", + "location": [1341, 1733, 20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "hirefighter", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "360bf7a9-be1c-453a-b74a-3d05f91508eb", + "name": "Spawner (125)", + "location": [1585, 1591, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adb4bd49-a4b7-48da-bd26-527aa960c988", + "name": "Spawner (125)", + "location": [1637, 1693, 37], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a48baab3-ef4a-428b-849d-f96b7fe2270c", + "name": "Spawner (125)", + "location": [1431, 1612, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b46e3bb9-2dba-4235-9d9a-e9a40e1b142a", + "name": "Spawner (125)", + "location": [1498, 1691, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "335880dc-a64c-488d-98c6-cadf4f8bca54", + "name": "Spawner (125)", + "location": [1470, 1765, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1a27bb4d-874d-4821-8ce3-a669d83ee6f1", + "name": "Spawner (125)", + "location": [1409, 1590, 42], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ca7daed-db6e-487c-a7b0-ea5448dd368c", + "name": "Spawner (125)", + "location": [1349, 1778, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Blacksmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e07cf894-eb63-4d4b-9096-e57bed868aca", + "name": "Spawner (125)", + "location": [1469, 1668, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2ae2226-0a3a-4bef-9683-cffb0c59975f", + "name": "Spawner (125)", + "location": [1510, 1543, 25], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5f4a1c5c-64b3-4636-9067-b309c48b93d3", + "name": "Spawner (125)", + "location": [1427, 1716, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "586454ef-4cc0-49a4-a2e4-89574c326a66", + "name": "Spawner (125)", + "location": [1449, 1723, 6], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5635ef7b-3e30-4f77-9dcc-1d32e90756b9", + "name": "Spawner (125)", + "location": [1650, 1608, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31da09ce-311f-409b-9c68-34b8058df581", + "name": "Spawner (125)", + "location": [1498, 1659, 27], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "043d3956-11cc-4f4f-a014-7752c464e34c", + "name": "Spawner (125)", + "location": [1425, 1690, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebf92564-8ea0-4ca8-bec5-8d51bd4e984c", + "name": "Spawner (125)", + "location": [1494, 1715, 6], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47b0d916-8f93-4580-b4c1-b9408b377a8b", + "name": "Spawner (125)", + "location": [1405, 1810, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Mage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5652fc2b-5bb0-4f6b-b01c-fa8a302ad4d3", + "name": "Spawner (125)", + "location": [1482, 1761, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "507f2688-b5dd-404d-b31b-50b0914c0e58", + "name": "Spawner (125)", + "location": [1228, 1572, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3330ffe8-07f8-4a7d-9515-34e5333dfe48", + "name": "Spawner (125)", + "location": [1355, 1754, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 14, + "entries": [ + { "name": "OrderGuard", "maxCount": 1, "probability": 100 }, + { "name": "hirefighter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "839452cf-a264-4ccc-983b-6cd13565676d", + "name": "Spawner (125)", + "location": [1243, 1699, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d3324c6-7226-45eb-af13-35bb32a227ac", + "name": "Spawner (125)", + "location": [1351, 1816, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "EscortableMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "33929eec-3fe3-4b78-978f-f1bef78a28eb", + "name": "Spawner (125)", + "location": [1306, 1804, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 6, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2c011c1-e49f-4005-86e9-ffec9a526573", + "name": "Spawner (125)", + "location": [1304, 1826, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "Peasant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9f0eb4c9-1e11-4abf-9459-31f25308fe70", + "name": "Spawner (125)", + "location": [1362, 1825, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 12, + "entries": [{ "name": "Artist", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adcf36e0-59c9-4804-8c64-8ac46e1dc0b4", + "name": "Spawner (125)", + "location": [1333, 1808, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "escortablewanderinghealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "54232896-d8a8-41ef-964e-1a8a426be590", + "name": "Spawner (125)", + "location": [1150, 1619, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cbb75550-e870-42fe-9689-d99120baf85b", + "name": "Spawner (125)", + "location": [1149, 1547, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0dd0b6c-633d-426a-8c36-3c2853ecd252", + "name": "Spawner (125)", + "location": [1295, 1773, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "BrideGroom", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "928e9340-fb04-4d78-94f9-8ec141fe6fb2", + "name": "Spawner (125)", + "location": [1316, 1747, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "hirefighter", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8b249196-07e0-42de-a67e-d6959d1ca4c9", + "name": "Spawner (125)", + "location": [1231, 1714, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "hirepeasant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d45157f9-2bf0-4101-8e11-a06255f0f3bf", + "name": "Spawner (125)", + "location": [1190, 1707, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 8, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "472a07d2-cb45-4ca4-9e2d-4153faae4d0e", + "name": "Spawner (125)", + "location": [1427, 1726, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "hirebard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1facba9c-8639-4def-bb21-08abc7456e8e", + "name": "Spawner (125)", + "location": [1438, 1761, -2], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [ + { "name": "hiresailor", "maxCount": 1, "probability": 100 }, + { "name": "Fisherman", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fa0e5cb-22de-46a4-9620-4f7103685d53", + "name": "Spawner (125)", + "location": [1477, 1741, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "hiresailor", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "469ffd2c-d656-45ca-89c5-f0a7fedf6176", + "name": "Spawner (125)", + "location": [1436, 1752, 14], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "HarborMaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "32655bec-db85-4a3e-95b4-c21dcf576d0a", + "name": "Spawner (125)", + "location": [1434, 1717, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Merchant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0537535f-e69b-4c83-8eaa-09b27fd71a85", + "name": "Spawner (125)", + "location": [2709, 2130, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fac7f6c9-0564-4baf-b2aa-9bf3a9e4b35c", + "name": "Spawner (125)", + "location": [2715, 2098, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2b03cbc1-378c-45ab-b01c-95d9e4944647", + "name": "Spawner (125)", + "location": [2659, 2194, 4], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "ThiefGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f723423b-7c27-48c1-b662-335797e6dff2", + "name": "Spawner (125)", + "location": [2680, 2238, 2], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a13d9bc-849b-4a5b-96b0-0afe64cad4d6", + "name": "Spawner (125)", + "location": [2737, 2250, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1b6c520f-c677-481b-9a8e-fa2ab5658a16", + "name": "Spawner (125)", + "location": [2731, 2192, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4bc9a4dd-1844-483d-997d-e73e53279198", + "name": "Spawner (125)", + "location": [2627, 2100, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "956834dc-651a-42d7-974b-b2bac74e903e", + "name": "Spawner (125)", + "location": [2752, 2155, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2b7a7c2b-f82b-4cbc-a83d-f9ff58b87562", + "name": "Spawner (125)", + "location": [2707, 2178, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "134c9621-c59d-47ef-9afc-b47e1d99e0bd", + "name": "Spawner (125)", + "location": [2634, 2083, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a2fbed0-d494-4930-8238-a74b2489e23a", + "name": "Spawner (125)", + "location": [2245, 1231, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bfdfa0ef-f98a-4095-ad75-c87737eb95a2", + "name": "Spawner (125)", + "location": [2216, 1167, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "808ba57f-46db-4659-b20c-52b61d520894", + "name": "Spawner (125)", + "location": [2216, 1192, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a79cb65c-847a-4b9c-b544-48de9bd6d103", + "name": "Spawner (125)", + "location": [2256, 1181, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "942aa3c4-39fc-4c2c-83cd-31336753d2e8", + "name": "Spawner (125)", + "location": [1435, 3820, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2150f1f-5cd9-4d5f-a340-7f4bfd0b3214", + "name": "Spawner (125)", + "location": [1404, 3802, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98464fd7-e5b6-4caa-a6a2-9164399191e6", + "name": "Spawner (125)", + "location": [1452, 3770, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3738191d-80e0-44da-936a-6a7ede0035a2", + "name": "Spawner (125)", + "location": [1456, 3850, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6a14d4b-f7c8-427e-a0c8-a1d972e3f3f6", + "name": "Spawner (125)", + "location": [1317, 3773, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54250593-bd68-4c4d-9017-593ea8a93c8d", + "name": "Spawner (125)", + "location": [1372, 3908, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ee56cd69-1e8d-46fe-906f-e3e91614708a", + "name": "Spawner (125)", + "location": [1512, 3989, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1b882487-2ce6-4819-8db4-7077c0217394", + "name": "Spawner (125)", + "location": [1504, 3693, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47d6019a-a955-4f5a-bcff-da5d6cb6acea", + "name": "Spawner (125)", + "location": [1125, 3687, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a318c022-1463-466e-95d1-e59b2cde9a63", + "name": "Spawner (125)", + "location": [1416, 3779, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d033238c-2772-4748-b09d-e03dd3a18768", + "name": "Spawner (125)", + "location": [1387, 3771, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3d9b805c-a0d7-4a4d-82a9-49227a879550", + "name": "Spawner (125)", + "location": [1425, 3981, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4eec52f5-a2ee-41fe-b83d-e4ef31e59cc4", + "name": "Spawner (125)", + "location": [1454, 4003, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d24ec90-79b5-4881-bcd6-568034cd3e2a", + "name": "Spawner (125)", + "location": [1360, 3815, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92c0ad53-3da1-446d-a39e-86131211cbb3", + "name": "Spawner (125)", + "location": [1442, 3802, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Provisioner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d114445e-e1ae-41b0-890e-49b568c65f68", + "name": "Spawner (125)", + "location": [1442, 3802, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Cobbler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "23f532bd-b465-4865-9e87-1acb1f975bb7", + "name": "Spawner (125)", + "location": [1437, 3750, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4e0c3ef6-5714-4bc8-8d0f-8cad7008c2b3", + "name": "Spawner (125)", + "location": [1447, 3981, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6c7d0542-d02a-49e2-b03d-2f27b68074e8", + "name": "Spawner (125)", + "location": [1452, 3747, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f85575f5-813e-48cc-b0a5-2eb116e610bd", + "name": "Spawner (125)", + "location": [1441, 3719, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b4c90a7-f7e7-41a8-a4f1-0e37591c1cd9", + "name": "Spawner (125)", + "location": [1419, 3859, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da93faab-336b-403b-8628-da3da7457f7f", + "name": "Spawner (125)", + "location": [1356, 3780, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0c400ce-9cdc-401a-8985-84dc26849dec", + "name": "Spawner (125)", + "location": [1364, 3732, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44c7bc31-c445-42e3-afa6-e86d6beac11b", + "name": "Spawner (125)", + "location": [1475, 3865, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2fff56f-b197-45be-a68c-af29db75338f", + "name": "Spawner (125)", + "location": [1354, 3754, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc7ab28c-9717-4c5d-938c-d6ea01440de7", + "name": "Spawner (125)", + "location": [1386, 3825, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "419c9bd7-ce77-4ef9-bdd6-2a913c93534b", + "name": "Spawner (125)", + "location": [1450, 4026, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2a605fd6-6428-4bd5-9571-ddd030de003c", + "name": "Spawner (125)", + "location": [1395, 3705, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c34b2d6-d0d5-4eb1-a0c2-53132b5e7b06", + "name": "Spawner (125)", + "location": [3683, 2252, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d9fe1303-74c5-490c-9d08-e43325425919", + "name": "Spawner (125)", + "location": [3667, 2252, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "482cd8b9-c3af-4b1c-9154-f722aa67b0ed", + "name": "Spawner (125)", + "location": [3727, 2224, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9b981d8-c473-411a-8655-19c74ef5fbe5", + "name": "Spawner (125)", + "location": [3687, 2227, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62d67d9c-82af-4b44-be9a-f1ac7ea02274", + "name": "Spawner (125)", + "location": [3674, 2289, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50b0e650-9163-4263-a71a-3912aa87b5c7", + "name": "Spawner (125)", + "location": [3665, 2140, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "db27e125-6f28-4335-a94d-ec1a349a9aba", + "name": "Spawner (125)", + "location": [3699, 2218, 20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4122c08b-45c7-46ae-8be2-f26a761c57c6", + "name": "Spawner (125)", + "location": [3703, 2249, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "07e55a6f-82ae-4f79-b758-f327f002a1cd", + "name": "Spawner (125)", + "location": [3699, 2163, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d1a1d0f-e8be-4947-90c6-e9611a3c86d7", + "name": "Spawner (125)", + "location": [3666, 2235, 20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f649d8d-7a53-46a1-bdab-64f309d4e6c7", + "name": "Spawner (125)", + "location": [3720, 2126, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9936a583-3fc5-4414-b85a-94fe319831b8", + "name": "Spawner (125)", + "location": [3755, 2227, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1ea130cc-27cc-4640-8351-976a0858188d", + "name": "Spawner (125)", + "location": [3734, 2149, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0402be0f-a2d2-4116-8c36-c7af2c230447", + "name": "Spawner (125)", + "location": [3683, 2171, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76b3a8d0-aa01-4956-bd37-e2bcd462e8e6", + "name": "Spawner (125)", + "location": [3661, 2183, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0bd547bd-7fe4-4869-bb5c-17a297013e44", + "name": "Spawner (125)", + "location": [2471, 564, 5], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f1fe615-63ac-460b-93e6-11eead781649", + "name": "Spawner (125)", + "location": [2461, 457, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85bf65b2-ed34-4719-bbca-e9449e8c1989", + "name": "Spawner (125)", + "location": [2505, 432, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f46915ea-0d08-4bca-bb53-335f8c12f07b", + "name": "Spawner (125)", + "location": [2577, 599, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bcefd37-a195-42d2-bb43-7087fa46c165", + "name": "Spawner (125)", + "location": [2455, 487, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ae039b67-65da-48a1-9d66-397f7f963c24", + "name": "Spawner (125)", + "location": [2424, 555, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Bard", "maxCount": 1, "probability": 100 }, + { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61d32da8-8cac-4113-8936-85f8836fb6f3", + "name": "Spawner (125)", + "location": [2478, 427, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e2bd9c74-593e-453e-9ae1-d8bd4fa67993", + "name": "Spawner (125)", + "location": [2513, 477, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a226f47-4b27-48ad-8786-77834d26479d", + "name": "Spawner (125)", + "location": [2450, 428, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3803febb-c2fc-4d51-a5cd-6faee0570a6a", + "name": "Spawner (125)", + "location": [2438, 410, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f120e595-13e3-4c97-a2f6-0ad960f38b0c", + "name": "Spawner (125)", + "location": [2526, 375, 23], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b434162b-e02d-4bf9-88ee-2dda13e9278a", + "name": "Spawner (125)", + "location": [2522, 524, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "52358248-3191-4dee-9e78-ea64d8370189", + "name": "Spawner (125)", + "location": [2526, 546, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e0a225e-8ab0-443c-b125-ab3ad0956662", + "name": "Spawner (125)", + "location": [2533, 572, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f14276d9-00a3-4122-aa2a-280dd4f8fac7", + "name": "Spawner (125)", + "location": [2503, 552, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba540c4e-a870-4886-90ba-d6c2f466d287", + "name": "Spawner (125)", + "location": [2475, 397, 15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "43174d70-989d-4a19-a02b-e39ffab8619c", + "name": "Spawner (125)", + "location": [4416, 1133, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5cc0651f-cc23-464c-8dd2-8c924688e76f", + "name": "Spawner (125)", + "location": [4392, 1068, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "46e1f663-0c59-4a2d-8f66-00f2a0d5195e", + "name": "Spawner (125)", + "location": [4416, 1085, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "049e60c9-d17c-4dd7-b41f-72a029d03e08", + "name": "Spawner (125)", + "location": [4392, 1117, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f163cb5b-d232-4cf8-ae17-2d13fe08623d", + "name": "Spawner (125)", + "location": [4406, 1038, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12e7d6c0-93ec-4a46-aa65-03407c9a409c", + "name": "Spawner (125)", + "location": [4394, 1083, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94408e1a-950c-424f-8d22-e75005e1b85e", + "name": "Spawner (125)", + "location": [4409, 1111, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e447298-7816-42f1-889a-cf6c8423fb58", + "name": "Spawner (125)", + "location": [4484, 1064, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7c18a846-5733-4274-969d-c219443a8c95", + "name": "Spawner (125)", + "location": [4448, 1090, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85c7a253-36a4-4352-b90e-b5e28c8dac8c", + "name": "Spawner (125)", + "location": [4458, 1060, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d2b5765-87c1-48c6-a9a6-27699ea30d72", + "name": "Spawner (125)", + "location": [4417, 1064, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9917a0c7-7df7-4ebc-a830-c9fe30b20354", + "name": "Spawner (125)", + "location": [4401, 1165, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aafefa52-d26c-4735-a2bc-b73bd8285db1", + "name": "Spawner (125)", + "location": [4440, 1162, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd48eb05-e200-4fc5-aa5d-8581121d0d79", + "name": "Spawner (125)", + "location": [4545, 860, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5c623567-93cd-4602-82a0-d110513635db", + "name": "Spawner (125)", + "location": [4481, 1085, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91c818b9-32b8-4d84-b2cc-ada32cbad1ff", + "name": "Spawner (125)", + "location": [4395, 1137, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "73b5f8a6-6c6d-4837-b3ca-7ff7e90ff27d", + "name": "Spawner (125)", + "location": [4471, 1156, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0929d639-9f70-42aa-b714-608c783bb2df", + "name": "Spawner (125)", + "location": [3778, 1172, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "de025061-4683-42e7-aaae-a864a81ea5a9", + "name": "Spawner (125)", + "location": [3769, 1218, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36841b9f-477e-49d9-88a3-d66da91aaef4", + "name": "Spawner (125)", + "location": [3546, 1185, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa5b770c-5cf3-4c34-bf04-b569d2997239", + "name": "Spawner (125)", + "location": [3546, 1194, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ded253d-b661-4582-a15d-79282c15e8e8", + "name": "Spawner (125)", + "location": [3555, 1171, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cc884f40-cdb6-4c6d-8be6-c9d15216dc51", + "name": "Spawner (125)", + "location": [3545, 1178, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09169e10-d045-41c3-9c74-7bca043adb3c", + "name": "Spawner (125)", + "location": [3554, 1202, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4166ae1-15e1-4540-a3ef-c08ab843c639", + "name": "Spawner (125)", + "location": [3731, 1320, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e87996f7-c1cc-46e1-a157-854b1049fa7e", + "name": "Spawner (125)", + "location": [3700, 1214, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae951a02-cc60-40f1-b810-9f09c34b4b7a", + "name": "Spawner (125)", + "location": [3729, 1258, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e2553ce-54f2-4cb4-9e20-9625257f4192", + "name": "Spawner (125)", + "location": [3774, 1265, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dc2017e-65fa-4231-9fc7-b1aa7c48f1b5", + "name": "Spawner (125)", + "location": [3764, 1317, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d94255da-ddc6-4df5-af70-9de05ab71faa", + "name": "Spawner (125)", + "location": [3740, 1197, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "BardGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "3e236130-7110-4161-87f5-74862a376118", "name": "Spawner (125)", "location": [3484, 2570, 20], @@ -11,12 +2700,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Banker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "12f15214-6492-4a29-807b-5023bc6e8184", "name": "Spawner (125)", "location": [3470, 2519, 49], @@ -27,12 +2714,10 @@ "team": 0, "homeRange": 0, "walkingRange": 3, - "entries": [ - { "name": "HairStylist", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "HairStylist", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2b817585-83c8-436a-85b8-a372532a32ec", "name": "Spawner (125)", "location": [3504, 2612, 0], @@ -43,12 +2728,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "66a41153-b0cc-4f62-893b-172454cd1264", "name": "Spawner (125)", "location": [3495, 2590, 35], @@ -59,12 +2742,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Shipwright", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9db0cd07-38eb-412a-a328-46ccf2dbf096", "name": "Spawner (125)", "location": [3496, 2547, 20], @@ -75,12 +2756,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Weaver", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Weaver", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5f102f6a-04cc-4f48-b793-fc29c36b7ddb", "name": "Spawner (125)", "location": [3497, 2552, 20], @@ -91,12 +2770,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Tailor", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Tailor", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e2a7c76a-b207-45b3-aaea-82b9aed19261", "name": "Spawner (125)", "location": [3494, 2551, 20], @@ -107,12 +2784,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a9ef5ff7-852a-4a9b-a9cb-da0b70e6c96f", "name": "Spawner (125)", "location": [3481, 2569, 20], @@ -123,12 +2798,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Minter", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "3d96a45c-ad7a-40f8-98a0-5080ff9066e5", "name": "Spawner (125)", "location": [3481, 2575, 20], @@ -139,12 +2812,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Minter", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f713041b-8980-4839-84cb-232cb39f710a", "name": "Spawner (125)", "location": [3463, 2558, 35], @@ -155,12 +2826,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Healer", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Healer", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e76ec8dd-9238-46d2-82a6-5896e68b5431", "name": "Spawner (125)", "location": [3463, 2558, 36], @@ -171,12 +2840,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1c62a5b0-f9fc-45b4-b835-798259d544ff", "name": "Spawner (125)", "location": [3502, 2414, 55], @@ -187,12 +2854,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "HolyMage", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "HolyMage", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5199f10b-7a24-4aab-89d8-4df0dcf757a7", "name": "Spawner (125)", "location": [3554, 2457, 15], @@ -203,12 +2868,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "EvilWanderingHealer", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "EvilWanderingHealer", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "256f5e74-a005-417b-a6f4-373aa8eb9a90", "name": "Spawner (125)", "location": [3480, 2533, 38], @@ -219,12 +2882,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "Merchant", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Merchant", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "569bab9d-7523-4d66-98db-676d0d82e87d", "name": "Spawner (125)", "location": [3412, 2602, 60], @@ -235,12 +2896,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Actor", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Actor", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ee62671b-f06d-4aa3-b839-067d11051144", "name": "Spawner (125)", "location": [3527, 2516, 45], @@ -251,12 +2910,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "Noble", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Noble", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "270bc1dd-a0e1-4b40-86ff-22b3e81d61c7", "name": "Spawner (125)", "location": [3473, 2493, 72], @@ -267,12 +2924,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "Noble", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Noble", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "89b5e674-6760-48e7-9ad2-e7a2540238f3", "name": "Spawner (125)", "location": [3485, 2493, 52], @@ -283,12 +2938,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "Noble", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Noble", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "13eb1b0e-1058-441f-ac30-640842ba4aa2", "name": "Spawner (125)", "location": [3526, 2519, 25], @@ -299,12 +2952,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "Noble", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Noble", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "cbdb06cc-55f7-4313-920d-4c2552280956", "name": "Spawner (125)", "location": [3470, 2539, 36], @@ -315,12 +2966,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "051c3bf5-86d1-4f51-925d-13ccab2021d4", "name": "Spawner (125)", "location": [3469, 2536, 41], @@ -331,12 +2980,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c10e754e-66c4-432f-98a1-55619df323f4", "name": "Spawner (125)", "location": [3526, 2536, 20], @@ -347,12 +2994,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d67f1961-a220-4ac7-a977-ed9093b91d98", "name": "Spawner (125)", "location": [3471, 2539, 36], @@ -363,12 +3008,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Weaponsmith", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f9642917-84a6-48d3-97ae-f31cdcabb985", "name": "Spawner (125)", "location": [3474, 2539, 31], @@ -379,12 +3022,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Armorer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2bb40f0a-7b32-4553-8891-30384ec56ca1", "name": "Spawner (125)", "location": [3471, 2528, 48], @@ -395,12 +3036,10 @@ "team": 0, "homeRange": 0, "walkingRange": 3, - "entries": [ - { "name": "Scribe", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Scribe", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "edd6b474-4629-4bf8-8d7c-53f560993456", "name": "Spawner (125)", "location": [3461, 2566, 36], @@ -411,12 +3050,10 @@ "team": 0, "homeRange": 0, "walkingRange": 3, - "entries": [ - { "name": "Herbalist", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Herbalist", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "95ed3464-8403-4c33-848f-2d419357f665", "name": "Spawner (125)", "location": [3470, 2519, 48], @@ -427,12 +3064,10 @@ "team": 0, "homeRange": 0, "walkingRange": 3, - "entries": [ - { "name": "Herbalist", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Herbalist", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fa5d25d6-dfd2-4a5a-9025-7991f2c1e000", "name": "Spawner (125)", "location": [3527, 2576, 7], @@ -443,12 +3078,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "11a13313-6107-4c4b-a717-c200576fd994", "name": "Spawner (125)", "location": [3520, 2598, 0], @@ -459,12 +3092,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Fisherman", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "18ac5e1e-c92f-4c96-8d6b-7d41fc01d559", "name": "Spawner (125)", "location": [3484, 2576, 20], @@ -475,12 +3106,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Banker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e423e201-c07c-4c73-bb26-b98e4c5715d6", "name": "Spawner (125)", "location": [3458, 2524, 53], @@ -491,12 +3120,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9add9763-95a0-4a70-876e-fdb16a5e7f25", "name": "Spawner (125)", "location": [3458, 2528, 53], @@ -507,12 +3134,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Tinker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "65655644-247b-45a8-b14c-5b2e3bc14f6b", "name": "Spawner (125)", "location": [3495, 2527, 27], @@ -523,12 +3148,10 @@ "team": 0, "homeRange": 0, "walkingRange": 3, - "entries": [ - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "7023d532-54cc-4dec-b43c-5b3b9722854f", "name": "Spawner (125)", "location": [3450, 2569, 30], @@ -539,12 +3162,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "Thief", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Thief", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fcc1684f-45d4-4e9d-a8ef-f0a82a4a2dbf", "name": "Spawner (125)", "location": [3463, 2576, 35], @@ -555,12 +3176,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Baker", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Baker", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "dcd02218-4bb6-4737-a09d-1c1eb19ed14c", "name": "Spawner (125)", "location": [3461, 2566, 37], @@ -571,12 +3190,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Alchemist", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Alchemist", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a3ee96bb-f740-41d8-ae43-d436a5d903fc", "name": "Spawner (125)", "location": [3445, 2552, 55], @@ -587,12 +3204,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "Waiter", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Waiter", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "8e30685f-b29d-4f6a-8d31-df3910837e8e", "name": "Spawner (125)", "location": [3461, 2566, 35], @@ -603,12 +3218,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "HairStylist", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "HairStylist", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "37164f75-e494-499c-af9c-adcb4b9a3015", "name": "Spawner (125)", "location": [3443, 2552, 35], @@ -619,12 +3232,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "Waiter", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Waiter", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "43989367-6208-484f-ac87-7c0b97942598", "name": "Spawner (125)", "location": [3442, 2567, 53], @@ -635,12 +3246,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "Waiter", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Waiter", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "cd8f7bd5-2623-4712-bec2-b4109aeeb9ff", "name": "Spawner (125)", "location": [3505, 2518, 27], @@ -651,12 +3260,10 @@ "team": 0, "homeRange": 0, "walkingRange": 6, - "entries": [ - { "name": "Waiter", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Waiter", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "32b9ee94-88a7-4591-ad3c-898b09d9f053", "name": "Spawner (125)", "location": [3501, 2517, 27], @@ -667,12 +3274,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c18cb625-4b4e-495c-81f3-181e6d33f446", "name": "Spawner (125)", "location": [3497, 2518, 27], @@ -683,12 +3288,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Cook", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Cook", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5ab3bb89-312e-441d-82d5-eadbd3c33832", "name": "Spawner (125)", "location": [3495, 2532, 27], @@ -699,12 +3302,10 @@ "team": 0, "homeRange": 0, "walkingRange": 3, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Mage", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "72e055e7-2dce-4946-9ff7-76c0a499f2cd", "name": "Spawner (125)", "location": [3495, 2530, 27], @@ -715,12 +3316,10 @@ "team": 0, "homeRange": 0, "walkingRange": 3, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Mage", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e316b602-c0f5-4c25-8a61-a5b5e01443c7", "name": "Spawner (125)", "location": [3446, 2606, 32], @@ -731,12 +3330,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "LeatherWorker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "LeatherWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "da916a95-fb6c-4605-866d-ebf58f55ec49", "name": "Spawner (125)", "location": [3446, 2606, 31], @@ -747,12 +3344,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Furtrader", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "79e04477-5ab1-4276-a4fe-c37385548e20", "name": "Spawner (125)", "location": [3446, 2606, 30], @@ -763,12 +3358,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Tanner", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f2e53c30-0994-4dc1-8e83-afa18e1fa5b3", "name": "Spawner (125)", "location": [3508, 2553, 20], @@ -779,12 +3372,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Jeweler", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "Jeweler", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "506a175a-b815-44fe-9adf-d0091ab5b9c3", "name": "Spawner (125)", "location": [3506, 2746, 0], @@ -795,12 +3386,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Miner", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Miner", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "398d0d16-59a2-4fa9-aaa1-5b86f083440a", "name": "Spawner (125)", "location": [3526, 2536, 21], @@ -811,12 +3400,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "IronWorker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "IronWorker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "fc151c52-294e-421f-a1df-e8d8456dcadf", "name": "Spawner (125)", "location": [3481, 2506, 45], @@ -827,12 +3414,10 @@ "team": 0, "homeRange": 0, "walkingRange": 1, - "entries": [ - { "name": "BaseEscortable", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BaseEscortable", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "94b1a002-db27-4710-b1b8-a959bb6de05e", "name": "Spawner (125)", "location": [3526, 2536, 22], @@ -843,12 +3428,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Miner", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Miner", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "42ac8da7-4ce3-45fd-9cb7-70bbc9309487", "name": "Spawner (125)", "location": [3434, 2568, 53], @@ -859,12 +3442,10 @@ "team": 0, "homeRange": 0, "walkingRange": 1, - "entries": [ - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Barkeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "edf355a6-d296-4a99-b632-cd13dd932b08", "name": "Spawner (125)", "location": [3432, 2554, 44], @@ -875,12 +3456,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Vagabond", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Vagabond", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "30b62d0b-446f-4577-bc8f-1614e1e579c9", "name": "Spawner (125)", "location": [3437, 2566, 53], @@ -891,12 +3470,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TavernKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "044b2029-6882-4e73-90ff-e23baf6f6067", "name": "Spawner (125)", "location": [3555, 2698, 5], @@ -907,12 +3484,10 @@ "team": 0, "homeRange": 0, "walkingRange": 2, - "entries": [ - { "name": "Dryad", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Dryad", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "70c30dc7-1d69-4bac-9767-3e4a869f3f0f", "name": "Spawner (125)", "location": [3545, 2563, 5], @@ -923,12 +3498,10 @@ "team": 0, "homeRange": 0, "walkingRange": 1, - "entries": [ - { "name": "BaseEscortable", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BaseEscortable", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b64ef097-7cb2-49fc-bcc3-8053602b0c5e", "name": "Spawner (125)", "location": [3471, 2589, 10], @@ -939,12 +3512,10 @@ "team": 0, "homeRange": 0, "walkingRange": 1, - "entries": [ - { "name": "BaseEscortable", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BaseEscortable", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6ac60b58-3ca7-48eb-b0ac-7d802fbce90a", "name": "Spawner (125)", "location": [3452, 2586, 30], @@ -955,12 +3526,10 @@ "team": 0, "homeRange": 0, "walkingRange": 4, - "entries": [ - { "name": "Peasant", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Peasant", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "7985d6cc-5f78-4866-8d28-bb7d8c581bbb", "name": "Spawner (125)", "location": [3443, 2535, 36], @@ -971,12 +3540,2312 @@ "team": 0, "homeRange": 0, "walkingRange": 2, + "entries": [{ "name": "SeekerOfAdventure", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a20fa086-8694-465b-a8bc-0988b5017b61", + "name": "Spawner (125)", + "location": [3009, 3455, 15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, "entries": [ - { "name": "SeekerOfAdventure", "maxCount": 1, "probability": 100 } + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 }, + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "92f199ed-e0d1-496f-a1f3-d6aa2a5e3155", + "name": "Spawner (125)", + "location": [2940, 3410, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d9c8783d-5147-4cc5-b2b7-0d0942297fcb", + "name": "Spawner (125)", + "location": [2975, 3353, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49dd6a0d-15dc-4eff-9f1a-9f8af38fe15e", + "name": "Spawner (125)", + "location": [3051, 3367, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "df077d96-f345-4d80-bee5-4ffad8a25c41", + "name": "Spawner (125)", + "location": [2883, 3502, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5df6d5cf-b7d5-4b51-a52e-a0788d7fc21d", + "name": "Spawner (125)", + "location": [3031, 3350, 35], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b907af34-1c6f-4533-92f4-65f9b68f7875", + "name": "Spawner (125)", + "location": [3036, 3464, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b64d4434-2246-4ffa-91a0-600326c097ac", + "name": "Spawner (125)", + "location": [3003, 3355, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1cd64ef2-a605-44ac-bf6a-54033cbd6093", + "name": "Spawner (125)", + "location": [2905, 3517, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "94f8f998-08d7-453a-8dc0-669e4e8ed554", + "name": "Spawner (125)", + "location": [3008, 3389, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a091648-0378-4b58-879b-b07aee75b351", + "name": "Spawner (125)", + "location": [3007, 3408, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c4ea485b-f871-4c9b-a809-33b53ceb78c2", + "name": "Spawner (125)", + "location": [3013, 3353, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "CustomHairstylist", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "01e0ae79-27ae-46b3-b4da-484ddf53f534", + "name": "Spawner (125)", + "location": [2967, 3408, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf770c7b-4a88-4198-b78f-a83eff9d5410", + "name": "Spawner (125)", + "location": [2997, 3428, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c193256a-99a5-44a6-934b-d2996199c5e5", + "name": "Spawner (125)", + "location": [3058, 3400, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5d4c103e-b3cc-44c6-81a5-a0ec9472d14f", + "name": "Spawner (125)", + "location": [2940, 3500, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2377d397-cf7b-41f0-a761-429f83c4fd93", + "name": "Spawner (125)", + "location": [2972, 3425, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "TavernKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e9d01be-2213-4fa8-b1f6-e6e82ed4983c", + "name": "Spawner (125)", + "location": [3013, 3353, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "485462f4-cbbc-4a46-aa31-aa7b42832509", + "name": "Spawner (125)", + "location": [2906, 3485, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "66d3b52d-23e0-4d3f-8c93-17a81be068ef", + "name": "Spawner (125)", + "location": [3018, 3426, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a9406a9-a389-4415-96f8-a8431e90009e", + "name": "Spawner (125)", + "location": [2880, 3472, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f74f17e3-537f-4cf5-9ed5-2677e3bddd84", + "name": "Spawner (125)", + "location": [2972, 3425, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d4579b8-5d76-4c53-8bda-3d2c4803f2e9", + "name": "Spawner (125)", + "location": [2992, 3451, 16], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6ce9596e-45b3-4eaa-be62-885d94134085", + "name": "Spawner (125)", + "location": [3007, 3408, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50090b05-cdf5-4e5f-994a-7f4d6b7eb59e", + "name": "Spawner (125)", + "location": [620, 2218, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b61ff7bc-b168-475d-9b08-b36ac604db8e", + "name": "Spawner (125)", + "location": [602, 2180, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "58876bbe-786f-496c-a9ad-de7150daedbe", + "name": "Spawner (125)", + "location": [650, 2161, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 }, + { "name": "Armorer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "57ed6a02-61ea-4190-bdb9-ea72f43675a6", + "name": "Spawner (125)", + "location": [659, 2141, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1fc9ddd3-9975-4095-8daf-8d86e1103009", + "name": "Spawner (125)", + "location": [650, 2178, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1d8091b-483b-4892-a4b6-e9b5f8ad167c", + "name": "Spawner (125)", + "location": [656, 2235, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5ce35259-7589-479c-8194-8ef26ee0bc9a", + "name": "Spawner (125)", + "location": [620, 2218, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Healer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ec85a134-cb7e-4b7b-bd1b-0023b55d9869", + "name": "Spawner (125)", + "location": [562, 2148, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "RangerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a620a8cf-1b8e-4f4b-8485-1a70f6160ed8", + "name": "Spawner (125)", + "location": [554, 2178, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "35d52dec-9192-40c7-b65d-bd9c41e87cdb", + "name": "Spawner (125)", + "location": [578, 2227, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6709b04a-f462-468e-b5f5-c8cb8088f0bf", + "name": "Spawner (125)", + "location": [592, 2277, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d156639-7f49-432e-a874-c01be5b54a7f", + "name": "Spawner (125)", + "location": [590, 2205, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12775f86-e464-4ae0-bd2d-e986f9a6452b", + "name": "Spawner (125)", + "location": [586, 2170, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4ffe4319-70b0-48c5-bb98-c3bf957662f0", + "name": "Spawner (125)", + "location": [630, 2194, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "de7a0b5f-a21f-4e3a-871c-648aa7100c20", + "name": "Spawner (125)", + "location": [587, 2146, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd5f20aa-6865-4e9c-91dc-1ab598efedc4", + "name": "Spawner (125)", + "location": [627, 2163, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d63ea5ae-32d5-48c0-a9b8-4706a9cd25a7", + "name": "Spawner (125)", + "location": [611, 2157, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d9d17a8-7908-4052-b17c-69e9417788cd", + "name": "Spawner (125)", + "location": [570, 2121, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "978c26c6-dbea-40db-8422-e0342c0738bf", + "name": "Spawner (125)", + "location": [582, 2186, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "843d989e-d401-464a-90b0-16b3469cb226", + "name": "Spawner (125)", + "location": [601, 2235, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0ba9f3dc-a0df-44d2-bebd-bd26fdd99730", + "name": "Spawner (125)", + "location": [2011, 2804, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "523e5f93-afd6-466e-bb27-db94c6bf7e0a", + "name": "Spawner (125)", + "location": [1935, 2766, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e0e6946-627a-4f89-b9d4-795be9b51694", + "name": "Spawner (125)", + "location": [1935, 2796, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b43faac-d666-46c1-ba1c-de0ff4a92dad", + "name": "Spawner (125)", + "location": [2002, 2721, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9c0ac700-6c3d-4025-93cf-6b67e28b80ae", + "name": "Spawner (125)", + "location": [1897, 2805, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6bb19a0b-8193-4146-bada-96ef1f69c2c9", + "name": "Spawner (125)", + "location": [1852, 2831, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a604ee94-149c-45ed-8f2e-b95904ea6550", + "name": "Spawner (125)", + "location": [2033, 2801, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bc9dea07-ca90-459f-bec2-b2efa4ddf507", + "name": "Spawner (125)", + "location": [1895, 2653, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bfd3ed22-e5b3-4203-8fb2-db2587c1f196", + "name": "Spawner (125)", + "location": [2026, 2845, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f722fc04-87d9-4293-a80b-77140c00250a", + "name": "Spawner (125)", + "location": [1848, 2680, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "360da1b6-8e1d-4afa-aa0a-b58ae555157f", + "name": "Spawner (125)", + "location": [1844, 2735, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d3b2922a-82a9-4c3f-ae87-d026e4865db8", + "name": "Spawner (125)", + "location": [1990, 2889, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0d673486-6203-4256-9264-e267a7f1bd71", + "name": "Spawner (125)", + "location": [2072, 2856, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "890e5e81-c5da-43c4-a7f1-deb55e5e8b9b", + "name": "Spawner (125)", + "location": [1991, 2867, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9faa97bf-28e2-4e7a-82a4-0e75f227759b", + "name": "Spawner (125)", + "location": [1911, 2805, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95eaf1c9-9b1e-4f08-845d-cf1fba01401e", + "name": "Spawner (125)", + "location": [1897, 2684, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9cd4bc8c-4f9a-4a91-9cee-70101c3804d8", + "name": "Spawner (125)", + "location": [1822, 2739, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0f546912-c197-4d33-a1e0-1f4efddb9311", + "name": "Spawner (125)", + "location": [2019, 2748, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f0019dbf-e6d5-44a4-8da4-16dbc44da350", + "name": "Spawner (125)", + "location": [1981, 2838, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfed240a-6345-42ad-a99b-8fa6b48d519c", + "name": "Spawner (125)", + "location": [1880, 2802, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dc73fe29-ef87-477e-9390-666aac11a2fa", + "name": "Spawner (125)", + "location": [1813, 2825, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9bdbb8fe-1b34-49c4-9cc2-6e69ad698018", + "name": "Spawner (125)", + "location": [1850, 2796, -8], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1373dadc-9178-4022-941f-b204570faf59", + "name": "Spawner (125)", + "location": [1939, 2739, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0746c0d5-0f6e-4d52-951f-f0e3b39bd19e", + "name": "Spawner (125)", + "location": [1846, 2715, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca419471-6da0-47e1-88f0-dcbffd67e416", + "name": "Spawner (125)", + "location": [2963, 813, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d40aef76-3174-45a1-bf02-b7a5ae236688", + "name": "Spawner (125)", + "location": [3012, 780, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41cdd533-ce2e-4c8b-9f9b-7467f6d27778", + "name": "Spawner (125)", + "location": [2899, 790, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33818055-2577-4e1c-ad37-a47f989f925f", + "name": "Spawner (125)", + "location": [2917, 798, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22f848cc-0054-4f92-8f4b-d55e59f8c0c9", + "name": "Spawner (125)", + "location": [2920, 856, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "90e83279-5d20-4b5e-9352-e019cd461edd", + "name": "Spawner (125)", + "location": [2782, 969, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "48829893-5b5f-4822-a43c-a2a05549f7ca", + "name": "Spawner (125)", + "location": [2865, 852, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "741b669a-ffda-4fcf-8617-207ce5be1530", + "name": "Spawner (125)", + "location": [2918, 672, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6232f09-ad45-49a6-ba0b-b4244c844d3b", + "name": "Spawner (125)", + "location": [2992, 844, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb4405b2-a53a-4e60-a659-98ce20fc2e6a", + "name": "Spawner (125)", + "location": [2995, 815, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "82e5da86-576b-4065-9dc1-b79b154de3fb", + "name": "Spawner (125)", + "location": [2901, 908, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "70a16a36-196a-48ef-b3ea-1e88ff8a7080", + "name": "Spawner (125)", + "location": [2881, 684, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbd3af4a-dd6a-4ea9-b79b-ea071b77c495", + "name": "Spawner (125)", + "location": [2855, 734, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "860c7844-79ed-4155-aa2d-1b40e99ffafb", + "name": "Spawner (125)", + "location": [2861, 812, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fda0fb25-6ca0-422e-836f-81bd46da8a61", + "name": "Spawner (125)", + "location": [2882, 723, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "650d8ee5-71d1-415f-af77-e051d754adf5", + "name": "Spawner (125)", + "location": [2961, 621, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1bc12e56-5192-41a7-9476-6d628159fcf5", + "name": "Spawner (125)", + "location": [2840, 885, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "597858d7-3494-465b-8ee1-6bd72419b247", + "name": "Spawner (125)", + "location": [2860, 999, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f23df328-1474-4abf-855f-08a262da0c92", + "name": "Spawner (125)", + "location": [2998, 760, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e3eb97de-3c51-4ac5-9e7d-e34818423213", + "name": "Spawner (125)", + "location": [3034, 827, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ef48e85-8383-43c1-806e-66855121aac7", + "name": "Spawner (125)", + "location": [2962, 885, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "68888d32-23aa-4e23-9295-3eaf1e026e16", + "name": "Spawner (125)", + "location": [2835, 805, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7829831c-f4e0-44a1-a2a9-e04196a72573", + "name": "Spawner (125)", + "location": [2986, 637, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eff84dda-bd01-479c-a669-ca56d9fffe3d", + "name": "Spawner (125)", + "location": [2890, 651, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "00a20674-f2f7-4d32-8f99-17333e610528", + "name": "Spawner (125)", + "location": [2954, 705, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Beekeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "677fe1ff-5c29-4d9b-86f2-01f8f6676500", + "name": "Spawner (125)", + "location": [2987, 777, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf49484f-4281-4b52-bbd7-786dbcecdf9f", + "name": "Spawner (125)", + "location": [2841, 907, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e88a973-4c4b-46fc-8012-0e5484272dc8", + "name": "Spawner (125)", + "location": [3017, 761, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7753854c-8437-457c-970f-5489505ba4c6", + "name": "Spawner (125)", + "location": [5236, 142, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "450a0f16-a428-4b13-9bea-7c1f0398f4dd", + "name": "Spawner (125)", + "location": [5351, 56, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e00042e4-6be0-4f04-8fc8-56c739a585d6", + "name": "Spawner (125)", + "location": [5154, 97, 5], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b65546f-275c-45fd-9d0f-46e6639237bc", + "name": "Spawner (125)", + "location": [5215, 117, 1], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73b991d6-dce0-42b7-90dd-dd8cc7671bed", + "name": "Spawner (125)", + "location": [5242, 180, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81515819-c2a9-4db8-b752-9f06685593aa", + "name": "Spawner (125)", + "location": [5161, 32, 17], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5d5d05fc-b9de-40cc-b2d8-015e97a4904a", + "name": "Spawner (125)", + "location": [5148, 60, 25], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e70df5a8-1ca9-41a6-beb7-0c13ab7f2884", + "name": "Spawner (125)", + "location": [5347, 76, 25], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98826ae5-c33c-45be-a99a-4463a5c10916", + "name": "Spawner (125)", + "location": [5263, 129, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ee02077-d7f2-439e-95b9-f9e82932f319", + "name": "Spawner (125)", + "location": [5203, 86, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36a0853e-86e1-4024-9ef6-efa398ae07c3", + "name": "Spawner (125)", + "location": [5218, 175, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a4040d64-c962-4fac-b73f-10a0c04cb4b8", + "name": "Spawner (125)", + "location": [5300, 90, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea124c6c-817e-49cd-9d27-567a818cd051", + "name": "Spawner (125)", + "location": [5306, 37, 40], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49c6d37a-7560-4977-bcbe-045801c60af8", + "name": "Spawner (125)", + "location": [624, 1145, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5c458af7-1675-41b8-b95d-33a9a747b0a4", + "name": "Spawner (125)", + "location": [631, 1034, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd67f242-2044-4c4f-b937-9fcad53ea655", + "name": "Spawner (125)", + "location": [540, 966, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5cecf5b2-1161-47d8-9098-c4158222a0bb", + "name": "Spawner (125)", + "location": [553, 985, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9bded978-79fc-4f1e-9360-de487896a391", + "name": "Spawner (125)", + "location": [570, 969, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ecd340e1-2000-4c61-8042-64e2a3a7734f", + "name": "Spawner (125)", + "location": [517, 986, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "813713e2-fce3-4dd5-b514-b0bfd0ff76a9", + "name": "Spawner (125)", + "location": [529, 1008, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4b5a2097-735a-4740-8b6a-87f079705d42", + "name": "Spawner (125)", + "location": [652, 820, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0468f726-d595-41a6-94a2-e073f3b30caf", + "name": "Spawner (125)", + "location": [610, 810, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "875e998f-beb1-40f4-aae2-ee36279337b5", + "name": "Spawner (125)", + "location": [535, 867, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78237184-1c64-4e47-962b-f8ec0ee4a823", + "name": "Spawner (125)", + "location": [564, 1011, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abda8289-add2-4cd0-97f9-3b5c2f54721e", + "name": "Spawner (125)", + "location": [643, 1083, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40c31da4-105d-4393-81d1-37b1e0e3e540", + "name": "Spawner (125)", + "location": [5191, 3989, 37], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39f529fe-2424-4e3f-9dc3-13a0e1f979f1", + "name": "Spawner (125)", + "location": [5296, 3978, 37], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ede9690-36bd-4b72-a43b-fccbdcc8f56c", + "name": "Spawner (125)", + "location": [5219, 4012, 37], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b370875b-ec9e-4655-9b0d-a2d8d8055302", + "name": "Spawner (125)", + "location": [5275, 3977, 37], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb0032c9-6652-41c4-b15b-8a3f64aa3968", + "name": "Spawner (125)", + "location": [5225, 4000, 38], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Blacksmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e32d662c-a5e3-4549-b2ff-4ec7a76d6961", + "name": "Spawner (125)", + "location": [5197, 4060, 37], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "01d45bd9-1b11-4740-a5fb-d03ab174b5ab", + "name": "Spawner (125)", + "location": [5297, 4007, 40], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "79424b72-3212-491e-afc1-ad1df19708d7", + "name": "Spawner (125)", + "location": [5234, 4025, 37], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "308f6ffb-7fb6-4f92-85be-56d1756e5823", + "name": "Spawner (125)", + "location": [5669, 3131, 14], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Minter", "maxCount": 1, "probability": 100 }, + { "name": "Banker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "463cd51f-8053-4bdb-9b1f-1ec4d7fb1813", + "name": "Spawner (125)", + "location": [5731, 3192, 9], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "Mage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bff8e072-e174-4aa7-93a8-e5e61b4d957f", + "name": "Spawner (125)", + "location": [5726, 3243, 16], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Tinker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94a33c3d-2fd0-4c9a-8424-b0315ddb023c", + "name": "Spawner (125)", + "location": [5662, 3150, 13], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "904e35eb-9ad9-433b-8717-cc1984111fb8", + "name": "Spawner (125)", + "location": [5671, 3287, 11], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4d000fba-9496-45e5-9ba0-93e2ad003206", + "name": "Spawner (125)", + "location": [5698, 3282, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e109199-da2b-4155-9890-9a0424be41cf", + "name": "Spawner (125)", + "location": [5805, 3300, 11], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 }, + { "name": "Armorer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad2a2a35-d46f-4b26-b787-236d6e956cd5", + "name": "Spawner (125)", + "location": [5714, 3202, 12], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "Herbalist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd4f9c92-cb01-4d9b-bcfe-2b4e397c3c10", + "name": "Spawner (125)", + "location": [5805, 3270, 11], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mapmaker", "maxCount": 1, "probability": 100 }, + { "name": "Shipwright", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d331fdc-c640-4e30-9d51-d8b28977ac69", + "name": "Spawner (125)", + "location": [5752, 3272, 16], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "Tailor", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a0464876-3bdb-466d-a5a8-40f63301fafd", + "name": "Spawner (125)", + "location": [5839, 3256, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "476e8e87-d2f3-4964-9bdd-3e11498cd362", + "name": "Spawner (125)", + "location": [5746, 3200, 16], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93eaa98d-0b9f-42b3-91e8-7c04cb15f971", + "name": "Spawner (125)", + "location": [5775, 3161, 14], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44a0c6b8-5b7d-486e-b373-affb981dcdd5", + "name": "Spawner (125)", + "location": [5737, 3222, 11], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Healer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b65a0806-7fee-4836-9e8e-e56e2d9d5f09", + "name": "Spawner (125)", + "location": [5691, 3209, 11], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "Carpenter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4cdd8db5-8499-4956-b094-1e77596039fe", + "name": "Spawner (125)", + "location": [5736, 3263, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Cobbler", "maxCount": 1, "probability": 100 }, + { "name": "Provisioner", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "6b09d5ff-9d1f-4f66-8e7b-679a90cf9c45", "name": "Spawner (125)", "location": [4571, 2303, -2], @@ -987,12 +5856,10 @@ "team": 0, "homeRange": 0, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "7f8a090b-7884-48d5-8d54-7449899cf78a", "name": "Spawner (125)", "location": [4553, 2318, -2], @@ -1003,12 +5870,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Tanner", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6d21124c-44cb-44f5-aa0b-d17142e60d1a", "name": "Spawner (125)", "location": [4550, 2307, -2], @@ -1025,7 +5890,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1e04c039-89e9-4788-bd06-789dbbb50b1d", "name": "Spawner (125)", "location": [4557, 2307, -2], @@ -1036,12 +5901,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e79907c6-57d3-4ed4-963d-bf9fc8574b09", "name": "Spawner (125)", "location": [4540, 2301, -1], @@ -1058,7 +5921,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0675ef64-df55-4c32-848a-bf30be59538f", "name": "Spawner (125)", "location": [4547, 2321, -2], @@ -1069,12 +5932,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "bodysculptor", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "bodysculptor", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e683aa9f-8b9b-46ff-9fe1-5fb145b44bd3", "name": "Spawner (125)", "location": [4551, 2334, -2], @@ -1085,12 +5946,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Shipwright", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "22fe0b5c-8a8b-48ad-930e-ad36d4487727", "name": "Spawner (125)", "location": [4556, 2330, -2], @@ -1101,12 +5960,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "753fc723-5058-4e85-b358-258d02f585b5", "name": "Spawner (125)", "location": [4533, 2333, -2], @@ -1124,7 +5981,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "48870208-fd28-4b37-b5bc-120a08bf0f7f", "name": "Spawner (125)", "location": [4569, 2304, -2], @@ -1135,12 +5992,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "dockmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "dockmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "98cfdac2-0e80-4e90-8670-899c3a3c9fc1", "name": "Spawner (125)", "location": [4549, 2346, -2], @@ -1151,12 +6006,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "995cf0a6-8a05-4bcd-8020-abc07be7e4ea", "name": "Spawner (125)", "location": [4562, 2349, -2], @@ -1167,12 +6020,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "hiresailor", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "hiresailor", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9139511d-30f8-43b2-897b-fa540e0c1c2f", "name": "Spawner (125)", "location": [4570, 2400, -2], @@ -1189,7 +6040,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "37402f51-4aeb-4a60-9992-3a80ef185ce3", "name": "Spawner (125)", "location": [4551, 2327, -2], @@ -1200,12 +6051,10 @@ "team": 0, "homeRange": 7, "walkingRange": -1, - "entries": [ - { "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0e6cf397-8099-46d5-ab34-5aee1bcf6c81", "name": "Spawner (125)", "location": [4530, 2382, -2], @@ -1216,12 +6065,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f760f138-6bb1-499f-adc2-65126b63c058", "name": "Spawner (125)", "location": [4551, 2320, -2], @@ -1232,12 +6079,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "boatpainter", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "boatpainter", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "04680f57-809d-4c04-8672-16f9e9011dc2", "name": "Spawner (125)", "location": [4573, 2364, -2], @@ -1248,12 +6093,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bd9a3f5c-4531-4083-8bd0-7c922b2a446f", "name": "Spawner (125)", "location": [4553, 2351, -2], @@ -1264,12 +6107,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b48da21e-223d-4e93-8062-6ce382e0ad9a", "name": "Spawner (125)", "location": [4537, 2348, -2], @@ -1280,12 +6121,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "846018e2-4ec0-4603-8b78-1804b234eebb", "name": "Spawner (125)", "location": [4549, 2301, -2], @@ -1296,12 +6135,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ab2385eb-4408-432c-99c2-a900f56e3a91", "name": "Spawner (125)", "location": [4553, 2343, -2], @@ -1312,12 +6149,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "612d1488-2e59-4ac4-9ad6-908c8110f2c5", "name": "Spawner (125)", "location": [4544, 2299, -1], @@ -1334,7 +6169,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b783dc23-05a2-44a8-bc47-c414ad20c677", "name": "Spawner (125)", "location": [4561, 2328, -2], @@ -1345,12 +6180,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Tinker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "88cc6da0-0d9c-4959-8b66-e7ad4249e7dd", "name": "Spawner (125)", "location": [4551, 2381, -2], @@ -1361,12 +6194,10 @@ "team": 0, "homeRange": 15, "walkingRange": -1, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "cb6d2672-e22b-400f-b6f6-6bb53a395307", "name": "Spawner (125)", "location": [4552, 2349, -2], @@ -1377,12 +6208,10 @@ "team": 0, "homeRange": 5, "walkingRange": -1, - "entries": [ - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "80396670-c909-44f6-bee2-197dd3664069", "name": "Spawner (125)", "location": [4552, 2390, -2], @@ -1393,12 +6222,10 @@ "team": 0, "homeRange": 7, "walkingRange": -1, - "entries": [ - { "name": "hiresailor", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "hiresailor", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b879011d-83c8-42d2-a425-fe79435eb34b", "name": "Spawner (125)", "location": [4542, 2329, -2], @@ -1409,12 +6236,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Alchemist", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Alchemist", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a93d6a97-80a5-4d4b-928e-4fe9036267eb", "name": "Spawner (125)", "location": [4551, 2323, -2], @@ -1425,12 +6250,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Banker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0d82601b-0709-48ab-a160-74b490782cd5", "name": "Spawner (125)", "location": [4564, 2298, -2], @@ -1441,12 +6264,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Shipwright", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0a388236-c79c-4c56-8f47-de4d983680e8", "name": "Spawner (125)", "location": [4547, 2376, -2], @@ -1457,12 +6278,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ab1a8ed2-6b9a-4e3e-8d78-2facd28d9bc1", "name": "Spawner (125)", "location": [4540, 2306, -1], @@ -1473,12 +6292,10 @@ "team": 0, "homeRange": 4, "walkingRange": -1, - "entries": [ - { "name": "Waiter", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Waiter", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "dea5dc25-4b58-4f7f-a78b-f960a011a116", "name": "Spawner (125)", "location": [4554, 2389, -2], @@ -1489,12 +6306,10 @@ "team": 0, "homeRange": 1, "walkingRange": -1, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c48ad7f6-9713-4ad9-bc2b-9929a1c7b491", "name": "Spawner (125)", "location": [4534, 2349, -2], @@ -1505,12 +6320,10 @@ "team": 0, "homeRange": 0, "walkingRange": -1, - "entries": [ - { "name": "treasurelevel1h", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "treasurelevel1h", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "9a513bd0-41d7-4021-b7b7-54db4b4d5983", "name": "Spawner (125)", "location": [4535, 2349, -2], @@ -1521,8 +6334,6 @@ "team": 0, "homeRange": 0, "walkingRange": -1, - "entries": [ - { "name": "treasurelevel1h", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "treasurelevel1h", "maxCount": 1, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/post-uoml/trammel/WildLife.json b/Distribution/Data/Spawns/post-uoml/trammel/WildLife.json index 129508795..40d23c927 100644 --- a/Distribution/Data/Spawns/post-uoml/trammel/WildLife.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/WildLife.json @@ -1,6 +1,5684 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "45803a8e-701d-4b75-9ef0-b6705a0499c6", + "name": "Spawner (126)", + "location": [4612, 3424, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f309b0c8-cbe1-4a42-a2a6-0b5bbadcdf70", + "name": "Spawner (126)", + "location": [4682, 3662, 77], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "376b8121-d1b0-4461-83c7-37f1920fa4a7", + "name": "Spawner (126)", + "location": [4446, 3706, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "036d98ab-980c-4af8-b924-dd900a6f438c", + "name": "Spawner (126)", + "location": [3232, 510, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fc540fa1-f1f8-45b4-8d06-f1d91b4575b0", + "name": "Spawner (126)", + "location": [4560, 3504, 2], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95395203-fa7a-46ab-9c61-360659b66537", + "name": "Spawner (126)", + "location": [4692, 3494, 1], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32c27855-0fa7-42cc-b9bb-749f3c6c737c", + "name": "Spawner (126)", + "location": [4478, 3218, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36d0217e-7fa6-4abb-a59c-572441f8e069", + "name": "Spawner (126)", + "location": [3394, 628, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b1ed0cd3-8f33-44ee-b48a-ccf8bfccbc62", + "name": "Spawner (126)", + "location": [3650, 2080, 21], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0e440be-1903-4921-8001-c203b99824d0", + "name": "Spawner (126)", + "location": [3608, 2140, 79], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7efa7e94-9493-4113-9804-2997584755fd", + "name": "Spawner (126)", + "location": [4384, 1218, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a339b0e-c70e-4798-8210-3f3e2de53574", + "name": "Spawner (126)", + "location": [4468, 1006, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "029a425c-6944-4f56-b9de-a3a007961469", + "name": "Spawner (126)", + "location": [4326, 1046, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f2ed951-4709-4c94-92ce-0fb0d21bd5f2", + "name": "Spawner (126)", + "location": [4544, 1128, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "259f44e8-a7d3-41cd-b3a8-d26f3458876d", + "name": "Spawner (126)", + "location": [4510, 1262, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f56a1bd-2813-46c3-a48e-df1bddf67a93", + "name": "Spawner (126)", + "location": [4590, 1326, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a3a4287c-82dd-4d54-ae9a-fc0de236aaee", + "name": "Spawner (126)", + "location": [4526, 986, 10], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1252b4d8-f8ff-41e4-be2b-45c0ed2c62e5", + "name": "Spawner (126)", + "location": [4336, 1130, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "413394e4-0a86-4b97-ad09-b787f7f95b13", + "name": "Spawner (126)", + "location": [4448, 1334, 3], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b65d99d3-a2c3-404c-9db5-e261da0b012d", + "name": "Spawner (126)", + "location": [4540, 1454, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72fa3aa7-e804-424f-b789-4f0ab28575a6", + "name": "Spawner (126)", + "location": [4632, 1164, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d47483e-14b9-4949-8e00-76c5431539d7", + "name": "Spawner (126)", + "location": [4470, 1430, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b04c36d0-8c69-46f0-a48c-2b947622bb3f", + "name": "Spawner (126)", + "location": [4592, 1424, 3], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8742ec13-802c-4bdc-80bb-37c4d6031baf", + "name": "Spawner (126)", + "location": [2814, 3500, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f143629b-57cc-4c98-a2bd-06c7a3f00e70", + "name": "Spawner (126)", + "location": [2998, 3590, 15], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7eb4c9b-fb14-4e00-9a15-e51095c2565b", + "name": "Spawner (126)", + "location": [2864, 150, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af606966-fb70-4d7f-8ad6-fcf293fbf012", + "name": "Spawner (126)", + "location": [3292, 570, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cdaade9c-e8af-48ed-8fd1-7326a2ddba80", + "name": "Spawner (126)", + "location": [3206, 366, 9], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72348476-489b-41c1-b61e-6900a22a61b5", + "name": "Spawner (126)", + "location": [3172, 664, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b7d99ad-681f-4b64-99c3-cda74412043b", + "name": "Spawner (126)", + "location": [3392, 424, 9], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d5a4c442-7233-4c40-950f-51aea9e80674", + "name": "Spawner (126)", + "location": [3076, 150, 1], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2af04186-1f66-4c97-8893-21966a8d38ff", + "name": "Spawner (126)", + "location": [3092, 542, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c6377b5f-e04c-457e-9ec5-3e45c8302d93", + "name": "Spawner (126)", + "location": [2512, 1066, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0fe92a55-c40e-49a3-9658-ebb41b284a60", + "name": "Spawner (126)", + "location": [2554, 1142, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f8ebac9-7bfd-4cb5-9c30-a69baa46be7b", + "name": "Spawner (126)", + "location": [2416, 1172, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8231afb-b1d4-48ae-bb33-0b6da6d81411", + "name": "Spawner (126)", + "location": [2188, 1986, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ee0e83f-fd06-4c5d-91d2-74e5b3750f53", + "name": "Spawner (126)", + "location": [2654, 2286, 9], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f0da5cc6-e368-44e9-a745-e6744fd2edf7", + "name": "Spawner (126)", + "location": [2946, 2168, 44], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0022416f-f5eb-483c-9ad4-eac25ac3af57", + "name": "Spawner (126)", + "location": [2744, 2014, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21f01798-bc50-4ce5-95d9-16c900a28689", + "name": "Spawner (126)", + "location": [2786, 2282, 13], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "99f98b9c-1885-479d-882c-f8251711f59d", + "name": "Spawner (126)", + "location": [2074, 1972, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f7a461d8-c310-48db-8dea-fb3a61fe9504", + "name": "Spawner (126)", + "location": [2122, 2108, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c28cde3-8cbf-4b0f-9a22-c4da0f91a9ef", + "name": "Spawner (126)", + "location": [1932, 3125, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ceff4e8-8c0f-4974-80dc-09aa04d29799", + "name": "Spawner (126)", + "location": [1824, 3027, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd5716e7-8f5b-4d35-9b8e-f2f3af6829de", + "name": "Spawner (126)", + "location": [1894, 2942, 19], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5001467d-867b-4f25-a046-9b42cfce15af", + "name": "Spawner (126)", + "location": [1222, 2499, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba1ea3b8-e6b1-4231-b2be-961396c2bfab", + "name": "Spawner (126)", + "location": [1005, 2647, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d61f40c0-87d2-4fc2-8282-5a6eedadd553", + "name": "Spawner (126)", + "location": [464, 2059, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7501f45b-0531-492c-91ea-de33a4a305dd", + "name": "Spawner (126)", + "location": [559, 2081, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc12e17d-1f65-4e97-a222-9fc7bf0f4444", + "name": "Spawner (126)", + "location": [883, 2129, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9457f8a-483a-4f47-bfa9-7a301f3e22d4", + "name": "Spawner (126)", + "location": [968, 2476, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b2f68c1-edb6-401d-a4e4-e2c5e25d3560", + "name": "Spawner (126)", + "location": [994, 2361, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfc04314-5772-4b9c-a50a-3652e7be42ea", + "name": "Spawner (126)", + "location": [964, 2214, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e3d2fd4-d529-45bd-a135-56ca809d2e35", + "name": "Spawner (126)", + "location": [1087, 2401, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3c776b4-3387-47ec-b8b6-6c6320e446f2", + "name": "Spawner (126)", + "location": [1222, 2378, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b520b06-67a4-4d03-9ac8-1b6b89918338", + "name": "Spawner (126)", + "location": [1334, 2311, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae37664f-7df6-4a33-853e-b577723971be", + "name": "Spawner (126)", + "location": [1315, 2192, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72d26907-465d-42cf-b64a-7b70c3f21b5e", + "name": "Spawner (126)", + "location": [1426, 2615, 10], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c11ab8d-0f10-4d9d-9b58-4d152567074f", + "name": "Spawner (126)", + "location": [1527, 2609, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cf3b84c2-c2b7-4692-ac18-e5c9df1e3a94", + "name": "Spawner (126)", + "location": [1582, 2722, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2f41e23-c367-4172-8dec-c42709950de9", + "name": "Spawner (126)", + "location": [1650, 2796, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c157367d-9508-4b38-9472-f87683a4756d", + "name": "Spawner (126)", + "location": [1724, 2781, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b25b4e76-d62b-4f92-89d8-6d72908611ec", + "name": "Spawner (126)", + "location": [1727, 2702, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e8d9152c-1bce-4b09-b08a-f43be0cd650e", + "name": "Spawner (126)", + "location": [1634, 2645, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0950a31a-0b42-481f-8658-46d97e5047f5", + "name": "Spawner (126)", + "location": [1505, 2445, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "832fae60-bc7c-4675-8bf1-747404eb52c2", + "name": "Spawner (126)", + "location": [1699, 2560, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4aecd554-03ce-4054-aa4b-e549bcda9099", + "name": "Spawner (126)", + "location": [1612, 2526, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eaa6d886-bacf-4ada-8dd9-b4059ed58700", + "name": "Spawner (126)", + "location": [1722, 2435, 10], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "edf184b7-267e-475a-9e9d-6a4da4720a0b", + "name": "Spawner (126)", + "location": [1715, 2307, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03f454af-c939-406a-b7df-3b11264c1e56", + "name": "Spawner (126)", + "location": [1531, 2351, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8e639d7-7836-4fcc-9420-e40bee6383de", + "name": "Spawner (126)", + "location": [1602, 2426, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ac54db3-d755-475f-84df-bec218f106b7", + "name": "Spawner (126)", + "location": [1628, 2334, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37ca5711-d9e9-4754-ab12-d72282bd3775", + "name": "Spawner (126)", + "location": [1563, 2277, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96c8d9da-bf02-4fd8-8578-1b2ed732d687", + "name": "Spawner (126)", + "location": [1503, 2207, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "06dc791a-faa3-4301-b930-038655903d16", + "name": "Spawner (126)", + "location": [1709, 1992, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75f30f5a-a828-474c-85a0-4f58fad8bbf0", + "name": "Spawner (126)", + "location": [1498, 2040, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "faedd551-9d07-4233-b959-540762d502b7", + "name": "Spawner (126)", + "location": [1439, 2104, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7da8b2c6-8444-46a1-8dc8-78ff324fb2cf", + "name": "Spawner (126)", + "location": [1387, 2023, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6c38b89-808a-44b4-a5c7-aac41fab07d6", + "name": "Spawner (126)", + "location": [1377, 1911, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3e5a953-2208-455f-9bd5-e160f387afbc", + "name": "Spawner (126)", + "location": [1459, 1963, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb9ece00-1e1a-4a80-8c19-b85b54ba4be6", + "name": "Spawner (126)", + "location": [1268, 2018, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e97f47f-63ea-4b3c-97aa-252f922c1fc3", + "name": "Spawner (126)", + "location": [1173, 2103, 2], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e68d9d2b-ce90-42ab-8a2d-87437babe0a8", + "name": "Spawner (126)", + "location": [1070, 2069, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5b3fb52-ce57-429c-a393-883235b01489", + "name": "Spawner (126)", + "location": [958, 2074, 1], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dccc7e1b-7ef9-44f3-9c3f-c2395b4c739a", + "name": "Spawner (126)", + "location": [999, 1839, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a5e2ae8-f5bb-49a4-a405-c392bd88e885", + "name": "Spawner (126)", + "location": [1011, 1945, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "caa501da-6efa-4136-8573-1b996c048e85", + "name": "Spawner (126)", + "location": [1229, 1655, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41d87f21-c25b-40bb-8a40-79cd984daac9", + "name": "Spawner (126)", + "location": [1725, 1447, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "388f1cb5-ac9c-4127-ad37-0fd812bfb769", + "name": "Spawner (126)", + "location": [1798, 1471, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0228c6c2-bc57-4dad-864a-f33844129d0a", + "name": "Spawner (126)", + "location": [1958, 1305, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0779e1d7-6a80-4907-83c9-a7b8a1cbd8bb", + "name": "Spawner (126)", + "location": [1867, 1244, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2687613-183f-4bd0-9103-d7bd70639793", + "name": "Spawner (126)", + "location": [1865, 1379, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e32abac-5a5c-424e-b50f-3e539dbb1556", + "name": "Spawner (126)", + "location": [1769, 1330, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "837adc9c-cd37-4302-9bf3-cf063de88772", + "name": "Spawner (126)", + "location": [1666, 1290, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3ef6251-4ee9-4e19-aa70-db8ea6f92bed", + "name": "Spawner (126)", + "location": [1486, 1301, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9aa8d68c-2713-4500-aedf-95471c1b15e9", + "name": "Spawner (126)", + "location": [1351, 1359, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c96e7fd6-bd4a-407b-8fe8-5116adb5c0e9", + "name": "Spawner (126)", + "location": [1231, 1462, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98b988f2-a420-45e7-8ba5-2bda0ae81f01", + "name": "Spawner (126)", + "location": [1017, 1726, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02d75559-5e11-47dd-9520-76f8d798f286", + "name": "Spawner (126)", + "location": [398, 1305, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebec0b27-0c1c-403f-abdc-9b7866570a8a", + "name": "Spawner (126)", + "location": [485, 1398, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "805e07bf-4287-4e09-8e30-437284cc96ec", + "name": "Spawner (126)", + "location": [210, 1447, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf6dee8d-749a-4894-a4f7-6b0c179aa2d4", + "name": "Spawner (126)", + "location": [851, 1770, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8fe92be0-9b3d-4ef0-850b-0e64e571d47c", + "name": "Spawner (126)", + "location": [977, 1638, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6903cd58-d9a3-4d0c-ab3b-41e09513bddb", + "name": "Spawner (126)", + "location": [919, 1718, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "333e31a1-765c-4086-bc1f-5e9f585929aa", + "name": "Spawner (126)", + "location": [876, 1639, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d62d25b-d1a9-4b3b-ba9d-273f3365465e", + "name": "Spawner (126)", + "location": [805, 1570, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a107d63c-c00c-4dff-9196-726441942476", + "name": "Spawner (126)", + "location": [725, 1483, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8b734bb-93cf-40c5-9178-20bc299963b3", + "name": "Spawner (126)", + "location": [623, 1328, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "25b8399a-d467-41ec-806f-e26ac6b3761a", + "name": "Spawner (126)", + "location": [727, 1394, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df245d50-9023-49ba-acc7-1cd6c8e27043", + "name": "Spawner (126)", + "location": [1020, 1360, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a5e8596-bdb5-4766-b431-45bb6cefbd6d", + "name": "Spawner (126)", + "location": [1024, 1244, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4bfe60c-9ce2-4739-974e-5b0c453aa1f6", + "name": "Spawner (126)", + "location": [802, 1280, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa7f7549-b005-4b92-950f-329e555c8bcd", + "name": "Spawner (126)", + "location": [855, 934, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8a74bc4-a2ed-4d10-b573-cf0ef0fb69db", + "name": "Spawner (126)", + "location": [771, 1013, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98f98072-a630-4276-abed-7d07faba2350", + "name": "Spawner (126)", + "location": [846, 1071, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9adcc4e-105e-4f9d-9044-4112ff31f5bc", + "name": "Spawner (126)", + "location": [940, 1028, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b862532b-c3c1-473e-9c75-2970ef697583", + "name": "Spawner (126)", + "location": [1152, 812, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5403a56a-456b-42ab-9518-889d08375356", + "name": "Spawner (126)", + "location": [1011, 896, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d41167e6-4e39-4443-a288-1ecbd92a54c2", + "name": "Spawner (126)", + "location": [1057, 765, 1], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8665d1be-a08f-41b7-9450-c10bb70577f2", + "name": "Spawner (126)", + "location": [974, 374, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4695be96-fea3-4b09-96ab-b8d7246a785f", + "name": "Spawner (126)", + "location": [1063, 541, 16], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9b091d7-b8ac-49f8-b6b0-420e2f503413", + "name": "Spawner (126)", + "location": [1018, 629, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b96c91c8-d294-44ec-b8ce-13ef5c35f2d4", + "name": "Spawner (126)", + "location": [951, 542, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec2190b0-7f31-40b6-a350-c1bf44d2098a", + "name": "Spawner (126)", + "location": [805, 576, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b2a798ca-14e2-4f65-a3d8-b361e2bec521", + "name": "Spawner (126)", + "location": [937, 743, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9ae00c78-f9de-4faa-b2e2-330e69da4478", + "name": "Spawner (126)", + "location": [890, 657, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d496d8f3-ba8e-47b5-ab86-c4b6a7cba030", + "name": "Spawner (126)", + "location": [895, 830, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3cfc4c1-7fdb-438c-b51d-40211ab8e2fa", + "name": "Spawner (126)", + "location": [689, 753, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd3e9539-89a7-4ccb-b6f4-f9a1621c6f6c", + "name": "Spawner (126)", + "location": [748, 669, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e81bc46f-505a-4020-8e34-026a669c750a", + "name": "Spawner (126)", + "location": [764, 822, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4339d779-ba0b-4a17-aa43-7b29eccd40e4", + "name": "Spawner (126)", + "location": [834, 751, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c598b76-349e-42b5-8a5a-f7395b444def", + "name": "Spawner (126)", + "location": [782, 1156, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9ba90ce-6733-4684-a385-858a97a81829", + "name": "Spawner (126)", + "location": [887, 1223, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a898cee-dada-46e4-89f6-2f32a901de39", + "name": "Spawner (126)", + "location": [916, 1159, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce46d463-3146-4ed2-9c0f-6d7d85c92d69", + "name": "Spawner (126)", + "location": [993, 1130, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd753390-ed2c-4648-a7d8-5b6afa41f989", + "name": "Spawner (126)", + "location": [1047, 1071, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "348ada5a-4afd-47f1-a414-a59766d0f0db", + "name": "Spawner (126)", + "location": [1092, 1010, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae784e69-bee7-4043-88c1-852832876c8c", + "name": "Spawner (126)", + "location": [1172, 974, 2], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4efec350-2605-46b3-923f-be57885e6aad", + "name": "Spawner (126)", + "location": [1388, 784, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f8830c8c-49c9-4ffd-b299-c5412d510b60", + "name": "Spawner (126)", + "location": [1631, 640, 22], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "480a555e-947b-4181-b211-4ed3fef3df68", + "name": "Spawner (126)", + "location": [1526, 960, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b94d2950-01d9-45d2-a308-c4d863487ffb", + "name": "Spawner (126)", + "location": [1545, 813, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60301ceb-5d40-41b1-81f3-d4581dbbb3b2", + "name": "Spawner (126)", + "location": [1695, 809, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd05b1bf-8c39-43cc-82f8-658b0a7837de", + "name": "Spawner (126)", + "location": [1728, 691, 6], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9d939fac-db2a-4bf7-bb3c-5cf12a16057d", + "name": "Spawner (126)", + "location": [1802, 495, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46baf50c-622c-4cfc-9a0c-cb38c883b270", + "name": "Spawner (126)", + "location": [1857, 600, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9fa7f8cb-f1b0-47ef-b274-fae3fdc58df7", + "name": "Spawner (126)", + "location": [1884, 735, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3d015a1-d167-47e6-8f63-e03f77a29019", + "name": "Spawner (126)", + "location": [1973, 751, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b585a36d-a2d6-48c1-a584-59d29425a6cd", + "name": "Spawner (126)", + "location": [2085, 658, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ead3c54c-7f7a-4472-8862-160168ce877d", + "name": "Spawner (126)", + "location": [2367, 748, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6fbfa112-3466-4ae1-b215-45b81f270cca", + "name": "Spawner (126)", + "location": [2289, 917, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8c09fcb-e2a0-466b-884c-b9859c718d3d", + "name": "Spawner (126)", + "location": [2338, 989, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "437c3cc4-ad9f-4da6-932c-b342014445a9", + "name": "Spawner (126)", + "location": [2418, 999, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5275d813-843e-497f-8801-103eaaf41f50", + "name": "Spawner (126)", + "location": [2495, 978, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "25b9f741-b303-4341-ad1a-e4eb7973d3c9", + "name": "Spawner (126)", + "location": [2583, 993, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "43cb2d22-fd8c-4256-860d-22cf169311a5", + "name": "Spawner (126)", + "location": [2513, 782, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f444d0ce-22f3-44d5-b9c2-79a8092e1b07", + "name": "Spawner (126)", + "location": [2748, 660, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba7c897f-c0ce-44c1-9141-7b3cfbb65791", + "name": "Spawner (126)", + "location": [2765, 754, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76e14719-a7c5-4ff5-9ca4-65d2bedd8229", + "name": "Spawner (126)", + "location": [2668, 1020, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a52ff072-2c3d-428e-9d26-cdc4c4b4217f", + "name": "Spawner (126)", + "location": [2669, 922, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6554a940-d516-4359-b224-3d7984509f21", + "name": "Spawner (126)", + "location": [2672, 814, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "110affec-d184-4b10-89a3-db7ed18d0f29", + "name": "Spawner (126)", + "location": [2643, 728, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1c3581a-b27d-41a6-98ce-3db7acc36783", + "name": "Spawner (126)", + "location": [2888, 391, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3c89bb5-3c88-4d7b-8bcf-57f0e40d3d59", + "name": "Spawner (126)", + "location": [2735, 417, 15], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d98a0bd4-2d35-4400-ac73-df07a0390baf", + "name": "Spawner (126)", + "location": [2914, 547, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "261fe98c-05ae-446c-8e4c-6ab6a5c4646b", + "name": "Spawner (126)", + "location": [2814, 492, 15], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbead4d1-29b0-43a8-a5a6-5ceb94ca384d", + "name": "Spawner (126)", + "location": [2688, 524, 11], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "53f1207a-7347-4899-8c9d-020fdd199822", "name": "Spawner (126)", "location": [1651, 612, 22], @@ -11,8 +5689,6 @@ "team": 0, "homeRange": 20, "walkingRange": 20, - "entries": [ - { "name": "TimberWolf", "maxCount": 3, "probability": 100 } - ] + "entries": [{ "name": "TimberWolf", "maxCount": 3, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/Wrong.json b/Distribution/Data/Spawns/post-uoml/trammel/Wrong.json similarity index 93% rename from Distribution/Data/Spawns/shared/trammel/Wrong.json rename to Distribution/Data/Spawns/post-uoml/trammel/Wrong.json index 65b490b2e..3aadd72e4 100644 --- a/Distribution/Data/Spawns/shared/trammel/Wrong.json +++ b/Distribution/Data/Spawns/post-uoml/trammel/Wrong.json @@ -1,15 +1,29 @@ [ { - "$type": "Spawner", - "guid": "0fe79572-c943-489f-917b-ac21ab051e1e", + "type": "Spawner", + "guid": "4c508240-4996-456a-904f-164a1574fda9", "name": "Spawner (127)", - "location": [5857, 562, 15], + "location": [5819, 591, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "JukaWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d0aa4dc9-71ae-41c9-b16a-457c9946ccc4", + "name": "Spawner (127)", + "location": [5808, 588, 12], "map": "Trammel", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 7, + "homeRange": 15, "walkingRange": 30, "entries": [ { "name": "JukaLord", "maxCount": 1, "probability": 100 }, @@ -18,7 +32,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2292c4c5-79a7-42fe-89a0-4cca8b5c9ae2", "name": "Spawner (127)", "location": [5792, 544, 10], @@ -35,23 +49,7 @@ ] }, { - "$type": "Spawner", - "guid": "4c508240-4996-456a-904f-164a1574fda9", - "name": "Spawner (127)", - "location": [5819, 591, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 25, - "entries": [ - { "name": "JukaWarrior", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "7efd5b02-49ba-4d20-a39e-82d9bb6622bb", "name": "Spawner (127)", "location": [5857, 586, 15], @@ -69,26 +67,25 @@ ] }, { - "$type": "Spawner", - "guid": "8d70da7b-2571-4c11-a5fe-4f9a8a3d4083", + "type": "Spawner", + "guid": "0fe79572-c943-489f-917b-ac21ab051e1e", "name": "Spawner (127)", - "location": [5724, 561, 20], + "location": [5857, 562, 15], "map": "Trammel", - "count": 6, + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 8, + "homeRange": 7, "walkingRange": 30, "entries": [ - { "name": "Golem", "maxCount": 1, "probability": 100 }, { "name": "JukaLord", "maxCount": 1, "probability": 100 }, { "name": "JukaMage", "maxCount": 1, "probability": 100 }, { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a3e4a12b-3ec2-4c5e-a333-211c738e2105", "name": "Spawner (127)", "location": [5828, 530, 0], @@ -106,25 +103,7 @@ ] }, { - "$type": "Spawner", - "guid": "aab64623-d091-4058-9ce0-6ca3c039b824", - "name": "Spawner (127)", - "location": [5690, 534, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 30, - "entries": [ - { "name": "JukaLord", "maxCount": 1, "probability": 100 }, - { "name": "JukaMage", "maxCount": 1, "probability": 100 }, - { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "b316ad79-73f6-44b1-99e7-34845ddd72cf", "name": "Spawner (127)", "location": [5659, 563, 20], @@ -143,16 +122,35 @@ ] }, { - "$type": "Spawner", - "guid": "d0aa4dc9-71ae-41c9-b16a-457c9946ccc4", + "type": "Spawner", + "guid": "8d70da7b-2571-4c11-a5fe-4f9a8a3d4083", "name": "Spawner (127)", - "location": [5808, 588, 12], + "location": [5724, 561, 20], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 30, + "entries": [ + { "name": "Golem", "maxCount": 1, "probability": 100 }, + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aab64623-d091-4058-9ce0-6ca3c039b824", + "name": "Spawner (127)", + "location": [5690, 534, 0], "map": "Trammel", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 15, + "homeRange": 8, "walkingRange": 30, "entries": [ { "name": "JukaLord", "maxCount": 1, "probability": 100 }, diff --git a/Distribution/Data/Spawns/shared/felucca/FelCropsLS.json b/Distribution/Data/Spawns/shared/felucca/FelCropsLS.json deleted file mode 100644 index 32d164867..000000000 --- a/Distribution/Data/Spawns/shared/felucca/FelCropsLS.json +++ /dev/null @@ -1,338 +0,0 @@ -[ - { - "$type": "RegionSpawner", - "guid": "0c48edf9-a88e-45f1-9ba3-cd8c919aefbc", - "name": "Spawner", - "location": [823, 2274, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCabbage", "maxCount": 6, "probability": 100 } - ], - "region": "A Cabbage Field in Skara Brae 2" - }, - { - "$type": "RegionSpawner", - "guid": "0c6481ab-3341-4113-ac1a-6523c19c881d", - "name": "Spawner", - "location": [1216, 1604, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableTurnip", "maxCount": 6, "probability": 100 } - ], - "region": "A Turnip Field in Britain 2" - }, - { - "$type": "RegionSpawner", - "guid": "2bf378f0-77ed-44cd-8dfb-181d068f401e", - "name": "Spawner", - "location": [843, 2352, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 6, "probability": 100 } - ], - "region": "A Wheatfield in Skara Brae 2" - }, - { - "$type": "RegionSpawner", - "guid": "3d7925bf-2642-4185-bc03-fa314fb75a7f", - "name": "Spawner", - "location": [675, 1179, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "Sheep", "maxCount": 10, "probability": 100 } - ], - "region": "A Field of Sheep in Yew 2" - }, - { - "$type": "RegionSpawner", - "guid": "425ac125-1597-41b4-9e6d-72b91801230b", - "name": "Spawner", - "location": [675, 939, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "Sheep", "maxCount": 10, "probability": 100 } - ], - "region": "A Field of Sheep in Yew 1" - }, - { - "$type": "RegionSpawner", - "guid": "427451dd-93fa-43c9-85c6-01536066c8cc", - "name": "Spawner", - "location": [823, 2254, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCarrot", "maxCount": 6, "probability": 100 } - ], - "region": "A Carrot Field in Skara Brae" - }, - { - "$type": "RegionSpawner", - "guid": "4b4e3973-d836-4f7c-b9f7-5fb7d468da28", - "name": "Spawner", - "location": [4567, 1475, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCotton", "maxCount": 8, "probability": 100 } - ], - "region": "A Cotton Field in Moonglow" - }, - { - "$type": "RegionSpawner", - "guid": "57da6436-6b16-4697-add5-f4b45776ccbc", - "name": "Spawner", - "location": [813, 2163, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 6, "probability": 100 } - ], - "region": "A Wheatfield in Skara Brae 1" - }, - { - "$type": "RegionSpawner", - "guid": "57f955bb-49e1-4a66-a214-4fbddf8d572b", - "name": "Spawner", - "location": [1231, 1723, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableOnion", "maxCount": 8, "probability": 100 } - ], - "region": "An Onion Field in Britain 1" - }, - { - "$type": "RegionSpawner", - "guid": "58b14343-2032-4507-a913-6b536507c2e5", - "name": "Spawner", - "location": [1199, 1823, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 8, "probability": 100 } - ], - "region": "A Wheatfield in Britain 2" - }, - { - "$type": "RegionSpawner", - "guid": "640fe3f8-9917-4dfa-986d-038923163add", - "name": "Spawner", - "location": [823, 2264, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableOnion", "maxCount": 6, "probability": 100 } - ], - "region": "An Onion Field in Skara Brae" - }, - { - "$type": "RegionSpawner", - "guid": "7b09d4b5-fe63-4f95-8525-3c970c513adc", - "name": "Spawner", - "location": [1232, 1604, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCarrot", "maxCount": 6, "probability": 100 } - ], - "region": "A Carrot Field in Britain 2" - }, - { - "$type": "RegionSpawner", - "guid": "8536c19c-2036-495a-a3c5-e82c9f32f5ad", - "name": "Spawner", - "location": [1183, 1683, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCabbage", "maxCount": 8, "probability": 100 } - ], - "region": "A Cabbage Field in Britain 1" - }, - { - "$type": "RegionSpawner", - "guid": "9805ef58-355f-4352-bfc4-43c6710ff409", - "name": "Spawner", - "location": [1215, 1723, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCarrot", "maxCount": 8, "probability": 100 } - ], - "region": "A Carrot Field in Britain 1" - }, - { - "$type": "RegionSpawner", - "guid": "aed692cc-2dac-4c80-931b-35c73ca8d092", - "name": "Spawner", - "location": [571, 1099, 0], - "map": "Felucca", - "count": 10, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "Sheep", "maxCount": 10, "probability": 100 } - ], - "region": "A Farm in Yew" - }, - { - "$type": "RegionSpawner", - "guid": "b29b54ab-5ae3-4ce3-93ac-326f324a34f6", - "name": "Spawner", - "location": [1135, 1791, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 8, "probability": 100 } - ], - "region": "A Wheatfield in Britain 1" - }, - { - "$type": "RegionSpawner", - "guid": "bba64d97-11eb-4a87-b326-e41e3c9b355d", - "name": "Spawner", - "location": [375, 1191, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 6, "probability": 100 } - ], - "region": "A Wheatfield in Yew 2" - }, - { - "$type": "RegionSpawner", - "guid": "d8a2e54e-4951-468f-93c5-376460070cba", - "name": "Spawner", - "location": [1199, 1683, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableTurnip", "maxCount": 8, "probability": 100 } - ], - "region": "A Turnip Field in Britain 1" - }, - { - "$type": "RegionSpawner", - "guid": "e37b4d74-c87e-4102-8c87-8f80c728fe0c", - "name": "Spawner", - "location": [567, 1239, 0], - "map": "Felucca", - "count": 5, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 5, "probability": 100 } - ], - "region": "A Wheatfield in Yew 1" - }, - { - "$type": "RegionSpawner", - "guid": "e416e9fa-5a9a-426b-962e-50cdce64ca7f", - "name": "Spawner", - "location": [1152, 1576, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 8, "probability": 100 } - ], - "region": "A Wheatfield in Britain 5" - }, - { - "$type": "RegionSpawner", - "guid": "e6a4aede-5a76-473d-8331-4bb73701ea1a", - "name": "Spawner", - "location": [1120, 1624, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 8, "probability": 100 } - ], - "region": "A Wheatfield in Britain 4" - }, - { - "$type": "RegionSpawner", - "guid": "e7b34039-ec1f-43c8-b64f-d7462ce7ae9d", - "name": "Spawner", - "location": [823, 2355, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCotton", "maxCount": 6, "probability": 100 } - ], - "region": "A Cotton Field in Skara Brae" - }, - { - "$type": "RegionSpawner", - "guid": "f4bccdc2-c663-4ccd-b7df-31656fa2ae09", - "name": "Spawner", - "location": [823, 2264, 0], - "map": "Felucca", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCabbage", "maxCount": 6, "probability": 100 } - ], - "region": "A Cabbage Field in Skara Brae 1" - }, - { - "$type": "RegionSpawner", - "guid": "f68b6bc8-f822-4c7a-9d01-37fad25bc95b", - "name": "Spawner", - "location": [1231, 1887, 0], - "map": "Felucca", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 8, "probability": 100 } - ], - "region": "A Wheatfield in Britain 3" - } -] diff --git a/Distribution/Data/Spawns/shared/ilshenar/Outdoors.json b/Distribution/Data/Spawns/shared/ilshenar/Outdoors.json deleted file mode 100644 index 94b543589..000000000 --- a/Distribution/Data/Spawns/shared/ilshenar/Outdoors.json +++ /dev/null @@ -1,3356 +0,0 @@ -[ - { - "$type": "Spawner", - "guid": "0094215e-9411-4fa1-a091-dbfd46540289", - "name": "Spawner (306)", - "location": [853, 762, -80], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 17, - "walkingRange": 17, - "entries": [ - { "name": "Quagmire", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "01b06a3d-d447-4ada-aa1e-c0ed77f20988", - "name": "Spawner (306)", - "location": [630, 828, -55], - "map": "Ilshenar", - "count": 25, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 85, - "walkingRange": 110, - "entries": [ - { "name": "TropicalBird", "maxCount": 7, "probability": 100 }, - { "name": "Boar", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 4, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 4, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 4, "probability": 100 }, - { "name": "Gorilla", "maxCount": 4, "probability": 100 }, - { "name": "Panther", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "04bb184e-3ba8-40ea-90db-59479a0e21a2", - "name": "Spawner (306)", - "location": [1070, 750, -80], - "map": "Ilshenar", - "count": 14, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "SewerRat", "maxCount": 4, "probability": 100 }, - { "name": "Corpser", "maxCount": 2, "probability": 100 }, - { "name": "Bogling", "maxCount": 2, "probability": 100 }, - { "name": "GiantRat", "maxCount": 2, "probability": 100 }, - { "name": "Alligator", "maxCount": 2, "probability": 100 }, - { "name": "Mongbat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "05ea0169-d712-439a-bbbc-decb66204c70", - "name": "Spawner (306)", - "location": [879, 781, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "ChaosDaemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0608e0a7-41e3-416a-b283-c4c7548ff433", - "name": "Spawner (306)", - "location": [681, 1125, -73], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 65, - "entries": [ - { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, - { "name": "Gorilla", "maxCount": 2, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0636eb85-29c6-4f6c-b3fa-56930c3ab4b0", - "name": "Spawner (306)", - "location": [1083, 664, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "JukaLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "08173e6f-aebd-4ba2-85ae-6b9390f2d26c", - "name": "Spawner (306)", - "location": [1504, 878, 10], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "GiantSpider", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "089d463b-27b6-4f55-84f7-e72bb08fcb58", - "name": "Spawner (306)", - "location": [742, 440, -59], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 }, - { "name": "LichLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "08c9e8fa-503a-4590-ae93-498397e9f798", - "name": "Spawner (306)", - "location": [646, 1209, -93], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 50, - "entries": [ - { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, - { "name": "Gorilla", "maxCount": 2, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "09d2520c-8083-4cd9-ac10-7e8c67cbb01b", - "name": "Spawner (306)", - "location": [638, 555, -75], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Wyvern", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0ab9d3c1-154c-4ceb-9fcc-cb2142950550", - "name": "Spawner (306)", - "location": [783, 448, -47], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 }, - { "name": "LichLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0b49d91f-2e00-4a51-bfe4-887dc0648c2f", - "name": "Spawner (306)", - "location": [370, 264, -52], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Wyvern", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "DreadSpider", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0b4c3c4c-418e-46f7-be0f-d39d51e8843a", - "name": "Spawner (306)", - "location": [397, 958, -80], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 70, - "entries": [ - { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, - { "name": "Gorilla", "maxCount": 2, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0bc9fdae-c152-4e2e-a117-f7664d7a7f30", - "name": "Spawner (306)", - "location": [1135, 743, -80], - "map": "Ilshenar", - "count": 14, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "SewerRat", "maxCount": 4, "probability": 100 }, - { "name": "Corpser", "maxCount": 2, "probability": 100 }, - { "name": "Bogling", "maxCount": 2, "probability": 100 }, - { "name": "GiantRat", "maxCount": 2, "probability": 100 }, - { "name": "Alligator", "maxCount": 2, "probability": 100 }, - { "name": "Mongbat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0c2ea734-6dea-4648-8c9d-37a5e91eea2b", - "name": "Spawner (306)", - "location": [489, 488, -62], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, - { "name": "Gazer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0d513dab-cb9d-42bc-8e86-ffd715ffc3e5", - "name": "Spawner (306)", - "location": [514, 1225, -66], - "map": "Ilshenar", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, - { "name": "Boar", "maxCount": 1, "probability": 100 }, - { "name": "Snake", "maxCount": 2, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 2, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 1, "probability": 100 }, - { "name": "Gorilla", "maxCount": 1, "probability": 100 }, - { "name": "Panther", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11402edf-4c62-4179-a86a-1f49db2c8b4d", - "name": "Spawner (306)", - "location": [295, 1348, -26], - "map": "Ilshenar", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "126847de-2d7c-49ce-baba-7939b13f5751", - "name": "Spawner (306)", - "location": [1084, 675, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 15, - "entries": [ - { "name": "JukaMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "135c1626-1ac5-4fe6-970c-3bf9d19f588c", - "name": "Spawner (306)", - "location": [1519, 1019, -6], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 0, - "entries": [ - { "name": "HealerCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "13bc99c3-a51d-4ca0-b6bc-4c5698870ad0", - "name": "Spawner (306)", - "location": [964, 456, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "EarthElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "14c41ba9-6903-43dc-bfb0-30b6c10df12f", - "name": "Spawner (306)", - "location": [1592, 1191, -20], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Unicorn", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "16b1d51f-a63a-402c-baf0-fccb22964b64", - "name": "Spawner (306)", - "location": [1160, 681, -80], - "map": "Ilshenar", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "SewerRat", "maxCount": 4, "probability": 100 }, - { "name": "Corpser", "maxCount": 2, "probability": 100 }, - { "name": "Bogling", "maxCount": 1, "probability": 100 }, - { "name": "GiantRat", "maxCount": 2, "probability": 100 }, - { "name": "Alligator", "maxCount": 2, "probability": 100 }, - { "name": "Mongbat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "16fb0dfe-0333-467f-a29e-395067847379", - "name": "Spawner (306)", - "location": [748, 492, -64], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, - { "name": "Gazer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1a3d283e-539d-4a54-8f31-ccfba1a1712e", - "name": "Spawner (306)", - "location": [756, 208, -40], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Cyclops", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1abdf777-6592-4e3e-8ce2-9863e7975f8d", - "name": "Spawner (306)", - "location": [1445, 384, 22], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Troll", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1bbb08b1-5a8f-49fa-923e-9e1a7c1a2758", - "name": "Spawner (306)", - "location": [1070, 750, -80], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 4, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 2, "probability": 100 }, - { "name": "AcidElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1bf4cbee-2835-4c1e-aa54-49f61260836e", - "name": "Spawner (306)", - "location": [1070, 639, -79], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "JukaLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d561eeb-d2ca-49c5-9abd-02b48dd5d7b4", - "name": "Spawner (306)", - "location": [964, 638, -80], - "map": "Ilshenar", - "count": 22, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 5, "probability": 100 }, - { "name": "Scorpion", "maxCount": 5, "probability": 100 }, - { "name": "SandVortex", "maxCount": 5, "probability": 100 }, - { "name": "Ridgeback", "maxCount": 1, "probability": 100 }, - { "name": "Efreet", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1df0bbb8-c293-4d0b-8ddc-080926fa4246", - "name": "Spawner (306)", - "location": [1352, 667, 111], - "map": "Ilshenar", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 22, - "walkingRange": 22, - "entries": [ - { "name": "FrostTroll", "maxCount": 3, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, - { "name": "IceElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1e5b54c0-d694-4a14-ade1-0da78a1829b8", - "name": "Spawner (306)", - "location": [1319, 560, -4], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Gargoyle", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1e695260-4946-433b-91c3-0e67eb3434ef", - "name": "Spawner (306)", - "location": [733, 1238, -68], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 65, - "entries": [ - { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, - { "name": "Gorilla", "maxCount": 2, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2078f000-de9a-492b-a35f-fa4a8cdff17c", - "name": "Spawner (306)", - "location": [1097, 702, -79], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 7, - "entries": [ - { "name": "GolemController", "maxCount": 1, "probability": 100 }, - { "name": "Golem", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "209ff492-213a-4e4f-bc0f-e00be14de63a", - "name": "Spawner (306)", - "location": [1160, 681, -80], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20d82817-a691-4340-b341-3aadd53f0471", - "name": "Spawner (306)", - "location": [1442, 576, -11], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Skeleton", "maxCount": 5, "probability": 100 }, - { "name": "Shade", "maxCount": 6, "probability": 100 }, - { "name": "Spectre", "maxCount": 6, "probability": 100 }, - { "name": "Wraith", "maxCount": 6, "probability": 100 }, - { "name": "Ghoul", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "251d70b0-01cd-4817-b072-1dd7dd5ba417", - "name": "Spawner (306)", - "location": [366, 881, -73], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "286d344f-fbf6-4cea-bd98-66a6ae098622", - "name": "Spawner (306)", - "location": [567, 807, -74], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 0, - "entries": [ - { "name": "MageCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2877d30d-dff4-449d-927f-2c30146d5060", - "name": "Spawner (306)", - "location": [445, 318, -64], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ba57b1c-8c9a-4b21-9d44-44a36804e6c5", - "name": "Spawner (306)", - "location": [1421, 517, 1], - "map": "Ilshenar", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 40, - "entries": [ - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2f26425c-ee86-48f4-84e8-96c85410fdf7", - "name": "Spawner (306)", - "location": [354, 1140, -52], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Shade", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2f2b8727-6b0d-4ce1-97f5-6893895a89bd", - "name": "Spawner (306)", - "location": [1090, 646, -60], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 10, - "entries": [ - { "name": "Betrayer", "maxCount": 1, "probability": 100 }, - { "name": "JukaLord", "maxCount": 1, "probability": 100 }, - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33d32b88-b234-4f29-911b-db8f5008fe7d", - "name": "Spawner (306)", - "location": [664, 665, -35], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 654, "y": 655, "z": -128 }, - "end": { "x": 674, "y": 675, "z": -20 } - }, - "walkingRange": 10, - "entries": [ - { "name": "RatmanMage", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3585e152-9781-42d6-8524-88edff5ca30e", - "name": "Spawner (306)", - "location": [1372, 623, 111], - "map": "Ilshenar", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 30, - "entries": [ - { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, - { "name": "IceElemental", "maxCount": 1, "probability": 100 }, - { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, - { "name": "FrostSpider", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "370cf0c4-a6d5-4940-b3bc-5e911aad3b67", - "name": "Spawner (306)", - "location": [353, 1140, -54], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Spectre", "maxCount": 3, "probability": 100 }, - { "name": "Shade", "maxCount": 3, "probability": 100 }, - { "name": "Wraith", "maxCount": 3, "probability": 100 }, - { "name": "Ghoul", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37253fe5-959b-45cf-88ab-8b6551fbbab7", - "name": "Spawner (306)", - "location": [1088, 661, -60], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 9, - "walkingRange": 10, - "entries": [ - { "name": "JukaMage", "maxCount": 1, "probability": 100 }, - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 }, - { "name": "Juggernaut", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3845912b-63e1-42e8-8574-63bf5c8f3401", - "name": "Spawner (306)", - "location": [739, 264, -54], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 35, - "walkingRange": 35, - "entries": [ - { "name": "Cyclops", "maxCount": 1, "probability": 100 }, - { "name": "Titan", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3846e700-72a4-4d94-84e5-06b5d9b044c5", - "name": "Spawner (306)", - "location": [978, 471, -80], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Imp", "maxCount": 1, "probability": 100 }, - { "name": "GolemController", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "38b098ef-adb7-4187-9c46-2266659838b7", - "name": "Spawner (306)", - "location": [325, 893, -72], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "38b2eaed-8ee0-4992-bc2d-b0b916e515b2", - "name": "Spawner (306)", - "location": [1571, 253, 14], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Imp", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e4a4532-780a-4b55-ac97-e1dc5f3f816a", - "name": "Spawner (306)", - "location": [1000, 272, 56], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 975, "y": 247, "z": -128 }, - "end": { "x": 1025, "y": 297, "z": 95 } - }, - "walkingRange": 25, - "entries": [ - { "name": "LichLord", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 3, "probability": 100 }, - { "name": "Spectre", "maxCount": 3, "probability": 100 }, - { "name": "Wraith", "maxCount": 3, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 }, - { "name": "Ghoul", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40d88f7b-2b85-4fb5-a207-7ce74ecf3f3a", - "name": "Spawner (306)", - "location": [1805, 469, -9], - "map": "Ilshenar", - "count": 14, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, - { "name": "Scorpion", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "413d50ce-9d24-4ed3-ae07-77b1fe16f29a", - "name": "Spawner (306)", - "location": [1041, 272, 56], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 1016, "y": 247, "z": -128 }, - "end": { "x": 1066, "y": 297, "z": 95 } - }, - "walkingRange": 25, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 3, "probability": 100 }, - { "name": "Spectre", "maxCount": 3, "probability": 100 }, - { "name": "Wraith", "maxCount": 3, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 }, - { "name": "Ghoul", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "42dd61c7-6bec-4781-b24e-6db7864b72a4", - "name": "Spawner (306)", - "location": [941, 471, -38], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Imp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4345b3d1-292e-4c01-af9c-e4abe339b8c5", - "name": "Spawner (306)", - "location": [330, 881, -73], - "map": "Ilshenar", - "count": 17, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 }, - { "name": "Spectre", "maxCount": 5, "probability": 100 }, - { "name": "Shade", "maxCount": 5, "probability": 100 }, - { "name": "Wraith", "maxCount": 5, "probability": 100 }, - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 1, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 2, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 4, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "44dd1dfd-d45f-41a4-a7bb-bd44c257a4f3", - "name": "Spawner (306)", - "location": [447, 425, -60], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "462ee8dd-f60c-438e-90a7-0bd1b7b2f137", - "name": "Spawner (306)", - "location": [223, 1360, -15], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "AncientWyrm", "maxCount": 1, "probability": 100 }, - { "name": "Wyvern", "maxCount": 2, "probability": 100 }, - { "name": "LavaLizard", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "48fa1520-858d-40ea-b7f0-a1d47fb0ebbc", - "name": "Spawner (306)", - "location": [963, 1028, -31], - "map": "Ilshenar", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 40, - "entries": [ - { "name": "Reaper", "maxCount": 2, "probability": 100 }, - { "name": "Mongbat", "maxCount": 4, "probability": 100 }, - { "name": "Imp", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49a3f002-3ed6-4e08-b8de-d6ffa7dae59f", - "name": "Spawner (306)", - "location": [1733, 1161, -5], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Skeleton", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d8998df-e3b7-41c8-9e15-7147a530e05c", - "name": "Spawner (306)", - "location": [430, 1176, -68], - "map": "Ilshenar", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "SwampTentacle", "maxCount": 4, "probability": 100 }, - { "name": "GiantToad", "maxCount": 12, "probability": 100 }, - { "name": "Alligator", "maxCount": 14, "probability": 100 }, - { "name": "Reaper", "maxCount": 4, "probability": 100 }, - { "name": "Corpser", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4f9a9db0-6b48-4134-be58-16590bbcd28d", - "name": "Spawner (306)", - "location": [1107, 627, -59], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 7, - "entries": [ - { "name": "JukaMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50392cce-759c-41f2-8ceb-926f00d1a455", - "name": "Spawner (306)", - "location": [1135, 743, -80], - "map": "Ilshenar", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 4, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 2, "probability": 100 }, - { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, - { "name": "BogThing", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5056885c-d7a0-4cff-9e18-b2edb0f507fe", - "name": "Spawner (306)", - "location": [377, 340, -14], - "map": "Ilshenar", - "count": 35, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 150, - "entries": [ - { "name": "TropicalBird", "maxCount": 9, "probability": 100 }, - { "name": "Boar", "maxCount": 5, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 5, "probability": 100 }, - { "name": "Gorilla", "maxCount": 5, "probability": 100 }, - { "name": "Panther", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "51c640e7-ecaf-4949-8de8-37b31daaa1b8", - "name": "Spawner (306)", - "location": [588, 674, -60], - "map": "Ilshenar", - "count": 25, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 75, - "walkingRange": 100, - "entries": [ - { "name": "TropicalBird", "maxCount": 7, "probability": 100 }, - { "name": "Boar", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 4, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 4, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 4, "probability": 100 }, - { "name": "Gorilla", "maxCount": 4, "probability": 100 }, - { "name": "Panther", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "53db67c5-be50-4250-bdaa-14d7684f9ee2", - "name": "Spawner (306)", - "location": [1183, 1287, -30], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "Skeleton", "maxCount": 3, "probability": 100 }, - { "name": "Zombie", "maxCount": 3, "probability": 100 }, - { "name": "Ghoul", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "58561047-30f5-4a7d-9aa9-6430566a554a", - "name": "Spawner (306)", - "location": [1852, 484, -13], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5a3db698-ab8a-4794-b798-79dcb81d2229", - "name": "Spawner (306)", - "location": [1358, 498, 3], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 0, - "entries": [ - { "name": "HealerCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5c8e9fc9-aef3-4248-aa1d-829108d3f59b", - "name": "Spawner (306)", - "location": [664, 668, 5], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 657, "y": 661, "z": 5 }, - "end": { "x": 671, "y": 675, "z": 20 } - }, - "walkingRange": 10, - "entries": [ - { "name": "RatmanMage", "maxCount": 1, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, - { "name": "Ratman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5ce8e092-abb1-4db0-ad42-c3cb2d4ab28d", - "name": "Spawner (306)", - "location": [350, 1204, -38], - "map": "Ilshenar", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "SwampTentacle", "maxCount": 2, "probability": 100 }, - { "name": "GiantToad", "maxCount": 5, "probability": 100 }, - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "Reaper", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d4c8c7a-0513-4f06-b84e-85896cd5c5c9", - "name": "Spawner (306)", - "location": [987, 791, -80], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "603df694-0c2b-450a-8c18-64b6dbbcbacf", - "name": "Spawner (306)", - "location": [605, 973, -82], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 65, - "entries": [ - { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, - { "name": "Gorilla", "maxCount": 2, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60e90560-1527-4f63-b097-6a4ec6c38f8e", - "name": "Spawner (306)", - "location": [648, 1321, -57], - "map": "Ilshenar", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 }, - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "628ed55b-7593-471e-8676-3a017e26ea43", - "name": "Spawner (306)", - "location": [1400, 687, 32], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Harpy", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "63b04be0-c512-4985-b0b2-30ba0b282d84", - "name": "Spawner (306)", - "location": [275, 453, -53], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 50, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6428c823-54f9-4675-9bb9-8c5d4b0a5db0", - "name": "Spawner (306)", - "location": [485, 891, -82], - "map": "Ilshenar", - "count": 14, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "RatmanMage", "maxCount": 1, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "HellHound", "maxCount": 2, "probability": 100 }, - { "name": "Slime", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "649e26f7-54c4-42ce-a8f4-7a755cc361f3", - "name": "Spawner (306)", - "location": [783, 448, -47], - "map": "Ilshenar", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 }, - { "name": "Shade", "maxCount": 4, "probability": 100 }, - { "name": "Spectre", "maxCount": 4, "probability": 100 }, - { "name": "Wraith", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 2, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 2, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6633838e-e038-4494-a3b9-2b7cb570c675", - "name": "Spawner (306)", - "location": [511, 545, -60], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 25, - "entries": [ - { "name": "Brigand", "maxCount": 4, "probability": 100 }, - { "name": "EvilMage", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "697f7eba-a9f6-4703-800a-44b754370384", - "name": "Spawner (306)", - "location": [930, 519, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 15, - "entries": [ - { "name": "Imp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6a0bfc1f-d6f8-47a2-9551-62bdea09f4d5", - "name": "Spawner (306)", - "location": [703, 984, -68], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 65, - "entries": [ - { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, - { "name": "Gorilla", "maxCount": 2, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6a7e29f4-5aa1-4106-94df-76d248d282f4", - "name": "Spawner (306)", - "location": [1075, 628, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 7, - "entries": [ - { "name": "JukaMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6e75609b-a0f2-4f77-8ea2-63bdbb1b24a8", - "name": "Spawner (306)", - "location": [1000, 272, 56], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 975, "y": 247, "z": -128 }, - "end": { "x": 1025, "y": 297, "z": 95 } - }, - "walkingRange": 25, - "entries": [ - { "name": "DreadSpider", "maxCount": 1, "probability": 100 }, - { "name": "StoneGargoyle", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 1, "probability": 100 }, - { "name": "ElderGazer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f341a81-ed10-448a-8986-9b6fe3f7f42e", - "name": "Spawner (306)", - "location": [664, 665, -15], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 657, "y": 658, "z": -15 }, - "end": { "x": 671, "y": 672, "z": 0 } - }, - "walkingRange": 10, - "entries": [ - { "name": "RatmanArcher", "maxCount": 2, "probability": 100 }, - { "name": "Ratman", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7431d589-5e24-4f05-b479-30dc17c7e942", - "name": "Spawner (306)", - "location": [1098, 698, -60], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 5, - "entries": [ - { "name": "GolemController", "maxCount": 1, "probability": 100 }, - { "name": "Golem", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "749fd6cf-bc7c-476e-b125-e980ab8d540e", - "name": "Spawner (306)", - "location": [468, 874, -80], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 50, - "entries": [ - { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, - { "name": "Gorilla", "maxCount": 2, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7513fe51-3c38-4bdd-9462-71b8442352ae", - "name": "Spawner (306)", - "location": [821, 758, -80], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 17, - "walkingRange": 17, - "entries": [ - { "name": "ChaosDaemon", "maxCount": 1, "probability": 100 }, - { "name": "Moloch", "maxCount": 1, "probability": 100 }, - { "name": "Doppleganger", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7566467d-86a1-4e77-8d7e-cd2c876f4af2", - "name": "Spawner (306)", - "location": [1585, 990, 20], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 10, - "entries": [ - { "name": "GiantRat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "757b03a1-ee65-4fa4-8465-cffc7754f9af", - "name": "Spawner (306)", - "location": [946, 430, -80], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76827ffe-c3f1-4bcf-84fc-b3e2bc796c2e", - "name": "Spawner (306)", - "location": [981, 470, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "EarthElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "78364853-0fe1-4bc7-b3e9-2c8cb742514c", - "name": "Spawner (306)", - "location": [1059, 675, -79], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "798e730a-c923-4159-abf5-0c1492331a67", - "name": "Spawner (306)", - "location": [1747, 1221, -1], - "map": "Ilshenar", - "count": 11, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 4, "probability": 100 }, - { "name": "Imp", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c2b6978-ce19-490d-8d71-7ed2aa368bfd", - "name": "Spawner (306)", - "location": [1480, 1207, -18], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Unicorn", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d92c3d5-24c0-4caa-b732-0acaa35b5a4b", - "name": "Spawner (306)", - "location": [1400, 572, -13], - "map": "Ilshenar", - "count": 11, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 }, - { "name": "Shade", "maxCount": 4, "probability": 100 }, - { "name": "Spectre", "maxCount": 4, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 }, - { "name": "Efreet", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7de1e641-1ffc-4161-b38f-a3730aeab3a2", - "name": "Spawner (306)", - "location": [1275, 401, -1], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 35, - "walkingRange": 40, - "entries": [ - { "name": "Drake", "maxCount": 2, "probability": 100 }, - { "name": "Harpy", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8148df05-3fd6-4b7e-a052-bc8830e173e5", - "name": "Spawner (306)", - "location": [338, 863, -72], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "LichLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "827416b4-3e01-4d6a-9901-2b0cdc57ef3b", - "name": "Spawner (306)", - "location": [460, 469, -49], - "map": "Ilshenar", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 75, - "entries": [ - { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 5, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, - { "name": "Gorilla", "maxCount": 3, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "82d08b20-2c54-4d66-b165-832eb2d9eefd", - "name": "Spawner (306)", - "location": [1828, 601, 38], - "map": "Ilshenar", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Wyvern", "maxCount": 2, "probability": 100 }, - { "name": "Mummy", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "82fd9f36-5fca-4deb-bbdf-7fe85fba6344", - "name": "Spawner (306)", - "location": [1775, 530, 66], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "85ff24c5-747c-429c-ac5b-288a259572f4", - "name": "Spawner (306)", - "location": [1092, 628, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 7, - "entries": [ - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "87acf4d6-d8a3-4302-82e7-da7e4bc8b040", - "name": "Spawner (306)", - "location": [1083, 628, -60], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 15, - "entries": [ - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "88063e8a-ea72-4a69-945a-274e4cd2f6a2", - "name": "Spawner (306)", - "location": [1530, 667, -14], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 15, - "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8a38f9e6-156d-4af4-a732-eceae98cb9c1", - "name": "Spawner (306)", - "location": [778, 264, -38], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 35, - "walkingRange": 35, - "entries": [ - { "name": "Cyclops", "maxCount": 1, "probability": 100 }, - { "name": "Titan", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ccefff3-a611-4d68-856d-c55d3f0f1c68", - "name": "Spawner (306)", - "location": [1496, 280, -10], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Imp", "maxCount": 3, "probability": 100 }, - { "name": "Daemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ed26f0f-de07-4412-bc99-6cee204f1f40", - "name": "Spawner (306)", - "location": [1072, 651, -60], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 6, - "entries": [ - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9066bb92-197e-4a1d-bc36-61ead0f50a79", - "name": "Spawner (306)", - "location": [1060, 659, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 7, - "entries": [ - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "91bf1d10-57ae-4e79-a7db-66b06c5e1320", - "name": "Spawner (306)", - "location": [913, 935, -34], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "WaterElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "91d2b43c-a0fe-4613-8cc3-cf9d67f3e5ca", - "name": "Spawner (306)", - "location": [973, 460, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "LavaLizard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "92d313e6-e98e-4803-b61b-faea999cd89f", - "name": "Spawner (306)", - "location": [1083, 651, -40], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Juggernaut", "maxCount": 3, "probability": 100 }, - { "name": "Golem", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9320cae4-3837-4cb8-8ef7-9238f5ca2eae", - "name": "Spawner (306)", - "location": [1237, 473, -4], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, - { "name": "RatmanMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93695f7d-f83a-4797-a125-6ff28e94c368", - "name": "Spawner (306)", - "location": [902, 1308, -71], - "map": "Ilshenar", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 35, - "walkingRange": 40, - "entries": [ - { "name": "Titan", "maxCount": 2, "probability": 100 }, - { "name": "Cyclops", "maxCount": 4, "probability": 100 }, - { "name": "Ettin", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "99def11e-ee3f-440e-bafb-2a0b3c89d0c9", - "name": "Spawner (306)", - "location": [1772, 67, -28], - "map": "Ilshenar", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 25, - "entries": [ - { "name": "MeerWarrior", "maxCount": 3, "probability": 100 }, - { "name": "MeerMage", "maxCount": 2, "probability": 100 }, - { "name": "MeerCaptain", "maxCount": 2, "probability": 100 }, - { "name": "MeerEternal", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9a9045a4-e6a0-4c29-85af-6f0f497b6c21", - "name": "Spawner (306)", - "location": [1186, 1223, -20], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 0, - "entries": [ - { "name": "BankerCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9addf88a-7b37-49e1-aadc-5779d6eb586d", - "name": "Spawner (306)", - "location": [858, 799, -80], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 17, - "walkingRange": 17, - "entries": [ - { "name": "ChaosDaemon", "maxCount": 2, "probability": 100 }, - { "name": "Moloch", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c699c4f-00f9-4e38-b62a-b94f5cfbcffb", - "name": "Spawner (306)", - "location": [1316, 633, 106], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 30, - "entries": [ - { "name": "FrostTroll", "maxCount": 2, "probability": 100 }, - { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, - { "name": "IceElemental", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9cdf8453-6a90-4b5b-baf9-9698ab361f4a", - "name": "Spawner (306)", - "location": [1416, 317, 74], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 7, - "walkingRange": 7, - "entries": [ - { "name": "AncientWyrm", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a037d753-db69-4c16-8c67-4c1405a4aa0f", - "name": "Spawner (306)", - "location": [564, 1142, -99], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 45, - "walkingRange": 65, - "entries": [ - { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, - { "name": "Gorilla", "maxCount": 2, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a1b1ec95-54f3-4f98-a891-4dfd576a0ac6", - "name": "Spawner (306)", - "location": [1700, 546, 5], - "map": "Ilshenar", - "count": 14, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 14, "probability": 100 }, - { "name": "Scorpion", "maxCount": 14, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a2ffa9ab-3ee2-4cea-8a60-b1c14c51a703", - "name": "Spawner (306)", - "location": [409, 264, -44], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Wyvern", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "DreadSpider", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a4f9334f-bdb9-411b-b848-4b41043b7a5c", - "name": "Spawner (306)", - "location": [1464, 241, 2], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Ettin", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a6753c19-0c02-4974-8f9a-ec93fb28116e", - "name": "Spawner (306)", - "location": [1087, 698, -80], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 7, - "entries": [ - { "name": "GolemController", "maxCount": 1, "probability": 100 }, - { "name": "Golem", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa6761b5-dd89-4700-8c1c-7fafcefcb035", - "name": "Spawner (306)", - "location": [1318, 1246, -13], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 50, - "entries": [ - { "name": "Pixie", "maxCount": 3, "probability": 100 }, - { "name": "Horse", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aabcb1f9-f76a-49db-a0f2-617e9abd2d66", - "name": "Spawner (306)", - "location": [605, 1061, -84], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 45, - "walkingRange": 55, - "entries": [ - { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, - { "name": "Gorilla", "maxCount": 2, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abd36839-1bf9-4879-b41e-6857521a0a28", - "name": "Spawner (306)", - "location": [742, 440, -59], - "map": "Ilshenar", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Skeleton", "maxCount": 4, "probability": 100 }, - { "name": "Shade", "maxCount": 4, "probability": 100 }, - { "name": "Spectre", "maxCount": 4, "probability": 100 }, - { "name": "Wraith", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, - { "name": "BoneKnight", "maxCount": 2, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 2, "probability": 100 }, - { "name": "BoneMagi", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abe02eba-8c77-43a4-b81a-88b366c1bbbb", - "name": "Spawner (306)", - "location": [400, 1334, -37], - "map": "Ilshenar", - "count": 18, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 70, - "walkingRange": 70, - "entries": [ - { "name": "SwampTentacle", "maxCount": 5, "probability": 100 }, - { "name": "GiantToad", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Alligator", "maxCount": 0, "probability": 100 }, - { "name": "Reaper", "maxCount": 0, "probability": 100 }, - { "name": "Corpser", "maxCount": 0, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ac49b12d-1a92-429d-9434-b88fe0ae6bcb", - "name": "Spawner (306)", - "location": [565, 343, -77], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Wyvern", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ace0f33f-dea2-40b1-a1d8-5d8404cdc903", - "name": "Spawner (306)", - "location": [1070, 650, -79], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 7, - "entries": [ - { "name": "JukaMage", "maxCount": 1, "probability": 100 }, - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae1cec6c-8c74-4b4b-849f-f482df430969", - "name": "Spawner (306)", - "location": [1129, 1236, -4], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "Brigand", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b0a1a3db-b946-47b0-9be9-26093c01cbef", - "name": "Spawner (306)", - "location": [855, 464, -2], - "map": "Ilshenar", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Skeleton", "maxCount": 6, "probability": 100 }, - { "name": "Wraith", "maxCount": 2, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 }, - { "name": "Zombie", "maxCount": 3, "probability": 100 }, - { "name": "SkeletalMage", "maxCount": 3, "probability": 100 }, - { "name": "Wyvern", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b158181c-8e65-42d1-ad74-5f8755e9e21e", - "name": "Spawner (306)", - "location": [1503, 239, -7], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Ettin", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b30f735e-cc37-4cae-b3a2-4bd44bf5441e", - "name": "Spawner (306)", - "location": [1083, 638, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4fbf340-bb61-47b8-8e2f-ac6ed604214a", - "name": "Spawner (306)", - "location": [1060, 643, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 7, - "entries": [ - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b70b8b97-55cc-4666-b5c0-5088913de58d", - "name": "Spawner (306)", - "location": [644, 819, -44], - "map": "Ilshenar", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 25, - "entries": [ - { "name": "RatmanMage", "maxCount": 3, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b7a6b349-9426-4332-8fca-238370a60f3b", - "name": "Spawner (306)", - "location": [1803, 66, -28], - "map": "Ilshenar", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 25, - "entries": [ - { "name": "MeerWarrior", "maxCount": 4, "probability": 100 }, - { "name": "MeerMage", "maxCount": 2, "probability": 100 }, - { "name": "MeerCaptain", "maxCount": 1, "probability": 100 }, - { "name": "MeerEternal", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9bbfa6b-b223-4fb1-828c-100ca2ceb176", - "name": "Spawner (306)", - "location": [487, 890, -82], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Jwilson", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bb3daf66-4926-47b9-9113-9bf6e30303f3", - "name": "Spawner (306)", - "location": [1763, 344, 52], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bb89d203-e35e-4b3c-87a0-83ac03e2822f", - "name": "Spawner (306)", - "location": [960, 524, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 15, - "entries": [ - { "name": "EarthElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bc7cac1d-5957-4d5f-b459-c7379c118384", - "name": "Spawner (306)", - "location": [350, 1441, 17], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Imp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd8c40d5-7d9f-46f2-86f9-ab58c0ec5d31", - "name": "Spawner (306)", - "location": [935, 1159, 13], - "map": "Ilshenar", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 40, - "entries": [ - { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bfef4953-b3bc-4ea4-a2f6-0d9885c7a2da", - "name": "Spawner (306)", - "location": [1534, 631, -14], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 15, - "entries": [ - { "name": "EarthElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c02c8708-ba44-46e1-9373-c0cce877084f", - "name": "Spawner (306)", - "location": [1647, 602, -14], - "map": "Ilshenar", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "DesertOstard", "maxCount": 5, "probability": 100 }, - { "name": "StoneHarpy", "maxCount": 1, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 2, "probability": 100 }, - { "name": "Efreet", "maxCount": 1, "probability": 100 }, - { "name": "LichLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c1f641e7-bd1c-4706-bc21-1af65e71bbee", - "name": "Spawner (306)", - "location": [1249, 957, -14], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Pixie", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2679185-4de7-41dd-a304-27da19abffb2", - "name": "Spawner (306)", - "location": [911, 502, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 15, - "entries": [ - { "name": "EarthElemental", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2927de7-9e4a-4874-bcb4-857d20ce731b", - "name": "Spawner (306)", - "location": [808, 781, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "GolemController", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c3a2fccf-7467-45b6-8c92-b18ec3463312", - "name": "Spawner (306)", - "location": [1674, 1074, 32], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c565a927-decf-4326-8213-bcd7efb5fc21", - "name": "Spawner (306)", - "location": [1480, 1207, -18], - "map": "Ilshenar", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 70, - "walkingRange": 70, - "entries": [ - { "name": "Pixie", "maxCount": 5, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c6da5392-5d37-43f5-8c50-0331d0bc4190", - "name": "Spawner (306)", - "location": [445, 366, -66], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c73818ae-1121-46ae-972e-b60e6a670889", - "name": "Spawner (306)", - "location": [1856, 517, -14], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Brigand", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ca1241f9-a5d2-432f-a4f5-23505d765970", - "name": "Spawner (306)", - "location": [685, 340, -64], - "map": "Ilshenar", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 125, - "walkingRange": 170, - "entries": [ - { "name": "TropicalBird", "maxCount": 15, "probability": 100 }, - { "name": "Boar", "maxCount": 9, "probability": 100 }, - { "name": "Snake", "maxCount": 9, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 9, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 9, "probability": 100 }, - { "name": "Gorilla", "maxCount": 9, "probability": 100 }, - { "name": "Panther", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cadd8d7c-9d3a-4121-83ae-e2613840894a", - "name": "Spawner (306)", - "location": [302, 1580, -29], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Lizardman", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cae7914f-420c-4bd4-a325-604479484886", - "name": "Spawner (306)", - "location": [325, 1576, -28], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 15, - "entries": [ - { "name": "Lizardman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "caf251aa-ecef-47a2-9671-c040818d4ea6", - "name": "Spawner (306)", - "location": [1095, 638, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "JukaMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cbb2778d-3936-46ea-97eb-f93214859600", - "name": "Spawner (306)", - "location": [1678, 971, 10], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Harpy", "maxCount": 3, "probability": 100 }, - { "name": "StoneHarpy", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cc71a047-fd22-43e8-96bb-953581239b63", - "name": "Spawner (306)", - "location": [822, 1324, -80], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ccbd8610-d346-4117-a518-9df86604bde4", - "name": "Spawner (306)", - "location": [1084, 652, -20], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "GolemController", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd17296a-77d8-4eef-9e9d-c042d03c928b", - "name": "Spawner (306)", - "location": [1088, 637, -60], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "JukaMage", "maxCount": 2, "probability": 100 }, - { "name": "JukaLord", "maxCount": 1, "probability": 100 }, - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cebde1e6-ed55-4160-87b6-acf8934a3778", - "name": "Spawner (306)", - "location": [1580, 538, -14], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 7, - "entries": [ - { "name": "Ratman", "maxCount": 2, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, - { "name": "RatmanMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cfb042de-f9a6-49a0-a18d-7ff3067c9c0a", - "name": "Spawner (306)", - "location": [291, 1313, -25], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d12c9543-0c01-47e6-b619-4cdddaba9f1b", - "name": "Spawner (306)", - "location": [1817, 976, -28], - "map": "Ilshenar", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 45, - "walkingRange": 45, - "entries": [ - { "name": "DreadSpider", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 13, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d26042c9-0119-45e9-a006-0a59d84f338b", - "name": "Spawner (306)", - "location": [278, 1440, 15], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Efreet", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d4fa9bb9-65f4-4e28-a0df-c30a910b37bc", - "name": "Spawner (306)", - "location": [438, 1431, 15], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "FireElemental", "maxCount": 2, "probability": 100 }, - { "name": "AirElemental", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d5cf478e-ed02-4bfb-943c-7b22d9d28b1b", - "name": "Spawner (306)", - "location": [1041, 272, 56], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 25, - "entries": [ - { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, - { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, - { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 }, - { "name": "Skeleton", "maxCount": 1, "probability": 100 }, - { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d6260f64-4918-4f57-a202-75c21906ebb9", - "name": "Spawner (306)", - "location": [449, 264, -57], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "Wyvern", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "DreadSpider", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d7d89534-d330-4b6a-a6b0-83187bf9ff54", - "name": "Spawner (306)", - "location": [1091, 702, -40], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "Golem", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d977c28c-0fde-46fb-ae30-07c36d70f077", - "name": "Spawner (306)", - "location": [722, 1354, -61], - "map": "Ilshenar", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da3f6bf3-177d-4280-bc57-e0f5b0adb651", - "name": "Spawner (306)", - "location": [938, 453, -80], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Imp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da798d63-b7b0-484f-a345-8f4849216de3", - "name": "Spawner (306)", - "location": [1580, 554, -13], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 7, - "entries": [ - { "name": "Ratman", "maxCount": 1, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, - { "name": "RatmanMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc6d3f64-16bf-4643-af4d-d4806ec1fc80", - "name": "Spawner (306)", - "location": [1083, 675, -60], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 15, - "entries": [ - { "name": "Betrayer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd6a1f71-e45b-40c9-ac4c-75cb8a7c2ae3", - "name": "Spawner (306)", - "location": [1360, 888, -21], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 40, - "entries": [ - { "name": "Troll", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ddb61039-cb35-4e01-92f4-fa49f273db3e", - "name": "Spawner (306)", - "location": [961, 924, -37], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 0, - "entries": [ - { "name": "BankerCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e13fea92-e3ac-4e7d-bf3a-05d99b66b057", - "name": "Spawner (306)", - "location": [1083, 968, -38], - "map": "Ilshenar", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "WanderingHealer", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e15d8287-9350-468d-8144-dd3baa906a53", - "name": "Spawner (306)", - "location": [1086, 702, -60], - "map": "Ilshenar", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "GolemController", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e1700d3c-9672-4e00-951d-feb0e20c80db", - "name": "Spawner (306)", - "location": [652, 555, -72], - "map": "Ilshenar", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 75, - "entries": [ - { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, - { "name": "Boar", "maxCount": 2, "probability": 100 }, - { "name": "Snake", "maxCount": 5, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, - { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, - { "name": "Gorilla", "maxCount": 3, "probability": 100 }, - { "name": "Panther", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e3410cc2-aabd-4257-ade9-6866bb429349", - "name": "Spawner (306)", - "location": [385, 421, -62], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e4773cbb-3189-4e17-9f1d-5f0828466171", - "name": "Spawner (306)", - "location": [821, 802, -80], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 17, - "walkingRange": 17, - "entries": [ - { "name": "Quagmire", "maxCount": 1, "probability": 100 }, - { "name": "Doppleganger", "maxCount": 1, "probability": 100 }, - { "name": "ArcaneDaemon", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e671ad18-2aa3-4188-8cbb-71fc3edcf9fb", - "name": "Spawner (306)", - "location": [963, 456, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "ExodusOverseer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e733d3be-2373-466b-a904-c90c1f87156c", - "name": "Spawner (306)", - "location": [314, 1436, 16], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "FireElemental", "maxCount": 1, "probability": 100 }, - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Imp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eaa928d7-cdd8-47cf-97b8-ff8daa37c6f7", - "name": "Spawner (306)", - "location": [1436, 960, -22], - "map": "Ilshenar", - "count": 9, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, - { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, - { "name": "Reaper", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eac70a7f-7a65-43b1-9292-599632f696ae", - "name": "Spawner (306)", - "location": [1445, 681, -1], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Imp", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec64bd53-d037-42d7-941a-aa2a0da52ce7", - "name": "Spawner (306)", - "location": [1095, 664, -80], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "efa8acca-e72d-4672-882b-c27f2506ed03", - "name": "Spawner (306)", - "location": [1060, 652, -40], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 15, - "entries": [ - { "name": "Betrayer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f1220ee4-7be3-40f0-b7eb-11e681dea5ae", - "name": "Spawner (306)", - "location": [907, 780, -80], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f5b5861e-4eb1-4ea6-8013-e04ecf78d1d2", - "name": "Spawner (306)", - "location": [830, 1206, -70], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 0, - "entries": [ - { "name": "MageCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f683a2f2-fab0-4a35-aeed-1d50b9dcf273", - "name": "Spawner (306)", - "location": [1059, 1153, -24], - "map": "Ilshenar", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 15, - "entries": [ - { "name": "RatmanMage", "maxCount": 2, "probability": 100 }, - { "name": "RatmanArcher", "maxCount": 2, "probability": 100 }, - { "name": "Ratman", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fab56f4a-1933-48ac-adb6-d1771228cb00", - "name": "Spawner (306)", - "location": [1083, 651, -40], - "map": "Ilshenar", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Betrayer", "maxCount": 1, "probability": 100 }, - { "name": "GolemController", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fb21b810-7b2c-4fd3-9a87-4dfe0671bb1d", - "name": "Spawner (306)", - "location": [1592, 1191, -20], - "map": "Ilshenar", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 70, - "walkingRange": 70, - "entries": [ - { "name": "Pixie", "maxCount": 5, "probability": 100 }, - { "name": "Horse", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fb648bc8-9c84-4454-9770-4d21d69c0774", - "name": "Spawner (306)", - "location": [1534, 600, -14], - "map": "Ilshenar", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 25, - "entries": [ - { "name": "Shade", "maxCount": 7, "probability": 100 }, - { "name": "Spectre", "maxCount": 7, "probability": 100 }, - { "name": "Wraith", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 }, - { "name": "Ghoul", "maxCount": 2, "probability": 100 }, - { "name": "Zombie", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fbd4d134-ec9e-4642-be28-e1b392df4d52", - "name": "Spawner (306)", - "location": [1717, 606, 17], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 0, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fbf30316-ff51-4001-84f3-d63501306a7f", - "name": "Spawner (306)", - "location": [1319, 1331, -14], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Sheep", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fca9b079-0c6f-4836-9860-39e260c7896d", - "name": "Spawner (306)", - "location": [1790, 435, -10], - "map": "Ilshenar", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 25, - "entries": [ - { "name": "EarthElemental", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fda17c85-19c4-4c90-b848-0c1c135ac5e2", - "name": "Spawner (306)", - "location": [1593, 602, 7], - "map": "Ilshenar", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "DesertOstard", "maxCount": 5, "probability": 100 }, - { "name": "StoneHarpy", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, - { "name": "Wyvern", "maxCount": 1, "probability": 100 }, - { "name": "Efreet", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ff667187-7d72-4573-aa08-064bbe619989", - "name": "Spawner (306)", - "location": [878, 527, -77], - "map": "Ilshenar", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 }, - { "name": "Ghoul", "maxCount": 1, "probability": 100 }, - { "name": "Zombie", "maxCount": 4, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 } - ] - } -] diff --git a/Distribution/Data/Spawns/shared/malas/North.json b/Distribution/Data/Spawns/shared/malas/North.json deleted file mode 100644 index 662d7b71a..000000000 --- a/Distribution/Data/Spawns/shared/malas/North.json +++ /dev/null @@ -1,349 +0,0 @@ -[ - { - "$type": "Spawner", - "guid": "0c628980-8a6f-45e1-bdc8-ed7ea16765fd", - "name": "Spawner (402)", - "location": [2205, 145, -90], - "map": "Malas", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Unicorn", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "135a20a6-0885-4a61-9a55-a01593f3e874", - "name": "Spawner (402)", - "location": [1482, 347, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 60, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2db6869c-b590-4f82-a5eb-edb7afdd18e4", - "name": "Spawner (402)", - "location": [1274, 300, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2efda65d-ebc3-4251-b6a3-356d42cca581", - "name": "Spawner (402)", - "location": [2291, 295, -90], - "map": "Malas", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "GoreFiend", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41a269ac-0492-4a16-bbc3-449c7efa9ef9", - "name": "Spawner (402)", - "location": [2121, 597, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "63d80004-20f7-48d8-bcc8-639a8d83c099", - "name": "Spawner (402)", - "location": [766, 114, -90], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "67b52f00-d808-4db4-91ca-9313330d0b40", - "name": "Spawner (402)", - "location": [1710, 403, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 40, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6ef4d890-5038-4d39-977c-5eb15448543a", - "name": "Spawner (402)", - "location": [1659, 112, -80], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a1e3857a-952a-47c2-9311-d6bcc3806c45", - "name": "Spawner (402)", - "location": [1980, 660, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a7be1139-619a-4d84-a98a-8ea04c550827", - "name": "Spawner (402)", - "location": [1869, 480, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abaa8a2d-9997-4bff-b033-a9dfa78d46f9", - "name": "Spawner (402)", - "location": [2030, 500, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 70, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b48ebb7a-8bfc-4c48-a0ab-f82ca41e38ac", - "name": "Spawner (402)", - "location": [1381, 340, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b85a0fee-612e-4bd9-8489-28fbcdc60ef5", - "name": "Spawner (402)", - "location": [1137, 223, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 40, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "be2f76e8-a03b-42b9-b39c-c641f03ee091", - "name": "Spawner (402)", - "location": [1206, 191, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 40, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dec6f03f-a79d-414a-8b8b-c46af723371a", - "name": "Spawner (402)", - "location": [1587, 386, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 40, - "walkingRange": 40, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e2af6c55-d320-4255-a85f-c694f8d9886b", - "name": "Spawner (402)", - "location": [2233, 555, -90], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e3f940e1-3225-416f-a9ba-717b28fb944a", - "name": "Spawner (402)", - "location": [1023, 192, -50], - "map": "Malas", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Walrus", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 } - ] - } -] diff --git a/Distribution/Data/Spawns/shared/malas/South.json b/Distribution/Data/Spawns/shared/malas/South.json deleted file mode 100644 index f00b0d527..000000000 --- a/Distribution/Data/Spawns/shared/malas/South.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "$type": "Spawner", - "guid": "6bbb82e4-b0d1-4a23-a9da-5d35911ce762", - "name": "Spawner (404)", - "location": [2231, 1605, -95], - "map": "Malas", - "count": 33, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 160, - "walkingRange": 160, - "entries": [ - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "SandVortex", "maxCount": 6, "probability": 100 }, - { "name": "Scorpion", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 }, - { "name": "Spectre", "maxCount": 7, "probability": 100 } - ] - } -] diff --git a/Distribution/Data/Spawns/shared/malas/Vendors.json b/Distribution/Data/Spawns/shared/malas/Vendors.json deleted file mode 100644 index 6e9b73e8d..000000000 --- a/Distribution/Data/Spawns/shared/malas/Vendors.json +++ /dev/null @@ -1,484 +0,0 @@ -[ - { - "$type": "Spawner", - "guid": "0d9c6323-7da9-4e12-861a-76fd30b3e3dc", - "name": "Spawner (405)", - "location": [950, 520, -55], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0db7d5e9-beaa-43a2-ac46-2802ac962fcb", - "name": "Spawner (405)", - "location": [2068, 1372, -75], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d301c77-1ade-4823-b2ff-f1d4e0ed2bf0", - "name": "Spawner (405)", - "location": [1029, 520, -55], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d69c950-70d4-48c5-8729-be8848330c1a", - "name": "Spawner (405)", - "location": [2045, 1397, -90], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3dcbbc92-14ac-4798-8753-41aa3b6b2450", - "name": "Spawner (405)", - "location": [2023, 1379, -80], - "map": "Malas", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54deea77-bc1e-446b-8274-ea23bddb369e", - "name": "Spawner (405)", - "location": [989, 527, -50], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "55fab330-6d2a-4823-a666-3dfa33dceeb6", - "name": "Spawner (405)", - "location": [1977, 1365, -80], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72a7f47d-d4cb-4530-9ba6-3878482279b2", - "name": "Spawner (405)", - "location": [2078, 1327, -80], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "79aa68b8-d2bd-4a4d-ad84-422588aeaaff", - "name": "Spawner (405)", - "location": [1016, 514, -70], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "KeeperOfChivalry", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7a10d380-53e3-48c9-9c5b-07a3c215032a", - "name": "Spawner (405)", - "location": [1027, 494, -70], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7bd799ab-c7fb-446e-b6cb-580d1fe2f7fb", - "name": "Spawner (405)", - "location": [976, 512, -50], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81e4af4e-3310-4864-8517-a032d487716f", - "name": "Spawner (405)", - "location": [1992, 1315, -90], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ed338dc-51c1-40b3-a91a-7ebd8d91131b", - "name": "Spawner (405)", - "location": [2060, 1283, -80], - "map": "Malas", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9347985d-23a4-4044-9a5c-79939c9f7df0", - "name": "Spawner (405)", - "location": [2037, 1311, -85], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "99cde945-5b9c-4cff-924f-a59142bcbdb9", - "name": "Spawner (405)", - "location": [989, 520, -50], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9ad9f47f-17bc-4790-9a84-1e9aee66478f", - "name": "Spawner (405)", - "location": [2011, 1326, -80], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a0c83d13-2354-494f-9535-d4e587cc7a6b", - "name": "Spawner (405)", - "location": [2025, 1387, -80], - "map": "Malas", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a263ccfc-65e1-4181-93b6-6e1c7914f5b8", - "name": "Spawner (405)", - "location": [2048, 1343, -85], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a4200c02-8035-48bc-b79d-8b45225ffd4f", - "name": "Spawner (405)", - "location": [1004, 527, -50], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "HolyMage", "maxCount": 1, "probability": 100 }, - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4f71d7e-8c76-47db-a1c1-b81c4484741b", - "name": "Spawner (405)", - "location": [2027, 1353, -90], - "map": "Malas", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b7226aea-9fd0-4079-8e21-70a7a443d0b2", - "name": "Spawner (405)", - "location": [1056, 1434, -85], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c1bda852-fee4-45ee-aa79-630980bc44e9", - "name": "Spawner (405)", - "location": [1003, 512, -50], - "map": "Malas", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d6de9f30-1ee9-4914-853c-be075bd119ce", - "name": "Spawner (405)", - "location": [2083, 1322, -80], - "map": "Malas", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df531ac9-d2b0-48ae-bc19-dc043d6af3de", - "name": "Spawner (405)", - "location": [993, 511, -50], - "map": "Malas", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 }, - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e08eb38c-0b25-4387-8999-f9d8a0a877aa", - "name": "Spawner (405)", - "location": [961, 517, -70], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "KeeperOfChivalry", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eb50b13f-dd98-42c1-bfa0-2599bdc85de3", - "name": "Spawner (405)", - "location": [2066, 1282, -80], - "map": "Malas", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f62df843-74a7-4bb0-a101-53d1dc4cb806", - "name": "Spawner (405)", - "location": [976, 527, -50], - "map": "Malas", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fa9dbd50-e0ca-4206-a197-0fe85a349900", - "name": "Spawner (405)", - "location": [2017, 1356, -90], - "map": "Malas", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - } -] diff --git a/Distribution/Data/Spawns/shared/trammel/TramCropsLS.json b/Distribution/Data/Spawns/shared/trammel/TramCropsLS.json deleted file mode 100644 index bad02ac02..000000000 --- a/Distribution/Data/Spawns/shared/trammel/TramCropsLS.json +++ /dev/null @@ -1,338 +0,0 @@ -[ - { - "$type": "RegionSpawner", - "guid": "0da29e36-7bff-4fcf-803d-79dd16212653", - "name": "Spawner", - "location": [1216, 1604, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableTurnip", "maxCount": 6, "probability": 100 } - ], - "region": "A Turnip Field in Britain 2" - }, - { - "$type": "RegionSpawner", - "guid": "1025906e-047c-460b-9bb8-581f57ff1357", - "name": "Spawner", - "location": [823, 2274, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCabbage", "maxCount": 6, "probability": 100 } - ], - "region": "A Cabbage Field in Skara Brae 2" - }, - { - "$type": "RegionSpawner", - "guid": "185e475b-36cf-4dad-8776-537e84b15e42", - "name": "Spawner", - "location": [823, 2355, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCotton", "maxCount": 6, "probability": 100 } - ], - "region": "A Cotton Field in Skara Brae" - }, - { - "$type": "RegionSpawner", - "guid": "34b0579c-1fc6-4868-82c5-09138c5e5878", - "name": "Spawner", - "location": [1152, 1576, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 8, "probability": 100 } - ], - "region": "A Wheatfield in Britain 5" - }, - { - "$type": "RegionSpawner", - "guid": "41a583ac-749c-40a7-8658-fb3deb1b222a", - "name": "Spawner", - "location": [823, 2264, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableOnion", "maxCount": 6, "probability": 100 } - ], - "region": "An Onion Field in Skara Brae" - }, - { - "$type": "RegionSpawner", - "guid": "4656ad9d-a455-4217-9316-ec10d10cf68c", - "name": "Spawner", - "location": [1231, 1723, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableOnion", "maxCount": 8, "probability": 100 } - ], - "region": "An Onion Field in Britain 1" - }, - { - "$type": "RegionSpawner", - "guid": "57551e80-0018-4fcf-8b83-9d39ef19b0a6", - "name": "Spawner", - "location": [1215, 1723, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCarrot", "maxCount": 8, "probability": 100 } - ], - "region": "A Carrot Field in Britain 1" - }, - { - "$type": "RegionSpawner", - "guid": "59e99d3b-b727-491b-b0f3-03a441b10c04", - "name": "Spawner", - "location": [843, 2352, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 6, "probability": 100 } - ], - "region": "A Wheatfield in Skara Brae 2" - }, - { - "$type": "RegionSpawner", - "guid": "690ccc1c-2ac8-4f05-8066-695aa53c98aa", - "name": "Spawner", - "location": [675, 1179, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "Sheep", "maxCount": 10, "probability": 100 } - ], - "region": "A Field of Sheep in Yew 2" - }, - { - "$type": "RegionSpawner", - "guid": "69dfbe70-b46c-42b0-9ff1-351fd9651884", - "name": "Spawner", - "location": [4567, 1475, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCotton", "maxCount": 8, "probability": 100 } - ], - "region": "A Cotton Field in Moonglow" - }, - { - "$type": "RegionSpawner", - "guid": "79af53a5-33e2-4fdb-95dd-278a050e54fc", - "name": "Spawner", - "location": [567, 1239, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 5, "probability": 100 } - ], - "region": "A Wheatfield in Yew 1" - }, - { - "$type": "RegionSpawner", - "guid": "8408baaa-0de1-4cb0-ad89-973782e3a565", - "name": "Spawner", - "location": [571, 1099, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "Sheep", "maxCount": 10, "probability": 100 } - ], - "region": "A Farm in Yew" - }, - { - "$type": "RegionSpawner", - "guid": "85cc6fca-4f66-4428-aa3a-347a88523a4b", - "name": "Spawner", - "location": [1120, 1624, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 8, "probability": 100 } - ], - "region": "A Wheatfield in Britain 4" - }, - { - "$type": "RegionSpawner", - "guid": "997d9ff5-ad2b-421b-ae76-6794e7e88150", - "name": "Spawner", - "location": [1183, 1683, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCabbage", "maxCount": 8, "probability": 100 } - ], - "region": "A Cabbage Field in Britain 1" - }, - { - "$type": "RegionSpawner", - "guid": "ab0cb920-b25f-4811-8ef6-938549dbfa27", - "name": "Spawner", - "location": [1232, 1604, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCarrot", "maxCount": 6, "probability": 100 } - ], - "region": "A Carrot Field in Britain 2" - }, - { - "$type": "RegionSpawner", - "guid": "afdfec82-dac3-43df-bd7b-cd2cfad5f265", - "name": "Spawner", - "location": [1135, 1791, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 8, "probability": 100 } - ], - "region": "A Wheatfield in Britain 1" - }, - { - "$type": "RegionSpawner", - "guid": "b17f2f9c-9d36-4bea-87db-69f8effc4926", - "name": "Spawner", - "location": [675, 939, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "Sheep", "maxCount": 10, "probability": 100 } - ], - "region": "A Field of Sheep in Yew 1" - }, - { - "$type": "RegionSpawner", - "guid": "bfb677f7-e824-46f6-95ef-ee5602f4f9c3", - "name": "Spawner", - "location": [1199, 1683, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableTurnip", "maxCount": 8, "probability": 100 } - ], - "region": "A Turnip Field in Britain 1" - }, - { - "$type": "RegionSpawner", - "guid": "c052a5e0-493d-483e-a98a-665cf05c5c14", - "name": "Spawner", - "location": [823, 2254, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCarrot", "maxCount": 6, "probability": 100 } - ], - "region": "A Carrot Field in Skara Brae" - }, - { - "$type": "RegionSpawner", - "guid": "c42ef172-78c8-4c5e-b26c-fcc3fc3da1cc", - "name": "Spawner", - "location": [1199, 1823, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 8, "probability": 100 } - ], - "region": "A Wheatfield in Britain 2" - }, - { - "$type": "RegionSpawner", - "guid": "d5b08634-5c5d-4bc2-b6cd-74cb06132423", - "name": "Spawner", - "location": [1231, 1887, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 8, "probability": 100 } - ], - "region": "A Wheatfield in Britain 3" - }, - { - "$type": "RegionSpawner", - "guid": "df4c5918-b490-4257-9aeb-541b44980b27", - "name": "Spawner", - "location": [823, 2264, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableCabbage", "maxCount": 6, "probability": 100 } - ], - "region": "A Cabbage Field in Skara Brae 1" - }, - { - "$type": "RegionSpawner", - "guid": "ee6692af-82cf-477e-96ec-375be7a23db9", - "name": "Spawner", - "location": [813, 2163, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 6, "probability": 100 } - ], - "region": "A Wheatfield in Skara Brae 1" - }, - { - "$type": "RegionSpawner", - "guid": "fb826650-9fad-43ff-8ba1-9d848f1f26c5", - "name": "Spawner", - "location": [375, 1191, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:00:10", - "maxDelay": "00:00:30", - "entries": [ - { "name": "FarmableWheat", "maxCount": 6, "probability": 100 } - ], - "region": "A Wheatfield in Yew 2" - } -] diff --git a/Distribution/Data/Spawns/shared/trammel/Vendors.json b/Distribution/Data/Spawns/shared/trammel/Vendors.json deleted file mode 100644 index fafd38b70..000000000 --- a/Distribution/Data/Spawns/shared/trammel/Vendors.json +++ /dev/null @@ -1,5271 +0,0 @@ -[ - { - "$type": "Spawner", - "guid": "00a20674-f2f7-4d32-8f99-17333e610528", - "name": "Spawner (125)", - "location": [2954, 705, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Beekeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "01d45bd9-1b11-4740-a5fb-d03ab174b5ab", - "name": "Spawner (125)", - "location": [5297, 4007, 40], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "01e0ae79-27ae-46b3-b4da-484ddf53f534", - "name": "Spawner (125)", - "location": [2967, 3408, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0339e1b7-4167-4d40-bc12-1cb62da96006", - "name": "Spawner (125)", - "location": [1650, 1642, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0402be0f-a2d2-4116-8c36-c7af2c230447", - "name": "Spawner (125)", - "location": [3683, 2171, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0435d4c7-1502-4434-b152-9ed1f6c77cf0", - "name": "Spawner (125)", - "location": [1341, 1733, 20], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "hirefighter", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "043d3956-11cc-4f4f-a014-7752c464e34c", - "name": "Spawner (125)", - "location": [1425, 1690, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0468f726-d595-41a6-94a2-e073f3b30caf", - "name": "Spawner (125)", - "location": [610, 810, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "049e60c9-d17c-4dd7-b41f-72a029d03e08", - "name": "Spawner (125)", - "location": [4392, 1117, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0537535f-e69b-4c83-8eaa-09b27fd71a85", - "name": "Spawner (125)", - "location": [2709, 2130, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0746c0d5-0f6e-4d52-951f-f0e3b39bd19e", - "name": "Spawner (125)", - "location": [1846, 2715, 10], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "07e55a6f-82ae-4f79-b758-f327f002a1cd", - "name": "Spawner (125)", - "location": [3699, 2163, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "09169e10-d045-41c3-9c74-7bca043adb3c", - "name": "Spawner (125)", - "location": [3554, 1202, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0929d639-9f70-42aa-b714-608c783bb2df", - "name": "Spawner (125)", - "location": [3778, 1172, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a9406a9-a389-4415-96f8-a8431e90009e", - "name": "Spawner (125)", - "location": [2880, 3472, 15], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0ba9f3dc-a0df-44d2-bebd-bd26fdd99730", - "name": "Spawner (125)", - "location": [2011, 2804, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0bd547bd-7fe4-4869-bb5c-17a297013e44", - "name": "Spawner (125)", - "location": [2471, 564, 5], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0d2b5765-87c1-48c6-a9a6-27699ea30d72", - "name": "Spawner (125)", - "location": [4417, 1064, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0d673486-6203-4256-9264-e267a7f1bd71", - "name": "Spawner (125)", - "location": [2072, 2856, -3], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f1fe615-63ac-460b-93e6-11eead781649", - "name": "Spawner (125)", - "location": [2461, 457, 15], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f546912-c197-4d33-a1e0-1f4efddb9311", - "name": "Spawner (125)", - "location": [2019, 2748, 30], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12775f86-e464-4ae0-bd2d-e986f9a6452b", - "name": "Spawner (125)", - "location": [586, 2170, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12e7d6c0-93ec-4a46-aa65-03407c9a409c", - "name": "Spawner (125)", - "location": [4394, 1083, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "134c9621-c59d-47ef-9afc-b47e1d99e0bd", - "name": "Spawner (125)", - "location": [2634, 2083, 10], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1373dadc-9178-4022-941f-b204570faf59", - "name": "Spawner (125)", - "location": [1939, 2739, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1647a7af-7960-4b9e-87aa-99e10a89920d", - "name": "Spawner (125)", - "location": [1458, 1525, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1a27bb4d-874d-4821-8ce3-a669d83ee6f1", - "name": "Spawner (125)", - "location": [1409, 1590, 42], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1a9355e3-e45f-43b3-bc0d-2afb86873a1c", - "name": "Spawner (125)", - "location": [1547, 1659, 26], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1b6c520f-c677-481b-9a8e-fa2ab5658a16", - "name": "Spawner (125)", - "location": [2731, 2192, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1b882487-2ce6-4819-8db4-7077c0217394", - "name": "Spawner (125)", - "location": [1504, 3693, -3], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1bc12e56-5192-41a7-9476-6d628159fcf5", - "name": "Spawner (125)", - "location": [2840, 885, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1cd64ef2-a605-44ac-bf6a-54033cbd6093", - "name": "Spawner (125)", - "location": [2905, 3517, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1ea130cc-27cc-4640-8351-976a0858188d", - "name": "Spawner (125)", - "location": [3734, 2149, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1ee02077-d7f2-439e-95b9-f9e82932f319", - "name": "Spawner (125)", - "location": [5203, 86, 5], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f649d8d-7a53-46a1-bdab-64f309d4e6c7", - "name": "Spawner (125)", - "location": [3720, 2126, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1facba9c-8639-4def-bb21-08abc7456e8e", - "name": "Spawner (125)", - "location": [1438, 1761, -2], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 10, - "entries": [ - { "name": "hiresailor", "maxCount": 1, "probability": 100 }, - { "name": "Fisherman", "maxCount": 1, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1fc9ddd3-9975-4095-8daf-8d86e1103009", - "name": "Spawner (125)", - "location": [650, 2178, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22f848cc-0054-4f92-8f4b-d55e59f8c0c9", - "name": "Spawner (125)", - "location": [2920, 856, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2377d397-cf7b-41f0-a761-429f83c4fd93", - "name": "Spawner (125)", - "location": [2972, 3425, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "23f532bd-b465-4865-9e87-1acb1f975bb7", - "name": "Spawner (125)", - "location": [1437, 3750, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "29a793dc-b980-4c98-a362-f90f50121b3b", - "name": "Spawner (125)", - "location": [1485, 1550, 30], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2a13d9bc-849b-4a5b-96b0-0afe64cad4d6", - "name": "Spawner (125)", - "location": [2737, 2250, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2a605fd6-6428-4bd5-9571-ddd030de003c", - "name": "Spawner (125)", - "location": [1395, 3705, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2aebb1a5-2e39-424d-9e9b-e8254a5e307d", - "name": "Spawner (125)", - "location": [1467, 1686, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2b03cbc1-378c-45ab-b01c-95d9e4944647", - "name": "Spawner (125)", - "location": [2659, 2194, 4], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "ThiefGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2b7a7c2b-f82b-4cbc-a83d-f9ff58b87562", - "name": "Spawner (125)", - "location": [2707, 2178, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2bb96113-3a2f-4092-b055-1883f7842b22", - "name": "Spawner (125)", - "location": [1547, 1768, 10], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2d331fdc-c640-4e30-9d51-d8b28977ac69", - "name": "Spawner (125)", - "location": [5752, 3272, 16], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "Tailor", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2d4579b8-5d76-4c53-8bda-3d2c4803f2e9", - "name": "Spawner (125)", - "location": [2992, 3451, 16], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2d9d17a8-7908-4052-b17c-69e9417788cd", - "name": "Spawner (125)", - "location": [570, 2121, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e0e6946-627a-4f89-b9d4-795be9b51694", - "name": "Spawner (125)", - "location": [1935, 2796, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e2553ce-54f2-4cb4-9e20-9625257f4192", - "name": "Spawner (125)", - "location": [3774, 1265, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e88a973-4c4b-46fc-8012-0e5484272dc8", - "name": "Spawner (125)", - "location": [3017, 761, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Farmer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "308f6ffb-7fb6-4f92-85be-56d1756e5823", - "name": "Spawner (125)", - "location": [5669, 3131, 14], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Minter", "maxCount": 1, "probability": 100 }, - { "name": "Banker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "31da09ce-311f-409b-9c68-34b8058df581", - "name": "Spawner (125)", - "location": [1498, 1659, 27], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32655bec-db85-4a3e-95b4-c21dcf576d0a", - "name": "Spawner (125)", - "location": [1434, 1717, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Merchant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "327a996b-05f3-4523-b029-90d7c4984a31", - "name": "Spawner (125)", - "location": [1602, 1712, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3330ffe8-07f8-4a7d-9515-34e5333dfe48", - "name": "Spawner (125)", - "location": [1355, 1754, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 14, - "entries": [ - { "name": "OrderGuard", "maxCount": 1, "probability": 100 }, - { "name": "hirefighter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "335880dc-a64c-488d-98c6-cadf4f8bca54", - "name": "Spawner (125)", - "location": [1470, 1765, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33818055-2577-4e1c-ad37-a47f989f925f", - "name": "Spawner (125)", - "location": [2917, 798, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33929eec-3fe3-4b78-978f-f1bef78a28eb", - "name": "Spawner (125)", - "location": [1306, 1804, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 6, - "entries": [ - { "name": "OrderGuard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "35d52dec-9192-40c7-b65d-bd9c41e87cdb", - "name": "Spawner (125)", - "location": [578, 2227, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "360bf7a9-be1c-453a-b74a-3d05f91508eb", - "name": "Spawner (125)", - "location": [1585, 1591, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "360da1b6-8e1d-4afa-aa0a-b58ae555157f", - "name": "Spawner (125)", - "location": [1844, 2735, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "36841b9f-477e-49d9-88a3-d66da91aaef4", - "name": "Spawner (125)", - "location": [3546, 1185, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "36a0853e-86e1-4024-9ef6-efa398ae07c3", - "name": "Spawner (125)", - "location": [5218, 175, 5], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3738191d-80e0-44da-936a-6a7ede0035a2", - "name": "Spawner (125)", - "location": [1456, 3850, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3803febb-c2fc-4d51-a5cd-6faee0570a6a", - "name": "Spawner (125)", - "location": [2438, 410, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "39f529fe-2424-4e3f-9dc3-13a0e1f979f1", - "name": "Spawner (125)", - "location": [5296, 3978, 37], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a2fbed0-d494-4930-8238-a74b2489e23a", - "name": "Spawner (125)", - "location": [2245, 1231, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d9b805c-a0d7-4a4d-82a9-49227a879550", - "name": "Spawner (125)", - "location": [1425, 3981, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3dc2017e-65fa-4231-9fc7-b1aa7c48f1b5", - "name": "Spawner (125)", - "location": [3764, 1317, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ded253d-b661-4582-a15d-79282c15e8e8", - "name": "Spawner (125)", - "location": [3555, 1171, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ef48e85-8383-43c1-806e-66855121aac7", - "name": "Spawner (125)", - "location": [2962, 885, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40c31da4-105d-4393-81d1-37b1e0e3e540", - "name": "Spawner (125)", - "location": [5191, 3989, 37], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40f86110-4700-404e-865a-0624ee3e88df", - "name": "Spawner (125)", - "location": [1493, 1616, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41161b38-3f7c-478f-92c2-53f0dd3e9dad", - "name": "Spawner (125)", - "location": [1620, 1586, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4122c08b-45c7-46ae-8be2-f26a761c57c6", - "name": "Spawner (125)", - "location": [3703, 2249, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "419c9bd7-ce77-4ef9-bdd6-2a913c93534b", - "name": "Spawner (125)", - "location": [1450, 4026, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41cdd533-ce2e-4c8b-9f9b-7467f6d27778", - "name": "Spawner (125)", - "location": [2899, 790, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "42088948-8d4a-4745-8ecc-b0f6a6f5f5db", - "name": "Spawner (125)", - "location": [1474, 1597, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "43174d70-989d-4a19-a02b-e39ffab8619c", - "name": "Spawner (125)", - "location": [4416, 1133, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "44a0c6b8-5b7d-486e-b373-affb981dcdd5", - "name": "Spawner (125)", - "location": [5737, 3222, 11], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Healer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "44c7bc31-c445-42e3-afa6-e86d6beac11b", - "name": "Spawner (125)", - "location": [1475, 3865, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "450a0f16-a428-4b13-9bea-7c1f0398f4dd", - "name": "Spawner (125)", - "location": [5351, 56, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "463cd51f-8053-4bdb-9b1f-1ec4d7fb1813", - "name": "Spawner (125)", - "location": [5731, 3192, 9], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "Mage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "469ffd2c-d656-45ca-89c5-f0a7fedf6176", - "name": "Spawner (125)", - "location": [1436, 1752, 14], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "HarborMaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "46e1f663-0c59-4a2d-8f66-00f2a0d5195e", - "name": "Spawner (125)", - "location": [4416, 1085, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "472a07d2-cb45-4ca4-9e2d-4153faae4d0e", - "name": "Spawner (125)", - "location": [1427, 1726, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "hirebard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "476e8e87-d2f3-4964-9bdd-3e11498cd362", - "name": "Spawner (125)", - "location": [5746, 3200, 16], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47b0d916-8f93-4580-b4c1-b9408b377a8b", - "name": "Spawner (125)", - "location": [1405, 1810, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47d6019a-a955-4f5a-bcff-da5d6cb6acea", - "name": "Spawner (125)", - "location": [1125, 3687, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "482cd8b9-c3af-4b1c-9154-f722aa67b0ed", - "name": "Spawner (125)", - "location": [3727, 2224, 20], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "485462f4-cbbc-4a46-aa31-aa7b42832509", - "name": "Spawner (125)", - "location": [2906, 3485, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "48829893-5b5f-4822-a43c-a2a05549f7ca", - "name": "Spawner (125)", - "location": [2865, 852, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49c6d37a-7560-4977-bcbe-045801c60af8", - "name": "Spawner (125)", - "location": [624, 1145, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49dd6a0d-15dc-4eff-9f1a-9f8af38fe15e", - "name": "Spawner (125)", - "location": [3051, 3367, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b4c90a7-f7e7-41a8-a4f1-0e37591c1cd9", - "name": "Spawner (125)", - "location": [1419, 3859, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b5a2097-735a-4740-8b6a-87f079705d42", - "name": "Spawner (125)", - "location": [652, 820, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b65546f-275c-45fd-9d0f-46e6639237bc", - "name": "Spawner (125)", - "location": [5215, 117, 1], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4bc9a4dd-1844-483d-997d-e73e53279198", - "name": "Spawner (125)", - "location": [2627, 2100, 10], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4cdd8db5-8499-4956-b094-1e77596039fe", - "name": "Spawner (125)", - "location": [5736, 3263, 15], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Cobbler", "maxCount": 1, "probability": 100 }, - { "name": "Provisioner", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d000fba-9496-45e5-9ba0-93e2ad003206", - "name": "Spawner (125)", - "location": [5698, 3282, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d24ec90-79b5-4881-bcd6-568034cd3e2a", - "name": "Spawner (125)", - "location": [1360, 3815, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4e0c3ef6-5714-4bc8-8d0f-8cad7008c2b3", - "name": "Spawner (125)", - "location": [1447, 3981, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4eec52f5-a2ee-41fe-b83d-e4ef31e59cc4", - "name": "Spawner (125)", - "location": [1454, 4003, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4fa0e5cb-22de-46a4-9620-4f7103685d53", - "name": "Spawner (125)", - "location": [1477, 1741, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "hiresailor", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4ffe4319-70b0-48c5-bb98-c3bf957662f0", - "name": "Spawner (125)", - "location": [630, 2194, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50090b05-cdf5-4e5f-994a-7f4d6b7eb59e", - "name": "Spawner (125)", - "location": [620, 2218, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "507f2688-b5dd-404d-b31b-50b0914c0e58", - "name": "Spawner (125)", - "location": [1228, 1572, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 10, - "entries": [ - { "name": "Farmer", "maxCount": 1, "probability": 100 }, - { "name": "Rancher", "maxCount": 1, "probability": 100 }, - { "name": "hirepeasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50b0e650-9163-4263-a71a-3912aa87b5c7", - "name": "Spawner (125)", - "location": [3665, 2140, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "52358248-3191-4dee-9e78-ea64d8370189", - "name": "Spawner (125)", - "location": [2526, 546, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "523e5f93-afd6-466e-bb27-db94c6bf7e0a", - "name": "Spawner (125)", - "location": [1935, 2766, 10], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54232896-d8a8-41ef-964e-1a8a426be590", - "name": "Spawner (125)", - "location": [1150, 1619, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 10, - "entries": [ - { "name": "Farmer", "maxCount": 1, "probability": 100 }, - { "name": "Rancher", "maxCount": 1, "probability": 100 }, - { "name": "hirepeasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54250593-bd68-4c4d-9017-593ea8a93c8d", - "name": "Spawner (125)", - "location": [1372, 3908, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "558ec561-9e56-4d94-8dc0-c86161bdb6a4", - "name": "Spawner (125)", - "location": [1685, 2985, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 1675, "y": 2975, "z": 0 }, - "end": { "x": 1695, "y": 2995, "z": 35 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Herbalist", "maxCount": 2, "probability": 100 }, - { "name": "Mage", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5635ef7b-3e30-4f77-9dcc-1d32e90756b9", - "name": "Spawner (125)", - "location": [1650, 1608, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5652fc2b-5bb0-4f6b-b01c-fa8a302ad4d3", - "name": "Spawner (125)", - "location": [1482, 1761, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "57ed6a02-61ea-4190-bdb9-ea72f43675a6", - "name": "Spawner (125)", - "location": [659, 2141, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "586454ef-4cc0-49a4-a2e4-89574c326a66", - "name": "Spawner (125)", - "location": [1449, 1723, 6], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "58876bbe-786f-496c-a9ad-de7150daedbe", - "name": "Spawner (125)", - "location": [650, 2161, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 }, - { "name": "Armorer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "597858d7-3494-465b-8ee1-6bd72419b247", - "name": "Spawner (125)", - "location": [2860, 999, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5bcefd37-a195-42d2-bb43-7087fa46c165", - "name": "Spawner (125)", - "location": [2455, 487, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5c458af7-1675-41b8-b95d-33a9a747b0a4", - "name": "Spawner (125)", - "location": [631, 1034, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5c623567-93cd-4602-82a0-d110513635db", - "name": "Spawner (125)", - "location": [4481, 1085, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Farmer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5cc0651f-cc23-464c-8dd2-8c924688e76f", - "name": "Spawner (125)", - "location": [4392, 1068, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5ce35259-7589-479c-8194-8ef26ee0bc9a", - "name": "Spawner (125)", - "location": [620, 2218, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5cecf5b2-1161-47d8-9098-c4158222a0bb", - "name": "Spawner (125)", - "location": [553, 985, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d3324c6-7226-45eb-af13-35bb32a227ac", - "name": "Spawner (125)", - "location": [1351, 1816, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "EscortableMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d4c103e-b3cc-44c6-81a5-a0ec9472d14f", - "name": "Spawner (125)", - "location": [2940, 3500, 10], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d5d05fc-b9de-40cc-b2d8-015e97a4904a", - "name": "Spawner (125)", - "location": [5148, 60, 25], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5df6d5cf-b7d5-4b51-a52e-a0788d7fc21d", - "name": "Spawner (125)", - "location": [3031, 3350, 35], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 3026, "y": 3345, "z": 35 }, - "end": { "x": 3036, "y": 3355, "z": 50 } - }, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5f4a1c5c-64b3-4636-9067-b309c48b93d3", - "name": "Spawner (125)", - "location": [1427, 1716, 20], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "61d32da8-8cac-4113-8936-85f8836fb6f3", - "name": "Spawner (125)", - "location": [2478, 427, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "62c4349d-d176-4b3d-8df5-c8b624a55423", - "name": "Spawner (125)", - "location": [1416, 1754, 10], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "62d67d9c-82af-4b44-be9a-f1ac7ea02274", - "name": "Spawner (125)", - "location": [3674, 2289, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "643c0e84-5e3f-40db-ac86-3c17c38a316f", - "name": "Spawner (125)", - "location": [1447, 1647, 10], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "650d8ee5-71d1-415f-af77-e051d754adf5", - "name": "Spawner (125)", - "location": [2961, 621, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "66d3b52d-23e0-4d3f-8c93-17a81be068ef", - "name": "Spawner (125)", - "location": [3018, 3426, 15], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6709b04a-f462-468e-b5f5-c8cb8088f0bf", - "name": "Spawner (125)", - "location": [592, 2277, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "677fe1ff-5c29-4d9b-86f2-01f8f6676500", - "name": "Spawner (125)", - "location": [2987, 777, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "68888d32-23aa-4e23-9295-3eaf1e026e16", - "name": "Spawner (125)", - "location": [2835, 805, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bb19a0b-8193-4146-bada-96ef1f69c2c9", - "name": "Spawner (125)", - "location": [1852, 2831, 10], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6c34b2d6-d0d5-4eb1-a0c2-53132b5e7b06", - "name": "Spawner (125)", - "location": [3683, 2252, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6c7d0542-d02a-49e2-b03d-2f27b68074e8", - "name": "Spawner (125)", - "location": [1452, 3747, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6ce9596e-45b3-4eaa-be62-885d94134085", - "name": "Spawner (125)", - "location": [3007, 3408, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6d156639-7f49-432e-a874-c01be5b54a7f", - "name": "Spawner (125)", - "location": [590, 2205, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "70a16a36-196a-48ef-b3ea-1e88ff8a7080", - "name": "Spawner (125)", - "location": [2881, 684, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "71f6509c-73fc-4b66-97e0-73117fa77c0c", - "name": "Spawner (125)", - "location": [1450, 1617, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "73b5f8a6-6c6d-4837-b3ca-7ff7e90ff27d", - "name": "Spawner (125)", - "location": [4471, 1156, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "73b991d6-dce0-42b7-90dd-dd8cc7671bed", - "name": "Spawner (125)", - "location": [5242, 180, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "741b669a-ffda-4fcf-8617-207ce5be1530", - "name": "Spawner (125)", - "location": [2918, 672, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76b3a8d0-aa01-4956-bd37-e2bcd462e8e6", - "name": "Spawner (125)", - "location": [3661, 2183, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7753854c-8437-457c-970f-5489505ba4c6", - "name": "Spawner (125)", - "location": [5236, 142, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "78237184-1c64-4e47-962b-f8ec0ee4a823", - "name": "Spawner (125)", - "location": [564, 1011, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7829831c-f4e0-44a1-a2a9-e04196a72573", - "name": "Spawner (125)", - "location": [2986, 637, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "79424b72-3212-491e-afc1-ad1df19708d7", - "name": "Spawner (125)", - "location": [5234, 4025, 37], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c18a846-5733-4274-969d-c219443a8c95", - "name": "Spawner (125)", - "location": [4448, 1090, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7e0a225e-8ab0-443c-b125-ab3ad0956662", - "name": "Spawner (125)", - "location": [2533, 572, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7e447298-7816-42f1-889a-cf6c8423fb58", - "name": "Spawner (125)", - "location": [4484, 1064, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "808ba57f-46db-4659-b20c-52b61d520894", - "name": "Spawner (125)", - "location": [2216, 1192, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "813713e2-fce3-4dd5-b514-b0bfd0ff76a9", - "name": "Spawner (125)", - "location": [529, 1008, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81515819-c2a9-4db8-b752-9f06685593aa", - "name": "Spawner (125)", - "location": [5161, 32, 17], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "82e5da86-576b-4065-9dc1-b79b154de3fb", - "name": "Spawner (125)", - "location": [2901, 908, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "839452cf-a264-4ccc-983b-6cd13565676d", - "name": "Spawner (125)", - "location": [1243, 1699, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Farmer", "maxCount": 1, "probability": 100 }, - { "name": "Rancher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "843d989e-d401-464a-90b0-16b3469cb226", - "name": "Spawner (125)", - "location": [601, 2235, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8562df8d-5b04-4426-8f62-4125b0bb81c8", - "name": "Spawner (125)", - "location": [1417, 1578, 30], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "85bf65b2-ed34-4719-bbca-e9449e8c1989", - "name": "Spawner (125)", - "location": [2505, 432, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "85c7a253-36a4-4352-b90e-b5e28c8dac8c", - "name": "Spawner (125)", - "location": [4458, 1060, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "860c7844-79ed-4155-aa2d-1b40e99ffafb", - "name": "Spawner (125)", - "location": [2861, 812, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "875e998f-beb1-40f4-aae2-ee36279337b5", - "name": "Spawner (125)", - "location": [535, 867, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "890e5e81-c5da-43c4-a7f1-deb55e5e8b9b", - "name": "Spawner (125)", - "location": [1991, 2867, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8a091648-0378-4b58-879b-b07aee75b351", - "name": "Spawner (125)", - "location": [3007, 3408, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8b249196-07e0-42de-a67e-d6959d1ca4c9", - "name": "Spawner (125)", - "location": [1231, 1714, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "hirepeasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8b43faac-d666-46c1-ba1c-de0ff4a92dad", - "name": "Spawner (125)", - "location": [2002, 2721, 30], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8c6bc394-02b5-4830-8848-55fbb6623a7a", - "name": "Spawner (125)", - "location": [1481, 1584, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d1a1d0f-e8be-4947-90c6-e9611a3c86d7", - "name": "Spawner (125)", - "location": [3666, 2235, 20], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d69a466-479b-4419-98df-ad54be6244cc", - "name": "Spawner (125)", - "location": [1362, 1574, 30], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8e109199-da2b-4155-9890-9a0424be41cf", - "name": "Spawner (125)", - "location": [5805, 3300, 11], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 }, - { "name": "Armorer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8e9d01be-2213-4fa8-b1f6-e6e82ed4983c", - "name": "Spawner (125)", - "location": [3013, 3353, 15], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ede9690-36bd-4b72-a43b-fccbdcc8f56c", - "name": "Spawner (125)", - "location": [5219, 4012, 37], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "904e35eb-9ad9-433b-8717-cc1984111fb8", - "name": "Spawner (125)", - "location": [5671, 3287, 11], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "90e83279-5d20-4b5e-9352-e019cd461edd", - "name": "Spawner (125)", - "location": [2782, 969, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "91c818b9-32b8-4d84-b2cc-ada32cbad1ff", - "name": "Spawner (125)", - "location": [4395, 1137, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "928e9340-fb04-4d78-94f9-8ec141fe6fb2", - "name": "Spawner (125)", - "location": [1316, 1747, 10], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "hirefighter", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "92c0ad53-3da1-446d-a39e-86131211cbb3", - "name": "Spawner (125)", - "location": [1442, 3802, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "92f199ed-e0d1-496f-a1f3-d6aa2a5e3155", - "name": "Spawner (125)", - "location": [2940, 3410, 1], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93eaa98d-0b9f-42b3-91e8-7c04cb15f971", - "name": "Spawner (125)", - "location": [5775, 3161, 14], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "942aa3c4-39fc-4c2c-83cd-31336753d2e8", - "name": "Spawner (125)", - "location": [1435, 3820, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94408e1a-950c-424f-8d22-e75005e1b85e", - "name": "Spawner (125)", - "location": [4409, 1111, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94a33c3d-2fd0-4c9a-8424-b0315ddb023c", - "name": "Spawner (125)", - "location": [5662, 3150, 13], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94f8f998-08d7-453a-8dc0-669e4e8ed554", - "name": "Spawner (125)", - "location": [3008, 3389, 15], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "956834dc-651a-42d7-974b-b2bac74e903e", - "name": "Spawner (125)", - "location": [2752, 2155, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95eaf1c9-9b1e-4f08-845d-cf1fba01401e", - "name": "Spawner (125)", - "location": [1897, 2684, 10], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "978c26c6-dbea-40db-8422-e0342c0738bf", - "name": "Spawner (125)", - "location": [582, 2186, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98464fd7-e5b6-4caa-a6a2-9164399191e6", - "name": "Spawner (125)", - "location": [1452, 3770, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98826ae5-c33c-45be-a99a-4463a5c10916", - "name": "Spawner (125)", - "location": [5263, 129, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98ab2774-ef8a-402a-805b-fd3f035f0458", - "name": "Spawner (125)", - "location": [1388, 1588, 30], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9917a0c7-7df7-4ebc-a830-c9fe30b20354", - "name": "Spawner (125)", - "location": [4401, 1165, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "991ca64d-4d48-49d8-a2a0-71a9d23fb2b3", - "name": "Spawner (125)", - "location": [1388, 1655, 30], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9936a583-3fc5-4414-b85a-94fe319831b8", - "name": "Spawner (125)", - "location": [3755, 2227, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9a226f47-4b27-48ad-8786-77834d26479d", - "name": "Spawner (125)", - "location": [2450, 428, 15], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9bdbb8fe-1b34-49c4-9cc2-6e69ad698018", - "name": "Spawner (125)", - "location": [1850, 2796, -8], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9bded978-79fc-4f1e-9360-de487896a391", - "name": "Spawner (125)", - "location": [570, 969, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c0ac700-6c3d-4025-93cf-6b67e28b80ae", - "name": "Spawner (125)", - "location": [1897, 2805, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9ca7daed-db6e-487c-a7b0-ea5448dd368c", - "name": "Spawner (125)", - "location": [1349, 1778, 15], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Blacksmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9cd4bc8c-4f9a-4a91-9cee-70101c3804d8", - "name": "Spawner (125)", - "location": [1822, 2739, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9f0eb4c9-1e11-4abf-9459-31f25308fe70", - "name": "Spawner (125)", - "location": [1362, 1825, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 12, - "entries": [ - { "name": "Artist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9faa97bf-28e2-4e7a-82a4-0e75f227759b", - "name": "Spawner (125)", - "location": [1911, 2805, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a0464876-3bdb-466d-a5a8-40f63301fafd", - "name": "Spawner (125)", - "location": [5839, 3256, 2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a20fa086-8694-465b-a8bc-0988b5017b61", - "name": "Spawner (125)", - "location": [3009, 3455, 15], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 }, - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a301e23d-1bfd-46ef-a2fc-dd4ca9756255", - "name": "Spawner (125)", - "location": [1430, 1597, 20], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a318c022-1463-466e-95d1-e59b2cde9a63", - "name": "Spawner (125)", - "location": [1416, 3779, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a4040d64-c962-4fac-b73f-10a0c04cb4b8", - "name": "Spawner (125)", - "location": [5300, 90, 15], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a48baab3-ef4a-428b-849d-f96b7fe2270c", - "name": "Spawner (125)", - "location": [1431, 1612, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a604ee94-149c-45ed-8f2e-b95904ea6550", - "name": "Spawner (125)", - "location": [2033, 2801, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a620a8cf-1b8e-4f4b-8485-1a70f6160ed8", - "name": "Spawner (125)", - "location": [554, 2178, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a79cb65c-847a-4b9c-b544-48de9bd6d103", - "name": "Spawner (125)", - "location": [2256, 1181, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa5b770c-5cf3-4c34-bf04-b569d2997239", - "name": "Spawner (125)", - "location": [3546, 1194, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aafefa52-d26c-4735-a2bc-b73bd8285db1", - "name": "Spawner (125)", - "location": [4440, 1162, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abda8289-add2-4cd0-97f9-3b5c2f54721e", - "name": "Spawner (125)", - "location": [643, 1083, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad2a2a35-d46f-4b26-b787-236d6e956cd5", - "name": "Spawner (125)", - "location": [5714, 3202, 12], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "Herbalist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "adb4bd49-a4b7-48da-bd26-527aa960c988", - "name": "Spawner (125)", - "location": [1637, 1693, 37], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "adcf36e0-59c9-4804-8c64-8ac46e1dc0b4", - "name": "Spawner (125)", - "location": [1333, 1808, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "escortablewanderinghealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae039b67-65da-48a1-9d66-397f7f963c24", - "name": "Spawner (125)", - "location": [2424, 555, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bard", "maxCount": 1, "probability": 100 }, - { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae951a02-cc60-40f1-b810-9f09c34b4b7a", - "name": "Spawner (125)", - "location": [3729, 1258, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b370875b-ec9e-4655-9b0d-a2d8d8055302", - "name": "Spawner (125)", - "location": [5275, 3977, 37], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4166ae1-15e1-4540-a3ef-c08ab843c639", - "name": "Spawner (125)", - "location": [3731, 1320, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b434162b-e02d-4bf9-88ee-2dda13e9278a", - "name": "Spawner (125)", - "location": [2522, 524, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b46e3bb9-2dba-4235-9d9a-e9a40e1b142a", - "name": "Spawner (125)", - "location": [1498, 1691, 20], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b61ff7bc-b168-475d-9b08-b36ac604db8e", - "name": "Spawner (125)", - "location": [602, 2180, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b64d4434-2246-4ffa-91a0-600326c097ac", - "name": "Spawner (125)", - "location": [3003, 3355, 15], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b65a0806-7fee-4836-9e8e-e56e2d9d5f09", - "name": "Spawner (125)", - "location": [5691, 3209, 11], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "Carpenter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b6c600be-26e6-4553-bd5b-17986a50c0ef", - "name": "Spawner (125)", - "location": [1422, 1654, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b907af34-1c6f-4533-92f4-65f9b68f7875", - "name": "Spawner (125)", - "location": [3036, 3464, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9b981d8-c473-411a-8655-19c74ef5fbe5", - "name": "Spawner (125)", - "location": [3687, 2227, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba540c4e-a870-4886-90ba-d6c2f466d287", - "name": "Spawner (125)", - "location": [2475, 397, 15], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bb4405b2-a53a-4e60-a659-98ce20fc2e6a", - "name": "Spawner (125)", - "location": [2995, 815, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bc9dea07-ca90-459f-bec2-b2efa4ddf507", - "name": "Spawner (125)", - "location": [1895, 2653, 10], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd48eb05-e200-4fc5-aa5d-8581121d0d79", - "name": "Spawner (125)", - "location": [4545, 860, 30], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd67f242-2044-4c4f-b937-9fcad53ea655", - "name": "Spawner (125)", - "location": [540, 966, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf49484f-4281-4b52-bbd7-786dbcecdf9f", - "name": "Spawner (125)", - "location": [2841, 907, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf770c7b-4a88-4198-b78f-a83eff9d5410", - "name": "Spawner (125)", - "location": [2997, 3428, 15], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bfd3ed22-e5b3-4203-8fb2-db2587c1f196", - "name": "Spawner (125)", - "location": [2026, 2845, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bfdfa0ef-f98a-4095-ad75-c87737eb95a2", - "name": "Spawner (125)", - "location": [2216, 1167, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bff8e072-e174-4aa7-93a8-e5e61b4d957f", - "name": "Spawner (125)", - "location": [5726, 3243, 16], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Tinker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0c400ce-9cdc-401a-8985-84dc26849dec", - "name": "Spawner (125)", - "location": [1364, 3732, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0dd0b6c-633d-426a-8c36-3c2853ecd252", - "name": "Spawner (125)", - "location": [1295, 1773, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "BrideGroom", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c193256a-99a5-44a6-934b-d2996199c5e5", - "name": "Spawner (125)", - "location": [3058, 3400, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c1d8091b-483b-4892-a4b6-e9b5f8ad167c", - "name": "Spawner (125)", - "location": [656, 2235, -3], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2c011c1-e49f-4005-86e9-ffec9a526573", - "name": "Spawner (125)", - "location": [1304, 1826, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "Peasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c4ea485b-f871-4c9b-a809-33b53ceb78c2", - "name": "Spawner (125)", - "location": [3013, 3353, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c5e06b77-5783-4d11-8468-2759e524524d", - "name": "Spawner (125)", - "location": [1470, 1578, 20], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c937726b-6b62-4b10-8598-9393b597ef90", - "name": "Spawner (125)", - "location": [1296, 1759, 10], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ca419471-6da0-47e1-88f0-dcbffd67e416", - "name": "Spawner (125)", - "location": [2963, 813, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cbb75550-e870-42fe-9689-d99120baf85b", - "name": "Spawner (125)", - "location": [1149, 1547, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Farmer", "maxCount": 1, "probability": 100 }, - { "name": "Rancher", "maxCount": 1, "probability": 100 }, - { "name": "hirepeasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cc884f40-cdb6-4c6d-8be6-c9d15216dc51", - "name": "Spawner (125)", - "location": [3545, 1178, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ced2b237-e589-49f6-9468-9c6b5bbb8ca4", - "name": "Spawner (125)", - "location": [1455, 1557, 30], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bard", "maxCount": 1, "probability": 100 }, - { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cfed240a-6345-42ad-a99b-8fa6b48d519c", - "name": "Spawner (125)", - "location": [1880, 2802, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d033238c-2772-4748-b09d-e03dd3a18768", - "name": "Spawner (125)", - "location": [1387, 3771, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d114445e-e1ae-41b0-890e-49b568c65f68", - "name": "Spawner (125)", - "location": [1442, 3802, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d1dbe32d-b712-4b26-8c3c-f34e94ffeeb2", - "name": "Spawner (125)", - "location": [1471, 1611, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d2ae2226-0a3a-4bef-9683-cffb0c59975f", - "name": "Spawner (125)", - "location": [1510, 1543, 25], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3a3af04-33a1-478f-856d-210fdbb5a9ce", - "name": "Spawner (125)", - "location": [1418, 1547, 30], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3b2922a-82a9-4c3f-ae87-d026e4865db8", - "name": "Spawner (125)", - "location": [1990, 2889, 5], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d40aef76-3174-45a1-bf02-b7a5ae236688", - "name": "Spawner (125)", - "location": [3012, 780, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Farmer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d45157f9-2bf0-4101-8e11-a06255f0f3bf", - "name": "Spawner (125)", - "location": [1190, 1707, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 8, - "entries": [ - { "name": "Farmer", "maxCount": 1, "probability": 100 }, - { "name": "Rancher", "maxCount": 1, "probability": 100 }, - { "name": "hirepeasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d6232f09-ad45-49a6-ba0b-b4244c844d3b", - "name": "Spawner (125)", - "location": [2992, 844, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d63ea5ae-32d5-48c0-a9b8-4706a9cd25a7", - "name": "Spawner (125)", - "location": [611, 2157, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Farmer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d94255da-ddc6-4df5-af70-9de05ab71faa", - "name": "Spawner (125)", - "location": [3740, 1197, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9c8783d-5147-4cc5-b2b7-0d0942297fcb", - "name": "Spawner (125)", - "location": [2975, 3353, 15], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9fe1303-74c5-490c-9d08-e43325425919", - "name": "Spawner (125)", - "location": [3667, 2252, 20], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da93faab-336b-403b-8628-da3da7457f7f", - "name": "Spawner (125)", - "location": [1356, 3780, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "db27e125-6f28-4335-a94d-ec1a349a9aba", - "name": "Spawner (125)", - "location": [3699, 2218, 20], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dbd3af4a-dd6a-4ea9-b79b-ea071b77c495", - "name": "Spawner (125)", - "location": [2855, 734, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dbd51949-6b2b-4973-8e5f-67aa1504d28b", - "name": "Spawner (125)", - "location": [1451, 1679, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc73fe29-ef87-477e-9390-666aac11a2fa", - "name": "Spawner (125)", - "location": [1813, 2825, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc7ab28c-9717-4c5d-938c-d6ea01440de7", - "name": "Spawner (125)", - "location": [1386, 3825, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Farmer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dced8f77-228f-4246-9b0a-96539575a2b0", - "name": "Spawner (125)", - "location": [1590, 1654, 10], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd4f9c92-cb01-4d9b-bcfe-2b4e397c3c10", - "name": "Spawner (125)", - "location": [5805, 3270, 11], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mapmaker", "maxCount": 1, "probability": 100 }, - { "name": "Shipwright", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd5f20aa-6865-4e9c-91dc-1ab598efedc4", - "name": "Spawner (125)", - "location": [627, 2163, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "de025061-4683-42e7-aaae-a864a81ea5a9", - "name": "Spawner (125)", - "location": [3769, 1218, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "de7a0b5f-a21f-4e3a-871c-648aa7100c20", - "name": "Spawner (125)", - "location": [587, 2146, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df077d96-f345-4d80-bee5-4ffad8a25c41", - "name": "Spawner (125)", - "location": [2883, 3502, 10], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e00042e4-6be0-4f04-8fc8-56c739a585d6", - "name": "Spawner (125)", - "location": [5154, 97, 5], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e07cf894-eb63-4d4b-9096-e57bed868aca", - "name": "Spawner (125)", - "location": [1469, 1668, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e2150f1f-5cd9-4d5f-a340-7f4bfd0b3214", - "name": "Spawner (125)", - "location": [1404, 3802, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e2bd9c74-593e-453e-9ae1-d8bd4fa67993", - "name": "Spawner (125)", - "location": [2513, 477, 15], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e2fff56f-b197-45be-a68c-af29db75338f", - "name": "Spawner (125)", - "location": [1354, 3754, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e32d662c-a5e3-4549-b2ff-4ec7a76d6961", - "name": "Spawner (125)", - "location": [5197, 4060, 37], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e3eb97de-3c51-4ac5-9e7d-e34818423213", - "name": "Spawner (125)", - "location": [3034, 827, -3], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e6a14d4b-f7c8-427e-a0c8-a1d972e3f3f6", - "name": "Spawner (125)", - "location": [1317, 3773, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e70df5a8-1ca9-41a6-beb7-0c13ab7f2884", - "name": "Spawner (125)", - "location": [5347, 76, 25], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e87996f7-c1cc-46e1-a157-854b1049fa7e", - "name": "Spawner (125)", - "location": [3700, 1214, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ea124c6c-817e-49cd-9d27-567a818cd051", - "name": "Spawner (125)", - "location": [5306, 37, 40], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ebf92564-8ea0-4ca8-bec5-8d51bd4e984c", - "name": "Spawner (125)", - "location": [1494, 1715, 6], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec85a134-cb7e-4b7b-bd1b-0023b55d9869", - "name": "Spawner (125)", - "location": [562, 2148, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "RangerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ecd340e1-2000-4c61-8042-64e2a3a7734f", - "name": "Spawner (125)", - "location": [517, 986, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ee56cd69-1e8d-46fe-906f-e3e91614708a", - "name": "Spawner (125)", - "location": [1512, 3989, -3], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eff84dda-bd01-479c-a669-ca56d9fffe3d", - "name": "Spawner (125)", - "location": [2890, 651, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f0019dbf-e6d5-44a4-8da4-16dbc44da350", - "name": "Spawner (125)", - "location": [1981, 2838, 15], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f120e595-13e3-4c97-a2f6-0ad960f38b0c", - "name": "Spawner (125)", - "location": [2526, 375, 23], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f14276d9-00a3-4122-aa2a-280dd4f8fac7", - "name": "Spawner (125)", - "location": [2503, 552, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f163cb5b-d232-4cf8-ae17-2d13fe08623d", - "name": "Spawner (125)", - "location": [4406, 1038, -2], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f23df328-1474-4abf-855f-08a262da0c92", - "name": "Spawner (125)", - "location": [2998, 760, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f46915ea-0d08-4bca-bb53-335f8c12f07b", - "name": "Spawner (125)", - "location": [2577, 599, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f722fc04-87d9-4293-a80b-77140c00250a", - "name": "Spawner (125)", - "location": [1848, 2680, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f723423b-7c27-48c1-b662-335797e6dff2", - "name": "Spawner (125)", - "location": [2680, 2238, 2], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f74f17e3-537f-4cf5-9ed5-2677e3bddd84", - "name": "Spawner (125)", - "location": [2972, 3425, 15], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f85575f5-813e-48cc-b0a5-2eb116e610bd", - "name": "Spawner (125)", - "location": [1441, 3719, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fac7f6c9-0564-4baf-b2aa-9bf3a9e4b35c", - "name": "Spawner (125)", - "location": [2715, 2098, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fb0032c9-6652-41c4-b15b-8a3f64aa3968", - "name": "Spawner (125)", - "location": [5225, 4000, 38], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Blacksmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fda0fb25-6ca0-422e-836f-81bd46da8a61", - "name": "Spawner (125)", - "location": [2882, 723, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - } -] diff --git a/Distribution/Data/Spawns/uoml/felucca/BlightedGrove.json b/Distribution/Data/Spawns/uoml/felucca/BlightedGrove.json new file mode 100644 index 000000000..e67514b2e --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/BlightedGrove.json @@ -0,0 +1,145 @@ +[ + { + "type": "Spawner", + "guid": "c6dedce5-b3a9-4510-b8a4-4e1be285305c", + "name": "Spawner (2201)", + "location": [6513, 871, 0], + "map": "Felucca", + "count": 17, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [ + { "name": "GiantSerpent", "maxCount": 17, "probability": 100 }, + { "name": "GiantToad", "maxCount": 17, "probability": 100 }, + { "name": "Harpy", "maxCount": 17, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 17, "probability": 100 }, + { "name": "Snake", "maxCount": 17, "probability": 100 }, + { "name": "Bogling", "maxCount": 17, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3bd66ec7-4d6b-46cb-8913-5c62344510c0", + "name": "Spawner (2201)", + "location": [6507, 843, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Tangle", "maxCount": 8, "probability": 100 }, + { "name": "Bogling", "maxCount": 8, "probability": 100 }, + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 }, + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "Changeling", "maxCount": 8, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "687f8c07-43c4-4724-8ede-078862373736", + "name": "Spawner (2201)", + "location": [6535, 848, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Abscess", "maxCount": 4, "probability": 100 }, + { "name": "Hydra", "maxCount": 4, "probability": 100 }, + { "name": "BogThing", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4583e64-cac2-49f5-bb09-86c9481e0147", + "name": "Spawner (2201)", + "location": [6518, 870, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Thrasher", "maxCount": 9, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Bogling", "maxCount": 9, "probability": 100 }, + { "name": "Alligator", "maxCount": 9, "probability": 100 }, + { "name": "Coil", "maxCount": 9, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "163c83bd-e63d-4654-8c0e-231486150f45", + "name": "Spawner (2201)", + "location": [6540, 872, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f708c05a-1da4-4452-8d8b-1965849d6e39", + "name": "Spawner (2201)", + "location": [6585, 878, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Harpy", "maxCount": 7, "probability": 100 }, + { "name": "Saliva", "maxCount": 7, "probability": 100 }, + { "name": "Changeling", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b045049-0b03-4f18-9148-b0bd1fa3600b", + "name": "Spawner (2201)", + "location": [6489, 897, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "196961bc-de35-4e5c-9fbf-079ab5092d5f", + "name": "Spawner (2201)", + "location": [6518, 879, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/BritainSewer.json b/Distribution/Data/Spawns/uoml/felucca/BritainSewer.json new file mode 100644 index 000000000..e9a2efb06 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/BritainSewer.json @@ -0,0 +1,356 @@ +[ + { + "type": "Spawner", + "guid": "aca1fcd4-cc2b-4472-ab6e-89e0322f9fec", + "name": "Spawner (2202)", + "location": [6049, 1447, 4], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8674d509-c36a-4ab8-9399-6329f1379984", + "name": "Spawner (2202)", + "location": [6091, 1491, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5da0bc6b-dfdb-47b8-a0df-0ff3eaf5c212", + "name": "Spawner (2202)", + "location": [6048, 1487, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07b03a23-af40-4539-be8b-6834573838df", + "name": "Spawner (2202)", + "location": [6065, 1459, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8273eff-be93-4ab9-8642-c1e9a92ddbbc", + "name": "Spawner (2202)", + "location": [6092, 1448, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebace9c4-5941-4346-9ebf-6ca7ff7e33c5", + "name": "Spawner (2202)", + "location": [6107, 1452, 25], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "371c3545-e908-4d18-8dbb-3b86c422566a", + "name": "Spawner (2202)", + "location": [6112, 1462, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a9a6ccfe-d9b7-40f3-bfa8-d00994214056", + "name": "Spawner (2202)", + "location": [6039, 1438, 4], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8680e6d-12db-4ef9-8107-9df6850893db", + "name": "Spawner (2202)", + "location": [6115, 1491, -15], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da9f7dcc-c64d-4a72-ba8a-b5846258a093", + "name": "Spawner (2202)", + "location": [6084, 1470, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 1, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c93d730a-8c5a-4580-8952-14008d80b395", + "name": "Spawner (2202)", + "location": [6049, 1470, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba6cecc1-f3c1-4e95-a624-ac4d5d668265", + "name": "Spawner (2202)", + "location": [6034, 1467, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75adb4cb-b0a7-44db-a5fe-d4e9b139ce7b", + "name": "Spawner (2202)", + "location": [6059, 1438, 4], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62ba5343-d337-495e-aaf4-9dfb4d46c8b3", + "name": "Spawner (2202)", + "location": [6061, 1490, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d0d1dbda-9b50-4646-9509-d5d32eb9f081", + "name": "Spawner (2202)", + "location": [6041, 1486, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2fd77ead-e282-4216-aa6c-0273391bdd4e", + "name": "Spawner (2202)", + "location": [6090, 1483, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "Alligator", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e8b1dc3e-cc13-4253-b228-12abe499061b", + "name": "Spawner (2202)", + "location": [6077, 1492, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "75e04bc3-078d-40c2-9ca8-d3a11a97965f", + "name": "Spawner (2202)", + "location": [6108, 1471, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3237bbc2-29a2-488c-a918-d8db1f5e7f60", + "name": "Spawner (2202)", + "location": [6087, 1443, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "099f6832-75ef-4f46-91b2-0d64509897c2", + "name": "Spawner (2202)", + "location": [6052, 1463, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "Alligator", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "77bf6da3-ca32-446e-8d0b-239bb4520afd", + "name": "Spawner (2202)", + "location": [6037, 1494, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Covetous.json b/Distribution/Data/Spawns/uoml/felucca/Covetous.json new file mode 100644 index 000000000..f06004744 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Covetous.json @@ -0,0 +1,767 @@ +[ + { + "type": "Spawner", + "guid": "85fdc7f9-7c5b-4d42-ba34-f0470a3470c9", + "name": "Spawner (2203)", + "location": [5472, 1877, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Harpy", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b4252b3-0f5e-4af8-b8c7-6dc2352924f5", + "name": "Spawner (2203)", + "location": [5453, 1905, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "HeadlessOne", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "938f10be-c2ce-4786-a151-5a946533d688", + "name": "Spawner (2203)", + "location": [5443, 1918, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 30, + "entries": [{ "name": "StoneHarpy", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e0b760fb-d7aa-45d3-8bf9-48011b0cf4d5", + "name": "Spawner (2203)", + "location": [5418, 1876, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Gazer", "maxCount": 4, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb869b8b-507c-427b-879d-4fe7b790a3a2", + "name": "Spawner (2203)", + "location": [5411, 1901, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Gazer", "maxCount": 2, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d06af3f-76ac-4beb-b7bb-c65efeb63a2d", + "name": "Spawner (2203)", + "location": [5393, 1921, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dd7e6d31-3034-4600-a6b9-ca42a8842c80", + "name": "Spawner (2203)", + "location": [5400, 1937, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7ea55605-a5c0-4993-a383-accfc14ac29a", + "name": "Spawner (2203)", + "location": [5452, 1891, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3bfaa0a-3984-451a-940c-3fd6890e0d94", + "name": "Spawner (2203)", + "location": [5399, 1859, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50cbf26b-8516-4000-869e-08fee55f37c8", + "name": "Spawner (2203)", + "location": [5387, 1911, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "076ead03-f463-46d8-9eae-962520cb986f", + "name": "Spawner (2203)", + "location": [5411, 1932, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46c6bb22-25f4-44cc-a42a-15ec120268bb", + "name": "Spawner (2203)", + "location": [5577, 2006, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "18614d1c-4026-4618-9066-f8369e353c0b", + "name": "Spawner (2203)", + "location": [5593, 2017, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b1325960-b3f5-4e08-bf86-4971d893a03b", + "name": "Spawner (2203)", + "location": [5558, 2025, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44351b8c-77f3-4eca-b121-b692abfefe34", + "name": "Spawner (2203)", + "location": [5498, 1998, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "57532ad5-1402-49c2-ac15-8b40fbe30210", + "name": "Spawner (2203)", + "location": [5474, 2035, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05c027e9-5cef-4b03-b4e2-b7f70c0f34cb", + "name": "Spawner (2203)", + "location": [5469, 2019, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Slime", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "610daa60-c1c5-4a85-be92-2bd7cec2d8d3", + "name": "Spawner (2203)", + "location": [5445, 2030, -3], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "WaterElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c7a0ddb7-7ad8-4f5e-aa17-6d4cb62deebe", + "name": "Spawner (2203)", + "location": [5440, 2023, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dede0e05-f13e-4e1f-bd7d-3a5ad86ae730", + "name": "Spawner (2203)", + "location": [5473, 1991, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 9, + "walkingRange": 25, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55ea4b3a-f7f0-4e41-8a13-4b839a657671", + "name": "Spawner (2203)", + "location": [5457, 1971, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Gazer", "maxCount": 4, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 }, + { "name": "ElderGazer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "081c0be6-7cd4-4ab6-95bd-b4a3ac8bc5eb", + "name": "Spawner (2203)", + "location": [5436, 1972, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "87a1cb9f-e49a-407e-a015-f52c4449902b", + "name": "Spawner (2203)", + "location": [5417, 1975, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21535b77-5477-4334-98a1-2fb854f492a2", + "name": "Spawner (2203)", + "location": [5429, 1989, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Gazer", "maxCount": 2, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7b58b7a8-e6b5-4353-82b2-787c87a6469d", + "name": "Spawner (2203)", + "location": [5425, 1992, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12837eff-77e0-423d-9308-aa8e4b92afc0", + "name": "Spawner (2203)", + "location": [5388, 2012, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Gazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5e88cb7f-eff4-43c8-8137-ec7efbef249e", + "name": "Spawner (2203)", + "location": [5503, 2002, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7b69d58b-9222-46b1-9833-4d9c5d73785e", + "name": "Spawner (2203)", + "location": [5413, 2000, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04a59b8a-4697-4c8a-b057-a7846a5cfe97", + "name": "Spawner (2203)", + "location": [5460, 1975, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d292834-991e-49ac-b1f8-9b19816f6619", + "name": "Spawner (2203)", + "location": [5448, 2027, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2c5902f-4e3a-413c-b938-f8e7bf3bef13", + "name": "Spawner (2203)", + "location": [2456, 861, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a6cd9b62-1f5a-4c7b-81b8-4df8e73a1c88", + "name": "Spawner (2203)", + "location": [2480, 863, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9274c0ec-c477-4846-b170-b903efdbba05", + "name": "Spawner (2203)", + "location": [2493, 883, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11ad3aa3-a368-4ae4-834f-873a14b82a5c", + "name": "Spawner (2203)", + "location": [2513, 862, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bd99f0d0-a90c-409b-be4c-91898afed157", + "name": "Spawner (2203)", + "location": [5560, 1903, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "18b6eb68-8acb-4269-8d51-e1e52df02f52", + "name": "Spawner (2203)", + "location": [5596, 1903, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc9968d8-52b1-415b-b5c7-05129358b666", + "name": "Spawner (2203)", + "location": [5560, 1876, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f0fae8d5-a1e1-41f4-a0ec-4469b9ca6648", + "name": "Spawner (2203)", + "location": [5603, 1875, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b661a309-8991-4fb9-9360-47dda815fae5", + "name": "Spawner (2203)", + "location": [5617, 1861, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78da4a22-a348-4c15-80c1-90a649a8551c", + "name": "Spawner (2203)", + "location": [5615, 1838, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bef2f7e-e2ca-444e-a1a9-04f03ce7b12c", + "name": "Spawner (2203)", + "location": [5579, 1859, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 11, + "walkingRange": 25, + "entries": [ + { "name": "Lich", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2ebc056-295d-46f8-9a00-5dde61c30859", + "name": "Spawner (2203)", + "location": [5560, 1843, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 40, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dcb4ea5c-002a-4589-be5c-f09de15f5d4e", + "name": "Spawner (2203)", + "location": [5528, 1808, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a733418d-d49e-4938-842b-cc7ffed4523a", + "name": "Spawner (2203)", + "location": [5504, 1808, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 12, + "entries": [ + { "name": "RottingCorpse", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f8fc4307-f4ce-4883-b41d-b8dca969991a", + "name": "Spawner (2203)", + "location": [5452, 1799, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca2ebd92-7ae7-43ab-a1bd-7bf52783f77f", + "name": "Spawner (2203)", + "location": [5461, 1818, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Deceit.json b/Distribution/Data/Spawns/uoml/felucca/Deceit.json new file mode 100644 index 000000000..b349f70c8 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Deceit.json @@ -0,0 +1,626 @@ +[ + { + "type": "Spawner", + "guid": "edd28bb3-5aeb-487a-8571-9e4e4aea0783", + "name": "Spawner (2204)", + "location": [5187, 603, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 35, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8507dbe4-8220-4439-adbe-5ef6ebb0fec3", + "name": "Spawner (2204)", + "location": [5186, 575, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 12, + "entries": [{ "name": "WaterElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb409b68-fe6b-422f-bf2d-95c4a92f5e21", + "name": "Spawner (2204)", + "location": [5187, 545, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19d96c26-d7f8-4209-9071-00e0117ed1d8", + "name": "Spawner (2204)", + "location": [5163, 567, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 25, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9862d38e-e12f-4e04-932f-0020f3121d9a", + "name": "Spawner (2204)", + "location": [5145, 553, -40], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 35, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8ba7cc3-c1f6-4701-b684-850203cb9e37", + "name": "Spawner (2204)", + "location": [5147, 579, -50], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ab35ec53-2a16-4cdd-a2a1-f1b84922e36c", + "name": "Spawner (2204)", + "location": [5147, 598, -50], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9f4d863-626f-480c-968a-9630e6a9a83d", + "name": "Spawner (2204)", + "location": [5145, 622, -50], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb79321c-1a80-4881-8961-1997174d0458", + "name": "Spawner (2204)", + "location": [5212, 531, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "684234a4-21ea-4f94-897b-e92d88a17ced", + "name": "Spawner (2204)", + "location": [5219, 552, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b6e0a59-f918-4f77-8bc8-f5b5d944d3a5", + "name": "Spawner (2204)", + "location": [5219, 573, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f6b68da6-e7f9-4cb1-870c-079ebff4787b", + "name": "Spawner (2204)", + "location": [5145, 618, -50], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1cbfae9c-7cc7-45cc-842f-0e75a158cd8a", + "name": "Spawner (2204)", + "location": [5307, 546, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4857128-1876-473c-951f-418107fad15c", + "name": "Spawner (2204)", + "location": [5327, 542, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b00a63e-cf2d-495b-8bd9-61cef122f168", + "name": "Spawner (2204)", + "location": [5286, 542, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2fc19bd-75c9-4f68-b59d-6efc65381167", + "name": "Spawner (2204)", + "location": [5283, 583, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7016863d-26dc-4d2e-beda-68031a966ebe", + "name": "Spawner (2204)", + "location": [5318, 578, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79dfd73d-ede7-44e2-8651-94cfe5f77314", + "name": "Spawner (2204)", + "location": [5342, 582, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d25517a-5b90-4468-9e23-4041f88774e2", + "name": "Spawner (2204)", + "location": [5299, 603, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa668f86-9786-48bc-a676-5a879e77bee2", + "name": "Spawner (2204)", + "location": [5295, 623, -5], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "BoneMagi", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "efc2564d-a6df-40a6-ad5e-6e6e81d066e9", + "name": "Spawner (2204)", + "location": [5322, 626, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "350cfc3c-1c89-463d-a93a-f2ab22149d19", + "name": "Spawner (2204)", + "location": [5323, 571, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5e81dcf-fa17-4d36-8aa1-e73496aad012", + "name": "Spawner (2204)", + "location": [5139, 661, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "750ebeff-4327-4bf0-b285-8c6749530985", + "name": "Spawner (2204)", + "location": [5190, 655, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a2d6e670-8282-4b60-bd5f-933e45d3d7a8", + "name": "Spawner (2204)", + "location": [5170, 678, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56939d4e-8a5d-47da-a117-e7f56ff6671b", + "name": "Spawner (2204)", + "location": [5151, 718, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0be3858f-bd81-4a27-9a8d-3846c0df0603", + "name": "Spawner (2204)", + "location": [5192, 695, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cacb456a-f52d-4168-9d59-8df80779a1d6", + "name": "Spawner (2204)", + "location": [5183, 727, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05d2885b-2b4d-4c4e-907e-2fdbe4d54e69", + "name": "Spawner (2204)", + "location": [5212, 674, -20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c69344fe-718b-4e69-83b8-d12141250707", + "name": "Spawner (2204)", + "location": [5219, 734, -20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "88b5fff8-7124-4a19-83ad-465e8aaf072b", + "name": "Spawner (2204)", + "location": [5150, 742, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a7f9024-7d0f-43a0-8721-52b6a3c27c8f", + "name": "Spawner (2204)", + "location": [5307, 675, -20], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbb8562b-7557-4347-9faa-a5facf1ba1d4", + "name": "Spawner (2204)", + "location": [5280, 675, 8], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "818d1f8c-a5ba-4ef1-a823-2b04bca2f2e2", + "name": "Spawner (2204)", + "location": [5320, 708, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "35acdddb-eef4-4687-b6d7-6dea19535b71", + "name": "Spawner (2204)", + "location": [5314, 748, -20], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "25b2761d-dc06-443e-b979-049c7f50abf6", + "name": "Spawner (2204)", + "location": [5267, 690, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 35, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Despise.json b/Distribution/Data/Spawns/uoml/felucca/Despise.json new file mode 100644 index 000000000..05d507c08 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Despise.json @@ -0,0 +1,636 @@ +[ + { + "type": "Spawner", + "guid": "9dbd8c5c-40d7-4908-8fbd-86cd1f149be0", + "name": "Spawner (2205)", + "location": [5503, 529, 60], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "831675ca-f594-4e7e-bd6f-b0f15ed60bf0", + "name": "Spawner (2205)", + "location": [5504, 597, 45], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb3ee679-fc60-4abb-b6b0-a809f77ca53b", + "name": "Spawner (2205)", + "location": [5464, 600, 45], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f0922369-d73d-4c7e-a669-28812e0a0559", + "name": "Spawner (2205)", + "location": [5435, 589, 47], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "74032d64-a50f-4f99-ad45-3d9ad9f1d718", + "name": "Spawner (2205)", + "location": [5410, 611, 45], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9f576fea-09aa-47fb-a3e4-a1b8369b650e", + "name": "Spawner (2205)", + "location": [5386, 612, 45], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b94a3e0-7977-459b-bc5f-6c13ce0062ff", + "name": "Spawner (2205)", + "location": [5405, 576, 61], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ba7fd9a9-bffc-4f04-8606-ae79b4bb830b", + "name": "Spawner (2205)", + "location": [5411, 541, 60], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11e8cbb5-45dd-4109-8606-54eb9bada453", + "name": "Spawner (2205)", + "location": [5460, 526, 60], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9e9bcade-4455-4c85-b7f2-4d9dc6e6c6f4", + "name": "Spawner (2205)", + "location": [5394, 525, 65], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d00942a-33e2-4714-8d03-9d6a40778772", + "name": "Spawner (2205)", + "location": [5390, 587, 45], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c99bb2a1-1690-4656-95d7-d61b0c5640c8", + "name": "Spawner (2205)", + "location": [5490, 676, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6a01c304-d63d-4a59-8b00-d004329d1af7", + "name": "Spawner (2205)", + "location": [5483, 711, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1adcf3d5-536f-473d-af18-4ae23415ebba", + "name": "Spawner (2205)", + "location": [5505, 747, 5], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 25, + "entries": [ + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c045d499-1fe2-4afa-be89-e8e5f3c6f4f8", + "name": "Spawner (2205)", + "location": [5475, 748, 5], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05844644-3d42-4021-bb28-8114d8f7f064", + "name": "Spawner (2205)", + "location": [5420, 746, 5], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 25, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ettin", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb7fc3f7-d80d-44c5-8a63-743a970f5842", + "name": "Spawner (2205)", + "location": [5385, 703, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7ec31075-3cef-4d6b-a20e-2c55540052fc", + "name": "Spawner (2205)", + "location": [5391, 660, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04207c29-fe38-4d43-a08d-8645a3a312e8", + "name": "Spawner (2205)", + "location": [5441, 680, 20], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "EarthElemental", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b453194c-94e8-4d58-87cc-3870d6292247", + "name": "Spawner (2205)", + "location": [5507, 656, 20], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "38967322-06dc-48bc-bed1-6dd60661b1c0", + "name": "Spawner (2205)", + "location": [5387, 697, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5c5b2844-5792-434b-9d9e-e5f607f32b6b", + "name": "Spawner (2205)", + "location": [5439, 851, 45], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21b9e0f9-7627-4967-b7bf-5453be3e0266", + "name": "Spawner (2205)", + "location": [5470, 860, 45], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f8f62c01-5bf8-41dc-ac7a-37cc7a97134b", + "name": "Spawner (2205)", + "location": [5426, 791, 60], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "310b722e-fe5d-4692-97c7-956d15b76472", + "name": "Spawner (2205)", + "location": [5482, 791, 60], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d58f315e-8fcf-4282-a4e4-5aaed9c19cd5", + "name": "Spawner (2205)", + "location": [5532, 787, 60], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93356ad6-8774-42cd-ab96-705c2d55e109", + "name": "Spawner (2205)", + "location": [5604, 789, 60], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c739ce37-7e4b-4a3a-886b-4f45a049cde6", + "name": "Spawner (2205)", + "location": [5558, 824, 45], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "OgreLord", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3d6315d9-3968-4b92-b71a-99a12bab509e", + "name": "Spawner (2205)", + "location": [5590, 871, 45], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0a6bf903-a9a3-4252-a4a0-652d89fb9381", + "name": "Spawner (2205)", + "location": [5528, 902, 30], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7573ab39-270e-45f0-99db-4304837ef7bb", + "name": "Spawner (2205)", + "location": [5487, 902, 30], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "51d69140-0708-491e-9dbd-9c5d58349172", + "name": "Spawner (2205)", + "location": [5504, 939, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb91b20f-4aee-45c7-b70b-de22c9adcdae", + "name": "Spawner (2205)", + "location": [5471, 973, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 25, + "entries": [{ "name": "Cyclops", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "441cff6e-d90b-4a74-bd0a-b10165fd6eb4", + "name": "Spawner (2205)", + "location": [5426, 943, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1855dd2e-81b1-4326-bc97-f3e50884e09a", + "name": "Spawner (2205)", + "location": [5391, 905, 30], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4780e4d1-5a3a-4519-a8db-3cc6e77648c7", + "name": "Spawner (2205)", + "location": [5398, 996, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "AcidElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9df386c1-05f8-4290-960b-8d0b6f0393ce", + "name": "Spawner (2205)", + "location": [5593, 794, 60], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "38fe4fe8-2f5d-441f-8f4b-8f679a955e96", + "name": "Spawner (2205)", + "location": [5546, 888, 30], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c71f2a8-2d6b-40b8-8dca-9eca3aa54d47", + "name": "Spawner (2205)", + "location": [5397, 995, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70c7a94c-7535-432d-917a-640d0a6f47ff", + "name": "Spawner (2205)", + "location": [5605, 799, 60], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "74fc3f59-8ee2-4ee5-b246-ddf07c415ca5", + "name": "Spawner (2205)", + "location": [5557, 828, 45], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e65ea3f-2bca-43cd-b6e2-916b8625d69b", + "name": "Spawner (2205)", + "location": [5398, 995, 5], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b328679f-d212-49e8-8900-af2cd1f77b04", + "name": "Spawner (2205)", + "location": [5564, 821, 45], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Destard.json b/Distribution/Data/Spawns/uoml/felucca/Destard.json new file mode 100644 index 000000000..5373e98ec --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Destard.json @@ -0,0 +1,465 @@ +[ + { + "type": "Spawner", + "guid": "e8017d92-b455-4ab2-b484-f95b2957b3be", + "name": "Spawner (2206)", + "location": [5209, 965, -40], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0fbce341-ca59-4ec4-a718-cc8f7b3ed34e", + "name": "Spawner (2206)", + "location": [5247, 956, -40], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79707431-23e6-4347-a89e-7bc970162938", + "name": "Spawner (2206)", + "location": [5280, 955, -40], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7dfd256b-c903-4a4f-abf1-bdce7ff2217b", + "name": "Spawner (2206)", + "location": [5284, 926, -40], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "889ccd57-8eda-4910-9790-2dc3bc80ca99", + "name": "Spawner (2206)", + "location": [5214, 917, -40], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e318f8e-aec0-4b16-b435-43dc4fde4372", + "name": "Spawner (2206)", + "location": [5241, 945, -40], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5973fd2-89b7-405a-bdea-4b1f2fafa92d", + "name": "Spawner (2206)", + "location": [5161, 940, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [{ "name": "GiantSerpent", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "326c64f4-34d3-414d-a79c-c7bb9d441e96", + "name": "Spawner (2206)", + "location": [5149, 907, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef6d6675-0683-47e4-b236-bfc890a12227", + "name": "Spawner (2206)", + "location": [5141, 915, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f8eb77e-64cb-4846-8c0f-0816f16c94b3", + "name": "Spawner (2206)", + "location": [5352, 934, -5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d045c909-34f1-46a6-8d61-7275dc6e45c9", + "name": "Spawner (2206)", + "location": [5317, 980, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2bea874-3646-4dec-8fc9-cbc0c67f2a0a", + "name": "Spawner (2206)", + "location": [5250, 869, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "GiantSerpent", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "87114b5e-ac9d-4d7f-90c1-c6db7a570c2f", + "name": "Spawner (2206)", + "location": [5226, 826, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "48b37663-ddee-4f04-978d-35f4c085f785", + "name": "Spawner (2206)", + "location": [5269, 811, 7], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "936bde4a-dce6-44bb-859c-81a3e1f0a87b", + "name": "Spawner (2206)", + "location": [5307, 815, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "831f9d08-398c-42b0-bc53-72e1c0685e34", + "name": "Spawner (2206)", + "location": [5294, 842, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "59c245e9-e59f-4f6c-b3cb-2add65aba939", + "name": "Spawner (2206)", + "location": [5203, 787, 5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b6125064-8514-4c32-953d-c28eca82fa4e", + "name": "Spawner (2206)", + "location": [5229, 841, 1], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9641ac5e-6eec-4f35-8536-226265c67ace", + "name": "Spawner (2206)", + "location": [5205, 778, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "be7b487d-c201-413a-94c8-8201dc3a1c4d", + "name": "Spawner (2206)", + "location": [5141, 834, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "ShadowWyrm", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "edd4aa65-92a9-491c-97e8-d9906e31fbea", + "name": "Spawner (2206)", + "location": [5169, 836, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "EvilMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2aa7cb3-ff27-4ea9-a2a4-fe62a0e78b76", + "name": "Spawner (2206)", + "location": [5152, 869, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "ShadowWyrm", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8aceeb2-7644-4b3d-96de-4e172f41abd6", + "name": "Spawner (2206)", + "location": [5153, 842, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "TreasureChestLevel4", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "171e7677-5e0b-4da4-8599-9c3c16200e7a", + "name": "Spawner (2206)", + "location": [5141, 968, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ad446354-3f49-4c9d-ab1d-dea29378794e", + "name": "Spawner (2206)", + "location": [5157, 997, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [ + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6703676-d851-4394-b09f-51286d5d85f2", + "name": "Spawner (2206)", + "location": [5185, 1006, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [{ "name": "AncientWyrm", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8b681dd7-a815-4513-85e6-901967f4e6ed", + "name": "Spawner (2206)", + "location": [5143, 986, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "506ce404-853e-4168-9b6b-1296d40c90e3", + "name": "Spawner (2206)", + "location": [5183, 1007, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/FelCropsLS.json b/Distribution/Data/Spawns/uoml/felucca/FelCropsLS.json new file mode 100644 index 000000000..2a31049f3 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/FelCropsLS.json @@ -0,0 +1,290 @@ +[ + { + "type": "RegionSpawner", + "location": [375, 1191, 0], + "map": "Felucca", + "region": "A Wheatfield in Yew 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "bba64d97-11eb-4a87-b326-e41e3c9b355d", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [567, 1239, 0], + "map": "Felucca", + "region": "A Wheatfield in Yew 1", + "count": 5, + "entries": [{ "maxCount": 5, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "e37b4d74-c87e-4102-8c87-8f80c728fe0c", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [571, 1099, 0], + "map": "Felucca", + "region": "A Farm in Yew", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "aed692cc-2dac-4c80-931b-35c73ca8d092", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [675, 939, 0], + "map": "Felucca", + "region": "A Field of Sheep in Yew 1", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "425ac125-1597-41b4-9e6d-72b91801230b", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [675, 1179, 0], + "map": "Felucca", + "region": "A Field of Sheep in Yew 2", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "3d7925bf-2642-4185-bc03-fa314fb75a7f", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [813, 2163, 0], + "map": "Felucca", + "region": "A Wheatfield in Skara Brae 1", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "57da6436-6b16-4697-add5-f4b45776ccbc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2254, 0], + "map": "Felucca", + "region": "A Carrot Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "427451dd-93fa-43c9-85c6-01536066c8cc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2264, 0], + "map": "Felucca", + "region": "An Onion Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableOnion", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "640fe3f8-9917-4dfa-986d-038923163add", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2264, 0], + "map": "Felucca", + "region": "A Cabbage Field in Skara Brae 1", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "f4bccdc2-c663-4ccd-b7df-31656fa2ae09", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2274, 0], + "map": "Felucca", + "region": "A Cabbage Field in Skara Brae 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "0c48edf9-a88e-45f1-9ba3-cd8c919aefbc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2355, 0], + "map": "Felucca", + "region": "A Cotton Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCotton", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "e7b34039-ec1f-43c8-b64f-d7462ce7ae9d", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [843, 2352, 0], + "map": "Felucca", + "region": "A Wheatfield in Skara Brae 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "2bf378f0-77ed-44cd-8dfb-181d068f401e", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1120, 1624, 0], + "map": "Felucca", + "region": "A Wheatfield in Britain 4", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "e6a4aede-5a76-473d-8331-4bb73701ea1a", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1135, 1791, 0], + "map": "Felucca", + "region": "A Wheatfield in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "b29b54ab-5ae3-4ce3-93ac-326f324a34f6", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1152, 1576, 0], + "map": "Felucca", + "region": "A Wheatfield in Britain 5", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "e416e9fa-5a9a-426b-962e-50cdce64ca7f", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1183, 1683, 0], + "map": "Felucca", + "region": "A Cabbage Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "8536c19c-2036-495a-a3c5-e82c9f32f5ad", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1199, 1683, 0], + "map": "Felucca", + "region": "A Turnip Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableTurnip", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "d8a2e54e-4951-468f-93c5-376460070cba", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1199, 1823, 0], + "map": "Felucca", + "region": "A Wheatfield in Britain 2", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "58b14343-2032-4507-a913-6b536507c2e5", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1215, 1723, 0], + "map": "Felucca", + "region": "A Carrot Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "9805ef58-355f-4352-bfc4-43c6710ff409", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1216, 1604, 0], + "map": "Felucca", + "region": "A Turnip Field in Britain 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableTurnip", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "0c6481ab-3341-4113-ac1a-6523c19c881d", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1231, 1723, 0], + "map": "Felucca", + "region": "An Onion Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableOnion", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "57f955bb-49e1-4a66-a214-4fbddf8d572b", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1231, 1887, 0], + "map": "Felucca", + "region": "A Wheatfield in Britain 3", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "f68b6bc8-f822-4c7a-9d01-37fad25bc95b", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1232, 1604, 0], + "map": "Felucca", + "region": "A Carrot Field in Britain 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "7b09d4b5-fe63-4f95-8525-3c970c513adc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [4567, 1475, 0], + "map": "Felucca", + "region": "A Cotton Field in Moonglow", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCotton", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "4b4e3973-d836-4f7c-b9f7-5fb7d468da28", + "name": "Spawner" + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Fire.json b/Distribution/Data/Spawns/uoml/felucca/Fire.json new file mode 100644 index 000000000..b35015699 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Fire.json @@ -0,0 +1,758 @@ +[ + { + "type": "Spawner", + "guid": "9df65008-f599-43d4-b788-df7138e37320", + "name": "Spawner (2207)", + "location": [5693, 1415, 38], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Slime", "maxCount": 1, "probability": 100 }, + { "name": "GiantRat", "maxCount": 2, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d77719be-10a6-40f5-97a0-95dc2e3137b4", + "name": "Spawner (2207)", + "location": [5695, 1434, -1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Slime", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a08f5c78-d35f-4755-a0f9-009660acb8c9", + "name": "Spawner (2207)", + "location": [5680, 1438, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "EvilMageLord", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1da36d4f-d8c9-4202-8ce0-b8456969fbe7", + "name": "Spawner (2207)", + "location": [5733, 1433, 22], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "GiantRat", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, + { "name": "EvilMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93908c67-1ffd-4196-bceb-b2ca2ec98e5d", + "name": "Spawner (2207)", + "location": [5787, 1427, 17], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaLizard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b6bd2f4-2d4f-4144-ac9a-e34b9d2f88b1", + "name": "Spawner (2207)", + "location": [5717, 1465, -2], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "FireElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b8f9386a-456c-44ff-9baa-0b8234e0c0c4", + "name": "Spawner (2207)", + "location": [5744, 1471, 2], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "FireElemental", "maxCount": 3, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8524d599-a31d-4fb0-977e-0cff77d2d297", + "name": "Spawner (2207)", + "location": [5790, 1451, 7], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [ + { "name": "Efreet", "maxCount": 1, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4c87574-6489-4e20-8642-61051697b2e1", + "name": "Spawner (2207)", + "location": [5852, 1468, 1], + "map": "Felucca", + "count": 17, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "Lich", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 6, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 6, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 3, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1e8b71e5-2c17-4c4d-b933-b887a9b830b4", + "name": "Spawner (2207)", + "location": [5852, 1468, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f24afdd-09b5-406c-90d6-c714c1811f4e", + "name": "Spawner (2207)", + "location": [5859, 1387, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaLizard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a874fb64-451c-4f30-ab6f-e56a9eb71604", + "name": "Spawner (2207)", + "location": [5787, 1386, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HellCat", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ce7b5441-9c5e-4426-9ff8-09085c48f899", + "name": "Spawner (2207)", + "location": [5818, 1379, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4f164c53-9758-4970-91ab-c4de29e3bfc2", + "name": "Spawner (2207)", + "location": [5836, 1364, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d009fa2f-ee2e-4adf-828d-917c991f5c8d", + "name": "Spawner (2207)", + "location": [5809, 1332, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e619be6-7384-415e-a23a-d7327b39a05e", + "name": "Spawner (2207)", + "location": [5859, 1341, -1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "06b7ba24-826f-4130-b3fb-f367ac6e9a82", + "name": "Spawner (2207)", + "location": [5835, 1304, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "Efreet", "maxCount": 1, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dfe6b8e9-4b85-4f76-a60f-47ae91fd38fa", + "name": "Spawner (2207)", + "location": [5867, 1309, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9157324f-0d09-409e-8ace-f0c4dcb44683", + "name": "Spawner (2207)", + "location": [5699, 1302, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3bac45c2-6a16-4892-b25c-f6c02788a5f4", + "name": "Spawner (2207)", + "location": [5723, 1291, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "25415970-01ad-463e-b9f0-053c077a209f", + "name": "Spawner (2207)", + "location": [5732, 1297, -1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 20, + "entries": [{ "name": "HellHound", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5bb1395f-d446-42de-bcdf-d257d7ed953a", + "name": "Spawner (2207)", + "location": [5667, 1315, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Efreet", "maxCount": 2, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5655a639-c25c-446b-9de9-30fb4d294143", + "name": "Spawner (2207)", + "location": [5747, 1320, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "436c4d7e-5dac-4f1f-a6bf-397e2e768683", + "name": "Spawner (2207)", + "location": [5717, 1333, 6], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a206916-74e5-44ec-8c78-bf01a8916f53", + "name": "Spawner (2207)", + "location": [5731, 1379, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [ + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88f058cc-ebdb-48bd-908a-cbb49142fd11", + "name": "Spawner (2207)", + "location": [5709, 1364, -1], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HellHound", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d11e7abf-7396-4fa4-a7a1-8dd69b700907", + "name": "Spawner (2207)", + "location": [5691, 1347, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Daemon", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "55d34b4f-fbf6-43a0-b8bb-5215ae74c2a6", + "name": "Spawner (2207)", + "location": [5678, 1340, -2], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d357111-cd98-4c2d-ab92-ead67f9da0e9", + "name": "Spawner (2207)", + "location": [5651, 1363, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 5, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d00cb0e2-efb1-47c3-a65f-079f2677219c", + "name": "Spawner (2207)", + "location": [5654, 1380, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "934c223f-19e3-401c-aaf9-494c751d8c1c", + "name": "Spawner (2207)", + "location": [5689, 1387, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6569aa86-8a7e-4a7d-8b0b-f46badca59c2", + "name": "Spawner (2207)", + "location": [5656, 1412, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 10, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d114e342-f07c-46b9-9164-43bafbd74988", + "name": "Spawner (2207)", + "location": [5651, 1402, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "90609dc2-cd78-4057-9b87-6023fc1fa263", + "name": "Spawner (2207)", + "location": [5643, 1403, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c60b60b7-79f5-4a77-b9af-c4a9b49d87f1", + "name": "Spawner (2207)", + "location": [5651, 1408, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [{ "name": "HellHound", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c701efc3-54d7-40d9-b3d0-909ded6a74d6", + "name": "Spawner (2207)", + "location": [5651, 1418, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d7e70c50-8b14-4153-b44f-a9f6764762f0", + "name": "Spawner (2207)", + "location": [5650, 1419, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e6a0e881-cdbe-4fe2-98dd-1d63debf8e1b", + "name": "Spawner (2207)", + "location": [5647, 1439, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 15, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f17e6176-bbdd-4861-bd51-8aa58d4de7f9", + "name": "Spawner (2207)", + "location": [5651, 1443, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c729c34e-7162-442e-a0bc-0e8b1ca89720", + "name": "Spawner (2207)", + "location": [5651, 1435, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "09685cb4-df4f-4bd2-98fd-4ca2f401d65b", + "name": "Spawner (2207)", + "location": [5643, 1435, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2834e29b-b0b5-4f09-80ea-c0cc6cbdb5a6", + "name": "Spawner (2207)", + "location": [5638, 1439, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 40, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8f08a4ce-78dd-48ca-81e4-1e6d4d35d15e", + "name": "Spawner (2207)", + "location": [5714, 1465, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bb0ec9be-567e-46d8-9045-c498fc51972d", + "name": "Spawner (2207)", + "location": [5723, 1390, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11550ec6-9ce8-4c50-a310-38383b51f230", + "name": "Spawner (2207)", + "location": [5679, 1438, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f3828caa-6ea3-40b5-86a0-21808b5eb4ea", + "name": "Spawner (2207)", + "location": [5715, 1465, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "be64cf9e-3d3a-4e25-9162-50fa7a777f44", + "name": "Spawner (2207)", + "location": [5722, 1390, 1], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c3250304-df94-4c9d-8a15-e9efa6845b65", + "name": "Spawner (2207)", + "location": [5679, 1439, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2f7684e-9246-4b09-b224-f993c15173b9", + "name": "Spawner (2207)", + "location": [5650, 1434, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "23349d09-37ca-4f61-ba48-b1a593d90c6a", + "name": "Spawner (2207)", + "location": [5643, 1435, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Graveyards.json b/Distribution/Data/Spawns/uoml/felucca/Graveyards.json new file mode 100644 index 000000000..ba6fa24da --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Graveyards.json @@ -0,0 +1,146 @@ +[ + { + "type": "Spawner", + "guid": "7dfee8fd-49f0-457d-a88c-f38c65bf2474", + "name": "Spawner (2208)", + "location": [1369, 1475, 10], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [ + { "name": "Spectre", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3ba766a-4852-4c5e-adf1-0276e193317f", + "name": "Spawner (2208)", + "location": [1285, 3731, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Spectre", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "Shade", "maxCount": 2, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "522530f4-50ea-4fbf-9119-b783bd955678", + "name": "Spawner (2208)", + "location": [2438, 1100, 8], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04856160-7e89-4672-a7d9-468b8634c3d6", + "name": "Spawner (2208)", + "location": [4545, 1317, 8], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47aa8449-5b66-401f-ac38-c5b6538627b6", + "name": "Spawner (2208)", + "location": [4543, 1314, 8], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "14b45cdf-987e-4dff-9e9f-10e90e3662e6", + "name": "Spawner (2208)", + "location": [2758, 867, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Spectre", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "43d53b7d-95f9-4332-8d22-33e5f5a1fcf6", + "name": "Spawner (2208)", + "location": [722, 1119, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8bdfe6b3-df80-48b6-8d1e-8e052efde18b", + "name": "Spawner (2208)", + "location": [3407, 2652, 48], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Zombie", "maxCount": 4, "probability": 100 }, + { "name": "Skeleton", "maxCount": 4, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Hythloth.json b/Distribution/Data/Spawns/uoml/felucca/Hythloth.json new file mode 100644 index 000000000..a4cccfd50 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Hythloth.json @@ -0,0 +1,1215 @@ +[ + { + "type": "Spawner", + "guid": "73a4bf99-0ec3-40f5-b6c6-86abb6b5b574", + "name": "Spawner (2209)", + "location": [5923, 48, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d5d1ad7-e8ec-4072-aaa5-60148b6b1633", + "name": "Spawner (2209)", + "location": [5911, 55, 22], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel1", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72a58090-313e-407c-88f3-4589706ce720", + "name": "Spawner (2209)", + "location": [5948, 49, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55c08276-33f5-4a81-b29d-69f5c6fd6eb6", + "name": "Spawner (2209)", + "location": [5954, 26, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8193feec-c25b-484d-ae5e-ed08011bdd4f", + "name": "Spawner (2209)", + "location": [5977, 49, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "74881cf0-b5c7-4405-bcda-94b3d3baeb31", + "name": "Spawner (2209)", + "location": [5995, 62, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireGargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "53c930b6-9da5-4543-be7e-3fc576ef5dd2", + "name": "Spawner (2209)", + "location": [5970, 80, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "74da3036-3d76-47ba-92ef-e2bf3b9e0ef4", + "name": "Spawner (2209)", + "location": [5994, 96, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc62bdf5-4b80-4d0f-b505-da1072a83a35", + "name": "Spawner (2209)", + "location": [5946, 75, 22], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5fdc3c1f-8471-405d-82f1-06f97369c994", + "name": "Spawner (2209)", + "location": [5940, 107, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92be9f7c-c84e-453f-8dc9-fe0080e20789", + "name": "Spawner (2209)", + "location": [5915, 91, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7b394cdd-416f-4d19-b797-2bfa1b7bcab6", + "name": "Spawner (2209)", + "location": [5990, 36, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98d3590c-691c-41df-a0a5-9306c5fcb9f1", + "name": "Spawner (2209)", + "location": [5985, 36, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "80419ca6-262a-4219-852b-bebc9d4ed947", + "name": "Spawner (2209)", + "location": [5989, 25, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b496ff0c-e786-4745-a5f2-15be500def57", + "name": "Spawner (2209)", + "location": [5983, 25, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c0c1350a-0f7f-4638-aa4f-04652af2a7da", + "name": "Spawner (2209)", + "location": [5979, 26, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b06bc9bb-e877-4958-a427-4df43a0748b9", + "name": "Spawner (2209)", + "location": [5981, 84, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e5f7287c-0731-4b03-b76e-0970507df18d", + "name": "Spawner (2209)", + "location": [5966, 70, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6468fabe-a889-451f-abe7-932bee576a28", + "name": "Spawner (2209)", + "location": [5958, 65, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a29d1eb0-e07b-4c26-99c9-27baf3edb014", + "name": "Spawner (2209)", + "location": [5937, 65, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e76e56fd-8b68-4535-b03d-4bed039daa55", + "name": "Spawner (2209)", + "location": [5921, 81, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "de879ff4-9c86-4eec-b48c-959301ff83f3", + "name": "Spawner (2209)", + "location": [5972, 86, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a404b892-ba90-4ac7-be93-43460c5e53f0", + "name": "Spawner (2209)", + "location": [5917, 93, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "15eda36a-c00b-49dc-bfe3-a50d92123c2f", + "name": "Spawner (2209)", + "location": [5929, 77, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cd40cd10-9531-4ccf-bdfe-c92c83bc3bc3", + "name": "Spawner (2209)", + "location": [5945, 78, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2acf18bc-021c-4853-a78e-fa673dbdd76b", + "name": "Spawner (2209)", + "location": [5967, 80, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3d52e48-5710-4ba5-b239-e09a0cd5e872", + "name": "Spawner (2209)", + "location": [5961, 169, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e4ef1d65-a740-496e-a5b4-f85312633052", + "name": "Spawner (2209)", + "location": [5992, 146, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Daemon", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab5876e4-9cba-4a92-afbe-9030402dcbf3", + "name": "Spawner (2209)", + "location": [5945, 175, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "90cd0867-831d-46d8-8d07-8ae5d65b1e47", + "name": "Spawner (2209)", + "location": [5956, 145, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d0d0c487-cfd5-4e29-a45d-f814967797fa", + "name": "Spawner (2209)", + "location": [5917, 156, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "26ac95c2-5558-4077-9998-c5beeb7e8046", + "name": "Spawner (2209)", + "location": [5914, 195, 22], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ccd087df-54f2-4696-bc15-a3be48470aa5", + "name": "Spawner (2209)", + "location": [5918, 223, 44], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "317df32a-2992-4f4a-b978-87eee963d561", + "name": "Spawner (2209)", + "location": [5918, 235, 44], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b34b407a-7f63-4257-bdc1-78f3163a5f75", + "name": "Spawner (2209)", + "location": [5954, 225, 22], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 20, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9bfd8f39-d27c-48e1-bfeb-0162f4881a2e", + "name": "Spawner (2209)", + "location": [5986, 203, 44], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33301994-da3d-4178-ae8c-f7db535784a0", + "name": "Spawner (2209)", + "location": [5983, 169, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb0d4918-8977-44d0-92d9-7e6267b62a7f", + "name": "Spawner (2209)", + "location": [5992, 147, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a782d10c-59bd-4bd3-8eb9-2a78719fd9b4", + "name": "Spawner (2209)", + "location": [5970, 169, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "225cdfdd-2f24-4c49-87ef-0448507da17f", + "name": "Spawner (2209)", + "location": [5997, 145, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ce75ae6-093f-4f10-b5c1-272a9776c2d2", + "name": "Spawner (2209)", + "location": [5978, 185, 44], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b90a3c30-9749-4223-9e81-9188a30c727d", + "name": "Spawner (2209)", + "location": [5962, 217, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "85e48021-924e-49bd-90e0-85b01b1021bc", + "name": "Spawner (2209)", + "location": [5989, 186, 44], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b0687b35-95d6-48e1-b075-55fd1faa5063", + "name": "Spawner (2209)", + "location": [5926, 230, 44], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e978a387-2727-4c39-85c8-16b3c6f2969e", + "name": "Spawner (2209)", + "location": [6094, 154, -22], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93fdf44f-9a95-4a11-b7c2-2d558ca2d437", + "name": "Spawner (2209)", + "location": [6122, 170, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7569f5b-dad0-419e-a784-af9b8bfc60b0", + "name": "Spawner (2209)", + "location": [6086, 178, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37cd9071-b0bb-41f1-93e1-ee50487edc74", + "name": "Spawner (2209)", + "location": [6119, 219, 22], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5b8d01c-2293-42c4-8ed0-4fffcc1e0aff", + "name": "Spawner (2209)", + "location": [6058, 193, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3421a293-1957-4a23-b050-910aaf9f211c", + "name": "Spawner (2209)", + "location": [6033, 199, 22], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "572ca10c-4892-4945-b7d4-2b9df1d51d13", + "name": "Spawner (2209)", + "location": [6052, 156, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db6a7440-9c85-400f-9fbc-9ea9c8c69164", + "name": "Spawner (2209)", + "location": [6121, 166, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "32f0f7d1-6145-48d7-a6bb-6a3015964780", + "name": "Spawner (2209)", + "location": [6124, 159, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70564f6b-1725-414c-9ef4-d2402e6cfde8", + "name": "Spawner (2209)", + "location": [6087, 186, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5a00adac-4b86-4cde-8520-eb6b51d271ec", + "name": "Spawner (2209)", + "location": [6060, 158, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b3ea2162-f70f-40ba-92e0-0d1820c4f479", + "name": "Spawner (2209)", + "location": [6046, 199, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9453078d-36c6-46c3-a5d7-7086780707b8", + "name": "Spawner (2209)", + "location": [6041, 222, 44], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "07267c9b-1455-47ec-885c-418388fe5df6", + "name": "Spawner (2209)", + "location": [6125, 221, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dd36a876-49a9-43df-b35a-a4ce32499d3d", + "name": "Spawner (2209)", + "location": [6120, 229, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50edd29c-ed4b-4321-a8d7-0b7561cc923b", + "name": "Spawner (2209)", + "location": [6092, 187, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "39726a71-b66c-4013-b625-b32a8ba2d721", + "name": "Spawner (2209)", + "location": [6041, 230, 44], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4815a5f0-aca1-4f0e-88b5-ef8aa4df08c2", + "name": "Spawner (2209)", + "location": [6033, 205, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ba8ccb47-e78a-441e-b626-7bd423077d3c", + "name": "Spawner (2209)", + "location": [6058, 153, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92a7ffa1-3c11-424a-a7af-bbb2dc49ac08", + "name": "Spawner (2209)", + "location": [6064, 96, 22], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ee96e4d-2c92-49fa-b01a-40e19ece56d3", + "name": "Spawner (2209)", + "location": [6081, 83, 21], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f67bfb20-8128-4699-936b-37a792b7086c", + "name": "Spawner (2209)", + "location": [6084, 66, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3c0ee712-4025-43a7-9ec1-3821d758bfe5", + "name": "Spawner (2209)", + "location": [6106, 67, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0e5f0f20-5cc7-4e18-87d9-93736f1d71b7", + "name": "Spawner (2209)", + "location": [6116, 86, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireGargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3302304b-f8a4-4638-9218-4bc883fb2811", + "name": "Spawner (2209)", + "location": [6099, 51, 22], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da0917f7-f30c-453c-b767-6f825428eb59", + "name": "Spawner (2209)", + "location": [6107, 37, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7f5c5979-7038-41ed-b055-2d022c8b06e5", + "name": "Spawner (2209)", + "location": [6054, 45, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7b88586b-18c3-45a4-a6de-900e6ceaa864", + "name": "Spawner (2209)", + "location": [6108, 66, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7672fa03-92ce-4855-90ae-66082355c179", + "name": "Spawner (2209)", + "location": [6117, 81, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c0b89eb0-7bc5-47b2-9ff6-32f16b236742", + "name": "Spawner (2209)", + "location": [6058, 92, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5dfa30dc-1590-4e52-a339-0ead51e7dd8e", + "name": "Spawner (2209)", + "location": [6113, 92, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7a2d0ba5-68fe-4672-88da-ecc1a8978c02", + "name": "Spawner (2209)", + "location": [6107, 70, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "349a17a0-eeeb-4b70-bd73-277509e44ac1", + "name": "Spawner (2209)", + "location": [6081, 44, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "53b614b7-1d3f-42dd-8be6-713057bcd618", + "name": "Spawner (2209)", + "location": [6097, 36, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ba4ffaa-796e-4b04-a9d5-6884f33928ef", + "name": "Spawner (2209)", + "location": [6064, 96, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40e9cae3-14ce-4132-be18-0879d45cd22c", + "name": "Spawner (2209)", + "location": [6086, 45, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a2b3facc-dd13-4ecd-9a59-5d7cc88565a3", + "name": "Spawner (2209)", + "location": [6108, 36, 22], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Ice.json b/Distribution/Data/Spawns/uoml/felucca/Ice.json new file mode 100644 index 000000000..f1ace5e4d --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Ice.json @@ -0,0 +1,522 @@ +[ + { + "type": "Spawner", + "guid": "5a62f325-4e00-49c3-8576-fdd9fcf330e5", + "name": "Spawner (2210)", + "location": [5771, 224, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3d3f7414-5249-4ee4-9602-c842af0da38c", + "name": "Spawner (2210)", + "location": [5799, 188, -6], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8a2691a-996a-4c38-834f-897b972bfe2f", + "name": "Spawner (2210)", + "location": [5867, 218, -4], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "FrostOoze", "maxCount": 2, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d31d21b1-8768-4d45-a962-182e67f5a5c0", + "name": "Spawner (2210)", + "location": [5850, 219, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RatmanMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e111cb63-9804-4eac-96ad-290a0b963c8f", + "name": "Spawner (2210)", + "location": [5814, 237, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d7d771c1-605a-4253-965e-dec048f72502", + "name": "Spawner (2210)", + "location": [5752, 144, 9], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "WhiteWyrm", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "03498749-3218-4779-8864-b854b36c79f7", + "name": "Spawner (2210)", + "location": [5839, 159, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "FrostOoze", "maxCount": 1, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "IceElemental", "maxCount": 1, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0614ccac-5ffc-4223-8348-cbb5a0f0cc2d", + "name": "Spawner (2210)", + "location": [5785, 161, -6], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 20, + "entries": [ + { "name": "FrostOoze", "maxCount": 1, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "IceElemental", "maxCount": 1, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61e8233d-f991-4429-9e4e-c7e0d24201d0", + "name": "Spawner (2210)", + "location": [5757, 213, -7], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "IceSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "IceElemental", "maxCount": 4, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8d3fc0f-231c-4d04-b0a3-7191cc4512b2", + "name": "Spawner (2210)", + "location": [5681, 191, -6], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "IceSerpent", "maxCount": 3, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "IceElemental", "maxCount": 4, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4ab984eb-913e-4b21-a9b2-5242726ae03f", + "name": "Spawner (2210)", + "location": [5731, 176, -5], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 35, + "entries": [ + { "name": "IceSerpent", "maxCount": 9, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 9, "probability": 100 }, + { "name": "IceElemental", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33003603-a1fc-43a4-b9d7-2408eb2e5e50", + "name": "Spawner (2210)", + "location": [5848, 226, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb37f510-fff9-4c76-8c51-2d59ddd8f715", + "name": "Spawner (2210)", + "location": [5683, 197, -4], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b96eb341-ef4c-4b7e-ba01-6fb487be56a7", + "name": "Spawner (2210)", + "location": [5721, 147, -1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3bf7b937-9eb6-45fc-81e6-d907910ca62a", + "name": "Spawner (2210)", + "location": [5758, 139, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1cf83838-f5d3-42f9-8390-f445a01ac889", + "name": "Spawner (2210)", + "location": [5754, 211, -6], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fe1657a5-a11c-4fe6-9544-a7ed0d2059b2", + "name": "Spawner (2210)", + "location": [5833, 242, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a3c447a5-c7bb-4826-a5ae-81d8b3863130", + "name": "Spawner (2210)", + "location": [5854, 227, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f2eb3f17-d5f4-4c58-a248-825e46a8a0f8", + "name": "Spawner (2210)", + "location": [5772, 188, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cdb2ea29-4ee5-48c3-9444-018c5003afbd", + "name": "Spawner (2210)", + "location": [5837, 245, -5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9b93a6e4-67e5-4d22-aa51-ff84ea98bb72", + "name": "Spawner (2210)", + "location": [5751, 140, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "479095c0-02ab-4651-9edc-362d77a65a65", + "name": "Spawner (2210)", + "location": [5763, 187, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed18e76c-c568-42fe-9c98-8482c467ff61", + "name": "Spawner (2210)", + "location": [5756, 203, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e89bcb09-65c3-4474-bded-50e62ce3c153", + "name": "Spawner (2210)", + "location": [5712, 145, -34], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4b8dd28c-f72f-45c5-afc4-84dc637c1e0a", + "name": "Spawner (2210)", + "location": [5684, 181, -5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8aba961b-8ce4-49ba-9ec8-7861204815c4", + "name": "Spawner (2210)", + "location": [5831, 358, -1], + "map": "Felucca", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Ratman", "maxCount": 12, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 7, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bca4ab21-15b1-4b04-b698-05d4f8df1f6c", + "name": "Spawner (2210)", + "location": [5838, 355, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72473a5f-3d9b-4f69-b3f9-0d266291468a", + "name": "Spawner (2210)", + "location": [5824, 363, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b080104-77b8-491b-b8f9-44cec8ecbb65", + "name": "Spawner (2210)", + "location": [5680, 306, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "IceFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "51445c30-3ef8-4c0e-95bc-b12e643e39ba", + "name": "Spawner (2210)", + "location": [5669, 330, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "IceFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d5ca5e49-cbb2-4079-81f1-c8679fcecb46", + "name": "Spawner (2210)", + "location": [5696, 303, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f522aee5-3ff7-427c-a3f1-31b162a65fbe", + "name": "Spawner (2210)", + "location": [5682, 314, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "57c38552-9c17-4451-9e53-3a8d2048d2f6", + "name": "Spawner (2210)", + "location": [5666, 331, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "89792e6b-89bb-4c0a-8f45-259ab52f6844", + "name": "Spawner (2210)", + "location": [5676, 331, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Khaldun.json b/Distribution/Data/Spawns/uoml/felucca/Khaldun.json new file mode 100644 index 000000000..b181642a4 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Khaldun.json @@ -0,0 +1,660 @@ +[ + { + "type": "Spawner", + "guid": "92f3597c-2d16-4f72-b8f0-c2e8e855b334", + "name": "Spawner (2228)", + "location": [5569, 1306, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e1b1ac2b-578c-4fcd-8882-4e78047852f2", + "name": "Spawner (2228)", + "location": [5539, 1301, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Cursed", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb1469d0-67e5-44cc-868c-7879e3d04603", + "name": "Spawner (2228)", + "location": [5525, 1296, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "ShadowFiend", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "86fa9698-3b65-416e-b947-55eb169a65b8", + "name": "Spawner (2228)", + "location": [5492, 1294, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3c57a7a8-4376-47c4-bbd0-79c14dfa1e9d", + "name": "Spawner (2228)", + "location": [5492, 1297, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e9d26f46-ca9e-4ef6-b946-1ad646d11bb7", + "name": "Spawner (2228)", + "location": [5458, 1303, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "LysanderGathenwale", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04591799-f2f2-446c-beee-df4ef3a88f1b", + "name": "Spawner (2228)", + "location": [5415, 1312, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a541dc54-0708-4584-8df2-aa8cd9e41efb", + "name": "Spawner (2228)", + "location": [5399, 1298, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff670d55-158a-40f9-b228-7ea17fd81559", + "name": "Spawner (2228)", + "location": [5398, 1299, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "MorgBergen", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7bfe01e3-1ba0-460e-b6e5-7a813bb616dc", + "name": "Spawner (2228)", + "location": [5412, 1330, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "BoneKnight", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9d0c1a06-557d-4eec-83fe-6c6f1340e5a6", + "name": "Spawner (2228)", + "location": [5415, 1331, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6c6e1194-471d-480d-9e8a-1dc63c0abbf3", + "name": "Spawner (2228)", + "location": [5403, 1332, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d1fa73c3-fc93-4630-a81c-eb272cd40185", + "name": "Spawner (2228)", + "location": [5420, 1355, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "TavaraSewel", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d37ef7a2-76ff-4c00-b104-059f764f76ff", + "name": "Spawner (2228)", + "location": [5422, 1355, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a6b68b32-e386-4456-ad05-125d9cba058f", + "name": "Spawner (2228)", + "location": [5435, 1346, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a6e622ec-a297-4c44-89e4-17fc7b127588", + "name": "Spawner (2228)", + "location": [5396, 1371, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "89edcd60-e1d3-42d8-b992-03aaec09a0ef", + "name": "Spawner (2228)", + "location": [5395, 1370, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Cursed", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eec23c16-31c0-4bed-887b-e5fa15891696", + "name": "Spawner (2228)", + "location": [5393, 1369, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "86ef240b-47f8-4918-803a-f043dd0b2e4e", + "name": "Spawner (2228)", + "location": [5400, 1397, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "KhaldunZealot", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b647c6fc-c8cf-4ac4-8862-3f6a288ac886", + "name": "Spawner (2228)", + "location": [5444, 1410, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0b253bce-6be4-4602-9ddc-0f0d03e9e788", + "name": "Spawner (2228)", + "location": [5442, 1411, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e34e42ae-4fba-428d-9460-8627acb32b8e", + "name": "Spawner (2228)", + "location": [5400, 1428, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "KhaldunSummoner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf7e1e29-9832-4df4-adc7-fa167afdbfa6", + "name": "Spawner (2228)", + "location": [5402, 1430, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ee38e2d0-0c75-483e-9503-03634907cab5", + "name": "Spawner (2228)", + "location": [5404, 1431, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a7c42c91-71d1-434e-97a2-6be2419da639", + "name": "Spawner (2228)", + "location": [5425, 1451, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6738216d-c615-4f86-b8b0-433804be6d2e", + "name": "Spawner (2228)", + "location": [5418, 1450, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eb722e41-4267-48cb-a817-1d0851c7895b", + "name": "Spawner (2228)", + "location": [5425, 1484, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "BoneMagi", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e6542e93-a203-4d2e-b2e6-0b93f695c863", + "name": "Spawner (2228)", + "location": [5430, 1485, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "ShadowFiend", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "66ab0f0e-6e24-4639-bce2-93bb86188979", + "name": "Spawner (2228)", + "location": [5433, 1485, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf42c575-292a-4cd2-babf-39233d4f540a", + "name": "Spawner (2228)", + "location": [5458, 1445, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "AncientLich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "71bad24b-147d-4001-b0c6-5d9a00c3ff95", + "name": "Spawner (2228)", + "location": [5459, 1449, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "KhaldunSummoner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "afb085cd-4efa-47da-aec4-494463708012", + "name": "Spawner (2228)", + "location": [5460, 1451, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "KhaldunZealot", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "46062a97-dd3a-4ba6-9b9e-233ebf3bc148", + "name": "Spawner (2228)", + "location": [5465, 1449, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "SkeletalMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d29cf8da-75e3-4e20-b1b8-51ecbde5942a", + "name": "Spawner (2228)", + "location": [5483, 1446, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "ShadowFiend", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d440099e-f38e-4f45-85ce-e0bbd4c50e7d", + "name": "Spawner (2228)", + "location": [5485, 1434, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12feef7c-3ac2-4474-b767-c980db063b56", + "name": "Spawner (2228)", + "location": [5499, 1411, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "ShadowFiend", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "013c84aa-40c1-44d8-b8e9-f6745b1c6b35", + "name": "Spawner (2228)", + "location": [5475, 1402, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "SpectralArmour", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0f6b8570-c002-4a2f-b3ce-745882fe08eb", + "name": "Spawner (2228)", + "location": [5491, 1370, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f53982c9-8d7c-460b-becf-2653818a5939", + "name": "Spawner (2228)", + "location": [5490, 1367, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "SpectralArmour", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6e56c148-f415-4bfe-b722-8b4482b9b782", + "name": "Spawner (2228)", + "location": [5521, 1397, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "ShadowFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5e913247-f30e-4a61-8c3d-b00b42f70f36", + "name": "Spawner (2228)", + "location": [5605, 1343, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 20, + "entries": [{ "name": "HarrowerTentacles", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8ccf110d-809e-4016-ad67-06d151088844", + "name": "Spawner (2228)", + "location": [5536, 1351, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "KhaldunSummoner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4b95ed14-7d77-47ec-b8f0-f8b9775cff31", + "name": "Spawner (2228)", + "location": [5536, 1345, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "KhaldunZealot", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a22f7aa4-357d-4b0e-80bf-46363acc2b42", + "name": "Spawner (2228)", + "location": [5549, 1324, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Cursed", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ddbab85a-3d5b-4417-b289-d7a95aeafff2", + "name": "Spawner (2228)", + "location": [5549, 1332, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "AncientLich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12a4f785-0e30-4d1d-8bac-9030e34043f2", + "name": "Spawner (2228)", + "location": [5505, 1316, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Cursed", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e568c255-b944-4f6d-a246-245d16aac43b", + "name": "Spawner (2228)", + "location": [5452, 1366, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "GrimmochDrummel", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/LostLands.json b/Distribution/Data/Spawns/uoml/felucca/LostLands.json new file mode 100644 index 000000000..fad20116d --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/LostLands.json @@ -0,0 +1,3430 @@ +[ + { + "type": "Spawner", + "guid": "5c677abc-6f32-45bb-a1cb-c0a826f8e95f", + "name": "Spawner (2211)", + "location": [5728, 3209, -3], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [{ "name": "SwampDragon", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f04320b3-02e3-457c-aa3d-85944e1987e1", + "name": "Spawner (2211)", + "location": [5457, 2651, 45], + "map": "Felucca", + "count": 54, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Imp", "maxCount": 15, "probability": 100 }, + { "name": "Scorpion", "maxCount": 15, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 6, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d68603f-69e3-4d13-b972-2648fa6e6f5b", + "name": "Spawner (2211)", + "location": [5457, 2651, 45], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 6, "probability": 100 }, + { "name": "Mummy", "maxCount": 4, "probability": 100 }, + { "name": "Ettin", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbad34f7-3dad-4a18-8143-dfae7052b6a8", + "name": "Spawner (2211)", + "location": [5520, 2890, 33], + "map": "Felucca", + "count": 37, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Imp", "maxCount": 10, "probability": 100 }, + { "name": "Scorpion", "maxCount": 10, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "413f3751-fb8f-4519-bc77-47bb4c6cb964", + "name": "Spawner (2211)", + "location": [5520, 2890, 33], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 4, "probability": 100 }, + { "name": "Mummy", "maxCount": 2, "probability": 100 }, + { "name": "Ettin", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9cf3ac9-5078-48a6-8d1d-8a622fa60024", + "name": "Spawner (2211)", + "location": [5580, 2885, 6], + "map": "Felucca", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "Reaper", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba775ba5-f4a7-462e-88f1-c9962147118a", + "name": "Spawner (2211)", + "location": [5557, 2625, 0], + "map": "Felucca", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 3, "probability": 100 }, + { "name": "Troll", "maxCount": 3, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1eee493b-6795-4b24-b390-72c7cd5b2b9a", + "name": "Spawner (2211)", + "location": [5394, 2530, 6], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Cyclops", "maxCount": 5, "probability": 100 }, + { "name": "Titan", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f0fa954-8f4e-4a76-aa93-5e5bf5cb1735", + "name": "Spawner (2211)", + "location": [5711, 2494, 46], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Imp", "maxCount": 3, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f56b7a46-f8e5-4e85-aa15-9f30cf0c409d", + "name": "Spawner (2211)", + "location": [5711, 2494, 46], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Ettin", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fa987bdc-474a-44da-91be-4b0bcd8cb807", + "name": "Spawner (2211)", + "location": [5765, 2637, 39], + "map": "Felucca", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 3, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 6, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 3, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 4, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12d2b037-bfa7-4ea1-9580-28e0623a2671", + "name": "Spawner (2211)", + "location": [5697, 2626, -2], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "Wyvern", "maxCount": 2, "probability": 100 }, + { "name": "Drake", "maxCount": 4, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6e1001c3-4e95-479b-a49a-850853fd7e59", + "name": "Spawner (2211)", + "location": [5729, 2767, -1], + "map": "Felucca", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 70, + "entries": [ + { "name": "Imp", "maxCount": 8, "probability": 100 }, + { "name": "Scorpion", "maxCount": 8, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc748df2-8d72-4078-a6a4-5a25406770a3", + "name": "Spawner (2211)", + "location": [5729, 2767, -1], + "map": "Felucca", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 70, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "Mummy", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07a9c362-3e90-470f-8020-52320f19ef71", + "name": "Spawner (2211)", + "location": [5724, 2949, 31], + "map": "Felucca", + "count": 44, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 65, + "walkingRange": 75, + "entries": [ + { "name": "HellHound", "maxCount": 8, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 8, "probability": 100 }, + { "name": "FireElemental", "maxCount": 10, "probability": 100 }, + { "name": "Efreet", "maxCount": 4, "probability": 100 }, + { "name": "HellCat", "maxCount": 6, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76d30438-3240-4879-aae2-083e9eecfd1f", + "name": "Spawner (2211)", + "location": [5724, 2949, 31], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 65, + "walkingRange": 75, + "entries": [{ "name": "LavaSnake", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6fae27d7-64f5-4e31-8a41-381a6344a96d", + "name": "Spawner (2211)", + "location": [5863, 2470, 46], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Wyvern", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0051ef48-a9fa-49be-8307-04ed26c3f8fc", + "name": "Spawner (2211)", + "location": [5501, 2361, 27], + "map": "Felucca", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 8, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, + { "name": "Walrus", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8f34e41-4ecb-4747-8300-cabad11db9f6", + "name": "Spawner (2211)", + "location": [5501, 2361, 27], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6ef79240-6f81-42f3-ae82-873af735b246", + "name": "Spawner (2211)", + "location": [5351, 2359, 0], + "map": "Felucca", + "count": 29, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 7, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, + { "name": "Walrus", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04c56ada-2f70-4d20-a168-217066a7920d", + "name": "Spawner (2211)", + "location": [5351, 2359, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e896c88-1cc3-4b00-a042-41b7daa309dc", + "name": "Spawner (2211)", + "location": [5173, 2368, 21], + "map": "Felucca", + "count": 28, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 10, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, + { "name": "Walrus", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bd68fd6-cde9-45a8-8a99-8173b0e61e54", + "name": "Spawner (2211)", + "location": [5173, 2368, 21], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "PolarBear", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "27c7c466-0a83-4356-8b5d-7aab73b1dde1", + "name": "Spawner (2211)", + "location": [6091, 2379, 25], + "map": "Felucca", + "count": 28, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 60, + "entries": [ + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 10, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, + { "name": "Walrus", "maxCount": 3, "probability": 100 }, + { "name": "PolarBear", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9fcbab64-2922-4c73-b1a1-8c7304252e57", + "name": "Spawner (2211)", + "location": [5967, 2370, 44], + "map": "Felucca", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 4, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c6585a9-e298-4753-a981-b77e13fec541", + "name": "Spawner (2211)", + "location": [5967, 2370, 44], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7a81959d-4cbc-487c-a41c-867dfa194ca0", + "name": "Spawner (2211)", + "location": [5817, 2367, 42], + "map": "Felucca", + "count": 23, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 3, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 3, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f706d02f-df96-4378-8e75-697f931d9eeb", + "name": "Spawner (2211)", + "location": [5817, 2367, 42], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "26b4a453-57c9-4bcf-b19b-a5caace69ace", + "name": "Spawner (2211)", + "location": [5756, 3453, -2], + "map": "Felucca", + "count": 42, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "Panther", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ab1c20f0-33bf-4b17-9d3e-b4a1bf3a7dc9", + "name": "Spawner (2211)", + "location": [5985, 3441, 17], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 90, + "entries": [ + { "name": "GiantToad", "maxCount": 8, "probability": 100 }, + { "name": "SwampDragon", "maxCount": 5, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "Alligator", "maxCount": 12, "probability": 100 }, + { "name": "SewerRat", "maxCount": 11, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c86b6986-06f2-4bfd-83a9-eb305dffd798", + "name": "Spawner (2211)", + "location": [5985, 3441, 17], + "map": "Felucca", + "count": 36, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 90, + "entries": [ + { "name": "BullFrog", "maxCount": 15, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 5, "probability": 100 }, + { "name": "Bogling", "maxCount": 13, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4fdfdb8-b446-4cad-a4d5-700f6e387112", + "name": "Spawner (2211)", + "location": [5220, 3664, -1], + "map": "Felucca", + "count": 23, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 3, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 6, "probability": 100 }, + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "507fcaa7-e8ed-4ce1-93db-f6f6d4bc5abc", + "name": "Spawner (2211)", + "location": [5212, 3619, 0], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "SavageRider", "maxCount": 3, "probability": 100 }, + { "name": "Savage", "maxCount": 9, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "048db5e6-adcb-4fec-91e7-3d12011d05b0", + "name": "Spawner (2211)", + "location": [5347, 3449, 17], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b35c2359-f228-4ede-ab15-7677b251219d", + "name": "Spawner (2211)", + "location": [5243, 3548, 14], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54cb046e-98e1-4971-8b9b-0b62d687c1ab", + "name": "Spawner (2211)", + "location": [5250, 3449, 16], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "270bb58a-fbba-4af0-98b2-3cece2112336", + "name": "Spawner (2211)", + "location": [5349, 3547, -1], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fba20373-697a-475b-8db9-52501d460fce", + "name": "Spawner (2211)", + "location": [5201, 3162, 53], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "150865b5-2285-49ea-94d4-42554405d181", + "name": "Spawner (2211)", + "location": [5201, 3162, 53], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15e3c862-5e27-47f3-ba4d-8a9ee45c3623", + "name": "Spawner (2211)", + "location": [5251, 3301, -2], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e69642d-acde-40f0-bc27-0e07d9292977", + "name": "Spawner (2211)", + "location": [5251, 3301, -2], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4bc10a0b-643a-434c-af08-7b789cfcd954", + "name": "Spawner (2211)", + "location": [5250, 3401, 1], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "673c07e2-de3f-4228-9c93-fcaa22aae807", + "name": "Spawner (2211)", + "location": [5250, 3401, 1], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "273dd9e4-983d-4029-8b2b-3e2ea1178a95", + "name": "Spawner (2211)", + "location": [5351, 3401, 30], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a102eab-82dd-45ca-9071-d38e2d759ce6", + "name": "Spawner (2211)", + "location": [5351, 3401, 30], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "620213e0-2aad-4795-9d8a-1c326bb59212", + "name": "Spawner (2211)", + "location": [6018, 3673, 18], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9d0907cc-7569-4001-9ef3-6c95a3273262", + "name": "Spawner (2211)", + "location": [6018, 3673, 18], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9cf77fad-29ee-4dc8-a174-7adf1ba52831", + "name": "Spawner (2211)", + "location": [6018, 3673, 18], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ac377e6-e430-4412-8fcd-8ed365924d58", + "name": "Spawner (2211)", + "location": [6018, 3673, 18], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "66e10caf-9bd5-4f1c-8e03-6c0b8ef97cda", + "name": "Spawner (2211)", + "location": [6022, 3773, 18], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15908c1e-bc66-4149-9c00-c11fe35e5a17", + "name": "Spawner (2211)", + "location": [6022, 3773, 18], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9d49bec-96ad-41ab-8397-20f8cb54ab0f", + "name": "Spawner (2211)", + "location": [6022, 3773, 18], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9b9dd7d-c27f-48e4-99b3-23cd89fc19cb", + "name": "Spawner (2211)", + "location": [6022, 3773, 18], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f8499d9a-396d-46a9-abba-b78ce9de9e74", + "name": "Spawner (2211)", + "location": [6022, 2874, 16], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3c7e0c0-82d2-4fa6-b8d8-ceb9131c811f", + "name": "Spawner (2211)", + "location": [6022, 2874, 16], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e646890b-dfcb-4572-bc2b-c4683711befa", + "name": "Spawner (2211)", + "location": [6022, 2874, 16], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04ebb8af-b6c7-488c-816c-b37805270c49", + "name": "Spawner (2211)", + "location": [6022, 2874, 16], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc3fc7a6-6646-4c86-8830-883c67dd0938", + "name": "Spawner (2211)", + "location": [6022, 3971, 17], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f31f301e-888f-4493-a562-b05219ff599d", + "name": "Spawner (2211)", + "location": [6022, 3971, 17], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4da8db24-068c-406f-a561-6128621f78e6", + "name": "Spawner (2211)", + "location": [6022, 3971, 17], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ed54effc-7e93-4d1e-be86-690a39878733", + "name": "Spawner (2211)", + "location": [6022, 3971, 17], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d59b4f4-57c3-43ef-9fc2-0364e61a718c", + "name": "Spawner (2211)", + "location": [5920, 3675, 12], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2665bd3-8dfc-4e5d-aa4b-0810ebf249a3", + "name": "Spawner (2211)", + "location": [5920, 3675, 12], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6336daa-16af-48dd-ad9a-04b2aea717ca", + "name": "Spawner (2211)", + "location": [5920, 3675, 12], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "616334e1-53ea-49c0-8901-856c483a69e5", + "name": "Spawner (2211)", + "location": [5920, 3675, 12], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "942c695f-f39f-4c28-8704-9714152cdefd", + "name": "Spawner (2211)", + "location": [5921, 3777, 28], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b532fb5d-8a9a-4ee4-8358-e7e2ef4095d9", + "name": "Spawner (2211)", + "location": [5921, 3777, 28], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20911691-e6c3-4c40-87d1-ee67a34d2669", + "name": "Spawner (2211)", + "location": [5921, 3777, 28], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e14998fb-042e-4d70-809a-d5ac583494b0", + "name": "Spawner (2211)", + "location": [5921, 3777, 28], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa42da05-496c-43fd-8178-a7fdb9ebfe63", + "name": "Spawner (2211)", + "location": [5919, 3875, 16], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4f901b5-6562-4689-9d5a-3ad3e3ad8a3f", + "name": "Spawner (2211)", + "location": [5919, 3875, 16], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "092875e2-4178-4845-97e9-1d70ab565933", + "name": "Spawner (2211)", + "location": [5919, 3875, 16], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fde4de4d-8369-49e1-944f-b76d4a2e42d2", + "name": "Spawner (2211)", + "location": [5919, 3875, 16], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "775ba293-8e3a-4d62-bf10-7e002b3b2ec5", + "name": "Spawner (2211)", + "location": [5918, 3975, 1], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55a13cb2-cc95-4d1b-8498-2babc070ce78", + "name": "Spawner (2211)", + "location": [5918, 3975, 1], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "050b5b91-6840-4ec9-adca-c73658982e58", + "name": "Spawner (2211)", + "location": [5918, 3975, 1], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae2d2521-6a54-46cc-8bd6-4e4ff10242e8", + "name": "Spawner (2211)", + "location": [5918, 3975, 1], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a98d669c-759b-45b5-96ad-3a1153f38d9d", + "name": "Spawner (2211)", + "location": [5820, 3675, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "741769ff-9984-44cf-a8b0-bdc393beffc2", + "name": "Spawner (2211)", + "location": [5820, 3675, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f72695d4-8b46-4aef-acd3-39aedce54767", + "name": "Spawner (2211)", + "location": [5820, 3675, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "30f49430-b98b-4da2-89b6-cfbf3f69182e", + "name": "Spawner (2211)", + "location": [5820, 3675, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "738a7321-91fd-4205-97a0-cb9ef78e5ad0", + "name": "Spawner (2211)", + "location": [5820, 3775, 48], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a9fe475-26ea-4f9c-a73b-e33ab2674130", + "name": "Spawner (2211)", + "location": [5820, 3775, 48], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdfb73eb-b35a-4701-afe9-0487da724239", + "name": "Spawner (2211)", + "location": [5820, 3775, 48], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b831bbb7-b063-4039-9d54-bdaf8c0a4cbf", + "name": "Spawner (2211)", + "location": [5820, 3775, 48], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "421bbe19-6e35-4125-a521-0a5294553b06", + "name": "Spawner (2211)", + "location": [5818, 3876, 18], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50aa6367-2c0f-475a-a0f7-81ee98f66244", + "name": "Spawner (2211)", + "location": [5818, 3876, 18], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "872072bf-8139-497f-8338-76cc9b06d93e", + "name": "Spawner (2211)", + "location": [5818, 3876, 18], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf8af0ff-9824-45cb-b254-bb762019c7ca", + "name": "Spawner (2211)", + "location": [5818, 3876, 18], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93c70268-f78d-436e-b3c0-3dd39fbadad3", + "name": "Spawner (2211)", + "location": [5819, 3974, 16], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d51c7215-68f4-4c9f-aa0c-c15f8c0555ed", + "name": "Spawner (2211)", + "location": [5819, 3974, 16], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3b2a1a28-2c30-483f-833c-1a9ad40bd153", + "name": "Spawner (2211)", + "location": [5819, 3974, 16], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf46ef5b-0f23-4bd9-93ce-b6bf724fd816", + "name": "Spawner (2211)", + "location": [5819, 3974, 16], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e64d60d3-0d19-4878-a69c-90548a99ef6e", + "name": "Spawner (2211)", + "location": [5721, 3675, 22], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2694242b-1d3f-4dfc-96c0-892202e3b950", + "name": "Spawner (2211)", + "location": [5721, 3675, 22], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "42d4b8fa-99ad-412e-9df7-bd3f92c023ac", + "name": "Spawner (2211)", + "location": [5721, 3675, 22], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "80c6a933-7596-4e44-9ecb-006e36b52a4c", + "name": "Spawner (2211)", + "location": [5721, 3675, 22], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54cc6589-3bd8-4258-a88c-834c928a2257", + "name": "Spawner (2211)", + "location": [5722, 3776, 32], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0fe27512-76eb-4830-939d-36ab2aafaa42", + "name": "Spawner (2211)", + "location": [5722, 3776, 32], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3108d12-e98e-4525-aea1-18bcdce8cb3f", + "name": "Spawner (2211)", + "location": [5722, 3776, 32], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d1cecc3-832e-4898-ae75-bce89940fb02", + "name": "Spawner (2211)", + "location": [5722, 3776, 32], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21f61927-4b6f-4b7d-8c37-4e0dbab8a750", + "name": "Spawner (2211)", + "location": [5721, 3876, -3], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1af1466-54a7-4db4-a6ba-d8867ab87512", + "name": "Spawner (2211)", + "location": [5721, 3876, -3], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88937169-f283-4c78-9d28-7f03ac03872c", + "name": "Spawner (2211)", + "location": [5721, 3876, -3], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "585f8ed2-b655-4de7-b9c8-e937bd858d43", + "name": "Spawner (2211)", + "location": [5721, 3876, -3], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8bbb0aca-5066-44cc-80b6-2a6e4862d6df", + "name": "Spawner (2211)", + "location": [5721, 3975, 21], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e79036f2-c56f-4467-8312-c95bf651af1c", + "name": "Spawner (2211)", + "location": [5721, 3975, 21], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4562d6b-3ea3-46c0-9a75-d102a5da6fef", + "name": "Spawner (2211)", + "location": [5721, 3975, 21], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a31f2cf-8495-4789-b1d7-3a480f373f73", + "name": "Spawner (2211)", + "location": [5721, 3975, 21], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41d9a9e4-66be-4a71-a1ea-b8dec95e6258", + "name": "Spawner (2211)", + "location": [5623, 3676, -3], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c010aa0-3974-42d1-ace5-aefc3014eda1", + "name": "Spawner (2211)", + "location": [5623, 3676, -3], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9d472611-6a0e-4aca-ba28-e3b6f3e2bb1e", + "name": "Spawner (2211)", + "location": [5623, 3676, -3], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95f3b7c6-e49c-483f-8598-6a5c71f6e959", + "name": "Spawner (2211)", + "location": [5623, 3676, -3], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "783e28bf-94fe-445d-b357-a4009b10eab0", + "name": "Spawner (2211)", + "location": [5621, 3775, 2], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d7e46421-f97f-427e-885c-2e3092bbbb23", + "name": "Spawner (2211)", + "location": [5621, 3775, 2], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "354f9453-76f6-4cc8-8255-873544d38355", + "name": "Spawner (2211)", + "location": [5621, 3775, 2], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6488c8bb-5ced-4f2f-9f3a-6f80958d1ae6", + "name": "Spawner (2211)", + "location": [5621, 3775, 2], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5702bcbc-9e12-44b5-a382-d2a56acda20f", + "name": "Spawner (2211)", + "location": [5623, 3875, 15], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2962e5e-fa41-4e95-8870-66cf709d80c5", + "name": "Spawner (2211)", + "location": [5623, 3875, 15], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21cba4e8-69f4-49b4-bdc2-1b5893001823", + "name": "Spawner (2211)", + "location": [5623, 3875, 15], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fdc76541-5d27-4dcf-ab0e-fb96e4beb7e8", + "name": "Spawner (2211)", + "location": [5623, 3875, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b8e8c6a-2fb2-443a-a5ac-6ba23572d23c", + "name": "Spawner (2211)", + "location": [5624, 3975, -3], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "64a11ac5-1b77-4d46-beed-78d63b227f89", + "name": "Spawner (2211)", + "location": [5624, 3975, -3], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "35afdfbe-5809-4afe-bf7a-b2de7ef3e6a4", + "name": "Spawner (2211)", + "location": [5624, 3975, -3], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a5e6b73-0af4-4461-96af-e980adc67771", + "name": "Spawner (2211)", + "location": [5624, 3975, -3], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62947b20-aa82-480c-887f-bbacd25e698f", + "name": "Spawner (2211)", + "location": [5523, 3676, 45], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7a43ad68-7a8b-43f6-b8e4-2f5bce9b3c1e", + "name": "Spawner (2211)", + "location": [5523, 3676, 45], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d4a0fe3-6d82-42e1-83a8-8eea8de98aee", + "name": "Spawner (2211)", + "location": [5523, 3676, 45], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6ff7fdd5-e4c7-41ea-958f-8b030f8aca55", + "name": "Spawner (2211)", + "location": [5523, 3676, 45], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21157414-21f7-4eb1-84e8-cb9ffb3b0b1b", + "name": "Spawner (2211)", + "location": [5521, 3775, 8], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d35c378-e89b-4bcc-9074-bd08850092de", + "name": "Spawner (2211)", + "location": [5521, 3775, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "38fd8a6b-4deb-4f8f-87be-70dcfd5b4055", + "name": "Spawner (2211)", + "location": [5521, 3775, 8], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4438dccc-cae8-4b97-8079-baee38c36b7a", + "name": "Spawner (2211)", + "location": [5521, 3775, 8], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8ac67eb-7ed1-4f41-80de-549bc900fccd", + "name": "Spawner (2211)", + "location": [5521, 3875, -1], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23eb1938-b1a7-400d-a9dd-12d6f514a3c5", + "name": "Spawner (2211)", + "location": [5521, 3875, -1], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "224ddc6f-f8ea-4752-8209-4a8484eeca66", + "name": "Spawner (2211)", + "location": [5521, 3875, -1], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8be2f678-bffa-4aa1-acf4-d4e3d2cfd92d", + "name": "Spawner (2211)", + "location": [5521, 3875, -1], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b991a2d-71f9-42d1-9d66-7a7a4d6ba42e", + "name": "Spawner (2211)", + "location": [5478, 3587, 3], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6686565b-fadf-4063-9125-c8fe0a138653", + "name": "Spawner (2211)", + "location": [5478, 3587, 3], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "18e0751c-cee9-4120-8873-04f87bfe4772", + "name": "Spawner (2211)", + "location": [5478, 3587, 3], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a30ea72-ec30-49a4-be3f-1f7a4a0217b2", + "name": "Spawner (2211)", + "location": [5478, 3587, 3], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "675ac5d1-85b0-4fe0-89c3-011c4ee3be55", + "name": "Spawner (2211)", + "location": [5520, 3939, 42], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "833ac732-4f3f-4863-8b88-f1b52673d063", + "name": "Spawner (2211)", + "location": [5784, 3951, 20], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [{ "name": "FrenziedOstard", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e118382e-b3f3-4e8c-ae91-52fcb3eb6e79", + "name": "Spawner (2211)", + "location": [5764, 2585, -4], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 75, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef9989f7-1231-48a7-b6ff-428d1df26bce", + "name": "Spawner (2211)", + "location": [5838, 2651, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 75, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3781051c-c4bb-4e65-b4f5-5e09c44b9a31", + "name": "Spawner (2211)", + "location": [5218, 3786, 1], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "273c3edf-416a-4113-b68a-fe94b128b63d", + "name": "Spawner (2211)", + "location": [5218, 3786, 1], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6ad3a966-1fcf-45e8-b36a-f3f2c5cbd374", + "name": "Spawner (2211)", + "location": [5371, 3820, -2], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3612f07e-f714-4ad8-bec2-e7bbca18b0ea", + "name": "Spawner (2211)", + "location": [5371, 3820, -2], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c05974e-e051-411c-8e9d-991adc1b11d7", + "name": "Spawner (2211)", + "location": [5368, 3887, 0], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5821f460-0bda-4984-8a75-10c711c09f64", + "name": "Spawner (2211)", + "location": [5368, 3887, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd202c62-aa55-4c9c-b3f6-1a0a6d44c32e", + "name": "Spawner (2211)", + "location": [5367, 4003, 19], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc8c9793-e6fe-48b5-9b83-28d3178fa5a0", + "name": "Spawner (2211)", + "location": [5367, 4003, 19], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b424a6e3-dd2b-4140-a335-fa3916397531", + "name": "Spawner (2211)", + "location": [5466, 4005, -1], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5d11127-71e9-4521-88ae-739adb829541", + "name": "Spawner (2211)", + "location": [5466, 4005, -1], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d41bb4a9-6e4c-4959-88ce-0f7cc1e518c3", + "name": "Spawner (2211)", + "location": [5194, 3885, -1], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 32, + "walkingRange": 45, + "entries": [ + { "name": "Bull", "maxCount": 10, "probability": 100 }, + { "name": "GreatHart", "maxCount": 6, "probability": 100 }, + { "name": "Cow", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33c93a22-4e4d-4b81-a122-0fc480c300a2", + "name": "Spawner (2211)", + "location": [5200, 3895, 2], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bc4fc91-8bea-4601-990a-f5bfac3d090e", + "name": "Spawner (2211)", + "location": [5159, 3913, 1], + "map": "Felucca", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 10, + "entries": [ + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 1, "probability": 100 }, + { "name": "Bull", "maxCount": 1, "probability": 100 }, + { "name": "Chicken", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13efbe43-2edd-44f9-8282-5b3274f79912", + "name": "Spawner (2211)", + "location": [5570, 3153, 14], + "map": "Felucca", + "count": 25, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 80, + "entries": [ + { "name": "Gorilla", "maxCount": 6, "probability": 100 }, + { "name": "Panther", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d03b6997-0732-409a-a2ec-f92413ac6a08", + "name": "Spawner (2211)", + "location": [5440, 3292, -4], + "map": "Felucca", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 56, + "walkingRange": 62, + "entries": [ + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "Panther", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84a12c7e-b2f6-4713-8d14-5c768f38358b", + "name": "Spawner (2211)", + "location": [5566, 3292, 4], + "map": "Felucca", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 56, + "walkingRange": 62, + "entries": [ + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "Panther", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91259235-0484-4698-906e-bf63a077d599", + "name": "Spawner (2211)", + "location": [5454, 3136, -60], + "map": "Felucca", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "TerathanMatriarch", "maxCount": 2, "probability": 100 }, + { "name": "TerathanAvenger", "maxCount": 4, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 10, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2e9a141-6f17-4844-bbfa-a8e9adacbc50", + "name": "Spawner (2211)", + "location": [5490, 3071, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "00b71fd9-b553-4846-8cc6-5ceaf42ba4b3", + "name": "Spawner (2211)", + "location": [5467, 3078, -3], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db44f2a2-5df8-4de6-94c9-cbe8f792f3e5", + "name": "Spawner (2211)", + "location": [5446, 3074, 3], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "440d81ae-8e54-44db-b6de-909abe0e75b8", + "name": "Spawner (2211)", + "location": [5432, 3079, -4], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f19e0ef2-66a2-48b9-a6df-83027370fd1d", + "name": "Spawner (2211)", + "location": [5408, 3077, 16], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/OrcCaves.json b/Distribution/Data/Spawns/uoml/felucca/OrcCaves.json new file mode 100644 index 000000000..176f5910e --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/OrcCaves.json @@ -0,0 +1,302 @@ +[ + { + "type": "Spawner", + "guid": "572a2fac-11c7-4583-837a-1b03eae15f85", + "name": "Spawner (2212)", + "location": [5146, 1995, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "Orc", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03b59250-db54-481c-85f4-1d0b8b433252", + "name": "Spawner (2212)", + "location": [5153, 1971, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a1dbbaf6-03e3-42f2-ba42-35d88349fd0c", + "name": "Spawner (2212)", + "location": [5153, 1961, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "DireWolf", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5145b262-72b3-4bf2-8a73-91efe509ce18", + "name": "Spawner (2212)", + "location": [5144, 1960, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "OrcishLord", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fe0be93b-5f45-40b0-9169-16df378c0e6e", + "name": "Spawner (2212)", + "location": [5141, 1968, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "OrcCaptain", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ee800978-f2bf-47e0-8e19-c7f5fe0b33bc", + "name": "Spawner (2212)", + "location": [5332, 1365, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0e858157-adc4-4963-a34b-101fefb109d1", + "name": "Spawner (2212)", + "location": [5308, 1372, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Noble", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "593ee3b4-df42-426d-8976-faced7cd2435", + "name": "Spawner (2212)", + "location": [5302, 1355, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 12, + "entries": [ + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a6833ee-9e32-4900-ad6e-87926e8fb427", + "name": "Spawner (2212)", + "location": [5316, 1332, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "GiantRat", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70960aad-63ab-4bc2-9784-0ae5db883fe1", + "name": "Spawner (2212)", + "location": [5331, 1346, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "DireWolf", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40c5eacc-9b5d-49f4-b13d-c605a32f6816", + "name": "Spawner (2212)", + "location": [5354, 1332, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01ae830b-c7f9-4857-8a6b-4bacca6758ad", + "name": "Spawner (2212)", + "location": [5356, 1304, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [ + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "471bb8b3-f768-4654-a3ae-9517ecc8dd45", + "name": "Spawner (2212)", + "location": [5317, 1309, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d313d866-1199-45fc-a72c-018d146bbe8c", + "name": "Spawner (2212)", + "location": [5300, 1314, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81a40400-c93d-42f3-8f19-0e9492198a82", + "name": "Spawner (2212)", + "location": [5292, 1316, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Corpser", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d01dca90-9fac-438e-9660-a93e9df7fc58", + "name": "Spawner (2212)", + "location": [5281, 2029, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1bff583d-3643-4e60-9a44-e3dea6882a02", + "name": "Spawner (2212)", + "location": [5308, 2005, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [ + { "name": "OrcBrute", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d24f1f9-20ff-4ee1-8f31-8304e2e4822d", + "name": "Spawner (2212)", + "location": [5310, 1969, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [ + { "name": "OrcBomber", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a70f186a-94a1-4f54-963f-5c039c7d4006", + "name": "Spawner (2212)", + "location": [5348, 2011, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 4, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/shared/trammel/Outdoors.json b/Distribution/Data/Spawns/uoml/felucca/Outdoors.json similarity index 78% rename from Distribution/Data/Spawns/shared/trammel/Outdoors.json rename to Distribution/Data/Spawns/uoml/felucca/Outdoors.json index fc60ca7bb..a555540e6 100644 --- a/Distribution/Data/Spawns/shared/trammel/Outdoors.json +++ b/Distribution/Data/Spawns/uoml/felucca/Outdoors.json @@ -1,10 +1,181 @@ [ { - "$type": "Spawner", - "guid": "0042c352-ea0e-4d42-860c-5f3462b43c61", - "name": "Spawner (113)", - "location": [893, 1861, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "33c2dd22-28c7-4365-8d2a-61a1594edcc5", + "name": "Spawner (2213)", + "location": [3619, 2814, 21], + "map": "Felucca", + "count": 17, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "HordeMinion", "maxCount": 17, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a9fffefd-9314-4e62-b029-0b55d7a51418", + "name": "Spawner (2213)", + "location": [3619, 2814, 21], + "map": "Felucca", + "count": 17, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Mongbat", "maxCount": 17, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "38809d23-8eee-4351-a9fd-337ee44ceb7c", + "name": "Spawner (2213)", + "location": [1147, 3524, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "216f74af-209f-4943-ac78-cb2703ba7f06", + "name": "Spawner (2213)", + "location": [1266, 1982, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f886f7b8-60a5-45c0-bf80-05032137df23", + "name": "Spawner (2213)", + "location": [1249, 2030, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "74a9ed57-25f8-4fe2-8286-2696225764f3", + "name": "Spawner (2213)", + "location": [1232, 2093, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3f9d6f0d-92a3-4b72-a6aa-d8bfba12e7f7", + "name": "Spawner (2213)", + "location": [1140, 2087, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55af7b2a-3f27-4bf0-9d5e-bcedbdb3b76b", + "name": "Spawner (2213)", + "location": [1089, 2033, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10667ef8-7144-444e-a63b-8bfe0ff5c55d", + "name": "Spawner (2213)", + "location": [1067, 2117, 5], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c51a903d-62e2-4963-81f0-c6a4a2efb2d0", + "name": "Spawner (2213)", + "location": [666, 1825, 0], + "map": "Felucca", "count": 8, "minDelay": "01:00:00", "maxDelay": "01:00:00", @@ -19,61 +190,4131 @@ ] }, { - "$type": "Spawner", - "guid": "00b85da5-f02c-4ddb-88c0-11c677a799c1", - "name": "Spawner (113)", - "location": [1037, 1325, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "156f17d7-f242-4657-87fd-48b947ad6f65", + "name": "Spawner (2213)", + "location": [741, 1838, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f63dafa0-bd09-499c-a34d-e3555d08fdc9", + "name": "Spawner (2213)", + "location": [733, 1937, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "70cee935-0141-46ca-a61d-693c873af487", + "name": "Spawner (2213)", + "location": [798, 1935, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "def51564-1874-418a-972b-13e052b9d80b", + "name": "Spawner (2213)", + "location": [819, 1841, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3199ad8-a84a-4919-8353-b3950ebbd19f", + "name": "Spawner (2213)", + "location": [893, 1861, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd52d8c8-0b49-4626-94c2-55a0aba0d970", + "name": "Spawner (2213)", + "location": [879, 1956, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "004df2b4-e99d-4946-8dec-4b088fa75ea3", + "name": "Spawner (2213)", + "location": [809, 1615, 0], + "map": "Felucca", "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", + "minDelay": "01:00:00", + "maxDelay": "01:00:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "00f29d73-3ef1-4c12-bdd7-ff19293fecef", - "name": "Spawner (113)", - "location": [854, 1686, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "Brigand", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "02eea69a-a67a-4244-b57d-d32b4d80510c", - "name": "Spawner (113)", - "location": [1833, 1245, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "024c3335-3bed-4475-bbc8-cda05fca9d6e", + "name": "Spawner (2213)", + "location": [4461, 3106, 0], + "map": "Felucca", "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "OrcishMage", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "890ffbb5-4dab-44d2-b7b0-7d11528f7bb0", + "name": "Spawner (2213)", + "location": [4729, 3485, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc0d728c-fa4a-4913-8834-555178746625", + "name": "Spawner (2213)", + "location": [4519, 3097, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "381a50a4-7600-462c-b95e-9c79f96e610e", + "name": "Spawner (2213)", + "location": [4613, 3272, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0d4101a9-dd3b-4e5e-b95b-f538addb8324", + "name": "Spawner (2213)", + "location": [4576, 3356, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "860745e5-2133-46bb-bf8b-b8cd1273b204", + "name": "Spawner (2213)", + "location": [4658, 3376, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6fad7f26-0faf-4aa1-a25f-c050cc1e0143", + "name": "Spawner (2213)", + "location": [4607, 3480, 29], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cbf48240-08c5-4939-9f2b-ec6e972b25a9", + "name": "Spawner (2213)", + "location": [3485, 2595, 12], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } + { "name": "Brigand", "maxCount": 3, "probability": 100 }, + { "name": "Harpy", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Troll", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "03369351-f3c2-4651-907d-2e9ec6fcfb6b", - "name": "Spawner (113)", - "location": [837, 361, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "4124fe40-7dde-4264-8393-5f92e87bdd80", + "name": "Spawner (2213)", + "location": [3446, 2741, 49], + "map": "Felucca", + "count": 7, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Brigand", "maxCount": 7, "probability": 100 }, + { "name": "Harpy", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21abb157-384a-4001-9ba6-743cd407fe3f", + "name": "Spawner (2213)", + "location": [3459, 2655, 9], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Brigand", "maxCount": 3, "probability": 100 }, + { "name": "Harpy", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Troll", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8211671-8686-48e8-ae56-66a5d815ecda", + "name": "Spawner (2213)", + "location": [3379, 2693, 35], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Brigand", "maxCount": 3, "probability": 100 }, + { "name": "Harpy", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Troll", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "769cc51f-efed-48b2-ba94-013f544fcb5a", + "name": "Spawner (2213)", + "location": [3411, 2592, 44], + "map": "Felucca", + "count": 6, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Brigand", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Spectre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2e12985-afee-4b4d-8981-d50e99e1a1d6", + "name": "Spawner (2213)", + "location": [3464, 2503, 49], + "map": "Felucca", + "count": 7, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Brigand", "maxCount": 7, "probability": 100 }, + { "name": "Harpy", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dcd101fb-c10a-4fe2-a5b2-9e5d7ac965ca", + "name": "Spawner (2213)", + "location": [3168, 629, 8], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa67c8fa-91bc-4db4-9db9-ce9ba0440401", + "name": "Spawner (2213)", + "location": [3172, 700, 8], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "324e278d-d03c-4a19-9f7c-90983f0f7372", + "name": "Spawner (2213)", + "location": [3173, 771, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f69aa349-7dd7-4a24-945e-f75cd274cfba", + "name": "Spawner (2213)", + "location": [1473, 229, 22], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68e9fc19-85e7-41d6-9791-d22fdad0e11b", + "name": "Spawner (2213)", + "location": [1337, 293, 22], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5630cd94-517e-4ddf-856d-fe59b8d2fc7a", + "name": "Spawner (2213)", + "location": [1184, 217, 27], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8aa3fee7-2653-4bb2-828b-c1952fa27afe", + "name": "Spawner (2213)", + "location": [1284, 433, 6], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47035c00-635d-410e-9208-3d07ee948ff6", + "name": "Spawner (2213)", + "location": [1203, 502, 30], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a5ccea8-791c-4a24-a5b8-1107fbd32673", + "name": "Spawner (2213)", + "location": [1300, 607, 15], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12281c4e-e6de-4abf-8313-db1b6895eaee", + "name": "Spawner (2213)", + "location": [1200, 633, 6], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46abf595-aaae-4e1c-85e6-8cb7b463409c", + "name": "Spawner (2213)", + "location": [1162, 704, 6], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "64615b94-7440-4c4b-873a-4b530cfd29e5", + "name": "Spawner (2213)", + "location": [1130, 830, 9], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd655b49-23cd-410b-b7a4-8c959b5bd7d5", + "name": "Spawner (2213)", + "location": [1013, 980, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "499d9dab-ec87-4739-8055-7ffd7a54cecf", + "name": "Spawner (2213)", + "location": [848, 917, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47fe6452-2b6d-486b-850c-86cb477c5e9c", + "name": "Spawner (2213)", + "location": [840, 1063, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ffe958cd-13e8-4caf-9694-af4cb65b8842", + "name": "Spawner (2213)", + "location": [1038, 1149, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13d7ad11-9375-4baf-8a4e-27993057790e", + "name": "Spawner (2213)", + "location": [844, 1235, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3177e9c8-ff1d-48b8-8280-3a66a202526b", + "name": "Spawner (2213)", + "location": [617, 1326, 0], + "map": "Felucca", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c25e54a0-e617-4cae-b5d0-0aeffa888d1a", + "name": "Spawner (2213)", + "location": [1831, 449, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85b3d37c-21b5-484e-841c-a2344d3a911b", + "name": "Spawner (2213)", + "location": [1832, 517, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07aff726-fbd5-4a47-a768-3ec347652b7f", + "name": "Spawner (2213)", + "location": [1749, 474, 6], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60caf162-33d5-4a0d-a754-04c8327bf10c", + "name": "Spawner (2213)", + "location": [1636, 532, 22], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1687b6e3-dbd1-4203-83e6-3b42459a20c5", + "name": "Spawner (2213)", + "location": [1743, 589, 16], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23af0fb5-5bba-41f3-8caa-1f8f5018f42f", + "name": "Spawner (2213)", + "location": [1887, 591, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9436808-f34f-438e-8b11-31f6470faf91", + "name": "Spawner (2213)", + "location": [1810, 647, 0], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb8b69ec-7762-4c73-82fa-cd4c6ab12311", + "name": "Spawner (2213)", + "location": [1631, 650, 16], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b782447-4c8b-4dbf-aad3-2d537084e961", + "name": "Spawner (2213)", + "location": [1723, 683, 6], + "map": "Felucca", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bdd2249-7e2c-44b8-9ae3-0d4eb5f5f224", + "name": "Spawner (2213)", + "location": [1980, 832, -1], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f13df549-7ce0-4283-87e7-ef2971f81d03", + "name": "Spawner (2213)", + "location": [1905, 883, -1], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c079015-240e-4b1c-bf6f-9802277d8148", + "name": "Spawner (2213)", + "location": [1849, 953, 7], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "06c5aa59-1beb-4100-abea-13c6d2079025", + "name": "Spawner (2213)", + "location": [1001, 787, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65846901-1333-4a0a-ab1d-85e2424e0b46", + "name": "Spawner (2213)", + "location": [987, 725, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ee6239d0-b83b-434d-831f-b5b533dfbb7d", + "name": "Spawner (2213)", + "location": [1912, 1283, 0], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c119e387-73ae-4d7f-8104-a4eb9618a34b", + "name": "Spawner (2213)", + "location": [1787, 1280, 0], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc9e19b0-3e29-4bab-b86f-980379e3a230", + "name": "Spawner (2213)", + "location": [1799, 1394, 0], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5c244d89-f413-49e1-abf4-70086903d0bd", + "name": "Spawner (2213)", + "location": [1652, 1400, 0], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "48847927-65aa-4abd-8423-86546faa0c80", + "name": "Spawner (2213)", + "location": [1755, 1474, 0], + "map": "Felucca", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "409e129c-f95e-4a05-aff8-e2d577aede77", + "name": "Spawner (2213)", + "location": [1325, 1071, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Troll", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "401fe491-1378-4d69-ae3e-4cbfbe471991", + "name": "Spawner (2213)", + "location": [2369, 3430, 3], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "11bc853d-5908-4b28-9578-8c556ee3d7e1", + "name": "Spawner (2213)", + "location": [2354, 3503, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "181514b8-e4b3-442a-920c-27b56e3df585", + "name": "Spawner (2213)", + "location": [2503, 3593, 3], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b8e488f-bcb3-4037-a201-fce494c31f3d", + "name": "Spawner (2213)", + "location": [2520, 3983, 3], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5988167-2735-4863-a380-20a2ab27f70c", + "name": "Spawner (2213)", + "location": [2438, 3914, 0], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03cdfce6-9d4a-4ce4-b09b-fccff27ba52e", + "name": "Spawner (2213)", + "location": [2143, 3953, 3], + "map": "Felucca", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aec49b53-770f-44dd-899a-3447bcf40650", + "name": "Spawner (2213)", + "location": [868, 488, 0], + "map": "Felucca", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "198ec5be-f20d-4f44-a5c2-36c153751cb6", + "name": "Spawner (2213)", + "location": [805, 483, 0], + "map": "Felucca", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02e35c83-6913-4607-a063-677964db0928", + "name": "Spawner (2213)", + "location": [839, 406, 0], + "map": "Felucca", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "343b53bd-40ed-499b-93cf-60b7c854e810", + "name": "Spawner (2213)", + "location": [841, 344, 0], + "map": "Felucca", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d842bcc-2ef7-4842-a73a-0bbd654b6056", + "name": "Spawner (2213)", + "location": [1131, 3415, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4c5eb40-bd81-4de8-ba47-459319c56d4f", + "name": "Spawner (2213)", + "location": [1158, 3457, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5438491f-692d-4434-9731-0bd0e7cf96c0", + "name": "Spawner (2213)", + "location": [1094, 3482, 0], + "map": "Felucca", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02650c18-4ae6-4d51-affd-69bc2244fb28", + "name": "Spawner (2213)", + "location": [1312, 613, 10], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e690a14-3014-4267-8fa9-87cc82f26077", + "name": "Spawner (2213)", + "location": [1214, 617, 13], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50f349ae-dfc6-4c38-b241-9055670b9a37", + "name": "Spawner (2213)", + "location": [1361, 522, 19], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "067a213b-da9a-43e9-baac-1c2628a6e58e", + "name": "Spawner (2213)", + "location": [1226, 449, 1], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e804689-212a-45f1-b593-eda7f9efdd48", + "name": "Spawner (2213)", + "location": [1276, 431, 16], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "004eb6c0-e616-488d-8331-baaca386890e", + "name": "Spawner (2213)", + "location": [1231, 192, 6], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3d68f12-966b-42ab-aa8f-6b035d9fb485", + "name": "Spawner (2213)", + "location": [1143, 180, 14], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0edde836-7b37-4de8-8fc1-6336dac33b51", + "name": "Spawner (2213)", + "location": [1191, 288, 11], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da43570c-3e93-4a98-847a-1480345bd5b2", + "name": "Spawner (2213)", + "location": [1295, 328, 22], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a0e5df46-d8d1-4c20-b25a-34e315646b5c", + "name": "Spawner (2213)", + "location": [1361, 275, 16], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5cfe198e-679e-4311-b464-825debc44f00", + "name": "Spawner (2213)", + "location": [1491, 238, 22], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84565ecf-6dc3-4c85-a6d8-9959325383af", + "name": "Spawner (2213)", + "location": [3440, 405, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6c0e2b6-b91d-4c81-a410-dc9eb6c05647", + "name": "Spawner (2213)", + "location": [3382, 375, 4], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cada760c-ea76-4c03-a520-f54419dbb958", + "name": "Spawner (2213)", + "location": [3327, 344, 4], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7ba962b8-dc8a-44c3-9535-16f076cc14aa", + "name": "Spawner (2213)", + "location": [3434, 316, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09c20c39-441d-4c33-8089-aab62d941c8a", + "name": "Spawner (2213)", + "location": [3382, 280, 9], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aad6f620-4709-4f67-9a6f-305ad3aff10b", + "name": "Spawner (2213)", + "location": [3322, 271, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0eca02f5-d149-4460-aed5-98695fa760be", + "name": "Spawner (2213)", + "location": [3360, 218, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e8501154-9dab-4c4c-a39f-b9628b3bbb76", + "name": "Spawner (2213)", + "location": [3390, 259, 4], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eaf8f756-84ca-4a4b-8192-b12cbc585c06", + "name": "Spawner (2213)", + "location": [3446, 213, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8535646-4ceb-4c18-8381-815cacb69db3", + "name": "Spawner (2213)", + "location": [3462, 136, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d55e8962-d269-430e-9b9e-3be35bc38f62", + "name": "Spawner (2213)", + "location": [2589, 204, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08f49123-9ff1-456d-90ec-e5aa91908226", + "name": "Spawner (2213)", + "location": [2549, 136, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c9fe5d1-3732-466e-a113-3be9fbc93e4c", + "name": "Spawner (2213)", + "location": [2659, 147, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "606d97e6-78a1-48c6-bece-8cfa117906cf", + "name": "Spawner (2213)", + "location": [2621, 99, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c662fb39-3f09-4671-82f3-7aff006a304f", + "name": "Spawner (2213)", + "location": [2549, 86, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe3d76ed-5b62-4db7-b8bd-ab6778bdbcc1", + "name": "Spawner (2213)", + "location": [2478, 100, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ff06e09-3cbe-48fe-99d3-4f5753b9dabc", + "name": "Spawner (2213)", + "location": [2468, 144, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02ea296d-d8b7-4ecd-ba51-56469126e50b", + "name": "Spawner (2213)", + "location": [2485, 191, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eb2156a6-745b-4c10-be4c-ebf1204d237f", + "name": "Spawner (2213)", + "location": [2454, 248, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fbb2008-7fa3-4f3a-8694-2fab575d41df", + "name": "Spawner (2213)", + "location": [2533, 240, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec92432d-3a21-4e09-8b18-c40643973bb3", + "name": "Spawner (2213)", + "location": [2077, 434, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a4bf08f-1f3d-438b-a995-b638b1cb09f2", + "name": "Spawner (2213)", + "location": [2080, 400, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41bf401b-72da-46c0-b246-04c1fce86c37", + "name": "Spawner (2213)", + "location": [2107, 358, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c351d958-abbf-4a43-879d-486e488ed804", + "name": "Spawner (2213)", + "location": [2138, 323, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "82a86397-6318-4f4d-aad4-7f846018a9e0", + "name": "Spawner (2213)", + "location": [2189, 298, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8993ccc9-a37b-4023-8e61-5cb2ebd7e2da", + "name": "Spawner (2213)", + "location": [2137, 271, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec21b7e4-dc3e-4852-a4b0-88bab20a85bc", + "name": "Spawner (2213)", + "location": [2113, 221, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ffe57e9-1807-40db-bbfc-3b9d7776d0f0", + "name": "Spawner (2213)", + "location": [2146, 178, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af6f5396-9061-4a3f-9a80-74e737f07633", + "name": "Spawner (2213)", + "location": [2093, 147, 3], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "adc2a006-48b8-4997-bd27-a61a4a6f4135", + "name": "Spawner (2213)", + "location": [2109, 109, 3], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6dc07357-369f-413b-9acc-acf5540ea8cb", + "name": "Spawner (2213)", + "location": [2115, 48, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea181e6f-6d69-462b-95a0-7c92a7a15736", + "name": "Spawner (2213)", + "location": [1996, 73, 8], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ecc2c95-94e1-4859-bdae-5b6e9f6d6094", + "name": "Spawner (2213)", + "location": [1956, 48, 4], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3cdb18b5-9aa3-42bc-9978-136a1d1263aa", + "name": "Spawner (2213)", + "location": [1944, 102, 4], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "528a7172-954d-48e9-a482-391304d6cd59", + "name": "Spawner (2213)", + "location": [2034, 46, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cf7782fd-55ea-4b33-8cfd-6ace1c591fb1", + "name": "Spawner (2213)", + "location": [2033, 74, 1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f619a76-b1b8-43dd-83a3-e3303ec767c3", + "name": "Spawner (2213)", + "location": [2058, 87, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dddfb4b7-f386-46f5-a7da-7aa2b4ee0315", + "name": "Spawner (2213)", + "location": [2034, 108, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "615a5cba-d2f6-44dc-95a6-4cfa0b679aa4", + "name": "Spawner (2213)", + "location": [2000, 141, 1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9169bc6c-ef7f-48ed-94aa-18469b88e35e", + "name": "Spawner (2213)", + "location": [1965, 160, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9237e86f-8498-4384-aa4d-0cddc7f78012", + "name": "Spawner (2213)", + "location": [1946, 186, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dfea35bb-7743-4f04-8ae3-17f91eddcc80", + "name": "Spawner (2213)", + "location": [1922, 173, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be9fc521-2647-453c-8311-0129a0d79172", + "name": "Spawner (2213)", + "location": [1894, 197, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8cd055a5-7d48-4bc2-9909-37a3617c35d7", + "name": "Spawner (2213)", + "location": [1908, 214, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33035953-5de9-4a8d-9f5e-05b56a5b7a87", + "name": "Spawner (2213)", + "location": [1891, 226, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c53659a8-543b-485d-b470-e5ed6acdb52f", + "name": "Spawner (2213)", + "location": [1869, 265, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "602665fa-b49a-4a81-8d2a-763665a13bb4", + "name": "Spawner (2213)", + "location": [1626, 266, 16], + "map": "Felucca", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "948f6864-11e8-41da-a67a-c7b6dcc2e9a1", + "name": "Spawner (2213)", + "location": [1682, 252, 6], + "map": "Felucca", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae693c99-8cb9-438a-87fe-86b5d033aca3", + "name": "Spawner (2213)", + "location": [1723, 252, 16], + "map": "Felucca", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bf4bb35-2ec4-4a39-92d3-68c249f5b09a", + "name": "Spawner (2213)", + "location": [1759, 259, 21], + "map": "Felucca", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ef1c873-6465-4e41-bc69-4b09c5a527b9", + "name": "Spawner (2213)", + "location": [1804, 247, 0], + "map": "Felucca", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33879016-b02f-4b25-bc21-dcbb882c5fff", + "name": "Spawner (2213)", + "location": [1875, 906, 7], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "386f438a-1ba3-4f0d-b7e8-b7b95789f518", + "name": "Spawner (2213)", + "location": [1829, 874, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd268637-d96f-4ec9-b6cc-a70144dc3165", + "name": "Spawner (2213)", + "location": [1852, 841, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0bfb8c5f-2695-41d7-8978-7e1155117e57", + "name": "Spawner (2213)", + "location": [1930, 864, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bdf87d2-daea-4576-984d-5463bc82d5cd", + "name": "Spawner (2213)", + "location": [1883, 827, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f09c9e65-1864-434c-bc3f-d767fa80e788", + "name": "Spawner (2213)", + "location": [1949, 824, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9d026080-bc0b-4dff-a9aa-eb948024a256", + "name": "Spawner (2213)", + "location": [1987, 822, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f41e03cd-3997-4d0d-bb42-49adf8ae92e9", + "name": "Spawner (2213)", + "location": [2013, 839, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1779892-5c27-47db-aa94-a4d59bc1b1e9", + "name": "Spawner (2213)", + "location": [1979, 885, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e0715c9-f838-4d37-be39-3c8a0f1e2580", + "name": "Spawner (2213)", + "location": [1947, 912, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "83a2d33b-b85c-48a2-88d6-bf2e2e3a90b8", + "name": "Spawner (2213)", + "location": [1915, 928, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c8c991b-9b97-404c-8a16-b2501645d88a", + "name": "Spawner (2213)", + "location": [1866, 938, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad3ac281-793d-43a4-82ca-b7c04c4e3bbd", + "name": "Spawner (2213)", + "location": [1838, 957, -1], + "map": "Felucca", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd671a3d-e780-4f6f-993b-daa7dc80457b", + "name": "Spawner (2213)", + "location": [1802, 1506, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c96876a-9456-4e48-b8e7-151973a5fe0f", + "name": "Spawner (2213)", + "location": [1692, 1463, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc4f1b34-7c58-4161-9674-50b983dd4162", + "name": "Spawner (2213)", + "location": [1833, 1245, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a26ea05c-1690-4f53-b10c-b44cc66607ed", + "name": "Spawner (2213)", + "location": [1726, 1362, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28dec03a-120b-40ca-a03e-482d3f2de528", + "name": "Spawner (2213)", + "location": [1640, 1431, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9802f03-9dbb-422c-91f0-6aba14af525e", + "name": "Spawner (2213)", + "location": [1912, 1302, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f0797410-53f9-4af5-976e-332f8d4db4d8", + "name": "Spawner (2213)", + "location": [1967, 1296, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f56546f-246a-491b-8b19-a8c668ea73fb", + "name": "Spawner (2213)", + "location": [1898, 1265, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af167225-49e3-4b16-89e9-d9ec5c1a7dcf", + "name": "Spawner (2213)", + "location": [1872, 1347, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9e1a43f-932f-4501-861d-ab721a87d572", + "name": "Spawner (2213)", + "location": [1812, 1314, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5349db7-c725-494b-9350-8b108ef5a1a1", + "name": "Spawner (2213)", + "location": [1797, 1393, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41dd143d-0df5-41eb-a23e-bc65a7255c49", + "name": "Spawner (2213)", + "location": [1862, 1434, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15b7f75c-58c0-4a76-9e29-97b123985326", + "name": "Spawner (2213)", + "location": [1877, 1515, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d5b7b60-a289-434e-be7a-e3f538e1d9d9", + "name": "Spawner (2213)", + "location": [1361, 1392, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12609af5-f153-4afa-96da-970e1be4afd0", + "name": "Spawner (2213)", + "location": [1445, 1365, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "57e9ff50-ef57-4bfe-bd82-399ec6db0def", + "name": "Spawner (2213)", + "location": [1375, 1330, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0731f9aa-2216-4b25-97f1-bcfa2c54688d", + "name": "Spawner (2213)", + "location": [1474, 1297, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc0604fa-8a01-4f09-bcce-86e207e4a514", + "name": "Spawner (2213)", + "location": [1407, 1281, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e410df9e-2140-4972-aaed-8e1366658bb4", + "name": "Spawner (2213)", + "location": [1225, 1477, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "516fd39d-d4d2-4916-8c90-49902582100b", + "name": "Spawner (2213)", + "location": [1222, 1418, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2319882c-d94a-4f58-97e7-6dca32e890e2", + "name": "Spawner (2213)", + "location": [1244, 1374, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d5e8e17c-a9b3-420d-b15c-c7ba5057c876", + "name": "Spawner (2213)", + "location": [1180, 1354, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e183556-fb83-4823-96e9-9dba1cc69dd9", + "name": "Spawner (2213)", + "location": [1240, 1317, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94f97606-ded4-4f36-9529-008c94c705be", + "name": "Spawner (2213)", + "location": [1340, 1268, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "653465ba-f89c-4ecf-ac58-20f45a4b1055", + "name": "Spawner (2213)", + "location": [1305, 1326, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8bb9b2d-6144-4bc2-82c9-0479a8d336af", + "name": "Spawner (2213)", + "location": [1304, 1413, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bff6e844-976b-4433-9a12-653c267de42f", + "name": "Spawner (2213)", + "location": [1300, 1481, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41fe89de-bbed-4af2-ba54-2d33bb39a64d", + "name": "Spawner (2213)", + "location": [1541, 1895, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1adc37c9-0eea-45ec-adf2-6825f0af9b46", + "name": "Spawner (2213)", + "location": [1534, 1941, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "714bf0dd-48b0-488e-ad59-432e8477b9d5", + "name": "Spawner (2213)", + "location": [1475, 1958, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da41ea67-2124-4dbe-9f22-66e678652e59", + "name": "Spawner (2213)", + "location": [1435, 1917, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8af8ecde-099a-4fbe-9047-af0b8d3f383f", + "name": "Spawner (2213)", + "location": [1383, 1949, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a64edbb-d1e3-4ec8-9cf3-7d8993ebbb7f", + "name": "Spawner (2213)", + "location": [1416, 1992, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "731e7d09-59ed-48a4-b1d7-f2827a2d05e5", + "name": "Spawner (2213)", + "location": [1504, 2007, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a309b246-ca7e-4a68-b57e-5a50c9c7d0b1", + "name": "Spawner (2213)", + "location": [1447, 2038, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c77216f-786f-4f37-b97d-3aebb21fe1a1", + "name": "Spawner (2213)", + "location": [1479, 2094, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13359902-1ded-4453-9330-765325c87e2b", + "name": "Spawner (2213)", + "location": [1525, 2053, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d5c1996-6320-4d37-a6e2-527c3eef0be6", + "name": "Spawner (2213)", + "location": [2095, 2352, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13d177ea-c83f-4d80-82a0-bbdaea29decf", + "name": "Spawner (2213)", + "location": [2077, 2305, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fabc9f7f-e030-43da-adf3-7c8065e0eeec", + "name": "Spawner (2213)", + "location": [2021, 2313, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "602c87dc-2586-4281-805c-948364d6fcd7", + "name": "Spawner (2213)", + "location": [1968, 2346, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5821b10d-c56f-4f82-8a8c-576bde50184d", + "name": "Spawner (2213)", + "location": [1947, 2294, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "070b08c1-489a-4446-a535-8ba4828a641b", + "name": "Spawner (2213)", + "location": [1915, 2385, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d481b37-cf17-44b3-964e-3c5a49eb3273", + "name": "Spawner (2213)", + "location": [1891, 2331, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b99597c2-8732-450d-a851-27fbef18abfa", + "name": "Spawner (2213)", + "location": [1890, 2270, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9ae149f-f3a4-41a1-87bf-19c32f436d18", + "name": "Spawner (2213)", + "location": [1873, 2426, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f0ee982-f3db-44cb-93a5-b8d337332dde", + "name": "Spawner (2213)", + "location": [1837, 2334, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "77e4e362-a87a-46eb-ac22-87b392a705f6", + "name": "Spawner (2213)", + "location": [1826, 2250, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f950f3e2-4656-41d1-a46d-6941b77d6ebf", + "name": "Spawner (2213)", + "location": [2109, 2989, 30], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3b09d5b9-f1e1-4628-92bb-7592ee885f6c", + "name": "Spawner (2213)", + "location": [2089, 3017, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db1e8042-1bba-4828-8444-1bd0404753f5", + "name": "Spawner (2213)", + "location": [2074, 3069, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe99c6b3-b023-46ff-8559-7c4b26728027", + "name": "Spawner (2213)", + "location": [2033, 3041, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6779c53c-467b-424f-86a7-5007798c4b47", + "name": "Spawner (2213)", + "location": [1970, 3052, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "45008529-dc3f-4fcc-8810-520775c4e411", + "name": "Spawner (2213)", + "location": [1915, 3050, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "34d6cb84-92a2-4e06-a314-e54bddd11403", + "name": "Spawner (2213)", + "location": [1866, 3040, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22a47de7-ed67-461f-830e-d7d395e638c4", + "name": "Spawner (2213)", + "location": [1812, 3036, 5], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eff8645a-0a22-4a43-b54e-919edba83505", + "name": "Spawner (2213)", + "location": [1841, 3000, 10], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3bdf6642-bec9-4d20-b880-37381726f5b9", + "name": "Spawner (2213)", + "location": [1803, 2981, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d15df44-cca8-413c-aead-64e7fa9e6c7b", + "name": "Spawner (2213)", + "location": [1867, 2962, 7], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "34134639-abeb-476b-9cd6-c0ad81f3615f", + "name": "Spawner (2213)", + "location": [1849, 2915, 30], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8292593-a88b-4eb5-8cb6-b028fc4baf7f", + "name": "Spawner (2213)", + "location": [1911, 2934, 10], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8efd16fe-fa70-4bc3-ab54-988dd78cda62", + "name": "Spawner (2213)", + "location": [1954, 2903, 30], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7211f788-6a3c-4d59-809d-dc4f5440e1f6", + "name": "Spawner (2213)", + "location": [851, 495, 0], + "map": "Felucca", "count": 8, "minDelay": "00:20:00", "maxDelay": "00:20:00", @@ -92,32 +4333,1238 @@ ] }, { - "$type": "Spawner", - "guid": "034b0848-3841-4c7a-befa-b47a3c32c96a", - "name": "Spawner (113)", - "location": [2184, 1376, 0], - "map": "Trammel", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", + "type": "Spawner", + "guid": "2f215ba7-0215-4501-a61a-dfc7bf9d549d", + "name": "Spawner (2213)", + "location": [805, 463, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", "team": 0, - "homeRange": 15, + "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, - { "name": "Savage", "maxCount": 3, "probability": 100 }, - { "name": "SavageRider", "maxCount": 2, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "03d53510-89c6-400b-84cc-058b3a747a9f", - "name": "Spawner (113)", - "location": [2326, 656, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "440f3392-8081-4185-b1d4-62ebb6e90b5b", + "name": "Spawner (2213)", + "location": [837, 361, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad6a2169-b01e-47ab-a323-fab15c6fabb6", + "name": "Spawner (2213)", + "location": [1029, 1969, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e26d2e1a-806c-4929-9f9c-606e861532cc", + "name": "Spawner (2213)", + "location": [1080, 1963, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b35e54f-77d1-4fe8-80d0-217df211d4e2", + "name": "Spawner (2213)", + "location": [1042, 1920, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5c94feb2-ffa2-45f5-8392-bef88c324f87", + "name": "Spawner (2213)", + "location": [821, 1938, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a159a3d-7dc5-441f-917b-c58af12fed0d", + "name": "Spawner (2213)", + "location": [736, 1938, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "005e6436-1cae-4ece-a97a-a95fae8949ce", + "name": "Spawner (2213)", + "location": [899, 1851, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef74d098-27d0-4c3d-8733-e2312c1b8de3", + "name": "Spawner (2213)", + "location": [759, 1828, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bd6347d-424f-4e25-80ce-a08fc96edc05", + "name": "Spawner (2213)", + "location": [672, 1825, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65c5883b-2663-411b-9820-28499977c4d8", + "name": "Spawner (2213)", + "location": [892, 1963, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f3369eb-d269-4cb8-b74a-96641125adb0", + "name": "Spawner (2213)", + "location": [815, 1603, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Spectre", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "006eec8a-3a67-4461-9688-1d7d0bfa0f1d", + "name": "Spawner (2213)", + "location": [1055, 1442, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "27b05c45-ec38-45db-b3ac-127bac6b8e99", + "name": "Spawner (2213)", + "location": [1021, 1415, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "460ad9dc-158f-4e2e-a988-b52dcfe37839", + "name": "Spawner (2213)", + "location": [1060, 1375, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f42e18d-e212-4379-868b-48f40b9aab76", + "name": "Spawner (2213)", + "location": [1004, 1369, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d1ffadb-f06d-4166-b4e1-549f742c05f3", + "name": "Spawner (2213)", + "location": [1037, 1325, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe612581-49b6-43f5-bc62-a245821a3041", + "name": "Spawner (2213)", + "location": [971, 1308, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79dcafe6-4ca2-4715-ab2c-80951c18fad7", + "name": "Spawner (2213)", + "location": [1000, 330, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60046b73-487d-41d2-aa57-a137e9b1dfe9", + "name": "Spawner (2213)", + "location": [964, 365, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a225256-b5b2-4e7a-8150-e4223df853a9", + "name": "Spawner (2213)", + "location": [947, 418, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e073944b-3ed8-4649-9a2e-861f8ed0f1d3", + "name": "Spawner (2213)", + "location": [956, 452, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75c2c949-73c9-4a97-8688-79c5cbcf4cd8", + "name": "Spawner (2213)", + "location": [423, 1595, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28724275-f05c-4b50-8e05-60aa69bb3e65", + "name": "Spawner (2213)", + "location": [418, 1665, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5349e354-eb19-4207-a2e0-831fda4d1850", + "name": "Spawner (2213)", + "location": [480, 1641, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a02a3c7-3e58-44d8-98e3-6abb2f2b099c", + "name": "Spawner (2213)", + "location": [516, 1585, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69d4b822-5483-487f-9578-8cb790fc9e49", + "name": "Spawner (2213)", + "location": [541, 1645, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81e26950-3b2a-4d34-b8ea-e6bc4431a202", + "name": "Spawner (2213)", + "location": [141, 1367, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5685505d-66f4-42c3-96b4-f71b071b40af", + "name": "Spawner (2213)", + "location": [189, 1344, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "53d1a2d3-250d-4eb4-bc6f-bba8133b27c1", + "name": "Spawner (2213)", + "location": [232, 1350, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2bf21be-ee5d-433d-bb96-ba564a604f03", + "name": "Spawner (2213)", + "location": [278, 1355, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cefea663-b05f-4d86-8d4a-b09f1358f866", + "name": "Spawner (2213)", + "location": [229, 1419, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5cc15e9-9e9b-4787-8003-004bac948bcc", + "name": "Spawner (2213)", + "location": [182, 1428, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fbfa536c-3e04-4609-953f-e77bb2a48bb4", + "name": "Spawner (2213)", + "location": [157, 1487, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95c40856-e7b8-4d17-b7d4-9600c9f0670b", + "name": "Spawner (2213)", + "location": [204, 1483, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1878494f-e3bd-44a8-9a78-e3c2de43cf0a", + "name": "Spawner (2213)", + "location": [240, 1443, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1e53b99-22b0-4072-a3f5-d82f9ac90cf1", + "name": "Spawner (2213)", + "location": [269, 1474, 0], + "map": "Felucca", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "faa69ada-cbbc-4c81-849d-ad4f66bb02ce", + "name": "Spawner (2213)", + "location": [4057, 300, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ff6bf9e-dcdf-4967-a32c-8b542a45ce6e", + "name": "Spawner (2213)", + "location": [4013, 239, 1], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84e221a0-eef4-4399-a87e-385b21ee014d", + "name": "Spawner (2213)", + "location": [3929, 235, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca397470-069c-4a2e-8669-259cfa3a8b07", + "name": "Spawner (2213)", + "location": [3920, 287, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c730966-efe7-4079-a56d-f836444b2b4d", + "name": "Spawner (2213)", + "location": [4090, 325, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3b59c9a-cc31-461a-96da-58a9916f1b6f", + "name": "Spawner (2213)", + "location": [4162, 395, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "818950cd-6dbc-4e5d-8836-7c32103b28fa", + "name": "Spawner (2213)", + "location": [4206, 471, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c7b6e2b9-38c4-4b1b-b8b0-72f87b2f66a9", + "name": "Spawner (2213)", + "location": [3928, 393, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1171adb5-c351-4aff-a15b-96e90a53b975", + "name": "Spawner (2213)", + "location": [3963, 508, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "001a5320-820c-4300-96f9-676e428b55be", + "name": "Spawner (2213)", + "location": [4066, 569, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "PolarBear", "maxCount": 8, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 8, "probability": 100 }, + { "name": "Walrus", "maxCount": 8, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0cd04146-09d2-4f8c-8faf-dc0e19d551a0", + "name": "Spawner (2213)", + "location": [4153, 601, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8b50362-2413-4aff-9efe-90c40aefd329", + "name": "Spawner (2213)", + "location": [4214, 655, 1], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a4883925-84fb-4f21-9f3e-9a31ef9821cd", + "name": "Spawner (2213)", + "location": [4264, 725, 1], + "map": "Felucca", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e6a3af0-b37b-44d0-a920-d1ea5053f2b9", + "name": "Spawner (2213)", + "location": [2963, 3617, 3], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49282da8-3f46-4b69-aab2-670dda1c7117", + "name": "Spawner (2213)", + "location": [2966, 3591, 11], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "865c8dd1-b8f3-4fbb-8d00-d4188cb4b630", + "name": "Spawner (2213)", + "location": [3013, 3591, 15], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aefc9ed6-d9e5-4094-97e8-cfcd27d232ee", + "name": "Spawner (2213)", + "location": [3024, 3566, 15], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4e653da6-2748-4c30-b242-e97125e5be12", + "name": "Spawner (2213)", + "location": [3042, 3590, 5], + "map": "Felucca", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c14d9101-3d93-4198-98c9-1ba34b4b8a5b", + "name": "Spawner (2213)", + "location": [2462, 725, 0], + "map": "Felucca", "count": 8, "minDelay": "00:20:00", "maxDelay": "00:20:00", @@ -136,28 +5583,2267 @@ ] }, { - "$type": "Spawner", - "guid": "03e8ed27-82da-4ecf-8d02-126f4b8bc35d", - "name": "Spawner (113)", - "location": [1875, 906, 7], - "map": "Trammel", - "count": 2, + "type": "Spawner", + "guid": "b894e46c-2304-482f-a36e-b80e8ff7c26f", + "name": "Spawner (2213)", + "location": [2428, 722, 8], + "map": "Felucca", + "count": 8, "minDelay": "00:20:00", "maxDelay": "00:20:00", "team": 0, "homeRange": 30, "walkingRange": 30, "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "04e28c4b-9b75-46a7-b932-3ce92bcc7d85", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "a43206c7-8096-4742-8975-2fe1e93a9c7a", + "name": "Spawner (2213)", + "location": [2375, 725, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f713474-29c7-4dff-865f-a57be332f44e", + "name": "Spawner (2213)", + "location": [2345, 676, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b7f6dd7-59c1-4ef1-8b7f-1e4b87c1f873", + "name": "Spawner (2213)", + "location": [2429, 690, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "929d7fd7-0b40-4937-a62c-86b180c63f66", + "name": "Spawner (2213)", + "location": [2379, 654, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e287924-f6b6-4a1f-b718-d3a8ab42d3f3", + "name": "Spawner (2213)", + "location": [2326, 656, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a37f96c3-c51a-47b2-b472-2d4a495ed755", + "name": "Spawner (2213)", + "location": [2319, 591, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "67ff897a-73a2-4138-a65e-266bc2553d42", + "name": "Spawner (2213)", + "location": [659, 1318, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "919eb0cd-ae28-41e9-8f0a-c27da48a14c1", + "name": "Spawner (2213)", + "location": [579, 1305, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f01ea1e-7189-495d-b62f-53698c79b009", + "name": "Spawner (2213)", + "location": [728, 1242, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b80b4982-0838-4dc8-b433-d8158860b3f0", + "name": "Spawner (2213)", + "location": [793, 1088, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c3a3d09-11df-451e-b796-b0ec4fc0849f", + "name": "Spawner (2213)", + "location": [849, 1262, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "215f7ae1-42f8-46a6-a35b-bebd67373a40", + "name": "Spawner (2213)", + "location": [909, 1175, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96a38541-94a9-4607-adaa-1bd6347bf66a", + "name": "Spawner (2213)", + "location": [974, 1186, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9873f87-4f01-4e8d-920b-2834ee78aa98", + "name": "Spawner (2213)", + "location": [1059, 1166, 5], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88c9fa5d-b9ee-4060-8e14-24961c30cf76", + "name": "Spawner (2213)", + "location": [823, 960, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bec45065-1a08-4933-96e5-e014ff2cad8e", + "name": "Spawner (2213)", + "location": [960, 910, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5ffd85a-c19d-49ed-812d-a3aa1f014a1a", + "name": "Spawner (2213)", + "location": [1042, 1024, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae2034e9-7884-4677-9137-68707037298d", + "name": "Spawner (2213)", + "location": [1147, 1048, 5], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8458a597-4941-4335-8537-6f89cd177920", + "name": "Spawner (2213)", + "location": [1097, 970, 2], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55ca4007-514b-4b20-89e5-408cccf0924a", + "name": "Spawner (2213)", + "location": [1192, 958, 5], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f39acaf-9dac-46d6-aca4-54e66f4280ef", + "name": "Spawner (2213)", + "location": [1166, 818, 5], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81aec24a-c7b8-46a7-a104-518c2bdd479c", + "name": "Spawner (2213)", + "location": [1083, 783, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a431c89a-9532-4cee-9ad2-843618cba4fd", + "name": "Spawner (2213)", + "location": [1268, 774, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85637171-e2f4-4170-b0a1-cb685aea8962", + "name": "Spawner (2213)", + "location": [1180, 737, 1], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1e173c7-f153-4d33-a8d8-056c97f97179", + "name": "Spawner (2213)", + "location": [1122, 718, 1], + "map": "Felucca", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "383c7654-67b0-4024-a455-f433ea40d522", + "name": "Spawner (2213)", + "location": [423, 1595, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae89bef0-87ec-40c4-85cf-29f8e2a8aa9f", + "name": "Spawner (2213)", + "location": [418, 1665, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9013421-1e07-4736-b861-4ae0e0eb5f1f", + "name": "Spawner (2213)", + "location": [480, 1641, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "519391cf-1cdd-424c-83c2-d646ea48343e", + "name": "Spawner (2213)", + "location": [516, 1585, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d2d0626-1d6c-4338-bb96-16281daaf313", + "name": "Spawner (2213)", + "location": [541, 1645, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "513b093a-a623-4073-b89c-44c8417c4c90", + "name": "Spawner (2213)", + "location": [4504, 1458, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf84bf7c-5e4e-4df0-841c-951a130df943", + "name": "Spawner (2213)", + "location": [4494, 1426, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1824a86e-4c8d-48fd-900d-e70e7127667a", + "name": "Spawner (2213)", + "location": [4455, 1434, 10], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c037ed10-c9d1-48d9-9b78-238fae407643", + "name": "Spawner (2213)", + "location": [4456, 1405, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4337a9d3-98f2-4eec-9459-c10f70f60319", + "name": "Spawner (2213)", + "location": [4455, 1358, 10], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7a444146-d8d7-4ba6-af15-1a5f440826c9", + "name": "Spawner (2213)", + "location": [4550, 1409, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2b2e285f-e9ae-40a8-a1f8-95a8fe906e13", + "name": "Spawner (2213)", + "location": [4552, 1447, 10], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "317f0495-b38f-4f35-90f3-05033507c676", + "name": "Spawner (2213)", + "location": [4615, 1469, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8cec028-8686-4f1e-ae36-007c7074cf39", + "name": "Spawner (2213)", + "location": [4612, 1418, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47242747-b6e8-49d1-82ff-e3ddede86281", + "name": "Spawner (2213)", + "location": [4605, 1365, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93f4c0ad-2380-488f-87a4-24d765cc9082", + "name": "Spawner (2213)", + "location": [4566, 1359, 8], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e9e81b9-071c-4b46-adf9-f3e39b7e73d1", + "name": "Spawner (2213)", + "location": [2372, 210, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b3a4772-0805-44ff-b7bd-2ec95092ded2", + "name": "Spawner (2213)", + "location": [2355, 179, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec24e1a4-87be-4d55-8565-38292ab737b6", + "name": "Spawner (2213)", + "location": [2351, 148, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d76ec9df-9494-4cb4-af37-e257ae4ffdf4", + "name": "Spawner (2213)", + "location": [2345, 112, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ed47473-6f84-449c-ab34-5d75e71d3335", + "name": "Spawner (2213)", + "location": [2328, 67, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "094d699e-d6ad-4322-b6eb-4a6883da6fe2", + "name": "Spawner (2213)", + "location": [2177, 3034, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1955cb3-e55a-4c69-a173-60d937009596", + "name": "Spawner (2213)", + "location": [2143, 3068, 20], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9e020c2-1a4f-4a3f-9f91-4811c7f0ba13", + "name": "Spawner (2213)", + "location": [1983, 1517, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9df57404-f990-46d9-b66f-dfbbd3496caf", + "name": "Spawner (2213)", + "location": [1986, 1463, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e34837e-aee8-4001-872a-373502c74cdb", + "name": "Spawner (2213)", + "location": [1969, 1409, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68e27fa5-b64e-4298-bf75-1df29d3cf1e1", + "name": "Spawner (2213)", + "location": [1912, 1302, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "354c29c0-32e9-448f-b17c-ef47ef6f1c41", + "name": "Spawner (2213)", + "location": [1967, 1296, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "399f4337-1564-4f34-b22c-5d1ec5b8a7ba", + "name": "Spawner (2213)", + "location": [1898, 1265, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b745f7ae-402e-4da9-b424-31712b0c8b4c", + "name": "Spawner (2213)", + "location": [1872, 1347, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8e1dfcc8-d717-4623-a37a-ded401bb0816", + "name": "Spawner (2213)", + "location": [1812, 1314, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2a94db5-73b3-4012-be1e-ac122f9ebed2", + "name": "Spawner (2213)", + "location": [1797, 1393, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3467c2e3-9fc0-45ba-97b6-207bddb5beb9", + "name": "Spawner (2213)", + "location": [1862, 1434, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "362c3cc5-8548-43be-8fab-4d981cab5ed1", + "name": "Spawner (2213)", + "location": [1877, 1515, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cdf5d19a-75c9-4b79-bcd1-4dcee392a090", + "name": "Spawner (2213)", + "location": [1802, 1506, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9276ac3-f4a9-4614-bcd5-ff08d76b1a27", + "name": "Spawner (2213)", + "location": [1692, 1463, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81cab405-c429-4e92-b73a-27f944780ae4", + "name": "Spawner (2213)", + "location": [1833, 1245, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d35fcf9-851c-4b60-8c51-ccd62e38d1c2", + "name": "Spawner (2213)", + "location": [1726, 1362, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10a9fb84-e7e7-47b1-ac1d-0bdba65b7d5f", + "name": "Spawner (2213)", + "location": [1640, 1431, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32260d9a-6ff8-417d-88e9-b08e0c04fa2c", + "name": "Spawner (2213)", + "location": [1055, 1442, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49a48c0e-c34c-464b-b5b2-5374b434aab5", + "name": "Spawner (2213)", + "location": [1021, 1415, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b364efa-4774-4c6a-afc7-a44d234328fb", + "name": "Spawner (2213)", + "location": [1060, 1375, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a88078e-0413-44ec-add5-83f6d71d84a2", + "name": "Spawner (2213)", + "location": [1004, 1369, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "14080e7e-45ac-4e42-aa16-5c423a4563df", + "name": "Spawner (2213)", + "location": [1037, 1325, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28d181e5-edb4-4d16-900d-b4a9b8a336f4", + "name": "Spawner (2213)", + "location": [971, 1308, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1501e046-7baa-43b3-8b46-a9b978b0460f", + "name": "Spawner (2213)", + "location": [2171, 1356, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, + { "name": "Savage", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d68ece9-2d7e-4792-b92d-71424147068b", + "name": "Spawner (2213)", + "location": [2184, 1376, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, + { "name": "Savage", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "87b90902-f43c-4892-963c-c8b5a3097b5f", + "name": "Spawner (2213)", + "location": [2155, 1370, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ca9d939-38bc-404a-b3f6-04727259fb8b", + "name": "Spawner (2213)", + "location": [2203, 1379, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a363c624-4c20-44dd-abb1-99a6f779c68b", + "name": "Spawner (2213)", + "location": [2038, 1023, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "132293a7-fc22-467d-b65d-6c6fa8609304", + "name": "Spawner (2213)", + "location": [2079, 1018, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8bff60da-cfb3-4fb7-9f6a-b1b406597762", + "name": "Spawner (2213)", + "location": [2067, 989, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdb6c75c-66db-4c72-aece-0d8bfe4ae4c1", + "name": "Spawner (2213)", + "location": [2028, 987, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3bc5021-baec-4097-8279-a50d34d8d905", + "name": "Spawner (2213)", + "location": [2033, 936, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23347175-c9e6-4776-a97c-04c85507e131", + "name": "Spawner (2213)", + "location": [1990, 976, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef1a52fa-ba6d-48c0-9817-03492eed0ad1", + "name": "Spawner (2213)", + "location": [1993, 1030, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd67249a-421d-4479-b771-3083f7d9e036", + "name": "Spawner (2213)", + "location": [2035, 1018, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a610c3f7-9200-4609-bbec-1f8e9fa3d46b", + "name": "Spawner (2213)", + "location": [2023, 1052, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54caf961-eba0-450a-a510-b078d9011faa", + "name": "Spawner (2213)", + "location": [2069, 1028, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cac38c89-0f20-4066-b946-6cd4876723ab", + "name": "Spawner (2213)", + "location": [2048, 1066, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "42663241-fc0a-4fcc-aaa8-8fd9ab4bb3a4", + "name": "Spawner (2213)", + "location": [1435, 2261, 5], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75aa2658-e0f1-4003-93e6-28da55db063e", + "name": "Spawner (2213)", + "location": [1651, 2413, 5], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39c6fca8-585d-419a-ad33-fdeecff5278e", + "name": "Spawner (2213)", + "location": [1619, 2725, 10], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6edd0c08-b27b-4bc3-a3bb-603b6359d3ac", + "name": "Spawner (2213)", + "location": [1427, 2525, 0], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "850d750e-203e-44c3-8437-b0784ecdd1e8", + "name": "Spawner (2213)", + "location": [1059, 2461, 0], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a406878-badb-424f-b754-f4ca946ecd46", + "name": "Spawner (2213)", + "location": [1243, 2437, 5], + "map": "Felucca", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9267b97a-6678-4f62-a2f3-099e8de35e35", + "name": "Spawner (2213)", + "location": [1145, 2231, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Daemon", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d26b49c8-1b3f-425e-8f29-87b0ae3073f1", + "name": "Spawner (2213)", + "location": [1072, 2195, 5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f866bbb8-35ac-4ff7-a190-de8ad06b77e7", + "name": "Spawner (2213)", + "location": [1070, 2256, 5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f4f2c223-5517-4b4b-99f1-8fef5d1e4397", + "name": "Spawner (2213)", + "location": [1215, 2195, 5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c067f57d-067c-4cfb-bf77-8d8c6cce1ae6", + "name": "Spawner (2213)", + "location": [1216, 2259, 5], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "87e84f7b-43c8-41fc-9891-000423b0afb8", + "name": "Spawner (2213)", + "location": [1595, 3045, 0], + "map": "Felucca", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 100, + "entries": [ + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1a5cffa-a389-4f0d-866b-84416c3e8eac", + "name": "Spawner (2213)", + "location": [2091, 3413, 0], + "map": "Felucca", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d532ee9e-fafc-4432-bc06-76cd59789b9c", + "name": "Spawner (2213)", + "location": [1931, 3205, 0], + "map": "Felucca", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d35912d-be04-44d1-97b2-f6a77882c755", + "name": "Spawner (2213)", + "location": [1827, 3429, 10], + "map": "Felucca", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d55517f-6927-4afd-b28c-123e332166ac", + "name": "Spawner (2213)", + "location": [1635, 3253, 0], + "map": "Felucca", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6375273a-4a07-4c39-9a5c-5af034151feb", + "name": "Spawner (2213)", + "location": [1387, 3109, 0], + "map": "Felucca", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c29f73f4-b7d1-4d83-ba23-d995a5c99085", + "name": "Spawner (2213)", + "location": [1110, 2672, 0], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3895c37-a997-421f-a67e-808c23a3ebe3", + "name": "Spawner (2213)", "location": [1158, 2872, 0], - "map": "Trammel", + "map": "Felucca", "count": 50, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -183,65 +7869,11 @@ ] }, { - "$type": "Spawner", - "guid": "056a5d12-e5e6-4906-9fe1-5e8fb605cfb7", - "name": "Spawner (113)", - "location": [1894, 197, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "056b2cc1-56cd-45e3-bae8-a8749a1ddc06", - "name": "Spawner (113)", - "location": [2438, 3914, 0], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "066c7e0c-e833-422b-9eab-6ce558dba618", - "name": "Spawner (113)", - "location": [839, 406, 0], - "map": "Trammel", - "count": 2, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "079a77cb-460d-4537-bd99-7ffe833a7648", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "e783706f-f00b-4bc1-9833-b3cafa22425a", + "name": "Spawner (2213)", "location": [1141, 2959, 0], - "map": "Trammel", + "map": "Felucca", "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -255,988 +7887,132 @@ ] }, { - "$type": "Spawner", - "guid": "081de1cf-9c2c-4ac8-9b21-ba2593487543", - "name": "Spawner (113)", - "location": [4493, 1364, 23], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, - { "name": "Boar", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 }, - { "name": "Hind", "maxCount": 1, "probability": 100 }, - { "name": "Llama", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "08534eb5-4011-42d7-9006-169b3d002ca1", - "name": "Spawner (113)", - "location": [1304, 1413, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "087f6b77-2393-4dac-8543-50a14a7ae6f1", - "name": "Spawner (113)", - "location": [182, 1428, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "08b4da27-33a7-4cfa-be11-f0eca92c6aa2", - "name": "Spawner (113)", - "location": [1890, 2270, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "090b4a9b-ed1e-4276-94db-cf708d34f9d6", - "name": "Spawner (113)", - "location": [1110, 2672, 0], - "map": "Trammel", - "count": 13, + "type": "Spawner", + "guid": "fb66a7cc-2b6c-4930-90e6-f5bd2da4ec33", + "name": "Spawner (2213)", + "location": [1166, 2958, 0], + "map": "Felucca", + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 20, "walkingRange": 20, "entries": [ - { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "Savage", "maxCount": 2, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "647ff862-b266-4923-bdeb-78d411d4f0fd", + "name": "Spawner (2213)", + "location": [854, 1686, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "Brigand", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b61ae14-8163-4511-97f7-bf81e83b4efe", + "name": "Spawner (2213)", + "location": [757, 1401, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Lich", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "136fc430-d130-446c-bfd0-a0daf0d69e50", + "name": "Spawner (2213)", + "location": [634, 1504, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, { "name": "OrcishMage", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "09a60df9-a7f8-4964-9e24-dc43558b7c96", - "name": "Spawner (113)", - "location": [2091, 3413, 0], - "map": "Trammel", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0ada6680-a6f8-4468-a941-912dbef13276", - "name": "Spawner (113)", - "location": [1435, 1917, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0b3c0d48-b7c1-4282-8233-b5bef002ab2a", - "name": "Spawner (113)", - "location": [2067, 989, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0d68cd11-ae7b-45c4-944a-6e58a6e65684", - "name": "Spawner (113)", - "location": [4519, 1358, 23], - "map": "Trammel", + "type": "Spawner", + "guid": "42a92b9b-a8ac-498c-b528-94ff6f10b775", + "name": "Spawner (2213)", + "location": [634, 1462, 0], + "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 4, + "homeRange": 10, "walkingRange": 15, "entries": [ - { "name": "Alligator", "maxCount": 1, "probability": 100 }, - { "name": "BullFrog", "maxCount": 1, "probability": 100 }, - { "name": "Gorilla", "maxCount": 1, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, - { "name": "GiantToad", "maxCount": 1, "probability": 100 } + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "0dea7015-1783-4b93-9ded-b5b52be021cb", - "name": "Spawner (113)", - "location": [1915, 3050, 15], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0e2c18bc-c2c6-4a16-bda2-1d4bd4d5574d", - "name": "Spawner (113)", - "location": [1244, 1374, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0eab19f2-96ae-41b9-a0d7-5dc82506af6a", - "name": "Spawner (113)", - "location": [1908, 214, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0ed841ed-3e0c-4c9a-aef2-1952720a260b", - "name": "Spawner (113)", - "location": [1838, 957, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0efa850b-b21d-4835-a4ff-08015747427a", - "name": "Spawner (113)", - "location": [2533, 240, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f87b3cc-3bec-4233-a193-faf5d766c8b1", - "name": "Spawner (113)", - "location": [1640, 1431, 0], - "map": "Trammel", - "count": 5, + "type": "Spawner", + "guid": "a608396f-9595-4436-b5a9-75a2947a948d", + "name": "Spawner (2213)", + "location": [633, 1484, 0], + "map": "Felucca", + "count": 14, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10cf4b5b-9477-402b-ac6c-57245b73756c", - "name": "Spawner (113)", - "location": [2328, 67, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 }, - { "name": "Zombie", "maxCount": 9, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10e3df7f-ffc8-4bd7-a216-0689f4decabc", - "name": "Spawner (113)", - "location": [1541, 1895, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10f682e2-a5bd-42f8-b228-ae66d1a9a4c5", - "name": "Spawner (113)", - "location": [2485, 191, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "11dcec12-bab6-4595-aa20-ef64eb749c0e", - "name": "Spawner (113)", - "location": [1947, 912, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12e72748-84ff-4d28-bfbe-39b7d5232ceb", - "name": "Spawner (113)", - "location": [4519, 3097, 0], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "12e896ea-da73-4645-a010-70d1cf7145c8", - "name": "Spawner (113)", - "location": [2428, 722, 8], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "1365e61d-0ffa-43f0-b926-45316a2a8048", - "name": "Spawner (113)", - "location": [2021, 2313, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "13ea30a4-a229-4926-8cc6-45d85d3680e2", - "name": "Spawner (113)", - "location": [4456, 1405, 8], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15701d78-3752-44f6-b158-ddca2746390e", - "name": "Spawner (113)", - "location": [1059, 1166, 5], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15f17b62-41e8-4db8-a62f-eb04db9c7606", - "name": "Spawner (113)", - "location": [1826, 2250, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "16d441e6-2418-4ffb-97c8-6c1b05f6d1a4", - "name": "Spawner (113)", - "location": [1636, 532, 22], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "17b36c45-5c86-4ede-8d59-8c9031e72a79", - "name": "Spawner (113)", - "location": [1872, 1347, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "17f64e17-61cb-4e36-aaa9-8f0f94218781", - "name": "Spawner (113)", - "location": [1877, 1515, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1812fd23-b8b5-4af6-b601-b12e3acd5de3", - "name": "Spawner (113)", - "location": [2069, 1028, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "193a49e9-ea5c-40f5-8118-d7f369008887", - "name": "Spawner (113)", - "location": [3963, 508, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "198d8d75-87a7-4c77-abc3-c45fb558825b", - "name": "Spawner (113)", - "location": [3446, 213, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1a5702d5-741d-4142-876f-f5446948c3ce", - "name": "Spawner (113)", - "location": [899, 1851, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1aebb4f5-9b3c-42b8-bb18-77dc7b26b98f", - "name": "Spawner (113)", - "location": [971, 1308, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1af8afa4-3937-4745-9a39-4fb67f03f640", - "name": "Spawner (113)", - "location": [1887, 591, 0], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1b36b219-b882-4af5-a52b-5097ee26680f", - "name": "Spawner (113)", - "location": [2103, 3371, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1b3fe9d8-e194-42e9-8316-aa67a21b887d", - "name": "Spawner (113)", - "location": [1312, 613, 10], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1c19f0de-4a64-471b-b656-9eb3d7a7d09e", - "name": "Spawner (113)", - "location": [4552, 1447, 10], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1c790a35-f7b6-4ee1-8d94-4e6076f94ed3", - "name": "Spawner (113)", - "location": [1064, 2488, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1c9e3733-5669-4f75-85fb-0d024e01cdb5", - "name": "Spawner (113)", - "location": [2080, 400, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1cab8e17-97d9-4873-8f47-b79eb03e8038", - "name": "Spawner (113)", - "location": [269, 1474, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1ce82610-b4ea-41c1-abc5-e2bbce8dfa46", - "name": "Spawner (113)", - "location": [2095, 2352, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1ec3e3a0-8c69-4620-8e2c-7f6b2d550787", - "name": "Spawner (113)", - "location": [1145, 2231, 20], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Daemon", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f1a08a0-b47a-4299-8733-94a2923e3d42", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "e260d7d5-b8e5-49da-97c3-0ea924088649", + "name": "Spawner (2213)", "location": [965, 757, 0], - "map": "Trammel", + "map": "Felucca", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 960, "y": 752, "z": -128 }, - "end": { "x": 970, "y": 762, "z": 15 } - }, - "walkingRange": 10, - "entries": [ - { "name": "Skeleton", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f66d1ea-1745-45de-bbd3-bbf8032b53bc", - "name": "Spawner (113)", - "location": [2093, 147, 3], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1f952e99-08cc-47bb-a84e-b814533856ba", - "name": "Spawner (113)", - "location": [2379, 654, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1fd94ac8-c5bb-4bcf-8ce9-511bd67357ce", - "name": "Spawner (113)", - "location": [4455, 1358, 10], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "20d0534e-aa34-4f34-9615-5e37cb005052", - "name": "Spawner (113)", - "location": [1017, 826, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } - ] + "walkingRange": 10, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "2169b133-512b-45c3-aead-50a931bd972a", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "579f51aa-6bce-480c-98e0-b29d2c0fcdd1", + "name": "Spawner (2213)", "location": [955, 707, 0], - "map": "Trammel", + "map": "Felucca", "count": 8, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 947, "y": 699, "z": -128 }, - "end": { "x": 963, "y": 715, "z": 15 } - }, + "homeRange": 8, "walkingRange": 12, "entries": [ { "name": "Wraith", "maxCount": 4, "probability": 100 }, @@ -1246,2082 +8022,11 @@ ] }, { - "$type": "Spawner", - "guid": "2310c1b9-bc6e-4752-be08-751194837081", - "name": "Spawner (113)", - "location": [2429, 690, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "249b097d-dd14-4d6b-9ddf-f29eea89a011", - "name": "Spawner (113)", - "location": [2058, 87, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "24cd546e-c1a8-4e63-9a2f-a10e04391e45", - "name": "Spawner (113)", - "location": [480, 1641, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "24d72ad1-069e-480c-93db-58654afde815", - "name": "Spawner (113)", - "location": [1037, 1325, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "26e6a133-1816-47f1-af9a-c65e04e37cf8", - "name": "Spawner (113)", - "location": [516, 1585, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "27db6adc-96c4-4afd-987f-269fccd23635", - "name": "Spawner (113)", - "location": [4550, 1409, 8], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "28aa4986-a824-4960-b415-73d72a3c2c6b", - "name": "Spawner (113)", - "location": [2369, 3430, 3], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "28cd7bd8-2a62-421d-8a48-3409ae908a33", - "name": "Spawner (113)", - "location": [2109, 109, 3], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2acf5f1c-d200-4621-9cbf-9d013a9e553b", - "name": "Spawner (113)", - "location": [2549, 86, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2b1d17be-9b13-46ea-88ff-89d1c0ddfa18", - "name": "Spawner (113)", - "location": [960, 910, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ba259c9-3aba-4651-a0f8-9fb5ad4c203b", - "name": "Spawner (113)", - "location": [1652, 1400, 0], - "map": "Trammel", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2bac5389-5a5b-45a2-bcad-03e258bcbf5c", - "name": "Spawner (113)", - "location": [2013, 839, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2c184c7e-21c7-46f1-b437-5e4e9091f0b9", - "name": "Spawner (113)", - "location": [2048, 1066, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2c1c9bf1-535e-4e13-a6ab-714d3439cd95", - "name": "Spawner (113)", - "location": [2089, 3017, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2d65bd05-bec9-44cc-8027-564ab2597d52", - "name": "Spawner (113)", - "location": [2345, 676, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2df0361e-29e1-454f-970b-3e153ff01794", - "name": "Spawner (113)", - "location": [2503, 3593, 3], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2dfe455c-76ff-4ce4-84dc-46d1c84ad63f", - "name": "Spawner (113)", - "location": [4153, 601, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2e53312f-8367-4864-b655-db9b4b3b652d", - "name": "Spawner (113)", - "location": [3024, 3566, 15], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 7, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ecfe434-8acd-4f36-b873-626fd9337937", - "name": "Spawner (113)", - "location": [1042, 1024, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2f05dbd6-1d96-4f1c-ad4e-7808de1dcbe0", - "name": "Spawner (113)", - "location": [2113, 221, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "30a11e6d-014f-427b-a461-63d0653fa3b8", - "name": "Spawner (113)", - "location": [1827, 3429, 10], - "map": "Trammel", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "30b6c30f-49a6-4cc7-ae52-78107281393a", - "name": "Spawner (113)", - "location": [1726, 1362, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "313bfee9-788e-4860-8506-48c6e3a2c9b9", - "name": "Spawner (113)", - "location": [1837, 2334, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32e86bfa-a1f9-4b2e-bf35-9c83335710a9", - "name": "Spawner (113)", - "location": [1631, 650, 16], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3366bb0e-f7cf-475b-968a-25da2580d36b", - "name": "Spawner (113)", - "location": [4607, 3480, 29], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "33d5a886-8aea-47d2-910d-067fdacd1def", - "name": "Spawner (113)", - "location": [4493, 1364, 23], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "Horse", "maxCount": 1, "probability": 100 }, - { "name": "Rat", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3415104e-2a61-4225-9d64-be6609276576", - "name": "Spawner (113)", - "location": [1083, 783, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "36b8e938-4f69-4df8-a64b-6bb43c48bbb8", - "name": "Spawner (113)", - "location": [141, 1367, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "36d83036-0fe8-4652-aecd-8f1dbe8a1388", - "name": "Spawner (113)", - "location": [821, 1938, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "372bf2eb-9738-4465-8da3-64c980dfc01e", - "name": "Spawner (113)", - "location": [1640, 1431, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37715bfb-2a0c-45ac-b416-45a3c3302b42", - "name": "Spawner (113)", - "location": [2345, 112, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 }, - { "name": "Zombie", "maxCount": 9, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37b5c086-ef17-49b8-ad0b-125ec80df443", - "name": "Spawner (113)", - "location": [2077, 434, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "387b8a0f-f657-418e-a8a4-0713e3bf18bb", - "name": "Spawner (113)", - "location": [2034, 46, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "38e273a0-f54c-4f47-a468-379b40ac1fe2", - "name": "Spawner (113)", - "location": [1787, 1280, 0], - "map": "Trammel", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3947c59c-9e6f-44fb-938f-d1770f47b622", - "name": "Spawner (113)", - "location": [423, 1595, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 }, - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Reaper", "maxCount": 5, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "39d7b2f6-d423-4d6c-8068-985bbd4865ef", - "name": "Spawner (113)", - "location": [3327, 344, 4], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a546dd6-47b1-48e5-ba06-a2c594caa9b2", - "name": "Spawner (113)", - "location": [841, 344, 0], - "map": "Trammel", - "count": 2, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a7de24f-2da7-4229-8f6f-fbaf1adcecdd", - "name": "Spawner (113)", - "location": [541, 1645, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ad1ac6d-989f-4590-8d0e-0b1894c5c216", - "name": "Spawner (113)", - "location": [1479, 2094, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3c00f933-5883-4c21-aac6-0e9cb9565b56", - "name": "Spawner (113)", - "location": [1534, 1941, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3cfc616f-5f47-49de-88e6-0aea4dcd0fa6", - "name": "Spawner (113)", - "location": [1473, 229, 22], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d0cd619-9e1a-4d4e-a3d1-2af50eea591e", - "name": "Spawner (113)", - "location": [1042, 1920, 0], - "map": "Trammel", - "count": 9, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Spectre", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d38fbcb-a573-405a-94cd-1cda898164a1", - "name": "Spawner (113)", - "location": [1931, 3205, 0], - "map": "Trammel", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d8ac8b6-9e7c-42d8-8dc5-bc38d61e535b", - "name": "Spawner (113)", - "location": [1325, 1071, 0], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Troll", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d956977-3c38-429d-bf32-32968f930d34", - "name": "Spawner (113)", - "location": [4506, 1358, 23], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "BrownBear", "maxCount": 1, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 1, "probability": 100 }, - { "name": "BlackBear", "maxCount": 1, "probability": 100 }, - { "name": "DireWolf", "maxCount": 1, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, - { "name": "Cougar", "maxCount": 1, "probability": 100 }, - { "name": "Panther", "maxCount": 1, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, - { "name": "GreatHart", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3db40c33-f93d-4707-a22e-b56e2170744f", - "name": "Spawner (113)", - "location": [2035, 1018, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3dc3563c-c729-4217-b57b-535caa81fdb2", - "name": "Spawner (113)", - "location": [1504, 2007, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e3414ca-ff05-479b-ab44-3dd7f6c46470", - "name": "Spawner (113)", - "location": [4729, 3485, 0], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e72e6b3-e644-4850-8a8d-b5586ac9cf55", - "name": "Spawner (113)", - "location": [3172, 700, 8], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 5, "probability": 100 }, - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e87d826-1451-4347-b6b1-e272bcd135cc", - "name": "Spawner (113)", - "location": [2023, 1052, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3e9214fe-1512-458b-8302-1820c027c565", - "name": "Spawner (113)", - "location": [3360, 218, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ea552d7-eb71-4a77-9508-fd3e69b4870a", - "name": "Spawner (113)", - "location": [2146, 178, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3effe468-2b2d-4d56-952e-9c378e1d9a84", - "name": "Spawner (113)", - "location": [1635, 3253, 0], - "map": "Trammel", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3fa601f2-2e8d-4170-8c1f-1960889ef12d", - "name": "Spawner (113)", - "location": [1042, 1394, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3fd0a082-52c5-460c-b284-52893bddcc32", - "name": "Spawner (113)", - "location": [1949, 824, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3fd5313b-815f-4ec8-96e1-4ceb46497e78", - "name": "Spawner (113)", - "location": [1986, 1463, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "402644f1-43d6-4f2a-ba97-7f86c9010bed", - "name": "Spawner (113)", - "location": [2520, 3983, 3], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "40d4b2f8-9cb0-4b8f-b46e-d460c0aba610", - "name": "Spawner (113)", - "location": [1965, 160, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41cb561e-ed07-4766-a993-ea37d3480d8b", - "name": "Spawner (113)", - "location": [2033, 74, 1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41daa032-dfaf-437c-aea8-5ebd5ae28f8d", - "name": "Spawner (113)", - "location": [1530, 999, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4245c498-a5a6-436c-91db-68989f925ce7", - "name": "Spawner (113)", - "location": [1416, 1992, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "457024f7-fdc7-4080-858e-b578a99cc7d5", - "name": "Spawner (113)", - "location": [2549, 136, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "467e3ecc-401c-48d9-b064-f260e6891b6f", - "name": "Spawner (113)", - "location": [1375, 1330, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47544599-de5d-4da5-9fec-857b970a1ce5", - "name": "Spawner (113)", - "location": [1912, 1283, 0], - "map": "Trammel", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "476be46e-7393-4617-afc5-7d03ce670c16", - "name": "Spawner (113)", - "location": [1812, 1314, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47abac64-ba65-4d28-8d46-a2b9c6872407", - "name": "Spawner (113)", - "location": [1226, 449, 1], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "482755b3-dd05-4836-8a75-6f71edc3098f", - "name": "Spawner (113)", - "location": [2109, 2989, 30], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "48328575-a500-4ebc-afcd-de93ed7209bc", - "name": "Spawner (113)", - "location": [3013, 3591, 15], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 7, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "483c1680-2f1b-4e6f-9d15-2665c8ac264f", - "name": "Spawner (113)", - "location": [1891, 226, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4848785f-07e3-4e86-b62a-916774bf18e1", - "name": "Spawner (113)", - "location": [947, 418, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 10, "probability": 100 }, - { "name": "DireWolf", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, - { "name": "Gazer", "maxCount": 10, "probability": 100 }, - { "name": "Harpy", "maxCount": 10, "probability": 100 }, - { "name": "Lizardman", "maxCount": 10, "probability": 100 }, - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4925ff65-5ca5-4f85-9d9b-6a228d778618", - "name": "Spawner (113)", - "location": [4612, 1418, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49369990-5bfb-4c5a-b64a-2dddbb892391", - "name": "Spawner (113)", - "location": [4605, 1365, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49b84769-1509-4e93-8bfb-6f952dc6ba84", - "name": "Spawner (113)", - "location": [1969, 1409, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "49c9739c-a70f-4687-859d-56e5f72d59dc", - "name": "Spawner (113)", - "location": [4206, 471, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b6c7f37-04ff-4f95-9167-8f299dd5ab5f", - "name": "Spawner (113)", - "location": [2548, 1173, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4bbc1a4a-e118-4531-aaae-b7c8ea90206f", - "name": "Spawner (113)", - "location": [2033, 936, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4c9f16f2-a241-42ad-bf26-7fed6e7b0693", - "name": "Spawner (113)", - "location": [617, 1326, 0], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4cb1e2e1-c235-4ea9-ac7a-86497d31fe93", - "name": "Spawner (113)", - "location": [1162, 704, 6], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4cc2ab2c-5acf-4063-92e1-db91e44a9870", - "name": "Spawner (113)", - "location": [1869, 265, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4cc9345c-fe2a-47b7-ab4c-bf9097b047d6", - "name": "Spawner (113)", - "location": [1883, 827, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4e4aa09e-a831-4f56-b374-8d39f3977e30", - "name": "Spawner (113)", - "location": [1231, 192, 6], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4eedbefe-4446-443f-a5be-6b1c56c842cb", - "name": "Spawner (113)", - "location": [1833, 1245, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4f8dcb56-8c3f-49fd-b778-75d243d770c6", - "name": "Spawner (113)", - "location": [1915, 2385, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4fcd562e-e69c-4792-9375-7df489650310", - "name": "Spawner (113)", - "location": [1180, 737, 1], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50c40fb1-1e3d-4d2e-9d95-6bfd7d57fa3f", - "name": "Spawner (113)", - "location": [4455, 1434, 10], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5127a1ad-b427-472b-aa36-8239cdf04b15", - "name": "Spawner (113)", - "location": [659, 1318, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5175dc34-0726-4304-bab5-bc9533355114", - "name": "Spawner (113)", - "location": [1080, 1963, 0], - "map": "Trammel", - "count": 9, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Spectre", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "51ec0cce-de67-476d-abb7-7882f5add66e", - "name": "Spawner (113)", - "location": [1749, 474, 6], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "52640d62-3b31-4a7a-819a-518a6f3fae3e", - "name": "Spawner (113)", - "location": [851, 495, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 }, - { "name": "Reaper", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "528890d0-376a-43c3-a832-69efcd0d56c1", - "name": "Spawner (113)", - "location": [1447, 2038, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "538691b1-1499-4081-96bd-2c2dd54a4599", - "name": "Spawner (113)", - "location": [1094, 3482, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54106890-a301-41d2-99ed-475fa5ebce54", - "name": "Spawner (113)", - "location": [1799, 1394, 0], - "map": "Trammel", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5436a84f-0593-4be2-bead-26e7960898a7", - "name": "Spawner (113)", - "location": [2189, 298, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "543e6d36-efb0-4382-b526-b615fd7d0cbf", - "name": "Spawner (113)", - "location": [2478, 100, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "547bc7d5-edce-4e93-967c-3430f125219f", - "name": "Spawner (113)", - "location": [1619, 2725, 10], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "54e24a3b-cd43-4265-9f30-d73326dd45a9", - "name": "Spawner (113)", - "location": [3462, 136, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "55959a0a-7dac-4bea-9689-01d82dcb1212", - "name": "Spawner (113)", - "location": [1491, 238, 22], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "563ed9d1-69ea-4407-8bcb-db6cdf69ca81", - "name": "Spawner (113)", - "location": [1877, 1515, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "566cfa8d-c4be-4e84-9779-19fd29a5c4af", - "name": "Spawner (113)", - "location": [1249, 2030, 0], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "574d5350-83ca-4a90-8494-1234476d9004", - "name": "Spawner (113)", - "location": [1152, 3471, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "574f956f-5ff2-4a72-a157-6ffb296b52b2", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "bbbaf085-fb59-4fa5-a525-860584b9f84d", + "name": "Spawner (2213)", "location": [962, 692, 0], - "map": "Trammel", + "map": "Felucca", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -3335,375 +8040,43 @@ ] }, { - "$type": "Spawner", - "guid": "577bcfed-a871-4aa1-b989-189a320eba2f", - "name": "Spawner (113)", - "location": [964, 365, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 10, "probability": 100 }, - { "name": "DireWolf", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, - { "name": "Gazer", "maxCount": 10, "probability": 100 }, - { "name": "Harpy", "maxCount": 10, "probability": 100 }, - { "name": "Lizardman", "maxCount": 10, "probability": 100 }, - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "57956924-7db0-48b3-b993-d4284974093a", - "name": "Spawner (113)", - "location": [2351, 148, 0], - "map": "Trammel", - "count": 10, + "type": "Spawner", + "guid": "fa3003a8-2ab1-4b80-a6d4-eae803b8ba80", + "name": "Spawner (2213)", + "location": [948, 703, 0], + "map": "Felucca", + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 2, + "walkingRange": 5, "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 }, - { "name": "Zombie", "maxCount": 9, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "583032d0-fa06-4e3c-ba13-1054ca5c022f", - "name": "Spawner (113)", - "location": [3173, 771, 0], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 5, "probability": 100 }, - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5ab6b426-1743-4857-9784-498e4156521c", - "name": "Spawner (113)", - "location": [418, 1665, 0], - "map": "Trammel", - "count": 7, + "type": "Spawner", + "guid": "bb22f21a-37bb-4e7a-b2a8-06db80a58e3a", + "name": "Spawner (2213)", + "location": [1016, 821, 0], + "map": "Felucca", + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] + "homeRange": 2, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "5bb0725a-0be8-4432-9256-9d4d41d7c168", - "name": "Spawner (113)", - "location": [1915, 928, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d05dae7-686d-4ca8-82c2-3b67c57adc30", - "name": "Spawner (113)", - "location": [4615, 1469, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d425d30-7a90-46b3-99ee-1be6e09e2ea4", - "name": "Spawner (113)", - "location": [892, 1963, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5da854a2-bca1-46e3-88fc-98d5dd640339", - "name": "Spawner (113)", - "location": [1867, 2962, 7], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5fa9a115-ce2a-4213-b48d-c9744e848d12", - "name": "Spawner (113)", - "location": [1268, 774, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "61a0bee6-034f-4e72-851a-0069ea03c769", - "name": "Spawner (113)", - "location": [1802, 1506, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "61b07a04-4d35-4af2-9fef-2575e7aa81f6", - "name": "Spawner (113)", - "location": [2034, 108, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "61c1a7de-68ec-4a55-b6d7-b71c315d93d1", - "name": "Spawner (113)", - "location": [2079, 1018, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "61e4635f-38a1-4bed-9d85-40665f917751", - "name": "Spawner (113)", - "location": [1723, 683, 6], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "620249c5-7ec3-4f73-bab8-52dd3f89d0c0", - "name": "Spawner (113)", - "location": [4162, 395, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "62abe17a-ffc3-43f4-b71d-281ff6e875bb", - "name": "Spawner (113)", - "location": [840, 1063, 0], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "62ff4954-fb25-4416-8ac1-3120144c4c14", - "name": "Spawner (113)", - "location": [1147, 3524, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6357f444-0d29-4154-8256-71dde67c0337", - "name": "Spawner (113)", - "location": [423, 1595, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "637a3aaf-fa96-4bdc-a770-ec273932916f", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "043c3e4b-4a7f-4cf4-b5f7-54064d1c0aeb", + "name": "Spawner (2213)", "location": [1015, 706, 0], - "map": "Trammel", + "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -3716,546 +8089,124 @@ ] }, { - "$type": "Spawner", - "guid": "640d4b0e-9a86-4035-8e71-c37fadb6a717", - "name": "Spawner (113)", - "location": [4214, 655, 1], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "66594f81-5b6b-4cfc-ab1c-48805b3ca936", - "name": "Spawner (113)", - "location": [4090, 325, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "66634507-bcd9-4a0a-b5c9-5b6dd0c9adc9", - "name": "Spawner (113)", - "location": [157, 1487, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6681af59-d2bd-46ee-96f7-15f9e48cf967", - "name": "Spawner (113)", - "location": [1983, 1517, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, - { "name": "Gazer", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "668b691e-8bdc-4002-93b8-ccc8d3ef1882", - "name": "Spawner (113)", - "location": [2077, 2305, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6701bc25-8a58-4dc8-aba0-5be8467a0c96", - "name": "Spawner (113)", - "location": [1726, 1362, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6781029f-3b4a-4030-ad2d-83948df6f257", - "name": "Spawner (113)", - "location": [1435, 2261, 5], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "67a947e4-5f60-4065-9469-43a878cb1bfc", - "name": "Spawner (113)", - "location": [4013, 239, 1], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "686118b7-c5ff-48d2-9035-55a6eabee085", - "name": "Spawner (113)", - "location": [821, 675, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "688f208d-8821-4c70-9b98-28f8594741bd", - "name": "Spawner (113)", - "location": [3322, 271, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "68c72e6d-a7fd-431c-8685-37f47afc4b0b", - "name": "Spawner (113)", - "location": [1407, 1281, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "68d63e2a-4dc7-49a8-9876-f6bbe9704568", - "name": "Spawner (113)", - "location": [1873, 2426, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "68ef188a-6290-4a22-a06b-089287e867ad", - "name": "Spawner (113)", - "location": [2033, 3041, 15], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6901fe1c-8be5-4cba-949a-8401a0cb640d", - "name": "Spawner (113)", - "location": [1743, 589, 16], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "697858f2-61ad-43d6-8cbb-97a5b872d8f4", - "name": "Spawner (113)", - "location": [516, 1585, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 }, - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Reaper", "maxCount": 5, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6987ffa4-6e58-48b7-a2a4-12d2eef6b86d", - "name": "Spawner (113)", - "location": [3390, 259, 4], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6a4006aa-ef63-42db-9a08-cc7df6515351", - "name": "Spawner (113)", - "location": [2203, 1379, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "bdc7678b-7586-468a-8fd2-185a10a2190d", + "name": "Spawner (2213)", + "location": [1017, 826, 0], + "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 2, - "walkingRange": 2, + "homeRange": 5, + "walkingRange": 5, "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "6a93c7a8-af80-45e5-8f20-920eb51c09ff", - "name": "Spawner (113)", - "location": [815, 1603, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Spectre", "maxCount": 5, "probability": 100 }, - { "name": "Zombie", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6aab8758-a4ee-4a8a-bc76-33cdbdd297c0", - "name": "Spawner (113)", - "location": [3382, 280, 9], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6b3ea5f3-1cf1-4b1f-85e5-97deb2d2741b", - "name": "Spawner (113)", - "location": [1803, 2981, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bd439c3-5ce9-4971-a8a8-45a432089787", - "name": "Spawner (113)", - "location": [1866, 3040, 15], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6c7570b9-067e-4887-8309-b653be55bd69", - "name": "Spawner (113)", - "location": [2621, 99, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6cc36c7c-cfd0-42f4-9e04-9ff5dbd963c5", - "name": "Spawner (113)", - "location": [1131, 3415, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6cf68fd8-cc90-4d19-b6cd-cc14fddfa5b9", - "name": "Spawner (113)", - "location": [1891, 2331, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6d3feafc-3dc2-441d-a9ab-18c468552742", - "name": "Spawner (113)", - "location": [1980, 832, -1], - "map": "Trammel", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Ratman", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6d941b3f-588d-49fe-a879-d50c6005b8c9", - "name": "Spawner (113)", - "location": [1947, 2294, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "700be867-c251-4ecc-a320-867ba65b024c", - "name": "Spawner (113)", - "location": [4531, 1363, 23], - "map": "Trammel", - "count": 4, + "type": "Spawner", + "guid": "1973fede-bee5-4d12-9620-4ac08754278c", + "name": "Spawner (2213)", + "location": [4596, 3607, 30], + "map": "Felucca", + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 4, - "walkingRange": 15, + "homeRange": 25, + "walkingRange": 50, "entries": [ - { "name": "Mummy", "maxCount": 1, "probability": 100 }, - { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, - { "name": "Scorpion", "maxCount": 1, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } + { "name": "Daemon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "71d63638-7d38-4e09-a599-2b6eace4ffff", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "1d32dbb7-e905-45c1-b8fe-a73bfb6d5806", + "name": "Spawner (2213)", + "location": [4571, 3572, 31], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96b77dd9-7c8a-4a6b-b3a3-18fbbde09fdb", + "name": "Spawner (2213)", + "location": [4595, 3574, 75], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [{ "name": "Daemon", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d296a28-acda-437c-82f8-b0dfb62be85d", + "name": "Spawner (2213)", + "location": [4618, 3570, 30], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4ae1f335-1c4e-4be3-b495-209990644141", + "name": "Spawner (2213)", + "location": [4595, 3570, 30], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be52369b-69c9-435f-b280-bc04927624aa", + "name": "Spawner (2213)", + "location": [608, 1702, 0], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Wisp", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c158877f-aa51-4f38-9e0f-0b9792b6603c", + "name": "Spawner (2213)", "location": [4530, 1387, 23], - "map": "Trammel", + "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -4271,1440 +8222,49 @@ ] }, { - "$type": "Spawner", - "guid": "72083877-79f3-49cf-a915-5ffb89bf1e48", - "name": "Spawner (113)", - "location": [805, 483, 0], - "map": "Trammel", - "count": 2, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "729cb568-ac02-4737-b9e7-4abd87c5f66a", - "name": "Spawner (113)", - "location": [1525, 2053, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "73a9fc66-3b63-4478-a41b-07df4bc6be50", - "name": "Spawner (113)", - "location": [736, 1938, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "73b21795-cf2b-49a5-9ee8-9e25a136cb0b", - "name": "Spawner (113)", - "location": [1004, 1369, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "76ef18b2-8642-45f5-99e0-e206048ed383", + "name": "Spawner (2213)", + "location": [4511, 1394, 23], + "map": "Felucca", "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "73e77785-b460-4d5d-840c-b13c96c1ae54", - "name": "Spawner (113)", - "location": [1295, 328, 22], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "748028eb-c0ef-4c6f-bbb9-e43359901099", - "name": "Spawner (113)", - "location": [2454, 248, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75118095-d436-46b3-8e73-d2cc56c66ce9", - "name": "Spawner (113)", - "location": [741, 1838, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75631a7a-9d4e-4464-9197-1e2ce34369cd", - "name": "Spawner (113)", - "location": [1810, 647, 0], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75764c8f-2910-43c9-9fde-d03748e5361e", - "name": "Spawner (113)", - "location": [1944, 102, 4], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, - { "name": "Gazer", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75b4dbbf-f0d3-4f4e-8148-520aba12abaa", - "name": "Spawner (113)", - "location": [1143, 180, 14], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "769fdfa1-dd5c-4b03-89a2-2e86fb41888f", - "name": "Spawner (113)", - "location": [2074, 3069, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "77395a8a-4c2d-4f5f-9692-58c84c4146a7", - "name": "Spawner (113)", - "location": [757, 1401, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lich", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "78613e32-9f25-4c0d-a566-f63f4b57607d", - "name": "Spawner (113)", - "location": [189, 1344, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "78c65c1b-b461-4203-9a8a-ad62146c81ba", - "name": "Spawner (113)", - "location": [2540, 130, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "792fd336-4cdb-41aa-91f2-ea5a748bf4f3", - "name": "Spawner (113)", - "location": [4658, 3376, 0], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "793c4766-d5b6-42f0-83ff-d0a267c26846", - "name": "Spawner (113)", - "location": [1872, 1347, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "799df1e3-5107-4858-8b7c-2e271b9756f1", - "name": "Spawner (113)", - "location": [480, 1641, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 }, - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Reaper", "maxCount": 5, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7abf83f6-4989-40d9-8b25-ee6299f59786", - "name": "Spawner (113)", - "location": [4461, 3106, 0], - "map": "Trammel", - "count": 5, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "OrcishMage", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b163acc-acd7-462a-a048-8f9a4ffc5cb8", - "name": "Spawner (113)", - "location": [1912, 1302, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b2a0a94-8536-4656-8b41-311715aabe6a", - "name": "Spawner (113)", - "location": [1970, 3052, 15], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7bb510ff-11c6-4ed6-88b2-28786db5e331", - "name": "Spawner (113)", - "location": [1222, 1418, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7bed7e28-21ce-4afc-b05e-ab91bc2a9b0a", - "name": "Spawner (113)", - "location": [2000, 141, 1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c517cdb-1ddc-43fc-a8f1-2929874413a1", - "name": "Spawner (113)", - "location": [2355, 179, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 }, - { "name": "Zombie", "maxCount": 9, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d3e512e-32f2-412f-a9de-4140e4e97a9e", - "name": "Spawner (113)", - "location": [4595, 3570, 30], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 8, - "entries": [ - { "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }, - { "name": "TreasureChestLevel4", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d4c137a-0a24-456a-837f-cfd51ab71a3a", - "name": "Spawner (113)", - "location": [1070, 2256, 5], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "80b26b9b-afc9-40a9-b6e7-47e04586711e", - "name": "Spawner (113)", - "location": [1912, 1302, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8103dd4b-9100-4c4b-bcb1-4b0b5bf054f8", - "name": "Spawner (113)", - "location": [1361, 1392, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81bfcb26-842e-4e50-91c2-572c3aee4bee", - "name": "Spawner (113)", - "location": [1147, 1048, 5], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81e040fc-9b61-42ca-acfb-5d96eadca6a4", - "name": "Spawner (113)", - "location": [1060, 1375, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "837610d0-3ae0-49e0-aa44-a02a4a3e379f", - "name": "Spawner (113)", - "location": [1214, 617, 13], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "83e06193-d371-4d06-93b6-9a4147fddee3", - "name": "Spawner (113)", - "location": [1300, 1481, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84124f00-8aa4-491d-a4d9-53e65b98036f", - "name": "Spawner (113)", - "location": [1967, 1296, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "847e76b7-5a08-438c-80a3-56285b0f4696", - "name": "Spawner (113)", - "location": [1946, 186, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "85053a68-d91a-4502-841e-697dde7fdbf5", - "name": "Spawner (113)", - "location": [1016, 821, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 10, - "entries": [ - { "name": "Lich", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8642c7b8-1bbe-4e19-a85a-29df23ca9944", - "name": "Spawner (113)", - "location": [956, 452, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 10, "probability": 100 }, - { "name": "DireWolf", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, - { "name": "Gazer", "maxCount": 10, "probability": 100 }, - { "name": "Harpy", "maxCount": 10, "probability": 100 }, - { "name": "Lizardman", "maxCount": 10, "probability": 100 }, - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8791d5da-aabf-4ef8-9e50-bd73ebf233d3", - "name": "Spawner (113)", - "location": [1130, 830, 9], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8792576e-488e-47bc-a672-01aee138d62e", - "name": "Spawner (113)", - "location": [1968, 2346, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 6, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, - { "name": "Lizardman", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 }, - { "name": "Slime", "maxCount": 6, "probability": 100 }, - { "name": "Snake", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8829fbf2-d914-48fc-bdf4-7286f71acb4c", - "name": "Spawner (113)", - "location": [1802, 1506, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "889af6a9-6809-4fdf-a28b-cc38170df5de", - "name": "Spawner (113)", - "location": [849, 1262, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "88e2ec5e-fbd6-4459-9128-74e244d4b01f", - "name": "Spawner (113)", - "location": [634, 1504, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "88ec5fa0-837d-41e1-9b72-864f82f40070", - "name": "Spawner (113)", - "location": [4485, 1387, 23], - "map": "Trammel", - "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 4, - "walkingRange": 15, - "entries": [ - { "name": "Alligator", "maxCount": 2, "probability": 100 }, - { "name": "BullFrog", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 1, "probability": 100 }, - { "name": "SwampTentacle", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8b7f54d4-bf98-4c1c-9c79-c0b6b6fe0082", - "name": "Spawner (113)", - "location": [1812, 3036, 5], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8c33edf4-3f59-4b73-bfee-9d57b2a4e7be", - "name": "Spawner (113)", - "location": [1383, 1949, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d14a61d-ea19-473b-8d74-b57ac8fd7f89", - "name": "Spawner (113)", - "location": [1200, 633, 6], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d3e5300-d499-4a9b-b4e0-f6daa25ac14b", - "name": "Spawner (113)", - "location": [2138, 323, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d6e79e4-c6c2-456c-9bf3-629536077417", - "name": "Spawner (113)", - "location": [1191, 288, 11], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ebc9621-1061-4ab5-aed9-c6d72a4c1514", - "name": "Spawner (113)", - "location": [2044, 2323, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, "walkingRange": 10, "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } + { "name": "IceSerpent", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "FrostOoze", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "8ff6852c-8cd0-434b-9eb8-9afd5940805c", - "name": "Spawner (113)", - "location": [4066, 569, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 80, - "walkingRange": 80, - "entries": [ - { "name": "PolarBear", "maxCount": 8, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 8, "probability": 100 }, - { "name": "Walrus", "maxCount": 8, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "91340fd5-c54d-4502-9253-adaf4569c7a3", - "name": "Spawner (113)", - "location": [1626, 266, 16], - "map": "Trammel", - "count": 13, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, - { "name": "Gazer", "maxCount": 11, "probability": 100 }, - { "name": "Harpy", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "93468ee6-bb6d-43d0-b4b7-eb86de5bcbc0", - "name": "Spawner (113)", - "location": [1651, 2413, 5], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "934bc4f3-d414-4cb1-8809-cdceca368616", - "name": "Spawner (113)", - "location": [579, 1305, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "94502e42-4b90-4a37-8c20-6a8988b7857b", - "name": "Spawner (113)", - "location": [4566, 1359, 8], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95bd45a8-6d9d-4663-a009-90bbfdb6c783", - "name": "Spawner (113)", - "location": [1140, 2087, 0], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "97faf684-9350-46c3-8da6-c3892f7f604d", - "name": "Spawner (113)", - "location": [1038, 1149, 0], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9956e808-d58e-42bd-8c6d-3a22c652480d", - "name": "Spawner (113)", - "location": [1337, 293, 22], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9a4e77a5-dd20-465a-a274-4a6dedad51b8", - "name": "Spawner (113)", - "location": [2038, 1023, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9ac79ca6-546f-4634-8048-5a9c59b662c2", - "name": "Spawner (113)", - "location": [1911, 2934, 10], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b5a9736-d1d3-4492-9674-e786e8ea6b55", - "name": "Spawner (113)", - "location": [1013, 980, 0], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9b819902-07ef-442c-bff5-d8201a95eb92", - "name": "Spawner (113)", - "location": [3168, 629, 8], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 5, "probability": 100 }, - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9bfb3456-0e22-429d-bf41-8b597f0c8170", - "name": "Spawner (113)", - "location": [1445, 1365, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c2beb8c-e9fd-4484-9395-d13dcec9502b", - "name": "Spawner (113)", - "location": [229, 1419, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c326969-58f1-4463-83a1-980619b49133", - "name": "Spawner (113)", - "location": [634, 1462, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "ac2a868d-5ee3-40a2-b50a-ca2b98717487", + "name": "Spawner (2213)", + "location": [4511, 1394, 23], + "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Orc", "maxCount": 3, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c656301-ba27-4061-b13c-67e32ca73351", - "name": "Spawner (113)", - "location": [1474, 1297, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9c79e8b6-595f-4914-9372-be6a16e42bf8", - "name": "Spawner (113)", - "location": [3382, 375, 4], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9cb6e3ec-d48c-416e-9e5e-4ce284e350b8", - "name": "Spawner (113)", - "location": [1097, 970, 2], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9e4825b9-1cbe-4a27-a4d2-e2552901924f", - "name": "Spawner (113)", - "location": [2349, 722, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, + "homeRange": 4, "walkingRange": 10, "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "9e4c86bb-8737-41c8-924c-f226b9aedfde", - "name": "Spawner (113)", - "location": [2107, 358, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9e52d805-c99f-49b2-8a7a-6c5e4e33038a", - "name": "Spawner (113)", - "location": [2137, 271, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9f8ff142-0e90-4ee2-bb29-c6960d4dc308", - "name": "Spawner (113)", - "location": [1203, 502, 30], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a08f71d7-7504-448f-814c-c7c34216019c", - "name": "Spawner (113)", - "location": [1387, 3109, 0], - "map": "Trammel", - "count": 60, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, - { "name": "Gorilla", "maxCount": 10, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a2162b8e-47c1-449a-bca4-8b1e29a7fa5b", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "bb340a5d-bc6e-4bf1-b384-5e3c4b1edf2b", + "name": "Spawner (2213)", "location": [4498, 1394, 23], - "map": "Trammel", + "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -5727,709 +8287,30 @@ ] }, { - "$type": "Spawner", - "guid": "a2691312-c437-4d3b-bb69-8e2a26a432cf", - "name": "Spawner (113)", - "location": [278, 1355, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a2b03adb-c492-487f-bdc1-4dc7f70818cd", - "name": "Spawner (113)", - "location": [666, 1825, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a33ee66a-277f-492a-940d-51c2654e1804", - "name": "Spawner (113)", - "location": [4264, 725, 1], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a40f806a-f922-415f-b88f-63dde478379b", - "name": "Spawner (113)", - "location": [1954, 2903, 30], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a48df719-c919-4066-b8a2-6c679032f463", - "name": "Spawner (113)", - "location": [1361, 522, 19], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a4e772e0-b570-46da-a6b8-4d84ad26dfcb", - "name": "Spawner (113)", - "location": [1215, 2195, 5], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a50eb7ab-718b-4cd9-b418-2a55ef6c8eb6", - "name": "Spawner (113)", - "location": [1243, 2437, 5], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a559cad1-a142-4969-bef2-8e154d658ec6", - "name": "Spawner (113)", - "location": [1979, 885, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a5677568-fa43-430c-9f73-0ed52edd08c9", - "name": "Spawner (113)", - "location": [1849, 2915, 30], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a5b60719-c3a1-4169-8070-a3d98da16df3", - "name": "Spawner (113)", - "location": [1340, 1268, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a6c232b6-7b8e-41b4-b958-3aa5080efaa4", - "name": "Spawner (113)", - "location": [2966, 3591, 11], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 7, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a6eefb00-5655-4f27-9d1b-9baa28ab2ffc", - "name": "Spawner (113)", - "location": [1759, 259, 21], - "map": "Trammel", - "count": 13, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, - { "name": "Gazer", "maxCount": 11, "probability": 100 }, - { "name": "Harpy", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a6ffb380-e7b3-4b1c-a89e-49a68626b151", - "name": "Spawner (113)", - "location": [1967, 1296, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a71a8ce9-b6a6-40b5-ba55-ae04b8940322", - "name": "Spawner (113)", - "location": [1831, 449, 0], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a756d7c7-629c-45cb-970e-deee0190baa3", - "name": "Spawner (113)", - "location": [1059, 2461, 0], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a7bf87e5-4f5b-4b8c-9710-4e4ab3343720", - "name": "Spawner (113)", - "location": [1692, 1463, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a7ea4508-795e-4bb9-97c3-41fe28f1f214", - "name": "Spawner (113)", - "location": [1849, 953, 7], - "map": "Trammel", - "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Ratman", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a94c7436-52ec-4820-9c38-88fcada332a9", - "name": "Spawner (113)", - "location": [1184, 217, 27], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a99fcecd-571e-4151-8c6d-b3d0611e4f5a", - "name": "Spawner (113)", - "location": [809, 1615, 0], - "map": "Trammel", - "count": 6, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a9a67290-a828-48fa-a3f7-a0b7f10fbd45", - "name": "Spawner (113)", - "location": [1192, 958, 5], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a9bdd33d-5beb-4e5f-b2a7-25bbe6de9194", - "name": "Spawner (113)", - "location": [1300, 607, 15], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa2759b8-efff-4868-97d0-674c377f63f3", - "name": "Spawner (113)", - "location": [4494, 1426, 8], - "map": "Trammel", + "type": "Spawner", + "guid": "f490fa81-fb97-460f-bc0f-5ab6db6fa9e4", + "name": "Spawner (2213)", + "location": [4485, 1387, 23], + "map": "Felucca", "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 4, + "walkingRange": 15, "entries": [ - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } + { "name": "Alligator", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ab5fd7a4-8a79-4ee4-8369-b9ed647a5fe6", - "name": "Spawner (113)", - "location": [1829, 874, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ab664229-d260-4bfd-9948-f2bb2b433504", - "name": "Spawner (113)", - "location": [3434, 316, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "abdfd7cf-7042-4881-b6bd-16231d352cb3", - "name": "Spawner (113)", - "location": [4596, 3607, 30], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 50, - "entries": [ - { "name": "Daemon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "acf731fa-53ee-48da-b0ef-356f9d61c38f", - "name": "Spawner (113)", - "location": [1305, 1326, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad79f7ea-b8bd-4821-8b80-d5d4fbe72c24", - "name": "Spawner (113)", - "location": [974, 1186, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad8627b0-4cf5-4650-96aa-7bbc32e9e18b", - "name": "Spawner (113)", - "location": [1029, 1969, 0], - "map": "Trammel", - "count": 9, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Spectre", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad938586-87a9-4dd7-a5b1-59cd95cd6d46", - "name": "Spawner (113)", - "location": [868, 488, 0], - "map": "Trammel", - "count": 2, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "af95e8e2-8d1f-43a8-ab45-cd79154fd947", - "name": "Spawner (113)", - "location": [4480, 1439, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "afd3e919-f63c-437a-bd36-b3431b394683", - "name": "Spawner (113)", - "location": [823, 960, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "afde0aad-5648-43b2-b77b-08270970f7b5", - "name": "Spawner (113)", - "location": [1361, 275, 16], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b0758b1c-baa8-4216-989d-000515e5227d", - "name": "Spawner (113)", - "location": [2143, 3953, 3], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b2274f08-2f99-4bed-b485-e6dd3228564f", - "name": "Spawner (113)", - "location": [1922, 173, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "Troll", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b280374f-3913-422f-a8fb-67a5b5774cbd", - "name": "Spawner (113)", - "location": [4511, 1394, 23], - "map": "Trammel", + "type": "Spawner", + "guid": "071f9e9b-26c7-4bc6-a5a0-7867a5049939", + "name": "Spawner (2213)", + "location": [4493, 1364, 23], + "map": "Felucca", "count": 6, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -6437,658 +8318,101 @@ "homeRange": 4, "walkingRange": 10, "entries": [ - { "name": "IceSerpent", "maxCount": 1, "probability": 100 }, - { "name": "IceSnake", "maxCount": 1, "probability": 100 }, - { "name": "PolarBear", "maxCount": 1, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, - { "name": "FrostOoze", "maxCount": 1, "probability": 100 } + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Boar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "b48075d3-37f2-4471-bd77-272fd0de644b", - "name": "Spawner (113)", - "location": [3920, 287, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b487a09f-6222-4251-a712-839233ccf85d", - "name": "Spawner (113)", - "location": [2462, 725, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b518dc40-be46-4410-9296-164bc7f5d6db", - "name": "Spawner (113)", - "location": [844, 1235, 0], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b5eb5bfe-0848-489f-9717-a8891ae7a1e9", - "name": "Spawner (113)", - "location": [1166, 2958, 0], - "map": "Trammel", - "count": 4, + "type": "Spawner", + "guid": "456e985f-4c8d-4f71-b998-d6efc19957f6", + "name": "Spawner (2213)", + "location": [4493, 1364, 23], + "map": "Felucca", + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Savage", "maxCount": 2, "probability": 100 }, - { "name": "SavageRider", "maxCount": 1, "probability": 100 }, - { "name": "SavageShaman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b77a44c2-c523-4718-ba7d-ace36df8fde5", - "name": "Spawner (113)", - "location": [1871, 1076, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, + "homeRange": 4, "walkingRange": 10, "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "b84a2533-eb93-46ba-8053-dc0c04146704", - "name": "Spawner (113)", - "location": [2589, 204, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "type": "Spawner", + "guid": "f372a717-67d5-4f65-a903-6bf06a8107a4", + "name": "Spawner (2213)", + "location": [4506, 1358, 23], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 4, + "walkingRange": 10, "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "Panther", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "b8a9dfe2-9620-4fe5-8d99-d1533d2f7da9", - "name": "Spawner (113)", - "location": [728, 1242, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "type": "Spawner", + "guid": "57f5c71c-e443-4f41-a4a0-dac167db544d", + "name": "Spawner (2213)", + "location": [4519, 1358, 23], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 4, + "walkingRange": 15, "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } + { "name": "Alligator", "maxCount": 1, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 }, + { "name": "Gorilla", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GiantToad", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "b8c9dac4-eb05-4d17-9741-c566db9f045f", - "name": "Spawner (113)", - "location": [1930, 864, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b950c826-32fc-4800-92c3-04292c5ab38b", - "name": "Spawner (113)", - "location": [1852, 841, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba037fbd-9398-4438-bee8-e6fce7d07c01", - "name": "Spawner (113)", - "location": [733, 1937, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba2ae76c-dcca-4df2-9274-fa8136a61efb", - "name": "Spawner (113)", - "location": [2963, 3617, 3], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 7, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba9ff3a4-d363-4fac-b113-bab612b9614f", - "name": "Spawner (113)", - "location": [1866, 938, -1], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bbc84d48-e44c-404a-9912-3f08fd80c2a8", - "name": "Spawner (113)", - "location": [848, 917, 0], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bbd652ce-e378-4c20-84c3-42788aac59b2", - "name": "Spawner (113)", - "location": [1755, 1474, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "0c68cb1c-7662-4ebc-a9e9-166ce2574f9e", + "name": "Spawner (2213)", + "location": [4531, 1363, 23], + "map": "Felucca", "count": 4, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, - { "name": "Gazer", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf514878-4b14-4e01-acd6-ccff87fe9980", - "name": "Spawner (113)", - "location": [1158, 3457, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c113386f-3b9c-40bf-a0bd-a45027fc8201", - "name": "Spawner (113)", - "location": [1180, 1354, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c133b0ca-429c-4f25-909f-36ce88862d76", - "name": "Spawner (113)", - "location": [240, 1443, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2aa2c25-88b6-44d5-af14-e47c7b2a798a", - "name": "Spawner (113)", - "location": [4613, 3272, 0], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2aba6ad-8726-41c2-92bb-73f1176d0422", - "name": "Spawner (113)", - "location": [1862, 1434, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2c12823-bb40-49bb-93ab-87fec1ebda5f", - "name": "Spawner (113)", - "location": [1266, 1982, 0], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c4feab07-a874-4ac6-a248-7a292a021674", - "name": "Spawner (113)", - "location": [2468, 144, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c5a204bf-a47d-47be-9f0b-9182bc4ad39f", - "name": "Spawner (113)", - "location": [1993, 1030, 0], - "map": "Trammel", - "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 4, + "walkingRange": 15, "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "c5df49ed-42f4-4533-bd84-0c6d38a134a6", - "name": "Spawner (113)", - "location": [1841, 3000, 10], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 4, "probability": 100 }, - { "name": "Ghoul", "maxCount": 4, "probability": 100 }, - { "name": "Ogre", "maxCount": 4, "probability": 100 }, - { "name": "Orc", "maxCount": 4, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c5eca3a0-ab6e-4a09-aea0-e5c02573761a", - "name": "Spawner (113)", - "location": [1055, 1442, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c673f9b8-1ae1-4de9-8d6f-b907c7b758a1", - "name": "Spawner (113)", - "location": [1797, 1393, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c710dd46-a5f7-4b86-8a96-a787c08e03cb", - "name": "Spawner (113)", - "location": [987, 725, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 3, "probability": 100 }, - { "name": "Lich", "maxCount": 3, "probability": 100 }, - { "name": "LichLord", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 }, - { "name": "Zombie", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c7248e18-5065-4a1f-8278-cd383e39a7ad", - "name": "Spawner (113)", - "location": [948, 703, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 946, "y": 701, "z": -128 }, - "end": { "x": 950, "y": 705, "z": 15 } - }, - "walkingRange": 5, - "entries": [ - { "name": "Wraith", "maxCount": 1, "probability": 100 }, - { "name": "Shade", "maxCount": 1, "probability": 100 }, - { "name": "Spectre", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c76cdb5d-42cb-4ab4-a7ec-6db8582f8f14", - "name": "Spawner (113)", - "location": [1682, 252, 6], - "map": "Trammel", - "count": 13, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, - { "name": "Gazer", "maxCount": 11, "probability": 100 }, - { "name": "Harpy", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c8129ab7-03a2-498f-b96e-cd45b8913271", - "name": "Spawner (113)", - "location": [1862, 1434, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c9fd2dbc-cb36-489c-8149-b815c16f8d09", - "name": "Spawner (113)", - "location": [1001, 787, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 3, "probability": 100 }, - { "name": "Lich", "maxCount": 3, "probability": 100 }, - { "name": "LichLord", "maxCount": 3, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 }, - { "name": "Zombie", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ca9bfc78-2835-4a5b-bc0d-6fe008b79b5c", - "name": "Spawner (113)", - "location": [1004, 1369, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "caeb038e-ab75-41e9-bce1-c5be5d25551e", - "name": "Spawner (113)", - "location": [4595, 3574, 75], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 50, - "entries": [ - { "name": "Daemon", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cb69e358-5119-4573-a663-19bb5ab7bdcd", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "f71bc29c-8c95-4531-8bdf-51f27e350b39", + "name": "Spawner (2213)", "location": [4512, 1372, 23], - "map": "Trammel", + "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -7104,199 +8428,11 @@ ] }, { - "$type": "Spawner", - "guid": "cbbe09b8-6b83-4d65-90b7-985dfa8335ad", - "name": "Spawner (113)", - "location": [3928, 393, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cc56ca14-97aa-4dc3-99a3-f684078e007e", - "name": "Spawner (113)", - "location": [3440, 405, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Ogre", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cc87ef42-d3a3-4a6b-8d44-ac0d38d75739", - "name": "Spawner (113)", - "location": [1956, 48, 4], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, - { "name": "Gazer", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd06b247-c98e-4218-93b9-b96ff85ba165", - "name": "Spawner (113)", - "location": [805, 463, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 8, "probability": 100 }, - { "name": "DireWolf", "maxCount": 8, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "Ratman", "maxCount": 8, "probability": 100 }, - { "name": "Reaper", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd5e98b7-cddd-4087-b1f4-c28350797537", - "name": "Spawner (113)", - "location": [1804, 247, 0], - "map": "Trammel", - "count": 13, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, - { "name": "Gazer", "maxCount": 11, "probability": 100 }, - { "name": "Harpy", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd80c05e-08c8-411f-aecd-0b030c095094", - "name": "Spawner (113)", - "location": [4618, 3570, 30], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cf631a13-97b5-4714-87db-23ad2e1a5ed4", - "name": "Spawner (113)", - "location": [1067, 2117, 5], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cfca73f2-a84f-402f-b50f-52b3fa851d6e", - "name": "Spawner (113)", - "location": [1055, 1442, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d1a8c12d-2186-49a2-81ae-6f14ea963ca1", - "name": "Spawner (113)", - "location": [1595, 3045, 0], - "map": "Trammel", - "count": 30, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 60, - "walkingRange": 100, - "entries": [ - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, - { "name": "Gorilla", "maxCount": 5, "probability": 100 }, - { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, - { "name": "WhippingVine", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d2b4059a-7a86-4b8d-aac4-d98f8ac842b5", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "1f59609c-ffa6-4a3c-ae8f-92caf32391a1", + "name": "Spawner (2213)", "location": [4512, 1380, 23], - "map": "Trammel", + "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -7312,751 +8448,11 @@ ] }, { - "$type": "Spawner", - "guid": "d3a4bb67-71eb-420c-b965-8dec687d5b66", - "name": "Spawner (113)", - "location": [1990, 976, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d4054a1d-96e5-47dc-8d44-853d05e4d9c7", - "name": "Spawner (113)", - "location": [232, 1350, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d4bc9831-73ab-4b9b-a6b9-21a02bf60b8c", - "name": "Spawner (113)", - "location": [1225, 1477, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d5de5b0d-fffe-496d-8167-85ff964f45d0", - "name": "Spawner (113)", - "location": [3170, 672, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d6092a82-aeba-47d2-81a2-e51332742aa4", - "name": "Spawner (113)", - "location": [1232, 2093, 0], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d61a295a-c615-4af8-a057-bf82c9c753c7", - "name": "Spawner (113)", - "location": [3042, 3590, 5], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 7, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Mongbat", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, - { "name": "Snake", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d739d7a9-b14c-49ed-ad4d-6765d520bec5", - "name": "Spawner (113)", - "location": [1996, 73, 8], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, - { "name": "Gazer", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d77d3fc0-43bc-47da-9a91-5c0f82f285aa", - "name": "Spawner (113)", - "location": [1812, 1314, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d80fc757-5a54-44c3-9700-ad91a1cee771", - "name": "Spawner (113)", - "location": [1240, 1317, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Lizardman", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9258088-60b5-4ded-a22a-280d4eee8573", - "name": "Spawner (113)", - "location": [2143, 3068, 20], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Spectre", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9737161-8220-4189-ade1-d48a07a04d7a", - "name": "Spawner (113)", - "location": [1987, 822, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Scorpion", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9fa1621-0c99-4082-8075-0e4a1f8e25f7", - "name": "Spawner (113)", - "location": [1072, 2195, 5], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da8f61e2-5aed-4bb9-a59e-c3c01cf2f4da", - "name": "Spawner (113)", - "location": [819, 1841, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc54c094-335f-4eb0-835b-df374aa73808", - "name": "Spawner (113)", - "location": [788, 1894, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc6d7ab6-b963-4216-85bd-99e6a7234b5d", - "name": "Spawner (113)", - "location": [1166, 818, 5], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd48c66c-dbf3-4729-9b05-896d93615858", - "name": "Spawner (113)", - "location": [1939, 1310, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:45:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 1, - "walkingRange": 10, - "entries": [ - { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, - { "name": "RatCamp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "decca3ad-554d-4411-b3eb-ee2535528d04", - "name": "Spawner (113)", - "location": [633, 1484, 0], - "map": "Trammel", - "count": 14, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 20, - "entries": [ - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, - { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df7d496c-92b3-487a-a49c-92ef77818fd0", - "name": "Spawner (113)", - "location": [672, 1825, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e085923c-489c-4b7b-b9bf-c6a4ca1b60c1", - "name": "Spawner (113)", - "location": [2354, 3503, 0], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Alligator", "maxCount": 8, "probability": 100 }, - { "name": "Lizardman", "maxCount": 8, "probability": 100 }, - { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, - { "name": "Snake", "maxCount": 8, "probability": 100 }, - { "name": "Wisp", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e0bc4302-a584-4339-b0b4-ebd374568aa7", - "name": "Spawner (113)", - "location": [1832, 517, 0], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 60, - "walkingRange": 60, - "entries": [ - { "name": "AirElemental", "maxCount": 1, "probability": 100 }, - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e146ad1f-0e5e-4018-956e-faddb83059ca", - "name": "Spawner (113)", - "location": [4511, 1394, 23], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, - { "name": "FrostTroll", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e252043e-aff1-4bea-b8bc-32e668d5dd59", - "name": "Spawner (113)", - "location": [2375, 725, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e263026b-c626-4692-9a46-67aafc5bc4c0", - "name": "Spawner (113)", - "location": [4504, 1458, 8], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "TimberWolf", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e5029ee0-ff6b-4336-bd11-196cfba8392c", - "name": "Spawner (113)", - "location": [1021, 1415, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e62f1551-be29-4561-9de2-8029f7d167ea", - "name": "Spawner (113)", - "location": [2319, 591, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Ghoul", "maxCount": 8, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Orc", "maxCount": 8, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, - { "name": "Skeleton", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e6349924-e5bd-4073-9b11-4836e15f9f91", - "name": "Spawner (113)", - "location": [793, 1088, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e6dfcbb5-788b-4b78-b87b-d3587f7ccc94", - "name": "Spawner (113)", - "location": [2028, 987, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, - { "name": "Alligator", "maxCount": 3, "probability": 100 }, - { "name": "BogThing", "maxCount": 3, "probability": 100 }, - { "name": "Bogling", "maxCount": 3, "probability": 100 }, - { "name": "Corpser", "maxCount": 3, "probability": 100 }, - { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, - { "name": "Lizardman", "maxCount": 3, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, - { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, - { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, - { "name": "Ratman", "maxCount": 3, "probability": 100 }, - { "name": "SewerRat", "maxCount": 3, "probability": 100 }, - { "name": "Slime", "maxCount": 3, "probability": 100 }, - { "name": "Snake", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e7ba00e8-d827-4435-8455-690aa3f2482d", - "name": "Spawner (113)", - "location": [2372, 210, 0], - "map": "Trammel", - "count": 10, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, - { "name": "Lizardman", "maxCount": 9, "probability": 100 }, - { "name": "Ogre", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Skeleton", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 }, - { "name": "Zombie", "maxCount": 9, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e97ce87e-cb97-493c-a692-d48ebf51a4a3", - "name": "Spawner (113)", - "location": [1898, 1265, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e9ef0197-6591-49f6-ae85-2a94712d64a6", - "name": "Spawner (113)", - "location": [1122, 718, 1], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eac45551-873f-48bb-9296-121cbd96318c", - "name": "Spawner (113)", - "location": [1427, 2525, 0], - "map": "Trammel", - "count": 50, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 150, - "walkingRange": 150, - "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ebb84eb3-cfb1-4925-9e17-ea98ad5df79e", - "name": "Spawner (113)", - "location": [1898, 1265, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ebbc9fcf-7284-4d3c-9d01-045bf8f31a5d", - "name": "Spawner (113)", - "location": [1723, 252, 16], - "map": "Trammel", - "count": 13, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "DireWolf", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, - { "name": "Gazer", "maxCount": 11, "probability": 100 }, - { "name": "Harpy", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec76be04-eeb8-4466-9328-008c0a24ded9", - "name": "Spawner (113)", - "location": [1021, 1415, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 6, "probability": 100 }, - { "name": "Gazer", "maxCount": 6, "probability": 100 }, - { "name": "Harpy", "maxCount": 6, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, - { "name": "Ogre", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ee147b0d-5a5f-4ad0-9d7a-974230bb3f53", - "name": "Spawner (113)", - "location": [798, 1935, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", - "maxDelay": "01:00:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "efb1438c-3ee7-466f-80b8-629a1e319b19", - "name": "Spawner (113)", + "type": "Spawner", + "guid": "f3330409-2d73-4dd3-88e5-449d92789a28", + "name": "Spawner (2213)", "location": [4500, 1382, 23], - "map": "Trammel", + "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -8072,545 +8468,275 @@ ] }, { - "$type": "Spawner", - "guid": "efe392b2-baea-4098-9549-0439983f0e4e", - "name": "Spawner (113)", - "location": [1276, 431, 16], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f019e22a-5265-452b-ac1f-9f1cd8018041", - "name": "Spawner (113)", - "location": [1216, 2259, 5], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 30, - "entries": [ - { "name": "Imp", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f0a8c785-3472-41f4-b59b-e3a0252e6962", - "name": "Spawner (113)", - "location": [4571, 3572, 31], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 25, - "walkingRange": 50, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f1707f1c-a8ae-4c20-9ae8-a8efea2f92dd", - "name": "Spawner (113)", - "location": [541, 1645, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 }, - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Reaper", "maxCount": 5, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f1e6337c-a7e4-4011-a777-d5e32ab30ace", - "name": "Spawner (113)", - "location": [1797, 1393, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 7, "probability": 100 }, - { "name": "DireWolf", "maxCount": 7, "probability": 100 }, - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Reaper", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f2676738-8f89-44ba-bd49-531b4a48c13a", - "name": "Spawner (113)", - "location": [2171, 1356, 0], - "map": "Trammel", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, - { "name": "Savage", "maxCount": 3, "probability": 100 }, - { "name": "SavageRider", "maxCount": 2, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, - { "name": "OrcishMage", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f26c8e00-9f91-478b-8d8e-089f164d81cf", - "name": "Spawner (113)", - "location": [971, 1308, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f2ad3f8c-d318-42b4-8a19-20a025ee838d", - "name": "Spawner (113)", - "location": [1284, 433, 6], - "map": "Trammel", - "count": 3, - "minDelay": "01:00:00", + "type": "Spawner", + "guid": "d3cd2585-5346-47a3-840c-3b4ccbdd34ab", + "name": "Spawner (2213)", + "location": [821, 675, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", "maxDelay": "01:00:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, - { "name": "Gazer", "maxCount": 3, "probability": 100 } + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f3cc3559-e015-4904-b37e-d464bcd6d7ef", - "name": "Spawner (113)", - "location": [2115, 48, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 3, "probability": 100 }, - { "name": "Ettin", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f430ad0e-4341-455b-9f4e-230f9f461670", - "name": "Spawner (113)", - "location": [1475, 1958, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 5, "probability": 100 }, - { "name": "Mongbat", "maxCount": 5, "probability": 100 }, - { "name": "Orc", "maxCount": 5, "probability": 100 }, - { "name": "Ratman", "maxCount": 5, "probability": 100 }, - { "name": "Troll", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f4ca25be-16e8-4b4a-acc1-b87e77f4953e", - "name": "Spawner (113)", - "location": [909, 1175, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 11, "probability": 100 }, - { "name": "Ettin", "maxCount": 11, "probability": 100 }, - { "name": "Ghoul", "maxCount": 11, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, - { "name": "Lizardman", "maxCount": 11, "probability": 100 }, - { "name": "Ogre", "maxCount": 11, "probability": 100 }, - { "name": "Orc", "maxCount": 11, "probability": 100 }, - { "name": "Reaper", "maxCount": 11, "probability": 100 }, - { "name": "Skeleton", "maxCount": 11, "probability": 100 }, - { "name": "Troll", "maxCount": 11, "probability": 100 }, - { "name": "Zombie", "maxCount": 11, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f4d67563-65b3-45b4-9cb9-02ecaf27d37e", - "name": "Spawner (113)", - "location": [418, 1665, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Corpser", "maxCount": 5, "probability": 100 }, - { "name": "DireWolf", "maxCount": 5, "probability": 100 }, - { "name": "Harpy", "maxCount": 5, "probability": 100 }, - { "name": "Reaper", "maxCount": 5, "probability": 100 }, - { "name": "Skeleton", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f5219997-2933-414d-b9c1-8a38c7d7169a", - "name": "Spawner (113)", - "location": [204, 1483, 0], - "map": "Trammel", - "count": 11, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 9, "probability": 100 }, - { "name": "DireWolf", "maxCount": 9, "probability": 100 }, - { "name": "Ettin", "maxCount": 9, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, - { "name": "Gazer", "maxCount": 9, "probability": 100 }, - { "name": "Harpy", "maxCount": 9, "probability": 100 }, - { "name": "Orc", "maxCount": 9, "probability": 100 }, - { "name": "Reaper", "maxCount": 9, "probability": 100 }, - { "name": "Troll", "maxCount": 9, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f5d51f12-ef63-4187-ad60-00f961a67f30", - "name": "Spawner (113)", - "location": [3929, 235, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f608b229-cfdb-4397-9908-249713f11b1f", - "name": "Spawner (113)", - "location": [759, 1828, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ghoul", "maxCount": 7, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f62d079f-93a0-412b-a5a2-f088592be535", - "name": "Spawner (113)", - "location": [4576, 3356, 0], - "map": "Trammel", - "count": 10, - "minDelay": "01:00:00", + "type": "Spawner", + "guid": "df065cb1-c48e-426b-a4df-f2ac9f1c585d", + "name": "Spawner (2213)", + "location": [1042, 1394, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", "maxDelay": "01:00:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "Orc", "maxCount": 10, "probability": 100 } + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f67516b1-35ef-4231-890f-5626fccbfef1", - "name": "Spawner (113)", - "location": [608, 1702, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Wisp", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f689f7d9-5600-4199-86fd-e4bfc33e670f", - "name": "Spawner (113)", - "location": [2155, 1370, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 2, - "walkingRange": 2, - "entries": [ - { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, - { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f8d690f4-c3ea-4da8-973b-7e442dd4fc2d", - "name": "Spawner (113)", - "location": [1692, 1463, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Mongbat", "maxCount": 3, "probability": 100 }, - { "name": "Orc", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f9e6dac6-5887-4a6e-a321-7166ad16b575", - "name": "Spawner (113)", - "location": [1905, 883, -1], - "map": "Trammel", - "count": 4, - "minDelay": "01:00:00", + "type": "Spawner", + "guid": "eab22a55-6437-4024-a9e0-b351b03c0042", + "name": "Spawner (2213)", + "location": [2540, 130, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", "maxDelay": "01:00:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, - { "name": "Ratman", "maxCount": 4, "probability": 100 } + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fa392213-96a7-4165-8c45-f5a96988b74e", - "name": "Spawner (113)", - "location": [2177, 3034, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Ratman", "maxCount": 7, "probability": 100 }, - { "name": "Skeleton", "maxCount": 7, "probability": 100 }, - { "name": "Spectre", "maxCount": 7, "probability": 100 }, - { "name": "Zombie", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fa7aa472-6b9e-4cc5-b138-720d5d413925", - "name": "Spawner (113)", - "location": [2659, 147, 0], - "map": "Trammel", - "count": 8, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Ettin", "maxCount": 7, "probability": 100 }, - { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, - { "name": "Lizardman", "maxCount": 7, "probability": 100 }, - { "name": "Ogre", "maxCount": 7, "probability": 100 }, - { "name": "Orc", "maxCount": 7, "probability": 100 }, - { "name": "Troll", "maxCount": 7, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fb7f5bad-4b07-4c56-bd44-8fb33a088fa5", - "name": "Spawner (113)", - "location": [879, 1956, 0], - "map": "Trammel", - "count": 8, - "minDelay": "01:00:00", + "type": "Spawner", + "guid": "faea3534-a9c2-4dd8-af03-3f231ad976e3", + "name": "Spawner (2213)", + "location": [3170, 672, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", "maxDelay": "01:00:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "Ettin", "maxCount": 8, "probability": 100 }, - { "name": "Harpy", "maxCount": 8, "probability": 100 }, - { "name": "Ogre", "maxCount": 8, "probability": 100 }, - { "name": "Troll", "maxCount": 8, "probability": 100 } + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fc005a29-add5-4782-9fa3-5a15644b9d97", - "name": "Spawner (113)", - "location": [1060, 1375, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 30, - "walkingRange": 30, - "entries": [ - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Ratman", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fc7f9d6e-d0ce-4879-b674-ba5ed365450a", - "name": "Spawner (113)", - "location": [1089, 2033, 0], - "map": "Trammel", - "count": 9, - "minDelay": "01:00:00", + "type": "Spawner", + "guid": "a9bd52d6-d0bb-4999-8a8b-e5168463c718", + "name": "Spawner (2213)", + "location": [2548, 1173, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", "maxDelay": "01:00:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "Ettin", "maxCount": 2, "probability": 100 }, - { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, - { "name": "Wisp", "maxCount": 1, "probability": 100 }, - { "name": "Mongbat", "maxCount": 6, "probability": 100 }, - { "name": "Orc", "maxCount": 6, "probability": 100 }, - { "name": "Troll", "maxCount": 6, "probability": 100 } + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "fe495dbf-3e33-4998-a844-3fdba286070b", - "name": "Spawner (113)", - "location": [1000, 330, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "type": "Spawner", + "guid": "87bb1dc1-dc09-44cf-b2cc-1b72e55ac804", + "name": "Spawner (2213)", + "location": [3602, 2815, 28], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "AirElemental", "maxCount": 2, "probability": 100 }, - { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, - { "name": "Corpser", "maxCount": 10, "probability": 100 }, - { "name": "DireWolf", "maxCount": 10, "probability": 100 }, - { "name": "Ettin", "maxCount": 10, "probability": 100 }, - { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, - { "name": "Gazer", "maxCount": 10, "probability": 100 }, - { "name": "Harpy", "maxCount": 10, "probability": 100 }, - { "name": "Lizardman", "maxCount": 10, "probability": 100 }, - { "name": "Orc", "maxCount": 10, "probability": 100 }, - { "name": "Ratman", "maxCount": 10, "probability": 100 }, - { "name": "Reaper", "maxCount": 10, "probability": 100 } + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ffa2abe9-90a6-4b35-ae74-538bd30c2419", - "name": "Spawner (113)", - "location": [4057, 300, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:20:00", - "maxDelay": "00:20:00", + "type": "Spawner", + "guid": "6629f955-9f48-46ed-affe-6ef6bb823646", + "name": "Spawner (2213)", + "location": [4480, 1439, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", "team": 0, - "homeRange": 30, - "walkingRange": 30, + "homeRange": 1, + "walkingRange": 10, "entries": [ - { "name": "PolarBear", "maxCount": 4, "probability": 100 }, - { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, - { "name": "Walrus", "maxCount": 4, "probability": 100 }, - { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96afa046-b31d-4bda-8839-026e8e3fd86f", + "name": "Spawner (2213)", + "location": [1152, 3471, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce5519e9-137c-4db7-b7a6-4dadcbdaa9ad", + "name": "Spawner (2213)", + "location": [1530, 999, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3e704fd-0a49-4396-a196-b56afeea5ffc", + "name": "Spawner (2213)", + "location": [1871, 1076, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eed75f19-218d-4bfe-8312-953669844813", + "name": "Spawner (2213)", + "location": [788, 1894, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84b2cd30-182a-4251-b95d-3904e80ee9ee", + "name": "Spawner (2213)", + "location": [1064, 2488, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6432a74-1126-4214-b9a0-6dec45594bd7", + "name": "Spawner (2213)", + "location": [2103, 3371, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a7b60db-3c30-449e-b20a-f85336dd5446", + "name": "Spawner (2213)", + "location": [2044, 2323, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "713534d6-6d42-4846-95e2-e43b5aaa340f", + "name": "Spawner (2213)", + "location": [1939, 1310, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e89c2bad-96b7-475d-a5a7-d759364bde62", + "name": "Spawner (2213)", + "location": [2349, 722, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/uoml/felucca/PaintedCaves.json b/Distribution/Data/Spawns/uoml/felucca/PaintedCaves.json new file mode 100644 index 000000000..ee892cbe8 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/PaintedCaves.json @@ -0,0 +1,42 @@ +[ + { + "type": "Spawner", + "guid": "2733b112-93bf-47cc-b071-02e87c3150ab", + "name": "Spawner (2214)", + "location": [6279, 879, -1], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc6475e8-885e-4e5e-bf83-49c3e408ac06", + "name": "Spawner (2214)", + "location": [6275, 879, -2], + "map": "Felucca", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Troglodyte", "maxCount": 3, "probability": 100 }, + { "name": "Grobu", "maxCount": 3, "probability": 100 }, + { "name": "Lurg", "maxCount": 3, "probability": 100 }, + { "name": "GiantRat", "maxCount": 3, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/PalaceOfParoxysmus.json b/Distribution/Data/Spawns/uoml/felucca/PalaceOfParoxysmus.json new file mode 100644 index 000000000..ba92e8721 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/PalaceOfParoxysmus.json @@ -0,0 +1,282 @@ +[ + { + "type": "Spawner", + "guid": "604b87ed-6718-4802-b1b6-f572ed7d9935", + "name": "Spawner (2215)", + "location": [6295, 616, -50], + "map": "Felucca", + "count": 27, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Daemon", "maxCount": 6, "probability": 100 }, + { "name": "Succubus", "maxCount": 6, "probability": 100 }, + { "name": "InterredGrizzle", "maxCount": 6, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 6, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 6, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 6, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 3, "probability": 100 }, + { "name": "Putrefier", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeastLord", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a586bde-b6a0-4f4d-b147-1e1886aae735", + "name": "Spawner (2215)", + "location": [6384, 580, -50], + "map": "Felucca", + "count": 31, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "InterredGrizzle", "maxCount": 4, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 4, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 3, "probability": 100 }, + { "name": "Balron", "maxCount": 9, "probability": 100 }, + { "name": "Succubus", "maxCount": 9, "probability": 100 }, + { "name": "Daemon", "maxCount": 9, "probability": 100 }, + { "name": "PlagueBeastLord", "maxCount": 11, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 11, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e695b242-221c-49b5-853a-9e943afe8355", + "name": "Spawner (2215)", + "location": [6328, 517, -50], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "PoisonElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a0d9e92-6661-4ad8-a38e-dcf7516e05de", + "name": "Spawner (2215)", + "location": [6354, 455, -40], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "PlagueBeast", "maxCount": 5, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 5, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 5, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04a75e0b-4499-4fe6-8bd6-49e3dbf0d528", + "name": "Spawner (2215)", + "location": [6416, 447, -40], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "PlagueBeast", "maxCount": 8, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 8, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 7, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad3fbc20-7914-4651-a7c1-994d9b88ed22", + "name": "Spawner (2215)", + "location": [6466, 543, -50], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Balron", "maxCount": 6, "probability": 100 }, + { "name": "Succubus", "maxCount": 6, "probability": 100 }, + { "name": "Daemon", "maxCount": 6, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeastLord", "maxCount": 8, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 8, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 8, "probability": 100 }, + { "name": "InterredGrizzle", "maxCount": 5, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b7da2a27-b13e-4113-b8c8-e5f2e7ac2e3a", + "name": "Spawner (2215)", + "location": [6292, 462, -50], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "ChaosDaemon", "maxCount": 7, "probability": 100 }, + { "name": "Moloch", "maxCount": 7, "probability": 100 }, + { "name": "Succubus", "maxCount": 7, "probability": 100 }, + { "name": "Daemon", "maxCount": 7, "probability": 100 }, + { "name": "Balron", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04cc01ab-f7da-4992-8913-4db4ea63149e", + "name": "Spawner (2215)", + "location": [6298, 395, 60], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "74269ae8-5e94-4496-ad04-611e81b14168", + "name": "Spawner (2215)", + "location": [6404, 378, -40], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 18, + "entries": [ + { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 2, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ef37566-7b9c-47fb-a88c-d6c0177d92f3", + "name": "Spawner (2215)", + "location": [6418, 358, -40], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "PlagueSpawn", "maxCount": 12, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 12, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 12, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 12, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 12, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 2, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 2, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 2, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3ba2375-fc73-4996-9f54-3a09dc39c653", + "name": "Spawner (2215)", + "location": [6319, 348, 60], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "EarthElemental", "maxCount": 5, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 5, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 5, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 5, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 1, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c1149a5-ac2a-42e6-b6e2-296e6ca05602", + "name": "Spawner (2215)", + "location": [6249, 352, 60], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "AcidElemental", "maxCount": 6, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 6, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af6ef316-bb00-417f-852c-14e9c98ca74c", + "name": "Spawner (2215)", + "location": [6285, 353, 60], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "PoisonElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bbed003d-43d6-4690-b93e-763f42a4187f", + "name": "Spawner (2215)", + "location": [6353, 400, 60], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 4, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/PrismOfLight.json b/Distribution/Data/Spawns/uoml/felucca/PrismOfLight.json new file mode 100644 index 000000000..72ca0dd36 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/PrismOfLight.json @@ -0,0 +1,315 @@ +[ + { + "type": "Spawner", + "guid": "b87b2fe5-b1dc-46b1-9c35-f1627c33ad04", + "name": "Spawner (2216)", + "location": [6553, 157, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "CrystalLatticeSeeker", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c04125c7-bad1-4747-8e55-7664e9797a6d", + "name": "Spawner (2216)", + "location": [6474, 74, -34], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [ + { "name": "Rat", "maxCount": 9, "probability": 100 }, + { "name": "IceSnake", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f083b3bb-c6c8-4774-a9d7-65d6b2cfc831", + "name": "Spawner (2216)", + "location": [6552, 155, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Wisp", "maxCount": 5, "probability": 100 }, + { "name": "CrystalWisp", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ccfb405-2b18-4397-9d18-5b37302d62e4", + "name": "Spawner (2216)", + "location": [6521, 139, -20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Wisp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49f09e2e-fb04-42a9-96e2-d930c04f729f", + "name": "Spawner (2216)", + "location": [6530, 139, -20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Wisp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f620b501-c242-4301-a813-b4d7a44e141f", + "name": "Spawner (2216)", + "location": [6576, 91, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalSeaSerpent", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "539ddfd6-d1ad-4e3f-b9cf-414a6dc0553a", + "name": "Spawner (2216)", + "location": [6509, 176, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "UnfrozenMummy", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84ae0146-70ef-44e4-9159-db9a045c14c9", + "name": "Spawner (2216)", + "location": [6536, 79, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9c7afc30-19c5-4034-a344-8b75e52b6b40", + "name": "Spawner (2216)", + "location": [6547, 91, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50809274-807a-49ee-b9f9-6c13693a56d0", + "name": "Spawner (2216)", + "location": [6572, 89, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalHydra", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8f88efe1-68b8-478e-8166-b03984e956e1", + "name": "Spawner (2216)", + "location": [6504, 181, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93922052-811a-473e-b3b2-eddf5380829a", + "name": "Spawner (2216)", + "location": [6475, 101, -44], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "ShadowWisp", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c66d4bde-6308-44bb-80fe-f6f0070beda7", + "name": "Spawner (2216)", + "location": [6478, 174, 4], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Wisp", "maxCount": 6, "probability": 100 }, + { "name": "CrystalWisp", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4e01097c-b1f4-4d22-ad9b-dcd117eeaeec", + "name": "Spawner (2216)", + "location": [6566, 120, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "CrystalVortex", "maxCount": 7, "probability": 100 }, + { "name": "IceElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9dc74009-4df1-4872-874f-fcc908ec105b", + "name": "Spawner (2216)", + "location": [6534, 178, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalWisp", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ce6b636-4bca-44d7-adf2-5ef841d08a61", + "name": "Spawner (2216)", + "location": [6535, 179, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "CrystalWisp", "maxCount": 5, "probability": 100 }, + { "name": "CrystalLatticeSeeker", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "adf86a17-213e-408b-be55-3cb37b9a1a36", + "name": "Spawner (2216)", + "location": [6502, 120, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e82c7115-8675-4d30-b321-c5e6a3b43ea0", + "name": "Spawner (2216)", + "location": [6507, 151, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Protector", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8fe6c0c3-9fcb-4725-b6b2-ee89df036d80", + "name": "Spawner (2216)", + "location": [6532, 79, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed613a4e-b19f-422e-943c-c94a29411ff8", + "name": "Spawner (2216)", + "location": [6578, 183, 31], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Ferret", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6912561a-5610-4441-8c64-16f4bd9f73f8", + "name": "Spawner (2216)", + "location": [6510, 171, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Protector", "maxCount": 3, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Reagents.json b/Distribution/Data/Spawns/uoml/felucca/Reagents.json new file mode 100644 index 000000000..654c50634 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Reagents.json @@ -0,0 +1,1382 @@ +[ + { + "type": "Spawner", + "guid": "1bafedf2-97d4-484d-9ef3-4597666765ce", + "name": "Spawner (2229)", + "location": [2854, 816, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "472b629a-65dc-4e26-a728-19a1dc715740", + "name": "Spawner (2229)", + "location": [4590, 1448, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd38b465-2ed5-4634-ae0a-e8b2ab38c2d4", + "name": "Spawner (2229)", + "location": [2490, 1096, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "11579f1f-1437-48f9-9061-a409f8468750", + "name": "Spawner (2229)", + "location": [1566, 1140, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1853a130-1967-4711-bb38-522e045b5820", + "name": "Spawner (2229)", + "location": [1514, 1584, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "046e36ec-faf1-4f5f-8f8f-4cf50e2ba3ef", + "name": "Spawner (2229)", + "location": [1938, 1408, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "450db427-aea5-48a7-95f4-36f2e64d4aec", + "name": "Spawner (2229)", + "location": [2374, 652, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0034e4c4-162c-4569-bd5d-9faef725ecd5", + "name": "Spawner (2229)", + "location": [2054, 516, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b82556d-2f5d-4fa0-b53e-fc95ed035b3f", + "name": "Spawner (2229)", + "location": [1286, 504, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b89e1c55-2a8a-4e42-aa92-5718f3699c0f", + "name": "Spawner (2229)", + "location": [746, 636, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d233e91c-82d3-4130-a747-2be35a3c7e55", + "name": "Spawner (2229)", + "location": [1134, 964, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a6221c9-7105-4707-aaa1-8e9005016a6b", + "name": "Spawner (2229)", + "location": [718, 1180, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21879949-0b91-42cc-ad2d-7b616b27ad05", + "name": "Spawner (2229)", + "location": [342, 1464, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54bb9a76-f991-425d-9b3a-20310659b1dd", + "name": "Spawner (2229)", + "location": [702, 1752, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "16af2efe-3319-4812-9089-2fe793653f21", + "name": "Spawner (2229)", + "location": [1114, 1416, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0199ccf-a3a0-4888-9939-42b7a5ad553a", + "name": "Spawner (2229)", + "location": [1150, 1876, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7a28f716-9fa0-4686-ad8c-ca1e7ab1a9e3", + "name": "Spawner (2229)", + "location": [1582, 2028, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2fe931f6-6f60-411c-a235-dcc4b9cb9fc2", + "name": "Spawner (2229)", + "location": [1934, 2328, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7eaf59ab-e769-4772-aaa3-06d741e424ae", + "name": "Spawner (2229)", + "location": [1490, 2472, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b3ccf5f-b1e6-4cd7-a1f7-b4662fe4ff9a", + "name": "Spawner (2229)", + "location": [1070, 2320, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4144faa9-61d4-47fa-be5c-665e94989f9c", + "name": "Spawner (2229)", + "location": [1466, 3032, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a796506-6692-420c-bc95-cd7fcf99d1b9", + "name": "Spawner (2229)", + "location": [2062, 964, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8255d504-90e7-4ca3-a2bd-1cdfc72983f7", + "name": "Spawner (2229)", + "location": [1846, 3260, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40ea761c-a1da-48b7-9663-0571df9a28cc", + "name": "Spawner (2229)", + "location": [1078, 2772, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5544628-f982-40f3-b44a-f5db0507a62e", + "name": "Spawner (2229)", + "location": [4710, 3796, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96d35be0-ba86-4f2e-8c27-2ef75a684c93", + "name": "Spawner (2229)", + "location": [5926, 3888, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8553a096-7a8e-4f69-accf-401bfef947fb", + "name": "Spawner (2229)", + "location": [5474, 3904, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54855ff6-f02b-4bc4-a803-455719541850", + "name": "Spawner (2229)", + "location": [5934, 3436, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4d5ac68-9b53-488f-94bd-6e6d1dec3493", + "name": "Spawner (2229)", + "location": [5462, 3452, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d10ddc0-6866-4c69-945e-830fe9fa18c5", + "name": "Spawner (2229)", + "location": [5674, 3120, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "065cdc7b-b2ce-4339-b45c-c96f4a734723", + "name": "Spawner (2229)", + "location": [5618, 2744, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6813adda-8875-4603-9a78-57aab7688e56", + "name": "Spawner (2229)", + "location": [5326, 2492, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbc82493-6b0c-46a7-a243-e0cb875f4bcf", + "name": "Spawner (2229)", + "location": [5966, 2456, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0d2917b-3296-4811-bf68-c9eb6fac7f2b", + "name": "Spawner (2229)", + "location": [5234, 3068, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd4c385d-b00e-47ab-a6bc-f2cb490c9567", + "name": "Spawner (2229)", + "location": [6042, 2916, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fe88456-6018-41e4-b9af-a129a1025651", + "name": "Spawner (2229)", + "location": [3602, 2948, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59cfb257-99a9-4a1d-9220-8aeb3712dd60", + "name": "Spawner (2229)", + "location": [3538, 2536, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50c090e2-807c-4a99-a467-a0430a8cc232", + "name": "Spawner (2229)", + "location": [2734, 2168, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f55cc26e-f8f2-47cc-9af0-ccd026d9a18d", + "name": "Spawner (2229)", + "location": [3714, 2152, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e17f7124-9255-4f70-94b4-c6716544873d", + "name": "Spawner (2229)", + "location": [3674, 1216, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d276b701-845a-4648-ba54-fde4f1a7d725", + "name": "Spawner (2229)", + "location": [3994, 328, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b229c260-ee7d-4aab-bacf-3e974e2e8934", + "name": "Spawner (2229)", + "location": [4166, 584, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "870cd346-44a3-45cd-b8f2-b65543021467", + "name": "Spawner (2229)", + "location": [3198, 504, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c91dce8-087c-4dff-96bf-6d2f32ff57b9", + "name": "Spawner (2229)", + "location": [2750, 368, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ab7cf0fc-e53d-4f1b-a6f6-7c9bc21df8df", + "name": "Spawner (2229)", + "location": [1694, 688, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b42e3cb-2224-4516-9fe2-165a19bcda3d", + "name": "Spawner (2229)", + "location": [366, 900, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abb85eaf-f92e-41a0-a6cf-8c0bfab71e30", + "name": "Spawner (2229)", + "location": [642, 2216, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b745dd5f-b8ef-4950-baef-569e80994036", + "name": "Spawner (2229)", + "location": [2226, 3532, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f229444f-13ea-4dba-aa0e-d0a4bd0887ee", + "name": "Spawner (2229)", + "location": [1850, 2796, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36a65d30-bbb1-4476-9336-302ca764c41b", + "name": "Spawner (2229)", + "location": [1658, 236, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "51190b84-1742-4bea-ab6e-9387c4ff0efa", + "name": "Spawner (2229)", + "location": [4426, 1028, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f547881-9057-4530-bacd-f2d8b8d3918a", + "name": "Spawner (2229)", + "location": [2522, 84, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3bf2b6e-3947-4a69-9600-04527ef88ff1", + "name": "Spawner (2229)", + "location": [3130, 116, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "821fbdbf-ae39-4073-adb0-17e2e9ee87aa", + "name": "Spawner (2229)", + "location": [2082, 60, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39aee7f8-373f-4191-b349-f5c9bb367abd", + "name": "Spawner (2229)", + "location": [2930, 3476, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c012739f-6a97-413c-b2bd-249fd2b7e51f", + "name": "Spawner (2229)", + "location": [1410, 3796, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4394742a-5799-4d8c-9d29-11753415b5b5", + "name": "Spawner (2229)", + "location": [1122, 3524, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c69fc76-ecf7-4282-84c1-9fa5cecda213", + "name": "Spawner (2229)", + "location": [2474, 3956, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cdd876a8-1378-4ccd-8f12-9cdac9c3487a", + "name": "Spawner (2229)", + "location": [4570, 3340, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f1da2ac-4a31-415d-99c6-76921de812b0", + "name": "Spawner (2229)", + "location": [4282, 3700, 0], + "map": "Felucca", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Sanctuary.json b/Distribution/Data/Spawns/uoml/felucca/Sanctuary.json new file mode 100644 index 000000000..ca5f46631 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Sanctuary.json @@ -0,0 +1,222 @@ +[ + { + "type": "Spawner", + "guid": "1511b1c9-952f-4b34-a527-40c8458fd839", + "name": "Spawner (2217)", + "location": [6263, 118, -10], + "map": "Felucca", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Chicken", "maxCount": 6, "probability": 100 }, + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 3, "probability": 100 }, + { "name": "Cat", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 6, "probability": 100 }, + { "name": "Bull", "maxCount": 6, "probability": 100 }, + { "name": "Pig", "maxCount": 3, "probability": 100 }, + { "name": "Boar", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "072beafe-a629-4c48-b5ff-b984254a5d46", + "name": "Spawner (2217)", + "location": [6349, 89, -20], + "map": "Felucca", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Ratman", "maxCount": 18, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 18, "probability": 100 }, + { "name": "Chiikkaha", "maxCount": 18, "probability": 100 }, + { "name": "Titan", "maxCount": 18, "probability": 100 }, + { "name": "Cyclops", "maxCount": 18, "probability": 100 }, + { "name": "Rat", "maxCount": 18, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 4, "probability": 100 }, + { "name": "Chiikkaha", "maxCount": 2, "probability": 100 }, + { "name": "Titan", "maxCount": 2, "probability": 100 }, + { "name": "Cyclops", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f96a5381-773a-4b5a-af6a-4cc8d2d08811", + "name": "Spawner (2217)", + "location": [6168, 117, 0], + "map": "Felucca", + "count": 34, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ratman", "maxCount": 11, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 11, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 11, "probability": 100 }, + { "name": "Changeling", "maxCount": 11, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 11, "probability": 100 }, + { "name": "Dog", "maxCount": 9, "probability": 100 }, + { "name": "Chicken", "maxCount": 9, "probability": 100 }, + { "name": "Cat", "maxCount": 9, "probability": 100 }, + { "name": "Boar", "maxCount": 9, "probability": 100 }, + { "name": "Bird", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Titan", "maxCount": 3, "probability": 100 }, + { "name": "Cyclops", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 5, "probability": 100 }, + { "name": "orcscout", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a03d9ae7-cc7d-41c7-83ad-fd30bdb1b5be", + "name": "Spawner (2217)", + "location": [6189, 71, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "OgreLord", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cow", "maxCount": 4, "probability": 100 }, + { "name": "Bull", "maxCount": 4, "probability": 100 }, + { "name": "Pig", "maxCount": 4, "probability": 100 }, + { "name": "Chicken", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "584c49ef-3604-4c2a-a385-7fca422c604e", + "name": "Spawner (2217)", + "location": [6187, 101, 0], + "map": "Felucca", + "count": 34, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Titan", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 10, "probability": 100 }, + { "name": "MougGuur", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Chicken", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 }, + { "name": "Cow", "maxCount": 10, "probability": 100 }, + { "name": "Bull", "maxCount": 10, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 7, "probability": 100 }, + { "name": "OrcBrute", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36e62443-3909-4f76-b267-0ffe2138c841", + "name": "Spawner (2217)", + "location": [6184, 29, 0], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Chicken", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1be8d7aa-7b8d-48b2-a99e-7e8aadda47ae", + "name": "Spawner (2217)", + "location": [6265, 48, -10], + "map": "Felucca", + "count": 53, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Szavetra", "maxCount": 22, "probability": 100 }, + { "name": "Imp", "maxCount": 22, "probability": 100 }, + { "name": "Titan", "maxCount": 22, "probability": 100 }, + { "name": "Snake", "maxCount": 22, "probability": 100 }, + { "name": "Ratman", "maxCount": 22, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 22, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 22, "probability": 100 }, + { "name": "Cow", "maxCount": 18, "probability": 100 }, + { "name": "Bull", "maxCount": 18, "probability": 100 }, + { "name": "Chicken", "maxCount": 18, "probability": 100 }, + { "name": "Bird", "maxCount": 18, "probability": 100 }, + { "name": "Pig", "maxCount": 18, "probability": 100 }, + { "name": "Dog", "maxCount": 18, "probability": 100 }, + { "name": "Goat", "maxCount": 18, "probability": 100 }, + { "name": "Cat", "maxCount": 18, "probability": 100 }, + { "name": "Rat", "maxCount": 18, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 9, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "OgreLord", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a976bf7f-b394-4429-92e9-b3dd47eae839", + "name": "Spawner (2217)", + "location": [6191, 155, 0], + "map": "Felucca", + "count": 42, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Changeling", "maxCount": 18, "probability": 100 }, + { "name": "Ratman", "maxCount": 18, "probability": 100 }, + { "name": "Ettin", "maxCount": 18, "probability": 100 }, + { "name": "Titan", "maxCount": 18, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 18, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 18, "probability": 100 }, + { "name": "Goat", "maxCount": 12, "probability": 100 }, + { "name": "Pig", "maxCount": 12, "probability": 100 }, + { "name": "Cow", "maxCount": 12, "probability": 100 }, + { "name": "Bull", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "GargoyleEnforcer", "maxCount": 9, "probability": 100 }, + { "name": "GargoyleDestroyer", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/SeaLife.json b/Distribution/Data/Spawns/uoml/felucca/SeaLife.json new file mode 100644 index 000000000..91ca2ca2c --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/SeaLife.json @@ -0,0 +1,2417 @@ +[ + { + "type": "Spawner", + "guid": "2d7cf932-ce8c-4a83-bd73-6371f3448072", + "name": "Spawner (2218)", + "location": [1395, 3421, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dcee48d-ec06-4315-9abc-ef03d4c03e4a", + "name": "Spawner (2218)", + "location": [4888, 375, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "670934bb-5897-4cdd-bbd3-1c90df939a21", + "name": "Spawner (2218)", + "location": [4352, 215, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e602a618-789c-4b14-84d6-fc4eba7fff99", + "name": "Spawner (2218)", + "location": [3688, 439, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6115838-1fe9-4d50-845e-758c36abdccb", + "name": "Spawner (2218)", + "location": [4512, 583, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fac96160-fbae-4833-8946-e315521ffb8e", + "name": "Spawner (2218)", + "location": [4904, 759, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0627e5fe-58d2-432f-9864-9d4bc05e187d", + "name": "Spawner (2218)", + "location": [4904, 1127, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f070052-489a-4be0-90b1-a4ac42a0c367", + "name": "Spawner (2218)", + "location": [3576, 823, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "696ab0e9-99f4-49ca-b79c-4d87f37fa250", + "name": "Spawner (2218)", + "location": [4016, 1031, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54fc0bab-2105-444b-8155-804624544ffd", + "name": "Spawner (2218)", + "location": [3992, 1447, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8fe15958-527f-4480-98d6-6d8c9e2653cd", + "name": "Spawner (2218)", + "location": [4232, 1655, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b90733e7-882e-42a3-ab1c-852611eec585", + "name": "Spawner (2218)", + "location": [4600, 1751, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01985344-d2d2-4fa8-ab4c-c1e3bd4e1426", + "name": "Spawner (2218)", + "location": [4896, 1519, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9703d8ff-da28-4ea8-9071-d9f9a4c6d836", + "name": "Spawner (2218)", + "location": [3616, 1807, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2abdef39-47e2-4145-9dd5-9af7e078d53c", + "name": "Spawner (2218)", + "location": [4032, 2039, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79aefaf1-568d-41d2-bed6-bd670ecce526", + "name": "Spawner (2218)", + "location": [4624, 2199, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd863ac3-ff02-4719-a235-9f8403f1f6b9", + "name": "Spawner (2218)", + "location": [4400, 2527, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b0dcb48-2f80-4503-8f25-ddbc29221473", + "name": "Spawner (2218)", + "location": [3984, 2423, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "89971651-6235-411e-b939-9d42fa08539a", + "name": "Spawner (2218)", + "location": [4896, 2119, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a3c1a4b2-df5b-4492-a0c5-0e9e5a0c399f", + "name": "Spawner (2218)", + "location": [4912, 2471, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e3b9e43-1b6c-407b-9c3f-325e87738c26", + "name": "Spawner (2218)", + "location": [4864, 2831, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe2bf6f9-ab40-4e8d-b7e9-ef01688462cb", + "name": "Spawner (2218)", + "location": [4536, 2847, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0014d520-3474-4486-86ec-00a967fb2a5e", + "name": "Spawner (2218)", + "location": [4896, 3215, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2910bc91-c850-493b-b157-d37b8a4dfde8", + "name": "Spawner (2218)", + "location": [4192, 2975, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20fdaa4f-d544-4a36-925b-cc0dfb565abc", + "name": "Spawner (2218)", + "location": [4008, 2767, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa54f319-d255-4aea-b782-d505cce89754", + "name": "Spawner (2218)", + "location": [3328, 2063, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b096bc10-2a6c-42a6-bd3c-1835e6ba57e3", + "name": "Spawner (2218)", + "location": [3176, 2383, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85204ae4-6b37-4f1e-9490-c081e806e26f", + "name": "Spawner (2218)", + "location": [3088, 2759, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d98fd38f-c4f3-4933-9ea3-53a81c886e8f", + "name": "Spawner (2218)", + "location": [3888, 3167, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c6c585b-1213-4135-bab3-6f15861f7aa0", + "name": "Spawner (2218)", + "location": [4152, 3415, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "201a3c66-f161-43f2-a6d0-0ca3597a6678", + "name": "Spawner (2218)", + "location": [3968, 3895, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb98801b-da33-4aab-b21a-8f4f0df5ee8c", + "name": "Spawner (2218)", + "location": [3240, 3151, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47efe73c-82a4-4117-b43b-e5d15136c149", + "name": "Spawner (2218)", + "location": [3776, 3519, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07342ec8-7e6f-4d0c-877f-743f3143d265", + "name": "Spawner (2218)", + "location": [3344, 3479, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6f4428d-db11-4d83-837f-4b92a3bee9e8", + "name": "Spawner (2218)", + "location": [2792, 2951, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f2b2fca-8ffe-4bb5-95d3-94a5ee0b72a8", + "name": "Spawner (2218)", + "location": [2608, 3295, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9be3514d-643b-4008-b316-aa0f47902fe8", + "name": "Spawner (2218)", + "location": [2744, 2567, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01b29f02-2bec-4d96-8d9a-ccb1957acff3", + "name": "Spawner (2218)", + "location": [2368, 2799, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10d5ae06-e475-4d16-93ca-a1deee44e78e", + "name": "Spawner (2218)", + "location": [3560, 3871, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5fe352af-61a0-464a-8ded-bd840ddc69c8", + "name": "Spawner (2218)", + "location": [3168, 3871, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8eb15e7-f507-4850-a1fd-9bc9f1ce1e07", + "name": "Spawner (2218)", + "location": [2768, 3847, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e4ba525-1583-4aed-a7b7-783f37239a5a", + "name": "Spawner (2218)", + "location": [1720, 3879, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bea968d2-ac27-494b-b59f-a3ed10e48402", + "name": "Spawner (2218)", + "location": [200, 3879, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a9ddbf8-0673-47fa-b250-08395ca453af", + "name": "Spawner (2218)", + "location": [544, 3719, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "395aeb43-6d36-4856-b6f5-19790d555189", + "name": "Spawner (2218)", + "location": [896, 3695, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "956a87d3-17ff-4f04-9165-ac12e0954594", + "name": "Spawner (2218)", + "location": [824, 3287, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "44c78479-90c2-44fe-92a8-23cb6de892fa", + "name": "Spawner (2218)", + "location": [176, 3551, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c993910-33aa-4227-b37d-e3cdec1ed456", + "name": "Spawner (2218)", + "location": [480, 3351, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e5967d0-64f1-46b3-a8c9-c85e60e2498f", + "name": "Spawner (2218)", + "location": [512, 2919, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c86a16f5-01d1-4a6b-8967-6e35ba6d4f30", + "name": "Spawner (2218)", + "location": [192, 2895, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23ad7990-6434-4baa-8ced-32317059b195", + "name": "Spawner (2218)", + "location": [208, 2503, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d557f82-a5f6-46f1-b459-fb3096e5852c", + "name": "Spawner (2218)", + "location": [840, 2863, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3938e64-993f-45c6-886e-dcdde4e43ee9", + "name": "Spawner (2218)", + "location": [600, 2511, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d01dce21-9d3a-4b51-85ce-e4589e03776f", + "name": "Spawner (2218)", + "location": [200, 2103, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef767a86-1b77-4ddb-a52b-d4c6599b42c0", + "name": "Spawner (2218)", + "location": [168, 1743, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e19fdd70-0246-43dd-bde0-a65e5324f684", + "name": "Spawner (2218)", + "location": [200, 559, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5bd5536-fac7-43f2-a074-01d548139c56", + "name": "Spawner (2218)", + "location": [184, 191, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "176e52b8-2ac0-496f-9647-fc42ea72ee8a", + "name": "Spawner (2218)", + "location": [592, 223, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "17574264-babd-4f28-92ed-1fcb3f07ee28", + "name": "Spawner (2218)", + "location": [496, 527, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "319bf1d1-606a-4b25-be88-11da80afe4fd", + "name": "Spawner (2218)", + "location": [2400, 2399, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8fb38b44-6776-4902-a7ec-ad918c7d58cc", + "name": "Spawner (2218)", + "location": [3008, 1895, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8faaf35e-e1d9-4bf5-8ddb-403d6628dc91", + "name": "Spawner (2218)", + "location": [2392, 2031, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec9b98b4-d409-4aaf-823c-a5cf9503f461", + "name": "Spawner (2218)", + "location": [2624, 1775, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "760de2a1-81b0-44b0-a936-749b378086cf", + "name": "Spawner (2218)", + "location": [3408, 1407, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e50039d-cd33-4802-bc94-d472eb3fc521", + "name": "Spawner (2218)", + "location": [3256, 1031, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4de0d475-b6de-4676-ae8c-715b17e0f44a", + "name": "Spawner (2218)", + "location": [2952, 1239, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df48afce-46f8-4d69-bae7-b843e1185554", + "name": "Spawner (2218)", + "location": [2752, 1455, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a798b4a7-f027-4e9f-86d0-a93f721ee033", + "name": "Spawner (2218)", + "location": [2208, 1703, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61eb0c80-5734-4aaa-8639-26c61907aa70", + "name": "Spawner (2218)", + "location": [1866, 1798, -5], + "map": "Felucca", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5a6b13c-50d2-4aa4-ba70-9b2abb9841b6", + "name": "Spawner (2218)", + "location": [931, 141, -5], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c02a7e0f-a448-45c3-b8e8-532f0441089d", + "name": "Spawner (2218)", + "location": [3267, 1717, -5], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2bdb1a3-d96b-4a44-95e7-099f39ff38cc", + "name": "Spawner (2218)", + "location": [2395, 1405, -5], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3867ead7-d062-44a3-a284-ba67c14b2b3b", + "name": "Spawner (2218)", + "location": [139, 1173, -5], + "map": "Felucca", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e131f5dc-1b9d-480e-b759-85af56822993", + "name": "Spawner (2218)", + "location": [3123, 1485, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df7a4d12-8fbd-4547-8f92-bb4dfc720ac2", + "name": "Spawner (2218)", + "location": [3811, 109, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da152e88-97ad-4c9e-b27d-c4d4385e8455", + "name": "Spawner (2218)", + "location": [3931, 1757, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "372bca4b-5296-48b9-9207-7782d129aa21", + "name": "Spawner (2218)", + "location": [2931, 3245, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a301447-cfe7-45e3-b30a-b9573379b108", + "name": "Spawner (2218)", + "location": [2291, 3181, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a7be8f4-75a8-495b-9ec7-18ca5a146566", + "name": "Spawner (2218)", + "location": [979, 3997, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07ce9fc5-0714-4583-bd08-329c1997ed4d", + "name": "Spawner (2218)", + "location": [147, 3213, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "667c9bca-f47b-4a12-b00e-55db4657cb46", + "name": "Spawner (2218)", + "location": [1891, 3597, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "296f03e2-d85d-4743-8fd2-0428b9390c89", + "name": "Spawner (2218)", + "location": [2971, 1605, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fc7ef1ce-76f7-4239-b044-7f30a4c32a42", + "name": "Spawner (2218)", + "location": [1580, 75, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60abd3c2-eba6-4e07-aae5-1d988ade2d99", + "name": "Spawner (2218)", + "location": [4275, 1309, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f61ab42b-c23e-462f-980d-ea4d236f4796", + "name": "Spawner (2218)", + "location": [4667, 109, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d45f2c8d-d661-4a91-bc0a-b04f901b64b2", + "name": "Spawner (2218)", + "location": [5003, 101, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b9bde41-61e8-486c-8893-f5de1e36bdf2", + "name": "Spawner (2218)", + "location": [3603, 125, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08ccd285-5416-4099-8edf-371c61bccbd2", + "name": "Spawner (2218)", + "location": [4112, 791, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "445d35c5-b9a8-4269-ab27-c8b294156811", + "name": "Spawner (2218)", + "location": [3891, 733, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e35061d7-cfbd-4383-b75b-065c37982be4", + "name": "Spawner (2218)", + "location": [3672, 1535, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a88ec077-c435-47e0-92b3-548e27f2e603", + "name": "Spawner (2218)", + "location": [4363, 1981, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a408764b-0975-45e6-87dc-031e05747605", + "name": "Spawner (2218)", + "location": [4995, 1829, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3e6d001-7ec1-48c0-a243-df5a177d56a7", + "name": "Spawner (2218)", + "location": [4283, 2221, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdd432ee-e61f-4df2-ab8b-d1296838dd90", + "name": "Spawner (2218)", + "location": [2008, 4007, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "817895c6-8683-41e6-b08d-01a5ff038f94", + "name": "Spawner (2218)", + "location": [2320, 4007, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec7aedaf-b518-4f7e-9d05-b864db54eac2", + "name": "Spawner (2218)", + "location": [4955, 3997, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f18cdfa-c425-4525-9056-54476d4bc98d", + "name": "Spawner (2218)", + "location": [4731, 3997, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05f1490c-6e6c-4905-8d78-01f2367c5f58", + "name": "Spawner (2218)", + "location": [4307, 3965, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96ea0189-bdc2-40de-8660-f07b8f3c6d51", + "name": "Spawner (2218)", + "location": [2227, 3533, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5114e22e-6538-4d21-b276-aa9a00a88a54", + "name": "Spawner (2218)", + "location": [1187, 3885, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8e4212a-a3d5-4e62-937a-3118350ea07d", + "name": "Spawner (2218)", + "location": [731, 3989, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5491c069-ef4f-4b5c-bc5b-5913c4339f3f", + "name": "Spawner (2218)", + "location": [491, 4013, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0738ba5-30cc-4819-9f84-3df391ddb250", + "name": "Spawner (2218)", + "location": [107, 909, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0071f2f5-305e-4819-b388-4ffbf15f7fb1", + "name": "Spawner (2218)", + "location": [1792, 79, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "52a8d52f-aec5-4ea8-8964-3a769c403cc2", + "name": "Spawner (2218)", + "location": [1336, 79, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1bc905f9-1691-4a2b-afca-738e6b22fc47", + "name": "Spawner (2218)", + "location": [4048, 63, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33e0f8af-c16e-4dc4-a240-5c0533ff3375", + "name": "Spawner (2218)", + "location": [4392, 823, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a4a476f6-ad25-4619-a34e-285bffea1f55", + "name": "Spawner (2218)", + "location": [3520, 2263, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "058ccec6-cf0a-4246-97d1-5397c1557502", + "name": "Spawner (2218)", + "location": [3568, 3215, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fec6b06d-982d-45b5-aa46-a6334f5ece5e", + "name": "Spawner (2218)", + "location": [4992, 3791, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8964dcc7-930d-44fb-85a8-f2f85e4fc458", + "name": "Spawner (2218)", + "location": [4984, 3567, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "add4463a-f94f-45b8-8504-98007a319caf", + "name": "Spawner (2218)", + "location": [2072, 2527, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54d209e5-3a8c-476b-bc51-e3ebb3449c6c", + "name": "Spawner (2218)", + "location": [2040, 3751, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd9175ae-f576-4ffd-bae8-2710ae3e07e1", + "name": "Spawner (2218)", + "location": [2288, 3767, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1e652609-1c8b-4f88-bd10-de2445856391", + "name": "Spawner (2218)", + "location": [496, 1879, -5], + "map": "Felucca", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/Shame.json b/Distribution/Data/Spawns/uoml/felucca/Shame.json new file mode 100644 index 000000000..2747dbca6 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Shame.json @@ -0,0 +1,1690 @@ +[ + { + "type": "Spawner", + "guid": "8d894292-aaba-4fd7-834a-68aea0d50e0a", + "name": "Spawner (2219)", + "location": [5404, 93, 10], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54cfd78d-c062-45de-9691-fc08b8225353", + "name": "Spawner (2219)", + "location": [5425, 58, 10], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 7, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6921911-2c96-4f33-96e9-d40efa57561c", + "name": "Spawner (2219)", + "location": [5387, 41, 20], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2a61736-5a76-4421-8178-deea7aabc615", + "name": "Spawner (2219)", + "location": [5394, 11, 30], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7f5f240f-d211-412b-b76d-eec1723c79fd", + "name": "Spawner (2219)", + "location": [5474, 20, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b907c1ba-e6aa-4ec8-81a2-8ebc40131a7a", + "name": "Spawner (2219)", + "location": [5477, 67, 20], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3f6e1f05-7dcb-4952-8342-2c6386f057e5", + "name": "Spawner (2219)", + "location": [5468, 109, 35], + "map": "Felucca", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, + { "name": "Scorpion", "maxCount": 20, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f21c56d6-5653-4bec-95de-58b70a981ff0", + "name": "Spawner (2219)", + "location": [5423, 112, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "61800760-7e33-48a1-970a-5c1c973e7398", + "name": "Spawner (2219)", + "location": [5468, 106, 35], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b283b315-485a-422a-a822-446e200a0080", + "name": "Spawner (2219)", + "location": [5434, 100, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "663e5aa7-2204-42a6-8b47-b4a5cdd8382e", + "name": "Spawner (2219)", + "location": [5492, 56, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b27e56f7-9157-4b01-8ddd-0aec26e185df", + "name": "Spawner (2219)", + "location": [5416, 59, -15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2eb38b8-d316-4f83-95f5-4c5dc6b43113", + "name": "Spawner (2219)", + "location": [5400, 45, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49a30e76-7efb-42df-a85b-f40fd1546cee", + "name": "Spawner (2219)", + "location": [5448, 33, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3498612c-357d-42f3-a1fc-89ee565569d2", + "name": "Spawner (2219)", + "location": [5437, 11, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9a246364-18c8-46c3-9fac-17025f9ab620", + "name": "Spawner (2219)", + "location": [5399, 11, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72d5ae48-41aa-478e-b063-23a3840b3255", + "name": "Spawner (2219)", + "location": [5421, 117, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ba178ecf-0529-4540-9edd-710d6deb748b", + "name": "Spawner (2219)", + "location": [5468, 100, 35], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72cfdb5a-89de-4277-9de9-66e2c36e48fc", + "name": "Spawner (2219)", + "location": [5392, 10, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "22b4ddba-b4dc-46ba-b8ca-ee5c6ab5538b", + "name": "Spawner (2219)", + "location": [5445, 12, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cad7fb39-f2aa-4f55-9899-64967860d07e", + "name": "Spawner (2219)", + "location": [5404, 43, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9303d7b0-a873-47f4-b4e3-1d70d547e4b1", + "name": "Spawner (2219)", + "location": [5443, 36, -10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ddce5aba-7b2b-4d1f-b055-d242db0509c1", + "name": "Spawner (2219)", + "location": [5402, 58, -20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "811e333f-7aa9-481f-9edf-8c61e6f984a5", + "name": "Spawner (2219)", + "location": [5493, 51, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b9c97892-db29-41e5-9478-0f6d9e28e8f7", + "name": "Spawner (2219)", + "location": [5434, 89, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7aaac8e3-5868-41ec-b4ae-fea008e39ba5", + "name": "Spawner (2219)", + "location": [5575, 16, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "099f8534-bad4-4baa-a701-6f52e856b767", + "name": "Spawner (2219)", + "location": [5615, 18, 10], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "DullCopperElemental", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5073e41f-2bd8-4e2b-a73a-e7ecf76a92c5", + "name": "Spawner (2219)", + "location": [5568, 36, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "37cbc800-73b6-4348-a6ed-f19dea8ff7f7", + "name": "Spawner (2219)", + "location": [5549, 60, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d1eed89e-ef04-4d60-812d-fea412921d76", + "name": "Spawner (2219)", + "location": [5549, 79, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5036ca70-e3f4-4e5e-989e-cddf68363ace", + "name": "Spawner (2219)", + "location": [5595, 82, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d1898122-15d6-4523-a6e6-84ae0cda8c6a", + "name": "Spawner (2219)", + "location": [5603, 93, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "DullCopperElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "746760c5-2c14-4d2c-80fe-9c1afe9a2692", + "name": "Spawner (2219)", + "location": [5603, 116, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "DullCopperElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1c97ea8d-413a-4925-8507-6c004a16ccd6", + "name": "Spawner (2219)", + "location": [5536, 119, -5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5cf37618-77d8-450d-b847-3ee1f14efcc1", + "name": "Spawner (2219)", + "location": [5546, 118, -5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a3d232dd-36fe-40f3-90b3-8e40d23c75e5", + "name": "Spawner (2219)", + "location": [5606, 25, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9b58940a-490b-4c30-8b87-69ff988cacd6", + "name": "Spawner (2219)", + "location": [5570, 118, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a7f36503-7139-4ef4-adef-0cc2889d0e90", + "name": "Spawner (2219)", + "location": [5620, 21, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "36e8b3e2-c177-4b5f-a9b8-e21ac99d93e5", + "name": "Spawner (2219)", + "location": [5572, 113, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c327247c-da8a-412c-9e92-d960c1b91ec2", + "name": "Spawner (2219)", + "location": [5546, 113, -5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4a851206-2957-4090-931d-54ed78cea448", + "name": "Spawner (2219)", + "location": [5474, 139, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b10ba4a-f6a2-4164-827a-c7f98da29341", + "name": "Spawner (2219)", + "location": [5477, 183, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a0c7b16e-fe26-4e43-ab3a-789041878480", + "name": "Spawner (2219)", + "location": [5469, 211, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a80cf5e-c29f-46d5-83e4-c622fbeba8ad", + "name": "Spawner (2219)", + "location": [5441, 187, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EvilMage", "maxCount": 5, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28a61b6b-d21f-4610-aaf2-98f0714ee469", + "name": "Spawner (2219)", + "location": [5412, 164, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1a6bec6-dc10-4658-bceb-bd9ecf6970cf", + "name": "Spawner (2219)", + "location": [5411, 163, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "29921605-0cc3-4a0f-8fcb-d5088c40eb8b", + "name": "Spawner (2219)", + "location": [5412, 201, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "25a4f4e6-8d5b-4e50-87c5-65961ef02649", + "name": "Spawner (2219)", + "location": [5412, 200, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b0f079f-a068-4b35-9c6a-13a2a0eefa41", + "name": "Spawner (2219)", + "location": [5394, 230, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7540f782-bcf5-4a5f-b2e6-ba385859a340", + "name": "Spawner (2219)", + "location": [5505, 180, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "ElderGazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e980b6d-486f-45a7-ad54-477a816dda3a", + "name": "Spawner (2219)", + "location": [5527, 210, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "DullCopperElemental", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "27ef1488-f84c-4729-a3fc-6ad074a90495", + "name": "Spawner (2219)", + "location": [5557, 222, 0], + "map": "Felucca", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 2, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca52e3b4-b46b-49ec-96ca-132c5ff0b4d6", + "name": "Spawner (2219)", + "location": [5577, 194, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EvilMage", "maxCount": 5, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ceb6c395-5f88-41cd-8d10-8a6dd7683cce", + "name": "Spawner (2219)", + "location": [5594, 182, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3cf4908c-dbae-4cb8-b48f-fdf387ecc501", + "name": "Spawner (2219)", + "location": [5571, 158, -10], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "86dc2f0d-baf0-4acc-8b9f-62a1bbbcca31", + "name": "Spawner (2219)", + "location": [5593, 141, 10], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d5036ac-c3f2-48f0-8183-daca945d0523", + "name": "Spawner (2219)", + "location": [5398, 148, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4b71965f-2829-4d76-8b31-9c6d55aa7b11", + "name": "Spawner (2219)", + "location": [5545, 153, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "90a2b066-3a4d-4627-bcd8-649434051203", + "name": "Spawner (2219)", + "location": [5594, 139, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d92657cf-0eff-4aa3-8d6b-b11164fe85f2", + "name": "Spawner (2219)", + "location": [5604, 166, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49dbe727-8641-49ac-8372-a3f2f8fa5cf7", + "name": "Spawner (2219)", + "location": [5608, 190, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "340a9686-1ac8-47ea-9052-31167add0da8", + "name": "Spawner (2219)", + "location": [5605, 202, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4fe7e9e3-3d65-433f-9c20-584fa7647e12", + "name": "Spawner (2219)", + "location": [5439, 137, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11f83b98-6f66-43cd-bc2d-c8435a753629", + "name": "Spawner (2219)", + "location": [5390, 145, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d4a17766-a790-4a7b-a7d2-22d095feb615", + "name": "Spawner (2219)", + "location": [5456, 157, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3962bad0-4c7f-4fed-b4f2-e69020569bcc", + "name": "Spawner (2219)", + "location": [5410, 170, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a40b89dc-77a6-4b5a-9f53-c7c6b75fe44b", + "name": "Spawner (2219)", + "location": [5415, 189, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc63948f-de6d-43de-a205-6e11d44249f1", + "name": "Spawner (2219)", + "location": [5396, 226, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2284227d-bd9b-4b23-99c7-e96eaeacb970", + "name": "Spawner (2219)", + "location": [5553, 146, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "00b4ffd2-c3fe-42e0-b913-9dc7f244bad1", + "name": "Spawner (2219)", + "location": [5595, 147, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "24bb9e8a-3124-4984-89be-39ff6cdd3e34", + "name": "Spawner (2219)", + "location": [5615, 173, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6b367539-626c-44b0-a97e-8795ee947c6e", + "name": "Spawner (2219)", + "location": [5616, 184, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4bbcb759-e08d-45a9-b7c2-76855e283b0f", + "name": "Spawner (2219)", + "location": [5456, 143, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f11035e3-c112-444e-9227-79c2254265b0", + "name": "Spawner (2219)", + "location": [5432, 158, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb19bd5b-21cf-475e-bd85-4c990c2d6596", + "name": "Spawner (2219)", + "location": [5474, 176, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4be1fbed-a047-4092-9ed8-e5ee308123de", + "name": "Spawner (2219)", + "location": [5406, 177, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "62098bc9-596a-4f31-80a0-74190ea65a91", + "name": "Spawner (2219)", + "location": [5405, 185, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1333be8f-f542-4131-a842-f5419ea6690b", + "name": "Spawner (2219)", + "location": [5405, 200, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "06a27dea-d6cf-4668-aaf5-11b92f37dac2", + "name": "Spawner (2219)", + "location": [5403, 235, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9add65b7-3729-4ab6-be5f-53fc13b3fbcb", + "name": "Spawner (2219)", + "location": [5475, 190, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bdfae837-0641-4ad9-ac46-23113dbe07e0", + "name": "Spawner (2219)", + "location": [5875, 44, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cc00b741-ad6a-46f6-8398-62ad6e66700a", + "name": "Spawner (2219)", + "location": [5856, 107, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a59d8de3-e39e-4d3e-84dd-b76c900d4f1f", + "name": "Spawner (2219)", + "location": [5845, 57, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "694e60e0-1db8-457f-b48c-d11372471f5f", + "name": "Spawner (2219)", + "location": [5828, 42, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c164e1f3-3128-4087-a1f5-ffcc45e5e300", + "name": "Spawner (2219)", + "location": [5819, 50, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56bcaa4f-deef-4ca3-983e-461ab73c81ca", + "name": "Spawner (2219)", + "location": [5818, 80, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "ElderGazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a980490d-cb4e-43dc-ad4f-1cd8c1f8846a", + "name": "Spawner (2219)", + "location": [5804, 12, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c3b25137-7ec8-4274-8af1-7a30f4cfaef3", + "name": "Spawner (2219)", + "location": [5721, 12, 10], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "BloodElemental", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3bc0b686-d31e-4ed6-b1bc-6236301c0d04", + "name": "Spawner (2219)", + "location": [5661, 16, -10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0acf386b-aab2-42da-9852-bbab5e17235c", + "name": "Spawner (2219)", + "location": [5649, 101, 10], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "56eb182b-e842-46da-b44c-cee5290561ac", + "name": "Spawner (2219)", + "location": [5684, 117, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "BloodElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da26e6d2-e197-479f-a3a0-2f3cd436539c", + "name": "Spawner (2219)", + "location": [5724, 118, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c23758c7-2a5d-4607-b09e-de24fed379ee", + "name": "Spawner (2219)", + "location": [5760, 99, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33b7379d-d169-43b4-a75a-2c550fb45d2e", + "name": "Spawner (2219)", + "location": [5711, 97, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bffaf353-dbe8-44a7-9468-acd13b4430b7", + "name": "Spawner (2219)", + "location": [5752, 44, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13410a04-955c-4e3b-b88a-c59e17ec343c", + "name": "Spawner (2219)", + "location": [5708, 43, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a545713-a7cb-4e80-b41f-9eccfb8d5f35", + "name": "Spawner (2219)", + "location": [5730, 90, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70127b13-674d-4def-86fe-bc5b90a6e88e", + "name": "Spawner (2219)", + "location": [5723, 73, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6786e744-3a8a-4d00-8bec-213325b489b6", + "name": "Spawner (2219)", + "location": [5715, 61, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "63e94588-f42b-42a6-a046-a9d4fe7a62b8", + "name": "Spawner (2219)", + "location": [5701, 60, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "17349861-3ab6-4103-bd27-07d40be823d0", + "name": "Spawner (2219)", + "location": [5817, 83, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "622a0111-2226-43de-bea6-a6e50b1a1a67", + "name": "Spawner (2219)", + "location": [5730, 93, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "584599e2-fdf8-423b-b7b7-9e6e2cdcf45b", + "name": "Spawner (2219)", + "location": [5723, 77, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "16036a13-721a-447b-ae6b-a5df60d9c9ad", + "name": "Spawner (2219)", + "location": [5717, 60, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e468b283-ac85-4c27-a6e8-462a971f007c", + "name": "Spawner (2219)", + "location": [5699, 61, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1bb3ab96-96dd-47df-8e4b-c788960b039f", + "name": "Spawner (2219)", + "location": [5817, 78, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e200a23-7f5e-4b50-b15b-72543331cbdd", + "name": "Spawner (2219)", + "location": [5733, 91, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b0e7336b-58cf-43d5-bb4c-d91d0d696b72", + "name": "Spawner (2219)", + "location": [5725, 78, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "91118d48-78d5-4fce-a97d-bd662a0bb903", + "name": "Spawner (2219)", + "location": [5715, 58, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3986c149-5d04-4234-9194-25d9ad0daeef", + "name": "Spawner (2219)", + "location": [5698, 57, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b1bc56ef-b214-4b53-b49b-fe725b9d4d9f", + "name": "Spawner (2219)", + "location": [5733, 93, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f14bf474-ef89-4f01-b3a7-0ec8aaf1f35a", + "name": "Spawner (2219)", + "location": [5727, 72, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "38198440-cbb9-4ddb-8808-59977415fa28", + "name": "Spawner (2219)", + "location": [5717, 58, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a01e8912-3b82-4214-a8b7-13c598cb23e0", + "name": "Spawner (2219)", + "location": [5701, 58, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/SolenHive.json b/Distribution/Data/Spawns/uoml/felucca/SolenHive.json new file mode 100644 index 000000000..73453e82c --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/SolenHive.json @@ -0,0 +1,605 @@ +[ + { + "type": "Spawner", + "guid": "c6783361-a68d-4811-a162-86af961cf503", + "name": "Spawner (2220)", + "location": [5670, 1807, 4], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "292f941a-a9c4-4ec2-8957-0252fe7be988", + "name": "Spawner (2220)", + "location": [5673, 1846, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4e878d51-e601-4e8c-b034-fcc572f4a840", + "name": "Spawner (2220)", + "location": [5693, 1834, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7841012e-e50b-44c7-80e5-9f0901304abd", + "name": "Spawner (2220)", + "location": [5704, 1807, 4], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "616e54a8-e1fd-4877-8f77-92f3361b266e", + "name": "Spawner (2220)", + "location": [5722, 1820, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81d29a4d-f337-46b8-971f-402bce72c1a3", + "name": "Spawner (2220)", + "location": [5709, 1874, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 50, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0b57c719-bf1c-4cbe-ac70-5aaabda64779", + "name": "Spawner (2220)", + "location": [5681, 1886, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 50, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fe17cd01-9207-4132-b25a-b23b6d0a21cd", + "name": "Spawner (2220)", + "location": [5756, 1857, 4], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 50, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "451c7289-0639-4054-879d-fd66e27e866c", + "name": "Spawner (2220)", + "location": [5912, 1800, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "00f9e4a9-d9a7-4d44-aecc-c2bb20a79c9c", + "name": "Spawner (2220)", + "location": [5920, 1842, 1], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a130822d-7738-4410-a154-7860ca71cba2", + "name": "Spawner (2220)", + "location": [5887, 1840, -18], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "315f85b8-ba97-4ca0-b2c4-92b7cd539c5b", + "name": "Spawner (2220)", + "location": [5847, 1819, 1], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "39cbbb5e-744c-4179-8646-fb1290698ad1", + "name": "Spawner (2220)", + "location": [5829, 1798, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c02ed532-4fdd-4e4a-83c4-f2f8a0e025e1", + "name": "Spawner (2220)", + "location": [5873, 1798, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }, + { "name": "BlackSolenQueen", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abaf512d-5839-439d-964e-888303a40d48", + "name": "Spawner (2220)", + "location": [5782, 1793, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "818ebcff-8a3d-4e6d-a8ac-fbd782899100", + "name": "Spawner (2220)", + "location": [5799, 1857, 5], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "308e3de7-9304-466e-9cc0-e71367242d75", + "name": "Spawner (2220)", + "location": [5815, 1881, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9c112ab3-aaed-483c-a895-12d56c3da76c", + "name": "Spawner (2220)", + "location": [5831, 1910, 3], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Beetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f7c24d72-6e8d-4e78-9cd2-fd6bf7e427e3", + "name": "Spawner (2220)", + "location": [5718, 2024, 4], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92c760f1-55bf-44cd-b51f-b3425595c3f4", + "name": "Spawner (2220)", + "location": [5776, 2019, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8bfb6db7-bd95-4b69-9274-330a3806885b", + "name": "Spawner (2220)", + "location": [5782, 2021, -11], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3bdddcd1-a470-49d7-bc08-41a4e9049e3d", + "name": "Spawner (2220)", + "location": [5767, 1962, 2], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 50, + "entries": [{ "name": "DreadSpider", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04a12d97-707d-4bc9-ad99-b32f0e88e158", + "name": "Spawner (2220)", + "location": [5671, 1979, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40a36fd0-161f-42c1-9cca-9db682d9c50e", + "name": "Spawner (2220)", + "location": [5703, 1991, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0cda81be-bbe3-4312-af53-dcf5b33a1be8", + "name": "Spawner (2220)", + "location": [5699, 1921, 2], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f9cdf3b9-37a7-456b-a24a-a9fc19153cda", + "name": "Spawner (2220)", + "location": [5748, 1923, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6f5ddf7f-8608-44e9-841d-b1b6248e54bf", + "name": "Spawner (2220)", + "location": [5736, 1939, 7], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "BlackSolenQueen", "maxCount": 1, "probability": 100 }, + { "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d94b6b1f-efd6-427f-aa69-88b326e256fd", + "name": "Spawner (2220)", + "location": [5876, 2018, 3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c1cfb2f5-31c9-4686-8478-7e23db2e9ab0", + "name": "Spawner (2220)", + "location": [5856, 2018, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4adcd151-0741-4ae0-88ef-f4b01523826c", + "name": "Spawner (2220)", + "location": [5838, 2019, 3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4fc74f26-5c3b-4122-8a88-9581e5d02e5a", + "name": "Spawner (2220)", + "location": [5910, 1991, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9fccb18d-7d55-428d-9a0e-6975b0f2f213", + "name": "Spawner (2220)", + "location": [5837, 1992, -2], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Beetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e5998826-a85a-4fff-81d0-382fa35bf4ae", + "name": "Spawner (2220)", + "location": [5863, 1958, 2], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "022918a5-aa50-4cf1-b286-bae8de9c9537", + "name": "Spawner (2220)", + "location": [5884, 1920, -15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "487e7cad-478d-4ffb-b109-fe2f398f2980", + "name": "Spawner (2220)", + "location": [5825, 1968, 4], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e82f829e-9f6f-494c-afe4-98c2cb4f1e33", + "name": "Spawner (2220)", + "location": [5855, 1932, 3], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BlackSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0876305c-d6ce-46ff-bb97-e34d5fb772a9", + "name": "Spawner (2220)", + "location": [5798, 1963, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 35, + "entries": [{ "name": "BlackSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b280605e-0ebe-492d-a0c0-f57d1e05e5a4", + "name": "Spawner (2220)", + "location": [5665, 1868, 11], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "AntLion", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a2fd7f95-2496-4594-9ed9-ab55fa31d08f", + "name": "Spawner (2220)", + "location": [5707, 1843, -15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "164e28e4-c77f-450f-916a-493904e79d9b", + "name": "Spawner (2220)", + "location": [3218, 502, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "RedSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "RedSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76303de9-d601-4f18-b341-ed8741315132", + "name": "Spawner (2220)", + "location": [1940, 3261, 1], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "RedSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "RedSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d1b08137-55ba-4356-8927-30febaac045b", + "name": "Spawner (2220)", + "location": [1361, 894, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 8, + "entries": [ + { "name": "RedSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "RedSolenInfiltratorWarrior", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/TerathanKeep.json b/Distribution/Data/Spawns/uoml/felucca/TerathanKeep.json new file mode 100644 index 000000000..4fbb0d235 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/TerathanKeep.json @@ -0,0 +1,638 @@ +[ + { + "type": "Spawner", + "guid": "ec99841b-c28c-4b7c-a4d3-15f2753a4184", + "name": "Spawner (2221)", + "location": [5296, 1561, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fee1141d-1c6e-479e-9cfc-d92fed242b0a", + "name": "Spawner (2221)", + "location": [5296, 1562, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3b5c6cf-915f-427b-97b1-89d49bdc440a", + "name": "Spawner (2221)", + "location": [5297, 1565, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6db88e49-590f-4fc9-9ff2-484c0caba22d", + "name": "Spawner (2221)", + "location": [5342, 1553, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "14d9a777-99d2-4e2a-92e4-98eb9ed2cc2a", + "name": "Spawner (2221)", + "location": [5342, 1552, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a472619-a1fd-43b6-8ed3-876689a8b7a4", + "name": "Spawner (2221)", + "location": [5343, 1551, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce1ce8c5-583f-4f67-84b6-126bc30487ff", + "name": "Spawner (2221)", + "location": [5334, 1613, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6b68ad4-a9c2-4982-9477-31a7cb202a69", + "name": "Spawner (2221)", + "location": [5334, 1614, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dfb3c171-67af-4dea-8b11-1d49762b2fb2", + "name": "Spawner (2221)", + "location": [5335, 1615, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78eb98fb-eb53-4956-919c-9b075b3a02ad", + "name": "Spawner (2221)", + "location": [5348, 1552, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "628166d6-c981-4d20-bee3-f9e68ca672e4", + "name": "Spawner (2221)", + "location": [5345, 1551, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da8f850f-a715-4ae6-9c10-47db89b3d732", + "name": "Spawner (2221)", + "location": [5344, 1757, -125], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3a73b6d-ffa1-4448-9308-6431b4f51d4b", + "name": "Spawner (2221)", + "location": [5345, 1757, -125], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ada2362f-c030-42f1-86ce-5d983ff64a38", + "name": "Spawner (2221)", + "location": [5346, 1756, -125], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9a936c9-3c10-4029-a46d-dbbdb5494b48", + "name": "Spawner (2221)", + "location": [5335, 1706, -70], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8cddc6ca-33de-411d-9d6d-03f61c427acc", + "name": "Spawner (2221)", + "location": [5335, 1707, -70], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56ff9b13-646b-4f60-be0b-b5aaa730d17d", + "name": "Spawner (2221)", + "location": [5336, 1708, -70], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "97e91add-b1d5-48e6-ae53-6dd25cb088bc", + "name": "Spawner (2221)", + "location": [5270, 1695, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fbc340d4-cf84-410e-9764-5987bdacc4fc", + "name": "Spawner (2221)", + "location": [5270, 1694, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "adcadb01-80d3-40a1-acf8-6a7493aeef84", + "name": "Spawner (2221)", + "location": [5271, 1693, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebca1c72-604f-4cba-b019-c5de4a5b55c8", + "name": "Spawner (2221)", + "location": [5365, 1707, -71], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "140da661-2c38-4502-a8d3-a093fb672c3e", + "name": "Spawner (2221)", + "location": [5363, 1709, -75], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7045923d-0e54-48e2-8c61-25f4e748bb62", + "name": "Spawner (2221)", + "location": [5176, 1702, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "877895b3-0048-42fa-943b-4246c6d0dbd1", + "name": "Spawner (2221)", + "location": [5176, 1701, 2], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b1a245ac-73e5-4f51-a9b0-72a7e0aeb999", + "name": "Spawner (2221)", + "location": [5177, 1700, 1], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c464287-4f2e-40c2-b6cd-7110d89097eb", + "name": "Spawner (2221)", + "location": [5132, 1624, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a720dbb7-0f4e-45c8-9bac-c9b76e028c02", + "name": "Spawner (2221)", + "location": [5132, 1623, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1613210d-f099-406c-8dad-b3e6c4978ea4", + "name": "Spawner (2221)", + "location": [5134, 1625, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "19a3a2ba-4466-440e-9eb8-69c30087faf0", + "name": "Spawner (2221)", + "location": [5160, 1586, -15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05f81bd1-8432-458d-973a-85449805e567", + "name": "Spawner (2221)", + "location": [5163, 1585, -15], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c8d7085-680b-49cf-a8c3-be20a79f94be", + "name": "Spawner (2221)", + "location": [5161, 1584, -15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b1ed67b-dcfd-41a6-9b27-9f815db3e48f", + "name": "Spawner (2221)", + "location": [5200, 1565, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e911183-4be3-4c7d-878b-a7b4d97d8f14", + "name": "Spawner (2221)", + "location": [5201, 1566, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "578848ef-81b8-4528-b3e5-7819296f1441", + "name": "Spawner (2221)", + "location": [5200, 1568, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/shared/trammel/TownsLife.json b/Distribution/Data/Spawns/uoml/felucca/TownsLife.json similarity index 61% rename from Distribution/Data/Spawns/shared/trammel/TownsLife.json rename to Distribution/Data/Spawns/uoml/felucca/TownsLife.json index a8b7d0cf0..4a36b81a2 100644 --- a/Distribution/Data/Spawns/shared/trammel/TownsLife.json +++ b/Distribution/Data/Spawns/uoml/felucca/TownsLife.json @@ -1,115 +1,27 @@ [ { - "$type": "Spawner", - "guid": "0596ff5d-53a1-4384-978a-11ec521c44ec", - "name": "Spawner (122)", - "location": [1400, 3775, 0], - "map": "Trammel", - "count": 40, + "type": "Spawner", + "guid": "97b2dcc3-dbbf-4bf5-89a2-47a112b9e4af", + "name": "Spawner (2222)", + "location": [5249, 228, 15], + "map": "Felucca", + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 100, - "walkingRange": 100, + "homeRange": 20, + "walkingRange": 20, "entries": [ - { "name": "Bird", "maxCount": 10, "probability": 100 }, - { "name": "Cat", "maxCount": 10, "probability": 100 }, - { "name": "Dog", "maxCount": 10, "probability": 100 }, - { "name": "Rat", "maxCount": 10, "probability": 100 } + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "0651efe3-7bee-409d-9934-7d686aabac52", - "name": "Spawner (122)", - "location": [3717, 2111, 20], - "map": "Trammel", - "count": 24, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 6, "probability": 100 }, - { "name": "Cat", "maxCount": 6, "probability": 100 }, - { "name": "Dog", "maxCount": 6, "probability": 100 }, - { "name": "Rat", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "089ed81a-4b3d-4a1a-aca7-10dd211255dc", - "name": "Spawner (122)", - "location": [7007, 382, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a4a9b61-4c34-441d-82b1-6e822fa8ceb4", - "name": "Spawner (122)", - "location": [7052, 386, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0dc3b704-8d6e-468e-ad7e-61b6097c289b", - "name": "Spawner (122)", - "location": [2972, 3436, 15], - "map": "Trammel", - "count": 48, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "Cat", "maxCount": 12, "probability": 100 }, - { "name": "Dog", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1208aae8-e36f-4170-97bf-76ebb6bebf09", - "name": "Spawner (122)", - "location": [4567, 1476, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 3, - "walkingRange": 3, - "entries": [ - { "name": "Chicken", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1218cc8e-4200-42cc-99ee-b27dd396e3b4", - "name": "Spawner (122)", + "type": "Spawner", + "guid": "91ddce58-c241-4606-81e8-1d08cfb7de58", + "name": "Spawner (2222)", "location": [5164, 205, 15], - "map": "Trammel", + "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -122,151 +34,25 @@ ] }, { - "$type": "Spawner", - "guid": "12aababc-881a-4724-a9f5-b99ed51f3fe0", - "name": "Spawner (122)", - "location": [1216, 1823, 0], - "map": "Trammel", - "count": 5, + "type": "Spawner", + "guid": "13543563-3b10-42fa-9e48-94856ed4b3c8", + "name": "Spawner (2222)", + "location": [5133, 150, 0], + "map": "Felucca", + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 80, - "walkingRange": 80, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 } - ] + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Daemon", "maxCount": 3, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "12f99d2c-3a40-451c-a75c-28b31f86c35a", - "name": "Spawner (122)", - "location": [7063, 410, 0], - "map": "Trammel", - "count": 6, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1378a58c-09d4-4940-81fa-209f75870791", - "name": "Spawner (122)", - "location": [4422, 1451, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Chicken", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "145f69d9-8fb7-4130-85d1-591799b5b309", - "name": "Spawner (122)", - "location": [2687, 2218, 0], - "map": "Trammel", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "156e6770-5cef-4a8e-abc0-10ae90fe8331", - "name": "Spawner (122)", - "location": [5255, 4000, 37], - "map": "Trammel", - "count": 24, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 80, - "entries": [ - { "name": "Bird", "maxCount": 6, "probability": 100 }, - { "name": "Cat", "maxCount": 6, "probability": 100 }, - { "name": "Dog", "maxCount": 6, "probability": 100 }, - { "name": "Rat", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "18bfc417-d6d9-4495-8215-979ef88c2e96", - "name": "Spawner (122)", - "location": [6986, 384, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1b035b3b-80f4-4486-8571-9138925425f2", - "name": "Spawner (122)", - "location": [5726, 3211, -2], - "map": "Trammel", - "count": 48, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Bird", "maxCount": 12, "probability": 100 }, - { "name": "Cat", "maxCount": 12, "probability": 100 }, - { "name": "Dog", "maxCount": 12, "probability": 100 }, - { "name": "Rat", "maxCount": 12, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1c4cdc18-06dc-4ecf-9f82-e5a723220ee3", - "name": "Spawner (122)", - "location": [2682, 2118, 0], - "map": "Trammel", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 }, - { "name": "Cat", "maxCount": 5, "probability": 100 }, - { "name": "Dog", "maxCount": 5, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d5e571c-c1e3-45d4-a374-04b465b36460", - "name": "Spawner (122)", + "type": "Spawner", + "guid": "0b4c7ad8-a59f-4328-8630-817246fd7cd1", + "name": "Spawner (2222)", "location": [1550, 1658, 26], - "map": "Trammel", + "map": "Felucca", "count": 60, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -281,197 +67,39 @@ ] }, { - "$type": "Spawner", - "guid": "1f7b05c2-d4a9-4828-be39-b1f1de6c3861", - "name": "Spawner (122)", - "location": [7033, 385, 0], - "map": "Trammel", - "count": 10, + "type": "Spawner", + "guid": "2e1c6a6c-b2b4-4946-ad62-a319330cef19", + "name": "Spawner (2222)", + "location": [1319, 1810, 0], + "map": "Felucca", + "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "253c3269-5141-4306-ab24-984c74677fa3", - "name": "Spawner (122)", - "location": [2904, 906, 0], - "map": "Trammel", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Bird", "maxCount": 10, "probability": 100 }, - { "name": "Cat", "maxCount": 10, "probability": 100 }, - { "name": "Dog", "maxCount": 10, "probability": 100 }, - { "name": "Rat", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "290150ac-103d-4117-a891-c09072cfb116", - "name": "Spawner (122)", - "location": [813, 2164, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Chicken", "maxCount": 3, "probability": 100 }, - { "name": "Sheep", "maxCount": 3, "probability": 100 }, - { "name": "Cow", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ac8567f-97e0-4553-aefd-04404e6fa403", - "name": "Spawner (122)", - "location": [1183, 3607, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, + "homeRange": 5, "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 }, - { "name": "Cow", "maxCount": 5, "probability": 100 }, - { "name": "Sheep", "maxCount": 5, "probability": 100 } - ] + "entries": [{ "name": "Cow", "maxCount": 7, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "39a9c600-a998-4cc2-b1a8-ee242efadaf2", - "name": "Spawner (122)", - "location": [7035, 387, 0], - "map": "Trammel", - "count": 6, + "type": "Spawner", + "guid": "22cd06b3-f02b-44f8-80cf-60c95ee4fcc4", + "name": "Spawner (2222)", + "location": [1319, 1821, 0], + "map": "Felucca", + "count": 7, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, + "homeRange": 5, "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Cow", "maxCount": 7, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "3afe2f59-7cc4-482f-8d58-341c919c8fab", - "name": "Spawner (122)", - "location": [1185, 1632, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 80, - "walkingRange": 80, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "405031eb-1ebb-4793-91f7-82fd2c8c4590", - "name": "Spawner (122)", - "location": [7032, 411, 7], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "42c9b654-4a10-4c16-8d34-487646e4c064", - "name": "Spawner (122)", - "location": [7002, 382, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "446d1aec-f4ab-4692-b0a1-f05455ade58e", - "name": "Spawner (122)", - "location": [7087, 382, 1], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "45807a8e-eb86-4b7a-a592-a4d04b1c024f", - "name": "Spawner (122)", - "location": [2566, 622, 0], - "map": "Trammel", - "count": 12, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 }, - { "name": "Cat", "maxCount": 3, "probability": 100 }, - { "name": "Dog", "maxCount": 3, "probability": 100 }, - { "name": "Rat", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4c814d05-058a-44a7-ad86-3e920797bc9e", - "name": "Spawner (122)", - "location": [7081, 381, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4dd3ea55-9eb8-4e5c-b31a-04a071ae46ab", - "name": "Spawner (122)", - "location": [1217, 1825, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "6434f6cc-a8b9-4667-a3e7-5109384d6c75", + "name": "Spawner (2222)", + "location": [1340, 1790, 15], + "map": "Felucca", "count": 20, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -486,43 +114,58 @@ ] }, { - "$type": "Spawner", - "guid": "519581dd-727c-42fe-9b48-854998e02194", - "name": "Spawner (122)", - "location": [570, 1099, 0], - "map": "Trammel", - "count": 15, + "type": "Spawner", + "guid": "6826cac3-d751-442e-9712-e56579d34015", + "name": "Spawner (2222)", + "location": [1339, 1788, 15], + "map": "Felucca", + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Sheep", "maxCount": 15, "probability": 100 } - ] + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "52df17e8-b859-401f-b3ad-d49035f45428", - "name": "Spawner (122)", - "location": [676, 1178, 0], - "map": "Trammel", - "count": 15, + "type": "Spawner", + "guid": "c73dd6cb-2264-4b89-829e-717f741a58b0", + "name": "Spawner (2222)", + "location": [1217, 1825, 0], + "map": "Felucca", + "count": 20, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 15, + "homeRange": 80, + "walkingRange": 80, "entries": [ - { "name": "Sheep", "maxCount": 15, "probability": 100 } + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "5396da5f-3f83-46e9-bd36-541a1af0972e", - "name": "Spawner (122)", + "type": "Spawner", + "guid": "1ad9e818-3743-43df-8834-2bef8d35d69f", + "name": "Spawner (2222)", + "location": [1216, 1823, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f9528415-54f7-4b5b-9390-d0cc6e9ffb1f", + "name": "Spawner (2222)", "location": [1186, 1633, 0], - "map": "Trammel", + "map": "Felucca", "count": 28, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -537,185 +180,25 @@ ] }, { - "$type": "Spawner", - "guid": "561930a3-2f73-4447-b49f-e4637f3fefdf", - "name": "Spawner (122)", - "location": [7044, 429, 0], - "map": "Trammel", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "562f9c3f-4db0-4c6c-831d-83daf19d53ce", - "name": "Spawner (122)", - "location": [3714, 2205, 20], - "map": "Trammel", - "count": 24, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 6, "probability": 100 }, - { "name": "Cat", "maxCount": 6, "probability": 100 }, - { "name": "Dog", "maxCount": 6, "probability": 100 }, - { "name": "Rat", "maxCount": 6, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e763cbc-b631-4c4c-8594-1472369cd6f7", - "name": "Spawner (122)", - "location": [4634, 1302, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "11532176-a402-41bb-801d-b2f4fedff8ce", + "name": "Spawner (2222)", + "location": [1185, 1632, 0], + "map": "Felucca", "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 }, - { "name": "Cow", "maxCount": 5, "probability": 100 }, - { "name": "Sheep", "maxCount": 5, "probability": 100 } - ] + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "66a2657f-2e25-46fb-aca9-585773bd5646", - "name": "Spawner (122)", - "location": [608, 2195, 0], - "map": "Trammel", - "count": 32, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 70, - "walkingRange": 70, - "entries": [ - { "name": "Bird", "maxCount": 8, "probability": 100 }, - { "name": "Cat", "maxCount": 8, "probability": 100 }, - { "name": "Dog", "maxCount": 8, "probability": 100 }, - { "name": "Rat", "maxCount": 8, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "66b8967e-4a25-462a-bac2-70b7c6ee2b08", - "name": "Spawner (122)", - "location": [2939, 746, 2], - "map": "Trammel", - "count": 40, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 100, - "walkingRange": 100, - "entries": [ - { "name": "Bird", "maxCount": 10, "probability": 100 }, - { "name": "Cat", "maxCount": 10, "probability": 100 }, - { "name": "Dog", "maxCount": 10, "probability": 100 }, - { "name": "Rat", "maxCount": 10, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "74e17b0b-5fe4-4965-b55c-787441371f56", - "name": "Spawner (122)", - "location": [7036, 409, 12], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75e5fd43-c65f-4862-b96b-be5fa6e722aa", - "name": "Spawner (122)", - "location": [382, 1192, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "80dd3733-74a7-4a58-baf5-88b1eb228a36", - "name": "Spawner (122)", - "location": [6992, 345, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81a21d4b-6f8a-44ae-a9e4-aca7eda1afa7", - "name": "Spawner (122)", - "location": [688, 1007, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "827b225e-094a-4fe0-80ef-e4ae2e2057c6", - "name": "Spawner (122)", - "location": [2481, 542, 0], - "map": "Trammel", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 70, - "walkingRange": 70, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 }, - { "name": "Cat", "maxCount": 5, "probability": 100 }, - { "name": "Dog", "maxCount": 5, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "84e06a55-fdcb-4448-9560-9a71ae7778fb", - "name": "Spawner (122)", + "type": "Spawner", + "guid": "88b89736-19fe-4265-a878-ae30fb1aa685", + "name": "Spawner (2222)", "location": [1937, 2760, 10], - "map": "Trammel", + "map": "Felucca", "count": 48, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -730,100 +213,30 @@ ] }, { - "$type": "Spawner", - "guid": "86f2e9e8-9ebb-44dc-8372-dc6edfb41847", - "name": "Spawner (122)", - "location": [1340, 1790, 15], - "map": "Trammel", - "count": 20, + "type": "Spawner", + "guid": "a8636c7c-90b3-4c44-8a8d-739728e3b44b", + "name": "Spawner (2222)", + "location": [608, 2195, 0], + "map": "Felucca", + "count": 32, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 80, - "walkingRange": 80, + "homeRange": 70, + "walkingRange": 70, "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 }, - { "name": "Cat", "maxCount": 5, "probability": 100 }, - { "name": "Dog", "maxCount": 5, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 } + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Cat", "maxCount": 8, "probability": 100 }, + { "name": "Dog", "maxCount": 8, "probability": 100 }, + { "name": "Rat", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "8beee950-860e-45be-af5d-437fdd2b5eeb", - "name": "Spawner (122)", - "location": [1435, 4000, 0], - "map": "Trammel", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 }, - { "name": "Cat", "maxCount": 5, "probability": 100 }, - { "name": "Dog", "maxCount": 5, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95de32e9-060f-4776-ac71-d112526ec4b6", - "name": "Spawner (122)", - "location": [3699, 1238, 0], - "map": "Trammel", - "count": 72, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 160, - "walkingRange": 160, - "entries": [ - { "name": "Bird", "maxCount": 18, "probability": 100 }, - { "name": "Cat", "maxCount": 18, "probability": 100 }, - { "name": "Dog", "maxCount": 18, "probability": 100 }, - { "name": "Rat", "maxCount": 18, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "97c5b1a1-5028-44ee-914e-c30b62486126", - "name": "Spawner (122)", - "location": [560, 1239, 0], - "map": "Trammel", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98ed6d30-11e2-4a50-9ca8-4e00cc255d17", - "name": "Spawner (122)", - "location": [675, 939, 0], - "map": "Trammel", - "count": 15, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Sheep", "maxCount": 15, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa850e39-3bd1-43cf-9b34-c8756a3eeb2f", - "name": "Spawner (122)", - "location": [830, 2353, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "e7061f73-946b-4058-be40-de5593cbd075", + "name": "Spawner (2222)", + "location": [813, 2164, 0], + "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -837,170 +250,47 @@ ] }, { - "$type": "Spawner", - "guid": "ac7bee3c-854c-4da1-8676-87cfc0f100e6", - "name": "Spawner (122)", - "location": [2223, 1151, 0], - "map": "Trammel", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 5, - "entries": [ - { "name": "Chicken", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae9c74fb-d8eb-4df7-aa22-412058583e8b", - "name": "Spawner (122)", - "location": [7048, 424, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b058d9e8-aa3b-4410-a984-736338f185e5", - "name": "Spawner (122)", - "location": [1127, 3583, 0], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 }, - { "name": "Cow", "maxCount": 5, "probability": 100 }, - { "name": "Sheep", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3d2bb40-d3d1-4ec9-9a5c-b20b737de066", - "name": "Spawner (122)", - "location": [1319, 1821, 0], - "map": "Trammel", - "count": 7, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 5, - "walkingRange": 10, - "entries": [ - { "name": "Cow", "maxCount": 7, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bb452f73-c524-4b3b-ac31-861a5ae7c30e", - "name": "Spawner (122)", - "location": [1154, 3639, 0], - "map": "Trammel", - "count": 16, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bb7d8052-56aa-48cc-8d9a-31d4d85c0d81", - "name": "Spawner (122)", - "location": [5249, 228, 15], - "map": "Trammel", + "type": "Spawner", + "guid": "ba72b0e5-a33d-437d-9028-5c4b71eea90c", + "name": "Spawner (2222)", + "location": [818, 2269, 0], + "map": "Felucca", "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "Dragon", "maxCount": 1, "probability": 100 }, - { "name": "Drake", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "be3e5a0a-6330-47c5-a254-0e0270696f3c", - "name": "Spawner (122)", - "location": [2472, 434, 15], - "map": "Trammel", - "count": 20, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 }, - { "name": "Cat", "maxCount": 5, "probability": 100 }, - { "name": "Dog", "maxCount": 5, "probability": 100 }, - { "name": "Rat", "maxCount": 5, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c14d6a08-a6b8-486d-bffe-2129d734437b", - "name": "Spawner (122)", - "location": [5267, 4012, 40], - "map": "Trammel", - "count": 5, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, + "homeRange": 10, "walkingRange": 15, "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 }, - { "name": "Sheep", "maxCount": 5, "probability": 100 }, - { "name": "Cow", "maxCount": 5, "probability": 100 }, - { "name": "Bull", "maxCount": 5, "probability": 100 } + { "name": "Chicken", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "c73117b7-d4f3-4fee-926d-b09a412dfa40", - "name": "Spawner (122)", - "location": [2242, 1197, 0], - "map": "Trammel", - "count": 16, + "type": "Spawner", + "guid": "0e4fee28-b3d1-42c8-8f95-4103c53f31e1", + "name": "Spawner (2222)", + "location": [830, 2353, 0], + "map": "Felucca", + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 50, - "walkingRange": 50, + "homeRange": 10, + "walkingRange": 15, "entries": [ - { "name": "Bird", "maxCount": 4, "probability": 100 }, - { "name": "Cat", "maxCount": 4, "probability": 100 }, - { "name": "Dog", "maxCount": 4, "probability": 100 }, - { "name": "Rat", "maxCount": 4, "probability": 100 } + { "name": "Chicken", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 3, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "c74c0ea9-7dae-43fd-99ab-1de27e40d2f8", - "name": "Spawner (122)", + "type": "Spawner", + "guid": "107a8d24-1749-4236-a32a-e1e0c286360e", + "name": "Spawner (2222)", "location": [536, 962, 0], - "map": "Trammel", + "map": "Felucca", "count": 80, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -1015,11 +305,482 @@ ] }, { - "$type": "Spawner", - "guid": "c7f550f6-bf86-41ff-86fe-406006dc60bb", - "name": "Spawner (122)", + "type": "Spawner", + "guid": "e1129f66-8c08-407c-bcfa-56010a3a4519", + "name": "Spawner (2222)", + "location": [676, 1178, 0], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f7e85f44-f68b-419a-859a-6c11cd6cabb6", + "name": "Spawner (2222)", + "location": [570, 1099, 0], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "016fd8f8-82f3-4109-9246-336705000145", + "name": "Spawner (2222)", + "location": [675, 939, 0], + "map": "Felucca", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a8c66161-cf43-47a9-a3a3-c5dcbfa4fbd9", + "name": "Spawner (2222)", + "location": [382, 1192, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "270c974f-1266-4c2e-80f1-d883e392c8c3", + "name": "Spawner (2222)", + "location": [560, 1239, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9132b08c-8598-4130-9779-b2eeaf3b0df0", + "name": "Spawner (2222)", + "location": [688, 1007, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21be647c-e373-43a4-90c9-f24eeafafeda", + "name": "Spawner (2222)", + "location": [2242, 1197, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "490ddc6d-d111-427d-b5cf-8e9f510d191f", + "name": "Spawner (2222)", + "location": [2223, 1151, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Chicken", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a5704357-3d9e-4895-ba9b-1148f0440001", + "name": "Spawner (2222)", + "location": [2939, 746, 2], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ff8d2d7-f679-42d4-b616-7e3006e18695", + "name": "Spawner (2222)", + "location": [2904, 906, 0], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f392c11b-1a43-4647-952c-99783a72e0fd", + "name": "Spawner (2222)", + "location": [2481, 542, 0], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9f8f4ae-ed8f-4888-88ba-14e7e0ab54e1", + "name": "Spawner (2222)", + "location": [2472, 434, 15], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05602f83-caa0-4822-b699-f4427d787aee", + "name": "Spawner (2222)", + "location": [2566, 622, 0], + "map": "Felucca", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "Cat", "maxCount": 3, "probability": 100 }, + { "name": "Dog", "maxCount": 3, "probability": 100 }, + { "name": "Rat", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "48afa381-95b4-4fca-98be-b6ff50b46106", + "name": "Spawner (2222)", + "location": [1154, 3639, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2db897d0-401a-4efe-8f40-ed186c60d376", + "name": "Spawner (2222)", + "location": [1127, 3583, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "004f420b-8d77-487d-9864-cc4879439ee5", + "name": "Spawner (2222)", + "location": [1183, 3607, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a813be3-6c2d-4a5e-a5a2-d7fd2e90abdf", + "name": "Spawner (2222)", + "location": [1400, 3775, 0], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a13d9f3-e379-4e9d-887f-2be3ed304042", + "name": "Spawner (2222)", + "location": [1435, 4000, 0], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96704675-d8b7-4772-9d44-1f334fa1d5bd", + "name": "Spawner (2222)", + "location": [2972, 3436, 15], + "map": "Felucca", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec2ab142-9d54-4cd1-9fd9-5918ddc5593f", + "name": "Spawner (2222)", + "location": [3674, 2624, 0], + "map": "Felucca", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ed2bfa6-08b9-4b93-bd1c-aa67f2ebde6a", + "name": "Spawner (2222)", + "location": [3726, 2647, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Chicken", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cd07faea-cb76-402c-a145-cc4ca8277359", + "name": "Spawner (2222)", + "location": [3652, 2511, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60755e38-7c07-4745-a61e-bd21ddfa841a", + "name": "Spawner (2222)", + "location": [3714, 2205, 20], + "map": "Felucca", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3d3fb31-99cc-4fa9-a5a3-0dd641081add", + "name": "Spawner (2222)", + "location": [3717, 2111, 20], + "map": "Felucca", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7ebf2c6a-3ecf-4da8-84dc-d0fe5bb38264", + "name": "Spawner (2222)", + "location": [2682, 2118, 0], + "map": "Felucca", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "472a9125-a41e-4796-9eb5-63fff737a52a", + "name": "Spawner (2222)", + "location": [2687, 2218, 0], + "map": "Felucca", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e7d8786-aeec-4481-acf8-7c65d6afa71c", + "name": "Spawner (2222)", + "location": [3699, 1238, 0], + "map": "Felucca", + "count": 72, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 160, + "walkingRange": 160, + "entries": [ + { "name": "Bird", "maxCount": 18, "probability": 100 }, + { "name": "Cat", "maxCount": 18, "probability": 100 }, + { "name": "Dog", "maxCount": 18, "probability": 100 }, + { "name": "Rat", "maxCount": 18, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8a3de25-676d-4d86-9400-9a656c62dec1", + "name": "Spawner (2222)", "location": [4438, 1115, 0], - "map": "Trammel", + "map": "Felucca", "count": 48, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -1034,117 +795,344 @@ ] }, { - "$type": "Spawner", - "guid": "d1437b0a-56bb-440c-9625-8313955cdde6", - "name": "Spawner (122)", - "location": [7007, 360, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "4c4303ac-98c9-41ae-a4ed-a286510232a9", + "name": "Spawner (2222)", + "location": [4634, 1302, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d1f89bea-7fed-494e-a03a-ebb7cc8fa3d2", + "name": "Spawner (2222)", + "location": [4567, 1476, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76958af8-18c3-4fcc-8624-be8ccfac900e", + "name": "Spawner (2222)", + "location": [4422, 1451, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Chicken", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3c8b3124-16ab-436f-9687-7ecf1c533563", + "name": "Spawner (2222)", + "location": [7036, 409, 12], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d53c7e28-e23d-48e5-a3cf-8cdd942ad988", + "name": "Spawner (2222)", + "location": [7048, 424, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "53d267c1-a427-4e36-be80-7a5cb96f4669", + "name": "Spawner (2222)", + "location": [7081, 381, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0aa73630-f7ee-48c5-b61a-24b0d6a44941", + "name": "Spawner (2222)", + "location": [7002, 382, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d8016c7e-7b98-4c9b-b7ae-4247f0e578d4", + "name": "Spawner (2222)", + "location": [6999, 352, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da82a0d4-2ddc-44cf-9f7e-227dbb1a0bdc", + "name": "Spawner (2222)", + "location": [7047, 382, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb2d9013-b557-43c3-ab94-f69c92c68a89", + "name": "Spawner (2222)", + "location": [7033, 385, 0], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Bird", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d287da59-9954-4e44-b743-f859fdc73d1c", + "name": "Spawner (2222)", + "location": [7032, 411, 7], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "34b6ad1f-4ab4-4eea-a295-36e62593cecf", + "name": "Spawner (2222)", + "location": [7044, 429, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf55508a-adf0-4dca-9e67-37c947b28064", + "name": "Spawner (2222)", + "location": [6986, 384, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1c30b2f9-fd07-4a84-9d98-21a9e0bf814e", + "name": "Spawner (2222)", + "location": [7063, 410, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "65aa9d17-8718-4d62-9dea-53f965b6a798", + "name": "Spawner (2222)", + "location": [7052, 386, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d4aab4aa-ae2e-4686-b282-39e05a6e0855", + "name": "Spawner (2222)", + "location": [7035, 387, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "79c882ff-dab2-497a-9534-d3971b71734c", + "name": "Spawner (2222)", + "location": [7007, 382, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "832487b1-6929-4cfd-bf10-8f8901a6af24", + "name": "Spawner (2222)", + "location": [7087, 382, 1], + "map": "Felucca", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 } - ] + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "d7f7b008-ad02-4eae-8d4a-8f9163288631", - "name": "Spawner (122)", - "location": [1319, 1810, 0], - "map": "Trammel", - "count": 7, + "type": "Spawner", + "guid": "d233e758-f2b9-4f55-8e65-1441e452f34e", + "name": "Spawner (2222)", + "location": [6992, 345, 0], + "map": "Felucca", + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 5, + "homeRange": 10, "walkingRange": 10, - "entries": [ - { "name": "Cow", "maxCount": 7, "probability": 100 } - ] + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "dd5c1a95-b5d7-4afa-8d8d-97a460e840b7", - "name": "Spawner (122)", - "location": [7047, 382, 0], - "map": "Trammel", - "count": 5, + "type": "Spawner", + "guid": "6f6053e2-9f9b-4e29-b3f6-f4675c82e68b", + "name": "Spawner (2222)", + "location": [7007, 360, 0], + "map": "Felucca", + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, - "walkingRange": 12, - "entries": [ - { "name": "Bird", "maxCount": 5, "probability": 100 } - ] + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e3d55bfe-9e18-4d68-ba3a-691cc402a410", - "name": "Spawner (122)", - "location": [6999, 352, 0], - "map": "Trammel", - "count": 3, + "type": "Spawner", + "guid": "b09a16a0-6abb-420c-8995-24671b9183c6", + "name": "Spawner (2222)", + "location": [5726, 3211, -2], + "map": "Felucca", + "count": 48, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 12, - "walkingRange": 12, + "homeRange": 100, + "walkingRange": 100, "entries": [ - { "name": "Bird", "maxCount": 3, "probability": 100 } + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ee6ae89b-6de4-489c-bc6c-62e43810a6a9", - "name": "Spawner (122)", - "location": [1339, 1788, 15], - "map": "Trammel", - "count": 5, + "type": "Spawner", + "guid": "425dfc37-404d-455d-8bf7-23a3f9857cad", + "name": "Spawner (2222)", + "location": [5255, 4000, 37], + "map": "Felucca", + "count": 24, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 80, "walkingRange": 80, "entries": [ - { "name": "Chicken", "maxCount": 5, "probability": 100 } + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "f474b7be-1817-4b1d-a492-42df4790cedb", - "name": "Spawner (122)", - "location": [5133, 150, 0], - "map": "Trammel", - "count": 3, + "type": "Spawner", + "guid": "dcfdc32c-876a-4432-97ec-9d733f76578f", + "name": "Spawner (2222)", + "location": [5267, 4012, 40], + "map": "Felucca", + "count": 5, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 15, "walkingRange": 15, "entries": [ - { "name": "Daemon", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fd871bd2-e5c3-49ed-9b4e-73021199efe9", - "name": "Spawner (122)", - "location": [818, 2269, 0], - "map": "Trammel", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 15, - "entries": [ - { "name": "Chicken", "maxCount": 3, "probability": 100 }, - { "name": "Sheep", "maxCount": 3, "probability": 100 }, - { "name": "Cow", "maxCount": 3, "probability": 100 } + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Bull", "maxCount": 5, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/uoml/felucca/TownsPeople.json b/Distribution/Data/Spawns/uoml/felucca/TownsPeople.json new file mode 100644 index 000000000..92458315e --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/TownsPeople.json @@ -0,0 +1,769 @@ +[ + { + "type": "Spawner", + "guid": "81cb310e-ae13-4fca-bd6e-3bb693d0ef0a", + "name": "Spawner (2223)", + "location": [1400, 1623, 28], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b902cab2-6165-44ca-b583-814d9d3849a0", + "name": "Spawner (2223)", + "location": [1400, 1628, 28], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e284edef-ae7d-4ed1-8620-441dfb4618c9", + "name": "Spawner (2223)", + "location": [1521, 1457, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "ChaosGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e97a4cd6-1170-4ab3-abf2-58b0c8e241bd", + "name": "Spawner (2223)", + "location": [1525, 1457, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "ChaosGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d916eb1-ed25-4d3b-a9cd-995e97630ed8", + "name": "Spawner (2223)", + "location": [1662, 1611, 19], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "409f20ec-24eb-4ac4-9768-ee6467476f0c", + "name": "Spawner (2223)", + "location": [1421, 1698, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70250667-d0b4-488e-95e3-c660bcab2428", + "name": "Spawner (2223)", + "location": [2254, 1207, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4bb5474-dfc9-46a7-ba0d-9b117110cb0b", + "name": "Spawner (2223)", + "location": [5290, 3987, 37], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "34e31c96-47b7-4760-8446-d0a1f816d315", + "name": "Spawner (2223)", + "location": [3652, 2619, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ee365839-216c-4690-89cc-f5d83845f669", + "name": "Spawner (2223)", + "location": [1330, 3778, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84f9a05f-e99b-4ece-9156-1978b789c47f", + "name": "Spawner (2223)", + "location": [3721, 2161, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9c4d2d6b-56e8-4473-959d-9cc9199c11e1", + "name": "Spawner (2223)", + "location": [2515, 557, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4de7c0e5-3203-4a68-a2d6-8e78652191fc", + "name": "Spawner (2223)", + "location": [4451, 1153, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ba1a481e-d5da-4445-af80-eee525b3e3c9", + "name": "Spawner (2223)", + "location": [3773, 1308, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8517bfc1-8190-42fb-bfec-f5a0a03488cc", + "name": "Spawner (2223)", + "location": [5685, 3148, -4], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92425d73-e98b-46d3-b391-77582cda9302", + "name": "Spawner (2223)", + "location": [2891, 3474, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fa72d822-a3e5-43f5-9eb6-f800b3e49553", + "name": "Spawner (2223)", + "location": [603, 2141, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c5d53f85-e6b8-4791-a77e-d241fab79d05", + "name": "Spawner (2223)", + "location": [1914, 2687, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0f9c22dd-0738-4b96-b652-3171ed867cbd", + "name": "Spawner (2223)", + "location": [1829, 2811, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a65b533b-cb58-4d72-8c9b-5375a7e7ad02", + "name": "Spawner (2223)", + "location": [2895, 686, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0f1fbc08-b932-424b-956a-f78082db8525", + "name": "Spawner (2223)", + "location": [627, 864, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9119d23e-0857-4d0c-a03e-c345aafdd7fe", + "name": "Spawner (2223)", + "location": [1399, 3743, -21], + "map": "Felucca", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "HireBard", "maxCount": 5, "probability": 100 }, + { "name": "HireBardArcher", "maxCount": 5, "probability": 100 }, + { "name": "HireBeggar", "maxCount": 5, "probability": 100 }, + { "name": "HireFighter", "maxCount": 5, "probability": 100 }, + { "name": "HireMage", "maxCount": 5, "probability": 100 }, + { "name": "HireRanger", "maxCount": 5, "probability": 100 }, + { "name": "HireRangerArcher", "maxCount": 5, "probability": 100 }, + { "name": "HireSailor", "maxCount": 5, "probability": 100 }, + { "name": "HireThief", "maxCount": 5, "probability": 100 }, + { "name": "HirePaladin", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63416791-9505-4a1e-ab72-91c9438612df", + "name": "Spawner (2223)", + "location": [1483, 1760, -2], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9522412c-95ef-4067-9b3c-fd814717f9b4", + "name": "Spawner (2223)", + "location": [1372, 3916, -2], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb794f47-6ae2-4f92-809d-27c34fbd0b7b", + "name": "Spawner (2223)", + "location": [1126, 3688, -2], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a39b544b-76ab-424d-9f6d-a91cd55e3ed6", + "name": "Spawner (2223)", + "location": [3674, 2291, -2], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "259cc674-14e4-4fe2-b8fa-10af4610ffbc", + "name": "Spawner (2223)", + "location": [4398, 1049, -2], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "017be0ee-b224-4daf-9626-6bcb46a4b433", + "name": "Spawner (2223)", + "location": [3815, 1217, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "87354a42-802a-4607-92e9-cd39a5bac46d", + "name": "Spawner (2223)", + "location": [3749, 1404, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6301492f-d4ed-4f24-afb2-cbbedb68afe6", + "name": "Spawner (2223)", + "location": [3645, 2661, -2], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aac6fef9-46e2-44b2-9297-cd812c3b2c7b", + "name": "Spawner (2223)", + "location": [2994, 3451, 16], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ee30f8c4-8c52-4aaf-a974-2e4df4d0e28e", + "name": "Spawner (2223)", + "location": [673, 2235, -3], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bba7781a-c2fd-486f-8bfa-587c809347d5", + "name": "Spawner (2223)", + "location": [573, 2246, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "380cd112-1791-4b59-8910-9ec257750d3e", + "name": "Spawner (2223)", + "location": [2068, 2854, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "710c1e19-1ae1-4131-99c6-184cb4636f03", + "name": "Spawner (2223)", + "location": [3041, 827, -3], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d95bb2d0-7f44-4ff3-9c87-93d0ba887e86", + "name": "Spawner (2223)", + "location": [631, 883, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ed933bb-802f-444d-855d-c499926302ab", + "name": "Spawner (2223)", + "location": [1813, 2834, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e134aa6f-68a5-48c8-819f-64e2806f8877", + "name": "Spawner (2223)", + "location": [2875, 675, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adee85c7-830f-4ef1-8539-77b5006da4f9", + "name": "Spawner (2223)", + "location": [2874, 692, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23e949aa-940a-4b5a-9ce9-762db82b58a6", + "name": "Spawner (2223)", + "location": [2884, 692, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "892bd9fc-6385-46b9-9c20-dd9eeb4dcdc8", + "name": "Spawner (2223)", + "location": [7082, 367, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Olaeni", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e6761b5-a94e-4a95-90da-c08d5966aeb5", + "name": "Spawner (2223)", + "location": [7063, 350, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [ + { "name": "Bolaevin", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "478d5bf8-5043-4d76-9a45-802677f0c30f", + "name": "Spawner (2223)", + "location": [7010, 363, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "athialon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c7251678-03dd-4030-8316-2529b932fecb", + "name": "Spawner (2223)", + "location": [7048, 382, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [ + { "name": "abbein", "maxCount": 1, "probability": 100 }, + { "name": "taellia", "maxCount": 1, "probability": 100 }, + { "name": "vicaie", "maxCount": 1, "probability": 100 }, + { "name": "mallew", "maxCount": 1, "probability": 100 }, + { "name": "jothan", "maxCount": 1, "probability": 100 }, + { "name": "alethanian", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9a63304-f0da-4144-9982-f8ce6d4c1914", + "name": "Spawner (2223)", + "location": [6989, 342, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Tyeelor", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d5e0ca7-10d5-4475-9aa3-23bdc96210de", + "name": "Spawner (2223)", + "location": [7053, 338, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, + "entries": [ + { "name": "aneen", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01057a94-5e22-4609-8162-aa947144da30", + "name": "Spawner (2223)", + "location": [7031, 409, 7], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Alelle", "maxCount": 1, "probability": 100 }, + { "name": "Daelas", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9f512c6-5b66-4f2a-9a03-6c0c99d7863f", + "name": "Spawner (2223)", + "location": [7090, 387, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "Aluniol", "maxCount": 1, "probability": 100 }, + { "name": "Rebinil", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/felucca/TrinsicPassage.json b/Distribution/Data/Spawns/uoml/felucca/TrinsicPassage.json new file mode 100644 index 000000000..b50e107f5 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/TrinsicPassage.json @@ -0,0 +1,119 @@ +[ + { + "type": "Spawner", + "guid": "c08391fb-b609-4c8d-be85-2d3986f1639a", + "name": "Spawner (2224)", + "location": [5976, 1387, -22], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0481877-f573-4c00-af1f-c1f5873f4bbb", + "name": "Spawner (2224)", + "location": [5940, 1356, -2], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "047a7695-7bd9-46ad-852d-f84b2895b843", + "name": "Spawner (2224)", + "location": [5912, 1326, 0], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8190243a-f39d-48b2-9df3-9b00b6e418d4", + "name": "Spawner (2224)", + "location": [5956, 1310, -2], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d661a6de-3520-464f-994f-77ff738ff5bd", + "name": "Spawner (2224)", + "location": [5999, 1327, 10], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37ea1620-9487-40df-8035-e76b2443a7b2", + "name": "Spawner (2224)", + "location": [5915, 1302, 2], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/shared/felucca/Vendors.json b/Distribution/Data/Spawns/uoml/felucca/Vendors.json similarity index 79% rename from Distribution/Data/Spawns/shared/felucca/Vendors.json rename to Distribution/Data/Spawns/uoml/felucca/Vendors.json index ed769fcf9..cd460eec3 100644 --- a/Distribution/Data/Spawns/shared/felucca/Vendors.json +++ b/Distribution/Data/Spawns/uoml/felucca/Vendors.json @@ -1,328 +1,25 @@ [ { - "$type": "Spawner", - "guid": "00c709c3-bd1f-464f-9851-6222d10bf35b", - "name": "Spawner (225)", - "location": [5215, 117, 1], + "type": "Spawner", + "guid": "da1bd33e-e6d5-4311-8177-9443fe6c1c63", + "name": "Spawner (2225)", + "location": [1685, 2985, 0], "map": "Felucca", - "count": 3, + "count": 4, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 0, - "walkingRange": 5, + "homeRange": 10, + "walkingRange": 20, "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + { "name": "Herbalist", "maxCount": 2, "probability": 100 }, + { "name": "Mage", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "01605fa2-174e-473c-b83b-7b2edb42c693", - "name": "Spawner (225)", - "location": [5726, 3243, 16], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Tinker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "017536ca-0a2b-45c1-90a2-e6279f1a2e79", - "name": "Spawner (225)", - "location": [587, 2146, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "01e09394-d02b-4fad-9ee9-511454e49140", - "name": "Spawner (225)", - "location": [1981, 2838, 15], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "02c8aec2-f3b8-4626-9127-c3685d861a3b", - "name": "Spawner (225)", - "location": [1897, 2805, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "03132a28-66bf-4d70-ad03-efc8590f21fe", - "name": "Spawner (225)", - "location": [564, 1011, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "039694fe-6a0b-41bf-a561-1391dfb2cb14", - "name": "Spawner (225)", - "location": [4484, 1064, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "03bc089d-0286-40d7-a5e1-dc425e0c8f53", - "name": "Spawner (225)", - "location": [1372, 3908, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "048d0dc3-9b09-4997-825b-e316da48d60f", - "name": "Spawner (225)", - "location": [1990, 2889, 5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "04c6be93-3c01-460a-9035-cc4a78774b5c", - "name": "Spawner (225)", - "location": [4401, 1165, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "053c4263-9e79-4067-ab30-8576df3e9b00", - "name": "Spawner (225)", - "location": [1456, 3850, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "058219b9-095c-409b-87b0-ffff99c4e0c9", - "name": "Spawner (225)", - "location": [650, 2178, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "05a68019-c07b-428f-8e99-90f5ace263b1", - "name": "Spawner (225)", - "location": [2438, 410, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "05b65869-ea89-4ac6-9233-aef3768a2796", - "name": "Spawner (225)", - "location": [1939, 2739, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "06054f54-b0fa-4f88-9306-92972f4dcca4", - "name": "Spawner (225)", - "location": [1498, 1659, 27], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "064da568-3987-4ae8-90b0-592f6e49acd3", - "name": "Spawner (225)", - "location": [2026, 2845, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "098512b2-9b9c-4dd1-95aa-631d5c79c220", - "name": "Spawner (225)", - "location": [1418, 1547, 30], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0caf1231-8fd1-4873-b525-29c1b9a4751c", - "name": "Spawner (225)", - "location": [5351, 56, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0d922542-dbaa-45a6-b70f-491621ed4509", - "name": "Spawner (225)", - "location": [5191, 3989, 37], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "0df23a73-3ee4-4f00-87ce-576069c5ce66", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [1451, 1679, 0], "map": "Felucca", "count": 1, @@ -331,47 +28,13 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "0f3b0c94-b2e2-4c22-99d5-c559758a9043", - "name": "Spawner (225)", - "location": [1422, 1654, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1002eb76-82f2-4dc6-863b-8c1720c1294c", - "name": "Spawner (225)", - "location": [1388, 1655, 30], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "10283e6b-f2de-47b8-b6c8-21ee93bbbaf7", - "name": "Spawner (225)", - "location": [1431, 1612, 20], + "type": "Spawner", + "guid": "7d4cb40b-fc42-4aac-8aaa-29f5c98a2e2e", + "name": "Spawner (2225)", + "location": [1602, 1712, 20], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -380,15 +43,29 @@ "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "134687d6-0adb-4dac-a522-1bf22a1c76c7", - "name": "Spawner (225)", - "location": [3774, 1265, 0], + "type": "Spawner", + "guid": "3fa1e658-1d2d-4d6b-8b45-49c4e941c223", + "name": "Spawner (2225)", + "location": [1470, 1578, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7aa8ce1b-2fda-4a02-a725-62342dc96905", + "name": "Spawner (2225)", + "location": [1547, 1659, 26], "map": "Felucca", "count": 3, "minDelay": "00:05:00", @@ -403,569 +80,9 @@ ] }, { - "$type": "Spawner", - "guid": "13a55ac1-98b8-4c8e-ae53-5408dc9aa8bb", - "name": "Spawner (225)", - "location": [2731, 2192, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1411c07d-db5c-45db-aef9-5ca1c7f9c769", - "name": "Spawner (225)", - "location": [3729, 1258, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "14284f7f-ef5e-4dff-a0aa-bf84616d15a8", - "name": "Spawner (225)", - "location": [2715, 2098, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "143a413e-2a69-4e60-a5b2-892becd26dac", - "name": "Spawner (225)", - "location": [1485, 1550, 30], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "147578da-6cae-4d44-ad6e-3b01f3e2d670", - "name": "Spawner (225)", - "location": [2841, 907, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "15b48a52-8c02-438e-bd97-aae13dada523", - "name": "Spawner (225)", - "location": [1935, 2796, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "16fdba4a-b4ce-4017-9f6d-6b535ae8350a", - "name": "Spawner (225)", - "location": [2526, 375, 23], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "173141ce-120f-451c-8d7e-781b07e8482a", - "name": "Spawner (225)", - "location": [553, 985, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "19957abb-f841-4c9a-b52a-ac8c19cd0ab0", - "name": "Spawner (225)", - "location": [3674, 2289, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "19fad4db-4751-4907-8561-8ef0a0d22959", - "name": "Spawner (225)", - "location": [3629, 2608, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1aa3d9a8-9ace-49cb-969e-6a4350b5ba08", - "name": "Spawner (225)", - "location": [2216, 1192, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1bf5b7f8-c166-419f-87f5-199254245758", - "name": "Spawner (225)", - "location": [529, 1008, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1d6a16c9-b172-40da-9573-4c979fa74ab3", - "name": "Spawner (225)", - "location": [1438, 1761, -2], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 10, - "entries": [ - { "name": "hiresailor", "maxCount": 1, "probability": 100 }, - { "name": "Fisherman", "maxCount": 1, "probability": 100 }, - { "name": "Rat", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21ea5650-c7b3-48d0-b78b-1195290b17f8", - "name": "Spawner (225)", - "location": [3018, 3426, 15], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22117025-4e5b-4bc6-a65c-3803cea02917", - "name": "Spawner (225)", - "location": [1510, 1543, 25], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22214376-1f4d-4e5a-a3c5-0041c4302fa3", - "name": "Spawner (225)", - "location": [620, 2218, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "22666a8e-0c05-4ebd-b93e-92411a48d9c9", - "name": "Spawner (225)", - "location": [3672, 2654, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2325bd37-aa70-467d-a132-c77447fa333d", - "name": "Spawner (225)", - "location": [2963, 813, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "24029208-9dc1-44d6-829c-f60e05b7089e", - "name": "Spawner (225)", - "location": [630, 2194, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "25c1334d-8fb1-442a-9315-f549ed3c5254", - "name": "Spawner (225)", - "location": [2256, 1181, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "267e1e11-d6b0-44cb-a161-c8c3b962d1af", - "name": "Spawner (225)", - "location": [3778, 1172, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2761adf7-e265-438a-a2a8-284e00c4b605", - "name": "Spawner (225)", - "location": [2890, 651, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "279cfcf8-24e9-4390-a4e2-a507e02e89ef", - "name": "Spawner (225)", - "location": [3673, 2614, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "27f0fb46-5d81-4688-8e10-7cdebe46fc32", - "name": "Spawner (225)", - "location": [4481, 1085, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Farmer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "298aca7b-3047-4808-8b9e-e972a0bc4b08", - "name": "Spawner (225)", - "location": [1228, 1572, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 10, - "entries": [ - { "name": "Farmer", "maxCount": 1, "probability": 100 }, - { "name": "Rancher", "maxCount": 1, "probability": 100 }, - { "name": "hirepeasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2be191ab-063f-49a5-8ecc-71c7b52e05b2", - "name": "Spawner (225)", - "location": [602, 2180, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ce77da7-68d6-4c82-a3f4-8fdc1161ab7f", - "name": "Spawner (225)", - "location": [1813, 2825, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2d2440ab-0e7c-4fa1-a078-688127e2feb5", - "name": "Spawner (225)", - "location": [1852, 2831, 10], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2dc8250b-995a-4eae-a615-2c7b0037fd64", - "name": "Spawner (225)", - "location": [1434, 1717, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Merchant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2dd708a7-fa32-46ad-a558-56d4464a6442", - "name": "Spawner (225)", - "location": [1395, 3705, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2eac5045-fa01-46a1-aab1-19d09c2bd246", - "name": "Spawner (225)", - "location": [1455, 1557, 30], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bard", "maxCount": 1, "probability": 100 }, - { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2ffb4f73-598f-47b4-b4e9-e40c7a7ab424", - "name": "Spawner (225)", - "location": [1150, 1619, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 10, - "entries": [ - { "name": "Farmer", "maxCount": 1, "probability": 100 }, - { "name": "Rancher", "maxCount": 1, "probability": 100 }, - { "name": "hirepeasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3037b09f-4d00-4b90-b77f-4a9c77d446bd", - "name": "Spawner (225)", - "location": [2972, 3425, 15], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "30a5dc4e-3f06-4dd4-b3aa-d66d55905b62", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [1620, 1586, 0], "map": "Felucca", "count": 4, @@ -982,1844 +99,9 @@ ] }, { - "$type": "Spawner", - "guid": "31475343-bb81-44f0-9ece-86234032a185", - "name": "Spawner (225)", - "location": [3031, 3350, 35], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "31cbfeeb-3e9c-440e-a424-2524ca287d1b", - "name": "Spawner (225)", - "location": [2033, 2801, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32955c0b-65f3-4a59-ad71-de6854ec16b9", - "name": "Spawner (225)", - "location": [2478, 427, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3327ba9f-5ab2-4203-81c3-10ed191fe8a8", - "name": "Spawner (225)", - "location": [1304, 1826, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "Peasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "333fe588-e295-4247-b8c6-c0b492f161cc", - "name": "Spawner (225)", - "location": [3699, 2163, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "347d6ccf-8681-4dc3-b6be-0b0edecb9d03", - "name": "Spawner (225)", - "location": [1498, 1691, 20], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "34a7b4b8-cdad-46e2-b1c7-5aebe2fa41d1", - "name": "Spawner (225)", - "location": [578, 2227, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "35225c44-2e25-43d6-a2f3-82ef04806cbd", - "name": "Spawner (225)", - "location": [1427, 1726, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "hirebard", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3535310b-bd5f-4938-92a8-07969bc0cdf5", - "name": "Spawner (225)", - "location": [1494, 1715, 6], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "358552dd-07fb-4fc4-ad49-366f4cbd7105", - "name": "Spawner (225)", - "location": [517, 986, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "37ea3966-1157-4d85-a26e-1a9f3008dd75", - "name": "Spawner (225)", - "location": [2707, 2178, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3825dd92-a301-4bf0-9d73-c29fdc1b0043", - "name": "Spawner (225)", - "location": [1435, 3820, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "38a24240-c538-40cf-93a6-65e6c31455c5", - "name": "Spawner (225)", - "location": [643, 1083, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "390c60c0-4fd2-43b8-a9a2-5c194dc235a7", - "name": "Spawner (225)", - "location": [3667, 2252, 20], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "39db05f3-b04e-4ff7-911c-49a9cd081aa5", - "name": "Spawner (225)", - "location": [3755, 2227, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3a125d56-a7a0-4e00-a7c6-b0f7fea301a3", - "name": "Spawner (225)", - "location": [2835, 805, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3b4c2e06-e89a-484e-9afe-d1c285a46db6", - "name": "Spawner (225)", - "location": [1467, 1686, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3b6c12ad-854f-409f-a281-f04dce59330f", - "name": "Spawner (225)", - "location": [1419, 3859, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ef25d3d-9926-4bcb-b14e-b043e23982af", - "name": "Spawner (225)", - "location": [1437, 3750, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3f935f7e-4454-44fa-9222-58e667345c4d", - "name": "Spawner (225)", - "location": [5714, 3202, 12], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "Herbalist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3fa1e658-1d2d-4d6b-8b45-49c4e941c223", - "name": "Spawner (225)", - "location": [1470, 1578, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3ff08692-d784-4f17-8291-8249aaf2dc6e", - "name": "Spawner (225)", - "location": [2450, 428, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41bac877-ca53-4a5f-aa2d-12de82f8b3f4", - "name": "Spawner (225)", - "location": [3036, 3464, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41d788c5-5cc4-4cd1-9110-67560560dade", - "name": "Spawner (225)", - "location": [2860, 999, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "42f7a139-1c12-457b-bfd7-5f50fc33a57e", - "name": "Spawner (225)", - "location": [1349, 1778, 15], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Blacksmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4381baac-5e70-48a1-bacb-99ff2a41663a", - "name": "Spawner (225)", - "location": [1585, 1591, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "43be54dd-c46c-4d7b-819e-a792890ff138", - "name": "Spawner (225)", - "location": [4471, 1156, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "43cb9a14-49b9-4f88-885d-d98eef7479b9", - "name": "Spawner (225)", - "location": [2882, 723, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4427c2d2-613b-4859-a8e0-c14df5bd0f52", - "name": "Spawner (225)", - "location": [1425, 3981, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4437c2af-dd86-4295-9b55-3d45746cd99a", - "name": "Spawner (225)", - "location": [3545, 1178, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4493f83f-d73b-4691-b01e-cda4b8046621", - "name": "Spawner (225)", - "location": [4440, 1162, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "44c610bd-fae7-481c-9174-9a3e34068786", - "name": "Spawner (225)", - "location": [2659, 2194, 4], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "ThiefGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "453d3fec-1f00-4ae4-9633-5c71510d64f8", - "name": "Spawner (225)", - "location": [2450, 428, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "456fbee0-848e-4ea7-b528-a7124166beb7", - "name": "Spawner (225)", - "location": [1316, 1747, 10], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 15, - "walkingRange": 20, - "entries": [ - { "name": "hirefighter", "maxCount": 3, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "46c5c4a0-48d4-4e36-88ae-cbf18533e56b", - "name": "Spawner (225)", - "location": [3647, 2681, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4723e0e9-a7e0-4657-9e74-0610afdf5a24", - "name": "Spawner (225)", - "location": [2920, 856, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "472774c8-7b94-461a-a9e1-4d3004062592", - "name": "Spawner (225)", - "location": [5662, 3150, 13], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "47601b12-bda9-4dab-92db-13ba9022ebaf", - "name": "Spawner (225)", - "location": [3667, 2586, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4a3ad267-fe69-4133-8021-6a46265945e8", - "name": "Spawner (225)", - "location": [2906, 3485, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4cc10b14-a086-4781-84ff-f656af24f44e", - "name": "Spawner (225)", - "location": [3665, 2140, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d7d6dc0-179c-41c3-88c3-26d47e2b66a8", - "name": "Spawner (225)", - "location": [2954, 705, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Beekeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4d903014-9897-4a01-8ca1-4dcddadaf629", - "name": "Spawner (225)", - "location": [2072, 2856, -3], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4dd996e0-2cf5-4d04-8b3b-3264c0bed46a", - "name": "Spawner (225)", - "location": [2918, 672, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "50c3d9c7-0a08-46b4-af3c-9ef791c012ee", - "name": "Spawner (225)", - "location": [1405, 1810, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 25, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "51487317-ca09-48ac-a372-7acae8ca4ca4", - "name": "Spawner (225)", - "location": [4395, 1137, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "51c50c2c-1ad2-4b38-bfd3-e3680b82624e", - "name": "Spawner (225)", - "location": [1452, 3747, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5382e8d9-4883-46b4-9f57-f7eedea8f0b3", - "name": "Spawner (225)", - "location": [5242, 180, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "546c63ee-9eca-4c1c-8405-108d7eb1df12", - "name": "Spawner (225)", - "location": [1409, 1590, 42], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "553b4277-f7f3-44a0-8d8a-b1cf97b10152", - "name": "Spawner (225)", - "location": [5839, 3256, 2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "56b03849-fb73-4883-8368-9f0a01df0403", - "name": "Spawner (225)", - "location": [3708, 2649, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "56c50bc0-b5de-45d5-b9ed-3da67c3efe73", - "name": "Spawner (225)", - "location": [3051, 3367, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "56c75ca9-bdbc-4709-bfae-6bb56308f2da", - "name": "Spawner (225)", - "location": [1362, 1825, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 6, - "walkingRange": 12, - "entries": [ - { "name": "Artist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "591c4553-36ec-4e20-8f04-1b7d2f056c7a", - "name": "Spawner (225)", - "location": [5671, 3287, 11], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "59707794-b18e-4101-9c64-f28940881270", - "name": "Spawner (225)", - "location": [3740, 1197, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "59739d59-4955-4749-b91e-0dc56587d9d4", - "name": "Spawner (225)", - "location": [1650, 1608, 20], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "59b02f45-35c8-4b36-a55b-431fbb8d3254", - "name": "Spawner (225)", - "location": [1149, 1547, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 10, - "walkingRange": 10, - "entries": [ - { "name": "Farmer", "maxCount": 1, "probability": 100 }, - { "name": "Rancher", "maxCount": 1, "probability": 100 }, - { "name": "hirepeasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5b3d26b3-c709-4ed0-bd77-a257a8ca84e2", - "name": "Spawner (225)", - "location": [3720, 2126, 20], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5cc88170-da30-4eb5-a8c7-19e8f29caa86", - "name": "Spawner (225)", - "location": [3734, 2149, 20], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5d2fdc26-5ecb-44f8-a59f-a04aa21f7f29", - "name": "Spawner (225)", - "location": [1897, 2684, 10], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e19650f-6ec9-4170-8aed-3ff6cb384366", - "name": "Spawner (225)", - "location": [5306, 37, 40], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5f377cf6-2581-4b00-95ac-e8804ab76415", - "name": "Spawner (225)", - "location": [5775, 3161, 14], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5f707815-9566-4210-a8e6-51f5affda55b", - "name": "Spawner (225)", - "location": [3058, 3400, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5f865ca2-1ac4-4612-9079-18dd99f2f42d", - "name": "Spawner (225)", - "location": [1512, 3989, -3], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5fec0502-6f90-4e48-b0ef-fd58b3c8ab5e", - "name": "Spawner (225)", - "location": [2513, 477, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Architect", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60ff9933-37fb-44e0-8794-2dfc53969609", - "name": "Spawner (225)", - "location": [3009, 3455, 15], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6101131e-6247-4e30-b693-0c7a26b09cec", - "name": "Spawner (225)", - "location": [2526, 546, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "612b7282-50f3-48c0-8172-4d6ed40f47ff", - "name": "Spawner (225)", - "location": [2987, 777, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "61506934-e743-4d80-8c62-daf3fa9c6648", - "name": "Spawner (225)", - "location": [2905, 3517, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "61bb7c02-bbc3-497f-9d42-bc82a6378c3a", - "name": "Spawner (225)", - "location": [5225, 4000, 38], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "62ab8dc9-0f3e-411a-aba1-d2449a7d882c", - "name": "Spawner (225)", - "location": [2245, 1231, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "64afa061-436e-4816-acc8-b4b932a2f31f", - "name": "Spawner (225)", - "location": [3764, 1317, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6602517d-493c-41c3-8bfb-2738aa2a2dcd", - "name": "Spawner (225)", - "location": [1386, 3825, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Farmer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "667b1e85-dea9-4c92-a796-9c8fe4958ac0", - "name": "Spawner (225)", - "location": [1477, 1741, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 20, - "walkingRange": 20, - "entries": [ - { "name": "hiresailor", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "66bba18a-084f-4657-909d-a2c7e06e4e49", - "name": "Spawner (225)", - "location": [3017, 761, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Farmer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "676fa552-31ef-4384-8dd1-e68d0d434f0c", - "name": "Spawner (225)", - "location": [2995, 815, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "67acd17b-db93-4bb1-9f67-f4c9015a1888", - "name": "Spawner (225)", - "location": [1295, 1773, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "BrideGroom", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6908fde5-9dd0-4385-8202-d07d88bfba2c", - "name": "Spawner (225)", - "location": [5737, 3222, 11], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Healer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "69144e2b-49b0-4027-ad34-f12811dae9a9", - "name": "Spawner (225)", - "location": [1454, 4003, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bae0089-ee4c-4961-9b8b-a04cb40f3fff", - "name": "Spawner (225)", - "location": [1387, 3771, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6bbc2f39-f8bf-4ff8-8cce-8128c08ae64b", - "name": "Spawner (225)", - "location": [2940, 3410, 1], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6c826160-cc14-4c24-91ac-8fac400a9d4c", - "name": "Spawner (225)", - "location": [4409, 1111, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6d74a89d-c5fa-49d6-8a1b-a52aa1484f5e", - "name": "Spawner (225)", - "location": [2627, 2100, 10], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6f32b48f-e661-4e16-a5c6-1755a1c77e57", - "name": "Spawner (225)", - "location": [2975, 3353, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6fa80ffa-14af-4633-9e2f-fcc64b96c946", - "name": "Spawner (225)", - "location": [2840, 885, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6fb8b91e-1bb9-4d0a-8ffe-ddfb552cabf5", - "name": "Spawner (225)", - "location": [610, 810, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "701c4bc8-b92a-4d2e-9f31-bb0e73157bc7", - "name": "Spawner (225)", - "location": [3731, 1320, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "74bc4555-d00a-4214-bb20-9c37ddffa4d6", - "name": "Spawner (225)", - "location": [4448, 1090, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "757854f4-8a3a-48bf-a288-3cf416c80b0c", - "name": "Spawner (225)", - "location": [659, 2141, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75b0732c-4590-4031-88c0-2ad4750e7445", - "name": "Spawner (225)", - "location": [2634, 2083, 10], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "778b3fd0-9f0f-4e70-9923-917b2b268fd1", - "name": "Spawner (225)", - "location": [3661, 2183, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "77ea211d-a375-4f9f-a2ff-31a864070728", - "name": "Spawner (225)", - "location": [5236, 142, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7aa8ce1b-2fda-4a02-a725-62342dc96905", - "name": "Spawner (225)", - "location": [1547, 1659, 26], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7acbc3b8-8176-40ce-8d76-848b7ce1bedc", - "name": "Spawner (225)", - "location": [3665, 2531, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7b2c31b7-20c9-46b8-9cd1-6b624a505c01", - "name": "Spawner (225)", - "location": [1880, 2802, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7cbf932d-ba9f-4a37-8ff3-068952fcbe6c", - "name": "Spawner (225)", - "location": [3013, 3353, 15], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d4728bf-f926-42cc-b670-37b8c74e5dbb", - "name": "Spawner (225)", - "location": [1935, 2766, 10], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7d4cb40b-fc42-4aac-8aaa-29f5c98a2e2e", - "name": "Spawner (225)", - "location": [1602, 1712, 20], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7ff0fc47-17c9-4073-a0e0-d1c7520cf9bb", - "name": "Spawner (225)", - "location": [1341, 1733, 20], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "hirefighter", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8018ee5a-2f60-4dad-8ac7-8678c56000ac", - "name": "Spawner (225)", - "location": [1190, 1707, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 8, - "entries": [ - { "name": "Farmer", "maxCount": 1, "probability": 100 }, - { "name": "Rancher", "maxCount": 1, "probability": 100 }, - { "name": "hirepeasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8080d16f-4ee6-4d7b-b017-cebc7113ce25", - "name": "Spawner (225)", - "location": [5746, 3200, 16], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "80a970ea-b163-4589-a626-c0fc6f875350", - "name": "Spawner (225)", - "location": [2997, 3428, 15], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81f52dcd-ccb6-470c-a58a-9141b90be9c3", - "name": "Spawner (225)", - "location": [5669, 3131, 14], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Minter", "maxCount": 1, "probability": 100 }, - { "name": "Banker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "81f5c4b0-e089-4a51-b311-56a700b2845d", - "name": "Spawner (225)", - "location": [1449, 1723, 6], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "82487046-2e78-49ae-ba4b-0a79b8247eb5", - "name": "Spawner (225)", - "location": [1450, 1617, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "825d89ed-d1f3-45e5-b7c0-0b86434ec759", - "name": "Spawner (225)", - "location": [1822, 2739, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8521a0ed-ffd6-430e-9cd6-b1e9dc43713f", - "name": "Spawner (225)", - "location": [1364, 3732, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "86011460-6b38-45da-8706-cbfad5250fe0", - "name": "Spawner (225)", - "location": [2962, 885, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "87de110f-213d-4275-b7f0-82e7991742ff", - "name": "Spawner (225)", - "location": [1481, 1584, 20], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "87f9a58b-fa94-4899-b3de-e2e344641494", - "name": "Spawner (225)", - "location": [5731, 3192, 9], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "Mage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8883ad81-f99d-4ccb-8d1d-65ada7848c65", - "name": "Spawner (225)", - "location": [3769, 1218, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "88bafb3d-aa60-4138-8d7d-2e7c9e733efd", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [1430, 1597, 20], "map": "Felucca", "count": 3, @@ -2835,214 +117,10 @@ ] }, { - "$type": "Spawner", - "guid": "8972d5d6-a75e-4dcb-85f4-565dd1a644e6", - "name": "Spawner (225)", - "location": [1388, 1588, 30], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "89aa4fc3-f834-4499-bbc6-ac47b395ce13", - "name": "Spawner (225)", - "location": [2475, 397, 15], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8b6efd78-0c41-4bd1-b51c-35e870f89a5c", - "name": "Spawner (225)", - "location": [562, 2148, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "RangerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8be1dd4d-eda1-42c7-a107-8f523ec06814", - "name": "Spawner (225)", - "location": [2002, 2721, 30], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8d40d06e-44aa-40b5-8e01-5f965c00bb67", - "name": "Spawner (225)", - "location": [611, 2157, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Farmer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8de04b9c-7762-426e-b7c6-79b531837288", - "name": "Spawner (225)", - "location": [586, 2170, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8e62476e-5f70-49e7-ae16-f8ed9ddd4c53", - "name": "Spawner (225)", - "location": [4394, 1083, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8ed2be48-c634-4ef3-82a4-c335d9b2e8f4", - "name": "Spawner (225)", - "location": [1475, 3865, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8f7e0a08-6898-4aaa-b655-e58bb3c88042", - "name": "Spawner (225)", - "location": [4545, 860, 30], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8fb2794d-96c6-4293-bfd6-4a24e772dbd2", - "name": "Spawner (225)", - "location": [4392, 1117, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "902148e8-f197-478a-95e5-25e0f3227606", - "name": "Spawner (225)", - "location": [2461, 457, 15], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9027faa4-bbd1-4feb-8ba7-59f0c4b9c0be", - "name": "Spawner (225)", - "location": [2424, 555, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bard", "maxCount": 1, "probability": 100 }, - { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "912580fa-084c-4ad0-ae90-e3dec3330cad", - "name": "Spawner (225)", - "location": [3628, 2541, 0], + "type": "Spawner", + "guid": "143a413e-2a69-4e60-a5b2-892becd26dac", + "name": "Spawner (2225)", + "location": [1485, 1550, 30], "map": "Felucca", "count": 3, "minDelay": "00:05:00", @@ -3057,81 +135,10 @@ ] }, { - "$type": "Spawner", - "guid": "9240f638-d05a-4e70-93bc-8be1c163e508", - "name": "Spawner (225)", - "location": [1427, 1716, 20], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, - { "name": "Waiter", "maxCount": 1, "probability": 100 }, - { "name": "Cook", "maxCount": 1, "probability": 100 }, - { "name": "Barkeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95195876-4c1c-422a-962e-31ce7ea8e3f8", - "name": "Spawner (225)", - "location": [2503, 552, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95936f14-8718-4f24-9236-d5e92d4fcc41", - "name": "Spawner (225)", - "location": [1846, 2715, 10], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95b1c31c-4fb0-46b2-8b18-8d4a8f7aaeb7", - "name": "Spawner (225)", - "location": [5805, 3300, 11], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 }, - { "name": "Armorer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "96956b69-4920-400c-8ca9-6effd0202fa5", - "name": "Spawner (225)", - "location": [5161, 32, 17], + "type": "Spawner", + "guid": "ced5124a-83f8-4d38-926b-ba8372a19275", + "name": "Spawner (2225)", + "location": [1296, 1759, 10], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -3139,15 +146,13 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "9808a90b-2ba1-4951-a011-a5ca40a24c8b", - "name": "Spawner (225)", - "location": [3632, 2595, 0], + "type": "Spawner", + "guid": "87de110f-213d-4275-b7f0-82e7991742ff", + "name": "Spawner (2225)", + "location": [1481, 1584, 20], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -3161,45 +166,10 @@ ] }, { - "$type": "Spawner", - "guid": "98952a90-a316-4809-a85f-d5840e2f6716", - "name": "Spawner (225)", - "location": [5300, 90, 15], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98d30ebe-3d65-4427-a8cb-84ce8b3a9f27", - "name": "Spawner (225)", - "location": [1895, 2653, 10], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "992f2215-28dd-466b-aa19-25b5d0089c1a", - "name": "Spawner (225)", - "location": [3612, 2466, 0], + "type": "Spawner", + "guid": "e59444cb-2d90-4137-85bc-e4e15e18e6a9", + "name": "Spawner (2225)", + "location": [1493, 1616, 20], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -3207,15 +177,13 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "997c3a7c-c590-4bd7-a2a7-f3b839ad0fd6", - "name": "Spawner (225)", - "location": [2505, 432, 15], + "type": "Spawner", + "guid": "0f3b0c94-b2e2-4c22-99d5-c559758a9043", + "name": "Spawner (2225)", + "location": [1422, 1654, 10], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -3223,47 +191,12 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "99dbe037-b50c-43b1-b490-94940fbe4cf9", - "name": "Spawner (225)", - "location": [3695, 2511, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9a8fd99e-04b8-4dba-9350-410c0db296b0", - "name": "Spawner (225)", - "location": [2455, 487, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "9ac62908-35f4-43c6-a40a-dba3a693666f", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [1547, 1768, 10], "map": "Felucca", "count": 4, @@ -3280,10 +213,10 @@ ] }, { - "$type": "Spawner", - "guid": "9acf583a-2654-4f9c-8be4-11f121aa97ab", - "name": "Spawner (225)", - "location": [5234, 4025, 37], + "type": "Spawner", + "guid": "7ff0fc47-17c9-4073-a0e0-d1c7520cf9bb", + "name": "Spawner (2225)", + "location": [1341, 1733, 20], "map": "Felucca", "count": 3, "minDelay": "00:05:00", @@ -3292,16 +225,15 @@ "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "hirefighter", "maxCount": 2, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "9b84b5ea-61db-496c-9804-f90ecae123bc", - "name": "Spawner (225)", - "location": [1848, 2680, 0], + "type": "Spawner", + "guid": "4381baac-5e70-48a1-bacb-99ff2a41663a", + "name": "Spawner (2225)", + "location": [1585, 1591, 20], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -3309,83 +241,47 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "9d559c33-8b55-426e-8ebc-e767487c32f0", - "name": "Spawner (225)", - "location": [4416, 1085, 0], + "type": "Spawner", + "guid": "afe473da-540a-477d-ad3b-233b8e7e4a89", + "name": "Spawner (2225)", + "location": [1637, 1693, 37], "map": "Felucca", - "count": 3, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "9dbabd5d-fd49-490f-86f1-e07e76420332", - "name": "Spawner (225)", - "location": [5296, 3978, 37], + "type": "Spawner", + "guid": "10283e6b-f2de-47b8-b6c8-21ee93bbbaf7", + "name": "Spawner (2225)", + "location": [1431, 1612, 20], "map": "Felucca", - "count": 3, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "9e7b7d17-aaa1-45c9-85f5-48f0e5c7f6cc", - "name": "Spawner (225)", - "location": [2019, 2748, 30], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9f79dae4-98e1-489e-9f04-b11d20f34897", - "name": "Spawner (225)", - "location": [5218, 175, 5], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a027a96b-cea5-4bfb-b048-2d7fcda9edd7", - "name": "Spawner (225)", - "location": [2901, 908, 0], + "type": "Spawner", + "guid": "347d6ccf-8681-4dc3-b6be-0b0edecb9d03", + "name": "Spawner (2225)", + "location": [1498, 1691, 20], "map": "Felucca", "count": 4, "minDelay": "00:05:00", @@ -3401,10 +297,136 @@ ] }, { - "$type": "Spawner", - "guid": "a14b366d-e223-4ef7-9715-298df09962b1", - "name": "Spawner (225)", - "location": [2992, 844, 0], + "type": "Spawner", + "guid": "e6abb0c5-a18d-4f82-bcf8-77c5d00edf24", + "name": "Spawner (2225)", + "location": [1470, 1765, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "546c63ee-9eca-4c1c-8405-108d7eb1df12", + "name": "Spawner (2225)", + "location": [1409, 1590, 42], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "42f7a139-1c12-457b-bfd7-5f50fc33a57e", + "name": "Spawner (2225)", + "location": [1349, 1778, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Blacksmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f29e3b5a-eb24-477a-adb1-3d1c7cfda113", + "name": "Spawner (2225)", + "location": [1469, 1668, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22117025-4e5b-4bc6-a65c-3803cea02917", + "name": "Spawner (2225)", + "location": [1510, 1543, 25], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9240f638-d05a-4e70-93bc-8be1c163e508", + "name": "Spawner (2225)", + "location": [1427, 1716, 20], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81f5c4b0-e089-4a51-b311-56a700b2845d", + "name": "Spawner (2225)", + "location": [1449, 1723, 6], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "59739d59-4955-4749-b91e-0dc56587d9d4", + "name": "Spawner (2225)", + "location": [1650, 1608, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "06054f54-b0fa-4f88-9306-92972f4dcca4", + "name": "Spawner (2225)", + "location": [1498, 1659, 27], "map": "Felucca", "count": 3, "minDelay": "00:05:00", @@ -3419,10 +441,10 @@ ] }, { - "$type": "Spawner", - "guid": "a1b458a3-908e-42c6-8e88-9f9a013f098c", - "name": "Spawner (225)", - "location": [5263, 129, 20], + "type": "Spawner", + "guid": "d3757ec1-febf-4562-9ae6-62181a418b8a", + "name": "Spawner (2225)", + "location": [1425, 1690, 0], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -3431,84 +453,57 @@ "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "a28db63a-c3b0-44e8-a446-7d33f63c081e", - "name": "Spawner (225)", - "location": [1354, 3754, 0], + "type": "Spawner", + "guid": "3535310b-bd5f-4938-92a8-07969bc0cdf5", + "name": "Spawner (2225)", + "location": [1494, 1715, 6], "map": "Felucca", - "count": 2, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "a3d9a50a-d415-4249-a6e0-c4c6cbe4d6de", - "name": "Spawner (225)", - "location": [3687, 2227, 20], + "type": "Spawner", + "guid": "adb68e3c-d5a4-45f0-9f22-afa40bd604c6", + "name": "Spawner (2225)", + "location": [1650, 1642, 0], "map": "Felucca", - "count": 2, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "a4163176-dab9-4a56-83ea-e97cd802579e", - "name": "Spawner (225)", - "location": [5203, 86, 5], + "type": "Spawner", + "guid": "82487046-2e78-49ae-ba4b-0a79b8247eb5", + "name": "Spawner (2225)", + "location": [1450, 1617, 20], "map": "Felucca", - "count": 3, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "a48c8b3d-d87d-4036-846f-e54593690566", - "name": "Spawner (225)", - "location": [1362, 1574, 30], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a4e55c2b-7487-4425-b114-f517851ddaab", - "name": "Spawner (225)", - "location": [3554, 1202, 0], + "type": "Spawner", + "guid": "098512b2-9b9c-4dd1-95aa-631d5c79c220", + "name": "Spawner (2225)", + "location": [1418, 1547, 30], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -3522,10 +517,10 @@ ] }, { - "$type": "Spawner", - "guid": "a76b7074-3933-477d-849a-99833d78d89a", - "name": "Spawner (225)", - "location": [5219, 4012, 37], + "type": "Spawner", + "guid": "f769d5d8-1f95-4671-be31-0082fd16c61c", + "name": "Spawner (2225)", + "location": [1471, 1611, 20], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -3534,66 +529,31 @@ "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "a9450d4e-81d6-4963-9afd-fd2da7c143eb", - "name": "Spawner (225)", - "location": [5752, 3272, 16], + "type": "Spawner", + "guid": "bd54442b-2a68-421a-b113-f38a66849e77", + "name": "Spawner (2225)", + "location": [1447, 1647, 10], "map": "Felucca", - "count": 3, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "Tailor", "maxCount": 1, "probability": 100 } + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ab871081-1d9a-4195-b251-88bd12201f99", - "name": "Spawner (225)", - "location": [1504, 3693, -3], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ab9d83de-4639-45e3-be08-04a7536fc2be", - "name": "Spawner (225)", - "location": [2883, 3502, 10], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "ac519e78-a9e7-4c70-b9e2-ec7ed9d6fbf7", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [1590, 1654, 10], "map": "Felucca", "count": 3, @@ -3609,10 +569,10 @@ ] }, { - "$type": "Spawner", - "guid": "acbb59a0-32ac-4add-9b8b-479cf4be15a3", - "name": "Spawner (225)", - "location": [5275, 3977, 37], + "type": "Spawner", + "guid": "2eac5045-fa01-46a1-aab1-19d09c2bd246", + "name": "Spawner (2225)", + "location": [1455, 1557, 30], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -3621,583 +581,15 @@ "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } + { "name": "Bard", "maxCount": 1, "probability": 100 }, + { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ad38d744-523e-425d-995c-6e954c9dbcc8", - "name": "Spawner (225)", - "location": [2917, 798, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ad85e0e4-4493-4c09-8fa7-61e108db38ab", - "name": "Spawner (225)", - "location": [2752, 2155, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "adb68e3c-d5a4-45f0-9f22-afa40bd604c6", - "name": "Spawner (225)", - "location": [1650, 1642, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "add1a992-7c4e-419d-9342-29642aac96af", - "name": "Spawner (225)", - "location": [5297, 4007, 40], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aedc8ecd-8443-4c79-a7ab-05da8844c493", - "name": "Spawner (225)", - "location": [540, 966, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "af2d2a82-247d-4cba-a657-6fb4e2b798f4", - "name": "Spawner (225)", - "location": [656, 2235, -3], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "af98980d-fb5a-44a9-961a-b32c6295132d", - "name": "Spawner (225)", - "location": [4417, 1064, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "afe473da-540a-477d-ad3b-233b8e7e4a89", - "name": "Spawner (225)", - "location": [1637, 1693, 37], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b060374a-df96-4f0b-a861-d2f50c651018", - "name": "Spawner (225)", - "location": [1436, 1752, 14], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "HarborMaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b1a005b4-d2a1-4b4d-a4e9-ca5adfda51ac", - "name": "Spawner (225)", - "location": [2940, 3500, 10], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b1af2e4f-16db-41d7-a4ae-fcce5350d51f", - "name": "Spawner (225)", - "location": [2865, 852, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b201992b-e843-4249-82a4-2122cc01f090", - "name": "Spawner (225)", - "location": [5736, 3263, 15], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Cobbler", "maxCount": 1, "probability": 100 }, - { "name": "Provisioner", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b37905b7-bed7-4376-bead-f9b7bdba21c7", - "name": "Spawner (225)", - "location": [1450, 4026, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4d83000-0cd1-4bca-bcfd-4f5c3e7fa942", - "name": "Spawner (225)", - "location": [1458, 1525, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b4ea855b-be4f-46f8-9b3a-6bac5d6a1c5a", - "name": "Spawner (225)", - "location": [5691, 3209, 11], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 }, - { "name": "Architect", "maxCount": 1, "probability": 100 }, - { "name": "Carpenter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b5a5d0c9-fa97-4709-9804-097f6bb733ce", - "name": "Spawner (225)", - "location": [3636, 2565, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9ebd4f7-d90e-4664-be98-a53c91926c79", - "name": "Spawner (225)", - "location": [1231, 1714, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 10, - "entries": [ - { "name": "hirepeasant", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba190817-c467-4702-83f4-f13272bd748a", - "name": "Spawner (225)", - "location": [5148, 60, 25], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bacf79a8-939c-416a-b7f3-56f5c4b4f247", - "name": "Spawner (225)", - "location": [601, 2235, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bbe4e681-6b3d-4ecd-912c-d3fca9eba8d2", - "name": "Spawner (225)", - "location": [2577, 599, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bcc5e683-0c16-470d-ac15-e9a597e0e1b6", - "name": "Spawner (225)", - "location": [4392, 1068, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd54442b-2a68-421a-b113-f38a66849e77", - "name": "Spawner (225)", - "location": [1447, 1647, 10], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bdb81c0f-d54b-4054-91a3-485614472f6f", - "name": "Spawner (225)", - "location": [3610, 2577, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bea16b73-7772-41e1-868e-7a6de8007a93", - "name": "Spawner (225)", - "location": [5154, 97, 5], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bedd9806-6e44-4d6b-98e2-e776ed3fb16b", - "name": "Spawner (225)", - "location": [3007, 3408, 15], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf5b64a0-83e5-4959-851a-990e7173c143", - "name": "Spawner (225)", - "location": [1441, 3719, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0024932-683f-457b-b4ca-fe0e60d8b42c", - "name": "Spawner (225)", - "location": [5805, 3270, 11], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mapmaker", "maxCount": 1, "probability": 100 }, - { "name": "Shipwright", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c0166342-30ea-4fb2-b95d-387c4a5ebc39", - "name": "Spawner (225)", - "location": [3012, 780, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 }, - { "name": "Farmer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c02625b1-ced5-4100-8c61-2820fbcaf129", - "name": "Spawner (225)", - "location": [1911, 2805, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c1a128fa-7b72-403f-9bb3-14a4f26d2c99", - "name": "Spawner (225)", - "location": [1351, 1816, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 1, - "walkingRange": 1, - "entries": [ - { "name": "EscortableMage", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c2efce74-1331-43af-9713-6b1c75fb6225", - "name": "Spawner (225)", - "location": [570, 969, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c351eb79-682e-4e9c-a193-0e5434f44634", - "name": "Spawner (225)", - "location": [2998, 760, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c55470a0-bfa8-4290-8ec0-c0c642e251e0", - "name": "Spawner (225)", - "location": [1243, 1699, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "Farmer", "maxCount": 1, "probability": 100 }, - { "name": "Rancher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c55b75f2-c734-40c2-b4a7-43a5218bf9e5", - "name": "Spawner (225)", - "location": [3003, 3355, 15], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Mage", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c688221a-a1d6-4c2c-80e2-5d1bd06c6c81", - "name": "Spawner (225)", - "location": [3666, 2235, 20], + "type": "Spawner", + "guid": "3b4c2e06-e89a-484e-9afe-d1c285a46db6", + "name": "Spawner (2225)", + "location": [1467, 1686, 0], "map": "Felucca", "count": 3, "minDelay": "00:05:00", @@ -4212,9 +604,51 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "ec68d28f-8601-4346-a801-0d1ff604d008", + "name": "Spawner (2225)", + "location": [1474, 1597, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cab9dba3-559b-4ff0-9d85-2b134c813ca5", + "name": "Spawner (2225)", + "location": [1417, 1578, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4d83000-0cd1-4bca-bcfd-4f5c3e7fa942", + "name": "Spawner (2225)", + "location": [1458, 1525, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "c778c01a-96fe-49fe-af2c-6788ccfb36d0", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [1416, 1754, 10], "map": "Felucca", "count": 2, @@ -4229,10 +663,330 @@ ] }, { - "$type": "Spawner", - "guid": "c8e0eaa2-df1d-478d-9ae1-85bc28647027", - "name": "Spawner (225)", - "location": [2855, 734, 0], + "type": "Spawner", + "guid": "50c3d9c7-0a08-46b4-af3c-9ef791c012ee", + "name": "Spawner (2225)", + "location": [1405, 1810, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Mage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eef4c3f5-0a7b-49da-a496-5f20882af7ad", + "name": "Spawner (2225)", + "location": [1482, 1761, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "298aca7b-3047-4808-8b9e-e972a0bc4b08", + "name": "Spawner (2225)", + "location": [1228, 1572, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9b34f6d-b58d-4106-85d0-c38fa2cd608d", + "name": "Spawner (2225)", + "location": [1355, 1754, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 14, + "entries": [ + { "name": "OrderGuard", "maxCount": 1, "probability": 100 }, + { "name": "hirefighter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c55470a0-bfa8-4290-8ec0-c0c642e251e0", + "name": "Spawner (2225)", + "location": [1243, 1699, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1a128fa-7b72-403f-9bb3-14a4f26d2c99", + "name": "Spawner (2225)", + "location": [1351, 1816, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "EscortableMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fd5e6859-407b-4ec2-bee4-88f1b88f0fbb", + "name": "Spawner (2225)", + "location": [1306, 1804, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 6, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3327ba9f-5ab2-4203-81c3-10ed191fe8a8", + "name": "Spawner (2225)", + "location": [1304, 1826, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "Peasant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "56c75ca9-bdbc-4709-bfae-6bb56308f2da", + "name": "Spawner (2225)", + "location": [1362, 1825, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 12, + "entries": [{ "name": "Artist", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f8fb6522-0e64-4dd6-8ef2-d6d5c9dcc3b5", + "name": "Spawner (2225)", + "location": [1333, 1808, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "escortablewanderinghealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2ffb4f73-598f-47b4-b4e9-e40c7a7ab424", + "name": "Spawner (2225)", + "location": [1150, 1619, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59b02f45-35c8-4b36-a55b-431fbb8d3254", + "name": "Spawner (2225)", + "location": [1149, 1547, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "67acd17b-db93-4bb1-9f67-f4c9015a1888", + "name": "Spawner (2225)", + "location": [1295, 1773, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "BrideGroom", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "456fbee0-848e-4ea7-b528-a7124166beb7", + "name": "Spawner (2225)", + "location": [1316, 1747, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "hirefighter", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b9ebd4f7-d90e-4664-be98-a53c91926c79", + "name": "Spawner (2225)", + "location": [1231, 1714, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "hirepeasant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8018ee5a-2f60-4dad-8ac7-8678c56000ac", + "name": "Spawner (2225)", + "location": [1190, 1707, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 8, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "35225c44-2e25-43d6-a2f3-82ef04806cbd", + "name": "Spawner (2225)", + "location": [1427, 1726, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "hirebard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d6a16c9-b172-40da-9573-4c979fa74ab3", + "name": "Spawner (2225)", + "location": [1438, 1761, -2], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [ + { "name": "hiresailor", "maxCount": 1, "probability": 100 }, + { "name": "Fisherman", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "667b1e85-dea9-4c92-a796-9c8fe4958ac0", + "name": "Spawner (2225)", + "location": [1477, 1741, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "hiresailor", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b060374a-df96-4f0b-a861-d2f50c651018", + "name": "Spawner (2225)", + "location": [1436, 1752, 14], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "HarborMaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2dc8250b-995a-4eae-a615-2c7b0037fd64", + "name": "Spawner (2225)", + "location": [1434, 1717, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Merchant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1002eb76-82f2-4dc6-863b-8c1720c1294c", + "name": "Spawner (2225)", + "location": [1388, 1655, 30], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -4240,47 +994,13 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ca51a670-1297-4de7-b01a-ed82119ceb90", - "name": "Spawner (225)", - "location": [554, 2178, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cab9dba3-559b-4ff0-9d85-2b134c813ca5", - "name": "Spawner (225)", - "location": [1417, 1578, 30], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "caff4a2a-380d-4672-b915-74bd60aab37e", - "name": "Spawner (225)", - "location": [3546, 1185, 0], + "type": "Spawner", + "guid": "a48c8b3d-d87d-4036-846f-e54593690566", + "name": "Spawner (2225)", + "location": [1362, 1574, 30], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -4289,48 +1009,60 @@ "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "cc0b834c-14d8-4b2d-937a-fc4dcf6d6220", - "name": "Spawner (225)", - "location": [1991, 2867, 20], + "type": "Spawner", + "guid": "8972d5d6-a75e-4dcb-85f4-565dd1a644e6", + "name": "Spawner (2225)", + "location": [1388, 1588, 30], "map": "Felucca", - "count": 2, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "cc4349ab-4016-43e9-8f41-f09733d7c0d4", - "name": "Spawner (225)", - "location": [1850, 2796, -8], + "type": "Spawner", + "guid": "14284f7f-ef5e-4dff-a0aa-bf84616d15a8", + "name": "Spawner (2225)", + "location": [2715, 2098, 0], "map": "Felucca", - "count": 2, + "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "44c610bd-fae7-481c-9174-9a3e34068786", + "name": "Spawner (2225)", + "location": [2659, 2194, 4], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "ThiefGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "cc8af24a-c3f4-4029-b238-a76b951d104e", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [2680, 2238, 2], "map": "Felucca", "count": 4, @@ -4347,9 +1079,447 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "ce01eeba-f0ab-459c-8c0c-f998f5caaf30", + "name": "Spawner (2225)", + "location": [2737, 2250, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13a55ac1-98b8-4c8e-ae53-5408dc9aa8bb", + "name": "Spawner (2225)", + "location": [2731, 2192, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d74a89d-c5fa-49d6-8a1b-a52aa1484f5e", + "name": "Spawner (2225)", + "location": [2627, 2100, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad85e0e4-4493-4c09-8fa7-61e108db38ab", + "name": "Spawner (2225)", + "location": [2752, 2155, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "37ea3966-1157-4d85-a26e-1a9f3008dd75", + "name": "Spawner (2225)", + "location": [2707, 2178, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75b0732c-4590-4031-88c0-2ad4750e7445", + "name": "Spawner (2225)", + "location": [2634, 2083, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc9225d4-8e01-4c04-ac86-0de58d5e6efc", + "name": "Spawner (2225)", + "location": [2709, 2130, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0834f2e-139b-4b00-b68a-b46b66301b5c", + "name": "Spawner (2225)", + "location": [2216, 1167, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "25c1334d-8fb1-442a-9315-f549ed3c5254", + "name": "Spawner (2225)", + "location": [2256, 1181, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1aa3d9a8-9ace-49cb-969e-6a4350b5ba08", + "name": "Spawner (2225)", + "location": [2216, 1192, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62ab8dc9-0f3e-411a-aba1-d2449a7d882c", + "name": "Spawner (2225)", + "location": [2245, 1231, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f865ca2-1ac4-4612-9079-18dd99f2f42d", + "name": "Spawner (2225)", + "location": [1512, 3989, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab871081-1d9a-4195-b251-88bd12201f99", + "name": "Spawner (2225)", + "location": [1504, 3693, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ef4209ee-affa-476d-a190-9121f8cd3e18", + "name": "Spawner (2225)", + "location": [1125, 3687, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d9ab6c79-9b10-49f4-ae60-78022c502dff", + "name": "Spawner (2225)", + "location": [1447, 3981, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "51c50c2c-1ad2-4b38-bfd3-e3680b82624e", + "name": "Spawner (2225)", + "location": [1452, 3747, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf5b64a0-83e5-4959-851a-990e7173c143", + "name": "Spawner (2225)", + "location": [1441, 3719, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3b6c12ad-854f-409f-a281-f04dce59330f", + "name": "Spawner (2225)", + "location": [1419, 3859, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f32b1627-e330-440e-b8da-982a8b308935", + "name": "Spawner (2225)", + "location": [1356, 3780, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8521a0ed-ffd6-430e-9cd6-b1e9dc43713f", + "name": "Spawner (2225)", + "location": [1364, 3732, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8ed2be48-c634-4ef3-82a4-c335d9b2e8f4", + "name": "Spawner (2225)", + "location": [1475, 3865, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a28db63a-c3b0-44e8-a446-7d33f63c081e", + "name": "Spawner (2225)", + "location": [1354, 3754, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6602517d-493c-41c3-8bfb-2738aa2a2dcd", + "name": "Spawner (2225)", + "location": [1386, 3825, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b37905b7-bed7-4376-bead-f9b7bdba21c7", + "name": "Spawner (2225)", + "location": [1450, 4026, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2dd708a7-fa32-46ad-a558-56d4464a6442", + "name": "Spawner (2225)", + "location": [1395, 3705, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3825dd92-a301-4bf0-9d73-c29fdc1b0043", + "name": "Spawner (2225)", + "location": [1435, 3820, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5037c29-a88d-4d7c-bfd0-7a98d12e3b81", + "name": "Spawner (2225)", + "location": [1404, 3802, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "cd42cc6c-c177-4292-b22f-8e150bc833d7", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [1452, 3770, 0], "map": "Felucca", "count": 4, @@ -4366,10 +1536,10 @@ ] }, { - "$type": "Spawner", - "guid": "ce01eeba-f0ab-459c-8c0c-f998f5caaf30", - "name": "Spawner (225)", - "location": [2737, 2250, 0], + "type": "Spawner", + "guid": "053c4263-9e79-4067-ab30-8576df3e9b00", + "name": "Spawner (2225)", + "location": [1456, 3850, 0], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -4378,46 +1548,14 @@ "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ced5124a-83f8-4d38-926b-ba8372a19275", - "name": "Spawner (225)", - "location": [1296, 1759, 10], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cf06d28e-ba5f-47dd-85ad-f49dddb548ef", - "name": "Spawner (225)", - "location": [4406, 1038, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "cfc4ea82-efcb-455c-b01a-482e43313f65", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [1317, 3773, 0], "map": "Felucca", "count": 2, @@ -4432,27 +1570,10 @@ ] }, { - "$type": "Spawner", - "guid": "cfcb2873-b661-44a8-b500-8cab42fefcf1", - "name": "Spawner (225)", - "location": [2880, 3472, 15], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d14b9830-d3cc-4717-af11-51f9bdd77aef", - "name": "Spawner (225)", - "location": [3545, 1178, 0], + "type": "Spawner", + "guid": "03bc089d-0286-40d7-a5e1-dc425e0c8f53", + "name": "Spawner (2225)", + "location": [1372, 3908, -2], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -4460,416 +1581,12 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "d19e8b0e-5ee0-4e21-b092-b4f5de33cb69", - "name": "Spawner (225)", - "location": [3683, 2252, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3757ec1-febf-4562-9ae6-62181a418b8a", - "name": "Spawner (225)", - "location": [1425, 1690, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3f60ee0-06c4-4e53-81d8-0abbbc0383a3", - "name": "Spawner (225)", - "location": [3034, 827, -3], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d66334a0-15b9-4362-8afc-0bb44e1a9f1c", - "name": "Spawner (225)", - "location": [1844, 2735, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d6634bfd-e77d-4595-b836-4ef3ff685e5a", - "name": "Spawner (225)", - "location": [2522, 524, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d673deb5-d4ef-4f50-8b5b-9b55a00bd7b3", - "name": "Spawner (225)", - "location": [1360, 3815, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d75fe7bf-4215-402e-9d5c-24bd7a8801bb", - "name": "Spawner (225)", - "location": [535, 867, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d8996375-dc5d-4fd9-a1bf-9931c0a1fe58", - "name": "Spawner (225)", - "location": [570, 2121, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9ab6c79-9b10-49f4-ae60-78022c502dff", - "name": "Spawner (225)", - "location": [1447, 3981, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Jeweler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9af91c7-daae-47de-958d-0169069fabde", - "name": "Spawner (225)", - "location": [2881, 684, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d9bfb07a-33e5-41c1-8e89-40a429ef8d7c", - "name": "Spawner (225)", - "location": [2967, 3408, 15], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "da1bd33e-e6d5-4311-8177-9443fe6c1c63", - "name": "Spawner (225)", - "location": [1685, 2985, 0], - "map": "Felucca", - "count": 4, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 1675, "y": 2975, "z": 0 }, - "end": { "x": 1695, "y": 2995, "z": 35 } - }, - "walkingRange": 20, - "entries": [ - { "name": "Herbalist", "maxCount": 2, "probability": 100 }, - { "name": "Mage", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dab5532c-bfaa-4e06-b2e9-9181a087402a", - "name": "Spawner (225)", - "location": [650, 2161, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dbea519d-d3d7-4287-9bd2-3231430294d8", - "name": "Spawner (225)", - "location": [2899, 790, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dc9225d4-8e01-4c04-ac86-0de58d5e6efc", - "name": "Spawner (225)", - "location": [2709, 2130, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd2e9db5-e5b2-4195-9587-76654566de99", - "name": "Spawner (225)", - "location": [2986, 637, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Provisioner", "maxCount": 1, "probability": 100 }, - { "name": "Cobbler", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "de27e5cc-f1d9-45b2-ab09-e09b520dcd28", - "name": "Spawner (225)", - "location": [3683, 2171, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dea7fe8f-3b6f-4679-9650-4cee301d7b71", - "name": "Spawner (225)", - "location": [4416, 1133, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Herbalist", "maxCount": 1, "probability": 100 }, - { "name": "Alchemist", "maxCount": 1, "probability": 100 }, - { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df027fa7-1a26-4c66-a7da-13e67012c84f", - "name": "Spawner (225)", - "location": [2011, 2804, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "AnimalTrainer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e0834f2e-139b-4b00-b68a-b46b66301b5c", - "name": "Spawner (225)", - "location": [2216, 1167, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Armorer", "maxCount": 1, "probability": 100 }, - { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e16bd6c9-e916-4616-9273-c5c3d1ab0115", - "name": "Spawner (225)", - "location": [631, 1034, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e1fc3ffc-4245-429f-a3cb-7e3d46a150bc", - "name": "Spawner (225)", - "location": [4458, 1060, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e363885b-3519-4ff2-b737-f7da5a04aab4", - "name": "Spawner (225)", - "location": [3546, 1194, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e4959103-c32c-4f31-8668-3d02188253eb", - "name": "Spawner (225)", - "location": [582, 2186, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "e51c1b00-e54a-4e00-837e-e0d86f596722", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [1416, 3779, 0], "map": "Felucca", "count": 2, @@ -4884,27 +1601,10 @@ ] }, { - "$type": "Spawner", - "guid": "e558f44a-c896-4b7a-ac0e-d45a0c1672b3", - "name": "Spawner (225)", - "location": [3602, 2615, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tanner", "maxCount": 1, "probability": 100 }, - { "name": "Furtrader", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e59444cb-2d90-4137-85bc-e4e15e18e6a9", - "name": "Spawner (225)", - "location": [1493, 1616, 20], + "type": "Spawner", + "guid": "6bae0089-ee4c-4961-9b8b-a04cb40f3fff", + "name": "Spawner (2225)", + "location": [1387, 3771, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -4912,32 +1612,49 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e65f0cdb-3a59-43a5-9e14-6088e448dd92", - "name": "Spawner (225)", - "location": [2471, 564, 5], + "type": "Spawner", + "guid": "4427c2d2-613b-4859-a8e0-c14df5bd0f52", + "name": "Spawner (2225)", + "location": [1425, 3981, 0], "map": "Felucca", - "count": 2, + "count": 3, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, - { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "e6689d71-5784-4c9c-a0ed-90bcdde2eb2b", - "name": "Spawner (225)", - "location": [5698, 3282, 15], + "type": "Spawner", + "guid": "69144e2b-49b0-4027-ad34-f12811dae9a9", + "name": "Spawner (2225)", + "location": [1454, 4003, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d673deb5-d4ef-4f50-8b5b-9b55a00bd7b3", + "name": "Spawner (2225)", + "location": [1360, 3815, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -4945,63 +1662,12 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e677e3f4-5885-4dfe-baeb-ebe4650a94be", - "name": "Spawner (225)", - "location": [2992, 3451, 16], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e6abb0c5-a18d-4f82-bcf8-77c5d00edf24", - "name": "Spawner (225)", - "location": [1470, 1765, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e9b34f6d-b58d-4106-85d0-c38fa2cd608d", - "name": "Spawner (225)", - "location": [1355, 1754, 20], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 8, - "walkingRange": 14, - "entries": [ - { "name": "OrderGuard", "maxCount": 1, "probability": 100 }, - { "name": "hirefighter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "eb3be177-f027-491b-85d2-099a52c52d14", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [1442, 3802, 0], "map": "Felucca", "count": 2, @@ -5016,9 +1682,162 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "3ef25d3d-9926-4bcb-b14e-b043e23982af", + "name": "Spawner (2225)", + "location": [1437, 3750, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "333fe588-e295-4247-b8c6-c0b492f161cc", + "name": "Spawner (2225)", + "location": [3699, 2163, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c688221a-a1d6-4c2c-80e2-5d1bd06c6c81", + "name": "Spawner (2225)", + "location": [3666, 2235, 20], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b3d26b3-c709-4ed0-bd77-a257a8ca84e2", + "name": "Spawner (2225)", + "location": [3720, 2126, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39db05f3-b04e-4ff7-911c-49a9cd081aa5", + "name": "Spawner (2225)", + "location": [3755, 2227, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5cc88170-da30-4eb5-a8c7-19e8f29caa86", + "name": "Spawner (2225)", + "location": [3734, 2149, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "de27e5cc-f1d9-45b2-ab09-e09b520dcd28", + "name": "Spawner (2225)", + "location": [3683, 2171, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "778b3fd0-9f0f-4e70-9923-917b2b268fd1", + "name": "Spawner (2225)", + "location": [3661, 2183, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d19e8b0e-5ee0-4e21-b092-b4f5de33cb69", + "name": "Spawner (2225)", + "location": [3683, 2252, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "390c60c0-4fd2-43b8-a9a2-5c194dc235a7", + "name": "Spawner (2225)", + "location": [3667, 2252, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "ebf29920-68ce-4aee-8a6d-84c0576c4c0d", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [3727, 2224, 20], "map": "Felucca", "count": 4, @@ -5035,42 +1854,10 @@ ] }, { - "$type": "Spawner", - "guid": "ec68d28f-8601-4346-a801-0d1ff604d008", - "name": "Spawner (225)", - "location": [1474, 1597, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec953969-18e0-4b71-949e-3b37f5a97c1d", - "name": "Spawner (225)", - "location": [3703, 2249, 20], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ed469ec1-25ae-4d8c-ab82-32d94428571c", - "name": "Spawner (225)", - "location": [3632, 2640, 0], + "type": "Spawner", + "guid": "a3d9a50a-d415-4249-a6e0-c4c6cbe4d6de", + "name": "Spawner (2225)", + "location": [3687, 2227, 20], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -5079,49 +1866,15 @@ "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "eef4c3f5-0a7b-49da-a496-5f20882af7ad", - "name": "Spawner (225)", - "location": [1482, 1761, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 4, - "entries": [ - { "name": "TownCrier", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ef0e9deb-1a5f-4e48-86db-18c1bb206530", - "name": "Spawner (225)", - "location": [2961, 621, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ef1db5ca-06dd-4a31-bfce-5bb4c75a1df8", - "name": "Spawner (225)", - "location": [5197, 4060, 37], + "type": "Spawner", + "guid": "19957abb-f841-4c9a-b52a-ac8c19cd0ab0", + "name": "Spawner (2225)", + "location": [3674, 2289, -2], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -5129,15 +1882,13 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ef25c059-a51f-4e6d-80f6-a39eb60c9c27", - "name": "Spawner (225)", - "location": [624, 1145, 0], + "type": "Spawner", + "guid": "4cc10b14-a086-4781-84ff-f656af24f44e", + "name": "Spawner (2225)", + "location": [3665, 2140, 20], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -5145,64 +1896,12 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "ef4209ee-affa-476d-a190-9121f8cd3e18", - "name": "Spawner (225)", - "location": [1125, 3687, -2], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f10e0fef-f74c-41c5-857f-00d429c69d60", - "name": "Spawner (225)", - "location": [2513, 477, 15], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Carpenter", "maxCount": 1, "probability": 100 }, - { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f19cc32a-4597-4070-8e43-f322ce4aa618", - "name": "Spawner (225)", - "location": [592, 2277, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f22df3f4-b9f0-4cf5-b444-c01f001f01bb", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [3699, 2218, 20], "map": "Felucca", "count": 3, @@ -5218,10 +1917,97 @@ ] }, { - "$type": "Spawner", - "guid": "f29e3b5a-eb24-477a-adb1-3d1c7cfda113", - "name": "Spawner (225)", - "location": [1469, 1668, 0], + "type": "Spawner", + "guid": "ec953969-18e0-4b71-949e-3b37f5a97c1d", + "name": "Spawner (2225)", + "location": [3703, 2249, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ff08692-d784-4f17-8291-8249aaf2dc6e", + "name": "Spawner (2225)", + "location": [2450, 428, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Provisioner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "453d3fec-1f00-4ae4-9633-5c71510d64f8", + "name": "Spawner (2225)", + "location": [2450, 428, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Cobbler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "05a68019-c07b-428f-8e99-90f5ace263b1", + "name": "Spawner (2225)", + "location": [2438, 410, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "16fdba4a-b4ce-4017-9f6d-6b535ae8350a", + "name": "Spawner (2225)", + "location": [2526, 375, 23], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d6634bfd-e77d-4595-b836-4ef3ff685e5a", + "name": "Spawner (2225)", + "location": [2522, 524, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6101131e-6247-4e30-b693-0c7a26b09cec", + "name": "Spawner (2225)", + "location": [2526, 546, 0], "map": "Felucca", "count": 2, "minDelay": "00:05:00", @@ -5235,45 +2021,10 @@ ] }, { - "$type": "Spawner", - "guid": "f32b1627-e330-440e-b8da-982a8b308935", - "name": "Spawner (225)", - "location": [1356, 3780, 0], - "map": "Felucca", - "count": 3, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tailor", "maxCount": 1, "probability": 100 }, - { "name": "Weaver", "maxCount": 1, "probability": 100 }, - { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f439eb85-35d6-487c-884f-a7a71716748f", - "name": "Spawner (225)", - "location": [652, 820, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Banker", "maxCount": 1, "probability": 100 }, - { "name": "Minter", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f4d6fd78-de3c-47bf-aa20-3e784327e814", - "name": "Spawner (225)", - "location": [3700, 1214, 0], + "type": "Spawner", + "guid": "5fec0502-6f90-4e48-b0ef-fd58b3c8ab5e", + "name": "Spawner (2225)", + "location": [2513, 477, 15], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -5281,65 +2032,12 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Shipwright", "maxCount": 1, "probability": 100 }, - { "name": "Mapmaker", "maxCount": 0, "probability": 100 } - ] + "entries": [{ "name": "Architect", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "f5037c29-a88d-4d7c-bfd0-7a98d12e3b81", - "name": "Spawner (225)", - "location": [1404, 3802, 0], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Tinker", "maxCount": 1, "probability": 100 }, - { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f769d5d8-1f95-4671-be31-0082fd16c61c", - "name": "Spawner (225)", - "location": [1471, 1611, 20], - "map": "Felucca", - "count": 2, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 0, - "walkingRange": 5, - "entries": [ - { "name": "Healer", "maxCount": 1, "probability": 100 }, - { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f8fb6522-0e64-4dd6-8ef2-d6d5c9dcc3b5", - "name": "Spawner (225)", - "location": [1333, 1808, 0], - "map": "Felucca", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 4, - "walkingRange": 4, - "entries": [ - { "name": "escortablewanderinghealer", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "f98a9a64-b87c-4f79-ad89-f1fbe0854cfc", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [2533, 572, 0], "map": "Felucca", "count": 2, @@ -5354,9 +2052,642 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "95195876-4c1c-422a-962e-31ce7ea8e3f8", + "name": "Spawner (2225)", + "location": [2503, 552, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "89aa4fc3-f834-4499-bbc6-ac47b395ce13", + "name": "Spawner (2225)", + "location": [2475, 397, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e65f0cdb-3a59-43a5-9e14-6088e448dd92", + "name": "Spawner (2225)", + "location": [2471, 564, 5], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "902148e8-f197-478a-95e5-25e0f3227606", + "name": "Spawner (2225)", + "location": [2461, 457, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "997c3a7c-c590-4bd7-a2a7-f3b839ad0fd6", + "name": "Spawner (2225)", + "location": [2505, 432, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bbe4e681-6b3d-4ecd-912c-d3fca9eba8d2", + "name": "Spawner (2225)", + "location": [2577, 599, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a8fd99e-04b8-4dba-9350-410c0db296b0", + "name": "Spawner (2225)", + "location": [2455, 487, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9027faa4-bbd1-4feb-8ba7-59f0c4b9c0be", + "name": "Spawner (2225)", + "location": [2424, 555, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Bard", "maxCount": 1, "probability": 100 }, + { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32955c0b-65f3-4a59-ad71-de6854ec16b9", + "name": "Spawner (2225)", + "location": [2478, 427, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f10e0fef-f74c-41c5-857f-00d429c69d60", + "name": "Spawner (2225)", + "location": [2513, 477, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1fc3ffc-4245-429f-a3cb-7e3d46a150bc", + "name": "Spawner (2225)", + "location": [4458, 1060, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af98980d-fb5a-44a9-961a-b32c6295132d", + "name": "Spawner (2225)", + "location": [4417, 1064, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04c6be93-3c01-460a-9035-cc4a78774b5c", + "name": "Spawner (2225)", + "location": [4401, 1165, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4493f83f-d73b-4691-b01e-cda4b8046621", + "name": "Spawner (2225)", + "location": [4440, 1162, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8f7e0a08-6898-4aaa-b655-e58bb3c88042", + "name": "Spawner (2225)", + "location": [4545, 860, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "27f0fb46-5d81-4688-8e10-7cdebe46fc32", + "name": "Spawner (2225)", + "location": [4481, 1085, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "51487317-ca09-48ac-a372-7acae8ca4ca4", + "name": "Spawner (2225)", + "location": [4395, 1137, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "43be54dd-c46c-4d7b-819e-a792890ff138", + "name": "Spawner (2225)", + "location": [4471, 1156, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dea7fe8f-3b6f-4679-9650-4cee301d7b71", + "name": "Spawner (2225)", + "location": [4416, 1133, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bcc5e683-0c16-470d-ac15-e9a597e0e1b6", + "name": "Spawner (2225)", + "location": [4392, 1068, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9d559c33-8b55-426e-8ebc-e767487c32f0", + "name": "Spawner (2225)", + "location": [4416, 1085, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8fb2794d-96c6-4293-bfd6-4a24e772dbd2", + "name": "Spawner (2225)", + "location": [4392, 1117, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cf06d28e-ba5f-47dd-85ad-f49dddb548ef", + "name": "Spawner (2225)", + "location": [4406, 1038, -2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e62476e-5f70-49e7-ae16-f8ed9ddd4c53", + "name": "Spawner (2225)", + "location": [4394, 1083, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c826160-cc14-4c24-91ac-8fac400a9d4c", + "name": "Spawner (2225)", + "location": [4409, 1111, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "039694fe-6a0b-41bf-a561-1391dfb2cb14", + "name": "Spawner (2225)", + "location": [4484, 1064, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "74bc4555-d00a-4214-bb20-9c37ddffa4d6", + "name": "Spawner (2225)", + "location": [4448, 1090, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a4e55c2b-7487-4425-b114-f517851ddaab", + "name": "Spawner (2225)", + "location": [3554, 1202, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "701c4bc8-b92a-4d2e-9f31-bb0e73157bc7", + "name": "Spawner (2225)", + "location": [3731, 1320, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f4d6fd78-de3c-47bf-aa20-3e784327e814", + "name": "Spawner (2225)", + "location": [3700, 1214, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 0, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1411c07d-db5c-45db-aef9-5ca1c7f9c769", + "name": "Spawner (2225)", + "location": [3729, 1258, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "134687d6-0adb-4dac-a522-1bf22a1c76c7", + "name": "Spawner (2225)", + "location": [3774, 1265, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4437c2af-dd86-4295-9b55-3d45746cd99a", + "name": "Spawner (2225)", + "location": [3545, 1178, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Tanner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "64afa061-436e-4816-acc8-b4b932a2f31f", + "name": "Spawner (2225)", + "location": [3764, 1317, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59707794-b18e-4101-9c64-f28940881270", + "name": "Spawner (2225)", + "location": [3740, 1197, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "BardGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "267e1e11-d6b0-44cb-a161-c8c3b962d1af", + "name": "Spawner (2225)", + "location": [3778, 1172, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8883ad81-f99d-4ccb-8d1d-65ada7848c65", + "name": "Spawner (2225)", + "location": [3769, 1218, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "caff4a2a-380d-4672-b915-74bd60aab37e", + "name": "Spawner (2225)", + "location": [3546, 1185, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e363885b-3519-4ff2-b737-f7da5a04aab4", + "name": "Spawner (2225)", + "location": [3546, 1194, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "faaed6d7-6878-44d7-a893-1c0d12dc9e0e", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [3555, 1171, 0], "map": "Felucca", "count": 1, @@ -5365,15 +2696,13 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "fc7a6537-745e-4573-8b1f-611e5de6b834", - "name": "Spawner (225)", - "location": [590, 2205, 0], + "type": "Spawner", + "guid": "d14b9830-d3cc-4717-af11-51f9bdd77aef", + "name": "Spawner (2225)", + "location": [3545, 1178, 0], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -5381,30 +2710,433 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Furtrader", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "fd5e6859-407b-4ec2-bee4-88f1b88f0fbb", - "name": "Spawner (225)", - "location": [1306, 1804, 0], + "type": "Spawner", + "guid": "46c5c4a0-48d4-4e36-88ae-cbf18533e56b", + "name": "Spawner (2225)", + "location": [3647, 2681, -2], "map": "Felucca", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "homeRange": 4, - "walkingRange": 6, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "992f2215-28dd-466b-aa19-25b5d0089c1a", + "name": "Spawner (2225)", + "location": [3612, 2466, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "912580fa-084c-4ad0-ae90-e3dec3330cad", + "name": "Spawner (2225)", + "location": [3628, 2541, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, "entries": [ - { "name": "OrderGuard", "maxCount": 1, "probability": 100 } + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "47601b12-bda9-4dab-92db-13ba9022ebaf", + "name": "Spawner (2225)", + "location": [3667, 2586, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56b03849-fb73-4883-8368-9f0a01df0403", + "name": "Spawner (2225)", + "location": [3708, 2649, 20], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "22666a8e-0c05-4ebd-b93e-92411a48d9c9", + "name": "Spawner (2225)", + "location": [3672, 2654, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed469ec1-25ae-4d8c-ab82-32d94428571c", + "name": "Spawner (2225)", + "location": [3632, 2640, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "99dbe037-b50c-43b1-b490-94940fbe4cf9", + "name": "Spawner (2225)", + "location": [3695, 2511, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7acbc3b8-8176-40ce-8d76-848b7ce1bedc", + "name": "Spawner (2225)", + "location": [3665, 2531, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "BardGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e558f44a-c896-4b7a-ac0e-d45a0c1672b3", + "name": "Spawner (2225)", + "location": [3602, 2615, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "279cfcf8-24e9-4390-a4e2-a507e02e89ef", + "name": "Spawner (2225)", + "location": [3673, 2614, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9808a90b-2ba1-4951-a011-a5ca40a24c8b", + "name": "Spawner (2225)", + "location": [3632, 2595, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "19fad4db-4751-4907-8561-8ef0a0d22959", + "name": "Spawner (2225)", + "location": [3629, 2608, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5a5d0c9-fa97-4709-9804-097f6bb733ce", + "name": "Spawner (2225)", + "location": [3636, 2565, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdb81c0f-d54b-4054-91a3-485614472f6f", + "name": "Spawner (2225)", + "location": [3610, 2577, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6f32b48f-e661-4e16-a5c6-1755a1c77e57", + "name": "Spawner (2225)", + "location": [2975, 3353, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4a3ad267-fe69-4133-8021-6a46265945e8", + "name": "Spawner (2225)", + "location": [2906, 3485, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "31475343-bb81-44f0-9ece-86234032a185", + "name": "Spawner (2225)", + "location": [3031, 3350, 35], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21ea5650-c7b3-48d0-b78b-1195290b17f8", + "name": "Spawner (2225)", + "location": [3018, 3426, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfcb2873-b661-44a8-b500-8cab42fefcf1", + "name": "Spawner (2225)", + "location": [2880, 3472, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "80a970ea-b163-4589-a626-c0fc6f875350", + "name": "Spawner (2225)", + "location": [2997, 3428, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41bac877-ca53-4a5f-aa2d-12de82f8b3f4", + "name": "Spawner (2225)", + "location": [3036, 3464, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c55b75f2-c734-40c2-b4a7-43a5218bf9e5", + "name": "Spawner (2225)", + "location": [3003, 3355, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61506934-e743-4d80-8c62-daf3fa9c6648", + "name": "Spawner (2225)", + "location": [2905, 3517, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "56c50bc0-b5de-45d5-b9ed-3da67c3efe73", + "name": "Spawner (2225)", + "location": [3051, 3367, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab9d83de-4639-45e3-be08-04a7536fc2be", + "name": "Spawner (2225)", + "location": [2883, 3502, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e677e3f4-5885-4dfe-baeb-ebe4650a94be", + "name": "Spawner (2225)", + "location": [2992, 3451, 16], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "fd956d04-c3b5-4033-99c9-d3ce4f16fbf6", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [3008, 3389, 15], "map": "Felucca", "count": 2, @@ -5419,9 +3151,311 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "b1a005b4-d2a1-4b4d-a4e9-ca5adfda51ac", + "name": "Spawner (2225)", + "location": [2940, 3500, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f707815-9566-4210-a8e6-51f5affda55b", + "name": "Spawner (2225)", + "location": [3058, 3400, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bedd9806-6e44-4d6b-98e2-e776ed3fb16b", + "name": "Spawner (2225)", + "location": [3007, 3408, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7cbf932d-ba9f-4a37-8ff3-068952fcbe6c", + "name": "Spawner (2225)", + "location": [3013, 3353, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9bfb07a-33e5-41c1-8e89-40a429ef8d7c", + "name": "Spawner (2225)", + "location": [2967, 3408, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3037b09f-4d00-4b90-b77f-4a9c77d446bd", + "name": "Spawner (2225)", + "location": [2972, 3425, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60ff9933-37fb-44e0-8794-2dfc53969609", + "name": "Spawner (2225)", + "location": [3009, 3455, 15], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bbc2f39-f8bf-4ff8-8cce-8128c08ae64b", + "name": "Spawner (2225)", + "location": [2940, 3410, 1], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "24029208-9dc1-44d6-829c-f60e05b7089e", + "name": "Spawner (2225)", + "location": [630, 2194, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b6efd78-0c41-4bd1-b51c-35e870f89a5c", + "name": "Spawner (2225)", + "location": [562, 2148, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "RangerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ca51a670-1297-4de7-b01a-ed82119ceb90", + "name": "Spawner (2225)", + "location": [554, 2178, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "058219b9-095c-409b-87b0-ffff99c4e0c9", + "name": "Spawner (2225)", + "location": [650, 2178, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af2d2a82-247d-4cba-a657-6fb4e2b798f4", + "name": "Spawner (2225)", + "location": [656, 2235, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "34a7b4b8-cdad-46e2-b1c7-5aebe2fa41d1", + "name": "Spawner (2225)", + "location": [578, 2227, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f19cc32a-4597-4070-8e43-f322ce4aa618", + "name": "Spawner (2225)", + "location": [592, 2277, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "017536ca-0a2b-45c1-90a2-e6279f1a2e79", + "name": "Spawner (2225)", + "location": [587, 2146, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8996375-dc5d-4fd9-a1bf-9931c0a1fe58", + "name": "Spawner (2225)", + "location": [570, 2121, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc7a6537-745e-4573-8b1f-611e5de6b834", + "name": "Spawner (2225)", + "location": [590, 2205, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e4959103-c32c-4f31-8668-3d02188253eb", + "name": "Spawner (2225)", + "location": [582, 2186, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "fe70d7f3-4e67-44ab-9be0-c714538b3db2", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [627, 2163, 0], "map": "Felucca", "count": 3, @@ -5437,9 +3471,1000 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "bacf79a8-939c-416a-b7f3-56f5c4b4f247", + "name": "Spawner (2225)", + "location": [601, 2235, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d40d06e-44aa-40b5-8e01-5f965c00bb67", + "name": "Spawner (2225)", + "location": [611, 2157, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dab5532c-bfaa-4e06-b2e9-9181a087402a", + "name": "Spawner (2225)", + "location": [650, 2161, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22214376-1f4d-4e5a-a3c5-0041c4302fa3", + "name": "Spawner (2225)", + "location": [620, 2218, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2be191ab-063f-49a5-8ecc-71c7b52e05b2", + "name": "Spawner (2225)", + "location": [602, 2180, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8de04b9c-7762-426e-b7c6-79b531837288", + "name": "Spawner (2225)", + "location": [586, 2170, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "757854f4-8a3a-48bf-a288-3cf416c80b0c", + "name": "Spawner (2225)", + "location": [659, 2141, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8be1dd4d-eda1-42c7-a107-8f523ec06814", + "name": "Spawner (2225)", + "location": [2002, 2721, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "02c8aec2-f3b8-4626-9127-c3685d861a3b", + "name": "Spawner (2225)", + "location": [1897, 2805, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2d2440ab-0e7c-4fa1-a078-688127e2feb5", + "name": "Spawner (2225)", + "location": [1852, 2831, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31cbfeeb-3e9c-440e-a424-2524ca287d1b", + "name": "Spawner (2225)", + "location": [2033, 2801, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98d30ebe-3d65-4427-a8cb-84ce8b3a9f27", + "name": "Spawner (2225)", + "location": [1895, 2653, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "064da568-3987-4ae8-90b0-592f6e49acd3", + "name": "Spawner (2225)", + "location": [2026, 2845, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b84b5ea-61db-496c-9804-f90ecae123bc", + "name": "Spawner (2225)", + "location": [1848, 2680, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "048d0dc3-9b09-4997-825b-e316da48d60f", + "name": "Spawner (2225)", + "location": [1990, 2889, 5], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4d903014-9897-4a01-8ca1-4dcddadaf629", + "name": "Spawner (2225)", + "location": [2072, 2856, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cc0b834c-14d8-4b2d-937a-fc4dcf6d6220", + "name": "Spawner (2225)", + "location": [1991, 2867, 20], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c02625b1-ced5-4100-8c61-2820fbcaf129", + "name": "Spawner (2225)", + "location": [1911, 2805, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d2fdc26-5ecb-44f8-a59f-a04aa21f7f29", + "name": "Spawner (2225)", + "location": [1897, 2684, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "825d89ed-d1f3-45e5-b7c0-0b86434ec759", + "name": "Spawner (2225)", + "location": [1822, 2739, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9e7b7d17-aaa1-45c9-85f5-48f0e5c7f6cc", + "name": "Spawner (2225)", + "location": [2019, 2748, 30], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d66334a0-15b9-4362-8afc-0bb44e1a9f1c", + "name": "Spawner (2225)", + "location": [1844, 2735, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "01e09394-d02b-4fad-9ee9-511454e49140", + "name": "Spawner (2225)", + "location": [1981, 2838, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7b2c31b7-20c9-46b8-9cd1-6b624a505c01", + "name": "Spawner (2225)", + "location": [1880, 2802, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2ce77da7-68d6-4c82-a3f4-8fdc1161ab7f", + "name": "Spawner (2225)", + "location": [1813, 2825, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc4349ab-4016-43e9-8f41-f09733d7c0d4", + "name": "Spawner (2225)", + "location": [1850, 2796, -8], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05b65869-ea89-4ac6-9233-aef3768a2796", + "name": "Spawner (2225)", + "location": [1939, 2739, 10], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "95936f14-8718-4f24-9236-d5e92d4fcc41", + "name": "Spawner (2225)", + "location": [1846, 2715, 10], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df027fa7-1a26-4c66-a7da-13e67012c84f", + "name": "Spawner (2225)", + "location": [2011, 2804, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d4728bf-f926-42cc-b670-37b8c74e5dbb", + "name": "Spawner (2225)", + "location": [1935, 2766, 10], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15b48a52-8c02-438e-bd97-aae13dada523", + "name": "Spawner (2225)", + "location": [1935, 2796, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8e0eaa2-df1d-478d-9ae1-85bc28647027", + "name": "Spawner (2225)", + "location": [2855, 734, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff67542d-65a0-4592-9df9-856ee2d1e834", + "name": "Spawner (2225)", + "location": [2861, 812, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "43cb9a14-49b9-4f88-885d-d98eef7479b9", + "name": "Spawner (2225)", + "location": [2882, 723, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ef0e9deb-1a5f-4e48-86db-18c1bb206530", + "name": "Spawner (2225)", + "location": [2961, 621, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6fa80ffa-14af-4633-9e2f-fcc64b96c946", + "name": "Spawner (2225)", + "location": [2840, 885, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41d788c5-5cc4-4cd1-9110-67560560dade", + "name": "Spawner (2225)", + "location": [2860, 999, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c351eb79-682e-4e9c-a193-0e5434f44634", + "name": "Spawner (2225)", + "location": [2998, 760, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d3f60ee0-06c4-4e53-81d8-0abbbc0383a3", + "name": "Spawner (2225)", + "location": [3034, 827, -3], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "86011460-6b38-45da-8706-cbfad5250fe0", + "name": "Spawner (2225)", + "location": [2962, 885, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a125d56-a7a0-4e00-a7c6-b0f7fea301a3", + "name": "Spawner (2225)", + "location": [2835, 805, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd2e9db5-e5b2-4195-9587-76654566de99", + "name": "Spawner (2225)", + "location": [2986, 637, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2761adf7-e265-438a-a2a8-284e00c4b605", + "name": "Spawner (2225)", + "location": [2890, 651, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d7d6dc0-179c-41c3-88c3-26d47e2b66a8", + "name": "Spawner (2225)", + "location": [2954, 705, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Beekeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "612b7282-50f3-48c0-8172-4d6ed40f47ff", + "name": "Spawner (2225)", + "location": [2987, 777, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "147578da-6cae-4d44-ad6e-3b01f3e2d670", + "name": "Spawner (2225)", + "location": [2841, 907, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "66bba18a-084f-4657-909d-a2c7e06e4e49", + "name": "Spawner (2225)", + "location": [3017, 761, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2325bd37-aa70-467d-a132-c77447fa333d", + "name": "Spawner (2225)", + "location": [2963, 813, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c0166342-30ea-4fb2-b95d-387c4a5ebc39", + "name": "Spawner (2225)", + "location": [3012, 780, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbea519d-d3d7-4287-9bd2-3231430294d8", + "name": "Spawner (2225)", + "location": [2899, 790, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad38d744-523e-425d-995c-6e954c9dbcc8", + "name": "Spawner (2225)", + "location": [2917, 798, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4723e0e9-a7e0-4657-9e74-0610afdf5a24", + "name": "Spawner (2225)", + "location": [2920, 856, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ff79ff29-6a36-42e4-9017-beb51a60290e", + "name": "Spawner (2225)", + "location": [2782, 969, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b1af2e4f-16db-41d7-a4ae-fcce5350d51f", + "name": "Spawner (2225)", + "location": [2865, 852, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4dd996e0-2cf5-4d04-8b3b-3264c0bed46a", + "name": "Spawner (2225)", + "location": [2918, 672, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a14b366d-e223-4ef7-9715-298df09962b1", + "name": "Spawner (2225)", + "location": [2992, 844, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "676fa552-31ef-4384-8dd1-e68d0d434f0c", + "name": "Spawner (2225)", + "location": [2995, 815, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a027a96b-cea5-4bfb-b048-2d7fcda9edd7", + "name": "Spawner (2225)", + "location": [2901, 908, 0], + "map": "Felucca", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9af91c7-daae-47de-958d-0169069fabde", + "name": "Spawner (2225)", + "location": [2881, 684, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5382e8d9-4883-46b4-9f57-f7eedea8f0b3", + "name": "Spawner (2225)", + "location": [5242, 180, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "96956b69-4920-400c-8ca9-6effd0202fa5", + "name": "Spawner (2225)", + "location": [5161, 32, 17], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ba190817-c467-4702-83f4-f13272bd748a", + "name": "Spawner (2225)", + "location": [5148, 60, 25], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "ff657041-f430-4a17-9995-279fdbb05ff1", - "name": "Spawner (225)", + "name": "Spawner (2225)", "location": [5347, 76, 25], "map": "Felucca", "count": 2, @@ -5454,26 +4479,45 @@ ] }, { - "$type": "Spawner", - "guid": "ff67542d-65a0-4592-9df9-856ee2d1e834", - "name": "Spawner (225)", - "location": [2861, 812, 0], + "type": "Spawner", + "guid": "a1b458a3-908e-42c6-8e88-9f9a013f098c", + "name": "Spawner (2225)", + "location": [5263, 129, 20], "map": "Felucca", - "count": 1, + "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, "homeRange": 0, "walkingRange": 5, "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } ] }, { - "$type": "Spawner", - "guid": "ff79ff29-6a36-42e4-9017-beb51a60290e", - "name": "Spawner (225)", - "location": [2782, 969, 0], + "type": "Spawner", + "guid": "a4163176-dab9-4a56-83ea-e97cd802579e", + "name": "Spawner (2225)", + "location": [5203, 86, 5], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f79dae4-98e1-489e-9f04-b11d20f34897", + "name": "Spawner (2225)", + "location": [5218, 175, 5], "map": "Felucca", "count": 1, "minDelay": "00:05:00", @@ -5481,8 +4525,681 @@ "team": 0, "homeRange": 0, "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98952a90-a316-4809-a85f-d5840e2f6716", + "name": "Spawner (2225)", + "location": [5300, 90, 15], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e19650f-6ec9-4170-8aed-3ff6cb384366", + "name": "Spawner (2225)", + "location": [5306, 37, 40], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "77ea211d-a375-4f9f-a2ff-31a864070728", + "name": "Spawner (2225)", + "location": [5236, 142, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0caf1231-8fd1-4873-b525-29c1b9a4751c", + "name": "Spawner (2225)", + "location": [5351, 56, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bea16b73-7772-41e1-868e-7a6de8007a93", + "name": "Spawner (2225)", + "location": [5154, 97, 5], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "00c709c3-bd1f-464f-9851-6222d10bf35b", + "name": "Spawner (2225)", + "location": [5215, 117, 1], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2efce74-1331-43af-9713-6b1c75fb6225", + "name": "Spawner (2225)", + "location": [570, 969, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "358552dd-07fb-4fc4-ad49-366f4cbd7105", + "name": "Spawner (2225)", + "location": [517, 986, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1bf5b7f8-c166-419f-87f5-199254245758", + "name": "Spawner (2225)", + "location": [529, 1008, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f439eb85-35d6-487c-884f-a7a71716748f", + "name": "Spawner (2225)", + "location": [652, 820, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6fb8b91e-1bb9-4d0a-8ffe-ddfb552cabf5", + "name": "Spawner (2225)", + "location": [610, 810, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d75fe7bf-4215-402e-9d5c-24bd7a8801bb", + "name": "Spawner (2225)", + "location": [535, 867, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03132a28-66bf-4d70-ad03-efc8590f21fe", + "name": "Spawner (2225)", + "location": [564, 1011, 0], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "38a24240-c538-40cf-93a6-65e6c31455c5", + "name": "Spawner (2225)", + "location": [643, 1083, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef25c059-a51f-4e6d-80f6-a39eb60c9c27", + "name": "Spawner (2225)", + "location": [624, 1145, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e16bd6c9-e916-4616-9273-c5c3d1ab0115", + "name": "Spawner (2225)", + "location": [631, 1034, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aedc8ecd-8443-4c79-a7ab-05da8844c493", + "name": "Spawner (2225)", + "location": [540, 966, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "173141ce-120f-451c-8d7e-781b07e8482a", + "name": "Spawner (2225)", + "location": [553, 985, 0], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a76b7074-3933-477d-849a-99833d78d89a", + "name": "Spawner (2225)", + "location": [5219, 4012, 37], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "acbb59a0-32ac-4add-9b8b-479cf4be15a3", + "name": "Spawner (2225)", + "location": [5275, 3977, 37], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61bb7c02-bbc3-497f-9d42-bc82a6378c3a", + "name": "Spawner (2225)", + "location": [5225, 4000, 38], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef1db5ca-06dd-4a31-bfce-5bb4c75a1df8", + "name": "Spawner (2225)", + "location": [5197, 4060, 37], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "add1a992-7c4e-419d-9342-29642aac96af", + "name": "Spawner (2225)", + "location": [5297, 4007, 40], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9acf583a-2654-4f9c-8be4-11f121aa97ab", + "name": "Spawner (2225)", + "location": [5234, 4025, 37], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d922542-dbaa-45a6-b70f-491621ed4509", + "name": "Spawner (2225)", + "location": [5191, 3989, 37], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9dbabd5d-fd49-490f-86f1-e07e76420332", + "name": "Spawner (2225)", + "location": [5296, 3978, 37], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81f52dcd-ccb6-470c-a58a-9141b90be9c3", + "name": "Spawner (2225)", + "location": [5669, 3131, 14], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Minter", "maxCount": 1, "probability": 100 }, + { "name": "Banker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "87f9a58b-fa94-4899-b3de-e2e344641494", + "name": "Spawner (2225)", + "location": [5731, 3192, 9], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "Mage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01605fa2-174e-473c-b83b-7b2edb42c693", + "name": "Spawner (2225)", + "location": [5726, 3243, 16], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Tinker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "472774c8-7b94-461a-a9e1-4d3004062592", + "name": "Spawner (2225)", + "location": [5662, 3150, 13], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "591c4553-36ec-4e20-8f04-1b7d2f056c7a", + "name": "Spawner (2225)", + "location": [5671, 3287, 11], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e6689d71-5784-4c9c-a0ed-90bcdde2eb2b", + "name": "Spawner (2225)", + "location": [5698, 3282, 15], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "95b1c31c-4fb0-46b2-8b18-8d4a8f7aaeb7", + "name": "Spawner (2225)", + "location": [5805, 3300, 11], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 }, + { "name": "Armorer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3f935f7e-4454-44fa-9222-58e667345c4d", + "name": "Spawner (2225)", + "location": [5714, 3202, 12], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "Herbalist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0024932-683f-457b-b4ca-fe0e60d8b42c", + "name": "Spawner (2225)", + "location": [5805, 3270, 11], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mapmaker", "maxCount": 1, "probability": 100 }, + { "name": "Shipwright", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a9450d4e-81d6-4963-9afd-fd2da7c143eb", + "name": "Spawner (2225)", + "location": [5752, 3272, 16], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "Tailor", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "553b4277-f7f3-44a0-8d8a-b1cf97b10152", + "name": "Spawner (2225)", + "location": [5839, 3256, 2], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8080d16f-4ee6-4d7b-b017-cebc7113ce25", + "name": "Spawner (2225)", + "location": [5746, 3200, 16], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5f377cf6-2581-4b00-95ac-e8804ab76415", + "name": "Spawner (2225)", + "location": [5775, 3161, 14], + "map": "Felucca", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6908fde5-9dd0-4385-8202-d07d88bfba2c", + "name": "Spawner (2225)", + "location": [5737, 3222, 11], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Healer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4ea855b-be4f-46f8-9b3a-6bac5d6a1c5a", + "name": "Spawner (2225)", + "location": [5691, 3209, 11], + "map": "Felucca", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "Carpenter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b201992b-e843-4249-82a4-2122cc01f090", + "name": "Spawner (2225)", + "location": [5736, 3263, 15], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Cobbler", "maxCount": 1, "probability": 100 }, + { "name": "Provisioner", "maxCount": 1, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/shared/trammel/WildLife.json b/Distribution/Data/Spawns/uoml/felucca/WildLife.json similarity index 85% rename from Distribution/Data/Spawns/shared/trammel/WildLife.json rename to Distribution/Data/Spawns/uoml/felucca/WildLife.json index 799d6fcd5..b21883d7b 100644 --- a/Distribution/Data/Spawns/shared/trammel/WildLife.json +++ b/Distribution/Data/Spawns/uoml/felucca/WildLife.json @@ -1,962 +1,10 @@ [ { - "$type": "Spawner", - "guid": "0022416f-f5eb-483c-9ad4-eac25ac3af57", - "name": "Spawner (126)", - "location": [2744, 2014, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0228c6c2-bc57-4dad-864a-f33844129d0a", - "name": "Spawner (126)", - "location": [1958, 1305, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "029a425c-6944-4f56-b9de-a3a007961469", - "name": "Spawner (126)", - "location": [4326, 1046, 8], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "02d75559-5e11-47dd-9520-76f8d798f286", - "name": "Spawner (126)", - "location": [398, 1305, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "036d98ab-980c-4af8-b924-dd900a6f438c", - "name": "Spawner (126)", - "location": [3232, 510, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "03f454af-c939-406a-b7df-3b11264c1e56", - "name": "Spawner (126)", - "location": [1531, 2351, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "06dc791a-faa3-4301-b930-038655903d16", - "name": "Spawner (126)", - "location": [1709, 1992, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0779e1d7-6a80-4907-83c9-a7b8a1cbd8bb", - "name": "Spawner (126)", - "location": [1867, 1244, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0950a31a-0b42-481f-8658-46d97e5047f5", - "name": "Spawner (126)", - "location": [1505, 2445, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0a898cee-dada-46e4-89f6-2f32a901de39", - "name": "Spawner (126)", - "location": [916, 1159, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0b7d99ad-681f-4b64-99c3-cda74412043b", - "name": "Spawner (126)", - "location": [3392, 424, 9], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0d47483e-14b9-4949-8e00-76c5431539d7", - "name": "Spawner (126)", - "location": [4470, 1430, 8], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0e3d2fd4-d529-45bd-a135-56ca809d2e35", - "name": "Spawner (126)", - "location": [1087, 2401, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0f8ebac9-7bfd-4cb5-9c30-a69baa46be7b", - "name": "Spawner (126)", - "location": [2416, 1172, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "0fe92a55-c40e-49a3-9658-ebb41b284a60", - "name": "Spawner (126)", - "location": [2554, 1142, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "110affec-d184-4b10-89a3-db7ed18d0f29", - "name": "Spawner (126)", - "location": [2643, 728, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1252b4d8-f8ff-41e4-be2b-45c0ed2c62e5", - "name": "Spawner (126)", - "location": [4336, 1130, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "1ceff4e8-8c0f-4974-80dc-09aa04d29799", - "name": "Spawner (126)", - "location": [1824, 3027, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "21f01798-bc50-4ce5-95d9-16c900a28689", - "name": "Spawner (126)", - "location": [2786, 2282, 13], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "259f44e8-a7d3-41cd-b3a8-d26f3458876d", - "name": "Spawner (126)", - "location": [4510, 1262, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "25b8399a-d467-41ec-806f-e26ac6b3761a", - "name": "Spawner (126)", - "location": [727, 1394, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "25b9f741-b303-4341-ad1a-e4eb7973d3c9", - "name": "Spawner (126)", - "location": [2583, 993, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "261fe98c-05ae-446c-8e4c-6ab6a5c4646b", - "name": "Spawner (126)", - "location": [2814, 492, 15], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2a5e8596-bdb5-4766-b431-45bb6cefbd6d", - "name": "Spawner (126)", - "location": [1024, 1244, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "2af04186-1f66-4c97-8893-21966a8d38ff", - "name": "Spawner (126)", - "location": [3092, 542, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "32c27855-0fa7-42cc-b9bb-749f3c6c737c", - "name": "Spawner (126)", - "location": [4478, 3218, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "333e31a1-765c-4086-bc1f-5e9f585929aa", - "name": "Spawner (126)", - "location": [876, 1639, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "348ada5a-4afd-47f1-a414-a59766d0f0db", - "name": "Spawner (126)", - "location": [1092, 1010, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "36d0217e-7fa6-4abb-a59c-572441f8e069", - "name": "Spawner (126)", + "type": "Spawner", + "guid": "acb8201d-8566-4625-ac64-54c9b24ec285", + "name": "Spawner (2226)", "location": [3394, 628, 5], - "map": "Trammel", + "map": "Felucca", "count": 19, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -986,11 +34,79 @@ ] }, { - "$type": "Spawner", - "guid": "376b8121-d1b0-4461-83c7-37f1920fa4a7", - "name": "Spawner (126)", + "type": "Spawner", + "guid": "e8375f72-dc08-4ec1-92ae-4eab98618ceb", + "name": "Spawner (2226)", + "location": [3232, 510, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd99393a-7ba7-49cd-8d52-b36eda439249", + "name": "Spawner (2226)", + "location": [4478, 3218, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "58861404-11fe-4f82-b962-70196d0d5ff6", + "name": "Spawner (2226)", "location": [4446, 3706, 0], - "map": "Trammel", + "map": "Felucca", "count": 19, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -1020,4431 +136,11 @@ ] }, { - "$type": "Spawner", - "guid": "37ca5711-d9e9-4754-ab12-d72282bd3775", - "name": "Spawner (126)", - "location": [1563, 2277, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "388f1cb5-ac9c-4127-ad37-0fd812bfb769", - "name": "Spawner (126)", - "location": [1798, 1471, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3c28cde3-8cbf-4b0f-9a22-c4da0f91a9ef", - "name": "Spawner (126)", - "location": [1932, 3125, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "3d62d25b-d1a9-4b3b-ba9d-273f3365465e", - "name": "Spawner (126)", - "location": [805, 1570, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "413394e4-0a86-4b97-ad09-b787f7f95b13", - "name": "Spawner (126)", - "location": [4448, 1334, 3], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "41d87f21-c25b-40bb-8a40-79cd984daac9", - "name": "Spawner (126)", - "location": [1725, 1447, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4339d779-ba0b-4a17-aa43-7b29eccd40e4", - "name": "Spawner (126)", - "location": [834, 751, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "437c3cc4-ad9f-4da6-932c-b342014445a9", - "name": "Spawner (126)", - "location": [2418, 999, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "43cb2d22-fd8c-4256-860d-22cf169311a5", - "name": "Spawner (126)", - "location": [2513, 782, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "45803a8e-701d-4b75-9ef0-b6705a0499c6", - "name": "Spawner (126)", - "location": [4612, 3424, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4695be96-fea3-4b09-96ab-b8d7246a785f", - "name": "Spawner (126)", - "location": [1063, 541, 16], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "46baf50c-622c-4cfc-9a0c-cb38c883b270", - "name": "Spawner (126)", - "location": [1857, 600, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "480a555e-947b-4181-b211-4ed3fef3df68", - "name": "Spawner (126)", - "location": [1526, 960, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4a339b0e-c70e-4798-8210-3f3e2de53574", - "name": "Spawner (126)", - "location": [4468, 1006, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4a5e2ae8-f5bb-49a4-a405-c392bd88e885", - "name": "Spawner (126)", - "location": [1011, 1945, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4aecd554-03ce-4054-aa4b-e549bcda9099", - "name": "Spawner (126)", - "location": [1612, 2526, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b2f68c1-edb6-401d-a4e4-e2c5e25d3560", - "name": "Spawner (126)", - "location": [994, 2361, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4b520b06-67a4-4d03-9ac8-1b6b89918338", - "name": "Spawner (126)", - "location": [1334, 2311, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "4efec350-2605-46b3-923f-be57885e6aad", - "name": "Spawner (126)", - "location": [1388, 784, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5001467d-867b-4f25-a046-9b42cfce15af", - "name": "Spawner (126)", - "location": [1222, 2499, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5275d813-843e-497f-8801-103eaaf41f50", - "name": "Spawner (126)", - "location": [2495, 978, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5403a56a-456b-42ab-9518-889d08375356", - "name": "Spawner (126)", - "location": [1011, 896, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5ac54db3-d755-475f-84df-bec218f106b7", - "name": "Spawner (126)", - "location": [1628, 2334, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5e97f47f-63ea-4b3c-97aa-252f922c1fc3", - "name": "Spawner (126)", - "location": [1173, 2103, 2], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "5ee0e83f-fd06-4c5d-91d2-74e5b3750f53", - "name": "Spawner (126)", - "location": [2654, 2286, 9], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "60301ceb-5d40-41b1-81f3-d4581dbbb3b2", - "name": "Spawner (126)", - "location": [1695, 809, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6554a940-d516-4359-b224-3d7984509f21", - "name": "Spawner (126)", - "location": [2672, 814, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6903cd58-d9a3-4d0c-ab3b-41e09513bddb", - "name": "Spawner (126)", - "location": [919, 1718, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "6fbfa112-3466-4ae1-b215-45b81f270cca", - "name": "Spawner (126)", - "location": [2289, 917, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72348476-489b-41c1-b61e-6900a22a61b5", - "name": "Spawner (126)", - "location": [3172, 664, 8], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72d26907-465d-42cf-b64a-7b70c3f21b5e", - "name": "Spawner (126)", - "location": [1426, 2615, 10], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "72fa3aa7-e804-424f-b789-4f0ab28575a6", - "name": "Spawner (126)", - "location": [4632, 1164, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7501f45b-0531-492c-91ea-de33a4a305dd", - "name": "Spawner (126)", - "location": [559, 2081, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "75f30f5a-a828-474c-85a0-4f58fad8bbf0", - "name": "Spawner (126)", - "location": [1498, 2040, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "76e14719-a7c5-4ff5-9ca4-65d2bedd8229", - "name": "Spawner (126)", - "location": [2668, 1020, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7c598b76-349e-42b5-8a5a-f7395b444def", - "name": "Spawner (126)", - "location": [782, 1156, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7da8b2c6-8444-46a1-8dc8-78ff324fb2cf", - "name": "Spawner (126)", - "location": [1387, 2023, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7e32abac-5a5c-424e-b50f-3e539dbb1556", - "name": "Spawner (126)", - "location": [1769, 1330, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "7efa7e94-9493-4113-9804-2997584755fd", - "name": "Spawner (126)", - "location": [4384, 1218, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "805e07bf-4287-4e09-8e30-437284cc96ec", - "name": "Spawner (126)", - "location": [210, 1447, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "832fae60-bc7c-4675-8bf1-747404eb52c2", - "name": "Spawner (126)", - "location": [1699, 2560, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "837adc9c-cd37-4302-9bf3-cf063de88772", - "name": "Spawner (126)", - "location": [1666, 1290, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8665d1be-a08f-41b7-9450-c10bb70577f2", - "name": "Spawner (126)", - "location": [974, 374, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8742ec13-802c-4bdc-80bb-37c4d6031baf", - "name": "Spawner (126)", - "location": [2814, 3500, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8c11ab8d-0f10-4d9d-9b58-4d152567074f", - "name": "Spawner (126)", - "location": [1527, 2609, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "8fe92be0-9b3d-4ef0-850b-0e64e571d47c", - "name": "Spawner (126)", - "location": [977, 1638, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "95395203-fa7a-46ab-9c61-360659b66537", - "name": "Spawner (126)", - "location": [4692, 3494, 1], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "96c8d9da-bf02-4fd8-8578-1b2ed732d687", - "name": "Spawner (126)", - "location": [1503, 2207, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98b988f2-a420-45e7-8ba5-2bda0ae81f01", - "name": "Spawner (126)", - "location": [1017, 1726, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "98f98072-a630-4276-abed-7d07faba2350", - "name": "Spawner (126)", - "location": [846, 1071, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "99f98b9c-1885-479d-882c-f8251711f59d", - "name": "Spawner (126)", - "location": [2074, 1972, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9aa8d68c-2713-4500-aedf-95471c1b15e9", - "name": "Spawner (126)", - "location": [1351, 1359, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9ae00c78-f9de-4faa-b2e2-330e69da4478", - "name": "Spawner (126)", - "location": [890, 657, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9d939fac-db2a-4bf7-bb3c-5cf12a16057d", - "name": "Spawner (126)", - "location": [1802, 495, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9f2ed951-4709-4c94-92ce-0fb0d21bd5f2", - "name": "Spawner (126)", - "location": [4544, 1128, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9f56a1bd-2813-46c3-a48e-df1bddf67a93", - "name": "Spawner (126)", - "location": [4590, 1326, 8], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "9fa7f8cb-f1b0-47ef-b274-fae3fdc58df7", - "name": "Spawner (126)", - "location": [1884, 735, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a107d63c-c00c-4dff-9196-726441942476", - "name": "Spawner (126)", - "location": [725, 1483, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a3a4287c-82dd-4d54-ae9a-fc0de236aaee", - "name": "Spawner (126)", - "location": [4526, 986, 10], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a52ff072-2c3d-428e-9d26-cdc4c4b4217f", - "name": "Spawner (126)", - "location": [2669, 922, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a6c38b89-808a-44b4-a5c7-aac41fab07d6", - "name": "Spawner (126)", - "location": [1377, 1911, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a7eb4c9b-fb14-4e00-9a15-e51095c2565b", - "name": "Spawner (126)", - "location": [2864, 150, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8a74bc4-a2ed-4d10-b573-cf0ef0fb69db", - "name": "Spawner (126)", - "location": [771, 1013, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "a8e639d7-7836-4fcc-9420-e40bee6383de", - "name": "Spawner (126)", - "location": [1602, 2426, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "aa7f7549-b005-4b92-950f-329e555c8bcd", - "name": "Spawner (126)", - "location": [855, 934, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae37664f-7df6-4a33-853e-b577723971be", - "name": "Spawner (126)", - "location": [1315, 2192, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ae784e69-bee7-4043-88c1-852832876c8c", - "name": "Spawner (126)", - "location": [1172, 974, 2], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "af606966-fb70-4d7f-8ad6-fcf293fbf012", - "name": "Spawner (126)", - "location": [3292, 570, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b04c36d0-8c69-46f0-a48c-2b947622bb3f", - "name": "Spawner (126)", - "location": [4592, 1424, 3], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b1ed0cd3-8f33-44ee-b48a-ccf8bfccbc62", - "name": "Spawner (126)", - "location": [3650, 2080, 21], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b25b4e76-d62b-4f92-89d8-6d72908611ec", - "name": "Spawner (126)", - "location": [1727, 2702, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b2a798ca-14e2-4f65-a3d8-b361e2bec521", - "name": "Spawner (126)", - "location": [937, 743, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3c776b4-3387-47ec-b8b6-6c6320e446f2", - "name": "Spawner (126)", - "location": [1222, 2378, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b3cfc4c1-7fdb-438c-b51d-40211ab8e2fa", - "name": "Spawner (126)", - "location": [689, 753, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b585a36d-a2d6-48c1-a584-59d29425a6cd", - "name": "Spawner (126)", - "location": [2085, 658, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b65d99d3-a2c3-404c-9db5-e261da0b012d", - "name": "Spawner (126)", - "location": [4540, 1454, 8], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b862532b-c3c1-473e-9c75-2970ef697583", - "name": "Spawner (126)", - "location": [1152, 812, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b8b734bb-93cf-40c5-9178-20bc299963b3", - "name": "Spawner (126)", - "location": [623, 1328, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b9457f8a-483a-4f47-bfa9-7a301f3e22d4", - "name": "Spawner (126)", - "location": [968, 2476, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b94d2950-01d9-45d2-a308-c4d863487ffb", - "name": "Spawner (126)", - "location": [1545, 813, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "b96c91c8-d294-44ec-b8ce-13ef5c35f2d4", - "name": "Spawner (126)", - "location": [951, 542, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba1ea3b8-e6b1-4231-b2be-961396c2bfab", - "name": "Spawner (126)", - "location": [1005, 2647, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ba7c897f-c0ce-44c1-9141-7b3cfbb65791", - "name": "Spawner (126)", - "location": [2765, 754, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bb9ece00-1e1a-4a80-8c19-b85b54ba4be6", - "name": "Spawner (126)", - "location": [1268, 2018, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bc12e17d-1f65-4e97-a222-9fc7bf0f4444", - "name": "Spawner (126)", - "location": [883, 2129, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd3e9539-89a7-4ccb-b6f4-f9a1621c6f6c", - "name": "Spawner (126)", - "location": [748, 669, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bd5716e7-8f5b-4d35-9b8e-f2f3af6829de", - "name": "Spawner (126)", - "location": [1894, 2942, 19], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "bf6dee8d-749a-4894-a4f7-6b0c179aa2d4", - "name": "Spawner (126)", - "location": [851, 1770, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c157367d-9508-4b38-9472-f87683a4756d", - "name": "Spawner (126)", - "location": [1724, 2781, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c3c89bb5-3c88-4d7b-8bcf-57f0e40d3d59", - "name": "Spawner (126)", - "location": [2735, 417, 15], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c4bfe60c-9ce2-4739-974e-5b0c453aa1f6", - "name": "Spawner (126)", - "location": [802, 1280, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c5b3fb52-ce57-429c-a393-883235b01489", - "name": "Spawner (126)", - "location": [958, 2074, 1], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c6377b5f-e04c-457e-9ec5-3e45c8302d93", - "name": "Spawner (126)", - "location": [2512, 1066, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c96e7fd6-bd4a-407b-8fe8-5116adb5c0e9", - "name": "Spawner (126)", - "location": [1231, 1462, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c9adcc4e-105e-4f9d-9044-4112ff31f5bc", - "name": "Spawner (126)", - "location": [940, 1028, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "c9ba90ce-6733-4684-a385-858a97a81829", - "name": "Spawner (126)", - "location": [887, 1223, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "caa501da-6efa-4136-8573-1b996c048e85", - "name": "Spawner (126)", - "location": [1229, 1655, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cd05b1bf-8c39-43cc-82f8-658b0a7837de", - "name": "Spawner (126)", - "location": [1728, 691, 6], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cdaade9c-e8af-48ed-8fd1-7326a2ddba80", - "name": "Spawner (126)", - "location": [3206, 366, 9], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ce46d463-3146-4ed2-9c0f-6d7d85c92d69", - "name": "Spawner (126)", - "location": [993, 1130, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cf3b84c2-c2b7-4692-ac18-e5c9df1e3a94", - "name": "Spawner (126)", - "location": [1582, 2722, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "cfc04314-5772-4b9c-a50a-3652e7be42ea", - "name": "Spawner (126)", - "location": [964, 2214, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d3ef6251-4ee9-4e19-aa70-db8ea6f92bed", - "name": "Spawner (126)", - "location": [1486, 1301, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d41167e6-4e39-4443-a288-1ecbd92a54c2", - "name": "Spawner (126)", - "location": [1057, 765, 1], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d496d8f3-ba8e-47b5-ab86-c4b6a7cba030", - "name": "Spawner (126)", - "location": [895, 830, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d5a4c442-7233-4c40-950f-51aea9e80674", - "name": "Spawner (126)", - "location": [3076, 150, 1], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d61f40c0-87d2-4fc2-8282-5a6eedadd553", - "name": "Spawner (126)", - "location": [464, 2059, 8], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d8231afb-b1d4-48ae-bb33-0b6da6d81411", - "name": "Spawner (126)", - "location": [2188, 1986, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d8c09fcb-e2a0-466b-884c-b9859c718d3d", - "name": "Spawner (126)", - "location": [2338, 989, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "d98a0bd4-2d35-4400-ac73-df07a0390baf", - "name": "Spawner (126)", - "location": [2914, 547, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dbead4d1-29b0-43a8-a5a6-5ceb94ca384d", - "name": "Spawner (126)", - "location": [2688, 524, 11], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dccc7e1b-7ef9-44f3-9c3f-c2395b4c739a", - "name": "Spawner (126)", - "location": [999, 1839, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "dd753390-ed2c-4648-a7d8-5b6afa41f989", - "name": "Spawner (126)", - "location": [1047, 1071, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "df245d50-9023-49ba-acc7-1cd6c8e27043", - "name": "Spawner (126)", - "location": [1020, 1360, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e0e440be-1903-4921-8001-c203b99824d0", - "name": "Spawner (126)", - "location": [3608, 2140, 79], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e3d015a1-d167-47e6-8f63-e03f77a29019", - "name": "Spawner (126)", - "location": [1973, 751, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e3e5a953-2208-455f-9bd5-e160f387afbc", - "name": "Spawner (126)", - "location": [1459, 1963, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e68d9d2b-ce90-42ab-8a2d-87437babe0a8", - "name": "Spawner (126)", - "location": [1070, 2069, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e81bc46f-505a-4020-8e34-026a669c750a", - "name": "Spawner (126)", - "location": [764, 822, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "e8d9152c-1bce-4b09-b08a-f43be0cd650e", - "name": "Spawner (126)", - "location": [1634, 2645, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "eaa6d886-bacf-4ada-8dd9-b4059ed58700", - "name": "Spawner (126)", - "location": [1722, 2435, 10], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ead3c54c-7f7a-4472-8862-160168ce877d", - "name": "Spawner (126)", - "location": [2367, 748, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ebec0b27-0c1c-403f-abdc-9b7866570a8a", - "name": "Spawner (126)", - "location": [485, 1398, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "ec2190b0-7f31-40b6-a350-c1bf44d2098a", - "name": "Spawner (126)", - "location": [805, 576, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "edf184b7-267e-475a-9e9d-6a4da4720a0b", - "name": "Spawner (126)", - "location": [1715, 2307, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f0da5cc6-e368-44e9-a745-e6744fd2edf7", - "name": "Spawner (126)", - "location": [2946, 2168, 44], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f143629b-57cc-4c98-a2bd-06c7a3f00e70", - "name": "Spawner (126)", - "location": [2998, 3590, 15], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f1c3581a-b27d-41a6-98ce-3db7acc36783", - "name": "Spawner (126)", - "location": [2888, 391, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f2687613-183f-4bd0-9103-d7bd70639793", - "name": "Spawner (126)", - "location": [1865, 1379, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f2f41e23-c367-4172-8dec-c42709950de9", - "name": "Spawner (126)", - "location": [1650, 2796, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f309b0c8-cbe1-4a42-a2a6-0b5bbadcdf70", - "name": "Spawner (126)", + "type": "Spawner", + "guid": "e030b63a-f358-4458-adf2-34e91b171761", + "name": "Spawner (2226)", "location": [4682, 3662, 77], - "map": "Trammel", + "map": "Felucca", "count": 19, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -5474,11 +170,11 @@ ] }, { - "$type": "Spawner", - "guid": "f444d0ce-22f3-44d5-b9c2-79a8092e1b07", - "name": "Spawner (126)", - "location": [2748, 660, 0], - "map": "Trammel", + "type": "Spawner", + "guid": "1230cda9-3dbe-4268-8e2f-cf765dccd339", + "name": "Spawner (2226)", + "location": [4692, 3494, 1], + "map": "Felucca", "count": 19, "minDelay": "00:05:00", "maxDelay": "00:10:00", @@ -5508,147 +204,5859 @@ ] }, { - "$type": "Spawner", - "guid": "f7a461d8-c310-48db-8dea-fb3a61fe9504", - "name": "Spawner (126)", - "location": [2122, 2108, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f8830c8c-49c9-4ffd-b299-c5412d510b60", - "name": "Spawner (126)", - "location": [1631, 640, 22], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "f9b091d7-b8ac-49f8-b6b0-420e2f503413", - "name": "Spawner (126)", - "location": [1018, 629, 0], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "faedd551-9d07-4233-b959-540762d502b7", - "name": "Spawner (126)", - "location": [1439, 2104, 5], - "map": "Trammel", - "count": 19, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "homeRange": 50, - "walkingRange": 50, - "entries": [ - { "name": "Boar", "maxCount": 7, "probability": 100 }, - { "name": "Cougar", "maxCount": 7, "probability": 100 }, - { "name": "Goat", "maxCount": 7, "probability": 100 }, - { "name": "Horse", "maxCount": 7, "probability": 100 }, - { "name": "Panther", "maxCount": 7, "probability": 100 }, - { "name": "Pig", "maxCount": 7, "probability": 100 }, - { "name": "Sheep", "maxCount": 7, "probability": 100 }, - { "name": "BlackBear", "maxCount": 5, "probability": 100 }, - { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, - { "name": "BrownBear", "maxCount": 5, "probability": 100 }, - { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, - { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, - { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, - { "name": "Bird", "maxCount": 2, "probability": 100 }, - { "name": "Eagle", "maxCount": 2, "probability": 100 }, - { "name": "GreatHart", "maxCount": 2, "probability": 100 }, - { "name": "Hind", "maxCount": 2, "probability": 100 }, - { "name": "Bull", "maxCount": 2, "probability": 100 }, - { "name": "Cow", "maxCount": 2, "probability": 100 } - ] - }, - { - "$type": "Spawner", - "guid": "fc540fa1-f1f8-45b4-8d06-f1d91b4575b0", - "name": "Spawner (126)", + "type": "Spawner", + "guid": "710644f8-df91-4fe2-948a-376700d3afcd", + "name": "Spawner (2226)", "location": [4560, 3504, 2], - "map": "Trammel", + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c3aaabb-253c-4de4-bc92-3ae604a8213e", + "name": "Spawner (2226)", + "location": [4612, 3424, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7dc555e2-daca-49fe-bde0-a49d5e3eaa51", + "name": "Spawner (2226)", + "location": [3607, 2417, 45], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d7e7d28-893b-406a-8f44-b8fc3c7e55af", + "name": "Spawner (2226)", + "location": [3555, 2525, 13], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54a0d275-e0fe-4565-bc4b-a36330991e7a", + "name": "Spawner (2226)", + "location": [3506, 2477, 28], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84f26198-6d50-4e80-a0fb-5f47b0a200b0", + "name": "Spawner (2226)", + "location": [3452, 2563, 32], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9ace0130-cad4-4d06-956a-f8a1fbd4c88f", + "name": "Spawner (2226)", + "location": [3429, 2650, 43], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69f2606e-f9c8-4110-8031-ed1889e5985d", + "name": "Spawner (2226)", + "location": [3449, 2737, 49], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f8585600-951d-4c74-9533-db771d36d4e8", + "name": "Spawner (2226)", + "location": [3736, 2512, 35], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "833a7019-688c-4996-93af-a5cdc6501324", + "name": "Spawner (2226)", + "location": [3718, 2736, 38], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a81fadc-a778-41a9-aea0-76fe8ec168cf", + "name": "Spawner (2226)", + "location": [3654, 2866, 61], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba6fdb72-4770-4b79-96d9-2f8ea6cbf65a", + "name": "Spawner (2226)", + "location": [3662, 2802, 51], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb5eb63b-330a-4d9b-9e78-a22466bf7a50", + "name": "Spawner (2226)", + "location": [3596, 2804, 51], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3713dbd8-18ac-4d4f-b278-5375fc33a7eb", + "name": "Spawner (2226)", + "location": [3546, 2812, 13], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63a9ae0f-6d62-4169-8494-e14fe4fa8539", + "name": "Spawner (2226)", + "location": [3650, 2080, 21], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "adaf3214-a59c-4c6b-86b8-466ce4e53018", + "name": "Spawner (2226)", + "location": [3608, 2140, 79], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f61bc16-a435-4de0-9aaa-223f3930c57c", + "name": "Spawner (2226)", + "location": [4384, 1218, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b40de864-46c6-49c8-9be6-7ee491a8c7f6", + "name": "Spawner (2226)", + "location": [4468, 1006, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85b70538-e38a-4e69-b0c9-fe1774848e03", + "name": "Spawner (2226)", + "location": [4326, 1046, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fcd2029-3cf4-442c-8977-e3f21a4fb8f5", + "name": "Spawner (2226)", + "location": [4544, 1128, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "11834407-82df-4241-bf81-5ea9687c955a", + "name": "Spawner (2226)", + "location": [4510, 1262, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4be9e0ff-5157-493c-90c2-9c1a1a45e3b1", + "name": "Spawner (2226)", + "location": [4590, 1326, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0f15020-27c0-4fd5-ab31-9055a7578081", + "name": "Spawner (2226)", + "location": [4526, 986, 10], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a7a1122-3dd4-49c7-b8f4-d0a47d53a32e", + "name": "Spawner (2226)", + "location": [4336, 1130, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9fe7ab6c-c783-431b-b05e-65f5ccb275cb", + "name": "Spawner (2226)", + "location": [4448, 1334, 3], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8464904f-68c6-4900-a548-1ee24a9605f2", + "name": "Spawner (2226)", + "location": [4540, 1454, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "345821d1-8277-47c0-8b9f-a722e65dd2e7", + "name": "Spawner (2226)", + "location": [4632, 1164, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f4ab748-bee7-48f9-820b-230e2b2c2f65", + "name": "Spawner (2226)", + "location": [4470, 1430, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "90a10ef6-dca6-4f41-9f9a-3ac84050e227", + "name": "Spawner (2226)", + "location": [4592, 1424, 3], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f44df805-ac2e-4331-b14f-2ddcee0e34c6", + "name": "Spawner (2226)", + "location": [2814, 3500, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f40f3fdb-8bf3-41c7-b8c4-4375c91c4c94", + "name": "Spawner (2226)", + "location": [2998, 3590, 15], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bd58b24-3bf1-48b8-9d6f-d8ff61a2e2c4", + "name": "Spawner (2226)", + "location": [2864, 150, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bcd487fc-33c6-47ea-b8ab-ba10f86db14f", + "name": "Spawner (2226)", + "location": [3292, 570, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76f11cc1-4ca0-4357-a659-026ea69ae181", + "name": "Spawner (2226)", + "location": [3206, 366, 9], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "717379e5-9754-4a5f-8083-2a7042b9b403", + "name": "Spawner (2226)", + "location": [3172, 664, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c42f7ec-a06f-492b-b19a-4c887a2057b2", + "name": "Spawner (2226)", + "location": [3392, 424, 9], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fff42bf6-596f-4059-943f-bf76af7660b8", + "name": "Spawner (2226)", + "location": [3076, 150, 1], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4ca5a94-fee0-447a-bed4-4bf70b4a7a8f", + "name": "Spawner (2226)", + "location": [3092, 542, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02a348ee-7285-4741-b23e-61a6ef88a7c0", + "name": "Spawner (2226)", + "location": [2512, 1066, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b814727e-e122-4a40-af91-bfa4632efc20", + "name": "Spawner (2226)", + "location": [2554, 1142, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9228d1aa-d550-4239-a8d1-df8c69125d44", + "name": "Spawner (2226)", + "location": [2416, 1172, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09d26956-a96f-4cce-b0e9-37f5bc2afffa", + "name": "Spawner (2226)", + "location": [2188, 1986, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a91ed63-e48f-4b5c-8f67-68e1b0519944", + "name": "Spawner (2226)", + "location": [2654, 2286, 9], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "51fc697b-edf6-413f-acda-11c084092eb7", + "name": "Spawner (2226)", + "location": [2946, 2168, 44], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5355f9c-f463-4132-9204-550d80ed45c5", + "name": "Spawner (2226)", + "location": [2744, 2014, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fa4af6f-bbc5-4dd0-b6df-20f4c7971725", + "name": "Spawner (2226)", + "location": [2786, 2282, 13], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3badeb6-8bf0-41a6-b6e0-6c77ad3c6ba8", + "name": "Spawner (2226)", + "location": [2074, 1972, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9dd37bd-c5f8-4525-b3b8-b06585d7ddaf", + "name": "Spawner (2226)", + "location": [2122, 2108, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "892408a1-3f82-4854-ba77-7f37a15582a1", + "name": "Spawner (2226)", + "location": [1932, 3125, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88c34d44-6f28-4353-bdea-ed04a0bf415a", + "name": "Spawner (2226)", + "location": [1824, 3027, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4e211be3-6af6-4035-a6f0-72704f498f6d", + "name": "Spawner (2226)", + "location": [1894, 2942, 19], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "579099c7-42d6-4f8c-843c-a0c993cc3430", + "name": "Spawner (2226)", + "location": [1222, 2499, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd418550-6b49-45a5-9c4a-6896b7fc2a6d", + "name": "Spawner (2226)", + "location": [1005, 2647, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2de2354-f225-4181-a7ce-bd709041b5fa", + "name": "Spawner (2226)", + "location": [464, 2059, 8], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "438632c6-7114-4c25-9ab4-ec3a0883720d", + "name": "Spawner (2226)", + "location": [559, 2081, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8bf5c14b-8f9c-4fa5-96fb-0b8521ffb0b2", + "name": "Spawner (2226)", + "location": [883, 2129, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebd28e0c-ea07-4ec1-b767-1ab08f5d8a0a", + "name": "Spawner (2226)", + "location": [968, 2476, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1017559-b6f9-44fc-b632-30a61d15f505", + "name": "Spawner (2226)", + "location": [994, 2361, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d44d4945-508a-4a08-a281-8ed15b4c0d6b", + "name": "Spawner (2226)", + "location": [964, 2214, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0acdd078-559c-40e8-bc74-2490d5c85d71", + "name": "Spawner (2226)", + "location": [1087, 2401, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f638b8b-dd33-49f6-8bbf-c7e638a912fe", + "name": "Spawner (2226)", + "location": [1222, 2378, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6adc698e-936e-4d69-8870-13a39fb1baa5", + "name": "Spawner (2226)", + "location": [1334, 2311, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03ab5a78-b854-4676-bad5-4a37e96c1529", + "name": "Spawner (2226)", + "location": [1315, 2192, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "29780577-caca-4fcc-8586-0d7d9f9ea4fc", + "name": "Spawner (2226)", + "location": [1426, 2615, 10], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "27a803b9-9e76-4bcd-8d3f-d55d65c8b721", + "name": "Spawner (2226)", + "location": [1527, 2609, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e87997b-c05c-429a-a632-a9076a5afd84", + "name": "Spawner (2226)", + "location": [1582, 2722, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7f778ca1-e3e1-411e-8029-e62ad6783492", + "name": "Spawner (2226)", + "location": [1650, 2796, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e4e5017-002b-4c43-b113-3ae91958fee4", + "name": "Spawner (2226)", + "location": [1724, 2781, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc6aeac6-caef-47bd-9f88-91e5852f81fd", + "name": "Spawner (2226)", + "location": [1727, 2702, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b654652-e5e1-4cbb-8404-e2760b2f180f", + "name": "Spawner (2226)", + "location": [1634, 2645, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b6895418-072d-48b0-92f8-cf2eb6b9d085", + "name": "Spawner (2226)", + "location": [1505, 2445, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a58ab96-dff2-424c-9486-829b5e1d0df8", + "name": "Spawner (2226)", + "location": [1699, 2560, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f6f3201e-4f38-486d-b797-81a09db9f3bf", + "name": "Spawner (2226)", + "location": [1612, 2526, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ff3449e7-8000-450f-bfb5-af18d60aced2", + "name": "Spawner (2226)", + "location": [1722, 2435, 10], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2b1d8e55-62da-4a75-a626-bc24e0415a5e", + "name": "Spawner (2226)", + "location": [1715, 2307, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4e006fc-cd03-4c67-9622-2b5d4199a265", + "name": "Spawner (2226)", + "location": [1531, 2351, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ba37ae4-8aa1-4631-a39e-4d727282f435", + "name": "Spawner (2226)", + "location": [1602, 2426, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "87498c2d-dbda-448c-8822-993811da1899", + "name": "Spawner (2226)", + "location": [1628, 2334, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4954f31e-56af-4be6-82de-5d909a232b8f", + "name": "Spawner (2226)", + "location": [1563, 2277, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f0a0d30-52e5-4e38-9853-06806f9bf957", + "name": "Spawner (2226)", + "location": [1503, 2207, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5de7027a-07d5-405f-aba7-ffa1edb10d69", + "name": "Spawner (2226)", + "location": [1709, 1992, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fe6b62d-c81a-4440-a608-26c0b40a8f08", + "name": "Spawner (2226)", + "location": [1498, 2040, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c88821a4-e46f-4867-9994-f1583d90db1c", + "name": "Spawner (2226)", + "location": [1439, 2104, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "18bb4b32-dbcd-416e-aae6-547bb1b13fb1", + "name": "Spawner (2226)", + "location": [1387, 2023, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9a18ee1-4484-4c93-8fb9-7a996468639e", + "name": "Spawner (2226)", + "location": [1377, 1911, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a1a9dd9-0073-47da-8ea9-f7b932cad724", + "name": "Spawner (2226)", + "location": [1459, 1963, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a0a3a37e-6c7f-4636-9680-fed8a5144063", + "name": "Spawner (2226)", + "location": [1268, 2018, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e2f4cce-2302-427b-8a93-ca5c43466bfc", + "name": "Spawner (2226)", + "location": [1173, 2103, 2], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0caa10e-e991-444f-94e5-66eca46e3520", + "name": "Spawner (2226)", + "location": [1070, 2069, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b45e7a5-6dad-4ddf-9d4e-75953242e058", + "name": "Spawner (2226)", + "location": [958, 2074, 1], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63a8dea7-3158-4ccf-8312-d60bab954514", + "name": "Spawner (2226)", + "location": [999, 1839, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "374ade5f-0b52-4d90-9695-394ffa268943", + "name": "Spawner (2226)", + "location": [1011, 1945, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23cb3cb9-b698-4de3-a49f-bd3ed42cac7c", + "name": "Spawner (2226)", + "location": [1229, 1655, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "edaec946-a62e-487f-82af-b30ccc8f0ccf", + "name": "Spawner (2226)", + "location": [1725, 1447, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12eea555-de46-4ae2-ad79-fb7b8a3f9053", + "name": "Spawner (2226)", + "location": [1798, 1471, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47c42859-f7e9-4eac-bc14-7ca42eba61b9", + "name": "Spawner (2226)", + "location": [1958, 1305, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "14c3e5bd-aae9-410c-9d47-19ead3ef7c10", + "name": "Spawner (2226)", + "location": [1867, 1244, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4e149dd8-9b15-43fb-9b8e-c7fd5974db3e", + "name": "Spawner (2226)", + "location": [1865, 1379, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8f29ae6e-ad1a-4e16-9ce6-5ddeec18003f", + "name": "Spawner (2226)", + "location": [1769, 1330, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05fcd31c-d53e-417b-8798-65d1e7c2b5de", + "name": "Spawner (2226)", + "location": [1666, 1290, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b214fa51-5b5f-466a-8c69-02bfb22af5c7", + "name": "Spawner (2226)", + "location": [1486, 1301, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb3efa23-3330-4812-aa08-ee1a5d9b6b61", + "name": "Spawner (2226)", + "location": [1351, 1359, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0632e64-37a8-4ea1-9c44-189db60d2285", + "name": "Spawner (2226)", + "location": [1231, 1462, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94f6e27d-8653-4b55-86d0-cc131a38aa4b", + "name": "Spawner (2226)", + "location": [1017, 1726, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ace2e1ec-f2fe-48b9-8cdd-1cf2482169ea", + "name": "Spawner (2226)", + "location": [398, 1305, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "450d4409-e114-42c1-84a7-9cd9e0c5f69e", + "name": "Spawner (2226)", + "location": [485, 1398, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be547090-d9da-4bc6-89bc-4dc712ef42af", + "name": "Spawner (2226)", + "location": [210, 1447, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "476014f7-4611-401d-bfc1-b5ca816ddd7d", + "name": "Spawner (2226)", + "location": [851, 1770, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "35bbcb04-b782-4975-90b0-fe6118462b3e", + "name": "Spawner (2226)", + "location": [977, 1638, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b67aaf83-665d-4d9d-95e4-af3072de86cc", + "name": "Spawner (2226)", + "location": [919, 1718, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79cda4aa-4199-4530-afd9-a41ce93c93b5", + "name": "Spawner (2226)", + "location": [876, 1639, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7305a62d-7a0b-4d9e-88e4-a2d3b0d25462", + "name": "Spawner (2226)", + "location": [805, 1570, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1956dff5-b7f6-477e-8fb2-5fd6840f0c8e", + "name": "Spawner (2226)", + "location": [725, 1483, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a232c41-76f0-4175-bb91-8dfcc2dfd800", + "name": "Spawner (2226)", + "location": [623, 1328, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2ec7572-57d8-4788-878c-8b9b0ac1f6a3", + "name": "Spawner (2226)", + "location": [727, 1394, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9d1f842-8619-4a7f-93ec-2125ef399296", + "name": "Spawner (2226)", + "location": [1020, 1360, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "92ff3311-a6f6-47a8-8069-9738fb0264d6", + "name": "Spawner (2226)", + "location": [1024, 1244, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "821ef012-218a-4691-882b-799f3eef1307", + "name": "Spawner (2226)", + "location": [802, 1280, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e93c6ddc-5771-42f2-8e50-fc5ca03b13fd", + "name": "Spawner (2226)", + "location": [855, 934, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2d5617a-b4c1-43dc-bed5-1258240f91b2", + "name": "Spawner (2226)", + "location": [771, 1013, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01728819-da08-4e42-9763-39cdf14b7707", + "name": "Spawner (2226)", + "location": [846, 1071, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "441009bc-1c05-4252-bac0-4dfd6d1b57a6", + "name": "Spawner (2226)", + "location": [940, 1028, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6499e448-465f-440e-8425-d8866179eb24", + "name": "Spawner (2226)", + "location": [1152, 812, 5], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c7aee851-a76e-47a0-a0c6-e55ee5d71208", + "name": "Spawner (2226)", + "location": [1011, 896, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "576c5f8a-deef-4600-aabc-b0494de12d9c", + "name": "Spawner (2226)", + "location": [1057, 765, 1], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9252566d-4ebe-4f24-84da-fd9b05dd8694", + "name": "Spawner (2226)", + "location": [974, 374, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d1921647-6d81-41cc-8956-618e06170bad", + "name": "Spawner (2226)", + "location": [1063, 541, 16], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4a6db11-d166-4762-869e-9d2a74168a90", + "name": "Spawner (2226)", + "location": [1018, 629, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "226ea014-96c5-4281-8c3c-6a1742392ac9", + "name": "Spawner (2226)", + "location": [951, 542, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f79d72b-455f-483f-b33f-eec6d1c50bc2", + "name": "Spawner (2226)", + "location": [805, 576, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8106158-5efa-4600-8c35-ee6a1178c0b3", + "name": "Spawner (2226)", + "location": [937, 743, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba4b69f8-15fe-4ca3-9c03-9a9551510113", + "name": "Spawner (2226)", + "location": [890, 657, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "260110a5-7142-4fb3-b18b-002b831bc2fa", + "name": "Spawner (2226)", + "location": [895, 830, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5cfeda8-987a-4c07-a165-dace4a70402d", + "name": "Spawner (2226)", + "location": [689, 753, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76aadaf7-2302-4077-96f3-e6d122b18000", + "name": "Spawner (2226)", + "location": [748, 669, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a54dc067-1553-42d8-9c2b-d4029bd44f24", + "name": "Spawner (2226)", + "location": [764, 822, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f7ac9eaa-2936-48d1-a3a7-e7de0d1e9293", + "name": "Spawner (2226)", + "location": [834, 751, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ef9fd31-972f-43c0-aa33-ed94fab149c2", + "name": "Spawner (2226)", + "location": [782, 1156, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15fc7f5b-6386-400e-9490-2d3fbfebf96d", + "name": "Spawner (2226)", + "location": [887, 1223, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d906a602-3187-4a61-84f3-ef94a6371a4f", + "name": "Spawner (2226)", + "location": [916, 1159, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df86c69d-e7c3-40e3-b819-8d425587d46b", + "name": "Spawner (2226)", + "location": [993, 1130, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a96f7041-620d-4f55-8856-106b2d8ecb5a", + "name": "Spawner (2226)", + "location": [1047, 1071, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3864b7b7-57d3-43ac-9105-d330ba4b32c9", + "name": "Spawner (2226)", + "location": [1092, 1010, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2248238d-727b-42d6-a39b-267d253d3c1e", + "name": "Spawner (2226)", + "location": [1172, 974, 2], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "622a57d0-5378-4a17-aee9-2203afc201ce", + "name": "Spawner (2226)", + "location": [1388, 784, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc8cd7ae-3f79-400b-b3b6-3e3c630f6198", + "name": "Spawner (2226)", + "location": [1631, 640, 22], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93f49e99-b7cc-427f-a04e-93aa82f9e20a", + "name": "Spawner (2226)", + "location": [1526, 960, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7f279f10-c41b-43f4-97ee-66aa3cda4c07", + "name": "Spawner (2226)", + "location": [1545, 813, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04263280-45f5-494d-9d8d-d36726bde577", + "name": "Spawner (2226)", + "location": [1695, 809, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb35b29d-953d-4d35-88f9-becfbcd3c6d6", + "name": "Spawner (2226)", + "location": [1728, 691, 6], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fcdb223f-fd95-481f-82a7-b8f7d29ebfed", + "name": "Spawner (2226)", + "location": [1802, 495, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e12e7bc4-f8df-4f78-bd8e-84ccec108eec", + "name": "Spawner (2226)", + "location": [1857, 600, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e80aa0d0-bfd3-42cd-b3e7-3f341aaed7a4", + "name": "Spawner (2226)", + "location": [1884, 735, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b8e890c-1f24-4b07-b0a8-3c0160b64932", + "name": "Spawner (2226)", + "location": [1973, 751, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1fd0dfb-3904-4170-b3a9-e85c2aef2021", + "name": "Spawner (2226)", + "location": [2085, 658, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "18abaf3e-2dd3-4f3c-afcd-21feb5e65b44", + "name": "Spawner (2226)", + "location": [2367, 748, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d720e7e8-da12-4afe-b297-164dcaa76804", + "name": "Spawner (2226)", + "location": [2289, 917, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d6b8996-a8c3-4110-9973-14084c70d0b9", + "name": "Spawner (2226)", + "location": [2338, 989, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b573305-001f-487c-9dc0-8411bb238161", + "name": "Spawner (2226)", + "location": [2418, 999, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6193d4d1-91f8-4237-b7eb-28d1217335bc", + "name": "Spawner (2226)", + "location": [2495, 978, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "689f30bd-7852-427e-8327-332dcfc9cbc3", + "name": "Spawner (2226)", + "location": [2583, 993, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "adcf2529-b297-4c4e-b1b9-8319df439c20", + "name": "Spawner (2226)", + "location": [2513, 782, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bb381bc-3862-4e05-84e8-2e13c62451b8", + "name": "Spawner (2226)", + "location": [2748, 660, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4140697-856a-4860-ab7d-30180de15320", + "name": "Spawner (2226)", + "location": [2765, 754, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d98da32-7abe-4c6e-bbed-71e5a727f5a9", + "name": "Spawner (2226)", + "location": [2668, 1020, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e68886a7-ef39-46d9-ac84-bbc0f201c5a1", + "name": "Spawner (2226)", + "location": [2669, 922, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21609f91-35f1-483c-9d4f-970c07c7499b", + "name": "Spawner (2226)", + "location": [2672, 814, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6aab7562-4d73-4806-b582-f8c32251e4fd", + "name": "Spawner (2226)", + "location": [2643, 728, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ef19957-7ff2-4d70-8ada-ad57fcbda8a7", + "name": "Spawner (2226)", + "location": [2888, 391, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1616a270-482b-465c-b9a8-f8103ba729b4", + "name": "Spawner (2226)", + "location": [2735, 417, 15], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c67ab1cc-072c-4579-9c12-beb4b562aaac", + "name": "Spawner (2226)", + "location": [2914, 547, 0], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fe8f267-3962-4c07-a7dd-23dc702c4830", + "name": "Spawner (2226)", + "location": [2814, 492, 15], + "map": "Felucca", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f64e4426-d22e-4091-bb29-227354ba124a", + "name": "Spawner (2226)", + "location": [2688, 524, 11], + "map": "Felucca", "count": 19, "minDelay": "00:05:00", "maxDelay": "00:10:00", diff --git a/Distribution/Data/Spawns/uoml/felucca/Wrong.json b/Distribution/Data/Spawns/uoml/felucca/Wrong.json new file mode 100644 index 000000000..a0b36f171 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/felucca/Wrong.json @@ -0,0 +1,161 @@ +[ + { + "type": "Spawner", + "guid": "29168b55-1571-4743-9178-01314586cbe5", + "name": "Spawner (2227)", + "location": [5819, 591, 0], + "map": "Felucca", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "JukaWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "419487ab-4c4b-4726-8f93-0cd1b49aed0d", + "name": "Spawner (2227)", + "location": [5808, 588, 12], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84d09a21-c001-44ed-9c8a-2e7791fa4b1e", + "name": "Spawner (2227)", + "location": [5792, 544, 10], + "map": "Felucca", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 30, + "entries": [ + { "name": "GolemController", "maxCount": 4, "probability": 100 }, + { "name": "Golem", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63285a8f-9f0d-4966-9f0e-a7fa33fe3a45", + "name": "Spawner (2227)", + "location": [5857, 586, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dfb2dd7e-c751-49e6-9059-25b892efce67", + "name": "Spawner (2227)", + "location": [5857, 562, 15], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41bd23d3-0a22-41fd-a2d2-5d6d809b2018", + "name": "Spawner (2227)", + "location": [5828, 530, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "346ff700-516b-4788-85a2-9ee7146726f9", + "name": "Spawner (2227)", + "location": [5659, 563, 20], + "map": "Felucca", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Brigand", "maxCount": 4, "probability": 100 }, + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "87ed7838-1bc6-4987-bd1e-0cc16bd54f32", + "name": "Spawner (2227)", + "location": [5724, 561, 20], + "map": "Felucca", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 30, + "entries": [ + { "name": "Golem", "maxCount": 1, "probability": 100 }, + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ce378dd-3952-4fac-a5fe-ad12a7b09f20", + "name": "Spawner (2227)", + "location": [5690, 534, 0], + "map": "Felucca", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Ancientlair.json b/Distribution/Data/Spawns/uoml/ilshenar/Ancientlair.json new file mode 100644 index 000000000..21fc715c9 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Ancientlair.json @@ -0,0 +1,101 @@ +[ + { + "type": "Spawner", + "guid": "cbb2e5a6-ad0e-4bf3-b53d-528756994f1b", + "name": "Spawner (2301)", + "location": [50, 689, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:10:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 15, + "entries": [{ "name": "AncientWyrm", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6c0fa0c8-c4fb-4c89-a310-528a89dc22e9", + "name": "Spawner (2301)", + "location": [115, 716, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "FireElemental", "maxCount": 3, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f1df533-f0b6-4d42-8c80-ebf852a0830b", + "name": "Spawner (2301)", + "location": [131, 78, 0], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60859b12-15e4-4d3f-bbc6-b48c940c4080", + "name": "Spawner (2301)", + "location": [54, 718, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fdfe8209-e586-454c-aa1e-fdabf9b43125", + "name": "Spawner (2301)", + "location": [82, 718, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "FireElemental", "maxCount": 2, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "113fa6c7-b58a-48eb-8cd8-18332e92e22e", + "name": "Spawner (2301)", + "location": [79, 687, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Ankh.json b/Distribution/Data/Spawns/uoml/ilshenar/Ankh.json new file mode 100644 index 000000000..1f2556981 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Ankh.json @@ -0,0 +1,439 @@ +[ + { + "type": "Spawner", + "guid": "e3b4ec7a-974a-42ef-b9be-80b93317c858", + "name": "Spawner (2302)", + "location": [129, 1530, -28], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 2, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6fa3458d-ad0c-4ec0-a058-1d831df7b112", + "name": "Spawner (2302)", + "location": [71, 1521, -28], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 40, + "entries": [ + { "name": "HellHound", "maxCount": 2, "probability": 100 }, + { "name": "Imp", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c37e0854-914d-46a7-850f-4b8072add151", + "name": "Spawner (2302)", + "location": [87, 1471, -22], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "EvilMage", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb063844-675e-4177-9da9-7e3c1d2704af", + "name": "Spawner (2302)", + "location": [63, 1471, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "EvilMage", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "32d38b45-94a0-429d-b06d-da2941222110", + "name": "Spawner (2302)", + "location": [39, 1491, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Daemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9debe6c0-b766-4c9a-94c7-2f4db1d1a7fd", + "name": "Spawner (2302)", + "location": [34, 1462, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "BloodElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "250608fe-f32f-472e-a726-d5e1e3924160", + "name": "Spawner (2302)", + "location": [15, 1503, -27], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2de1d85a-a211-4c0d-8cdb-3820a2f4cde3", + "name": "Spawner (2302)", + "location": [62, 1446, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "EvilMage", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e87e63f3-2c4e-4a95-ad84-7cae33a9071b", + "name": "Spawner (2302)", + "location": [31, 1410, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 11, + "entries": [{ "name": "SkeletalKnight", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "675f68f6-58e7-4a9c-adef-31aa0ecdbc00", + "name": "Spawner (2302)", + "location": [86, 1447, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "Mummy", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "94a6eb04-35cb-49c0-8697-ac3caf736e1a", + "name": "Spawner (2302)", + "location": [124, 1435, -16], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "SkeletalMage", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc88bc1a-1266-4ae7-ad00-50ecb32896ff", + "name": "Spawner (2302)", + "location": [124, 1436, -16], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed586ce9-3e23-403c-b92a-013f2e88fb3c", + "name": "Spawner (2302)", + "location": [107, 1390, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40c9f7b2-9ce5-4c7c-ba9c-d78cbd9a7eab", + "name": "Spawner (2302)", + "location": [131, 1331, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd6c1c50-ee38-412c-8957-3f74936e8020", + "name": "Spawner (2302)", + "location": [132, 1332, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Zombie", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5644d83b-87a0-46aa-a23d-5166d9d4f2f7", + "name": "Spawner (2302)", + "location": [55, 1303, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [ + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01be3c7a-f159-4721-aa38-5861f526d3b8", + "name": "Spawner (2302)", + "location": [45, 905, -29], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12ed93c0-e81b-4de2-afe6-0c5c34975a41", + "name": "Spawner (2302)", + "location": [49, 846, -30], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "PoisonElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e73b0e45-8071-4f7a-8126-d3b05a6ac95e", + "name": "Spawner (2302)", + "location": [114, 908, -26], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "EarthElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9a01a01c-2e9f-46ad-b9a9-0d1af7d3b211", + "name": "Spawner (2302)", + "location": [102, 959, -43], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "PoisonElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "192e009f-b329-47a3-b290-cd1af4504db6", + "name": "Spawner (2302)", + "location": [148, 953, -29], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Kirin", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a474135e-6997-45bf-a865-77ca9c5c854b", + "name": "Spawner (2302)", + "location": [29, 1009, -27], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 35, + "entries": [{ "name": "Dragon", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5c977889-217c-4562-bc51-d549a3ce616d", + "name": "Spawner (2302)", + "location": [29, 1016, -27], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 35, + "entries": [{ "name": "Drake", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b88fa850-e3c3-468e-b8e4-afc1430e5ad6", + "name": "Spawner (2302)", + "location": [87, 1051, -29], + "map": "Ilshenar", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [ + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9bca331b-b283-436c-b46b-446d69e18593", + "name": "Spawner (2302)", + "location": [108, 1110, -27], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c5dbed97-1066-4bcc-8b21-cffeb33843ad", + "name": "Spawner (2302)", + "location": [99, 1158, -23], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "AcidElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1146a823-b233-4508-89f4-802dc5f67112", + "name": "Spawner (2302)", + "location": [66, 1171, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Nightmare", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e4279b1a-4375-4a93-9ed5-df2a8f9ade4a", + "name": "Spawner (2302)", + "location": [438, 1552, -28], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Pixie", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f444295e-dedd-4070-8386-54b35d4ac4bb", + "name": "Spawner (2302)", + "location": [451, 1552, -27], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Wisp", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "94b96b34-65e4-4af5-a3cd-56c2c8e8478f", + "name": "Spawner (2302)", + "location": [473, 1552, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "SerpentineDragon", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Blood.json b/Distribution/Data/Spawns/uoml/ilshenar/Blood.json new file mode 100644 index 000000000..a7572870e --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Blood.json @@ -0,0 +1,257 @@ +[ + { + "type": "Spawner", + "guid": "b36243ef-5201-4922-972a-8e0dd78ffe11", + "name": "Spawner (2303)", + "location": [2114, 846, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1a119f51-d328-4a35-bda3-d63ca1d2f3f0", + "name": "Spawner (2303)", + "location": [2051, 837, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8540d8e1-53c7-416b-b611-94127a44d56c", + "name": "Spawner (2303)", + "location": [2050, 859, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "HellCat", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e60ce70-cd00-4d8d-82fb-0733b5b06c32", + "name": "Spawner (2303)", + "location": [2090, 868, -14], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "HellCat", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d0c57916-16d8-4135-ba77-c43c93947a9d", + "name": "Spawner (2303)", + "location": [2177, 836, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d083d0d-b124-4567-96ec-6a35ac8cbc06", + "name": "Spawner (2303)", + "location": [2177, 858, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b73a5bca-9065-495e-8801-7b776732c00b", + "name": "Spawner (2303)", + "location": [2138, 868, -14], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49e5b4ac-570c-43eb-85cc-8c113c5bea7b", + "name": "Spawner (2303)", + "location": [2138, 868, -14], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f6a7fef-6d82-4d17-8505-0927a56a3302", + "name": "Spawner (2303)", + "location": [2083, 911, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a670a008-155a-4420-ab3c-e15e3e951f83", + "name": "Spawner (2303)", + "location": [2115, 917, -23], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1e39d497-d9f3-48ba-8c6a-af1bf7a9ba3d", + "name": "Spawner (2303)", + "location": [2145, 911, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2eada9f7-16d7-4d0e-9418-9e013208bf4f", + "name": "Spawner (2303)", + "location": [2170, 920, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 32, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7f72864a-7c9b-41ce-abda-a205be287234", + "name": "Spawner (2303)", + "location": [2058, 907, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d08b49a2-1291-4d12-a97f-50aee1c289ff", + "name": "Spawner (2303)", + "location": [2058, 932, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8b88f31e-d1c6-4a32-b719-2062d630872f", + "name": "Spawner (2303)", + "location": [2083, 951, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Daemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ac12b1c2-8db2-4664-91db-7381d677eb02", + "name": "Spawner (2303)", + "location": [2114, 951, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Daemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2dbb818a-3f08-4752-bfb0-414d433fa5d3", + "name": "Spawner (2303)", + "location": [2146, 951, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Daemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a8ab857-7a3d-4cd4-9466-567fd9d0b3ea", + "name": "Spawner (2303)", + "location": [2113, 1017, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "Succubus", "maxCount": 2, "probability": 100 }, + { "name": "HellCat", "maxCount": 4, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Exodus.json b/Distribution/Data/Spawns/uoml/ilshenar/Exodus.json new file mode 100644 index 000000000..e02285dd2 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Exodus.json @@ -0,0 +1,342 @@ +[ + { + "type": "Spawner", + "guid": "8e098781-936b-42cc-adfa-806111b9a9f2", + "name": "Spawner (2304)", + "location": [1980, 116, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "EnslavedGargoyle", "maxCount": 2, "probability": 100 }, + { "name": "GolemController", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d39e9374-f30b-488c-8a12-5f3a6315f391", + "name": "Spawner (2304)", + "location": [1939, 116, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "ExodusMinion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "35394aa1-6039-446f-b247-51d9f7926f6c", + "name": "Spawner (2304)", + "location": [1894, 116, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [ + { "name": "GolemController", "maxCount": 2, "probability": 100 }, + { "name": "ExodusOverseer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a35646cf-0460-48d2-a4ea-5e9cc33ad9ab", + "name": "Spawner (2304)", + "location": [1964, 164, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "918f3735-8e70-42c3-a633-2fe03730f22d", + "name": "Spawner (2304)", + "location": [1932, 164, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1156d090-d270-4d2c-99b5-d9801dc4b411", + "name": "Spawner (2304)", + "location": [1932, 196, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "GolemController", "maxCount": 2, "probability": 100 }, + { "name": "Golem", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75b1d8fc-c98d-42c5-9445-12024a38f167", + "name": "Spawner (2304)", + "location": [1895, 160, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleDestroyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2704244a-7822-45bf-bc70-120c145fc097", + "name": "Spawner (2304)", + "location": [1979, 192, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "ExodusOverseer", "maxCount": 2, "probability": 100 }, + { "name": "ExodusMinion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f7bef168-af9c-4bc5-9928-348891d481c0", + "name": "Spawner (2304)", + "location": [1996, 164, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a54ee30-73d0-4f81-8889-ace39a951514", + "name": "Spawner (2304)", + "location": [2032, 184, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleEnforcer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "224edb01-38bc-4e79-abee-c1754f857c1a", + "name": "Spawner (2304)", + "location": [2063, 183, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleEnforcer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dddffe92-2c45-4677-9690-2e782e597530", + "name": "Spawner (2304)", + "location": [2058, 163, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "GargoyleEnforcer", "maxCount": 2, "probability": 100 }, + { "name": "GargoyleDestroyer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2ff6be0-f521-42fa-8cda-0ca0436133d6", + "name": "Spawner (2304)", + "location": [2044, 143, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "GolemController", "maxCount": 3, "probability": 100 }, + { "name": "Golem", "maxCount": 3, "probability": 100 }, + { "name": "ExodusMinion", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98c1814f-9347-402a-93aa-0d3fad5ca8e5", + "name": "Spawner (2304)", + "location": [2071, 116, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "GolemController", "maxCount": 2, "probability": 100 }, + { "name": "ExodusOverseer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c609c03-ea54-4cb3-8e79-ae67bbe813a8", + "name": "Spawner (2304)", + "location": [2044, 87, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "ExodusMinion", "maxCount": 2, "probability": 100 }, + { "name": "Golem", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3d9b4cc-ec56-4fed-a26c-330d2bd4edac", + "name": "Spawner (2304)", + "location": [2031, 48, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleEnforcer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f226735c-6233-4aba-989b-d6f90cd48e2f", + "name": "Spawner (2304)", + "location": [2038, 67, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleDestroyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21934889-6573-468b-b7e9-40d090d3e9e3", + "name": "Spawner (2304)", + "location": [1995, 67, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2cddb981-ac94-4967-9c20-2cc3de1a52c9", + "name": "Spawner (2304)", + "location": [1980, 39, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "ExodusOverseer", "maxCount": 3, "probability": 100 }, + { "name": "ExodusMinion", "maxCount": 3, "probability": 100 }, + { "name": "GolemController", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b44ae0e1-0a06-4385-b550-c72c58b6b41f", + "name": "Spawner (2304)", + "location": [1931, 67, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "412a318b-d693-413c-86e6-92996375f16e", + "name": "Spawner (2304)", + "location": [1896, 72, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "GargoyleDestroyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c6d64523-ac82-4ad3-9f39-f2d80731a5a3", + "name": "Spawner (2304)", + "location": [1932, 35, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "GolemController", "maxCount": 2, "probability": 100 }, + { "name": "Golem", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Mushroom.json b/Distribution/Data/Spawns/uoml/ilshenar/Mushroom.json new file mode 100644 index 000000000..e5cf003e2 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Mushroom.json @@ -0,0 +1,58 @@ +[ + { + "type": "Spawner", + "guid": "46803838-2abe-45af-8c77-4deccf2e99d7", + "name": "Spawner (2305)", + "location": [1444, 1519, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:02:00", + "maxDelay": "00:05:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MushroomTrap", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f5a9bc72-4489-4b3a-83ee-7c476d4c9396", + "name": "Spawner (2305)", + "location": [1441, 1532, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:02:00", + "maxDelay": "00:05:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MushroomTrap", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3e3f2805-df30-4d29-abdd-e8a53002b1d2", + "name": "Spawner (2305)", + "location": [1415, 1498, -27], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:02:00", + "maxDelay": "00:05:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "MushroomTrap", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e7588959-6148-4bf7-a0b1-829b3dca6e1b", + "name": "Spawner (2305)", + "location": [1464, 1495, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:02:00", + "maxDelay": "00:05:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MushroomTrap", "maxCount": 4, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Outdoors.json b/Distribution/Data/Spawns/uoml/ilshenar/Outdoors.json index 12b0cb939..cfd5c1794 100644 --- a/Distribution/Data/Spawns/uoml/ilshenar/Outdoors.json +++ b/Distribution/Data/Spawns/uoml/ilshenar/Outdoors.json @@ -1,18 +1,740 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "bb3daf66-4926-47b9-9113-9bf6e30303f3", + "name": "Spawner (2306)", + "location": [1763, 344, 52], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Dragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fca9b079-0c6f-4836-9860-39e260c7896d", + "name": "Spawner (2306)", + "location": [1790, 435, -10], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [{ "name": "EarthElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40d88f7b-2b85-4fb5-a207-7ce74ecf3f3a", + "name": "Spawner (2306)", + "location": [1805, 469, -9], + "map": "Ilshenar", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Scorpion", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "58561047-30f5-4a7d-9aa9-6430566a554a", + "name": "Spawner (2306)", + "location": [1852, 484, -13], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c73818ae-1121-46ae-972e-b60e6a670889", + "name": "Spawner (2306)", + "location": [1856, 517, -14], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Brigand", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "82fd9f36-5fca-4deb-bbdf-7fe85fba6344", + "name": "Spawner (2306)", + "location": [1775, 530, 66], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "AirElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "82d08b20-2c54-4d66-b165-832eb2d9eefd", + "name": "Spawner (2306)", + "location": [1828, 601, 38], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Wyvern", "maxCount": 2, "probability": 100 }, + { "name": "Mummy", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fbd4d134-ec9e-4642-be28-e1b392df4d52", + "name": "Spawner (2306)", + "location": [1717, 606, 17], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "OrcCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c02c8708-ba44-46e1-9373-c0cce877084f", + "name": "Spawner (2306)", + "location": [1647, 602, -14], + "map": "Ilshenar", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DesertOstard", "maxCount": 5, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 2, "probability": 100 }, + { "name": "Efreet", "maxCount": 1, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fda17c85-19c4-4c90-b848-0c1c135ac5e2", + "name": "Spawner (2306)", + "location": [1593, 602, 7], + "map": "Ilshenar", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DesertOstard", "maxCount": 5, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "Efreet", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1b1ec95-54f3-4f98-a891-4dfd576a0ac6", + "name": "Spawner (2306)", + "location": [1700, 546, 5], + "map": "Ilshenar", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 14, "probability": 100 }, + { "name": "Scorpion", "maxCount": 14, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cebde1e6-ed55-4160-87b6-acf8934a3778", + "name": "Spawner (2306)", + "location": [1580, 538, -14], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 7, + "entries": [ + { "name": "Ratman", "maxCount": 2, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da798d63-b7b0-484f-a345-8f4849216de3", + "name": "Spawner (2306)", + "location": [1580, 554, -13], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 7, + "entries": [ + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bfef4953-b3bc-4ea4-a2f6-0d9885c7a2da", + "name": "Spawner (2306)", + "location": [1534, 631, -14], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "88063e8a-ea72-4a69-945a-274e4cd2f6a2", + "name": "Spawner (2306)", + "location": [1530, 667, -14], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb648bc8-9c84-4454-9770-4d21d69c0774", + "name": "Spawner (2306)", + "location": [1534, 600, -14], + "map": "Ilshenar", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Shade", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Wraith", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eac70a7f-7a65-43b1-9292-599632f696ae", + "name": "Spawner (2306)", + "location": [1445, 681, -1], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Imp", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "628ed55b-7593-471e-8676-3a017e26ea43", + "name": "Spawner (2306)", + "location": [1400, 687, 32], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Harpy", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1df0bbb8-c293-4d0b-8ddc-080926fa4246", + "name": "Spawner (2306)", + "location": [1352, 667, 111], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 22, + "walkingRange": 22, + "entries": [ + { "name": "FrostTroll", "maxCount": 3, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, + { "name": "IceElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3585e152-9781-42d6-8524-88edff5ca30e", + "name": "Spawner (2306)", + "location": [1372, 623, 111], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "IceElemental", "maxCount": 1, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c699c4f-00f9-4e38-b62a-b94f5cfbcffb", + "name": "Spawner (2306)", + "location": [1316, 633, 106], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "FrostTroll", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, + { "name": "IceElemental", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20d82817-a691-4340-b341-3aadd53f0471", + "name": "Spawner (2306)", + "location": [1442, 576, -11], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Shade", "maxCount": 6, "probability": 100 }, + { "name": "Spectre", "maxCount": 6, "probability": 100 }, + { "name": "Wraith", "maxCount": 6, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d92c3d5-24c0-4caa-b732-0acaa35b5a4b", + "name": "Spawner (2306)", + "location": [1400, 572, -13], + "map": "Ilshenar", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Efreet", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ba57b1c-8c9a-4b21-9d44-44a36804e6c5", + "name": "Spawner (2306)", + "location": [1421, 517, 1], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a3db698-ab8a-4794-b798-79dcb81d2229", + "name": "Spawner (2306)", + "location": [1358, 498, 3], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "HealerCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1e5b54c0-d694-4a14-ade1-0da78a1829b8", + "name": "Spawner (2306)", + "location": [1319, 560, -4], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Gargoyle", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9320cae4-3837-4cb8-8ef7-9238f5ca2eae", + "name": "Spawner (2306)", + "location": [1237, 473, -4], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7de1e641-1ffc-4161-b38f-a3730aeab3a2", + "name": "Spawner (2306)", + "location": [1275, 401, -1], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 40, + "entries": [ + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Harpy", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1abdf777-6592-4e3e-8ce2-9863e7975f8d", + "name": "Spawner (2306)", + "location": [1445, 384, 22], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Troll", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9cdf8453-6a90-4b5b-baf9-9698ab361f4a", + "name": "Spawner (2306)", + "location": [1416, 317, 74], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "AncientWyrm", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8ccefff3-a611-4d68-856d-c55d3f0f1c68", + "name": "Spawner (2306)", + "location": [1496, 280, -10], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Imp", "maxCount": 3, "probability": 100 }, + { "name": "Daemon", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a4f9334f-bdb9-411b-b848-4b41043b7a5c", + "name": "Spawner (2306)", + "location": [1464, 241, 2], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Ettin", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b158181c-8e65-42d1-ad74-5f8755e9e21e", + "name": "Spawner (2306)", + "location": [1503, 239, -7], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Ettin", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "38b2eaed-8ee0-4992-bc2d-b0b916e515b2", + "name": "Spawner (2306)", + "location": [1571, 253, 14], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Imp", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04bb184e-3ba8-40ea-90db-59479a0e21a2", + "name": "Spawner (2306)", + "location": [1070, 750, -80], + "map": "Ilshenar", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SewerRat", "maxCount": 4, "probability": 100 }, + { "name": "Corpser", "maxCount": 2, "probability": 100 }, + { "name": "Bogling", "maxCount": 2, "probability": 100 }, + { "name": "GiantRat", "maxCount": 2, "probability": 100 }, + { "name": "Alligator", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1bbb08b1-5a8f-49fa-923e-9e1a7c1a2758", + "name": "Spawner (2306)", + "location": [1070, 750, -80], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 4, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 2, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0bc9fdae-c152-4e2e-a117-f7664d7a7f30", + "name": "Spawner (2306)", + "location": [1135, 743, -80], + "map": "Ilshenar", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SewerRat", "maxCount": 4, "probability": 100 }, + { "name": "Corpser", "maxCount": 2, "probability": 100 }, + { "name": "Bogling", "maxCount": 2, "probability": 100 }, + { "name": "GiantRat", "maxCount": 2, "probability": 100 }, + { "name": "Alligator", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50392cce-759c-41f2-8ceb-926f00d1a455", + "name": "Spawner (2306)", + "location": [1135, 743, -80], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 4, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 2, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "BogThing", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "16b1d51f-a63a-402c-baf0-fccb22964b64", + "name": "Spawner (2306)", + "location": [1160, 681, -80], + "map": "Ilshenar", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SewerRat", "maxCount": 4, "probability": 100 }, + { "name": "Corpser", "maxCount": 2, "probability": 100 }, + { "name": "Bogling", "maxCount": 1, "probability": 100 }, + { "name": "GiantRat", "maxCount": 2, "probability": 100 }, + { "name": "Alligator", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "209ff492-213a-4e4f-bc0f-e00be14de63a", + "name": "Spawner (2306)", + "location": [1160, 681, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2078f000-de9a-492b-a35f-fa4a8cdff17c", + "name": "Spawner (2306)", + "location": [1097, 702, -79], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 7, + "entries": [ + { "name": "GolemController", "maxCount": 1, "probability": 100 }, + { "name": "Golem", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6753c19-0c02-4974-8f9a-ec93fb28116e", + "name": "Spawner (2306)", + "location": [1087, 698, -80], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 7, + "entries": [ + { "name": "GolemController", "maxCount": 1, "probability": 100 }, + { "name": "Golem", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e15d8287-9350-468d-8144-dd3baa906a53", + "name": "Spawner (2306)", + "location": [1086, 702, -60], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "GolemController", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "7431d589-5e24-4f05-b479-30dc17c7e942", - "name": "Spawner (306)", + "name": "Spawner (2306)", "location": [1098, 698, -60], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 1097, "y": 697, "z": -65 }, - "end": { "x": 1099, "y": 699, "z": -45 } - }, + "homeRange": 1, "walkingRange": 5, "entries": [ { "name": "GolemController", "maxCount": 1, "probability": 100 }, @@ -20,60 +742,2425 @@ ] }, { - "$type": "Spawner", - "guid": "8ed26f0f-de07-4412-bc99-6cee204f1f40", - "name": "Spawner (306)", - "location": [1072, 651, -60], - "map": "Ilshenar", - "count": 1, - "minDelay": "00:05:00", - "maxDelay": "00:10:00", - "team": 0, - "spawnBounds": { - "start": { "x": 1066, "y": 645, "z": -65 }, - "end": { "x": 1078, "y": 657, "z": -45 } - }, - "walkingRange": 6, - "entries": [ - { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } - ] - }, - { - "$type": "Spawner", + "type": "Spawner", "guid": "d7d89534-d330-4b6a-a6b0-83187bf9ff54", - "name": "Spawner (306)", + "name": "Spawner (2306)", "location": [1091, 702, -40], "map": "Ilshenar", "count": 1, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 1086, "y": 697, "z": -45 }, - "end": { "x": 1096, "y": 707, "z": -25 } - }, + "homeRange": 5, "walkingRange": 10, - "entries": [ - { "name": "Golem", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Golem", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", - "guid": "e15d8287-9350-468d-8144-dd3baa906a53", - "name": "Spawner (306)", - "location": [1086, 702, -60], + "type": "Spawner", + "guid": "126847de-2d7c-49ce-baba-7939b13f5751", + "name": "Spawner (2306)", + "location": [1084, 675, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [{ "name": "JukaMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "78364853-0fe1-4bc7-b3e9-2c8cb742514c", + "name": "Spawner (2306)", + "location": [1059, 675, -79], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9066bb92-197e-4a1d-bc36-61ead0f50a79", + "name": "Spawner (2306)", + "location": [1060, 659, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4fbf340-bb61-47b8-8e2f-ac6ed604214a", + "name": "Spawner (2306)", + "location": [1060, 643, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6a7e29f4-5aa1-4106-94df-76d248d282f4", + "name": "Spawner (2306)", + "location": [1075, 628, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [{ "name": "JukaMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "85ff24c5-747c-429c-ac5b-288a259572f4", + "name": "Spawner (2306)", + "location": [1092, 628, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b30f735e-cc37-4cae-b3a2-4bd44bf5441e", + "name": "Spawner (2306)", + "location": [1083, 638, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "caf251aa-ecef-47a2-9671-c040818d4ea6", + "name": "Spawner (2306)", + "location": [1095, 638, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "JukaMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ec64bd53-d037-42d7-941a-aa2a0da52ce7", + "name": "Spawner (2306)", + "location": [1095, 664, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0636eb85-29c6-4f6c-b3fa-56930c3ab4b0", + "name": "Spawner (2306)", + "location": [1083, 664, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "JukaLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1bf4cbee-2835-4c1e-aa54-49f61260836e", + "name": "Spawner (2306)", + "location": [1070, 639, -79], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "JukaLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ace0f33f-dea2-40b1-a1d8-5d8404cdc903", + "name": "Spawner (2306)", + "location": [1070, 650, -79], "map": "Ilshenar", "count": 2, "minDelay": "00:05:00", "maxDelay": "00:10:00", "team": 0, - "spawnBounds": { - "start": { "x": 1081, "y": 697, "z": -65 }, - "end": { "x": 1091, "y": 707, "z": -45 } - }, - "walkingRange": 5, + "homeRange": 5, + "walkingRange": 7, "entries": [ - { "name": "GolemController", "maxCount": 2, "probability": 100 } + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ed26f0f-de07-4412-bc99-6cee204f1f40", + "name": "Spawner (2306)", + "location": [1072, 651, -60], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "87acf4d6-d8a3-4302-82e7-da7e4bc8b040", + "name": "Spawner (2306)", + "location": [1083, 628, -60], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [{ "name": "JukaWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4f9a9db0-6b48-4134-be58-16590bbcd28d", + "name": "Spawner (2306)", + "location": [1107, 627, -59], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [{ "name": "JukaMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dc6d3f64-16bf-4643-af4d-d4806ec1fc80", + "name": "Spawner (2306)", + "location": [1083, 675, -60], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [{ "name": "Betrayer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "37253fe5-959b-45cf-88ab-8b6551fbbab7", + "name": "Spawner (2306)", + "location": [1088, 661, -60], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 9, + "walkingRange": 10, + "entries": [ + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 1, "probability": 100 }, + { "name": "Juggernaut", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f2b8727-6b0d-4ce1-97f5-6893895a89bd", + "name": "Spawner (2306)", + "location": [1090, 646, -60], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 10, + "entries": [ + { "name": "Betrayer", "maxCount": 1, "probability": 100 }, + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd17296a-77d8-4eef-9e9d-c042d03c928b", + "name": "Spawner (2306)", + "location": [1088, 637, -60], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "JukaMage", "maxCount": 2, "probability": 100 }, + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "efa8acca-e72d-4672-882b-c27f2506ed03", + "name": "Spawner (2306)", + "location": [1060, 652, -40], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [{ "name": "Betrayer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fab56f4a-1933-48ac-adb6-d1771228cb00", + "name": "Spawner (2306)", + "location": [1083, 651, -40], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Betrayer", "maxCount": 1, "probability": 100 }, + { "name": "GolemController", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "92d313e6-e98e-4803-b61b-faea999cd89f", + "name": "Spawner (2306)", + "location": [1083, 651, -40], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Juggernaut", "maxCount": 3, "probability": 100 }, + { "name": "Golem", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ccbd8610-d346-4117-a518-9df86604bde4", + "name": "Spawner (2306)", + "location": [1084, 652, -20], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "GolemController", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "99def11e-ee3f-440e-bafb-2a0b3c89d0c9", + "name": "Spawner (2306)", + "location": [1772, 67, -28], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "MeerWarrior", "maxCount": 3, "probability": 100 }, + { "name": "MeerMage", "maxCount": 2, "probability": 100 }, + { "name": "MeerCaptain", "maxCount": 2, "probability": 100 }, + { "name": "MeerEternal", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b7a6b349-9426-4332-8fca-238370a60f3b", + "name": "Spawner (2306)", + "location": [1803, 66, -28], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "MeerWarrior", "maxCount": 4, "probability": 100 }, + { "name": "MeerMage", "maxCount": 2, "probability": 100 }, + { "name": "MeerCaptain", "maxCount": 1, "probability": 100 }, + { "name": "MeerEternal", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0a1a3db-b946-47b0-9be9-26093c01cbef", + "name": "Spawner (2306)", + "location": [855, 464, -2], + "map": "Ilshenar", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 6, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 }, + { "name": "Zombie", "maxCount": 3, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d5cf478e-ed02-4bfb-943c-7b22d9d28b1b", + "name": "Spawner (2306)", + "location": [1041, 272, 56], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "413d50ce-9d24-4ed3-ae07-77b1fe16f29a", + "name": "Spawner (2306)", + "location": [1041, 272, 56], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Wraith", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Ghoul", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6e75609b-a0f2-4f77-8ea2-63bdbb1b24a8", + "name": "Spawner (2306)", + "location": [1000, 272, 56], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "DreadSpider", "maxCount": 1, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 1, "probability": 100 }, + { "name": "ElderGazer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e4a4532-780a-4b55-ac97-e1dc5f3f816a", + "name": "Spawner (2306)", + "location": [1000, 272, 56], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Wraith", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Ghoul", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "16fb0dfe-0333-467f-a29e-395067847379", + "name": "Spawner (2306)", + "location": [748, 492, -64], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "649e26f7-54c4-42ce-a8f4-7a755cc361f3", + "name": "Spawner (2306)", + "location": [783, 448, -47], + "map": "Ilshenar", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 2, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0ab9d3c1-154c-4ceb-9fcc-cb2142950550", + "name": "Spawner (2306)", + "location": [783, 448, -47], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abd36839-1bf9-4879-b41e-6857521a0a28", + "name": "Spawner (2306)", + "location": [742, 440, -59], + "map": "Ilshenar", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 2, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "089d463b-27b6-4f55-84f7-e72bb08fcb58", + "name": "Spawner (2306)", + "location": [742, 440, -59], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a3d283e-539d-4a54-8f31-ccfba1a1712e", + "name": "Spawner (2306)", + "location": [756, 208, -40], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Cyclops", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3845912b-63e1-42e8-8574-63bf5c8f3401", + "name": "Spawner (2306)", + "location": [739, 264, -54], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [ + { "name": "Cyclops", "maxCount": 1, "probability": 100 }, + { "name": "Titan", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a38f9e6-156d-4af4-a732-eceae98cb9c1", + "name": "Spawner (2306)", + "location": [778, 264, -38], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [ + { "name": "Cyclops", "maxCount": 1, "probability": 100 }, + { "name": "Titan", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6633838e-e038-4494-a3b9-2b7cb570c675", + "name": "Spawner (2306)", + "location": [511, 545, -60], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "Brigand", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09d2520c-8083-4cd9-ac10-7e8c67cbb01b", + "name": "Spawner (2306)", + "location": [638, 555, -75], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Wyvern", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ac49b12d-1a92-429d-9434-b88fe0ae6bcb", + "name": "Spawner (2306)", + "location": [565, 343, -77], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Wyvern", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0c2ea734-6dea-4648-8c9d-37a5e91eea2b", + "name": "Spawner (2306)", + "location": [489, 488, -62], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3410cc2-aabd-4257-ade9-6866bb429349", + "name": "Spawner (2306)", + "location": [385, 421, -62], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Dragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44dd1dfd-d45f-41a4-a7bb-bd44c257a4f3", + "name": "Spawner (2306)", + "location": [447, 425, -60], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Dragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c6da5392-5d37-43f5-8c50-0331d0bc4190", + "name": "Spawner (2306)", + "location": [445, 366, -66], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Dragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2877d30d-dff4-449d-927f-2c30146d5060", + "name": "Spawner (2306)", + "location": [445, 318, -64], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Dragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0b49d91f-2e00-4a51-bfe4-887dc0648c2f", + "name": "Spawner (2306)", + "location": [370, 264, -52], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2ffa9ab-3ee2-4cea-8a60-b1c14c51a703", + "name": "Spawner (2306)", + "location": [409, 264, -44], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6260f64-4918-4f57-a202-75c21906ebb9", + "name": "Spawner (2306)", + "location": [449, 264, -57], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d561eeb-d2ca-49c5-9abd-02b48dd5d7b4", + "name": "Spawner (2306)", + "location": [964, 638, -80], + "map": "Ilshenar", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 5, "probability": 100 }, + { "name": "Scorpion", "maxCount": 5, "probability": 100 }, + { "name": "SandVortex", "maxCount": 5, "probability": 100 }, + { "name": "Ridgeback", "maxCount": 1, "probability": 100 }, + { "name": "Efreet", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76827ffe-c3f1-4bcf-84fc-b3e2bc796c2e", + "name": "Spawner (2306)", + "location": [981, 470, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3846e700-72a4-4d94-84e5-06b5d9b044c5", + "name": "Spawner (2306)", + "location": [978, 471, -80], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "GolemController", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91d2b43c-a0fe-4613-8cc3-cf9d67f3e5ca", + "name": "Spawner (2306)", + "location": [973, 460, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "LavaLizard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "13bc99c3-a51d-4ca0-b6bc-4c5698870ad0", + "name": "Spawner (2306)", + "location": [964, 456, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e671ad18-2aa3-4188-8cbb-71fc3edcf9fb", + "name": "Spawner (2306)", + "location": [963, 456, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "ExodusOverseer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "42dd61c7-6bec-4781-b24e-6db7864b72a4", + "name": "Spawner (2306)", + "location": [941, 471, -38], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Imp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "757b03a1-ee65-4fa4-8465-cffc7754f9af", + "name": "Spawner (2306)", + "location": [946, 430, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Gargoyle", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff667187-7d72-4573-aa08-064bbe619989", + "name": "Spawner (2306)", + "location": [878, 527, -77], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da3f6bf3-177d-4280-bc57-e0f5b0adb651", + "name": "Spawner (2306)", + "location": [938, 453, -80], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bb89d203-e35e-4b3c-87a0-83ac03e2822f", + "name": "Spawner (2306)", + "location": [960, 524, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2679185-4de7-41dd-a304-27da19abffb2", + "name": "Spawner (2306)", + "location": [911, 502, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "697f7eba-a9f6-4703-800a-44b754370384", + "name": "Spawner (2306)", + "location": [930, 519, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "Imp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2927de7-9e4a-4874-bcb4-857d20ce731b", + "name": "Spawner (2306)", + "location": [808, 781, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "GolemController", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0094215e-9411-4fa1-a091-dbfd46540289", + "name": "Spawner (2306)", + "location": [853, 762, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 17, + "walkingRange": 17, + "entries": [{ "name": "Quagmire", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7513fe51-3c38-4bdd-9462-71b8442352ae", + "name": "Spawner (2306)", + "location": [821, 758, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 17, + "walkingRange": 17, + "entries": [ + { "name": "ChaosDaemon", "maxCount": 1, "probability": 100 }, + { "name": "Moloch", "maxCount": 1, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e4773cbb-3189-4e17-9f1d-5f0828466171", + "name": "Spawner (2306)", + "location": [821, 802, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 17, + "walkingRange": 17, + "entries": [ + { "name": "Quagmire", "maxCount": 1, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 1, "probability": 100 }, + { "name": "ArcaneDaemon", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9addf88a-7b37-49e1-aadc-5779d6eb586d", + "name": "Spawner (2306)", + "location": [858, 799, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 17, + "walkingRange": 17, + "entries": [ + { "name": "ChaosDaemon", "maxCount": 2, "probability": 100 }, + { "name": "Moloch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05ea0169-d712-439a-bbbc-decb66204c70", + "name": "Spawner (2306)", + "location": [879, 781, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "ChaosDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f1220ee4-7be3-40f0-b7eb-11e681dea5ae", + "name": "Spawner (2306)", + "location": [907, 780, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5d4c8c7a-0513-4f06-b84e-85896cd5c5c9", + "name": "Spawner (2306)", + "location": [987, 791, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "63b04be0-c512-4985-b0b2-30ba0b282d84", + "name": "Spawner (2306)", + "location": [275, 453, -53], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "AirElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e1700d3c-9672-4e00-951d-feb0e20c80db", + "name": "Spawner (2306)", + "location": [652, 555, -72], + "map": "Ilshenar", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [ + { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, + { "name": "Gorilla", "maxCount": 3, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca1241f9-a5d2-432f-a4f5-23505d765970", + "name": "Spawner (2306)", + "location": [685, 340, -64], + "map": "Ilshenar", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 125, + "walkingRange": 170, + "entries": [ + { "name": "TropicalBird", "maxCount": 15, "probability": 100 }, + { "name": "Boar", "maxCount": 9, "probability": 100 }, + { "name": "Snake", "maxCount": 9, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 9, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 9, "probability": 100 }, + { "name": "Gorilla", "maxCount": 9, "probability": 100 }, + { "name": "Panther", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "827416b4-3e01-4d6a-9901-2b0cdc57ef3b", + "name": "Spawner (2306)", + "location": [460, 469, -49], + "map": "Ilshenar", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [ + { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, + { "name": "Gorilla", "maxCount": 3, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5056885c-d7a0-4cff-9e18-b2edb0f507fe", + "name": "Spawner (2306)", + "location": [377, 340, -14], + "map": "Ilshenar", + "count": 35, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 150, + "entries": [ + { "name": "TropicalBird", "maxCount": 9, "probability": 100 }, + { "name": "Boar", "maxCount": 5, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 5, "probability": 100 }, + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "Panther", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e13fea92-e3ac-4e7d-bf3a-05d99b66b057", + "name": "Spawner (2306)", + "location": [1083, 968, -38], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "WanderingHealer", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f683a2f2-fab0-4a35-aeed-1d50b9dcf273", + "name": "Spawner (2306)", + "location": [1059, 1153, -24], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "RatmanMage", "maxCount": 2, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 2, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae1cec6c-8c74-4b4b-849f-f482df430969", + "name": "Spawner (2306)", + "location": [1129, 1236, -4], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Brigand", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9a9045a4-e6a0-4c29-85af-6f0f497b6c21", + "name": "Spawner (2306)", + "location": [1186, 1223, -20], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "BankerCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "135c1626-1ac5-4fe6-970c-3bf9d19f588c", + "name": "Spawner (2306)", + "location": [1519, 1019, -6], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "HealerCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb21b810-7b2c-4fd3-9a87-4dfe0671bb1d", + "name": "Spawner (2306)", + "location": [1592, 1191, -20], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Pixie", "maxCount": 5, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "14c41ba9-6903-43dc-bfb0-30b6c10df12f", + "name": "Spawner (2306)", + "location": [1592, 1191, -20], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Unicorn", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c565a927-decf-4326-8213-bcd7efb5fc21", + "name": "Spawner (2306)", + "location": [1480, 1207, -18], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Pixie", "maxCount": 5, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c2b6978-ce19-490d-8d71-7ed2aa368bfd", + "name": "Spawner (2306)", + "location": [1480, 1207, -18], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Unicorn", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aa6761b5-dd89-4700-8c1c-7fafcefcb035", + "name": "Spawner (2306)", + "location": [1318, 1246, -13], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 50, + "entries": [ + { "name": "Pixie", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "53db67c5-be50-4250-bdaa-14d7684f9ee2", + "name": "Spawner (2306)", + "location": [1183, 1287, -30], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [ + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 3, "probability": 100 }, + { "name": "Ghoul", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fbf30316-ff51-4001-84f3-d63501306a7f", + "name": "Spawner (2306)", + "location": [1319, 1331, -14], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Sheep", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49a3f002-3ed6-4e08-b8de-d6ffa7dae59f", + "name": "Spawner (2306)", + "location": [1733, 1161, -5], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Skeleton", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "798e730a-c923-4159-abf5-0c1492331a67", + "name": "Spawner (2306)", + "location": [1747, 1221, -1], + "map": "Ilshenar", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 4, "probability": 100 }, + { "name": "Imp", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cbb2778d-3936-46ea-97eb-f93214859600", + "name": "Spawner (2306)", + "location": [1678, 971, 10], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Harpy", "maxCount": 3, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3a2fccf-7467-45b6-8c92-b18ec3463312", + "name": "Spawner (2306)", + "location": [1674, 1074, 32], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7566467d-86a1-4e77-8d7e-cd2c876f4af2", + "name": "Spawner (2306)", + "location": [1585, 990, 20], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 10, + "entries": [{ "name": "GiantRat", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eaa928d7-cdd8-47cf-97b8-ff8daa37c6f7", + "name": "Spawner (2306)", + "location": [1436, 960, -22], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "Reaper", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1f641e7-bd1c-4706-bc21-1af65e71bbee", + "name": "Spawner (2306)", + "location": [1249, 957, -14], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Pixie", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dd6a1f71-e45b-40c9-ac4c-75cb8a7c2ae3", + "name": "Spawner (2306)", + "location": [1360, 888, -21], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 40, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d12c9543-0c01-47e6-b619-4cdddaba9f1b", + "name": "Spawner (2306)", + "location": [1817, 976, -28], + "map": "Ilshenar", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 45, + "walkingRange": 45, + "entries": [ + { "name": "DreadSpider", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 13, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08173e6f-aebd-4ba2-85ae-6b9390f2d26c", + "name": "Spawner (2306)", + "location": [1504, 878, 10], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "GiantSpider", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5c8e9fc9-aef3-4248-aa1d-829108d3f59b", + "name": "Spawner (2306)", + "location": [664, 668, 5], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 10, + "entries": [ + { "name": "RatmanMage", "maxCount": 1, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f341a81-ed10-448a-8986-9b6fe3f7f42e", + "name": "Spawner (2306)", + "location": [664, 665, -15], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 10, + "entries": [ + { "name": "RatmanArcher", "maxCount": 2, "probability": 100 }, + { "name": "Ratman", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33d32b88-b234-4f29-911b-db8f5008fe7d", + "name": "Spawner (2306)", + "location": [664, 665, -35], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "RatmanMage", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b70b8b97-55cc-4666-b5c0-5088913de58d", + "name": "Spawner (2306)", + "location": [644, 819, -44], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "RatmanMage", "maxCount": 3, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ddb61039-cb35-4e01-92f4-fa49f273db3e", + "name": "Spawner (2306)", + "location": [961, 924, -37], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "BankerCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "286d344f-fbf6-4cea-bd98-66a6ae098622", + "name": "Spawner (2306)", + "location": [567, 807, -74], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "MageCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f5b5861e-4eb1-4ea6-8013-e04ecf78d1d2", + "name": "Spawner (2306)", + "location": [830, 1206, -70], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "MageCamp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6428c823-54f9-4675-9bb9-8c5d4b0a5db0", + "name": "Spawner (2306)", + "location": [485, 891, -82], + "map": "Ilshenar", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 1, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 }, + { "name": "Slime", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9bbfa6b-b223-4fb1-828c-100ca2ceb176", + "name": "Spawner (2306)", + "location": [487, 890, -82], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Jwilson", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4345b3d1-292e-4c01-af9c-e4abe339b8c5", + "name": "Spawner (2306)", + "location": [330, 881, -73], + "map": "Ilshenar", + "count": 17, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 5, "probability": 100 }, + { "name": "Shade", "maxCount": 5, "probability": 100 }, + { "name": "Wraith", "maxCount": 5, "probability": 100 }, + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 2, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 2, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "38b098ef-adb7-4187-9c46-2266659838b7", + "name": "Spawner (2306)", + "location": [325, 893, -72], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "251d70b0-01cd-4817-b072-1dd7dd5ba417", + "name": "Spawner (2306)", + "location": [366, 881, -73], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8148df05-3fd6-4b7e-a052-bc8830e173e5", + "name": "Spawner (2306)", + "location": [338, 863, -72], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "370cf0c4-a6d5-4940-b3bc-5e911aad3b67", + "name": "Spawner (2306)", + "location": [353, 1140, -54], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Shade", "maxCount": 3, "probability": 100 }, + { "name": "Wraith", "maxCount": 3, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f26425c-ee86-48f4-84e8-96c85410fdf7", + "name": "Spawner (2306)", + "location": [354, 1140, -52], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Shade", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4d8998df-e3b7-41c8-9e15-7147a530e05c", + "name": "Spawner (2306)", + "location": [430, 1176, -68], + "map": "Ilshenar", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "SwampTentacle", "maxCount": 4, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 }, + { "name": "Alligator", "maxCount": 14, "probability": 100 }, + { "name": "Reaper", "maxCount": 4, "probability": 100 }, + { "name": "Corpser", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ce8e092-abb1-4db0-ad42-c3cb2d4ab28d", + "name": "Spawner (2306)", + "location": [350, 1204, -38], + "map": "Ilshenar", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "SwampTentacle", "maxCount": 2, "probability": 100 }, + { "name": "GiantToad", "maxCount": 5, "probability": 100 }, + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "Reaper", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abe02eba-8c77-43a4-b81a-88b366c1bbbb", + "name": "Spawner (2306)", + "location": [400, 1334, -37], + "map": "Ilshenar", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "SwampTentacle", "maxCount": 5, "probability": 100 }, + { "name": "GiantToad", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Alligator", "maxCount": 0, "probability": 100 }, + { "name": "Reaper", "maxCount": 0, "probability": 100 }, + { "name": "Corpser", "maxCount": 0, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfb042de-f9a6-49a0-a18d-7ff3067c9c0a", + "name": "Spawner (2306)", + "location": [291, 1313, -25], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Lizardman", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11402edf-4c62-4179-a86a-1f49db2c8b4d", + "name": "Spawner (2306)", + "location": [295, 1348, -26], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Lizardman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cadd8d7c-9d3a-4121-83ae-e2613840894a", + "name": "Spawner (2306)", + "location": [302, 1580, -29], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Lizardman", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cae7914f-420c-4bd4-a325-604479484886", + "name": "Spawner (2306)", + "location": [325, 1576, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [{ "name": "Lizardman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "462ee8dd-f60c-438e-90a7-0bd1b7b2f137", + "name": "Spawner (2306)", + "location": [223, 1360, -15], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "AncientWyrm", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 2, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d26042c9-0119-45e9-a006-0a59d84f338b", + "name": "Spawner (2306)", + "location": [278, 1440, 15], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Efreet", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e733d3be-2373-466b-a904-c90c1f87156c", + "name": "Spawner (2306)", + "location": [314, 1436, 16], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc7cac1d-5957-4d5f-b459-c7379c118384", + "name": "Spawner (2306)", + "location": [350, 1441, 17], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4fa9bb9-65f4-4e28-a0df-c30a910b37bc", + "name": "Spawner (2306)", + "location": [438, 1431, 15], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 2, "probability": 100 }, + { "name": "AirElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60e90560-1527-4f63-b097-6a4ec6c38f8e", + "name": "Spawner (2306)", + "location": [648, 1321, -57], + "map": "Ilshenar", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 }, + { "name": "Imp", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d977c28c-0fde-46fb-ae30-07c36d70f077", + "name": "Spawner (2306)", + "location": [722, 1354, -61], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91bf1d10-57ae-4e79-a7db-66b06c5e1320", + "name": "Spawner (2306)", + "location": [913, 935, -34], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "WaterElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "48fa1520-858d-40ea-b7f0-a1d47fb0ebbc", + "name": "Spawner (2306)", + "location": [963, 1028, -31], + "map": "Ilshenar", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "Reaper", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "Imp", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd8c40d5-7d9f-46f2-86f9-ab58c0ec5d31", + "name": "Spawner (2306)", + "location": [935, 1159, 13], + "map": "Ilshenar", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Imp", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc71a047-fd22-43e8-96bb-953581239b63", + "name": "Spawner (2306)", + "location": [822, 1324, -80], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93695f7d-f83a-4797-a125-6ff28e94c368", + "name": "Spawner (2306)", + "location": [902, 1308, -71], + "map": "Ilshenar", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 40, + "entries": [ + { "name": "Titan", "maxCount": 2, "probability": 100 }, + { "name": "Cyclops", "maxCount": 4, "probability": 100 }, + { "name": "Ettin", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08c9e8fa-503a-4590-ae93-498397e9f798", + "name": "Spawner (2306)", + "location": [646, 1209, -93], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 50, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aabcb1f9-f76a-49db-a0f2-617e9abd2d66", + "name": "Spawner (2306)", + "location": [605, 1061, -84], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 45, + "walkingRange": 55, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a037d753-db69-4c16-8c67-4c1405a4aa0f", + "name": "Spawner (2306)", + "location": [564, 1142, -99], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 45, + "walkingRange": 65, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0608e0a7-41e3-416a-b283-c4c7548ff433", + "name": "Spawner (2306)", + "location": [681, 1125, -73], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 65, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a0bfc1f-d6f8-47a2-9551-62bdea09f4d5", + "name": "Spawner (2306)", + "location": [703, 984, -68], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 65, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "603df694-0c2b-450a-8c18-64b6dbbcbacf", + "name": "Spawner (2306)", + "location": [605, 973, -82], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 65, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d513dab-cb9d-42bc-8e86-ffd715ffc3e5", + "name": "Spawner (2306)", + "location": [514, 1225, -66], + "map": "Ilshenar", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 1, "probability": 100 }, + { "name": "Snake", "maxCount": 2, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 2, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 1, "probability": 100 }, + { "name": "Gorilla", "maxCount": 1, "probability": 100 }, + { "name": "Panther", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1e695260-4946-433b-91c3-0e67eb3434ef", + "name": "Spawner (2306)", + "location": [733, 1238, -68], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 65, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b4c3c4c-418e-46f7-be0f-d39d51e8843a", + "name": "Spawner (2306)", + "location": [397, 958, -80], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 70, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "749fd6cf-bc7c-476e-b125-e980ab8d540e", + "name": "Spawner (2306)", + "location": [468, 874, -80], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 50, + "entries": [ + { "name": "TropicalBird", "maxCount": 4, "probability": 100 }, + { "name": "Boar", "maxCount": 2, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 }, + { "name": "Gorilla", "maxCount": 2, "probability": 100 }, + { "name": "Panther", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01b06a3d-d447-4ada-aa1e-c0ed77f20988", + "name": "Spawner (2306)", + "location": [630, 828, -55], + "map": "Ilshenar", + "count": 25, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 85, + "walkingRange": 110, + "entries": [ + { "name": "TropicalBird", "maxCount": 7, "probability": 100 }, + { "name": "Boar", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 4, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 4, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 4, "probability": 100 }, + { "name": "Gorilla", "maxCount": 4, "probability": 100 }, + { "name": "Panther", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "51c640e7-ecaf-4949-8de8-37b31daaa1b8", + "name": "Spawner (2306)", + "location": [588, 674, -60], + "map": "Ilshenar", + "count": 25, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 75, + "walkingRange": 100, + "entries": [ + { "name": "TropicalBird", "maxCount": 7, "probability": 100 }, + { "name": "Boar", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 4, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 4, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 4, "probability": 100 }, + { "name": "Gorilla", "maxCount": 4, "probability": 100 }, + { "name": "Panther", "maxCount": 3, "probability": 100 } ] } ] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Ratmancave.json b/Distribution/Data/Spawns/uoml/ilshenar/Ratmancave.json new file mode 100644 index 000000000..c2a7a9b98 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Ratmancave.json @@ -0,0 +1,87 @@ +[ + { + "type": "Spawner", + "guid": "46e1d6c5-ebe2-4575-beeb-3e35754caef7", + "name": "Spawner (2307)", + "location": [1310, 1519, -28], + "map": "Ilshenar", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 7, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a96346f4-9aca-4dd6-b1ff-e823d8741a19", + "name": "Spawner (2307)", + "location": [1224, 1524, -28], + "map": "Ilshenar", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SkeletalKnight", "maxCount": 5, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 5, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0182a6f9-bdde-4ca6-95c8-a7c146b1a0e1", + "name": "Spawner (2307)", + "location": [1224, 1494, -28], + "map": "Ilshenar", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SkeletalKnight", "maxCount": 5, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 5, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c6766cd8-89ac-4169-a2d9-8216a74369a4", + "name": "Spawner (2307)", + "location": [1181, 1512, -68], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "SkeletalDragon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2694470a-4c93-488c-96a3-87938993d306", + "name": "Spawner (2307)", + "location": [1171, 1511, -68], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Rock.json b/Distribution/Data/Spawns/uoml/ilshenar/Rock.json new file mode 100644 index 000000000..f61e20ef5 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Rock.json @@ -0,0 +1,221 @@ +[ + { + "type": "Spawner", + "guid": "2481376c-f54f-453e-aa09-b0ae25a2da13", + "name": "Spawner (2308)", + "location": [2188, 310, -7], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Wraith", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93052936-70f8-43b4-a20c-d68adab714c6", + "name": "Spawner (2308)", + "location": [2220, 75, -27], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "AcidElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6d9c7f2b-dc74-4ca3-9396-fb2a38fa2d80", + "name": "Spawner (2308)", + "location": [2207, 113, -27], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "StoneGargoyle", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5c78812b-3143-4ab4-a3e2-950a74181fb1", + "name": "Spawner (2308)", + "location": [2185, 168, -32], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d713f41-daf3-40c4-9d90-bbf4befb1b08", + "name": "Spawner (2308)", + "location": [2229, 143, -32], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [ + { "name": "EvilMage", "maxCount": 2, "probability": 100 }, + { "name": "Executioner", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdba50d6-07c1-40ad-ba33-ac67631712cc", + "name": "Spawner (2308)", + "location": [2154, 163, -27], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc19f651-1312-4542-80df-b9f2362974da", + "name": "Spawner (2308)", + "location": [2125, 165, -32], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [ + { "name": "EvilMage", "maxCount": 2, "probability": 100 }, + { "name": "Executioner", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "151afe47-a4c7-42d9-999b-6be7ab8ed901", + "name": "Spawner (2308)", + "location": [2125, 137, -32], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a6c8beb-1840-4e1a-b55e-b44158315f59", + "name": "Spawner (2308)", + "location": [2138, 97, -27], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "ElderGazer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4f5851f5-110c-4155-91a4-5d6715b52c2f", + "name": "Spawner (2308)", + "location": [2183, 85, -32], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "148839f1-8a5b-44bd-b6f5-3b310de5c951", + "name": "Spawner (2308)", + "location": [2155, 24, -32], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [ + { "name": "PoisonElemental", "maxCount": 2, "probability": 100 }, + { "name": "LichLord", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "92ee0621-2fc1-434c-a2d8-11b9359e01bc", + "name": "Spawner (2308)", + "location": [2124, 96, -32], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 15, + "entries": [ + { "name": "PoisonElemental", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "027c989e-9d77-4dfd-85d8-b7c83f722bde", + "name": "Spawner (2308)", + "location": [2094, 56, -32], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Executioner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f8cc122d-1672-4b14-a6bf-4dcc6178efdc", + "name": "Spawner (2308)", + "location": [2127, 29, -32], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [ + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Sorcerers.json b/Distribution/Data/Spawns/uoml/ilshenar/Sorcerers.json new file mode 100644 index 000000000..896af0f33 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Sorcerers.json @@ -0,0 +1,508 @@ +[ + { + "type": "Spawner", + "guid": "fc22ceb7-01e2-4389-a356-ffc788071f0f", + "name": "Spawner (2309)", + "location": [427, 67, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Zombie", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5e285fc0-52a0-4f23-ad8f-bc7e90b2fcec", + "name": "Spawner (2309)", + "location": [455, 70, -28], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 10, + "entries": [ + { "name": "AcidElemental", "maxCount": 5, "probability": 100 }, + { "name": "FireElemental", "maxCount": 5, "probability": 100 }, + { "name": "BloodElemental", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6433de3a-a4f6-4d51-9249-0dc37a3fe107", + "name": "Spawner (2309)", + "location": [394, 69, -28], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Shade", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72c10d82-2c9a-489b-8bad-28dac3e794c9", + "name": "Spawner (2309)", + "location": [447, 19, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73efa56f-434e-4fc0-8822-c028c9a83e6c", + "name": "Spawner (2309)", + "location": [431, 16, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [ + { "name": "HeadlessOne", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "29b9ab63-8072-4a0e-b8b2-cbdbe54a74c6", + "name": "Spawner (2309)", + "location": [462, 16, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [ + { "name": "HeadlessOne", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "86f27a56-95cc-49ed-8483-38c07cd31de2", + "name": "Spawner (2309)", + "location": [462, 16, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [{ "name": "EarthElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9cebea0f-1699-45af-a9b1-0d79d99d290e", + "name": "Spawner (2309)", + "location": [383, 29, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "271dd12c-8c89-4962-8766-27c6e23ab039", + "name": "Spawner (2309)", + "location": [427, 95, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20ea3ede-9733-4b69-a2a9-44312ad782df", + "name": "Spawner (2309)", + "location": [254, 20, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "27540523-3186-413d-99da-3fd4ef5be98f", + "name": "Spawner (2309)", + "location": [281, 20, -28], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "807fd985-4f3a-4d1f-a7be-942294f1d782", + "name": "Spawner (2309)", + "location": [238, 48, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 10, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Daemon", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bceb873-73d8-4326-8ec1-46bb48c65d60", + "name": "Spawner (2309)", + "location": [214, 67, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 40, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f4631bce-3338-4d0e-88c9-9faa4ba968ef", + "name": "Spawner (2309)", + "location": [262, 84, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bed076f5-fb2b-4342-9423-6a3228cc1978", + "name": "Spawner (2309)", + "location": [262, 84, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "EvilMage", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b6f7c48e-faa6-44a6-b59f-d0b47313a4f6", + "name": "Spawner (2309)", + "location": [278, 53, -28], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "HellHound", "maxCount": 6, "probability": 100 }, + { "name": "BloodElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3aad6c47-815b-4713-93f2-c57156aded30", + "name": "Spawner (2309)", + "location": [305, 20, -28], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "DullCopperElemental", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "99674640-e132-4cdd-a69e-1607e28dc5f3", + "name": "Spawner (2309)", + "location": [310, 92, -13], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "LichLord", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a304cb5f-12b3-401a-96d3-104ee9f33544", + "name": "Spawner (2309)", + "location": [311, 91, -13], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, + { "name": "BloodElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1fb85d78-33ef-42c5-8145-e0af0ec5bd33", + "name": "Spawner (2309)", + "location": [336, 37, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "091c33d5-ee75-471b-aafc-78ea45d3dd66", + "name": "Spawner (2309)", + "location": [336, 48, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ce0e4a2-0318-4e8d-83a6-48933b302425", + "name": "Spawner (2309)", + "location": [336, 42, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 15, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "846a8862-f62b-4814-856b-88a6f9cb0a27", + "name": "Spawner (2309)", + "location": [350, 42, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a1b6deba-e340-4851-bba7-4f5ad2d03353", + "name": "Spawner (2309)", + "location": [146, 83, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "818d30b9-0568-4e69-9b00-a8567e0f63b4", + "name": "Spawner (2309)", + "location": [143, 70, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "73b4ab03-1cab-4034-a3c0-9b8ba61d3600", + "name": "Spawner (2309)", + "location": [160, 59, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "10f6b4be-fd86-480d-b285-d00f0f55b59c", + "name": "Spawner (2309)", + "location": [155, 21, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Balron", "maxCount": 2, "probability": 100 }, + { "name": "ElderGazer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd576d60-5525-4ee3-b85f-8439e6dd38fd", + "name": "Spawner (2309)", + "location": [160, 9, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 5, + "entries": [{ "name": "Efreet", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "65f92121-fb34-4f86-9722-4ee64ec64b61", + "name": "Spawner (2309)", + "location": [109, 44, -28], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 4, "probability": 100 }, + { "name": "Mummy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94e38cf9-78c0-498b-927d-f7151001f811", + "name": "Spawner (2309)", + "location": [67, 70, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "LichLord", "maxCount": 2, "probability": 100 }, + { "name": "Mummy", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37598c17-946c-4a0a-8e86-20d1329e4bca", + "name": "Spawner (2309)", + "location": [133, 114, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "492af6a5-b034-4a8b-aa7b-923b906321e2", + "name": "Spawner (2309)", + "location": [133, 114, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "BloodElemental", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Spectre.json b/Distribution/Data/Spawns/uoml/ilshenar/Spectre.json new file mode 100644 index 000000000..9cba3d62c --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Spectre.json @@ -0,0 +1,23 @@ +[ + { + "type": "Spawner", + "guid": "70e1e800-e6dc-4f5c-a610-269930e6c327", + "name": "Spawner (2310)", + "location": [1981, 1059, -28], + "map": "Ilshenar", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Shade", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Towns.json b/Distribution/Data/Spawns/uoml/ilshenar/Towns.json new file mode 100644 index 000000000..b72094dcc --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Towns.json @@ -0,0 +1,631 @@ +[ + { + "type": "Spawner", + "guid": "1b101011-42fa-41b1-b15c-f15fdcaaf6ec", + "name": "Spawner (2311)", + "location": [1517, 558, 5], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [{ "name": "Skeleton", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "824d3710-eb67-4f27-8917-6debd7c8e2ca", + "name": "Spawner (2311)", + "location": [1517, 552, 10], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "Spectre", "maxCount": 6, "probability": 100 }, + { "name": "Shade", "maxCount": 6, "probability": 100 }, + { "name": "Wraith", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9d3ed0a0-e458-4fa9-bc94-008304706893", + "name": "Spawner (2311)", + "location": [1516, 557, 4], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [ + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4c688bb-cf62-4b3d-84bd-2633333c0242", + "name": "Spawner (2311)", + "location": [1517, 552, 10], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "SkeletalKnight", "maxCount": 3, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 3, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "149dfca3-652c-4777-b005-a0cc95f30d9d", + "name": "Spawner (2311)", + "location": [1517, 541, 11], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c671596f-69b6-45df-a106-b8fce71aa156", + "name": "Spawner (2311)", + "location": [1517, 541, 61], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb1de953-cfdd-46d6-aab9-68cbdfe60780", + "name": "Spawner (2311)", + "location": [1203, 1138, -25], + "map": "Ilshenar", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 55, + "entries": [ + { "name": "MeerWarrior", "maxCount": 16, "probability": 100 }, + { "name": "MeerCaptain", "maxCount": 16, "probability": 100 }, + { "name": "MeerMage", "maxCount": 16, "probability": 100 }, + { "name": "MeerEternal", "maxCount": 16, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef56cf38-1547-41ad-adc4-ba72aa156f87", + "name": "Spawner (2311)", + "location": [1127, 1112, -34], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 35, + "entries": [ + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7f13f3fa-2330-4dd5-9985-164d8e240659", + "name": "Spawner (2311)", + "location": [1180, 1188, -25], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Snake", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93476fd0-4859-4358-90ab-4371f8c1ac40", + "name": "Spawner (2311)", + "location": [1181, 1189, -25], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "GiantSerpent", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6ac6b235-2d7c-4058-bc8d-d7f73cd17d5a", + "name": "Spawner (2311)", + "location": [817, 1065, -30], + "map": "Ilshenar", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 70, + "entries": [ + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "OgreLord", "maxCount": 2, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 5, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0710f4e-9d44-4e1e-be0d-3f560d5f52c6", + "name": "Spawner (2311)", + "location": [1643, 310, 48], + "map": "Ilshenar", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Phoenix", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "HellHound", "maxCount": 5, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 4, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 4, "probability": 100 }, + { "name": "Daemon", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f216ee7b-bb49-415a-aace-924f1778e3cc", + "name": "Spawner (2311)", + "location": [1643, 310, 48], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Succubus", "maxCount": 2, "probability": 100 }, + { "name": "Efreet", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1e1dd613-72d1-4715-b899-1086679533ab", + "name": "Spawner (2311)", + "location": [1665, 244, 93], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "165f16c6-b66f-474a-aed6-0d6c2ee57123", + "name": "Spawner (2311)", + "location": [1715, 228, 78], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Balron", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "34ad74fe-d7eb-4427-8c7e-e7658bd63156", + "name": "Spawner (2311)", + "location": [1363, 1046, -13], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "EtherealWarrior", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4befab4e-1b1a-4290-ae76-6c176b48a18a", + "name": "Spawner (2311)", + "location": [1046, 517, -80], + "map": "Ilshenar", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "SavageRider", "maxCount": 3, "probability": 100 }, + { "name": "Savage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb0b433c-9d26-4a0f-b9a0-4ddb16ea1904", + "name": "Spawner (2311)", + "location": [1046, 517, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 10, + "entries": [{ "name": "SavageShaman", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "59ae00b4-1b69-42de-8faa-670350014ad7", + "name": "Spawner (2311)", + "location": [1116, 527, -80], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Savage", "maxCount": 1, "probability": 100 }, + { "name": "SavageRidgeback", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88f2279d-e351-4611-8255-206a456394d6", + "name": "Spawner (2311)", + "location": [1110, 542, -77], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6c8f4381-39be-4530-bfc3-1478035c1aa5", + "name": "Spawner (2311)", + "location": [1108, 492, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f0236b1-b26e-4572-b03a-d53d94ab1a6a", + "name": "Spawner (2311)", + "location": [1110, 542, -77], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "063240fa-7f32-4bad-b79e-bf9c8ca7b9b9", + "name": "Spawner (2311)", + "location": [1108, 492, -80], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a5f79c6-3813-428e-a5fe-00893d247556", + "name": "Spawner (2311)", + "location": [1161, 668, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "64e33b76-ba88-4906-a91f-4b56dea52a81", + "name": "Spawner (2311)", + "location": [1181, 691, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eb0f7637-62d3-483f-835e-248e48ffdc44", + "name": "Spawner (2311)", + "location": [1187, 685, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "86ff8b56-5d0c-4218-80d4-749414e3f568", + "name": "Spawner (2311)", + "location": [1191, 696, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 7, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "17243bc5-68af-437f-bb95-963913a05306", + "name": "Spawner (2311)", + "location": [1191, 696, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "SavageRider", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3bfd2dc7-d6d7-4604-858f-246ed9b25d35", + "name": "Spawner (2311)", + "location": [1203, 762, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Savage", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1e2bfa6b-abe9-4dcd-b996-bc4dcdd17f9f", + "name": "Spawner (2311)", + "location": [1234, 760, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "SavageShaman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "38614de6-1ed9-41a0-b30b-59ae8191316f", + "name": "Spawner (2311)", + "location": [1213, 719, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Savage", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b71629a-ede8-4fba-bd86-63a895c4c422", + "name": "Spawner (2311)", + "location": [1214, 744, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Savage", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72ce5891-f2dd-4a3c-8019-199928c49dcc", + "name": "Spawner (2311)", + "location": [1231, 694, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Savage", "maxCount": 2, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56b2d766-8057-4400-913f-6c10a5155f98", + "name": "Spawner (2311)", + "location": [1251, 703, -80], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Savage", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "917aa704-acc1-49ce-81c6-401f0604b260", + "name": "Spawner (2311)", + "location": [1274, 728, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "26a0cc2e-3dfc-49d7-9274-d993fc0371db", + "name": "Spawner (2311)", + "location": [1292, 755, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9fce7b2d-3671-4d8e-bf22-19c10ec5709f", + "name": "Spawner (2311)", + "location": [1280, 770, -80], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "Savage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04236803-3dc7-426b-8a8d-d927b1bd0086", + "name": "Spawner (2311)", + "location": [1279, 781, -80], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Ridgeback", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5f1d23bd-e6c8-422e-b69e-a88d61fa61c0", + "name": "Spawner (2311)", + "location": [1243, 732, -80], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Savage", "maxCount": 1, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ed8774de-7f1f-4f40-bb03-0ff9e7d1ff31", + "name": "Spawner (2311)", + "location": [1262, 758, -80], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Savage", "maxCount": 4, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/TwistedWeald.json b/Distribution/Data/Spawns/uoml/ilshenar/TwistedWeald.json new file mode 100644 index 000000000..e90de2284 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/TwistedWeald.json @@ -0,0 +1,227 @@ +[ + { + "type": "Spawner", + "guid": "46dc7fc7-d062-4be9-8134-f960352fbca3", + "name": "Spawner (2314)", + "location": [2171, 1181, -44], + "map": "Ilshenar", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "CuSidhe", "maxCount": 8, "probability": 100 }, + { "name": "DryadA", "maxCount": 8, "probability": 100 }, + { "name": "Changeling", "maxCount": 8, "probability": 100 }, + { "name": "Satyr", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "418a0be2-0604-4ff8-b39d-1fb570fa7b7e", + "name": "Spawner (2314)", + "location": [2177, 1210, -60], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "LadyLissith", "maxCount": 2, "probability": 100 }, + { "name": "LadySabrix", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd6dab60-126f-4877-b9dd-f786cbfad49b", + "name": "Spawner (2314)", + "location": [2138, 1212, -60], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Quagmire", "maxCount": 8, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 8, "probability": 100 }, + { "name": "Irk", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5cb54b29-10c4-448f-a036-5ba23dcb350a", + "name": "Spawner (2314)", + "location": [2224, 1218, 13], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Swoop", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aa8d4c55-b124-40ae-a0db-31b873c7a549", + "name": "Spawner (2314)", + "location": [2134, 1174, -46], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "Gnaw", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a47595b-b894-4d45-b35d-5714de0c8c51", + "name": "Spawner (2314)", + "location": [2206, 1220, -8], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "CuSidhe", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fe2db064-ee88-4f04-8c49-b3d7a56fd9a2", + "name": "Spawner (2314)", + "location": [2159, 1223, -60], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "Spite", "maxCount": 5, "probability": 100 }, + { "name": "Guile", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eea8e238-78e9-4d84-a91b-95360f6f2fd6", + "name": "Spawner (2314)", + "location": [2170, 1247, -60], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Malefic", "maxCount": 4, "probability": 100 }, + { "name": "Virulent", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "852fa8b9-931f-493c-9ee0-200a3f229110", + "name": "Spawner (2314)", + "location": [2163, 1189, -48], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Changeling", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6eee069d-8662-415f-bdcc-78975f1a54ae", + "name": "Spawner (2314)", + "location": [2207, 1180, -36], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "CuSidhe", "maxCount": 12, "probability": 100 }, + { "name": "Dryada", "maxCount": 12, "probability": 100 }, + { "name": "Changeling", "maxCount": 12, "probability": 100 }, + { "name": "Satyr", "maxCount": 12, "probability": 100 }, + { "name": "DireWolf", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76ef21c0-5e48-410e-84f5-57023fa7efb4", + "name": "Spawner (2314)", + "location": [2137, 1216, -60], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Silk", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b5b1118b-161e-43fb-80dd-f890dec2be53", + "name": "Spawner (2314)", + "location": [2177, 1229, -60], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7e991368-4fad-4e2b-af3b-88d360b88627", + "name": "Spawner (2314)", + "location": [2157, 1206, -60], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Virulent", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "68b1853c-f9bd-4bd1-852f-dcc9977a3049", + "name": "Spawner (2314)", + "location": [2167, 1266, -60], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Malefic", "maxCount": 3, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Vendors.json b/Distribution/Data/Spawns/uoml/ilshenar/Vendors.json new file mode 100644 index 000000000..4eaa286f0 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Vendors.json @@ -0,0 +1,758 @@ +[ + { + "type": "Spawner", + "guid": "826d7ee8-c78b-47ea-9ead-b30014afbe4d", + "name": "Spawner (2312)", + "location": [791, 618, 0], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "StoneCrafter", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81fc796d-04ed-48fb-a783-ad6ce619b116", + "name": "Spawner (2312)", + "location": [808, 697, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "751c4f29-04b6-4c50-b914-591862159429", + "name": "Spawner (2312)", + "location": [808, 586, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bce87417-e533-491a-8a6a-734227db69db", + "name": "Spawner (2312)", + "location": [1225, 563, -18], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "GypsyAnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2b7c425b-7a52-414a-a76a-cb0734c3165c", + "name": "Spawner (2312)", + "location": [1388, 428, -23], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "GypsyAnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0457d1c2-1736-4b97-96be-0625a8a08c91", + "name": "Spawner (2312)", + "location": [1497, 614, -14], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20c754a1-ce9b-4068-89ae-b50d95a64c82", + "name": "Spawner (2312)", + "location": [854, 680, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a08c2d8-b7b6-4b2f-b550-008e42f3a68b", + "name": "Spawner (2312)", + "location": [855, 603, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd5a6f5e-87cf-40c3-bbf6-d42f59e8f730", + "name": "Spawner (2312)", + "location": [766, 641, 0], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d27bf3f-df46-48bb-a611-a0f17e8d1051", + "name": "Spawner (2312)", + "location": [1226, 554, -12], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "GypsyBanker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b523ffff-8496-40a1-b051-f2e81e74263c", + "name": "Spawner (2312)", + "location": [1610, 556, -19], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6ce1b420-27ed-4adf-9ba2-0eeb9b0600fe", + "name": "Spawner (2312)", + "location": [826, 679, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5053b0d7-ca6f-4156-bb35-28f937b1db6b", + "name": "Spawner (2312)", + "location": [824, 602, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c9b9466-17bc-4285-8c04-294fa9b2b885", + "name": "Spawner (2312)", + "location": [1400, 434, -11], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "GypsyMaiden", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f4a51238-7658-469e-9ff3-eec8ba035f95", + "name": "Spawner (2312)", + "location": [665, 665, -35], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e9c58794-1e68-4a12-8e96-fe9695224e36", + "name": "Spawner (2312)", + "location": [1364, 1051, -13], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "14094381-ef68-4f89-b700-0d824d8968b2", + "name": "Spawner (2312)", + "location": [1261, 582, -18], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d10ff188-6468-434f-b032-dc92db16a183", + "name": "Spawner (2312)", + "location": [284, 536, -24], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37693d18-5f7b-4461-b8a3-49202500e8da", + "name": "Spawner (2312)", + "location": [1235, 543, -15], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FortuneTeller", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff91a9cc-14c5-4a0d-a138-20f24c30cee0", + "name": "Spawner (2312)", + "location": [1407, 428, -8], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FortuneTeller", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "68189f98-29d3-49e2-a415-c57862584b26", + "name": "Spawner (2312)", + "location": [1628, 548, -11], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6670bd00-6ae7-43fd-a102-f77c877c6107", + "name": "Spawner (2312)", + "location": [1518, 619, -19], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba48a4cb-841e-4be5-8ef9-1f06970d1829", + "name": "Spawner (2312)", + "location": [1093, 955, -38], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d6ef24f-8252-459b-a03b-d6fdaeb96597", + "name": "Spawner (2312)", + "location": [872, 586, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3f80bac-4411-4382-8781-582340ff4f03", + "name": "Spawner (2312)", + "location": [1620, 554, -20], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c8e54c5c-c4fb-4aa2-8da2-40d6c7c23d72", + "name": "Spawner (2312)", + "location": [1605, 541, -13], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "305ecea0-47a1-4561-bf25-5d879a152003", + "name": "Spawner (2312)", + "location": [859, 698, -40], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "07dcfcfd-1f11-4fe7-8cc5-d1981fb1e74f", + "name": "Spawner (2312)", + "location": [868, 657, -40], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "de233679-dda4-4826-be6f-66a66bc3ed0c", + "name": "Spawner (2312)", + "location": [869, 625, -40], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93dc7896-715b-44ad-b6e5-01a37dd42f81", + "name": "Spawner (2312)", + "location": [1405, 439, 2], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "GypsyBanker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "33de0f8c-f6ba-4e9b-a09b-dc36c96db2ea", + "name": "Spawner (2312)", + "location": [1394, 441, 13], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FortuneTeller", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9fffcfc7-2091-4013-b643-205c9567c311", + "name": "Spawner (2312)", + "location": [1505, 611, -19], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c4eb8ed-3851-4ff2-80a6-a0c6f6c3f948", + "name": "Spawner (2312)", + "location": [774, 1144, -30], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "52da389e-73f7-493e-b830-88dc0c4525f4", + "name": "Spawner (2312)", + "location": [1598, 539, -19], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f38481b6-0a95-44e0-af74-8cc94195e282", + "name": "Spawner (2312)", + "location": [369, 1432, 15], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12d3edd5-4ebb-41a7-82ff-2d2ab3c4adef", + "name": "Spawner (2312)", + "location": [1318, 1328, -14], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69fd06d1-04cb-457e-bbd9-e02475f6c4a8", + "name": "Spawner (2312)", + "location": [896, 610, -40], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32050353-d0d9-4b06-afee-a65fae031a98", + "name": "Spawner (2312)", + "location": [896, 610, -40], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ba1aca3-914a-4c73-91b0-12e42056659e", + "name": "Spawner (2312)", + "location": [1499, 619, -19], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5568a6cc-21a3-4550-9cc2-3ad836c7983b", + "name": "Spawner (2312)", + "location": [1600, 552, -16], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce3b9e5a-5028-4768-86ca-ebc7591eb49d", + "name": "Spawner (2312)", + "location": [840, 571, 0], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Glassblower", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84e30990-a039-4283-9bf3-00d8e70489de", + "name": "Spawner (2312)", + "location": [840, 707, 0], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4e5b8807-7ced-4f40-ae49-c4507b82fb78", + "name": "Spawner (2312)", + "location": [1253, 588, -19], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bae2beb6-304b-4002-b459-680d881199e5", + "name": "Spawner (2312)", + "location": [1566, 1049, -8], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "724e9d36-13e4-4abc-ac60-18aa45fd712f", + "name": "Spawner (2312)", + "location": [792, 665, 0], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d0135b3-074b-49ae-b850-25281059b466", + "name": "Spawner (2312)", + "location": [575, 537, -69], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ee8d0d83-c586-49d8-8de2-038e1d6f2848", + "name": "Spawner (2312)", + "location": [682, 297, -41], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b40eb968-c112-4c66-a331-b96a7d08622b", + "name": "Spawner (2312)", + "location": [1514, 611, -14], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/ilshenar/Wisp.json b/Distribution/Data/Spawns/uoml/ilshenar/Wisp.json new file mode 100644 index 000000000..2e3b78c63 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/ilshenar/Wisp.json @@ -0,0 +1,324 @@ +[ + { + "type": "Spawner", + "guid": "ae956108-5118-4793-94d7-ac885617030f", + "name": "Spawner (2313)", + "location": [681, 1535, -28], + "map": "Ilshenar", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 60, + "entries": [{ "name": "Wisp", "maxCount": 20, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "67d46705-d56a-4098-bafb-ede2658118a8", + "name": "Spawner (2313)", + "location": [855, 1567, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, + "entries": [{ "name": "SkeletalMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2ef49b9b-97d6-431a-946d-ffd2f1615697", + "name": "Spawner (2313)", + "location": [855, 1566, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "Shade", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a90852cd-b672-4c4f-83bb-99dcc861ead0", + "name": "Spawner (2313)", + "location": [851, 1498, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 20, + "entries": [{ "name": "RottingCorpse", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6cf2cd79-9f0f-4f25-af2b-333a9317171f", + "name": "Spawner (2313)", + "location": [827, 1479, -28], + "map": "Ilshenar", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Shade", "maxCount": 2, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 2, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "add779de-8361-4edb-b214-900500ca81f0", + "name": "Spawner (2313)", + "location": [888, 1471, -28], + "map": "Ilshenar", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 20, + "entries": [ + { "name": "Shade", "maxCount": 3, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4f8aa84-982a-4bd5-a708-9724d8598dd7", + "name": "Spawner (2313)", + "location": [901, 1507, 2], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "EvilMageLord", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "79272788-c6c6-4216-84e0-985b942af71b", + "name": "Spawner (2313)", + "location": [871, 1531, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "EvilMage", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "14b1989d-ec45-42db-af1b-92f49023b981", + "name": "Spawner (2313)", + "location": [891, 1537, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "RottingCorpse", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0fe99fe0-a203-4563-994b-5e02d70f1b3f", + "name": "Spawner (2313)", + "location": [888, 1568, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be42e727-e04f-47a2-b07c-49046fdafcb9", + "name": "Spawner (2313)", + "location": [939, 1531, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Imp", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "516473d8-b5d1-4924-ad74-556649f98470", + "name": "Spawner (2313)", + "location": [939, 1492, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Imp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc4fedc9-9489-4385-bf85-9ec569081f4a", + "name": "Spawner (2313)", + "location": [961, 1502, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "Ettin", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "530dcd87-a132-4cdb-a9c9-0f78758e4ad0", + "name": "Spawner (2313)", + "location": [962, 1480, -28], + "map": "Ilshenar", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b19163ea-9376-48e7-9bf7-b0a6a2e75d38", + "name": "Spawner (2313)", + "location": [1000, 1512, 0], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Titan", "maxCount": 2, "probability": 100 }, + { "name": "Cyclops", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "74498688-9c91-4ea3-8abd-adcd2beb3b8d", + "name": "Spawner (2313)", + "location": [959, 1530, -28], + "map": "Ilshenar", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 10, + "entries": [ + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 0, "probability": 100 }, + { "name": "Spectre", "maxCount": 0, "probability": 100 }, + { "name": "Shade", "maxCount": 0, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b9e3103-e83e-48a4-88b3-c72acbe5e173", + "name": "Spawner (2313)", + "location": [936, 1559, -22], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "33185d9f-afa2-43d7-a7aa-7d221c1a309a", + "name": "Spawner (2313)", + "location": [964, 1554, -28], + "map": "Ilshenar", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 10, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0d68da82-d86a-4c40-8085-174a01c65f55", + "name": "Spawner (2313)", + "location": [775, 1479, -28], + "map": "Ilshenar", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Balron", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5ce9d857-549d-4bf0-8e70-2dd6902c4ccf", + "name": "Spawner (2313)", + "location": [775, 1480, -28], + "map": "Ilshenar", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Imp", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "649b7645-25f8-4c0c-ac2d-896e558b2767", + "name": "Spawner (2313)", + "location": [776, 1480, -28], + "map": "Ilshenar", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 9, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 9, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 6, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 6, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/malas/Bedlam.json b/Distribution/Data/Spawns/uoml/malas/Bedlam.json new file mode 100644 index 000000000..c5b29eccb --- /dev/null +++ b/Distribution/Data/Spawns/uoml/malas/Bedlam.json @@ -0,0 +1,40 @@ +[ + { + "type": "Spawner", + "guid": "8c2124f4-e175-4198-8cf8-9cf64804fc22", + "name": "Spawner (2408)", + "location": [98, 1656, 0], + "map": "Malas", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "GoreFiend", "maxCount": 4, "probability": 100 }, + { "name": "PatchworkSkeleton", "maxCount": 4, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 6, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 6, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 6, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 6, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 }, + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eaa69fa3-4076-4fc5-950a-fe175713bbd3", + "name": "Spawner (2408)", + "location": [104, 1655, 0], + "map": "Malas", + "count": 1, + "minDelay": "00:15:00", + "maxDelay": "00:30:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "RedDeath", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/malas/Citadel.json b/Distribution/Data/Spawns/uoml/malas/Citadel.json new file mode 100644 index 000000000..a0d4fd895 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/malas/Citadel.json @@ -0,0 +1,259 @@ +[ + { + "type": "Spawner", + "guid": "17abd9d2-3b7a-44ec-a724-57c202236788", + "name": "Spawner (2406)", + "location": [179, 1869, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 4, "probability": 100 }, + { "name": "tigersclawthief", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "856c181e-183a-4231-acc8-5146301c56c6", + "name": "Spawner (2406)", + "location": [185, 1920, 0], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "magedragonsflamemage", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4854c18d-7bf2-4f8c-a69f-7679204b6d7d", + "name": "Spawner (2406)", + "location": [155, 1869, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 4, "probability": 100 }, + { "name": "dragonsflamemage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28535caf-b979-4745-8a87-e62691d7cb11", + "name": "Spawner (2406)", + "location": [145, 1898, 0], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 3, "probability": 100 }, + { "name": "tigersclawthief", "maxCount": 3, "probability": 100 }, + { "name": "eliteninjawarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5fed5a1-35db-4f09-b4b6-8017146a8bf3", + "name": "Spawner (2406)", + "location": [174, 1900, 0], + "map": "Malas", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 5, "probability": 100 }, + { "name": "dragonsflamemage", "maxCount": 5, "probability": 100 }, + { "name": "eliteninjawarrior", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d629e57-63db-4610-b6d2-cf3bfd34f0b0", + "name": "Spawner (2406)", + "location": [181, 1883, 0], + "map": "Malas", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 8, "probability": 100 }, + { "name": "tigersclawthief", "maxCount": 8, "probability": 100 }, + { "name": "dragonsflamemage", "maxCount": 8, "probability": 100 }, + { "name": "eliteninjawarrior", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "347e85d2-c752-4abf-86c7-bda2a2051033", + "name": "Spawner (2406)", + "location": [183, 1961, 0], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "dragonsflamemage", "maxCount": 2, "probability": 100 }, + { "name": "serpentsfangassassin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f02ef97-7699-4a65-8cd6-615aa50cc318", + "name": "Spawner (2406)", + "location": [139, 1868, 0], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 7, "probability": 100 }, + { "name": "tigersclawthief", "maxCount": 7, "probability": 100 }, + { "name": "magedragonsflamemage", "maxCount": 7, "probability": 100 }, + { "name": "dragonsflamemage", "maxCount": 7, "probability": 100 }, + { "name": "eliteninjawarrior", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "364cc553-16d0-428d-bf97-061c3ddfd120", + "name": "Spawner (2406)", + "location": [170, 1974, 0], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 3, "probability": 100 }, + { "name": "serpentsfanghighexecutioner", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dccae74d-bafa-40ff-9984-7a2a0af5d8de", + "name": "Spawner (2406)", + "location": [140, 1974, 0], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "magedragonsflamemage", "maxCount": 2, "probability": 100 }, + { "name": "serpentsfangassassin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb6ee275-c5c2-4780-9180-76e3a4ac1a97", + "name": "Spawner (2406)", + "location": [134, 1947, 0], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "magedragonsflamemage", "maxCount": 2, "probability": 100 }, + { "name": "dragonsflamemage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "435ce67b-aad4-4ed0-b621-73ab4b6b0042", + "name": "Spawner (2406)", + "location": [81, 1892, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "eliteninjawarrior", "maxCount": 4, "probability": 100 }, + { "name": "serpentsfangassassin", "maxCount": 4, "probability": 100 }, + { "name": "dragonsflamemage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "114d80f2-07e6-49e3-9418-6e7c30291028", + "name": "Spawner (2406)", + "location": [142, 1921, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "tigersclawthief", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50c87975-6021-46cb-bbe1-58c45ba776b7", + "name": "Spawner (2406)", + "location": [77, 1883, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "dragonsflamemage", "maxCount": 4, "probability": 100 }, + { "name": "eliteninjawarrior", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15b169f9-fe9e-4ef7-973b-cb0619b9937d", + "name": "Spawner (2406)", + "location": [82, 1875, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "serpentsfangassassin", "maxCount": 4, "probability": 100 }, + { "name": "eliteninjawarrior", "maxCount": 4, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/malas/Doom.json b/Distribution/Data/Spawns/uoml/malas/Doom.json new file mode 100644 index 000000000..7b1393361 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/malas/Doom.json @@ -0,0 +1,710 @@ +[ + { + "type": "Spawner", + "guid": "646d5241-162f-477e-a05c-17ba7eb95a83", + "name": "Spawner (2401)", + "location": [379, 147, -1], + "map": "Malas", + "count": 20, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "PatchworkSkeleton", "maxCount": 10, "probability": 100 }, + { "name": "VampireBat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c85f6fd0-ad43-4e05-a43e-b49002b7f22c", + "name": "Spawner (2401)", + "location": [372, 201, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "42bbf95d-6b39-4df6-a4f4-7a8d385b49ef", + "name": "Spawner (2401)", + "location": [442, 205, -1], + "map": "Malas", + "count": 1, + "minDelay": "00:20:00", + "maxDelay": "00:40:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "BoneDemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dcade1a7-1652-4eb7-a550-d9ed7a7449ac", + "name": "Spawner (2401)", + "location": [442, 205, -1], + "map": "Malas", + "count": 11, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PatchworkSkeleton", "maxCount": 6, "probability": 100 }, + { "name": "VampireBat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5c28d40c-81f7-428d-b661-6e272aeabf3d", + "name": "Spawner (2401)", + "location": [480, 227, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:03:00", + "maxDelay": "00:05:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff72dd04-bba5-4a6f-8899-d851b4c1fe88", + "name": "Spawner (2401)", + "location": [480, 227, -1], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d5daa218-49a1-446c-a871-a7ebc77cacf9", + "name": "Spawner (2401)", + "location": [480, 227, -1], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2ca4b73-4847-47ab-83de-c3f0abb570a9", + "name": "Spawner (2401)", + "location": [493, 202, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Skeleton", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "182c0603-13fa-414f-849c-dcbde31a739a", + "name": "Spawner (2401)", + "location": [479, 168, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:02:00", + "maxDelay": "00:03:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Devourer", "maxCount": 3, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f909d2a4-b3bf-4d0c-b07c-f36839c99209", + "name": "Spawner (2401)", + "location": [479, 168, -1], + "map": "Malas", + "count": 5, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a42b1d2c-dcd4-4d43-aada-f5707a5f9e9f", + "name": "Spawner (2401)", + "location": [468, 141, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:03:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cf26832d-8e20-4277-8491-91461dd2995e", + "name": "Spawner (2401)", + "location": [487, 118, -1], + "map": "Malas", + "count": 5, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Mummy", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e708303-8a2d-4207-bdc3-d7f732e04efe", + "name": "Spawner (2401)", + "location": [496, 75, 0], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "PatchworkSkeleton", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a1434ffe-19f2-4076-b1ce-28e59dc9f627", + "name": "Spawner (2401)", + "location": [496, 75, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "VampireBat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0bb98d0c-0614-431b-a6ce-c30718f447c8", + "name": "Spawner (2401)", + "location": [447, 25, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Skeleton", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f20c08f3-28e9-47a3-b24e-b31370ce2c18", + "name": "Spawner (2401)", + "location": [447, 25, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "32a2e0d8-8246-42ff-8e13-c15b51a9f73f", + "name": "Spawner (2401)", + "location": [470, 30, -2], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "PatchworkSkeleton", "maxCount": 4, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbfc66e4-a4fd-486a-960e-5cb7c64c2e72", + "name": "Spawner (2401)", + "location": [456, 48, -1], + "map": "Malas", + "count": 12, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "FleshGolem", "maxCount": 6, "probability": 100 }, + { "name": "Devourer", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59d35721-49b5-4ff9-9c91-fad1b3d4b953", + "name": "Spawner (2401)", + "location": [395, 33, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "VampireBat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "218aaa26-c02b-4605-8c79-c9879523ea0b", + "name": "Spawner (2401)", + "location": [356, 40, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "VampireBat", "maxCount": 4, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e641eb9-2f8a-41ae-afb9-24a4b5879a0a", + "name": "Spawner (2401)", + "location": [305, 28, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Gibberling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e12bb836-80f3-4ce1-b068-9d2485922b0f", + "name": "Spawner (2401)", + "location": [290, 28, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "GoreFiend", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f9c8b1af-1ea5-466d-90a1-e956f8c8de31", + "name": "Spawner (2401)", + "location": [266, 26, 0], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 5, + "walkingRange": 8, + "entries": [{ "name": "LichLord", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b0f74ae9-9e85-4525-9903-6e69c2f818e4", + "name": "Spawner (2401)", + "location": [309, 15, 0], + "map": "Malas", + "count": 7, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 2, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d03c82ab-0be4-4818-8708-14afba5d8eee", + "name": "Spawner (2401)", + "location": [366, 13, -1], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "20977621-9e81-4c25-b802-19fa12fc3633", + "name": "Spawner (2401)", + "location": [323, 90, -1], + "map": "Malas", + "count": 9, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "PatchworkSkeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59819649-f460-4934-ba36-a89a98aa13f2", + "name": "Spawner (2401)", + "location": [320, 114, -1], + "map": "Malas", + "count": 7, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [{ "name": "Zombie", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5bc9de9b-c7d7-4152-9fd1-19e57598100e", + "name": "Spawner (2401)", + "location": [320, 114, -1], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Mummy", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2a98c34c-cb3d-4d28-8c12-1a3cf7cc58ad", + "name": "Spawner (2401)", + "location": [330, 136, 0], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d8a6cfe1-0e71-423a-94e9-4ad92ef8d73d", + "name": "Spawner (2401)", + "location": [330, 136, 0], + "map": "Malas", + "count": 1, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "693a953a-75cf-43df-9708-b781c8a04439", + "name": "Spawner (2401)", + "location": [325, 172, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [ + { "name": "PatchworkSkeleton", "maxCount": 3, "probability": 100 }, + { "name": "VampireBat", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "613d79a6-10e0-4465-9937-142ee7e701b0", + "name": "Spawner (2401)", + "location": [289, 154, 5], + "map": "Malas", + "count": 1, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "LichLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93ce2586-d403-49a2-acbb-034ef0ee797a", + "name": "Spawner (2401)", + "location": [278, 233, 0], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [{ "name": "Devourer", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "55898d74-f2cf-4c30-953e-e5f3d9a3b5bf", + "name": "Spawner (2401)", + "location": [278, 233, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PatchworkSkeleton", "maxCount": 2, "probability": 100 }, + { "name": "VampireBat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "13e0051b-bff0-4826-9e89-73ea9ec64e7a", + "name": "Spawner (2401)", + "location": [320, 207, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6492e163-beb3-4abc-a674-921cbf5ba5df", + "name": "Spawner (2401)", + "location": [356, 234, -1], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Skeleton", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "48538f93-9afa-44c9-baab-b1c3612bdde5", + "name": "Spawner (2401)", + "location": [356, 234, -1], + "map": "Malas", + "count": 3, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Devourer", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "74182f58-a286-4576-9615-f92bbd4e7b71", + "name": "Spawner (2401)", + "location": [378, 236, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Devourer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a81f4ff2-ee77-4e1f-968d-335a0ecbe7b9", + "name": "Spawner (2401)", + "location": [427, 256, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "RestlessSoul", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2be25342-c032-406a-af47-8eb5994c0875", + "name": "Spawner (2401)", + "location": [427, 256, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "RottingCorpse", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "298afdb1-74fb-4668-9bc2-5f625a61caed", + "name": "Spawner (2401)", + "location": [478, 206, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "RottingCorpse", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d99b3aa7-013c-4dd9-8258-ec68e91e6bf5", + "name": "Spawner (2401)", + "location": [443, 128, -1], + "map": "Malas", + "count": 16, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "PatchworkSkeleton", "maxCount": 7, "probability": 100 }, + { "name": "VampireBat", "maxCount": 7, "probability": 100 }, + { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "92450262-dc59-4666-b8bd-233fa481ddcb", + "name": "Spawner (2401)", + "location": [400, 174, -1], + "map": "Malas", + "count": 1, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1210b304-4b1a-47be-a503-9f490764d069", + "name": "Spawner (2401)", + "location": [287, 81, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Skeleton", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e764f652-de2c-420f-8419-7d29a2d6cd9c", + "name": "Spawner (2401)", + "location": [287, 81, -1], + "map": "Malas", + "count": 8, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "VampireBat", "maxCount": 6, "probability": 100 }, + { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5110399b-8443-448e-a68c-f59ac2303716", + "name": "Spawner (2401)", + "location": [441, 78, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PatchworkSkeleton", "maxCount": 4, "probability": 100 }, + { "name": "RottingCorpse", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "968ff190-b606-41f5-8cf0-36e15d3fd7d4", + "name": "Spawner (2401)", + "location": [382, 82, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:01:00", + "maxDelay": "00:02:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "VampireBat", "maxCount": 6, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/malas/Labyrinth.json b/Distribution/Data/Spawns/uoml/malas/Labyrinth.json new file mode 100644 index 000000000..75b5f243a --- /dev/null +++ b/Distribution/Data/Spawns/uoml/malas/Labyrinth.json @@ -0,0 +1,293 @@ +[ + { + "type": "Spawner", + "guid": "3ba58850-4bed-4935-8a37-e0c703f3b768", + "name": "Spawner (2407)", + "location": [366, 1928, 0], + "map": "Malas", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Drake", "maxCount": 4, "probability": 100 }, + { "name": "AirElemental", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 }, + { "name": "Snake", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebe544f9-9a54-4822-bb37-ee11ba1059db", + "name": "Spawner (2407)", + "location": [384, 1932, 10], + "map": "Malas", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Minotaur", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b34e749-7a4e-4dc8-8b84-6d6444d80525", + "name": "Spawner (2407)", + "location": [335, 1920, 0], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Miasma", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eea9dc24-23e1-4198-8dbc-b22bddb5a71c", + "name": "Spawner (2407)", + "location": [1740, 990, -80], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b9da636f-3e98-4924-a7da-8ac3d0141e5f", + "name": "Spawner (2407)", + "location": [1742, 990, -84], + "map": "Malas", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [ + { "name": "Scorpion", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8196e8f-9f8f-4362-b188-6bb06dbfea9d", + "name": "Spawner (2407)", + "location": [336, 1891, 0], + "map": "Malas", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [ + { "name": "Minotaur", "maxCount": 5, "probability": 100 }, + { "name": "MinotaurScout", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a9e68d9-d730-402c-96cc-979f6d08fcfa", + "name": "Spawner (2407)", + "location": [357, 1898, 5], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Rend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f5972024-9852-4b8d-96a2-bf1afc05c588", + "name": "Spawner (2407)", + "location": [346, 1959, 0], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Squirrel", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af7b61de-563e-4559-9905-d5113f0e7c02", + "name": "Spawner (2407)", + "location": [392, 1917, 0], + "map": "Malas", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "MinotaurScout", "maxCount": 11, "probability": 100 }, + { "name": "Minotaur", "maxCount": 11, "probability": 100 }, + { "name": "MinotaurCaptain", "maxCount": 11, "probability": 100 }, + { "name": "Squirrel", "maxCount": 9, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 9, "probability": 100 }, + { "name": "Bird", "maxCount": 9, "probability": 100 }, + { "name": "Cat", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "613eb20c-bb8e-4f16-8561-bd839851b463", + "name": "Spawner (2407)", + "location": [390, 1883, 0], + "map": "Malas", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Eagle", "maxCount": 8, "probability": 100 }, + { "name": "Cat", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a451b30c-88b8-411d-99b6-3a66cdfd0cd5", + "name": "Spawner (2407)", + "location": [355, 1918, 0], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Reptalon", "maxCount": 6, "probability": 100 }, + { "name": "Minotaur", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1e202ec-c6ba-4c76-8a26-29767ae27907", + "name": "Spawner (2407)", + "location": [346, 1910, 0], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [ + { "name": "TropicalBird", "maxCount": 6, "probability": 100 }, + { "name": "Bird", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46f54372-6b9c-4e33-9f35-99fc01204b59", + "name": "Spawner (2407)", + "location": [427, 1923, 5], + "map": "Malas", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "MinotaurCaptain", "maxCount": 6, "probability": 100 }, + { "name": "Minotaur", "maxCount": 6, "probability": 100 }, + { "name": "MinotaurScout", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef581668-95b6-4e1b-b43f-06e805e2e646", + "name": "Spawner (2407)", + "location": [348, 1948, 0], + "map": "Malas", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "TropicalBird", "maxCount": 12, "probability": 100 }, + { "name": "Eagle", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "MinotaurScout", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50f241e9-15cf-4561-9a1c-0fe8522d01cd", + "name": "Spawner (2407)", + "location": [415, 1897, 5], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Pyre", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aaca3ab0-5ea8-4af5-ac5f-c037d41b00fa", + "name": "Spawner (2407)", + "location": [347, 1888, 5], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "flurry", "maxCount": 4, "probability": 100 }, + { "name": "grim", "maxCount": 4, "probability": 100 }, + { "name": "mistral", "maxCount": 4, "probability": 100 }, + { "name": "tempest", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d9f4f90-3f61-4dc1-9715-0d614c34c99c", + "name": "Spawner (2407)", + "location": [382, 1913, 0], + "map": "Malas", + "count": 0, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Meraktus", "maxCount": 0, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/malas/North.json b/Distribution/Data/Spawns/uoml/malas/North.json index 7f7fe17e4..e0b419d4e 100644 --- a/Distribution/Data/Spawns/uoml/malas/North.json +++ b/Distribution/Data/Spawns/uoml/malas/North.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "6f75ce3a-5afe-4d59-8cea-521a9cab1c9a", "name": "Spawner (2402)", "location": [1674, 659, -84], @@ -21,7 +21,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b2985f08-8004-4d85-8b11-e98cd205b584", "name": "Spawner (2402)", "location": [1434, 607, -89], @@ -42,7 +42,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "7ef09a35-9032-4c5c-8273-9cdeb13281f6", "name": "Spawner (2402)", "location": [1194, 638, -84], @@ -63,7 +63,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5a950bac-ad86-44ce-9c3f-b26ac67b1f63", "name": "Spawner (2402)", "location": [920, 356, -85], @@ -82,5 +82,348 @@ { "name": "GreatHart", "maxCount": 1, "probability": 100 }, { "name": "Rabbit", "maxCount": 4, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "6ef4d890-5038-4d39-977c-5eb15448543a", + "name": "Spawner (2402)", + "location": [1659, 112, -80], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7be1139-619a-4d84-a98a-8ea04c550827", + "name": "Spawner (2402)", + "location": [1869, 480, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41a269ac-0492-4a16-bbc3-449c7efa9ef9", + "name": "Spawner (2402)", + "location": [2121, 597, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1e3857a-952a-47c2-9311-d6bcc3806c45", + "name": "Spawner (2402)", + "location": [1980, 660, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2af6c55-d320-4255-a85f-c694f8d9886b", + "name": "Spawner (2402)", + "location": [2233, 555, -90], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b85a0fee-612e-4bd9-8489-28fbcdc60ef5", + "name": "Spawner (2402)", + "location": [1137, 223, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b48ebb7a-8bfc-4c48-a0ab-f82ca41e38ac", + "name": "Spawner (2402)", + "location": [1381, 340, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "67b52f00-d808-4db4-91ca-9313330d0b40", + "name": "Spawner (2402)", + "location": [1710, 403, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63d80004-20f7-48d8-bcc8-639a8d83c099", + "name": "Spawner (2402)", + "location": [766, 114, -90], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be2f76e8-a03b-42b9-b39c-c641f03ee091", + "name": "Spawner (2402)", + "location": [1206, 191, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abaa8a2d-9997-4bff-b033-a9dfa78d46f9", + "name": "Spawner (2402)", + "location": [2030, 500, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 70, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "135a20a6-0885-4a61-9a55-a01593f3e874", + "name": "Spawner (2402)", + "location": [1482, 347, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 60, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3f940e1-3225-416f-a9ba-717b28fb944a", + "name": "Spawner (2402)", + "location": [1023, 192, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2db6869c-b590-4f82-a5eb-edb7afdd18e4", + "name": "Spawner (2402)", + "location": [1274, 300, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dec6f03f-a79d-414a-8b8b-c46af723371a", + "name": "Spawner (2402)", + "location": [1587, 386, -50], + "map": "Malas", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c628980-8a6f-45e1-bdc8-ed7ea16765fd", + "name": "Spawner (2402)", + "location": [2205, 145, -90], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Unicorn", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2efda65d-ebc3-4251-b6a3-356d42cca581", + "name": "Spawner (2402)", + "location": [2291, 295, -90], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [{ "name": "GoreFiend", "maxCount": 6, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/uoml/malas/OrcForts.json b/Distribution/Data/Spawns/uoml/malas/OrcForts.json new file mode 100644 index 000000000..8e6532917 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/malas/OrcForts.json @@ -0,0 +1,178 @@ +[ + { + "type": "Spawner", + "guid": "f4837b68-40f5-42db-b907-f4fe65248cf9", + "name": "Spawner (2403)", + "location": [1343, 1248, -90], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Orc", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9a42ced3-e2d2-4d65-bb78-3903a4092c9f", + "name": "Spawner (2403)", + "location": [1343, 1196, -90], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Orc", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0cb005f6-2405-41d4-b9b6-9ad8c75a6851", + "name": "Spawner (2403)", + "location": [1372, 1225, -90], + "map": "Malas", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "OrcBrute", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea44d8fe-27b9-4650-9c22-fed0630e5cc3", + "name": "Spawner (2403)", + "location": [1322, 1225, -90], + "map": "Malas", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 20, + "walkingRange": 40, + "entries": [ + { "name": "OrcBrute", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b187d6bd-fe00-43c8-906d-f18ee330f432", + "name": "Spawner (2403)", + "location": [911, 194, -79], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c93264b1-19bd-47d9-ae8d-261e8d36fc30", + "name": "Spawner (2403)", + "location": [1665, 356, -50], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 25, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2abcbbd3-3842-46f6-95f1-41b743bb7083", + "name": "Spawner (2403)", + "location": [1364, 596, -86], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "415080ef-9b67-4529-8651-f82d4e0fba5c", + "name": "Spawner (2403)", + "location": [1205, 700, -88], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "778c0ad2-e39e-4c61-9b1c-e45b6a9b554e", + "name": "Spawner (2403)", + "location": [1257, 1326, -90], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4a2ba68-4d62-480c-a9a9-b893f56a1a5d", + "name": "Spawner (2403)", + "location": [1598, 1819, -110], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:15:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 2, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/malas/South.json b/Distribution/Data/Spawns/uoml/malas/South.json index 6e044c8e2..f55eeaf8e 100644 --- a/Distribution/Data/Spawns/uoml/malas/South.json +++ b/Distribution/Data/Spawns/uoml/malas/South.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "436de8ee-4a2f-4494-ab76-e24fc6658954", "name": "Spawner (2404)", "location": [2267, 1218, -85], @@ -31,7 +31,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a2c8f9a1-d9fe-4626-9349-c75746354a52", "name": "Spawner (2404)", "location": [2267, 1218, -85], @@ -56,7 +56,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "adeed33a-83d4-4cf9-b289-f14877d5da5f", "name": "Spawner (2404)", "location": [1943, 1270, -90], @@ -87,7 +87,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "38709150-9301-4899-9792-039c4c4b1790", "name": "Spawner (2404)", "location": [1943, 1270, -90], @@ -112,7 +112,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "86cc85cb-88c2-44a2-bb9a-2f06be879c18", "name": "Spawner (2404)", "location": [2304, 1258, -86], @@ -136,7 +136,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b158c1ee-114f-418a-8272-cc602195304b", "name": "Spawner (2404)", "location": [1471, 1357, -85], @@ -157,7 +157,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "42155e29-bf66-4f72-bd02-fda44a8d9882", "name": "Spawner (2404)", "location": [1837, 1803, -110], @@ -178,7 +178,27 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "6bbb82e4-b0d1-4a23-a9da-5d35911ce762", + "name": "Spawner (2404)", + "location": [2231, 1605, -95], + "map": "Malas", + "count": 33, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 160, + "walkingRange": 160, + "entries": [ + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "SandVortex", "maxCount": 6, "probability": 100 }, + { "name": "Scorpion", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "db534aca-9faa-4405-90a0-ba19554e47fb", "name": "Spawner (2404)", "location": [1641, 1797, -110], @@ -198,7 +218,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "73285bc9-88f2-4fcf-9daa-9fc686d523d3", "name": "Spawner (2404)", "location": [1899, 1609, -110], @@ -217,7 +237,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bc9d5276-a7d4-4f9f-99f9-ce378158a5af", "name": "Spawner (2404)", "location": [1741, 1487, -110], @@ -237,7 +257,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "783e52d1-194e-474a-927e-0a50e4366d87", "name": "Spawner (2404)", "location": [881, 927, -90], @@ -248,12 +268,10 @@ "team": 0, "homeRange": 30, "walkingRange": 30, - "entries": [ - { "name": "Pixie", "maxCount": 6, "probability": 100 } - ] + "entries": [{ "name": "Pixie", "maxCount": 6, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "62849279-92c4-4de1-8e02-1f0d4bf2b6f5", "name": "Spawner (2404)", "location": [769, 1277, -90], @@ -270,7 +288,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ca139d63-ba9b-48df-aa95-4d2f6310f5eb", "name": "Spawner (2404)", "location": [761, 1283, -90], @@ -281,12 +299,10 @@ "team": 0, "homeRange": 200, "walkingRange": 200, - "entries": [ - { "name": "Eagle", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Eagle", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "a7c283cc-1081-4d32-983b-3d4a7df48438", "name": "Spawner (2404)", "location": [743, 1280, -90], @@ -297,12 +313,10 @@ "team": 0, "homeRange": 100, "walkingRange": 100, - "entries": [ - { "name": "EvilHealer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "EvilHealer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ede8df6e-ef32-443e-b6fc-53d5a652ce53", "name": "Spawner (2404)", "location": [2304, 1258, -86], @@ -313,12 +327,10 @@ "team": 0, "homeRange": 70, "walkingRange": 70, - "entries": [ - { "name": "EvilHealer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "EvilHealer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5bfb14ac-698f-4f85-b3b0-6a7bf9e80efb", "name": "Spawner (2404)", "location": [771, 1277, -90], @@ -337,7 +349,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c9ea7841-c46b-45af-ae9c-df25b1acab4d", "name": "Spawner (2404)", "location": [1347, 1739, -110], diff --git a/Distribution/Data/Spawns/uoml/malas/Vendors.json b/Distribution/Data/Spawns/uoml/malas/Vendors.json index 3ae31cccd..d25773a27 100644 --- a/Distribution/Data/Spawns/uoml/malas/Vendors.json +++ b/Distribution/Data/Spawns/uoml/malas/Vendors.json @@ -1,6 +1,470 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "99cde945-5b9c-4cff-924f-a59142bcbdb9", + "name": "Spawner (2405)", + "location": [989, 520, -50], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bd799ab-c7fb-446e-b6cb-580d1fe2f7fb", + "name": "Spawner (2405)", + "location": [976, 512, -50], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f62df843-74a7-4bb0-a101-53d1dc4cb806", + "name": "Spawner (2405)", + "location": [976, 527, -50], + "map": "Malas", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79aa68b8-d2bd-4a4d-ad84-422588aeaaff", + "name": "Spawner (2405)", + "location": [1016, 514, -70], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "KeeperOfChivalry", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e08eb38c-0b25-4387-8999-f9d8a0a877aa", + "name": "Spawner (2405)", + "location": [961, 517, -70], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "KeeperOfChivalry", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a4200c02-8035-48bc-b79d-8b45225ffd4f", + "name": "Spawner (2405)", + "location": [1004, 527, -50], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "HolyMage", "maxCount": 1, "probability": 100 }, + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1bda852-fee4-45ee-aa79-630980bc44e9", + "name": "Spawner (2405)", + "location": [1003, 512, -50], + "map": "Malas", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df531ac9-d2b0-48ae-bc19-dc043d6af3de", + "name": "Spawner (2405)", + "location": [993, 511, -50], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 }, + { "name": "Jeweler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54deea77-bc1e-446b-8274-ea23bddb369e", + "name": "Spawner (2405)", + "location": [989, 527, -50], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7a10d380-53e3-48c9-9c5b-07a3c215032a", + "name": "Spawner (2405)", + "location": [1027, 494, -70], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3d301c77-1ade-4823-b2ff-f1d4e0ed2bf0", + "name": "Spawner (2405)", + "location": [1029, 520, -55], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d9c6323-7da9-4e12-861a-76fd30b3e3dc", + "name": "Spawner (2405)", + "location": [950, 520, -55], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55fab330-6d2a-4823-a666-3dfa33dceeb6", + "name": "Spawner (2405)", + "location": [1977, 1365, -80], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81e4af4e-3310-4864-8517-a032d487716f", + "name": "Spawner (2405)", + "location": [1992, 1315, -90], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ad9f47f-17bc-4790-9a84-1e9aee66478f", + "name": "Spawner (2405)", + "location": [2011, 1326, -80], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa9dbd50-e0ca-4206-a197-0fe85a349900", + "name": "Spawner (2405)", + "location": [2017, 1356, -90], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4f71d7e-8c76-47db-a1c1-b81c4484741b", + "name": "Spawner (2405)", + "location": [2027, 1353, -90], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dcbbc92-14ac-4798-8753-41aa3b6b2450", + "name": "Spawner (2405)", + "location": [2023, 1379, -80], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a0c83d13-2354-494f-9535-d4e587cc7a6b", + "name": "Spawner (2405)", + "location": [2025, 1387, -80], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d69c950-70d4-48c5-8729-be8848330c1a", + "name": "Spawner (2405)", + "location": [2045, 1397, -90], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a263ccfc-65e1-4181-93b6-6e1c7914f5b8", + "name": "Spawner (2405)", + "location": [2048, 1343, -85], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9347985d-23a4-4044-9a5c-79939c9f7df0", + "name": "Spawner (2405)", + "location": [2037, 1311, -85], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eb50b13f-dd98-42c1-bfa0-2599bdc85de3", + "name": "Spawner (2405)", + "location": [2066, 1282, -80], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ed338dc-51c1-40b3-a91a-7ebd8d91131b", + "name": "Spawner (2405)", + "location": [2060, 1283, -80], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6de9f30-1ee9-4914-853c-be075bd119ce", + "name": "Spawner (2405)", + "location": [2083, 1322, -80], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72a7f47d-d4cb-4530-9ba6-3878482279b2", + "name": "Spawner (2405)", + "location": [2078, 1327, -80], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0db7d5e9-beaa-43a2-ac46-2802ac962fcb", + "name": "Spawner (2405)", + "location": [2068, 1372, -75], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b7226aea-9fd0-4079-8e21-70a7a443d0b2", + "name": "Spawner (2405)", + "location": [1056, 1434, -85], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "706b7616-e5d6-465d-9f4f-3094eda8a29f", "name": "Spawner (2405)", "location": [1051, 1434, -85], diff --git a/Distribution/Data/Spawns/uoml/tokuno/FanDancersDojo.json b/Distribution/Data/Spawns/uoml/tokuno/FanDancersDojo.json new file mode 100644 index 000000000..86703b4ef --- /dev/null +++ b/Distribution/Data/Spawns/uoml/tokuno/FanDancersDojo.json @@ -0,0 +1,352 @@ +[ + { + "type": "Spawner", + "guid": "42b06210-4cd3-49d6-a5d1-74e5ab0cc77a", + "name": "Spawner (2501)", + "location": [977, 218, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "FanDancer", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47256f4f-b709-4c19-866b-2d7bdd896fd6", + "name": "Spawner (2501)", + "location": [78, 546, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Daemon", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 }, + { "name": "Succubus", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "66d98c69-5788-471a-b93c-d82e252471b2", + "name": "Spawner (2501)", + "location": [110, 540, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Daemon", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 }, + { "name": "Succubus", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "005c0fd4-1893-489e-9a09-14c53b246567", + "name": "Spawner (2501)", + "location": [96, 540, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "FanDancer", "maxCount": 1, "probability": 100 }, + { "name": "Ronin", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f093fd90-d7b6-48a7-b5f3-c2524f4ca2ca", + "name": "Spawner (2501)", + "location": [100, 499, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "24b2ba02-2289-4cb3-a957-e3ec7629c0b1", + "name": "Spawner (2501)", + "location": [64, 488, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 2, "probability": 100 }, + { "name": "HordeMinion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3743807-ec35-485a-82b5-f3cd3ba27bc0", + "name": "Spawner (2501)", + "location": [70, 376, -1], + "map": "Malas", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "FanDancer", "maxCount": 3, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, + { "name": "HordeMinion", "maxCount": 3, "probability": 100 }, + { "name": "Daemon", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "06d87817-7b93-45c4-9696-35c26ca4218d", + "name": "Spawner (2501)", + "location": [120, 415, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "HordeMinion", "maxCount": 2, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd077bc0-b36c-41b4-9e4c-e3f067d6846e", + "name": "Spawner (2501)", + "location": [99, 387, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "HeadlessOne", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4849d20d-77fc-4f0d-a513-a352a5849467", + "name": "Spawner (2501)", + "location": [123, 387, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "HeadlessOne", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "183ea3fc-7ed8-4f54-bd05-513be50f48d6", + "name": "Spawner (2501)", + "location": [79, 521, -1], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "HeadlessOne", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "624d9055-be69-4b42-9aa8-f8876a6c3cbf", + "name": "Spawner (2501)", + "location": [88, 332, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "FanDancer", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cdd374da-1e88-4389-ac42-a6c63ba30d0b", + "name": "Spawner (2501)", + "location": [97, 467, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "HordeMinion", "maxCount": 2, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Succubus", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "752b7dfa-4f8c-4bb6-875e-2f18385881b5", + "name": "Spawner (2501)", + "location": [111, 352, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "HordeMinion", "maxCount": 2, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "016a082b-0696-40c3-815b-c3dbef373d50", + "name": "Spawner (2501)", + "location": [173, 615, -1], + "map": "Malas", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "HordeMinion", "maxCount": 2, "probability": 100 }, + { "name": "FanDancer", "maxCount": 2, "probability": 100 }, + { "name": "Oni", "maxCount": 2, "probability": 100 }, + { "name": "Daemon", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a384aa9-3d4c-4df7-82df-58e658f4bf5f", + "name": "Spawner (2501)", + "location": [147, 646, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "HeadlessOne", "maxCount": 2, "probability": 100 }, + { "name": "FanDancer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a661087-c1ec-4503-9451-8873a8c24bd9", + "name": "Spawner (2501)", + "location": [175, 660, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "HordeMinion", "maxCount": 3, "probability": 100 }, + { "name": "HellCat", "maxCount": 2, "probability": 100 }, + { "name": "Balron", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3fd2f65-4f1e-4af5-8cab-26493d9c5350", + "name": "Spawner (2501)", + "location": [142, 692, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "FanDancer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a4392a5c-8729-40c2-bd31-4b145cba2f1a", + "name": "Spawner (2501)", + "location": [106, 685, -1], + "map": "Malas", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "Succubus", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "477ff85e-e7ac-4a85-a99f-75d1b3e9eb74", + "name": "Spawner (2501)", + "location": [110, 649, -1], + "map": "Malas", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "FanDancer", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "Balron", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12d3edf5-87f3-4fb4-9ca5-8462b94a81d8", + "name": "Spawner (2501)", + "location": [134, 651, -1], + "map": "Malas", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "FanDancer", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/tokuno/Outdoors.json b/Distribution/Data/Spawns/uoml/tokuno/Outdoors.json new file mode 100644 index 000000000..1046e8cfe --- /dev/null +++ b/Distribution/Data/Spawns/uoml/tokuno/Outdoors.json @@ -0,0 +1,3331 @@ +[ + { + "type": "Spawner", + "guid": "db468eba-47a3-46c1-b47a-c2179f0b2a5f", + "name": "Spawner (2502)", + "location": [1215, 1086, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f388571-783f-4271-ad85-19979804caed", + "name": "Spawner (2502)", + "location": [643, 540, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BakeKitsune", "maxCount": 4, "probability": 100 }, + { "name": "HellCat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6218636-c822-4e08-9b7c-1355ccae6ccf", + "name": "Spawner (2502)", + "location": [615, 556, 37], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, + { "name": "HellCat", "maxCount": 8, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4421adb-275f-4462-95dc-2d8bcd981471", + "name": "Spawner (2502)", + "location": [1184, 837, 36], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5906f708-87ed-4315-9849-69799e96a1fe", + "name": "Spawner (2502)", + "location": [499, 530, 32], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "LesserHiryu", "maxCount": 6, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 6, "probability": 100 }, + { "name": "Oni", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3042275-6dc0-4a32-b09f-01d26bbb2c29", + "name": "Spawner (2502)", + "location": [1197, 813, 27], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Hiryu", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b0318874-b52d-4005-aaf0-540bce7fc3e9", + "name": "Spawner (2502)", + "location": [1247, 791, 6], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2facb999-7cc7-4e29-9a29-d77bb5a2d6fa", + "name": "Spawner (2502)", + "location": [1345, 881, 13], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a8f16a28-29cc-41c2-a79e-be9e10a32363", + "name": "Spawner (2502)", + "location": [1377, 882, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "LavaSerpent", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93a819a9-3b0f-40bd-b923-20ed865b6d0f", + "name": "Spawner (2502)", + "location": [208, 641, 36], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6bc3be1f-048d-44a1-99d1-416c753c9468", + "name": "Spawner (2502)", + "location": [582, 505, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, + { "name": "HellCat", "maxCount": 8, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 8, "probability": 100 }, + { "name": "HellHound", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9149d3f-2c41-4774-9758-1f8f71b1598e", + "name": "Spawner (2502)", + "location": [1311, 776, 10], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Hiryu", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e2ab6de1-7751-4971-b901-3fb6f9ad2a2c", + "name": "Spawner (2502)", + "location": [554, 476, 32], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "BakeKitsune", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "55d6194b-1117-472c-b276-543f6234d504", + "name": "Spawner (2502)", + "location": [571, 550, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60281adb-b53d-4daf-9ce8-84c89c800e56", + "name": "Spawner (2502)", + "location": [526, 556, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BakeKitsune", "maxCount": 8, "probability": 100 }, + { "name": "HellCat", "maxCount": 8, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a1bebc3-fa84-492d-9b65-7a1e95a18fe1", + "name": "Spawner (2502)", + "location": [1336, 810, 9], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EliteNinja", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a8390df1-8ea6-42e6-9d13-e09159f03a0e", + "name": "Spawner (2502)", + "location": [1344, 774, 19], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98f4d240-c7db-4d14-ad8b-9b8fdb381d82", + "name": "Spawner (2502)", + "location": [528, 552, 32], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "LesserHiryu", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c80f169f-7335-426e-a51c-ca1771920137", + "name": "Spawner (2502)", + "location": [502, 553, 32], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Oni", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4bf88f4-306f-49f5-a2ab-ef5412ede34e", + "name": "Spawner (2502)", + "location": [517, 513, 32], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "BakeKitsune", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "402e05e6-7371-4469-ab9b-32ac67e0dbf0", + "name": "Spawner (2502)", + "location": [1291, 695, 19], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "LesserHiryu", "maxCount": 2, "probability": 100 }, + { "name": "RevenantLion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1fa14d5-38a8-41b5-8deb-d256e37f49d7", + "name": "Spawner (2502)", + "location": [1354, 700, 33], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EliteNinja", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7f962e07-3668-48c6-889e-d2afab115c85", + "name": "Spawner (2502)", + "location": [273, 606, 7], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "038fac93-3c13-44ef-aba9-491c8ad45a2b", + "name": "Spawner (2502)", + "location": [518, 477, 32], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "BakeKitsune", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "99dd39c0-83e8-4f4d-a661-eb6bee0601b3", + "name": "Spawner (2502)", + "location": [159, 688, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "52415a9d-22d9-4e34-af5a-f5f995a08d02", + "name": "Spawner (2502)", + "location": [157, 685, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, + { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "742dfb62-1a8b-4abe-89d2-2aa5b3eccd8c", + "name": "Spawner (2502)", + "location": [246, 768, 55], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1966d23-2f5c-4a49-bc2c-b18756f292c3", + "name": "Spawner (2502)", + "location": [186, 534, 41], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "05dffa7d-7472-4317-82f0-35e8d6c53fc8", + "name": "Spawner (2502)", + "location": [374, 820, 34], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "RaiJu", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "34706834-7851-4da5-928a-c74d6b0c84c3", + "name": "Spawner (2502)", + "location": [258, 709, 41], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a5616fa0-d8b0-4711-bab9-34bacc355ed0", + "name": "Spawner (2502)", + "location": [124, 646, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e531f0c-3c40-4567-bc25-d433936fcf2a", + "name": "Spawner (2502)", + "location": [123, 647, 36], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, + { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0af3775d-7a5c-458c-920d-9147d2490ac4", + "name": "Spawner (2502)", + "location": [356, 722, 54], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "YomotsuWarrior", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ae8aaaa8-249c-4994-b84d-c27860c04b12", + "name": "Spawner (2502)", + "location": [201, 731, 30], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1763036e-1279-413f-93f8-447989b5b34c", + "name": "Spawner (2502)", + "location": [181, 588, 45], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19d3293a-39da-428f-b8f0-64d1aa2ca711", + "name": "Spawner (2502)", + "location": [180, 586, 45], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, + { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84d7c14d-faf3-4902-a569-f1426b2f16c3", + "name": "Spawner (2502)", + "location": [200, 803, 62], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ec646b02-cb01-4fba-b336-7d617095b3d3", + "name": "Spawner (2502)", + "location": [166, 583, 49], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Yamandon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9b5e2d60-bf93-418d-a447-fa42e903a4e4", + "name": "Spawner (2502)", + "location": [190, 856, 52], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e396724d-7481-4cb6-9302-77ed6dfb8b98", + "name": "Spawner (2502)", + "location": [206, 891, 56], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6f788eb2-7306-4b06-9368-c6ff57e09ff7", + "name": "Spawner (2502)", + "location": [1008, 246, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "005a9ed1-16b4-49a2-8f12-ee718ea97d4d", + "name": "Spawner (2502)", + "location": [212, 646, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, + { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b6be5e10-9a08-4766-beb3-e9a445d4ae57", + "name": "Spawner (2502)", + "location": [208, 936, 43], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9d0b815f-1f4b-499f-9c0d-4cfc20a62983", + "name": "Spawner (2502)", + "location": [248, 928, 36], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5865a64e-b788-493a-9334-43fcec0bab98", + "name": "Spawner (2502)", + "location": [221, 968, 34], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0c8687ee-6ffe-4243-81ba-5938dc3c80c3", + "name": "Spawner (2502)", + "location": [192, 1014, 10], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1dc1ab31-c521-4fdd-8248-fcf3c97717c6", + "name": "Spawner (2502)", + "location": [1016, 292, 30], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31ab5811-6361-44eb-9bda-c31fb4444295", + "name": "Spawner (2502)", + "location": [232, 1116, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3f8ba73c-44f5-41b9-affa-72a4bf38bee9", + "name": "Spawner (2502)", + "location": [948, 247, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 }, + { "name": "EliteNinja", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65bb731d-dfb2-4eeb-9858-03d58c785b2a", + "name": "Spawner (2502)", + "location": [285, 1156, 13], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61f7f65b-b91f-4e51-8551-e72d045d225f", + "name": "Spawner (2502)", + "location": [261, 1186, 20], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "73714dcc-c107-4e57-98e3-800002a6ba8b", + "name": "Spawner (2502)", + "location": [251, 1074, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1eabd7ef-f919-48c9-a2d9-bd0953eca101", + "name": "Spawner (2502)", + "location": [962, 203, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 }, + { "name": "EliteNinja", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "818696a6-799b-49c6-855a-0b4efd4dcfda", + "name": "Spawner (2502)", + "location": [916, 225, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 }, + { "name": "EliteNinja", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2fb8bd06-f301-46d7-bc73-b79ea25bc9a7", + "name": "Spawner (2502)", + "location": [899, 271, 34], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ac2fd03-8350-4d2a-8c44-a2a7a126356e", + "name": "Spawner (2502)", + "location": [819, 208, 38], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02267402-1f6a-40ba-82fc-1b3bec08fcd6", + "name": "Spawner (2502)", + "location": [248, 525, 41], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 6, "probability": 100 }, + { "name": "Kappa", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "90b5cbd2-ec33-4b76-ab24-9b3dace6724b", + "name": "Spawner (2502)", + "location": [824, 280, 33], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc472613-2b05-41aa-af9a-db4ef9e40fd0", + "name": "Spawner (2502)", + "location": [284, 423, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d34f4cf7-eecf-4074-82e9-065d8aa94c1a", + "name": "Spawner (2502)", + "location": [265, 406, 22], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "EliteNinja", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fb71c80f-279b-4f05-8037-e53f05fef9f4", + "name": "Spawner (2502)", + "location": [289, 477, 32], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "EliteNinja", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "31e9bebd-7db7-46c5-a143-e540ca8ccd98", + "name": "Spawner (2502)", + "location": [326, 454, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a4c44311-a4b2-4bc4-8533-2723551964b5", + "name": "Spawner (2502)", + "location": [765, 189, 42], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a83437ce-696c-4d66-93e7-90777f276afb", + "name": "Spawner (2502)", + "location": [360, 466, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aa9544d7-3f07-4e3f-b2e1-0a37763cd99b", + "name": "Spawner (2502)", + "location": [384, 460, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e6108ac2-0a40-4c99-a006-ee8b9d3950b9", + "name": "Spawner (2502)", + "location": [418, 463, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Mongbat", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "516cda7d-9e69-4aba-9e5d-9fded4599601", + "name": "Spawner (2502)", + "location": [425, 443, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "149dab9c-9eba-43fc-922d-6f0c001552a4", + "name": "Spawner (2502)", + "location": [472, 433, 27], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4dfbc791-7f39-44f7-99e9-4558e98c5055", + "name": "Spawner (2502)", + "location": [407, 399, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1a9c5dc9-b415-4da6-83f1-eab7300e39bf", + "name": "Spawner (2502)", + "location": [358, 396, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "896fbcbf-72c3-4280-bc24-c65df790c56e", + "name": "Spawner (2502)", + "location": [725, 246, 36], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa3b9c76-9b9c-44f7-94cf-43c7b90277a7", + "name": "Spawner (2502)", + "location": [769, 152, 12], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f48b4b8-cd8e-48fd-815b-04539b2e7295", + "name": "Spawner (2502)", + "location": [323, 374, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adafc69d-7044-4319-8e4a-6e729aabfba0", + "name": "Spawner (2502)", + "location": [773, 229, 64], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "318bacdf-f3b3-4619-95b6-dc8ce45e2f67", + "name": "Spawner (2502)", + "location": [415, 351, 28], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Ronin", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "26a8d09d-b64b-40ab-a798-4df18a9b37a9", + "name": "Spawner (2502)", + "location": [789, 276, 56], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "796af405-0888-48d6-945b-2da6c9f5578d", + "name": "Spawner (2502)", + "location": [493, 378, 45], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cba2353c-6c72-4abe-b04e-5dbc6fc24027", + "name": "Spawner (2502)", + "location": [454, 374, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2cb5338d-2c87-40b8-bd7a-f6ed6162ecc8", + "name": "Spawner (2502)", + "location": [798, 377, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d344dbc9-d8ce-4502-bd9d-09be8e5e1286", + "name": "Spawner (2502)", + "location": [472, 336, 38], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "435eff18-25d3-4e71-849c-6e8c69c05c9f", + "name": "Spawner (2502)", + "location": [841, 326, 32], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RuneBeetle", "maxCount": 6, "probability": 100 }, + { "name": "EvilMage", "maxCount": 6, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 6, "probability": 100 }, + { "name": "FanDancer", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1afa53a-2806-4717-9ce7-04b6ac4afeab", + "name": "Spawner (2502)", + "location": [907, 292, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d0734d58-9c91-45c2-8b51-69d73217e9d7", + "name": "Spawner (2502)", + "location": [526, 299, 26], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Yamandon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0eef3fac-cdf6-4f36-bfca-b979d8f936af", + "name": "Spawner (2502)", + "location": [540, 343, 8], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "436deca6-7510-45d7-84b4-9d5749246ca3", + "name": "Spawner (2502)", + "location": [527, 393, 53], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "TsukiWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a25ae4f2-e3d4-4c6c-ae56-10c00f828436", + "name": "Spawner (2502)", + "location": [566, 430, 35], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98e06265-c66b-426c-87f1-fa8bcfbfbd18", + "name": "Spawner (2502)", + "location": [892, 391, 14], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3946cdc1-9fcf-40fd-91ab-2f52f6c97592", + "name": "Spawner (2502)", + "location": [873, 326, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "EvilMage", "maxCount": 4, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "919574b9-7243-4843-94d0-cb4f5b4d0f1b", + "name": "Spawner (2502)", + "location": [594, 362, 22], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47a5ecc2-c805-4edf-a622-3f9cc0655911", + "name": "Spawner (2502)", + "location": [861, 335, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Kappa", "maxCount": 4, "probability": 100 }, + { "name": "Ronin", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d7641cb5-bb06-4d5a-9b9d-24413ae09e8c", + "name": "Spawner (2502)", + "location": [853, 386, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Kappa", "maxCount": 4, "probability": 100 }, + { "name": "Ronin", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "270fa492-ee22-44dd-9f22-b83bcd5ce6eb", + "name": "Spawner (2502)", + "location": [649, 409, 36], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "RaiJu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e84fef60-0b86-48d0-8e63-b754a89b1579", + "name": "Spawner (2502)", + "location": [677, 446, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RevenantLion", "maxCount": 4, "probability": 100 }, + { "name": "BakeKitsune", "maxCount": 4, "probability": 100 }, + { "name": "Oni", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4980823a-bcbe-431e-8ad2-594bf7e28aaf", + "name": "Spawner (2502)", + "location": [907, 394, 10], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9bf85cd-f6ef-4207-852e-0c8128711512", + "name": "Spawner (2502)", + "location": [927, 451, 14], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10592fab-f09a-450d-999b-b35b30fe5cb0", + "name": "Spawner (2502)", + "location": [932, 364, 3], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e60bef67-7439-47c5-950a-4d6ba75f9456", + "name": "Spawner (2502)", + "location": [930, 405, 11], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "97c87b67-b804-4135-8bbf-80e2e1595bf2", + "name": "Spawner (2502)", + "location": [992, 383, 5], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b504e238-76e5-4634-a3cc-deb3b3ce3db2", + "name": "Spawner (2502)", + "location": [997, 407, -2], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37a35bdc-a33a-47ec-b7d3-e520093ebe29", + "name": "Spawner (2502)", + "location": [951, 409, 9], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01d0fd73-f752-4375-9fa8-46a112f9cf01", + "name": "Spawner (2502)", + "location": [1036, 366, 7], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19e63dc7-82b4-4a67-94e4-5f016bf5be36", + "name": "Spawner (2502)", + "location": [1034, 293, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a66af15-e45e-41be-a474-91e6ff754ae5", + "name": "Spawner (2502)", + "location": [980, 278, 30], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ronin", "maxCount": 4, "probability": 100 }, + { "name": "FanDancer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7142d685-eb27-4e84-92cb-4344d63d739e", + "name": "Spawner (2502)", + "location": [948, 522, 7], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Ronin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9cc378fc-7158-4e04-83ad-895160e6d1ec", + "name": "Spawner (2502)", + "location": [918, 439, 14], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "602fd66a-e62a-4963-a028-bbdc0d8f9401", + "name": "Spawner (2502)", + "location": [951, 467, 12], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62fb0bce-3f04-4d27-9341-14e8e9ea6497", + "name": "Spawner (2502)", + "location": [1150, 1072, 14], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "Kappa", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46e8f0bc-a094-423d-bd18-23a05efa801d", + "name": "Spawner (2502)", + "location": [979, 477, 2], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c61fe6f-8b81-40ed-acef-188199111f3c", + "name": "Spawner (2502)", + "location": [983, 448, -1], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91372a1f-eb92-49f7-b7ae-25c8fb221855", + "name": "Spawner (2502)", + "location": [1051, 473, 7], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b818a6a6-2295-402a-912f-a3c7b680d71a", + "name": "Spawner (2502)", + "location": [1062, 469, 8], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DireWolf", "maxCount": 4, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9bbad6f-cb96-4a0d-90dd-5711a4b19ec7", + "name": "Spawner (2502)", + "location": [1086, 452, 14], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 }, + { "name": "RuneBeetle", "maxCount": 4, "probability": 100 }, + { "name": "Lizardman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c79a8c2-cbae-49ae-87ca-3f32e5c1eda0", + "name": "Spawner (2502)", + "location": [1084, 464, 15], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Yamandon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "866db38c-6643-486a-973b-ed4eb3855e05", + "name": "Spawner (2502)", + "location": [999, 580, 14], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "KazeKemono", "maxCount": 4, "probability": 100 }, + { "name": "TsukiWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d935aa73-025f-4c79-92df-f46b642cda74", + "name": "Spawner (2502)", + "location": [921, 593, 20], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7ddcace7-c82a-4f05-9855-3c4c413e4602", + "name": "Spawner (2502)", + "location": [1042, 559, 22], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Reaper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0f013060-e752-4f42-972f-9b5638b67aa4", + "name": "Spawner (2502)", + "location": [1062, 564, 22], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Reaper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d3d6ff67-6800-4108-9039-071f96f48955", + "name": "Spawner (2502)", + "location": [1092, 563, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e7895e4-b062-48a8-bf1e-11bd423cf49f", + "name": "Spawner (2502)", + "location": [1146, 516, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9d0a3d34-a633-4d61-9d84-b2df29ae7051", + "name": "Spawner (2502)", + "location": [192, 770, 56], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Mongbat", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4615040f-530b-4719-bd2f-2a201281cd44", + "name": "Spawner (2502)", + "location": [1214, 531, 28], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e030c4f-6f79-41b9-add4-785e95c7d016", + "name": "Spawner (2502)", + "location": [1172, 535, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Reaper", "maxCount": 4, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ace8cee6-59ca-4e3c-90a7-1285a1758f14", + "name": "Spawner (2502)", + "location": [1184, 616, 19], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "LavaSerpent", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "59600e4f-219e-4f5b-baa3-6714be5b9f7e", + "name": "Spawner (2502)", + "location": [1058, 799, 13], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3cfcea02-60a9-463b-a5a3-67bae5267235", + "name": "Spawner (2502)", + "location": [1181, 484, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "97ce7efd-4bea-4256-aec4-dde1404c559b", + "name": "Spawner (2502)", + "location": [1196, 507, 26], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Reaper", "maxCount": 4, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "150fd272-8a8e-4313-9751-5b5e9a7b9c74", + "name": "Spawner (2502)", + "location": [1258, 531, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Reaper", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c0f9cdb5-34f3-470e-9080-e375959d95e1", + "name": "Spawner (2502)", + "location": [1285, 573, 30], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bbf25923-04a3-4b44-9e43-6199ea5a5dd1", + "name": "Spawner (2502)", + "location": [1321, 619, 38], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, + { "name": "BoneKnight", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 8, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f7d75f4-e71b-474c-8711-00345b91e83d", + "name": "Spawner (2502)", + "location": [1214, 652, 90], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5228c3d-ef03-46e5-9d44-8b2e223eef4d", + "name": "Spawner (2502)", + "location": [1176, 701, 72], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b5ace63-c337-4f3b-b82b-7901942fb71a", + "name": "Spawner (2502)", + "location": [1144, 667, 70], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5316c66-ce00-4cd1-9151-7508e8d0b6e8", + "name": "Spawner (2502)", + "location": [1134, 705, 72], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 4, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa991041-da28-4f4e-980c-8034ec03aa57", + "name": "Spawner (2502)", + "location": [1105, 753, 44], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "HellCat", "maxCount": 4, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9314780-e6f4-4589-a64b-cd470dc5bb32", + "name": "Spawner (2502)", + "location": [1021, 802, 0], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, + { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd76c01b-91e2-4a35-b2d2-c30a7ddfe78a", + "name": "Spawner (2502)", + "location": [931, 833, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7fdc0616-9830-43aa-9d9c-a51bda258191", + "name": "Spawner (2502)", + "location": [1006, 857, 15], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, + { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d393499a-3788-4887-b582-2903c0ae3c16", + "name": "Spawner (2502)", + "location": [980, 815, 15], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, + { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4354759f-5548-41f0-acb1-fbc9c7a9694a", + "name": "Spawner (2502)", + "location": [892, 652, 17], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "KazeKemono", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "67b16a8e-dbf3-46f2-8c1c-89f4601d9f56", + "name": "Spawner (2502)", + "location": [968, 753, 3], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TsukiWolf", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3c541073-7877-418c-af03-7864f952a710", + "name": "Spawner (2502)", + "location": [938, 722, 10], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "KazeKemono", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f314eaf0-1752-4943-82f0-89288cf6b903", + "name": "Spawner (2502)", + "location": [932, 685, 12], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "KazeKemono", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7ebea3a2-31d2-4ff1-b858-42ce1945f701", + "name": "Spawner (2502)", + "location": [1120, 1099, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 8, "probability": 100 }, + { "name": "RevenantLion", "maxCount": 8, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5da7254b-6b8b-4ad4-b253-e9a3f8b84350", + "name": "Spawner (2502)", + "location": [840, 714, 16], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "KazeKemono", "maxCount": 8, "probability": 100 }, + { "name": "EliteNinja", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65e659e7-d71c-472d-9552-755fd0b7d187", + "name": "Spawner (2502)", + "location": [883, 604, 39], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Hiryu", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a30ef889-c94e-4870-a1c7-6bc38738919d", + "name": "Spawner (2502)", + "location": [1012, 631, 11], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19a680ca-e843-4941-8443-572b8299cccf", + "name": "Spawner (2502)", + "location": [1076, 864, 56], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, + { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22d610e3-5455-4ad6-ba5a-0894e1a78d2e", + "name": "Spawner (2502)", + "location": [1055, 887, 38], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb28888e-536f-4588-87ed-32e0151c3557", + "name": "Spawner (2502)", + "location": [1052, 976, 26], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "497466b2-aa4f-4a36-b431-53c89e5c6afe", + "name": "Spawner (2502)", + "location": [1064, 1029, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 8, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfdb9550-70ee-48ef-a165-4dd2b82a5ace", + "name": "Spawner (2502)", + "location": [1092, 980, 33], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Kappa", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e5ec5310-19bc-428f-92d3-b6e972280758", + "name": "Spawner (2502)", + "location": [1086, 910, 61], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 4, "probability": 100 }, + { "name": "EliteNinja", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e8727ee4-86b2-4ac0-bff7-bd503904199f", + "name": "Spawner (2502)", + "location": [637, 357, 32], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "RevenantLion", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "18a2424f-1bd0-4745-b96d-07275a0b28cd", + "name": "Spawner (2502)", + "location": [1282, 898, 12], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Oni", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cabd6472-2187-4078-85e1-437ab1d2d0b8", + "name": "Spawner (2502)", + "location": [243, 1031, 13], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1c5f957-9c22-4839-9b22-1be66ab4a078", + "name": "Spawner (2502)", + "location": [742, 1012, 33], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d54520b-6357-4a09-a9dd-024c82a9dc3d", + "name": "Spawner (2502)", + "location": [662, 495, 28], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "BakeKitsune", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47a74e20-3a5b-43ee-b381-d945b34ddc8a", + "name": "Spawner (2502)", + "location": [1293, 864, -1], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7043408d-2dc5-4c52-b192-2e95d93195c5", + "name": "Spawner (2502)", + "location": [1321, 847, 13], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EliteNinja", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d46fba23-4a24-4736-8701-daa96189ff9f", + "name": "Spawner (2502)", + "location": [1343, 654, 35], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "LesserHiryu", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eb40a1dc-8905-430d-a5b6-180d5629d0a2", + "name": "Spawner (2502)", + "location": [294, 748, 55], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e9f063b7-3bd6-4490-804e-1dcedcd98cc0", + "name": "Spawner (2502)", + "location": [198, 924, 42], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "YomotsuWarrior", "maxCount": 6, "probability": 100 }, + { "name": "KazeKemono", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07e15b51-9db0-4924-8c8e-5626e94a0eec", + "name": "Spawner (2502)", + "location": [185, 1046, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1382b18e-ee1d-4364-8fe0-604a01e29711", + "name": "Spawner (2502)", + "location": [210, 1085, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Kappa", "maxCount": 12, "probability": 100 }, + { "name": "Lizardman", "maxCount": 12, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 12, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 12, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "deb29408-0b0a-4bbe-bfee-962353f80e94", + "name": "Spawner (2502)", + "location": [581, 953, 33], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "997db593-15d5-4963-9659-0a372b565d86", + "name": "Spawner (2502)", + "location": [533, 991, 53], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b2ac0fff-06ad-438a-a365-0bb7ad3502a6", + "name": "Spawner (2502)", + "location": [562, 984, 43], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "defa7e90-33db-45c3-8ef7-6f5b153bf4cd", + "name": "Spawner (2502)", + "location": [511, 956, 27], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4540125e-93b0-4491-bdee-fd09145a4a6c", + "name": "Spawner (2502)", + "location": [524, 934, 19], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5bf51fd3-f2c9-49f3-b680-549db2d662fe", + "name": "Spawner (2502)", + "location": [535, 918, 14], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2d2e12f-2919-4097-81b2-69c88f8d4f63", + "name": "Spawner (2502)", + "location": [526, 889, 11], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ee8bcfb4-c8aa-46d8-a29c-3350388df282", + "name": "Spawner (2502)", + "location": [573, 933, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ceff3a89-0382-4aa9-b03d-e5935edcafe7", + "name": "Spawner (2502)", + "location": [612, 888, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Skeleton", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4526aff8-ef6d-4c5b-b1ea-0754687af7bb", + "name": "Spawner (2502)", + "location": [570, 840, 0], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3cc7a85a-0c2e-4368-8912-0c52aef08729", + "name": "Spawner (2502)", + "location": [601, 930, 32], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "SkeletalMage", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8dcd72ef-b063-43c7-ae83-84dd5d2389c2", + "name": "Spawner (2502)", + "location": [638, 866, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9723d0f8-bdca-4e26-a59c-750042c787a3", + "name": "Spawner (2502)", + "location": [666, 896, 28], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3247cd95-f365-49a5-8a8d-efa7b29def12", + "name": "Spawner (2502)", + "location": [700, 826, 36], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Orc", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ddecebbf-fc04-45ad-9eea-328790b80fc0", + "name": "Spawner (2502)", + "location": [715, 831, 36], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Orc", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a2b02241-de57-4e42-908a-d89b90ae45d3", + "name": "Spawner (2502)", + "location": [725, 823, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "de26b10f-8b26-4f72-92aa-058a19600dd2", + "name": "Spawner (2502)", + "location": [771, 824, 16], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "73b7620d-f5f9-4fdb-9379-831522388d84", + "name": "Spawner (2502)", + "location": [737, 853, 35], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "243f4da3-3b86-46e7-afd0-66b9e67a506e", + "name": "Spawner (2502)", + "location": [709, 877, 18], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "462cc023-8496-4574-8963-5577ce18642d", + "name": "Spawner (2502)", + "location": [683, 857, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Orc", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "67cd30f3-8ec3-4cbb-91b5-a2074fab67f4", + "name": "Spawner (2502)", + "location": [667, 943, 43], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c41c2c47-c049-41b3-b4cf-b48cc130a9e0", + "name": "Spawner (2502)", + "location": [619, 981, 45], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f22b0f9-3a7d-4520-935a-7d3e965708d4", + "name": "Spawner (2502)", + "location": [588, 1012, 62], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7537f1f7-5755-4e45-ba02-7f4a79ccb98b", + "name": "Spawner (2502)", + "location": [644, 1005, 55], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9ed6e580-23db-415f-a93d-4a1841495166", + "name": "Spawner (2502)", + "location": [668, 986, 46], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd996179-baa6-48a6-b065-5371b0592cfd", + "name": "Spawner (2502)", + "location": [688, 979, 44], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c226f07-9334-453c-b888-63f6f7bffeb4", + "name": "Spawner (2502)", + "location": [680, 956, 44], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "692aae7e-d475-458e-8d26-a15b280edd8f", + "name": "Spawner (2502)", + "location": [722, 916, 40], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetle", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8fb6a683-1f7b-4364-9538-2db139f0288e", + "name": "Spawner (2502)", + "location": [729, 934, 42], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "990e1da9-704d-4e3d-b1a6-9180cea7dabe", + "name": "Spawner (2502)", + "location": [771, 979, 29], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "904d1a0d-b3b7-43b4-b37f-5c8a61bffbdb", + "name": "Spawner (2502)", + "location": [803, 961, 14], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76c890d5-23df-453d-bb6b-d1e6789384c3", + "name": "Spawner (2502)", + "location": [819, 944, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "86222a57-5629-47c3-8a8d-977853e8081c", + "name": "Spawner (2502)", + "location": [792, 920, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed3f775c-f53d-46b2-8a53-bc325a2cb088", + "name": "Spawner (2502)", + "location": [794, 890, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b332005a-d3b2-4d5a-a527-6858e5197c0b", + "name": "Spawner (2502)", + "location": [819, 902, 27], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eb58e0a4-29a4-4f28-a54b-226136752d59", + "name": "Spawner (2502)", + "location": [770, 875, 30], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19703e47-77ae-4ee1-97ba-199e119d633d", + "name": "Spawner (2502)", + "location": [760, 941, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }, + { "name": "DeathwatchBeetle", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a53a4f7-a748-48c5-b72c-40315e1b6cc8", + "name": "Spawner (2502)", + "location": [874, 971, 45], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "DeathwatchBeetleHatchling", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "414e159b-efdc-4c32-be33-60f677efc824", + "name": "Spawner (2502)", + "location": [1105, 855, 50], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 4, "probability": 100 }, + { "name": "GiantBlackWidow", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e4489f8-0e90-420b-a546-8628a6b39871", + "name": "Spawner (2502)", + "location": [926, 145, 49], + "map": "Tokuno", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "IceElemental", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9412516d-cb2a-4ea7-a03d-9ea7354b5437", + "name": "Spawner (2502)", + "location": [879, 128, 44], + "map": "Tokuno", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "IceElemental", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a2f5196-88ac-4cc7-9f02-d08890632f18", + "name": "Spawner (2502)", + "location": [914, 55, 36], + "map": "Tokuno", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "IceElemental", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63477a04-4f55-4c09-b594-0031d9d4e25d", + "name": "Spawner (2502)", + "location": [947, 103, 46], + "map": "Tokuno", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "IceElemental", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3416dcdb-aebf-4f59-8158-bd16a36d460c", + "name": "Spawner (2502)", + "location": [976, 144, 35], + "map": "Tokuno", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "IceElemental", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 2, "probability": 100 }, + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "LadyOfTheSnow", "maxCount": 2, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "Walrus", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/tokuno/TownsLife.json b/Distribution/Data/Spawns/uoml/tokuno/TownsLife.json new file mode 100644 index 000000000..cf8edb6a7 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/tokuno/TownsLife.json @@ -0,0 +1,498 @@ +[ + { + "type": "Spawner", + "guid": "660e512a-833e-42d2-b13d-8d9aee2b8e68", + "name": "Spawner (2503)", + "location": [667, 1343, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Rabbit", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7c22ce54-d85a-48c0-8388-25f26d1c7904", + "name": "Spawner (2503)", + "location": [756, 1236, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "787cabab-33ea-4938-a32d-5f3443af22c4", + "name": "Spawner (2503)", + "location": [712, 1238, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Cat", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8065872f-41df-44a9-b382-d5d16e9f31da", + "name": "Spawner (2503)", + "location": [683, 1231, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Rat", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3eced8bf-e4d6-484b-ae5f-6519e827a787", + "name": "Spawner (2503)", + "location": [679, 1224, 25], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dbffb36b-2e05-4968-96f6-c8f53ddeb50a", + "name": "Spawner (2503)", + "location": [706, 1208, 25], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "217af4ba-df3f-4134-b360-8f8f921e8a01", + "name": "Spawner (2503)", + "location": [721, 1274, 25], + "map": "Tokuno", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9780bf47-bc15-4bdd-8dc6-0a5ec2abddea", + "name": "Spawner (2503)", + "location": [740, 1284, 25], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ae3ea0e1-e260-4f24-a0f2-7a617cbe7bbe", + "name": "Spawner (2503)", + "location": [713, 1351, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 2, "probability": 100 }, + { "name": "Cat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9dd4287-6d11-4e50-b4b1-834c431ee60b", + "name": "Spawner (2503)", + "location": [710, 1350, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb8699ff-ca4c-44b1-9dd8-422e6e31fbbf", + "name": "Spawner (2503)", + "location": [636, 1331, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "Pig", "maxCount": 1, "probability": 100 }, + { "name": "Crane", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "59135bf6-67a7-42db-ae76-2d9d53ee8d6b", + "name": "Spawner (2503)", + "location": [628, 1310, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "366108fc-b366-4620-b8bb-4d04014d40b7", + "name": "Spawner (2503)", + "location": [625, 1317, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 2, "probability": 100 }, + { "name": "Goat", "maxCount": 1, "probability": 100 }, + { "name": "Rabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "63828858-74cb-4daa-a2e3-d79928cd539d", + "name": "Spawner (2503)", + "location": [630, 1225, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "Crane", "maxCount": 3, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ca277a3-a47d-4c33-8470-60d67d1fb2c7", + "name": "Spawner (2503)", + "location": [628, 1272, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "Cat", "maxCount": 3, "probability": 100 }, + { "name": "Dog", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40d40ab7-a774-4443-8225-790b81878d3a", + "name": "Spawner (2503)", + "location": [768, 1192, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3688f98-8e8b-4100-8952-4f90020ab8ae", + "name": "Spawner (2503)", + "location": [857, 1166, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Sheep", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c7bce2da-6a6c-4d0f-9c79-70c5a2895962", + "name": "Spawner (2503)", + "location": [853, 1287, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Sheep", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e828c6c6-2867-4d26-a77a-ab581c4a7dcc", + "name": "Spawner (2503)", + "location": [843, 1247, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Sheep", "maxCount": 2, "probability": 100 }, + { "name": "Pig", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "962520ff-e7ec-4187-b385-5550af5428cb", + "name": "Spawner (2503)", + "location": [844, 1198, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Rabbit", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "Pig", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4400a7f9-33b7-4f12-993e-6370e8422577", + "name": "Spawner (2503)", + "location": [812, 1222, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9bf1fbe7-640a-44fe-89c5-b8771032d64b", + "name": "Spawner (2503)", + "location": [807, 1287, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "42f392a2-faec-48fe-b8fa-44c394ce4f31", + "name": "Spawner (2503)", + "location": [844, 1318, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Crane", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Pig", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40d3a681-f9b5-4866-b6c2-93aa8d211660", + "name": "Spawner (2503)", + "location": [834, 1351, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3cc46920-b82d-4c3b-b622-611f7b416b66", + "name": "Spawner (2503)", + "location": [803, 1338, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df568bbb-9e8c-4594-a32b-85dd93540f10", + "name": "Spawner (2503)", + "location": [770, 1344, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Goat", "maxCount": 1, "probability": 100 }, + { "name": "Pig", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03f1632a-1fc5-4132-92a7-288b4baf9ae9", + "name": "Spawner (2503)", + "location": [744, 1347, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Sheep", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a5e3558-be1a-49cc-a5cc-201c160e6c7b", + "name": "Spawner (2503)", + "location": [711, 1322, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Dog", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Crane", "maxCount": 2, "probability": 100 }, + { "name": "Sheep", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4d88eea-fd00-4029-bf05-960a4a03a082", + "name": "Spawner (2503)", + "location": [690, 1288, 25], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Dog", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "Cat", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/tokuno/Vendors.json b/Distribution/Data/Spawns/uoml/tokuno/Vendors.json new file mode 100644 index 000000000..b8f7415bd --- /dev/null +++ b/Distribution/Data/Spawns/uoml/tokuno/Vendors.json @@ -0,0 +1,673 @@ +[ + { + "type": "Spawner", + "guid": "698d030e-cf3f-40fd-be6e-4a92866dbfb1", + "name": "Spawner (2504)", + "location": [668, 1254, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "BardGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "15590b9b-8815-4906-8e9e-d08a54b6a2f1", + "name": "Spawner (2504)", + "location": [773, 1248, 26], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f44766ae-31e3-46fa-94ce-869a53621012", + "name": "Spawner (2504)", + "location": [662, 1210, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bdc0117b-2e5b-458b-90ca-0f3bfa5489e8", + "name": "Spawner (2504)", + "location": [703, 1262, 25], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 10, + "entries": [ + { "name": "Baker", "maxCount": 1, "probability": 100 }, + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "840e2e09-7a7e-43cc-a5d0-a5be3d4e00d6", + "name": "Spawner (2504)", + "location": [669, 1266, 47], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Shipwright", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "97d04faf-bf2f-4085-bdb9-2a6538c4a97a", + "name": "Spawner (2504)", + "location": [720, 1211, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Tanner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a4b62432-203b-4b3e-894c-651e62d68567", + "name": "Spawner (2504)", + "location": [739, 1223, 45], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Carpenter", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8c714386-6b61-4f4e-9eec-f75cd78010a6", + "name": "Spawner (2504)", + "location": [732, 1256, 30], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f6481ae3-b345-40ab-aaa2-2eea59aee211", + "name": "Spawner (2504)", + "location": [781, 1301, 26], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "152e7156-8c9c-4aae-a560-7972868cbc57", + "name": "Spawner (2504)", + "location": [789, 1278, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0360875a-9408-4f4a-a42c-200f359baccb", + "name": "Spawner (2504)", + "location": [756, 1302, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Armorer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f9e3288-f988-4ea3-95e8-251aa38d064c", + "name": "Spawner (2504)", + "location": [692, 1213, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "TavernKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1bef801b-5ac8-4cde-a3bd-71fff904d997", + "name": "Spawner (2504)", + "location": [679, 1207, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "33f10364-0097-42d5-95b1-c3f73750fda0", + "name": "Spawner (2504)", + "location": [720, 1213, 45], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Tinker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e081b4f9-5c48-4808-82e7-f8bb95309a19", + "name": "Spawner (2504)", + "location": [744, 1219, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Tailor", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1457ebfc-398e-415e-b091-20427af4fb70", + "name": "Spawner (2504)", + "location": [691, 1264, 47], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Mapmaker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f50b6789-7be4-43f0-a1be-c53e85a6997b", + "name": "Spawner (2504)", + "location": [675, 1265, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Bard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "16523597-999a-4504-aa59-7b01c881978d", + "name": "Spawner (2504)", + "location": [689, 1255, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1b386724-c26f-4714-80b3-edaaa46b3ab4", + "name": "Spawner (2504)", + "location": [683, 1298, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Herbalist", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1990cbea-3f71-44da-aec2-0af4e07e3618", + "name": "Spawner (2504)", + "location": [702, 1298, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Alchemist", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11f9b7fa-b1ea-4a93-848a-5339c852afa5", + "name": "Spawner (2504)", + "location": [695, 1297, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Mage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "117e79c9-e92c-4094-89af-ceef02b6d875", + "name": "Spawner (2504)", + "location": [724, 1297, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21467215-aae1-4e1c-bf72-1137b91b71eb", + "name": "Spawner (2504)", + "location": [730, 1282, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Provisioner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5ada367e-0bcb-487e-9e44-ef259bfec2c7", + "name": "Spawner (2504)", + "location": [781, 1219, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Healer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d001030a-99d7-4146-83f9-b5e1aff453b0", + "name": "Spawner (2504)", + "location": [729, 1249, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Banker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "448a5e8c-0682-488b-ba85-4a457ed8329c", + "name": "Spawner (2504)", + "location": [742, 1304, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Armorer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d64f79d1-bb69-4aab-8099-04cc92244b60", + "name": "Spawner (2504)", + "location": [806, 1308, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Weaponsmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7dad1cc2-dfab-490e-915f-8d00ed39f14c", + "name": "Spawner (2504)", + "location": [749, 1303, 25], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "67fe5dc9-67d2-4e5f-afab-03691f497fed", + "name": "Spawner (2504)", + "location": [661, 1209, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dc6b9648-9165-491e-87b2-f5950d8e38ad", + "name": "Spawner (2504)", + "location": [744, 1210, 25], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Weaver", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4b940cb-17ef-4234-842c-2bdcbd7827f0", + "name": "Spawner (2504)", + "location": [779, 1259, 26], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "Weaponsmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "30f1eaf5-633e-494a-aadb-2997f3e8d0a4", + "name": "Spawner (2504)", + "location": [735, 1256, 30], + "map": "Tokuno", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 80, + "entries": [ + { "name": "Noble", "maxCount": 10, "probability": 100 }, + { "name": "Peasant", "maxCount": 10, "probability": 100 }, + { "name": "hirebeggar", "maxCount": 10, "probability": 100 }, + { "name": "Samurai", "maxCount": 10, "probability": 100 }, + { "name": "Ninja", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb70140d-b0d9-41c7-9975-c8136c4ee686", + "name": "Spawner (2504)", + "location": [1166, 802, 27], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cfc1714e-8851-4cf8-a9a5-bb1d4d9236b0", + "name": "Spawner (2504)", + "location": [558, 534, 32], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "03112703-010a-4495-90de-3dceafabf6bb", + "name": "Spawner (2504)", + "location": [502, 589, 24], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1159a610-a01a-4cac-841f-ca37fcf2ab21", + "name": "Spawner (2504)", + "location": [1177, 985, 38], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "901b6857-11f1-4c9f-bb1f-1399406dc3b6", + "name": "Spawner (2504)", + "location": [399, 549, 15], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "294130b1-fd5a-4cef-9c68-a5491f5e59c6", + "name": "Spawner (2504)", + "location": [226, 786, 57], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4830fbdc-9c83-4e22-af7a-cd868807eb9c", + "name": "Spawner (2504)", + "location": [297, 637, 15], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "80122c8e-85c4-41a7-a347-d8c0074e6031", + "name": "Spawner (2504)", + "location": [371, 431, 32], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3df1960a-2e81-4997-ab85-3f1062065c7d", + "name": "Spawner (2504)", + "location": [763, 247, 71], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "95447e64-a9d3-45ca-81c7-318441ca97d4", + "name": "Spawner (2504)", + "location": [1111, 495, 18], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "af820fc2-57d8-4b19-9a54-9bb57f9736cb", + "name": "Spawner (2504)", + "location": [1221, 494, 27], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b5b2efe1-d96b-4d37-ac13-ec6251ad23f9", + "name": "Spawner (2504)", + "location": [895, 519, 53], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "876d5b73-9f59-4446-8144-dd997bf93916", + "name": "Spawner (2504)", + "location": [1152, 936, 65], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b65649fc-1b3a-4124-b063-0f0e7b35d3d2", + "name": "Spawner (2504)", + "location": [979, 238, 21], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "57f4d4ba-3841-4d12-bec6-37b059c49840", + "name": "Spawner (2504)", + "location": [248, 1089, 10], + "map": "Tokuno", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "WanderingHealer", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/tokuno/WildLife.json b/Distribution/Data/Spawns/uoml/tokuno/WildLife.json index 4983e2d18..b9b15fc44 100644 --- a/Distribution/Data/Spawns/uoml/tokuno/WildLife.json +++ b/Distribution/Data/Spawns/uoml/tokuno/WildLife.json @@ -1,6 +1,889 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "7b7eb7c3-f465-4f75-b739-52557290bd17", + "name": "Spawner (2505)", + "location": [1177, 1002, 34], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6177f745-08f2-4d37-a5ae-2f2e3129b903", + "name": "Spawner (2505)", + "location": [629, 510, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 1, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a445a5be-f116-48cf-a9bc-0b083796ba9e", + "name": "Spawner (2505)", + "location": [1198, 941, 36], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f7998dee-c474-49e9-8833-279d375db898", + "name": "Spawner (2505)", + "location": [1141, 894, 57], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a39a0058-8f78-43a4-9028-f509c65ffa7e", + "name": "Spawner (2505)", + "location": [623, 551, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "614d3af0-78c4-4099-9a35-0add14ce3e3f", + "name": "Spawner (2505)", + "location": [1197, 851, 32], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ded03e2-db27-4a82-88ec-7274d5521db0", + "name": "Spawner (2505)", + "location": [1230, 895, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "536320cb-cd64-4a9b-9f44-6b2c3a6b4770", + "name": "Spawner (2505)", + "location": [1255, 866, 12], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54b64e89-5437-4c61-81ee-2adc677a3195", + "name": "Spawner (2505)", + "location": [1230, 822, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ced8bd2c-f463-445a-ad5b-90a5b43e0aa6", + "name": "Spawner (2505)", + "location": [1289, 843, 4], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e22936fe-88bf-42b6-8563-b4d85f37af0d", + "name": "Spawner (2505)", + "location": [1377, 882, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8835e0c4-4f91-493e-a9a5-474c0e5db0b6", + "name": "Spawner (2505)", + "location": [1299, 814, 9], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d629be0-95e5-4dec-bb25-5cfdc8339993", + "name": "Spawner (2505)", + "location": [518, 329, 15], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Gaman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9306a516-f374-4a37-8188-8ba412962f12", + "name": "Spawner (2505)", + "location": [1263, 754, -1], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c444b27-ab3c-4376-aa20-5f39a9623573", + "name": "Spawner (2505)", + "location": [1311, 777, 9], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d97602d0-7bee-4c1b-ab0c-be5b52de898c", + "name": "Spawner (2505)", + "location": [551, 544, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aaba65e1-c0ef-4788-9167-b7cddede3aa5", + "name": "Spawner (2505)", + "location": [502, 532, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "52b4ffec-21bb-4847-ae83-65f2001d581d", + "name": "Spawner (2505)", + "location": [1345, 775, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ec9ed72-e359-46ad-afa1-10a09818b0bb", + "name": "Spawner (2505)", + "location": [1312, 704, 21], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f1c63ab-a484-4556-88c2-2f513b6a73ff", + "name": "Spawner (2505)", + "location": [273, 606, 7], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dacf4f2f-fe97-46a9-9dd9-48b3eed8ba3c", + "name": "Spawner (2505)", + "location": [505, 474, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 2, "probability": 100 }, + { "name": "BrownBear", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "503c5b72-b18c-4f8e-bf90-9b161a77db25", + "name": "Spawner (2505)", + "location": [364, 565, 15], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 3, "probability": 100 }, + { "name": "Dog", "maxCount": 3, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1330307-1cab-42c3-9047-d07dcdb05571", + "name": "Spawner (2505)", + "location": [404, 592, 26], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "BrownBear", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a63fd209-3706-4242-8004-48d9b24e87f7", + "name": "Spawner (2505)", + "location": [377, 635, 50], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c091a0bd-f518-4132-9879-039057bc949b", + "name": "Spawner (2505)", + "location": [58, 104, 0], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8651cb79-bd9b-4294-aedb-a4ca96bae64a", + "name": "Spawner (2505)", + "location": [356, 705, 50], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d903bb8-480f-4d62-b957-ae627809d9d5", + "name": "Spawner (2505)", + "location": [190, 856, 52], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Gaman", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ec7098d8-a0eb-43da-8b23-43032cb8b93a", + "name": "Spawner (2505)", + "location": [324, 664, 29], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f0ecf0a7-871d-4d75-8fbb-487dbe7ef934", + "name": "Spawner (2505)", + "location": [327, 604, 15], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "Cat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f2ecc77-7a9d-483c-b2d2-28b021b69767", + "name": "Spawner (2505)", + "location": [381, 511, 22], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "BrownBear", "maxCount": 2, "probability": 100 }, + { "name": "BlackBear", "maxCount": 2, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c461fc3-195d-4f3e-961b-c46b24a68048", + "name": "Spawner (2505)", + "location": [307, 572, 33], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d5217d9-a132-4859-ba9e-af45db26bc7a", + "name": "Spawner (2505)", + "location": [146, 109, 0], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40d60aa8-583e-474f-a59f-8b86a0233014", + "name": "Spawner (2505)", + "location": [206, 891, 56], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dcf2c166-cc37-459d-9ce7-be185248d6bb", + "name": "Spawner (2505)", + "location": [208, 936, 43], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84e49574-5215-480c-93e0-98a4320678a1", + "name": "Spawner (2505)", + "location": [221, 968, 34], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2dfdc1dd-5220-4169-86e7-4342c692e2f6", + "name": "Spawner (2505)", + "location": [192, 1014, 10], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 8, "probability": 100 }, + { "name": "Rat", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca287a73-5914-4d4f-a338-9db48f48e9b9", + "name": "Spawner (2505)", + "location": [1008, 260, 36], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2be2ad47-84cf-4567-abe9-a0eea8a95ee4", + "name": "Spawner (2505)", + "location": [232, 1116, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "BullFrog", "maxCount": 12, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8978752-311f-4eef-86ff-3a85cf17c09d", + "name": "Spawner (2505)", + "location": [1021, 297, 28], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4943e08b-e268-4035-a091-6f3d05e5aab1", + "name": "Spawner (2505)", + "location": [285, 1156, 13], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "BullFrog", "maxCount": 12, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e89bf8f1-8463-4d29-89f0-89965fd42d44", + "name": "Spawner (2505)", + "location": [941, 206, 17], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1b8a9d9-eb0c-4de4-aa22-f8ebe4e6cd65", + "name": "Spawner (2505)", + "location": [261, 1186, 20], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44acca90-d356-438d-9a80-a27d07ecac27", + "name": "Spawner (2505)", + "location": [251, 1074, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "BullFrog", "maxCount": 12, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd7c3147-b193-4867-9912-9e8cf19ccde5", + "name": "Spawner (2505)", + "location": [901, 264, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "4e6fb0ca-fe70-4eef-84e7-841ecf8dc448", "name": "Spawner (2505)", "location": [926, 145, 49], @@ -18,7 +901,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ebe89f8e-f0ac-4a42-9b01-f7fc624c4bd4", "name": "Spawner (2505)", "location": [879, 128, 44], @@ -36,7 +919,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c4dd55c7-4108-4f31-897c-f563f1639e2b", "name": "Spawner (2505)", "location": [914, 55, 36], @@ -54,7 +937,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "f0063c43-6b87-44af-9255-a10aa29f60f0", "name": "Spawner (2505)", "location": [947, 103, 46], @@ -72,7 +955,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c871b42a-1409-4899-a2fe-23299cd6c4ee", "name": "Spawner (2505)", "location": [976, 144, 35], @@ -88,5 +971,2298 @@ { "name": "Walrus", "maxCount": 8, "probability": 100 }, { "name": "SnowLeopard", "maxCount": 8, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "a730d8a0-de99-42bc-8c4b-06bbb07de2bd", + "name": "Spawner (2505)", + "location": [243, 563, 42], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Crane", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81e3bdc8-9415-461f-b1ab-91824c47f65e", + "name": "Spawner (2505)", + "location": [812, 218, 41], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb021200-15d6-43f6-9d1f-92c5da81b194", + "name": "Spawner (2505)", + "location": [384, 460, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "Mongbat", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09a9d3ff-55d0-4e8c-9fe7-d8458206ec1b", + "name": "Spawner (2505)", + "location": [418, 463, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "Mongbat", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f4e6a55-2723-4d2e-b192-a8881e2b7c96", + "name": "Spawner (2505)", + "location": [356, 403, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c1322bf-5ebb-464a-99e8-c137e14ff1d4", + "name": "Spawner (2505)", + "location": [1011, 509, 26], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d65b2115-acc5-4d5b-9f25-4fe8b7fc3d92", + "name": "Spawner (2505)", + "location": [493, 378, 45], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c10dca38-deb2-48a7-87f4-487e862ede2f", + "name": "Spawner (2505)", + "location": [785, 303, 41], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e8d2eac-e0f8-484a-8316-ecb5720962ef", + "name": "Spawner (2505)", + "location": [454, 374, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9552423-f109-40b1-a654-5e44bfd7e688", + "name": "Spawner (2505)", + "location": [793, 368, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0773f05d-07e5-426e-a2e1-85c61ed07de5", + "name": "Spawner (2505)", + "location": [472, 336, 38], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a15f08c-dfd3-4184-8b1e-e37833f41bc2", + "name": "Spawner (2505)", + "location": [856, 324, 37], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "53507cb5-dd64-4105-8f65-3535910989ea", + "name": "Spawner (2505)", + "location": [905, 331, 8], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1a61bd2-63af-4d3d-beaf-ffa0f49bd279", + "name": "Spawner (2505)", + "location": [527, 393, 53], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Gaman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "be2a0d5d-dd14-422c-89c2-4885993f8776", + "name": "Spawner (2505)", + "location": [566, 430, 35], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Sheep", "maxCount": 8, "probability": 100 }, + { "name": "Gaman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c58ae7c-5335-4da7-88f4-e0f4d9de498e", + "name": "Spawner (2505)", + "location": [497, 453, 17], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aac6e8bc-1ab7-4b96-a84a-0f4fcc720cd0", + "name": "Spawner (2505)", + "location": [861, 335, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6ef86062-ac38-4916-ae0c-8e9b7f02b400", + "name": "Spawner (2505)", + "location": [565, 447, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Sheep", "maxCount": 8, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bccad70b-6bc6-4f7d-b2c8-510036a4b9bc", + "name": "Spawner (2505)", + "location": [656, 411, 33], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Sheep", "maxCount": 8, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "581859e8-1196-47e2-bc24-1359c385fecb", + "name": "Spawner (2505)", + "location": [857, 376, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6b48855-1dec-4cfc-8630-055f969403df", + "name": "Spawner (2505)", + "location": [702, 419, 18], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Crane", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bed998a1-f2a1-46dc-a006-a4e4f49e7b51", + "name": "Spawner (2505)", + "location": [955, 391, 4], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fdfab629-3712-4720-bfb3-3d06b510f089", + "name": "Spawner (2505)", + "location": [935, 521, 16], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2b38eeb-aff3-40aa-a028-09b3b7ad2196", + "name": "Spawner (2505)", + "location": [1074, 529, 22], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8406e7c2-5200-4cc1-b87e-0455f0b66efc", + "name": "Spawner (2505)", + "location": [1062, 469, 8], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DireWolf", "maxCount": 4, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32ba9655-ba7d-4112-9e6a-3f36b34102bd", + "name": "Spawner (2505)", + "location": [1122, 501, 19], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93f967ad-747c-4a5b-b39c-4693af13a61e", + "name": "Spawner (2505)", + "location": [1027, 562, 29], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c6024e89-5dcf-4bbb-8a3c-7ecdc07fecd2", + "name": "Spawner (2505)", + "location": [949, 589, 7], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bcbabf9f-03b8-427a-895f-06d963e636f7", + "name": "Spawner (2505)", + "location": [1105, 539, 22], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad0e593d-294e-4bd2-aa48-91a130447bc2", + "name": "Spawner (2505)", + "location": [921, 593, 20], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbb6df60-7185-44d4-87d4-7ba60d7bd95b", + "name": "Spawner (2505)", + "location": [192, 770, 56], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "35aa8796-06bb-4243-840f-44d4ce68b110", + "name": "Spawner (2505)", + "location": [1181, 547, 23], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4ebe134-af3c-4210-b8c6-687f6d4d7859", + "name": "Spawner (2505)", + "location": [1184, 616, 19], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MountainGoat", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8f58b5f2-f80e-458b-9eaf-a8042b0c8c61", + "name": "Spawner (2505)", + "location": [1235, 506, 28], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7e0bbcc-6499-4720-8db9-edc7b5ab8b35", + "name": "Spawner (2505)", + "location": [1258, 531, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc5b8bce-6c63-4d77-97ac-7661248770fc", + "name": "Spawner (2505)", + "location": [1307, 559, 31], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a5ac06e-23f0-40c6-a1f4-9961465b6ddc", + "name": "Spawner (2505)", + "location": [1176, 701, 72], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "MountainGoat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2d1b64ad-e246-4c2f-b1b1-e2b25c6e53e2", + "name": "Spawner (2505)", + "location": [1144, 667, 70], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "MountainGoat", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a335efbd-4bde-4687-afaa-b44445efea4e", + "name": "Spawner (2505)", + "location": [1134, 705, 72], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1c39d957-43f3-4e45-9fca-0f82fcd80baf", + "name": "Spawner (2505)", + "location": [1074, 806, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "BrownBear", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "811da3e8-b320-416d-936e-505e490d503e", + "name": "Spawner (2505)", + "location": [955, 828, 27], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba085b7c-aa9a-4138-b480-6dd562a9bb52", + "name": "Spawner (2505)", + "location": [1028, 818, 13], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "651e3dfe-31b6-4a95-9110-5861aac65171", + "name": "Spawner (2505)", + "location": [1101, 832, 37], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "BrownBear", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8dfd0e1e-dc0e-45a9-bea3-622dde6659d4", + "name": "Spawner (2505)", + "location": [967, 755, 3], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55ccd405-6e56-4289-a671-c1521f8b1b4e", + "name": "Spawner (2505)", + "location": [956, 705, 16], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb4ff5b1-2124-4bce-8b53-3b48dcf0af29", + "name": "Spawner (2505)", + "location": [911, 643, 15], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d612093e-9a8c-4400-ac9f-7e7db6183ed9", + "name": "Spawner (2505)", + "location": [868, 660, 7], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84e8cdc1-226f-4a5c-9732-628e95c380a8", + "name": "Spawner (2505)", + "location": [889, 516, 47], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "MountainGoat", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc8e7cfd-1a8c-4f6a-ad02-a571e65e8222", + "name": "Spawner (2505)", + "location": [862, 490, 54], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8edf5c3c-7085-4c94-8e30-7a5d41e355fd", + "name": "Spawner (2505)", + "location": [998, 656, 11], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9c32da2-2d36-45a6-9808-94bb89477d71", + "name": "Spawner (2505)", + "location": [1065, 708, 17], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "MountainGoat", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9fefec2c-5373-4ac5-8c36-400eff03d46d", + "name": "Spawner (2505)", + "location": [1227, 1082, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e0c0300-cb5b-4a45-a97a-12b40100f047", + "name": "Spawner (2505)", + "location": [1039, 952, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aadcbe2d-6fca-4527-a5ac-6ce004224916", + "name": "Spawner (2505)", + "location": [1052, 1030, 24], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "11f32a2d-41af-40ea-b076-14452d79a9c9", + "name": "Spawner (2505)", + "location": [1092, 1024, 34], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8be2d00c-c9fb-4563-bf7e-619326a17121", + "name": "Spawner (2505)", + "location": [1133, 957, 64], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c6b8543-bd53-4cf8-ac52-a20347b89cad", + "name": "Spawner (2505)", + "location": [1088, 980, 30], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10d55af6-cce8-498b-9c29-4e2713f79d9b", + "name": "Spawner (2505)", + "location": [1091, 897, 69], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98e287de-8406-4373-8955-829652114180", + "name": "Spawner (2505)", + "location": [1056, 904, 23], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7bbee32e-82df-4a88-b864-dda360fed83e", + "name": "Spawner (2505)", + "location": [143, 131, 0], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Gaman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6b0b9c5e-f925-4f24-97f9-d9c9915d8d8f", + "name": "Spawner (2505)", + "location": [687, 439, 26], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Sheep", "maxCount": 8, "probability": 100 }, + { "name": "MountainGoat", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e7d1a09-96fd-4d52-a8c7-26be104f1050", + "name": "Spawner (2505)", + "location": [823, 279, 33], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aebda4c6-8377-4938-a945-4da8600bb792", + "name": "Spawner (2505)", + "location": [1265, 927, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f71ccad-9194-41b5-8af2-819e8d5ff14e", + "name": "Spawner (2505)", + "location": [419, 529, 15], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 8, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b8f1c02-d05a-4bc9-a013-57e201cca765", + "name": "Spawner (2505)", + "location": [960, 293, 29], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c67a873-1b6e-422d-bce9-5cc93722eecc", + "name": "Spawner (2505)", + "location": [243, 1031, 13], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "BullFrog", "maxCount": 12, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "109decf7-c5fa-4839-ab48-51d6e1a0a984", + "name": "Spawner (2505)", + "location": [988, 335, 17], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d84bf13-4790-4286-a8dd-ad78174f533a", + "name": "Spawner (2505)", + "location": [771, 228, 63], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 40, + "entries": [ + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dc8cf0c-106b-49a9-92aa-82758885387d", + "name": "Spawner (2505)", + "location": [443, 402, 32], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "BlackBear", "maxCount": 8, "probability": 100 }, + { "name": "BrownBear", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e42281b9-4fde-4a3e-8d04-a01f8459d320", + "name": "Spawner (2505)", + "location": [1255, 804, 5], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f72385df-b6f1-4ecf-a2cd-6b712deb7068", + "name": "Spawner (2505)", + "location": [288, 652, 15], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [{ "name": "Gaman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d88e96d0-39ff-46fe-86be-9b06a791891f", + "name": "Spawner (2505)", + "location": [459, 483, 21], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Gaman", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f5b24c6b-5308-461b-a977-f9bc646b451d", + "name": "Spawner (2505)", + "location": [185, 1046, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "259e2b9a-187b-4009-8e43-277c10cff6b5", + "name": "Spawner (2505)", + "location": [210, 1085, 10], + "map": "Tokuno", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Crane", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "BullFrog", "maxCount": 12, "probability": 100 }, + { "name": "GiantToad", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1882a00b-e0b5-44b2-9a53-ad4faabbd6d8", + "name": "Spawner (2505)", + "location": [596, 1289, 21], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 }, + { "name": "Pig", "maxCount": 8, "probability": 100 }, + { "name": "Goat", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a91c9b1-2c2c-4e5d-b3ab-98b110013172", + "name": "Spawner (2505)", + "location": [566, 1287, 22], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Crane", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7faed2d6-3375-4d3d-a635-921e06255853", + "name": "Spawner (2505)", + "location": [525, 1263, 24], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c2e0be2-cc73-4518-9c76-3e960cf286b6", + "name": "Spawner (2505)", + "location": [547, 1240, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Dog", "maxCount": 8, "probability": 100 }, + { "name": "Cat", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3692b482-e864-4cc3-92f2-02bbbf69c448", + "name": "Spawner (2505)", + "location": [534, 1189, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1155a3a8-591d-47ed-9672-30b5d0d10e5f", + "name": "Spawner (2505)", + "location": [572, 1211, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Goat", "maxCount": 8, "probability": 100 }, + { "name": "Sheep", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15fd3cea-416b-449a-925f-c571ada88282", + "name": "Spawner (2505)", + "location": [600, 1198, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Goat", "maxCount": 8, "probability": 100 }, + { "name": "Horse", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07015515-1c9d-4bfd-9199-5a8eeccce91a", + "name": "Spawner (2505)", + "location": [580, 1170, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Llama", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 }, + { "name": "Dog", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a3eb5fcc-51b8-4116-8762-6fd6758595b4", + "name": "Spawner (2505)", + "location": [567, 1160, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Rabbit", "maxCount": 8, "probability": 100 }, + { "name": "Llama", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65473288-20d0-40f6-9da4-ab3c1213c9e1", + "name": "Spawner (2505)", + "location": [563, 1133, 43], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "512c0d13-c23a-4f39-9dd1-584f708660fb", + "name": "Spawner (2505)", + "location": [561, 1101, 33], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "GreyWolf", "maxCount": 8, "probability": 100 }, + { "name": "Pig", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "83a06690-beab-4182-a040-4466d61e8bd3", + "name": "Spawner (2505)", + "location": [550, 1092, 25], + "map": "Tokuno", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "RidableLlama", "maxCount": 8, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd5426bf-46f4-494f-9818-bd66d8b8f4f9", + "name": "Spawner (2505)", + "location": [524, 1054, 18], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12723266-85ed-413a-bab7-c825593b42bb", + "name": "Spawner (2505)", + "location": [504, 1042, 24], + "map": "Tokuno", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "855c2b06-17c2-452b-bbd6-bd081a79a64b", + "name": "Spawner (2505)", + "location": [533, 991, 53], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d3dd1fa-c7a9-4de7-b7c2-9ab56d5118b8", + "name": "Spawner (2505)", + "location": [526, 889, 11], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "025966e3-5dff-48d2-8101-47bb942642d1", + "name": "Spawner (2505)", + "location": [612, 857, 15], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a23effc8-f42c-4893-a20a-0813bf1bd503", + "name": "Spawner (2505)", + "location": [558, 862, 6], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a38a3b3-ecad-4c71-b4af-6a862e5f1e38", + "name": "Spawner (2505)", + "location": [724, 831, 34], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b1a526f-836a-4c63-a353-066cf22828bf", + "name": "Spawner (2505)", + "location": [771, 824, 16], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "783c5a65-45be-49b2-978e-839ee3d8ad42", + "name": "Spawner (2505)", + "location": [746, 814, 16], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4c7e0f56-72ac-454c-9392-b00a2ca81ff0", + "name": "Spawner (2505)", + "location": [683, 857, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "JackRabbit", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47507391-0312-43b8-a52f-1da5ef65d134", + "name": "Spawner (2505)", + "location": [685, 891, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "974b9a03-2f13-4f89-9c95-02da87ffde75", + "name": "Spawner (2505)", + "location": [635, 966, 44], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8ae605c-db83-4c3c-9c6b-3ca1ecac20fb", + "name": "Spawner (2505)", + "location": [591, 1010, 58], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f21407d4-22ef-4284-94cb-ec01f21513c8", + "name": "Spawner (2505)", + "location": [680, 987, 47], + "map": "Tokuno", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "377c039b-1370-49ed-ac1f-b5453a0f98ae", + "name": "Spawner (2505)", + "location": [722, 916, 40], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 6, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c207158-3448-4593-bd8a-390f278dc088", + "name": "Spawner (2505)", + "location": [727, 1071, 39], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Horse", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd53dbe3-1a65-4629-8fbd-fe54f458741b", + "name": "Spawner (2505)", + "location": [710, 1115, 41], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "GreyWolf", "maxCount": 6, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5fddebe2-5f66-4157-a95e-50f1d376ab67", + "name": "Spawner (2505)", + "location": [644, 1142, 28], + "map": "Tokuno", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "DireWolf", "maxCount": 6, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 6, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d8b101e-4530-4396-98db-3b3f0a0e6a23", + "name": "Spawner (2505)", + "location": [775, 1127, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "BlackBear", "maxCount": 4, "probability": 100 }, + { "name": "BrownBear", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bcfae829-1bba-48df-96e4-90bcf0664ad1", + "name": "Spawner (2505)", + "location": [792, 920, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "JackRabbit", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "edc05e21-90b2-4bf2-9539-929f0b217be7", + "name": "Spawner (2505)", + "location": [794, 890, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "504e34ec-555a-4638-aa23-440d042f355a", + "name": "Spawner (2505)", + "location": [833, 908, 15], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e0d9671-9a22-41db-98ef-754e21134ec4", + "name": "Spawner (2505)", + "location": [770, 875, 30], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "073bd67c-3055-4bf5-8879-4bca7908de15", + "name": "Spawner (2505)", + "location": [874, 971, 45], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a81ef6e2-3c78-41f6-a98b-c6e169a3e67c", + "name": "Spawner (2505)", + "location": [911, 1025, 16], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Crane", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62196314-f7e7-42e8-a131-db562b52bee0", + "name": "Spawner (2505)", + "location": [879, 1038, 16], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Goat", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "208e7ae6-ff8a-4482-aad8-88fac46e3821", + "name": "Spawner (2505)", + "location": [887, 1072, 20], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a5adf0ce-96eb-410d-9052-57b63b4fa2e6", + "name": "Spawner (2505)", + "location": [913, 1055, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2112cd3e-2459-4ced-b190-dc6ce7d36d3a", + "name": "Spawner (2505)", + "location": [916, 1120, 17], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Pig", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c20bc6c-30d5-4b2d-97e3-0db6146a7226", + "name": "Spawner (2505)", + "location": [883, 1121, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fc35baac-54c5-442a-9808-686341d564e4", + "name": "Spawner (2505)", + "location": [837, 1075, 16], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "df75557f-0588-4342-8138-89d5a4c76e36", + "name": "Spawner (2505)", + "location": [884, 1152, 29], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Sheep", "maxCount": 4, "probability": 100 }, + { "name": "Pig", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "221b2448-0059-4522-8cdc-c1f0ea38145a", + "name": "Spawner (2505)", + "location": [907, 1143, 11], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7f3e95c9-608e-4e32-95fa-5830bd9f5372", + "name": "Spawner (2505)", + "location": [942, 1170, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Crane", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Sheep", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd3a1f7e-e3da-4c36-8195-3aa7b42d299a", + "name": "Spawner (2505)", + "location": [939, 1199, 22], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b655fa8-4c47-4101-8a05-e138b0eb8e14", + "name": "Spawner (2505)", + "location": [934, 1233, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b96c9765-f852-40d2-b621-ecf427dffa93", + "name": "Spawner (2505)", + "location": [887, 1286, 13], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Goat", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49dc8190-fb53-4f7a-85c1-4bb0645f7da8", + "name": "Spawner (2505)", + "location": [1139, 999, 48], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "365b6b36-18ef-4ad8-8a0a-896cfdd1ba1b", + "name": "Spawner (2505)", + "location": [1133, 1036, 31], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Crane", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d149ddd-a7b8-4045-9b98-144298a83710", + "name": "Spawner (2505)", + "location": [1207, 1037, 25], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gaman", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Crane", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d848d73f-350b-4b9a-8e5f-896386d076bb", + "name": "Spawner (2505)", + "location": [1178, 1076, 19], + "map": "Tokuno", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Crane", "maxCount": 4, "probability": 100 }] } ] diff --git a/Distribution/Data/Spawns/uoml/tokuno/YomutsoMines.json b/Distribution/Data/Spawns/uoml/tokuno/YomutsoMines.json new file mode 100644 index 000000000..0d00164c1 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/tokuno/YomutsoMines.json @@ -0,0 +1,168 @@ +[ + { + "type": "Spawner", + "guid": "9cbd85a7-68f8-401d-a8c3-b2a220b2e431", + "name": "Spawner (2506)", + "location": [16, 56, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ee2e7f73-a95f-40e6-8493-80dbe4c2ea65", + "name": "Spawner (2506)", + "location": [25, 19, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "115246e3-d444-4ad4-ad93-ba5f98ade944", + "name": "Spawner (2506)", + "location": [71, 29, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "922f0ff6-6efc-4787-aab1-32b8c41831ab", + "name": "Spawner (2506)", + "location": [54, 67, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba3f54b0-b977-476e-be97-2ad5ba39dc19", + "name": "Spawner (2506)", + "location": [83, 114, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a0525c92-565a-4b15-b6b0-61c6be263ae7", + "name": "Spawner (2506)", + "location": [102, 84, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc1fe934-3641-4ddd-8b6c-207ccf0861ef", + "name": "Spawner (2506)", + "location": [116, 61, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6561df12-d971-4504-9a84-fba65d6e3339", + "name": "Spawner (2506)", + "location": [111, 20, -1], + "map": "Malas", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "DullCopperElemental", "maxCount": 1, "probability": 100 }, + { "name": "YomotsuWarrior", "maxCount": 2, "probability": 100 }, + { "name": "YomotsuPriest", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "694ea01f-680b-4054-9f13-bf9e7317a6d8", + "name": "Spawner (2506)", + "location": [107, 23, -1], + "map": "Malas", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "FireBeetle", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/BlightedGrove.json b/Distribution/Data/Spawns/uoml/trammel/BlightedGrove.json new file mode 100644 index 000000000..abedff09c --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/BlightedGrove.json @@ -0,0 +1,145 @@ +[ + { + "type": "Spawner", + "guid": "b04cfa39-6091-49da-bdba-1b737c266a0a", + "name": "Spawner (2101)", + "location": [6507, 843, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Tangle", "maxCount": 8, "probability": 100 }, + { "name": "Bogling", "maxCount": 8, "probability": 100 }, + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 }, + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "Changeling", "maxCount": 8, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "713c3e56-33d2-430c-b182-364da7607c83", + "name": "Spawner (2101)", + "location": [6513, 871, 0], + "map": "Trammel", + "count": 17, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [ + { "name": "GiantSerpent", "maxCount": 17, "probability": 100 }, + { "name": "GiantToad", "maxCount": 17, "probability": 100 }, + { "name": "Harpy", "maxCount": 17, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 17, "probability": 100 }, + { "name": "Snake", "maxCount": 17, "probability": 100 }, + { "name": "Bogling", "maxCount": 17, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c6215ac8-c0e3-4c83-9394-984deb29c773", + "name": "Spawner (2101)", + "location": [6585, 878, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Harpy", "maxCount": 7, "probability": 100 }, + { "name": "Saliva", "maxCount": 7, "probability": 100 }, + { "name": "Changeling", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "025ff0bc-ff5b-4175-b963-1e393ee70543", + "name": "Spawner (2101)", + "location": [6535, 848, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Abscess", "maxCount": 4, "probability": 100 }, + { "name": "Hydra", "maxCount": 4, "probability": 100 }, + { "name": "BogThing", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e840761f-1c5b-45f2-ba91-2a273e9739c2", + "name": "Spawner (2101)", + "location": [6540, 872, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "43cff389-a041-49a6-89fb-cd5111466e70", + "name": "Spawner (2101)", + "location": [6518, 870, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Thrasher", "maxCount": 9, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Bogling", "maxCount": 9, "probability": 100 }, + { "name": "Alligator", "maxCount": 9, "probability": 100 }, + { "name": "Coil", "maxCount": 9, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dbad35b-a4c0-4d6d-b1d7-ad4cb792da01", + "name": "Spawner (2101)", + "location": [6489, 897, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04ebab40-c487-455f-8bea-211ae849bf2a", + "name": "Spawner (2101)", + "location": [6518, 879, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "InsaneDryad", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/BritainSewer.json b/Distribution/Data/Spawns/uoml/trammel/BritainSewer.json new file mode 100644 index 000000000..fe2d5238f --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/BritainSewer.json @@ -0,0 +1,356 @@ +[ + { + "type": "Spawner", + "guid": "308b2558-7402-46d1-a300-11eb22feaf0b", + "name": "Spawner (2102)", + "location": [6049, 1447, 4], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4502169a-d510-4779-a2d4-7ecff61c32a1", + "name": "Spawner (2102)", + "location": [6091, 1491, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "70842ac5-c2f9-490b-8fca-603396002f94", + "name": "Spawner (2102)", + "location": [6048, 1487, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73483117-862e-472d-b856-13fbe0f2cb04", + "name": "Spawner (2102)", + "location": [6065, 1459, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2059e438-3ea0-4553-9d80-2c4b3e179956", + "name": "Spawner (2102)", + "location": [6092, 1448, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb41f5b8-add1-42be-8bbe-48b76f9a4dd9", + "name": "Spawner (2102)", + "location": [6107, 1452, 25], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e47e277-8d43-46c0-b43a-a15ce3623ceb", + "name": "Spawner (2102)", + "location": [6112, 1462, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5873288f-b3d0-4ffc-b937-1a938b0ab0a3", + "name": "Spawner (2102)", + "location": [6039, 1438, 4], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31142c48-f400-4b04-a235-4df3d184432a", + "name": "Spawner (2102)", + "location": [6115, 1491, -15], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ed8f2188-17cd-4235-86d8-54d62209ef93", + "name": "Spawner (2102)", + "location": [6084, 1470, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 1, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "16798caa-188d-4b2d-8494-c3b56cbaa4c7", + "name": "Spawner (2102)", + "location": [6049, 1470, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bda24adb-c409-4c12-a0c0-8aacd36ab3d9", + "name": "Spawner (2102)", + "location": [6034, 1467, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd1b022d-98d5-4f06-aac1-7751d2416304", + "name": "Spawner (2102)", + "location": [6059, 1438, 4], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "82cbfa7a-4ea4-476b-ac8c-a3963746fffd", + "name": "Spawner (2102)", + "location": [6061, 1490, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5f0bff7-c610-4923-8c17-b32a00911628", + "name": "Spawner (2102)", + "location": [6041, 1486, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 2, + "walkingRange": 15, + "entries": [ + { "name": "SewerRat", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "80883952-a77b-466d-b01e-f810fca42c11", + "name": "Spawner (2102)", + "location": [6090, 1483, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "Alligator", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "639f6230-75e9-4633-8e2b-cfd6f35f4fdc", + "name": "Spawner (2102)", + "location": [6077, 1492, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "424591d5-810a-4248-b002-51d2d993b09e", + "name": "Spawner (2102)", + "location": [6108, 1471, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "551ba232-804a-48e2-a289-048ac2c5952c", + "name": "Spawner (2102)", + "location": [6087, 1443, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1fb937a2-b8d8-4796-8f7e-62dc86db4541", + "name": "Spawner (2102)", + "location": [6052, 1463, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "Alligator", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e40a3f76-4701-425c-a806-06cb36504593", + "name": "Spawner (2102)", + "location": [6037, 1494, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:00:00", + "maxDelay": "00:01:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "Alligator", "maxCount": 2, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Covetous.json b/Distribution/Data/Spawns/uoml/trammel/Covetous.json new file mode 100644 index 000000000..0b8119fb8 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Covetous.json @@ -0,0 +1,767 @@ +[ + { + "type": "Spawner", + "guid": "234f3c85-84fb-4f3d-bb0e-c90cc95c16b0", + "name": "Spawner (2103)", + "location": [5472, 1877, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Harpy", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "41f90fd5-f4fe-47f3-b05f-4e00f83dd415", + "name": "Spawner (2103)", + "location": [5453, 1905, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "HeadlessOne", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5cb18cd8-da7a-414d-9f4d-f9b20709b796", + "name": "Spawner (2103)", + "location": [5443, 1918, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 30, + "entries": [{ "name": "StoneHarpy", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bc45c254-4539-4c60-a37f-2711c2e2ddec", + "name": "Spawner (2103)", + "location": [5418, 1876, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Gazer", "maxCount": 4, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be80b563-6951-4564-869e-e64efe2b116a", + "name": "Spawner (2103)", + "location": [5411, 1901, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Gazer", "maxCount": 2, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af277400-a7ad-4048-bdb1-fdcc10b20756", + "name": "Spawner (2103)", + "location": [5393, 1921, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84ba2d57-7801-4461-8ed9-c7d45c95f473", + "name": "Spawner (2103)", + "location": [5400, 1937, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3828dfe6-a240-4615-bf09-4fd7eccd4b90", + "name": "Spawner (2103)", + "location": [5452, 1891, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "156f3558-d5ea-4117-bcdb-f6f2f2ff25bd", + "name": "Spawner (2103)", + "location": [5399, 1859, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4755ff86-849d-443e-aaf9-4da5a523f28c", + "name": "Spawner (2103)", + "location": [5387, 1911, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "16f79bca-0bcf-4440-a5df-ac2a73ece818", + "name": "Spawner (2103)", + "location": [5411, 1932, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec453c02-9ef8-40bb-8e12-06c74c0ce96e", + "name": "Spawner (2103)", + "location": [5577, 2006, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b180d203-9331-4077-86a1-5cec055f0fc8", + "name": "Spawner (2103)", + "location": [5593, 2017, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "df30762f-98e0-4753-97ef-d2e548612633", + "name": "Spawner (2103)", + "location": [5558, 2025, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HeadlessOne", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d56c22bc-e344-47d6-ae5f-d65acdea6940", + "name": "Spawner (2103)", + "location": [5498, 1998, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "368ab9e9-2c6c-4cc2-9418-8d978ddaea67", + "name": "Spawner (2103)", + "location": [5474, 2035, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cdffe875-e196-4511-b07b-bafe2547279b", + "name": "Spawner (2103)", + "location": [5469, 2019, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Slime", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e2d21b4-67af-4720-b7ff-d8aa07544362", + "name": "Spawner (2103)", + "location": [5445, 2030, -3], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "WaterElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b005fb7e-e674-496d-89f1-ee8d3b8961e0", + "name": "Spawner (2103)", + "location": [5440, 2023, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22bd9b72-f463-431e-bd87-17e25e1c077f", + "name": "Spawner (2103)", + "location": [5473, 1991, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 9, + "walkingRange": 25, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "DreadSpider", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6e4df3f9-6915-4733-9efb-f999f6725e3e", + "name": "Spawner (2103)", + "location": [5457, 1971, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Gazer", "maxCount": 4, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 }, + { "name": "ElderGazer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca031256-07f4-4a12-8303-54c2c2606afc", + "name": "Spawner (2103)", + "location": [5436, 1972, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "824e3fe5-d203-4b2c-82b1-4c6af496a131", + "name": "Spawner (2103)", + "location": [5417, 1975, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d782c02-bb11-4a7b-a864-c6aa0d9372b6", + "name": "Spawner (2103)", + "location": [5429, 1989, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Gazer", "maxCount": 2, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fce765ed-6ec5-45ae-94e6-762879f661a9", + "name": "Spawner (2103)", + "location": [5425, 1992, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Corpser", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc2ab61a-331b-4b29-8639-552cb33a9a7b", + "name": "Spawner (2103)", + "location": [5388, 2012, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Gazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "58e61f09-1d8e-4eb0-bdee-18678d4d7ca4", + "name": "Spawner (2103)", + "location": [5503, 2002, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49d2dedd-e659-40b1-8faa-92ec79d5ebc6", + "name": "Spawner (2103)", + "location": [5413, 2000, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1fc3857a-ad6d-467c-9feb-df23f3192492", + "name": "Spawner (2103)", + "location": [5460, 1975, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e3bb584-e75f-47d2-b3ca-9926aef55326", + "name": "Spawner (2103)", + "location": [5448, 2027, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "807182c4-b6fd-4053-a8a3-14b83dff0d1e", + "name": "Spawner (2103)", + "location": [2456, 861, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "de81051f-f31d-4254-b09b-3a629e29f763", + "name": "Spawner (2103)", + "location": [2480, 863, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "437c19eb-a47b-4477-88f2-90e3f8171a94", + "name": "Spawner (2103)", + "location": [2493, 883, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "03fd7675-5b18-4bd1-b886-549fff24a24e", + "name": "Spawner (2103)", + "location": [2513, 862, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8a7ef603-1a81-4834-afba-20cbe04710ba", + "name": "Spawner (2103)", + "location": [5560, 1903, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "792c60a4-bf05-4088-a0f1-295a8cbcc26d", + "name": "Spawner (2103)", + "location": [5596, 1903, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "43148509-fa06-41d1-9ae9-f6cbf2b9cd63", + "name": "Spawner (2103)", + "location": [5560, 1876, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1bc2e58-de79-4851-9b49-119ff6a2609a", + "name": "Spawner (2103)", + "location": [5603, 1875, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94f3b7ec-a856-4779-8ad6-0f90895790a0", + "name": "Spawner (2103)", + "location": [5617, 1861, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7a96822d-8531-469e-b32b-42ca4c914e5c", + "name": "Spawner (2103)", + "location": [5615, 1838, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60678191-e2a8-48ee-9dc7-56ff237b3ddb", + "name": "Spawner (2103)", + "location": [5579, 1859, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 11, + "walkingRange": 25, + "entries": [ + { "name": "Lich", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1b287d2c-4992-4071-a577-23a460b7af73", + "name": "Spawner (2103)", + "location": [5560, 1843, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 40, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15e19e7f-7de5-412c-873e-e38fd7f4c99d", + "name": "Spawner (2103)", + "location": [5528, 1808, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a827c58c-f729-41d8-a600-7e85d4fa2fb5", + "name": "Spawner (2103)", + "location": [5504, 1808, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 12, + "entries": [ + { "name": "RottingCorpse", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6e763f3e-fd66-40af-9c05-e3bd8604b646", + "name": "Spawner (2103)", + "location": [5452, 1799, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d0223bc-6ca5-4794-ae48-16a6969f44e3", + "name": "Spawner (2103)", + "location": [5461, 1818, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Deceit.json b/Distribution/Data/Spawns/uoml/trammel/Deceit.json new file mode 100644 index 000000000..6d0ffcd0c --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Deceit.json @@ -0,0 +1,626 @@ +[ + { + "type": "Spawner", + "guid": "4563aa47-639e-47f3-a301-c430a6e1777a", + "name": "Spawner (2104)", + "location": [5187, 603, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 35, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94066b8f-d45a-4273-b649-ab593fbe55e8", + "name": "Spawner (2104)", + "location": [5186, 575, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 12, + "entries": [{ "name": "WaterElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f4c26861-33c2-4f4b-af35-62f556e385cb", + "name": "Spawner (2104)", + "location": [5187, 545, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 25, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "28f3eccc-1c12-4576-ae46-e9cfb11fb19f", + "name": "Spawner (2104)", + "location": [5163, 567, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 25, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b83d71f9-6539-4a90-a43a-4feb8ee48678", + "name": "Spawner (2104)", + "location": [5145, 553, -40], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 35, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "462bded2-9162-4b79-a1a3-7d35a889f899", + "name": "Spawner (2104)", + "location": [5147, 579, -50], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5da72c5b-46fd-4a13-962f-3accc945f462", + "name": "Spawner (2104)", + "location": [5147, 598, -50], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5c3582a8-fa9c-4839-aff6-82457f87f023", + "name": "Spawner (2104)", + "location": [5145, 622, -50], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5018fced-3b89-4654-88ac-143ceed1e566", + "name": "Spawner (2104)", + "location": [5212, 531, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d729f42a-87ef-4a42-8a3b-b65ae177ab40", + "name": "Spawner (2104)", + "location": [5219, 552, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2dac00d-2532-4fc4-8ae2-bde51687b3e1", + "name": "Spawner (2104)", + "location": [5219, 573, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b1e99f8-bde4-41f5-a76d-78b9dca9844c", + "name": "Spawner (2104)", + "location": [5145, 618, -50], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d846b9eb-23d3-4b86-8a61-20331e2cf8db", + "name": "Spawner (2104)", + "location": [5307, 546, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fb75ec8-f949-4480-b573-064a299b34c0", + "name": "Spawner (2104)", + "location": [5327, 542, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dfec3c5-2cc6-4908-b975-51182b394218", + "name": "Spawner (2104)", + "location": [5286, 542, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6ac0a6d7-ac00-48c9-bb46-827c103b44ef", + "name": "Spawner (2104)", + "location": [5283, 583, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b094c982-2cfd-42ff-ab54-b175a134de30", + "name": "Spawner (2104)", + "location": [5318, 578, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "564f98cb-f8fe-43dd-a180-9da4652a4f1e", + "name": "Spawner (2104)", + "location": [5342, 582, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 4, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a63e78b-f33d-4502-9760-81c36f272658", + "name": "Spawner (2104)", + "location": [5299, 603, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a44b5c06-0cda-4edc-8468-fa4a00a52b1e", + "name": "Spawner (2104)", + "location": [5295, 623, -5], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [{ "name": "BoneMagi", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9f4cf647-abba-4181-aa0a-b477d21800c4", + "name": "Spawner (2104)", + "location": [5322, 626, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "591e02f3-9855-42a6-ae25-d11d0fbe788b", + "name": "Spawner (2104)", + "location": [5323, 571, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2d34809-99ba-41b9-9f74-ed1a65248ca0", + "name": "Spawner (2104)", + "location": [5139, 661, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4afcb2a6-cecf-4d42-a449-de7e061ccd6c", + "name": "Spawner (2104)", + "location": [5190, 655, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7f6975e8-9689-441b-b484-e0c803cf29db", + "name": "Spawner (2104)", + "location": [5170, 678, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7038def8-3dd9-481a-aff9-b344e58e0122", + "name": "Spawner (2104)", + "location": [5151, 718, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f6f496d1-7f72-4e0a-be1f-e8249bc31633", + "name": "Spawner (2104)", + "location": [5192, 695, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3f74896-465e-4cc7-bb83-63d508157390", + "name": "Spawner (2104)", + "location": [5183, 727, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0cf1788-6671-41ad-aa47-4e8287fbac5e", + "name": "Spawner (2104)", + "location": [5212, 674, -20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd67b487-cfbb-436c-9623-b05e87602698", + "name": "Spawner (2104)", + "location": [5219, 734, -20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "20f986bd-7976-415c-bf34-2df8bde4025e", + "name": "Spawner (2104)", + "location": [5150, 742, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 15, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf4c7226-e5d1-4d90-b70c-cc4d3ad472cb", + "name": "Spawner (2104)", + "location": [5307, 675, -20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Ghoul", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7a2d79d7-d842-4977-8449-5d7735e73c6f", + "name": "Spawner (2104)", + "location": [5280, 675, 8], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8f579b88-cb8e-4c17-b795-fd4d1d3d69d2", + "name": "Spawner (2104)", + "location": [5320, 708, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Lich", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a3e5c4ff-6bd0-4380-aebd-47ee3c1c1421", + "name": "Spawner (2104)", + "location": [5314, 748, -20], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 25, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6006bae6-ddb4-4c35-9c38-c1378f71e584", + "name": "Spawner (2104)", + "location": [5267, 690, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 35, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Despise.json b/Distribution/Data/Spawns/uoml/trammel/Despise.json new file mode 100644 index 000000000..443018183 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Despise.json @@ -0,0 +1,636 @@ +[ + { + "type": "Spawner", + "guid": "6c3fc410-6d7c-4e7e-80fb-62d05f146e28", + "name": "Spawner (2105)", + "location": [5503, 529, 60], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "312b70e0-4afc-411d-8f1a-d902ea0f9e16", + "name": "Spawner (2105)", + "location": [5504, 597, 45], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4f3cb4cd-569f-4ad1-9343-e7fe4eb4775a", + "name": "Spawner (2105)", + "location": [5464, 600, 45], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ff5edaf2-353f-4e22-8d77-75ba2cb489be", + "name": "Spawner (2105)", + "location": [5435, 589, 47], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "39d6eab1-27c8-4f51-b3d7-4befdbd17478", + "name": "Spawner (2105)", + "location": [5410, 611, 45], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6eb7ccc4-5d54-4e56-b637-4726b2606957", + "name": "Spawner (2105)", + "location": [5386, 612, 45], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20ae1a8a-90cd-4046-819f-f7eae50c6b24", + "name": "Spawner (2105)", + "location": [5405, 576, 61], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "77392dd3-f72b-43fa-b5b4-9ce81858fe4f", + "name": "Spawner (2105)", + "location": [5411, 541, 60], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "41a2d01b-14a5-4b87-a4fc-85053f7ae31a", + "name": "Spawner (2105)", + "location": [5460, 526, 60], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Lizardman", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "60ec080d-a3a9-44a8-b00c-e1d1bb81818f", + "name": "Spawner (2105)", + "location": [5394, 525, 65], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "90882c2e-a2b4-4c37-905e-b87f5ed61a60", + "name": "Spawner (2105)", + "location": [5390, 587, 45], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36aa83bc-3e2a-42f8-bdeb-0ee9cb004a64", + "name": "Spawner (2105)", + "location": [5490, 676, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0de2bfd4-ccb8-4e8c-88b7-e234eb2f5122", + "name": "Spawner (2105)", + "location": [5483, 711, 15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9fcd5ea5-3722-49ef-8d15-8c6f86f1d10c", + "name": "Spawner (2105)", + "location": [5505, 747, 5], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 25, + "entries": [ + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1eb2840b-690f-4f10-80d0-00a7e8d22a07", + "name": "Spawner (2105)", + "location": [5475, 748, 5], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "EarthElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c1a2c73-e451-4e78-8fff-e320bb67ea7d", + "name": "Spawner (2105)", + "location": [5420, 746, 5], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 25, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ettin", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ac09482f-cf78-4b67-849e-81d969044806", + "name": "Spawner (2105)", + "location": [5385, 703, 15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d710fdd3-1600-4436-a76b-7062ab33aa72", + "name": "Spawner (2105)", + "location": [5391, 660, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "20f0fb49-eaea-4b6b-a14d-0868d204ceaf", + "name": "Spawner (2105)", + "location": [5441, 680, 20], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "EarthElemental", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ed82ee8-dd07-43ef-a454-a1dd5e964fa7", + "name": "Spawner (2105)", + "location": [5507, 656, 20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d35770ae-714d-4f38-b2ea-ce1e4320a82e", + "name": "Spawner (2105)", + "location": [5387, 697, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "462a3304-fa2a-4473-99f4-438e9f55886e", + "name": "Spawner (2105)", + "location": [5439, 851, 45], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9a3db886-d50c-4e9c-b241-c3a0c5ee9e98", + "name": "Spawner (2105)", + "location": [5470, 860, 45], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0c46651c-0760-476e-8ebe-d51ca00006bf", + "name": "Spawner (2105)", + "location": [5426, 791, 60], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3e8b1378-fe18-4c3b-9e86-5b35a30a1c6f", + "name": "Spawner (2105)", + "location": [5482, 791, 60], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "Troll", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "68d435c5-668c-4af0-a9fd-5e9c3e2002d0", + "name": "Spawner (2105)", + "location": [5532, 787, 60], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e6a67238-55d8-4735-8ac3-7efcc25d3193", + "name": "Spawner (2105)", + "location": [5604, 789, 60], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "de0083f1-6b11-411b-8875-f94bbd11846b", + "name": "Spawner (2105)", + "location": [5558, 824, 45], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "OgreLord", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2fbd19bb-2caa-494d-ac09-70784915cc6e", + "name": "Spawner (2105)", + "location": [5590, 871, 45], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e6b0982f-8c36-4bc7-be61-f5fb371fd66f", + "name": "Spawner (2105)", + "location": [5528, 902, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ogre", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "306d0efd-88fc-4880-a46d-d2f9e5b04ec7", + "name": "Spawner (2105)", + "location": [5487, 902, 30], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cd736cc2-c0d3-4259-bbe2-949af88f94e7", + "name": "Spawner (2105)", + "location": [5504, 939, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cab315f4-6eaf-4772-a991-c2f4ee6c5284", + "name": "Spawner (2105)", + "location": [5471, 973, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 25, + "entries": [{ "name": "Cyclops", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b7c5f3ca-e60d-4623-b31c-86583932aa77", + "name": "Spawner (2105)", + "location": [5426, 943, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5e82bdcb-378c-4879-af67-03ecc96f3ff0", + "name": "Spawner (2105)", + "location": [5391, 905, 30], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "Ettin", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "25b5c0ec-29bf-4618-b857-9e68e23fa088", + "name": "Spawner (2105)", + "location": [5398, 996, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "AcidElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5329add2-7d3f-4884-8b4c-61dd7b78d29b", + "name": "Spawner (2105)", + "location": [5593, 794, 60], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8ecf5903-3e52-48af-8a3e-3deececd5314", + "name": "Spawner (2105)", + "location": [5546, 888, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9e70e19-7819-454d-8641-1fab82224c65", + "name": "Spawner (2105)", + "location": [5397, 995, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d88f3a9b-067f-48cc-ba7f-636f89d9445e", + "name": "Spawner (2105)", + "location": [5605, 799, 60], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cf63c3f7-751e-498b-bc4f-3e185280593b", + "name": "Spawner (2105)", + "location": [5557, 828, 45], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f9357ebd-0256-47bb-b849-2add953fab65", + "name": "Spawner (2105)", + "location": [5398, 995, 5], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "193bc157-aeb6-4e74-88e4-11e980858cec", + "name": "Spawner (2105)", + "location": [5564, 821, 45], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Destard.json b/Distribution/Data/Spawns/uoml/trammel/Destard.json new file mode 100644 index 000000000..8952697c5 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Destard.json @@ -0,0 +1,465 @@ +[ + { + "type": "Spawner", + "guid": "2d985bfc-95b3-40be-8a3e-f1919da38b19", + "name": "Spawner (2106)", + "location": [5209, 965, -40], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "34426acb-8319-4eb6-8760-d0acb86670f9", + "name": "Spawner (2106)", + "location": [5247, 956, -40], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fce8877c-f374-4e24-9d3f-65205427600e", + "name": "Spawner (2106)", + "location": [5280, 955, -40], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95541af6-6526-41ad-8329-a9819a3a1f8d", + "name": "Spawner (2106)", + "location": [5284, 926, -40], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da94b96f-7cbc-4016-91b5-6ec3df649e23", + "name": "Spawner (2106)", + "location": [5214, 917, -40], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a321325-9193-4861-a25e-c6f075636a07", + "name": "Spawner (2106)", + "location": [5241, 945, -40], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68815cec-f528-4033-be41-04c6d75415e1", + "name": "Spawner (2106)", + "location": [5161, 940, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [{ "name": "GiantSerpent", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b705ea99-1d95-42ff-8287-fb6588d0ed24", + "name": "Spawner (2106)", + "location": [5149, 907, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "Wyvern", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b05c0d4-7c45-4d29-9f15-adbb0b12686f", + "name": "Spawner (2106)", + "location": [5141, 915, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f5e5a38-ef2b-4e49-8574-b5349c21dd06", + "name": "Spawner (2106)", + "location": [5352, 934, -5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9f0b2731-3658-43bd-9fe4-3bca1a7c5212", + "name": "Spawner (2106)", + "location": [5317, 980, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a097ab1-3372-4f64-bb50-b18470eed97f", + "name": "Spawner (2106)", + "location": [5250, 869, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "GiantSerpent", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "89faa9ac-5c83-4145-a239-0f1bbc3088a6", + "name": "Spawner (2106)", + "location": [5226, 826, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01f5635b-645f-43a3-b8e1-de505a9805f4", + "name": "Spawner (2106)", + "location": [5269, 811, 7], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6da5d02-55ae-4e14-8aec-e02b2e7cf188", + "name": "Spawner (2106)", + "location": [5307, 815, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31627f88-cd2c-42f2-8aa2-df019970100f", + "name": "Spawner (2106)", + "location": [5294, 842, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cde84676-d0c3-44e7-8be3-9335dc9c5b1b", + "name": "Spawner (2106)", + "location": [5203, 787, 5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "WaterElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "45519d94-93ec-4b71-9fcd-0693de58f4e8", + "name": "Spawner (2106)", + "location": [5229, 841, 1], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "def6615c-5bbd-4b7c-ab81-4dc4327c97d8", + "name": "Spawner (2106)", + "location": [5205, 778, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "039faa13-d838-4ccf-8790-13500ac30d28", + "name": "Spawner (2106)", + "location": [5141, 834, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "ShadowWyrm", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73ef3d35-2c97-40c0-8c9d-f949bedf4bf0", + "name": "Spawner (2106)", + "location": [5169, 836, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "EvilMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22700190-506d-4f55-be3b-4ecf63f097a1", + "name": "Spawner (2106)", + "location": [5152, 869, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "ShadowWyrm", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4dea2c35-b11b-43cd-9916-6ee8d03105c7", + "name": "Spawner (2106)", + "location": [5153, 842, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "TreasureChestLevel4", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb1e3f19-4276-4b1f-b87f-0b4e38ebd70b", + "name": "Spawner (2106)", + "location": [5141, 968, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b2eb4c30-1f29-4ef5-b80a-851c68af76f9", + "name": "Spawner (2106)", + "location": [5157, 997, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [ + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "699dfc8f-e891-4978-9c73-a74cb1762849", + "name": "Spawner (2106)", + "location": [5185, 1006, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 50, + "entries": [{ "name": "AncientWyrm", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "58412982-34fe-41e4-9c85-cd0d5691d62c", + "name": "Spawner (2106)", + "location": [5143, 986, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db505150-0a76-44ac-8b27-841ef6b87321", + "name": "Spawner (2106)", + "location": [5183, 1007, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Fire.json b/Distribution/Data/Spawns/uoml/trammel/Fire.json new file mode 100644 index 000000000..1f566fad0 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Fire.json @@ -0,0 +1,758 @@ +[ + { + "type": "Spawner", + "guid": "0eb73fc0-e467-4792-ad60-4d3102e26915", + "name": "Spawner (2107)", + "location": [5693, 1415, 38], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Slime", "maxCount": 1, "probability": 100 }, + { "name": "GiantRat", "maxCount": 2, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df760fe6-6df1-45ac-a3c2-0f27637dd457", + "name": "Spawner (2107)", + "location": [5695, 1434, -1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Slime", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7e6d1ab1-e613-416e-98c1-943cfdc638ea", + "name": "Spawner (2107)", + "location": [5680, 1438, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "EvilMageLord", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c474fe51-44fd-4dc4-8a50-2a35fb00bfe8", + "name": "Spawner (2107)", + "location": [5733, 1433, 22], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "GiantRat", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, + { "name": "EvilMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d402bb06-3c2f-4151-bd55-a97f1459aa8c", + "name": "Spawner (2107)", + "location": [5787, 1427, 17], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaLizard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9253307c-4dbe-44bb-9e7f-7177e491daf7", + "name": "Spawner (2107)", + "location": [5717, 1465, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "FireElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a3a036ef-e0c6-4c5c-9573-2117548224e9", + "name": "Spawner (2107)", + "location": [5744, 1471, 2], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "FireElemental", "maxCount": 3, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93d283d5-a55c-47a8-b022-cb2807cd6864", + "name": "Spawner (2107)", + "location": [5790, 1451, 7], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 15, + "entries": [ + { "name": "Efreet", "maxCount": 1, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cf42e1f4-a588-42f6-a9ec-f25f181ca6ae", + "name": "Spawner (2107)", + "location": [5852, 1468, 1], + "map": "Trammel", + "count": 17, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "Lich", "maxCount": 4, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 6, "probability": 100 }, + { "name": "SkeletalMage", "maxCount": 6, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 3, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "56b8cca4-7325-4d9f-ae81-dfbe47bf02b5", + "name": "Spawner (2107)", + "location": [5852, 1468, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c7d55c1f-b262-4f11-a264-c731516e29a4", + "name": "Spawner (2107)", + "location": [5859, 1387, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaLizard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "017e2ed5-fb4e-4776-9c76-cff6f836f32d", + "name": "Spawner (2107)", + "location": [5787, 1386, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HellCat", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6ac34bad-d1e3-4d92-b379-d07ad3e49439", + "name": "Spawner (2107)", + "location": [5818, 1379, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c8ed6c9a-fa12-4cd8-8a14-e3eb1f07154a", + "name": "Spawner (2107)", + "location": [5836, 1364, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "835ef9b2-6755-46e7-bf92-e2004cd6b3f9", + "name": "Spawner (2107)", + "location": [5809, 1332, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "LavaSnake", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "20407dd7-05e5-4af7-8771-bcf92a833e0d", + "name": "Spawner (2107)", + "location": [5859, 1341, -1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72406438-fb9d-4ffb-8daa-a290a93efba5", + "name": "Spawner (2107)", + "location": [5835, 1304, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 4, "probability": 100 }, + { "name": "Efreet", "maxCount": 1, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "193c201f-41f0-4dad-a2d7-0bcf80e30199", + "name": "Spawner (2107)", + "location": [5867, 1309, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "263d2d5e-da10-4537-ac29-3010728ca3b4", + "name": "Spawner (2107)", + "location": [5699, 1302, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "FireElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "82c4dd19-052a-4f08-a8d7-540d994eee6e", + "name": "Spawner (2107)", + "location": [5723, 1291, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "df5837df-c256-4851-9863-5c9338442d8e", + "name": "Spawner (2107)", + "location": [5732, 1297, -1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 20, + "entries": [{ "name": "HellHound", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3dd210f5-6903-45b5-874a-1213f20378aa", + "name": "Spawner (2107)", + "location": [5667, 1315, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Efreet", "maxCount": 2, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eb42506a-19bf-470c-a4b6-4e96d6ede07f", + "name": "Spawner (2107)", + "location": [5747, 1320, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07e0ca08-16a9-4ea3-9349-bc11d1d86995", + "name": "Spawner (2107)", + "location": [5717, 1333, 6], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f8ce435-dfce-4ebc-ac13-34516a8067a7", + "name": "Spawner (2107)", + "location": [5731, 1379, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 7, + "entries": [ + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3c05932-b64d-45a7-aaf0-98bae95935e7", + "name": "Spawner (2107)", + "location": [5709, 1364, -1], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "HellHound", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "641ecc65-2a7a-4d79-8d6d-09064de015b9", + "name": "Spawner (2107)", + "location": [5691, 1347, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Daemon", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2a2291bc-7a04-4d90-be90-c9541aec5498", + "name": "Spawner (2107)", + "location": [5678, 1340, -2], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c6978e2b-eb18-46b6-8809-1dba551a5efc", + "name": "Spawner (2107)", + "location": [5651, 1363, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 5, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c4823a0-17b4-43bc-8033-ee7cde4874fd", + "name": "Spawner (2107)", + "location": [5654, 1380, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "63aa9c1a-6933-4ee4-bbf5-fbaed82df249", + "name": "Spawner (2107)", + "location": [5689, 1387, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "06eeaf2c-c00f-42fa-8c42-539a81928818", + "name": "Spawner (2107)", + "location": [5656, 1412, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 10, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e56c3640-05b4-407f-b362-11a83dfc9670", + "name": "Spawner (2107)", + "location": [5651, 1402, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3a2c62cd-5e59-4da4-a2ce-9a0c5c93febd", + "name": "Spawner (2107)", + "location": [5643, 1403, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "849224e3-2e29-427b-80eb-0a151e93e199", + "name": "Spawner (2107)", + "location": [5651, 1408, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [{ "name": "HellHound", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c15a4f6b-9710-4def-b8d0-09c117d33575", + "name": "Spawner (2107)", + "location": [5651, 1418, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 3, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "580578d9-a4f1-4054-8d68-ca645c60d4c4", + "name": "Spawner (2107)", + "location": [5650, 1419, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4b7d444d-5a83-4a4a-9f9b-f268080c00c6", + "name": "Spawner (2107)", + "location": [5647, 1439, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 15, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3f1fa8e4-0461-43c5-915d-1f8d6243c984", + "name": "Spawner (2107)", + "location": [5651, 1443, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a5f9ec55-e754-40f7-a83c-dfd7b78efc93", + "name": "Spawner (2107)", + "location": [5651, 1435, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c4aa67ce-4174-42c6-a18c-54d72771a68e", + "name": "Spawner (2107)", + "location": [5643, 1435, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 5, + "entries": [{ "name": "EvilMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19c7452e-c000-43d9-97f9-7e969d89bda8", + "name": "Spawner (2107)", + "location": [5638, 1439, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 40, + "entries": [{ "name": "EvilMageLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "40fc9fd2-ec56-4328-ba07-79287c997be5", + "name": "Spawner (2107)", + "location": [5714, 1465, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc9a02b7-3d5f-4609-b586-907c4ca497ee", + "name": "Spawner (2107)", + "location": [5723, 1390, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6e37e3a4-0e7e-474f-8f47-762a96993d74", + "name": "Spawner (2107)", + "location": [5679, 1438, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "85f5476d-1709-4dc9-b429-dadbc4f40506", + "name": "Spawner (2107)", + "location": [5715, 1465, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0077d996-0107-433c-868d-32757f57d4ec", + "name": "Spawner (2107)", + "location": [5722, 1390, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 7, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f8a27029-7a4d-4bc6-99c7-1e691e0e6c45", + "name": "Spawner (2107)", + "location": [5679, 1439, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab04e5e0-3592-4288-b924-ac2f2b78aac7", + "name": "Spawner (2107)", + "location": [5650, 1434, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "456851f3-5839-4c42-b5e2-5bebcb146b2b", + "name": "Spawner (2107)", + "location": [5643, 1435, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Graveyards.json b/Distribution/Data/Spawns/uoml/trammel/Graveyards.json new file mode 100644 index 000000000..45fd84fa1 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Graveyards.json @@ -0,0 +1,132 @@ +[ + { + "type": "Spawner", + "guid": "1b1194dd-0699-4c9e-9cbe-1332a477a90d", + "name": "Spawner (2108)", + "location": [1369, 1475, 10], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [ + { "name": "Spectre", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f40bd4f4-6ff1-40ba-84c3-296ade50ab51", + "name": "Spawner (2108)", + "location": [1285, 3731, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Spectre", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "Shade", "maxCount": 2, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eb0eb81f-7aea-4162-bf8d-1a0e614ecbcd", + "name": "Spawner (2108)", + "location": [2438, 1100, 8], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "003c2e17-0f7e-4af8-9b25-de6ccc4c6e24", + "name": "Spawner (2108)", + "location": [4545, 1317, 8], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Spectre", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69dd4fe9-7f47-466e-b290-963f9bf240d8", + "name": "Spawner (2108)", + "location": [2758, 867, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Spectre", "maxCount": 2, "probability": 100 }, + { "name": "Wraith", "maxCount": 2, "probability": 100 }, + { "name": "Skeleton", "maxCount": 3, "probability": 100 }, + { "name": "Zombie", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "18ac21c4-da65-4449-997e-6e3952e53d46", + "name": "Spawner (2108)", + "location": [722, 1119, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Skeleton", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fd6f9733-1638-485e-a3fd-3d6f9550591e", + "name": "Spawner (2108)", + "location": [3407, 2652, 48], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Zombie", "maxCount": 4, "probability": 100 }, + { "name": "Skeleton", "maxCount": 4, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Hythloth.json b/Distribution/Data/Spawns/uoml/trammel/Hythloth.json new file mode 100644 index 000000000..9c7b68328 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Hythloth.json @@ -0,0 +1,1215 @@ +[ + { + "type": "Spawner", + "guid": "276dda12-6154-456f-9cc9-ac111160fcb0", + "name": "Spawner (2109)", + "location": [5923, 48, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf3d2161-79d8-402f-9e07-26ebcab8fdc2", + "name": "Spawner (2109)", + "location": [5911, 55, 22], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel1", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0b1509d-4dd2-4b7a-bd21-84c8c93347aa", + "name": "Spawner (2109)", + "location": [5948, 49, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8afc042c-474c-4b81-bcc8-c05eb2d8f24e", + "name": "Spawner (2109)", + "location": [5954, 26, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ff350d79-2434-441a-85e9-254afcdafe5a", + "name": "Spawner (2109)", + "location": [5977, 49, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8db6fca-406c-4fcd-951d-5108bb4ed3d2", + "name": "Spawner (2109)", + "location": [5995, 62, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireGargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92736344-898e-4a70-aa23-229c2b92024e", + "name": "Spawner (2109)", + "location": [5970, 80, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94814adf-f077-409f-bd7d-84ca6a68e389", + "name": "Spawner (2109)", + "location": [5994, 96, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "772b0b8b-373b-4859-aeb9-46d2958b0cd8", + "name": "Spawner (2109)", + "location": [5946, 75, 22], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4244ac16-86b4-4f39-aa2a-7474103986f5", + "name": "Spawner (2109)", + "location": [5940, 107, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2179be82-2867-4f8e-b797-4102cc967c4c", + "name": "Spawner (2109)", + "location": [5915, 91, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d182489e-af6b-449d-a4d9-fd345ee98ce1", + "name": "Spawner (2109)", + "location": [5990, 36, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93525eb9-32ec-49c8-8565-e9a1728d098b", + "name": "Spawner (2109)", + "location": [5985, 36, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a6d37bb4-cfe5-4a9c-a35b-c8d8a11b8ab7", + "name": "Spawner (2109)", + "location": [5989, 25, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9e850a41-0f8a-4411-94e0-7c6df352f6fc", + "name": "Spawner (2109)", + "location": [5983, 25, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7e0d9ef3-4a24-44f4-bf1f-cc2c98ef3fa6", + "name": "Spawner (2109)", + "location": [5979, 26, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3f509b00-f24b-4d5a-b609-ec88fe0e025d", + "name": "Spawner (2109)", + "location": [5981, 84, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "04d3a931-3af8-4863-8989-58f6d1cae959", + "name": "Spawner (2109)", + "location": [5966, 70, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "969dad27-c44e-43c2-8456-a94c292253e1", + "name": "Spawner (2109)", + "location": [5958, 65, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d2957c0-20a9-43d6-8bc9-f1c46585f525", + "name": "Spawner (2109)", + "location": [5937, 65, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5b6e1b73-46ac-46b3-b416-c1a4b5e147f1", + "name": "Spawner (2109)", + "location": [5921, 81, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ad9f1c34-b053-4f75-bcca-67b8d0749726", + "name": "Spawner (2109)", + "location": [5972, 86, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5a2b323b-7048-480c-bd22-b8c557dd7571", + "name": "Spawner (2109)", + "location": [5917, 93, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "545e00c0-3dfb-485b-9f32-dc003ad34c1c", + "name": "Spawner (2109)", + "location": [5929, 77, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "159531c4-89e0-48c6-ac04-b4d67b078cae", + "name": "Spawner (2109)", + "location": [5945, 78, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6598121a-66ed-4839-b985-cca9e8cb789f", + "name": "Spawner (2109)", + "location": [5967, 80, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "798fd763-c76c-4656-86c3-988524cd12b6", + "name": "Spawner (2109)", + "location": [5961, 169, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f90467c3-88f7-43eb-81c2-fa882c5b62a9", + "name": "Spawner (2109)", + "location": [5992, 146, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Daemon", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fdf36d81-05fc-4c57-8b01-09f8de27484f", + "name": "Spawner (2109)", + "location": [5945, 175, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bd13d82-5c43-47a4-9610-882a8dbb5800", + "name": "Spawner (2109)", + "location": [5956, 145, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f80c3502-c704-4632-ae0d-322f3f7ff388", + "name": "Spawner (2109)", + "location": [5917, 156, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d56d6379-f8d7-4adc-bd09-0bdb75059548", + "name": "Spawner (2109)", + "location": [5914, 195, 22], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b63bde3-f24f-407e-9d11-0eb2c04305b3", + "name": "Spawner (2109)", + "location": [5918, 223, 44], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "350f7c02-4640-4932-89b6-2f4c4c4bc1b2", + "name": "Spawner (2109)", + "location": [5918, 235, 44], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "176b940f-621c-490e-baee-3c2816004e14", + "name": "Spawner (2109)", + "location": [5954, 225, 22], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 20, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c84dd874-d913-4a50-b681-ebe45af83d6a", + "name": "Spawner (2109)", + "location": [5986, 203, 44], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [ + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb68f3ce-29a6-47ea-ad5d-c74a7bfe7ad0", + "name": "Spawner (2109)", + "location": [5983, 169, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f2fcd059-3b7c-4855-a406-c419a3b46036", + "name": "Spawner (2109)", + "location": [5992, 147, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "52f85430-2e62-4e62-b548-46c1a184cf9e", + "name": "Spawner (2109)", + "location": [5970, 169, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72de652f-c027-4100-ab8b-43e229bc527b", + "name": "Spawner (2109)", + "location": [5997, 145, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "35fc4240-bcd9-4335-b930-b2140d486cc2", + "name": "Spawner (2109)", + "location": [5978, 185, 44], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0d2d46b6-5b3a-4668-b064-34d8035b7854", + "name": "Spawner (2109)", + "location": [5962, 217, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "760cb112-5308-4e18-9fd5-2e7dabc74fb5", + "name": "Spawner (2109)", + "location": [5989, 186, 44], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "88c01a9f-467c-4183-9e4a-ef7c47b8e5ce", + "name": "Spawner (2109)", + "location": [5926, 230, 44], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5a73a11a-55be-42d7-b6d4-9db6b6e0762f", + "name": "Spawner (2109)", + "location": [6094, 154, -22], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df4026a4-516d-4f35-bbdd-10fb253627c1", + "name": "Spawner (2109)", + "location": [6122, 170, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "ElderGazer", "maxCount": 1, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "443e9b08-9415-41e4-8e4e-07c813d1f687", + "name": "Spawner (2109)", + "location": [6086, 178, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9a143ac-b466-4e8f-a283-146f9cba767f", + "name": "Spawner (2109)", + "location": [6119, 219, 22], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fab57726-33f0-45bb-9aa3-28650b92d61b", + "name": "Spawner (2109)", + "location": [6058, 193, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "132536ba-9f7b-47a7-bf31-be4487936235", + "name": "Spawner (2109)", + "location": [6033, 199, 22], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23ec32d7-3f5f-4d4c-b89c-2449ba45aa6e", + "name": "Spawner (2109)", + "location": [6052, 156, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "GazerLarva", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3501f38-bdac-418b-969f-4e0e9f7d9c42", + "name": "Spawner (2109)", + "location": [6121, 166, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "185cd08c-716f-4255-a8aa-b9e2dbe06e44", + "name": "Spawner (2109)", + "location": [6124, 159, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e59bd560-153a-46a4-b083-8d4278b0ef2c", + "name": "Spawner (2109)", + "location": [6087, 186, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6524efb1-e7c2-47e4-91ad-393224529ad8", + "name": "Spawner (2109)", + "location": [6060, 158, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6638a784-d1d4-4ceb-b913-0d19c3876bd1", + "name": "Spawner (2109)", + "location": [6046, 199, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9339a8f1-6dd9-444e-a105-3fda18569885", + "name": "Spawner (2109)", + "location": [6041, 222, 44], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3e2dd9b5-5fc4-4308-9ae0-a8817d4fac63", + "name": "Spawner (2109)", + "location": [6125, 221, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d5b56fe5-edf3-4e8a-933c-692ff3bc752c", + "name": "Spawner (2109)", + "location": [6120, 229, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e141c079-d2a8-43ad-a226-1cb79f87cc7d", + "name": "Spawner (2109)", + "location": [6092, 187, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4653daba-0405-4fb3-b89c-b712cf560782", + "name": "Spawner (2109)", + "location": [6041, 230, 44], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1deb5cae-7cab-4b3b-91ef-a933a9836369", + "name": "Spawner (2109)", + "location": [6033, 205, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2f802063-b694-457e-8662-8c56600b28d1", + "name": "Spawner (2109)", + "location": [6058, 153, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab1c29a8-d843-4ba3-84b9-b5ef0ef2f524", + "name": "Spawner (2109)", + "location": [6064, 96, 22], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Daemon", "maxCount": 2, "probability": 100 }, + { "name": "HellHound", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0f29c64-4887-41c8-ab48-463a37a2f599", + "name": "Spawner (2109)", + "location": [6081, 83, 21], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e94b0430-2565-4cdd-a7df-f87d3cb61887", + "name": "Spawner (2109)", + "location": [6084, 66, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76399213-8099-47d2-b5e5-6f12d7e3ffde", + "name": "Spawner (2109)", + "location": [6106, 67, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84e31077-87fd-482c-ad78-6c7a8857b572", + "name": "Spawner (2109)", + "location": [6116, 86, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "FireGargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5d613a12-4519-421f-afd7-dfe2d8dfc2ed", + "name": "Spawner (2109)", + "location": [6099, 51, 22], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Gargoyle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d3e6e31a-5547-47b2-afaa-f7f64512bdb7", + "name": "Spawner (2109)", + "location": [6107, 37, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "Balron", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8254a266-263f-4f5f-b8ac-7646b42b484a", + "name": "Spawner (2109)", + "location": [6054, 45, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "StoneGargoyle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23a59d14-b35b-4776-9e39-c2b32a16262d", + "name": "Spawner (2109)", + "location": [6108, 66, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a8ce27ea-3cc0-49b9-afd4-6f404432bec5", + "name": "Spawner (2109)", + "location": [6117, 81, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bdf775bc-e899-44df-b7aa-36759f438e22", + "name": "Spawner (2109)", + "location": [6058, 92, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d5def22-5137-48d5-ba95-54fb10dae441", + "name": "Spawner (2109)", + "location": [6113, 92, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b538b4b4-eba9-4854-9c66-411d622b3ed8", + "name": "Spawner (2109)", + "location": [6107, 70, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "36a58f71-fb57-48db-b111-d17ec727d1aa", + "name": "Spawner (2109)", + "location": [6081, 44, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3da1531b-38a3-42f5-a69e-1f7a1aa2cb89", + "name": "Spawner (2109)", + "location": [6097, 36, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "75f93255-3c6f-47dc-b862-3d4a61bf454a", + "name": "Spawner (2109)", + "location": [6064, 96, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8eeb526f-8a8e-40ab-9f37-79d211549bef", + "name": "Spawner (2109)", + "location": [6086, 45, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "177607bb-ea2f-484c-8e06-262edf56f6d6", + "name": "Spawner (2109)", + "location": [6108, 36, 22], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Ice.json b/Distribution/Data/Spawns/uoml/trammel/Ice.json new file mode 100644 index 000000000..37ebbac9c --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Ice.json @@ -0,0 +1,522 @@ +[ + { + "type": "Spawner", + "guid": "dee07fb6-4dcc-43d5-bd0a-706c36801e8d", + "name": "Spawner (2110)", + "location": [5771, 224, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9cbfc951-e954-4e8d-b1af-cf9457216b53", + "name": "Spawner (2110)", + "location": [5799, 188, -6], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "IceSerpent", "maxCount": 2, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a74f4f2c-7906-46c3-bd5f-8ce30e43417c", + "name": "Spawner (2110)", + "location": [5867, 218, -4], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "FrostOoze", "maxCount": 2, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84103371-1387-4ebe-af6a-91faf5d3a5f2", + "name": "Spawner (2110)", + "location": [5850, 219, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RatmanMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ada3263b-aece-4dee-a69b-fe3ef96a3550", + "name": "Spawner (2110)", + "location": [5814, 237, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "ArcticOgreLord", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3def478d-d193-49d2-96e0-b36023034db7", + "name": "Spawner (2110)", + "location": [5752, 144, 9], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "WhiteWyrm", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49c552fa-1934-405a-8d30-4fb7db89313f", + "name": "Spawner (2110)", + "location": [5839, 159, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "FrostOoze", "maxCount": 1, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "IceElemental", "maxCount": 1, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "308a4f85-5d3f-4f44-af64-371951608f97", + "name": "Spawner (2110)", + "location": [5785, 161, -6], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 20, + "entries": [ + { "name": "FrostOoze", "maxCount": 1, "probability": 100 }, + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "IceElemental", "maxCount": 1, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6faf1f3-65fa-47a8-a597-1bd61d7dc350", + "name": "Spawner (2110)", + "location": [5757, 213, -7], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "IceSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "IceElemental", "maxCount": 4, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a9adcc2c-c67d-4f0d-8241-7aa8d73d0fdd", + "name": "Spawner (2110)", + "location": [5681, 191, -6], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [ + { "name": "IceSerpent", "maxCount": 3, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "IceElemental", "maxCount": 4, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ec99717-4da8-4d7c-9fb2-dd9ba4c845bc", + "name": "Spawner (2110)", + "location": [5731, 176, -5], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 35, + "entries": [ + { "name": "IceSerpent", "maxCount": 9, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 9, "probability": 100 }, + { "name": "IceElemental", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b69a34f7-3ff9-4b19-b38e-817c98a7c93f", + "name": "Spawner (2110)", + "location": [5848, 226, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b5f90c32-64bd-4524-bfc6-84716f186595", + "name": "Spawner (2110)", + "location": [5683, 197, -4], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f7a22de1-cc80-4060-aa02-9728d7e241a0", + "name": "Spawner (2110)", + "location": [5721, 147, -1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c1a3b5ae-9c59-488f-8d7f-24d25e0c7673", + "name": "Spawner (2110)", + "location": [5758, 139, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "66e9c531-1fee-42e4-910a-cdf71b212b95", + "name": "Spawner (2110)", + "location": [5754, 211, -6], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "106c22c9-9d01-43a0-8983-bea255146fd5", + "name": "Spawner (2110)", + "location": [5833, 242, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8855a9a7-aed4-4c54-94da-a94024783d51", + "name": "Spawner (2110)", + "location": [5854, 227, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3cfd9a1b-000a-430f-b44c-e64720d69237", + "name": "Spawner (2110)", + "location": [5772, 188, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "03886f2b-1e0e-48fd-8b92-35a1a60a95c5", + "name": "Spawner (2110)", + "location": [5837, 245, -5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bc1b0fa0-397d-400e-a8a1-46c6980d3ed7", + "name": "Spawner (2110)", + "location": [5751, 140, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "722d603b-dddb-4b71-88c4-9e965918d6e9", + "name": "Spawner (2110)", + "location": [5763, 187, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7ebb7a60-b45f-469c-ad0b-86490c855be1", + "name": "Spawner (2110)", + "location": [5756, 203, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9456469a-1f9f-43a1-9c7b-639ca4e367f3", + "name": "Spawner (2110)", + "location": [5712, 145, -34], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4df74fc7-0443-4fdc-a6ad-57d9a4d6bc7f", + "name": "Spawner (2110)", + "location": [5684, 181, -5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44394753-6925-4e7f-95d0-d1998f5bb036", + "name": "Spawner (2110)", + "location": [5831, 358, -1], + "map": "Trammel", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Ratman", "maxCount": 12, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 7, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6502c7e1-5c7a-43b7-a0de-33f73f78e6a3", + "name": "Spawner (2110)", + "location": [5838, 355, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76706bc3-f4c4-4040-b206-650aa04e041d", + "name": "Spawner (2110)", + "location": [5824, 363, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4133a659-4bcb-4b38-a3fe-097866c8aa4e", + "name": "Spawner (2110)", + "location": [5680, 306, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "IceFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "947f998d-a84b-49c8-8619-852fff3e5295", + "name": "Spawner (2110)", + "location": [5669, 330, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "IceFiend", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ca52ec08-1f4f-400a-ab1f-9e05892dbdd2", + "name": "Spawner (2110)", + "location": [5696, 303, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9f689286-dcc8-44ac-9d6b-c34c002d5ff5", + "name": "Spawner (2110)", + "location": [5682, 314, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3f420eb7-724d-47e0-89fa-1e844feee72c", + "name": "Spawner (2110)", + "location": [5666, 331, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b4e94abe-5829-4d51-acb5-e112d8b33ce1", + "name": "Spawner (2110)", + "location": [5676, 331, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/LostLands.json b/Distribution/Data/Spawns/uoml/trammel/LostLands.json new file mode 100644 index 000000000..a74694b4c --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/LostLands.json @@ -0,0 +1,3430 @@ +[ + { + "type": "Spawner", + "guid": "12cb6fcd-2072-4003-885b-76821f1993cf", + "name": "Spawner (2111)", + "location": [5728, 3209, -3], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [{ "name": "SwampDragon", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "efc4c627-fca8-4e56-ae35-f66d3552c8bd", + "name": "Spawner (2111)", + "location": [5457, 2651, 45], + "map": "Trammel", + "count": 54, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Imp", "maxCount": 15, "probability": 100 }, + { "name": "Scorpion", "maxCount": 15, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 6, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cefa6e21-5391-467f-818c-5da5eb3ab833", + "name": "Spawner (2111)", + "location": [5457, 2651, 45], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 6, "probability": 100 }, + { "name": "Mummy", "maxCount": 4, "probability": 100 }, + { "name": "Ettin", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef5cab61-3a77-4439-9342-d0834b31b96a", + "name": "Spawner (2111)", + "location": [5520, 2890, 33], + "map": "Trammel", + "count": 37, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Imp", "maxCount": 10, "probability": 100 }, + { "name": "Scorpion", "maxCount": 10, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec56459e-8667-4086-8068-699bbe693b8d", + "name": "Spawner (2111)", + "location": [5520, 2890, 33], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 4, "probability": 100 }, + { "name": "Mummy", "maxCount": 2, "probability": 100 }, + { "name": "Ettin", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84c8a1d7-5dfd-4115-a610-95e853d19ff8", + "name": "Spawner (2111)", + "location": [5580, 2885, 6], + "map": "Trammel", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Gazer", "maxCount": 3, "probability": 100 }, + { "name": "Reaper", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2071c33b-72f7-4fb4-b2b3-923940d3ab47", + "name": "Spawner (2111)", + "location": [5557, 2625, 0], + "map": "Trammel", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 3, "probability": 100 }, + { "name": "Troll", "maxCount": 3, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "527e7c9f-a712-4fb2-956f-cb48ed793b21", + "name": "Spawner (2111)", + "location": [5394, 2530, 6], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [ + { "name": "Cyclops", "maxCount": 5, "probability": 100 }, + { "name": "Titan", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f8909944-1483-4ba8-9387-6706bbf6aa41", + "name": "Spawner (2111)", + "location": [5711, 2494, 46], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Imp", "maxCount": 3, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "562a1a95-4a94-4fde-8bca-5e7f53561a76", + "name": "Spawner (2111)", + "location": [5711, 2494, 46], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Ettin", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f9f0921-b571-4713-b9e0-b61ea7b54b01", + "name": "Spawner (2111)", + "location": [5765, 2637, 39], + "map": "Trammel", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 3, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 6, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 3, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 4, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "48e240ce-cc40-4668-8ea9-a1238cc8f812", + "name": "Spawner (2111)", + "location": [5697, 2626, -2], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [ + { "name": "Wyvern", "maxCount": 2, "probability": 100 }, + { "name": "Drake", "maxCount": 4, "probability": 100 }, + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0eea03f4-90bc-445e-b9d2-a5c563620af3", + "name": "Spawner (2111)", + "location": [5729, 2767, -1], + "map": "Trammel", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 70, + "entries": [ + { "name": "Imp", "maxCount": 8, "probability": 100 }, + { "name": "Scorpion", "maxCount": 8, "probability": 100 }, + { "name": "DesertOstard", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abd01cf8-e978-4a3b-9368-8004894052df", + "name": "Spawner (2111)", + "location": [5729, 2767, -1], + "map": "Trammel", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 70, + "entries": [ + { "name": "StoneGargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Wyvern", "maxCount": 3, "probability": 100 }, + { "name": "Mummy", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a51c39e-d65f-4ae3-bef0-7b35a7b6f278", + "name": "Spawner (2111)", + "location": [5724, 2949, 31], + "map": "Trammel", + "count": 44, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 65, + "walkingRange": 75, + "entries": [ + { "name": "HellHound", "maxCount": 8, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 8, "probability": 100 }, + { "name": "FireElemental", "maxCount": 10, "probability": 100 }, + { "name": "Efreet", "maxCount": 4, "probability": 100 }, + { "name": "HellCat", "maxCount": 6, "probability": 100 }, + { "name": "LavaSerpent", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "521f89eb-db58-4e5c-8633-f435c40ab6eb", + "name": "Spawner (2111)", + "location": [5724, 2949, 31], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 65, + "walkingRange": 75, + "entries": [{ "name": "LavaSnake", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "057290d8-05ed-4d10-8b1b-b6ff72396554", + "name": "Spawner (2111)", + "location": [5863, 2470, 46], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [{ "name": "Wyvern", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aa8093ad-4207-4986-9724-1fae5cf6e257", + "name": "Spawner (2111)", + "location": [5501, 2361, 27], + "map": "Trammel", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 8, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, + { "name": "Walrus", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f6e752bd-6e0e-4436-9b9e-7598b356993b", + "name": "Spawner (2111)", + "location": [5501, 2361, 27], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b5a3d231-4dca-41de-92e9-8f8eb2e23cf9", + "name": "Spawner (2111)", + "location": [5351, 2359, 0], + "map": "Trammel", + "count": 29, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 7, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 5, "probability": 100 }, + { "name": "Walrus", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8efa8f7f-e693-4967-86be-e19bb93893c0", + "name": "Spawner (2111)", + "location": [5351, 2359, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "894ddf46-b0fd-43c4-8fc0-a2bf626e2a53", + "name": "Spawner (2111)", + "location": [5173, 2368, 21], + "map": "Trammel", + "count": 28, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [ + { "name": "IceSerpent", "maxCount": 4, "probability": 100 }, + { "name": "IceSnake", "maxCount": 10, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, + { "name": "Walrus", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "230b1817-c355-4413-a899-3d1dcd5087c9", + "name": "Spawner (2111)", + "location": [5173, 2368, 21], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 50, + "entries": [{ "name": "PolarBear", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c142b48f-bf5a-4953-9af2-8f72be636600", + "name": "Spawner (2111)", + "location": [6091, 2379, 25], + "map": "Trammel", + "count": 28, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 60, + "entries": [ + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 10, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 3, "probability": 100 }, + { "name": "Walrus", "maxCount": 3, "probability": 100 }, + { "name": "PolarBear", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa842995-ea24-4be7-9594-6632e33971c3", + "name": "Spawner (2111)", + "location": [5967, 2370, 44], + "map": "Trammel", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 4, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3d1dec7-ad92-4678-926f-6addcb325ebc", + "name": "Spawner (2111)", + "location": [5967, 2370, 44], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8c3d2529-fd5d-4c3a-8071-4df7504d621e", + "name": "Spawner (2111)", + "location": [5817, 2367, 42], + "map": "Trammel", + "count": 23, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [ + { "name": "IceSerpent", "maxCount": 6, "probability": 100 }, + { "name": "IceSnake", "maxCount": 3, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 3, "probability": 100 }, + { "name": "SnowElemental", "maxCount": 3, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20ee3b9f-9776-4cf4-aee9-faa751edca21", + "name": "Spawner (2111)", + "location": [5817, 2367, 42], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 75, + "entries": [{ "name": "PolarBear", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "17dfff61-8b8f-400c-b93a-1e15c55b42db", + "name": "Spawner (2111)", + "location": [5756, 3453, -2], + "map": "Trammel", + "count": 42, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "Panther", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9dfdd93-a5e2-4db4-b98c-9da90a65101f", + "name": "Spawner (2111)", + "location": [5985, 3441, 17], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 90, + "entries": [ + { "name": "GiantToad", "maxCount": 8, "probability": 100 }, + { "name": "SwampDragon", "maxCount": 5, "probability": 100 }, + { "name": "GiantRat", "maxCount": 12, "probability": 100 }, + { "name": "Alligator", "maxCount": 12, "probability": 100 }, + { "name": "SewerRat", "maxCount": 11, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf87c030-7319-45ba-bf07-e086dda7f7f3", + "name": "Spawner (2111)", + "location": [5985, 3441, 17], + "map": "Trammel", + "count": 36, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 90, + "entries": [ + { "name": "BullFrog", "maxCount": 15, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 5, "probability": 100 }, + { "name": "Bogling", "maxCount": 13, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa220a4b-57d9-4b63-b224-bd411a926045", + "name": "Spawner (2111)", + "location": [5220, 3664, -1], + "map": "Trammel", + "count": 23, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 25, + "entries": [ + { "name": "BoneKnight", "maxCount": 3, "probability": 100 }, + { "name": "SkeletalKnight", "maxCount": 3, "probability": 100 }, + { "name": "BoneMagi", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 6, "probability": 100 }, + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe198cdf-57f9-40d8-8878-de34a8c84298", + "name": "Spawner (2111)", + "location": [5212, 3619, 0], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "SavageRider", "maxCount": 3, "probability": 100 }, + { "name": "Savage", "maxCount": 9, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b13fd88-7eec-4422-9b79-bd314a1bba2f", + "name": "Spawner (2111)", + "location": [5347, 3449, 17], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "218c75c8-acf3-4239-b72e-4ac850c8918a", + "name": "Spawner (2111)", + "location": [5243, 3548, 14], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9cb57aa-0183-4149-af6b-a47499a2ff72", + "name": "Spawner (2111)", + "location": [5250, 3449, 16], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3b32813c-00d7-479a-b5e5-1570dd11e90e", + "name": "Spawner (2111)", + "location": [5349, 3547, -1], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 4, "probability": 100 }, + { "name": "Hind", "maxCount": 16, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "17e80af9-7b12-4515-99af-8a61e7ba5d44", + "name": "Spawner (2111)", + "location": [5201, 3162, 53], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94ea8ab3-3e9d-4d64-8c52-c9a96347b234", + "name": "Spawner (2111)", + "location": [5201, 3162, 53], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0ae04c79-0948-4ebf-a7e8-2241db34b661", + "name": "Spawner (2111)", + "location": [5251, 3301, -2], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "020cf0fc-c7b4-4735-b113-a5f076bfd189", + "name": "Spawner (2111)", + "location": [5251, 3301, -2], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "034a2452-0f15-48f2-9bc3-3a1fc3deed2e", + "name": "Spawner (2111)", + "location": [5250, 3401, 1], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7ff8cd5c-784b-4c77-b20a-de86c8fafa76", + "name": "Spawner (2111)", + "location": [5250, 3401, 1], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a49e3c21-9ea2-426f-a94d-f36324e96751", + "name": "Spawner (2111)", + "location": [5351, 3401, 30], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 6, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 6, "probability": 100 }, + { "name": "BlackBear", "maxCount": 6, "probability": 100 }, + { "name": "Horse", "maxCount": 4, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 4, "probability": 100 }, + { "name": "Rabbit", "maxCount": 4, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 4, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Cougar", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8cdfed0c-8b67-4041-9923-82af57871539", + "name": "Spawner (2111)", + "location": [5351, 3401, 30], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Llama", "maxCount": 2, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72d79fc1-9984-4107-9c30-f4dc77c2976f", + "name": "Spawner (2111)", + "location": [6018, 3673, 18], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e30e600c-0ae2-4521-8721-ec8a952f47b4", + "name": "Spawner (2111)", + "location": [6018, 3673, 18], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6f3017b-f7d2-4957-8de4-c415f99c7a6c", + "name": "Spawner (2111)", + "location": [6018, 3673, 18], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f584b25-d87b-49e8-94ae-0405a3d3cc45", + "name": "Spawner (2111)", + "location": [6018, 3673, 18], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd0e2342-a6e7-4b41-acb2-8d0d6f316062", + "name": "Spawner (2111)", + "location": [6022, 3773, 18], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98af7987-041e-4589-aa4a-259b5faf9057", + "name": "Spawner (2111)", + "location": [6022, 3773, 18], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f6ce1e0-6056-4ae7-b976-ea650d4a0a9c", + "name": "Spawner (2111)", + "location": [6022, 3773, 18], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "afb0dc5f-fdc5-441a-a5dc-288767a16e69", + "name": "Spawner (2111)", + "location": [6022, 3773, 18], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f5d2b74-9714-4a11-8f0d-b88413e22556", + "name": "Spawner (2111)", + "location": [6022, 2874, 16], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d463133b-6448-4ead-95a3-6cbfe8ce50cd", + "name": "Spawner (2111)", + "location": [6022, 2874, 16], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d4b41e7-0405-43b3-babf-4855aa1222aa", + "name": "Spawner (2111)", + "location": [6022, 2874, 16], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ad6c839-2bd5-4b9e-8ce6-3e55b1d2de83", + "name": "Spawner (2111)", + "location": [6022, 2874, 16], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebb689ed-d6b6-4395-b26b-ef1fecd26093", + "name": "Spawner (2111)", + "location": [6022, 3971, 17], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d0844af0-b9fd-4500-8751-8d4e0f913ee2", + "name": "Spawner (2111)", + "location": [6022, 3971, 17], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cbf0e551-7d26-4227-b14e-9ae59fbfafd1", + "name": "Spawner (2111)", + "location": [6022, 3971, 17], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "27ac5355-03f9-44a1-9d04-78db0e9b9d25", + "name": "Spawner (2111)", + "location": [6022, 3971, 17], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4d538e7-9d37-4707-81ff-db9eef73be4e", + "name": "Spawner (2111)", + "location": [5920, 3675, 12], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37c219c7-139b-4604-acd9-34e10d961cd9", + "name": "Spawner (2111)", + "location": [5920, 3675, 12], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a268bfc5-82da-4d3a-aac5-ca114f7f2c9a", + "name": "Spawner (2111)", + "location": [5920, 3675, 12], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28a1961d-3ff6-4890-a603-c5fb5512eb3e", + "name": "Spawner (2111)", + "location": [5920, 3675, 12], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12b8818a-8be6-47c4-bdc6-27a68420bc60", + "name": "Spawner (2111)", + "location": [5921, 3777, 28], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4be24872-6eab-4638-85db-fca18bf64762", + "name": "Spawner (2111)", + "location": [5921, 3777, 28], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37ba19f6-eb60-4539-8f17-b87529148607", + "name": "Spawner (2111)", + "location": [5921, 3777, 28], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "502dd43b-fe21-4851-83cc-0479b0fd3f99", + "name": "Spawner (2111)", + "location": [5921, 3777, 28], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84c9128d-15f3-4670-ab03-a6472bb66f26", + "name": "Spawner (2111)", + "location": [5919, 3875, 16], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "11d43b66-9a30-4c18-801b-a93222314461", + "name": "Spawner (2111)", + "location": [5919, 3875, 16], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f10cdd8b-b8ea-44ad-9b02-9bb147f5c85d", + "name": "Spawner (2111)", + "location": [5919, 3875, 16], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5dd609e-9369-4d3c-b3e0-2d74ce2d204a", + "name": "Spawner (2111)", + "location": [5919, 3875, 16], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2b552925-08cd-4be2-b96f-85701200ab9a", + "name": "Spawner (2111)", + "location": [5918, 3975, 1], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2b1377f8-2453-4797-8f16-121a5da7dd1d", + "name": "Spawner (2111)", + "location": [5918, 3975, 1], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b100d6ba-9d60-4454-9657-4b4b5a84b528", + "name": "Spawner (2111)", + "location": [5918, 3975, 1], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03829553-0468-42d4-b617-02116bca3200", + "name": "Spawner (2111)", + "location": [5918, 3975, 1], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2e8331c-2368-4014-ba4c-afa7e63c86e3", + "name": "Spawner (2111)", + "location": [5820, 3675, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "05f8720b-51de-4359-96c4-edee763473a8", + "name": "Spawner (2111)", + "location": [5820, 3675, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "34888307-f095-4c8b-afe0-e5f3c45c5a24", + "name": "Spawner (2111)", + "location": [5820, 3675, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8add37de-2b09-4215-9568-e8586520f1ce", + "name": "Spawner (2111)", + "location": [5820, 3675, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d89f6fd3-bc3e-4d39-a5b2-6d0b186d2a44", + "name": "Spawner (2111)", + "location": [5820, 3775, 48], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e42b948-0bd7-4f93-8303-2d7723a1bb1d", + "name": "Spawner (2111)", + "location": [5820, 3775, 48], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3bb22e15-16d5-4f29-b41a-051fa2a5bf56", + "name": "Spawner (2111)", + "location": [5820, 3775, 48], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a34689d-a46f-4d99-a9d3-96eb175dcf83", + "name": "Spawner (2111)", + "location": [5820, 3775, 48], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b7087528-b615-4aba-8544-e4904472600f", + "name": "Spawner (2111)", + "location": [5818, 3876, 18], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec1caee7-0d96-49ac-8b1f-f759b03efdf8", + "name": "Spawner (2111)", + "location": [5818, 3876, 18], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e23f7c11-dffc-4ba8-bae8-af7acae97635", + "name": "Spawner (2111)", + "location": [5818, 3876, 18], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e889357-16df-4213-bc1e-e19a942f58ad", + "name": "Spawner (2111)", + "location": [5818, 3876, 18], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a91547bb-7e0f-4518-92f8-dbaadb86bdfb", + "name": "Spawner (2111)", + "location": [5819, 3974, 16], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9afd457-3945-403c-a0de-fb2b0ab73b8e", + "name": "Spawner (2111)", + "location": [5819, 3974, 16], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e007014-f2b1-4454-8fc7-66065189e313", + "name": "Spawner (2111)", + "location": [5819, 3974, 16], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c914773-48a0-4129-ae65-87ee6707e4f7", + "name": "Spawner (2111)", + "location": [5819, 3974, 16], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b614a58d-1a96-4cc1-b136-30bc2ee50291", + "name": "Spawner (2111)", + "location": [5721, 3675, 22], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8fce4e75-131b-4255-b270-41bcaf47d4b4", + "name": "Spawner (2111)", + "location": [5721, 3675, 22], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db56d1fb-cd8c-4a7d-a029-1170f3bd78dc", + "name": "Spawner (2111)", + "location": [5721, 3675, 22], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50ccc29d-4ee3-4606-b7e2-c03156afb9ba", + "name": "Spawner (2111)", + "location": [5721, 3675, 22], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa8905ce-91db-4d17-92c1-ae5b04738dea", + "name": "Spawner (2111)", + "location": [5722, 3776, 32], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79f39847-4344-4381-8b37-8eaa8c2ffef7", + "name": "Spawner (2111)", + "location": [5722, 3776, 32], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6e6ba9f1-8c4c-4142-b251-141d2ea7a7f0", + "name": "Spawner (2111)", + "location": [5722, 3776, 32], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54dcd3be-256f-48ca-900d-f585ae0a721a", + "name": "Spawner (2111)", + "location": [5722, 3776, 32], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1524f465-d2dd-4315-b98f-e54e03000f27", + "name": "Spawner (2111)", + "location": [5721, 3876, -3], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bb402f0-2399-410d-ac65-eed098e522ca", + "name": "Spawner (2111)", + "location": [5721, 3876, -3], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "318ff931-f450-4626-b97f-13af5c0e41e3", + "name": "Spawner (2111)", + "location": [5721, 3876, -3], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "65753d38-25f4-4282-9dab-3fa55cba90f4", + "name": "Spawner (2111)", + "location": [5721, 3876, -3], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fdde56d8-75b8-4488-8f25-3768fe5b8b14", + "name": "Spawner (2111)", + "location": [5721, 3975, 21], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad3477fd-221f-4a59-ba0d-e120e09a9146", + "name": "Spawner (2111)", + "location": [5721, 3975, 21], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e64232f7-0ccb-4c81-9a36-0c96fdd8e00a", + "name": "Spawner (2111)", + "location": [5721, 3975, 21], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4306a87f-e65c-43ba-96d2-276065502e18", + "name": "Spawner (2111)", + "location": [5721, 3975, 21], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78272d11-7b2e-4e49-b533-9f82138558d1", + "name": "Spawner (2111)", + "location": [5623, 3676, -3], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd4ffdc9-a292-4a4b-a765-1f3a1ec0e9c4", + "name": "Spawner (2111)", + "location": [5623, 3676, -3], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c07ac1f6-f0bd-483f-ac86-db1e8cc1914f", + "name": "Spawner (2111)", + "location": [5623, 3676, -3], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "86e1ffa4-247e-40e8-9b24-069df5468fc4", + "name": "Spawner (2111)", + "location": [5623, 3676, -3], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b655971-59b1-4016-b9d2-c2d4c2a11e50", + "name": "Spawner (2111)", + "location": [5621, 3775, 2], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce64bca0-53f1-4f9a-a4ff-2da36a7bdcb6", + "name": "Spawner (2111)", + "location": [5621, 3775, 2], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "53071c27-4eb5-4101-b20f-0008810bf616", + "name": "Spawner (2111)", + "location": [5621, 3775, 2], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d055b36c-22ea-42c2-b862-493a9a0ba34e", + "name": "Spawner (2111)", + "location": [5621, 3775, 2], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "896be635-414c-4750-8e26-742fea9d795c", + "name": "Spawner (2111)", + "location": [5623, 3875, 15], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "036ad518-7a5a-4805-9d06-6deb52839f4f", + "name": "Spawner (2111)", + "location": [5623, 3875, 15], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68e6f042-92f2-45a9-ae8d-0752b3b4b944", + "name": "Spawner (2111)", + "location": [5623, 3875, 15], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6087c54a-11de-4326-8ec5-cd5b5cde47a4", + "name": "Spawner (2111)", + "location": [5623, 3875, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c339abe3-345c-4881-aa87-f0c360517450", + "name": "Spawner (2111)", + "location": [5624, 3975, -3], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0eeef349-4bf3-414a-8a66-b0a1de50f17c", + "name": "Spawner (2111)", + "location": [5624, 3975, -3], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5812b2ff-56e7-47ee-937f-040e60498ca1", + "name": "Spawner (2111)", + "location": [5624, 3975, -3], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7181d30e-02d5-4a40-9d4a-d856732d05e9", + "name": "Spawner (2111)", + "location": [5624, 3975, -3], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a5e4485-6df8-4fae-9288-90ac1e3a871d", + "name": "Spawner (2111)", + "location": [5523, 3676, 45], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0213ce1-2ab9-4aa0-899c-8a9c5aed26f7", + "name": "Spawner (2111)", + "location": [5523, 3676, 45], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09c49d83-9516-43f4-b947-545a44f71516", + "name": "Spawner (2111)", + "location": [5523, 3676, 45], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "227477e6-a891-4b3e-b43b-305c6b6541f4", + "name": "Spawner (2111)", + "location": [5523, 3676, 45], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "79cc7028-11d1-49df-b0a5-75066e5a6559", + "name": "Spawner (2111)", + "location": [5521, 3775, 8], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df38e40e-4233-447c-9faa-0eda376e975d", + "name": "Spawner (2111)", + "location": [5521, 3775, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7aa1e207-3ea8-4aca-b0dc-a0a585466eed", + "name": "Spawner (2111)", + "location": [5521, 3775, 8], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbd8cc70-dbcc-477c-bbe3-9865e30453da", + "name": "Spawner (2111)", + "location": [5521, 3775, 8], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "832c6d54-6e88-4e70-ba0f-9731b9ecc3cc", + "name": "Spawner (2111)", + "location": [5521, 3875, -1], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "162b114a-bf25-4054-8124-1ed834e586b1", + "name": "Spawner (2111)", + "location": [5521, 3875, -1], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a43ee478-1a1b-4c04-85b9-0c9fe436e7db", + "name": "Spawner (2111)", + "location": [5521, 3875, -1], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea9eae49-ae57-49b2-8a41-c956d8e8aafe", + "name": "Spawner (2111)", + "location": [5521, 3875, -1], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94b422df-ea7d-4c7f-816f-1f89ad5c9e21", + "name": "Spawner (2111)", + "location": [5478, 3587, 3], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "BrownBear", "maxCount": 3, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 3, "probability": 100 }, + { "name": "BlackBear", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 2, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 2, "probability": 100 }, + { "name": "Rabbit", "maxCount": 2, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 2, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7dca1dbd-9eb3-4a64-b276-02d6205baf2a", + "name": "Spawner (2111)", + "location": [5478, 3587, 3], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 1, "probability": 100 }, + { "name": "ForestOstard", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "917b5953-f939-46c0-863e-17a1ca293dd8", + "name": "Spawner (2111)", + "location": [5478, 3587, 3], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Gazer", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Mongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4ea0559-6a74-406b-a53a-aa0aa9338d4d", + "name": "Spawner (2111)", + "location": [5478, 3587, 3], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rat", "maxCount": 1, "probability": 100 }, + { "name": "Ratman", "maxCount": 1, "probability": 100 }, + { "name": "Reaper", "maxCount": 1, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1308141-2a7f-45ea-8514-38453ba30742", + "name": "Spawner (2111)", + "location": [5520, 3939, 42], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "ForestOstard", "maxCount": 3, "probability": 100 }, + { "name": "RidableLlama", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "57a3231a-5b0e-45dd-bcfc-3340878eb9cf", + "name": "Spawner (2111)", + "location": [5784, 3951, 20], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 35, + "walkingRange": 35, + "entries": [{ "name": "FrenziedOstard", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b7df3e65-2706-4ad9-89e4-20e50c6f83ba", + "name": "Spawner (2111)", + "location": [5764, 2585, -4], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 75, + "entries": [ + { "name": "Imp", "maxCount": 2, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07bf9106-e8c2-4f1c-8d4b-1344e90624a3", + "name": "Spawner (2111)", + "location": [5838, 2651, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 75, + "entries": [ + { "name": "Imp", "maxCount": 1, "probability": 100 }, + { "name": "Harpy", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 }, + { "name": "StoneHarpy", "maxCount": 1, "probability": 100 }, + { "name": "Wyvern", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8bfa4c3d-f412-4707-a48f-e358295e31e1", + "name": "Spawner (2111)", + "location": [5218, 3786, 1], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1315e61d-169b-47e9-9281-395b54cd4fbb", + "name": "Spawner (2111)", + "location": [5218, 3786, 1], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "caf23365-239f-47ea-a5df-e469a64d10f9", + "name": "Spawner (2111)", + "location": [5371, 3820, -2], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "db7d0ea8-885d-460c-95dc-08a5870b6b32", + "name": "Spawner (2111)", + "location": [5371, 3820, -2], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f77b4431-b6bf-47bc-9353-1ed0fa25dc0e", + "name": "Spawner (2111)", + "location": [5368, 3887, 0], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "723998f0-08d5-4124-84d3-df5401888a32", + "name": "Spawner (2111)", + "location": [5368, 3887, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d5989a1c-98df-44fd-8c77-1fecf310a3d2", + "name": "Spawner (2111)", + "location": [5367, 4003, 19], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abe4fbf3-2c4d-4e8d-be6c-d1fda9d7bab6", + "name": "Spawner (2111)", + "location": [5367, 4003, 19], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46191d14-a2b4-4995-b761-4d91195932b0", + "name": "Spawner (2111)", + "location": [5466, 4005, -1], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Rabbit", "maxCount": 20, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 20, "probability": 100 }, + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 }, + { "name": "Hind", "maxCount": 4, "probability": 100 }, + { "name": "Llama", "maxCount": 3, "probability": 100 }, + { "name": "Horse", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e58b638c-9203-4d48-925a-0129f612fae4", + "name": "Spawner (2111)", + "location": [5466, 4005, -1], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Mongbat", "maxCount": 4, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e2ede8e-0dab-4f8e-b6c2-e84626c0acb3", + "name": "Spawner (2111)", + "location": [5194, 3885, -1], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 32, + "walkingRange": 45, + "entries": [ + { "name": "Bull", "maxCount": 10, "probability": 100 }, + { "name": "GreatHart", "maxCount": 6, "probability": 100 }, + { "name": "Cow", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd535de1-dc4b-46a9-9018-feedf4c80edb", + "name": "Spawner (2111)", + "location": [5200, 3895, 2], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6f64b0f6-5882-4551-9a27-ca031288fbae", + "name": "Spawner (2111)", + "location": [5159, 3913, 1], + "map": "Trammel", + "count": 11, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 10, + "entries": [ + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 1, "probability": 100 }, + { "name": "Bull", "maxCount": 1, "probability": 100 }, + { "name": "Chicken", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8df84079-fd67-48ff-85d2-c2aab5b08d89", + "name": "Spawner (2111)", + "location": [5570, 3153, 14], + "map": "Trammel", + "count": 25, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 80, + "entries": [ + { "name": "Gorilla", "maxCount": 6, "probability": 100 }, + { "name": "Panther", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8d068d0-cdb5-4976-a3af-bff4443a1e0c", + "name": "Spawner (2111)", + "location": [5440, 3292, -4], + "map": "Trammel", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 56, + "walkingRange": 62, + "entries": [ + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "Panther", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b7e25a1-2cca-4a59-823e-ec421274efc1", + "name": "Spawner (2111)", + "location": [5566, 3292, 4], + "map": "Trammel", + "count": 21, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 56, + "walkingRange": 62, + "entries": [ + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "Panther", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b4617e7-da4a-42b4-9f4b-7d2e5e1fad7f", + "name": "Spawner (2111)", + "location": [5454, 3136, -60], + "map": "Trammel", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "TerathanMatriarch", "maxCount": 2, "probability": 100 }, + { "name": "TerathanAvenger", "maxCount": 4, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 10, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a02dc119-74bf-4864-92ab-94c29aefc6be", + "name": "Spawner (2111)", + "location": [5490, 3071, 15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c6882bff-77fb-4555-8369-c948185411a3", + "name": "Spawner (2111)", + "location": [5467, 3078, -3], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b606b627-c425-445c-9bc1-0e8e0a502c0d", + "name": "Spawner (2111)", + "location": [5446, 3074, 3], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40332222-33e9-4df1-bf72-15f818f2c5f2", + "name": "Spawner (2111)", + "location": [5432, 3079, -4], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e44775de-291a-43dd-9103-11c3a2063c7c", + "name": "Spawner (2111)", + "location": [5408, 3077, 16], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [ + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 2, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/OrcCaves.json b/Distribution/Data/Spawns/uoml/trammel/OrcCaves.json new file mode 100644 index 000000000..eb56fb589 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/OrcCaves.json @@ -0,0 +1,302 @@ +[ + { + "type": "Spawner", + "guid": "cd4b9397-58f2-42bf-ac91-033ad8396eda", + "name": "Spawner (2112)", + "location": [5146, 1995, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "Orc", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04dd6b85-c84e-49e1-8e6a-fbea6734f30b", + "name": "Spawner (2112)", + "location": [5153, 1971, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "264b83e9-092d-4d4b-9dac-8f717b8a21a6", + "name": "Spawner (2112)", + "location": [5153, 1961, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "DireWolf", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4abbaeaf-4d90-4eef-bca1-55bbc1b0b631", + "name": "Spawner (2112)", + "location": [5144, 1960, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "OrcishLord", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "20658391-8218-48bd-bdab-fd04bbafad31", + "name": "Spawner (2112)", + "location": [5141, 1968, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "OrcCaptain", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c343a444-ed8b-4bc7-9c5f-4ac06bcb18f8", + "name": "Spawner (2112)", + "location": [5332, 1365, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "17e3d0fd-73d1-435c-9d32-ea6716977e80", + "name": "Spawner (2112)", + "location": [5308, 1372, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Noble", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "48b68bf1-d9b1-4fac-af9e-9c51c90fe891", + "name": "Spawner (2112)", + "location": [5302, 1355, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 12, + "entries": [ + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f128f5b2-ca75-465f-87b2-d5bbf8c933cf", + "name": "Spawner (2112)", + "location": [5316, 1332, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "GiantRat", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "02f05175-47b3-49ce-adfd-6838f0bb93a7", + "name": "Spawner (2112)", + "location": [5331, 1346, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "DireWolf", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0076bc53-87a0-493c-9251-8b689c0aac25", + "name": "Spawner (2112)", + "location": [5354, 1332, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebff4db1-6752-4457-b629-109818ec0294", + "name": "Spawner (2112)", + "location": [5356, 1304, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [ + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d6544ae-63b4-4c8b-8be3-7b52b505f7f9", + "name": "Spawner (2112)", + "location": [5317, 1309, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "646f189b-b834-4e7d-9e96-dc8b5d71412d", + "name": "Spawner (2112)", + "location": [5300, 1314, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "434454b5-2093-4125-8b2c-59c57d6bd98b", + "name": "Spawner (2112)", + "location": [5292, 1316, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Corpser", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "def6d26d-288a-4659-9ccc-b7556ba93dfe", + "name": "Spawner (2112)", + "location": [5281, 2029, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Orc", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c13bf083-3691-486a-b5af-7779a398b1f8", + "name": "Spawner (2112)", + "location": [5308, 2005, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [ + { "name": "OrcBrute", "maxCount": 1, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f769344d-9b4e-4573-b61a-5d1e4d66361d", + "name": "Spawner (2112)", + "location": [5310, 1969, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [ + { "name": "OrcBomber", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03496f57-4e3d-4a83-a12c-8ee3523f4efb", + "name": "Spawner (2112)", + "location": [5348, 2011, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 4, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Outdoors.json b/Distribution/Data/Spawns/uoml/trammel/Outdoors.json index f108daddd..8e7c5e2b4 100644 --- a/Distribution/Data/Spawns/uoml/trammel/Outdoors.json +++ b/Distribution/Data/Spawns/uoml/trammel/Outdoors.json @@ -1,6 +1,6 @@ [ { - "$type": "Spawner", + "type": "Spawner", "guid": "949f3b33-3f72-4065-b212-a15495e305d6", "name": "Spawner (2113)", "location": [3619, 2814, 21], @@ -11,12 +11,10 @@ "team": 0, "homeRange": 50, "walkingRange": 50, - "entries": [ - { "name": "HordeMinion", "maxCount": 17, "probability": 100 } - ] + "entries": [{ "name": "HordeMinion", "maxCount": 17, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b4555e77-ee2f-44d0-b85b-d7d6f26612ef", "name": "Spawner (2113)", "location": [3619, 2814, 21], @@ -27,12 +25,401 @@ "team": 0, "homeRange": 50, "walkingRange": 50, + "entries": [{ "name": "Mongbat", "maxCount": 17, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "62ff4954-fb25-4416-8ac1-3120144c4c14", + "name": "Spawner (2113)", + "location": [1147, 3524, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, "entries": [ - { "name": "Mongbat", "maxCount": 17, "probability": 100 } + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "c2c12823-bb40-49bb-93ab-87fec1ebda5f", + "name": "Spawner (2113)", + "location": [1266, 1982, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "566cfa8d-c4be-4e84-9779-19fd29a5c4af", + "name": "Spawner (2113)", + "location": [1249, 2030, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6092a82-aeba-47d2-81a2-e51332742aa4", + "name": "Spawner (2113)", + "location": [1232, 2093, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95bd45a8-6d9d-4663-a009-90bbfdb6c783", + "name": "Spawner (2113)", + "location": [1140, 2087, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fc7f9d6e-d0ce-4879-b674-ba5ed365450a", + "name": "Spawner (2113)", + "location": [1089, 2033, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cf631a13-97b5-4714-87db-23ad2e1a5ed4", + "name": "Spawner (2113)", + "location": [1067, 2117, 5], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "Mongbat", "maxCount": 6, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2b03adb-c492-487f-bdc1-4dc7f70818cd", + "name": "Spawner (2113)", + "location": [666, 1825, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75118095-d436-46b3-8e73-d2cc56c66ce9", + "name": "Spawner (2113)", + "location": [741, 1838, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba037fbd-9398-4438-bee8-e6fce7d07c01", + "name": "Spawner (2113)", + "location": [733, 1937, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ee147b0d-5a5f-4ad0-9d7a-974230bb3f53", + "name": "Spawner (2113)", + "location": [798, 1935, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da8f61e2-5aed-4bb9-a59e-c3c01cf2f4da", + "name": "Spawner (2113)", + "location": [819, 1841, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0042c352-ea0e-4d42-860c-5f3462b43c61", + "name": "Spawner (2113)", + "location": [893, 1861, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb7f5bad-4b07-4c56-bd44-8fb33a088fa5", + "name": "Spawner (2113)", + "location": [879, 1956, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a99fcecd-571e-4151-8c6d-b3d0611e4f5a", + "name": "Spawner (2113)", + "location": [809, 1615, 0], + "map": "Trammel", + "count": 6, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7abf83f6-4989-40d9-8b25-ee6299f59786", + "name": "Spawner (2113)", + "location": [4461, 3106, 0], + "map": "Trammel", + "count": 5, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "OrcishMage", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3e3414ca-ff05-479b-ab44-3dd7f6c46470", + "name": "Spawner (2113)", + "location": [4729, 3485, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12e72748-84ff-4d28-bfbe-39b7d5232ceb", + "name": "Spawner (2113)", + "location": [4519, 3097, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2aa2c25-88b6-44d5-af14-e47c7b2a798a", + "name": "Spawner (2113)", + "location": [4613, 3272, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f62d079f-93a0-412b-a5a2-f088592be535", + "name": "Spawner (2113)", + "location": [4576, 3356, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "792fd336-4cdb-41aa-91f2-ea5a748bf4f3", + "name": "Spawner (2113)", + "location": [4658, 3376, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3366bb0e-f7cf-475b-968a-25da2580d36b", + "name": "Spawner (2113)", + "location": [4607, 3480, 29], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Orc", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "8cd18e64-2c1c-4853-b82e-8842b4b71dcd", "name": "Spawner (2113)", "location": [3485, 2595, 12], @@ -55,7 +442,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "c08f91a3-eb9d-464c-96ce-d70f6c5a8fc4", "name": "Spawner (2113)", "location": [3446, 2741, 49], @@ -78,7 +465,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0111020b-4ccf-45e7-bfbe-8a4f4fbeedad", "name": "Spawner (2113)", "location": [3459, 2655, 9], @@ -101,7 +488,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ed918ae7-13cc-45da-a9ee-166d198bb83f", "name": "Spawner (2113)", "location": [3379, 2693, 35], @@ -124,7 +511,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "63c48500-e80c-4662-a58d-b32b2591f94d", "name": "Spawner (2113)", "location": [3411, 2592, 44], @@ -147,7 +534,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "8dc26593-aecf-4d5c-97c6-d4d11e8411dc", "name": "Spawner (2113)", "location": [3464, 2503, 49], @@ -170,7 +557,8003 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", + "guid": "9b819902-07ef-442c-bff5-d8201a95eb92", + "name": "Spawner (2113)", + "location": [3168, 629, 8], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e72e6b3-e644-4850-8a8d-b5586ac9cf55", + "name": "Spawner (2113)", + "location": [3172, 700, 8], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "583032d0-fa06-4e3c-ba13-1054ca5c022f", + "name": "Spawner (2113)", + "location": [3173, 771, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 5, "probability": 100 }, + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3cfc616f-5f47-49de-88e6-0aea4dcd0fa6", + "name": "Spawner (2113)", + "location": [1473, 229, 22], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9956e808-d58e-42bd-8c6d-3a22c652480d", + "name": "Spawner (2113)", + "location": [1337, 293, 22], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a94c7436-52ec-4820-9c38-88fcada332a9", + "name": "Spawner (2113)", + "location": [1184, 217, 27], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2ad3f8c-d318-42b4-8a19-20a025ee838d", + "name": "Spawner (2113)", + "location": [1284, 433, 6], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f8ff142-0e90-4ee2-bb29-c6960d4dc308", + "name": "Spawner (2113)", + "location": [1203, 502, 30], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a9bdd33d-5beb-4e5f-b2a7-25bbe6de9194", + "name": "Spawner (2113)", + "location": [1300, 607, 15], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d14a61d-ea19-473b-8d74-b57ac8fd7f89", + "name": "Spawner (2113)", + "location": [1200, 633, 6], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4cb1e2e1-c235-4ea9-ac7a-86497d31fe93", + "name": "Spawner (2113)", + "location": [1162, 704, 6], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8791d5da-aabf-4ef8-9e50-bd73ebf233d3", + "name": "Spawner (2113)", + "location": [1130, 830, 9], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b5a9736-d1d3-4492-9674-e786e8ea6b55", + "name": "Spawner (2113)", + "location": [1013, 980, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbc84d48-e44c-404a-9912-3f08fd80c2a8", + "name": "Spawner (2113)", + "location": [848, 917, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62abe17a-ffc3-43f4-b71d-281ff6e875bb", + "name": "Spawner (2113)", + "location": [840, 1063, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "97faf684-9350-46c3-8da6-c3892f7f604d", + "name": "Spawner (2113)", + "location": [1038, 1149, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b518dc40-be46-4410-9296-164bc7f5d6db", + "name": "Spawner (2113)", + "location": [844, 1235, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c9f16f2-a241-42ad-bf26-7fed6e7b0693", + "name": "Spawner (2113)", + "location": [617, 1326, 0], + "map": "Trammel", + "count": 3, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Gazer", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a71a8ce9-b6a6-40b5-ba55-ae04b8940322", + "name": "Spawner (2113)", + "location": [1831, 449, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0bc4302-a584-4339-b0b4-ebd374568aa7", + "name": "Spawner (2113)", + "location": [1832, 517, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "51ec0cce-de67-476d-abb7-7882f5add66e", + "name": "Spawner (2113)", + "location": [1749, 474, 6], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "16d441e6-2418-4ffb-97c8-6c1b05f6d1a4", + "name": "Spawner (2113)", + "location": [1636, 532, 22], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6901fe1c-8be5-4cba-949a-8401a0cb640d", + "name": "Spawner (2113)", + "location": [1743, 589, 16], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1af8afa4-3937-4745-9a39-4fb67f03f640", + "name": "Spawner (2113)", + "location": [1887, 591, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75631a7a-9d4e-4464-9197-1e2ce34369cd", + "name": "Spawner (2113)", + "location": [1810, 647, 0], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32e86bfa-a1f9-4b2e-bf35-9c83335710a9", + "name": "Spawner (2113)", + "location": [1631, 650, 16], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61e4635f-38a1-4bed-9d85-40665f917751", + "name": "Spawner (2113)", + "location": [1723, 683, 6], + "map": "Trammel", + "count": 9, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 60, + "walkingRange": 60, + "entries": [ + { "name": "AirElemental", "maxCount": 1, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d3feafc-3dc2-441d-a9ab-18c468552742", + "name": "Spawner (2113)", + "location": [1980, 832, -1], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9e6dac6-5887-4a6e-a321-7166ad16b575", + "name": "Spawner (2113)", + "location": [1905, 883, -1], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7ea4508-795e-4bb9-97c3-41fe28f1f214", + "name": "Spawner (2113)", + "location": [1849, 953, 7], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9fd2dbc-cb36-489c-8149-b815c16f8d09", + "name": "Spawner (2113)", + "location": [1001, 787, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c710dd46-a5f7-4b86-8a96-a787c08e03cb", + "name": "Spawner (2113)", + "location": [987, 725, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 3, "probability": 100 }, + { "name": "Lich", "maxCount": 3, "probability": 100 }, + { "name": "LichLord", "maxCount": 3, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47544599-de5d-4da5-9fec-857b970a1ce5", + "name": "Spawner (2113)", + "location": [1912, 1283, 0], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "38e273a0-f54c-4f47-a468-379b40ac1fe2", + "name": "Spawner (2113)", + "location": [1787, 1280, 0], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54106890-a301-41d2-99ed-475fa5ebce54", + "name": "Spawner (2113)", + "location": [1799, 1394, 0], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ba259c9-3aba-4651-a0f8-9fb5ad4c203b", + "name": "Spawner (2113)", + "location": [1652, 1400, 0], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbd652ce-e378-4c20-84c3-42788aac59b2", + "name": "Spawner (2113)", + "location": [1755, 1474, 0], + "map": "Trammel", + "count": 4, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Gargoyle", "maxCount": 4, "probability": 100 }, + { "name": "Gazer", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d8ac8b6-9e7c-42d8-8dc5-bc38d61e535b", + "name": "Spawner (2113)", + "location": [1325, 1071, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Troll", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "28aa4986-a824-4960-b415-73d72a3c2c6b", + "name": "Spawner (2113)", + "location": [2369, 3430, 3], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e085923c-489c-4b7b-b9bf-c6a4ca1b60c1", + "name": "Spawner (2113)", + "location": [2354, 3503, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2df0361e-29e1-454f-970b-3e153ff01794", + "name": "Spawner (2113)", + "location": [2503, 3593, 3], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "402644f1-43d6-4f2a-ba97-7f86c9010bed", + "name": "Spawner (2113)", + "location": [2520, 3983, 3], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "056b2cc1-56cd-45e3-bae8-a8749a1ddc06", + "name": "Spawner (2113)", + "location": [2438, 3914, 0], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0758b1c-baa8-4216-989d-000515e5227d", + "name": "Spawner (2113)", + "location": [2143, 3953, 3], + "map": "Trammel", + "count": 10, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 8, "probability": 100 }, + { "name": "Snake", "maxCount": 8, "probability": 100 }, + { "name": "Wisp", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad938586-87a9-4dd7-a5b1-59cd95cd6d46", + "name": "Spawner (2113)", + "location": [868, 488, 0], + "map": "Trammel", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72083877-79f3-49cf-a915-5ffb89bf1e48", + "name": "Spawner (2113)", + "location": [805, 483, 0], + "map": "Trammel", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "066c7e0c-e833-422b-9eab-6ce558dba618", + "name": "Spawner (2113)", + "location": [839, 406, 0], + "map": "Trammel", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a546dd6-47b1-48e5-ba06-a2c594caa9b2", + "name": "Spawner (2113)", + "location": [841, 344, 0], + "map": "Trammel", + "count": 2, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6cc36c7c-cfd0-42f4-9e04-9ff5dbd963c5", + "name": "Spawner (2113)", + "location": [1131, 3415, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf514878-4b14-4e01-acd6-ccff87fe9980", + "name": "Spawner (2113)", + "location": [1158, 3457, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "538691b1-1499-4081-96bd-2c2dd54a4599", + "name": "Spawner (2113)", + "location": [1094, 3482, 0], + "map": "Trammel", + "count": 8, + "minDelay": "01:00:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1b3fe9d8-e194-42e9-8316-aa67a21b887d", + "name": "Spawner (2113)", + "location": [1312, 613, 10], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "837610d0-3ae0-49e0-aa44-a02a4a3e379f", + "name": "Spawner (2113)", + "location": [1214, 617, 13], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a48df719-c919-4066-b8a2-6c679032f463", + "name": "Spawner (2113)", + "location": [1361, 522, 19], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "47abac64-ba65-4d28-8d46-a2b9c6872407", + "name": "Spawner (2113)", + "location": [1226, 449, 1], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "efe392b2-baea-4098-9549-0439983f0e4e", + "name": "Spawner (2113)", + "location": [1276, 431, 16], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4e4aa09e-a831-4f56-b374-8d39f3977e30", + "name": "Spawner (2113)", + "location": [1231, 192, 6], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75b4dbbf-f0d3-4f4e-8148-520aba12abaa", + "name": "Spawner (2113)", + "location": [1143, 180, 14], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d6e79e4-c6c2-456c-9bf3-629536077417", + "name": "Spawner (2113)", + "location": [1191, 288, 11], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73e77785-b460-4d5d-840c-b13c96c1ae54", + "name": "Spawner (2113)", + "location": [1295, 328, 22], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "afde0aad-5648-43b2-b77b-08270970f7b5", + "name": "Spawner (2113)", + "location": [1361, 275, 16], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55959a0a-7dac-4bea-9689-01d82dcb1212", + "name": "Spawner (2113)", + "location": [1491, 238, 22], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc56ca14-97aa-4dc3-99a3-f684078e007e", + "name": "Spawner (2113)", + "location": [3440, 405, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c79e8b6-595f-4914-9372-be6a16e42bf8", + "name": "Spawner (2113)", + "location": [3382, 375, 4], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39d7b2f6-d423-4d6c-8068-985bbd4865ef", + "name": "Spawner (2113)", + "location": [3327, 344, 4], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ab664229-d260-4bfd-9948-f2bb2b433504", + "name": "Spawner (2113)", + "location": [3434, 316, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6aab8758-a4ee-4a8a-bc76-33cdbdd297c0", + "name": "Spawner (2113)", + "location": [3382, 280, 9], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "688f208d-8821-4c70-9b98-28f8594741bd", + "name": "Spawner (2113)", + "location": [3322, 271, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e9214fe-1512-458b-8302-1820c027c565", + "name": "Spawner (2113)", + "location": [3360, 218, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6987ffa4-6e58-48b7-a2a4-12d2eef6b86d", + "name": "Spawner (2113)", + "location": [3390, 259, 4], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "198d8d75-87a7-4c77-abc3-c45fb558825b", + "name": "Spawner (2113)", + "location": [3446, 213, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54e24a3b-cd43-4265-9f30-d73326dd45a9", + "name": "Spawner (2113)", + "location": [3462, 136, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Ogre", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b84a2533-eb93-46ba-8053-dc0c04146704", + "name": "Spawner (2113)", + "location": [2589, 204, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "457024f7-fdc7-4080-858e-b578a99cc7d5", + "name": "Spawner (2113)", + "location": [2549, 136, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa7aa472-6b9e-4cc5-b138-720d5d413925", + "name": "Spawner (2113)", + "location": [2659, 147, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c7570b9-067e-4887-8309-b653be55bd69", + "name": "Spawner (2113)", + "location": [2621, 99, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2acf5f1c-d200-4621-9cbf-9d013a9e553b", + "name": "Spawner (2113)", + "location": [2549, 86, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "543e6d36-efb0-4382-b526-b615fd7d0cbf", + "name": "Spawner (2113)", + "location": [2478, 100, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4feab07-a874-4ac6-a248-7a292a021674", + "name": "Spawner (2113)", + "location": [2468, 144, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10f682e2-a5bd-42f8-b228-ae66d1a9a4c5", + "name": "Spawner (2113)", + "location": [2485, 191, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "748028eb-c0ef-4c6f-bbb9-e43359901099", + "name": "Spawner (2113)", + "location": [2454, 248, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0efa850b-b21d-4835-a4ff-08015747427a", + "name": "Spawner (2113)", + "location": [2533, 240, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37b5c086-ef17-49b8-ad0b-125ec80df443", + "name": "Spawner (2113)", + "location": [2077, 434, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1c9e3733-5669-4f75-85fb-0d024e01cdb5", + "name": "Spawner (2113)", + "location": [2080, 400, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e4c86bb-8737-41c8-924c-f226b9aedfde", + "name": "Spawner (2113)", + "location": [2107, 358, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d3e5300-d499-4a9b-b4e0-f6daa25ac14b", + "name": "Spawner (2113)", + "location": [2138, 323, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5436a84f-0593-4be2-bead-26e7960898a7", + "name": "Spawner (2113)", + "location": [2189, 298, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e52d805-c99f-49b2-8a7a-6c5e4e33038a", + "name": "Spawner (2113)", + "location": [2137, 271, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f05dbd6-1d96-4f1c-ad4e-7808de1dcbe0", + "name": "Spawner (2113)", + "location": [2113, 221, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ea552d7-eb71-4a77-9508-fd3e69b4870a", + "name": "Spawner (2113)", + "location": [2146, 178, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f66d1ea-1745-45de-bbd3-bbf8032b53bc", + "name": "Spawner (2113)", + "location": [2093, 147, 3], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28cd7bd8-2a62-421d-8a48-3409ae908a33", + "name": "Spawner (2113)", + "location": [2109, 109, 3], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3cc3559-e015-4904-b37e-d464bcd6d7ef", + "name": "Spawner (2113)", + "location": [2115, 48, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "Ettin", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d739d7a9-b14c-49ed-ad4d-6765d520bec5", + "name": "Spawner (2113)", + "location": [1996, 73, 8], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cc87ef42-d3a3-4a6b-8d44-ac0d38d75739", + "name": "Spawner (2113)", + "location": [1956, 48, 4], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75764c8f-2910-43c9-9fde-d03748e5361e", + "name": "Spawner (2113)", + "location": [1944, 102, 4], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 5, "probability": 100 }, + { "name": "Gazer", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "387b8a0f-f657-418e-a8a4-0713e3bf18bb", + "name": "Spawner (2113)", + "location": [2034, 46, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41cb561e-ed07-4766-a993-ea37d3480d8b", + "name": "Spawner (2113)", + "location": [2033, 74, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "249b097d-dd14-4d6b-9ddf-f29eea89a011", + "name": "Spawner (2113)", + "location": [2058, 87, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61b07a04-4d35-4af2-9fef-2575e7aa81f6", + "name": "Spawner (2113)", + "location": [2034, 108, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bed7e28-21ce-4afc-b05e-ab91bc2a9b0a", + "name": "Spawner (2113)", + "location": [2000, 141, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40d4b2f8-9cb0-4b8f-b46e-d460c0aba610", + "name": "Spawner (2113)", + "location": [1965, 160, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "847e76b7-5a08-438c-80a3-56285b0f4696", + "name": "Spawner (2113)", + "location": [1946, 186, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b2274f08-2f99-4bed-b485-e6dd3228564f", + "name": "Spawner (2113)", + "location": [1922, 173, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "056a5d12-e5e6-4906-9fe1-5e8fb605cfb7", + "name": "Spawner (2113)", + "location": [1894, 197, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0eab19f2-96ae-41b9-a0d7-5dc82506af6a", + "name": "Spawner (2113)", + "location": [1908, 214, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "483c1680-2f1b-4e6f-9d15-2665c8ac264f", + "name": "Spawner (2113)", + "location": [1891, 226, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4cc2ab2c-5acf-4063-92e1-db91e44a9870", + "name": "Spawner (2113)", + "location": [1869, 265, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 2, "probability": 100 }, + { "name": "Troll", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91340fd5-c54d-4502-9253-adaf4569c7a3", + "name": "Spawner (2113)", + "location": [1626, 266, 16], + "map": "Trammel", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c76cdb5d-42cb-4ab4-a7ec-6db8582f8f14", + "name": "Spawner (2113)", + "location": [1682, 252, 6], + "map": "Trammel", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebbc9fcf-7284-4d3c-9d01-045bf8f31a5d", + "name": "Spawner (2113)", + "location": [1723, 252, 16], + "map": "Trammel", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6eefb00-5655-4f27-9d1b-9baa28ab2ffc", + "name": "Spawner (2113)", + "location": [1759, 259, 21], + "map": "Trammel", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd5e98b7-cddd-4087-b1f4-c28350797537", + "name": "Spawner (2113)", + "location": [1804, 247, 0], + "map": "Trammel", + "count": 13, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "DireWolf", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 11, "probability": 100 }, + { "name": "Gazer", "maxCount": 11, "probability": 100 }, + { "name": "Harpy", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03e8ed27-82da-4ecf-8d02-126f4b8bc35d", + "name": "Spawner (2113)", + "location": [1875, 906, 7], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ab5fd7a4-8a79-4ee4-8369-b9ed647a5fe6", + "name": "Spawner (2113)", + "location": [1829, 874, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b950c826-32fc-4800-92c3-04292c5ab38b", + "name": "Spawner (2113)", + "location": [1852, 841, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8c9dac4-eb05-4d17-9741-c566db9f045f", + "name": "Spawner (2113)", + "location": [1930, 864, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4cc9345c-fe2a-47b7-ab4c-bf9097b047d6", + "name": "Spawner (2113)", + "location": [1883, 827, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fd0a082-52c5-460c-b284-52893bddcc32", + "name": "Spawner (2113)", + "location": [1949, 824, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9737161-8220-4189-ade1-d48a07a04d7a", + "name": "Spawner (2113)", + "location": [1987, 822, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2bac5389-5a5b-45a2-bcad-03e258bcbf5c", + "name": "Spawner (2113)", + "location": [2013, 839, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a559cad1-a142-4969-bef2-8e154d658ec6", + "name": "Spawner (2113)", + "location": [1979, 885, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "11dcec12-bab6-4595-aa20-ef64eb749c0e", + "name": "Spawner (2113)", + "location": [1947, 912, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bb0725a-0be8-4432-9256-9d4d41d7c168", + "name": "Spawner (2113)", + "location": [1915, 928, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba9ff3a4-d363-4fac-b113-bab612b9614f", + "name": "Spawner (2113)", + "location": [1866, 938, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0ed841ed-3e0c-4c9a-aef2-1952720a260b", + "name": "Spawner (2113)", + "location": [1838, 957, -1], + "map": "Trammel", + "count": 2, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 2, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61a0bee6-034f-4e72-851a-0069ea03c769", + "name": "Spawner (2113)", + "location": [1802, 1506, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7bf87e5-4f5b-4b8c-9710-4e4ab3343720", + "name": "Spawner (2113)", + "location": [1692, 1463, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4eedbefe-4446-443f-a5be-6b1c56c842cb", + "name": "Spawner (2113)", + "location": [1833, 1245, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "30b6c30f-49a6-4cc7-ae52-78107281393a", + "name": "Spawner (2113)", + "location": [1726, 1362, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "372bf2eb-9738-4465-8da3-64c980dfc01e", + "name": "Spawner (2113)", + "location": [1640, 1431, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "80b26b9b-afc9-40a9-b6e7-47e04586711e", + "name": "Spawner (2113)", + "location": [1912, 1302, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6ffb380-e7b3-4b1c-a89e-49a68626b151", + "name": "Spawner (2113)", + "location": [1967, 1296, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebb84eb3-cfb1-4925-9e17-ea98ad5df79e", + "name": "Spawner (2113)", + "location": [1898, 1265, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "17b36c45-5c86-4ede-8d59-8c9031e72a79", + "name": "Spawner (2113)", + "location": [1872, 1347, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "476be46e-7393-4617-afc5-7d03ce670c16", + "name": "Spawner (2113)", + "location": [1812, 1314, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1e6337c-a7e4-4011-a777-d5e32ab30ace", + "name": "Spawner (2113)", + "location": [1797, 1393, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c2aba6ad-8726-41c2-92bb-73f1176d0422", + "name": "Spawner (2113)", + "location": [1862, 1434, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "17f64e17-61cb-4e36-aaa9-8f0f94218781", + "name": "Spawner (2113)", + "location": [1877, 1515, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 7, "probability": 100 }, + { "name": "DireWolf", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Reaper", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8103dd4b-9100-4c4b-bcb1-4b0b5bf054f8", + "name": "Spawner (2113)", + "location": [1361, 1392, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9bfb3456-0e22-429d-bf41-8b597f0c8170", + "name": "Spawner (2113)", + "location": [1445, 1365, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "467e3ecc-401c-48d9-b064-f260e6891b6f", + "name": "Spawner (2113)", + "location": [1375, 1330, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c656301-ba27-4061-b13c-67e32ca73351", + "name": "Spawner (2113)", + "location": [1474, 1297, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68c72e6d-a7fd-431c-8685-37f47afc4b0b", + "name": "Spawner (2113)", + "location": [1407, 1281, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4bc9831-73ab-4b9b-a6b9-21a02bf60b8c", + "name": "Spawner (2113)", + "location": [1225, 1477, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7bb510ff-11c6-4ed6-88b2-28786db5e331", + "name": "Spawner (2113)", + "location": [1222, 1418, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e2c18bc-c2c6-4a16-bda2-1d4bd4d5574d", + "name": "Spawner (2113)", + "location": [1244, 1374, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c113386f-3b9c-40bf-a0bd-a45027fc8201", + "name": "Spawner (2113)", + "location": [1180, 1354, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d80fc757-5a54-44c3-9700-ad91a1cee771", + "name": "Spawner (2113)", + "location": [1240, 1317, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5b60719-c3a1-4169-8070-a3d98da16df3", + "name": "Spawner (2113)", + "location": [1340, 1268, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "acf731fa-53ee-48da-b0ef-356f9d61c38f", + "name": "Spawner (2113)", + "location": [1305, 1326, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08534eb5-4011-42d7-9006-169b3d002ca1", + "name": "Spawner (2113)", + "location": [1304, 1413, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "83e06193-d371-4d06-93b6-9a4147fddee3", + "name": "Spawner (2113)", + "location": [1300, 1481, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Lizardman", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10e3df7f-ffc8-4bd7-a216-0689f4decabc", + "name": "Spawner (2113)", + "location": [1541, 1895, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c00f933-5883-4c21-aac6-0e9cb9565b56", + "name": "Spawner (2113)", + "location": [1534, 1941, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f430ad0e-4341-455b-9f4e-230f9f461670", + "name": "Spawner (2113)", + "location": [1475, 1958, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0ada6680-a6f8-4468-a941-912dbef13276", + "name": "Spawner (2113)", + "location": [1435, 1917, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c33edf4-3f59-4b73-bfee-9d57b2a4e7be", + "name": "Spawner (2113)", + "location": [1383, 1949, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4245c498-a5a6-436c-91db-68989f925ce7", + "name": "Spawner (2113)", + "location": [1416, 1992, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dc3563c-c729-4217-b57b-535caa81fdb2", + "name": "Spawner (2113)", + "location": [1504, 2007, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "528890d0-376a-43c3-a832-69efcd0d56c1", + "name": "Spawner (2113)", + "location": [1447, 2038, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ad1ac6d-989f-4590-8d0e-0b1894c5c216", + "name": "Spawner (2113)", + "location": [1479, 2094, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "729cb568-ac02-4737-b9e7-4abd87c5f66a", + "name": "Spawner (2113)", + "location": [1525, 2053, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 5, "probability": 100 }, + { "name": "Mongbat", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Troll", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ce82610-b4ea-41c1-abc5-e2bbce8dfa46", + "name": "Spawner (2113)", + "location": [2095, 2352, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "668b691e-8bdc-4002-93b8-ccc8d3ef1882", + "name": "Spawner (2113)", + "location": [2077, 2305, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1365e61d-0ffa-43f0-b926-45316a2a8048", + "name": "Spawner (2113)", + "location": [2021, 2313, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8792576e-488e-47bc-a672-01aee138d62e", + "name": "Spawner (2113)", + "location": [1968, 2346, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d941b3f-588d-49fe-a879-d50c6005b8c9", + "name": "Spawner (2113)", + "location": [1947, 2294, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f8dcb56-8c3f-49fd-b778-75d243d770c6", + "name": "Spawner (2113)", + "location": [1915, 2385, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6cf68fd8-cc90-4d19-b6cd-cc14fddfa5b9", + "name": "Spawner (2113)", + "location": [1891, 2331, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "08b4da27-33a7-4cfa-be11-f0eca92c6aa2", + "name": "Spawner (2113)", + "location": [1890, 2270, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68d63e2a-4dc7-49a8-9876-f6bbe9704568", + "name": "Spawner (2113)", + "location": [1873, 2426, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "313bfee9-788e-4860-8506-48c6e3a2c9b9", + "name": "Spawner (2113)", + "location": [1837, 2334, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15f17b62-41e8-4db8-a62f-eb04db9c7606", + "name": "Spawner (2113)", + "location": [1826, 2250, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 6, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 6, "probability": 100 }, + { "name": "Lizardman", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 }, + { "name": "Slime", "maxCount": 6, "probability": 100 }, + { "name": "Snake", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "482755b3-dd05-4836-8a75-6f71edc3098f", + "name": "Spawner (2113)", + "location": [2109, 2989, 30], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c1c9bf1-535e-4e13-a6ab-714d3439cd95", + "name": "Spawner (2113)", + "location": [2089, 3017, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "769fdfa1-dd5c-4b03-89a2-2e86fb41888f", + "name": "Spawner (2113)", + "location": [2074, 3069, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "68ef188a-6290-4a22-a06b-089287e867ad", + "name": "Spawner (2113)", + "location": [2033, 3041, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7b2a0a94-8536-4656-8b41-311715aabe6a", + "name": "Spawner (2113)", + "location": [1970, 3052, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0dea7015-1783-4b93-9ded-b5b52be021cb", + "name": "Spawner (2113)", + "location": [1915, 3050, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bd439c3-5ce9-4971-a8a8-45a432089787", + "name": "Spawner (2113)", + "location": [1866, 3040, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b7f54d4-bf98-4c1c-9c79-c0b6b6fe0082", + "name": "Spawner (2113)", + "location": [1812, 3036, 5], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5df49ed-42f4-4533-bd84-0c6d38a134a6", + "name": "Spawner (2113)", + "location": [1841, 3000, 10], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6b3ea5f3-1cf1-4b1f-85e5-97deb2d2741b", + "name": "Spawner (2113)", + "location": [1803, 2981, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5da854a2-bca1-46e3-88fc-98d5dd640339", + "name": "Spawner (2113)", + "location": [1867, 2962, 7], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a5677568-fa43-430c-9f73-0ed52edd08c9", + "name": "Spawner (2113)", + "location": [1849, 2915, 30], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9ac79ca6-546f-4634-8048-5a9c59b662c2", + "name": "Spawner (2113)", + "location": [1911, 2934, 10], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a40f806a-f922-415f-b88f-63dde478379b", + "name": "Spawner (2113)", + "location": [1954, 2903, 30], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Orc", "maxCount": 4, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "52640d62-3b31-4a7a-819a-518a6f3fae3e", + "name": "Spawner (2113)", + "location": [851, 495, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd06b247-c98e-4218-93b9-b96ff85ba165", + "name": "Spawner (2113)", + "location": [805, 463, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03369351-f3c2-4651-907d-2e9ec6fcfb6b", + "name": "Spawner (2113)", + "location": [837, 361, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 8, "probability": 100 }, + { "name": "DireWolf", "maxCount": 8, "probability": 100 }, + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Harpy", "maxCount": 8, "probability": 100 }, + { "name": "Lizardman", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Reaper", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad8627b0-4cf5-4650-96aa-7bbc32e9e18b", + "name": "Spawner (2113)", + "location": [1029, 1969, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5175dc34-0726-4304-bab5-bc9533355114", + "name": "Spawner (2113)", + "location": [1080, 1963, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d0cd619-9e1a-4d4e-a3d1-2af50eea591e", + "name": "Spawner (2113)", + "location": [1042, 1920, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Spectre", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36d83036-0fe8-4652-aecd-8f1dbe8a1388", + "name": "Spawner (2113)", + "location": [821, 1938, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73a9fc66-3b63-4478-a41b-07df4bc6be50", + "name": "Spawner (2113)", + "location": [736, 1938, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a5702d5-741d-4142-876f-f5446948c3ce", + "name": "Spawner (2113)", + "location": [899, 1851, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f608b229-cfdb-4397-9908-249713f11b1f", + "name": "Spawner (2113)", + "location": [759, 1828, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df7d496c-92b3-487a-a49c-92ef77818fd0", + "name": "Spawner (2113)", + "location": [672, 1825, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d425d30-7a90-46b3-99ee-1be6e09e2ea4", + "name": "Spawner (2113)", + "location": [892, 1963, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ghoul", "maxCount": 7, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 7, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a93c7a8-af80-45e5-8f20-920eb51c09ff", + "name": "Spawner (2113)", + "location": [815, 1603, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "Ratman", "maxCount": 5, "probability": 100 }, + { "name": "Spectre", "maxCount": 5, "probability": 100 }, + { "name": "Zombie", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfca73f2-a84f-402f-b50f-52b3fa851d6e", + "name": "Spawner (2113)", + "location": [1055, 1442, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec76be04-eeb8-4466-9328-008c0a24ded9", + "name": "Spawner (2113)", + "location": [1021, 1415, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81e040fc-9b61-42ca-acfb-5d96eadca6a4", + "name": "Spawner (2113)", + "location": [1060, 1375, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73b21795-cf2b-49a5-9ee8-9e25a136cb0b", + "name": "Spawner (2113)", + "location": [1004, 1369, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "24d72ad1-069e-480c-93db-58654afde815", + "name": "Spawner (2113)", + "location": [1037, 1325, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1aebb4f5-9b3c-42b8-bb18-77dc7b26b98f", + "name": "Spawner (2113)", + "location": [971, 1308, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 6, "probability": 100 }, + { "name": "Gazer", "maxCount": 6, "probability": 100 }, + { "name": "Harpy", "maxCount": 6, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 6, "probability": 100 }, + { "name": "Ogre", "maxCount": 6, "probability": 100 }, + { "name": "Troll", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe495dbf-3e33-4998-a844-3fdba286070b", + "name": "Spawner (2113)", + "location": [1000, 330, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "577bcfed-a871-4aa1-b989-189a320eba2f", + "name": "Spawner (2113)", + "location": [964, 365, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4848785f-07e3-4e86-b62a-916774bf18e1", + "name": "Spawner (2113)", + "location": [947, 418, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8642c7b8-1bbe-4e19-a85a-29df23ca9944", + "name": "Spawner (2113)", + "location": [956, 452, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 10, "probability": 100 }, + { "name": "DireWolf", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 10, "probability": 100 }, + { "name": "Gazer", "maxCount": 10, "probability": 100 }, + { "name": "Harpy", "maxCount": 10, "probability": 100 }, + { "name": "Lizardman", "maxCount": 10, "probability": 100 }, + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3947c59c-9e6f-44fb-938f-d1770f47b622", + "name": "Spawner (2113)", + "location": [423, 1595, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4d67563-65b3-45b4-9cb9-02ecaf27d37e", + "name": "Spawner (2113)", + "location": [418, 1665, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "799df1e3-5107-4858-8b7c-2e271b9756f1", + "name": "Spawner (2113)", + "location": [480, 1641, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "697858f2-61ad-43d6-8cbb-97a5b872d8f4", + "name": "Spawner (2113)", + "location": [516, 1585, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1707f1c-a8ae-4c20-9ae8-a8efea2f92dd", + "name": "Spawner (2113)", + "location": [541, 1645, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 5, "probability": 100 }, + { "name": "DireWolf", "maxCount": 5, "probability": 100 }, + { "name": "Harpy", "maxCount": 5, "probability": 100 }, + { "name": "Reaper", "maxCount": 5, "probability": 100 }, + { "name": "Skeleton", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36b8e938-4f69-4df8-a64b-6bb43c48bbb8", + "name": "Spawner (2113)", + "location": [141, 1367, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78613e32-9f25-4c0d-a566-f63f4b57607d", + "name": "Spawner (2113)", + "location": [189, 1344, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d4054a1d-96e5-47dc-8d44-853d05e4d9c7", + "name": "Spawner (2113)", + "location": [232, 1350, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2691312-c437-4d3b-bb69-8e2a26a432cf", + "name": "Spawner (2113)", + "location": [278, 1355, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c2beb8c-e9fd-4484-9395-d13dcec9502b", + "name": "Spawner (2113)", + "location": [229, 1419, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "087f6b77-2393-4dac-8543-50a14a7ae6f1", + "name": "Spawner (2113)", + "location": [182, 1428, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "66634507-bcd9-4a0a-b5c9-5b6dd0c9adc9", + "name": "Spawner (2113)", + "location": [157, 1487, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5219997-2933-414d-b9c1-8a38c7d7169a", + "name": "Spawner (2113)", + "location": [204, 1483, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c133b0ca-429c-4f25-909f-36ce88862d76", + "name": "Spawner (2113)", + "location": [240, 1443, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1cab8e17-97d9-4873-8f47-b79eb03e8038", + "name": "Spawner (2113)", + "location": [269, 1474, 0], + "map": "Trammel", + "count": 11, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 }, + { "name": "Corpser", "maxCount": 9, "probability": 100 }, + { "name": "DireWolf", "maxCount": 9, "probability": 100 }, + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Gazer", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Reaper", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ffa2abe9-90a6-4b35-ae74-538bd30c2419", + "name": "Spawner (2113)", + "location": [4057, 300, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "67a947e4-5f60-4065-9469-43a878cb1bfc", + "name": "Spawner (2113)", + "location": [4013, 239, 1], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f5d51f12-ef63-4187-ad60-00f961a67f30", + "name": "Spawner (2113)", + "location": [3929, 235, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b48075d3-37f2-4471-bd77-272fd0de644b", + "name": "Spawner (2113)", + "location": [3920, 287, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "66594f81-5b6b-4cfc-ab1c-48805b3ca936", + "name": "Spawner (2113)", + "location": [4090, 325, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "620249c5-7ec3-4f73-bab8-52dd3f89d0c0", + "name": "Spawner (2113)", + "location": [4162, 395, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49c9739c-a70f-4687-859d-56e5f72d59dc", + "name": "Spawner (2113)", + "location": [4206, 471, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cbbe09b8-6b83-4d65-90b7-985dfa8335ad", + "name": "Spawner (2113)", + "location": [3928, 393, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "193a49e9-ea5c-40f5-8118-d7f369008887", + "name": "Spawner (2113)", + "location": [3963, 508, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ff6852c-8cd0-434b-9eb8-9afd5940805c", + "name": "Spawner (2113)", + "location": [4066, 569, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "PolarBear", "maxCount": 8, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 8, "probability": 100 }, + { "name": "Walrus", "maxCount": 8, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2dfe455c-76ff-4ce4-84dc-46d1c84ad63f", + "name": "Spawner (2113)", + "location": [4153, 601, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "640d4b0e-9a86-4035-8e71-c37fadb6a717", + "name": "Spawner (2113)", + "location": [4214, 655, 1], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a33ee66a-277f-492a-940d-51c2654e1804", + "name": "Spawner (2113)", + "location": [4264, 725, 1], + "map": "Trammel", + "count": 4, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "PolarBear", "maxCount": 4, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 4, "probability": 100 }, + { "name": "Walrus", "maxCount": 4, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba2ae76c-dcca-4df2-9274-fa8136a61efb", + "name": "Spawner (2113)", + "location": [2963, 3617, 3], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6c232b6-7b8e-41b4-b958-3aa5080efaa4", + "name": "Spawner (2113)", + "location": [2966, 3591, 11], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "48328575-a500-4ebc-afcd-de93ed7209bc", + "name": "Spawner (2113)", + "location": [3013, 3591, 15], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e53312f-8367-4864-b655-db9b4b3b652d", + "name": "Spawner (2113)", + "location": [3024, 3566, 15], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d61a295a-c615-4af8-a057-bf82c9c753c7", + "name": "Spawner (2113)", + "location": [3042, 3590, 5], + "map": "Trammel", + "count": 7, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Alligator", "maxCount": 7, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Lizardman", "maxCount": 7, "probability": 100 }, + { "name": "Mongbat", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "SilverSerpent", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b487a09f-6222-4251-a712-839233ccf85d", + "name": "Spawner (2113)", + "location": [2462, 725, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12e896ea-da73-4645-a010-70d1cf7145c8", + "name": "Spawner (2113)", + "location": [2428, 722, 8], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e252043e-aff1-4bea-b8bc-32e668d5dd59", + "name": "Spawner (2113)", + "location": [2375, 725, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d65bd05-bec9-44cc-8027-564ab2597d52", + "name": "Spawner (2113)", + "location": [2345, 676, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2310c1b9-bc6e-4752-be08-751194837081", + "name": "Spawner (2113)", + "location": [2429, 690, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f952e99-08cc-47bb-a84e-b814533856ba", + "name": "Spawner (2113)", + "location": [2379, 654, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03d53510-89c6-400b-84cc-058b3a747a9f", + "name": "Spawner (2113)", + "location": [2326, 656, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e62f1551-be29-4561-9de2-8029f7d167ea", + "name": "Spawner (2113)", + "location": [2319, 591, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 8, "probability": 100 }, + { "name": "Ghoul", "maxCount": 8, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 8, "probability": 100 }, + { "name": "Ogre", "maxCount": 8, "probability": 100 }, + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 8, "probability": 100 }, + { "name": "Skeleton", "maxCount": 8, "probability": 100 }, + { "name": "Troll", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5127a1ad-b427-472b-aa36-8239cdf04b15", + "name": "Spawner (2113)", + "location": [659, 1318, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "934bc4f3-d414-4cb1-8809-cdceca368616", + "name": "Spawner (2113)", + "location": [579, 1305, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8a9dfe2-9620-4fe5-8d99-d1533d2f7da9", + "name": "Spawner (2113)", + "location": [728, 1242, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6349924-e5bd-4073-9b11-4836e15f9f91", + "name": "Spawner (2113)", + "location": [793, 1088, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "889af6a9-6809-4fdf-a28b-cc38170df5de", + "name": "Spawner (2113)", + "location": [849, 1262, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f4ca25be-16e8-4b4a-acc1-b87e77f4953e", + "name": "Spawner (2113)", + "location": [909, 1175, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad79f7ea-b8bd-4821-8b80-d5d4fbe72c24", + "name": "Spawner (2113)", + "location": [974, 1186, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15701d78-3752-44f6-b158-ddca2746390e", + "name": "Spawner (2113)", + "location": [1059, 1166, 5], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "afd3e919-f63c-437a-bd36-b3431b394683", + "name": "Spawner (2113)", + "location": [823, 960, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2b1d17be-9b13-46ea-88ff-89d1c0ddfa18", + "name": "Spawner (2113)", + "location": [960, 910, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ecfe434-8acd-4f36-b873-626fd9337937", + "name": "Spawner (2113)", + "location": [1042, 1024, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81bfcb26-842e-4e50-91c2-572c3aee4bee", + "name": "Spawner (2113)", + "location": [1147, 1048, 5], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9cb6e3ec-d48c-416e-9e5e-4ce284e350b8", + "name": "Spawner (2113)", + "location": [1097, 970, 2], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a9a67290-a828-48fa-a3f7-a0b7f10fbd45", + "name": "Spawner (2113)", + "location": [1192, 958, 5], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc6d7ab6-b963-4216-85bd-99e6a7234b5d", + "name": "Spawner (2113)", + "location": [1166, 818, 5], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3415104e-2a61-4225-9d64-be6609276576", + "name": "Spawner (2113)", + "location": [1083, 783, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5fa9a115-ce2a-4213-b48d-c9744e848d12", + "name": "Spawner (2113)", + "location": [1268, 774, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fcd562e-e69c-4792-9375-7df489650310", + "name": "Spawner (2113)", + "location": [1180, 737, 1], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e9ef0197-6591-49f6-ae85-2a94712d64a6", + "name": "Spawner (2113)", + "location": [1122, 718, 1], + "map": "Trammel", + "count": 12, + "minDelay": "00:20:00", + "maxDelay": "00:20:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Corpser", "maxCount": 11, "probability": 100 }, + { "name": "Ettin", "maxCount": 11, "probability": 100 }, + { "name": "Ghoul", "maxCount": 11, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 11, "probability": 100 }, + { "name": "Lizardman", "maxCount": 11, "probability": 100 }, + { "name": "Ogre", "maxCount": 11, "probability": 100 }, + { "name": "Orc", "maxCount": 11, "probability": 100 }, + { "name": "Reaper", "maxCount": 11, "probability": 100 }, + { "name": "Skeleton", "maxCount": 11, "probability": 100 }, + { "name": "Troll", "maxCount": 11, "probability": 100 }, + { "name": "Zombie", "maxCount": 11, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6357f444-0d29-4154-8256-71dde67c0337", + "name": "Spawner (2113)", + "location": [423, 1595, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ab6b426-1743-4857-9784-498e4156521c", + "name": "Spawner (2113)", + "location": [418, 1665, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "24cd546e-c1a8-4e63-9a2f-a10e04391e45", + "name": "Spawner (2113)", + "location": [480, 1641, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "26e6a133-1816-47f1-af9a-c65e04e37cf8", + "name": "Spawner (2113)", + "location": [516, 1585, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a7de24f-2da7-4229-8f6f-fbaf1adcecdd", + "name": "Spawner (2113)", + "location": [541, 1645, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 7, "probability": 100 }, + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e263026b-c626-4692-9a46-67aafc5bc4c0", + "name": "Spawner (2113)", + "location": [4504, 1458, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aa2759b8-efff-4868-97d0-674c377f63f3", + "name": "Spawner (2113)", + "location": [4494, 1426, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50c40fb1-1e3d-4d2e-9d95-6bfd7d57fa3f", + "name": "Spawner (2113)", + "location": [4455, 1434, 10], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "13ea30a4-a229-4926-8cc6-45d85d3680e2", + "name": "Spawner (2113)", + "location": [4456, 1405, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1fd94ac8-c5bb-4bcf-8ce9-511bd67357ce", + "name": "Spawner (2113)", + "location": [4455, 1358, 10], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "TimberWolf", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "27db6adc-96c4-4afd-987f-269fccd23635", + "name": "Spawner (2113)", + "location": [4550, 1409, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1c19f0de-4a64-471b-b656-9eb3d7a7d09e", + "name": "Spawner (2113)", + "location": [4552, 1447, 10], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d05dae7-686d-4ca8-82c2-3b67c57adc30", + "name": "Spawner (2113)", + "location": [4615, 1469, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4925ff65-5ca5-4f85-9d9b-6a228d778618", + "name": "Spawner (2113)", + "location": [4612, 1418, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49369990-5bfb-4c5a-b64a-2dddbb892391", + "name": "Spawner (2113)", + "location": [4605, 1365, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94502e42-4b90-4a37-8c20-6a8988b7857b", + "name": "Spawner (2113)", + "location": [4566, 1359, 8], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e7ba00e8-d827-4435-8455-690aa3f2482d", + "name": "Spawner (2113)", + "location": [2372, 210, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c517cdb-1ddc-43fc-a8f1-2929874413a1", + "name": "Spawner (2113)", + "location": [2355, 179, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "57956924-7db0-48b3-b993-d4284974093a", + "name": "Spawner (2113)", + "location": [2351, 148, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37715bfb-2a0c-45ac-b416-45a3c3302b42", + "name": "Spawner (2113)", + "location": [2345, 112, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10cf4b5b-9477-402b-ac6c-57245b73756c", + "name": "Spawner (2113)", + "location": [2328, 67, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 9, "probability": 100 }, + { "name": "Harpy", "maxCount": 9, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 9, "probability": 100 }, + { "name": "Lizardman", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 9, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "Skeleton", "maxCount": 9, "probability": 100 }, + { "name": "Troll", "maxCount": 9, "probability": 100 }, + { "name": "Zombie", "maxCount": 9, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa392213-96a7-4165-8c45-f5a96988b74e", + "name": "Spawner (2113)", + "location": [2177, 3034, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9258088-60b5-4ded-a22a-280d4eee8573", + "name": "Spawner (2113)", + "location": [2143, 3068, 20], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Ratman", "maxCount": 7, "probability": 100 }, + { "name": "Skeleton", "maxCount": 7, "probability": 100 }, + { "name": "Spectre", "maxCount": 7, "probability": 100 }, + { "name": "Zombie", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6681af59-d2bd-46ee-96f7-15f9e48cf967", + "name": "Spawner (2113)", + "location": [1983, 1517, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fd5313b-815f-4ec8-96e1-4ceb46497e78", + "name": "Spawner (2113)", + "location": [1986, 1463, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "49b84769-1509-4e93-8bfb-6f952dc6ba84", + "name": "Spawner (2113)", + "location": [1969, 1409, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ettin", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Gazer", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Troll", "maxCount": 7, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7b163acc-acd7-462a-a048-8f9a4ffc5cb8", + "name": "Spawner (2113)", + "location": [1912, 1302, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84124f00-8aa4-491d-a4d9-53e65b98036f", + "name": "Spawner (2113)", + "location": [1967, 1296, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e97ce87e-cb97-493c-a692-d48ebf51a4a3", + "name": "Spawner (2113)", + "location": [1898, 1265, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "793c4766-d5b6-42f0-83ff-d0a267c26846", + "name": "Spawner (2113)", + "location": [1872, 1347, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d77d3fc0-43bc-47da-9a91-5c0f82f285aa", + "name": "Spawner (2113)", + "location": [1812, 1314, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c673f9b8-1ae1-4de9-8d6f-b907c7b758a1", + "name": "Spawner (2113)", + "location": [1797, 1393, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c8129ab7-03a2-498f-b96e-cd45b8913271", + "name": "Spawner (2113)", + "location": [1862, 1434, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "563ed9d1-69ea-4407-8bcb-db6cdf69ca81", + "name": "Spawner (2113)", + "location": [1877, 1515, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8829fbf2-d914-48fc-bdf4-7286f71acb4c", + "name": "Spawner (2113)", + "location": [1802, 1506, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f8d690f4-c3ea-4da8-973b-7e442dd4fc2d", + "name": "Spawner (2113)", + "location": [1692, 1463, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02eea69a-a67a-4244-b57d-d32b4d80510c", + "name": "Spawner (2113)", + "location": [1833, 1245, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6701bc25-8a58-4dc8-aba0-5be8467a0c96", + "name": "Spawner (2113)", + "location": [1726, 1362, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f87b3cc-3bec-4233-a193-faf5d766c8b1", + "name": "Spawner (2113)", + "location": [1640, 1431, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Mongbat", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5eca3a0-ab6e-4a09-aea0-e5c02573761a", + "name": "Spawner (2113)", + "location": [1055, 1442, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e5029ee0-ff6b-4336-bd11-196cfba8392c", + "name": "Spawner (2113)", + "location": [1021, 1415, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fc005a29-add5-4782-9fa3-5a15644b9d97", + "name": "Spawner (2113)", + "location": [1060, 1375, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca9bfc78-2835-4a5b-bc0d-6fe008b79b5c", + "name": "Spawner (2113)", + "location": [1004, 1369, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "00b85da5-f02c-4ddb-88c0-11c677a799c1", + "name": "Spawner (2113)", + "location": [1037, 1325, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f26c8e00-9f91-478b-8d8e-089f164d81cf", + "name": "Spawner (2113)", + "location": [971, 1308, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "Ratman", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2676738-8f89-44ba-bd49-531b4a48c13a", + "name": "Spawner (2113)", + "location": [2171, 1356, 0], + "map": "Trammel", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, + { "name": "Savage", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "034b0848-3841-4c7a-befa-b47a3c32c96a", + "name": "Spawner (2113)", + "location": [2184, 1376, 0], + "map": "Trammel", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "SavageShaman", "maxCount": 1, "probability": 100 }, + { "name": "Savage", "maxCount": 3, "probability": 100 }, + { "name": "SavageRider", "maxCount": 2, "probability": 100 }, + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f689f7d9-5600-4199-86fd-e4bfc33e670f", + "name": "Spawner (2113)", + "location": [2155, 1370, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a4006aa-ef63-42db-9a08-cc7df6515351", + "name": "Spawner (2113)", + "location": [2203, 1379, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a4e77a5-dd20-465a-a274-4a6dedad51b8", + "name": "Spawner (2113)", + "location": [2038, 1023, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61c1a7de-68ec-4a55-b6d7-b71c315d93d1", + "name": "Spawner (2113)", + "location": [2079, 1018, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b3c0d48-b7c1-4282-8233-b5bef002ab2a", + "name": "Spawner (2113)", + "location": [2067, 989, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6dfcbb5-788b-4b78-b87b-d3587f7ccc94", + "name": "Spawner (2113)", + "location": [2028, 987, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4bbc1a4a-e118-4531-aaae-b7c8ea90206f", + "name": "Spawner (2113)", + "location": [2033, 936, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3a4bb67-71eb-420c-b965-8dec687d5b66", + "name": "Spawner (2113)", + "location": [1990, 976, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5a204bf-a47d-47be-9f0b-9182bc4ad39f", + "name": "Spawner (2113)", + "location": [1993, 1030, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3db40c33-f93d-4707-a22e-b56e2170744f", + "name": "Spawner (2113)", + "location": [2035, 1018, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e87d826-1451-4347-b6b1-e272bcd135cc", + "name": "Spawner (2113)", + "location": [2023, 1052, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1812fd23-b8b5-4af6-b601-b12e3acd5de3", + "name": "Spawner (2113)", + "location": [2069, 1028, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2c184c7e-21c7-46f1-b437-5e4e9091f0b9", + "name": "Spawner (2113)", + "location": [2048, 1066, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 3, "probability": 100 }, + { "name": "Alligator", "maxCount": 3, "probability": 100 }, + { "name": "BogThing", "maxCount": 3, "probability": 100 }, + { "name": "Bogling", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 3, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 3, "probability": 100 }, + { "name": "Lizardman", "maxCount": 3, "probability": 100 }, + { "name": "Wisp", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 3, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 3, "probability": 100 }, + { "name": "Ratman", "maxCount": 3, "probability": 100 }, + { "name": "SewerRat", "maxCount": 3, "probability": 100 }, + { "name": "Slime", "maxCount": 3, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6781029f-3b4a-4030-ad2d-83948df6f257", + "name": "Spawner (2113)", + "location": [1435, 2261, 5], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "93468ee6-bb6d-43d0-b4b7-eb86de5bcbc0", + "name": "Spawner (2113)", + "location": [1651, 2413, 5], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "547bc7d5-edce-4e93-967c-3430f125219f", + "name": "Spawner (2113)", + "location": [1619, 2725, 10], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eac45551-873f-48bb-9296-121cbd96318c", + "name": "Spawner (2113)", + "location": [1427, 2525, 0], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a756d7c7-629c-45cb-970e-deee0190baa3", + "name": "Spawner (2113)", + "location": [1059, 2461, 0], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a50eb7ab-718b-4cd9-b418-2a55ef6c8eb6", + "name": "Spawner (2113)", + "location": [1243, 2437, 5], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Ratman", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "Reaper", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ec3e3a0-8c69-4620-8e2c-7f6b2d550787", + "name": "Spawner (2113)", + "location": [1145, 2231, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Daemon", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d9fa1621-0c99-4082-8075-0e4a1f8e25f7", + "name": "Spawner (2113)", + "location": [1072, 2195, 5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d4c137a-0a24-456a-837f-cfd51ab71a3a", + "name": "Spawner (2113)", + "location": [1070, 2256, 5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a4e772e0-b570-46da-a6b8-4d84ad26dfcb", + "name": "Spawner (2113)", + "location": [1215, 2195, 5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f019e22a-5265-452b-ac1f-9f1cd8018041", + "name": "Spawner (2113)", + "location": [1216, 2259, 5], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "Imp", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d1a8c12d-2186-49a2-81ae-6f14ea963ca1", + "name": "Spawner (2113)", + "location": [1595, 3045, 0], + "map": "Trammel", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 60, + "walkingRange": 100, + "entries": [ + { "name": "Orc", "maxCount": 5, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 5, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 5, "probability": 100 }, + { "name": "Gorilla", "maxCount": 5, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 5, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09a60df9-a7f8-4964-9e24-dc43558b7c96", + "name": "Spawner (2113)", + "location": [2091, 3413, 0], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d38fbcb-a573-405a-94cd-1cda898164a1", + "name": "Spawner (2113)", + "location": [1931, 3205, 0], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "30a11e6d-014f-427b-a461-63d0653fa3b8", + "name": "Spawner (2113)", + "location": [1827, 3429, 10], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3effe468-2b2d-4d56-952e-9c378e1d9a84", + "name": "Spawner (2113)", + "location": [1635, 3253, 0], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a08f71d7-7504-448f-814c-c7c34216019c", + "name": "Spawner (2113)", + "location": [1387, 3109, 0], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 150, + "walkingRange": 150, + "entries": [ + { "name": "Orc", "maxCount": 10, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 10, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 10, "probability": 100 }, + { "name": "Gorilla", "maxCount": 10, "probability": 100 }, + { "name": "TropicalBird", "maxCount": 10, "probability": 100 }, + { "name": "WhippingVine", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "090b4a9b-ed1e-4276-94db-cf708d34f9d6", + "name": "Spawner (2113)", + "location": [1110, 2672, 0], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "HeadlessOne", "maxCount": 3, "probability": 100 }, + { "name": "Orc", "maxCount": 6, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "04e28c4b-9b75-46a7-b932-3ce92bcc7d85", + "name": "Spawner (2113)", + "location": [1158, 2872, 0], + "map": "Trammel", + "count": 50, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "AcidElemental", "maxCount": 24, "probability": 100 }, + { "name": "Alligator", "maxCount": 24, "probability": 100 }, + { "name": "BogThing", "maxCount": 24, "probability": 100 }, + { "name": "Bogling", "maxCount": 24, "probability": 100 }, + { "name": "Corpser", "maxCount": 24, "probability": 100 }, + { "name": "GiantRat", "maxCount": 24, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 24, "probability": 100 }, + { "name": "Lizardman", "maxCount": 23, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 23, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 23, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 23, "probability": 100 }, + { "name": "Ratman", "maxCount": 23, "probability": 100 }, + { "name": "SewerRat", "maxCount": 23, "probability": 100 }, + { "name": "Snake", "maxCount": 23, "probability": 100 }, + { "name": "Wisp", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "079a77cb-460d-4537-bd99-7ffe833a7648", + "name": "Spawner (2113)", + "location": [1141, 2959, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Savage", "maxCount": 2, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b5eb5bfe-0848-489f-9717-a8891ae7a1e9", + "name": "Spawner (2113)", + "location": [1166, 2958, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Savage", "maxCount": 2, "probability": 100 }, + { "name": "SavageRider", "maxCount": 1, "probability": 100 }, + { "name": "SavageShaman", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "00f29d73-3ef1-4c12-bdd7-ff19293fecef", + "name": "Spawner (2113)", + "location": [854, 1686, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "Brigand", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "77395a8a-4c2d-4f5f-9692-58c84c4146a7", + "name": "Spawner (2113)", + "location": [757, 1401, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Lich", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "88e2ec5e-fbd6-4459-9128-74e244d4b01f", + "name": "Spawner (2113)", + "location": [634, 1504, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9c326969-58f1-4463-83a1-980619b49133", + "name": "Spawner (2113)", + "location": [634, 1462, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Orc", "maxCount": 3, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 1, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "decca3ad-554d-4411-b3eb-ee2535528d04", + "name": "Spawner (2113)", + "location": [633, 1484, 0], + "map": "Trammel", + "count": 14, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Orc", "maxCount": 8, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 1, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 1, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 2, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f1a08a0-b47a-4299-8733-94a2923e3d42", + "name": "Spawner (2113)", + "location": [965, 757, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Skeleton", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2169b133-512b-45c3-aead-50a931bd972a", + "name": "Spawner (2113)", + "location": [955, 707, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 12, + "entries": [ + { "name": "Wraith", "maxCount": 4, "probability": 100 }, + { "name": "Shade", "maxCount": 4, "probability": 100 }, + { "name": "Spectre", "maxCount": 4, "probability": 100 }, + { "name": "Ghoul", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "574f956f-5ff2-4a72-a157-6ffb296b52b2", + "name": "Spawner (2113)", + "location": [962, 692, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 5, + "entries": [ + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c7248e18-5065-4a1f-8278-cd383e39a7ad", + "name": "Spawner (2113)", + "location": [948, 703, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 5, + "entries": [ + { "name": "Wraith", "maxCount": 1, "probability": 100 }, + { "name": "Shade", "maxCount": 1, "probability": 100 }, + { "name": "Spectre", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85053a68-d91a-4502-841e-697dde7fdbf5", + "name": "Spawner (2113)", + "location": [1016, 821, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 10, + "entries": [{ "name": "Lich", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "637a3aaf-fa96-4bdc-a770-ec273932916f", + "name": "Spawner (2113)", + "location": [1015, 706, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "20d0534e-aa34-4f34-9615-5e37cb005052", + "name": "Spawner (2113)", + "location": [1017, 826, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abdfd7cf-7042-4881-b6bd-16231d352cb3", + "name": "Spawner (2113)", + "location": [4596, 3607, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Daemon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f0a8c785-3472-41f4-b59b-e3a0252e6962", + "name": "Spawner (2113)", + "location": [4571, 3572, 31], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "caeb038e-ab75-41e9-bce1-c5be5d25551e", + "name": "Spawner (2113)", + "location": [4595, 3574, 75], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [{ "name": "Daemon", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cd80c05e-08c8-411f-aecd-0b030c095094", + "name": "Spawner (2113)", + "location": [4618, 3570, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 50, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d3e512e-32f2-412f-a9de-4140e4e97a9e", + "name": "Spawner (2113)", + "location": [4595, 3570, 30], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 3, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f67516b1-35ef-4231-890f-5626fccbfef1", + "name": "Spawner (2113)", + "location": [608, 1702, 0], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "Wisp", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "71d63638-7d38-4e09-a599-2b6eace4ffff", + "name": "Spawner (2113)", + "location": [4530, 1387, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "GiantRat", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Jwilson", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b280374f-3913-422f-a8fb-67a5b5774cbd", + "name": "Spawner (2113)", + "location": [4511, 1394, 23], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "IceSerpent", "maxCount": 1, "probability": 100 }, + { "name": "IceSnake", "maxCount": 1, "probability": 100 }, + { "name": "PolarBear", "maxCount": 1, "probability": 100 }, + { "name": "SnowLeopard", "maxCount": 1, "probability": 100 }, + { "name": "WhiteWolf", "maxCount": 1, "probability": 100 }, + { "name": "FrostOoze", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e146ad1f-0e5e-4018-956e-faddb83059ca", + "name": "Spawner (2113)", + "location": [4511, 1394, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "FrostSpider", "maxCount": 1, "probability": 100 }, + { "name": "FrostTroll", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a2162b8e-47c1-449a-bca4-8b1e29a7fa5b", + "name": "Spawner (2113)", + "location": [4498, 1394, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Rabbit", "maxCount": 1, "probability": 100 }, + { "name": "JackRabbit", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "Panther", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 }, + { "name": "Eagle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88ec5fa0-837d-41e1-9b72-864f82f40070", + "name": "Spawner (2113)", + "location": [4485, 1387, 23], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Alligator", "maxCount": 2, "probability": 100 }, + { "name": "BullFrog", "maxCount": 3, "probability": 100 }, + { "name": "Corpser", "maxCount": 1, "probability": 100 }, + { "name": "SwampTentacle", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "081de1cf-9c2c-4ac8-9b21-ba2593487543", + "name": "Spawner (2113)", + "location": [4493, 1364, 23], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GiantSpider", "maxCount": 1, "probability": 100 }, + { "name": "Boar", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 }, + { "name": "Hind", "maxCount": 1, "probability": 100 }, + { "name": "Llama", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33d5a886-8aea-47d2-910d-067fdacd1def", + "name": "Spawner (2113)", + "location": [4493, 1364, 23], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "Horse", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d956977-3c38-429d-bf32-32968f930d34", + "name": "Spawner (2113)", + "location": [4506, 1358, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [ + { "name": "BrownBear", "maxCount": 1, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 1, "probability": 100 }, + { "name": "BlackBear", "maxCount": 1, "probability": 100 }, + { "name": "DireWolf", "maxCount": 1, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 1, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 1, "probability": 100 }, + { "name": "Cougar", "maxCount": 1, "probability": 100 }, + { "name": "Panther", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GreatHart", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d68cd11-ae7b-45c4-944a-6e58a6e65684", + "name": "Spawner (2113)", + "location": [4519, 1358, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Alligator", "maxCount": 1, "probability": 100 }, + { "name": "BullFrog", "maxCount": 1, "probability": 100 }, + { "name": "Gorilla", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "GiantToad", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "700be867-c251-4ecc-a320-867ba65b024c", + "name": "Spawner (2113)", + "location": [4531, 1363, 23], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 15, + "entries": [ + { "name": "Mummy", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb69e358-5119-4573-a663-19bb5ab7bdcd", + "name": "Spawner (2113)", + "location": [4512, 1372, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 10, + "entries": [ + { "name": "RottingCorpse", "maxCount": 1, "probability": 100 }, + { "name": "Skeleton", "maxCount": 1, "probability": 100 }, + { "name": "Slime", "maxCount": 1, "probability": 100 }, + { "name": "Snake", "maxCount": 1, "probability": 100 }, + { "name": "StrongMongbat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2b4059a-7a86-4b8d-aac4-d98f8ac842b5", + "name": "Spawner (2113)", + "location": [4512, 1380, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 10, + "entries": [ + { "name": "Ghoul", "maxCount": 1, "probability": 100 }, + { "name": "GiantSerpent", "maxCount": 1, "probability": 100 }, + { "name": "HeadlessOne", "maxCount": 1, "probability": 100 }, + { "name": "Jwilson", "maxCount": 1, "probability": 100 }, + { "name": "Zombie", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "efb1438c-3ee7-466f-80b8-629a1e319b19", + "name": "Spawner (2113)", + "location": [4500, 1382, 23], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Chicken", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Pig", "maxCount": 1, "probability": 100 }, + { "name": "Sheep", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "686118b7-c5ff-48d2-9035-55a6eabee085", + "name": "Spawner (2113)", + "location": [821, 675, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3fa601f2-2e8d-4170-8c1f-1960889ef12d", + "name": "Spawner (2113)", + "location": [1042, 1394, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78c65c1b-b461-4203-9a8a-ad62146c81ba", + "name": "Spawner (2113)", + "location": [2540, 130, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d5de5b0d-fffe-496d-8167-85ff964f45d0", + "name": "Spawner (2113)", + "location": [3170, 672, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b6c7f37-04ff-4f95-9167-8f299dd5ab5f", + "name": "Spawner (2113)", + "location": [2548, 1173, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "2733ca83-6c31-48d5-b360-eedf6f885beb", "name": "Spawner (2113)", "location": [3602, 2815, 28], @@ -185,5 +8568,175 @@ { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, { "name": "RatCamp", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "af95e8e2-8d1f-43a8-ab45-cd79154fd947", + "name": "Spawner (2113)", + "location": [4480, 1439, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "574d5350-83ca-4a90-8494-1234476d9004", + "name": "Spawner (2113)", + "location": [1152, 3471, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41daa032-dfaf-437c-aea8-5ebd5ae28f8d", + "name": "Spawner (2113)", + "location": [1530, 999, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b77a44c2-c523-4718-ba7d-ace36df8fde5", + "name": "Spawner (2113)", + "location": [1871, 1076, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc54c094-335f-4eb0-835b-df374aa73808", + "name": "Spawner (2113)", + "location": [788, 1894, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1c790a35-f7b6-4ee1-8d94-4e6076f94ed3", + "name": "Spawner (2113)", + "location": [1064, 2488, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1b36b219-b882-4af5-a52b-5097ee26680f", + "name": "Spawner (2113)", + "location": [2103, 3371, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ebc9621-1061-4ab5-aed9-c6d72a4c1514", + "name": "Spawner (2113)", + "location": [2044, 2323, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd48c66c-dbf3-4729-9b05-896d93615858", + "name": "Spawner (2113)", + "location": [1939, 1310, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e4825b9-1cbe-4a27-a4d2-e2552901924f", + "name": "Spawner (2113)", + "location": [2349, 722, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:45:00", + "maxDelay": "01:00:00", + "team": 0, + "homeRange": 1, + "walkingRange": 10, + "entries": [ + { "name": "OrcCamp", "maxCount": 1, "probability": 100 }, + { "name": "RatCamp", "maxCount": 1, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/uoml/trammel/PaintedCaves.json b/Distribution/Data/Spawns/uoml/trammel/PaintedCaves.json new file mode 100644 index 000000000..bcc9c1e1e --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/PaintedCaves.json @@ -0,0 +1,42 @@ +[ + { + "type": "Spawner", + "guid": "3cf3a839-be51-4f1c-8592-9a1f89b02b87", + "name": "Spawner (2114)", + "location": [6279, 880, 1], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 1, "probability": 100 }, + { "name": "Dog", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "67d9fa9b-5bc4-4f2b-8f19-fc31af6f2333", + "name": "Spawner (2114)", + "location": [6275, 879, -2], + "map": "Trammel", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Troglodyte", "maxCount": 3, "probability": 100 }, + { "name": "Grobu", "maxCount": 3, "probability": 100 }, + { "name": "Lurg", "maxCount": 3, "probability": 100 }, + { "name": "GiantRat", "maxCount": 3, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/PalaceOfParoxysmus.json b/Distribution/Data/Spawns/uoml/trammel/PalaceOfParoxysmus.json new file mode 100644 index 000000000..45187240f --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/PalaceOfParoxysmus.json @@ -0,0 +1,282 @@ +[ + { + "type": "Spawner", + "guid": "9599a1e0-f678-49f5-8ec6-c4f01998f96f", + "name": "Spawner (2115)", + "location": [6295, 616, -50], + "map": "Trammel", + "count": 27, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Daemon", "maxCount": 6, "probability": 100 }, + { "name": "Succubus", "maxCount": 6, "probability": 100 }, + { "name": "InterredGrizzle", "maxCount": 6, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 6, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 6, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 6, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 3, "probability": 100 }, + { "name": "Putrefier", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeastLord", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bae60d5d-cbc7-4fed-ae27-75eb68d79870", + "name": "Spawner (2115)", + "location": [6384, 580, -50], + "map": "Trammel", + "count": 31, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "InterredGrizzle", "maxCount": 4, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 4, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 3, "probability": 100 }, + { "name": "Balron", "maxCount": 9, "probability": 100 }, + { "name": "Succubus", "maxCount": 9, "probability": 100 }, + { "name": "Daemon", "maxCount": 9, "probability": 100 }, + { "name": "PlagueBeastLord", "maxCount": 11, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 11, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 11, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cb00720f-c41e-413f-8cec-b90e2fe72b4a", + "name": "Spawner (2115)", + "location": [6328, 517, -50], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "PoisonElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c3c0fc01-0e9e-420e-a994-ce64bc57a96e", + "name": "Spawner (2115)", + "location": [6354, 455, -40], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "PlagueBeast", "maxCount": 5, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 5, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 5, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02cf36b9-6188-4bda-9e7f-66d0aac930b8", + "name": "Spawner (2115)", + "location": [6416, 447, -40], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "PlagueBeast", "maxCount": 8, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 8, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 7, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c68cc7ec-9447-455e-ac60-ab1d1f319bc5", + "name": "Spawner (2115)", + "location": [6466, 543, -50], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Balron", "maxCount": 6, "probability": 100 }, + { "name": "Succubus", "maxCount": 6, "probability": 100 }, + { "name": "Daemon", "maxCount": 6, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 3, "probability": 100 }, + { "name": "PlagueBeastLord", "maxCount": 8, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 8, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 8, "probability": 100 }, + { "name": "InterredGrizzle", "maxCount": 5, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b986151-c607-40ec-b8b2-3b8a09e1e828", + "name": "Spawner (2115)", + "location": [6292, 462, -50], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 40, + "walkingRange": 40, + "entries": [ + { "name": "ChaosDaemon", "maxCount": 7, "probability": 100 }, + { "name": "Moloch", "maxCount": 7, "probability": 100 }, + { "name": "Succubus", "maxCount": 7, "probability": 100 }, + { "name": "Daemon", "maxCount": 7, "probability": 100 }, + { "name": "Balron", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8bbbe927-ffbd-4f1c-aa89-4982088f1dca", + "name": "Spawner (2115)", + "location": [6298, 395, 60], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "851856f8-49a9-4dfd-95bc-e969d748bd9c", + "name": "Spawner (2115)", + "location": [6404, 378, -40], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 18, + "walkingRange": 18, + "entries": [ + { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 2, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dcc7984a-1911-4368-81df-344fa7b0a2b5", + "name": "Spawner (2115)", + "location": [6418, 358, -40], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "PlagueSpawn", "maxCount": 12, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 12, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 12, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 12, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 12, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 2, "probability": 100 }, + { "name": "PlagueBeast", "maxCount": 2, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 2, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad7e7828-7b85-416c-8ca6-6a9e7de99d95", + "name": "Spawner (2115)", + "location": [6319, 348, 60], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "EarthElemental", "maxCount": 5, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 5, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 5, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 5, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 1, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "PlagueSpawn", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "546847b9-bd52-4a7b-be03-bd9b9f26cb17", + "name": "Spawner (2115)", + "location": [6249, 352, 60], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "AcidElemental", "maxCount": 6, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 6, "probability": 100 }, + { "name": "AcidElemental", "maxCount": 1, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 1, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "676cc139-9378-4935-9667-0cbd86e0b2b1", + "name": "Spawner (2115)", + "location": [6285, 353, 60], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "PoisonElemental", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c32b93fc-8549-4d80-8d8a-de99e885a477", + "name": "Spawner (2115)", + "location": [6353, 400, 60], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "CorrosiveSlime", "maxCount": 4, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/PrismOfLight.json b/Distribution/Data/Spawns/uoml/trammel/PrismOfLight.json new file mode 100644 index 000000000..69ee832a6 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/PrismOfLight.json @@ -0,0 +1,315 @@ +[ + { + "type": "Spawner", + "guid": "4664466b-b753-46bf-8d9a-4c66246ed060", + "name": "Spawner (2116)", + "location": [6553, 157, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "CrystalLatticeSeeker", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "70250ff2-f75d-4f6a-adcd-e32d3f8c05e4", + "name": "Spawner (2116)", + "location": [6474, 74, -34], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [ + { "name": "Rat", "maxCount": 9, "probability": 100 }, + { "name": "IceSnake", "maxCount": 9, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "44ede0db-e4a9-4fc2-bbd4-41741dc3a138", + "name": "Spawner (2116)", + "location": [6552, 155, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Wisp", "maxCount": 5, "probability": 100 }, + { "name": "CrystalWisp", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ce801dd-1e54-4eb6-b908-d095fc3ac3d6", + "name": "Spawner (2116)", + "location": [6521, 139, -20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Wisp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d4e26c54-212c-4dc9-bbb8-a6e2ecd02d55", + "name": "Spawner (2116)", + "location": [6530, 139, -20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Wisp", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "500789a1-6c13-4c8c-94c4-d7290a6d9238", + "name": "Spawner (2116)", + "location": [6576, 91, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalSeaSerpent", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2bdadfea-e542-43dd-8391-a0a55761bd09", + "name": "Spawner (2116)", + "location": [6509, 176, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "UnfrozenMummy", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19d77ed8-3a64-4317-9570-c8a19dc885ed", + "name": "Spawner (2116)", + "location": [6536, 79, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "68fe4e5a-136b-4f9d-8b23-1289f26cbfb3", + "name": "Spawner (2116)", + "location": [6547, 91, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ad2e27a-0fe6-44eb-9a7e-1840e2333414", + "name": "Spawner (2116)", + "location": [6572, 89, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalHydra", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6c1abed9-6d57-4f20-a0e6-1598ab7b0602", + "name": "Spawner (2116)", + "location": [6504, 181, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cb0719bf-0812-4551-a287-3fe92e6dd66b", + "name": "Spawner (2116)", + "location": [6475, 101, -44], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "ShadowWisp", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2b5f366f-9197-4fb0-842c-d7e50f9d3395", + "name": "Spawner (2116)", + "location": [6478, 174, 4], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Wisp", "maxCount": 6, "probability": 100 }, + { "name": "CrystalWisp", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5a151ef4-7184-49a1-b516-e2e5ba7973f4", + "name": "Spawner (2116)", + "location": [6566, 120, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "CrystalVortex", "maxCount": 7, "probability": 100 }, + { "name": "IceElemental", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e2e128a-5312-4afc-858f-4ee70783f6c8", + "name": "Spawner (2116)", + "location": [6534, 178, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalWisp", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d8d2b685-7458-494d-a596-90baea5763c8", + "name": "Spawner (2116)", + "location": [6535, 179, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "CrystalWisp", "maxCount": 5, "probability": 100 }, + { "name": "CrystalLatticeSeeker", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 5, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ed761287-3375-4732-be78-2277dec45e2e", + "name": "Spawner (2116)", + "location": [6502, 120, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 0, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "57899529-d774-4c8f-a5b3-263237a5cb74", + "name": "Spawner (2116)", + "location": [6507, 151, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Protector", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dbdcaa33-dad3-447b-be16-737b98732315", + "name": "Spawner (2116)", + "location": [6532, 79, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "CrystalDaemon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9e39061c-b9fa-4988-abef-2eac1f2344f3", + "name": "Spawner (2116)", + "location": [6578, 183, 31], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Ferret", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6d47bfa4-0820-4805-865d-7a7f3e54e159", + "name": "Spawner (2116)", + "location": [6510, 171, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Protector", "maxCount": 3, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Reagents.json b/Distribution/Data/Spawns/uoml/trammel/Reagents.json new file mode 100644 index 000000000..67a04333c --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Reagents.json @@ -0,0 +1,1382 @@ +[ + { + "type": "Spawner", + "guid": "a3c31fb0-0ee3-4582-8fc1-9b80dca33840", + "name": "Spawner (2129)", + "location": [2854, 816, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c230fcb8-e618-4853-9a08-1ea490cf6f9d", + "name": "Spawner (2129)", + "location": [4590, 1448, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1b8bcfb-3ee9-4a4a-8ae3-1a8d955cd210", + "name": "Spawner (2129)", + "location": [2490, 1096, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23902d00-71f8-4e7d-95d8-a237c88a77bd", + "name": "Spawner (2129)", + "location": [1566, 1140, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe92f02b-a3b1-4bf2-add0-ca9a447f6982", + "name": "Spawner (2129)", + "location": [1514, 1584, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9dc0305-842f-4328-b377-a50189c056db", + "name": "Spawner (2129)", + "location": [1938, 1408, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2bba2ae7-487a-434b-be26-4a3b8a33cbdf", + "name": "Spawner (2129)", + "location": [2374, 652, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1fe40850-5b0c-4b89-880f-a64de82f0651", + "name": "Spawner (2129)", + "location": [2054, 516, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b6ca6516-11a1-4797-8dc5-9895256a30c2", + "name": "Spawner (2129)", + "location": [1286, 504, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "30a36df2-0426-4686-b7b7-abff8e0db0fe", + "name": "Spawner (2129)", + "location": [746, 636, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41a38d34-c7a4-40b6-b95f-821fc2a8b77d", + "name": "Spawner (2129)", + "location": [1134, 964, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d8b589d-c874-4358-88f7-581e4a9f5c04", + "name": "Spawner (2129)", + "location": [718, 1180, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abbe6e65-1093-417c-8cb5-57314087f26b", + "name": "Spawner (2129)", + "location": [342, 1464, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d68c680-4113-41e3-8d15-bda7b4f8631b", + "name": "Spawner (2129)", + "location": [702, 1752, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98e1387c-8ef9-4fb5-b816-eaa040824d11", + "name": "Spawner (2129)", + "location": [1114, 1416, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81761c3a-8870-44aa-a753-2730826c047e", + "name": "Spawner (2129)", + "location": [1150, 1876, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31e3fd64-5ee4-43cb-95e5-10558cb9dfbe", + "name": "Spawner (2129)", + "location": [1582, 2028, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6a3759d1-5599-4a4d-9d3d-bb9dfd92bbf4", + "name": "Spawner (2129)", + "location": [1934, 2328, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "97319bac-f1df-456d-bf1c-9819cf6b1fdc", + "name": "Spawner (2129)", + "location": [1490, 2472, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "125b2482-f852-4e28-b180-1718ebb4d062", + "name": "Spawner (2129)", + "location": [1070, 2320, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "84464230-dfb3-41cf-acba-ff69341c5099", + "name": "Spawner (2129)", + "location": [1466, 3032, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "de6f1dd7-f7f4-449b-96c6-8c4e93df7ddc", + "name": "Spawner (2129)", + "location": [2062, 964, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6e42372d-6d54-44c8-8e89-af54e9a086f4", + "name": "Spawner (2129)", + "location": [1846, 3260, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af3c879d-6b46-424a-85a2-8178306e3ed0", + "name": "Spawner (2129)", + "location": [1078, 2772, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4df7d7dd-b748-4680-9564-a561bd44a1ba", + "name": "Spawner (2129)", + "location": [4710, 3796, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "29f7b655-522d-449b-a528-0bf2f4458aac", + "name": "Spawner (2129)", + "location": [5926, 3888, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c0091b8-6f09-437c-9e2f-7be1fbc92a9b", + "name": "Spawner (2129)", + "location": [5474, 3904, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3212484a-1a7f-4939-9704-f251885c3958", + "name": "Spawner (2129)", + "location": [5934, 3436, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad7ee5e1-f8ef-418f-a08e-7208a7f1e6db", + "name": "Spawner (2129)", + "location": [5462, 3452, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15b9837c-a21b-46c8-9855-ac45895bdd07", + "name": "Spawner (2129)", + "location": [5674, 3120, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "546305f1-85c7-4072-864c-1a8c146b3b55", + "name": "Spawner (2129)", + "location": [5618, 2744, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be631686-1638-4057-89af-f53b90b3c57f", + "name": "Spawner (2129)", + "location": [5326, 2492, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a95d1ad3-6006-422c-8457-86b455be3589", + "name": "Spawner (2129)", + "location": [5966, 2456, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd345daf-05c6-4a88-9294-23ededf39f0e", + "name": "Spawner (2129)", + "location": [5234, 3068, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c90c7638-9e53-460d-85c3-52002d27f48f", + "name": "Spawner (2129)", + "location": [6042, 2916, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "820cbf36-79e2-4d81-9e18-d6fe00e18223", + "name": "Spawner (2129)", + "location": [3602, 2948, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae94454e-1590-489c-801b-cf6a1bf4942f", + "name": "Spawner (2129)", + "location": [3538, 2536, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bed21e3a-c8fa-44c0-a584-8d95e8c12cb8", + "name": "Spawner (2129)", + "location": [2734, 2168, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6136db46-6bdb-40ca-8b33-9299b082585d", + "name": "Spawner (2129)", + "location": [3714, 2152, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60e9fe4d-adfa-43a4-b237-b07d751c58bb", + "name": "Spawner (2129)", + "location": [3674, 1216, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2e9abe0-41c7-48e3-a9a7-45433525764f", + "name": "Spawner (2129)", + "location": [3994, 328, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f73e2029-5699-40d8-9c3e-71fee49654fa", + "name": "Spawner (2129)", + "location": [4166, 584, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bfd11968-9616-4213-922f-b645b97863e6", + "name": "Spawner (2129)", + "location": [3198, 504, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a64e927-c349-447b-930c-c4de5d8d257d", + "name": "Spawner (2129)", + "location": [2750, 368, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23205131-5ad2-41a7-a07a-bb7fe741e100", + "name": "Spawner (2129)", + "location": [1694, 688, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a78242f1-ab8f-46d6-b5d9-7652c2b01a86", + "name": "Spawner (2129)", + "location": [366, 900, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea100cd7-2afd-4001-a2db-aa9e7de29957", + "name": "Spawner (2129)", + "location": [642, 2216, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2634bcca-5534-4964-97f3-e7f949d7f428", + "name": "Spawner (2129)", + "location": [2226, 3532, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fedbe286-f965-4ce8-95d2-0e78001edb80", + "name": "Spawner (2129)", + "location": [1850, 2796, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b59164a9-46d0-4751-89b6-4f4cd3059f2e", + "name": "Spawner (2129)", + "location": [1658, 236, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bbe4df4d-2e1d-47da-97e4-ef89fe7a85e6", + "name": "Spawner (2129)", + "location": [4426, 1028, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d69d8667-b6eb-46b6-a480-e67290f6b12e", + "name": "Spawner (2129)", + "location": [2522, 84, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0c8ebc0f-8be3-45a7-85bf-51960355ac9b", + "name": "Spawner (2129)", + "location": [3130, 116, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "159fe79b-9b72-475b-8a75-7da4f5cca213", + "name": "Spawner (2129)", + "location": [2082, 60, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c714dac4-dbfb-4a12-94ea-4536cd915ca7", + "name": "Spawner (2129)", + "location": [2930, 3476, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "53399cd4-2e2c-468d-9509-c794caa5a693", + "name": "Spawner (2129)", + "location": [1410, 3796, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d69ca3b5-5114-4aa8-9ff9-fa1b32848294", + "name": "Spawner (2129)", + "location": [1122, 3524, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1bba845c-2dc5-4395-b547-932b73723320", + "name": "Spawner (2129)", + "location": [2474, 3956, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d2e8680-ffd9-43ff-bc25-dc0d1b290e7d", + "name": "Spawner (2129)", + "location": [4570, 3340, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01bb3555-b7f0-4e46-b0ff-0b7266390cf3", + "name": "Spawner (2129)", + "location": [4282, 3700, 0], + "map": "Trammel", + "count": 160, + "minDelay": "00:11:00", + "maxDelay": "00:23:00", + "team": 0, + "homeRange": 200, + "walkingRange": 200, + "entries": [ + { "name": "BlackPearl", "maxCount": 160, "probability": 100 }, + { "name": "Bloodmoss", "maxCount": 160, "probability": 100 }, + { "name": "SpidersSilk", "maxCount": 160, "probability": 100 }, + { "name": "SulfurousAsh", "maxCount": 160, "probability": 100 }, + { "name": "Garlic", "maxCount": 160, "probability": 100 }, + { "name": "Ginseng", "maxCount": 160, "probability": 100 }, + { "name": "Nightshade", "maxCount": 160, "probability": 100 }, + { "name": "MandrakeRoot", "maxCount": 160, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Sanctuary.json b/Distribution/Data/Spawns/uoml/trammel/Sanctuary.json new file mode 100644 index 000000000..a2bdd87e5 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Sanctuary.json @@ -0,0 +1,222 @@ +[ + { + "type": "Spawner", + "guid": "499d7a5c-d73c-4e43-889f-f88bc94cf40e", + "name": "Spawner (2117)", + "location": [6263, 118, -10], + "map": "Trammel", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Chicken", "maxCount": 6, "probability": 100 }, + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 3, "probability": 100 }, + { "name": "Cat", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 6, "probability": 100 }, + { "name": "Bull", "maxCount": 6, "probability": 100 }, + { "name": "Pig", "maxCount": 3, "probability": 100 }, + { "name": "Boar", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "18850a42-5089-43d9-9f0c-1adca014ed3e", + "name": "Spawner (2117)", + "location": [6349, 89, -20], + "map": "Trammel", + "count": 30, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Ratman", "maxCount": 18, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 18, "probability": 100 }, + { "name": "Chiikkaha", "maxCount": 18, "probability": 100 }, + { "name": "Titan", "maxCount": 18, "probability": 100 }, + { "name": "Cyclops", "maxCount": 18, "probability": 100 }, + { "name": "Rat", "maxCount": 18, "probability": 100 }, + { "name": "Ratman", "maxCount": 4, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 4, "probability": 100 }, + { "name": "Chiikkaha", "maxCount": 2, "probability": 100 }, + { "name": "Titan", "maxCount": 2, "probability": 100 }, + { "name": "Cyclops", "maxCount": 2, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01891c2e-7b0f-4f3e-81a6-199eb062ec9c", + "name": "Spawner (2117)", + "location": [6168, 117, 0], + "map": "Trammel", + "count": 34, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Ratman", "maxCount": 11, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 11, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 11, "probability": 100 }, + { "name": "Changeling", "maxCount": 11, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 11, "probability": 100 }, + { "name": "Dog", "maxCount": 9, "probability": 100 }, + { "name": "Chicken", "maxCount": 9, "probability": 100 }, + { "name": "Cat", "maxCount": 9, "probability": 100 }, + { "name": "Boar", "maxCount": 9, "probability": 100 }, + { "name": "Bird", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 3, "probability": 100 }, + { "name": "Titan", "maxCount": 3, "probability": 100 }, + { "name": "Cyclops", "maxCount": 3, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 5, "probability": 100 }, + { "name": "orcscout", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e35998b5-c9fa-4547-b793-3efeeca0253f", + "name": "Spawner (2117)", + "location": [6189, 71, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "OgreLord", "maxCount": 8, "probability": 100 }, + { "name": "OrcishLord", "maxCount": 8, "probability": 100 }, + { "name": "Ratman", "maxCount": 8, "probability": 100 }, + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cow", "maxCount": 4, "probability": 100 }, + { "name": "Bull", "maxCount": 4, "probability": 100 }, + { "name": "Pig", "maxCount": 4, "probability": 100 }, + { "name": "Chicken", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "28f37a01-81c7-4090-a181-5cd3f670ef72", + "name": "Spawner (2117)", + "location": [6187, 101, 0], + "map": "Trammel", + "count": 34, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Titan", "maxCount": 10, "probability": 100 }, + { "name": "Ettin", "maxCount": 10, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 10, "probability": 100 }, + { "name": "MougGuur", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Chicken", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 }, + { "name": "Cow", "maxCount": 10, "probability": 100 }, + { "name": "Bull", "maxCount": 10, "probability": 100 }, + { "name": "OrcBomber", "maxCount": 7, "probability": 100 }, + { "name": "OrcBrute", "maxCount": 7, "probability": 100 }, + { "name": "Orc", "maxCount": 7, "probability": 100 }, + { "name": "Snake", "maxCount": 7, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 7, "probability": 100 }, + { "name": "Ogre", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0983061e-4deb-4750-8f2b-ee10756735ec", + "name": "Spawner (2117)", + "location": [6184, 29, 0], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Chicken", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2862d617-a6ad-449e-b063-3a11f39753d7", + "name": "Spawner (2117)", + "location": [6265, 48, -10], + "map": "Trammel", + "count": 53, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "Szavetra", "maxCount": 22, "probability": 100 }, + { "name": "Imp", "maxCount": 22, "probability": 100 }, + { "name": "Titan", "maxCount": 22, "probability": 100 }, + { "name": "Snake", "maxCount": 22, "probability": 100 }, + { "name": "Ratman", "maxCount": 22, "probability": 100 }, + { "name": "Doppleganger", "maxCount": 22, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 22, "probability": 100 }, + { "name": "Cow", "maxCount": 18, "probability": 100 }, + { "name": "Bull", "maxCount": 18, "probability": 100 }, + { "name": "Chicken", "maxCount": 18, "probability": 100 }, + { "name": "Bird", "maxCount": 18, "probability": 100 }, + { "name": "Pig", "maxCount": 18, "probability": 100 }, + { "name": "Dog", "maxCount": 18, "probability": 100 }, + { "name": "Goat", "maxCount": 18, "probability": 100 }, + { "name": "Cat", "maxCount": 18, "probability": 100 }, + { "name": "Rat", "maxCount": 18, "probability": 100 }, + { "name": "Orc", "maxCount": 9, "probability": 100 }, + { "name": "OrcCaptain", "maxCount": 9, "probability": 100 }, + { "name": "OrcishMage", "maxCount": 9, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "Ogre", "maxCount": 4, "probability": 100 }, + { "name": "OgreLord", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f510865-94bd-49b7-90bf-c24131ef1151", + "name": "Spawner (2117)", + "location": [6191, 155, 0], + "map": "Trammel", + "count": 42, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 25, + "entries": [ + { "name": "Changeling", "maxCount": 18, "probability": 100 }, + { "name": "Ratman", "maxCount": 18, "probability": 100 }, + { "name": "Ettin", "maxCount": 18, "probability": 100 }, + { "name": "Titan", "maxCount": 18, "probability": 100 }, + { "name": "RatmanArcher", "maxCount": 18, "probability": 100 }, + { "name": "RatmanMage", "maxCount": 18, "probability": 100 }, + { "name": "Goat", "maxCount": 12, "probability": 100 }, + { "name": "Pig", "maxCount": 12, "probability": 100 }, + { "name": "Cow", "maxCount": 12, "probability": 100 }, + { "name": "Bull", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 }, + { "name": "GargoyleEnforcer", "maxCount": 9, "probability": 100 }, + { "name": "GargoyleDestroyer", "maxCount": 9, "probability": 100 }, + { "name": "Gargoyle", "maxCount": 9, "probability": 100 }, + { "name": "Snake", "maxCount": 3, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/SeaLife.json b/Distribution/Data/Spawns/uoml/trammel/SeaLife.json new file mode 100644 index 000000000..79c335cce --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/SeaLife.json @@ -0,0 +1,2396 @@ +[ + { + "type": "Spawner", + "guid": "ebfc8a09-79e6-4af0-9436-4728c1c8f90f", + "name": "Spawner (2118)", + "location": [1395, 3421, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23fac61f-7a20-4329-8a8e-6136e0fab29e", + "name": "Spawner (2118)", + "location": [4888, 375, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8f431e71-483c-4493-9f2f-80e89d6f0554", + "name": "Spawner (2118)", + "location": [4352, 215, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1b48857b-fc9d-42ef-8d0b-e372a256d37b", + "name": "Spawner (2118)", + "location": [3688, 439, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dcb85ac4-7906-49f5-9e13-223112e63dc3", + "name": "Spawner (2118)", + "location": [4512, 583, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40854e37-49b5-485d-b2e4-c514f7e04657", + "name": "Spawner (2118)", + "location": [4904, 759, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b70aa657-8435-4c48-bae7-38c62fd87d02", + "name": "Spawner (2118)", + "location": [4904, 1127, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98a90cb4-d8a4-46cb-a791-3767989189a7", + "name": "Spawner (2118)", + "location": [3576, 823, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5db1f5df-d7c7-4d1b-a938-a0008044eba8", + "name": "Spawner (2118)", + "location": [4016, 1031, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bee4ac0d-312f-4bed-8690-545f989cbf2b", + "name": "Spawner (2118)", + "location": [3992, 1447, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fa433125-925e-4c08-b036-6d42c8c2ac42", + "name": "Spawner (2118)", + "location": [4232, 1655, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bee3753d-3d3b-4a69-94b7-a9396767b655", + "name": "Spawner (2118)", + "location": [4600, 1751, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d9c9755-604e-4cad-b35c-f8f910ff42f1", + "name": "Spawner (2118)", + "location": [4896, 1519, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f3473140-bfcf-4672-b602-5a26d6273ff9", + "name": "Spawner (2118)", + "location": [3616, 1807, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4c974c21-e97f-4ab5-a375-8d24a2b25f8f", + "name": "Spawner (2118)", + "location": [4032, 2039, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f295d667-2c49-4737-be40-1e50874b6308", + "name": "Spawner (2118)", + "location": [4624, 2199, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40a637b6-5c6b-4a77-885a-da2491d97e5f", + "name": "Spawner (2118)", + "location": [4400, 2527, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d3d6b34-3a34-4b08-afaf-2d7d27071c5a", + "name": "Spawner (2118)", + "location": [3984, 2423, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "306cfd71-75e0-43de-b0b0-16c5271b8da8", + "name": "Spawner (2118)", + "location": [4896, 2119, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8df655e8-c716-4ad9-8ed9-4a5fc03ae90e", + "name": "Spawner (2118)", + "location": [4912, 2471, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "517db203-cdaa-41f6-93f2-906280e1344d", + "name": "Spawner (2118)", + "location": [4864, 2831, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c9ea5dd-8743-4733-990c-44ba4af4c3b0", + "name": "Spawner (2118)", + "location": [4536, 2847, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df266a1d-632f-4a87-a31d-1e5dc6bccf9f", + "name": "Spawner (2118)", + "location": [4896, 3215, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a62d0aec-b3d0-4ef7-b81a-304bc4e317e5", + "name": "Spawner (2118)", + "location": [4192, 2975, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2640ac8d-34c1-4869-8f3e-7533872fc883", + "name": "Spawner (2118)", + "location": [4008, 2767, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2fb5cbd9-1d4b-4e04-a3e3-a90e53dc60ba", + "name": "Spawner (2118)", + "location": [3328, 2063, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a4c154b-5279-42ee-bdd6-98873ee0bcec", + "name": "Spawner (2118)", + "location": [3176, 2383, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "405e5843-eff4-41c2-b17a-f3d9468fa0b2", + "name": "Spawner (2118)", + "location": [3088, 2759, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "796cf3aa-e1ce-4249-b39a-97246dbd2a82", + "name": "Spawner (2118)", + "location": [3888, 3167, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a84139ff-b09e-4b56-b69c-5a2a19ae0cab", + "name": "Spawner (2118)", + "location": [3968, 3895, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "144aa07d-868c-4d0a-b4b8-fb127d1f9651", + "name": "Spawner (2118)", + "location": [3240, 3151, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa8fc3ef-70ac-4310-aca2-44e2f5756e2f", + "name": "Spawner (2118)", + "location": [3776, 3519, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5af7b6a7-f921-4188-9f09-c7d45bf22b58", + "name": "Spawner (2118)", + "location": [3344, 3479, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e68a1d8-740d-4fef-b2a6-204c774cbc0a", + "name": "Spawner (2118)", + "location": [2792, 2951, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ac69ff02-6f53-401b-830d-e5cd4b8cfb6d", + "name": "Spawner (2118)", + "location": [2608, 3295, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95014bb0-76a6-4bc9-8b10-79296fa33ad6", + "name": "Spawner (2118)", + "location": [2744, 2567, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "062057b0-ded5-4aed-8ed7-1ea3204bbc27", + "name": "Spawner (2118)", + "location": [2368, 2799, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a432cdec-33eb-492d-93a8-6c8557cf643c", + "name": "Spawner (2118)", + "location": [3560, 3871, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce95bb5c-8753-4ae6-a109-5abcf47d36c9", + "name": "Spawner (2118)", + "location": [3168, 3871, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e4f0bf4-79c5-463f-8c3e-7dc5c38c9e0f", + "name": "Spawner (2118)", + "location": [2768, 3847, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eac92a71-5b24-4bb0-9518-d07ea06e1deb", + "name": "Spawner (2118)", + "location": [1720, 3879, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39e15c45-9188-4dbf-aab7-17a8c633911c", + "name": "Spawner (2118)", + "location": [200, 3879, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4209e2ed-53c1-45f1-9987-49f5ba958ec8", + "name": "Spawner (2118)", + "location": [544, 3719, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33bc2f0f-243c-40d2-bf2d-dad2466fa1dc", + "name": "Spawner (2118)", + "location": [896, 3695, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f5e480b-e325-405d-9250-55050cb07bab", + "name": "Spawner (2118)", + "location": [824, 3287, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2d164cb-e8de-4998-b1d2-457408ba7d68", + "name": "Spawner (2118)", + "location": [176, 3551, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef51ae2c-fc05-45af-8b7c-c8db93d36a3d", + "name": "Spawner (2118)", + "location": [480, 3351, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "934908c0-3860-4133-a090-ea95e09e10a0", + "name": "Spawner (2118)", + "location": [512, 2919, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fa56369-c24e-46da-872d-f2a43d43ceee", + "name": "Spawner (2118)", + "location": [192, 2895, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3f604920-6da5-4233-ac70-c870acfa2c9a", + "name": "Spawner (2118)", + "location": [208, 2503, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01b080a0-d726-4fd8-92b2-fe774c8c5646", + "name": "Spawner (2118)", + "location": [840, 2863, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4f7e8b07-f31c-418d-a11f-a45c0b4096e1", + "name": "Spawner (2118)", + "location": [600, 2511, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a76c8c9-5e59-4915-9f25-d9503f076b0d", + "name": "Spawner (2118)", + "location": [200, 2103, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ce072bf-6016-4902-bb6c-7cf6626eeb1f", + "name": "Spawner (2118)", + "location": [168, 1743, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da0f30cb-55c4-42a9-896a-1327277ed67e", + "name": "Spawner (2118)", + "location": [200, 559, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1e293bc0-3f8c-44b5-b5d3-e13f6c03353f", + "name": "Spawner (2118)", + "location": [184, 191, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abfabdf0-bf96-4776-8036-8bd8449fa042", + "name": "Spawner (2118)", + "location": [592, 223, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fafb6838-5696-495e-8633-ae7909d2607b", + "name": "Spawner (2118)", + "location": [496, 527, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8158ece9-f497-4858-a0a2-ce68e1bd9b55", + "name": "Spawner (2118)", + "location": [2400, 2399, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "def01738-95eb-4463-9025-57f58ab767c8", + "name": "Spawner (2118)", + "location": [3008, 1895, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1313a5ba-08e2-450f-850e-e3b8dd09d2ac", + "name": "Spawner (2118)", + "location": [2392, 2031, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1d3c81c-4063-4c1b-a855-bc98dc5f1c6d", + "name": "Spawner (2118)", + "location": [2624, 1775, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "103196d1-0c89-4383-9e0e-a0b01aaf3c20", + "name": "Spawner (2118)", + "location": [3408, 1407, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7d4b5771-7c1e-4bf1-a7ff-40815ccc6e43", + "name": "Spawner (2118)", + "location": [3256, 1031, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aacc2b81-5061-4474-af3a-3128191b5515", + "name": "Spawner (2118)", + "location": [2952, 1239, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2f9961f3-4539-4558-a08d-e55c0ffabb57", + "name": "Spawner (2118)", + "location": [2752, 1455, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d2e0d12-3e70-4572-8cae-ccc6f7bfd585", + "name": "Spawner (2118)", + "location": [2208, 1703, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c00499fb-21a8-48a6-9140-72e1b964da3d", + "name": "Spawner (2118)", + "location": [1866, 1798, -5], + "map": "Trammel", + "count": 22, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 175, + "walkingRange": 175, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 15, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 15, "probability": 100 }, + { "name": "Kraken", "maxCount": 15, "probability": 100 }, + { "name": "Dolphin", "maxCount": 6, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "15221ce6-7198-4de6-b750-6610f6680d51", + "name": "Spawner (2118)", + "location": [931, 141, -5], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "07b89a5e-cd89-4448-8b46-56bdd1d989a8", + "name": "Spawner (2118)", + "location": [3267, 1717, -5], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "10b4a195-d28e-4534-b058-ce0d08ad7ae4", + "name": "Spawner (2118)", + "location": [2395, 1405, -5], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7be74f9e-3d46-49c1-8845-f2891a31472b", + "name": "Spawner (2118)", + "location": [139, 1173, -5], + "map": "Trammel", + "count": 18, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 130, + "walkingRange": 130, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 13, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 13, "probability": 100 }, + { "name": "Kraken", "maxCount": 13, "probability": 100 }, + { "name": "Dolphin", "maxCount": 4, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6cdb8fc8-8406-4535-a2e1-7f663e171f39", + "name": "Spawner (2118)", + "location": [3123, 1485, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2191b9c5-c69e-4a83-bc9f-f17cced550b3", + "name": "Spawner (2118)", + "location": [3811, 109, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf8e1268-6e29-4feb-8ad5-4f7f2e0b81d0", + "name": "Spawner (2118)", + "location": [3931, 1757, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e68e3874-1188-4c5b-9909-4539e8119a58", + "name": "Spawner (2118)", + "location": [2931, 3245, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a02156e-b816-4c27-8dd0-f377e6378524", + "name": "Spawner (2118)", + "location": [2291, 3181, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "23637b7b-52d1-4ae2-95ff-e08be730be14", + "name": "Spawner (2118)", + "location": [979, 3997, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "301c54c5-7fe3-4538-903c-396311156077", + "name": "Spawner (2118)", + "location": [147, 3213, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd078e33-7679-49ae-bc54-4be36e1e3c61", + "name": "Spawner (2118)", + "location": [1891, 3597, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9576fcbe-ac4f-4382-931a-18d85664a5c4", + "name": "Spawner (2118)", + "location": [2971, 1605, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6ef0ce60-872f-47a8-b8c7-288de410fd37", + "name": "Spawner (2118)", + "location": [1580, 75, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9ab3a044-b6f4-4fda-b048-e4da84173f15", + "name": "Spawner (2118)", + "location": [4275, 1309, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9aac32c6-3e67-4413-9ded-df193f4cd205", + "name": "Spawner (2118)", + "location": [4667, 109, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc9c68d1-115b-4d9f-a75a-1c7b914f865d", + "name": "Spawner (2118)", + "location": [5003, 101, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d36c3ef-0186-4aa2-8248-e32fa525c5d1", + "name": "Spawner (2118)", + "location": [3603, 125, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0725865a-b679-4d54-ad18-8238b2eb6f38", + "name": "Spawner (2118)", + "location": [4112, 791, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5b4931fa-70ca-4abc-b62a-4496f8d6bd3b", + "name": "Spawner (2118)", + "location": [3891, 733, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "55c44ca9-d830-4a74-bb0f-e529edef11e1", + "name": "Spawner (2118)", + "location": [3672, 1535, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5af749ec-cde5-4635-a69c-8d9df732688c", + "name": "Spawner (2118)", + "location": [4363, 1981, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6ffba6b0-025d-4465-9bf8-1c7e22f8dea1", + "name": "Spawner (2118)", + "location": [4995, 1829, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8f72a6c-ac20-4b11-9f88-d7ac1fcc16d3", + "name": "Spawner (2118)", + "location": [4283, 2221, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f334a29c-42e6-4c51-a19a-9bdbd57efb70", + "name": "Spawner (2118)", + "location": [2008, 4007, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54c24f2b-a9b4-4a6b-a005-4974491cb4b8", + "name": "Spawner (2118)", + "location": [2320, 4007, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ecb155b-60d2-4de3-9c43-330d492797ac", + "name": "Spawner (2118)", + "location": [4955, 3997, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4707f57e-e053-4515-8905-ead9aab81c34", + "name": "Spawner (2118)", + "location": [4731, 3997, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6bee85a9-3484-41ed-bf30-bc94accd887c", + "name": "Spawner (2118)", + "location": [4307, 3965, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5a35e9a-e886-4acb-be41-17fbfc5c28d2", + "name": "Spawner (2118)", + "location": [2227, 3533, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "769bcacc-1b82-4198-b3c5-6f19c298f770", + "name": "Spawner (2118)", + "location": [1187, 3885, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9cf6fa1b-6813-491f-ab42-5308c43fe9f5", + "name": "Spawner (2118)", + "location": [731, 3989, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe923a99-b8c8-443c-993d-2925273bcb35", + "name": "Spawner (2118)", + "location": [491, 4013, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a3d212e-7f85-4ed1-84f6-e0cab9db1504", + "name": "Spawner (2118)", + "location": [107, 909, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7cceffb4-478d-44b1-a66f-c932e0c8d7e0", + "name": "Spawner (2118)", + "location": [1792, 79, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "71890746-952f-4b24-a473-76515b23e7e6", + "name": "Spawner (2118)", + "location": [1336, 79, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b258c234-07e5-4a5e-8083-e95cee0aa7c2", + "name": "Spawner (2118)", + "location": [4048, 63, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fbde8d9-735a-41d0-a2a9-e2d1a0a201e0", + "name": "Spawner (2118)", + "location": [4392, 823, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce84448b-cd5c-4db2-9623-807a216f2e65", + "name": "Spawner (2118)", + "location": [3520, 2263, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "71147c8d-bc3f-4d7f-a2e8-04b3cd99fc21", + "name": "Spawner (2118)", + "location": [3568, 3215, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d7e454a9-f662-438f-aedf-1f7813c34536", + "name": "Spawner (2118)", + "location": [4992, 3791, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b85b2928-f90d-4814-aee9-099661bb58bc", + "name": "Spawner (2118)", + "location": [4984, 3567, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abe19479-98b3-4b2d-a030-b0719a2016e4", + "name": "Spawner (2118)", + "location": [2072, 2527, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fe6e7d88-7bb3-4492-9aec-2d002562c69a", + "name": "Spawner (2118)", + "location": [2040, 3751, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1cbdc49-7c36-4f0a-9c71-0c7ac05f6519", + "name": "Spawner (2118)", + "location": [2288, 3767, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ef98baa5-fd8a-4c7e-abe6-2f9e2972e0e6", + "name": "Spawner (2118)", + "location": [496, 1879, -5], + "map": "Trammel", + "count": 13, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "DeepSeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 10, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 10, "probability": 100 }, + { "name": "Kraken", "maxCount": 10, "probability": 100 }, + { "name": "Dolphin", "maxCount": 2, "probability": 100 }, + { "name": "SeaHorse", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Shame.json b/Distribution/Data/Spawns/uoml/trammel/Shame.json new file mode 100644 index 000000000..4b8ffc7d1 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Shame.json @@ -0,0 +1,1690 @@ +[ + { + "type": "Spawner", + "guid": "09ded4e4-5965-4d2b-b5bd-d19dd22d1d78", + "name": "Spawner (2119)", + "location": [5404, 93, 10], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b6cce94b-7e83-4d5e-aa5a-620dd839887f", + "name": "Spawner (2119)", + "location": [5425, 58, 10], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 7, "probability": 100 }, + { "name": "Scorpion", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9b565f16-387d-48d3-b5c9-bb230a1f2175", + "name": "Spawner (2119)", + "location": [5387, 41, 20], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32fbcd32-5e8c-4061-b8d3-760447f9d436", + "name": "Spawner (2119)", + "location": [5394, 11, 30], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ae367fa-1674-4515-874c-639dc46b0983", + "name": "Spawner (2119)", + "location": [5474, 20, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cd11b1a6-10bb-4c7f-9eb9-fb89c49f1cff", + "name": "Spawner (2119)", + "location": [5477, 67, 20], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1d32e77e-c8dd-4626-81e5-ef5e71cc2c4d", + "name": "Spawner (2119)", + "location": [5468, 109, 35], + "map": "Trammel", + "count": 26, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 30, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 6, "probability": 100 }, + { "name": "Scorpion", "maxCount": 20, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f61afcad-3fea-45bb-8cb9-b7e278859277", + "name": "Spawner (2119)", + "location": [5423, 112, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f73d08aa-ffec-4132-accc-5eacbe40e8a8", + "name": "Spawner (2119)", + "location": [5468, 106, 35], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72ee6353-de85-4b30-bd7e-5b3bb8274c8e", + "name": "Spawner (2119)", + "location": [5434, 100, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "888398f2-e222-44e8-aa2c-85fee9df40b7", + "name": "Spawner (2119)", + "location": [5492, 56, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2237b125-9c62-49d2-8969-0a408f480409", + "name": "Spawner (2119)", + "location": [5416, 59, -15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6f6ac8eb-4d03-40b4-9a85-ecfadeae978d", + "name": "Spawner (2119)", + "location": [5400, 45, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d4a93223-fe95-4565-824d-a75653d2e466", + "name": "Spawner (2119)", + "location": [5448, 33, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "82bf7919-cc1a-43ba-b4cd-f8dc919ce842", + "name": "Spawner (2119)", + "location": [5437, 11, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ddd573db-303c-4e55-8b3d-276b8185ddc7", + "name": "Spawner (2119)", + "location": [5399, 11, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e740c0d1-2ab2-43c6-acda-fe9874666ac4", + "name": "Spawner (2119)", + "location": [5421, 117, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9633f2a9-53e9-477f-900d-ec62e3ad68d8", + "name": "Spawner (2119)", + "location": [5468, 100, 35], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7661fbb1-6302-4c11-9ee6-14cc5f24b79e", + "name": "Spawner (2119)", + "location": [5392, 10, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c18e446d-8c84-4f2d-8b78-a67767b70920", + "name": "Spawner (2119)", + "location": [5445, 12, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6a7ec713-9962-445d-90c4-78bb4424b61c", + "name": "Spawner (2119)", + "location": [5404, 43, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7d79079a-6f9c-479b-86fc-46e2a5361c83", + "name": "Spawner (2119)", + "location": [5443, 36, -10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4ec81dd8-9d60-4705-81bd-8cd7e50ba6c6", + "name": "Spawner (2119)", + "location": [5402, 58, -20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ac52c146-adc0-4501-84c0-c9d38f301dce", + "name": "Spawner (2119)", + "location": [5493, 51, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f1141039-567b-499e-b3e9-4749a47f6ced", + "name": "Spawner (2119)", + "location": [5434, 89, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "079c93fb-f739-40a5-a21d-0a4aba793bc8", + "name": "Spawner (2119)", + "location": [5575, 16, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5b5cc1fc-cbae-4895-b1f8-f05ee581851c", + "name": "Spawner (2119)", + "location": [5615, 18, 10], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "DullCopperElemental", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d1a66cc8-5ebf-4a29-9c4d-7724f82d2db4", + "name": "Spawner (2119)", + "location": [5568, 36, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bd0003c3-3bd7-43e1-a8aa-f0518996d54a", + "name": "Spawner (2119)", + "location": [5549, 60, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 3, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a838c4b-48f3-4181-832e-c77b2067e8c2", + "name": "Spawner (2119)", + "location": [5549, 79, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3ed65348-422d-4f40-86e9-14b3cb8fa14b", + "name": "Spawner (2119)", + "location": [5595, 82, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "001ee924-4432-49b8-b36b-b617d376c561", + "name": "Spawner (2119)", + "location": [5603, 93, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 25, + "entries": [{ "name": "DullCopperElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "67c32364-17d1-4910-9dc2-0c5f849e3c3a", + "name": "Spawner (2119)", + "location": [5603, 116, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "DullCopperElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6e6b46bf-7fdd-44c9-a7be-4f5d4a5ad13f", + "name": "Spawner (2119)", + "location": [5536, 119, -5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4df550f5-2395-4415-bce2-780cef42016e", + "name": "Spawner (2119)", + "location": [5546, 118, -5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "edc5910f-203a-4888-83a4-cf67cea2cc43", + "name": "Spawner (2119)", + "location": [5606, 25, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "953785e9-e0b7-4464-bd7d-9fb1b2f6b535", + "name": "Spawner (2119)", + "location": [5570, 118, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "97536e41-0cd7-4692-884c-25dd30d94b99", + "name": "Spawner (2119)", + "location": [5620, 21, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ad47daba-e2ba-49ba-a625-23f6b5803519", + "name": "Spawner (2119)", + "location": [5572, 113, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e7ae4e7b-0861-4ff2-b25d-bf84299ac1e0", + "name": "Spawner (2119)", + "location": [5546, 113, -5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d9160a0d-2b7d-479e-a737-6dd383189d54", + "name": "Spawner (2119)", + "location": [5474, 139, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "baadf71e-a920-4903-8852-0c06cb1cf279", + "name": "Spawner (2119)", + "location": [5477, 183, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69e9d6d7-5fa3-40e6-bae3-5eb077de6a00", + "name": "Spawner (2119)", + "location": [5469, 211, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3b03d43c-d591-4845-bf47-815b88c21c29", + "name": "Spawner (2119)", + "location": [5441, 187, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EvilMage", "maxCount": 5, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6cd6d9d8-95d2-4517-bce2-bc0172947842", + "name": "Spawner (2119)", + "location": [5412, 164, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "314487e0-1198-40a4-b0c9-d37c010700b4", + "name": "Spawner (2119)", + "location": [5411, 163, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd5f3ab6-1ba5-4db0-8598-6b7d91486ac3", + "name": "Spawner (2119)", + "location": [5412, 201, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "AirElemental", "maxCount": 2, "probability": 100 }, + { "name": "EarthElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0e358ad-1171-4102-a7fc-d6c934dca6af", + "name": "Spawner (2119)", + "location": [5412, 200, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "PoisonElemental", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ffbfcdb1-28bb-472c-9886-e2f86381c699", + "name": "Spawner (2119)", + "location": [5394, 230, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8115af0b-b4a8-4c92-8fd6-a3e6353257e8", + "name": "Spawner (2119)", + "location": [5505, 180, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "ElderGazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da924d39-dbec-4eab-87de-90db6d447919", + "name": "Spawner (2119)", + "location": [5527, 210, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "DullCopperElemental", "maxCount": 4, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2771037f-84f2-43da-98d9-db67d02d1431", + "name": "Spawner (2119)", + "location": [5557, 222, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 2, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 2, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "773e4496-e32b-4ab8-aac2-969f0c927494", + "name": "Spawner (2119)", + "location": [5577, 194, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EvilMage", "maxCount": 5, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f0f2698-3569-4115-8e04-3792d50dfabc", + "name": "Spawner (2119)", + "location": [5594, 182, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eb4b7903-2927-4029-b565-8ebdc5da5b44", + "name": "Spawner (2119)", + "location": [5571, 158, -10], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 4, "probability": 100 }, + { "name": "Scorpion", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d750d4b4-310a-461d-9288-ddc0658fe807", + "name": "Spawner (2119)", + "location": [5593, 141, 10], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d07b5d4b-bf25-46aa-b179-37265ef7c5e5", + "name": "Spawner (2119)", + "location": [5398, 148, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ac1ca0f0-ce5f-4aca-b1b5-6b80ea586a4e", + "name": "Spawner (2119)", + "location": [5545, 153, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c8b77b20-a7ba-4791-b5c3-feda828d6c7f", + "name": "Spawner (2119)", + "location": [5594, 139, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "08ada2f5-25a8-4964-a067-c74348006ff5", + "name": "Spawner (2119)", + "location": [5604, 166, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a5729af1-b157-4483-a1c3-6ec1ca5c057c", + "name": "Spawner (2119)", + "location": [5608, 190, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "46f4e0e9-6b99-4e2e-a7ca-2a4c8a429f52", + "name": "Spawner (2119)", + "location": [5605, 202, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "719a5843-5c2b-4fe2-9606-5cfe1bea7fcc", + "name": "Spawner (2119)", + "location": [5439, 137, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "eeed29d5-5317-4534-ad38-3c0992c39684", + "name": "Spawner (2119)", + "location": [5390, 145, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3bed0f7f-ea30-4440-8a18-93377ac6ce62", + "name": "Spawner (2119)", + "location": [5456, 157, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "521558b3-b945-413e-b3fb-3d8ec3498ce2", + "name": "Spawner (2119)", + "location": [5410, 170, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8a352d4f-c130-4c91-b8a1-0beded1ed79a", + "name": "Spawner (2119)", + "location": [5415, 189, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e7f17883-f57f-40fd-a1dd-140010fb9699", + "name": "Spawner (2119)", + "location": [5396, 226, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adbef39f-4d4f-4b24-ac09-16b247e1aa60", + "name": "Spawner (2119)", + "location": [5553, 146, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab9458c7-cafb-4752-813c-822bb1dbc3a6", + "name": "Spawner (2119)", + "location": [5595, 147, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4cf19ca1-b747-42ff-b6ba-647eb155a930", + "name": "Spawner (2119)", + "location": [5615, 173, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8ad8e084-ea10-46e2-9767-fa71ee0c5add", + "name": "Spawner (2119)", + "location": [5616, 184, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a30b9f6a-dcb9-4e6b-8968-61dca09727f8", + "name": "Spawner (2119)", + "location": [5456, 143, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f026769e-09f2-46db-bc0a-e5294bee1910", + "name": "Spawner (2119)", + "location": [5432, 158, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "481e0770-ef9b-4f8c-975f-06f954ede9d8", + "name": "Spawner (2119)", + "location": [5474, 176, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e1baaf02-08a2-48f0-995a-e0f605750a9c", + "name": "Spawner (2119)", + "location": [5406, 177, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "509eea60-a86e-406f-80dd-32310ad518cd", + "name": "Spawner (2119)", + "location": [5405, 185, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a9e6fa28-2c45-4b1c-bcda-19a702017cc2", + "name": "Spawner (2119)", + "location": [5405, 200, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7e839e78-3211-4cac-9773-fa08866d4026", + "name": "Spawner (2119)", + "location": [5403, 235, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "10bd98c5-918f-4da9-9073-5bd147f09113", + "name": "Spawner (2119)", + "location": [5475, 190, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aafa33d0-8132-46b6-88d5-fc65bef565ef", + "name": "Spawner (2119)", + "location": [5875, 44, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f97a9dbc-694a-4fce-8ff9-5f6bb478274c", + "name": "Spawner (2119)", + "location": [5856, 107, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e8968c9-b416-4792-9b0c-3670a4b23695", + "name": "Spawner (2119)", + "location": [5845, 57, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7492a94d-efd9-4505-a057-1e260cc4adbb", + "name": "Spawner (2119)", + "location": [5828, 42, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "725c89d8-4f6d-429b-ac19-f467eaab6f3d", + "name": "Spawner (2119)", + "location": [5819, 50, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "EvilMage", "maxCount": 1, "probability": 100 }, + { "name": "EvilMageLord", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d197175-de78-46d5-a9ce-23947f16d376", + "name": "Spawner (2119)", + "location": [5818, 80, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "ElderGazer", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21d404c6-8dcc-49a1-b03c-ae4ef9898b55", + "name": "Spawner (2119)", + "location": [5804, 12, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "AirElemental", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "991c1f24-db8a-4a4e-8620-1d07266240fb", + "name": "Spawner (2119)", + "location": [5721, 12, 10], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "BloodElemental", "maxCount": 8, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f216166d-1cad-4be4-9bc0-37d09a0a2d6a", + "name": "Spawner (2119)", + "location": [5661, 16, -10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "FireElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a96bff59-e6a9-4ab9-872d-bfe06a6d55fd", + "name": "Spawner (2119)", + "location": [5649, 101, 10], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "EarthElemental", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e2e06115-367a-4fde-9f22-fbcbea8f9bb8", + "name": "Spawner (2119)", + "location": [5684, 117, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "BloodElemental", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a1ff514a-58c1-4173-9577-188a2a41b3d5", + "name": "Spawner (2119)", + "location": [5724, 118, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "EarthElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d37c623-d85a-4986-878b-3db1485a3932", + "name": "Spawner (2119)", + "location": [5760, 99, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "AcidElemental", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "Scorpion", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "29b1dfff-7701-4436-9bf1-9b31e8ee667c", + "name": "Spawner (2119)", + "location": [5711, 97, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a1cd5afc-94a1-4674-ab44-62265f6978dd", + "name": "Spawner (2119)", + "location": [5752, 44, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "SeaSerpent", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d97533a-8934-4eff-95f5-1ade2f51a092", + "name": "Spawner (2119)", + "location": [5708, 43, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [ + { "name": "Kraken", "maxCount": 1, "probability": 100 }, + { "name": "WaterElemental", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "69104c32-673c-44d2-9326-7d4d03633df3", + "name": "Spawner (2119)", + "location": [5730, 90, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "30d586e5-c20c-44c1-af6c-5b73b505fdf6", + "name": "Spawner (2119)", + "location": [5723, 73, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e9fbf301-1258-4a79-b9e7-4d63acc98240", + "name": "Spawner (2119)", + "location": [5715, 61, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "54528337-9b35-44af-80d4-ef8176ac81d2", + "name": "Spawner (2119)", + "location": [5701, 60, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "19050742-7cad-48c2-b1bd-1a6da29d076c", + "name": "Spawner (2119)", + "location": [5817, 83, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "18fc907b-29dc-48b4-ad1c-7dbc4d94c838", + "name": "Spawner (2119)", + "location": [5730, 93, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "885a80e9-bfa3-4078-8ae6-a97eea5abb1e", + "name": "Spawner (2119)", + "location": [5723, 77, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "da517822-6aee-42d2-955e-61f32f64248c", + "name": "Spawner (2119)", + "location": [5717, 60, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a0d04624-3586-4efd-b7d3-2ec4258544bd", + "name": "Spawner (2119)", + "location": [5699, 61, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b3a3061a-1a09-41cf-bb40-2d6503a76301", + "name": "Spawner (2119)", + "location": [5817, 78, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "008c5583-538b-4a11-a7c0-3fbd453b9a25", + "name": "Spawner (2119)", + "location": [5733, 91, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "490f88ed-d12a-419f-9c25-02766dd7acbe", + "name": "Spawner (2119)", + "location": [5725, 78, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "be48657e-5a30-4917-9f02-9eeb8e55430e", + "name": "Spawner (2119)", + "location": [5715, 58, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "22645272-781c-4559-9897-622f9c8c78cf", + "name": "Spawner (2119)", + "location": [5698, 57, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b9b42bc5-e1f5-4b91-924c-88c12a7ae994", + "name": "Spawner (2119)", + "location": [5733, 93, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d390a29-becb-4f59-b28d-a07fec3f5f60", + "name": "Spawner (2119)", + "location": [5727, 72, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "56012eb5-81d3-4912-9910-4a08a69ac977", + "name": "Spawner (2119)", + "location": [5717, 58, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bed0b85f-695c-441c-ba0e-dcb292dc561f", + "name": "Spawner (2119)", + "location": [5701, 58, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 1, "probability": 100 }] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/SolenHive.json b/Distribution/Data/Spawns/uoml/trammel/SolenHive.json new file mode 100644 index 000000000..8a16eea09 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/SolenHive.json @@ -0,0 +1,605 @@ +[ + { + "type": "Spawner", + "guid": "80a9c1da-dd8f-490b-9742-3f044df6342a", + "name": "Spawner (2120)", + "location": [5670, 1807, 4], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 25, + "walkingRange": 30, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e2dfbc83-aee1-4e9d-9320-df1cea768140", + "name": "Spawner (2120)", + "location": [5673, 1846, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "245aab6c-8a39-4f1b-9aaf-eaafc8117f58", + "name": "Spawner (2120)", + "location": [5693, 1834, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72ee4a95-09bc-4efa-baf4-4a9c75c7b397", + "name": "Spawner (2120)", + "location": [5704, 1807, 4], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "23a8fa37-4b98-4d53-b5f8-df35c72b6ff2", + "name": "Spawner (2120)", + "location": [5722, 1820, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5dd054bd-aabe-4f16-8029-1c766ba83308", + "name": "Spawner (2120)", + "location": [5709, 1874, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 50, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "196acab9-0c1f-491f-a11e-7aabb7c56648", + "name": "Spawner (2120)", + "location": [5681, 1886, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 50, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ab7b16cd-6111-412a-9a29-27d1da389076", + "name": "Spawner (2120)", + "location": [5756, 1857, 4], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 50, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "274ebd5a-249f-4dd9-92ad-25dcb95d8d06", + "name": "Spawner (2120)", + "location": [5912, 1800, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "caba66b7-9425-4fd5-b2f0-576ef17354da", + "name": "Spawner (2120)", + "location": [5920, 1842, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "821a5e93-b1a5-4fef-9ef0-45f6833fa68d", + "name": "Spawner (2120)", + "location": [5887, 1840, -18], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7b61d8d6-669b-45e7-b478-7df484b954d6", + "name": "Spawner (2120)", + "location": [5847, 1819, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "97e600cc-7679-4998-aa88-d6124f061421", + "name": "Spawner (2120)", + "location": [5829, 1798, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f5300b36-01bc-48bd-842d-a8d57f03cdd1", + "name": "Spawner (2120)", + "location": [5873, 1798, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }, + { "name": "RedSolenQueen", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d325fbe4-9db7-4583-8eb3-dbabaefba8fe", + "name": "Spawner (2120)", + "location": [5782, 1793, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e5564c6c-b83e-46be-8af8-c35024c13df3", + "name": "Spawner (2120)", + "location": [5799, 1857, 5], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3db4e70c-3423-4aaf-b80c-656c9b657c69", + "name": "Spawner (2120)", + "location": [5815, 1881, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "204955db-7ad2-40bc-a4e4-09a07d091c72", + "name": "Spawner (2120)", + "location": [5831, 1910, 3], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Beetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "43fe7167-b65e-4e21-ae5f-d52b5c58607c", + "name": "Spawner (2120)", + "location": [5718, 2024, 4], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f780f0c8-a550-427a-99a8-1834f747a867", + "name": "Spawner (2120)", + "location": [5776, 2019, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c49c6762-912b-4609-9df3-c33cb865e2b6", + "name": "Spawner (2120)", + "location": [5782, 2021, -11], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "75a0efe5-1432-4b69-abeb-69058db48d31", + "name": "Spawner (2120)", + "location": [5767, 1962, 2], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 50, + "entries": [{ "name": "DreadSpider", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "21746fe5-d247-4351-b3fe-e58cfd3db9b2", + "name": "Spawner (2120)", + "location": [5671, 1979, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 30, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3e0b4f9c-116a-44ad-9cf5-fc8f9179c02c", + "name": "Spawner (2120)", + "location": [5703, 1991, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 35, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed1dd6c0-4260-415d-8ef1-4185452bdfa3", + "name": "Spawner (2120)", + "location": [5699, 1921, 2], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "22b0f532-20f7-4b1e-9807-a8ffcd5732d7", + "name": "Spawner (2120)", + "location": [5748, 1923, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "beafc393-f412-4d90-8816-c00a26814f92", + "name": "Spawner (2120)", + "location": [5736, 1939, 7], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "RedSolenQueen", "maxCount": 1, "probability": 100 }, + { "name": "RedSolenWorker", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "71a71fe3-081c-49d4-9eed-8a280eb9fe95", + "name": "Spawner (2120)", + "location": [5876, 2018, 3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4ecaeb0d-e3c7-4680-9859-c2879f71cd2c", + "name": "Spawner (2120)", + "location": [5856, 2018, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8b45fed3-db59-49c8-b477-b5d76f1c5fd2", + "name": "Spawner (2120)", + "location": [5838, 2019, 3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWorker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "495718b4-baad-4872-8d48-07cec97e5fda", + "name": "Spawner (2120)", + "location": [5910, 1991, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 25, + "entries": [{ "name": "RedSolenWorker", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "16d57106-d22a-48c5-a704-96675ab47670", + "name": "Spawner (2120)", + "location": [5837, 1992, -2], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Beetle", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "373d83af-8b04-46f3-9602-7199c792fc9f", + "name": "Spawner (2120)", + "location": [5863, 1958, 2], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50b74a27-fb2a-4890-a4cf-6309a7c1a2f2", + "name": "Spawner (2120)", + "location": [5884, 1920, -15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "72980a1c-b916-4b7f-95d3-5a66b4f47fca", + "name": "Spawner (2120)", + "location": [5825, 1968, 4], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2c2b2ec3-aabe-4bce-b80a-21292660b372", + "name": "Spawner (2120)", + "location": [5855, 1932, 3], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "RedSolenWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d1fce2c8-e0e7-454e-9546-d648f27a2bc3", + "name": "Spawner (2120)", + "location": [5798, 1963, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 35, + "entries": [{ "name": "RedSolenWorker", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9b557083-7599-44ef-87f4-f8dff1b09c7e", + "name": "Spawner (2120)", + "location": [5665, 1868, 11], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [{ "name": "AntLion", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ed6f6b69-45b4-4591-9865-f7d03509aa8a", + "name": "Spawner (2120)", + "location": [5707, 1843, -15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "AntLion", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7c89c785-3d76-4b10-a309-20ff87727f55", + "name": "Spawner (2120)", + "location": [3218, 502, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "BlackSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "BlackSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d575dc9a-eb61-43f3-a8c1-758e59010f50", + "name": "Spawner (2120)", + "location": [1940, 3261, 1], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "BlackSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "BlackSolenInfiltratorWarrior", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bcf0b17e-8f7c-4c2e-b13b-a979643031fc", + "name": "Spawner (2120)", + "location": [1361, 894, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 8, + "entries": [ + { "name": "BlackSolenInfiltratorQueen", "maxCount": 1, "probability": 100 }, + { "name": "BlackSolenInfiltratorWarrior", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/TerathanKeep.json b/Distribution/Data/Spawns/uoml/trammel/TerathanKeep.json new file mode 100644 index 000000000..6fa66ea0d --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/TerathanKeep.json @@ -0,0 +1,638 @@ +[ + { + "type": "Spawner", + "guid": "0b2133ab-a3f1-4857-80a2-c41233c499fe", + "name": "Spawner (2121)", + "location": [5296, 1561, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88aac11e-5718-4d06-be82-14b956899e5c", + "name": "Spawner (2121)", + "location": [5296, 1562, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fab17e79-c410-496f-8cf8-b7b07363b973", + "name": "Spawner (2121)", + "location": [5297, 1565, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0945ec2d-d81a-4b3e-9c49-4d67c615a056", + "name": "Spawner (2121)", + "location": [5342, 1553, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5f285bad-aaa5-40d3-add2-5b550f38e408", + "name": "Spawner (2121)", + "location": [5342, 1552, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81220dc9-b072-44ff-8b29-3257236c2b25", + "name": "Spawner (2121)", + "location": [5343, 1551, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9fb21a92-1084-4819-a2b2-15e4887c701c", + "name": "Spawner (2121)", + "location": [5334, 1613, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "031eb48a-ab35-4358-b90b-4c6f50b96a38", + "name": "Spawner (2121)", + "location": [5334, 1614, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "656b11cd-88d3-40fb-b9dc-f830f7e22ae5", + "name": "Spawner (2121)", + "location": [5335, 1615, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3f984984-b04b-4635-8685-0e4e2eae07a7", + "name": "Spawner (2121)", + "location": [5348, 1552, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "52c5db98-5de5-4d55-9a26-85f3bc461810", + "name": "Spawner (2121)", + "location": [5345, 1551, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d6923469-ec40-4861-a6e7-ef21b07024a7", + "name": "Spawner (2121)", + "location": [5344, 1757, -125], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3e3c0553-182a-40da-b633-ef4bf2c42425", + "name": "Spawner (2121)", + "location": [5345, 1757, -125], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cff18fb7-c66e-4c05-bb2e-01f883d660ce", + "name": "Spawner (2121)", + "location": [5346, 1756, -125], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "50432a43-ccb4-4064-8336-06ea08e4b61e", + "name": "Spawner (2121)", + "location": [5335, 1706, -70], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1bd125f-c580-49ef-be95-922862466bad", + "name": "Spawner (2121)", + "location": [5335, 1707, -70], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4cc200d3-c1ba-4433-92ba-fa48868ee0f8", + "name": "Spawner (2121)", + "location": [5336, 1708, -70], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6fe29b7c-0452-4412-a1b9-d0c30c0721fc", + "name": "Spawner (2121)", + "location": [5270, 1695, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b116f136-d3a6-43fa-b111-ef38b6ff3efa", + "name": "Spawner (2121)", + "location": [5270, 1694, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "245b8698-8502-4d28-a477-04532306bfd3", + "name": "Spawner (2121)", + "location": [5271, 1693, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9238348-7288-4d4f-97c2-9df88a2981b3", + "name": "Spawner (2121)", + "location": [5365, 1707, -71], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2ce3592b-1ef9-4c42-8731-660451045829", + "name": "Spawner (2121)", + "location": [5363, 1709, -75], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7c3950c7-e1df-4308-905f-f987fe5c9b99", + "name": "Spawner (2121)", + "location": [5176, 1702, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf601676-5f93-4b75-ae89-ac1b1a4774f6", + "name": "Spawner (2121)", + "location": [5176, 1701, 2], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "45a4e9ce-7994-4faa-bca4-adaf4e330450", + "name": "Spawner (2121)", + "location": [5177, 1700, 1], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c337921-7949-4be3-bd19-9353b66560ae", + "name": "Spawner (2121)", + "location": [5132, 1624, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "27edcd48-0444-44f7-a6d2-dbf5e2bb08f1", + "name": "Spawner (2121)", + "location": [5132, 1623, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3d9ecd4-8f8e-42a2-9029-b585a881657f", + "name": "Spawner (2121)", + "location": [5134, 1625, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdcd9c84-b005-4f1e-8c9f-0af0f8800d38", + "name": "Spawner (2121)", + "location": [5160, 1586, -15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f27e8d0a-6a88-40e1-b02b-97b2c550065c", + "name": "Spawner (2121)", + "location": [5163, 1585, -15], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "286c764e-4d51-42fa-97d5-86fc2f4902f8", + "name": "Spawner (2121)", + "location": [5161, 1584, -15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1a9ae4d0-2900-4a34-a4b0-72d5ed252665", + "name": "Spawner (2121)", + "location": [5200, 1565, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Balron", "maxCount": 1, "probability": 100 }, + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 1, "probability": 100 }, + { "name": "Nightmare", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b0fde845-3212-41e0-8f65-c457b0570a53", + "name": "Spawner (2121)", + "location": [5201, 1566, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "OphidianArchmage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianKnight", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMage", "maxCount": 1, "probability": 100 }, + { "name": "OphidianWarrior", "maxCount": 1, "probability": 100 }, + { "name": "OphidianMatriarch", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8af3286d-17b3-4447-8292-ee407d69f826", + "name": "Spawner (2121)", + "location": [5200, 1568, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "TerathanAvenger", "maxCount": 1, "probability": 100 }, + { "name": "TerathanDrone", "maxCount": 1, "probability": 100 }, + { "name": "TerathanMatriarch", "maxCount": 1, "probability": 100 }, + { "name": "TerathanWarrior", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/TownsLife.json b/Distribution/Data/Spawns/uoml/trammel/TownsLife.json index ea5d54f28..33a82a9b1 100644 --- a/Distribution/Data/Spawns/uoml/trammel/TownsLife.json +++ b/Distribution/Data/Spawns/uoml/trammel/TownsLife.json @@ -1,6 +1,635 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "bb7d8052-56aa-48cc-8d9a-31d4d85c0d81", + "name": "Spawner (2122)", + "location": [5249, 228, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [ + { "name": "Dragon", "maxCount": 1, "probability": 100 }, + { "name": "Drake", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1218cc8e-4200-42cc-99ee-b27dd396e3b4", + "name": "Spawner (2122)", + "location": [5164, 205, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "LichLord", "maxCount": 1, "probability": 100 }, + { "name": "Lich", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f474b7be-1817-4b1d-a492-42df4790cedb", + "name": "Spawner (2122)", + "location": [5133, 150, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [{ "name": "Daemon", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1d5e571c-c1e3-45d4-a374-04b465b36460", + "name": "Spawner (2122)", + "location": [1550, 1658, 26], + "map": "Trammel", + "count": 60, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 120, + "walkingRange": 120, + "entries": [ + { "name": "Bird", "maxCount": 15, "probability": 100 }, + { "name": "Cat", "maxCount": 15, "probability": 100 }, + { "name": "Dog", "maxCount": 15, "probability": 100 }, + { "name": "Rat", "maxCount": 15, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d7f7b008-ad02-4eae-8d4a-8f9163288631", + "name": "Spawner (2122)", + "location": [1319, 1810, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Cow", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b3d2bb40-d3d1-4ec9-9a5c-b20b737de066", + "name": "Spawner (2122)", + "location": [1319, 1821, 0], + "map": "Trammel", + "count": 7, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 10, + "entries": [{ "name": "Cow", "maxCount": 7, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "86f2e9e8-9ebb-44dc-8372-dc6edfb41847", + "name": "Spawner (2122)", + "location": [1340, 1790, 15], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ee6ae89b-6de4-489c-bc6c-62e43810a6a9", + "name": "Spawner (2122)", + "location": [1339, 1788, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4dd3ea55-9eb8-4e5c-b31a-04a071ae46ab", + "name": "Spawner (2122)", + "location": [1217, 1825, 0], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "12aababc-881a-4724-a9f5-b99ed51f3fe0", + "name": "Spawner (2122)", + "location": [1216, 1823, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5396da5f-3f83-46e9-bd36-541a1af0972e", + "name": "Spawner (2122)", + "location": [1186, 1633, 0], + "map": "Trammel", + "count": 28, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Bird", "maxCount": 7, "probability": 100 }, + { "name": "Cat", "maxCount": 7, "probability": 100 }, + { "name": "Dog", "maxCount": 7, "probability": 100 }, + { "name": "Rat", "maxCount": 7, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3afe2f59-7cc4-482f-8d58-341c919c8fab", + "name": "Spawner (2122)", + "location": [1185, 1632, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [{ "name": "Chicken", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "84e06a55-fdcb-4448-9560-9a71ae7778fb", + "name": "Spawner (2122)", + "location": [1937, 2760, 10], + "map": "Trammel", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 120, + "walkingRange": 120, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "66a2657f-2e25-46fb-aca9-585773bd5646", + "name": "Spawner (2122)", + "location": [608, 2195, 0], + "map": "Trammel", + "count": 32, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Bird", "maxCount": 8, "probability": 100 }, + { "name": "Cat", "maxCount": 8, "probability": 100 }, + { "name": "Dog", "maxCount": 8, "probability": 100 }, + { "name": "Rat", "maxCount": 8, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "290150ac-103d-4117-a891-c09072cfb116", + "name": "Spawner (2122)", + "location": [813, 2164, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd871bd2-e5c3-49ed-9b4e-73021199efe9", + "name": "Spawner (2122)", + "location": [818, 2269, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa850e39-3bd1-43cf-9b34-c8756a3eeb2f", + "name": "Spawner (2122)", + "location": [830, 2353, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 3, "probability": 100 }, + { "name": "Sheep", "maxCount": 3, "probability": 100 }, + { "name": "Cow", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c74c0ea9-7dae-43fd-99ab-1de27e40d2f8", + "name": "Spawner (2122)", + "location": [536, 962, 0], + "map": "Trammel", + "count": 80, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 160, + "walkingRange": 160, + "entries": [ + { "name": "Bird", "maxCount": 20, "probability": 100 }, + { "name": "Cat", "maxCount": 20, "probability": 100 }, + { "name": "Dog", "maxCount": 20, "probability": 100 }, + { "name": "Rat", "maxCount": 20, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "52df17e8-b859-401f-b3ad-d49035f45428", + "name": "Spawner (2122)", + "location": [676, 1178, 0], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "519581dd-727c-42fe-9b48-854998e02194", + "name": "Spawner (2122)", + "location": [570, 1099, 0], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "98ed6d30-11e2-4a50-9ca8-4e00cc255d17", + "name": "Spawner (2122)", + "location": [675, 939, 0], + "map": "Trammel", + "count": 15, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 15, + "entries": [{ "name": "Sheep", "maxCount": 15, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "75e5fd43-c65f-4862-b96b-be5fa6e722aa", + "name": "Spawner (2122)", + "location": [382, 1192, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "97c5b1a1-5028-44ee-914e-c30b62486126", + "name": "Spawner (2122)", + "location": [560, 1239, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81a21d4b-6f8a-44ae-a9e4-aca7eda1afa7", + "name": "Spawner (2122)", + "location": [688, 1007, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c73117b7-d4f3-4fee-926d-b09a412dfa40", + "name": "Spawner (2122)", + "location": [2242, 1197, 0], + "map": "Trammel", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ac7bee3c-854c-4da1-8676-87cfc0f100e6", + "name": "Spawner (2122)", + "location": [2223, 1151, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Chicken", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "66b8967e-4a25-462a-bac2-70b7c6ee2b08", + "name": "Spawner (2122)", + "location": [2939, 746, 2], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "253c3269-5141-4306-ab24-984c74677fa3", + "name": "Spawner (2122)", + "location": [2904, 906, 0], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "827b225e-094a-4fe0-80ef-e4ae2e2057c6", + "name": "Spawner (2122)", + "location": [2481, 542, 0], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "be3e5a0a-6330-47c5-a254-0e0270696f3c", + "name": "Spawner (2122)", + "location": [2472, 434, 15], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "45807a8e-eb86-4b7a-a592-a4d04b1c024f", + "name": "Spawner (2122)", + "location": [2566, 622, 0], + "map": "Trammel", + "count": 12, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 3, "probability": 100 }, + { "name": "Cat", "maxCount": 3, "probability": 100 }, + { "name": "Dog", "maxCount": 3, "probability": 100 }, + { "name": "Rat", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb452f73-c524-4b3b-ac31-861a5ae7c30e", + "name": "Spawner (2122)", + "location": [1154, 3639, 0], + "map": "Trammel", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b058d9e8-aa3b-4410-a984-736338f185e5", + "name": "Spawner (2122)", + "location": [1127, 3583, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2ac8567f-97e0-4553-aefd-04404e6fa403", + "name": "Spawner (2122)", + "location": [1183, 3607, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0596ff5d-53a1-4384-978a-11ec521c44ec", + "name": "Spawner (2122)", + "location": [1400, 3775, 0], + "map": "Trammel", + "count": 40, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 10, "probability": 100 }, + { "name": "Cat", "maxCount": 10, "probability": 100 }, + { "name": "Dog", "maxCount": 10, "probability": 100 }, + { "name": "Rat", "maxCount": 10, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8beee950-860e-45be-af5d-437fdd2b5eeb", + "name": "Spawner (2122)", + "location": [1435, 4000, 0], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0dc3b704-8d6e-468e-ad7e-61b6097c289b", + "name": "Spawner (2122)", + "location": [2972, 3436, 15], + "map": "Trammel", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "1ddbb313-bf13-4c9e-9116-3e67c835fbb9", "name": "Spawner (2122)", "location": [3674, 2624, 0], @@ -19,7 +648,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "988e7381-2448-4d37-b763-714f1078540b", "name": "Spawner (2122)", "location": [3726, 2647, 0], @@ -30,12 +659,10 @@ "team": 0, "homeRange": 25, "walkingRange": 25, - "entries": [ - { "name": "Chicken", "maxCount": 10, "probability": 100 } - ] + "entries": [{ "name": "Chicken", "maxCount": 10, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "b52ecefe-4f07-4bcd-9870-d01a0a1a41be", "name": "Spawner (2122)", "location": [3652, 2511, 0], @@ -52,5 +679,460 @@ { "name": "Dog", "maxCount": 4, "probability": 100 }, { "name": "Rat", "maxCount": 4, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "562f9c3f-4db0-4c6c-831d-83daf19d53ce", + "name": "Spawner (2122)", + "location": [3714, 2205, 20], + "map": "Trammel", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0651efe3-7bee-409d-9934-7d686aabac52", + "name": "Spawner (2122)", + "location": [3717, 2111, 20], + "map": "Trammel", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1c4cdc18-06dc-4ecf-9f82-e5a723220ee3", + "name": "Spawner (2122)", + "location": [2682, 2118, 0], + "map": "Trammel", + "count": 20, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 5, "probability": 100 }, + { "name": "Cat", "maxCount": 5, "probability": 100 }, + { "name": "Dog", "maxCount": 5, "probability": 100 }, + { "name": "Rat", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "145f69d9-8fb7-4130-85d1-591799b5b309", + "name": "Spawner (2122)", + "location": [2687, 2218, 0], + "map": "Trammel", + "count": 16, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Bird", "maxCount": 4, "probability": 100 }, + { "name": "Cat", "maxCount": 4, "probability": 100 }, + { "name": "Dog", "maxCount": 4, "probability": 100 }, + { "name": "Rat", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95de32e9-060f-4776-ac71-d112526ec4b6", + "name": "Spawner (2122)", + "location": [3699, 1238, 0], + "map": "Trammel", + "count": 72, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 160, + "walkingRange": 160, + "entries": [ + { "name": "Bird", "maxCount": 18, "probability": 100 }, + { "name": "Cat", "maxCount": 18, "probability": 100 }, + { "name": "Dog", "maxCount": 18, "probability": 100 }, + { "name": "Rat", "maxCount": 18, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c7f550f6-bf86-41ff-86fe-406006dc60bb", + "name": "Spawner (2122)", + "location": [4438, 1115, 0], + "map": "Trammel", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 70, + "walkingRange": 70, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e763cbc-b631-4c4c-8594-1472369cd6f7", + "name": "Spawner (2122)", + "location": [4634, 1302, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1208aae8-e36f-4170-97bf-76ebb6bebf09", + "name": "Spawner (2122)", + "location": [4567, 1476, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 3, + "walkingRange": 3, + "entries": [{ "name": "Chicken", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1378a58c-09d4-4940-81fa-209f75870791", + "name": "Spawner (2122)", + "location": [4422, 1451, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "Chicken", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "74e17b0b-5fe4-4965-b55c-787441371f56", + "name": "Spawner (2122)", + "location": [7036, 409, 12], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ae9c74fb-d8eb-4df7-aa22-412058583e8b", + "name": "Spawner (2122)", + "location": [7048, 424, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4c814d05-058a-44a7-ad86-3e920797bc9e", + "name": "Spawner (2122)", + "location": [7081, 381, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "42c9b654-4a10-4c16-8d34-487646e4c064", + "name": "Spawner (2122)", + "location": [7002, 382, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 20, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e3d55bfe-9e18-4d68-ba3a-691cc402a410", + "name": "Spawner (2122)", + "location": [6999, 352, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dd5c1a95-b5d7-4afa-8d8d-97a460e840b7", + "name": "Spawner (2122)", + "location": [7047, 382, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [{ "name": "Bird", "maxCount": 5, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1f7b05c2-d4a9-4828-be39-b1f1de6c3861", + "name": "Spawner (2122)", + "location": [7033, 385, 0], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [{ "name": "Bird", "maxCount": 10, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "405031eb-1ebb-4793-91f7-82fd2c8c4590", + "name": "Spawner (2122)", + "location": [7032, 411, 7], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "561930a3-2f73-4447-b49f-e4637f3fefdf", + "name": "Spawner (2122)", + "location": [7044, 429, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "18bfc417-d6d9-4495-8215-979ef88c2e96", + "name": "Spawner (2122)", + "location": [6986, 384, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 4, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12f99d2c-3a40-451c-a75c-28b31f86c35a", + "name": "Spawner (2122)", + "location": [7063, 410, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0a4a9b61-4c34-441d-82b1-6e822fa8ceb4", + "name": "Spawner (2122)", + "location": [7052, 386, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "39a9c600-a998-4cc2-b1a8-ee242efadaf2", + "name": "Spawner (2122)", + "location": [7035, 387, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "089ed81a-4b3d-4a1a-aca7-10dd211255dc", + "name": "Spawner (2122)", + "location": [7007, 382, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "446d1aec-f4ab-4692-b0a1-f05455ade58e", + "name": "Spawner (2122)", + "location": [7087, 382, 1], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "80dd3733-74a7-4a58-baf5-88b1eb228a36", + "name": "Spawner (2122)", + "location": [6992, 345, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d1437b0a-56bb-440c-9625-8313955cdde6", + "name": "Spawner (2122)", + "location": [7007, 360, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [{ "name": "MiniatureMushroom", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1b035b3b-80f4-4486-8571-9138925425f2", + "name": "Spawner (2122)", + "location": [5726, 3211, -2], + "map": "Trammel", + "count": 48, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 100, + "walkingRange": 100, + "entries": [ + { "name": "Bird", "maxCount": 12, "probability": 100 }, + { "name": "Cat", "maxCount": 12, "probability": 100 }, + { "name": "Dog", "maxCount": 12, "probability": 100 }, + { "name": "Rat", "maxCount": 12, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "156e6770-5cef-4a8e-abc0-10ae90fe8331", + "name": "Spawner (2122)", + "location": [5255, 4000, 37], + "map": "Trammel", + "count": 24, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 80, + "walkingRange": 80, + "entries": [ + { "name": "Bird", "maxCount": 6, "probability": 100 }, + { "name": "Cat", "maxCount": 6, "probability": 100 }, + { "name": "Dog", "maxCount": 6, "probability": 100 }, + { "name": "Rat", "maxCount": 6, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c14d6a08-a6b8-486d-bffe-2129d734437b", + "name": "Spawner (2122)", + "location": [5267, 4012, 40], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 15, + "entries": [ + { "name": "Chicken", "maxCount": 5, "probability": 100 }, + { "name": "Sheep", "maxCount": 5, "probability": 100 }, + { "name": "Cow", "maxCount": 5, "probability": 100 }, + { "name": "Bull", "maxCount": 5, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/uoml/trammel/TownsPeople.json b/Distribution/Data/Spawns/uoml/trammel/TownsPeople.json new file mode 100644 index 000000000..b369ed136 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/TownsPeople.json @@ -0,0 +1,769 @@ +[ + { + "type": "Spawner", + "guid": "a1fec8b0-aeb0-4cfe-bedb-a630f60f483b", + "name": "Spawner (2123)", + "location": [1400, 1623, 28], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d7c52fa-6f53-40ae-8f24-1daf0a7ce571", + "name": "Spawner (2123)", + "location": [1400, 1628, 28], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0cd9c91b-ab80-4dbd-8594-ace0132a0fc8", + "name": "Spawner (2123)", + "location": [1521, 1457, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "ChaosGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fc87ea0f-7cd3-413a-a409-d1b304ec5299", + "name": "Spawner (2123)", + "location": [1525, 1457, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 1, + "entries": [{ "name": "ChaosGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81ee4607-e452-45ca-ac26-7c5a8d47f201", + "name": "Spawner (2123)", + "location": [1662, 1611, 19], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d351ad8d-c82c-43b5-a10c-b7d42d47cc9f", + "name": "Spawner (2123)", + "location": [1421, 1698, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81d50c82-2e5a-41d8-b572-f50a66302d6e", + "name": "Spawner (2123)", + "location": [2254, 1207, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "faa84300-0e93-40c1-a328-863f8ed209fe", + "name": "Spawner (2123)", + "location": [5290, 3987, 37], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8b75bcf8-7b83-4d94-8573-92aff88cd41f", + "name": "Spawner (2123)", + "location": [3652, 2619, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c26e2162-6a56-4eeb-b6ec-300b0ef5ed0f", + "name": "Spawner (2123)", + "location": [1330, 3778, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "56ff4f2b-88cc-44ce-bc5c-cadd416e069d", + "name": "Spawner (2123)", + "location": [3721, 2161, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93c7a59d-449b-4993-b89d-8261edf96f2d", + "name": "Spawner (2123)", + "location": [2515, 557, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ffaf3446-ee45-4f0f-9020-7508409e7da0", + "name": "Spawner (2123)", + "location": [4451, 1153, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "34473f72-e1ae-4968-97aa-0388ead486b9", + "name": "Spawner (2123)", + "location": [3773, 1308, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d746f23-5e37-467c-9b8c-2af3b1ba0882", + "name": "Spawner (2123)", + "location": [5685, 3148, -4], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "756f5e4b-690b-4d80-a775-3ff67397e700", + "name": "Spawner (2123)", + "location": [2891, 3474, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "17960040-e115-49e8-a1f3-f14583d9517f", + "name": "Spawner (2123)", + "location": [603, 2141, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76bb7e24-7ad9-4dc2-97c8-f575ff05a259", + "name": "Spawner (2123)", + "location": [1914, 2687, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2b35dd52-619d-448f-86d1-8aaa6f146ac7", + "name": "Spawner (2123)", + "location": [1829, 2811, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "65e4866a-41f4-47ec-8f26-aa92f48aac80", + "name": "Spawner (2123)", + "location": [2895, 686, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "36bf73a1-cc52-4776-8b8b-99088d86d03c", + "name": "Spawner (2123)", + "location": [627, 864, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 5, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "26a2621e-6b2c-47ef-b957-4d5630091661", + "name": "Spawner (2123)", + "location": [1399, 3743, -21], + "map": "Trammel", + "count": 10, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "HireBard", "maxCount": 5, "probability": 100 }, + { "name": "HireBardArcher", "maxCount": 5, "probability": 100 }, + { "name": "HireBeggar", "maxCount": 5, "probability": 100 }, + { "name": "HireFighter", "maxCount": 5, "probability": 100 }, + { "name": "HireMage", "maxCount": 5, "probability": 100 }, + { "name": "HireRanger", "maxCount": 5, "probability": 100 }, + { "name": "HireRangerArcher", "maxCount": 5, "probability": 100 }, + { "name": "HireSailor", "maxCount": 5, "probability": 100 }, + { "name": "HireThief", "maxCount": 5, "probability": 100 }, + { "name": "HirePaladin", "maxCount": 5, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "852fdf43-a6c6-4fb9-acd9-35b1943f4d11", + "name": "Spawner (2123)", + "location": [1483, 1760, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "82760905-485e-4bbe-83b6-56ccc903665a", + "name": "Spawner (2123)", + "location": [1372, 3916, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "112087fa-8961-4d69-a729-19390bb722c5", + "name": "Spawner (2123)", + "location": [1126, 3688, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc65ad16-fde0-4172-b5c9-df90020f5ed5", + "name": "Spawner (2123)", + "location": [3674, 2291, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9e077b7d-aea5-4e45-9435-4cce0aaa6962", + "name": "Spawner (2123)", + "location": [4398, 1049, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31d1057e-9886-428e-82ef-4c08bf202117", + "name": "Spawner (2123)", + "location": [3815, 1217, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3203a756-0011-4741-9fae-8a1c92166c45", + "name": "Spawner (2123)", + "location": [3749, 1404, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce3d7ff4-6411-4a30-914a-6b2bd1b9c752", + "name": "Spawner (2123)", + "location": [3645, 2661, -2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1de05315-2afe-4cc6-8b83-22fe3ebab00e", + "name": "Spawner (2123)", + "location": [2994, 3451, 16], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "88ba3663-c0bb-4d49-bcb5-cc4b6490b9cf", + "name": "Spawner (2123)", + "location": [673, 2235, -3], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d9dbef7c-e798-47c8-8939-7bbb74d297a9", + "name": "Spawner (2123)", + "location": [573, 2246, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "81e6cbf7-1e74-4547-b10f-024a75b64d40", + "name": "Spawner (2123)", + "location": [2068, 2854, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea87d9c8-52d9-492a-9a5f-9b32bf2ee832", + "name": "Spawner (2123)", + "location": [3041, 827, -3], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e1071149-0c52-45c8-a0db-75da33c0a77e", + "name": "Spawner (2123)", + "location": [631, 883, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "EscortableMage", "maxCount": 3, "probability": 100 }, + { "name": "SeekerOfAdventure", "maxCount": 3, "probability": 100 }, + { "name": "Noble", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "86739b8d-2726-43ff-aeaa-562873cee364", + "name": "Spawner (2123)", + "location": [1813, 2834, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [ + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel4", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "64802440-be2d-4b3e-babb-d8612ae85e90", + "name": "Spawner (2123)", + "location": [2875, 675, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 2, + "walkingRange": 2, + "entries": [{ "name": "TreasureChestLevel1", "maxCount": 6, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "79236906-51cd-4390-86c5-36b5ea71f943", + "name": "Spawner (2123)", + "location": [2874, 692, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [ + { "name": "TreasureChestLevel1", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f539d13c-6972-46fe-bce6-a09531c3473a", + "name": "Spawner (2123)", + "location": [2884, 692, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 2, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e006ab7-3370-4b4e-8fbc-cfa849b3d0c5", + "name": "Spawner (2123)", + "location": [7082, 367, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Olaeni", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4258947f-59d2-4fbb-98e7-11b65f16d8a9", + "name": "Spawner (2123)", + "location": [7063, 350, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [ + { "name": "Bolaevin", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fd8c35db-5081-4db5-9859-2873a070fbac", + "name": "Spawner (2123)", + "location": [7010, 363, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "athialon", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "99c8e802-f37a-4f2e-b5d5-23239e0ba219", + "name": "Spawner (2123)", + "location": [7048, 382, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 12, + "walkingRange": 12, + "entries": [ + { "name": "abbein", "maxCount": 1, "probability": 100 }, + { "name": "taellia", "maxCount": 1, "probability": 100 }, + { "name": "vicaie", "maxCount": 1, "probability": 100 }, + { "name": "mallew", "maxCount": 1, "probability": 100 }, + { "name": "jothan", "maxCount": 1, "probability": 100 }, + { "name": "alethanian", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "995f4053-0947-4757-8665-98cdac36ca2b", + "name": "Spawner (2123)", + "location": [6989, 342, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [{ "name": "Tyeelor", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "37d7e7c7-3ef0-4b63-bc92-87b172a02533", + "name": "Spawner (2123)", + "location": [7053, 338, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 6, + "entries": [ + { "name": "aneen", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f7c934c-8e02-433d-a88f-4c8769a02081", + "name": "Spawner (2123)", + "location": [7031, 409, 7], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Alelle", "maxCount": 1, "probability": 100 }, + { "name": "Daelas", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46dc4d43-91c1-4d09-a0e0-9d2e89804977", + "name": "Spawner (2123)", + "location": [7090, 387, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 8, + "entries": [ + { "name": "Aluniol", "maxCount": 1, "probability": 100 }, + { "name": "Rebinil", "maxCount": 1, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/TramCropsLS.json b/Distribution/Data/Spawns/uoml/trammel/TramCropsLS.json new file mode 100644 index 000000000..ba95364c1 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/TramCropsLS.json @@ -0,0 +1,290 @@ +[ + { + "type": "RegionSpawner", + "location": [375, 1191, 0], + "map": "Trammel", + "region": "A Wheatfield in Yew 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "fb826650-9fad-43ff-8ba1-9d848f1f26c5", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [567, 1239, 0], + "map": "Trammel", + "region": "A Wheatfield in Yew 1", + "count": 5, + "entries": [{ "maxCount": 5, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "79af53a5-33e2-4fdb-95dd-278a050e54fc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [571, 1099, 0], + "map": "Trammel", + "region": "A Farm in Yew", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "8408baaa-0de1-4cb0-ad89-973782e3a565", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [675, 939, 0], + "map": "Trammel", + "region": "A Field of Sheep in Yew 1", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "b17f2f9c-9d36-4bea-87db-69f8effc4926", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [675, 1179, 0], + "map": "Trammel", + "region": "A Field of Sheep in Yew 2", + "count": 10, + "entries": [{ "maxCount": 10, "name": "Sheep", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "690ccc1c-2ac8-4f05-8066-695aa53c98aa", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [813, 2163, 0], + "map": "Trammel", + "region": "A Wheatfield in Skara Brae 1", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "ee6692af-82cf-477e-96ec-375be7a23db9", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2254, 0], + "map": "Trammel", + "region": "A Carrot Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "c052a5e0-493d-483e-a98a-665cf05c5c14", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2264, 0], + "map": "Trammel", + "region": "An Onion Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableOnion", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "41a583ac-749c-40a7-8658-fb3deb1b222a", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2264, 0], + "map": "Trammel", + "region": "A Cabbage Field in Skara Brae 1", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "df4c5918-b490-4257-9aeb-541b44980b27", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2274, 0], + "map": "Trammel", + "region": "A Cabbage Field in Skara Brae 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "1025906e-047c-460b-9bb8-581f57ff1357", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [823, 2355, 0], + "map": "Trammel", + "region": "A Cotton Field in Skara Brae", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCotton", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "185e475b-36cf-4dad-8776-537e84b15e42", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [843, 2352, 0], + "map": "Trammel", + "region": "A Wheatfield in Skara Brae 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "59e99d3b-b727-491b-b0f3-03a441b10c04", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1120, 1624, 0], + "map": "Trammel", + "region": "A Wheatfield in Britain 4", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "85cc6fca-4f66-4428-aa3a-347a88523a4b", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1135, 1791, 0], + "map": "Trammel", + "region": "A Wheatfield in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "afdfec82-dac3-43df-bd7b-cd2cfad5f265", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1152, 1576, 0], + "map": "Trammel", + "region": "A Wheatfield in Britain 5", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "34b0579c-1fc6-4868-82c5-09138c5e5878", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1183, 1683, 0], + "map": "Trammel", + "region": "A Cabbage Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCabbage", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "997d9ff5-ad2b-421b-ae76-6794e7e88150", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1199, 1683, 0], + "map": "Trammel", + "region": "A Turnip Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableTurnip", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "bfb677f7-e824-46f6-95ef-ee5602f4f9c3", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1199, 1823, 0], + "map": "Trammel", + "region": "A Wheatfield in Britain 2", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "c42ef172-78c8-4c5e-b26c-fcc3fc3da1cc", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1215, 1723, 0], + "map": "Trammel", + "region": "A Carrot Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "57551e80-0018-4fcf-8b83-9d39ef19b0a6", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1216, 1604, 0], + "map": "Trammel", + "region": "A Turnip Field in Britain 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableTurnip", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "0da29e36-7bff-4fcf-803d-79dd16212653", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1231, 1723, 0], + "map": "Trammel", + "region": "An Onion Field in Britain 1", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableOnion", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "4656ad9d-a455-4217-9316-ec10d10cf68c", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1231, 1887, 0], + "map": "Trammel", + "region": "A Wheatfield in Britain 3", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableWheat", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "d5b08634-5c5d-4bc2-b6cd-74cb06132423", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [1232, 1604, 0], + "map": "Trammel", + "region": "A Carrot Field in Britain 2", + "count": 6, + "entries": [{ "maxCount": 6, "name": "FarmableCarrot", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "ab0cb920-b25f-4811-8ef6-938549dbfa27", + "name": "Spawner" + }, + { + "type": "RegionSpawner", + "location": [4567, 1475, 0], + "map": "Trammel", + "region": "A Cotton Field in Moonglow", + "count": 8, + "entries": [{ "maxCount": 8, "name": "FarmableCotton", "probability": 100 }], + "minDelay": "00:00:10", + "maxDelay": "00:00:30", + "guid": "69dfbe70-b46c-42b0-9ff1-351fd9651884", + "name": "Spawner" + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/TrinsicPassage.json b/Distribution/Data/Spawns/uoml/trammel/TrinsicPassage.json new file mode 100644 index 000000000..2a5673319 --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/TrinsicPassage.json @@ -0,0 +1,119 @@ +[ + { + "type": "Spawner", + "guid": "54cb95e8-ceb7-42de-872f-6c2208f075c1", + "name": "Spawner (2124)", + "location": [5976, 1387, -22], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bdc0fed3-7087-4713-9690-92e8878992cb", + "name": "Spawner (2124)", + "location": [5940, 1356, -2], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1e21e474-6f3f-4e15-b16a-da201c371cfe", + "name": "Spawner (2124)", + "location": [5912, 1326, 0], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "098f3928-ea97-4278-ad09-63a202b30fea", + "name": "Spawner (2124)", + "location": [5956, 1310, -2], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "01aee8cd-fc2b-4e00-8d21-991131290d9b", + "name": "Spawner (2124)", + "location": [5999, 1327, 10], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "DreadSpider", "maxCount": 2, "probability": 100 }, + { "name": "FireElemental", "maxCount": 1, "probability": 100 }, + { "name": "HellCat", "maxCount": 1, "probability": 100 }, + { "name": "LavaLizard", "maxCount": 1, "probability": 100 }, + { "name": "LavaSnake", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "faefe911-578d-4e50-b1b5-918119689237", + "name": "Spawner (2124)", + "location": [5915, 1302, 2], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [ + { "name": "TreasureChestLevel2", "maxCount": 1, "probability": 100 }, + { "name": "TreasureChestLevel3", "maxCount": 2, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/Spawns/uoml/trammel/Vendors.json b/Distribution/Data/Spawns/uoml/trammel/Vendors.json index 379a3900a..37b6f083c 100644 --- a/Distribution/Data/Spawns/uoml/trammel/Vendors.json +++ b/Distribution/Data/Spawns/uoml/trammel/Vendors.json @@ -1,6 +1,2695 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "558ec561-9e56-4d94-8dc0-c86161bdb6a4", + "name": "Spawner (2125)", + "location": [1685, 2985, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 20, + "entries": [ + { "name": "Herbalist", "maxCount": 2, "probability": 100 }, + { "name": "Mage", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0339e1b7-4167-4d40-bc12-1cb62da96006", + "name": "Spawner (2125)", + "location": [1650, 1642, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "71f6509c-73fc-4b66-97e0-73117fa77c0c", + "name": "Spawner (2125)", + "location": [1450, 1617, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d3a3af04-33a1-478f-856d-210fdbb5a9ce", + "name": "Spawner (2125)", + "location": [1418, 1547, 30], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d1dbe32d-b712-4b26-8c3c-f34e94ffeeb2", + "name": "Spawner (2125)", + "location": [1471, 1611, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "643c0e84-5e3f-40db-ac86-3c17c38a316f", + "name": "Spawner (2125)", + "location": [1447, 1647, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dced8f77-228f-4246-9b0a-96539575a2b0", + "name": "Spawner (2125)", + "location": [1590, 1654, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ced2b237-e589-49f6-9468-9c6b5bbb8ca4", + "name": "Spawner (2125)", + "location": [1455, 1557, 30], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Bard", "maxCount": 1, "probability": 100 }, + { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98ab2774-ef8a-402a-805b-fd3f035f0458", + "name": "Spawner (2125)", + "location": [1388, 1588, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d69a466-479b-4419-98df-ad54be6244cc", + "name": "Spawner (2125)", + "location": [1362, 1574, 30], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2aebb1a5-2e39-424d-9e9b-e8254a5e307d", + "name": "Spawner (2125)", + "location": [1467, 1686, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "42088948-8d4a-4745-8ecc-b0f6a6f5f5db", + "name": "Spawner (2125)", + "location": [1474, 1597, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8562df8d-5b04-4426-8f62-4125b0bb81c8", + "name": "Spawner (2125)", + "location": [1417, 1578, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1647a7af-7960-4b9e-87aa-99e10a89920d", + "name": "Spawner (2125)", + "location": [1458, 1525, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "62c4349d-d176-4b3d-8df5-c8b624a55423", + "name": "Spawner (2125)", + "location": [1416, 1754, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbd51949-6b2b-4973-8e5f-67aa1504d28b", + "name": "Spawner (2125)", + "location": [1451, 1679, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "327a996b-05f3-4523-b029-90d7c4984a31", + "name": "Spawner (2125)", + "location": [1602, 1712, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5e06b77-5783-4d11-8468-2759e524524d", + "name": "Spawner (2125)", + "location": [1470, 1578, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1a9355e3-e45f-43b3-bc0d-2afb86873a1c", + "name": "Spawner (2125)", + "location": [1547, 1659, 26], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41161b38-3f7c-478f-92c2-53f0dd3e9dad", + "name": "Spawner (2125)", + "location": [1620, 1586, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a301e23d-1bfd-46ef-a2fc-dd4ca9756255", + "name": "Spawner (2125)", + "location": [1430, 1597, 20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "29a793dc-b980-4c98-a362-f90f50121b3b", + "name": "Spawner (2125)", + "location": [1485, 1550, 30], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c937726b-6b62-4b10-8598-9393b597ef90", + "name": "Spawner (2125)", + "location": [1296, 1759, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "991ca64d-4d48-49d8-a2a0-71a9d23fb2b3", + "name": "Spawner (2125)", + "location": [1388, 1655, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8c6bc394-02b5-4830-8848-55fbb6623a7a", + "name": "Spawner (2125)", + "location": [1481, 1584, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40f86110-4700-404e-865a-0624ee3e88df", + "name": "Spawner (2125)", + "location": [1493, 1616, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b6c600be-26e6-4553-bd5b-17986a50c0ef", + "name": "Spawner (2125)", + "location": [1422, 1654, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2bb96113-3a2f-4092-b055-1883f7842b22", + "name": "Spawner (2125)", + "location": [1547, 1768, 10], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0435d4c7-1502-4434-b152-9ed1f6c77cf0", + "name": "Spawner (2125)", + "location": [1341, 1733, 20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "hirefighter", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "360bf7a9-be1c-453a-b74a-3d05f91508eb", + "name": "Spawner (2125)", + "location": [1585, 1591, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adb4bd49-a4b7-48da-bd26-527aa960c988", + "name": "Spawner (2125)", + "location": [1637, 1693, 37], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a48baab3-ef4a-428b-849d-f96b7fe2270c", + "name": "Spawner (2125)", + "location": [1431, 1612, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b46e3bb9-2dba-4235-9d9a-e9a40e1b142a", + "name": "Spawner (2125)", + "location": [1498, 1691, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "335880dc-a64c-488d-98c6-cadf4f8bca54", + "name": "Spawner (2125)", + "location": [1470, 1765, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1a27bb4d-874d-4821-8ce3-a669d83ee6f1", + "name": "Spawner (2125)", + "location": [1409, 1590, 42], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9ca7daed-db6e-487c-a7b0-ea5448dd368c", + "name": "Spawner (2125)", + "location": [1349, 1778, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Blacksmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e07cf894-eb63-4d4b-9096-e57bed868aca", + "name": "Spawner (2125)", + "location": [1469, 1668, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d2ae2226-0a3a-4bef-9683-cffb0c59975f", + "name": "Spawner (2125)", + "location": [1510, 1543, 25], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5f4a1c5c-64b3-4636-9067-b309c48b93d3", + "name": "Spawner (2125)", + "location": [1427, 1716, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "586454ef-4cc0-49a4-a2e4-89574c326a66", + "name": "Spawner (2125)", + "location": [1449, 1723, 6], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5635ef7b-3e30-4f77-9dcc-1d32e90756b9", + "name": "Spawner (2125)", + "location": [1650, 1608, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "31da09ce-311f-409b-9c68-34b8058df581", + "name": "Spawner (2125)", + "location": [1498, 1659, 27], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "043d3956-11cc-4f4f-a014-7752c464e34c", + "name": "Spawner (2125)", + "location": [1425, 1690, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebf92564-8ea0-4ca8-bec5-8d51bd4e984c", + "name": "Spawner (2125)", + "location": [1494, 1715, 6], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47b0d916-8f93-4580-b4c1-b9408b377a8b", + "name": "Spawner (2125)", + "location": [1405, 1810, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 25, + "entries": [{ "name": "Mage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5652fc2b-5bb0-4f6b-b01c-fa8a302ad4d3", + "name": "Spawner (2125)", + "location": [1482, 1761, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 4, + "entries": [{ "name": "TownCrier", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "507f2688-b5dd-404d-b31b-50b0914c0e58", + "name": "Spawner (2125)", + "location": [1228, 1572, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3330ffe8-07f8-4a7d-9515-34e5333dfe48", + "name": "Spawner (2125)", + "location": [1355, 1754, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 14, + "entries": [ + { "name": "OrderGuard", "maxCount": 1, "probability": 100 }, + { "name": "hirefighter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "839452cf-a264-4ccc-983b-6cd13565676d", + "name": "Spawner (2125)", + "location": [1243, 1699, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5d3324c6-7226-45eb-af13-35bb32a227ac", + "name": "Spawner (2125)", + "location": [1351, 1816, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "EscortableMage", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "33929eec-3fe3-4b78-978f-f1bef78a28eb", + "name": "Spawner (2125)", + "location": [1306, 1804, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 6, + "entries": [{ "name": "OrderGuard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c2c011c1-e49f-4005-86e9-ffec9a526573", + "name": "Spawner (2125)", + "location": [1304, 1826, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 1, + "walkingRange": 1, + "entries": [{ "name": "Peasant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9f0eb4c9-1e11-4abf-9459-31f25308fe70", + "name": "Spawner (2125)", + "location": [1362, 1825, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 12, + "entries": [{ "name": "Artist", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "adcf36e0-59c9-4804-8c64-8ac46e1dc0b4", + "name": "Spawner (2125)", + "location": [1333, 1808, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "escortablewanderinghealer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "54232896-d8a8-41ef-964e-1a8a426be590", + "name": "Spawner (2125)", + "location": [1150, 1619, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cbb75550-e870-42fe-9689-d99120baf85b", + "name": "Spawner (2125)", + "location": [1149, 1547, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 10, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0dd0b6c-633d-426a-8c36-3c2853ecd252", + "name": "Spawner (2125)", + "location": [1295, 1773, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "BrideGroom", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "928e9340-fb04-4d78-94f9-8ec141fe6fb2", + "name": "Spawner (2125)", + "location": [1316, 1747, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 20, + "entries": [{ "name": "hirefighter", "maxCount": 3, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8b249196-07e0-42de-a67e-d6959d1ca4c9", + "name": "Spawner (2125)", + "location": [1231, 1714, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 10, + "entries": [{ "name": "hirepeasant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d45157f9-2bf0-4101-8e11-a06255f0f3bf", + "name": "Spawner (2125)", + "location": [1190, 1707, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 8, + "entries": [ + { "name": "Farmer", "maxCount": 1, "probability": 100 }, + { "name": "Rancher", "maxCount": 1, "probability": 100 }, + { "name": "hirepeasant", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "472a07d2-cb45-4ca4-9e2d-4153faae4d0e", + "name": "Spawner (2125)", + "location": [1427, 1726, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "hirebard", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1facba9c-8639-4def-bb21-08abc7456e8e", + "name": "Spawner (2125)", + "location": [1438, 1761, -2], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 10, + "entries": [ + { "name": "hiresailor", "maxCount": 1, "probability": 100 }, + { "name": "Fisherman", "maxCount": 1, "probability": 100 }, + { "name": "Rat", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4fa0e5cb-22de-46a4-9620-4f7103685d53", + "name": "Spawner (2125)", + "location": [1477, 1741, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 20, + "walkingRange": 20, + "entries": [{ "name": "hiresailor", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "469ffd2c-d656-45ca-89c5-f0a7fedf6176", + "name": "Spawner (2125)", + "location": [1436, 1752, 14], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "HarborMaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "32655bec-db85-4a3e-95b4-c21dcf576d0a", + "name": "Spawner (2125)", + "location": [1434, 1717, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 4, + "walkingRange": 4, + "entries": [{ "name": "Merchant", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0537535f-e69b-4c83-8eaa-09b27fd71a85", + "name": "Spawner (2125)", + "location": [2709, 2130, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fac7f6c9-0564-4baf-b2aa-9bf3a9e4b35c", + "name": "Spawner (2125)", + "location": [2715, 2098, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2b03cbc1-378c-45ab-b01c-95d9e4944647", + "name": "Spawner (2125)", + "location": [2659, 2194, 4], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "ThiefGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f723423b-7c27-48c1-b662-335797e6dff2", + "name": "Spawner (2125)", + "location": [2680, 2238, 2], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a13d9bc-849b-4a5b-96b0-0afe64cad4d6", + "name": "Spawner (2125)", + "location": [2737, 2250, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1b6c520f-c677-481b-9a8e-fa2ab5658a16", + "name": "Spawner (2125)", + "location": [2731, 2192, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4bc9a4dd-1844-483d-997d-e73e53279198", + "name": "Spawner (2125)", + "location": [2627, 2100, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "956834dc-651a-42d7-974b-b2bac74e903e", + "name": "Spawner (2125)", + "location": [2752, 2155, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2b7a7c2b-f82b-4cbc-a83d-f9ff58b87562", + "name": "Spawner (2125)", + "location": [2707, 2178, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "134c9621-c59d-47ef-9afc-b47e1d99e0bd", + "name": "Spawner (2125)", + "location": [2634, 2083, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3a2fbed0-d494-4930-8238-a74b2489e23a", + "name": "Spawner (2125)", + "location": [2245, 1231, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bfdfa0ef-f98a-4095-ad75-c87737eb95a2", + "name": "Spawner (2125)", + "location": [2216, 1167, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "808ba57f-46db-4659-b20c-52b61d520894", + "name": "Spawner (2125)", + "location": [2216, 1192, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a79cb65c-847a-4b9c-b544-48de9bd6d103", + "name": "Spawner (2125)", + "location": [2256, 1181, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "942aa3c4-39fc-4c2c-83cd-31336753d2e8", + "name": "Spawner (2125)", + "location": [1435, 3820, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2150f1f-5cd9-4d5f-a340-7f4bfd0b3214", + "name": "Spawner (2125)", + "location": [1404, 3802, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98464fd7-e5b6-4caa-a6a2-9164399191e6", + "name": "Spawner (2125)", + "location": [1452, 3770, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3738191d-80e0-44da-936a-6a7ede0035a2", + "name": "Spawner (2125)", + "location": [1456, 3850, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e6a14d4b-f7c8-427e-a0c8-a1d972e3f3f6", + "name": "Spawner (2125)", + "location": [1317, 3773, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "54250593-bd68-4c4d-9017-593ea8a93c8d", + "name": "Spawner (2125)", + "location": [1372, 3908, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ee56cd69-1e8d-46fe-906f-e3e91614708a", + "name": "Spawner (2125)", + "location": [1512, 3989, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1b882487-2ce6-4819-8db4-7077c0217394", + "name": "Spawner (2125)", + "location": [1504, 3693, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "47d6019a-a955-4f5a-bcff-da5d6cb6acea", + "name": "Spawner (2125)", + "location": [1125, 3687, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a318c022-1463-466e-95d1-e59b2cde9a63", + "name": "Spawner (2125)", + "location": [1416, 3779, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d033238c-2772-4748-b09d-e03dd3a18768", + "name": "Spawner (2125)", + "location": [1387, 3771, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3d9b805c-a0d7-4a4d-82a9-49227a879550", + "name": "Spawner (2125)", + "location": [1425, 3981, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4eec52f5-a2ee-41fe-b83d-e4ef31e59cc4", + "name": "Spawner (2125)", + "location": [1454, 4003, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4d24ec90-79b5-4881-bcd6-568034cd3e2a", + "name": "Spawner (2125)", + "location": [1360, 3815, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "92c0ad53-3da1-446d-a39e-86131211cbb3", + "name": "Spawner (2125)", + "location": [1442, 3802, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Provisioner", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d114445e-e1ae-41b0-890e-49b568c65f68", + "name": "Spawner (2125)", + "location": [1442, 3802, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Cobbler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "23f532bd-b465-4865-9e87-1acb1f975bb7", + "name": "Spawner (2125)", + "location": [1437, 3750, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4e0c3ef6-5714-4bc8-8d0f-8cad7008c2b3", + "name": "Spawner (2125)", + "location": [1447, 3981, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6c7d0542-d02a-49e2-b03d-2f27b68074e8", + "name": "Spawner (2125)", + "location": [1452, 3747, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f85575f5-813e-48cc-b0a5-2eb116e610bd", + "name": "Spawner (2125)", + "location": [1441, 3719, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b4c90a7-f7e7-41a8-a4f1-0e37591c1cd9", + "name": "Spawner (2125)", + "location": [1419, 3859, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "da93faab-336b-403b-8628-da3da7457f7f", + "name": "Spawner (2125)", + "location": [1356, 3780, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c0c400ce-9cdc-401a-8985-84dc26849dec", + "name": "Spawner (2125)", + "location": [1364, 3732, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44c7bc31-c445-42e3-afa6-e86d6beac11b", + "name": "Spawner (2125)", + "location": [1475, 3865, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e2fff56f-b197-45be-a68c-af29db75338f", + "name": "Spawner (2125)", + "location": [1354, 3754, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dc7ab28c-9717-4c5d-938c-d6ea01440de7", + "name": "Spawner (2125)", + "location": [1386, 3825, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "419c9bd7-ce77-4ef9-bdd6-2a913c93534b", + "name": "Spawner (2125)", + "location": [1450, 4026, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2a605fd6-6428-4bd5-9571-ddd030de003c", + "name": "Spawner (2125)", + "location": [1395, 3705, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6c34b2d6-d0d5-4eb1-a0c2-53132b5e7b06", + "name": "Spawner (2125)", + "location": [3683, 2252, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d9fe1303-74c5-490c-9d08-e43325425919", + "name": "Spawner (2125)", + "location": [3667, 2252, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "482cd8b9-c3af-4b1c-9154-f722aa67b0ed", + "name": "Spawner (2125)", + "location": [3727, 2224, 20], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9b981d8-c473-411a-8655-19c74ef5fbe5", + "name": "Spawner (2125)", + "location": [3687, 2227, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "62d67d9c-82af-4b44-be9a-f1ac7ea02274", + "name": "Spawner (2125)", + "location": [3674, 2289, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50b0e650-9163-4263-a71a-3912aa87b5c7", + "name": "Spawner (2125)", + "location": [3665, 2140, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "db27e125-6f28-4335-a94d-ec1a349a9aba", + "name": "Spawner (2125)", + "location": [3699, 2218, 20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4122c08b-45c7-46ae-8be2-f26a761c57c6", + "name": "Spawner (2125)", + "location": [3703, 2249, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MerchantGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "07e55a6f-82ae-4f79-b758-f327f002a1cd", + "name": "Spawner (2125)", + "location": [3699, 2163, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8d1a1d0f-e8be-4947-90c6-e9611a3c86d7", + "name": "Spawner (2125)", + "location": [3666, 2235, 20], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1f649d8d-7a53-46a1-bdab-64f309d4e6c7", + "name": "Spawner (2125)", + "location": [3720, 2126, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9936a583-3fc5-4414-b85a-94fe319831b8", + "name": "Spawner (2125)", + "location": [3755, 2227, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "1ea130cc-27cc-4640-8351-976a0858188d", + "name": "Spawner (2125)", + "location": [3734, 2149, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0402be0f-a2d2-4116-8c36-c7af2c230447", + "name": "Spawner (2125)", + "location": [3683, 2171, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "76b3a8d0-aa01-4956-bd37-e2bcd462e8e6", + "name": "Spawner (2125)", + "location": [3661, 2183, 20], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0bd547bd-7fe4-4869-bb5c-17a297013e44", + "name": "Spawner (2125)", + "location": [2471, 564, 5], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f1fe615-63ac-460b-93e6-11eead781649", + "name": "Spawner (2125)", + "location": [2461, 457, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85bf65b2-ed34-4719-bbca-e9449e8c1989", + "name": "Spawner (2125)", + "location": [2505, 432, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f46915ea-0d08-4bca-bb53-335f8c12f07b", + "name": "Spawner (2125)", + "location": [2577, 599, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5bcefd37-a195-42d2-bb43-7087fa46c165", + "name": "Spawner (2125)", + "location": [2455, 487, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ae039b67-65da-48a1-9d66-397f7f963c24", + "name": "Spawner (2125)", + "location": [2424, 555, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Bard", "maxCount": 1, "probability": 100 }, + { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "61d32da8-8cac-4113-8936-85f8836fb6f3", + "name": "Spawner (2125)", + "location": [2478, 427, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e2bd9c74-593e-453e-9ae1-d8bd4fa67993", + "name": "Spawner (2125)", + "location": [2513, 477, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9a226f47-4b27-48ad-8786-77834d26479d", + "name": "Spawner (2125)", + "location": [2450, 428, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3803febb-c2fc-4d51-a5cd-6faee0570a6a", + "name": "Spawner (2125)", + "location": [2438, 410, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f120e595-13e3-4c97-a2f6-0ad960f38b0c", + "name": "Spawner (2125)", + "location": [2526, 375, 23], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b434162b-e02d-4bf9-88ee-2dda13e9278a", + "name": "Spawner (2125)", + "location": [2522, 524, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "52358248-3191-4dee-9e78-ea64d8370189", + "name": "Spawner (2125)", + "location": [2526, 546, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e0a225e-8ab0-443c-b125-ab3ad0956662", + "name": "Spawner (2125)", + "location": [2533, 572, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f14276d9-00a3-4122-aa2a-280dd4f8fac7", + "name": "Spawner (2125)", + "location": [2503, 552, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba540c4e-a870-4886-90ba-d6c2f466d287", + "name": "Spawner (2125)", + "location": [2475, 397, 15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "43174d70-989d-4a19-a02b-e39ffab8619c", + "name": "Spawner (2125)", + "location": [4416, 1133, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5cc0651f-cc23-464c-8dd2-8c924688e76f", + "name": "Spawner (2125)", + "location": [4392, 1068, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "46e1f663-0c59-4a2d-8f66-00f2a0d5195e", + "name": "Spawner (2125)", + "location": [4416, 1085, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "049e60c9-d17c-4dd7-b41f-72a029d03e08", + "name": "Spawner (2125)", + "location": [4392, 1117, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f163cb5b-d232-4cf8-ae17-2d13fe08623d", + "name": "Spawner (2125)", + "location": [4406, 1038, -2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12e7d6c0-93ec-4a46-aa65-03407c9a409c", + "name": "Spawner (2125)", + "location": [4394, 1083, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94408e1a-950c-424f-8d22-e75005e1b85e", + "name": "Spawner (2125)", + "location": [4409, 1111, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e447298-7816-42f1-889a-cf6c8423fb58", + "name": "Spawner (2125)", + "location": [4484, 1064, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "7c18a846-5733-4274-969d-c219443a8c95", + "name": "Spawner (2125)", + "location": [4448, 1090, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "85c7a253-36a4-4352-b90e-b5e28c8dac8c", + "name": "Spawner (2125)", + "location": [4458, 1060, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d2b5765-87c1-48c6-a9a6-27699ea30d72", + "name": "Spawner (2125)", + "location": [4417, 1064, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9917a0c7-7df7-4ebc-a830-c9fe30b20354", + "name": "Spawner (2125)", + "location": [4401, 1165, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "aafefa52-d26c-4735-a2bc-b73bd8285db1", + "name": "Spawner (2125)", + "location": [4440, 1162, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd48eb05-e200-4fc5-aa5d-8581121d0d79", + "name": "Spawner (2125)", + "location": [4545, 860, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5c623567-93cd-4602-82a0-d110513635db", + "name": "Spawner (2125)", + "location": [4481, 1085, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "91c818b9-32b8-4d84-b2cc-ada32cbad1ff", + "name": "Spawner (2125)", + "location": [4395, 1137, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "73b5f8a6-6c6d-4837-b3ca-7ff7e90ff27d", + "name": "Spawner (2125)", + "location": [4471, 1156, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0929d639-9f70-42aa-b714-608c783bb2df", + "name": "Spawner (2125)", + "location": [3778, 1172, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "de025061-4683-42e7-aaae-a864a81ea5a9", + "name": "Spawner (2125)", + "location": [3769, 1218, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36841b9f-477e-49d9-88a3-d66da91aaef4", + "name": "Spawner (2125)", + "location": [3546, 1185, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa5b770c-5cf3-4c34-bf04-b569d2997239", + "name": "Spawner (2125)", + "location": [3546, 1194, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ded253d-b661-4582-a15d-79282c15e8e8", + "name": "Spawner (2125)", + "location": [3555, 1171, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "cc884f40-cdb6-4c6d-8be6-c9d15216dc51", + "name": "Spawner (2125)", + "location": [3545, 1178, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "09169e10-d045-41c3-9c74-7bca043adb3c", + "name": "Spawner (2125)", + "location": [3554, 1202, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b4166ae1-15e1-4540-a3ef-c08ab843c639", + "name": "Spawner (2125)", + "location": [3731, 1320, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e87996f7-c1cc-46e1-a157-854b1049fa7e", + "name": "Spawner (2125)", + "location": [3700, 1214, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae951a02-cc60-40f1-b810-9f09c34b4b7a", + "name": "Spawner (2125)", + "location": [3729, 1258, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e2553ce-54f2-4cb4-9e20-9625257f4192", + "name": "Spawner (2125)", + "location": [3774, 1265, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3dc2017e-65fa-4231-9fc7-b1aa7c48f1b5", + "name": "Spawner (2125)", + "location": [3764, 1317, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d94255da-ddc6-4df5-af70-9de05ab71faa", + "name": "Spawner (2125)", + "location": [3740, 1197, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "BardGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", "guid": "011fc0f3-9e98-44ed-ab77-e0669247aacf", "name": "Spawner (2125)", "location": [3645, 2617, 0], @@ -17,7 +2706,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "19ecf802-ce51-4125-b13a-efb0afd02cbf", "name": "Spawner (2125)", "location": [3667, 2506, 0], @@ -34,7 +2723,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "56140e6f-fc52-49b6-bf74-fe782454a95a", "name": "Spawner (2125)", "location": [3655, 2665, -2], @@ -45,12 +2734,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Fisherman", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "625f9122-d16c-4e4e-9d33-bfd367428140", "name": "Spawner (2125)", "location": [3615, 2476, 0], @@ -61,12 +2748,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Scribe", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d6745c4f-710a-4fc4-b39a-bc54123feac8", "name": "Spawner (2125)", "location": [3693, 2509, 0], @@ -77,12 +2762,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "ThiefGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "ThiefGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "43797a84-5e69-45b3-87b6-122e033083e1", "name": "Spawner (2125)", "location": [3665, 2581, 0], @@ -99,7 +2782,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "e022aca5-1733-4cbc-b444-2bc16d30cf13", "name": "Spawner (2125)", "location": [3673, 2595, 0], @@ -116,7 +2799,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "ca72a646-61b9-41a7-988f-8bcf55f63745", "name": "Spawner (2125)", "location": [3632, 2573, 0], @@ -134,7 +2817,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "6ed9b012-4871-4674-a39c-c71f94ca6bc0", "name": "Spawner (2125)", "location": [3633, 2635, 0], @@ -151,7 +2834,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "64838953-eca4-4076-9c4a-6b8d53dc6da4", "name": "Spawner (2125)", "location": [3754, 2704, 40], @@ -162,12 +2845,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4391a5e3-0060-49e3-bb90-6bdcbf56f863", "name": "Spawner (2125)", "location": [3714, 2651, 20], @@ -178,12 +2859,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Butcher", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "85b91e45-a5ec-46df-9286-89b0f4a602a4", "name": "Spawner (2125)", "location": [3740, 2692, 40], @@ -194,12 +2873,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5156ee52-c3e4-42b0-ac5e-8d84b49cacb8", "name": "Spawner (2125)", "location": [3620, 2617, 0], @@ -216,7 +2893,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d952003f-99e5-4cf4-bea7-06265ffd2dfb", "name": "Spawner (2125)", "location": [3628, 2540, 0], @@ -227,12 +2904,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Baker", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "20e39658-8433-437e-b7fc-d2ea491189f6", "name": "Spawner (2125)", "location": [3687, 2477, 0], @@ -250,7 +2925,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "0ef8d67e-4e4c-4a53-ab5e-2474e573f096", "name": "Spawner (2125)", "location": [3668, 2652, 0], @@ -269,7 +2944,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "dfa29c8c-8888-4752-a063-f8161f01f569", "name": "Spawner (2125)", "location": [3661, 2531, 0], @@ -280,12 +2955,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "BardGuildmaster", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "BardGuildmaster", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "d753713d-6bf9-47fe-b4f1-58b6f304ad5b", "name": "Spawner (2125)", "location": [3594, 2595, 0], @@ -302,7 +2975,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "98ef6eb6-72ea-43a5-b2ea-8c153f6135d3", "name": "Spawner (2125)", "location": [3671, 2615, 0], @@ -313,12 +2986,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "InnKeeper", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "5294b511-5759-452f-8009-94157b248961", "name": "Spawner (2125)", "location": [3745, 2585, 40], @@ -329,12 +3000,10 @@ "team": 0, "homeRange": 0, "walkingRange": 5, - "entries": [ - { "name": "Bowyer", "maxCount": 1, "probability": 100 } - ] + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bd1d8c55-7a8a-465a-a8ff-f47adad641c6", "name": "Spawner (2125)", "location": [3642, 2504, 0], @@ -350,5 +3019,2307 @@ { "name": "Architect", "maxCount": 1, "probability": 100 }, { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "a20fa086-8694-465b-a8bc-0988b5017b61", + "name": "Spawner (2125)", + "location": [3009, 3455, 15], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 }, + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "92f199ed-e0d1-496f-a1f3-d6aa2a5e3155", + "name": "Spawner (2125)", + "location": [2940, 3410, 1], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d9c8783d-5147-4cc5-b2b7-0d0942297fcb", + "name": "Spawner (2125)", + "location": [2975, 3353, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49dd6a0d-15dc-4eff-9f1a-9f8af38fe15e", + "name": "Spawner (2125)", + "location": [3051, 3367, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "df077d96-f345-4d80-bee5-4ffad8a25c41", + "name": "Spawner (2125)", + "location": [2883, 3502, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5df6d5cf-b7d5-4b51-a52e-a0788d7fc21d", + "name": "Spawner (2125)", + "location": [3031, 3350, 35], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b907af34-1c6f-4533-92f4-65f9b68f7875", + "name": "Spawner (2125)", + "location": [3036, 3464, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b64d4434-2246-4ffa-91a0-600326c097ac", + "name": "Spawner (2125)", + "location": [3003, 3355, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1cd64ef2-a605-44ac-bf6a-54033cbd6093", + "name": "Spawner (2125)", + "location": [2905, 3517, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "94f8f998-08d7-453a-8dc0-669e4e8ed554", + "name": "Spawner (2125)", + "location": [3008, 3389, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8a091648-0378-4b58-879b-b07aee75b351", + "name": "Spawner (2125)", + "location": [3007, 3408, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "c4ea485b-f871-4c9b-a809-33b53ceb78c2", + "name": "Spawner (2125)", + "location": [3013, 3353, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "CustomHairstylist", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "01e0ae79-27ae-46b3-b4da-484ddf53f534", + "name": "Spawner (2125)", + "location": [2967, 3408, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf770c7b-4a88-4198-b78f-a83eff9d5410", + "name": "Spawner (2125)", + "location": [2997, 3428, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c193256a-99a5-44a6-934b-d2996199c5e5", + "name": "Spawner (2125)", + "location": [3058, 3400, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5d4c103e-b3cc-44c6-81a5-a0ec9472d14f", + "name": "Spawner (2125)", + "location": [2940, 3500, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2377d397-cf7b-41f0-a761-429f83c4fd93", + "name": "Spawner (2125)", + "location": [2972, 3425, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "TavernKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e9d01be-2213-4fa8-b1f6-e6e82ed4983c", + "name": "Spawner (2125)", + "location": [3013, 3353, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "485462f4-cbbc-4a46-aa31-aa7b42832509", + "name": "Spawner (2125)", + "location": [2906, 3485, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "66d3b52d-23e0-4d3f-8c93-17a81be068ef", + "name": "Spawner (2125)", + "location": [3018, 3426, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a9406a9-a389-4415-96f8-a8431e90009e", + "name": "Spawner (2125)", + "location": [2880, 3472, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f74f17e3-537f-4cf5-9ed5-2677e3bddd84", + "name": "Spawner (2125)", + "location": [2972, 3425, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d4579b8-5d76-4c53-8bda-3d2c4803f2e9", + "name": "Spawner (2125)", + "location": [2992, 3451, 16], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6ce9596e-45b3-4eaa-be62-885d94134085", + "name": "Spawner (2125)", + "location": [3007, 3408, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Blacksmith", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "50090b05-cdf5-4e5f-994a-7f4d6b7eb59e", + "name": "Spawner (2125)", + "location": [620, 2218, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "b61ff7bc-b168-475d-9b08-b36ac604db8e", + "name": "Spawner (2125)", + "location": [602, 2180, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "58876bbe-786f-496c-a9ad-de7150daedbe", + "name": "Spawner (2125)", + "location": [650, 2161, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 }, + { "name": "Armorer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "57ed6a02-61ea-4190-bdb9-ea72f43675a6", + "name": "Spawner (2125)", + "location": [659, 2141, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1fc9ddd3-9975-4095-8daf-8d86e1103009", + "name": "Spawner (2125)", + "location": [650, 2178, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c1d8091b-483b-4892-a4b6-e9b5f8ad167c", + "name": "Spawner (2125)", + "location": [656, 2235, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5ce35259-7589-479c-8194-8ef26ee0bc9a", + "name": "Spawner (2125)", + "location": [620, 2218, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Healer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ec85a134-cb7e-4b7b-bd1b-0023b55d9869", + "name": "Spawner (2125)", + "location": [562, 2148, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 5, + "entries": [{ "name": "RangerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a620a8cf-1b8e-4f4b-8485-1a70f6160ed8", + "name": "Spawner (2125)", + "location": [554, 2178, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "35d52dec-9192-40c7-b65d-bd9c41e87cdb", + "name": "Spawner (2125)", + "location": [578, 2227, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6709b04a-f462-468e-b5f5-c8cb8088f0bf", + "name": "Spawner (2125)", + "location": [592, 2277, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6d156639-7f49-432e-a874-c01be5b54a7f", + "name": "Spawner (2125)", + "location": [590, 2205, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "12775f86-e464-4ae0-bd2d-e986f9a6452b", + "name": "Spawner (2125)", + "location": [586, 2170, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4ffe4319-70b0-48c5-bb98-c3bf957662f0", + "name": "Spawner (2125)", + "location": [630, 2194, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "de7a0b5f-a21f-4e3a-871c-648aa7100c20", + "name": "Spawner (2125)", + "location": [587, 2146, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd5f20aa-6865-4e9c-91dc-1ab598efedc4", + "name": "Spawner (2125)", + "location": [627, 2163, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d63ea5ae-32d5-48c0-a9b8-4706a9cd25a7", + "name": "Spawner (2125)", + "location": [611, 2157, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d9d17a8-7908-4052-b17c-69e9417788cd", + "name": "Spawner (2125)", + "location": [570, 2121, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "978c26c6-dbea-40db-8422-e0342c0738bf", + "name": "Spawner (2125)", + "location": [582, 2186, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "843d989e-d401-464a-90b0-16b3469cb226", + "name": "Spawner (2125)", + "location": [601, 2235, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0ba9f3dc-a0df-44d2-bebd-bd26fdd99730", + "name": "Spawner (2125)", + "location": [2011, 2804, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "523e5f93-afd6-466e-bb27-db94c6bf7e0a", + "name": "Spawner (2125)", + "location": [1935, 2766, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Blacksmith", "maxCount": 1, "probability": 100 }, + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2e0e6946-627a-4f89-b9d4-795be9b51694", + "name": "Spawner (2125)", + "location": [1935, 2796, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8b43faac-d666-46c1-ba1c-de0ff4a92dad", + "name": "Spawner (2125)", + "location": [2002, 2721, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9c0ac700-6c3d-4025-93cf-6b67e28b80ae", + "name": "Spawner (2125)", + "location": [1897, 2805, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "6bb19a0b-8193-4146-bada-96ef1f69c2c9", + "name": "Spawner (2125)", + "location": [1852, 2831, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a604ee94-149c-45ed-8f2e-b95904ea6550", + "name": "Spawner (2125)", + "location": [2033, 2801, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bc9dea07-ca90-459f-bec2-b2efa4ddf507", + "name": "Spawner (2125)", + "location": [1895, 2653, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bfd3ed22-e5b3-4203-8fb2-db2587c1f196", + "name": "Spawner (2125)", + "location": [2026, 2845, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f722fc04-87d9-4293-a80b-77140c00250a", + "name": "Spawner (2125)", + "location": [1848, 2680, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "360da1b6-8e1d-4afa-aa0a-b58ae555157f", + "name": "Spawner (2125)", + "location": [1844, 2735, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d3b2922a-82a9-4c3f-ae87-d026e4865db8", + "name": "Spawner (2125)", + "location": [1990, 2889, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0d673486-6203-4256-9264-e267a7f1bd71", + "name": "Spawner (2125)", + "location": [2072, 2856, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "890e5e81-c5da-43c4-a7f1-deb55e5e8b9b", + "name": "Spawner (2125)", + "location": [1991, 2867, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9faa97bf-28e2-4e7a-82a4-0e75f227759b", + "name": "Spawner (2125)", + "location": [1911, 2805, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95eaf1c9-9b1e-4f08-845d-cf1fba01401e", + "name": "Spawner (2125)", + "location": [1897, 2684, 10], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9cd4bc8c-4f9a-4a91-9cee-70101c3804d8", + "name": "Spawner (2125)", + "location": [1822, 2739, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0f546912-c197-4d33-a1e0-1f4efddb9311", + "name": "Spawner (2125)", + "location": [2019, 2748, 30], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "f0019dbf-e6d5-44a4-8da4-16dbc44da350", + "name": "Spawner (2125)", + "location": [1981, 2838, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfed240a-6345-42ad-a99b-8fa6b48d519c", + "name": "Spawner (2125)", + "location": [1880, 2802, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "dc73fe29-ef87-477e-9390-666aac11a2fa", + "name": "Spawner (2125)", + "location": [1813, 2825, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9bdbb8fe-1b34-49c4-9cc2-6e69ad698018", + "name": "Spawner (2125)", + "location": [1850, 2796, -8], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1373dadc-9178-4022-941f-b204570faf59", + "name": "Spawner (2125)", + "location": [1939, 2739, 10], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "0746c0d5-0f6e-4d52-951f-f0e3b39bd19e", + "name": "Spawner (2125)", + "location": [1846, 2715, 10], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ca419471-6da0-47e1-88f0-dcbffd67e416", + "name": "Spawner (2125)", + "location": [2963, 813, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "FisherGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d40aef76-3174-45a1-bf02-b7a5ae236688", + "name": "Spawner (2125)", + "location": [3012, 780, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41cdd533-ce2e-4c8b-9f9b-7467f6d27778", + "name": "Spawner (2125)", + "location": [2899, 790, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tinker", "maxCount": 1, "probability": 100 }, + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "33818055-2577-4e1c-ad37-a47f989f925f", + "name": "Spawner (2125)", + "location": [2917, 798, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "22f848cc-0054-4f92-8f4b-d55e59f8c0c9", + "name": "Spawner (2125)", + "location": [2920, 856, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "90e83279-5d20-4b5e-9352-e019cd461edd", + "name": "Spawner (2125)", + "location": [2782, 969, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "48829893-5b5f-4822-a43c-a2a05549f7ca", + "name": "Spawner (2125)", + "location": [2865, 852, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "741b669a-ffda-4fcf-8617-207ce5be1530", + "name": "Spawner (2125)", + "location": [2918, 672, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d6232f09-ad45-49a6-ba0b-b4244c844d3b", + "name": "Spawner (2125)", + "location": [2992, 844, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb4405b2-a53a-4e60-a659-98ce20fc2e6a", + "name": "Spawner (2125)", + "location": [2995, 815, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "82e5da86-576b-4065-9dc1-b79b154de3fb", + "name": "Spawner (2125)", + "location": [2901, 908, 0], + "map": "Trammel", + "count": 4, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TavernKeeper", "maxCount": 1, "probability": 100 }, + { "name": "Waiter", "maxCount": 1, "probability": 100 }, + { "name": "Cook", "maxCount": 1, "probability": 100 }, + { "name": "Barkeeper", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "70a16a36-196a-48ef-b3ea-1e88ff8a7080", + "name": "Spawner (2125)", + "location": [2881, 684, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbd3af4a-dd6a-4ea9-b79b-ea071b77c495", + "name": "Spawner (2125)", + "location": [2855, 734, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "MinerGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "860c7844-79ed-4155-aa2d-1b40e99ffafb", + "name": "Spawner (2125)", + "location": [2861, 812, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "fda0fb25-6ca0-422e-836f-81bd46da8a61", + "name": "Spawner (2125)", + "location": [2882, 723, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "650d8ee5-71d1-415f-af77-e051d754adf5", + "name": "Spawner (2125)", + "location": [2961, 621, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1bc12e56-5192-41a7-9476-6d628159fcf5", + "name": "Spawner (2125)", + "location": [2840, 885, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "597858d7-3494-465b-8ee1-6bd72419b247", + "name": "Spawner (2125)", + "location": [2860, 999, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f23df328-1474-4abf-855f-08a262da0c92", + "name": "Spawner (2125)", + "location": [2998, 760, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e3eb97de-3c51-4ac5-9e7d-e34818423213", + "name": "Spawner (2125)", + "location": [3034, 827, -3], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "3ef48e85-8383-43c1-806e-66855121aac7", + "name": "Spawner (2125)", + "location": [2962, 885, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "68888d32-23aa-4e23-9295-3eaf1e026e16", + "name": "Spawner (2125)", + "location": [2835, 805, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Armorer", "maxCount": 1, "probability": 100 }, + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7829831c-f4e0-44a1-a2a9-e04196a72573", + "name": "Spawner (2125)", + "location": [2986, 637, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eff84dda-bd01-479c-a669-ca56d9fffe3d", + "name": "Spawner (2125)", + "location": [2890, 651, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "00a20674-f2f7-4d32-8f99-17333e610528", + "name": "Spawner (2125)", + "location": [2954, 705, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Beekeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "677fe1ff-5c29-4d9b-86f2-01f8f6676500", + "name": "Spawner (2125)", + "location": [2987, 777, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "bf49484f-4281-4b52-bbd7-786dbcecdf9f", + "name": "Spawner (2125)", + "location": [2841, 907, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "WarriorGuildmaster", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "2e88a973-4c4b-46fc-8012-0e5484272dc8", + "name": "Spawner (2125)", + "location": [3017, 761, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Butcher", "maxCount": 1, "probability": 100 }, + { "name": "Farmer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7753854c-8437-457c-970f-5489505ba4c6", + "name": "Spawner (2125)", + "location": [5236, 142, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "450a0f16-a428-4b13-9bea-7c1f0398f4dd", + "name": "Spawner (2125)", + "location": [5351, 56, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "e00042e4-6be0-4f04-8fc8-56c739a585d6", + "name": "Spawner (2125)", + "location": [5154, 97, 5], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b65546f-275c-45fd-9d0f-46e6639237bc", + "name": "Spawner (2125)", + "location": [5215, 117, 1], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Herbalist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "73b991d6-dce0-42b7-90dd-dd8cc7671bed", + "name": "Spawner (2125)", + "location": [5242, 180, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Scribe", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "81515819-c2a9-4db8-b752-9f06685593aa", + "name": "Spawner (2125)", + "location": [5161, 32, 17], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5d5d05fc-b9de-40cc-b2d8-015e97a4904a", + "name": "Spawner (2125)", + "location": [5148, 60, 25], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e70df5a8-1ca9-41a6-beb7-0c13ab7f2884", + "name": "Spawner (2125)", + "location": [5347, 76, 25], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98826ae5-c33c-45be-a99a-4463a5c10916", + "name": "Spawner (2125)", + "location": [5263, 129, 20], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ee02077-d7f2-439e-95b9-f9e82932f319", + "name": "Spawner (2125)", + "location": [5203, 86, 5], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "36a0853e-86e1-4024-9ef6-efa398ae07c3", + "name": "Spawner (2125)", + "location": [5218, 175, 5], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "a4040d64-c962-4fac-b73f-10a0c04cb4b8", + "name": "Spawner (2125)", + "location": [5300, 90, 15], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ea124c6c-817e-49cd-9d27-567a818cd051", + "name": "Spawner (2125)", + "location": [5306, 37, 40], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "49c6d37a-7560-4977-bcbe-045801c60af8", + "name": "Spawner (2125)", + "location": [624, 1145, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "5c458af7-1675-41b8-b95d-33a9a747b0a4", + "name": "Spawner (2125)", + "location": [631, 1034, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Shipwright", "maxCount": 1, "probability": 100 }, + { "name": "Mapmaker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd67f242-2044-4c4f-b937-9fcad53ea655", + "name": "Spawner (2125)", + "location": [540, 966, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5cecf5b2-1161-47d8-9098-c4158222a0bb", + "name": "Spawner (2125)", + "location": [553, 985, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "9bded978-79fc-4f1e-9360-de487896a391", + "name": "Spawner (2125)", + "location": [570, 969, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Bowyer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "ecd340e1-2000-4c61-8042-64e2a3a7734f", + "name": "Spawner (2125)", + "location": [517, 986, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tanner", "maxCount": 1, "probability": 100 }, + { "name": "Furtrader", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "813713e2-fce3-4dd5-b514-b0bfd0ff76a9", + "name": "Spawner (2125)", + "location": [529, 1008, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4b5a2097-735a-4740-8b6a-87f079705d42", + "name": "Spawner (2125)", + "location": [652, 820, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0468f726-d595-41a6-94a2-e073f3b30caf", + "name": "Spawner (2125)", + "location": [610, 810, 0], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "875e998f-beb1-40f4-aae2-ee36279337b5", + "name": "Spawner (2125)", + "location": [535, 867, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "78237184-1c64-4e47-962b-f8ec0ee4a823", + "name": "Spawner (2125)", + "location": [564, 1011, 0], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Carpenter", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "abda8289-add2-4cd0-97f9-3b5c2f54721e", + "name": "Spawner (2125)", + "location": [643, 1083, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "40c31da4-105d-4393-81d1-37b1e0e3e540", + "name": "Spawner (2125)", + "location": [5191, 3989, 37], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Healer", "maxCount": 1, "probability": 100 }, + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "39f529fe-2424-4e3f-9dc3-13a0e1f979f1", + "name": "Spawner (2125)", + "location": [5296, 3978, 37], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mage", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8ede9690-36bd-4b72-a43b-fccbdcc8f56c", + "name": "Spawner (2125)", + "location": [5219, 4012, 37], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Provisioner", "maxCount": 1, "probability": 100 }, + { "name": "Cobbler", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b370875b-ec9e-4655-9b0d-a2d8d8055302", + "name": "Spawner (2125)", + "location": [5275, 3977, 37], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Banker", "maxCount": 1, "probability": 100 }, + { "name": "Minter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fb0032c9-6652-41c4-b15b-8a3f64aa3968", + "name": "Spawner (2125)", + "location": [5225, 4000, 38], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "BlacksmithGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Blacksmith", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e32d662c-a5e3-4549-b2ff-4ec7a76d6961", + "name": "Spawner (2125)", + "location": [5197, 4060, 37], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "01d45bd9-1b11-4740-a5fb-d03ab174b5ab", + "name": "Spawner (2125)", + "location": [5297, 4007, 40], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "79424b72-3212-491e-afc1-ad1df19708d7", + "name": "Spawner (2125)", + "location": [5234, 4025, 37], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Tailor", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "308f6ffb-7fb6-4f92-85be-56d1756e5823", + "name": "Spawner (2125)", + "location": [5669, 3131, 14], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Minter", "maxCount": 1, "probability": 100 }, + { "name": "Banker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "463cd51f-8053-4bdb-9b1f-1ec4d7fb1813", + "name": "Spawner (2125)", + "location": [5731, 3192, 9], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "MageGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "Mage", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bff8e072-e174-4aa7-93a8-e5e61b4d957f", + "name": "Spawner (2125)", + "location": [5726, 3243, 16], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TinkerGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Tinker", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "94a33c3d-2fd0-4c9a-8424-b0315ddb023c", + "name": "Spawner (2125)", + "location": [5662, 3150, 13], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Jeweler", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "904e35eb-9ad9-433b-8717-cc1984111fb8", + "name": "Spawner (2125)", + "location": [5671, 3287, 11], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "AnimalTrainer", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "4d000fba-9496-45e5-9ba0-93e2ad003206", + "name": "Spawner (2125)", + "location": [5698, 3282, 15], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Butcher", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "8e109199-da2b-4155-9890-9a0424be41cf", + "name": "Spawner (2125)", + "location": [5805, 3300, 11], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Weaponsmith", "maxCount": 1, "probability": 100 }, + { "name": "Armorer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ad2a2a35-d46f-4b26-b787-236d6e956cd5", + "name": "Spawner (2125)", + "location": [5714, 3202, 12], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "CustomHairstylist", "maxCount": 1, "probability": 100 }, + { "name": "Alchemist", "maxCount": 1, "probability": 100 }, + { "name": "Herbalist", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd4f9c92-cb01-4d9b-bcfe-2b4e397c3c10", + "name": "Spawner (2125)", + "location": [5805, 3270, 11], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Mapmaker", "maxCount": 1, "probability": 100 }, + { "name": "Shipwright", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2d331fdc-c640-4e30-9d51-d8b28977ac69", + "name": "Spawner (2125)", + "location": [5752, 3272, 16], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "TailorGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Weaver", "maxCount": 1, "probability": 100 }, + { "name": "Tailor", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a0464876-3bdb-466d-a5a8-40f63301fafd", + "name": "Spawner (2125)", + "location": [5839, 3256, 2], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Fisherman", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "476e8e87-d2f3-4964-9bdd-3e11498cd362", + "name": "Spawner (2125)", + "location": [5746, 3200, 16], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "Baker", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "93eaa98d-0b9f-42b3-91e8-7c04cb15f971", + "name": "Spawner (2125)", + "location": [5775, 3161, 14], + "map": "Trammel", + "count": 1, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [{ "name": "InnKeeper", "maxCount": 1, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "44a0c6b8-5b7d-486e-b373-affb981dcdd5", + "name": "Spawner (2125)", + "location": [5737, 3222, 11], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "HealerGuildmaster", "maxCount": 1, "probability": 100 }, + { "name": "Healer", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b65a0806-7fee-4836-9e8e-e56e2d9d5f09", + "name": "Spawner (2125)", + "location": [5691, 3209, 11], + "map": "Trammel", + "count": 3, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "RealEstateBroker", "maxCount": 1, "probability": 100 }, + { "name": "Architect", "maxCount": 1, "probability": 100 }, + { "name": "Carpenter", "maxCount": 1, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4cdd8db5-8499-4956-b094-1e77596039fe", + "name": "Spawner (2125)", + "location": [5736, 3263, 15], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 0, + "walkingRange": 5, + "entries": [ + { "name": "Cobbler", "maxCount": 1, "probability": 100 }, + { "name": "Provisioner", "maxCount": 1, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/uoml/trammel/WildLife.json b/Distribution/Data/Spawns/uoml/trammel/WildLife.json index f1df7b053..8f372de0b 100644 --- a/Distribution/Data/Spawns/uoml/trammel/WildLife.json +++ b/Distribution/Data/Spawns/uoml/trammel/WildLife.json @@ -1,6 +1,278 @@ [ { - "$type": "Spawner", + "type": "Spawner", + "guid": "36d0217e-7fa6-4abb-a59c-572441f8e069", + "name": "Spawner (2126)", + "location": [3394, 628, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "036d98ab-980c-4af8-b924-dd900a6f438c", + "name": "Spawner (2126)", + "location": [3232, 510, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "32c27855-0fa7-42cc-b9bb-749f3c6c737c", + "name": "Spawner (2126)", + "location": [4478, 3218, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "376b8121-d1b0-4461-83c7-37f1920fa4a7", + "name": "Spawner (2126)", + "location": [4446, 3706, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f309b0c8-cbe1-4a42-a2a6-0b5bbadcdf70", + "name": "Spawner (2126)", + "location": [4682, 3662, 77], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "95395203-fa7a-46ab-9c61-360659b66537", + "name": "Spawner (2126)", + "location": [4692, 3494, 1], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "fc540fa1-f1f8-45b4-8d06-f1d91b4575b0", + "name": "Spawner (2126)", + "location": [4560, 3504, 2], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "45803a8e-701d-4b75-9ef0-b6705a0499c6", + "name": "Spawner (2126)", + "location": [4612, 3424, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", "guid": "85bca9f2-2bf4-4400-ac33-e72e379d2bb0", "name": "Spawner (2126)", "location": [3607, 2417, 45], @@ -34,7 +306,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "2828aaaa-15bc-48ca-8200-b30244c9e88f", "name": "Spawner (2126)", "location": [3555, 2525, 13], @@ -68,7 +340,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "bf2cf539-4f86-4a2c-81a2-6d09d6a36dee", "name": "Spawner (2126)", "location": [3506, 2477, 28], @@ -102,7 +374,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "335130b1-2341-4f80-88b6-1a2b1261d158", "name": "Spawner (2126)", "location": [3452, 2563, 32], @@ -136,7 +408,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "21e1d3a4-1447-4e38-9b17-70a49c7b9093", "name": "Spawner (2126)", "location": [3429, 2650, 43], @@ -170,7 +442,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "37fab5a5-6dcf-4ea0-9bcf-cee7546a8788", "name": "Spawner (2126)", "location": [3449, 2737, 49], @@ -204,7 +476,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "85d1ae4d-d1dd-431f-a749-7ba9e19b7adc", "name": "Spawner (2126)", "location": [3736, 2512, 35], @@ -238,7 +510,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "03dcb299-f89d-4554-8700-c389b70b560e", "name": "Spawner (2126)", "location": [3718, 2736, 38], @@ -272,7 +544,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "4415e8ab-b437-46d7-a245-8056c9611fa4", "name": "Spawner (2126)", "location": [3654, 2866, 61], @@ -306,7 +578,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "363b94f4-e1ba-474a-ab21-76e457b14abf", "name": "Spawner (2126)", "location": [3662, 2802, 51], @@ -340,7 +612,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "8d4ae520-6345-40f2-b7cb-3bb2e98d2219", "name": "Spawner (2126)", "location": [3596, 2804, 51], @@ -374,7 +646,7 @@ ] }, { - "$type": "Spawner", + "type": "Spawner", "guid": "1105d916-9527-4d49-8ca2-54e4ca61a342", "name": "Spawner (2126)", "location": [3546, 2812, 13], @@ -406,5 +678,5411 @@ { "name": "Bull", "maxCount": 2, "probability": 100 }, { "name": "Cow", "maxCount": 2, "probability": 100 } ] + }, + { + "type": "Spawner", + "guid": "b1ed0cd3-8f33-44ee-b48a-ccf8bfccbc62", + "name": "Spawner (2126)", + "location": [3650, 2080, 21], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e0e440be-1903-4921-8001-c203b99824d0", + "name": "Spawner (2126)", + "location": [3608, 2140, 79], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7efa7e94-9493-4113-9804-2997584755fd", + "name": "Spawner (2126)", + "location": [4384, 1218, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a339b0e-c70e-4798-8210-3f3e2de53574", + "name": "Spawner (2126)", + "location": [4468, 1006, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "029a425c-6944-4f56-b9de-a3a007961469", + "name": "Spawner (2126)", + "location": [4326, 1046, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f2ed951-4709-4c94-92ce-0fb0d21bd5f2", + "name": "Spawner (2126)", + "location": [4544, 1128, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "259f44e8-a7d3-41cd-b3a8-d26f3458876d", + "name": "Spawner (2126)", + "location": [4510, 1262, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9f56a1bd-2813-46c3-a48e-df1bddf67a93", + "name": "Spawner (2126)", + "location": [4590, 1326, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a3a4287c-82dd-4d54-ae9a-fc0de236aaee", + "name": "Spawner (2126)", + "location": [4526, 986, 10], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1252b4d8-f8ff-41e4-be2b-45c0ed2c62e5", + "name": "Spawner (2126)", + "location": [4336, 1130, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "413394e4-0a86-4b97-ad09-b787f7f95b13", + "name": "Spawner (2126)", + "location": [4448, 1334, 3], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b65d99d3-a2c3-404c-9db5-e261da0b012d", + "name": "Spawner (2126)", + "location": [4540, 1454, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72fa3aa7-e804-424f-b789-4f0ab28575a6", + "name": "Spawner (2126)", + "location": [4632, 1164, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0d47483e-14b9-4949-8e00-76c5431539d7", + "name": "Spawner (2126)", + "location": [4470, 1430, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b04c36d0-8c69-46f0-a48c-2b947622bb3f", + "name": "Spawner (2126)", + "location": [4592, 1424, 3], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8742ec13-802c-4bdc-80bb-37c4d6031baf", + "name": "Spawner (2126)", + "location": [2814, 3500, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f143629b-57cc-4c98-a2bd-06c7a3f00e70", + "name": "Spawner (2126)", + "location": [2998, 3590, 15], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a7eb4c9b-fb14-4e00-9a15-e51095c2565b", + "name": "Spawner (2126)", + "location": [2864, 150, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "af606966-fb70-4d7f-8ad6-fcf293fbf012", + "name": "Spawner (2126)", + "location": [3292, 570, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cdaade9c-e8af-48ed-8fd1-7326a2ddba80", + "name": "Spawner (2126)", + "location": [3206, 366, 9], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72348476-489b-41c1-b61e-6900a22a61b5", + "name": "Spawner (2126)", + "location": [3172, 664, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0b7d99ad-681f-4b64-99c3-cda74412043b", + "name": "Spawner (2126)", + "location": [3392, 424, 9], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d5a4c442-7233-4c40-950f-51aea9e80674", + "name": "Spawner (2126)", + "location": [3076, 150, 1], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2af04186-1f66-4c97-8893-21966a8d38ff", + "name": "Spawner (2126)", + "location": [3092, 542, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c6377b5f-e04c-457e-9ec5-3e45c8302d93", + "name": "Spawner (2126)", + "location": [2512, 1066, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0fe92a55-c40e-49a3-9658-ebb41b284a60", + "name": "Spawner (2126)", + "location": [2554, 1142, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0f8ebac9-7bfd-4cb5-9c30-a69baa46be7b", + "name": "Spawner (2126)", + "location": [2416, 1172, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8231afb-b1d4-48ae-bb33-0b6da6d81411", + "name": "Spawner (2126)", + "location": [2188, 1986, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ee0e83f-fd06-4c5d-91d2-74e5b3750f53", + "name": "Spawner (2126)", + "location": [2654, 2286, 9], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f0da5cc6-e368-44e9-a745-e6744fd2edf7", + "name": "Spawner (2126)", + "location": [2946, 2168, 44], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0022416f-f5eb-483c-9ad4-eac25ac3af57", + "name": "Spawner (2126)", + "location": [2744, 2014, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "21f01798-bc50-4ce5-95d9-16c900a28689", + "name": "Spawner (2126)", + "location": [2786, 2282, 13], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "99f98b9c-1885-479d-882c-f8251711f59d", + "name": "Spawner (2126)", + "location": [2074, 1972, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f7a461d8-c310-48db-8dea-fb3a61fe9504", + "name": "Spawner (2126)", + "location": [2122, 2108, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3c28cde3-8cbf-4b0f-9a22-c4da0f91a9ef", + "name": "Spawner (2126)", + "location": [1932, 3125, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "1ceff4e8-8c0f-4974-80dc-09aa04d29799", + "name": "Spawner (2126)", + "location": [1824, 3027, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd5716e7-8f5b-4d35-9b8e-f2f3af6829de", + "name": "Spawner (2126)", + "location": [1894, 2942, 19], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5001467d-867b-4f25-a046-9b42cfce15af", + "name": "Spawner (2126)", + "location": [1222, 2499, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba1ea3b8-e6b1-4231-b2be-961396c2bfab", + "name": "Spawner (2126)", + "location": [1005, 2647, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d61f40c0-87d2-4fc2-8282-5a6eedadd553", + "name": "Spawner (2126)", + "location": [464, 2059, 8], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7501f45b-0531-492c-91ea-de33a4a305dd", + "name": "Spawner (2126)", + "location": [559, 2081, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bc12e17d-1f65-4e97-a222-9fc7bf0f4444", + "name": "Spawner (2126)", + "location": [883, 2129, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b9457f8a-483a-4f47-bfa9-7a301f3e22d4", + "name": "Spawner (2126)", + "location": [968, 2476, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b2f68c1-edb6-401d-a4e4-e2c5e25d3560", + "name": "Spawner (2126)", + "location": [994, 2361, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cfc04314-5772-4b9c-a50a-3652e7be42ea", + "name": "Spawner (2126)", + "location": [964, 2214, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0e3d2fd4-d529-45bd-a135-56ca809d2e35", + "name": "Spawner (2126)", + "location": [1087, 2401, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3c776b4-3387-47ec-b8b6-6c6320e446f2", + "name": "Spawner (2126)", + "location": [1222, 2378, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4b520b06-67a4-4d03-9ac8-1b6b89918338", + "name": "Spawner (2126)", + "location": [1334, 2311, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae37664f-7df6-4a33-853e-b577723971be", + "name": "Spawner (2126)", + "location": [1315, 2192, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "72d26907-465d-42cf-b64a-7b70c3f21b5e", + "name": "Spawner (2126)", + "location": [1426, 2615, 10], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8c11ab8d-0f10-4d9d-9b58-4d152567074f", + "name": "Spawner (2126)", + "location": [1527, 2609, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cf3b84c2-c2b7-4692-ac18-e5c9df1e3a94", + "name": "Spawner (2126)", + "location": [1582, 2722, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2f41e23-c367-4172-8dec-c42709950de9", + "name": "Spawner (2126)", + "location": [1650, 2796, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c157367d-9508-4b38-9472-f87683a4756d", + "name": "Spawner (2126)", + "location": [1724, 2781, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b25b4e76-d62b-4f92-89d8-6d72908611ec", + "name": "Spawner (2126)", + "location": [1727, 2702, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e8d9152c-1bce-4b09-b08a-f43be0cd650e", + "name": "Spawner (2126)", + "location": [1634, 2645, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0950a31a-0b42-481f-8658-46d97e5047f5", + "name": "Spawner (2126)", + "location": [1505, 2445, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "832fae60-bc7c-4675-8bf1-747404eb52c2", + "name": "Spawner (2126)", + "location": [1699, 2560, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4aecd554-03ce-4054-aa4b-e549bcda9099", + "name": "Spawner (2126)", + "location": [1612, 2526, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "eaa6d886-bacf-4ada-8dd9-b4059ed58700", + "name": "Spawner (2126)", + "location": [1722, 2435, 10], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "edf184b7-267e-475a-9e9d-6a4da4720a0b", + "name": "Spawner (2126)", + "location": [1715, 2307, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "03f454af-c939-406a-b7df-3b11264c1e56", + "name": "Spawner (2126)", + "location": [1531, 2351, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8e639d7-7836-4fcc-9420-e40bee6383de", + "name": "Spawner (2126)", + "location": [1602, 2426, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5ac54db3-d755-475f-84df-bec218f106b7", + "name": "Spawner (2126)", + "location": [1628, 2334, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "37ca5711-d9e9-4754-ab12-d72282bd3775", + "name": "Spawner (2126)", + "location": [1563, 2277, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "96c8d9da-bf02-4fd8-8578-1b2ed732d687", + "name": "Spawner (2126)", + "location": [1503, 2207, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "06dc791a-faa3-4301-b930-038655903d16", + "name": "Spawner (2126)", + "location": [1709, 1992, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "75f30f5a-a828-474c-85a0-4f58fad8bbf0", + "name": "Spawner (2126)", + "location": [1498, 2040, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "faedd551-9d07-4233-b959-540762d502b7", + "name": "Spawner (2126)", + "location": [1439, 2104, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7da8b2c6-8444-46a1-8dc8-78ff324fb2cf", + "name": "Spawner (2126)", + "location": [1387, 2023, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a6c38b89-808a-44b4-a5c7-aac41fab07d6", + "name": "Spawner (2126)", + "location": [1377, 1911, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3e5a953-2208-455f-9bd5-e160f387afbc", + "name": "Spawner (2126)", + "location": [1459, 1963, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bb9ece00-1e1a-4a80-8c19-b85b54ba4be6", + "name": "Spawner (2126)", + "location": [1268, 2018, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5e97f47f-63ea-4b3c-97aa-252f922c1fc3", + "name": "Spawner (2126)", + "location": [1173, 2103, 2], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e68d9d2b-ce90-42ab-8a2d-87437babe0a8", + "name": "Spawner (2126)", + "location": [1070, 2069, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c5b3fb52-ce57-429c-a393-883235b01489", + "name": "Spawner (2126)", + "location": [958, 2074, 1], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dccc7e1b-7ef9-44f3-9c3f-c2395b4c739a", + "name": "Spawner (2126)", + "location": [999, 1839, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4a5e2ae8-f5bb-49a4-a405-c392bd88e885", + "name": "Spawner (2126)", + "location": [1011, 1945, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "caa501da-6efa-4136-8573-1b996c048e85", + "name": "Spawner (2126)", + "location": [1229, 1655, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "41d87f21-c25b-40bb-8a40-79cd984daac9", + "name": "Spawner (2126)", + "location": [1725, 1447, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "388f1cb5-ac9c-4127-ad37-0fd812bfb769", + "name": "Spawner (2126)", + "location": [1798, 1471, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0228c6c2-bc57-4dad-864a-f33844129d0a", + "name": "Spawner (2126)", + "location": [1958, 1305, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0779e1d7-6a80-4907-83c9-a7b8a1cbd8bb", + "name": "Spawner (2126)", + "location": [1867, 1244, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f2687613-183f-4bd0-9103-d7bd70639793", + "name": "Spawner (2126)", + "location": [1865, 1379, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7e32abac-5a5c-424e-b50f-3e539dbb1556", + "name": "Spawner (2126)", + "location": [1769, 1330, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "837adc9c-cd37-4302-9bf3-cf063de88772", + "name": "Spawner (2126)", + "location": [1666, 1290, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d3ef6251-4ee9-4e19-aa70-db8ea6f92bed", + "name": "Spawner (2126)", + "location": [1486, 1301, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9aa8d68c-2713-4500-aedf-95471c1b15e9", + "name": "Spawner (2126)", + "location": [1351, 1359, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c96e7fd6-bd4a-407b-8fe8-5116adb5c0e9", + "name": "Spawner (2126)", + "location": [1231, 1462, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98b988f2-a420-45e7-8ba5-2bda0ae81f01", + "name": "Spawner (2126)", + "location": [1017, 1726, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "02d75559-5e11-47dd-9520-76f8d798f286", + "name": "Spawner (2126)", + "location": [398, 1305, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ebec0b27-0c1c-403f-abdc-9b7866570a8a", + "name": "Spawner (2126)", + "location": [485, 1398, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "805e07bf-4287-4e09-8e30-437284cc96ec", + "name": "Spawner (2126)", + "location": [210, 1447, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bf6dee8d-749a-4894-a4f7-6b0c179aa2d4", + "name": "Spawner (2126)", + "location": [851, 1770, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8fe92be0-9b3d-4ef0-850b-0e64e571d47c", + "name": "Spawner (2126)", + "location": [977, 1638, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6903cd58-d9a3-4d0c-ab3b-41e09513bddb", + "name": "Spawner (2126)", + "location": [919, 1718, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "333e31a1-765c-4086-bc1f-5e9f585929aa", + "name": "Spawner (2126)", + "location": [876, 1639, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "3d62d25b-d1a9-4b3b-ba9d-273f3365465e", + "name": "Spawner (2126)", + "location": [805, 1570, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a107d63c-c00c-4dff-9196-726441942476", + "name": "Spawner (2126)", + "location": [725, 1483, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b8b734bb-93cf-40c5-9178-20bc299963b3", + "name": "Spawner (2126)", + "location": [623, 1328, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "25b8399a-d467-41ec-806f-e26ac6b3761a", + "name": "Spawner (2126)", + "location": [727, 1394, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "df245d50-9023-49ba-acc7-1cd6c8e27043", + "name": "Spawner (2126)", + "location": [1020, 1360, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2a5e8596-bdb5-4766-b431-45bb6cefbd6d", + "name": "Spawner (2126)", + "location": [1024, 1244, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c4bfe60c-9ce2-4739-974e-5b0c453aa1f6", + "name": "Spawner (2126)", + "location": [802, 1280, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aa7f7549-b005-4b92-950f-329e555c8bcd", + "name": "Spawner (2126)", + "location": [855, 934, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a8a74bc4-a2ed-4d10-b573-cf0ef0fb69db", + "name": "Spawner (2126)", + "location": [771, 1013, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "98f98072-a630-4276-abed-7d07faba2350", + "name": "Spawner (2126)", + "location": [846, 1071, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9adcc4e-105e-4f9d-9044-4112ff31f5bc", + "name": "Spawner (2126)", + "location": [940, 1028, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b862532b-c3c1-473e-9c75-2970ef697583", + "name": "Spawner (2126)", + "location": [1152, 812, 5], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5403a56a-456b-42ab-9518-889d08375356", + "name": "Spawner (2126)", + "location": [1011, 896, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d41167e6-4e39-4443-a288-1ecbd92a54c2", + "name": "Spawner (2126)", + "location": [1057, 765, 1], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8665d1be-a08f-41b7-9450-c10bb70577f2", + "name": "Spawner (2126)", + "location": [974, 374, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4695be96-fea3-4b09-96ab-b8d7246a785f", + "name": "Spawner (2126)", + "location": [1063, 541, 16], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f9b091d7-b8ac-49f8-b6b0-420e2f503413", + "name": "Spawner (2126)", + "location": [1018, 629, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b96c91c8-d294-44ec-b8ce-13ef5c35f2d4", + "name": "Spawner (2126)", + "location": [951, 542, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ec2190b0-7f31-40b6-a350-c1bf44d2098a", + "name": "Spawner (2126)", + "location": [805, 576, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b2a798ca-14e2-4f65-a3d8-b361e2bec521", + "name": "Spawner (2126)", + "location": [937, 743, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9ae00c78-f9de-4faa-b2e2-330e69da4478", + "name": "Spawner (2126)", + "location": [890, 657, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d496d8f3-ba8e-47b5-ab86-c4b6a7cba030", + "name": "Spawner (2126)", + "location": [895, 830, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b3cfc4c1-7fdb-438c-b51d-40211ab8e2fa", + "name": "Spawner (2126)", + "location": [689, 753, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "bd3e9539-89a7-4ccb-b6f4-f9a1621c6f6c", + "name": "Spawner (2126)", + "location": [748, 669, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e81bc46f-505a-4020-8e34-026a669c750a", + "name": "Spawner (2126)", + "location": [764, 822, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4339d779-ba0b-4a17-aa43-7b29eccd40e4", + "name": "Spawner (2126)", + "location": [834, 751, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7c598b76-349e-42b5-8a5a-f7395b444def", + "name": "Spawner (2126)", + "location": [782, 1156, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c9ba90ce-6733-4684-a385-858a97a81829", + "name": "Spawner (2126)", + "location": [887, 1223, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0a898cee-dada-46e4-89f6-2f32a901de39", + "name": "Spawner (2126)", + "location": [916, 1159, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ce46d463-3146-4ed2-9c0f-6d7d85c92d69", + "name": "Spawner (2126)", + "location": [993, 1130, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dd753390-ed2c-4648-a7d8-5b6afa41f989", + "name": "Spawner (2126)", + "location": [1047, 1071, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "348ada5a-4afd-47f1-a414-a59766d0f0db", + "name": "Spawner (2126)", + "location": [1092, 1010, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ae784e69-bee7-4043-88c1-852832876c8c", + "name": "Spawner (2126)", + "location": [1172, 974, 2], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "4efec350-2605-46b3-923f-be57885e6aad", + "name": "Spawner (2126)", + "location": [1388, 784, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f8830c8c-49c9-4ffd-b299-c5412d510b60", + "name": "Spawner (2126)", + "location": [1631, 640, 22], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "480a555e-947b-4181-b211-4ed3fef3df68", + "name": "Spawner (2126)", + "location": [1526, 960, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b94d2950-01d9-45d2-a308-c4d863487ffb", + "name": "Spawner (2126)", + "location": [1545, 813, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "60301ceb-5d40-41b1-81f3-d4581dbbb3b2", + "name": "Spawner (2126)", + "location": [1695, 809, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "cd05b1bf-8c39-43cc-82f8-658b0a7837de", + "name": "Spawner (2126)", + "location": [1728, 691, 6], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9d939fac-db2a-4bf7-bb3c-5cf12a16057d", + "name": "Spawner (2126)", + "location": [1802, 495, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "46baf50c-622c-4cfc-9a0c-cb38c883b270", + "name": "Spawner (2126)", + "location": [1857, 600, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "9fa7f8cb-f1b0-47ef-b274-fae3fdc58df7", + "name": "Spawner (2126)", + "location": [1884, 735, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "e3d015a1-d167-47e6-8f63-e03f77a29019", + "name": "Spawner (2126)", + "location": [1973, 751, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b585a36d-a2d6-48c1-a584-59d29425a6cd", + "name": "Spawner (2126)", + "location": [2085, 658, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ead3c54c-7f7a-4472-8862-160168ce877d", + "name": "Spawner (2126)", + "location": [2367, 748, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6fbfa112-3466-4ae1-b215-45b81f270cca", + "name": "Spawner (2126)", + "location": [2289, 917, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d8c09fcb-e2a0-466b-884c-b9859c718d3d", + "name": "Spawner (2126)", + "location": [2338, 989, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "437c3cc4-ad9f-4da6-932c-b342014445a9", + "name": "Spawner (2126)", + "location": [2418, 999, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "5275d813-843e-497f-8801-103eaaf41f50", + "name": "Spawner (2126)", + "location": [2495, 978, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "25b9f741-b303-4341-ad1a-e4eb7973d3c9", + "name": "Spawner (2126)", + "location": [2583, 993, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "43cb2d22-fd8c-4256-860d-22cf169311a5", + "name": "Spawner (2126)", + "location": [2513, 782, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f444d0ce-22f3-44d5-b9c2-79a8092e1b07", + "name": "Spawner (2126)", + "location": [2748, 660, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "ba7c897f-c0ce-44c1-9141-7b3cfbb65791", + "name": "Spawner (2126)", + "location": [2765, 754, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "76e14719-a7c5-4ff5-9ca4-65d2bedd8229", + "name": "Spawner (2126)", + "location": [2668, 1020, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a52ff072-2c3d-428e-9d26-cdc4c4b4217f", + "name": "Spawner (2126)", + "location": [2669, 922, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "6554a940-d516-4359-b224-3d7984509f21", + "name": "Spawner (2126)", + "location": [2672, 814, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "110affec-d184-4b10-89a3-db7ed18d0f29", + "name": "Spawner (2126)", + "location": [2643, 728, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "f1c3581a-b27d-41a6-98ce-3db7acc36783", + "name": "Spawner (2126)", + "location": [2888, 391, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "c3c89bb5-3c88-4d7b-8bcf-57f0e40d3d59", + "name": "Spawner (2126)", + "location": [2735, 417, 15], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "d98a0bd4-2d35-4400-ac73-df07a0390baf", + "name": "Spawner (2126)", + "location": [2914, 547, 0], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "261fe98c-05ae-446c-8e4c-6ab6a5c4646b", + "name": "Spawner (2126)", + "location": [2814, 492, 15], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "dbead4d1-29b0-43a8-a5a6-5ceb94ca384d", + "name": "Spawner (2126)", + "location": [2688, 524, 11], + "map": "Trammel", + "count": 19, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 50, + "walkingRange": 50, + "entries": [ + { "name": "Boar", "maxCount": 7, "probability": 100 }, + { "name": "Cougar", "maxCount": 7, "probability": 100 }, + { "name": "Goat", "maxCount": 7, "probability": 100 }, + { "name": "Horse", "maxCount": 7, "probability": 100 }, + { "name": "Panther", "maxCount": 7, "probability": 100 }, + { "name": "Pig", "maxCount": 7, "probability": 100 }, + { "name": "Sheep", "maxCount": 7, "probability": 100 }, + { "name": "BlackBear", "maxCount": 5, "probability": 100 }, + { "name": "GrizzlyBear", "maxCount": 5, "probability": 100 }, + { "name": "BrownBear", "maxCount": 5, "probability": 100 }, + { "name": "TimberWolf", "maxCount": 5, "probability": 100 }, + { "name": "GreyWolf", "maxCount": 5, "probability": 100 }, + { "name": "WanderingHealer", "maxCount": 1, "probability": 100 }, + { "name": "Bird", "maxCount": 2, "probability": 100 }, + { "name": "Eagle", "maxCount": 2, "probability": 100 }, + { "name": "GreatHart", "maxCount": 2, "probability": 100 }, + { "name": "Hind", "maxCount": 2, "probability": 100 }, + { "name": "Bull", "maxCount": 2, "probability": 100 }, + { "name": "Cow", "maxCount": 2, "probability": 100 } + ] } ] diff --git a/Distribution/Data/Spawns/uoml/trammel/Wrong.json b/Distribution/Data/Spawns/uoml/trammel/Wrong.json new file mode 100644 index 000000000..adc10ee1c --- /dev/null +++ b/Distribution/Data/Spawns/uoml/trammel/Wrong.json @@ -0,0 +1,161 @@ +[ + { + "type": "Spawner", + "guid": "4c508240-4996-456a-904f-164a1574fda9", + "name": "Spawner (2127)", + "location": [5819, 591, 0], + "map": "Trammel", + "count": 2, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 5, + "walkingRange": 25, + "entries": [{ "name": "JukaWarrior", "maxCount": 2, "probability": 100 }] + }, + { + "type": "Spawner", + "guid": "d0aa4dc9-71ae-41c9-b16a-457c9946ccc4", + "name": "Spawner (2127)", + "location": [5808, 588, 12], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 15, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "2292c4c5-79a7-42fe-89a0-4cca8b5c9ae2", + "name": "Spawner (2127)", + "location": [5792, 544, 10], + "map": "Trammel", + "count": 8, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 6, + "walkingRange": 30, + "entries": [ + { "name": "GolemController", "maxCount": 4, "probability": 100 }, + { "name": "Golem", "maxCount": 4, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "7efd5b02-49ba-4d20-a39e-82d9bb6622bb", + "name": "Spawner (2127)", + "location": [5857, 586, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "0fe79572-c943-489f-917b-ac21ab051e1e", + "name": "Spawner (2127)", + "location": [5857, 562, 15], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 7, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "a3e4a12b-3ec2-4c5e-a333-211c738e2105", + "name": "Spawner (2127)", + "location": [5828, 530, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "b316ad79-73f6-44b1-99e7-34845ddd72cf", + "name": "Spawner (2127)", + "location": [5659, 563, 20], + "map": "Trammel", + "count": 9, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 10, + "walkingRange": 30, + "entries": [ + { "name": "Brigand", "maxCount": 4, "probability": 100 }, + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "8d70da7b-2571-4c11-a5fe-4f9a8a3d4083", + "name": "Spawner (2127)", + "location": [5724, 561, 20], + "map": "Trammel", + "count": 6, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 30, + "entries": [ + { "name": "Golem", "maxCount": 1, "probability": 100 }, + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + }, + { + "type": "Spawner", + "guid": "aab64623-d091-4058-9ce0-6ca3c039b824", + "name": "Spawner (2127)", + "location": [5690, 534, 0], + "map": "Trammel", + "count": 5, + "minDelay": "00:05:00", + "maxDelay": "00:10:00", + "team": 0, + "homeRange": 8, + "walkingRange": 30, + "entries": [ + { "name": "JukaLord", "maxCount": 1, "probability": 100 }, + { "name": "JukaMage", "maxCount": 1, "probability": 100 }, + { "name": "JukaWarrior", "maxCount": 3, "probability": 100 } + ] + } +] diff --git a/Distribution/Data/assemblies.json b/Distribution/Data/assemblies.json index fc24c4266..147fa946f 100644 --- a/Distribution/Data/assemblies.json +++ b/Distribution/Data/assemblies.json @@ -1,3 +1,4 @@ [ - "UOContent.dll" + "UOContent.dll", + "AddonExample.dll" ] diff --git a/Distribution/Data/commands/template.html b/Distribution/Data/commands.html similarity index 100% rename from Distribution/Data/commands/template.html rename to Distribution/Data/commands.html diff --git a/Distribution/Data/npc-speeds.json b/Distribution/Data/npc-speeds.json index 707f61e4a..6e07859d5 100644 --- a/Distribution/Data/npc-speeds.json +++ b/Distribution/Data/npc-speeds.json @@ -3,16 +3,12 @@ "level": "VerySlow", "active": 0.4, "passive": 0.8, - "activeMove": 0.9, - "passiveMove": 1.5, "types": [] }, { "level": "Slow", "active": 0.3, "passive": 0.6, - "activeMove": 0.6, - "passiveMove": 1.2, "types": [ "AntLion", "ArcticOgreLord", "BogThing", "Bogle", "BoneKnight", "EarthElemental", @@ -32,8 +28,6 @@ "level": "Medium", "active": 0.25, "passive": 0.5, - "activeMove": 0.45, - "passiveMove": 1.05, "types": [ "AcidElemental", "AgapiteElemental", "Alligator", "AncientLich", "Betrayer", "Bird", @@ -114,8 +108,6 @@ "level": "Fast", "active": 0.2, "passive": 0.4, - "activeMove": 0.3, - "passiveMove": 0.9, "types": [ "LordOaks", "Silvani", "AirElemental", "AncientWyrm", "Balron", "BladeSpirits", @@ -147,8 +139,6 @@ "level": "VeryFast", "active": 0.125, "passive": 0.30, - "activeMove": 0.125, - "passiveMove": 0.6, "types": [ "Barracoon", "Mephitis", "Neira", "Rikktor", "Semidar", "EnergyVortex", diff --git a/Distribution/Data/skills.json b/Distribution/Data/skills.json index c70ac7206..f3e4b28eb 100644 --- a/Distribution/Data/skills.json +++ b/Distribution/Data/skills.json @@ -915,12 +915,12 @@ "SkillID": 57, "Name": "Throwing", "Title": "Bladeweaver", - "StrScale": 0.025, - "DexScale": 0.075, + "StrScale": 0, + "DexScale": 0, "IntScale": 0, - "StatTotal": 10, - "StrGain": 0.25, - "DexGain": 0.75, + "StatTotal": 0, + "StrGain": 0, + "DexGain": 0, "IntGain": 0, "GainFactor": 1, "ProfessionSkillName": "Throwing", diff --git a/GEMINI.md b/GEMINI.md deleted file mode 100644 index 2fe47ff90..000000000 --- a/GEMINI.md +++ /dev/null @@ -1 +0,0 @@ -Read and follow all instructions in CLAUDE.md in this repository's root. diff --git a/ModernUO.sln b/ModernUO.sln new file mode 100644 index 000000000..d280756b2 --- /dev/null +++ b/ModernUO.sln @@ -0,0 +1,75 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33927.249 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "Projects\Server\Server.csproj", "{5E93BB35-3661-4822-9A8A-859726BAD87F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UOContent", "Projects\UOContent\UOContent.csproj", "{83CF2484-BCCB-4B7C-9C5F-7AB43AEA5E8F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.Tests", "Projects\Server.Tests\Server.Tests.csproj", "{D7A5D3AF-D607-46EF-BAAD-0D424190311F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UOContent.Tests", "Projects\UOContent.Tests\UOContent.Tests.csproj", "{3C4797F9-603E-44EF-8E8C-9275CC9EA74B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "Projects\Application\Application.csproj", "{E9849FA1-D4F5-4D68-A36B-249F4CB4E374}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddonExample", "Projects\AddonExample\AddonExample.csproj", "{73F435F7-7D30-4275-8B74-D80A0BD7AC23}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logger", "Projects\Logger\Logger.csproj", "{ECAD3793-A7C5-4546-AA88-77DD24574410}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Analyze|Any CPU = Analyze|Any CPU + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5E93BB35-3661-4822-9A8A-859726BAD87F}.Analyze|Any CPU.ActiveCfg = Analyze|Any CPU + {5E93BB35-3661-4822-9A8A-859726BAD87F}.Analyze|Any CPU.Build.0 = Analyze|Any CPU + {5E93BB35-3661-4822-9A8A-859726BAD87F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E93BB35-3661-4822-9A8A-859726BAD87F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E93BB35-3661-4822-9A8A-859726BAD87F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E93BB35-3661-4822-9A8A-859726BAD87F}.Release|Any CPU.Build.0 = Release|Any CPU + {83CF2484-BCCB-4B7C-9C5F-7AB43AEA5E8F}.Analyze|Any CPU.ActiveCfg = Analyze|Any CPU + {83CF2484-BCCB-4B7C-9C5F-7AB43AEA5E8F}.Analyze|Any CPU.Build.0 = Analyze|Any CPU + {83CF2484-BCCB-4B7C-9C5F-7AB43AEA5E8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83CF2484-BCCB-4B7C-9C5F-7AB43AEA5E8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83CF2484-BCCB-4B7C-9C5F-7AB43AEA5E8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83CF2484-BCCB-4B7C-9C5F-7AB43AEA5E8F}.Release|Any CPU.Build.0 = Release|Any CPU + {D7A5D3AF-D607-46EF-BAAD-0D424190311F}.Analyze|Any CPU.ActiveCfg = Analyze|Any CPU + {D7A5D3AF-D607-46EF-BAAD-0D424190311F}.Analyze|Any CPU.Build.0 = Analyze|Any CPU + {D7A5D3AF-D607-46EF-BAAD-0D424190311F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7A5D3AF-D607-46EF-BAAD-0D424190311F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7A5D3AF-D607-46EF-BAAD-0D424190311F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7A5D3AF-D607-46EF-BAAD-0D424190311F}.Release|Any CPU.Build.0 = Release|Any CPU + {3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Analyze|Any CPU.ActiveCfg = Analyze|Any CPU + {3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Analyze|Any CPU.Build.0 = Analyze|Any CPU + {3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Release|Any CPU.Build.0 = Release|Any CPU + {E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Analyze|Any CPU.ActiveCfg = Analyze|Any CPU + {E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Analyze|Any CPU.Build.0 = Analyze|Any CPU + {E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9849FA1-D4F5-4D68-A36B-249F4CB4E374}.Release|Any CPU.Build.0 = Release|Any CPU + {73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Analyze|Any CPU.ActiveCfg = Analyze|Any CPU + {73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Analyze|Any CPU.Build.0 = Analyze|Any CPU + {73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73F435F7-7D30-4275-8B74-D80A0BD7AC23}.Release|Any CPU.Build.0 = Release|Any CPU + {ECAD3793-A7C5-4546-AA88-77DD24574410}.Analyze|Any CPU.ActiveCfg = Analyze|Any CPU + {ECAD3793-A7C5-4546-AA88-77DD24574410}.Analyze|Any CPU.Build.0 = Analyze|Any CPU + {ECAD3793-A7C5-4546-AA88-77DD24574410}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECAD3793-A7C5-4546-AA88-77DD24574410}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECAD3793-A7C5-4546-AA88-77DD24574410}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECAD3793-A7C5-4546-AA88-77DD24574410}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8F69EF13-E193-40B9-BA9F-4862A9F0C038} + EndGlobalSection +EndGlobal diff --git a/ModernUO.slnx b/ModernUO.slnx deleted file mode 100644 index 5697dface..000000000 --- a/ModernUO.slnx +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/Projects/AddonExample/001-new-project.png b/Projects/AddonExample/001-new-project.png new file mode 100644 index 000000000..3e1e39b98 Binary files /dev/null and b/Projects/AddonExample/001-new-project.png differ diff --git a/Projects/AddonExample/Addon.cs b/Projects/AddonExample/Addon.cs new file mode 100644 index 000000000..78718cac2 --- /dev/null +++ b/Projects/AddonExample/Addon.cs @@ -0,0 +1,32 @@ +using Server.Accounting; + +namespace Server.Addon; + +public class Addon +{ + public static void Configure() + { + // ModernUO lifecycle hook before world loads + EventSink.AccountLogin += AccountLogin; + } + + public static void Initialize() + { + // ModernUO lifecycle hook after world loads + } + + public static void AccountLogin(AccountLoginEventArgs e) + { + if (Accounts.GetAccount(e.Username) is not Account account) + { + return; + } + + if (!account.Young) + { + return; + } + + account.RemoveYoungStatus(0); + } +} diff --git a/Projects/AddonExample/AddonExample.csproj b/Projects/AddonExample/AddonExample.csproj new file mode 100644 index 000000000..e38ce6f21 --- /dev/null +++ b/Projects/AddonExample/AddonExample.csproj @@ -0,0 +1,18 @@ + + + Server.Addon + AddonExample + ModernUO Example Addon + ..\..\Distribution\Assemblies + ..\..\Distribution\Assemblies + Debug;Release;Analyze + + + + false + + + false + + + diff --git a/Projects/AddonExample/README.md b/Projects/AddonExample/README.md new file mode 100644 index 000000000..e16c05beb --- /dev/null +++ b/Projects/AddonExample/README.md @@ -0,0 +1,173 @@ +# Addon Assembly Tutorial + +## What We'll Do + +We're going to make an addon that is a separate `dll` file from the `UOContent.dll`, but has access to code from `Server` and `UOContent`. + +Specifically we're going to make a small addon that removes young player status from a player when they login. This isn't a perfect example because unfortunately when an account is created it's not young yet. Just... imagine it works the first time they log in. + +(note: can probably fix the shortcoming above, but this is a really basic example) + +OKAY let's go... + +### Create a Project + +In your IDE, create a new project in the ModernUO solution. For this example we'll call it `AddonExample`. It's important that the project is created as a subdirectory of the `Projects` directory within the repository. + +![001-new-project.png](001-new-project.png) + +### Configure that Project + +The process differs between IDEs, but we want to navigate to and open the `AddonExample.csproj` file. Replace the contents of the file with the following contents, and we will walk through it step by step. + +```xml + + + Server.Addon + AddonExample + ModernUO Example Addon + ..\..\Distribution\Assemblies + ..\..\Distribution\Assemblies + Debug;Release;Analyze + + + + false + + + false + + + +``` +#### `PropertyGroup` + +* `RootNamespace` is the namespace of our addon and we will use this when we write the code. +* `AssemblyName` will determine the name of the DLL file that gets built. This is important for configuration later. +* `Product` is an arbitrary name to identify your addon. +* `OutDir` and `PublishDir` shouldn't be changed, the `UOContent.dll` file, for example, is output here, and this is where ModernUO will look for our addon. +* `Configurations` are build configurations, and probably should not change. + +#### `ItemGroup` (1) + +* `ProjectReference` indicates that we are referencing another project, so that we can reference code in that project in our addon. We do this twice, once for `Server` and once for `UOContent`. + +#### `ItemGroup` (2) + +* `AdditionalFiles` indicates we want to copy some files into the build environment. If we were to add custom items which are saved and loaded from the game we would need to provide migrations for changes in what those items save. + +### Write Some Code + +The project should have a directory structure something like this at this point. + +``` +ModernUO + Projects + AddonExample + Class1.cs +``` + +Remove `Class1.cs` and create a new class called `Addon`. Replace the code in that generated class with the following contents, and we'll walk through it step by step. + +```csharp +using Server.Accounting; + +namespace Server.Addon; + +public class Addon +{ + public static void Configure() + { + EventSink.AccountLogin += AccountLogin; + } + + public static void Initialize() + { + } + + public static void AccountLogin(AccountLoginEventArgs e) + { + if (Accounts.GetAccount(e.Username) is not Account account) + { + return; + } + + if (!account.Young) + { + return; + } + + account.RemoveYoungStatus(0); + } +} +``` + +#### The Correct Namespace +```csharp +using Server.Accounting; // ignore this for now, we will use it below + +// this is the RootNamespace from AddonExample.csproj +namespace Server.Addon; +``` + +#### The Configure and Initialize Functions + +```csharp + public static void Configure() + { + /* + The Configure function is called once before the World is loaded. + Do setup steps here. + */ + + // In our case, we're going to add a callback function that will get called + // whenever the AccountLogin event is raised (magic). + EventSink.AccountLogin += AccountLogin; + } + + public static void Initialize() + { + /* + We don't actually use this here, but it's important to know about the Initialize function. + After the world loads, but before the game starts, this function is called once. + Do setup steps here. + */ + } +``` + +#### Do Something +```csharp + // This is the function we added to the AccountLogin event above + public static void AccountLogin(AccountLoginEventArgs e) + { + // check if the account exists, if not, return + if (Accounts.GetAccount(e.Username) is not Account account) + { + return; + } + + // check if account is young, if not we don't care + if (!account.Young) + { + return; + } + + // remove the young status from the account + account.RemoveYoungStatus(0); + } +``` + +### Load Your Assembly + +The file `Distribution/Data/assemblies.json` determines what files ModernUO will load as addons. Replace it with the following to include our file `AddonExample.dll`. + +```json +[ + "UOContent.dll", + "AddonExample.dll" +] +``` + +### Run It? I think + +I haven't fixed my ModernUO to build on dotnet 9 yet. diff --git a/Projects/Application/Application.csproj b/Projects/Application/Application.csproj index 1a162645c..130bcc78f 100644 --- a/Projects/Application/Application.csproj +++ b/Projects/Application/Application.csproj @@ -28,7 +28,4 @@ false - - - diff --git a/Projects/Application/MUO.ico b/Projects/Application/MUO.ico index c1c644d1a..a69475ab7 100644 Binary files a/Projects/Application/MUO.ico and b/Projects/Application/MUO.ico differ diff --git a/Projects/BuildTool/Branding.cs b/Projects/BuildTool/Branding.cs deleted file mode 100644 index 8764b20bf..000000000 --- a/Projects/BuildTool/Branding.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Spectre.Console; - -namespace BuildTool; - -/// -/// Static branding assets for the BuildTool CLI. -/// Colors sourced from ModernUO brand palette (docs/packets/template.html, docs/branding/logo.svg). -/// -public static class Branding -{ - // ModernUO brand gold palette (from the "U" in the logo) - public static readonly Color Gold = new(213, 191, 116); // #d5bf74 - primary brand gold - public static readonly Color GoldLight = new(223, 198, 136); // #dfc688 - interactive elements - public static readonly Color GoldMuted = new(232, 206, 161); // #e8cea1 - body text - public static readonly Color GoldDim = new(162, 145, 71); // #a29147 - dimmed gold - - // Silver palette (from the "O" in the logo) - public static readonly Color Silver = new(223, 223, 221); // #dfdfdd - primary silver - public static readonly Color SilverDim = new(150, 150, 148); // #969694 - dimmed silver - - // Functional colors - public static readonly Color Success = new(34, 197, 94); // #22c55e - green - public static readonly Color Info = new(59, 130, 246); // #3b82f6 - blue - - // Styles - public static readonly Style GoldStyle = new(Gold); - public static readonly Style SilverStyle = new(Silver); - public static readonly Style HighlightStyle = new(GoldLight); - public static readonly Style DimGoldStyle = new(GoldDim); - - /// - /// The iconic ModernUO circular UO monogram with true-color ANSI escape codes. - /// Converted from the brand PNG (docs/branding/android-chrome-512x512.png). - /// Gold U on the left, silver O on the right, rendered with per-character RGB colors. - /// Output with Console.Write() to bypass Spectre markup parsing. - /// - public const string UOLogoAnsi = - " \e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;2;2;1m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;19;17;10m \e[0m\e[38;2;56;49;31m.\e[0m\e[38;2;46;41;24m.\e[0m\e[38;2;86;86;87m-\e[0m\e[38;2;134;133;131m+\e[0m\e[38;2;136;135;134m+\e[0m\e[38;2;133;132;131m+\e[0m\e[38;2;119;118;117m=\e[0m\e[38;2;87;86;85m-\e[0m\e[38;2;52;52;51m:\e[0m\e[38;2;20;20;20m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;2;2;2m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\n" + - " \e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;19;17;11m \e[0m\e[38;2;97;87;52m-\e[0m\e[38;2;163;145;87m+\e[0m\e[38;2;184;164;101m*\e[0m\e[38;2;202;179;111m*\e[0m\e[38;2;135;121;74m=\e[0m\e[38;2;96;97;101m-\e[0m\e[38;2;134;134;131m+\e[0m\e[38;2;124;123;123m=\e[0m\e[38;2;131;131;130m+\e[0m\e[38;2;144;143;143m+\e[0m\e[38;2;197;196;195m#\e[0m\e[38;2;211;210;209m%\e[0m\e[38;2;195;194;193m#\e[0m\e[38;2;169;168;167m*\e[0m\e[38;2;99;99;98m-\e[0m\e[38;2;19;18;18m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\n" + - " \e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;6;6;4m \e[0m\e[38;2;120;109;64m=\e[0m\e[38;2;228;204;120m#\e[0m\e[38;2;218;196;118m#\e[0m\e[38;2;180;163;101m*\e[0m\e[38;2;229;206;122m%\e[0m\e[38;2;231;207;122m%\e[0m\e[38;2;145;130;80m+\e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;139;140;139m+\e[0m\e[38;2;236;237;236m@\e[0m\e[38;2;234;234;234m@\e[0m\e[38;2;183;183;182m#\e[0m\e[38;2;222;222;222m%\e[0m\e[38;2;231;231;231m@\e[0m\e[38;2;120;120;119m=\e[0m\e[38;2;6;6;6m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\n" + - " \e[38;2;0;0;0m \e[0m\e[38;2;18;17;11m \e[0m\e[38;2;180;162;93m*\e[0m\e[38;2;241;216;123m%\e[0m\e[38;2;172;156;94m*\e[0m\e[38;2;22;21;15m \e[0m\e[38;2;68;63;41m:\e[0m\e[38;2;240;216;123m%\e[0m\e[38;2;244;219;125m%\e[0m\e[38;2;150;135;80m+\e[0m\e[38;2;2;2;1m \e[0m\e[38;2;3;3;3m \e[0m\e[38;2;3;3;3m \e[0m\e[38;2;3;3;3m \e[0m\e[38;2;2;2;2m \e[0m\e[38;2;148;148;148m+\e[0m\e[38;2;244;245;245m@\e[0m\e[38;2;240;241;241m@\e[0m\e[38;2;66;66;66m:\e[0m\e[38;2;21;21;21m \e[0m\e[38;2;171;172;171m*\e[0m\e[38;2;241;242;242m@\e[0m\e[38;2;179;179;179m#\e[0m\e[38;2;17;17;17m \e[0m\e[38;2;0;0;0m \e[0m\n" + - " \e[38;2;1;1;2m \e[0m\e[38;2;136;120;70m=\e[0m\e[38;2;207;180;98m#\e[0m\e[38;2;146;130;76m+\e[0m\e[38;2;0;1;4m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;76;68;42m:\e[0m\e[38;2;204;178;98m*\e[0m\e[38;2;204;177;97m*\e[0m\e[38;2;135;119;70m=\e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;134;135;134m+\e[0m\e[38;2;208;209;209m%\e[0m\e[38;2;207;208;207m%\e[0m\e[38;2;75;75;75m:\e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;146;147;146m+\e[0m\e[38;2;211;212;212m%\e[0m\e[38;2;135;135;135m+\e[0m\e[38;2;0;0;0m \e[0m\n" + - " \e[38;2;33;30;21m.\e[0m\e[38;2;161;134;70m+\e[0m\e[38;2;161;133;69m+\e[0m\e[38;2;51;46;30m.\e[0m\e[38;2;0;0;0m \e[0m\e[38;2;1;1;0m \e[0m\e[38;2;67;59;37m:\e[0m\e[38;2;162;134;69m+\e[0m\e[38;2;158;129;65m+\e[0m\e[38;2;118;102;58m=\e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;118;118;118m=\e[0m\e[38;2;165;166;166m*\e[0m\e[38;2;167;168;168m*\e[0m\e[38;2;66;66;66m:\e[0m\e[38;2;1;1;1m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;50;50;50m.\e[0m\e[38;2;166;167;167m*\e[0m\e[38;2;165;166;165m*\e[0m\e[38;2;32;32;31m.\e[0m\n" + - " \e[38;2;35;32;23m.\e[0m\e[38;2;154;128;62m+\e[0m\e[38;2;154;127;61m+\e[0m\e[38;2;49;44;29m.\e[0m\e[38;2;0;0;0m \e[0m\e[38;2;1;1;0m \e[0m\e[38;2;66;59;36m:\e[0m\e[38;2;154;126;61m+\e[0m\e[38;2;150;121;56m=\e[0m\e[38;2;112;96;54m-\e[0m\e[38;2;0;0;0m \e[0m\e[38;2;3;2;0m \e[0m\e[38;2;3;3;1m \e[0m\e[38;2;2;2;1m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;118;119;118m=\e[0m\e[38;2;165;165;165m*\e[0m\e[38;2;167;168;168m*\e[0m\e[38;2;67;67;66m:\e[0m\e[38;2;1;1;1m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;48;48;47m.\e[0m\e[38;2;167;168;168m*\e[0m\e[38;2;166;167;166m*\e[0m\e[38;2;33;33;33m.\e[0m\n" + - " \e[38;2;3;3;5m \e[0m\e[38;2;130;114;62m=\e[0m\e[38;2;181;154;68m+\e[0m\e[38;2;132;115;59m=\e[0m\e[38;2;0;0;2m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;46;42;28m.\e[0m\e[38;2;184;159;76m*\e[0m\e[38;2;178;152;66m+\e[0m\e[38;2;151;132;66m+\e[0m\e[38;2;13;12;9m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;132;132;132m+\e[0m\e[38;2;212;213;213m%\e[0m\e[38;2;208;208;208m%\e[0m\e[38;2;50;50;49m.\e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;146;146;146m+\e[0m\e[38;2;213;213;213m%\e[0m\e[38;2;139;139;139m+\e[0m\e[38;2;1;1;1m \e[0m\n" + - " \e[38;2;0;0;0m \e[0m\e[38;2;22;21;16m \e[0m\e[38;2;161;142;72m+\e[0m\e[38;2;197;172;77m*\e[0m\e[38;2;138;122;60m=\e[0m\e[38;2;24;22;13m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;89;80;46m-\e[0m\e[38;2;185;162;76m*\e[0m\e[38;2;195;171;77m*\e[0m\e[38;2;192;168;78m*\e[0m\e[38;2;151;132;62m+\e[0m\e[38;2;136;119;56m=\e[0m\e[38;2;160;140;67m+\e[0m\e[38;2;115;100;48m-\e[0m\e[38;2;143;145;150m+\e[0m\e[38;2;220;219;218m%\e[0m\e[38;2;92;92;92m-\e[0m\e[38;2;0;0;0m \e[0m\e[38;2;24;24;24m \e[0m\e[38;2;155;155;155m*\e[0m\e[38;2;232;232;232m@\e[0m\e[38;2;180;180;180m#\e[0m\e[38;2;21;21;21m \e[0m\e[38;2;0;0;0m \e[0m\n" + - " \e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;11;11;11m \e[0m\e[38;2;109;97;54m-\e[0m\e[38;2;183;159;79m*\e[0m\e[38;2;181;157;78m*\e[0m\e[38;2;109;94;48m-\e[0m\e[38;2;36;32;19m.\e[0m\e[38;2;33;31;21m.\e[0m\e[38;2;68;62;36m:\e[0m\e[38;2;102;91;48m-\e[0m\e[38;2;126;111;57m=\e[0m\e[38;2;131;116;59m=\e[0m\e[38;2;127;113;58m=\e[0m\e[38;2;71;64;36m:\e[0m\e[38;2;37;37;40m.\e[0m\e[38;2;39;39;38m.\e[0m\e[38;2;38;38;38m.\e[0m\e[38;2;122;122;121m=\e[0m\e[38;2;204;204;204m%\e[0m\e[38;2;208;208;208m%\e[0m\e[38;2;116;116;115m=\e[0m\e[38;2;9;9;9m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\n" + - " \e[38;2;0;0;0m \e[0m\e[38;2;1;1;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;24;23;17m \e[0m\e[38;2;88;77;46m-\e[0m\e[38;2;142;123;67m=\e[0m\e[38;2;161;138;75m+\e[0m\e[38;2;143;121;65m=\e[0m\e[38;2;115;97;54m-\e[0m\e[38;2;95;80;46m-\e[0m\e[38;2;83;71;42m:\e[0m\e[38;2;79;67;40m:\e[0m\e[38;2;86;74;44m:\e[0m\e[38;2;62;54;30m:\e[0m\e[38;2;87;88;89m-\e[0m\e[38;2;160;160;159m*\e[0m\e[38;2;178;178;178m#\e[0m\e[38;2;156;156;156m*\e[0m\e[38;2;94;94;94m-\e[0m\e[38;2;22;22;22m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;1;1;1m \e[0m\e[38;2;0;0;0m \e[0m\n" + - " \e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;2;1;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;1m \e[0m\e[38;2;25;22;17m \e[0m\e[38;2;58;51;34m:\e[0m\e[38;2;87;75;47m:\e[0m\e[38;2;105;90;56m-\e[0m\e[38;2;115;98;60m-\e[0m\e[38;2;117;99;60m-\e[0m\e[38;2;115;99;60m-\e[0m\e[38;2;76;67;41m:\e[0m\e[38;2;57;58;60m:\e[0m\e[38;2;66;65;65m:\e[0m\e[38;2;23;23;23m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;2;2;2m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\e[38;2;0;0;0m \e[0m\n"; - - /// - /// Subtitle shown below the banner. - /// - public const string Subtitle = "Ultima Online Server Emulator"; -} diff --git a/Projects/BuildTool/BuildOptions.cs b/Projects/BuildTool/BuildOptions.cs deleted file mode 100644 index 9e1d56388..000000000 --- a/Projects/BuildTool/BuildOptions.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace BuildTool; - -public enum BuildAction -{ - Publish, - Migrate -} - -public sealed class BuildOptions -{ - public BuildAction Action { get; set; } = BuildAction.Publish; - public string Config { get; set; } = "Release"; - public string? Os { get; set; } - public string? Arch { get; set; } - public bool SkipPrereqs { get; set; } - public bool Interactive { get; set; } - - /// - /// Report the native library prerequisites and exit. The interactive flow is the only other - /// path that runs these checks, so without this there is no way to verify a deployment target - /// from a script or a container. - /// - public bool CheckPrereqsOnly { get; set; } -} diff --git a/Projects/BuildTool/BuildTool.csproj b/Projects/BuildTool/BuildTool.csproj deleted file mode 100644 index 983000240..000000000 --- a/Projects/BuildTool/BuildTool.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - Exe - build-tool.ico - net10.0 - Preview - false - BuildTool - build-tool - enable - true - true - link - true - win-x64;win-arm64;osx-x64;osx-arm64;linux-x64;linux-arm64 - true - - - - - diff --git a/Projects/BuildTool/Interactive/CancellationTracker.cs b/Projects/BuildTool/Interactive/CancellationTracker.cs deleted file mode 100644 index 21bb2a1c0..000000000 --- a/Projects/BuildTool/Interactive/CancellationTracker.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace BuildTool.Interactive; - -/// -/// Tracks Ctrl+C press timing to detect double-press for exit. -/// -public sealed class CancellationTracker -{ - private DateTime _lastCancelTime = DateTime.MinValue; - private const int DoublePressMs = 500; - - /// - /// Checks if this is a double-press (within 500ms of last press). - /// Also updates the last press time. - /// - public bool IsDoublePress() - { - var now = DateTime.UtcNow; - var isDouble = (now - _lastCancelTime).TotalMilliseconds < DoublePressMs; - _lastCancelTime = now; - return isDouble; - } - - /// Global instance for app-wide tracking. - public static CancellationTracker Instance { get; } = new(); -} diff --git a/Projects/BuildTool/Interactive/GuidedMode.cs b/Projects/BuildTool/Interactive/GuidedMode.cs deleted file mode 100644 index e86561d30..000000000 --- a/Projects/BuildTool/Interactive/GuidedMode.cs +++ /dev/null @@ -1,365 +0,0 @@ -using BuildTool.Platform; -using BuildTool.Prerequisites; -using BuildTool.Publishing; -using Spectre.Console; - -namespace BuildTool.Interactive; - -/// -/// Handles interactive mode when the BuildTool is run without arguments. -/// Provides a polished menu-driven experience with proper Ctrl+C navigation. -/// Uses ModernUO brand gold color palette. -/// -public static class GuidedMode -{ - // Brand gold as markup string for inline use - private const string GoldMarkup = "rgb(213,191,116)"; - private const string GoldLightMarkup = "rgb(223,198,136)"; - - /// - /// Tracks menu depth. 0 = root menu, >0 = submenu. - /// Used by ShowPrompt to automatically add Back option in submenus. - /// - private static int _menuDepth; - - public static int Run(PlatformInfo platform, string repoRoot) - { - return ShowMainMenu(platform, repoRoot); - } - - /// - /// Shows a selection prompt with automatic Back support. - /// When _menuDepth > 0, appends a ":left_arrow: Back" choice. - /// Returns null if the user chose Back or pressed Ctrl+C in a submenu. - /// At the root menu, Ctrl+C requires double-press to exit (returns null on double-press). - /// - private static string? ShowPrompt(string title, params string[] choices) - { - var allChoices = choices.Where(c => !string.IsNullOrEmpty(c)).ToList(); - - if (_menuDepth > 0) - { - allChoices.Add(":left_arrow: Back"); - } - - var prompt = new SelectionPrompt() - .Title($"[{GoldMarkup}]{title}[/]") - .PageSize(10) - .HighlightStyle(Branding.HighlightStyle) - .AddChoices(allChoices); - - string selection; - try - { - selection = prompt.Show(AnsiConsole.Console); - } - catch (OperationCanceledException) - { - InteractiveCancellation.Instance.Reset(); - - if (_menuDepth > 0) - { - return null; // Back - } - - // Root menu: require double-press to exit - if (CancellationTracker.Instance.IsDoublePress()) - { - AnsiConsole.MarkupLine("\n[grey]Exiting...[/]"); - return ":cross_mark: Exit"; - } - - AnsiConsole.Write(new Text("\nPress Ctrl+C again to exit\n", Branding.DimGoldStyle)); - Thread.Sleep(100); - return ""; // Signal to redraw menu - } - - if (selection.Contains("Back")) - { - return null; - } - - return selection; - } - - private static int ShowMainMenu(PlatformInfo platform, string repoRoot) - { - while (true) - { - AnsiConsole.Clear(); - - // Write the true-color ANSI logo directly (bypasses Spectre markup parsing) - Console.Write(Branding.UOLogoAnsi); - AnsiConsole.Write(new Text($" {Branding.Subtitle}\n", Branding.DimGoldStyle)); - AnsiConsole.WriteLine(); - - // Show environment info - var sdkVersion = GetDotNetSdkVersion(); - var table = new Table() - .Border(TableBorder.Rounded) - .BorderColor(Branding.GoldDim) - .HideHeaders() - .AddColumn("Key") - .AddColumn("Value"); - - table.AddRow( - new Text("Platform", Branding.DimGoldStyle), - new Markup($"[white]{Markup.Escape(platform.OsName)} {platform.ArchRid}[/]") - ); - table.AddRow( - new Text(".NET SDK", Branding.DimGoldStyle), - sdkVersion is not null - ? new Markup($"[white]{Markup.Escape(sdkVersion)}[/]") - : new Markup("[red]Not found[/]") - ); - - if (platform.DistroName is not null) - { - table.AddRow( - new Text("Distribution", Branding.DimGoldStyle), - new Markup($"[white]{Markup.Escape(platform.DistroName)}[/]") - ); - } - - if (platform.KernelVersion is not null) - { - table.AddRow( - new Text("Kernel", Branding.DimGoldStyle), - new Markup($"[white]{Markup.Escape(platform.KernelVersion)}[/]") - ); - } - - AnsiConsole.Write(table); - AnsiConsole.WriteLine(); - - var selection = ShowPrompt( - "What would you like to do?", - ":hammer: Publish Server", - ":stethoscope: Check Prerequisites", - "", - ":cross_mark: Exit" - ); - - if (string.IsNullOrEmpty(selection)) - { - continue; // Redraw (single Ctrl+C) - } - - if (selection.Contains("Publish")) - { - _menuDepth++; - AnsiConsole.Clear(); - AnsiConsole.MarkupLine($"[bold {GoldMarkup}]:hammer: PUBLISH SERVER[/]\n"); - var result = RunPublish(platform, repoRoot); - _menuDepth--; - - if (result is not null) // null = Back, skip WaitForKey - { - WaitForKey(); - } - } - else if (selection.Contains("Prerequisites")) - { - _menuDepth++; - AnsiConsole.Clear(); - AnsiConsole.MarkupLine($"[bold {GoldMarkup}]:stethoscope: PREREQUISITE CHECK[/]\n"); - PrerequisiteChecker.CheckAll(platform, repoRoot, interactive: true); - _menuDepth--; - WaitForKey(); - } - else if (selection.Contains("Exit")) - { - return 0; - } - } - } - - /// - /// Returns null if user navigated Back to main menu, 0 on success, non-zero on error. - /// Uses a step loop so Back goes to the previous step, not the main menu. - /// - /// - /// Returns null if user navigated Back to main menu, 0 on success, non-zero on error. - /// Uses a step loop so Back goes to the previous step, not the main menu. - /// - private static int? RunPublish(PlatformInfo platform, string repoRoot) - { - // Step 0: SDK check (always needed for building) - if (!PrerequisiteChecker.CheckSdk(platform, repoRoot, interactive: true)) - { - return 1; - } - - var config = "Release"; - var rid = platform.Rid; - var os = platform.OsRid; - var arch = platform.ArchRid; - - // Steps 1+: interactive prompts with back navigation - var step = 1; - while (step <= 4) - { - switch (step) - { - case 1: // Configuration - { - var choice = ShowPrompt("Configuration:", "Release (Recommended)", "Debug"); - if (choice is null) - { - return null; // Back from first prompt = back to main menu - } - - config = choice.Contains("Release") ? "Release" : "Debug"; - step++; - break; - } - case 2: // Platform confirmation - { - var choice = ShowPrompt( - $"Target platform: [{GoldLightMarkup}]{platform.Rid}[/] [grey](auto-detected)[/]", - $":check_mark: Use {platform.Rid}", - ":wrench: Choose different platform" - ); - - if (choice is null) - { - step--; - break; - } - - if (choice.Contains("Choose")) - { - step++; - } - else - { - rid = platform.Rid; - os = platform.OsRid; - step = 5; // Skip OS/arch selection, go to publish - } - break; - } - case 3: // OS selection (only if custom platform) - { - var choice = ShowPrompt( - "Operating System:", - "win (Windows)", - "osx (macOS)", - "linux (Linux)" - ); - - if (choice is null) - { - step--; - break; - } - - os = choice.Split(' ')[0].Trim(); - step++; - break; - } - case 4: // Architecture selection (only if custom platform) - { - var choice = ShowPrompt( - "Architecture:", - "x64 (Intel/AMD 64-bit)", - "arm64 (ARM 64-bit)" - ); - - if (choice is null) - { - step--; - break; - } - - arch = choice.Split(' ')[0].Trim(); - rid = $"{os}-{arch}"; - step++; - break; - } - } - } - - var isCrossCompile = os != platform.OsRid; - - // Native library checks — only relevant when targeting the current OS - if (!isCrossCompile) - { - if (!PrerequisiteChecker.CheckNativeLibraries(platform, interactive: true)) - { - return 1; - } - } - - // Run publish - var exitCode = PublishOrchestrator.Run(config, rid, interactive: true, isCrossCompile); - - if (exitCode == 0) - { - CheckFirstTimeSetup(repoRoot); - - if (isCrossCompile) - { - DisplayTargetRequirements(os, rid); - } - } - - return exitCode; - } - - private static void DisplayTargetRequirements(string targetOs, string rid) - { - var (osName, commands) = NativeLibraryChecker.GetRequirementsForTarget(targetOs); - - var rows = new List - { - new Markup($"[bold {GoldMarkup}]:clipboard: Prerequisites for {Markup.Escape(osName)} target ({rid})[/]"), - new Markup("") - }; - - for (var i = 0; i < commands.Length; i++) - { - var cmd = commands[i]; - if (i > 0) - { - rows.Add(new Markup("")); - } - rows.Add(new Markup($" [grey]\u2022[/] {Markup.Escape(cmd)}")); - } - - rows.Add(new Markup("")); - rows.Add(new Markup("")); - rows.Add(new Markup("[grey]Install these on the target machine, then copy the [/][rgb(213,191,116)]Distribution[/] [grey]folder to the server and run it.[/]")); - - AnsiConsole.Write(new Panel(new Rows(rows)) - .Border(BoxBorder.Rounded) - .BorderColor(Branding.GoldDim) - .Padding(1, 0)); - AnsiConsole.WriteLine(); - } - - private static void CheckFirstTimeSetup(string repoRoot) - { - var configPath = Path.Combine(repoRoot, "Distribution", "Configuration", "modernuo.json"); - if (!File.Exists(configPath)) - { - AnsiConsole.WriteLine(); - AnsiConsole.Write(new Text( - ":light_bulb: Tip: The server will prompt you for game data file locations on first run.\n", - Branding.DimGoldStyle)); - } - } - - private static string? GetDotNetSdkVersion() - { - var result = ProcessRunner.RunCaptured("dotnet", "--version"); - return result.Success ? result.StandardOutput.Trim() : null; - } - - private static void WaitForKey() - { - AnsiConsole.WriteLine(); - AnsiConsole.MarkupLine("[grey]Press any key to continue...[/]"); - Console.ReadKey(true); - } -} diff --git a/Projects/BuildTool/Interactive/InteractiveCancellation.cs b/Projects/BuildTool/Interactive/InteractiveCancellation.cs deleted file mode 100644 index d8d052f7d..000000000 --- a/Projects/BuildTool/Interactive/InteractiveCancellation.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace BuildTool.Interactive; - -/// -/// Manages cancellation for interactive mode. -/// When Ctrl+C is pressed, the current token is cancelled, causing prompts to throw OperationCanceledException. -/// The token source is then reset so the next prompt can work. -/// -public sealed class InteractiveCancellation : IDisposable -{ - private CancellationTokenSource _cts = new(); - private readonly object _lock = new(); - - /// - /// Gets the current cancellation token to pass to prompts. - /// - public CancellationToken Token - { - get - { - lock (_lock) - { - return _cts.Token; - } - } - } - - /// - /// Signals cancellation (called from Ctrl+C handler). - /// - public void Cancel() - { - lock (_lock) - { - _cts.Cancel(); - } - } - - /// - /// Resets the cancellation token source so new prompts can work. - /// Call this after catching OperationCanceledException. - /// - public void Reset() - { - lock (_lock) - { - _cts.Dispose(); - _cts = new CancellationTokenSource(); - } - } - - /// Global instance for app-wide interactive cancellation. - public static InteractiveCancellation Instance { get; } = new(); - - public void Dispose() - { - _cts.Dispose(); - } -} diff --git a/Projects/BuildTool/Json/GlobalJsonContext.cs b/Projects/BuildTool/Json/GlobalJsonContext.cs deleted file mode 100644 index d64fa29b1..000000000 --- a/Projects/BuildTool/Json/GlobalJsonContext.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Text.Json.Serialization; - -namespace BuildTool.Json; - -public sealed class GlobalJson -{ - [JsonPropertyName("sdk")] - public SdkConfig? Sdk { get; set; } -} - -public sealed class SdkConfig -{ - [JsonPropertyName("version")] - public string? Version { get; set; } - - [JsonPropertyName("rollForward")] - public string? RollForward { get; set; } - - [JsonPropertyName("allowPrerelease")] - public bool AllowPrerelease { get; set; } -} - -[JsonSerializable(typeof(GlobalJson))] -[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] -internal partial class GlobalJsonContext : JsonSerializerContext; diff --git a/Projects/BuildTool/Platform/PlatformDetector.cs b/Projects/BuildTool/Platform/PlatformDetector.cs deleted file mode 100644 index ac7e74dfd..000000000 --- a/Projects/BuildTool/Platform/PlatformDetector.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System.Runtime.InteropServices; -using BuildTool.Publishing; - -namespace BuildTool.Platform; - -public static class PlatformDetector -{ - // Windows 11 starts at build 22000 - private const int Windows11MinBuild = 22000; - - public static PlatformInfo Detect() - { - var osRid = GetOsRid(); - var archRid = GetArchRid(); - var (distroId, distroName) = GetLinuxDistroInfo(); - var kernelVersion = GetKernelVersion(osRid); - var osName = GetOsDisplayName(osRid, distroName, kernelVersion); - var packageManager = DetectPackageManager(osRid, distroId); - - return new PlatformInfo - { - OsName = osName, - OsRid = osRid, - ArchRid = archRid, - DistroId = distroId, - DistroName = distroName, - KernelVersion = kernelVersion, - PackageManager = packageManager - }; - } - - private static string GetOsDisplayName(string osRid, string? distroName, string? kernelVersion) - { - if (OperatingSystem.IsWindows()) - { - var version = Environment.OSVersion.Version; - var windowsVersion = version.Build >= Windows11MinBuild ? "11" : "10"; - return $"Windows {windowsVersion} (Build {version.Build})"; - } - - if (OperatingSystem.IsMacOS()) - { - var version = Environment.OSVersion.Version; - var macosName = GetMacOSCodename(version.Major); - return macosName is not null - ? $"macOS {version.Major}.{version.Minor} {macosName}" - : $"macOS {version.Major}.{version.Minor}"; - } - - // Linux: show distro name with kernel version - if (distroName is not null && kernelVersion is not null) - { - return $"{distroName} (kernel {kernelVersion})"; - } - - return distroName ?? (kernelVersion is not null ? $"Linux (kernel {kernelVersion})" : "Linux"); - } - - private static string? GetMacOSCodename(int majorVersion) => - majorVersion switch - { - 15 => "Sequoia", - 14 => "Sonoma", - 13 => "Ventura", - 12 => "Monterey", - _ => null - }; - - private static string GetOsRid() - { - if (OperatingSystem.IsWindows()) - { - return "win"; - } - - if (OperatingSystem.IsMacOS()) - { - return "osx"; - } - - return "linux"; - } - - private static string GetArchRid() => - RuntimeInformation.ProcessArchitecture switch - { - Architecture.X64 => "x64", - Architecture.Arm64 => "arm64", - _ => "x64" - }; - - private static string? GetKernelVersion(string osRid) - { - if (osRid != "linux") - { - return null; - } - - // Try /proc/version first (most reliable) - if (File.Exists("/proc/version")) - { - try - { - var procVersion = File.ReadAllText("/proc/version"); - // Format: "Linux version 6.5.0-44-generic ..." - var parts = procVersion.Split(' ', StringSplitOptions.RemoveEmptyEntries); - if (parts.Length >= 3 && parts[0] == "Linux" && parts[1] == "version") - { - return parts[2]; - } - } - catch - { - // Fall through to uname - } - } - - // Fall back to uname -r - var result = ProcessRunner.RunCaptured("uname", "-r"); - return result.Success ? result.StandardOutput.Trim() : null; - } - - private static (string? Id, string? Name) GetLinuxDistroInfo() - { - if (!OperatingSystem.IsLinux()) - { - return (null, null); - } - - const string osReleasePath = "/etc/os-release"; - if (!File.Exists(osReleasePath)) - { - return (null, null); - } - - string? id = null; - string? name = null; - - foreach (var line in File.ReadLines(osReleasePath)) - { - if (line.StartsWith("ID=", StringComparison.Ordinal)) - { - id = line[3..].Trim('"'); - } - else if (line.StartsWith("PRETTY_NAME=", StringComparison.Ordinal)) - { - name = line[12..].Trim('"'); - } - - if (id is not null && name is not null) - { - break; - } - } - - return (id, name); - } - - private static PackageManager DetectPackageManager(string osRid, string? distroId) - { - if (osRid == "osx") - { - return PackageManager.Brew; - } - - if (osRid != "linux") - { - return PackageManager.Unknown; - } - - // Check distro ID first for accuracy - return distroId?.ToLowerInvariant() switch - { - "ubuntu" or "debian" or "linuxmint" or "pop" or "elementary" or "zorin" => PackageManager.Apt, - "fedora" or "centos" or "rhel" or "rocky" or "alma" or "ol" => PackageManager.Dnf, - "opensuse" or "opensuse-leap" or "opensuse-tumbleweed" or "sles" => PackageManager.Zypper, - "arch" or "manjaro" or "endeavouros" => PackageManager.Pacman, - "alpine" => PackageManager.Apk, - _ => DetectPackageManagerFromBinaries() - }; - } - - private static PackageManager DetectPackageManagerFromBinaries() - { - if (File.Exists("/usr/bin/apt-get") || File.Exists("/usr/bin/apt")) - { - return PackageManager.Apt; - } - - if (File.Exists("/usr/bin/dnf")) - { - return PackageManager.Dnf; - } - - if (File.Exists("/usr/bin/zypper")) - { - return PackageManager.Zypper; - } - - if (File.Exists("/usr/bin/pacman")) - { - return PackageManager.Pacman; - } - - if (File.Exists("/sbin/apk")) - { - return PackageManager.Apk; - } - - return PackageManager.Unknown; - } -} diff --git a/Projects/BuildTool/Platform/PlatformInfo.cs b/Projects/BuildTool/Platform/PlatformInfo.cs deleted file mode 100644 index 5d40827d1..000000000 --- a/Projects/BuildTool/Platform/PlatformInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace BuildTool.Platform; - -public enum PackageManager -{ - Unknown, - Apt, - Dnf, - Zypper, - Pacman, - Apk, - Brew -} - -public sealed class PlatformInfo -{ - public required string OsName { get; init; } - public required string OsRid { get; init; } - public required string ArchRid { get; init; } - public string Rid => $"{OsRid}-{ArchRid}"; - public string? DistroId { get; init; } - public string? DistroName { get; init; } - public string? KernelVersion { get; init; } - public PackageManager PackageManager { get; init; } = PackageManager.Unknown; - - public bool IsWindows => OsRid == "win"; - public bool IsMacOS => OsRid == "osx"; - public bool IsLinux => OsRid == "linux"; -} diff --git a/Projects/BuildTool/Prerequisites/DotNetSdkManager.cs b/Projects/BuildTool/Prerequisites/DotNetSdkManager.cs deleted file mode 100644 index 5ab2ea295..000000000 --- a/Projects/BuildTool/Prerequisites/DotNetSdkManager.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System.Text.Json; -using System.Text.RegularExpressions; -using BuildTool.Json; -using BuildTool.Platform; -using BuildTool.Publishing; -using Spectre.Console; - -namespace BuildTool.Prerequisites; - -public static partial class DotNetSdkManager -{ - private const string DotNetLinuxInstallUrl = "https://learn.microsoft.com/en-us/dotnet/core/install/linux"; - - /// - /// Returns the .NET download page URL for the required SDK's major.minor channel. - /// - private static string GetDownloadUrl(Version version) => - $"https://dotnet.microsoft.com/download/dotnet/{version.Major}.{version.Minor}"; - - /// - /// Returns the direct installer download URL for a specific SDK version + platform. - /// Windows uses .exe, macOS uses .pkg. - /// - private static string GetInstallerUrl(Version version, string rid, string extension) => - $"https://builds.dotnet.microsoft.com/dotnet/Sdk/{version}/dotnet-sdk-{version}-{rid}.{extension}"; - - public static PrerequisiteResult CheckSdk(string repoRoot) - { - var requiredVersion = ReadRequiredVersion(repoRoot); - var downloadUrl = GetDownloadUrl(requiredVersion); - - // Check if dotnet is on PATH - var result = ProcessRunner.RunCaptured("dotnet", "--list-sdks"); - if (!result.Success) - { - return new PrerequisiteResult - { - Name = ".NET SDK", - Passed = false, - Details = $".NET SDK {requiredVersion}+ is not installed", - DownloadUrl = downloadUrl - }; - } - - // Parse installed SDK versions — find the best match - Version? bestVersion = null; - string? bestVersionStr = null; - - foreach (var line in result.StandardOutput.Split('\n', StringSplitOptions.RemoveEmptyEntries)) - { - var match = SdkVersionRegex().Match(line); - if (!match.Success) - { - continue; - } - - var versionStr = match.Groups[1].Value; - if (!Version.TryParse(versionStr, out var version)) - { - continue; - } - - if (bestVersion is null || version > bestVersion) - { - bestVersion = version; - bestVersionStr = versionStr; - } - } - - if (bestVersion is null || bestVersion < requiredVersion) - { - return new PrerequisiteResult - { - Name = ".NET SDK", - Passed = false, - Details = bestVersionStr is not null - ? $".NET SDK {bestVersionStr} found, but {requiredVersion}+ is required" - : $".NET SDK {requiredVersion}+ is required but no SDK was found", - DownloadUrl = downloadUrl - }; - } - - return new PrerequisiteResult - { - Name = ".NET SDK", - Passed = true, - Details = $".NET SDK {bestVersionStr}" - }; - } - - public static bool OfferInstall(PlatformInfo platform, string repoRoot, bool interactive) - { - var requiredVersion = ReadRequiredVersion(repoRoot); - var downloadUrl = GetDownloadUrl(requiredVersion); - - if (!interactive) - { - AnsiConsole.MarkupLine("[red]Error:[/] .NET SDK is not installed."); - AnsiConsole.MarkupLine($" Download from: [link]{downloadUrl}[/]"); - return false; - } - - if (platform.IsWindows) - { - return OfferWindowsInstall(platform, requiredVersion, downloadUrl); - } - - if (platform.IsMacOS) - { - return OfferMacOsInstall(platform, requiredVersion, downloadUrl); - } - - // Linux — just show instructions - AnsiConsole.MarkupLine($"\n[yellow]Install the .NET SDK using your package manager:[/]"); - AnsiConsole.MarkupLine($" [link]{DotNetLinuxInstallUrl}[/]"); - AnsiConsole.WriteLine(); - return false; - } - - private static bool OfferWindowsInstall(PlatformInfo platform, Version requiredVersion, string downloadUrl) - { - var rid = $"win-{platform.ArchRid}"; - var installerUrl = GetInstallerUrl(requiredVersion, rid, "exe"); - - var install = AnsiConsole.Confirm( - "[yellow].NET SDK is required. Download and run the installer?[/]", - defaultValue: true - ); - - if (!install) - { - AnsiConsole.MarkupLine($"\n Download manually from: [link]{downloadUrl}[/]"); - return false; - } - - AnsiConsole.MarkupLine("\n[blue]Opening .NET SDK installer...[/]"); - - var result = ProcessRunner.RunPassthrough("cmd", $"/c start \"\" \"{installerUrl}\""); - if (result != 0) - { - AnsiConsole.MarkupLine("[red]Failed to open installer.[/]"); - AnsiConsole.MarkupLine($" Download manually from: [link]{downloadUrl}[/]"); - return false; - } - - AnsiConsole.MarkupLine("[yellow]Install the SDK, then restart your terminal and run this tool again.[/]"); - return false; // User needs to restart after installing - } - - private static bool OfferMacOsInstall(PlatformInfo platform, Version requiredVersion, string downloadUrl) - { - var rid = $"osx-{platform.ArchRid}"; - var installerUrl = GetInstallerUrl(requiredVersion, rid, "pkg"); - - var install = AnsiConsole.Confirm( - "[yellow].NET SDK is required. Download and run the installer?[/]", - defaultValue: true - ); - - if (!install) - { - AnsiConsole.MarkupLine($"\n Download manually from: [link]{downloadUrl}[/]"); - return false; - } - - AnsiConsole.MarkupLine("\n[blue]Opening .NET SDK installer...[/]"); - - var result = ProcessRunner.RunPassthrough("open", installerUrl); - if (result != 0) - { - AnsiConsole.MarkupLine("[red]Failed to open installer.[/]"); - AnsiConsole.MarkupLine($" Download manually from: [link]{downloadUrl}[/]"); - return false; - } - - AnsiConsole.MarkupLine("[yellow]Install the SDK, then restart your terminal and run this tool again.[/]"); - return false; // User needs to restart after installing - } - - /// - /// Reads the required SDK version from global.json. - /// Falls back to 10.0 if global.json is missing or unparseable. - /// - private static Version ReadRequiredVersion(string repoRoot) - { - var globalJsonPath = Path.Combine(repoRoot, "global.json"); - if (File.Exists(globalJsonPath)) - { - var json = File.ReadAllText(globalJsonPath); - var globalJson = JsonSerializer.Deserialize(json, GlobalJsonContext.Default.GlobalJson); - if (globalJson?.Sdk?.Version is { } versionStr && Version.TryParse(versionStr, out var version)) - { - return version; - } - } - - return new Version(10, 0); - } - - [GeneratedRegex(@"^(\d+\.\d+\.\d+\S*)")] - private static partial Regex SdkVersionRegex(); -} diff --git a/Projects/BuildTool/Prerequisites/NativeLibraryChecker.cs b/Projects/BuildTool/Prerequisites/NativeLibraryChecker.cs deleted file mode 100644 index 95c521c24..000000000 --- a/Projects/BuildTool/Prerequisites/NativeLibraryChecker.cs +++ /dev/null @@ -1,373 +0,0 @@ -using System.Runtime.InteropServices; -using BuildTool.Platform; -using BuildTool.Publishing; - -namespace BuildTool.Prerequisites; - -public static class NativeLibraryChecker -{ - /// - /// Returns the required prerequisites for a target OS (without checking the local machine). - /// Used to inform users what they need to install on the deployment target after cross-compiling. - /// - public static (string Description, string[] InstallCommands) GetRequirementsForTarget(string targetOs) - { - return targetOs switch - { - "win" => ( - "Windows", - [ - ".NET 10 Runtime — https://dotnet.microsoft.com/download/dotnet/10.0", - "VC++ Redistributable v14 — https://aka.ms/vs/17/release/vc_redist.x64.exe" - ] - ), - "osx" => ( - "macOS", - [ - ".NET 10 Runtime — https://dotnet.microsoft.com/download/dotnet/10.0", - "brew install icu4c libdeflate zstd argon2" - ] - ), - "linux" => ( - "Linux", - [ - ".NET 10 Runtime — https://dotnet.microsoft.com/download/dotnet/10.0", - "Debian/Ubuntu: sudo apt-get install -y libdeflate0 libargon2-1 libicuNN tzdata", - " (libicuNN varies by release, e.g. libicu76 — run build-tool --check-prereqs there for the exact name)", - " (add tzdata-legacy if the shard is configured with an alias such as US/Eastern)", - "Fedora/RHEL: sudo dnf install -y libdeflate libargon2 libicu tzdata", - "CentOS: Also requires epel-release and CRB enabled" - ] - ), - _ => ("Unknown", [".NET 10 Runtime — https://dotnet.microsoft.com/download/dotnet/10.0"]) - }; - } - - public static List Check(PlatformInfo platform) - { - if (platform.IsWindows) - { - return CheckWindows(platform); - } - - if (platform.IsMacOS) - { - return CheckMacOS(); - } - - if (platform.IsLinux) - { - return CheckLinux(platform); - } - - return []; - } - - private static List CheckWindows(PlatformInfo platform) - { - var results = new List(); - - // Check VC++ Redistributable via registry - var vcRedistInstalled = CheckVcRedist(platform.ArchRid); - var downloadUrl = platform.ArchRid == "arm64" - ? "https://aka.ms/vs/17/release/vc_redist.arm64.exe" - : "https://aka.ms/vs/17/release/vc_redist.x64.exe"; - - results.Add(new PrerequisiteResult - { - Name = "VC++ Redistributable v14", - Passed = vcRedistInstalled, - Details = vcRedistInstalled ? "Installed" : "Not found", - DownloadUrl = vcRedistInstalled ? null : downloadUrl - }); - - return results; - } - - private static bool CheckVcRedist(string arch) - { - if (!OperatingSystem.IsWindows()) - { - return false; - } - - // Check multiple known registry paths for VC++ 14.x Redistributable - string[] registryPaths = - [ - $@"SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\{arch}", - $@"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\{arch}" - ]; - - foreach (var path in registryPaths) - { - try - { - using var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(path); - if (key?.GetValue("Installed") is int installed && installed == 1) - { - return true; - } - } - catch - { - // Registry access may fail, continue checking - } - } - - return false; - } - - private static List CheckMacOS() - { - var results = new List(); - - // Check if Homebrew is installed - var brewResult = ProcessRunner.RunCaptured("which", "brew"); - if (!brewResult.Success) - { - results.Add(new PrerequisiteResult - { - Name = "Homebrew", - Passed = false, - Details = "Homebrew is required to install native dependencies", - DownloadUrl = "https://brew.sh" - }); - return results; - } - - // Check required Homebrew formulae - var formulae = new[] { "icu4c", "libdeflate", "zstd", "argon2" }; - var listResult = ProcessRunner.RunCaptured("brew", "list --formula"); - var installedFormulae = listResult.Success - ? new HashSet( - listResult.StandardOutput.Split('\n', StringSplitOptions.RemoveEmptyEntries), - StringComparer.OrdinalIgnoreCase) - : new HashSet(StringComparer.OrdinalIgnoreCase); - - var missing = new List(); - foreach (var formula in formulae) - { - // Check exact match or versioned variant (e.g. "icu4c@78" matches "icu4c") - var installed = installedFormulae.Contains(formula) || - installedFormulae.Any(f => f.StartsWith($"{formula}@", StringComparison.OrdinalIgnoreCase)); - if (!installed) - { - missing.Add(formula); - } - - results.Add(new PrerequisiteResult - { - Name = formula, - Passed = installed, - Details = installed ? "Installed" : "Not installed", - InstallCommand = installed ? null : $"brew install {formula}" - }); - } - - if (missing.Count > 0) - { - results.Add(new PrerequisiteResult - { - Name = "Install all missing", - Passed = false, - IsWarning = true, - Details = "Run the following command to install all missing dependencies:", - InstallCommand = $"brew install {string.Join(' ', missing)}" - }); - } - - return results; - } - - /// - /// Native libraries the server needs from the system on Linux, and the SONAME range to accept - /// for each. Rationale and per-distro package names: dev-docs/platform-prerequisites.md. - /// - private static readonly (string Name, int MinSoVersion, int MaxSoVersion)[] _linuxLibraries = - [ - ("libicuuc", 60, 120), - ("libicui18n", 60, 120), - ("libdeflate", 0, 9), - ("libargon2", 0, 9) - ]; - - private static List CheckLinux(PlatformInfo platform) - { - var results = new List(); - var missing = new List(); - - foreach (var (name, minSoVersion, maxSoVersion) in _linuxLibraries) - { - var found = CanLoad(name, minSoVersion, maxSoVersion); - - if (!found) - { - missing.Add(name); - } - - results.Add(new PrerequisiteResult - { - Name = name, - Passed = found, - Details = found ? "Found" : "Not found" - }); - } - - var hasTimeZoneData = HasTimeZoneData(); - if (!hasTimeZoneData) - { - missing.Add("tzdata"); - } - - results.Add(new PrerequisiteResult - { - Name = "tzdata", - Passed = hasTimeZoneData, - Details = hasTimeZoneData ? "Found" : "Not found — every zone except UTC will throw" - }); - - if (missing.Count == 0) - { - return results; - } - - if (platform.DistroId?.Equals("centos", StringComparison.OrdinalIgnoreCase) == true) - { - results.Add(new PrerequisiteResult - { - Name = "EPEL Repository", - Passed = false, - IsWarning = true, - Details = "CentOS requires EPEL for some packages. Enable it first:", - InstallCommand = "sudo dnf install -y epel-release epel-next-release && sudo dnf config-manager --set-enabled crb" - }); - } - - results.Add(new PrerequisiteResult - { - Name = "Install all missing", - Passed = false, - IsWarning = true, - Details = "Install the missing dependencies. The -dev/-devel packages are not required:", - InstallCommand = BuildInstallCommand(platform, missing) - }); - - return results; - } - - /// - /// tzdata is data, not a library, so no loader probe finds it. Asking the runtime rather than - /// stat'ing a path keeps TZDIR honoured, and the count is still accurate under - /// InvariantGlobalization, which this tool runs with — only display names degrade there. - /// - private static bool HasTimeZoneData() - { - try - { - return TimeZoneInfo.GetSystemTimeZones().Count > 1; - } - catch - { - return false; - } - } - - /// - /// Asks the loader directly rather than querying a package database or scanning ldconfig's - /// cache, both of which answer a different question and can disagree with what dlopen will do. - /// Mirrors the binding packages' own probing: the unversioned name first, then libfoo.so.N - /// descending. Bare names go through the full loader search path, so LD_LIBRARY_PATH and - /// /etc/ld.so.conf.d still apply. - /// - private static bool CanLoad(string library, int minSoVersion, int maxSoVersion) - { - if (TryLoadAndFree($"{library}.so")) - { - return true; - } - - for (var soVersion = maxSoVersion; soVersion >= minSoVersion; soVersion--) - { - if (TryLoadAndFree($"{library}.so.{soVersion}")) - { - return true; - } - } - - return false; - } - - private static bool TryLoadAndFree(string candidate) - { - if (!NativeLibrary.TryLoad(candidate, out var handle)) - { - return false; - } - - NativeLibrary.Free(handle); - return true; - } - - private static string BuildInstallCommand(PlatformInfo platform, List missing) - { - switch (platform.PackageManager) - { - case PackageManager.Apt: - { - // Distinct because the two ICU libraries resolve to the same package, and - // ResolveAptIcuPackage shells out, so it is memoized rather than called per name. - var packages = missing.Select( - library => library switch - { - "libdeflate" => "libdeflate0", - "libargon2" => "libargon2-1", - "tzdata" => "tzdata", - _ => _aptIcuPackage ??= ResolveAptIcuPackage() - } - ).Distinct(); - - return $"sudo apt-get install -y {string.Join(' ', packages)}"; - } - case PackageManager.Dnf: - { - var packages = missing.Select( - library => library switch - { - "libdeflate" => "libdeflate", - "libargon2" => "libargon2", - "tzdata" => "tzdata", - _ => "libicu" - } - ).Distinct(); - - return $"sudo dnf install -y {string.Join(' ', packages)}"; - } - default: - return $"Install your distribution's runtime packages for: {string.Join(", ", missing)}"; - } - } - - private static string _aptIcuPackage; - - /// - /// ICU's apt package carries the ABI version in its name and there is no stable alias, so ask - /// apt which one this release actually ships instead of printing a name that rots. - /// - private static string ResolveAptIcuPackage() - { - var result = ProcessRunner.RunCaptured("apt-cache", "search --names-only ^libicu[0-9]+$"); - if (!result.Success) - { - return "libicu"; - } - - var best = result.StandardOutput - .Split('\n', StringSplitOptions.RemoveEmptyEntries) - .Select(line => line.Split(' ', 2)[0].Trim()) - .Where(name => name.StartsWith("libicu", StringComparison.Ordinal)) - .OrderBy(name => int.TryParse(name.AsSpan(6), out var version) ? version : 0) - .LastOrDefault(); - - return best ?? "libicu"; - } -} diff --git a/Projects/BuildTool/Prerequisites/PrerequisiteChecker.cs b/Projects/BuildTool/Prerequisites/PrerequisiteChecker.cs deleted file mode 100644 index c5b4f1305..000000000 --- a/Projects/BuildTool/Prerequisites/PrerequisiteChecker.cs +++ /dev/null @@ -1,165 +0,0 @@ -using BuildTool.Platform; -using Spectre.Console; - -namespace BuildTool.Prerequisites; - -public static class PrerequisiteChecker -{ - /// - /// Runs all prerequisite checks (SDK + native libraries) and displays results. - /// Returns true if all critical prerequisites pass. - /// - public static bool CheckAll(PlatformInfo platform, string repoRoot, bool interactive) - { - var panel = new Panel("[bold]Checking prerequisites[/]") - .Border(BoxBorder.Rounded) - .BorderColor(Branding.Gold) - .Padding(1, 0); - AnsiConsole.Write(panel); - AnsiConsole.WriteLine(); - - if (!CheckSdkInternal(platform, repoRoot, interactive)) - { - return false; - } - - return CheckNativeLibrariesInternal(platform, interactive); - } - - /// - /// Checks only the .NET SDK (sufficient for cross-compilation). - /// Returns true if the SDK is available. - /// - public static bool CheckSdk(PlatformInfo platform, string repoRoot, bool interactive) - { - var panel = new Panel("[bold]Checking .NET SDK[/]") - .Border(BoxBorder.Rounded) - .BorderColor(Branding.Gold) - .Padding(1, 0); - AnsiConsole.Write(panel); - AnsiConsole.WriteLine(); - - var passed = CheckSdkInternal(platform, repoRoot, interactive); - AnsiConsole.WriteLine(); - return passed; - } - - /// - /// Checks native libraries for the current platform. - /// Call this only when the publish target matches the current OS. - /// Returns true if all pass or the user chose to continue. - /// - public static bool CheckNativeLibraries(PlatformInfo platform, bool interactive) - { - AnsiConsole.Write(new Panel("[bold]Checking native libraries[/]") - .Border(BoxBorder.Rounded) - .BorderColor(Branding.Gold) - .Padding(1, 0)); - AnsiConsole.WriteLine(); - - var passed = CheckNativeLibrariesInternal(platform, interactive); - AnsiConsole.WriteLine(); - return passed; - } - - private static bool CheckSdkInternal(PlatformInfo platform, string repoRoot, bool interactive) - { - var sdkResult = DotNetSdkManager.CheckSdk(repoRoot); - DisplayResult(sdkResult); - - if (!sdkResult.Passed) - { - var installed = DotNetSdkManager.OfferInstall(platform, repoRoot, interactive); - if (!installed) - { - return false; - } - - sdkResult = DotNetSdkManager.CheckSdk(repoRoot); - if (!sdkResult.Passed) - { - AnsiConsole.MarkupLine("[red]SDK installation may require a terminal restart.[/]"); - return false; - } - - DisplayResult(sdkResult); - } - - return true; - } - - private static bool CheckNativeLibrariesInternal(PlatformInfo platform, bool interactive) - { - var nativeResults = NativeLibraryChecker.Check(platform); - var hasMissing = false; - - foreach (var result in nativeResults) - { - if (result.IsWarning) - { - continue; - } - - DisplayResult(result); - if (!result.Passed) - { - hasMissing = true; - } - } - - if (hasMissing) - { - AnsiConsole.WriteLine(); - - foreach (var result in nativeResults) - { - if (!result.IsWarning) - { - continue; - } - - if (result.InstallCommand is not null) - { - AnsiConsole.MarkupLine($" [yellow]:warning: {result.Details}[/]"); - AnsiConsole.MarkupLine($" [white on grey23] {result.InstallCommand} [/]"); - AnsiConsole.WriteLine(); - } - } - - if (interactive) - { - var continueAnyway = AnsiConsole.Confirm( - "[yellow]Some prerequisites are missing. Continue anyway?[/]", - defaultValue: false - ); - - if (!continueAnyway) - { - return false; - } - } - - return interactive; // In interactive mode, user chose to continue - } - - AnsiConsole.MarkupLine("[green]:check_mark_button: All prerequisites satisfied.[/]"); - return true; - } - - private static void DisplayResult(PrerequisiteResult result) - { - if (result.IsWarning) - { - return; - } - - var icon = result.Passed ? "[green]:check_mark:[/]" : "[red]:cross_mark:[/]"; - var details = result.Details is not null ? $" [grey]({Markup.Escape(result.Details)})[/]" : ""; - AnsiConsole.MarkupLine($" {icon} {Markup.Escape(result.Name)}{details}"); - - if (!result.Passed && result.DownloadUrl is not null) - { - AnsiConsole.MarkupLine($" [grey]Download: {result.DownloadUrl}[/]"); - } - } -} diff --git a/Projects/BuildTool/Prerequisites/PrerequisiteResult.cs b/Projects/BuildTool/Prerequisites/PrerequisiteResult.cs deleted file mode 100644 index e7b123730..000000000 --- a/Projects/BuildTool/Prerequisites/PrerequisiteResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace BuildTool.Prerequisites; - -public sealed class PrerequisiteResult -{ - public required string Name { get; init; } - public required bool Passed { get; init; } - public string? Details { get; init; } - public string? InstallCommand { get; init; } - public string? DownloadUrl { get; init; } - public bool IsWarning { get; init; } -} diff --git a/Projects/BuildTool/Program.cs b/Projects/BuildTool/Program.cs deleted file mode 100644 index 915839bbf..000000000 --- a/Projects/BuildTool/Program.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System.Text; -using BuildTool; -using BuildTool.Interactive; -using BuildTool.Platform; -using BuildTool.Prerequisites; -using BuildTool.Publishing; -using Spectre.Console; - -Console.OutputEncoding = Encoding.UTF8; - -var repoRoot = FindRepoRoot(); -if (repoRoot is null) -{ - Console.Error.WriteLine("Error: Could not find ModernUO.slnx. Run this tool from the repository root."); - return 1; -} - -var options = ParseArguments(args); - -// Interactive mode: no args provided -if (options.Interactive) -{ - // Handle Ctrl+C by signaling our cancellation token (not killing the process) - Console.CancelKeyPress += (_, e) => - { - e.Cancel = true; - InteractiveCancellation.Instance.Cancel(); - }; - - var platform = PlatformDetector.Detect(); - return GuidedMode.Run(platform, repoRoot); -} - -// Non-interactive mode -var detectedPlatform = PlatformDetector.Detect(); -options.Os ??= detectedPlatform.OsRid; -options.Arch ??= detectedPlatform.ArchRid; -var rid = $"{options.Os}-{options.Arch}"; - -if (options.CheckPrereqsOnly) -{ - // Same renderer the guided menu uses, so the two cannot drift. Spectre drops ANSI styling by - // itself when stdout is not a terminal, which is the case this flag exists for, but it also - // falls back to an 80 column width and folds anything longer. The install hints we print are - // shell commands — the CentOS one is 95 characters — and a fold puts a newline in the middle of - // a command that someone is meant to copy. Widen the profile so they stay on one line. - if (Console.IsOutputRedirected) - { - AnsiConsole.Profile.Width = 200; - } - - // Exit code is the machine-readable half: 0 when everything resolves, 1 when anything is missing. - return PrerequisiteChecker.CheckNativeLibraries(detectedPlatform, interactive: false) ? 0 : 1; -} - -// Run prerequisite checks unless skipped -if (!options.SkipPrereqs) -{ - var sdkResult = DotNetSdkManager.CheckSdk(repoRoot); - if (!sdkResult.Passed) - { - Console.Error.WriteLine($"Error: {sdkResult.Details}"); - if (sdkResult.DownloadUrl is not null) - { - Console.Error.WriteLine($"Download: {sdkResult.DownloadUrl}"); - } - return 1; - } -} - -return options.Action switch -{ - BuildAction.Publish => PublishOrchestrator.Run(options.Config, rid, interactive: false), - BuildAction.Migrate => SchemaMigrator.Run(interactive: false), - _ => 1 -}; - -static BuildOptions ParseArguments(string[] args) -{ - var options = new BuildOptions(); - - if (args.Length == 0) - { - options.Interactive = true; - return options; - } - - // Check for named arguments first - var hasNamedArgs = false; - for (var i = 0; i < args.Length; i++) - { - switch (args[i]) - { - case "--config" when i + 1 < args.Length: - { - options.Config = NormalizeConfig(args[++i]); - hasNamedArgs = true; - break; - } - case "--os" when i + 1 < args.Length: - { - options.Os = args[++i].ToLowerInvariant(); - hasNamedArgs = true; - break; - } - case "--arch" when i + 1 < args.Length: - { - options.Arch = args[++i].ToLowerInvariant(); - hasNamedArgs = true; - break; - } - case "--action" when i + 1 < args.Length: - { - options.Action = args[++i].ToLowerInvariant() switch - { - "migrate" => BuildAction.Migrate, - _ => BuildAction.Publish - }; - hasNamedArgs = true; - break; - } - case "--skip-prereqs": - { - options.SkipPrereqs = true; - hasNamedArgs = true; - break; - } - case "--check-prereqs": - { - options.CheckPrereqsOnly = true; - hasNamedArgs = true; - break; - } - case "--interactive": - { - options.Interactive = true; - hasNamedArgs = true; - break; - } - } - } - - if (hasNamedArgs) - { - return options; - } - - // Positional argument parsing (backward compat: [config] [os] [arch]) - if (args.Length >= 1) - { - options.Config = NormalizeConfig(args[0]); - } - - if (args.Length >= 2) - { - options.Os = args[1].ToLowerInvariant(); - } - - if (args.Length >= 3) - { - options.Arch = args[2].ToLowerInvariant(); - } - - return options; -} - -static string NormalizeConfig(string config) => - config.ToLowerInvariant() switch - { - "release" => "Release", - "debug" => "Debug", - _ => char.ToUpperInvariant(config[0]) + config[1..].ToLowerInvariant() - }; - -static string? FindRepoRoot() -{ - // Check current directory first - var current = Directory.GetCurrentDirectory(); - if (File.Exists(Path.Combine(current, "ModernUO.slnx"))) - { - return current; - } - - // Walk up to find the solution file - var dir = new DirectoryInfo(current); - while (dir?.Parent is not null) - { - dir = dir.Parent; - if (File.Exists(Path.Combine(dir.FullName, "ModernUO.slnx"))) - { - return dir.FullName; - } - } - - return null; -} diff --git a/Projects/BuildTool/Publishing/ProcessRunner.cs b/Projects/BuildTool/Publishing/ProcessRunner.cs deleted file mode 100644 index 861268032..000000000 --- a/Projects/BuildTool/Publishing/ProcessRunner.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System.Diagnostics; -using System.Text; - -namespace BuildTool.Publishing; - -public sealed class ProcessResult -{ - public required int ExitCode { get; init; } - public required string StandardOutput { get; init; } - public required string StandardError { get; init; } - public bool Success => ExitCode == 0; -} - -public static class ProcessRunner -{ - /// - /// Runs a command with output captured (for parsing results). - /// - public static ProcessResult RunCaptured(string fileName, string arguments, string? workingDirectory = null) - { - var stdout = new StringBuilder(); - var stderr = new StringBuilder(); - - var psi = new ProcessStartInfo - { - FileName = fileName, - Arguments = arguments, - WorkingDirectory = workingDirectory ?? Directory.GetCurrentDirectory(), - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true - }; - - using var process = Process.Start(psi); - if (process is null) - { - return new ProcessResult - { - ExitCode = -1, - StandardOutput = "", - StandardError = $"Failed to start process: {fileName}" - }; - } - - process.OutputDataReceived += (_, e) => - { - if (e.Data is not null) - { - stdout.AppendLine(e.Data); - } - }; - process.ErrorDataReceived += (_, e) => - { - if (e.Data is not null) - { - stderr.AppendLine(e.Data); - } - }; - - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - process.WaitForExit(); - - return new ProcessResult - { - ExitCode = process.ExitCode, - StandardOutput = stdout.ToString(), - StandardError = stderr.ToString() - }; - } - - /// - /// Runs a command with output passed through to the console (for build commands in non-interactive mode). - /// - public static int RunPassthrough(string fileName, string arguments, string? workingDirectory = null) - { - var psi = new ProcessStartInfo - { - FileName = fileName, - Arguments = arguments, - WorkingDirectory = workingDirectory ?? Directory.GetCurrentDirectory(), - UseShellExecute = false - }; - - using var process = Process.Start(psi); - if (process is null) - { - return -1; - } - - process.WaitForExit(); - return process.ExitCode; - } -} diff --git a/Projects/BuildTool/Publishing/PublishOrchestrator.cs b/Projects/BuildTool/Publishing/PublishOrchestrator.cs deleted file mode 100644 index 1a704b9b3..000000000 --- a/Projects/BuildTool/Publishing/PublishOrchestrator.cs +++ /dev/null @@ -1,182 +0,0 @@ -using BuildTool.Platform; -using Spectre.Console; - -namespace BuildTool.Publishing; - -public static class PublishOrchestrator -{ - // Target the Application project (not the solution) to avoid building BuildTool and test projects - private const string AppProject = "Projects/Application/Application.csproj"; - - private static readonly (string Description, string Command, string Arguments)[] BuildSteps = - [ - ("Restoring tools", "dotnet", "tool restore"), - ("Cleaning project", "dotnet", $"clean {AppProject} --verbosity quiet"), - ("Restoring packages", "dotnet", $"restore {AppProject} --force-evaluate --source https://api.nuget.org/v3/index.json"), - ]; - - public static int Run(string config, PlatformInfo platform, bool interactive) - { - return Run(config, platform.Rid, interactive); - } - - public static int Run(string config, string rid, bool interactive, bool isCrossCompile = false) - { - if (interactive) - { - return RunInteractive(config, rid, isCrossCompile); - } - - return RunNonInteractive(config, rid); - } - - private static int RunInteractive(string config, string rid, bool isCrossCompile) - { - var panel = new Panel($"[bold]Publishing[/] [rgb(223,198,136)]{config}[/] for [rgb(223,198,136)]{rid}[/]") - .Border(BoxBorder.Rounded) - .BorderColor(Branding.Gold) - .Padding(1, 0); - AnsiConsole.Write(panel); - AnsiConsole.WriteLine(); - - var allSteps = new List<(string Description, string Command, string Arguments)>(BuildSteps) - { - ($"Publishing ({config}, {rid})", "dotnet", $"publish {AppProject} -c {config} -r {rid} --no-restore --self-contained=false"), - ("Generating serialization schema", "dotnet", "tool run ModernUOSchemaGenerator -- ModernUO.slnx") - }; - - var completed = 0; - var total = allSteps.Count; - - foreach (var (description, command, arguments) in allSteps) - { - completed++; - var exitCode = RunStepInteractive(description, command, arguments, completed, total); - if (exitCode != 0) - { - AnsiConsole.WriteLine(); - AnsiConsole.Write(new Panel("[red bold]Build failed.[/] See error output above for details.") - .Border(BoxBorder.Rounded) - .BorderColor(Color.Red) - .Padding(1, 0)); - return exitCode; - } - } - - AnsiConsole.WriteLine(); - DisplaySuccess(rid, isCrossCompile); - return 0; - } - - private static int RunStepInteractive(string description, string command, string arguments, int step, int total) - { - var exitCode = 0; - string errorOutput = ""; - - AnsiConsole.Status() - .Spinner(Spinner.Known.Dots) - .SpinnerStyle(Branding.GoldStyle) - .Start($"[grey]({step}/{total})[/] {description}...", _ => - { - var result = ProcessRunner.RunCaptured(command, arguments); - exitCode = result.ExitCode; - errorOutput = result.StandardError; - }); - - if (exitCode == 0) - { - AnsiConsole.MarkupLine($" [green]:check_mark:[/] [grey]({step}/{total})[/] {description}"); - } - else - { - AnsiConsole.MarkupLine($" [red]:cross_mark:[/] [grey]({step}/{total})[/] {description} [red](failed)[/]"); - - if (!string.IsNullOrWhiteSpace(errorOutput)) - { - AnsiConsole.WriteLine(); - AnsiConsole.Write(new Panel(Markup.Escape(errorOutput.Trim())) - .Header("[red]Error Output[/]") - .Border(BoxBorder.Rounded) - .BorderColor(Color.Red) - .Expand()); - } - - AnsiConsole.WriteLine(); - AnsiConsole.MarkupLine($" [grey]Command: {command} {arguments}[/]"); - } - - return exitCode; - } - - private static int RunNonInteractive(string config, string rid) - { - // Run common build steps - foreach (var (description, command, arguments) in BuildSteps) - { - Console.WriteLine($"{command} {arguments}"); - var exitCode = ProcessRunner.RunPassthrough(command, arguments); - if (exitCode != 0) - { - Console.Error.WriteLine($"Error: '{description}' failed with exit code {exitCode}"); - return exitCode; - } - } - - // Publish step - { - var publishArgs = $"publish {AppProject} -c {config} -r {rid} --no-restore --self-contained=false"; - Console.WriteLine($"dotnet {publishArgs}"); - var exitCode = ProcessRunner.RunPassthrough("dotnet", publishArgs); - if (exitCode != 0) - { - Console.Error.WriteLine($"Error: 'publish' failed with exit code {exitCode}"); - return exitCode; - } - } - - // Schema generation - { - Console.WriteLine("Generating serialization migration schema..."); - const string schemaArgs = "tool run ModernUOSchemaGenerator -- ModernUO.slnx"; - var exitCode = ProcessRunner.RunPassthrough("dotnet", schemaArgs); - if (exitCode != 0) - { - Console.Error.WriteLine($"Error: schema generation failed with exit code {exitCode}"); - return exitCode; - } - } - - return 0; - } - - internal static void DisplaySuccess(string rid, bool isCrossCompile = false) - { - var isWindows = rid.StartsWith("win", StringComparison.OrdinalIgnoreCase); - var runCommand = isWindows ? "ModernUO.exe" : "dotnet ModernUO.dll"; - - var rows = new List - { - new Markup("[green bold]:check_mark_button: Build complete![/]"), - new Markup("") - }; - - if (isCrossCompile) - { - rows.Add(new Markup($"Copy the [rgb(213,191,116)]Distribution[/] folder to your [rgb(213,191,116)]{rid}[/] server, then run:")); - } - else - { - rows.Add(new Markup("Run the server from the [rgb(213,191,116)]Distribution[/] directory:")); - } - - rows.Add(new Markup("")); - rows.Add(new Markup(" [white on grey23] cd Distribution [/]")); - rows.Add(new Markup($" [white on grey23] {runCommand} [/]")); - - AnsiConsole.Write(new Panel(new Rows(rows)) - .Border(BoxBorder.Rounded) - .BorderColor(Color.Green) - .Padding(1, 0)); - AnsiConsole.WriteLine(); - } -} diff --git a/Projects/BuildTool/Publishing/SchemaMigrator.cs b/Projects/BuildTool/Publishing/SchemaMigrator.cs deleted file mode 100644 index 6bbe5291c..000000000 --- a/Projects/BuildTool/Publishing/SchemaMigrator.cs +++ /dev/null @@ -1,126 +0,0 @@ -using Spectre.Console; - -namespace BuildTool.Publishing; - -public static class SchemaMigrator -{ - public static int Run(bool interactive) - { - if (interactive) - { - return RunInteractive(); - } - - return RunNonInteractive(); - } - - private static int RunInteractive() - { - var panel = new Panel("[bold]Running schema migration[/]") - .Border(BoxBorder.Rounded) - .BorderColor(Branding.Gold) - .Padding(1, 0); - AnsiConsole.Write(panel); - AnsiConsole.WriteLine(); - - // Tool restore - { - var exitCode = 0; - string errorOutput = ""; - - AnsiConsole.Status() - .Spinner(Spinner.Known.Dots) - .SpinnerStyle(Branding.GoldStyle) - .Start("[grey](1/2)[/] Restoring tools...", _ => - { - var result = ProcessRunner.RunCaptured("dotnet", "tool restore"); - exitCode = result.ExitCode; - errorOutput = result.StandardError; - }); - - if (exitCode == 0) - { - AnsiConsole.MarkupLine(" [green]:check_mark:[/] [grey](1/2)[/] Tools restored"); - } - else - { - AnsiConsole.MarkupLine(" [red]:cross_mark:[/] [grey](1/2)[/] Tool restore [red](failed)[/]"); - if (!string.IsNullOrWhiteSpace(errorOutput)) - { - AnsiConsole.Write(new Panel(Markup.Escape(errorOutput.Trim())) - .Header("[red]Error[/]") - .Border(BoxBorder.Rounded) - .BorderColor(Color.Red)); - } - - return exitCode; - } - } - - // Schema generation - { - var exitCode = 0; - string errorOutput = ""; - - AnsiConsole.Status() - .Spinner(Spinner.Known.Dots) - .SpinnerStyle(Branding.GoldStyle) - .Start("[grey](2/2)[/] Generating serialization schema...", _ => - { - var result = ProcessRunner.RunCaptured( - "dotnet", - "tool run ModernUOSchemaGenerator -- ModernUO.slnx" - ); - exitCode = result.ExitCode; - errorOutput = result.StandardError; - }); - - if (exitCode == 0) - { - AnsiConsole.MarkupLine(" [green]:check_mark:[/] [grey](2/2)[/] Schema generated"); - } - else - { - AnsiConsole.MarkupLine(" [red]:cross_mark:[/] [grey](2/2)[/] Schema generation [red](failed)[/]"); - if (!string.IsNullOrWhiteSpace(errorOutput)) - { - AnsiConsole.Write(new Panel(Markup.Escape(errorOutput.Trim())) - .Header("[red]Error[/]") - .Border(BoxBorder.Rounded) - .BorderColor(Color.Red)); - } - - return exitCode; - } - } - - AnsiConsole.WriteLine(); - AnsiConsole.Write(new Panel("[green bold]:check_mark_button: Schema migration complete.[/]") - .Border(BoxBorder.Rounded) - .BorderColor(Color.Green) - .Padding(1, 0)); - AnsiConsole.WriteLine(); - return 0; - } - - private static int RunNonInteractive() - { - Console.WriteLine("dotnet tool restore"); - var exitCode = ProcessRunner.RunPassthrough("dotnet", "tool restore"); - if (exitCode != 0) - { - Console.Error.WriteLine($"Error: tool restore failed with exit code {exitCode}"); - return exitCode; - } - - Console.WriteLine("Generating serialization migration schema..."); - exitCode = ProcessRunner.RunPassthrough("dotnet", "tool run ModernUOSchemaGenerator -- ModernUO.slnx"); - if (exitCode != 0) - { - Console.Error.WriteLine($"Error: schema generation failed with exit code {exitCode}"); - return exitCode; - } - - return 0; - } -} diff --git a/Projects/BuildTool/build-tool.ico b/Projects/BuildTool/build-tool.ico deleted file mode 100644 index f5591bbb7..000000000 Binary files a/Projects/BuildTool/build-tool.ico and /dev/null differ diff --git a/Projects/Logger/ILogger.cs b/Projects/Logger/ILogger.cs index edc214e5e..ed038267a 100644 --- a/Projects/Logger/ILogger.cs +++ b/Projects/Logger/ILogger.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ILogger.cs * * * diff --git a/Projects/Logger/LogFactory.cs b/Projects/Logger/LogFactory.cs index 729343681..bed3ea20e 100644 --- a/Projects/Logger/LogFactory.cs +++ b/Projects/Logger/LogFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: LogFactory.cs * * * @@ -15,13 +15,12 @@ using System; using Serilog; -using Serilog.Core; namespace Server.Logging; public static class LogFactory { - private static readonly Logger serilogLogger = new LoggerConfiguration() + private static readonly Serilog.ILogger serilogLogger = new LoggerConfiguration() .WriteTo.Async(a => a.Console( outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {NewLine}{Exception}" )) diff --git a/Projects/Logger/Logger.csproj b/Projects/Logger/Logger.csproj index 6011e8299..e94ae59a8 100644 --- a/Projects/Logger/Logger.csproj +++ b/Projects/Logger/Logger.csproj @@ -7,13 +7,14 @@ ..\..\Distribution\Assemblies Debug;Release;Analyze - - - - - - - + + + + + + + + diff --git a/Projects/Logger/SerilogLogger.cs b/Projects/Logger/SerilogLogger.cs index b52e42f77..8204e7138 100644 --- a/Projects/Logger/SerilogLogger.cs +++ b/Projects/Logger/SerilogLogger.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SerilogLogger.cs * * * diff --git a/Projects/Server.Tests/Fixtures/ServerFixture.cs b/Projects/Server.Tests/Fixtures/ServerFixture.cs index e2795848f..b57248286 100644 --- a/Projects/Server.Tests/Fixtures/ServerFixture.cs +++ b/Projects/Server.Tests/Fixtures/ServerFixture.cs @@ -1,41 +1,41 @@ -using Xunit; +using System; +using System.Reflection; namespace Server.Tests; -/// -/// Fixture for all server tests. Attempts to load TileData from client files if available. -/// Configure client path via MODERNUO_CLIENT_PATH environment variable or place files at C:\Ultima Online Classic. -/// -[CollectionDefinition("Sequential Server Tests", DisableParallelization = true)] -public class ServerFixture : ICollectionFixture +internal class ServerFixture : IDisposable { - /// - /// True if TileData was successfully loaded from client files. - /// - public static bool TileDataLoaded => TestServerInitializer.TileDataLoaded; + // Global setup + static ServerFixture() + { + Core.ApplicationAssembly = Assembly.GetExecutingAssembly(); // Server.Tests.dll - /// - /// A tile ID known to have the Surface flag. 0 if not found. - /// - public static ushort SurfaceTileId => TestServerInitializer.SurfaceTileId; + // Load Configurations + ServerConfiguration.Load(true); - /// - /// A tile ID known to have the Impassable flag. 0 if not found. - /// - public static ushort ImpassableTileId => TestServerInitializer.ImpassableTileId; + // Load an empty assembly list into the resolver + ServerConfiguration.AssemblyDirectories.Add(Core.BaseDirectory); + AssemblyHandler.LoadAssemblies(["Server.dll"]); - /// - /// A tile ID known to have the Wet flag (water). 0 if not found. - /// - public static ushort WetTileId => TestServerInitializer.WetTileId; + Core.LoopContext = new EventLoopContext(); + Core.Expansion = Expansion.EJ; - /// - /// A tile ID known to have both Surface and Impassable flags (tables, furniture). 0 if not found. - /// - public static ushort SurfaceImpassableTileId => TestServerInitializer.SurfaceImpassableTileId; + // Configure / Initialize + TestMapDefinitions.ConfigureTestMapDefinitions(); - // Global init runs exactly once via the shared, guarded TestServerInitializer. The single - // bootstrap owns global state for the lifetime of the test host, so there is no - // per-collection teardown — matching UOContent.Tests' TestServerInitializer pattern. - public ServerFixture() => TestServerInitializer.Initialize(loadTileData: true); + // Configure the world + World.Configure(); + + Timer.Init(0); + + // Load the world + World.Load(); + + World.ExitSerializationThreads(); + } + + public void Dispose() + { + Timer.Init(0); + } } diff --git a/Projects/Server.Tests/Fixtures/TestMapDefinitions.cs b/Projects/Server.Tests/Fixtures/TestMapDefinitions.cs index a6f3f74ab..a94ae799a 100644 --- a/Projects/Server.Tests/Fixtures/TestMapDefinitions.cs +++ b/Projects/Server.Tests/Fixtures/TestMapDefinitions.cs @@ -1,33 +1,34 @@ -namespace Server.Tests.Maps; - -public static class TestMapDefinitions +namespace Server { - public static void ConfigureTestMapDefinitions() + public static class TestMapDefinitions { - RegisterMap(0, 0, 0, 7168, 4096, 4, "Felucca", MapRules.FeluccaRules); - RegisterMap(1, 1, 1, 7168, 4096, 0, "Trammel", MapRules.TrammelRules); - RegisterMap(2, 2, 2, 2304, 1600, 1, "Ilshenar", MapRules.TrammelRules); - RegisterMap(3, 3, 3, 2560, 2048, 1, "Malas", MapRules.TrammelRules); - RegisterMap(4, 4, 4, 1448, 1448, 1, "Tokuno", MapRules.TrammelRules); - RegisterMap(5, 5, 5, 1280, 4096, 1, "TerMur", MapRules.TrammelRules); + public static void ConfigureTestMapDefinitions() + { + RegisterMap(0, 0, 0, 7168, 4096, 4, "Felucca", MapRules.FeluccaRules); + RegisterMap(1, 1, 1, 7168, 4096, 0, "Trammel", MapRules.TrammelRules); + RegisterMap(2, 2, 2, 2304, 1600, 1, "Ilshenar", MapRules.TrammelRules); + RegisterMap(3, 3, 3, 2560, 2048, 1, "Malas", MapRules.TrammelRules); + RegisterMap(4, 4, 4, 1448, 1448, 1, "Tokuno", MapRules.TrammelRules); + RegisterMap(5, 5, 5, 1280, 4096, 1, "TerMur", MapRules.TrammelRules); - RegisterMap(0x7F, 0x7F, 0x7F, Map.SectorSize, Map.SectorSize, 1, "Internal", MapRules.Internal); - } + RegisterMap(0x7F, 0x7F, 0x7F, Map.SectorSize, Map.SectorSize, 1, "Internal", MapRules.Internal); + } - private static void RegisterMap( - int mapIndex, - int mapID, - int fileIndex, - int width, - int height, - int season, - string name, - MapRules rules - ) - { - var newMap = new Map(mapID, mapIndex, fileIndex, width, height, season, name, rules); + private static void RegisterMap( + int mapIndex, + int mapID, + int fileIndex, + int width, + int height, + int season, + string name, + MapRules rules + ) + { + var newMap = new Map(mapID, mapIndex, fileIndex, width, height, season, name, rules); - Map.Maps[mapIndex] = newMap; - Map.AllMaps.Add(newMap); + Map.Maps[mapIndex] = newMap; + Map.AllMaps.Add(newMap); + } } } diff --git a/Projects/Server.Tests/Fixtures/TestServerInitializer.cs b/Projects/Server.Tests/Fixtures/TestServerInitializer.cs deleted file mode 100644 index 845efb115..000000000 --- a/Projects/Server.Tests/Fixtures/TestServerInitializer.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using System.Threading; -using Server.Items; -using Server.Tests.Maps; - -namespace Server.Tests; - -/// -/// Shared server initialization logic for test fixtures. -/// Ensures the server is only initialized once across all test collections. -/// -internal static class TestServerInitializer -{ - private const string DefaultDataDirectory = @"C:\Ultima Online Classic"; - private static bool _initialized; - private static readonly Lock _lock = new(); - - /// - /// True if TileData was successfully loaded from client files. - /// - public static bool TileDataLoaded { get; private set; } - - /// - /// A tile ID known to have the Surface flag. 0 if not found. - /// - public static ushort SurfaceTileId { get; private set; } - - /// - /// A tile ID known to have the Impassable flag. 0 if not found. - /// - public static ushort ImpassableTileId { get; private set; } - - /// - /// A tile ID known to have the Wet flag (water). 0 if not found. - /// - public static ushort WetTileId { get; private set; } - - /// - /// A tile ID known to have both Surface and Impassable flags (tables, furniture). 0 if not found. - /// - public static ushort SurfaceImpassableTileId { get; private set; } - - /// - /// Initializes the test server. Safe to call multiple times - only initializes once. - /// - /// If true, attempts to load TileData from client files. - public static void Initialize(bool loadTileData = false) - { - lock (_lock) - { - if (_initialized) - { - // Already initialized, but check if we need to load TileData now - if (loadTileData && !TileDataLoaded && TileData.MaxItemValue == 0) - { - TryLoadTileData(); - DetectTileIds(); - } - return; - } - - Core.ApplicationAssembly = Assembly.GetExecutingAssembly(); - - // Load Configurations - ServerConfiguration.Load(true); - - // Try to load TileData if requested - if (loadTileData) - { - TryLoadTileData(); - } - - // Load an empty assembly list into the resolver - ServerConfiguration.AssemblyDirectories.Add(Core.BaseDirectory); - AssemblyHandler.LoadAssemblies(["Server.dll"]); - - Core.LoopContext = new EventLoopContext(); - Core.Expansion = Expansion.EJ; - - // Seed the loop clock as Main.cs does before the Configure sweep; otherwise Core.Now is - // DateTime.MinValue for the whole test host. - Core._now = DateTime.UtcNow; - - // Timer wheel must exist before NetState.Configure(), which schedules a recurring - // sweep via Timer.DelayCall (matches production ordering in Main.cs: Timer.Init runs - // before AssemblyHandler.Invoke("Configure")). - Timer.Init(0); - - // Configure networking (initializes RingSocketManager for tests) - Server.Network.NetState.Configure(); - - // Configure / Initialize - TestMapDefinitions.ConfigureTestMapDefinitions(); - - // Configure the world - World.Configure(); - - // Load the world - World.Load(); - - World.ExitSerializationThreads(); - - // Detect tile IDs if TileData was loaded - if (loadTileData) - { - DetectTileIds(); - } - - DecayScheduler.Configure(); - - _initialized = true; - } - } - - private static void TryLoadTileData() - { - var dataDir = GetDataDirectory(); - if (string.IsNullOrEmpty(dataDir) || !Directory.Exists(dataDir)) - { - return; - } - - ServerConfiguration.DataDirectories.Add(dataDir); - - var tileDataPath = Core.FindDataFile("tiledata.mul", false); - if (File.Exists(tileDataPath)) - { - TileData.Load(); - } - } - - private static void DetectTileIds() - { - if (TileData.MaxItemValue == 0) - { - return; - } - - SurfaceTileId = FindTileWithFlag(TileFlag.Surface); - ImpassableTileId = FindTileWithFlag(TileFlag.Impassable); - WetTileId = FindTileWithFlag(TileFlag.Wet); - SurfaceImpassableTileId = FindTileWithFlags(TileFlag.Surface | TileFlag.Impassable); - TileDataLoaded = SurfaceTileId > 0 && ImpassableTileId > 0 && WetTileId > 0; - } - - private static ushort FindTileWithFlag(TileFlag flag) - { - for (ushort i = 1; i <= TileData.MaxItemValue && i < 0xFFFF; i++) - { - var data = TileData.ItemTable[i]; - if ((data.Flags & flag) != 0) - { - return i; - } - } - return 0; - } - - private static ushort FindTileWithFlags(TileFlag flags) - { - for (ushort i = 1; i <= TileData.MaxItemValue && i < 0xFFFF; i++) - { - var data = TileData.ItemTable[i]; - if ((data.Flags & flags) == flags) - { - return i; - } - } - return 0; - } - - private static string GetDataDirectory() - { - // Check environment variable first - var envDir = System.Environment.GetEnvironmentVariable("MODERNUO_CLIENT_PATH"); - if (!string.IsNullOrEmpty(envDir) && Directory.Exists(envDir)) - { - return envDir; - } - - // Fall back to default directory - if (Directory.Exists(DefaultDataDirectory)) - { - return DefaultDataDirectory; - } - - return null; - } -} diff --git a/Projects/Server.Tests/Helpers/GumpUtilities.cs b/Projects/Server.Tests/Helpers/GumpUtilities.cs index f5a910db8..94ebedca2 100644 --- a/Projects/Server.Tests/Helpers/GumpUtilities.cs +++ b/Projects/Server.Tests/Helpers/GumpUtilities.cs @@ -8,7 +8,7 @@ public static class GumpUtilities { public static Packet Compile(this Gump g, NetState ns = null) { - var disp = new DisplayGumpPacked(g); + IGumpWriter disp = new DisplayGumpPacked(g); if (!g.Draggable) { @@ -46,7 +46,7 @@ public static class GumpUtilities disp.Flush(); - return disp; + return (Packet)disp; } public static int Intern(this List strings, string value) diff --git a/Projects/Server.Tests/Helpers/PacketTestUtilities.cs b/Projects/Server.Tests/Helpers/PacketTestUtilities.cs index 5c6c793d4..afcbc6423 100644 --- a/Projects/Server.Tests/Helpers/PacketTestUtilities.cs +++ b/Projects/Server.Tests/Helpers/PacketTestUtilities.cs @@ -1,94 +1,16 @@ using System; -using System.Collections.Generic; -using System.Net; using System.Net.Sockets; -using System.Network; using Server.Network; -namespace Server.Tests.Network; - -public static class PacketTestUtilities +namespace Server.Tests.Network { - private static nint _testListener; - private static int _testPort; - private static readonly List _testSocketClients = []; - - public static Span Compile(this Packet p) => p.Compile(false, out var length).AsSpan(0, length); - - /// - /// Creates a NetState for unit testing. - /// Uses a real Socket and RingSocket with actual buffers. - /// Must be disposed after use (use 'using' statement). - /// - public static NetState CreateTestNetState() + public static class PacketTestUtilities { - NetState.Slice(); // Process disconnects/disposes + public static Span Compile(this Packet p) => + p.Compile(false, out var length).AsSpan(0, length); - for (var i = _testSocketClients.Count - 1; i >= 0; i--) - { - var sock = _testSocketClients[i]; - if (!sock.Connected) - { - sock.Dispose(); - _testSocketClients.RemoveAt(i); - } - } - - var ring = NetState.Ring; - - // Create a test listener if we don't have one (using the ring for RIO-compatible sockets) - if (_testListener == 0) - { - // Disable rate limiter for tests - we don't want connection attempts to be throttled - // NetState.DisableRateLimiter(); - - _testListener = ring.CreateListener("127.0.0.1", 0, 128); - if (_testListener == -1) - { - throw new InvalidOperationException("Failed to create test listener"); - } - - _testPort = SocketHelper.GetLocalEndPoint(_testListener)?.Port ?? 0; - if (_testPort == 0) - { - throw new InvalidOperationException("Failed to get test listener port"); - } - } - - // Queue an accept operation - ring.PrepareAccept(_testListener, 0, 0, IORingUserData.EncodeAccept()); - ring.Submit(); - - Core._now = DateTime.UtcNow; - - // Create a client socket and connect to trigger the accept - var testSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - testSocket.Connect(IPAddress.Loopback, _testPort); - _testSocketClients.Add(testSocket); - - // Slice until we have a new NetState instance added - // AcceptEx is asynchronous, so we may need to wait/retry - const int maxRetries = 100; - for (var i = 0; i < maxRetries; i++) - { - NetState.Slice(); - - // Get the latest instance connected. - foreach (var ns in NetState.Instances) - { - if (ns.ConnectedOn == Core._now) - { - return ns; - } - } - - // Wait a bit for AcceptEx to complete - if (i < maxRetries - 1) - { - System.Threading.Thread.Sleep(1); - } - } - - throw new Exception("Failed to slice for test NetState instance after retries"); + public static NetState CreateTestNetState() => new( + new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) + ); } } diff --git a/Projects/Server.Tests/Helpers/PredictableRandom.cs b/Projects/Server.Tests/Helpers/PredictableRandom.cs deleted file mode 100644 index 1f270520a..000000000 --- a/Projects/Server.Tests/Helpers/PredictableRandom.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using Server.Random; - -namespace Server.Tests; - -/// -/// Replaces with a that returns -/// a fixed value, making skill checks and other RNG-dependent code deterministic in tests. -/// Restores the original generator on . -/// Usage: -/// -/// using var rng = new PredictableRandom(10); // Utility.Random(21) returns 10 -/// -/// -public sealed class PredictableRandom : IDisposable -{ - private readonly System.Random _original; - - public PredictableRandom(int fixedValue) - { - _original = BuiltInRng.Generator; - BuiltInRng.Generator = new FixedRandom(fixedValue); - } - - public void Dispose() - { - BuiltInRng.Generator = _original; - } - - private sealed class FixedRandom(int value) : System.Random - { - public override int Next() => value; - public override int Next(int maxValue) => Math.Clamp(value, 0, maxValue - 1); - public override int Next(int minValue, int maxValue) => Math.Clamp(value + minValue, minValue, maxValue - 1); - public override long NextInt64() => value; - public override long NextInt64(long maxValue) => Math.Clamp(value, 0, maxValue - 1); - public override long NextInt64(long minValue, long maxValue) => Math.Clamp(value + minValue, minValue, maxValue - 1); - public override double NextDouble() => Math.Clamp(value / 20.0, 0.0, 1.0); - - public override void NextBytes(byte[] buffer) => Array.Fill(buffer, (byte)Math.Clamp(value, 0, 255)); - public override void NextBytes(Span buffer) => buffer.Fill((byte)Math.Clamp(value, 0, 255)); - } -} diff --git a/Projects/Server.Tests/SequentialTestCollectionDefinition.cs b/Projects/Server.Tests/SequentialTestCollectionDefinition.cs new file mode 100644 index 000000000..2db7f513c --- /dev/null +++ b/Projects/Server.Tests/SequentialTestCollectionDefinition.cs @@ -0,0 +1,9 @@ +using Xunit; + +namespace Server.Tests +{ + [CollectionDefinition("Sequential Tests", DisableParallelization = true)] + public class SequentialTestCollectionDefinition + { + } +} diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index 4bae245cb..e5d6a179b 100644 --- a/Projects/Server.Tests/Server.Tests.csproj +++ b/Projects/Server.Tests/Server.Tests.csproj @@ -5,10 +5,9 @@ Server.Tests - - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -16,13 +15,6 @@ - - - - PreserveNewest - ioring.dll - - diff --git a/Projects/Server.Tests/Tests/Buffers/RawInterpolatedStringHandlerTests.cs b/Projects/Server.Tests/Tests/Buffers/RawInterpolatedStringHandlerTests.cs deleted file mode 100644 index d4777b758..000000000 --- a/Projects/Server.Tests/Tests/Buffers/RawInterpolatedStringHandlerTests.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using Server.Buffers; -using Xunit; - -namespace Server.Tests.Buffers; - -[Collection("Sequential Server Tests")] -public class RawInterpolatedStringHandlerTests -{ - [Fact] - public void TestLowercaseFormatString() - { - var name = "Hello WORLD"; - var handler = new RawInterpolatedStringHandler(0, 1); - handler.AppendFormatted(name, format: "L"); - Assert.Equal("hello world", handler.Text.ToString()); - handler.Clear(); - } - - [Fact] - public void TestLowercaseFormatEnum() - { - var handler = new RawInterpolatedStringHandler(0, 1); - handler.AppendFormatted(DayOfWeek.Wednesday, format: "L"); - Assert.Equal("wednesday", handler.Text.ToString()); - handler.Clear(); - } - - [Fact] - public void TestLowercaseFormatInt() - { - // Numerics have no uppercase chars; :L should be a no-op - var handler = new RawInterpolatedStringHandler(0, 1); - handler.AppendFormatted(42, format: "L"); - Assert.Equal("42", handler.Text.ToString()); - handler.Clear(); - } - - [Fact] - public void TestLowercaseFormatSpan() - { - var span = "MIXED Case TEXT".AsSpan(); - var handler = new RawInterpolatedStringHandler(0, 1); - handler.AppendFormatted(span, alignment: 0, format: "L"); - Assert.Equal("mixed case text", handler.Text.ToString()); - handler.Clear(); - } - - [Fact] - public void TestLowercaseFormatWithAlignment() - { - // Right-aligned: "Gold" in width 8 with :L -> " gold" - var handler = new RawInterpolatedStringHandler(0, 1); - handler.AppendFormatted("Gold", alignment: 8, format: "L"); - Assert.Equal(" gold", handler.Text.ToString()); - handler.Clear(); - } - - [Fact] - public void TestNoFormatPreservesCase() - { - var handler = new RawInterpolatedStringHandler(0, 1); - handler.AppendFormatted("Hello WORLD"); - Assert.Equal("Hello WORLD", handler.Text.ToString()); - handler.Clear(); - } - - [Fact] - public void TestUnicodeLowercase() - { - // ToLowerInvariant on Greek letter - var handler = new RawInterpolatedStringHandler(0, 1); - handler.AppendFormatted("ΑΒΓ", format: "L"); - Assert.Equal("αβγ", handler.Text.ToString()); - handler.Clear(); - } - - [Fact] - public void TestLowercaseFormatSurrogatePairAtChunkBoundary() - { - // The chunked lowercase path uses a 256-char stackalloc temp buffer. Place a - // supplementary-plane code point (U+10400 DESERET CAPITAL LONG I, encoded as - // surrogate pair "𐐀") so its high half lands at offset 255 and its - // low half at offset 256 — straddling the chunk boundary. Without the - // surrogate-aware boundary trim, ToLowerInvariant would see two lone surrogates - // and pass them through unchanged, leaving the capital code point intact. - // With the trim, the chunk shrinks to 255 chars and the pair stays together - // in the next chunk, lowercasing correctly to U+10428 ("𐐨"). - var input = new string('a', 255) + "𐐀" + new string('b', 10); - var handler = new RawInterpolatedStringHandler(0, 1); - handler.AppendFormatted(input, format: "L"); - var expected = new string('a', 255) + "𐐨" + new string('b', 10); - Assert.Equal(expected, handler.Text.ToString()); - handler.Clear(); - } -} diff --git a/Projects/Server.Tests/Tests/Buffers/STArrayPoolTests.cs b/Projects/Server.Tests/Tests/Buffers/STArrayPoolTests.cs index 2695b8e7c..3a51999c0 100644 --- a/Projects/Server.Tests/Tests/Buffers/STArrayPoolTests.cs +++ b/Projects/Server.Tests/Tests/Buffers/STArrayPoolTests.cs @@ -2,9 +2,9 @@ using System; using Server.Buffers; using Xunit; -namespace Server.Tests.Buffers; +namespace Server.Tests.Tests.Buffers; -[Collection("Sequential Server Tests")] +[Collection("Sequential Tests")] public class STArrayPoolTests { [Theory] diff --git a/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs b/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs deleted file mode 100644 index 8d6e24e6d..000000000 --- a/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs +++ /dev/null @@ -1,603 +0,0 @@ -using System; -using System.Buffers; -using System.IO; -using Xunit; - -namespace Server.Tests.Buffers; - -public class SpanReaderTests -{ - [Fact] - public void TestReadByte() - { - ReadOnlySpan buffer = [0x12, 0x34]; - var reader = new SpanReader(buffer); - - Assert.Equal(0x12, reader.ReadByte()); - Assert.Equal(0x34, reader.ReadByte()); - Assert.Equal(2, reader.Position); - } - - [Fact] - public void TestReadByteAtEnd() - { - Assert.Throws( - () => - { - ReadOnlySpan buffer = [0x12]; - var reader = new SpanReader(buffer); - reader.ReadByte(); - reader.ReadByte(); - } - ); - } - - [Fact] - public void TestReadBoolean() - { - ReadOnlySpan buffer = [0, 1, 2, 255]; - var reader = new SpanReader(buffer); - - Assert.False(reader.ReadBoolean()); - Assert.True(reader.ReadBoolean()); - Assert.True(reader.ReadBoolean()); - Assert.True(reader.ReadBoolean()); - Assert.Equal(4, reader.Position); - } - - [Fact] - public void TestReadSByte() - { - ReadOnlySpan buffer = [0xFF, 0x7F]; - var reader = new SpanReader(buffer); - - Assert.Equal(-1, reader.ReadSByte()); - Assert.Equal(127, reader.ReadSByte()); - Assert.Equal(2, reader.Position); - } - - [Fact] - public void TestReadInt16BigEndian() - { - ReadOnlySpan buffer = [0x12, 0x34]; - var reader = new SpanReader(buffer); - - Assert.Equal(0x1234, reader.ReadInt16()); - Assert.Equal(2, reader.Position); - } - - [Fact] - public void TestReadInt16LittleEndian() - { - ReadOnlySpan buffer = [0x34, 0x12]; - var reader = new SpanReader(buffer); - - Assert.Equal(0x1234, reader.ReadInt16LE()); - Assert.Equal(2, reader.Position); - } - - [Fact] - public void TestReadInt16AtEnd() - { - Assert.Throws( - () => - { - ReadOnlySpan buffer = [0x12]; - var reader = new SpanReader(buffer); - reader.ReadInt16(); - } - ); - } - - [Fact] - public void TestReadUInt16BigEndian() - { - ReadOnlySpan buffer = [0x12, 0x34]; - var reader = new SpanReader(buffer); - - Assert.Equal((ushort)0x1234, reader.ReadUInt16()); - Assert.Equal(2, reader.Position); - } - - [Fact] - public void TestReadUInt16LittleEndian() - { - ReadOnlySpan buffer = [0x34, 0x12]; - var reader = new SpanReader(buffer); - - Assert.Equal((ushort)0x1234, reader.ReadUInt16LE()); - Assert.Equal(2, reader.Position); - } - - [Fact] - public void TestReadInt32BigEndian() - { - ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78]; - var reader = new SpanReader(buffer); - - Assert.Equal(0x12345678, reader.ReadInt32()); - Assert.Equal(4, reader.Position); - } - - [Fact] - public void TestReadInt32AtEnd() - { - Assert.Throws( - () => - { - ReadOnlySpan buffer = [0x12, 0x34, 0x56]; - var reader = new SpanReader(buffer); - reader.ReadInt32(); - } - ); - } - - [Fact] - public void TestReadUInt32BigEndian() - { - ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78]; - var reader = new SpanReader(buffer); - - Assert.Equal(0x12345678u, reader.ReadUInt32()); - Assert.Equal(4, reader.Position); - } - - [Fact] - public void TestReadUInt32LittleEndian() - { - ReadOnlySpan buffer = [0x78, 0x56, 0x34, 0x12]; - var reader = new SpanReader(buffer); - - Assert.Equal(0x12345678u, reader.ReadUInt32LE()); - Assert.Equal(4, reader.Position); - } - - [Fact] - public void TestReadInt64BigEndian() - { - ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; - var reader = new SpanReader(buffer); - - Assert.Equal(0x123456789ABCDEF0L, reader.ReadInt64()); - Assert.Equal(8, reader.Position); - } - - [Fact] - public void TestReadInt64AtEnd() - { - Assert.Throws( - () => - { - ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE]; - var reader = new SpanReader(buffer); - reader.ReadInt64(); - } - ); - } - - [Fact] - public void TestReadUInt64BigEndian() - { - ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; - var reader = new SpanReader(buffer); - - Assert.Equal(0x123456789ABCDEF0UL, reader.ReadUInt64()); - Assert.Equal(8, reader.Position); - } - - [Fact] - public void TestReadAsciiString() - { - var buffer = "Hello"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadAscii(); - - Assert.Equal("Hello", result); - Assert.Equal(5, reader.Position); - } - - [Fact] - public void TestReadAsciiStringWithNull() - { - var buffer = "Hel\0o"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadAscii(); - - Assert.Equal("Hel", result); - Assert.Equal(4, reader.Position); - } - - [Fact] - public void TestReadAsciiStringFixedLength() - { - var buffer = "Hello\0\0\0\0\0"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadAscii(10); - - Assert.Equal("Hello", result); - Assert.Equal(10, reader.Position); - } - - [Fact] - public void TestReadAsciiStringFixedLengthTooLarge() - { - Assert.Throws( - () => - { - var buffer = "Hel"u8; - var reader = new SpanReader(buffer); - reader.ReadAscii(10); - } - ); - } - - [Fact] - public void TestReadAsciiSafe() - { - ReadOnlySpan buffer = [(byte)'H', (byte)'e', 0xFF, (byte)'l', (byte)'o']; - var reader = new SpanReader(buffer); - - var result = reader.ReadAsciiSafe(); - - Assert.Equal("He?lo", result); - Assert.Equal(5, reader.Position); - } - - [Fact] - public void TestReadUTF8String() - { - var buffer = "Hello"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadUTF8(); - - Assert.Equal("Hello", result); - Assert.Equal(5, reader.Position); - } - - [Fact] - public void TestReadUTF8StringWithNull() - { - var buffer = "Hi\0Bye"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadUTF8(); - - Assert.Equal("Hi", result); - Assert.Equal(3, reader.Position); - } - - [Fact] - public void TestReadLittleUniString() - { - var buffer = "H\0i\0"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadLittleUni(); - - Assert.Equal("Hi", result); - Assert.Equal(4, reader.Position); - } - - [Fact] - public void TestReadLittleUniStringWithNull() - { - var buffer = "H\0i\0\0\0X\0"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadLittleUni(); - - Assert.Equal("Hi", result); - // Position 6: 4 bytes for "Hi" + 2 bytes for UTF-16 null terminator - Assert.Equal(6, reader.Position); - } - - [Fact] - public void TestReadLittleUniStringFixedLength() - { - var buffer = "H\0i\0\0\0\0\0"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadLittleUni(4); - - Assert.Equal("Hi", result); - Assert.Equal(8, reader.Position); - } - - [Fact] - public void TestReadLittleUniSafe() - { - // Test with C1 control code (0x85 = NEL) which should be filtered - ReadOnlySpan buffer = [(byte)'H', 0, 0x85, 0x00, (byte)'i', 0]; - var reader = new SpanReader(buffer); - - var result = reader.ReadLittleUniSafe(); - - // C1 control (0x0085) removed - client renders nothing for invalid chars - Assert.Equal("Hi", result); - Assert.Equal(6, reader.Position); - } - - [Fact] - public void TestReadBigUniString() - { - var buffer = "\0H\0i"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadBigUni(); - - Assert.Equal("Hi", result); - Assert.Equal(4, reader.Position); - } - - [Fact] - public void TestReadBigUniStringWithNull() - { - var buffer = "\0H\0i\0\0\0X"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadBigUni(); - - Assert.Equal("Hi", result); - // Position 6: 4 bytes for "Hi" + 2 bytes for UTF-16 null terminator - Assert.Equal(6, reader.Position); - } - - [Fact] - public void TestReadBigUniStringFixedLength() - { - var buffer = "\0H\0i\0\0\0\0"u8; - var reader = new SpanReader(buffer); - - var result = reader.ReadBigUni(4); - - Assert.Equal("Hi", result); - Assert.Equal(8, reader.Position); - } - - [Fact] - public void TestReadBigUniSafe() - { - // Test with C1 control code (0x0085 = NEL) which should be filtered - ReadOnlySpan buffer = [0, (byte)'H', 0x00, 0x85, 0, (byte)'i']; - var reader = new SpanReader(buffer); - - var result = reader.ReadBigUniSafe(); - - // C1 control (0x0085) removed - client renders nothing for invalid chars - Assert.Equal("Hi", result); - Assert.Equal(6, reader.Position); - } - - [Fact] - public void TestSeekBegin() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; - var reader = new SpanReader(buffer); - - reader.ReadByte(); - reader.ReadByte(); - - var pos = reader.Seek(0, SeekOrigin.Begin); - - Assert.Equal(0, pos); - Assert.Equal(0, reader.Position); - Assert.Equal(0x01, reader.ReadByte()); - } - - [Fact] - public void TestSeekCurrent() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; - var reader = new SpanReader(buffer); - - reader.ReadByte(); - var pos = reader.Seek(2, SeekOrigin.Current); - - Assert.Equal(3, pos); - Assert.Equal(3, reader.Position); - Assert.Equal(0x04, reader.ReadByte()); - } - - [Fact] - public void TestSeekEnd() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; - var reader = new SpanReader(buffer); - - var pos = reader.Seek(-2, SeekOrigin.End); - - Assert.Equal(3, pos); - Assert.Equal(3, reader.Position); - Assert.Equal(0x04, reader.ReadByte()); - } - - [Fact] - public void TestSeekNegativeThrows() - { - Assert.Throws( - () => - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03]; - var reader = new SpanReader(buffer); - reader.Seek(-1, SeekOrigin.Begin); - } - ); - } - - [Fact] - public void TestSeekBeyondEndThrows() - { - Assert.Throws( - () => - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03]; - var reader = new SpanReader(buffer); - reader.Seek(10, SeekOrigin.Begin); - } - ); - } - - [Fact] - public void TestRead() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; - var reader = new SpanReader(buffer); - - Span dest = stackalloc byte[3]; - var bytesRead = reader.Read(dest); - - Assert.Equal(3, bytesRead); - Assert.Equal(3, reader.Position); - AssertThat.Equal(dest, [0x01, 0x02, 0x03]); - } - - [Fact] - public void TestReadPartial() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03]; - var reader = new SpanReader(buffer); - - Span dest = stackalloc byte[5]; - var bytesRead = reader.Read(dest); - - Assert.Equal(3, bytesRead); - Assert.Equal(3, reader.Position); - AssertThat.Equal(dest[..3], [0x01, 0x02, 0x03]); - } - - [Fact] - public void TestReadEmpty() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03]; - var reader = new SpanReader(buffer); - - Span dest = []; - var bytesRead = reader.Read(dest); - - Assert.Equal(0, bytesRead); - Assert.Equal(0, reader.Position); - } - - [Fact] - public void TestReadAtEnd() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03]; - var reader = new SpanReader(buffer); - - reader.Seek(3, SeekOrigin.Begin); - - Span dest = stackalloc byte[5]; - var bytesRead = reader.Read(dest); - - Assert.Equal(0, bytesRead); - Assert.Equal(3, reader.Position); - } - - [Fact] - public void TestLength() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; - var reader = new SpanReader(buffer); - - Assert.Equal(5, reader.Length); - } - - [Fact] - public void TestPosition() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03]; - var reader = new SpanReader(buffer); - - Assert.Equal(0, reader.Position); - - reader.ReadByte(); - Assert.Equal(1, reader.Position); - - reader.ReadUInt16(); - Assert.Equal(3, reader.Position); - } - - [Fact] - public void TestRemaining() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; - var reader = new SpanReader(buffer); - - Assert.Equal(5, reader.Remaining); - - reader.ReadByte(); - Assert.Equal(4, reader.Remaining); - - reader.ReadUInt16(); - Assert.Equal(2, reader.Remaining); - - reader.ReadUInt16(); - Assert.Equal(0, reader.Remaining); - } - - [Fact] - public void TestBuffer() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03]; - var reader = new SpanReader(buffer); - - var bufferProperty = reader.Buffer; - - Assert.Equal(3, bufferProperty.Length); - AssertThat.Equal(bufferProperty, buffer); - } - - [Fact] - public void TestReadStringEmptyFixedLength() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03]; - var reader = new SpanReader(buffer); - - var result = reader.ReadAscii(0); - - Assert.Equal("", result); - Assert.Equal(0, reader.Position); - } - - [Fact] - public void TestReadMultipleStringsWithNullTerminators() - { - var buffer = "AB\0CD\0EF"u8; - var reader = new SpanReader(buffer); - - Assert.Equal("AB", reader.ReadAscii()); - Assert.Equal("CD", reader.ReadAscii()); - Assert.Equal("EF", reader.ReadAscii()); - Assert.Equal(8, reader.Position); - } - - [Fact] - public void TestReadLittleUniOddByteCount() - { - // If buffer has odd number of bytes, the last byte should be ignored - ReadOnlySpan buffer = [(byte)'H', 0, (byte)'i', 0, 0xFF]; - var reader = new SpanReader(buffer); - - var result = reader.ReadLittleUni(); - - Assert.Equal("Hi", result); - Assert.Equal(4, reader.Position); - } - - [Fact] - public void TestReadBigUniOddByteCount() - { - // If buffer has odd number of bytes, the last byte should be ignored - ReadOnlySpan buffer = [0, (byte)'H', 0, (byte)'i', 0xFF]; - var reader = new SpanReader(buffer); - - var result = reader.ReadBigUni(); - - Assert.Equal("Hi", result); - Assert.Equal(4, reader.Position); - } -} diff --git a/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs b/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs index 370024370..0aeffe574 100644 --- a/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs +++ b/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs @@ -1,591 +1,63 @@ using System; using System.Buffers; -using System.IO; using Xunit; -namespace Server.Tests.Buffers; - -public class SpanWriterTests +namespace Server.Tests { - [Fact] - public unsafe void TestSpanWriterResizes() + public class SpanWriterTests { - Span smallStack = stackalloc byte[8]; - var writer = new SpanWriter(smallStack, true); - writer.Write(0x1024L); - writer.Write(0x1024L); - - var span = writer.RawBuffer; - fixed (byte* spanPtr = span) + [Fact] + public unsafe void TestSpanWriterResizes() { - fixed (byte* stackPtr = smallStack) + Span smallStack = stackalloc byte[8]; + using var writer = new SpanWriter(smallStack, true); + writer.Write(0x1024L); + writer.Write(0x1024L); + + var span = writer.RawBuffer; + fixed (byte* spanPtr = span) { - Assert.True(spanPtr != stackPtr); + fixed (byte* stackPtr = smallStack) + { + Assert.True(spanPtr != stackPtr); + } } + + Assert.True(span.Length > smallStack.Length); } - Assert.True(span.Length > smallStack.Length); - writer.Dispose(); - } - - [Fact] - public unsafe void TestSpanWriterOnlyStackAlloc() - { - Span smallStack = stackalloc byte[8]; - var writer = new SpanWriter(smallStack, true); - writer.Write(0x1024L); - - var span = writer.RawBuffer; - fixed (byte* spanPtr = span) + [Fact] + public unsafe void TestSpanWriterOnlyStackAlloc() { - fixed (byte* stackPtr = smallStack) + Span smallStack = stackalloc byte[8]; + using var writer = new SpanWriter(smallStack, true); + writer.Write(0x1024L); + + var span = writer.RawBuffer; + fixed (byte* spanPtr = span) { - Assert.True(spanPtr == stackPtr); + fixed (byte* stackPtr = smallStack) + { + Assert.True(spanPtr == stackPtr); + } } + + Assert.True(span.Length == smallStack.Length); + AssertThat.Equal(smallStack, stackalloc byte[] { 0, 0, 0, 0, 0, 0, 0x10, 0x24 }); } - Assert.Equal(8, span.Length); - AssertThat.Equal(smallStack, [0, 0, 0, 0, 0, 0, 0x10, 0x24]); - } - - [Fact] - public void TestSpanWriterNoResizeThrows() - { - Assert.Throws( - () => - { - Span smallStack = stackalloc byte[8]; - var writer = new SpanWriter(smallStack); - writer.Write(0x1024L); - writer.Write(0x1024L); - } - ); - } - - [Fact] - public void TestWriteBool() - { - Span buffer = stackalloc byte[2]; - var writer = new SpanWriter(buffer); - - writer.Write(true); - writer.Write(false); - - Assert.Equal(2, writer.Position); - AssertThat.Equal(writer.Span, [1, 0]); - } - - [Fact] - public void TestWriteByte() - { - Span buffer = stackalloc byte[2]; - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x12); - writer.Write((byte)0x34); - - Assert.Equal(2, writer.Position); - AssertThat.Equal(writer.Span, [0x12, 0x34]); - } - - [Fact] - public void TestWriteSByte() - { - Span buffer = stackalloc byte[2]; - var writer = new SpanWriter(buffer); - - writer.Write((sbyte)-1); - writer.Write((sbyte)127); - - Assert.Equal(2, writer.Position); - AssertThat.Equal(writer.Span, [0xFF, 0x7F]); - } - - [Fact] - public void TestWriteInt16BigEndian() - { - Span buffer = stackalloc byte[2]; - var writer = new SpanWriter(buffer); - - writer.Write((short)0x1234); - - Assert.Equal(2, writer.Position); - AssertThat.Equal(writer.Span, [0x12, 0x34]); - } - - [Fact] - public void TestWriteInt16LittleEndian() - { - Span buffer = stackalloc byte[2]; - var writer = new SpanWriter(buffer); - - writer.WriteLE((short)0x1234); - - Assert.Equal(2, writer.Position); - AssertThat.Equal(writer.Span, [0x34, 0x12]); - } - - [Fact] - public void TestWriteUInt16BigEndian() - { - Span buffer = stackalloc byte[2]; - var writer = new SpanWriter(buffer); - - writer.Write((ushort)0x1234); - - Assert.Equal(2, writer.Position); - AssertThat.Equal(writer.Span, [0x12, 0x34]); - } - - [Fact] - public void TestWriteUInt16LittleEndian() - { - Span buffer = stackalloc byte[2]; - var writer = new SpanWriter(buffer); - - writer.WriteLE((ushort)0x1234); - - Assert.Equal(2, writer.Position); - AssertThat.Equal(writer.Span, [0x34, 0x12]); - } - - [Fact] - public void TestWriteInt32BigEndian() - { - Span buffer = stackalloc byte[4]; - var writer = new SpanWriter(buffer); - - writer.Write(0x12345678); - - Assert.Equal(4, writer.Position); - AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78]); - } - - [Fact] - public void TestWriteInt32LittleEndian() - { - Span buffer = stackalloc byte[4]; - var writer = new SpanWriter(buffer); - - writer.WriteLE(0x12345678); - - Assert.Equal(4, writer.Position); - AssertThat.Equal(writer.Span, [0x78, 0x56, 0x34, 0x12]); - } - - [Fact] - public void TestWriteUInt32BigEndian() - { - Span buffer = stackalloc byte[4]; - var writer = new SpanWriter(buffer); - - writer.Write(0x12345678u); - - Assert.Equal(4, writer.Position); - AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78]); - } - - [Fact] - public void TestWriteUInt32LittleEndian() - { - Span buffer = stackalloc byte[4]; - var writer = new SpanWriter(buffer); - - writer.WriteLE(0x12345678u); - - Assert.Equal(4, writer.Position); - AssertThat.Equal(writer.Span, [0x78, 0x56, 0x34, 0x12]); - } - - [Fact] - public void TestWriteInt64BigEndian() - { - Span buffer = stackalloc byte[8]; - var writer = new SpanWriter(buffer); - - writer.Write(0x123456789ABCDEF0L); - - Assert.Equal(8, writer.Position); - AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]); - } - - [Fact] - public void TestWriteUInt64BigEndian() - { - Span buffer = stackalloc byte[8]; - var writer = new SpanWriter(buffer); - - writer.Write(0x123456789ABCDEF0UL); - - Assert.Equal(8, writer.Position); - AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]); - } - - [Fact] - public void TestWriteSpan() - { - Span buffer = stackalloc byte[6]; - var writer = new SpanWriter(buffer); - - ReadOnlySpan data = [0x01, 0x02, 0x03]; - writer.Write(data); - writer.Write(data); - - Assert.Equal(6, writer.Position); - AssertThat.Equal(writer.Span, [0x01, 0x02, 0x03, 0x01, 0x02, 0x03]); - } - - [Fact] - public void TestWriteAsciiChar() - { - Span buffer = stackalloc byte[2]; - var writer = new SpanWriter(buffer); - - writer.WriteAscii('A'); - writer.WriteAscii('B'); - - Assert.Equal(2, writer.Position); - AssertThat.Equal(writer.Span, "AB"u8); - } - - [Fact] - public void TestWriteAsciiString() - { - Span buffer = stackalloc byte[5]; - var writer = new SpanWriter(buffer); - - writer.WriteAscii("Hello"); - - Assert.Equal(5, writer.Position); - AssertThat.Equal(writer.Span, "Hello"u8); - } - - [Fact] - public void TestWriteAsciiStringNull() - { - Span buffer = stackalloc byte[6]; - var writer = new SpanWriter(buffer); - - writer.WriteAsciiNull("Hello"); - - Assert.Equal(6, writer.Position); - AssertThat.Equal(writer.Span, "Hello\0"u8); - } - - [Fact] - public void TestWriteAsciiStringFixedLength() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.WriteAscii("Hello", 10); - - Assert.Equal(10, writer.Position); - AssertThat.Equal(writer.Span, "Hello\0\0\0\0\0"u8); - } - - [Fact] - public void TestWriteUTF8String() - { - Span buffer = stackalloc byte[5]; - var writer = new SpanWriter(buffer); - - writer.WriteUTF8("Hello"); - - Assert.Equal(5, writer.Position); - AssertThat.Equal(writer.Span, "Hello"u8); - } - - [Fact] - public void TestWriteUTF8StringNull() - { - Span buffer = stackalloc byte[6]; - var writer = new SpanWriter(buffer); - - writer.WriteUTF8Null("Hello"); - - Assert.Equal(6, writer.Position); - AssertThat.Equal(writer.Span, "Hello\0"u8); - } - - [Fact] - public void TestWriteLittleUniString() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.WriteLittleUni("Hello"); - - Assert.Equal(10, writer.Position); - AssertThat.Equal(writer.Span, "H\0e\0l\0l\0o\0"u8); - } - - [Fact] - public void TestWriteLittleUniStringNull() - { - Span buffer = stackalloc byte[12]; - var writer = new SpanWriter(buffer); - - writer.WriteLittleUniNull("Hello"); - - Assert.Equal(12, writer.Position); - AssertThat.Equal(writer.Span, "H\0e\0l\0l\0o\0\0\0"u8); - } - - [Fact] - public void TestWriteBigUniString() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.WriteBigUni("Hello"); - - Assert.Equal(10, writer.Position); - AssertThat.Equal(writer.Span, "\0H\0e\0l\0l\0o"u8); - } - - [Fact] - public void TestWriteBigUniStringNull() - { - Span buffer = stackalloc byte[12]; - var writer = new SpanWriter(buffer); - - writer.WriteBigUniNull("Hello"); - - Assert.Equal(12, writer.Position); - AssertThat.Equal(writer.Span, "\0H\0e\0l\0l\0o\0\0"u8); - } - - [Fact] - public void TestClear() - { - Span buffer = stackalloc byte[10]; - buffer.Fill(0xFF); - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x01); - writer.Clear(5); - writer.Write((byte)0x02); - - Assert.Equal(7, writer.Position); - AssertThat.Equal(writer.Span, [0x01, 0, 0, 0, 0, 0, 0x02]); - } - - [Fact] - public void TestSeekBegin() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x01); - writer.Write((byte)0x02); - writer.Write((byte)0x03); - - var pos = writer.Seek(1, SeekOrigin.Begin); - - Assert.Equal(1, pos); - Assert.Equal(1, writer.Position); - Assert.Equal(3, writer.BytesWritten); - } - - [Fact] - public void TestSeekCurrent() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x01); - writer.Write((byte)0x02); - - var pos = writer.Seek(2, SeekOrigin.Current); - - Assert.Equal(4, pos); - Assert.Equal(4, writer.Position); - } - - [Fact] - public void TestSeekEnd() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x01); - writer.Write((byte)0x02); - writer.Write((byte)0x03); - - var pos = writer.Seek(-1, SeekOrigin.End); - - Assert.Equal(2, pos); - Assert.Equal(2, writer.Position); - } - - [Fact] - public void TestSeekNegativeThrows() - { - Assert.Throws( - () => - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - writer.Seek(-1, SeekOrigin.Begin); - } - ); - } - - [Fact] - public void TestSeekBeyondCapacityNoResizeThrows() - { - Assert.Throws( - () => - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - writer.Seek(20, SeekOrigin.Begin); - } - ); - } - - [Fact] - public void TestSeekBeyondCapacityWithResize() - { - var writer = new SpanWriter(10, true); - - var pos = writer.Seek(20, SeekOrigin.Begin); - - Assert.Equal(20, pos); - Assert.True(writer.Capacity >= 20); - writer.Dispose(); - } - - [Fact] - public void TestEnsureCapacityNoResize() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.EnsureCapacity(10); - Assert.Equal(10, writer.Capacity); - } - - [Fact] - public void TestEnsureCapacityNoResizeThrows() - { - Assert.Throws( - () => - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - writer.EnsureCapacity(20); - } - ); - } - - [Fact] - public void TestEnsureCapacityWithResize() - { - var writer = new SpanWriter(10, true); - - writer.EnsureCapacity(50); - - Assert.True(writer.Capacity >= 50); - writer.Dispose(); - } - - [Fact] - public void TestToSpanWithStackAlloc() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x01); - writer.Write((byte)0x02); - writer.Write((byte)0x03); - - using var owner = writer.ToSpan(); - - Assert.Equal(3, owner.Span.Length); - AssertThat.Equal(owner.Span, [0x01, 0x02, 0x03]); - } - - [Fact] - public void TestToSpanWithRentedBuffer() - { - var writer = new SpanWriter(10); - - writer.Write((byte)0x01); - writer.Write((byte)0x02); - writer.Write((byte)0x03); - - using var owner = writer.ToSpan(); - - Assert.Equal(3, owner.Span.Length); - AssertThat.Equal(owner.Span, [0x01, 0x02, 0x03]); - } - - [Fact] - public void TestToSpanEmpty() - { - var writer = new SpanWriter(10); - - using var owner = writer.ToSpan(); - - Assert.Equal(0, owner.Span.Length); - } - - [Fact] - public void TestBytesWritten() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - Assert.Equal(0, writer.BytesWritten); - - writer.Write((byte)0x01); - Assert.Equal(1, writer.BytesWritten); - - writer.Write((ushort)0x0203); - Assert.Equal(3, writer.BytesWritten); - - writer.Seek(1, SeekOrigin.Begin); - Assert.Equal(3, writer.BytesWritten); - - writer.Write((byte)0xFF); - Assert.Equal(3, writer.BytesWritten); - } - - [Fact] - public void TestCapacity() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - Assert.Equal(10, writer.Capacity); - } - - [Fact] - public void TestRawBuffer() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x01); - - var raw = writer.RawBuffer; - Assert.Equal(10, raw.Length); - Assert.Equal(0x01, raw[0]); - } - - [Fact] - public void TestSpan() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x01); - writer.Write((byte)0x02); - - var span = writer.Span; - Assert.Equal(2, span.Length); - AssertThat.Equal(span, [0x01, 0x02]); + [Fact] + public void TestSpanWriterNoResizeThrows() + { + Assert.Throws( + () => + { + Span smallStack = stackalloc byte[8]; + using var writer = new SpanWriter(smallStack); + writer.Write(0x1024L); + writer.Write(0x1024L); + } + ); + } } } diff --git a/Projects/Server.Tests/Tests/Buffers/ValueStringBuilderTests.cs b/Projects/Server.Tests/Tests/Buffers/ValueStringBuilderTests.cs index 7299a94c4..52a892772 100644 --- a/Projects/Server.Tests/Tests/Buffers/ValueStringBuilderTests.cs +++ b/Projects/Server.Tests/Tests/Buffers/ValueStringBuilderTests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Server.Tests.Buffers; -[Collection("Sequential Server Tests")] +[Collection("Sequential Tests")] public class ValueStringBuilderTests { [Theory] @@ -40,255 +40,10 @@ public class ValueStringBuilderTests public void TestAppendInterpolation(object value) { var sb = ValueStringBuilder.Create(); - sb.Append($"Hi, this is {value}."); + sb.Append( $"Hi, this is {value}."); sb.Append(" I am a string."); Assert.Equal($"Hi, this is {value}. I am a string.", sb.ToString()); sb.Dispose(); } - - // --- InterpolationHandler reconciliation tests --- - // These validate the copy-and-reconcile pattern: the handler receives a VALUE COPY of the builder, - // writes into the shared buffer, and Append() reconciles via `this = handler._builder`. - // Critical to detect if C# compiler codegen changes break this assumption. - - [Fact] - public void Interpolation_Stackalloc_NoGrow() - { - // Stackalloc buffer large enough — no Grow needed. - // Validates: copy's Span shares same stackalloc memory, _length is reconciled. - var sb = new ValueStringBuilder(stackalloc char[64]); - sb.Append($"Hello {42} world"); - - Assert.Equal("Hello 42 world", sb.ToString()); - Assert.Equal(14, sb.Length); - sb.Dispose(); - } - - [Fact] - public void Interpolation_Stackalloc_WithGrow() - { - // Tiny stackalloc forces Grow inside the handler's copy. - // Validates: after Grow, copy moves to pooled array; reconciliation updates - // _chars, _arrayToReturnToPool, and _length on the original. - var sb = new ValueStringBuilder(stackalloc char[4]); - sb.Append($"This string is much longer than 4 chars: {12345}"); - - var expected = "This string is much longer than 4 chars: 12345"; - Assert.Equal(expected, sb.ToString()); - Assert.Equal(expected.Length, sb.Length); - // After grow, capacity should have expanded beyond 4 - Assert.True(sb.Capacity >= 47); - sb.Dispose(); - } - - [Fact] - public void Interpolation_Stackalloc_PreExistingContent_NoGrow() - { - // Append plain text first, then interpolation. Buffer large enough. - // Validates: interpolation appends after existing content, doesn't overwrite. - var sb = new ValueStringBuilder(stackalloc char[64]); - sb.Append("prefix-"); - sb.Append($"value={99}"); - - Assert.Equal("prefix-value=99", sb.ToString()); - Assert.Equal(15, sb.Length); - sb.Dispose(); - } - - [Fact] - public void Interpolation_Stackalloc_PreExistingContent_WithGrow() - { - // Fill most of a small stackalloc, then interpolate enough to force Grow. - // Validates: existing content is preserved through the Grow, new content is appended. - var sb = new ValueStringBuilder(stackalloc char[16]); - sb.Append("0123456789"); // 10 chars, 6 remaining - sb.Append($"abcdefghij{42}"); // 12 chars, exceeds remaining — Grow - - Assert.Equal("0123456789abcdefghij42", sb.ToString()); - Assert.Equal(22, sb.Length); - Assert.True(sb.Capacity >= 22); - sb.Dispose(); - } - - [Fact] - public void Interpolation_Heap_NoGrow() - { - // Heap-allocated (Create) with sufficient capacity. - // Validates: handler works with pooled backing, _length is reconciled. - var sb = ValueStringBuilder.Create(64); - sb.Append($"Score: {100}, Name: {"Test"}"); - - Assert.Equal("Score: 100, Name: Test", sb.ToString()); - Assert.Equal(22, sb.Length); - sb.Dispose(); - } - - [Fact] - public void Interpolation_Heap_WithGrow() - { - // Small heap allocation forces Grow. - // Validates: after Grow, copy's new pooled array replaces original's - // (original's old array was returned to pool by copy's Grow). - var sb = ValueStringBuilder.Create(4); - sb.Append($"This exceeds the initial 4 char capacity: {67890}"); - - var expected = "This exceeds the initial 4 char capacity: 67890"; - Assert.Equal(expected, sb.ToString()); - Assert.Equal(expected.Length, sb.Length); - Assert.True(sb.Capacity >= expected.Length); - sb.Dispose(); - } - - [Fact] - public void Interpolation_Heap_PreExistingContent_WithGrow() - { - // Heap with pre-existing content, then Grow via interpolation. - // Validates: existing content preserved, pooled array properly transitioned. - var sb = ValueStringBuilder.Create(8); - sb.Append("ABCD"); - sb.Append($"EFGHIJKLMNOP{42}"); - - Assert.Equal("ABCDEFGHIJKLMNOP42", sb.ToString()); - Assert.Equal(18, sb.Length); - sb.Dispose(); - } - - [Fact] - public void Interpolation_Sequential_MultipleAppends() - { - // Multiple sequential Append($"...") calls. - // Validates: each reconciliation correctly advances _length, subsequent calls - // see the updated state from prior reconciliations. - var sb = new ValueStringBuilder(stackalloc char[128]); - sb.Append($"A={1}"); - sb.Append($" B={2}"); - sb.Append($" C={3}"); - sb.Append($" D={4}"); - - var expected = "A=1 B=2 C=3 D=4"; - Assert.Equal(expected, sb.ToString()); - Assert.Equal(expected.Length, sb.Length); - sb.Dispose(); - } - - [Fact] - public void Interpolation_Sequential_GrowOnSecondAppend() - { - // First Append fits, second triggers Grow. - // Validates: reconciliation after first Append leaves builder in a state - // that the second Append (with Grow) works correctly. - var sb = new ValueStringBuilder(stackalloc char[16]); - sb.Append($"Fits: {1}"); // 7 chars, fits in 16 - sb.Append($" - Now this is a much longer string that forces growth: {999}"); - - Assert.Equal("Fits: 1 - Now this is a much longer string that forces growth: 999", sb.ToString()); - sb.Dispose(); - } - - [Fact] - public void Interpolation_MixedAppendStyles() - { - // Mix plain Append with interpolated Append. - // Validates: reconciliation is compatible with non-interpolated Append calls. - var sb = new ValueStringBuilder(stackalloc char[64]); - sb.Append("plain-"); - sb.Append($"interp={42}-"); - sb.Append("plain2-"); - sb.Append($"interp2={99}"); - - Assert.Equal("plain-interp=42-plain2-interp2=99", sb.ToString()); - sb.Dispose(); - } - - [Fact] - public void Interpolation_EmptyInterpolation() - { - // Empty interpolation expression. - // Validates: handler with zero holes still reconciles correctly. - var sb = new ValueStringBuilder(stackalloc char[32]); - sb.Append("before"); - sb.Append($""); - sb.Append("after"); - - Assert.Equal("beforeafter", sb.ToString()); - sb.Dispose(); - } - - [Fact] - public void Interpolation_OnlyLiteral() - { - // Interpolation with no holes (just a literal). - // Validates: AppendLiteral-only path reconciles _length. - var sb = new ValueStringBuilder(stackalloc char[32]); - sb.Append($"just a literal"); - - Assert.Equal("just a literal", sb.ToString()); - sb.Dispose(); - } - - [Fact] - public void Interpolation_FormatSpecifiers() - { - // Format specifiers in interpolation holes. - // Validates: AppendFormatted with format string works through handler. - var sb = new ValueStringBuilder(stackalloc char[64]); - sb.Append($"pi={3.14159:F2}, hex={255:X4}, date={new System.DateTime(2025, 1, 15):yyyy-MM-dd}"); - - Assert.Equal("pi=3.14, hex=00FF, date=2025-01-15", sb.ToString()); - sb.Dispose(); - } - - [Fact] - public void Interpolation_SpanFormattableTypes() - { - // Various ISpanFormattable types. - // Validates: int, double, DateTime all format directly via TryFormat (no ToString allocation). - var sb = new ValueStringBuilder(stackalloc char[64]); - sb.Append($"{42}{3.14}{true}"); - - Assert.Equal("423.14True", sb.ToString()); - sb.Dispose(); - } - - [Fact] - public void Interpolation_NullString() - { - // Null string in interpolation hole. - // Validates: AppendFormatted(string?) handles null without crash. - string? name = null; - var sb = new ValueStringBuilder(stackalloc char[32]); - sb.Append($"Name: {name}!"); - - Assert.Equal("Name: !", sb.ToString()); - sb.Dispose(); - } - - [Fact] - public void Interpolation_Stackalloc_DisposeAfterGrow() - { - // After Grow moves stackalloc to pool, Dispose should return the pooled array. - // Validates: _arrayToReturnToPool is correctly reconciled so Dispose works. - var sb = new ValueStringBuilder(stackalloc char[4]); - sb.Append($"grow beyond stackalloc: {12345}"); - // If _arrayToReturnToPool wasn't reconciled, Dispose would either - // not return the pooled array (leak) or return null (no-op when it shouldn't be). - sb.Dispose(); - // No assertion needed — if _arrayToReturnToPool was wrong, pool corruption - // would surface as test failures elsewhere. The test passing = no crash. - } - - [Fact] - public void Interpolation_Heap_DoubleGrow() - { - // Force two consecutive Grows via two interpolated appends on a tiny buffer. - // Validates: reconciliation after first Grow leaves builder in valid state - // for the second Grow to succeed. - var sb = ValueStringBuilder.Create(4); - sb.Append($"First grow: {"ABCDEFGHIJ"}"); // forces first grow - sb.Append($"Second grow: {"KLMNOPQRSTUVWXYZ0123456789"}"); // forces second grow - - Assert.Equal("First grow: ABCDEFGHIJSecond grow: KLMNOPQRSTUVWXYZ0123456789", sb.ToString()); - sb.Dispose(); - } } diff --git a/Projects/Server.Tests/Tests/Client/ClientVersionTests.cs b/Projects/Server.Tests/Tests/Client/ClientVersionTests.cs index 471a5584c..6e0f3f039 100644 --- a/Projects/Server.Tests/Tests/Client/ClientVersionTests.cs +++ b/Projects/Server.Tests/Tests/Client/ClientVersionTests.cs @@ -2,7 +2,7 @@ using Xunit; namespace Server.Tests.Network; -[Collection("Sequential Server Tests")] +[Collection("Sequential Tests")] public class ClientVersionTests { [Theory] diff --git a/Projects/Server.Tests/Tests/Collections/OrderedSetTests.cs b/Projects/Server.Tests/Tests/Collections/OrderedSetTests.cs index 3a9a29da6..4d81d5598 100644 --- a/Projects/Server.Tests/Tests/Collections/OrderedSetTests.cs +++ b/Projects/Server.Tests/Tests/Collections/OrderedSetTests.cs @@ -20,7 +20,7 @@ public class OrderedSetTests Assert.True(set.Contains(4)); Assert.False(set.Contains(8)); - var index = 0; + int index = 0; foreach (var value in set) { Assert.Equal(expected[index++], value); diff --git a/Projects/Server.Tests/Tests/Console/ConsoleInputPumpTests.cs b/Projects/Server.Tests/Tests/Console/ConsoleInputPumpTests.cs deleted file mode 100644 index 50d015555..000000000 --- a/Projects/Server.Tests/Tests/Console/ConsoleInputPumpTests.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Server; -using Xunit; - -namespace Server.Tests; - -// These tests exercise the pump's cross-thread rendezvous directly, so they block on -// bounded-timeout Task.Wait() calls by design rather than using async/await. -#pragma warning disable xUnit1031 -public class ConsoleInputPumpTests -{ - private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(5); - - // Deterministic synchronization: spin until a real condition holds (the pump's - // rendezvous state or the reader's progress) instead of sleeping a fixed interval, - // which races on slow/loaded CI runners. - private static void WaitUntil(Func condition, string message) => - Assert.True(SpinWait.SpinUntil(condition, Timeout), message); - - // A TextReader whose ReadLine() blocks until a line is fed, and returns null after Complete(). - private sealed class BlockingTextReader : TextReader - { - private readonly BlockingCollection _lines = new(); - private int _readCalls; - - // Number of times ReadLine() has been entered — lets a test wait until the pump's - // reader loop is blocked waiting for the next line (e.g. after consuming a command). - public int ReadCallCount => Volatile.Read(ref _readCalls); - - public void Feed(string line) => _lines.Add(line); - public void Complete() => _lines.CompleteAdding(); - - public override string ReadLine() - { - Interlocked.Increment(ref _readCalls); - try - { - return _lines.Take(); - } - catch (InvalidOperationException) - { - return null; // CompleteAdding + empty => EOF - } - } - } - - [Fact] - public void Eof_ends_loop_without_spinning() - { - var pump = new ConsoleInputPump(new StringReader(""), _ => null); - - var ran = Task.Run(pump.Run); - - Assert.True(ran.Wait(Timeout), "Run() did not return on EOF"); - Assert.False(pump.Running); - } - - [Fact] - public void Recognized_line_dispatches_command() - { - var invokedWith = new TaskCompletionSource(); - Action onPing = arg => invokedWith.TrySetResult(arg); - - var pump = new ConsoleInputPump( - new StringReader("ping hello\n"), - cmd => cmd == "ping" ? onPing : null - ); - - Task.Run(pump.Run); - - Assert.True(invokedWith.Task.Wait(Timeout), "command not dispatched"); - Assert.Equal("hello", invokedWith.Task.Result); - } - - [Fact] - public void Pending_prompt_receives_next_line() - { - var reader = new BlockingTextReader(); - var pump = new ConsoleInputPump(reader, _ => null); - Task.Run(pump.Run); - - // Register the prompt, then wait until it is actually pending before feeding a - // line — otherwise the line could be consumed as a command before registration. - var prompt = Task.Run(pump.ReadLine); - WaitUntil(() => pump.HasPendingPrompt, "prompt did not register"); - reader.Feed("the answer"); - - Assert.True(prompt.Wait(Timeout), "prompt did not receive a line"); - Assert.Equal("the answer", prompt.Result); - - reader.Complete(); - } - - [Fact] - public void Eof_while_prompt_pending_completes_prompt_with_null() - { - var reader = new BlockingTextReader(); - var pump = new ConsoleInputPump(reader, _ => null); - Task.Run(pump.Run); - - var prompt = Task.Run(pump.ReadLine); - WaitUntil(() => pump.HasPendingPrompt, "prompt did not register"); - reader.Complete(); // EOF while prompt is waiting - - Assert.True(prompt.Wait(Timeout), "prompt hung on EOF"); - Assert.Null(prompt.Result); - Assert.False(pump.Running); - } - - [Fact] - public void Throwing_lookup_does_not_hang_pending_prompt() - { - var reader = new BlockingTextReader(); - var pump = new ConsoleInputPump(reader, _ => throw new InvalidOperationException("boom")); - Task.Run(pump.Run); - - // Wait until the reader loop is blocked on its first read, then feed a bad command. - WaitUntil(() => reader.ReadCallCount >= 1, "reader did not start"); - reader.Feed("badcommand"); - - // Wait until the loop has consumed that command (throwing lookup swallowed) and is - // blocked waiting for the NEXT line — a second ReadLine() entry. This guarantees - // the command was dispatched (not delivered to a prompt) before we register one. - WaitUntil(() => reader.ReadCallCount >= 2, "bad command was not consumed"); - - var prompt = Task.Run(pump.ReadLine); - WaitUntil(() => pump.HasPendingPrompt, "prompt did not register"); - reader.Complete(); // EOF while prompt is waiting - - Assert.True(prompt.Wait(Timeout), "prompt hung after throwing lookup"); - Assert.Null(prompt.Result); - Assert.False(pump.Running); - } -} -#pragma warning restore xUnit1031 diff --git a/Projects/Server.Tests/Tests/Geometry/Point2DTests.cs b/Projects/Server.Tests/Tests/Geometry/Point2DTests.cs index b93a391af..0d1426275 100644 --- a/Projects/Server.Tests/Tests/Geometry/Point2DTests.cs +++ b/Projects/Server.Tests/Tests/Geometry/Point2DTests.cs @@ -19,7 +19,7 @@ public class Point2DTests [Fact] public void TestPoint2DTryFormatSucceeds() { - var array = new char[128]; + char[] array = new char[128]; var p1 = new Point2D(0, 0); Assert.True(p1.TryFormat(array, out var cp1, null, null)); @@ -44,7 +44,7 @@ public class Point2DTests [Fact] public void TestPoint2DTryFormatFails() { - var array = new char[1]; + char[] array = new char[1]; var p1 = new Point2D(0, 0); Assert.False(p1.TryFormat(array, out var cp1, null, null)); diff --git a/Projects/Server.Tests/Tests/Geometry/Point3DTests.cs b/Projects/Server.Tests/Tests/Geometry/Point3DTests.cs index 8bdd279f5..e90a7531d 100644 --- a/Projects/Server.Tests/Tests/Geometry/Point3DTests.cs +++ b/Projects/Server.Tests/Tests/Geometry/Point3DTests.cs @@ -32,7 +32,7 @@ public class Point3DTests { const int max = int.MaxValue; const int min = int.MinValue; - var array = new char[128]; + char[] array = new char[128]; var p1 = new Point3D(0, 0, 0); Assert.True(p1.TryFormat(array, out var cp1, null, null)); @@ -59,7 +59,7 @@ public class Point3DTests { const int max = int.MaxValue; const int min = int.MinValue; - var array = new char[1]; + char[] array = new char[1]; var p1 = new Point3D(0, 0, 0); Assert.False(p1.TryFormat(array, out var cp1, null, null)); diff --git a/Projects/Server.Tests/Tests/Geometry/Rectangle2DTests.cs b/Projects/Server.Tests/Tests/Geometry/Rectangle2DTests.cs index 1c9bb58fd..b009ac0af 100644 --- a/Projects/Server.Tests/Tests/Geometry/Rectangle2DTests.cs +++ b/Projects/Server.Tests/Tests/Geometry/Rectangle2DTests.cs @@ -21,7 +21,7 @@ public class Rectangle2DTests { const int max = int.MaxValue; const int min = int.MinValue; - var array = new char[128]; + char[] array = new char[128]; var p1 = new Rectangle2D(0, 0, 0, 0); Assert.True(p1.TryFormat(array, out var cp1, null, null)); @@ -48,7 +48,7 @@ public class Rectangle2DTests { const int max = int.MaxValue; const int min = int.MinValue; - var array = new char[1]; + char[] array = new char[1]; var p1 = new Rectangle2D(0, 0, 0, 0); Assert.False(p1.TryFormat(array, out var cp1, null, null)); diff --git a/Projects/Server.Tests/Tests/Geometry/Rectangle3DTests.cs b/Projects/Server.Tests/Tests/Geometry/Rectangle3DTests.cs index 2b030a183..8dcbef2a6 100644 --- a/Projects/Server.Tests/Tests/Geometry/Rectangle3DTests.cs +++ b/Projects/Server.Tests/Tests/Geometry/Rectangle3DTests.cs @@ -21,7 +21,7 @@ public class Rectangle3DTests { const int max = int.MaxValue; const int min = int.MinValue; - var array = new char[128]; + char[] array = new char[128]; var p1 = new Rectangle3D(0, 0, 0, 0, 0, 0); Assert.True(p1.TryFormat(array, out var cp1, null, null)); @@ -48,7 +48,7 @@ public class Rectangle3DTests { const int max = int.MaxValue; const int min = int.MinValue; - var array = new char[1]; + char[] array = new char[1]; var p1 = new Rectangle3D(0, 0, 0, 0, 0, 0); Assert.False(p1.TryFormat(array, out var cp1, null, null)); diff --git a/Projects/Server.Tests/Tests/Geometry/WorldLocationTests.cs b/Projects/Server.Tests/Tests/Geometry/WorldLocationTests.cs index b9fcc6f7a..1425e7b24 100644 --- a/Projects/Server.Tests/Tests/Geometry/WorldLocationTests.cs +++ b/Projects/Server.Tests/Tests/Geometry/WorldLocationTests.cs @@ -3,8 +3,7 @@ using Xunit; namespace Server.Tests; -[Collection("Sequential Server Tests")] -public sealed class WorldLocationTests +public sealed class WorldLocationTests : IClassFixture { private static Map CreateMap(string name) => new(0, 0, 0, 1, 1, 0, name, MapRules.Internal); @@ -50,7 +49,7 @@ public sealed class WorldLocationTests const int max = int.MaxValue; const int min = int.MinValue; - var array = new char[128]; + char[] array = new char[128]; var p1 = new WorldLocation(0, 0, 0, map); Assert.True(p1.TryFormat(array, out var cp1, null, null)); @@ -82,7 +81,7 @@ public sealed class WorldLocationTests const int max = int.MaxValue; const int min = int.MinValue; - var array = new char[1]; + char[] array = new char[1]; var p1 = new WorldLocation(0, 0, 0, map); Assert.False(p1.TryFormat(array, out var cp1, null, null)); diff --git a/Projects/Server.Tests/Tests/Items/ContainerTests.cs b/Projects/Server.Tests/Tests/Items/ContainerTests.cs index 6b7634da8..d6d597754 100644 --- a/Projects/Server.Tests/Tests/Items/ContainerTests.cs +++ b/Projects/Server.Tests/Tests/Items/ContainerTests.cs @@ -5,17 +5,14 @@ using Xunit; namespace Server.Tests; -[Collection("Sequential Server Tests")] -public class ContainerTests +public class ContainerTests : IClassFixture { - [Theory] - [InlineData(typeof(Container))] - [InlineData(typeof(Item))] - public void TestFindItemsByType(Type itemType) + [Fact] + public void TestFindItemsByType() { var staticSerial = (Serial)0x3; - var container = itemType.CreateInstance((Serial)0x1); + var container = new Container((Serial)0x1); container.AddItem(new Item((Serial)0x2)); container.AddItem(new Static(staticSerial)); @@ -29,26 +26,24 @@ public class ContainerTests Assert.Equal(staticSerial, staticItem.Serial); } - [Theory] - [InlineData(typeof(Container))] - [InlineData(typeof(Item))] - public void TestFindItemsByTypeNested(Type itemType) + [Fact] + public void TestFindItemsByTypeNested() { var static1 = new Static((Serial)0x3); var static2 = new Static((Serial)0x6); - var container = itemType.CreateInstance((Serial)0x1); + var container = new Container((Serial)0x1); container.AddItem(new Item((Serial)0x2)); - var container2 = itemType.CreateInstance((Serial)0x4); + var container2 = new Container((Serial)0x4); container.AddItem(container2); - var container3 = itemType.CreateInstance((Serial)0x5); + var container3 = new Container((Serial)0x5); container2.AddItem(container3); container3.AddItem(static2); container2.AddItem(static1); - var statics = new List(); + List statics = new List(); foreach (var item in container.FindItemsByType()) { statics.Add(item); @@ -59,14 +54,12 @@ public class ContainerTests Assert.Equal(static2, statics[1]); } - [Theory] - [InlineData(typeof(Container))] - [InlineData(typeof(Item))] - public void TestFindItemsByTypeNotMatching(Type itemType) + [Fact] + public void TestFindItemsByTypeNotMatching() { - var container = itemType.CreateInstance((Serial)0x1); + var container = new Container((Serial)0x1); container.AddItem(new Item((Serial)0x2)); - var container2 = itemType.CreateInstance((Serial)0x4); + var container2 = new Container((Serial)0x4); container.AddItem(container2); container2.AddItem(new Item((Serial)0x5)); @@ -79,12 +72,10 @@ public class ContainerTests Assert.Null(staticItem); } - [Theory] - [InlineData(typeof(Container))] - [InlineData(typeof(Item))] - public void TestFindItemsByTypeShouldThrowWhenModified(Type itemType) + [Fact] + public void TestFindItemsByTypeShouldThrowWhenModified() { - var container = itemType.CreateInstance((Serial)0x1); + var container = new Container((Serial)0x1); container.AddItem(new Item((Serial)0x2)); var staticItem = new Static((Serial)0x3); container.AddItem(staticItem); @@ -104,12 +95,10 @@ public class ContainerTests ); } - [Theory] - [InlineData(typeof(Container))] - [InlineData(typeof(Item))] - public void TestEnumerateItemsByTypeWhenModified(Type itemType) + [Fact] + public void TestEnumerateItemsByTypeWhenModified() { - var container = itemType.CreateInstance((Serial)0x1); + var container = new Container((Serial)0x1); var item1 = new Item((Serial)0x2); container.AddItem(item1); @@ -132,484 +121,4 @@ public class ContainerTests item => Assert.Equal(item3, item) ); } - - // ------------------------------------------------------------------------- - // ConsumeTotal / ConsumeTotalGrouped / GetBestGroupAmount / GetAmount / - // FindItemByType / ConsumeUpTo behavior locks. - // - // These tests pin down the public contract before the optimization pass: - // - all-or-nothing semantics across multi-slot consume - // - callback ordering and per-item delta values - // - "first sufficient group wins" (not best) for ConsumeTotalGrouped - // - grouper(a, b) receives the group leader as `a` - // - >= amount threshold for groups - // ------------------------------------------------------------------------- - - private static Container MakeContainer(uint serial = 0x100) => new((Serial)serial); - - private static Item MakeStack(uint serial, int amount, int hue = 0) - { - var item = new Item((Serial)serial) { Amount = amount }; - if (hue != 0) - { - item.Hue = hue; - } - return item; - } - - private static TestReagentA MakeReagentA(uint serial, int amount, int hue = 0) - { - var item = new TestReagentA((Serial)serial) { Amount = amount }; - if (hue != 0) - { - item.Hue = hue; - } - return item; - } - - private static TestReagentB MakeReagentB(uint serial, int amount, int hue = 0) - { - var item = new TestReagentB((Serial)serial) { Amount = amount }; - if (hue != 0) - { - item.Hue = hue; - } - return item; - } - - // Mirrors CraftItem.CheckHueGrouping: groups items that share a hue. - private static int HueGrouper(Item a, Item b) => b.Hue.CompareTo(a.Hue); - - [Fact] - public void TestConsumeTotal_SingleType_ExactAmount_Succeeds() - { - var c = MakeContainer(0x200); - c.AddItem(MakeReagentA(0x201, 5)); - - Assert.True(c.ConsumeTotal(typeof(TestReagentA), 5)); - Assert.Empty(c.Items); // Stack was fully consumed -> Delete() - } - - [Fact] - public void TestConsumeTotal_SingleType_AcrossStacks_PartialOnLast() - { - var c = MakeContainer(0x210); - var a = MakeReagentA(0x211, 3); - var b = MakeReagentA(0x212, 4); - c.AddItem(a); - c.AddItem(b); - - Assert.True(c.ConsumeTotal(typeof(TestReagentA), 5)); - // BFS order: a fully consumed (3), b partially (2 of 4 remain). - Assert.True(a.Deleted); - Assert.False(b.Deleted); - Assert.Equal(2, b.Amount); - } - - [Fact] - public void TestConsumeTotal_SingleType_NotEnough_NoConsumption() - { - var c = MakeContainer(0x220); - var a = MakeReagentA(0x221, 2); - var b = MakeReagentA(0x222, 2); - c.AddItem(a); - c.AddItem(b); - - Assert.False(c.ConsumeTotal(typeof(TestReagentA), 10)); - // Must not partially consume. - Assert.Equal(2, a.Amount); - Assert.Equal(2, b.Amount); - } - - [Fact] - public void TestConsumeTotal_MultiType_FailsAtSlot1_Slot0Untouched() - { - var c = MakeContainer(0x230); - var ra = MakeReagentA(0x231, 5); - var rb = MakeReagentB(0x232, 1); // not enough for slot 1 - c.AddItem(ra); - c.AddItem(rb); - - var result = c.ConsumeTotal( - new[] { typeof(TestReagentA), typeof(TestReagentB) }, - new[] { 5, 5 } - ); - - Assert.Equal(1, result); - // Critical all-or-nothing invariant. - Assert.Equal(5, ra.Amount); - Assert.Equal(1, rb.Amount); - } - - [Fact] - public void TestConsumeTotal_NestedContainer_RecurseCountsChildItems() - { - var outer = MakeContainer(0x240); - var inner = MakeContainer(0x241); - outer.AddItem(inner); - inner.AddItem(MakeReagentA(0x242, 4)); - outer.AddItem(MakeReagentA(0x243, 1)); - - Assert.True(outer.ConsumeTotal(typeof(TestReagentA), 5)); - // Both reagents deleted; inner container itself persists in outer. - Assert.Single(outer.Items); - Assert.Empty(inner.Items); - } - - [Fact] - public void TestConsumeTotal_CallbackFires_PerItem_WithDelta() - { - var c = MakeContainer(0x250); - c.AddItem(MakeReagentA(0x251, 3)); - c.AddItem(MakeReagentA(0x252, 4)); - - var calls = new List<(int serial, int amount)>(); - Assert.True( - c.ConsumeTotal( - typeof(TestReagentA), 5, true, - (item, amt) => calls.Add(((int)item.Serial.Value, amt)) - ) - ); - - Assert.Equal(2, calls.Count); - Assert.Equal((0x251, 3), calls[0]); // full first stack - Assert.Equal((0x252, 2), calls[1]); // partial second stack - } - - [Fact] - public void TestConsumeTotalGrouped_HueGrouping_FirstSufficientGroupWins() - { - // Two hue groups: blue (sum=4, insufficient), red (sum=8, sufficient). - // First group meeting >= amount in BFS order wins. Blue is first; it's - // skipped because insufficient. Red wins. Verify red consumed, blue intact. - var c = MakeContainer(0x260); - var blue1 = MakeReagentA(0x261, 2, hue: 0x10); - var blue2 = MakeReagentA(0x262, 2, hue: 0x10); - var red1 = MakeReagentA(0x263, 5, hue: 0x20); - var red2 = MakeReagentA(0x264, 3, hue: 0x20); - c.AddItem(blue1); - c.AddItem(blue2); - c.AddItem(red1); - c.AddItem(red2); - - var result = c.ConsumeTotalGrouped( - new[] { typeof(TestReagentA) }, new[] { 6 }, - true, null, HueGrouper - ); - - Assert.Equal(-1, result); - Assert.Equal(2, blue1.Amount); - Assert.Equal(2, blue2.Amount); - // Red consumed: 5 + 1 = 6 needed. - Assert.True(red1.Deleted); - Assert.False(red2.Deleted); - Assert.Equal(2, red2.Amount); - } - - [Fact] - public void TestConsumeTotalGrouped_GrouperReceivesGroupLeader() - { - // grouper(a, b) must receive the group's first item as `a`, not the - // previous item. This matters when grouping is order-sensitive. - var c = MakeContainer(0x270); - var i1 = MakeReagentA(0x271, 1, hue: 0x10); - var i2 = MakeReagentA(0x272, 1, hue: 0x10); - var i3 = MakeReagentA(0x273, 1, hue: 0x10); - c.AddItem(i1); - c.AddItem(i2); - c.AddItem(i3); - - var leaderSerials = new List(); - c.ConsumeTotalGrouped( - new[] { typeof(TestReagentA) }, new[] { 3 }, - true, null, - (a, b) => - { - leaderSerials.Add((int)a.Serial.Value); - return b.Hue.CompareTo(a.Hue); - } - ); - - // Leader for every comparison in the single group must be i1 (the first). - Assert.NotEmpty(leaderSerials); - Assert.All(leaderSerials, s => Assert.Equal((int)i1.Serial.Value, s)); - } - - [Fact] - public void TestConsumeTotalGrouped_PartialFailure_NothingConsumedFromAnySlot() - { - // Slot 0 has enough; slot 1 does not. Nothing consumed, return 1. - var c = MakeContainer(0x280); - var a = MakeReagentA(0x281, 5, hue: 0x10); - var b = MakeReagentB(0x282, 1, hue: 0x10); - c.AddItem(a); - c.AddItem(b); - - var result = c.ConsumeTotalGrouped( - new[] { typeof(TestReagentA), typeof(TestReagentB) }, - new[] { 5, 5 }, - true, null, HueGrouper - ); - - Assert.Equal(1, result); - Assert.Equal(5, a.Amount); - Assert.Equal(1, b.Amount); - } - - [Fact] - public void TestConsumeTotalGrouped_ExactGroupAmount_Succeeds() - { - // Group sum equals exactly the requested amount. Threshold is >=, so - // a group of exactly N satisfies a request for N. - var c = MakeContainer(0x330); - var stack = MakeReagentA(0x331, 5, hue: 0x10); - c.AddItem(stack); - - var result = c.ConsumeTotalGrouped( - new[] { typeof(TestReagentA) }, new[] { 5 }, - true, null, HueGrouper - ); - - Assert.Equal(-1, result); - Assert.True(stack.Deleted); - } - - [Fact] - public void TestConsumeTotalGrouped_CallbackOrderMatchesBFS() - { - // OnResourceConsumed in CraftItem.cs retains the hue of the largest - // consumed stack, so callback order must match BFS iteration order. - var c = MakeContainer(0x290); - c.AddItem(MakeReagentA(0x291, 3, hue: 0x10)); - c.AddItem(MakeReagentA(0x292, 4, hue: 0x10)); - - var calls = new List<(int serial, int amount)>(); - c.ConsumeTotalGrouped( - new[] { typeof(TestReagentA) }, new[] { 5 }, - true, - (item, amt) => calls.Add(((int)item.Serial.Value, amt)), - HueGrouper - ); - - Assert.Equal(2, calls.Count); - Assert.Equal((0x291, 3), calls[0]); - Assert.Equal((0x292, 2), calls[1]); - } - - [Fact] - public void TestGetBestGroupAmount_ReturnsLargestGroupSum() - { - var c = MakeContainer(0x2A0); - c.AddItem(MakeReagentA(0x2A1, 2, hue: 0x10)); - c.AddItem(MakeReagentA(0x2A2, 2, hue: 0x10)); - c.AddItem(MakeReagentA(0x2A3, 5, hue: 0x20)); - c.AddItem(MakeReagentA(0x2A4, 3, hue: 0x20)); - - var best = c.GetBestGroupAmount(new[] { typeof(TestReagentA) }, true, HueGrouper); - Assert.Equal(8, best); - } - - [Fact] - public void TestGetBestGroupAmount_EmptyOrNoMatch_ReturnsZero() - { - var c = MakeContainer(0x2B0); - Assert.Equal(0, c.GetBestGroupAmount(new[] { typeof(TestReagentA) }, true, HueGrouper)); - - c.AddItem(MakeReagentB(0x2B1, 5)); - Assert.Equal(0, c.GetBestGroupAmount(new[] { typeof(TestReagentA) }, true, HueGrouper)); - } - - [Fact] - public void TestGetBestGroupAmount_NullGrouper_Throws() - { - var c = MakeContainer(0x2C0); - Assert.Throws( - () => c.GetBestGroupAmount(new[] { typeof(TestReagentA) }, true, null) - ); - } - - [Fact] - public void TestGetAmount_TypeArray_SumsAcrossTypes() - { - var c = MakeContainer(0x2D0); - c.AddItem(MakeReagentA(0x2D1, 3)); - c.AddItem(MakeReagentB(0x2D2, 4)); - c.AddItem(MakeStack(0x2D3, 99)); // base Item, doesn't match A or B - - var total = c.GetAmount(new[] { typeof(TestReagentA), typeof(TestReagentB) }); - Assert.Equal(7, total); - } - - [Fact] - public void TestFindItemByType_Generic_WithPredicate() - { - var c = MakeContainer(0x2E0); - c.AddItem(MakeReagentA(0x2E1, 1, hue: 0x10)); - var target = MakeReagentA(0x2E2, 1, hue: 0x20); - c.AddItem(target); - c.AddItem(MakeReagentA(0x2E3, 1, hue: 0x10)); - - var found = c.FindItemByType(true, item => item.Hue == 0x20); - Assert.Same(target, found); - } - - [Fact] - public void TestConsumeUpTo_DeletesExhaustedStacks() - { - var c = MakeContainer(0x2F0); - var s1 = MakeReagentA(0x2F1, 3); - var s2 = MakeReagentA(0x2F2, 3); - c.AddItem(s1); - c.AddItem(s2); - - var consumed = c.ConsumeUpTo(typeof(TestReagentA), 5); - Assert.Equal(5, consumed); - // BFS: first stack fully (3), second partially (2 of 3 remain). - Assert.True(s1.Deleted); - Assert.False(s2.Deleted); - Assert.Equal(1, s2.Amount); - } - - [Fact] - public void TestConsumeTotal_LengthMismatch_Throws() - { - var c = MakeContainer(0x300); - Assert.Throws( - () => c.ConsumeTotal(new[] { typeof(TestReagentA) }, new[] { 1, 2 }) - ); - } - - [Fact] - public void TestConsumeTotalGrouped_LengthMismatch_Throws() - { - var c = MakeContainer(0x310); - Assert.Throws( - () => c.ConsumeTotalGrouped( - new[] { typeof(TestReagentA) }, new[] { 1, 2 }, - true, null, HueGrouper - ) - ); - } - - [Fact] - public void TestConsumeTotalGrouped_NullGrouper_Throws() - { - var c = MakeContainer(0x320); - Assert.Throws( - () => c.ConsumeTotalGrouped( - new[] { typeof(TestReagentA) }, new[] { 1 }, - true, null, null - ) - ); - } - - // ------------------------------------------------------------------------- - // FindItemsByType(Type) and FindItemsByType(ReadOnlySpan) — Phase 10: - // these used to allocate a Predicate per call (method-group conversion - // for the single-Type case, real closure for the Type[] case). The - // enumerator now stores the filter directly. - // ------------------------------------------------------------------------- - - [Fact] - public void TestFindItemsByType_RuntimeType_MatchesPredicatePath() - { - var c = MakeContainer(0x340); - c.AddItem(MakeReagentA(0x341, 1)); - c.AddItem(MakeReagentB(0x342, 1)); - c.AddItem(MakeReagentA(0x343, 1)); - c.AddItem(MakeStack(0x344, 1)); // base Item, doesn't match TestReagentA - - var fromRuntime = new List(); - foreach (var item in c.FindItemsByType(typeof(TestReagentA))) - { - fromRuntime.Add((int)item.Serial.Value); - } - - var fromGeneric = new List(); - foreach (var item in c.FindItemsByType()) - { - fromGeneric.Add((int)item.Serial.Value); - } - - Assert.Equal(fromGeneric, fromRuntime); - Assert.Equal(new[] { 0x341, 0x343 }, fromRuntime); - } - - [Fact] - public void TestFindItemsByType_TypeSpan_MatchesUnionOfTypes() - { - var c = MakeContainer(0x350); - c.AddItem(MakeReagentA(0x351, 1)); - c.AddItem(MakeReagentB(0x352, 1)); - c.AddItem(MakeStack(0x353, 1)); // base Item - - var serials = new List(); - foreach (var item in c.FindItemsByType(new[] { typeof(TestReagentA), typeof(TestReagentB) })) - { - serials.Add((int)item.Serial.Value); - } - - Assert.Equal(new[] { 0x351, 0x352 }, serials); - } - - [Fact] - public void TestFindItemsByType_RuntimeType_NoAllocations() - { - // Establishes that the (Type) path no longer allocates a Predicate per - // call. Snapshot allocation counter, run a few iterations, assert flat. - var c = MakeContainer(0x360); - c.AddItem(MakeReagentA(0x361, 1)); - c.AddItem(MakeReagentA(0x362, 1)); - - // Warm up to load any first-call jitting. - foreach (var _ in c.FindItemsByType(typeof(TestReagentA))) { } - - var before = GC.GetAllocatedBytesForCurrentThread(); - for (var i = 0; i < 100; i++) - { - foreach (var _ in c.FindItemsByType(typeof(TestReagentA)) ) { } - } - var delta = GC.GetAllocatedBytesForCurrentThread() - before; - - // PooledRefQueue rents from the pool but the rental itself doesn't - // allocate when the bucket is warm. Allow a small headroom for any - // first-rent-after-pool-cleanup allocations but well below the ~48 - // bytes/call the old delegate path would have produced (=4800 bytes). - Assert.True(delta < 1024, $"Expected near-zero allocations, got {delta} bytes across 100 iterations"); - } - - [Fact] - public void TestFindItemsByType_TypeSpan_NoAllocations() - { - var c = MakeContainer(0x370); - c.AddItem(MakeReagentA(0x371, 1)); - c.AddItem(MakeReagentB(0x372, 1)); - - var types = new[] { typeof(TestReagentA), typeof(TestReagentB) }; - foreach (var _ in c.FindItemsByType(types)) { } // warm - - var before = GC.GetAllocatedBytesForCurrentThread(); - for (var i = 0; i < 100; i++) - { - foreach (var _ in c.FindItemsByType(types)) { } - } - var delta = GC.GetAllocatedBytesForCurrentThread() - before; - - // Old closure path: ~80 bytes/call → ~8000 bytes for 100 iterations. - Assert.True(delta < 1024, $"Expected near-zero allocations, got {delta} bytes across 100 iterations"); - } -} - -// Test-only Item subclasses used to differentiate concrete types in the -// consume/find/group tests above. They have no serialization generator -// (the tests never round-trip through World). Stackable is set so the Amount -// setter doesn't log "Amount changed for non-stackable item" warnings. -public sealed class TestReagentA : Item -{ - public TestReagentA(Serial serial) : base(serial) => Stackable = true; -} - -public sealed class TestReagentB : Item -{ - public TestReagentB(Serial serial) : base(serial) => Stackable = true; } diff --git a/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs b/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs deleted file mode 100644 index 59d736c6f..000000000 --- a/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs +++ /dev/null @@ -1,498 +0,0 @@ -using System; -using Server.Items; -using Xunit; - -namespace Server.Tests; - -[Collection("Sequential Server Tests")] -public class DecayRegistrationTests -{ - // The wheel is static and these tests move the clock, so start each from a known schedule. - public DecayRegistrationTests() => DecayScheduler.ResetForTests(); - - // Stands in for a region that refuses decay, e.g. HouseRegion over a locked down item. - private class RefusesDecayItem : Item - { - public RefusesDecayItem() : base(0x1234) - { - } - - public override bool OnDecay() => false; - } - - // The item is already dequeued, so a refusal that does not reschedule strands it forever. - [Fact] - public void ItemWhoseDecayIsRefused_StaysTrackedAndSurvives() - { - var start = Core._now; - - try - { - var item = new RefusesDecayItem(); - item.MoveToWorld(new Point3D(106, 100, 0), Map.Felucca); - - AdvanceDecay(start, item.DecayTime + TimeSpan.FromMinutes(2), item); - - Assert.False(item.Deleted, "A refused decay must not delete the item."); - Assert.True( - DecayScheduler.IsRegistered(item), - "A refused decay must leave the item tracked so it is retried." - ); - - item.Delete(); - } - finally - { - Core._now = start; - } - } - - // Drives the scheduler and the game clock without depending on the shared timer wheel. - private static void AdvanceDecay(DateTime start, TimeSpan duration, Item item) - { - var deadline = start + duration; - - for (var now = start; now <= deadline && !item.Deleted; now += TimeSpan.FromMilliseconds(256)) - { - Core._now = now; - DecayScheduler.ProcessTick(now); - } - } - - [Fact] - public void ItemOnGround_ActuallyDecaysAfterDecayTime() - { - var start = Core._now; - - try - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(100, 100, 0), Map.Felucca); - - Assert.True(DecayScheduler.IsRegistered(item)); - - AdvanceDecay(start, item.DecayTime + TimeSpan.FromMinutes(2), item); - - Assert.True(item.Deleted, "Item on the ground must decay once DecayTime elapses."); - } - finally - { - Core._now = start; - } - } - - [Fact] - public void ItemOnGround_DoesNotDecayBeforeDecayTime() - { - var start = Core._now; - - try - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(102, 100, 0), Map.Felucca); - - AdvanceDecay(start, item.DecayTime - TimeSpan.FromMinutes(2), item); - - Assert.False(item.Deleted, "Item must not decay before DecayTime elapses."); - - item.Delete(); - } - finally - { - Core._now = start; - } - } - - [Fact] - public void ItemPlacedInWorld_IsRegisteredForDecay() - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(100, 100, 0), Map.Felucca); - - Assert.True(item.CanDecay()); - Assert.True(DecayScheduler.IsRegistered(item), "Item on the ground must be tracked for decay."); - - item.Delete(); - } - - // The real client path: Mobile.Lift internalizes, then Mobile.Drop calls MoveToWorld. - [Fact] - public void ItemLiftedThenDroppedToGround_IsRegisteredForDecay() - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(100, 100, 0), Map.Felucca); - - // Player lifts the item to the cursor. - item.Internalize(); - Assert.Equal(Map.Internal, item.Map); - - // Player drops it back onto the ground. - item.MoveToWorld(new Point3D(101, 100, 0), Map.Felucca); - - Assert.True(item.CanDecay()); - Assert.True( - DecayScheduler.IsRegistered(item), - "Item dropped to the ground after being lifted must be tracked for decay." - ); - - item.Delete(); - } - - // Held on the cursor means Map.Internal, which must not be tracked. - [Fact] - public void ItemLiftedToCursor_IsNotRegisteredForDecay() - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(100, 100, 0), Map.Felucca); - - item.Internalize(); - - Assert.False(item.CanDecay()); - Assert.False(DecayScheduler.IsRegistered(item), "Item held on the cursor must not be tracked for decay."); - - item.Delete(); - } - - // A new item is on Map.Internal, so construction must not touch the scheduler. - [Fact] - public void NewItem_IsNotTracked() - { - var item = new Item(0x1234); - - Assert.Equal(DecayScheduler.SlotNone, item.DecaySlot); - Assert.False(DecayScheduler.IsRegistered(item), "A newly constructed item must not be tracked."); - - item.Delete(); - } - - // Unregister() trusts DecaySlot; IsRegistered() scans the structures. Assert both to catch drift. - [Fact] - public void DecaySlot_AgreesWithStructures_AcrossLifecycle() - { - var item = new Item(0x1234); - - item.MoveToWorld(new Point3D(103, 100, 0), Map.Felucca); - Assert.NotEqual(DecayScheduler.SlotNone, item.DecaySlot); - Assert.True(DecayScheduler.IsRegistered(item)); - - // Lifted to the cursor: untracked, and no stale slot left behind. - item.Internalize(); - Assert.Equal(DecayScheduler.SlotNone, item.DecaySlot); - Assert.False(DecayScheduler.IsRegistered(item)); - - // Back to the ground: tracked again. - item.MoveToWorld(new Point3D(104, 100, 0), Map.Felucca); - Assert.NotEqual(DecayScheduler.SlotNone, item.DecaySlot); - Assert.True(DecayScheduler.IsRegistered(item)); - - item.Delete(); - Assert.False(DecayScheduler.IsRegistered(item), "A deleted item must leave no trace in the scheduler."); - } - - // Contents spilled onto the ground must start decaying. - [Fact] - public void ContainerDestroy_ContentsDroppedToGroundAreTracked() - { - var pack = new Container(0xE75); - pack.MoveToWorld(new Point3D(105, 100, 0), Map.Felucca); - - var item = new Item(0x1234); - pack.AddItem(item); - Assert.False(DecayScheduler.IsRegistered(item)); - - pack.Destroy(); - - Assert.True(item.CanDecay()); - Assert.True(DecayScheduler.IsRegistered(item), "Contents spilled by Container.Destroy must be tracked."); - - item.Delete(); - } - - // Unfreezing an item with a stale LastMoved must grant a fresh decay window, - // not delete it on the next tick. - [Fact] - public void StaleImmovableItemMadeMovable_GetsAFreshDecayWindow() - { - var start = Core._now; - - try - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(107, 100, 0), Map.Felucca); - - item.Movable = false; - Assert.False(DecayScheduler.IsRegistered(item), "A frozen item must not be tracked for decay."); - - Core._now = start + TimeSpan.FromDays(30); - var flipped = Core._now; - - item.Movable = true; - - Assert.True(DecayScheduler.IsRegistered(item), "An unfrozen item must be tracked for decay."); - - AdvanceDecay(flipped, item.DecayTime - TimeSpan.FromMinutes(2), item); - Assert.False(item.Deleted, "An unfrozen item must get a full decay window, not vanish immediately."); - - AdvanceDecay(Core._now, TimeSpan.FromMinutes(4), item); - Assert.True(item.Deleted, "An unfrozen item must still decay once the fresh window elapses."); - } - finally - { - Core._now = start; - } - } - - // Same transition through the Visible setter: unhiding a long-hidden item. - [Fact] - public void StaleHiddenItemMadeVisible_GetsAFreshDecayWindow() - { - var start = Core._now; - - try - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(109, 100, 0), Map.Felucca); - - item.Visible = false; - Assert.False(DecayScheduler.IsRegistered(item), "A hidden item must not be tracked for decay."); - - Core._now = start + TimeSpan.FromDays(30); - var flipped = Core._now; - - item.Visible = true; - - Assert.True(DecayScheduler.IsRegistered(item), "An unhidden item must be tracked for decay."); - - AdvanceDecay(flipped, item.DecayTime - TimeSpan.FromMinutes(2), item); - Assert.False(item.Deleted, "An unhidden item must get a full decay window, not vanish immediately."); - - AdvanceDecay(Core._now, TimeSpan.FromMinutes(4), item); - Assert.True(item.Deleted, "An unhidden item must still decay once the fresh window elapses."); - } - finally - { - Core._now = start; - } - } - - // A refusal restarts the countdown without rewriting LastMoved. - [Fact] - public void RefusedDecay_DoesNotRewriteLastMoved() - { - var start = Core._now; - - try - { - var item = new RefusesDecayItem(); - item.MoveToWorld(new Point3D(110, 100, 0), Map.Felucca); - var lastMoved = item.LastMoved; - - AdvanceDecay(start, item.DecayTime + TimeSpan.FromMinutes(2), item); - - Assert.False(item.Deleted, "A refused decay must not delete the item."); - Assert.True(DecayScheduler.IsRegistered(item), "A refused decay must leave the item tracked."); - Assert.Equal(lastMoved, item.LastMoved); - - item.Delete(); - } - finally - { - Core._now = start; - } - } - - // The fresh window must survive a save/load cycle, or a restart mid-window deletes the item. - [Fact] - public void FreshDecayWindow_SurvivesSerialization() - { - var start = Core._now; - - try - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(111, 100, 0), Map.Felucca); - - item.Movable = false; - Core._now = start + TimeSpan.FromDays(30); - item.Movable = true; - - var expected = item.ScheduledDecayTime; - - var writer = new BufferWriter(new byte[512], true); - item.Serialize(writer); - - var copy = new Item(item.Serial); - copy.Deserialize(new BufferReader(writer.Buffer)); - - // The stamp is stored as a delta, so it ages only by the real time between - // write and read - milliseconds here, the downtime in production. - Assert.True( - (copy.ScheduledDecayTime - expected).Duration() <= TimeSpan.FromSeconds(5), - "The restarted decay window must survive a save/load cycle." - ); - - item.Delete(); - copy.Delete(); - } - finally - { - Core._now = start; - } - } - - // A real move supersedes the reset stamp; it must be dropped so the CompactInfo can collapse. - [Fact] - public void MovingAnItem_ClearsASupersededDecayResetStamp() - { - var start = Core._now; - - try - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(112, 100, 0), Map.Felucca); - - item.Movable = false; - Core._now = start + TimeSpan.FromDays(30); - item.Movable = true; - - Assert.NotEqual(default, item.DecayResetTime); - - Core._now += TimeSpan.FromMinutes(1); - item.MoveToWorld(new Point3D(113, 100, 0), Map.Felucca); - - Assert.Equal(default, item.DecayResetTime); - Assert.Equal(item.LastMoved + item.DecayTime, item.ScheduledDecayTime); - Assert.True(DecayScheduler.IsRegistered(item)); - - item.Delete(); - } - finally - { - Core._now = start; - } - } - - // Losing decay eligibility makes the stamp meaningless; it must be dropped so the - // CompactInfo is not held for as long as the item stays ineligible. - [Fact] - public void ItemBecomingIneligible_DropsTheDecayResetStamp() - { - var start = Core._now; - - try - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(115, 100, 0), Map.Felucca); - - item.Movable = false; - Core._now = start + TimeSpan.FromDays(30); - item.Movable = true; - - Assert.NotEqual(default, item.DecayResetTime); - - item.Movable = false; - - Assert.Equal(default, item.DecayResetTime); - - item.Delete(); - } - finally - { - Core._now = start; - } - } - - // Moving a stamped item into a container programmatically (no drop, no SetLastMoved) - // must also drop the stamp. - [Fact] - public void StampedItemAddedToContainer_DropsTheDecayResetStamp() - { - var start = Core._now; - - try - { - var pack = new Container(0xE75); - pack.MoveToWorld(new Point3D(116, 100, 0), Map.Felucca); - - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(117, 100, 0), Map.Felucca); - - item.Movable = false; - Core._now = start + TimeSpan.FromDays(30); - item.Movable = true; - - Assert.NotEqual(default, item.DecayResetTime); - - pack.AddItem(item); - - Assert.Equal(default, item.DecayResetTime); - - pack.Delete(); - } - finally - { - Core._now = start; - } - } - - // A raw Map assignment (e.g. a GM changing Map through props) is a move: it must - // enroll an untracked item for decay. - [Fact] - public void ItemMovedToRealMapViaMapSetter_IsRegisteredForDecay() - { - var item = new Item(0x1234); - Assert.False(DecayScheduler.IsRegistered(item)); - - item.Map = Map.Felucca; - - Assert.True(item.CanDecay()); - Assert.True(DecayScheduler.IsRegistered(item), "Item placed on a map via the Map setter must be tracked."); - - item.Delete(); - } - - // LiftItemDupe places the remainder of a partially lifted ground stack via raw - // Location/Map assignments, with no MoveToWorld fallback: it must still be tracked. - [Fact] - public void PartialLiftOfGroundStack_LeavesRemainderRegisteredForDecay() - { - var stack = new Item(0x1234) { Stackable = true, Amount = 10 }; - stack.MoveToWorld(new Point3D(114, 100, 0), Map.Felucca); - - var remainder = Mobile.LiftItemDupe(stack, 3); - - Assert.NotNull(remainder); - Assert.Equal(7, remainder.Amount); - Assert.Null(remainder.Parent); - Assert.Equal(Map.Felucca, remainder.Map); - Assert.True( - DecayScheduler.IsRegistered(remainder), - "The remainder of a partially lifted ground stack must be tracked for decay." - ); - - stack.Delete(); - remainder.Delete(); - } - - // Dropping into a container must untrack; taking it back out to the ground must re-track. - [Fact] - public void ItemMovedIntoContainerThenBackToGround_IsRegisteredForDecay() - { - var pack = new Container(0xE75); - pack.MoveToWorld(new Point3D(100, 100, 0), Map.Felucca); - - var item = new Item(0x1234); - pack.AddItem(item); - - Assert.False(DecayScheduler.IsRegistered(item), "Item inside a container must not be tracked for decay."); - - item.Internalize(); - item.MoveToWorld(new Point3D(101, 100, 0), Map.Felucca); - - Assert.True(item.CanDecay()); - Assert.True(DecayScheduler.IsRegistered(item), "Item taken out of a container must be tracked for decay."); - - item.Delete(); - pack.Delete(); - } -} diff --git a/Projects/Server.Tests/Tests/Items/PlayerConstructedStackingTests.cs b/Projects/Server.Tests/Tests/Items/PlayerConstructedStackingTests.cs deleted file mode 100644 index 955a87293..000000000 --- a/Projects/Server.Tests/Tests/Items/PlayerConstructedStackingTests.cs +++ /dev/null @@ -1,95 +0,0 @@ -using Xunit; - -namespace Server.Tests; - -[Collection("Sequential Server Tests")] -public class PlayerConstructedStackingTests -{ - // PlayerConstructed is per-instance provenance, and stack operations were written when no - // item carried any. Merging keeps the receiver's copy of a field and splitting rebuilds one - // half from a fixed list of fields, so a flag that is not accounted for in both places is - // one that ordinary stacking can launder or erase. - - // Stands in for a real stackable type. LiftItemDupe builds the remainder through the - // parameterless constructor and copies only a fixed list of fields onto it -- Stackable is - // not on that list -- so the remainder is only stackable if the type restores it the way - // every genuine stackable does. - private class StackableItem : Item - { - public StackableItem() => Stackable = true; - - public StackableItem(Serial serial) : base(serial) => Stackable = true; - } - - private static StackableItem MakeStack(Serial serial, int amount, bool playerConstructed) => - new(serial) { Amount = amount, PlayerConstructed = playerConstructed }; - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void CanStackWith_IsTrueWhenProvenanceMatches(bool playerConstructed) - { - var first = MakeStack((Serial)0x1, 5, playerConstructed); - var second = MakeStack((Serial)0x2, 7, playerConstructed); - - try - { - Assert.True(first.CanStackWith(second)); - } - finally - { - first.Delete(); - second.Delete(); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void LiftItemDupe_CopiesPlayerConstructedToRemainder(bool playerConstructed) - { - var stack = MakeStack((Serial)0x1, 10, playerConstructed); - Item remainder = null; - - try - { - remainder = Mobile.LiftItemDupe(stack, 4); - - Assert.NotNull(remainder); - Assert.NotSame(stack, remainder); - Assert.Equal(4, stack.Amount); - Assert.Equal(6, remainder.Amount); - Assert.Equal(playerConstructed, remainder.PlayerConstructed); - } - finally - { - stack.Delete(); - remainder?.Delete(); - } - } - - [Fact] - public void SplitHalvesRemainStackableWithEachOther() - { - // The two halves of a split must still be one pile's worth: if the split dropped the - // flag, the remainder would no longer stack back onto what it came from. - var stack = MakeStack((Serial)0x1, 10, true); - Item remainder = null; - - try - { - remainder = Mobile.LiftItemDupe(stack, 4); - Assert.NotNull(remainder); - - Assert.True(stack.CanStackWith(remainder)); - Assert.True(stack.StackWith(null, remainder, false)); - Assert.Equal(10, stack.Amount); - Assert.True(stack.PlayerConstructed); - } - finally - { - stack.Delete(); - remainder?.Delete(); - } - } -} diff --git a/Projects/Server.Tests/Tests/Json/JsonDiscoverableTypeAttributeTests.cs b/Projects/Server.Tests/Tests/Json/JsonDiscoverableTypeAttributeTests.cs deleted file mode 100644 index 437b674ff..000000000 --- a/Projects/Server.Tests/Tests/Json/JsonDiscoverableTypeAttributeTests.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Server.Json; -using Xunit; - -namespace Server.Tests.Json; - -public class JsonDiscoverableTypeAttributeTests -{ - [JsonDiscoverableType] - private sealed class DefaultName { } - - [JsonDiscoverableType("custom")] - private sealed class Overridden { } - - [Fact] - public void DefaultDiscriminator_IsNull() - { - var attr = (JsonDiscoverableTypeAttribute)System.Attribute.GetCustomAttribute( - typeof(DefaultName), typeof(JsonDiscoverableTypeAttribute), false); - Assert.NotNull(attr); - Assert.Null(attr.Discriminator); - } - - [Fact] - public void OverrideDiscriminator_IsReturned() - { - var attr = (JsonDiscoverableTypeAttribute)System.Attribute.GetCustomAttribute( - typeof(Overridden), typeof(JsonDiscoverableTypeAttribute), false); - Assert.Equal("custom", attr.Discriminator); - } -} diff --git a/Projects/Server.Tests/Tests/Json/Rectangle3DConverterTests.cs b/Projects/Server.Tests/Tests/Json/Rectangle3DConverterTests.cs deleted file mode 100644 index b91c521dc..000000000 --- a/Projects/Server.Tests/Tests/Json/Rectangle3DConverterTests.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Text.Json; -using Server.Json; -using Xunit; - -namespace Server.Tests.Json; - -public class Rectangle3DConverterTests -{ - private static Rectangle3D RoundTrip(Rectangle3D value) - { - var json = JsonSerializer.Serialize(value, JsonConfig.DefaultOptions); - return JsonSerializer.Deserialize(json, JsonConfig.DefaultOptions); - } - - [Theory] - // homeRange-style full vertical range (z1=-128, depth 256 -> z2=128): must survive losslessly. - [InlineData(100, 100, -128, 11, 11, 256)] - // The omitted-z sentinel itself (z1=-128, z2=127 -> depth 255). - [InlineData(100, 100, -128, 11, 11, 255)] - // Ordinary bounds with a meaningful z. - [InlineData(100, 100, 0, 11, 11, 5)] - [InlineData(100, 100, 5, 11, 11, 122)] // z2 == 127 but z1 != -128 - [InlineData(100, 100, -128, 11, 11, 200)] // z1 == -128 but z2 != 127 - public void RoundTrips(int x, int y, int z, int w, int h, int d) - { - var rect = new Rectangle3D(x, y, z, w, h, d); - Assert.Equal(rect, RoundTrip(rect)); - } - - [Fact] - public void FullRangeSentinel_OmitsZ() - { - // z1=-128, z2=127 is the only case z is omitted. - var json = JsonSerializer.Serialize(new Rectangle3D(0, 0, -128, 10, 10, 255), JsonConfig.DefaultOptions); - Assert.DoesNotContain("z1", json); - Assert.DoesNotContain("z2", json); - } - - [Fact] - public void HomeRangeBounds_WritesZ() - { - var json = JsonSerializer.Serialize(new Rectangle3D(0, 0, -128, 10, 10, 256), JsonConfig.DefaultOptions); - Assert.Contains("\"z1\":", json); - Assert.Contains("\"z2\":", json); - } -} diff --git a/Projects/Server.Tests/Tests/Localization/LocalizationEntryTests.cs b/Projects/Server.Tests/Tests/Localization/LocalizationEntryTests.cs index 154cfe7a6..f10c21555 100644 --- a/Projects/Server.Tests/Tests/Localization/LocalizationEntryTests.cs +++ b/Projects/Server.Tests/Tests/Localization/LocalizationEntryTests.cs @@ -2,7 +2,7 @@ using Xunit; namespace Server.Tests; -[Collection("Sequential Server Tests")] +[Collection("Sequential Tests")] public class LocalizationEntryTests { [Fact] diff --git a/Projects/Server.Tests/Tests/Maps/CanFitItemTests.cs b/Projects/Server.Tests/Tests/Maps/CanFitItemTests.cs deleted file mode 100644 index 57a6431ef..000000000 --- a/Projects/Server.Tests/Tests/Maps/CanFitItemTests.cs +++ /dev/null @@ -1,296 +0,0 @@ -using Server.Items; -using Xunit; - -namespace Server.Tests.Maps; - -/// -/// Tests for CanFitItem which allows Surface+Impassable tiles (tables, furniture) as valid surfaces. -/// -[Collection("Sequential Server Tests")] -public class CanFitItemTests -{ - private void SkipIfNoTileData() - { - Skip.If(!ServerFixture.TileDataLoaded, "TileData not loaded - client files required"); - } - - [Fact] - public void CanFitItem_InternalMapReturnsFalse() - { - var result = Map.Internal.CanFitItem(100, 100, 0, 1); - - Assert.False(result); - } - - [Fact] - public void CanFitItem_InvalidCoordinatesReturnsFalse() - { - var map = Map.Felucca; - - Assert.False(map.CanFitItem(-1, 100, 0, 1)); - Assert.False(map.CanFitItem(100, -1, 0, 1)); - Assert.False(map.CanFitItem(map.Width + 1, 100, 0, 1)); - Assert.False(map.CanFitItem(100, map.Height + 1, 0, 1)); - } - - private const int TestLandX = 1500; - private const int TestLandY = 1600; - - [Fact] - public void CanFitItem_LandSurfaceReturnsTrue() - { - var map = Map.Felucca; - var avgZ = map.GetAverageZ(TestLandX, TestLandY); - - var result = map.CanFitItem(TestLandX, TestLandY, avgZ, 1); - - Assert.True(result, $"Expected land at Z={avgZ} to be valid surface for items"); - } - - [Fact] - public void CanFitItem_AboveLandWithNoSurfaceReturnsFalse() - { - var map = Map.Felucca; - var avgZ = map.GetAverageZ(TestLandX, TestLandY); - - // Try to place item 50 units above land with no surface there - var result = map.CanFitItem(TestLandX, TestLandY, avgZ + 50, 1); - - Assert.False(result, "No surface 50 units above land"); - } - - [SkippableFact] - public void CanFitItem_SurfaceImpassableMultiIsValidSurface() - { - SkipIfNoTileData(); - Skip.If(ServerFixture.SurfaceImpassableTileId == 0, "No Surface+Impassable tile found in TileData"); - - var map = Map.Felucca; - const int x = 1700; - const int y = 1700; - - TestMulti multi = null; - try - { - // Get the actual tile height from TileData - var tileData = TileData.ItemTable[ServerFixture.SurfaceImpassableTileId]; - var tileHeight = tileData.CalcHeight; - const int tileZ = 10; - - // Create a multi with a Surface+Impassable tile (like a table) at Z=10 - multi = CreateSurfaceImpassableMulti(map, new Point3D(x, y, 0), surfaceZ: tileZ); - - // CanFitItem should treat Surface+Impassable as a valid surface - var surfaceTop = tileZ + tileHeight; - var result = map.CanFitItem(x, y, surfaceTop, 1); - - Assert.True(result, $"Surface+Impassable tile should be valid surface for items at Z={surfaceTop}"); - } - finally - { - multi?.Delete(); - } - } - - [SkippableFact] - public void CanFitItem_CanFitDoesNotAllowSurfaceImpassable() - { - SkipIfNoTileData(); - Skip.If(ServerFixture.SurfaceImpassableTileId == 0, "No Surface+Impassable tile found in TileData"); - - var map = Map.Felucca; - const int x = 1750; - const int y = 1750; - - TestMulti multi = null; - try - { - // Get the actual tile height from TileData - var tileData = TileData.ItemTable[ServerFixture.SurfaceImpassableTileId]; - var tileHeight = tileData.CalcHeight; - const int tileZ = 10; - - // Create a multi with a Surface+Impassable tile at Z=10 - multi = CreateSurfaceImpassableMulti(map, new Point3D(x, y, 0), surfaceZ: tileZ); - - var surfaceTop = tileZ + tileHeight; - - // Regular CanFit should NOT treat Surface+Impassable as valid (for comparison) - var canFitResult = map.CanFit(x, y, surfaceTop, 1, requireSurface: true); - - // This test documents the difference between CanFit and CanFitItem - // CanFit requires surface && !impassable - Assert.False(canFitResult, "CanFit should NOT allow Surface+Impassable as valid surface"); - } - finally - { - multi?.Delete(); - } - } - - [SkippableFact] - public void CanFitItem_NonMovableWorldItemSurfaceWorks() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1800; - const int y = 1800; - - Item surfaceItem = null; - try - { - // Create a non-movable surface item (like a placed table) - surfaceItem = new Item(ServerFixture.SurfaceTileId) - { - Movable = false - }; - surfaceItem.MoveToWorld(new Point3D(x, y, 10), map); - - var itemTop = 10 + surfaceItem.ItemData.CalcHeight; - var result = map.CanFitItem(x, y, itemTop, 1); - - Assert.True(result, $"Non-movable surface item should be valid surface at Z={itemTop}"); - } - finally - { - surfaceItem?.Delete(); - } - } - - [SkippableFact] - public void CanFitItem_MovableWorldItemNotValidSurface() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1850; - const int y = 1850; - - Item movableItem = null; - try - { - // Create a movable surface item - movableItem = new Item(ServerFixture.SurfaceTileId) - { - Movable = true - }; - movableItem.MoveToWorld(new Point3D(x, y, 10), map); - - var itemTop = 10 + movableItem.ItemData.CalcHeight; - // Try placing at item top - should fail unless there's land there too - var avgZ = map.GetAverageZ(x, y); - - // If avgZ != itemTop, there's no other surface, so it should fail - if (avgZ != itemTop) - { - var result = map.CanFitItem(x, y, itemTop, 1); - Assert.False(result, "Movable item should not count as valid surface"); - } - } - finally - { - movableItem?.Delete(); - } - } - - [SkippableFact] - public void CanFitItem_ImpassableTileBlocksPlacement() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1900; - const int y = 1900; - - TestMulti multi = null; - try - { - // Create a multi with an impassable blocker at Z=10 - multi = CreateImpassableMulti(map, new Point3D(x, y, 0), blockerZ: 10); - - // Try to place item at Z=10 (inside the blocker) - var result = map.CanFitItem(x, y, 10, 5); - - Assert.False(result, "Should not be able to place item inside impassable tile"); - } - finally - { - multi?.Delete(); - } - } - - [SkippableFact] - public void CanFitItem_CanPlaceOnTopOfImpassable() - { - SkipIfNoTileData(); - Skip.If(ServerFixture.SurfaceImpassableTileId == 0, "No Surface+Impassable tile found in TileData"); - - var map = Map.Felucca; - const int x = 1950; - const int y = 1950; - - TestMulti multi = null; - try - { - // Get the actual tile height from TileData - var tileData = TileData.ItemTable[ServerFixture.SurfaceImpassableTileId]; - var tileHeight = tileData.CalcHeight; - const int tileZ = 10; - - // Create a Surface+Impassable tile at Z=10 - multi = CreateSurfaceImpassableMulti(map, new Point3D(x, y, 0), surfaceZ: tileZ); - - // Place item on top should work - var surfaceTop = tileZ + tileHeight; - var result = map.CanFitItem(x, y, surfaceTop, 1); - - Assert.True(result, $"Should be able to place item on top of Surface+Impassable tile at Z={surfaceTop}"); - } - finally - { - multi?.Delete(); - } - } - - private TestMulti CreateSurfaceImpassableMulti(Map map, Point3D location, int surfaceZ) - { - // Use SurfaceImpassableTileId - a tile that has both Surface and Impassable flags - var multi = new TestMulti(new MultiComponentList( - [ - new MultiTileEntry( - ServerFixture.SurfaceImpassableTileId, - 0, 0, (short)surfaceZ, - TileFlag.Surface | TileFlag.Impassable - ) - ])); - multi.MoveToWorld(location, map); - return multi; - } - - private TestMulti CreateImpassableMulti(Map map, Point3D location, int blockerZ) - { - var multi = new TestMulti(new MultiComponentList( - [ - new MultiTileEntry( - ServerFixture.ImpassableTileId, - 0, 0, (short)blockerZ, - TileFlag.Impassable - ) - ])); - multi.MoveToWorld(location, map); - return multi; - } - - private class TestMulti : BaseMulti - { - private readonly MultiComponentList _components; - - public TestMulti(MultiComponentList components) : base(0x1) - { - _components = components ?? MultiComponentList.Empty; - } - - public override MultiComponentList Components => _components; - } -} diff --git a/Projects/Server.Tests/Tests/Maps/CanSpawnMobileTests.cs b/Projects/Server.Tests/Tests/Maps/CanSpawnMobileTests.cs deleted file mode 100644 index c90cce9ea..000000000 --- a/Projects/Server.Tests/Tests/Maps/CanSpawnMobileTests.cs +++ /dev/null @@ -1,163 +0,0 @@ -using Xunit; - -namespace Server.Tests.Maps; - -/// -/// Tests for CanSpawnMobile that don't require TileData (client files). -/// For tests that require client files, see CanSpawnMobileTileDataTests. -/// -[Collection("Sequential Server Tests")] -public class CanSpawnMobileTests -{ - [Fact] - public void CanSpawnMobile_InternalMapReturnsFalse() - { - var result = Map.Internal.CanSpawnMobile(100, 100, -128, 127, false, false, out var spawnZ); - - Assert.False(result); - Assert.Equal(0, spawnZ); - } - - [Fact] - public void CanSpawnMobile_InvalidCoordinatesReturnsFalse() - { - var map = Map.Felucca; - - Assert.False(map.CanSpawnMobile(-1, 100, -128, 127, false, false, out _)); - Assert.False(map.CanSpawnMobile(100, -1, -128, 127, false, false, out _)); - Assert.False(map.CanSpawnMobile(map.Width + 1, 100, -128, 127, false, false, out _)); - Assert.False(map.CanSpawnMobile(100, map.Height + 1, -128, 127, false, false, out _)); - } - - // Use coordinates near Britain which should be walkable land in both test and real data - private const int TestLandX = 1500; - private const int TestLandY = 1600; - - [Fact] - public void CanSpawnMobile_EmptyLocationWithLandReturnsTrue() - { - var map = Map.Felucca; - map.GetAverageZ(TestLandX, TestLandY, out _, out var landZ, out _); - - // Test with full Z range to find land - var result = map.CanSpawnMobile(TestLandX, TestLandY, -128, 127, false, false, out var spawnZ); - - // Should find a valid spawn point (land surface) - Assert.True(result, $"Expected to find valid spawn point on land at ({TestLandX}, {TestLandY})"); - } - - [Fact] - public void CanSpawnMobile_ZRangeExcludingLandFails() - { - var map = Map.Felucca; - map.GetAverageZ(TestLandX, TestLandY, out _, out var avgZ, out _); - - // Use a Z range that excludes the land surface (50+ units above land) - var result = map.CanSpawnMobile(TestLandX, TestLandY, avgZ + 50, avgZ + 100, false, false, out var spawnZ); - - Assert.False(result, $"Expected no spawn point above land at Z={avgZ}"); - } - - [Fact] - public void CanSpawnMobile_CantWalkSkipsLandSurface() - { - var map = Map.Felucca; - // cantWalk=true means the mob can only swim, so land shouldn't be valid - var result = map.CanSpawnMobile(TestLandX, TestLandY, -128, 127, false, true, out var spawnZ); - - Assert.False(result, "cantWalk=true should not find land as valid surface"); - } - - [Fact] - public void CanSpawnMobile_MobileBlocksSpawn() - { - var map = Map.Felucca; - // Use the same test coordinates - map.GetAverageZ(TestLandX, TestLandY, out _, out var landZ, out _); - - Mobile blockingMobile = null; - try - { - // Create a mobile at the land surface - blockingMobile = new TestMobile(); - blockingMobile.MoveToWorld(new Point3D(TestLandX, TestLandY, landZ), map); - - // The mobile should block spawning at its location and Z - var result = map.CanSpawnMobile(TestLandX, TestLandY, landZ - 16, landZ + 16, false, false, out var spawnZ); - - // Land surface is blocked by mobile at the same Z - Assert.False(result, "Mobile should block spawn at same location"); - } - finally - { - blockingMobile?.Delete(); - } - } - - [Fact] - public void CanSpawnMobile_HiddenGMDoesNotBlock() - { - var map = Map.Felucca; - // Use slightly different coordinates to avoid test interference - const int x = TestLandX + 10; - const int y = TestLandY + 10; - map.GetAverageZ(x, y, out _, out var landZ, out _); - - Mobile gm = null; - try - { - // Create a hidden GM mobile - gm = new TestMobile - { - AccessLevel = AccessLevel.GameMaster, - Hidden = true - }; - gm.MoveToWorld(new Point3D(x, y, landZ), map); - - // Hidden GM should not block spawning - var result = map.CanSpawnMobile(x, y, landZ - 16, landZ + 16, false, false, out var spawnZ); - - Assert.True(result, "Hidden GM should not block spawn"); - } - finally - { - gm?.Delete(); - } - } - - [Fact] - public void CanSpawnMobile_MobileAtDifferentZDoesNotBlock() - { - var map = Map.Felucca; - // Use slightly different coordinates to avoid test interference - const int x = TestLandX + 20; - const int y = TestLandY + 20; - map.GetAverageZ(x, y, out _, out var landZ, out _); - - Mobile blockingMobile = null; - try - { - // Create a mobile at Z=landZ+50 (far above the land) - blockingMobile = new TestMobile(); - blockingMobile.MoveToWorld(new Point3D(x, y, landZ + 50), map); - - // Land should still be valid (mobile above doesn't block ground level) - var result = map.CanSpawnMobile(x, y, landZ - 16, landZ + 16, false, false, out var spawnZ); - - Assert.True(result, "Mobile at different Z should not block"); - } - finally - { - blockingMobile?.Delete(); - } - } - - private class TestMobile : Mobile - { - public TestMobile() - { - AccessLevel = AccessLevel.Player; - Hidden = false; - } - } -} diff --git a/Projects/Server.Tests/Tests/Maps/CanSpawnMobileTileDataTests.cs b/Projects/Server.Tests/Tests/Maps/CanSpawnMobileTileDataTests.cs deleted file mode 100644 index 87135f080..000000000 --- a/Projects/Server.Tests/Tests/Maps/CanSpawnMobileTileDataTests.cs +++ /dev/null @@ -1,360 +0,0 @@ -using Server.Items; -using Xunit; - -namespace Server.Tests.Maps; - -/// -/// Tests for CanSpawnMobile that require TileData (client files) to be loaded. -/// These tests will be skipped if client files are not available. -/// Configure client path via MODERNUO_CLIENT_PATH environment variable or place files at C:\Ultima Online Classic. -/// -[Collection("Sequential Server Tests")] -public class CanSpawnMobileTileDataTests -{ - private readonly ServerFixture _fixture; - - public CanSpawnMobileTileDataTests(ServerFixture fixture) - { - _fixture = fixture; - } - - private void SkipIfNoTileData() - { - Skip.If(!ServerFixture.TileDataLoaded, "TileData not loaded - client files required"); - } - - [SkippableFact] - public void CanSpawnMobile_FindsSurfaceOnMulti() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1100; - const int y = 1100; - - TestMulti multi = null; - try - { - // Create a multi with a floor at Z=20 using a real surface tile - multi = CreateFloorMulti(map, new Point3D(x, y, 0), floorZ: 20); - - var result = map.CanSpawnMobile(x, y, 15, 30, false, false, out var spawnZ); - - Assert.True(result); - Assert.True(spawnZ >= 15 && spawnZ <= 30); - } - finally - { - multi?.Delete(); - } - } - - [SkippableFact] - public void CanSpawnMobile_FindsLowestValidSurface() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1200; - const int y = 1200; - - TestMulti multi = null; - try - { - // Create a multi with floors at Z=10 and Z=50 - // Need 16+ units between floors for mobile clearance - multi = CreateTwoFloorMulti(map, new Point3D(x, y, 0), floor1Z: 10, floor2Z: 50); - - var result = map.CanSpawnMobile(x, y, 5, 60, false, false, out var spawnZ); - - Assert.True(result); - // Should find the lowest floor (around Z=10 + tile height) - Assert.True(spawnZ <= 20, $"Expected lowest floor around Z=10-15, got {spawnZ}"); - } - finally - { - multi?.Delete(); - } - } - - [SkippableFact] - public void CanSpawnMobile_ZRangeSelectsCorrectFloor() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1300; - const int y = 1300; - - TestMulti multi = null; - try - { - // Create a multi with floors at Z=10 and Z=50 - // Need 16+ units between floors for mobile clearance - multi = CreateTwoFloorMulti(map, new Point3D(x, y, 0), floor1Z: 10, floor2Z: 50); - - // Request Z range that only includes the second floor - var result = map.CanSpawnMobile(x, y, 45, 60, false, false, out var spawnZ); - - Assert.True(result); - Assert.True(spawnZ >= 45 && spawnZ <= 60, $"Expected second floor around Z=50, got {spawnZ}"); - } - finally - { - multi?.Delete(); - } - } - - [SkippableFact] - public void CanSpawnMobile_LowCeilingBlocksSpawn() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1400; - const int y = 1400; - - TestMulti multi = null; - try - { - // Create a multi with floor at Z=20 and ceiling at Z=28 (only 8 units clearance) - // Mobile height is 16, so this should block - multi = CreateFloorWithCeilingMulti(map, new Point3D(x, y, 0), floorZ: 20, ceilingZ: 28); - - // The low ceiling should block spawning (need 16 units clearance for mobiles) - var result = map.CanSpawnMobile(x, y, 15, 35, false, false, out var spawnZ); - - Assert.False(result); - } - finally - { - multi?.Delete(); - } - } - - [SkippableFact] - public void CanSpawnMobile_HighCeilingAllowsSpawn() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1450; - const int y = 1450; - - TestMulti multi = null; - try - { - // Create a multi with floor at Z=20 and ceiling at Z=50 (30 units clearance) - multi = CreateFloorWithCeilingMulti(map, new Point3D(x, y, 0), floorZ: 20, ceilingZ: 50); - - var result = map.CanSpawnMobile(x, y, 15, 35, false, false, out var spawnZ); - - Assert.True(result); - } - finally - { - multi?.Delete(); - } - } - - [SkippableFact] - public void CanSpawnMobile_BlockerOutsideZRangeIgnored() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1475; - const int y = 1475; - - TestMulti multi = null; - try - { - // Create a multi with floor at Z=20 and a blocker at Z=100 (outside our search range) - multi = CreateFloorWithCeilingMulti(map, new Point3D(x, y, 0), floorZ: 20, ceilingZ: 100); - - // The blocker at Z=100 shouldn't affect spawning in range 15-35 - var result = map.CanSpawnMobile(x, y, 15, 35, false, false, out var spawnZ); - - Assert.True(result); - } - finally - { - multi?.Delete(); - } - } - - [SkippableFact] - public void CanSpawnMobile_WaterSurfaceForSwimmingMob() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1550; - const int y = 1550; - - TestMulti multi = null; - try - { - // Create a multi with a water tile at Z=0 - multi = CreateWaterMulti(map, new Point3D(x, y, 0), waterZ: 0); - - // canSwim=true should find the water surface - var result = map.CanSpawnMobile(x, y, -10, 10, true, false, out var spawnZ); - - Assert.True(result); - } - finally - { - multi?.Delete(); - } - } - - [SkippableFact] - public void CanSpawnMobile_WorldItemSurfaceWorks() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1650; - const int y = 1650; - - Item surfaceItem = null; - try - { - // Create a non-movable item that acts as a surface using a real surface tile ID - surfaceItem = new Item(ServerFixture.SurfaceTileId) - { - Movable = false - }; - surfaceItem.MoveToWorld(new Point3D(x, y, 10), map); - - var result = map.CanSpawnMobile(x, y, 5, 30, false, false, out var spawnZ); - - // Should find either land at Z=0 or the item surface, whichever is lowest - Assert.True(result); - } - finally - { - surfaceItem?.Delete(); - } - } - - [SkippableFact] - public void CanSpawnMobile_MalasBuilding_FindsGroundFloor() - { - SkipIfNoTileData(); - - var map = Map.Malas; - const int x = 991; - const int y = 519; - - var result = map.CanSpawnMobile(x, y, -60, -40, false, false, out var spawnZ); - - Assert.True(result); - Assert.True(spawnZ >= -60 && spawnZ <= -40, $"Expected ground floor around -50, got {spawnZ}"); - } - - [SkippableFact] - public void CanSpawnMobile_MalasBuilding_FindsSecondFloor() - { - SkipIfNoTileData(); - - var map = Map.Malas; - const int x = 991; - const int y = 519; - - var result = map.CanSpawnMobile(x, y, -30, 0, false, false, out var spawnZ); - - Assert.True(result); - Assert.True(spawnZ >= -30 && spawnZ <= 0, $"Expected second floor, got {spawnZ}"); - } - - [SkippableFact] - public void CanSpawnMobile_MalasBuilding_LowestFloorPreferred() - { - SkipIfNoTileData(); - - var map = Map.Malas; - const int x = 991; - const int y = 519; - - var result = map.CanSpawnMobile(x, y, -60, 0, false, false, out var spawnZ); - - Assert.True(result); - Assert.True(spawnZ <= -40, $"Expected lowest floor around -50, got {spawnZ}"); - } - - private TestMulti CreateFloorMulti(Map map, Point3D location, int floorZ) - { - var multi = new TestMulti(CreateFloorComponents(floorZ)); - multi.MoveToWorld(location, map); - return multi; - } - - private TestMulti CreateTwoFloorMulti(Map map, Point3D location, int floor1Z, int floor2Z) - { - var multi = new TestMulti(CreateTwoFloorComponents(floor1Z, floor2Z)); - multi.MoveToWorld(location, map); - return multi; - } - - private TestMulti CreateFloorWithCeilingMulti(Map map, Point3D location, int floorZ, int ceilingZ) - { - var multi = new TestMulti(CreateFloorWithCeilingComponents(floorZ, ceilingZ)); - multi.MoveToWorld(location, map); - return multi; - } - - private TestMulti CreateWaterMulti(Map map, Point3D location, int waterZ) - { - var multi = new TestMulti(CreateWaterComponents(waterZ)); - multi.MoveToWorld(location, map); - return multi; - } - - private MultiComponentList CreateFloorComponents(int floorZ) - { - return new MultiComponentList( - [ - new MultiTileEntry(ServerFixture.SurfaceTileId, 0, 0, (short)floorZ, TileFlag.Surface) - ]); - } - - private MultiComponentList CreateTwoFloorComponents(int floor1Z, int floor2Z) - { - return new MultiComponentList( - [ - new MultiTileEntry(ServerFixture.SurfaceTileId, 0, 0, (short)floor1Z, TileFlag.Surface), - new MultiTileEntry(ServerFixture.SurfaceTileId, 0, 0, (short)floor2Z, TileFlag.Surface) - ]); - } - - private MultiComponentList CreateFloorWithCeilingComponents(int floorZ, int ceilingZ) - { - return new MultiComponentList( - [ - new MultiTileEntry(ServerFixture.SurfaceTileId, 0, 0, (short)floorZ, TileFlag.Surface), - new MultiTileEntry(ServerFixture.ImpassableTileId, 0, 0, (short)ceilingZ, TileFlag.Impassable) - ]); - } - - private MultiComponentList CreateWaterComponents(int waterZ) - { - return new MultiComponentList( - [ - new MultiTileEntry(ServerFixture.WetTileId, 0, 0, (short)waterZ, TileFlag.Wet) - ]); - } - - private class TestMulti : BaseMulti - { - private readonly MultiComponentList _components; - - public TestMulti(MultiComponentList components) : base(0x1) - { - _components = components ?? MultiComponentList.Empty; - } - - public override MultiComponentList Components => _components; - } -} diff --git a/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs deleted file mode 100644 index bfbc18e94..000000000 --- a/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs +++ /dev/null @@ -1,546 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Accounting; -using Server.Network; -using Server.Tests.Network; -using Xunit; - -namespace Server.Tests.Maps; - -[Collection("Sequential Server Tests")] -public class ClientEnumeratorTests -{ - [Fact] - public void ClientEnumerator_FiltersByBoundsAndOrder() - { - var map = Map.Felucca; - var rect = new Rectangle2D(100, 100, 32, 32); - - var clients = new (NetState, Mobile)[3]; - try - { - clients[0] = CreateClientWithMobile(map, new Point3D(105, 105, 0)); - clients[1] = CreateClientWithMobile(map, new Point3D(130, 130, 0)); - clients[2] = CreateClientWithMobile(map, new Point3D(90, 90, 0)); - - var found = new List(); - foreach (var ns in map.GetClientsInBounds(rect)) - { - found.Add(ns); - } - - Assert.Equal(2, found.Count); - Assert.All(found, ns => Assert.True(rect.Contains(ns.Mobile.Location))); - Assert.Equal(new[] { clients[0].Item1, clients[1].Item1 }, found); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientEnumerator_SkipsNullMobiles() - { - var map = Map.Felucca; - var rect = new Rectangle2D(200, 200, 16, 16); - - var clients = new (NetState, Mobile)[3]; - try - { - clients[0] = CreateClientWithMobile(map, new Point3D(205, 205, 0)); - clients[1] = CreateClientWithMobile(map, new Point3D(206, 205, 0)); - clients[2] = CreateClientWithMobile(map, new Point3D(207, 205, 0)); - - // Remove the mobile from the second client - clients[1].Item2.Delete(); - clients[1].Item1.Mobile = null; - - var found = new List(); - foreach (var ns in map.GetClientsInBounds(rect)) - { - found.Add(ns); - } - - Assert.Equal(2, found.Count); - Assert.Equal(new[] { clients[0].Item1, clients[2].Item1 }, found); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientEnumerator_RespectsMakeBoundsInclusiveFlag() - { - var map = Map.Felucca; - var rect = new Rectangle2D(300, 300, 1, 1); - - var clients = new (NetState, Mobile)[1]; - try - { - clients[0] = CreateClientWithMobile(map, new Point3D(301, 301, 0)); - - var enumerator = map.GetClientsInBounds(rect, makeBoundsInclusive: true).GetEnumerator(); - - Assert.True(enumerator.MoveNext()); - Assert.Equal(clients[0].Item1, enumerator.Current); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.ClientBoundsEnumerable(null, Rectangle2D.Empty, false).GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void ClientEnumerator_ThrowsOnVersionChange() - { - var map = Map.Felucca; - var rect = new Rectangle2D(400, 400, 16, 16); - - var clients = new[] - { - CreateClientWithMobile(map, new Point3D(405, 405, 0)), - CreateClientWithMobile(map, new Point3D(406, 405, 0)) - }; - - try - { - var enumerator = map.GetClientsInBounds(rect).GetEnumerator(); - Assert.True(enumerator.MoveNext()); - - clients[1].Item2.Delete(); - - // Ref structs cannot be captured in lambdas, so we test the exception directly - var exceptionThrown = false; - try - { - enumerator.MoveNext(); - } - catch (InvalidOperationException) - { - exceptionThrown = true; - } - - Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientEnumerator_StepsAcrossSectors() - { - var map = Map.Felucca; - var rect = new Rectangle2D(500, 500, Map.SectorSize * 2, Map.SectorSize * 2); - - var clients = new[] - { - CreateClientWithMobile(map, new Point3D(rect.X + 1, rect.Y + 1, 0)), - CreateClientWithMobile(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + 1, 0)), - CreateClientWithMobile(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + Map.SectorSize + 1, 0)) - }; - - try - { - var result = new List(); - foreach (var ns in map.GetClientsInBounds(rect)) - { - result.Add(ns); - } - - Assert.Equal(new[] { clients[0].Item1, clients[1].Item1, clients[2].Item1 }, result); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientEnumerator_MapBoundsAreClamped() - { - var map = Map.Felucca; - var width = map.Width; - var height = map.Height; - - var rect = new Rectangle2D(width - Map.SectorSize - 2, height - Map.SectorSize - 2, Map.SectorSize * 2, Map.SectorSize * 2); - - var clients = new[] - { - CreateClientWithMobile(map, new Point3D(width - 2, height - 2, 0)) - }; - - try - { - var enumerator = map.GetClientsInBounds(rect).GetEnumerator(); - - Assert.True(enumerator.MoveNext()); - Assert.Equal(clients[0].Item1, enumerator.Current); - Assert.False(enumerator.MoveNext()); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientAtEnumerator_FiltersExactLocation() - { - var map = Map.Felucca; - var location = new Point3D(600, 600, 0); - var differentLocation = new Point3D(601, 600, 0); - - var clients = new (NetState, Mobile)[3]; - try - { - clients[0] = CreateClientWithMobile(map, location); - clients[1] = CreateClientWithMobile(map, location); - clients[2] = CreateClientWithMobile(map, differentLocation); - - var found = new List(); - foreach (var ns in map.GetClientsAt(location)) - { - found.Add(ns); - } - - Assert.Equal(2, found.Count); - Assert.All(found, ns => - { - Assert.NotNull(ns.Mobile); - Assert.Equal(location.X, ns.Mobile.X); - Assert.Equal(location.Y, ns.Mobile.Y); - }); - Assert.Contains(clients[0].Item1, found); - Assert.Contains(clients[1].Item1, found); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientAtEnumerator_SkipsNullMobiles() - { - var map = Map.Felucca; - var location = new Point3D(650, 650, 0); - - var clients = new (NetState, Mobile)[3]; - try - { - clients[0] = CreateClientWithMobile(map, location); - clients[1] = CreateClientWithMobile(map, location); - clients[2] = CreateClientWithMobile(map, location); - - // Remove the mobile from the second client - clients[1].Item2.Delete(); - clients[1].Item1.Mobile = null; - - var found = new List(); - foreach (var ns in map.GetClientsAt(location)) - { - found.Add(ns); - } - - Assert.Equal(2, found.Count); - Assert.Equal(new[] { clients[0].Item1, clients[2].Item1 }, found); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientAtEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.ClientAtEnumerable(null, new Point2D(0, 0)).GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void ClientAtEnumerator_ThrowsOnVersionChange() - { - var map = Map.Felucca; - var location = new Point3D(750, 750, 0); - - var clients = new[] - { - CreateClientWithMobile(map, location), - CreateClientWithMobile(map, location) - }; - - try - { - var enumerator = map.GetClientsAt(location).GetEnumerator(); - Assert.True(enumerator.MoveNext()); - - clients[1].Item2.Delete(); - - // Ref structs cannot be captured in lambdas, so we test the exception directly - var exceptionThrown = false; - try - { - enumerator.MoveNext(); - } - catch (InvalidOperationException) - { - exceptionThrown = true; - } - - Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientAtEnumerator_UsesDifferentPoint3DOverloads() - { - var map = Map.Felucca; - var location = new Point3D(800, 800, 5); - - var clients = new (NetState, Mobile)[1]; - try - { - clients[0] = CreateClientWithMobile(map, location); - - // Test Point3D overload - var found1 = new List(); - foreach (var ns in map.GetClientsAt(location)) - { - found1.Add(ns); - } - - // Test (int, int) overload - should find the same client (Z is ignored) - var found2 = new List(); - foreach (var ns in map.GetClientsAt(location.X, location.Y)) - { - found2.Add(ns); - } - - // Test Point2D overload - var found3 = new List(); - foreach (var ns in map.GetClientsAt(new Point2D(location.X, location.Y))) - { - found3.Add(ns); - } - - Assert.Single(found1); - Assert.Equal(clients[0].Item1, found1[0]); - Assert.Equal(found1, found2); - Assert.Equal(found1, found3); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientEnumerator_GetClientsInRange() - { - var map = Map.Felucca; - var center = new Point3D(900, 900, 0); - const int range = 5; - - var clients = new (NetState, Mobile)[3]; - try - { - clients[0] = CreateClientWithMobile(map, new Point3D(902, 902, 0)); // Within range - clients[1] = CreateClientWithMobile(map, new Point3D(898, 898, 0)); // Within range - clients[2] = CreateClientWithMobile(map, new Point3D(910, 910, 0)); // Outside range (906+ is outside) - - var found = new List(); - foreach (var ns in map.GetClientsInRange(center, range)) - { - found.Add(ns); - } - - // GetClientsInRange uses a bounding rectangle, not circular distance - // Range of 5 means rectangle from (895, 895) to (905, 905) - Assert.Equal(2, found.Count); - Assert.Contains(clients[0].Item1, found); - Assert.Contains(clients[1].Item1, found); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientEnumerator_DeletedMobilesAreSkipped() - { - var map = Map.Felucca; - var rect = new Rectangle2D(1000, 1000, 16, 16); - - var clients = new (NetState, Mobile)[3]; - try - { - clients[0] = CreateClientWithMobile(map, new Point3D(1005, 1005, 0)); - clients[1] = CreateClientWithMobile(map, new Point3D(1006, 1005, 0)); - clients[2] = CreateClientWithMobile(map, new Point3D(1007, 1005, 0)); - - // Delete the mobile (but not the NetState) - clients[1].Item2.Delete(); - - var found = new List(); - foreach (var ns in map.GetClientsInBounds(rect)) - { - found.Add(ns); - } - - // Should skip the client whose mobile was deleted - Assert.Equal(2, found.Count); - Assert.Contains(clients[0].Item1, found); - Assert.Contains(clients[2].Item1, found); - } - finally - { - DeleteAll(clients); - } - } - - private class MockAccount : IAccount - { - public int TotalGold { get; } - public int TotalPlat { get; } - public bool DepositGold(int amount) => throw new NotImplementedException(); - public bool DepositPlat(int amount) => throw new NotImplementedException(); - public bool WithdrawGold(int amount) => throw new NotImplementedException(); - public bool WithdrawPlat(int amount) => throw new NotImplementedException(); - public long GetTotalGold() => throw new NotImplementedException(); - public int CompareTo(IAccount other) => throw new NotImplementedException(); - public string Username { get; } - public string Email { get; set; } - public AccessLevel AccessLevel { get; set; } - public int Length { get; } - public int Limit { get; set; } = 6; // Default to 6 character slots - public int Count { get; } - - private readonly Dictionary _mobiles = new(); - public Mobile this[int index] - { - get => _mobiles.GetValueOrDefault(index); - set => _mobiles[index] = value; - } - - public DateTime Created { get; set; } - public Serial Serial { get; } - public void Deserialize(IGenericReader reader) => throw new NotImplementedException(); - public void Serialize(IGenericWriter writer) => throw new NotImplementedException(); - public bool Deleted { get; } - public void Delete() => throw new NotImplementedException(); - public bool TrySetUsername(string username) => throw new NotImplementedException(); - public void SetPassword(string password) => throw new NotImplementedException(); - public bool CheckPassword(string password) => throw new NotImplementedException(); - } - - private static (NetState, Mobile) CreateClientWithMobile(Map map, Point3D location) - { - // Create test NetState with real socket and buffers - var ns = PacketTestUtilities.CreateTestNetState(); - - // Assign a mock account to avoid null reference issues - ns.Account = new MockAccount(); - - // Use a unique serial for each mobile - var serial = World.NewMobile; - var mobile = new Mobile(serial); - mobile.DefaultMobileInit(); - - // Set the NetState on the mobile BEFORE moving it to the world - // so the sector's client list gets updated properly - ns.Mobile = mobile; - mobile.NetState = ns; - - mobile.MoveToWorld(location, map); - return (ns, mobile); - } - - [Fact] - public void ClientEnumerator_ZeroRangeReturnsOnlyCenter() - { - var map = Map.Felucca; - var center = new Point3D(950, 950, 0); - const int range = 0; - - var clients = new (NetState, Mobile)[2]; - try - { - clients[0] = CreateClientWithMobile(map, center); // Exact center - clients[1] = CreateClientWithMobile(map, new Point3D(951, 950, 0)); // 1 tile away - - var found = new List(); - foreach (var ns in map.GetClientsInRange(center, range)) - { - found.Add(ns); - } - - Assert.Single(found); - Assert.Equal(clients[0].Item1, found[0]); - } - finally - { - DeleteAll(clients); - } - } - - [Fact] - public void ClientEnumerator_NegativeRangeCreates1x1Bounds() - { - var map = Map.Felucca; - var center = new Point3D(1050, 1050, 0); - const int range = -5; - - var clients = new (NetState, Mobile)[2]; - try - { - clients[0] = CreateClientWithMobile(map, center); - clients[1] = CreateClientWithMobile(map, new Point3D(1051, 1050, 0)); // 1 tile away - - var found = new List(); - foreach (var ns in map.GetClientsInRange(center, range)) - { - found.Add(ns); - } - - // With negative range creating a 1x1 bounds, only exact center matches - Assert.Single(found); - Assert.Equal(clients[0].Item1, found[0]); - } - finally - { - DeleteAll(clients); - } - } - - private static void DeleteAll((NetState state, Mobile m)[] clients) - { - for (var i = 0; i < clients.Length; i++) - { - if (clients[i].state != null) - { - clients[i].state.Mobile = null; - clients[i].state.Dispose(); - } - clients[i].m?.Delete(); - } - } -} - diff --git a/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs deleted file mode 100644 index 458409705..000000000 --- a/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs +++ /dev/null @@ -1,605 +0,0 @@ -using System; -using System.Collections.Generic; -using Xunit; - -namespace Server.Tests.Maps; - -[Collection("Sequential Server Tests")] -public class ItemByDistanceEnumeratorTests -{ - [Fact] - public void ItemByDistanceEnumerator_ReturnsNearbyItems() - { - var map = Map.Felucca; - var center = new Point3D(100, 100, 0); - const int range = 5; - - var Items = new TestItem[3]; - try - { - Items[0] = CreateItem(map, new Point3D(102, 102, 0)); // Within range - Items[1] = CreateItem(map, new Point3D(98, 98, 0)); // Within range - Items[2] = CreateItem(map, new Point3D(110, 110, 0)); // Outside range - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - } - - Assert.Equal(2, found.Count); - Assert.Contains(Items[0], found); - Assert.Contains(Items[1], found); - Assert.DoesNotContain(Items[2], found); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_DeletedItemsAreSkipped() - { - var map = Map.Felucca; - var center = new Point3D(200, 200, 0); - const int range = 5; - - var Items = new TestItem[3]; - try - { - Items[0] = CreateItem(map, new Point3D(202, 202, 0)); - Items[1] = CreateItem(map, new Point3D(203, 202, 0)); - Items[2] = CreateItem(map, new Point3D(204, 202, 0)); - - Items[1].Delete(); - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - } - - Assert.Equal(2, found.Count); - Assert.Contains(Items[0], found); - Assert.Contains(Items[2], found); - Assert.DoesNotContain(Items[1], found); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_ReturnsMinDistance() - { - var map = Map.Felucca; - var center = new Point3D(300, 300, 0); - const int range = 10; - - var Items = new TestItem[2]; - try - { - Items[0] = CreateItem(map, new Point3D(305, 305, 0)); - Items[1] = CreateItem(map, new Point3D(302, 302, 0)); - - var foundWithDistance = new List<(Item, int)>(); - foreach (var result in map.GetItemsInRangeByDistance(center, range)) - { - foundWithDistance.Add(result); - } - - Assert.Equal(2, foundWithDistance.Count); - // Each Item should have a non-negative min distance - Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_OrderedBySector() - { - var map = Map.Felucca; - var center = new Point3D(400, 400, 0); - const int range = Map.SectorSize * 2; - - var Items = new TestItem[3]; - try - { - // Place Items in different sectors - Items[0] = CreateItem(map, new Point3D(center.X + 2, center.Y + 2, 0)); - Items[1] = CreateItem(map, new Point3D(center.X + Map.SectorSize + 2, center.Y + 2, 0)); - Items[2] = CreateItem(map, new Point3D(center.X + 2, center.Y + Map.SectorSize + 2, 0)); - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - } - - Assert.Equal(3, found.Count); - Assert.Contains(Items[0], found); - Assert.Contains(Items[1], found); - Assert.Contains(Items[2], found); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_MapNullYieldsEmpty() - { - var center = new Point2D(0, 0); - var bounds = new Rectangle2D(center.m_X - 10, center.m_Y - 10, 21, 21); - var enumerator = new Map.ItemDistanceEnumerable(null, bounds, center, false).GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void ItemByDistanceEnumerator_ThrowsOnVersionChange() - { - var map = Map.Felucca; - var center = new Point3D(500, 500, 0); - const int range = 5; - - var Items = new[] - { - CreateItem(map, new Point3D(502, 502, 0)), - CreateItem(map, new Point3D(503, 502, 0)) - }; - - try - { - var enumerator = map.GetItemsInRangeByDistance(center, range).GetEnumerator(); - Assert.True(enumerator.MoveNext()); - - Items[1].Delete(); - - // Ref structs cannot be captured in lambdas, so we test the exception directly - var exceptionThrown = false; - try - { - enumerator.MoveNext(); - } - catch (InvalidOperationException) - { - exceptionThrown = true; - } - - Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_ZeroRangeReturnsOnlyCenter() - { - var map = Map.Felucca; - var center = new Point3D(600, 600, 0); - const int range = 0; - - var Items = new TestItem[2]; - try - { - Items[0] = CreateItem(map, center); // Exact center - Items[1] = CreateItem(map, new Point3D(601, 600, 0)); // 1 tile away - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - } - - Assert.Single(found); - Assert.Equal(Items[0], found[0]); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_FiltersByType() - { - var map = Map.Felucca; - var center = new Point3D(700, 700, 0); - const int range = 5; - - var testItem2 = new TestItem2(World.NewItem); - var testItem = new TestItem(World.NewItem); - - try - { - testItem2.MoveToWorld(new Point3D(702, 702, 0), map); - testItem.MoveToWorld(new Point3D(703, 702, 0), map); - - var foundPlayers = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - foundPlayers.Add(Item); - } - - Assert.Single(foundPlayers); - Assert.Equal(testItem2, foundPlayers[0]); - } - finally - { - testItem2.Delete(); - testItem.Delete(); - } - } - - [Fact] - public void ItemByDistanceEnumerator_UsesDifferentPointOverloads() - { - var map = Map.Felucca; - var center = new Point3D(800, 800, 5); - const int range = 5; - - var Items = new TestItem[1]; - try - { - Items[0] = CreateItem(map, new Point3D(802, 802, 0)); - - // Test Point3D overload - var found1 = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found1.Add(Item); - } - - // Test (int, int) overload - var found2 = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center.X, center.Y, range)) - { - found2.Add(Item); - } - - // Test Point2D overload - var found3 = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(new Point2D(center.X, center.Y), range)) - { - found3.Add(Item); - } - - Assert.Single(found1); - Assert.Equal(Items[0], found1[0]); - Assert.Equal(found1, found2); - Assert.Equal(found1, found3); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_RingTraversal() - { - var map = Map.Felucca; - var center = new Point3D(900, 900, 0); - const int range = Map.SectorSize * 2; - - var Items = new TestItem[4]; - try - { - // Place Items in different rings around the center - Items[0] = CreateItem(map, center); // Ring 0 (center sector) - Items[1] = CreateItem(map, new Point3D(center.X + Map.SectorSize, center.Y, 0)); // Ring 1 - Items[2] = CreateItem(map, new Point3D(center.X, center.Y + Map.SectorSize, 0)); // Ring 1 - Items[3] = CreateItem(map, new Point3D(center.X + Map.SectorSize * 2 - 1, center.Y, 0)); // Ring 2 - - var found = new List(); - var distances = new List(); - foreach (var (Item, minDistance) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - distances.Add(minDistance); - } - - // All Items should be found - Assert.Equal(4, found.Count); - Assert.Contains(Items[0], found); - Assert.Contains(Items[1], found); - Assert.Contains(Items[2], found); - Assert.Contains(Items[3], found); - - // Items should be processed by sector distance (ring-based) - // The center Item should have distance 0 - var centerIndex = found.IndexOf(Items[0]); - Assert.Equal(0, distances[centerIndex]); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_MapBoundsAreClamped() - { - var map = Map.Felucca; - var width = map.Width; - var height = map.Height; - - var center = new Point3D(width - 2, height - 2, 0); - const int range = Map.SectorSize * 2; - - var Items = new TestItem[1]; - try - { - Items[0] = CreateItem(map, center); - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - } - - Assert.Single(found); - Assert.Equal(Items[0], found[0]); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_NegativeRangeIsZero() - { - var map = Map.Felucca; - var center = new Point3D(1000, 1000, 0); - const int range = -5; - - var Items = new TestItem[2]; - try - { - Items[0] = CreateItem(map, center); - Items[1] = CreateItem(map, new Point3D(1001, 1000, 0)); // 1 tile away - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - } - - // With negative range creating a 1x1 bounds, only exact center matches - Assert.Single(found); - Assert.Equal(Items[0], found[0]); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_MultipleRings() - { - var map = Map.Felucca; - var center = new Point3D(1100, 1100, 0); - const int range = Map.SectorSize * 3; - - var Items = new List(); - try - { - // Create a grid of Items across multiple sectors - for (var ringOffset = 0; ringOffset <= 2; ringOffset++) - { - for (var side = 0; side < 4; side++) - { - var offset = ringOffset * Map.SectorSize; - var pos = side switch - { - 0 => new Point3D(center.X + offset, center.Y, 0), - 1 => new Point3D(center.X, center.Y + offset, 0), - 2 => new Point3D(center.X - offset, center.Y, 0), - _ => new Point3D(center.X, center.Y - offset, 0) - }; - Items.Add(CreateItem(map, pos)); - } - } - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - } - - // Should find all Items within range - Assert.True(found.Count > 0); - Assert.All(found, Item => - { - var dx = Item.X - center.X; - var dy = Item.Y - center.Y; - var distSq = dx * dx + dy * dy; - Assert.True(distSq <= range * range); - }); - } - finally - { - foreach (var Item in Items) - { - Item?.Delete(); - } - } - } - - private static TestItem CreateItem(Map map, Point3D location) - { - var Item = new TestItem(World.NewItem); - Item.MoveToWorld(location, map); - return Item; - } - - private static void DeleteAll(TestItem[] Items) - { - for (var i = 0; i < Items.Length; i++) - { - Items[i]?.Delete(); - } - } - - [Fact] - public void ItemByDistanceEnumerator_Bounds_FindsItemsInBounds() - { - var map = Map.Felucca; - var bounds = new Rectangle2D(100, 100, 50, 50); - - var Items = new TestItem[3]; - try - { - // Item inside bounds - Items[0] = CreateItem(map, new Point3D(120, 120, 0)); - // Item at edge of bounds - Items[1] = CreateItem(map, new Point3D(149, 149, 0)); - // Item outside bounds - Items[2] = CreateItem(map, new Point3D(200, 200, 0)); - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInBoundsByDistance(bounds)) - { - found.Add(Item); - } - - Assert.Equal(2, found.Count); - Assert.Contains(Items[0], found); - Assert.Contains(Items[1], found); - Assert.DoesNotContain(Items[2], found); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_Bounds_MakeBoundsInclusive() - { - var map = Map.Felucca; - var bounds = new Rectangle2D(100, 100, 50, 50); - - var Items = new TestItem[2]; - try - { - // Item at edge (inclusive) - Items[0] = CreateItem(map, new Point3D(149, 149, 0)); - // Item just outside edge (will be included with makeBoundsInclusive) - Items[1] = CreateItem(map, new Point3D(150, 150, 0)); - - var foundWithoutInclusive = new List(); - foreach (var (Item, _) in map.GetItemsInBoundsByDistance(bounds)) - { - foundWithoutInclusive.Add(Item); - } - - var foundWithInclusive = new List(); - foreach (var (Item, _) in map.GetItemsInBoundsByDistance(bounds, true)) - { - foundWithInclusive.Add(Item); - } - - Assert.Single(foundWithoutInclusive); - Assert.Contains(Items[0], foundWithoutInclusive); - - Assert.Equal(2, foundWithInclusive.Count); - Assert.Contains(Items[0], foundWithInclusive); - Assert.Contains(Items[1], foundWithInclusive); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_Bounds_ReturnsMinDistance() - { - var map = Map.Felucca; - var bounds = new Rectangle2D(300, 300, 20, 20); - - var Items = new TestItem[2]; - try - { - Items[0] = CreateItem(map, new Point3D(305, 305, 0)); - Items[1] = CreateItem(map, new Point3D(315, 315, 0)); - - var foundWithDistance = new List<(Item, int)>(); - foreach (var result in map.GetItemsInBoundsByDistance(bounds)) - { - foundWithDistance.Add(result); - } - - Assert.Equal(2, foundWithDistance.Count); - Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_Bounds_OrdersByProximityToCenter() - { - var map = Map.Felucca; - var bounds = new Rectangle2D(500, 500, 64, 64); - - var Items = new TestItem[3]; - try - { - // Place Items at different distances from center - Items[0] = CreateItem(map, new Point3D(532, 532, 0)); // At center - Items[1] = CreateItem(map, new Point3D(548, 532, 0)); // 16 tiles away - Items[2] = CreateItem(map, new Point3D(563, 563, 0)); // Far corner - - var found = new List<(Item, int)>(); - foreach (var result in map.GetItemsInBoundsByDistance(bounds)) - { - found.Add(result); - } - - Assert.Equal(3, found.Count); - - // Verify ordering by distance - closer Items should be found earlier (lower minDistance) - var Item0Index = found.FindIndex(x => x.Item1 == Items[0]); - var Item1Index = found.FindIndex(x => x.Item1 == Items[1]); - var Item2Index = found.FindIndex(x => x.Item1 == Items[2]); - - // The minDistance should increase (or stay the same) as we go through the list - Assert.True(found[Item0Index].Item2 <= found[Item1Index].Item2); - Assert.True(found[Item1Index].Item2 <= found[Item2Index].Item2); - } - finally - { - DeleteAll(Items); - } - } - - // Test implementation of Item - private class TestItem : Item - { - public TestItem(Serial serial) : base(serial) - { - } - } - - private class TestItem2 : Item - { - public TestItem2(Serial serial) : base(serial) - { - } - } -} - diff --git a/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs deleted file mode 100644 index bc92c3f58..000000000 --- a/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs +++ /dev/null @@ -1,484 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Items; -using Xunit; - -namespace Server.Tests.Maps; - -[Collection("Sequential Server Tests")] -public class ItemEnumeratorTests -{ - [Fact] - public void ItemEnumerator_FiltersByBoundsAndOrder() - { - var map = Map.Felucca; - var rect = new Rectangle2D(100, 100, 32, 32); - - var items = new Item[3]; - try - { - items[0] = CreateItem(map, new Point3D(105, 105, 0)); - items[1] = CreateItem(map, new Point3D(130, 130, 0)); - items[2] = CreateItem(map, new Point3D(90, 90, 0)); - - var found = new List(); - foreach (var item in map.GetItemsInBounds(rect)) - { - found.Add(item); - } - - Assert.Equal(2, found.Count); - Assert.All(found, item => Assert.True(rect.Contains(item.Location))); - Assert.Equal(new[] { items[0], items[1] }, found); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemEnumerator_DeletedItemsAreSkipped() - { - var map = Map.Felucca; - var rect = new Rectangle2D(200, 200, 16, 16); - - var items = new Item[3]; - try - { - items[0] = CreateItem(map, new Point3D(205, 205, 0)); - items[1] = CreateItem(map, new Point3D(206, 205, 0)); - items[2] = CreateItem(map, new Point3D(207, 205, 0)); - - items[1].Delete(); - - var found = new List(); - foreach (var item in map.GetItemsInBounds(rect)) - { - found.Add(item); - } - - Assert.Equal(new[] { items[0], items[2] }, found); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemEnumerator_ItemsWithParentAreSkipped() - { - var map = Map.Felucca; - var rect = new Rectangle2D(250, 250, 16, 16); - - var items = new Item[2]; - var container = new Container(0xE75); - try - { - items[0] = CreateItem(map, new Point3D(255, 255, 0)); - items[1] = CreateItem(map, new Point3D(256, 255, 0)); - container.MoveToWorld(new Point3D(255, 255, 0), map); - - // Move items[1] into the container - it should be skipped - items[1].Parent = container; - - var found = new List(); - foreach (var item in map.GetItemsInBounds(rect)) - { - found.Add(item); - } - - // Should only find items[0] and container, not items[1] (which has a parent) - Assert.Equal(2, found.Count); - Assert.Contains(items[0], found); - Assert.Contains(container, found); - Assert.DoesNotContain(items[1], found); - } - finally - { - DeleteAll(items); - container?.Delete(); - } - } - - [Fact] - public void ItemEnumerator_RespectsMakeBoundsInclusiveFlag() - { - var map = Map.Felucca; - var rect = new Rectangle2D(300, 300, 1, 1); - - var items = new Item[1]; - try - { - items[0] = CreateItem(map, new Point3D(301, 301, 0)); - - var enumerator = map.GetItemsInBounds(rect, makeBoundsInclusive: true).GetEnumerator(); - - Assert.True(enumerator.MoveNext()); - Assert.Equal(items[0], enumerator.Current); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.ItemEnumerator(null, Rectangle2D.Empty, false); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void ItemEnumerator_ThrowsOnVersionChange() - { - var map = Map.Felucca; - var rect = new Rectangle2D(400, 400, 16, 16); - - var items = new[] - { - CreateItem(map, new Point3D(405, 405, 0)), - CreateItem(map, new Point3D(406, 405, 0)) - }; - - try - { - var enumerator = map.GetItemsInBounds(rect).GetEnumerator(); - Assert.True(enumerator.MoveNext()); - - items[1].Delete(); - - // Ref structs cannot be captured in lambdas, so we test the exception directly - var exceptionThrown = false; - try - { - enumerator.MoveNext(); - } - catch (InvalidOperationException) - { - exceptionThrown = true; - } - - Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemEnumerator_StepsAcrossSectors() - { - var map = Map.Felucca; - var rect = new Rectangle2D(500, 500, Map.SectorSize * 2, Map.SectorSize * 2); - - var items = new[] - { - CreateItem(map, new Point3D(rect.X + 1, rect.Y + 1, 0)), - CreateItem(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + 1, 0)), - CreateItem(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + Map.SectorSize + 1, 0)) - }; - - try - { - var result = new List(); - foreach (var item in map.GetItemsInBounds(rect)) - { - result.Add(item); - } - - Assert.Equal(items, result); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemEnumerator_MapBoundsAreClamped() - { - var map = Map.Felucca; - var width = map.Width; - var height = map.Height; - - var rect = new Rectangle2D(width - Map.SectorSize - 2, height - Map.SectorSize - 2, Map.SectorSize * 2, Map.SectorSize * 2); - - var items = new[] - { - CreateItem(map, new Point3D(width - 2, height - 2, 0)) - }; - - try - { - var enumerator = map.GetItemsInBounds(rect).GetEnumerator(); - - Assert.True(enumerator.MoveNext()); - Assert.Equal(items[0], enumerator.Current); - Assert.False(enumerator.MoveNext()); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemAtEnumerator_FiltersExactLocation() - { - var map = Map.Felucca; - var location = new Point3D(600, 600, 0); - - var items = new Item[3]; - try - { - items[0] = CreateItem(map, location); - items[1] = CreateItem(map, location); - items[2] = CreateItem(map, new Point3D(601, 600, 0)); // Different location - - var found = new List(); - foreach (var item in map.GetItemsAt(location)) - { - found.Add(item); - } - - Assert.Equal(2, found.Count); - Assert.Contains(items[0], found); - Assert.Contains(items[1], found); - Assert.DoesNotContain(items[2], found); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemAtEnumerator_DeletedItemsAreSkipped() - { - var map = Map.Felucca; - var location = new Point3D(650, 650, 0); - - var items = new Item[3]; - try - { - items[0] = CreateItem(map, location); - items[1] = CreateItem(map, location); - items[2] = CreateItem(map, location); - - items[1].Delete(); - - var found = new List(); - foreach (var item in map.GetItemsAt(location)) - { - found.Add(item); - } - - Assert.Equal(new[] { items[0], items[2] }, found); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemAtEnumerator_ItemsWithParentAreSkipped() - { - var map = Map.Felucca; - var location = new Point3D(700, 700, 0); - - var items = new Item[2]; - var container = new Container(0xE75); - try - { - items[0] = CreateItem(map, location); - items[1] = CreateItem(map, location); - container.MoveToWorld(location, map); - - // Move items[1] into the container - it should be skipped - items[1].Parent = container; - - var found = new List(); - foreach (var item in map.GetItemsAt(location)) - { - found.Add(item); - } - - Assert.Equal(2, found.Count); - Assert.Contains(items[0], found); - Assert.Contains(container, found); - Assert.DoesNotContain(items[1], found); - } - finally - { - DeleteAll(items); - container?.Delete(); - } - } - - [Fact] - public void ItemAtEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.ItemAtEnumerator(null, new Point2D(0, 0)); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void ItemAtEnumerator_ThrowsOnVersionChange() - { - var map = Map.Felucca; - var location = new Point3D(750, 750, 0); - - var items = new[] - { - CreateItem(map, location), - CreateItem(map, location) - }; - - try - { - var enumerator = map.GetItemsAt(location).GetEnumerator(); - Assert.True(enumerator.MoveNext()); - - items[1].Delete(); - - // Ref structs cannot be captured in lambdas, so we test the exception directly - var exceptionThrown = false; - try - { - enumerator.MoveNext(); - } - catch (InvalidOperationException) - { - exceptionThrown = true; - } - - Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemAtEnumerator_UsesDifferentPoint3DOverloads() - { - var map = Map.Felucca; - var location = new Point3D(800, 800, 5); - - var items = new Item[1]; - try - { - items[0] = CreateItem(map, location); - - // Test Point3D overload - var found1 = new List(); - foreach (var item in map.GetItemsAt(location)) - { - found1.Add(item); - } - - // Test (int, int) overload - should find the same item (Z is ignored) - var found2 = new List(); - foreach (var item in map.GetItemsAt(location.X, location.Y)) - { - found2.Add(item); - } - - // Test Point2D overload - var found3 = new List(); - foreach (var item in map.GetItemsAt(new Point2D(location.X, location.Y))) - { - found3.Add(item); - } - - Assert.Single(found1); - Assert.Equal(items[0], found1[0]); - Assert.Equal(found1, found2); - Assert.Equal(found1, found3); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemEnumerator_ZeroRangeReturnsOnlyCenter() - { - var map = Map.Felucca; - var center = new Point3D(850, 850, 0); - const int range = 0; - - var items = new Item[2]; - try - { - items[0] = CreateItem(map, center); // Exact center - items[1] = CreateItem(map, new Point3D(851, 850, 0)); // 1 tile away - - var found = new List(); - foreach (var item in map.GetItemsInRange(center, range)) - { - found.Add(item); - } - - Assert.Single(found); - Assert.Equal(items[0], found[0]); - } - finally - { - DeleteAll(items); - } - } - - [Fact] - public void ItemEnumerator_NegativeRangeCreates1x1Bounds() - { - var map = Map.Felucca; - var center = new Point3D(900, 900, 0); - const int range = -5; - - var items = new Item[2]; - try - { - items[0] = CreateItem(map, center); - items[1] = CreateItem(map, new Point3D(901, 900, 0)); // 1 tile away - - var found = new List(); - foreach (var item in map.GetItemsInRange(center, range)) - { - found.Add(item); - } - - // With negative range creating a 1x1 bounds, only exact center matches - Assert.Single(found); - Assert.Equal(items[0], found[0]); - } - finally - { - DeleteAll(items); - } - } - - private static Item CreateItem(Map map, Point3D location) - { - var item = new Item(0x1); - item.Movable = false; - item.MoveToWorld(location, map); - return item; - } - - private static void DeleteAll(Item[] items) - { - for (var i = 0; i < items.Length; i++) - { - items[i]?.Delete(); - } - } -} - diff --git a/Projects/Server.Tests/Tests/Maps/MapSelectionTests.cs b/Projects/Server.Tests/Tests/Maps/MapSelectionTests.cs index 60c3904b3..6c34c584a 100644 --- a/Projects/Server.Tests/Tests/Maps/MapSelectionTests.cs +++ b/Projects/Server.Tests/Tests/Maps/MapSelectionTests.cs @@ -4,7 +4,7 @@ using Server.Json; using System.Text.Json.Serialization; using System.Text.Json; -namespace Server.Tests.Maps +namespace Server.Tests.Tests.Maps { public class MapSelectionTests { @@ -40,8 +40,8 @@ namespace Server.Tests.Maps }; // When - var serialized = JsonConfig.Serialize(mapConfig); - var deserialized = JsonSerializer.Deserialize(serialized, JsonConfig.GetOptions()); + string serialized = JsonConfig.Serialize(mapConfig); + TestMapConfig deserialized = JsonSerializer.Deserialize(serialized, JsonConfig.GetOptions()); // Then Assert.Equal(mapConfig.TestMapFlags, deserialized.TestMapFlags); diff --git a/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs deleted file mode 100644 index 3f78377f9..000000000 --- a/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs +++ /dev/null @@ -1,608 +0,0 @@ -using System; -using System.Collections.Generic; -using Xunit; - -namespace Server.Tests.Maps; - -[Collection("Sequential Server Tests")] -public class MobileByDistanceEnumeratorTests -{ - [Fact] - public void MobileByDistanceEnumerator_ReturnsNearbyMobiles() - { - var map = Map.Felucca; - var center = new Point3D(100, 100, 0); - const int range = 5; - - var mobiles = new TestMobile[3]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(102, 102, 0)); // Within range - mobiles[1] = CreateMobile(map, new Point3D(98, 98, 0)); // Within range - mobiles[2] = CreateMobile(map, new Point3D(110, 110, 0)); // Outside range - - var found = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found.Add(mobile); - } - - Assert.Equal(2, found.Count); - Assert.Contains(mobiles[0], found); - Assert.Contains(mobiles[1], found); - Assert.DoesNotContain(mobiles[2], found); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_DeletedMobilesAreSkipped() - { - var map = Map.Felucca; - var center = new Point3D(200, 200, 0); - const int range = 5; - - var mobiles = new TestMobile[3]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(202, 202, 0)); - mobiles[1] = CreateMobile(map, new Point3D(203, 202, 0)); - mobiles[2] = CreateMobile(map, new Point3D(204, 202, 0)); - - mobiles[1].Delete(); - - var found = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found.Add(mobile); - } - - Assert.Equal(2, found.Count); - Assert.Contains(mobiles[0], found); - Assert.Contains(mobiles[2], found); - Assert.DoesNotContain(mobiles[1], found); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_ReturnsMinDistance() - { - var map = Map.Felucca; - var center = new Point3D(300, 300, 0); - const int range = 10; - - var mobiles = new TestMobile[2]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(305, 305, 0)); - mobiles[1] = CreateMobile(map, new Point3D(302, 302, 0)); - - var foundWithDistance = new List<(Mobile, int)>(); - foreach (var result in map.GetMobilesInRangeByDistance(center, range)) - { - foundWithDistance.Add(result); - } - - Assert.Equal(2, foundWithDistance.Count); - // Each mobile should have a non-negative min distance - Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_OrderedBySector() - { - var map = Map.Felucca; - var center = new Point3D(400, 400, 0); - const int range = Map.SectorSize * 2; - - var mobiles = new TestMobile[3]; - try - { - // Place mobiles in different sectors - mobiles[0] = CreateMobile(map, new Point3D(center.X + 2, center.Y + 2, 0)); - mobiles[1] = CreateMobile(map, new Point3D(center.X + Map.SectorSize + 2, center.Y + 2, 0)); - mobiles[2] = CreateMobile(map, new Point3D(center.X + 2, center.Y + Map.SectorSize + 2, 0)); - - var found = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found.Add(mobile); - } - - Assert.Equal(3, found.Count); - Assert.Contains(mobiles[0], found); - Assert.Contains(mobiles[1], found); - Assert.Contains(mobiles[2], found); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_MapNullYieldsEmpty() - { - var center = new Point2D(0, 0); - var bounds = new Rectangle2D(center.m_X - 10, center.m_Y - 10, 21, 21); - var enumerator = new Map.MobileDistanceEnumerable(null, bounds, center, false).GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void MobileByDistanceEnumerator_ThrowsOnVersionChange() - { - var map = Map.Felucca; - var center = new Point3D(500, 500, 0); - const int range = 5; - - var mobiles = new[] - { - CreateMobile(map, new Point3D(502, 502, 0)), - CreateMobile(map, new Point3D(503, 502, 0)) - }; - - try - { - var enumerator = map.GetMobilesInRangeByDistance(center, range).GetEnumerator(); - Assert.True(enumerator.MoveNext()); - - mobiles[1].Delete(); - - // Ref structs cannot be captured in lambdas, so we test the exception directly - var exceptionThrown = false; - try - { - enumerator.MoveNext(); - } - catch (InvalidOperationException) - { - exceptionThrown = true; - } - - Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_ZeroRangeReturnsOnlyCenter() - { - var map = Map.Felucca; - var center = new Point3D(600, 600, 0); - const int range = 0; - - var mobiles = new TestMobile[2]; - try - { - mobiles[0] = CreateMobile(map, center); // Exact center - mobiles[1] = CreateMobile(map, new Point3D(601, 600, 0)); // 1 tile away - - var found = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found.Add(mobile); - } - - Assert.Single(found); - Assert.Equal(mobiles[0], found[0]); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_FiltersByType() - { - var map = Map.Felucca; - var center = new Point3D(700, 700, 0); - const int range = 5; - - var player = new TestPlayerMobile(World.NewMobile); - var npc = new TestMobile(World.NewMobile); - - try - { - player.DefaultMobileInit(); - npc.DefaultMobileInit(); - player.MoveToWorld(new Point3D(702, 702, 0), map); - npc.MoveToWorld(new Point3D(703, 702, 0), map); - - var foundPlayers = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - foundPlayers.Add(mobile); - } - - Assert.Single(foundPlayers); - Assert.Equal(player, foundPlayers[0]); - } - finally - { - player.Delete(); - npc.Delete(); - } - } - - [Fact] - public void MobileByDistanceEnumerator_UsesDifferentPointOverloads() - { - var map = Map.Felucca; - var center = new Point3D(800, 800, 5); - const int range = 5; - - var mobiles = new TestMobile[1]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(802, 802, 0)); - - // Test Point3D overload - var found1 = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found1.Add(mobile); - } - - // Test (int, int) overload - var found2 = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center.X, center.Y, range)) - { - found2.Add(mobile); - } - - // Test Point2D overload - var found3 = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(new Point2D(center.X, center.Y), range)) - { - found3.Add(mobile); - } - - Assert.Single(found1); - Assert.Equal(mobiles[0], found1[0]); - Assert.Equal(found1, found2); - Assert.Equal(found1, found3); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_RingTraversal() - { - var map = Map.Felucca; - var center = new Point3D(900, 900, 0); - const int range = Map.SectorSize * 2; - - var mobiles = new TestMobile[4]; - try - { - // Place mobiles in different rings around the center - mobiles[0] = CreateMobile(map, center); // Ring 0 (center sector) - mobiles[1] = CreateMobile(map, new Point3D(center.X + Map.SectorSize, center.Y, 0)); // Ring 1 - mobiles[2] = CreateMobile(map, new Point3D(center.X, center.Y + Map.SectorSize, 0)); // Ring 1 - mobiles[3] = CreateMobile(map, new Point3D(center.X + Map.SectorSize * 2 - 1, center.Y, 0)); // Ring 2 - - var found = new List(); - var distances = new List(); - foreach (var (mobile, minDistance) in map.GetMobilesInRangeByDistance(center, range)) - { - found.Add(mobile); - distances.Add(minDistance); - } - - // All mobiles should be found - Assert.Equal(4, found.Count); - Assert.Contains(mobiles[0], found); - Assert.Contains(mobiles[1], found); - Assert.Contains(mobiles[2], found); - Assert.Contains(mobiles[3], found); - - // Mobiles should be processed by sector distance (ring-based) - // The center mobile should have distance 0 - var centerIndex = found.IndexOf(mobiles[0]); - Assert.Equal(0, distances[centerIndex]); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_MapBoundsAreClamped() - { - var map = Map.Felucca; - var width = map.Width; - var height = map.Height; - - var center = new Point3D(width - 2, height - 2, 0); - const int range = Map.SectorSize * 2; - - var mobiles = new TestMobile[1]; - try - { - mobiles[0] = CreateMobile(map, center); - - var found = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found.Add(mobile); - } - - Assert.Single(found); - Assert.Equal(mobiles[0], found[0]); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_NegativeRangeIsZero() - { - var map = Map.Felucca; - var center = new Point3D(1000, 1000, 0); - const int range = -5; - - var mobiles = new TestMobile[2]; - try - { - mobiles[0] = CreateMobile(map, center); - mobiles[1] = CreateMobile(map, new Point3D(1001, 1000, 0)); - - var found = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found.Add(mobile); - } - - // With negative range creating a 1x1 bounds, only exact center matches - Assert.Single(found); - Assert.Equal(mobiles[0], found[0]); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_MultipleRings() - { - var map = Map.Felucca; - var center = new Point3D(1100, 1100, 0); - const int range = Map.SectorSize * 3; - - var mobiles = new List(); - try - { - // Create a grid of mobiles across multiple sectors - for (var ringOffset = 0; ringOffset <= 2; ringOffset++) - { - for (var side = 0; side < 4; side++) - { - var offset = ringOffset * Map.SectorSize; - var pos = side switch - { - 0 => new Point3D(center.X + offset, center.Y, 0), - 1 => new Point3D(center.X, center.Y + offset, 0), - 2 => new Point3D(center.X - offset, center.Y, 0), - _ => new Point3D(center.X, center.Y - offset, 0) - }; - mobiles.Add(CreateMobile(map, pos)); - } - } - - var found = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found.Add(mobile); - } - - // Should find all mobiles within range - Assert.True(found.Count > 0); - Assert.All(found, mobile => - { - var dx = mobile.X - center.X; - var dy = mobile.Y - center.Y; - var distSq = dx * dx + dy * dy; - Assert.True(distSq <= range * range); - }); - } - finally - { - foreach (var mobile in mobiles) - { - mobile?.Delete(); - } - } - } - - private static TestMobile CreateMobile(Map map, Point3D location) - { - var mobile = new TestMobile(World.NewMobile); - mobile.DefaultMobileInit(); - mobile.MoveToWorld(location, map); - return mobile; - } - - private static void DeleteAll(TestMobile[] mobiles) - { - for (var i = 0; i < mobiles.Length; i++) - { - mobiles[i]?.Delete(); - } - } - - [Fact] - public void MobileByDistanceEnumerator_Bounds_FindsMobilesInBounds() - { - var map = Map.Felucca; - var bounds = new Rectangle2D(100, 100, 50, 50); - - var mobiles = new TestMobile[3]; - try - { - // Mobile inside bounds - mobiles[0] = CreateMobile(map, new Point3D(120, 120, 0)); - // Mobile at edge of bounds - mobiles[1] = CreateMobile(map, new Point3D(149, 149, 0)); - // Mobile outside bounds - mobiles[2] = CreateMobile(map, new Point3D(200, 200, 0)); - - var found = new List(); - foreach (var (mobile, _) in map.GetMobilesInBoundsByDistance(bounds)) - { - found.Add(mobile); - } - - Assert.Equal(2, found.Count); - Assert.Contains(mobiles[0], found); - Assert.Contains(mobiles[1], found); - Assert.DoesNotContain(mobiles[2], found); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_Bounds_MakeBoundsInclusive() - { - var map = Map.Felucca; - var bounds = new Rectangle2D(100, 100, 50, 50); - - var mobiles = new TestMobile[2]; - try - { - // Mobile at edge (inclusive) - mobiles[0] = CreateMobile(map, new Point3D(149, 149, 0)); - // Mobile just outside edge (will be included with makeBoundsInclusive) - mobiles[1] = CreateMobile(map, new Point3D(150, 150, 0)); - - var foundWithoutInclusive = new List(); - foreach (var (mobile, _) in map.GetMobilesInBoundsByDistance(bounds)) - { - foundWithoutInclusive.Add(mobile); - } - - var foundWithInclusive = new List(); - foreach (var (mobile, _) in map.GetMobilesInBoundsByDistance(bounds, true)) - { - foundWithInclusive.Add(mobile); - } - - Assert.Single(foundWithoutInclusive); - Assert.Contains(mobiles[0], foundWithoutInclusive); - - Assert.Equal(2, foundWithInclusive.Count); - Assert.Contains(mobiles[0], foundWithInclusive); - Assert.Contains(mobiles[1], foundWithInclusive); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_Bounds_ReturnsMinDistance() - { - var map = Map.Felucca; - var bounds = new Rectangle2D(300, 300, 20, 20); - - var mobiles = new TestMobile[2]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(305, 305, 0)); - mobiles[1] = CreateMobile(map, new Point3D(315, 315, 0)); - - var foundWithDistance = new List<(Mobile, int)>(); - foreach (var result in map.GetMobilesInBoundsByDistance(bounds)) - { - foundWithDistance.Add(result); - } - - Assert.Equal(2, foundWithDistance.Count); - Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_Bounds_OrdersByProximityToCenter() - { - var map = Map.Felucca; - var bounds = new Rectangle2D(500, 500, 64, 64); - - var mobiles = new TestMobile[3]; - try - { - // Place mobiles at different distances from center - mobiles[0] = CreateMobile(map, new Point3D(532, 532, 0)); // At center - mobiles[1] = CreateMobile(map, new Point3D(548, 532, 0)); // 16 tiles away - mobiles[2] = CreateMobile(map, new Point3D(563, 563, 0)); // Far corner - - var found = new List<(Mobile, int)>(); - foreach (var result in map.GetMobilesInBoundsByDistance(bounds)) - { - found.Add(result); - } - - Assert.Equal(3, found.Count); - - // Verify ordering by distance - closer mobiles should be found earlier (lower minDistance) - var mobile0Index = found.FindIndex(x => x.Item1 == mobiles[0]); - var mobile1Index = found.FindIndex(x => x.Item1 == mobiles[1]); - var mobile2Index = found.FindIndex(x => x.Item1 == mobiles[2]); - - // The minDistance should increase (or stay the same) as we go through the list - Assert.True(found[mobile0Index].Item2 <= found[mobile1Index].Item2); - Assert.True(found[mobile1Index].Item2 <= found[mobile2Index].Item2); - } - finally - { - DeleteAll(mobiles); - } - } - - // Test implementation of Mobile - private class TestMobile : Mobile - { - public TestMobile(Serial serial) : base(serial) - { - } - } - - private class TestPlayerMobile : Mobile - { - public TestPlayerMobile(Serial serial) : base(serial) - { - } - } -} - diff --git a/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs deleted file mode 100644 index ee932096f..000000000 --- a/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System; -using System.Collections.Generic; -using Xunit; - -namespace Server.Tests.Maps; - -[Collection("Sequential Server Tests")] -public class MobileEnumeratorTests -{ - [Fact] - public void MobileEnumerator_FiltersByBoundsAndOrder() - { - var map = Map.Felucca; - var rect = new Rectangle2D(100, 100, 32, 32); - - var mobiles = new Mobile[3]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(105, 105, 0)); - mobiles[1] = CreateMobile(map, new Point3D(130, 130, 0)); - mobiles[2] = CreateMobile(map, new Point3D(90, 90, 0)); - - var found = new List(); - foreach (var m in map.GetMobilesInBounds(rect)) - { - found.Add(m); - } - - Assert.Equal(2, found.Count); - Assert.All(found, m => Assert.True(rect.Contains(m.Location))); - Assert.Equal(new[] { mobiles[0], mobiles[1] }, found); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileEnumerator_DeletedMobilesAreSkipped() - { - var map = Map.Felucca; - var rect = new Rectangle2D(200, 200, 16, 16); - - var mobiles = new Mobile[3]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(205, 205, 0)); - mobiles[1] = CreateMobile(map, new Point3D(206, 205, 0)); - mobiles[2] = CreateMobile(map, new Point3D(207, 205, 0)); - - mobiles[1].Delete(); - - var found = new List(); - foreach (var m in map.GetMobilesInBounds(rect)) - { - found.Add(m); - } - - Assert.Equal(new[] { mobiles[0], mobiles[2] }, found); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileEnumerator_RespectsMakeBoundsInclusiveFlag() - { - var map = Map.Felucca; - var rect = new Rectangle2D(300, 300, 1, 1); - - var mobiles = new Mobile[1]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(301, 301, 0)); - - var enumerator = map.GetMobilesInBounds(rect, makeBoundsInclusive: true).GetEnumerator(); - - Assert.True(enumerator.MoveNext()); - Assert.Equal(mobiles[0], enumerator.Current); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.MobileEnumerator(null, Rectangle2D.Empty, false); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void MobileEnumerator_ThrowsOnVersionChange() - { - var map = Map.Felucca; - var rect = new Rectangle2D(400, 400, 16, 16); - - var mobiles = new[] - { - CreateMobile(map, new Point3D(405, 405, 0)), - CreateMobile(map, new Point3D(406, 405, 0)) - }; - - try - { - var enumerator = map.GetMobilesInBounds(rect).GetEnumerator(); - Assert.True(enumerator.MoveNext()); - - mobiles[1].Delete(); - - var exceptionThrown = false; - try - { - enumerator.MoveNext(); - } - catch (InvalidOperationException) - { - exceptionThrown = true; - } - - Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileEnumerator_StepsAcrossSectors() - { - var map = Map.Felucca; - var rect = new Rectangle2D(500, 500, Map.SectorSize * 2, Map.SectorSize * 2); - - var mobiles = new[] - { - CreateMobile(map, new Point3D(rect.X + 1, rect.Y + 1, 0)), - CreateMobile(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + 1, 0)), - CreateMobile(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + Map.SectorSize + 1, 0)) - }; - - try - { - var result = new List(); - foreach (var m in map.GetMobilesInBounds(rect)) - { - result.Add(m); - } - - Assert.Equal(mobiles, result); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileEnumerator_MapBoundsAreClamped() - { - var map = Map.Felucca; - var width = map.Width; - var height = map.Height; - - var rect = new Rectangle2D(width - Map.SectorSize - 2, height - Map.SectorSize - 2, Map.SectorSize * 2, Map.SectorSize * 2); - - var mobiles = new[] - { - CreateMobile(map, new Point3D(width - 2, height - 2, 0)) - }; - - try - { - var enumerator = map.GetMobilesInBounds(rect).GetEnumerator(); - - Assert.True(enumerator.MoveNext()); - Assert.Equal(mobiles[0], enumerator.Current); - Assert.False(enumerator.MoveNext()); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileEnumerator_ZeroRangeReturnsOnlyCenter() - { - var map = Map.Felucca; - var center = new Point3D(700, 700, 0); - const int range = 0; - - var mobiles = new Mobile[2]; - try - { - mobiles[0] = CreateMobile(map, center); // Exact center - mobiles[1] = CreateMobile(map, new Point3D(701, 700, 0)); // 1 tile away - - var found = new List(); - foreach (var mobile in map.GetMobilesInRange(center, range)) - { - found.Add(mobile); - } - - Assert.Single(found); - Assert.Equal(mobiles[0], found[0]); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileEnumerator_NegativeRangeCreates1x1Bounds() - { - var map = Map.Felucca; - var center = new Point3D(750, 750, 0); - const int range = -5; - - var mobiles = new Mobile[2]; - try - { - mobiles[0] = CreateMobile(map, center); - mobiles[1] = CreateMobile(map, new Point3D(751, 750, 0)); // 1 tile away - - var found = new List(); - foreach (var mobile in map.GetMobilesInRange(center, range)) - { - found.Add(mobile); - } - - // With negative range creating a 1x1 bounds, only exact center matches - Assert.Single(found); - Assert.Equal(mobiles[0], found[0]); - } - finally - { - DeleteAll(mobiles); - } - } - - private static Mobile CreateMobile(Map map, Point3D location) - { - var mobile = new Mobile(World.NewMobile); - mobile.DefaultMobileInit(); - mobile.MoveToWorld(location, map); - return mobile; - } - - private static void DeleteAll(Mobile[] mobiles) - { - for (var i = 0; i < mobiles.Length; i++) - { - mobiles[i]?.Delete(); - } - } -} diff --git a/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs deleted file mode 100644 index cb06fe48d..000000000 --- a/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs +++ /dev/null @@ -1,412 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Items; -using Xunit; - -namespace Server.Tests.Maps; - -[Collection("Sequential Server Tests")] -public class MultiEnumeratorTests -{ - [Fact] - public void MultiEnumerator_FiltersByBoundsAndOrder() - { - var map = Map.Felucca; - var rect = new Rectangle2D(100, 100, 32, 32); - - var multis = new TestMulti[3]; - try - { - multis[0] = CreateMulti(map, new Point3D(105, 105, 0)); - multis[1] = CreateMulti(map, new Point3D(130, 130, 0)); - multis[2] = CreateMulti(map, new Point3D(90, 90, 0)); - - var found = new List(); - foreach (var multi in map.GetMultisInBounds(rect)) - { - found.Add(multi); - } - - Assert.Equal(2, found.Count); - Assert.All(found, multi => Assert.True(rect.Contains(multi.Location))); - Assert.Equal(new[] { multis[0], multis[1] }, found); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiEnumerator_DeletedMultisAreSkipped() - { - var map = Map.Felucca; - var rect = new Rectangle2D(200, 200, 16, 16); - - var multis = new TestMulti[3]; - try - { - multis[0] = CreateMulti(map, new Point3D(205, 205, 0)); - multis[1] = CreateMulti(map, new Point3D(206, 205, 0)); - multis[2] = CreateMulti(map, new Point3D(207, 205, 0)); - - multis[1].Delete(); - - var found = new List(); - foreach (var multi in map.GetMultisInBounds(rect)) - { - found.Add(multi); - } - - Assert.Equal(new[] { multis[0], multis[2] }, found); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiEnumerator_RespectsMakeBoundsInclusiveFlag() - { - var map = Map.Felucca; - var rect = new Rectangle2D(300, 300, 1, 1); - - var multis = new TestMulti[1]; - try - { - multis[0] = CreateMulti(map, new Point3D(301, 301, 0)); - - var enumerator = map.GetMultisInBounds(rect, makeBoundsInclusive: true).GetEnumerator(); - - Assert.True(enumerator.MoveNext()); - Assert.Equal(multis[0], enumerator.Current); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.MultiBoundsEnumerable(null, Rectangle2D.Empty, false).GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void MultiEnumerator_ThrowsOnVersionChange() - { - var map = Map.Felucca; - var rect = new Rectangle2D(400, 400, 16, 16); - - var multis = new[] - { - CreateMulti(map, new Point3D(405, 405, 0)), - CreateMulti(map, new Point3D(406, 405, 0)) - }; - - try - { - var enumerator = map.GetMultisInBounds(rect).GetEnumerator(); - Assert.True(enumerator.MoveNext()); - - multis[1].Delete(); - - // Ref structs cannot be captured in lambdas, so we test the exception directly - var exceptionThrown = false; - try - { - enumerator.MoveNext(); - } - catch (InvalidOperationException) - { - exceptionThrown = true; - } - - Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiEnumerator_StepsAcrossSectors() - { - var map = Map.Felucca; - var rect = new Rectangle2D(500, 500, Map.SectorSize * 2, Map.SectorSize * 2); - - var multis = new[] - { - CreateMulti(map, new Point3D(rect.X + 1, rect.Y + 1, 0)), - CreateMulti(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + 1, 0)), - CreateMulti(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + Map.SectorSize + 1, 0)) - }; - - try - { - var result = new List(); - foreach (var multi in map.GetMultisInBounds(rect)) - { - result.Add(multi); - } - - Assert.Equal(multis, result); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiEnumerator_MapBoundsAreClamped() - { - var map = Map.Felucca; - var width = map.Width; - var height = map.Height; - - var rect = new Rectangle2D(width - Map.SectorSize - 2, height - Map.SectorSize - 2, Map.SectorSize * 2, Map.SectorSize * 2); - - var multis = new[] - { - CreateMulti(map, new Point3D(width - 2, height - 2, 0)) - }; - - try - { - var enumerator = map.GetMultisInBounds(rect).GetEnumerator(); - - Assert.True(enumerator.MoveNext()); - Assert.Equal(multis[0], enumerator.Current); - Assert.False(enumerator.MoveNext()); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiEnumerator_GetMultisInRange() - { - var map = Map.Felucca; - var center = new Point3D(600, 600, 0); - var range = 5; - - var multis = new TestMulti[3]; - try - { - multis[0] = CreateMulti(map, new Point3D(602, 602, 0)); // Within range - multis[1] = CreateMulti(map, new Point3D(598, 598, 0)); // Within range - multis[2] = CreateMulti(map, new Point3D(610, 610, 0)); // Outside range - - var found = new List(); - foreach (var multi in map.GetMultisInRange(center, range)) - { - found.Add(multi); - } - - Assert.Equal(2, found.Count); - Assert.Contains(multis[0], found); - Assert.Contains(multis[1], found); - Assert.DoesNotContain(multis[2], found); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiSectorEnumerator_FiltersToSingleSector() - { - var map = Map.Felucca; - var location = new Point3D(700, 700, 0); - - var multis = new TestMulti[2]; - try - { - multis[0] = CreateMulti(map, location); - multis[1] = CreateMulti(map, new Point3D(location.X + 1, location.Y, 0)); - - var found = new List(); - foreach (var multi in map.GetMultisInSector(location)) - { - found.Add(multi); - } - - // Both should be in the same sector - Assert.Equal(2, found.Count); - Assert.Contains(multis[0], found); - Assert.Contains(multis[1], found); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiSectorEnumerator_DeletedMultisAreSkipped() - { - var map = Map.Felucca; - var location = new Point3D(750, 750, 0); - - var multis = new TestMulti[3]; - try - { - multis[0] = CreateMulti(map, location); - multis[1] = CreateMulti(map, new Point3D(location.X + 1, location.Y, 0)); - multis[2] = CreateMulti(map, new Point3D(location.X + 2, location.Y, 0)); - - multis[1].Delete(); - - var found = new List(); - foreach (var multi in map.GetMultisInRange(location, 10)) - { - found.Add(multi); - } - - Assert.Equal(new[] { multis[0], multis[2] }, found); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiSectorEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.MultiSectorEnumerable(null, new Point2D(0, 0)).GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void MultiSectorEnumerator_UsesDifferentPointOverloads() - { - var map = Map.Felucca; - var location = new Point3D(800, 800, 5); - - var multis = new TestMulti[1]; - try - { - multis[0] = CreateMulti(map, location); - - // Test Point3D overload - var found1 = new List(); - foreach (var multi in map.GetMultisInSector(location)) - { - found1.Add(multi); - } - - // Test (int, int) overload - var found2 = new List(); - foreach (var multi in map.GetMultisInSector(location.X, location.Y)) - { - found2.Add(multi); - } - - // Test Point2D overload - var found3 = new List(); - foreach (var multi in map.GetMultisInSector(new Point2D(location.X, location.Y))) - { - found3.Add(multi); - } - - Assert.Single(found1); - Assert.Equal(multis[0], found1[0]); - Assert.Equal(found1, found2); - Assert.Equal(found1, found3); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiEnumerator_ZeroRangeReturnsOnlyCenter() - { - var map = Map.Felucca; - var center = new Point3D(800, 800, 0); - const int range = 0; - - var multis = new TestMulti[2]; - try - { - multis[0] = CreateMulti(map, center); // Exact center - multis[1] = CreateMulti(map, new Point3D(801, 800, 0)); // 1 tile away - - var found = new List(); - foreach (var multi in map.GetMultisInRange(center, range)) - { - found.Add(multi); - } - - Assert.Single(found); - Assert.Equal(multis[0], found[0]); - } - finally - { - DeleteAll(multis); - } - } - - [Fact] - public void MultiEnumerator_NegativeRangeCreates1x1Bounds() - { - var map = Map.Felucca; - var center = new Point3D(850, 850, 0); - const int range = -5; - - var multis = new TestMulti[2]; - try - { - multis[0] = CreateMulti(map, center); - multis[1] = CreateMulti(map, new Point3D(851, 850, 0)); // 1 tile away - - var found = new List(); - foreach (var multi in map.GetMultisInRange(center, range)) - { - found.Add(multi); - } - - // With negative range creating a 1x1 bounds, only exact center matches - Assert.Single(found); - Assert.Equal(multis[0], found[0]); - } - finally - { - DeleteAll(multis); - } - } - - private static TestMulti CreateMulti(Map map, Point3D location) - { - var multi = new TestMulti(); - multi.MoveToWorld(location, map); - return multi; - } - - private static void DeleteAll(TestMulti[] multis) - { - for (var i = 0; i < multis.Length; i++) - { - multis[i]?.Delete(); - } - } - - // Test implementation of BaseMulti - private class TestMulti : BaseMulti - { - public TestMulti() : base(0x1) - { - } - } -} - diff --git a/Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs deleted file mode 100644 index 1ac335f7a..000000000 --- a/Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs +++ /dev/null @@ -1,319 +0,0 @@ -using System.Collections.Generic; -using Server.Items; -using Xunit; - -namespace Server.Tests.Maps; - -[Collection("Sequential Server Tests")] -public class StaticTileEnumeratorTests -{ - [Fact] - public void StaticTileEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.StaticTileEnumerable(null, new Point2D(0, 0)).GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void StaticTileEnumerator_EmptyLocationYieldsEmpty() - { - var map = Map.Felucca; - var location = new Point2D(100, 100); - - var tiles = new List(); - foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: true, includeMultis: false)) - { - tiles.Add(tile); - } - - // Since we don't have actual map files loaded, this should be empty - Assert.Empty(tiles); - } - - [Fact] - public void StaticTileEnumerator_IncludeStaticsOnlyWorks() - { - var map = Map.Felucca; - var location = new Point2D(200, 200); - - TestMulti multi = null; - try - { - // Create a multi at the location - multi = CreateMultiWithComponents(map, new Point3D(200, 200, 0)); - - // Get tiles with statics only (no multis) - var tiles = new List(); - foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: true, includeMultis: false)) - { - tiles.Add(tile); - } - - // Should not include multi tiles - Assert.Empty(tiles); - } - finally - { - multi?.Delete(); - } - } - - [Fact] - public void StaticTileEnumerator_IncludeMultisOnlyWorks() - { - var map = Map.Felucca; - var location = new Point2D(300, 300); - - TestMulti multi = null; - try - { - // Create a multi at the location with components - multi = CreateMultiWithComponents(map, new Point3D(300, 300, 0)); - - // Get tiles with multis only (no statics) - var tiles = new List(); - foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: false, includeMultis: true)) - { - tiles.Add(tile); - } - - // Should include multi tiles - Assert.NotEmpty(tiles); - } - finally - { - multi?.Delete(); - } - } - - [Fact] - public void StaticTileEnumerator_IncludeBothStaticsAndMultisWorks() - { - var map = Map.Felucca; - var location = new Point2D(400, 400); - - TestMulti multi = null; - try - { - // Create a multi at the location - multi = CreateMultiWithComponents(map, new Point3D(400, 400, 0)); - - // Get all tiles (statics and multis) - var tiles = new List(); - foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: true, includeMultis: true)) - { - tiles.Add(tile); - } - - // Should include multi tiles (statics would be empty without map files) - Assert.NotEmpty(tiles); - } - finally - { - multi?.Delete(); - } - } - - [Fact] - public void StaticTileEnumerator_MultiTileZOffsetApplied() - { - var map = Map.Felucca; - var location = new Point2D(500, 500); - var multiZ = 10; - - TestMulti multi = null; - try - { - // Create a multi at Z=10 - multi = CreateMultiWithComponents(map, new Point3D(500, 500, multiZ)); - - // Get multi tiles - var tiles = new List(); - foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: false, includeMultis: true)) - { - tiles.Add(tile); - } - - // All tiles should have Z offset by the multi's Z position - Assert.NotEmpty(tiles); - Assert.All(tiles, tile => Assert.True(tile.Z >= multiZ)); - } - finally - { - multi?.Delete(); - } - } - - [Fact] - public void StaticTileEnumerator_TileMatrixGetStaticTilesWorks() - { - var map = Map.Felucca; - var x = 600; - var y = 600; - - // Test the TileMatrix.GetStaticTiles method - var tiles = new List(); - foreach (var tile in map.Tiles.GetStaticTiles(x, y)) - { - tiles.Add(tile); - } - - // Without map files loaded, should be empty - Assert.Empty(tiles); - } - - [Fact] - public void StaticTileEnumerator_TileMatrixGetStaticAndMultiTilesWorks() - { - var map = Map.Felucca; - var x = 700; - var y = 700; - - TestMulti multi = null; - try - { - // Create a multi at the location - multi = CreateMultiWithComponents(map, new Point3D(700, 700, 0)); - - // Test the TileMatrix.GetStaticAndMultiTiles method - var tiles = new List(); - foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y)) - { - tiles.Add(tile); - } - - // Should include multi tiles - Assert.NotEmpty(tiles); - } - finally - { - multi?.Delete(); - } - } - - [Fact] - public void StaticTileEnumerator_TileMatrixGetMultiTilesWorks() - { - var map = Map.Felucca; - var x = 800; - var y = 800; - - TestMulti multi = null; - try - { - // Create a multi at the location - multi = CreateMultiWithComponents(map, new Point3D(800, 800, 0)); - - // Test the TileMatrix.GetMultiTiles method - var tiles = new List(); - foreach (var tile in map.Tiles.GetMultiTiles(x, y)) - { - tiles.Add(tile); - } - - // Should include multi tiles - Assert.NotEmpty(tiles); - } - finally - { - multi?.Delete(); - } - } - - [Fact] - public void StaticTileEnumerator_MultipleMultisAtSameLocation() - { - var map = Map.Felucca; - var location = new Point2D(900, 900); - - var multis = new TestMulti[2]; - try - { - // Create two multis at the same location - multis[0] = CreateMultiWithComponents(map, new Point3D(900, 900, 0)); - multis[1] = CreateMultiWithComponents(map, new Point3D(900, 900, 5)); - - // Get all multi tiles - var tiles = new List(); - foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: false, includeMultis: true)) - { - tiles.Add(tile); - } - - // Should include tiles from both multis - Assert.NotEmpty(tiles); - // We expect at least tiles from both multis - Assert.True(tiles.Count >= 2); - } - finally - { - multis[0]?.Delete(); - multis[1]?.Delete(); - } - } - - [Fact] - public void StaticTileEnumerator_EmptyReturnsCorrectly() - { - var enumerator = Map.StaticTileEnumerable.Empty.GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void StaticTileEnumerator_DeletedMultiSkipped() - { - var map = Map.Felucca; - var location = new Point2D(1000, 1000); - - var multis = new TestMulti[2]; - try - { - // Create two multis - multis[0] = CreateMultiWithComponents(map, new Point3D(1000, 1000, 0)); - multis[1] = CreateMultiWithComponents(map, new Point3D(1000, 1000, 5)); - - // Delete the first multi - multis[0].Delete(); - - // Get all multi tiles - var tiles = new List(); - foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: false, includeMultis: true)) - { - tiles.Add(tile); - } - - // Should only include tiles from the second multi - Assert.NotEmpty(tiles); - Assert.All(tiles, tile => Assert.True(tile.Z >= 5)); - } - finally - { - multis[0]?.Delete(); - multis[1]?.Delete(); - } - } - - private static TestMulti CreateMultiWithComponents(Map map, Point3D location) - { - var multi = new TestMulti(World.NewItem); - multi.MoveToWorld(location, map); - return multi; - } - - private class TestMulti : BaseMulti - { - public TestMulti(Serial serial) : base(serial) - { - } - - public override MultiComponentList Components => DefaultComponents; - - private static readonly MultiComponentList DefaultComponents = new( - [ - new MultiTileEntry(0x1, 0, 0, 0, 0x0), - new MultiTileEntry(0x2, 1, 0, 0, 0x0) - ] - ); - } -} - diff --git a/Projects/Server.Tests/Tests/Network/Bans/BanChannelTests.cs b/Projects/Server.Tests/Tests/Network/Bans/BanChannelTests.cs deleted file mode 100644 index 5eb1895d8..000000000 --- a/Projects/Server.Tests/Tests/Network/Bans/BanChannelTests.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests.Network.Bans; - -public class BanChannelTests -{ - private sealed class FakeReporter : IBanReporter - { - public readonly List<(IPAddress ip, TimeSpan ttl, string reason)> Reports = []; - public readonly List Retractions = []; - public bool ThrowOnReport; - - public string Name => "fake"; - public bool CanRetract => true; - public void Register() { } - public void Start(CancellationToken token) { } - public void Stop() { } - - public void Report(IPAddress address, TimeSpan ttl, string reason) - { - if (ThrowOnReport) - { - throw new InvalidOperationException("boom"); - } - - Reports.Add((address, ttl, reason)); - } - - public void Retract(IPAddress address) => Retractions.Add(address); - } - - [Fact] - public void Report_FansOutToAllReporters() - { - var a = new FakeReporter(); - var b = new FakeReporter(); - BanChannel.ConfigureForTesting([a, b]); - - BanChannel.Report(IPAddress.Parse("1.2.3.4"), TimeSpan.FromHours(1), "rate-limit"); - - Assert.Single(a.Reports); - Assert.Single(b.Reports); - Assert.Equal("rate-limit", a.Reports[0].reason); - } - - [Fact] - public void Report_SwallowsReporterException() - { - var bad = new FakeReporter { ThrowOnReport = true }; - var good = new FakeReporter(); - BanChannel.ConfigureForTesting([bad, good]); - - BanChannel.Report(IPAddress.Parse("1.2.3.4"), TimeSpan.FromHours(1), "manual"); - - Assert.Single(good.Reports); // the throwing reporter does not block the others - } - - [Fact] - public void Report_SuppressedForExemptAddress() - { - var reporter = new FakeReporter(); - BanChannel.ConfigureForTesting([reporter]); - - var exempt = IPAddress.Parse("203.0.113.7"); - BanChannel.IsExempt = (ip, _) => ip.Equals(exempt); - - try - { - BanChannel.Report(exempt, TimeSpan.FromHours(1), "rate-limit"); - Assert.Empty(reporter.Reports); // escalation suppressed; the local gate already acted - - BanChannel.Report(IPAddress.Parse("203.0.113.8"), TimeSpan.FromHours(1), "rate-limit"); - Assert.Single(reporter.Reports); // everyone else is still contributed - } - finally - { - BanChannel.IsExempt = null; - } - } - - [Fact] - public void Retract_ReachesRetractCapableReporters() - { - var a = new FakeReporter(); - BanChannel.ConfigureForTesting([a]); - - BanChannel.Retract(IPAddress.Parse("9.9.9.9")); - - Assert.Single(a.Retractions); - } -} diff --git a/Projects/Server.Tests/Tests/Network/Bans/BanConfigurationTests.cs b/Projects/Server.Tests/Tests/Network/Bans/BanConfigurationTests.cs deleted file mode 100644 index ee232b1df..000000000 --- a/Projects/Server.Tests/Tests/Network/Bans/BanConfigurationTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Text.Json; -using Server.Json; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests; - -public class BanConfigurationTests -{ - // Locks the JsonConfig casing/converter contract: JsonConfig's options are case-SENSITIVE, so - // every settings member must carry an explicit [JsonPropertyName("camelCase")] or it silently - // binds nothing. These tests round-trip through the exact options the loader uses. - - [Fact] - public void BanSettings_RoundTripsThroughJsonConfig() - { - var original = new BanSettings - { - ReportRateLimitTrips = false, - AutoBanDuration = TimeSpan.FromHours(2) - }; - - var json = JsonConfig.Serialize(original); - - Assert.Contains("\"reportRateLimitTrips\"", json); - Assert.Contains("\"autoBanDuration\"", json); - - var restored = JsonSerializer.Deserialize(json, JsonConfig.DefaultOptions); - - Assert.NotNull(restored); - Assert.Equal(original.ReportRateLimitTrips, restored.ReportRateLimitTrips); - Assert.Equal(original.AutoBanDuration, restored.AutoBanDuration); // TimeSpan survives - } - - [Fact] - public void BanSettings_Defaults_AreReportRateLimitTripsFourHourAutoBan() - { - var settings = new BanSettings(); - - Assert.True(settings.ReportRateLimitTrips); - Assert.Equal(TimeSpan.FromHours(4), settings.AutoBanDuration); - } -} diff --git a/Projects/Server.Tests/Tests/Network/ConnectionFiltersTests.cs b/Projects/Server.Tests/Tests/Network/ConnectionFiltersTests.cs deleted file mode 100644 index b7e134794..000000000 --- a/Projects/Server.Tests/Tests/Network/ConnectionFiltersTests.cs +++ /dev/null @@ -1,133 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ConnectionFiltersTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Net; -using System.Threading; -using Server.Network; -using Xunit; - -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class ConnectionFiltersTests : IDisposable -{ - public ConnectionFiltersTests() => ConnectionFilters.ResetForTesting(); - - public void Dispose() => ConnectionFilters.ResetForTesting(); - - [Fact] - public void No_filters_denies_nothing() - { - Assert.False(ConnectionFilters.ShouldDeny(IPAddress.Parse("1.2.3.4"), out var deniedBy)); - Assert.Null(deniedBy); - } - - [Fact] - public void Register_is_idempotent_by_name() - { - ConnectionFilters.Register(new FakeFilter("dupe", deny: false)); - ConnectionFilters.Register(new FakeFilter("dupe", deny: true)); - - // The second registration is ignored, so the deny:true instance never gets consulted. - Assert.Single(ConnectionFilters.Filters); - Assert.False(ConnectionFilters.ShouldDeny(IPAddress.Parse("1.2.3.4"), out _)); - } - - [Fact] - public void First_denying_filter_short_circuits_and_is_named() - { - var first = new FakeFilter("allow-all", deny: false); - var second = new FakeFilter("deny-all", deny: true); - var third = new FakeFilter("never-reached", deny: true); - - ConnectionFilters.Register(first); - ConnectionFilters.Register(second); - ConnectionFilters.Register(third); - - Assert.True(ConnectionFilters.ShouldDeny(IPAddress.Parse("1.2.3.4"), out var deniedBy)); - Assert.Equal("deny-all", deniedBy); - Assert.Equal(1, first.Calls); - Assert.Equal(1, second.Calls); - Assert.Equal(0, third.Calls); // short-circuited - } - - // A filter that throws once throws for every subsequent connection, which would turn one bug into an - // exception per accept. It must be dropped, and the connection must fail open rather than be denied - // by a filter that never actually answered. - [Fact] - public void Throwing_filter_is_unregistered_and_fails_open() - { - var bad = new FakeFilter("bad", deny: true, throws: true); - var good = new FakeFilter("good", deny: false); - - ConnectionFilters.Register(bad); - ConnectionFilters.Register(good); - - Assert.False(ConnectionFilters.ShouldDeny(IPAddress.Parse("1.2.3.4"), out _)); - Assert.Single(ConnectionFilters.Filters); - Assert.Equal("good", ConnectionFilters.Filters[0].Name); - - // Remaining filters still run on the same pass the faulty one was dropped in. - Assert.Equal(1, good.Calls); - } - - [Fact] - public void Register_configures_immediately() - { - var filter = new FakeFilter("cfg", deny: false); - ConnectionFilters.Register(filter); - - Assert.True(filter.Configured); - } - - private sealed class FakeFilter : IConnectionFilter - { - private readonly bool _deny; - private readonly bool _throws; - - public FakeFilter(string name, bool deny, bool throws = false) - { - Name = name; - _deny = deny; - _throws = throws; - } - - public string Name { get; } - public int Calls { get; private set; } - public bool Configured { get; private set; } - - public void Register() => Configured = true; - - public void Start(CancellationToken token) - { - } - - public void Stop() - { - } - - public bool ShouldDeny(IPAddress address) - { - Calls++; - if (_throws) - { - throw new InvalidOperationException("simulated filter bug"); - } - - return _deny; - } - } -} diff --git a/Projects/Server.Tests/Tests/Network/EventLoopIdleTests.cs b/Projects/Server.Tests/Tests/Network/EventLoopIdleTests.cs deleted file mode 100644 index dfb8c7be7..000000000 --- a/Projects/Server.Tests/Tests/Network/EventLoopIdleTests.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Xunit; - -namespace Server.Tests; - -/// -/// The event loop only sleeps when every queue it drains is empty. These drains are deliberately -/// bounded -- ExecuteTasks stops at its per-frame cap -- so leftover work is normal and must keep -/// the loop awake. Getting this wrong strands queued work for the length of a sleep. -/// -[Collection("Sequential Server Tests")] -public class EventLoopIdleTests -{ - [Fact] - public void FreshContextIsEmpty() - { - var context = new EventLoopContext(); - - Assert.True(context.IsEmpty); - } - - [Fact] - public void PostedWorkMakesContextNonEmpty() - { - var context = new EventLoopContext(); - - context.Post(() => { }); - - Assert.False(context.IsEmpty); - } - - [Fact] - public void PriorityWorkMakesContextNonEmpty() - { - var context = new EventLoopContext(); - - context.Post(() => { }, EventLoopContext.Priority.High); - - Assert.False(context.IsEmpty); - } - - [Fact] - public void ContextIsEmptyAgainOnceDrained() - { - var context = new EventLoopContext(); - context.Post(() => { }); - - context.ExecuteTasks(); - - Assert.True(context.IsEmpty); - } - - [Fact] - public void WorkBeyondThePerFrameCapKeepsContextNonEmpty() - { - // The cap is what makes IsEmpty necessary: a single ExecuteTasks pass cannot be assumed - // to have drained everything, so the loop must not treat "I just ran tasks" as "idle". - const int perFrameCap = 128; - var context = new EventLoopContext(perFrameCap); - - for (var i = 0; i < perFrameCap + 10; i++) - { - context.Post(() => { }); - } - - context.ExecuteTasks(); - - Assert.False(context.IsEmpty); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallEntryTests.cs b/Projects/Server.Tests/Tests/Network/Firewall/FirewallEntryTests.cs similarity index 100% rename from Projects/UOContent.Tests/Tests/Network/Firewall/FirewallEntryTests.cs rename to Projects/Server.Tests/Tests/Network/Firewall/FirewallEntryTests.cs diff --git a/Projects/Server.Tests/Tests/Network/ForeignProtocolTests.cs b/Projects/Server.Tests/Tests/Network/ForeignProtocolTests.cs deleted file mode 100644 index 95a3c4114..000000000 --- a/Projects/Server.Tests/Tests/Network/ForeignProtocolTests.cs +++ /dev/null @@ -1,116 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ForeignProtocolTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Text; -using Server.Network; -using Xunit; - -namespace Server.Tests.Network; - -public class ForeignProtocolTests -{ - private static ForeignProtocolMatch Identify(byte[] bytes, out ForeignProtocolKind kind) => - ForeignProtocol.Identify(bytes, out kind); - - private static byte[] Ascii(string s) => Encoding.ASCII.GetBytes(s); - - [Theory] - [InlineData("GET / HTTP/1.1\r\n")] - [InlineData("POST /a HTTP/1.1\r\n")] - [InlineData("HEAD / HTTP/1.0\r\n")] - [InlineData("OPTIONS * HTTP/1.1\r\n")] - [InlineData("CONNECT host:443 HTTP/1.1\r\n")] - [InlineData("DELETE /x HTTP/1.1\r\n")] - public void Http_requests_are_identified(string request) - { - Assert.Equal(ForeignProtocolMatch.Confirmed, Identify(Ascii(request), out var kind)); - Assert.Equal(ForeignProtocolKind.Http, kind); - } - - [Fact] - public void Ssh_banner_is_identified() - { - Assert.Equal(ForeignProtocolMatch.Confirmed, Identify(Ascii("SSH-2.0-OpenSSH_9.6"), out var kind)); - Assert.Equal(ForeignProtocolKind.Ssh, kind); - } - - [Fact] - public void Tls_client_hello_is_identified() - { - // handshake, TLS 1.2 record, length 0x0100, ClientHello - byte[] hello = [0x16, 0x03, 0x03, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFC]; - - Assert.Equal(ForeignProtocolMatch.Confirmed, Identify(hello, out var kind)); - Assert.Equal(ForeignProtocolKind.Tls, kind); - } - - [Fact] - public void Tls_prefix_without_a_client_hello_is_not_foreign() - { - // A seed can spell 0x16 0x03 0x0? -- the address 22.3.x.x. Byte five is a packet id, not a - // handshake type, so it must fall through to normal parsing. - byte[] seedThenLogin = [0x16, 0x03, 0x03, 0x04, 0x00, 0x80, 0x00, 0x00]; - - Assert.Equal(ForeignProtocolMatch.None, Identify(seedThenLogin, out var kind)); - Assert.Equal(ForeignProtocolKind.None, kind); - } - - [Fact] - public void Seed_that_spells_an_http_method_falls_through() - { - // Seed 0x47455420 spells "GET ". The next byte is the 0x80 login packet id, not printable, so this - // is a real client and must not be flagged. - byte[] seedThenLogin = [(byte)'G', (byte)'E', (byte)'T', (byte)' ', 0x80, 0x00, 0x3A, 0x00]; - - Assert.Equal(ForeignProtocolMatch.None, Identify(seedThenLogin, out _)); - } - - [Theory] - [InlineData(0x80)] // login request - [InlineData(0x91)] // game server login - [InlineData(0xEF)] // new-style seed packet - public void Ordinary_uo_openings_are_not_foreign(byte secondPacketId) - { - byte[] buffer = [0x7F, 0x00, 0x00, 0x01, secondPacketId, 0x00, 0x00, 0x00]; - - Assert.Equal(ForeignProtocolMatch.None, Identify(buffer, out _)); - } - - [Fact] - public void Encrypted_login_is_not_mistaken_for_a_foreign_protocol() - { - // A legitimate client with encryption on when the shard expects none. The login cipher is a - // byte-for-byte XOR, so this is noise of exactly the right length. - byte[] buffer = [0x7F, 0x00, 0x00, 0x01, 0xC3, 0x9A, 0x04, 0xE1, 0x55, 0xB2]; - - Assert.Equal(ForeignProtocolMatch.None, Identify(buffer, out _)); - } - - [Fact] - public void Prefix_match_without_enough_bytes_waits() - { - // Framing is never assumed: "GET " split from its request line must wait. - Assert.Equal(ForeignProtocolMatch.Incomplete, Identify(Ascii("GET "), out _)); - Assert.Equal(ForeignProtocolMatch.Incomplete, Identify(Ascii("GET /"), out _)); - } - - [Fact] - public void Too_few_bytes_to_match_a_prefix_is_not_foreign() - { - // Under four bytes the caller's own short-read handling applies. - Assert.Equal(ForeignProtocolMatch.None, Identify(Ascii("GE"), out _)); - Assert.Equal(ForeignProtocolMatch.None, Identify([], out _)); - } -} diff --git a/Projects/Server.Tests/Tests/Network/MovementThrottleLatencyTests.cs b/Projects/Server.Tests/Tests/Network/MovementThrottleLatencyTests.cs deleted file mode 100644 index 63cb9d4a6..000000000 --- a/Projects/Server.Tests/Tests/Network/MovementThrottleLatencyTests.cs +++ /dev/null @@ -1,430 +0,0 @@ -using Server.Network; -using Xunit; - -namespace Server.Tests.Network; - -/// -/// Manual tests for MovementThrottle latency scenarios. -/// These tests simulate specific timing scenarios and should NOT run in CI/CD. -/// -/// To run these tests manually: -/// dotnet test --filter "Category=Manual" -/// -/// To exclude from CI/CD, add to your test command: -/// dotnet test --filter "Category!=Manual" -/// -[Collection("Sequential Server Tests")] -[Trait("Category", "Manual")] -public class MovementThrottleLatencyTests -{ - /// - /// Sets the simulated tick count for testing. - /// - private static void SetTickCount(long ticks) - { - Core._tickCount = ticks; - } - - /// - /// Simulates recording movements at specific intervals. - /// - private static void SimulateMovements(NetState ns, params (int delayMs, int costMs)[] movements) - { - ns._movementHistory ??= new NetState.MovementRecord[20]; - ns._movementHistoryIndex = 0; - ns._movementHistoryFull = false; - ns._lastMovementRecordTime = 0; - - var currentTime = 0L; - - foreach (var (delayMs, costMs) in movements) - { - currentTime += delayMs; - SetTickCount(currentTime); - - if (ns._lastMovementRecordTime > 0) - { - var interval = (int)(currentTime - ns._lastMovementRecordTime); - - // Skip chain-breaking gaps (> 2000ms) like the real code does - if (interval > 2000) - { - ns._lastGapDuration = interval; - ns._lastMovementRecordTime = currentTime; - continue; - } - - ns._movementHistory[ns._movementHistoryIndex] = new NetState.MovementRecord - { - Interval = (short)interval, - TargetSpeed = (ushort)costMs, - QueueDepth = 0, - Flags = 0 - }; - - ns._movementHistoryIndex++; - if (ns._movementHistoryIndex >= 20) - { - ns._movementHistoryIndex = 0; - ns._movementHistoryFull = true; - } - } - - ns._lastMovementRecordTime = currentTime; - } - } - - /// - /// Scenario: Player experiences a 3-second network lag spike, then all buffered - /// packets arrive at once. This should NOT trigger false positive detection. - /// - /// Timeline: - /// - T=0-1000ms: Normal movement (10 packets at 100ms intervals) - /// - T=1000-4000ms: Network lag (client buffers packets) - /// - T=4000ms: Network recovers, 30 buffered packets arrive simultaneously - /// - [Fact] - public void LagSpike_BurstRecovery_NoFalsePositive() - { - var ns = PacketTestUtilities.CreateTestNetState(); - SetTickCount(0); - - // Phase 1: Normal movement for 1 second - var movements = new (int delayMs, int costMs)[10]; - for (var i = 0; i < 10; i++) - { - movements[i] = (100, 100); // Normal mounted running - } - SimulateMovements(ns, movements); - - // Verify normal rate before lag - var rateBeforeLag = MovementThrottle.CalculateMovementRate(ns, out var samples); - Assert.True(samples >= 8, $"Expected >= 8 samples, got {samples}"); - Assert.True(rateBeforeLag >= 0.95f && rateBeforeLag <= 1.05f, - $"Expected normal rate before lag, got {rateBeforeLag}"); - - // Phase 2: Simulate 3 second lag followed by burst - // The gap (3000ms) is tracked, then burst packets arrive - var currentTime = Core.TickCount; - - // Record the lag gap - SetTickCount(currentTime + 3000); - ns._lastGapDuration = 3000; // Gap > maxChainGap triggers this - - // Burst packets arriving (5 packets with tiny intervals) - for (var i = 0; i < 5; i++) - { - SetTickCount(Core.TickCount + 2); // 2ms between packets (simultaneous arrival) - - if (ns._lastMovementRecordTime > 0) - { - var interval = (int)(Core.TickCount - ns._lastMovementRecordTime); - ns._movementHistory[ns._movementHistoryIndex] = new NetState.MovementRecord - { - Interval = (short)interval, - TargetSpeed = 100, - QueueDepth = 0, - Flags = 0 - }; - ns._movementHistoryIndex++; - if (ns._movementHistoryIndex >= 20) - { - ns._movementHistoryIndex = 0; - ns._movementHistoryFull = true; - } - } - ns._lastMovementRecordTime = Core.TickCount; - } - - // Analyze - should apply burst forgiveness due to large gap - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out _, out var confidence); - - // The burst will have high rate due to tiny intervals, but: - // 1. _lastGapDuration > 1000 (maxChainGap/2) triggers burst forgiveness - // 2. Confidence should be reduced by 0.3f multiplier - // 3. Should NOT be Definite unless truly extreme - Assert.True( - verdict != MovementThrottle.DetectionVerdict.Definite, - $"Lag recovery should not be Definite. Rate={rate}, Confidence={confidence}, Verdict={verdict}" - ); - - // Gap duration should be cleared after analysis - Assert.Equal(0, ns._lastGapDuration); - } - - /// - /// Scenario: Player with consistently high latency (400ms RTT) moves normally. - /// Their connection is consistent but slow - should NOT be considered "stable" - /// for the purpose of tightening detection thresholds. - /// - [Fact] - public void HighLatencyConsistent_NotConsideredStable() - { - var ns = PacketTestUtilities.CreateTestNetState(); - - // Set up high but consistent RTT - ns._rttHistory = new long[] { 400, 405, 398, 402, 401, 399, 403, 400 }; - ns._rttSampleCount = 8; - - // Calculate variance (should be low since values are consistent) - long sum = 0, sumSq = 0; - for (var i = 0; i < 8; i++) - { - sum += ns._rttHistory[i]; - sumSq += ns._rttHistory[i] * ns._rttHistory[i]; - } - var mean = sum / 8; - ns._rttVariance = sumSq / 8 - mean * mean; - - // Variance is low (consistent), but should NOT be "stable" due to high latency - Assert.False(ns.HasStableConnection, - $"400ms RTT should not be stable. AvgRTT={ns.AverageRtt}, Variance={ns._rttVariance}"); - - // Now verify detection gives more leniency for high-latency connections - SimulateMovements(ns, - (105, 100), (105, 100), (105, 100), (105, 100), (105, 100), - (105, 100), (105, 100), (105, 100), (105, 100), (105, 100) - ); - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out _, out var confidence); - - // Slightly slow but high-latency player should get benefit of doubt - Assert.True( - verdict == MovementThrottle.DetectionVerdict.Normal || - verdict == MovementThrottle.DetectionVerdict.Possible, - $"High latency player should get leniency. Verdict={verdict}" - ); - } - - /// - /// Scenario: RTT probing and measurement. - /// Verifies that RTT variance is calculated correctly and stability - /// is properly determined. - /// - [Fact] - public void RttMeasurement_VarianceCalculation() - { - var ns = PacketTestUtilities.CreateTestNetState(); - SetTickCount(1000); - - // Simulate stable low-latency RTT measurements - var rttValues = new long[] { 50, 48, 52, 49, 51, 50, 48, 52 }; - - ns._rttHistory = new long[8]; - for (var i = 0; i < rttValues.Length; i++) - { - ns._rttHistory[i] = rttValues[i]; - } - ns._rttSampleCount = 8; - - // Calculate expected variance - long sum = 0, sumSq = 0; - foreach (var rtt in rttValues) - { - sum += rtt; - sumSq += rtt * rtt; - } - var expectedMean = sum / 8; - var expectedVariance = sumSq / 8 - expectedMean * expectedMean; - - // Clamp negative variance (safety check) - if (expectedVariance < 0) - { - expectedVariance = 0; - } - - ns._rttVariance = expectedVariance; - - Assert.True(ns._rttVariance >= 0, "Variance should never be negative"); - Assert.True(ns._rttVariance < 2500, "Low-variance samples should have variance < 2500"); - Assert.True(ns.AverageRtt > 0 && ns.AverageRtt < 200, "Average RTT should be ~50ms"); - Assert.True(ns.HasStableConnection, "Should be considered stable connection"); - } - - /// - /// Scenario: Actual speed hacker with stable connection. - /// Should be detected with high confidence. - /// - [Fact] - public void SpeedHack_StableConnection_DetectedDefinite() - { - var ns = PacketTestUtilities.CreateTestNetState(); - - // Set up stable, low-latency RTT - ns._rttHistory = new long[] { 30, 32, 28, 31, 29, 30, 31, 30 }; - ns._rttSampleCount = 8; - - long sum = 0, sumSq = 0; - for (var i = 0; i < 8; i++) - { - sum += ns._rttHistory[i]; - sumSq += ns._rttHistory[i] * ns._rttHistory[i]; - } - ns._rttVariance = sumSq / 8 - (sum / 8) * (sum / 8); - if (ns._rttVariance < 0) - { - ns._rttVariance = 0; - } - - Assert.True(ns.HasStableConnection, "Should be stable for this test"); - - // Simulate speed hack: 50% faster movement - SimulateMovements(ns, - (67, 100), (67, 100), (67, 100), (67, 100), (67, 100), - (67, 100), (67, 100), (67, 100), (67, 100), (67, 100), - (67, 100), (67, 100), (67, 100), (67, 100), (67, 100) - ); - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out _, out var confidence); - - Assert.True(rate > 1.4f, $"Expected rate > 1.4 (50% speed hack), got {rate}"); - Assert.Equal(MovementThrottle.DetectionVerdict.Definite, verdict); - Assert.True(confidence > 0.5f, $"Expected high confidence, got {confidence}"); - } - - /// - /// Scenario: Borderline speed hack (7% faster) with unstable connection. - /// Should give benefit of doubt (not Definite). - /// - [Fact] - public void BorderlineSpeed_UnstableConnection_BenefitOfDoubt() - { - var ns = PacketTestUtilities.CreateTestNetState(); - - // Set up unstable connection - ns._rttHistory = new long[] { 50, 300, 80, 250, 100, 400, 60, 350 }; - ns._rttSampleCount = 8; - - long sum = 0, sumSq = 0; - for (var i = 0; i < 8; i++) - { - sum += ns._rttHistory[i]; - sumSq += ns._rttHistory[i] * ns._rttHistory[i]; - } - ns._rttVariance = sumSq / 8 - (sum / 8) * (sum / 8); - - Assert.False(ns.HasStableConnection, "Should be unstable due to high variance"); - - // Simulate borderline speed (7% faster) - SimulateMovements(ns, - (93, 100), (93, 100), (93, 100), (93, 100), (93, 100), - (93, 100), (93, 100), (93, 100), (93, 100), (93, 100) - ); - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out _, out var confidence); - - Assert.True(rate > 1.05f && rate < 1.10f, $"Expected rate ~1.07, got {rate}"); - - // Unstable connection + borderline rate = should NOT be Definite - Assert.True( - verdict != MovementThrottle.DetectionVerdict.Definite, - $"Borderline case with unstable connection should not be Definite. Verdict={verdict}, Confidence={confidence}" - ); - } - - /// - /// Scenario: Verifies that burst forgiveness triggers correctly and clears gap duration. - /// - [Fact] - public void MultipleLagSpikes_NoAccumulatedSuspicion() - { - var ns = PacketTestUtilities.CreateTestNetState(); - SetTickCount(0); - - // Set up history buffer with mostly normal movements and a burst at the end - ns._movementHistory = new NetState.MovementRecord[20]; - - // Fill with 17 normal movements (100ms intervals) - for (var i = 0; i < 17; i++) - { - ns._movementHistory[i] = new NetState.MovementRecord - { - Interval = 100, - TargetSpeed = 100, - QueueDepth = 0, - Flags = 0 - }; - } - - // Add 3 burst movements at the end (10ms intervals = burst) - for (var i = 17; i < 20; i++) - { - ns._movementHistory[i] = new NetState.MovementRecord - { - Interval = 10, - TargetSpeed = 100, - QueueDepth = 0, - Flags = 0 - }; - } - - ns._movementHistoryIndex = 0; // Next write position (wrapped) - ns._movementHistoryFull = true; // Buffer is full - - // Set gap duration (simulating a lag spike before the burst) - ns._lastGapDuration = 2500; - - // Analyze - burst forgiveness should trigger - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out var samples, out var confidence); - - // Key assertions: - // 1. Gap duration should be cleared (burst forgiveness triggered) - Assert.Equal(0, ns._lastGapDuration); - - // 2. Confidence should be reduced by 0.3x multiplier - Assert.True( - confidence < 0.5f, - $"Burst after gap should have low confidence. Confidence={confidence}, Rate={rate}" - ); - - // 3. Should NOT be Definite despite the burst (forgiveness applied) - Assert.True( - verdict != MovementThrottle.DetectionVerdict.Definite, - $"Should not be Definite after burst forgiveness. Verdict={verdict}" - ); - } - - /// - /// Scenario: Walking vs running speed differences. - /// Walking (200ms interval) vs mounted running (100ms interval) should both - /// calculate correct rates. - /// - [Fact] - public void WalkingVsRunning_CorrectRateCalculation() - { - // Test walking (200ms expected interval) - var nsWalking = PacketTestUtilities.CreateTestNetState(); - SimulateMovements(nsWalking, - (200, 200), (200, 200), (200, 200), (200, 200), (200, 200), - (200, 200), (200, 200), (200, 200), (200, 200), (200, 200) - ); - - var walkingRate = MovementThrottle.CalculateMovementRate(nsWalking, out var walkingSamples); - Assert.True(walkingSamples >= 8, "Should have enough walking samples"); - Assert.True(walkingRate >= 0.95f && walkingRate <= 1.05f, - $"Walking rate should be ~1.0, got {walkingRate}"); - - // Test mounted running (100ms expected interval) - var nsRunning = PacketTestUtilities.CreateTestNetState(); - SimulateMovements(nsRunning, - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100), - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100) - ); - - var runningRate = MovementThrottle.CalculateMovementRate(nsRunning, out var runningSamples); - Assert.True(runningSamples >= 8, "Should have enough running samples"); - Assert.True(runningRate >= 0.95f && runningRate <= 1.05f, - $"Running rate should be ~1.0, got {runningRate}"); - - // Test speed hack while walking - var nsWalkingHack = PacketTestUtilities.CreateTestNetState(); - SimulateMovements(nsWalkingHack, - (100, 200), (100, 200), (100, 200), (100, 200), (100, 200), - (100, 200), (100, 200), (100, 200), (100, 200), (100, 200) - ); - - var walkingHackRate = MovementThrottle.CalculateMovementRate(nsWalkingHack, out _); - Assert.True(walkingHackRate >= 1.9f && walkingHackRate <= 2.1f, - $"Walking at running speed should be rate ~2.0, got {walkingHackRate}"); - } -} diff --git a/Projects/Server.Tests/Tests/Network/MovementThrottleTests.cs b/Projects/Server.Tests/Tests/Network/MovementThrottleTests.cs deleted file mode 100644 index eca2ee0bf..000000000 --- a/Projects/Server.Tests/Tests/Network/MovementThrottleTests.cs +++ /dev/null @@ -1,385 +0,0 @@ -using Server.Network; -using Xunit; - -namespace Server.Tests.Network; - -/// -/// Unit tests for MovementThrottle rate calculation and detection logic. -/// These tests are CI/CD safe - they don't depend on real timing. -/// -[Collection("Sequential Server Tests")] -public class MovementThrottleTests -{ - /// - /// Creates a test NetState with movement history pre-populated. - /// - private static NetState CreateNetStateWithHistory(params (short interval, ushort targetSpeed)[] records) - { - var ns = PacketTestUtilities.CreateTestNetState(); - - if (records.Length == 0) - { - return ns; - } - - // Initialize history buffer - ns._movementHistory = new NetState.MovementRecord[20]; - ns._movementHistoryIndex = 0; - ns._movementHistoryFull = false; - - foreach (var (interval, targetSpeed) in records) - { - ns._movementHistory[ns._movementHistoryIndex] = new NetState.MovementRecord - { - Interval = interval, - TargetSpeed = targetSpeed, - QueueDepth = 0, - Flags = 0 - }; - ns._movementHistoryIndex++; - - if (ns._movementHistoryIndex >= 20) - { - ns._movementHistoryIndex = 0; - ns._movementHistoryFull = true; - } - } - - return ns; - } - - [Fact] - public void CalculateMovementRate_NoHistory_ReturnsOne() - { - var ns = CreateNetStateWithHistory(); - - var rate = MovementThrottle.CalculateMovementRate(ns, out var sampleCount); - - Assert.Equal(1.0f, rate); - Assert.Equal(0, sampleCount); - } - - [Fact] - public void CalculateMovementRate_InsufficientSamples_ReturnsOne() - { - // Less than 8 samples (minSamplesForRate default) - var ns = CreateNetStateWithHistory( - (100, 100), - (100, 100), - (100, 100) - ); - - var rate = MovementThrottle.CalculateMovementRate(ns, out var sampleCount); - - Assert.Equal(1.0f, rate); - // When insufficient samples, method returns early with sampleCount = 0 - Assert.Equal(0, sampleCount); - } - - [Fact] - public void CalculateMovementRate_NormalSpeed_ReturnsOne() - { - // 10 samples at exactly expected speed (100ms interval, 100ms target) - var ns = CreateNetStateWithHistory( - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100), - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100) - ); - - var rate = MovementThrottle.CalculateMovementRate(ns, out var sampleCount); - - Assert.Equal(1.0f, rate); - Assert.Equal(10, sampleCount); - } - - [Fact] - public void CalculateMovementRate_SpeedHack_ReturnsHighRate() - { - // Moving at 50ms intervals when 100ms is expected = 2x speed - var ns = CreateNetStateWithHistory( - (50, 100), (50, 100), (50, 100), (50, 100), (50, 100), - (50, 100), (50, 100), (50, 100), (50, 100), (50, 100) - ); - - var rate = MovementThrottle.CalculateMovementRate(ns, out var sampleCount); - - Assert.Equal(2.0f, rate); - Assert.Equal(10, sampleCount); - } - - [Fact] - public void CalculateMovementRate_SlowMovement_ReturnsLowRate() - { - // Moving at 200ms intervals when 100ms is expected = 0.5x speed - var ns = CreateNetStateWithHistory( - (200, 100), (200, 100), (200, 100), (200, 100), (200, 100), - (200, 100), (200, 100), (200, 100), (200, 100), (200, 100) - ); - - var rate = MovementThrottle.CalculateMovementRate(ns, out var sampleCount); - - Assert.Equal(0.5f, rate); - Assert.Equal(10, sampleCount); - } - - [Fact] - public void CalculateMovementRate_MixedSpeeds_ReturnsAverageRate() - { - // Mix of mounted running (100ms target) at varying speeds - // Total target: 1000ms, Total actual: 900ms = 1.11 rate - var ns = CreateNetStateWithHistory( - (80, 100), (100, 100), (90, 100), (100, 100), (80, 100), - (90, 100), (100, 100), (80, 100), (90, 100), (90, 100) - ); - - var rate = MovementThrottle.CalculateMovementRate(ns, out var sampleCount); - - // 1000 / 900 = 1.111... - Assert.True(rate > 1.10f && rate < 1.12f, $"Expected rate ~1.11, got {rate}"); - Assert.Equal(10, sampleCount); - } - - [Fact] - public void DetectRecentBurst_NoBurst_ReturnsZero() - { - // Normal intervals, no burst - var ns = CreateNetStateWithHistory( - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100), - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100) - ); - - var (burstSize, precedingGap) = MovementThrottle.DetectRecentBurst(ns); - - Assert.Equal(0, burstSize); - } - - [Fact] - public void DetectRecentBurst_BurstDetected_ReturnsBurstSizeAndGap() - { - // Last 4 packets arrived in burst (< 15ms intervals), preceded by 500ms gap - var ns = CreateNetStateWithHistory( - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100), - (500, 100), (5, 100), (5, 100), (5, 100), (5, 100) - ); - - var (burstSize, precedingGap) = MovementThrottle.DetectRecentBurst(ns); - - Assert.Equal(4, burstSize); - Assert.Equal(500, precedingGap); - } - - [Fact] - public void DetectRecentBurst_SmallBurst_DetectsCorrectly() - { - // 2 packets in burst - var ns = CreateNetStateWithHistory( - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100), - (100, 100), (100, 100), (100, 100), (200, 100), (10, 100) - ); - - var (burstSize, precedingGap) = MovementThrottle.DetectRecentBurst(ns); - - Assert.Equal(1, burstSize); - Assert.Equal(200, precedingGap); - } - - [Fact] - public void AnalyzeMovement_NormalMovement_ReturnsNormal() - { - var ns = CreateNetStateWithHistory( - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100), - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100) - ); - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out var sampleCount, out var confidence); - - Assert.Equal(MovementThrottle.DetectionVerdict.Normal, verdict); - Assert.Equal(1.0f, rate); - Assert.Equal(10, sampleCount); - } - - [Fact] - public void AnalyzeMovement_ClearSpeedHack_ReturnsDefinite() - { - // 30% faster than expected - clear speed hack - var ns = CreateNetStateWithHistory( - (77, 100), (77, 100), (77, 100), (77, 100), (77, 100), - (77, 100), (77, 100), (77, 100), (77, 100), (77, 100) - ); - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out var sampleCount, out var confidence); - - Assert.Equal(MovementThrottle.DetectionVerdict.Definite, verdict); - Assert.True(rate > 1.25f, $"Expected rate > 1.25, got {rate}"); - } - - [Fact] - public void AnalyzeMovement_ModerateSpeedHack_ReturnsPossibleOrLikely() - { - // 8% faster than expected - moderate, should be Possible or Likely - var ns = CreateNetStateWithHistory( - (93, 100), (93, 100), (93, 100), (93, 100), (93, 100), - (93, 100), (93, 100), (93, 100), (93, 100), (93, 100) - ); - - // Set up stable, low-latency RTT (required for Likely verdict path) - ns._rttHistory = [50, 52, 48, 51, 49, 50, 51, 50]; - ns._rttSampleCount = 8; - ns._rttVariance = 4; // Low variance - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out var sampleCount, out var confidence); - - Assert.True( - verdict == MovementThrottle.DetectionVerdict.Possible || - verdict == MovementThrottle.DetectionVerdict.Likely, - $"Expected Possible or Likely, got {verdict}. Rate={rate}, Confidence={confidence}" - ); - Assert.True(rate > 1.05f && rate < 1.15f, $"Expected rate ~1.07, got {rate}"); - } - - [Fact] - public void AnalyzeMovement_BurstAfterLargeGap_ReducesConfidence() - { - // Simulate lag recovery: large gap followed by burst of packets - var ns = CreateNetStateWithHistory( - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100), - (5, 100), (5, 100), (5, 100), (5, 100), (5, 100) - ); - - // Set up the gap duration (simulates what RecordMovement does) - ns._lastGapDuration = 1500; // Gap > maxChainGap/2 (1000) - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out var sampleCount, out var confidence); - - // Even though rate is high, confidence should be reduced due to burst forgiveness - // The gap duration should trigger the forgiveness path - Assert.True(confidence < 0.5f, $"Expected low confidence due to burst forgiveness, got {confidence}"); - - // After analysis, gap duration should be reset - Assert.Equal(0, ns._lastGapDuration); - } - - [Fact] - public void AnalyzeMovement_HighQueue_IncreasesConfidence() - { - var ns = CreateNetStateWithHistory( - (95, 100), (95, 100), (95, 100), (95, 100), (95, 100), - (95, 100), (95, 100), (95, 100), (95, 100), (95, 100) - ); - - // Simulate high queue depth (modified client indicator) - ns._movementQueue = new System.Collections.Generic.Queue(); - for (var i = 0; i < 5; i++) - { - ns._movementQueue.Enqueue(new NetState.QueuedMovement()); - } - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out var sampleCount, out var confidence); - - // Queue > 4 indicates modified client - should be Definite - Assert.Equal(MovementThrottle.DetectionVerdict.Definite, verdict); - } - - [Fact] - public void AnalyzeMovement_StableConnectionLowLatency_IncreasesConfidence() - { - var ns = CreateNetStateWithHistory( - (95, 100), (95, 100), (95, 100), (95, 100), (95, 100), - (95, 100), (95, 100), (95, 100), (95, 100), (95, 100) - ); - - // Set up stable, low-latency RTT - ns._rttHistory = [50, 52, 48, 51, 49, 50, 51, 50]; - ns._rttSampleCount = 8; - ns._rttVariance = 4; // Low variance - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out var sampleCount, out var confidence); - - // With stable connection, even small anomalies are more suspicious - Assert.True(confidence > 0.2f, $"Expected higher confidence for stable connection, got {confidence}"); - } - - [Fact] - public void AnalyzeMovement_UnstableConnection_ReducesConfidence() - { - var ns = CreateNetStateWithHistory( - (90, 100), (90, 100), (90, 100), (90, 100), (90, 100), - (90, 100), (90, 100), (90, 100), (90, 100), (90, 100) - ); - - // Set up unstable connection (high variance) - ns._rttHistory = [50, 200, 80, 350, 100, 250, 90, 300]; - ns._rttSampleCount = 8; - ns._rttVariance = 15000; // High variance - - var verdictUnstable = MovementThrottle.AnalyzeMovement(ns, out _, out _, out var confidenceUnstable); - - // Reset and test with stable connection for comparison - var nsStable = CreateNetStateWithHistory( - (90, 100), (90, 100), (90, 100), (90, 100), (90, 100), - (90, 100), (90, 100), (90, 100), (90, 100), (90, 100) - ); - nsStable._rttHistory = [50, 52, 48, 51, 49, 50, 51, 50]; - nsStable._rttSampleCount = 8; - nsStable._rttVariance = 4; - - var verdictStable = MovementThrottle.AnalyzeMovement(nsStable, out _, out _, out var confidenceStable); - - // Unstable connection should have lower confidence - Assert.True( - confidenceUnstable < confidenceStable, - $"Expected unstable ({confidenceUnstable}) < stable ({confidenceStable})" - ); - } - - [Fact] - public void HasStableConnection_LowLatencyLowVariance_ReturnsTrue() - { - var ns = PacketTestUtilities.CreateTestNetState(); - - // Stable: low variance, enough samples, low latency - ns._rttHistory = [50, 52, 48, 51, 49, 50, 51, 50]; - ns._rttSampleCount = 8; - ns._rttVariance = 4; // Low variance < 2500 - - Assert.True(ns.HasStableConnection); - } - - [Fact] - public void HasStableConnection_HighLatency_ReturnsFalse() - { - var ns = PacketTestUtilities.CreateTestNetState(); - - // High but consistent latency (500ms) - should NOT be considered stable - ns._rttHistory = [500, 502, 498, 501, 499, 500, 501, 500]; - ns._rttSampleCount = 8; - ns._rttVariance = 4; // Low variance, but latency > 200ms - - Assert.False(ns.HasStableConnection, "High latency connection should not be considered stable"); - } - - [Fact] - public void HasStableConnection_HighVariance_ReturnsFalse() - { - var ns = PacketTestUtilities.CreateTestNetState(); - - // Low average latency but high variance - ns._rttHistory = [50, 200, 30, 180, 40, 150, 60, 170]; - ns._rttSampleCount = 8; - ns._rttVariance = 5000; // High variance > 2500 - - Assert.False(ns.HasStableConnection); - } - - [Fact] - public void HasStableConnection_InsufficientSamples_ReturnsFalse() - { - var ns = PacketTestUtilities.CreateTestNetState(); - - // Only 2 samples - ns._rttHistory = [50, 50, 0, 0, 0, 0, 0, 0]; - ns._rttSampleCount = 2; - ns._rttVariance = 0; - - Assert.False(ns.HasStableConnection, "Needs at least 3 samples"); - } -} diff --git a/Projects/Server.Tests/Tests/Network/NetworkCompressionBoundsTests.cs b/Projects/Server.Tests/Tests/Network/NetworkCompressionBoundsTests.cs deleted file mode 100644 index c1bc6a800..000000000 --- a/Projects/Server.Tests/Tests/Network/NetworkCompressionBoundsTests.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using Server.Network; -using Xunit; - -namespace Server.Tests.Network; - -/// -/// Bounds behaviour of the Huffman compressor when the destination is too small. -/// -/// This is reachable in production: NetState only checks that the send buffer has *some* writable -/// space before handing the remainder to Compress, so a nearly-full buffer can offer a span of one -/// to three bytes. The internal guard is computed as an unsigned output.Length - 4, which -/// underflows for those sizes and stops bounding the writes at all. -/// -public class NetworkCompressionBoundsTests -{ - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - public void RefusesOutputTooSmallToBound(int outputSize) - { - var input = new byte[64]; - Array.Fill(input, (byte)'A'); - - // Sentinel-filled backing array; only the middle window is offered to the compressor, so - // any write past the span shows up as a modified sentinel rather than silent corruption. - var backing = new byte[256]; - Array.Fill(backing, (byte)0xCC); - - const int windowStart = 64; - var output = backing.AsSpan(windowStart, outputSize); - - var written = NetworkCompression.Compress(input, output); - - Assert.Equal(0, written); - - for (var i = 0; i < backing.Length; i++) - { - Assert.Equal(0xCC, backing[i]); - } - } - - [Fact] - public void StillCompressesWhenOutputIsLargeEnough() - { - var input = new byte[64]; - Array.Fill(input, (byte)'A'); - - var output = new byte[256]; - - var written = NetworkCompression.Compress(input, output); - - Assert.True(written > 0); - Assert.True(written <= output.Length); - } - - [Fact] - public void ReportsFailureRatherThanOverrunningATightOutput() - { - // Large input against a small-but-bounded output: the guard is well-defined here, so this - // must fail cleanly rather than write past the end. - var input = new byte[4096]; - Array.Fill(input, (byte)'A'); - - var backing = new byte[256]; - Array.Fill(backing, (byte)0xCC); - - const int windowStart = 64; - const int windowSize = 16; - var output = backing.AsSpan(windowStart, windowSize); - - NetworkCompression.Compress(input, output); - - for (var i = 0; i < windowStart; i++) - { - Assert.Equal(0xCC, backing[i]); - } - - for (var i = windowStart + windowSize; i < backing.Length; i++) - { - Assert.Equal(0xCC, backing[i]); - } - } -} diff --git a/Projects/Server.Tests/Tests/Network/NetworkCompressionTests.cs b/Projects/Server.Tests/Tests/Network/NetworkCompressionTests.cs deleted file mode 100644 index 826d44deb..000000000 --- a/Projects/Server.Tests/Tests/Network/NetworkCompressionTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using Server.Network; -using Xunit; - -namespace Server.Tests.Network; - -/// -/// Known-answer test for outgoing huffman compression. -/// The table is a contract with the client's hard-coded decompression tree, so a single wrong -/// entry -- or a subtle bit-packing error in a future rewrite of -- -/// silently corrupts every affected packet and desyncs the client's framing with no error. -/// A round-trip test cannot catch that, since encoder and decoder would share the same mistake. -/// The expected bytes below were generated from the canonical table, not from the implementation. -/// -public class NetworkCompressionTests -{ - // Every symbol 0x00-0xFF exactly once. Any single wrong table entry changes these bytes, so this - // pins all 256 entries plus the terminal code, and exercises the encoder end to end. - private static ReadOnlySpan AllSymbolsCompressed => - [ - 0x3F, 0x13, 0x4E, 0xB4, 0x76, 0xCB, 0x81, 0x8A, 0xB3, 0xCE, 0x76, 0x5E, - 0xAA, 0xEE, 0x2B, 0x7C, 0xCA, 0x74, 0xB3, 0xD9, 0x9F, 0x72, 0x3C, 0xE7, - 0xE9, 0x05, 0x97, 0xB9, 0x1C, 0x6B, 0x70, 0xCD, 0xE9, 0x37, 0x31, 0x69, - 0xAE, 0x70, 0x29, 0x14, 0xA4, 0x13, 0x3F, 0x71, 0x90, 0x39, 0xDF, 0x3A, - 0x5C, 0x03, 0x20, 0x9D, 0x86, 0x4B, 0x33, 0xCA, 0xDC, 0xF9, 0x48, 0x6F, - 0x4B, 0x6F, 0xD8, 0x8B, 0x42, 0xAA, 0x5A, 0x73, 0x87, 0x9C, 0x47, 0xE3, - 0xD2, 0x28, 0x76, 0x35, 0xDD, 0x12, 0xD6, 0x46, 0x7B, 0xBD, 0x68, 0x4B, - 0x25, 0xD7, 0x9A, 0xBC, 0x88, 0x95, 0xDC, 0xA1, 0x48, 0xD9, 0xD4, 0x71, - 0xE1, 0x78, 0x30, 0xEE, 0xE3, 0xBF, 0xD3, 0x3F, 0x1B, 0xC9, 0xE5, 0xD4, - 0xE1, 0x40, 0x83, 0x0B, 0x58, 0x35, 0x60, 0xF1, 0xF1, 0x63, 0x4C, 0x86, - 0xED, 0x9A, 0x6A, 0x2A, 0xFC, 0x96, 0x75, 0x26, 0xC0, 0xDD, 0x33, 0xDC, - 0xFB, 0x97, 0x29, 0x84, 0x93, 0x30, 0x6C, 0x90, 0xFA, 0x1C, 0x14, 0x75, - 0x63, 0xE1, 0x7D, 0xB5, 0xF5, 0x06, 0xE5, 0xE4, 0xE5, 0xD3, 0x19, 0xE6, - 0x5C, 0xF4, 0xAF, 0xFD, 0xF7, 0x77, 0x2D, 0xBB, 0x70, 0xB4, 0x1E, 0xE8, - 0xF7, 0x92, 0x57, 0xA1, 0xD2, 0xE6, 0xB6, 0xB6, 0x58, 0xCA, 0xE1, 0x75, - 0xCF, 0x19, 0xF1, 0x40, 0xB4, 0xD4, 0x91, 0x66, 0xC7, 0x6F, 0x16, 0x51, - 0xBA, 0x65, 0x2F, 0x53, 0xA0, 0x72, 0x31, 0xE3, 0x92, 0x7A, 0xB1, 0x9D, - 0x98, 0xE4, 0x1B, 0xB7, 0x42, 0xF1, 0x21, 0x18, 0x0E, 0x55, 0x18, 0xBE, - 0x0A, 0x6D, 0xEB, 0xC0, 0x4E, 0xB6, 0x13, 0x94, 0xF2, 0xC8, 0xF6, 0x3F, - 0xCB, 0xD1, 0xCE, 0x13, 0x1C, 0x5F, 0x30, 0xAB, 0x82, 0x76, 0x94, 0x1F, - 0x1F, 0x2C, 0xC7, 0xC0, 0x90, 0x34, 0x61, 0x32, 0xC2, 0x51, 0xF3, 0xAC, - 0x72, 0x6F, 0x85, 0xDE, 0x7A, 0xD8, 0xEB, 0xE5, 0x7C, 0x90, 0x3B, 0xC7, - 0x9D, 0xDE, 0xA6, 0x02, 0xC5, 0xED, 0x24, 0x8D, 0xA4, 0xC9, 0x9E, 0xBA, - 0x2D, 0x7F, 0x3D, 0x27, 0xB7, 0x39, 0x25, 0xB9, 0x59, 0xE1, 0x08, 0xE9, - 0xB8, 0x75, 0xEC, 0x75, 0xD9, 0x8B, 0x40 - ]; - - [Fact] - public void Compress_EncodesEverySymbol_MatchingCanonicalTable() - { - Span input = stackalloc byte[256]; - for (var i = 0; i < input.Length; i++) - { - input[i] = (byte)i; - } - - Span output = stackalloc byte[512]; - var length = NetworkCompression.Compress(input, output); - - Assert.Equal(AllSymbolsCompressed.Length, length); - - var expected = AllSymbolsCompressed; - for (var i = 0; i < expected.Length; i++) - { - Assert.True(expected[i] == output[i], $"Byte {i}: expected 0x{expected[i]:X2}, got 0x{output[i]:X2}"); - } - } -} diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPacketTests.cs index 6d8bd9fd9..ed3bfd4e3 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPacketTests.cs @@ -7,8 +7,7 @@ using Xunit; namespace Server.Tests.Network; -[Collection("Sequential Server Tests")] -public class AccountPacketTests +public class AccountPacketTests : IClassFixture { private class MockedAccount : IAccount { @@ -26,7 +25,7 @@ public class AccountPacketTests public int CompareTo(IAccount other) => throw new NotImplementedException(); - public string Username { get; } + public string Username { get; set; } public string Email { get; set; } public AccessLevel AccessLevel { get; set; } public int Length { get; } @@ -52,12 +51,15 @@ public class AccountPacketTests public Serial Serial { get; } public void Deserialize(IGenericReader reader) => throw new NotImplementedException(); + public byte SerializedThread { get; set; } + public int SerializedPosition { get; set; } + public int SerializedLength { get; set; } + public void Serialize(IGenericWriter writer) => throw new NotImplementedException(); public bool Deleted { get; } public void Delete() => throw new NotImplementedException(); - public bool TrySetUsername(string username) => throw new NotImplementedException(); public void SetPassword(string password) => throw new NotImplementedException(); @@ -85,10 +87,10 @@ public class AccountPacketTests // var account = new MockAccount(new[] { firstMobile, null, secondMobile }); var expected = new ChangeCharacter(account).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendChangeCharacter(account); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -97,11 +99,11 @@ public class AccountPacketTests { var expected = new ClientVersionReq().Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendClientVersionRequest(); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -110,10 +112,10 @@ public class AccountPacketTests { var expected = new DeleteResult(DeleteResultType.BadRequest).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendCharacterDeleteResult(DeleteResultType.BadRequest); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -122,10 +124,10 @@ public class AccountPacketTests { var expected = new PopupMessage(PMMessage.LoginSyncError).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendPopupMessage(PMMessage.LoginSyncError); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -145,14 +147,14 @@ public class AccountPacketTests [4] = null }; - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.Account = account; ns.ProtocolChanges = protocolChanges; var expected = new SupportedFeatures(ns).Compile(); ns.SendSupportedFeature(); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -170,10 +172,10 @@ public class AccountPacketTests var expected = new LoginConfirm(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendLoginConfirmation(m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -182,10 +184,10 @@ public class AccountPacketTests { var expected = new LoginComplete().Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendLoginComplete(); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -207,10 +209,10 @@ public class AccountPacketTests var expected = new CharacterListUpdate(account).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendCharacterListUpdate(account); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -237,14 +239,14 @@ public class AccountPacketTests var expected = new CharacterList(account, info).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.CityInfo = info; ns.Account = account; ns.ProtocolChanges = ProtocolChanges.Version70130; ns.SendCharacterList(); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -271,13 +273,13 @@ public class AccountPacketTests var expected = new CharacterListOld(account, info).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.CityInfo = info; ns.Account = account; ns.SendCharacterList(); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -287,10 +289,10 @@ public class AccountPacketTests var reason = ALRReason.BadComm; var expected = new AccountLoginRej(reason).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendAccountLoginRejected(reason); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -304,12 +306,12 @@ public class AccountPacketTests var expected = new AccountLoginAck(info).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.ServerInfo = info; ns.SendAccountLoginAck(); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -317,15 +319,15 @@ public class AccountPacketTests public void TestPlayServerAck() { var si = new ServerInfo("Test Server", 0, TimeZoneInfo.Local, IPEndPoint.Parse("127.0.0.1")); - const int authId = 0x123456; + var authId = 0x123456; var expected = new PlayServerAck(si, authId).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendPlayServerAck(si, authId); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPackets.cs index 9e8c664a5..bba17d8af 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AccountPackets.cs @@ -2,360 +2,361 @@ using System; using Server.Accounting; using Server.Network; -namespace Server.Tests.Network; - -public sealed class ChangeCharacter : Packet +namespace Server.Tests.Network { - public ChangeCharacter(IAccount a) : base(0x81) + public sealed class ChangeCharacter : Packet { - EnsureCapacity(305); - - var count = 0; - - for (var i = 0; i < a.Length; ++i) + public ChangeCharacter(IAccount a) : base(0x81) { - if (a[i] != null) + EnsureCapacity(305); + + var count = 0; + + for (var i = 0; i < a.Length; ++i) { - ++count; + if (a[i] != null) + { + ++count; + } + } + + Stream.Write((byte)count); + Stream.Write((byte)0); + + for (var i = 0; i < a.Length; ++i) + { + var m = a[i]; + if (a[i] != null) + { + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + + Stream.WriteAsciiFixed(name, 30); + Stream.Fill(30); // password + } + else + { + Stream.Fill(60); + } } } + } - Stream.Write((byte)count); - Stream.Write((byte)0); - - for (var i = 0; i < a.Length; ++i) + public sealed class ClientVersionReq : Packet + { + public ClientVersionReq() : base(0xBD) { - var m = a[i]; - if (a[i] != null) - { - var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + EnsureCapacity(3); + } + } - Stream.WriteAsciiFixed(name, 30); - Stream.Fill(30); // password + public sealed class DeleteResult : Packet + { + public DeleteResult(DeleteResultType res) : base(0x85, 2) + { + Stream.Write((byte)res); + } + } + + public sealed class PopupMessage : Packet + { + public PopupMessage(PMMessage msg) : base(0x53, 2) + { + Stream.Write((byte)msg); + } + } + + public sealed class SupportedFeatures : Packet + { + public SupportedFeatures(NetState ns) : base(0xB9, ns.ExtendedSupportedFeatures ? 5 : 3) + { + var flags = ExpansionInfo.CoreExpansion.SupportedFeatures; + + if (ns.Account.Limit >= 6) + { + flags |= FeatureFlags.LiveAccount; + flags &= ~FeatureFlags.UOTD; + + if (ns.Account.Limit > 6) + { + flags |= FeatureFlags.SeventhCharacterSlot; + } + else + { + flags |= FeatureFlags.SixthCharacterSlot; + } + } + + if (ns.ExtendedSupportedFeatures) + { + Stream.Write((uint)flags); } else { - Stream.Fill(60); + Stream.Write((ushort)flags); } } } -} -public sealed class ClientVersionReq : Packet -{ - public ClientVersionReq() : base(0xBD) + public sealed class LoginConfirm : Packet { - EnsureCapacity(3); - } -} - -public sealed class DeleteResult : Packet -{ - public DeleteResult(DeleteResultType res) : base(0x85, 2) - { - Stream.Write((byte)res); - } -} - -public sealed class PopupMessage : Packet -{ - public PopupMessage(PMMessage msg) : base(0x53, 2) - { - Stream.Write((byte)msg); - } -} - -public sealed class SupportedFeatures : Packet -{ - public SupportedFeatures(NetState ns) : base(0xB9, ns.ExtendedSupportedFeatures ? 5 : 3) - { - var flags = ExpansionInfo.CoreExpansion.SupportedFeatures; - - if (ns.Account.Limit >= 6) + public LoginConfirm(Mobile m) : base(0x1B, 37) { - flags |= FeatureFlags.LiveAccount; - flags &= ~FeatureFlags.UOTD; - - if (ns.Account.Limit > 6) - { - flags |= FeatureFlags.SeventhCharacterSlot; - } - else - { - flags |= FeatureFlags.SixthCharacterSlot; - } - } - - if (ns.ExtendedSupportedFeatures) - { - Stream.Write((uint)flags); - } - else - { - Stream.Write((ushort)flags); - } - } -} - -public sealed class LoginConfirm : Packet -{ - public LoginConfirm(Mobile m) : base(0x1B, 37) - { - Stream.Write(m.Serial); - Stream.Write(0); - Stream.Write((short)m.Body); - Stream.Write((short)m.X); - Stream.Write((short)m.Y); - Stream.Write((short)m.Z); - Stream.Write((byte)m.Direction); - Stream.Write((byte)0); - Stream.Write(-1); - - var map = m.Map; - - if (map == null || map == Map.Internal) - { - map = m.LogoutMap; - } - - Stream.Write((short)0); - Stream.Write((short)0); - Stream.Write((short)(map?.Width ?? Map.Felucca.Width)); - Stream.Write((short)(map?.Height ?? Map.Felucca.Height)); - - Stream.Fill(); - } -} - -public sealed class LoginComplete : Packet -{ - public LoginComplete() : base(0x55, 1) - { - } -} - -public sealed class CharacterListUpdate : Packet -{ - public CharacterListUpdate(IAccount a) : base(0x86) - { - EnsureCapacity(4 + a.Length * 60); - - var highSlot = -1; - - for (var i = 0; i < a.Length; ++i) - { - if (a[i] != null) - { - highSlot = i; - } - } - - var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); - - Stream.Write((byte)count); - - for (var i = 0; i < count; ++i) - { - var m = a[i]; - - if (m != null) - { - var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - Stream.WriteAsciiFixed(name, 30); - Stream.Fill(30); // password - } - else - { - Stream.Fill(60); - } - } - } -} - -public sealed class CharacterList : Packet -{ - public CharacterList(IAccount a, CityInfo[] info) : base(0xA9) - { - EnsureCapacity(11 + a.Length * 60 + info.Length * 89); - - var highSlot = -1; - - for (var i = 0; i < a.Length; ++i) - { - if (a[i] != null) - { - highSlot = i; - } - } - - var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); - - Stream.Write((byte)count); - - for (var i = 0; i < count; ++i) - { - if (a[i] != null) - { - Stream.WriteAsciiFixed(a[i].Name, 30); - Stream.Fill(30); // password - } - else - { - Stream.Fill(60); - } - } - - Stream.Write((byte)info.Length); - - for (var i = 0; i < info.Length; ++i) - { - var ci = info[i]; - - Stream.Write((byte)i); - Stream.WriteAsciiFixed(ci.City, 32); - Stream.WriteAsciiFixed(ci.Building, 32); - Stream.Write(ci.X); - Stream.Write(ci.Y); - Stream.Write(ci.Z); - Stream.Write(ci.Map.MapID); - Stream.Write(ci.Description); + Stream.Write(m.Serial); Stream.Write(0); - } + Stream.Write((short)m.Body); + Stream.Write((short)m.X); + Stream.Write((short)m.Y); + Stream.Write((short)m.Z); + Stream.Write((byte)m.Direction); + Stream.Write((byte)0); + Stream.Write(-1); - var flags = ExpansionInfo.CoreExpansion.CharacterListFlags; + var map = m.Map; - if (count > 6) - { - flags |= CharacterListFlags.SeventhCharacterSlot | - CharacterListFlags.SixthCharacterSlot; // 7th Character Slot - TODO: Is SixthCharacterSlot Required? - } - else if (count == 6) - { - flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot - } - else if (a.Limit == 1) - { - flags |= CharacterListFlags.SlotLimit & - CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character - } - - Stream.Write((int)flags); // Additional Flags - - Stream.Write((short)-1); - } -} - -public sealed class CharacterListOld : Packet -{ - public CharacterListOld(IAccount a, CityInfo[] info) : base(0xA9) - { - EnsureCapacity(9 + a.Length * 60 + info.Length * 63); - - var highSlot = -1; - - for (var i = 0; i < a.Length; ++i) - { - if (a[i] != null) + if (map == null || map == Map.Internal) { - highSlot = i; + map = m.LogoutMap; + } + + Stream.Write((short)0); + Stream.Write((short)0); + Stream.Write((short)(map?.Width ?? Map.Felucca.Width)); + Stream.Write((short)(map?.Height ?? Map.Felucca.Height)); + + Stream.Fill(); + } + } + + public sealed class LoginComplete : Packet + { + public LoginComplete() : base(0x55, 1) + { + } + } + + public sealed class CharacterListUpdate : Packet + { + public CharacterListUpdate(IAccount a) : base(0x86) + { + EnsureCapacity(4 + a.Length * 60); + + var highSlot = -1; + + for (var i = 0; i < a.Length; ++i) + { + if (a[i] != null) + { + highSlot = i; + } + } + + var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); + + Stream.Write((byte)count); + + for (var i = 0; i < count; ++i) + { + var m = a[i]; + + if (m != null) + { + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + Stream.WriteAsciiFixed(name, 30); + Stream.Fill(30); // password + } + else + { + Stream.Fill(60); + } } } + } - var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); - - Stream.Write((byte)count); - - for (var i = 0; i < count; ++i) + public sealed class CharacterList : Packet + { + public CharacterList(IAccount a, CityInfo[] info) : base(0xA9) { - var m = a[i]; - if (m != null) + EnsureCapacity(11 + a.Length * 60 + info.Length * 89); + + var highSlot = -1; + + for (var i = 0; i < a.Length; ++i) { - var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - Stream.WriteAsciiFixed(name, 30); - Stream.Fill(30); // password + if (a[i] != null) + { + highSlot = i; + } } - else + + var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); + + Stream.Write((byte)count); + + for (var i = 0; i < count; ++i) { - Stream.Fill(60); + if (a[i] != null) + { + Stream.WriteAsciiFixed(a[i].Name, 30); + Stream.Fill(30); // password + } + else + { + Stream.Fill(60); + } + } + + Stream.Write((byte)info.Length); + + for (var i = 0; i < info.Length; ++i) + { + var ci = info[i]; + + Stream.Write((byte)i); + Stream.WriteAsciiFixed(ci.City, 32); + Stream.WriteAsciiFixed(ci.Building, 32); + Stream.Write(ci.X); + Stream.Write(ci.Y); + Stream.Write(ci.Z); + Stream.Write(ci.Map.MapID); + Stream.Write(ci.Description); + Stream.Write(0); + } + + var flags = ExpansionInfo.CoreExpansion.CharacterListFlags; + + if (count > 6) + { + flags |= CharacterListFlags.SeventhCharacterSlot | + CharacterListFlags.SixthCharacterSlot; // 7th Character Slot - TODO: Is SixthCharacterSlot Required? + } + else if (count == 6) + { + flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot + } + else if (a.Limit == 1) + { + flags |= CharacterListFlags.SlotLimit & + CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character + } + + Stream.Write((int)flags); // Additional Flags + + Stream.Write((short)-1); + } + } + + public sealed class CharacterListOld : Packet + { + public CharacterListOld(IAccount a, CityInfo[] info) : base(0xA9) + { + EnsureCapacity(9 + a.Length * 60 + info.Length * 63); + + var highSlot = -1; + + for (var i = 0; i < a.Length; ++i) + { + if (a[i] != null) + { + highSlot = i; + } + } + + var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); + + Stream.Write((byte)count); + + for (var i = 0; i < count; ++i) + { + var m = a[i]; + if (m != null) + { + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + Stream.WriteAsciiFixed(name, 30); + Stream.Fill(30); // password + } + else + { + Stream.Fill(60); + } + } + + Stream.Write((byte)info.Length); + + for (var i = 0; i < info.Length; ++i) + { + var ci = info[i]; + + Stream.Write((byte)i); + Stream.WriteAsciiFixed(ci.City, 31); + Stream.WriteAsciiFixed(ci.Building, 31); + } + + var flags = ExpansionInfo.CoreExpansion.CharacterListFlags; + + if (count > 6) + { + flags |= CharacterListFlags.SeventhCharacterSlot | + CharacterListFlags.SixthCharacterSlot; // 7th Character Slot - TODO: Is SixthCharacterSlot Required? + } + else if (count == 6) + { + flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot + } + else if (a.Limit == 1) + { + flags |= CharacterListFlags.SlotLimit & + CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character + } + + Stream.Write((int)flags); // Additional Flags + } + } + + public sealed class AccountLoginRej : Packet + { + public AccountLoginRej(ALRReason reason) : base(0x82, 2) + { + Stream.Write((byte)reason); + } + } + + public sealed class AccountLoginAck : Packet + { + public AccountLoginAck(ServerInfo[] info) : base(0xA8) + { + EnsureCapacity(6 + info.Length * 40); + + Stream.Write((byte)0x5D); // Unknown + + Stream.Write((ushort)info.Length); + + for (var i = 0; i < info.Length; ++i) + { + var si = info[i]; + + Stream.Write((ushort)i); + Stream.WriteAsciiFixed(si.Name, 32); + Stream.Write((byte)si.FullPercent); + Stream.Write((sbyte)si.TimeZone); + // UO Doesn't support IPv6 + Stream.Write(si.RawAddress); } } - - Stream.Write((byte)info.Length); - - for (var i = 0; i < info.Length; ++i) - { - var ci = info[i]; - - Stream.Write((byte)i); - Stream.WriteAsciiFixed(ci.City, 31); - Stream.WriteAsciiFixed(ci.Building, 31); - } - - var flags = ExpansionInfo.CoreExpansion.CharacterListFlags; - - if (count > 6) - { - flags |= CharacterListFlags.SeventhCharacterSlot | - CharacterListFlags.SixthCharacterSlot; // 7th Character Slot - TODO: Is SixthCharacterSlot Required? - } - else if (count == 6) - { - flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot - } - else if (a.Limit == 1) - { - flags |= CharacterListFlags.SlotLimit & - CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character - } - - Stream.Write((int)flags); // Additional Flags } -} -public sealed class AccountLoginRej : Packet -{ - public AccountLoginRej(ALRReason reason) : base(0x82, 2) + public sealed class PlayServerAck : Packet { - Stream.Write((byte)reason); - } -} - -public sealed class AccountLoginAck : Packet -{ - public AccountLoginAck(ServerInfo[] info) : base(0xA8) - { - EnsureCapacity(6 + info.Length * 40); - - Stream.Write((byte)0x5D); // Unknown - - Stream.Write((ushort)info.Length); - - for (var i = 0; i < info.Length; ++i) + public PlayServerAck(ServerInfo si, int authId) : base(0x8C, 11) { - var si = info[i]; + var addr = si.RawAddress; - Stream.Write((ushort)i); - Stream.WriteAsciiFixed(si.Name, 32); - Stream.Write((byte)si.FullPercent); - Stream.Write((sbyte)si.TimeZone); - // UO Doesn't support IPv6 - Stream.Write(si.RawAddress); + Stream.Write((byte)addr); + Stream.Write((byte)(addr >> 8)); + Stream.Write((byte)(addr >> 16)); + Stream.Write((byte)(addr >> 24)); + + Stream.Write((short)si.Address.Port); + Stream.Write(authId); } } } - -public sealed class PlayServerAck : Packet -{ - public PlayServerAck(ServerInfo si, int authId) : base(0x8C, 11) - { - var addr = si.RawAddress; - - Stream.Write((byte)addr); - Stream.Write((byte)(addr >> 8)); - Stream.Write((byte)(addr >> 16)); - Stream.Write((byte)(addr >> 24)); - - Stream.Write((short)si.Address.Port); - Stream.Write(authId); - } -} diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AttributeNormalizer.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AttributeNormalizer.cs index 59c160b63..a1fd4bef6 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AttributeNormalizer.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/AttributeNormalizer.cs @@ -1,36 +1,37 @@ -namespace Server.Network; - -public static class AttributeNormalizer +namespace Server.Network { - public static int Maximum { get; set; } = 100; - - public static bool Enabled { get; set; } = true; - - public static void Write(PacketWriter stream, int cur, int max) + public static class AttributeNormalizer { - if (Enabled && max != 0) + public static int Maximum { get; set; } = 100; + + public static bool Enabled { get; set; } = true; + + public static void Write(PacketWriter stream, int cur, int max) { - stream.Write((short)Maximum); - stream.Write((short)(cur * Maximum / max)); + if (Enabled && max != 0) + { + stream.Write((short)Maximum); + stream.Write((short)(cur * Maximum / max)); + } + else + { + stream.Write((short)max); + stream.Write((short)cur); + } } - else + + public static void WriteReverse(PacketWriter stream, int cur, int max) { - stream.Write((short)max); - stream.Write((short)cur); + if (Enabled && max != 0) + { + stream.Write((short)(cur * Maximum / max)); + stream.Write((short)Maximum); + } + else + { + stream.Write((short)cur); + stream.Write((short)max); + } } } - - public static void WriteReverse(PacketWriter stream, int cur, int max) - { - if (Enabled && max != 0) - { - stream.Write((short)(cur * Maximum / max)); - stream.Write((short)Maximum); - } - else - { - stream.Write((short)cur); - stream.Write((short)max); - } - } -} \ No newline at end of file +} diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/CombatPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/CombatPacketTests.cs index a885cae9e..6638c125b 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/CombatPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/CombatPacketTests.cs @@ -1,49 +1,49 @@ using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class CombatPacketTests +namespace Server.Tests.Network { - [Fact] - public void TestSwing() + public class CombatPacketTests { - var attacker = (Serial)0x1024; - var defender = (Serial)0x2048; + [Fact] + public void TestSwing() + { + Serial attacker = (Serial)0x1024; + Serial defender = (Serial)0x2048; - var expected = new Swing(attacker, defender).Compile(); + var expected = new Swing(attacker, defender).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendSwing(attacker, defender); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendSwing(attacker, defender); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory, InlineData(true), InlineData(false)] - public void TestSetWarMode(bool warmode) - { - var expected = new SetWarMode(warmode).Compile(); + [Theory, InlineData(true), InlineData(false)] + public void TestSetWarMode(bool warmode) + { + var expected = new SetWarMode(warmode).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendSetWarMode(warmode); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendSetWarMode(warmode); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestChangeCombatant() - { - var serial = (Serial)0x1024; + [Fact] + public void TestChangeCombatant() + { + Serial serial = (Serial)0x1024; - var expected = new ChangeCombatant(serial).Compile(); + var expected = new ChangeCombatant(serial).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendChangeCombatant(serial); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendChangeCombatant(serial); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/CombatPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/CombatPackets.cs index beced4b4b..ef5921239 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/CombatPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/CombatPackets.cs @@ -1,30 +1,31 @@ -namespace Server.Network; - -public sealed class Swing : Packet +namespace Server.Network { - public Swing(Serial attacker, Serial defender) : base(0x2F, 10) + public sealed class Swing : Packet { - Stream.Write((byte)0); - Stream.Write(attacker); - Stream.Write(defender); + public Swing(Serial attacker, Serial defender) : base(0x2F, 10) + { + Stream.Write((byte)0); + Stream.Write(attacker); + Stream.Write(defender); + } + } + + public sealed class SetWarMode : Packet + { + public SetWarMode(bool mode) : base(0x72, 5) + { + Stream.Write(mode); + Stream.Write((byte)0x00); + Stream.Write((byte)0x32); + Stream.Write((byte)0x00); + } + } + + public sealed class ChangeCombatant : Packet + { + public ChangeCombatant(Serial combatant) : base(0xAA, 5) + { + Stream.Write(combatant); + } } } - -public sealed class SetWarMode : Packet -{ - public SetWarMode(bool mode) : base(0x72, 5) - { - Stream.Write(mode); - Stream.Write((byte)0x00); - Stream.Write((byte)0x32); - Stream.Write((byte)0x00); - } -} - -public sealed class ChangeCombatant : Packet -{ - public ChangeCombatant(Serial combatant) : base(0xAA, 5) - { - Stream.Write(combatant); - } -} \ No newline at end of file diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ContainerPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ContainerPacketTests.cs index e18b999f0..1730017b9 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ContainerPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ContainerPacketTests.cs @@ -2,198 +2,200 @@ using Server.Items; using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class ContainerPacketTests +namespace Server.Tests.Network { - [Fact] - public void TestContainerDisplay() + [Collection("Sequential Tests")] + public class ContainerPacketTests : IClassFixture { - var serial = (Serial)0x1024; - ushort gumpId = 100; - var expected = new ContainerDisplay(serial, gumpId).Compile(); + [Fact] + public void TestContainerDisplay() + { + Serial serial = (Serial)0x1024; + ushort gumpId = 100; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDisplayContainer(serial, gumpId); + var expected = new ContainerDisplay(serial, gumpId).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplayContainer(serial, gumpId); - [Fact] - public void TestContainerDisplayHS() - { - var serial = (Serial)0x1024; - ushort gumpId = 100; + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var expected = new ContainerDisplayHS(serial, gumpId).Compile(); + [Fact] + public void TestContainerDisplayHS() + { + Serial serial = (Serial)0x1024; + ushort gumpId = 100; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = ns.ProtocolChanges | ProtocolChanges.ContainerGridLines | ProtocolChanges.HighSeas; - ns.SendDisplayContainer(serial, gumpId); + var expected = new ContainerDisplayHS(serial, gumpId).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = ns.ProtocolChanges | ProtocolChanges.ContainerGridLines | ProtocolChanges.HighSeas; + ns.SendDisplayContainer(serial, gumpId); - [Fact] - public void TestDisplaySpellbook() - { - var serial = (Serial)0x1024; + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var expected = new DisplaySpellbook(serial).Compile(); + [Fact] + public void TestDisplaySpellbook() + { + Serial serial = (Serial)0x1024; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDisplaySpellbook(serial); + var expected = new DisplaySpellbook(serial).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplaySpellbook(serial); - [Fact] - public void TestDisplaySpellbookHS() - { - var serial = (Serial)0x1024; + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var expected = new DisplaySpellbookHS(serial).Compile(); + [Fact] + public void TestDisplaySpellbookHS() + { + Serial serial = (Serial)0x1024; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = ns.ProtocolChanges | ProtocolChanges.ContainerGridLines | ProtocolChanges.HighSeas; - ns.SendDisplaySpellbook(serial); + var expected = new DisplaySpellbookHS(serial).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = ns.ProtocolChanges | ProtocolChanges.ContainerGridLines | ProtocolChanges.HighSeas; + ns.SendDisplaySpellbook(serial); - [Fact] - public void TestNewSpellbookContent() - { - var serial = (Serial)0x1024; - ushort graphic = 100; - ushort offset = 10; - ulong content = 0x123456789ABCDEF0; - var opl = ObjectPropertyList.Enabled; + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var expected = new NewSpellbookContent(serial, graphic, offset, content).Compile(); + [Fact] + public void TestNewSpellbookContent() + { + Serial serial = (Serial)0x1024; + ushort graphic = 100; + ushort offset = 10; + ulong content = 0x123456789ABCDEF0; + bool opl = ObjectPropertyList.Enabled; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = ns.ProtocolChanges | ProtocolChanges.ContainerGridLines | ProtocolChanges.NewSpellbook; - ObjectPropertyList.Enabled = true; - ns.SendSpellbookContent(serial, graphic, offset, content); - ObjectPropertyList.Enabled = opl; + var expected = new NewSpellbookContent(serial, graphic, offset, content).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = ns.ProtocolChanges | ProtocolChanges.ContainerGridLines | ProtocolChanges.NewSpellbook; + ObjectPropertyList.Enabled = true; + ns.SendSpellbookContent(serial, graphic, offset, content); + ObjectPropertyList.Enabled = opl; - [Fact] - public void TestSpellbookContent() - { - var serial = (Serial)0x1024; - ushort offset = 10; - ushort graphic = 100; - ulong content = 0x123456789ABCDEF0; + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var expected = new SpellbookContent(serial, offset, content).Compile(); + [Fact] + public void TestSpellbookContent() + { + Serial serial = (Serial)0x1024; + ushort offset = 10; + ushort graphic = 100; + ulong content = 0x123456789ABCDEF0; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendSpellbookContent(serial, graphic, offset, content); + var expected = new SpellbookContent(serial, offset, content).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendSpellbookContent(serial, graphic, offset, content); - [Fact] - public void TestSpellbookContent6017() - { - var serial = (Serial)0x1024; - ushort offset = 10; - ushort graphic = 100; - ulong content = 0x123456789ABCDEF0; + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var expected = new SpellbookContent6017(serial, offset, content).Compile(); + [Fact] + public void TestSpellbookContent6017() + { + Serial serial = (Serial)0x1024; + ushort offset = 10; + ushort graphic = 100; + ulong content = 0x123456789ABCDEF0; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges |= ProtocolChanges.ContainerGridLines; - ns.SendSpellbookContent(serial, graphic, offset, content); + var expected = new SpellbookContent6017(serial, offset, content).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges |= ProtocolChanges.ContainerGridLines; + ns.SendSpellbookContent(serial, graphic, offset, content); - [Fact] - public void TestContainerContentUpdate() - { - var serial = (Serial)0x1024; - var item = new Item(serial); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var expected = new ContainerContentUpdate(item).Compile(); + [Fact] + public void TestContainerContentUpdate() + { + Serial serial = (Serial)0x1024; + var item = new Item(serial); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendContainerContentUpdate(item); + var expected = new ContainerContentUpdate(item).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendContainerContentUpdate(item); - [Fact] - public void TestContainerContentUpdate6017() - { - var serial = (Serial)0x1024; - var item = new Item(serial); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var expected = new ContainerContentUpdate6017(item).Compile(); + [Fact] + public void TestContainerContentUpdate6017() + { + Serial serial = (Serial)0x1024; + var item = new Item(serial); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges |= ProtocolChanges.ContainerGridLines; - ns.SendContainerContentUpdate(item); + var expected = new ContainerContentUpdate6017(item).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges |= ProtocolChanges.ContainerGridLines; + ns.SendContainerContentUpdate(item); - [Fact] - public void TestContainerContent() - { - var cont = new Container(World.NewItem); - cont.AddItem(new Item(World.NewItem)); - cont.Map = Map.Felucca; + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); - m.AccessLevel = AccessLevel.Administrator; - m.Map = Map.Felucca; + [Fact] + public void TestContainerContent() + { + var cont = new Container(World.NewItem); + cont.AddItem(new Item(World.NewItem)); + cont.Map = Map.Felucca; - var expected = new ContainerContent(m, cont).Compile(); + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); + m.AccessLevel = AccessLevel.Administrator; + m.Map = Map.Felucca; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendContainerContent(m, cont); + var expected = new ContainerContent(m, cont).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendContainerContent(m, cont); - [Fact] - public void TestContainerContent6017() - { - var cont = new Container(World.NewItem); - cont.AddItem(new Item(World.NewItem)); - cont.Map = Map.Felucca; + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); - m.AccessLevel = AccessLevel.Administrator; - m.Map = Map.Felucca; + [Fact] + public void TestContainerContent6017() + { + var cont = new Container(World.NewItem); + cont.AddItem(new Item(World.NewItem)); + cont.Map = Map.Felucca; - var expected = new ContainerContent6017(m, cont).Compile(); + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); + m.AccessLevel = AccessLevel.Administrator; + m.Map = Map.Felucca; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges |= ProtocolChanges.ContainerGridLines; - ns.SendContainerContent(m, cont); + var expected = new ContainerContent6017(m, cont).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges |= ProtocolChanges.ContainerGridLines; + ns.SendContainerContent(m, cont); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ContainerPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ContainerPackets.cs index 23adb5336..543533b48 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ContainerPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ContainerPackets.cs @@ -1,269 +1,270 @@ using System; using System.IO; -namespace Server.Network; - -public sealed class DisplaySpellbook : Packet +namespace Server.Network { - public DisplaySpellbook(Serial book) : base(0x24, 7) + public sealed class DisplaySpellbook : Packet { - Stream.Write(book); - Stream.Write((short)-1); - } -} - -public sealed class DisplaySpellbookHS : Packet -{ - public DisplaySpellbookHS(Serial book) : base(0x24, 9) - { - Stream.Write(book); - Stream.Write((short)-1); - Stream.Write((short)0x7D); - } -} - -public sealed class NewSpellbookContent : Packet -{ - public NewSpellbookContent(Serial spellbook, int graphic, int offset, ulong content) : base(0xBF) - { - EnsureCapacity(23); - - Stream.Write((short)0x1B); - Stream.Write((short)0x01); - - Stream.Write(spellbook); - Stream.Write((short)graphic); - Stream.Write((short)offset); - - for (var i = 0; i < 8; ++i) + public DisplaySpellbook(Serial book) : base(0x24, 7) { - Stream.Write((byte)(content >> (i * 8))); + Stream.Write(book); + Stream.Write((short)-1); } } -} -public sealed class SpellbookContent : Packet -{ - public SpellbookContent(Serial spellbook, int offset, ulong content) : base(0x3C) + public sealed class DisplaySpellbookHS : Packet { - EnsureCapacity(5 + 64 * 19); - - var written = 0; - - Stream.Write((ushort)0); - - ulong mask = 1; - - for (var i = 0; i < 64; ++i, mask <<= 1) + public DisplaySpellbookHS(Serial book) : base(0x24, 9) { - if ((content & mask) != 0) - { - Stream.Write(0x7FFFFFFF - i); - Stream.Write((ushort)0); - Stream.Write((byte)0); - Stream.Write((ushort)(i + offset)); - Stream.Write((short)0); - Stream.Write((short)0); - Stream.Write(spellbook); - Stream.Write((short)0); + Stream.Write(book); + Stream.Write((short)-1); + Stream.Write((short)0x7D); + } + } - ++written; + public sealed class NewSpellbookContent : Packet + { + public NewSpellbookContent(Serial spellbook, int graphic, int offset, ulong content) : base(0xBF) + { + EnsureCapacity(23); + + Stream.Write((short)0x1B); + Stream.Write((short)0x01); + + Stream.Write(spellbook); + Stream.Write((short)graphic); + Stream.Write((short)offset); + + for (var i = 0; i < 8; ++i) + { + Stream.Write((byte)(content >> (i * 8))); } } - - Stream.Seek(3, SeekOrigin.Begin); - Stream.Write((ushort)written); } -} -public sealed class SpellbookContent6017 : Packet -{ - public SpellbookContent6017(Serial spellbook, int offset, ulong content) : base(0x3C) + public sealed class SpellbookContent : Packet { - EnsureCapacity(5 + 64 * 20); - - var written = 0; - - Stream.Write((ushort)0); - - ulong mask = 1; - - for (var i = 0; i < 64; ++i, mask <<= 1) + public SpellbookContent(Serial spellbook, int offset, ulong content) : base(0x3C) { - if ((content & mask) != 0) + EnsureCapacity(5 + 64 * 19); + + var written = 0; + + Stream.Write((ushort)0); + + ulong mask = 1; + + for (var i = 0; i < 64; ++i, mask <<= 1) { - Stream.Write(0x7FFFFFFF - i); - Stream.Write((ushort)0); - Stream.Write((byte)0); - Stream.Write((ushort)(i + offset)); - Stream.Write((short)0); - Stream.Write((short)0); - Stream.Write((byte)0); // Grid Location? - Stream.Write(spellbook); - Stream.Write((short)0); + if ((content & mask) != 0) + { + Stream.Write(0x7FFFFFFF - i); + Stream.Write((ushort)0); + Stream.Write((byte)0); + Stream.Write((ushort)(i + offset)); + Stream.Write((short)0); + Stream.Write((short)0); + Stream.Write(spellbook); + Stream.Write((short)0); - ++written; + ++written; + } } + + Stream.Seek(3, SeekOrigin.Begin); + Stream.Write((ushort)written); } - - Stream.Seek(3, SeekOrigin.Begin); - Stream.Write((ushort)written); } -} -public sealed class ContainerDisplay : Packet -{ - public ContainerDisplay(Serial cont, int gumpId) : base(0x24, 7) + public sealed class SpellbookContent6017 : Packet { - Stream.Write(cont); - Stream.Write((short)gumpId); - } -} - -public sealed class ContainerDisplayHS : Packet -{ - public ContainerDisplayHS(Serial cont, int gumpId) : base(0x24, 9) - { - Stream.Write(cont); - Stream.Write((short)gumpId); - Stream.Write((short)0x7D); - } -} - -public sealed class ContainerContentUpdate : Packet -{ - public ContainerContentUpdate(Item item) : base(0x25, 20) - { - Serial parentSerial; - - if (item.Parent is Item parentItem) + public SpellbookContent6017(Serial spellbook, int offset, ulong content) : base(0x3C) { - parentSerial = parentItem.Serial; - } - else - { - Console.WriteLine("Warning: ContainerContentUpdate on item with !(parent is Item)"); - parentSerial = Serial.Zero; - } + EnsureCapacity(5 + 64 * 20); - Stream.Write(item.Serial); - Stream.Write((ushort)item.ItemID); - Stream.Write((byte)0); // signed, itemID offset - Stream.Write((ushort)Math.Min(item.Amount, ushort.MaxValue)); - Stream.Write((short)item.X); - Stream.Write((short)item.Y); - Stream.Write(parentSerial); - Stream.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue)); - } -} + var written = 0; -public sealed class ContainerContentUpdate6017 : Packet -{ - public ContainerContentUpdate6017(Item item) : base(0x25, 21) - { - Serial parentSerial; + Stream.Write((ushort)0); - if (item.Parent is Item parentItem) - { - parentSerial = parentItem.Serial; - } - else - { - Console.WriteLine("Warning: ContainerContentUpdate on item with !(parent is Item)"); - parentSerial = Serial.Zero; - } + ulong mask = 1; - Stream.Write(item.Serial); - Stream.Write((ushort)item.ItemID); - Stream.Write((byte)0); // signed, itemID offset - Stream.Write((ushort)Math.Min(item.Amount, ushort.MaxValue)); - Stream.Write((short)item.X); - Stream.Write((short)item.Y); - Stream.Write((byte)0); // Grid Location? - Stream.Write(parentSerial); - Stream.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue)); - } -} - -public sealed class ContainerContent : Packet -{ - public ContainerContent(Mobile beholder, Item beheld) : base(0x3C) - { - var items = beheld.Items; - var count = items.Count; - - EnsureCapacity(5 + count * 19); - - var pos = Stream.Position; - - var written = 0; - - Stream.Write((ushort)0); - - for (var i = 0; i < count; ++i) - { - var child = items[i]; - - if (!child.Deleted && beholder.CanSee(child)) + for (var i = 0; i < 64; ++i, mask <<= 1) { - var loc = child.Location; + if ((content & mask) != 0) + { + Stream.Write(0x7FFFFFFF - i); + Stream.Write((ushort)0); + Stream.Write((byte)0); + Stream.Write((ushort)(i + offset)); + Stream.Write((short)0); + Stream.Write((short)0); + Stream.Write((byte)0); // Grid Location? + Stream.Write(spellbook); + Stream.Write((short)0); - Stream.Write(child.Serial); - Stream.Write((ushort)child.ItemID); - Stream.Write((byte)0); // signed, itemID offset - Stream.Write((ushort)Math.Min(child.Amount, ushort.MaxValue)); - Stream.Write((short)loc.X); - Stream.Write((short)loc.Y); - Stream.Write(beheld.Serial); - Stream.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue)); - - ++written; + ++written; + } } - } - Stream.Seek(pos, SeekOrigin.Begin); - Stream.Write((ushort)written); + Stream.Seek(3, SeekOrigin.Begin); + Stream.Write((ushort)written); + } + } + + public sealed class ContainerDisplay : Packet + { + public ContainerDisplay(Serial cont, int gumpId) : base(0x24, 7) + { + Stream.Write(cont); + Stream.Write((short)gumpId); + } + } + + public sealed class ContainerDisplayHS : Packet + { + public ContainerDisplayHS(Serial cont, int gumpId) : base(0x24, 9) + { + Stream.Write(cont); + Stream.Write((short)gumpId); + Stream.Write((short)0x7D); + } + } + + public sealed class ContainerContentUpdate : Packet + { + public ContainerContentUpdate(Item item) : base(0x25, 20) + { + Serial parentSerial; + + if (item.Parent is Item parentItem) + { + parentSerial = parentItem.Serial; + } + else + { + Console.WriteLine("Warning: ContainerContentUpdate on item with !(parent is Item)"); + parentSerial = Serial.Zero; + } + + Stream.Write(item.Serial); + Stream.Write((ushort)item.ItemID); + Stream.Write((byte)0); // signed, itemID offset + Stream.Write((ushort)Math.Min(item.Amount, ushort.MaxValue)); + Stream.Write((short)item.X); + Stream.Write((short)item.Y); + Stream.Write(parentSerial); + Stream.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue)); + } + } + + public sealed class ContainerContentUpdate6017 : Packet + { + public ContainerContentUpdate6017(Item item) : base(0x25, 21) + { + Serial parentSerial; + + if (item.Parent is Item parentItem) + { + parentSerial = parentItem.Serial; + } + else + { + Console.WriteLine("Warning: ContainerContentUpdate on item with !(parent is Item)"); + parentSerial = Serial.Zero; + } + + Stream.Write(item.Serial); + Stream.Write((ushort)item.ItemID); + Stream.Write((byte)0); // signed, itemID offset + Stream.Write((ushort)Math.Min(item.Amount, ushort.MaxValue)); + Stream.Write((short)item.X); + Stream.Write((short)item.Y); + Stream.Write((byte)0); // Grid Location? + Stream.Write(parentSerial); + Stream.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue)); + } + } + + public sealed class ContainerContent : Packet + { + public ContainerContent(Mobile beholder, Item beheld) : base(0x3C) + { + var items = beheld.Items; + var count = items.Count; + + EnsureCapacity(5 + count * 19); + + var pos = Stream.Position; + + var written = 0; + + Stream.Write((ushort)0); + + for (var i = 0; i < count; ++i) + { + var child = items[i]; + + if (!child.Deleted && beholder.CanSee(child)) + { + var loc = child.Location; + + Stream.Write(child.Serial); + Stream.Write((ushort)child.ItemID); + Stream.Write((byte)0); // signed, itemID offset + Stream.Write((ushort)Math.Min(child.Amount, ushort.MaxValue)); + Stream.Write((short)loc.X); + Stream.Write((short)loc.Y); + Stream.Write(beheld.Serial); + Stream.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue)); + + ++written; + } + } + + Stream.Seek(pos, SeekOrigin.Begin); + Stream.Write((ushort)written); + } + } + + public sealed class ContainerContent6017 : Packet + { + public ContainerContent6017(Mobile beholder, Item beheld) : base(0x3C) + { + var items = beheld.Items; + var count = items.Count; + + EnsureCapacity(5 + count * 20); + + var pos = Stream.Position; + + var written = 0; + + Stream.Write((ushort)0); + + for (var i = 0; i < count; ++i) + { + var child = items[i]; + + if (!child.Deleted && beholder.CanSee(child)) + { + var loc = child.Location; + + Stream.Write(child.Serial); + Stream.Write((ushort)child.ItemID); + Stream.Write((byte)0); // signed, itemID offset + Stream.Write((ushort)Math.Min(child.Amount, ushort.MaxValue)); + Stream.Write((short)loc.X); + Stream.Write((short)loc.Y); + Stream.Write((byte)0); // Grid Location? + Stream.Write(beheld.Serial); + Stream.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue)); + + ++written; + } + } + + Stream.Seek(pos, SeekOrigin.Begin); + Stream.Write((ushort)written); + } } } - -public sealed class ContainerContent6017 : Packet -{ - public ContainerContent6017(Mobile beholder, Item beheld) : base(0x3C) - { - var items = beheld.Items; - var count = items.Count; - - EnsureCapacity(5 + count * 20); - - var pos = Stream.Position; - - var written = 0; - - Stream.Write((ushort)0); - - for (var i = 0; i < count; ++i) - { - var child = items[i]; - - if (!child.Deleted && beholder.CanSee(child)) - { - var loc = child.Location; - - Stream.Write(child.Serial); - Stream.Write((ushort)child.ItemID); - Stream.Write((byte)0); // signed, itemID offset - Stream.Write((ushort)Math.Min(child.Amount, ushort.MaxValue)); - Stream.Write((short)loc.X); - Stream.Write((short)loc.Y); - Stream.Write((byte)0); // Grid Location? - Stream.Write(beheld.Serial); - Stream.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue)); - - ++written; - } - } - - Stream.Seek(pos, SeekOrigin.Begin); - Stream.Write((ushort)written); - } -} \ No newline at end of file diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/DamagePacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/DamagePacketTests.cs index 5f31a49d4..e5ec188a6 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/DamagePacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/DamagePacketTests.cs @@ -1,38 +1,38 @@ using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class DamagePacketTests +namespace Server.Tests.Network { - [Theory, InlineData(10), InlineData(-5), InlineData(1024)] - public void TestDamagePacketOld(int inputAmount) + public class DamagePacketTests : IClassFixture { - var serial = (Serial)0x1024; + [Theory, InlineData(10), InlineData(-5), InlineData(1024)] + public void TestDamagePacketOld(int inputAmount) + { + Serial serial = (Serial)0x1024; - var expected = new DamagePacketOld(serial, inputAmount).Compile(); + var expected = new DamagePacketOld(serial, inputAmount).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDamage(serial, inputAmount); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDamage(serial, inputAmount); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory, InlineData(10), InlineData(-5), InlineData(1024), InlineData(100000)] - public void TestDamage(int inputAmount) - { - var serial = (Serial)0x1024; + [Theory, InlineData(10), InlineData(-5), InlineData(1024), InlineData(100000)] + public void TestDamage(int inputAmount) + { + Serial serial = (Serial)0x1024; - var expected = new DamagePacket(serial, inputAmount).Compile(); + var expected = new DamagePacket(serial, inputAmount).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = ProtocolChanges.DamagePacket; + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = ProtocolChanges.DamagePacket; - ns.SendDamage(serial, inputAmount); + ns.SendDamage(serial, inputAmount); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/DamagePackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/DamagePackets.cs index 77d4d9cce..fb38f8767 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/DamagePackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/DamagePackets.cs @@ -1,27 +1,28 @@ using System; -namespace Server.Network; - -public sealed class DamagePacketOld : Packet +namespace Server.Network { - public DamagePacketOld(Serial mobile, int amount) : base(0xBF) + public sealed class DamagePacketOld : Packet { - EnsureCapacity(11); + public DamagePacketOld(Serial mobile, int amount) : base(0xBF) + { + EnsureCapacity(11); - Stream.Write((short)0x22); - Stream.Write((byte)1); - Stream.Write(mobile); + Stream.Write((short)0x22); + Stream.Write((byte)1); + Stream.Write(mobile); - Stream.Write((byte)Math.Clamp(amount, 0, 255)); + Stream.Write((byte)Math.Clamp(amount, 0, 255)); + } + } + + public sealed class DamagePacket : Packet + { + public DamagePacket(Serial mobile, int amount) : base(0x0B, 7) + { + Stream.Write(mobile); + + Stream.Write((ushort)Math.Clamp(amount, 0, 0xFFFF)); + } } } - -public sealed class DamagePacket : Packet -{ - public DamagePacket(Serial mobile, int amount) : base(0x0B, 7) - { - Stream.Write(mobile); - - Stream.Write((ushort)Math.Clamp(amount, 0, 0xFFFF)); - } -} \ No newline at end of file diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EffectPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EffectPacketTests.cs index 7ade294f6..73af5b0a4 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EffectPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EffectPacketTests.cs @@ -2,117 +2,117 @@ using System; using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class EffectPackets +namespace Server.Tests.Network { - [Theory, InlineData(10, 1000, 10, 5)] - public void TestSoundEffect(ushort soundID, int x, int y, int z) + public class EffectPackets { - var p = new Point3D(x, y, z); + [Theory, InlineData(10, 1000, 10, 5)] + public void TestSoundEffect(ushort soundID, int x, int y, int z) + { + var p = new Point3D(x, y, z); - var expected = new PlaySound(soundID, p).Compile(); + var expected = new PlaySound(soundID, p).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendSoundEffect(soundID, p); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendSoundEffect(soundID, p); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestParticleEffect() - { - var effectType = EffectType.Moving; - var serial = (Serial)0x4000; - var from = (Serial)0x1000; - var to = (Serial)0x2000; - var itemId = 0x100; - var fromPoint = new Point3D(1000, 100, -10); - var toPoint = new Point3D(1500, 500, 0); - byte speed = 3; - byte duration = 2; - var direction = false; - var explode = false; - var hue = 0x1024; - var renderMode = 1; - ushort effect = 3; - ushort explodeEffect = 0; - ushort explodeSound = 0; - byte layer = 9; - ushort unknown = 0; + [Fact] + public void TestParticleEffect() + { + var effectType = EffectType.Moving; + Serial serial = (Serial)0x4000; + Serial from = (Serial)0x1000; + Serial to = (Serial)0x2000; + var itemId = 0x100; + var fromPoint = new Point3D(1000, 100, -10); + var toPoint = new Point3D(1500, 500, 0); + byte speed = 3; + byte duration = 2; + var direction = false; + var explode = false; + var hue = 0x1024; + var renderMode = 1; + ushort effect = 3; + ushort explodeEffect = 0; + ushort explodeSound = 0; + byte layer = 9; + ushort unknown = 0; - var expected = new ParticleEffect( - effectType, from, to, itemId, fromPoint, toPoint, speed, duration, direction, - explode, hue, renderMode, effect, explodeEffect, explodeSound, serial, layer, - unknown - ).Compile(); + var expected = new ParticleEffect( + effectType, from, to, itemId, fromPoint, toPoint, speed, duration, direction, + explode, hue, renderMode, effect, explodeEffect, explodeSound, serial, layer, + unknown + ).Compile(); - Span actual = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength]; - OutgoingEffectPackets.CreateParticleEffect( - actual, - effectType, from, to, itemId, fromPoint, toPoint, speed, duration, direction, - explode, hue, renderMode, effect, explodeEffect, explodeSound, serial, layer, - unknown - ); + Span actual = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength]; + OutgoingEffectPackets.CreateParticleEffect( + actual, + effectType, from, to, itemId, fromPoint, toPoint, speed, duration, direction, + explode, hue, renderMode, effect, explodeEffect, explodeSound, serial, layer, + unknown + ); - AssertThat.Equal(actual, expected); - } + AssertThat.Equal(actual, expected); + } - [Fact] - public void TestHuedEffect() - { - var effectType = EffectType.Moving; - var from = (Serial)0x1000; - var to = (Serial)0x2000; - var itemId = 0x100; - var fromPoint = new Point3D(1000, 100, -10); - var toPoint = new Point3D(1500, 500, 0); - byte speed = 3; - byte duration = 2; - var direction = false; - var explode = false; - var hue = 0x1024; - var renderMode = 1; + [Fact] + public void TestHuedEffect() + { + var effectType = EffectType.Moving; + Serial from = (Serial)0x1000; + Serial to = (Serial)0x2000; + var itemId = 0x100; + var fromPoint = new Point3D(1000, 100, -10); + var toPoint = new Point3D(1500, 500, 0); + byte speed = 3; + byte duration = 2; + var direction = false; + var explode = false; + var hue = 0x1024; + var renderMode = 1; - var expected = new HuedEffect( - effectType, from, to, itemId, fromPoint, toPoint, speed, - duration, direction, explode, hue, renderMode - ).Compile(); + var expected = new HuedEffect( + effectType, from, to, itemId, fromPoint, toPoint, speed, + duration, direction, explode, hue, renderMode + ).Compile(); - Span actual = stackalloc byte[OutgoingEffectPackets.HuedEffectLength]; - OutgoingEffectPackets.CreateHuedEffect( - actual, - effectType, from, to, itemId, fromPoint, toPoint, speed, - duration, direction, explode, hue, renderMode - ); + Span actual = stackalloc byte[OutgoingEffectPackets.HuedEffectLength]; + OutgoingEffectPackets.CreateHuedEffect( + actual, + effectType, from, to, itemId, fromPoint, toPoint, speed, + duration, direction, explode, hue, renderMode + ); - AssertThat.Equal(actual, expected); - } + AssertThat.Equal(actual, expected); + } - [Theory, InlineData(ScreenEffectType.DarkFlash), InlineData(ScreenEffectType.FadeInOut)] - public void TestScreenEffect(ScreenEffectType screenType) - { - var expected = new ScreenEffect(screenType).Compile(); + [Theory, InlineData(ScreenEffectType.DarkFlash), InlineData(ScreenEffectType.FadeInOut)] + public void TestScreenEffect(ScreenEffectType screenType) + { + var expected = new ScreenEffect(screenType).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendScreenEffect(screenType); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendScreenEffect(screenType); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestBoltEffect() - { - IEntity entity = new Entity((Serial)0x1000, new Point3D(1000, 100, -10), Map.Felucca); - var hue = 0x1024; - var expected = new BoltEffect(entity, hue).Compile(); + [Fact] + public void TestBoltEffect() + { + IEntity entity = new Entity((Serial)0x1000, new Point3D(1000, 100, -10), Map.Felucca); + var hue = 0x1024; + var expected = new BoltEffect(entity, hue).Compile(); - Span actual = stackalloc byte[OutgoingEffectPackets.BoltEffectLength]; - OutgoingEffectPackets.CreateBoltEffect(actual, entity, hue); + Span actual = stackalloc byte[OutgoingEffectPackets.BoltEffectLength]; + OutgoingEffectPackets.CreateBoltEffect(actual, entity, hue); - AssertThat.Equal(actual, expected); + AssertThat.Equal(actual, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EffectPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EffectPackets.cs index e18256383..c176c2d61 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EffectPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EffectPackets.cs @@ -1,264 +1,265 @@ -namespace Server.Network; - -public sealed class PlaySound : Packet +namespace Server.Network { - public PlaySound(int soundID, IPoint3D target) : base(0x54, 12) + public sealed class PlaySound : Packet { - Stream.Write((byte)1); // flags - Stream.Write((short)soundID); - Stream.Write((short)0); // volume - Stream.Write((short)target.X); - Stream.Write((short)target.Y); - Stream.Write((short)target.Z); + public PlaySound(int soundID, IPoint3D target) : base(0x54, 12) + { + Stream.Write((byte)1); // flags + Stream.Write((short)soundID); + Stream.Write((short)0); // volume + Stream.Write((short)target.X); + Stream.Write((short)target.Y); + Stream.Write((short)target.Z); + } + } + + public class ParticleEffect : Packet + { + public ParticleEffect( + EffectType type, Serial from, Serial to, int itemID, IPoint3D fromPoint, IPoint3D toPoint, + int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode, int effect, + int explodeEffect, int explodeSound, Serial serial, int layer, int unknown + ) : base(0xC7, 49) + { + Stream.Write((byte)type); + Stream.Write(from); + Stream.Write(to); + Stream.Write((short)itemID); + Stream.Write((short)fromPoint.X); + Stream.Write((short)fromPoint.Y); + Stream.Write((sbyte)fromPoint.Z); + Stream.Write((short)toPoint.X); + Stream.Write((short)toPoint.Y); + Stream.Write((sbyte)toPoint.Z); + Stream.Write((byte)speed); + Stream.Write((byte)duration); + Stream.Write((byte)0); + Stream.Write((byte)0); + Stream.Write(fixedDirection); + Stream.Write(explode); + Stream.Write(hue); + Stream.Write(renderMode); + Stream.Write((short)effect); + Stream.Write((short)explodeEffect); + Stream.Write((short)explodeSound); + Stream.Write(serial); + Stream.Write((byte)layer); + Stream.Write((short)unknown); + } + } + + public class HuedEffect : Packet + { + public HuedEffect( + EffectType type, Serial from, Serial to, int itemID, IPoint3D fromPoint, IPoint3D toPoint, + int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode + ) : base(0xC0, 36) + { + Stream.Write((byte)type); + Stream.Write(from); + Stream.Write(to); + Stream.Write((short)itemID); + Stream.Write((short)fromPoint.X); + Stream.Write((short)fromPoint.Y); + Stream.Write((sbyte)fromPoint.Z); + Stream.Write((short)toPoint.X); + Stream.Write((short)toPoint.Y); + Stream.Write((sbyte)toPoint.Z); + Stream.Write((byte)speed); + Stream.Write((byte)duration); + Stream.Write((byte)0); + Stream.Write((byte)0); + Stream.Write(fixedDirection); + Stream.Write(explode); + Stream.Write(hue); + Stream.Write(renderMode); + } + } + + public sealed class TargetParticleEffect : ParticleEffect + { + public TargetParticleEffect( + IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, + int layer, int unknown + ) : base( + EffectType.FixedFrom, + e.Serial, + Serial.Zero, + itemID, + e.Location, + e.Location, + speed, + duration, + true, + false, + hue, + renderMode, + effect, + 1, + 0, + e.Serial, + layer, + unknown + ) + { + } + } + + public sealed class TargetEffect : HuedEffect + { + public TargetEffect(IEntity e, int itemID, int speed, int duration, int hue, int renderMode) : base( + EffectType.FixedFrom, + e.Serial, + Serial.Zero, + itemID, + e.Location, + e.Location, + speed, + duration, + true, + false, + hue, + renderMode + ) + { + } + } + + public sealed class LocationParticleEffect : ParticleEffect + { + public LocationParticleEffect( + IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, + int unknown + ) : base( + EffectType.FixedXYZ, + e.Serial, + Serial.Zero, + itemID, + e.Location, + e.Location, + speed, + duration, + true, + false, + hue, + renderMode, + effect, + 1, + 0, + e.Serial, + 255, + unknown + ) + { + } + } + + public sealed class LocationEffect : HuedEffect + { + public LocationEffect(IPoint3D p, int itemID, int speed, int duration, int hue, int renderMode) : base( + EffectType.FixedXYZ, + Serial.Zero, + Serial.Zero, + itemID, + p, + p, + speed, + duration, + true, + false, + hue, + renderMode + ) + { + } + } + + public sealed class MovingParticleEffect : ParticleEffect + { + public MovingParticleEffect( + IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, + bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, EffectLayer layer, + int unknown + ) : base( + EffectType.Moving, + from.Serial, + to.Serial, + itemID, + from.Location, + to.Location, + speed, + duration, + fixedDirection, + explodes, + hue, + renderMode, + effect, + explodeEffect, + explodeSound, + Serial.Zero, + (int)layer, + unknown + ) + { + } + } + + public sealed class MovingEffect : HuedEffect + { + public MovingEffect( + IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, + bool explodes, int hue, int renderMode + ) : base( + EffectType.Moving, + from.Serial, + to.Serial, + itemID, + from.Location, + to.Location, + speed, + duration, + fixedDirection, + explodes, + hue, + renderMode + ) + { + } + } + + public class ScreenEffect : Packet + { + public ScreenEffect(ScreenEffectType type) + : base(0x70, 28) + { + Stream.Write((byte)0x04); + Stream.Fill(8); + Stream.Write((short)type); + Stream.Fill(16); + } + } + + public sealed class BoltEffect : Packet + { + public BoltEffect(IEntity target, int hue) : base(0xC0, 36) + { + Stream.Write((byte)0x01); // type + Stream.Write(target.Serial); + Stream.Write(Serial.Zero); + Stream.Write((short)0); // itemID + Stream.Write((short)target.X); + Stream.Write((short)target.Y); + Stream.Write((sbyte)target.Z); + Stream.Write((short)target.X); + Stream.Write((short)target.Y); + Stream.Write((sbyte)target.Z); + Stream.Write((byte)0); // speed + Stream.Write((byte)0); // duration + Stream.Write((short)0); // unk + Stream.Write(false); // fixed direction + Stream.Write(false); // explode + Stream.Write(hue); + Stream.Write(0); // render mode + } } } - -public class ParticleEffect : Packet -{ - public ParticleEffect( - EffectType type, Serial from, Serial to, int itemID, IPoint3D fromPoint, IPoint3D toPoint, - int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode, int effect, - int explodeEffect, int explodeSound, Serial serial, int layer, int unknown - ) : base(0xC7, 49) - { - Stream.Write((byte)type); - Stream.Write(from); - Stream.Write(to); - Stream.Write((short)itemID); - Stream.Write((short)fromPoint.X); - Stream.Write((short)fromPoint.Y); - Stream.Write((sbyte)fromPoint.Z); - Stream.Write((short)toPoint.X); - Stream.Write((short)toPoint.Y); - Stream.Write((sbyte)toPoint.Z); - Stream.Write((byte)speed); - Stream.Write((byte)duration); - Stream.Write((byte)0); - Stream.Write((byte)0); - Stream.Write(fixedDirection); - Stream.Write(explode); - Stream.Write(hue); - Stream.Write(renderMode); - Stream.Write((short)effect); - Stream.Write((short)explodeEffect); - Stream.Write((short)explodeSound); - Stream.Write(serial); - Stream.Write((byte)layer); - Stream.Write((short)unknown); - } -} - -public class HuedEffect : Packet -{ - public HuedEffect( - EffectType type, Serial from, Serial to, int itemID, IPoint3D fromPoint, IPoint3D toPoint, - int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode - ) : base(0xC0, 36) - { - Stream.Write((byte)type); - Stream.Write(from); - Stream.Write(to); - Stream.Write((short)itemID); - Stream.Write((short)fromPoint.X); - Stream.Write((short)fromPoint.Y); - Stream.Write((sbyte)fromPoint.Z); - Stream.Write((short)toPoint.X); - Stream.Write((short)toPoint.Y); - Stream.Write((sbyte)toPoint.Z); - Stream.Write((byte)speed); - Stream.Write((byte)duration); - Stream.Write((byte)0); - Stream.Write((byte)0); - Stream.Write(fixedDirection); - Stream.Write(explode); - Stream.Write(hue); - Stream.Write(renderMode); - } -} - -public sealed class TargetParticleEffect : ParticleEffect -{ - public TargetParticleEffect( - IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, - int layer, int unknown - ) : base( - EffectType.FixedFrom, - e.Serial, - Serial.Zero, - itemID, - e.Location, - e.Location, - speed, - duration, - true, - false, - hue, - renderMode, - effect, - 1, - 0, - e.Serial, - layer, - unknown - ) - { - } -} - -public sealed class TargetEffect : HuedEffect -{ - public TargetEffect(IEntity e, int itemID, int speed, int duration, int hue, int renderMode) : base( - EffectType.FixedFrom, - e.Serial, - Serial.Zero, - itemID, - e.Location, - e.Location, - speed, - duration, - true, - false, - hue, - renderMode - ) - { - } -} - -public sealed class LocationParticleEffect : ParticleEffect -{ - public LocationParticleEffect( - IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, - int unknown - ) : base( - EffectType.FixedXYZ, - e.Serial, - Serial.Zero, - itemID, - e.Location, - e.Location, - speed, - duration, - true, - false, - hue, - renderMode, - effect, - 1, - 0, - e.Serial, - 255, - unknown - ) - { - } -} - -public sealed class LocationEffect : HuedEffect -{ - public LocationEffect(IPoint3D p, int itemID, int speed, int duration, int hue, int renderMode) : base( - EffectType.FixedXYZ, - Serial.Zero, - Serial.Zero, - itemID, - p, - p, - speed, - duration, - true, - false, - hue, - renderMode - ) - { - } -} - -public sealed class MovingParticleEffect : ParticleEffect -{ - public MovingParticleEffect( - IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, - bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, EffectLayer layer, - int unknown - ) : base( - EffectType.Moving, - from.Serial, - to.Serial, - itemID, - from.Location, - to.Location, - speed, - duration, - fixedDirection, - explodes, - hue, - renderMode, - effect, - explodeEffect, - explodeSound, - Serial.Zero, - (int)layer, - unknown - ) - { - } -} - -public sealed class MovingEffect : HuedEffect -{ - public MovingEffect( - IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, - bool explodes, int hue, int renderMode - ) : base( - EffectType.Moving, - from.Serial, - to.Serial, - itemID, - from.Location, - to.Location, - speed, - duration, - fixedDirection, - explodes, - hue, - renderMode - ) - { - } -} - -public class ScreenEffect : Packet -{ - public ScreenEffect(ScreenEffectType type) - : base(0x70, 28) - { - Stream.Write((byte)0x04); - Stream.Fill(8); - Stream.Write((short)type); - Stream.Fill(16); - } -} - -public sealed class BoltEffect : Packet -{ - public BoltEffect(IEntity target, int hue) : base(0xC0, 36) - { - Stream.Write((byte)0x01); // type - Stream.Write(target.Serial); - Stream.Write(Serial.Zero); - Stream.Write((short)0); // itemID - Stream.Write((short)target.X); - Stream.Write((short)target.Y); - Stream.Write((sbyte)target.Z); - Stream.Write((short)target.X); - Stream.Write((short)target.Y); - Stream.Write((sbyte)target.Z); - Stream.Write((byte)0); // speed - Stream.Write((byte)0); // duration - Stream.Write((short)0); // unk - Stream.Write(false); // fixed direction - Stream.Write(false); // explode - Stream.Write(hue); - Stream.Write(0); // render mode - } -} \ No newline at end of file diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPacketTests.cs index 8dc535e03..6192c90a6 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPacketTests.cs @@ -2,64 +2,64 @@ using System.Collections.Generic; using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class EquipmentPacketTests +namespace Server.Tests.Network { - [Theory] - [InlineData(null, false)] - [InlineData("Some Crafter", false)] - [InlineData("", true)] - public void TestDisplayEquipmentInfo(string name, bool unidentified) + public class EquipmentPacketTests : IClassFixture { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); - m.RawName = name; + [Theory] + [InlineData(null, false)] + [InlineData("Some Crafter", false)] + [InlineData("", true)] + public void TestDisplayEquipmentInfo(string name, bool unidentified) + { + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); + m.RawName = name; - var item = new Item(World.NewItem); + var item = new Item(World.NewItem); - var info = new EquipmentInfo( - 500000, - m, - unidentified, - new[] - { - new EquipInfoAttribute(500001, 1), - new EquipInfoAttribute(500002, 2), - new EquipInfoAttribute(500002, 3) - } - ); + var info = new EquipmentInfo( + 500000, + m, + unidentified, + new[] + { + new EquipInfoAttribute(500001, 1), + new EquipInfoAttribute(500002, 2), + new EquipInfoAttribute(500002, 3) + } + ); - var expected = new DisplayEquipmentInfo(item, info).Compile(); + var expected = new DisplayEquipmentInfo(item, info).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDisplayEquipmentInfo( - item.Serial, - info.Number, - info.Crafter?.RawName, - info.Unidentified, - new List(info.Attributes) - ); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplayEquipmentInfo( + item.Serial, + info.Number, + info.Crafter?.RawName, + info.Unidentified, + new List(info.Attributes) + ); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestEquipUpdate() - { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + [Fact] + public void TestEquipUpdate() + { + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var item = new Item(World.NewItem) { Parent = m }; + var item = new Item(World.NewItem) { Parent = m }; - var expected = new EquipUpdate(item).Compile(); + var expected = new EquipUpdate(item).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendEquipUpdate(item); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendEquipUpdate(item); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPackets.cs index 14b68445e..b00bf2c04 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/EquipmentPackets.cs @@ -1,97 +1,98 @@ using System; -namespace Server.Network; - -public class EquipmentInfo +namespace Server.Network { - public EquipmentInfo(int number, Mobile crafter, bool unidentified, EquipInfoAttribute[] attributes) + public class EquipmentInfo { - Number = number; - Crafter = crafter; - Unidentified = unidentified; - Attributes = attributes; + public EquipmentInfo(int number, Mobile crafter, bool unidentified, EquipInfoAttribute[] attributes) + { + Number = number; + Crafter = crafter; + Unidentified = unidentified; + Attributes = attributes; + } + + public int Number { get; } + + public Mobile Crafter { get; } + + public bool Unidentified { get; } + + public EquipInfoAttribute[] Attributes { get; } } - public int Number { get; } - - public Mobile Crafter { get; } - - public bool Unidentified { get; } - - public EquipInfoAttribute[] Attributes { get; } -} - -public sealed class DisplayEquipmentInfo : Packet -{ - public DisplayEquipmentInfo(Item item, EquipmentInfo info) : base(0xBF) + public sealed class DisplayEquipmentInfo : Packet { - var attrs = info.Attributes; - - EnsureCapacity( - 17 + (info.Crafter?.RawName?.Length ?? 0) + - (info.Unidentified ? 4 : 0) + attrs.Length * 6 - ); - - Stream.Write((short)0x10); - Stream.Write(item.Serial); - - Stream.Write(info.Number); - - var name = info.Crafter?.RawName?.Trim() ?? ""; - - if (name.Length > 0) + public DisplayEquipmentInfo(Item item, EquipmentInfo info) : base(0xBF) { - Stream.Write(-3); + var attrs = info.Attributes; - var length = name.Length; - Stream.Write((ushort)length); - Stream.WriteAsciiFixed(name, length); - } + EnsureCapacity( + 17 + (info.Crafter?.RawName?.Length ?? 0) + + (info.Unidentified ? 4 : 0) + attrs.Length * 6 + ); - if (info.Unidentified) - { - Stream.Write(-4); - } + Stream.Write((short)0x10); + Stream.Write(item.Serial); - for (var i = 0; i < attrs.Length; ++i) - { - Stream.Write(attrs[i].Number); - Stream.Write((short)attrs[i].Charges); - } + Stream.Write(info.Number); - Stream.Write(-1); - } -} + var name = info.Crafter?.RawName?.Trim() ?? ""; -public sealed class EquipUpdate : Packet -{ - public EquipUpdate(Item item) : base(0x2E, 15) - { - Serial parentSerial; - - var parent = item.Parent as Mobile; - var hue = item.Hue; - - if (parent != null) - { - parentSerial = parent.Serial; - - if (parent.SolidHueOverride >= 0) + if (name.Length > 0) { - hue = parent.SolidHueOverride; - } - } - else - { - Console.WriteLine("Warning: EquipUpdate on item with !(parent is Mobile)"); - parentSerial = Serial.Zero; - } + Stream.Write(-3); - Stream.Write(item.Serial); - Stream.Write((short)item.ItemID); - Stream.Write((byte)0); - Stream.Write((byte)item.Layer); - Stream.Write(parentSerial); - Stream.Write((short)hue); + var length = name.Length; + Stream.Write((ushort)length); + Stream.WriteAsciiFixed(name, length); + } + + if (info.Unidentified) + { + Stream.Write(-4); + } + + for (var i = 0; i < attrs.Length; ++i) + { + Stream.Write(attrs[i].Number); + Stream.Write((short)attrs[i].Charges); + } + + Stream.Write(-1); + } } -} \ No newline at end of file + + public sealed class EquipUpdate : Packet + { + public EquipUpdate(Item item) : base(0x2E, 15) + { + Serial parentSerial; + + var parent = item.Parent as Mobile; + var hue = item.Hue; + + if (parent != null) + { + parentSerial = parent.Serial; + + if (parent.SolidHueOverride >= 0) + { + hue = parent.SolidHueOverride; + } + } + else + { + Console.WriteLine("Warning: EquipUpdate on item with !(parent is Mobile)"); + parentSerial = Serial.Zero; + } + + Stream.Write(item.Serial); + Stream.Write((short)item.ItemID); + Stream.Write((byte)0); + Stream.Write((byte)item.Layer); + Stream.Write(parentSerial); + Stream.Write((short)hue); + } + } +} diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPacketTests.cs index edbe6b668..4247ca123 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPacketTests.cs @@ -3,8 +3,8 @@ using Xunit; namespace Server.Tests.Network; -[Collection("Sequential Server Tests")] -public class GumpPacketTests +[Collection("Sequential Tests")] +public class GumpPacketTests : IClassFixture { [Theory] [InlineData(100, 10)] @@ -12,27 +12,27 @@ public class GumpPacketTests { var expected = new CloseGump(typeId, buttonId).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendCloseGump(typeId, buttonId); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } [Fact] public void TestDisplaySignGump() { - var gumpSerial = (Serial)0x1000; + Serial gumpSerial = (Serial)0x1000; const int gumpId = 100; const string unknownString = "This is an unknown string"; const string caption = "This is a caption"; var expected = new DisplaySignGump(gumpSerial, gumpId, unknownString, caption).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendDisplaySignGump(gumpSerial, gumpId, unknownString, caption); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -41,12 +41,12 @@ public class GumpPacketTests { var gump = new NameChangeDeedGump(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); var expected = gump.Compile(ns).Compile(); ns.SendGump(gump); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -60,12 +60,12 @@ public class GumpPacketTests var gump = new AdminGump(m, AdminGumpPage.Clients); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); var expected = gump.Compile(ns).Compile(); ns.SendGump(gump); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPackets.cs index 9a1a02167..2620bba2b 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/GumpPackets.cs @@ -6,189 +6,190 @@ using Server.Compression; using Server.Gumps; using Server.Network; -namespace Server.Tests; - -public interface IGumpWriter +namespace Server.Tests { - int TextEntries { get; set; } - int Switches { get; set; } - - void AppendLayout(bool val); - void AppendLayout(int val); - void AppendLayout(uint val); - void AppendLayout(Serial serial); - void AppendLayoutNS(int val); - void AppendLayout(string text); - void AppendLayoutNS(string text); - void AppendLayout(ReadOnlySpan buffer); - void WriteStrings(List strings); - void Flush(); -} - -public sealed class CloseGump : Packet -{ - public CloseGump(int typeID, int buttonID) : base(0xBF) + public interface IGumpWriter { - EnsureCapacity(13); + int TextEntries { get; set; } + int Switches { get; set; } - Stream.Write((short)0x04); - Stream.Write(typeID); - Stream.Write(buttonID); - } -} - -public sealed class DisplayGumpPacked : Packet, IGumpWriter -{ - private static readonly byte[] m_Buffer = new byte[48]; - - private readonly Gump m_Gump; - - private readonly PacketWriter m_Layout; - private readonly PacketWriter m_Strings; - - private int m_StringCount; - - static DisplayGumpPacked() => m_Buffer[0] = (byte)' '; - - public DisplayGumpPacked(Gump gump) - : base(0xDD) - { - m_Gump = gump; - - m_Layout = PacketWriter.CreateInstance(8192); - m_Strings = PacketWriter.CreateInstance(8192); + void AppendLayout(bool val); + void AppendLayout(int val); + void AppendLayout(uint val); + void AppendLayout(Serial serial); + void AppendLayoutNS(int val); + void AppendLayout(string text); + void AppendLayoutNS(string text); + void AppendLayout(ReadOnlySpan buffer); + void WriteStrings(List strings); + void Flush(); } - public int TextEntries { get; set; } - - public int Switches { get; set; } - - public void AppendLayout(bool val) + public sealed class CloseGump : Packet { - AppendLayout(val ? " 1"u8 : " 0"u8); - } - - public void AppendLayout(int val) - { - var toString = val.ToString(); - var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1) + 1; - - m_Layout.Write(m_Buffer, 0, bytes); - } - - public void AppendLayout(uint val) - { - var toString = val.ToString(); - var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1) + 1; - - m_Layout.Write(m_Buffer, 0, bytes); - } - - public void AppendLayout(Serial serial) => AppendLayout(serial.Value); - - public void AppendLayoutNS(int val) - { - var toString = val.ToString(); - var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1); - - m_Layout.Write(m_Buffer, 1, bytes); - } - - public void AppendLayoutNS(string text) - { - m_Layout.WriteAsciiFixed(text, text.Length); - } - - public void AppendLayout(string text) - { - AppendLayout(" @"u8); - - m_Layout.WriteAsciiFixed(text, text.Length); - - AppendLayout("@"u8); - } - - public void AppendLayout(ReadOnlySpan buffer) - { - m_Layout.Write(buffer); - } - - public void WriteStrings(List strings) - { - m_StringCount = strings.Count; - - for (var i = 0; i < strings.Count; ++i) + public CloseGump(int typeID, int buttonID) : base(0xBF) { - var v = strings[i] ?? ""; + EnsureCapacity(13); - m_Strings.Write((ushort)v.Length); - m_Strings.WriteBigUniFixed(v, v.Length); + Stream.Write((short)0x04); + Stream.Write(typeID); + Stream.Write(buttonID); } } - public void Flush() + public sealed class DisplayGumpPacked : Packet, IGumpWriter { - EnsureCapacity(28 + (int)m_Layout.Length + (int)m_Strings.Length); + private static readonly byte[] m_Buffer = new byte[48]; - Stream.Write(m_Gump.Serial); - Stream.Write(m_Gump.TypeID); - Stream.Write(m_Gump.X); - Stream.Write(m_Gump.Y); + private readonly Gump m_Gump; - // Note: layout MUST be null terminated (don't listen to krrios) - m_Layout.Write((byte)0); + private readonly PacketWriter m_Layout; + private readonly PacketWriter m_Strings; - WritePacked(m_Layout); + private int m_StringCount; - Stream.Write(m_StringCount); + static DisplayGumpPacked() => m_Buffer[0] = (byte)' '; - WritePacked(m_Strings); - - PacketWriter.ReleaseInstance(m_Layout); - PacketWriter.ReleaseInstance(m_Strings); - } - - private void WritePacked(PacketWriter src) - { - var buffer = src.UnderlyingStream.GetBuffer(); - var length = (int)src.Length; - - if (length == 0) + public DisplayGumpPacked(Gump gump) + : base(0xDD) { - Stream.Write(0); - return; + m_Gump = gump; + + m_Layout = PacketWriter.CreateInstance(8192); + m_Strings = PacketWriter.CreateInstance(8192); } - var wantLength = 1 + length * 1024 / 1000; + public int TextEntries { get; set; } - wantLength += 4095; - wantLength &= ~4095; + public int Switches { get; set; } - var packBuffer = ArrayPool.Shared.Rent(wantLength); - var bytesPacked = Deflate.Standard.Pack(packBuffer, buffer.AsSpan(0, length)); + public void AppendLayout(bool val) + { + AppendLayout(val ? " 1"u8 : " 0"u8); + } - Stream.Write(4 + bytesPacked); - Stream.Write(length); - Stream.Write(packBuffer, 0, bytesPacked); + public void AppendLayout(int val) + { + var toString = val.ToString(); + var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1) + 1; - ArrayPool.Shared.Return(packBuffer); + m_Layout.Write(m_Buffer, 0, bytes); + } + + public void AppendLayout(uint val) + { + var toString = val.ToString(); + var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1) + 1; + + m_Layout.Write(m_Buffer, 0, bytes); + } + + public void AppendLayout(Serial serial) => AppendLayout(serial.Value); + + public void AppendLayoutNS(int val) + { + var toString = val.ToString(); + var bytes = Encoding.ASCII.GetBytes(toString, 0, toString.Length, m_Buffer, 1); + + m_Layout.Write(m_Buffer, 1, bytes); + } + + public void AppendLayoutNS(string text) + { + m_Layout.WriteAsciiFixed(text, text.Length); + } + + public void AppendLayout(string text) + { + AppendLayout(" @"u8); + + m_Layout.WriteAsciiFixed(text, text.Length); + + AppendLayout("@"u8); + } + + public void AppendLayout(ReadOnlySpan buffer) + { + m_Layout.Write(buffer); + } + + public void WriteStrings(List strings) + { + m_StringCount = strings.Count; + + for (var i = 0; i < strings.Count; ++i) + { + var v = strings[i] ?? ""; + + m_Strings.Write((ushort)v.Length); + m_Strings.WriteBigUniFixed(v, v.Length); + } + } + + public void Flush() + { + EnsureCapacity(28 + (int)m_Layout.Length + (int)m_Strings.Length); + + Stream.Write(m_Gump.Serial); + Stream.Write(m_Gump.TypeID); + Stream.Write(m_Gump.X); + Stream.Write(m_Gump.Y); + + // Note: layout MUST be null terminated (don't listen to krrios) + m_Layout.Write((byte)0); + + WritePacked(m_Layout); + + Stream.Write(m_StringCount); + + WritePacked(m_Strings); + + PacketWriter.ReleaseInstance(m_Layout); + PacketWriter.ReleaseInstance(m_Strings); + } + + private void WritePacked(PacketWriter src) + { + var buffer = src.UnderlyingStream.GetBuffer(); + var length = (int)src.Length; + + if (length == 0) + { + Stream.Write(0); + return; + } + + var wantLength = 1 + length * 1024 / 1000; + + wantLength += 4095; + wantLength &= ~4095; + + var packBuffer = ArrayPool.Shared.Rent(wantLength); + var bytesPacked = Deflate.Standard.Pack(packBuffer, buffer.AsSpan(0, length)); + + Stream.Write(4 + bytesPacked); + Stream.Write(length); + Stream.Write(packBuffer, 0, bytesPacked); + + ArrayPool.Shared.Return(packBuffer); + } + } + + public sealed class DisplaySignGump : Packet + { + public DisplaySignGump(Serial serial, int gumpID, string unknown, string caption) : base(0x8B) + { + unknown ??= ""; + caption ??= ""; + + EnsureCapacity(15 + unknown.Length + caption.Length); + + Stream.Write(serial); + Stream.Write((short)gumpID); + Stream.Write((short)(unknown.Length + 1)); + Stream.WriteAsciiNull(unknown); + Stream.Write((short)(caption.Length + 1)); + Stream.WriteAsciiNull(caption); + } } } - -public sealed class DisplaySignGump : Packet -{ - public DisplaySignGump(Serial serial, int gumpID, string unknown, string caption) : base(0x8B) - { - unknown ??= ""; - caption ??= ""; - - EnsureCapacity(15 + unknown.Length + caption.Length); - - Stream.Write(serial); - Stream.Write((short)gumpID); - Stream.Write((short)(unknown.Length + 1)); - Stream.WriteAsciiNull(unknown); - Stream.Write((short)(caption.Length + 1)); - Stream.WriteAsciiNull(caption); - } -} \ No newline at end of file diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ItemPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ItemPacketTests.cs index 27bad8aaf..48bbb0c32 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ItemPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ItemPacketTests.cs @@ -1,116 +1,116 @@ using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class ItemPacketTests +namespace Server.Tests.Network { - [Fact] - public void TestWorldItemPacket() + public class ItemPacketTests : IClassFixture { - var serial = (Serial)0x1024; - var itemId = 1; - - // Move to fixture - TileData.ItemTable[itemId] = new ItemData( - "Test Item Data", - TileFlag.Generic, - 1, - 1, - 1, - 1, - 1, - 1 - ); - - var item = new Item(serial) + [Fact] + public void TestWorldItemPacket() { - ItemID = itemId, - Hue = 0x1024, - Amount = 10, - Location = new Point3D(1000, 100, -10), - Direction = Direction.Left - }; + Serial serial = (Serial)0x1024; + var itemId = 1; - var expected = new WorldItem(item).Compile(); + // Move to fixture + TileData.ItemTable[itemId] = new ItemData( + "Test Item Data", + TileFlag.Generic, + 1, + 1, + 1, + 1, + 1, + 1 + ); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendWorldItem(item); + var item = new Item(serial) + { + ItemID = itemId, + Hue = 0x1024, + Amount = 10, + Location = new Point3D(1000, 100, -10), + Direction = Direction.Left + }; - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var expected = new WorldItem(item).Compile(); - [Fact] - public void TestWorldItemSAPacket() - { - var serial = (Serial)0x1024; - ushort itemId = 1; + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendWorldItem(item); - // Move to fixture - TileData.ItemTable[itemId] = new ItemData( - "Test Item Data", - TileFlag.Generic, - 1, - 1, - 1, - 1, - 1, - 1 - ); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var item = new Item(serial) + [Fact] + public void TestWorldItemSAPacket() { - ItemID = itemId, - Hue = 0x1024, - Amount = 10, - Location = new Point3D(1000, 100, -10) - }; + Serial serial = (Serial)0x1024; + ushort itemId = 1; - var expected = new WorldItemSA(item).Compile(); + // Move to fixture + TileData.ItemTable[itemId] = new ItemData( + "Test Item Data", + TileFlag.Generic, + 1, + 1, + 1, + 1, + 1, + 1 + ); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = ProtocolChanges.StygianAbyss; - ns.SendWorldItem(item); + var item = new Item(serial) + { + ItemID = itemId, + Hue = 0x1024, + Amount = 10, + Location = new Point3D(1000, 100, -10) + }; - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var expected = new WorldItemSA(item).Compile(); - [Fact] - public void TestWorldItemHSPacket() - { - var serial = (Serial)0x1024; - var itemId = 1; + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = ProtocolChanges.StygianAbyss; + ns.SendWorldItem(item); - // Move to fixture - TileData.ItemTable[itemId] = new ItemData( - "Test Item Data", - TileFlag.Generic, - 1, - 1, - 1, - 1, - 1, - 1 - ); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var item = new Item(serial) + [Fact] + public void TestWorldItemHSPacket() { - ItemID = itemId, - Hue = 0x1024, - Amount = 10, - Location = new Point3D(1000, 100, -10) - }; + Serial serial = (Serial)0x1024; + var itemId = 1; - var expected = new WorldItemHS(item).Compile(); + // Move to fixture + TileData.ItemTable[itemId] = new ItemData( + "Test Item Data", + TileFlag.Generic, + 1, + 1, + 1, + 1, + 1, + 1 + ); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = ProtocolChanges.StygianAbyss | ProtocolChanges.HighSeas; - ns.SendWorldItem(item); + var item = new Item(serial) + { + ItemID = itemId, + Hue = 0x1024, + Amount = 10, + Location = new Point3D(1000, 100, -10) + }; - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var expected = new WorldItemHS(item).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = ProtocolChanges.StygianAbyss | ProtocolChanges.HighSeas; + ns.SendWorldItem(item); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ItemPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ItemPackets.cs index 084ba4587..732fa6556 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ItemPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/ItemPackets.cs @@ -1,207 +1,208 @@ using Server.Items; -namespace Server.Network; - -public sealed class WorldItem : Packet +namespace Server.Network { - public WorldItem(Item item) : base(0x1A) + public sealed class WorldItem : Packet { - EnsureCapacity(20); - - // 14 base length - // +2 - Amount - // +2 - Hue - // +1 - Flags - - var serial = item.Serial.Value; - var itemID = item.ItemID & 0x3FFF; - var amount = item.Amount; - var loc = item.Location; - var x = loc.X; - var y = loc.Y; - var hue = item.Hue; - var flags = item.GetPacketFlags(); - var direction = (int)item.Direction; - - if (amount != 0) + public WorldItem(Item item) : base(0x1A) { - serial |= 0x80000000; - } - else - { - serial &= 0x7FFFFFFF; + EnsureCapacity(20); + + // 14 base length + // +2 - Amount + // +2 - Hue + // +1 - Flags + + var serial = item.Serial.Value; + var itemID = item.ItemID & 0x3FFF; + var amount = item.Amount; + var loc = item.Location; + var x = loc.X; + var y = loc.Y; + var hue = item.Hue; + var flags = item.GetPacketFlags(); + var direction = (int)item.Direction; + + if (amount != 0) + { + serial |= 0x80000000; + } + else + { + serial &= 0x7FFFFFFF; + } + + Stream.Write(serial); + + if (item is BaseMulti) + { + Stream.Write((short)(itemID | 0x4000)); + } + else + { + Stream.Write((short)itemID); + } + + if (amount != 0) + { + Stream.Write((short)amount); + } + + x &= 0x7FFF; + + if (direction != 0) + { + x |= 0x8000; + } + + Stream.Write((short)x); + + y &= 0x3FFF; + + if (hue != 0) + { + y |= 0x8000; + } + + if (flags != 0) + { + y |= 0x4000; + } + + Stream.Write((short)y); + + if (direction != 0) + { + Stream.Write((byte)direction); + } + + Stream.Write((sbyte)loc.Z); + + if (hue != 0) + { + Stream.Write((ushort)hue); + } + + if (flags != 0) + { + Stream.Write((byte)flags); + } } + } - Stream.Write(serial); + public sealed class WorldItemSA : Packet + { + public WorldItemSA(Item item) : base(0xF3, 24) + { + Stream.Write((short)0x1); - if (item is BaseMulti) - { - Stream.Write((short)(itemID | 0x4000)); - } - else - { - Stream.Write((short)itemID); - } + var itemID = item.ItemID; - if (amount != 0) - { + if (item is BaseMulti) + { + Stream.Write((byte)0x02); + + Stream.Write(item.Serial); + + itemID &= 0x3FFF; + + Stream.Write((short)itemID); + + Stream.Write((byte)0); + } + else + { + Stream.Write((byte)0x00); + + Stream.Write(item.Serial); + + itemID &= 0x7FFF; + + Stream.Write((short)itemID); + + Stream.Write((byte)0); + } + + var amount = item.Amount; Stream.Write((short)amount); + Stream.Write((short)amount); + + var loc = item.Location; + Stream.Write((short)loc.X); + Stream.Write((short)loc.Y); + Stream.Write((sbyte)loc.Z); + + Stream.Write((byte)item.Light); + Stream.Write((short)item.Hue); + Stream.Write((byte)item.GetPacketFlags()); } + } - x &= 0x7FFF; - - if (direction != 0) + public sealed class WorldItemHS : Packet + { + public WorldItemHS(Item item) : base(0xF3, 26) { - x |= 0x8000; + Stream.Write((short)0x1); + + var itemID = item.ItemID; + + if (item is BaseMulti) + { + Stream.Write((byte)0x02); + + Stream.Write(item.Serial); + + itemID &= 0x3FFF; + + Stream.Write((ushort)itemID); + + Stream.Write((byte)0); + } + else + { + Stream.Write((byte)0x00); + + Stream.Write(item.Serial); + + itemID &= 0xFFFF; + + Stream.Write((ushort)itemID); + + Stream.Write((byte)0); + } + + var amount = item.Amount; + Stream.Write((short)amount); + Stream.Write((short)amount); + + var loc = item.Location; + Stream.Write((short)loc.X); + Stream.Write((short)loc.Y); + Stream.Write((sbyte)loc.Z); + + Stream.Write((byte)item.Light); + Stream.Write((short)item.Hue); + Stream.Write((byte)item.GetPacketFlags()); + + Stream.Write((short)0x00); // ?? } + } - Stream.Write((short)x); - - y &= 0x3FFF; - - if (hue != 0) + public sealed class OPLInfo : Packet + { + /*public OPLInfo( ObjectPropertyList list ) : base( 0xBF ) { - y |= 0x8000; - } + EnsureCapacity( 13 ); - if (flags != 0) + m_Stream.Write( (short) 0x10 ); + m_Stream.Write( (int) list.Entity.Serial ); + m_Stream.Write( (int) list.Hash ); + }*/ + + public OPLInfo(Serial serial, int hash) : base(0xDC, 9) { - y |= 0x4000; - } - - Stream.Write((short)y); - - if (direction != 0) - { - Stream.Write((byte)direction); - } - - Stream.Write((sbyte)loc.Z); - - if (hue != 0) - { - Stream.Write((ushort)hue); - } - - if (flags != 0) - { - Stream.Write((byte)flags); + Stream.Write(serial); + Stream.Write(hash); } } } - -public sealed class WorldItemSA : Packet -{ - public WorldItemSA(Item item) : base(0xF3, 24) - { - Stream.Write((short)0x1); - - var itemID = item.ItemID; - - if (item is BaseMulti) - { - Stream.Write((byte)0x02); - - Stream.Write(item.Serial); - - itemID &= 0x3FFF; - - Stream.Write((short)itemID); - - Stream.Write((byte)0); - } - else - { - Stream.Write((byte)0x00); - - Stream.Write(item.Serial); - - itemID &= 0x7FFF; - - Stream.Write((short)itemID); - - Stream.Write((byte)0); - } - - var amount = item.Amount; - Stream.Write((short)amount); - Stream.Write((short)amount); - - var loc = item.Location; - Stream.Write((short)loc.X); - Stream.Write((short)loc.Y); - Stream.Write((sbyte)loc.Z); - - Stream.Write((byte)item.Light); - Stream.Write((short)item.Hue); - Stream.Write((byte)item.GetPacketFlags()); - } -} - -public sealed class WorldItemHS : Packet -{ - public WorldItemHS(Item item) : base(0xF3, 26) - { - Stream.Write((short)0x1); - - var itemID = item.ItemID; - - if (item is BaseMulti) - { - Stream.Write((byte)0x02); - - Stream.Write(item.Serial); - - itemID &= 0x3FFF; - - Stream.Write((ushort)itemID); - - Stream.Write((byte)0); - } - else - { - Stream.Write((byte)0x00); - - Stream.Write(item.Serial); - - itemID &= 0xFFFF; - - Stream.Write((ushort)itemID); - - Stream.Write((byte)0); - } - - var amount = item.Amount; - Stream.Write((short)amount); - Stream.Write((short)amount); - - var loc = item.Location; - Stream.Write((short)loc.X); - Stream.Write((short)loc.Y); - Stream.Write((sbyte)loc.Z); - - Stream.Write((byte)item.Light); - Stream.Write((short)item.Hue); - Stream.Write((byte)item.GetPacketFlags()); - - Stream.Write((short)0x00); // ?? - } -} - -public sealed class OPLInfo : Packet -{ - /*public OPLInfo( ObjectPropertyList list ) : base( 0xBF ) - { - EnsureCapacity( 13 ); - - m_Stream.Write( (short) 0x10 ); - m_Stream.Write( (int) list.Entity.Serial ); - m_Stream.Write( (int) list.Hash ); - }*/ - - public OPLInfo(Serial serial, int hash) : base(0xDC, 9) - { - Stream.Write(serial); - Stream.Write(hash); - } -} \ No newline at end of file diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPacketTests.cs index c66a61ca7..9a6ddf8ee 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPacketTests.cs @@ -1,35 +1,35 @@ using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class LightPacketTests +namespace Server.Tests.Network { - [Fact] - public void TestGlobalLightLevel() + public class LightPacketTests { - const byte lightLevel = 5; - var expected = new GlobalLightLevel(lightLevel).Compile(); + [Fact] + public void TestGlobalLightLevel() + { + const byte lightLevel = 5; + var expected = new GlobalLightLevel(lightLevel).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendGlobalLightLevel(lightLevel); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendGlobalLightLevel(lightLevel); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestPersonalLightLevel() - { - var serial = (Serial)0x1024; - byte lightLevel = 5; - var expected = new PersonalLightLevel(serial, lightLevel).Compile(); + [Fact] + public void TestPersonalLightLevel() + { + Serial serial = (Serial)0x1024; + byte lightLevel = 5; + var expected = new PersonalLightLevel(serial, lightLevel).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendPersonalLightLevel(serial, lightLevel); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPersonalLightLevel(serial, lightLevel); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPackets.cs index b0ac46143..5da20a6d5 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/LightPackets.cs @@ -1,34 +1,35 @@ -namespace Server.Network; - -public sealed class GlobalLightLevel : Packet +namespace Server.Network { - private static readonly GlobalLightLevel[] m_Cache = new GlobalLightLevel[0x100]; - - public GlobalLightLevel(int level) : base(0x4F, 2) + public sealed class GlobalLightLevel : Packet { - Stream.Write((sbyte)level); - } + private static readonly GlobalLightLevel[] m_Cache = new GlobalLightLevel[0x100]; - public static GlobalLightLevel Instantiate(int level) - { - var lvl = (byte)level; - var p = m_Cache[lvl]; - - if (p == null) + public GlobalLightLevel(int level) : base(0x4F, 2) { - m_Cache[lvl] = p = new GlobalLightLevel(level); - p.SetStatic(); + Stream.Write((sbyte)level); } - return p; + public static GlobalLightLevel Instantiate(int level) + { + var lvl = (byte)level; + var p = m_Cache[lvl]; + + if (p == null) + { + m_Cache[lvl] = p = new GlobalLightLevel(level); + p.SetStatic(); + } + + return p; + } + } + + public sealed class PersonalLightLevel : Packet + { + public PersonalLightLevel(Serial mobile, int level = 0) : base(0x4E, 6) + { + Stream.Write(mobile); + Stream.Write((sbyte)level); + } } } - -public sealed class PersonalLightLevel : Packet -{ - public PersonalLightLevel(Serial mobile, int level = 0) : base(0x4E, 6) - { - Stream.Write(mobile); - Stream.Write((sbyte)level); - } -} \ No newline at end of file diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MapPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MapPacketTests.cs index a438730e0..28872e9e6 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MapPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MapPacketTests.cs @@ -1,48 +1,48 @@ using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class MapPatchesTests +namespace Server.Tests.Network { - [Fact] - public void TestMapPatches() + public class MapPatchesTests : IClassFixture { - using var ns = PacketTestUtilities.CreateTestNetState(); + [Fact] + public void TestMapPatches() + { + var ns = PacketTestUtilities.CreateTestNetState(); - var expected = new MapPatches().Compile(); + var expected = new MapPatches().Compile(); - ns.SendMapPatches(); + ns.SendMapPatches(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestInvalidMapEnable() - { - var expected = new InvalidMapEnable().Compile(); + [Fact] + public void TestInvalidMapEnable() + { + var expected = new InvalidMapEnable().Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendInvalidMap(); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendInvalidMap(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData("Felucca")] - [InlineData("Malas")] - public void TestMapChange(string mapName) - { - var map = Map.Parse(mapName); - var expected = new MapChange(map).Compile(); + [Theory] + [InlineData("Felucca")] + [InlineData("Malas")] + public void TestMapChange(string mapName) + { + var map = Map.Parse(mapName); + var expected = new MapChange(map).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMapChange(map); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMapChange(map); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MapPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MapPackets.cs index 23ff3853f..b7bfa8e6e 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MapPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MapPackets.cs @@ -1,43 +1,44 @@ -namespace Server.Network; - -public sealed class MapPatches : Packet +namespace Server.Network { - public MapPatches() : base(0xBF) + public sealed class MapPatches : Packet { - EnsureCapacity(9 + 4 * 8); + public MapPatches() : base(0xBF) + { + EnsureCapacity(9 + 4 * 8); - Stream.Write((short)0x18); + Stream.Write((short)0x18); - Stream.Write(4); + Stream.Write(4); - Stream.Write(Map.Felucca.Tiles.Patch.StaticBlocks); - Stream.Write(Map.Felucca.Tiles.Patch.LandBlocks); + Stream.Write(Map.Felucca.Tiles.Patch.StaticBlocks); + Stream.Write(Map.Felucca.Tiles.Patch.LandBlocks); - Stream.Write(Map.Trammel.Tiles.Patch.StaticBlocks); - Stream.Write(Map.Trammel.Tiles.Patch.LandBlocks); + Stream.Write(Map.Trammel.Tiles.Patch.StaticBlocks); + Stream.Write(Map.Trammel.Tiles.Patch.LandBlocks); - Stream.Write(Map.Ilshenar.Tiles.Patch.StaticBlocks); - Stream.Write(Map.Ilshenar.Tiles.Patch.LandBlocks); + Stream.Write(Map.Ilshenar.Tiles.Patch.StaticBlocks); + Stream.Write(Map.Ilshenar.Tiles.Patch.LandBlocks); - Stream.Write(Map.Malas.Tiles.Patch.StaticBlocks); - Stream.Write(Map.Malas.Tiles.Patch.LandBlocks); + Stream.Write(Map.Malas.Tiles.Patch.StaticBlocks); + Stream.Write(Map.Malas.Tiles.Patch.LandBlocks); + } + } + + public sealed class InvalidMapEnable : Packet + { + public InvalidMapEnable() : base(0xC6, 1) + { + } + } + + public sealed class MapChange : Packet + { + public MapChange(Map map) : base(0xBF) + { + EnsureCapacity(6); + + Stream.Write((short)0x08); + Stream.Write((byte)map.MapID); + } } } - -public sealed class InvalidMapEnable : Packet -{ - public InvalidMapEnable() : base(0xC6, 1) - { - } -} - -public sealed class MapChange : Packet -{ - public MapChange(Map map) : base(0xBF) - { - EnsureCapacity(6); - - Stream.Write((short)0x08); - Stream.Write((byte)map.MapID); - } -} \ No newline at end of file diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MenuPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MenuPacketTests.cs index 3da55c89e..243bff188 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MenuPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MenuPacketTests.cs @@ -5,97 +5,98 @@ using Server.Menus.Questions; using Server.Network; using Xunit; -namespace Server.Tests.Network; - -internal class ContextMenuItem : Item +namespace Server.Tests.Network { - private readonly bool _requiresNewPacket; - public ContextMenuItem(Serial serial, bool requiresNewPacket) : base(serial) => - _requiresNewPacket = requiresNewPacket; - - public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) + internal class ContextMenuItem : Item { - base.GetContextMenuEntries(from, ref list); + private readonly bool _requiresNewPacket; + public ContextMenuItem(Serial serial, bool requiresNewPacket) : base(serial) => + _requiresNewPacket = requiresNewPacket; - list.Add(new ContextMenuEntry(3000001)); - list.Add(new ContextMenuEntry(3000002)); - list.Add(new ContextMenuEntry(3000003)); - if (_requiresNewPacket) + public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) { - list.Add(new ContextMenuEntry(500000)); + base.GetContextMenuEntries(from, ref list); + + list.Add(new ContextMenuEntry(3000001)); + list.Add(new ContextMenuEntry(3000002)); + list.Add(new ContextMenuEntry(3000003)); + if (_requiresNewPacket) + { + list.Add(new ContextMenuEntry(500000)); + } + } + } + + [Collection("Sequential Tests")] + public class MenuPacketTests : IClassFixture + { + [Fact] + public void TestDisplayItemListMenu() + { + var menu = new ItemListMenu( + "Which item would you choose?", + [ + new ItemListEntry("Item 1", 0x01), + new ItemListEntry("Item 2", 0x100), + new ItemListEntry("Item 3", 0x1000, 250) + ] + ); + + var expected = new DisplayItemListMenu(menu).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplayItemListMenu(menu); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + [Fact] + public void TestDisplayQuestionMenu() + { + var menu = new QuestionMenu( + "Which option would you choose?", + [ + "Option 1", + "Option 2", + "Option 3" + ] + ); + + var expected = new DisplayQuestionMenu(menu).Compile(); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplayQuestionMenu(menu); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void TestDisplayContextMenu(bool newHaven, bool newPacket) + { + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); + + var item = new ContextMenuItem(World.NewItem, newPacket); + var menu = ContextMenuSystem.CreateContextMenu(m, item); + + var packet = newHaven && newPacket ? (Packet)new DisplayContextMenu(menu) : new DisplayContextMenuOld(menu); + var expected = packet.Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + if (newHaven) + { + ns.ProtocolChanges |= ProtocolChanges.NewHaven; + } + + ns.SendDisplayContextMenu(menu); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); } } } - -[Collection("Sequential Server Tests")] -public class MenuPacketTests -{ - [Fact] - public void TestDisplayItemListMenu() - { - var menu = new ItemListMenu( - "Which item would you choose?", - [ - new ItemListEntry("Item 1", 0x01), - new ItemListEntry("Item 2", 0x100), - new ItemListEntry("Item 3", 0x1000, 250) - ] - ); - - var expected = new DisplayItemListMenu(menu).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDisplayItemListMenu(menu); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestDisplayQuestionMenu() - { - var menu = new QuestionMenu( - "Which option would you choose?", - [ - "Option 1", - "Option 2", - "Option 3" - ] - ); - - var expected = new DisplayQuestionMenu(menu).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDisplayQuestionMenu(menu); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Theory] - [InlineData(false, false)] - [InlineData(false, true)] - [InlineData(true, false)] - [InlineData(true, true)] - public void TestDisplayContextMenu(bool newHaven, bool newPacket) - { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); - - var item = new ContextMenuItem(World.NewItem, newPacket); - var menu = ContextMenuSystem.CreateContextMenu(m, item); - - var packet = newHaven && newPacket ? (Packet)new DisplayContextMenu(menu) : new DisplayContextMenuOld(menu); - var expected = packet.Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - if (newHaven) - { - ns.ProtocolChanges |= ProtocolChanges.NewHaven; - } - - ns.SendDisplayContextMenu(menu); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } -} diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MenuPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MenuPackets.cs index e806600f6..59c4ae8b8 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MenuPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MenuPackets.cs @@ -3,217 +3,218 @@ using Server.Menus; using Server.Menus.ItemLists; using Server.Menus.Questions; -namespace Server.Network; - -public sealed class DisplayItemListMenu : Packet +namespace Server.Network { - public DisplayItemListMenu(ItemListMenu menu) : base(0x7C) + public sealed class DisplayItemListMenu : Packet { - EnsureCapacity(256); - - Stream.Write(((IMenu)menu).Serial); - Stream.Write((short)0); - - var question = menu.Question; - - if (question == null) + public DisplayItemListMenu(ItemListMenu menu) : base(0x7C) { - Stream.Write((byte)0); - } - else - { - var questionLength = question.Length; - Stream.Write((byte)questionLength); - Stream.WriteAsciiFixed(question, questionLength); - } + EnsureCapacity(256); - var entries = menu.Entries; + Stream.Write(((IMenu)menu).Serial); + Stream.Write((short)0); - int entriesLength = (byte)entries.Length; + var question = menu.Question; - Stream.Write((byte)entriesLength); - - for (var i = 0; i < entriesLength; ++i) - { - var e = entries[i]; - - Stream.Write((ushort)e.ItemID); - Stream.Write((short)e.Hue); - - var name = e.Name; - - if (name == null) + if (question == null) { Stream.Write((byte)0); } else { - var nameLength = name.Length; - Stream.Write((byte)nameLength); - Stream.WriteAsciiFixed(name, nameLength); + var questionLength = question.Length; + Stream.Write((byte)questionLength); + Stream.WriteAsciiFixed(question, questionLength); + } + + var entries = menu.Entries; + + int entriesLength = (byte)entries.Length; + + Stream.Write((byte)entriesLength); + + for (var i = 0; i < entriesLength; ++i) + { + var e = entries[i]; + + Stream.Write((ushort)e.ItemID); + Stream.Write((short)e.Hue); + + var name = e.Name; + + if (name == null) + { + Stream.Write((byte)0); + } + else + { + var nameLength = name.Length; + Stream.Write((byte)nameLength); + Stream.WriteAsciiFixed(name, nameLength); + } } } } -} -public sealed class DisplayQuestionMenu : Packet -{ - public DisplayQuestionMenu(QuestionMenu menu) : base(0x7C) + public sealed class DisplayQuestionMenu : Packet { - EnsureCapacity(256); - - Stream.Write(((IMenu)menu).Serial); - Stream.Write((short)0); - - var question = menu.Question; - - if (question == null) + public DisplayQuestionMenu(QuestionMenu menu) : base(0x7C) { - Stream.Write((byte)0); - } - else - { - var questionLength = question.Length; - Stream.Write((byte)questionLength); - Stream.WriteAsciiFixed(question, questionLength); - } + EnsureCapacity(256); - var answers = menu.Answers; + Stream.Write(((IMenu)menu).Serial); + Stream.Write((short)0); - int answersLength = (byte)answers.Length; + var question = menu.Question; - Stream.Write((byte)answersLength); - - for (var i = 0; i < answersLength; ++i) - { - Stream.Write(0); - - var answer = answers[i]; - - if (answer == null) + if (question == null) { Stream.Write((byte)0); } else { - var answerLength = answer.Length; - Stream.Write((byte)answerLength); - Stream.WriteAsciiFixed(answer, answerLength); + var questionLength = question.Length; + Stream.Write((byte)questionLength); + Stream.WriteAsciiFixed(question, questionLength); + } + + var answers = menu.Answers; + + int answersLength = (byte)answers.Length; + + Stream.Write((byte)answersLength); + + for (var i = 0; i < answersLength; ++i) + { + Stream.Write(0); + + var answer = answers[i]; + + if (answer == null) + { + Stream.Write((byte)0); + } + else + { + var answerLength = answer.Length; + Stream.Write((byte)answerLength); + Stream.WriteAsciiFixed(answer, answerLength); + } + } + } + } + + public sealed class DisplayContextMenu : Packet + { + public DisplayContextMenu(ContextMenu menu) : base(0xBF) + { + var entries = menu.Entries; + + int length = (byte)entries.Length; + + EnsureCapacity(12 + length * 8); + + Stream.Write((short)0x14); + Stream.Write((short)0x02); + + var target = menu.Target; + + Stream.Write(target.Serial); + + Stream.Write((byte)length); + + var p = target switch + { + Mobile _ => target.Location, + Item item => item.GetWorldLocation(), + _ => Point3D.Zero + }; + + for (var i = 0; i < length; ++i) + { + var e = entries[i]; + + Stream.Write(e.Number); + Stream.Write((short)i); + + var range = e.Range; + + if (range == -1) + { + range = Core.GlobalUpdateRange; + } + + var flags = e.Flags; + if (!(e.Enabled && menu.From.InRange(p, range))) + { + flags |= CMEFlags.Disabled; + } + + Stream.Write((short)flags); + } + } + } + + public sealed class DisplayContextMenuOld : Packet + { + public DisplayContextMenuOld(ContextMenu menu) : base(0xBF) + { + var entries = menu.Entries; + + int length = (byte)entries.Length; + + EnsureCapacity(12 + length * 8); + + Stream.Write((short)0x14); + Stream.Write((short)0x01); + + var target = menu.Target; + + Stream.Write(target.Serial); + + Stream.Write((byte)length); + + var p = target switch + { + Mobile _ => target.Location, + Item item => item.GetWorldLocation(), + _ => Point3D.Zero + }; + + for (var i = 0; i < length; ++i) + { + var e = entries[i]; + + Stream.Write((short)i); + Stream.Write((ushort)(e.Number - 3000000)); + + var range = e.Range; + + if (range == -1) + { + range = Core.GlobalUpdateRange; + } + + var flags = e.Flags; + if (!(e.Enabled && menu.From.InRange(p, range))) + { + flags |= CMEFlags.Disabled; + } + + var color = e.Color & 0xFFFF; + + if (color != 0xFFFF) + { + flags |= CMEFlags.Colored; + } + + Stream.Write((short)flags); + + if ((flags & CMEFlags.Colored) != 0) + { + Stream.Write((short)color); + } } } } } - -public sealed class DisplayContextMenu : Packet -{ - public DisplayContextMenu(ContextMenu menu) : base(0xBF) - { - var entries = menu.Entries; - - int length = (byte)entries.Length; - - EnsureCapacity(12 + length * 8); - - Stream.Write((short)0x14); - Stream.Write((short)0x02); - - var target = menu.Target; - - Stream.Write(target.Serial); - - Stream.Write((byte)length); - - var p = target switch - { - Mobile _ => target.Location, - Item item => item.GetWorldLocation(), - _ => Point3D.Zero - }; - - for (var i = 0; i < length; ++i) - { - var e = entries[i]; - - Stream.Write(e.Number); - Stream.Write((short)i); - - var range = e.Range; - - if (range == -1) - { - range = Core.GlobalUpdateRange; - } - - var flags = e.Flags; - if (!(e.Enabled && menu.From.InRange(p, range))) - { - flags |= CMEFlags.Disabled; - } - - Stream.Write((short)flags); - } - } -} - -public sealed class DisplayContextMenuOld : Packet -{ - public DisplayContextMenuOld(ContextMenu menu) : base(0xBF) - { - var entries = menu.Entries; - - int length = (byte)entries.Length; - - EnsureCapacity(12 + length * 8); - - Stream.Write((short)0x14); - Stream.Write((short)0x01); - - var target = menu.Target; - - Stream.Write(target.Serial); - - Stream.Write((byte)length); - - var p = target switch - { - Mobile _ => target.Location, - Item item => item.GetWorldLocation(), - _ => Point3D.Zero - }; - - for (var i = 0; i < length; ++i) - { - var e = entries[i]; - - Stream.Write((short)i); - Stream.Write((ushort)(e.Number - 3000000)); - - var range = e.Range; - - if (range == -1) - { - range = Core.GlobalUpdateRange; - } - - var flags = e.Flags; - if (!(e.Enabled && menu.From.InRange(p, range))) - { - flags |= CMEFlags.Disabled; - } - - var color = e.Color & 0xFFFF; - - if (color != 0xFFFF) - { - flags |= CMEFlags.Colored; - } - - Stream.Write((short)flags); - - if ((flags & CMEFlags.Colored) != 0) - { - Stream.Write((short)color); - } - } - } -} \ No newline at end of file diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MessagePacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MessagePacketTests.cs index b5eb423f1..dc4beb6a9 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MessagePacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MessagePacketTests.cs @@ -2,297 +2,221 @@ using Server.Network; using Server.Prompts; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class MessageTests +namespace Server.Tests.Network { - [Fact] - public void TestMessageLocalized() - { - var serial = (Serial)0x1024; - var graphic = 0x100; - var messageType = MessageType.Label; - var hue = 1024; - var font = 3; - var number = 150000; - var name = "Stuff"; - var args = "Arguments"; - - var expected = new MessageLocalized( - serial, - graphic, - messageType, - hue, - font, - number, - name, - args - ).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMessageLocalized( - serial, - graphic, - messageType, - hue, - font, - number, - name, - args - ); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestMessageLocalizedInterpolated() - { - var serial = (Serial)0x1024; - var graphic = 0x100; - var messageType = MessageType.Label; - var hue = 1024; - var font = 3; - var number = 150000; - var name = "Stuff"; - var argValue = 42; - - var expected = new MessageLocalized( - serial, graphic, messageType, hue, font, number, name, $"value: {argValue}" - ).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMessageLocalized(serial, graphic, messageType, hue, font, number, name, $"value: {argValue}"); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestMessageLocalizedAffix() - { - var serial = (Serial)0x1024; - var graphic = 0x100; - var messageType = MessageType.Label; - var hue = 1024; - var font = 3; - var number = 150000; - var name = "Stuff"; - var args = "Arguments"; - var affixType = AffixType.System; - var affix = "Affix"; - - var expected = new MessageLocalizedAffix( - serial, - graphic, - messageType, - hue, - font, - number, - name, - affixType, - affix, - args - ).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMessageLocalizedAffix( - serial, - graphic, - messageType, - hue, - font, - number, - name, - affixType, - affix, - args - ); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestMessageLocalizedAffixInterpolated() - { - var serial = (Serial)0x1024; - var graphic = 0x100; - var messageType = MessageType.Label; - var hue = 1024; - var font = 3; - var number = 150000; - var name = "Stuff"; - var affixType = AffixType.System; - var affix = "Affix"; - var argValue = 42; - - var expected = new MessageLocalizedAffix( - serial, graphic, messageType, hue, font, number, name, affixType, affix, $"value: {argValue}" - ).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMessageLocalizedAffix( - serial, graphic, messageType, hue, font, number, name, affixType, affix, - $"value: {argValue}" - ); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestUnicodeMessageInterpolated() - { - var serial = (Serial)0x1024; - var graphic = 0x100; - var messageType = MessageType.Label; - var hue = 1024; - var font = 3; - var lang = "ENU"; - var name = "Stuff"; - var who = "Alice"; - - var expected = new UnicodeMessage( - serial, graphic, messageType, hue, font, lang, name, $"hello, {who}" - ).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMessage( - serial, graphic, messageType, hue, font, false, lang, name, $"hello, {who}" - ); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestAsciiMessage() - { - var serial = (Serial)0x1024; - var graphic = 0x100; - var messageType = MessageType.Label; - var hue = 1024; - var font = 3; - var name = "Stuff"; - var text = "Some Text"; - - var expected = new AsciiMessage( - serial, - graphic, - messageType, - hue, - font, - name, - text - ).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMessage( - serial, - graphic, - messageType, - hue, - font, - true, - null, - name, - text - ); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestUnicodeMessage() - { - var serial = (Serial)0x1024; - var graphic = 0x100; - var messageType = MessageType.Label; - var hue = 1024; - var font = 3; - var lang = "ENU"; - var name = "Stuff"; - var text = "Some Text"; - - var expected = new UnicodeMessage( - serial, - graphic, - messageType, - hue, - font, - lang, - name, - text - ).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMessage( - serial, - graphic, - messageType, - hue, - font, - false, - lang, - name, - text - ); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestFollowMessage() - { - var serial = (Serial)0x1024; - var serial2 = (Serial)0x2; - - var expected = new FollowMessage(serial, serial2).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendFollowMessage(serial, serial2); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestObjectHelpResponse() - { - var s = (Serial)0x100; - var text = "This is some testing text"; - - var expected = new ObjectHelpResponse(s, text).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendHelpResponse(s, text); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - internal class TestPrompt : Prompt - { - } - - [Collection("Sequential Server Tests")] - public class UnicodePromptTests + public class MessageTests { [Fact] - public void TestUnicodePrompt() + public void TestMessageLocalized() { - var prompt = new TestPrompt(); - var expected = new UnicodePrompt(prompt).Compile(); + Serial serial = (Serial)0x1024; + var graphic = 0x100; + var messageType = MessageType.Label; + var hue = 1024; + var font = 3; + var number = 150000; + var name = "Stuff"; + var args = "Arguments"; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendPrompt(prompt); + var expected = new MessageLocalized( + serial, + graphic, + messageType, + hue, + font, + number, + name, + args + ).Compile(); - var result = ns.SendBuffer.GetReadSpan(); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMessageLocalized( + serial, + graphic, + messageType, + hue, + font, + number, + name, + args + ); + + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } + + [Fact] + public void TestMessageLocalizedAffix() + { + Serial serial = (Serial)0x1024; + var graphic = 0x100; + var messageType = MessageType.Label; + var hue = 1024; + var font = 3; + var number = 150000; + var name = "Stuff"; + var args = "Arguments"; + var affixType = AffixType.System; + var affix = "Affix"; + + var expected = new MessageLocalizedAffix( + serial, + graphic, + messageType, + hue, + font, + number, + name, + affixType, + affix, + args + ).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMessageLocalizedAffix( + serial, + graphic, + messageType, + hue, + font, + number, + name, + affixType, + affix, + args + ); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + [Fact] + public void TestAsciiMessage() + { + Serial serial = (Serial)0x1024; + var graphic = 0x100; + var messageType = MessageType.Label; + var hue = 1024; + var font = 3; + var name = "Stuff"; + var text = "Some Text"; + + var expected = new AsciiMessage( + serial, + graphic, + messageType, + hue, + font, + name, + text + ).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMessage( + serial, + graphic, + messageType, + hue, + font, + true, + null, + name, + text + ); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + [Fact] + public void TestUnicodeMessage() + { + Serial serial = (Serial)0x1024; + var graphic = 0x100; + var messageType = MessageType.Label; + var hue = 1024; + var font = 3; + var lang = "ENU"; + var name = "Stuff"; + var text = "Some Text"; + + var expected = new UnicodeMessage( + serial, + graphic, + messageType, + hue, + font, + lang, + name, + text + ).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMessage( + serial, + graphic, + messageType, + hue, + font, + false, + lang, + name, + text + ); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + [Fact] + public void TestFollowMessage() + { + Serial serial = (Serial)0x1024; + Serial serial2 = (Serial)0x2; + + var expected = new FollowMessage(serial, serial2).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendFollowMessage(serial, serial2); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + [Fact] + public void TestObjectHelpResponse() + { + Serial s = (Serial)0x100; + var text = "This is some testing text"; + + var expected = new ObjectHelpResponse(s, text).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendHelpResponse(s, text); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + internal class TestPrompt : Prompt + { + } + + public class UnicodePromptTests + { + [Fact] + public void TestUnicodePrompt() + { + var prompt = new TestPrompt(); + var expected = new UnicodePrompt(prompt).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPrompt(prompt); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + + } + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MessagePackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MessagePackets.cs index ade2fd950..0447cbb4b 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MessagePackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MessagePackets.cs @@ -1,154 +1,155 @@ using Server.Prompts; -namespace Server.Network; - -public sealed class MessageLocalized : Packet +namespace Server.Network { - public MessageLocalized( - Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, string args - ) : base(0xC1) + public sealed class MessageLocalized : Packet { - name ??= ""; - args ??= ""; - - if (hue == 0) + public MessageLocalized( + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, string args + ) : base(0xC1) { - hue = 0x3B2; + name ??= ""; + args ??= ""; + + if (hue == 0) + { + hue = 0x3B2; + } + + EnsureCapacity(50 + args.Length * 2); + + Stream.Write(serial); + Stream.Write((short)graphic); + Stream.Write((byte)type); + Stream.Write((short)hue); + Stream.Write((short)font); + Stream.Write(number); + Stream.WriteAsciiFixed(name, 30); + Stream.WriteLittleUniNull(args); } - - EnsureCapacity(50 + args.Length * 2); - - Stream.Write(serial); - Stream.Write((short)graphic); - Stream.Write((byte)type); - Stream.Write((short)hue); - Stream.Write((short)font); - Stream.Write(number); - Stream.WriteAsciiFixed(name, 30); - Stream.WriteLittleUniNull(args); } -} -public sealed class MessageLocalizedAffix : Packet -{ - public MessageLocalizedAffix( - Serial serial, int graphic, MessageType messageType, int hue, int font, int number, - string name, AffixType affixType, string affix, string args - ) : base(0xCC) + public sealed class MessageLocalizedAffix : Packet { - name ??= ""; - affix ??= ""; - args ??= ""; - - if (hue == 0) + public MessageLocalizedAffix( + Serial serial, int graphic, MessageType messageType, int hue, int font, int number, + string name, AffixType affixType, string affix, string args + ) : base(0xCC) { - hue = 0x3B2; + name ??= ""; + affix ??= ""; + args ??= ""; + + if (hue == 0) + { + hue = 0x3B2; + } + + EnsureCapacity(52 + affix.Length + args.Length * 2); + + Stream.Write(serial); + Stream.Write((short)graphic); + Stream.Write((byte)messageType); + Stream.Write((short)hue); + Stream.Write((short)font); + Stream.Write(number); + Stream.Write((byte)affixType); + Stream.WriteAsciiFixed(name, 30); + Stream.WriteAsciiNull(affix); + Stream.WriteBigUniNull(args); } - - EnsureCapacity(52 + affix.Length + args.Length * 2); - - Stream.Write(serial); - Stream.Write((short)graphic); - Stream.Write((byte)messageType); - Stream.Write((short)hue); - Stream.Write((short)font); - Stream.Write(number); - Stream.Write((byte)affixType); - Stream.WriteAsciiFixed(name, 30); - Stream.WriteAsciiNull(affix); - Stream.WriteBigUniNull(args); } -} -public sealed class AsciiMessage : Packet -{ - public AsciiMessage( - Serial serial, int graphic, MessageType type, int hue, int font, string name, string text - ) : base(0x1C) + public sealed class AsciiMessage : Packet { - name ??= ""; - text ??= ""; - - if (hue == 0) + public AsciiMessage( + Serial serial, int graphic, MessageType type, int hue, int font, string name, string text + ) : base(0x1C) { - hue = 0x3B2; + name ??= ""; + text ??= ""; + + if (hue == 0) + { + hue = 0x3B2; + } + + EnsureCapacity(45 + text.Length); + + Stream.Write(serial); + Stream.Write((short)graphic); + Stream.Write((byte)type); + Stream.Write((short)hue); + Stream.Write((short)font); + Stream.WriteAsciiFixed(name, 30); + Stream.WriteAsciiNull(text); } - - EnsureCapacity(45 + text.Length); - - Stream.Write(serial); - Stream.Write((short)graphic); - Stream.Write((byte)type); - Stream.Write((short)hue); - Stream.Write((short)font); - Stream.WriteAsciiFixed(name, 30); - Stream.WriteAsciiNull(text); } -} -public sealed class UnicodeMessage : Packet -{ - public UnicodeMessage( - Serial serial, int graphic, MessageType type, int hue, int font, string lang, string name, - string text - ) : base(0xAE) + public sealed class UnicodeMessage : Packet { - if (string.IsNullOrEmpty(lang)) + public UnicodeMessage( + Serial serial, int graphic, MessageType type, int hue, int font, string lang, string name, + string text + ) : base(0xAE) { - lang = "ENU"; + if (string.IsNullOrEmpty(lang)) + { + lang = "ENU"; + } + + name ??= ""; + text ??= ""; + + if (hue == 0) + { + hue = 0x3B2; + } + + EnsureCapacity(50 + text.Length * 2); + + Stream.Write(serial); + Stream.Write((short)graphic); + Stream.Write((byte)type); + Stream.Write((short)hue); + Stream.Write((short)font); + Stream.WriteAsciiFixed(lang, 4); + Stream.WriteAsciiFixed(name, 30); + Stream.WriteBigUniNull(text); } + } - name ??= ""; - text ??= ""; - - if (hue == 0) + public sealed class FollowMessage : Packet + { + public FollowMessage(Serial serial1, Serial serial2) : base(0x15, 9) { - hue = 0x3B2; + Stream.Write(serial1); + Stream.Write(serial2); } - - EnsureCapacity(50 + text.Length * 2); - - Stream.Write(serial); - Stream.Write((short)graphic); - Stream.Write((byte)type); - Stream.Write((short)hue); - Stream.Write((short)font); - Stream.WriteAsciiFixed(lang, 4); - Stream.WriteAsciiFixed(name, 30); - Stream.WriteBigUniNull(text); } -} -public sealed class FollowMessage : Packet -{ - public FollowMessage(Serial serial1, Serial serial2) : base(0x15, 9) + public sealed class UnicodePrompt : Packet { - Stream.Write(serial1); - Stream.Write(serial2); - } -} + public UnicodePrompt(Prompt prompt) : base(0xC2) + { + EnsureCapacity(21); -public sealed class UnicodePrompt : Packet -{ - public UnicodePrompt(Prompt prompt) : base(0xC2) + Stream.Write(prompt.Serial); // TODO: Does this value even matter? + Stream.Write(prompt.Serial); + Stream.Write(0); + Stream.Write(0); + Stream.Write((short)0); + } + } + + public sealed class ObjectHelpResponse : Packet { - EnsureCapacity(21); + public ObjectHelpResponse(Serial e, string text) : base(0xB7) + { + EnsureCapacity(9 + text.Length * 2); - Stream.Write(prompt.Serial); // TODO: Does this value even matter? - Stream.Write(prompt.Serial); - Stream.Write(0); - Stream.Write(0); - Stream.Write((short)0); - } -} - -public sealed class ObjectHelpResponse : Packet -{ - public ObjectHelpResponse(Serial e, string text) : base(0xB7) - { - EnsureCapacity(9 + text.Length * 2); - - Stream.Write(e); - Stream.WriteBigUniNull(text); + Stream.Write(e); + Stream.WriteBigUniNull(text); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MobilePacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MobilePacketTests.cs index d7d3beca2..348a06253 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MobilePacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MobilePacketTests.cs @@ -3,36 +3,36 @@ using Xunit; namespace Server.Tests.Network; -[Collection("Sequential Server Tests")] -public class MobilePacketTests +[Collection("Sequential Tests")] +public class MobilePacketTests : IClassFixture { [Fact] public void TestDeathAnimation() { - var killed = (Serial)0x1; - var corpse = (Serial)0x1000; + Serial killed = (Serial)0x1; + Serial corpse = (Serial)0x1000; var expected = new DeathAnimation(killed, corpse).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendDeathAnimation(killed, corpse); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } [Fact] public void TestBondStatus() { - var petSerial = (Serial)0x1; + Serial petSerial = (Serial)0x1; const bool bonded = true; var expected = new BondedStatus(petSerial, bonded).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendBondedStatus(petSerial, bonded); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -46,13 +46,13 @@ public class MobilePacketTests var noto = 10; - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.ProtocolChanges = protocolChanges; var expected = new MobileMoving(m, noto, ns.StygianAbyss).Compile(); ns.SendMobileMoving(m, noto); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -67,10 +67,10 @@ public class MobilePacketTests var expected = new MobileName(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileName(m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -79,7 +79,7 @@ public class MobilePacketTests [InlineData(10, 100, 25, true, false, 0)] public void TestMobileAnimation(int action, int frameCount, int repeatCount, bool reverse, bool repeat, byte delay) { - var mobile = (Serial)0x1; + Serial mobile = (Serial)0x1; var expected = new MobileAnimation( mobile, @@ -91,7 +91,7 @@ public class MobilePacketTests delay ).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileAnimation( mobile, action, @@ -102,7 +102,7 @@ public class MobilePacketTests delay ); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -111,7 +111,7 @@ public class MobilePacketTests [InlineData(10, 100, 20)] public void TestNewMobileAnimation(int action, int frameCount, byte delay) { - var mobile = (Serial)0x1; + Serial mobile = (Serial)0x1; var expected = new NewMobileAnimation( mobile, @@ -120,7 +120,7 @@ public class MobilePacketTests delay ).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendNewMobileAnimation( mobile, action, @@ -128,7 +128,7 @@ public class MobilePacketTests delay ); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -145,10 +145,10 @@ public class MobilePacketTests var expected = new HealthbarPoison(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileHealthbar(m, Healthbar.Poison); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -166,10 +166,10 @@ public class MobilePacketTests var expected = new HealthbarYellow(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileHealthbar(m, Healthbar.Yellow); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -189,10 +189,10 @@ public class MobilePacketTests var expected = new MobileStatusCompact(canBeRenamed, m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileStatusCompact(m, canBeRenamed); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -219,13 +219,13 @@ public class MobilePacketTests beheld.Dex = 25; beheld.Stam = 100; - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.ProtocolChanges = changes; var expected = new MobileStatus(beholder, beheld, ns).Compile(); ns.SendMobileStatus(beholder, beheld); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -255,7 +255,7 @@ public class MobilePacketTests m.Dex = 25; m.Stam = 100; - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.ProtocolChanges = changes; ns.Version = new ClientVersion(version); ns.Flags = clientFlags; @@ -263,7 +263,7 @@ public class MobilePacketTests var expected = new MobileStatusExtended(m, ns).Compile(); ns.SendMobileStatus(m, m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); Core.Expansion = oldExpansion; expansionInfo.MobileStatusVersion = oldVersion; @@ -278,13 +278,13 @@ public class MobilePacketTests m.DefaultMobileInit(); m.SolidHueOverride = solidHueOverride; - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.ProtocolChanges = changes; var expected = new MobileUpdate(m, ns.StygianAbyss).Compile(); ns.SendMobileUpdate(m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -333,13 +333,13 @@ public class MobilePacketTests beheld.FacialHairItemID = facialHairItemId; beheld.FacialHairHue = facialHairHue; - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.ProtocolChanges = changes; var expected = new MobileIncoming(ns, beholder, beheld).Compile(); ns.SendMobileIncoming(beholder, beheld); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -353,10 +353,10 @@ public class MobilePacketTests var expected = new MobileHits(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileHits(m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -370,10 +370,10 @@ public class MobilePacketTests var expected = new MobileHitsN(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileHits(m, true); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -387,10 +387,10 @@ public class MobilePacketTests var expected = new MobileMana(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileMana(m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -404,10 +404,10 @@ public class MobilePacketTests var expected = new MobileManaN(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileMana(m, true); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -421,10 +421,10 @@ public class MobilePacketTests var expected = new MobileStam(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileStam(m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -438,10 +438,10 @@ public class MobilePacketTests var expected = new MobileStamN(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileStam(m, true); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -459,10 +459,10 @@ public class MobilePacketTests var expected = new MobileAttributes(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileAttributes(m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -480,23 +480,23 @@ public class MobilePacketTests var expected = new MobileAttributesN(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendMobileAttributes(m, true); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } [Fact] public void TestRemoveEntity() { - var e = (Serial)0x1000; + Serial e = (Serial)0x1000; var expected = new RemoveEntity(e).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendRemoveEntity(e); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MobilePackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MobilePackets.cs index a9c2fd5be..a9ddb1175 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MobilePackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MobilePackets.cs @@ -1,647 +1,649 @@ using System.Threading; using Server.Network; -namespace Server.Tests.Network; - -public sealed class BondedStatus : Packet +namespace Server.Tests.Network { - public BondedStatus(Serial serial, bool bonded) : base(0xBF) + public sealed class BondedStatus : Packet { - EnsureCapacity(11); - - Stream.Write((short)0x19); - Stream.Write((byte)0); - Stream.Write(serial); - Stream.Write((byte)(bonded ? 1 : 0)); - } -} - -public sealed class DeathAnimation : Packet -{ - public DeathAnimation(Serial killed, Serial corpse) : base(0xAF, 13) - { - Stream.Write(killed); - Stream.Write(corpse); - Stream.Write(0); - } -} - -public sealed class MobileMoving : Packet -{ - public MobileMoving(Mobile m, int noto, bool stygianAbyss) : base(0x77, 17) - { - var loc = m.Location; - - var hue = m.Hue; - - if (m.SolidHueOverride >= 0) + public BondedStatus(Serial serial, bool bonded) : base(0xBF) { - hue = m.SolidHueOverride; - } + EnsureCapacity(11); - Stream.Write(m.Serial); - Stream.Write((short)m.Body); - Stream.Write((short)loc.X); - Stream.Write((short)loc.Y); - Stream.Write((sbyte)loc.Z); - Stream.Write((byte)m.Direction); - Stream.Write((short)hue); - Stream.Write((byte)m.GetPacketFlags(stygianAbyss)); - Stream.Write((byte)noto); - } -} - -public sealed class MobileHits : Packet -{ - public MobileHits(Mobile m) : base(0xA1, 9) - { - Stream.Write(m.Serial); - Stream.Write((short)m.HitsMax); - Stream.Write((short)m.Hits); - } -} - -public sealed class MobileHitsN : Packet -{ - public MobileHitsN(Mobile m) : base(0xA1, 9) - { - Stream.Write(m.Serial); - AttributeNormalizer.Write(Stream, m.Hits, m.HitsMax); - } -} - -public sealed class MobileMana : Packet -{ - public MobileMana(Mobile m) : base(0xA2, 9) - { - Stream.Write(m.Serial); - Stream.Write((short)m.ManaMax); - Stream.Write((short)m.Mana); - } -} - -public sealed class MobileManaN : Packet -{ - public MobileManaN(Mobile m) : base(0xA2, 9) - { - Stream.Write(m.Serial); - AttributeNormalizer.Write(Stream, m.Mana, m.ManaMax); - } -} - -public sealed class MobileStam : Packet -{ - public MobileStam(Mobile m) : base(0xA3, 9) - { - Stream.Write(m.Serial); - Stream.Write((short)m.StamMax); - Stream.Write((short)m.Stam); - } -} - -public sealed class MobileStamN : Packet -{ - public MobileStamN(Mobile m) : base(0xA3, 9) - { - Stream.Write(m.Serial); - AttributeNormalizer.Write(Stream, m.Stam, m.StamMax); - } -} - -public sealed class MobileAttributes : Packet -{ - public MobileAttributes(Mobile m) : base(0x2D, 17) - { - Stream.Write(m.Serial); - - Stream.Write((short)m.HitsMax); - Stream.Write((short)m.Hits); - - Stream.Write((short)m.ManaMax); - Stream.Write((short)m.Mana); - - Stream.Write((short)m.StamMax); - Stream.Write((short)m.Stam); - } -} - -public sealed class MobileAttributesN : Packet -{ - public MobileAttributesN(Mobile m) : base(0x2D, 17) - { - Stream.Write(m.Serial); - - AttributeNormalizer.Write(Stream, m.Hits, m.HitsMax); - AttributeNormalizer.Write(Stream, m.Mana, m.ManaMax); - AttributeNormalizer.Write(Stream, m.Stam, m.StamMax); - } -} - -public sealed class MobileName : Packet -{ - public MobileName(Mobile m) : base(0x98) - { - EnsureCapacity(37); - - Stream.Write(m.Serial); - Stream.WriteAsciiFixed(m.Name ?? "", 29); - Stream.Write((byte)0); // Null terminator - } -} - -public sealed class MobileAnimation : Packet -{ - public MobileAnimation( - Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay - ) : base(0x6E, 14) - { - Stream.Write(mobile); - Stream.Write((short)action); - Stream.Write((short)frameCount); - Stream.Write((short)repeatCount); - Stream.Write(!forward); // protocol has really "reverse" but I find this more intuitive - Stream.Write(repeat); - Stream.Write((byte)delay); - } -} - -public sealed class NewMobileAnimation : Packet -{ - public NewMobileAnimation(Serial mobile, int action, int frameCount, int delay) : base(0xE2, 10) - { - Stream.Write(mobile); - Stream.Write((short)action); - Stream.Write((short)frameCount); - Stream.Write((byte)delay); - } -} - -public sealed class HealthbarPoison : Packet -{ - public HealthbarPoison(Mobile m) : base(0x17) - { - EnsureCapacity(12); - - Stream.Write(m.Serial); - Stream.Write((short)1); // Show Bar? - - Stream.Write((short)1); // Poison Bar - - var p = m.Poison; - - if (p != null) - { - Stream.Write((byte)(p.Level + 1)); - } - else - { + Stream.Write((short)0x19); Stream.Write((byte)0); + Stream.Write(serial); + Stream.Write((byte)(bonded ? 1 : 0)); } } -} -public sealed class HealthbarYellow : Packet -{ - public HealthbarYellow(Mobile m) : base(0x17) + public sealed class DeathAnimation : Packet { - EnsureCapacity(12); - - Stream.Write(m.Serial); - Stream.Write((short)1); - - Stream.Write((short)2); - - if (m.Blessed || m.YellowHealthbar) + public DeathAnimation(Serial killed, Serial corpse) : base(0xAF, 13) { - Stream.Write((byte)1); - } - else - { - Stream.Write((byte)0); + Stream.Write(killed); + Stream.Write(corpse); + Stream.Write(0); } } -} -public sealed class MobileStatusCompact : Packet -{ - public MobileStatusCompact(bool canBeRenamed, Mobile m) : base(0x11) + public sealed class MobileMoving : Packet { - EnsureCapacity(43); - - Stream.Write(m.Serial); - Stream.WriteAsciiFixed(m.Name ?? "", 30); - - AttributeNormalizer.WriteReverse(Stream, m.Hits, m.HitsMax); - - Stream.Write(canBeRenamed); - - Stream.Write((byte)0); // type - } -} - -public sealed class MobileStatusExtended : Packet -{ - public MobileStatusExtended(Mobile m) : this(m, m.NetState) - { - } - - public MobileStatusExtended(Mobile m, NetState ns) : base(0x11) - { - var name = m.Name ?? ""; - - int type; - - if (Core.HS && ns?.ExtendedStatus == true) + public MobileMoving(Mobile m, int noto, bool stygianAbyss) : base(0x77, 17) { - type = 6; - EnsureCapacity(121); - } - else if (Core.ML && ns?.SupportsExpansion(Expansion.ML) == true) - { - type = 5; - EnsureCapacity(91); - } - else - { - type = Core.AOS ? 4 : 3; - EnsureCapacity(88); - } + var loc = m.Location; - Stream.Write(m.Serial); - Stream.WriteAsciiFixed(name, 30); + var hue = m.Hue; - Stream.Write((short)m.Hits); - Stream.Write((short)m.HitsMax); - - Stream.Write(m.CanBeRenamedBy(m)); - - Stream.Write((byte)type); - - Stream.Write(m.Female); - - Stream.Write((short)m.Str); - Stream.Write((short)m.Dex); - Stream.Write((short)m.Int); - - Stream.Write((short)m.Stam); - Stream.Write((short)m.StamMax); - - Stream.Write((short)m.Mana); - Stream.Write((short)m.ManaMax); - - Stream.Write(m.TotalGold); - Stream.Write((short)(Core.AOS ? m.PhysicalResistance : (int)(m.ArmorRating + 0.5))); - Stream.Write((short)(Mobile.BodyWeight + m.TotalWeight)); - - if (type >= 5) - { - Stream.Write((short)m.MaxWeight); - Stream.Write((byte)(m.Race?.RaceID + 1 ?? 0)); // Would be 0x00 if it's a non-ML enabled account but... - } - - Stream.Write((short)m.StatCap); - - Stream.Write((byte)m.Followers); - Stream.Write((byte)m.FollowersMax); - - if (type >= 4) - { - Stream.Write((short)m.FireResistance); // Fire - Stream.Write((short)m.ColdResistance); // Cold - Stream.Write((short)m.PoisonResistance); // Poison - Stream.Write((short)m.EnergyResistance); // Energy - Stream.Write((short)m.Luck); // Luck - - var weapon = m.Weapon; - - if (weapon != null) + if (m.SolidHueOverride >= 0) { - weapon.GetStatusDamage(m, out var min, out var max); - Stream.Write((short)min); // Damage min - Stream.Write((short)max); // Damage max + hue = m.SolidHueOverride; + } + + Stream.Write(m.Serial); + Stream.Write((short)m.Body); + Stream.Write((short)loc.X); + Stream.Write((short)loc.Y); + Stream.Write((sbyte)loc.Z); + Stream.Write((byte)m.Direction); + Stream.Write((short)hue); + Stream.Write((byte)m.GetPacketFlags(stygianAbyss)); + Stream.Write((byte)noto); + } + } + + public sealed class MobileHits : Packet + { + public MobileHits(Mobile m) : base(0xA1, 9) + { + Stream.Write(m.Serial); + Stream.Write((short)m.HitsMax); + Stream.Write((short)m.Hits); + } + } + + public sealed class MobileHitsN : Packet + { + public MobileHitsN(Mobile m) : base(0xA1, 9) + { + Stream.Write(m.Serial); + AttributeNormalizer.Write(Stream, m.Hits, m.HitsMax); + } + } + + public sealed class MobileMana : Packet + { + public MobileMana(Mobile m) : base(0xA2, 9) + { + Stream.Write(m.Serial); + Stream.Write((short)m.ManaMax); + Stream.Write((short)m.Mana); + } + } + + public sealed class MobileManaN : Packet + { + public MobileManaN(Mobile m) : base(0xA2, 9) + { + Stream.Write(m.Serial); + AttributeNormalizer.Write(Stream, m.Mana, m.ManaMax); + } + } + + public sealed class MobileStam : Packet + { + public MobileStam(Mobile m) : base(0xA3, 9) + { + Stream.Write(m.Serial); + Stream.Write((short)m.StamMax); + Stream.Write((short)m.Stam); + } + } + + public sealed class MobileStamN : Packet + { + public MobileStamN(Mobile m) : base(0xA3, 9) + { + Stream.Write(m.Serial); + AttributeNormalizer.Write(Stream, m.Stam, m.StamMax); + } + } + + public sealed class MobileAttributes : Packet + { + public MobileAttributes(Mobile m) : base(0x2D, 17) + { + Stream.Write(m.Serial); + + Stream.Write((short)m.HitsMax); + Stream.Write((short)m.Hits); + + Stream.Write((short)m.ManaMax); + Stream.Write((short)m.Mana); + + Stream.Write((short)m.StamMax); + Stream.Write((short)m.Stam); + } + } + + public sealed class MobileAttributesN : Packet + { + public MobileAttributesN(Mobile m) : base(0x2D, 17) + { + Stream.Write(m.Serial); + + AttributeNormalizer.Write(Stream, m.Hits, m.HitsMax); + AttributeNormalizer.Write(Stream, m.Mana, m.ManaMax); + AttributeNormalizer.Write(Stream, m.Stam, m.StamMax); + } + } + + public sealed class MobileName : Packet + { + public MobileName(Mobile m) : base(0x98) + { + EnsureCapacity(37); + + Stream.Write(m.Serial); + Stream.WriteAsciiFixed(m.Name ?? "", 29); + Stream.Write((byte)0); // Null terminator + } + } + + public sealed class MobileAnimation : Packet + { + public MobileAnimation( + Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay + ) : base(0x6E, 14) + { + Stream.Write(mobile); + Stream.Write((short)action); + Stream.Write((short)frameCount); + Stream.Write((short)repeatCount); + Stream.Write(!forward); // protocol has really "reverse" but I find this more intuitive + Stream.Write(repeat); + Stream.Write((byte)delay); + } + } + + public sealed class NewMobileAnimation : Packet + { + public NewMobileAnimation(Serial mobile, int action, int frameCount, int delay) : base(0xE2, 10) + { + Stream.Write(mobile); + Stream.Write((short)action); + Stream.Write((short)frameCount); + Stream.Write((byte)delay); + } + } + + public sealed class HealthbarPoison : Packet + { + public HealthbarPoison(Mobile m) : base(0x17) + { + EnsureCapacity(12); + + Stream.Write(m.Serial); + Stream.Write((short)1); // Show Bar? + + Stream.Write((short)1); // Poison Bar + + var p = m.Poison; + + if (p != null) + { + Stream.Write((byte)(p.Level + 1)); } else { - Stream.Write((short)0); // Damage min - Stream.Write((short)0); // Damage max + Stream.Write((byte)0); } - - Stream.Write(m.TithingPoints); } + } - if (type >= 6) + public sealed class HealthbarYellow : Packet + { + public HealthbarYellow(Mobile m) : base(0x17) { - for (var i = 0; i < 15; ++i) + EnsureCapacity(12); + + Stream.Write(m.Serial); + Stream.Write((short)1); + + Stream.Write((short)2); + + if (m.Blessed || m.YellowHealthbar) { - Stream.Write((short)m.GetAOSStatus(i)); + Stream.Write((byte)1); + } + else + { + Stream.Write((byte)0); } } } -} -public sealed class MobileStatus : Packet -{ - public MobileStatus(Mobile beholder, Mobile beheld) : this(beholder, beheld, beheld.NetState) + public sealed class MobileStatusCompact : Packet { - } - - public MobileStatus(Mobile beholder, Mobile beheld, NetState ns) : base(0x11) - { - var name = beheld.Name ?? ""; - - int type; - - if (beholder != beheld) + public MobileStatusCompact(bool canBeRenamed, Mobile m) : base(0x11) { - type = 0; EnsureCapacity(43); + + Stream.Write(m.Serial); + Stream.WriteAsciiFixed(m.Name ?? "", 30); + + AttributeNormalizer.WriteReverse(Stream, m.Hits, m.HitsMax); + + Stream.Write(canBeRenamed); + + Stream.Write((byte)0); // type } - else if (Core.HS && ns?.ExtendedStatus == true) + } + + public sealed class MobileStatusExtended : Packet + { + public MobileStatusExtended(Mobile m) : this(m, m.NetState) { - type = 6; - EnsureCapacity(121); - } - else if (Core.ML && ns?.SupportsExpansion(Expansion.ML) == true) - { - type = 5; - EnsureCapacity(91); - } - else - { - type = Core.AOS ? 4 : 3; - EnsureCapacity(88); } - Stream.Write(beheld.Serial); - - Stream.WriteAsciiFixed(name, 30); - - if (beholder == beheld) + public MobileStatusExtended(Mobile m, NetState ns) : base(0x11) { - WriteAttr(beheld.Hits, beheld.HitsMax); - } - else - { - WriteAttrNorm(beheld.Hits, beheld.HitsMax); - } + var name = m.Name ?? ""; - Stream.Write(beheld.CanBeRenamedBy(beholder)); + int type; - Stream.Write((byte)type); - - if (type <= 0) - { - return; - } - - Stream.Write(beheld.Female); - - Stream.Write((short)beheld.Str); - Stream.Write((short)beheld.Dex); - Stream.Write((short)beheld.Int); - - WriteAttr(beheld.Stam, beheld.StamMax); - WriteAttr(beheld.Mana, beheld.ManaMax); - - Stream.Write(beheld.TotalGold); - Stream.Write((short)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5))); - Stream.Write((short)(Mobile.BodyWeight + beheld.TotalWeight)); - - if (type >= 5) - { - Stream.Write((short)beheld.MaxWeight); - Stream.Write((byte)(beheld.Race.RaceID + 1)); // Would be 0x00 if it's a non-ML enabled account but... - } - - Stream.Write((short)beheld.StatCap); - - Stream.Write((byte)beheld.Followers); - Stream.Write((byte)beheld.FollowersMax); - - if (type >= 4) - { - Stream.Write((short)beheld.FireResistance); // Fire - Stream.Write((short)beheld.ColdResistance); // Cold - Stream.Write((short)beheld.PoisonResistance); // Poison - Stream.Write((short)beheld.EnergyResistance); // Energy - Stream.Write((short)beheld.Luck); // Luck - - var weapon = beheld.Weapon; - - if (weapon != null) + if (Core.HS && ns?.ExtendedStatus == true) { - weapon.GetStatusDamage(beheld, out var min, out var max); - Stream.Write((short)min); // Damage min - Stream.Write((short)max); // Damage max + type = 6; + EnsureCapacity(121); + } + else if (Core.ML && ns?.SupportsExpansion(Expansion.ML) == true) + { + type = 5; + EnsureCapacity(91); } else { - Stream.Write((short)0); // Damage min - Stream.Write((short)0); // Damage max + type = Core.AOS ? 4 : 3; + EnsureCapacity(88); } - Stream.Write(beheld.TithingPoints); - } + Stream.Write(m.Serial); + Stream.WriteAsciiFixed(name, 30); - if (type >= 6) - { - for (var i = 0; i < 15; ++i) + Stream.Write((short)m.Hits); + Stream.Write((short)m.HitsMax); + + Stream.Write(m.CanBeRenamedBy(m)); + + Stream.Write((byte)type); + + Stream.Write(m.Female); + + Stream.Write((short)m.Str); + Stream.Write((short)m.Dex); + Stream.Write((short)m.Int); + + Stream.Write((short)m.Stam); + Stream.Write((short)m.StamMax); + + Stream.Write((short)m.Mana); + Stream.Write((short)m.ManaMax); + + Stream.Write(m.TotalGold); + Stream.Write((short)(Core.AOS ? m.PhysicalResistance : (int)(m.ArmorRating + 0.5))); + Stream.Write((short)(Mobile.BodyWeight + m.TotalWeight)); + + if (type >= 5) { - Stream.Write((short)beheld.GetAOSStatus(i)); + Stream.Write((short)m.MaxWeight); + Stream.Write((byte)(m.Race?.RaceID + 1 ?? 0)); // Would be 0x00 if it's a non-ML enabled account but... + } + + Stream.Write((short)m.StatCap); + + Stream.Write((byte)m.Followers); + Stream.Write((byte)m.FollowersMax); + + if (type >= 4) + { + Stream.Write((short)m.FireResistance); // Fire + Stream.Write((short)m.ColdResistance); // Cold + Stream.Write((short)m.PoisonResistance); // Poison + Stream.Write((short)m.EnergyResistance); // Energy + Stream.Write((short)m.Luck); // Luck + + var weapon = m.Weapon; + + if (weapon != null) + { + weapon.GetStatusDamage(m, out var min, out var max); + Stream.Write((short)min); // Damage min + Stream.Write((short)max); // Damage max + } + else + { + Stream.Write((short)0); // Damage min + Stream.Write((short)0); // Damage max + } + + Stream.Write(m.TithingPoints); + } + + if (type >= 6) + { + for (var i = 0; i < 15; ++i) + { + Stream.Write((short)m.GetAOSStatus(i)); + } } } } - private void WriteAttr(int current, int maximum) + public sealed class MobileStatus : Packet { - Stream.Write((short)current); - Stream.Write((short)maximum); + public MobileStatus(Mobile beholder, Mobile beheld) : this(beholder, beheld, beheld.NetState) + { + } + + public MobileStatus(Mobile beholder, Mobile beheld, NetState ns) : base(0x11) + { + var name = beheld.Name ?? ""; + + int type; + + if (beholder != beheld) + { + type = 0; + EnsureCapacity(43); + } + else if (Core.HS && ns?.ExtendedStatus == true) + { + type = 6; + EnsureCapacity(121); + } + else if (Core.ML && ns?.SupportsExpansion(Expansion.ML) == true) + { + type = 5; + EnsureCapacity(91); + } + else + { + type = Core.AOS ? 4 : 3; + EnsureCapacity(88); + } + + Stream.Write(beheld.Serial); + + Stream.WriteAsciiFixed(name, 30); + + if (beholder == beheld) + { + WriteAttr(beheld.Hits, beheld.HitsMax); + } + else + { + WriteAttrNorm(beheld.Hits, beheld.HitsMax); + } + + Stream.Write(beheld.CanBeRenamedBy(beholder)); + + Stream.Write((byte)type); + + if (type <= 0) + { + return; + } + + Stream.Write(beheld.Female); + + Stream.Write((short)beheld.Str); + Stream.Write((short)beheld.Dex); + Stream.Write((short)beheld.Int); + + WriteAttr(beheld.Stam, beheld.StamMax); + WriteAttr(beheld.Mana, beheld.ManaMax); + + Stream.Write(beheld.TotalGold); + Stream.Write((short)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5))); + Stream.Write((short)(Mobile.BodyWeight + beheld.TotalWeight)); + + if (type >= 5) + { + Stream.Write((short)beheld.MaxWeight); + Stream.Write((byte)(beheld.Race.RaceID + 1)); // Would be 0x00 if it's a non-ML enabled account but... + } + + Stream.Write((short)beheld.StatCap); + + Stream.Write((byte)beheld.Followers); + Stream.Write((byte)beheld.FollowersMax); + + if (type >= 4) + { + Stream.Write((short)beheld.FireResistance); // Fire + Stream.Write((short)beheld.ColdResistance); // Cold + Stream.Write((short)beheld.PoisonResistance); // Poison + Stream.Write((short)beheld.EnergyResistance); // Energy + Stream.Write((short)beheld.Luck); // Luck + + var weapon = beheld.Weapon; + + if (weapon != null) + { + weapon.GetStatusDamage(beheld, out var min, out var max); + Stream.Write((short)min); // Damage min + Stream.Write((short)max); // Damage max + } + else + { + Stream.Write((short)0); // Damage min + Stream.Write((short)0); // Damage max + } + + Stream.Write(beheld.TithingPoints); + } + + if (type >= 6) + { + for (var i = 0; i < 15; ++i) + { + Stream.Write((short)beheld.GetAOSStatus(i)); + } + } + } + + private void WriteAttr(int current, int maximum) + { + Stream.Write((short)current); + Stream.Write((short)maximum); + } + + private void WriteAttrNorm(int current, int maximum) + { + AttributeNormalizer.WriteReverse(Stream, current, maximum); + } } - private void WriteAttrNorm(int current, int maximum) + public sealed class MobileUpdate : Packet { - AttributeNormalizer.WriteReverse(Stream, current, maximum); - } -} - -public sealed class MobileUpdate : Packet -{ - public MobileUpdate(Mobile m, bool stygianAbyss) : base(0x20, 19) - { - var hue = m.Hue; - - if (m.SolidHueOverride >= 0) - { - hue = m.SolidHueOverride; - } - - Stream.Write(m.Serial); - Stream.Write((short)m.Body); - Stream.Write((byte)0); - Stream.Write((short)hue); - Stream.Write((byte)m.GetPacketFlags(stygianAbyss)); - Stream.Write((short)m.X); - Stream.Write((short)m.Y); - Stream.Write((short)0); - Stream.Write((byte)m.Direction); - Stream.Write((sbyte)m.Z); - } -} - -public sealed class MobileIncoming : Packet -{ - private static readonly ThreadLocal m_DupedLayersTL = new(() => new int[256]); - private static readonly ThreadLocal m_VersionTL = new(); - - public MobileIncoming(NetState ns, Mobile beholder, Mobile beheld) : base(0x78) - { - var sa = ns.StygianAbyss; - var newPacket = ns.NewMobileIncoming; - var itemIdMask = newPacket ? 0xFFFF : 0x7FFF; - - var m_Version = ++m_VersionTL.Value; - var m_DupedLayers = m_DupedLayersTL.Value; - - var eq = beheld.Items; - var count = eq.Count; - var hairItemId = beheld.HairItemID; - var facialHairItemId = beheld.FacialHairItemID; - - if (hairItemId > 0) - { - count++; - } - - if (facialHairItemId > 0) - { - count++; - } - - EnsureCapacity(23 + count * 9); - - var hue = beheld.Hue; - - if (beheld.SolidHueOverride >= 0) - { - hue = beheld.SolidHueOverride; - } - - Stream.Write(beheld.Serial); - Stream.Write((short)beheld.Body); - Stream.Write((short)beheld.X); - Stream.Write((short)beheld.Y); - Stream.Write((sbyte)beheld.Z); - Stream.Write((byte)beheld.Direction); - Stream.Write((short)hue); - Stream.Write((byte)beheld.GetPacketFlags(sa)); - Stream.Write((byte)Notoriety.Compute(beholder, beheld)); - - for (var i = 0; i < eq.Count; ++i) - { - var item = eq[i]; - - var layer = (byte)item.Layer; - - if (!item.Deleted && beholder.CanSee(item) && m_DupedLayers![layer] != m_Version) - { - m_DupedLayers[layer] = m_Version; - - hue = item.Hue; - - if (beheld.SolidHueOverride >= 0) - { - hue = beheld.SolidHueOverride; - } - - var itemID = item.ItemID & itemIdMask; - var writeHue = newPacket || hue != 0; - - if (!newPacket && writeHue) - { - itemID |= 0x8000; - } - - Stream.Write(item.Serial); - Stream.Write((ushort)itemID); - Stream.Write(layer); - - if (writeHue) - { - Stream.Write((short)hue); - } - } - } - - if (hairItemId > 0) - { - if (m_DupedLayers![(int)Layer.Hair] != m_Version) - { - m_DupedLayers[(int)Layer.Hair] = m_Version; - hue = beheld.HairHue; - - if (beheld.SolidHueOverride >= 0) - { - hue = beheld.SolidHueOverride; - } - - var itemID = hairItemId & itemIdMask; - var writeHue = newPacket || hue != 0; - - if (!newPacket && writeHue) - { - itemID |= 0x8000; - } - - Stream.Write(beheld.HairSerial); - Stream.Write((ushort)itemID); - Stream.Write((byte)Layer.Hair); - - if (writeHue) - { - Stream.Write((short)hue); - } - } - } - - if (facialHairItemId > 0) - { - if (m_DupedLayers![(int)Layer.FacialHair] != m_Version) - { - m_DupedLayers[(int)Layer.FacialHair] = m_Version; - hue = beheld.FacialHairHue; - - if (beheld.SolidHueOverride >= 0) - { - hue = beheld.SolidHueOverride; - } - - var itemID = facialHairItemId & itemIdMask; - var writeHue = newPacket || hue != 0; - - if (!newPacket && writeHue) - { - itemID |= 0x8000; - } - - Stream.Write(beheld.FacialHairSerial); - Stream.Write((ushort)itemID); - Stream.Write((byte)Layer.FacialHair); - - if (writeHue) - { - Stream.Write((short)hue); - } - } - } - - Stream.Write(0); // terminate - } -} - -public sealed class RemoveEntity : Packet -{ - public RemoveEntity(Serial entity) : base(0x1D, 5) - { - Stream.Write(entity); + public MobileUpdate(Mobile m, bool stygianAbyss) : base(0x20, 19) + { + var hue = m.Hue; + + if (m.SolidHueOverride >= 0) + { + hue = m.SolidHueOverride; + } + + Stream.Write(m.Serial); + Stream.Write((short)m.Body); + Stream.Write((byte)0); + Stream.Write((short)hue); + Stream.Write((byte)m.GetPacketFlags(stygianAbyss)); + Stream.Write((short)m.X); + Stream.Write((short)m.Y); + Stream.Write((short)0); + Stream.Write((byte)m.Direction); + Stream.Write((sbyte)m.Z); + } + } + + public sealed class MobileIncoming : Packet + { + private static readonly ThreadLocal m_DupedLayersTL = new(() => new int[256]); + private static readonly ThreadLocal m_VersionTL = new(); + + public MobileIncoming(NetState ns, Mobile beholder, Mobile beheld) : base(0x78) + { + var sa = ns.StygianAbyss; + var newPacket = ns.NewMobileIncoming; + var itemIdMask = newPacket ? 0xFFFF : 0x7FFF; + + var m_Version = ++m_VersionTL.Value; + var m_DupedLayers = m_DupedLayersTL.Value; + + var eq = beheld.Items; + var count = eq.Count; + var hair = beheld.Hair; + var facialHair = beheld.FacialHair; + + + if (hair != null) + { + count++; + } + + if (facialHair != null) + { + count++; + } + + EnsureCapacity(23 + count * 9); + + var hue = beheld.Hue; + + if (beheld.SolidHueOverride >= 0) + { + hue = beheld.SolidHueOverride; + } + + Stream.Write(beheld.Serial); + Stream.Write((short)beheld.Body); + Stream.Write((short)beheld.X); + Stream.Write((short)beheld.Y); + Stream.Write((sbyte)beheld.Z); + Stream.Write((byte)beheld.Direction); + Stream.Write((short)hue); + Stream.Write((byte)beheld.GetPacketFlags(sa)); + Stream.Write((byte)Notoriety.Compute(beholder, beheld)); + + for (var i = 0; i < eq.Count; ++i) + { + var item = eq[i]; + + var layer = (byte)item.Layer; + + if (!item.Deleted && beholder.CanSee(item) && m_DupedLayers![layer] != m_Version) + { + m_DupedLayers[layer] = m_Version; + + hue = item.Hue; + + if (beheld.SolidHueOverride >= 0) + { + hue = beheld.SolidHueOverride; + } + + var itemID = item.ItemID & itemIdMask; + var writeHue = newPacket || hue != 0; + + if (!newPacket && writeHue) + { + itemID |= 0x8000; + } + + Stream.Write(item.Serial); + Stream.Write((ushort)itemID); + Stream.Write(layer); + + if (writeHue) + { + Stream.Write((short)hue); + } + } + } + + if (hair?.ItemId > 0) + { + if (m_DupedLayers![(int)Layer.Hair] != m_Version) + { + m_DupedLayers[(int)Layer.Hair] = m_Version; + hue = hair.Hue; + + if (beheld.SolidHueOverride >= 0) + { + hue = beheld.SolidHueOverride; + } + + var itemID = hair.ItemId & itemIdMask; + var writeHue = newPacket || hue != 0; + + if (!newPacket && writeHue) + { + itemID |= 0x8000; + } + + Stream.Write(hair.VirtualSerial); + Stream.Write((ushort)itemID); + Stream.Write((byte)Layer.Hair); + + if (writeHue) + { + Stream.Write((short)hue); + } + } + } + + if (facialHair?.ItemId > 0) + { + if (m_DupedLayers![(int)Layer.FacialHair] != m_Version) + { + m_DupedLayers[(int)Layer.FacialHair] = m_Version; + hue = facialHair.Hue; + + if (beheld.SolidHueOverride >= 0) + { + hue = beheld.SolidHueOverride; + } + + var itemID = facialHair.ItemId & itemIdMask; + var writeHue = newPacket || hue != 0; + + if (!newPacket && writeHue) + { + itemID |= 0x8000; + } + + Stream.Write(facialHair.VirtualSerial); + Stream.Write((ushort)itemID); + Stream.Write((byte)Layer.FacialHair); + + if (writeHue) + { + Stream.Write((short)hue); + } + } + } + + Stream.Write(0); // terminate + } + } + + public sealed class RemoveEntity : Packet + { + public RemoveEntity(Serial entity) : base(0x1D, 5) + { + Stream.Write(entity); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPacketTests.cs index 330eaf0b5..e438fab9e 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPacketTests.cs @@ -1,70 +1,70 @@ using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class MovementPacketTests +namespace Server.Tests.Network { - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void TestSpeedControl(byte speedControl) + public class MovementPacketTests : IClassFixture { - var expected = new SpeedControl(speedControl).Compile(); + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + public void TestSpeedControl(byte speedControl) + { + var expected = new SpeedControl(speedControl).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendSpeedControl((SpeedControlSetting)speedControl); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendSpeedControl((SpeedControlSetting)speedControl); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestMovePlayer() - { - const Direction d = Direction.Left; - var expected = new MovePlayer(d).Compile(); + [Fact] + public void TestMovePlayer() + { + const Direction d = Direction.Left; + var expected = new MovePlayer(d).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMovePlayer(d); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMovePlayer(d); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestMovementRej() - { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + [Fact] + public void TestMovementRej() + { + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - const byte seq = 100; + const byte seq = 100; - var expected = new MovementRej(seq, m).Compile(); + var expected = new MovementRej(seq, m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMovementRej(seq, m); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMovementRej(seq, m); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestMovementAck() - { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + [Fact] + public void TestMovementAck() + { + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - const byte seq = 100; + const byte seq = 100; - var expected = MovementAck.Instantiate(seq, m).Compile(); + var expected = MovementAck.Instantiate(seq, m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMovementAck(seq, m); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMovementAck(seq, m); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPackets.cs index 1e9d7b89f..57d1e9dd5 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/MovementPackets.cs @@ -1,67 +1,68 @@ -namespace Server.Network; - -public sealed class SpeedControl : Packet +namespace Server.Network { - public static readonly Packet WalkSpeed = SetStatic(new SpeedControl(2)); - public static readonly Packet MountSpeed = SetStatic(new SpeedControl(1)); - public static readonly Packet Disable = SetStatic(new SpeedControl(0)); - - public SpeedControl(int speedControl) : base(0xBF) + public sealed class SpeedControl : Packet { - EnsureCapacity(6); + public static readonly Packet WalkSpeed = SetStatic(new SpeedControl(2)); + public static readonly Packet MountSpeed = SetStatic(new SpeedControl(1)); + public static readonly Packet Disable = SetStatic(new SpeedControl(0)); - Stream.Write((short)0x26); - Stream.Write((byte)speedControl); - } -} - -/// -/// Causes the client to walk in a given direction. It does not send a movement request. -/// -public sealed class MovePlayer : Packet -{ - public MovePlayer(Direction d) : base(0x97, 2) - { - Stream.Write((byte)d); - - // @4C63B0 - } -} - -public sealed class MovementRej : Packet -{ - public MovementRej(int seq, Mobile m) : base(0x21, 8) - { - Stream.Write((byte)seq); - Stream.Write((short)m.X); - Stream.Write((short)m.Y); - Stream.Write((byte)m.Direction); - Stream.Write((sbyte)m.Z); - } -} - -public sealed class MovementAck : Packet -{ - private static readonly MovementAck[] m_Cache = new MovementAck[8 * 256]; - - private MovementAck(int seq, int noto) : base(0x22, 3) - { - Stream.Write((byte)seq); - Stream.Write((byte)noto); - } - - public static MovementAck Instantiate(int seq, Mobile m) - { - var noto = Notoriety.Compute(m, m); - - var p = m_Cache[noto * seq]; - - if (p == null) + public SpeedControl(int speedControl) : base(0xBF) { - m_Cache[noto * seq] = p = new MovementAck(seq, noto); - p.SetStatic(); + EnsureCapacity(6); + + Stream.Write((short)0x26); + Stream.Write((byte)speedControl); + } + } + + /// + /// Causes the client to walk in a given direction. It does not send a movement request. + /// + public sealed class MovePlayer : Packet + { + public MovePlayer(Direction d) : base(0x97, 2) + { + Stream.Write((byte)d); + + // @4C63B0 + } + } + + public sealed class MovementRej : Packet + { + public MovementRej(int seq, Mobile m) : base(0x21, 8) + { + Stream.Write((byte)seq); + Stream.Write((short)m.X); + Stream.Write((short)m.Y); + Stream.Write((byte)m.Direction); + Stream.Write((sbyte)m.Z); + } + } + + public sealed class MovementAck : Packet + { + private static readonly MovementAck[] m_Cache = new MovementAck[8 * 256]; + + private MovementAck(int seq, int noto) : base(0x22, 3) + { + Stream.Write((byte)seq); + Stream.Write((byte)noto); } - return p; + public static MovementAck Instantiate(int seq, Mobile m) + { + var noto = Notoriety.Compute(m, m); + + var p = m_Cache[noto * seq]; + + if (p == null) + { + m_Cache[noto * seq] = p = new MovementAck(seq, noto); + p.SetStatic(); + } + + return p; + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/PlayerPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/PlayerPacketTests.cs index 6d6e5daa3..2e20a6f80 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/PlayerPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/PlayerPacketTests.cs @@ -3,295 +3,296 @@ using Server.HuePickers; using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class PlayerPacketTests +namespace Server.Tests.Network { - [Theory] - [InlineData(StatLockType.Down, StatLockType.Up, StatLockType.Locked)] - public void TestStatLockInfo(StatLockType str, StatLockType intel, StatLockType dex) + [Collection("Sequential Tests")] + public class PlayerPacketTests : IClassFixture { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); - m.StrLock = str; - m.IntLock = intel; - m.DexLock = dex; + [Theory] + [InlineData(StatLockType.Down, StatLockType.Up, StatLockType.Locked)] + public void TestStatLockInfo(StatLockType str, StatLockType intel, StatLockType dex) + { + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); + m.StrLock = str; + m.IntLock = intel; + m.DexLock = dex; - var expected = new StatLockInfo(m).Compile(); + var expected = new StatLockInfo(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendStatLockInfo(m); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendStatLockInfo(m); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(0)] - [InlineData(10)] - [InlineData(200)] - public void TestChangeUpdateRange(int range) - { - var expected = new ChangeUpdateRange(range).Compile(); + [Theory] + [InlineData(0)] + [InlineData(10)] + [InlineData(200)] + public void TestChangeUpdateRange(int range) + { + var expected = new ChangeUpdateRange(range).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendChangeUpdateRange((byte)range); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendChangeUpdateRange((byte)range); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestDeathStatus() - { - var expected = new DeathStatus(false).Compile(); + [Fact] + public void TestDeathStatus() + { + var expected = new DeathStatus(false).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDeathStatus(); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDeathStatus(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(0x1000u, "This is a header", "This is a body", "This is a footer")] - [InlineData(0x1000u, null, null, null)] - public void TestDisplayProfile(uint serial, string header, string body, string footer) - { - var expected = new DisplayProfile((Serial)serial, header, body, footer).Compile(); + [Theory] + [InlineData(0x1000u, "This is a header", "This is a body", "This is a footer")] + [InlineData(0x1000u, null, null, null)] + public void TestDisplayProfile(uint serial, string header, string body, string footer) + { + var expected = new DisplayProfile((Serial)serial, header, body, footer).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDisplayProfile((Serial)serial, header, body, footer); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplayProfile((Serial)serial, header, body, footer); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(LRReason.CannotLift)] - [InlineData(LRReason.TryToSteal)] - public void TestLiftRej(LRReason reason) - { - var expected = new LiftRej(reason).Compile(); + [Theory] + [InlineData(LRReason.CannotLift)] + [InlineData(LRReason.TryToSteal)] + public void TestLiftRej(LRReason reason) + { + var expected = new LiftRej(reason).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendLiftReject(reason); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendLiftReject(reason); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestLogoutAck() - { - var expected = new LogoutAck().Compile(); + [Fact] + public void TestLogoutAck() + { + var expected = new LogoutAck().Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendLogoutAck(); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendLogoutAck(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(1, 2, 3)] - [InlineData(4, 5, 6)] - [InlineData(0x1234, 0x5678, 0x9ABC)] - public void TestWeather(int type, int density, int temp) - { - var expected = new Weather(type, density, temp).Compile(); + [Theory] + [InlineData(1, 2, 3)] + [InlineData(4, 5, 6)] + [InlineData(0x1234, 0x5678, 0x9ABC)] + public void TestWeather(int type, int density, int temp) + { + var expected = new Weather(type, density, temp).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendWeather((byte)type, (byte)density, (byte)temp); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendWeather((byte)type, (byte)density, (byte)temp); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(100, 1000, 1, 0)] - public void TestServerChange(int x, int y, int z, int mapID) - { - var p = new Point3D(x, y, z); - var map = Map.Maps[mapID]; - var expected = new ServerChange(p, map).Compile(); + [Theory] + [InlineData(100, 1000, 1, 0)] + public void TestServerChange(int x, int y, int z, int mapID) + { + var p = new Point3D(x, y, z); + var map = Map.Maps[mapID]; + var expected = new ServerChange(p, map).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendServerChange(p, map); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendServerChange(p, map); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(0)] - [InlineData(10)] - [InlineData(255)] - public void TestSequence(byte num) - { - var expected = new Sequence(num).Compile(); + [Theory] + [InlineData(0)] + [InlineData(10)] + [InlineData(255)] + public void TestSequence(byte num) + { + var expected = new Sequence(num).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendSequence(num); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendSequence(num); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData("This is a URL, I promise")] - [InlineData("https://www.modernuo.com")] - public void TestLaunchBrowser(string uri) - { - var expected = new LaunchBrowser(uri).Compile(); + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("This is a URL, I promise")] + [InlineData("https://www.modernuo.com")] + public void TestLaunchBrowser(string uri) + { + var expected = new LaunchBrowser(uri).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendLaunchBrowser(uri); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendLaunchBrowser(uri); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(0x1000u, 1000, 100, 10, 0x2000u, 1125, 125, 5, 0x384, 1024, 25)] - public void TestDragEffect( - uint srcSerial, int srcX, int srcY, int srcZ, - uint trgSerial, int trgX, int trgY, int trgZ, - int itemId, int hue, int amount - ) - { - var src = new Entity((Serial)srcSerial, new Point3D(srcX, srcY, srcZ), null); - var targ = new Entity((Serial)trgSerial, new Point3D(trgX, trgY, trgZ), null); + [Theory] + [InlineData(0x1000u, 1000, 100, 10, 0x2000u, 1125, 125, 5, 0x384, 1024, 25)] + public void TestDragEffect( + uint srcSerial, int srcX, int srcY, int srcZ, + uint trgSerial, int trgX, int trgY, int trgZ, + int itemId, int hue, int amount + ) + { + var src = new Entity((Serial)srcSerial, new Point3D(srcX, srcY, srcZ), null); + var targ = new Entity((Serial)trgSerial, new Point3D(trgX, trgY, trgZ), null); - var expected = new DragEffect(src, targ, itemId, hue, amount).Compile(); + var expected = new DragEffect(src, targ, itemId, hue, amount).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDragEffect( - src.Serial, src.Location, - targ.Serial, targ.Location, - itemId, hue, amount - ); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDragEffect( + src.Serial, src.Location, + targ.Serial, targ.Location, + itemId, hue, amount + ); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(1, false)] - [InlineData(2, true)] - public void TestSeasonChange(int season, bool playSound) - { - var expected = new SeasonChange(season, playSound).Compile(); + [Theory] + [InlineData(1, false)] + [InlineData(2, true)] + public void TestSeasonChange(int season, bool playSound) + { + var expected = new SeasonChange(season, playSound).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendSeasonChange((byte)season, playSound); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendSeasonChange((byte)season, playSound); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(0x1024u, "Test Title", true, true)] - [InlineData(0x1024u, "Test Title", false, true)] - [InlineData(0x1024u, "Test Title", true, false)] - public void TestDisplayPaperdoll(uint m, string title, bool warmode, bool canLift) - { - var expected = new DisplayPaperdoll((Serial)m, title, warmode, canLift).Compile(); + [Theory] + [InlineData(0x1024u, "Test Title", true, true)] + [InlineData(0x1024u, "Test Title", false, true)] + [InlineData(0x1024u, "Test Title", true, false)] + public void TestDisplayPaperdoll(uint m, string title, bool warmode, bool canLift) + { + var expected = new DisplayPaperdoll((Serial)m, title, warmode, canLift).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDisplayPaperdoll((Serial)m, title, warmode, canLift); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplayPaperdoll((Serial)m, title, warmode, canLift); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(MusicName.Approach)] - [InlineData(MusicName.Combat1)] - [InlineData(MusicName.ValoriaShips)] - public void TestPlayMusic(MusicName music) - { - var expected = new PlayMusic(music).Compile(); + [Theory] + [InlineData(MusicName.Approach)] + [InlineData(MusicName.Combat1)] + [InlineData(MusicName.ValoriaShips)] + public void TestPlayMusic(MusicName music) + { + var expected = new PlayMusic(music).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendPlayMusic(music); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPlayMusic(music); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(10, 1, "Some text")] - [InlineData(100, 10, "Some more text")] - public void TestScrollMessage(int type, int tip, string text) - { - var expected = new ScrollMessage(type, tip, text).Compile(); + [Theory] + [InlineData(10, 1, "Some text")] + [InlineData(100, 10, "Some more text")] + public void TestScrollMessage(int type, int tip, string text) + { + var expected = new ScrollMessage(type, tip, text).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendScrollMessage(type, tip ,text); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendScrollMessage(type, tip ,text); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(14, 10, 05)] - public void TestCurrentTime(int hour, int minute, int second) - { - var date = new DateTime(2020, 1, 1, hour, minute, second); - var expected = new CurrentTime(date).Compile(); + [Theory] + [InlineData(14, 10, 05)] + public void TestCurrentTime(int hour, int minute, int second) + { + var date = new DateTime(2020, 1, 1, hour, minute, second); + var expected = new CurrentTime(date).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendCurrentTime(date); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendCurrentTime(date); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(1000, 10, 1)] - public void TestPathfindMessage(int x, int y, int z) - { - var p = new Point3D(x, y, z); - var expected = new PathfindMessage(p).Compile(); + [Theory] + [InlineData(1000, 10, 1)] + public void TestPathfindMessage(int x, int y, int z) + { + var p = new Point3D(x, y, z); + var expected = new PathfindMessage(p).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendPathfindMessage(p); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPathfindMessage(p); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(0)] - [InlineData(10)] - [InlineData(100)] - public void TestPingAck(byte ping) - { - var expected = new PingAck(ping).Compile(); + [Theory] + [InlineData(0)] + [InlineData(10)] + [InlineData(100)] + public void TestPingAck(byte ping) + { + var expected = new PingAck(ping).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendPingAck(ping); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPingAck(ping); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(0xFF01)] - public void TestDisplayHuePicker(int itemID) - { - var huePicker = new HuePicker(itemID); + [Theory] + [InlineData(0xFF01)] + public void TestDisplayHuePicker(int itemID) + { + var huePicker = new HuePicker(itemID); - var expected = new DisplayHuePicker(huePicker).Compile(); + var expected = new DisplayHuePicker(huePicker).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDisplayHuePicker(huePicker.Serial, huePicker.ItemID); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplayHuePicker(huePicker.Serial, huePicker.ItemID); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/PlayerPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/PlayerPackets.cs index 8ad0da0bb..f8cb355e1 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/PlayerPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/PlayerPackets.cs @@ -1,372 +1,373 @@ using System; using Server.HuePickers; -namespace Server.Network; - -public sealed class StatLockInfo : Packet +namespace Server.Network { - public StatLockInfo(Mobile m) : base(0xBF) + public sealed class StatLockInfo : Packet { - EnsureCapacity(12); - - Stream.Write((short)0x19); - Stream.Write((byte)2); - Stream.Write(m.Serial); - Stream.Write((byte)0); - - var lockBits = ((int)m.StrLock << 4) | ((int)m.DexLock << 2) | (int)m.IntLock; - - Stream.Write((byte)lockBits); - } -} - -public sealed class ChangeUpdateRange : Packet -{ - private static readonly ChangeUpdateRange[] m_Cache = new ChangeUpdateRange[0x100]; - - public ChangeUpdateRange(int range) : base(0xC8, 2) - { - Stream.Write((byte)range); - } - - public static ChangeUpdateRange Instantiate(int range) - { - var idx = (byte)range; - var p = m_Cache[idx]; - - if (p == null) + public StatLockInfo(Mobile m) : base(0xBF) { - m_Cache[idx] = p = new ChangeUpdateRange(range); - p.SetStatic(); + EnsureCapacity(12); + + Stream.Write((short)0x19); + Stream.Write((byte)2); + Stream.Write(m.Serial); + Stream.Write((byte)0); + + var lockBits = ((int)m.StrLock << 4) | ((int)m.DexLock << 2) | (int)m.IntLock; + + Stream.Write((byte)lockBits); + } + } + + public sealed class ChangeUpdateRange : Packet + { + private static readonly ChangeUpdateRange[] m_Cache = new ChangeUpdateRange[0x100]; + + public ChangeUpdateRange(int range) : base(0xC8, 2) + { + Stream.Write((byte)range); } - return p; - } -} - -public sealed class DeathStatus : Packet -{ - public static readonly Packet Dead = SetStatic(new DeathStatus(true)); - public static readonly Packet Alive = SetStatic(new DeathStatus(false)); - - public DeathStatus(bool dead) : base(0x2C, 2) - { - Stream.Write((byte)(dead ? 0 : 2)); - } - - public static Packet Instantiate(bool dead) => dead ? Dead : Alive; -} - -public sealed class DisplayProfile : Packet -{ - public DisplayProfile(Serial m, string header, string body, string footer) : base(0xB8) - { - header ??= ""; - body ??= ""; - footer ??= ""; - - EnsureCapacity(12 + header.Length + footer.Length * 2 + body.Length * 2); - - Stream.Write(m); - Stream.WriteAsciiNull(header); - Stream.WriteBigUniNull(footer); - Stream.WriteBigUniNull(body); - } -} - -public sealed class LiftRej : Packet -{ - public LiftRej(LRReason reason) : base(0x27, 2) - { - Stream.Write((byte)reason); - } -} - -public sealed class LogoutAck : Packet -{ - public LogoutAck() : base(0xD1, 2) - { - Stream.Write((byte)0x01); - } -} - -public sealed class Weather : Packet -{ - public Weather(int type, int density, int temp) : base(0x65, 4) - { - Stream.Write((byte)type); - Stream.Write((byte)density); - Stream.Write((byte)temp); - } -} - -public sealed class ServerChange : Packet -{ - public ServerChange(Point3D p, Map map) : base(0x76, 16) - { - Stream.Write((short)p.X); - Stream.Write((short)p.Y); - Stream.Write((short)p.Z); - Stream.Write((byte)0); - Stream.Write((short)0); - Stream.Write((short)0); - Stream.Write((short)map.Width); - Stream.Write((short)map.Height); - } -} - -public sealed class SkillUpdate : Packet -{ - public SkillUpdate(Skills skills) : base(0x3A) - { - EnsureCapacity(6 + skills.Length * 9); - - Stream.Write((byte)0x02); // type: absolute, capped - - for (var i = 0; i < skills.Length; ++i) + public static ChangeUpdateRange Instantiate(int range) { - var s = skills[i]; - - var v = s.NonRacialValue; - var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF); - - Stream.Write((ushort)(s.Info.SkillID + 1)); - Stream.Write((ushort)uv); - Stream.Write((ushort)s.BaseFixedPoint); - Stream.Write((byte)s.Lock); - Stream.Write((ushort)s.CapFixedPoint); - } - - Stream.Write((short)0); // terminate - } -} - -public sealed class Sequence : Packet -{ - public Sequence(int num) : base(0x7B, 2) - { - Stream.Write((byte)num); - } -} - -public sealed class SkillChange : Packet -{ - public SkillChange(Skill skill) : base(0x3A) - { - EnsureCapacity(13); - - var v = skill.NonRacialValue; - var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF); - - Stream.Write((byte)0xDF); // type: delta, capped - Stream.Write((ushort)skill.Info.SkillID); - Stream.Write((ushort)uv); - Stream.Write((ushort)skill.BaseFixedPoint); - Stream.Write((byte)skill.Lock); - Stream.Write((ushort)skill.CapFixedPoint); - } -} - -public sealed class LaunchBrowser : Packet -{ - public LaunchBrowser(string url) : base(0xA5) - { - url ??= ""; - - EnsureCapacity(4 + url.Length); - - Stream.WriteAsciiNull(url); - } -} - -public sealed class DragEffect : Packet -{ - public DragEffect(IEntity src, IEntity trg, int itemID, int hue, int amount) : base(0x23, 26) - { - Stream.Write((short)itemID); - Stream.Write((byte)0); - Stream.Write((short)hue); - Stream.Write((short)amount); - Stream.Write(src.Serial); - Stream.Write((short)src.X); - Stream.Write((short)src.Y); - Stream.Write((sbyte)src.Z); - Stream.Write(trg.Serial); - Stream.Write((short)trg.X); - Stream.Write((short)trg.Y); - Stream.Write((sbyte)trg.Z); - } -} - -public sealed class SeasonChange : Packet -{ - private static readonly SeasonChange[][] m_Cache = - { - new SeasonChange[2], - new SeasonChange[2], - new SeasonChange[2], - new SeasonChange[2], - new SeasonChange[2] - }; - - public SeasonChange(int season, bool playSound = true) : base(0xBC, 3) - { - Stream.Write((byte)season); - Stream.Write(playSound); - } - - public static SeasonChange Instantiate(int season) => Instantiate(season, true); - - public static SeasonChange Instantiate(int season, bool playSound) - { - if (season >= 0 && season < m_Cache.Length) - { - var idx = playSound ? 1 : 0; - - var p = m_Cache[season][idx]; + var idx = (byte)range; + var p = m_Cache[idx]; if (p == null) { - m_Cache[season][idx] = p = new SeasonChange(season, playSound); + m_Cache[idx] = p = new ChangeUpdateRange(range); p.SetStatic(); } return p; } - - return new SeasonChange(season, playSound); - } -} - -public sealed class DisplayPaperdoll : Packet -{ - public DisplayPaperdoll(Serial m, string title, bool warmode, bool canLift) : base(0x88, 66) - { - byte flags = 0x00; - - if (warmode) - { - flags |= 0x01; - } - - if (canLift) - { - flags |= 0x02; - } - - Stream.Write(m); - Stream.WriteAsciiFixed(title, 60); - Stream.Write(flags); - } -} - -public sealed class PlayMusic : Packet -{ - public static readonly Packet InvalidInstance = SetStatic(new PlayMusic(MusicName.Invalid)); - - private static readonly Packet[] m_Instances = new Packet[60]; - - public PlayMusic(MusicName name) : base(0x6D, 3) - { - Stream.Write((short)name); } - public static Packet GetInstance(MusicName name) + public sealed class DeathStatus : Packet { - if (name == MusicName.Invalid) + public static readonly Packet Dead = SetStatic(new DeathStatus(true)); + public static readonly Packet Alive = SetStatic(new DeathStatus(false)); + + public DeathStatus(bool dead) : base(0x2C, 2) { - return InvalidInstance; + Stream.Write((byte)(dead ? 0 : 2)); } - var v = (int)name; - Packet p; + public static Packet Instantiate(bool dead) => dead ? Dead : Alive; + } - if (v >= 0 && v < m_Instances.Length) + public sealed class DisplayProfile : Packet + { + public DisplayProfile(Serial m, string header, string body, string footer) : base(0xB8) { - p = m_Instances[v]; + header ??= ""; + body ??= ""; + footer ??= ""; + + EnsureCapacity(12 + header.Length + footer.Length * 2 + body.Length * 2); + + Stream.Write(m); + Stream.WriteAsciiNull(header); + Stream.WriteBigUniNull(footer); + Stream.WriteBigUniNull(body); + } + } + + public sealed class LiftRej : Packet + { + public LiftRej(LRReason reason) : base(0x27, 2) + { + Stream.Write((byte)reason); + } + } + + public sealed class LogoutAck : Packet + { + public LogoutAck() : base(0xD1, 2) + { + Stream.Write((byte)0x01); + } + } + + public sealed class Weather : Packet + { + public Weather(int type, int density, int temp) : base(0x65, 4) + { + Stream.Write((byte)type); + Stream.Write((byte)density); + Stream.Write((byte)temp); + } + } + + public sealed class ServerChange : Packet + { + public ServerChange(Point3D p, Map map) : base(0x76, 16) + { + Stream.Write((short)p.X); + Stream.Write((short)p.Y); + Stream.Write((short)p.Z); + Stream.Write((byte)0); + Stream.Write((short)0); + Stream.Write((short)0); + Stream.Write((short)map.Width); + Stream.Write((short)map.Height); + } + } + + public sealed class SkillUpdate : Packet + { + public SkillUpdate(Skills skills) : base(0x3A) + { + EnsureCapacity(6 + skills.Length * 9); + + Stream.Write((byte)0x02); // type: absolute, capped + + for (var i = 0; i < skills.Length; ++i) + { + var s = skills[i]; + + var v = s.NonRacialValue; + var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF); + + Stream.Write((ushort)(s.Info.SkillID + 1)); + Stream.Write((ushort)uv); + Stream.Write((ushort)s.BaseFixedPoint); + Stream.Write((byte)s.Lock); + Stream.Write((ushort)s.CapFixedPoint); + } + + Stream.Write((short)0); // terminate + } + } + + public sealed class Sequence : Packet + { + public Sequence(int num) : base(0x7B, 2) + { + Stream.Write((byte)num); + } + } + + public sealed class SkillChange : Packet + { + public SkillChange(Skill skill) : base(0x3A) + { + EnsureCapacity(13); + + var v = skill.NonRacialValue; + var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF); + + Stream.Write((byte)0xDF); // type: delta, capped + Stream.Write((ushort)skill.Info.SkillID); + Stream.Write((ushort)uv); + Stream.Write((ushort)skill.BaseFixedPoint); + Stream.Write((byte)skill.Lock); + Stream.Write((ushort)skill.CapFixedPoint); + } + } + + public sealed class LaunchBrowser : Packet + { + public LaunchBrowser(string url) : base(0xA5) + { + url ??= ""; + + EnsureCapacity(4 + url.Length); + + Stream.WriteAsciiNull(url); + } + } + + public sealed class DragEffect : Packet + { + public DragEffect(IEntity src, IEntity trg, int itemID, int hue, int amount) : base(0x23, 26) + { + Stream.Write((short)itemID); + Stream.Write((byte)0); + Stream.Write((short)hue); + Stream.Write((short)amount); + Stream.Write(src.Serial); + Stream.Write((short)src.X); + Stream.Write((short)src.Y); + Stream.Write((sbyte)src.Z); + Stream.Write(trg.Serial); + Stream.Write((short)trg.X); + Stream.Write((short)trg.Y); + Stream.Write((sbyte)trg.Z); + } + } + + public sealed class SeasonChange : Packet + { + private static readonly SeasonChange[][] m_Cache = + { + new SeasonChange[2], + new SeasonChange[2], + new SeasonChange[2], + new SeasonChange[2], + new SeasonChange[2] + }; + + public SeasonChange(int season, bool playSound = true) : base(0xBC, 3) + { + Stream.Write((byte)season); + Stream.Write(playSound); + } + + public static SeasonChange Instantiate(int season) => Instantiate(season, true); + + public static SeasonChange Instantiate(int season, bool playSound) + { + if (season >= 0 && season < m_Cache.Length) + { + var idx = playSound ? 1 : 0; + + var p = m_Cache[season][idx]; + + if (p == null) + { + m_Cache[season][idx] = p = new SeasonChange(season, playSound); + p.SetStatic(); + } + + return p; + } + + return new SeasonChange(season, playSound); + } + } + + public sealed class DisplayPaperdoll : Packet + { + public DisplayPaperdoll(Serial m, string title, bool warmode, bool canLift) : base(0x88, 66) + { + byte flags = 0x00; + + if (warmode) + { + flags |= 0x01; + } + + if (canLift) + { + flags |= 0x02; + } + + Stream.Write(m); + Stream.WriteAsciiFixed(title, 60); + Stream.Write(flags); + } + } + + public sealed class PlayMusic : Packet + { + public static readonly Packet InvalidInstance = SetStatic(new PlayMusic(MusicName.Invalid)); + + private static readonly Packet[] m_Instances = new Packet[60]; + + public PlayMusic(MusicName name) : base(0x6D, 3) + { + Stream.Write((short)name); + } + + public static Packet GetInstance(MusicName name) + { + if (name == MusicName.Invalid) + { + return InvalidInstance; + } + + var v = (int)name; + Packet p; + + if (v >= 0 && v < m_Instances.Length) + { + p = m_Instances[v]; + + if (p == null) + { + m_Instances[v] = p = SetStatic(new PlayMusic(name)); + } + } + else + { + p = new PlayMusic(name); + } + + return p; + } + } + + public sealed class ScrollMessage : Packet + { + public ScrollMessage(int type, int tip, string text) : base(0xA6) + { + text ??= ""; + + EnsureCapacity(10 + text.Length); + + Stream.Write((byte)type); + Stream.Write(tip); + Stream.Write((ushort)text.Length); + Stream.WriteAsciiFixed(text, text.Length); + } + } + + public sealed class CurrentTime : Packet + { + public CurrentTime() : this(DateTime.Now) + { + } + + public CurrentTime(DateTime date) : base(0x5B, 4) + { + Stream.Write((byte)date.Hour); + Stream.Write((byte)date.Minute); + Stream.Write((byte)date.Second); + } + } + + public sealed class PathfindMessage : Packet + { + public PathfindMessage(Point3D p) : base(0x38, 7) + { + Stream.Write((short)p.X); + Stream.Write((short)p.Y); + Stream.Write((short)p.Z); + } + } + + public sealed class PingAck : Packet + { + private static readonly PingAck[] m_Cache = new PingAck[0x100]; + + public PingAck(byte ping) : base(0x73, 2) + { + Stream.Write(ping); + } + + public static PingAck Instantiate(byte ping) + { + var p = m_Cache[ping]; if (p == null) { - m_Instances[v] = p = SetStatic(new PlayMusic(name)); + m_Cache[ping] = p = new PingAck(ping); + p.SetStatic(); } + + return p; } - else + } + + public sealed class DisplayHuePicker : Packet + { + public DisplayHuePicker(HuePicker huePicker) : base(0x95, 9) { - p = new PlayMusic(name); + Stream.Write(huePicker.Serial); + Stream.Write((short)0); + Stream.Write((short)huePicker.ItemID); } - - return p; - } -} - -public sealed class ScrollMessage : Packet -{ - public ScrollMessage(int type, int tip, string text) : base(0xA6) - { - text ??= ""; - - EnsureCapacity(10 + text.Length); - - Stream.Write((byte)type); - Stream.Write(tip); - Stream.Write((ushort)text.Length); - Stream.WriteAsciiFixed(text, text.Length); - } -} - -public sealed class CurrentTime : Packet -{ - public CurrentTime() : this(DateTime.Now) - { - } - - public CurrentTime(DateTime date) : base(0x5B, 4) - { - Stream.Write((byte)date.Hour); - Stream.Write((byte)date.Minute); - Stream.Write((byte)date.Second); - } -} - -public sealed class PathfindMessage : Packet -{ - public PathfindMessage(Point3D p) : base(0x38, 7) - { - Stream.Write((short)p.X); - Stream.Write((short)p.Y); - Stream.Write((short)p.Z); - } -} - -public sealed class PingAck : Packet -{ - private static readonly PingAck[] m_Cache = new PingAck[0x100]; - - public PingAck(byte ping) : base(0x73, 2) - { - Stream.Write(ping); - } - - public static PingAck Instantiate(byte ping) - { - var p = m_Cache[ping]; - - if (p == null) - { - m_Cache[ping] = p = new PingAck(ping); - p.SetStatic(); - } - - return p; - } -} - -public sealed class DisplayHuePicker : Packet -{ - public DisplayHuePicker(HuePicker huePicker) : base(0x95, 9) - { - Stream.Write(huePicker.Serial); - Stream.Write((short)0); - Stream.Write((short)huePicker.ItemID); } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/SecureTradePacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/SecureTradePacketTests.cs index 6c10b63e9..2519f5f7c 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/SecureTradePacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/SecureTradePacketTests.cs @@ -2,113 +2,113 @@ using Server.Items; using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class SecureTradePacketTests +namespace Server.Tests.Network { - [Theory] - [InlineData("short-name")] - [InlineData("this is a really long name that is more than 30 characters, probably")] - public void TestDisplaySecureTrade(string name) + public class SecureTradePacketTests : IClassFixture { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + [Theory] + [InlineData("short-name")] + [InlineData("this is a really long name that is more than 30 characters, probably")] + public void TestDisplaySecureTrade(string name) + { + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var firstCont = new Container(World.NewItem); - var secondCont = new Container(World.NewItem); + var firstCont = new Container(World.NewItem); + var secondCont = new Container(World.NewItem); - var expected = new DisplaySecureTrade(m, firstCont, secondCont, name).Compile(); + var expected = new DisplaySecureTrade(m, firstCont, secondCont, name).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDisplaySecureTrade(m, firstCont, secondCont, name); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplaySecureTrade(m, firstCont, secondCont, name); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestCloseSecureTrade() - { - var cont = new Container(World.NewItem); + [Fact] + public void TestCloseSecureTrade() + { + var cont = new Container(World.NewItem); - var expected = new CloseSecureTrade(cont).Compile(); + var expected = new CloseSecureTrade(cont).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendCloseSecureTrade(cont); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendCloseSecureTrade(cont); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory, InlineData(true, false), InlineData(false, true)] - // Update first - // Update second - public void TestUpdateSecureTrade(bool first, bool second) - { - var firstCont = new Container(World.NewItem); - var secondCont = new Container(World.NewItem); + [Theory, InlineData(true, false), InlineData(false, true)] + // Update first + // Update second + public void TestUpdateSecureTrade(bool first, bool second) + { + var firstCont = new Container(World.NewItem); + var secondCont = new Container(World.NewItem); - var cont = first ? firstCont : secondCont; - var expected = new UpdateSecureTrade(cont, first, second).Compile(); + var cont = first ? firstCont : secondCont; + var expected = new UpdateSecureTrade(cont, first, second).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendUpdateSecureTrade(cont, first, second); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendUpdateSecureTrade(cont, first, second); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Theory] - [InlineData(100000, 30, TradeFlag.UpdateGold)] - [InlineData(250000, 50000, TradeFlag.UpdateLedger)] - public void TestUpdateGoldSecureTrade(int gold, int plat, TradeFlag flag) - { - var cont = new Container(World.NewItem); - var expected = new UpdateSecureTrade(cont, flag, gold, plat).Compile(); + [Theory] + [InlineData(100000, 30, TradeFlag.UpdateGold)] + [InlineData(250000, 50000, TradeFlag.UpdateLedger)] + public void TestUpdateGoldSecureTrade(int gold, int plat, TradeFlag flag) + { + var cont = new Container(World.NewItem); + var expected = new UpdateSecureTrade(cont, flag, gold, plat).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendUpdateSecureTrade(cont, flag, gold, plat); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendUpdateSecureTrade(cont, flag, gold, plat); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestSecureTradeEquip() - { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + [Fact] + public void TestSecureTradeEquip() + { + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var cont = new Container(World.NewItem); - var itemInCont = new Item(World.NewItem) { Parent = cont }; + var cont = new Container(World.NewItem); + var itemInCont = new Item(World.NewItem) { Parent = cont }; - var expected = new SecureTradeEquip(itemInCont, m).Compile(); + var expected = new SecureTradeEquip(itemInCont, m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendSecureTradeEquip(itemInCont, m); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendSecureTradeEquip(itemInCont, m); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestSecureTradeEquip6017() - { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + [Fact] + public void TestSecureTradeEquip6017() + { + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var cont = new Container(World.NewItem); - var itemInCont = new Item(World.NewItem) { Parent = cont }; + var cont = new Container(World.NewItem); + var itemInCont = new Item(World.NewItem) { Parent = cont }; - var expected = new SecureTradeEquip6017(itemInCont, m).Compile(); + var expected = new SecureTradeEquip6017(itemInCont, m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges |= ProtocolChanges.ContainerGridLines; + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges |= ProtocolChanges.ContainerGridLines; - ns.SendSecureTradeEquip(itemInCont, m); + ns.SendSecureTradeEquip(itemInCont, m); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/SecureTradePackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/SecureTradePackets.cs index d5470d765..906bed30f 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/SecureTradePackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/SecureTradePackets.cs @@ -1,88 +1,89 @@ using Server.Items; -namespace Server.Network; - -public sealed class DisplaySecureTrade : Packet +namespace Server.Network { - public DisplaySecureTrade(Mobile them, Container first, Container second, string name) - : base(0x6F) + public sealed class DisplaySecureTrade : Packet { - name ??= ""; + public DisplaySecureTrade(Mobile them, Container first, Container second, string name) + : base(0x6F) + { + name ??= ""; - EnsureCapacity(17 + name.Length); + EnsureCapacity(17 + name.Length); - Stream.Write((byte)0); // Display - Stream.Write(them.Serial); - Stream.Write(first.Serial); - Stream.Write(second.Serial); - Stream.Write(true); + Stream.Write((byte)0); // Display + Stream.Write(them.Serial); + Stream.Write(first.Serial); + Stream.Write(second.Serial); + Stream.Write(true); - Stream.WriteAsciiFixed(name, 30); - } -} - -public sealed class CloseSecureTrade : Packet -{ - public CloseSecureTrade(Container cont) - : base(0x6F) - { - EnsureCapacity(17); - - Stream.Write((byte)1); // Close - Stream.Write(cont.Serial); - Stream.Write(0); - Stream.Write(0); - Stream.Write(false); - } -} - -public sealed class UpdateSecureTrade : Packet -{ - public UpdateSecureTrade(Container cont, bool first, bool second) - : this(cont, TradeFlag.Update, first ? 1 : 0, second ? 1 : 0) - { - } - - public UpdateSecureTrade(Container cont, TradeFlag flag, int first, int second) - : base(0x6F) - { - EnsureCapacity(17); - - Stream.Write((byte)flag); - Stream.Write(cont.Serial); - Stream.Write(first); - Stream.Write(second); - Stream.Write(false); - } -} - -public sealed class SecureTradeEquip : Packet -{ - public SecureTradeEquip(Item item, Mobile m) : base(0x25, 20) - { - Stream.Write(item.Serial); - Stream.Write((short)item.ItemID); - Stream.Write((byte)0); - Stream.Write((short)item.Amount); - Stream.Write((short)item.X); - Stream.Write((short)item.Y); - Stream.Write(m.Serial); - Stream.Write((short)item.Hue); - } -} - -public sealed class SecureTradeEquip6017 : Packet -{ - public SecureTradeEquip6017(Item item, Mobile m) : base(0x25, 21) - { - Stream.Write(item.Serial); - Stream.Write((short)item.ItemID); - Stream.Write((byte)0); - Stream.Write((short)item.Amount); - Stream.Write((short)item.X); - Stream.Write((short)item.Y); - Stream.Write((byte)0); // Grid Location? - Stream.Write(m.Serial); - Stream.Write((short)item.Hue); + Stream.WriteAsciiFixed(name, 30); + } + } + + public sealed class CloseSecureTrade : Packet + { + public CloseSecureTrade(Container cont) + : base(0x6F) + { + EnsureCapacity(17); + + Stream.Write((byte)1); // Close + Stream.Write(cont.Serial); + Stream.Write(0); + Stream.Write(0); + Stream.Write(false); + } + } + + public sealed class UpdateSecureTrade : Packet + { + public UpdateSecureTrade(Container cont, bool first, bool second) + : this(cont, TradeFlag.Update, first ? 1 : 0, second ? 1 : 0) + { + } + + public UpdateSecureTrade(Container cont, TradeFlag flag, int first, int second) + : base(0x6F) + { + EnsureCapacity(17); + + Stream.Write((byte)flag); + Stream.Write(cont.Serial); + Stream.Write(first); + Stream.Write(second); + Stream.Write(false); + } + } + + public sealed class SecureTradeEquip : Packet + { + public SecureTradeEquip(Item item, Mobile m) : base(0x25, 20) + { + Stream.Write(item.Serial); + Stream.Write((short)item.ItemID); + Stream.Write((byte)0); + Stream.Write((short)item.Amount); + Stream.Write((short)item.X); + Stream.Write((short)item.Y); + Stream.Write(m.Serial); + Stream.Write((short)item.Hue); + } + } + + public sealed class SecureTradeEquip6017 : Packet + { + public SecureTradeEquip6017(Item item, Mobile m) : base(0x25, 21) + { + Stream.Write(item.Serial); + Stream.Write((short)item.ItemID); + Stream.Write((byte)0); + Stream.Write((short)item.Amount); + Stream.Write((short)item.X); + Stream.Write((short)item.Y); + Stream.Write((byte)0); // Grid Location? + Stream.Write(m.Serial); + Stream.Write((short)item.Hue); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/TargetPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/TargetPackets.cs index 5d6110b43..e5dd5fc94 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/TargetPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/TargetPackets.cs @@ -1,66 +1,67 @@ using System.IO; using Server.Targeting; -namespace Server.Network; - -public sealed class MultiTargetReqHS : Packet +namespace Server.Network { - public MultiTargetReqHS(MultiTarget t) : base(0x99, 30) + public sealed class MultiTargetReqHS : Packet { - Stream.Write(t.AllowGround); - Stream.Write(t.TargetID); - Stream.Write((byte)t.Flags); + public MultiTargetReqHS(MultiTarget t) : base(0x99, 30) + { + Stream.Write(t.AllowGround); + Stream.Write(t.TargetID); + Stream.Write((byte)t.Flags); - Stream.Fill(); + Stream.Fill(); - Stream.Seek(18, SeekOrigin.Begin); - Stream.Write((short)t.MultiID); - Stream.Write((short)t.Offset.X); - Stream.Write((short)t.Offset.Y); - Stream.Write((short)t.Offset.Z); + Stream.Seek(18, SeekOrigin.Begin); + Stream.Write((short)t.MultiID); + Stream.Write((short)t.Offset.X); + Stream.Write((short)t.Offset.Y); + Stream.Write((short)t.Offset.Z); - // DWORD Hue - } -} - -public sealed class MultiTargetReq : Packet -{ - public MultiTargetReq(MultiTarget t) : base(0x99, 26) - { - Stream.Write(t.AllowGround); - Stream.Write(t.TargetID); - Stream.Write((byte)t.Flags); - - Stream.Fill(); - - Stream.Seek(18, SeekOrigin.Begin); - Stream.Write((short)t.MultiID); - Stream.Write((short)t.Offset.X); - Stream.Write((short)t.Offset.Y); - Stream.Write((short)t.Offset.Z); - } -} - -public sealed class CancelTarget : Packet -{ - public static readonly Packet Instance = SetStatic(new CancelTarget()); - - public CancelTarget() : base(0x6C, 19) - { - Stream.Write((byte)0); - Stream.Write(0); - Stream.Write((byte)3); - Stream.Fill(); - } -} - -public sealed class TargetReq : Packet -{ - public TargetReq(Target t) : base(0x6C, 19) - { - Stream.Write(t.AllowGround); - Stream.Write(t.TargetID); - Stream.Write((byte)t.Flags); - Stream.Fill(); + // DWORD Hue + } + } + + public sealed class MultiTargetReq : Packet + { + public MultiTargetReq(MultiTarget t) : base(0x99, 26) + { + Stream.Write(t.AllowGround); + Stream.Write(t.TargetID); + Stream.Write((byte)t.Flags); + + Stream.Fill(); + + Stream.Seek(18, SeekOrigin.Begin); + Stream.Write((short)t.MultiID); + Stream.Write((short)t.Offset.X); + Stream.Write((short)t.Offset.Y); + Stream.Write((short)t.Offset.Z); + } + } + + public sealed class CancelTarget : Packet + { + public static readonly Packet Instance = SetStatic(new CancelTarget()); + + public CancelTarget() : base(0x6C, 19) + { + Stream.Write((byte)0); + Stream.Write(0); + Stream.Write((byte)3); + Stream.Fill(); + } + } + + public sealed class TargetReq : Packet + { + public TargetReq(Target t) : base(0x6C, 19) + { + Stream.Write(t.AllowGround); + Stream.Write(t.TargetID); + Stream.Write((byte)t.Flags); + Stream.Fill(); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/TargetPacketsTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/TargetPacketsTests.cs index c2d4d29fc..56fd26021 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/TargetPacketsTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/TargetPacketsTests.cs @@ -2,89 +2,89 @@ using Server.Network; using Server.Targeting; using Xunit; -namespace Server.Tests.Network; - -public class TestMultiTarget : MultiTarget +namespace Server.Tests.Network { - public TestMultiTarget( - int multiID, - Point3D offset, - int range = 10, - bool allowGround = true, - TargetFlags flags = TargetFlags.None - ) : base(multiID, offset, range, allowGround, flags) + public class TestMultiTarget : MultiTarget { - } -} - -public class TestTarget : Target -{ - public TestTarget( - int range, - bool allowGround, - TargetFlags flags - ) : base(range, allowGround, flags) - { - } -} - -[Collection("Sequential Server Tests")] -public class TargetPacketsTests -{ - [Fact] - public void TestMultiTargetReqHS() - { - var multiID = 0x1024; - var p = new Point3D(1000, 100, 10); - MultiTarget t = new TestMultiTarget(multiID, p); - - var expected = new MultiTargetReqHS(t).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges |= ProtocolChanges.HighSeas; - ns.SendMultiTargetReq(t); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestMultiTargetReq() - { - var multiID = 0x1024; - var p = new Point3D(1000, 100, 10); - MultiTarget t = new TestMultiTarget(multiID, p); - - var expected = new MultiTargetReq(t).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMultiTargetReq(t); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestCancelTarget() - { - var expected = new CancelTarget().Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendCancelTarget(); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestTargetReq() - { - var t = new TestTarget(10, true, TargetFlags.Beneficial); - var expected = new TargetReq(t).Compile(); - - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendTargetReq(t); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + public TestMultiTarget( + int multiID, + Point3D offset, + int range = 10, + bool allowGround = true, + TargetFlags flags = TargetFlags.None + ) : base(multiID, offset, range, allowGround, flags) + { + } + } + + public class TestTarget : Target + { + public TestTarget( + int range, + bool allowGround, + TargetFlags flags + ) : base(range, allowGround, flags) + { + } + } + + public class TargetPacketsTests + { + [Fact] + public void TestMultiTargetReqHS() + { + var multiID = 0x1024; + var p = new Point3D(1000, 100, 10); + MultiTarget t = new TestMultiTarget(multiID, p); + + var expected = new MultiTargetReqHS(t).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges |= ProtocolChanges.HighSeas; + ns.SendMultiTargetReq(t); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + [Fact] + public void TestMultiTargetReq() + { + var multiID = 0x1024; + var p = new Point3D(1000, 100, 10); + MultiTarget t = new TestMultiTarget(multiID, p); + + var expected = new MultiTargetReq(t).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMultiTargetReq(t); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + [Fact] + public void TestCancelTarget() + { + var expected = new CancelTarget().Compile(); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendCancelTarget(); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + [Fact] + public void TestTargetReq() + { + var t = new TestTarget(10, true, TargetFlags.Beneficial); + var expected = new TargetReq(t).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendTargetReq(t); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorBuyPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorBuyPacketTests.cs index 6262cc243..0a353169d 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorBuyPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorBuyPacketTests.cs @@ -3,91 +3,92 @@ using Server.Items; using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class VendorBuyPacketTests +namespace Server.Tests.Network { - [Theory] - [InlineData(ProtocolChanges.None)] - [InlineData(ProtocolChanges.ContainerGridLines)] - public void TestVendorBuyContent(ProtocolChanges protocolChanges) + [Collection("Sequential Tests")] + public class VendorBuyPacketTests : IClassFixture { - var cont = new Container(World.NewItem); - - var buyStates = new List + [Theory] + [InlineData(ProtocolChanges.None)] + [InlineData(ProtocolChanges.ContainerGridLines)] + public void TestVendorBuyContent(ProtocolChanges protocolChanges) { - new("First Item", cont.Serial, World.NewItem, 10, 1, 0x01, 0), - new("Second Item", cont.Serial, World.NewItem, 20, 2, 0x0A, 0), - new("Third Item", cont.Serial, World.NewItem, 30, 10, 0x0F, 0) - }; + var cont = new Container(World.NewItem); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = protocolChanges; + var buyStates = new List + { + new("First Item", cont.Serial, World.NewItem, 10, 1, 0x01, 0), + new("Second Item", cont.Serial, World.NewItem, 20, 2, 0x0A, 0), + new("Third Item", cont.Serial, World.NewItem, 30, 10, 0x0F, 0) + }; - var expected = new VendorBuyContent(buyStates, ns.ContainerGridLines).Compile(); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = protocolChanges; - ns.SendVendorBuyContent(buyStates); + var expected = new VendorBuyContent(buyStates, ns.ContainerGridLines).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + ns.SendVendorBuyContent(buyStates); - [Theory] - [InlineData(ProtocolChanges.None)] - [InlineData(ProtocolChanges.HighSeas)] - public void TestDisplayBuyList(ProtocolChanges protocolChanges) - { - var vendor = new Mobile((Serial)0x1); - vendor.DefaultMobileInit(); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = protocolChanges; - - var expected = new DisplayBuyList(vendor.Serial, ns.HighSeas).Compile(); - - ns.SendDisplayBuyList(vendor.Serial); - - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void TestVendorBuyList() - { - var vendor = new Mobile((Serial)0x1); - vendor.DefaultMobileInit(); - - var cont = new Container(World.NewItem); - - var buyStates = new List + [Theory] + [InlineData(ProtocolChanges.None)] + [InlineData(ProtocolChanges.HighSeas)] + public void TestDisplayBuyList(ProtocolChanges protocolChanges) { - new("First Item", cont.Serial, World.NewItem, 10, 1, 0x01, 0), - new("Second Item", cont.Serial, World.NewItem, 20, 2, 0x0A, 0), - new("Third Item", cont.Serial, World.NewItem, 30, 10, 0x0F, 0) - }; + var vendor = new Mobile((Serial)0x1); + vendor.DefaultMobileInit(); - var expected = new VendorBuyList(vendor, buyStates).Compile(); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = protocolChanges; - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendVendorBuyList(vendor, buyStates); + var expected = new DisplayBuyList(vendor.Serial, ns.HighSeas).Compile(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + ns.SendDisplayBuyList(vendor.Serial); - [Fact] - public void TestEndVendorBuy() - { - var vendor = new Mobile((Serial)0x1); - vendor.DefaultMobileInit(); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - var expected = new EndVendorBuy(vendor.Serial).Compile(); + [Fact] + public void TestVendorBuyList() + { + var vendor = new Mobile((Serial)0x1); + vendor.DefaultMobileInit(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendEndVendorBuy(vendor.Serial); + var cont = new Container(World.NewItem); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var buyStates = new List + { + new("First Item", cont.Serial, World.NewItem, 10, 1, 0x01, 0), + new("Second Item", cont.Serial, World.NewItem, 20, 2, 0x0A, 0), + new("Third Item", cont.Serial, World.NewItem, 30, 10, 0x0F, 0) + }; + + var expected = new VendorBuyList(vendor, buyStates).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendVendorBuyList(vendor, buyStates); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } + + [Fact] + public void TestEndVendorBuy() + { + var vendor = new Mobile((Serial)0x1); + vendor.DefaultMobileInit(); + + var expected = new EndVendorBuy(vendor.Serial).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendEndVendorBuy(vendor.Serial); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorBuyPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorBuyPackets.cs index 5a2716d2d..d7414ea4c 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorBuyPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorBuyPackets.cs @@ -1,79 +1,80 @@ using System.Collections.Generic; using Server.Items; -namespace Server.Network; - -public sealed class VendorBuyContent : Packet +namespace Server.Network { - public VendorBuyContent(List list, bool containerGridLines) : base(0x3C) + public sealed class VendorBuyContent : Packet { - EnsureCapacity(list.Count * 19 + 5); - - Stream.Write((short)list.Count); - - for (var i = list.Count - 1; i >= 0; --i) + public VendorBuyContent(List list, bool containerGridLines) : base(0x3C) { - var bis = list[i]; + EnsureCapacity(list.Count * 19 + 5); - Stream.Write(bis.MySerial); - Stream.Write((ushort)bis.ItemID); - Stream.Write((byte)0); // itemID offset - Stream.Write((ushort)bis.Amount); - Stream.Write((short)(i + 1)); // x - Stream.Write((short)1); // y - if (containerGridLines) + Stream.Write((short)list.Count); + + for (var i = list.Count - 1; i >= 0; --i) { - Stream.Write((byte)0); // Grid Location? + var bis = list[i]; + + Stream.Write(bis.MySerial); + Stream.Write((ushort)bis.ItemID); + Stream.Write((byte)0); // itemID offset + Stream.Write((ushort)bis.Amount); + Stream.Write((short)(i + 1)); // x + Stream.Write((short)1); // y + if (containerGridLines) + { + Stream.Write((byte)0); // Grid Location? + } + Stream.Write(bis.ContainerSerial); + Stream.Write((ushort)bis.Hue); } - Stream.Write(bis.ContainerSerial); - Stream.Write((ushort)bis.Hue); } } -} -public sealed class DisplayBuyList : Packet -{ - public DisplayBuyList(Serial vendor, bool highSeas) : base(0x24, highSeas ? 9 : 7) + public sealed class DisplayBuyList : Packet { - Stream.Write(vendor); - Stream.Write((short)0x30); // buy window id? - if (highSeas) + public DisplayBuyList(Serial vendor, bool highSeas) : base(0x24, highSeas ? 9 : 7) { - Stream.Write((short)0x00); + Stream.Write(vendor); + Stream.Write((short)0x30); // buy window id? + if (highSeas) + { + Stream.Write((short)0x00); + } } } -} -public sealed class VendorBuyList : Packet -{ - public VendorBuyList(Mobile vendor, List list) : base(0x74) + public sealed class VendorBuyList : Packet { - EnsureCapacity(256); - - Stream.Write(vendor.FindItemOnLayer(Layer.ShopBuy) is not Container buyPack ? Serial.MinusOne : buyPack.Serial); - - Stream.Write((byte)list.Count); - - for (var i = 0; i < list.Count; ++i) + public VendorBuyList(Mobile vendor, List list) : base(0x74) { - var bis = list[i]; + EnsureCapacity(256); - Stream.Write(bis.Price); + Stream.Write(vendor.FindItemOnLayer(Layer.ShopBuy) is not Container buyPack ? Serial.MinusOne : buyPack.Serial); - var desc = bis.Description ?? ""; + Stream.Write((byte)list.Count); - Stream.Write((byte)(desc.Length + 1)); - Stream.WriteAsciiNull(desc); + for (var i = 0; i < list.Count; ++i) + { + var bis = list[i]; + + Stream.Write(bis.Price); + + var desc = bis.Description ?? ""; + + Stream.Write((byte)(desc.Length + 1)); + Stream.WriteAsciiNull(desc); + } + } + } + + public sealed class EndVendorBuy : Packet + { + public EndVendorBuy(Serial vendor) : base(0x3B, 8) + { + Stream.Write((ushort)8); // length + Stream.Write(vendor); + Stream.Write((byte)0); } } } - -public sealed class EndVendorBuy : Packet -{ - public EndVendorBuy(Serial vendor) : base(0x3B, 8) - { - Stream.Write((ushort)8); // length - Stream.Write(vendor); - Stream.Write((byte)0); - } -} diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorSellPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorSellPacketTests.cs index 154223349..77685d9a9 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorSellPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorSellPacketTests.cs @@ -3,49 +3,50 @@ using System.Linq; using Server.Network; using Xunit; -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class VendorSellPacketTests +namespace Server.Tests.Network { - [Fact] - public void TestVendorSellList() + [Collection("Sequential Tests")] + public class VendorSellPacketTests : IClassFixture { - var vendor = new Mobile((Serial)0x1024u); - vendor.DefaultMobileInit(); - - var item1 = new Item(World.NewItem); - var item2 = new Item(World.NewItem) { Name = "Second Item" }; - var item3 = new Item(World.NewItem); - - var sellStates = new HashSet + [Fact] + public void TestVendorSellList() { - new(item1, 100, "Item 1"), - new(item2, 100000, "Item 2"), - new(item3, 1, "Item 3") - }; + var vendor = new Mobile((Serial)0x1024u); + vendor.DefaultMobileInit(); - var expected = new VendorSellList(vendor, sellStates.ToList()).Compile(); + var item1 = new Item(World.NewItem); + var item2 = new Item(World.NewItem) { Name = "Second Item" }; + var item3 = new Item(World.NewItem); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendVendorSellList(vendor.Serial, sellStates); + var sellStates = new HashSet + { + new(item1, 100, "Item 1"), + new(item2, 100000, "Item 2"), + new(item3, 1, "Item 3") + }; - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var expected = new VendorSellList(vendor, sellStates.ToList()).Compile(); - [Fact] - public void TestEndVendorSell() - { - var vendor = new Mobile((Serial)0x1024u); - vendor.DefaultMobileInit(); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendVendorSellList(vendor.Serial, sellStates); - var expected = new EndVendorBuy(vendor.Serial).Compile(); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendEndVendorSell(vendor.Serial); + [Fact] + public void TestEndVendorSell() + { + var vendor = new Mobile((Serial)0x1024u); + vendor.DefaultMobileInit(); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); + var expected = new EndVendorBuy(vendor.Serial).Compile(); + + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendEndVendorSell(vendor.Serial); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorSellPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorSellPackets.cs index 2354098bf..1ef29819a 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorSellPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VendorSellPackets.cs @@ -1,39 +1,55 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: VendorSellPackets.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + using System.Collections.Generic; -namespace Server.Network; - -public sealed class VendorSellList : Packet +namespace Server.Network { - public VendorSellList(Mobile shopkeeper, List sis) : base(0x9E) + public sealed class VendorSellList : Packet { - EnsureCapacity(256); - - Stream.Write(shopkeeper.Serial); - - Stream.Write((ushort)sis.Count); - - foreach (var state in sis) + public VendorSellList(Mobile shopkeeper, List sis) : base(0x9E) { - Stream.Write(state.Item.Serial); - Stream.Write((ushort)state.Item.ItemID); - Stream.Write((ushort)state.Item.Hue); - Stream.Write((ushort)state.Item.Amount); - Stream.Write((ushort)state.Price); + EnsureCapacity(256); - var name = string.IsNullOrWhiteSpace(state.Item.Name) ? state.Name ?? "" : state.Item.Name.Trim(); + Stream.Write(shopkeeper.Serial); - Stream.Write((ushort)name.Length); - Stream.WriteAsciiFixed(name, (ushort)name.Length); + Stream.Write((ushort)sis.Count); + + foreach (var state in sis) + { + Stream.Write(state.Item.Serial); + Stream.Write((ushort)state.Item.ItemID); + Stream.Write((ushort)state.Item.Hue); + Stream.Write((ushort)state.Item.Amount); + Stream.Write((ushort)state.Price); + + var name = string.IsNullOrWhiteSpace(state.Item.Name) ? state.Name ?? "" : state.Item.Name.Trim(); + + Stream.Write((ushort)name.Length); + Stream.WriteAsciiFixed(name, (ushort)name.Length); + } + } + } + + public sealed class EndVendorSell : Packet + { + public EndVendorSell(Mobile vendor) : base(0x3B, 8) + { + Stream.Write((ushort)8); // length + Stream.Write(vendor.Serial); + Stream.Write((byte)0); } } } - -public sealed class EndVendorSell : Packet -{ - public EndVendorSell(Mobile vendor) : base(0x3B, 8) - { - Stream.Write((ushort)8); // length - Stream.Write(vendor.Serial); - Stream.Write((byte)0); - } -} diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VirtualHairPacketTests.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VirtualHairPacketTests.cs index 3a105d1a4..13a6ac9c7 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VirtualHairPacketTests.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VirtualHairPacketTests.cs @@ -2,78 +2,40 @@ using Server.Network; using Server.Tests.Network; using Xunit; -namespace Server.Tests; - -[Collection("Sequential Server Tests")] -public class VirtualHairPacketTests +namespace Server.Tests { - [Fact] - public void TestSendVirtualHairUpdate() + public class VirtualHairPacketTests: IClassFixture { - var m = new Mobile((Serial)0x1024u); - m.DefaultMobileInit(); - m.HairHue = 0x1000; - m.HairItemID = 0x2000; + [Fact] + public void TestSendVirtualHairUpdate() + { + var m = new Mobile((Serial)0x1024u); + m.DefaultMobileInit(); + m.HairHue = 0x1000; + m.HairItemID = 0x2000; - var expected = new HairEquipUpdate(m).Compile(); + var expected = new HairEquipUpdate(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendHairEquipUpdatePacket(m, (uint)m.HairSerial, m.HairItemID, m.HairHue, Layer.Hair); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendHairEquipUpdatePacket(m, (uint)m.Hair.VirtualSerial, m.Hair.ItemId, m.Hair.Hue, Layer.Hair); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } - [Fact] - public void TestSendRemoveVirtualHair() - { - var m = new Mobile((Serial)0x1024u); - m.DefaultMobileInit(); + [Fact] + public void TestSendRemoveVirtualHair() + { + var m = new Mobile((Serial)0x1024u); + m.DefaultMobileInit(); - var expected = new RemoveHair(m).Compile(); + var expected = new RemoveHair(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendRemoveHairPacket((uint)m.HairSerial); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendRemoveHairPacket((uint) m.Hair.VirtualSerial); - var result = ns.SendBuffer.GetReadSpan(); - AssertThat.Equal(result, expected); - } - - [Fact] - public void RemoveHairUsesEquippedSerial() - { - var m = new Mobile((Serial)0x1025u); - m.DefaultMobileInit(); - m.HairItemID = 0x2000; - m.HairHue = 0x1000; - - var equippedSerial = m.HairSerial; - Assert.NotEqual(Serial.Zero, equippedSerial); - - m.HairItemID = 0; // remove - - // Serial must survive removal so the client can remove the correct entity. - Assert.Equal(equippedSerial, m.HairSerial); - // Hue is cleared with the item id (matches the legacy object-nulling behavior). - Assert.Equal(0, m.HairHue); - } - - [Fact] - public void RemoveFacialHairUsesEquippedSerial() - { - var m = new Mobile((Serial)0x1026u); - m.DefaultMobileInit(); - m.FacialHairItemID = 0x2040; - m.FacialHairHue = 0x1000; - - var equippedSerial = m.FacialHairSerial; - Assert.NotEqual(Serial.Zero, equippedSerial); - - m.FacialHairItemID = 0; // remove - - // Serial must survive removal so the client can remove the correct entity. - Assert.Equal(equippedSerial, m.FacialHairSerial); - // Hue is cleared with the item id (matches the legacy object-nulling behavior). - Assert.Equal(0, m.FacialHairHue); + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } } } diff --git a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VirtualHairPackets.cs b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VirtualHairPackets.cs index 135a6e698..a037f07bb 100644 --- a/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VirtualHairPackets.cs +++ b/Projects/Server.Tests/Tests/Network/Packets/Outgoing/VirtualHairPackets.cs @@ -1,51 +1,52 @@ -namespace Server.Network; - -public sealed class HairEquipUpdate : Packet +namespace Server.Network { - public HairEquipUpdate(Mobile parent) - : base(0x2E, 15) + public sealed class HairEquipUpdate : Packet { - var hue = parent.SolidHueOverride >= 0 ? parent.SolidHueOverride : parent.HairHue; + public HairEquipUpdate(Mobile parent) + : base(0x2E, 15) + { + var hue = parent.SolidHueOverride >= 0 ? parent.SolidHueOverride : parent.HairHue; - Stream.Write(parent.HairSerial); - Stream.Write((short)parent.HairItemID); - Stream.Write((byte)0); - Stream.Write((byte)Layer.Hair); - Stream.Write(parent.Serial); - Stream.Write((short)hue); - } -} - -public sealed class FacialHairEquipUpdate : Packet -{ - public FacialHairEquipUpdate(Mobile parent) - : base(0x2E, 15) - { - var hue = parent.SolidHueOverride >= 0 ? parent.SolidHueOverride : parent.FacialHairHue; - - Stream.Write(parent.FacialHairSerial); - Stream.Write((short)parent.FacialHairItemID); - Stream.Write((byte)0); - Stream.Write((byte)Layer.FacialHair); - Stream.Write(parent.Serial); - Stream.Write((short)hue); - } -} - -public sealed class RemoveHair : Packet -{ - public RemoveHair(Mobile parent) - : base(0x1D, 5) - { - Stream.Write(parent.HairSerial); - } -} - -public sealed class RemoveFacialHair : Packet -{ - public RemoveFacialHair(Mobile parent) - : base(0x1D, 5) - { - Stream.Write(parent.FacialHairSerial); + Stream.Write(parent.Hair.VirtualSerial); + Stream.Write((short)parent.HairItemID); + Stream.Write((byte)0); + Stream.Write((byte)Layer.Hair); + Stream.Write(parent.Serial); + Stream.Write((short)hue); + } + } + + public sealed class FacialHairEquipUpdate : Packet + { + public FacialHairEquipUpdate(Mobile parent) + : base(0x2E, 15) + { + var hue = parent.SolidHueOverride >= 0 ? parent.SolidHueOverride : parent.FacialHairHue; + + Stream.Write(parent.FacialHair.VirtualSerial); + Stream.Write((short)parent.FacialHairItemID); + Stream.Write((byte)0); + Stream.Write((byte)Layer.FacialHair); + Stream.Write(parent.Serial); + Stream.Write((short)hue); + } + } + + public sealed class RemoveHair : Packet + { + public RemoveHair(Mobile parent) + : base(0x1D, 5) + { + Stream.Write(parent.Hair.VirtualSerial); + } + } + + public sealed class RemoveFacialHair : Packet + { + public RemoveFacialHair(Mobile parent) + : base(0x1D, 5) + { + Stream.Write(parent.FacialHair.VirtualSerial); + } } } diff --git a/Projects/Server.Tests/Tests/Network/PipeTests.cs b/Projects/Server.Tests/Tests/Network/PipeTests.cs new file mode 100644 index 000000000..c32145589 --- /dev/null +++ b/Projects/Server.Tests/Tests/Network/PipeTests.cs @@ -0,0 +1,89 @@ +using System; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network; + +public class PipeTests +{ + [Fact] + public void TestSizeMatchesPageSize() + { + var pageSize = (uint)Environment.SystemPageSize; + using var pipe = new Pipe(128); + + // Available memory should be in increments of system page size minus one. + Assert.Equal(pageSize, pipe.Size); + Assert.Equal(pageSize - 1, (uint)pipe.Writer.AvailableToWrite().Length); + } + + [Fact] + public void TestWriteReadsWrap() + { + var pageSize = (uint)Environment.SystemPageSize; + using var pipe = new Pipe(pageSize); + + var span = pipe.Writer.AvailableToWrite(); + + for (var i = 0; i < span.Length; i++) + { + span[i] = (byte)(i % 256); + } + + pipe.Writer.Advance((uint)(span.Length - 10)); + + var readBytes = pipe.Reader.AvailableToRead(); + + // Make a sequence from what we expect. + Span seq = new byte[readBytes.Length]; + for (var i = 0; i < readBytes.Length; i++) + { + seq[i] = (byte)(i % 256); + } + + AssertThat.Equal(readBytes, seq); + + // Advance by half. Expected writer length should be half + 10 + pipe.Reader.Advance(pageSize / 2); + + span = pipe.Writer.AvailableToWrite(); + + var halfStart = pageSize / 2 + 10; + + Assert.Equal(halfStart, (uint)span.Length); + + seq = new byte[20]; + + for (var i = 0; i < 10; i++) + { + seq[i] = (byte)(0xF5 + i); + } + + // The last element, the sentinel, is excluded. + // The wrap around values start at 11 + for (var i = 11; i < 20; i++) + { + seq[i] = (byte)(i - 11); + } + + // Test the uncommitted overwritten memory and shifted offset to make sure the ring is working + AssertThat.Equal(span[..20], seq[..20]); + } + + [Fact] + public void TestWriteReadMatches() + { + var pipe = new Pipe(16); + + var reader = pipe.Reader; + var writer = pipe.Writer; + + for (uint i = 0; i < 16; i++) + { + writer.Advance(i); + + Assert.Equal((int)i, reader.AvailableToRead().Length); + reader.Advance(i); + } + } +} diff --git a/Projects/Server.Tests/Tests/Network/PollGroupTests.cs b/Projects/Server.Tests/Tests/Network/PollGroupTests.cs new file mode 100644 index 000000000..34d97e9a2 --- /dev/null +++ b/Projects/Server.Tests/Tests/Network/PollGroupTests.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network; + +public class PollGroupTests +{ + [Fact] + public void TestPollGroup() + { + // var group = new KQueuePollGroup(); + var nss = new NetState[2048]; + var handles = new IntPtr[2048]; + for (var i = 0; i < nss.Length; i++) + { + nss[i] = PacketTestUtilities.CreateTestNetState(); + handles[i] = (IntPtr)nss[i].Handle; + } + + GC.AddMemoryPressure(10000000000); + GC.Collect(); + GC.RemoveMemoryPressure(10000000000); + GC.Collect(); + + Thread.Sleep(1000); + + for (var i = 0; i < nss.Length; i++) + { + Assert.Equal(nss[i].Handle, (GCHandle)handles[i]); + } + + // group.Dispose(); + } +} diff --git a/Projects/Server.Tests/Tests/PropertyList/ObjectPropertyListReentrancyTests.cs b/Projects/Server.Tests/Tests/PropertyList/ObjectPropertyListReentrancyTests.cs deleted file mode 100644 index a30e9c9e3..000000000 --- a/Projects/Server.Tests/Tests/PropertyList/ObjectPropertyListReentrancyTests.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using Xunit; - -namespace Server.Tests; - -/// -/// The interpolation buffer is rented by the handler ctor and returned by the closing Add, so every -/// hole is evaluated while it is live. A Reset()/Dispose() landing in that window used to leave the -/// next Append* spanning a null array: ArgumentNullException, parameter "array". -/// -// Sequential: building a list rents from STArrayPool, which is not thread-safe. -[Collection("Sequential Server Tests")] -public class ObjectPropertyListReentrancyTests -{ - // Stands in for a property getter that invalidates while its own tooltip is being built. - private static string ResettingHole(ObjectPropertyList list, string value) - { - list.Reset(); - return value; - } - - private static string DisposingHole(ObjectPropertyList list, string value) - { - list.Dispose(); - return value; - } - - [Fact] - public void InterpolatedAdd_ResetMidHole_DoesNotThrow() - { - var opl = new ObjectPropertyList(null); - - var ex = Record.Exception( - () => opl.Add(1060776, $"{ResettingHole(opl, "Knight")}\t{"Council of Mages"}") - ); - - Assert.Null(ex); - } - - [Fact] - public void InterpolatedAdd_DisposeMidHole_DoesNotThrow() - { - var opl = new ObjectPropertyList(null); - - var ex = Record.Exception( - () => opl.Add(1060776, $"{DisposingHole(opl, "Knight")}\t{"Council of Mages"}") - ); - - Assert.Null(ex); - } -} - -/// -/// The guard is per-list, so nested builds (a GetProperties override that reads another entity's -/// PropertyList) cannot unguard the outer one the way a single shared slot would. -/// -[Collection("Sequential Server Tests")] -public class ObjectPropertyListNestedBuildTests -{ - [Fact] - public void NestedBuild_DoesNotUnguardTheOuterList() - { - var outer = new ObjectPropertyList(null); - var inner = new ObjectPropertyList(null); - - outer.IsBuilding = true; - inner.IsBuilding = true; // another entity starts building, and finishes - inner.IsBuilding = false; - - Assert.True(outer.IsBuilding); - } - - [Fact] - public void Reset_MidInterpolation_LeavesTheListUsable() - { - var opl = new ObjectPropertyList(null); - - opl.Add(1060776, $"{Reset(opl, "Knight")}\t{"Council of Mages"}"); - opl.Add(1042971, "still working"); - opl.Terminate(); - - Assert.NotNull(opl.Buffer); - } - - private static string Reset(ObjectPropertyList list, string value) - { - list.Reset(); - return value; - } -} - - -/// -/// Invalidating from inside GetProperties is a defect in the getter, not a case to recover from: -/// DEBUG throws, RELEASE keeps a possibly stale tooltip without crashing or leaking. -/// -[Collection("Sequential Server Tests")] -public class PropertyListInvalidationDuringBuildTests -{ - private class SelfInvalidatingMobile : Mobile - { - public int Builds; - - public override void GetProperties(IPropertyList list) - { - Builds++; - base.GetProperties(list); - InvalidateProperties(); - list.Add(1060776, $"{"Knight"}\t{"Council of Mages"}"); - } - } - - private static SelfInvalidatingMobile Place(int x) - { - var m = new SelfInvalidatingMobile(); - m.MoveToWorld(new Point3D(x, 1000, 0), Map.Felucca); - return m; - } - - [Fact] - public void InvalidatingFromGetProperties_FailsLoudlyWithoutTearingDownTheBuild() - { - var wasEnabled = ObjectPropertyList.Enabled; - ObjectPropertyList.Enabled = true; - - try - { - var m = Place(1000); - -#if DEBUG - Assert.Throws(() => _ = m.PropertyList); -#else - Assert.Null(Record.Exception(() => _ = m.PropertyList)); -#endif - - // Refused, not retried. - Assert.Equal(1, m.Builds); - m.Delete(); - } - finally - { - ObjectPropertyList.Enabled = wasEnabled; - } - } -} diff --git a/Projects/Server.Tests/Tests/PropertyList/ObjectPropertyListSpanAddTests.cs b/Projects/Server.Tests/Tests/PropertyList/ObjectPropertyListSpanAddTests.cs deleted file mode 100644 index 97e2d9f58..000000000 --- a/Projects/Server.Tests/Tests/PropertyList/ObjectPropertyListSpanAddTests.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.Text; -using Server; -using Xunit; - -namespace Server.Tests; - -public class ObjectPropertyListSpanAddTests -{ - // Decodes a terminated OPL buffer into (cliloc, argument) entries. - // The OPL packet is big-endian (SpanWriter default), so use BinaryPrimitives. - private static (int cliloc, string arg)[] Decode(ObjectPropertyList opl) - { - opl.Terminate(); - var buffer = opl.Buffer; - var entries = new List<(int, string)>(); - var pos = 15; // header is 15 bytes - while (true) - { - var cliloc = BinaryPrimitives.ReadInt32BigEndian(buffer.AsSpan(pos)); - pos += 4; - if (cliloc == 0) - { - break; - } - - var byteLen = BinaryPrimitives.ReadUInt16BigEndian(buffer.AsSpan(pos)); - pos += 2; - var arg = Encoding.Unicode.GetString(buffer, pos, byteLen); - pos += byteLen; - entries.Add((cliloc, arg)); - } - - return entries.ToArray(); - } - - [Fact] - public void SpanAdd_ProducesSameBytesAsStringArgument() - { - // Add(int, string) routes through the interpolation InternalAdd; Add(int, ReadOnlySpan) - // through the span InternalAdd. Both must produce identical bytes and hash. - var fromString = new ObjectPropertyList(null); - fromString.Add(1070722, "Hello World"); - - var fromSpan = new ObjectPropertyList(null); - fromSpan.Add(1070722, "Hello World".AsSpan()); - - Assert.Equal(Decode(fromString), Decode(fromSpan)); - Assert.Equal(fromString.Hash, fromSpan.Hash); - } - - [Fact] - public void SpanAdd_WithNumber_EmitsClilocAndArgument() - { - var opl = new ObjectPropertyList(null); - opl.Add(1070722, "Custom".AsSpan()); - - var entries = Decode(opl); - Assert.Single(entries); - Assert.Equal((1070722, "Custom"), entries[0]); - } - - [Fact] - public void HashFormat_OnlyMarksIntegers() - { - // Integer {value:#} emits the cliloc marker "#". - var intList = new ObjectPropertyList(null); - intList.Add(1062028, $"{1043009:#}"); - Assert.Equal((1062028, "#1043009"), Decode(intList)[0]); - - // Float {value:#} is the standard '#' custom-numeric (digit-placeholder) format, not a cliloc - // marker -- so no leading '#'. - var dblList = new ObjectPropertyList(null); - dblList.Add(1062028, $"{42.0:#}"); - Assert.Equal((1062028, 42.0.ToString("#")), Decode(dblList)[0]); // "42" - } - - [Fact] - public void Add_TruncatesArgumentOverMaxLength() - { - var oversized = new string('x', ObjectPropertyList.MaxArgumentLength + 50); - - var opl = new ObjectPropertyList(null); - opl.Add(1070722, oversized.AsSpan()); - - var entries = Decode(opl); - Assert.Single(entries); - Assert.Equal(ObjectPropertyList.MaxArgumentLength, entries[0].arg.Length); - } - - [Fact] - public void AddChunked_SplitsAtNewlinesSoNoEntryExceedsCap() - { - // 10 lines x 100 chars joined by '\n' (~1009 chars), well over the cap. - var lines = new string[10]; - for (var i = 0; i < lines.Length; i++) - { - lines[i] = new string((char)('a' + i), 100); - } - - var text = string.Join("\n", lines); - - var opl = new ObjectPropertyList(null); - opl.AddChunked(text.AsSpan()); - - var entries = Decode(opl); - Assert.True(entries.Length > 1, "expected multiple chunks"); - foreach (var (_, arg) in entries) - { - Assert.True(arg.Length <= ObjectPropertyList.MaxArgumentLength); - } - - // AddChunked breaks only at '\n' (dropping that '\n'), so rejoining with '\n' is lossless. - var rejoined = string.Join("\n", Array.ConvertAll(entries, e => e.arg)); - Assert.Equal(text, rejoined); - } -} diff --git a/Projects/Server.Tests/Tests/PropertyList/OplTextBlockTests.cs b/Projects/Server.Tests/Tests/PropertyList/OplTextBlockTests.cs deleted file mode 100644 index 71dd163d8..000000000 --- a/Projects/Server.Tests/Tests/PropertyList/OplTextBlockTests.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.Text; -using Server; -using Xunit; - -namespace Server.Tests; - -public class OplTextBlockTests -{ - private static (int cliloc, string arg)[] Decode(ObjectPropertyList opl) - { - opl.Terminate(); - var buffer = opl.Buffer; - var entries = new List<(int, string)>(); - var pos = 15; - while (true) - { - var cliloc = BinaryPrimitives.ReadInt32BigEndian(buffer.AsSpan(pos)); - pos += 4; - if (cliloc == 0) - { - break; - } - - var byteLen = BinaryPrimitives.ReadUInt16BigEndian(buffer.AsSpan(pos)); - pos += 2; - entries.Add((cliloc, Encoding.Unicode.GetString(buffer, pos, byteLen))); - pos += byteLen; - } - - return entries.ToArray(); - } - - [Fact] - public void MultipleLines_JoinIntoSingleCyclingEntry() - { - var opl = new ObjectPropertyList(null); - using (var block = opl.TextBlock()) - { - block.Add("Line One".AsSpan()); - block.Add("Line Two".AsSpan()); - } - - var entries = Decode(opl); - Assert.Single(entries); - Assert.Equal((1042971, "Line One\nLine Two"), entries[0]); // first cycling cliloc - } - - [Fact] - public void EmptyLines_AreSkipped() - { - var opl = new ObjectPropertyList(null); - using (var block = opl.TextBlock()) - { - block.Add("Only".AsSpan()); - block.Add(ReadOnlySpan.Empty); - } - - Assert.Equal((1042971, "Only"), Decode(opl)[0]); - } - - [Fact] - public void NoLines_EmitsNothing() - { - var opl = new ObjectPropertyList(null); - using (var block = opl.TextBlock()) - { - // add nothing - } - - Assert.Empty(Decode(opl)); - } - - [Fact] - public void Interpolated_AppendsZeroAlloc() - { - var opl = new ObjectPropertyList(null); - var v = 5; - using (var block = opl.TextBlock()) - { - block.Add($"Luck Bonus: +{v}%"); - } - - Assert.Equal((1042971, "Luck Bonus: +5%"), Decode(opl)[0]); - } - - [Fact] - public void LongContent_SplitsIntoMultipleEntriesUnderCap() - { - var opl = new ObjectPropertyList(null); - using (var block = opl.TextBlock()) - { - for (var i = 0; i < 10; i++) - { - block.Add(new string((char)('a' + i), 100).AsSpan()); - } - } - - var entries = Decode(opl); - Assert.True(entries.Length > 1, "expected the long block to chunk into multiple entries"); - foreach (var (_, arg) in entries) - { - Assert.True(arg.Length <= ObjectPropertyList.MaxArgumentLength); - } - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/AnchoredItemSerializationTests.cs b/Projects/Server.Tests/Tests/Serialization/AnchoredItemSerializationTests.cs deleted file mode 100644 index 7889c57c8..000000000 --- a/Projects/Server.Tests/Tests/Serialization/AnchoredItemSerializationTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using Xunit; - -namespace Server.Tests; - -[Collection("Sequential Server Tests")] -public class AnchoredItemSerializationTests -{ - private static byte[] SerializeItem(Item item) - { - var writer = new BufferWriter(new byte[256], true); - item.Serialize(writer); - return writer.Buffer[..(int)writer.Position]; - } - - /// - /// Item v11 stores LastMoved and DecayResetTime as anchored time: the serialized bytes - /// are a function of item state only, not of when the save runs. Pre-v11 stored - /// minutes-since-moved and delta time, which rewrote the bytes on every save. - /// - [Fact] - public void ItemBytes_AreStable_AcrossSavesAtDifferentTimes() - { - var start = Core._now; - - try - { - var item = new Item(0x1F13); - item.MoveToWorld(new Point3D(120, 100, 0), Map.Felucca); - item.RestartDecay(); - - var first = SerializeItem(item); - - // A save hours later, with no state change, must produce identical bytes. - Core._now = start + TimeSpan.FromHours(5); - var second = SerializeItem(item); - - Assert.Equal(first, second); - - item.Delete(); - } - finally - { - Core._now = start; - } - } - - /// - /// Pre-v11 LastMoved was stored at whole-minute precision relative to the save time and - /// could never round-trip exactly. Anchored storage is absolute and exact. - /// - [Fact] - public void LastMovedAndDecayReset_RoundTripExactly() - { - var item = new Item(0x1F13); - item.MoveToWorld(new Point3D(121, 100, 0), Map.Felucca); - - // Sub-minute precision that the old minutes encoding would have destroyed. - var moved = Core.Now - TimeSpan.FromSeconds(90.5) - TimeSpan.FromMilliseconds(123); - item.LastMoved = moved; - - item.RestartDecay(); - var decayReset = item.DecayResetTime; - Assert.NotEqual(default(DateTime), decayReset); - - var bytes = SerializeItem(item); - - var restored = new Item((Serial)0x7ffff123u); - restored.Deserialize(new BufferReader(bytes)); - - Assert.Equal(moved, restored.LastMoved); - Assert.Equal(decayReset, restored.DecayResetTime); - - item.Delete(); - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/AnchoredTimeTests.cs b/Projects/Server.Tests/Tests/Serialization/AnchoredTimeTests.cs deleted file mode 100644 index a99b9850c..000000000 --- a/Projects/Server.Tests/Tests/Serialization/AnchoredTimeTests.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Xunit; - -namespace Server.Tests; - -public class AnchoredTimeTests -{ - private static (BufferWriter Writer, Func Read) CreateRoundTrip() - { - var writer = new BufferWriter(new byte[64], true); - return (writer, shift => new BufferReader(writer.Buffer) { AnchoredTimeShift = shift }); - } - - [Fact] - public void AnchoredTime_RoundTripsExactly_WithZeroShift() - { - var (writer, read) = CreateRoundTrip(); - var value = new DateTime(2026, 8, 22, 12, 30, 0, DateTimeKind.Utc); - - writer.WriteAnchoredTime(value); - - Assert.Equal(value, read(TimeSpan.Zero).ReadAnchoredTime()); - } - - [Fact] - public void AnchoredTime_AppliesShiftOnRead() - { - var (writer, read) = CreateRoundTrip(); - var value = new DateTime(2026, 8, 22, 12, 30, 0, DateTimeKind.Utc); - var shift = TimeSpan.FromHours(3); - - writer.WriteAnchoredTime(value); - - Assert.Equal(value + shift, read(shift).ReadAnchoredTime()); - } - - [Fact] - public void AnchoredTime_SentinelsPassThroughUnshifted() - { - var (writer, read) = CreateRoundTrip(); - - writer.WriteAnchoredTime(DateTime.MinValue); - writer.WriteAnchoredTime(DateTime.MaxValue); - - var reader = read(TimeSpan.FromDays(2)); - Assert.Equal(DateTime.MinValue, reader.ReadAnchoredTime()); - Assert.Equal(DateTime.MaxValue, reader.ReadAnchoredTime()); - } - - [Fact] - public void AnchoredTime_SaturatesInsteadOfOverflowing() - { - var (writer, read) = CreateRoundTrip(); - - writer.WriteAnchoredTime(DateTime.MaxValue - TimeSpan.FromMinutes(1)); - - Assert.Equal(DateTime.MaxValue, read(TimeSpan.FromDays(1)).ReadAnchoredTime()); - } - - [Fact] - public void AnchoredTime_NormalizesLocalKindOnWrite() - { - var (writer, read) = CreateRoundTrip(); - var local = new DateTime(2026, 8, 22, 12, 30, 0, DateTimeKind.Local); - - writer.WriteAnchoredTime(local); - - Assert.Equal(local.ToUniversalTime(), read(TimeSpan.Zero).ReadAnchoredTime()); - } -} - -internal class AnchoredEntity : ISerializable -{ - public AnchoredEntity(Serial serial) => Serial = serial; - - public Serial Serial { get; } - public DateTime Created { get; set; } = DateTime.UtcNow; - public bool Deleted => false; - - public DateTime LastRested { get; set; } - - public void Delete() - { - } - - public void Serialize(IGenericWriter writer) => writer.WriteAnchoredTime(LastRested); - - public void Deserialize(IGenericReader reader) => LastRested = reader.ReadAnchoredTime(); -} - -[Collection("Sequential Server Tests")] -public class AnchoredTimePersistenceTests -{ - private class AnchoredPersistence : GenericEntityPersistence - { - public AnchoredPersistence(int priority) : base("AnchoredTrip", priority, 1, 0x7FFFFFFF) - { - } - } - - /// - /// The idx v5 header carries the save-start anchor; loading re-bases anchored timestamps - /// by the elapsed time since the save started, so downtime does not age them. - /// - [Fact] - public void SaveStartAnchor_RebasesAnchoredTimestampsAtLoad() - { - var previousAssemblies = AssemblyHandler.Assemblies; - AssemblyHandler.Assemblies = [.. previousAssemblies ?? [], typeof(AnchoredEntity).Assembly]; - - var source = new SerializationChunkSource(); - var workers = new SerializationThreadWorker[2]; - for (var i = 0; i < workers.Length; i++) - { - workers[i] = new SerializationThreadWorker(i, source); - workers[i].AllocateHeap(); - } - - var previousWorkers = World._threadWorkers; - World._threadWorkers = workers; - - var previousSaveStart = World.SaveStartTime; - - var persistence = new AnchoredPersistence(2100); - AnchoredPersistence loaded = null; - - var dir = Path.Combine(Path.GetTempPath(), $"muo-anchored-{Guid.NewGuid():N}"); - Directory.CreateDirectory(dir); - - try - { - var lastRested = Core.Now - TimeSpan.FromMinutes(10); - var serial = (Serial)1u; - persistence.EntitiesBySerial[serial] = new AnchoredEntity(serial) { LastRested = lastRested }; - persistence.RegisterType(typeof(AnchoredEntity)); - - // Pretend the save started two hours ago, as if the server had been down since. - var downtime = TimeSpan.FromHours(2); - World.SaveStartTime = Core.Now - downtime; - - foreach (var worker in workers) - { - worker.Wake(); - } - - source.SetOwner(persistence); - Assert.True(persistence.TrySnapshotEntries(out var slotCount)); - source.PushSlotRanges(persistence, slotCount); - - source.Flush(); - foreach (var worker in workers) - { - worker.Sleep(); - } - - persistence.WriteSnapshot(dir); - persistence.PostWorldSave(); - - loaded = new AnchoredPersistence(2101); - loaded.DeserializeIndexes(dir, null); - loaded.Deserialize(dir, null); - - var entity = loaded.EntitiesBySerial[serial]; - var expected = lastRested + downtime; - - Assert.True( - (entity.LastRested - expected).Duration() <= TimeSpan.FromSeconds(30), - $"Anchored timestamp must re-base by the downtime; expected ~{expected}, got {entity.LastRested}." - ); - } - finally - { - World.SaveStartTime = previousSaveStart; - persistence.Unregister(); - loaded?.Unregister(); - - foreach (var worker in workers) - { - worker.Exit(); - } - - World._threadWorkers = previousWorkers; - AssemblyHandler.Assemblies = previousAssemblies; - Directory.Delete(dir, true); - } - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/BufferWriterTests.cs b/Projects/Server.Tests/Tests/Serialization/BufferWriterTests.cs deleted file mode 100644 index bb6e5aa70..000000000 --- a/Projects/Server.Tests/Tests/Serialization/BufferWriterTests.cs +++ /dev/null @@ -1,310 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.IO; -using System.Linq; -using System.Text; -using Xunit; - -namespace Server.Tests; - -/// -/// Pins BufferWriter's byte-level output and position semantics so the write path can be -/// optimized without behavioral drift. -/// -public class BufferWriterTests -{ - [Fact] - public void PrimitivesAreLittleEndianAtExpectedOffsets() - { - var writer = new BufferWriter(new byte[256], true); - - writer.Write((byte)0xAB); - writer.Write((sbyte)-5); - writer.Write(true); - writer.Write(false); - writer.Write((short)-12345); - writer.Write((ushort)54321); - writer.Write(-123456789); - writer.Write(3123456789u); - writer.Write(-1234567890123456789L); - writer.Write(12345678901234567890UL); - writer.Write(1234.5678d); - writer.Write(56.75f); - writer.Write((Serial)0x40000001u); - - Assert.Equal(1 + 1 + 1 + 1 + 2 + 2 + 4 + 4 + 8 + 8 + 8 + 4 + 4, writer.Position); - - var b = writer.Buffer; - Assert.Equal(0xAB, b[0]); - Assert.Equal(unchecked((byte)-5), b[1]); - Assert.Equal(1, b[2]); - Assert.Equal(0, b[3]); - Assert.Equal(-12345, BinaryPrimitives.ReadInt16LittleEndian(b.AsSpan(4))); - Assert.Equal(54321, BinaryPrimitives.ReadUInt16LittleEndian(b.AsSpan(6))); - Assert.Equal(-123456789, BinaryPrimitives.ReadInt32LittleEndian(b.AsSpan(8))); - Assert.Equal(3123456789u, BinaryPrimitives.ReadUInt32LittleEndian(b.AsSpan(12))); - Assert.Equal(-1234567890123456789L, BinaryPrimitives.ReadInt64LittleEndian(b.AsSpan(16))); - Assert.Equal(12345678901234567890UL, BinaryPrimitives.ReadUInt64LittleEndian(b.AsSpan(24))); - Assert.Equal(1234.5678d, BinaryPrimitives.ReadDoubleLittleEndian(b.AsSpan(32))); - Assert.Equal(56.75f, BinaryPrimitives.ReadSingleLittleEndian(b.AsSpan(40))); - Assert.Equal(0x40000001u, BinaryPrimitives.ReadUInt32LittleEndian(b.AsSpan(44))); - } - - [Fact] - public void SeekEndUsesHighWaterMarkNotCurrentPosition() - { - var writer = new BufferWriter(new byte[256], true); - - writer.Write(1L); - writer.Write(2L); - writer.Write(3L); // high water = 24 - - writer.Seek(4, SeekOrigin.Begin); - writer.Write(99); // position now 8, high water still 24 - - Assert.Equal(24, writer.Seek(0, SeekOrigin.End)); - Assert.Equal(24, writer.Position); - } - - [Fact] - public void SeekCurrentAndBeginBehave() - { - var writer = new BufferWriter(new byte[64], true); - - writer.Write(0xDEADBEEF); - Assert.Equal(2, writer.Seek(2, SeekOrigin.Begin)); - Assert.Equal(3, writer.Seek(1, SeekOrigin.Current)); - - writer.Write((byte)0x77); - Assert.Equal(0x77, writer.Buffer[3]); - } - - [Fact] - public void GrowthPreservesContentAndPosition() - { - var writer = new BufferWriter(new byte[16], true); - - for (var i = 0; i < 100; i++) - { - writer.Write((long)i); - } - - Assert.Equal(800, writer.Position); - Assert.True(writer.Buffer.Length >= 800); - - for (var i = 0; i < 100; i++) - { - Assert.Equal(i, BinaryPrimitives.ReadInt64LittleEndian(writer.Buffer.AsSpan(i * 8))); - } - } - - [Fact] - public void SpanWriteCrossesGrowthBoundary() - { - var writer = new BufferWriter(new byte[8], true); - - Span payload = stackalloc byte[64]; - for (var i = 0; i < payload.Length; i++) - { - payload[i] = (byte)(i + 1); - } - - writer.Write((ushort)7); - writer.Write(payload); - - Assert.Equal(66, writer.Position); - Assert.Equal(payload.ToArray(), writer.Buffer.AsSpan(2, 64).ToArray()); - } - - [Theory] - [InlineData(0, new byte[] { 0x00 })] - [InlineData(127, new byte[] { 0x7F })] - [InlineData(128, new byte[] { 0x80, 0x01 })] - [InlineData(0x3FFF, new byte[] { 0xFF, 0x7F })] - [InlineData(0x4000, new byte[] { 0x80, 0x80, 0x01 })] - [InlineData(0x1F_FFFF, new byte[] { 0xFF, 0xFF, 0x7F })] - [InlineData(0x20_0000, new byte[] { 0x80, 0x80, 0x80, 0x01 })] - [InlineData(0xFFF_FFFF, new byte[] { 0xFF, 0xFF, 0xFF, 0x7F })] - [InlineData(0x1000_0000, new byte[] { 0x80, 0x80, 0x80, 0x80, 0x01 })] - [InlineData(int.MaxValue, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0x07 })] - [InlineData(-1, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0x0F })] - public void EncodedIntMatchesFormat(int value, byte[] expected) - { - var writer = new BufferWriter(new byte[16], true); - - ((IGenericWriter)writer).WriteEncodedInt(value); - - Assert.Equal(expected.Length, writer.Position); - Assert.Equal(expected, writer.Buffer.AsSpan(0, expected.Length).ToArray()); - } - - [Fact] - public void PrefixedStringsWriteFlagLengthAndUtf8() - { - var writer = new BufferWriter(new byte[256], true); - - writer.Write("héllo Ωorld"); - var utf8 = Encoding.UTF8.GetBytes("héllo Ωorld"); - - var b = writer.Buffer; - Assert.Equal(1, b[0]); // not-null flag - Assert.Equal(utf8.Length, b[1]); // encoded length (small string = 1 byte) - Assert.Equal(utf8, b.AsSpan(2, utf8.Length).ToArray()); - Assert.Equal(2 + utf8.Length, writer.Position); - - writer.Write((string)null); - Assert.Equal(0, b[2 + utf8.Length]); // null flag - } - - [Theory] - [InlineData(84)] // scratch path - [InlineData(85)] // scratch path boundary - [InlineData(86)] // two-pass path - [InlineData(300)] // two-pass, length prefix > 1 byte - public void StringPathsAgreeAcrossTheScratchBoundary(int chars) - { - // Mixed ASCII, 2-byte, 3-byte, and surrogate-pair (4-byte) content - var builder = new StringBuilder(chars); - for (var i = 0; builder.Length < chars; i++) - { - switch (i % 4) - { - case 0: - builder.Append('a'); - break; - case 1: - builder.Append('é'); - break; - case 2: - builder.Append('Ω'); - break; - default: - if (builder.Length + 2 <= chars) - { - builder.Append("𝔘"); // surrogate pair - } - else - { - builder.Append('z'); - } - break; - } - } - - var value = builder.ToString(); - Assert.Equal(chars, value.Length); - - var writer = new BufferWriter(new byte[16], true); // forces growth through both paths - writer.Write(value); - - var utf8 = Encoding.UTF8.GetBytes(value); - var b = writer.Buffer; - Assert.Equal(1, b[0]); - - // decode the 7-bit encoded length prefix - var offset = 1; - var length = 0; - var shift = 0; - byte current; - do - { - current = b[offset++]; - length |= (current & 0x7F) << shift; - shift += 7; - } while ((current & 0x80) != 0); - - Assert.Equal(utf8.Length, length); - Assert.Equal(utf8, b.AsSpan(offset, length).ToArray()); - Assert.Equal(offset + length, writer.Position); - } - - [Fact] - public void DateTimeWritesUtcTicksViaInterface() - { - var writer = new BufferWriter(new byte[64], true); - IGenericWriter iface = writer; - - var utc = new DateTime(2026, 7, 13, 1, 2, 3, DateTimeKind.Utc); - var local = utc.ToLocalTime(); - - iface.Write(utc); - iface.Write(local); // must convert to UTC - - Assert.Equal(utc.Ticks, BinaryPrimitives.ReadInt64LittleEndian(writer.Buffer.AsSpan(0))); - Assert.Equal(utc.Ticks, BinaryPrimitives.ReadInt64LittleEndian(writer.Buffer.AsSpan(8))); - } - - [Fact] - public void Point3DWritesThreeInts() - { - var writer = new BufferWriter(new byte[64], true); - IGenericWriter iface = writer; - - iface.Write(new Point3D(100, -200, 30)); - - Assert.Equal(12, writer.Position); - Assert.Equal(100, BinaryPrimitives.ReadInt32LittleEndian(writer.Buffer.AsSpan(0))); - Assert.Equal(-200, BinaryPrimitives.ReadInt32LittleEndian(writer.Buffer.AsSpan(4))); - Assert.Equal(30, BinaryPrimitives.ReadInt32LittleEndian(writer.Buffer.AsSpan(8))); - } - - [Fact] - public void DecimalRoundTripsThroughReader() - { - var writer = new BufferWriter(new byte[64], true); - writer.Write(1234567.89012m); - - IGenericReader reader = new BufferReader(writer.Buffer); - Assert.Equal(1234567.89012m, reader.ReadDecimal()); - } - - [Fact] - public void LongStringPrefixIsZeroPaddedToWorstCaseWidth() - { - // 100 ASCII chars: worst case 300 bytes -> 2-byte prefix; actual 100 bytes would - // canonically fit in 1. The prefix must be the non-minimal 2-byte form the readers - // decode identically: (100 | 0x80), 0x00. - var value = new string('a', 100); - var writer = new BufferWriter(new byte[1024], false); - writer.WriteRaw(value); - - var b = writer.Buffer; - Assert.Equal((byte)(100 | 0x80), b[0]); - Assert.Equal(0, b[1]); - Assert.Equal(2 + 100, writer.Position); - - IGenericReader reader = new BufferReader(writer.Buffer); - Assert.Equal(value, reader.ReadStringRaw()); - } - - [Theory] - [InlineData(42)] // canonical 1-byte prefix (3 * 42 < 0x80) - [InlineData(100)] // padded prefix - [InlineData(10_000)] // multi-byte prefix, forces growth from a small buffer - public void ThreeBytePerCharContentRoundTrips(int chars) - { - // CJK content encodes at the UTF-8 worst case of 3 bytes per char - the case an - // undersized scratch would truncate or throw on. - var value = new string('二', chars); - var writer = new BufferWriter(new byte[16], true); - writer.Write(value); - - Assert.Equal(Encoding.UTF8.GetByteCount(value), 3 * chars); - - IGenericReader reader = new BufferReader(writer.Buffer); - Assert.Equal(value, reader.ReadString()); - } - - [Fact] - public void SurrogatePairContentRoundTripsThroughRawPath() - { - // Surrogate pairs encode 2 chars into 4 bytes (2 bytes per char) - under the - // 3-bytes-per-char reservation, exercising written < maxLength with a padded prefix. - var value = string.Concat(Enumerable.Repeat("😀", 60)); // 120 chars, 240 bytes - var writer = new BufferWriter(new byte[16], false); - writer.WriteRaw(value); - - IGenericReader reader = new BufferReader(writer.Buffer); - Assert.Equal(value, reader.ReadStringRaw()); - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/DecimalSerializationTests.cs b/Projects/Server.Tests/Tests/Serialization/DecimalSerializationTests.cs index 5a25189d6..246307e74 100644 --- a/Projects/Server.Tests/Tests/Serialization/DecimalSerializationTests.cs +++ b/Projects/Server.Tests/Tests/Serialization/DecimalSerializationTests.cs @@ -35,7 +35,7 @@ public class DecimalSerializationTests public void TestSerializeDecimal(decimal value) { // Arrange - var buffer = new byte[sizeof(decimal)]; + byte[] buffer = new byte[sizeof(decimal)]; // Act Write(buffer, value); diff --git a/Projects/Server.Tests/Tests/Serialization/FileBufferWriterTests.cs b/Projects/Server.Tests/Tests/Serialization/FileBufferWriterTests.cs deleted file mode 100644 index fcbfb70a0..000000000 --- a/Projects/Server.Tests/Tests/Serialization/FileBufferWriterTests.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System; -using System.IO; -using System.Text; -using Xunit; - -namespace Server.Tests; - -public class FileBufferWriterTests -{ - private static string TempFile() => - Path.Combine(Path.GetTempPath(), $"muo-fbw-{Guid.NewGuid():N}.bin"); - - [Fact] - public void DrainsAcrossStagingBoundariesAndPatchesBackwards() - { - var path = TempFile(); - - try - { - // Tiny staging block so every few records cross a drain; mirrors the idx - // pattern: version, count placeholder, records, backwards count patch. - using (var writer = new FileBufferWriter(path, expectedSize: 64)) - { - writer.Write(3); // version - - var countPosition = writer.Position; - writer.Write(0); - - const int records = 1000; - for (var i = 0; i < records; i++) - { - writer.Write((ulong)i * 0x9E3779B97F4A7C15); - writer.Write(i); - } - - var end = writer.Position; - writer.Seek(countPosition, SeekOrigin.Begin); - writer.Write(records); - writer.Seek(0, SeekOrigin.End); - - Assert.Equal(end, writer.Position); - } - - var bytes = File.ReadAllBytes(path); - Assert.Equal(4 + 4 + 1000 * 12, bytes.Length); - - IGenericReader reader = new BufferReader(bytes); - Assert.Equal(3, reader.ReadInt()); - Assert.Equal(1000, reader.ReadInt()); - - for (var i = 0; i < 1000; i++) - { - Assert.Equal((ulong)i * 0x9E3779B97F4A7C15, reader.ReadULong()); - Assert.Equal(i, reader.ReadInt()); - } - } - finally - { - File.Delete(path); - } - } - - [Fact] - public void OversizedSingleItemGrowsTheStagingBlock() - { - var path = TempFile(); - - try - { - // A string whose worst-case reservation exceeds the staging block must grow - // the block instead of deadlocking the drain loop. - var value = new string('二', 500); // 1500 bytes utf8, staging 64 - - using (var writer = new FileBufferWriter(path, expectedSize: 64)) - { - writer.Write(value); - writer.Write(0xC0FFEE); - } - - IGenericReader reader = new BufferReader(File.ReadAllBytes(path)); - Assert.Equal(value, reader.ReadString()); - Assert.Equal(0xC0FFEE, reader.ReadInt()); - } - finally - { - File.Delete(path); - } - } - - [Fact] - public void SpanWritesCrossDrains() - { - var path = TempFile(); - - try - { - var payload = new byte[777]; - new System.Random(0x5EED).NextBytes(payload); - - using (var writer = new FileBufferWriter(path, expectedSize: 64)) - { - writer.Write((ReadOnlySpan)payload); - } - - Assert.Equal(payload, File.ReadAllBytes(path)); - } - finally - { - File.Delete(path); - } - } - - [Fact] - public void TypeWritesUseTagAndHashFormat() - { - var path = TempFile(); - - try - { - using (var writer = new FileBufferWriter(path)) - { - writer.Write(typeof(string)); - writer.Write((Type)null); - } - - var bytes = File.ReadAllBytes(path); - Assert.Equal(1 + 8 + 1, bytes.Length); // flag + hash + null flag - Assert.Equal(2, bytes[0]); - Assert.Equal(0, bytes[^1]); - } - finally - { - File.Delete(path); - } - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/GenericEntityPersistenceRoundTripTests.cs b/Projects/Server.Tests/Tests/Serialization/GenericEntityPersistenceRoundTripTests.cs deleted file mode 100644 index f2114a427..000000000 --- a/Projects/Server.Tests/Tests/Serialization/GenericEntityPersistenceRoundTripTests.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Xunit; - -namespace Server.Tests; - -internal class RoundTripEntity : ISerializable -{ - public RoundTripEntity(Serial serial) => Serial = serial; - - public Serial Serial { get; } - public DateTime Created { get; set; } = DateTime.UtcNow; - public bool Deleted => false; - - public int Value { get; set; } - public string Name { get; set; } - - public void Delete() - { - } - - public void Serialize(IGenericWriter writer) - { - writer.Write(Value); - writer.Write(Name); - } - - public void Deserialize(IGenericReader reader) - { - Value = reader.ReadInt(); - Name = reader.ReadString(); - } -} - -[Collection("Sequential Server Tests")] -public class GenericEntityPersistenceRoundTripTests -{ - private const uint SelfPayloadMarker = 0xDEADBEEF; - - private class RoundTripPersistence : GenericEntityPersistence - { - public bool SelfPayloadDeserialized { get; private set; } - - public RoundTripPersistence(int priority) : base("RoundTrip", priority, 1, 0x7FFFFFFF) - { - } - - public override void Serialize(IGenericWriter writer) => writer.Write(SelfPayloadMarker); - - public override void Deserialize(IGenericReader reader) - { - Assert.Equal(SelfPayloadMarker, reader.ReadUInt()); - SelfPayloadDeserialized = true; - } - } - - /// - /// Drives the real pipeline end to end: entities serialize on workers via slot-range - /// chunks (plus the persistence self-payload as a single), WriteSnapshot assembles the - /// idx/bin from the per-worker segment logs, and a fresh persistence loads it all back. - /// - [Fact] - public void SnapshotRoundTripsThroughWorkersAndSegmentLogs() - { - // The loader resolves types by hash through AssemblyHandler; make this test - // assembly visible for the duration of the test. - var previousAssemblies = AssemblyHandler.Assemblies; - AssemblyHandler.Assemblies = [.. previousAssemblies ?? [], typeof(RoundTripEntity).Assembly]; - - var source = new SerializationChunkSource(); - var workers = new SerializationThreadWorker[3]; - for (var i = 0; i < workers.Length; i++) - { - workers[i] = new SerializationThreadWorker(i, source); - workers[i].AllocateHeap(); - } - - var previousWorkers = World._threadWorkers; - World._threadWorkers = workers; - - var persistence = new RoundTripPersistence(2000); - RoundTripPersistence loaded = null; - - var dir = Path.Combine(Path.GetTempPath(), $"muo-roundtrip-{Guid.NewGuid():N}"); - Directory.CreateDirectory(dir); - - try - { - const int entityCount = 25_000; - var rng = new System.Random(0x5EED); - - for (var i = 1; i <= entityCount; i++) - { - var serial = (Serial)(uint)i; - persistence.EntitiesBySerial[serial] = new RoundTripEntity(serial) - { - Value = rng.Next(), - Name = i % 5 == 0 ? null : $"entity-{i}" - }; - } - - persistence.RegisterType(typeof(RoundTripEntity)); - - // Freeze: what Persistence.SerializeAll + GenericEntityPersistence.Serialize do, - // against this test's chunk source instead of the world's. - foreach (var worker in workers) - { - worker.Wake(); - } - - source.SetOwner(persistence); - source.PushSingle(persistence); - Assert.True(persistence.TrySnapshotEntries(out var slotCount)); - source.PushSlotRanges(persistence, slotCount); - - // Mirrors World.PauseSerializationThreads - source.Flush(); - foreach (var worker in workers) - { - worker.Sleep(); - } - - // Background write phase: snapshot from the segment logs. v4 embeds the type - // table in the idx, so no SerializedTypes.db is produced or needed. - persistence.WriteSnapshot(dir); - - persistence.PostWorldSave(); // releases the entries snapshot - - // Load into a fresh persistence, like a server boot would. - loaded = new RoundTripPersistence(2001); - loaded.DeserializeIndexes(dir, null); - loaded.Deserialize(dir, null); - - Assert.True(loaded.SelfPayloadDeserialized); - Assert.Equal(persistence.EntitiesBySerial.Count, loaded.EntitiesBySerial.Count); - - foreach (var (serial, original) in persistence.EntitiesBySerial) - { - var entity = loaded.EntitiesBySerial[serial]; - Assert.Equal(original.Value, entity.Value); - Assert.Equal(original.Name, entity.Name); - Assert.Equal(original.Created.Ticks, entity.Created.Ticks); - } - - // Loading must hydrate the type table so the next save can write indexes. - Assert.True(loaded.TryGetTypeIndex(typeof(RoundTripEntity), out _)); - } - finally - { - persistence.Unregister(); - loaded?.Unregister(); - - foreach (var worker in workers) - { - worker.Exit(); - } - - World._threadWorkers = previousWorkers; - AssemblyHandler.Assemblies = previousAssemblies; - Directory.Delete(dir, true); - } - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/GenericEntityPersistenceTypeTableTests.cs b/Projects/Server.Tests/Tests/Serialization/GenericEntityPersistenceTypeTableTests.cs deleted file mode 100644 index a4aceba36..000000000 --- a/Projects/Server.Tests/Tests/Serialization/GenericEntityPersistenceTypeTableTests.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Xunit; - -namespace Server.Tests; - -[Collection("Sequential Server Tests")] -public class GenericEntityPersistenceTypeTableTests -{ - private class TypeTablePersistence : GenericEntityPersistence - { - public TypeTablePersistence() : base("TypeTable", 3000, 1, 0x7FFFFFFF) - { - } - - public override void Serialize(IGenericWriter writer) - { - } - - public override void Deserialize(IGenericReader reader) - { - } - } - - [Fact] - public void RegisterTypeAssignsStableInsertionOrderedIndexes() - { - var persistence = new TypeTablePersistence(); - - try - { - persistence.RegisterType(typeof(RoundTripEntity)); - persistence.RegisterType(typeof(string)); - persistence.RegisterType(typeof(RoundTripEntity)); // duplicate is a no-op - - Assert.Equal(2, persistence.TypeTable.Count); - Assert.Same(typeof(RoundTripEntity), persistence.TypeTable[0]); - Assert.Same(typeof(string), persistence.TypeTable[1]); - - Assert.True(persistence.TryGetTypeIndex(typeof(RoundTripEntity), out var first)); - Assert.Equal(0, first); - Assert.True(persistence.TryGetTypeIndex(typeof(string), out var second)); - Assert.Equal(1, second); - Assert.False(persistence.TryGetTypeIndex(typeof(int), out _)); - } - finally - { - persistence.Unregister(); - } - } - - [Fact] - public void UnresolvedTableEntrySkipsItsRecordsAfterConfirmation() - { - var previousAssemblies = AssemblyHandler.Assemblies; - AssemblyHandler.Assemblies = [.. previousAssemblies ?? [], typeof(RoundTripEntity).Assembly]; - - var dir = Path.Combine(Path.GetTempPath(), $"muo-typetable-{Guid.NewGuid():N}"); - Directory.CreateDirectory(Path.Combine(dir, "TypeTable")); - - TypeTablePersistence loaded = null; - var previousIn = Console.In; - - try - { - // Hand-write a v4 idx: two table entries (one bogus), one record per type. - using (var idx = new FileBufferWriter(Path.Combine(dir, "TypeTable", "TypeTable.idx"))) - { - idx.Write(4); // version - idx.Write(2); // type table count - idx.WriteRaw(typeof(RoundTripEntity).FullName); // index 0: resolvable - idx.WriteRaw("Server.Tests.DoesNotExistAnymore"); // index 1: bogus - idx.Write(2); // record count - idx.Write((ushort)0); // record 1: real type - idx.Write(1u); // serial - idx.Write(DateTime.UtcNow.Ticks); - idx.Write(0L); // position - idx.Write(4); // length - idx.Write((ushort)1); // record 2: bogus type - idx.Write(2u); - idx.Write(DateTime.UtcNow.Ticks); - idx.Write(4L); - idx.Write(4); - } - - // GetConstructorFor prompts on the console; answer "y" (delete those types). - Console.SetIn(new StringReader("y\n")); - - loaded = new TypeTablePersistence(); - loaded.DeserializeIndexes(dir, null); - - Assert.Single(loaded.EntitiesBySerial); - Assert.True(loaded.EntitiesBySerial.ContainsKey((Serial)1u)); - Assert.True(loaded.TryGetTypeIndex(typeof(RoundTripEntity), out _)); - } - finally - { - Console.SetIn(previousIn); - loaded?.Unregister(); - AssemblyHandler.Assemblies = previousAssemblies; - Directory.Delete(dir, true); - } - } - - [Fact] - public void LegacyV3IndexesStillLoadAndHydrateTheTypeTable() - { - var previousAssemblies = AssemblyHandler.Assemblies; - AssemblyHandler.Assemblies = [.. previousAssemblies ?? [], typeof(RoundTripEntity).Assembly]; - - var dir = Path.Combine(Path.GetTempPath(), $"muo-legacyidx-{Guid.NewGuid():N}"); - Directory.CreateDirectory(Path.Combine(dir, "TypeTable")); - - TypeTablePersistence loaded = null; - - try - { - var hash = AssemblyHandler.GetTypeHash(typeof(RoundTripEntity)); - - // Hand-write a v3 idx: records carry flag + 8-byte hash, resolved via typesDb. - using (var idx = new FileBufferWriter(Path.Combine(dir, "TypeTable", "TypeTable.idx"))) - { - idx.Write(3); // version - idx.Write(1); // record count - idx.Write((byte)2); // xxHash3 flag - idx.Write(hash); - idx.Write(1u); // serial - idx.Write(DateTime.UtcNow.Ticks); - idx.Write(0L); // position - idx.Write(4); // length - } - - var typesDb = new Dictionary { [hash] = typeof(RoundTripEntity).FullName }; - - loaded = new TypeTablePersistence(); - loaded.DeserializeIndexes(dir, typesDb); - - Assert.Single(loaded.EntitiesBySerial); - Assert.True(loaded.EntitiesBySerial.ContainsKey((Serial)1u)); - - // Legacy loads must hydrate the table so the NEXT save can write v4 indexes. - Assert.True(loaded.TryGetTypeIndex(typeof(RoundTripEntity), out _)); - } - finally - { - loaded?.Unregister(); - AssemblyHandler.Assemblies = previousAssemblies; - Directory.Delete(dir, true); - } - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/SerializationChunkSourceTests.cs b/Projects/Server.Tests/Tests/Serialization/SerializationChunkSourceTests.cs deleted file mode 100644 index 69387f4e6..000000000 --- a/Projects/Server.Tests/Tests/Serialization/SerializationChunkSourceTests.cs +++ /dev/null @@ -1,312 +0,0 @@ -using System; -using System.Collections.Generic; -using Xunit; - -namespace Server.Tests; - -// Constructing a persistence mutates the static registry (an unsynchronized SortedSet); -// tests that do so must share the sequential collection. -[Collection("Sequential Server Tests")] -public class SerializationChunkSourceTests -{ - private class TestEntity : IGenericSerializable - { - public int PayloadSize { get; init; } = 16; - - public void Serialize(IGenericWriter writer) - { - for (var i = 0; i < PayloadSize; i++) - { - writer.Write((byte)(i & 0xFF)); - } - } - } - - private class TestPersistence : GenericPersistence - { - public int PayloadSize { get; init; } = 16; - - public TestPersistence() : base("ChunkSourceTest", 100) - { - } - - public (byte Thread, int Position, int Length) Placement => (_selfThread, _selfPosition, _selfLength); - - public override void Serialize(IGenericWriter writer) - { - for (var i = 0; i < PayloadSize; i++) - { - writer.Write((byte)(i & 0xFF)); - } - } - - public override void Deserialize(IGenericReader reader) - { - } - } - - private static List Drain(SerializationChunkSource source) - { - var drained = new List(); - while (source.TryTake(out var chunk)) - { - for (var i = 0; i < chunk.Count; i++) - { - drained.Add((TestEntity)chunk.Buffer[i]); - } - - source.Return(chunk.Buffer, chunk.Count); - } - - return drained; - } - - [Fact] - public void PartialChunkIsNotVisibleUntilFlush() - { - var source = new SerializationChunkSource(); - var entities = new List(); - - for (var i = 0; i < 100; i++) - { - var e = new TestEntity(); - entities.Add(e); - source.Push(e); - } - - Assert.False(source.TryTake(out _)); - - source.Flush(); - - var drained = Drain(source); - Assert.Equal(entities, drained); - - // Flush again should publish nothing - source.Flush(); - Assert.False(source.TryTake(out _)); - } - - [Fact] - public void FullChunkPublishesWithoutFlush() - { - var source = new SerializationChunkSource(); - - for (var i = 0; i < 4096; i++) - { - source.Push(new TestEntity()); - } - - Assert.True(source.TryTake(out var chunk)); - Assert.Null(chunk.Single); - Assert.Equal(4096, chunk.Count); - source.Return(chunk.Buffer, chunk.Count); - - // Nothing partial left behind - source.Flush(); - Assert.False(source.TryTake(out _)); - } - - [Fact] - public void PushSingleDoesNotDisturbPartialChunk() - { - var source = new SerializationChunkSource(); - var heavy = new TestPersistence(); - - try - { - var small1 = new TestEntity(); - var small2 = new TestEntity(); - - source.Push(small1); - source.PushSingle(heavy); // published immediately as a single, ahead of the partial chunk - source.Push(small2); - - Assert.True(source.TryTake(out var chunk)); - Assert.Same(heavy, chunk.Single); - Assert.Equal(1, chunk.Count); - - Assert.False(source.TryTake(out _)); - source.Flush(); - - var drained = Drain(source); - Assert.Equal([small1, small2], drained); - } - finally - { - heavy.Unregister(); - } - } - - [Fact] - public void SetOwnerPublishesPartialChunkOnChange() - { - var source = new SerializationChunkSource(); - var ownerA = new TestPersistence(); - var ownerB = new TestPersistence(); - - try - { - source.SetOwner(ownerA); - source.Push(new TestEntity()); - source.Push(new TestEntity()); - - // Same owner: partial chunk stays private to the producer. - source.SetOwner(ownerA); - Assert.False(source.TryTake(out _)); - - // Owner change publishes the partial chunk, keeping chunks persistence-homogeneous. - source.SetOwner(ownerB); - Assert.True(source.TryTake(out var chunk)); - Assert.Same(ownerA, chunk.Owner); - Assert.Equal(2, chunk.Count); - source.Return(chunk.Buffer, chunk.Count); - - source.Push(new TestEntity()); - source.Flush(); - Assert.True(source.TryTake(out chunk)); - Assert.Same(ownerB, chunk.Owner); - } - finally - { - ownerA.Unregister(); - ownerB.Unregister(); - } - } - - [Fact] - public void ReturnedBuffersAreClearedAndReused() - { - var source = new SerializationChunkSource(); - - for (var i = 0; i < 4096; i++) - { - source.Push(new TestEntity()); - } - - Assert.True(source.TryTake(out var chunk)); - var buffer = chunk.Buffer; - source.Return(buffer, chunk.Count); - - Assert.All(buffer, Assert.Null); - - // Next fill rents the pooled buffer instead of allocating - source.Push(new TestEntity()); - source.Flush(); - Assert.True(source.TryTake(out var reused)); - Assert.Same(buffer, reused.Buffer); - } - - [Fact] - public void WorkersDrainAllEntitiesAndLogSegments() - { - var source = new SerializationChunkSource(); - var owner = new TestPersistence { PayloadSize = 512 * 1024 }; - var workers = new SerializationThreadWorker[2]; - for (var i = 0; i < workers.Length; i++) - { - workers[i] = new SerializationThreadWorker(i, source); - workers[i].AllocateHeap(); - } - - try - { - var entities = new List(); - for (var i = 0; i < 10_000; i++) - { - entities.Add(new TestEntity { PayloadSize = 16 + i % 64 }); - } - - foreach (var worker in workers) - { - worker.Wake(); - } - - // A large payload published as a dedicated single chunk (a persistence - // self-payload), interleaved with the bare entity stream. - source.SetOwner(owner); - for (var i = 0; i < entities.Count; i++) - { - if (i == 5000) - { - source.PushSingle(owner); - } - - source.Push(entities[i]); - } - - // Mirrors World.PauseSerializationThreads - source.Flush(); - foreach (var worker in workers) - { - worker.Sleep(); - } - - long totalEntities = 0; - long totalBytes = 0; - foreach (var worker in workers) - { - totalEntities += worker.EntitiesSerialized; - totalBytes += worker.BytesSerialized; - } - - Assert.Equal(entities.Count + 1, totalEntities); - - // The self-payload recorded its placement on the persistence itself. - var (selfThread, selfPosition, selfLength) = owner.Placement; - Assert.Equal(owner.PayloadSize, selfLength); - Assert.InRange(selfThread, (byte)0, (byte)(workers.Length - 1)); - var selfHeap = workers[selfThread].GetHeap(selfPosition, selfLength); - Assert.Equal(0, selfHeap[0]); - Assert.Equal((selfLength - 1) & 0xFF, selfHeap[^1]); - - // Every entity appears exactly once in the worker segment logs, with a - // consistent span on that worker's heap: positions are implicit (contiguous - // writes), identity comes from the buffer-entities log. - var seen = new HashSet(); - long expectedBytes = owner.PayloadSize; - foreach (var e in entities) - { - expectedBytes += e.PayloadSize; - } - - foreach (var worker in workers) - { - var lengths = worker.Lengths; - var bufferEntities = worker.BufferEntities; - - foreach (var segment in worker.Segments) - { - Assert.Same(owner, segment.Owner); - Assert.Equal(-1, segment.SlotOffset); - - var heapPos = (int)segment.HeapStart; - for (var i = 0; i < segment.RecordCount; i++) - { - var entity = (TestEntity)bufferEntities[segment.EntitiesStart + i]; - var length = lengths[segment.LengthsStart + i]; - - Assert.True(seen.Add(entity)); - Assert.Equal(entity.PayloadSize, length); - - var heap = workers[Array.IndexOf(workers, worker)].GetHeap(heapPos, length); - Assert.Equal(0, heap[0]); - Assert.Equal((length - 1) & 0xFF, heap[^1]); - - heapPos += length; - } - } - } - - Assert.Equal(entities.Count, seen.Count); - Assert.Equal(expectedBytes, totalBytes); - } - finally - { - owner.Unregister(); - foreach (var worker in workers) - { - worker.Exit(); - } - } - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/SerializationThreadWorkerHandshakeTests.cs b/Projects/Server.Tests/Tests/Serialization/SerializationThreadWorkerHandshakeTests.cs deleted file mode 100644 index 5ea281172..000000000 --- a/Projects/Server.Tests/Tests/Serialization/SerializationThreadWorkerHandshakeTests.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Threading; -using Xunit; - -namespace Server.Tests; - -[Collection("Sequential Server Tests")] -public class SerializationThreadWorkerHandshakeTests -{ - // The pause handshake must tolerate a new cycle starting the moment _stopEvent is - // set (Exit right after Sleep). The watchdog turns a reintroduced deadlock into a - // failure instead of a hung run. - [Fact] - public void WakeSleepExitChurn_NeverDeadlocks() - { - Exception failure = null; - var done = new ManualResetEventSlim(); - - var churn = new Thread(() => - { - try - { - for (var i = 0; i < 2000; i++) - { - var source = new SerializationChunkSource(); - var worker = new SerializationThreadWorker(0, source); - worker.AllocateHeap(); - - worker.Wake(); - worker.Sleep(); - worker.Exit(); // Immediately after Sleep returns — the racy window. - } - } - catch (Exception e) - { - failure = e; - } - finally - { - done.Set(); - } - }) - { - IsBackground = true, - Name = "Handshake Churn" - }; - - churn.Start(); - - Assert.True( - done.Wait(TimeSpan.FromMinutes(2)), - "Worker pause/exit handshake deadlocked (owner blocked in Sleep or worker spinning)." - ); - Assert.Null(failure); - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/ShadowDictionaryEntriesTests.cs b/Projects/Server.Tests/Tests/Serialization/ShadowDictionaryEntriesTests.cs deleted file mode 100644 index bdee95698..000000000 --- a/Projects/Server.Tests/Tests/Serialization/ShadowDictionaryEntriesTests.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using Xunit; - -namespace Server.Tests; - -// Constructing a persistence mutates the static registry (an unsynchronized SortedSet); -// tests that do so must share the sequential collection. -[Collection("Sequential Server Tests")] -public class ShadowDictionaryEntriesTests -{ - private class TestEntity : ISerializable - { - public TestEntity(Serial serial) => Serial = serial; - - public Serial Serial { get; } - public DateTime Created { get; set; } - public bool Deleted => false; - - public void Delete() - { - } - - public void Serialize(IGenericWriter writer) - { - writer.Write(Serial); - writer.Write(0xC0FFEE); - } - - public void Deserialize(IGenericReader reader) - { - } - } - - private static TestEntity GetSlotValue(Array entries, int slot) => - System.Runtime.CompilerServices.Unsafe.As[]>(entries)[slot].Value; - - [Fact] - public void RuntimeLayoutIsSupported() - { - // If this fails on a runtime upgrade, saves still work via the fallback path, - // but the parallel iteration fast path is silently lost — this test makes it loud. - Assert.True(ShadowDictionaryEntries.Supported); - } - - [Fact] - public void SerializeRangeCoversExactlyTheLiveEntities() - { - var persistence = new GenericEntityPersistence("ShadowTest", 1000, 1, 0x7FFFFFFF); - - try - { - var rng = new System.Random(0xBEEF); - var dict = persistence.EntitiesBySerial; - - // Heavy churn: adds, removes, and re-adds to exercise freelist reuse and resizes, - // leaving free slots scattered through the entries array. - var serials = new List(); - for (var i = 0; i < 50_000; i++) - { - var serial = (Serial)(uint)rng.Next(1, int.MaxValue); - if (dict.TryAdd(serial, new TestEntity(serial))) - { - serials.Add(serial); - } - - if (i % 4 == 3) - { - var index = rng.Next(serials.Count); - dict.Remove(serials[index]); - serials.RemoveAt(index); - } - } - - Assert.True(persistence.TrySnapshotEntries(out var slotCount)); - Assert.True(slotCount >= dict.Count); - - var source = (ISlotRangeSource)persistence; - var writer = new BufferWriter(new byte[dict.Count * 16], true); - var lengths = new List(); - - // Serialize in worker-sized slices, like the drain does. - var serialized = 0; - for (var offset = 0; offset < slotCount; offset += 4096) - { - serialized += source.SerializeRange(writer, lengths, offset, Math.Min(4096, slotCount - offset)); - } - - Assert.Equal(dict.Count, serialized); - Assert.Equal(dict.Count, lengths.Count); - - // Re-walk the same slots in the same order, pairing each occupied slot with the - // next logged length — exactly how the snapshot writer locates each record. - var entriesField = ShadowDictionaryEntries.GetEntriesField(); - var entries = (Array)entriesField.GetValue(dict); - - var position = 0; - var lengthIndex = 0; - var matched = 0; - - for (var slot = 0; slot < entries.Length; slot++) - { - // Occupancy is exactly the non-null values, same as production. - var entity = GetSlotValue(entries, slot); - if (entity == null) - { - continue; - } - - var length = lengths[lengthIndex++]; - Assert.Equal(8, length); // serial + int - - var span = writer.Buffer.AsSpan(position, length); - Assert.Equal(entity.Serial, (Serial)System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(span)); - - position += length; - matched++; - } - - Assert.Equal(dict.Count, matched); - } - finally - { - persistence.Unregister(); - } - } - - [Fact] - public void SnapshotFailsGracefullyOnEmptyDictionary() - { - var persistence = new GenericEntityPersistence("ShadowTestEmpty", 1001, 1, 0x7FFFFFFF); - - try - { - Assert.False(persistence.TrySnapshotEntries(out var slotCount)); - Assert.Equal(0, slotCount); - } - finally - { - persistence.Unregister(); - } - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/TypeConverterTests.cs b/Projects/Server.Tests/Tests/Serialization/TypeConverterTests.cs index 9d1c8114b..a0ec96034 100644 --- a/Projects/Server.Tests/Tests/Serialization/TypeConverterTests.cs +++ b/Projects/Server.Tests/Tests/Serialization/TypeConverterTests.cs @@ -8,18 +8,17 @@ using Xunit; namespace Server.Tests; -[Collection("Sequential Server Tests")] -public class TypeConverterTests +public class TypeConverterTests : IClassFixture { [Fact] public void TestReadAfterWrite() { // Arrange - var itemType = typeof(Item); + Type itemType = typeof(Item); - var options = new JsonSerializerOptions(); + JsonSerializerOptions options = new JsonSerializerOptions(); - var converter = new TypeConverter(); + TypeConverter converter = new TypeConverter(); var bufferWriter = new ArrayBufferWriter(); using var jsonWriter = new Utf8JsonWriter(bufferWriter); converter.Write(jsonWriter, itemType, options); @@ -28,11 +27,11 @@ public class TypeConverterTests jsonWriter.Flush(); var jsonData = bufferWriter.WrittenSpan; - var jsonReader = new Utf8JsonReader(jsonData); + Utf8JsonReader jsonReader = new Utf8JsonReader(jsonData); jsonReader.Read(); // Assert - var readType = converter.Read(ref jsonReader, typeof(Type), options); + Type readType = converter.Read(ref jsonReader, typeof(Type), options); Assert.Equal(typeof(Item), readType); } } diff --git a/Projects/Server.Tests/Tests/Timer/TimerTests.cs b/Projects/Server.Tests/Tests/Timer/TimerTests.cs index 9f610de50..6f00b6684 100644 --- a/Projects/Server.Tests/Tests/Timer/TimerTests.cs +++ b/Projects/Server.Tests/Tests/Timer/TimerTests.cs @@ -3,8 +3,8 @@ using Xunit; namespace Server.Tests; -[Collection("Sequential Server Tests")] -public class TimerTests +[Collection("Sequential Tests")] +public class TimerTests : IClassFixture { [Theory] [InlineData(0L, 8L)] @@ -23,7 +23,7 @@ public class TimerTests var tickCount = expectedTicks / 8; - for (var i = 1; i <= tickCount; i++) + for (int i = 1; i <= tickCount; i++) { timerTicks.Ticks = i * 8; @@ -52,7 +52,7 @@ public class TimerTests var tickCount = (expectedDelayTicks + (expectedIntervalTicks * count - 1)) / 8; - for (var i = 1; i <= tickCount; i++) + for (int i = 1; i <= tickCount; i++) { timerTicks.Ticks = i * 8; diff --git a/Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs b/Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs deleted file mode 100644 index e2172a191..000000000 --- a/Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System; -using System.Linq; -using Xunit; - -namespace Server.Tests; - -/// -/// Tests for the Html.EscapeHtml extension methods. -/// These tests verify correct HTML entity escaping and edge cases. -/// -[Collection("Sequential Server Tests")] -public class HtmlEscapeTests -{ - [Theory(DisplayName = "No escaping needed")] - [InlineData("")] - [InlineData("Hello World")] - [InlineData("Plain text without special characters")] - [InlineData("123456789")] - [InlineData("!@#$%^*()_+-=[]{}|;:,.?")] - public void EscapeHtml_NoSpecialCharacters_ReturnsUnchanged(string input) - { - var result = input.EscapeHtml(); - Assert.Equal(input, result); - } - - [Theory(DisplayName = "Single character escaping")] - [InlineData("<", "<")] - [InlineData(">", ">")] - [InlineData("&", "&")] - [InlineData("\"", """)] - [InlineData("'", "'")] - public void EscapeHtml_SingleSpecialCharacter_EscapesCorrectly(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "Multiple instances of same character")] - [InlineData("<><>", "<><>")] - [InlineData("&&&&", "&&&&")] - [InlineData("\"\"\"", """"")] - [InlineData("'''", "'''")] - [InlineData(">>>", ">>>")] - public void EscapeHtml_MultipleSpecialCharacters_EscapesAll(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "Mixed content")] - [InlineData("Hello ", "Hello <world>")] - [InlineData("
Hello
", "<div>Hello</div>")] - [InlineData("Tom & Jerry", "Tom & Jerry")] - [InlineData("He said \"hello\"", "He said "hello"")] - [InlineData("It's a test", "It's a test")] - public void EscapeHtml_MixedContent_EscapesMixedSpecialCharacters(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "Starting with special character")] - [InlineData("World", ">World")] - [InlineData("&Start", "&Start")] - [InlineData("\"Quote", ""Quote")] - [InlineData("'Apostrophe", "'Apostrophe")] - public void EscapeHtml_StartsWithSpecialCharacter_EscapesStart(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "Ending with special character")] - [InlineData("Hello<", "Hello<")] - [InlineData("World>", "World>")] - [InlineData("End&", "End&")] - [InlineData("Quote\"", "Quote"")] - [InlineData("Test'", "Test'")] - public void EscapeHtml_EndsWithSpecialCharacter_EscapesEnd(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "Complex mixed scenarios")] - [InlineData("

Hello & goodbye

", "<p>Hello & goodbye</p>")] - [InlineData("<already>", "&lt;already&gt;")] - [InlineData("", "<tag attr="value" data='test'>")] - [InlineData("ac&d\"e'f", "a<b>c&d"e'f")] - [InlineData(" ", "&nbsp;")] - public void EscapeHtml_ComplexScenarios_EscapesAllSpecialCharacters(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "Null string input")] - public void EscapeHtml_NullString_ReturnsEmpty() - { - string? input = null; - var result = input.EscapeHtml(); - Assert.Empty(result); - } - - [Fact(DisplayName = "Empty string input")] - public void EscapeHtml_EmptyString_ReturnsEmpty() - { - var result = "".EscapeHtml(); - Assert.Empty(result); - } - - [Fact(DisplayName = "Only special characters")] - public void EscapeHtml_OnlySpecialCharacters_EscapesAll() - { - const string input = "<>&\"'"; - const string expected = "<>&"'"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "Ampersand must be escaped first")] - public void EscapeHtml_AmpersandFirst_PreventDoubleEscaping() - { - // This is critical: & must be escaped to & - // If we're not careful, we could double-escape already-escaped content - const string input = "<"; - const string expected = "&lt;"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "ReadOnlySpan overload - no special characters")] - [InlineData("Hello World")] - [InlineData("Plain text")] - public void EscapeHtml_ReadOnlySpan_NoSpecialCharacters_ReturnsUnchanged(string input) - { - var result = input.AsSpan().EscapeHtml(); - Assert.Equal(input, result); - } - - [Theory(DisplayName = "ReadOnlySpan overload - with special characters")] - [InlineData("
", "<div>")] - [InlineData("Tom & Jerry", "Tom & Jerry")] - public void EscapeHtml_ReadOnlySpan_WithSpecialCharacters_EscapesCorrectly(string input, string expected) - { - var result = input.AsSpan().EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "ReadOnlySpan overload - empty input")] - [InlineData("")] - public void EscapeHtml_ReadOnlySpan_Empty_ReturnsEmpty(string input) - { - var result = input.AsSpan().EscapeHtml(); - Assert.Empty(result); - } - - [Fact(DisplayName = "Consecutive special characters")] - public void EscapeHtml_ConsecutiveSpecialCharacters_EscapesAll() - { - const string input = "<<>>&&\"\"''"; - const string expected = "<<>>&&""''"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "Special characters with single normal character between")] - public void EscapeHtml_SpecialCharactersWithGaps_EscapesAll() - { - const string input = "b&c\"d'e"; - const string expected = "<a>b&c"d'e"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "HTML tags")] - public void EscapeHtml_HtmlTags_EscapesTagBrackets() - { - var input = "Hello"; - var expected = "<html><body>Hello</body></html>"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "HTML attributes with mixed quotes")] - public void EscapeHtml_HtmlAttributesWithQuotes_EscapesCorrectly() - { - var input = ""; - var expected = "<a href="test" data='value'>"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "Whitespace handling")] - [InlineData(" spaces ", " spaces ")] - [InlineData("\ttabs\t", "\ttabs\t")] - [InlineData("\nnewlines\n", "\nnewlines\n")] - public void EscapeHtml_Whitespace_PreservedAsIs(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "Performance: long string without special characters")] - public void EscapeHtml_LongStringNoSpecialCharacters_ReturnsQuickly() - { - var input = new string('a', 10000); - var result = input.EscapeHtml(); - Assert.Equal(input, result); - } - - [Fact(DisplayName = "Performance: long string with special characters")] - public void EscapeHtml_LongStringWithSpecialCharacters_HandlesCorrectly() - { - var input = $"Start{new string('<', 100)}End{new string('&', 100)}Final"; - var expected = - $"Start{string.Join("", Enumerable.Repeat("<", 100))}End{string.Join("", Enumerable.Repeat("&", 100))}Final"; - - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "Unicode characters")] - public void EscapeHtml_UnicodeCharacters_PreservedWithSpecialCharsEscaped() - { - const string input = "Hello 世界 & 🎉"; - const string expected = "Hello 世界 <test> & 🎉"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "String overload matches ReadOnlySpan overload")] - public void EscapeHtml_StringVsReadOnlySpan_ProduceSameResult() - { - const string input = "
Tom & Jerry 'in' \"quotes\"
"; - - var resultString = input.EscapeHtml(); - var resultSpan = input.AsSpan().EscapeHtml(); - - Assert.Equal(resultString, resultSpan); - } -} diff --git a/Projects/Server.Tests/Tests/Utility/StringHelperTests.cs b/Projects/Server.Tests/Tests/Utility/StringHelperTests.cs index f428a060a..3c34488b0 100644 --- a/Projects/Server.Tests/Tests/Utility/StringHelperTests.cs +++ b/Projects/Server.Tests/Tests/Utility/StringHelperTests.cs @@ -4,7 +4,7 @@ using Xunit; namespace Server.Tests; -[Collection("Sequential Server Tests")] +[Collection("Sequential Tests")] public class TestStringHelpers { [Theory] diff --git a/Projects/Server.Tests/Tests/World/VirtualSerialTests.cs b/Projects/Server.Tests/Tests/World/VirtualSerialTests.cs index 6c841f9f8..a8d0241e4 100644 --- a/Projects/Server.Tests/Tests/World/VirtualSerialTests.cs +++ b/Projects/Server.Tests/Tests/World/VirtualSerialTests.cs @@ -2,17 +2,17 @@ using Xunit; namespace Server.Tests; -[Collection("Sequential Server Tests")] -public class VirtualSerialTests +[Collection("Sequential Tests")] +public class VirtualSerialTests : IClassFixture { [Fact] public void TestNewVirtualGetsAndRollover() { // Acquire virtual serials until we hit the max - var lastSerial = World.NewVirtual; + Serial lastSerial = World.NewVirtual; do { - var virtualSerial = World.NewVirtual; + Serial virtualSerial = World.NewVirtual; Assert.Equal(lastSerial + 1, virtualSerial); lastSerial = (Serial) (uint) virtualSerial; } while (lastSerial != World.MaxVirtualSerial); diff --git a/Projects/Server/AssemblyHandler.cs b/Projects/Server/AssemblyHandler.cs index 8ad046e68..2ff44bd4f 100644 --- a/Projects/Server/AssemblyHandler.cs +++ b/Projects/Server/AssemblyHandler.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: AssemblyHandler.cs * * * @@ -152,7 +152,7 @@ public static class AssemblyHandler { var types = assembly.GetTypes(); - for (var i = 0; i < types.Length; i++) + for (int i = 0; i < types.Length; i++) { var m = types[i].GetMethod(method, BindingFlags.Static | BindingFlags.Public); if (m?.GetParameters().Length == 0) @@ -234,10 +234,10 @@ public class TypeCache private static ILogger logger = LogFactory.GetLogger(typeof(TypeCache)); #endif - private readonly Dictionary _nameMap = []; - private readonly Dictionary _nameMapInsensitive = []; - private readonly Dictionary _fullNameMap = []; - private readonly Dictionary _fullNameMapInsensitive = []; + private Dictionary _nameMap = new(); + private Dictionary _nameMapInsensitive = new(); + private Dictionary _fullNameMap = new(); + private Dictionary _fullNameMapInsensitive = new(); public TypeCache(Assembly asm) { @@ -248,6 +248,15 @@ public class TypeCache var fullNameMap = new Dictionary>(); var fullNameMapInsensitive = new Dictionary>(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void addTypeToRefs(Type type, string typeName, string fullTypeName) + { + AddToRefs(type, typeName, nameMap); + AddToRefs(type, typeName.ToLower(), nameMapInsensitive); + AddToRefs(type, fullTypeName, fullNameMap); + AddToRefs(type, fullTypeName.ToLower(), fullNameMapInsensitive); + } + var aliasType = typeof(TypeAliasAttribute); for (var i = 0; i < Types.Length; i++) { @@ -258,7 +267,7 @@ public class TypeCache for (var j = 0; j < alias.Aliases.Length; j++) { var fullTypeName = alias.Aliases[j]; - var typeName = fullTypeName[(fullTypeName.AsSpan().LastIndexOf('.') + 1)..]; + var typeName = fullTypeName[(fullTypeName.LastIndexOf('.')+1)..]; addTypeToRefs(current, typeName, fullTypeName); } } @@ -313,17 +322,6 @@ public class TypeCache } #endif } - - return; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void addTypeToRefs(Type type, string typeName, string fullTypeName) - { - AddToRefs(type, typeName, nameMap); - AddToRefs(type, typeName.ToLower(), nameMapInsensitive); - AddToRefs(type, fullTypeName, fullNameMap); - AddToRefs(type, fullTypeName.ToLower(), fullNameMapInsensitive); - } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -340,7 +338,7 @@ public class TypeCache } else { - refs = [type]; + refs = new HashSet { type }; map.Add(key, refs); } } @@ -371,12 +369,12 @@ public class TypeCache if (ignoreCase) { var map = full ? cache._fullNameMapInsensitive : cache._nameMapInsensitive; - _values = map.TryGetValue(hash, out var values) ? values : []; + _values = map.TryGetValue(hash, out var values) ? values : Array.Empty(); } else { var map = full ? cache._fullNameMap : cache._nameMap; - _values = map.TryGetValue(hash, out var values) ? values : []; + _values = map.TryGetValue(hash, out var values) ? values : Array.Empty(); } _index = 0; diff --git a/Projects/Server/Attributes.cs b/Projects/Server/Attributes.cs index 37e98ef66..daab18e7e 100644 --- a/Projects/Server/Attributes.cs +++ b/Projects/Server/Attributes.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Attributes.cs * * * @@ -145,18 +145,16 @@ public class CommandPropertyAttribute : Attribute AccessLevel level, bool readOnly = false, bool canModify = false - ) : this(level, level, readOnly, canModify) + ) : this(level, level) { + ReadOnly = readOnly; + CanModify = canModify; } - public CommandPropertyAttribute( - AccessLevel readLevel, AccessLevel writeLevel, bool readOnly = false, bool canModify = false - ) + public CommandPropertyAttribute(AccessLevel readLevel, AccessLevel writeLevel) { ReadLevel = readLevel; WriteLevel = writeLevel; - ReadOnly = readOnly; - CanModify = canModify; } public AccessLevel ReadLevel { get; } @@ -172,18 +170,16 @@ public class SerializedCommandPropertyAttribute : SerializedPropertyAttrAttribut AccessLevel level, bool readOnly = false, bool canModify = false - ) : this(level, level, readOnly, canModify) + ) : this(level, level) { + ReadOnly = readOnly; + CanModify = canModify; } - public SerializedCommandPropertyAttribute( - AccessLevel readLevel, AccessLevel writeLevel, bool readOnly = false, bool canModify = false - ) + public SerializedCommandPropertyAttribute(AccessLevel readLevel, AccessLevel writeLevel) { ReadLevel = readLevel; WriteLevel = writeLevel; - ReadOnly = readOnly; - CanModify = canModify; } public AccessLevel ReadLevel { get; } diff --git a/Projects/Server/Buffers/PooledArraySpanFormattable.cs b/Projects/Server/Buffers/PooledArraySpanFormattable.cs index c76a80140..86df09876 100644 --- a/Projects/Server/Buffers/PooledArraySpanFormattable.cs +++ b/Projects/Server/Buffers/PooledArraySpanFormattable.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: PooledArraySpanFormattable.cs * * * diff --git a/Projects/Server/Buffers/RawInterpolatedStringHandler.cs b/Projects/Server/Buffers/RawInterpolatedStringHandler.cs index e6d2e88fa..5f29c594d 100644 --- a/Projects/Server/Buffers/RawInterpolatedStringHandler.cs +++ b/Projects/Server/Buffers/RawInterpolatedStringHandler.cs @@ -84,7 +84,7 @@ public ref struct RawInterpolatedStringHandler [MethodImpl(MethodImplOptions.AggressiveInlining)] // used only on a few hot paths public void Clear() { - var toReturn = _arrayToReturnToPool; + char[]? toReturn = _arrayToReturnToPool; this = default; // defensive clear if (toReturn is not null) { @@ -102,8 +102,8 @@ public ref struct RawInterpolatedStringHandler { if (value.Length == 1) { - var chars = _chars; - var pos = _pos; + Span chars = _chars; + int pos = _pos; if ((uint)pos < (uint)chars.Length) { chars[pos] = value[0]; @@ -261,72 +261,49 @@ public ref struct RawInterpolatedStringHandler } /// Writes the specified value to the handler. /// The value to write. - /// - /// The format string. Pass "L" to lowercase the formatted hole's output - /// using ; the underlying value's - /// TryFormat/ToString never sees "L". - /// + /// The format string. public void AppendFormatted(T value, string? format) { - var lowercase = format == "L"; - if (lowercase) - { - format = null; - } - - var startPos = _pos; - // If there's a custom formatter, always use it. if (_hasCustomFormatter) { AppendCustomFormatter(value, format); + return; + } + + // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter + // requires the former. For value types, it won't matter as the type checks devolve into + // JIT-time constants. For reference types, they're more likely to implement IFormattable + // than they are to implement ISpanFormattable: if they don't implement either, we save an + // interface check over first checking for ISpanFormattable and then for IFormattable, and + // if it only implements IFormattable, we come out even: only if it implements both do we + // end up paying for an extra interface check. + string? s; + if (value is IFormattable) + { + // If the value can format itself directly into our buffer, do so. + if (value is ISpanFormattable) + { + int charsWritten; + while (!((ISpanFormattable)value).TryFormat(_chars[_pos..], out charsWritten, format, _provider)) // constrained call avoiding boxing for value types + { + Grow(); + } + + _pos += charsWritten; + return; + } + + s = ((IFormattable)value).ToString(format, _provider); // constrained call avoiding boxing for value types } else { - // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter - // requires the former. For value types, it won't matter as the type checks devolve into - // JIT-time constants. For reference types, they're more likely to implement IFormattable - // than they are to implement ISpanFormattable: if they don't implement either, we save an - // interface check over first checking for ISpanFormattable and then for IFormattable, and - // if it only implements IFormattable, we come out even: only if it implements both do we - // end up paying for an extra interface check. - string? s; - if (value is IFormattable) - { - // If the value can format itself directly into our buffer, do so. - if (value is ISpanFormattable) - { - int charsWritten; - while (!((ISpanFormattable)value).TryFormat(_chars[_pos..], out charsWritten, format, _provider)) // constrained call avoiding boxing for value types - { - Grow(); - } - - _pos += charsWritten; - - if (lowercase) - { - LowercaseRange(_chars.Slice(startPos, _pos - startPos)); - } - return; - } - - s = ((IFormattable)value).ToString(format, _provider); // constrained call avoiding boxing for value types - } - else - { - s = value?.ToString(); - } - - if (s is not null) - { - AppendStringDirect(s); - } + s = value?.ToString(); } - if (lowercase) + if (s is not null) { - LowercaseRange(_chars.Slice(startPos, _pos - startPos)); + AppendStringDirect(s); } } @@ -335,7 +312,7 @@ public ref struct RawInterpolatedStringHandler /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. public void AppendFormatted(T value, int alignment) { - var startingPos = _pos; + int startingPos = _pos; AppendFormatted(value); if (alignment != 0) { @@ -349,7 +326,7 @@ public ref struct RawInterpolatedStringHandler /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. public void AppendFormatted(T value, int alignment, string? format) { - var startingPos = _pos; + int startingPos = _pos; AppendFormatted(value, format); if (alignment != 0) { @@ -377,32 +354,22 @@ public ref struct RawInterpolatedStringHandler /// Writes the specified string of chars to the handler. /// The span to write. /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. - /// - /// The format string. Pass "L" to lowercase the value's chars (only the - /// value range, not any alignment padding) using . - /// + /// The format string. public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) { - var lowercase = format == "L"; - - var leftAlign = false; + bool leftAlign = false; if (alignment < 0) { leftAlign = true; alignment = -alignment; } - var paddingRequired = alignment - value.Length; + int paddingRequired = alignment - value.Length; if (paddingRequired <= 0) { // The value is as large or larger than the required amount of padding, // so just write the value. - var startPos = _pos; AppendFormatted(value); - if (lowercase) - { - LowercaseRange(_chars.Slice(startPos, _pos - startPos)); - } return; } @@ -410,13 +377,8 @@ public ref struct RawInterpolatedStringHandler EnsureCapacityForAdditionalChars(value.Length + paddingRequired); if (leftAlign) { - var valueStart = _pos; value.CopyTo(_chars[_pos..]); _pos += value.Length; - if (lowercase) - { - LowercaseRange(_chars.Slice(valueStart, _pos - valueStart)); - } _chars.Slice(_pos, paddingRequired).Fill(' '); _pos += paddingRequired; } @@ -424,13 +386,8 @@ public ref struct RawInterpolatedStringHandler { _chars.Slice(_pos, paddingRequired).Fill(' '); _pos += paddingRequired; - var valueStart = _pos; value.CopyTo(_chars[_pos..]); _pos += value.Length; - if (lowercase) - { - LowercaseRange(_chars.Slice(valueStart, _pos - valueStart)); - } } } #endregion @@ -520,7 +477,7 @@ public ref struct RawInterpolatedStringHandler Debug.Assert(_hasCustomFormatter); Debug.Assert(_provider != null); - var formatter = (ICustomFormatter?)_provider.GetFormat(typeof(ICustomFormatter)); + ICustomFormatter? formatter = (ICustomFormatter?)_provider.GetFormat(typeof(ICustomFormatter)); Debug.Assert(formatter != null, "An incorrectly written provider said it implemented ICustomFormatter, and then didn't"); if (formatter?.Format(format, value, _provider) is string customFormatted) @@ -537,16 +494,16 @@ public ref struct RawInterpolatedStringHandler Debug.Assert(startingPos >= 0 && startingPos <= _pos); Debug.Assert(alignment != 0); - var charsWritten = _pos - startingPos; + int charsWritten = _pos - startingPos; - var leftAlign = false; + bool leftAlign = false; if (alignment < 0) { leftAlign = true; alignment = -alignment; } - var paddingNeeded = alignment - charsWritten; + int paddingNeeded = alignment - charsWritten; if (paddingNeeded > 0) { EnsureCapacityForAdditionalChars(paddingNeeded); @@ -565,32 +522,6 @@ public ref struct RawInterpolatedStringHandler } } - /// - /// Lowercases the chars in in the half-open range [start, end) - /// using the BCL's vectorized . - /// Copies through a stackalloc temp for ranges up to 256 chars, otherwise rents from - /// . The BCL overload throws on overlapping source/destination - /// spans, so a temp is always required; running one big SIMD pass beats chunking on long inputs - /// and avoids splitting surrogate pairs at chunk boundaries. - /// - private static void LowercaseRange(Span dest) - { - if (dest.Length <= MinimumArrayPoolLength) - { - Span temp = stackalloc char[MinimumArrayPoolLength]; - var t = temp[..dest.Length]; - dest.CopyTo(t); - ((ReadOnlySpan)t).ToLowerInvariant(dest); - return; - } - - var rented = STArrayPool.Shared.Rent(dest.Length); - var rentedSlice = rented.AsSpan(0, dest.Length); - dest.CopyTo(rentedSlice); - ((ReadOnlySpan)rentedSlice).ToLowerInvariant(dest); - STArrayPool.Shared.Return(rented); - } - /// Ensures has the capacity to store beyond . [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnsureCapacityForAdditionalChars(int additionalChars) @@ -652,13 +583,13 @@ public ref struct RawInterpolatedStringHandler // ints that could technically overflow if someone tried to, for example, append a huge string to a huge string, we also clamp to int.MaxValue. // Even if the array creation fails in such a case, we may later fail in ToStringAndClear. - var newCapacity = Math.Max(requiredMinCapacity, Math.Min((uint)_chars.Length * 2, 0x3FFFFFDF)); - var arraySize = (int)Math.Clamp(newCapacity, MinimumArrayPoolLength, int.MaxValue); + uint newCapacity = Math.Max(requiredMinCapacity, Math.Min((uint)_chars.Length * 2, 0x3FFFFFDF)); + int arraySize = (int)Math.Clamp(newCapacity, MinimumArrayPoolLength, int.MaxValue); - var newArray = STArrayPool.Shared.Rent(arraySize); + char[] newArray = STArrayPool.Shared.Rent(arraySize); _chars[.._pos].CopyTo(newArray); - var toReturn = _arrayToReturnToPool; + char[]? toReturn = _arrayToReturnToPool; _chars = _arrayToReturnToPool = newArray; if (toReturn is not null) diff --git a/Projects/Server/Buffers/STArrayPool.cs b/Projects/Server/Buffers/STArrayPool.cs index b611c7a65..25dd6a7f4 100644 --- a/Projects/Server/Buffers/STArrayPool.cs +++ b/Projects/Server/Buffers/STArrayPool.cs @@ -245,7 +245,7 @@ public class STArrayPool : ArrayPool [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetMaxSizeForBucket(int binIndex) { - var maxSize = 16 << binIndex; + int maxSize = 16 << binIndex; Debug.Assert(maxSize >= 0); return maxSize; } @@ -259,7 +259,7 @@ public class STArrayPool : ArrayPool internal static MemoryPressure GetMemoryPressure() { - var memoryInfo = GC.GetGCMemoryInfo(); + GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo(); if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * 0.90) { @@ -333,7 +333,7 @@ public class STArrayPool : ArrayPool return; } - var trimCount = 1; + int trimCount = 1; switch (pressure) { case MemoryPressure.Medium: diff --git a/Projects/Server/Buffers/SpanReader.cs b/Projects/Server/Buffers/SpanReader.cs index 8a99fbaaa..c127327c5 100644 --- a/Projects/Server/Buffers/SpanReader.cs +++ b/Projects/Server/Buffers/SpanReader.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SpanReader.cs * * * @@ -14,6 +14,7 @@ *************************************************************************/ using System.Buffers.Binary; +using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Text; @@ -44,7 +45,7 @@ public ref struct SpanReader { if (Position >= Length) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } return _buffer[Position++]; @@ -61,7 +62,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadInt16BigEndian(_buffer[Position..], out var value)) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } Position += 2; @@ -73,7 +74,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadInt16LittleEndian(_buffer[Position..], out var value)) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } Position += 2; @@ -85,7 +86,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadUInt16BigEndian(_buffer[Position..], out var value)) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } Position += 2; @@ -97,7 +98,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadUInt16LittleEndian(_buffer[Position..], out var value)) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } Position += 2; @@ -109,7 +110,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadInt32BigEndian(_buffer[Position..], out var value)) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } Position += 4; @@ -121,7 +122,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadUInt32BigEndian(_buffer[Position..], out var value)) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } Position += 4; @@ -133,7 +134,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadUInt32LittleEndian(_buffer[Position..], out var value)) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } Position += 4; @@ -145,7 +146,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadInt64BigEndian(_buffer[Position..], out var value)) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } Position += 8; @@ -157,7 +158,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadUInt64BigEndian(_buffer[Position..], out var value)) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } Position += 8; @@ -172,9 +173,9 @@ public ref struct SpanReader return ""; } - var byteLength = encoding.GetByteLengthForEncoding(); + int byteLength = encoding.GetByteLengthForEncoding(); - var isFixedLength = fixedLength > -1; + bool isFixedLength = fixedLength > -1; var remaining = Remaining; int size; @@ -183,7 +184,7 @@ public ref struct SpanReader size = fixedLength * byteLength; if (size > Remaining) { - throw new EndOfStreamException("Cannot read past the end of the buffer."); + throw new OutOfMemoryException(); } } else @@ -207,277 +208,99 @@ public ref struct SpanReader } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLittleUniSafe(int fixedLength) => ReadStringLittleUni(true, fixedLength); + public string ReadLittleUniSafe(int fixedLength) => ReadString(TextEncoding.UnicodeLE, true, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLittleUniSafe() => ReadStringLittleUni(true); + public string ReadLittleUniSafe() => ReadString(TextEncoding.UnicodeLE, true); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLittleUni(int fixedLength) => ReadStringLittleUni(false, fixedLength); + public string ReadLittleUni(int fixedLength) => ReadString(TextEncoding.UnicodeLE, false, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLittleUni() => ReadStringLittleUni(false); + public string ReadLittleUni() => ReadString(TextEncoding.UnicodeLE); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadBigUniSafe(int fixedLength) => ReadStringBigUni(true, fixedLength); + public string ReadBigUniSafe(int fixedLength) => ReadString(TextEncoding.Unicode, true, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadBigUniSafe() => ReadStringBigUni(true); + public string ReadBigUniSafe() => ReadString(TextEncoding.Unicode, true); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadBigUni(int fixedLength) => ReadStringBigUni(false, fixedLength); + public string ReadBigUni(int fixedLength) => ReadString(TextEncoding.Unicode, false, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadBigUni() => ReadStringBigUni(false); + public string ReadBigUni() => ReadString(TextEncoding.Unicode); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadUTF8Safe(int fixedLength) => ReadStringUtf8(true, fixedLength); + public string ReadUTF8Safe(int fixedLength) => ReadString(TextEncoding.UTF8, true, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadUTF8Safe() => ReadStringUtf8(true); + public string ReadUTF8Safe() => ReadString(TextEncoding.UTF8, true); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadUTF8(int fixedLength) => ReadStringUtf8(false, fixedLength); + public string ReadUTF8() => ReadString(TextEncoding.UTF8); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadUTF8() => ReadStringUtf8(false); + public string ReadAsciiSafe(int fixedLength) => ReadString(Encoding.ASCII, true, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadAsciiSafe(int fixedLength) => ReadStringAscii(true, fixedLength); + public string ReadAsciiSafe() => ReadString(Encoding.ASCII, true); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadAsciiSafe() => ReadStringAscii(true); + public string ReadAscii(int fixedLength) => ReadString(Encoding.ASCII, false, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadAscii(int fixedLength) => ReadStringAscii(false, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadAscii() => ReadStringAscii(false); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLatin1Safe(int fixedLength) => ReadStringLatin1(true, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLatin1Safe() => ReadStringLatin1(true); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLatin1(int fixedLength) => ReadStringLatin1(false, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLatin1() => ReadStringLatin1(false); - - /// - /// Reads a big-endian UTF-16 string, filtering control codes and non-characters in safe mode. - /// - private string ReadStringBigUni(bool safeString, int fixedLength = -1) - { - if (fixedLength == 0) - { - return ""; - } - - const int byteLength = 2; - var isFixedLength = fixedLength > -1; - - var remaining = Remaining; - int size; - if (isFixedLength) - { - size = fixedLength * byteLength; - if (size > remaining) - { - throw new EndOfStreamException("Cannot read past the end of the buffer."); - } - } - else - { - // Ensure even number of bytes - size = remaining - (remaining & 1); - } - - var span = _buffer.Slice(Position, size); - var index = span.IndexOfTerminator(byteLength); - - if (index > -1) - { - span = _buffer.Slice(Position, index); - } - - Position += isFixedLength || index < 0 ? size : index + byteLength; - - return TextEncoding.GetStringBigUni(span, safeString); - } - - /// - /// Reads a little-endian UTF-16 string, filtering control codes and non-characters in safe mode. - /// - private string ReadStringLittleUni(bool safeString, int fixedLength = -1) - { - if (fixedLength == 0) - { - return ""; - } - - const int byteLength = 2; - var isFixedLength = fixedLength > -1; - - var remaining = Remaining; - int size; - if (isFixedLength) - { - size = fixedLength * byteLength; - if (size > remaining) - { - throw new EndOfStreamException("Cannot read past the end of the buffer."); - } - } - else - { - // Ensure even number of bytes - size = remaining - (remaining & 1); - } - - var span = _buffer.Slice(Position, size); - var index = span.IndexOfTerminator(byteLength); - - if (index > -1) - { - span = _buffer.Slice(Position, index); - } - - Position += isFixedLength || index < 0 ? size : index + byteLength; - - return TextEncoding.GetStringLittleUni(span, safeString); - } - - /// - /// Reads an ASCII string, filtering C0 control codes and DEL in safe mode. - /// - private string ReadStringAscii(bool safeString, int fixedLength = -1) - { - if (fixedLength == 0) - { - return ""; - } - - var isFixedLength = fixedLength > -1; - - int size = isFixedLength ? fixedLength : Remaining; - if (size > Remaining) - { - throw new EndOfStreamException("Cannot read past the end of the buffer."); - } - - var span = _buffer.Slice(Position, size); - var index = span.IndexOf((byte)0); - - if (index > -1) - { - span = _buffer.Slice(Position, index); - } - - Position += isFixedLength || index < 0 ? size : index + 1; - - return TextEncoding.GetStringAscii(span, safeString); - } - - /// - /// Reads a UTF-8 string, filtering control codes and non-characters in safe mode. - /// - private string ReadStringUtf8(bool safeString, int fixedLength = -1) - { - if (fixedLength == 0) - { - return ""; - } - - var isFixedLength = fixedLength > -1; - - int size = isFixedLength ? fixedLength : Remaining; - if (size > Remaining) - { - throw new EndOfStreamException("Cannot read past the end of the buffer."); - } - - var span = _buffer.Slice(Position, size); - var index = span.IndexOf((byte)0); - - if (index > -1) - { - span = _buffer.Slice(Position, index); - } - - Position += isFixedLength || index < 0 ? size : index + 1; - - return TextEncoding.GetStringUtf8(span, safeString); - } - - /// - /// Reads a Latin1 string, filtering C0/C1 control codes in safe mode. - /// - private string ReadStringLatin1(bool safeString, int fixedLength = -1) - { - if (fixedLength == 0) - { - return ""; - } - - var isFixedLength = fixedLength > -1; - - int size = isFixedLength ? fixedLength : Remaining; - if (size > Remaining) - { - throw new EndOfStreamException("Cannot read past the end of the buffer."); - } - - var span = _buffer.Slice(Position, size); - var index = span.IndexOf((byte)0); - - if (index > -1) - { - span = _buffer.Slice(Position, index); - } - - Position += isFixedLength || index < 0 ? size : index + 1; - - return TextEncoding.GetStringLatin1(span, safeString); - } + public string ReadAscii() => ReadString(Encoding.ASCII); [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Seek(int offset, SeekOrigin origin) { - var newPosition = origin switch + Debug.Assert( + origin != SeekOrigin.End || offset <= 0, + "Attempting to seek to a position beyond capacity using SeekOrigin.End" + ); + + Debug.Assert( + origin != SeekOrigin.End || offset >= -_buffer.Length, + "Attempting to seek to a negative position using SeekOrigin.End" + ); + + Debug.Assert( + origin != SeekOrigin.Begin || offset >= 0, + "Attempting to seek to a negative position using SeekOrigin.Begin" + ); + + Debug.Assert( + origin != SeekOrigin.Begin || offset <= _buffer.Length, + "Attempting to seek to a position beyond the capacity using SeekOrigin.Begin" + ); + + Debug.Assert( + origin != SeekOrigin.Current || Position + offset >= 0, + "Attempting to seek to a negative position using SeekOrigin.Current" + ); + + Debug.Assert( + origin != SeekOrigin.Current || Position + offset <= _buffer.Length, + "Attempting to seek to a position beyond the capacity using SeekOrigin.Current" + ); + + return Position = Math.Max(0, origin switch { SeekOrigin.Current => Position + offset, SeekOrigin.End => _buffer.Length + offset, _ => offset // Begin - }; - - if (newPosition < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "Seek operation would result in a negative position."); - } - - if (newPosition > _buffer.Length) - { - throw new ArgumentOutOfRangeException(nameof(offset), $"Cannot seek to position {newPosition} beyond buffer length {_buffer.Length}."); - } - - return Position = newPosition; + }); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Read(scoped Span bytes) + public bool Read(Span bytes) { - if (bytes.Length == 0) + if (bytes.Length < Length) { - return 0; + throw new ArgumentOutOfRangeException(nameof(bytes)); } - var bytesWritten = Math.Min(bytes.Length, Remaining); - _buffer.Slice(Position, bytesWritten).CopyTo(bytes); - - Position += bytesWritten; - return bytesWritten; + return _buffer.TryCopyTo(bytes); } } diff --git a/Projects/Server/Buffers/SpanWriter.cs b/Projects/Server/Buffers/SpanWriter.cs index 594cdc503..4ce423c5b 100644 --- a/Projects/Server/Buffers/SpanWriter.cs +++ b/Projects/Server/Buffers/SpanWriter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SpanWriter.cs * * * @@ -14,6 +14,7 @@ *************************************************************************/ using System.Buffers.Binary; +using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -55,14 +56,14 @@ public ref struct SpanWriter public Span RawBuffer => _buffer; /** - * Converts the writer to a Span using a SpanOwner. - * If the buffer was stackalloc, it will be copied to a rented buffer. - * Otherwise the existing rented buffer is used. - * - * Note: - * Do not use the SpanWriter after calling this method. - * This method will effectively dispose of the SpanWriter and is therefore considered terminal. - */ + * Converts the writer to a Span using a SpanOwner. + * If the buffer was stackalloc, it will be copied to a rented buffer. + * Otherwise the existing rented buffer is used. + * + * Note: + * Do not use the SpanWriter after calling this method. + * This method will effectively dispose of the SpanWriter and is therefore considered terminal. + */ [MethodImpl(MethodImplOptions.AggressiveInlining)] public SpanOwner ToSpan() { @@ -116,11 +117,11 @@ public ref struct SpanWriter private void Grow(int additionalCapacity) { var newSize = Math.Max(BytesWritten + additionalCapacity, _buffer.Length * 2); - var poolArray = STArrayPool.Shared.Rent(newSize); + byte[] poolArray = STArrayPool.Shared.Rent(newSize); _buffer[..BytesWritten].CopyTo(poolArray); - var toReturn = _arrayToReturnToPool; + byte[] toReturn = _arrayToReturnToPool; _buffer = _arrayToReturnToPool = poolArray; if (toReturn != null) { @@ -135,7 +136,7 @@ public ref struct SpanWriter { if (!_resize) { - throw new InvalidOperationException("Buffer is full and resizing is disabled."); + throw new OutOfMemoryException(); } Grow(count); @@ -150,7 +151,7 @@ public ref struct SpanWriter { if (!_resize) { - throw new InvalidOperationException("Buffer is full and resizing is disabled."); + throw new OutOfMemoryException(); } Grow(capacity - BytesWritten); @@ -273,7 +274,8 @@ public ref struct SpanWriter [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteAscii(char chr) => Write((byte)chr); - public void WriteAscii(ref RawInterpolatedStringHandler handler) + public void WriteAscii( + ref RawInterpolatedStringHandler handler) { Write(handler.Text, Encoding.ASCII); handler.Clear(); @@ -288,22 +290,9 @@ public ref struct SpanWriter handler.Clear(); } - public void WriteLatin1(ref RawInterpolatedStringHandler handler) - { - Write(handler.Text, Encoding.Latin1); - handler.Clear(); - } - - public void WriteLatin1( - IFormatProvider? formatProvider, - [InterpolatedStringHandlerArgument("formatProvider")] + public void Write( + Encoding encoding, ref RawInterpolatedStringHandler handler) - { - Write(handler.Text, Encoding.Latin1); - handler.Clear(); - } - - public void Write(Encoding encoding, ref RawInterpolatedStringHandler handler) { Write(handler.Text, encoding); handler.Clear(); @@ -352,66 +341,53 @@ public ref struct SpanWriter } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLittleUni(ReadOnlySpan value) => Write(value, TextEncoding.UnicodeLE); + public void WriteLittleUni(string value) => Write(value, TextEncoding.UnicodeLE); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLittleUniNull(ReadOnlySpan value) + public void WriteLittleUniNull(string value) { Write(value, TextEncoding.UnicodeLE); Write((ushort)0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLittleUni(ReadOnlySpan value, int fixedLength) => Write(value, TextEncoding.UnicodeLE, fixedLength); + public void WriteLittleUni(string value, int fixedLength) => Write(value, TextEncoding.UnicodeLE, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBigUni(ReadOnlySpan value) => Write(value, TextEncoding.Unicode); + public void WriteBigUni(string value) => Write(value, TextEncoding.Unicode); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBigUniNull(ReadOnlySpan value) + public void WriteBigUniNull(string value) { Write(value, TextEncoding.Unicode); Write((ushort)0); // '\0' } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBigUni(ReadOnlySpan value, int fixedLength) => Write(value, TextEncoding.Unicode, fixedLength); + public void WriteBigUni(string value, int fixedLength) => Write(value, TextEncoding.Unicode, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUTF8(ReadOnlySpan value) => Write(value, TextEncoding.UTF8); + public void WriteUTF8(string value) => Write(value, TextEncoding.UTF8); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUTF8Null(ReadOnlySpan value) + public void WriteUTF8Null(string value) { Write(value, TextEncoding.UTF8); Write((byte)0); // '\0' } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteAscii(ReadOnlySpan value) => Write(value, Encoding.ASCII); + public void WriteAscii(string value) => Write(value, Encoding.ASCII); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteAsciiNull(ReadOnlySpan value) + public void WriteAsciiNull(string value) { Write(value, Encoding.ASCII); Write((byte)0); // '\0' } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteAscii(ReadOnlySpan value, int fixedLength) => Write(value, Encoding.ASCII, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLatin1(ReadOnlySpan value) => Write(value, Encoding.Latin1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLatin1(ReadOnlySpan value, int fixedLength) => Write(value, Encoding.Latin1, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLatin1Null(ReadOnlySpan value) - { - Write(value, Encoding.Latin1); - Write((byte)0); // '\0' - } + public void WriteAscii(string value, int fixedLength) => Write(value, Encoding.ASCII, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear(int count) @@ -424,25 +400,46 @@ public ref struct SpanWriter [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Seek(int offset, SeekOrigin origin) { - var newPosition = origin switch + Debug.Assert( + origin != SeekOrigin.End || _resize || offset <= 0, + "Attempting to seek to a position beyond capacity using SeekOrigin.End without resize" + ); + + Debug.Assert( + origin != SeekOrigin.End || offset >= -_buffer.Length, + + "Attempting to seek to a negative position using SeekOrigin.End" + ); + + Debug.Assert( + origin != SeekOrigin.Begin || offset >= 0, + "Attempting to seek to a negative position using SeekOrigin.Begin" + ); + + Debug.Assert( + origin != SeekOrigin.Begin || _resize || offset <= _buffer.Length, + "Attempting to seek to a position beyond the capacity using SeekOrigin.Begin without resize" + ); + + Debug.Assert( + origin != SeekOrigin.Current || _position + offset >= 0, + "Attempting to seek to a negative position using SeekOrigin.Current" + ); + + Debug.Assert( + origin != SeekOrigin.Current || _resize || _position + offset <= _buffer.Length, + "Attempting to seek to a position beyond the capacity using SeekOrigin.Current without resize" + ); + + var newPosition = Math.Max(0, origin switch { SeekOrigin.Current => _position + offset, SeekOrigin.End => BytesWritten + offset, _ => offset // Begin - }; - - if (newPosition < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "Seek operation would result in a negative position."); - } + }); if (newPosition > _buffer.Length) { - if (!_resize) - { - throw new InvalidOperationException($"Cannot seek to position {newPosition} beyond buffer capacity {_buffer.Length} when resizing is disabled."); - } - Grow(newPosition - _buffer.Length + 1); } @@ -452,7 +449,7 @@ public ref struct SpanWriter [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - var toReturn = _arrayToReturnToPool; + byte[] toReturn = _arrayToReturnToPool; this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again if (toReturn != null) { @@ -481,7 +478,7 @@ public ref struct SpanWriter [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - var toReturn = _arrayToReturnToPool; + byte[] toReturn = _arrayToReturnToPool; this = default; if (_length > 0) { diff --git a/Projects/Server/Buffers/ValueStringBuilder.cs b/Projects/Server/Buffers/ValueStringBuilder.cs index 5ea01fa46..3196ca45e 100644 --- a/Projects/Server/Buffers/ValueStringBuilder.cs +++ b/Projects/Server/Buffers/ValueStringBuilder.cs @@ -151,7 +151,7 @@ public ref struct ValueStringBuilder Grow(count); } - var remaining = _length - index; + int remaining = _length - index; _chars.Slice(index, remaining).CopyTo(_chars[(index + count)..]); _chars.Slice(index, count).Fill(value); _length += count; @@ -165,14 +165,14 @@ public ref struct ValueStringBuilder return; } - var count = s.Length; + int count = s.Length; if (_length > _chars.Length - count) { Grow(count); } - var remaining = _length - index; + int remaining = _length - index; _chars.Slice(index, remaining).CopyTo(_chars[(index + count)..]); s.AsSpan().CopyTo(_chars[index..]); _length += count; @@ -184,7 +184,7 @@ public ref struct ValueStringBuilder { if (value is ISpanFormattable) { - var destination = _chars[_length..]; + Span destination = _chars[_length..]; int charsWritten; while (!((ISpanFormattable)value).TryFormat(destination, out charsWritten, format, default)) { @@ -210,6 +210,15 @@ public ref struct ValueStringBuilder } } + // Compiler generated + public void Append(ref RawInterpolatedStringHandler handler) => Append(handler.Text); + + // Compiler generated + public void Append( + IFormatProvider? formatProvider, + [InterpolatedStringHandlerArgument("formatProvider")] + ref RawInterpolatedStringHandler handler + ) => Append(handler.Text); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -220,7 +229,7 @@ public ref struct ValueStringBuilder return; } - var pos = _length; + int pos = _length; if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. { _chars[pos] = s[0]; @@ -256,7 +265,7 @@ public ref struct ValueStringBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AppendSlow(string? s) { - var pos = _length; + int pos = _length; if (pos > _chars.Length - s.Length) { Grow(s.Length); @@ -274,8 +283,8 @@ public ref struct ValueStringBuilder Grow(count); } - var dst = _chars.Slice(_length, count); - for (var i = 0; i < dst.Length; i++) + Span dst = _chars.Slice(_length, count); + for (int i = 0; i < dst.Length; i++) { dst[i] = c; } @@ -285,14 +294,14 @@ public ref struct ValueStringBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void Append(char* value, int length) { - var pos = _length; + int pos = _length; if (pos > _chars.Length - length) { Grow(length); } - var dst = _chars.Slice(_length, length); - for (var i = 0; i < dst.Length; i++) + Span dst = _chars.Slice(_length, length); + for (int i = 0; i < dst.Length; i++) { dst[i] = *value++; } @@ -300,9 +309,9 @@ public ref struct ValueStringBuilder } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Append(scoped ReadOnlySpan value) + public void Append(ReadOnlySpan value) { - var pos = _length; + int pos = _length; if (pos > _chars.Length - value.Length) { Grow(value.Length); @@ -315,7 +324,7 @@ public ref struct ValueStringBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span AppendSpan(int length) { - var origPos = _length; + int origPos = _length; if (origPos > _chars.Length - length) { Grow(length); @@ -336,11 +345,11 @@ public ref struct ValueStringBuilder [MethodImpl(MethodImplOptions.NoInlining)] private void Grow(int additionalCapacityBeyondPos) { - var poolArray = ArrayPool.Rent(Math.Max(_length + additionalCapacityBeyondPos, _chars.Length * 2)); + char[] poolArray = ArrayPool.Rent(Math.Max(_length + additionalCapacityBeyondPos, _chars.Length * 2)); _chars[.._length].CopyTo(poolArray); - var toReturn = _arrayToReturnToPool; + char[] toReturn = _arrayToReturnToPool; _chars = _arrayToReturnToPool = poolArray; if (toReturn != null) { @@ -363,7 +372,7 @@ public ref struct ValueStringBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReplaceAny(ReadOnlySpan oldChars, ReadOnlySpan newChars, int startIndex, int count) { - var currentLength = _length; + int currentLength = _length; if ((uint)startIndex > (uint)currentLength) { throw new ArgumentOutOfRangeException(nameof(startIndex)); @@ -394,7 +403,7 @@ public ref struct ValueStringBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Replace(char oldChar, char newChar, int startIndex, int count) { - var currentLength = _length; + int currentLength = _length; if ((uint)startIndex > (uint)currentLength) { throw new ArgumentOutOfRangeException(nameof(startIndex)); @@ -454,128 +463,4 @@ public ref struct ValueStringBuilder _length -= length; } - - // Copy-and-reconcile interpolation handler. - // The handler receives a VALUE COPY of the builder (C# limitation: [InterpolatedStringHandlerArgument("")] - // passes struct receivers by value). The copy shares the same underlying char buffer (Span points to - // the same stackalloc/pooled memory), so writes go to the original buffer. If Grow() happens, the copy - // gets a new buffer. Append() reconciles by copying the handler's state back to the original. - // Safe because the game loop is single-threaded — no concurrent access between handler construction - // and reconciliation. - - public void Append( - [InterpolatedStringHandlerArgument("")] - scoped ref InterpolationHandler handler) - { - // Reconcile: the handler's copy has the updated _length (and possibly new buffer from Grow). - this = handler._builder; - } - - [InterpolatedStringHandler] - public ref struct InterpolationHandler - { - internal ValueStringBuilder _builder; - - public InterpolationHandler(int literalLength, int formattedCount, ValueStringBuilder builder) - { - _builder = builder; - _builder.EnsureCapacity(_builder._length + literalLength + formattedCount * 11); - } - - public void AppendLiteral(string value) => _builder.Append(value); - - public void AppendFormatted(T value) => _builder.Append(value); - - public void AppendFormatted(T value, string? format) => _builder.Append(value, format); - - public void AppendFormatted(T value, int alignment) - { - var startingPos = _builder._length; - _builder.Append(value); - if (alignment != 0) - { - AppendOrInsertAlignmentIfNeeded(startingPos, alignment); - } - } - - public void AppendFormatted(T value, int alignment, string? format) - { - var startingPos = _builder._length; - _builder.Append(value, format); - if (alignment != 0) - { - AppendOrInsertAlignmentIfNeeded(startingPos, alignment); - } - } - - public void AppendFormatted(scoped ReadOnlySpan value) => _builder.Append(value); - - public void AppendFormatted(scoped ReadOnlySpan value, int alignment = 0, string? format = null) - { - var leftAlign = false; - if (alignment < 0) - { - leftAlign = true; - alignment = -alignment; - } - - var paddingRequired = alignment - value.Length; - if (paddingRequired <= 0) - { - _builder.Append(value); - return; - } - - _builder.EnsureCapacity(_builder._length + value.Length + paddingRequired); - if (leftAlign) - { - _builder.Append(value); - _builder.AppendSpan(paddingRequired).Fill(' '); - } - else - { - _builder.AppendSpan(paddingRequired).Fill(' '); - _builder.Append(value); - } - } - - public void AppendFormatted(string? value) => _builder.Append(value); - - public void AppendFormatted(string? value, int alignment = 0, string? format = null) => - AppendFormatted(value, alignment, format); - - public void AppendFormatted(object? value, int alignment = 0, string? format = null) => - AppendFormatted(value, alignment, format); - - private void AppendOrInsertAlignmentIfNeeded(int startingPos, int alignment) - { - var charsWritten = _builder._length - startingPos; - - var leftAlign = false; - if (alignment < 0) - { - leftAlign = true; - alignment = -alignment; - } - - var paddingNeeded = alignment - charsWritten; - if (paddingNeeded <= 0) - { - return; - } - - if (leftAlign) - { - _builder.AppendSpan(paddingNeeded).Fill(' '); - } - else - { - // Shift content right, fill padding on left - _builder.AppendSpan(paddingNeeded); - _builder._chars.Slice(startingPos, charsWritten) - .CopyTo(_builder._chars[(startingPos + paddingNeeded)..]); - _builder._chars.Slice(startingPos, paddingNeeded).Fill(' '); - } - } - } } diff --git a/Projects/Server/CityInfo.cs b/Projects/Server/CityInfo.cs index bbf0f6d9f..1a3de5e09 100644 --- a/Projects/Server/CityInfo.cs +++ b/Projects/Server/CityInfo.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CityInfo.cs * * * diff --git a/Projects/Server/Client/ArtData.cs b/Projects/Server/Client/ArtData.cs index 06af4d70b..ba6e4167a 100644 --- a/Projects/Server/Client/ArtData.cs +++ b/Projects/Server/Client/ArtData.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ArtData.cs * * * @@ -50,18 +50,18 @@ public class ArtData : IDisposable } } - public (ushort Width, ushort Height, Rectangle2D Bounds) GetStaticBounds(int index) + public Rectangle2D GetStaticBounds(int index) { if (index is < 0 or > 0x10000) { - return (0, 0, Rectangle2D.Empty); + return Rectangle2D.Empty; } index += 16384; if (!_dataRanges.TryGetValue(index, out var entry)) { - return (0, 0, Rectangle2D.Empty); + return Rectangle2D.Empty; } Span buffer = stackalloc ushort[entry.Size / 2]; @@ -73,10 +73,10 @@ public class ArtData : IDisposable if (width == 0 || height == 0) { - return (0, 0, Rectangle2D.Empty); + return Rectangle2D.Empty; } - return (width, height, GetBoundsFromRGBA1555Bitmap(width, height, buffer[4..])); + return GetBoundsFromRGBA1555Bitmap(width, height, buffer[4..]); } private static Dictionary LoadMulRanges(string idxPath) @@ -106,15 +106,15 @@ public class ArtData : IDisposable public static Rectangle2D GetBoundsFromRGBA1555Bitmap(int width, int height, ReadOnlySpan data) { - var lookups = data[..height]; + ReadOnlySpan lookups = data[..height]; data = data[height..]; - var xMin = width; - var yMin = height; - var xMax = -1; - var yMax = -1; + int xMin = width; + int yMin = height; + int xMax = -1; + int yMax = -1; - for (var y = 0; y < height; y++) + for (int y = 0; y < height; y++) { var i = lookups[y]; var x = 0; @@ -133,7 +133,7 @@ public class ArtData : IDisposable for (var end = x + pixelCount; x < end; x++) { // Get the pixel value from the bitmap data - var pixel = data[i++]; + ushort pixel = data[i++]; // Check if the pixel is non-black (0 in 555 RGB is completely black) if (pixel == 0) diff --git a/Projects/Server/Client/ClientType.cs b/Projects/Server/Client/ClientType.cs index 93dd51138..f7cab9168 100644 --- a/Projects/Server/Client/ClientType.cs +++ b/Projects/Server/Client/ClientType.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ClientType.cs * * * diff --git a/Projects/Server/Client/ClientVersion.cs b/Projects/Server/Client/ClientVersion.cs index bd093e9e9..008650aae 100644 --- a/Projects/Server/Client/ClientVersion.cs +++ b/Projects/Server/Client/ClientVersion.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ClientVersion.cs * * * @@ -23,12 +23,13 @@ namespace Server; public class ClientVersion : IComparable, IComparer, IEquatable { + public static readonly ClientVersion Version400a = new("4.0.0a"); + public static readonly ClientVersion Version407a = new("4.0.7a"); public static readonly ClientVersion Version500a = new("5.0.0a"); public static readonly ClientVersion Version502b = new("5.0.2b"); public static readonly ClientVersion Version6000 = new("6.0.0.0"); public static readonly ClientVersion Version6000KR = new("66.55.38"); // KR 2.44.0.15 (First release) public static readonly ClientVersion Version6017 = new("6.0.1.7"); - public static readonly ClientVersion Version6050 = new("6.0.5.0"); public static readonly ClientVersion Version60142 = new("6.0.14.2"); public static readonly ClientVersion Version60142KR = new("66.55.53"); // KR 2.59.0.2 public static readonly ClientVersion Version7000 = new("7.0.0.0"); @@ -210,7 +211,7 @@ public class ClientVersion : IComparable, IComparer((int)fs.Length, true); _ = fs.Read(buffer); // VS_VERSION_INFO (unicode) diff --git a/Projects/Server/Client/UOPEntry.cs b/Projects/Server/Client/UOPEntry.cs index 7c4501c8b..a6e9a010e 100644 --- a/Projects/Server/Client/UOPEntry.cs +++ b/Projects/Server/Client/UOPEntry.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: UOPEntry.cs * * * diff --git a/Projects/Server/Client/UOPFiles.cs b/Projects/Server/Client/UOPFiles.cs index 0b6046ad7..e52d5ed86 100644 --- a/Projects/Server/Client/UOPFiles.cs +++ b/Projects/Server/Client/UOPFiles.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: UOPFiles.cs * * * @@ -22,8 +22,7 @@ namespace Server; public static class UOPFiles { public static Dictionary ReadUOPIndexes( - FileStream stream, string fileExt, int entryCount = 0x14000, int expectedVersion = -1, int precision = 8, - Dictionary additionalHashes = null + FileStream stream, string fileExt, int entryCount = 0x14000, int expectedVersion = -1, int precision = 8 ) { var reader = new BinaryReader(stream); @@ -71,12 +70,8 @@ public static class UOPFiles var compressed = reader.ReadInt16() == 1; - if (offset == 0 || compressedLength <= 0 || decompressedLength <= 0) - { - continue; - } - - if (hashes.TryGetValue(fileNameHash, out var fileIndex)) + if (offset != 0 && hashes.TryGetValue(fileNameHash, out var fileIndex) && compressedLength > 0 && + decompressedLength > 0) { entries[fileIndex] = new UOPEntry(offset + headerLength, decompressedLength) { @@ -84,14 +79,6 @@ public static class UOPFiles CompressedSize = compressedLength }; } - else if (additionalHashes != null && additionalHashes.ContainsKey(fileNameHash)) - { - additionalHashes[fileNameHash] = new UOPEntry(offset + headerLength, decompressedLength) - { - Compressed = compressed, - CompressedSize = compressedLength - }; - } } } while (nextBlock != 0 && nextBlock < length); diff --git a/Projects/Server/Collections/ArrayEnumerator.cs b/Projects/Server/Collections/ArrayEnumerator.cs index c265d3b07..cec7abb8b 100644 --- a/Projects/Server/Collections/ArrayEnumerator.cs +++ b/Projects/Server/Collections/ArrayEnumerator.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ArrayEnumerator.cs * * * @@ -44,7 +44,7 @@ public struct ArrayEnumerator : IEnumerator public bool MoveNext() { - var localList = _array; + T[] localList = _array; if ((uint)_index < (uint)localList.Length) { diff --git a/Projects/Server/Collections/CollectionHelpers.cs b/Projects/Server/Collections/CollectionHelpers.cs index 9fb6378b6..623652d9a 100644 --- a/Projects/Server/Collections/CollectionHelpers.cs +++ b/Projects/Server/Collections/CollectionHelpers.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CollectionHelpers.cs * * * diff --git a/Projects/Server/Collections/CollectionThrowStrings.cs b/Projects/Server/Collections/CollectionThrowStrings.cs index 8374ab242..292567672 100644 --- a/Projects/Server/Collections/CollectionThrowStrings.cs +++ b/Projects/Server/Collections/CollectionThrowStrings.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CollectionThrowStrings.cs * * * diff --git a/Projects/Server/Collections/HashHelpers.cs b/Projects/Server/Collections/HashHelpers.cs index 314ff28bf..2e8f8ac8b 100644 --- a/Projects/Server/Collections/HashHelpers.cs +++ b/Projects/Server/Collections/HashHelpers.cs @@ -43,8 +43,8 @@ internal static class HashHelpers { if ((candidate & 1) != 0) { - var limit = (int)Math.Sqrt(candidate); - for (var divisor = 3; divisor <= limit; divisor += 2) + int limit = (int)Math.Sqrt(candidate); + for (int divisor = 3; divisor <= limit; divisor += 2) { if (candidate % divisor == 0) { @@ -63,9 +63,9 @@ internal static class HashHelpers throw new ArgumentException("Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table."); } - for (var i = 0; i < primes.Length; i++) + for (int i = 0; i < primes.Length; i++) { - var prime = primes[i]; + int prime = primes[i]; if (prime >= min) { return prime; @@ -74,7 +74,7 @@ internal static class HashHelpers //outside of our predefined table. //compute the hard way. - for (var i = min | 1; i < int.MaxValue; i += 2) + for (int i = min | 1; i < int.MaxValue; i += 2) { if (IsPrime(i) && (i - 1) % HashPrime != 0) { @@ -87,7 +87,7 @@ internal static class HashHelpers // Returns size of hashtable to grow to. public static int ExpandPrime(int oldSize) { - var newSize = 2 * oldSize; + int newSize = 2 * oldSize; // Allow the hashtables to grow to maximum possible size (~2G elements) before encountering capacity overflow. // Note that this check works even when _items.Length overflowed thanks to the (uint) cast @@ -116,7 +116,7 @@ internal static class HashHelpers // This is equivalent of (uint)Math.BigMul(multiplier * value, divisor, out _). This version // is faster than BigMul currently because we only need the high bits. - var highbits = (uint)(((((multiplier * value) >> 32) + 1) * divisor) >> 32); + uint highbits = (uint)(((((multiplier * value) >> 32) + 1) * divisor) >> 32); Debug.Assert(highbits == value % divisor); return highbits; diff --git a/Projects/Server/Collections/OrderedSet.cs b/Projects/Server/Collections/OrderedSet.cs index 241d78eb8..7e55db30b 100644 --- a/Projects/Server/Collections/OrderedSet.cs +++ b/Projects/Server/Collections/OrderedSet.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OrderedSet.cs * * * diff --git a/Projects/Server/Collections/PooledRefList.cs b/Projects/Server/Collections/PooledRefList.cs index dc7a80d1f..f17476a44 100644 --- a/Projects/Server/Collections/PooledRefList.cs +++ b/Projects/Server/Collections/PooledRefList.cs @@ -70,7 +70,7 @@ public ref struct PooledRefList _version = 0; _mt = mt; - var count = collection.Count; + int count = collection.Count; if (count == 0) { _items = s_emptyArray; @@ -100,7 +100,7 @@ public ref struct PooledRefList if (collection is ICollection c) { - var count = c.Count; + int count = c.Count; if (count == 0) { _items = s_emptyArray; @@ -117,7 +117,7 @@ public ref struct PooledRefList { _size = 0; _items = s_emptyArray; - using var en = collection!.GetEnumerator(); + using IEnumerator en = collection!.GetEnumerator(); while (en.MoveNext()) { Add(en.Current); @@ -143,7 +143,7 @@ public ref struct PooledRefList { if (value > 0) { - var newItems = ArrayPool.Rent(value); + T[] newItems = ArrayPool.Rent(value); if (_size > 0) { Array.Copy(_items, newItems, _size); @@ -211,8 +211,8 @@ public ref struct PooledRefList public void Add(T item) { _version++; - var array = _items; - var size = _size; + T[] array = _items; + int size = _size; if ((uint)size < (uint)array.Length) { _size = size + 1; @@ -229,7 +229,7 @@ public ref struct PooledRefList private void AddWithResize(T item) { Debug.Assert(_size == _items.Length); - var size = _size; + int size = _size; Grow(size + 1); _size = size + 1; _items[size] = item; @@ -292,7 +292,7 @@ public ref struct PooledRefList _version++; if (RuntimeHelpers.IsReferenceOrContainsReferences()) { - var size = _size; + int size = _size; _size = 0; if (size > 0) { @@ -329,8 +329,8 @@ public ref struct PooledRefList throw new ArgumentNullException(nameof(converter)); } - var list = new PooledRefList(_size); - for (var i = 0; i < _size; i++) + PooledRefList list = new PooledRefList(_size); + for (int i = 0; i < _size; i++) { list._items[i] = converter(_items[i]); } @@ -393,7 +393,7 @@ public ref struct PooledRefList { Debug.Assert(_items.Length < capacity); - var newcapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length; + int newcapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length; // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. // Note that this check works even when _items.Length overflowed thanks to the (uint) cast @@ -421,7 +421,7 @@ public ref struct PooledRefList throw new ArgumentNullException(nameof(match)); } - for (var i = 0; i < _size; i++) + for (int i = 0; i < _size; i++) { if (match(_items[i])) { @@ -438,8 +438,8 @@ public ref struct PooledRefList throw new ArgumentNullException(nameof(match)); } - var list = new PooledRefList(); - for (var i = 0; i < _size; i++) + PooledRefList list = new PooledRefList(); + for (int i = 0; i < _size; i++) { if (match(_items[i])) { @@ -470,8 +470,8 @@ public ref struct PooledRefList throw new ArgumentNullException(nameof(match)); } - var endIndex = startIndex + count; - for (var i = startIndex; i < endIndex; i++) + int endIndex = startIndex + count; + for (int i = startIndex; i < endIndex; i++) { if (match(_items[i])) { @@ -488,7 +488,7 @@ public ref struct PooledRefList throw new ArgumentNullException(nameof(match)); } - for (var i = _size - 1; i >= 0; i--) + for (int i = _size - 1; i >= 0; i--) { if (match(_items[i])) { @@ -532,8 +532,8 @@ public ref struct PooledRefList throw new ArgumentOutOfRangeException(nameof(count)); } - var endIndex = startIndex - count; - for (var i = startIndex; i > endIndex; i--) + int endIndex = startIndex - count; + for (int i = startIndex; i > endIndex; i--) { if (match(_items[i])) { @@ -550,9 +550,9 @@ public ref struct PooledRefList throw new ArgumentNullException(nameof(action)); } - var version = _version; + int version = _version; - for (var i = 0; i < _size; i++) + for (int i = 0; i < _size; i++) { if (version != _version) { @@ -591,7 +591,7 @@ public ref struct PooledRefList throw new ArgumentException("Length must be greater than zero"); } - var list = new PooledRefList(count); + PooledRefList list = new PooledRefList(count); Array.Copy(_items, index, list._items, 0, count); list._size = count; return list; @@ -694,7 +694,7 @@ public ref struct PooledRefList if (collection is ICollection c) { - var count = c.Count; + int count = c.Count; if (count > 0) { if (_items.Length - _size < count) @@ -712,7 +712,7 @@ public ref struct PooledRefList } else { - using var en = collection.GetEnumerator(); + using IEnumerator en = collection.GetEnumerator(); while (en.MoveNext()) { Insert(index++, en.Current); @@ -801,7 +801,7 @@ public ref struct PooledRefList // decreased by one. public bool Remove(T item) { - var index = IndexOf(item); + int index = IndexOf(item); if (index >= 0) { RemoveAt(index); @@ -820,7 +820,7 @@ public ref struct PooledRefList throw new ArgumentNullException(nameof(match)); } - var freeIndex = 0; // the first free slot in items array + int freeIndex = 0; // the first free slot in items array // Find the first item which needs to be removed. while (freeIndex < _size && !match(_items[freeIndex])) @@ -833,7 +833,7 @@ public ref struct PooledRefList return 0; } - var current = freeIndex + 1; + int current = freeIndex + 1; while (current < _size) { // Find the first item which needs to be kept. @@ -854,7 +854,7 @@ public ref struct PooledRefList Array.Clear(_items, freeIndex, _size - freeIndex); // Clear the elements so that the gc can reclaim the references. } - var result = _size - freeIndex; + int result = _size - freeIndex; _size = freeIndex; _version++; return result; @@ -1002,7 +1002,7 @@ public ref struct PooledRefList return s_emptyArray; } - var array = new T[_size]; + T[] array = new T[_size]; Array.Copy(_items, array, _size); return array; } @@ -1014,13 +1014,11 @@ public ref struct PooledRefList return s_emptyArray; } - var array = ArrayPool.Rent(_size); + T[] array = ArrayPool.Rent(_size); Array.Copy(_items, array, _size); return array; } - public ReadOnlySpan AsSpan() => _items.AsSpan(0, _size); - // Sets the capacity of this list to the size of the list. This method can // be used to minimize a list's memory overhead once it is known that no // new elements will be added to the list. To completely clear a list and @@ -1032,7 +1030,7 @@ public ref struct PooledRefList // public void TrimExcess() { - var threshold = (int)(_items.Length * 0.9); + int threshold = (int)(_items.Length * 0.9); if (_size < threshold) { Capacity = _size; @@ -1046,7 +1044,7 @@ public ref struct PooledRefList throw new ArgumentNullException(nameof(match)); } - for (var i = 0; i < _size; i++) + for (int i = 0; i < _size; i++) { if (!match(_items[i])) { @@ -1079,7 +1077,7 @@ public ref struct PooledRefList public bool MoveNext() { - var localList = _list; + PooledRefList localList = _list; if (_version == localList._version && (uint)_index < (uint)localList._size) { diff --git a/Projects/Server/Collections/PooledRefQueue.cs b/Projects/Server/Collections/PooledRefQueue.cs index 70da5f099..deddb67de 100644 --- a/Projects/Server/Collections/PooledRefQueue.cs +++ b/Projects/Server/Collections/PooledRefQueue.cs @@ -97,13 +97,13 @@ public ref struct PooledRefQueue throw new ArgumentException(CollectionThrowStrings.Argument_InvalidOffLen); } - var numToCopy = _size; + int numToCopy = _size; if (numToCopy == 0) { return; } - var firstPart = Math.Min(_array.Length - _head, numToCopy); + int firstPart = Math.Min(_array.Length - _head, numToCopy); Array.Copy(_array, _head, array, arrayIndex, firstPart); numToCopy -= firstPart; if (numToCopy > 0) @@ -135,15 +135,15 @@ public ref struct PooledRefQueue // InvalidOperationException. public T Dequeue() { - var head = _head; - var array = _array; + int head = _head; + T[] array = _array; if (_size == 0) { ThrowForEmptyQueue(); } - var removed = array[head]; + T removed = array[head]; if (RuntimeHelpers.IsReferenceOrContainsReferences()) { array[head] = default!; @@ -156,8 +156,8 @@ public ref struct PooledRefQueue public bool TryDequeue([MaybeNullWhen(false)] out T result) { - var head = _head; - var array = _array; + int head = _head; + T[] array = _array; if (_size == 0) { @@ -248,7 +248,7 @@ public ref struct PooledRefQueue return s_emptyArray; } - var arr = new T[_size]; + T[] arr = new T[_size]; if (_head < _tail) { @@ -270,7 +270,7 @@ public ref struct PooledRefQueue return s_emptyArray; } - var arr = (mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(_size); + T[] arr = (mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(_size); if (_head < _tail) { @@ -289,7 +289,7 @@ public ref struct PooledRefQueue // must be >= _size. private void SetCapacity(int capacity) { - var newarray = (_mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(capacity); + T[] newarray = (_mt ? ArrayPool.Shared : STArrayPool.Shared).Rent(capacity); if (_size > 0) { if (_head < _tail) @@ -321,7 +321,7 @@ public ref struct PooledRefQueue // It is tempting to use the remainder operator here but it is actually much slower // than a simple comparison and a rarely taken branch. // JIT produces better code than with ternary operator ?: - var tmp = index + 1; + int tmp = index + 1; if (tmp == _array.Length) { tmp = 0; @@ -359,7 +359,7 @@ public ref struct PooledRefQueue const int GrowFactor = 2; const int MinimumGrow = 4; - var newcapacity = GrowFactor * _array.Length; + int newcapacity = GrowFactor * _array.Length; // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. // Note that this check works even when _items.Length overflowed thanks to the (uint) cast @@ -441,13 +441,13 @@ public ref struct PooledRefQueue } // Cache some fields in locals to decrease code size - var array = _q._array; - var capacity = array.Length; + T[] array = _q._array; + int capacity = array.Length; // _index represents the 0-based index into the queue, however the queue // doesn't have to start from 0 and it may not even be stored contiguously in memory. - var arrayIndex = _q._head + _index; // this is the actual index into the queue's backing array + int arrayIndex = _q._head + _index; // this is the actual index into the queue's backing array if (arrayIndex >= capacity) { // NOTE: Originally we were using the modulo operator here, however diff --git a/Projects/Server/Collections/SortedRangeIndex.cs b/Projects/Server/Collections/SortedRangeIndex.cs deleted file mode 100644 index 5af37fd59..000000000 --- a/Projects/Server/Collections/SortedRangeIndex.cs +++ /dev/null @@ -1,134 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SortedRangeIndex.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Numerics; - -namespace Server.Collections; - -/// -/// Immutable, allocation-lean membership index over a set of inclusive integer ranges. Ranges are -/// stored as two parallel arrays (_mins/_maxs) sorted by minimum and coalesced into -/// disjoint runs, so a single binary search decides membership. Coalescing is required for -/// correctness: only inspects the rightmost run whose minimum is <= the -/// value, which is only sound when the runs never overlap or nest. -/// -public sealed class SortedRangeIndex where T : IBinaryInteger -{ - public static readonly SortedRangeIndex Empty = new([], []); - - /// An inclusive [Min, Max] range. Singles are represented as Min == Max. - public readonly record struct Range(T Min, T Max); - - /// Orders ranges ascending by minimum; the coalescing pass in requires this. - public static readonly Comparison ByMin = static (a, b) => a.Min.CompareTo(b.Min); - - private readonly T[] _mins; - private readonly T[] _maxs; - - private SortedRangeIndex(T[] mins, T[] maxs) - { - _mins = mins; - _maxs = maxs; - } - - public int Count => _mins.Length; - - /// - /// True when falls in any range. Binary-searches for the rightmost run - /// whose minimum is <= the value, then tests that value against that run's maximum. - /// - public bool Contains(T value) - { - var lo = 0; - var hi = _mins.Length - 1; - var found = -1; - while (lo <= hi) - { - var mid = (lo + hi) >> 1; - if (_mins[mid] <= value) - { - found = mid; - lo = mid + 1; - } - else - { - hi = mid - 1; - } - } - - return found >= 0 && value <= _maxs[found]; - } - - /// - /// Builds an index from ranges that are already sorted ascending by (sort - /// the source with first). Overlapping and nested ranges are merged into disjoint - /// runs. Two passes over the (pooled) input keep the run count exact so only the two final arrays are - /// heap-allocated: pass one counts the runs, pass two fills the exact-size arrays. - /// - public static SortedRangeIndex Build(ReadOnlySpan sortedByMin) - { - if (sortedByMin.IsEmpty) - { - return Empty; - } - - // Pass 1: count the disjoint runs so the final arrays can be sized exactly. - var runs = 1; - var curMax = sortedByMin[0].Max; - for (var i = 1; i < sortedByMin.Length; i++) - { - var r = sortedByMin[i]; - if (r.Min <= curMax) - { - if (r.Max > curMax) - { - curMax = r.Max; - } - } - else - { - runs++; - curMax = r.Max; - } - } - - // Pass 2: write the coalesced runs into the exact-size final arrays. - var mins = new T[runs]; - var maxs = new T[runs]; - var w = 0; - mins[0] = sortedByMin[0].Min; - maxs[0] = sortedByMin[0].Max; - for (var i = 1; i < sortedByMin.Length; i++) - { - var r = sortedByMin[i]; - if (r.Min <= maxs[w]) - { - if (r.Max > maxs[w]) - { - maxs[w] = r.Max; - } - } - else - { - w++; - mins[w] = r.Min; - maxs[w] = r.Max; - } - } - - return new SortedRangeIndex(mins, maxs); - } -} diff --git a/Projects/Server/Collections/ValueLinkList.cs b/Projects/Server/Collections/ValueLinkList.cs index 5832c5801..0accf691f 100644 --- a/Projects/Server/Collections/ValueLinkList.cs +++ b/Projects/Server/Collections/ValueLinkList.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ValueLinkList.cs * * * diff --git a/Projects/Server/Commands.cs b/Projects/Server/Commands.cs index e5cbe45cf..6819dfe91 100644 --- a/Projects/Server/Commands.cs +++ b/Projects/Server/Commands.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Commands.cs * * * @@ -41,8 +41,6 @@ public class CommandEventArgs public string[] Arguments { get; } - private Dictionary _context; - public int Length => Arguments.Length; public string GetString(int index) => index < 0 || index >= Arguments.Length ? "" : Arguments[index]; @@ -59,25 +57,6 @@ public class CommandEventArgs public TimeSpan GetTimeSpan(int index) => index < 0 || index >= Arguments.Length ? TimeSpan.Zero : Utility.ToTimeSpan(Arguments[index]); - - public bool GetContext(string key, out T t) - { - if (_context != null && _context.TryGetValue(key, out var value) && value is T tValue) - { - t = tValue; - return true; - } - - t = default; - return false; - } - - public T SetContext(string key, T value) - { - _context ??= new Dictionary(); - _context[key] = value; - return value; - } } public static partial class EventSink diff --git a/Projects/Server/Comparers/DescendingComparer.cs b/Projects/Server/Comparers/DescendingComparer.cs index 5a1989d65..67bd2449f 100644 --- a/Projects/Server/Comparers/DescendingComparer.cs +++ b/Projects/Server/Comparers/DescendingComparer.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: DescendingComparer.cs * * * diff --git a/Projects/Server/Comparers/LocationComparer.cs b/Projects/Server/Comparers/LocationComparer.cs index 6e48e2877..707c73f42 100644 --- a/Projects/Server/Comparers/LocationComparer.cs +++ b/Projects/Server/Comparers/LocationComparer.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: LocationComparer.cs * * * diff --git a/Projects/Server/Compression/Deflate.cs b/Projects/Server/Compression/Deflate.cs index 7f4bc0dcf..deec8eb18 100644 --- a/Projects/Server/Compression/Deflate.cs +++ b/Projects/Server/Compression/Deflate.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Deflate.cs * * * @@ -23,11 +23,5 @@ public static class Deflate [ThreadStatic] private static LibDeflateBinding _standard; - [ThreadStatic] - private static LibDeflateBinding _maximum; - public static LibDeflateBinding Standard => _standard ??= new LibDeflateBinding(); - - // Best-ratio compressor, cached per thread like Standard (construction allocates native state). - public static LibDeflateBinding Maximum => _maximum ??= new LibDeflateBinding(LibDeflateCompressionLevel.VeryHigh); } diff --git a/Projects/Server/Configuration/ExpansionConfigurationPrompts.cs b/Projects/Server/Configuration/ExpansionConfigurationPrompts.cs index 44881716f..54d508b08 100644 --- a/Projects/Server/Configuration/ExpansionConfigurationPrompts.cs +++ b/Projects/Server/Configuration/ExpansionConfigurationPrompts.cs @@ -23,7 +23,7 @@ public static class ExpansionConfigurationPrompts do { Console.Write("[enter for {0}]> ", maxExpansionName); - var input = ConsoleInputHandler.ReadLine(); + var input = Console.ReadLine(); Expansion expansion; if (string.IsNullOrWhiteSpace(input)) @@ -77,7 +77,7 @@ public static class ExpansionConfigurationPrompts do { OutputSelectedMaps(expansion, selectedMaps); - lastInput = ConsoleInputHandler.ReadLine()?.TrimEnd(); + lastInput = Console.ReadLine()?.TrimEnd(); if (string.IsNullOrWhiteSpace(lastInput)) { diff --git a/Projects/Server/Configuration/ServerConfiguration.cs b/Projects/Server/Configuration/ServerConfiguration.cs index c70b060e4..e5d318996 100644 --- a/Projects/Server/Configuration/ServerConfiguration.cs +++ b/Projects/Server/Configuration/ServerConfiguration.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ServerConfiguration.cs * * * @@ -44,7 +44,7 @@ public static class ServerConfiguration _settings.Settings.TryGetValue(key, out var value) ? new ClientVersion(value) : defaultValue; public static string GetSetting(string key, string defaultValue) => - _settings.Settings.GetValueOrDefault(key, defaultValue); + _settings.Settings.TryGetValue(key, out var value) ? value : defaultValue; public static int GetSetting(string key, int defaultValue) { @@ -218,13 +218,11 @@ public static class ServerConfiguration Save(); } - // Reads (or creates) the configuration file. The interactive first-boot prompts live in - // ConfigurePrompts (run later via AssemblyHandler.Invoke("ConfigurePrompts")) so all - // first-boot prompting — engine and content — shares one phase/wiring. mocked skips prompts - // entirely (ConfigurePrompts is gated on m_Mocked and isn't invoked by the test fixtures). + // If mock is enabled we skip the console readline. public static void Load(bool mocked = false) { m_Mocked = mocked; + var updated = false; if (File.Exists(m_FilePath)) { @@ -241,27 +239,15 @@ public static class ServerConfiguration } else { + updated = true; _settings = new ServerSettings(); } - } - // First-boot interactive configuration, discovered + run by AssemblyHandler.Invoke( - // "ConfigurePrompts") after assemblies load but before Serilog's first log line, so the - // console prompts are not interleaved with the async console sink (see - // dev-docs/server-lifecycle.md). CallPriority(0) so the engine's own prompts (data dirs, - // listeners, server name, expansion + map selection) run before any content ConfigurePrompts - // that build on them (e.g. map selection before a pathfinding pre-bake prompt). Also resolves - // Core.Expansion on every non-mocked boot. - [CallPriority(0)] - public static void ConfigurePrompts() - { - if (m_Mocked) + if (mocked) { return; } - var updated = false; - if (_settings.DataDirectories.Count == 0) { updated = true; @@ -285,12 +271,6 @@ public static class ServerConfiguration _settings.Listeners.AddRange(ServerConfigurationPrompts.GetListeners()); } - if (!_settings.Settings.ContainsKey("serverListing.serverName")) - { - updated = true; - _settings.Settings["serverListing.serverName"] = ServerConfigurationPrompts.GetServerName(); - } - // We have a known, current expansion, so we can deserialize it from Configuration if (!ExpansionInfo.LoadConfiguration(out var currentExpansion)) { diff --git a/Projects/Server/Configuration/ServerConfigurationPrompts.cs b/Projects/Server/Configuration/ServerConfigurationPrompts.cs index ebd41e907..b1094211a 100644 --- a/Projects/Server/Configuration/ServerConfigurationPrompts.cs +++ b/Projects/Server/Configuration/ServerConfigurationPrompts.cs @@ -7,6 +7,81 @@ namespace Server; public static class ServerConfigurationPrompts { + internal static bool GetIsClient7090() + { + if (UOClient.ServerClientVersion != null) + { + return UOClient.ServerClientVersion >= ClientVersion.Version7090; + } + + Console.WriteLine("Will you be using a client version 7.0.9.0 or newer?"); + + do + { + Console.Write("[y] or n> "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || input.InsensitiveStartsWith("y")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client >= 7.0.9.0 chosen."); + Utility.PopColor(); + return true; + } + + if (input.InsensitiveStartsWith("n")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client < 7.0.9.0 chosen."); + Utility.PopColor(); + return false; + } + + Console.Write("Invalid option "); + Utility.PushColor(ConsoleColor.Red); + Console.Write(input); + Utility.PopColor(); + Console.WriteLine(". Press y for yes or n for no."); + } while (true); + } + + + internal static bool GetIsClientPre6000() + { + if (UOClient.ServerClientVersion != null) + { + return UOClient.ServerClientVersion < ClientVersion.Version6000; + } + + Console.WriteLine("Will you be using a client version older than 6.0.0.0?"); + + do + { + Console.Write("y or [n]> "); + var input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || input.InsensitiveStartsWith("n")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client >= 6.0.0.0 chosen."); + Utility.PopColor(); + return false; + } + + if (input.InsensitiveStartsWith("y")) + { + Utility.PushColor(ConsoleColor.Yellow); + Console.WriteLine("Client < 6.0.0.0 chosen."); + Utility.PopColor(); + return true; + } + + Console.Write("Invalid option "); + Utility.PushColor(ConsoleColor.Red); + Console.Write(input); + Utility.PopColor(); + Console.WriteLine(". Press y for yes or n for no."); + } while (true); + } + internal static List GetDataDirectories() { Console.WriteLine("Please enter the absolute path to your ClassicUO or Ultima Online data:"); @@ -16,7 +91,7 @@ public static class ServerConfigurationPrompts do { Console.Write("{0}> ", directories.Count > 0 ? "[enter to finish]" : " "); - var directory = ConsoleInputHandler.ReadLine(); + var directory = Console.ReadLine(); if (string.IsNullOrWhiteSpace(directory)) { break; @@ -55,7 +130,7 @@ public static class ServerConfigurationPrompts { // IP:Port? Console.Write("[{0}]> ", ips.Count > 0 ? "enter to finish" : "0.0.0.0:2593"); - var ipStr = ConsoleInputHandler.ReadLine(); + var ipStr = Console.ReadLine(); IPEndPoint ip; if (string.IsNullOrWhiteSpace(ipStr)) @@ -93,31 +168,4 @@ public static class ServerConfigurationPrompts } while (true); return ips; } - - internal static string GetServerName() - { - Console.WriteLine("Please enter the name of your shard:"); - - string serverName; - do - { - Console.Write("[ModernUO]> "); - serverName = ConsoleInputHandler.ReadLine(); - - if (string.IsNullOrWhiteSpace(serverName)) - { - serverName = "ModernUO"; - } - - break; - } while (true); - - Console.Write("Server name set to "); - Utility.PushColor(ConsoleColor.Green); - Console.Write(serverName); - Utility.PopColor(); - Console.WriteLine("."); - - return serverName; - } } diff --git a/Projects/Server/Configuration/ServerSettings.cs b/Projects/Server/Configuration/ServerSettings.cs index 40a65fdb3..58ba59cfc 100644 --- a/Projects/Server/Configuration/ServerSettings.cs +++ b/Projects/Server/Configuration/ServerSettings.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ServerSettings.cs * * * diff --git a/Projects/Server/Console/ConsoleInputHandler.cs b/Projects/Server/Console/ConsoleInputHandler.cs index 0cd091110..3c621085b 100644 --- a/Projects/Server/Console/ConsoleInputHandler.cs +++ b/Projects/Server/Console/ConsoleInputHandler.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ConsoleInputHandler.cs * * * @@ -24,9 +24,13 @@ namespace Server; public static class ConsoleInputHandler { - private static ConsoleInputPump _pump; + private static readonly AutoResetEvent _receivedUserInput = new(false); + private static readonly AutoResetEvent _endUserInput = new(false); + private static bool _initialized; + private static bool _expectUserInput; private static readonly Dictionary _inputCommands = new(); private static string[] _commandDescriptions; + private static string _input; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void RegisterCommand(string command, string description, Action function) => @@ -69,6 +73,7 @@ public static class ConsoleInputHandler lock (_inputCommands) { var action = _inputCommands.GetValueOrDefault(command)?.Function; + _commandDescriptions = null; return action; } } @@ -81,15 +86,9 @@ public static class ConsoleInputHandler [CallPriority(0)] public static void Initialize() { - if (Core.Headless) - { - logger.Information("Console input disabled (headless: stdin is not a TTY)."); - return; - } + _initialized = true; - _pump = new ConsoleInputPump(Console.In, GetInputCommand, logger); - - new Thread(_pump.Run) + new Thread(ProcessConsoleInput) { IsBackground = true, Name = "Console Input Handler" @@ -151,21 +150,69 @@ public static class ConsoleInputHandler private static readonly ILogger logger = LogFactory.GetLogger(typeof(ConsoleInputHandler)); + private static async void ProcessConsoleInput() + { + while (!Core.Closing) + { + string input; + try + { + input = Console.ReadLine()?.Trim(); + } + catch + { + logger.Warning("Console commands have been disabled due to an error."); + _initialized = false; + return; + } + + if (Volatile.Read(ref _expectUserInput)) + { + _input = input; + _receivedUserInput.Set(); + _endUserInput.WaitOne(); + continue; + } + + if (string.IsNullOrEmpty(input)) + { + continue; + } + + var splitInput = input.Split(' ', 2); + var command = splitInput[0].ToLower(); + + try + { + Action action; + lock (_inputCommands) + { + action = _inputCommands.GetValueOrDefault(command)?.Function; + } + + action?.Invoke(splitInput.Length > 1 ? splitInput[1] : string.Empty); + } + catch (Exception e) + { + logger.Error(e, "Failed to execute console command: {Command}", input); + } + } + } + public static string ReadLine() { - if (Core.Headless) - { - throw new HeadlessConsoleInputException("ConsoleInputHandler.ReadLine"); - } - - // Early startup (before Initialize) or after the loop ended: read directly. - var pump = _pump; - if (pump is not { Running: true }) + if (!_initialized) { return Console.ReadLine(); } - return pump.ReadLine(); + Volatile.Write(ref _expectUserInput, true); + _receivedUserInput.WaitOne(); + var line = _input; + Volatile.Write(ref _expectUserInput, false); + _endUserInput.Set(); + + return line; } private class ConsoleCommand(string[] commands, string description, Action function) diff --git a/Projects/Server/Console/ConsoleInputPump.cs b/Projects/Server/Console/ConsoleInputPump.cs deleted file mode 100644 index b18ffccc1..000000000 --- a/Projects/Server/Console/ConsoleInputPump.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using Server.Logging; - -namespace Server; - -/// -/// Owns a console input stream. Each line read is atomically either delivered to a -/// waiting caller (a prompt) or dispatched as a command via the -/// supplied lookup. EOF ends the loop instead of spinning. Correct-by-construction: -/// the prompt-vs-command decision is made under a single lock at the moment a line is read. -/// Command handlers dispatched by execute on the reader thread and -/// must never call — doing so would deadlock the pump (the reader -/// thread would be blocked waiting on itself to read the next line). -/// -internal sealed class ConsoleInputPump -{ - private readonly TextReader _input; - private readonly Func> _lookup; - private readonly Server.Logging.ILogger _logger; - private readonly Lock _gate = new(); - private readonly AutoResetEvent _promptDelivered = new(false); - - private bool _promptPending; - private string _promptResult; - private volatile bool _running = true; - - public ConsoleInputPump(TextReader input, Func> lookup, Server.Logging.ILogger logger = null) - { - _input = input ?? throw new ArgumentNullException(nameof(input)); - _lookup = lookup ?? throw new ArgumentNullException(nameof(lookup)); - _logger = logger; - } - - public bool Running => _running; - - // Test-observable: true while a ReadLine() caller is registered and waiting for the - // next line. Lets tests synchronize on the rendezvous state instead of sleeping. - internal bool HasPendingPrompt - { - get - { - lock (_gate) - { - return _promptPending; - } - } - } - - public void Run() - { - try - { - while (_running && !Core.Closing) - { - string line; - try - { - line = _input.ReadLine(); - } - catch - { - _logger?.Warning("Console commands have been disabled due to an error."); - break; - } - - if (line == null) - { - break; // EOF — never spin - } - - bool isCommand; - lock (_gate) - { - if (_promptPending) - { - _promptResult = line; - _promptPending = false; - _promptDelivered.Set(); - isCommand = false; - } - else - { - isCommand = true; - } - } - - if (!isCommand) - { - continue; - } - - var trimmed = line.Trim(); - if (trimmed.Length == 0) - { - continue; - } - - var split = trimmed.Split(' ', 2); - - try - { - var action = _lookup(split[0].ToLower()); - action?.Invoke(split.Length > 1 ? split[1] : string.Empty); - } - catch (Exception e) - { - _logger?.Error(e, "Failed to execute console command: {Command}", line); - } - } - } - finally - { - _running = false; - ReleasePendingPrompt(null); - } - } - - /// - /// Blocks the calling thread until the next console line is available, or until the - /// pump stops (returning null). Intended for a single, sequential caller at a - /// time — ModernUO's console prompts run one after another during startup/steps. - /// Concurrent callers are not supported: a second caller overlapping with a pending - /// prompt will race with it for the next line. Must not be called from the reader - /// thread (i.e. from within a command handler dispatched by ), as - /// that would deadlock the pump. - /// - public string ReadLine() - { - lock (_gate) - { - if (!_running) - { - return null; - } - - _promptResult = null; - _promptPending = true; - } - - _promptDelivered.WaitOne(); - - lock (_gate) - { - return _promptResult; - } - } - - private void ReleasePendingPrompt(string result) - { - lock (_gate) - { - if (_promptPending) - { - _promptResult = result; - _promptPending = false; - _promptDelivered.Set(); - } - } - } -} diff --git a/Projects/Server/Console/HeadlessConsoleInputException.cs b/Projects/Server/Console/HeadlessConsoleInputException.cs deleted file mode 100644 index ff02fcc94..000000000 --- a/Projects/Server/Console/HeadlessConsoleInputException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace Server; - -/// -/// Thrown when interactive console input is required but the server is running -/// headless (stdin is not a TTY). Rides the fatal-shutdown path. -/// -public sealed class HeadlessConsoleInputException : Exception -{ - public HeadlessConsoleInputException(string context) - : base( - $"Interactive console input required but the server is headless (stdin is not a TTY). " + - $"Pre-supply the required configuration/save data. Prompt: {context}." - ) - { - } -} diff --git a/Projects/Server/ContextMenus/ContextMenu.cs b/Projects/Server/ContextMenus/ContextMenu.cs index 3254c9c5a..ea5b4f620 100644 --- a/Projects/Server/ContextMenus/ContextMenu.cs +++ b/Projects/Server/ContextMenus/ContextMenu.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ContextMenu.cs * * * diff --git a/Projects/Server/ContextMenus/ContextMenuEntry.cs b/Projects/Server/ContextMenus/ContextMenuEntry.cs index 7bb409d74..96088b6d7 100644 --- a/Projects/Server/ContextMenus/ContextMenuEntry.cs +++ b/Projects/Server/ContextMenus/ContextMenuEntry.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ContextMenuEntry.cs * * * diff --git a/Projects/Server/Diagnostics/EventLoopProfiler.cs b/Projects/Server/Diagnostics/EventLoopProfiler.cs deleted file mode 100644 index 615a84afd..000000000 --- a/Projects/Server/Diagnostics/EventLoopProfiler.cs +++ /dev/null @@ -1,234 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: EventLoopProfiler.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace Server; - -public enum LoopPhase -{ - MobileDeltas, - ItemDeltas, - TimerSlice, - NetworkSlice, - LoopTasks, - WorldSnapshot, -} - -/// -/// Event-loop time accounting, compiled out of normal builds. Build with -/// -p:EventLoopProfiling=true to enable; every hook is -/// [Conditional("EVENT_LOOP_PROFILING")], so without the flag the call sites do not exist -/// in the IL and this class is dormant. See dev-docs/debugging-event-loop.md for how to read it. -/// -/// -/// Each one-second sample decomposes wall time into work (per ), sleep, -/// GC pause, and a stolen residual (wall - work - sleep): time the host ran something else. -/// Samples land in a ring buffer (~15 minutes) so a lag episode can be compared against the good -/// minutes on the same box, build, and world — the baseline RunUO's profiler never had. -/// -public static class EventLoopProfiler -{ - public const int PhaseCount = 6; - private const int RingSize = 900; - private const long SampleIntervalMs = 1000; - - public struct Sample - { - public long WallStart; // Core.TickCount at sample start - public long WallMs; // sample length - public long Iterations; - public long Sleeps; - public double SleepMs; // total time blocked in WaitForCompletion - public double SleepOvershootMaxMs; // worst (elapsed - requested) this sample - public long LateWakes; // overshoot >= Timer.TickRate - public long WheelLagMaxMs; // worst wheel lateness observed at Slice entry - public long WakesIssued; - public long WakesElided; - public double GcPauseMs; // GC.GetTotalPauseDuration delta - public int Gen0; - public int Gen1; - public int Gen2; - public PhaseTimes Phases; - - // Work the phases did not account for and the loop did not spend sleeping: host - // scheduling steals, and anything between the bracketed phases. GC pauses inside a - // phase or sleep inflate those measurements instead, so GcPauseMs overlaps rather - // than subtracts. - public double StolenMs - { - get - { - var known = SleepMs + Phases.Total; - return WallMs > known ? WallMs - known : 0; - } - } - } - - [InlineArray(PhaseCount)] - public struct PhaseTimes - { - private double _element0; - - public double Total - { - get - { - double total = 0; - for (var i = 0; i < PhaseCount; i++) - { - total += this[i]; - } - - return total; - } - } - } - - private static readonly double _msPerTick = 1000.0 / Stopwatch.Frequency; - - private static Sample[] _ring; - private static int _ringCount; - private static int _ringHead; - - private static Sample _current; - private static long _phaseStartTimestamp; - private static long _sampleStartedAt; - private static TimeSpan _lastGcPause; - private static int _lastGen0; - private static int _lastGen1; - private static int _lastGen2; - - /// Number of samples recorded so far (capped at the ring size). - public static int SampleCount => _ringCount; - - /// The sample currently being accumulated (not yet in the ring). - public static Sample Current => _current; - - /// - /// Copies the newest completed samples, oldest first. - /// - public static Sample[] History(int count = RingSize) - { - count = Math.Min(count, _ringCount); - var result = new Sample[count]; - for (var i = 0; i < count; i++) - { - result[i] = _ring[(_ringHead - count + i + RingSize) % RingSize]; - } - - return result; - } - - [Conditional("EVENT_LOOP_PROFILING")] - public static void IterationStart(long tickCount) - { - if (_ring == null) - { - _ring = new Sample[RingSize]; - _sampleStartedAt = tickCount; - _current.WallStart = tickCount; - _lastGcPause = GC.GetTotalPauseDuration(); - _lastGen0 = GC.CollectionCount(0); - _lastGen1 = GC.CollectionCount(1); - _lastGen2 = GC.CollectionCount(2); - } - - _current.Iterations++; - - if (tickCount - _sampleStartedAt < SampleIntervalMs) - { - return; - } - - _current.WallMs = tickCount - _sampleStartedAt; - - var pause = GC.GetTotalPauseDuration(); - _current.GcPauseMs = (pause - _lastGcPause).TotalMilliseconds; - _lastGcPause = pause; - - var gen0 = GC.CollectionCount(0); - var gen1 = GC.CollectionCount(1); - var gen2 = GC.CollectionCount(2); - _current.Gen0 = gen0 - _lastGen0; - _current.Gen1 = gen1 - _lastGen1; - _current.Gen2 = gen2 - _lastGen2; - _lastGen0 = gen0; - _lastGen1 = gen1; - _lastGen2 = gen2; - - _ring[_ringHead] = _current; - _ringHead = (_ringHead + 1) % RingSize; - if (_ringCount < RingSize) - { - _ringCount++; - } - - _sampleStartedAt = tickCount; - _current = default; - _current.WallStart = tickCount; - } - - [Conditional("EVENT_LOOP_PROFILING")] - public static void PhaseStart(LoopPhase phase) => _phaseStartTimestamp = Stopwatch.GetTimestamp(); - - [Conditional("EVENT_LOOP_PROFILING")] - public static void PhaseEnd(LoopPhase phase) => - _current.Phases[(int)phase] += (Stopwatch.GetTimestamp() - _phaseStartTimestamp) * _msPerTick; - - [Conditional("EVENT_LOOP_PROFILING")] - public static void SleepEnd(int requestedMs, long elapsedMs) - { - _current.Sleeps++; - _current.SleepMs += elapsedMs; - - var overshoot = elapsedMs - requestedMs; - if (overshoot > _current.SleepOvershootMaxMs) - { - _current.SleepOvershootMaxMs = overshoot; - } - - if (overshoot >= Timer.TickRate) - { - _current.LateWakes++; - } - } - - [Conditional("EVENT_LOOP_PROFILING")] - public static void WheelSlice(long deltaSinceTurn) - { - var lag = deltaSinceTurn - Timer.TickRate; - if (lag > _current.WheelLagMaxMs) - { - _current.WheelLagMaxMs = lag; - } - } - - // Cross-thread; approximate counts are fine for diagnosis, so no interlocked. - [Conditional("EVENT_LOOP_PROFILING")] - public static void WakeSignal(bool elided) - { - if (elided) - { - _current.WakesElided++; - } - else - { - _current.WakesIssued++; - } - } -} diff --git a/Projects/Server/Effects.cs b/Projects/Server/Effects.cs index 3f41c5aa8..938413701 100644 --- a/Projects/Server/Effects.cs +++ b/Projects/Server/Effects.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Effects.cs * * * @@ -72,7 +72,7 @@ public static class Effects if (map != null) { - var buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); foreach (var state in map.GetClientsInRange(p)) { @@ -94,9 +94,9 @@ public static class Effects e.ProcessDelta(); - var preEffect = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength].InitializePacket(); - var boltEffect = stackalloc byte[OutgoingEffectPackets.BoltEffectLength].InitializePacket(); - var soundEffect = sound ? stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket() : null; + Span preEffect = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength].InitializePacket(); + Span boltEffect = stackalloc byte[OutgoingEffectPackets.BoltEffectLength].InitializePacket(); + Span soundEffect = sound ? stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket() : null; foreach (var state in map.GetClientsInRange(e.Location)) { @@ -131,7 +131,7 @@ public static class Effects Point3D p, Map map, int itemID, int duration, int speed = 10, int hue = 0, int renderMode = 0 ) { - var effect = stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket(); + Span effect = stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket(); OutgoingEffectPackets.CreateLocationHuedEffect( effect, p, itemID, speed, duration, hue, renderMode @@ -161,9 +161,9 @@ public static class Effects return; } - var particles = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength].InitializePacket(); + Span particles = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength].InitializePacket(); - var regular = itemID != 0 ? stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket() : null; + Span regular = itemID != 0 ? stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket() : null; foreach (var state in map.GetClientsInRange(e.Location)) { @@ -192,7 +192,7 @@ public static class Effects { (target as Mobile)?.ProcessDelta(); - var effect = stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket(); + Span effect = stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket(); OutgoingEffectPackets.CreateTargetHuedEffect( effect, target, itemID, speed, duration, hue, renderMode @@ -223,8 +223,8 @@ public static class Effects return; } - var particles = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength].InitializePacket(); - var regular = itemID != 0 ? stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket() : null; + Span particles = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength].InitializePacket(); + Span regular = itemID != 0 ? stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket() : null; foreach (var state in map.GetClientsInRange(target.Location)) { @@ -363,7 +363,7 @@ public static class Effects int speed, int duration, bool fixedDirection = false, bool explodes = false, int hue = 0, int renderMode = 0 ) { - var effect = stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket(); + Span effect = stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket(); OutgoingEffectPackets.CreateMovingHuedEffect( effect, from, to, itemID, fromLocation, toLocation, speed, duration, fixedDirection, @@ -435,8 +435,8 @@ public static class Effects return; } - var particles = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength].InitializePacket(); - var regular = itemID != 0 ? stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket() : null; + Span particles = stackalloc byte[OutgoingEffectPackets.ParticleEffectLength].InitializePacket(); + Span regular = itemID != 0 ? stackalloc byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket() : null; foreach (var state in map.GetClientsInRange(from.Location)) { diff --git a/Projects/Server/EntityFinalizationTracker.cs b/Projects/Server/EntityFinalizationTracker.cs deleted file mode 100644 index 6355cbe6c..000000000 --- a/Projects/Server/EntityFinalizationTracker.cs +++ /dev/null @@ -1,177 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: EntityFinalizationTracker.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using Server.Logging; - -namespace Server; - -public static class EntityFinalizationTracker -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(EntityFinalizationTracker)); - - private sealed record TrackedEntity(WeakReference Reference, int ReferenceHash, DateTime RemovedAt); - - private static readonly Lock _sync = new(); - private static bool _enabled; - private static DateTime _nextCheck = DateTime.MinValue; - -#if TRACK_LEAKS - private const bool CanBeEnabled = true; -#else - private const bool CanBeEnabled = false; -#endif - - public static void Configure() - { - CommandSystem.Register("gc", AccessLevel.Administrator, _ => GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true, compacting: true)); - CommandSystem.Register("TrackLeaks", AccessLevel.Developer, TrackLeaks_OnCommand); - } - - [Usage("TrackLeaks ")] - [Description("Enables or disables entity leak tracking. May impact performance and should be used only when necessary!")] - private static void TrackLeaks_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - if (!CanBeEnabled) { - from.SendMessage("Entity leak tracking is not enabled in this build. Rebuild with TRACK_LEAKS defined."); - return; - } - - if (e.Arguments.Length == 0) - { - from.SendMessage("Usage: TrackLeaks "); - return; - } - - var enable = Utility.ToBoolean(e.Arguments[0]); - var status = enable ? "enabled" : "disabled"; - if (enable == _enabled) - { - from.SendMessage($"Entity leak tracking already {status}."); - return; - } - - if (_enabled) - { - EnableLeakTracking(from); - } - else - { - DisableLeakTracking(from); - } - } - - private static void EnableLeakTracking(Mobile from) - { - if (_enabled) - { - return; - } - - _enabled = true; - Gen2GcCallback.Register(() => - { - CheckLeaks(); - return _enabled; - }); - - from.SendMessage("Entity leak tracking enabled."); - logger.Warning("Entity leak tracking enabled by {Name} ({Serial:X8})", from.Name, from.Serial); - } - - private static void DisableLeakTracking(Mobile from) - { - if (!_enabled) - { - return; - } - - _enabled = false; - from.SendMessage("Entity leak tracking disabled."); - logger.Warning("Entity leak tracking disabled by {Name} ({Serial:X8})", from.Name, from.Serial); - } - - private static readonly List _entities = []; - private static readonly HashSet _finalizedHashes = []; - - public static void TrackEntity(T entity) where T : IEntity - { - lock (_sync) - { - var hash = RuntimeHelpers.GetHashCode(entity); - _entities.Add(new TrackedEntity(new WeakReference(entity), hash, Core.Now)); - } - } - - public static void NotifyFinalized(object entity) - { - lock (_sync) - { - _finalizedHashes.Add(RuntimeHelpers.GetHashCode(entity)); - } - } - - private static void CheckLeaks() - { - if (_entities.Count == 0) - { - return; - } - - var now = Core.Now; - if (now <= _nextCheck) - { - return; - } - - _nextCheck = now + TimeSpan.FromMinutes(2); - - _entities.RemoveAll(entry => - { - if (_finalizedHashes.Remove(entry.ReferenceHash)) - { - return true; - } - - if (!entry.Reference.TryGetTarget(out var obj) || obj is not IEntity entity) - { - return true; - } - - if (ExceptionToLeakCheck(entity)) - { - return false; - } - - var duration = now - entry.RemovedAt; - - if (duration <= TimeSpan.FromSeconds(120)) - { - return false; - } - - logger.Warning("[Leak Warning] {Name} ({Serial:X8}) collected but not finalized after {Duration}.", entity.GetType().Name, entity.Serial, duration); - return false; - }); - } - - private static bool ExceptionToLeakCheck(IEntity entity) => - // Mobiles that are deleted, but have a reference to a corpse that is not deleted should be exempt - (entity as Mobile)?.Corpse?.Deleted == false; -} diff --git a/Projects/Server/EventLoopTasks.cs b/Projects/Server/EventLoopTasks.cs index c3ff2c10c..d8d587156 100644 --- a/Projects/Server/EventLoopTasks.cs +++ b/Projects/Server/EventLoopTasks.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: EventLoopTasks.cs * * * @@ -21,68 +21,20 @@ namespace Server; public sealed class EventLoopContext : SynchronizationContext { - public enum Priority - { - Normal, - High - } - private readonly ConcurrentQueue _queue; - private readonly ConcurrentQueue _priorityQueue; private readonly Thread _mainThread; - private readonly int _maxPerFrame; - public EventLoopContext(int maxPerFrame = 128) + public EventLoopContext() { - _maxPerFrame = maxPerFrame; - _queue = []; - _priorityQueue = []; + _queue = new ConcurrentQueue(); _mainThread = Thread.CurrentThread; } public override SynchronizationContext CreateCopy() => new EventLoopContext(); - /// - /// True when no callbacks are waiting to run. - /// - /// - /// drains at most _maxPerFrame callbacks, so work can - /// legitimately be left over. The event loop checks this before sleeping so a backlog keeps - /// it running instead. - /// - public bool IsEmpty => _queue.IsEmpty && _priorityQueue.IsEmpty; + public void Post(Action d) => _queue.Enqueue(d); - public void Post(Action d, Priority priority = Priority.Normal) - { - (priority == Priority.High ? _priorityQueue : _queue).Enqueue(d); - WakeEventLoop(); - } - - public override void Post(SendOrPostCallback d, object state) - { - _queue.Enqueue(() => d(state)); - WakeEventLoop(); - } - - /// - /// Nudges the game loop in case it is asleep: the loop blocks on network I/O, which a queue - /// push alone does not signal. - /// - private void WakeEventLoop() - { - // A post from the loop thread cannot need a wake -- the loop is executing this very call - // -- and the signal is a syscall on every backend. - if (Thread.CurrentThread == _mainThread) - { - EventLoopProfiler.WakeSignal(elided: true); - return; - } - - EventLoopProfiler.WakeSignal(elided: false); - - // Safe before networking is configured and after teardown; NetState.Wake does nothing. - Network.NetState.Wake(); - } + public override void Post(SendOrPostCallback d, object state) => _queue.Enqueue(() => d(state)); public override void Send(SendOrPostCallback d, object state) { @@ -92,7 +44,7 @@ public sealed class EventLoopContext : SynchronizationContext return; } - var evt = new AutoResetEvent(false); + AutoResetEvent evt = new AutoResetEvent(false); _queue.Enqueue(() => { @@ -100,8 +52,6 @@ public sealed class EventLoopContext : SynchronizationContext evt.Set(); }); - WakeEventLoop(); - evt.WaitOne(); } @@ -112,19 +62,9 @@ public sealed class EventLoopContext : SynchronizationContext throw new Exception("Called EventLoop.ExecuteTasks on incorrect thread!"); } - var count = _priorityQueue.Count; + var count = _queue.Count; - for (var i = 0; i < count; i++) - { - if (_priorityQueue.TryDequeue(out var a)) - { - a(); - } - } - - count = Math.Min(_queue.Count, _maxPerFrame); - - for (var i = 0; i < count; i++) + for (int i = 0; i < count; i++) { if (_queue.TryDequeue(out var a)) { diff --git a/Projects/Server/Events/AccountLoginEvent.cs b/Projects/Server/Events/AccountLoginEvent.cs index cf1c0bd1a..76628e0eb 100644 --- a/Projects/Server/Events/AccountLoginEvent.cs +++ b/Projects/Server/Events/AccountLoginEvent.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: AccountLoginEvent.cs * * * @@ -37,13 +37,6 @@ public class AccountLoginEventArgs public bool Accepted { get; set; } public ALRReason RejectReason { get; set; } - - /// - /// No verdict yet: a subscriber moved the password check off the game loop and replies itself - /// once it lands. The packet handler must send neither accept nor reject while this is set, or - /// the client gets two answers to one login. - /// - public bool Deferred { get; set; } } public static partial class EventSink diff --git a/Projects/Server/Events/AggressiveActionEvent.cs b/Projects/Server/Events/AggressiveActionEvent.cs index 81bd51751..6a1ad1892 100644 --- a/Projects/Server/Events/AggressiveActionEvent.cs +++ b/Projects/Server/Events/AggressiveActionEvent.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: AggressiveActionEvent.cs * * * diff --git a/Projects/Server/Events/EventSink.cs b/Projects/Server/Events/EventSink.cs index 02cb0c2f0..6a72185f4 100644 --- a/Projects/Server/Events/EventSink.cs +++ b/Projects/Server/Events/EventSink.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: EventSink.cs * * * diff --git a/Projects/Server/Events/MovementEvent.cs b/Projects/Server/Events/MovementEvent.cs index 841277f40..ab7c12c74 100644 --- a/Projects/Server/Events/MovementEvent.cs +++ b/Projects/Server/Events/MovementEvent.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MovementEvent.cs * * * diff --git a/Projects/Server/Events/ServerCrashedEvent.cs b/Projects/Server/Events/ServerCrashedEvent.cs index ba1c046ee..527f82dd2 100644 --- a/Projects/Server/Events/ServerCrashedEvent.cs +++ b/Projects/Server/Events/ServerCrashedEvent.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ServerCrashedEvent.cs * * * diff --git a/Projects/Server/Events/SocketConnectionEvent.cs b/Projects/Server/Events/SocketConnectionEvent.cs index ec3e35cdd..9ac96dcd3 100644 --- a/Projects/Server/Events/SocketConnectionEvent.cs +++ b/Projects/Server/Events/SocketConnectionEvent.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SocketConnectionEvent.cs * * * @@ -14,20 +14,20 @@ *************************************************************************/ using System; -using System.Net; +using System.Net.Sockets; using System.Runtime.CompilerServices; namespace Server; public class SocketConnectEventArgs { - public SocketConnectEventArgs(IPAddress address) + public SocketConnectEventArgs(Socket c) { - Address = address; + Connection = c; AllowConnection = true; } - public IPAddress Address { get; } + public Socket Connection { get; } public bool AllowConnection { get; set; } } diff --git a/Projects/Server/Events/SpeechEvent.cs b/Projects/Server/Events/SpeechEvent.cs index 9e363cb23..c5a126a90 100644 --- a/Projects/Server/Events/SpeechEvent.cs +++ b/Projects/Server/Events/SpeechEvent.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SpeechEvent.cs * * * diff --git a/Projects/Server/Exceptions/ExceptionExtensions.cs b/Projects/Server/Exceptions/ExceptionExtensions.cs index 41e964163..dc95bde9f 100644 --- a/Projects/Server/Exceptions/ExceptionExtensions.cs +++ b/Projects/Server/Exceptions/ExceptionExtensions.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ExceptionExtensions.cs * * * @@ -28,14 +28,14 @@ public static class ExceptionExtensions private static Func _createStackTraceMethod() { - var target = Expression.Parameter(typeof(Exception)); - var stack = Expression.Parameter(typeof(StackTrace)); - var traceFormatType = typeof(StackTrace).GetNestedType("TraceFormat", BindingFlags.NonPublic); - var toString = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { traceFormatType }, null); - var normalTraceFormat = Enum.GetValues(traceFormatType!).GetValue(0); - var stackTraceString = Expression.Call(stack, toString!, Expression.Constant(normalTraceFormat, traceFormatType)); - var stackTraceStringField = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance); - var assign = Expression.Assign(Expression.Field(target, stackTraceStringField!), stackTraceString); + ParameterExpression target = Expression.Parameter(typeof(Exception)); + ParameterExpression stack = Expression.Parameter(typeof(StackTrace)); + Type traceFormatType = typeof(StackTrace).GetNestedType("TraceFormat", BindingFlags.NonPublic); + MethodInfo toString = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { traceFormatType }, null); + object normalTraceFormat = Enum.GetValues(traceFormatType!).GetValue(0); + MethodCallExpression stackTraceString = Expression.Call(stack, toString!, Expression.Constant(normalTraceFormat, traceFormatType)); + FieldInfo stackTraceStringField = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance); + BinaryExpression assign = Expression.Assign(Expression.Field(target, stackTraceStringField!), stackTraceString); return Expression.Lambda>(Expression.Block(assign, target), target, stack).Compile(); } } diff --git a/Projects/Server/Exceptions/InvalidThreadException.cs b/Projects/Server/Exceptions/InvalidThreadException.cs index 0de5b650e..7a2e49f0d 100644 --- a/Projects/Server/Exceptions/InvalidThreadException.cs +++ b/Projects/Server/Exceptions/InvalidThreadException.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: InvalidThreadException.cs * * * diff --git a/Projects/Server/Exceptions/MaxConnectionsException.cs b/Projects/Server/Exceptions/MaxConnectionsException.cs index cb8bcc10b..57c24e626 100644 --- a/Projects/Server/Exceptions/MaxConnectionsException.cs +++ b/Projects/Server/Exceptions/MaxConnectionsException.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MaxConnectionsException.cs * * * diff --git a/Projects/Server/ExpansionInfo.cs b/Projects/Server/ExpansionInfo.cs index 8c41350e6..48020fdea 100644 --- a/Projects/Server/ExpansionInfo.cs +++ b/Projects/Server/ExpansionInfo.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ExpansionInfo.cs * * * @@ -193,7 +193,7 @@ public class ExpansionInfo public static void StoreMapSelection(MapSelectionFlags mapSelectionFlags, Expansion expansion) { - var expansionIndex = (int)expansion; + int expansionIndex = (int)expansion; Table[expansionIndex].MapSelectionFlags = mapSelectionFlags; } @@ -207,14 +207,14 @@ public class ExpansionInfo { var pathToExpansionFile = Path.Combine(Core.BaseDirectory, ExpansionConfigurationPath); - var expansionConfig = JsonConfig.Deserialize(pathToExpansionFile); + ExpansionInfo expansionConfig = JsonConfig.Deserialize(pathToExpansionFile); if (expansionConfig == null) { expansion = Expansion.None; return false; } - var currentExpansionIndex = expansionConfig.Id; + int currentExpansionIndex = expansionConfig.Id; Table[currentExpansionIndex] = expansionConfig; expansion = (Expansion)currentExpansionIndex; return true; diff --git a/Projects/Server/FeatureFlags.cs b/Projects/Server/FeatureFlags.cs deleted file mode 100644 index a70b4e452..000000000 --- a/Projects/Server/FeatureFlags.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Server; - -/// -/// Static boolean flags for Server project hot paths. -/// Values are synced by FeatureFlagManager in UOContent when flags change. -/// -public static class ServerFeatureFlags -{ - public static bool PlayerTrading { get; set; } = true; - public static bool PvPCombat { get; set; } = true; - public static bool BankAccess { get; set; } = true; - public static bool SpeedhackDetection { get; set; } - public static bool InsuranceEnabled { get; set; } -} diff --git a/Projects/Server/GarbageCollection/Gen2GcCallback.cs b/Projects/Server/GarbageCollection/Gen2GcCallback.cs index d8d7f2c28..fc23db8ce 100644 --- a/Projects/Server/GarbageCollection/Gen2GcCallback.cs +++ b/Projects/Server/GarbageCollection/Gen2GcCallback.cs @@ -13,31 +13,18 @@ namespace System; /// internal sealed class Gen2GcCallback : CriticalFinalizerObject { - private readonly Func? _callback0; - private readonly Func? _callback1; + private readonly Func _callback; private GCHandle _weakTargetObj; - private Gen2GcCallback(Func callback) => _callback0 = callback; - private Gen2GcCallback(Func callback, object targetObj) { - _callback1 = callback; + _callback = callback; _weakTargetObj = GCHandle.Alloc(targetObj, GCHandleType.Weak); } /// /// Schedule 'callback' to be called in the next GC. If the callback returns true it is - /// rescheduled for the next Gen 2 GC, otherwise the callback stops. - /// - public static void Register(Func callback) - { - // Create an unreachable object that remembers the callback function and target object. - new Gen2GcCallback(callback); - } - - /// - /// Schedule 'callback' to be called in the next GC. If the callback returns true it is - /// rescheduled for the next Gen 2 GC, otherwise the callback stops. + /// rescheduled for the next Gen 2 GC. Otherwise the callbacks stop. /// /// NOTE: This callback will be kept alive until either the callback function returns false, /// or the target object dies. @@ -53,7 +40,7 @@ internal sealed class Gen2GcCallback : CriticalFinalizerObject if (_weakTargetObj.IsAllocated) { // Check to see if the target object is still alive. - var targetObj = _weakTargetObj.Target; + object? targetObj = _weakTargetObj.Target; if (targetObj == null) { // The target object is dead, so this callback object is no longer needed. @@ -64,8 +51,8 @@ internal sealed class Gen2GcCallback : CriticalFinalizerObject // Execute the callback method. try { - Debug.Assert(_callback1 != null); - if (!_callback1(targetObj)) + Debug.Assert(_callback != null); + if (_callback?.Invoke(targetObj) != true) { // If the callback returns false, this callback object is no longer needed. _weakTargetObj.Free(); @@ -76,29 +63,8 @@ internal sealed class Gen2GcCallback : CriticalFinalizerObject { // Ensure that we still get a chance to resurrect this object, even if the callback throws an exception. #if DEBUG - // Except in DEBUG, as we really shouldn't be hitting any exceptions here. - throw; -#endif - } - } - else - { - // Execute the callback method. - try - { - Debug.Assert(_callback0 != null); - if (!_callback0()) - { - // If the callback returns false, this callback object is no longer needed. - return; - } - } - catch - { - // Ensure that we still get a chance to resurrect this object, even if the callback throws an exception. -#if DEBUG - // Except in DEBUG, as we really shouldn't be hitting any exceptions here. - throw; + // Except in DEBUG, as we really shouldn't be hitting any exceptions here. + throw; #endif } } diff --git a/Projects/Server/Geometry/Point2D.cs b/Projects/Server/Geometry/Point2D.cs index 17c2cc780..296194fac 100644 --- a/Projects/Server/Geometry/Point2D.cs +++ b/Projects/Server/Geometry/Point2D.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Point2D.cs * * * diff --git a/Projects/Server/Geometry/Point3D.cs b/Projects/Server/Geometry/Point3D.cs index 6886fbc30..2d0ef523f 100644 --- a/Projects/Server/Geometry/Point3D.cs +++ b/Projects/Server/Geometry/Point3D.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Point3D.cs * * * diff --git a/Projects/Server/Geometry/Point3DList.cs b/Projects/Server/Geometry/Point3DList.cs index 3de9410d2..244a20def 100644 --- a/Projects/Server/Geometry/Point3DList.cs +++ b/Projects/Server/Geometry/Point3DList.cs @@ -9,7 +9,7 @@ public class Point3DList public Point3DList() { - m_List = new Point3D[16]; + m_List = new Point3D[8]; Count = 0; } diff --git a/Projects/Server/Geometry/Rectangle2D.cs b/Projects/Server/Geometry/Rectangle2D.cs index 2c89d89e4..959873a18 100644 --- a/Projects/Server/Geometry/Rectangle2D.cs +++ b/Projects/Server/Geometry/Rectangle2D.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Rectangle2D.cs * * * diff --git a/Projects/Server/Geometry/Rectangle3D.cs b/Projects/Server/Geometry/Rectangle3D.cs index 6dc6fcdde..c7ec149cc 100644 --- a/Projects/Server/Geometry/Rectangle3D.cs +++ b/Projects/Server/Geometry/Rectangle3D.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Rectangle3D.cs * * * @@ -20,7 +20,7 @@ namespace Server; [NoSort] [PropertyObject] -public struct Rectangle3D : IEquatable, ISpanFormattable, ISpanParsable +public struct Rectangle3D : IEquatable, ISpanFormattable { private Point3D _start; private Point3D _end; @@ -81,16 +81,6 @@ public struct Rectangle3D : IEquatable, ISpanFormattable, ISpanPars [CommandProperty(AccessLevel.Counselor)] public int Depth => _end.Z - _start.Z; - /// - /// Returns a normalized rectangle where Start contains the minimum coordinates - /// and End contains the maximum coordinates in all dimensions. - /// Use this when Start/End ordering matters (e.g., Contains checks, Z range calculations). - /// - public Rectangle3D Normalized => new( - new Point3D(Math.Min(_start.X, _end.X), Math.Min(_start.Y, _end.Y), Math.Min(_start.Z, _end.Z)), - new Point3D(Math.Max(_start.X, _end.X), Math.Max(_start.Y, _end.Y), Math.Max(_start.Z, _end.Z)) - ); - public bool Equals(Rectangle3D other) => _start == other._start && _end == other._end; public override bool Equals(object obj) => obj is Rectangle3D other && Equals(other); diff --git a/Projects/Server/Geometry/WorldLocation.cs b/Projects/Server/Geometry/WorldLocation.cs index 0553719b6..2f0ee7ac0 100644 --- a/Projects/Server/Geometry/WorldLocation.cs +++ b/Projects/Server/Geometry/WorldLocation.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: WorldLocation.cs * * * @@ -167,8 +167,8 @@ public struct WorldLocation : IPoint3D, IComparable, IEquatable.Shared.Rent(128); - var span = array.AsSpan(); + char[] array = ArrayPool.Shared.Rent(128); + Span span = array.AsSpan(); while (!TryFormat(span, out charsWritten, null, null)) { array = ArrayPool.Shared.Rent(array.Length * 2); diff --git a/Projects/Server/Guild.cs b/Projects/Server/Guild.cs index 1b2b013d1..8998860cf 100644 --- a/Projects/Server/Guild.cs +++ b/Projects/Server/Guild.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Guild.cs * * * @@ -52,13 +52,17 @@ public abstract class BaseGuild : ISerializable [CommandProperty(AccessLevel.GameMaster, readOnly: true)] public DateTime Created { get; set; } = Core.Now; + public byte SerializedThread { get; set; } + public int SerializedPosition { get; set; } + public int SerializedLength { get; set; } + public abstract void Serialize(IGenericWriter writer); public abstract void Deserialize(IGenericReader reader); public abstract void OnDelete(Mobile mob); - public static BaseGuild FindByName(ReadOnlySpan name) + public static BaseGuild FindByName(string name) { foreach (var g in World.Guilds.Values) { @@ -71,7 +75,7 @@ public abstract class BaseGuild : ISerializable return null; } - public static BaseGuild FindByAbbrev(ReadOnlySpan abbr) + public static BaseGuild FindByAbbrev(string abbr) { foreach (var g in World.Guilds.Values) { @@ -84,29 +88,19 @@ public abstract class BaseGuild : ISerializable return null; } - public static HashSet Search(ReadOnlySpan find) + public static HashSet Search(string find) { + var words = find.ToLower().Split(' '); var results = new HashSet(); - find = find.Trim(); - if (find.IsEmpty) - { - return results; - } foreach (var g in World.Guilds.Values) { - var name = g.Name.AsSpan(); + var name = g.Name; - var all = true; - foreach (var wordRange in find.Split(' ')) + bool all = true; + foreach (var t in words) { - var word = find[wordRange]; - if (word.IsEmpty) - { - continue; - } - - if (name.InsensitiveContains(word)) + if (name.InsensitiveIndexOf(t) == -1) { all = false; break; diff --git a/Projects/Server/IAccount.cs b/Projects/Server/IAccount.cs index 57b70baaa..911bb8c0a 100644 --- a/Projects/Server/IAccount.cs +++ b/Projects/Server/IAccount.cs @@ -97,7 +97,7 @@ public interface IGoldAccount public interface IAccount : ISerializable, IGoldAccount, IComparable { - string Username { get; } + string Username { get; set; } string Email { get; set; } AccessLevel AccessLevel { get; set; } @@ -107,7 +107,6 @@ public interface IAccount : ISerializable, IGoldAccount, IComparable Mobile this[int index] { get; set; } void Delete(); - bool TrySetUsername(string username); void SetPassword(string password); bool CheckPassword(string password); } diff --git a/Projects/Server/IEntity.cs b/Projects/Server/IEntity.cs index 63a02fc02..95b11843b 100644 --- a/Projects/Server/IEntity.cs +++ b/Projects/Server/IEntity.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IEntity.cs * * * @@ -115,6 +115,10 @@ public class Entity : IEntity Timer.StartTimer(Delete); } + public byte SerializedThread { get; set; } + public int SerializedPosition { get; set; } + public int SerializedLength { get; set; } + public void Serialize(IGenericWriter writer) { } diff --git a/Projects/Server/Interfaces.cs b/Projects/Server/Interfaces.cs index 8638e4fc7..a75437b37 100644 --- a/Projects/Server/Interfaces.cs +++ b/Projects/Server/Interfaces.cs @@ -57,8 +57,8 @@ public interface ISpawner : IEntity { Guid Guid { get; } bool UnlinkOnTaming { get; } - int WalkingRange { get; } - Rectangle3D SpawnBounds { get; } + Point3D HomeLocation { get; } + int HomeRange { get; } Region Region { get; } bool ReturnOnDeactivate { get; } bool Running { get; } @@ -66,11 +66,6 @@ public interface ISpawner : IEntity void Remove(ISpawnable spawn); Point3D GetSpawnPosition(ISpawnable spawned, Map map); void Respawn(); - - /// - /// Checks if the given location is within the spawn bounds. - /// - bool IsInSpawnBounds(Point3D location); } public interface ISpawnable : IEntity diff --git a/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs b/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs new file mode 100644 index 000000000..973e1bf4c --- /dev/null +++ b/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs @@ -0,0 +1,503 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: BaseMulti.SectorMultiLinkList.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using Server.Collections; + +namespace Server.Items; + +// Adds support for the specific value link list on sectors for multis, separate from items +public partial class BaseMulti +{ + // Sectors, specifically for multis + public BaseMulti SectorMultiNext { get; set; } + public BaseMulti SectorMultiPrevious { get; set; } + public bool OnSectorMultiLinkList { get; set; } +} + +public struct SectorMultiValueLinkList +{ + public int Count { get; internal set; } + internal BaseMulti _first; + internal BaseMulti _last; + + public int Version { get; private set; } + + public void Remove(BaseMulti node) + { + if (node == null) + { + return; + } + + if (!node.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to remove a node that is not on the list."); + } + + if (node.SectorMultiPrevious == null) + { + // If SectorMultiPrevious is null, then it is the first element. + if (_first != node) + { + throw new ArgumentException("Attempted to remove a node that is not on the list."); + } + + if (_first == _last) + { + _last = null; + _first = null; + } + else + { + _first = node.SectorMultiNext; + } + + if (node.SectorMultiNext != null) + { + node.SectorMultiNext.SectorMultiPrevious = null; + } + } + else + { + node.SectorMultiPrevious.SectorMultiNext = node.SectorMultiNext; + + // If SectorMultiNext is null, then it is the last element. + if (node.SectorMultiNext == null) + { + _last = node.SectorMultiPrevious; + } + else + { + node.SectorMultiNext.SectorMultiPrevious = node.SectorMultiPrevious; + } + } + + node.SectorMultiNext = null; + node.SectorMultiPrevious = null; + node.OnSectorMultiLinkList = false; + Count--; + Version++; + + if (Count < 0) + { + throw new Exception("Count is negative!"); + } + } + + // Remove all entries before this node, not including this node. + public void RemoveAllBefore(BaseMulti e) + { + if (e == null) + { + return; + } + + if (!e.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to remove nodes before a node that is not on the list."); + } + + if (e.SectorMultiPrevious == null) + { + return; + } + + var current = e.SectorMultiPrevious; + e.SectorMultiPrevious = null; + + while (current != null) + { + var SectorMultiPrevious = current.SectorMultiPrevious; + + current.OnSectorMultiLinkList = false; + current.SectorMultiNext = null; + current.SectorMultiPrevious = null; + Count--; + + if (Count < 0) + { + throw new Exception("Count is negative!"); + } + + current = SectorMultiPrevious; + } + + _first = e; + Version++; + } + + // Remove all entries after this node, not including this node. + public void RemoveAllAfter(BaseMulti e) + { + if (e == null) + { + return; + } + + if (!e.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to remove nodes after a node that is not on the list."); + } + + if (e.SectorMultiNext == null) + { + return; + } + + var current = e.SectorMultiNext; + e.SectorMultiNext = null; + + while (current != null) + { + var SectorMultiNext = current.SectorMultiNext; + + current.OnSectorMultiLinkList = false; + current.SectorMultiNext = null; + current.SectorMultiPrevious = null; + Count--; + + if (Count < 0) + { + throw new Exception("Count is negative!"); + } + + current = SectorMultiNext; + } + + _last = e; + Version++; + } + + public void AddLast(BaseMulti e) + { + if (e == null) + { + return; + } + + if (e.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + if (_last != null) + { + AddAfter(_last, e); + } + else + { + _first = e; + _last = e; + Count = 1; + Version++; + e.OnSectorMultiLinkList = true; + } + } + + public void AddFirst(BaseMulti e) + { + if (e == null) + { + return; + } + + if (e.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + if (_first != null) + { + AddBefore(_first, e); + } + else + { + _first = e; + _last = e; + Count = 1; + Version++; + e.OnSectorMultiLinkList = true; + } + } + + public void AddBefore(BaseMulti existing, BaseMulti node) + { + if (node == null) + { + return; + } + + ArgumentNullException.ThrowIfNull(existing); + + if (!existing.OnSectorMultiLinkList) + { + throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list."); + } + + if (node.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + node.SectorMultiNext = existing; + node.SectorMultiPrevious = existing.SectorMultiPrevious; + + if (existing.SectorMultiPrevious != null) + { + existing.SectorMultiPrevious.SectorMultiNext = node; + } + else + { + _first = node; + } + + existing.SectorMultiPrevious = node; + node.OnSectorMultiLinkList = true; + Count++; + Version++; + } + + public void AddAfter(BaseMulti existing, BaseMulti node) + { + if (node == null) + { + return; + } + + ArgumentNullException.ThrowIfNull(existing); + + if (!existing.OnSectorMultiLinkList) + { + throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list."); + } + + if (node.OnSectorMultiLinkList) + { + throw new ArgumentException("Attempted to add a node that is already on a list."); + } + + node.SectorMultiPrevious = existing; + node.SectorMultiNext = existing.SectorMultiNext; + + if (existing.SectorMultiNext != null) + { + existing.SectorMultiNext.SectorMultiPrevious = node; + } + else + { + _last = node; + } + + existing.SectorMultiNext = node; + node.OnSectorMultiLinkList = true; + Count++; + Version++; + } + + public void RemoveAll() + { + var current = _first; + while (current != null) + { + var SectorMultiNext = current.SectorMultiNext; + + current.OnSectorMultiLinkList = false; + current.SectorMultiNext = null; + current.SectorMultiPrevious = null; + current = SectorMultiNext; + } + + _first = null; + _last = null; + Count = 0; + Version++; + } + + public void AddLast(ref SectorMultiValueLinkList otherList, BaseMulti start, BaseMulti end) + { + // Should we check if start and end actually exist on the other list? + if (otherList.Count == 0 || otherList.Count == 1 && (start != end || otherList._first != start)) + { + throw new ArgumentException("Attempted to add nodes that are not on the specified linklist."); + } + + if (start.SectorMultiPrevious != null) + { + start.SectorMultiPrevious.SectorMultiNext = end.SectorMultiNext; + } + else + { + // Start is first + otherList._first = end.SectorMultiNext; + } + + if (end.SectorMultiNext != null) + { + end.SectorMultiNext.SectorMultiPrevious = start.SectorMultiPrevious; + } + else + { + otherList._last = start.SectorMultiPrevious; + } + + var count = 1; + var current = start; + + // Assume start and end are in the right order, or bad things happen (crash). + while (current != end) + { + count++; + current = current.SectorMultiNext; + } + + otherList.Count -= count; + + if (otherList.Count < 0) + { + throw new Exception("Count is negative!"); + } + + if (_last != null) + { + _last.SectorMultiNext = start; + start.SectorMultiPrevious = _last; + } + else + { + _first = start; + } + + _last = end; + Count += count; + Version++; + } + + public BaseMulti[] ToArray() + { + var arr = new BaseMulti[Count]; + + var index = 0; + foreach (var t in this) + { + arr[index++] = t; + } + + return arr; + } + + public ref struct SectorMultiValueListEnumerator + { + private bool _started; + private BaseMulti _current; + private ref readonly SectorMultiValueLinkList _linkList; + private int _version; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SectorMultiValueListEnumerator(in SectorMultiValueLinkList linkList) + { + _linkList = ref linkList; + _started = false; + _current = null; + _version = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + if (!_started) + { + _current = _linkList._first; + _started = true; + _version = _linkList.Version; + } + else if (_linkList.Version != _version) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + else + { + _current = _current.SectorMultiNext; + } + + return _current != null; + } + + public BaseMulti Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + } + + public ref struct DescendingSectorMultiValueListEnumerator + { + private bool _started; + private BaseMulti _current; + private ref readonly SectorMultiValueLinkList _linkList; + private int _version; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public DescendingSectorMultiValueListEnumerator(in SectorMultiValueLinkList linkList) + { + _linkList = ref linkList; + _started = false; + _current = null; + _version = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + if (!_started) + { + _current = _linkList._last; + _started = true; + _version = _linkList.Version; + } + else if (_linkList.Version != _version) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + else + { + _current = _current.SectorMultiPrevious; + } + + return _current != null; + } + + public BaseMulti Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public DescendingSectorMultiValueListEnumerator GetEnumerator() => this; + } +} + +public static class SectorMultiValueLinkListExt +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SectorMultiValueLinkList.SectorMultiValueListEnumerator GetEnumerator(this in SectorMultiValueLinkList linkList) + => new(in linkList); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SectorMultiValueLinkList.DescendingSectorMultiValueListEnumerator ByDescending(this in SectorMultiValueLinkList linkList) + => new(in linkList); +} diff --git a/Projects/Server/Items/BaseMulti.cs b/Projects/Server/Items/BaseMulti.cs index 0ebcac7c5..41c448776 100644 --- a/Projects/Server/Items/BaseMulti.cs +++ b/Projects/Server/Items/BaseMulti.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BaseMulti.cs * * * @@ -19,24 +19,6 @@ using ModernUO.Serialization; namespace Server.Items; -/// -/// Whether a multi instance's footprint is eligible for the pathfinding interior-mask cache -/// (Server.Engines.Pathing.Cache.MultiMaskCache). Stored on -/// ; recomputed when it is -/// (e.g. after a move resets it). -/// -public enum MultiInteriorCacheState : byte -{ - /// Not yet determined; recompute on next use. - Unknown = 0, - - /// Whole footprint terrain is below the floor → interior cells may serve from the cache. - Clean = 1, - - /// Terrain intrudes into the footprint → fall back to live synthesis. - Dirty = 2 -} - [SerializationGenerator(0, false)] public abstract partial class BaseMulti : Item { @@ -53,10 +35,6 @@ public abstract partial class BaseMulti : Item Map?.OnLeave(this); base.ItemID = value; Map?.OnEnter(this); - - // The footprint shape changes with ItemID (e.g. a boat's heading swaps the MCL), so - // the pathfinding interior-cache clean/dirty status must be recomputed. - PathInteriorCacheState = MultiInteriorCacheState.Unknown; } } } @@ -87,27 +65,6 @@ public abstract partial class BaseMulti : Item public virtual MultiComponentList Components => MultiData.GetComponents(ItemID); - /// - /// Pathfinding interior-mask cache gate (Server.Engines.Pathing.Cache.MultiMaskCache). - /// Reset to whenever the footprint's world-terrain - /// relationship can change — a location change, a map change, or an ItemID change (e.g. a boat's - /// heading swaps the MCL). Subclasses that override / - /// MUST call base for the reset to fire. - /// - public MultiInteriorCacheState PathInteriorCacheState { get; set; } - - public override void OnLocationChange(Point3D oldLocation) - { - base.OnLocationChange(oldLocation); - PathInteriorCacheState = MultiInteriorCacheState.Unknown; - } - - public override void OnMapChange() - { - base.OnMapChange(); - PathInteriorCacheState = MultiInteriorCacheState.Unknown; - } - public override int GetMaxUpdateRange() => 22; public override int GetUpdateRange(Mobile m) => 22; @@ -150,17 +107,17 @@ public abstract partial class BaseMulti : Item return false; } - var minX = Math.Max(bounds.X, Location.X + Components.Min.X); - var maxX = Math.Min(bounds.X + bounds.Width, Location.X + Components.Max.X); - var minY = Math.Max(bounds.Y, Location.Y + Components.Min.Y); - var maxY = Math.Min(bounds.Y + bounds.Height, Location.Y + Components.Max.Y); + int minX = Math.Max(bounds.X, Location.X + Components.Min.X); + int maxX = Math.Min(bounds.X + bounds.Width, Location.X + Components.Max.X); + int minY = Math.Max(bounds.Y, Location.Y + Components.Min.Y); + int maxY = Math.Min(bounds.Y + bounds.Height, Location.Y + Components.Max.Y); - for (var x = minX; x <= maxX; x++) + for (int x = minX; x <= maxX; x++) { - for (var y = minY; y <= maxY; y++) + for (int y = minY; y <= maxY; y++) { - var offsetX = x - Location.X - Components.Min.X; - var offsetY = y - Location.Y - Components.Min.Y; + int offsetX = x - Location.X - Components.Min.X; + int offsetY = y - Location.Y - Components.Min.Y; if (offsetX < 0 || offsetY < 0 || offsetX >= Components.Width || offsetY >= Components.Height) { diff --git a/Projects/Server/Items/Item.Enumerable.cs b/Projects/Server/Items/Container.Enumerable.cs similarity index 67% rename from Projects/Server/Items/Item.Enumerable.cs rename to Projects/Server/Items/Container.Enumerable.cs index 417c9df58..fc652c636 100644 --- a/Projects/Server/Items/Item.Enumerable.cs +++ b/Projects/Server/Items/Container.Enumerable.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: Item.Enumerable.cs * + * File: Container.Enumerable.cs * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -18,13 +18,13 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Server.Collections; -namespace Server; +namespace Server.Items; -public partial class Item +public partial class Container { /// /// Performs a breadth-first search through all the s and - /// nested s within this . + /// nested s within this . /// /// /// DO NOT consume, delete, or move items while iterating with any FindItemByType or FindItems overloads @@ -42,8 +42,8 @@ public partial class Item /// Type of objects being searched for /// /// Optional: If true, the search will recursively - /// check any nested s; otherwise, nested - /// s will not be searched. + /// check any nested s; otherwise, nested + /// s will not be searched. /// /// /// Optional: A predicate to check if the @@ -54,16 +54,16 @@ public partial class Item /// . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public FindItemsByTypeEnumerator FindItemsByType(bool recurse = true, Predicate predicate = null) - where T : Item => new(this, recurse, predicate); + public FindItemsByTypeEnumerator FindItemsByType(bool recurse = true, Predicate predicate = null) where T : Item => + new(this, recurse, predicate); [MethodImpl(MethodImplOptions.AggressiveInlining)] public FindItemsByTypeEnumerator FindItemsByType(Type type, bool recurse = true) => - new(this, recurse, type); + new(this, recurse, type.IsInstanceOfType); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public FindItemsByTypeEnumerator FindItemsByType(ReadOnlySpan types, bool recurse = true) => - new(this, recurse, types); + public FindItemsByTypeEnumerator FindItemsByType(Type[] types, bool recurse = true) => + new(this, recurse, item => item.InTypeList(types)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public FindItemsByTypeEnumerator FindItems(bool recurse = true, Predicate predicate = null) => @@ -71,7 +71,7 @@ public partial class Item /// /// Safely enumerates items using a breadth-first search through all the s and - /// nested s within this . + /// nested s within this . /// /// /// Use EnumerateItemsByType for situations where the item might be manipulated, consumed, or moved. @@ -80,8 +80,7 @@ public partial class Item /// /// /// - /// using var queue = cont.EnumerateItemsByType<Item>(); - /// foreach (var item in queue) + /// foreach (var item in cont.EnumerateItemsByType<Item>()) /// { /// if (item.LootType is not LootType.Blessed) /// { @@ -93,8 +92,8 @@ public partial class Item /// Type of objects being searched for /// /// Optional: If true, the search will recursively - /// check any nested s; otherwise, nested - /// s will not be searched. + /// check any nested s; otherwise, nested + /// s will not be searched. /// /// /// Optional: A predicate to check if the @@ -122,22 +121,28 @@ public partial class Item { var queue = PooledRefQueue.Create(128); - foreach (var item in FindItemsByType(type, recurse)) + foreach (var item in FindItemsByType(recurse)) { - queue.Enqueue(item); + if (type.IsInstanceOfType(item)) + { + queue.Enqueue(item); + } } return queue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public PooledRefQueue EnumerateItemsByType(ReadOnlySpan types, bool recurse = true) + public PooledRefQueue EnumerateItemsByType(Type[] types, bool recurse = true) { var queue = PooledRefQueue.Create(128); - foreach (var item in FindItemsByType(types, recurse)) + foreach (var item in FindItemsByType(recurse)) { - queue.Enqueue(item); + if (item.InTypeList(types)) + { + queue.Enqueue(item); + } } return queue; @@ -164,22 +169,28 @@ public partial class Item { var list = PooledRefList.Create(128); - foreach (var item in FindItemsByType(type, recurse)) + foreach (var item in FindItemsByType(recurse)) { - list.Add(item); + if (type.IsInstanceOfType(item)) + { + list.Add(item); + } } return list; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public PooledRefList ListItemsByType(ReadOnlySpan types, bool recurse = true) + public PooledRefList ListItemsByType(Type[] types, bool recurse = true) { var list = PooledRefList.Create(128); - foreach (var item in FindItemsByType(types, recurse)) + foreach (var item in FindItemsByType(recurse)) { - list.Add(item); + if (item.InTypeList(types)) + { + list.Add(item); + } } return list; @@ -192,49 +203,36 @@ public partial class Item public ref struct FindItemsByTypeEnumerator where T : Item { private const string InvalidOperation_EnumFailedVersion = - "Item was modified after enumerator was instantiated. Use Item.EnumerateItems method instead for safe enumerations."; + "Container was modified after enumerator was instantiated. Use Container.EnumerateItems method instead for safe enumerations."; - private PooledRefQueue _containers; + private PooledRefQueue _containers; private Span _items; private int _index; private T _current; private readonly bool _recurse; - private Item _currentContainer; + private readonly Predicate _predicate; + private Container _currentContainer; private int _version; - // Exactly one filter source is used per enumerator instance, depending on - // which constructor was called. The unused fields stay at default and the - // branches below pick the right path. This avoids the per-call delegate - // allocation that the (Type)/(Type[]) factory methods used to incur. - private readonly Predicate _predicate; - private readonly Type _runtimeType; - private readonly ReadOnlySpan _runtimeTypes; - - public FindItemsByTypeEnumerator(Item container, bool recurse, Predicate predicate) - : this(container, recurse) => _predicate = predicate; - - public FindItemsByTypeEnumerator(Item container, bool recurse, Type runtimeType) - : this(container, recurse) => _runtimeType = runtimeType; - - public FindItemsByTypeEnumerator(Item container, bool recurse, ReadOnlySpan runtimeTypes) - : this(container, recurse) => _runtimeTypes = runtimeTypes; - - private FindItemsByTypeEnumerator(Item container, bool recurse) + public FindItemsByTypeEnumerator(Container container, bool recurse, Predicate predicate) { - _recurse = recurse; - _containers = PooledRefQueue.Create(recurse ? 64 : 0); + _containers = PooledRefQueue.Create(_recurse ? 64 : 0); if (container != null) { - var items = container.LookupItems(); - if (items != null) + if (container.m_Items != null) { - _items = CollectionsMarshal.AsSpan(items); + _items = CollectionsMarshal.AsSpan(container.m_Items); } _currentContainer = container; - _version = container.LookupContainerVersion(); + _version = container._version; } + + _current = default; + _index = 0; + _recurse = recurse; + _predicate = predicate; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -246,9 +244,9 @@ public partial class Item while (_containers.TryDequeue(out var c)) { _currentContainer = c; - _items = CollectionsMarshal.AsSpan(c.LookupItems()); + _items = CollectionsMarshal.AsSpan(c.m_Items); _index = 0; - _version = c.LookupContainerVersion(); + _version = c._version; if (SetNextItem()) { @@ -259,41 +257,25 @@ public partial class Item return false; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool Matches(T t) - { - if (_runtimeType is not null) - { - return _runtimeType.IsInstanceOfType(t); - } - - if (_runtimeTypes.Length > 0) - { - return t.GetType().InTypeList(_runtimeTypes); - } - - return _predicate?.Invoke(t) != false; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool SetNextItem() { - if (_version != _currentContainer.LookupContainerVersion()) + if (_version != _currentContainer._version) { throw new InvalidOperationException(InvalidOperation_EnumFailedVersion); } while (_index < _items.Length) { - var item = _items[_index++]; - if (_recurse && item.LookupItems() is { Count: > 0 }) + Item item = _items[_index++]; + if (_recurse && item is Container { m_Items.Count: > 0 } c) { - _containers.Enqueue(item); + _containers.Enqueue(c); } - if (item is T t && Matches(t)) + if (item is T t && _predicate?.Invoke(t) != false) { - if (_version != _currentContainer.LookupContainerVersion()) + if (_version != _currentContainer._version) { throw new InvalidOperationException(InvalidOperation_EnumFailedVersion); } diff --git a/Projects/Server/Items/Container.cs b/Projects/Server/Items/Container.cs index 06c676b25..2d50aa8b3 100644 --- a/Projects/Server/Items/Container.cs +++ b/Projects/Server/Items/Container.cs @@ -1,22 +1,6 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Container.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - using System; using System.Collections.Generic; using System.IO; -using System.Runtime.CompilerServices; using Server.Collections; using Server.Logging; using Server.Network; @@ -33,21 +17,21 @@ public delegate void ContainerSnoopHandler(Container cont, Mobile from); [SerializationGenerator(0, false)] public partial class Container : Item { - private ContainerData _containerData; + private ContainerData m_ContainerData; - internal List _items; + internal List m_Items; - private int _totalGold; + private int m_TotalGold; - private int _totalItems; - private int _totalWeight; - internal int _version; + private int m_TotalItems; + private int m_TotalWeight; + private int _version; [SerializableField(3)] - [SaveFlag(nameof(ShouldSerializeLiftOverride))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _liftOverride; + [SerializableFieldSaveFlag(3)] private bool ShouldSerializeLiftOverride() => _liftOverride; public Container(int itemID) : base(itemID) @@ -61,8 +45,8 @@ public partial class Container : Item public ContainerData ContainerData { - get => _containerData ?? UpdateContainerData(); - set => _containerData = value; + get => m_ContainerData ?? UpdateContainerData(); + set => m_ContainerData = value; } [CommandProperty(AccessLevel.GameMaster)] @@ -77,14 +61,13 @@ public partial class Container : Item if (ItemID != oldID) { - _containerData = null; + m_ContainerData = null; } } } [EncodedInt] [SerializableProperty(0)] - [SaveFlag(nameof(ShouldSerializeMaxItems), nameof(MaxItemsDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int MaxItems { @@ -97,13 +80,14 @@ public partial class Container : Item } } + [SerializableFieldSaveFlag(0)] private bool ShouldSerializeMaxItems() => _maxItems != -1; + [SerializableFieldDefault(0)] private int MaxItemsDefaultValue() => -1; [EncodedInt] [SerializableProperty(1)] - [SaveFlag(nameof(ShouldSerializeGumpId), nameof(GumpIDDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int GumpID { @@ -115,13 +99,14 @@ public partial class Container : Item } } + [SerializableFieldSaveFlag(1)] private bool ShouldSerializeGumpId() => _gumpID != -1; + [SerializableFieldDefault(1)] private int GumpIDDefaultValue() => -1; [EncodedInt] [SerializableProperty(2)] - [SaveFlag(nameof(ShouldSerializeDropSound), nameof(DropSoundDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int DropSound { @@ -133,8 +118,10 @@ public partial class Container : Item } } + [SerializableFieldSaveFlag(2)] private bool ShouldSerializeDropSound() => _dropSound != -1; + [SerializableFieldDefault(2)] private int DropSoundDefaultValue() => -1; [CommandProperty(AccessLevel.GameMaster)] @@ -199,10 +186,8 @@ public partial class Container : Item return base.CheckItemUse(from, item); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool CheckHold(Mobile m, Item item, bool message) => CheckHold(m, item, message, true, 0, 0); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool CheckHold(Mobile m, Item item, bool message, bool checkItems) => CheckHold(m, item, message, checkItems, 0, 0); @@ -246,13 +231,18 @@ public partial class Container : Item var parent = Parent; - while (parent is Item parentItem) + while (parent != null) { - if (parentItem is Container container) + if (parent is Container container) { return container.CheckHold(m, item, message, checkItems, plusItems, plusWeight); } + if (parent is not Item parentItem) + { + break; + } + parent = parentItem.Parent; } @@ -291,6 +281,16 @@ public partial class Container : Item return true; } + private static void SetSaveFlag(ref SaveFlag flags, SaveFlag toSet, bool setIf) + { + if (setIf) + { + flags |= toSet; + } + } + + private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet) => (flags & toGet) != 0; + [AfterDeserialization] private void AfterDeserialization() { @@ -301,9 +301,9 @@ public partial class Container : Item { return type switch { - TotalType.Gold => _totalGold, - TotalType.Items => _totalItems, - TotalType.Weight => _totalWeight, + TotalType.Gold => m_TotalGold, + TotalType.Items => m_TotalItems, + TotalType.Weight => m_TotalWeight, _ => base.GetTotal(type) }; } @@ -316,20 +316,20 @@ public partial class Container : Item { case TotalType.Gold: { - _totalGold += delta; + m_TotalGold += delta; break; } case TotalType.Items: { - _totalItems += delta; + m_TotalItems += delta; InvalidateProperties(); break; } case TotalType.Weight: { - _totalWeight += delta; + m_TotalWeight += delta; InvalidateProperties(); break; } @@ -341,11 +341,11 @@ public partial class Container : Item public override void UpdateTotals() { - _totalGold = 0; - _totalItems = 0; - _totalWeight = 0; + m_TotalGold = 0; + m_TotalItems = 0; + m_TotalWeight = 0; - var items = _items; + var items = m_Items; if (items == null) { @@ -363,27 +363,24 @@ public partial class Container : Item continue; } - _totalGold += item.TotalGold; - _totalItems += item.TotalItems + 1; - _totalWeight += item.TotalWeight + item.PileWeight; + m_TotalGold += item.TotalGold; + m_TotalItems += item.TotalItems + 1; + m_TotalWeight += item.TotalWeight + item.PileWeight; } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void OnItemAdded(Item item) { base.OnItemAdded(item); _version++; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void OnItemRemoved(Item item) { base.OnItemRemoved(item); _version++; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual bool OnStackAttempt(Mobile from, Item stack, Item dropped) => CheckHold(from, dropped, true, false) && stack.StackWith(from, dropped); @@ -426,15 +423,75 @@ public partial class Container : Item return false; } + public virtual bool TryDropItems(Mobile from, bool sendFullMessage, params Item[] droppedItems) + { + var dropItems = new List(); + var stackItems = new List(); + + var extraItems = 0; + var extraWeight = 0; + + for (var i = 0; i < droppedItems.Length; i++) + { + var dropped = droppedItems[i]; + + var list = Items; + + var stacked = false; + + for (var j = 0; j < list.Count; ++j) + { + var item = list[j]; + + if (item is not Container && CheckHold(from, dropped, false, false, 0, extraWeight) && + item.CanStackWith(dropped)) + { + stackItems.Add(new ItemStackEntry(item, dropped)); + extraWeight += (int)Math.Ceiling(item.Weight * (item.Amount + dropped.Amount)) - + item.PileWeight; // extra weight delta, do not need TotalWeight as we do not have hybrid stackable container types + stacked = true; + break; + } + } + + if (!stacked && CheckHold(from, dropped, false, true, extraItems, extraWeight)) + { + dropItems.Add(dropped); + extraItems++; + extraWeight += dropped.TotalWeight + dropped.PileWeight; + } + } + + if (dropItems.Count + stackItems.Count == droppedItems.Length) // All good + { + for (var i = 0; i < dropItems.Count; i++) + { + DropItem(dropItems[i]); + } + + for (var i = 0; i < stackItems.Count; i++) + { + stackItems[i].m_StackItem.StackWith(from, stackItems[i].m_DropItem, false); + } + + return true; + } + + return false; + } + public virtual void Destroy() { var loc = GetWorldLocation(); var map = Map; - var items = Items; - for (var i = items.Count - 1; i >= 0; --i) + for (var i = Items.Count - 1; i >= 0; --i) { - items[i].MoveToWorld(loc, map); + if (i < Items.Count) + { + Items[i].SetLastMoved(); + Items[i].MoveToWorld(loc, map); + } } Delete(); @@ -527,6 +584,8 @@ public partial class Container : Item LabelTo(from, $"({TotalItems} items, {TotalWeight} stones)"); } } + + // LabelTo( from, 1050044, String.Format( "{0}\t{1}", TotalItems.ToString(), TotalWeight.ToString() ) ); } public override void OnDelete() @@ -550,10 +609,9 @@ public partial class Container : Item if (ObjectPropertyList.Enabled) { - var items = Items; - for (var i = 0; i < items.Count; ++i) + for (var i = 0; i < Items.Count; ++i) { - ns.SendOPLInfo(items[i]); + ns.SendOPLInfo(Items[i]); } } } @@ -595,7 +653,7 @@ public partial class Container : Item if (!contains) { - Openers ??= []; + Openers ??= new List(); Openers.Add(opener); } @@ -652,102 +710,109 @@ public partial class Container : Item } } - public int ConsumeTotalGrouped( - ReadOnlySpan types, ReadOnlySpan amounts, - bool recurse, OnItemConsumed callback, CheckItemGroup grouper) + public bool ConsumeTotalGrouped(Type type, int amount, bool recurse, OnItemConsumed callback, CheckItemGroup grouper) { - if (types.Length != amounts.Length) + if (grouper == null) { - throw new ArgumentException("length of types and amounts must match"); + throw new ArgumentNullException(nameof(grouper)); } - ArgumentNullException.ThrowIfNull(grouper); - // Phase 1: every slot must have a single group with sum >= amount - // before any consume happens (preserves all-or-nothing semantics). - for (var i = 0; i < types.Length; i++) + using var typedItems = ListItemsByType(type, recurse); + + var groups = new List>(); + var idx = 0; + + while (idx < typedItems.Count) { - using var items = ListItemsByType(types[i], recurse); - if (!TryFindGroupMeetingAmount(items, amounts[i], grouper, out _, out _)) + var a = typedItems[idx++]; + var group = new List { - return i; + a + }; + + while (idx < typedItems.Count) + { + var b = typedItems[idx]; + var v = grouper(a, b); + + if (v == 0) + { + group.Add(b); + } + else + { + break; + } + + ++idx; + } + + groups.Add(group); + } + + var items = new Item[groups.Count][]; + var totals = new int[groups.Count]; + + var hasEnough = false; + + for (var i = 0; i < groups.Count; ++i) + { + items[i] = groups[i].ToArray(); + + for (var j = 0; j < items[i].Length; ++j) + { + totals[i] += items[i][j].Amount; + } + + if (totals[i] >= amount) + { + hasEnough = true; } } - // Phase 2: re-list and consume. Trades one extra ListItemsByType per - // slot (small) for eliminating List> + Item[][] + int[] - // grouping bridges (large). Live mutation of the container during - // Item.Consume bumps _version, so each phase walks its own snapshot. - for (var i = 0; i < types.Length; i++) + if (!hasEnough) { - using var items = ListItemsByType(types[i], recurse); - if (TryFindGroupMeetingAmount(items, amounts[i], grouper, out var start, out var len)) + return false; + } + + for (var i = 0; i < items.Length; ++i) + { + if (totals[i] >= amount) { - ConsumeSlice(items, start, len, amounts[i], callback); + var need = amount; + + for (var j = 0; j < items[i].Length; ++j) + { + var item = items[i][j]; + + var theirAmount = item.Amount; + + if (theirAmount < need) + { + callback?.Invoke(item, theirAmount); + + item.Consume(theirAmount); + need -= theirAmount; + } + else + { + callback?.Invoke(item, need); + + item.Consume(need); + break; + } + } + + break; } } - return -1; + return true; } public int ConsumeTotalGrouped( - Type[][] types, ReadOnlySpan amounts, - bool recurse, OnItemConsumed callback, CheckItemGroup grouper) - { - if (types.Length != amounts.Length) - { - throw new ArgumentException("length of types and amounts must match"); - } - ArgumentNullException.ThrowIfNull(grouper); - - for (var i = 0; i < types.Length; i++) - { - using var items = ListItemsByType(types[i], recurse); - if (!TryFindGroupMeetingAmount(items, amounts[i], grouper, out _, out _)) - { - return i; - } - } - - for (var i = 0; i < types.Length; i++) - { - using var items = ListItemsByType(types[i], recurse); - if (TryFindGroupMeetingAmount(items, amounts[i], grouper, out var start, out var len)) - { - ConsumeSlice(items, start, len, amounts[i], callback); - } - } - - return -1; - } - - public int ConsumeTotal(Type[][] types, ReadOnlySpan amounts, bool recurse = true, OnItemConsumed callback = null) - { - if (types.Length != amounts.Length) - { - throw new ArgumentException("length of types and amounts must match"); - } - - // Phase 1: validate every slot before any consume (all-or-nothing). - for (var i = 0; i < types.Length; i++) - { - if (GetAmount(types[i], recurse) < amounts[i]) - { - return i; - } - } - - // Phase 2: materialize per slot and consume. - for (var i = 0; i < types.Length; i++) - { - using var items = ListItemsByType(types[i], recurse); - ConsumeSlice(items, 0, items.Count, amounts[i], callback); - } - - return -1; - } - - public int ConsumeTotal( - ReadOnlySpan types, ReadOnlySpan amounts, bool recurse = true, OnItemConsumed callback = null + Type[] types, int[] amounts, bool recurse, OnItemConsumed callback, + CheckItemGroup grouper ) { if (types.Length != amounts.Length) @@ -755,18 +820,343 @@ public partial class Container : Item throw new ArgumentException("length of types and amounts must match"); } - for (var i = 0; i < types.Length; i++) + if (grouper == null) { - if (GetAmount(types[i], recurse) < amounts[i]) + throw new ArgumentNullException(nameof(grouper)); + } + + var items = new Item[types.Length][][]; + var totals = new int[types.Length][]; + + for (var i = 0; i < types.Length; ++i) + { + var type = types[i]; + + using var typedItems = ListItemsByType(type, recurse); + + var groups = new List>(); + var idx = 0; + + while (idx < typedItems.Count) + { + var a = typedItems[idx++]; + var group = new List + { + a + }; + + while (idx < typedItems.Count) + { + var b = typedItems[idx]; + var v = grouper(a, b); + + if (v == 0) + { + group.Add(b); + } + else + { + break; + } + + ++idx; + } + + groups.Add(group); + } + + items[i] = new Item[groups.Count][]; + totals[i] = new int[groups.Count]; + + var hasEnough = false; + + for (var j = 0; j < groups.Count; ++j) + { + items[i][j] = groups[j].ToArray(); + + for (var k = 0; k < items[i][j].Length; ++k) + { + totals[i][j] += items[i][j][k].Amount; + } + + if (totals[i][j] >= amounts[i]) + { + hasEnough = true; + } + } + + if (!hasEnough) { return i; } } - for (var i = 0; i < types.Length; i++) + for (var i = 0; i < items.Length; ++i) { - using var items = ListItemsByType(types[i], recurse); - ConsumeSlice(items, 0, items.Count, amounts[i], callback); + for (var j = 0; j < items[i].Length; ++j) + { + if (totals[i][j] >= amounts[i]) + { + var need = amounts[i]; + + for (var k = 0; k < items[i][j].Length; ++k) + { + var item = items[i][j][k]; + + var theirAmount = item.Amount; + + if (theirAmount < need) + { + callback?.Invoke(item, theirAmount); + + item.Consume(theirAmount); + need -= theirAmount; + } + else + { + callback?.Invoke(item, need); + + item.Consume(need); + break; + } + } + + break; + } + } + } + + return -1; + } + + public int ConsumeTotalGrouped( + Type[][] types, int[] amounts, bool recurse, OnItemConsumed callback, + CheckItemGroup grouper + ) + { + if (types.Length != amounts.Length) + { + throw new ArgumentException("length of types and amounts must match"); + } + + if (grouper == null) + { + throw new ArgumentNullException(nameof(grouper)); + } + + var items = new Item[types.Length][][]; + var totals = new int[types.Length][]; + + for (var i = 0; i < types.Length; ++i) + { + using var typedItems = ListItemsByType(types[i], recurse); + + var groups = new List>(); + var idx = 0; + + while (idx < typedItems.Count) + { + var a = typedItems[idx++]; + var group = new List + { + a + }; + + while (idx < typedItems.Count) + { + var b = typedItems[idx]; + var v = grouper(a, b); + + if (v == 0) + { + group.Add(b); + } + else + { + break; + } + + ++idx; + } + + groups.Add(group); + } + + items[i] = new Item[groups.Count][]; + totals[i] = new int[groups.Count]; + + var hasEnough = false; + + for (var j = 0; j < groups.Count; ++j) + { + items[i][j] = groups[j].ToArray(); + + for (var k = 0; k < items[i][j].Length; ++k) + { + totals[i][j] += items[i][j][k].Amount; + } + + if (totals[i][j] >= amounts[i]) + { + hasEnough = true; + } + } + + if (!hasEnough) + { + return i; + } + } + + for (var i = 0; i < items.Length; ++i) + { + for (var j = 0; j < items[i].Length; ++j) + { + if (totals[i][j] >= amounts[i]) + { + var need = amounts[i]; + + for (var k = 0; k < items[i][j].Length; ++k) + { + var item = items[i][j][k]; + + var theirAmount = item.Amount; + + if (theirAmount < need) + { + callback?.Invoke(item, theirAmount); + + item.Consume(theirAmount); + need -= theirAmount; + } + else + { + callback?.Invoke(item, need); + + item.Consume(need); + break; + } + } + + break; + } + } + } + + return -1; + } + + public int ConsumeTotal(Type[][] types, int[] amounts, bool recurse = true, OnItemConsumed callback = null) + { + if (types.Length != amounts.Length) + { + throw new ArgumentException("length of types and amounts must match"); + } + + var items = new Item[types.Length][]; + var totals = new int[types.Length]; + + for (var i = 0; i < types.Length; ++i) + { + using var typedItems = ListItemsByType(types[i], recurse); + + items[i] = new Item[typedItems.Count]; + + for (var j = 0; j < typedItems.Count; ++j) + { + items[i][j] = typedItems[j]; + totals[i] += typedItems[j].Amount; + } + + if (totals[i] < amounts[i]) + { + return i; + } + } + + for (var i = 0; i < types.Length; ++i) + { + var need = amounts[i]; + + for (var j = 0; j < items[i].Length; ++j) + { + var item = items[i][j]; + + var theirAmount = item.Amount; + + if (theirAmount < need) + { + callback?.Invoke(item, theirAmount); + + item.Consume(theirAmount); + need -= theirAmount; + } + else + { + callback?.Invoke(item, need); + + item.Consume(need); + break; + } + } + } + + return -1; + } + + public int ConsumeTotal(Type[] types, int[] amounts, bool recurse = true, OnItemConsumed callback = null) + { + if (types.Length != amounts.Length) + { + throw new ArgumentException("length of types and amounts must match"); + } + + var items = new Item[types.Length][]; + var totals = new int[types.Length]; + + for (var i = 0; i < types.Length; ++i) + { + using var typedItems = ListItemsByType(types[i], recurse); + + items[i] = new Item[typedItems.Count]; + + for (var j = 0; j < typedItems.Count; ++j) + { + items[i][j] = typedItems[j]; + totals[i] += typedItems[j].Amount; + } + + if (totals[i] < amounts[i]) + { + return i; + } + } + + for (var i = 0; i < types.Length; ++i) + { + var need = amounts[i]; + + for (var j = 0; j < items[i].Length; ++j) + { + var item = items[i][j]; + + var theirAmount = item.Amount; + + if (theirAmount < need) + { + callback?.Invoke(item, theirAmount); + + item.Consume(theirAmount); + need -= theirAmount; + } + else + { + callback?.Invoke(item, need); + + item.Consume(need); + break; + } + } } return -1; @@ -774,36 +1164,69 @@ public partial class Container : Item public bool ConsumeTotal(Type type, int amount = 1, bool recurse = true, OnItemConsumed callback = null) { - if (!HasAmount(type, amount, recurse)) + var total = 0; + + using var typedItems = ListItemsByType(type, recurse); + + // First pass, compute total + foreach (var item in typedItems) { - return false; + total += item.Amount; + + if (total >= amount) + { + break; + } } - using var items = ListItemsByType(type, recurse); - ConsumeSlice(items, 0, items.Count, amount, callback); - return true; + // We have enough, so consume it + if (total >= amount) + { + var need = amount; + + foreach (var item in typedItems) + { + var theirAmount = item.Amount; + + if (theirAmount < need) + { + callback?.Invoke(item, theirAmount); + + item.Consume(theirAmount); + need -= theirAmount; + } + else + { + callback?.Invoke(item, need); + + item.Consume(need); + return true; + } + } + } + + return false; } public int ConsumeUpTo(Type type, int amount, bool recurse = true) { var consumed = 0; - var toDelete = PooledRefQueue.Create(); + using var toDelete = PooledRefQueue.Create(); - RecurseConsumeUpTo(this, type, amount, recurse, ref consumed, ref toDelete); + RecurseConsumeUpTo(this, type, amount, recurse, ref consumed, toDelete); while (toDelete.Count > 0) { toDelete.Dequeue().Delete(); } - toDelete.Dispose(); - return consumed; } private static void RecurseConsumeUpTo( - Item current, Type type, int amount, bool recurse, ref int consumed, ref PooledRefQueue toDelete + Item current, Type type, int amount, bool recurse, ref int consumed, + PooledRefQueue toDelete ) { if (current == null || current.Items.Count == 0) @@ -837,16 +1260,196 @@ public partial class Container : Item } else if (recurse && item is Container) { - RecurseConsumeUpTo(item, type, amount, true, ref consumed, ref toDelete); + RecurseConsumeUpTo(item, type, amount, true, ref consumed, toDelete); } } } + public int GetBestGroupAmount(Type type, bool recurse, CheckItemGroup grouper) + { + if (grouper == null) + { + throw new ArgumentNullException(nameof(grouper)); + } + + var best = 0; + + using var typedItems = ListItemsByType(type, recurse); + + var groups = new List>(); + var idx = 0; + + while (idx < typedItems.Count) + { + var a = typedItems[idx++]; + var group = new List + { + a + }; + + while (idx < typedItems.Count) + { + var b = typedItems[idx]; + var v = grouper(a, b); + + if (v == 0) + { + group.Add(b); + } + else + { + break; + } + + ++idx; + } + + groups.Add(group); + } + + for (var i = 0; i < groups.Count; ++i) + { + var items = groups[i].ToArray(); + + var total = 0; + + for (var j = 0; j < items.Length; ++j) + { + total += items[j].Amount; + } + + if (total >= best) + { + best = total; + } + } + + return best; + } + public int GetBestGroupAmount(Type[] types, bool recurse, CheckItemGroup grouper) { - ArgumentNullException.ThrowIfNull(grouper); - using var items = ListItemsByType(types, recurse); - return BestGroupTotal(items, grouper); + if (grouper == null) + { + throw new ArgumentNullException(nameof(grouper)); + } + + var best = 0; + + var typedItems = ListItemsByType(types, recurse); + + var groups = new List>(); + var idx = 0; + + while (idx < typedItems.Count) + { + var a = typedItems[idx++]; + var group = new List + { + a + }; + + while (idx < typedItems.Count) + { + var b = typedItems[idx]; + var v = grouper(a, b); + + if (v == 0) + { + group.Add(b); + } + else + { + break; + } + + ++idx; + } + + groups.Add(group); + } + + for (var j = 0; j < groups.Count; ++j) + { + var items = groups[j].ToArray(); + var total = 0; + + foreach (var item in items) + { + total += item.Amount; + } + + if (total >= best) + { + best = total; + } + } + + return best; + } + + public int GetBestGroupAmount(Type[][] types, bool recurse, CheckItemGroup grouper) + { + if (grouper == null) + { + throw new ArgumentNullException(nameof(grouper)); + } + + var best = 0; + + for (var i = 0; i < types.Length; ++i) + { + using var typedItems = ListItemsByType(types[i], recurse); + + var groups = new List>(); + var idx = 0; + + while (idx < typedItems.Count) + { + var a = typedItems[idx++]; + var group = new List + { + a + }; + + while (idx < typedItems.Count) + { + var b = typedItems[idx]; + var v = grouper(a, b); + + if (v == 0) + { + group.Add(b); + } + else + { + break; + } + + ++idx; + } + + groups.Add(group); + } + + for (var j = 0; j < groups.Count; ++j) + { + var items = groups[j].ToArray(); + var total = 0; + + for (var k = 0; k < items.Length; ++k) + { + total += items[k].Amount; + } + + if (total >= best) + { + best = total; + } + } + } + + return best; } public int GetAmount(Type type, bool recurse = true) @@ -878,7 +1481,6 @@ public partial class Container : Item return total; } - public Item FindItemByType(Type type, bool recurse = true) { foreach (var item in FindItems(recurse)) @@ -933,112 +1535,27 @@ public partial class Container : Item return null; } - // Sums Item.Amount of all items matching `type`, bailing as soon as - // `amount` is reached. Walks the alloc-free FindItemsByType enumerator — - // no list materialization. Used as the Phase-1 sufficiency check by the - // ConsumeTotal overloads. - private bool HasAmount(Type type, int amount, bool recurse) + private struct ItemStackEntry { - var total = 0; - foreach (var item in FindItemsByType(type, recurse)) - { - total += item.Amount; - if (total >= amount) - { - return true; - } - } - return false; - } + public readonly Item m_StackItem; + public readonly Item m_DropItem; - // Walks `items` (BFS-ordered snapshot) in adjacency-based groups defined - // by `grouper`. Returns the first group whose Amount sum is >= `amount`, - // emitting its slice [start, start+length). Streaming, no per-group list. - private static bool TryFindGroupMeetingAmount( - PooledRefList items, int amount, CheckItemGroup grouper, out int groupStart, out int groupLength - ) - { - var i = 0; - while (i < items.Count) + public ItemStackEntry(Item stack, Item drop) { - var leader = items[i]; - var start = i; - var total = leader.Amount; - i++; - while (i < items.Count && grouper(leader, items[i]) == 0) - { - total += items[i].Amount; - i++; - } - if (total >= amount) - { - groupStart = start; - groupLength = i - start; - return true; - } - } - groupStart = 0; - groupLength = 0; - return false; - } - - // Returns the largest group sum across `items` partitioned by `grouper`. - // Streaming, no per-group list. - private static int BestGroupTotal(PooledRefList items, CheckItemGroup grouper) - { - var best = 0; - var i = 0; - while (i < items.Count) - { - var leader = items[i]; - var total = leader.Amount; - i++; - while (i < items.Count && grouper(leader, items[i]) == 0) - { - total += items[i].Amount; - i++; - } - if (total > best) - { - best = total; - } - } - return best; - } - - // Consumes `need` units from the slice [start, start+length) of `items`, - // firing `callback` once per item touched with the actual delta. Items - // with Amount <= delta are deleted via Item.Consume. - private static void ConsumeSlice(PooledRefList items, int start, int length, int need, OnItemConsumed callback) - { - for (var k = 0; k < length && need > 0; k++) - { - var item = items[start + k]; - var theirAmount = item.Amount; - if (theirAmount < need) - { - callback?.Invoke(item, theirAmount); - item.Consume(theirAmount); - need -= theirAmount; - } - else - { - callback?.Invoke(item, need); - item.Consume(need); - return; - } + m_StackItem = stack; + m_DropItem = drop; } } } public class ContainerData { - private static readonly ILogger _logger = LogFactory.GetLogger(typeof(ContainerData)); - private static readonly Dictionary _table; + private static ILogger logger = LogFactory.GetLogger(typeof(ContainerData)); + private static readonly Dictionary m_Table; static ContainerData() { - _table = new Dictionary(); + m_Table = new Dictionary(); var path = Path.Combine(Core.BaseDirectory, "Data/containers.cfg"); @@ -1096,13 +1613,13 @@ public class ContainerData { var id = Utility.ToInt32(aIDs[i]); - if (_table.ContainsKey(id)) + if (m_Table.ContainsKey(id)) { - _logger.Warning("double ItemID entry in Data\\containers.cfg"); + logger.Warning("double ItemID entry in Data\\containers.cfg"); } else { - _table[id] = data; + m_Table[id] = data; } } } @@ -1135,7 +1652,7 @@ public class ContainerData public static ContainerData GetData(int itemID) { - _table.TryGetValue(itemID, out var data); + m_Table.TryGetValue(itemID, out var data); return data ?? Default; } } diff --git a/Projects/Server/Items/Containers.cs b/Projects/Server/Items/Containers.cs index e3c78d723..e436b78c0 100644 --- a/Projects/Server/Items/Containers.cs +++ b/Projects/Server/Items/Containers.cs @@ -1,17 +1,13 @@ -using ModernUO.Serialization; using Server.Accounting; using Server.Network; namespace Server.Items; -[SerializationGenerator(0, false)] -public partial class BankBox : Container +public class BankBox : Container { - [SerializableField(0, setter: "private")] - private Mobile _owner; - - [SerializableField(1, setter: "private")] - private bool _opened; + public BankBox(Serial serial) : base(serial) + { + } public BankBox(Mobile owner) : base(0xE7C) { @@ -24,16 +20,14 @@ public partial class BankBox : Container public override bool IsVirtualItem => true; + public Mobile Owner { get; private set; } + + public bool Opened { get; private set; } + public static bool SendDeleteOnClose { get; set; } public void Open() { - if (!ServerFeatureFlags.BankAccess && Owner?.AccessLevel < AccessLevel.Administrator) - { - Owner.SendMessage(0x22, "Bank access is temporarily disabled."); - return; - } - Opened = true; if (Owner != null) @@ -51,9 +45,38 @@ public partial class BankBox : Container } } - [AfterDeserialization] - private void AfterDeserialization() + public override void Serialize(IGenericWriter writer) { + base.Serialize(writer); + + writer.Write(0); // version + + writer.Write(Owner); + writer.Write(Opened); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: + { + Owner = reader.ReadEntity(); + Opened = reader.ReadBool(); + + break; + } + } + + if (ItemID == 0xE41) + { + ItemID = 0xE7C; + } + if (Owner == null) { Timer.DelayCall(Delete); diff --git a/Projects/Server/Items/DecayScheduler.cs b/Projects/Server/Items/DecayScheduler.cs deleted file mode 100644 index 70dc1ef27..000000000 --- a/Projects/Server/Items/DecayScheduler.cs +++ /dev/null @@ -1,436 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: DecayScheduler.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; - -namespace Server.Items; - -/// -/// Timer wheel-based decay scheduler with O(1) registration/unregistration. -/// Uses coarse-grained HashSet buckets for time intervals, with a PriorityQueue -/// for fine-grained processing of items due within the current window. -/// -/// An item that becomes decay eligible must be passed to -/// once its parent, map and location are final. The scheduler drops items that stop being eligible, -/// but nothing enrolls one that becomes eligible while untracked. -/// -/// -/// and overrides must be stable rather than -/// recomputed per read: is read repeatedly, and a value that -/// changes between reads re-buckets the item every tick and decays it at the wrong time. -/// -/// -public class DecayScheduler : Timer -{ - // Item.DecaySlot values; any non-negative value is a bucket index. - internal const sbyte SlotNone = -1; - internal const sbyte SlotOverflow = -2; - internal const sbyte SlotActive = -3; - - private const int BucketCount = 12; - private static int _maxItemsPerTick; - private static TimeSpan _tickInterval; - private static TimeSpan _bucketInterval; - private static int _jitterMaxMilliseconds; - - // Timer wheel buckets (HashSets for O(1) add/remove) - private static HashSet[] _buckets; - private static int _currentBucketIndex; - private static DateTime _nextBucketRotation; - - // Overflow bucket (separate from regular rotation, for items with decay > total bucket span) - private static HashSet _overflowBucket; - private static TimeSpan _totalBucketSpan; - private static DateTime _nextOverflowCheck; - - // Active processing queue for items due within current bucket window - private static readonly PriorityQueue _activeQueue = new(); - - public static DecayScheduler Shared { get; private set; } - - public static void Configure() - { - _maxItemsPerTick = ServerConfiguration.GetSetting("decay.maxItemsPerTick", 250); - _tickInterval = ServerConfiguration.GetSetting("decay.tickInterval", TimeSpan.FromMilliseconds(256)); - _bucketInterval = ServerConfiguration.GetSetting("decay.bucketInterval", TimeSpan.FromMinutes(5)); - _jitterMaxMilliseconds = ServerConfiguration.GetSetting("decay.jitterMaxMs", 25); // ±25ms jitter - - _buckets = new HashSet[BucketCount]; - for (var i = 0; i < BucketCount; i++) - { - _buckets[i] = []; - } - - // Overflow bucket is separate from the timer wheel - _overflowBucket = []; - _totalBucketSpan = TimeSpan.FromTicks(_bucketInterval.Ticks * BucketCount); - - var now = Core.Now; - _nextBucketRotation = now + _bucketInterval; - _nextOverflowCheck = now + _totalBucketSpan; - - Shared = new DecayScheduler(); - } - - private DecayScheduler() : base(_tickInterval, _tickInterval) - { - } - - /// - /// Test hook: drops all tracked items and re-bases the rotation schedule on - /// , so deadlines left by a test that moved the clock do not suppress - /// rotations in the next one. - /// - internal static void ResetForTests() - { - Shared?.Stop(); - - _activeQueue.Clear(); - _overflowBucket.Clear(); - - for (var i = 0; i < BucketCount; i++) - { - _buckets[i].Clear(); - } - - _currentBucketIndex = 0; - - var now = Core.Now; - _nextBucketRotation = now + _bucketInterval; - _nextOverflowCheck = now + _totalBucketSpan; - } - - /// - /// Diagnostics: true if the item is currently tracked in any bucket, the overflow - /// bucket, or the active queue. - /// - internal static bool IsRegistered(Item item) - { - if (item == null) - { - return false; - } - - if (_overflowBucket.Contains(item)) - { - return true; - } - - for (var i = 0; i < BucketCount; i++) - { - if (_buckets[i].Contains(item)) - { - return true; - } - } - - foreach (var (queued, _) in _activeQueue.UnorderedItems) - { - if (queued == item) - { - return true; - } - } - - return false; - } - - /// - /// Checks if all decay tracking structures are empty. - /// - private static bool IsEmpty() - { - if (_activeQueue.Count > 0 || _overflowBucket.Count > 0) - { - return false; - } - - for (var i = 0; i < BucketCount; i++) - { - if (_buckets[i].Count > 0) - { - return false; - } - } - - return true; - } - - /// - /// Registers an item for decay tracking. Call after item becomes decay-eligible. - /// - public static void Register(Item item) - { - if (item?.Deleted != false || !item.CanDecay()) - { - return; - } - - // Start timer if not running - Shared.Start(); - - var decayTime = item.ScheduledDecayTime; - var timeUntilDecay = decayTime - Core.Now; - - // If already due or due very soon, add directly to active queue - if (timeUntilDecay <= _bucketInterval) - { - _activeQueue.Enqueue(item, decayTime); - item.DecaySlot = SlotActive; - } - // If beyond the total bucket span, add to overflow bucket - else if (timeUntilDecay > _totalBucketSpan) - { - _overflowBucket.Add(item); - item.DecaySlot = SlotOverflow; - } - else - { - // Calculate bucket index relative to current position - var bucketOffset = GetBucketOffset(timeUntilDecay); - var absoluteIndex = (_currentBucketIndex + bucketOffset) % BucketCount; - _buckets[absoluteIndex].Add(item); - item.DecaySlot = (sbyte)absoluteIndex; - } - } - - /// - /// Unregisters an item from decay tracking. Call when item is picked up, deleted, or otherwise ineligible. - /// - public static void Unregister(Item item) - { - if (item == null || item.DecaySlot == SlotNone) - { - return; - } - - var slot = item.DecaySlot; - item.DecaySlot = SlotNone; - - if (slot == SlotOverflow) - { - _overflowBucket.Remove(item); - } - else if (slot == SlotActive) - { - _activeQueue.Remove(item, out _, out _); - } - else - { - _buckets[slot].Remove(item); - } - } - - /// - /// Gets the bucket offset for an item based on time until decay. - /// Returns a value from 0 to BucketCount - 1, representing how many buckets ahead of current. - /// - private static int GetBucketOffset(TimeSpan timeUntilDecay) - { - // Items due within _bucketInterval go to active queue, so offset 0 means _bucketInterval to 2*_bucketInterval - var bucketOffset = (int)(timeUntilDecay.Ticks / _bucketInterval.Ticks) - 1; - - // Clamp to valid range within regular buckets - return Math.Clamp(bucketOffset, 0, BucketCount - 1); - } - - /// - /// Advances the wheel for . Returns true when nothing is left to track. - /// - internal static bool ProcessTick(DateTime now) - { - // Process items from active queue first (smaller queue = faster log(n) operations) - ProcessActiveQueue(now); - - // Then check if it's time to rotate buckets (adds items for next tick) - if (now >= _nextBucketRotation) - { - RotateBuckets(now); - } - - // Check overflow bucket on its own schedule (when items might enter the regular window) - if (now >= _nextOverflowCheck) - { - ProcessOverflow(now); - } - - return IsEmpty(); - } - - protected override void OnTick() - { - // Stop timer if nothing left to track - if (ProcessTick(Core.Now)) - { - Stop(); - return; - } - - // Apply jitter for next tick to prevent synchronization with other systems - if (_jitterMaxMilliseconds > 0) - { - var jitter = Utility.Random(-_jitterMaxMilliseconds, _jitterMaxMilliseconds * 2 + 1); - Interval = _tickInterval + TimeSpan.FromMilliseconds(jitter); - } - } - - /// - /// Rotates the timer wheel, moving the next bucket's contents into the active queue. - /// - private static void RotateBuckets(DateTime now) - { - _nextBucketRotation = now + _bucketInterval; - - // Get the next bucket to process - var bucket = _buckets[_currentBucketIndex]; - - // Move all items from this bucket into the active queue (with validation) - foreach (var item in bucket) - { - if (item.Deleted || !item.CanDecay()) - { - item.DecaySlot = SlotNone; // Lazy cleanup - item was picked up or deleted - continue; - } - - var decayTime = item.ScheduledDecayTime; - var timeUntilDecay = decayTime - now; - - if (timeUntilDecay > _bucketInterval) - { - // Deadline was pushed out (SetLastMoved/RestartDecay) - re-bucket or move to overflow - if (timeUntilDecay > _totalBucketSpan) - { - // Extended beyond total span - move to overflow - _overflowBucket.Add(item); - item.DecaySlot = SlotOverflow; - } - else - { - // Re-bucket within regular buckets - var bucketOffset = GetBucketOffset(timeUntilDecay); - var actualIndex = (_currentBucketIndex + bucketOffset) % BucketCount; - - if (actualIndex != _currentBucketIndex) - { - _buckets[actualIndex].Add(item); - item.DecaySlot = (sbyte)actualIndex; - } - else - { - // Still maps to current bucket - add to active queue - _activeQueue.Enqueue(item, decayTime); - item.DecaySlot = SlotActive; - } - } - continue; - } - - // Due within next window - add to active queue - _activeQueue.Enqueue(item, decayTime); - item.DecaySlot = SlotActive; - } - - // Clear the processed bucket - bucket.Clear(); - - // Advance to next bucket - _currentBucketIndex = (_currentBucketIndex + 1) % BucketCount; - } - - /// - /// Processes the overflow bucket, moving items that are now within the regular bucket window. - /// - private static void ProcessOverflow(DateTime now) - { - _nextOverflowCheck = now + _totalBucketSpan; - - // Move items that are now within the regular bucket span - _overflowBucket.RemoveWhere(item => - { - if (item.Deleted || !item.CanDecay()) - { - item.DecaySlot = SlotNone; - return true; // Remove invalid items - } - - var decayTime = item.ScheduledDecayTime; - var timeUntilDecay = decayTime - now; - - if (timeUntilDecay > _totalBucketSpan) - { - return false; // Keep in overflow - } - - // Now within regular bucket range - move to appropriate bucket - if (timeUntilDecay <= _bucketInterval) - { - _activeQueue.Enqueue(item, decayTime); - item.DecaySlot = SlotActive; - } - else - { - var bucketOffset = GetBucketOffset(timeUntilDecay); - var actualIndex = (_currentBucketIndex + bucketOffset) % BucketCount; - _buckets[actualIndex].Add(item); - item.DecaySlot = (sbyte)actualIndex; - } - - return true; // Remove from overflow - }); - } - - /// - /// Processes items from the active queue that are due for decay. - /// - private static void ProcessActiveQueue(DateTime now) - { - var processed = 0; - - while (_activeQueue.TryPeek(out var item, out var scheduledTime) && processed < _maxItemsPerTick) - { - // Not yet due - stop processing - if (scheduledTime > now) - { - break; - } - - processed++; - _activeQueue.Dequeue(); - item.DecaySlot = SlotNone; - - if (item.Deleted || !item.CanDecay()) - { - continue; - } - - if (item.ScheduledDecayTime > now) - { - Register(item); - } - else if (item.OnDecay()) - { - item.Delete(); - } - else - { - // Refused by the region. Restart the clock rather than dropping the item, which has - // already left the queue; re-registering as-is would spin on a due time in the past. - item.RestartDecay(); - } - } - } -} diff --git a/Projects/Server/Items/Item.Messages.cs b/Projects/Server/Items/Item.Messages.cs deleted file mode 100644 index b8e24aa17..000000000 --- a/Projects/Server/Items/Item.Messages.cs +++ /dev/null @@ -1,139 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Item.Messages.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using Server.Buffers; -using Server.Network; - -namespace Server; - -public partial class Item -{ - private readonly struct PublicVisibilityFilter : IBroadcastFilter - { - private readonly Item _source; - private readonly Point3D _worldLoc; - - public PublicVisibilityFilter(Item source, Point3D worldLoc) - { - _source = source; - _worldLoc = worldLoc; - } - - public bool Allow(NetState state) - { - var m = state.Mobile; - return m.CanSee(_source) && m.InRange(_worldLoc, _source.GetUpdateRange(m)); - } - } - - public void PublicOverheadMessage(MessageType type, int hue, bool ascii, ReadOnlySpan text) - { - var worldLoc = GetWorldLocation(); - OutgoingMessagePackets.BroadcastMessage( - m_Map, worldLoc, GetMaxUpdateRange(), - Serial, m_ItemID, type, hue, 3, ascii, "ENU", Name, text, - new PublicVisibilityFilter(this, worldLoc) - ); - } - - public void PublicOverheadMessage(MessageType type, int hue, int number, ReadOnlySpan args = default) - { - var worldLoc = GetWorldLocation(); - OutgoingMessagePackets.BroadcastMessageLocalized( - m_Map, worldLoc, GetMaxUpdateRange(), - Serial, m_ItemID, type, hue, 3, number, Name, args, - new PublicVisibilityFilter(this, worldLoc) - ); - } - - public void SendLocalizedMessageTo(Mobile to, int number, ReadOnlySpan args = default) - { - if (Deleted || !to.CanSee(this)) - { - return; - } - - to?.NetState?.SendMessageLocalized(Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", args); - } - - public void SendLocalizedMessageTo(Mobile to, int number, AffixType affixType, ReadOnlySpan affix, ReadOnlySpan args) - { - if (Deleted || !to.CanSee(this)) - { - return; - } - - to?.NetState?.SendMessageLocalizedAffix( - Serial, - ItemID, - MessageType.Regular, - 0x3B2, - 3, - number, - "", - affixType, - affix, - args - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SendLocalizedMessageTo(Mobile to, int number, int hue, ReadOnlySpan args = default) - => to?.NetState?.SendMessageLocalized(Serial, ItemID, MessageType.Regular, hue, 3, number, "", args); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SendMessageTo(Mobile to, ReadOnlySpan text, int hue = 0x3B2) - => to?.NetState?.SendMessage(Serial, ItemID, MessageType.Regular, hue, 3, false, "ENU", "", text); - - // ---------- Interpolated handler overloads ---------- - - public void PublicOverheadMessage(MessageType type, int hue, bool ascii, ref RawInterpolatedStringHandler text) - { - PublicOverheadMessage(type, hue, ascii, text.Text); - text.Clear(); - } - - public void PublicOverheadMessage(MessageType type, int hue, int number, ref RawInterpolatedStringHandler args) - { - PublicOverheadMessage(type, hue, number, args.Text); - args.Clear(); - } - - public void SendLocalizedMessageTo(Mobile to, int number, ref RawInterpolatedStringHandler args) - { - SendLocalizedMessageTo(to, number, args.Text); - args.Clear(); - } - - public void SendLocalizedMessageTo(Mobile to, int number, AffixType affixType, ReadOnlySpan affix, ref RawInterpolatedStringHandler args) - { - SendLocalizedMessageTo(to, number, affixType, affix, args.Text); - args.Clear(); - } - - public void SendLocalizedMessageTo(Mobile to, int number, int hue, ref RawInterpolatedStringHandler args) - { - SendLocalizedMessageTo(to, number, hue, args.Text); - args.Clear(); - } - - public void SendMessageTo(Mobile to, int hue, ref RawInterpolatedStringHandler text) - { - SendMessageTo(to, text.Text, hue); - text.Clear(); - } -} diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 13f249f2f..e9be609cc 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Item.cs * * * @@ -14,7 +14,6 @@ *************************************************************************/ using System; -using System.Diagnostics; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; @@ -132,7 +131,7 @@ public class BounceInfo var loc = reader.ReadPoint3D(); var worldLoc = reader.ReadPoint3D(); - var parent = reader.ReadEntity(); + IEntity parent = reader.ReadEntity(); return new BounceInfo(map, loc, worldLoc, parent); } @@ -193,7 +192,7 @@ public enum ExpandFlag Spawner = 0x100 } -public partial class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEntity, IValueLinkListNode +public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEntity, IValueLinkListNode { private static readonly ILogger logger = LogFactory.GetLogger(typeof(Item)); @@ -209,10 +208,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert private ItemDelta m_DeltaFlags; private Direction m_Direction; private ImplFlag m_Flags; - - // Where DecayScheduler tracks this item. Not serialized; PostDeserialize re-registers. - internal sbyte DecaySlot = DecayScheduler.SlotNone; - private int m_Hue; private int m_ItemID; private Layer m_Layer; @@ -230,12 +225,12 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert m_ItemID = itemID; Serial = World.NewItem; - // Assigned directly: the Visible/Movable setters and SetLastMoved() would register the item - // for decay while m_Map is still null, then unregister it once it is Map.Internal. - m_Flags = ImplFlag.Visible | ImplFlag.Movable; + Visible = true; + Movable = true; Amount = 1; m_Map = Map.Internal; - LastMoved = Core.Now; + + SetLastMoved(); World.AddEntity(this); } @@ -333,27 +328,9 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert public virtual TimeSpan DecayTime => DefaultDecayTime; [CommandProperty(AccessLevel.GameMaster)] - public virtual bool Decays => Movable && Visible && Spawner == null; + public virtual bool Decays => Movable && Visible; - private DateTime _lastMoved; - - public DateTime LastMoved - { - get => _lastMoved; - set - { - _lastMoved = value; - - // A move at or past the reset stamp supersedes it; drop it so the CompactInfo can collapse. - var info = LookupCompactInfo(); - - if (info != null && info.m_DecayReset != default && info.m_DecayReset <= value) - { - info.m_DecayReset = default; - VerifyCompactInfo(); - } - } - } + public DateTime LastMoved { get; set; } [CommandProperty(AccessLevel.GameMaster)] public bool Stackable @@ -376,7 +353,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { var worldLoc = GetWorldLocation(); - var removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); + Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); foreach (var state in m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange())) { @@ -391,7 +368,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } Delta(ItemDelta.Update); - RestartDecay(); } } } @@ -407,7 +383,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert SetFlag(ImplFlag.Movable, value); Delta(ItemDelta.Update); - RestartDecay(); } } } @@ -499,27 +474,25 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } set { - var defaultWeight = DefaultWeight; - var info = LookupCompactInfo(); - var oldWeight = info is { m_Weight: >= 0 } ? info.m_Weight : defaultWeight; - - if (oldWeight == value) + if (Weight != value) { - return; - } + var info = AcquireCompactInfo(); - if (value < 0 || defaultWeight == value) - { - info?.m_Weight = -1; - VerifyCompactInfo(); - } - else - { - (info ?? AcquireCompactInfo()).m_Weight = value; - } + var oldPileWeight = PileWeight; - UpdateTotal(this, TotalType.Weight, PileWeight - (int)Math.Ceiling(oldWeight * Amount)); - InvalidateProperties(); + info.m_Weight = value; + + if (info.m_Weight < 0) + { + VerifyCompactInfo(); + } + + var newPileWeight = PileWeight; + + UpdateTotal(this, TotalType.Weight, newPileWeight - oldPileWeight); + + InvalidateProperties(); + } } } @@ -559,18 +532,8 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } } - // SendMobileIncoming (0x78) sends only the first item per layer (it dedupes via the - // `layers` span). Equipment Layer comes from tiledata, so e.g. a two-handed weapon and a - // shield both resolve to Layer.TwoHanded; a later same-layer item sent on its own via - // EquipUpdate (0x2E) or its OPL would leave the client holding two items on one equipment - // slot (a use-after-free on the legacy 2D client when that slot is later torn down). - // Returns true if this equipped item is such a later duplicate and must not be sent alone. - public bool IsDupedEquipLayer() => m_Parent is Mobile m && m.FindItemOnLayer(m_Layer) != this; - public List Items => LookupItems() ?? EmptyItems; - public int LookupContainerVersion() => (this as Container)?._version ?? LookupCompactInfo()?.Version ?? 0; - [CommandProperty(AccessLevel.GameMaster)] public IEntity RootParent { @@ -630,19 +593,19 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert get => LookupCompactInfo()?.m_Name ?? DefaultName; set { - var info = LookupCompactInfo(); - - if (value == null || value == DefaultName) + if (value == null || value != DefaultName) { - info?.m_Name = null; - VerifyCompactInfo(); - } - else - { - (info ?? AcquireCompactInfo()).m_Name = value; - } + var info = AcquireCompactInfo(); - InvalidateProperties(); + info.m_Name = value; + + if (info.m_Name == null) + { + VerifyCompactInfo(); + } + + InvalidateProperties(); + } } } @@ -767,12 +730,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert public static bool ScissorCopyLootType { get; set; } - /// - /// True when the item was produced by the crafting system rather than bought or looted. - /// - [CommandProperty(AccessLevel.GameMaster)] - public bool PlayerConstructed { get; set; } - [CommandProperty(AccessLevel.GameMaster)] public bool QuestItem { @@ -823,22 +780,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert public virtual int HuedItemID => m_ItemID; - public ObjectPropertyList PropertyList - { - get - { - if (m_PropertyList == null) - { - // Publish the list before building it so a nested InvalidateProperties can see the - // build in progress and defer instead of recursing into a second throwaway list. - var list = new ObjectPropertyList(this); - m_PropertyList = list; - InitializePropertyList(list); - } - - return m_PropertyList; - } - } + public ObjectPropertyList PropertyList => m_PropertyList ??= InitializePropertyList(new ObjectPropertyList(this)); /// /// Overridable. Fills an with everything applicable. By default, this invokes @@ -850,7 +792,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert public virtual void GetProperties(IPropertyList list) { AddNameProperties(list); - AppendChildNameProperties(list); } [IgnoreDupe] @@ -861,9 +802,13 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert [CommandProperty(AccessLevel.Counselor)] public Serial Serial { get; } + public byte SerializedThread { get; set; } + public int SerializedPosition { get; set; } + public int SerializedLength { get; set; } + public virtual void Serialize(IGenericWriter writer) { - writer.Write(11); // version + writer.Write(9); // version var flags = SaveFlag.None; @@ -973,11 +918,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { flags |= SaveFlag.SavedFlags; } - - if (info.m_DecayReset > LastMoved) - { - flags |= SaveFlag.DecayReset; - } } if (info == null || info.m_Weight < 0) @@ -1008,21 +948,16 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert flags |= SaveFlag.ImplFlags; } - if (PlayerConstructed) - { - flags |= SaveFlag.PlayerConstructed; - } - writer.Write((int)flags); - // Anchored: shifted by downtime at load, so time-since-moved is preserved and the - // bytes are stable across saves while the item does not move. - writer.WriteAnchoredTime(LastMoved); + /* begin last moved time optimization */ + var ticks = LastMoved.Ticks; + var now = Core.Now.Ticks; - if (GetSaveFlag(flags, SaveFlag.DecayReset)) - { - writer.WriteAnchoredTime(info.m_DecayReset); - } + var minutes = new TimeSpan(now - ticks).TotalMinutes; + + writer.WriteEncodedInt((int)Math.Clamp(minutes, int.MinValue, int.MaxValue)); + /* end */ if (GetSaveFlag(flags, SaveFlag.Direction)) { @@ -1157,9 +1092,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert var oldLocation = GetWorldLocation(); var oldRealLocation = m_Location; - // Register at the end instead: CanDecay() reads parent, map and location, none of which - // are final yet. - LastMoved = Core.Now; + SetLastMoved(); if (Parent is Mobile mobile) { @@ -1201,9 +1134,9 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert if (m_Map != null) { - var oldWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); - var saWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); - var hsWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); + Span oldWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); + Span saWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); + Span hsWorldItem = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); foreach (var state in m_Map.GetClientsInRange(m_Location, GetMaxUpdateRange())) { @@ -1256,7 +1189,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { if (oldLocation.m_X != 0) { - var removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); + Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); foreach (var state in m_Map.GetClientsInRange(oldLocation, GetMaxUpdateRange())) { @@ -1294,8 +1227,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert Map = map; Location = location; } - - UpdateDecayRegistration(); } /// @@ -1341,12 +1272,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert OnMapChange(); - if (m_Parent == null) - { - // A map change is a move; nothing else updates decay registration for a raw Map change. - SetLastMoved(); - } - if (old == null || old == Map.Internal) { InvalidateProperties(); @@ -1455,14 +1380,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert return; } - // A second item sharing an equipment layer is omitted by SendMobileIncoming (0x78); - // sending it on its own via EquipUpdate/OPL would leave the client with two items on - // one slot. Skip the per-client sends entirely for the dupe. - if (IsDupedEquipLayer()) - { - return; - } - foreach (var state in map.GetClientsInRange(worldLoc, GetMaxUpdateRange())) { var m = state.Mobile; @@ -1514,7 +1431,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } OnDelete(); - DecayScheduler.Unregister(this); var items = LookupItems(); @@ -1557,8 +1473,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert ClearProperties(); } - public virtual bool SkipSerialization => false; - [IgnoreDupe] public ISpawner Spawner { @@ -1566,7 +1480,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert set { var info = AcquireCompactInfo(); - var oldValue = info.m_Spawner; info.m_Spawner = value; @@ -1574,11 +1487,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { VerifyCompactInfo(); } - - if (oldValue != value) - { - RestartDecay(); - } } } @@ -1609,7 +1517,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { if (m_Location.m_X != 0) { - var removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); + Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); foreach (var state in m_Map.GetClientsInRange(oldLocation, GetMaxUpdateRange())) { @@ -1771,7 +1679,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert || info.m_HeldBy != null || info.m_BlessedFor != null || info.m_Spawner != null - || info.m_DecayReset != default || info.m_TempFlags != 0 || info.m_SavedFlags != 0 || info.m_Weight >= 0; @@ -1782,17 +1689,17 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } } - public List LookupItems() => (this is Container container ? container._items : LookupCompactInfo()?.m_Items) ?? EmptyItems; + public List LookupItems() => (this is Container container ? container.m_Items : LookupCompactInfo()?.m_Items) ?? EmptyItems; public List AcquireItems() { if (this is Container cont) { - return cont._items ??= new List(); + return cont.m_Items ?? (cont.m_Items = new List()); } var info = AcquireCompactInfo(); - return info.m_Items ??= new List(); + return info.m_Items ?? (info.m_Items = new List()); } private void SetFlag(ImplFlag flag, bool value) @@ -1941,7 +1848,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { list.Add(1049643); // cursed } - else if (ServerFeatureFlags.InsuranceEnabled && Insured) + else if (Insured) { list.Add(1061682); // insured } @@ -2036,6 +1943,8 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { AddQuestItemProperty(list); } + + AppendChildNameProperties(list); } /// @@ -2269,7 +2178,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert return DeathMoveResult.MoveToBackpack; } - if (CheckNewbied() && !parent.Murderer) + if (CheckNewbied() && parent.Kills < 5) { return DeathMoveResult.MoveToBackpack; } @@ -2299,7 +2208,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert return DeathMoveResult.MoveToBackpack; } - if (CheckNewbied() && !parent.Murderer) + if (CheckNewbied() && parent.Kills < 5) { return DeathMoveResult.MoveToBackpack; } @@ -2356,85 +2265,9 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert public virtual bool OnDecay() => CanDecay() && Region.Find(Location, Map).OnDecay(this); - public DateTime ScheduledDecayTime - { - get - { - var reset = DecayResetTime; - var lastMoved = LastMoved; - - return (reset > lastMoved ? reset : lastMoved) + DecayTime; - } - } - - /// - /// When decay eligibility was last restored without the item moving, e.g. a GM unfreezing it. - /// The decay countdown runs from the later of this and . - /// - public DateTime DecayResetTime - { - get => LookupCompactInfo()?.m_DecayReset ?? default; - private set - { - if (value == default) - { - var info = LookupCompactInfo(); - - if (info != null && info.m_DecayReset != default) - { - info.m_DecayReset = default; - VerifyCompactInfo(); - } - } - else - { - AcquireCompactInfo().m_DecayReset = value; - } - } - } - - /// - /// Restarts the decay countdown without touching : call when decay - /// eligibility changes state (Movable/Visible/Spawner) or a region refuses a decay, where a - /// stale would otherwise decay the item on the next tick. - /// Stamps only when that extends the current deadline, then - /// updates the scheduler registration. - /// - public void RestartDecay() - { - if (CanDecay()) - { - var now = Core.Now; - - if (ScheduledDecayTime < now + DecayTime) - { - DecayResetTime = now; - } - } - - UpdateDecayRegistration(); - } - - public void UpdateDecayRegistration() - { - DecayScheduler.Unregister(this); - - if (CanDecay()) - { - DecayScheduler.Register(this); - } - else - { - // No countdown to anchor while ineligible; drop the stamp so the CompactInfo - // can collapse. Re-eligibility always re-anchors. - DecayResetTime = default; - } - } - public void SetLastMoved() { LastMoved = Core.Now; - UpdateDecayRegistration(); } public virtual bool CanStackWith(Item dropped) => @@ -2461,11 +2294,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } Amount += dropped.Amount; - if (PlayerConstructed != dropped.PlayerConstructed) - { - PlayerConstructed = false; - } - dropped.Delete(); if (playSound && from != null) @@ -2518,7 +2346,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert }; } - var bounds = ItemBounds.Bounds[itemID & 0x3FFF]; + var bounds = ItemBounds.Table[itemID & 0x3FFF]; if (doubled) { @@ -2554,19 +2382,9 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert private ObjectPropertyList InitializePropertyList(ObjectPropertyList list) { - list.IsBuilding = true; - - try - { - GetProperties(list); - AppendChildProperties(list); - list.Terminate(); - } - finally - { - list.IsBuilding = false; - } - + GetProperties(list); + AppendChildProperties(list); + list.Terminate(); return list; } @@ -2583,26 +2401,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert return; } - // Always a bug in the property getter, and there is no correct recovery: refuse rather than - // hide it. RELEASE keeps a possibly stale tooltip, DEBUG throws. - // See dev-docs/property-lists.md "Never Invalidate From Inside GetProperties". - if (m_PropertyList?.IsBuilding == true) - { - logger.Error( - "{Entity} called InvalidateProperties() while its property list was being built. Remove the side effect from the property getter, or defer it with Timer.DelayCall.\n{StackTrace}", - this, - new StackTrace() - ); - -#if DEBUG - throw new InvalidOperationException( - $"{this} invalidated its property list from inside GetProperties. Remove the side effect from the property getter." - ); -#else - return; -#endif - } - if (m_Map != null && m_Map != Map.Internal && !World.Loading) { int? oldHash; @@ -2674,6 +2472,15 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void SetSaveFlag(ref SaveFlag flags, SaveFlag toSet, bool setIf) + { + if (setIf) + { + flags |= toSet; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet) => (flags & toGet) != 0; @@ -2760,14 +2567,10 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { var version = reader.ReadInt(); - // Default for versions without a saved value. Stamp only: map and parent are still unread, - // and PostDeserialize registers every item once its state is final. - LastMoved = Core.Now; + SetLastMoved(); switch (version) { - case 11: - case 10: case 9: case 8: case 7: @@ -2775,11 +2578,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert { var flags = (SaveFlag)reader.ReadInt(); - if (version >= 11) - { - LastMoved = reader.ReadAnchoredTime(); - } - else if (version < 7) + if (version < 7) { LastMoved = reader.ReadDeltaTime(); } @@ -2797,18 +2596,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } } - if (version >= 10 && GetSaveFlag(flags, SaveFlag.DecayReset)) - { - var reset = version >= 11 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime(); - - // Pre-v11 LastMoved was stored at whole-minute precision; keep the - // stamp only while it still extends the deadline. - if (reset > LastMoved) - { - DecayResetTime = reset; - } - } - if (GetSaveFlag(flags, SaveFlag.Direction)) { m_Direction = (Direction)reader.ReadByte(); @@ -2882,7 +2669,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert if (GetSaveFlag(flags, SaveFlag.Parent)) { - var parent = reader.ReadSerial(); + Serial parent = reader.ReadSerial(); if (parent.IsMobile) { @@ -2909,7 +2696,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert if (this is Container) { - (this as Container)._items = items; + (this as Container).m_Items = items; } else { @@ -2981,8 +2768,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert AcquireCompactInfo().m_SavedFlags = reader.ReadEncodedInt(); } - PlayerConstructed = GetSaveFlag(flags, SaveFlag.PlayerConstructed); - if (m_Map != null && m_Parent == null) { m_Map.OnEnter(this); @@ -3045,7 +2830,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert if (GetSaveFlag(flags, SaveFlag.Parent)) { - var parent = reader.ReadSerial(); + Serial parent = reader.ReadSerial(); if (parent.IsMobile) { @@ -3072,7 +2857,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert if (this is Container cont) { - cont._items = items; + cont.m_Items = items; } else { @@ -3163,7 +2948,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert AcquireCompactInfo().m_Name = name; } - var parent = reader.ReadSerial(); + Serial parent = reader.ReadSerial(); if (parent.IsMobile) { @@ -3201,7 +2986,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert if (this is Container cont) { - cont._items = items; + cont.m_Items = items; } else { @@ -3411,12 +3196,8 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert item.Map = m_Map; var items = AcquireItems(); - items.Add(item); - if (this is not Container) - { - AcquireCompactInfo().Version++; - } + items.Add(item); if (!item.IsVirtualItem) { @@ -3426,7 +3207,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } item.Delta(ItemDelta.Update); - item.UpdateDecayRegistration(); item.OnAdded(this); OnItemAdded(item); @@ -3454,12 +3234,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert m_DeltaFlags &= ~flags; } - /// - /// True when deltas remain queued after a pass, which is - /// bounded by the count it saw on entry. The event loop consults this before sleeping. - /// - public static bool HasQueuedDeltas => m_DeltaQueue.Count > 0; - public static void ProcessDeltaQueue() { var limit = m_DeltaQueue.Count; @@ -3494,13 +3268,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } } -#if TRACK_LEAKS - ~Item() - { - EntityFinalizationTracker.NotifyFinalized(this); - } -#endif - public virtual void OnDelete() { if (Spawner != null) @@ -3515,6 +3282,68 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert Delete(); } + public void PublicOverheadMessage(MessageType type, int hue, bool ascii, string text) + { + if (m_Map == null) + { + return; + } + + var worldLoc = GetWorldLocation(); + + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); + + foreach (var state in m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange())) + { + var m = state.Mobile; + + if (m.CanSee(this) && m.InRange(worldLoc, GetUpdateRange(m))) + { + var length = OutgoingMessagePackets.CreateMessage( + buffer, Serial, m_ItemID, type, hue, 3, ascii, "ENU", Name, text + ); + + if (length != buffer.Length) + { + buffer = buffer[..length]; // Adjust to the actual size + } + + state.Send(buffer); + } + } + } + + public void PublicOverheadMessage(MessageType type, int hue, int number, string args = "") + { + if (m_Map == null) + { + return; + } + + var worldLoc = GetWorldLocation(); + + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedLength(args)].InitializePacket(); + + foreach (var state in m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange())) + { + var m = state.Mobile; + + if (m.CanSee(this) && m.InRange(worldLoc, GetUpdateRange(m))) + { + var length = OutgoingMessagePackets.CreateMessageLocalized( + buffer, Serial, m_ItemID, type, hue, 3, number, Name, args + ); + + if (length != buffer.Length) + { + buffer = buffer[..length]; // Adjust to the actual size + } + + state.Send(buffer); + } + } + } + public virtual void OnAfterDelete() { } @@ -3525,11 +3354,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert if (items.Remove(item)) { - if (this is not Container) - { - AcquireCompactInfo().Version++; - } - item.SendRemovePacket(); if (!item.IsVirtualItem) @@ -3540,7 +3364,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } item.Parent = null; - item.UpdateDecayRegistration(); item.OnRemoved(this); OnItemRemoved(item); @@ -3566,7 +3389,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert for (var i = 0; i < props.Length; i++) { var p = props[i]; - if (p.GetCustomAttribute(true) != null || !p.CanRead || !p.CanWrite) + if (p.GetCustomAttribute(typeof(IgnoreDupeAttribute), true) != null || !p.CanRead || !p.CanWrite) { continue; } @@ -3987,7 +3810,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert return; } - var removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); + Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); foreach (var state in m_Map.GetClientsInRange(worldLoc, GetMaxUpdateRange())) { @@ -4039,6 +3862,37 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert ); } + public void SendLocalizedMessageTo(Mobile to, int number, string args = "") + { + if (Deleted || !to.CanSee(this)) + { + return; + } + + to.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", args); + } + + public void SendLocalizedMessageTo(Mobile to, int number, AffixType affixType, string affix, string args) + { + if (Deleted || !to.CanSee(this)) + { + return; + } + + to.NetState.SendMessageLocalizedAffix( + Serial, + ItemID, + MessageType.Regular, + 0x3B2, + 3, + number, + "", + affixType, + affix, + args + ); + } + public virtual void OnSnoop(Mobile from) { } @@ -4309,20 +4163,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert ); } } - else if (m_Amount > 1) - { - ns.SendMessage( - Serial, - m_ItemID, - MessageType.Label, - 0x3B2, - 3, - false, - "ENU", - "", - $"{Name} : {m_Amount}" - ); - } else { ns.SendMessage( @@ -4334,7 +4174,7 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert false, "ENU", "", - $"{Name}" + $"{Name}{(m_Amount > 1 ? $" : {m_Amount}" : "")}" ); } } @@ -4397,12 +4237,12 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert } public virtual bool CheckBlessed(Mobile m) => - m_LootType == LootType.Blessed || ServerFeatureFlags.InsuranceEnabled && Insured || m != null && m == BlessedFor; + m_LootType == LootType.Blessed || Mobile.InsuranceEnabled && Insured || m != null && m == BlessedFor; public virtual bool CheckNewbied() => m_LootType == LootType.Newbied; public virtual bool IsStandardLoot() => - (!ServerFeatureFlags.InsuranceEnabled || !Insured) && BlessedFor == null && m_LootType == LootType.Regular; + (!Mobile.InsuranceEnabled || !Insured) && BlessedFor == null && m_LootType == LootType.Regular; public override string ToString() => $"{Serial} \"{GetType().Name}\""; @@ -4472,13 +4312,9 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert public ISpawner m_Spawner; - public DateTime m_DecayReset; - public int m_TempFlags; public double m_Weight = -1; - - public int Version; } [Flags] @@ -4511,8 +4347,6 @@ public partial class Item : IHued, IComparable, ISpawnable, IObjectPropert HeldBy = 0x00800000, IntWeight = 0x01000000, SavedFlags = 0x02000000, - NullWeight = 0x04000000, - PlayerConstructed = 0x08000000, - DecayReset = 0x10000000 + NullWeight = 0x04000000 } } diff --git a/Projects/Server/Items/ItemBounds.cs b/Projects/Server/Items/ItemBounds.cs index a36e271e8..4b957b80e 100644 --- a/Projects/Server/Items/ItemBounds.cs +++ b/Projects/Server/Items/ItemBounds.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ItemBounds.cs * * * @@ -17,15 +17,13 @@ using System; using System.IO; using System.Threading; using Server.Logging; -using Size = System.ValueTuple; namespace Server; public static class ItemBounds { private static readonly ILogger logger = LogFactory.GetLogger(typeof(ItemBounds)); - private const string _boundsFileName = "ItemBounds.bin"; - private static readonly string _boundsFolder = Path.Combine(Core.BaseDirectory, "Data", "Items"); + private static readonly string _pathToBounds = Path.Combine(Core.BaseDirectory, "Data", "Binary", "Bounds.bin"); private static bool _isGenerating; public static void Configure() @@ -34,7 +32,7 @@ public static class ItemBounds } [Usage("GenBounds")] - [Description("Asynchronously generates the ItemBounds.bin file from art.mul/artidx.mul or artLegacyMUL.uop to determine graphic sizes and boundaries.")] + [Description("Asynchronously generates the bounds.bin file from art.mul/artidx.mul or artLegacyMUL.uop to determine container boundaries.")] private static void GenBounds_OnCommand(CommandEventArgs e) { GenerateBoundsFileAsync(e.Mobile); @@ -42,20 +40,19 @@ public static class ItemBounds static ItemBounds() { - if (!File.Exists(Path.Combine(_boundsFolder, _boundsFileName))) + Table = new Rectangle2D[TileData.ItemTable.Length]; + + if (!File.Exists(_pathToBounds)) { - logger.Information("Generating {BoundsFilePath}...", _boundsFileName); + logger.Information("Generating {BoundsFilePath}...", "Bounds.bin"); try { - GenerateBoundsFile(out var sizes, out var bounds); - Sizes = sizes; - Bounds = bounds; - - logger.Information("Generated {BoundsFilePath} successfully.", _boundsFileName); + GenerateBoundsFile(); + logger.Information("Generated {BoundsFilePath} successfully.", "Bounds.bin"); } catch (Exception ex) { - logger.Error(ex, "Failed to generate {BoundsFilePath}", _boundsFileName); + logger.Error(ex, "Failed to generate {BoundsFilePath}", "Bounds.bin"); } return; @@ -64,9 +61,7 @@ public static class ItemBounds GenerateTable(); } - public static Size[] Sizes { get; private set; } - - public static Rectangle2D[] Bounds { get; private set; } + public static Rectangle2D[] Table { get; private set; } private static void GenerateBoundsFileAsync(Mobile m) { @@ -81,21 +76,16 @@ public static class ItemBounds state => { var from = state as Mobile; - logger.Information("Generating {BoundsFilePath}...", _boundsFileName); + logger.Information("Generating {BoundsFilePath}...", "Bounds.bin"); if (from != null) { - Core.LoopContext.Post(() => from.SendMessage("Generating bounds file...")); + Core.LoopContext.Post(() => from?.SendMessage("Generating bounds file...")); } try { - GenerateBoundsFile(out var sizes, out var bounds); - Core.LoopContext.Post(() => - { - Sizes = sizes; - Bounds = bounds; - } - ); + var table = GenerateBoundsFile(); + Core.LoopContext.Post(() => Table = table); } catch (Exception ex) { @@ -103,21 +93,21 @@ public static class ItemBounds { Core.LoopContext.Post(() => { - from.SendMessage("Failed to generate bounds file:"); - from.SendMessage(ex.Message); + from?.SendMessage("Failed to generate bounds file:"); + from?.SendMessage(ex.Message); } ); } - logger.Error(ex, "Failed to generate {BoundsFilePath}", _boundsFileName); + logger.Error(ex, "Failed to generate {BoundsFilePath}", "Bounds.bin"); return; } if (from != null) { Core.LoopContext.Post( - () => from.SendMessage( - $"Bounds file saved to {Path.GetRelativePath(Core.BaseDirectory, Path.Combine(_boundsFolder, _boundsFileName))}." + () => from?.SendMessage( + $"Bounds file saved to {Path.GetRelativePath(Core.BaseDirectory, _pathToBounds)}." ) ); } @@ -129,57 +119,48 @@ public static class ItemBounds ); } - private static void GenerateBoundsFile(out Size[] sizes, out Rectangle2D[] bounds) + private static Rectangle2D[] GenerateBoundsFile() { - bounds = new Rectangle2D[TileData.ItemTable.Length]; - sizes = new Size[TileData.ItemTable.Length]; - + var table = new Rectangle2D[TileData.ItemTable.Length]; using var artData = new ArtData(); if (!artData.IsInitialized) { throw new FileNotFoundException("Unable to load art.mul/artidx.mul or artLegacyMUL.uop"); } - PathUtility.EnsureDirectory(_boundsFolder); - using var fs = new FileStream(Path.Combine(_boundsFolder, _boundsFileName), FileMode.Create, FileAccess.Write); + using var fs = new FileStream(_pathToBounds, FileMode.Create, FileAccess.Write); using var bw = new BinaryWriter(fs); - for (var i = 0; i < bounds.Length; i++) + for (var i = 0; i < table.Length; i++) { - var (w, h, b) = artData.GetStaticBounds(i); + var bounds = artData.GetStaticBounds(i); - bw.Write(w); - bw.Write(h); - bw.Write((short)b.X); - bw.Write((short)b.Y); - bw.Write((short)(b.X + b.Width + 1)); - bw.Write((short)(b.Y + b.Height + 1)); + bw.Write((short)bounds.X); + bw.Write((short)bounds.Y); + bw.Write((short)(bounds.X + bounds.Width + 1)); + bw.Write((short)(bounds.Y + bounds.Height + 1)); - bounds[i].Set(b.X, b.Y, b.Width, b.Height); - sizes[i] = (w, h); + table[i] = bounds; } + + return table; } private static void GenerateTable() { - Bounds = new Rectangle2D[TileData.ItemTable.Length]; - Sizes = new Size[TileData.ItemTable.Length]; - - using var fs = new FileStream(Path.Combine(_boundsFolder, _boundsFileName), FileMode.Open, FileAccess.Read, FileShare.Read); + using var fs = new FileStream(_pathToBounds, FileMode.Open, FileAccess.Read, FileShare.Read); using var bin = new BinaryReader(fs); - var count = Math.Min(Bounds.Length, (int)(fs.Length / 8)); + var count = Math.Min(Table.Length, (int)(fs.Length / 8)); for (var i = 0; i < count; ++i) { - Sizes[i] = (bin.ReadUInt16(), bin.ReadUInt16()); - int xMin = bin.ReadInt16(); int yMin = bin.ReadInt16(); int xMax = bin.ReadInt16(); int yMax = bin.ReadInt16(); - Bounds[i].Set(xMin, yMin, xMax - xMin, yMax - yMin); + Table[i].Set(xMin, yMin, xMax - xMin, yMax - yMin); } } } diff --git a/Projects/Server/Items/Layer.cs b/Projects/Server/Items/Layer.cs index 8d2f2103d..1612aaec7 100644 --- a/Projects/Server/Items/Layer.cs +++ b/Projects/Server/Items/Layer.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Layer.cs * * * diff --git a/Projects/Server/Items/LightType.cs b/Projects/Server/Items/LightType.cs index 55058445e..25da0ce4f 100644 --- a/Projects/Server/Items/LightType.cs +++ b/Projects/Server/Items/LightType.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: LightType.cs * * * diff --git a/Projects/Server/Items/SecureTradeContainer.cs b/Projects/Server/Items/SecureTradeContainer.cs index 64ff87841..2c0952aed 100644 --- a/Projects/Server/Items/SecureTradeContainer.cs +++ b/Projects/Server/Items/SecureTradeContainer.cs @@ -1,11 +1,9 @@ -using ModernUO.Serialization; using Server.Accounting; using Server.Network; namespace Server.Items; -[SerializationGenerator(0, false)] -public partial class SecureTradeContainer : Container +public class SecureTradeContainer : Container { public SecureTradeContainer(SecureTrade trade) : base(0x1E5E) { @@ -14,6 +12,10 @@ public partial class SecureTradeContainer : Container Movable = false; } + public SecureTradeContainer(Serial serial) : base(serial) + { + } + public SecureTrade Trade { get; } public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) @@ -93,4 +95,18 @@ public partial class SecureTradeContainer : Container child is VirtualCheck ? AccountGold.Enabled && m.NetState is not { NewSecureTrading: true } : base.IsChildVisibleTo(m, child); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } diff --git a/Projects/Server/Items/VirtualCheck.cs b/Projects/Server/Items/VirtualCheck.cs index 8037bf678..424a04eae 100644 --- a/Projects/Server/Items/VirtualCheck.cs +++ b/Projects/Server/Items/VirtualCheck.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: VirtualCheck.cs * * * @@ -47,7 +47,6 @@ public sealed partial class VirtualCheck : Item Movable = false; } - public override bool SkipSerialization => true; public override bool IsVirtualItem => true; public override bool DisplayWeight => false; public override bool DisplayLootType => false; @@ -159,4 +158,10 @@ public sealed partial class VirtualCheck : Item Editor = null; } } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } diff --git a/Projects/Server/Items/OutgoingVirtualHairPackets.cs b/Projects/Server/Items/VirtualHair.cs similarity index 65% rename from Projects/Server/Items/OutgoingVirtualHairPackets.cs rename to Projects/Server/Items/VirtualHair.cs index ba764764d..ce6735073 100644 --- a/Projects/Server/Items/OutgoingVirtualHairPackets.cs +++ b/Projects/Server/Items/VirtualHair.cs @@ -1,5 +1,6 @@ using System; using System.Buffers; +using ModernUO.Serialization; using Server.Network; namespace Server; @@ -16,7 +17,7 @@ public static class OutgoingVirtualHairPackets return; } - var buffer = stackalloc byte[EquipUpdatePacketLength].InitializePacket(); + Span buffer = stackalloc byte[EquipUpdatePacketLength].InitializePacket(); CreateHairEquipUpdatePacket(buffer, m, hairSerial, itemId, hue, layer); ns.Send(buffer); } @@ -46,7 +47,7 @@ public static class OutgoingVirtualHairPackets return; } - var buffer = stackalloc byte[RemovePacketLength].InitializePacket(); + Span buffer = stackalloc byte[RemovePacketLength].InitializePacket(); CreateRemoveHairPacket(buffer, hairSerial); ns.Send(buffer); } @@ -63,3 +64,34 @@ public static class OutgoingVirtualHairPackets writer.Write(hairSerial); } } + +[SerializationGenerator(0, false)] +public partial class VirtualHairInfo +{ + [SerializableField(0)] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _itemId; + + [SerializableField(1)] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private int _hue; + + public VirtualHairInfo() : this(0) + { + } + + public VirtualHairInfo(int itemid, int hue = 0) + { + _itemId = itemid; + _hue = hue; + VirtualSerial = World.NewVirtual; + } + + [AfterDeserialization] + private void AfterDeserialization() + { + VirtualSerial = World.NewVirtual; + } + + public Serial VirtualSerial { get; private set; } +} diff --git a/Projects/Server/Json/Converters/BitArrayEnumIndexConverter.cs b/Projects/Server/Json/Converters/BitArrayEnumIndexConverter.cs index 05af1b8bf..5bb2ec850 100644 --- a/Projects/Server/Json/Converters/BitArrayEnumIndexConverter.cs +++ b/Projects/Server/Json/Converters/BitArrayEnumIndexConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BitArrayEnumIndexConverter.cs * * * diff --git a/Projects/Server/Json/Converters/ClientVersionConverter.cs b/Projects/Server/Json/Converters/ClientVersionConverter.cs index d843c0e8e..6e9c62239 100644 --- a/Projects/Server/Json/Converters/ClientVersionConverter.cs +++ b/Projects/Server/Json/Converters/ClientVersionConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ClientVersionConverter.cs * * * diff --git a/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs b/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs index b170985e3..dabd02fd6 100644 --- a/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs +++ b/Projects/Server/Json/Converters/ClientVersionConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ClientVersionConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/FlagsConverter.cs b/Projects/Server/Json/Converters/FlagsConverter.cs index 581f15d15..ae811345c 100644 --- a/Projects/Server/Json/Converters/FlagsConverter.cs +++ b/Projects/Server/Json/Converters/FlagsConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: FlagsConverter.cs * * * diff --git a/Projects/Server/Json/Converters/GuidConverter.cs b/Projects/Server/Json/Converters/GuidConverter.cs index 7a060a5e5..c7f5cc79b 100644 --- a/Projects/Server/Json/Converters/GuidConverter.cs +++ b/Projects/Server/Json/Converters/GuidConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GuidConverter.cs * * * diff --git a/Projects/Server/Json/Converters/GuidConverterFactory.cs b/Projects/Server/Json/Converters/GuidConverterFactory.cs index 04a7ce848..44125e06f 100644 --- a/Projects/Server/Json/Converters/GuidConverterFactory.cs +++ b/Projects/Server/Json/Converters/GuidConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GuidConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/IPEndPointConverter.cs b/Projects/Server/Json/Converters/IPEndPointConverter.cs index 70c0ebafc..98ced2598 100644 --- a/Projects/Server/Json/Converters/IPEndPointConverter.cs +++ b/Projects/Server/Json/Converters/IPEndPointConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IPEndPointConverter.cs * * * diff --git a/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs b/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs index 9a95c68f8..f3d9e766b 100644 --- a/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs +++ b/Projects/Server/Json/Converters/IPEndPointConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IPEndPointConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/MapConverter.cs b/Projects/Server/Json/Converters/MapConverter.cs index d2120ee6b..6ba4edc3a 100644 --- a/Projects/Server/Json/Converters/MapConverter.cs +++ b/Projects/Server/Json/Converters/MapConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MapConverter.cs * * * diff --git a/Projects/Server/Json/Converters/MapConverterFactory.cs b/Projects/Server/Json/Converters/MapConverterFactory.cs index 8f93d41c8..5efc2556e 100644 --- a/Projects/Server/Json/Converters/MapConverterFactory.cs +++ b/Projects/Server/Json/Converters/MapConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MapConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/Point2DConverter.cs b/Projects/Server/Json/Converters/Point2DConverter.cs index 75ad86ec2..4b5be99e9 100644 --- a/Projects/Server/Json/Converters/Point2DConverter.cs +++ b/Projects/Server/Json/Converters/Point2DConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Point2DConverter.cs * * * diff --git a/Projects/Server/Json/Converters/Point2DConverterFactory.cs b/Projects/Server/Json/Converters/Point2DConverterFactory.cs index 664f3c94a..6bef01fab 100644 --- a/Projects/Server/Json/Converters/Point2DConverterFactory.cs +++ b/Projects/Server/Json/Converters/Point2DConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Point2DConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/Point3DConverter.cs b/Projects/Server/Json/Converters/Point3DConverter.cs index 6193eb811..82d029282 100644 --- a/Projects/Server/Json/Converters/Point3DConverter.cs +++ b/Projects/Server/Json/Converters/Point3DConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Point3DConverter.cs * * * diff --git a/Projects/Server/Json/Converters/Point3DConverterFactory.cs b/Projects/Server/Json/Converters/Point3DConverterFactory.cs index 925589dec..2c3261c90 100644 --- a/Projects/Server/Json/Converters/Point3DConverterFactory.cs +++ b/Projects/Server/Json/Converters/Point3DConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Point3DConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/Rectangle3DConverter.cs b/Projects/Server/Json/Converters/Rectangle3DConverter.cs index 890c5d6f7..75948ad4c 100644 --- a/Projects/Server/Json/Converters/Rectangle3DConverter.cs +++ b/Projects/Server/Json/Converters/Rectangle3DConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Rectangle3DConverter.cs * * * @@ -175,7 +175,7 @@ public class Rectangle3DConverter : JsonConverter public override void Write(Utf8JsonWriter writer, Rectangle3D value, JsonSerializerOptions options) { - var writeZ = value.Start.Z != sbyte.MinValue || value.End.Z != sbyte.MaxValue; + var writeZ = value.Start.Z is > sbyte.MinValue and < sbyte.MaxValue || value.End.Z is > sbyte.MinValue and < sbyte.MaxValue; writer.WriteStartObject(); writer.WritePropertyName("x1"); diff --git a/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs b/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs index 97e734d0d..9cfaaeeee 100644 --- a/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs +++ b/Projects/Server/Json/Converters/Rectangle3DConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Rectangle3DConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/RegionByNameConverter.cs b/Projects/Server/Json/Converters/RegionByNameConverter.cs index bd0bd4871..df0209c47 100644 --- a/Projects/Server/Json/Converters/RegionByNameConverter.cs +++ b/Projects/Server/Json/Converters/RegionByNameConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: RegionByNameConverter.cs * * * @@ -44,7 +44,7 @@ public class RegionByNameConverter : JsonConverter throw new JsonException("Invalid json for RegionByName"); } - var property = reader.GetString()?.ToLower(); + string property = reader.GetString()?.ToLower(); if (property != "name" && property != "map") { diff --git a/Projects/Server/Json/Converters/RegionByNameConverterFactory.cs b/Projects/Server/Json/Converters/RegionByNameConverterFactory.cs index 8206715b8..15faa0e14 100644 --- a/Projects/Server/Json/Converters/RegionByNameConverterFactory.cs +++ b/Projects/Server/Json/Converters/RegionByNameConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: RegionByNameConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/TextDefinitionConverter.cs b/Projects/Server/Json/Converters/TextDefinitionConverter.cs index b4881fd76..f83846796 100644 --- a/Projects/Server/Json/Converters/TextDefinitionConverter.cs +++ b/Projects/Server/Json/Converters/TextDefinitionConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TextDefinitionConverter.cs * * * diff --git a/Projects/Server/Json/Converters/TextDefinitionConverterFactory.cs b/Projects/Server/Json/Converters/TextDefinitionConverterFactory.cs index 174fd539a..323072697 100644 --- a/Projects/Server/Json/Converters/TextDefinitionConverterFactory.cs +++ b/Projects/Server/Json/Converters/TextDefinitionConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TextDefinitionConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/TimeSpanConverter.cs b/Projects/Server/Json/Converters/TimeSpanConverter.cs index 4779e0643..f54934cc6 100644 --- a/Projects/Server/Json/Converters/TimeSpanConverter.cs +++ b/Projects/Server/Json/Converters/TimeSpanConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TimeSpanConverter.cs * * * diff --git a/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs b/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs index 2abb34678..8af1494b2 100644 --- a/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs +++ b/Projects/Server/Json/Converters/TimeSpanConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TimeSpanConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/TypeConverter.cs b/Projects/Server/Json/Converters/TypeConverter.cs index 2fcda79b1..3b117719e 100644 --- a/Projects/Server/Json/Converters/TypeConverter.cs +++ b/Projects/Server/Json/Converters/TypeConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TypeConverter.cs * * * diff --git a/Projects/Server/Json/Converters/TypeConverterFactory.cs b/Projects/Server/Json/Converters/TypeConverterFactory.cs index d7095291a..3726cdbd0 100644 --- a/Projects/Server/Json/Converters/TypeConverterFactory.cs +++ b/Projects/Server/Json/Converters/TypeConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TypeConverterFactory.cs * * * diff --git a/Projects/Server/Json/Converters/WorldLocationConverter.cs b/Projects/Server/Json/Converters/WorldLocationConverter.cs index fd16ea782..d83cd4180 100644 --- a/Projects/Server/Json/Converters/WorldLocationConverter.cs +++ b/Projects/Server/Json/Converters/WorldLocationConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: WorldLocationConverter.cs * * * diff --git a/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs b/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs index ba5220e31..cad85d1da 100644 --- a/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs +++ b/Projects/Server/Json/Converters/WorldLocationConverterFactory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: WorldLocationConverterFactory.cs * * * diff --git a/Projects/Server/Json/DynamicJson.cs b/Projects/Server/Json/DynamicJson.cs new file mode 100644 index 000000000..09cc85236 --- /dev/null +++ b/Projects/Server/Json/DynamicJson.cs @@ -0,0 +1,66 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: DynamicJson.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Server.Json; + +public class DynamicJson +{ + public static DynamicJson Create(Type type) => new() + { + Type = type.Name, + Data = new Dictionary() + }; + + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonExtensionData] + public Dictionary Data { get; set; } + + // TODO: Use JSON Node in .NET 6 + public void SetProperty(string key, JsonSerializerOptions options, T value) + { + using var doc = JsonDocument.Parse(JsonSerializer.SerializeToUtf8Bytes(value, options)); + Data[key] = doc.RootElement.Clone(); + } + + public bool GetProperty(string key, JsonSerializerOptions options, out T t) + { + if (Data.TryGetValue(key, out var el)) + { + t = el.ToObject(options); + return true; + } + + t = default; + return false; + } + + public bool GetEnumProperty(string key, JsonSerializerOptions options, out T t) where T : struct, Enum + { + if (Data.TryGetValue(key, out var el)) + { + return Enum.TryParse(el.ToObject(options), out t); + } + + t = default; + return false; + } +} diff --git a/Projects/Server/Json/JsonConfig.cs b/Projects/Server/Json/JsonConfig.cs index 75b6a2898..f3b5fdda9 100644 --- a/Projects/Server/Json/JsonConfig.cs +++ b/Projects/Server/Json/JsonConfig.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: JsonConfig.cs * * * diff --git a/Projects/Server/Json/JsonDiscoverableTypeAttribute.cs b/Projects/Server/Json/JsonDiscoverableTypeAttribute.cs deleted file mode 100644 index 0246b44ae..000000000 --- a/Projects/Server/Json/JsonDiscoverableTypeAttribute.cs +++ /dev/null @@ -1,32 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: JsonDiscoverableTypeAttribute.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Json; - -/// -/// Marks a concrete class as a discoverable polymorphic JSON derived type. A consumer -/// (e.g. SpawnerJsonSerializer) scans assemblies for marked subclasses of a chosen -/// base and registers them for System.Text.Json polymorphism. Optionally overrides the -/// $type discriminator value (defaults to the type's ). -/// -[AttributeUsage(AttributeTargets.Class, Inherited = false)] -public sealed class JsonDiscoverableTypeAttribute : Attribute -{ - public JsonDiscoverableTypeAttribute(string discriminator = null) => Discriminator = discriminator; - - public string Discriminator { get; } -} diff --git a/Projects/Server/Json/JsonUtilities.cs b/Projects/Server/Json/JsonUtilities.cs index 730223c89..df6c5c0ac 100644 --- a/Projects/Server/Json/JsonUtilities.cs +++ b/Projects/Server/Json/JsonUtilities.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: JsonPropertySorter.cs * * * @@ -26,7 +26,7 @@ public static class JsonUtilities { public static string SortByPropertyName(string jsonStr) { - using var doc = JsonDocument.Parse(jsonStr); + using JsonDocument doc = JsonDocument.Parse(jsonStr); return SortByPropertyName(doc.RootElement); } @@ -34,7 +34,7 @@ public static class JsonUtilities { // TODO: Better way to do this than a stream? using var ms = new MemoryStream(); - var opts = new JsonWriterOptions + JsonWriterOptions opts = new JsonWriterOptions { Indented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping @@ -58,7 +58,7 @@ public static class JsonUtilities writer.WriteStartObject(); // TODO: This is slow, can make it faster? - foreach (var x in je.EnumerateObject().OrderBy(prop => prop.Name)) + foreach (JsonProperty x in je.EnumerateObject().OrderBy(prop => prop.Name)) { writer.WritePropertyName(x.Name); WriteJsonElementSorted(x.Value, writer); @@ -70,7 +70,7 @@ public static class JsonUtilities case JsonValueKind.Array: { writer.WriteStartArray(); - foreach(var x in je.EnumerateArray()) + foreach(JsonElement x in je.EnumerateArray()) { WriteJsonElementSorted(x, writer); } diff --git a/Projects/Server/Json/SerializedJsonIgnore.cs b/Projects/Server/Json/SerializedJsonIgnore.cs index de6adfff3..9165b6f77 100644 --- a/Projects/Server/Json/SerializedJsonIgnore.cs +++ b/Projects/Server/Json/SerializedJsonIgnore.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SerializedJsonIgnore.cs * * * diff --git a/Projects/Server/Json/SerializedJsonPropertyName.cs b/Projects/Server/Json/SerializedJsonPropertyName.cs index 9e803924d..ec322d091 100644 --- a/Projects/Server/Json/SerializedJsonPropertyName.cs +++ b/Projects/Server/Json/SerializedJsonPropertyName.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SerializedJsonPropertyName.cs * * * diff --git a/Projects/Server/Localization/Localization.cs b/Projects/Server/Localization/Localization.cs index 21dbe801f..0e08c1e0c 100644 --- a/Projects/Server/Localization/Localization.cs +++ b/Projects/Server/Localization/Localization.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Localization.cs * * * @@ -88,7 +88,7 @@ public static class Localization private static Dictionary LoadClilocs(string lang, string file) { lang = lang?.ToLower() ?? FallbackLanguage; - var entries = _localizations[lang] = new Dictionary(); + Dictionary entries = _localizations[lang] = new Dictionary(); if (lang == FallbackLanguage) { _fallbackEntries = entries; diff --git a/Projects/Server/Localization/LocalizationEntry.cs b/Projects/Server/Localization/LocalizationEntry.cs index dac79696b..b17810392 100644 --- a/Projects/Server/Localization/LocalizationEntry.cs +++ b/Projects/Server/Localization/LocalizationEntry.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: LocalizationEntry.cs * * * @@ -51,10 +51,10 @@ public class LocalizationEntry private static void ParseText(string text, out string[] textSlices, out string stringFormatter) { - using var sb = ValueStringBuilder.Create(256); + var sb = ValueStringBuilder.Create(256); using var queue = PooledRefQueue.Create(); - var hasMatch = false; + bool hasMatch = false; var prevIndex = 0; foreach (Match match in _textRegex.Matches(text)) { @@ -81,6 +81,8 @@ public class LocalizationEntry textSlices = queue.ToArray(); stringFormatter = hasMatch ? sb.ToString() : null; + + sb.Dispose(); } /// diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index 608256651..535327cbe 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -1,6 +1,6 @@ -/************************************************************************* +/************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Main.cs * * * @@ -30,7 +30,6 @@ using Server.Compression; using Server.Json; using Server.Logging; using Server.Network; -using Server.Network.Bans; using Server.Text; namespace Server; @@ -39,181 +38,10 @@ public static class Core { private static readonly ILogger logger = LogFactory.GetLogger(typeof(Core)); - // Written off-loop (Kill, RequestSnapshot); volatile because the loop blocks between reads. - private static volatile bool _performProcessKill; + private static bool _performProcessKill; private static bool _restartOnKill; - private static volatile bool _performSnapshot; + private static bool _performSnapshot; private static string _snapshotPath; - - // A backstop, not a latency control: the wheel's tick rate already bounds the sleep. - // Measured across 1/2/4/8ms; 2 is optimal. - private static int _eventLoopIdleWaitMs = 2; - - /// - /// Longest the loop will block while idle, in milliseconds. 0 spins instead; the backoff - /// does the same temporarily when the host keeps returning waits late. - /// - public static int EventLoopIdleWaitMs => _eventLoopIdleWaitMs; - - /// - /// True when idle sleeping was disabled at startup because the host cannot honor short - /// waits, overriding whatever server.eventLoopIdleWaitMs was configured to. - /// - public static bool IdleSleepUnsupported { get; private set; } - - /// - /// Whether idle sleeping is currently suspended because the host returned waits late. - /// - /// - /// Compared by subtraction, never directly: tick counts can start enormous and wrap. - /// See dev-docs/tick-counts.md. - /// - public static bool IdleSleepSuspended => _tickCount - _idleSleepSuspendedUntil < 0; - - private const long HealthSampleIntervalMs = 1000; - - // Doubling: a fixed suspension oscillates forever on a persistently bad host, while doubling - // converges on "stop sleeping" yet still recovers from a transient. - private const long BackoffBaseMs = 5000; - private const long BackoffMaxMs = 120_000; - private const int BackoffMaxShift = 5; - - // Clean streak that clears the escalation. - private const long BackoffResetAfterCleanMs = 60_000; - - // Below this a backoff is still recoverable and not actionable, so it only logs at Debug. - private const int WarnAfterConsecutiveBackoffs = 3; - - // A sleep is bounded by the next wheel turn, so only a wait returning late can cost a deadline. - // Measured per sleep, which is why server work (saves, heavy commands) cannot trip the backoff. - private static int _lateWakes; - - // Denominator for the late-wake rate. - private static int _sleepAttempts; - - private static long _nextHealthSample; - private static long _idleSleepSuspendedUntil; - private static int _lateWakeThreshold = 1; - private static int _lateWakePercent = 10; - private static long _idleSleepBackoffs; - private static int _consecutiveBadSamples; - private static int _consecutiveBackoffs; - private static long _currentBackoffMs = BackoffBaseMs; - private static long _lastBackoffAt; - private static bool _loggedBackoffCeiling; - - /// - /// Once a second, suspends idle sleeping (with escalating duration) if the host keeps - /// returning idle waits a full tick or more late. - /// - private static void CheckSchedulerHealth() - { - if (_tickCount - _nextHealthSample < 0) - { - return; - } - - _nextHealthSample = _tickCount + HealthSampleIntervalMs; - - var late = _lateWakes; - var sleeps = _sleepAttempts; - _lateWakes = 0; - _sleepAttempts = 0; - - // A clean streak resets the escalation and re-arms the ceiling Error. Gated on the count - // rather than a "_lastBackoffAt > 0" sentinel because tick counts are not guaranteed positive. - if (_consecutiveBackoffs > 0 && _tickCount - _lastBackoffAt > BackoffResetAfterCleanMs) - { - if (_consecutiveBackoffs >= WarnAfterConsecutiveBackoffs) - { - logger.Information( - "This host has returned idle waits on time for {Duration}ms; idle sleeping is back to normal", - BackoffResetAfterCleanMs - ); - } - - _consecutiveBackoffs = 0; - _loggedBackoffCeiling = false; - } - - if (late <= _lateWakeThreshold) - { - _consecutiveBadSamples = 0; - return; - } - - // Lateness is a rate: an idle loop sleeps hundreds of times a second, so a few outliers are - // normal, while a host that cannot schedule the process returns most of its waits late. The - // threshold above is the floor for windows with too few sleeps for a proportion to mean anything. - if (late * 100 < sleeps * _lateWakePercent) - { - _consecutiveBadSamples = 0; - return; - } - - // Require persistence: any host can drop one sample to unrelated load, but an oversubscribed - // one stays bad. - if (++_consecutiveBadSamples < 2) - { - return; - } - - if (_eventLoopIdleWaitMs <= 0) - { - return; - } - - _currentBackoffMs = Math.Min(BackoffBaseMs << Math.Min(_consecutiveBackoffs, BackoffMaxShift), BackoffMaxMs); - _consecutiveBackoffs++; - _lastBackoffAt = _tickCount; - _idleSleepSuspendedUntil = _tickCount + _currentBackoffMs; - _idleSleepBackoffs++; - - if (_currentBackoffMs >= BackoffMaxMs) - { - // Escalation has run out of room; say so once. - if (!_loggedBackoffCeiling) - { - _loggedBackoffCeiling = true; - logger.Error( - "This host keeps returning idle waits late and sleeping has backed off {Count} times. " + - "The process is not being scheduled promptly, which is typical of shared or burstable vCPUs. " + - "Set server.eventLoopIdleWaitMs to 0 to disable sleeping permanently and trade a full core for latency.", - _idleSleepBackoffs - ); - } - - return; - } - - // Each backoff doubles the suspension, so every line is a distinct escalation step and - // needs no further rate limiting. - if (_consecutiveBackoffs < WarnAfterConsecutiveBackoffs) - { - logger.Debug( - "This host returned a {Requested}ms idle wait at least {TickRate}ms late {Count} of {Sleeps} time(s) " + - "in the last second; idle sleeping suspended for {Duration}ms", - _eventLoopIdleWaitMs, - Timer.TickRate, - late, - sleeps, - _currentBackoffMs - ); - - return; - } - - logger.Warning( - "This host returned a {Requested}ms idle wait at least {TickRate}ms late {Count} of {Sleeps} time(s) in " + - "the last second, for the {Backoffs}th time running; idle sleeping suspended for {Duration}ms", - _eventLoopIdleWaitMs, - Timer.TickRate, - late, - sleeps, - _consecutiveBackoffs, - _currentBackoffMs - ); - } private static bool _crashed; private static string _baseDirectory; @@ -272,9 +100,9 @@ public static class Core private static long _firstTick; - // Make these available to unit tests for mocking - internal static long _tickCount; - internal static DateTime _now; + private static long _tickCount; + + private static DateTime _now; public static long TickCount => _tickCount; @@ -282,6 +110,13 @@ public static class Core public static long Uptime => TickCount - _firstTick; + private static long _cycleIndex; + private static readonly double[] _cyclesPerSecond = new double[128]; + + public static double CyclesPerSecond => _cyclesPerSecond[_cycleIndex]; + + public static double AverageCPS => _cyclesPerSecond.Average(); + public static string BaseDirectory { get @@ -311,8 +146,6 @@ public static class Core public static bool Closing => ClosingTokenSource.IsCancellationRequested; - public static bool Headless { get; private set; } - public static int GlobalUpdateRange { get; set; } = 18; public static int GlobalMaxUpdateRange { get; set; } = 24; @@ -398,10 +231,6 @@ public static class Core { _restartOnKill = restart; _performProcessKill = true; - - // Callers are usually off-loop (console input, signal handlers); wake so the request - // is noticed now rather than whenever the loop next surfaces. - NetState.Wake(); } public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) @@ -428,7 +257,7 @@ public static class Core // ignored } - if (!close && !Headless) + if (!close) { Console.WriteLine("This exception is fatal, press return to exit"); ConsoleInputHandler.ReadLine(); @@ -509,9 +338,7 @@ public static class Core World.WaitForWriteCompletion(); World.ExitSerializationThreads(); PingServer.Shutdown(); - NetState.Shutdown(); - BanChannel.Stop(); - ConnectionFilters.Stop(); + TcpServer.Shutdown(); if (!_crashed) { @@ -519,20 +346,8 @@ public static class Core } } - private static readonly bool UseFastTimestampMath = Stopwatch.Frequency % 1000 == 0; - private static readonly ulong FrequencyInMilliseconds = (ulong)Stopwatch.Frequency / 1000; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetTimestamp() - { - if (UseFastTimestampMath) - { - return (long)((ulong)Stopwatch.GetTimestamp() / FrequencyInMilliseconds); - } - - // Fast calculation will be lossy, fallback to slower but accurate calculation - return (long)((UInt128)Stopwatch.GetTimestamp() * 1000 / (ulong)Stopwatch.Frequency); - } + public static long GetTimestamp() => 1000L * Stopwatch.GetTimestamp() / Stopwatch.Frequency; public static void Setup(Assembly applicationAssembly, Process process) { @@ -568,7 +383,7 @@ public static class Core Utility.PopColor(); Utility.PushColor(ConsoleColor.DarkGray); - Console.WriteLine(@"Copyright 2019-2026 ModernUO Development Team + Console.WriteLine(@"Copyright 2019-2023 ModernUO Development Team This program comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under certain conditions. @@ -579,56 +394,13 @@ public static class Core Console.CancelKeyPress += Console_CancelKeyPressed; - Headless = Console.IsInputRedirected; - if (Headless) - { - logger.Information("Headless mode detected (stdin is not a TTY); interactive console input is disabled."); - } - // LibDeflate is not thread safe, so we need to create a new instance for each thread var standard = Deflate.Standard; AppDomain.CurrentDomain.ProcessExit += (_, _) => standard.Dispose(); ServerConfiguration.Load(); - // 0 disables idle sleeping entirely (full-core spin, zero scheduling overhead). - var idleWaitMs = ServerConfiguration.GetSetting("server.eventLoopIdleWaitMs", 2); - if (idleWaitMs < 0) - { - logger.Warning( - "server.eventLoopIdleWaitMs {Value} is negative; using 0 (idle sleeping disabled)", - idleWaitMs - ); - } - - _eventLoopIdleWaitMs = Math.Max(0, idleWaitMs); - - // Floor for the backoff: idle waits per second the host may return a full tick late before - // the rate test below applies at all. Set very high to disable the backoff. - var lateWakeThreshold = ServerConfiguration.GetSetting("server.lateWakeThreshold", 1); - if (lateWakeThreshold < 0) - { - logger.Warning( - "server.lateWakeThreshold {Value} is negative; using 0", - lateWakeThreshold - ); - } - - _lateWakeThreshold = Math.Max(0, lateWakeThreshold); - - // Share of a second's idle waits that must return late before the backoff trips. 0 leaves - // the threshold above in sole charge. - var lateWakePercent = ServerConfiguration.GetSetting("server.lateWakePercent", 10); - if (lateWakePercent is < 0 or > 100) - { - logger.Warning( - "server.lateWakePercent {Value} is outside 0-100; using {Clamped}", - lateWakePercent, - Math.Clamp(lateWakePercent, 0, 100) - ); - } - - _lateWakePercent = Math.Clamp(lateWakePercent, 0, 100); + logger.Information("Running on {Framework}", RuntimeInformation.FrameworkDescription); var assemblyPath = Path.Join(BaseDirectory, AssembliesConfiguration); @@ -646,22 +418,11 @@ public static class Core AssemblyHandler.LoadAssemblies(assemblyFiles); - // First-boot interactive setup. After assemblies load so content can register prompts, - // before any Serilog output so prompts are not interleaved with the async console sink. - AssemblyHandler.Invoke("ConfigurePrompts"); - - logger.Information("Running on {Framework}", RuntimeInformation.FrameworkDescription); - VerifySerialization(); _now = DateTime.UtcNow; _firstTick = _tickCount = GetTimestamp(); - // Seed from a real tick: tick counts need not start near zero, so a zero-initialized - // deadline compares wrong. See dev-docs/tick-counts.md. - _nextHealthSample = _tickCount + HealthSampleIntervalMs; - _idleSleepSuspendedUntil = _tickCount; - Timer.Init(_tickCount); AssemblyHandler.Invoke("Configure"); @@ -673,76 +434,50 @@ public static class Core AssemblyHandler.Invoke("Initialize"); - BanChannel.Start(ClosingTokenSource.Token); - ConnectionFilters.Start(ClosingTokenSource.Token); - NetState.Start(); + TcpServer.Start(); PingServer.Start(); EventSink.InvokeServerStarted(); - - // Without a high-resolution wait a 2ms request quantises to 15.625ms and the loop runs a - // tick behind. Only fires when the high-res timer and the timeBeginPeriod fallback both failed. - if (_eventLoopIdleWaitMs > 0 && NetState.Ring?.SupportsHighResolutionWait == false) - { - logger.Error( - "This host cannot honor short waits (no high-resolution timer, and raising the system timer " + - "resolution failed). Idle sleeping is disabled. The loop will spin instead, using a full core." - ); - - IdleSleepUnsupported = true; - _eventLoopIdleWaitMs = 0; - } - RunEventLoop(); } - /// - /// True when every queue the loop drains is empty, so sleeping cannot strand pending work. - /// The drains are bounded, so leftovers are normal and must keep the loop awake. - /// - private static bool IsIdle() => - !Mobile.HasQueuedDeltas && !Item.HasQueuedDeltas && LoopContext.IsEmpty && NetState.IsIdle; - public static void RunEventLoop() { try { +#if DEBUG + const bool idleCPU = true; +#else + var idleCPU = ServerConfiguration.GetOrUpdateSetting("core.enableIdleCPU", false); +#endif + + var cycleCount = _cyclesPerSecond.Length; + long last = _tickCount; + const int interval = 100; + double frequency = Stopwatch.Frequency * interval; + + int sample = 0; + while (!Closing) { _tickCount = GetTimestamp(); _now = DateTime.UtcNow; - EventLoopProfiler.IterationStart(_tickCount); - - EventLoopProfiler.PhaseStart(LoopPhase.MobileDeltas); Mobile.ProcessDeltaQueue(); - EventLoopProfiler.PhaseEnd(LoopPhase.MobileDeltas); - - EventLoopProfiler.PhaseStart(LoopPhase.ItemDeltas); Item.ProcessDeltaQueue(); - EventLoopProfiler.PhaseEnd(LoopPhase.ItemDeltas); - - EventLoopProfiler.PhaseStart(LoopPhase.TimerSlice); Timer.Slice(_tickCount); - EventLoopProfiler.PhaseEnd(LoopPhase.TimerSlice); // Handle networking - EventLoopProfiler.PhaseStart(LoopPhase.NetworkSlice); NetState.Slice(); - EventLoopProfiler.PhaseEnd(LoopPhase.NetworkSlice); // Execute captured post-await methods (like Timer.Pause) - EventLoopProfiler.PhaseStart(LoopPhase.LoopTasks); LoopContext.ExecuteTasks(); - EventLoopProfiler.PhaseEnd(LoopPhase.LoopTasks); Timer.CheckTimerPool(); // Check for pool depletion so we can async refill it. if (_performSnapshot) { - EventLoopProfiler.PhaseStart(LoopPhase.WorldSnapshot); // Return value is the offset that can be used to fix timers that should drift World.Snapshot(_snapshotPath); - EventLoopProfiler.PhaseEnd(LoopPhase.WorldSnapshot); _performSnapshot = false; } @@ -752,35 +487,23 @@ public static class Core break; } - CheckSchedulerHealth(); - - if (_eventLoopIdleWaitMs > 0 && _tickCount - _idleSleepSuspendedUntil >= 0 && IsIdle()) + if (sample++ == interval) { - // Re-read the clock: a stale timestamp overstates the time to the next tick - // and sleeps straight past it. - var start = GetTimestamp(); - var due = Timer.MillisecondsUntilNextTick(start); - if (due > 0) + sample = 0; + var now = GetTimestamp(); + + var cyclesPerSecond = frequency / (now - last); + _cyclesPerSecond[_cycleIndex++] = cyclesPerSecond; + if (_cycleIndex == cycleCount) { - var requested = (int)Math.Min(due, _eventLoopIdleWaitMs); + _cycleIndex = 0; + } - // The GC prefers to collect during idle sleeps, so its pauses land here by - // design and are not the host's fault. Gen1 and above (what - // CollectionCount(1) counts) are the only pauses long enough to reach a tick. - var collections = GC.CollectionCount(1); + last = now; - NetState.WaitForCompletion(requested); - - var elapsed = GetTimestamp() - start; - EventLoopProfiler.SleepEnd(requested, elapsed); - _sleepAttempts++; - - // The second collection read sits behind the overshoot test, so the common - // path reads the counter once, not twice. - if (elapsed - requested >= Timer.TickRate && GC.CollectionCount(1) == collections) - { - _lateWakes++; - } + if (idleCPU && cyclesPerSecond > 125) + { + Thread.Sleep(2); } } } @@ -796,11 +519,8 @@ public static class Core internal static void RequestSnapshot(string snapshotPath) { - _snapshotPath = snapshotPath; _performSnapshot = true; - - // Save requests arrive off-loop; wake so the snapshot starts now. - NetState.Wake(); + _snapshotPath = snapshotPath; } public static void VerifySerialization() diff --git a/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs b/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs deleted file mode 100644 index eb79410ae..000000000 --- a/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs +++ /dev/null @@ -1,299 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Map.ClientByDistanceEnumerator.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using Server.Collections; -using Server.Network; - -namespace Server; - -public partial class Map -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerable GetClientsInRangeByDistance(Point3D p) => - GetClientsInRangeByDistance(p, Core.GlobalMaxUpdateRange); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerable GetClientsInRangeByDistance(Point3D p, int range) => - GetClientsInRangeByDistance(p.m_X, p.m_Y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerable GetClientsInRangeByDistance(Point2D p) => - GetClientsInRangeByDistance(p, Core.GlobalMaxUpdateRange); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerable GetClientsInRangeByDistance(Point2D p, int range) => - GetClientsInRangeByDistance(p.m_X, p.m_Y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerable GetClientsInRangeByDistance(int x, int y, int range) - { - var clampedRange = Math.Max(0, range); - var edge = clampedRange * 2 + 1; - return GetClientsInBoundsByDistance( - new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge), - new Point2D(x, y) - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerable GetClientsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) => - GetClientsInBoundsByDistance(bounds, new Point2D(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2), makeBoundsInclusive); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ClientDistanceEnumerable GetClientsInBoundsByDistance( - Rectangle2D bounds, Point2D center, bool makeBoundsInclusive = false - ) => new(this, bounds, center, makeBoundsInclusive); - - public ref struct ClientDistanceEnumerable - { - private readonly Map _map; - private readonly Rectangle2D _bounds; - private readonly Point2D _center; - private readonly bool _makeBoundsInclusive; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerable(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) - { - _map = map; - _bounds = bounds; - _center = center; - _makeBoundsInclusive = makeBoundsInclusive; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerator GetEnumerator() => new(_map, _bounds, _center, _makeBoundsInclusive); - } - - public ref struct ClientDistanceEnumerator - { - private Map _map; - private Point2D _center; - private Rectangle2D _bounds; - - private int _sectorStartX; - private int _maxRing; - - private int _ring; // -1 = uninitialized, then 0.._maxRing - private int _ringIndex; // Current index within the ring - - private int _currentSectorX; - private int _currentSectorY; - - private ref readonly ValueLinkList _linkList; - private int _currentVersion; - private NetState _current; - - private int _minDistance; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerator(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) - { - _map = map; - _center = center; - _bounds = makeBoundsInclusive - ? new Rectangle2D(bounds.X, bounds.Y, bounds.Width + 1, bounds.Height + 1) - : bounds; - - _current = null; - - if (map != null) - { - var centerSectorX = center.m_X / SectorSize; - var centerSectorY = center.m_Y / SectorSize; - - map.CalculateSectors(_bounds, out _sectorStartX, out var sectorStartY, out var sectorEndX, out var sectorEndY); - - // Calculate max ring based on bounds - var dx = Math.Max(centerSectorX - _sectorStartX, sectorEndX - centerSectorX); - var dy = Math.Max(centerSectorY - sectorStartY, sectorEndY - centerSectorY); - _maxRing = Math.Max(dx, dy); - } - - _ring = -1; - _ringIndex = -1; - _currentSectorX = 0; - _currentSectorY = 0; - - _currentVersion = 0; - _minDistance = 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - var map = _map; - - if (map == null) - { - return false; - } - - if (!Unsafe.IsNullRef(in _linkList) && _linkList.Version != _currentVersion) - { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); - } - - var current = _current; - - while (true) - { - current = current?.Next; - - while (current == null) - { - while (!TryNextSectorInRing(out _currentSectorX, out _currentSectorY)) - { - // Current ring exhausted, try next ring - if (_ring >= _maxRing) - { - return false; // No more rings to search - } - - _ring++; - _ringIndex = -1; - } - - _linkList = ref map.GetRealSector(_currentSectorX, _currentSectorY).Clients; - _currentVersion = _linkList.Version; - current = _linkList._first; - - if (current != null) - { - _minDistance = MinDistToSectorSqrt(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); - } - } - - var m = current.Mobile; - if (m?.Deleted == false && _bounds.Contains(m.Location)) - { - _current = current; - return true; - } - } - } - - public (NetState Value, int MinDistance) Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (_current, _minDistance); - } - - private bool TryNextSectorInRing(out int sx, out int sy) - { - if (_ring == 0) - { - // Center sector - if (_ringIndex < 0) - { - _ringIndex = 0; - sx = _center.m_X / SectorSize; - sy = _center.m_Y / SectorSize; - return sx >= _sectorStartX; - } - - sx = sy = 0; - return false; - } - - var totalSectors = _ring * 8; - - // Keep trying sectors in this ring until we find a valid one or exhaust the ring - while (true) - { - var nextIndex = _ringIndex + 1; - - if (nextIndex >= totalSectors) - { - sx = sy = 0; - return false; - } - - _ringIndex = nextIndex; - CalculatePositionFromIndex(nextIndex, out sx, out sy); - - if (sx >= _sectorStartX) - { - return true; - } - } - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CalculatePositionFromIndex(int index, out int x, out int y) - { - var centerSectorX = _center.m_X / SectorSize; - var centerSectorY = _center.m_Y / SectorSize; - - var ringSize = _ring * 2; - var startX = centerSectorX - _ring; - var startY = centerSectorY - _ring; - - if (index <= ringSize) // Top edge - { - x = startX + index; - y = startY; - } - else if (index <= ringSize * 2) // Right edge - { - x = startX + ringSize; - y = startY + (index - ringSize); - } - else if (index <= ringSize * 3) // Bottom edge - { - x = startX + ringSize - (index - ringSize * 2); - y = startY + ringSize; - } - else // Left edge - { - x = startX; - y = startY + ringSize - (index - ringSize * 3); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int MinDistToSectorSqrt(int cx, int cy, int sectorX, int sectorY) - { - var x0 = sectorX * SectorSize; - var y0 = sectorY * SectorSize; - var x1 = x0 + (SectorSize - 1); - var y1 = y0 + (SectorSize - 1); - - var dx = 0; - if (cx < x0) - { - dx = x0 - cx; - } - else if (cx > x1) - { - dx = cx - x1; - } - - var dy = 0; - if (cy < y0) - { - dy = y0 - cy; - } - else if (cy > y1) - { - dy = cy - y1; - } - - return (int)Math.Sqrt(dx * dx + dy * dy); - } - } -} diff --git a/Projects/Server/Maps/Map.ClientEnumerator.cs b/Projects/Server/Maps/Map.ClientEnumerator.cs index 0e9dc04eb..ae53010a1 100644 --- a/Projects/Server/Maps/Map.ClientEnumerator.cs +++ b/Projects/Server/Maps/Map.ClientEnumerator.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Map.ClientEnumerator.cs * * * @@ -46,12 +46,8 @@ public partial class Map GetClientsInRange(p.m_X, p.m_Y, range); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientBoundsEnumerable GetClientsInRange(int x, int y, int range) - { - var clampedRange = Math.Max(0, range); - var edge = clampedRange * 2 + 1; - return GetClientsInBounds(new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge)); - } + public ClientBoundsEnumerable GetClientsInRange(int x, int y, int range) => + GetClientsInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public ClientBoundsEnumerable GetClientsInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) => @@ -80,7 +76,6 @@ public partial class Map public ref struct ClientAtEnumerator { - private readonly Map _map; private bool _started; private Point2D _location; private ref readonly ValueLinkList _linkList; @@ -90,15 +85,9 @@ public partial class Map [MethodImpl(MethodImplOptions.AggressiveInlining)] public ClientAtEnumerator(Map map, Point2D loc) { - _map = map; _started = false; _location = loc; - - if (map != null) - { - _linkList = ref map.GetSector(loc.m_X, loc.m_Y).Clients; - } - + _linkList = ref map.GetSector(loc.m_X, loc.m_Y).Clients; _version = 0; _current = null; } @@ -106,11 +95,6 @@ public partial class Map [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { - if (_map == null) - { - return false; - } - ref var loc = ref _location; NetState current; Mobile m; @@ -121,7 +105,7 @@ public partial class Map _started = true; _version = _linkList.Version; - m = current?.Mobile; + m = current.Mobile; if (m?.Deleted == false && m.X == loc.m_X && m.Y == loc.m_Y) { _current = current; @@ -141,7 +125,7 @@ public partial class Map { current = current.Next; - m = current?.Mobile; + m = current.Mobile; if (m?.Deleted == false && m.X == loc.m_X && m.Y == loc.m_Y) { _current = current; @@ -179,10 +163,10 @@ public partial class Map } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientBoundsEnumerator GetEnumerator() => new(_map, _bounds, _makeBoundsInclusive); + public MobileEnumerator GetEnumerator() => new(_map, _bounds, _makeBoundsInclusive); } - public ref struct ClientBoundsEnumerator + public ref struct MobileEnumerator { private readonly Map _map; private readonly int _sectorStartX; @@ -198,7 +182,7 @@ public partial class Map private NetState _current; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientBoundsEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive) + public MobileEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive) { _map = map; _bounds = bounds; @@ -236,8 +220,9 @@ public partial class Map throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); } - var current = _current; - ref var bounds = ref _bounds; + Mobile m; + NetState current = _current; + ref Rectangle2D bounds = ref _bounds; var currentSectorX = _currentSectorX; var currentSectorY = _currentSectorY; var sectorEndX = _sectorEndX; @@ -270,7 +255,7 @@ public partial class Map current = _linkList._first; } - var m = current.Mobile; + m = current.Mobile; if (m?.Deleted == false && bounds.Contains(m.Location)) { _current = current; diff --git a/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs b/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs deleted file mode 100644 index 207928f68..000000000 --- a/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs +++ /dev/null @@ -1,325 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Map.ItemByDistanceEnumerator.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using Server.Collections; - -namespace Server; - -public partial class Map -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p) => - GetItemsInRangeByDistance(p, Core.GlobalMaxUpdateRange); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p, int range) => - GetItemsInRangeByDistance(p, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p) where T : Item => - GetItemsInRangeByDistance(p, Core.GlobalMaxUpdateRange); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p, int range) where T : Item => - GetItemsInRangeByDistance(p.m_X, p.m_Y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p) => - GetItemsInRangeByDistance(p, Core.GlobalMaxUpdateRange); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p, int range) => - GetItemsInRangeByDistance(p, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p) where T : Item => - GetItemsInRangeByDistance(p, Core.GlobalMaxUpdateRange); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p, int range) where T : Item => - GetItemsInRangeByDistance(p.m_X, p.m_Y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInRangeByDistance(int x, int y, int range) => - GetItemsInRangeByDistance(x, y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInRangeByDistance(int x, int y, int range) where T : Item - { - var clampedRange = Math.Max(0, range); - var edge = clampedRange * 2 + 1; - return GetItemsInBoundsByDistance( - new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge), - new Point2D(x, y) - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) => - GetItemsInBoundsByDistance(bounds, makeBoundsInclusive); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) - where T : Item => - GetItemsInBoundsByDistance( - bounds, - new Point2D(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2), - makeBoundsInclusive - ); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ItemDistanceEnumerable GetItemsInBoundsByDistance( - Rectangle2D bounds, Point2D center, bool makeBoundsInclusive = false - ) where T : Item => new(this, bounds, center, makeBoundsInclusive); - - public ref struct ItemDistanceEnumerable where T : Item - { - private readonly Map _map; - private readonly Rectangle2D _bounds; - private readonly Point2D _center; - private readonly bool _makeBoundsInclusive; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) - { - _map = map; - _bounds = bounds; - _center = center; - _makeBoundsInclusive = makeBoundsInclusive; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerator GetEnumerator() => new(_map, _bounds, _center, _makeBoundsInclusive); - } - - public ref struct ItemDistanceEnumerator where T : Item - { - private Map _map; - private Point2D _center; - private Rectangle2D _bounds; - - private int _sectorStartX; - private int _maxRing; - - private int _ring; // -1 = uninitialized, then 0.._maxRing - private int _ringIndex; // Current index within the ring - - private int _currentSectorX; - private int _currentSectorY; - - private ref readonly ValueLinkList _linkList; - private int _currentVersion; - private T _current; - - private int _minDistance; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerator(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) - { - _map = map; - _center = center; - _bounds = makeBoundsInclusive - ? new Rectangle2D(bounds.X, bounds.Y, bounds.Width + 1, bounds.Height + 1) - : bounds; - - _current = null; - - if (map != null) - { - var centerSectorX = center.m_X / SectorSize; - var centerSectorY = center.m_Y / SectorSize; - - map.CalculateSectors(_bounds, out _sectorStartX, out var sectorStartY, out var sectorEndX, out var sectorEndY); - - // Calculate max ring based on bounds - var dx = Math.Max(centerSectorX - _sectorStartX, sectorEndX - centerSectorX); - var dy = Math.Max(centerSectorY - sectorStartY, sectorEndY - centerSectorY); - _maxRing = Math.Max(dx, dy); - } - - _ring = -1; - _ringIndex = -1; - _currentSectorX = 0; - _currentSectorY = 0; - - _currentVersion = 0; - _minDistance = 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - var map = _map; - - if (map == null) - { - return false; - } - - if (!Unsafe.IsNullRef(in _linkList) && _linkList.Version != _currentVersion) - { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); - } - - Item current = _current; - - while (true) - { - current = current?.Next; - - while (current == null) - { - while (!TryNextSectorInRing(out _currentSectorX, out _currentSectorY)) - { - // Current ring exhausted, try next ring - if (_ring >= _maxRing) - { - return false; // No more rings to search - } - - _ring++; - _ringIndex = -1; - } - - _linkList = ref map.GetRealSector(_currentSectorX, _currentSectorY).Items; - _currentVersion = _linkList.Version; - current = _linkList._first; - - if (current != null) - { - _minDistance = MinDistToSectorSqrt(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); - } - } - - if (current is T { Deleted: false } o && _bounds.Contains(o.Location)) - { - _current = o; - return true; - } - } - } - - public (T Value, int MinDistance) Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (_current, _minDistance); - } - private bool TryNextSectorInRing(out int sx, out int sy) - { - if (_ring == 0) - { - // Center sector - if (_ringIndex < 0) - { - _ringIndex = 0; - sx = _center.m_X / SectorSize; - sy = _center.m_Y / SectorSize; - return sx >= _sectorStartX; - } - - sx = sy = 0; - return false; - } - - var totalSectors = _ring * 8; - - // Keep trying sectors in this ring until we find a valid one or exhaust the ring - while (true) - { - var nextIndex = _ringIndex + 1; - - if (nextIndex >= totalSectors) - { - sx = sy = 0; - return false; - } - - _ringIndex = nextIndex; - CalculatePositionFromIndex(nextIndex, out sx, out sy); - - if (sx >= _sectorStartX) - { - return true; - } - } - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CalculatePositionFromIndex(int index, out int x, out int y) - { - var centerSectorX = _center.m_X / SectorSize; - var centerSectorY = _center.m_Y / SectorSize; - - var ringSize = _ring * 2; - var startX = centerSectorX - _ring; - var startY = centerSectorY - _ring; - - if (index <= ringSize) // Top edge - { - x = startX + index; - y = startY; - } - else if (index <= ringSize * 2) // Right edge - { - x = startX + ringSize; - y = startY + (index - ringSize); - } - else if (index <= ringSize * 3) // Bottom edge - { - x = startX + ringSize - (index - ringSize * 2); - y = startY + ringSize; - } - else // Left edge - { - x = startX; - y = startY + ringSize - (index - ringSize * 3); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int MinDistToSectorSqrt(int cx, int cy, int sectorX, int sectorY) - { - var x0 = sectorX * SectorSize; - var y0 = sectorY * SectorSize; - var x1 = x0 + (SectorSize - 1); - var y1 = y0 + (SectorSize - 1); - - var dx = 0; - if (cx < x0) - { - dx = x0 - cx; - } - else if (cx > x1) - { - dx = cx - x1; - } - - var dy = 0; - if (cy < y0) - { - dy = y0 - cy; - } - else if (cy > y1) - { - dy = cy - y1; - } - - return (int)Math.Sqrt(dx * dx + dy * dy); - } - } -} diff --git a/Projects/Server/Maps/Map.ItemEnumerator.cs b/Projects/Server/Maps/Map.ItemEnumerator.cs index db13edfaf..4bf8f4202 100644 --- a/Projects/Server/Maps/Map.ItemEnumerator.cs +++ b/Projects/Server/Maps/Map.ItemEnumerator.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Map.Enumerators.cs * * * @@ -69,12 +69,8 @@ public partial class Map GetItemsInRange(p.m_X, p.m_Y, range); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemBoundsEnumerable GetItemsInRange(int x, int y, int range) where T : Item - { - var clampedRange = Math.Max(0, range); - var edge = clampedRange * 2 + 1; - return GetItemsInBounds(new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge)); - } + public ItemBoundsEnumerable GetItemsInRange(int x, int y, int range) where T : Item => + GetItemsInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public ItemBoundsEnumerable GetItemsInBounds(Rectangle2D bounds) => GetItemsInBounds(bounds); @@ -256,7 +252,7 @@ public partial class Map } Item current = _current; - ref var bounds = ref _bounds; + ref Rectangle2D bounds = ref _bounds; var currentSectorX = _currentSectorX; var currentSectorY = _currentSectorY; var sectorEndX = _sectorEndX; diff --git a/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs b/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs deleted file mode 100644 index e5a8d0b91..000000000 --- a/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs +++ /dev/null @@ -1,320 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Map.MobileByDistanceEnumerator.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using Server.Collections; - -namespace Server; - -public partial class Map -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p) => - GetMobilesInRangeByDistance(p, Core.GlobalMaxUpdateRange); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p, int range) => - GetMobilesInRangeByDistance(p, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p) where T : Mobile => - GetMobilesInRangeByDistance(p, Core.GlobalMaxUpdateRange); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p, int range) where T : Mobile => - GetMobilesInRangeByDistance(p.m_X, p.m_Y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p) => - GetMobilesInRangeByDistance(p, Core.GlobalMaxUpdateRange); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p, int range) => - GetMobilesInRangeByDistance(p, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p) where T : Mobile => - GetMobilesInRangeByDistance(p, Core.GlobalMaxUpdateRange); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p, int range) where T : Mobile => - GetMobilesInRangeByDistance(p.m_X, p.m_Y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInRangeByDistance(int x, int y, int range) => - GetMobilesInRangeByDistance(x, y, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInRangeByDistance(int x, int y, int range) where T : Mobile - { - var clampedRange = Math.Max(0, range); - var edge = clampedRange * 2 + 1; - return GetMobilesInBoundsByDistance( - new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge), - new Point2D(x, y) - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) => - GetMobilesInBoundsByDistance(bounds, makeBoundsInclusive); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : Mobile => - GetMobilesInBoundsByDistance(bounds, new Point2D(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2), makeBoundsInclusive); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private MobileDistanceEnumerable GetMobilesInBoundsByDistance( - Rectangle2D bounds, Point2D center, bool makeBoundsInclusive = false - ) where T : Mobile => new(this, bounds, center, makeBoundsInclusive); - - public ref struct MobileDistanceEnumerable where T : Mobile - { - private readonly Map _map; - private readonly Rectangle2D _bounds; - private readonly Point2D _center; - private readonly bool _makeBoundsInclusive; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) - { - _map = map; - _bounds = bounds; - _center = center; - _makeBoundsInclusive = makeBoundsInclusive; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerator GetEnumerator() => new(_map, _bounds, _center, _makeBoundsInclusive); - } - - public ref struct MobileDistanceEnumerator where T : Mobile - { - private Map _map; - private Point2D _center; - private Rectangle2D _bounds; - - private int _sectorStartX; - private int _maxRing; - - private int _ring; // -1 = uninitialized, then 0.._maxRing - private int _ringIndex; // Current index within the ring - - private int _currentSectorX; - private int _currentSectorY; - - private ref readonly ValueLinkList _linkList; - private int _currentVersion; - private T _current; - - private int _minDistance; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerator(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) - { - _map = map; - _center = center; - _bounds = makeBoundsInclusive - ? new Rectangle2D(bounds.X, bounds.Y, bounds.Width + 1, bounds.Height + 1) - : bounds; - - _current = null; - - if (map != null) - { - var centerSectorX = center.m_X / SectorSize; - var centerSectorY = center.m_Y / SectorSize; - - map.CalculateSectors(_bounds, out _sectorStartX, out var sectorStartY, out var sectorEndX, out var sectorEndY); - - // Calculate max ring based on bounds - var dx = Math.Max(centerSectorX - _sectorStartX, sectorEndX - centerSectorX); - var dy = Math.Max(centerSectorY - sectorStartY, sectorEndY - centerSectorY); - _maxRing = Math.Max(dx, dy); - } - - _ring = -1; - _ringIndex = -1; - _currentSectorX = 0; - _currentSectorY = 0; - - _currentVersion = 0; - _minDistance = 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - var map = _map; - - if (map == null) - { - return false; - } - - if (!Unsafe.IsNullRef(in _linkList) && _linkList.Version != _currentVersion) - { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); - } - - Mobile current = _current; - - while (true) - { - current = current?.Next; - - while (current == null) - { - while (!TryNextSectorInRing(out _currentSectorX, out _currentSectorY)) - { - // Current ring exhausted, try next ring - if (_ring >= _maxRing) - { - return false; // No more rings to search - } - - _ring++; - _ringIndex = -1; - } - - _linkList = ref map.GetRealSector(_currentSectorX, _currentSectorY).Mobiles; - _currentVersion = _linkList.Version; - current = _linkList._first; - - if (current != null) - { - _minDistance = MinDistToSectorSqrt(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); - } - } - - if (current is T { Deleted: false } o && _bounds.Contains(o.Location)) - { - _current = o; - return true; - } - } - } - - public (T Value, int MinDistance) Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (_current, _minDistance); - } - private bool TryNextSectorInRing(out int sx, out int sy) - { - if (_ring == 0) - { - // Center sector - if (_ringIndex < 0) - { - _ringIndex = 0; - sx = _center.m_X / SectorSize; - sy = _center.m_Y / SectorSize; - return sx >= _sectorStartX; - } - - sx = sy = 0; - return false; - } - - var totalSectors = _ring * 8; - - // Keep trying sectors in this ring until we find a valid one or exhaust the ring - while (true) - { - var nextIndex = _ringIndex + 1; - - if (nextIndex >= totalSectors) - { - sx = sy = 0; - return false; - } - - _ringIndex = nextIndex; - CalculatePositionFromIndex(nextIndex, out sx, out sy); - - if (sx >= _sectorStartX) - { - return true; - } - } - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CalculatePositionFromIndex(int index, out int x, out int y) - { - var centerSectorX = _center.m_X / SectorSize; - var centerSectorY = _center.m_Y / SectorSize; - - var ringSize = _ring * 2; - var startX = centerSectorX - _ring; - var startY = centerSectorY - _ring; - - if (index <= ringSize) // Top edge - { - x = startX + index; - y = startY; - } - else if (index <= ringSize * 2) // Right edge - { - x = startX + ringSize; - y = startY + (index - ringSize); - } - else if (index <= ringSize * 3) // Bottom edge - { - x = startX + ringSize - (index - ringSize * 2); - y = startY + ringSize; - } - else // Left edge - { - x = startX; - y = startY + ringSize - (index - ringSize * 3); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int MinDistToSectorSqrt(int cx, int cy, int sectorX, int sectorY) - { - var x0 = sectorX * SectorSize; - var y0 = sectorY * SectorSize; - var x1 = x0 + (SectorSize - 1); - var y1 = y0 + (SectorSize - 1); - - var dx = 0; - if (cx < x0) - { - dx = x0 - cx; - } - else if (cx > x1) - { - dx = cx - x1; - } - - var dy = 0; - if (cy < y0) - { - dy = y0 - cy; - } - else if (cy > y1) - { - dy = cy - y1; - } - - return (int)Math.Sqrt(dx * dx + dy * dy); - } - } -} diff --git a/Projects/Server/Maps/Map.MobileEnumerator.cs b/Projects/Server/Maps/Map.MobileEnumerator.cs index 6c27103e6..cac64ce3a 100644 --- a/Projects/Server/Maps/Map.MobileEnumerator.cs +++ b/Projects/Server/Maps/Map.MobileEnumerator.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Map.MobileEnumerator.cs * * * @@ -69,12 +69,8 @@ public partial class Map GetMobilesInRange(p.m_X, p.m_Y, range); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileBoundsEnumerable GetMobilesInRange(int x, int y, int range) where T : Mobile - { - var clampedRange = Math.Max(0, range); - var edge = clampedRange * 2 + 1; - return GetMobilesInBounds(new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge)); - } + public MobileBoundsEnumerable GetMobilesInRange(int x, int y, int range) where T : Mobile => + GetMobilesInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public MobileBoundsEnumerable GetMobilesInBounds(Rectangle2D bounds) => GetMobilesInBounds(bounds); @@ -255,7 +251,7 @@ public partial class Map } Mobile current = _current; - ref var bounds = ref _bounds; + ref Rectangle2D bounds = ref _bounds; var currentSectorX = _currentSectorX; var currentSectorY = _currentSectorY; var sectorEndX = _sectorEndX; diff --git a/Projects/Server/Maps/Map.MultiEnumerator.cs b/Projects/Server/Maps/Map.MultiEnumerator.cs index b0e069a75..bc1f7b018 100644 --- a/Projects/Server/Maps/Map.MultiEnumerator.cs +++ b/Projects/Server/Maps/Map.MultiEnumerator.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Map.MultiEnumerator.cs * * * @@ -17,13 +17,15 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Server.Collections; using Server.Items; namespace Server; public partial class Map { + private static SectorMultiValueLinkList _emptyMultiLinkList = new(); + public static ref readonly SectorMultiValueLinkList EmptyMultiLinkList => ref _emptyMultiLinkList; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiSectorEnumerable GetMultisInSector(Point3D p) => GetMultisInSector(p); @@ -70,12 +72,8 @@ public partial class Map GetMultisInRange(p.m_X, p.m_Y, range); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiBoundsEnumerable GetMultisInRange(int x, int y, int range) where T : BaseMulti - { - var clampedRange = Math.Max(0, range); - var edge = clampedRange * 2 + 1; - return GetMultisInBounds(new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge)); - } + public MultiBoundsEnumerable GetMultisInRange(int x, int y, int range) where T : BaseMulti => + GetMultisInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiBoundsEnumerable GetMultisInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) => @@ -85,8 +83,6 @@ public partial class Map public MultiBoundsEnumerable GetMultisInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : BaseMulti => new(this, bounds, makeBoundsInclusive); - private static readonly HashSet _sharedDupes = []; - public ref struct MultiSectorEnumerable(Map map, Point2D loc) where T : BaseMulti { public static MultiSectorEnumerable Empty @@ -102,42 +98,27 @@ public partial class Map public ref struct MultiSectorEnumerator where T : BaseMulti { private readonly Span _list; - private readonly int _version; - private readonly Sector _sector; private int _index; private T _current; [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiSectorEnumerator(Map map, Point2D loc) { - if (map == null) - { - _list = Span.Empty; - _sector = null; - _version = 0; - } - else - { - _sector = map.GetSector(loc.m_X, loc.m_Y); - _list = CollectionsMarshal.AsSpan(_sector.Multis); - _version = _sector.MultisVersion; - } + _list = map == null + ? Span.Empty + : CollectionsMarshal.AsSpan(map.GetSector(loc.m_X, loc.m_Y).Multis); - _index = -1; + _index = 0; _current = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { - if (_sector != null && _version != _sector.MultisVersion) + while ((uint)_index < (uint)_list.Length) { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); - } - - while (++_index < _list.Length) - { - if (_list[_index] is T { Deleted: false } o) + var current = _list[_index++]; + if (current is T { Deleted: false } o) { _current = o; return true; @@ -179,27 +160,24 @@ public partial class Map public ref struct MultiBoundsEnumerator where T : BaseMulti { - private Map _map; - private int _sectorStartX; - private int _sectorEndX; - private int _sectorEndY; + private readonly Map _map; + private readonly int _sectorStartX; + private readonly int _sectorEndX; + private readonly int _sectorEndY; private Rectangle2D _bounds; private int _currentSectorX; private int _currentSectorY; - private Span _currentList; - private int _currentIndex; - private int _currentVersion; - private Sector _currentSector; + private Span _list; private T _current; + private int _index; + private HashSet _dupes; [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiBoundsEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive) { - _sharedDupes.Clear(); - _map = map; _bounds = bounds; @@ -218,78 +196,62 @@ public partial class Map // We start the X sector one short because it gets incremented immediately in MoveNext() _currentSectorX = _sectorStartX - 1; _currentSectorY = _sectorStartY; + _index = 0; } - - _currentList = default; - _currentIndex = -1; - _currentVersion = 0; - _currentSector = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() + private bool GetMulti() { - var map = _map; + ref Rectangle2D bounds = ref _bounds; - if (map == null) + while ((uint)_index < (uint)_list.Length) { - return false; + var current = _list[_index++]; + _dupes ??= new HashSet(); + + if (current is T { Deleted: false } o && bounds.Contains(o.Location) && !_dupes.Contains(o.Serial)) + { + _dupes.Add(o.Serial); + _current = o; + return true; + } } - ref var bounds = ref _bounds; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool GetSector() + { var currentSectorX = _currentSectorX; var currentSectorY = _currentSectorY; var sectorEndX = _sectorEndX; var sectorEndY = _sectorEndY; - while (true) + // Move to next sector + if (currentSectorX < sectorEndX) { - // Try to advance in the current list - if (_currentList.Length > 0) - { - if (_currentVersion != _currentSector.MultisVersion) - { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); - } - - while (++_currentIndex < _currentList.Length) - { - var item = _currentList[_currentIndex]; - if (item is T { Deleted: false } o && bounds.Contains(o.Location)) - { - // Multis can span multiple sectors, so we need to deduplicate - if (_sharedDupes.Add(o.Serial)) - { - _current = o; - return true; - } - } - } - } - - // Move to next sector - if (currentSectorX < sectorEndX) - { - _currentSectorX = ++currentSectorX; - } - else if (currentSectorY < sectorEndY) - { - _currentSectorX = currentSectorX = _sectorStartX; - _currentSectorY = ++currentSectorY; - } - else - { - // Ran out of sectors - return false; - } - - _currentSector = map.GetRealSector(currentSectorX, currentSectorY); - _currentList = CollectionsMarshal.AsSpan(_currentSector.Multis); - _currentVersion = _currentSector.MultisVersion; - _currentIndex = -1; + _currentSectorX = ++currentSectorX; } + else if (currentSectorY < sectorEndY) + { + _currentSectorX = currentSectorX = _sectorStartX; + _currentSectorY = ++currentSectorY; + } + else + { + // Ran out of sectors + return false; + } + + _list = CollectionsMarshal.AsSpan(_map.GetRealSector(currentSectorX, currentSectorY).Multis); + return GetMulti(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() => _map != null && (GetMulti() || GetSector()); + public T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/Server/Maps/Map.StaticTileEnumerator.cs b/Projects/Server/Maps/Map.StaticTileEnumerator.cs index 27075b59d..ec83d244e 100644 --- a/Projects/Server/Maps/Map.StaticTileEnumerator.cs +++ b/Projects/Server/Maps/Map.StaticTileEnumerator.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Map.StaticTileEnumerator.cs * * * @@ -92,8 +92,8 @@ public partial class Map var components = multi!.Components; var location = multi!.Location; - var offsetX = p.X - location.X - components.Min.X; - var offsetY = p.Y - location.Y - components.Min.Y; + int offsetX = p.X - location.X - components.Min.X; + int offsetY = p.Y - location.Y - components.Min.Y; if (offsetX >= 0 && offsetY >= 0 && offsetX < components.Width && offsetY < components.Height) { diff --git a/Projects/Server/Maps/Map.cs b/Projects/Server/Maps/Map.cs index e51a848d2..0d1bc591b 100644 --- a/Projects/Server/Maps/Map.cs +++ b/Projects/Server/Maps/Map.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Map.cs * * * @@ -38,20 +38,6 @@ public enum MapRules FeluccaRules = None } -/// -/// Indicates why a spawn position check failed. Used by spawners to determine -/// if optimization (caching) should be enabled. -/// -[Flags] -public enum SpawnFailureReason -{ - None = 0, - InvalidMap = 1 << 0, // Internal map or out of bounds - RegionBlocked = 1 << 1, // Region doesn't allow spawning - TransientBlocker = 1 << 2, // Mobile or movable item blocking - NonTransientBlocker = 1 << 3 // Static, multi, impassable land, or non-movable item -} - public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsable { public const int SectorSize = 16; @@ -483,89 +469,6 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return surface; } - /// - /// Gets the Z level of the highest surface that is at or below . - /// - /// The reference point. - /// The Z level of the surface, or p.Z if no surface is found below. - public int GetTopSurfaceZ(Point3D p) - { - if (this == Internal) - { - return p.Z; - } - - var surfaceZ = int.MinValue; - - var lt = Tiles.GetLandTile(p.X, p.Y); - - if (!lt.Ignored) - { - var avgZ = GetAverageZ(p.X, p.Y); - - if (avgZ <= p.Z) - { - surfaceZ = avgZ; - - if (surfaceZ == p.Z) - { - return surfaceZ; - } - } - } - - foreach (var tile in Tiles.GetStaticAndMultiTiles(p.X, p.Y)) - { - var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - - if (id.Surface || id.Wet) - { - var tileZ = tile.Z + id.CalcHeight; - - if (tileZ > surfaceZ && tileZ <= p.Z) - { - surfaceZ = tileZ; - - if (surfaceZ == p.Z) - { - return surfaceZ; - } - } - } - } - - var sector = GetSector(p.X, p.Y); - - foreach (var item in sector.Items) - { - if (item is BaseMulti || item.ItemID > TileData.MaxItemValue || !item.AtWorldPoint(p.X, p.Y) || - item.Movable) - { - continue; - } - - var id = item.ItemData; - - if (id.Surface || id.Wet) - { - var itemZ = item.Z + id.CalcHeight; - - if (itemZ > surfaceZ && itemZ <= p.Z) - { - surfaceZ = itemZ; - - if (surfaceZ == p.Z) - { - return surfaceZ; - } - } - } - } - - // If no surface found below, return the original Z - return surfaceZ == int.MinValue ? p.Z : surfaceZ; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Bound(int x, int y, out int newX, out int newY) { @@ -591,10 +494,10 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa out int sectorStartX, out int sectorStartY, out int sectorEndX, out int sectorEndY) { - var left = bounds.Start.X; - var top = bounds.Start.Y; - var right = bounds.End.X; - var bottom = bounds.End.Y; + int left = bounds.Start.X; + int top = bounds.Start.Y; + int right = bounds.End.X; + int bottom = bounds.End.Y; // Limit the coordinates to inside the valid map region Bound(left, top, out left, out top); @@ -1012,91 +915,6 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return !requireSurface || hasSurface; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CanFitItem(Point3D p, int height) => CanFitItem(p.m_X, p.m_Y, p.m_Z, height); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CanFitItem(Point2D p, int z, int height) => CanFitItem(p.m_X, p.m_Y, z, height); - - /// - /// Checks if an item can be placed at the specified location. - /// Unlike CanFit, this treats Surface+Impassable tiles (tables, furniture) as valid surfaces, - /// matching the behavior of item drop logic. - /// - public bool CanFitItem(int x, int y, int z, int height) - { - if (this == Internal || x < 0 || y < 0 || x >= Width || y >= Height) - { - return false; - } - - var hasSurface = false; - - var lt = Tiles.GetLandTile(x, y); - GetAverageZ(x, y, out var lowZ, out var avgZ, out _); - var landFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags; - - // Impassable land still blocks items - if ((landFlags & TileFlag.Impassable) != 0 && avgZ > z && z + height > lowZ) - { - return false; - } - - // Passable land is a valid surface - if ((landFlags & TileFlag.Impassable) == 0 && z == avgZ && !lt.Ignored) - { - hasSurface = true; - } - - foreach (var tile in Tiles.GetStaticAndMultiTiles(x, y)) - { - var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - var surface = id.Surface; - var impassable = id.Impassable; - - // Tiles block if item would intersect with them - if ((surface || impassable) && tile.Z + id.CalcHeight > z && z + height > tile.Z) - { - return false; - } - - // Surface tiles (including Surface+Impassable like tables) are valid surfaces for items - if (surface && z == tile.Z + id.CalcHeight) - { - hasSurface = true; - } - } - - var sector = GetSector(x, y); - - foreach (var item in sector.Items) - { - if (item is BaseMulti || item.ItemID > TileData.MaxItemValue || !item.AtWorldPoint(x, y)) - { - continue; - } - - var id = item.ItemData; - var surface = id.Surface; - var impassable = id.Impassable; - - // Items block if placement would intersect - if ((surface || impassable) && item.Z + id.CalcHeight > z && z + height > item.Z) - { - return false; - } - - // Surface items (including Surface+Impassable like tables) are valid surfaces - // Must be non-movable to be a stable surface - if (surface && !item.Movable && z == item.Z + id.CalcHeight) - { - hasSurface = true; - } - } - - return hasSurface; - } - public bool CanSpawnMobile(Point3D p) => CanSpawnMobile(p.m_X, p.m_Y, p.m_Z); public bool CanSpawnMobile(Point2D p, int z) => CanSpawnMobile(p.m_X, p.m_Y, z); @@ -1104,323 +922,6 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public bool CanSpawnMobile(int x, int y, int z) => Region.Find(new Point3D(x, y, z), this).AllowSpawn() && CanFit(x, y, z, 16); - /// - /// Finds a valid spawn Z within the specified range by checking land, static, multi tiles, and world items. - /// Prefers the lowest valid surface (ground/floor over tables/platforms). - /// - /// X coordinate - /// Y coordinate - /// Minimum Z (inclusive) - /// Maximum Z (inclusive) - /// Whether the spawned entity can swim (water surfaces valid) - /// Whether the spawned entity cannot walk (water-only) - /// The valid spawn Z if found - /// True if a valid spawn Z was found within the range - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CanSpawnMobile(int x, int y, int minZ, int maxZ, bool canSwim, bool cantWalk, out int spawnZ) - => CanSpawnMobile(x, y, minZ, maxZ, canSwim, cantWalk, out spawnZ, out _); - - /// - /// Finds a valid spawn Z within the specified range by checking land, static, multi tiles, and world items. - /// Prefers the lowest valid surface (ground/floor over tables/platforms). - /// Also reports the reason for failure if no valid position is found. - /// - /// X coordinate - /// Y coordinate - /// Minimum Z (inclusive) - /// Maximum Z (inclusive) - /// Whether the spawned entity can swim (water surfaces valid) - /// Whether the spawned entity cannot walk (water-only) - /// The valid spawn Z if found - /// Indicates why spawn failed (if it did) - /// True if a valid spawn Z was found within the range - public bool CanSpawnMobile(int x, int y, int minZ, int maxZ, bool canSwim, bool cantWalk, out int spawnZ, out SpawnFailureReason failureReason) - { - spawnZ = 0; - failureReason = SpawnFailureReason.None; - - if (this == Internal || x < 0 || y < 0 || x >= Width || y >= Height) - { - failureReason = SpawnFailureReason.InvalidMap; - return false; - } - - if (!Region.Find(new Point3D(x, y, minZ), this).AllowSpawn()) - { - failureReason = SpawnFailureReason.RegionBlocked; - return false; - } - - // 256-bit bitmask approach for full Z range support (-128 to 127). - // Each bit represents a Z level relative to minZ. - // openSlots: bit set = Z level is not blocked - // surfaces: bit set = Z level has a valid surface - // Final result: lowest set bit in (surfaces & openSlots) - var openSlots = BitMask256.AllSet(); - var surfaces = BitMask256.AllClear(); - - // Track what types of blockers we encounter (for failure reason) - var hasNonTransientBlocker = false; - var hasTransientBlocker = false; - - // 1. Land tile - var landTile = Tiles.GetLandTile(x, y); - GetAverageZ(x, y, out var lowZ, out var avgZ, out _); - - if (!landTile.Ignored) - { - var landFlags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; - var isImpassable = (landFlags & TileFlag.Impassable) != 0; - var isWet = (landFlags & TileFlag.Wet) != 0; - - // Impassable land blocks, except water tiles don't block swimming mobs - if (isImpassable && !(canSwim && isWet)) - { - // Impassable land blocks z in range (lowZ - 16, avgZ) - ApplyBlockerMask(ref openSlots, lowZ - 16, avgZ, minZ); - hasNonTransientBlocker = true; - } - - // Surface: water for swimmers, passable land for walkers - if (avgZ >= minZ && avgZ <= maxZ && (canSwim && isWet || !cantWalk && !isImpassable)) - { - surfaces.SetBit(avgZ - minZ); - } - } - - // 2. Static and multi tiles (always non-transient) - foreach (var tile in Tiles.GetStaticAndMultiTiles(x, y)) - { - var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - var tileTop = tile.Z + id.CalcHeight; - var isSurface = id.Surface; - var isImpassable = id.Impassable; - var isWet = id.Wet; - - // Blocking: (surface || impassable) tiles block z in range (tile.Z - 16, tileTop) - // Exception: water tiles (Impassable | Wet) don't block swimming mobs - if ((isSurface || isImpassable) && !(canSwim && isWet)) - { - ApplyBlockerMask(ref openSlots, tile.Z - 16, tileTop, minZ); - hasNonTransientBlocker = true; - } - - // Surface candidate - if (tileTop >= minZ && tileTop <= maxZ && - (canSwim && isWet || !cantWalk && isSurface && !isImpassable)) - { - surfaces.SetBit(tileTop - minZ); - } - } - - // 3. World items - var sector = GetSector(x, y); - foreach (var item in sector.Items) - { - if (item is BaseMulti || item.ItemID > TileData.MaxItemValue || !item.AtWorldPoint(x, y)) - { - continue; - } - - var id = item.ItemData; - var itemTop = item.Z + id.CalcHeight; - var isSurface = id.Surface; - var isImpassable = id.Impassable; - var isWet = id.Wet; - - // Blocking: (surface || impassable) items block z in range (item.Z - 16, itemTop) - // Exception: water items (Impassable | Wet) don't block swimming mobs - if ((isSurface || isImpassable) && !(canSwim && isWet)) - { - ApplyBlockerMask(ref openSlots, item.Z - 16, itemTop, minZ); - - // Movable items are transient, non-movable are permanent - if (item.Movable || item.CanDecay()) - { - hasTransientBlocker = true; - } - else - { - hasNonTransientBlocker = true; - } - } - - // Surface candidate (non-movable only) - if (!item.Movable && itemTop >= minZ && itemTop <= maxZ && - (canSwim && isWet || !cantWalk && isSurface && !isImpassable)) - { - surfaces.SetBit(itemTop - minZ); - } - } - - // 4. Mobiles (always transient blockers) - foreach (var m in sector.Mobiles) - { - if (m.Location.m_X == x && m.Location.m_Y == y && - (m.AccessLevel == AccessLevel.Player || !m.Hidden)) - { - // Mobiles block z in range (m.Z - 16, m.Z + 16) - ApplyBlockerMask(ref openSlots, m.Z - 16, m.Z + 16, minZ); - hasTransientBlocker = true; - } - } - - // Find the lowest unblocked surface - var validSurfaces = surfaces.And(in openSlots); - var lowestBit = validSurfaces.LowestSetBit(); - - if (lowestBit < 0) - { - // Determine failure reason based on what blockers we encountered - // If no surfaces existed at all, that's also a non-transient issue (map geometry) - if (hasNonTransientBlocker || surfaces.IsEmpty()) - { - failureReason |= SpawnFailureReason.NonTransientBlocker; - } - - if (hasTransientBlocker) - { - failureReason |= SpawnFailureReason.TransientBlocker; - } - - return false; - } - - spawnZ = minZ + lowestBit; - return true; - } - - /// - /// Finds a valid spawn Z for items within the specified range. - /// Unlike CanSpawnMobile, treats Surface+Impassable tiles (tables, furniture) as valid surfaces. - /// - /// X coordinate - /// Y coordinate - /// Minimum Z (inclusive) - /// Maximum Z (inclusive) - /// The valid spawn Z if found - /// True if a valid spawn Z was found within the range - public bool CanSpawnItem(int x, int y, int minZ, int maxZ, out int spawnZ) - { - spawnZ = 0; - - if (this == Internal || x < 0 || y < 0 || x >= Width || y >= Height) - { - return false; - } - - if (!Region.Find(new Point3D(x, y, minZ), this).AllowSpawn()) - { - return false; - } - - // 256-bit bitmask approach for full Z range support. - // Unlike CanSpawnMobile, Surface+Impassable tiles (tables) are valid surfaces for items. - var openSlots = BitMask256.AllSet(); - var surfaces = BitMask256.AllClear(); - - // 1. Land tile - var landTile = Tiles.GetLandTile(x, y); - GetAverageZ(x, y, out var lowZ, out var avgZ, out _); - - if (!landTile.Ignored) - { - var landFlags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; - var isImpassable = (landFlags & TileFlag.Impassable) != 0; - - // Impassable land blocks - if (isImpassable) - { - ApplyBlockerMask(ref openSlots, lowZ - 16, avgZ, minZ); - } - - // Passable land is a valid surface - if (!isImpassable && avgZ >= minZ && avgZ <= maxZ) - { - surfaces.SetBit(avgZ - minZ); - } - } - - // 2. Static and multi tiles - foreach (var tile in Tiles.GetStaticAndMultiTiles(x, y)) - { - var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - var tileTop = tile.Z + id.CalcHeight; - var isSurface = id.Surface; - var isImpassable = id.Impassable; - - // Blocking: (surface || impassable) tiles block z in range (tile.Z - 16, tileTop) - if (isSurface || isImpassable) - { - ApplyBlockerMask(ref openSlots, tile.Z - 16, tileTop, minZ); - } - - // Surface candidate: Surface flag (including Surface+Impassable like tables) - if (isSurface && tileTop >= minZ && tileTop <= maxZ) - { - surfaces.SetBit(tileTop - minZ); - } - } - - // 3. World items - var sector = GetSector(x, y); - foreach (var item in sector.Items) - { - if (item is BaseMulti || item.ItemID > TileData.MaxItemValue || !item.AtWorldPoint(x, y)) - { - continue; - } - - var id = item.ItemData; - var itemTop = item.Z + id.CalcHeight; - var isSurface = id.Surface; - var isImpassable = id.Impassable; - - // Blocking: (surface || impassable) items block - if (isSurface || isImpassable) - { - ApplyBlockerMask(ref openSlots, item.Z - 16, itemTop, minZ); - } - - // Surface candidate: non-movable Surface items (including Surface+Impassable) - if (!item.Movable && isSurface && itemTop >= minZ && itemTop <= maxZ) - { - surfaces.SetBit(itemTop - minZ); - } - } - - // Find the lowest unblocked surface - var validSurfaces = surfaces.And(in openSlots); - var lowestBit = validSurfaces.LowestSetBit(); - - if (lowestBit < 0) - { - return false; - } - - spawnZ = minZ + lowestBit; - return true; - } - - /// - /// Clears bits in the mask for Z levels blocked by an object. - /// Converts (blockLow, blockHigh) exclusive world Z range to bit indices relative to minZ. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ApplyBlockerMask(ref BitMask256 mask, int blockLow, int blockHigh, int minZ) - { - // Blocked range is (blockLow, blockHigh) exclusive = [blockLow + 1, blockHigh - 1] inclusive - var startBit = blockLow - minZ + 1; - var endBit = blockHigh - minZ - 1; - - if (endBit < 0 || startBit > 255 || startBit > endBit) - { - return; - } - - mask.ClearRange(Math.Max(0, startBit), Math.Min(255, endBit)); - } - private class ZComparer : IComparer { public static readonly ZComparer Default = new(); @@ -1460,36 +961,43 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return _invalidSector; } - public bool LineOfSight(Point3D origin, Point3D destination) + public bool LineOfSight(Point3D org, Point3D dest) { if (this == Internal) { return false; } - if (!Utility.InRange(origin, destination, MaxLOSDistance)) + if (!Utility.InRange(org, dest, MaxLOSDistance)) { return false; } - if (origin == destination) + var end = dest; + + if (org.X > dest.X || org.X == dest.X && org.Y > dest.Y || org.X == dest.X && org.Y == dest.Y && org.Z > dest.Z) + { + (org, dest) = (dest, org); + } + + int height; + Point3D p; + var path = new Point3DList(); + TileFlag flags; + + if (org == dest) { return true; } - var end = destination; - - if (origin.X > destination.X || origin.X == destination.X && origin.Y > destination.Y || origin.X == destination.X - && origin.Y == destination.Y && origin.Z > destination.Z) + if (path.Count > 0) { - (origin, destination) = (destination, origin); + path.Clear(); } - var path = new Point3DList(); - - var xd = destination.X - origin.X; - var yd = destination.Y - origin.Y; - var zd = destination.Z - origin.Z; + var xd = dest.m_X - org.m_X; + var yd = dest.m_Y - org.m_Y; + var zd = dest.m_Z - org.m_Z; var zslp = Math.Sqrt(xd * xd + yd * yd); var sq3d = zd != 0 ? Math.Sqrt(zslp * zslp + zd * zd) : zslp; @@ -1497,22 +1005,20 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa var run = xd / sq3d; zslp = zd / sq3d; - double y = origin.Y; - double z = origin.Z; - double x = origin.X; - while (Utility.NumberBetween(x, destination.X, origin.X, 0.5) && - Utility.NumberBetween(y, destination.Y, origin.Y, 0.5) && - Utility.NumberBetween(z, destination.Z, origin.Z, 0.5)) + double y = org.m_Y; + double z = org.m_Z; + double x = org.m_X; + while (Utility.NumberBetween(x, dest.m_X, org.m_X, 0.5) && Utility.NumberBetween(y, dest.m_Y, org.m_Y, 0.5) && + Utility.NumberBetween(z, dest.m_Z, org.m_Z, 0.5)) { var ix = (int)Math.Round(x); var iy = (int)Math.Round(y); var iz = (int)Math.Round(z); - if (path.Count > 0) { - var p = path.Last; + p = path.Last; - if (p.X != ix || p.Y != iy || p.Z != iz) + if (p.m_X != ix || p.m_Y != iy || p.m_Z != iz) { path.Add(ix, iy, iz); } @@ -1532,24 +1038,29 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return true; // <--should never happen, but to be safe. } - if (path.Last != destination) + p = path.Last; + + if (p != dest) { - path.Add(destination); + path.Add(dest); } + Point3D pTop = org, pBottom = dest; + Utility.FixPoints(ref pTop, ref pBottom); + var pathCount = path.Count; - var endTop = end.Z + 1; + var endTop = end.m_Z + 1; for (var i = 0; i < pathCount; ++i) { var point = path[i]; - var pointTop = point.Z + 1; + var pointTop = point.m_Z + 1; var landTile = Tiles.GetLandTile(point.X, point.Y); - GetAverageZ(point.X, point.Y, out var landZ, out _, out var landTop); + GetAverageZ(point.m_X, point.m_Y, out var landZ, out _, out var landTop); if (landZ <= pointTop && landTop >= point.m_Z && - (point.X != end.X || point.Y != end.Y || landZ > endTop || landTop < end.Z) && + (point.m_X != end.m_X || point.m_Y != end.m_Y || landZ > endTop || landTop < end.m_Z) && !landTile.Ignored) { return false; @@ -1569,31 +1080,31 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa contains = ltID == InvalidLandTiles[j]; } - var foundStatic = false; + bool foundStatic = false; - foreach (var t in Tiles.GetStaticAndMultiTiles(point.X, point.Y)) + foreach (var t in Tiles.GetStaticAndMultiTiles(point.m_X, point.m_Y)) { foundStatic = true; var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; - var flags = id.Flags; + flags = id.Flags; + height = id.CalcHeight; - if ( - t.Z <= pointTop && t.Z + id.CalcHeight >= point.Z && - (flags & (TileFlag.Window | TileFlag.NoShoot)) != 0 && - (point.X != end.X || - point.Y != end.Y || - t.Z > endTop || t.Z + id.CalcHeight < end.Z) - ) + if (t.Z <= pointTop && t.Z + height >= point.Z && (flags & (TileFlag.Window | TileFlag.NoShoot)) != 0) { + if (point.m_X == end.m_X && point.m_Y == end.m_Y && t.Z <= endTop && t.Z + height >= end.m_Z) + { + continue; + } + return false; } } if (contains && !foundStatic) { - foreach (var item in GetItemsAt(point)) + foreach (Item item in GetItemsAt(point)) { if (item.Visible) { @@ -1609,66 +1120,57 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa } } - var pTop = origin; - var pBottom = destination; - Utility.FixPoints(ref pTop, ref pBottom); + var rect = new Rectangle2D(pTop.m_X, pTop.m_Y, pBottom.m_X - pTop.m_X + 1, pBottom.m_Y - pTop.m_Y + 1); - var rect = new Rectangle2D(pTop.X, pTop.Y, pBottom.X - pTop.X + 1, pBottom.Y - pTop.Y + 1); - - foreach (var item in GetItemsInBounds(rect)) + foreach (var i in GetItemsInBounds(rect)) { - if (!item.Visible) + if (!i.Visible) { continue; } - if (item is BaseMulti || item.ItemID > TileData.MaxItemValue) + if (i is BaseMulti || i.ItemID > TileData.MaxItemValue) { continue; } - var id = item.ItemData; - var flags = id.Flags; + var id = i.ItemData; + flags = id.Flags; if ((flags & (TileFlag.Window | TileFlag.NoShoot)) == 0) { continue; } - for (var i = 0; i < path.Count; ++i) + height = id.CalcHeight; + + var found = false; + + var count = path.Count; + + for (var j = 0; j < count; ++j) { - var pathPoint = path[i]; - var pointTop = pathPoint.Z + 1; - var itemLocation = item.Location; + var point = path[j]; + var pointTop = point.m_Z + 1; + var loc = i.Location; - if ( - // Item is on same tile as this point along the LOS path - itemLocation.X == pathPoint.X && - itemLocation.Y == pathPoint.Y && - itemLocation.Z <= pointTop && - - // Item rests on the same level as the path - itemLocation.Z + id.CalcHeight >= pathPoint.Z && - - // Fix door bugging monsters when door is at the START or END of the LOS path by allowing LOS - !(flags.HasFlag(TileFlag.Door) && - itemLocation.X == origin.X && itemLocation.Y == origin.Y || - itemLocation.X == destination.X && itemLocation.Y == destination.Y) && - - // Item is at some point along the path BEFORE the target - (itemLocation.X != end.X || - itemLocation.Y != end.Y || - - // Item is diagonally looking DOWN at the target - itemLocation.Z > endTop || - - // Item is diagonally looking UP at the target - itemLocation.Z + id.CalcHeight < end.Z) - ) + // if (t.Z <= point.Z && t.Z+height >= point.Z && ( height != 0 || ( t.Z == dest.Z && zd != 0 ) )) + if (loc.m_X == point.m_X && loc.m_Y == point.m_Y && loc.m_Z <= pointTop && loc.m_Z + height >= point.m_Z) { - return false; + if (loc.m_X != end.m_X || loc.m_Y != end.m_Y || loc.m_Z > endTop || loc.m_Z + height < end.m_Z) + { + found = true; + break; + } } } + + if (!found) + { + continue; + } + + return false; } return true; @@ -1788,7 +1290,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return Internal; } - for (var i = 0; i < Maps.Length; i++) + for (int i = 0; i < Maps.Length; i++) { var map = Maps[i]; if (map == null) @@ -1831,7 +1333,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return true; } - for (var i = 0; i < Maps.Length; i++) + for (int i = 0; i < Maps.Length; i++) { var map = Maps[i]; if (map == null) @@ -1854,13 +1356,11 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa { // TODO: Can we avoid this? private static readonly List m_DefaultRectList = new(); - private static readonly List m_DefaultMultiList = new(); private bool m_Active; private ValueLinkList _clients; private ValueLinkList _items; private ValueLinkList _mobiles; - private List _multis; - private int _multisVersion; + private List _multis = new(); private List _regions; public Sector(int x, int y, Map owner) @@ -1873,17 +1373,9 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public List Regions => _regions ?? m_DefaultRectList; - internal List Multis => _multis ?? m_DefaultMultiList; + internal List Multis => _multis; - // Cheap public "does this sector currently contain any multi" check. MultisVersion can't - // answer this (it counts enter AND leave, so a place-then-remove leaves it non-zero with - // zero multis). Used by the pathfinding step cache to route multi-covered cells to the - // live movement path instead of the static-only chunk cache. - public bool HasMultis => _multis is { Count: > 0 }; - - public int MultisVersion => _multisVersion; - - internal ref readonly ValueLinkList Mobiles => ref _mobiles; + internal ref ValueLinkList Mobiles => ref _mobiles; internal ref readonly ValueLinkList Items => ref _items; @@ -2012,17 +1504,12 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public void OnMultiEnter(BaseMulti multi) { - _multis ??= new List(); _multis.Add(multi); - _multisVersion++; } public void OnMultiLeave(BaseMulti multi) { - if (_multis?.Remove(multi) == true) - { - _multisVersion++; - } + _multis.Remove(multi); } public void Activate() diff --git a/Projects/Server/Maps/MapLoader.cs b/Projects/Server/Maps/MapLoader.cs index 850df2856..7a39eb880 100644 --- a/Projects/Server/Maps/MapLoader.cs +++ b/Projects/Server/Maps/MapLoader.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MapLoader.cs * * * diff --git a/Projects/Server/Maps/MapSelection.cs b/Projects/Server/Maps/MapSelection.cs index 01a7e0704..a0988c0a6 100644 --- a/Projects/Server/Maps/MapSelection.cs +++ b/Projects/Server/Maps/MapSelection.cs @@ -42,7 +42,7 @@ public static class MapSelection public static string ToCommaDelimitedString(this MapSelectionFlags flags) { - using var builder = new ValueStringBuilder(stackalloc char[160]); + using var builder = ValueStringBuilder.Create(); foreach (var flag in flags.GetEnumerable()) { @@ -88,7 +88,7 @@ public static class MapSelection [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { - var localList = _allMaps; + MapSelectionFlags[] localList = _allMaps; while ((uint)_index < (uint)localList.Length) { diff --git a/Projects/Server/Menus/BaseMenu.cs b/Projects/Server/Menus/BaseMenu.cs deleted file mode 100644 index 6a6c8f5b8..000000000 --- a/Projects/Server/Menus/BaseMenu.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Server.Network; - -namespace Server.Menus; - -public abstract class BaseMenu : IMenu -{ - private static int _nextSerial; - - public int Serial { get; } - - public abstract int EntryLength { get; } - - public BaseMenu() - { - var serial = ++_nextSerial; - if (serial <= 0) - { - serial = 1; - _nextSerial = 1; - } - - Serial = serial; - } - - public abstract void SendTo(NetState state); - - public virtual void OnCancel(NetState state) - { - } - - public virtual void OnResponse(NetState state, int index) - { - } -} diff --git a/Projects/Server/Menus/ItemListMenu.cs b/Projects/Server/Menus/ItemListMenu.cs index 9f71f7d7d..6c5422fa0 100644 --- a/Projects/Server/Menus/ItemListMenu.cs +++ b/Projects/Server/Menus/ItemListMenu.cs @@ -4,12 +4,11 @@ namespace Server.Menus.ItemLists; public class ItemListEntry { - public ItemListEntry(string name, int itemID, int hue = 0, int craftIndex = 0) + public ItemListEntry(string name, int itemID, int hue = 0) { Name = name?.Trim() ?? ""; ItemID = itemID; Hue = hue; - CraftIndex = craftIndex; } public string Name { get; } @@ -17,25 +16,43 @@ public class ItemListEntry public int ItemID { get; } public int Hue { get; } - - public int CraftIndex { get; } } -public class ItemListMenu : BaseMenu +public class ItemListMenu : IMenu { + private static int m_NextSerial; + public ItemListMenu(string question, ItemListEntry[] entries) { Question = question.Trim(); Entries = entries; + + do + { + Serial = m_NextSerial++; + Serial &= 0x7FFFFFFF; + } while (Serial == 0); + + Serial = (int)((uint)Serial | 0x80000000); } public string Question { get; } public ItemListEntry[] Entries { get; set; } - public override int EntryLength => Entries.Length; + public int Serial { get; } - public override void SendTo(NetState state) + public int EntryLength => Entries.Length; + + public virtual void OnCancel(NetState state) + { + } + + public virtual void OnResponse(NetState state, int index) + { + } + + public void SendTo(NetState state) { state.AddMenu(this); state.SendDisplayItemListMenu(this); diff --git a/Projects/Server/Menus/QuestionMenu.cs b/Projects/Server/Menus/QuestionMenu.cs index 7a52e0af1..a1a9acfec 100644 --- a/Projects/Server/Menus/QuestionMenu.cs +++ b/Projects/Server/Menus/QuestionMenu.cs @@ -2,21 +2,39 @@ using Server.Network; namespace Server.Menus.Questions; -public class QuestionMenu : BaseMenu +public class QuestionMenu : IMenu { + private static int m_NextSerial; + public QuestionMenu(string question, string[] answers) { Question = question?.Trim() ?? ""; Answers = answers; + + do + { + Serial = ++m_NextSerial; + Serial &= 0x7FFFFFFF; + } while (Serial == 0); } public string Question { get; } public string[] Answers { get; } - public override int EntryLength => Answers.Length; + public int Serial { get; } - public override void SendTo(NetState state) + public int EntryLength => Answers.Length; + + public virtual void OnCancel(NetState state) + { + } + + public virtual void OnResponse(NetState state, int index) + { + } + + public void SendTo(NetState state) { state.AddMenu(this); state.SendDisplayQuestionMenu(this); diff --git a/Projects/Server/MessageType.cs b/Projects/Server/MessageType.cs index 6a5ed6797..81bcfa539 100644 --- a/Projects/Server/MessageType.cs +++ b/Projects/Server/MessageType.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MessageType.cs * * * diff --git a/Projects/Server/Migrations/Server.Items.BankBox.v0.json b/Projects/Server/Migrations/Server.Items.BankBox.v0.json deleted file mode 100644 index 0d48d0bd9..000000000 --- a/Projects/Server/Migrations/Server.Items.BankBox.v0.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.BankBox", - "properties": [ - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Opened", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/Server/Migrations/Server.Items.SecureTradeContainer.v0.json b/Projects/Server/Migrations/Server.Items.SecureTradeContainer.v0.json deleted file mode 100644 index f219e9c92..000000000 --- a/Projects/Server/Migrations/Server.Items.SecureTradeContainer.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.SecureTradeContainer" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.BarkeeperRumor.v0.json b/Projects/Server/Migrations/Server.VirtualHairInfo.v0.json similarity index 63% rename from Projects/UOContent/Migrations/Server.Mobiles.BarkeeperRumor.v0.json rename to Projects/Server/Migrations/Server.VirtualHairInfo.v0.json index 4df4f77e1..1eaf27a0d 100644 --- a/Projects/UOContent/Migrations/Server.Mobiles.BarkeeperRumor.v0.json +++ b/Projects/Server/Migrations/Server.VirtualHairInfo.v0.json @@ -1,18 +1,18 @@ { "version": 0, - "type": "Server.Mobiles.BarkeeperRumor", + "type": "Server.VirtualHairInfo", "properties": [ { - "name": "Message", - "type": "string", + "name": "ItemId", + "type": "int", "rule": "PrimitiveTypeMigrationRule", "ruleArguments": [ "" ] }, { - "name": "Keyword", - "type": "string", + "name": "Hue", + "type": "int", "rule": "PrimitiveTypeMigrationRule", "ruleArguments": [ "" diff --git a/Projects/Server/Mobiles/Body.cs b/Projects/Server/Mobiles/Body.cs index 00138ecfe..12826ab63 100644 --- a/Projects/Server/Mobiles/Body.cs +++ b/Projects/Server/Mobiles/Body.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Body.cs * * * diff --git a/Projects/Server/Mobiles/IMount.cs b/Projects/Server/Mobiles/IMount.cs index 1c1407552..82f5b6932 100644 --- a/Projects/Server/Mobiles/IMount.cs +++ b/Projects/Server/Mobiles/IMount.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IMount.cs * * * diff --git a/Projects/Server/Mobiles/IVendor.cs b/Projects/Server/Mobiles/IVendor.cs index b04ff7cbe..6bcf409b4 100644 --- a/Projects/Server/Mobiles/IVendor.cs +++ b/Projects/Server/Mobiles/IVendor.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IVendor.cs * * * diff --git a/Projects/Server/Mobiles/Mobile.Enumerators.cs b/Projects/Server/Mobiles/Mobile.Enumerators.cs deleted file mode 100644 index 4ca864222..000000000 --- a/Projects/Server/Mobiles/Mobile.Enumerators.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace Server; - -public partial class Mobile -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ItemAtEnumerable GetItemsAt() => - m_Map == null ? Map.ItemAtEnumerable.Empty : m_Map.GetItemsAt(m_Location); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ItemAtEnumerable GetItemsAt() where T : Item => - m_Map == null ? Map.ItemAtEnumerable.Empty : m_Map.GetItemsAt(m_Location); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ItemBoundsEnumerable GetItemsInRange(int range) => GetItemsInRange(range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ItemBoundsEnumerable GetItemsInRange(int range) where T : Item => - m_Map == null ? Map.ItemBoundsEnumerable.Empty : m_Map.GetItemsInRange(m_Location, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.MobileAtEnumerable GetMobilesInRange() => GetMobilesInRange(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.MobileAtEnumerable GetMobilesInRange() where T : Mobile => - m_Map == null ? Map.MobileAtEnumerable.Empty : m_Map.GetMobilesAt(m_Location); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.MobileBoundsEnumerable GetMobilesInRange(int range) => GetMobilesInRange(range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.MobileBoundsEnumerable GetMobilesInRange(int range) where T : Mobile => - m_Map == null ? Map.MobileBoundsEnumerable.Empty : m_Map.GetMobilesInRange(m_Location, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ClientAtEnumerable GetClientsAt() => - m_Map == null ? Map.ClientAtEnumerable.Empty : Map.GetClientsAt(m_Location); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ClientBoundsEnumerable GetClientsInRange(int range) => - m_Map == null ? Map.ClientBoundsEnumerable.Empty : Map.GetClientsInRange(m_Location, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ItemDistanceEnumerable GetItemsInRangeByDistance(int range) => - GetItemsInRangeByDistance(range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ItemDistanceEnumerable GetItemsInRangeByDistance(int range) where T : Item => - m_Map == null ? default : m_Map.GetItemsInRangeByDistance(m_Location, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.MobileDistanceEnumerable GetMobilesInRangeByDistance(int range) => - GetMobilesInRangeByDistance(range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.MobileDistanceEnumerable GetMobilesInRangeByDistance(int range) where T : Mobile => - m_Map == null ? default : m_Map.GetMobilesInRangeByDistance(m_Location, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ClientDistanceEnumerable GetClientsInRangeByDistance(int range) => - m_Map == null ? default : m_Map.GetClientsInRangeByDistance(m_Location, range); -} diff --git a/Projects/Server/Mobiles/Mobile.Messages.cs b/Projects/Server/Mobiles/Mobile.Messages.cs deleted file mode 100644 index 29331c5c6..000000000 --- a/Projects/Server/Mobiles/Mobile.Messages.cs +++ /dev/null @@ -1,389 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Mobile.Messages.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using Server.Buffers; -using Server.Network; - -namespace Server; - -public partial class Mobile -{ - // ---------- Broadcast filter structs ---------- - - private readonly struct PublicVisibilityFilter : IBroadcastFilter - { - private readonly Mobile _source; - private readonly bool _noLineOfSight; - private readonly AccessLevel _accessLevel; - - public PublicVisibilityFilter(Mobile source, bool noLineOfSight, AccessLevel accessLevel) - { - _source = source; - _noLineOfSight = noLineOfSight; - _accessLevel = accessLevel; - } - - public bool Allow(NetState state) => - state.Mobile.AccessLevel >= _accessLevel && - state.Mobile.CanSee(_source) && - (_noLineOfSight || state.Mobile.InLOS(_source)); - } - - private readonly struct LocalizedVisibilityFilter : IBroadcastFilter - { - private readonly Mobile _source; - private readonly bool _noLineOfSight; - - public LocalizedVisibilityFilter(Mobile source, bool noLineOfSight) - { - _source = source; - _noLineOfSight = noLineOfSight; - } - - public bool Allow(NetState state) => state.Mobile.CanSee(_source) && (_noLineOfSight || state.Mobile.InLOS(_source)); - } - - private readonly struct NonlocalVisibilityFilter : IBroadcastFilter - { - private readonly Mobile _source; - private readonly NetState _exclude; - - public NonlocalVisibilityFilter(Mobile source, NetState exclude) - { - _source = source; - _exclude = exclude; - } - - public bool Allow(NetState state) => state != _exclude && state.Mobile.CanSee(_source); - } - - // ---------- Say / Emote / Whisper / Yell ---------- - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Say(bool ascii, ReadOnlySpan text) => - PublicOverheadMessage(MessageType.Regular, SpeechHue, ascii, text); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Say(ReadOnlySpan text) => - PublicOverheadMessage(MessageType.Regular, SpeechHue, false, text); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Say(int number, AffixType type, ReadOnlySpan affix, ReadOnlySpan args) => - PublicOverheadMessage(MessageType.Regular, SpeechHue, number, type, affix, args); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Say(int number, ReadOnlySpan args = default) => - PublicOverheadMessage(MessageType.Regular, SpeechHue, number, args); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Emote(ReadOnlySpan text) => - PublicOverheadMessage(MessageType.Emote, EmoteHue, false, text); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Emote(int number, ReadOnlySpan args = default) => - PublicOverheadMessage(MessageType.Emote, EmoteHue, number, args); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Whisper(ReadOnlySpan text) => - PublicOverheadMessage(MessageType.Whisper, WhisperHue, false, text); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Whisper(int number, ReadOnlySpan args = default) => - PublicOverheadMessage(MessageType.Whisper, WhisperHue, number, args); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Yell(ReadOnlySpan text) => - PublicOverheadMessage(MessageType.Yell, YellHue, false, text); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Yell(int number, ReadOnlySpan args = default) => - PublicOverheadMessage(MessageType.Yell, YellHue, number, args); - - // ---------- PublicOverheadMessage ---------- - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PublicOverheadMessage( - MessageType type, int hue, bool ascii, ReadOnlySpan text, bool noLineOfSight = true, - AccessLevel accessLevel = AccessLevel.Player - ) => OutgoingMessagePackets.BroadcastMessage( - m_Map, m_Location, - Serial, Body, type, hue, 3, ascii, Language, Name, text, - new PublicVisibilityFilter(this, noLineOfSight, accessLevel) - ); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PublicOverheadMessage( - MessageType type, int hue, int number, ReadOnlySpan args = default, bool noLineOfSight = true - ) => OutgoingMessagePackets.BroadcastMessageLocalized( - m_Map, m_Location, - Serial, Body, type, hue, 3, number, Name, args, - new LocalizedVisibilityFilter(this, noLineOfSight)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PublicOverheadMessage( - MessageType type, int hue, int number, AffixType affixType, ReadOnlySpan affix, - ReadOnlySpan args = default, bool noLineOfSight = false, - AccessLevel accessLevel = AccessLevel.Player - ) => OutgoingMessagePackets.BroadcastMessageLocalizedAffix( - m_Map, m_Location, - Serial, Body, type, hue, 3, number, Name, affixType, affix, args, - new PublicVisibilityFilter(this, noLineOfSight, accessLevel) - ); - - // ---------- PrivateOverheadMessage ---------- - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PrivateOverheadMessage(MessageType type, int hue, bool ascii, ReadOnlySpan text, NetState state) => - state.SendMessage(Serial, Body, type, hue, 3, ascii, m_Language, Name, text); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PrivateOverheadMessage(MessageType type, int hue, int number, NetState state) => - PrivateOverheadMessage(type, hue, number, default, state); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PrivateOverheadMessage(MessageType type, int hue, int number, ReadOnlySpan args, NetState state) => - state.SendMessageLocalized(Serial, Body, type, hue, 3, number, Name, args); - - // ---------- LocalOverheadMessage ---------- - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void LocalOverheadMessage(MessageType type, int hue, bool ascii, ReadOnlySpan text) => - m_NetState.SendMessage(Serial, Body, type, hue, 3, ascii, m_Language, Name, text); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void LocalOverheadMessage(MessageType type, int hue, int number, ReadOnlySpan args = default) => - m_NetState.SendMessageLocalized(Serial, Body, type, hue, 3, number, Name, args); - - // ---------- NonlocalOverheadMessage ---------- - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void NonlocalOverheadMessage(MessageType type, int hue, int number, ReadOnlySpan args = default) => - OutgoingMessagePackets.BroadcastMessageLocalized( - m_Map, m_Location, - Serial, Body, type, hue, 3, number, Name, args, - new NonlocalVisibilityFilter(this, m_NetState) - ); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void NonlocalOverheadMessage(MessageType type, int hue, bool ascii, ReadOnlySpan text) => - OutgoingMessagePackets.BroadcastMessage( - m_Map, m_Location, - Serial, Body, type, hue, 3, ascii, Language, Name, text, - new NonlocalVisibilityFilter(this, m_NetState) - ); - - // ---------- SendLocalizedMessage / SendMessage / SendAsciiMessage ---------- - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SendLocalizedMessage(int number, ReadOnlySpan args = default, int hue = 0x3B2) => - m_NetState.SendMessageLocalized(Serial.MinusOne, -1, MessageType.Regular, hue, 3, number, "System", args); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SendLocalizedMessage( - int number, bool append, ReadOnlySpan affix, ReadOnlySpan args = default, int hue = 0x3B2 - ) => m_NetState.SendMessageLocalizedAffix( - Serial.MinusOne, - -1, - MessageType.Regular, - hue, - 3, - number, - "System", - (append ? AffixType.Append : AffixType.Prepend) | AffixType.System, - affix, - args - ); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SendMessage(ReadOnlySpan text) => SendMessage(0x3B2, text); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SendMessage(int hue, ReadOnlySpan text) => - m_NetState.SendMessage(Serial.MinusOne, -1, MessageType.Regular, hue, 3, false, "ENU", "System", text); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SendAsciiMessage(ReadOnlySpan text) => SendAsciiMessage(0x3B2, text); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SendAsciiMessage(int hue, ReadOnlySpan text) => - m_NetState.SendMessage(Serial.MinusOne, -1, MessageType.Regular, hue, 3, true, null, "System", text); - - // ---------- Interpolated handler overloads ---------- - - public void Say(bool ascii, ref RawInterpolatedStringHandler text) - { - PublicOverheadMessage(MessageType.Regular, SpeechHue, ascii, text.Text); - text.Clear(); - } - - public void Say(ref RawInterpolatedStringHandler text) - { - PublicOverheadMessage(MessageType.Regular, SpeechHue, false, text.Text); - text.Clear(); - } - - public void Say(int number, AffixType type, ReadOnlySpan affix, ref RawInterpolatedStringHandler args) - { - PublicOverheadMessage(MessageType.Regular, SpeechHue, number, type, affix, args.Text); - args.Clear(); - } - - public void Say(int number, ref RawInterpolatedStringHandler args) - { - PublicOverheadMessage(MessageType.Regular, SpeechHue, number, args.Text); - args.Clear(); - } - - public void Emote(ref RawInterpolatedStringHandler text) - { - PublicOverheadMessage(MessageType.Emote, EmoteHue, false, text.Text); - text.Clear(); - } - - public void Emote(int number, ref RawInterpolatedStringHandler args) - { - PublicOverheadMessage(MessageType.Emote, EmoteHue, number, args.Text); - args.Clear(); - } - - public void Whisper(ref RawInterpolatedStringHandler text) - { - PublicOverheadMessage(MessageType.Whisper, WhisperHue, false, text.Text); - text.Clear(); - } - - public void Whisper(int number, ref RawInterpolatedStringHandler args) - { - PublicOverheadMessage(MessageType.Whisper, WhisperHue, number, args.Text); - args.Clear(); - } - - public void Yell(ref RawInterpolatedStringHandler text) - { - PublicOverheadMessage(MessageType.Yell, YellHue, false, text.Text); - text.Clear(); - } - - public void Yell(int number, ref RawInterpolatedStringHandler args) - { - PublicOverheadMessage(MessageType.Yell, YellHue, number, args.Text); - args.Clear(); - } - - public void PublicOverheadMessage( - MessageType type, int hue, bool ascii, ref RawInterpolatedStringHandler text, bool noLineOfSight = true, - AccessLevel accessLevel = AccessLevel.Player - ) - { - PublicOverheadMessage(type, hue, ascii, text.Text, noLineOfSight, accessLevel); - text.Clear(); - } - - public void PublicOverheadMessage(MessageType type, int hue, int number, ref RawInterpolatedStringHandler args, bool noLineOfSight = true) - { - PublicOverheadMessage(type, hue, number, args.Text, noLineOfSight); - args.Clear(); - } - - public void PublicOverheadMessage( - MessageType type, int hue, int number, AffixType affixType, ReadOnlySpan affix, - ref RawInterpolatedStringHandler args, bool noLineOfSight = false, - AccessLevel accessLevel = AccessLevel.Player - ) - { - PublicOverheadMessage(type, hue, number, affixType, affix, args.Text, noLineOfSight, accessLevel); - args.Clear(); - } - - public void PrivateOverheadMessage( - MessageType type, int hue, bool ascii, ref RawInterpolatedStringHandler text, NetState state - ) - { - PrivateOverheadMessage(type, hue, ascii, text.Text, state); - text.Clear(); - } - - public void PrivateOverheadMessage( - MessageType type, int hue, int number, ref RawInterpolatedStringHandler args, NetState state - ) - { - PrivateOverheadMessage(type, hue, number, args.Text, state); - args.Clear(); - } - - public void LocalOverheadMessage(MessageType type, int hue, bool ascii, ref RawInterpolatedStringHandler text) - { - LocalOverheadMessage(type, hue, ascii, text.Text); - text.Clear(); - } - - public void LocalOverheadMessage(MessageType type, int hue, int number, ref RawInterpolatedStringHandler args) - { - LocalOverheadMessage(type, hue, number, args.Text); - args.Clear(); - } - - public void NonlocalOverheadMessage(MessageType type, int hue, int number, ref RawInterpolatedStringHandler args) - { - NonlocalOverheadMessage(type, hue, number, args.Text); - args.Clear(); - } - - public void NonlocalOverheadMessage(MessageType type, int hue, bool ascii, ref RawInterpolatedStringHandler text) - { - NonlocalOverheadMessage(type, hue, ascii, text.Text); - text.Clear(); - } - - public void SendLocalizedMessage(int number, ref RawInterpolatedStringHandler args, int hue = 0x3B2) - { - SendLocalizedMessage(number, args.Text, hue); - args.Clear(); - } - - public void SendLocalizedMessage( - int number, bool append, ReadOnlySpan affix, ref RawInterpolatedStringHandler args, int hue = 0x3B2 - ) - { - SendLocalizedMessage(number, append, affix, args.Text, hue); - args.Clear(); - } - - public void SendMessage(ref RawInterpolatedStringHandler text) - { - SendMessage(0x3B2, text.Text); - text.Clear(); - } - - public void SendMessage(int hue, ref RawInterpolatedStringHandler text) - { - SendMessage(hue, text.Text); - text.Clear(); - } - - public void SendAsciiMessage(ref RawInterpolatedStringHandler text) - { - SendAsciiMessage(0x3B2, text.Text); - text.Clear(); - } - - public void SendAsciiMessage(int hue, ref RawInterpolatedStringHandler text) - { - SendAsciiMessage(hue, text.Text); - text.Clear(); - } -} diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index fb028b9d7..d6b9eb9a2 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Mobile.cs * * * @@ -25,11 +25,10 @@ using Server.Mobiles; using Server.Network; using Server.Prompts; using Server.Targeting; +using Server.Text; using System; -using System.Diagnostics; using System.Collections.Generic; using System.Runtime.CompilerServices; -using Server.Buffers; using CalcMoves = Server.Movement.Movement; namespace Server; @@ -273,9 +272,8 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro private TimerExecutionToken _expireAggrTimerToken; private TimerExecutionToken _expireCombatantTimerToken; private TimerExecutionToken _expireCriminalTimerToken; - private int _facialHairItemId; - private int _facialHairHue; - private Serial _facialHairSerial; + private VirtualHairInfo _facialHair; + public VirtualHairInfo FacialHair => _facialHair ??= new VirtualHairInfo(FacialHairItemID, FacialHairHue); private int m_Fame, m_Karma; private bool m_Female, m_Warmode, m_Hidden, m_Blessed, m_Flying; @@ -285,9 +283,8 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro private BaseGuild m_Guild; private string m_GuildTitle; - private int _hairItemId; - private int _hairHue; - private Serial _hairSerial; + private VirtualHairInfo _hair; + public VirtualHairInfo Hair => _hair ??= new VirtualHairInfo(HairItemID, HairHue); private int m_Hits, m_Stam, m_Mana; @@ -296,7 +293,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro private int m_HueMod = -1; private int m_Hunger; - private int _thirst; private bool m_InDeltaQueue; private int m_Kills; @@ -517,20 +513,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro } [CommandProperty(AccessLevel.GameMaster)] - public int Thirst - { - get => _thirst; - set - { - var oldValue = _thirst; - - if (oldValue != value) - { - _thirst = value; - OnThirstChanged(oldValue); - } - } - } + public int Thirst { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int BAC { get; set; } @@ -678,7 +661,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro } } - [CommandProperty(AccessLevel.Administrator)] public long NextActionTime { get; set; } public long NextActionMessage { get; set; } @@ -693,7 +675,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro public virtual bool CanRegenStam => Alive; public virtual bool CanRegenMana => Alive; - [CommandProperty(AccessLevel.Administrator)] public long NextSkillTime { get; set; } public List Aggressors { get; private set; } @@ -914,7 +895,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro [CommandProperty(AccessLevel.Administrator)] public bool AutoPageNotify { get; set; } - [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator, canModify: true)] + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Owner)] public IAccount Account { get; set; } [CommandProperty(AccessLevel.GameMaster)] @@ -954,6 +935,8 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro public static TimeSpan AutoManifestTimeout { get; set; } = TimeSpan.FromSeconds(5.0); + public static bool InsuranceEnabled { get; set; } + public static int ActionDelay { get; set; } = 500; public static VisibleDamageType VisibleDamageType { get; set; } @@ -1246,7 +1229,12 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro { get { - return m_NetState is { IsConnected: true } ? m_NetState : null; + if (m_NetState?.Connection == null) + { + m_NetState = null; + } + + return m_NetState; } set { @@ -1643,7 +1631,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro { m_Kills = Math.Max(value, 0); - if (oldValue >= 5 != Murderer) + if (oldValue >= 5 != m_Kills >= 5) { Delta(MobileDelta.Noto); InvalidateProperties(); @@ -1654,8 +1642,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro } } - public virtual bool Murderer => Kills >= 5; - [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] public virtual bool Criminal { @@ -2182,16 +2168,20 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro [CommandProperty(AccessLevel.GameMaster)] public int HairItemID { - get => _hairItemId; + get => _hair?.ItemId ?? 0; set { - // An item id of 0 is the "no hair" state; clear the hue with it, but keep - // _hairSerial so a pending removal packet references the equipped serial. - _hairItemId = value < 0 ? 0 : value; - - if (_hairItemId == 0) + if (_hair == null && value > 0) { - _hairHue = 0; + _hair = new VirtualHairInfo(value); + } + else if (value <= 0) + { + _hair = null; + } + else if (_hair != null) + { + _hair.ItemId = value; } Delta(MobileDelta.Hair); @@ -2202,14 +2192,20 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro [CommandProperty(AccessLevel.GameMaster)] public int FacialHairItemID { - get => _facialHairItemId; + get => _facialHair?.ItemId ?? 0; set { - _facialHairItemId = value < 0 ? 0 : value; - - if (_facialHairItemId == 0) + if (_facialHair == null && value > 0) { - _facialHairHue = 0; + _facialHair = new VirtualHairInfo(value); + } + else if (value <= 0) + { + _facialHair = null; + } + else if (_facialHair != null) + { + _facialHair.ItemId = value; } Delta(MobileDelta.FacialHair); @@ -2220,12 +2216,12 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro [CommandProperty(AccessLevel.GameMaster)] public int HairHue { - get => _hairHue; + get => _hair?.Hue ?? 0; set { - if (_hairItemId > 0) + if (_hair != null) { - _hairHue = value; + _hair.Hue = value; Delta(MobileDelta.Hair); this.MarkDirty(); } @@ -2235,44 +2231,18 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro [CommandProperty(AccessLevel.GameMaster)] public int FacialHairHue { - get => _facialHairHue; + get => _facialHair?.Hue ?? 0; set { - if (_facialHairItemId > 0) + if (_facialHair != null) { - _facialHairHue = value; + _facialHair.Hue = value; Delta(MobileDelta.FacialHair); this.MarkDirty(); } } } - public Serial HairSerial - { - get - { - if (_hairItemId > 0 && _hairSerial == Serial.Zero) - { - _hairSerial = World.NewVirtual; - } - - return _hairSerial; - } - } - - public Serial FacialHairSerial - { - get - { - if (_facialHairItemId > 0 && _facialHairSerial == Serial.Zero) - { - _facialHairSerial = World.NewVirtual; - } - - return _facialHairSerial; - } - } - public Item ShieldArmor => FindItemOnLayer(Layer.TwoHanded); public Item NeckArmor => FindItemOnLayer(Layer.Neck); @@ -2292,22 +2262,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro public int CompareTo(Mobile other) => other == null ? -1 : Serial.CompareTo(other.Serial); public virtual int HuedItemID => m_Female ? 0x2107 : 0x2106; - public ObjectPropertyList PropertyList - { - get - { - if (m_PropertyList == null) - { - // Publish the list before building it so a nested InvalidateProperties can see the - // build in progress and defer instead of recursing into a second throwaway list. - var list = new ObjectPropertyList(this); - m_PropertyList = list; - InitializePropertyList(list); - } - - return m_PropertyList; - } - } + public ObjectPropertyList PropertyList => m_PropertyList ??= InitializePropertyList(new ObjectPropertyList(this)); public virtual void GetProperties(IPropertyList list) { @@ -2322,22 +2277,26 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro [CommandProperty(AccessLevel.Counselor)] public Serial Serial { get; } + public byte SerializedThread { get; set; } + public int SerializedPosition { get; set; } + public int SerializedLength { get; set; } + public virtual void Serialize(IGenericWriter writer) { - writer.Write(38); // version + writer.Write(36); // version - writer.WriteAnchoredTime(LastStrGain); - writer.WriteAnchoredTime(LastIntGain); - writer.WriteAnchoredTime(LastDexGain); + writer.WriteDeltaTime(LastStrGain); + writer.WriteDeltaTime(LastIntGain); + writer.WriteDeltaTime(LastDexGain); byte hairflag = 0x00; - if (_hairItemId > 0) + if (_hair != null) { hairflag |= 0x01; } - if (_facialHairItemId > 0) + if (_facialHair != null) { hairflag |= 0x02; } @@ -2346,14 +2305,12 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro if ((hairflag & 0x01) != 0) { - writer.Write(_hairItemId); - writer.Write(_hairHue); + _hair?.Serialize(writer); } if ((hairflag & 0x02) != 0) { - writer.Write(_facialHairItemId); - writer.Write(_facialHairHue); + _facialHair?.Serialize(writer); } writer.Write(Race); @@ -2488,6 +2445,8 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro m_Map?.OnLeave(this); m_Map = null; + _hair = null; + _facialHair = null; m_MountItem = null; World.RemoveEntity(this); @@ -2497,8 +2456,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro m_PropertyList = null; } - public virtual bool SkipSerialization => false; - [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] public Map Map { @@ -2550,7 +2507,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro if (ns != null) { - ns.ResetMovementState(); + ns.Sequence = 0; if (m_Map != null) { @@ -2762,23 +2719,24 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro sendFacialHair = true; } - var hairSerial = HairSerial; + var hairSerial = Hair.VirtualSerial; var hairLength = removeHair ? OutgoingVirtualHairPackets.RemovePacketLength : OutgoingVirtualHairPackets.EquipUpdatePacketLength; - var hairPacket = stackalloc byte[hairLength].InitializePacket(); + Span hairPacket = stackalloc byte[hairLength].InitializePacket(); - var facialHairSerial = FacialHairSerial; + var facialHairSerial = FacialHair.VirtualSerial; var facialHairLength = removeFacialHair ? OutgoingVirtualHairPackets.RemovePacketLength : OutgoingVirtualHairPackets.EquipUpdatePacketLength; - var facialHairPacket = stackalloc byte[facialHairLength].InitializePacket(); + Span facialHairPacket = stackalloc byte[facialHairLength].InitializePacket(); const int cacheLength = OutgoingMobilePackets.MobileMovingPacketCacheByteLength; - var mobileMovingCache = stackalloc byte[cacheLength].InitializePacket(); + Span mobileMovingCache = stackalloc byte[cacheLength]; + mobileMovingCache.Clear(); var ourState = m_NetState; @@ -2786,7 +2744,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro { if (sendUpdate) { - ourState.ResetMovementState(); + ourState.Sequence = 0; ourState.SendMobileUpdate(this); } @@ -2920,14 +2878,13 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var statBufferTrue = stackalloc byte[OutgoingMobilePackets.MobileStatusCompactLength].InitializePacket(); - var statBufferFalse = stackalloc byte[OutgoingMobilePackets.MobileStatusCompactLength].InitializePacket(); - var hbpBuffer = stackalloc byte[OutgoingMobilePackets.MobileHealthbarPacketLength].InitializePacket(); - var hbyBuffer = stackalloc byte[OutgoingMobilePackets.MobileHealthbarPacketLength].InitializePacket(); - var deadBuffer = stackalloc byte[OutgoingMobilePackets.BondedStatusPacketLength].InitializePacket(); - var removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); - var hitsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributePacketLength].InitializePacket(); - mobileMovingCache.InitializePacket(); + Span statBufferTrue = stackalloc byte[OutgoingMobilePackets.MobileStatusCompactLength].InitializePacket(); + Span statBufferFalse = stackalloc byte[OutgoingMobilePackets.MobileStatusCompactLength].InitializePacket(); + Span hbpBuffer = stackalloc byte[OutgoingMobilePackets.MobileHealthbarPacketLength].InitializePacket(); + Span hbyBuffer = stackalloc byte[OutgoingMobilePackets.MobileHealthbarPacketLength].InitializePacket(); + Span deadBuffer = stackalloc byte[OutgoingMobilePackets.BondedStatusPacketLength].InitializePacket(); + Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); + Span hitsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributePacketLength].InitializePacket(); foreach (var state in Map.GetClientsInRange(m_Location)) { @@ -3329,7 +3286,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro var hasTitle = PropertyTitle && !string.IsNullOrEmpty(Title); var hasGuild = guild != null && (m_Player || m_DisplayGuildTitle); - var suffix = hasTitle switch + string suffix = hasTitle switch { true when hasGuild => $" {Title} [{guild.Abbreviation.FixHtmlFormattable()}]", true => $" {Title}", @@ -3764,12 +3721,36 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro { } - /// - /// Overridable. Virtual event invoked after the property has changed. - /// - /// - public virtual void OnThirstChanged(int oldValue) + public double GetDistanceToSqrt(Point3D p) { + var xDelta = m_Location.m_X - p.m_X; + var yDelta = m_Location.m_Y - p.m_Y; + + return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); + } + + public double GetDistanceToSqrt(Mobile m) + { + var xDelta = m_Location.m_X - m.m_Location.m_X; + var yDelta = m_Location.m_Y - m.m_Location.m_Y; + + return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); + } + + public double GetDistanceToSqrt(Point2D p) + { + var xDelta = m_Location.m_X - p.X; + var yDelta = m_Location.m_Y - p.Y; + + return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); + } + + public double GetDistanceToSqrt(IPoint2D p) + { + var xDelta = m_Location.m_X - p.X; + var yDelta = m_Location.m_Y - p.Y; + + return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); } public virtual void AggressiveAction(Mobile aggressor) => AggressiveAction(aggressor, false); @@ -4337,32 +4318,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro if (m_NetState != null) { - var cost = ComputeMovementSpeed(d); - var now = Core.TickCount; - var delta = now - m_NetState._nextMovementTime; - - // Cap drift to prevent banking "lateness" for speed hacking later. - // maxDrift should match the credit buffer so the systems are symmetric. - const int maxDrift = 200; - - if (cost == 0) - { - // Direction-only turn: reset to now to prevent turn accumulation - m_NetState._nextMovementTime = now; - } - else - { - // Clamp _nextMovementTime so it's never more than maxDrift behind now. - // This limits how much "lateness" can be banked. - if (delta > maxDrift) - { - m_NetState._nextMovementTime = now - maxDrift; - } - - // Accumulative timing: add cost to current baseline - m_NetState._nextMovementTime += cost; - } - + m_NetState._nextMovementTime += ComputeMovementSpeed(d); m_NetState.SendMovementAck(m_NetState.Sequence, this); } @@ -4402,7 +4358,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro const int cacheLength = OutgoingMobilePackets.MobileMovingPacketCacheByteLength; Span mobileMovingCache = stackalloc byte[cacheLength]; - mobileMovingCache.InitializePacket(); + mobileMovingCache.Clear(); while (moveClientQueue.Count > 0) { @@ -4617,13 +4573,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro } } -#if TRACK_LEAKS - ~Mobile() - { - EntityFinalizationTracker.NotifyFinalized(this); - } -#endif - /// /// Overridable. Virtual event invoked before the Mobile is deleted. /// @@ -4833,8 +4782,8 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro { var corpseSerial = c?.Serial ?? Serial.Zero; - var deathAnimation = stackalloc byte[OutgoingMobilePackets.DeathAnimationPacketLength].InitializePacket(); - var removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); + Span deathAnimation = stackalloc byte[OutgoingMobilePackets.DeathAnimationPacketLength].InitializePacket(); + Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); foreach (var state in m_Map.GetClientsInRange(m_Location)) { @@ -5123,7 +5072,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro { var rootItem = root as Item; - var buffer = stackalloc byte[OutgoingPlayerPackets.DragEffectPacketLength] + Span buffer = stackalloc byte[OutgoingPlayerPackets.DragEffectPacketLength] .InitializePacket(); foreach (var ns in map.GetClientsInRange(from.Location)) @@ -5197,18 +5146,14 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro } else if (item.Parent is Mobile) { - // Don't re-show a duped-layer item on its own (see Item.IsDupedEquipLayer). - if (!item.IsDupedEquipLayer()) - { - state.SendEquipUpdate(item); - } + state.SendEquipUpdate(item); } else { item.SendInfoTo(state); } - if (item.Parent != null && !item.IsDupedEquipLayer()) + if (item.Parent != null) { item.SendOPLPacketTo(state); } @@ -5248,15 +5193,8 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro item.Name = oldItem.Name; item.Weight = oldItem.Weight; - item.PlayerConstructed = oldItem.PlayerConstructed; item.Amount = oldAmount - amount; - - // A parented remainder gets its map from AddItem (parent first, then map), keeping the - // split off the decay scheduler; a ground remainder is placed and enrolled here. - if (oldItem.Parent == null) - { - item.Map = oldItem.Map; - } + item.Map = oldItem.Map; oldItem.OnAfterDuped(item); @@ -5290,7 +5228,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var buffer = stackalloc byte[OutgoingPlayerPackets.DragEffectPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingPlayerPackets.DragEffectPacketLength].InitializePacket(); foreach (var ns in map.GetClientsInRange(m_Location)) { @@ -5433,32 +5371,13 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return false; } - ReadOnlySpan ghostChars = (GhostChars ?? DefaultGhostChars).AsSpan(); - - var length = text.Length; - char[] rentedChars = null; - var chars = length <= 256 - ? stackalloc char[length] - : rentedChars = STArrayPool.Shared.Rent(length); - - try + using var sb = new ValueStringBuilder(stackalloc char[Math.Min(text.Length, 256)]); + for (var i = 0; i < text.Length; ++i) { - var textSpan = text.AsSpan(); - for (var i = 0; i < textSpan.Length; ++i) - { - chars[i] = textSpan[i] != ' ' ? ghostChars.RandomElement() : ' '; - } - - text = new string(chars[..length]); - } - finally - { - if (rentedChars != null) - { - STArrayPool.Shared.Return(rentedChars); - } + sb.Append(text[i] != ' ' ? (GhostChars ?? DefaultGhostChars).RandomElement() : ' '); } + text = sb.ToString(); context = m_GhostMutateContext; return true; } @@ -5499,7 +5418,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro public virtual bool CheckHearsMutatedSpeech(Mobile m, object context) => context != m_GhostMutateContext || m.Alive && !m.CanHearGhosts; - private static void AddSpeechItemsFrom(List list, Container cont) + private void AddSpeechItemsFrom(List list, Container cont) { for (var i = 0; i < cont.Items.Count; ++i) { @@ -5551,12 +5470,33 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro break; } case MessageType.System: + { + break; + } case MessageType.Label: + { + break; + } case MessageType.Focus: + { + break; + } case MessageType.Spell: + { + break; + } case MessageType.Guild: + { + break; + } case MessageType.Alliance: + { + break; + } case MessageType.Command: + { + break; + } case MessageType.Encoded: { break; @@ -5650,9 +5590,9 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro ProcessDelta(); - var regBuffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text.Length)].InitializePacket(); - var mutBuffer = - stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(mutatedText.Length)].InitializePacket(); + Span regBuffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); + Span mutBuffer = + stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(mutatedText)].InitializePacket(); // TODO: Should this be sorted like onSpeech is below? for (var i = 0; i < hears.Count; ++i) @@ -6150,8 +6090,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro switch (version) { - case 38: // Stat-gain stamps moved from delta time to anchored time - case 37: // Decomposed hair into inline item id/hue (dropped the VirtualHairInfo object) case 36: // Moved virtues to VirtueSystem case 35: // Moved short term murders to PlayerMurderSystem case 34: // Moved Stabled to PlayerMobile @@ -6159,47 +6097,26 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro case 32: // Removed StuckMenu case 31: { - if (version >= 38) - { - LastStrGain = reader.ReadAnchoredTime(); - LastIntGain = reader.ReadAnchoredTime(); - LastDexGain = reader.ReadAnchoredTime(); - } - else - { - LastStrGain = reader.ReadDeltaTime(); - LastIntGain = reader.ReadDeltaTime(); - LastDexGain = reader.ReadDeltaTime(); - } + LastStrGain = reader.ReadDeltaTime(); + LastIntGain = reader.ReadDeltaTime(); + LastDexGain = reader.ReadDeltaTime(); goto case 30; } case 30: { - // Before v37 each hair was a VirtualHairInfo whose Serialize wrote a - // leading version int ahead of the item id and hue. var hairflag = reader.ReadByte(); if ((hairflag & 0x01) != 0) { - if (version < 37) - { - reader.ReadInt(); // legacy VirtualHairInfo version - } - - _hairItemId = reader.ReadInt(); - _hairHue = reader.ReadInt(); + _hair = new VirtualHairInfo(); + _hair.Deserialize(reader); } if ((hairflag & 0x02) != 0) { - if (version < 37) - { - reader.ReadInt(); // legacy VirtualHairInfo version - } - - _facialHairItemId = reader.ReadInt(); - _facialHairHue = reader.ReadInt(); + _facialHair = new VirtualHairInfo(); + _facialHair.Deserialize(reader); } goto case 29; @@ -6766,7 +6683,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro ProcessDelta(); - var buffer = stackalloc byte[OutgoingMobilePackets.MobileAnimationPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingMobilePackets.MobileAnimationPacketLength].InitializePacket(); foreach (var state in map.GetClientsInRange(m_Location)) { @@ -6843,7 +6760,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); foreach (var state in m_Map.GetClientsInRange(m_Location)) { @@ -6893,7 +6810,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); + Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); foreach (var state in m_Map.GetClientsInRange(m_Location)) { @@ -7071,9 +6988,12 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro flags |= 0x04; } } - else if (m_Poison != null) + else { - flags |= 0x04; + if (m_Poison != null) + { + flags |= 0x04; + } } if (m_Blessed || m_YellowHealthbar) @@ -7123,7 +7043,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - var removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); + Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); foreach (var state in m_Map.GetClientsInRange(m_Location)) { @@ -7185,16 +7105,22 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return false; } - if (item.Parent is Item parent) + if (item.Parent != null) { - if (!(CanSee(parent) && parent.IsChildVisibleTo(this, item))) + if (item.Parent is Item parent) { - return false; + if (!(CanSee(parent) && parent.IsChildVisibleTo(this, item))) + { + return false; + } + } + else if (item.Parent is Mobile mobile) + { + if (!CanSee(mobile)) + { + return false; + } } - } - else if (item.Parent is Mobile mobile && !CanSee(mobile)) - { - return false; } if (item is BankBox box && m_AccessLevel <= AccessLevel.Counselor && (box.Owner != this || !box.Opened)) @@ -7258,18 +7184,8 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro private ObjectPropertyList InitializePropertyList(ObjectPropertyList list) { - list.IsBuilding = true; - - try - { - GetProperties(list); - list.Terminate(); - } - finally - { - list.IsBuilding = false; - } - + GetProperties(list); + list.Terminate(); return list; } @@ -7286,26 +7202,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return; } - // Always a bug in the property getter, and there is no correct recovery: refuse rather than - // hide it. RELEASE keeps a possibly stale tooltip, DEBUG throws. - // See dev-docs/property-lists.md "Never Invalidate From Inside GetProperties". - if (m_PropertyList?.IsBuilding == true) - { - logger.Error( - "{Entity} called InvalidateProperties() while its property list was being built. Remove the side effect from the property getter, or defer it with Timer.DelayCall.\n{StackTrace}", - this, - new StackTrace() - ); - -#if DEBUG - throw new InvalidOperationException( - $"{this} invalidated its property list from inside GetProperties. Remove the side effect from the property getter." - ); -#else - return; -#endif - } - if (m_Map != null && m_Map != Map.Internal && !World.Loading) { int? oldHash; @@ -7366,7 +7262,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro if (isTeleport && (!m_NetState.HighSeas || !NoMoveHS)) { - m_NetState.ResetMovementState(); + m_NetState.Sequence = 0; m_NetState.SendMobileUpdate(this); } } @@ -7376,7 +7272,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro if (map != null) { // First, send a remove message to everyone who can no longer see us. (inOldRange && !inNewRange) - var removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); + Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); foreach (var ns in map.GetClientsInRange(oldLocation)) { @@ -7606,12 +7502,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro public virtual bool OpenTrade(Mobile from, Item offer = null) { - if (!ServerFeatureFlags.PlayerTrading) - { - from.SendMessage(0x22, "Player trading is temporarily disabled."); - return false; - } - if (!from.Player || !Player || !from.Alive || !Alive) { return false; @@ -7851,12 +7741,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro } } - /// - /// True when deltas remain queued after a pass, which is - /// bounded by the count it saw on entry. The event loop consults this before sleeping. - /// - public static bool HasQueuedDeltas => m_DeltaQueue.Count > 0; - public static void ProcessDeltaQueue() { var limit = m_DeltaQueue.Count; @@ -7907,7 +7791,10 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return false; } - public void LaunchBrowser(ReadOnlySpan url) => m_NetState?.LaunchBrowser(url); + public void LaunchBrowser(string url) + { + m_NetState?.LaunchBrowser(url); + } public void InitStats(int str, int dex, int intel) { @@ -8006,26 +7893,14 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro type = ""; } - if (title.Length <= 0) - { - PrivateOverheadMessage( - MessageType.Regular, - SpeechHue, - true, - $"[{guild.Abbreviation}]{type}", - from.NetState - ); - } - else - { - PrivateOverheadMessage( - MessageType.Regular, - SpeechHue, - true, - $"[{title}, {guild.Abbreviation}]{type}", - from.NetState - ); - } + var text = string.Format( + title.Length <= 0 ? "[{1}]{2}" : "[{0}, {1}]{2}", + title, + guild.Abbreviation, + type + ); + + PrivateOverheadMessage(MessageType.Regular, SpeechHue, true, text, from.NetState); } } @@ -8119,7 +7994,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro public static TimeSpan GetManaRegenRate(Mobile m) => ManaRegenRateHandler?.Invoke(m) ?? DefaultManaRate; - public static readonly char[] DefaultGhostChars = ['o', 'O']; + public static char[] DefaultGhostChars = { 'o', 'O' }; public Prompt BeginPrompt(PromptCallback callback, PromptCallback cancelCallback) => Prompt = new SimplePrompt(callback, cancelCallback); @@ -8191,6 +8066,43 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return -1; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ItemAtEnumerable GetItemsAt() => + m_Map == null ? Map.ItemAtEnumerable.Empty : m_Map.GetItemsAt(m_Location); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ItemAtEnumerable GetItemsAt() where T : Item => + m_Map == null ? Map.ItemAtEnumerable.Empty : m_Map.GetItemsAt(m_Location); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ItemBoundsEnumerable GetItemsInRange(int range) => GetItemsInRange(range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ItemBoundsEnumerable GetItemsInRange(int range) where T : Item => + m_Map == null ? Map.ItemBoundsEnumerable.Empty : m_Map.GetItemsInRange(m_Location, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.MobileAtEnumerable GetMobilesInRange() => GetMobilesInRange(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.MobileAtEnumerable GetMobilesInRange() where T : Mobile => + m_Map == null ? Map.MobileAtEnumerable.Empty : m_Map.GetMobilesAt(m_Location); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.MobileBoundsEnumerable GetMobilesInRange(int range) => GetMobilesInRange(range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.MobileBoundsEnumerable GetMobilesInRange(int range) where T : Mobile => + m_Map == null ? Map.MobileBoundsEnumerable.Empty : m_Map.GetMobilesInRange(m_Location, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ClientAtEnumerable GetClientsAt() => + m_Map == null ? Map.ClientAtEnumerable.Empty : Map.GetClientsAt(m_Location); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ClientBoundsEnumerable GetClientsInRange(int range) => + m_Map == null ? Map.ClientBoundsEnumerable.Empty : Map.GetClientsInRange(m_Location, range); + public void SayTo(Mobile to, bool ascii, string text) => PrivateOverheadMessage(MessageType.Regular, SpeechHue, ascii, text, to.NetState); @@ -8199,8 +8111,28 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro public void SayTo(Mobile to, int number, string args = "") => to.NetState.SendMessageLocalized(Serial, Body, MessageType.Regular, SpeechHue, 3, number, Name, args); - public void SayTo(Mobile to, int number, int hue, string args = "") => - to.NetState.SendMessageLocalized(Serial, Body, MessageType.Regular, hue, 3, number, Name, args); + public void Say(bool ascii, string text) => PublicOverheadMessage(MessageType.Regular, SpeechHue, ascii, text); + + public void Say(string text) => PublicOverheadMessage(MessageType.Regular, SpeechHue, false, text); + + public void Say(int number, AffixType type, string affix, string args) => + PublicOverheadMessage(MessageType.Regular, SpeechHue, number, type, affix, args); + + public void Say(int number, string args = "") => PublicOverheadMessage(MessageType.Regular, SpeechHue, number, args); + + public void Emote(string text) => PublicOverheadMessage(MessageType.Emote, EmoteHue, false, text); + + public void Emote(int number, string args = "") => PublicOverheadMessage(MessageType.Emote, EmoteHue, number, args); + + public void Whisper(string text) => PublicOverheadMessage(MessageType.Whisper, WhisperHue, false, text); + + public void Whisper(int number, string args = "") => + PublicOverheadMessage(MessageType.Whisper, WhisperHue, number, args); + + public void Yell(string text) => PublicOverheadMessage(MessageType.Yell, YellHue, false, text); + + public void Yell(int number, string args = "") => + PublicOverheadMessage(MessageType.Yell, YellHue, number, args); public bool SendHuePicker(HuePicker p) { @@ -8342,16 +8274,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return true; } - if (!ServerFeatureFlags.PvPCombat && Player && target.Player) - { - if (message) - { - SendLocalizedMessage(1001018); // You can not perform negative acts on your target. - } - - return false; - } - // TODO: Pets if (!Region.AllowHarmful(this, target)) { @@ -8395,7 +8317,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro Region.OnDidHarmful(this, target); target.Region.OnGotHarmful(this, target); - if (!indirect && !ChangingCombatant) + if (!indirect) { Combatant = target; } @@ -8934,6 +8856,245 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro public Direction GetDirectionTo(IPoint2D p, bool run = false) => p == null ? Direction.North | (run ? Direction.Running : 0) : GetDirectionTo(p.X, p.Y, run); + public void PublicOverheadMessage( + MessageType type, int hue, bool ascii, string text, bool noLineOfSight = true, + AccessLevel accessLevel = AccessLevel.Player + ) + { + if (m_Map == null) + { + return; + } + + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); + + foreach (var state in m_Map.GetClientsInRange(m_Location)) + { + if ( + state.Mobile.AccessLevel >= accessLevel && + state.Mobile.CanSee(this) && + (noLineOfSight || state.Mobile.InLOS(this)) + ) + { + var length = OutgoingMessagePackets.CreateMessage( + buffer, + Serial, + Body, + type, + hue, + 3, + ascii, + Language, + Name, + text + ); + + if (length != buffer.Length) + { + buffer = buffer[..length]; // Adjust to the actual size + } + + state.Send(buffer); + } + } + } + + public void PublicOverheadMessage(MessageType type, int hue, int number, string args = "", bool noLineOfSight = true) + { + if (m_Map == null) + { + return; + } + + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedLength(args)].InitializePacket(); + + foreach (var state in m_Map.GetClientsInRange(m_Location)) + { + if (state.Mobile.CanSee(this) && (noLineOfSight || state.Mobile.InLOS(this))) + { + var length = OutgoingMessagePackets.CreateMessageLocalized( + buffer, + Serial, + Body, + type, + hue, + 3, + number, + Name, + args + ); + + if (length != buffer.Length) + { + buffer = buffer[..length]; // Adjust to the actual size + } + + state.Send(buffer); + } + } + } + + public void PublicOverheadMessage( + MessageType type, int hue, int number, AffixType affixType, string affix, + string args = "", bool noLineOfSight = false, + AccessLevel accessLevel = AccessLevel.Player + ) + { + if (m_Map == null) + { + return; + } + + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedAffixLength(affix, args)] + .InitializePacket(); + + foreach (var state in m_Map.GetClientsInRange(m_Location)) + { + if ( + state.Mobile.AccessLevel >= accessLevel && + state.Mobile.CanSee(this) && + (noLineOfSight || state.Mobile.InLOS(this)) + ) + { + var length = OutgoingMessagePackets.CreateMessageLocalizedAffix( + buffer, + Serial, + Body, + type, + hue, + 3, + number, + Name, + affixType, + affix, + args + ); + + if (length != buffer.Length) + { + buffer = buffer[..length]; // Adjust to the actual size + } + + state.Send(buffer); + } + } + } + + public void PrivateOverheadMessage(MessageType type, int hue, bool ascii, string text, NetState state) + { + state.SendMessage(Serial, Body, type, hue, 3, ascii, m_Language, Name, text); + } + + public void PrivateOverheadMessage(MessageType type, int hue, int number, NetState state) => + PrivateOverheadMessage(type, hue, number, "", state); + + public void PrivateOverheadMessage(MessageType type, int hue, int number, string args, NetState state) => + state.SendMessageLocalized(Serial, Body, type, hue, 3, number, Name, args); + + public void LocalOverheadMessage(MessageType type, int hue, bool ascii, string text) => + m_NetState.SendMessage(Serial, Body, type, hue, 3, ascii, m_Language, Name, text); + + public void LocalOverheadMessage(MessageType type, int hue, int number, string args = "") => + m_NetState.SendMessageLocalized(Serial, Body, type, hue, 3, number, Name, args); + + public void NonlocalOverheadMessage(MessageType type, int hue, int number, string args = "") + { + if (m_Map == null) + { + return; + } + + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedLength(args)].InitializePacket(); + + foreach (var state in m_Map.GetClientsInRange(m_Location)) + { + if (state != m_NetState && state.Mobile.CanSee(this)) + { + var length = OutgoingMessagePackets.CreateMessageLocalized( + buffer, + Serial, + Body, + type, + hue, + 3, + number, + Name, + args + ); + + if (length != buffer.Length) + { + buffer = buffer[..length]; // Adjust to the actual size + } + + state.Send(buffer); + } + } + } + + public void NonlocalOverheadMessage(MessageType type, int hue, bool ascii, string text) + { + if (m_Map == null) + { + return; + } + + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); + + foreach (var state in m_Map.GetClientsInRange(m_Location)) + { + if (state != m_NetState && state.Mobile.CanSee(this)) + { + var length = OutgoingMessagePackets.CreateMessage( + buffer, + Serial, + Body, + type, + hue, + 3, + ascii, + Language, + Name, + text + ); + + if (length != buffer.Length) + { + buffer = buffer[..length]; // Adjust to the actual size + } + + state.Send(buffer); + } + } + } + + public void SendLocalizedMessage(int number, string args = "", int hue = 0x3B2) => + m_NetState.SendMessageLocalized(Serial.MinusOne, -1, MessageType.Regular, hue, 3, number, "System", args); + + public void SendLocalizedMessage(int number, bool append, string affix, string args = "", int hue = 0x3B2) => + m_NetState.SendMessageLocalizedAffix( + Serial.MinusOne, + -1, + MessageType.Regular, + hue, + 3, + number, + "System", + (append ? AffixType.Append : AffixType.Prepend) | AffixType.System, + affix, + args + ); + + public void SendMessage(string text) => SendMessage(0x3B2, text); + + public void SendMessage(int hue, string text) => + m_NetState.SendMessage(Serial.MinusOne, -1, MessageType.Regular, hue, 3, false, "ENU", "System", text); + + public void SendAsciiMessage(string text) => SendAsciiMessage(0x3B2, text); + + public void SendAsciiMessage(int hue, string text) => + m_NetState.SendMessage(Serial.MinusOne, -1, MessageType.Regular, hue, 3, true, null, "System", text); + /// /// Overridable. Event invoked when the Mobile is double clicked. By default, this method can either dismount or open the /// paperdoll. diff --git a/Projects/Server/Mobiles/Mods/DefaultSkillMod.cs b/Projects/Server/Mobiles/Mods/DefaultSkillMod.cs index f9d5221d1..82f64db93 100644 --- a/Projects/Server/Mobiles/Mods/DefaultSkillMod.cs +++ b/Projects/Server/Mobiles/Mods/DefaultSkillMod.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: DefaultSkillMod.cs * * * diff --git a/Projects/Server/Mobiles/Mods/EquippedSkillMod.cs b/Projects/Server/Mobiles/Mods/EquippedSkillMod.cs index ab3e1388d..1977409f8 100644 --- a/Projects/Server/Mobiles/Mods/EquippedSkillMod.cs +++ b/Projects/Server/Mobiles/Mods/EquippedSkillMod.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: EquippedSkillMod.cs * * * diff --git a/Projects/Server/Mobiles/Mods/MobileMod.cs b/Projects/Server/Mobiles/Mods/MobileMod.cs index 49473775d..c5abec926 100644 --- a/Projects/Server/Mobiles/Mods/MobileMod.cs +++ b/Projects/Server/Mobiles/Mods/MobileMod.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MobileMod.cs * * * diff --git a/Projects/Server/Mobiles/Mods/ResistanceMod.cs b/Projects/Server/Mobiles/Mods/ResistanceMod.cs index a5423e039..c3ade7ed7 100644 --- a/Projects/Server/Mobiles/Mods/ResistanceMod.cs +++ b/Projects/Server/Mobiles/Mods/ResistanceMod.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ResistanceMod.cs * * * @@ -13,7 +13,6 @@ * along with this program. If not, see . * *************************************************************************/ -using System.Runtime.CompilerServices; using ModernUO.Serialization; namespace Server; @@ -21,18 +20,6 @@ namespace Server; [SerializationGenerator(0)] public partial class ResistanceMod : MobileMod { - [SerializableField(0, fieldChanged: nameof(OnTypeChanged))] - private ResistanceType _type; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void OnTypeChanged(ResistanceType oldValue, ResistanceType newValue) => Owner?.UpdateResistances(); - - [SerializableField(1, fieldChanged: nameof(OnOffsetChanged))] - private int _offset; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void OnOffsetChanged(int oldValue, int newValue) => Owner?.UpdateResistances(); - public ResistanceMod(Mobile owner) : base(owner) { } @@ -42,4 +29,36 @@ public partial class ResistanceMod : MobileMod _type = type; _offset = offset; } + + [SerializableProperty(0)] + public ResistanceType Type + { + get => _type; + set + { + if (_type != value) + { + _type = value; + + Owner?.UpdateResistances(); + MarkDirty(); + } + } + } + + [SerializableProperty(1)] + public int Offset + { + get => _offset; + set + { + if (_offset != value) + { + _offset = value; + + Owner?.UpdateResistances(); + MarkDirty(); + } + } + } } diff --git a/Projects/Server/Mobiles/Mods/SkillMod.cs b/Projects/Server/Mobiles/Mods/SkillMod.cs index cfe96c0c4..4324bdeb8 100644 --- a/Projects/Server/Mobiles/Mods/SkillMod.cs +++ b/Projects/Server/Mobiles/Mods/SkillMod.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SkillMod.cs * * * @@ -21,31 +21,6 @@ namespace Server; [SerializationGenerator(0)] public abstract partial class SkillMod : MobileMod { - [SerializableField(0, fieldChanged: nameof(OnObeyCapChanged))] - private bool _obeyCap; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void OnObeyCapChanged(bool oldValue, bool newValue) => Owner?.Skills[_skill]?.Update(); - - [SerializableField(1, fieldChanged: nameof(OnSkillChanged))] - private SkillName _skill; - - private void OnSkillChanged(SkillName oldValue, SkillName newValue) - { - Owner?.Skills[newValue]?.Update(); - Owner?.Skills[oldValue]?.Update(); - } - - [SerializableField(2, fieldChanged: nameof(OnRelativeChanged))] - private bool _relative; - - private void OnRelativeChanged(bool oldValue, bool newValue) => Owner?.Skills[_skill]?.Update(); - - [SerializableField(3, fieldChanged: nameof(OnValueChanged))] - private double _value; - - private void OnValueChanged(double oldValue, double newValue) => Owner?.Skills[_skill]?.Update(); - public SkillMod(Mobile owner) : base(owner) { } @@ -57,12 +32,80 @@ public abstract partial class SkillMod : MobileMod _value = value; } + [SerializableProperty(0)] + public bool ObeyCap + { + get => _obeyCap; + set + { + _obeyCap = value; + + var sk = Owner?.Skills[_skill]; + sk?.Update(); + MarkDirty(); + } + } + + [SerializableProperty(1)] + public SkillName Skill + { + get => _skill; + set + { + if (_skill != value) + { + var oldUpdate = Owner?.Skills[_skill]; + + _skill = value; + + var sk = Owner?.Skills[_skill]; + sk?.Update(); + oldUpdate?.Update(); + MarkDirty(); + } + } + } + + [SerializableProperty(2)] + public bool Relative + { + get => _relative; + set + { + if (_relative != value) + { + _relative = value; + + var sk = Owner?.Skills[_skill]; + sk?.Update(); + MarkDirty(); + } + } + } + public bool Absolute { get => !Relative; set => Relative = !value; } + [SerializableProperty(3)] + public double Value + { + get => _value; + set + { + if (_value != value) + { + _value = value; + + var sk = Owner?.Skills[_skill]; + sk?.Update(); + MarkDirty(); + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Remove() => Owner?.RemoveSkillMod(this); diff --git a/Projects/Server/Mobiles/Mods/StatMod.cs b/Projects/Server/Mobiles/Mods/StatMod.cs index 1afa3162e..60f69f424 100644 --- a/Projects/Server/Mobiles/Mods/StatMod.cs +++ b/Projects/Server/Mobiles/Mods/StatMod.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: StatMod.cs * * * diff --git a/Projects/Server/Mobiles/Mods/TimedSkillMod.cs b/Projects/Server/Mobiles/Mods/TimedSkillMod.cs index 1b07973f6..352278f8a 100644 --- a/Projects/Server/Mobiles/Mods/TimedSkillMod.cs +++ b/Projects/Server/Mobiles/Mods/TimedSkillMod.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TimedSkillMod.cs * * * diff --git a/Projects/Server/Mobiles/Movement.cs b/Projects/Server/Mobiles/Movement.cs index 89d14f1b2..f3f509bfb 100644 --- a/Projects/Server/Mobiles/Movement.cs +++ b/Projects/Server/Mobiles/Movement.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Movement.cs * * * diff --git a/Projects/Server/Mobiles/SimplePrompt.cs b/Projects/Server/Mobiles/SimplePrompt.cs index a96483759..5bacd89dc 100644 --- a/Projects/Server/Mobiles/SimplePrompt.cs +++ b/Projects/Server/Mobiles/SimplePrompt.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SimplePrompt.cs * * * diff --git a/Projects/Server/Mobiles/SimpleTarget.cs b/Projects/Server/Mobiles/SimpleTarget.cs index ecb9ec863..c7e44eebb 100644 --- a/Projects/Server/Mobiles/SimpleTarget.cs +++ b/Projects/Server/Mobiles/SimpleTarget.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SimpleTarget.cs * * * diff --git a/Projects/Server/Client/MultiData.cs b/Projects/Server/MultiData.cs similarity index 96% rename from Projects/Server/Client/MultiData.cs rename to Projects/Server/MultiData.cs index 43498614d..f5860c3f3 100644 --- a/Projects/Server/Client/MultiData.cs +++ b/Projects/Server/MultiData.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MultiData.cs * * * @@ -25,9 +25,6 @@ namespace Server; public static class MultiData { - public static string HousingUOPPath { get; private set; } - public static UOPEntry HousingEntry { get; private set; } - public static void Configure() { var multiUOPPath = Core.FindDataFile("MultiCollection.uop", false); @@ -56,12 +53,10 @@ public static class MultiData { using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); - var housingHash = UOPFiles.HashLittle2("build/multicollection/housing.bin"); - var additionalHashes = new Dictionary { [housingHash] = default }; + // TODO: Find out if housing.bin is needed and read that. + var uopEntries = UOPFiles.ReadUOPIndexes(stream, ".bin", 0x10000, 4, 6); - var uopEntries = UOPFiles.ReadUOPIndexes(stream, ".bin", 0x10000, 4, 6, additionalHashes); - - var compressionBuffer = STArrayPool.Shared.Rent(0x10000); + byte[] compressionBuffer = STArrayPool.Shared.Rent(0x10000); var buffer = STArrayPool.Shared.Rent(0x10000); foreach (var (i, entry) in uopEntries) @@ -125,12 +120,6 @@ public static class MultiData STArrayPool.Shared.Return(buffer); STArrayPool.Shared.Return(compressionBuffer); - - if (additionalHashes.TryGetValue(housingHash, out var housingEntry) && housingEntry.Size > 0) - { - HousingUOPPath = path; - HousingEntry = housingEntry; - } } private static void LoadMul(bool postHSMulFormat) diff --git a/Projects/Server/Network/Bans/BanChannel.cs b/Projects/Server/Network/Bans/BanChannel.cs deleted file mode 100644 index b682158d0..000000000 --- a/Projects/Server/Network/Bans/BanChannel.cs +++ /dev/null @@ -1,160 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BanChannel.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; -using Server.Logging; - -namespace Server.Network.Bans; - -/// -/// Coordinates the configured contribution sinks. Enforcement is NOT here — -/// the accept path asks . This channel only fans locally-decided bans out -/// to external systems (CrowdSec), which distribute them to OS-level bouncers. -/// -public static class BanChannel -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(BanChannel)); - - private static IBanReporter[] _reporters = []; - - public static IReadOnlyList Reporters => _reporters; - - /// - /// Registers a contribution sink from content (inversion of control). Idempotent by - /// : a second registration of the same name is ignored. Configures the - /// reporter immediately so it is ready before . - /// - public static void Register(IBanReporter reporter) - { - if (reporter == null) - { - return; - } - - var reporters = _reporters; - for (var i = 0; i < reporters.Length; i++) - { - if (reporters[i].Name == reporter.Name) - { - return; - } - } - - reporter.Register(); - - var updated = new IBanReporter[_reporters.Length + 1]; - Array.Copy(_reporters, updated, _reporters.Length); - updated[^1] = reporter; - _reporters = updated; - - logger.Information("Ban channel registered reporter '{Name}'", reporter.Name); - } - - internal static void ConfigureForTesting(IBanReporter[] reporters) => _reporters = reporters ?? []; - - public static void Start(CancellationToken token) - { - var reporters = _reporters; - for (var i = 0; i < reporters.Length; i++) - { - var reporter = reporters[i]; - try - { - reporter.Start(token); - } - catch (Exception e) - { - // A broken contribution path must not crash boot — enforcement is local and unaffected. - logger.Error(e, "Ban reporter '{Name}' failed to start; continuing without it", reporter.Name); - } - } - } - - public static void Stop() - { - var reporters = _reporters; - for (var i = 0; i < reporters.Length; i++) - { - var reporter = reporters[i]; - try - { - reporter.Stop(); - } - catch (Exception e) - { - logger.Warning(e, "Ban reporter '{Name}' threw while stopping", reporter.Name); - } - } - } - - /// Fans a locally-decided ban out to every reporter. Non-blocking; never throws. - /// - /// Optional content-supplied exemption; true drops the contribution before any reporter sees it. This - /// withholds escalation only — the gate that reached the verdict has already acted. - /// - /// - /// An implementation that ignores reason would silently swallow manual bans. See - /// . - /// - public static Func IsExempt { get; set; } - - public static void Report(IPAddress ip, TimeSpan ttl, string reason) - { - var exempt = IsExempt; - if (exempt != null && exempt(ip, reason)) - { - logger.Debug("{Address} not contributed ('{Reason}'): exempt", ip, reason); - return; - } - - var reporters = _reporters; - for (var i = 0; i < reporters.Length; i++) - { - try - { - reporters[i].Report(ip, ttl, reason); - } - catch (Exception e) - { - logger.Warning(e, "Ban reporter '{Name}' threw during Report", reporters[i].Name); - } - } - } - - /// Fans a retraction (manual unban) out to every retract-capable reporter. - public static void Retract(IPAddress ip) - { - var reporters = _reporters; - for (var i = 0; i < reporters.Length; i++) - { - if (!reporters[i].CanRetract) - { - continue; - } - - try - { - reporters[i].Retract(ip); - } - catch (Exception e) - { - logger.Warning(e, "Ban reporter '{Name}' threw during Retract", reporters[i].Name); - } - } - } -} diff --git a/Projects/Server/Network/Bans/BanConfiguration.cs b/Projects/Server/Network/Bans/BanConfiguration.cs deleted file mode 100644 index d678a6470..000000000 --- a/Projects/Server/Network/Bans/BanConfiguration.cs +++ /dev/null @@ -1,98 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BanConfiguration.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.IO; -using System.Text.Json.Serialization; -using Server.Json; - -namespace Server.Network.Bans; - -/// -/// Loads the from Configuration/bans.json (matching the per-feature -/// JSON config pattern used by AssistantConfiguration). Loaded once; a missing file writes a -/// local-only, fail-open template so operators have something to edit. -/// -public static class BanConfiguration -{ - private const string _path = "Configuration/bans.json"; - - private static bool _loaded; - - /// - /// Never null: the accept and reap paths read this per connection, including before - /// has run (a harness driving NetState.Slice directly), so it starts at the record's defaults. - /// - public static BanSettings Settings { get; private set; } = new(); - - public static void Configure() - { - // Idempotent; flagged rather than null-checked because Settings is non-null from the start. - if (_loaded) - { - return; - } - - _loaded = true; - - var path = Path.Join(Core.BaseDirectory, _path); - - if (File.Exists(path)) - { - Settings = JsonConfig.Deserialize(path); - } - else - { - Settings = new BanSettings - { - ReportRateLimitTrips = true, - AutoBanDuration = TimeSpan.FromHours(4) - }; - - Save(); - } - } - - private static void Save() - { - JsonConfig.Serialize(Path.Join(Core.BaseDirectory, _path), Settings); - } -} - -/// Ban-channel policy: which reporters receive contributions, and how auto-detections are handled. -public record BanSettings -{ - /// Whether IP rate-limiter trips are contributed to reporters. They never enter the local firewall set. - [JsonPropertyName("reportRateLimitTrips")] - public bool ReportRateLimitTrips { get; set; } = true; - - /// Duration reported for an auto-detected (rate-limit) ban. - [JsonPropertyName("autoBanDuration")] - public TimeSpan AutoBanDuration { get; set; } = TimeSpan.FromHours(4); - - /// - /// Whether behavioural detections are contributed to reporters. Those connections are disconnected either - /// way; this only controls escalation. - /// - /// - /// Keyed on bytes-received, never elapsed time: a connection that sent something and ran out of time is - /// far more likely a slow link than an attack. See dev-docs/ip-bans-and-allowlists.md. - /// - [JsonPropertyName("reportBadConnects")] - public bool ReportBadConnects { get; set; } = true; - - [JsonPropertyName("badConnectDuration")] - public TimeSpan BadConnectDuration { get; set; } = TimeSpan.FromHours(4); -} diff --git a/Projects/Server/Network/Bans/BanReasons.cs b/Projects/Server/Network/Bans/BanReasons.cs deleted file mode 100644 index 16708f716..000000000 --- a/Projects/Server/Network/Bans/BanReasons.cs +++ /dev/null @@ -1,48 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BanReasons.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -namespace Server.Network.Bans; - -/// The reason slugs contributed through . Policy keys off these. -public static class BanReasons -{ - /// An operator banned this address explicitly. Never exempt, never auto-denied. - public const string Manual = "manual"; - - public const string RateLimit = "rate-limit"; - - /// Matched the reputation blocklist. Enforced by its own filter, not by behaviour. - public const string Blocklist = "blocklist"; - - /// Reaped without ever sending a byte. - public const string SilentConnect = "silent-connect"; - - /// Opened with a zero seed, which no real client sends. - public const string InvalidSeed = "invalid-seed"; - - /// Positively identified as another protocol entirely. See . - public const string ForeignProtocol = "foreign-protocol"; - - /// - /// Verdicts the shard reached by watching the connection. Only these may be exempted, and only these feed - /// the local denylist. - /// - /// - /// Opt-in rather than "everything except ", so a reason added later escalates normally - /// instead of silently inheriting an exemption. - /// - public static bool IsBehavioral(string reason) => - reason is RateLimit or SilentConnect or InvalidSeed or ForeignProtocol; -} diff --git a/Projects/Server/Network/Bans/IBanReporter.cs b/Projects/Server/Network/Bans/IBanReporter.cs deleted file mode 100644 index 190c6936e..000000000 --- a/Projects/Server/Network/Bans/IBanReporter.cs +++ /dev/null @@ -1,55 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: IBanReporter.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Net; -using System.Threading; - -namespace Server.Network.Bans; - -/// -/// A contribution sink behind . Reporters receive locally-decided bans -/// (manual admin bans, rate-limit trips, blocklist promotions) and forward them to an external system -/// (e.g. CrowdSec), which distributes them to OS-level bouncers. Reporters never answer the accept-path -/// membership query — that is an 's job. -/// -public interface IBanReporter -{ - /// Stable id for logging/config (e.g. crowdsec). - string Name { get; } - - /// Reads configuration. No network or file I/O here. - void Register(); - - /// Starts background delivery. The token is cancelled on shutdown. - void Start(CancellationToken token); - - /// Flushes and tears down background delivery. - void Stop(); - - /// - /// Enqueues a ban contribution. MUST be non-blocking and safe on the accept path: it may only - /// enqueue (bounded, drop-on-overflow) and never perform synchronous I/O. - /// - /// or negative = use the reporter's default duration. - /// Short slug (manual, rate-limit) used as the scenario suffix. - void Report(IPAddress address, TimeSpan ttl, string reason); - - /// True if this reporter can retract a previously-reported ban. - bool CanRetract { get; } - - /// Enqueues a retraction (e.g. a manual unban). No-op if unsupported. - void Retract(IPAddress address); -} diff --git a/Projects/Server/Network/ConnectionFilters.cs b/Projects/Server/Network/ConnectionFilters.cs deleted file mode 100644 index c0bb0b2fb..000000000 --- a/Projects/Server/Network/ConnectionFilters.cs +++ /dev/null @@ -1,155 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ConnectionFilters.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; -using Server.Logging; - -namespace Server.Network; - -/// -/// Registry of the gates the accept path consults, and their lifecycle. -/// Filters are registered during the Configure sweep and the backing store is a plain array, so -/// is an indexed loop over a field read — no enumerator, no closure, no -/// allocation. The whole accept path runs on the game loop, so no synchronization is needed. -/// -public static class ConnectionFilters -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ConnectionFilters)); - - private static IConnectionFilter[] _filters = []; - - public static IReadOnlyList Filters => _filters; - - /// - /// Registers a gate (inversion of control, mirroring BanChannel.Register). Idempotent by - /// . Filters are consulted in registration order, so register - /// the cheapest and most selective first — core registers the firewall before content is swept. - /// - public static void Register(IConnectionFilter filter) - { - if (filter == null) - { - return; - } - - var filters = _filters; - for (var i = 0; i < filters.Length; i++) - { - if (filters[i].Name == filter.Name) - { - return; - } - } - - filter.Register(); - - var updated = new IConnectionFilter[_filters.Length + 1]; - Array.Copy(_filters, updated, _filters.Length); - updated[^1] = filter; - _filters = updated; - - logger.Information("Registered connection filter '{Name}'", filter.Name); - } - - /// - /// True when any filter denies the connection. Short-circuits on the first denial; - /// names it for logging. - /// - public static bool ShouldDeny(IPAddress address, out string deniedBy) - { - var filters = _filters; - for (var i = 0; i < filters.Length; i++) - { - // A faulty filter must not take down the accept loop for every connection. - try - { - if (filters[i].ShouldDeny(address)) - { - deniedBy = filters[i].Name; - return true; - } - } - catch (Exception e) - { - Disable(filters[i], e); - } - } - - deniedBy = null; - return false; - } - - /// - /// Drops a filter that threw on the accept path: one that faults once faults for every subsequent - /// connection, costing an exception and a log line per accept. Failing open is deliberate — a broken - /// filter must not be able to deny every connection either. - /// - private static void Disable(IConnectionFilter filter, Exception e) - { - logger.Error(e, "Connection filter '{Name}' threw on the accept path; unregistering it", filter.Name); - - var filters = _filters; - var updated = new List(filters.Length); - for (var i = 0; i < filters.Length; i++) - { - if (!ReferenceEquals(filters[i], filter)) - { - updated.Add(filters[i]); - } - } - - _filters = updated.ToArray(); - } - - public static void Start(CancellationToken token) - { - var filters = _filters; - for (var i = 0; i < filters.Length; i++) - { - var filter = filters[i]; - try - { - filter.Start(token); - } - catch (Exception e) - { - // A filter that cannot hydrate must not crash boot; it simply denies nothing. - logger.Error(e, "Connection filter '{Name}' failed to start; continuing without it", filter.Name); - } - } - } - - public static void Stop() - { - var filters = _filters; - for (var i = 0; i < filters.Length; i++) - { - var filter = filters[i]; - try - { - filter.Stop(); - } - catch (Exception e) - { - logger.Warning(e, "Connection filter '{Name}' threw while stopping", filter.Name); - } - } - } - - internal static void ResetForTesting() => _filters = []; -} diff --git a/Projects/Server/Network/EncodedPacketHandler.cs b/Projects/Server/Network/EncodedPacketHandler.cs index cbe0cf92c..8dbe303ea 100644 --- a/Projects/Server/Network/EncodedPacketHandler.cs +++ b/Projects/Server/Network/EncodedPacketHandler.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: EncodedPacketHandler.cs * * * diff --git a/Projects/Server/Network/EncodedReader.cs b/Projects/Server/Network/EncodedReader.cs index ddc05e932..ff589e082 100644 --- a/Projects/Server/Network/EncodedReader.cs +++ b/Projects/Server/Network/EncodedReader.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: EncodedReader.cs * * * diff --git a/Projects/Server/Network/Encryption/EncryptionConfig.cs b/Projects/Server/Network/Encryption/EncryptionConfig.cs deleted file mode 100644 index fe7fc7edf..000000000 --- a/Projects/Server/Network/Encryption/EncryptionConfig.cs +++ /dev/null @@ -1,45 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: EncryptionConfig.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Network; - -/// -/// Specifies which encryption modes the server will accept. -/// -[Flags] -public enum EncryptionMode -{ - /// - /// Encryption handling is disabled. Current behavior. - /// - None = 0x0, - - /// - /// Accept unencrypted clients (e.g., ClassicUO with encryption disabled). - /// - Unencrypted = 0x1, - - /// - /// Accept encrypted clients (original UO client, Enhanced Client). - /// - Encrypted = 0x2, - - /// - /// Auto-detect and accept both encrypted and unencrypted clients. - /// - Both = Unencrypted | Encrypted -} diff --git a/Projects/Server/Network/Encryption/EncryptionManager.cs b/Projects/Server/Network/Encryption/EncryptionManager.cs deleted file mode 100644 index 881430f66..000000000 --- a/Projects/Server/Network/Encryption/EncryptionManager.cs +++ /dev/null @@ -1,207 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: EncryptionManager.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Buffers.Binary; -using Server.Logging; - -namespace Server.Network; - -/// -/// Manages encryption detection and configuration for client connections. -/// -public static class EncryptionManager -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(EncryptionManager)); - - private static EncryptionMode _mode = EncryptionMode.None; - private static bool _debug; - - /// - /// Gets whether encryption handling is enabled. - /// - public static bool Enabled => _mode != EncryptionMode.None; - - /// - /// Gets the current encryption mode. - /// - public static EncryptionMode Mode => _mode; - - /// - /// Gets whether debug logging is enabled for encryption. - /// - public static bool Debug => _debug; - - /// - /// Configures encryption settings from server configuration. - /// - public static void Configure() - { - _mode = ServerConfiguration.GetSetting("network.encryptionMode", EncryptionMode.Both); - _debug = ServerConfiguration.GetSetting("network.encryptionDebug", false); - - if (_mode != EncryptionMode.None) - { - logger.Information("Encryption support enabled: {Mode}", _mode); - } - } - - /// The network state. - extension(NetState ns) - { - /// - /// Detects and initializes encryption for a login packet (0x80). - /// - /// The 62-byte login packet buffer. - /// The detected encryption, or null if unencrypted. - /// True if detection succeeded (encrypted or unencrypted), false if rejected. - public bool DetectLoginEncryption(ReadOnlySpan buffer, out IClientEncryption encryption) - { - encryption = null; - - if (buffer.Length < 62) - { - return false; - } - - // Check if unencrypted: - // - Packet ID is 0x80, OR - // - Username and password null terminators are present - var isUnencrypted = buffer[0] == 0x80 || buffer[30] == 0x00 && buffer[60] == 0x00; - - if (isUnencrypted) - { - if (!_mode.HasFlag(EncryptionMode.Unencrypted)) - { - if (_debug) - { - logger.Debug("Client {Address}: Unencrypted login rejected (mode: {Mode})", ns.Address, _mode); - } - return false; - } - - if (_debug) - { - logger.Debug("Client {Address}: Unencrypted login detected", ns.Address); - } - - return true; - } - - // Try encrypted - if (!_mode.HasFlag(EncryptionMode.Encrypted)) - { - if (_debug) - { - logger.Debug("Client {Address}: Encrypted login rejected (mode: {Mode})", ns.Address, _mode); - } - return false; - } - - // Attempt decryption with version-derived keys - if (LoginEncryption.TryDecrypt(ns.Version, (uint)ns.Seed, buffer, out var loginEncryption)) - { - encryption = loginEncryption; - - if (_debug) - { - logger.Debug("Client {Address}: Encrypted login detected (version: {Version})", ns.Address, ns.Version); - } - - return true; - } - - if (_debug) - { - logger.Debug("Client {Address}: Login encryption detection failed", ns.Address); - } - - return false; - } - - /// - /// Detects and initializes encryption for a game server login packet (0x91). - /// - /// The 65-byte game login packet buffer. - /// The detected encryption, or null if unencrypted. - /// True if detection succeeded (encrypted or unencrypted), false if rejected. - public bool DetectGameEncryption(ReadOnlySpan buffer, out IClientEncryption encryption) - { - encryption = null; - - if (buffer.Length < 65) - { - return false; - } - - // Extract auth ID from packet (bytes 1-4, big-endian) - var authId = BinaryPrimitives.ReadUInt32BigEndian(buffer[1..]); - - // Check if unencrypted: - // - Packet ID is 0x91, OR - // - Auth ID equals seed (indicates no encryption applied) - var isUnencrypted = buffer[0] == 0x91 || authId == (uint)ns.Seed; - - if (isUnencrypted) - { - if (!_mode.HasFlag(EncryptionMode.Unencrypted)) - { - if (_debug) - { - logger.Debug("Client {Address}: Unencrypted game login rejected (mode: {Mode})", ns.Address, _mode); - } - return false; - } - - if (_debug) - { - logger.Debug("Client {Address}: Unencrypted game login detected", ns.Address); - } - - return true; - } - - // Try encrypted - if (!_mode.HasFlag(EncryptionMode.Encrypted)) - { - if (_debug) - { - logger.Debug("Client {Address}: Encrypted game login rejected (mode: {Mode})", ns.Address, _mode); - } - return false; - } - - // Attempt decryption with seed-derived Twofish - if (GameEncryption.TryDecrypt((uint)ns.Seed, buffer, out var gameEncryption)) - { - encryption = gameEncryption; - - if (_debug) - { - logger.Debug("Client {Address}: Encrypted game login detected (seed: 0x{Seed:X8})", ns.Address, ns.Seed); - } - - return true; - } - - if (_debug) - { - logger.Debug("Client {Address}: Game encryption detection failed", ns.Address); - } - - return false; - } - } -} diff --git a/Projects/Server/Network/Encryption/GameEncryption.cs b/Projects/Server/Network/Encryption/GameEncryption.cs deleted file mode 100644 index e8eb3ea91..000000000 --- a/Projects/Server/Network/Encryption/GameEncryption.cs +++ /dev/null @@ -1,202 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: GameEncryption.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.Intrinsics; -using System.Security.Cryptography; -using Server.Logging; - -namespace Server.Network; - -/// -/// Implements game packet encryption/decryption using Twofish + MD5. -/// Used for all game packets after login (0x91 and onwards). -/// -public sealed class GameEncryption : IClientEncryption -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(GameEncryption)); - - private const int CipherTableSize = 256; - private const int BlockSize = 16; - - // Static identity table [0..255] for vectorized CopyTo initialization - private static readonly byte[] IdentityTable = CreateIdentityTable(); - - private static byte[] CreateIdentityTable() - { - var table = new byte[CipherTableSize]; - for (var i = 0; i < CipherTableSize; i++) - { - table[i] = (byte)i; - } - return table; - } - - private readonly TwofishEngine _twofish; - private readonly byte[] _cipherTable; - private readonly byte[] _xorKey; - - private ushort _recvPos; - private byte _sendPos; - - public GameEncryption(uint seed) - { - // Create 16-byte key from seed (repeated 4 times) - Span key = stackalloc byte[16]; - key[0] = key[4] = key[8] = key[12] = (byte)((seed >> 24) & 0xFF); - key[1] = key[5] = key[9] = key[13] = (byte)((seed >> 16) & 0xFF); - key[2] = key[6] = key[10] = key[14] = (byte)((seed >> 8) & 0xFF); - key[3] = key[7] = key[11] = key[15] = (byte)(seed & 0xFF); - - _twofish = new TwofishEngine(key); - - // Initialize cipher table with identity [0..255] using vectorized copy - _cipherTable = GC.AllocateUninitializedArray(CipherTableSize); - IdentityTable.CopyTo(_cipherTable, 0); - - // Encrypt cipher table with Twofish - RefreshCipherTable(); - - // Compute MD5 hash of cipher table for server->client XOR key - _xorKey = MD5.HashData(_cipherTable); - } - - /// - /// Refreshes the cipher table by encrypting it with Twofish. - /// Called every 256 bytes of received data. - /// - private void RefreshCipherTable() - { - // Encrypt cipher table in 16-byte blocks - for (var i = 0; i < CipherTableSize; i += BlockSize) - { - _twofish.EncryptBlock(_cipherTable.AsSpan(i, BlockSize)); - } - - _recvPos = 0; - } - - /// - /// Decrypts incoming data from the client (in-place). - /// XORs with cipher table, refreshing every 256 bytes. - /// - public void ClientDecrypt(Span buffer) - { - for (var i = 0; i < buffer.Length; i++) - { - if (_recvPos >= CipherTableSize) - { - RefreshCipherTable(); - } - - buffer[i] ^= _cipherTable[_recvPos++]; - } - } - - /// - /// Encrypts outgoing data to the client (in-place). - /// XORs with MD5 hash of cipher table (16-byte rotating key). - /// Uses SIMD optimization for larger buffers. - /// - public void ServerEncrypt(Span buffer) - { - var i = 0; - - // SIMD path: process 16 bytes at a time when aligned with key - if (_sendPos == 0 && buffer.Length >= 16 && Vector128.IsHardwareAccelerated) - { - var keyVec = Vector128.Create(_xorKey); - - for (; i + 16 <= buffer.Length; i += 16) - { - var chunk = Vector128.LoadUnsafe(ref buffer[i]); - var result = Vector128.Xor(chunk, keyVec); - result.StoreUnsafe(ref buffer[i]); - } - } - - // Scalar path for remainder or when not aligned - for (; i < buffer.Length; i++) - { - buffer[i] ^= _xorKey[_sendPos++]; - _sendPos &= 0x0F; // Wrap at 16 - } - } - - /// - /// Attempts to decrypt a game login packet and validate it. - /// Returns true if the packet appears to be validly encrypted. - /// - public static bool TryDecrypt(uint seed, ReadOnlySpan encryptedPacket, out GameEncryption encryption) - { - const int GameLoginPacketSize = 65; - - encryption = null; - - if (encryptedPacket.Length < GameLoginPacketSize) - { - if (EncryptionManager.Debug) - { - logger.Debug("GameEncryption.TryDecrypt: Invalid buffer length {Length}", encryptedPacket.Length); - } - return false; - } - - if (EncryptionManager.Debug) - { - logger.Debug("GameEncryption.TryDecrypt: Seed=0x{Seed:X8}", seed); - logger.Debug("GameEncryption.TryDecrypt: Encrypted[0..16]: {Bytes}", Convert.ToHexString(encryptedPacket[..16])); - } - - // Copy and decrypt - Span decrypted = stackalloc byte[GameLoginPacketSize]; - encryptedPacket[..GameLoginPacketSize].CopyTo(decrypted); - var enc = new GameEncryption(seed); - - if (EncryptionManager.Debug) - { - logger.Debug("GameEncryption.TryDecrypt: CipherTable[0..16]: {Bytes}", - Convert.ToHexString(enc._cipherTable.AsSpan(0, 16))); - logger.Debug("GameEncryption.TryDecrypt: XorKey: {Bytes}", Convert.ToHexString(enc._xorKey)); - } - - enc.ClientDecrypt(decrypted); - - if (EncryptionManager.Debug) - { - logger.Debug("GameEncryption.TryDecrypt: Decrypted[0..16]: {Bytes}", Convert.ToHexString(decrypted[..16])); - logger.Debug("GameEncryption.TryDecrypt: First byte=0x{Byte:X2} (expected 0x91)", decrypted[0]); - } - - // Validate: first byte must be 0x91 (game server login packet ID) - if (decrypted[0] != 0x91) - { - if (EncryptionManager.Debug) - { - logger.Debug("GameEncryption.TryDecrypt: Validation FAILED - first byte is not 0x91"); - } - return false; - } - - if (EncryptionManager.Debug) - { - logger.Debug("GameEncryption.TryDecrypt: Validation PASSED"); - } - - // Re-create encryption for actual use - encryption = new GameEncryption(seed); - return true; - } -} diff --git a/Projects/Server/Network/Encryption/IClientEncryption.cs b/Projects/Server/Network/Encryption/IClientEncryption.cs deleted file mode 100644 index 7755fbf71..000000000 --- a/Projects/Server/Network/Encryption/IClientEncryption.cs +++ /dev/null @@ -1,37 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: IClientEncryption.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Network; - -/// -/// Interface for client encryption implementations. -/// Uses Span-based API for zero-allocation in the hot path. -/// -public interface IClientEncryption -{ - /// - /// Decrypts incoming data from the client (in-place). - /// - /// The buffer containing encrypted data. Modified in-place. - void ClientDecrypt(Span buffer); - - /// - /// Encrypts outgoing data to the client (in-place). - /// - /// The buffer containing plaintext data. Modified in-place. - void ServerEncrypt(Span buffer); -} diff --git a/Projects/Server/Network/Encryption/LoginEncryption.cs b/Projects/Server/Network/Encryption/LoginEncryption.cs deleted file mode 100644 index e1310cfa5..000000000 --- a/Projects/Server/Network/Encryption/LoginEncryption.cs +++ /dev/null @@ -1,149 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: LoginEncryption.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Network; - -/// -/// Implements login packet encryption/decryption using XOR with version-derived keys. -/// Used for the initial account login packet (0x80). -/// -public sealed class LoginEncryption : IClientEncryption -{ - private uint _table1; - private uint _table2; - private readonly uint _key1; - private readonly uint _key2; - - public LoginEncryption(uint seed, LoginKeys keys) - { - _key1 = keys.Key1; - _key2 = keys.Key2; - - // Initialize state tables from seed - _table1 = ((~seed ^ 0x00001357) << 16) | ((seed ^ 0xFFFFAAAA) & 0x0000FFFF); - _table2 = ((seed ^ 0x43210000) >> 16) | ((~seed ^ 0xABCDFFFF) & 0xFFFF0000); - } - - /// - /// Attempts to initialize login encryption and validate the packet. - /// Returns true if the packet appears to be validly encrypted with this scheme. - /// When version is null (pre-6.0.5 clients that don't send 0xEF), tries all known legacy keys. - /// - public static bool TryDecrypt( - ClientVersion version, - uint seed, - ReadOnlySpan encryptedPacket, - out LoginEncryption encryption) - { - const int LoginPacketSize = 62; - - encryption = null; - - if (encryptedPacket.Length < LoginPacketSize) - { - return false; - } - - if (version != null) - { - var keys = LoginKeys.GetKeys(version); - return keys is not { Key1: 0, Key2: 0 } && TryDecryptWithKeys(keys, seed, encryptedPacket, out encryption); - } - - // No version available (pre-6.0.5 client) - try all known legacy keys - for (var i = 0; i < LoginKeys.LegacyKeys.Length; i++) - { - var legacyKeys = LoginKeys.LegacyKeys[i]; - if (TryDecryptWithKeys(legacyKeys, seed, encryptedPacket, out encryption)) - { - return true; - } - } - - return false; - } - - /// - /// Attempts decryption with a specific key pair and validates the result. - /// - private static bool TryDecryptWithKeys( - LoginKeys keys, - uint seed, - ReadOnlySpan encryptedPacket, - out LoginEncryption encryption) - { - const int LoginPacketSize = 62; - - // Copy and decrypt - Span decrypted = stackalloc byte[LoginPacketSize]; - encryptedPacket[..LoginPacketSize].CopyTo(decrypted); - var enc = new LoginEncryption(seed, keys); - enc.ClientDecrypt(decrypted); - - // Validate decrypted packet structure: - // - Byte 0 must be 0x80 (account login packet ID) - // - Byte 30 must be 0x00 (null terminator for username) - // - Byte 60 must be 0x00 (null terminator for password) - if (decrypted[0] != 0x80 || decrypted[30] != 0x00 || decrypted[60] != 0x00) - { - encryption = null; - return false; - } - - // Re-initialize encryption state for actual use - encryption = new LoginEncryption(seed, keys); - return true; - } - - /// - /// Decrypts incoming data from the client (in-place). - /// - public void ClientDecrypt(Span buffer) - { - for (var i = 0; i < buffer.Length; i++) - { - buffer[i] ^= (byte)(_table1 & 0xFF); - - var edx = _table2; - var esi = _table1 << 31; - var eax = _table2 >> 1; - - eax |= esi; - eax ^= _key1 - 1; - edx <<= 31; - eax >>= 1; - - var ecx = _table1 >> 1; - - eax |= esi; - ecx |= edx; - eax ^= _key1; - ecx ^= _key2; - - _table1 = ecx; - _table2 = eax; - } - } - - /// - /// Server does not encrypt login responses, so this is a no-op. - /// - public void ServerEncrypt(Span buffer) - { - // Login encryption is client-to-server only - } -} diff --git a/Projects/Server/Network/Encryption/LoginKeys.cs b/Projects/Server/Network/Encryption/LoginKeys.cs deleted file mode 100644 index 6b04a5bb7..000000000 --- a/Projects/Server/Network/Encryption/LoginKeys.cs +++ /dev/null @@ -1,118 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: LoginKeys.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace Server.Network; - -/// -/// Represents encryption keys derived from a client version. -/// Used for login packet encryption/decryption. -/// -public readonly struct LoginKeys -{ - public static readonly LoginKeys Empty = new(0, 0); - - private static readonly Dictionary _cache = []; - - private static LoginKeys[] _legacyKeys; - - public uint Key1 { get; } - public uint Key2 { get; } - - private LoginKeys(uint key1, uint key2) - { - Key1 = key1; - Key2 = key2; - } - - /// - /// Gets pre-computed login keys for all known client versions before 6.0.5 - /// that do not send the 0xEF packet (and thus have no version available at login time). - /// - public static ReadOnlySpan LegacyKeys => _legacyKeys ??= BuildLegacyKeys(); - - /// - /// Gets or computes encryption keys for the specified client version. - /// Results are cached for performance. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static LoginKeys GetKeys(ClientVersion version) - { - if (version == null) - { - return Empty; - } - - if (_cache.TryGetValue(version, out var keys)) - { - return keys; - } - - keys = ComputeKeys((uint)version.Major, (uint)version.Minor, (uint)version.Revision); - _cache[version] = keys; - return keys; - } - - /// - /// Computes encryption keys from version components using the UO key derivation algorithm. - /// - private static LoginKeys ComputeKeys(uint major, uint minor, uint revision) - { - // Key1 derivation - uint key1 = (major << 23) | (minor << 14) | (revision << 4); - key1 ^= (revision * revision) << 9; - key1 ^= minor * minor; - key1 ^= (minor * 11) << 24; - key1 ^= (revision * 7) << 19; - key1 ^= 0x2C13A5FD; - - // Key2 derivation - uint key2 = (major << 22) | (revision << 13) | (minor << 3); - key2 ^= (revision * revision * 3) << 10; - key2 ^= minor * minor; - key2 ^= (minor * 13) << 23; - key2 ^= (revision * 7) << 18; - key2 ^= 0xA31D527F; - - return new LoginKeys(key1, key2); - } - - /// - /// Builds the static array of pre-computed keys for pre-6.0.5 client versions. - /// These versions don't send 0xEF, so login encryption must be detected by trying known keys. - /// - private static LoginKeys[] BuildLegacyKeys() - { - // All unique (Major, Minor, Revision) tuples from 4.0.11 through 6.0.4 - ReadOnlySpan<(uint Major, uint Minor, uint Revision)> versions = - [ - (4, 0, 11), - (5, 0, 0), (5, 0, 1), (5, 0, 2), (5, 0, 3), (5, 0, 4), - (5, 0, 5), (5, 0, 6), (5, 0, 7), (5, 0, 8), (5, 0, 9), - (6, 0, 0), (6, 0, 1), (6, 0, 2), (6, 0, 3), (6, 0, 4), - ]; - - var keys = new LoginKeys[versions.Length]; - for (var i = 0; i < versions.Length; i++) - { - keys[i] = ComputeKeys(versions[i].Major, versions[i].Minor, versions[i].Revision); - } - - return keys; - } -} diff --git a/Projects/Server/Network/Encryption/TwofishEngine.cs b/Projects/Server/Network/Encryption/TwofishEngine.cs deleted file mode 100644 index 912db20c1..000000000 --- a/Projects/Server/Network/Encryption/TwofishEngine.cs +++ /dev/null @@ -1,271 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: TwofishEngine.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Server.Network; - -/// -/// Twofish block cipher implementation for UO encryption. -/// Implements 128-bit block encryption with 128-bit key in ECB mode. -/// Based on the public domain Twofish algorithm by Bruce Schneier et al. -/// -public sealed class TwofishEngine -{ - private const int BlockSize = 16; // 128 bits - private const int Rounds = 16; - private const int InputWhiten = 0; - private const int OutputWhiten = 4; - private const int RoundSubkeys = 8; - private const int TotalSubkeys = RoundSubkeys + 2 * Rounds; - - private const uint SkStep = 0x02020202u; - private const uint SkBump = 0x01010101u; - private const int SkRotl = 9; - private const uint RsGfFdbk = 0x14D; - private const int MdsGfFdbk = 0x169; - - // P0 and P1 permutation tables - private static readonly byte[] P0 = - { - 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, - 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, - 0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23, 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, - 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, 0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61, - 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, - 0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66, 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, - 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, 0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71, - 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, - 0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2, 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, - 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, 0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF, - 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, - 0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A, 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, - 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, 0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D, - 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, - 0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8, 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, - 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0 - }; - - private static readonly byte[] P1 = - { - 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, - 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, - 0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D, 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, - 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, 0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51, - 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, - 0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70, 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, - 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, 0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2, - 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, - 0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3, 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, - 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, 0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9, - 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, - 0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19, 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, - 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, 0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69, - 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, - 0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB, 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, - 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91 - }; - - private readonly uint[] _sboxKeys = new uint[2]; // For 128-bit key - private readonly uint[] _subKeys = new uint[TotalSubkeys]; - - /// - /// Creates a new Twofish engine with the specified 128-bit key. - /// - public TwofishEngine(ReadOnlySpan key) - { - if (key.Length != 16) - { - throw new ArgumentException("Key must be 16 bytes (128 bits)", nameof(key)); - } - - GenerateSubkeys(MemoryMarshal.Cast(key)); - } - - private void GenerateSubkeys(ReadOnlySpan keyWords) - { - // Split key into even and odd words - var k0 = keyWords[0]; - var k1 = keyWords[1]; - var k2 = keyWords[2]; - var k3 = keyWords[3]; - - // Compute S-box keys using RS matrix - _sboxKeys[0] = RsMdsEncode(k2, k3); - _sboxKeys[1] = RsMdsEncode(k0, k1); - - // Generate round subkeys - for (var i = 0; i < TotalSubkeys / 2; i++) - { - var a = F32((uint)(i * SkStep), k0, k2); - var b = F32((uint)(i * SkStep + SkBump), k1, k3); - b = BitOperations.RotateLeft(b, 8); - - _subKeys[2 * i] = a + b; - _subKeys[2 * i + 1] = BitOperations.RotateLeft(a + 2 * b, SkRotl); - } - } - - /// - /// Encrypts a 16-byte block in place. - /// - public void EncryptBlock(Span block) - { - if (block.Length < BlockSize) - { - throw new ArgumentException("Block must be at least 16 bytes", nameof(block)); - } - - var x = MemoryMarshal.Cast(block); - - // Input whitening - x[0] ^= _subKeys[InputWhiten]; - x[1] ^= _subKeys[InputWhiten + 1]; - x[2] ^= _subKeys[InputWhiten + 2]; - x[3] ^= _subKeys[InputWhiten + 3]; - - // 16 rounds - for (var r = 0; r < Rounds; r++) - { - var t0 = F32Sbox(x[0]); - var t1 = F32Sbox(BitOperations.RotateLeft(x[1], 8)); - - x[3] = BitOperations.RotateLeft(x[3], 1); - x[2] ^= t0 + t1 + _subKeys[RoundSubkeys + 2 * r]; - x[3] ^= t0 + 2 * t1 + _subKeys[RoundSubkeys + 2 * r + 1]; - x[2] = BitOperations.RotateRight(x[2], 1); - - if (r < Rounds - 1) - { - // Swap for next round - (x[0], x[2]) = (x[2], x[0]); - (x[1], x[3]) = (x[3], x[1]); - } - } - - // Output whitening - x[0] ^= _subKeys[OutputWhiten]; - x[1] ^= _subKeys[OutputWhiten + 1]; - x[2] ^= _subKeys[OutputWhiten + 2]; - x[3] ^= _subKeys[OutputWhiten + 3]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private uint F32Sbox(uint x) - { - // For 128-bit key, use 2 S-box keys - // Permutation sequence from Twofish spec (P_ij constants): - // b0: P0[P0[P0[x]^k1]^k0] then P1 final - // b1: P0[P0[P1[x]^k1]^k0] then P0 final - // b2: P1[P1[P0[x]^k1]^k0] then P1 final - // b3: P1[P1[P1[x]^k1]^k0] then P0 final - - var b0 = (byte)x; - var b1 = (byte)(x >> 8); - var b2 = (byte)(x >> 16); - var b3 = (byte)(x >> 24); - - var k0 = _sboxKeys[0]; - var k1 = _sboxKeys[1]; - - // First layer: P_02=0(P0), P_12=1(P1), P_22=0(P0), P_32=1(P1) - b0 = (byte)(P0[b0] ^ (byte)k1); - b1 = (byte)(P1[b1] ^ (byte)(k1 >> 8)); - b2 = (byte)(P0[b2] ^ (byte)(k1 >> 16)); - b3 = (byte)(P1[b3] ^ (byte)(k1 >> 24)); - - // Second layer: P_01=0(P0), P_11=0(P0), P_21=1(P1), P_31=1(P1) - b0 = (byte)(P0[b0] ^ (byte)k0); - b1 = (byte)(P0[b1] ^ (byte)(k0 >> 8)); - b2 = (byte)(P1[b2] ^ (byte)(k0 >> 16)); - b3 = (byte)(P1[b3] ^ (byte)(k0 >> 24)); - - // Final layer: P_00=1(P1), P_10=0(P0), P_20=1(P1), P_30=0(P0) - // MDS matrix multiply - return MdsMultiply(P1[b0], P0[b1], P1[b2], P0[b3]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint F32(uint x, uint k0, uint k2) - { - var b0 = (byte)x; - var b1 = (byte)(x >> 8); - var b2 = (byte)(x >> 16); - var b3 = (byte)(x >> 24); - - // First layer: P_02=0(P0), P_12=1(P1), P_22=0(P0), P_32=1(P1) - b0 = (byte)(P0[b0] ^ (byte)k2); - b1 = (byte)(P1[b1] ^ (byte)(k2 >> 8)); - b2 = (byte)(P0[b2] ^ (byte)(k2 >> 16)); - b3 = (byte)(P1[b3] ^ (byte)(k2 >> 24)); - - // Second layer: P_01=0(P0), P_11=0(P0), P_21=1(P1), P_31=1(P1) - b0 = (byte)(P0[b0] ^ (byte)k0); - b1 = (byte)(P0[b1] ^ (byte)(k0 >> 8)); - b2 = (byte)(P1[b2] ^ (byte)(k0 >> 16)); - b3 = (byte)(P1[b3] ^ (byte)(k0 >> 24)); - - // Final layer: P_00=1(P1), P_10=0(P0), P_20=1(P1), P_30=0(P0) - return MdsMultiply(P1[b0], P0[b1], P1[b2], P0[b3]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint MdsMultiply(byte b0, byte b1, byte b2, byte b3) - { - // MDS matrix multiplication (Galois Field 2^8) - var m0 = (uint)(b0 ^ Lfsr2(b1) ^ Lfsr1(b2) ^ Lfsr1(b3)); - var m1 = (uint)(Lfsr1(b0) ^ Lfsr2(b1) ^ Lfsr2(b2) ^ b3); - var m2 = (uint)(Lfsr2(b0) ^ Lfsr1(b1) ^ b2 ^ Lfsr2(b3)); - var m3 = (uint)(Lfsr2(b0) ^ b1 ^ Lfsr2(b2) ^ Lfsr1(b3)); - - return m0 | (m1 << 8) | (m2 << 16) | (m3 << 24); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Lfsr1(int val) => val ^ Lfsr4(val); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Lfsr2(int val) => val ^ Lfsr3(val) ^ Lfsr4(val); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Lfsr3(int val) => (val >> 1) ^ ((val & 0x01) == 0x01 ? MdsGfFdbk / 2 : 0); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Lfsr4(int val) => - (val >> 2) ^ ((val & 0x02) == 0x02 ? MdsGfFdbk / 2 : 0) ^ ((val & 0x01) == 0x01 ? MdsGfFdbk / 4 : 0); - - private static uint RsMdsEncode(uint k0, uint k1) - { - uint r = 0; - for (var i = 0; i < 2; i++) - { - r ^= i > 0 ? k0 : k1; - - for (var j = 0; j < 4; j++) - { - var v1 = (byte)(r >> 24); - var v2 = (uint)(((v1 << 1) ^ ((v1 & 0x80) == 0x80 ? RsGfFdbk : 0)) & 0xFF); - var v3 = (uint)(((v1 >> 1) & 0x7F) ^ ((v1 & 1) == 1 ? RsGfFdbk >> 1 : 0) ^ v2); - - r = (r << 8) ^ (v3 << 24) ^ (v2 << 16) ^ (v3 << 8) ^ v1; - } - } - - return r; - } -} diff --git a/Projects/UOContent/Network/Firewall/BaseFirewallEntry.cs b/Projects/Server/Network/Firewall/BaseFirewallEntry.cs similarity index 97% rename from Projects/UOContent/Network/Firewall/BaseFirewallEntry.cs rename to Projects/Server/Network/Firewall/BaseFirewallEntry.cs index a44fffe6b..9834555ad 100644 --- a/Projects/UOContent/Network/Firewall/BaseFirewallEntry.cs +++ b/Projects/Server/Network/Firewall/BaseFirewallEntry.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BaseFirewallEntry.cs * * * diff --git a/Projects/UOContent/Network/Firewall/CidrFirewallEntry.cs b/Projects/Server/Network/Firewall/CidrFirewallEntry.cs similarity index 68% rename from Projects/UOContent/Network/Firewall/CidrFirewallEntry.cs rename to Projects/Server/Network/Firewall/CidrFirewallEntry.cs index 06527ab71..fc6467387 100644 --- a/Projects/UOContent/Network/Firewall/CidrFirewallEntry.cs +++ b/Projects/Server/Network/Firewall/CidrFirewallEntry.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CidrFirewallEntry.cs * * * @@ -25,15 +25,8 @@ public class CidrFirewallEntry : BaseFirewallEntry public override UInt128 MaxIpAddress { get; } public CidrFirewallEntry(string ipAddressOrCidr) + : this(ParseIPAddress(ipAddressOrCidr, out var prefixLength), prefixLength) { - // Core owns the CIDR -> normalized range parse. - if (!IPAddressUtility.TryParseCidrRange(ipAddressOrCidr, out var min, out var max)) - { - throw new ArgumentException("Invalid IP address or CIDR.", nameof(ipAddressOrCidr)); - } - - MinIpAddress = min; - MaxIpAddress = max; } public CidrFirewallEntry(IPAddress minAddress, IPAddress maxAddress) @@ -57,4 +50,26 @@ public class CidrFirewallEntry : BaseFirewallEntry MaxIpAddress = Utility.CreateCidrAddress(bytes, prefixLength, true); } + private static IPAddress ParseIPAddress(ReadOnlySpan ipString, out int prefixLength) + { + int slashIndex = ipString.IndexOf('/'); + var ipAddress = IPAddress.Parse(slashIndex > -1 ? ipString[..slashIndex] : ipString); + var maxPrefixLength = ipAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32; + + if (slashIndex == -1) + { + prefixLength = maxPrefixLength; + } + else + { + var prefixPart = ipString[(slashIndex + 1)..]; + + if (!int.TryParse(prefixPart, out prefixLength) || prefixLength < 0 || prefixLength > maxPrefixLength) + { + throw new ArgumentException("Invalid prefix length."); + } + } + + return ipAddress; + } } diff --git a/Projects/Server/Network/Firewall/Firewall.cs b/Projects/Server/Network/Firewall/Firewall.cs new file mode 100644 index 000000000..e80050416 --- /dev/null +++ b/Projects/Server/Network/Firewall/Firewall.cs @@ -0,0 +1,111 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2024 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Firewall.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Net; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Server.Network; + +public static class Firewall +{ + private static InternalValidationEntry _validationEntry; + private static readonly Dictionary _isBlockedCache = new(); + + private static readonly SortedSet _firewallSet = new(); + + public static SortedSet FirewallSet => _firewallSet; + + internal static bool IsBlocked(IPAddress address) + { + ref var isBlocked = ref CollectionsMarshal.GetValueRefOrAddDefault(_isBlockedCache, address, out var exists); + if (exists) + { + return isBlocked; + } + + if (_validationEntry == null) + { + _validationEntry = new InternalValidationEntry(address); + } + else + { + _validationEntry.Address = address; + } + + // Get all entries that are lower than our validation entry + var view = _firewallSet.GetViewBetween(_firewallSet.Min, _validationEntry); + + // Loop backward since there shouldn't be any entries where the Min address is higher than ours + foreach (var firewallEntry in view.Reverse()) + { + if (firewallEntry.IsBlocked(_validationEntry.MinIpAddress)) + { + isBlocked = true; + return true; + } + } + + isBlocked = view.Max?.IsBlocked(_validationEntry.MinIpAddress) == true; + + return isBlocked; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Add(IFirewallEntry firewallEntry) + { + if (_firewallSet.Add(firewallEntry)) + { + _isBlockedCache.Clear(); + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Remove(IFirewallEntry entry) + { + if (entry == null) + { + return false; + } + + if (_firewallSet.Remove(entry)) + { + _isBlockedCache.Clear(); + return true; + } + + return false; + } + + private class InternalValidationEntry : BaseFirewallEntry + { + private UInt128 _address; + + public IPAddress Address + { + set => _address = value.ToUInt128(); + } + + public override UInt128 MinIpAddress => _address; + public override UInt128 MaxIpAddress => _address; + + public InternalValidationEntry(IPAddress ipAddress) => Address = ipAddress; + } +} diff --git a/Projects/UOContent/Network/Firewall/IFirewallEntry.cs b/Projects/Server/Network/Firewall/IFirewallEntry.cs similarity index 96% rename from Projects/UOContent/Network/Firewall/IFirewallEntry.cs rename to Projects/Server/Network/Firewall/IFirewallEntry.cs index 7baca0603..4a68f2441 100644 --- a/Projects/UOContent/Network/Firewall/IFirewallEntry.cs +++ b/Projects/Server/Network/Firewall/IFirewallEntry.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IFirewallEntry.cs * * * @@ -40,12 +40,12 @@ public interface IFirewallEntry : IComparable return 1; } - if (MaxIpAddress > other.MaxIpAddress) + if (MaxIpAddress < other.MaxIpAddress) { return -1; } - if (MaxIpAddress < other.MaxIpAddress) + if (MaxIpAddress > other.MaxIpAddress) { return 1; } diff --git a/Projects/UOContent/Network/Firewall/SingleIpFirewallEntry.cs b/Projects/Server/Network/Firewall/SingleIpFirewallEntry.cs similarity index 95% rename from Projects/UOContent/Network/Firewall/SingleIpFirewallEntry.cs rename to Projects/Server/Network/Firewall/SingleIpFirewallEntry.cs index 00c5f6c11..8b040b5fd 100644 --- a/Projects/UOContent/Network/Firewall/SingleIpFirewallEntry.cs +++ b/Projects/Server/Network/Firewall/SingleIpFirewallEntry.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SingleIpFirewallEntry.cs * * * diff --git a/Projects/Server/Network/ForeignProtocol.cs b/Projects/Server/Network/ForeignProtocol.cs deleted file mode 100644 index 059a69d3f..000000000 --- a/Projects/Server/Network/ForeignProtocol.cs +++ /dev/null @@ -1,146 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ForeignProtocol.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Network; - -public enum ForeignProtocolKind -{ - None, - Http, - Tls, - Ssh -} - -public enum ForeignProtocolMatch -{ - None, - - /// A prefix matched, but more bytes are needed to be sure. - Incomplete, - - Confirmed -} - -/// -/// Identifies traffic that is positively some OTHER protocol (HTTP, TLS, SSH), rather than deciding whether -/// a connection is a good Ultima Online client. -/// -/// -/// The direction matters. A legitimate client with encryption enabled when the shard expects none sends a -/// structurally correct connection whose payload is noise, because LoginEncryption.ClientDecrypt is a -/// byte-for-byte stream XOR: length survives, content does not. So "unreadable" cannot mean "hostile", while -/// "speaks HTTP" safely can. Nothing here assumes arrival framing; see -/// dev-docs/ip-bans-and-allowlists.md. -/// -public static class ForeignProtocol -{ - private const int RequiredBytes = 8; - private const int MaxTlsRecordLength = 16384; - - public static ForeignProtocolMatch Identify(ReadOnlySpan buffer, out ForeignProtocolKind kind) - { - kind = ForeignProtocolKind.None; - - // Too little to match a prefix; the caller's own short-read handling covers it. - if (buffer.Length < 4) - { - return ForeignProtocolMatch.None; - } - - var candidate = MatchPrefix(buffer); - if (candidate == ForeignProtocolKind.None) - { - return ForeignProtocolMatch.None; - } - - if (buffer.Length < RequiredBytes) - { - return ForeignProtocolMatch.Incomplete; - } - - if (!Confirm(buffer, candidate)) - { - return ForeignProtocolMatch.None; - } - - kind = candidate; - return ForeignProtocolMatch.Confirmed; - } - - private static ForeignProtocolKind MatchPrefix(ReadOnlySpan buffer) - { - // TLS handshake record: content type 0x16, major version 3, minor version 0..4 (SSL 3.0 - TLS 1.3). - if (buffer[0] == 0x16 && buffer[1] == 0x03 && buffer[2] <= 0x04) - { - return ForeignProtocolKind.Tls; - } - - if (StartsWith(buffer, "SSH-")) - { - return ForeignProtocolKind.Ssh; - } - - // HTTP request methods. Four bytes only selects a candidate; Confirm checks the request line. - if (StartsWith(buffer, "GET ") || StartsWith(buffer, "POST") || StartsWith(buffer, "HEAD") || - StartsWith(buffer, "PUT ") || StartsWith(buffer, "OPTI") || StartsWith(buffer, "DELE") || - StartsWith(buffer, "CONN") || StartsWith(buffer, "TRAC") || StartsWith(buffer, "PATC")) - { - return ForeignProtocolKind.Http; - } - - return ForeignProtocolKind.None; - } - - private static bool Confirm(ReadOnlySpan buffer, ForeignProtocolKind candidate) - { - if (candidate == ForeignProtocolKind.Tls) - { - // A seed can collide with the 0x16 0x03 0x0? prefix (that is just the address 22.3.x.x), so - // require a ClientHello inside a plausible record. - var recordLength = (buffer[3] << 8) | buffer[4]; - return buffer[5] == 0x01 && recordLength is >= 4 and <= MaxTlsRecordLength; - } - - // A UO client's fifth byte is a packet id (0x80, 0x91, 0xEF), none of them printable, so requiring - // the request line to continue in ASCII lets a seed that spells "GET " fall through. - for (var i = 4; i < buffer.Length && i < 16; i++) - { - if (!IsPrintableAscii(buffer[i])) - { - return false; - } - } - - return true; - } - - private static bool IsPrintableAscii(byte value) => - value is >= 0x20 and <= 0x7E or (byte)'\r' or (byte)'\n' or (byte)'\t'; - - private static bool StartsWith(ReadOnlySpan buffer, string ascii) - { - for (var i = 0; i < ascii.Length; i++) - { - if (buffer[i] != (byte)ascii[i]) - { - return false; - } - } - - return true; - } -} diff --git a/Projects/Server/Network/IConnectionFilter.cs b/Projects/Server/Network/IConnectionFilter.cs deleted file mode 100644 index 48e971c85..000000000 --- a/Projects/Server/Network/IConnectionFilter.cs +++ /dev/null @@ -1,60 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: IConnectionFilter.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Net; -using System.Threading; - -namespace Server.Network; - -/// -/// A gate consulted for every inbound connection, before the socket is configured and before any -/// per-connection allocation. Implementations decide membership only — the accept path neither knows -/// nor cares where a filter's data comes from, so a filter may be a handful of admin-curated entries, -/// a millions-strong list hydrated from a file, or a query against something else entirely. Core owns -/// the question; content owns every answer (see Firewall and BlocklistFilter in UOContent). -/// -/// -/// -/// runs on the game loop once per accepted socket, which is the path that has -/// to survive a DDoS. Implementations MUST be allocation-free and O(log n) at worst, MUST NOT perform -/// I/O, and MUST NOT block. Anything expensive (parsing, reloading, reporting to an external service) -/// belongs off the loop or behind a bounded, non-blocking enqueue. -/// -/// -/// Side effects that a hit implies (contributing to BanChannel, promoting to an OS firewall, -/// suppressing duplicate reports) are the filter's own business, not the accept path's. This is why -/// returns a bare bool: the accept path asks one question and does one thing. -/// -/// -public interface IConnectionFilter -{ - /// Stable id for logging/config (e.g. firewall, blocklist). - string Name { get; } - - /// Reads configuration. Called by . No I/O beyond config. - void Register(); - - /// Starts any background hydration. The token is cancelled on shutdown. - void Start(CancellationToken token); - - /// Flushes and tears down. Called during shutdown. - void Stop(); - - /// - /// True to deny the connection. Must be allocation-free and non-blocking; see the remarks on - /// . - /// - bool ShouldDeny(IPAddress address); -} diff --git a/Projects/Server/Network/IPLimiter.cs b/Projects/Server/Network/IPLimiter.cs new file mode 100644 index 000000000..dc4717645 --- /dev/null +++ b/Projects/Server/Network/IPLimiter.cs @@ -0,0 +1,108 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2024 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: IPLimiter.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Net; + +namespace Server.Misc; + +public static class IPLimiter +{ + private static readonly SortedSet _connectionAttempts = []; + private static readonly SortedSet _throttledAddresses = []; + + private static readonly IPAddress _localHost = IPAddress.Parse("127.0.0.1"); + + public static TimeSpan ConnectionAttemptsDuration { get; private set; } + public static TimeSpan ConnectionThrottleDuration { get; private set; } + + public static bool Enabled { get; private set; } + public static int MaxConnections { get; private set; } + + public static void Configure() + { + Enabled = ServerConfiguration.GetOrUpdateSetting("ipLimiter.enable", true); + MaxConnections = ServerConfiguration.GetOrUpdateSetting("ipLimiter.maxConnectionsPerIP", 5); + ConnectionAttemptsDuration = ServerConfiguration.GetOrUpdateSetting("ipLimiter.clearConnectionAttemptsDuration", TimeSpan.FromSeconds(10)); + ConnectionThrottleDuration = ServerConfiguration.GetOrUpdateSetting("ipLimiter.connectionThrottleDuration", TimeSpan.FromMinutes(5)); + } + + private static readonly IPAccessLog _accessCheck = new(IPAddress.None, DateTime.MinValue); + + public static bool Verify(IPAddress ourAddress) + { + if (!Enabled || ourAddress.Equals(_localHost)) + { + return true; + } + + var now = Core.Now; + + IPAccessLog accessLog; + + while (_throttledAddresses.Count > 0) + { + accessLog = _throttledAddresses.Min; + if (now <= accessLog.Expiration) + { + break; + } + + _throttledAddresses.Remove(accessLog); + } + + _accessCheck.IPAddress = ourAddress; + + if (_connectionAttempts.TryGetValue(_accessCheck, out accessLog)) + { + _connectionAttempts.Remove(accessLog); + accessLog.Count++; + accessLog.Expiration = now + ConnectionAttemptsDuration; + + if (now <= accessLog.Expiration && accessLog.Count >= MaxConnections) + { + _throttledAddresses.Add(accessLog); + return false; + } + } + else + { + accessLog = new IPAccessLog(ourAddress, now + ConnectionAttemptsDuration); + } + + // Add it back so it is sorted properly + _connectionAttempts.Add(accessLog); + + return true; + } + + private class IPAccessLog : IComparable + { + public IPAddress IPAddress; + public DateTime Expiration; + public int Count; + + public IPAccessLog(IPAddress ipAddress, DateTime expiration) + { + IPAddress = ipAddress; + Expiration = expiration; + Count = 1; + } + + public int CompareTo(IPAccessLog other) => + IPAddress.Equals(other.IPAddress) ? 0 : Expiration.CompareTo(other.Expiration); + } +} diff --git a/Projects/Server/Network/IPRateLimiter.cs b/Projects/Server/Network/IPRateLimiter.cs deleted file mode 100644 index 05ede12c4..000000000 --- a/Projects/Server/Network/IPRateLimiter.cs +++ /dev/null @@ -1,203 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: IPRateLimiter.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Concurrent; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace Server.Network; - -public class IPRateLimiter -{ - private static readonly ConcurrentQueue _statsPool = []; - private const int MaxPoolSize = 32_768; - - private readonly SemaphoreSlim _cleanupSignal = new(0, 1); - private readonly ConcurrentDictionary _ipAttempts; - private readonly ConcurrentQueue _cleanupQueue; - private readonly CancellationTokenSource _cts; - - private readonly int _maxAttempts; - private readonly long _timeWindow; // milliseconds - private readonly long _initialBackoff; // milliseconds - private readonly double _backoffMultiplier; - private readonly long _maxBackoff; // milliseconds - - public IPRateLimiter( - int maxAttempts, long timeWindow, long initialBackoff, double backoffMultiplier, long maxBackoff, - CancellationToken token - ) - { - _ipAttempts = []; - _cleanupQueue = []; - _maxAttempts = maxAttempts; - _timeWindow = timeWindow; - _initialBackoff = initialBackoff; - _backoffMultiplier = backoffMultiplier; - _maxBackoff = maxBackoff; - _cts = CancellationTokenSource.CreateLinkedTokenSource(token); - - Task.Run(CleanupLoop, Core.ClosingTokenSource.Token); - } - - public bool Verify(IPAddress ip, out int totalAttempts) - { - if (ip.IsPrivateNetwork()) - { - totalAttempts = 0; - return true; - } - - var nowTicks = Core.TickCount; - var ipStats = _ipAttempts.GetOrAdd(ip, _ => GetOrCreateIPStats()); - var added = ipStats.AttemptCount == 0; - - lock (ipStats) - { - if (nowTicks - ipStats.LastAttemptTicks > _timeWindow) - { - ipStats.AttemptCount = 1; // Reset - } - else - { - ipStats.AttemptCount++; - } - - totalAttempts = ipStats.AttemptCount; - ipStats.LastAttemptTicks = nowTicks; - - if (ipStats.BlockUntilTicks - nowTicks > 0) - { - return false; - } - - if (ipStats.AttemptCount > _maxAttempts) - { - var backoffTime = Math.Min( - (long)(_initialBackoff * Math.Pow(_backoffMultiplier, ipStats.AttemptCount - _maxAttempts)), - _maxBackoff - ); - - ipStats.BlockUntilTicks = nowTicks + backoffTime; - return false; - } - - if (added) - { - _cleanupQueue.Enqueue(ip); - RunCleanup(); - } - } - - return true; - } - - private static IPStats GetOrCreateIPStats() => _statsPool.TryDequeue(out var stats) ? stats : new IPStats(); - - private static void ReturnToPool(IPStats stats) - { - stats.Reset(); - - if (_statsPool.Count < MaxPoolSize) - { - _statsPool.Enqueue(stats); - } - } - - private void RunCleanup() - { - if (_cleanupSignal.CurrentCount > 0) - { - return; - } - - try - { - _cleanupSignal.Release(); - Task.Run(CleanupLoop, _cts.Token); - } - catch - { - // Do nothing - } - } - - private async ValueTask CleanupLoop() - { - while (!_cts.IsCancellationRequested) - { - await _cleanupSignal.WaitAsync(_cts.Token); - - var maxToProcess = Math.Min(_cleanupQueue.Count, 500); - var nowTicks = Core.TickCount; - - for (var i = 0; i < maxToProcess; i++) - { - if (!_cts.IsCancellationRequested) - { - break; - } - - if (!_cleanupQueue.TryDequeue(out var ip) || !_ipAttempts.TryGetValue(ip, out var ipStats)) - { - continue; - } - - lock (ipStats) - { - if (nowTicks - ipStats.LastAttemptTicks < _timeWindow) - { - _cleanupQueue.Enqueue(ip); - continue; - } - - if (_ipAttempts.TryRemove(ip, out _)) - { - ReturnToPool(ipStats); - } - } - } - - if (!_cleanupQueue.IsEmpty) - { - await Task.Delay(TimeSpan.FromMinutes(1), _cts.Token); - try - { - _cleanupSignal.Release(); - } - catch - { - // Do nothing - } - } - } - } - - private class IPStats - { - public int AttemptCount; - public long LastAttemptTicks; - public long BlockUntilTicks; - - public void Reset() - { - AttemptCount = 0; - LastAttemptTicks = 0; - BlockUntilTicks = 0; - } - } -} diff --git a/Projects/Server/Network/MovementThrottle.cs b/Projects/Server/Network/MovementThrottle.cs index c28ef2228..22ce9c1e4 100644 --- a/Projects/Server/Network/MovementThrottle.cs +++ b/Projects/Server/Network/MovementThrottle.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MovementThrottle.cs * * * @@ -14,1142 +14,56 @@ *************************************************************************/ using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Server.Logging; namespace Server.Network; -/// -/// Movement throttle system using a hybrid credit + queue approach. -/// Credit buffer absorbs small timing jitter from legitimate players. -/// Queue handles larger bursts, draining at proper intervals. -/// Speed hacks detected by movement rate analysis and sustained queue depth. -/// public static class MovementThrottle { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(MovementThrottle)); - - // Configuration values - private static int _maxCredit = 200; // Max credit buffer (ms) - private static int _maxRttBonus = 150; // Max extra credit for high-latency players (ms) - private static int _hardQueueLimit = 10; // Reject and clear at this limit - - // Movement history configuration - private static int _movementHistorySize = 20; // Circular buffer size - private static int _minSamplesForRate = 8; // Minimum movements to calculate rate - private static int _maxChainGap = 2000; // Gap (ms) that breaks a movement chain - private static float _suspiciousRateThreshold = 1.05f; // 5% faster than expected - private static float _definiteRateThreshold = 1.10f; // 10% faster than expected - private static int _speedHackNotificationCooldown = 300000; // 5 minutes between notifications per player - - // Client movement limits (UO protocol constants) - // The client can have at most 5 unacknowledged movements pending. - // This means our server-side queue should never exceed 4 with an unmodified client. - // Queue > 4 indicates client modification (not just speed hack checkbox in Cheat Engine). - private const int ClientMaxUnackedMovements = 5; - private const int MaxQueueWithUnmodifiedClient = ClientMaxUnackedMovements - 1; // 4 - - // Debug logging - enable for testing speed hack detection - private static bool _debugLogging = false; - - // Track NetStates with queued movements for efficient processing - private static readonly HashSet _netStatesWithQueuedMovements = new(256); - - /// - /// Gets the dynamic credit buffer for a connection based on measured RTT. - /// High-latency players get more tolerance; stable low-latency gets tighter security. - /// - private static int GetDynamicCredit(NetState ns) - { - var avgRtt = ns.AverageRtt; - - // No RTT data yet - use default - if (avgRtt <= 0) - { - return _maxCredit; - } - - // Stable, low-latency connection - use base credit (tighter security) - if (ns.HasStableConnection && avgRtt < 50) - { - return _maxCredit; - } - - // Add RTT-based bonus for higher latency, capped at max bonus - var rttBonus = Math.Min(avgRtt / 2, _maxRttBonus); - return _maxCredit + (int)rttBonus; - } + private static long _movementThrottleReset; // 1 second + private static long _throttleThreshold; // 400 milliseconds public static void Configure() { - _maxCredit = ServerConfiguration.GetOrUpdateSetting( - "movementThrottle.maxCredit", - _maxCredit - ); - - _hardQueueLimit = ServerConfiguration.GetOrUpdateSetting( - "movementThrottle.hardQueueLimit", - _hardQueueLimit - ); - - _movementHistorySize = ServerConfiguration.GetOrUpdateSetting( - "movementThrottle.movementHistorySize", - _movementHistorySize - ); - - _minSamplesForRate = ServerConfiguration.GetOrUpdateSetting( - "movementThrottle.minSamplesForRate", - _minSamplesForRate - ); - - _suspiciousRateThreshold = (float)ServerConfiguration.GetOrUpdateSetting( - "movementThrottle.suspiciousRateThreshold", - _suspiciousRateThreshold - ); - - _definiteRateThreshold = (float)ServerConfiguration.GetOrUpdateSetting( - "movementThrottle.definiteRateThreshold", - _definiteRateThreshold - ); - - _debugLogging = ServerConfiguration.GetOrUpdateSetting( - "movementThrottle.debugLogging", - _debugLogging - ); + _movementThrottleReset = ServerConfiguration.GetOrUpdateSetting("movement.throttleReset", 1000); + _throttleThreshold = ServerConfiguration.GetOrUpdateSetting("movement.throttleThreshold", 400); } - /// - /// Called from MovementReq packet handler. Validates timing and either - /// executes the movement immediately or queues it for later execution. - /// - public static void ValidateAndQueueMovement(NetState ns, Mobile mobile, Direction dir, int seq) + public static unsafe void Initialize() { - if (mobile?.Deleted != false) - { - return; - } - - // Staff bypass all throttling; also bypass when speedhack detection is disabled - if (mobile.AccessLevel > AccessLevel.Player || !ServerFeatureFlags.SpeedhackDetection) - { - ExecuteMovement(ns, mobile, dir, seq); - return; - } - - // Check for sequence mismatch (sequence was reset by paralysis, teleport, etc.) - if (ns.Sequence == 0 && seq != 0) - { - RejectAndReset(ns, mobile, seq); - return; - } - - var now = Core.TickCount; - - // Track movement packet rate (packets per second) - ns.TrackMovementRate(); - - // RTT probe - only when actively moving (event-driven, not global loop) - ns.MaybeSendRttProbe(); - - // Calculate movement cost - this has FULL CONTEXT (mounted, running, direction change) - var cost = mobile.ComputeMovementSpeed(dir); - - // Record movement in history for rate analysis (do this early, before any returns) - RecordMovement(ns, now, cost, dir, mobile); - - // Periodic rate-based detection (every 2 seconds, regardless of queue depth) - if (now - ns._lastQueueDepthCheck >= 2000) - { - ns._lastQueueDepthCheck = now; - var verdict = CheckAndNotifyStaff(ns); - - // Adjust probe frequency based on detection verdict - ns.SetProbeFrequency((int)verdict); - } - - // Calculate timing delta: positive = on-time/late, negative = early - var delta = now - ns._nextMovementTime; - - // If there are already queued movements, add to queue to maintain order - if (ns._hasQueuedMovements) - { - QueueMovement(ns, dir, seq); - return; - } - - // Get dynamic credit limit based on connection latency - var dynamicCredit = GetDynamicCredit(ns); - - // Handle early packets with credit buffer - if (delta < 0) - { - // Packet arrived early - var earlyAmount = -delta; - - // Can we absorb this with credit? - // Credit can go negative up to -dynamicCredit (debt limit) - if (ns._movementCredit - earlyAmount >= -dynamicCredit) - { - var prevCredit = ns._movementCredit; - // Use credit to cover early arrival - ns._movementCredit -= earlyAmount; - - if (_debugLogging && ns._movementLogging) - { - logger.Debug( - "[Credit] {Name}: delta={Delta}ms early={Early}ms credit={PrevCredit}->{Credit}/{MaxCredit} action=execute", - mobile.RawName, delta, earlyAmount, prevCredit, ns._movementCredit, dynamicCredit - ); - } - - // Execute immediately since credit covers it - ExecuteMovement(ns, mobile, dir, seq); - return; - } - - if (_debugLogging && ns._movementLogging) - { - logger.Debug( - "[Credit] {Name}: delta={Delta}ms early={Early}ms credit={Credit}/{MaxCredit} EXHAUSTED -> queue", - mobile.RawName, delta, earlyAmount, ns._movementCredit, dynamicCredit - ); - } - - // Credit exhausted - must queue - QueueMovement(ns, dir, seq); - return; - } - - // On-time or late - rebuild credit (capped at dynamic max) - if (delta > 0) - { - var prevCredit = ns._movementCredit; - ns._movementCredit = Math.Min(ns._movementCredit + delta, dynamicCredit); - - if (_debugLogging && ns._movementLogging && ns._movementCredit != prevCredit) - { - logger.Debug( - "[Credit] {Name}: delta=+{Delta}ms credit={PrevCredit}->{Credit}/{MaxCredit} action=execute", - mobile.RawName, delta, prevCredit, ns._movementCredit, dynamicCredit - ); - } - } - - // Execute immediately - ExecuteMovement(ns, mobile, dir, seq); + IncomingPackets.RegisterThrottler(0x02, &Throttle); } - /// - /// Executes a single movement and updates state. - /// - private static void ExecuteMovement(NetState ns, Mobile mobile, Direction dir, int seq) + public static bool Throttle(int packetId, NetState ns) { - if (!mobile.Move(dir)) - { - if (_debugLogging && ns._movementLogging) - { - logger.Debug( - "[Execute] {Name}: Move FAILED dir={Dir} seq={Seq} -> reject+reset", - mobile.RawName, dir, seq - ); - } + var from = ns.Mobile; - // Movement failed (blocked, paralyzed, frozen, etc.) - RejectAndReset(ns, mobile, seq); - return; + if (from?.Deleted != false || from.AccessLevel > AccessLevel.Player) + { + return false; } - if (_debugLogging && ns._movementLogging) + long now = Core.TickCount; + long credit = ns._movementCredit; + long nextMove = ns._nextMovementTime; + + // Reset system if idle for more than 1 second + if (now - nextMove + _movementThrottleReset > 0) { - logger.Debug( - "[Execute] {Name}: Move OK dir={Dir} seq={Seq} nextMove={NextMove}ms", - mobile.RawName, dir, seq, ns._nextMovementTime - Core.TickCount - ); + ns._movementCredit = 0; + ns._nextMovementTime = now; + return false; } - // Success - Mobile.Move() already updated _nextMovementTime and sent ack - // Update sequence - var newSeq = seq + 1; - if (newSeq == 256) - { - newSeq = 1; - } - ns.Sequence = newSeq; - } + long cost = nextMove - now; - /// - /// Queues a movement for later execution. - /// - private static void QueueMovement(NetState ns, Direction dir, int seq) - { - // Lazy initialize queue - ns._movementQueue ??= new Queue(_hardQueueLimit); - - // Check hard limit - if (ns._movementQueue.Count >= _hardQueueLimit) + if (credit < cost) { - LogQueueOverflow(ns); - RejectAndReset(ns, ns.Mobile, seq); - return; + // Not enough credit, therefore throttled + return true; } - // Enqueue - ns._movementQueue.Enqueue(new NetState.QueuedMovement - { - Direction = dir, - Sequence = seq - }); - - ns._hasQueuedMovements = true; - _netStatesWithQueuedMovements.Add(ns); - - if (_debugLogging && ns._movementLogging) - { - logger.Debug( - "[Queue] {Name}: enqueued dir={Dir} seq={Seq} (depth={Depth})", - ns.Mobile?.RawName, dir, seq, ns._movementQueue.Count - ); - } - } - - /// - /// Rejects a movement and resets movement state. - /// - private static void RejectAndReset(NetState ns, Mobile mobile, int seq) - { - ns.SendMovementRej(seq, mobile); - ns.ResetMovementState(); - _netStatesWithQueuedMovements.Remove(ns); - } - - /// - /// Processes queued movements for all NetStates. Called from NetState.Slice(). - /// - public static void ProcessAllQueues() - { - if (_netStatesWithQueuedMovements.Count == 0) - { - return; - } - - // Process each NetState with queued movements - // Use a snapshot to avoid modification during iteration - var toProcess = new List(_netStatesWithQueuedMovements); - - for (var i = 0; i < toProcess.Count; i++) - { - var ns = toProcess[i]; - if (!ns.Running) - { - _netStatesWithQueuedMovements.Remove(ns); - continue; - } - - ProcessMovementQueue(ns); - } - } - - /// - /// Processes the movement queue for a single NetState. - /// - public static void ProcessMovementQueue(NetState ns) - { - var mobile = ns.Mobile; - if (mobile?.Deleted != false) - { - ClearQueue(ns); - return; - } - - // Staff don't queue - if (mobile.AccessLevel > AccessLevel.Player) - { - DrainQueueImmediately(ns, mobile); - return; - } - - var now = Core.TickCount; - - while (ns._movementQueue?.Count > 0) - { - // Check if it's time to execute - if (now < ns._nextMovementTime) - { - // Not yet - leave remaining items in queue for next Slice - break; - } - - var movement = ns._movementQueue.Dequeue(); - var remaining = ns._movementQueue.Count; - - // Validate sequence - if (ns.Sequence == 0 && movement.Sequence != 0) - { - // Sequence was reset (paralysis, teleport, etc.) - RejectAndReset(ns, mobile, movement.Sequence); - return; - } - - // Execute the move - if (!mobile.Move(movement.Direction)) - { - if (_debugLogging && ns._movementLogging) - { - logger.Debug( - "[Queue] {Name}: dequeued FAILED dir={Dir} (remaining={Remaining})", - mobile.RawName, movement.Direction, remaining - ); - } - - // Movement failed - RejectAndReset(ns, mobile, movement.Sequence); - return; - } - - if (_debugLogging && ns._movementLogging) - { - var waited = now - ns._nextMovementTime; - logger.Debug( - "[Queue] {Name}: dequeued OK dir={Dir} (remaining={Remaining}, waited={Waited}ms)", - mobile.RawName, movement.Direction, remaining, waited >= 0 ? waited : 0 - ); - } - - // Success - update sequence - var newSeq = movement.Sequence + 1; - if (newSeq == 256) - { - newSeq = 1; - } - ns.Sequence = newSeq; - - // Refresh time for next iteration - now = Core.TickCount; - } - - // Update tracking - ns._hasQueuedMovements = ns._movementQueue?.Count > 0; - if (!ns._hasQueuedMovements) - { - _netStatesWithQueuedMovements.Remove(ns); - } - } - - /// - /// Drains the entire queue immediately for staff members. - /// - private static void DrainQueueImmediately(NetState ns, Mobile mobile) - { - while (ns._movementQueue?.Count > 0) - { - var movement = ns._movementQueue.Dequeue(); - - if (!mobile.Move(movement.Direction)) - { - RejectAndReset(ns, mobile, movement.Sequence); - return; - } - - var newSeq = movement.Sequence + 1; - if (newSeq == 256) - { - newSeq = 1; - } - ns.Sequence = newSeq; - } - - ClearQueue(ns); - } - - /// - /// Clears the movement queue for a NetState. - /// - private static void ClearQueue(NetState ns) - { - ns._movementQueue?.Clear(); - ns._hasQueuedMovements = false; - _netStatesWithQueuedMovements.Remove(ns); - } - - // Maximum expected packets per second (mounted running = 100ms = 10/sec, plus tolerance) - private const int MaxExpectedPacketRate = 12; - - /// - /// Logs when a queue overflow occurs (hard limit reached). - /// - private static void LogQueueOverflow(NetState ns) - { - var mobile = ns.Mobile; - logger.Information( - "Movement queue overflow: {Character} ({Account}) | " + - "Queue reached hard limit: {Limit} | IP: {IP}", - mobile?.RawName ?? "Unknown", - ns.Account?.Username ?? "Unknown", - _hardQueueLimit, - ns.Address - ); - } - - /// - /// Resets movement timing for all connected players after a world save. - /// This prevents post-save burst rejections. - /// - public static void ResetAllMovementTiming() - { - foreach (var ns in NetState.Instances) - { - if (ns.Mobile?.Deleted == false) - { - ns._nextMovementTime = Core.TickCount; - ns._movementCredit = _maxCredit; // Give full credit after save - } - } - } - - /// - /// Records a movement in the history buffer for rate analysis. - /// This is a hot path - optimized for minimal allocations and branches. - /// Skips recording for chain-breaking movements (first in sequence, long gaps). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void RecordMovement(NetState ns, long now, int cost, Direction dir, Mobile mobile) - { - // Calculate interval since last movement - var interval = ns._lastMovementRecordTime > 0 - ? (int)(now - ns._lastMovementRecordTime) - : -1; // -1 indicates first movement (no previous time) - - // Skip recording for chain-breaking movements - they waste buffer space - // and get skipped during analysis anyway. Still update timestamp for next movement. - if (interval <= 0 || interval > _maxChainGap) - { - ns._lastMovementRecordTime = now; - - // Use RTT to distinguish "stopped moving" vs "lagged" - // - Stable low-latency connection with gap >> RTT → player stopped, reset history - // - Unstable/high-latency connection with gap → might be lag, preserve history - if (interval > _maxChainGap) - { - var avgRtt = ns.AverageRtt; - var shouldReset = ns.HasStableConnection && avgRtt > 0 && avgRtt < 200 && avgRtt < interval / 4; - - if (shouldReset) - { - ns._movementHistoryIndex = 0; - ns._movementHistoryFull = false; - } - - // Track gap duration for burst forgiveness logic - // A large gap followed by a burst of packets = likely lag recovery, not speed hack - ns._lastGapDuration = interval; - - if (_debugLogging && mobile?.RawName != null) - { - var action = shouldReset ? "history reset" : "history preserved (possible lag)"; - logger.Debug( - "[Movement] {Name}: SKIP recording (gap {Gap}ms > {MaxGap}ms, " + - "RTT={RTT}ms stable={Stable} → {Action})", - mobile.RawName, interval, _maxChainGap, avgRtt, ns.HasStableConnection, action - ); - } - } - else if (_debugLogging && mobile?.RawName != null) - { - logger.Debug("[Movement] {Name}: SKIP recording (first in chain)", mobile.RawName); - } - - return; - } - - // Lazy initialize buffer - ns._movementHistory ??= new NetState.MovementRecord[_movementHistorySize]; - - // Build flags - // Direction-only changes (cost=0) don't contribute to rate calculation. - // Don't record them - they would pollute interval measurements. - // Example bug if recorded: direction changes between real moves make - // the next real move's interval artificially short, inflating rate. - if (cost == 0) - { - if (_debugLogging && mobile?.RawName != null) - { - logger.Debug( - "[Movement] {Name}: SKIP direction-only change (preserves interval measurement)", - mobile.RawName - ); - } - return; - } - - var flags = NetState.MovementRecordFlags.None; - if ((dir & Direction.Running) != 0) - { - flags |= NetState.MovementRecordFlags.Running; - } - if (mobile.Mounted) - { - flags |= NetState.MovementRecordFlags.Mounted; - } - if (ns._hasQueuedMovements) - { - flags |= NetState.MovementRecordFlags.WasQueued; - } - - // Write to circular buffer (struct assignment, no heap allocation) - // interval is already validated: > 0 and <= _maxChainGap (2000ms < short.MaxValue) - ref var record = ref ns._movementHistory[ns._movementHistoryIndex]; - record.Interval = (short)interval; - record.TargetSpeed = (ushort)cost; - record.QueueDepth = (byte)Math.Min(ns._movementQueue?.Count ?? 0, 255); - record.Flags = (byte)flags; - - // Advance index - ns._movementHistoryIndex++; - if (ns._movementHistoryIndex >= _movementHistorySize) - { - ns._movementHistoryIndex = 0; - ns._movementHistoryFull = true; - } - - ns._lastMovementRecordTime = now; - - // Debug logging - if (_debugLogging && mobile?.RawName != null) - { - var historyCount = ns._movementHistoryFull ? _movementHistorySize : ns._movementHistoryIndex; - logger.Debug( - "[Movement] {Name}: interval={Interval}ms target={Target}ms queue={Queue} " + - "flags={Flags} history={History}/{MaxHistory} RTT={RTT}ms", - mobile.RawName, interval, cost, record.QueueDepth, - flags, historyCount, _movementHistorySize, ns.AverageRtt - ); - } - } - - /// - /// Calculates the movement rate ratio: targetTime / actualTime. - /// Returns 1.0 for normal speed, >1.0 for faster than allowed. - /// - /// The NetState to analyze - /// Output: number of samples used in calculation - /// Rate ratio (1.0 = normal, 1.1 = 10% faster) - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float CalculateMovementRate(NetState ns, out int sampleCount) - { - sampleCount = 0; - - if (ns._movementHistory == null) - { - return 1.0f; - } - - var historyCount = ns._movementHistoryFull ? _movementHistorySize : ns._movementHistoryIndex; - if (historyCount < _minSamplesForRate) - { - return 1.0f; - } - - long totalTarget = 0; - long totalActual = 0; - var count = 0; - - // Walk backwards through history from most recent - // All entries are guaranteed valid (interval > 0 and <= _maxChainGap) due to - // filtering in RecordMovement() and history reset on chain breaks. - for (var i = 0; i < historyCount; i++) - { - // Calculate index going backwards from current position - var idx = ns._movementHistoryIndex - 1 - i; - if (idx < 0) - { - idx += _movementHistorySize; - } - - ref readonly var record = ref ns._movementHistory[idx]; - - // Skip direction-only changes (cost = 0) - they don't contribute to movement rate - if ((record.Flags & (byte)NetState.MovementRecordFlags.DirectionChangeOnly) != 0) - { - continue; - } - - totalTarget += record.TargetSpeed; - totalActual += record.Interval; - count++; - } - - sampleCount = count; - - if (count < _minSamplesForRate || totalActual <= 0) - { - return 1.0f; - } - - return (float)totalTarget / totalActual; - } - - /// - /// Dumps movement history for debugging rate calculation. - /// - private static void DumpMovementHistory(NetState ns, int limit) - { - if (ns._movementHistory == null) - { - logger.Debug(" [History] null"); - return; - } - - var historyCount = ns._movementHistoryFull ? _movementHistorySize : ns._movementHistoryIndex; - var showCount = Math.Min(limit, historyCount); - long totalTarget = 0; - long totalActual = 0; - - logger.Debug(" [History] Showing {ShowCount} of {HistoryCount} entries:", showCount, historyCount); - - for (var i = 0; i < showCount; i++) - { - var idx = ns._movementHistoryIndex - 1 - i; - if (idx < 0) - { - idx += _movementHistorySize; - } - - ref readonly var record = ref ns._movementHistory[idx]; - var flags = (NetState.MovementRecordFlags)record.Flags; - - totalTarget += record.TargetSpeed; - totalActual += record.Interval; - - var cumRate = totalActual > 0 ? (float)totalTarget / totalActual : 0; - logger.Debug( - " [{Index}] interval={Interval}ms target={Target}ms queue={Queue} flags={Flags} cumRate={CumRate:F3}", - i, record.Interval, record.TargetSpeed, record.QueueDepth, flags, cumRate - ); - } - } - - /// - /// Detects if recent movements arrived as a burst (multiple packets with near-zero intervals). - /// - /// The NetState to analyze - /// Tuple of (burst size, preceding gap in ms) - public static (int burstSize, int precedingGap) DetectRecentBurst(NetState ns) - { - if (ns._movementHistory == null) - { - return (0, 0); - } - - var historyCount = ns._movementHistoryFull ? _movementHistorySize : ns._movementHistoryIndex; - if (historyCount < 2) - { - return (0, 0); - } - - const int burstThreshold = 15; // Packets within 15ms are "simultaneous" - var burstSize = 0; - var precedingGap = 0; - - for (var i = 0; i < historyCount; i++) - { - var idx = ns._movementHistoryIndex - 1 - i; - if (idx < 0) - { - idx += _movementHistorySize; - } - - var interval = ns._movementHistory[idx].Interval; - - // All intervals are guaranteed > 0 due to filtering in RecordMovement() - if (interval <= burstThreshold) - { - burstSize++; - } - else - { - precedingGap = interval; - break; - } - } - - return (burstSize, precedingGap); - } - - /// - /// Detection verdict levels for speed hack analysis. - /// - public enum DetectionVerdict - { - Normal, // Rate within tolerance - Possible, // Slight anomaly, could be network - Likely, // Multiple signals point to cheating - Definite // Clear speed hacking - } - - /// - /// Analyzes movement patterns and calculates detection confidence. - /// - /// The NetState to analyze - /// Output: calculated movement rate - /// Output: number of samples used - /// Output: confidence score 0.0-1.0 - /// Detection verdict - public static DetectionVerdict AnalyzeMovement( - NetState ns, - out float rate, - out int sampleCount, - out float confidence - ) - { - rate = CalculateMovementRate(ns, out sampleCount); - confidence = 0f; - - // Not enough data - if (sampleCount < _minSamplesForRate) - { - return DetectionVerdict.Normal; - } - - var averageRtt = ns.AverageRtt; - - // Detailed rate breakdown for debugging - if (_debugLogging) - { - logger.Debug("[MovementAnalysis] Rate={Rate:F3}, Samples={Samples}, RTT={RTT}ms", - rate, sampleCount, averageRtt); - DumpMovementHistory(ns, sampleCount); - } - - // Signal 1: Movement rate (primary signal) - if (rate > 1.15f) - { - confidence += 0.5f; - } - else if (rate > _definiteRateThreshold) - { - confidence += 0.35f; - } - else if (rate > _suspiciousRateThreshold) - { - confidence += 0.2f; - } - else if (rate > 1.02f) - { - confidence += 0.1f; - } - - // Signal 2: Packet rate (secondary signal) - var packetRate = ns.CurrentMovementRate; - if (packetRate > 15) - { - confidence += 0.15f; - } - else if (packetRate > MaxExpectedPacketRate) - { - confidence += 0.1f; - } - - // Signal 2b: Queue depth (critical for ACK-throttled speed hacks) - // The UO client limits unacked movements to 5, so our queue should max at 4. - // When going straight at high speed, ACK flow limits packet rate, but queue stays high. - // Queue > 4 indicates a modified client (not just Cheat Engine speed hack). - var queueDepth = ns._movementQueue?.Count ?? 0; - if (queueDepth > MaxQueueWithUnmodifiedClient) - { - confidence += 0.5f; // Modified client - extremely suspicious - } - else if (queueDepth >= MaxQueueWithUnmodifiedClient) - { - confidence += 0.25f; // At client limit - speed hack with unmodified client - } - else if (queueDepth >= 2) - { - confidence += 0.1f; - } - - // Signal 3: RTT correlation (modifier) - if (ns.HasStableConnection && averageRtt < 100) - { - // Stable, low-latency connection - problems are more suspicious - if (rate > 1.02f) - { - confidence += 0.2f; - } - } - else if (ns._rttVariance > 10000 || averageRtt > 300) - { - // Unstable connection - reduce confidence - confidence *= 0.6f; - } - - // Signal 4: Burst pattern analysis - var (burstSize, _) = DetectRecentBurst(ns); - - // Forgiveness for lag recovery: burst preceded by large gap - // A legitimate lag spike will have a large gap followed by burst of packets arriving at once - // These packets will have HIGH rate (tiny intervals) but it's not cheating - if (burstSize >= 3 && ns._lastGapDuration > _maxChainGap / 2) - { - // Recent large gap + burst = lag recovery, strong forgiveness - confidence *= 0.3f; - ns._lastGapDuration = 0; // Reset after applying forgiveness - } - else if (burstSize >= 3 && rate < _suspiciousRateThreshold) - { - // Burst pattern with normal rate = likely legitimate lag - confidence *= 0.5f; - } - - // Signal 5: Sample count modifier - if (sampleCount < 10) - { - confidence *= 0.7f; - } - else if (sampleCount >= 20) - { - confidence = Math.Min(confidence * 1.1f, 1.0f); - } - - // Determine verdict - if (rate > _definiteRateThreshold && confidence > 0.6f) - { - return DetectionVerdict.Definite; - } - - if (rate > 1.20f) - { - return DetectionVerdict.Definite; - } - - // Modified client: queue exceeds what unmodified client can produce - // This is impossible with unmodified client, so it's definite regardless of persistence - if (queueDepth > MaxQueueWithUnmodifiedClient) - { - return DetectionVerdict.Definite; - } - - // Queue-based detection: requires SUSTAINED high queue, not momentary spike - // A legitimate lag burst can momentarily fill queue to 4, but it drains quickly. - // Speed hackers maintain queue at 4 continuously because they send at max ACK rate. - // We use Likely here and require sustained detection before alerting. - if (queueDepth >= MaxQueueWithUnmodifiedClient && ns.HasStableConnection && averageRtt < 100) - { - // Stable low-latency connection with high queue - suspicious but needs sustained check - return DetectionVerdict.Likely; - } - - if (queueDepth >= 3 && ns.HasStableConnection) - { - return DetectionVerdict.Likely; - } - - if (rate > _suspiciousRateThreshold && confidence > 0.4f && ns.HasStableConnection) - { - return DetectionVerdict.Likely; - } - - if (rate > 1.03f && confidence > 0.2f) - { - return DetectionVerdict.Possible; - } - - if (packetRate > 14) - { - return DetectionVerdict.Possible; - } - - if (queueDepth >= 2 && ns.HasStableConnection) - { - return DetectionVerdict.Possible; - } - - return DetectionVerdict.Normal; - } - - /// - /// Checks if staff should be notified about suspicious movement. - /// Called periodically during sustained queue depth tracking. - /// Returns the detection verdict for probe frequency adjustment. - /// - public static DetectionVerdict CheckAndNotifyStaff(NetState ns) - { - var verdict = AnalyzeMovement(ns, out var rate, out var sampleCount, out var confidence); - - // Debug logging - if (_debugLogging && ns.Mobile?.RawName != null) - { - var (burstSize, _) = DetectRecentBurst(ns); - var probeStatus = ns._rttProbeTime > 0 ? "pending" : "idle"; - var queueDepth = ns._movementQueue?.Count ?? 0; - logger.Debug( - "[RateCheck] {Name}: rate={Rate:F3} samples={Samples} verdict={Verdict} " + - "confidence={Confidence:P0} queue={Queue} burst={Burst} sustained={Sustained}s", - ns.Mobile.RawName, rate, sampleCount, verdict, confidence, queueDepth, burstSize, ns._consecutiveHighRateSeconds - ); - logger.Debug( - " RTT: avg={Avg}ms last={Last}ms var={Var} samples={RttSamples} stable={Stable} probe={Probe}", - ns.AverageRtt, ns._lastRtt, ns._rttVariance, ns._rttSampleCount, ns.HasStableConnection, probeStatus - ); - } - - // Update sustained counter based on verdict - if (verdict >= DetectionVerdict.Likely) - { - ns._consecutiveHighRateSeconds += 2; // Called every ~2 seconds - } - else - { - ns._consecutiveHighRateSeconds = Math.Max(0, ns._consecutiveHighRateSeconds - 1); - } - - // Determine if we should notify - var shouldNotify = false; - var urgency = ""; - - if (verdict == DetectionVerdict.Definite) - { - shouldNotify = true; - urgency = "HIGH"; - } - else if (verdict == DetectionVerdict.Likely && ns._consecutiveHighRateSeconds >= 10) - { - shouldNotify = true; - urgency = "MEDIUM"; - } - else if (verdict == DetectionVerdict.Possible && ns._consecutiveHighRateSeconds >= 30) - { - shouldNotify = true; - urgency = "LOW"; - } - - if (shouldNotify) - { - if (_debugLogging) - { - logger.Debug( - "[ALERT] {Urgency} - {Name}: rate={Rate:F3} verdict={Verdict} confidence={Confidence:P0}", - urgency, ns.Mobile?.RawName, rate, verdict, confidence - ); - } - NotifyStaff(ns, rate, sampleCount, confidence, verdict, urgency); - } - - return verdict; - } - - /// - /// Sends notification to staff about potential speed hacking. - /// Rate-limited per player. - /// - private static void NotifyStaff( - NetState ns, - float rate, - int sampleCount, - float confidence, - DetectionVerdict verdict, - string urgency - ) - { - var now = Core.TickCount; - - // Rate-limit notifications per player - if (now - ns._lastSpeedHackNotification < _speedHackNotificationCooldown) - { - return; - } - - ns._lastSpeedHackNotification = now; - - var mobile = ns.Mobile; - - // Log to file with full details - logger.Warning( - "[{Urgency}] Speed hack detected: {Character} ({Account}) | " + - "Rate: {Rate:F2} ({Samples} samples) | Verdict: {Verdict} | Confidence: {Confidence:P0} | " + - "PacketRate: {PacketRate}/s (peak: {PeakRate}/s) | RTT: {Rtt}ms (stable: {Stable}) | " + - "Sustained: {Sustained}s | Queue: {Queue} | Location: {Location} Map: {Map} | IP: {IP}", - urgency, - mobile?.RawName ?? "Unknown", - ns.Account?.Username ?? "Unknown", - rate, - sampleCount, - verdict, - confidence, - ns.CurrentMovementRate, - ns.PeakMovementRate, - ns.AverageRtt, - ns.HasStableConnection, - ns._consecutiveHighRateSeconds, - ns._movementQueue?.Count ?? 0, - mobile?.Location, - mobile?.Map?.Name, - ns.Address - ); - - // Invoke callback for staff notification (if configured) - // Server operators can hook this to broadcast to staff - OnSpeedHackDetected?.Invoke(ns, mobile, rate, verdict, urgency); - } - - /// - /// Event raised when a speed hack is detected. - /// Server operators can subscribe to broadcast to staff or take other actions. - /// - public static event Action OnSpeedHackDetected; - - /// - /// Snapshot of a player's movement throttle state for diagnostic display. - /// - public readonly struct MovementStats - { - public float Rate { get; init; } - public int SampleCount { get; init; } - public DetectionVerdict Verdict { get; init; } - public float Confidence { get; init; } - public long AverageRtt { get; init; } - public long LastRtt { get; init; } - public long RttVariance { get; init; } - public bool StableConnection { get; init; } - public int RttSampleCount { get; init; } - public int QueueDepth { get; init; } - public long MovementCredit { get; init; } - public int CurrentPacketRate { get; init; } - public int PeakPacketRate { get; init; } - public int BurstSize { get; init; } - public int PrecedingGap { get; init; } - public int SustainedSeconds { get; init; } - public bool DebugLogging { get; init; } - } - - /// - /// Gets a snapshot of the current movement throttle state for a player. - /// Safe to call from UOContent (exposes internal state via public struct). - /// - public static MovementStats GetMovementStats(NetState ns) - { - var verdict = AnalyzeMovement(ns, out var rate, out var sampleCount, out var confidence); - var (burstSize, precedingGap) = DetectRecentBurst(ns); - - return new MovementStats - { - Rate = rate, - SampleCount = sampleCount, - Verdict = verdict, - Confidence = confidence, - AverageRtt = ns.AverageRtt, - LastRtt = ns._lastRtt, - RttVariance = ns._rttVariance, - StableConnection = ns.HasStableConnection, - RttSampleCount = ns._rttSampleCount, - QueueDepth = ns._movementQueue?.Count ?? 0, - MovementCredit = ns._movementCredit, - CurrentPacketRate = ns.CurrentMovementRate, - PeakPacketRate = ns.PeakMovementRate, - BurstSize = burstSize, - PrecedingGap = precedingGap, - SustainedSeconds = ns._consecutiveHighRateSeconds, - DebugLogging = ns.MovementLogging, - }; + // On the next event loop, the player receives up to 400ms in grace latency + ns._movementCredit = Math.Min(_throttleThreshold, credit - cost); + return false; } } diff --git a/Projects/Server/Network/NetState/DumpNetStates.cs b/Projects/Server/Network/NetState/DumpNetStates.cs index 018ebb7e1..22746238e 100644 --- a/Projects/Server/Network/NetState/DumpNetStates.cs +++ b/Projects/Server/Network/NetState/DumpNetStates.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: DumpNetStates.cs * * * @@ -32,7 +32,7 @@ public static class DumpNetStates foreach (var ns in NetState.Instances) { - file.WriteLine($"{ns}, {ns.ConnectedOn}, {ns.NextActivityCheck}, {ns.IsConnected}, {ns._protocolState}, {ns._parserState}"); + file.WriteLine($"{ns}, {ns.ConnectedOn}, {ns.NextActivityCheck}, {ns.Connection.Connected}, {ns._protocolState}, {ns._parserState}"); } } } diff --git a/Projects/Server/Network/NetState/NetState.ClientVersion.cs b/Projects/Server/Network/NetState/NetState.ClientVersion.cs index 2e2a159b2..d4e8bcf84 100644 --- a/Projects/Server/Network/NetState/NetState.ClientVersion.cs +++ b/Projects/Server/Network/NetState/NetState.ClientVersion.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: NetState.ClientVersion.cs * * * diff --git a/Projects/Server/Network/NetState/NetState.Movement.cs b/Projects/Server/Network/NetState/NetState.Movement.cs deleted file mode 100644 index 1cc79429e..000000000 --- a/Projects/Server/Network/NetState/NetState.Movement.cs +++ /dev/null @@ -1,389 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: NetState.Movement.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; -using Server.Logging; - -namespace Server.Network; - -public partial class NetState -{ - private static readonly ILogger movementLogger = LogFactory.GetLogger(typeof(NetState)); - - // Per-connection movement logging (RTT instrumentation, etc.) - // Can be enabled at runtime for specific connections - internal bool _movementLogging; - - /// - /// Gets or sets whether movement/RTT logging is enabled for this connection. - /// When enabled, logs detailed RTT probe and response timing. - /// - public bool MovementLogging - { - get => _movementLogging; - set => _movementLogging = value; - } - - internal struct QueuedMovement - { - public Direction Direction; - public int Sequence; - } - - // Movement history record for rate calculation (8 bytes, cache-aligned) - [StructLayout(LayoutKind.Sequential, Pack = 2)] - internal struct MovementRecord - { - public short Interval; // Time since previous packet (ms), capped at 32767 - public ushort TargetSpeed; // Expected interval (100ms mounted running, etc.) - public byte QueueDepth; // Queue size when received - public byte Flags; // MovementRecordFlags - public short Reserved; // Padding to 8 bytes for cache alignment - } - - [Flags] - internal enum MovementRecordFlags : byte - { - None = 0, - Running = 1, - Mounted = 2, - DirectionChangeOnly = 4, // Cost was 0 (turn in place) - WasQueued = 8 // Packet was queued, not executed immediately - } - - // Movement queue state - internal Queue _movementQueue; // Lazy initialized - internal long _movementCredit; // Credit buffer for timing jitter - internal long _nextMovementTime = Core.TickCount; // When next movement is allowed - internal int _sustainedQueueDepth; // Tracks sustained high queue depth - internal long _lastQueueDepthCheck; // Throttle depth check frequency - internal bool _hasQueuedMovements; // Fast check for Slice() - - // Movement history for rate-based speed hack detection (lazy initialized) - internal MovementRecord[] _movementHistory; // Circular buffer - internal int _movementHistoryIndex; // Next write position (also serves as count until full) - internal bool _movementHistoryFull; // True once buffer has wrapped - internal long _lastMovementRecordTime; // For calculating intervals - - // Detection state - internal int _consecutiveHighRateSeconds; // Sustained detection counter - internal long _lastSpeedHackNotification; // Rate-limit notifications - internal int _lastGapDuration; // Duration of last gap > maxChainGap (for burst forgiveness) - - // Movement packet rate tracking (for speed hack detection) - internal long _movementWindowStart; // Start of current 1-second window - internal int _movementsInWindow; // Count in current window - internal int _peakMovementRate; // Highest rate seen (packets/sec) - - /// - /// Resets movement state when sequence needs to be cleared (paralysis, teleport, map change, etc.) - /// - public void ResetMovementState() - { - _movementQueue?.Clear(); - Sequence = 0; - _nextMovementTime = Core.TickCount; - _movementCredit = 0; - _hasQueuedMovements = false; - _sustainedQueueDepth = 0; - - // Reset movement history - next movement starts a new chain - _lastMovementRecordTime = 0; - _movementHistoryIndex = 0; - _movementHistoryFull = false; - - // Reset detection state - sustained detection loses context on teleport/map change - _consecutiveHighRateSeconds = 0; - _lastGapDuration = 0; - _rttProbeInterval = RttProbeIntervalNormal; - - // Reset packet rate window - _movementWindowStart = 0; - _movementsInWindow = 0; - } - - /// - /// Tracks movement packet rate. Called for each movement packet received. - /// Returns the current rate (packets per second in the last window). - /// - public int TrackMovementRate() - { - var now = Core.TickCount; - - // Check if we're in a new 1-second window - if (now - _movementWindowStart >= 1000) - { - // Record peak rate if this window had movements - if (_movementsInWindow > _peakMovementRate) - { - _peakMovementRate = _movementsInWindow; - } - - // Start new window - _movementWindowStart = now; - _movementsInWindow = 1; - return 1; - } - - // Same window, increment count - _movementsInWindow++; - return _movementsInWindow; - } - - /// - /// Gets the current movement rate (packets in the current 1-second window). - /// - public int CurrentMovementRate => _movementsInWindow; - - /// - /// Gets the peak movement rate observed for this session. - /// - public int PeakMovementRate => _peakMovementRate; - - // RTT Measurement Configuration - private const int RttProbeIntervalNormal = 5000; // Normal: probe every 5 seconds - private const int RttProbeIntervalSuspicious = 2000; // Suspicious: probe every 2 seconds - private const int RttProbeIntervalDefinite = 1000; // Definite cheater: probe every 1 second - private const int RttProbeJitter = 500; // Random jitter to prevent bursts - private const int RttHistorySize = 8; // Keep 8 samples - private const long StableVarianceThreshold = 2500; // Variance < 50ms std dev = stable - private const long MaxStableLatency = 200; // Max RTT (ms) for "stable" connection - - // RTT state - internal long _rttProbeTime; // When we sent the probe (0 = not waiting) - internal long _lastRtt; // Most recent RTT measurement - internal long[] _rttHistory; // Rolling history (lazy init) - internal int _rttHistoryIndex; // Current position in history - internal int _rttSampleCount; // Number of samples collected (saturates at RttHistorySize) - internal long _rttVariance; // Calculated variance for stability - internal long _nextRttProbe; // When to send next probe - internal int _rttProbeInterval = RttProbeIntervalNormal; // Current probe interval - - // High-resolution timestamp for RTT measurement (Stopwatch ticks, not game loop ticks) - private long _rttProbeTimestampHiRes; - - /// - /// Sets the RTT probe interval based on suspicion level. - /// More suspicious = more frequent probes for better evidence. - /// - public void SetProbeFrequency(int suspicionLevel) - { - _rttProbeInterval = suspicionLevel switch - { - >= 3 => RttProbeIntervalDefinite, // Definite cheater - >= 2 => RttProbeIntervalSuspicious, // Likely cheater - _ => RttProbeIntervalNormal // Normal or Possible - }; - } - - /// - /// Sends an RTT probe if enough time has passed since the last one. - /// Called from movement validation when player is actively moving. - /// - public void MaybeSendRttProbe() - { - // Only probe logged-in players - if (Mobile?.Deleted != false) - { - return; - } - - var now = Core.TickCount; - - // Don't send if we're still waiting for a response - if (_rttProbeTime > 0) - { - // Timeout after 10 seconds - connection is probably dead or very laggy - if (now - _rttProbeTime > 10000) - { - _rttProbeTime = 0; - _rttProbeTimestampHiRes = 0; - } - return; - } - - // First probe: send immediately when player starts moving - // Subsequent probes: send when interval has passed - if (_nextRttProbe == 0 || now >= _nextRttProbe) - { - _rttProbeTime = now; - _rttProbeTimestampHiRes = Stopwatch.GetTimestamp(); - _nextRttProbe = now + _rttProbeInterval + Utility.Random(RttProbeJitter); - - if (_movementLogging) - { - movementLogger.Debug( - "[RTT-Probe] {Account}: Sending probe at TickCount={TickCount}", - Account?.Username ?? _toString, now - ); - } - - this.SendClientVersionRequest(); - } - } - - /// - /// Records an RTT measurement when ClientVersion response is received. - /// - public void RecordRttMeasurement() - { - var nowHiRes = Stopwatch.GetTimestamp(); - var now = Core.TickCount; - - if (_rttProbeTime <= 0) - { - // Not expecting a response (client-initiated version send) - ignore silently - return; - } - - var rtt = now - _rttProbeTime; - - // High-resolution RTT in microseconds - var rttHiResUs = (nowHiRes - _rttProbeTimestampHiRes) * 1_000_000 / Stopwatch.Frequency; - - if (_movementLogging) - { - movementLogger.Debug( - "[RTT-Response] {Account}: {Rtt}ms (HiRes: {RttHiRes:F2}ms)", - Account?.Username ?? _toString, rtt, rttHiResUs / 1000.0 - ); - } - - _rttProbeTime = 0; - _rttProbeTimestampHiRes = 0; - - // Sanity check - RTT should be positive and reasonable - if (rtt is <= 0 or > 10000) - { - if (_movementLogging) - { - movementLogger.Debug( - "[RTT-Response] {Account}: Invalid RTT {Rtt}ms, discarding", - Account?.Username ?? _toString, rtt - ); - } - return; - } - - // Lazy init history - _rttHistory ??= new long[RttHistorySize]; - - // Update history - _rttHistory[_rttHistoryIndex++ & (RttHistorySize - 1)] = rtt; - _lastRtt = rtt; - - // Track sample count (saturates at buffer size) - if (_rttSampleCount < RttHistorySize) - { - _rttSampleCount++; - } - - // Recalculate variance - UpdateRttVariance(); - - if (_movementLogging) - { - movementLogger.Debug( - "[RTT-Response] {Account}: Recorded RTT={Rtt}ms, Avg={Avg}ms, Var={Var}, Samples={Samples}, Stable={Stable}", - Account?.Username ?? _toString, rtt, AverageRtt, _rttVariance, _rttSampleCount, HasStableConnection - ); - } - } - - /// - /// Calculates the variance of RTT measurements for connection stability assessment. - /// - private void UpdateRttVariance() - { - if (_rttHistory == null) - { - _rttVariance = 0; - return; - } - - long sum = 0; - long sumSq = 0; - int count = 0; - - for (int i = 0; i < RttHistorySize; i++) - { - var sample = _rttHistory[i]; - if (sample > 0) - { - sum += sample; - sumSq += sample * sample; - count++; - } - } - - if (count < 2) - { - _rttVariance = 0; - return; - } - - var mean = sum / count; - _rttVariance = sumSq / count - mean * mean; - - // Safety clamp: integer division rounding can produce negative variance - if (_rttVariance < 0) - { - _rttVariance = 0; - } - } - - /// - /// Gets the average RTT from recent measurements. - /// - public long AverageRtt - { - get - { - if (_rttHistory == null) - { - return 0; - } - - long sum = 0; - int count = 0; - - for (int i = 0; i < RttHistorySize; i++) - { - var sample = _rttHistory[i]; - if (sample > 0) - { - sum += sample; - count++; - } - } - - return count > 0 ? sum / count : 0; - } - } - - /// - /// Returns true if the connection has stable, low-variance, low-latency connection. - /// Requires at least 3 samples to make a stability determination. - /// Checks both variance (consistency) and absolute latency (quality). - /// - public bool HasStableConnection => - _rttSampleCount >= 3 && - _rttVariance < StableVarianceThreshold && - AverageRtt > 0 && - AverageRtt < MaxStableLatency; -} diff --git a/Projects/Server/Network/NetState/NetState.Network.cs b/Projects/Server/Network/NetState/NetState.Network.cs deleted file mode 100644 index f69f92076..000000000 --- a/Projects/Server/Network/NetState/NetState.Network.cs +++ /dev/null @@ -1,624 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: NetState.Network.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.NetworkInformation; -using System.Network; -using System.Numerics; - -namespace Server.Network; - -/// -/// Network infrastructure for IORingGroup-based socket I/O. -/// -public partial class NetState -{ - // Buffer sizes - private const int RecvBufferSize = 1024 * 64; // 64KB recv buffers - private const int DefaultSendBufferSize = 1024 * 256; // 256KB send buffers - private const int MinSendBufferSize = 1024 * 64; // Platform allocation granularity - private const int MaxConnections = 4096; // Max concurrent connections - - private static readonly Queue _disposed = []; - private static readonly TimeSpan ConnectingSocketIdleLimit = TimeSpan.FromMilliseconds(5000); // 5 seconds - - // Socket manager handles buffer pools, socket lifecycle, and I/O operations - private static RingSocketManager _socketManager; - - // NetState storage indexed by RingSocket.Id - private static readonly NetState[] _netStates = new NetState[MaxConnections]; - - // Events buffer for ProcessCompletions. Bounded by one event per peeked completion - // (maxSockets), doubled for headroom. Undersizing drops DataReceived events whose bytes were - // already committed, leaving them unparsed until the next recv completes. - private static readonly RingSocketEvent[] _events = new RingSocketEvent[MaxConnections * 2]; - - // Listener management - private static nint[] _listeners = Array.Empty(); - private static int _pendingAcceptCount; - private const int PendingAcceptsPerListener = 32; - - private const long AliveCheckIntervalMs = 5000; - private static long _nextAliveCheck; - - /// - /// Gets the IORingGroup instance for socket operations. - /// - public static IIORingGroup Ring => _socketManager?.Ring; - - /// - /// Waits for network I/O completions or until the specified timeout expires. - /// Used by the game loop to sleep efficiently while remaining responsive to network events. - /// - /// Maximum time to wait in milliseconds. - public static void WaitForCompletion(int timeoutMs) - { - _socketManager?.WaitForCompletion(timeoutMs); - } - - /// - /// Wakes the game loop if it is blocked in . Safe from any - /// thread; a no-op before networking is configured or after teardown. The signal is sticky, - /// so a wake racing the loop's decision to sleep is not lost. - /// - public static void Wake() - { - _socketManager?.Ring?.Wake(); - } - - /// - /// True when no queued network work remains for the loop to drain. defers - /// work in several places, so an empty completion queue alone is not enough. - /// - internal static bool IsIdle => - _throttled.Count == 0 && _throttledPending.Count == 0 && - _flushPending.Count == 0 && _pendingDisconnects.Count == 0 && _disposed.Count == 0; - - /// - /// Gets the listening addresses that the server is bound to. - /// - public static IPEndPoint[] ListeningAddresses { get; private set; } - - private static IPRateLimiter _ipRateLimiter; - - /// - /// Configures the IORingGroup and socket manager. - /// - private static void ConfigureNetwork() - { - // Skip if already configured - if (_socketManager != null) - { - return; - } - - // Initialize IP rate limiter - _ipRateLimiter = new IPRateLimiter(10, 10000, 1000, 2.0, 3_600_000, Core.ClosingTokenSource.Token); - - // Sends in flight per connection; honoured by RIO only (see IIORingGroup). Costs a - // request-queue and completion-queue slot per send, not another buffer. Worst-case added - // latency is roughly completion RTT / this value. - var maxOutstandingSends = ServerConfiguration.GetOrUpdateSetting("network.maxOutstandingSends", 32); - - // Initialize IORingGroup - var ring = IORingGroup.Create( - queueSize: MaxConnections * 2, - maxConnections: MaxConnections, - maxOutstandingSends: maxOutstandingSends - ); - - // Per-connection send buffer: the lever for "send buffer exhausted" disconnects, and the - // per-connection memory ceiling. - var sendBufferSize = GetSendBufferSize(); - - // Create socket manager which handles buffer pools and socket lifecycle - _socketManager = new RingSocketManager( - ring, - maxSockets: MaxConnections, - recvBufferSize: RecvBufferSize, - sendBufferSize: sendBufferSize, - initialBufferSlabs: 8, - maxBufferSlabs: 32 - ); - } - - /// - /// Reads the configured send buffer size, coerced to a power of two of at least the platform - /// allocation granularity. IORingBuffer requires this and would otherwise throw at socket - /// creation rather than at startup. - /// - private static int GetSendBufferSize() - { - var configured = ServerConfiguration.GetOrUpdateSetting("network.sendBufferSize", DefaultSendBufferSize); - var size = Math.Max(MinSendBufferSize, configured); - - if (!BitOperations.IsPow2(size)) - { - size = (int)BitOperations.RoundUpToPowerOf2((uint)size); - } - - if (size != configured) - { - logger.Warning( - "network.sendBufferSize {Configured} is not a power of two of at least {Minimum}; using {Adjusted}", - configured, - MinSendBufferSize, - size - ); - } - - return size; - } - - /// - /// Starts the network server on configured listening addresses. - /// - public static void Start() - { - HashSet listeningAddresses = []; - List listeners = []; - - var ring = _socketManager.Ring; - for (var i = 0; i < ServerConfiguration.Listeners.Count; i++) - { - var ipep = ServerConfiguration.Listeners[i]; - var listener = ring.CreateListener(ipep.Address.ToString(), (ushort)ipep.Port, 256); - if (listener == -1) - { - logger.Warning("Failed to create listener for {Address}", ipep); - continue; - } - - if (ipep.Address.Equals(IPAddress.Any) || ipep.Address.Equals(IPAddress.IPv6Any)) - { - listeningAddresses.UnionWith(GetListeningAddresses(ipep)); - } - else - { - listeningAddresses.Add(ipep); - } - - listeners.Add(listener); - } - - foreach (var ipep in listeningAddresses) - { - logger.Information("Listening: {Address}", ipep); - } - - ListeningAddresses = listeningAddresses.ToArray(); - - // Register listeners to start accepting connections - RegisterListeners(listeners.ToArray()); - } - - /// - /// Shuts down the network server and closes all listeners. - /// - public static void Shutdown() - { - CloseListeners(); - } - - /// - /// Gets the actual listening addresses for a wildcard endpoint. - /// - public static IEnumerable GetListeningAddresses(IPEndPoint ipep) => - NetworkInterface.GetAllNetworkInterfaces().SelectMany(adapter => - adapter.GetIPProperties().UnicastAddresses - .Where(uip => ipep.AddressFamily == uip.Address.AddressFamily) - .Select(uip => new IPEndPoint(uip.Address, ipep.Port)) - ); - - /// - /// Registers listeners with the ring and starts accepting connections. - /// - private static void RegisterListeners(nint[] listeners) - { - _listeners = listeners; - - var ring = _socketManager.Ring; - - // Queue initial accept operations for each listener - for (var i = 0; i < _listeners.Length; i++) - { - var listener = _listeners[i]; - for (var j = 0; j < PendingAcceptsPerListener; j++) - { - ring.PrepareAccept(listener, 0, 0, IORingUserData.EncodeAccept()); - _pendingAcceptCount++; - } - } - } - - /// - /// Closes all listeners. - /// - private static void CloseListeners() - { - var ring = _socketManager?.Ring; - if (ring == null) - { - return; - } - - foreach (var listener in _listeners) - { - ring.CloseListener(listener); - } - - _listeners = []; - } - - private static void HandleAcceptCompletion(int result) - { - _pendingAcceptCount--; - - var ring = _socketManager.Ring; - - // EAGAIN (-11) means no connection pending - just re-queue - if (result == -11) - { - goto ReplenishAccepts; - } - - if (result >= 0) - { - var clientSocket = (nint)result; - var remoteIP = SocketHelper.GetRemoteAddress(clientSocket); - - if (remoteIP != null) - { - if (_ipRateLimiter != null && !_ipRateLimiter.Verify(remoteIP, out var totalAttempts)) - { - logger.Debug("{Address} Past IP limit threshold ({TotalAttempts})", remoteIP, totalAttempts); - - if (Bans.BanConfiguration.Settings.ReportRateLimitTrips) - { - // Enqueue-only contribution; NOT added to the local firewall set (the limiter already - // gates it here and the OS bouncer drops it at the kernel). - Bans.BanChannel.Report(remoteIP, Bans.BanConfiguration.Settings.AutoBanDuration, Bans.BanReasons.RateLimit); - } - } - else if (ConnectionFilters.ShouldDeny(remoteIP, out var deniedBy)) - { - // Whatever a hit implies (persisting, promoting to an OS bouncer, contributing to the - // ban channel) is the filter's own business; the accept path just drops the socket. - logger.Debug("{Address} denied by connection filter '{Filter}'", remoteIP, deniedBy); - } - else - { - // Allow event handlers to reject the connection - var args = new SocketConnectEventArgs(remoteIP); - EventSink.InvokeSocketConnect(args); - - if (args.AllowConnection) - { - ring.ConfigureSocket(clientSocket); - CreateFromSocket(clientSocket, remoteIP); - goto ReplenishAccepts; - } - - logger.Debug("{Address} Rejected by socket handler", remoteIP); - } - } - - ring.CloseSocket(clientSocket); - } - else if (result != -4) // EINTR - { - logger.Debug("Accept error: {Result}", result); - } - - ReplenishAccepts: - var targetAccepts = _listeners.Length * PendingAcceptsPerListener; - while (_pendingAcceptCount < targetAccepts && _listeners.Length > 0) - { - var listenerIndex = _pendingAcceptCount % _listeners.Length; - ring.PrepareAccept(_listeners[listenerIndex], 0, 0, IORingUserData.EncodeAccept()); - _pendingAcceptCount++; - } - } - - /// - /// Creates a NetState from an accepted socket handle. - /// - internal static NetState CreateFromSocket(nint socketHandle, IPAddress address) - { - // Use socket manager to create managed socket (handles buffers, registration, recv posting) - var socket = _socketManager.CreateSocket(socketHandle); - if (socket == null) - { - logger.Debug("Failed to create socket (resources exhausted)"); - _socketManager.Ring.CloseSocket(socketHandle); - return null; - } - - // Create NetState and map by socket ID - var ns = new NetState(socket, address); - return _netStates[socket.Id] = ns; - } - - private static void DisconnectUnattachedSockets() - { - var now = Core.Now; - - // Process connecting queue with lazy removal - O(1) operations - while (_connectingQueue.TryPeek(out var ns)) - { - // Lazy removal: skip already-authenticated or disconnected connections - if (!ns.Running || ns.Account != null) - { - _connectingQueue.Dequeue(); - continue; - } - - // If the socket has been connected for less than the limit, we can stop - // (queue is ordered by connection time, so remaining entries are newer) - if (now - ns.ConnectedOn < ConnectingSocketIdleLimit) - { - break; - } - - _connectingQueue.Dequeue(); - - // Socket must have finished the entire authentication process or be forcibly disconnected - if (!ns.SentFirstPacket || !ns.Seeded) - { - // Only the totally silent ones are evidence. A connection that sent SOME data and ran out of - // time is far more likely a slow link, and banning those makes the player retry, trip the - // rate limiter, and compound it into an hours-long ban. - if (!ns._receivedData && Bans.BanConfiguration.Settings.ReportBadConnects) - { - Bans.BanChannel.Report( - ns.Address, - Bans.BanConfiguration.Settings.BadConnectDuration, - Bans.BanReasons.SilentConnect - ); - } - - ns.Disconnect(null); - - // Force immediate cleanup - these are unauthenticated connections - // where graceful disconnect can get stuck with pending sends. - if (ns._socket is { DisconnectPending: true }) - { - _socketManager.DisconnectImmediate(ns._socket); - } - } - } - } - - public static void FlushAll() - { - while (_flushPending.TryDequeue(out var ns)) - { - if (ns == null) - { - continue; - } - - // Reset flag to allow re-queueing if more data is added later - ns._flushQueued = false; - - if (ns.Running) - { - ns._socket?.QueueSend(); - } - } - - // Submit any pending operations - _socketManager?.Submit(); - } - - public static void Slice() - { - var curTicks = Core.TickCount; - DisconnectUnattachedSockets(); - - // Process throttled states - while (_throttled.Count > 0) - { - var ns = _throttled.Dequeue(); - if (ns.Running) - { - ns.HandleReceive(true); - } - } - - // This is enqueued by HandleReceive if already throttled and still throttled - while (_throttledPending.Count > 0) - { - _throttled.Enqueue(_throttledPending.Dequeue()); - } - - // Process queued movements at proper intervals - MovementThrottle.ProcessAllQueues(); - - // Process all completions through the manager FIRST - // This ensures DataReceived events are processed and HandleReceive runs, - // which may call Send() and add to _flushPending - var eventCount = _socketManager.ProcessCompletions(_events); - - for (var i = 0; i < eventCount; i++) - { - ref var evt = ref _events[i]; - - switch (evt.Type) - { - case RingSocketEventType.Accept: - { - // Handle accept - AcceptedSocketHandle contains the result - HandleAcceptCompletion((int)evt.AcceptedSocketHandle); - break; - } - - case RingSocketEventType.DataReceived: - { - var nsRecv = _netStates[evt.Socket.Id]; - // Verify generation via object identity to avoid stale completion issues - if (nsRecv != null && nsRecv._socket == evt.Socket) - { - nsRecv.NextActivityCheck = curTicks + 30000; - HandleDataReceived(nsRecv, evt.BytesTransferred); - } - break; - } - - case RingSocketEventType.DataSent: - { - var nsSend = _netStates[evt.Socket.Id]; - // Verify generation via object identity - if (nsSend != null && nsSend._socket == evt.Socket) - { - // Update activity check on successful send - nsSend.NextActivityCheck = curTicks + 30000; - } - break; - } - - case RingSocketEventType.Disconnected: - { - var nsDisc = _netStates[evt.Socket.Id]; - // Verify generation via object identity - if (nsDisc != null && nsDisc._socket == evt.Socket) - { - HandleDisconnected(nsDisc); - } - break; - } - } - } - - // Process flush queue AFTER event processing - // This ensures sends triggered by HandleReceive (via packet handlers like SendPlayServerAck) - // are queued in the SAME Slice, not the next one - while (_flushPending.TryDequeue(out var ns)) - { - // Reset flag to allow re-queueing if more data is added later - ns._flushQueued = false; - - if (ns.Running) - { - ns._socket?.QueueSend(); - } - } - - // CRITICAL: Process send queue NOW to post pending sends - // This ensures PostSend() runs and sets SendPending=true BEFORE disconnect checks - // Without this, Disconnect() would see SendPending=false even though data is queued - _socketManager.ProcessSendQueue(); - - // Process pending disconnects AFTER flush queue AND send queue processing - // This ensures the traditional order: Game Logic (Sends/Disconnects) → Receives → Flush → Disconnect - // Any Send() calls made after Disconnect() in the same tick are flushed before disconnect - while (_pendingDisconnects.TryDequeue(out var ns)) - { - // Reset flag to allow re-queueing if reconnect happens - ns._disconnectQueued = false; - - if (ns.Running && ns._socket != null) - { - // RingSocket.Disconnect() handles graceful disconnect: - // - Waits for pending sends to flush (if SendBuffer.ReadableBytes > 0) - // - Waits for in-flight I/O to complete - // - Ensures buffers aren't released while kernel is still using them - ns._socket.Disconnect(); - } - } - - // Submit any queued operations - _socketManager.Submit(); - - // Process disposes - while (_disposed.TryDequeue(out var ns)) - { - ns.DisposeInternal(); - } - - // Check for dead connections AFTER processing all completions. - // Recv completions reset NextActivityCheck, so after a server stall, - // buffered client pings update timestamps before this check fires. - if (curTicks - _nextAliveCheck >= 0) - { - _nextAliveCheck = curTicks + AliveCheckIntervalMs; - CheckAllAlive(); - } - } - - private static void HandleDataReceived(NetState ns, int bytesReceived) - { - if (!ns._running) - { - return; - } - - if (bytesReceived > 0) - { - ns._receivedData = true; - } - - // Data is already committed to buffer by RingSocketManager - // Decode if encryption is enabled - ns.DecryptRecvBuffer(bytesReceived); - - // Process packets - ns.HandleReceive(); - } - - private static void HandleDisconnected(NetState ns) - { - var slotId = ns._socket.Id; - - // IMPORTANT: Check if the slot still points to this NetState - // During quick reconnect, the slot might have been reused for a new connection - var currentNs = _netStates[slotId]; - if (currentNs != ns) - { - // Slot was already reused - don't clear it! - // Just mark this NetState as not running and queue for dispose - ns._running = false; - _disposed.Enqueue(ns); - return; - } - - // Clear the NetState slot - _netStates[slotId] = null; - - // Mark as not running and queue for dispose - ns._running = false; - _disposed.Enqueue(ns); - } - - public static void CheckAllAlive() - { - try - { - var curTicks = Core.TickCount; - - foreach (var ns in Instances) - { - ns.CheckAlive(curTicks); - } - } - catch (Exception ex) - { - TraceException(ex); - } - } -} diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs index 7312603f7..137070973 100755 --- a/Projects/Server/Network/NetState/NetState.cs +++ b/Projects/Server/Network/NetState/NetState.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: NetState.cs * * * @@ -21,52 +21,63 @@ using Server.Logging; using Server.Menus; using System; using System.Buffers; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Net; +using System.Net.Sockets; using System.Network; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Server.Network; -public partial class NetState : IComparable, IValueLinkListNode, IDisposable +public delegate void DecodePacket(Span buffer, ref int length); +public delegate int EncodePacket(ReadOnlySpan inputBuffer, Span outputBuffer); + +public partial class NetState : IComparable, IValueLinkListNode { private static readonly ILogger logger = LogFactory.GetLogger(typeof(NetState)); + private static readonly TimeSpan ConnectingSocketIdleLimit = TimeSpan.FromMilliseconds(5000); // 5 seconds + private const int RecvPipeSize = 1024 * 64; + private const int SendPipeSize = 1024 * 256; private const int HuePickerCap = 512; private const int MenuCap = 512; private const int PacketPerSecondThreshold = 3000; + private static readonly GCHandle[] _polledStates = new GCHandle[2048]; + private static readonly IPollGroup _pollGroup = PollGroup.Create(); private static readonly Queue _flushPending = new(2048); - private static readonly Queue _pendingDisconnects = new(256); // Processed AFTER flush + private static readonly Queue _flushedPartials = new(256); + private static readonly ConcurrentQueue _disposed = new(); private static readonly Queue _throttled = new(256); private static readonly Queue _throttledPending = new(256); - private static readonly Queue _connectingQueue = new(2048); + private static readonly SortedSet _connecting = new(NetStateConnectingComparer.Instance); private static readonly HashSet _instances = new(2048); public static IReadOnlySet Instances => _instances; private readonly string _toString; private ClientVersion _version; private bool _running = true; - private IClientEncryption _encryption; + private volatile DecodePacket _packetDecoder; + private volatile EncodePacket _packetEncoder; private bool _flushQueued; - private bool _disconnectQueued; // Queued for disconnect processing (after flush) private long[] _packetThrottles; private long[] _packetCounts; private string _disconnectReason = string.Empty; internal ParserState _parserState = ParserState.AwaitingNextPacket; internal ProtocolState _protocolState = ProtocolState.AwaitingSeed; + internal GCHandle _handle; private bool _packetLogging; - // Whether ANY inbound bytes have arrived: what separates a slow client from a socket held open on - // purpose. See BanSettings.ReportBadConnects. - internal bool _receivedData; - - // Managed socket with buffers (handles lifecycle automatically) - internal RingSocket _socket; + public GCHandle Handle => _handle; + // Speed Hack Prevention + internal long _movementCredit; + internal long _nextMovementTime; private IAccount _account; internal enum ParserState @@ -97,29 +108,52 @@ public partial class NetState : IComparable, IValueLinkListNode, IValueLinkListNode _encryption; - set => _encryption = value; + get => _packetDecoder; + set => _packetDecoder = value; + } + + public EncodePacket PacketEncoder + { + get => _packetEncoder; + set => _packetEncoder = value; } public int CurrentPacket { get; internal set; } @@ -170,27 +210,13 @@ public partial class NetState : IComparable, IValueLinkListNode _running; - /// - /// Gets whether the socket is connected. - /// - public bool IsConnected => _socket != null; - - /// - /// Gets the socket handle. - /// - public nint SocketHandle => _socket?.Handle ?? 0; - - /// - /// Gets the local endpoint (address/port) the client connected to. - /// - public IPEndPoint LocalEndPoint => _socket != null ? SocketHelper.GetLocalEndPoint(_socket.Handle) : null; - - /// - /// Gets the send buffer for this connection. - /// - internal IORingBuffer SendBuffer => _socket?.SendBuffer; + public Socket Connection { get; private set; } public bool CompressionEnabled { get; set; } @@ -209,7 +235,15 @@ public partial class NetState : IComparable, IValueLinkListNode _account; - set => _account = value; + set + { + if (_account != null) + { + _connecting.Remove(this); + } + + _account = value; + } } public string Assistant { get; set; } @@ -244,9 +278,9 @@ public partial class NetState : IComparable, IValueLinkListNode PacketPerSecondThreshold) @@ -309,28 +343,7 @@ public partial class NetState : IComparable, IValueLinkListNode FindTrade(m)?.From.Container; public SecureTradeContainer AddTrade(NetState state) { @@ -408,7 +421,7 @@ public partial class NetState : IComparable, IValueLinkListNode url) + public void LaunchBrowser(string url) { this.SendMessageLocalized(Serial.MinusOne, -1, MessageType.Label, 0x35, 3, 501231); this.SendLaunchBrowser(url); @@ -430,14 +443,8 @@ public partial class NetState : IComparable, IValueLinkListNode.Empty; - return false; - } - - buffer = _socket.SendBuffer.GetWriteSpan(); - return buffer.Length > 0; + buffer = SendPipe.Writer.AvailableToWrite(); + return !(SendPipe.Writer.IsClosed || buffer.Length <= 0); } public void Send(ReadOnlySpan span) @@ -448,51 +455,28 @@ public partial class NetState : IComparable, IValueLinkListNode buffer.Length) - { - SendBufferExhausted(span.Length, buffer.Length); - return; + length = _packetEncoder(span, buffer); } else { span.CopyTo(buffer); } - // Then encrypt (if encryption is enabled) - _encryption?.ServerEncrypt(buffer[..length]); - if (PacketLogging) { LogPacket(span, false); } - _socket.SendBuffer.CommitWrite(length); + SendPipe.Writer.Advance((uint)length); if (!_flushQueued) { @@ -507,31 +491,6 @@ public partial class NetState : IComparable, IValueLinkListNode - /// Handles a packet that cannot be placed in the send buffer. - /// - /// - /// High unacked means a slow client holding the buffer; needed approaching capacity means the - /// buffer is too small for this shard and network.sendBufferSize should be raised. - /// - private void SendBufferExhausted(int needed, int writable) - { - var sendBuffer = _socket?.SendBuffer; - var unacked = sendBuffer?.InFlightBytes ?? 0; - var capacity = sendBuffer?.PhysicalSize ?? 0; - - logger.Warning( - "{NetState}: send buffer exhausted - needed {Needed} bytes, {Writable} writable, {Unacked} awaiting acknowledgement, {Capacity} capacity. Raise network.sendBufferSize (power of two) if this recurs on healthy connections.", - this, - needed, - writable, - unacked, - capacity - ); - - Disconnect($"Send buffer exhausted (needed {needed}, writable {writable}, unacked {unacked}, capacity {capacity})"); - } - private void StartPacketLog() { try @@ -574,34 +533,26 @@ public partial class NetState : IComparable, IValueLinkListNode, IValueLinkListNode, IValueLinkListNode, IValueLinkListNode= 4) { - var newSeed = (packetId << 24) | (packetReader.ReadByte() << 16) | (packetReader.ReadByte() << 8) | packetReader.ReadByte(); + int newSeed = (packetId << 24) | (packetReader.ReadByte() << 16) | (packetReader.ReadByte() << 8) | packetReader.ReadByte(); if (newSeed == 0) { - // No real client sends a zero seed, so this is deliberate garbage - // rather than a damaged connection — unlike the short-read branch - // below, which a fragmented first segment can reach honestly. - if (Bans.BanConfiguration.Settings.ReportBadConnects) - { - Bans.BanChannel.Report( - Address, - Bans.BanConfiguration.Settings.BadConnectDuration, - Bans.BanReasons.InvalidSeed - ); - } - Disconnect(string.Empty); return; } @@ -694,11 +602,8 @@ public partial class NetState : IComparable, IValueLinkListNode, IValueLinkListNode, IValueLinkListNode, IValueLinkListNode, IValueLinkListNode, IValueLinkListNode= 0) + _flushQueued = false; + + // We don't have a running check since we need to send the last bits of data even after a disconnect, but before a dispose. + if (Connection == null) + { + return true; + } + + var reader = SendPipe.Reader; + var buffer = reader.AvailableToRead(); + + if (reader.IsClosed || buffer.Length == 0) + { + return true; + } + + var bytesWritten = 0; + + try + { + bytesWritten = Connection.Send(buffer, SocketFlags.None); + } + catch (SocketException ex) + { + if (ex.SocketErrorCode != SocketError.WouldBlock) + { + logger.Debug(ex, "Disconnected due to a socket exception"); + Disconnect(string.Empty); + return true; + } + } + catch (Exception ex) + { + Disconnect($"Disconnected with error: {ex}"); + TraceException(ex); + return true; + } + + if (bytesWritten > 0) + { + NextActivityCheck = Core.TickCount + 90000; + reader.Advance((uint)bytesWritten); + } + + return bytesWritten == buffer.Length; + } + + private void DecodePacket(Span buffer, ref int length) + { + _packetDecoder?.Invoke(buffer, ref length); + } + + private void ReceiveData() + { + var writer = RecvPipe.Writer; + var buffer = writer.AvailableToWrite(); + + if (writer.IsClosed || buffer.Length == 0) { return; } - if (_socket.DisconnectPending) + var bytesWritten = 0; + + try { - LogInfo("Force disconnecting stuck socket..."); - _socketManager.DisconnectImmediate(_socket); + bytesWritten = Connection.Receive(buffer, SocketFlags.None); } - else + catch (SocketException ex) { - // Authenticated pre-game clients (login screens): send keep-alive instead of disconnecting. - // The 0xBD ClientVersionRequest resets NextActivityCheck via DataSent. - if (_account != null && Mobile == null) + if (ex.ErrorCode is not 54 and not 89 and not 995) { - this.SendClientVersionRequest(); - return; + logger.Debug(ex, "Disconnected due to a socket exception"); } + Disconnect(string.Empty); + } + catch (Exception ex) + { + Disconnect($"Disconnected with error: {ex}"); + TraceException(ex); + } + + if (bytesWritten <= 0) + { + Disconnect(string.Empty); + return; + } + + DecodePacket(buffer, ref bytesWritten); + + writer.Advance((uint)bytesWritten); + NextActivityCheck = Core.TickCount + 90000; + } + + private static void DisconnectUnattachedSockets() + { + var now = Core.Now; + + // Clear out any sockets that have been connecting for too long + while (_connecting.Count > 0) + { + var ns = _connecting.Min; + var socketTime = ns.ConnectedOn; + + // If the socket has been connected for less than the limit, we can stop checking + if (now - socketTime < ConnectingSocketIdleLimit) + { + break; + } + + // Socket must have finished the entire authentication process or be forcibly disconnected. + if (!ns.Running || !ns.SentFirstPacket || !ns.Seeded || ns.Account == null) + { + // Not sending a message because it will fill up the logs. + ns.Disconnect(null); + } + + _connecting.Remove(ns); + } + } + + public static void FlushAll() + { + while (_flushPending.Count != 0) + { + _flushPending.Dequeue()?.Flush(); + } + } + + public static void Slice() + { + DisconnectUnattachedSockets(); + + while (_throttled.Count > 0) + { + var ns = _throttled.Dequeue(); + if (ns.Running) + { + ns.HandleReceive(true); + } + } + + // This is enqueued by HandleReceive if already throttled and still throttled + while (_throttledPending.Count > 0) + { + _throttled.Enqueue(_throttledPending.Dequeue()); + } + + var count = _pollGroup.Poll(_polledStates); + + if (count > 0) + { + for (int i = 0; i < count; i++) + { + (_polledStates[i].Target as NetState)?.HandleReceive(); + _polledStates[i] = default; + } + } + + while (_flushPending.TryDequeue(out var ns)) + { + if (!ns.Flush()) + { + // Incomplete data, so we need to requeue + _flushedPartials.Enqueue(ns); + } + } + + var hasDisposes = false; + while (_disposed.TryDequeue(out var ns)) + { + hasDisposes = true; + ns.Dispose(); + } + + // If they weren't disconnected, requeue them + while (_flushedPartials.TryDequeue(out var ns)) + { + if (ns.Running) + { + _flushPending.Enqueue(ns); + } + } + + if (hasDisposes) + { + _pollGroup.Poll(_polledStates.Length); + } + } + + public void CheckAlive(long curTicks) + { + if (Connection != null && NextActivityCheck - curTicks < 0) + { LogInfo("Disconnecting due to inactivity..."); Disconnect("Disconnecting due to inactivity."); } } + public static void CheckAllAlive() + { + try + { + long curTicks = Core.TickCount; + + foreach (var ns in Instances) + { + ns.CheckAlive(curTicks); + } + } + catch (Exception ex) + { + TraceException(ex); + } + } + public void Trace(ReadOnlySpan buffer) { // We don't have data, so nothing to trace @@ -1086,24 +1084,17 @@ public partial class NetState : IComparable, IValueLinkListNode - /// Requests a graceful disconnect. The disconnect is queued and processed after the flush - /// queue in Slice(), ensuring Send() calls made in the same tick are processed first. - /// public void Disconnect(string reason) { - if (!_running || _socket == null) + if (!_running) { return; } - _disconnectReason = reason; + _running = false; - if (!_disconnectQueued) - { - _disconnectQueued = true; - _pendingDisconnects.Enqueue(this); - } + _disconnectReason = reason; + _disposed.Enqueue(this); } public static void TraceDisconnect(string reason, string ip) @@ -1130,27 +1121,19 @@ public partial class NetState : IComparable, IValueLinkListNode Dispose(); - - // Do not run this directly. Use Disconnect instead. - // This is available for testing cleanup only. - [Obsolete("Use Disconnect instead")] - public void Dispose() + private void Dispose() { - var wasRunning = _running; - _running = false; // It's possible we could queue for dispose multiple times - if (_socket == null) + if (Connection == null) { return; } TraceDisconnect(_disconnectReason, _toString); - // If still running, force immediate disconnect - if (wasRunning) + if (_running) { - _socketManager?.DisconnectImmediate(_socket); + throw new Exception("Disconnected a NetState that is still running."); } var m = Mobile; @@ -1160,17 +1143,21 @@ public partial class NetState : IComparable, IValueLinkListNode= 0 && slotId < _netStates.Length && _netStates[slotId] == this) + try { - _netStates[slotId] = null; + _pollGroup.Remove(Connection, _handle); + } + catch (Exception ex) + { + TraceException(ex); } - // Note: RingSocketManager handles cleanup of ring resources (unregister, close, buffer release) - // when it processes the disconnect event. We just clear our reference. - _socket = null; + Connection.Close(); + _handle.Free(); + RecvPipe.Dispose(); + SendPipe.Dispose(); Mobile = null; @@ -1181,9 +1168,46 @@ public partial class NetState : IComparable, IValueLinkListNode + { + public static readonly IComparer Instance = new NetStateConnectingComparer(); + + public int Compare(NetState x, NetState y) + { + if (x == null && y == null) + { + return 0; + } + + if (x == null) + { + return -1; + } + + if (y == null) + { + return 1; + } + + if (ReferenceEquals(x, y)) + { + return 0; + } + + var connectedOn = x.ConnectedOn.CompareTo(y.ConnectedOn); + if (connectedOn != 0) + { + return connectedOn; + } + + return x.CompareTo(y); + } + } } diff --git a/Projects/Server/Network/NetState/ProtocolChanges.cs b/Projects/Server/Network/NetState/ProtocolChanges.cs index fb0a4a183..6b677e422 100644 --- a/Projects/Server/Network/NetState/ProtocolChanges.cs +++ b/Projects/Server/Network/NetState/ProtocolChanges.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ProtocolChanges.cs * * * diff --git a/Projects/Server/Network/NetworkCompression.cs b/Projects/Server/Network/NetworkCompression.cs index 12b4108da..e2ef4d0ec 100644 --- a/Projects/Server/Network/NetworkCompression.cs +++ b/Projects/Server/Network/NetworkCompression.cs @@ -1,7 +1,4 @@ using System; -using System.Buffers.Binary; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace Server.Network; @@ -10,11 +7,15 @@ namespace Server.Network; /// public static class NetworkCompression { + private const int CountIndex = 0; + private const int ValueIndex = 1; + // UO packets may not exceed 64kb in length public const int BufferSize = 0x10000; // Optimal compression ratio is 2 / 8; worst compression ratio is 11 / 8 private const int MinimalCodeLength = 2; + private const int MaximalCodeLength = 11; // Fixed overhead, in bits, per compression call private const int TerminalCodeLength = 4; @@ -22,164 +23,99 @@ public static class NetworkCompression // If our input exceeds this length, we cannot possibly compress it within the buffer private const int DefiniteOverflow = (BufferSize * 8 - TerminalCodeLength) / MinimalCodeLength; - // Packed Table: High 16 bits = Length, Low 16 bits = Value - private static readonly uint[] _packedHuffmanTable = new uint[257]; - - static NetworkCompression() + private static readonly int[] _huffmanTable = { - ReadOnlySpan rawTable = [ - 0x2, 0x000, 0x5, 0x01F, 0x6, 0x022, 0x7, 0x034, 0x7, 0x075, 0x6, 0x028, 0x6, 0x03B, 0x7, 0x032, - 0x8, 0x0E0, 0x8, 0x062, 0x7, 0x056, 0x8, 0x079, 0x9, 0x19D, 0x8, 0x097, 0x6, 0x02A, 0x7, 0x057, - 0x8, 0x071, 0x8, 0x05B, 0x9, 0x1CC, 0x8, 0x0A7, 0x7, 0x025, 0x7, 0x04F, 0x8, 0x066, 0x8, 0x07D, - 0x9, 0x191, 0x9, 0x1CE, 0x7, 0x03F, 0x9, 0x090, 0x8, 0x059, 0x8, 0x07B, 0x8, 0x091, 0x8, 0x0C6, - 0x6, 0x02D, 0x9, 0x186, 0x8, 0x06F, 0x9, 0x093, 0xA, 0x1CC, 0x8, 0x05A, 0xA, 0x1AE, 0xA, 0x1C0, - 0x9, 0x148, 0x9, 0x14A, 0x9, 0x082, 0xA, 0x19F, 0x9, 0x171, 0x9, 0x120, 0x9, 0x0E7, 0xA, 0x1F3, - 0x9, 0x14B, 0x9, 0x100, 0x9, 0x190, 0x6, 0x013, 0x9, 0x161, 0x9, 0x125, 0x9, 0x133, 0x9, 0x195, - 0x9, 0x173, 0x9, 0x1CA, 0x9, 0x086, 0x9, 0x1E9, 0x9, 0x0DB, 0x9, 0x1EC, 0x9, 0x08B, 0x9, 0x085, - 0x5, 0x00A, 0x8, 0x096, 0x8, 0x09C, 0x9, 0x1C3, 0x9, 0x19C, 0x9, 0x08F, 0x9, 0x18F, 0x9, 0x091, - 0x9, 0x087, 0x9, 0x0C6, 0x9, 0x177, 0x9, 0x089, 0x9, 0x0D6, 0x9, 0x08C, 0x9, 0x1EE, 0x9, 0x1EB, - 0x9, 0x084, 0x9, 0x164, 0x9, 0x175, 0x9, 0x1CD, 0x8, 0x05E, 0x9, 0x088, 0x9, 0x12B, 0x9, 0x172, - 0x9, 0x10A, 0x9, 0x08D, 0x9, 0x13A, 0x9, 0x11C, 0xA, 0x1E1, 0xA, 0x1E0, 0x9, 0x187, 0xA, 0x1DC, - 0xA, 0x1DF, 0x7, 0x074, 0x9, 0x19F, 0x8, 0x08D, 0x8, 0x0E4, 0x7, 0x079, 0x9, 0x0EA, 0x9, 0x0E1, - 0x8, 0x040, 0x7, 0x041, 0x9, 0x10B, 0x9, 0x0B0, 0x8, 0x06A, 0x8, 0x0C1, 0x7, 0x071, 0x7, 0x078, - 0x8, 0x0B1, 0x9, 0x14C, 0x7, 0x043, 0x8, 0x076, 0x7, 0x066, 0x7, 0x04D, 0x9, 0x08A, 0x6, 0x02F, - 0x8, 0x0C9, 0x9, 0x0CE, 0x9, 0x149, 0x9, 0x160, 0xA, 0x1BA, 0xA, 0x19E, 0xA, 0x39F, 0x9, 0x0E5, - 0x9, 0x194, 0x9, 0x184, 0x9, 0x126, 0x7, 0x030, 0x8, 0x06C, 0x9, 0x121, 0x9, 0x1E8, 0xA, 0x1C1, - 0xA, 0x11D, 0xA, 0x163, 0xA, 0x385, 0xA, 0x3DB, 0xA, 0x17D, 0xA, 0x106, 0xA, 0x397, 0xA, 0x24E, - 0x7, 0x02E, 0x8, 0x098, 0xA, 0x33C, 0xA, 0x32E, 0xA, 0x1E9, 0x9, 0x0BF, 0xA, 0x3DF, 0xA, 0x1DD, - 0xA, 0x32D, 0xA, 0x2ED, 0xA, 0x30B, 0xA, 0x107, 0xA, 0x2E8, 0xA, 0x3DE, 0xA, 0x125, 0xA, 0x1E8, - 0x9, 0x0E9, 0xA, 0x1CD, 0xA, 0x1B5, 0x9, 0x165, 0xA, 0x232, 0xA, 0x2E1, 0xB, 0x3AE, 0xB, 0x3C6, - 0xB, 0x3E2, 0xA, 0x205, 0xA, 0x29A, 0xA, 0x248, 0xA, 0x2CD, 0xA, 0x23B, 0xB, 0x3C5, 0xA, 0x251, - 0xA, 0x2E9, 0xA, 0x252, 0x9, 0x1EA, 0xB, 0x3A0, 0xB, 0x391, 0xA, 0x23C, 0xB, 0x392, 0xB, 0x3D5, - 0xA, 0x233, 0xA, 0x2CC, 0xB, 0x390, 0xA, 0x1BB, 0xB, 0x3A1, 0xB, 0x3C4, 0xA, 0x211, 0xA, 0x203, - 0x9, 0x12A, 0xA, 0x231, 0xB, 0x3E0, 0xA, 0x29B, 0xB, 0x3D7, 0xA, 0x202, 0xB, 0x3AD, 0xA, 0x213, - 0xA, 0x253, 0xA, 0x32C, 0xA, 0x23D, 0xA, 0x23F, 0xA, 0x32F, 0xA, 0x11C, 0xA, 0x384, 0xA, 0x31C, - 0xA, 0x17C, 0xA, 0x30A, 0xA, 0x2E0, 0xA, 0x276, 0xA, 0x250, 0xB, 0x3E3, 0xA, 0x396, 0xA, 0x18F, - 0xA, 0x204, 0xA, 0x206, 0xA, 0x230, 0xA, 0x265, 0xA, 0x212, 0xA, 0x23E, 0xB, 0x3AC, 0xB, 0x393, - 0xB, 0x3E1, 0xA, 0x1DE, 0xB, 0x3D6, 0xA, 0x31D, 0xB, 0x3E5, 0xB, 0x3E4, 0xA, 0x207, 0xB, 0x3C7, - 0xA, 0x277, 0xB, 0x3D4, 0x8, 0x0C0, 0xA, 0x162, 0xA, 0x3DA, 0xA, 0x124, 0xA, 0x1B4, 0xA, 0x264, - 0xA, 0x33D, 0xA, 0x1D1, 0xA, 0x1AF, 0xA, 0x39E, 0xA, 0x24F, 0xB, 0x373, 0xA, 0x249, 0xB, 0x372, - 0x9, 0x167, 0xA, 0x210, 0xA, 0x23A, 0xA, 0x1B8, 0xB, 0x3AF, 0xA, 0x18E, 0xA, 0x2EC, 0x7, 0x062, - 0x4, 0x00D - ]; - - for (var i = 0; i < 257; i++) - { - var len = (uint)rawTable[i * 2]; - var val = (uint)rawTable[i * 2 + 1]; - _packedHuffmanTable[i] = (len << 16) | val; - } - } + 0x2, 0x000, 0x5, 0x01F, 0x6, 0x022, 0x7, 0x034, 0x7, 0x075, 0x6, 0x028, 0x6, 0x03B, 0x7, 0x032, + 0x8, 0x0E0, 0x8, 0x062, 0x7, 0x056, 0x8, 0x079, 0x9, 0x19D, 0x8, 0x097, 0x6, 0x02A, 0x7, 0x057, + 0x8, 0x071, 0x8, 0x05B, 0x9, 0x1CC, 0x8, 0x0A7, 0x7, 0x025, 0x7, 0x04F, 0x8, 0x066, 0x8, 0x07D, + 0x9, 0x191, 0x9, 0x1CE, 0x7, 0x03F, 0x9, 0x090, 0x8, 0x059, 0x8, 0x07B, 0x8, 0x091, 0x8, 0x0C6, + 0x6, 0x02D, 0x9, 0x186, 0x8, 0x06F, 0x9, 0x093, 0xA, 0x1CC, 0x8, 0x05A, 0xA, 0x1AE, 0xA, 0x1C0, + 0x9, 0x148, 0x9, 0x14A, 0x9, 0x082, 0xA, 0x19F, 0x9, 0x171, 0x9, 0x120, 0x9, 0x0E7, 0xA, 0x1F3, + 0x9, 0x14B, 0x9, 0x100, 0x9, 0x190, 0x6, 0x013, 0x9, 0x161, 0x9, 0x125, 0x9, 0x133, 0x9, 0x195, + 0x9, 0x173, 0x9, 0x1CA, 0x9, 0x086, 0x9, 0x1E9, 0x9, 0x0DB, 0x9, 0x1EC, 0x9, 0x08B, 0x9, 0x085, + 0x5, 0x00A, 0x8, 0x096, 0x8, 0x09C, 0x9, 0x1C3, 0x9, 0x19C, 0x9, 0x08F, 0x9, 0x18F, 0x9, 0x091, + 0x9, 0x087, 0x9, 0x0C6, 0x9, 0x177, 0x9, 0x089, 0x9, 0x0D6, 0x9, 0x08C, 0x9, 0x1EE, 0x9, 0x1EB, + 0x9, 0x084, 0x9, 0x164, 0x9, 0x175, 0x9, 0x1CD, 0x8, 0x05E, 0x9, 0x088, 0x9, 0x12B, 0x9, 0x172, + 0x9, 0x10A, 0x9, 0x08D, 0x9, 0x13A, 0x9, 0x11C, 0xA, 0x1E1, 0xA, 0x1E0, 0x9, 0x187, 0xA, 0x1DC, + 0xA, 0x1DF, 0x7, 0x074, 0x9, 0x19F, 0x8, 0x08D, 0x8, 0x0E4, 0x7, 0x079, 0x9, 0x0EA, 0x9, 0x0E1, + 0x8, 0x040, 0x7, 0x041, 0x9, 0x10B, 0x9, 0x0B0, 0x8, 0x06A, 0x8, 0x0C1, 0x7, 0x071, 0x7, 0x078, + 0x8, 0x0B1, 0x9, 0x14C, 0x7, 0x043, 0x8, 0x076, 0x7, 0x066, 0x7, 0x04D, 0x9, 0x08A, 0x6, 0x02F, + 0x8, 0x0C9, 0x9, 0x0CE, 0x9, 0x149, 0x9, 0x160, 0xA, 0x1BA, 0xA, 0x19E, 0xA, 0x39F, 0x9, 0x0E5, + 0x9, 0x194, 0x9, 0x184, 0x9, 0x126, 0x7, 0x030, 0x8, 0x06C, 0x9, 0x121, 0x9, 0x1E8, 0xA, 0x1C1, + 0xA, 0x11D, 0xA, 0x163, 0xA, 0x385, 0xA, 0x3DB, 0xA, 0x17D, 0xA, 0x106, 0xA, 0x397, 0xA, 0x24E, + 0x7, 0x02E, 0x8, 0x098, 0xA, 0x33C, 0xA, 0x32E, 0xA, 0x1E9, 0x9, 0x0BF, 0xA, 0x3DF, 0xA, 0x1DD, + 0xA, 0x32D, 0xA, 0x2ED, 0xA, 0x30B, 0xA, 0x107, 0xA, 0x2E8, 0xA, 0x3DE, 0xA, 0x125, 0xA, 0x1E8, + 0x9, 0x0E9, 0xA, 0x1CD, 0xA, 0x1B5, 0x9, 0x165, 0xA, 0x232, 0xA, 0x2E1, 0xB, 0x3AE, 0xB, 0x3C6, + 0xB, 0x3E2, 0xA, 0x205, 0xA, 0x29A, 0xA, 0x248, 0xA, 0x2CD, 0xA, 0x23B, 0xB, 0x3C5, 0xA, 0x251, + 0xA, 0x2E9, 0xA, 0x252, 0x9, 0x1EA, 0xB, 0x3A0, 0xB, 0x391, 0xA, 0x23C, 0xB, 0x392, 0xB, 0x3D5, + 0xA, 0x233, 0xA, 0x2CC, 0xB, 0x390, 0xA, 0x1BB, 0xB, 0x3A1, 0xB, 0x3C4, 0xA, 0x211, 0xA, 0x203, + 0x9, 0x12A, 0xA, 0x231, 0xB, 0x3E0, 0xA, 0x29B, 0xB, 0x3D7, 0xA, 0x202, 0xB, 0x3AD, 0xA, 0x213, + 0xA, 0x253, 0xA, 0x32C, 0xA, 0x23D, 0xA, 0x23F, 0xA, 0x32F, 0xA, 0x11C, 0xA, 0x384, 0xA, 0x31C, + 0xA, 0x17C, 0xA, 0x30A, 0xA, 0x2E0, 0xA, 0x276, 0xA, 0x250, 0xB, 0x3E3, 0xA, 0x396, 0xA, 0x18F, + 0xA, 0x204, 0xA, 0x206, 0xA, 0x230, 0xA, 0x265, 0xA, 0x212, 0xA, 0x23E, 0xB, 0x3AC, 0xB, 0x393, + 0xB, 0x3E1, 0xA, 0x1DE, 0xB, 0x3D6, 0xA, 0x31D, 0xB, 0x3E5, 0xB, 0x3E4, 0xA, 0x207, 0xB, 0x3C7, + 0xA, 0x277, 0xB, 0x3D4, 0x8, 0x0C0, 0xA, 0x162, 0xA, 0x3DA, 0xA, 0x124, 0xA, 0x1B4, 0xA, 0x264, + 0xA, 0x33D, 0xA, 0x1D1, 0xA, 0x1AF, 0xA, 0x39E, 0xA, 0x24F, 0xB, 0x373, 0xA, 0x249, 0xB, 0x372, + 0x9, 0x167, 0xA, 0x210, 0xA, 0x23A, 0xA, 0x1B8, 0xB, 0x3AF, 0xA, 0x18E, 0xA, 0x2EC, 0x7, 0x062, + 0x4, 0x00D + }; public static int Compress(ReadOnlySpan input, Span output) { - // output.Length < 4 underflows safeOutputLength below (nuint), defeating the hot loop's - // bounds check. Reachable whenever the send buffer is nearly full. - if (input.Length > DefiniteOverflow || output.Length < 4) + if (input.Length > DefiniteOverflow) { return 0; } - ref var inputRef = ref MemoryMarshal.GetReference(input); - ref var outputRef = ref MemoryMarshal.GetReference(output); - ref var tableRef = ref MemoryMarshal.GetReference(_packedHuffmanTable.AsSpan()); + int bitCount = 0; + int bitValue = 0; - nuint i = 0; - ulong bitValue = 0; - var bitCount = 0; - nuint outputIdx = 0; - var safeOutputLength = (nuint)output.Length - 4; - var unrolledLimit = (nuint)input.Length - 1; + int inputIdx = 0; + int outputIdx = 0; - // Unrolled 2x hot loop using native integer indices - while (i < unrolledLimit) + while (inputIdx < input.Length) { - var b1 = Unsafe.Add(ref inputRef, i); - var entry1 = Unsafe.Add(ref tableRef, b1); - var len1 = (int)(entry1 >> 16); - ulong val1 = entry1 & 0xFFFF; + int i = input[inputIdx++] << 1; - bitValue = (bitValue << len1) | val1; - bitCount += len1; + bitCount += _huffmanTable[i]; + bitValue = (bitValue << _huffmanTable[i]) | _huffmanTable[i + 1]; - var b2 = Unsafe.Add(ref inputRef, i + 1); - var entry2 = Unsafe.Add(ref tableRef, b2); - var len2 = (int)(entry2 >> 16); - ulong val2 = entry2 & 0xFFFF; - - bitValue = (bitValue << len2) | val2; - bitCount += len2; - - if (bitCount >= 32) + while (bitCount >= 8) { - if (outputIdx > safeOutputLength) + bitCount -= 8; + + if (output.Length < outputIdx + 1) { return 0; } - bitCount -= 32; - var word = (uint)(bitValue >> bitCount); - Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputRef, outputIdx), BinaryPrimitives.ReverseEndianness(word)); - outputIdx += 4; - } - - i += 2; - } - - if (i < (nuint)input.Length) - { - var b = Unsafe.Add(ref inputRef, i); - var entry = Unsafe.Add(ref tableRef, b); - var len = (int)(entry >> 16); - ulong val = entry & 0xFFFF; - - bitValue = (bitValue << len) | val; - bitCount += len; - - if (bitCount >= 32) - { - if (outputIdx > safeOutputLength) - { - return 0; - } - - bitCount -= 32; - var word = (uint)(bitValue >> bitCount); - Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputRef, outputIdx), BinaryPrimitives.ReverseEndianness(word)); - outputIdx += 4; + output[outputIdx++] = (byte)(bitValue >> bitCount); } } - // Terminal code logic - var termEntry = Unsafe.Add(ref tableRef, 256); - var termLen = (int)(termEntry >> 16); - ulong termVal = termEntry & 0xFFFF; + // terminal code + bitCount += _huffmanTable[0x200]; + bitValue = (bitValue << _huffmanTable[0x200]) | _huffmanTable[0x201]; - bitValue = (bitValue << termLen) | termVal; - bitCount += termLen; - - var remainder = bitCount & 7; - if (remainder != 0) + // align on byte boundary + if ((bitCount & 7) != 0) { - var padding = 8 - remainder; - bitValue <<= padding; - bitCount += padding; + bitValue <<= 8 - (bitCount & 7); + bitCount += 8 - (bitCount & 7); } - bitValue <<= 64 - bitCount; - - while (bitCount > 0) + while (bitCount >= 8) { - if (outputIdx >= (nuint)output.Length) + bitCount -= 8; + + if (output.Length < outputIdx + 1) { return 0; } - Unsafe.Add(ref outputRef, outputIdx++) = (byte)(bitValue >> 56); - bitValue <<= 8; - bitCount -= 8; + output[outputIdx++] = (byte)(bitValue >> bitCount); } - return (int)outputIdx; + return outputIdx; } } diff --git a/Projects/Server/Network/PacketHandler.cs b/Projects/Server/Network/PacketHandler.cs index 4db106bc2..f8ef9bf0c 100644 --- a/Projects/Server/Network/PacketHandler.cs +++ b/Projects/Server/Network/PacketHandler.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: PacketHandler.cs * * * diff --git a/Projects/Server/Network/PacketUtilities.cs b/Projects/Server/Network/PacketUtilities.cs index 4a5780b76..7c49a1c59 100644 --- a/Projects/Server/Network/PacketUtilities.cs +++ b/Projects/Server/Network/PacketUtilities.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: PacketUtilities.cs * * * diff --git a/Projects/Server/Network/Packets/IBroadcastFilter.cs b/Projects/Server/Network/Packets/IBroadcastFilter.cs deleted file mode 100644 index 5068b0a21..000000000 --- a/Projects/Server/Network/Packets/IBroadcastFilter.cs +++ /dev/null @@ -1,21 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: IBroadcastFilter.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -namespace Server.Network; - -public interface IBroadcastFilter -{ - bool Allow(NetState state); -} diff --git a/Projects/Server/Network/Packets/IncomingPackets.cs b/Projects/Server/Network/Packets/IncomingPackets.cs index 952965580..d257e8b5b 100644 --- a/Projects/Server/Network/Packets/IncomingPackets.cs +++ b/Projects/Server/Network/Packets/IncomingPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingPackets.cs * * * diff --git a/Projects/Server/Network/Packets/OutgoingAccountPackets.cs b/Projects/Server/Network/Packets/OutgoingAccountPackets.cs index c8923a933..0da200248 100644 --- a/Projects/Server/Network/Packets/OutgoingAccountPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingAccountPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingAccountPackets.cs * * * @@ -74,7 +74,7 @@ public static class OutgoingAccountPackets writer.Write((ushort)length); writer.Write((ushort)0); // Count & Placeholder - var count = 0; + int count = 0; for (var i = 0; i < a.Length; ++i) { @@ -89,7 +89,7 @@ public static class OutgoingAccountPackets var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); count++; - writer.WriteLatin1(name, 30); + writer.WriteAscii(name, 30); writer.Clear(30); // Password (empty) } } @@ -109,7 +109,7 @@ public static class OutgoingAccountPackets * Sends a requests for the client version */ [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendClientVersionRequest(this NetState ns) => ns?.Send([0xBD, 0x00, 0x03]); + public static void SendClientVersionRequest(this NetState ns) => ns?.Send(stackalloc byte[] { 0xBD, 0x00, 0x03 }); /** * Packet: 0x85 @@ -265,7 +265,7 @@ public static class OutgoingAccountPackets writer.Write((byte)count); - for (var i = 0; i < count; i++) + for (int i = 0; i < count; i++) { var m = a[i]; @@ -276,7 +276,7 @@ public static class OutgoingAccountPackets else { var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - writer.WriteLatin1(name, 30); + writer.WriteAscii(name, 30); writer.Clear(30); // password } } @@ -330,7 +330,7 @@ public static class OutgoingAccountPackets writer.Write((ushort)length); writer.Write((byte)count); // TODO: It is probably more proper to use count. - for (var i = 0; i < count; i++) + for (int i = 0; i < count; i++) { var m = acct[i]; @@ -341,20 +341,20 @@ public static class OutgoingAccountPackets else { var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - writer.WriteLatin1(name, 30); + writer.WriteAscii(name, 30); writer.Clear(30); // password } } writer.Write((byte)cityInfo.Length); - for (var i = 0; i < cityInfo.Length; ++i) + for (int i = 0; i < cityInfo.Length; ++i) { var ci = cityInfo[i]; writer.Write((byte)i); - writer.WriteLatin1(ci.City, textLength); - writer.WriteLatin1(ci.Building, textLength); + writer.WriteAscii(ci.City, textLength); + writer.WriteAscii(ci.Building, textLength); if (client70130) { writer.Write(ci.X); @@ -428,7 +428,7 @@ public static class OutgoingAccountPackets var si = info[i]; writer.Write((ushort)i); - writer.WriteLatin1(si.Name, 32); + writer.WriteAscii(si.Name, 32); writer.Write((byte)si.FullPercent); writer.Write((sbyte)si.TimeZone); // UO only supports IPv4 diff --git a/Projects/Server/Network/Packets/OutgoingCombatPackets.cs b/Projects/Server/Network/Packets/OutgoingCombatPackets.cs index 2f97538a4..3c4ba9c26 100644 --- a/Projects/Server/Network/Packets/OutgoingCombatPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingCombatPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingCombatPackets.cs * * * diff --git a/Projects/Server/Network/Packets/OutgoingContainerPackets.cs b/Projects/Server/Network/Packets/OutgoingContainerPackets.cs index fdb71bf90..1f72e395c 100644 --- a/Projects/Server/Network/Packets/OutgoingContainerPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingContainerPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingContainerPackets.cs * * * @@ -33,7 +33,7 @@ public static class OutgoingContainerPackets return; } - if (Core.AOS && ns.NewSpellbook) + if (ns.NewSpellbook) { ns.SendNewSpellbookContent(book, graphic, offset, content); } diff --git a/Projects/Server/Network/Packets/OutgoingDamagePackets.cs b/Projects/Server/Network/Packets/OutgoingDamagePackets.cs index 8b5c9ec9b..83d0f8950 100644 --- a/Projects/Server/Network/Packets/OutgoingDamagePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingDamagePackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingDamagePackets.cs * * * diff --git a/Projects/Server/Network/Packets/OutgoingEffectPackets.cs b/Projects/Server/Network/Packets/OutgoingEffectPackets.cs index 7cbe0d48b..a88e904b3 100644 --- a/Projects/Server/Network/Packets/OutgoingEffectPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEffectPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingEffectPackets.cs * * * @@ -32,7 +32,7 @@ public static class OutgoingEffectPackets return; } - var buffer = stackalloc byte[SoundPacketLength].InitializePacket(); + Span buffer = stackalloc byte[SoundPacketLength].InitializePacket(); CreateSoundEffect(buffer, soundID, target); ns.Send(buffer); diff --git a/Projects/Server/Network/Packets/OutgoingEntityPackets.cs b/Projects/Server/Network/Packets/OutgoingEntityPackets.cs index b51c56853..9ab810d9c 100644 --- a/Projects/Server/Network/Packets/OutgoingEntityPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEntityPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingEntityPackets.cs * * * @@ -54,7 +54,7 @@ public static class OutgoingEntityPackets return; } - var buffer = stackalloc byte[OPLPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OPLPacketLength].InitializePacket(); CreateOPLInfo(buffer, serial, hash); ns.Send(buffer); @@ -79,7 +79,7 @@ public static class OutgoingEntityPackets return; } - var buffer = stackalloc byte[RemoveEntityLength].InitializePacket(); + Span buffer = stackalloc byte[RemoveEntityLength].InitializePacket(); CreateRemoveEntity(buffer, serial); ns.Send(buffer); @@ -96,12 +96,12 @@ public static class OutgoingEntityPackets writer.Write((byte)0xF3); // Packet ID writer.Write((short)0x1); // command - var type = 0; - var gfx = 0; - var amount = 1; - var hue = 0; + int type = 0; + int gfx = 0; + int amount = 1; + int hue = 0; byte light = 0; - var flags = 0; + int flags = 0; if (entity is BaseMulti multi) { diff --git a/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs b/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs index 97c296424..0e2109eb9 100644 --- a/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingEquipmentPackets.cs * * * @@ -63,7 +63,7 @@ public static class OutgoingEquipmentPackets writer.Write(-3); // crafted by writer.Write((ushort)crafterName.Length); - writer.WriteLatin1(crafterName); + writer.WriteAscii(crafterName); } if (unidentified) diff --git a/Projects/Server/Network/Packets/OutgoingItemPackets.cs b/Projects/Server/Network/Packets/OutgoingItemPackets.cs index e20733c5a..645465042 100644 --- a/Projects/Server/Network/Packets/OutgoingItemPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingItemPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingItemPackets.cs * * * @@ -88,7 +88,7 @@ public static class OutgoingItemPackets return; } - var buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); var length = ns.StygianAbyss ? OutgoingEntityPackets.CreateWorldEntity(buffer, item, ns.HighSeas) : diff --git a/Projects/Server/Network/Packets/OutgoingLightPackets.cs b/Projects/Server/Network/Packets/OutgoingLightPackets.cs index f18d24b51..0961f45a6 100644 --- a/Projects/Server/Network/Packets/OutgoingLightPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingLightPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingLightPackets.cs * * * diff --git a/Projects/Server/Network/Packets/OutgoingMapPackets.cs b/Projects/Server/Network/Packets/OutgoingMapPackets.cs index 45c5cdb54..48bbc2bdc 100644 --- a/Projects/Server/Network/Packets/OutgoingMapPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMapPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingMapPackets.cs * * * @@ -37,7 +37,7 @@ public static class OutgoingMapPackets writer.Write((ushort)0x18); // Subpacket writer.Write(4); - for (var i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { var map = Map.Maps[i]; diff --git a/Projects/Server/Network/Packets/OutgoingMenuPackets.cs b/Projects/Server/Network/Packets/OutgoingMenuPackets.cs index 21382eebe..96787c9f0 100644 --- a/Projects/Server/Network/Packets/OutgoingMenuPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMenuPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingMenuPackets.cs * * * @@ -47,7 +47,7 @@ public static class OutgoingMenuPackets int entriesLength = (byte)entries.Length; var maxLength = 11 + questionLength; - for (var i = 0; i < entriesLength; i++) + for (int i = 0; i < entriesLength; i++) { maxLength += 5 + entries[i].Name?.Length ?? 0; // could be trimmed } @@ -62,7 +62,7 @@ public static class OutgoingMenuPackets if (question != null) { - writer.WriteLatin1(question); + writer.WriteAscii(question); } writer.Write((byte)entriesLength); @@ -84,7 +84,7 @@ public static class OutgoingMenuPackets { var nameLength = name.Length; writer.Write((byte)nameLength); - writer.WriteLatin1(name); + writer.WriteAscii(name); } } @@ -106,7 +106,7 @@ public static class OutgoingMenuPackets int answersLength = (byte)answers.Length; var maxLength = 11 + questionLength; - for (var i = 0; i < answersLength; i++) + for (int i = 0; i < answersLength; i++) { maxLength += 5 + answers[i]?.Length ?? 0; // could be trimmed } @@ -120,7 +120,7 @@ public static class OutgoingMenuPackets if (question != null) { - writer.WriteLatin1(question); + writer.WriteAscii(question); } writer.Write((byte)answersLength); @@ -139,7 +139,7 @@ public static class OutgoingMenuPackets { var nameLength = answer.Length; writer.Write((byte)nameLength); - writer.WriteLatin1(answer); + writer.WriteAscii(answer); } } diff --git a/Projects/Server/Network/Packets/OutgoingMessagePackets.Broadcast.cs b/Projects/Server/Network/Packets/OutgoingMessagePackets.Broadcast.cs deleted file mode 100644 index dc48ff324..000000000 --- a/Projects/Server/Network/Packets/OutgoingMessagePackets.Broadcast.cs +++ /dev/null @@ -1,182 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: OutgoingMessagePackets.Broadcast.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Network; - -public static partial class OutgoingMessagePackets -{ - public static void BroadcastMessageLocalized( - Map map, Point3D location, - Serial serial, int graphic, MessageType type, int hue, int font, - int number, ReadOnlySpan name, ReadOnlySpan args, - TFilter filter - ) where TFilter : struct, IBroadcastFilter - { - if (map == null) - { - return; - } - - var buffer = stackalloc byte[GetMaxMessageLocalizedLength(args.Length)].InitializePacket(); - - foreach (var state in map.GetClientsInRange(location)) - { - if (filter.Allow(state)) - { - var length = CreateMessageLocalized( - buffer, serial, graphic, type, hue, font, number, name, args - ); - - if (length != buffer.Length) - { - buffer = buffer[..length]; - } - - state.Send(buffer); - } - } - } - - public static void BroadcastMessageLocalized( - Map map, Point3D location, int range, - Serial serial, int graphic, MessageType type, int hue, int font, - int number, ReadOnlySpan name, ReadOnlySpan args, - TFilter filter - ) where TFilter : struct, IBroadcastFilter - { - if (map == null) - { - return; - } - - var buffer = stackalloc byte[GetMaxMessageLocalizedLength(args.Length)].InitializePacket(); - - foreach (var state in map.GetClientsInRange(location, range)) - { - if (filter.Allow(state)) - { - var length = CreateMessageLocalized( - buffer, serial, graphic, type, hue, font, number, name, args - ); - - if (length != buffer.Length) - { - buffer = buffer[..length]; - } - - state.Send(buffer); - } - } - } - - public static void BroadcastMessageLocalizedAffix( - Map map, Point3D location, - Serial serial, int graphic, MessageType type, int hue, int font, - int number, ReadOnlySpan name, AffixType affixType, - ReadOnlySpan affix, ReadOnlySpan args, - TFilter filter - ) where TFilter : struct, IBroadcastFilter - { - if (map == null) - { - return; - } - - var buffer = stackalloc byte[GetMaxMessageLocalizedAffixLength(affix.Length, args.Length)].InitializePacket(); - - foreach (var state in map.GetClientsInRange(location)) - { - if (filter.Allow(state)) - { - var length = CreateMessageLocalizedAffix( - buffer, serial, graphic, type, hue, font, number, name, affixType, affix, args - ); - - if (length != buffer.Length) - { - buffer = buffer[..length]; - } - - state.Send(buffer); - } - } - } - - public static void BroadcastMessage( - Map map, Point3D location, - Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, - string lang, ReadOnlySpan name, ReadOnlySpan text, - TFilter filter - ) where TFilter : struct, IBroadcastFilter - { - if (map == null) - { - return; - } - - var buffer = stackalloc byte[GetMaxMessageLength(text.Length)].InitializePacket(); - - foreach (var state in map.GetClientsInRange(location)) - { - if (filter.Allow(state)) - { - var length = CreateMessage( - buffer, serial, graphic, type, hue, font, ascii, lang, name, text - ); - - if (length != buffer.Length) - { - buffer = buffer[..length]; - } - - state.Send(buffer); - } - } - } - - public static void BroadcastMessage( - Map map, Point3D location, int range, - Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, - string lang, ReadOnlySpan name, ReadOnlySpan text, - TFilter filter - ) where TFilter : struct, IBroadcastFilter - { - if (map == null) - { - return; - } - - var buffer = stackalloc byte[GetMaxMessageLength(text.Length)].InitializePacket(); - - foreach (var state in map.GetClientsInRange(location, range)) - { - if (filter.Allow(state)) - { - var length = CreateMessage( - buffer, serial, graphic, type, hue, font, ascii, lang, name, text - ); - - if (length != buffer.Length) - { - buffer = buffer[..length]; - } - - state.Send(buffer); - } - } - } -} diff --git a/Projects/Server/Network/Packets/OutgoingMessagePackets.Interpolated.cs b/Projects/Server/Network/Packets/OutgoingMessagePackets.Interpolated.cs deleted file mode 100644 index e1798688c..000000000 --- a/Projects/Server/Network/Packets/OutgoingMessagePackets.Interpolated.cs +++ /dev/null @@ -1,57 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: OutgoingMessagePackets.Interpolated.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using Server.Buffers; - -namespace Server.Network; - -public static partial class OutgoingMessagePackets -{ - public static void SendMessageLocalized( - this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, int number, - ReadOnlySpan name, - ref RawInterpolatedStringHandler args - ) - { - ns.SendMessageLocalized(serial, graphic, type, hue, font, number, name, args.Text); - args.Clear(); - } - - public static void SendMessageLocalizedAffix( - this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, int number, - ReadOnlySpan name, AffixType affixType, ReadOnlySpan affix, - ref RawInterpolatedStringHandler args - ) - { - ns.SendMessageLocalizedAffix( - serial, graphic, type, hue, font, number, name, affixType, affix, args.Text - ); - args.Clear(); - } - - public static void SendMessage( - this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, - string lang, ReadOnlySpan name, - ref RawInterpolatedStringHandler text - ) - { - ns.SendMessage(serial, graphic, type, hue, font, ascii, lang, name, text.Text); - text.Clear(); - } -} diff --git a/Projects/Server/Network/Packets/OutgoingMessagePackets.cs b/Projects/Server/Network/Packets/OutgoingMessagePackets.cs index a531336b8..aba639176 100644 --- a/Projects/Server/Network/Packets/OutgoingMessagePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMessagePackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingMessagePackets.cs * * * @@ -29,12 +29,11 @@ public enum AffixType : byte System = 0x02 } -public static partial class OutgoingMessagePackets +public static class OutgoingMessagePackets { public static void SendMessageLocalized( this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, int number, - ReadOnlySpan name = default, ReadOnlySpan args = default + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = "" ) { if (ns.CannotSendPackets()) @@ -42,7 +41,7 @@ public static partial class OutgoingMessagePackets return; } - var buffer = stackalloc byte[GetMaxMessageLocalizedLength(args.Length)].InitializePacket(); + Span buffer = stackalloc byte[GetMaxMessageLocalizedLength(args)].InitializePacket(); var length = CreateMessageLocalized( buffer, serial, graphic, type, hue, font, number, name, args ); @@ -51,16 +50,11 @@ public static partial class OutgoingMessagePackets } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLocalizedLength(int argsCharCount) => 50 + argsCharCount * 2; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLocalizedLength(ReadOnlySpan args) => - GetMaxMessageLocalizedLength(args.Length); + public static int GetMaxMessageLocalizedLength(string args) => 50 + (args?.Length ?? 0) * 2; public static int CreateMessageLocalized( Span buffer, - Serial serial, int graphic, MessageType type, int hue, int font, int number, - ReadOnlySpan name = default, ReadOnlySpan args = default + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = "" ) { if (buffer[0] != 0) @@ -68,6 +62,9 @@ public static partial class OutgoingMessagePackets return buffer.Length; } + name ??= ""; + args ??= ""; + if (hue == 0) { hue = 0x3B2; @@ -82,7 +79,7 @@ public static partial class OutgoingMessagePackets writer.Write((short)hue); writer.Write((short)font); writer.Write(number); - writer.WriteLatin1(name, 30); + writer.WriteAscii(name, 30); writer.WriteLittleUniNull(args); writer.WritePacketLength(); @@ -91,9 +88,8 @@ public static partial class OutgoingMessagePackets public static void SendMessageLocalizedAffix( this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, int number, - ReadOnlySpan name, AffixType affixType, - ReadOnlySpan affix = default, ReadOnlySpan args = default + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, + AffixType affixType, string affix = "", string args = "" ) { if (ns.CannotSendPackets()) @@ -101,7 +97,7 @@ public static partial class OutgoingMessagePackets return; } - var buffer = stackalloc byte[GetMaxMessageLocalizedAffixLength(affix.Length, args.Length)].InitializePacket(); + Span buffer = stackalloc byte[GetMaxMessageLocalizedAffixLength(affix, args)].InitializePacket(); var length = CreateMessageLocalizedAffix( buffer, serial, graphic, type, hue, font, number, name, affixType, affix, args ); @@ -110,18 +106,13 @@ public static partial class OutgoingMessagePackets } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLocalizedAffixLength(int affixCharCount, int argsCharCount) => - 52 + affixCharCount + argsCharCount * 2; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLocalizedAffixLength(ReadOnlySpan affix, ReadOnlySpan args) => - GetMaxMessageLocalizedAffixLength(affix.Length, args.Length); + public static int GetMaxMessageLocalizedAffixLength(string affix, string args) => + 52 + (affix?.Length ?? 0) + (args?.Length ?? 0) * 2; public static int CreateMessageLocalizedAffix( Span buffer, - Serial serial, int graphic, MessageType type, int hue, int font, int number, - ReadOnlySpan name, AffixType affixType, - ReadOnlySpan affix = default, ReadOnlySpan args = default + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, + AffixType affixType, string affix = "", string args = "" ) { if (buffer[0] != 0) @@ -129,6 +120,10 @@ public static partial class OutgoingMessagePackets return buffer.Length; } + name ??= ""; + affix ??= ""; + args ??= ""; + if (hue == 0) { hue = 0x3B2; @@ -144,8 +139,8 @@ public static partial class OutgoingMessagePackets writer.Write((short)font); writer.Write(number); writer.Write((byte)affixType); - writer.WriteLatin1(name, 30); - writer.WriteLatin1Null(affix); + writer.WriteAscii(name, 30); + writer.WriteAsciiNull(affix); writer.WriteBigUniNull(args); writer.WritePacketLength(); @@ -154,8 +149,7 @@ public static partial class OutgoingMessagePackets public static void SendMessage( this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, - string lang, ReadOnlySpan name, ReadOnlySpan text + Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, string lang, string name, string text ) { if (ns.CannotSendPackets()) @@ -163,7 +157,7 @@ public static partial class OutgoingMessagePackets return; } - var buffer = stackalloc byte[GetMaxMessageLength(text.Length)].InitializePacket(); + Span buffer = stackalloc byte[GetMaxMessageLength(text)].InitializePacket(); var length = CreateMessage( buffer, serial, @@ -181,10 +175,7 @@ public static partial class OutgoingMessagePackets } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLength(int textCharCount) => 50 + textCharCount * 2; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLength(ReadOnlySpan text) => GetMaxMessageLength(text.Length); + public static int GetMaxMessageLength(string text) => 50 + (text?.Length ?? 0) * 2; public static int CreateMessage( Span buffer, @@ -195,8 +186,8 @@ public static partial class OutgoingMessagePackets int font, bool ascii, string lang, - ReadOnlySpan name, - ReadOnlySpan text + string name, + string text ) { if (buffer[0] != 0) @@ -204,6 +195,8 @@ public static partial class OutgoingMessagePackets return buffer.Length; } + name ??= ""; + text ??= ""; lang ??= "ENU"; if (hue == 0) @@ -221,13 +214,13 @@ public static partial class OutgoingMessagePackets writer.Write((short)font); if (ascii) { - writer.WriteLatin1(name, 30); - writer.WriteLatin1Null(text); + writer.WriteAscii(name, 30); + writer.WriteAsciiNull(text); } else { writer.WriteAscii(lang, 4); - writer.WriteLatin1(name, 30); + writer.WriteAscii(name, 30); writer.WriteBigUniNull(text); } diff --git a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs index 082016f12..2e41e656d 100644 --- a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingMobilePackets.cs * * * @@ -25,10 +25,8 @@ public static class OutgoingMobilePackets public const int BondedStatusPacketLength = 11; public const int DeathAnimationPacketLength = 13; public const int MobileMovingPacketLength = 17; - - // Mobile Moving Packet plus 2 bytes for regular/stygian flags - public const int MobileMovingPacketCacheByteLength = MobileMovingPacketLength + 2; - + public const int MobileMovingPacketCacheHeight = 7 * 2; // 7 notoriety, 2 client versions + public const int MobileMovingPacketCacheByteLength = MobileMovingPacketLength * MobileMovingPacketCacheHeight; public const int AttributeMaximum = 100; public const int MobileAttributePacketLength = 9; public const int MobileAttributesPacketLength = 17; @@ -96,31 +94,32 @@ public static class OutgoingMobilePackets return; } - var span = stackalloc byte[DeathAnimationPacketLength].InitializePacket(); + Span span = stackalloc byte[DeathAnimationPacketLength].InitializePacket(); CreateDeathAnimation(span, killed, corpse); ns.Send(span); } - public static void CreateMobileMoving(Span buffer, Mobile m, int noto, byte packetFlags) + public static void CreateMobileMoving(Span buffer, Mobile m, int noto, bool stygianAbyss) { - if (buffer[0] == 0) + if (buffer[0] != 0) { - var loc = m.Location; - var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue; - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x77); // Packet ID - writer.Write(m.Serial); - writer.Write((short)m.Body); - writer.Write((short)loc.m_X); - writer.Write((short)loc.m_Y); - writer.Write((sbyte)loc.m_Z); - writer.Write((byte)m.Direction); - writer.Write((short)hue); + return; } - buffer[15] = packetFlags; - buffer[16] = (byte)noto; + var loc = m.Location; + var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue; + + var writer = new SpanWriter(buffer); + writer.Write((byte)0x77); // Packet ID + writer.Write(m.Serial); + writer.Write((short)m.Body); + writer.Write((short)loc.m_X); + writer.Write((short)loc.m_Y); + writer.Write((sbyte)loc.m_Z); + writer.Write((byte)m.Direction); + writer.Write((short)hue); + writer.Write((byte)m.GetPacketFlags(stygianAbyss)); + writer.Write((byte)noto); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -134,9 +133,8 @@ public static class OutgoingMobilePackets return; } - var buffer = stackalloc byte[MobileMovingPacketLength].InitializePacket(); - var packetFlags = (byte)target.GetPacketFlags(ns.StygianAbyss); - CreateMobileMoving(buffer, target, noto, packetFlags); + Span buffer = stackalloc byte[MobileMovingPacketLength].InitializePacket(); + CreateMobileMoving(buffer, target, noto, ns.StygianAbyss); ns.Send(buffer); } @@ -144,6 +142,8 @@ public static class OutgoingMobilePackets public static void SendMobileMovingUsingCache(this NetState ns, Span cache, Mobile source, Mobile target) => ns.SendMobileMovingUsingCache(cache, target, Notoriety.Compute(source, target)); + // Requires a buffer of 14 packets, 17 bytes per packet (238 bytes). + // Requires cache to have the first byte of each packet initially zeroed. public static void SendMobileMovingUsingCache(this NetState ns, Span cache, Mobile target, int noto) { if (ns.CannotSendPackets()) @@ -151,15 +151,13 @@ public static class OutgoingMobilePackets return; } - // Cache the packet flags for regular/stygian if the packet hasn't been built yet - if (cache[0] == 0) - { - cache[17] = (byte)target.GetPacketFlags(false); - cache[18] = (byte)target.GetPacketFlags(true); - } + var stygianAbyss = ns.StygianAbyss; + // Indexes 0-6 for pre-SA, and 7-13 for SA + var row = noto + (stygianAbyss ? 6 : -1); + var buffer = cache.Slice(row * MobileMovingPacketLength, MobileMovingPacketLength); + CreateMobileMoving(buffer, target, noto, stygianAbyss); - CreateMobileMoving(cache, target, noto, ns.StygianAbyss ? cache[18] : cache[17]); - ns.Send(cache[..17]); + ns.Send(buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -202,7 +200,7 @@ public static class OutgoingMobilePackets return; } - var span = stackalloc byte[MobileAttributePacketLength].InitializePacket(); + Span span = stackalloc byte[MobileAttributePacketLength].InitializePacket(); CreateMobileHits(span, m, normalize); ns.Send(span); } @@ -227,7 +225,7 @@ public static class OutgoingMobilePackets return; } - var span = stackalloc byte[MobileAttributePacketLength].InitializePacket(); + Span span = stackalloc byte[MobileAttributePacketLength].InitializePacket(); CreateMobileMana(span, m, normalize); ns.Send(span); } @@ -252,7 +250,7 @@ public static class OutgoingMobilePackets return; } - var span = stackalloc byte[MobileAttributePacketLength].InitializePacket(); + Span span = stackalloc byte[MobileAttributePacketLength].InitializePacket(); CreateMobileStam(span, m, normalize); ns.Send(span); } @@ -277,7 +275,7 @@ public static class OutgoingMobilePackets return; } - var span = stackalloc byte[MobileAttributesPacketLength].InitializePacket(); + Span span = stackalloc byte[MobileAttributesPacketLength].InitializePacket(); CreateMobileAttributes(span, m, normalize); ns.Send(span); } @@ -309,7 +307,7 @@ public static class OutgoingMobilePackets writer.Write((byte)0x98); // Packet ID writer.Write((ushort)37); writer.Write(m.Serial); - writer.WriteLatin1(m.Name ?? "", 29); + writer.WriteAscii(m.Name ?? "", 29); writer.Write((byte)0); // Null terminator ns.Send(writer.Span); @@ -346,7 +344,7 @@ public static class OutgoingMobilePackets return; } - var span = stackalloc byte[MobileAnimationPacketLength].InitializePacket(); + Span span = stackalloc byte[MobileAnimationPacketLength].InitializePacket(); CreateMobileAnimation(span, mobile, action, frameCount, repeatCount, forward, repeat, delay); ns.Send(span); } @@ -371,7 +369,7 @@ public static class OutgoingMobilePackets return; } - var span = stackalloc byte[NewMobileAnimationPacketLength].InitializePacket(); + Span span = stackalloc byte[NewMobileAnimationPacketLength].InitializePacket(); CreateNewMobileAnimation(span, mobile, action, frameCount, delay); ns.Send(span); } @@ -383,7 +381,7 @@ public static class OutgoingMobilePackets return; } - var span = stackalloc byte[MobileHealthbarPacketLength].InitializePacket(); + Span span = stackalloc byte[MobileHealthbarPacketLength].InitializePacket(); CreateMobileHealthbar(span, m, healthbar); ns.Send(span); } @@ -438,7 +436,7 @@ public static class OutgoingMobilePackets return; } - var buffer = stackalloc byte[MobileStatusCompactLength].InitializePacket(); + Span buffer = stackalloc byte[MobileStatusCompactLength].InitializePacket(); CreateMobileStatusCompact(buffer, m, canBeRenamed); ns.Send(buffer); @@ -489,7 +487,7 @@ public static class OutgoingMobilePackets } } - var buffer = stackalloc byte[length].InitializePacket(); + Span buffer = stackalloc byte[length].InitializePacket(); CreateMobileStatus(buffer, beheld, version, beheld.CanBeRenamedBy(beholder)); ns.Send(buffer); } @@ -507,7 +505,7 @@ public static class OutgoingMobilePackets writer.Write((byte)0x11); // Packet ID writer.Seek(2, SeekOrigin.Current); writer.Write(beheld.Serial); - writer.WriteLatin1(name, 30); + writer.WriteAscii(name, 30); writer.WriteAttribute(beheld.HitsMax, beheld.Hits, version == 0, true); writer.Write(canBeRenamed); writer.Write((byte)version); @@ -674,7 +672,7 @@ public static class OutgoingMobilePackets itemID |= 0x8000; } - writer.Write(beheld.HairSerial); + writer.Write(beheld.Hair.VirtualSerial); writer.Write((ushort)itemID); writer.Write((byte)Layer.Hair); @@ -697,7 +695,7 @@ public static class OutgoingMobilePackets itemID |= 0x8000; } - writer.Write(beheld.FacialHairSerial); + writer.Write(beheld.FacialHair.VirtualSerial); writer.Write((ushort)itemID); writer.Write((byte)Layer.FacialHair); diff --git a/Projects/Server/Network/Packets/OutgoingMovementPackets.cs b/Projects/Server/Network/Packets/OutgoingMovementPackets.cs index f8140dc0f..bd953293f 100644 --- a/Projects/Server/Network/Packets/OutgoingMovementPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMovementPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingMovementPackets.cs * * * diff --git a/Projects/Server/Network/Packets/OutgoingPackets.cs b/Projects/Server/Network/Packets/OutgoingPackets.cs index ce2cce0f4..18e95f796 100644 --- a/Projects/Server/Network/Packets/OutgoingPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingPackets.cs @@ -7,7 +7,7 @@ public static class OutgoingPackets [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool CannotSendPackets(this NetState ns) => // Do not check for NetState.Running. Packets are sent to a "disconnected" socket as part of the OnDisconnect events - // up until the socket is closed. Closing the connection is done synchronously, therefore packets will not be sent + // up until the Connection is nulled. Closing the connection is done synchronously, therefore packets will not be sent // once the Mobile.NetState is null. - ns == null || ns.SocketHandle == 0 || ns.BlockAllPackets; + ns?.Connection == null || ns.BlockAllPackets; } diff --git a/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs b/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs index 3df2ec1e1..f12fc388f 100644 --- a/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingPlayerPackets.cs * * * @@ -56,10 +56,12 @@ public static class OutgoingPlayerPackets } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendChangeUpdateRange(this NetState ns, byte range) => ns?.Send(stackalloc byte[] { 0xC8, range }); + public static void SendChangeUpdateRange(this NetState ns, byte range) => + ns?.Send(stackalloc byte[] { 0xC8, range }); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendDeathStatus(this NetState ns) => ns?.Send(stackalloc byte[] { 0x2C, 2 }); + public static void SendDeathStatus(this NetState ns) => + ns?.Send(stackalloc byte[] { 0x2C, 2 }); public static void SendDisplayProfile(this NetState ns, Serial m, string header, string body, string footer) { @@ -77,7 +79,7 @@ public static class OutgoingPlayerPackets writer.Write((byte)0xB8); // Packet ID writer.Write((ushort)length); writer.Write(m); - writer.WriteLatin1Null(header); + writer.WriteAsciiNull(header); writer.WriteBigUniNull(footer); writer.WriteBigUniNull(body); @@ -175,13 +177,15 @@ public static class OutgoingPlayerPackets ns.Send(writer.Span); } - public static void SendLaunchBrowser(this NetState ns, ReadOnlySpan uri) + public static void SendLaunchBrowser(this NetState ns, string uri) { if (ns.CannotSendPackets()) { return; } + uri ??= ""; + var length = 4 + uri.Length; var writer = new SpanWriter(stackalloc byte[length]); writer.Write((byte)0xA5); // Packet ID @@ -231,7 +235,7 @@ public static class OutgoingPlayerPackets return; } - var buffer = stackalloc byte[DragEffectPacketLength].InitializePacket(); + Span buffer = stackalloc byte[DragEffectPacketLength].InitializePacket(); CreateDragEffect(buffer, srcSerial, srcLocation, trgSerial, trgLocation, itemID, hue, amount); ns.Send(buffer); } @@ -262,7 +266,7 @@ public static class OutgoingPlayerPackets var writer = new SpanWriter(stackalloc byte[66]); writer.Write((byte)0x88); // Packet ID writer.Write(m); - writer.WriteLatin1(title, 60); + writer.WriteAscii(title, 60); writer.Write(flags); ns.Send(writer.Span); @@ -300,7 +304,7 @@ public static class OutgoingPlayerPackets writer.Write((byte)type); writer.Write(tip); writer.Write((ushort)text.Length); - writer.WriteLatin1(text); + writer.WriteAscii(text); ns.Send(writer.Span); } diff --git a/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs b/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs index 1ee77d7d2..ca4d0e65b 100644 --- a/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingSecureTradePackets.cs * * * @@ -29,7 +29,9 @@ public enum TradeFlag : byte public static class OutgoingSecureTradePackets { - public static void SendDisplaySecureTrade(this NetState ns, Mobile them, Container first, Container second, string name) + public static void SendDisplaySecureTrade( + this NetState ns, Mobile them, Container first, Container second, string name + ) { if (ns.CannotSendPackets()) { @@ -45,7 +47,7 @@ public static class OutgoingSecureTradePackets writer.Write(second.Serial); writer.Write(true); - writer.WriteLatin1(name ?? "", 30); + writer.WriteAscii(name ?? "", 30); ns.Send(writer.Span); } diff --git a/Projects/Server/Network/Packets/OutgoingTargetPackets.cs b/Projects/Server/Network/Packets/OutgoingTargetPackets.cs index 78708b34e..40b106eb6 100644 --- a/Projects/Server/Network/Packets/OutgoingTargetPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingTargetPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingTargetPackets.cs * * * diff --git a/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs b/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs index e668537ff..b3b679a35 100644 --- a/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingVendorBuyPackets.cs * * * @@ -82,7 +82,7 @@ public static class OutgoingVendorBuyPackets } var length = 8; - for (var i = 0; i < list.Count; i++) + for (int i = 0; i < list.Count; i++) { length += 6 + list[i].Description?.Length ?? 0; } @@ -102,7 +102,7 @@ public static class OutgoingVendorBuyPackets var desc = bis.Description ?? ""; writer.Write((byte)(desc.Length + 1)); - writer.WriteLatin1Null(desc); + writer.WriteAsciiNull(desc); } ns.Send(writer.Span); diff --git a/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs b/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs index 4f8e70e04..83caf7ad6 100644 --- a/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingVendorSellPackets.cs * * * @@ -55,7 +55,7 @@ public static class OutgoingVendorSellPackets var name = (item.Name?.Trim()).DefaultIfNullOrEmpty(sis.Name ?? ""); writer.Write((ushort)name.Length); - writer.WriteLatin1(name); + writer.WriteAscii(name); } writer.WritePacketLength(); diff --git a/Projects/Server/Network/Packets/PacketContainerBuilder.cs b/Projects/Server/Network/Packets/PacketContainerBuilder.cs index c510ac1b0..ca7ca64f0 100644 --- a/Projects/Server/Network/Packets/PacketContainerBuilder.cs +++ b/Projects/Server/Network/Packets/PacketContainerBuilder.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: PacketContainerBuilder.cs * * * @@ -89,11 +89,11 @@ public ref struct PacketContainerBuilder private void Grow(int additionalCapacityBeyondPos) { var newLength = Math.Max(Length + additionalCapacityBeyondPos, _bytes.Length * 2); - var poolArray = STArrayPool.Shared.Rent(newLength); + byte[] poolArray = STArrayPool.Shared.Rent(newLength); _bytes[..Length].CopyTo(poolArray); - var toReturn = _arrayToReturnToPool; + byte[] toReturn = _arrayToReturnToPool; _bytes = _arrayToReturnToPool = poolArray; if (toReturn != null) { @@ -104,7 +104,7 @@ public ref struct PacketContainerBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - var toReturn = _arrayToReturnToPool; + byte[] toReturn = _arrayToReturnToPool; this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again if (toReturn != null) { diff --git a/Projects/Server/Network/PingServer.cs b/Projects/Server/Network/PingServer.cs index 5ba1a3ded..41a59468b 100644 --- a/Projects/Server/Network/PingServer.cs +++ b/Projects/Server/Network/PingServer.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: PingServer.cs * * * @@ -61,7 +61,7 @@ public static class PingServer if (ipep.Address.Equals(IPAddress.Any) || ipep.Address.Equals(IPAddress.IPv6Any)) { - listeningAddresses.UnionWith(NetState.GetListeningAddresses(ipep)); + listeningAddresses.UnionWith(TcpServer.GetListeningAddresses(ipep)); } else { diff --git a/Projects/Server/Network/Pipe.cs b/Projects/Server/Network/Pipe.cs new file mode 100644 index 000000000..a1c998ee0 --- /dev/null +++ b/Projects/Server/Network/Pipe.cs @@ -0,0 +1,519 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Pipe.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Server.Network; + +public partial class Pipe : IDisposable +{ + public class PipeWriter + { + private readonly Pipe _pipe; + + internal PipeWriter(Pipe pipe) => _pipe = pipe; + + public unsafe Span AvailableToWrite() + { + var read = _pipe._readIdx; + var write = _pipe._writeIdx; + + uint sz; + if (read <= write) + { + sz = _pipe.Size - write + read - 1; + } + else + { + sz = read - write - 1; + } + + return new Span((void*)(_pipe._buffer + write), (int)sz); + } + + public void Advance(uint count) + { + var read = _pipe._readIdx; + var write = _pipe._writeIdx; + + if (count == 0) + { + return; + } + + if (count > _pipe.Size - 1) + { + throw new EndOfPipeException("Unable to advance beyond the end of the pipe."); + } + + if (read <= write) + { + if (count > read + _pipe.Size - write - 1) + { + throw new EndOfPipeException("Unable to advance beyond the end of the pipe."); + } + + var sz = Math.Min(count, _pipe.Size - write); + + write += sz; + if (write > _pipe.Size - 1) + { + write = 0; + } + count -= sz; + + if (count > 0) + { + if (count >= read) + { + throw new EndOfPipeException("Unable to advance beyond the end of the pipe."); + } + + write = count; + } + } + else + { + if (count > read - write - 1) + { + throw new EndOfPipeException("Unable to advance beyond the end of the pipe."); + } + + write += count; + } + + // It's never valid to advance the write pointer to become equal to + // the read pointer. Check that here. + if (write == read) + { + throw new EndOfPipeException("Unable to advance beyond the end of the pipe."); + } + + _pipe._writeIdx = write; + } + + public void Close() => _pipe._closed = true; + + public bool IsClosed => _pipe._closed; + } + + public class PipeReader + { + private readonly Pipe _pipe; + + internal PipeReader(Pipe pipe) => _pipe = pipe; + + public unsafe Span AvailableToRead() + { + var read = _pipe._readIdx; + var write = _pipe._writeIdx; + + uint sz; + if (read <= write) + { + sz = write - read; + } + else + { + sz = _pipe.Size - read + write; + } + + return new Span((void*)(_pipe._buffer + read), (int)sz); + } + + public void Advance(uint count) + { + var read = _pipe._readIdx; + var write = _pipe._writeIdx; + + if (read <= write) + { + if (count > write - read) + { + throw new EndOfPipeException("Unable to advance beyond the end of the pipe."); + } + + read += count; + } + else + { + var sz = Math.Min(count, _pipe.Size - read); + + read += sz; + if (read > _pipe.Size - 1) + { + read = 0; + } + count -= sz; + + if (count > 0) + { + if (count > write) + { + throw new EndOfPipeException("Unable to advance beyond the end of the pipe."); + } + + read = count; + } + } + + if (read == write) + { + // If the read pointer catches up to the write pointer, then the pipe is empty. + // As a performance optimization, set both to 0. This should improve the chances cache lines are hit. + _pipe._readIdx = 0; + _pipe._writeIdx = 0; + } + else + { + _pipe._readIdx = read; + } + } + + public void Close() => _pipe._closed = true; + + public bool IsClosed => _pipe._closed; + } + + private IntPtr _handle; // Doubles as the file descriptor for linux/darwin + private IntPtr _buffer; + private readonly uint _bufferSize; + private uint _writeIdx; + private uint _readIdx; + private bool _closed; + + public PipeWriter Writer { get; } + public PipeReader Reader { get; } + + public uint Size => _bufferSize; + + public bool Closed => _closed; + + public Pipe(uint size) + { + var pageSize = (uint)Environment.SystemPageSize; + + // Virtual allocation requires multiples of system page size + // So let's adjust the requested size rounded to the next available page size + var adjustedSize = (size + pageSize - 1) & ~(pageSize - 1); + + if (Core.IsWindows) + { + // Reserve a region of virtual memory. We need twice the size so we can later mirror. + var region = NativeMethods_Windows.VirtualAlloc2( + IntPtr.Zero, + IntPtr.Zero, + adjustedSize * 2, + NativeMethods_Windows.MEM_RESERVE | NativeMethods_Windows.MEM_RESERVE_PLACEHOLDER, + NativeMethods_Windows.PAGE_NOACCESS, + IntPtr.Zero, + 0 + ); + + if (region == IntPtr.Zero) + { + throw new InvalidOperationException($"Allocating virtual memory failed. ({Marshal.GetLastPInvokeError()})"); + } + + // Releases half of the region so we can map the same memory region twice + var freed = NativeMethods_Windows.VirtualFree( + region, + adjustedSize, + NativeMethods_Windows.MEM_RELEASE | NativeMethods_Windows.MEM_PRESERVE_PLACEHOLDER + ); + + if (!freed) + { + throw new InvalidOperationException($"Creating virtual placeholder failed. ({Marshal.GetLastPInvokeError()})"); + } + + // Create a file descriptor + _handle = NativeMethods_Windows.CreateFileMappingW( + NativeMethods_Windows.InvalidHandleValue, + IntPtr.Zero, + NativeMethods_Windows.PAGE_READWRITE, + 0, + adjustedSize, + null + ); + + if (_handle == IntPtr.Zero) + { + throw new InvalidOperationException($"Creating file mapping failed. ({Marshal.GetLastPInvokeError()})"); + } + + // Map the region to the first half of the virtual space + _buffer = NativeMethods_Windows.MapViewOfFile3( + _handle, + IntPtr.Zero, + region, + 0, + adjustedSize, + NativeMethods_Windows.MEM_REPLACE_PLACEHOLDER, + NativeMethods_Windows.PAGE_READWRITE, + IntPtr.Zero, + 0 + ); + + if (_buffer == IntPtr.Zero) + { + throw new InvalidOperationException($"Mapping file view failed. ({Marshal.GetLastPInvokeError()})"); + } + + // Map the same region to the second half of the virtual space + var view2 = NativeMethods_Windows.MapViewOfFile3( + _handle, + IntPtr.Zero, + new IntPtr(_buffer + adjustedSize), + 0, + adjustedSize, + NativeMethods_Windows.MEM_REPLACE_PLACEHOLDER, + NativeMethods_Windows.PAGE_READWRITE, + IntPtr.Zero, + 0 + ); + + if (view2 == IntPtr.Zero) + { + throw new InvalidOperationException($"Mapping file view mirror failed. ({Marshal.GetLastPInvokeError()})"); + } + } + else if (Core.IsLinux || Core.IsDarwin) + { + var anon = Core.IsLinux ? NativeMethods_Linux.MAP_ANONYMOUS : NativeMethods_Linux.MAP_ANON; + + int fd; + + if (Core.IsLinux) + { + // Create a memory-backed file descriptor + fd = NativeMethods_Linux.memfd_create("mirrored_ring_buffer", 0); + } + else + { + var fdName = $"/muo/ring/{GetHashCode()}"; + fd = NativeMethods_Linux.shm_open(fdName, NativeMethods_Linux.O_CREAT | NativeMethods_Linux.O_RDWR, 0600); + + // Unlink immediately to emulate memfd_create() functionality + NativeMethods_Linux.shm_unlink(fdName); + } + + if (fd == NativeMethods_Linux.InvalidPtrValue) + { + throw new InvalidOperationException($"Creating file descriptor failed. ({Marshal.GetLastPInvokeError()})"); + } + + // Set the size of the file descriptor + if (NativeMethods_Linux.ftruncate(fd, (int)adjustedSize) != 0) + { + throw new InvalidOperationException($"Setting file descriptor size failed. ({Marshal.GetLastPInvokeError()})"); + } + + // Get virtual address space, must be double the size so we can map twice + _buffer = NativeMethods_Linux.mmap(IntPtr.Zero, adjustedSize * 2, + NativeMethods_Linux.PROT_READ | NativeMethods_Linux.PROT_WRITE, + NativeMethods_Linux.MAP_PRIVATE | anon, NativeMethods_Linux.InvalidFileDescriptor, 0); + + if (_buffer == NativeMethods_Linux.InvalidPtrValue) + { + throw new InsufficientMemoryException($"Allocating virtual memory failed. ({Marshal.GetLastPInvokeError()})"); + } + + // Map the file descriptor to the first half of the virtual space + var view1 = NativeMethods_Linux.mmap(_buffer, adjustedSize, + NativeMethods_Linux.PROT_READ | NativeMethods_Linux.PROT_WRITE, + NativeMethods_Linux.MAP_SHARED | NativeMethods_Linux.MAP_FIXED, fd, 0); + + if (view1 == NativeMethods_Linux.InvalidPtrValue) + { + throw new InvalidOperationException($"Mapping memory failed. ({Marshal.GetLastPInvokeError()})"); + } + + // Map the file descriptor to the second half of the virtual space + var view2 = NativeMethods_Linux.mmap(new IntPtr(_buffer + adjustedSize), adjustedSize, + NativeMethods_Linux.PROT_READ | NativeMethods_Linux.PROT_WRITE, + NativeMethods_Linux.MAP_SHARED | NativeMethods_Linux.MAP_FIXED, fd, 0); + + if (view2 == NativeMethods_Linux.InvalidPtrValue) + { + throw new InvalidOperationException($"Mapping mirrored memory failed. ({Marshal.GetLastPInvokeError()})"); + } + + _handle = fd; + } + + _bufferSize = adjustedSize; + _writeIdx = 0; + _readIdx = 0; + _closed = false; + + Writer = new PipeWriter(this); + Reader = new PipeReader(this); + } + + private static partial class NativeMethods_Windows + { + private const string Kernel32 = "kernel32.dll"; + private const string KernelBase = "kernelbase.dll"; + public const IntPtr InvalidHandleValue = -1; + + [LibraryImport(Kernel32, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + public static partial IntPtr CreateFileMappingW( + IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, + string lpName + ); + + [LibraryImport(KernelBase, SetLastError = true)] + public static partial IntPtr MapViewOfFile3( + IntPtr hFileMappingObject, IntPtr processHandle, IntPtr pvBaseAddress, ulong ullOffset, ulong ullSize, + uint allocFlags, uint dwDesiredAccess, + IntPtr hExtendedParameter, int parameterCount + ); + + [LibraryImport(Kernel32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool UnmapViewOfFile(IntPtr lpBaseAddress); + + [LibraryImport(Kernel32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool CloseHandle(IntPtr hObject); + + [LibraryImport(KernelBase, SetLastError = true)] + public static partial IntPtr VirtualAlloc2( + IntPtr process, + IntPtr address, + ulong size, + uint allocationType, + uint protect, + IntPtr extendedParameters, + uint parameterCount + ); + + [LibraryImport(Kernel32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType); + + public const uint MEM_PRESERVE_PLACEHOLDER = 0x02; + public const uint MEM_RESERVE = 0x2000; + public const uint MEM_REPLACE_PLACEHOLDER = 0x4000; + public const uint MEM_RELEASE = 0x8000; + public const uint MEM_RESERVE_PLACEHOLDER = 0x40000; + public const uint PAGE_NOACCESS = 0x01; + public const uint PAGE_READWRITE = 0x04; + } + + private static partial class NativeMethods_Linux + { + private const string LibC = "libc"; + public const IntPtr InvalidPtrValue = -1; + public const int InvalidFileDescriptor = -1; + + // For MacOS + [LibraryImport(LibC, SetLastError = true, StringMarshalling = StringMarshalling.Utf8)] + public static partial int shm_open(string name, int oflag, int mode); + + [LibraryImport(LibC, SetLastError = true, StringMarshalling = StringMarshalling.Utf8)] + public static partial int shm_unlink(string name); + + [LibraryImport(LibC, SetLastError = true, StringMarshalling = StringMarshalling.Utf8)] + public static partial int memfd_create(string name, uint flags); + + [LibraryImport(LibC, SetLastError = true)] + public static partial int ftruncate(int fd, int length); + + [LibraryImport(LibC, SetLastError = true)] + public static partial int close(int fd); + + [LibraryImport(LibC, SetLastError = true)] + public static partial IntPtr mmap(IntPtr addr, ulong length, int prot, int flags, int fd, int offset); + + [LibraryImport(LibC, SetLastError = true)] + public static partial int munmap(IntPtr addr, ulong length); + + public const int PROT_READ = 0x1; + public const int PROT_WRITE = 0x2; + public const int MAP_PRIVATE = 0x02; + public const int MAP_SHARED = 0x01; + public const int MAP_FIXED = 0x10; + public const int MAP_ANONYMOUS = 0x20; + + // Darwin + public const int O_RDWR = 0x2; + public const int O_CREAT = 0x200; + public const int MAP_ANON = 0x1000; + } + + private void ReleaseUnmanagedResources() + { + if (_buffer == IntPtr.Zero) + { + return; + } + + if (Core.IsWindows) + { + if (_handle != IntPtr.Zero) + { + NativeMethods_Windows.CloseHandle(_handle); + _handle = IntPtr.Zero; + } + + if (_buffer != IntPtr.Zero) + { + NativeMethods_Windows.UnmapViewOfFile(_buffer); + NativeMethods_Windows.UnmapViewOfFile(new IntPtr(_buffer + _bufferSize)); + } + } + else if (Core.IsLinux || Core.IsDarwin) + { + if (_handle != NativeMethods_Linux.InvalidFileDescriptor) + { +#pragma warning disable CA2020 + NativeMethods_Linux.close((int)_handle); +#pragma warning restore CA2020 + + _handle = NativeMethods_Linux.InvalidFileDescriptor; + } + + if (_buffer != IntPtr.Zero) + { + NativeMethods_Linux.munmap(_buffer, _bufferSize); + NativeMethods_Linux.munmap(new IntPtr(_buffer + _bufferSize), _bufferSize); + } + } + + _buffer = IntPtr.Zero; + } + + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + ~Pipe() + { + ReleaseUnmanagedResources(); + } +} + +public class EndOfPipeException : IOException +{ + public EndOfPipeException(string message) : base(message) + { + } +} diff --git a/Projects/Server/Network/ServerInfo.cs b/Projects/Server/Network/ServerInfo.cs index 59f4a29b4..2950047fc 100644 --- a/Projects/Server/Network/ServerInfo.cs +++ b/Projects/Server/Network/ServerInfo.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ServerInfo.cs * * * diff --git a/Projects/Server/Network/SocketHelper.cs b/Projects/Server/Network/SocketHelper.cs deleted file mode 100644 index 34d239e86..000000000 --- a/Projects/Server/Network/SocketHelper.cs +++ /dev/null @@ -1,218 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SocketHelper.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Net; -using System.Net.Sockets; -using System.Runtime.InteropServices; - -namespace Server.Network; - -/// -/// Platform-specific socket utilities for working with raw socket handles. -/// -public static partial class SocketHelper -{ - /// - /// Gets the remote IP address from a socket handle. - /// - /// The socket handle. - /// The remote IP address, or null if unable to retrieve. - public static IPAddress GetRemoteAddress(nint socket) - { - if (socket is 0 or -1) - { - return null; - } - - try - { - return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? GetRemoteAddressWindows(socket) - : GetRemoteAddressUnix(socket); - } - catch - { - return null; - } - } - - private static unsafe IPAddress GetRemoteAddressWindows(nint socket) - { - Span buffer = stackalloc byte[128]; - var len = buffer.Length; - - fixed (byte* ptr = buffer) - { - if (getpeername(socket, ptr, ref len) != 0) - { - return null; - } - } - - return ParseSockAddr(buffer[..len]); - } - - private static unsafe IPAddress GetRemoteAddressUnix(nint socket) - { - Span buffer = stackalloc byte[128]; - var len = (uint)buffer.Length; - - fixed (byte* ptr = buffer) - { - if (getpeername_unix(socket, ptr, ref len) != 0) - { - return null; - } - } - - return ParseSockAddr(buffer[..(int)len]); - } - - /// - /// Gets the local endpoint from a socket handle. - /// - /// The socket handle. - /// The local endpoint, or null if unable to retrieve. - public static IPEndPoint GetLocalEndPoint(nint socket) - { - if (socket is 0 or -1) - { - return null; - } - - try - { - return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? GetLocalEndPointWindows(socket) - : GetLocalEndPointUnix(socket); - } - catch - { - return null; - } - } - - private static unsafe IPEndPoint GetLocalEndPointWindows(nint socket) - { - Span buffer = stackalloc byte[128]; - var len = buffer.Length; - - fixed (byte* ptr = buffer) - { - if (getsockname(socket, ptr, ref len) != 0) - { - return null; - } - } - - return ParseSockAddrEndPoint(buffer[..len]); - } - - private static unsafe IPEndPoint GetLocalEndPointUnix(nint socket) - { - Span buffer = stackalloc byte[128]; - var len = (uint)buffer.Length; - - fixed (byte* ptr = buffer) - { - if (getsockname_unix(socket, ptr, ref len) != 0) - { - return null; - } - } - - return ParseSockAddrEndPoint(buffer[..(int)len]); - } - - private static IPAddress ParseSockAddr(ReadOnlySpan buffer) - { - if (buffer.Length < 2) - { - return null; - } - - // macOS/BSD: sockaddr has sin_len (1 byte) + sin_family (1 byte) - // Linux: sockaddr has sa_family (2 bytes) - var isBsd = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || - RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD); - var family = isBsd - ? (AddressFamily)buffer[1] // BSD: family is second byte - : (AddressFamily)BitConverter.ToInt16(buffer); // Linux: family is first 2 bytes - - if (family == AddressFamily.InterNetwork && buffer.Length >= 8) - { - // IPv4: family (2) + port (2) + addr (4) - return new IPAddress(buffer.Slice(4, 4)); - } - - if (family == AddressFamily.InterNetworkV6 && buffer.Length >= 28) - { - // IPv6: family (2) + port (2) + flowinfo (4) + addr (16) + scope (4) - return new IPAddress(buffer.Slice(8, 16)); - } - - return null; - } - - private static IPEndPoint ParseSockAddrEndPoint(ReadOnlySpan buffer) - { - if (buffer.Length < 4) - { - return null; - } - - // macOS/BSD: sockaddr has sin_len (1 byte) + sin_family (1 byte) - // Linux: sockaddr has sa_family (2 bytes) - var isBsd = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || - RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD); - var family = isBsd - ? (AddressFamily)buffer[1] // BSD: family is second byte - : (AddressFamily)BitConverter.ToInt16(buffer); // Linux: family is first 2 bytes - - // Port is in network byte order (big-endian), at offset 2 on both platforms - var port = (buffer[2] << 8) | buffer[3]; - - if (family == AddressFamily.InterNetwork && buffer.Length >= 8) - { - // IPv4: family (2) + port (2) + addr (4) - return new IPEndPoint(new IPAddress(buffer.Slice(4, 4)), port); - } - - if (family == AddressFamily.InterNetworkV6 && buffer.Length >= 28) - { - // IPv6: family (2) + port (2) + flowinfo (4) + addr (16) + scope (4) - return new IPEndPoint(new IPAddress(buffer.Slice(8, 16)), port); - } - - return null; - } - - // Windows getpeername - [LibraryImport("ws2_32.dll", SetLastError = true)] - private static unsafe partial int getpeername(nint s, byte* name, ref int namelen); - - // Unix/Linux getpeername - [LibraryImport("libc", EntryPoint = "getpeername", SetLastError = true)] - private static unsafe partial int getpeername_unix(nint sockfd, byte* addr, ref uint addrlen); - - // Windows getsockname - [LibraryImport("ws2_32.dll", SetLastError = true)] - private static unsafe partial int getsockname(nint s, byte* name, ref int namelen); - - // Unix/Linux getsockname - [LibraryImport("libc", EntryPoint = "getsockname", SetLastError = true)] - private static unsafe partial int getsockname_unix(nint sockfd, byte* addr, ref uint addrlen); -} diff --git a/Projects/Server/Network/TcpServer.cs b/Projects/Server/Network/TcpServer.cs new file mode 100644 index 000000000..d2c6751c8 --- /dev/null +++ b/Projects/Server/Network/TcpServer.cs @@ -0,0 +1,203 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2024 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: TcpServer.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using Server.Logging; +using Server.Misc; + +namespace Server.Network; + +public static class TcpServer +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(TcpServer)); + + // AccountLoginReject BadComm + private static readonly byte[] _socketRejected = [0x82, 0xFF]; + + public static IPEndPoint[] ListeningAddresses { get; private set; } + public static Socket[] Listeners { get; private set; } + + public static void Start() + { + HashSet listeningAddresses = []; + List listeners = []; + foreach (var ipep in ServerConfiguration.Listeners) + { + var listener = CreateListener(ipep); + if (listener == null) + { + continue; + } + + if (ipep.Address.Equals(IPAddress.Any) || ipep.Address.Equals(IPAddress.IPv6Any)) + { + listeningAddresses.UnionWith(GetListeningAddresses(ipep)); + } + else + { + listeningAddresses.Add(ipep); + } + + listeners.Add(listener); + BeginAcceptingSockets(listener); + } + + foreach (var ipep in listeningAddresses) + { + logger.Information("Listening: {Address}", ipep); + } + + ListeningAddresses = listeningAddresses.ToArray(); + Listeners = listeners.ToArray(); + } + + public static void Shutdown() + { + foreach (var listener in Listeners) + { + listener.Close(); + } + } + + public static IEnumerable GetListeningAddresses(IPEndPoint ipep) => + NetworkInterface.GetAllNetworkInterfaces().SelectMany(adapter => + adapter.GetIPProperties().UnicastAddresses + .Where(uip => ipep.AddressFamily == uip.Address.AddressFamily) + .Select(uip => new IPEndPoint(uip.Address, ipep.Port)) + ); + + public static Socket CreateListener(IPEndPoint ipep) + { + var listener = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp) + { + LingerState = new LingerOption(false, 0), + ExclusiveAddressUse = true, + NoDelay = true, + Blocking = false, + SendBufferSize = 64 * 1024, + ReceiveBufferSize = 64 * 1024 + }; + + try + { + listener.Bind(ipep); + listener.Listen(256); + return listener; + } + catch (SocketException se) + { + // WSAEADDRINUSE + if (se.ErrorCode == 10048) + { + logger.Warning("Listener: {Address} Exception: {Reason}", ipep, "Currently in use"); + } + // WSAEADDRNOTAVAIL + else if (se.ErrorCode == 10049) + { + logger.Warning("Listener {Address} Exception: {Reason}", ipep, "Unavailable"); + } + else + { + logger.Warning(se, "Listener {Address} Exception: {Reason}", ipep, se.Message); + } + } + + return null; + } + + private static async void BeginAcceptingSockets(Socket listener) + { + while (!Core.Closing) + { + Socket socket = null; + try + { + socket = await listener.AcceptAsync(); + var remoteIP = ((IPEndPoint)socket.RemoteEndPoint)!.Address; + + if (!IPLimiter.Verify(remoteIP)) + { + TraceDisconnect("Past IP limit threshold", remoteIP); + logger.Debug("{Address} Past IP limit threshold", remoteIP); + } + else if (Firewall.IsBlocked(remoteIP)) + { + TraceDisconnect("Firewalled", remoteIP); + logger.Debug("{Address} Firewalled", remoteIP); + } + else + { + var args = new SocketConnectEventArgs(socket); + EventSink.InvokeSocketConnect(args); + + if (args.AllowConnection) + { + _ = new NetState(socket); + continue; + } + + TraceDisconnect("Rejected by socket event handler", remoteIP); + + // Reject the connection + socket.Send(_socketRejected, SocketFlags.None); + } + + CloseSocket(socket); + } + catch + { + CloseSocket(socket); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CloseSocket(Socket socket) + { + try + { + socket.Shutdown(SocketShutdown.Both); + } + finally + { + socket.Close(); + } + } + + private static void TraceDisconnect(string reason, IPAddress ip) + { + try + { + using StreamWriter op = new StreamWriter("network-socket-disconnects.log", true); + op.WriteLine($"# {Core.Now}"); + + op.WriteLine($"Address: {ip}"); + op.WriteLine(reason); + + op.WriteLine(); + op.WriteLine(); + } + catch + { + // ignored + } + } +} diff --git a/Projects/Server/Poison.cs b/Projects/Server/Poison.cs index 532c563dd..64d804870 100644 --- a/Projects/Server/Poison.cs +++ b/Projects/Server/Poison.cs @@ -4,56 +4,75 @@ using System.Runtime.CompilerServices; namespace Server; -public enum PoisonFamily { Standard, Darkglow, Parasitic } - public abstract class Poison : ISpanParsable { - public static List Poisons { get; } = []; - public static Dictionary PoisonsByName { get; } = new(StringComparer.OrdinalIgnoreCase); - - public Poison(int index) => Index = index; - - public int Index { get; } + /*public abstract TimeSpan Interval{ get; } + public abstract TimeSpan Duration{ get; }*/ public abstract string Name { get; } public abstract int Level { get; } - public abstract PoisonFamily Family { get; } + + public static Poison Lesser => GetPoison("Lesser"); + public static Poison Regular => GetPoison("Regular"); + public static Poison Greater => GetPoison("Greater"); + public static Poison Deadly => GetPoison("Deadly"); + public static Poison Lethal => GetPoison("Lethal"); + + public static List Poisons { get; } = new(); public abstract Timer ConstructTimer(Mobile m); + /*public abstract void OnDamage( Mobile m, ref object state );*/ public override string ToString() => Name; public static void Register(Poison reg) { - var regName = reg.Name; + var regName = reg.Name.ToLower(); for (var i = 0; i < Poisons.Count; i++) { - var poison = Poisons[i]; - if (reg.Index == poison.Index) + if (reg.Level == Poisons[i].Level) { - throw new Exception("A poison with that index already exists."); + throw new Exception("A poison with that level already exists."); } - if (GetPoison(regName) != null) + if (regName == Poisons[i].Name.ToLower()) { throw new Exception("A poison with that name already exists."); } } Poisons.Add(reg); - PoisonsByName.Add(regName, reg); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Poison GetPoisonByIndex(int index) => index >= 0 && index < Poisons.Count ? Poisons[index] : null; + public static Poison GetPoison(int level) + { + for (var i = 0; i < Poisons.Count; ++i) + { + var p = Poisons[i]; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Poison IncreaseLevel(Poison oldPoison) => - oldPoison == null ? null : GetPoisonByIndex(oldPoison.Index + 1) ?? oldPoison; + if (p.Level == level) + { + return p; + } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Poison GetPoison(ReadOnlySpan name) => - PoisonsByName.GetAlternateLookup>().TryGetValue(name, out var poison) ? poison : null; + return null; + } + + public static Poison GetPoison(ReadOnlySpan name) + { + for (var i = 0; i < Poisons.Count; ++i) + { + var p = Poisons[i]; + + if (name.InsensitiveEquals(p.Name)) + { + return p; + } + } + + return null; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Poison Parse(string s) => Parse(s, null); @@ -67,9 +86,9 @@ public abstract class Poison : ISpanParsable public static Poison Parse(ReadOnlySpan s, IFormatProvider provider) { - if (int.TryParse(s, provider, out var index)) + if (int.TryParse(s, provider, out var pLevel)) { - var result = GetPoisonByIndex(index); + var result = GetPoison(pLevel); if (result != null) { return result; @@ -87,9 +106,9 @@ public abstract class Poison : ISpanParsable public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, out Poison result) { - if (int.TryParse(s, provider, out var index)) + if (int.TryParse(s, provider, out var pLevel)) { - result = GetPoisonByIndex(index); + result = GetPoison(pLevel); if (result != null) { return true; diff --git a/Projects/Server/PropertyList/IObjectPropertyListEntity.cs b/Projects/Server/PropertyList/IObjectPropertyListEntity.cs index c757a6ed7..549b7bf0a 100644 --- a/Projects/Server/PropertyList/IObjectPropertyListEntity.cs +++ b/Projects/Server/PropertyList/IObjectPropertyListEntity.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IObjectPropertyListEntity.cs * * * diff --git a/Projects/Server/PropertyList/IPropertyList.cs b/Projects/Server/PropertyList/IPropertyList.cs index d6b529823..b2d62ff2e 100644 --- a/Projects/Server/PropertyList/IPropertyList.cs +++ b/Projects/Server/PropertyList/IPropertyList.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IPropertyList.cs * * * @@ -13,7 +13,6 @@ * along with this program. If not, see . * *************************************************************************/ -using System; using System.Runtime.CompilerServices; using Server.Text; @@ -29,17 +28,8 @@ public interface IPropertyList : ISelfInterpolatedStringHandler /** Convenience method for $"{argument}". */ public void Add(int number, string argument); - /** Convenience method for span-based text without allocating a string. */ - public void Add(ReadOnlySpan argument); - - /** Convenience method for span-based text without allocating a string. */ - public void Add(int number, ReadOnlySpan argument); - - /** Emits newline-joined text across multiple properties so none exceeds the legacy client's per-property buffer. */ - public void AddChunked(ReadOnlySpan text); - - /** Builder for variable-length multi-line free text; flushes via AddChunked on dispose. */ - public OplTextBlock TextBlock(); + /** Convenience method for $"{text}". */ + public void Add(string text); /** Convenience method for $"{value}". */ public void Add(int number, int value); diff --git a/Projects/Server/PropertyList/ObjectPropertyList.cs b/Projects/Server/PropertyList/ObjectPropertyList.cs index 58b247990..5a8a94e97 100644 --- a/Projects/Server/PropertyList/ObjectPropertyList.cs +++ b/Projects/Server/PropertyList/ObjectPropertyList.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ObjectPropertyList.cs * * * @@ -20,7 +20,6 @@ using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using Server.Buffers; -using Server.Logging; using Server.Network; using Server.Text; @@ -38,14 +37,6 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable 1114779 // ~1_val~ }; - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ObjectPropertyList)); - - // Max characters for a SINGLE OPL property argument. The legacy 2D client copies each - // property's text into a fixed ~512-char (1024-byte) buffer; exceeding it corrupts the heap - // (smashes an adjacent world object's vtable -> client crash). 504 = multiple of 8, safely - // under the empirically confirmed ~510-char ceiling. For multi-line content use AddChunked(). - public const int MaxArgumentLength = 504; - private int _hash; private int _stringNumbersIndex; private byte[] _buffer; @@ -55,12 +46,6 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable private int _pos; private char[]? _arrayToReturnToPool; - /// - /// True while GetProperties is populating this list. Set by the owning entity so a nested - /// InvalidateProperties can be refused instead of Reset()ing a build already in flight. - /// - internal bool IsBuilding { get; set; } - public ObjectPropertyList(IEntity? e) { Entity = e; @@ -114,7 +99,7 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable public void Terminate() { - var length = _bufferPos + 4; + int length = _bufferPos + 4; if (length != _buffer.Length) { Resize(length); @@ -150,7 +135,7 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable AddHash(number); - var length = _bufferPos + 6; + int length = _bufferPos + 6; while (length > _buffer.Length) { Flush(); @@ -163,106 +148,11 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable } public void Add(int number, string? arguments) => InternalAdd(number, $"{arguments}"); + public void Add(string argument) => InternalAdd(GetStringNumber(), $"{argument}"); public void Add(int number, int value) => InternalAdd(number, $"{value}"); public void AddLocalized(int value) => InternalAdd(GetStringNumber(), $"{value:#}"); public void AddLocalized(int number, int value) => InternalAdd(number, $"{value:#}"); - public void Add(ReadOnlySpan argument) => InternalAdd(GetStringNumber(), argument); - public void Add(int number, ReadOnlySpan argument) => InternalAdd(number, argument); - public OplTextBlock TextBlock() => new(this); - - // Emits newline-joined text across as many OPL properties as needed, breaking ONLY at '\n', - // so no single property exceeds MaxArgumentLength characters. Each chunk goes through the - // passthrough-cliloc rotation. Use for variable-length multi-line content instead of one - // Add(joined) call, which would overflow the legacy 2D-client per-property tooltip buffer. - public void AddChunked(ReadOnlySpan text) - { - if (text.IsEmpty) - { - return; - } - - var chunkStart = 0; - var searchFrom = 0; - - while (true) - { - var nl = text[searchFrom..].IndexOf('\n'); - var lineEnd = nl < 0 ? text.Length : searchFrom + nl; - - // If appending this line would push the current chunk past the cap, flush the chunk - // up to the end of the previous line (excluding its '\n') first. - if (lineEnd - chunkStart > MaxArgumentLength && searchFrom > chunkStart) - { - Add(text[chunkStart..(searchFrom - 1)]); - chunkStart = searchFrom; - continue; - } - - if (nl < 0) - { - Add(text[chunkStart..]); // remainder (a single over-cap line is clamped by Add) - return; - } - - searchFrom = lineEnd + 1; - } - } - - // Hard backstop: never let a single property exceed the legacy client's per-property buffer. - // Callers with multi-line content should use AddChunked(); this truncates anything that slips - // through and surfaces the offending entity/cliloc. - private ReadOnlySpan ClampArgument(int number, ReadOnlySpan chars) - { - if (chars.Length <= MaxArgumentLength) - { - return chars; - } - - logger.Warning( - "OPL property on {Entity} (cliloc {Cliloc}) is {Length} chars; truncating to {Max} to avoid legacy 2D-client tooltip-buffer overflow. Use AddChunked for multi-line text.", - Entity, - number, - chars.Length, - MaxArgumentLength - ); - - return chars[..MaxArgumentLength]; - } - - private void InternalAdd(int number, ReadOnlySpan chars) - { - if (number == 0) - { - return; - } - - chars = ClampArgument(number, chars); - - if (Header == 0) - { - Header = number; - HeaderArgs = chars.ToString(); - } - - AddHash(number); - AddHash(string.GetHashCode(chars, StringComparison.Ordinal)); - - var strLength = chars.Length * 2; - var length = _bufferPos + 6 + strLength; - while (length > _buffer.Length) - { - Flush(); - } - - var writer = new SpanWriter(_buffer.AsSpan(_bufferPos)); - writer.Write(number); - writer.Write((ushort)strLength); - writer.Write(chars, TextEncoding.UnicodeLE); - - _bufferPos += writer.BytesWritten; - } - private int GetStringNumber() => _stringNumbers[_stringNumbersIndex++ % _stringNumbers.Length]; // String Interpolation @@ -287,7 +177,7 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable return; } - var chars = ClampArgument(number, _arrayToReturnToPool.AsSpan(0, _pos)); + var chars = _arrayToReturnToPool.AsSpan(0, _pos); if (Header == 0) { @@ -298,8 +188,8 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable AddHash(number); AddHash(string.GetHashCode(chars, StringComparison.Ordinal)); - var strLength = chars.Length * 2; - var length = _bufferPos + 6 + strLength; + int strLength = chars.Length * 2; + int length = _bufferPos + 6 + strLength; while (length > _buffer.Length) { Flush(); @@ -325,27 +215,12 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable private static int GetDefaultLength(int literalLength, int formattedCount) => Math.Max(256, literalLength + formattedCount * 11); - // Reset()/Dispose() return the scratch buffer to the pool. If either lands while a `$"..."` - // handler is still appending, re-rent rather than spanning a null array and throwing out of - // GetProperties. Mobile/Item hold the primary guard; this covers any other caller. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void EnsureInterpolationBuffer() - { - if (_arrayToReturnToPool == null) - { - _arrayToReturnToPool = STArrayPool.Shared.Rent(256); - _pos = 0; - } - } - public void AppendLiteral(string value) { - EnsureInterpolationBuffer(); - if (value.Length == 1) { - var chars = _arrayToReturnToPool.AsSpan(); - var pos = _pos; + Span chars = _arrayToReturnToPool.AsSpan(); + int pos = _pos; if ((uint)pos < (uint)chars.Length) { chars[pos] = value[0]; @@ -375,8 +250,6 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable public void AppendFormatted(T value) { - EnsureInterpolationBuffer(); - string? s; if (value is IFormattable) { @@ -407,11 +280,9 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable public void AppendFormatted(T value, string? format) { - EnsureInterpolationBuffer(); - - // '#' marks an integer argument as a cliloc ("#"). Integers only -- a float/double/decimal - // '#' is the standard numeric format, not a cliloc marker. - if (format == "#" && value is int or uint or long or ulong or short or ushort or byte or sbyte) + // We support localization '#' cliloc formatter for custom property lists + // This allows someone to build an IPropertyList that creates HTML using the same syntax as LocalizationInterpolationHandler + if (format == "#") { AppendLiteral("#"); format = null; @@ -447,7 +318,7 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable public void AppendFormatted(T value, int alignment) { - var startingPos = _pos; + int startingPos = _pos; AppendFormatted(value); if (alignment != 0) { @@ -457,7 +328,7 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable public void AppendFormatted(T value, int alignment, string? format) { - var startingPos = _pos; + int startingPos = _pos; AppendFormatted(value, format); if (alignment != 0) { @@ -467,8 +338,6 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable public void AppendFormatted(ReadOnlySpan value) { - EnsureInterpolationBuffer(); - if (value.TryCopyTo(_arrayToReturnToPool.AsSpan(_pos..))) { _pos += value.Length; @@ -481,16 +350,14 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) { - EnsureInterpolationBuffer(); - - var leftAlign = false; + bool leftAlign = false; if (alignment < 0) { leftAlign = true; alignment = -alignment; } - var paddingRequired = alignment - value.Length; + int paddingRequired = alignment - value.Length; if (paddingRequired <= 0) { AppendFormatted(value); @@ -517,8 +384,6 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable public void AppendFormatted(string? value) { - EnsureInterpolationBuffer(); - if (value?.TryCopyTo(_arrayToReturnToPool.AsSpan(_pos..)) == true) { _pos += value.Length; @@ -551,16 +416,16 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable Debug.Assert(startingPos >= 0 && startingPos <= _pos); Debug.Assert(alignment != 0); - var charsWritten = _pos - startingPos; + int charsWritten = _pos - startingPos; - var leftAlign = false; + bool leftAlign = false; if (alignment < 0) { leftAlign = true; alignment = -alignment; } - var paddingNeeded = alignment - charsWritten; + int paddingNeeded = alignment - charsWritten; if (paddingNeeded > 0) { EnsureCapacityForAdditionalChars(paddingNeeded); @@ -621,13 +486,13 @@ public sealed class ObjectPropertyList : IPropertyList, IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining)] private void GrowCore(uint requiredMinCapacity) { - var newCapacity = Math.Max(requiredMinCapacity, Math.Min((uint)_arrayToReturnToPool.Length * 2, 0x3FFFFFDF)); - var arraySize = (int)Math.Clamp(newCapacity, 256, int.MaxValue); + uint newCapacity = Math.Max(requiredMinCapacity, Math.Min((uint)_arrayToReturnToPool.Length * 2, 0x3FFFFFDF)); + int arraySize = (int)Math.Clamp(newCapacity, 256, int.MaxValue); - var newArray = STArrayPool.Shared.Rent(arraySize); + char[] newArray = STArrayPool.Shared.Rent(arraySize); _arrayToReturnToPool.AsSpan(.._pos).CopyTo(newArray); - var toReturn = _arrayToReturnToPool; + char[] toReturn = _arrayToReturnToPool; _arrayToReturnToPool = newArray; STArrayPool.Shared.Return(toReturn); diff --git a/Projects/Server/PropertyList/OplTextBlock.cs b/Projects/Server/PropertyList/OplTextBlock.cs deleted file mode 100644 index 296094a21..000000000 --- a/Projects/Server/PropertyList/OplTextBlock.cs +++ /dev/null @@ -1,113 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: OplTextBlock.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using Server.Text; - -namespace Server; - -// Accumulates '\n'-joined free-text lines and emits them on dispose via AddChunked, which splits -// into as many cycling passthrough entries as needed so no single OPL property exceeds the legacy -// client's per-property buffer (ObjectPropertyList.MaxArgumentLength). -// Use with `using var block = list.TextBlock();`. ref struct: single-threaded OPL build only. -public ref struct OplTextBlock -{ - private readonly IPropertyList _list; - internal ValueStringBuilder _builder; - private bool _any; - - internal OplTextBlock(IPropertyList list) - { - _list = list; - _builder = ValueStringBuilder.Create(); - _any = false; - } - - public void Add(scoped ReadOnlySpan line) - { - if (line.IsEmpty) - { - return; - } - - _builder.Append(line); - _builder.Append('\n', 1); // ValueStringBuilder has no single-char Append - _any = true; - } - - // Zero-alloc interpolated overload: block.Add($"Luck Bonus: +{v}%"). - public void Add([InterpolatedStringHandlerArgument("")] scoped ref OplInterpolationHandler handler) - { - var wrote = handler._wrote; - this = handler._block; // reconcile possibly-grown builder - if (wrote) - { - _builder.Append('\n', 1); - _any = true; - } - } - - public void Dispose() - { - if (_any) - { - // Strip the trailing '\n' (Length >= 2 whenever _any: content + separator). - // AddChunked splits across multiple properties so a long block never overflows the - // legacy client's per-property tooltip buffer. - _list.AddChunked(_builder.AsSpan(0, _builder.Length - 1)); - } - - _builder.Dispose(); - } - - [InterpolatedStringHandler] - public ref struct OplInterpolationHandler - { - internal OplTextBlock _block; - internal bool _wrote; - - public OplInterpolationHandler(int literalLength, int formattedCount, OplTextBlock block) - { - _block = block; - _wrote = false; - _block._builder.EnsureCapacity(_block._builder.Length + literalLength + formattedCount * 11); - } - - public void AppendLiteral(string value) - { - _block._builder.Append(value); - _wrote = true; - } - - public void AppendFormatted(T value) - { - _block._builder.Append(value); - _wrote = true; - } - - public void AppendFormatted(T value, string format) - { - _block._builder.Append(value, format); - _wrote = true; - } - - public void AppendFormatted(scoped ReadOnlySpan value) - { - _block._builder.Append(value); - _wrote = true; - } - } -} diff --git a/Projects/Server/Random/BuiltInRng.cs b/Projects/Server/Random/BuiltInRng.cs index 4ab3fb943..d3927242d 100644 --- a/Projects/Server/Random/BuiltInRng.cs +++ b/Projects/Server/Random/BuiltInRng.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BuiltInRng.cs * * * @@ -20,7 +20,7 @@ namespace Server.Random; public static class BuiltInRng { - public static System.Random Generator { get; internal set; } = new(); + public static System.Random Generator { get; private set; } = new(); public static void Reset() => Generator = new System.Random(); diff --git a/Projects/UOContent/World Saves/ArchiveEnums.cs b/Projects/Server/Random/BuiltInSecureRng.cs similarity index 64% rename from Projects/UOContent/World Saves/ArchiveEnums.cs rename to Projects/Server/Random/BuiltInSecureRng.cs index fe412d4b9..dec408338 100644 --- a/Projects/UOContent/World Saves/ArchiveEnums.cs +++ b/Projects/Server/Random/BuiltInSecureRng.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: ArchiveEnums.cs * + * File: SecureRandom.cs * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -13,11 +13,16 @@ * along with this program. If not, see . * *************************************************************************/ -namespace Server.Saves; +using System; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; -public enum ArchivePeriod +namespace Server; + +public static class BuiltInSecureRng { - Hourly, - Daily, - Monthly + public static RandomNumberGenerator Generator { get; } = RandomNumberGenerator.Create(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NextBytes(Span buffer) => Generator.GetBytes(buffer); } diff --git a/Projects/Server/Random/StableRandom.cs b/Projects/Server/Random/StableRandom.cs index 64d5c46af..c79cbfb58 100644 --- a/Projects/Server/Random/StableRandom.cs +++ b/Projects/Server/Random/StableRandom.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: StableRandom.cs * * * @@ -38,11 +38,11 @@ public static class StableRandom [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong NextUInt64(Span state, ulong maxValue) { - var randomProduct = Math.BigMul(maxValue, NextUInt64(ref state), out var lowPart); + ulong randomProduct = Math.BigMul(maxValue, NextUInt64(ref state), out ulong lowPart); if (lowPart < maxValue) { - var remainder = (0ul - maxValue) % maxValue; + ulong remainder = (0ul - maxValue) % maxValue; while (lowPart < remainder) { @@ -56,8 +56,8 @@ public static class StableRandom [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong NextUInt64(ref Span state) { - var result = BitOperations.RotateLeft(state[1] * 5, 7) * 9; - var t = state[1] << 17; + ulong result = BitOperations.RotateLeft(state[1] * 5, 7) * 9; + ulong t = state[1] << 17; state[2] ^= state[0]; state[3] ^= state[1]; diff --git a/Projects/Server/Random/WeightedValue.cs b/Projects/Server/Random/WeightedValue.cs index c205c76ae..a49217f26 100644 --- a/Projects/Server/Random/WeightedValue.cs +++ b/Projects/Server/Random/WeightedValue.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: WeightedValue.cs * * * diff --git a/Projects/Server/Regions/Region.cs b/Projects/Server/Regions/Region.cs index 1c71756a4..0045a1ffa 100644 --- a/Projects/Server/Regions/Region.cs +++ b/Projects/Server/Regions/Region.cs @@ -127,7 +127,7 @@ public class Region : IComparable, IValueLinkListNode public const int MinZ = sbyte.MinValue; public const int MaxZ = sbyte.MaxValue + 1; - public Region(string name, Map map, int priority, params ReadOnlySpan area) : this( + public Region(string name, Map map, int priority, params Rectangle2D[] area) : this( name, map, priority, @@ -147,7 +147,7 @@ public class Region : IComparable, IValueLinkListNode public Region(string name, Map map, Region parent, int priority, params Rectangle3D[] area) : this(name, map, parent, area) => Priority = priority; - public Region(string name, Map map, Region parent, params ReadOnlySpan area) : this( + public Region(string name, Map map, Region parent, params Rectangle2D[] area) : this( name, map, parent, @@ -314,7 +314,7 @@ public class Region : IComparable, IValueLinkListNode public static Rectangle3D ConvertTo3D(Rectangle2D rect) => new(new Point3D(rect.Start, MinZ), new Point3D(rect.End, MaxZ)); - public static Rectangle3D[] ConvertTo3D(ReadOnlySpan rects) + public static Rectangle3D[] ConvertTo3D(Rectangle2D[] rects) { var ret = new Rectangle3D[rects.Length]; @@ -547,29 +547,10 @@ public class Region : IComparable, IValueLinkListNode public virtual bool AcceptsSpawnsFrom(Region region) => AllowSpawn() && (region == this || Parent?.AcceptsSpawnsFrom(region) == true); - public PooledRefList GetPlayersPooled() - { - var list = PooledRefList.Create(); - for (var i = 0; i < Sectors?.Length; i++) - { - var sector = Sectors[i]; - - foreach (var ns in sector.Clients) - { - var player = ns.Mobile; - if (player?.Deleted == false && player.Region.IsPartOf(this)) - { - list.Add(ns.Mobile); - } - } - } - - return list; - } - public List GetPlayers() { - List list = []; + var list = new List(); + for (var i = 0; i < Sectors?.Length; i++) { var sector = Sectors[i]; @@ -628,25 +609,6 @@ public class Region : IComparable, IValueLinkListNode return list; } - public PooledRefList GetMobilesPooled() - { - var list = PooledRefList.Create(); - for (var i = 0; i < Sectors?.Length; i++) - { - var sector = Sectors[i]; - - foreach (var mobile in sector.Mobiles) - { - if (mobile.Region.IsPartOf(this)) - { - list.Add(mobile); - } - } - } - - return list; - } - public int GetMobileCount() { var count = 0; @@ -687,26 +649,6 @@ public class Region : IComparable, IValueLinkListNode return list; } - public PooledRefList GetItemsPooled() - { - var list = PooledRefList.Create(); - - for (var i = 0; i < Sectors?.Length; i++) - { - var sector = Sectors[i]; - - foreach (var item in sector.Items) - { - if (Find(item.Location, item.Map).IsPartOf(this)) - { - list.Add(item); - } - } - } - - return list; - } - public int GetItemCount() { var count = 0; diff --git a/Projects/Server/Regions/RegionJsonSerializer.cs b/Projects/Server/Regions/RegionJsonSerializer.cs index 3e406a567..ab1312809 100644 --- a/Projects/Server/Regions/RegionJsonSerializer.cs +++ b/Projects/Server/Regions/RegionJsonSerializer.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: RegionJsonSerializer.cs * * * diff --git a/Projects/Server/SecureTrade.cs b/Projects/Server/SecureTrade.cs index b01653047..dd7f7e30a 100644 --- a/Projects/Server/SecureTrade.cs +++ b/Projects/Server/SecureTrade.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SecureTrade.cs * * * diff --git a/Projects/Server/Serial.cs b/Projects/Server/Serial.cs index ba966d8d8..83f537f16 100644 --- a/Projects/Server/Serial.cs +++ b/Projects/Server/Serial.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Serial.cs * * * diff --git a/Projects/Server/Serialization/AdhocPersistence.cs b/Projects/Server/Serialization/AdhocPersistence.cs index d6c132549..b60e2482a 100644 --- a/Projects/Server/Serialization/AdhocPersistence.cs +++ b/Projects/Server/Serialization/AdhocPersistence.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: AdhocPersistence.cs * * * @@ -14,6 +14,7 @@ *************************************************************************/ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.IO.MemoryMappedFiles; @@ -26,9 +27,9 @@ public static class AdhocPersistence /** * Serializes to memory. */ - public static IGenericWriter SerializeToBuffer(Action serializer) + public static IGenericWriter SerializeToBuffer(Action serializer, ConcurrentQueue types = null) { - var saveBuffer = new BufferWriter(true); + var saveBuffer = new BufferWriter(true, types); serializer(saveBuffer); return saveBuffer; } @@ -54,11 +55,22 @@ public static class AdhocPersistence { var fullPath = PathUtility.GetFullPath(filePath, Core.BaseDirectory); PathUtility.EnsureDirectory(Path.GetDirectoryName(fullPath)); - - var writer = new FileBufferWriter(fullPath, sizeHint); + HashSet typesSet = []; + var writer = new MemoryMapFileWriter(new FileStream(filePath, FileMode.Create), sizeHint, typesSet); serializer(writer); - Task.Run(() => writer.Dispose(), Core.ClosingTokenSource.Token); + Task.Run( + () => + { + var fs = writer.FileStream; + + writer.Dispose(); + fs.Dispose(); + + Persistence.WriteSerializedTypesSnapshot(Path.GetDirectoryName(fullPath), typesSet); + }, + Core.ClosingTokenSource.Token + ); } public static unsafe void Deserialize(string filePath, Action deserializer) @@ -82,7 +94,7 @@ public static class AdhocPersistence byte* ptr = null; accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); - var dataReader = new UnmanagedDataReader(ptr, accessor.Length); + UnmanagedDataReader dataReader = new UnmanagedDataReader(ptr, accessor.Length); deserializer(dataReader); error = dataReader.Position != fileLength diff --git a/Projects/Server/Serialization/Attributes/ManualDirtyCheckingAttribute.cs b/Projects/Server/Serialization/Attributes/ManualDirtyCheckingAttribute.cs index 27271167a..bc344b698 100644 --- a/Projects/Server/Serialization/Attributes/ManualDirtyCheckingAttribute.cs +++ b/Projects/Server/Serialization/Attributes/ManualDirtyCheckingAttribute.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ManualDirtyCheckingAttribute.cs * * * diff --git a/Projects/Server/Serialization/BinaryFileReader.cs b/Projects/Server/Serialization/BinaryFileReader.cs index f8b51c182..f540f8e2e 100644 --- a/Projects/Server/Serialization/BinaryFileReader.cs +++ b/Projects/Server/Serialization/BinaryFileReader.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BinaryFileReader.cs * * * @@ -74,12 +74,6 @@ public sealed unsafe class BinaryFileReader : IDisposable, IGenericReader /// public long Position => _reader.Position; - public TimeSpan AnchoredTimeShift - { - get => _reader.AnchoredTimeShift; - set => _reader.AnchoredTimeShift = value; - } - public void Dispose() { _accessor?.SafeMemoryMappedViewHandle.ReleasePointer(); @@ -173,7 +167,7 @@ public sealed unsafe class BinaryFileReader : IDisposable, IGenericReader public Serial ReadSerial() => _reader.ReadSerial(); /// - /// Reads the next Byte which helps determine how to read the following Type. + /// Reads the next Byte which helps determin how to read the following Type. ///
If the byte returns 1 => and translate into a Type via the
///
If the byte returns 2 =>
///
else return null
diff --git a/Projects/Server/Serialization/BufferReader.cs b/Projects/Server/Serialization/BufferReader.cs index bd37f4ff6..6e55a643a 100644 --- a/Projects/Server/Serialization/BufferReader.cs +++ b/Projects/Server/Serialization/BufferReader.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BufferReader.cs * * * @@ -37,8 +37,6 @@ public class BufferReader : IGenericReader public long Position => _position; public long BufferSize => _buffer.Length; - public TimeSpan AnchoredTimeShift { get; set; } - public BufferReader(byte[] buffer, Dictionary typesDb = null, Encoding encoding = null) { _buffer = buffer; diff --git a/Projects/Server/Serialization/BufferWriter.cs b/Projects/Server/Serialization/BufferWriter.cs index ab53e2f2f..d3be1f050 100644 --- a/Projects/Server/Serialization/BufferWriter.cs +++ b/Projects/Server/Serialization/BufferWriter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BufferWriter.cs * * * @@ -14,12 +14,10 @@ *************************************************************************/ using System; -using System.Buffers; using System.Buffers.Binary; -using System.Collections; +using System.Collections.Concurrent; using System.Diagnostics; using System.IO; -using System.Net; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -29,9 +27,9 @@ namespace Server; public class BufferWriter : IGenericWriter { + private readonly ConcurrentQueue _types; private readonly Encoding _encoding; private readonly bool _prefixStrings; - private long _bytesWritten; private long _index; @@ -58,25 +56,27 @@ public class BufferWriter : IGenericWriter private byte[] _buffer; - public BufferWriter(byte[] buffer, bool prefixStr) + public BufferWriter(byte[] buffer, bool prefixStr, ConcurrentQueue types = null) { _prefixStrings = prefixStr; _encoding = TextEncoding.UTF8; _buffer = buffer; + _types = types; } - public BufferWriter(bool prefixStr) : this(0, prefixStr) + public BufferWriter(bool prefixStr, ConcurrentQueue types = null) : this(0, prefixStr, types) { } - public BufferWriter(int count, bool prefixStr) + public BufferWriter(int count, bool prefixStr, ConcurrentQueue types = null) { _prefixStrings = prefixStr; _encoding = TextEncoding.UTF8; _buffer = GC.AllocateUninitializedArray(count < 1 ? BufferSize : count); + _types = types; } - public virtual long Position => _index; + public virtual long Position => Index; protected virtual int BufferSize => 256; @@ -89,8 +89,6 @@ public class BufferWriter : IGenericWriter [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Resize(int size) { - _bytesWritten = Math.Max(_bytesWritten, _index); - // We shouldn't ever resize to a 0 length buffer. That is dangerous if (size <= 0) { @@ -109,23 +107,13 @@ public class BufferWriter : IGenericWriter public virtual void Flush() => Resize(Math.Clamp(_buffer.Length * 2, BufferSize, _buffer.Length + 1024 * 1024 * 64)); - /// - /// Ensures capacity, returns a ref at the current position, and advances the index. - /// The capacity check proves the caller's unaligned store is in-bounds, and the index - /// only moves forward between Seek calls, so no per-write validation is needed. Growth - /// (Flush -> Resize) always adds at least BufferSize, covering any primitive width. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ref byte Reserve(int bytes) + private void FlushIfNeeded(int amount) { - if ((uint)(_index + bytes) > (uint)_buffer.Length) + if (Index + amount > _buffer.Length) { Flush(); } - - ref var result = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_buffer), (nint)_index); - _index += bytes; - return ref result; } public virtual void Write(byte[] bytes) => Write(bytes.AsSpan()); @@ -142,7 +130,7 @@ public class BufferWriter : IGenericWriter } bytes.CopyTo(_buffer.AsSpan((int)_index)); - _index += length; + Index += length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -157,15 +145,13 @@ public class BufferWriter : IGenericWriter "Attempting to seek to an invalid position using SeekOrigin.Begin" ); Debug.Assert( - origin != SeekOrigin.Current || _index + offset >= 0 && _index + offset < _buffer.Length, + origin != SeekOrigin.Current || Index + offset >= 0 && Index + offset < _buffer.Length, "Attempting to seek to an invalid position using SeekOrigin.Current" ); - _bytesWritten = Math.Max(_bytesWritten, _index); - return Index = Math.Max(0, origin switch { - SeekOrigin.Current => _index + offset, + SeekOrigin.Current => Index + offset, SeekOrigin.End => _bytesWritten + offset, _ => offset // Begin }); @@ -183,111 +169,107 @@ public class BufferWriter : IGenericWriter else { Write(true); - WriteRaw(value); + InternalWriteString(value); } } else { - WriteRaw(value); + InternalWriteString(value); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(long value) { - if (!BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } + FlushIfNeeded(8); - Unsafe.WriteUnaligned(ref Reserve(8), value); + BinaryPrimitives.WriteInt64LittleEndian(_buffer.AsSpan((int)_index), value); + Index += 8; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(ulong value) { - if (!BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } + FlushIfNeeded(8); - Unsafe.WriteUnaligned(ref Reserve(8), value); + BinaryPrimitives.WriteUInt64LittleEndian(_buffer.AsSpan((int)_index), value); + Index += 8; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(int value) { - if (!BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } + FlushIfNeeded(4); - Unsafe.WriteUnaligned(ref Reserve(4), value); + BinaryPrimitives.WriteInt32LittleEndian(_buffer.AsSpan((int)_index), value); + Index += 4; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(uint value) { - if (!BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } + FlushIfNeeded(4); - Unsafe.WriteUnaligned(ref Reserve(4), value); + BinaryPrimitives.WriteUInt32LittleEndian(_buffer.AsSpan((int)_index), value); + Index += 4; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(short value) { - if (!BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } + FlushIfNeeded(2); - Unsafe.WriteUnaligned(ref Reserve(2), value); + BinaryPrimitives.WriteInt16LittleEndian(_buffer.AsSpan((int)_index), value); + Index += 2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(ushort value) { - if (!BitConverter.IsLittleEndian) - { - value = BinaryPrimitives.ReverseEndianness(value); - } + FlushIfNeeded(2); - Unsafe.WriteUnaligned(ref Reserve(2), value); + BinaryPrimitives.WriteUInt16LittleEndian(_buffer.AsSpan((int)_index), value); + Index += 2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(double value) { - if (!BitConverter.IsLittleEndian) - { - value = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReverseEndianness(BitConverter.DoubleToInt64Bits(value))); - } + FlushIfNeeded(8); - Unsafe.WriteUnaligned(ref Reserve(8), value); + BinaryPrimitives.WriteDoubleLittleEndian(_buffer.AsSpan((int)_index), value); + Index += 8; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(float value) { - if (!BitConverter.IsLittleEndian) - { - value = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReverseEndianness(BitConverter.SingleToInt32Bits(value))); - } + FlushIfNeeded(4); - Unsafe.WriteUnaligned(ref Reserve(4), value); + BinaryPrimitives.WriteSingleLittleEndian(_buffer.AsSpan((int)_index), value); + Index += 4; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(byte value) => Reserve(1) = value; + public void Write(byte value) + { + FlushIfNeeded(1); + _buffer[Index++] = value; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(sbyte value) => Reserve(1) = (byte)value; + public void Write(sbyte value) + { + FlushIfNeeded(1); + _buffer[Index++] = (byte)value; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(bool value) => Reserve(1) = Unsafe.As(ref value); + public unsafe void Write(bool value) + { + FlushIfNeeded(1); + _buffer[Index++] = *(byte*)&value; // up to 30% faster to dereference the raw value on the stack + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(Serial serial) => Write(serial.Value); @@ -303,6 +285,7 @@ public class BufferWriter : IGenericWriter { Write((byte)0x2); // xxHash3 64bit Write(AssemblyHandler.GetTypeHash(type)); + _types?.Enqueue(type); } } @@ -316,278 +299,18 @@ public class BufferWriter : IGenericWriter } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteEncodedInt(int value) + internal void InternalWriteString(string value) { - var v = (uint)value; + var length = _encoding.GetByteCount(value); - // FAST PATH: 1 byte (0 to 127). - // This keeps the inlined code incredibly tiny at the call site. - if (v < 0x80) - { - Reserve(1) = (byte)v; - } - else - { - // SLOW PATH: Push to a non-inlined method to prevent code bloat. - WriteEncodedIntMultiByte(v); - } - } + ((IGenericWriter)this).WriteEncodedInt(length); - [MethodImpl(MethodImplOptions.NoInlining)] - private void WriteEncodedIntMultiByte(uint v) - { - // We already know v >= 0x80. Unroll the loop entirely based on magnitude. - // This allows us to call Reserve() exactly ONE time. - - if (v < 0x4000) // 2 bytes - { - ref byte ptr = ref Reserve(2); - ptr = (byte)(v | 0x80); - Unsafe.Add(ref ptr, 1) = (byte)(v >> 7); - } - else if (v < 0x200000) // 3 bytes - { - ref byte ptr = ref Reserve(3); - ptr = (byte)(v | 0x80); - Unsafe.Add(ref ptr, 1) = (byte)((v >> 7) | 0x80); - Unsafe.Add(ref ptr, 2) = (byte)(v >> 14); - } - else if (v < 0x10000000) // 4 bytes - { - ref byte ptr = ref Reserve(4); - ptr = (byte)(v | 0x80); - Unsafe.Add(ref ptr, 1) = (byte)((v >> 7) | 0x80); - Unsafe.Add(ref ptr, 2) = (byte)((v >> 14) | 0x80); - Unsafe.Add(ref ptr, 3) = (byte)(v >> 21); - } - else // 5 bytes (including all negative numbers due to logical shift) - { - ref byte ptr = ref Reserve(5); - ptr = (byte)(v | 0x80); - Unsafe.Add(ref ptr, 1) = (byte)((v >> 7) | 0x80); - Unsafe.Add(ref ptr, 2) = (byte)((v >> 14) | 0x80); - Unsafe.Add(ref ptr, 3) = (byte)((v >> 21) | 0x80); - Unsafe.Add(ref ptr, 4) = (byte)(v >> 28); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(DateTime value) - { - // If DateTimeKind is Unspecified, we can't assume it needs to be converted. - if (value.Kind == DateTimeKind.Local) - { - value = value.ToUniversalTime(); - } - - Write(value.Ticks); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("Delta time rewrites its bytes on every save. Write anchored time instead (WriteAnchoredTime, or [AnchoredDateTime] on generated fields); bump the containing type's version, as the wire format changes. Existing delta payloads remain readable through ReadDeltaTime in old-version fallbacks.")] - public void WriteDeltaTime(DateTime value) - { - if (value == DateTime.MinValue) - { - Write(long.MinValue); - return; - } - - if (value == DateTime.MaxValue) - { - Write(long.MaxValue); - return; - } - - if (value.Kind == DateTimeKind.Local) - { - value = value.ToUniversalTime(); - } - - // Technically supports negative deltas for times in the past - Write(value.Ticks - DateTime.UtcNow.Ticks); - } - - /// - /// Writes the absolute value; re-bases it - /// by the elapsed time since the save started, so downtime does not age it and an - /// unchanged value serializes to identical bytes. - /// - public void WriteAnchoredTime(DateTime value) - { - if (value.Kind == DateTimeKind.Local) - { - value = value.ToUniversalTime(); - } - - Write(value.Ticks); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(IPAddress value) - { - Span stack = stackalloc byte[16]; - value.TryWriteBytes(stack, out var bytesWritten); - Write((byte)bytesWritten); - Write(stack[..bytesWritten]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(TimeSpan value) => Write(value.Ticks); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(Point3D value) - { - Write(value.m_X); - Write(value.m_Y); - Write(value.m_Z); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(Point2D value) - { - Write(value.m_X); - Write(value.m_Y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(Rectangle2D value) - { - Write(value.Start); - Write(value.End); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(Rectangle3D value) - { - Write(value.Start); - Write(value.End); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(Map value) => Write((byte)(value?.MapIndex ?? 0xFF)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(Race value) => Write((byte)(value?.RaceIndex ?? 0xFF)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteEnum(T value) where T : unmanaged, Enum - { - switch (sizeof(T)) - { - default: - { - throw new ArgumentException($"Argument of type {typeof(T)} is not a normal enum"); - } - case 1: - { - Write(*(byte*)&value); - break; - } - case 2: - { - Write(*(ushort*)&value); - break; - } - case 4: - { - WriteEncodedInt(*(int*)&value); - break; - } - case 8: - { - Write(*(ulong*)&value); - break; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(Guid guid) - { - Span stack = stackalloc byte[16]; - guid.TryWriteBytes(stack); - Write(stack); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(BitArray bitArray) - { - var bitLength = bitArray.Length; - var byteLength = (bitLength + 7) / 8; - - WriteEncodedInt(bitLength); - - var arrayBuffer = ArrayPool.Shared.Rent(byteLength); - try - { - bitArray.CopyTo(arrayBuffer, 0); - Write(arrayBuffer.AsSpan(0, byteLength)); - } - finally - { - ArrayPool.Shared.Return(arrayBuffer); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(TextDefinition def) - { - if (def == null) - { - WriteEncodedInt(3); - } - else if (def.Number > 0) - { - WriteEncodedInt(1); - WriteEncodedInt(def.Number); - } - else if (def.String != null) - { - WriteEncodedInt(2); - Write(def.String); - } - else - { - WriteEncodedInt(0); // Empty - } - } - - public void WriteRaw(string value) - { - // Single pass, in place: reserve the UTF-8 worst case (3 bytes per char) plus a - // length prefix sized for that worst case, encode directly into the buffer, then - // write the actual byte count into the reserved prefix zero-padded to the same - // width. Readers accumulate 7-bit groups, so non-minimal prefixes decode - // identically — no second pass over the string, no scratch copy, no pooling. - var maxLength = value.Length * 3; - var prefixWidth = EncodedIntWidth(maxLength); - - while (_buffer.Length - _index < prefixWidth + maxLength) + while (_buffer.Length - _index < length) { Flush(); } - var written = _encoding.GetBytes(value, _buffer.AsSpan((int)(_index + prefixWidth))); - - WriteEncodedIntPadded(written, prefixWidth); - _index += written; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int EncodedIntWidth(int value) => - value < 0x80 ? 1 : value < 0x4000 ? 2 : value < 0x20_0000 ? 3 : value < 0x1000_0000 ? 4 : 5; - - private void WriteEncodedIntPadded(int value, int width) - { - var v = (uint)value; - - for (var i = 1; i < width; i++) - { - _buffer[_index++] = (byte)(v | 0x80); - v >>= 7; - } - - _buffer[_index++] = (byte)v; // fits in 7 bits because width >= EncodedIntWidth(value) + // We don't use spans here since that incurs extra allocations for safety. + Index += _encoding.GetBytes(value, 0, value.Length, _buffer, (int)_index); } } diff --git a/Projects/Server/Serialization/FileBufferWriter.cs b/Projects/Server/Serialization/FileBufferWriter.cs deleted file mode 100644 index 9f1e9c290..000000000 --- a/Projects/Server/Serialization/FileBufferWriter.cs +++ /dev/null @@ -1,138 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: FileBufferWriter.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Buffers; -using System.IO; -using Microsoft.Win32.SafeHandles; - -namespace Server; - -/// -/// A whose staging block drains to a file when full instead of -/// growing: the full raw write path (unrolled encoded ints, in-place strings) composes into -/// memory, and the file sees large sequential positional writes. Seeks flush the staging -/// block and move the file offset, so backwards patches (e.g. the idx entity count) become -/// small positional writes. Memory-mapped writing pays soft page faults on every composed -/// page and dirty-section teardown stalls at dispose — measured ~4x slower at snapshot sizes. -/// A single item larger than the staging block grows the block via the base resize path, -/// so oversized spans and strings remain correct. -/// -public class FileBufferWriter : BufferWriter, IDisposable -{ - private const int MinStagingSize = 256; - private const int MaxStagingSize = 1024 * 1024; // 1MB write granularity for large files - - private readonly SafeFileHandle _handle; - private readonly byte[] _rentedStaging; - private long _fileOffset; // file position where the staging block begins - private long _fileHighWater; // logical end of file across seeks - - /// Destination file; created/truncated. - /// - /// Expected total file size when known. Files at or under the staging cap never - /// drain until close; larger files stream through a pooled block at the cap. The - /// block comes from ArrayPool so sequential snapshot writers recycle one buffer - /// instead of dropping a large-object allocation per file per save. - /// - public FileBufferWriter(string filePath, long expectedSize = MaxStagingSize) - : base(RentStaging(expectedSize), true) - { - _rentedStaging = Buffer; - _handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write, FileShare.None, FileOptions.SequentialScan); - } - - private static byte[] RentStaging(long expectedSize) => - ArrayPool.Shared.Rent((int)Math.Clamp(expectedSize, MinStagingSize, MaxStagingSize)); - - public override long Position => _fileOffset + Index; - - public override void Flush() - { - if (Index > 0) - { - Drain(); - } - else - { - // Nothing staged and still not enough room: a single item larger than the - // staging block. Grow the block so the base write loops always make progress. - base.Flush(); - } - } - - private void Drain() - { - var length = (int)Index; - RandomAccess.Write(_handle, Buffer.AsSpan(0, length), _fileOffset); - _fileOffset += length; - - if (_fileOffset > _fileHighWater) - { - _fileHighWater = _fileOffset; - } - - Index = 0; - } - - public override long Seek(long offset, SeekOrigin origin) - { - var position = Position; - - if (position > _fileHighWater) - { - _fileHighWater = position; - } - - var target = origin switch - { - SeekOrigin.Current => position + offset, - SeekOrigin.End => _fileHighWater + offset, - _ => offset // Begin - }; - - if (target < 0) - { - throw new InvalidOperationException("Seek before start of file"); - } - - if (Index > 0) - { - Drain(); - } - - _fileOffset = target; - return target; - } - - public override void Close() - { - if (!_handle.IsClosed) - { - if (Index > 0) - { - Drain(); - } - - _handle.Dispose(); - - // Safe even if an oversized item grew the staging block: growth replaced the - // base buffer with a fresh array, so the rented one is no longer referenced. - ArrayPool.Shared.Return(_rentedStaging); - } - } - - public void Dispose() => Close(); -} diff --git a/Projects/Server/Serialization/GenericEntityPersistence.cs b/Projects/Server/Serialization/GenericEntityPersistence.cs index c7323329f..e9ef932c1 100644 --- a/Projects/Server/Serialization/GenericEntityPersistence.cs +++ b/Projects/Server/Serialization/GenericEntityPersistence.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GenericEntityPersistence.cs * * * @@ -28,23 +28,13 @@ namespace Server; public interface IGenericEntityPersistence { - void DeserializeIndexes(string savePath, Dictionary typesDb); + public void DeserializeIndexes(string savePath, Dictionary typesDb); } -public class GenericEntityPersistence : GenericPersistence, IGenericEntityPersistence, ISlotRangeSource - where T : class, ISerializable +public class GenericEntityPersistence : GenericPersistence, IGenericEntityPersistence where T : class, ISerializable { private static readonly ILogger logger = LogFactory.GetLogger(typeof(GenericEntityPersistence)); - // Layout-validated direct access to EntitiesBySerial's entries array, letting workers - // iterate the dictionary in parallel during saves. Null when unsupported on this runtime. - private static readonly FieldInfo _entriesField = - ShadowDictionaryEntries.Supported ? ShadowDictionaryEntries.GetEntriesField() : null; - - // The entries array captured at freeze time. The dictionary cannot mutate while saving - // (adds/removes divert to the pending queues), so the array is stable until released. - private object _entriesSnapshot; - // Support legacy split file serialization private static Dictionary>> _entities; @@ -54,35 +44,6 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer private readonly Dictionary _pendingAdd = new(); private readonly Dictionary _pendingDelete = new(); - // Insertion-ordered table of every entity type added since boot. idx v4 records - // reference types by table index, so entries are never removed — a type whose - // entities were all deleted keeps its slot until restart. Only mutated on the game - // thread (AddEntity, deserialize); only read on the background writer thread during - // WritingSave, when AddEntity diverts to the pending queues. - private readonly Dictionary _typeIndexes = new(); - private readonly List _typeTable = []; - - internal IReadOnlyList TypeTable => _typeTable; - - internal bool TryGetTypeIndex(Type type, out ushort index) => _typeIndexes.TryGetValue(type, out index); - - internal void RegisterType(Type type) - { - ref var index = ref CollectionsMarshal.GetValueRefOrAddDefault(_typeIndexes, type, out var exists); - if (!exists) - { - if (_typeTable.Count > ushort.MaxValue) - { - throw new InvalidOperationException( - $"{Name} exceeded {ushort.MaxValue + 1} distinct entity types." - ); - } - - index = (ushort)_typeTable.Count; - _typeTable.Add(type); - } - } - public Dictionary EntitiesBySerial { get; } = new(); public GenericEntityPersistence(string name, int priority, uint minSerial, uint maxSerial) : this( @@ -102,275 +63,83 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer typeof(T).RegisterFindEntity(Find); } - public override void WriteSnapshot(string savePath) + public override void WriteSnapshot(string savePath, HashSet typeSet) { var dir = Path.Combine(savePath, Name); PathUtility.EnsureDirectory(dir); var threads = World._threadWorkers; - // 1MB buffer: segments are written as large spans, but idx entries and skip splits - // still benefit on the snapshot thread. - using var binFs = new FileStream( - Path.Combine(dir, $"{Name}.bin"), FileMode.Create, FileAccess.Write, FileShare.None, 1024 * 1024 - ); - // v4 records are fixed-width 26 bytes; the v5 header carries the save-start anchor - // and the type table (name lengths vary — 64 bytes per entry is a staging hint, not - // a contract). - var expectedIdxSize = 20 + 26L * EntitiesBySerial.Count + 64L * _typeTable.Count; - using var idx = new FileBufferWriter(Path.Combine(dir, $"{Name}.idx"), expectedIdxSize); + using var binFs = new FileStream(Path.Combine(dir, $"{Name}.bin"), FileMode.Create, FileAccess.Write, FileShare.None); + using var idxFs = new FileStream(Path.Combine(dir, $"{Name}.idx"), FileMode.Create); + using var idx = new MemoryMapFileWriter(idxFs, 1024 * 1024, typeSet); // 1MB var binPosition = 0L; // Support for non-entity generic serialization. - if (_selfLength > 0) + if (SerializedLength > 0) { try { - binFs.Write(threads[_selfThread].GetHeap(_selfPosition, _selfLength)); + binFs.Write(threads[SerializedThread].GetHeap(SerializedPosition, SerializedLength)); } catch (Exception error) { logger.Error( error, - "Error writing self-payload: (Thread: {Thread} - {Start} {Length})", - _selfThread, - _selfPosition, - _selfLength + "Error writing entity: (Thread: {Thread} - {Start} {Length})", + SerializedThread, + SerializedPosition, + SerializedLength ); } - binPosition += _selfLength; + binPosition += SerializedLength; } - idx.Write(5); // Version + idx.Write(3); // Version + idx.Write(EntitiesBySerial.Values.Count); - // One anchor for the whole save: the world is frozen from the moment it is stamped. - idx.Write(World.SaveStartTime.Ticks); - - // The type table is fully known at freeze (AddEntity diverts to the pending - // queues while saving) and is written before the records so the loader can - // resolve constructors before reading them. - idx.Write(_typeTable.Count); - for (var i = 0; i < _typeTable.Count; i++) + foreach (var e in EntitiesBySerial.Values) { - idx.WriteRaw(_typeTable[i].FullName); - } + var thread = e.SerializedThread; + var heapStart = e.SerializedPosition; + var heapLength = e.SerializedLength; - var countPosition = idx.Position; - idx.Write(0); + idx.Write(e.GetType()); + idx.Write(e.Serial); + idx.Write(e.Created.Ticks); + idx.Write(binPosition); + idx.Write(heapLength); - // The bin is written in worker-heap order, not dictionary order: each worker logged - // (segment, record lengths) as it serialized, so records pair with their bytes by - // re-walking the same slots in the same order. The idx stores absolute positions, - // so the loader never cares about record order. - var entityCount = 0; - - for (var t = 0; t < threads.Length; t++) - { - var worker = threads[t]; - var segments = worker.Segments; - - for (var s = 0; s < segments.Count; s++) + try { - var segment = segments[s]; - if (!ReferenceEquals(segment.Owner, this)) - { - continue; - } - - try - { - binPosition = WriteSegmentRecords(worker, in segment, idx, binFs, binPosition, ref entityCount); - } - catch (Exception error) - { - logger.Error( - error, - "Error writing segment: (Thread: {Thread} - {Start}, {Records} records)", - t, - segment.HeapStart, - segment.RecordCount - ); - } + binFs.Write(threads[thread].GetHeap(heapStart, heapLength)); } - } - - var currentPosition = idx.Position; - idx.Seek(countPosition, SeekOrigin.Begin); - idx.Write(entityCount); - idx.Seek(currentPosition, SeekOrigin.Begin); - } - - private long WriteSegmentRecords( - SerializationThreadWorker worker, in SerializedSegment segment, FileBufferWriter idx, FileStream binFs, - long binPosition, ref int entityCount - ) - { - var lengths = worker.Lengths; - var lengthIndex = segment.LengthsStart; - - var heapPos = (int)segment.HeapStart; - var spanStart = heapPos; - - if (segment.SlotOffset >= 0) - { - // Re-walk the same slots the worker serialized; occupancy cannot have changed - // because dictionary mutations divert to the pending queues until PostWorldSave. - var entries = Unsafe.As[]>(_entriesSnapshot); - ref var entry = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(entries), segment.SlotOffset); - - for (var i = 0; i < segment.SlotCount; i++, entry = ref Unsafe.Add(ref entry, 1)) + catch (Exception error) { - var entity = entry.Value; - if (entity == null) - { - continue; - } - - var length = lengths[lengthIndex++]; - - if (entity is Item { SkipSerialization: true } or Mobile { SkipSerialization: true }) - { - // The bytes exist in the heap but are not part of the save: split the span. - if (heapPos > spanStart) - { - binFs.Write(worker.GetHeap(spanStart, heapPos - spanStart)); - } - - heapPos += length; - spanStart = heapPos; - continue; - } - - idx.Write(GetTypeIndex(entity)); - idx.Write(entity.Serial); - idx.Write(entity.Created.Ticks); - idx.Write(binPosition); - idx.Write(length); - - binPosition += length; - heapPos += length; - entityCount++; + logger.Error( + error, + "Error writing entity: {Entity} (Thread: {Thread} - {Start} {Length})", + e, + thread, + heapStart, + heapLength + ); } + + binPosition += heapLength; } - else - { - var bufferEntities = worker.BufferEntities; - - for (var i = 0; i < segment.RecordCount; i++) - { - var entity = (T)bufferEntities[segment.EntitiesStart + i]; - var length = lengths[lengthIndex++]; - - if (entity is Item { SkipSerialization: true } or Mobile { SkipSerialization: true }) - { - if (heapPos > spanStart) - { - binFs.Write(worker.GetHeap(spanStart, heapPos - spanStart)); - } - - heapPos += length; - spanStart = heapPos; - continue; - } - - idx.Write(GetTypeIndex(entity)); - idx.Write(entity.Serial); - idx.Write(entity.Created.Ticks); - idx.Write(binPosition); - idx.Write(length); - - binPosition += length; - heapPos += length; - entityCount++; - } - } - - if (heapPos > spanStart) - { - binFs.Write(worker.GetHeap(spanStart, heapPos - spanStart)); - } - - return binPosition; - } - - private ushort GetTypeIndex(T entity) - { - // Every path into EntitiesBySerial registers the type first, so this cannot fire. - // If it ever does, the segment-level catch in WriteSnapshot logs it and moves on — - // the failed segment's records are dropped from the idx while binPosition rewinds, - // so treat any occurrence as a serious bug in an insertion path, not a bad entity. - if (!_typeIndexes.TryGetValue(entity.GetType(), out var typeIndex)) - { - throw new InvalidOperationException( - $"{entity.GetType()} was serialized but never registered; entities must enter {Name} through AddEntity." - ); - } - - return typeIndex; } public override void Serialize() { - // Self-payload first so a large one overlaps the entity stream instead of ending it. - World.PushSingleToCache(this); - - // Fast path: publish slot ranges of the dictionary's entries array so the workers - // iterate it directly in parallel — the main thread never touches the entities. - if (TrySnapshotEntries(out var slotCount)) - { - World.PushSlotRangesToCache(this, slotCount); - return; - } - - // Fallback: enumerate and hand off every entity from the main thread. Kept branch-free: - // a bare loop is ~2.3x faster than one carrying per-entity logic, and multi-megabyte - // entities are rare enough that riding inside a shared chunk is an acceptable tail. foreach (var entity in EntitiesBySerial.Values) { World.PushToCache(entity); } - } - internal bool TrySnapshotEntries(out int slotCount) - { - if (_entriesField != null && EntitiesBySerial.Count > 0 && - _entriesField.GetValue(EntitiesBySerial) is Array entries) - { - _entriesSnapshot = entries; - slotCount = entries.Length; - return true; - } - - slotCount = 0; - return false; - } - - int ISlotRangeSource.SerializeRange(BufferWriter writer, List lengths, int offset, int count) - { - // Layout proven at startup by ShadowDictionaryEntries.Supported; ranges are produced - // from the same array's length, so every read is in-bounds. - var entries = Unsafe.As[]>(_entriesSnapshot); - var serialized = 0; - - ref var entry = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(entries), offset); - - for (var i = 0; i < count; i++, entry = ref Unsafe.Add(ref entry, 1)) - { - // Occupied slots are exactly the non-null values: Dictionary clears reference - // values on remove, and never-used capacity is zero-initialized. - var entity = entry.Value; - if (entity != null) - { - var start = writer.Position; - entity.Serialize(writer); - lengths.Add((int)(writer.Position - start)); - serialized++; - } - } - - return serialized; + World.PushToCache(this); } private static ConstructorInfo GetConstructorFor(string typeName, Type t, Type[] constructorTypes) @@ -383,7 +152,7 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer Console.Write($"Error: Type '{typeName}' was {issue}. Delete all of those types? (y/n): "); - if (ConsoleInputHandler.ReadLine().InsensitiveEquals("y")) + if (Console.ReadLine().InsensitiveEquals("y")) { Console.WriteLine("Loading..."); return null; @@ -407,12 +176,12 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer /** * Legacy ReadTypes for backward compatibility with old saves that still have a tdb file */ - private unsafe Dictionary ReadTypes(string savePath) + private unsafe Dictionary ReadTypes(string savePath) { - var typesPath = Path.Combine(savePath, Name, $"{Name}.tdb"); + string typesPath = Path.Combine(savePath, Name, $"{Name}.tdb"); if (!File.Exists(typesPath)) { - return []; + return null; } Type[] ctorArguments = [typeof(Serial)]; @@ -425,22 +194,13 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer var dataReader = new UnmanagedDataReader(ptr, accessor.Length); var count = dataReader.ReadInt(); - var types = new Dictionary(count); + var types = new Dictionary(count); for (var i = 0; i < count; ++i) { // Legacy didn't have the null flag check var typeName = dataReader.ReadStringRaw(); - var type = AssemblyHandler.FindTypeByName(typeName); - var ctor = GetConstructorFor(typeName, type, ctorArguments); - - if (ctor != null) - { - // Keep the type table complete so the next (v4) save can index it. - RegisterType(type); - } - - types.Add((ulong)i, ctor); + types.Add(i, GetConstructorFor(typeName, AssemblyHandler.FindTypeByName(typeName), ctorArguments)); } accessor.SafeMemoryMappedViewHandle.ReleasePointer(); @@ -449,7 +209,7 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer public virtual void DeserializeIndexes(string savePath, Dictionary typesDb) { - var indexPath = Path.Combine(savePath, Name, $"{Name}.idx"); + string indexPath = Path.Combine(savePath, Name, $"{Name}.idx"); _entities ??= []; @@ -498,133 +258,52 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer var version = dataReader.ReadInt(); - if (version >= 5) - { - // Re-base anchored timestamps by the elapsed time since the save started. - var anchor = new DateTime(dataReader.ReadLong(), DateTimeKind.Utc); - var shift = Core.Now - anchor; - _anchoredTimeShift = anchor.Ticks > 0 && shift > TimeSpan.Zero ? shift : TimeSpan.Zero; + Dictionary ctors = []; - // The whole save shares one anchor. Publish it so payloads without their own - // (GenericPersistence bins) can shift too; indexes load before any of them. - World.LoadTimeShift = _anchoredTimeShift; + if (version < 2) + { + ctors = ReadTypes(Path.GetDirectoryName(filePath)); } - if (version >= 4) + if (typesDb == null && ctors == null) { - DeserializeIndexesV4(dataReader, entities); - } - else - { - var ctors = version < 2 ? ReadTypes(Path.GetDirectoryName(filePath)) : []; - - if (typesDb == null && ctors.Count == 0) - { - accessor.SafeMemoryMappedViewHandle.ReleasePointer(); - return; - } - - var now = DateTime.UtcNow; - var ctorArgs = new object[1]; - Type[] ctorArguments = [typeof(Serial)]; - - var count = dataReader.ReadInt(); - - for (var i = 0; i < count; ++i) - { - ulong hash; - // Version 2 & 3 with SerializedTypes.db - if (version >= 2) - { - var flag = dataReader.ReadByte(); - if (flag != 2) - { - throw new Exception($"Invalid type flag, expected 2 but received {flag}."); - } - - hash = dataReader.ReadULong(); - } - else - { - hash = (ulong)dataReader.ReadInt(); // Legacy RunUO tdb index - } - - if (!ctors.TryGetValue(hash, out var ctor) && typesDb?.TryGetValue(hash, out var typeName) == true) - { - var type = AssemblyHandler.FindTypeByHash(hash); - ctors[hash] = ctor = GetConstructorFor(typeName, type, ctorArguments); - - if (ctor != null) - { - // Keep the type table complete so the next (v4) save can index it. - RegisterType(type); - } - } - - var serial = (Serial)dataReader.ReadUInt(); - var created = version == 0 ? now : new DateTime(dataReader.ReadLong(), DateTimeKind.Utc); - if (version is > 0 and < 3) - { - dataReader.ReadLong(); // LastSerialized - } - - var pos = dataReader.ReadLong(); - var length = dataReader.ReadInt(); - - if (ctor == null) - { - continue; - } - - ctorArgs[0] = serial; - - if (ctor.Invoke(ctorArgs) is T entity) - { - entity.Created = created; - entities.Add(new EntitySpan(entity, pos, length)); - EntitiesBySerial[serial] = entity; - } - } + return; } - accessor.SafeMemoryMappedViewHandle.ReleasePointer(); - - if (EntitiesBySerial.Count > 0) - { - _lastEntitySerial = EntitiesBySerial.Keys.Max(); - } - } - - private void DeserializeIndexesV4(UnmanagedDataReader dataReader, List> entities) - { + var now = DateTime.UtcNow; + var ctorArgs = new object[1]; Type[] ctorArguments = [typeof(Serial)]; - var typeCount = dataReader.ReadInt(); - var ctors = new ConstructorInfo[typeCount]; - - for (var i = 0; i < typeCount; i++) - { - var typeName = dataReader.ReadStringRaw(); - var type = AssemblyHandler.FindTypeByHash(HashUtility.ComputeHash64(typeName)); - var ctor = GetConstructorFor(typeName, type, ctorArguments); - - if (ctor != null) - { - // Keep the type table complete so the next save can index it. - RegisterType(type); - } - - ctors[i] = ctor; - } - - var ctorArgs = new object[1]; var count = dataReader.ReadInt(); for (var i = 0; i < count; ++i) { - var ctor = ctors[dataReader.ReadUShort()]; - var serial = (Serial)dataReader.ReadUInt(); - var created = new DateTime(dataReader.ReadLong(), DateTimeKind.Utc); + ConstructorInfo ctor; + // Version 2 & 3 with SerializedTypes.db + if (version >= 2) + { + var flag = dataReader.ReadByte(); + if (flag != 2) + { + throw new Exception($"Invalid type flag, expected 2 but received {flag}."); + } + + var hash = dataReader.ReadULong(); + typesDb!.TryGetValue(hash, out var typeName); + ctor = GetConstructorFor(typeName, AssemblyHandler.FindTypeByHash(hash), ctorArguments); + } + else + { + ctor = ctors?[dataReader.ReadInt()]; + } + + Serial serial = (Serial)dataReader.ReadUInt(); + var created = version == 0 ? now : new DateTime(dataReader.ReadLong(), DateTimeKind.Utc); + if (version is > 0 and < 3) + { + dataReader.ReadLong(); // LastSerialized + } + var pos = dataReader.ReadLong(); var length = dataReader.ReadInt(); @@ -642,11 +321,18 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer EntitiesBySerial[serial] = entity; } } + + accessor.SafeMemoryMappedViewHandle.ReleasePointer(); + + if (EntitiesBySerial.Count > 0) + { + _lastEntitySerial = EntitiesBySerial.Keys.Max(); + } } public override void Deserialize(string savePath, Dictionary typesDb) { - var dataPath = Path.Combine(savePath, Name, $"{Name}.bin"); + string dataPath = Path.Combine(savePath, Name, $"{Name}.bin"); var fi = new FileInfo(dataPath); if (!fi.Exists) @@ -661,24 +347,8 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer _entities.Clear(); _entities.TrimExcess(); _entities = null; - - if (_toDelete != null) - { - foreach (var t in _toDelete) - { - t.Delete(); - } - - _toDelete.Clear(); - _toDelete = null; - } } - private static List _toDelete; - - // From the loaded idx (v5+); zero when the save predates the anchor. - private TimeSpan _anchoredTimeShift; - private unsafe void InternalDeserialize(string filePath, int index, Dictionary typesDb) { using var mmf = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open); @@ -686,10 +356,7 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer byte* ptr = null; accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); - var dataReader = new UnmanagedDataReader(ptr, accessor.Length, typesDb) - { - AnchoredTimeShift = _anchoredTimeShift - }; + UnmanagedDataReader dataReader = new UnmanagedDataReader(ptr, accessor.Length, typesDb); Deserialize(dataReader); @@ -697,7 +364,7 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer foreach (var entry in _entities[index]) { - var t = entry.Entity; + T t = entry.Entity; if (entry.Length == 0) { @@ -738,7 +405,7 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer if (!deleteAllFailures) { Console.Write("Delete the object and continue? (y/n/a): "); - var pressedKey = ConsoleInputHandler.ReadLine(); + var pressedKey = Console.ReadLine(); if (pressedKey.InsensitiveEquals("a")) { @@ -750,8 +417,7 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer } } - _toDelete ??= []; - _toDelete.Add(t); + t.Delete(); } } @@ -786,8 +452,6 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer public override void PostWorldSave() { - // Release the snapshot so a between-saves resize doesn't pin the old array. - _entriesSnapshot = null; ProcessSafetyQueues(); } @@ -865,8 +529,7 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer case WorldState.PendingSave: case WorldState.Running: { - RegisterType(entity.GetType()); - ref var entityEntry = ref CollectionsMarshal.GetValueRefOrAddDefault(EntitiesBySerial, entity.Serial, out var exists); + ref var entityEntry = ref CollectionsMarshal.GetValueRefOrAddDefault(EntitiesBySerial, entity.Serial, out bool exists); if (exists) { if (entityEntry == entity) diff --git a/Projects/Server/Serialization/GenericPersistence.cs b/Projects/Server/Serialization/GenericPersistence.cs index 2268a86c6..a89e19141 100644 --- a/Projects/Server/Serialization/GenericPersistence.cs +++ b/Projects/Server/Serialization/GenericPersistence.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GenericPersistence.cs * * * @@ -25,24 +25,9 @@ public abstract class GenericPersistence : Persistence, IGenericSerializable public string Name { get; } public string SaveFilePath { get; protected set; } // "/.bin" - // Placement of the self-payload in the worker heaps for the most recent save. Only - // persistences carry placement state — entities are located through the per-worker - // segment logs instead. - private protected byte _selfThread; - private protected int _selfPosition; - private protected int _selfLength; - - internal void SetSelfPlacement(byte thread, int position, int length) - { - _selfThread = thread; - _selfPosition = position; - _selfLength = length; - } - - private long _loadedFileLength; - - // Scheduling estimate only: previous save's payload size, or the loaded file size before the first save. - internal long EstimatedSize => _selfLength > 0 ? _selfLength : _loadedFileLength; + public byte SerializedThread { get; set; } + public int SerializedPosition { get; set; } + public int SerializedLength { get; set; } public GenericPersistence(string name, int priority) : base(priority) { @@ -52,14 +37,12 @@ public abstract class GenericPersistence : Persistence, IGenericSerializable public override void Serialize() { - // Always a dedicated chunk: self-payloads can be arbitrarily large and must not - // ride inside a shared chunk where one worker would serialize them plus the chunk. - World.PushSingleToCache(this); + World.PushToCache(this); } - public override void WriteSnapshot(string savePath) + public override void WriteSnapshot(string savePath, HashSet typeSet) { - if (_selfLength == 0) + if (SerializedLength == 0) { return; } @@ -72,7 +55,11 @@ public abstract class GenericPersistence : Persistence, IGenericSerializable using var binFs = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None); - binFs.Write(threads[_selfThread].GetHeap(_selfPosition, _selfLength)); + var thread = SerializedThread; + var heapStart = SerializedPosition; + var heapLength = SerializedLength; + + binFs.Write(threads[thread].GetHeap(heapStart, heapLength)); } public override unsafe void Deserialize(string savePath, Dictionary typesDb) @@ -87,7 +74,6 @@ public abstract class GenericPersistence : Persistence, IGenericSerializable } var fileLength = file.Length; - _loadedFileLength = fileLength; string error; @@ -98,13 +84,7 @@ public abstract class GenericPersistence : Persistence, IGenericSerializable byte* ptr = null; accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr); - var dataReader = new UnmanagedDataReader(ptr, accessor.Length, typesDb) - { - // These payloads carry no anchor of their own; they inherit the save-wide - // shift stamped while the entity indexes were read (indexes always load - // before persistence payloads — see Persistence.Load). - AnchoredTimeShift = World.LoadTimeShift - }; + var dataReader = new UnmanagedDataReader(ptr, accessor.Length, typesDb); Deserialize(dataReader); error = dataReader.Position != fileLength @@ -124,7 +104,7 @@ public abstract class GenericPersistence : Persistence, IGenericSerializable Console.WriteLine(error); Console.Write("Skip this file and continue? (y/n): "); - var y = ConsoleInputHandler.ReadLine(); + var y = Console.ReadLine(); if (!y.InsensitiveEquals("y")) { diff --git a/Projects/Server/Serialization/IGenericReader.cs b/Projects/Server/Serialization/IGenericReader.cs index bfa302b39..e3fe07deb 100644 --- a/Projects/Server/Serialization/IGenericReader.cs +++ b/Projects/Server/Serialization/IGenericReader.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IGenericReader.cs * * * @@ -14,7 +14,6 @@ *************************************************************************/ using System; -using System.Buffers; using System.Collections; using System.IO; using System.Net; @@ -43,12 +42,6 @@ public interface IGenericReader DateTime ReadDateTime() => new(ReadLong(), DateTimeKind.Utc); TimeSpan ReadTimeSpan() => new(ReadLong()); - /// - /// Decodes a legacy delta-time value. Only for reading old-version payloads (version - /// fallbacks and migration replays) — current formats store anchored time and read it - /// with . is - /// obsolete: no current-version format may write delta time. - /// DateTime ReadDeltaTime() { return ReadLong() switch @@ -58,38 +51,7 @@ public interface IGenericReader var delta => new DateTime(delta + DateTime.UtcNow.Ticks, DateTimeKind.Utc) }; } - - /// - /// Elapsed time between the loaded save starting and this load, applied by - /// . Zero when the source carries no anchor. - /// - TimeSpan AnchoredTimeShift => TimeSpan.Zero; - - DateTime ReadAnchoredTime() - { - var value = ReadDateTime(); - - if (value == DateTime.MinValue || value == DateTime.MaxValue) - { - return value; - } - - var shift = AnchoredTimeShift; - if (shift == TimeSpan.Zero) - { - return value; - } - - var ticks = value.Ticks + shift.Ticks; - - if (ticks >= DateTime.MaxValue.Ticks) - { - return DateTime.MaxValue; - } - - return ticks <= 0 ? DateTime.MinValue : new DateTime(ticks, DateTimeKind.Utc); - } - decimal ReadDecimal() => new([ReadInt(), ReadInt(), ReadInt(), ReadInt()]); + decimal ReadDecimal() => new(stackalloc int[4] { ReadInt(), ReadInt(), ReadInt(), ReadInt() }); int ReadEncodedInt() { int v = 0, shift = 0; @@ -107,7 +69,7 @@ public interface IGenericReader } IPAddress ReadIPAddress() { - var length = ReadByte(); + byte length = ReadByte(); // Either 2 ushorts, or 8 ushorts Span integer = stackalloc byte[length]; Read(integer); @@ -157,19 +119,14 @@ public interface IGenericReader public BitArray ReadBitArray() { - var bitLength = ReadEncodedInt(); - var byteLength = (bitLength + 7) / 8; + var byteArrayLength = ReadEncodedInt(); - var buffer = ArrayPool.Shared.Rent(byteLength); - try - { - Read(buffer.AsSpan(0, byteLength)); - return new BitArray(buffer) { Length = bitLength }; - } - finally - { - ArrayPool.Shared.Return(buffer); - } + // We need an exact array size since the ctor doesn't allow for offset/length, not much we can do at this point. + var byteArray = new byte[byteArrayLength]; + + Read(byteArray); + + return new BitArray(byteArray); } TextDefinition ReadTextDefinition() diff --git a/Projects/Server/Serialization/IGenericSerializable.cs b/Projects/Server/Serialization/IGenericSerializable.cs index d53b1c03d..ecc54042d 100644 --- a/Projects/Server/Serialization/IGenericSerializable.cs +++ b/Projects/Server/Serialization/IGenericSerializable.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IGenericSerializable.cs * * * @@ -17,5 +17,9 @@ namespace Server; public interface IGenericSerializable { + byte SerializedThread { get; set; } + int SerializedPosition { get; set; } + int SerializedLength { get; set; } + void Serialize(IGenericWriter writer); } diff --git a/Projects/Server/Serialization/IGenericWriter.cs b/Projects/Server/Serialization/IGenericWriter.cs index 4162655de..63ac04970 100644 --- a/Projects/Server/Serialization/IGenericWriter.cs +++ b/Projects/Server/Serialization/IGenericWriter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IGenericWriter.cs * * * @@ -14,6 +14,7 @@ *************************************************************************/ using System; +using System.Buffers; using System.Collections; using System.IO; using System.Net; @@ -38,28 +39,161 @@ public interface IGenericWriter void Write(Serial serial); void Write(Type type); void Write(decimal value); - void WriteEncodedInt(int value); - void Write(DateTime value); - [Obsolete("Delta time rewrites its bytes on every save. Write anchored time instead (WriteAnchoredTime, or [AnchoredDateTime] on generated fields); bump the containing type's version, as the wire format changes. Existing delta payloads remain readable through ReadDeltaTime in old-version fallbacks.")] - void WriteDeltaTime(DateTime value); + void Write(DateTime value) + { + // If DateTimeKind is Unspecified, we can't assume it needs to be converted. + if (value.Kind == DateTimeKind.Local) + { + value = value.ToUniversalTime(); + } - void WriteAnchoredTime(DateTime value); - void Write(IPAddress value); - void Write(TimeSpan value); - void Write(Point3D value); - void Write(Point2D value); - void Write(Rectangle2D value); - void Write(Rectangle3D value); - void Write(Map value); - void Write(Race value); + Write(value.Ticks); + } + void WriteDeltaTime(DateTime value) + { + if (value == DateTime.MinValue) + { + Write(long.MinValue); + return; + } + + if (value == DateTime.MaxValue) + { + Write(long.MaxValue); + return; + } + + if (value.Kind == DateTimeKind.Local) + { + value = value.ToUniversalTime(); + } + + // Technically supports negative deltas for times in the past + Write(value.Ticks - DateTime.UtcNow.Ticks); + } + void Write(IPAddress value) + { + Span stack = stackalloc byte[16]; + value.TryWriteBytes(stack, out var bytesWritten); + Write((byte)bytesWritten); + Write(stack[..bytesWritten]); + } + + void Write(TimeSpan value) + { + Write(value.Ticks); + } + + void WriteEncodedInt(int value) + { + var v = (uint)value; + + while (v >= 0x80) + { + Write((byte)(v | 0x80)); + v >>= 7; + } + + Write((byte)v); + } + + void Write(Point3D value) + { + Write(value.m_X); + Write(value.m_Y); + Write(value.m_Z); + } + void Write(Point2D value) + { + Write(value.m_X); + Write(value.m_Y); + } + void Write(Rectangle2D value) + { + Write(value.Start); + Write(value.End); + } + void Write(Rectangle3D value) + { + Write(value.Start); + Write(value.End); + } + void Write(Map value) => Write((byte)(value?.MapIndex ?? 0xFF)); + void Write(Race value) => Write((byte)(value?.RaceIndex ?? 0xFF)); void Write(byte[] bytes); void Write(byte[] bytes, int offset, int count); void Write(ReadOnlySpan bytes); - void WriteEnum(T value) where T : unmanaged, Enum; - void Write(Guid guid); - void Write(BitArray bitArray); - void Write(TextDefinition def); + unsafe void WriteEnum(T value) where T : unmanaged, Enum + { + switch (sizeof(T)) + { + default: + { + throw new ArgumentException($"Argument of type {typeof(T)} is not a normal enum"); + } + case 1: + { + Write(*(byte*)&value); + break; + } + case 2: + { + Write(*(ushort*)&value); + break; + } + case 4: + { + WriteEncodedInt(*(int*)&value); + break; + } + case 8: + { + Write(*(ulong*)&value); + break; + } + } + } + void Write(Guid guid) + { + Span stack = stackalloc byte[16]; + guid.TryWriteBytes(stack); + Write(stack); + } + + public void Write(BitArray bitArray) + { + var bytesLength = (bitArray.Length - 1 + (1 << 3)) >>> 3; + var arrayBuffer = ArrayPool.Shared.Rent(bytesLength); + + WriteEncodedInt(bytesLength); + bitArray.CopyTo(arrayBuffer, 0); + + Write(arrayBuffer.AsSpan(0, bytesLength)); + ArrayPool.Shared.Return(arrayBuffer); + } + + void Write(TextDefinition def) + { + if (def == null) + { + WriteEncodedInt(3); + } + else if (def.Number > 0) + { + WriteEncodedInt(1); + WriteEncodedInt(def.Number); + } + else if (def.String != null) + { + WriteEncodedInt(2); + Write(def.String); + } + else + { + WriteEncodedInt(0); // Empty + } + } long Seek(long offset, SeekOrigin origin); } diff --git a/Projects/Server/Serialization/ISerializable.cs b/Projects/Server/Serialization/ISerializable.cs index cff1f3986..e44c92b2f 100644 --- a/Projects/Server/Serialization/ISerializable.cs +++ b/Projects/Server/Serialization/ISerializable.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ISerializable.cs * * * diff --git a/Projects/Server/Serialization/ISerializableExtensions.cs b/Projects/Server/Serialization/ISerializableExtensions.cs index 40be623a9..2f407a023 100644 --- a/Projects/Server/Serialization/ISerializableExtensions.cs +++ b/Projects/Server/Serialization/ISerializableExtensions.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ISerializableExtensions.cs * * * diff --git a/Projects/Server/Serialization/MemoryMapFileWriter.cs b/Projects/Server/Serialization/MemoryMapFileWriter.cs new file mode 100644 index 000000000..67de0a4ab --- /dev/null +++ b/Projects/Server/Serialization/MemoryMapFileWriter.cs @@ -0,0 +1,304 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2024 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: MemoryMapFileWriter.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.IO; +using System.IO.MemoryMappedFiles; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using Server.Text; + +namespace Server; + +public unsafe class MemoryMapFileWriter : IGenericWriter, IDisposable +{ + private readonly Encoding _encoding; + + private readonly HashSet _types; + private readonly FileStream _fileStream; + private MemoryMappedFile _mmf; + private MemoryMappedViewAccessor _accessor; + private byte* _ptr; + private long _position; + private long _size; + + public MemoryMapFileWriter(FileStream fileStream, long initialSize, HashSet types = null) + { + _types = types; + _fileStream = fileStream; + _encoding = TextEncoding.UTF8; + _size = Math.Max(initialSize, 1024); + + ResizeMemoryMappedFile(initialSize); + } + + public long Position => _position; + + public FileStream FileStream => _fileStream; + + private void ResizeMemoryMappedFile(long newSize) + { + _accessor?.SafeMemoryMappedViewHandle.ReleasePointer(); + _accessor?.Dispose(); + _mmf?.Dispose(); + + // Do the actual resizing + _fileStream.SetLength(newSize); + + _mmf = MemoryMappedFile.CreateFromFile(_fileStream, null, newSize, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, leaveOpen: true); + _accessor = _mmf.CreateViewAccessor(); + _accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref _ptr); + } + + private void EnsureCapacity(long bytesToWrite) + { + var shouldResize = false; + while (_position + bytesToWrite > _size) + { + // Don't double forever, eventually we want to have a maximum, like 256MB at a time or something + _size += Math.Min(_size, 1024 * 1024 * 256); + shouldResize = true; + } + + if (shouldResize) + { + ResizeMemoryMappedFile(_size); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(byte[] bytes) => Write(bytes.AsSpan()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(byte[] bytes, int offset, int count) => Write(bytes.AsSpan(offset, count)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(ReadOnlySpan bytes) + { + var byteCount = bytes.Length; + EnsureCapacity(byteCount); + + bytes.CopyTo(new Span(_ptr + _position, byteCount)); + _position += byteCount; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + { + if (offset > _size) + { + EnsureCapacity(offset); + } + + _position = offset; + break; + } + case SeekOrigin.Current: + { + EnsureCapacity(offset); + _position += offset; + break; + } + case SeekOrigin.End: + { + if (_position + offset > _size) + { + EnsureCapacity(offset); + } + + _position = _size + offset; + + if (_position < 0) + { + Dispose(); + throw new InvalidOperationException("Seek before start of file"); + } + break; + } + } + + return _position; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(string value) + { + if (value == null) + { + Write(false); + } + else + { + Write(true); + WriteStringRaw(value); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(long value) + { + EnsureCapacity(sizeof(long)); + BinaryPrimitives.WriteInt64LittleEndian(new Span(_ptr + _position, sizeof(long)), value); + _position += sizeof(long); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(ulong value) + { + EnsureCapacity(sizeof(ulong)); + BinaryPrimitives.WriteUInt64LittleEndian(new Span(_ptr + _position, sizeof(ulong)), value); + _position += sizeof(ulong); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(int value) + { + EnsureCapacity(sizeof(int)); + BinaryPrimitives.WriteInt32LittleEndian(new Span(_ptr + _position, sizeof(int)), value); + _position += sizeof(int); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(uint value) + { + EnsureCapacity(sizeof(uint)); + BinaryPrimitives.WriteUInt32LittleEndian(new Span(_ptr + _position, sizeof(uint)), value); + _position += sizeof(uint); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(short value) + { + EnsureCapacity(sizeof(short)); + BinaryPrimitives.WriteInt16LittleEndian(new Span(_ptr + _position, sizeof(short)), value); + _position += sizeof(short); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(ushort value) + { + EnsureCapacity(sizeof(ushort)); + BinaryPrimitives.WriteUInt16LittleEndian(new Span(_ptr + _position, sizeof(ushort)), value); + _position += sizeof(ushort); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(double value) + { + EnsureCapacity(sizeof(double)); + BinaryPrimitives.WriteDoubleLittleEndian(new Span(_ptr + _position, sizeof(double)), value); + _position += sizeof(double); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(float value) + { + EnsureCapacity(sizeof(float)); + BinaryPrimitives.WriteSingleLittleEndian(new Span(_ptr + _position, sizeof(float)), value); + _position += sizeof(float); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(byte value) + { + EnsureCapacity(1); + *(_ptr + _position++) = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(sbyte value) => Write((byte)value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(bool value) => Write(*(byte*)&value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(Serial serial) => Write(serial.Value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(Type type) + { + if (type == null) + { + Write((byte)0); + } + else + { + Write((byte)0x2); // xxHash3 64bit + Write(AssemblyHandler.GetTypeHash(type)); + _types.Add(type); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(decimal value) + { + Span buffer = stackalloc int[sizeof(decimal) / 4]; + decimal.GetBits(value, buffer); + + Write(MemoryMarshal.Cast(buffer)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteStringRaw(ReadOnlySpan value) + { + var length = _encoding.GetByteCount(value); + + EnsureCapacity(length + 5); + + // WriteEncodedInt + var v = (uint)length; + + while (v >= 0x80) + { + *(_ptr + _position++) = (byte)(v | 0x80); + v >>= 7; + } + *(_ptr + _position++) = (byte)v; + + _encoding.GetBytes(value, new Span(_ptr + _position, length)); + _position += length; + } + + private void Dispose(bool disposing) + { + if (disposing) + { + _accessor.SafeMemoryMappedViewHandle.ReleasePointer(); + _accessor.Dispose(); + _mmf.Dispose(); + + // Truncate the file + _fileStream.SetLength(_position); + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~MemoryMapFileWriter() + { + Dispose(false); + } +} diff --git a/Projects/Server/Serialization/Persistence.cs b/Projects/Server/Serialization/Persistence.cs index 35d1d7514..3c41c564e 100644 --- a/Projects/Server/Serialization/Persistence.cs +++ b/Projects/Server/Serialization/Persistence.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Persistence.cs * * * @@ -56,7 +56,7 @@ public abstract class Persistence { var db = new Dictionary(); - var typesPath = Path.Combine(path, "SerializedTypes.db"); + string typesPath = Path.Combine(path, "SerializedTypes.db"); if (!File.Exists(typesPath)) { return db; @@ -84,35 +84,41 @@ public abstract class Persistence } // Note: This is strictly on a background thread - internal static void WriteSnapshotAll(string path) + internal static void WriteSnapshotAll(string path, HashSet typeSet) { foreach (var p in _registry) { - p.WriteSnapshot(path); + p.WriteSnapshot(path, typeSet); + } + + WriteSerializedTypesSnapshot(path, typeSet); + } + + public static void WriteSerializedTypesSnapshot(string path, HashSet types) + { + string typesPath = Path.Combine(path, "SerializedTypes.db"); + using var fs = new FileStream(typesPath, FileMode.Create); + using var writer = new MemoryMapFileWriter(fs, 1024 * 1024 * 4); + + writer.Write(0); // version + writer.Write(types.Count); + + foreach (var type in types) + { + var fullName = type.FullName; + writer.Write(HashUtility.ComputeHash64(fullName)); + writer.WriteStringRaw(fullName); } } internal static void SerializeAll() { - // Largest known payloads first (LPT scheduling). An indivisible multi-megabyte system - // serialized last extends the freeze by its entire duration; serialized first it overlaps - // the entity stream. Sizes come from the previous save, or the loaded files on first save. - var ordered = new Persistence[_registry.Count]; - _registry.CopyTo(ordered); - Array.Sort(ordered, static (a, b) => GetEstimatedSize(b).CompareTo(GetEstimatedSize(a))); - - foreach (var p in ordered) + foreach (var p in _registry) { - // Chunks are persistence-homogeneous: publishing the partial chunk at each - // boundary lets workers attribute buffer-chunk records to their owner without - // any per-entity state. - World.SetChunkSourceOwner(p); p.Serialize(); } } - private static long GetEstimatedSize(Persistence p) => (p as GenericPersistence)?.EstimatedSize ?? 0; - internal static void PostWorldSaveAll() { foreach (var p in _registry) @@ -130,7 +136,7 @@ public abstract class Persistence } // Note: This should only be run on a background thread - public abstract void WriteSnapshot(string savePath); + public abstract void WriteSnapshot(string savePath, HashSet typeSet); public abstract void Serialize(); diff --git a/Projects/Server/Serialization/SerializationChunkSource.cs b/Projects/Server/Serialization/SerializationChunkSource.cs deleted file mode 100644 index 8b43304b3..000000000 --- a/Projects/Server/Serialization/SerializationChunkSource.cs +++ /dev/null @@ -1,171 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SerializationChunkSource.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Server; - -/// -/// A range of backing-store slots that a serialization worker can serialize directly, -/// letting workers iterate a persistence's storage in parallel instead of the main thread -/// enumerating and handing off every entity. Implemented by -/// over its dictionary's entries array. -/// -public interface ISlotRangeSource -{ - /// - /// Serializes every occupied slot in [offset, offset + count) into the writer, appending - /// each record's byte length to . Returns the number serialized. - /// - int SerializeRange(BufferWriter writer, List lengths, int offset, int count); -} - -/// -/// Single-producer/multi-consumer handoff between the game loop and the serialization -/// thread workers during a world save. The producer batches entities into pooled chunks -/// so the per-entity cost is a plain array store instead of a synchronized enqueue, and -/// workers pull whole chunks so they naturally load-balance: a worker busy with a thick -/// entity simply takes fewer chunks. -/// Persistence self-payloads are published as dedicated single-entity chunks so large -/// systems spread across workers instead of riding inside one chunk. -/// Persistences that support direct parallel iteration publish slot ranges instead of -/// filled chunks, removing the per-entity handoff from the freeze entirely. -/// -public sealed class SerializationChunkSource -{ - // 4096 refs (32KB per chunk) keeps producer sync cost at one enqueue per 4096 entities - // while the drain tail stays sub-millisecond. - private const int ChunkCapacity = 4096; - - internal readonly struct Chunk - { - public readonly GenericPersistence Single; - public readonly IGenericSerializable[] Buffer; - public readonly ISlotRangeSource Source; - public readonly Persistence Owner; // buffer chunks only; ranges use Source, singles record their own placement - public readonly int Offset; - public readonly int Count; - - public Chunk(GenericPersistence single) - { - Single = single; - Count = 1; - } - - public Chunk(IGenericSerializable[] buffer, int count, Persistence owner) - { - Buffer = buffer; - Count = count; - Owner = owner; - } - - public Chunk(ISlotRangeSource source, int offset, int count) - { - Source = source; - Offset = offset; - Count = count; - } - } - - private readonly ConcurrentQueue _chunks = new(); - private readonly ConcurrentQueue _pool = new(); - - // Producer state - written only by the game loop thread. - private IGenericSerializable[] _current; - private int _count; - private Persistence _currentOwner; - - /// - /// Declares the owner of subsequently pushed entities. Publishes the partial chunk when - /// the owner changes, keeping buffer chunks persistence-homogeneous so workers can - /// attribute their serialized records to a persistence without any per-entity state. - /// - public void SetOwner(Persistence owner) - { - if (!ReferenceEquals(_currentOwner, owner)) - { - Flush(); - _currentOwner = owner; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Push(IGenericSerializable entity) - { - var current = _current ??= Rent(); - - // Ref store skips the bounds and array-covariance checks. Safe by construction: - // _count is producer-thread-only and always < ChunkCapacity here (reset on publish), - // and the array's element type is exactly IGenericSerializable. - Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(current), _count) = entity; - - if (++_count == ChunkCapacity) - { - _chunks.Enqueue(new Chunk(current, ChunkCapacity, _currentOwner)); - _current = null; - _count = 0; - } - } - - /// - /// Publishes a persistence self-payload as a dedicated chunk regardless of its - /// estimated size — it can be large on the first save before an estimate exists. - /// The worker records the payload's placement on the persistence itself. - /// - public void PushSingle(GenericPersistence persistence) => _chunks.Enqueue(new Chunk(persistence)); - - /// - /// Publishes slot ranges covering [0, slotCount) of a directly-iterable persistence. - /// Workers claim ranges like any other chunk, so the per-entity handoff cost disappears - /// and load balancing is unchanged. - /// - public void PushSlotRanges(ISlotRangeSource source, int slotCount) - { - for (var offset = 0; offset < slotCount; offset += ChunkCapacity) - { - _chunks.Enqueue(new Chunk(source, offset, Math.Min(ChunkCapacity, slotCount - offset))); - } - } - - /// - /// Publishes the partial chunk, if any. Must be called on the producer thread before - /// the workers are told to finish draining, or the tail of the stream is not serialized. - /// - public void Flush() - { - if (_count > 0) - { - _chunks.Enqueue(new Chunk(_current, _count, _currentOwner)); - _current = null; - _count = 0; - } - } - - internal bool TryTake(out Chunk chunk) => _chunks.TryDequeue(out chunk); - - internal void Return(IGenericSerializable[] buffer, int count) - { - // Clear so pooled chunks don't keep entities reachable between saves. - Array.Clear(buffer, 0, count); - _pool.Enqueue(buffer); - } - - private IGenericSerializable[] Rent() => - _pool.TryDequeue(out var buffer) ? buffer : new IGenericSerializable[ChunkCapacity]; -} diff --git a/Projects/Server/Serialization/SerializationExtensions.cs b/Projects/Server/Serialization/SerializationExtensions.cs index bbf3e7748..198837dc0 100644 --- a/Projects/Server/Serialization/SerializationExtensions.cs +++ b/Projects/Server/Serialization/SerializationExtensions.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SerializationExtensions.cs * * * @@ -31,7 +31,7 @@ public static class SerializationExtensions public static T ReadEntity(this IGenericReader reader) where T : class, ISerializable { - var serial = reader.ReadSerial(); + Serial serial = reader.ReadSerial(); var typeT = typeof(T); T entity; @@ -182,10 +182,10 @@ public static class SerializationExtensions else { writer.Write(true); - writer.Write((byte)p.Index); + writer.Write((byte)p.Level); } } public static Poison ReadPoison(this IGenericReader reader) => - reader.ReadBool() ? Poison.GetPoisonByIndex(reader.ReadByte()) : null; + reader.ReadBool() ? Poison.GetPoison(reader.ReadByte()) : null; } diff --git a/Projects/Server/Serialization/SerializationThreadWorker.cs b/Projects/Server/Serialization/SerializationThreadWorker.cs index 5a9b5ed1c..d01dca470 100644 --- a/Projects/Server/Serialization/SerializationThreadWorker.cs +++ b/Projects/Server/Serialization/SerializationThreadWorker.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SerializationThreadWorker.cs * * * @@ -14,44 +14,12 @@ *************************************************************************/ using System; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Runtime.CompilerServices; using System.Threading; namespace Server; -/// -/// One contiguous run of records a worker serialized into its heap from a single chunk. -/// Together with the worker's lengths log this replaces per-entity placement state: -/// positions are implicit (a worker's writes are contiguous), and identity comes from -/// re-walking the same slots for range segments, or from the entities log for buffer -/// segments. The snapshot writer routes segments to files by . -/// -internal readonly struct SerializedSegment -{ - public readonly object Owner; // ISlotRangeSource for range segments, Persistence for buffer segments - public readonly int SlotOffset; // -1 when the segment came from a buffer chunk - public readonly int SlotCount; - public readonly long HeapStart; - public readonly int LengthsStart; - public readonly int RecordCount; - public readonly int EntitiesStart; // buffer segments only - - public SerializedSegment( - object owner, int slotOffset, int slotCount, long heapStart, int lengthsStart, int recordCount, - int entitiesStart - ) - { - Owner = owner; - SlotOffset = slotOffset; - SlotCount = slotCount; - HeapStart = heapStart; - LengthsStart = lengthsStart; - RecordCount = recordCount; - EntitiesStart = entitiesStart; - } -} - public class SerializationThreadWorker { private const int MinHeapSize = 1024 * 1024; // 1MB @@ -59,69 +27,22 @@ public class SerializationThreadWorker private readonly Thread _thread; private readonly AutoResetEvent _startEvent; // Main thread tells the thread to start working private readonly AutoResetEvent _stopEvent; // Main thread waits for the worker finish draining - private readonly SerializationChunkSource _chunkSource; - private readonly int _heapSizeHint; private bool _pause; private bool _exit; - private bool _exited; private byte[] _heap; - private long _entitiesSerialized; - private long _bytesSerialized; - // What this worker serialized where, logged during the drain and consumed by - // WriteSnapshot on the background writer thread. Cleared once the snapshot is on disk. - private readonly List _segments = []; - private readonly List _lengths = []; - private readonly List _bufferEntities = []; + private readonly ConcurrentQueue _entities; - internal List Segments => _segments; - internal List Lengths => _lengths; - internal List BufferEntities => _bufferEntities; - - /// - /// Releases the write logs after the snapshot is written so serialized entity - /// references don't linger between saves. Capacity is retained: the logs regrow to - /// the same size every save. - /// - internal void ReleaseWriteLogs() - { - _segments.Clear(); - _lengths.Clear(); - _bufferEntities.Clear(); - } - - public SerializationThreadWorker(int index, SerializationChunkSource chunkSource, int heapSizeHint = 0) - : this(index, chunkSource, heapSizeHint, inline: false) - { - } - - private SerializationThreadWorker(int index, SerializationChunkSource chunkSource, int heapSizeHint, bool inline) + public SerializationThreadWorker(int index) { _index = index; - _chunkSource = chunkSource; - _heapSizeHint = heapSizeHint; - - if (!inline) - { - _startEvent = new AutoResetEvent(false); - _stopEvent = new AutoResetEvent(false); - _thread = new Thread(Execute); - _thread.Start(this); - } + _startEvent = new AutoResetEvent(false); + _stopEvent = new AutoResetEvent(false); + _entities = new ConcurrentQueue(); + _thread = new Thread(Execute); + _thread.Start(this); } - /// - /// Creates a worker with no thread of its own. The owner drains chunks inline via - /// — used by the main thread to join the drain instead of - /// idling while the thread workers finish. - /// - public static SerializationThreadWorker CreateInline(int index, SerializationChunkSource chunkSource, int heapSizeHint = 0) => - new(index, chunkSource, heapSizeHint, inline: true); - - // Stats from the most recent save, for diagnosing load balance. - public long EntitiesSerialized => _entitiesSerialized; - public long BytesSerialized => _bytesSerialized; - public void Wake() { _startEvent.Set(); @@ -135,150 +56,55 @@ public class SerializationThreadWorker public void Exit() { - if (_exited) - { - return; - } - - _exited = true; _exit = true; Wake(); Sleep(); } - // Sized from the previous world load so the first save doesn't pay copy-on-grow during the freeze. - public void AllocateHeap() => - _heap ??= GC.AllocateUninitializedArray(Math.Max(MinHeapSize, _heapSizeHint)); + public void AllocateHeap() => _heap ??= GC.AllocateUninitializedArray(MinHeapSize); // 1MB + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Push(IGenericSerializable entity) => _entities.Enqueue(entity); [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan GetHeap(int start, int length) => _heap.AsSpan(start, length); - private long ProcessChunk(in SerializationChunkSource.Chunk chunk, BufferWriter writer) - { - if (chunk.Single != null) - { - // Self-payloads are written to their own file, so they record placement on the - // persistence itself instead of the segment logs. - var start = writer.Position; - chunk.Single.Serialize(writer); - chunk.Single.SetSelfPlacement((byte)_index, (int)start, (int)(writer.Position - start)); - return 1; - } - - if (chunk.Source != null) - { - var heapStart = writer.Position; - var lengthsStart = _lengths.Count; - var serialized = chunk.Source.SerializeRange(writer, _lengths, chunk.Offset, chunk.Count); - - if (serialized > 0) - { - _segments.Add( - new SerializedSegment(chunk.Source, chunk.Offset, chunk.Count, heapStart, lengthsStart, serialized, -1) - ); - } - - return serialized; - } - - var buffer = chunk.Buffer; - var count = chunk.Count; - - var bufferHeapStart = writer.Position; - var bufferLengthsStart = _lengths.Count; - var entitiesStart = _bufferEntities.Count; - - for (var i = 0; i < count; i++) - { - var e = buffer[i]; - var start = writer.Position; - e.Serialize(writer); - _lengths.Add((int)(writer.Position - start)); - _bufferEntities.Add(e); - } - - _segments.Add( - new SerializedSegment(chunk.Owner, -1, 0, bufferHeapStart, bufferLengthsStart, count, entitiesStart) - ); - - _chunkSource.Return(buffer, count); - return count; - } - - /// - /// Drains chunks on the calling thread until the queue is empty, then returns. - /// Only valid on inline workers; the main thread calls this after publishing all work - /// so it contributes drain throughput instead of idling. - /// - public void DrainInline() - { - ReleaseWriteLogs(); - - var writer = new BufferWriter(_heap, true); - var entities = 0L; - - while (_chunkSource.TryTake(out var chunk)) - { - entities += ProcessChunk(in chunk, writer); - } - - _heap = writer.Buffer; - _entitiesSerialized = entities; - _bytesSerialized = writer.Position; - - writer.Close(); - } - private static void Execute(object obj) { var worker = (SerializationThreadWorker)obj; + var threadIndex = (byte)worker._index; - var chunkSource = worker._chunkSource; + var queue = worker._entities; + var serializedTypes = World.SerializedTypes; while (worker._startEvent.WaitOne()) { - worker.ReleaseWriteLogs(); - - var writer = new BufferWriter(worker._heap, true); - var entities = 0L; - var spinner = new SpinWait(); + var writer = new BufferWriter(worker._heap, true, serializedTypes); while (true) { var pauseRequested = Volatile.Read(ref worker._pause); - if (chunkSource.TryTake(out var chunk)) + if (queue.TryDequeue(out var e)) { - spinner.Reset(); - entities += worker.ProcessChunk(in chunk, writer); + e.SerializedThread = threadIndex; + var start = e.SerializedPosition = (int)writer.Position; + e.Serialize(writer); + e.SerializedLength = (int)(writer.Position - start); } else if (pauseRequested) // Break when finished { break; } - else - { - // Idle backoff instead of hammering the queue head while the producer works. - // sleep1Threshold: -1 keeps escalation at Yield/Sleep(0) and never Sleep(1), - // avoiding timer-resolution stalls at the end of the drain. - spinner.SpinOnce(-1); - } } worker._heap = writer.Buffer; - worker._entitiesSerialized = entities; - worker._bytesSerialized = writer.Position; writer.Close(); - // The owning thread may start another pause cycle the moment _stopEvent is set - // (Exit does exactly that). Clear _pause and sample the exit condition before - // signaling, or the new cycle's pause request is clobbered / its Sleep orphaned. - var exiting = Core.Closing || worker._exit; - Volatile.Write(ref worker._pause, false); - worker._stopEvent.Set(); // Allow the main thread to continue now that we are finished + worker._pause = false; - if (exiting) + if (Core.Closing || worker._exit) { return; } diff --git a/Projects/Server/Serialization/ShadowDictionaryEntries.cs b/Projects/Server/Serialization/ShadowDictionaryEntries.cs deleted file mode 100644 index 170349f2a..000000000 --- a/Projects/Server/Serialization/ShadowDictionaryEntries.cs +++ /dev/null @@ -1,197 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ShadowDictionaryEntries.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Server.Logging; - -namespace Server; - -/// -/// Mirrors the field layout of Dictionary<Serial, TValue>.Entry so serialization -/// workers can scan a dictionary's backing entries array directly, in parallel, without the -/// main thread enumerating and handing off every entity. -/// The CLR's auto-layout algorithm is deterministic for identical field sequences, so this -/// struct lays out identically to the runtime's private Entry struct — and -/// proves that empirically at startup before -/// any code reads through it. If validation fails on a future runtime, callers fall back to -/// the enumerate-and-push path. -/// A free or never-used slot always has a null value (Dictionary clears values of -/// reference-type TValue on remove to release references), so occupancy is exactly -/// Value != null. -/// -internal struct ShadowEntry -{ - // Field order must mirror S.P.CoreLib Dictionary.Entry: hashCode, next, key, value - public uint HashCode; - public int Next; - public Serial Key; - public TValue Value; -} - -internal static class ShadowDictionaryEntries -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ShadowDictionaryEntries)); - - /// - /// True when this runtime's Dictionary entry layout matches , - /// proven by validation at startup. All reference-type TValue instantiations share one - /// canonical layout, so a single validation covers every entity dictionary. - /// - internal static readonly bool Supported = Validate(); - - internal static FieldInfo GetEntriesField() where TValue : class => - typeof(Dictionary).GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); - - private sealed class ValidationValue - { - public int Id; - } - - private static bool Validate() - { - try - { - var field = GetEntriesField(); - if (field == null) - { - logger.Warning("Dictionary._entries not found; parallel save iteration disabled."); - return false; - } - - // A compacting GC between snapshotting reference bits and scanning them can only - // produce a false negative, so retry a few times before falling back. - for (var attempt = 0; attempt < 3; attempt++) - { - if (RunValidationPass(field)) - { - return true; - } - } - - logger.Warning("Dictionary entry layout mismatch; parallel save iteration disabled."); - return false; - } - catch (Exception e) - { - logger.Warning(e, "Dictionary entry layout validation failed; parallel save iteration disabled."); - return false; - } - } - - /// - /// Measures the true element stride of the runtime's Entry struct by allocating a large - /// array of it and reading the precise allocated byte count. Verifying the stride first - /// guarantees every subsequent shadow read is in-bounds of the entries array even if the - /// field layout were wrong. - /// - private static bool StrideMatches(Type entryType) - { - const int probeLength = 64 * 1024; - const int arrayHeaderSize = 24; // 64-bit: sync block + method table + length + padding - - // Warm the reflection/allocation path so the measured delta contains only the probe. - Array.CreateInstance(entryType, 1); - - var before = GC.GetAllocatedBytesForCurrentThread(); - var probe = Array.CreateInstance(entryType, probeLength); - var delta = GC.GetAllocatedBytesForCurrentThread() - before; - GC.KeepAlive(probe); - - // Alignment slack is < 8 bytes on a 512KB+ allocation, so integer division is exact. - var actualStride = (delta - arrayHeaderSize) / probeLength; - - return actualStride == Unsafe.SizeOf>(); - } - - /// - /// Proves the layout without ever materializing a managed reference through the shadow - /// view: value slots are compared as raw pointer bits (nint). Only after the stride and - /// every key and value-pointer of a churned, resized, freelist-exercised dictionary match - /// is the layout trusted for typed reads. - /// - private static bool RunValidationPass(FieldInfo field) - { - const int count = 1000; - - var dict = new Dictionary(); - var rng = new System.Random(0x5EED); - var inserted = new List(count); - - // Adds with interleaved removes and re-adds: exercises resizes and freelist reuse so - // freed slots (which production skips via null values) are present in the array. - for (var i = 0; i < count; i++) - { - var serial = (Serial)(uint)rng.Next(1, int.MaxValue); - if (dict.TryAdd(serial, new ValidationValue { Id = i })) - { - inserted.Add(serial); - } - - if (i % 3 == 2) - { - var victim = inserted[rng.Next(inserted.Count)]; - if (dict.Remove(victim)) - { - inserted.Remove(victim); - } - } - } - - if (field.GetValue(dict) is not Array entriesObj || !StrideMatches(entriesObj.GetType().GetElementType()!)) - { - return false; - } - - // Snapshot expected key -> value-pointer-bits pairs before scanning, so no allocation - // happens between reading the real references and reading the shadow view. - var expected = new Dictionary(dict.Count); - foreach (var (key, value) in dict) - { - var v = value; - expected[key] = Unsafe.As(ref v); - } - - var entries = Unsafe.As[]>(entriesObj); - var length = entriesObj.Length; - var matched = 0; - - ref var entry = ref MemoryMarshal.GetArrayDataReference(entries); - - for (var i = 0; i < length; i++, entry = ref Unsafe.Add(ref entry, 1)) - { - // Read the value slot as pointer bits only — never as a reference — so a wrong - // field offset cannot fabricate a managed reference for the GC to trip over. - var bits = Unsafe.As(ref entry.Value); - - if (bits == 0) - { - continue; // free or never-used slot - } - - if (!expected.TryGetValue(entry.Key, out var expectedBits) || bits != expectedBits) - { - return false; - } - - matched++; - } - - return matched == dict.Count; - } -} diff --git a/Projects/Server/Serialization/UnmanagedDataReader.cs b/Projects/Server/Serialization/UnmanagedDataReader.cs index aa3916ee9..9babb86fd 100644 --- a/Projects/Server/Serialization/UnmanagedDataReader.cs +++ b/Projects/Server/Serialization/UnmanagedDataReader.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: UnmanagedDataReader.cs * * * @@ -43,8 +43,6 @@ public unsafe class UnmanagedDataReader : IGenericReader ///
public long Position { get; private set; } - public TimeSpan AnchoredTimeShift { get; set; } - /// /// Read bits of data raw from a serialized file using Little-endian. /// @@ -220,7 +218,7 @@ public unsafe class UnmanagedDataReader : IGenericReader public Serial ReadSerial() => (Serial)ReadUInt(); /// - /// Reads the next Byte which helps determine how to read the following Type. + /// Reads the next Byte which helps determin how to read the following Type. ///
If the byte returns 1 => and translate into a Type via the
///
If the byte returns 2 =>
///
else return null
diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index fd69557b7..443802d8a 100644 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -21,12 +21,12 @@ - - + + @@ -34,21 +34,15 @@ - - - - + + + + - - + + - - <_Parameter1>Server.Tests - - - <_Parameter1>UOContent.Tests - diff --git a/Projects/Server/Skills.cs b/Projects/Server/Skills.cs index 5b10aec52..2123d1e20 100644 --- a/Projects/Server/Skills.cs +++ b/Projects/Server/Skills.cs @@ -873,7 +873,7 @@ public class Skills [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { - var localList = _skills; + Skill[] localList = _skills; while ((uint)_index < (uint)localList.Length) { diff --git a/Projects/Server/Targeting/Target.cs b/Projects/Server/Targeting/Target.cs index f544f3cce..bf263b156 100644 --- a/Projects/Server/Targeting/Target.cs +++ b/Projects/Server/Targeting/Target.cs @@ -161,7 +161,7 @@ public abstract class Target Mobile mobile = null; var isValidTargetType = true; - var valid = targeted switch + bool valid = targeted switch { LandTarget landTarget => CanTarget(from, landTarget, ref loc, ref map), StaticTarget staticTarget => CanTarget(from, staticTarget, ref loc, ref map), diff --git a/Projects/Server/Text/HexStringConverter.cs b/Projects/Server/Text/HexStringConverter.cs index 68dd8c9f2..ba2154fdc 100644 --- a/Projects/Server/Text/HexStringConverter.cs +++ b/Projects/Server/Text/HexStringConverter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: HexStringConverter.cs * * * @@ -64,7 +64,7 @@ public static class HexStringConverter var charsWritten = 0; fixed (char* resultP = result) { - for (var i = 0; i < bytes.Length; i++) + for (int i = 0; i < bytes.Length; i++) { var resultP2 = (uint*)(resultP + charsWritten); *resultP2 = m_Lookup32Chars[bytes[i]]; diff --git a/Projects/Server/Text/ISelfInterpolatedStringHandler.cs b/Projects/Server/Text/ISelfInterpolatedStringHandler.cs index 2a72b1af5..8e6bcd17b 100644 --- a/Projects/Server/Text/ISelfInterpolatedStringHandler.cs +++ b/Projects/Server/Text/ISelfInterpolatedStringHandler.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ISelfInterpolatedStringHandler.cs * * * @@ -20,21 +20,21 @@ namespace Server.Text; public interface ISelfInterpolatedStringHandler { - void Add([InterpolatedStringHandlerArgument("")] ref InterpolatedStringHandler handler); - void InitializeInterpolation(int literalLength, int formattedCount); - void AppendLiteral(string value); - void AppendFormatted(T value); - void AppendFormatted(T value, string? format); - void AppendFormatted(T value, int alignment); - void AppendFormatted(T value, int alignment, string? format); - void AppendFormatted(ReadOnlySpan value); - void AppendFormatted(ReadOnlySpan value, int alignment, string? format = null); - void AppendFormatted(object? value, int alignment = 0, string? format = null); - void AppendFormatted(string? value); - void AppendFormatted(string? value, int alignment, string? format = null); + public void Add([InterpolatedStringHandlerArgument("")] ref InterpolatedStringHandler handler); + public void InitializeInterpolation(int literalLength, int formattedCount); + public void AppendLiteral(string value); + public void AppendFormatted(T value); + public void AppendFormatted(T value, string? format); + public void AppendFormatted(T value, int alignment); + public void AppendFormatted(T value, int alignment, string? format); + public void AppendFormatted(ReadOnlySpan value); + public void AppendFormatted(ReadOnlySpan value, int alignment, string? format = null); + public void AppendFormatted(object? value, int alignment = 0, string? format = null); + public void AppendFormatted(string? value); + public void AppendFormatted(string? value, int alignment, string? format = null); [InterpolatedStringHandler] - ref struct InterpolatedStringHandler + public ref struct InterpolatedStringHandler { private ISelfInterpolatedStringHandler _parent; diff --git a/Projects/Server/Text/InsensitiveStringHelpers.cs b/Projects/Server/Text/InsensitiveStringHelpers.cs index a1c22075f..b4dfad590 100644 --- a/Projects/Server/Text/InsensitiveStringHelpers.cs +++ b/Projects/Server/Text/InsensitiveStringHelpers.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: InsensitiveStringHelpers.cs * * * diff --git a/Projects/Server/Text/OrdinalStringHelpers.cs b/Projects/Server/Text/OrdinalStringHelpers.cs index 748782270..c1d7f5ba1 100644 --- a/Projects/Server/Text/OrdinalStringHelpers.cs +++ b/Projects/Server/Text/OrdinalStringHelpers.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OrdinalStringHelpers.cs * * * diff --git a/Projects/Server/Text/StringHelpers.cs b/Projects/Server/Text/StringHelpers.cs index 262e31199..2b9349f6c 100644 --- a/Projects/Server/Text/StringHelpers.cs +++ b/Projects/Server/Text/StringHelpers.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: StringHelpers.cs * * * @@ -83,7 +83,7 @@ public static class StringHelpers return ""; } - var chrs = STArrayPool.Shared.Rent(a.Length); + char[] chrs = STArrayPool.Shared.Rent(a.Length); var span = chrs.AsSpan(0, a.Length); a.Remove(b, comparison, span, out var size); @@ -102,7 +102,7 @@ public static class StringHelpers return value; } - var chrs = STArrayPool.Shared.Rent(value.Length); + char[] chrs = STArrayPool.Shared.Rent(value.Length); var span = chrs.AsSpan(0, value.Length); var sliced = value.AsSpan(); diff --git a/Projects/Server/Text/TextDefinition.cs b/Projects/Server/Text/TextDefinition.cs index 4951c2290..e91a9e8b5 100644 --- a/Projects/Server/Text/TextDefinition.cs +++ b/Projects/Server/Text/TextDefinition.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TextDefinition.cs * * * diff --git a/Projects/Server/Text/TextEncoding.cs b/Projects/Server/Text/TextEncoding.cs index fa2ae69c0..bf8051d71 100644 --- a/Projects/Server/Text/TextEncoding.cs +++ b/Projects/Server/Text/TextEncoding.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TextEncoding.cs * * * @@ -14,56 +14,18 @@ *************************************************************************/ using System; -using System.Buffers; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text; -using Server.Buffers; namespace Server.Text; public static class TextEncoding { - private static Encoding _utf8, _unicode, _unicodeLE; + private static Encoding m_UTF8, m_Unicode, m_UnicodeLE; - // SearchValues for invalid ASCII display bytes (C0: 0x00-0x1F, DEL: 0x7F) - // Note: Bytes >= 0x80 are invalid ASCII and become '?' when decoded, which is acceptable - private static readonly SearchValues InvalidAsciiBytes = SearchValues.Create( - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x7F - ); - - // SearchValues for invalid Latin1 bytes (C0: 0x00-0x1F, C1: 0x80-0x9F) - private static readonly SearchValues InvalidLatin1Bytes = SearchValues.Create( - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F - ); - - // SearchValues for invalid Latin1 chars (same ranges as bytes) - private static readonly SearchValues InvalidLatin1Chars = SearchValues.Create( - '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', - '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', - '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', - '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F', - '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87', - '\x88', '\x89', '\x8A', '\x8B', '\x8C', '\x8D', '\x8E', '\x8F', - '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97', - '\x98', '\x99', '\x9A', '\x9B', '\x9C', '\x9D', '\x9E', '\x9F' - ); - - public static Encoding UTF8 => _utf8 ??= new UTF8Encoding(false, false); - public static Encoding Unicode => _unicode ??= new UnicodeEncoding(true, false, false); - public static Encoding UnicodeLE => _unicodeLE ??= new UnicodeEncoding(false, false, false); - public static Encoding Latin1 => Encoding.Latin1; + public static Encoding UTF8 => m_UTF8 ??= new UTF8Encoding(false, false); + public static Encoding Unicode => m_Unicode ??= new UnicodeEncoding(true, false, false); + public static Encoding UnicodeLE => m_UnicodeLE ??= new UnicodeEncoding(false, false, false); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte[] GetBytesAscii(this string str) => GetBytes(str, Encoding.ASCII); @@ -80,9 +42,6 @@ public static class TextEncoding [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte[] GetBytesAscii(this ReadOnlySpan str) => GetBytes(str, Encoding.ASCII); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] GetBytesLatin1(this ReadOnlySpan str) => GetBytes(str, Latin1); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte[] GetBytesBigUni(this ReadOnlySpan str) => GetBytes(str, Unicode); @@ -126,12 +85,6 @@ public static class TextEncoding [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetBytesAscii(this ReadOnlySpan str, Span buffer) => Encoding.ASCII.GetBytes(str, buffer); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetBytesLatin1(this ReadOnlySpan str, Span buffer) => Latin1.GetBytes(str, buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetBytesLatin1(this string str, Span buffer) => Latin1.GetBytes(str, buffer); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetBytesBigUni(this string str, Span buffer) => Unicode.GetBytes(str, buffer); @@ -161,434 +114,33 @@ public static class TextEncoding _ => 1 }; - /// - /// Decodes an ASCII byte span to a string, filtering C0 control codes and DEL in safe mode. - /// - /// - /// For ASCII, bytes >= 0x80 are invalid and become '?' when decoded, which is acceptable for display. - /// Only C0 (0x00-0x1F) and DEL (0x7F) need explicit filtering. - /// - public static string GetStringAscii(ReadOnlySpan bytes, bool safeString = false) - { - if (!safeString) - { - return Encoding.ASCII.GetString(bytes); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsSafeChar(ushort c) => c is >= 0x20 and < 0xFFFE; - // Check bytes directly - filter at byte level, then decode once - var index = bytes.IndexOfAny(InvalidAsciiBytes); - if (index == -1) - { - return Encoding.ASCII.GetString(bytes); - } - - // Has invalid bytes, filter and decode - return FilterInvalidAsciiBytes(bytes, index); - } - - private static string FilterInvalidAsciiBytes(ReadOnlySpan bytes, int firstInvalidIndex) - { - var maxLength = bytes.Length; - - char[] rentedChars = null; - var charBuffer = maxLength <= 256 - ? stackalloc char[maxLength] - : rentedChars = STArrayPool.Shared.Rent(maxLength); - - try - { - using var sb = maxLength <= 256 - ? new ValueStringBuilder(stackalloc char[maxLength]) - : ValueStringBuilder.Create(maxLength); - - var index = firstInvalidIndex; - - while (index != -1) - { - if (index > 0) - { - // Decode segment and append - var segment = bytes[..index]; - var decoded = Encoding.ASCII.GetChars(segment, charBuffer); - sb.Append(charBuffer[..decoded]); - } - - if (index + 1 < bytes.Length) - { - bytes = bytes[(index + 1)..]; - index = bytes.IndexOfAny(InvalidAsciiBytes); - } - else - { - bytes = []; - index = -1; - } - } - - if (bytes.Length > 0) - { - var decoded = Encoding.ASCII.GetChars(bytes, charBuffer); - sb.Append(charBuffer[..decoded]); - } - - return sb.ToString(); - } - finally - { - if (rentedChars != null) - { - STArrayPool.Shared.Return(rentedChars); - } - } - } - - /// - /// Decodes a Latin1 byte span to a string, filtering C0/C1 control codes in safe mode. - /// - public static string GetStringLatin1(ReadOnlySpan bytes, bool safeString = false) - { - if (!safeString) - { - return Latin1.GetString(bytes); - } - - // Check bytes directly - Latin1 is 1:1 mapping, avoids char buffer for valid strings - var index = bytes.IndexOfAny(InvalidLatin1Bytes); - if (index == -1) - { - return Latin1.GetString(bytes); - } - - // Has invalid bytes, need to filter - now allocate char buffer - return FilterInvalidLatin1Chars(bytes, index); - } - - private static string FilterInvalidLatin1Chars(ReadOnlySpan bytes, int firstInvalidIndex) - { - var charCount = bytes.Length; - - char[] rentedChars = null; - var chars = charCount <= 256 - ? stackalloc char[charCount] - : rentedChars = STArrayPool.Shared.Rent(charCount); - - try - { - var length = Latin1.GetChars(bytes, chars); - chars = chars[..length]; - - using var sb = charCount <= 256 - ? new ValueStringBuilder(stackalloc char[charCount]) - : ValueStringBuilder.Create(charCount); - - // Start from the first invalid index we already found - var index = firstInvalidIndex; - - while (index != -1) - { - sb.Append(chars[..index]); - - if (index + 1 < chars.Length) - { - chars = chars[(index + 1)..]; - index = chars.IndexOfAny(InvalidLatin1Chars); - } - else - { - index = -1; - } - } - - if (chars.Length > 0) - { - sb.Append(chars); - } - - return sb.ToString(); - } - finally - { - if (rentedChars != null) - { - STArrayPool.Shared.Return(rentedChars); - } - } - } - - /// - /// Decodes a UTF-8 byte span to a string, filtering control codes and non-characters in safe mode. - /// - /// - /// Filters: C0 (0x00-0x1F), DEL (0x7F), C1 (0x80-0x9F), and non-characters (0xFFFD-0xFFFF). - /// Optimized to check bytes for C0/DEL first (single-byte in UTF-8), then chars for C1/non-chars. - /// - public static string GetStringUtf8(ReadOnlySpan bytes, bool safeString = false) - { - if (!safeString) - { - return UTF8.GetString(bytes); - } - - // Quick check: C0 (0x00-0x1F) and DEL (0x7F) are single-byte in UTF-8 - var hasC0OrDel = bytes.IndexOfAny(InvalidAsciiBytes) >= 0; - - var charCount = UTF8.GetMaxCharCount(bytes.Length); - - char[] rentedChars = null; - var chars = charCount <= 256 - ? stackalloc char[charCount] - : rentedChars = STArrayPool.Shared.Rent(charCount); - - try - { - var length = UTF8.GetChars(bytes, chars); - chars = chars[..length]; - - // If no C0/DEL in bytes, only need to check for C1 and non-chars - var index = hasC0OrDel - ? IndexOfInvalidUnicodeChar(chars) - : IndexOfInvalidUnicodeCharNonAscii(chars); - - if (index == -1) - { - return new string(chars); - } - - return FilterInvalidUnicodeChars(chars, index, hasC0OrDel); - } - finally - { - if (rentedChars != null) - { - STArrayPool.Shared.Return(rentedChars); - } - } - } - - /// - /// Decodes a big-endian UTF-16 byte span to a string, filtering control codes and non-characters in safe mode. - /// - public static string GetStringBigUni(ReadOnlySpan bytes, bool safeString = false) - { - if (!safeString) - { - return Unicode.GetString(bytes); - } - - var charCount = Unicode.GetMaxCharCount(bytes.Length); - - char[] rentedChars = null; - var chars = charCount <= 256 - ? stackalloc char[charCount] - : rentedChars = STArrayPool.Shared.Rent(charCount); - - try - { - var length = Unicode.GetChars(bytes, chars); - chars = chars[..length]; - - var index = IndexOfInvalidUnicodeChar(chars); - if (index == -1) - { - return new string(chars); - } - - return FilterInvalidUnicodeChars(chars, index, fullCheck: true); - } - finally - { - if (rentedChars != null) - { - STArrayPool.Shared.Return(rentedChars); - } - } - } - - /// - /// Decodes a little-endian UTF-16 byte span to a string, filtering control codes and non-characters in safe mode. - /// - /// - /// Optimized for little-endian systems using direct memory cast (no decoding overhead). - /// - public static string GetStringLittleUni(ReadOnlySpan bytes, bool safeString = false) - { - // Direct cast - UTF-16 LE bytes map directly to chars on little-endian systems - var chars = MemoryMarshal.Cast(bytes); - - if (!safeString) - { - return new string(chars); - } - - var index = IndexOfInvalidUnicodeChar(chars); - if (index == -1) - { - return new string(chars); - } - - return FilterInvalidUnicodeChars(chars, index, fullCheck: true); - } - - /// - /// Generic string decoding with filtering. Prefer encoding-specific methods for better performance. - /// public static string GetString(ReadOnlySpan span, Encoding encoding, bool safeString = false) { + string s = encoding.GetString(span); + if (!safeString) { - return encoding.GetString(span); + return s; } - var charCount = encoding.GetMaxCharCount(span.Length); + ReadOnlySpan chars = s.AsSpan(); - char[] rentedChars = null; - var chars = charCount <= 256 - ? stackalloc char[charCount] - : rentedChars = STArrayPool.Shared.Rent(charCount); + using var sb = new ValueStringBuilder(stackalloc char[256]); + var hasDoneAnyReplacements = false; - try + for (int i = 0, last = 0; i < chars.Length; i++) { - var length = encoding.GetChars(span, chars); - chars = chars[..length]; - - var index = IndexOfInvalidUnicodeChar(chars); - if (index == -1) + if (!IsSafeChar(chars[i]) && i == chars.Length - 1) { - return new string(chars); - } - - using var sb = charCount <= 256 - ? new ValueStringBuilder(stackalloc char[charCount]) - : ValueStringBuilder.Create(charCount); - - while (index != -1) - { - sb.Append(chars[..index]); - - if (index + 1 < chars.Length) - { - chars = chars[(index + 1)..]; - index = IndexOfInvalidUnicodeChar(chars); - } - else - { - index = -1; - } - } - - if (chars.Length > 0) - { - sb.Append(chars); - } - - return sb.ToString(); - } - finally - { - if (rentedChars != null) - { - STArrayPool.Shared.Return(rentedChars); - } - } - } - - /// - /// Finds the first invalid Unicode character for display. - /// Invalid: C0 (0x00-0x1F), DEL (0x7F), C1 (0x80-0x9F), non-chars (0xFFFE-0xFFFF). - /// Note: Surrogate pairs (0xD800-0xDFFF) are not filtered - proper validation requires context checking. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int IndexOfInvalidUnicodeChar(ReadOnlySpan chars) - { - // Check invalid ranges and return the minimum index found - var c0Index = chars.IndexOfAnyInRange((char)0x00, (char)0x1F); // C0 control codes - var delC1Index = chars.IndexOfAnyInRange((char)0x7F, (char)0x9F); // DEL + C1 control codes - var nonCharIndex = chars.IndexOfAnyInRange((char)0xFFFE, (char)0xFFFF); // Non-characters - - // Find minimum non-negative index - var minIndex = -1; - - if (c0Index >= 0) - { - minIndex = c0Index; - } - - if (delC1Index >= 0 && (minIndex < 0 || delC1Index < minIndex)) - { - minIndex = delC1Index; - } - - if (nonCharIndex >= 0 && (minIndex < 0 || nonCharIndex < minIndex)) - { - minIndex = nonCharIndex; - } - - return minIndex; - } - - /// - /// Finds the first invalid Unicode character, excluding C0/DEL (already checked at byte level). - /// Checks: C1 (0x80-0x9F), non-chars (0xFFFE-0xFFFF). - /// Note: Surrogate pairs (0xD800-0xDFFF) are not filtered - proper validation requires context checking. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int IndexOfInvalidUnicodeCharNonAscii(ReadOnlySpan chars) - { - // Only check C1 and non-chars (C0/DEL already verified absent at byte level) - var c1Index = chars.IndexOfAnyInRange((char)0x80, (char)0x9F); // C1 control codes - var nonCharIndex = chars.IndexOfAnyInRange((char)0xFFFE, (char)0xFFFF); // Non-characters - - if (c1Index < 0) - { - return nonCharIndex; - } - - if (nonCharIndex < 0) - { - return c1Index; - } - - return Math.Min(c1Index, nonCharIndex); - } - - /// - /// Filters invalid Unicode characters from a char span by removing them. - /// The UO client renders nothing for invalid characters, so removal is most efficient. - /// - private static string FilterInvalidUnicodeChars(ReadOnlySpan chars, int firstInvalidIndex, bool fullCheck) - { - var maxLength = chars.Length; - - using var sb = maxLength <= 256 - ? new ValueStringBuilder(stackalloc char[maxLength]) - : ValueStringBuilder.Create(maxLength); - - var index = firstInvalidIndex; - - while (index != -1) - { - sb.Append(chars[..index]); - // Skip the invalid character (don't append replacement - client renders nothing anyway) - - if (index + 1 < chars.Length) - { - chars = chars[(index + 1)..]; - index = fullCheck - ? IndexOfInvalidUnicodeChar(chars) - : IndexOfInvalidUnicodeCharNonAscii(chars); - } - else - { - chars = []; - index = -1; + hasDoneAnyReplacements = true; + sb.Append(chars.Slice(last, i - last)); + last = i + 1; // Skip the unsafe char } } - if (chars.Length > 0) - { - sb.Append(chars); - } - - return sb.ToString(); + return !hasDoneAnyReplacements ? s : sb.ToString(); } } diff --git a/Projects/Server/TileData.cs b/Projects/Server/TileData.cs index 0729cc9f2..4b1ba805b 100644 --- a/Projects/Server/TileData.cs +++ b/Projects/Server/TileData.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TileData.cs * * * @@ -300,7 +300,7 @@ public static class TileData Load(); } - internal static unsafe void Load() + private static unsafe void Load() { var filePath = Core.FindDataFile("tiledata.mul"); diff --git a/Projects/Server/TileList.cs b/Projects/Server/TileList.cs index ebd472194..440d17750 100644 --- a/Projects/Server/TileList.cs +++ b/Projects/Server/TileList.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TileList.cs * * * diff --git a/Projects/Server/TileMatrix/TileMatrix.cs b/Projects/Server/TileMatrix/TileMatrix.cs index 333cea53e..3a6a7b958 100644 --- a/Projects/Server/TileMatrix/TileMatrix.cs +++ b/Projects/Server/TileMatrix/TileMatrix.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TileMatrix.cs * * * @@ -17,7 +17,6 @@ using Server.Logging; using System; using System.Collections.Generic; using System.IO; -using System.IO.Hashing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -49,16 +48,6 @@ public class TileMatrix public FileStream DataStream { get; } public BinaryReader IndexReader { get; } - /// - /// XxHash3 fingerprint over the bytes of the three on-disk files for this map - /// (mapX.mul / .uop, staidxX.mul, staticsX.mul). The .mul format has no built-in - /// CRC and CentredSharp / UOFiddler rewrite these files in place, so this is the - /// only way to detect "client patched / mapper saved" changes that invalidate - /// downstream caches keyed on map data. Computed once at construction; never - /// changes for the lifetime of the TileMatrix. - /// - public ulong MapFilesFingerprint { get; } - public static bool Pre6000ClientSupport { get; private set; } public static void Configure() @@ -151,12 +140,6 @@ public class TileMatrix { logger.Warning("{File} was not found.", $"statics{fileIndex}.mul"); } - - // Stream all three on-disk files through XxHash3 once. Saves + restores - // each stream's position so this is invisible to the rest of the constructor - // (UOP path has already read part of MapStream by now). Cost is ~5-10ms per - // map at boot — paid once, then MapFilesFingerprint is a constant. - MapFilesFingerprint = ComputeMapFilesFingerprint(); } _emptyStaticBlock = new StaticTile[8][][]; @@ -181,27 +164,6 @@ public class TileMatrix Patch = new TileMatrixPatch(this, fileIndex); } - private ulong ComputeMapFilesFingerprint() - { - var hasher = HashUtility.CreateXxHash3(); - AppendStreamFromStart(hasher, MapStream); - AppendStreamFromStart(hasher, IndexStream); - AppendStreamFromStart(hasher, DataStream); - return hasher.GetCurrentHashAsUInt64(); - } - - private static void AppendStreamFromStart(XxHash3 hasher, FileStream stream) - { - if (stream == null) - { - return; - } - var originalPosition = stream.Position; - stream.Position = 0; - hasher.Append(stream); - stream.Position = originalPosition; - } - public StaticTile[][][] EmptyStaticBlock => _emptyStaticBlock; public void SetStaticBlock(int x, int y, StaticTile[][][] value) diff --git a/Projects/Server/TileMatrix/TileMatrixLoader.cs b/Projects/Server/TileMatrix/TileMatrixLoader.cs index ee7e2e57a..54a1095c4 100644 --- a/Projects/Server/TileMatrix/TileMatrixLoader.cs +++ b/Projects/Server/TileMatrix/TileMatrixLoader.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TileMatrixLoader.cs * * * diff --git a/Projects/Server/TileMatrix/TileMatrixPatch.cs b/Projects/Server/TileMatrix/TileMatrixPatch.cs index 37b8f6dea..623da9d41 100644 --- a/Projects/Server/TileMatrix/TileMatrixPatch.cs +++ b/Projects/Server/TileMatrix/TileMatrixPatch.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TileMatrixPatch.cs * * * diff --git a/Projects/Server/TimeZones/TimeZoneHandler.cs b/Projects/Server/TimeZones/TimeZoneHandler.cs index 713628ab7..5e56cddff 100644 --- a/Projects/Server/TimeZones/TimeZoneHandler.cs +++ b/Projects/Server/TimeZones/TimeZoneHandler.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TimeZoneHandler.cs * * * diff --git a/Projects/Server/TimeZones/TimeZoneWithRegions.cs b/Projects/Server/TimeZones/TimeZoneWithRegions.cs index 86067d7e5..490d4536a 100644 --- a/Projects/Server/TimeZones/TimeZoneWithRegions.cs +++ b/Projects/Server/TimeZones/TimeZoneWithRegions.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TimeZoneWithRegions.cs * * * diff --git a/Projects/Server/Timer/Timer.DelayCall.cs b/Projects/Server/Timer/Timer.DelayCall.cs index ca69e3e13..c5fc44037 100644 --- a/Projects/Server/Timer/Timer.DelayCall.cs +++ b/Projects/Server/Timer/Timer.DelayCall.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Timer.DelayCall.cs * * * @@ -44,7 +44,7 @@ public partial class Timer [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DelayCallTimer DelayCall(TimeSpan delay, TimeSpan interval, int count, Action callback) { - var t = new DelayCallTimer(delay, interval, count, callback); + DelayCallTimer t = new DelayCallTimer(delay, interval, count, callback); t.Start(); #if DEBUG_TIMERS t._allowFinalization = true; @@ -70,7 +70,7 @@ public partial class Timer [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback) { - var t = DelayCallTimer.GetTimer(delay, interval, count, callback); + DelayCallTimer t = DelayCallTimer.GetTimer(delay, interval, count, callback); t._returnOnDetach = true; t.Start(); @@ -98,7 +98,7 @@ public partial class Timer [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void StartTimer(TimeSpan delay, TimeSpan interval, int count, Action callback, out TimerExecutionToken token) { - var t = DelayCallTimer.GetTimer(delay, interval, count, callback); + DelayCallTimer t = DelayCallTimer.GetTimer(delay, interval, count, callback); t.Start(); #if DEBUG_TIMERS diff --git a/Projects/Server/Timer/Timer.DelayStateCall.cs b/Projects/Server/Timer/Timer.DelayStateCall.cs index 801e6f092..112627142 100644 --- a/Projects/Server/Timer/Timer.DelayStateCall.cs +++ b/Projects/Server/Timer/Timer.DelayStateCall.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Timer.DelayStateCall.cs * * * diff --git a/Projects/Server/Timer/Timer.Pool.cs b/Projects/Server/Timer/Timer.Pool.cs index e397e1cfb..3aeb8cca7 100644 --- a/Projects/Server/Timer/Timer.Pool.cs +++ b/Projects/Server/Timer/Timer.Pool.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Timer.Pool.cs * * * diff --git a/Projects/Server/Timer/Timer.TimerWheel.cs b/Projects/Server/Timer/Timer.TimerWheel.cs index 2ab3ce547..f8524a759 100644 --- a/Projects/Server/Timer/Timer.TimerWheel.cs +++ b/Projects/Server/Timer/Timer.TimerWheel.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Timer.TimerWheel.cs * * * @@ -44,22 +44,15 @@ public partial class Timer { _lastTickTurned = tickCount; - for (var i = 0; i < _rings.Length; i++) + for (int i = 0; i < _rings.Length; i++) { _rings[i] = new Timer[_ringSize]; _ringIndexes[i] = 0; } } - /// - /// Milliseconds of simulated time one wheel turn advances. - /// - public static int TickRate => _tickRate; - public static void Slice(long tickCount) { - EventLoopProfiler.WheelSlice(tickCount - _lastTickTurned); - var deltaSinceTurn = tickCount - _lastTickTurned; while (deltaSinceTurn >= _tickRate) { @@ -69,9 +62,6 @@ public partial class Timer } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long MillisecondsUntilNextTick(long tickCount) => Math.Max(0, _tickRate - (tickCount - _lastTickTurned)); - private static void Turn() { var turnNextWheel = false; diff --git a/Projects/Server/Timer/Timer.cs b/Projects/Server/Timer/Timer.cs index 66fa8be7e..dd36c7551 100644 --- a/Projects/Server/Timer/Timer.cs +++ b/Projects/Server/Timer/Timer.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Timer.cs * * * diff --git a/Projects/Server/Timer/TimerExecutionToken.cs b/Projects/Server/Timer/TimerExecutionToken.cs index 74ed1c89c..b6897bda2 100644 --- a/Projects/Server/Timer/TimerExecutionToken.cs +++ b/Projects/Server/Timer/TimerExecutionToken.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TimerExecutionToken.cs * * * diff --git a/Projects/Server/Utilities/ActivatorExtensions.cs b/Projects/Server/Utilities/ActivatorExtensions.cs index a35ce9890..96c116304 100644 --- a/Projects/Server/Utilities/ActivatorExtensions.cs +++ b/Projects/Server/Utilities/ActivatorExtensions.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ActivatorExtensions.cs * * * @@ -45,9 +45,9 @@ public static class ActivatorExtensions try { - for (var i = 0; i < ctors.Length; i++) + for (int i = 0; i < ctors.Length; i++) { - var info = ctors[i]; + ConstructorInfo info = ctors[i]; if (predicate?.Invoke(info) == false) { @@ -62,12 +62,12 @@ public static class ActivatorExtensions continue; } - var validated = true; + bool validated = true; // Check that all args match params for (var j = 0; j < parameters.Length; j++) { - var param = parameters[j]; + ParameterInfo param = parameters[j]; // All extra parameters must be optional if (j >= args.Length) @@ -124,7 +124,7 @@ public static class ActivatorExtensions var argLength = args?.Length ?? 0; var types = argLength > 0 ? new Type[argLength] : []; - for (var i = 0; i < types.Length; i++) + for (int i = 0; i < types.Length; i++) { types[i] = args![i]?.GetType(); } @@ -148,7 +148,7 @@ public static class ActivatorExtensions else { paramArgs = new object[paramCount]; - for (var i = 0; i < paramCount; i++) + for (int i = 0; i < paramCount; i++) { paramArgs[i] = i < argLength ? args![i] : Type.Missing; } diff --git a/Projects/Server/Utilities/BitMask256.cs b/Projects/Server/Utilities/BitMask256.cs deleted file mode 100644 index 52b604fb5..000000000 --- a/Projects/Server/Utilities/BitMask256.cs +++ /dev/null @@ -1,420 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BitMask256.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics.X86; - -namespace Server; - -/// -/// A 256-bit bitmask stored as 4 ulongs. -/// Useful for tracking 256 discrete states such as: -/// - Z levels in UO (-128 to 127 = 256 values) -/// - Sector positions (16x16 = 256 tiles) -/// Uses BMI2 for efficient bit selection when available. -/// -public struct BitMask256 -{ - public ulong Bits0, Bits1, Bits2, Bits3; - - /// - /// Creates a mask with all 256 bits set. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BitMask256 AllSet() => new() - { - Bits0 = ulong.MaxValue, - Bits1 = ulong.MaxValue, - Bits2 = ulong.MaxValue, - Bits3 = ulong.MaxValue - }; - - /// - /// Creates a mask with all bits cleared (default state). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BitMask256 AllClear() => default; - - /// - /// Sets a single bit at the specified index (0-255). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetBit(int index) - { - if ((uint)index >= 256) - { - return; - } - - var segment = index >> 6; - var localBit = index & 0x3F; - var mask = 1UL << localBit; - - switch (segment) - { - case 0: - { - Bits0 |= mask; break; - } - case 1: - { - Bits1 |= mask; break; - } - case 2: - { - Bits2 |= mask; break; - } - case 3: - { - Bits3 |= mask; break; - } - } - } - - /// - /// Clears a single bit at the specified index (0-255). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ClearBit(int index) - { - if ((uint)index >= 256) - { - return; - } - - var segment = index >> 6; - var localBit = index & 0x3F; - var mask = 1UL << localBit; - - switch (segment) - { - case 0: - { - Bits0 &= ~mask; break; - } - case 1: - { - Bits1 &= ~mask; break; - } - case 2: - { - Bits2 &= ~mask; break; - } - case 3: - { - Bits3 &= ~mask; break; - } - } - } - - /// - /// Gets the value of a single bit at the specified index (0-255). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool GetBit(int index) - { - if ((uint)index >= 256) - { - return false; - } - - var segment = index >> 6; - var localBit = index & 0x3F; - var mask = 1UL << localBit; - - return segment switch - { - 0 => (Bits0 & mask) != 0, - 1 => (Bits1 & mask) != 0, - 2 => (Bits2 & mask) != 0, - 3 => (Bits3 & mask) != 0, - _ => false - }; - } - - /// - /// Sets all bits in the inclusive range [start, end]. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetRange(int start, int end) - { - if (end < 0 || start > 255 || start > end) - { - return; - } - - start = Math.Max(0, start); - end = Math.Min(255, end); - - Bits0 |= CreateSegmentMask(start, end, 0); - Bits1 |= CreateSegmentMask(start, end, 64); - Bits2 |= CreateSegmentMask(start, end, 128); - Bits3 |= CreateSegmentMask(start, end, 192); - } - - /// - /// Clears all bits in the inclusive range [start, end]. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ClearRange(int start, int end) - { - if (end < 0 || start > 255 || start > end) - { - return; - } - - start = Math.Max(0, start); - end = Math.Min(255, end); - - Bits0 &= ~CreateSegmentMask(start, end, 0); - Bits1 &= ~CreateSegmentMask(start, end, 64); - Bits2 &= ~CreateSegmentMask(start, end, 128); - Bits3 &= ~CreateSegmentMask(start, end, 192); - } - - /// - /// Creates a bitmask for the portion of [start, end] that falls within a 64-bit segment. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong CreateSegmentMask(int start, int end, int segmentOffset) - { - var localStart = start - segmentOffset; - var localEnd = end - segmentOffset; - - localStart = Math.Max(0, localStart); - localEnd = Math.Min(63, localEnd); - - if (localStart > 63 || localEnd < 0 || localStart > localEnd) - { - return 0; - } - - var bitCount = localEnd - localStart + 1; - var mask = bitCount >= 64 ? ulong.MaxValue : (1UL << bitCount) - 1; - return mask << localStart; - } - - /// - /// Returns the bitwise AND of this mask with another. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly BitMask256 And(in BitMask256 other) => new() - { - Bits0 = Bits0 & other.Bits0, - Bits1 = Bits1 & other.Bits1, - Bits2 = Bits2 & other.Bits2, - Bits3 = Bits3 & other.Bits3 - }; - - /// - /// Returns the bitwise OR of this mask with another. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly BitMask256 Or(in BitMask256 other) => new() - { - Bits0 = Bits0 | other.Bits0, - Bits1 = Bits1 | other.Bits1, - Bits2 = Bits2 | other.Bits2, - Bits3 = Bits3 | other.Bits3 - }; - - /// - /// Returns the bitwise XOR of this mask with another. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly BitMask256 Xor(in BitMask256 other) => new() - { - Bits0 = Bits0 ^ other.Bits0, - Bits1 = Bits1 ^ other.Bits1, - Bits2 = Bits2 ^ other.Bits2, - Bits3 = Bits3 ^ other.Bits3 - }; - - /// - /// Returns the bitwise AND-NOT (this & ~other). - /// Clears bits in this mask that are set in the other mask. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly BitMask256 AndNot(in BitMask256 other) => new() - { - Bits0 = Bits0 & ~other.Bits0, - Bits1 = Bits1 & ~other.Bits1, - Bits2 = Bits2 & ~other.Bits2, - Bits3 = Bits3 & ~other.Bits3 - }; - - /// - /// Returns the bitwise NOT of this mask. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly BitMask256 Not() => new() - { - Bits0 = ~Bits0, - Bits1 = ~Bits1, - Bits2 = ~Bits2, - Bits3 = ~Bits3 - }; - - /// - /// Returns the total number of bits set (population count). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly int PopCount() => - BitOperations.PopCount(Bits0) + - BitOperations.PopCount(Bits1) + - BitOperations.PopCount(Bits2) + - BitOperations.PopCount(Bits3); - - /// - /// Returns true if no bits are set. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool IsEmpty() => (Bits0 | Bits1 | Bits2 | Bits3) == 0; - - /// - /// Returns true if all 256 bits are set. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool IsFull() => - Bits0 == ulong.MaxValue && - Bits1 == ulong.MaxValue && - Bits2 == ulong.MaxValue && - Bits3 == ulong.MaxValue; - - /// - /// Returns the index of the lowest set bit (0-255), or -1 if no bits are set. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly int LowestSetBit() - { - if (Bits0 != 0) - { - return BitOperations.TrailingZeroCount(Bits0); - } - - if (Bits1 != 0) - { - return 64 + BitOperations.TrailingZeroCount(Bits1); - } - - if (Bits2 != 0) - { - return 128 + BitOperations.TrailingZeroCount(Bits2); - } - - if (Bits3 != 0) - { - return 192 + BitOperations.TrailingZeroCount(Bits3); - } - - return -1; - } - - /// - /// Returns the index of the highest set bit (0-255), or -1 if no bits are set. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly int HighestSetBit() - { - if (Bits3 != 0) - { - return 255 - BitOperations.LeadingZeroCount(Bits3); - } - - if (Bits2 != 0) - { - return 191 - BitOperations.LeadingZeroCount(Bits2); - } - - if (Bits1 != 0) - { - return 127 - BitOperations.LeadingZeroCount(Bits1); - } - - if (Bits0 != 0) - { - return 63 - BitOperations.LeadingZeroCount(Bits0); - } - - return -1; - } - - /// - /// Returns the index of the Nth set bit (0-indexed), or -1 if fewer than N+1 bits are set. - /// Uses BMI2 PDEP for O(1) performance when available, otherwise O(popcount) fallback. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly int GetNthSetBit(int n) - { - if (n < 0) - { - return -1; - } - - var count0 = BitOperations.PopCount(Bits0); - if (n < count0) - { - return GetNthBitInUlong(Bits0, n); - } - - n -= count0; - - var count1 = BitOperations.PopCount(Bits1); - if (n < count1) - { - return 64 + GetNthBitInUlong(Bits1, n); - } - - n -= count1; - - var count2 = BitOperations.PopCount(Bits2); - if (n < count2) - { - return 128 + GetNthBitInUlong(Bits2, n); - } - - n -= count2; - - var count3 = BitOperations.PopCount(Bits3); - if (n < count3) - { - return 192 + GetNthBitInUlong(Bits3, n); - } - - return -1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int GetNthBitInUlong(ulong bits, int n) - { - // BMI2 PDEP: O(1) - deposits the nth selector bit into the position of the nth set bit - if (Bmi2.X64.IsSupported) - { - var deposited = Bmi2.X64.ParallelBitDeposit(1UL << n, bits); - return BitOperations.TrailingZeroCount(deposited); - } - - // Fallback: O(popcount) - clear n set bits, then find position of next one - while (n > 0 && bits != 0) - { - bits &= bits - 1; - n--; - } - - return bits == 0 ? -1 : BitOperations.TrailingZeroCount(bits); - } -} diff --git a/Projects/Server/Utilities/EntityActivatorExtensions.cs b/Projects/Server/Utilities/EntityActivatorExtensions.cs index 68a658cd1..191b96458 100644 --- a/Projects/Server/Utilities/EntityActivatorExtensions.cs +++ b/Projects/Server/Utilities/EntityActivatorExtensions.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: EntityActivatorExtensions.cs * * * diff --git a/Projects/Server/Utilities/HashUtility.cs b/Projects/Server/Utilities/HashUtility.cs index 76f61f911..9f4b6c58c 100644 --- a/Projects/Server/Utilities/HashUtility.cs +++ b/Projects/Server/Utilities/HashUtility.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: HashUtility.cs * * * @@ -14,7 +14,6 @@ *************************************************************************/ using System; -using System.IO; using System.IO.Hashing; using System.Numerics; using System.Runtime.InteropServices; @@ -51,52 +50,6 @@ public static class HashUtility return result; } - public static ulong ComputeHash64(ReadOnlySpan bytes) - { - if (bytes.Length == 0) - { - return 0; - } - - var hasher = _xxHash3 ??= new XxHash3(unchecked((long)xxHash3Seed)); - hasher.Append(bytes); - - var result = hasher.GetCurrentHashAsUInt64(); - hasher.Reset(); - - return result; - } - - /// - /// One-shot streaming hash. Reads from the stream's current position to its end and - /// returns the XxHash3 result. The caller is responsible for setting the stream - /// position before the call (and restoring it afterward, if the stream will be reused). - /// Returns 0 on a null or unreadable stream. - /// - public static ulong ComputeHash64(Stream stream) - { - if (stream == null || !stream.CanRead) - { - return 0; - } - - var hasher = _xxHash3 ??= new XxHash3(unchecked((long)xxHash3Seed)); - hasher.Append(stream); - - var result = hasher.GetCurrentHashAsUInt64(); - hasher.Reset(); - - return result; - } - - /// - /// Returns a fresh seeded with HashUtility's standard seed. - /// Use this when you need to combine multiple inputs into a single hash via - /// successive Append calls — the one-shot ComputeHash64 overloads are stateless - /// (Reset between callers) and can't compose. Caller owns the returned instance. - /// - public static XxHash3 CreateXxHash3() => new(unchecked((long)xxHash3Seed)); - public static uint ComputeHash32(ReadOnlySpan str) { if (str == ReadOnlySpan.Empty) @@ -123,10 +76,10 @@ public static class HashUtility fixed (char* src = &str.GetPinnableReference()) { uint hash1 = (5381 << 16) + 5381; - var hash2 = hash1; + uint hash2 = hash1; - var ptr = (uint*)src; - var length = str.Length; + uint* ptr = (uint*)src; + int length = str.Length; while (length > 2) { diff --git a/Projects/Server/Utilities/Html.cs b/Projects/Server/Utilities/Html.cs index 369faffbc..00b8c91cd 100644 --- a/Projects/Server/Utilities/Html.cs +++ b/Projects/Server/Utilities/Html.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Html.cs * * * @@ -14,270 +14,82 @@ *************************************************************************/ using System; -using System.Buffers; using System.Runtime.CompilerServices; +using System.Text; using Server.Buffers; -using Server.Text; namespace Server; -public enum TextAlignment : byte -{ - Left = 0, - Center = 1, - Right = 2 -} - public static class Html { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center(this string input) => Center(input.AsSpan()); + public static string Color(this string text, int color) => $"{text}"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center(this ReadOnlySpan input) => $"
{input}
"; + public static RawInterpolatedStringHandler Color(this ReadOnlySpan text, int color) => + $"{text}"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center(ref RawInterpolatedStringHandler input) - { - var str = input.Text.Center(); - input.Clear(); - return str; - } + public static string Color(this string text, int color, int size) => $"{text}"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center(this string input, int color) => Center(input.AsSpan(), color); + public static string Color(this string text, string color) => $"{text}"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center(this string input, ReadOnlySpan color) => Center(input.AsSpan(), color); + public static string Color(this string text, string color, int size) => + $"{text}"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center(this ReadOnlySpan input, int color) => - $"
{input}
"; + public static string Center(this string text) => $"
{text}
"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center(this ReadOnlySpan input, ReadOnlySpan color) => - $"
{input}
"; + public static RawInterpolatedStringHandler Center(this ReadOnlySpan text) => $"
{text}
"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center(ref RawInterpolatedStringHandler input, int color) - { - var str = input.Text.Center(color); - input.Clear(); - return str; - } + public static string Center(this string text, int color) => + $"
{text}
"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center(ref RawInterpolatedStringHandler input, ReadOnlySpan color) - { - var str = input.Text.Center(color); - input.Clear(); - return str; - } + public static RawInterpolatedStringHandler Center(this ReadOnlySpan text, int color) => + $"
{text}
"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Color(this string input, int color) => Color(input.AsSpan(), color); + public static string Center(this string text, int color, int size) => + $"
{text}
"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Color(this string input, ReadOnlySpan color) => Color(input.AsSpan(), color); + public static string Center(this string text, string color) => + $"
{text}
"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Color(this ReadOnlySpan input, int color) => $"{input}"; + public static string Center(this string text, string color, int size) => + $"
{text}
"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Color(this ReadOnlySpan input, ReadOnlySpan color) => - $"{input}"; + public static string Right(this string text) => $"{text}"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Color(ref RawInterpolatedStringHandler input, int color) - { - var str = input.Text.Color(color); - input.Clear(); - return str; - } + public static string Right(this string text, int color) => + $"{text}"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Color(ref RawInterpolatedStringHandler input, ReadOnlySpan color) - { - var str = input.Text.Color(color); - input.Clear(); - return str; - } + public static string Right(this string text, int color, int size) => + $"{text}"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right(this string input) => Right(input.AsSpan()); + public static string Right(this string text, string color) => $"{text}"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right(this ReadOnlySpan input) => $"{input}"; + public static string Right(this string text, string color, int size) => + $"{text}"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right(ref RawInterpolatedStringHandler input) - { - var str = input.Text.Right(); - input.Clear(); - return str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right(this string input, int color) => Right(input.AsSpan(), color); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right(this string input, ReadOnlySpan color) => Right(input.AsSpan(), color); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right(this ReadOnlySpan input, int color) => - $"{input}"; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right(this ReadOnlySpan input, ReadOnlySpan color) => - $"{input}"; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right(ref RawInterpolatedStringHandler input, int color) - { - var str = input.Text.Right(color); - input.Clear(); - return str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right(ref RawInterpolatedStringHandler input, ReadOnlySpan color) - { - var str = input.Text.Right(color); - input.Clear(); - return str; - } - - private static readonly SearchValues _htmlSearchValues = SearchValues.Create('<', '>', '&', '"', '\''); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string EscapeHtml(this string input) - { - if (string.IsNullOrEmpty(input)) - { - return input ?? ""; - } - - return EscapeHtml(input.AsSpan()); - } - - public static string EscapeHtml(this ReadOnlySpan input) - { - if (input.IsEmpty) - { - return string.Empty; - } - - var indexOfAny = input.IndexOfAny(_htmlSearchValues); - if (indexOfAny < 0) - { - return input.ToString(); - } - - using var builder = ValueStringBuilder.Create(input.Length * 2); - var lastIndex = 0; - - while (indexOfAny >= 0) - { - if (indexOfAny > lastIndex) - { - builder.Append(input[lastIndex..indexOfAny]); - } - - var c = input[indexOfAny]; - var replacement = c switch - { - '&' => "&", - '<' => "<", - '>' => ">", - '"' => """, - '\'' => "'" - }; - builder.Append(replacement); - - lastIndex = indexOfAny + 1; - indexOfAny = input[lastIndex..].IndexOfAny(_htmlSearchValues); - if (indexOfAny < 0) - { - break; - } - - indexOfAny += lastIndex; - } - - if (lastIndex < input.Length) - { - builder.Append(input[lastIndex..]); - } - - var result = builder.ToString(); - builder.Dispose(); - return result; - } - - public static string Build( - ReadOnlySpan text, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, - TextAlignment align = TextAlignment.Left - ) - { - var arr = STArrayPool.Shared.Rent(BuildCharCount(text, color)); - var bytesWritten = Build(text, arr.AsSpan(), color, size, fontStyle, align); - - var result = arr.AsSpan(0, bytesWritten).ToString(); - STArrayPool.Shared.Return(arr); - - return result; - } - - public static int BuildCharCount(ReadOnlySpan text, ReadOnlySpan color) => 61 + text.Length + color.Length; - - public static int Build( - ReadOnlySpan text, Span dest, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, - TextAlignment align = TextAlignment.Left - ) - { - using var builder = new ValueStringBuilder(dest); - if (align == TextAlignment.Right) - { - builder.Append(""); - } - else if (align == TextAlignment.Center) - { - builder.Append("
"); - } - - if (color.Length > 0 || size > -1 || fontStyle > 0) - { - builder.Append(" 0) - { - builder.Append($" COLOR={color}"); - } - - if (size > -1) - { - builder.Append($" SIZE={size}"); - } - - if (fontStyle > 0) - { - builder.Append($" STYLE={fontStyle}"); - } - builder.Append($">{text}"); - } - else - { - builder.Append(text); - } - - if (align == TextAlignment.Right) - { - builder.Append(""); - } - else if (align == TextAlignment.Center) - { - builder.Append("
"); - } - - return builder.Length; - } + public static string EscapeHtml(this string input) => + new StringBuilder(input.Length).Append(input) + .Replace("<", "<") + .Replace(">", ">") + .Replace("&", "&") + .Replace("\"", """) + .Replace("'", "'") + .ToString(); } diff --git a/Projects/Server/Utilities/IPAddressUtility.cs b/Projects/Server/Utilities/IPAddressUtility.cs deleted file mode 100644 index 12f81e516..000000000 --- a/Projects/Server/Utilities/IPAddressUtility.cs +++ /dev/null @@ -1,240 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: IPAddressUtility.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Buffers.Binary; -using System.Net; -using System.Net.Sockets; -using System.Numerics; - -namespace Server; - -/// -/// Low-level IPAddress conversion and parsing helpers shared by the firewall, ban channel, and -/// blocklist. All members are allocation-free (stack buffers only) so they are safe on hot accept -/// paths and inside tight parse loops. -/// -public static class IPAddressUtility -{ - // Converts an IPAddress to a UInt128 in IPv6 format. - // The IsIPv4MappedToIPv6 clause below looks redundant (the BCL only ever sets it on InterNetworkV6), - // but it guards the v4 -> UInt128 -> IPAddress round-trip, which can return a mapped v6 address for - // what is really a v4 one. - //TODO Rework as an explicit "to canonical v6 bits" step that needs no family check - // (see dev-docs/networking-packets.md, "IP Address Normalization") - public static UInt128 ToUInt128(this IPAddress ip) - { - if (ip.AddressFamily == AddressFamily.InterNetwork && !ip.IsIPv4MappedToIPv6) - { - Span integer = stackalloc byte[4]; - return !ip.TryWriteBytes(integer, out _) - ? (UInt128)0 - : new UInt128(0, 0xFFFF00000000UL | BinaryPrimitives.ReadUInt32BigEndian(integer)); - } - - Span bytes = stackalloc byte[16]; - if (!ip.TryWriteBytes(bytes, out _)) - { - return 0; - } - - var high = BinaryPrimitives.ReadUInt64BigEndian(bytes[..8]); - var low = BinaryPrimitives.ReadUInt64BigEndian(bytes.Slice(8, 8)); - - return new UInt128(high, low); - } - - // Converts a UInt128 in IPv6 format to an IPAddress - public static IPAddress ToIpAddress(this UInt128 value, bool mapToIpv6 = false) - { - // IPv4 mapped IPv6 address - if (!mapToIpv6 && value >= 0xFFFF00000000UL && value <= 0xFFFFFFFFFFFFUL) - { - var newAddress = IPAddress.HostToNetworkOrder((int)value); - return new IPAddress(unchecked((uint)newAddress)); - } - - Span bytes = stackalloc byte[16]; // 128 bits for IPv6 address - ((IBinaryInteger)value).WriteBigEndian(bytes); - - return new IPAddress(bytes); - } - - /// - /// Parses a.b.c.d/n, ::/n, or a bare address (treated as a single-host range) into an - /// inclusive range in normalized IPv6 form. A bare IPv4 prefix is widened by 96 - /// bits so v4 and v6 ranges are directly comparable. Returns false on anything malformed. - /// - public static bool TryParseCidrRange(ReadOnlySpan cidr, out UInt128 min, out UInt128 max) - { - min = default; - max = default; - - var slash = cidr.IndexOf('/'); - if (!IPAddress.TryParse(slash >= 0 ? cidr[..slash] : cidr, out var ip)) - { - return false; - } - - var isV6 = ip.AddressFamily == AddressFamily.InterNetworkV6; - var maxPrefixLength = isV6 ? 128 : 32; - int prefixLength; - - if (slash < 0) - { - prefixLength = maxPrefixLength; - } - else if (!int.TryParse(cidr[(slash + 1)..], out prefixLength) || - prefixLength < 0 || prefixLength > maxPrefixLength) - { - return false; - } - - if (!isV6) - { - prefixLength += 96; // 32 -> 128 - } - - Span bytes = stackalloc byte[16]; - ip.WriteMappedIPv6To(bytes); - - min = Utility.CreateCidrAddress(bytes, prefixLength, false); - max = Utility.CreateCidrAddress(bytes, prefixLength, true); - return true; - } - - /// Extracts the big-endian uint of an address. - public static bool TryV4(IPAddress ip, out uint v) - { - Span b = stackalloc byte[4]; - if (ip.TryWriteBytes(b, out var n) && n == 4) - { - v = ((uint)b[0] << 24) | ((uint)b[1] << 16) | ((uint)b[2] << 8) | b[3]; - return true; - } - - v = 0; - return false; - } - - /// - /// Extracts the embedded v4 uint from a v4-mapped-v6 address directly from the mapped bytes, - /// avoiding the allocation of . - /// - public static bool TryMappedV4(IPAddress ip, out uint v) - { - Span b = stackalloc byte[16]; - if (ip.TryWriteBytes(b, out var n) && n == 16) - { - v = ((uint)b[12] << 24) | ((uint)b[13] << 16) | ((uint)b[14] << 8) | b[15]; - return true; - } - - v = 0; - return false; - } - - /// Parses a dotted-quad IPv4 literal into a big-endian uint. Allocation-free, strict. - public static bool TryParseV4(ReadOnlySpan s, out uint v) - { - v = 0; - uint acc = 0; - int octet = 0, digits = 0, dots = 0; - for (var i = 0; i < s.Length; i++) - { - var c = s[i]; - if (c == '.') - { - if (digits == 0 || octet > 255) - { - return false; - } - - acc = (acc << 8) | (uint)octet; - dots++; - octet = 0; - digits = 0; - } - else if (c is >= '0' and <= '9') - { - octet = octet * 10 + (c - '0'); - if (++digits > 3) - { - return false; - } - } - else - { - return false; - } - } - - if (dots != 3 || digits == 0 || octet > 255) - { - return false; - } - - v = (acc << 8) | (uint)octet; - return true; - } - - /// - /// UTF-8/ASCII byte overload of , mirroring its - /// validation exactly so the blocklist can parse dotted-quads straight from file bytes with no - /// per-line string allocation. - /// - public static bool TryParseV4(ReadOnlySpan s, out uint v) - { - v = 0; - uint acc = 0; - int octet = 0, digits = 0, dots = 0; - for (var i = 0; i < s.Length; i++) - { - var c = s[i]; - if (c == (byte)'.') - { - if (digits == 0 || octet > 255) - { - return false; - } - - acc = (acc << 8) | (uint)octet; - dots++; - octet = 0; - digits = 0; - } - else if (c is >= (byte)'0' and <= (byte)'9') - { - octet = octet * 10 + (c - '0'); - if (++digits > 3) - { - return false; - } - } - else - { - return false; - } - } - - if (dots != 3 || digits == 0 || octet > 255) - { - return false; - } - - v = (acc << 8) | (uint)octet; - return true; - } -} diff --git a/Projects/Server/Utilities/NetworkUtilities.cs b/Projects/Server/Utilities/NetworkUtilities.cs deleted file mode 100644 index deeffb66f..000000000 --- a/Projects/Server/Utilities/NetworkUtilities.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using Server.Collections; - -namespace Server; - -public static class NetworkUtilities -{ - public static bool IsPrivateNetwork(this IPAddress ip) => - ip.AddressFamily switch - { - AddressFamily.InterNetwork => ip.IsPrivateNetworkV4(), - AddressFamily.InterNetworkV6 => ip.IsPrivateNetworkV6(), - _ => false - }; - - // These are constant reserved ranges, not firewall entries -- they only ever answer "is this address - // in one of these blocks?", which is exactly what SortedRangeIndex is for. Building them through the - // firewall entry types was a convenience that made core depend on the firewall for something that has - // nothing to do with banning. - private static readonly SortedRangeIndex _privateNetworkV4 = BuildIndex( - "127.0.0.1/8", - "192.168.0.0/16", - "10.0.0.0/8", - "172.16.0.0/12", - "169.254.0.0/16", - "100.64.0.0/10" - ); - - private static readonly SortedRangeIndex _privateNetworkV6 = BuildIndex( - "fc00::/7", - "fe80::/10" - ); - - private static SortedRangeIndex BuildIndex(params ReadOnlySpan cidrs) - { - var ranges = new SortedRangeIndex.Range[cidrs.Length]; - for (var i = 0; i < cidrs.Length; i++) - { - if (!IPAddressUtility.TryParseCidrRange(cidrs[i], out var min, out var max)) - { - throw new ArgumentException($"Invalid reserved-network CIDR \"{cidrs[i]}\""); - } - - ranges[i] = new SortedRangeIndex.Range(min, max); - } - - Array.Sort(ranges, SortedRangeIndex.ByMin); - return SortedRangeIndex.Build(ranges); - } - - public static bool IsPrivateNetworkV4(this IPAddress ip) => _privateNetworkV4.Contains(ip.ToUInt128()); - - public static bool IsPrivateNetworkV6(this IPAddress ip) => _privateNetworkV6.Contains(ip.ToUInt128()); -} diff --git a/Projects/Server/Utilities/PathUtility.cs b/Projects/Server/Utilities/PathUtility.cs index 0531745e8..11f5fa1af 100644 --- a/Projects/Server/Utilities/PathUtility.cs +++ b/Projects/Server/Utilities/PathUtility.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: PathUtility.cs * * * @@ -75,17 +75,17 @@ public static class PathUtility Directory.CreateDirectory(destDir); - foreach (var file in dir.GetFiles()) + foreach (FileInfo file in dir.GetFiles()) { - var targetFilePath = Path.Combine(destDir, file.Name); + string targetFilePath = Path.Combine(destDir, file.Name); file.CopyTo(targetFilePath, true); } if (recursive) { - foreach (var subdir in dir.GetDirectories()) + foreach (DirectoryInfo subdir in dir.GetDirectories()) { - var destSubDir = Path.Combine(destDir, subdir.Name); + string destSubDir = Path.Combine(destDir, subdir.Name); CopyDirectoryContents(subdir.FullName, destSubDir); } } @@ -102,16 +102,16 @@ public static class PathUtility Directory.CreateDirectory(destDir); - foreach (var file in dir.GetFiles()) + foreach (FileInfo file in dir.GetFiles()) { file.MoveTo(Path.Combine(destDir, file.Name)); } if (recursive) { - foreach (var subdir in dir.GetDirectories()) + foreach (DirectoryInfo subdir in dir.GetDirectories()) { - var destSubDir = Path.Combine(destDir, subdir.Name); + string destSubDir = Path.Combine(destDir, subdir.Name); MoveDirectoryContents(subdir.FullName, destSubDir); } } diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 9d7cd9f3b..61545077d 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -93,26 +93,65 @@ public static partial class Utility if (prefixLength < 64) { - var bitsToFlip = 64 - prefixLength; - var highMask = isMax ? ~0UL >> bitsToFlip : ~0UL << (bitsToFlip + 1); + int bitsToFlip = 64 - prefixLength; + ulong highMask = isMax ? ~0UL >> bitsToFlip : ~0UL << (bitsToFlip + 1); high = isMax ? high | highMask : high & highMask; low = isMax ? ~0UL : 0UL; } else { - var bitsToFlip = 128 - prefixLength; - var lowMask = isMax ? ~0UL >> (64 - bitsToFlip) : ~0UL << bitsToFlip; + int bitsToFlip = 128 - prefixLength; + ulong lowMask = isMax ? ~0UL >> (64 - bitsToFlip) : ~0UL << bitsToFlip; low = isMax ? low | lowMask : low & lowMask; } } + // Converts an IPAddress to a UInt128 in IPv6 format + public static UInt128 ToUInt128(this IPAddress ip) + { + if (ip.AddressFamily == AddressFamily.InterNetwork && !ip.IsIPv4MappedToIPv6) + { + Span integer = stackalloc byte[4]; + return !ip.TryWriteBytes(integer, out _) + ? (UInt128)0 + : new UInt128(0, 0xFFFF00000000UL | BinaryPrimitives.ReadUInt32BigEndian(integer)); + } + + Span bytes = stackalloc byte[16]; + if (!ip.TryWriteBytes(bytes, out _)) + { + return 0; + } + + ulong high = BinaryPrimitives.ReadUInt64BigEndian(bytes[..8]); + ulong low = BinaryPrimitives.ReadUInt64BigEndian(bytes.Slice(8, 8)); + + return new UInt128(high, low); + } + + // Converts a UInt128 in IPv6 format to an IPAddress + public static IPAddress ToIpAddress(this UInt128 value, bool mapToIpv6 = false) + { + // IPv4 mapped IPv6 address + if (!mapToIpv6 && value >= 0xFFFF00000000UL && value <= 0xFFFFFFFFFFFFUL) + { + var newAddress = IPAddress.HostToNetworkOrder((int)value); + return new IPAddress(unchecked((uint)newAddress)); + } + + Span bytes = stackalloc byte[16]; // 128 bits for IPv6 address + ((IBinaryInteger)value).WriteBigEndian(bytes); + + return new IPAddress(bytes); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static UInt128 CreateCidrAddress(ReadOnlySpan bytes, int prefixLength, bool isMax) { - var high = BinaryPrimitives.ReadUInt64BigEndian(bytes[..8]); - var low = BinaryPrimitives.ReadUInt64BigEndian(bytes.Slice(8, 8)); + ulong high = BinaryPrimitives.ReadUInt64BigEndian(bytes[..8]); + ulong low = BinaryPrimitives.ReadUInt64BigEndian(bytes.Slice(8, 8)); if (prefixLength < 128) { @@ -170,7 +209,7 @@ public static partial class Utility var span = chars.AsSpan(0, str.Length); str.CopyTo(span); - span.FixHtml(); + FixHtml(span); var fixedStr = span.ToString(); STArrayPool.Shared.Return(chars); @@ -204,7 +243,7 @@ public static partial class Utility if (!str.IsNullOrWhiteSpace()) { - span.FixHtml(); + FixHtml(span); } return formattable; @@ -549,7 +588,7 @@ public static partial class Utility while (amount > 0) { // Range is 2^amount exclusively, maximum of 62 bits can be used - var num = amount >= 62 + ulong num = amount >= 62 ? (ulong)BuiltInRng.NextLong() : (ulong)BuiltInRng.Next(1L << amount); @@ -573,7 +612,7 @@ public static partial class Utility while (amount > 0) { // Range is 2^amount exclusively, maximum of 62 bits can be used - var num = amount >= 62 + ulong num = amount >= 62 ? (ulong)BuiltInRng.NextLong() : (ulong)BuiltInRng.Next(1L << amount); @@ -706,7 +745,7 @@ public static partial class Utility } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T RandomList(params ReadOnlySpan list) => list.RandomElement(); + public static T RandomList(params T[] list) => list.RandomElement(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T RandomElement(this ReadOnlySpan list) => list.Length == 0 ? default : list[Random(list.Length)]; @@ -1020,7 +1059,7 @@ public static partial class Utility [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Tidy(this List list) where T : ISerializable { - for (var i = list.Count - 1; i >= 0; i--) + for (int i = list.Count - 1; i >= 0; i--) { var entry = list[i]; if (entry?.Deleted != false) @@ -1050,16 +1089,28 @@ public static partial class Utility return; } + using var queue = PooledRefQueue.Create(); foreach (var (key, value) in dictionary) { - var deleted = serializableKey - ? ((ISerializable)key).Deleted - : value == null || ((ISerializable)value).Deleted; - - if (deleted) + if (serializableKey) { - dictionary.Remove(key); + if (key == null || ((ISerializable)key).Deleted) + { + queue.Enqueue(key); + } } + else + { + if (value == null || ((ISerializable)value).Deleted) + { + queue.Enqueue(key); + } + } + } + + while (queue.Count > 0) + { + dictionary.Remove(queue.Dequeue()); } dictionary.TrimExcess(); @@ -1076,7 +1127,7 @@ public static partial class Utility [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Abs(this int value) { - var mask = value >> 31; + int mask = value >> 31; return (value + mask) ^ mask; } @@ -1254,15 +1305,15 @@ public static partial class Utility } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T[] Combine(this IList source, params ReadOnlySpan> arrays) => + public static T[] Combine(this IList source, params IList[] arrays) => source.Combine(false, arrays); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T[] CombinePooled(this IList source, params ReadOnlySpan> arrays) => + public static T[] CombinePooled(this IList source, params IList[] arrays) => source.Combine(true, arrays); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T[] Combine(this IList source, bool pooled, params ReadOnlySpan> arrays) + public static T[] Combine(this IList source, bool pooled, params IList[] arrays) { var totalLength = source.Count; foreach (var arr in arrays) @@ -1371,9 +1422,9 @@ public static partial class Utility public static bool IsNullOrWhiteSpace(this ReadOnlySpan span) => span.IsEmpty || span.IsWhiteSpace(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool InTypeList(this T obj, ReadOnlySpan types) => obj.GetType().InTypeList(types); + public static bool InTypeList(this T obj, Type[] types) => obj.GetType().InTypeList(types); - public static bool InTypeList(this Type t, ReadOnlySpan types) + public static bool InTypeList(this Type t, Type[] types) { for (var i = 0; i < types.Length; ++i) { @@ -1417,109 +1468,4 @@ public static partial class Utility table.Remove(key); table.Add(key, value); } - - public static DateTime LocalToUtc(this DateTime local, TimeZoneInfo tz) - { - if (tz.IsAmbiguousTime(local)) - { - var offsets = tz.GetAmbiguousTimeOffsets(local); - return DateTime.SpecifyKind(local - offsets[1], DateTimeKind.Utc); - } - - return DateTime.SpecifyKind(local - tz.GetUtcOffset(local), DateTimeKind.Utc); - } - - public static string FormatTimeCompact(this TimeSpan ts, bool showSeconds = false) - { - using var sb = ValueStringBuilder.Create(); - if (ts.Days >= 1) - { - sb.Append($"{ts.Days}d"); - } - - if (sb.Length > 0) - { - sb.Append($" {ts.Hours}h"); - } - else if (ts.Hours >= 1) - { - sb.Append($"{ts.Hours}h"); - } - - if (sb.Length > 0) - { - sb.Append($" {ts.Minutes}m"); - } - else if (ts.Minutes >= 1) - { - sb.Append($"{ts.Minutes}m"); - } - - if (showSeconds) - { - if (sb.Length > 0) - { - sb.Append($" {ts.Seconds}s"); - } - else if (ts.Seconds >= 1) - { - sb.Append($"{ts.Seconds}s"); - } - } - else if (sb.Length == 0) - { - sb.Append("0m"); - } - - return sb.ToString(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetDistanceToSqrt(this IEntity entity, Point2D p) => GetDistanceToSqrt(entity.Location, p); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetDistanceToSqrt(this IEntity entity, Point3D p) => GetDistanceToSqrt(entity.Location, p); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetDistanceToSqrt(this IEntity from, IEntity to) => GetDistanceToSqrt(from.Location, to.Location); - - public static double GetDistanceToSqrt(this IEntity entity, IPoint2D p) - { - var xDelta = entity.X - p.X; - var yDelta = entity.Y - p.Y; - - return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - } - - public static double GetDistanceToSqrt(this Point2D from, Point2D to) - { - var xDelta = from.m_X - to.m_X; - var yDelta = from.m_Y - to.m_Y; - - return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - } - - public static double GetDistanceToSqrt(this Point2D from, Point3D to) - { - var xDelta = from.m_X - to.m_X; - var yDelta = from.m_Y - to.m_Y; - - return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - } - - public static double GetDistanceToSqrt(this Point3D from, Point2D to) - { - var xDelta = from.m_X - to.m_X; - var yDelta = from.m_Y - to.m_Y; - - return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - } - - public static double GetDistanceToSqrt(this Point3D from, Point3D to) - { - var xDelta = from.m_X - to.m_X; - var yDelta = from.m_Y - to.m_Y; - - return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - } } diff --git a/Projects/Server/World/EntitySpan.cs b/Projects/Server/World/EntitySpan.cs index 8603f3f65..5c2c61be1 100644 --- a/Projects/Server/World/EntitySpan.cs +++ b/Projects/Server/World/EntitySpan.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: EntitySpan.cs * * * diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs index c00fc85f6..234c76d0b 100644 --- a/Projects/Server/World/World.cs +++ b/Projects/Server/World/World.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: World.cs * * * @@ -14,6 +14,7 @@ *************************************************************************/ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -43,12 +44,10 @@ public static class World private static readonly MobilePersistence _mobilePersistence = new(); private static readonly GenericEntityPersistence _guildPersistence = new("Guilds", 3, 1, 0x7FFFFFFF); - // All workers including the main thread's inline worker (last index), for heap lookups. + private static int _threadId; internal static SerializationThreadWorker[] _threadWorkers; - // How many of those own a thread (wake/sleep/exit applies only to these). - private static int _realWorkerCount; - private static readonly SerializationChunkSource _chunkSource = new(); private static readonly ManualResetEvent _diskWriteHandle = new(true); + private static readonly ConcurrentQueue _decayQueue = new(); private static string _tempSavePath; // Path to the temporary folder for the save @@ -93,21 +92,6 @@ public static class World public static string SavePath { get; private set; } public static WorldState WorldState { get; private set; } public static bool Saving => WorldState == WorldState.Saving; - - /// - /// UTC time the current or most recent world save started. Written into save indexes so - /// anchored timestamps can be re-based by the downtime at load. - /// - public static DateTime SaveStartTime { get; internal set; } - - /// - /// The anchored-time shift for the save currently being loaded: the downtime between the - /// save's start and this load. Stamped while entity indexes are read (they all carry the - /// same anchor, since the whole save shares one ) and applied - /// to every reader of that save's files — including - /// payloads, which carry no anchor of their own. Zero for saves that predate the anchor. - /// - public static TimeSpan LoadTimeShift { get; internal set; } public static bool Running => WorldState is not WorldState.Loading and not WorldState.Initial; public static bool Loading => WorldState == WorldState.Loading; @@ -125,11 +109,23 @@ public static class World [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WaitForWriteCompletion() => _diskWriteHandle.WaitOne(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void EnqueueForDecay(Item item) + { + if (WorldState != WorldState.Saving) + { + logger.Warning("Attempting to queue {Item} for decay but the world is not saving", item); + return; + } + + _decayQueue.Enqueue(item); + } + public static void Broadcast(int hue, bool ascii, string text) { - var length = OutgoingMessagePackets.GetMaxMessageLength(text.Length); + var length = OutgoingMessagePackets.GetMaxMessageLength(text); - var buffer = stackalloc byte[length].InitializePacket(); + Span buffer = stackalloc byte[length].InitializePacket(); foreach (var ns in NetState.Instances) { @@ -153,13 +149,11 @@ public static class World NetState.FlushAll(); } - public static void BroadcastStaff(string text) => BroadcastStaff(0x35, false, text); - public static void BroadcastStaff(int hue, bool ascii, string text) { - var length = OutgoingMessagePackets.GetMaxMessageLength(text.Length); + var length = OutgoingMessagePackets.GetMaxMessageLength(text); - var buffer = stackalloc byte[length].InitializePacket(); + Span buffer = stackalloc byte[length].InitializePacket(); foreach (var ns in NetState.Instances) { @@ -212,47 +206,63 @@ public static class World watch.Elapsed.TotalSeconds ); - // Create the serialization threads, plus an inline worker the main thread uses to - // join the drain after publishing work instead of idling until the workers finish. - _realWorkerCount = UseMultiThreadedSaves ? Math.Max(Environment.ProcessorCount - 1, 1) : 1; - _threadWorkers = new SerializationThreadWorker[_realWorkerCount + 1]; + // Create the serialization threads. + var threadCount = UseMultiThreadedSaves ? Math.Max(Environment.ProcessorCount - 1, 1) : 1; + _threadWorkers = new SerializationThreadWorker[threadCount]; - // The save we just loaded tells us how big the heaps need to be, so the first save - // doesn't pay copy-on-grow inside the freeze window. 25% headroom for world growth. - var heapSizeHint = (int)Math.Min(GetLoadedSaveSize() / _threadWorkers.Length * 5 / 4, 1024 * 1024 * 1024); - - for (var i = 0; i < _realWorkerCount; i++) + for (var i = 0; i < _threadWorkers.Length; i++) { - _threadWorkers[i] = new SerializationThreadWorker(i, _chunkSource, heapSizeHint); + _threadWorkers[i] = new SerializationThreadWorker(i); } - - _threadWorkers[_realWorkerCount] = - SerializationThreadWorker.CreateInline(_realWorkerCount, _chunkSource, heapSizeHint); } - private static long GetLoadedSaveSize() + private static void ProcessDecay() { - try + while (_decayQueue.TryDequeue(out var item)) { - if (!Directory.Exists(SavePath)) + if (item.OnDecay()) { - return 0; + // TODO: Add Logging + item.Delete(); } - - var total = 0L; - foreach (var file in Directory.EnumerateFiles(SavePath, "*.bin", SearchOption.AllDirectories)) - { - total += new FileInfo(file).Length; - } - - return total; - } - catch - { - return 0; } } + private static DateTime _serializationStart; + + /** + * Duplicates can be weeded out asynchronously while flushing + * If performance becomes a problem, we need to build a dual mode concurrent array. + * + ****************************************************** Proposal ****************************************************** + * The structure is initialized with a large capacity to avoid unnecessary resizing. + * Write Mode: + * - Multiple threads can add a single, or a range of elements concurrently. + * - Elements can be Peeked, but there are no guarantees. + * - To resize the internal array, replaced it with the next size up from an array pool. + * - The structure cannot be cleared in this mode. + * + * Read Mode: + * - The array can be read from multiple threads using a ref struct enumerator. + * - Elements cannot be added or reassigned. + * - Cleared by replacing the internal array with another one from the pool. + * - Note: Upon clearing, the existing array is not sent back to the pool until there are zero enumerators. + * + * Enumeration: + * - Multiple threads can enumerate while in read mode. The enumerator will Interlocked.Increment a read counter. + * - Upon dispose of the enumerator, the read counter will be lowered with an Interlocked.Decrement + * - When the read counter reaches 0, if there is a cleared array, the array is sent back to the pool zeroed. + * + * Notes: + * - Elements can never be removed. + * + * How is this different from ConcurrentQueue? + * The functionality is very similar, except the constraints allow the implementation to be done without locks. + * Since this implementation uses pooled arrays, allocations will approach zero over time. + ********************************************************************************************************************** + */ + public static ConcurrentQueue SerializedTypes { get; } = new(); + public static void Save() { if (WorldState != WorldState.Running) @@ -269,6 +279,8 @@ public static class World private static void Preserialize(object state) { + var tempPath = PathUtility.EnsureRandomPath(_tempSavePath); + try { // Allocate the heaps for the GC @@ -278,9 +290,7 @@ public static class World } WakeSerializationThreads(); - - // Execute this synchronously so we don't have a race condition - Core.LoopContext.Post(() => Core.RequestSnapshot(PathUtility.EnsureRandomPath(_tempSavePath))); + Core.RequestSnapshot(tempPath); } catch (Exception ex) { @@ -302,10 +312,6 @@ public static class World WorldState = WorldState.Saving; - // The world is frozen from here: one anchor for the whole save. Written into save - // indexes so anchored timestamps can be re-based by the downtime at load. - SaveStartTime = Core.Now; - Broadcast(0x35, true, "The world is saving, please wait."); logger.Information("Saving world"); @@ -316,15 +322,10 @@ public static class World try { - if (string.IsNullOrEmpty(snapshotPath)) - { - throw new ArgumentException("Snapshot path cannot be null or empty", nameof(snapshotPath)); - } + _serializationStart = Core.Now; Persistence.SerializeAll(); PauseSerializationThreads(); - LogWorkerBalance(); - EventSink.InvokeWorldSave(); } catch (Exception ex) @@ -352,6 +353,8 @@ public static class World } } + private static readonly HashSet _typesSet = []; + private static void WriteFiles(object state) { var snapshotPath = (string)state; @@ -360,7 +363,15 @@ public static class World var watch = Stopwatch.StartNew(); logger.Information("Writing world save snapshot"); - Persistence.WriteSnapshotAll(snapshotPath); + // Dedupe the types + while (SerializedTypes.TryDequeue(out var type)) + { + _typesSet.Add(type); + } + + Persistence.WriteSnapshotAll(snapshotPath, _typesSet); + + _typesSet.Clear(); try { @@ -384,6 +395,9 @@ public static class World BroadcastStaff(0x35, true, "Writing world save snapshot failed! Check the logs!"); } + // Clear types + SerializedTypes.Clear(); + _diskWriteHandle.Set(); Core.LoopContext.Post(FinishWorldSave); } @@ -392,85 +406,45 @@ public static class World { WorldState = WorldState.Running; Persistence.PostWorldSaveAll(); // Process decay and safety queues - MovementThrottle.ResetAllMovementTiming(); // Prevent post-save movement rejection bursts - - // The snapshot is on disk; release the per-worker write logs so serialized - // entity references don't linger between saves. - for (var i = 0; i < _threadWorkers.Length; i++) - { - _threadWorkers[i].ReleaseWriteLogs(); - } - } - - // Debug-level output only exists in DEBUG builds (see LogFactory), so Release builds - // should not pay for the stat summing and argument boxing inside the freeze at all. - [Conditional("DEBUG")] - private static void LogWorkerBalance() - { - var totalEntities = 0L; - var totalBytes = 0L; - var minBytes = long.MaxValue; - var maxBytes = 0L; - - for (var i = 0; i < _threadWorkers.Length; i++) - { - var worker = _threadWorkers[i]; - totalEntities += worker.EntitiesSerialized; - var bytes = worker.BytesSerialized; - totalBytes += bytes; - minBytes = Math.Min(minBytes, bytes); - maxBytes = Math.Max(maxBytes, bytes); - } - - logger.Debug( - "Serialized {EntityCount} entities ({ByteCount} bytes) across {WorkerCount} workers (min {MinBytes}, max {MaxBytes} bytes per worker)", - totalEntities, - totalBytes, - _threadWorkers.Length, - minBytes, - maxBytes - ); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void WakeSerializationThreads() { - for (var i = 0; i < _realWorkerCount; i++) + for (var i = 0; i < _threadWorkers.Length; i++) { _threadWorkers[i].Wake(); } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void PauseSerializationThreads() { - // Publish the partial chunk before the workers are told to finish draining. - _chunkSource.Flush(); - - // Join the drain: the main thread would otherwise idle here while workers finish. - _threadWorkers[_realWorkerCount].DrainInline(); - - for (var i = 0; i < _realWorkerCount; i++) + for (var i = 0; i < _threadWorkers.Length; i++) { _threadWorkers[i].Sleep(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void SetChunkSourceOwner(Persistence owner) => _chunkSource.SetOwner(owner); + internal static int GetThreadWorkerCount() => Math.Max(Environment.ProcessorCount - 1, 1); [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void PushToCache(IGenericSerializable e) => _chunkSource.Push(e); + internal static void ResetRoundRobin() => _threadId = 0; [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void PushSingleToCache(GenericPersistence persistence) => _chunkSource.PushSingle(persistence); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void PushSlotRangesToCache(ISlotRangeSource source, int slotCount) => - _chunkSource.PushSlotRanges(source, slotCount); + internal static void PushToCache(IGenericSerializable e) + { + _threadWorkers[_threadId++].Push(e); + if (_threadId == _threadWorkers.Length) + { + _threadId = 0; + } + } public static void ExitSerializationThreads() { - for (var i = 0; i < _realWorkerCount; i++) + for (var i = 0; i < _threadWorkers.Length; i++) { _threadWorkers[i]?.Exit(); } @@ -513,12 +487,7 @@ public static class World else { logger.Warning($"Attempted to call World.RemoveEntity with '{entity.GetType()}'. Must be a mobile or item."); - return; } - -#if TRACK_LEAKS - EntityFinalizationTracker.TrackEntity(entity); -#endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -564,9 +533,28 @@ public static class World } item.ClearProperties(); - item.UpdateDecayRegistration(); } } + + public override void Serialize() + { + foreach (var item in EntitiesBySerial.Values) + { + if (item.CanDecay() && item.LastMoved + item.DecayTime <= _serializationStart) + { + EnqueueForDecay(item); + } + + PushToCache(item); + } + } + + public override void PostWorldSave() + { + ProcessDecay(); // Run this before the safety queue + + base.PostWorldSave(); + } } private class MobilePersistence : GenericEntityPersistence diff --git a/Projects/UOContent.Tests/Fixtures/PathfindingTestFixture.cs b/Projects/UOContent.Tests/Fixtures/PathfindingTestFixture.cs deleted file mode 100644 index 02dd2bbb1..000000000 --- a/Projects/UOContent.Tests/Fixtures/PathfindingTestFixture.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Xunit; - -namespace Server.Tests.Pathfinding; - -[CollectionDefinition("Sequential Pathfinding Tests", DisableParallelization = true)] -public class PathfindingTestFixture : ICollectionFixture -{ - // Shares the single process-wide bootstrap (TestServerInitializer, in the parent - // Server.Tests namespace). The superset bootstrap already loads the UO client tile data - // and configures movement/pathfinding, so this collection needs no extra setup. - public PathfindingTestFixture() => TestServerInitializer.Initialize(); -} diff --git a/Projects/UOContent.Tests/Fixtures/ServerFixture.cs b/Projects/UOContent.Tests/Fixtures/ServerFixture.cs new file mode 100644 index 000000000..a78941054 --- /dev/null +++ b/Projects/UOContent.Tests/Fixtures/ServerFixture.cs @@ -0,0 +1,47 @@ +using System; +using System.Reflection; +using Server.Misc; + +namespace Server.Tests; + +internal class ServerFixture : IDisposable +{ + // Global setup + static ServerFixture() + { + Core.ApplicationAssembly = Assembly.GetExecutingAssembly(); + Core.LoopContext = new EventLoopContext(); + Core.Expansion = Expansion.EJ; + + // Load Configurations + ServerConfiguration.Load(true); + + // Load UOContent.dll into the type resolver + ServerConfiguration.AssemblyDirectories.Add(Core.BaseDirectory); + AssemblyHandler.LoadAssemblies(["Server.dll", "UOContent.dll"]); + + // Load Skills + SkillsInfo.Configure(); + + // Configure / Initialize + TestMapDefinitions.ConfigureTestMapDefinitions(); + + // Configure the world + World.Configure(); + + Timer.Init(0); + + // Configure Races + RaceDefinitions.Configure(); + + // Load the world + World.Load(); + + World.ExitSerializationThreads(); + } + + public void Dispose() + { + Timer.Init(0); + } +} diff --git a/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs b/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs deleted file mode 100644 index a38af9c3d..000000000 --- a/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using System.Threading; -using Server.Items; -using Server.Misc; -using Server.Movement; -using Server.PathAlgorithms; -using Server.Tests.Maps; - -namespace Server.Tests; - -/// -/// Single, process-wide ModernUO bootstrap for the UOContent test host. Mirrors Server.Tests' -/// TestServerInitializer in name and shape; kept as a separate (non-shared) copy because this -/// one loads the UOContent assembly and configures the UOContent-specific systems. Both types -/// are internal so the shared name stays scoped to each assembly. -/// -/// ModernUO bootstraps its global singletons (Core, ServerConfiguration, AssemblyHandler, -/// NetState/io-ring, World, Timer, the serialization workers, and TileData) exactly once per -/// process. is guarded to run once, and -/// must run once against the live workers. Each -/// xUnit collection gets its own fixture instance, so this guard makes the bootstrap run a -/// single time regardless of how many collection fixtures are constructed. The two stateful -/// collections use [CollectionDefinition(DisableParallelization = true)] so they never -/// overlap; pure tests still run in parallel. -/// -internal static class TestServerInitializer -{ - private static bool _initialized; - private static readonly Lock _lock = new(); - - /// - /// True if the UO client tile data was found and loaded. When false (e.g. CI, where the - /// copyrighted client files are absent), tile/map/multi-dependent tests must skip rather than - /// fail. Guard such tests with Skip.If(!TestServerInitializer.TileDataLoaded, ...). - /// - public static bool TileDataLoaded { get; private set; } - - public static void Initialize() - { - lock (_lock) - { - if (_initialized) - { - return; - } - - Core.ApplicationAssembly = Assembly.GetExecutingAssembly(); - Core.LoopContext = new EventLoopContext(); - Core.Expansion = Expansion.EJ; - - ServerConfiguration.Load(true); - ServerConfiguration.AssemblyDirectories.Add(Core.BaseDirectory); - - // Required for the pathfinding tests (real .mul tile data). Harmless for the rest. - var clientFiles = Environment.GetEnvironmentVariable("MODERNUO_TEST_DATA_DIR") - ?? @"C:\Ultima Online Classic"; - ServerConfiguration.DataDirectories.Add(clientFiles); - - AssemblyHandler.LoadAssemblies(["Server.dll", "UOContent.dll"]); - - SkillsInfo.Configure(); - - // Seed the loop clock as Main.cs does before the Configure sweep; otherwise Core.Now is - // DateTime.MinValue for the whole test host. - Core._now = DateTime.UtcNow; - - // Timer wheel must exist before NetState.Configure(), which schedules a recurring - // sweep via Timer.DelayCall (matches production ordering in Main.cs: Timer.Init runs - // before AssemblyHandler.Invoke("Configure")). - Timer.Init(0); - Server.Network.NetState.Configure(); - TestMapDefinitions.ConfigureTestMapDefinitions(); - - // TileData's static cctor short-circuits when running under xUnit - // (see Server/TileData.cs:295). Force-load via reflection so LandTable/ItemTable - // flags are populated before anything that reads TileData (MultiData, MovementImpl, - // CheckMovement). Without this, TileData.MaxItemValue is 0 at MultiData.Configure() - // time, causing every MCL tile ID to be masked to 0 and stored as ID=0 in Tiles[x][y]. - // The copyrighted client files are absent on CI; when tiledata.mul is missing we skip - // the tile/map/multi-dependent bootstrap and leave TileDataLoaded false so those tests - // skip instead of failing the whole collection from the fixture constructor. - TileDataLoaded = TryForceLoadTileData(); - - // Production runs every static Configure() via AssemblyHandler.Invoke("Configure"); - // the fixture calls a curated subset, so configure the pathfinding singleton here so - // BitmapAStarAlgorithm.Instance carries its configured MaxSearchNodes before any test - // calls Find. ServerConfiguration is already loaded above, so the setting resolves. - BitmapAStarAlgorithm.Configure(); - - if (TileDataLoaded) - { - // Multi component lists (multi.mul / MultiCollection.uop). Production invokes this via - // AssemblyHandler.Invoke("Configure"); the curated fixture subset must call it so that - // BaseMulti.Components (MultiData.GetComponents) returns real footprints instead of - // MultiComponentList.Empty. Required by the Multi pathfinding tests. Depends on the - // client files, so it only runs when tile data loaded. - MultiData.Configure(); - } - - World.Configure(); - // Registers the Accounts entity persistence; without it no test can construct an Account. - Server.Accounting.Accounts.Configure(); - RaceDefinitions.Configure(); - MovementImpl.Configure(); - PathFollower.Configure(); - World.Load(); - World.ExitSerializationThreads(); - DecayScheduler.Configure(); - Server.Engines.Spawners.SpawnerJsonSerializer.Configure(); - - if (TileDataLoaded) - { - VerifyTrammelTileDataLoaded(); - } - - _initialized = true; - } - } - - private static bool TryForceLoadTileData() - { - var tileDataPath = Core.FindDataFile("tiledata.mul", false); - if (string.IsNullOrEmpty(tileDataPath) || !File.Exists(tileDataPath)) - { - return false; - } - - var loadMethod = typeof(TileData).GetMethod( - "Load", - BindingFlags.Static | BindingFlags.NonPublic - ); - if (loadMethod == null) - { - throw new InvalidOperationException( - "TileData.Load not found via reflection — engine may have refactored." - ); - } - loadMethod.Invoke(null, null); - return true; - } - - private static void VerifyTrammelTileDataLoaded() - { - var trammel = Map.Maps[1]; - if (trammel == null) - { - throw new InvalidOperationException( - "Trammel (mapId=1) was not registered. Check TestMapDefinitions." - ); - } - - var tile = trammel.Tiles.GetLandTile(1500, 1600); - if (tile.ID == 0) - { - throw new InvalidOperationException( - $"Trammel tile data did not load — GetLandTile(1500,1600) returned ID 0. " + - $"Verify Distribution/Data/map1*.mul (or map1LegacyMUL.uop) is present at " + - $"{Path.Combine(Core.BaseDirectory, "Data")}." - ); - } - } -} diff --git a/Projects/UOContent.Tests/Fixtures/TileDataRequirement.cs b/Projects/UOContent.Tests/Fixtures/TileDataRequirement.cs deleted file mode 100644 index 7b9af1f27..000000000 --- a/Projects/UOContent.Tests/Fixtures/TileDataRequirement.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Xunit; - -namespace Server.Tests; - -/// -/// Shared guard for tests that require the copyrighted UO client tile/map data (tiledata.mul and -/// friends), which is absent on CI. Call as the first statement of a -/// [SkippableFact]/[SkippableTheory] so the test is skipped — not failed — when the -/// data was not loaded. See . -/// -internal static class TileDataRequirement -{ - public static void SkipIfMissing() => - Skip.If( - !TestServerInitializer.TileDataLoaded, - "Requires UO client tile data (tiledata.mul); absent on CI." - ); -} diff --git a/Projects/UOContent.Tests/Fixtures/UOContentFixture.cs b/Projects/UOContent.Tests/Fixtures/UOContentFixture.cs deleted file mode 100644 index d934fda57..000000000 --- a/Projects/UOContent.Tests/Fixtures/UOContentFixture.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Xunit; - -namespace Server.Tests; - -[CollectionDefinition("Sequential UOContent Tests", DisableParallelization = true)] -public class UOContentFixture : ICollectionFixture -{ - // All process-global initialization lives in TestServerInitializer and runs exactly once, - // shared across every collection fixture. Tearing down global state here is intentionally - // omitted: the world/serialization workers are initialized once and reused for the whole - // test host, so there is nothing per-collection to dispose. - public UOContentFixture() => TestServerInitializer.Initialize(); -} diff --git a/Projects/UOContent.Tests/Tests/Accounting/AccountHandlerTests.cs b/Projects/UOContent.Tests/Tests/Accounting/AccountHandlerTests.cs deleted file mode 100644 index 0520f411e..000000000 --- a/Projects/UOContent.Tests/Tests/Accounting/AccountHandlerTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Server.Misc; -using Xunit; - -namespace Server.Tests.Accounting; - -public class AccountHandlerTests -{ - [Theory] - [InlineData("", false)] // Empty username - [InlineData(" ", false)] // Single space - [InlineData(".", false)] // Single period - [InlineData("Invalid AccountSecurity.CurrentAlgorithm = _originalAlgorithm; - - [Theory] - [InlineData(PasswordProtectionAlgorithm.SHA1)] - [InlineData(PasswordProtectionAlgorithm.SHA2)] - [InlineData(PasswordProtectionAlgorithm.PBKDF2)] - [InlineData(PasswordProtectionAlgorithm.Argon2)] - public void NewAccount_CanLogIn(PasswordProtectionAlgorithm algorithm) - { - AccountSecurity.CurrentAlgorithm = algorithm; - var account = new Account($"new-{algorithm}-user", Password); - - Assert.Equal(algorithm, account.PasswordAlgorithm); - Assert.True(account.CheckPassword(Password)); - Assert.False(account.CheckPassword("wrong-password")); - } - - // SetPassword assigns PasswordAlgorithm before deriving the phrase from it. Reversed, the hash - // is salted by the outgoing algorithm's rule but stored under the incoming one, which verifies - // once and then never again. - [Theory] - [InlineData(PasswordProtectionAlgorithm.SHA1)] - [InlineData(PasswordProtectionAlgorithm.SHA2)] - [InlineData(PasswordProtectionAlgorithm.PBKDF2)] - public void UpgradingAlgorithm_DoesNotLockTheAccountOut(PasswordProtectionAlgorithm from) - { - AccountSecurity.CurrentAlgorithm = from; - var account = new Account($"upgrade-{from}-user", Password); - Assert.True(account.CheckPassword(Password)); - - AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; - - Assert.True(account.CheckPassword(Password)); - Assert.Equal(PasswordProtectionAlgorithm.Argon2, account.PasswordAlgorithm); - - // Must verify against what the rehash wrote. - Assert.True(account.CheckPassword(Password)); - Assert.False(account.CheckPassword("wrong-password")); - } - - [Fact] - public void StaleArgon2Parameters_AreRehashedOnLogin() - { - AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; - var account = new Account("stale-params-user", Password); - - // The shipping default before this change: Argon2i, m=8192, t=3, p=1. - account.Password = - "$argon2i$v=19$m=8192,t=3,p=1$LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw"; - - Assert.True(account.CheckPassword(Password)); - Assert.StartsWith("$argon2id$v=19$m=16384,t=1,p=1$", account.Password); - - // Already current: verifying again must not rewrite the hash. - var afterFirst = account.Password; - Assert.True(account.CheckPassword(Password)); - Assert.Equal(afterFirst, account.Password); - } -} diff --git a/Projects/UOContent.Tests/Tests/Accounting/PasswordWorkerTests.cs b/Projects/UOContent.Tests/Tests/Accounting/PasswordWorkerTests.cs deleted file mode 100644 index 5972e5eb9..000000000 --- a/Projects/UOContent.Tests/Tests/Accounting/PasswordWorkerTests.cs +++ /dev/null @@ -1,229 +0,0 @@ -using System; -using System.Threading; -using Server.Accounting; -using Server.Accounting.Security; -using Xunit; - -namespace Server.Tests.Accounting; - -[Collection("Sequential UOContent Tests")] -public class PasswordWorkerTests : IDisposable -{ - private const string Password = "hunter2"; - - private readonly PasswordProtectionAlgorithm _originalAlgorithm = AccountSecurity.CurrentAlgorithm; - - public PasswordWorkerTests() => AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; - - public void Dispose() => AccountSecurity.CurrentAlgorithm = _originalAlgorithm; - - private static Account CreateAccount(string username) => - Accounts.GetAccount(username) as Account ?? new Account(username, Password); - - /// - /// Enqueues work, then pumps the loop context until or the deadline. - /// - /// The context pins itself to the thread that constructed it and refuses ExecuteTasks - /// from any other. The fixture's belongs to whichever thread built the fixture, and xUnit gives - /// no guarantee that a test method runs on that thread even inside a sequential collection -- - /// so this owns one for the duration and puts the original back. Pumping the fixture's context - /// passed locally and failed on CI. - /// - private static void PumpUntil(Action enqueue, Func complete, int timeoutSeconds = 20) - { - var original = Core.LoopContext; - var owned = new EventLoopContext(); - Core.LoopContext = owned; - - try - { - enqueue(); - - var deadline = DateTime.UtcNow.AddSeconds(timeoutSeconds); - - while (!complete() && DateTime.UtcNow < deadline) - { - owned.ExecuteTasks(); - Thread.Sleep(5); - } - - // Anything that landed between the last pump and the final check. - owned.ExecuteTasks(); - } - finally - { - Core.LoopContext = original; - } - } - - private static PasswordJob JobFor(Account account, string submitted) => - new() - { - Account = account, - StoredHash = account.Password, - VerifyPhrase = account.GetVerifyPhrase(submitted), - HashPhrase = account.NeedsPasswordUpgrade() ? account.GetRehashPhrase(submitted) : null, - StoredAlgorithm = account.PasswordAlgorithm, - TargetAlgorithm = AccountSecurity.CurrentAlgorithm - }; - - /// - /// Drives the real queue rather than ComputeInline. A job with no NetState attached -- an - /// admin password change -- was being dropped by the liveness check, which read a null State as - /// a dead connection, so the change silently never happened and its callback never fired. - /// - [Fact] - public void RunsAJobThatHasNoConnectionAttached() - { - var account = CreateAccount("offloop-no-netstate-user"); - var applied = false; - - var job = new PasswordJob - { - Account = account, - HashPhrase = account.GetRehashPhrase("a-queued-password"), - TargetAlgorithm = AccountSecurity.CurrentAlgorithm, - OnComplete = (_, outcome) => applied = outcome.Hash != null - }; - - PumpUntil(() => Assert.True(PasswordWorker.TryEnqueue(job)), () => applied); - - Assert.True(applied); - Assert.True(account.CheckPassword("a-queued-password")); - } - - [Fact] - public void VerifiesTheCorrectPassword() - { - var account = CreateAccount("offloop-correct-user"); - - var outcome = PasswordWorker.ComputeInline(JobFor(account, Password)); - - Assert.True(outcome.Verified); - } - - [Fact] - public void RejectsTheWrongPassword() - { - var account = CreateAccount("offloop-wrong-user"); - - var outcome = PasswordWorker.ComputeInline(JobFor(account, "not-the-password")); - - Assert.False(outcome.Verified); - Assert.Null(outcome.Hash); - } - - [Fact] - public void ProducesNoUpgradeWhenParametersAreCurrent() - { - var account = CreateAccount("offloop-current-user"); - - var outcome = PasswordWorker.ComputeInline(JobFor(account, Password)); - - Assert.True(outcome.Verified); - Assert.Null(outcome.Hash); - } - - [Fact] - public void ProducesAnUpgradeWhenParametersAreStale() - { - var account = CreateAccount("offloop-stale-user"); - - // The shipping default before #2562: Argon2i, m=8192, t=3, p=1. - account.Password = - "$argon2i$v=19$m=8192,t=3,p=1$LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw"; - - var outcome = PasswordWorker.ComputeInline(JobFor(account, Password)); - - Assert.True(outcome.Verified); - Assert.StartsWith("$argon2id$v=19$m=16384,t=1,p=1$", outcome.Hash); - } - - [Fact] - public void ProducesNoUpgradeWhenThePasswordIsWrong() - { - var account = CreateAccount("offloop-wrong-stale-user"); - account.Password = - "$argon2i$v=19$m=8192,t=3,p=1$LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw"; - - var outcome = PasswordWorker.ComputeInline(JobFor(account, "not-the-password")); - - Assert.False(outcome.Verified); - Assert.Null(outcome.Hash); - } - - [Fact] - public void AppliesAWrite() - { - var account = CreateAccount("offloop-apply-user"); - var upgraded = Argon2PasswordProtection.Instance.EncryptPassword(Password); - - account.ApplyPasswordWrite(upgraded, PasswordProtectionAlgorithm.Argon2); - - Assert.Equal(upgraded, account.Password); - Assert.True(account.CheckPassword(Password)); - } - - /// - /// Writes apply in dispatch order, which is what makes a guard unnecessary: dispatch is on the - /// loop, one worker drains FIFO, and results return through the loop context in that same order. - /// A second worker thread would break this and would need ordering reintroduced. - /// - [Fact] - public void WritesApplyInDispatchOrder() - { - var account = CreateAccount("offloop-two-writes-user"); - var done = 0; - - PumpUntil( - () => - { - for (var i = 1; i <= 2; i++) - { - Assert.True( - PasswordWorker.TryEnqueue( - new PasswordJob - { - Account = account, - HashPhrase = account.GetRehashPhrase($"password-{i}"), - StoredAlgorithm = account.PasswordAlgorithm, - TargetAlgorithm = AccountSecurity.CurrentAlgorithm, - OnComplete = (_, _) => done++ - } - ) - ); - } - }, - () => done >= 2 - ); - - Assert.Equal(2, done); - Assert.True(account.CheckPassword("password-2")); - Assert.False(account.CheckPassword("password-1")); - } - - [Theory] - [InlineData(PasswordProtectionAlgorithm.SHA1)] - [InlineData(PasswordProtectionAlgorithm.SHA2)] - public void UsesTheUsernameSaltedPhraseForShaAccounts(PasswordProtectionAlgorithm algorithm) - { - AccountSecurity.CurrentAlgorithm = algorithm; - var account = CreateAccount($"offloop-phrase-{algorithm}-user"); - - // Verification must use the algorithm the hash was stored under... - Assert.Equal($"{account.Username}{Password}", account.GetVerifyPhrase(Password)); - - // ...and a rehash the one it is moving to. Swapping these is the #2562 lockout. - AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; - Assert.Equal(Password, account.GetRehashPhrase(Password)); - } - - [Fact] - public void UsesTheBarePasswordForArgon2Accounts() - { - var account = CreateAccount("offloop-phrase-argon2-user"); - - Assert.Equal(Password, account.GetVerifyPhrase(Password)); - Assert.Equal(Password, account.GetRehashPhrase(Password)); - } -} diff --git a/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs b/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs index a0ef97a56..7b99f1996 100644 --- a/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs +++ b/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs @@ -17,31 +17,31 @@ public class PasswordProtectionTest [InlineData(typeof(HashAlgorithmPasswordProtection), "SHA2")] public void TestValidates(Type protectionType, string algorithmType) { - IPasswordProtection passwordProtection; - if (protectionType == typeof(HashAlgorithmPasswordProtection)) - { - passwordProtection = algorithmType switch + IPasswordProtection passwordProtection; + if (protectionType == typeof(HashAlgorithmPasswordProtection)) { - "SHA1" => HashAlgorithmPasswordProtection.SHA1Instance, - "SHA2" => HashAlgorithmPasswordProtection.SHA2Instance, - _ => HashAlgorithmPasswordProtection.MD5Instance, - }; - } - else - { - passwordProtection = Activator.CreateInstance(protectionType) as IPasswordProtection; - } + passwordProtection = algorithmType switch + { + "SHA1" => HashAlgorithmPasswordProtection.SHA1Instance, + "SHA2" => HashAlgorithmPasswordProtection.SHA2Instance, + _ => HashAlgorithmPasswordProtection.MD5Instance, + }; + } + else + { + passwordProtection = Activator.CreateInstance(protectionType) as IPasswordProtection; + } - if (passwordProtection == null) - { - Assert.Fail($"{protectionType.Name} is not an IPasswordProtection."); + if (passwordProtection == null) + { + Assert.Fail($"{protectionType.Name} is not an IPasswordProtection."); + } + + var encryptedPassword = passwordProtection.EncryptPassword(plainPassword); + + Assert.True(passwordProtection.ValidatePassword(encryptedPassword, plainPassword)); } - var encryptedPassword = passwordProtection.EncryptPassword(plainPassword); - - Assert.True(passwordProtection.ValidatePassword(encryptedPassword, plainPassword)); - } - [Theory] [InlineData(typeof(Argon2PasswordProtection), null)] [InlineData(typeof(PBKDF2PasswordProtection), null)] @@ -50,113 +50,28 @@ public class PasswordProtectionTest [InlineData(typeof(HashAlgorithmPasswordProtection), "SHA2")] public void TestPasswordDoesNotValidate(Type protectionType, string algorithmType) { - IPasswordProtection passwordProtection; - if (protectionType == typeof(HashAlgorithmPasswordProtection)) - { - passwordProtection = algorithmType switch + IPasswordProtection passwordProtection; + if (protectionType == typeof(HashAlgorithmPasswordProtection)) { - "SHA1" => HashAlgorithmPasswordProtection.SHA1Instance, - "SHA2" => HashAlgorithmPasswordProtection.SHA2Instance, - _ => HashAlgorithmPasswordProtection.MD5Instance, - }; + passwordProtection = algorithmType switch + { + "SHA1" => HashAlgorithmPasswordProtection.SHA1Instance, + "SHA2" => HashAlgorithmPasswordProtection.SHA2Instance, + _ => HashAlgorithmPasswordProtection.MD5Instance, + }; + } + else + { + passwordProtection = Activator.CreateInstance(protectionType) as IPasswordProtection; + } + + if (passwordProtection == null) + { + Assert.Fail($"{protectionType.Name} is not an IPasswordProtection."); + } + + var encryptedPassword = passwordProtection.EncryptPassword(plainPassword); + + Assert.False(passwordProtection.ValidatePassword(encryptedPassword, "Not the same password")); } - else - { - passwordProtection = Activator.CreateInstance(protectionType) as IPasswordProtection; - } - - if (passwordProtection == null) - { - Assert.Fail($"{protectionType.Name} is not an IPasswordProtection."); - } - - var encryptedPassword = passwordProtection.EncryptPassword(plainPassword); - - Assert.False(passwordProtection.ValidatePassword(encryptedPassword, "Not the same password")); - } - - /// - /// Literal digests of , so the stored format cannot drift. These are - /// compared as strings against what is already in every account database -- a casing or encoding - /// change would lock out every SHA and MD5 account on the shard at once. - /// - [Theory] - [InlineData("MD5", "52284053181040AC90DBDE74A0E7FF5E")] - [InlineData("SHA1", "9AC635509803AAE2D8312BA1879289259A50C5F0")] - [InlineData( - "SHA2", - "5A727BFF8F8E08A24BDF6B0CD5065F30A1F8E0060B857BB8AFD6955BE0ACBC489DA63F19B8F4CF08D73DE4069CF4B" + - "29D94B353F31513B2FB2D9382EFE15AE975" - )] - public void HashAlgorithm_StoredFormatIsStable(string algorithmType, string expected) - { - var protection = algorithmType switch - { - "SHA1" => HashAlgorithmPasswordProtection.SHA1Instance, - "SHA2" => HashAlgorithmPasswordProtection.SHA2Instance, - _ => HashAlgorithmPasswordProtection.MD5Instance, - }; - - Assert.Equal(expected, protection.EncryptPassword(plainPassword)); - Assert.True(protection.ValidatePassword(expected, plainPassword)); - } - - // The shipping default before this change, as a literal so it cannot drift with the configured - // defaults. Password: "hunter2". - private const string LegacyArgon2iHash = - "$argon2i$v=19$m=8192,t=3,p=1$LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw"; - - [Fact] - public void Argon2_ValidatesLegacyArgon2iHash() - { - Assert.True(Argon2PasswordProtection.Instance.ValidatePassword(LegacyArgon2iHash, "hunter2")); - Assert.False(Argon2PasswordProtection.Instance.ValidatePassword(LegacyArgon2iHash, "wrong")); - } - - [Theory] - // type, memory, time, parallelism -> expected NeedsRehash - [InlineData("argon2id", 16384, 1, 1, false)] // current defaults - [InlineData("argon2i", 8192, 3, 1, true)] // the old shipping default - [InlineData("argon2id", 8192, 1, 1, true)] // right type, stale memory - [InlineData("argon2id", 16384, 3, 1, true)] // right type, stale iterations - [InlineData("argon2id", 16384, 1, 2, true)] // right type, stale parallelism - [InlineData("argon2i", 16384, 1, 1, true)] // right cost, stale type - public void Argon2_NeedsRehash_ComparesTypeAndCost( - string type, int memory, int time, int parallelism, bool expected - ) - { - var hash = $"${type}$v=19$m={memory},t={time},p={parallelism}$" + - "LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw"; - - Assert.Equal(expected, Argon2PasswordProtection.Instance.NeedsRehash(hash)); - } - - // Digest and salt lengths are decoded base64 sizes rather than parameter-list entries, so they - // need their own literals. Current type and cost throughout; only a length differs. - [Theory] - // 16-byte digest: 22 base64 chars instead of the 43 a 32-byte digest encodes to. - [InlineData("$argon2id$v=19$m=16384,t=1,p=1$LD1XJz7P3wQmIJ+Tu6ScgA$NO5hBABsHQ172C5nDO2X4g")] - // 8-byte salt: 11 base64 chars instead of the 22 a 16-byte salt encodes to. - [InlineData("$argon2id$v=19$m=16384,t=1,p=1$LD1XJz7P3wQ$NO5hBABsHQ172C5nDO2X4gWnB4jDef3x6WhLdVE2LFw")] - public void Argon2_NeedsRehash_ComparesSaltAndDigestLengths(string hash) - { - Assert.True(Argon2PasswordProtection.Instance.NeedsRehash(hash)); - } - - [Theory] - [InlineData("")] - [InlineData("not-a-hash")] - public void Argon2_NeedsRehash_IsTrueForUnparseableHashes(string hash) - { - Assert.True(Argon2PasswordProtection.Instance.NeedsRehash(hash)); - } - - [Fact] - public void NonArgon2Protections_NeverNeedRehash() - { - Assert.False(PBKDF2PasswordProtection.Instance.NeedsRehash("anything")); - Assert.False(HashAlgorithmPasswordProtection.SHA2Instance.NeedsRehash("anything")); - Assert.False(HashAlgorithmPasswordProtection.SHA1Instance.NeedsRehash("anything")); - Assert.False(HashAlgorithmPasswordProtection.MD5Instance.NeedsRehash("anything")); - } -} +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchPagingTests.cs b/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchPagingTests.cs deleted file mode 100644 index 5166124e5..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchPagingTests.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Server.Engines.AdvancedSearch; -using Xunit; - -namespace UOContent.Tests; - -public class AdvancedSearchPagingTests -{ - [Theory] - [InlineData(20, 0, 18, 18)] // full first page - [InlineData(20, 18, 18, 2)] // partial last page -> 2 visible (bug rendered 0 in descending) - [InlineData(5, 0, 18, 5)] - [InlineData(0, 0, 18, 0)] - public void VisibleCount_IsCorrect(int total, int from, int max, int expected) - { - Assert.Equal(expected, AdvancedSearchGump.VisibleCount(total, from, max)); - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchTypesTests.cs b/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchTypesTests.cs deleted file mode 100644 index ea14f3cf2..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchTypesTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Server; -using Server.Engines.AdvancedSearch; -using Xunit; - -namespace UOContent.Tests; - -[Collection("Sequential UOContent Tests")] -public class AdvancedSearchTypesTests -{ - [Fact] - public void CompareValues_Poison_ReferenceTypeParsedViaTypes() - { - PoisonKinds.Configure(); // idempotent; registers Lesser..Lethal now that Core.Expansion is set - - // Poison is a reference type implementing ISpanParsable; it can't use the compile-time span - // path and routes through the shared Server.Types converter. Poison.Parse returns the - // registered singleton, so "= Lethal" is a reference-equality match — this is the case that - // previously compared a Poison against the raw string and always failed. - var prop = Poison.Lethal; - Assert.True(AdvancedSearchUtilities.CompareValues(typeof(Poison), prop, "Lethal", "=")); - Assert.False(AdvancedSearchUtilities.CompareValues(typeof(Poison), prop, "Lesser", "=")); - - var ex = Record.Exception(() => - Assert.False(AdvancedSearchUtilities.CompareValues(typeof(Poison), prop, "notapoison", "="))); - Assert.Null(ex); - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchUtilitiesTests.cs b/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchUtilitiesTests.cs deleted file mode 100644 index 9176fae4a..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchUtilitiesTests.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using Server; -using Server.Engines.AdvancedSearch; -using Xunit; - -namespace UOContent.Tests; - -public class AdvancedSearchUtilitiesTests -{ - [Theory] - [InlineData("abc")] // not a number -> was FormatException - [InlineData("99999999999")] // overflows int -> was OverflowException - [InlineData("0xZZ")] // bad hex -> was FormatException - public void CompareValues_BadNumeric_ReturnsFalse_DoesNotThrow(string value) - { - var ex = Record.Exception(() => - { - var result = AdvancedSearchUtilities.CompareValues(typeof(int), 5, value, ">"); - Assert.False(result); - }); - Assert.Null(ex); - } - - [Theory] - [InlineData("Bogus")] // not a member -> was ArgumentException - [InlineData("onehandedxyz")] // not a member, even case-insensitively -> was ArgumentException - public void CompareValues_BadEnum_ReturnsFalse_DoesNotThrow(string value) - { - var ex = Record.Exception(() => - { - var result = AdvancedSearchUtilities.CompareValues(typeof(Layer), (byte)Layer.OneHanded, value, "="); - Assert.False(result); - }); - Assert.Null(ex); - } - - [Fact] - public void CompareValues_ValidEnum_IgnoresCase() - { - Assert.True(AdvancedSearchUtilities.CompareValues(typeof(Layer), (byte)Layer.OneHanded, "onehanded", "=")); - } - - [Theory] - // leaf value is "T"/"F"; evalLeaf returns leaf=="T" - [InlineData("T", true)] - [InlineData("F", false)] - [InlineData("F@F|T", true)] // (F&&F)||T = T (buggy code gave F&&(F||T)=F) - [InlineData("T|F@F", true)] // T||(F&&F) = T (buggy code gave (T||F)&&F=F) - [InlineData("T@F", false)] - [InlineData("T@T", true)] - [InlineData("F|F", false)] - public void EvaluateBoolean_Precedence(string expr, bool expected) - { - // State is unused here; the leaf evaluator just checks the span equals "T". - var result = AdvancedSearchUtilities.EvaluateBoolean(expr, 0, static (_, leaf) => leaf.SequenceEqual("T")); - Assert.Equal(expected, result); - } - - [Fact] - public void CompareValues_ReferenceType_EqualityByString_NoThrow() - { - // A reference-typed property (e.g. RootParent name-ish) compared with "=" should not throw, - // and ordering operators must return false rather than throwing. - var ex = Record.Exception(() => - { - Assert.False(AdvancedSearchUtilities.CompareValues(typeof(object), new object(), "whatever", ">")); - }); - Assert.Null(ex); - } - - [Fact] - public void CompareValues_TimeSpan_ParsesViaSpanParsable() - { - // TimeSpan is not IConvertible, so the old Convert.ChangeType fallback threw and silently - // returned no-match. ISpanParsable parses it correctly. - var prop = TimeSpan.FromMinutes(5); - Assert.True(AdvancedSearchUtilities.CompareValues(typeof(TimeSpan), prop, "00:05:00", "=")); - Assert.False(AdvancedSearchUtilities.CompareValues(typeof(TimeSpan), prop, "00:10:00", "=")); - Assert.True(AdvancedSearchUtilities.CompareValues(typeof(TimeSpan), prop, "00:01:00", ">")); - } - - [Fact] - public void CompareValues_TimeSpan_BadInput_ReturnsFalse_NoThrow() - { - var ex = Record.Exception(() => - Assert.False(AdvancedSearchUtilities.CompareValues(typeof(TimeSpan), TimeSpan.Zero, "notaspan", "="))); - Assert.Null(ex); - } - - [Fact] - public void CompareValues_Guid_ValueTypeParsedViaTypes() - { - // Guid is a value type not named by the hot paths; it's parsed via Types (IParsable) and - // compared by value. - var g = Guid.Parse("00000000-0000-0000-0000-000000000001"); - Assert.True(AdvancedSearchUtilities.CompareValues(typeof(Guid), g, "00000000-0000-0000-0000-000000000001", "=")); - Assert.False(AdvancedSearchUtilities.CompareValues(typeof(Guid), g, "00000000-0000-0000-0000-000000000002", "=")); - } - - // A reference type with a legacy RunUO-style static Parse(string) and NO IParsable<> interface — - // the Faction/Town shape. Types must still discover its Parse by reflection. - private sealed class LegacyParseType - { - public string Value { get; private init; } - public static LegacyParseType Parse(string s) => new() { Value = s }; - public override bool Equals(object obj) => obj is LegacyParseType o && o.Value == Value; - public override int GetHashCode() => Value?.GetHashCode() ?? 0; - } - - [Fact] - public void CompareValues_LegacyParseString_ParsedViaTypes() - { - // Pre-IParsable types (only a static Parse(string)) must still be searchable: Types binds the - // legacy Parse by reflection, so we compare against a real parsed instance, not the raw text. - var prop = LegacyParseType.Parse("alpha"); - Assert.True(AdvancedSearchUtilities.CompareValues(typeof(LegacyParseType), prop, "alpha", "=")); - Assert.False(AdvancedSearchUtilities.CompareValues(typeof(LegacyParseType), prop, "beta", "=")); - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchWorkerTests.cs b/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchWorkerTests.cs deleted file mode 100644 index 2bfe94af8..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchWorkerTests.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Concurrent; -using Server; -using Server.Engines.AdvancedSearch; -using Server.Items; -using Server.Tests; -using Xunit; - -namespace UOContent.Tests; - -[Collection("Sequential UOContent Tests")] -public class AdvancedSearchWorkerTests -{ - // An item whose property test path will throw when evaluated. - private sealed class ThrowingItem : Item - { - public ThrowingItem() : base(0x1) { } - public ThrowingItem(Serial s) : base(s) { } - public string Boom => throw new InvalidOperationException("boom"); - } - - [Fact] - public void Worker_FilterThrows_DoesNotEscape_ReturnsNoMatch() - { - var worker = new AdvancedSearchThreadWorker(); - var results = new ConcurrentQueue(); - var ignore = new ConcurrentQueue(); - var filter = new AdvancedSearchFilter - { - FilterPropertyTest = true, - PropertyTest = "Boom=1", // reflection GetValue -> throws - }; - - var item = new ThrowingItem(); - - try - { - worker.Wake(new WorldLocation(Point3D.Zero, Map.Felucca), filter, results, ignore); - worker.Push(item); - worker.Sleep(); // drains; must not crash the test process - - Assert.Empty(results); - } - finally - { - item.Delete(); - worker.Exit(); - } - } - - [Fact] - public void Worker_DeletedEntity_IsSkipped() - { - var worker = new AdvancedSearchThreadWorker(); - var results = new ConcurrentQueue(); - var ignore = new ConcurrentQueue(); - var filter = new AdvancedSearchFilter(); // no filters -> everything matches - - var item = new Item(0x1); - item.Delete(); - - try - { - worker.Wake(new WorldLocation(Point3D.Zero, Map.Felucca), filter, results, ignore); - worker.Push(item); - worker.Sleep(); - - Assert.Empty(results); - } - finally - { - worker.Exit(); - } - } - - [Fact] - public void DoSearch_IsGuarded_AgainstReentry() - { - // White-box: flip the guard, assert a second entry is rejected, then clear. - // _searchInProgress is process-global static state; release it in finally so a - // failed assert here can't leak the guard into other tests. - Assert.False(AdvancedSearchGump.IsSearchInProgress); - Assert.True(AdvancedSearchGump.TryBeginSearch()); // acquires - try - { - Assert.False(AdvancedSearchGump.TryBeginSearch()); // rejected - } - finally - { - AdvancedSearchGump.EndSearch(); // releases - } - - Assert.False(AdvancedSearchGump.IsSearchInProgress); - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Chat/ChatPacketTests.cs b/Projects/UOContent.Tests/Tests/Engines/Chat/ChatPacketTests.cs index a3fc311d0..7ecb91c62 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Chat/ChatPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Chat/ChatPacketTests.cs @@ -5,7 +5,6 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] public class ChatPacketTests { [Theory] @@ -13,12 +12,12 @@ public class ChatPacketTests [InlineData("ENU", 200, "a third param", "another param")] public void TestSendChatMessage(string lang, int number, string param1, string param2) { - var expected = new ChatMessagePacket(lang, number, param1, param2).Compile(); + var expected = new ChatMessagePacket(lang, number, param1, param2).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendChatMessage(lang, number, param1, param2); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendChatMessage(lang, number, param1, param2); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Engines/Chat/Packets.cs b/Projects/UOContent.Tests/Tests/Engines/Chat/Packets.cs index 5cd71285e..58c872652 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Chat/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Chat/Packets.cs @@ -6,23 +6,23 @@ public sealed class ChatMessagePacket : Packet { public ChatMessagePacket(string lang, int number, string param1, string param2) : base(0xB2) { - param1 ??= string.Empty; - param2 ??= string.Empty; + param1 ??= string.Empty; + param2 ??= string.Empty; - EnsureCapacity(13 + (param1.Length + param2.Length) * 2); + EnsureCapacity(13 + (param1.Length + param2.Length) * 2); - Stream.Write((ushort)(number - 20)); + Stream.Write((ushort)(number - 20)); - if (lang != null) - { - Stream.WriteAsciiFixed(lang, 4); + if (lang != null) + { + Stream.WriteAsciiFixed(lang, 4); + } + else + { + Stream.Write(0); + } + + Stream.WriteBigUniNull(param1); + Stream.WriteBigUniNull(param2); } - else - { - Stream.Write(0); - } - - Stream.WriteBigUniNull(param1); - Stream.WriteBigUniNull(param2); - } -} +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Engines/Events/EventSchedulerTests.cs b/Projects/UOContent.Tests/Tests/Engines/Events/EventSchedulerTests.cs deleted file mode 100644 index addfea7d0..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Events/EventSchedulerTests.cs +++ /dev/null @@ -1,313 +0,0 @@ -using System; -using System.Collections.Generic; -using Server; -using Server.Engines.Events; -using Xunit; - -namespace UOContent.Tests; - -[Collection("Sequential UOContent Tests")] -public class EventSchedulerTests -{ - // Test implementations for controlled testing - private class TestRecurrencePattern : IRecurrencePattern - { - private readonly TimeSpan _interval; - private readonly int _maxOccurrences; - private int _currentOccurrence; - - public TestRecurrencePattern(TimeSpan interval, int maxOccurrences = int.MaxValue) - { - _interval = interval; - _maxOccurrences = maxOccurrences; - _currentOccurrence = 0; - } - - public DateTime GetNextOccurrence(DateTime afterUtc, TimeOnly time, TimeZoneInfo timeZone) - { - _currentOccurrence++; - - if (_currentOccurrence > _maxOccurrences) - { - return DateTime.MaxValue; - } - - return afterUtc + _interval; - } - } - - private class TestScheduledEvent : ScheduledEvent - { - public int CallCount { get; private set; } - public Action Callback { get; } - - public TestScheduledEvent(TimeOnly time, Action callback, IRecurrencePattern recurrence = null) - : base(time, recurrence) - { - CallCount = 0; - Callback = callback; - } - - public override void OnEvent() - { - CallCount++; - Callback?.Invoke(); - } - } - - private static void Init() - { - Core._now = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc); - Timer.Init(0); - EventScheduler.Configure(); - } - - private static void Finish() - { - EventScheduler.Shared.Stop(); - } - - [Fact] - public void ScheduleEvent_ExecutesCallback_HappyPath() - { - Init(); - - try - { - var called = false; - var evt = EventScheduler.Shared.ScheduleEvent( - Core._now, - TimeOnly.FromDateTime(Core._now), - () => called = true - ); - - Assert.Equal(Core._now, evt.NextOccurrence); - Timer.Slice(8); - - Assert.True(called); - Assert.Equal(DateTime.MaxValue, evt.NextOccurrence); - - evt.Cancel(); - } - finally - { - Finish(); - } - } - - [Fact] - public void Scheduler_OrdersEventsByNextOccurrence() - { - Init(); - - try - { - var executionOrder = new List(); - - // Create events with staggered occurrences - var evt1Start = Core._now.AddSeconds(10); - var evt1 = new TestScheduledEvent( - TimeOnly.FromDateTime(Core._now.AddSeconds(10)), - () => executionOrder.Add(1) - ); - - var evt2Start = Core._now.AddSeconds(20); - var evt2 = new TestScheduledEvent( - TimeOnly.FromDateTime(evt2Start), - () => executionOrder.Add(2) - ); - - var evt3Start = Core._now.AddSeconds(30); - var evt3 = new TestScheduledEvent( - TimeOnly.FromDateTime(evt3Start), - () => executionOrder.Add(3) - ); - - // Add out of order - evt3.Schedule(evt3Start); - evt1.Schedule(evt1Start); - evt2.Schedule(evt2Start); - - // Advance time to after all events - Core._now = Core._now.AddSeconds(40); - Timer.Slice(8); - - // Verify they executed in time order, not addition order - Assert.Equal([1, 2, 3], executionOrder); - - evt1.Cancel(); - evt2.Cancel(); - evt3.Cancel(); - } - finally - { - Finish(); - } - } - - [Fact] - public void Scheduler_HandlesRecurringEvents() - { - for (var i = 0; i < 100_000; i++) - { - Init(); - - try - { - var callCount = 0; - - // Create a recurrence pattern that fires every 10 seconds, up to 3 times - var recurrence = new TestRecurrencePattern(TimeSpan.FromSeconds(10), 3); - - var evt = new TestScheduledEvent( - TimeOnly.FromDateTime(Core._now), - () => callCount++, - recurrence - ); - - evt.Schedule(Core._now); - - // Advance time to after all occurrences should have happened - Core._now = Core._now.AddSeconds(50); - Timer.Slice(8); - - // Should have fired 4 times (3 recurrences) - Assert.Equal(3, callCount); - Assert.Equal(3, evt.CallCount); - - evt.Cancel(); - } - finally - { - Finish(); - } - } - } - - [Fact] - public void Scheduler_HandlesExceptionsInEvents() - { - Init(); - - try - { - var failedEventCalled = false; - var laterEventCalled = false; - - // Event that throws exception - var failedEvt = EventScheduler.Shared.ScheduleEvent( - Core._now.AddSeconds(10), - () => - { - failedEventCalled = true; - throw new Exception("Test exception"); - } - ); - - // Later event that should still execute - var laterEvt = EventScheduler.Shared.ScheduleEvent( - Core._now.AddSeconds(20), - () => laterEventCalled = true - ); - - // Advance time to after both events - Core._now = Core._now.AddSeconds(30); - Timer.Slice(8); - - // Both should have been called despite the exception - Assert.True(failedEventCalled); - Assert.True(laterEventCalled); - - failedEvt.Cancel(); - laterEvt.Cancel(); - } - finally - { - Finish(); - } - } - - [Fact] - public void Scheduler_RemovesEventsCorrectly() - { - Init(); - - try - { - var eventCalled = false; - - var evt = EventScheduler.Shared.ScheduleEvent( - Core._now.AddSeconds(10), - () => eventCalled = true - ); - - // Remove before execution - evt.Cancel(); - - // Advance time - Core._now = Core._now.AddSeconds(20); - Timer.Slice(8); - - // Event should not have executed - Assert.False(eventCalled); - } - finally - { - Finish(); - } - } - - [Fact] - public void Scheduler_HandlesEventsWithEndDates() - { - Init(); - - try - { - var callCount = 0; - - // Create a recurrence pattern that fires every 10 seconds - var recurrence = new TestRecurrencePattern(TimeSpan.FromSeconds(10)); - - // Create a custom event with an end date - var startTime = Core._now; - var endTime = Core._now.AddSeconds(36); - - var customEvent = new CustomEvent( - TimeOnly.FromDateTime(startTime), - endTime, - recurrence, - () => callCount++ - ); - - customEvent.Schedule(startTime); - - Core._now = Core._now.AddSeconds(50); - Timer.Slice(8); - - // Should have fired 3 times only (initial + 2 within the timeframe) - Assert.Equal(3, callCount); - } - finally - { - Finish(); - } - } - - private class CustomEvent : ScheduledEvent - { - private readonly Action _callback; - - public CustomEvent( - TimeOnly time, - DateTime endOn, - IRecurrencePattern recurrence, - Action callback - ) : base(time, endOn, recurrence) => _callback = callback; - - public override void OnEvent() - { - _callback?.Invoke(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Events/RecurrencePatternTests.cs b/Projects/UOContent.Tests/Tests/Engines/Events/RecurrencePatternTests.cs deleted file mode 100644 index b08a49007..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Events/RecurrencePatternTests.cs +++ /dev/null @@ -1,313 +0,0 @@ -using System; -using Server; -using Server.Engines.Events; -using Xunit; - -namespace UOContent.Tests; - -public class RecurrencePatternTests -{ - [Theory] - [InlineData(2024, 5, 15, 12, 30, 1, 2024, 5, 15, 13, 30)] // Normal case - [InlineData(2024, 5, 15, 23, 30, 2, 2024, 5, 16, 1, 30)] // Cross day boundary - public void HourlyRecurrencePattern_GetNextOccurrence_ReturnsCorrectTime( - int startYear, int startMonth, int startDay, int startHour, int startMinute, - int intervalHours, - int expectedYear, int expectedMonth, int expectedDay, int expectedHour, int expectedMinute) - { - // Arrange - var tz = TimeZoneInfo.Utc; - var pattern = new HourlyRecurrencePattern(intervalHours); - var afterUtc = new DateTime(startYear, startMonth, startDay, startHour, startMinute, 0, DateTimeKind.Utc); - var time = new TimeOnly(0, expectedMinute); - - // Act - var result = pattern.GetNextOccurrence(afterUtc, time, tz); - - // Assert - var expected = new DateTime(expectedYear, expectedMonth, expectedDay, expectedHour, expectedMinute, 0, DateTimeKind.Utc); - Assert.Equal(expected, result); - } - - [Theory] - [InlineData(2024, 5, 15, 12, 30, 1, 2024, 5, 16, 12, 30)] // Next day - [InlineData(2024, 5, 31, 12, 30, 2, 2024, 6, 2, 12, 30)] // Across month boundary - public void DailyRecurrencePattern_GetNextOccurrence_ReturnsCorrectDay( - int startYear, int startMonth, int startDay, int startHour, int startMinute, - int intervalDays, - int expectedYear, int expectedMonth, int expectedDay, int expectedHour, int expectedMinute) - { - // Arrange - var tz = TimeZoneInfo.Utc; - var pattern = new DailyRecurrencePattern(intervalDays); - var afterUtc = new DateTime(startYear, startMonth, startDay, startHour, startMinute, 0, DateTimeKind.Utc); - var time = new TimeOnly(expectedHour, expectedMinute); - - // Act - var result = pattern.GetNextOccurrence(afterUtc, time, tz); - - // Assert - var expected = new DateTime(expectedYear, expectedMonth, expectedDay, expectedHour, expectedMinute, 0, DateTimeKind.Utc); - Assert.Equal(expected, result); - } - - [Theory] - [InlineData(2024, 5, 15, 12, 30, 1, AllowedDays.All, 2024, 5, 16, 12, 30)] // Next day (Thursday) - [InlineData(2024, 5, 15, 12, 30, 1, AllowedDays.Monday, 2024, 5, 20, 12, 30)] // Next Monday - [InlineData(2024, 5, 15, 12, 30, 2, AllowedDays.Wednesday, 2024, 5, 29, 12, 30)] // Biweekly Wednesday - public void WeeklyRecurrencePattern_BasicPatterns_ReturnsCorrectDay( - int startYear, int startMonth, int startDay, int startHour, int startMinute, - int intervalWeeks, AllowedDays allowedDays, - int expectedYear, int expectedMonth, int expectedDay, int expectedHour, int expectedMinute) - { - // Arrange - var tz = TimeZoneInfo.Utc; - var pattern = new WeeklyRecurrencePattern(intervalWeeks, AllowedMonths.All, allowedDays); - var afterUtc = new DateTime(startYear, startMonth, startDay, startHour, startMinute, 0, DateTimeKind.Utc); - var time = new TimeOnly(expectedHour, expectedMinute); - - // Act - var result = pattern.GetNextOccurrence(afterUtc, time, tz); - - // Assert - var expected = new DateTime(expectedYear, expectedMonth, expectedDay, expectedHour, expectedMinute, 0, DateTimeKind.Utc); - Assert.Equal(expected, result); - } - - [Theory] - [InlineData(2024, 12, 30, 12, 30, 1, AllowedDays.Monday, 2025, 1, 6, 12, 30)] // Across year boundary - [InlineData(2024, 5, 31, 12, 30, 1, AllowedDays.Saturday, 2024, 6, 1, 12, 30)] // Across month boundary - public void WeeklyRecurrencePattern_CrossBoundaries_ReturnsCorrectDay( - int startYear, int startMonth, int startDay, int startHour, int startMinute, - int intervalWeeks, AllowedDays allowedDays, - int expectedYear, int expectedMonth, int expectedDay, int expectedHour, int expectedMinute) - { - // Arrange - var tz = TimeZoneInfo.Utc; - var pattern = new WeeklyRecurrencePattern(intervalWeeks, AllowedMonths.All, allowedDays); - var afterUtc = new DateTime(startYear, startMonth, startDay, startHour, startMinute, 0, DateTimeKind.Utc); - var time = new TimeOnly(expectedHour, expectedMinute); - - // Act - var result = pattern.GetNextOccurrence(afterUtc, time, tz); - - // Assert - var expected = new DateTime(expectedYear, expectedMonth, expectedDay, expectedHour, expectedMinute, 0, DateTimeKind.Utc); - Assert.Equal(expected, result); - } - - [Theory] - [InlineData(2024, 5, 15, 12, 30, AllowedMonths.January | AllowedMonths.February, AllowedDays.Wednesday, 2025, 1, 1, 12, 30)] // Skip to January - [InlineData(2024, 12, 15, 12, 30, AllowedMonths.January | AllowedMonths.December, AllowedDays.Wednesday, 2024, 12, 18, 12, 30)] // Same month - [InlineData(2024, 1, 31, 12, 30, AllowedMonths.February | AllowedMonths.April, AllowedDays.Saturday, 2024, 2, 3, 12, 30)] // Next month allowed - public void WeeklyRecurrencePattern_MonthFiltering_ReturnsCorrectDay( - int startYear, int startMonth, int startDay, int startHour, int startMinute, - AllowedMonths allowedMonths, AllowedDays allowedDays, - int expectedYear, int expectedMonth, int expectedDay, int expectedHour, int expectedMinute) - { - // Arrange - var tz = TimeZoneInfo.Utc; - var pattern = new WeeklyRecurrencePattern(1, allowedMonths, allowedDays); - var afterUtc = new DateTime(startYear, startMonth, startDay, startHour, startMinute, 0, DateTimeKind.Utc); - var time = new TimeOnly(expectedHour, expectedMinute); - - // Act - var result = pattern.GetNextOccurrence(afterUtc, time, tz); - - // Assert - var expected = new DateTime(expectedYear, expectedMonth, expectedDay, expectedHour, expectedMinute, 0, DateTimeKind.Utc); - Assert.Equal(expected, result); - } - - [Theory] - [InlineData("America/New_York", 2024, 3, 9, 12, 0, 1, AllowedDays.Sunday, 2024, 3, 10, 12, 0)] // Before spring forward - [InlineData("America/New_York", 2024, 3, 10, 2, 30, 1, AllowedDays.Sunday, 2024, 3, 17, 2, 30)] // During spring forward (invalid time) - [InlineData("America/New_York", 2024, 11, 2, 12, 0, 1, AllowedDays.Sunday, 2024, 11, 3, 12, 0)] // Before fall back - [InlineData("America/New_York", 2024, 11, 3, 1, 30, 1, AllowedDays.Sunday, 2024, 11, 10, 1, 30)] // During fall back (ambiguous time) - public void WeeklyRecurrencePattern_DSTTransitions_HandlesCorrectly( - string tzId, int startYear, int startMonth, int startDay, int startHour, int startMinute, - int intervalWeeks, AllowedDays allowedDays, - int expectedYear, int expectedMonth, int expectedDay, int expectedHour, int expectedMinute) - { - // Arrange - var tz = TimeZoneInfo.FindSystemTimeZoneById(tzId); - var pattern = new WeeklyRecurrencePattern(intervalWeeks, AllowedMonths.All, allowedDays); - - var startLocal = new DateTime(startYear, startMonth, startDay, startHour, startMinute, 0); - var afterUtc = startLocal.LocalToUtc(tz); - var time = new TimeOnly(expectedHour, expectedMinute); - - // Act - var result = pattern.GetNextOccurrence(afterUtc, time, tz); - - // Assert - var resultLocal = TimeZoneInfo.ConvertTimeFromUtc(result, tz); - Assert.Equal(new DateTime(expectedYear, expectedMonth, expectedDay, expectedHour, expectedMinute, 0), resultLocal); - } - - [Theory] - [InlineData(2024, 5, 29, 12, 0, AllowedDays.None, 2024, 5, 29, 12, 30)] // Default to current day - [InlineData(2024, 5, 29, 12, 0, AllowedDays.Wednesday | AllowedDays.Friday, 2024, 5, 29, 12, 30)] // Current day is Wednesday - [InlineData(2024, 5, 29, 12, 0, AllowedDays.Monday | AllowedDays.Friday, 2024, 5, 31, 12, 30)] // Next allowed day (Friday) - public void WeeklyRecurrencePattern_DaysOfWeekHandling_ReturnsCorrectDay( - int startYear, int startMonth, int startDay, int startHour, int startMinute, - AllowedDays allowedDays, - int expectedYear, int expectedMonth, int expectedDay, int expectedHour, int expectedMinute) - { - // Arrange - var tz = TimeZoneInfo.Utc; - var pattern = new WeeklyRecurrencePattern(1, AllowedMonths.All, allowedDays); - var afterUtc = new DateTime(startYear, startMonth, startDay, startHour, startMinute, 0, DateTimeKind.Utc); - var time = new TimeOnly(expectedHour, expectedMinute); - - // Act - var result = pattern.GetNextOccurrence(afterUtc, time, tz); - - // Assert - var expected = new DateTime(expectedYear, expectedMonth, expectedDay, expectedHour, expectedMinute, 0, DateTimeKind.Utc); - Assert.Equal(expected, result); - } - - [Theory] - [InlineData(2024, 1, 29, 12, 0, 1, 2024, 1, 29, 12, 30, AllowedMonths.January | AllowedMonths.March, 2024, 1, 30, 12, 30)] // Current month allowed - [InlineData(2024, 1, 31, 12, 0, 1, 2024, 1, 31, 12, 30, AllowedMonths.January | AllowedMonths.March, 2024, 3, 1, 12, 30)] // Skip February - public void WeeklyRecurrencePattern_WeekSpanningMonths_ReturnsCorrectDay( - int startYear, int startMonth, int startDay, int startHour, int startMinute, - int intervalWeeks, - int expectedYear, int expectedMonth, int expectedDay, int expectedHour, int expectedMinute, - AllowedMonths allowedMonths, - int nextExpectedYear, int nextExpectedMonth, int nextExpectedDay, int nextExpectedHour, int nextExpectedMinute) - { - // Arrange - var tz = TimeZoneInfo.Utc; - var pattern = new WeeklyRecurrencePattern(intervalWeeks, allowedMonths); - var afterUtc = new DateTime(startYear, startMonth, startDay, startHour, startMinute, 0, DateTimeKind.Utc); - var time = new TimeOnly(expectedHour, expectedMinute); - - // Act - first call should hit expectedDate - var result = pattern.GetNextOccurrence(afterUtc, time, tz); - - // Assert - var expected = new DateTime(expectedYear, expectedMonth, expectedDay, expectedHour, expectedMinute, 0, DateTimeKind.Utc); - Assert.Equal(expected, result); - - // Call again - should move to next available day in allowed months - var nextResult = pattern.GetNextOccurrence(result, time, tz); - var nextExpected = new DateTime(nextExpectedYear, nextExpectedMonth, nextExpectedDay, nextExpectedHour, nextExpectedMinute, 0, DateTimeKind.Utc); - Assert.Equal(nextExpected, nextResult); - } - - [Theory] - [InlineData(2024, 11, 3, 1, 30, DayOfWeek.Sunday, OrdinalDayOccurrence.First, "America/New_York", 2024, 11, 3)] - [InlineData(2024, 3, 31, 0, 0, DayOfWeek.Sunday, OrdinalDayOccurrence.Last, "America/New_York", 2024, 3, 31)] - [InlineData(2024, 3, 31, 0, 0, DayOfWeek.Sunday, OrdinalDayOccurrence.Fifth, "America/New_York", 2024, 3, 31)] - [InlineData(2024, 4, 3, 0, 0, DayOfWeek.Sunday, OrdinalDayOccurrence.Fifth, "America/New_York", 2024, 6, 30)] // June has 5 Sundays (2, 9, 16, 23, 30) - public void MonthlyOrdinalRecurrencePattern_GetNextOccurrence_ReturnsCorrectDate( - int startYear, int startMonth, int startDay, int startHour, int startMinute, - DayOfWeek dow, OrdinalDayOccurrence ordinal, string tzId, - int expectedYear, int expectedMonth, int expectedDay) - { - // Arrange - var tz = TimeZoneInfo.FindSystemTimeZoneById(tzId); - var pattern = new MonthlyOrdinalRecurrencePattern(ordinal, dow); - - // Use a day BEFORE the expected date - var startLocal = new DateTime(startYear, startMonth, startDay, startHour, startMinute, 0); - var beforeUtc = startLocal.AddDays(-1).LocalToUtc(tz); - var timeOnly = new TimeOnly(startHour, startMinute); - - // Act - var result = pattern.GetNextOccurrence(beforeUtc, timeOnly, tz); - - // Assert - convert back to local for comparison - var resultLocal = TimeZoneInfo.ConvertTimeFromUtc(result, tz); - Assert.Equal(new DateTime(expectedYear, expectedMonth, expectedDay, startHour, startMinute, 0), resultLocal); - } - - [Theory] - [InlineData("America/New_York", 2024, 3, 10, 2, 30)] // Spring forward - 2:30 AM doesn't exist - [InlineData("America/New_York", 2024, 11, 3, 1, 30)] // Fall back - 1:30 AM happens twice - public void MonthlyRecurrencePattern_DSTTransitions_HandlesCorrectly( - string tzId, int year, int month, int day, int hour, int minute) - { - // Arrange - var tz = TimeZoneInfo.FindSystemTimeZoneById(tzId); - var pattern = new MonthlyRecurrencePattern(day); - - var localDateBefore = new DateTime(year, month, day, 0, 0, 0); - var beforeUtc = localDateBefore.LocalToUtc(tz); - var timeOnly = new TimeOnly(hour, minute); - - // Act - shouldn't throw exceptions - var result = pattern.GetNextOccurrence(beforeUtc, timeOnly, tz); - - // Assert - just make sure we got a valid result - Assert.NotEqual(DateTime.MaxValue, result); - - // For invalid times (spring forward), should skip to next valid time - var resultLocal = TimeZoneInfo.ConvertTimeFromUtc(result, tz); - if (month == 3) // Spring forward - { - // Should skip the invalid 2:30 AM time - Assert.True(resultLocal.Hour >= 3 || resultLocal > new DateTime(year, month, day)); - } - } - - [Fact] - public void MonthlyOrdinalRecurrencePattern_DST_FallBack_HandlesAmbiguousTimeCorrectly() - { - // This tests specifically the DST fall back case that was failing - var tz = TimeZoneInfo.FindSystemTimeZoneById("America/New_York"); - var pattern = new MonthlyOrdinalRecurrencePattern(OrdinalDayOccurrence.First, DayOfWeek.Sunday); - - // November 3, 2024 at 1:30 AM - First Sunday, falls on DST transition - var beforeDate = new DateTime(2024, 11, 2, 12, 0, 0); - var beforeUtc = beforeDate.LocalToUtc(tz); - var timeOnly = new TimeOnly(1, 30); - - // Act - var result = pattern.GetNextOccurrence(beforeUtc, timeOnly, tz); - - // Assert - should be November 3, 2024 at 1:30 AM - var resultLocal = TimeZoneInfo.ConvertTimeFromUtc(result, tz); - Assert.Equal(new DateTime(2024, 11, 3, 1, 30, 0), resultLocal); - } - - [Fact] - public void MonthlyRecurrence_MonthWithoutDay_SkipsToNextValidMonth() - { - // Arrange - var tz = TimeZoneInfo.Utc; - var pattern = new MonthlyRecurrencePattern(31); - - // February doesn't have 31 days - var februaryDate = new DateTime(2024, 2, 1, 12, 0, 0, DateTimeKind.Utc); - var timeOnly = new TimeOnly(12, 0); - - // Act - var result = pattern.GetNextOccurrence(februaryDate, timeOnly, tz); - - // Assert - should be March 31 - Assert.Equal(new DateTime(2024, 3, 31, 12, 0, 0), result); - } - - [Theory] - [InlineData("America/New_York", 2024, 11, 3, 1, 30)] // Fall back - ambiguous time - public void LocalToUtc_AmbiguousTime_HandlesConsistently( - string tzId, int year, int month, int day, int hour, int minute) - { - // Arrange - var tz = TimeZoneInfo.FindSystemTimeZoneById(tzId); - var localTime = new DateTime(year, month, day, hour, minute, 0); - - // Act - var result = localTime.LocalToUtc(tz); - - // Assert - should be consistent, not testing exact value - Assert.Equal(DateTimeKind.Utc, result.Kind); - - // Convert back should give either standard or DST time - var backToLocal = TimeZoneInfo.ConvertTimeFromUtc(result, tz); - Assert.Equal(new DateTime(year, month, day, hour, minute, 0), backToLocal); - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Factions/FactionRankTests.cs b/Projects/UOContent.Tests/Tests/Engines/Factions/FactionRankTests.cs deleted file mode 100644 index 2416055b1..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Factions/FactionRankTests.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Collections.Generic; -using Server; -using Server.Factions; -using Xunit; - -namespace UOContent.Tests; - -/// -/// PlayerState.Rank is read from GetProperties, so it must stay a plain field read. These pin what -/// that requires: the rank is never null, and it is correct without anyone having read it first. -/// -[Collection("Sequential UOContent Tests")] -public class FactionRankTests -{ - // The faction ctor builds its own Definition, so no world state is needed. - private static Faction NewFaction() => new CouncilOfMages(); - - private static PlayerState AddMember(Faction faction, List owner) - { - var state = new PlayerState(new Mobile(), faction, owner); - owner.Add(state); - return state; - } - - [Fact] - public void Rank_IsPopulatedBeforeAnythingReadsIt() - { - var faction = NewFaction(); - var state = new PlayerState(new Mobile(), faction, []); - - // Nothing recomputes on read, so the ctor must leave a usable value or Rank.Title NREs. - Assert.NotNull(state.Rank); - Assert.NotNull(state.Rank.Title); - } - - [Fact] - public void Rank_IsTheLowestRank_ForAnUnrankedMember() - { - var faction = NewFaction(); - var owner = new List(); - var a = AddMember(faction, owner); - var b = AddMember(faction, owner); - - a.UpdateRank(); - b.UpdateRank(); - - var lowest = faction.Definition.Ranks[^1]; - - Assert.Equal(lowest.Rank, a.Rank.Rank); - Assert.Equal(lowest.Rank, b.Rank.Rank); - } - - [Fact] - public void SettingRankIndex_UpdatesRankWithoutAnyoneReadingIt() - { - var faction = NewFaction(); - var owner = new List(); - var top = AddMember(faction, owner); - var bottom = AddMember(faction, owner); - - faction.ZeroRankOffset = 2; - - top.RankIndex = 0; - bottom.RankIndex = 1; - - // No read triggered these, yet the ordering is reflected. - Assert.True(top.Rank.Rank > bottom.Rank.Rank); - } - - [Fact] - public void ReadingRank_IsStableAndSideEffectFree() - { - var faction = NewFaction(); - var owner = new List(); - var state = AddMember(faction, owner); - - faction.ZeroRankOffset = 1; - state.RankIndex = 0; - - var first = state.Rank; - var second = state.Rank; - - Assert.Same(first, second); - } - - [Fact] - public void RankIndexOutOfSyncWithZeroRankOffset_StillResolvesARank() - { - var faction = NewFaction(); - var owner = new List(); - var a = AddMember(faction, owner); - AddMember(faction, owner); - - // A negative percent used to match no rank at all, leaving Rank null. - faction.ZeroRankOffset = 1; - a.RankIndex = 5; - - Assert.NotNull(a.Rank); - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Help/HelpTopic.cs b/Projects/UOContent.Tests/Tests/Engines/Help/HelpTopic.cs index 58b76edee..235fb5c69 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Help/HelpTopic.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Help/HelpTopic.cs @@ -4,11 +4,11 @@ public class DisplayHelpTopic : Packet { public DisplayHelpTopic(int topicID, bool display) : base(0xBF) { - EnsureCapacity(11); + EnsureCapacity(11); - Stream.Write((short)0x17); - Stream.Write((byte)1); - Stream.Write(topicID); - Stream.Write(display); - } -} + Stream.Write((short)0x17); + Stream.Write((byte)1); + Stream.Write(topicID); + Stream.Write(display); + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Engines/Help/TestHelpTopicPacket.cs b/Projects/UOContent.Tests/Tests/Engines/Help/TestHelpTopicPacket.cs index 520dfd22e..feca1c09a 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Help/TestHelpTopicPacket.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Help/TestHelpTopicPacket.cs @@ -6,7 +6,6 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] public class TestHelpTopicPacket { [Theory] @@ -14,12 +13,12 @@ public class TestHelpTopicPacket [InlineData(HelpTopic.EmptyingBowl, true)] public void TestDisplayHelpTopic(int topic, bool display) { - var expected = new DisplayHelpTopic(topic, display).Compile(); + var expected = new DisplayHelpTopic(topic, display).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDisplayHelpTopic(topic, display); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDisplayHelpTopic(topic, display); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Engines/ML Quests/MLQuestPacketTests.cs b/Projects/UOContent.Tests/Tests/Engines/ML Quests/MLQuestPacketTests.cs index 3f28fc1e3..f0890887e 100644 --- a/Projects/UOContent.Tests/Tests/Engines/ML Quests/MLQuestPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/ML Quests/MLQuestPacketTests.cs @@ -5,7 +5,6 @@ using Xunit; namespace Server.Engines.MLQuests; -[Collection("Sequential UOContent Tests")] public class MLQuestPacketTests { private class MockedRace : Race @@ -70,10 +69,10 @@ public class MLQuestPacketTests var expected = new RaceChanger(female, race).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendRaceChanger(female, race); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -82,10 +81,10 @@ public class MLQuestPacketTests { var expected = new CloseRaceChanger().Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendCloseRaceChanger(); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } } diff --git a/Projects/UOContent.Tests/Tests/Engines/ML Quests/Packets.cs b/Projects/UOContent.Tests/Tests/Engines/ML Quests/Packets.cs index 5001f3cfc..8fd383e19 100644 --- a/Projects/UOContent.Tests/Tests/Engines/ML Quests/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Engines/ML Quests/Packets.cs @@ -6,22 +6,22 @@ public sealed class RaceChanger : Packet { public RaceChanger(bool female, Race targetRace) : base(0xBF) { - EnsureCapacity(7); + EnsureCapacity(7); - Stream.Write((short)0x2A); - Stream.Write((byte)(female ? 1 : 0)); - Stream.Write((byte)(targetRace.RaceID + 1)); - } + Stream.Write((short)0x2A); + Stream.Write((byte)(female ? 1 : 0)); + Stream.Write((byte)(targetRace.RaceID + 1)); + } } public sealed class CloseRaceChanger : Packet { public CloseRaceChanger() : base(0xBF) { - EnsureCapacity(7); + EnsureCapacity(7); - Stream.Write((short)0x2A); - Stream.Write((byte)0); - Stream.Write((byte)0xFF); - } -} + Stream.Write((short)0x2A); + Stream.Write((byte)0); + Stream.Write((byte)0xFF); + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Engines/Party/Packets.cs b/Projects/UOContent.Tests/Tests/Engines/Party/Packets.cs index 0ad84a5b0..ddda0685c 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Party/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Party/Packets.cs @@ -6,74 +6,74 @@ public sealed class PartyEmptyList : Packet { public PartyEmptyList(Serial m) : base(0xBF) { - EnsureCapacity(7); + EnsureCapacity(7); - Stream.Write((short)0x0006); - Stream.Write((byte)0x02); - Stream.Write((byte)0); - Stream.Write(m); - } + Stream.Write((short)0x0006); + Stream.Write((byte)0x02); + Stream.Write((byte)0); + Stream.Write(m); + } } public sealed class PartyMemberList : Packet { public PartyMemberList(Party p) : base(0xBF) { - EnsureCapacity(7 + p.Count * 4); + EnsureCapacity(7 + p.Count * 4); - Stream.Write((short)0x0006); - Stream.Write((byte)0x01); - Stream.Write((byte)p.Count); + Stream.Write((short)0x0006); + Stream.Write((byte)0x01); + Stream.Write((byte)p.Count); - for (var i = 0; i < p.Count; ++i) - { - Stream.Write(p[i].Mobile.Serial); + for (var i = 0; i < p.Count; ++i) + { + Stream.Write(p[i].Mobile.Serial); + } } - } } public sealed class PartyRemoveMember : Packet { public PartyRemoveMember(Serial removed, Party p) : base(0xBF) { - EnsureCapacity(11 + p.Count * 4); + EnsureCapacity(11 + p.Count * 4); - Stream.Write((short)0x0006); - Stream.Write((byte)0x02); - Stream.Write((byte)p.Count); + Stream.Write((short)0x0006); + Stream.Write((byte)0x02); + Stream.Write((byte)p.Count); - Stream.Write(removed); + Stream.Write(removed); - for (var i = 0; i < p.Count; ++i) - { - Stream.Write(p[i].Mobile.Serial); + for (var i = 0; i < p.Count; ++i) + { + Stream.Write(p[i].Mobile.Serial); + } } - } } public sealed class PartyTextMessage : Packet { public PartyTextMessage(bool toAll, Serial from, string text) : base(0xBF) { - text ??= ""; + text ??= ""; - EnsureCapacity(12 + text.Length * 2); + EnsureCapacity(12 + text.Length * 2); - Stream.Write((short)0x0006); - Stream.Write((byte)(toAll ? 0x04 : 0x03)); - Stream.Write(from); - Stream.WriteBigUniNull(text); - } + Stream.Write((short)0x0006); + Stream.Write((byte)(toAll ? 0x04 : 0x03)); + Stream.Write(from); + Stream.WriteBigUniNull(text); + } } public sealed class PartyInvitation : Packet { public PartyInvitation(Serial leader) : base(0xBF) { - EnsureCapacity(10); + EnsureCapacity(10); - Stream.Write((short)0x0006); - Stream.Write((byte)0x07); - Stream.Write(leader); - } -} + Stream.Write((short)0x0006); + Stream.Write((byte)0x07); + Stream.Write(leader); + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Engines/Party/PartyPacketTests.cs b/Projects/UOContent.Tests/Tests/Engines/Party/PartyPacketTests.cs index 99921aea6..a61a729b8 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Party/PartyPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Party/PartyPacketTests.cs @@ -6,93 +6,92 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] -public class PartyPacketTests +public class PartyPacketTests : IClassFixture { [Fact] public void TestPartyEmptyList() { - var m = (Serial)0x1024u; + Serial m = (Serial)0x1024u; - var expected = new PartyEmptyList(m).Compile(); + var expected = new PartyEmptyList(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendPartyRemoveMember(m); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPartyRemoveMember(m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Fact] public void TestPartyRemoveMember() { - var leader = new Mobile((Serial)0x1024u); - leader.DefaultMobileInit(); + var leader = new Mobile((Serial)0x1024u); + leader.DefaultMobileInit(); - var member = new Mobile((Serial)0x2048u); - member.DefaultMobileInit(); + var member = new Mobile((Serial)0x2048u); + member.DefaultMobileInit(); - var p = new Party(leader); - p.Add(member); + var p = new Party(leader); + p.Add(member); - var expected = new PartyRemoveMember(member.Serial, p).Compile(); + var expected = new PartyRemoveMember(member.Serial, p).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendPartyRemoveMember(member.Serial, p); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPartyRemoveMember(member.Serial, p); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Fact] public void TestPartyMemberList() { - var leader = new Mobile((Serial)0x1024u); - leader.DefaultMobileInit(); + var leader = new Mobile((Serial)0x1024u); + leader.DefaultMobileInit(); - var member = new Mobile((Serial)0x2048u); - member.DefaultMobileInit(); + var member = new Mobile((Serial)0x2048u); + member.DefaultMobileInit(); - var p = new Party(leader); - p.Add(member); + var p = new Party(leader); + p.Add(member); - var expected = new PartyMemberList(p).Compile(); + var expected = new PartyMemberList(p).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendPartyMemberList(p); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPartyMemberList(p); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(true)] [InlineData(false)] public void TestPartyTextMessage(bool toAll) { - var serial = (Serial)0x1024u; - var text = "[Party] Stuff Happens"; + Serial serial = (Serial)0x1024u; + var text = "[Party] Stuff Happens"; - var expected = new PartyTextMessage(toAll, serial, text).Compile(); + var expected = new PartyTextMessage(toAll, serial, text).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendPartyTextMessage(serial, text, toAll); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPartyTextMessage(serial, text, toAll); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Fact] public void TestPartyInvitation() { - var m = (Serial)0x1024u; + Serial m = (Serial)0x1024u; - var expected = new PartyInvitation(m).Compile(); + var expected = new PartyInvitation(m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendPartyInvitation(m); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPartyInvitation(m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/BitmapAStarAlgorithmTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/BitmapAStarAlgorithmTests.cs deleted file mode 100644 index 20d729c0f..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/BitmapAStarAlgorithmTests.cs +++ /dev/null @@ -1,473 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Server.Mobiles; -using Server.PathAlgorithms; -using Server.Systems.FeatureFlags; -using Xunit; -using Xunit.Abstractions; - -namespace Server.Tests.Pathfinding; - -/// -/// Smoke tests for 's two branches: cache-direct fast -/// path (default walkers) and per-cell slow path (capability creatures and non-GM players). -/// Exercised end-to-end against the real Trammel TileMatrix to ensure neither regresses -/// to "no path found" on reachable goals. -/// -[Collection("Sequential Pathfinding Tests")] -public class BitmapAStarAlgorithmTests -{ - private readonly ITestOutputHelper _output; - - public BitmapAStarAlgorithmTests(ITestOutputHelper output) - { - _output = output; - } - - [Theory] - // Pinned cell (1500, 1600, z=10): mask=0xC1 → N, W, NW walkable. - // Use start.Z for goal.Z so destNode lands in the same Z plane the algorithm reaches - // during expansion (GetAverageZ at the goal cell may differ from the engine's - // runtime-computed standing Z, which would break the destNode equality check). - [InlineData(1500, 1600, 1498, 1598)] // NW, 2 cells diagonal - [InlineData(1500, 1600, 1497, 1599)] // NW-ish, 3 W + 1 N - public void DefaultWalker_FindsPath_ViaCacheFastPath(int sx, int sy, int gx, int gy) - { - StepCache.Instance.Clear(); - var map = Map.Maps[1]; - Assert.NotNull(map); - - var stub = new DefaultWalkerStub(); - map.GetAverageZ(sx, sy, out _, out var startZ, out _); - var start = new Point3D(sx, sy, (sbyte)startZ); - var goal = new Point3D(gx, gy, (sbyte)startZ); - - stub.MoveToWorld(start, map); - - var result = BitmapAStarAlgorithm.Instance.Find(stub, map, start, goal); - - stub.Delete(); - - Assert.NotNull(result); - Assert.NotEmpty(result); - _output.WriteLine($"default-walker ({sx},{sy})->({gx},{gy}): {result.Length} steps"); - } - - [Theory] - [InlineData(1500, 1600, 1498, 1598)] // NW, 2 cells diagonal - [InlineData(1500, 1600, 1497, 1599)] // NW-ish, 3 W + 1 N - public void SwimCreature_FindsPath_ViaCacheCapabilityOverlay(int sx, int sy, int gx, int gy) - { - StepCache.Instance.Clear(); - StepCache.Instance.MissPromotionThreshold = 1; - var map = Map.Maps[1]; - Assert.NotNull(map); - - var stub = new SwimmingStub(World.NewMobile); - stub.DefaultMobileInit(); - stub.CanSwim = true; // overlay route — cache + (walkMask | wetMask&canSwim) - map.GetAverageZ(sx, sy, out _, out var startZ, out _); - var start = new Point3D(sx, sy, (sbyte)startZ); - var goal = new Point3D(gx, gy, (sbyte)startZ); - - stub.MoveToWorld(start, map); - - var statsBefore = StepCache.Instance.GetStats(); - var result = BitmapAStarAlgorithm.Instance.Find(stub, map, start, goal); - var statsAfter = StepCache.Instance.GetStats(); - - stub.Delete(); - - Assert.NotNull(result); - Assert.NotEmpty(result); - Assert.True(statsAfter.BuildsTotal > statsBefore.BuildsTotal, - "CanSwim creature should use the cache via capability overlay, not the slow path"); - _output.WriteLine($"swimmer ({sx},{sy})->({gx},{gy}): {result.Length} steps"); - } - - [Fact] - public void DynamicObstaclePass_RejectsCellOccupiedByLivingMobile() - { - StepCache.Instance.Clear(); - var map = Map.Maps[1]; - Assert.NotNull(map); - - // (1500, 1600) walks N/W/NW only (mask=0xC1). Path NW two cells; plant a blocker - // on the direct NW step so the algorithm must route via N→W or W→N around it. - var sx = 1500; - var sy = 1600; - var gx = 1498; - var gy = 1598; - var blockX = 1499; - var blockY = 1599; - - map.GetAverageZ(sx, sy, out _, out var startZ, out _); - var start = new Point3D(sx, sy, (sbyte)startZ); - var goal = new Point3D(gx, gy, (sbyte)startZ); - - var walker = new DefaultWalkerStub(); - walker.MoveToWorld(start, map); - - // Living blocker on the only direct-line cell. CanMoveOver returns false for an - // alive non-staff mobile, so the dynamic-obstacle pass must reject this cell. - map.GetAverageZ(blockX, blockY, out _, out var blockZ, out _); - var blocker = new DefaultWalkerStub(); - blocker.MoveToWorld(new Point3D(blockX, blockY, (sbyte)blockZ), map); - - var result = BitmapAStarAlgorithm.Instance.Find(walker, map, start, goal); - - // Path may exist via an alternate route, but must NOT pass through the blocker. - Assert.NotNull(result); - var x = sx; - var y = sy; - foreach (var dir in result) - { - Movement.Movement.Offset(dir, ref x, ref y); - Assert.False(x == blockX && y == blockY, - $"path traversed blocker cell ({blockX},{blockY})"); - } - - walker.Delete(); - blocker.Delete(); - } - - [SkippableFact] - public void DynamicObstaclePass_RejectsCellOccupiedByImpassableItem() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[1]; - Assert.NotNull(map); - - // Same NW-around-blocker scenario as the mobile-blocker test, but with an item. - var sx = 1500; - var sy = 1600; - var gx = 1498; - var gy = 1598; - var blockX = 1499; - var blockY = 1599; - - // Find any ItemID whose TileData has ImpassableSurface so the dynamic pass - // rejects the cell. Pinning to a specific ID would couple the test to UO art data. - ushort blockerItemId = 0; - for (ushort id = 1; id < TileData.MaxItemValue; id++) - { - if (TileData.ItemTable[id].ImpassableSurface) - { - blockerItemId = id; - break; - } - } - Assert.NotEqual(0, blockerItemId); - - map.GetAverageZ(sx, sy, out _, out var startZ, out _); - var start = new Point3D(sx, sy, (sbyte)startZ); - var goal = new Point3D(gx, gy, (sbyte)startZ); - - var walker = new DefaultWalkerStub(); - walker.MoveToWorld(start, map); - - map.GetAverageZ(blockX, blockY, out _, out var blockZ, out _); - var blocker = new Item(World.NewItem) - { - ItemID = blockerItemId, - Map = map, - Location = new Point3D(blockX, blockY, (sbyte)blockZ) - }; - - var result = BitmapAStarAlgorithm.Instance.Find(walker, map, start, goal); - - Assert.NotNull(result); - var x = sx; - var y = sy; - foreach (var dir in result) - { - Movement.Movement.Offset(dir, ref x, ref y); - Assert.False(x == blockX && y == blockY, - $"path traversed item-blocker cell ({blockX},{blockY})"); - } - - walker.Delete(); - blocker.Delete(); - } - - [Fact] - public void FeatureFlagDisabled_RoutesToSlowPath_NoCacheUse() - { - StepCache.Instance.Clear(); - var map = Map.Maps[1]; - Assert.NotNull(map); - - var stub = new DefaultWalkerStub(); - map.GetAverageZ(1500, 1600, out _, out var startZ, out _); - var start = new Point3D(1500, 1600, (sbyte)startZ); - var goal = new Point3D(1498, 1598, (sbyte)startZ); - stub.MoveToWorld(start, map); - - var statsBefore = StepCache.Instance.GetStats(); - var prevFlag = ContentFeatureFlags.BitmapPathfindingCache; - try - { - ContentFeatureFlags.BitmapPathfindingCache = false; - var result = BitmapAStarAlgorithm.Instance.Find(stub, map, start, goal); - Assert.NotNull(result); - Assert.NotEmpty(result); - } - finally - { - ContentFeatureFlags.BitmapPathfindingCache = prevFlag; - } - var statsAfter = StepCache.Instance.GetStats(); - - stub.Delete(); - Assert.Equal(statsBefore.BuildsTotal, statsAfter.BuildsTotal); - } - - [Fact] - public void FlyCreature_RoutesToSlowPath_NoCacheUse() - { - StepCache.Instance.Clear(); - var map = Map.Maps[1]; - - var stub = new FlyingStub(World.NewMobile); - stub.DefaultMobileInit(); - map.GetAverageZ(1500, 1600, out _, out var startZ, out _); - var start = new Point3D(1500, 1600, (sbyte)startZ); - var goal = new Point3D(1498, 1598, (sbyte)startZ); - - stub.MoveToWorld(start, map); - - var statsBefore = StepCache.Instance.GetStats(); - var result = BitmapAStarAlgorithm.Instance.Find(stub, map, start, goal); - var statsAfter = StepCache.Instance.GetStats(); - - stub.Delete(); - - Assert.NotNull(result); - Assert.NotEmpty(result); - Assert.Equal(statsBefore.BuildsTotal, statsAfter.BuildsTotal); - } - - [Fact] - public void NonGmPlayer_UsesCache_WithStrictDiagonalRule() - { - StepCache.Instance.Clear(); - StepCache.Instance.MissPromotionThreshold = 1; - var map = Map.Maps[1]; - Assert.NotNull(map); - - var stub = new PlayerStub(); - map.GetAverageZ(1500, 1600, out _, out var startZ, out _); - var start = new Point3D(1500, 1600, (sbyte)startZ); - var goal = new Point3D(1498, 1598, (sbyte)startZ); - - stub.MoveToWorld(start, map); - - var statsBefore = StepCache.Instance.GetStats(); - var result = BitmapAStarAlgorithm.Instance.Find(stub, map, start, goal); - var statsAfter = StepCache.Instance.GetStats(); - - stub.Delete(); - - Assert.NotNull(result); - Assert.NotEmpty(result); - // BuildsTotal increments on every chunk build, which happens only when the cache - // is queried. Slow path never touches the cache. - Assert.True(statsAfter.BuildsTotal > statsBefore.BuildsTotal, - "Non-GM player should use the cache, not the slow path"); - } - - [Fact] - public void DoorCreature_UsesCache_NotSlowPath() - { - StepCache.Instance.Clear(); - StepCache.Instance.MissPromotionThreshold = 1; - var map = Map.Maps[1]; - - var stub = new DoorOpenerStub(World.NewMobile); - stub.DefaultMobileInit(); - map.GetAverageZ(1500, 1600, out _, out var startZ, out _); - var start = new Point3D(1500, 1600, (sbyte)startZ); - var goal = new Point3D(1498, 1598, (sbyte)startZ); - - stub.MoveToWorld(start, map); - - var statsBefore = StepCache.Instance.GetStats(); - var result = BitmapAStarAlgorithm.Instance.Find(stub, map, start, goal); - var statsAfter = StepCache.Instance.GetStats(); - - stub.Delete(); - - Assert.NotNull(result); - Assert.NotEmpty(result); - Assert.True(statsAfter.BuildsTotal > statsBefore.BuildsTotal, - "CanOpenDoors creature should use the cache (doors are dynamic items)"); - } - - [Fact] - public void ObstacleCreature_UsesCache_NotSlowPath() - { - StepCache.Instance.Clear(); - StepCache.Instance.MissPromotionThreshold = 1; - var map = Map.Maps[1]; - - var stub = new ObstacleClimberStub(World.NewMobile); - stub.DefaultMobileInit(); - map.GetAverageZ(1500, 1600, out _, out var startZ, out _); - var start = new Point3D(1500, 1600, (sbyte)startZ); - var goal = new Point3D(1498, 1598, (sbyte)startZ); - - stub.MoveToWorld(start, map); - - var statsBefore = StepCache.Instance.GetStats(); - var result = BitmapAStarAlgorithm.Instance.Find(stub, map, start, goal); - var statsAfter = StepCache.Instance.GetStats(); - - stub.Delete(); - - Assert.NotNull(result); - Assert.NotEmpty(result); - Assert.True(statsAfter.BuildsTotal > statsBefore.BuildsTotal, - "CanMoveOverObstacles creature should use the cache (movables are dynamic items)"); - } - - // --------------------------------------------------------------------------------- - // Promotion-gate integration tests. These exercise BitmapAStarAlgorithm.Find() - // end-to-end against the live StepCache to prove the per-Find generation gate - // actually defers BuildChunk on a single pathfind. They duplicate behavior that - // unit tests cover at the cache layer; the value is end-to-end verification that - // the bench-relevant scenario (single Find on cleared cache) skips builds entirely. - // TODO: REMOVE these two tests once PR-6's gate is proven stable in production BDN. - // --------------------------------------------------------------------------------- - - [Fact] - public void Find_SinglePathfindOnClearedCache_DoesNotBuildAnyChunk() - { - StepCache.Instance.Clear(); - StepCache.Instance.MissPromotionThreshold = 2; - - var map = Map.Maps[1]; - var stub = new DefaultWalkerStub(); - map.GetAverageZ(1500, 1600, out _, out var startZ, out _); - var start = new Point3D(1500, 1600, (sbyte)startZ); - var goal = new Point3D(1498, 1598, (sbyte)startZ); - stub.MoveToWorld(start, map); - - var result = BitmapAStarAlgorithm.Instance.Find(stub, map, start, goal); - - var stats = StepCache.Instance.GetStats(); - stub.Delete(); - - Assert.NotNull(result); - Assert.Equal(0L, stats.BuildsTotal); - Assert.True(stats.FallthroughNotBuilt > 0L, - $"expected fallthrough on every chunk touched once; got 0 (residents={stats.ResidentChunks})"); - _output.WriteLine( - $"single-Find gate: builds={stats.BuildsTotal} fallthrough_not_built={stats.FallthroughNotBuilt}" - ); - } - - [Fact] - public void Find_TwoPathfindsOverlappingChunks_PromoteToBuildOnSecondFind() - { - StepCache.Instance.Clear(); - StepCache.Instance.MissPromotionThreshold = 2; - - var map = Map.Maps[1]; - var stub = new DefaultWalkerStub(); - map.GetAverageZ(1500, 1600, out _, out var startZ, out _); - var start = new Point3D(1500, 1600, (sbyte)startZ); - var goal = new Point3D(1498, 1598, (sbyte)startZ); - stub.MoveToWorld(start, map); - - // Find #1: first time anyone touches these chunks. Gate defers; no builds. - BitmapAStarAlgorithm.Instance.Find(stub, map, start, goal); - var afterFirst = StepCache.Instance.GetStats(); - Assert.Equal(0L, afterFirst.BuildsTotal); - - // Find #2: same path; chunks now hit their second distinct Find inside the window. - // Gate promotes — at least one BuildChunk fires. - BitmapAStarAlgorithm.Instance.Find(stub, map, start, goal); - var afterSecond = StepCache.Instance.GetStats(); - - stub.Delete(); - - Assert.True(afterSecond.BuildsTotal > 0L, - $"second Find through overlapping chunks must promote (got {afterSecond.BuildsTotal} builds)"); - _output.WriteLine( - $"two-Find gate: first builds={afterFirst.BuildsTotal} second builds={afterSecond.BuildsTotal}" - ); - } - - /// - /// Plain Mobile — RequiresSlowPath returns false, the bitmap algorithm uses the cache - /// fast path on every expansion. - /// - private sealed class DefaultWalkerStub : Mobile - { - public DefaultWalkerStub() - { - Body = 0xC9; - } - } - - /// - /// Mobile with Player=true and default AccessLevel (Player). Triggers the strict - /// AND-rule for diagonal corner-cut while still using the cache. - /// - private sealed class PlayerStub : Mobile - { - public PlayerStub() - { - Body = 0xC9; - Player = true; - } - } - - /// - /// BaseCreature with CanSwim=true — uses the cache via capability overlay (walk OR - /// (wet AND canSwim)). Use the Serial constructor (deserialization path) to bypass - /// NPCSpeeds init, which requires the npc-speeds.json table loaded — not available - /// in tests. - /// - private sealed class SwimmingStub : BaseCreature - { - public SwimmingStub(Serial serial) : base(serial) - { - Body = 0xC9; - } - } - - /// - /// BaseCreature with CanFly=true — RequiresSlowPath returns true (Z-jumping is beyond - /// the cache's static-only scope), so GetSuccessors short-circuits to the slow path. - /// - private sealed class FlyingStub : BaseCreature - { - public FlyingStub(Serial serial) : base(serial) - { - Body = 0xC9; - } - - public override bool CanFly => true; - } - - private sealed class DoorOpenerStub : BaseCreature - { - public DoorOpenerStub(Serial serial) : base(serial) - { - Body = 0xC9; - } - - public override bool CanOpenDoors => true; - } - - private sealed class ObstacleClimberStub : BaseCreature - { - public ObstacleClimberStub(Serial serial) : base(serial) - { - Body = 0xC9; - } - - public override bool CanMoveOverObstacles => true; - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs deleted file mode 100644 index 694374757..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs +++ /dev/null @@ -1,89 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Server.Items; -using Xunit; -using Xunit.Abstractions; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class BoatPathTests -{ - private readonly ITestOutputHelper _output; - - public BoatPathTests(ITestOutputHelper output) => _output = output; - - // Open water in the south-Britain bay (Trammel), used by the existing swim-bake test. - private const int MapId = 1; - private const int WaterX = 1450; - private const int WaterY = 1770; - - [SkippableFact] - public void BoatDeck_HasWalkableSurfaceCells() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - - // SmallBoat North heading = multiID 0x0. A deck is made of surface tiles. - var boat = new TestMulti(0x0); - map.GetAverageZ(WaterX, WaterY, out _, out var z, out _); - boat.MoveToWorld(new Point3D(WaterX, WaterY, (sbyte)z), map); - - try - { - var floor = MultiArt.FindFloorCell(boat); - Assert.True(floor.HasValue, "non-vacuity: boat deck must have surface (floor) tiles"); - - // The deck cell falls through to the live multi-aware path. - var mask = StepCache.Instance.TryGetMask(map, floor.Value.X, floor.Value.Y, (sbyte)z); - Assert.Equal(CacheHitKind.Fallthrough_Multi, mask.HitKind); - _output.WriteLine($"boat deck floor cell at ({floor.Value.X},{floor.Value.Y})"); - } - finally - { - boat.Delete(); - } - } - - [SkippableFact] - public void BoatDeck_FootprintShape_IsPositionInvariant() - { - TileDataRequirement.SkipIfMissing(); - // The property Phase 2's local-frame, movement-invariant boat cache must preserve: - // the deck's covered-cell shape (in local coords) is identical at two world positions. - var map = Map.Maps[MapId]; - - var a = new TestMulti(0x0); - map.GetAverageZ(WaterX, WaterY, out _, out var za, out _); - a.MoveToWorld(new Point3D(WaterX, WaterY, (sbyte)za), map); - var shapeA = LocalShape(a); - a.Delete(); - - var b = new TestMulti(0x0); - map.GetAverageZ(WaterX + 5, WaterY + 3, out _, out var zb, out _); - b.MoveToWorld(new Point3D(WaterX + 5, WaterY + 3, (sbyte)zb), map); - var shapeB = LocalShape(b); - b.Delete(); - - Assert.Equal(shapeA, shapeB); - Assert.NotEmpty(shapeA); - _output.WriteLine($"boat local deck shape stable across positions: {shapeA.Count} cells"); - } - - private static System.Collections.Generic.HashSet<(int lx, int ly)> LocalShape(BaseMulti multi) - { - var mcl = multi.Components; - var set = new System.Collections.Generic.HashSet<(int, int)>(); - for (var lx = 0; lx < mcl.Width; lx++) - { - for (var ly = 0; ly < mcl.Height; ly++) - { - if (mcl.Tiles[lx][ly].Length > 0) - { - set.Add((lx, ly)); - } - } - } - return set; - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs deleted file mode 100644 index e82520ce9..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs +++ /dev/null @@ -1,89 +0,0 @@ -using Server; -using Server.Engines.Pathing.Cache; -using Server.Items; -using Xunit; -using Xunit.Abstractions; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class FoundationRedesignTests -{ - private readonly ITestOutputHelper _output; - - public FoundationRedesignTests(ITestOutputHelper output) => _output = output; - - private const int MapId = 1; - private const int PlaceX = 1500; - private const int PlaceY = 1600; - - [SkippableFact] - public void SwappingComponents_ChangesFootprint() - { - TileDataRequirement.SkipIfMissing(); - var foundation = new SwappableFoundation(0x74); // GuildHouse footprint - try - { - var beforeCount = MultiArt.FootprintCells(foundation).Count; - Assert.True(beforeCount > 0, "non-vacuity: initial footprint must cover cells"); - - foundation.Redesign(MultiData.GetComponents(0x7A)); // Tower footprint (different shape) - var afterCount = MultiArt.FootprintCells(foundation).Count; - - Assert.NotEqual(beforeCount, afterCount); - _output.WriteLine($"redesign footprint {beforeCount} -> {afterCount} cells"); - } - finally - { - foundation.Delete(); - } - } - - [SkippableFact] - public void RedesignReRegistered_RoutesNewFootprintToLivePath() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - Assert.NotNull(map); - - var foundation = new SwappableFoundation(0x74); - map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); - var loc = new Point3D(PlaceX, PlaceY, (sbyte)z); - foundation.MoveToWorld(loc, map); - - try - { - // Current design's covered cells route to the live (multi-aware) path. - var before = MultiArt.FindFloorCell(foundation); - Assert.True(before.HasValue, "non-vacuity: initial design must have a floor cell"); - var maskBefore = StepCache.Instance.TryGetMask(map, before.Value.X, before.Value.Y, (sbyte)z); - Assert.Equal(CacheHitKind.Fallthrough_Multi, maskBefore.HitKind); - - // Redesign, RE-REGISTERED so sectors track the new footprint (model a real commit). - // Internalize() moves the multi to Map.Internal, which fires Map.OnLeave and removes - // the OLD footprint's sector registration. We then swap the MCL and MoveToWorld back, - // which fires Map.OnEnter -> AddMulti against the fresh Components (new footprint). - foundation.Internalize(); - foundation.Redesign(MultiData.GetComponents(0x7A)); - foundation.MoveToWorld(loc, map); - - var after = MultiArt.FindFloorCell(foundation); - Assert.True(after.HasValue, "non-vacuity: redesigned design must have a floor cell"); - // The new footprint cell must be registered (HasMultis) and route to the live path. - var sector = map.GetRealSector(after.Value.X >> 4, after.Value.Y >> 4); - Assert.True(sector.HasMultis, "redesigned footprint must re-register its sector"); - var maskAfter = StepCache.Instance.TryGetMask(map, after.Value.X, after.Value.Y, (sbyte)z); - Assert.Equal(CacheHitKind.Fallthrough_Multi, maskAfter.HitKind); - - _output.WriteLine($"redesign re-registered: before {before.Value.X},{before.Value.Y} after {after.Value.X},{after.Value.Y}"); - } - finally - { - if (!foundation.Deleted) - { - foundation.Delete(); - } - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/HousePathRoutingTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/HousePathRoutingTests.cs deleted file mode 100644 index 8ecbd0591..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/HousePathRoutingTests.cs +++ /dev/null @@ -1,227 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Server.Items; -using Server.Mobiles; -using Server.PathAlgorithms; -using Xunit; -using Xunit.Abstractions; -using CalcMoves = Server.Movement.Movement; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class HousePathRoutingTests -{ - private readonly ITestOutputHelper _output; - - public HousePathRoutingTests(ITestOutputHelper output) => _output = output; - - private const int MapId = 1; - - private sealed class WalkerStub : Mobile - { - public WalkerStub() => Body = 0xC9; - } - - [SkippableFact] - public void PathAround_NeverTraversesAWallCell() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var prevThreshold = StepCache.Instance.MissPromotionThreshold; - StepCache.Instance.MissPromotionThreshold = 1; - var map = Map.Maps[MapId]; - Assert.NotNull(map); - - // Open area with lateral room to flank a 7x7 house (verified reachable all 8 dirs). - const int hx = 1480, hy = 1620; - const int sx = 1480, sy = 1630; - const int gx = 1480, gy = 1610; - - map.GetAverageZ(hx, hy, out _, out var hz, out _); - var multi = new TestMulti(0x74); - multi.MoveToWorld(new Point3D(hx, hy, (sbyte)hz), map); - - var wall = MultiArt.FindWallCell(multi); - Assert.True(wall.HasValue, "non-vacuity: house must have wall cells"); - - var walker = new WalkerStub(); - map.GetAverageZ(sx, sy, out _, out var sz, out _); - var start = new Point3D(sx, sy, (sbyte)sz); - var goal = new Point3D(gx, gy, (sbyte)sz); - walker.MoveToWorld(start, map); - - try - { - var path = BitmapAStarAlgorithm.Instance.Find(walker, map, start, goal); - Assert.NotNull(path); // non-vacuity: a route around must exist - Assert.NotEmpty(path); - - // Walk the path; assert it never lands on the known wall cell. - var x = sx; - var y = sy; - foreach (var dir in path) - { - CalcMoves.Offset(dir, ref x, ref y); - Assert.False(x == wall.Value.X && y == wall.Value.Y, - $"path traversed wall cell ({wall.Value.X},{wall.Value.Y})"); - } - _output.WriteLine($"around house: {path.Length} steps, avoided wall"); - } - finally - { - StepCache.Instance.MissPromotionThreshold = prevThreshold; - walker.Delete(); - multi.Delete(); - } - } - - [SkippableFact] - public void Demolish_ReopensCoveredCells() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - Assert.NotNull(map); - - const int hx = 1480, hy = 1620; - map.GetAverageZ(hx, hy, out _, out var hz, out _); - var multi = new TestMulti(0x74); - multi.MoveToWorld(new Point3D(hx, hy, (sbyte)hz), map); - - // Find a wall cell that is blocked purely by the multi — not by underlying static/land - // terrain. FindWallCell returns the first MCL-impassable cell, which may land over - // terrain that is itself impassable. Instead scan until we find one where the base - // terrain is passable so that after demolish the cell provably reopens. - var w = FindPureMultiWallCell(multi, map); - Assert.True(w.HasValue, "non-vacuity: house must have a wall cell over passable terrain"); - var walker = new WalkerStub(); - - var cell = w.Value; - - try - { - // Before demolish: every neighbour fails to step onto the wall cell (it is blocked). - var blockedBefore = NeighbourBlockedInto(map, walker, cell); - Assert.True(blockedBefore, "wall cell should block entry while the house stands"); - - multi.Delete(); - - // After demolish: the cell reverts to open ground; entry from a neighbour should now - // succeed in at least one direction. - StepCache.Instance.Clear(); - var openAfter = !NeighbourBlockedInto(map, walker, cell); - Assert.True(openAfter, $"cell ({cell.X},{cell.Y}) should reopen after demolish"); - _output.WriteLine($"demolish reopened ({cell.X},{cell.Y})"); - } - finally - { - if (!multi.Deleted) - { - multi.Delete(); - } - walker.Delete(); - } - } - - /// - /// Find the first MCL wall cell (Impassable && !Surface) over terrain that is not - /// independently blocked by land or statics. Using this instead of - /// ensures the cell reopens after demolish. - /// - private static MultiArt.Cell? FindPureMultiWallCell(BaseMulti multi, Map map) - { - var mcl = multi.Components; - for (var lx = 0; lx < mcl.Width; lx++) - { - for (var ly = 0; ly < mcl.Height; ly++) - { - var hasWall = false; - var hasSurface = false; - foreach (var t in mcl.Tiles[lx][ly]) - { - var data = TileData.ItemTable[t.ID & TileData.MaxItemValue]; - if (data.Impassable && !data.Surface) - { - hasWall = true; - } - if (data.Surface) - { - hasSurface = true; - } - } - - if (!hasWall || hasSurface) - { - continue; - } - - var wx = multi.X + mcl.Min.X + lx; - var wy = multi.Y + mcl.Min.Y + ly; - - // Check the underlying terrain is not independently impassable. - if (IsTerrainBlocked(map, wx, wy)) - { - continue; - } - - return new MultiArt.Cell(wx, wy); - } - } - return null; - } - - /// - /// Returns true if land tile or any static tile (non-multi) at (x,y) is impassable. - /// - private static bool IsTerrainBlocked(Map map, int x, int y) - { - var lt = map.Tiles.GetLandTile(x, y); - if (!lt.Ignored) - { - var landData = TileData.LandTable[lt.ID & TileData.MaxLandValue]; - if ((landData.Flags & TileFlag.Impassable) != 0) - { - return true; - } - } - - foreach (var tile in map.Tiles.GetStaticTiles(x, y)) - { - var data = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - if (data.Impassable) - { - return true; - } - } - - return false; - } - - // True if EVERY in-range neighbour fails to step into target (target is blocked). - private static bool NeighbourBlockedInto(Map map, Mobile walker, MultiArt.Cell target) - { - for (var d = 0; d < 8; d++) - { - var nx = target.X; - var ny = target.Y; - CalcMoves.Offset((Direction)((d + 4) & 7), ref nx, ref ny); - if (nx < 0 || ny < 0 || nx >= map.Width || ny >= map.Height) - { - continue; - } - map.GetAverageZ(nx, ny, out _, out var nz, out _); - walker.MoveToWorld(new Point3D(nx, ny, (sbyte)nz), map); - if (CalcMoves.CheckMovement(walker, map, walker.Location, (Direction)d, out _)) - { - var tx = nx; - var ty = ny; - CalcMoves.Offset((Direction)d, ref tx, ref ty); - if (tx == target.X && ty == target.Y) - { - return false; // an entry succeeded → not blocked - } - } - } - return true; - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiCacheUsedTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiCacheUsedTests.cs deleted file mode 100644 index fc9e58898..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiCacheUsedTests.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Server.PathAlgorithms; -using Server.Systems.FeatureFlags; -using Xunit; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class MultiCacheUsedTests -{ - private const int MapId = 1; // Trammel - private const int GuildHouseId = 0x74; - - [Fact] - public void PathNearMulti_IncrementsMultiLocalHits() - { - var map = Map.Maps[MapId]; - map.GetAverageZ(1480, 1620, out _, out var z, out _); - var houseLoc = new Point3D(1480, 1620, (sbyte)z); - var multi = new TestMulti(GuildHouseId); - - var cacheWas = ContentFeatureFlags.BitmapPathfindingCache; - ContentFeatureFlags.BitmapPathfindingCache = true; - - var mover = MultiTestSupport.GetWalkerOracle(map, new Point3D(1480, 1630, (sbyte)z)); - try - { - multi.MoveToWorld(houseLoc, map); - StepCache.Instance.Clear(); - - var before = StepCache.Instance.GetStats().MultiLocalHits; - - // Start outside, goal on the far side, forcing expansion through the multi's halo. - var start = new Point3D(1480, 1630, (sbyte)z); - var goal = new Point3D(1480, 1610, (sbyte)z); - var path = BitmapAStarAlgorithm.Instance.Find(mover, map, start, goal); - - var after = StepCache.Instance.GetStats().MultiLocalHits; - - Assert.NotNull(path); - Assert.True(after > before, $"expected MultiLocalHits to increase, before={before} after={after}"); - } - finally - { - mover.Delete(); - multi.Delete(); - ContentFeatureFlags.BitmapPathfindingCache = cacheWas; - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs deleted file mode 100644 index 8aa0b8d19..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs +++ /dev/null @@ -1,162 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Server.Items; -using Xunit; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class MultiEdgeCaseTests -{ - private const int MapId = 1; // Trammel - private const int GuildHouseId = 0x74; - - // GuildHouse placement (mirrors MultiMaskSynthesisTests) — open Trammel ground. - private const int HouseX = 1480; - private const int HouseY = 1620; - - // Open water in the south-Britain bay (mirrors BoatPathTests). - private const int BoatMultiId = 0x0; // SmallBoat North heading - private const int WaterX = 1450; - private const int WaterY = 1770; - private const sbyte DeckZ = 0; // boat deck floor tiles stand at world Z 0 (not the water avgZ) - - /// - /// Two overlapping GuildHouse multis whose footprints intersect. At the stacked cells - /// GetStaticAndMultiTiles yields tiles from BOTH multis; the synthesizer must still - /// agree with CheckMovement everywhere over the union footprint + halo. - /// - [SkippableFact] - public void OverlappingMultis_SynthesizerMatchesCheckMovement() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - - map.GetAverageZ(HouseX, HouseY, out _, out var z, out _); - var locA = new Point3D(HouseX, HouseY, (sbyte)z); - // Origins 3 tiles apart on X so the GuildHouse footprints overlap. - var locB = new Point3D(HouseX + 3, HouseY, (sbyte)z); - - var multiA = new TestMulti(GuildHouseId); - var multiB = new TestMulti(GuildHouseId); - try - { - multiA.MoveToWorld(locA, map); - multiB.MoveToWorld(locB, map); - - // Non-vacuity: prove the two footprints actually intersect at the chosen 3-tile - // separation. The per-sweep touchedMulti guard only proves each multi touched its OWN - // footprint; without this, "overlapping" would be an unverified comment. - var overlap = MultiArt.FootprintCells(multiA); - var setB = new System.Collections.Generic.HashSet(MultiArt.FootprintCells(multiB)); - overlap.RemoveAll(c => !setB.Contains(c)); - Assert.NotEmpty(overlap); // the two footprints must actually intersect, else the test is meaningless - - // The synthesizer must match the oracle over BOTH footprints (each sweep crosses - // the shared, doubly-covered cells). - MultiTestSupport.AssertSynthesizerMatchesCheckMovement(multiA, map); - MultiTestSupport.AssertSynthesizerMatchesCheckMovement(multiB, map); - } - finally - { - multiA.Delete(); - multiB.Delete(); - } - } - - /// - /// A boat placed over open water: deck surface tiles are walkable, surrounding water blocks - /// the (non-swimming) walker. Exercises the synthesizer over water-adjacent deck-edge geometry. - /// - /// - /// The boat is placed at Z=0 (the deck's world Z), NOT at the water average Z (-15 here). - /// The deck floor tiles stand at world Z 0, so a non-swimming walker only finds walkable - /// transitions when the sweep origin Z equals the deck Z. Placing at the water avgZ makes the - /// sweep vacuous ("no walkable transitions") because the deck is 15 tiles overhead and water - /// blocks the rest — that vacuity is a fixture concern, not a synthesizer divergence (the - /// synthesizer agrees with the oracle at every direction either way). - /// - [SkippableFact] - public void BoatOverWater_SynthesizerMatchesCheckMovement() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - - var boat = new TestMulti(BoatMultiId); - boat.MoveToWorld(new Point3D(WaterX, WaterY, DeckZ), map); - try - { - MultiTestSupport.AssertSynthesizerMatchesCheckMovement(boat, map); - } - finally - { - boat.Delete(); - } - } - - /// - /// Redesign a foundation in place (Internalize -> swap MCL -> MoveToWorld back, the same - /// re-registration pattern HouseFoundation uses on commit) and assert the synthesizer reads - /// the LIVE, post-redesign Components — i.e. it matches CheckMovement on the NEW shape. - /// - [SkippableFact] - public void RedesignedFoundation_SynthesizerMatchesNewFootprint() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - - var foundation = new SwappableFoundation(GuildHouseId); - map.GetAverageZ(HouseX, HouseY, out _, out var z, out _); - var loc = new Point3D(HouseX, HouseY, (sbyte)z); - foundation.MoveToWorld(loc, map); - try - { - // Redesign, RE-REGISTERED so sectors track the new footprint (model a real commit). - // Internalize() fires Map.OnLeave (removes the OLD footprint's registration); we swap - // the MCL and MoveToWorld back, firing Map.OnEnter -> AddMulti against the new shape. - foundation.Internalize(); - foundation.Redesign(MultiData.GetComponents(0x7A)); // Tower footprint (different shape) - foundation.MoveToWorld(loc, map); - - MultiTestSupport.AssertSynthesizerMatchesCheckMovement(foundation, map); - } - finally - { - if (!foundation.Deleted) - { - foundation.Delete(); - } - } - } - - /// - /// Extensible slot for repo-owner-supplied gnarly placements. The assertion already covers - /// any (map,x,y) by construction — only the coordinates need filling in. - /// - /// TODO(coords): repo owner to supply (map,x,y) for a static tree inside a footprint and a - /// dungeon cave-wall corner; add InlineData rows here — the assertion already covers them by - /// construction. (Left intentionally unhunted: do not invent tree/dungeon coords.) - /// - [SkippableTheory] - [InlineData(MapId, HouseX, HouseY)] // known-good open Trammel placement (passes today) - public void UserSuppliedScenarios_SynthesizerMatchesCheckMovement(int mapId, int x, int y) - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[mapId]; - - var multi = new TestMulti(GuildHouseId); - map.GetAverageZ(x, y, out _, out var z, out _); - multi.MoveToWorld(new Point3D(x, y, (sbyte)z), map); - try - { - MultiTestSupport.AssertSynthesizerMatchesCheckMovement(multi, map); - } - finally - { - multi.Delete(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs deleted file mode 100644 index 7487ce8de..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs +++ /dev/null @@ -1,277 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Server.Items; -using Xunit; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class MultiMaskCacheTests -{ - private const int MapId = 1; - private const int GuildHouseId = 0x74; - private const int PlaceX = 1480; - private const int PlaceY = 1620; - - [SkippableFact] - public void TryResolveCoveringMulti_FindsPlacedMulti_AndLocalIndices() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); - var loc = new Point3D(PlaceX, PlaceY, (sbyte)z); - var multi = new TestMulti(GuildHouseId); - try - { - multi.MoveToWorld(loc, map); - - Assert.True(MultiMaskCache.TryResolveCoveringMulti(map, PlaceX, PlaceY, out var found, out var lx, out var ly)); - Assert.Same(multi, found); - var mcl = multi.Components; - Assert.Equal(PlaceX - multi.X - mcl.Min.X, lx); - Assert.Equal(PlaceY - multi.Y - mcl.Min.Y, ly); - - Assert.False(MultiMaskCache.TryResolveCoveringMulti(map, PlaceX + 200, PlaceY + 200, out _, out _, out _)); - } - finally - { - multi.Delete(); - } - } - - [SkippableFact] - public void IsInteriorLocalCell_TrueDeepInside_FalseAtEdge() - { - TileDataRequirement.SkipIfMissing(); - var multi = new TestMulti(GuildHouseId); - try - { - var mcl = multi.Components; - - var foundInterior = false; - var foundEdge = false; - for (var ly = 0; ly < mcl.Height && (!foundInterior || !foundEdge); ly++) - { - for (var lx = 0; lx < mcl.Width; lx++) - { - if (mcl.Tiles[lx][ly].Length == 0) - { - continue; - } - if (MultiMaskCache.IsInteriorLocalCell(mcl, lx, ly)) - { - foundInterior = true; - } - else - { - foundEdge = true; - } - } - } - - Assert.True(foundInterior, "a guild house must have at least one interior cell"); - Assert.True(foundEdge, "a guild house must have at least one edge/perimeter cell"); - } - finally - { - multi.Delete(); - } - } - - [Fact] - public void LocalWorldZ_RoundTrips_AndPreservesMasks() - { - var world = new StepMask( - walkMask: 0b1010_1010, wetMask: 0b0101_0101, - walkZN: 10, walkZNE: 11, walkZE: 12, walkZSE: 13, walkZS: 14, walkZSW: 15, walkZW: 16, walkZNW: 17, - swimZN: -1, swimZNE: -2, swimZE: -3, swimZSE: -4, swimZS: -5, swimZSW: -6, swimZW: -7, swimZNW: -8 - ); - const int multiZ = 7; - - Assert.True(MultiMaskCache.TryToLocalZ(world, multiZ, out var local)); - var back = MultiMaskCache.ToWorldZ(local, multiZ); - - Assert.Equal(world.WalkMask, back.WalkMask); - Assert.Equal(world.WetMask, back.WetMask); - for (var d = 0; d < 8; d++) - { - Assert.Equal(world.GetWalkZ((Direction)d), back.GetWalkZ((Direction)d)); - Assert.Equal(world.GetSwimZ((Direction)d), back.GetSwimZ((Direction)d)); - } - } - - [Fact] - public void TryToLocalZ_RejectsOverflow() - { - var world = new StepMask( - walkMask: 0xFF, wetMask: 0, - walkZN: 100, walkZNE: 0, walkZE: 0, walkZSE: 0, walkZS: 0, walkZSW: 0, walkZW: 0, walkZNW: 0, - swimZN: 0, swimZNE: 0, swimZE: 0, swimZSE: 0, swimZS: 0, swimZSW: 0, swimZW: 0, swimZNW: 0 - ); - Assert.False(MultiMaskCache.TryToLocalZ(world, -100, out _)); - } - - [Fact] - public void TerrainTopBelow_TrueWhenFloorWellAboveGround() - { - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - map.GetAverageZ(PlaceX, PlaceY, out _, out var ground, out _); - - // Floor far above ground → terrain is below → guard passes. - Assert.True(MultiMaskCache.TerrainTopBelow(map, PlaceX, PlaceY, (sbyte)(ground + 50))); - // Floor at/below ground → terrain reaches the envelope → guard fails. - Assert.False(MultiMaskCache.TerrainTopBelow(map, PlaceX, PlaceY, (sbyte)(ground - 50))); - } - - [SkippableFact] - public void PathThroughHouseInterior_IncrementsMultiMaskCacheHits() - { - TileDataRequirement.SkipIfMissing(); - // (PlaceX,PlaceY)=(1480,1620) is cluttered (footprint overlaps tall map statics → dirty), so it - // would never serve the interior cache under the footprint-clean gate. Use a known flat/clear - // spot so a house there is footprint-clean and its interior cells serve from the cache. - const int CleanX = 1560; - const int CleanY = 1616; - - var map = Map.Maps[MapId]; - map.GetAverageZ(CleanX, CleanY, out _, out var z, out _); - var houseLoc = new Point3D(CleanX, CleanY, (sbyte)z); - var multi = new TestMulti(GuildHouseId); - var cacheWas = Server.Systems.FeatureFlags.ContentFeatureFlags.BitmapPathfindingCache; - Server.Systems.FeatureFlags.ContentFeatureFlags.BitmapPathfindingCache = true; - var mover = MultiTestSupport.GetWalkerOracle(map, new Point3D(CleanX, CleanY + 10, (sbyte)z)); - try - { - multi.MoveToWorld(houseLoc, map); - Assert.True(MultiMaskCache.ComputeFootprintClean(map, multi), - "precondition: (1560,1616) must be footprint-clean for the cache to serve"); - StepCache.Instance.Clear(); - - var start = new Point3D(CleanX, CleanY + 10, (sbyte)z); - var goal = new Point3D(CleanX, CleanY - 10, (sbyte)z); - // First pass builds the interior cache (live synth); second pass should hit it. - Server.PathAlgorithms.BitmapAStarAlgorithm.Instance.Find(mover, map, start, goal); - - var before = StepCache.Instance.GetStats().MultiMaskCacheHits; - Server.PathAlgorithms.BitmapAStarAlgorithm.Instance.Find(mover, map, start, goal); - var after = StepCache.Instance.GetStats().MultiMaskCacheHits; - - Assert.True(after > before, $"expected MultiMaskCacheHits to increase, before={before} after={after}"); - } - finally - { - mover.Delete(); - multi.Delete(); - Server.Systems.FeatureFlags.ContentFeatureFlags.BitmapPathfindingCache = cacheWas; - } - } - - [Fact] - public void ClutteredHouse_DoesNotServeCache_ButStillPaths() - { - var map = Map.Maps[MapId]; - map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); - var multi = new TestMulti(GuildHouseId); - var cacheWas = Server.Systems.FeatureFlags.ContentFeatureFlags.BitmapPathfindingCache; - Server.Systems.FeatureFlags.ContentFeatureFlags.BitmapPathfindingCache = true; - var mover = MultiTestSupport.GetWalkerOracle(map, new Point3D(PlaceX, PlaceY + 10, (sbyte)z)); - try - { - // (1480,1620) overlaps tall map statics → footprint dirty → cache must NOT serve. - multi.MoveToWorld(new Point3D(PlaceX, PlaceY, (sbyte)z), map); - Assert.False(MultiMaskCache.ComputeFootprintClean(map, multi), - "precondition: (1480,1620) must be footprint-dirty for this test to be meaningful"); - StepCache.Instance.Clear(); - - var start = new Point3D(PlaceX, PlaceY + 10, (sbyte)z); - var goal = new Point3D(PlaceX, PlaceY - 10, (sbyte)z); - Server.PathAlgorithms.BitmapAStarAlgorithm.Instance.Find(mover, map, start, goal); // warm - var before = StepCache.Instance.GetStats().MultiMaskCacheHits; - var path = Server.PathAlgorithms.BitmapAStarAlgorithm.Instance.Find(mover, map, start, goal); - var after = StepCache.Instance.GetStats().MultiMaskCacheHits; - - Assert.NotNull(path); // pathfinding still works (degraded to live-synth) - Assert.Equal(before, after); // dirty footprint → zero cache serves - } - finally - { - mover.Delete(); - multi.Delete(); - Server.Systems.FeatureFlags.ContentFeatureFlags.BitmapPathfindingCache = cacheWas; - } - } - - [Fact] - public void PathInteriorCacheState_ResetsOnMove() - { - var map = Map.Maps[MapId]; - var multi = new TestMulti(GuildHouseId); - try - { - multi.MoveToWorld(new Point3D(PlaceX, PlaceY, 0), map); - multi.PathInteriorCacheState = MultiInteriorCacheState.Clean; - - // A move changes the footprint's world terrain, so the gate must reset to Unknown and - // recompute on next use. (BaseMulti.OnLocationChange does this; subclasses like BaseHouse - // and BaseBoat must call base for it to fire — this pins that contract.) - multi.MoveToWorld(new Point3D(PlaceX + 8, PlaceY + 8, 0), map); - Assert.Equal(MultiInteriorCacheState.Unknown, multi.PathInteriorCacheState); - } - finally - { - multi.Delete(); - } - } - - [Fact] - public void PathInteriorCacheState_ResetsOnItemIdChange() - { - var map = Map.Maps[MapId]; - var multi = new TestMulti(GuildHouseId); - try - { - multi.MoveToWorld(new Point3D(PlaceX, PlaceY, 0), map); - multi.PathInteriorCacheState = MultiInteriorCacheState.Clean; - - // Changing ItemID swaps the footprint (e.g. a boat changing heading), so the gate must - // reset to recompute cleanliness for the new shape. - multi.ItemID = 0x7A; // Tower - Assert.Equal(MultiInteriorCacheState.Unknown, multi.PathInteriorCacheState); - } - finally - { - multi.Delete(); - } - } - - [SkippableFact] - public void ComputeFootprintClean_TrueAtNormalPlacement_FalseWhenSunk() - { - TileDataRequirement.SkipIfMissing(); - // (PlaceX,PlaceY) is a cluttered spot whose footprint overlaps tall map statics, so a guild - // house there is never footprint-clean. Use a known flat/clear spot for the clean assertion. - const int CleanX = 1560; - const int CleanY = 1616; - - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - map.GetAverageZ(CleanX, CleanY, out _, out var ground, out _); - - var normal = new TestMulti(GuildHouseId); - var sunk = new TestMulti(GuildHouseId); - try - { - normal.MoveToWorld(new Point3D(CleanX, CleanY, (sbyte)ground), map); - Assert.True(MultiMaskCache.ComputeFootprintClean(map, normal)); - - sunk.MoveToWorld(new Point3D(CleanX + 60, CleanY, (sbyte)(ground - 40)), map); - Assert.False(MultiMaskCache.ComputeFootprintClean(map, sunk)); - } - finally - { - normal.Delete(); - sunk.Delete(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskSynthesisTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskSynthesisTests.cs deleted file mode 100644 index b415ec3d6..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskSynthesisTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Xunit; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class MultiMaskSynthesisTests -{ - private const int MapId = 1; // Trammel - private const int GuildHouseId = 0x74; // static house: walls, door aperture, floor - private const int PlaceX = 1480; - private const int PlaceY = 1620; - - [SkippableFact] - public void ComputeMultiMaskAt_MatchesCheckMovement_OverFootprintAndHalo() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); - var loc = new Point3D(PlaceX, PlaceY, (sbyte)z); - - var multi = new TestMulti(GuildHouseId); - try - { - multi.MoveToWorld(loc, map); - MultiTestSupport.AssertSynthesizerMatchesCheckMovement(multi, map); - } - finally - { - multi.Delete(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiPathInvariantTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiPathInvariantTests.cs deleted file mode 100644 index 9a920282f..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiPathInvariantTests.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Server.Items; -using Server.Mobiles; -using Server.PathAlgorithms; -using Server.Systems.FeatureFlags; -using Xunit; -using Xunit.Abstractions; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class MultiPathInvariantTests -{ - private readonly ITestOutputHelper _output; - - public MultiPathInvariantTests(ITestOutputHelper output) => _output = output; - - private const int MapId = 1; - - private sealed class WalkerStub : Mobile - { - public WalkerStub() => Body = 0xC9; - } - - private static Direction[] FindWithFlag(Mobile m, Map map, Point3D start, Point3D goal, bool cacheOn) - { - var prev = ContentFeatureFlags.BitmapPathfindingCache; - try - { - ContentFeatureFlags.BitmapPathfindingCache = cacheOn; - StepCache.Instance.Clear(); - StepCache.Instance.MissPromotionThreshold = 1; - return BitmapAStarAlgorithm.Instance.Find(m, map, start, goal); - } - finally - { - ContentFeatureFlags.BitmapPathfindingCache = prev; - } - } - - [Theory] - // start, goal: straddle a house placed between them (house at ~ midpoint). - [InlineData(1500, 1600, 1500, 1612)] // N-S across the footprint - [InlineData(1494, 1606, 1512, 1606)] // E-W across the footprint - public void Find_CacheOn_EqualsCacheOff_WithHousePresent(int sx, int sy, int gx, int gy) - { - var map = Map.Maps[MapId]; - Assert.NotNull(map); - - // Place the house at the midpoint so it sits between start and goal. - var hx = (sx + gx) / 2; - var hy = (sy + gy) / 2; - map.GetAverageZ(hx, hy, out _, out var hz, out _); - var multi = new TestMulti(0x74); - multi.MoveToWorld(new Point3D(hx, hy, (sbyte)hz), map); - - var walker = new WalkerStub(); - map.GetAverageZ(sx, sy, out _, out var sz, out _); - var start = new Point3D(sx, sy, (sbyte)sz); - var goal = new Point3D(gx, gy, (sbyte)sz); - walker.MoveToWorld(start, map); - - try - { - var on = FindWithFlag(walker, map, start, goal, cacheOn: true); - var off = FindWithFlag(walker, map, start, goal, cacheOn: false); - - // Both-null or both-equal arrays. Equality of the direction sequence is the invariant. - Assert.Equal(off == null, on == null); - if (on != null) - { - Assert.Equal(off, on); - _output.WriteLine($"({sx},{sy})->({gx},{gy}) house: {on.Length} steps, cache==slow"); - } - else - { - _output.WriteLine($"({sx},{sy})->({gx},{gy}) house: no path (both)"); - } - } - finally - { - walker.Delete(); - multi.Delete(); - } - } - - [Theory] - [InlineData(1500, 1600, 1498, 1598)] - [InlineData(1500, 1600, 1497, 1599)] - public void Find_CacheOn_EqualsCacheOff_NoMultiControl(int sx, int sy, int gx, int gy) - { - var map = Map.Maps[MapId]; - var walker = new WalkerStub(); - map.GetAverageZ(sx, sy, out _, out var sz, out _); - var start = new Point3D(sx, sy, (sbyte)sz); - var goal = new Point3D(gx, gy, (sbyte)sz); - walker.MoveToWorld(start, map); - - try - { - var on = FindWithFlag(walker, map, start, goal, cacheOn: true); - var off = FindWithFlag(walker, map, start, goal, cacheOn: false); - Assert.Equal(off == null, on == null); - if (on != null) - { - Assert.Equal(off, on); - } - } - finally - { - walker.Delete(); - } - } - - [Fact] - public void Find_CacheOn_EqualsCacheOff_RoutesAroundHouse() - { - var map = Map.Maps[MapId]; - Assert.NotNull(map); - - // Open area at (1480,1620,z=20): paths exist in all 8 directions (probe confirmed). - // N-S route: start=(1480,1630) goal=(1480,1610). House at (1480,1620) on direct line. - // Detour exists E (~1488+) and W (~1472-). Both cache-on and cache-off must agree. - const int hx = 1480, hy = 1620; - const int sx = 1480, sy = 1630; - const int gx = 1480, gy = 1610; - - map.GetAverageZ(hx, hy, out _, out var hz, out _); - var multi = new TestMulti(0x74); - multi.MoveToWorld(new Point3D(hx, hy, (sbyte)hz), map); - - var walker = new WalkerStub(); - map.GetAverageZ(sx, sy, out _, out var sz, out _); - map.GetAverageZ(gx, gy, out _, out var gz, out _); - var start = new Point3D(sx, sy, (sbyte)sz); - var goal = new Point3D(gx, gy, (sbyte)gz); - walker.MoveToWorld(start, map); - - try - { - var on = FindWithFlag(walker, map, start, goal, cacheOn: true); - var off = FindWithFlag(walker, map, start, goal, cacheOn: false); - - // Non-vacuity: a real around-the-house route must exist on BOTH sides. - Assert.NotNull(off); - Assert.NotNull(on); - // The invariant: cache and slow path agree on that route. - Assert.Equal(off, on); - - _output.WriteLine($"around-house: {on.Length} steps, cache==slow"); - } - finally - { - walker.Delete(); - multi.Delete(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiSplitRoutingTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiSplitRoutingTests.cs deleted file mode 100644 index 1103cdb83..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiSplitRoutingTests.cs +++ /dev/null @@ -1,91 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Server.Items; -using Xunit; -using Xunit.Abstractions; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class MultiSplitRoutingTests -{ - private readonly ITestOutputHelper _output; - - public MultiSplitRoutingTests(ITestOutputHelper output) => _output = output; - - // Open plain on Trammel used by existing pathfinding tests; room for a 7x7 house. - private const int MapId = 1; - private const int PlaceX = 1500; - private const int PlaceY = 1600; - - [SkippableFact] - public void PlacedMulti_RoutesFootprintAndHalo_ToFallthroughMulti() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - Assert.NotNull(map); - - map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); - var multi = new TestMulti(0x74); // GuildHouse footprint - multi.MoveToWorld(new Point3D(PlaceX, PlaceY, (sbyte)z), map); - - try - { - var sector = map.GetRealSector(PlaceX >> 4, PlaceY >> 4); - Assert.True(sector.HasMultis, "placing a multi must set Sector.HasMultis"); - - var cells = MultiArt.FootprintWithHalo(multi); - var checkedCovered = 0; - foreach (var c in cells) - { - if (c.X < 0 || c.Y < 0 || c.X >= map.Width || c.Y >= map.Height) - { - continue; - } - var mask = StepCache.Instance.TryGetMask(map, c.X, c.Y, (sbyte)z); - Assert.Equal(CacheHitKind.Fallthrough_Multi, mask.HitKind); - checkedCovered++; - } - - Assert.True(checkedCovered > 0, "non-vacuity: expected at least one covered cell"); - _output.WriteLine($"covered/halo cells routed to fallthrough: {checkedCovered}"); - } - finally - { - multi.Delete(); - } - } - - [Fact] - public void CleanMap_AfterMultiRemoved_ServesStaticHitAgain() - { - StepCache.Instance.Clear(); - var prevThreshold = StepCache.Instance.MissPromotionThreshold; - StepCache.Instance.MissPromotionThreshold = 1; // build on first touch - var map = Map.Maps[MapId]; - Assert.NotNull(map); - - map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); - - var multi = new TestMulti(0x74); - multi.MoveToWorld(new Point3D(PlaceX, PlaceY, (sbyte)z), map); - var sector = map.GetRealSector(PlaceX >> 4, PlaceY >> 4); - Assert.True(sector.HasMultis, "placing a multi must set Sector.HasMultis"); - - try - { - multi.Delete(); - Assert.False(sector.HasMultis, "removing the only multi must clear HasMultis"); - - // A cell that is NOT a static-fallthrough kind (e.g. off-map / multi) should now be - // eligible for a real static answer. Use the placement center, which is open plain. - var mask = StepCache.Instance.TryGetMask(map, PlaceX, PlaceY, (sbyte)z); - Assert.True(mask.IsHit, $"expected a real static answer after removal, got {mask.HitKind}"); - _output.WriteLine($"post-removal hitKind at center: {mask.HitKind}"); - } - finally - { - StepCache.Instance.MissPromotionThreshold = prevThreshold; - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiTestSupport.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiTestSupport.cs deleted file mode 100644 index 092b28711..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiTestSupport.cs +++ /dev/null @@ -1,242 +0,0 @@ -using System.Collections.Generic; -using Server; -using Server.Engines.Pathing.Cache; -using Server.Items; -using Xunit; -using CalcMoves = Server.Movement.Movement; - -namespace Server.Tests.Pathfinding; - -/// -/// Minimal concrete for tests. Walkability depends only on -/// Components (the shared MCL for the multiID) + Location, so this is a faithful stand-in -/// for any fixed-design multi (classic house, camp, boat heading) without the owning -/// house/boat machinery. Never serialized in tests. -/// -public sealed class TestMulti : BaseMulti -{ - public TestMulti(int itemID) : base(itemID) - { - } -} - -/// Default walker body, shared across pathfinding test fixtures. -public sealed class WalkerStub : Mobile -{ - public WalkerStub() => Body = 0xC9; -} - -/// -/// Stand-in for a customizable foundation: Components is a swappable MCL, exactly the -/// runtime-mutation shape HouseFoundation uses (it replaces its MCL wholesale on redesign -/// commit). Lets the test change the footprint and assert the engine reflects it without -/// driving full house placement/customization. -/// -public sealed class SwappableFoundation : BaseMulti -{ - private MultiComponentList _mcl; - - public SwappableFoundation(int baseMultiID) : base(baseMultiID) => - _mcl = MultiData.GetComponents(baseMultiID); - - public override MultiComponentList Components => _mcl; - - public void Redesign(MultiComponentList replacement) => _mcl = replacement; -} - -/// -/// Shared helpers for placing/probing multis in pathfinding tests. -/// -public static class MultiTestSupport -{ - // A default-walker oracle mobile (CanSwim=false, CantWalk=false) placed in-world so MovementImpl - // state reads are valid. Caller MUST Delete() it (do it in a finally). - public static Mobile GetWalkerOracle(Map map, Point3D loc) - { - var w = new WalkerStub(); - w.MoveToWorld(loc, map); - return w; - } - - public static bool HasMultiTileAt(BaseMulti multi, int wx, int wy) - { - var mcl = multi.Components; - var lx = wx - multi.X + mcl.Center.X; - var ly = wy - multi.Y + mcl.Center.Y; - if (lx < 0 || ly < 0 || lx >= mcl.Width || ly >= mcl.Height) - { - return false; - } - return mcl.Tiles[lx][ly].Length > 0; - } - - /// - /// Sweeps the multi's footprint + 1-cell halo and asserts the multi mask synthesizer - /// () agrees with the CheckMovement oracle - /// for all 8 directions at every cell, including the exact forward walk-Z on allowed moves. - /// Creates its own walker oracle internally and Delete()s it; the caller owns the multi. - /// - public static void AssertSynthesizerMatchesCheckMovement(BaseMulti multi, Map map) - { - var loc = new Point3D(multi.X, multi.Y, multi.Z); - var mover = GetWalkerOracle(map, loc); - try - { - var cells = MultiArt.FootprintWithHalo(multi); - Assert.NotEmpty(cells); - - var touchedMulti = 0; - var sawWalkable = 0; - var sawBlocked = 0; - - foreach (var c in cells) - { - var sourceZ = (sbyte)loc.Z; - var p = new Point3D(c.X, c.Y, sourceZ); - - if (HasMultiTileAt(multi, c.X, c.Y)) - { - touchedMulti++; - } - - var mask = StepProbe.ComputeMultiMaskAt(map, c.X, c.Y, sourceZ); - - for (var d = 0; d < 8; d++) - { - var dir = (Direction)d; - var expectWalk = CalcMoves.CheckMovement(mover, map, p, dir, out var expectZ); - - // The synthesizer reports the raw forward-cell step per direction and does NOT - // apply diagonal corner-cutting — by design, the caller ANDs the partner cells. - // CheckMovement (the oracle) DOES corner-cut. Replicate the caller's corner-cut - // on the mask so we compare like-for-like. The walker is not a player, so the - // diagonal is blocked only when BOTH orthogonal partner cells are blocked. - var forwardWalk = (mask.WalkMask & (1 << d)) != 0; - var gotWalk = forwardWalk; - var isDiagonal = (d & 0x1) == 0x1; - if (forwardWalk && isDiagonal) - { - var leftBit = (d - 1) & 0x7; - var rightBit = (d + 1) & 0x7; - var leftWalk = (mask.WalkMask & (1 << leftBit)) != 0; - var rightWalk = (mask.WalkMask & (1 << rightBit)) != 0; - if (!leftWalk && !rightWalk) - { - gotWalk = false; - } - } - - Assert.Equal(expectWalk, gotWalk); - - if (expectWalk) - { - // Z is taken from the forward cell only; corner-cut never alters newZ when - // the move is allowed. - Assert.Equal((sbyte)expectZ, mask.GetWalkZ(dir)); - sawWalkable++; - } - else - { - sawBlocked++; - } - } - } - - Assert.True(touchedMulti > 0, "sweep touched no multi-covered cells"); - Assert.True(sawWalkable > 0, "sweep observed no walkable transitions"); - Assert.True(sawBlocked > 0, "sweep observed no blocked transitions"); - } - finally - { - mover.Delete(); - } - } -} - -/// -/// Helpers that derive expected geometry from a multi's MCL art at runtime, so tests -/// encode no hardcoded cell coordinates and survive art-data changes. -/// -public static class MultiArt -{ - public readonly record struct Cell(int X, int Y); - - /// Every world cell the multi's footprint covers (Tiles stack non-empty). - public static List FootprintCells(BaseMulti multi) - { - var mcl = multi.Components; - var result = new List(); - for (var lx = 0; lx < mcl.Width; lx++) - { - for (var ly = 0; ly < mcl.Height; ly++) - { - if (mcl.Tiles[lx][ly].Length == 0) - { - continue; - } - result.Add(new Cell(multi.X + mcl.Min.X + lx, multi.Y + mcl.Min.Y + ly)); - } - } - return result; - } - - /// Footprint cells plus a 1-cell halo ring (the cells the split also routes to slow path). - public static HashSet FootprintWithHalo(BaseMulti multi) - { - var foot = FootprintCells(multi); - var set = new HashSet(); - foreach (var c in foot) - { - for (var dx = -1; dx <= 1; dx++) - { - for (var dy = -1; dy <= 1; dy++) - { - set.Add(new Cell(c.X + dx, c.Y + dy)); - } - } - } - return set; - } - - /// First world cell whose MCL stack contains an impassable, non-surface (wall) tile, or null. - public static Cell? FindWallCell(BaseMulti multi) - { - var mcl = multi.Components; - for (var lx = 0; lx < mcl.Width; lx++) - { - for (var ly = 0; ly < mcl.Height; ly++) - { - foreach (var t in mcl.Tiles[lx][ly]) - { - var data = TileData.ItemTable[t.ID & TileData.MaxItemValue]; - if (data.Impassable && !data.Surface) - { - return new Cell(multi.X + mcl.Min.X + lx, multi.Y + mcl.Min.Y + ly); - } - } - } - } - return null; - } - - /// First world cell whose MCL stack contains a walkable surface (floor) tile, or null. - public static Cell? FindFloorCell(BaseMulti multi) - { - var mcl = multi.Components; - for (var lx = 0; lx < mcl.Width; lx++) - { - for (var ly = 0; ly < mcl.Height; ly++) - { - foreach (var t in mcl.Tiles[lx][ly]) - { - var data = TileData.ItemTable[t.ID & TileData.MaxItemValue]; - if (data.Surface && !data.Impassable) - { - return new Cell(multi.X + mcl.Min.X + lx, multi.Y + mcl.Min.Y + ly); - } - } - } - } - return null; - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiWalkabilityTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiWalkabilityTests.cs deleted file mode 100644 index d8d46e7f8..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiWalkabilityTests.cs +++ /dev/null @@ -1,139 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Server.Items; -using Server.Mobiles; -using Xunit; -using Xunit.Abstractions; -using CalcMoves = Server.Movement.Movement; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class MultiWalkabilityTests -{ - private readonly ITestOutputHelper _output; - - public MultiWalkabilityTests(ITestOutputHelper output) => _output = output; - - private const int MapId = 1; - private const int PlaceX = 1500; - private const int PlaceY = 1600; - - private sealed class WalkerStub : Mobile - { - public WalkerStub() => Body = 0xC9; - } - - [SkippableFact] - public void WallCell_CannotBeEnteredFromAnyAdjacentCell() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); - - var multi = new TestMulti(0x74); - multi.MoveToWorld(new Point3D(PlaceX, PlaceY, (sbyte)z), map); - - var walker = new WalkerStub(); - walker.MoveToWorld(new Point3D(PlaceX, PlaceY, (sbyte)z), map); - - try - { - var wall = MultiArt.FindWallCell(multi); - Assert.True(wall.HasValue, "non-vacuity: house MCL must contain a wall tile"); - var w = wall.Value; - - // From each of the 8 cells surrounding the wall, try stepping in all 8 directions. - // No step may land ON the wall cell. We also require that SOME step succeeds, so a - // "zero wall entries" result reflects the wall blocking — not the walker being - // unable to move here at all (e.g. a Z mismatch blocking everything: a vacuous pass). - var entriesIntoWall = 0; - var successfulSteps = 0; - for (var around = 0; around < 8; around++) - { - var cx = w.X; - var cy = w.Y; - CalcMoves.Offset((Direction)around, ref cx, ref cy); - map.GetAverageZ(cx, cy, out _, out var cz, out _); - var from = new Point3D(cx, cy, (sbyte)cz); - - for (var d = 0; d < 8; d++) - { - if (!CalcMoves.CheckMovement(walker, map, from, (Direction)d, out _)) - { - continue; - } - - successfulSteps++; - - var tx = cx; - var ty = cy; - CalcMoves.Offset((Direction)d, ref tx, ref ty); - if (tx == w.X && ty == w.Y) - { - entriesIntoWall++; - _output.WriteLine($"UNEXPECTED entry into wall ({w.X},{w.Y}) from ({cx},{cy}) dir {d}"); - } - } - } - - Assert.True(successfulSteps > 0, "non-vacuity: walker must be able to move near the wall"); - Assert.Equal(0, entriesIntoWall); - } - finally - { - walker.Delete(); - multi.Delete(); - } - } - - [SkippableFact] - public void FloorCell_IsStandable() - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[MapId]; - map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); - - var multi = new TestMulti(0x74); - multi.MoveToWorld(new Point3D(PlaceX, PlaceY, (sbyte)z), map); - - var walker = new WalkerStub(); - - try - { - var floor = MultiArt.FindFloorCell(multi); - Assert.True(floor.HasValue, "non-vacuity: house MCL must contain a floor tile"); - var f = floor.Value; - - // Stand the walker on a cardinal neighbour of the floor cell and require at least - // one direction that successfully steps onto the floor cell. - var enteredFloor = false; - for (var d = 0; d < 8 && !enteredFloor; d++) - { - var nx = f.X; - var ny = f.Y; - CalcMoves.Offset((Direction)((d + 4) & 7), ref nx, ref ny); - map.GetAverageZ(nx, ny, out _, out var nz, out _); - walker.MoveToWorld(new Point3D(nx, ny, (sbyte)nz), map); - if (CalcMoves.CheckMovement(walker, map, walker.Location, (Direction)d, out _)) - { - var tx = nx; - var ty = ny; - CalcMoves.Offset((Direction)d, ref tx, ref ty); - if (tx == f.X && ty == f.Y) - { - enteredFloor = true; - } - } - } - - Assert.True(enteredFloor, $"expected the floor cell ({f.X},{f.Y}) to be reachable from a neighbour"); - } - finally - { - walker.Delete(); - multi.Delete(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/PathfindRecorderTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/PathfindRecorderTests.cs deleted file mode 100644 index 0930bad7a..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/PathfindRecorderTests.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System.IO; -using Server.Engines.Pathing; -using Xunit; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class PathfindRecorderTests -{ - private static string NewTempPath() => - Path.Combine(Path.GetTempPath(), $"pathfind-recorder-{System.Guid.NewGuid():N}.jsonl"); - - [Fact] - public void Disabled_RecordIfEnabled_DoesNothing() - { - var path = NewTempPath(); - PathfindRecorder.OutputPath = path; - PathfindRecorder.SetEnabled(false); - - try - { - var stub = new RecorderStub(World.NewMobile); - stub.DefaultMobileInit(); - stub.MoveToWorld(new Point3D(1500, 1600, 0), Map.Maps[1]); - - PathfindRecorder.RecordIfEnabled( - stub, Map.Maps[1], - new Point3D(1500, 1600, 0), new Point3D(1498, 1598, 0) - ); - - stub.Delete(); - Assert.False(File.Exists(path), "disabled recorder must not create the output file"); - } - finally - { - if (File.Exists(path)) { File.Delete(path); } - } - } - - [Fact] - public void Enabled_RecordIfEnabled_WritesValidJsonlLine() - { - var path = NewTempPath(); - PathfindRecorder.OutputPath = path; - PathfindRecorder.SetEnabled(true); - - try - { - Assert.True(PathfindRecorder.Enabled); - - var stub = new RecorderStub(World.NewMobile); - stub.DefaultMobileInit(); - stub.MoveToWorld(new Point3D(1500, 1600, 0), Map.Maps[1]); - - PathfindRecorder.RecordIfEnabled( - stub, Map.Maps[1], - new Point3D(1500, 1600, 5), new Point3D(1498, 1598, 5) - ); - - // Disable to flush + close before reading. - PathfindRecorder.SetEnabled(false); - stub.Delete(); - - Assert.True(File.Exists(path)); - var content = File.ReadAllText(path).TrimEnd(); - Assert.Single(content.Split('\n')); - Assert.StartsWith("{\"Name\":\"recorded\"", content); - Assert.Contains("\"MapId\":1", content); - Assert.Contains("\"StartX\":1500", content); - Assert.Contains("\"StartY\":1600", content); - Assert.Contains("\"GoalX\":1498", content); - Assert.Contains("\"GoalY\":1598", content); - Assert.Contains("\"CanSwim\":false", content); - Assert.EndsWith("}", content); - } - finally - { - PathfindRecorder.SetEnabled(false); - if (File.Exists(path)) { File.Delete(path); } - } - } - - [Fact] - public void Enabled_RecordsCapabilityFlagsFromBaseCreature() - { - var path = NewTempPath(); - PathfindRecorder.OutputPath = path; - PathfindRecorder.SetEnabled(true); - - try - { - var stub = new RecorderStub(World.NewMobile); - stub.DefaultMobileInit(); - stub.MoveToWorld(new Point3D(1500, 1600, 0), Map.Maps[1]); - stub.CanSwim = true; - - PathfindRecorder.RecordIfEnabled( - stub, Map.Maps[1], - new Point3D(1500, 1600, 0), new Point3D(1498, 1598, 0) - ); - - PathfindRecorder.SetEnabled(false); - stub.Delete(); - - var content = File.ReadAllText(path); - Assert.Contains("\"CanSwim\":true", content); - Assert.Contains("\"CanFly\":false", content); - Assert.Contains("\"CanOpenDoors\":", content); // value depends on BaseCreature defaults - Assert.Contains("\"CanMoveOverObstacles\":", content); // — same. - } - finally - { - PathfindRecorder.SetEnabled(false); - if (File.Exists(path)) { File.Delete(path); } - } - } - - [Fact] - public void SetEnabled_TogglingTwice_IsIdempotent() - { - var path = NewTempPath(); - PathfindRecorder.OutputPath = path; - - try - { - PathfindRecorder.SetEnabled(true); - Assert.True(PathfindRecorder.Enabled); - - PathfindRecorder.SetEnabled(true); // second on: no-op, no error - Assert.True(PathfindRecorder.Enabled); - - PathfindRecorder.SetEnabled(false); - Assert.False(PathfindRecorder.Enabled); - - PathfindRecorder.SetEnabled(false); // second off: no-op - Assert.False(PathfindRecorder.Enabled); - } - finally - { - PathfindRecorder.SetEnabled(false); - if (File.Exists(path)) { File.Delete(path); } - } - } - - private sealed class RecorderStub : Server.Mobiles.BaseCreature - { - public RecorderStub(Serial serial) : base(serial) => Body = 0xC9; - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/PathingTestSupport.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/PathingTestSupport.cs deleted file mode 100644 index 9027332c7..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/PathingTestSupport.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using Server.Engines.Pathing.Cache; - -namespace Server.Tests.Pathfinding; - -/// -/// Shared fixtures for the step-cache tests: the walker the parity tests measure against, the -/// cell-index arithmetic, and builders for the chunk state several tests inject by hand. -/// -internal static class PathingTestSupport -{ - /// - /// Trammel. Every seed coordinate below is a real location on it, so these tests need the - /// client's map files; they skip when those are absent. - /// - public static Map TestMap => Map.Maps[1]; - - /// - /// A cell in open Britain countryside — flat, walkable in all directions, no statics. The - /// default subject when a test needs a chunk to exist and doesn't care what's in it. - /// - public const int PlainX = 1500; - public const int PlainY = 1600; - - /// Index of world cell (x, y) within its own chunk. - public static int CellIndex(int x, int y) => ((y & 15) << 4) | (x & 15); - - /// A strata offset table with every cell marked single-Z. - public static ushort[] NoStrataOffsets() - { - var offsets = new ushort[StepChunk.CellsPerChunk]; - Array.Fill(offsets, StepChunk.NoStrata); - return offsets; - } - - /// - /// Packs a one-stratum record: a count byte, then the stratum itself. Directions not named in - /// stay at 0. Mirrors the layout StepCache.WriteStratum produces. - /// - public static byte[] OneStratum(sbyte zCenter, byte walkMask = 0, byte wetMask = 0, params sbyte[] walkZs) - { - var data = new byte[1 + StepChunk.StratumByteLength]; - data[0] = 1; // stratum count - data[1] = (byte)zCenter; - data[2] = walkMask; - data[3] = wetMask; - - // walkZ_N..NW occupy bytes 4..11; swimZ_N..NW follow at 12..19. - for (var i = 0; i < walkZs.Length && i < 8; i++) - { - data[4 + i] = (byte)walkZs[i]; - } - - return data; - } - - /// - /// The default static walker. Deriving straight from rather than - /// BaseCreature is the point: MovementImpl then sees no creature capabilities (no swim, no fly, - /// no door-opening), which is exactly the walker the cache bakes for. - /// - public sealed class StaticWalker : Mobile - { - public StaticWalker() - { - Body = 0xC9; - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileFormatTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileFormatTests.cs deleted file mode 100644 index 3fe72b74a..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileFormatTests.cs +++ /dev/null @@ -1,406 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Server.Engines.Pathing.Cache; -using Xunit; - -namespace Server.Tests.Pathfinding; - -/// -/// The .swb encoding, exercised through Write → OpenForLazy → TryReadChunk. Three transforms stack -/// in a record and each can silently corrupt the ones under it, so every chunk shape here is -/// asserted byte-identical after a round trip: -/// -/// predictive-Z — a directional-Z array that matches its prediction is omitted entirely, -/// compression — each record deflates independently, or stores raw when that doesn't shrink it, -/// compact index — the trailer carries no file offsets; the reader sums record lengths instead. -/// -/// Several tests assert on file size, because a round trip alone cannot tell you a transform ran: -/// an encoder that elided nothing and compressed nothing would still round-trip perfectly. -/// -[Collection("Sequential Pathfinding Tests")] -public class StepCacheFileFormatTests -{ - // ---- chunk builders ---- - - /// - /// Per-cell varying masks and Zs. Nothing about it is uniform or predictable, so it exercises - /// the Full record with residual arrays present. - /// - private static StepChunk VariedChunk(int seed = 0) - { - var c = new StepChunk(); - for (var i = 0; i < StepChunk.CellsPerChunk; i++) - { - c.WalkMask[i] = (byte)((i + seed) & 0xFF); - c.WetMask[i] = (byte)((i * 7 + seed) & 0xFF); - c.SourceZ[i] = (sbyte)((i + seed) % 40 - 20); - c.WalkZN[i] = (sbyte)(c.SourceZ[i] + i % 3); - c.SwimZS[i] = (sbyte)(c.SourceZ[i] - i % 2); - } - - return c; - } - - /// Every cell identical — the Uniform record, ~28 bytes on disk. - private static StepChunk UniformChunk(sbyte z = 10) - { - var c = new StepChunk(); - Array.Fill(c.WalkMask, (byte)0xC1); - Array.Fill(c.SourceZ, z); - foreach (var arr in AllBaseZArrays(c)) - { - Array.Fill(arr, z); - } - - return c; - } - - /// - /// Flat terrain, but NOT uniform: masks and SourceZ vary per cell while every directional Z - /// equals its masked prediction. That is the exact shape predictive-Z is built for, so all 16 - /// arrays must elide. It doubles as the coastline case — partial walkability, non-zero SourceZ, - /// and 0 in every blocked direction, which is where a naive (unmasked) predictor would emit a - /// -SourceZ residual on every blocked direction and elide nothing. - /// - private static StepChunk FlatFullChunk(sbyte baseZ = 10) - { - var c = new StepChunk(); - for (var i = 0; i < StepChunk.CellsPerChunk; i++) - { - c.WalkMask[i] = (byte)(i & 0xFF); - c.WetMask[i] = (byte)(~i & 0xFF); - c.SourceZ[i] = (sbyte)(baseZ + i % 7 - 3); - } - - var walk = new[] { c.WalkZN, c.WalkZNE, c.WalkZE, c.WalkZSE, c.WalkZS, c.WalkZSW, c.WalkZW, c.WalkZNW }; - var swim = new[] { c.SwimZN, c.SwimZNE, c.SwimZE, c.SwimZSE, c.SwimZS, c.SwimZSW, c.SwimZW, c.SwimZNW }; - for (var i = 0; i < StepChunk.CellsPerChunk; i++) - { - for (var b = 0; b < 8; b++) - { - walk[b][i] = (sbyte)((c.WalkMask[i] >> b & 1) != 0 ? c.SourceZ[i] : 0); - swim[b][i] = (sbyte)((c.WetMask[i] >> b & 1) != 0 ? c.SourceZ[i] : 0); - } - } - - return c; - } - - private static StepChunk WithSwimLayer(StepChunk c) - { - c.AllocateSwimLayer(); - for (var i = 0; i < StepChunk.CellsPerChunk; i++) - { - c.SwimSourceZ[i] = (sbyte)(i % 30 - 15); - c.SwimMask[i] = (byte)(i * 5 & 0xFF); - c.SwimZN_Layer[i] = (sbyte)(i % 7); - c.SwimZNW_Layer[i] = (sbyte)-(i % 4); - } - - return c; - } - - private static StepChunk WithStrataAt(StepChunk c, int cell) - { - var offsets = new ushort[StepChunk.CellsPerChunk]; - Array.Fill(offsets, StepChunk.NoStrata); - offsets[cell] = 0; - - var data = new byte[1 + StepChunk.StratumByteLength]; - data[0] = 1; - c.SetStrata(offsets, data); - - return c; - } - - private static sbyte[][] AllBaseZArrays(StepChunk c) => - [ - c.WalkZN, c.WalkZNE, c.WalkZE, c.WalkZSE, c.WalkZS, c.WalkZSW, c.WalkZW, c.WalkZNW, - c.SwimZN, c.SwimZNE, c.SwimZE, c.SwimZSE, c.SwimZS, c.SwimZSW, c.SwimZW, c.SwimZNW - ]; - - private static sbyte[][] AllSwimLayerArrays(StepChunk c) => - [ - c.SwimZN_Layer, c.SwimZNE_Layer, c.SwimZE_Layer, c.SwimZSE_Layer, - c.SwimZS_Layer, c.SwimZSW_Layer, c.SwimZW_Layer, c.SwimZNW_Layer - ]; - - // ---- round-trip plumbing ---- - - private static string Write(params (int cx, int cy, StepChunk c)[] chunks) - { - var path = Path.Combine(Path.GetTempPath(), $"swb_{Guid.NewGuid():N}.swb"); - StepCacheFile.Write(path, 1u, chunks); - return path; - } - - private static StepChunk RoundTrip(StepChunk src, out long fileLength, int cx = 3, int cy = 4) - { - var path = Write((cx, cy, src)); - try - { - fileLength = new FileInfo(path).Length; - - using var reader = StepCacheFile.OpenForLazy(path); - Assert.NotNull(reader); - - var rt = reader!.TryReadChunk(cx, cy); - Assert.NotNull(rt); - return rt!; - } - finally - { - File.Delete(path); - } - } - - private static StepChunk RoundTrip(StepChunk src) => RoundTrip(src, out _); - - private static void AssertIdentical(StepChunk expected, StepChunk actual) - { - Assert.True(expected.WalkMask.AsSpan().SequenceEqual(actual.WalkMask), "WalkMask differs"); - Assert.True(expected.WetMask.AsSpan().SequenceEqual(actual.WetMask), "WetMask differs"); - Assert.True(expected.SourceZ.AsSpan().SequenceEqual(actual.SourceZ), "SourceZ differs"); - - var ez = AllBaseZArrays(expected); - var az = AllBaseZArrays(actual); - for (var i = 0; i < ez.Length; i++) - { - Assert.True(ez[i].AsSpan().SequenceEqual(az[i]), $"base Z array {i} differs"); - } - - Assert.Equal(expected.HasSwimLayer, actual.HasSwimLayer); - if (expected.HasSwimLayer) - { - Assert.True(expected.SwimSourceZ.AsSpan().SequenceEqual(actual.SwimSourceZ), "SwimSourceZ differs"); - Assert.True(expected.SwimMask.AsSpan().SequenceEqual(actual.SwimMask), "SwimMask differs"); - - var el = AllSwimLayerArrays(expected); - var al = AllSwimLayerArrays(actual); - for (var i = 0; i < el.Length; i++) - { - Assert.True(el[i].AsSpan().SequenceEqual(al[i]), $"swim-layer Z array {i} differs"); - } - } - - Assert.True(expected.StrataData.SequenceEqual(actual.StrataData), "StrataData differs"); - } - - // ---- predictive-Z transform ---- - - [Theory] - [InlineData((sbyte)0, (sbyte)0)] - [InlineData((sbyte)10, (sbyte)10)] - [InlineData((sbyte)0, (sbyte)10)] - [InlineData((sbyte)10, (sbyte)0)] - [InlineData((sbyte)-20, (sbyte)15)] - [InlineData(sbyte.MinValue, sbyte.MaxValue)] - [InlineData(sbyte.MaxValue, sbyte.MinValue)] - [InlineData(sbyte.MinValue, (sbyte)1)] - [InlineData((sbyte)127, (sbyte)-1)] - public void Residual_RoundTripsLosslessly_AcrossTheFullSByteRange(sbyte z, sbyte predict) - { - var residual = StepCacheFile.EncodeResidual(z, predict); - Assert.Equal(z, StepCacheFile.DecodeZ(predict, residual)); - } - - [Theory] - [InlineData((byte)0b0000_0001, 0, (sbyte)42, (sbyte)42)] // passable -> predict SourceZ - [InlineData((byte)0b0000_0000, 0, (sbyte)42, (sbyte)0)] // blocked -> predict 0 - [InlineData((byte)0b1000_0000, 7, (sbyte)-13, (sbyte)-13)] - [InlineData((byte)0b0111_1111, 7, (sbyte)-13, (sbyte)0)] - public void Predict_IsSourceZWherePassable_ZeroWhereBlocked(byte maskByte, int bit, sbyte sourceZ, sbyte expected) - { - Assert.Equal(expected, StepCacheFile.Predict(maskByte, bit, sourceZ)); - } - - [Fact] - public void FlatChunk_ElidesEveryZArray() - { - var src = FlatFullChunk(); - var rt = RoundTrip(src, out var fileLength); - - AssertIdentical(src, rt); - - // A Full record carrying all 16 Z arrays runs past 5 KB. Landing under 1100 bytes is only - // possible if every one of them elided. - Assert.True(fileLength < 1100, $"expected every base Z array to elide; file was {fileLength} bytes"); - } - - [Fact] - public void CoastlineChunk_ElidesEveryZArray() - { - // Partial walkability with a non-zero SourceZ: the shape that defeats an unmasked predictor. - var src = FlatFullChunk(baseZ: 25); - var rt = RoundTrip(src, out var fileLength); - - AssertIdentical(src, rt); - Assert.True(fileLength < 1100, $"masked predictor should elide every array; file was {fileLength} bytes"); - } - - [Fact] - public void SlopeInOneDirection_StoresOnlyThatZArray() - { - var flat = FlatFullChunk(); - RoundTrip(flat, out var flatLength); - - // Raise WalkZN on cells walkable to the north. Exactly one array (WalkZN) now disagrees - // with its prediction; the other 15 must still elide. - var sloped = FlatFullChunk(); - for (var i = 0; i < StepChunk.CellsPerChunk; i++) - { - if ((sloped.WalkMask[i] & 1) != 0) - { - sloped.WalkZN[i]++; - } - } - - var rt = RoundTrip(sloped, out var slopedLength); - - AssertIdentical(sloped, rt); - Assert.True(slopedLength > flatLength, "a present Z array should grow the record"); - Assert.True( - slopedLength <= flatLength + StepChunk.CellsPerChunk, - $"only one 256-byte residual array should have been added; grew by {slopedLength - flatLength}" - ); - } - - // ---- compression ---- - - [Fact] - public void VariedChunk_Compresses_AndRoundTrips() - { - var src = VariedChunk(seed: 4); - var rt = RoundTrip(src, out var fileLength); - - AssertIdentical(src, rt); - - // The uncompressed Full record for a varied chunk exceeds 5 KB. - Assert.True(fileLength < 4000, $"expected compression to shrink the record; file was {fileLength} bytes"); - } - - [Fact] - public void UniformChunk_StoredRaw_RoundTrips() - { - // A Uniform body is ~28 bytes and deflate cannot shrink it, so the writer stores it raw and - // the reader has to notice that from the payload length alone. - var src = UniformChunk(z: 12); - var rt = RoundTrip(src, out var fileLength); - - AssertIdentical(src, rt); - Assert.True(fileLength < 200, $"uniform record should stay tiny; file was {fileLength} bytes"); - } - - // ---- optional trailers ---- - - [Fact] - public void SwimLayer_RoundTrips() => AssertIdentical( - WithSwimLayer(VariedChunk()), - RoundTrip(WithSwimLayer(VariedChunk())) - ); - - [Fact] - public void Strata_RoundTrips() - { - var src = WithStrataAt(VariedChunk(), cell: 10); - var rt = RoundTrip(src); - - AssertIdentical(src, rt); - Assert.True(rt.IsCellMultiZ(10)); - } - - [Fact] - public void SwimLayerAndStrata_RoundTripTogether() - { - // Both trailers present at once, which is the only case that pins their relative order. - var src = WithStrataAt(WithSwimLayer(VariedChunk()), cell: 20); - var rt = RoundTrip(src); - - AssertIdentical(src, rt); - Assert.True(rt.HasSwimLayer); - Assert.True(rt.IsCellMultiZ(20)); - } - - // ---- compact index ---- - - [Fact] - public void MultipleChunks_ResolveIndividually_FromDerivedOffsets() - { - // The index stores no offsets, so a reader that mis-sums record lengths would hand back a - // neighbouring chunk's bytes. Distinct content per coordinate is what catches that. The mix - // of record sizes matters: a raw-stored Uniform sits between two compressed Full records, - // and one coordinate is large enough to exercise the packed key's high 16 bits. - var chunks = new List<(int cx, int cy, StepChunk c)> - { - (1, 1, VariedChunk(seed: 3)), - (2, 5, UniformChunk(z: 14)), - (10, 3, VariedChunk(seed: 99)), - (300, 200, VariedChunk(seed: 17)) - }; - - var path = Write(chunks.ToArray()); - try - { - using var reader = StepCacheFile.OpenForLazy(path); - Assert.NotNull(reader); - Assert.Equal((uint)chunks.Count, reader!.ChunkCount); - - foreach (var (cx, cy, src) in chunks) - { - Assert.True(reader.Has(cx, cy), $"missing chunk ({cx},{cy})"); - - var rt = reader.TryReadChunk(cx, cy); - Assert.NotNull(rt); - AssertIdentical(src, rt!); - } - - Assert.Null(reader.TryReadChunk(7, 7)); // never written - } - finally - { - File.Delete(path); - } - } - - [Fact] - public void EmptyChunkSet_WritesAReadableFile() - { - var path = Write(); - try - { - using var reader = StepCacheFile.OpenForLazy(path); - Assert.NotNull(reader); - Assert.Equal(0u, reader!.ChunkCount); - Assert.Null(reader.TryReadChunk(0, 0)); - } - finally - { - File.Delete(path); - } - } - - // ---- version gate ---- - - [Theory] - [InlineData(0u)] - [InlineData(5u)] - [InlineData(8u)] - [InlineData(StepCacheFile.FormatVersion + 1)] - public void UnsupportedVersion_IsRejected(uint version) - { - var path = Write((0, 0, UniformChunk())); - try - { - var bytes = File.ReadAllBytes(path); - BitConverter.GetBytes(version).CopyTo(bytes, 4); // Version sits right after Magic - File.WriteAllBytes(path, bytes); - - Assert.Null(StepCacheFile.OpenForLazy(path)); - } - finally - { - File.Delete(path); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileTests.cs deleted file mode 100644 index d29b79ef9..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileTests.cs +++ /dev/null @@ -1,441 +0,0 @@ -using System; -using System.IO; -using Server.Engines.Pathing.Cache; -using Xunit; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class StepCacheFileTests -{ - /// - /// Round-trip a populated cache through a .swb file under lazy loading: build chunks, - /// save, clear (closes lazy readers + drops residents), open as lazy backing store, - /// then re-query and verify chunks come back from the file (not the runtime baker). - /// - [Fact] - public void RoundTrip_LazyLoad_PreservesChunkMaskAndZ() - { - var cache = StepCache.Instance; - cache.Clear(); - cache.MissPromotionThreshold = 1; // eager build to populate chunks for save - - var map = Map.Maps[1]; - Assert.NotNull(map); - - // Populate three distinct chunks by querying different sectors. Query at each cell's - // real standable surface Z (where the cache anchors) so first-touch yields a clean - // hit rather than an off-surface fallthrough. - var sourceQueries = new[] { (1500, 1600), (1516, 1600), (1500, 1616) }; - var standZ = new sbyte[sourceQueries.Length]; - { - Span surfZ = stackalloc sbyte[16]; - for (var i = 0; i < sourceQueries.Length; i++) - { - var (qx, qy) = sourceQueries[i]; - var n = StepProbe.ComputeStandableSurfaceZs(map, qx, qy, surfZ); - Assert.True(n > 0, $"({qx},{qy}) has no standable surface — bad test cell"); - standZ[i] = surfZ[0]; - } - } - for (var i = 0; i < sourceQueries.Length; i++) - { - var (x, y) = sourceQueries[i]; - cache.TryGetMask(map, x, y, standZ[i]); - } - - Assert.Equal(3, cache.GetStats().ResidentChunks); - Assert.Equal(3L, cache.GetStats().BuildsTotal); - - // Snapshot the answers we expect to recover after round-trip. - var expected = new StepMask[sourceQueries.Length]; - for (var i = 0; i < sourceQueries.Length; i++) - { - var (x, y) = sourceQueries[i]; - expected[i] = cache.TryGetMask(map, x, y, standZ[i]); - } - - var path = Path.Combine(Path.GetTempPath(), $"step-cache-roundtrip-{Guid.NewGuid():N}.swb"); - try - { - var written = cache.SaveToFile(path, map.MapID); - Assert.Equal(3, written); - - cache.Clear(); - Assert.Equal(0, cache.GetStats().ResidentChunks); - - Assert.True(cache.TryOpenLazyReader(path, map.MapID)); - Assert.Equal(1, cache.OpenLazyReaderCount); - - // Lazy: opening doesn't materialize chunks, so the resident set stays empty - // until we query. - Assert.Equal(0, cache.GetStats().ResidentChunks); - Assert.Equal(0L, cache.GetStats().BuildsTotal); - - // Sanity: the lazy reader's index covers each chunk we're about to query. - foreach (var (x, y) in sourceQueries) - { - Assert.True(cache.LazyReaderHasChunk(map.MapID, x >> 4, y >> 4), - $"lazy reader missing chunk ({x >> 4},{y >> 4})"); - } - - for (var i = 0; i < sourceQueries.Length; i++) - { - var (x, y) = sourceQueries[i]; - var lookup = cache.TryGetMask(map, x, y, standZ[i]); - Assert.Equal(CacheHitKind.Miss_NotBuilt, lookup.HitKind); - Assert.Equal(expected[i].WalkMask, lookup.WalkMask); - Assert.Equal(expected[i].WetMask, lookup.WetMask); - Assert.Equal(expected[i].WalkZ_N, lookup.WalkZ_N); - Assert.Equal(expected[i].WalkZ_NW, lookup.WalkZ_NW); - } - - Assert.Equal(0L, cache.GetStats().BuildsTotal); - Assert.Equal(3, cache.GetStats().ResidentChunks); - } - finally - { - cache.Clear(); // Releases the FileStream so the delete below succeeds on Windows. - if (File.Exists(path)) - { - File.Delete(path); - } - } - } - - [Fact] - public void TryOpenLazyReader_MissingFile_ReturnsFalse() - { - var cache = StepCache.Instance; - cache.Clear(); - - var path = Path.Combine(Path.GetTempPath(), $"step-cache-missing-{Guid.NewGuid():N}.swb"); - Assert.False(cache.TryOpenLazyReader(path, mapId: 1)); - Assert.Equal(0, cache.OpenLazyReaderCount); - } - - /// - /// HasLazyReader is the boot prebake's skip predicate (PathCacheCommands.Initialize): a map - /// with an open, fingerprint-valid reader needs no bake. Lock the open/clear contract. - /// - [Fact] - public void HasLazyReader_TracksOpenAndClear() - { - var cache = StepCache.Instance; - cache.Clear(); - - var map = Map.Maps[1]; - Assert.NotNull(map); - Assert.False(cache.HasLazyReader(map.MapID)); - - // Build + save a chunk so there's a valid .swb to open. - cache.MissPromotionThreshold = 1; - Span surfZ = stackalloc sbyte[16]; - Assert.True(StepProbe.ComputeStandableSurfaceZs(map, 1500, 1600, surfZ) > 0); - cache.TryGetMask(map, 1500, 1600, surfZ[0]); - - var path = Path.Combine(Path.GetTempPath(), $"step-cache-haslazy-{Guid.NewGuid():N}.swb"); - try - { - Assert.True(cache.SaveToFile(path, map.MapID) > 0); - cache.Clear(); - Assert.False(cache.HasLazyReader(map.MapID)); - - Assert.True(cache.TryOpenLazyReader(path, map.MapID)); - Assert.True(cache.HasLazyReader(map.MapID)); // open → true - - cache.Clear(); - Assert.False(cache.HasLazyReader(map.MapID)); // clear closes the reader → false - } - finally - { - cache.Clear(); - if (File.Exists(path)) - { - File.Delete(path); - } - } - } - - [Fact] - public void TryOpenLazyReader_BadMagic_ReturnsFalse() - { - var cache = StepCache.Instance; - cache.Clear(); - - var path = Path.Combine(Path.GetTempPath(), $"step-cache-badmagic-{Guid.NewGuid():N}.swb"); - try - { - File.WriteAllBytes(path, new byte[] - { - 0xDE, 0xAD, 0xBE, 0xEF, - 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }); - Assert.False(cache.TryOpenLazyReader(path, mapId: 1)); - Assert.Equal(0, cache.OpenLazyReaderCount); - } - finally - { - if (File.Exists(path)) - { - File.Delete(path); - } - } - } - - [Fact] - public void TryOpenLazyReader_FingerprintMismatch_ReturnsFalse() - { - var cache = StepCache.Instance; - cache.Clear(); - - var map = Map.Maps[1]; - cache.TryGetMask(map, 1500, 1600, sourceZ: 10); - - var path = Path.Combine(Path.GetTempPath(), $"step-cache-stalehash-{Guid.NewGuid():N}.swb"); - try - { - cache.SaveToFile(path, map.MapID); - - // Corrupt the Fingerprint field at byte offset 12 (Magic[4] + Version[4] + MapId[4]). - var bytes = File.ReadAllBytes(path); - for (var i = 12; i < 20; i++) - { - bytes[i] ^= 0xFF; - } - File.WriteAllBytes(path, bytes); - - cache.Clear(); - Assert.False(cache.TryOpenLazyReader(path, map.MapID)); - Assert.Equal(0, cache.OpenLazyReaderCount); - } - finally - { - if (File.Exists(path)) - { - File.Delete(path); - } - } - } - - /// - /// The lazy reader holds a FileStream open. Saving 100 chunks then opening them all - /// must NOT materialize any of them in the resident set — that's the whole point of - /// lazy loading on RAM-constrained shards. A query for one specific chunk pulls only - /// that one chunk into memory. - /// - [Fact] - public void LazyReader_DoesNotMaterializeUntilQueried() - { - var cache = StepCache.Instance; - cache.Clear(); - cache.MissPromotionThreshold = 1; // eager build to populate chunks for save - - var map = Map.Maps[1]; - Assert.NotNull(map); - - // Populate a handful of chunks. - var coords = new[] - { - (1500, 1600), (1516, 1600), (1500, 1616), (1516, 1616), (1532, 1600) - }; - foreach (var (x, y) in coords) - { - cache.TryGetMask(map, x, y, sourceZ: 10); - } - - var path = Path.Combine(Path.GetTempPath(), $"step-cache-lazy-{Guid.NewGuid():N}.swb"); - try - { - Assert.Equal(coords.Length, cache.SaveToFile(path, map.MapID)); - - cache.Clear(); - Assert.True(cache.TryOpenLazyReader(path, map.MapID)); - - // Open succeeded — but no chunks resident yet. - Assert.Equal(0, cache.GetStats().ResidentChunks); - - // Query one specific chunk: only that chunk lands in the resident set. - cache.TryGetMask(map, coords[0].Item1, coords[0].Item2, sourceZ: 10); - Assert.Equal(1, cache.GetStats().ResidentChunks); - Assert.Equal(0L, cache.GetStats().BuildsTotal); // resolved from file, not baker - } - finally - { - cache.Clear(); - if (File.Exists(path)) - { - File.Delete(path); - } - } - } - - /// - /// A chunk's swim layer must survive Save → Clear → LazyOpen → first-touch query. The layer is - /// an optional trailer, so a chunk that has one is the only thing that proves it is written and - /// read back rather than silently dropped. - /// - [Fact] - public void SwimLayer_RoundTrips_ThroughLazyReader() - { - var cache = StepCache.Instance; - cache.Clear(); - cache.MissPromotionThreshold = 1; - - var map = Map.Maps[1]; - Assert.NotNull(map); - - // Build a chunk and inject a synthetic swim layer onto one cell. - cache.TryGetMask(map, 1500, 1600, sourceZ: 10); - - var chunk = cache.GetResidentChunk(map.MapID, 1500 >> 4, 1600 >> 4); - Assert.NotNull(chunk); - - chunk.AllocateSwimLayer(); - var cellIndex = PathingTestSupport.CellIndex(1500, 1600); - chunk.SwimSourceZ[cellIndex] = -7; - chunk.SwimMask[cellIndex] = 0b0000_1111; - chunk.SwimZN_Layer[cellIndex] = -7; - chunk.SwimZNE_Layer[cellIndex] = -7; - chunk.SwimZE_Layer[cellIndex] = -7; - chunk.SwimZSE_Layer[cellIndex] = -7; - - var path = Path.Combine(Path.GetTempPath(), $"step-cache-swim-{Guid.NewGuid():N}.swb"); - try - { - Assert.Equal(1, cache.SaveToFile(path, map.MapID)); - - cache.Clear(); - cache.MissPromotionThreshold = 1; - Assert.True(cache.TryOpenLazyReader(path, map.MapID)); - - // Pull the chunk back via a query at swim Z; the layer must hit and serve our - // injected mask. Walk-Z query of the same cell should still hit the walk - // layer with whatever the bake produced. - var swim = cache.TryGetMask(map, 1500, 1600, sourceZ: -7); - Assert.True(swim.IsHit); - Assert.Equal((byte)0, swim.WalkMask); - Assert.Equal((byte)0b0000_1111, swim.WetMask); - Assert.Equal((sbyte)-7, swim.SwimZ_N); - Assert.Equal((sbyte)-7, swim.SwimZ_E); - } - finally - { - cache.Clear(); - if (File.Exists(path)) - { - File.Delete(path); - } - } - } - - /// - /// PreloadOnLazyOpen=true must materialize every chunk in the .swb file into the - /// resident set immediately, eliminating first-touch file-read latency. Counterpart - /// to which proves the - /// default lazy behavior. - /// - [SkippableFact] - public void TryOpenLazyReader_WithPreloadFlag_MaterializesAllChunksImmediately() - { - TileDataRequirement.SkipIfMissing(); - var cache = StepCache.Instance; - cache.Clear(); - cache.MissPromotionThreshold = 1; - - var map = Map.Maps[1]; - Assert.NotNull(map); - - var coords = new[] - { - (1500, 1600), (1516, 1600), (1500, 1616), (1516, 1616), (1532, 1600) - }; - foreach (var (x, y) in coords) - { - cache.TryGetMask(map, x, y, sourceZ: 10); - } - - var path = Path.Combine(Path.GetTempPath(), $"step-cache-preload-{Guid.NewGuid():N}.swb"); - try - { - Assert.Equal(coords.Length, cache.SaveToFile(path, map.MapID)); - - cache.Clear(); - cache.PreloadOnLazyOpen = true; - try - { - Assert.True(cache.TryOpenLazyReader(path, map.MapID)); - - // Every chunk should be resident — no further queries needed. - Assert.Equal(coords.Length, cache.GetStats().ResidentChunks); - Assert.Equal(0L, cache.GetStats().BuildsTotal); // came from file, not baker - - // Subsequent query is a clean Hit, not a Miss_NotBuilt. - var lookup = cache.TryGetMask(map, coords[0].Item1, coords[0].Item2, sourceZ: 10); - Assert.Equal(CacheHitKind.Hit, lookup.HitKind); - Assert.Equal(coords.Length, cache.GetStats().ResidentChunks); - } - finally - { - cache.PreloadOnLazyOpen = false; - } - } - finally - { - cache.Clear(); - if (File.Exists(path)) - { - File.Delete(path); - } - } - } - - /// - /// A chunk the .swb can satisfy must be served on first touch, without consulting the promotion - /// gate. This is the deployment shape where an admin ships baked files and expects the very - /// first pathfind through a region to use the cache rather than the slow path — the gate would - /// otherwise defer that first touch and defeat the whole point of shipping the bake. - /// - [SkippableFact] - public void LazyReaderHit_BypassesMissTrackerOnFirstTouch() - { - TileDataRequirement.SkipIfMissing(); - var cache = StepCache.Instance; - cache.Clear(); - cache.MissPromotionThreshold = 1; // eager build for save phase - - var map = Map.Maps[1]; - - // Build + save one chunk. - cache.TryGetMask(map, 1500, 1600, sourceZ: 10); - var path = Path.Combine(Path.GetTempPath(), $"step-cache-bypass-{Guid.NewGuid():N}.swb"); - try - { - Assert.Equal(1, cache.SaveToFile(path, map.MapID)); - - // Reset to a fresh state with the file open as a lazy reader and the deferred - // promotion threshold restored to 2. - cache.Clear(); - cache.MissPromotionThreshold = 2; - Assert.True(cache.TryOpenLazyReader(path, map.MapID)); - - // First touch must NOT return Fallthrough_NotBuilt — the lazy reader has the - // chunk and serves it before the miss tracker is consulted. - var lookup = cache.TryGetMask(map, 1500, 1600, sourceZ: 10); - Assert.True(lookup.IsHit); - Assert.Equal(CacheHitKind.Miss_NotBuilt, lookup.HitKind); - Assert.Equal(1, cache.GetStats().ResidentChunks); - Assert.Equal(0L, cache.GetStats().FallthroughNotBuilt); - Assert.Equal(0L, cache.GetStats().BuildsTotal); // came from file, not baker - } - finally - { - cache.Clear(); - if (File.Exists(path)) - { - File.Delete(path); - } - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFingerprintTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFingerprintTests.cs deleted file mode 100644 index dee698e59..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFingerprintTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Xunit; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class StepCacheFingerprintTests -{ - /// - /// Regression: the cache fingerprint must hash the on-disk tiledata.mul, NOT the mutable - /// in-memory tables. The server patches item flags/heights at runtime - /// (ItemFixes, LOSBlocker, PotionKeg, CTF, ...) at nondeterministic lifecycle points, so a - /// fingerprint taken over the live tables depended on WHEN it was computed: a runtime - /// [PathBake stamped one value into the .swb and the next startup's Initialize() recomputed a - /// different one, marking the bake stale and re-baking on every boot. Hashing the file makes - /// the fingerprint a pure function of the client's tile data, immune to those mutations. - /// - [Fact] - public void Fingerprint_IgnoresRuntimeTileDataMutation() - { - const int mapId = 1; // Trammel — loaded by the test bootstrap. - - var before = StepCacheFile.ComputeFingerprint(mapId); - - const int probeId = 0x2A0; - var original = TileData.ItemTable[probeId].Flags; - try - { - // Mutate an in-memory item flag the way ItemFixes/CTF/etc. do at runtime. XOR - // guarantees the value actually changes regardless of the current flag state. - TileData.ItemTable[probeId].Flags ^= TileFlag.NoShoot; - Assert.NotEqual(original, TileData.ItemTable[probeId].Flags); // sanity: mutation took - - var after = StepCacheFile.ComputeFingerprint(mapId); - - Assert.Equal(before, after); - } - finally - { - TileData.ItemTable[probeId].Flags = original; - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs deleted file mode 100644 index 7b630173c..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs +++ /dev/null @@ -1,424 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using System.Threading; -using Server.Engines.Pathing.Cache; -using Server.Items; -using Xunit; -using static Server.Tests.Pathfinding.PathingTestSupport; - -namespace Server.Tests.Pathfinding; - -/// -/// How the cache decides what to build, what to serve, and what to throw away: the promotion gate, -/// the four fallthrough routes out of , the strata and swim -/// layers, and LRU eviction. -/// -[Collection("Sequential Pathfinding Tests")] -public class StepCacheLifecycleTests -{ - /// Resets to a known state and returns the singleton. - private static StepCache FreshCache(int promotionThreshold) - { - var cache = StepCache.Instance; - cache.Clear(); - cache.MissPromotionThreshold = promotionThreshold; - return cache; - } - - /// Builds the plain chunk and hands it back for a test to inject state into. - private static StepChunk BuiltPlainChunk(StepCache cache, Map map) - { - cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10); - - var chunk = cache.GetResidentChunk(map.MapID, PlainX >> 4, PlainY >> 4); - Assert.NotNull(chunk); - return chunk; - } - - [Fact] - public void Clear_OnEmptyCache_LeavesStatsZero() - { - var stats = FreshCache(2).GetStats(); - - Assert.Equal(0, stats.ResidentChunks); - Assert.Equal(0L, stats.Hits); - Assert.Equal(0L, stats.BuildsTotal); - } - - // ---- promotion gate ---- - - /// - /// A chunk nothing has shown sustained interest in must not be built. The caller reads - /// IsHit=false as "use the slow path", which is the cheaper trade for a pet crossing a chunk - /// once: BuildChunk costs far more than the handful of slow-path steps it would save. - /// - [Fact] - public void FirstTouch_DefersBuild_AndFallsThrough() - { - var cache = FreshCache(promotionThreshold: 2); - var map = TestMap; - - var lookup = cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10); - - Assert.False(lookup.IsHit); - Assert.Equal(CacheHitKind.Fallthrough_NotBuilt, lookup.HitKind); - - var stats = cache.GetStats(); - Assert.Equal(0, stats.ResidentChunks); - Assert.Equal(0L, stats.BuildsTotal); - Assert.Equal(0L, stats.MissesNotBuilt); - Assert.Equal(1L, stats.FallthroughNotBuilt); - } - - [SkippableFact] - public void SecondTouchInsideWindow_PromotesAndServes() - { - TileDataRequirement.SkipIfMissing(); - - var cache = FreshCache(promotionThreshold: 2); - var map = TestMap; - - Assert.False(cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10).IsHit); - - var promoted = cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10); - Assert.True(promoted.IsHit); - Assert.Equal(CacheHitKind.Miss_NotBuilt, promoted.HitKind); - Assert.Equal((byte)0xC1, promoted.WalkMask); // pinned: open plain, walkable N/NE/... per the bake - Assert.Equal((sbyte)10, promoted.WalkZ_N); - - var stats = cache.GetStats(); - Assert.Equal(1, stats.ResidentChunks); - Assert.Equal(1L, stats.MissesNotBuilt); - Assert.Equal(1L, stats.BuildsTotal); - Assert.Equal(1L, stats.FallthroughNotBuilt); - - // Now resident: a third query is a clean hit, not another miss. - var hit = cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10); - Assert.Equal(CacheHitKind.Hit, hit.HitKind); - Assert.Equal((byte)0xC1, hit.WalkMask); - } - - /// - /// Two touches spread wider than the window are not interest, they're coincidence — a chunk - /// someone glanced through, then an unrelated creature wandering past minutes later. The count - /// restarts rather than accumulating toward a build. - /// - [Fact] - public void SecondTouchAfterWindow_RestartsTheCount() - { - var cache = FreshCache(promotionThreshold: 2); - cache.MissPromotionWindowMs = 1; - - var map = TestMap; - - Assert.False(cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10).IsHit); - Thread.Sleep(20); // outrun the window - - var second = cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10); - Assert.False(second.IsHit); - Assert.Equal(CacheHitKind.Fallthrough_NotBuilt, second.HitKind); - Assert.Equal(0, cache.GetStats().ResidentChunks); - Assert.Equal(2L, cache.GetStats().FallthroughNotBuilt); - } - - /// - /// The gate counts Finds, not probes. A single pathfind hits a chunk once per cell it expands - /// there, so counting probes would cross any threshold on the second cell and gate nothing at - /// all — the deferral would be dead code. - /// - [SkippableFact] - public void ManyProbesInOneFind_CountAsOneTouch() - { - TileDataRequirement.SkipIfMissing(); - - var cache = FreshCache(promotionThreshold: 2); - var map = TestMap; - - cache.BeginFindGeneration(); - for (var i = 0; i < 8; i++) - { - // Eight different cells, all inside the same chunk. - var lookup = cache.TryGetMask(map, PlainX + i, PlainY, sourceZ: 10); - Assert.False(lookup.IsHit); - Assert.Equal(CacheHitKind.Fallthrough_NotBuilt, lookup.HitKind); - } - - Assert.Equal(0, cache.GetStats().ResidentChunks); - Assert.Equal(0L, cache.GetStats().BuildsTotal); - Assert.Equal(8L, cache.GetStats().FallthroughNotBuilt); - - // A second Find is the second distinct touch, and crosses the threshold. - cache.BeginFindGeneration(); - var promoted = cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10); - - Assert.Equal(CacheHitKind.Miss_NotBuilt, promoted.HitKind); - Assert.Equal(1, cache.GetStats().ResidentChunks); - Assert.Equal(1L, cache.GetStats().BuildsTotal); - } - - /// Distinct Finds still don't promote if they straddle the window. - [Fact] - public void TwoFindsAcrossTheWindow_DoNotPromote() - { - var cache = FreshCache(promotionThreshold: 2); - cache.MissPromotionWindowMs = 1; - - var map = TestMap; - - cache.BeginFindGeneration(); - Assert.False(cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10).IsHit); - - Thread.Sleep(20); - - cache.BeginFindGeneration(); - var second = cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10); - - Assert.Equal(CacheHitKind.Fallthrough_NotBuilt, second.HitKind); - Assert.Equal(0, cache.GetStats().ResidentChunks); - } - - [Fact] - public void EachChunkIsTrackedSeparately() - { - var cache = FreshCache(promotionThreshold: 2); - var map = TestMap; - - // One touch each, in two different chunks: neither reaches the threshold on its own. - Assert.False(cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10).IsHit); - Assert.False(cache.TryGetMask(map, 1600, 1700, sourceZ: 10).IsHit); - - Assert.Equal(0, cache.GetStats().ResidentChunks); - Assert.Equal(2L, cache.GetStats().FallthroughNotBuilt); - } - - // ---- fallthrough routes ---- - - [Fact] - public void OffMapCell_FallsThrough() - { - var lookup = FreshCache(2).TryGetMask(TestMap, -1, -1, sourceZ: 0); - - Assert.False(lookup.IsHit); - Assert.Equal(CacheHitKind.Fallthrough_OffMap, lookup.HitKind); - Assert.Equal((byte)0, lookup.WalkMask); - } - - /// - /// A multi's cells fall through, and so does the 1-cell halo around it: a cell's mask encodes - /// the edges TO its neighbours, so a wall one cell over has to block them. - /// - [SkippableFact] - public void MultiCoveredCell_AndItsHalo_FallThrough() - { - TileDataRequirement.SkipIfMissing(); - - var cache = FreshCache(promotionThreshold: 1); - var map = TestMap; - - // A cell nowhere near a multi still serves from the static cache. - Assert.True(cache.TryGetMask(map, PlainX, PlainY, 10).IsHit); - - // Mark an isolated sector as multi-bearing. Sector.HasMultis only tests Count > 0 and the - // fallthrough never dereferences the multi, so a single null entry is enough — no real - // BaseMulti needed. - const int mx = 2000; - const int my = 2000; - var sx = mx >> 4; - var sy = my >> 4; - - var sector = map.GetRealSector(sx, sy); - var multisField = typeof(Map.Sector).GetField("_multis", BindingFlags.NonPublic | BindingFlags.Instance); - Assert.NotNull(multisField); - - var original = multisField.GetValue(sector); - try - { - multisField.SetValue(sector, new List { null }); - - // Inside the multi's sector. - Assert.Equal(CacheHitKind.Fallthrough_Multi, cache.TryGetMask(map, mx, my, 0).HitKind); - - // Last cell of the neighbouring sector: its halo reaches across the boundary. - Assert.Equal(CacheHitKind.Fallthrough_Multi, cache.TryGetMask(map, sx * 16 - 1, my, 0).HitKind); - - // One cell further out: halo no longer reaches, so the static cache handles it. - Assert.NotEqual(CacheHitKind.Fallthrough_Multi, cache.TryGetMask(map, sx * 16 - 2, my, 0).HitKind); - - Assert.True(cache.GetStats().FallthroughMulti >= 2); - } - finally - { - multisField.SetValue(sector, original); - } - } - - /// A query too far from the cell's baked Z gets no answer, rather than a wrong one. - [Fact] - public void SourceZFarFromBake_FallsThrough() - { - var cache = FreshCache(promotionThreshold: 1); - var map = TestMap; - - cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10); - var before = cache.GetStats().FallthroughSourceZMismatch; - - var lookup = cache.TryGetMask(map, PlainX, PlainY, sourceZ: 100); - - Assert.False(lookup.IsHit); - Assert.Equal(CacheHitKind.Fallthrough_SourceZMismatch, lookup.HitKind); - Assert.Equal(before + 1L, cache.GetStats().FallthroughSourceZMismatch); - } - - // ---- strata ---- - - [Fact] - public void Stratum_MatchingQueryZ_IsServed() - { - var cache = FreshCache(promotionThreshold: 1); - var map = TestMap; - var chunk = BuiltPlainChunk(cache, map); - - var offsets = NoStrataOffsets(); - offsets[CellIndex(PlainX, PlainY)] = 0; - chunk.SetStrata(offsets, OneStratum(zCenter: 42, walkMask: 0b0000_0011, walkZs: [42, 42])); - - var lookup = cache.TryGetMask(map, PlainX, PlainY, sourceZ: 42); - - Assert.True(lookup.IsHit); - Assert.Equal((byte)0b0000_0011, lookup.WalkMask); - Assert.Equal((sbyte)42, lookup.WalkZ_N); - Assert.Equal((sbyte)42, lookup.WalkZ_NE); - } - - [Fact] - public void Stratum_QueryZOutOfReach_FallsThrough() - { - var cache = FreshCache(promotionThreshold: 1); - var map = TestMap; - var chunk = BuiltPlainChunk(cache, map); - - var offsets = NoStrataOffsets(); - offsets[CellIndex(PlainX, PlainY)] = 0; - chunk.SetStrata(offsets, OneStratum(zCenter: 42)); - - // 10 is more than StepHeight from the only stratum, so nothing can answer. - var lookup = cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10); - - Assert.False(lookup.IsHit); - Assert.Equal(CacheHitKind.Fallthrough_MultiZ, lookup.HitKind); - } - - /// - /// A cell flagged multi-Z is served only from its strata. If it has none that match — here, a - /// zero-count record — it must fall through rather than quietly fall back to the main mask, - /// which was baked for a different surface. - /// - [Fact] - public void MultiZCell_WithNoUsableStratum_FallsThrough() - { - var cache = FreshCache(promotionThreshold: 1); - var map = TestMap; - var chunk = BuiltPlainChunk(cache, map); - - var before = cache.GetStats().FallthroughMultiZ; - - var offsets = NoStrataOffsets(); - offsets[CellIndex(PlainX, PlainY)] = 0; - chunk.SetStrata(offsets, [0]); // a record declaring zero strata - - var lookup = cache.TryGetMask(map, PlainX, PlainY, sourceZ: 10); - - Assert.False(lookup.IsHit); - Assert.Equal(CacheHitKind.Fallthrough_MultiZ, lookup.HitKind); - Assert.Equal(before + 1L, cache.GetStats().FallthroughMultiZ); - } - - // ---- swim layer ---- - - [Fact] - public void SwimLayer_QueryAtWaterZ_IsServedFromTheLayer() - { - var cache = FreshCache(promotionThreshold: 1); - var map = TestMap; - var chunk = BuiltPlainChunk(cache, map); - - var cell = CellIndex(PlainX, PlainY); - var bakedZ = chunk.SourceZ[cell]; - - // Place the water surface well clear of the walk surface, so the primary source-Z guard is - // guaranteed to reject the swim query and hand it to the layer. - var swimZ = (sbyte)(bakedZ - 20); - - chunk.AllocateSwimLayer(); - chunk.SwimSourceZ[cell] = swimZ; - chunk.SwimMask[cell] = 0b0000_0011; - chunk.SwimZN_Layer[cell] = swimZ; - chunk.SwimZNE_Layer[cell] = swimZ; - - // At the walk surface, the layer is not consulted at all. - Assert.Equal(CacheHitKind.Hit, cache.TryGetMask(map, PlainX, PlainY, bakedZ).HitKind); - - var swim = cache.TryGetMask(map, PlainX, PlainY, swimZ); - Assert.True(swim.IsHit); - Assert.Equal((byte)0, swim.WalkMask); // a swimmer can't walk - Assert.Equal((byte)0b0000_0011, swim.WetMask); - Assert.Equal(swimZ, swim.SwimZ_N); - Assert.Equal(swimZ, swim.SwimZ_NE); - } - - /// - /// An inland cell in a chunk that has a swim layer carries the NoSwimLayerCell sentinel. That - /// sentinel is sbyte.MinValue, so a query at sbyte.MinValue would match it exactly on a naive - /// distance check — the guard has to reject the sentinel before measuring anything. - /// - [Fact] - public void SwimLayer_SentinelCell_IsNeverMatched() - { - var cache = FreshCache(promotionThreshold: 1); - var map = TestMap; - var chunk = BuiltPlainChunk(cache, map); - - chunk.AllocateSwimLayer(); // allocated for some other cell; this one stays at the sentinel - - var cell = CellIndex(PlainX, PlainY); - Assert.Equal(StepChunk.NoSwimLayerCell, chunk.SwimSourceZ[cell]); - - var before = cache.GetStats().FallthroughSourceZMismatch; - var lookup = cache.TryGetMask(map, PlainX, PlainY, sourceZ: sbyte.MinValue); - - Assert.False(lookup.IsHit); - Assert.Equal(CacheHitKind.Fallthrough_SourceZMismatch, lookup.HitKind); - Assert.Equal(before + 1L, cache.GetStats().FallthroughSourceZMismatch); - } - - // ---- eviction ---- - - [Fact] - public void LruCap_EvictsDownToTheCap() - { - var cache = FreshCache(promotionThreshold: 1); - cache.MaxResidentChunks = 4; - - try - { - var map = TestMap; - - // Five chunks into a cache that holds four. - for (var i = 0; i < 5; i++) - { - cache.TryGetMask(map, PlainX + i * 16, PlainY, sourceZ: 10); - Thread.Sleep(2); // separate their LastTouchedTicks so LRU has something to order by - } - - cache.EnforceLruCap(); - - Assert.Equal(4, cache.GetStats().ResidentChunks); - Assert.True(cache.GetStats().EvictionsByLruCap >= 1L); - Assert.True(cache.ResidentIndexInSync(), "eviction desynced the key list from the resident set"); - } - finally - { - cache.MaxResidentChunks = 8192; - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheParityTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheParityTests.cs deleted file mode 100644 index b38c15277..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheParityTests.cs +++ /dev/null @@ -1,371 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Engines.Pathing.Cache; -using Xunit; -using Xunit.Abstractions; -using static Server.Tests.Pathfinding.PathingTestSupport; - -namespace Server.Tests.Pathfinding; - -/// -/// The cache is only worth having if it answers exactly as MovementImpl would. These tests pin -/// that down at each layer, so a failure says which one broke: -/// -/// StepProbe vs MovementImpl — does the bake compute the right answer? -/// StepCache vs StepProbe — does the chunk store and return it intact? -/// StepCache vs MovementImpl — end to end, over the states A* actually visits. -/// -/// The end-to-end test is the one that matters, but it can only tell you something is wrong; the -/// two layer tests tell you where. It also measures coverage, not just correctness — a cache that -/// falls through on everything agrees with the slow path perfectly and is worthless. -/// -[Collection("Sequential Pathfinding Tests")] -public class StepCacheParityTests -{ - private readonly ITestOutputHelper _output; - - public StepCacheParityTests(ITestOutputHelper output) => _output = output; - - // ---- layer 1: the bake agrees with MovementImpl ---- - - /// - /// Sweeps a region and compares StepProbe's mask against MovementImpl for all 8 directions. - /// The probe stores raw masks and leaves the diagonal corner-cut to the caller, so the rule has - /// to be applied here before the two are comparable. - /// - [SkippableTheory] - [InlineData("britain_inn_dense", 1480, 1610, 32)] - [InlineData("trammel_open_plain", 1500, 1600, 32)] - public void ProbeMatchesSlowPath(string label, int xStart, int yStart, int size) - { - TileDataRequirement.SkipIfMissing(); - - var map = TestMap; - Assert.NotNull(map); - - var walker = new StaticWalker(); - walker.MoveToWorld(new Point3D(xStart, yStart, 0), map); - - var disagreements = 0; - var samples = 0; - var walkable = 0; - - for (var x = xStart; x < xStart + size; x++) - { - for (var y = yStart; y < yStart + size; y++) - { - map.GetAverageZ(x, y, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - var loc = new Point3D(x, y, sourceZ); - - var probe = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - - for (var d = 0; d < 8; d++) - { - var dir = (Direction)d; - samples++; - - var slowOk = Movement.Movement.CheckMovement(walker, map, loc, dir, out var slowZ); - - // Creature corner-cut: a diagonal needs at least one flanking cardinal. - var probeOk = probe.IsWalkable(dir); - if (probeOk && (d & 1) == 1) - { - probeOk = probe.IsWalkable((Direction)((d - 1) & 7)) || probe.IsWalkable((Direction)((d + 1) & 7)); - } - - if (slowOk) - { - walkable++; - } - - if (slowOk != probeOk) - { - disagreements++; - _output.WriteLine($"WALKABLE DIFF @ ({x},{y},{sourceZ}) dir={dir} slow={slowOk} probe={probeOk}"); - } - else if (slowOk && slowZ != probe.GetWalkZ(dir)) - { - disagreements++; - _output.WriteLine($"Z DIFF @ ({x},{y},{sourceZ}) dir={dir} slow={slowZ} probe={probe.GetWalkZ(dir)}"); - } - } - } - } - - walker.Delete(); - _output.WriteLine($"[{label}] samples={samples} walkable={walkable} disagreements={disagreements}"); - - // The dense region must contain a mix. All-walkable or all-blocked would mean the sweep - // agreed about nothing interesting. - if (label == "britain_inn_dense") - { - Assert.NotEqual(0, walkable); - Assert.NotEqual(samples, walkable); - } - - Assert.Equal(0, disagreements); - } - - /// - /// The swim bake must actually produce swim output. A probe that silently returned an empty - /// WetMask everywhere would pass every parity test above — walkers would still agree — while - /// leaving every swimming creature unable to move. - /// - [SkippableFact] - public void ProbeBakesWetCells_OnAKnownCoastline() - { - TileDataRequirement.SkipIfMissing(); - - var map = TestMap; - Assert.NotNull(map); - - // South Britain into Britain bay: 64x64 straddling the Atlantic shoreline. - const int xStart = 1430; - const int yStart = 1740; - const int size = 64; - - var wetCells = 0; - for (var x = xStart; x < xStart + size; x++) - { - for (var y = yStart; y < yStart + size; y++) - { - map.GetAverageZ(x, y, out _, out var avgZ, out _); - var sourceZ = (sbyte)StepProbe.ComputeStandingZ(map, x, y, avgZ); - if (StepProbe.ComputeMaskAt(map, x, y, sourceZ).WetMask != 0) - { - wetCells++; - } - } - } - - _output.WriteLine($"south-britain coastline: wetCells={wetCells} of {size * size}"); - Assert.True(wetCells > 0, $"swim bake produced zero wet cells across a {size}x{size} coastal region"); - } - - // ---- layer 2: the chunk returns what was baked ---- - - /// - /// Sweeps a region and compares what the cache serves against what StepProbe computes for the - /// same cell. The chunk is built from the probe, so any disagreement is a storage fault — a - /// bad cell index, a Z array crossed with another, a guard firing when it shouldn't. - /// - /// Queries run at the cell's standable surface Z, which is where the cache anchors. Querying at - /// the land average instead would trip the source-Z guard on raised terrain (a causeway, a - /// walkway) and report a fallthrough that is correct behaviour rather than a fault. - /// - [Theory] - [InlineData("britain_inn_dense", 1480, 1610, 32)] - [InlineData("trammel_open_plain", 1500, 1600, 32)] - [InlineData("britain_causeway", 1475, 1641, 32)] - public void CacheMatchesProbe(string label, int xStart, int yStart, int size) - { - var cache = StepCache.Instance; - cache.Clear(); - cache.MissPromotionThreshold = 1; // build on first touch: every cell should get a real answer - - var map = TestMap; - Assert.NotNull(map); - - var disagreements = 0; - var samples = 0; - var multiZ = 0; - - Span surfaces = stackalloc sbyte[16]; - - for (var x = xStart; x < xStart + size; x++) - { - for (var y = yStart; y < yStart + size; y++) - { - if (StepProbe.ComputeStandableSurfaceZs(map, x, y, surfaces) == 0) - { - continue; // nothing for a walker to stand on here - } - - var sourceZ = surfaces[0]; - var probe = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - var cached = cache.TryGetMask(map, x, y, sourceZ); - samples++; - - if (cached.HitKind == CacheHitKind.Fallthrough_MultiZ) - { - multiZ++; - continue; - } - - Assert.True(cached.IsHit, $"cache returned {cached.HitKind} at ({x},{y})"); - - if (cached.WalkMask != probe.WalkMask) - { - disagreements++; - _output.WriteLine($"WALK MASK DIFF @ ({x},{y}) cache=0x{cached.WalkMask:X2} probe=0x{probe.WalkMask:X2}"); - continue; - } - - if (cached.WetMask != probe.WetMask) - { - disagreements++; - _output.WriteLine($"WET MASK DIFF @ ({x},{y}) cache=0x{cached.WetMask:X2} probe=0x{probe.WetMask:X2}"); - continue; - } - - for (var d = 0; d < 8; d++) - { - var dir = (Direction)d; - if (cached.GetWalkZ(dir) != probe.GetWalkZ(dir)) - { - disagreements++; - _output.WriteLine( - $"Z DIFF @ ({x},{y}) dir={dir} cache={cached.GetWalkZ(dir)} probe={probe.GetWalkZ(dir)}" - ); - break; - } - } - } - } - - _output.WriteLine($"[{label}] samples={samples} disagreements={disagreements} multiZ={multiZ}"); - - // Guard the sweep itself: if every cell fell through as multi-Z, the comparison above never - // actually ran and a zero disagreement count would mean nothing. - if (label == "britain_inn_dense") - { - Assert.True(samples - multiZ > 0, "no cell produced a real cache answer — the sweep proved nothing"); - } - - Assert.Equal(0, disagreements); - } - - // ---- layer 3: end to end, over the states A* actually visits ---- - - /// - /// Flood-fills outward from a known-walkable tile using MovementImpl itself, and demands the - /// cache serve — and agree on — every state it reaches. - /// - /// The fill is what makes this meaningful. MovementImpl returns the Z a step lands on, so each - /// reached (x, y, z) is a genuine standing state at its true Z: exactly the set A* would query, - /// discovered rather than assumed. It follows stair treads up at their own Zs and climbs onto - /// upper floors, so a single seed covers a whole connected structure with no fixed-Z guess to - /// get wrong. That matters because the failure this test exists to catch — anchoring a cell at - /// the land beneath a walkway instead of the walkway itself — is invisible to any test that - /// queries at the land Z, and turned the Britain sewer into a ~98% cache miss. - /// - /// Cardinals only: the cache stores raw masks and applies the corner-cut at query time, so a - /// raw diagonal bit legitimately differs from MovementImpl's diagonal answer. - /// - [Theory] - // Seeds chosen for the terrain classes the standable-surface bake has to get right. Each one - // floods across a wide local area, so a handful covers thousands of states without a map walk. - [InlineData("brit_sewer_walkway", 6034, 1476, 5, 2500)] // static walkway over impassable land - [InlineData("brit_inn_stairs_to_floors", 1495, 1628, 10, 2500)] // stairs up to multi-Z upper floors - [InlineData("brit_town_cobblestones", 1494, 1626, 10, 2500)] // mixed buildings, stairs, raised floors - [InlineData("trammel_open_plain", 1500, 1600, 10, 2500)] // flat ground: catches clearance false-positives - public void CacheServesReachableWalkStates(string label, int sx, int sy, int sz, int maxStates) - { - var cache = StepCache.Instance; - cache.Clear(); - cache.MissPromotionThreshold = 1; // build on first touch: every reached state should be answered - - var map = TestMap; - Assert.NotNull(map); - - var walker = new StaticWalker(); - walker.MoveToWorld(new Point3D(sx, sy, sz), map); - - var startIsWalkable = false; - for (var d = 0; d < 8 && !startIsWalkable; d++) - { - startIsWalkable = Movement.Movement.CheckMovement(walker, map, new Point3D(sx, sy, sz), (Direction)d, out _); - } - - Assert.True(startIsWalkable, $"[{label}] seed ({sx},{sy},{sz}) is not walkable — bad waypoint"); - - var visited = new HashSet<(int x, int y, int z)> { (sx, sy, sz) }; - var frontier = new Queue<(int x, int y, int z)>(); - frontier.Enqueue((sx, sy, sz)); - - var states = 0; - var fellThrough = 0; - var disagreements = 0; - const int maxLog = 12; - - while (frontier.Count > 0) - { - var (x, y, z) = frontier.Dequeue(); - var loc = new Point3D(x, y, z); - var cached = cache.TryGetMask(map, x, y, (sbyte)z); - states++; - - if (!cached.IsHit) - { - if (fellThrough < maxLog) - { - _output.WriteLine($"FELL THROUGH @ ({x},{y},{z}) hitKind={cached.HitKind}"); - } - - fellThrough++; - } - - for (var d = 0; d < 8; d++) - { - var dir = (Direction)d; - var slowOk = Movement.Movement.CheckMovement(walker, map, loc, dir, out var slowZ); - - if (slowOk) - { - var nx = x; - var ny = y; - Movement.Movement.Offset(dir, ref nx, ref ny); - - if (visited.Count < maxStates && visited.Add((nx, ny, slowZ))) - { - frontier.Enqueue((nx, ny, slowZ)); - } - } - - if ((d & 1) != 0 || !cached.IsHit) - { - continue; - } - - if (cached.IsWalkable(dir) != slowOk) - { - if (disagreements < maxLog) - { - _output.WriteLine($"WALK DIFF @ ({x},{y},{z}) dir={dir} slow={slowOk} cache={cached.IsWalkable(dir)}"); - } - - disagreements++; - } - else if (slowOk && slowZ != cached.GetWalkZ(dir)) - { - if (disagreements < maxLog) - { - _output.WriteLine($"Z DIFF @ ({x},{y},{z}) dir={dir} slow={slowZ} cache={cached.GetWalkZ(dir)}"); - } - - disagreements++; - } - } - } - - walker.Delete(); - - var fallthroughPct = states == 0 ? 0 : 100.0 * fellThrough / states; - _output.WriteLine($"[{label}] states={states} fellThrough={fellThrough} ({fallthroughPct:F2}%) disagreements={disagreements}"); - - Assert.True(states > 50, $"[{label}] flood-fill stalled at {states} states — bad waypoint"); - - // Where the cache answers at all, it must be right. - Assert.Equal(0, disagreements); - - // And it must answer nearly everywhere. A small residual is legitimate: a walkable surface - // directly beneath a bridge or stair ramp falls through because the bake's clearance check - // is deliberately conservative there. An anchor regression is not small — the pre-fix sewer - // fell through on ~98% — so a 1% ceiling separates the two comfortably. - Assert.True( - fallthroughPct < 1.0, - $"[{label}] cache fell through on {fallthroughPct:F2}% ({fellThrough}/{states}) of reachable states" - ); - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs deleted file mode 100644 index 9a32d66bf..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs +++ /dev/null @@ -1,251 +0,0 @@ -using Server.Engines.Pathing.Cache; -using Xunit; -using Xunit.Abstractions; - -namespace Server.Tests.Pathfinding; - -[Collection("Sequential Pathfinding Tests")] -public class StepProbeTests -{ - private readonly ITestOutputHelper _output; - - public StepProbeTests(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - public void ComputeMaskAt_NullMap_ReturnsDefault() - { - var result = StepProbe.ComputeMaskAt(null, 100, 100, 0); - - Assert.Equal(0, result.WalkMask); - for (var d = 0; d < 8; d++) - { - Assert.Equal(0, result.GetWalkZ((Direction)d)); - } - } - - [Fact] - public void ComputeMaskAt_InternalMap_ReturnsDefault() - { - var result = StepProbe.ComputeMaskAt(Map.Internal, 100, 100, 0); - - Assert.Equal(0, result.WalkMask); - for (var d = 0; d < 8; d++) - { - Assert.Equal(0, result.GetWalkZ((Direction)d)); - } - } - - [Fact] - public void ComputeMaskAt_TopLeftCorner_OffMapDirectionsBlocked() - { - // From source (0, 0), these neighbor cells are off-map and MUST be blocked, - // regardless of map content: - // N = (0, -1) bit 0 - // NE = (1, -1) bit 1 - // SW = (-1, 1) bit 5 - // W = (-1, 0) bit 6 - // NW = (-1, -1) bit 7 - // Bits 2 (E), 3 (SE), 4 (S) depend on map content and aren't asserted. - var map = Map.Maps[1]; - Assert.NotNull(map); - - var result = StepProbe.ComputeMaskAt(map, 0, 0, 0); - - Assert.False(result.IsWalkable(Direction.North), "N should be off-map"); - Assert.False(result.IsWalkable(Direction.Right), "NE should be off-map"); - Assert.False(result.IsWalkable(Direction.Left), "SW should be off-map"); - Assert.False(result.IsWalkable(Direction.West), "W should be off-map"); - Assert.False(result.IsWalkable(Direction.Up), "NW should be off-map"); - } - - [Fact] - public void ComputeMaskAt_BottomRightCorner_OffMapDirectionsBlocked() - { - var map = Map.Maps[1]; - Assert.NotNull(map); - - var x = map.Width - 1; - var y = map.Height - 1; - var result = StepProbe.ComputeMaskAt(map, x, y, 0); - - // From the SE corner, S/SE/SW/E/NE all go off-map (only N/NW/W in-map). - Assert.False(result.IsWalkable(Direction.Right), "NE should be off-map"); - Assert.False(result.IsWalkable(Direction.East), "E should be off-map"); - Assert.False(result.IsWalkable(Direction.Down), "SE should be off-map"); - Assert.False(result.IsWalkable(Direction.South), "S should be off-map"); - Assert.False(result.IsWalkable(Direction.Left), "SW should be off-map"); - } - - [Fact] - public void ComputeMaskAt_TrammelOpenPlain_HasFlatCellWithAllDirectionsWalkable() - { - // Invariant: somewhere in the open-plain region, there must be a fully-flat cell - // whose mask is 0xFF and all 8 destZ values equal sourceZ (no slope). - // This protects against a regression where the baker stops detecting flat cells. - var map = Map.Maps[1]; - Assert.NotNull(map); - - var found = false; - - for (var x = 1500; x < 1532 && !found; x++) - { - for (var y = 1600; y < 1632 && !found; y++) - { - map.GetAverageZ(x, y, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - var result = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - - if (result.WalkMask != 0xFF) - { - continue; - } - - var allSameZ = true; - for (var d = 0; d < 8; d++) - { - if (result.GetWalkZ((Direction)d) != sourceZ) - { - allSameZ = false; - break; - } - } - - if (allSameZ) - { - found = true; - _output.WriteLine($"Flat-open cell found at ({x}, {y}, {sourceZ})"); - } - } - } - - Assert.True(found, "Expected at least one fully-flat open cell in (1500..1532, 1600..1632)"); - } - - [SkippableFact] - public void ComputeMaskAt_BritainInnDense_HasCellWithBlockedDirections() - { - TileDataRequirement.SkipIfMissing(); - // Invariant: inside the dense Britain inn region, at least one cell must have - // at least one direction blocked by a static. Protects against a regression - // where the baker reports everything as walkable (the original false-pass bug). - var map = Map.Maps[1]; - Assert.NotNull(map); - - var blockedCellCount = 0; - - for (var x = 1480; x < 1512; x++) - { - for (var y = 1610; y < 1642; y++) - { - map.GetAverageZ(x, y, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - var result = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - - if (result.WalkMask != 0xFF) - { - blockedCellCount++; - } - } - } - - _output.WriteLine($"Cells with at least one blocked direction: {blockedCellCount} / 1024"); - Assert.True(blockedCellCount > 0, "Expected at least one cell with a blocked direction in the dense region"); - } - - [Theory] - [InlineData(1500, 1600)] - [InlineData(1480, 1610)] - [InlineData(0, 0)] - public void ComputeMaskAt_IsDeterministic(int x, int y) - { - // Same inputs must always produce identical output. Catches accidental - // statefulness in the baker (e.g., a static cache that gets corrupted). - var map = Map.Maps[1]; - Assert.NotNull(map); - - map.GetAverageZ(x, y, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - - var first = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - var second = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - var third = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - - Assert.Equal(first.WalkMask, second.WalkMask); - Assert.Equal(first.WalkMask, third.WalkMask); - for (var d = 0; d < 8; d++) - { - Assert.Equal(first.GetWalkZ((Direction)d), second.GetWalkZ((Direction)d)); - Assert.Equal(first.GetWalkZ((Direction)d), third.GetWalkZ((Direction)d)); - } - } - - [SkippableFact] - public void ComputeMaskAt_PinnedCell_TrammelOpenPlainOrigin() - { - TileDataRequirement.SkipIfMissing(); - // PINNING test: locks specific output for Trammel (1500, 1600). - // Cell at z=10 with mask 0xC1 (N + W + NW only walkable). The other five - // directions are blocked by water (E/SE/S/SW are wet tiles, NE is shore). - // If this changes, either the baker logic changed or the underlying tile - // data changed; re-run the parity test to confirm before updating expected values. - var map = Map.Maps[1]; - Assert.NotNull(map); - - map.GetAverageZ(1500, 1600, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - - var result = StepProbe.ComputeMaskAt(map, 1500, 1600, sourceZ); - - Assert.Equal((sbyte)10, sourceZ); - Assert.Equal((byte)0xC1, result.WalkMask); - - Assert.True(result.IsWalkable(Direction.North)); - Assert.False(result.IsWalkable(Direction.Right)); - Assert.False(result.IsWalkable(Direction.East)); - Assert.False(result.IsWalkable(Direction.Down)); - Assert.False(result.IsWalkable(Direction.South)); - Assert.False(result.IsWalkable(Direction.Left)); - Assert.True(result.IsWalkable(Direction.West)); - Assert.True(result.IsWalkable(Direction.Up)); - - Assert.Equal((sbyte)10, result.GetWalkZ(Direction.North)); - Assert.Equal((sbyte)10, result.GetWalkZ(Direction.West)); - Assert.Equal((sbyte)10, result.GetWalkZ(Direction.Up)); - } - - [SkippableFact] - public void ComputeMaskAt_PinnedCell_BritainInnDenseOrigin() - { - TileDataRequirement.SkipIfMissing(); - // PINNING test: locks specific output for Trammel (1480, 1610). - // Cell at z=20 with mask 0x3F (N/NE/E/SE/S/SW walkable, W/NW blocked by - // a wall to the west). All walkable directions stay flat at z=20. - var map = Map.Maps[1]; - Assert.NotNull(map); - - map.GetAverageZ(1480, 1610, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - - var result = StepProbe.ComputeMaskAt(map, 1480, 1610, sourceZ); - - Assert.Equal((sbyte)20, sourceZ); - Assert.Equal((byte)0x3F, result.WalkMask); - - Assert.True(result.IsWalkable(Direction.North)); - Assert.True(result.IsWalkable(Direction.Right)); - Assert.True(result.IsWalkable(Direction.East)); - Assert.True(result.IsWalkable(Direction.Down)); - Assert.True(result.IsWalkable(Direction.South)); - Assert.True(result.IsWalkable(Direction.Left)); - Assert.False(result.IsWalkable(Direction.West)); - Assert.False(result.IsWalkable(Direction.Up)); - - for (var d = 0; d < 6; d++) - { - Assert.Equal((sbyte)20, result.GetWalkZ((Direction)d)); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/AllSpawnFilesLoadTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/AllSpawnFilesLoadTests.cs deleted file mode 100644 index 9eb9e1909..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/AllSpawnFilesLoadTests.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Regression test: verifies that EVERY migrated spawn file deserializes and builds spawners. -// This catches any migration defects where a spawn file is syntactically correct JSON -// but fails to build spawners via DTO.ToSpawner(). Any failure names the offending file -// and is a real migration defect, not a test weakness. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; -using Server; -using Server.Engines.Spawners; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners.Json; - -[Collection("Sequential UOContent Tests")] -public class AllSpawnFilesLoadTests -{ - [Fact] - public void EverySpawnFile_DeserializesAndBuildsSpawners() - { - var root = Path.Combine(Core.BaseDirectory, "Data", "Spawns"); - if (!Directory.Exists(root)) - { - return; // distribution data not present in this checkout - } - - var failures = new List(); - var fileCount = 0; - var spawnerCount = 0; - - foreach (var file in Directory.EnumerateFiles(root, "*.json", SearchOption.AllDirectories)) - { - fileCount++; - try - { - var dtos = JsonSerializer.Deserialize>( - File.ReadAllText(file), SpawnerJsonSerializer.Options); - - if (dtos != null) - { - foreach (var dto in dtos) - { - try - { - var spawner = dto.ToSpawner(); - spawnerCount++; - spawner?.Delete(); - } - catch (Exception ex) - { - failures.Add($"{file}: ToSpawner failed: {ex.Message}"); - } - } - } - } - catch (JsonException ex) - { - failures.Add($"{file}: JSON deserialization failed: {ex.Message}"); - } - catch (Exception ex) - { - failures.Add($"{file}: {ex.GetType().Name}: {ex.Message}"); - } - } - - Assert.True( - failures.Count == 0, - $"Loaded {fileCount} files, built {spawnerCount} spawners. Failures:\n{string.Join("\n", failures)}" - ); - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/BoundsEquivalenceTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/BoundsEquivalenceTests.cs deleted file mode 100644 index 80abec869..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/BoundsEquivalenceTests.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Validates that the spawnBounds JSON emitted by tools/spawner-json-migrate/migrate.mjs -// deserializes to the SAME Rectangle3D that the runtime homeRange path produces. -// This test MUST pass before bulk-migrating the spawn data files. -// -// toBounds formula (from migrate.mjs): -// hr == 0 -> { x1:x, y1:y, z1:z, x2:x+1, y2:y+1, z2:z } -// hr > 0 -> { x1:x-hr, y1:y-hr, z1:-128, x2:x+hr+1, y2:y+hr+1, z2:128 } -// -// Rectangle3DConverter x1/y1/z1/x2/y2/z2 form creates Rectangle3D(Point3D(x1,y1,z1), Point3D(x2,y2,z2)) -// where _start==(x1,y1,z1) and _end==(x2,y2,z2). Rectangle3D.End is exclusive. - -using System.Collections.Generic; -using System.Text.Json; -using Server; -using Server.Engines.Spawners; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners.Json; - -[Collection("Sequential UOContent Tests")] -public class BoundsEquivalenceTests -{ - private const int LocX = 100; - private const int LocY = 200; - private const int LocZ = 5; - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(3)] - [InlineData(7)] - public void SpawnBoundsJson_EqualsHomeRangePath(int hr) - { - BaseSpawner fromHomeRange = null; - BaseSpawner fromSpawnBounds = null; - try - { - // Path A: legacy homeRange field -> ApplyDto sets SpawnBounds - var homeRangeJson = BuildHomeRangeJson(hr); - var dtosA = JsonSerializer.Deserialize>(homeRangeJson, SpawnerJsonSerializer.Options); - fromHomeRange = Assert.Single(dtosA).ToSpawner(); - var expected = Assert.IsType(fromHomeRange).SpawnBounds; - - // Path B: migrated spawnBounds field -> SpawnerDataDto.ToSpawner sets SpawnBounds - var spawnBoundsJson = BuildSpawnBoundsJson(hr); - var dtosB = JsonSerializer.Deserialize>(spawnBoundsJson, SpawnerJsonSerializer.Options); - fromSpawnBounds = Assert.Single(dtosB).ToSpawner(); - var actual = Assert.IsType(fromSpawnBounds).SpawnBounds; - - Assert.Equal(expected, actual); - } - finally - { - fromHomeRange?.Delete(); - fromSpawnBounds?.Delete(); - } - } - - private static string BuildHomeRangeJson(int hr) => $$""" - [ - { - "$type": "Spawner", - "location": [{{LocX}}, {{LocY}}, {{LocZ}}], - "map": "Felucca", - "count": 1, - "homeRange": {{hr}}, - "entries": [] - } - ] - """; - - private static string BuildSpawnBoundsJson(int hr) - { - int x1, y1, z1, x2, y2, z2; - if (hr == 0) - { - // hr==0: _start=(x,y,z), _end=(x+1,y+1,z) — single tile, depth 0 - (x1, y1, z1, x2, y2, z2) = (LocX, LocY, LocZ, LocX + 1, LocY + 1, LocZ); - } - else - { - // hr>0: _start=(x-hr,y-hr,-128), _end=(x+hr+1,y+hr+1,128) — hr*2+1 wide, depth 256 - (x1, y1, z1, x2, y2, z2) = (LocX - hr, LocY - hr, -128, LocX + hr + 1, LocY + hr + 1, 128); - } - - return $$""" - [ - { - "$type": "Spawner", - "location": [{{LocX}}, {{LocY}}, {{LocZ}}], - "map": "Felucca", - "count": 1, - "spawnBounds": { "x1": {{x1}}, "y1": {{y1}}, "z1": {{z1}}, "x2": {{x2}}, "y2": {{y2}}, "z2": {{z2}} }, - "entries": [] - } - ] - """; - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/ExportImportFileTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/ExportImportFileTests.cs deleted file mode 100644 index 687942a7b..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/ExportImportFileTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Server; -using Server.Engines.Spawners; -using Server.Json; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners.Json; - -[Collection("Sequential UOContent Tests")] -public class ExportImportFileTests -{ - [Fact] - public void Serialize_ThenDeserialize_File_PreservesSpawner() - { - Spawner original = null; - BaseSpawner rebuilt = null; - var path = Path.GetTempFileName(); - try - { - original = new Spawner(3, TimeSpan.FromMinutes(4), TimeSpan.FromMinutes(8), 1, - new Rectangle3D(200, 200, 0, 9, 9, 0), "Tanner"); - original.MoveToWorld(new Point3D(204, 204, 0), Map.Felucca); - - JsonConfig.Serialize(path, new List { original.ToDto() }, SpawnerJsonSerializer.Options); - - var dtos = JsonConfig.Deserialize>(path, SpawnerJsonSerializer.Options); - rebuilt = Assert.Single(dtos).ToSpawner(); - var s = Assert.IsType(rebuilt); - Assert.Equal(3, s.Count); - Assert.Equal(1, s.Team); - Assert.Equal(new Rectangle3D(200, 200, 0, 9, 9, 0), s.SpawnBounds); - } - finally - { - rebuilt?.Delete(); - original?.Delete(); - File.Delete(path); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/ImportCleanupTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/ImportCleanupTests.cs deleted file mode 100644 index a9b7554fd..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/ImportCleanupTests.cs +++ /dev/null @@ -1,200 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Server; -using Server.Engines.Spawners; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners.Json; - -[Collection("Sequential UOContent Tests")] -public class ImportCleanupTests -{ - private const string SpawnerGuid = "11111111-1111-1111-1111-111111111111"; - - [Fact] - public void Import_ValidFile_PlacesSpawner() - { - var dir = Path.Combine(Path.GetTempPath(), "muo-spawner-import-" + Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(dir); - var path = Path.Combine(dir, "test.json"); - File.WriteAllText(path, """ - [ - { - "$type": "Spawner", - "guid": "11111111-1111-1111-1111-111111111111", - "location": [305, 305, 0], - "map": "Felucca", - "count": 1, - "spawnBounds": { "x1": 300, "y1": 300, "x2": 310, "y2": 310 }, - "entries": [] - } - ] - """); - - BaseSpawner placedSpawner = null; - try - { - var all = new Dictionary(); - ImportSpawnersCommand.ImportFile(new FileInfo(path), all); - - foreach (var s in Map.Felucca.GetItemsAt(new Point3D(305, 305, 0))) - { - if (s.Guid == new Guid(SpawnerGuid)) - { - placedSpawner = s; - break; - } - } - - Assert.NotNull(placedSpawner); - Assert.True(all.ContainsKey(new Guid(SpawnerGuid))); - } - finally - { - placedSpawner?.Delete(); - if (Directory.Exists(dir)) - { - Directory.Delete(dir, true); - } - } - } - - [Fact] - public void Import_NoMap_DeletesOrphanedSpawner() - { - // A spawner with no map entry deserialized by STJ will have map == null (or Map.Internal). - // Verify it does NOT end up on any live map. - var dir = Path.Combine(Path.GetTempPath(), "muo-spawner-import-nomap-" + Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(dir); - var path = Path.Combine(dir, "test-nomap.json"); - File.WriteAllText(path, """ - [ - { - "$type": "Spawner", - "guid": "22222222-2222-2222-2222-222222222222", - "location": [400, 400, 0], - "count": 1, - "spawnBounds": { "x1": 395, "y1": 395, "x2": 405, "y2": 405 }, - "entries": [] - } - ] - """); - - try - { - var all = new Dictionary(); - ImportSpawnersCommand.ImportFile(new FileInfo(path), all); - - // The spawner must NOT be in allSpawners (rejected due to missing map). - Assert.DoesNotContain(new Guid("22222222-2222-2222-2222-222222222222"), all.Keys); - } - finally - { - if (Directory.Exists(dir)) - { - Directory.Delete(dir, true); - } - } - } - - [Fact] - public void Import_MalformedFile_LeaksNoWorldItems() - { - // First entry is syntactically valid JSON for a Spawner, second entry is truncated/garbage. - // STJ throws a JsonException during array deserialization before any Items are created - // (the DTO path is GC-only), so zero new spawners should appear at the valid entry's location. - var dir = Path.Combine(Path.GetTempPath(), "muo-import-malformed-" + Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(dir); - File.WriteAllText(Path.Combine(dir, "bad.json"), """ - [ { "$type": "Spawner", "location": [320, 320, 0], "map": "Felucca", "count": 1, - "entries": [ { "name": "Fisherman", "maxCount": 1, "probability": 100 } ] }, - { "$type": "Spawner", "location": [ THIS IS NOT JSON - """); - try - { - var before = CountSpawnersNear(new Point3D(320, 320, 0)); - ImportSpawnersCommand.ImportFile(new FileInfo(Path.Combine(dir, "bad.json")), new Dictionary()); - // Malformed parse must construct zero Items (DTO is GC-only); nothing placed or orphaned. - Assert.Equal(before, CountSpawnersNear(new Point3D(320, 320, 0))); - } - finally - { - Directory.Delete(dir, true); - } - } - - private static int CountSpawnersNear(Point3D p) - { - var n = 0; - foreach (var _ in Map.Felucca.GetItemsAt(p)) - { - n++; - } - - return n; - } - - [Fact] - public void Import_DuplicateLocation_ReplacesExistingSpawner() - { - // Verifies that an existing spawner at the same location and type is removed and replaced. - var dir = Path.Combine(Path.GetTempPath(), "muo-spawner-import-dup-" + Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(dir); - var path = Path.Combine(dir, "test-dup.json"); - - var newGuid = new Guid("33333333-3333-3333-3333-333333333333"); - // Use a non-interpolated raw string literal; the guid is a known constant so hardcode it. - File.WriteAllText(path, """ - [ - { - "$type": "Spawner", - "guid": "33333333-3333-3333-3333-333333333333", - "location": [310, 310, 0], - "map": "Felucca", - "count": 1, - "spawnBounds": { "x1": 305, "y1": 305, "x2": 315, "y2": 315 }, - "entries": [] - } - ] - """); - - // Place a pre-existing spawner at the same location. - var existing = new Spawner(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10)); - existing.MoveToWorld(new Point3D(310, 310, 0), Map.Felucca); - - BaseSpawner placedSpawner = null; - try - { - var all = new Dictionary { [existing.Guid] = existing }; - ImportSpawnersCommand.ImportFile(new FileInfo(path), all); - - // existing should have been deleted and replaced. - Assert.True(existing.Deleted); - Assert.DoesNotContain(existing.Guid, all.Keys); - - foreach (var s in Map.Felucca.GetItemsAt(new Point3D(310, 310, 0))) - { - if (s.Guid == newGuid) - { - placedSpawner = s; - break; - } - } - - Assert.NotNull(placedSpawner); - } - finally - { - if (!existing.Deleted) - { - existing.Delete(); - } - placedSpawner?.Delete(); - if (Directory.Exists(dir)) - { - Directory.Delete(dir, true); - } - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/LegacyHomeRangeTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/LegacyHomeRangeTests.cs deleted file mode 100644 index 90e66b353..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/LegacyHomeRangeTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json; -using Server; -using Server.Engines.Spawners; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners.Json; - -[Collection("Sequential UOContent Tests")] -public class LegacyHomeRangeTests -{ - [Fact] - public void HomeRange_NoSpawnBounds_ProducesCenteredBounds() - { - const string legacy = """ - [ - { - "$type": "Spawner", - "guid": "3df0543a-373c-4673-a98b-8191686f4ab3", - "location": [100, 200, 5], - "map": "Felucca", - "count": 1, - "homeRange": 3, - "entries": [ { "name": "Fisherman", "maxCount": 1, "probability": 100 } ] - } - ] - """; - - var dtos = JsonSerializer.Deserialize>(legacy, SpawnerJsonSerializer.Options); - var dto = Assert.Single(dtos); - var s = Assert.IsType(dto.ToSpawner()); - - try - { - // homeRange 3 -> Rectangle3D(100-3, 200-3, -128, 7, 7, 256) - Assert.Equal(new Rectangle3D(97, 197, -128, 7, 7, 256), s.SpawnBounds); - } - finally - { - s.Delete(); - } - } - - [Fact] - public void HomeRangeZero_ProducesSingleTileBounds() - { - const string legacy = """ - [ - { - "$type": "Spawner", - "location": [100, 200, 5], - "map": "Felucca", - "count": 1, - "homeRange": 0, - "entries": [ { "name": "Fisherman", "maxCount": 1, "probability": 100 } ] - } - ] - """; - - var dtos = JsonSerializer.Deserialize>(legacy, SpawnerJsonSerializer.Options); - var dto = Assert.Single(dtos); - var s = Assert.IsType(dto.ToSpawner()); - - try - { - // homeRange 0 -> Rectangle3D(100, 200, 5, 1, 1, 0) - Assert.Equal(new Rectangle3D(100, 200, 5, 1, 1, 0), s.SpawnBounds); - } - finally - { - s.Delete(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/MigratedDataLoadTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/MigratedDataLoadTests.cs deleted file mode 100644 index b0846a795..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/MigratedDataLoadTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Regression guard: verifies that a real migrated spawn file (post-uoml/felucca/Vendors.json) -// can be deserialized as List and each DTO produces a valid spawner via ToSpawner(). -// Exercises the $type discriminator path introduced by the data migration. - -using System.Collections.Generic; -using System.IO; -using System.Text.Json; -using Server; -using Server.Engines.Spawners; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners.Json; - -[Collection("Sequential UOContent Tests")] -public class MigratedDataLoadTests -{ - [Fact] - public void MigratedFile_LoadsWithDollarType() - { - var path = Path.Combine(Core.BaseDirectory, "Data", "Spawns", "post-uoml", "felucca", "Vendors.json"); - if (!File.Exists(path)) - { - return; // distribution data not present in this checkout - } - - var dtos = JsonSerializer.Deserialize>(File.ReadAllText(path), SpawnerJsonSerializer.Options); - Assert.NotEmpty(dtos); - - var spawners = new List(dtos.Count); - try - { - foreach (var dto in dtos) - { - spawners.Add(dto.ToSpawner()); - } - - Assert.Equal(dtos.Count, spawners.Count); - } - finally - { - foreach (var s in spawners) - { - s?.Delete(); - } - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerCompactWriterTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerCompactWriterTests.cs deleted file mode 100644 index d25abd9a0..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerCompactWriterTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json; -using Server; -using Server.Engines.Spawners; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners.Json; - -[Collection("Sequential UOContent Tests")] -public class SpawnerCompactWriterTests -{ - [Fact] - public void SerializeCompact_ProducesCompactLoadableFormat() - { - Spawner original = null; - BaseSpawner rebuilt = null; - try - { - original = new Spawner("Fisherman"); - original.MoveToWorld(new Point3D(200, 200, 0), Map.Felucca); - original.SpawnBounds = new Rectangle3D(195, 195, -128, 11, 11, 256); // == homeRange 5 - - var json = SpawnerJsonSerializer.SerializeCompact(new List { original.ToDto() }); - - // No BOM (StartsWith '[' proves it), UTF-8, LF, trailing newline. - Assert.StartsWith("[", json); - Assert.DoesNotContain("\r", json); - Assert.EndsWith("]\n", json); - - // $type first; scalar containers inline; entries (array of objects) expanded. - Assert.Contains("\"$type\": \"Spawner\"", json); - Assert.Contains("\"location\": [200, 200, 0]", json); - Assert.Contains("\"homeRange\": 5", json); - Assert.DoesNotContain("spawnBounds", json); - Assert.Contains("\"entries\": [\n", json); // array of objects -> expanded - Assert.Contains("{ \"name\": \"Fisherman\",", json); // each entry inline - - // Round-trips back to an equivalent spawner. - var dtos = JsonSerializer.Deserialize>(json, SpawnerJsonSerializer.Options); - rebuilt = Assert.Single(dtos).ToSpawner(); - Assert.Equal(new Rectangle3D(195, 195, -128, 11, 11, 256), rebuilt.SpawnBounds); - } - finally - { - rebuilt?.Delete(); - original?.Delete(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerDiscoveryValidationTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerDiscoveryValidationTests.cs deleted file mode 100644 index 5f8e80f92..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerDiscoveryValidationTests.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Engines.Spawners; -using Server.Json; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners.Json; - -public class SpawnerDiscoveryValidationTests -{ - [JsonDiscoverableType("dup")] - private sealed record DupA : SpawnerDto - { - protected override BaseSpawner CreateEmpty() => new Spawner(); - } - - [JsonDiscoverableType("dup")] - private sealed record DupB : SpawnerDto - { - protected override BaseSpawner CreateEmpty() => new Spawner(); - } - - [Fact] - public void DuplicateDiscriminator_Throws() - { - var map = new Dictionary(); - var (disc, _) = SpawnerJsonSerializer.Validate(typeof(DupA), map); - map[disc] = typeof(DupA); - - var ex = Assert.Throws(() => SpawnerJsonSerializer.Validate(typeof(DupB), map)); - Assert.Contains("discriminator 'dup'", ex.Message); - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerDtoRoundTripTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerDtoRoundTripTests.cs deleted file mode 100644 index 13ba62da3..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerDtoRoundTripTests.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json; -using Server; -using Server.Engines.Spawners; -using Server.Regions; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners.Json; - -[Collection("Sequential UOContent Tests")] -public class SpawnerDtoRoundTripTests -{ - [Fact] - public void Spawner_NonSquareBounds_WritesSpawnBounds_RoundTrips() - { - Spawner original = null; - BaseSpawner rebuilt = null; - try - { - // Bounds centered on (102,102), placed at (105,105) -> not a homeRange square. - original = new Spawner(2, TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(7), 0, - new Rectangle3D(100, 100, 0, 5, 5, 0), "Fisherman"); - original.MoveToWorld(new Point3D(105, 105, 0), Map.Felucca); - - var json = JsonSerializer.Serialize(new List { original.ToDto() }, SpawnerJsonSerializer.Options); - Assert.Contains("\"$type\": \"Spawner\"", json); - Assert.Contains("\"count\": 2", json); - Assert.Contains("spawnBounds", json); - Assert.DoesNotContain("homeRange", json); - - var dtos = JsonSerializer.Deserialize>(json, SpawnerJsonSerializer.Options); - rebuilt = Assert.Single(dtos).ToSpawner(); - var s = Assert.IsType(rebuilt); - Assert.Equal(2, s.Count); - Assert.Equal(TimeSpan.FromMinutes(3), s.MinDelay); - Assert.Equal(new Rectangle3D(100, 100, 0, 5, 5, 0), s.SpawnBounds); - Assert.Equal("Fisherman", Assert.Single(s.Entries).SpawnedName); - } - finally - { - rebuilt?.Delete(); - original?.Delete(); - } - } - - [Fact] - public void Spawner_HomeRangeSquare_WritesHomeRange_RoundTrips() - { - Spawner original = null; - BaseSpawner rebuilt = null; - try - { - original = new Spawner("Fisherman"); - original.MoveToWorld(new Point3D(200, 200, 0), Map.Felucca); - // Exactly what homeRange 5 reconstructs (centered square, standard z/depth). - original.SpawnBounds = new Rectangle3D(195, 195, -128, 11, 11, 256); - - var json = JsonSerializer.Serialize(new List { original.ToDto() }, SpawnerJsonSerializer.Options); - Assert.Contains("\"homeRange\": 5", json); - Assert.DoesNotContain("spawnBounds", json); - - var dtos = JsonSerializer.Deserialize>(json, SpawnerJsonSerializer.Options); - rebuilt = Assert.Single(dtos).ToSpawner(); - Assert.Equal(new Rectangle3D(195, 195, -128, 11, 11, 256), rebuilt.SpawnBounds); - } - finally - { - rebuilt?.Delete(); - original?.Delete(); - } - } - - [Fact] - public void Spawner_WritesMandatory_OmitsOptionalDefaults() - { - Spawner original = null; - try - { - // Default delays (5/10), team 0, default maxSpawnAttempts. - original = new Spawner("Fisherman"); - original.MoveToWorld(new Point3D(110, 110, 0), Map.Felucca); - var json = JsonSerializer.Serialize(new List { original.ToDto() }, SpawnerJsonSerializer.Options); - - // Mandatory — always written, even at default. - Assert.Contains("\"guid\":", json); - Assert.Contains("minDelay", json); - Assert.Contains("maxDelay", json); - - // Optional defaults — omitted. - Assert.DoesNotContain("\"team\"", json); - Assert.DoesNotContain("maxSpawnAttempts", json); - Assert.DoesNotContain("spawnLocationIsHome", json); - } - finally - { - original?.Delete(); - } - } - - [Fact] - public void RegionSpawner_RoundTrips_RegionByName() - { - var region = new BaseRegion("DtoTestRegion", Map.Felucca, 50, new Rectangle3D(1400, 1670, 0, 40, 40, 0)); - region.Register(); - RegionSpawner original = null; - BaseSpawner rebuilt = null; - try - { - original = new RegionSpawner("Fisherman") { SpawnRegion = region }; - original.MoveToWorld(new Point3D(1416, 1683, 0), Map.Felucca); - var json = JsonSerializer.Serialize(new List { original.ToDto() }, SpawnerJsonSerializer.Options); - Assert.Contains("\"$type\": \"RegionSpawner\"", json); - Assert.Contains("DtoTestRegion", json); - Assert.DoesNotContain("homeRange", json); - Assert.DoesNotContain("spawnBounds", json); - - var dtos = JsonSerializer.Deserialize>(json, SpawnerJsonSerializer.Options); - rebuilt = Assert.Single(dtos).ToSpawner(); - Assert.Equal("DtoTestRegion", Assert.IsType(rebuilt).SpawnRegion?.Name); - } - finally - { - rebuilt?.Delete(); - original?.Delete(); - region.Unregister(); - } - } - - [Fact] - public void ProximitySpawner_RoundTrips_Fields() - { - ProximitySpawner original = null; - BaseSpawner rebuilt = null; - try - { - original = new ProximitySpawner("Fisherman") { TriggerRange = 4, InstantFlag = true, SpawnMessage = 500000 }; - original.MoveToWorld(new Point3D(120, 120, 0), Map.Felucca); - var json = JsonSerializer.Serialize(new List { original.ToDto() }, SpawnerJsonSerializer.Options); - Assert.Contains("\"$type\": \"ProximitySpawner\"", json); - Assert.Contains("\"triggerRange\": 4", json); - Assert.Contains("\"instant\": true", json); - - var dtos = JsonSerializer.Deserialize>(json, SpawnerJsonSerializer.Options); - rebuilt = Assert.Single(dtos).ToSpawner(); - var p = Assert.IsType(rebuilt); - Assert.Equal(4, p.TriggerRange); - Assert.True(p.InstantFlag); - Assert.Equal(500000, p.SpawnMessage.Number); - } - finally - { - rebuilt?.Delete(); - original?.Delete(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/SectorSpawnCacheTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/SectorSpawnCacheTests.cs deleted file mode 100644 index bbf8e2112..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/SectorSpawnCacheTests.cs +++ /dev/null @@ -1,575 +0,0 @@ -using System.Collections.Generic; -using Server; -using Server.Engines.Spawners; -using Xunit; - -namespace UOContent.Tests; - -public class BitMask256Tests -{ - [Fact] - public void BitMask256_InitialState_AllBitsZero() - { - var mask = BitMask256.AllClear(); - - Assert.Equal(0, mask.PopCount()); - } - - [Theory] - [InlineData(0)] - [InlineData(63)] - [InlineData(64)] - [InlineData(127)] - [InlineData(128)] - [InlineData(191)] - [InlineData(192)] - [InlineData(255)] - public void BitMask256_SetBit_SetsCorrectBit(int bitIndex) - { - var mask = BitMask256.AllClear(); - - mask.SetBit(bitIndex); - - Assert.True(mask.GetBit(bitIndex)); - Assert.Equal(1, mask.PopCount()); - } - - [Fact] - public void BitMask256_SetMultipleBits_CountsCorrectly() - { - var mask = BitMask256.AllClear(); - - mask.SetBit(0); - mask.SetBit(64); - mask.SetBit(128); - mask.SetBit(192); - - Assert.Equal(4, mask.PopCount()); - Assert.True(mask.GetBit(0)); - Assert.True(mask.GetBit(64)); - Assert.True(mask.GetBit(128)); - Assert.True(mask.GetBit(192)); - Assert.False(mask.GetBit(1)); - } - - [Fact] - public void BitMask256_ClearBit_ClearsCorrectBit() - { - var mask = BitMask256.AllClear(); - - mask.SetBit(50); - mask.SetBit(100); - Assert.Equal(2, mask.PopCount()); - - mask.ClearBit(50); - - Assert.False(mask.GetBit(50)); - Assert.True(mask.GetBit(100)); - Assert.Equal(1, mask.PopCount()); - } - - [Theory] - [InlineData(0, 0)] // First bit of first ulong - [InlineData(1, 64)] // First bit of second ulong - [InlineData(2, 128)] // First bit of third ulong - [InlineData(3, 192)] // First bit of fourth ulong - public void BitMask256_GetNthSetBit_FirstBitInEachUlong(int n, int expectedPosition) - { - var mask = BitMask256.AllClear(); - - // Set first bit in each ulong - mask.SetBit(0); - mask.SetBit(64); - mask.SetBit(128); - mask.SetBit(192); - - Assert.Equal(expectedPosition, mask.GetNthSetBit(n)); - } - - [Fact] - public void BitMask256_GetNthSetBit_WithinSingleUlong() - { - var mask = BitMask256.AllClear(); - - mask.SetBit(5); - mask.SetBit(10); - mask.SetBit(20); - - Assert.Equal(5, mask.GetNthSetBit(0)); - Assert.Equal(10, mask.GetNthSetBit(1)); - Assert.Equal(20, mask.GetNthSetBit(2)); - } -} - -[Collection("Sequential UOContent Tests")] -public class SectorSpawnCacheManagerTests -{ - public SectorSpawnCacheManagerTests() - { - // Clear cache before each test - SectorSpawnCacheManager.ClearAll(); - } - - [Fact] - public void ClearAll_ResetsCache() - { - SectorSpawnCacheManager.ClearAll(); - Assert.Equal(0, SectorSpawnCacheManager.CachedSectorCount); - Assert.Equal(0, SectorSpawnCacheManager.LandCacheCount); - Assert.Equal(0, SectorSpawnCacheManager.WaterCacheCount); - } - - [Fact] - public void SetValid_CreatesSectorCache() - { - var map = Map.Felucca; - var pos = new Point3D(100, 100, 0); - - SectorSpawnCacheManager.SetValid(map, pos, isWater: false); - - Assert.Equal(1, SectorSpawnCacheManager.CachedSectorCount); - Assert.Equal(1, SectorSpawnCacheManager.LandCacheCount); - Assert.Equal(0, SectorSpawnCacheManager.WaterCacheCount); - } - - [Fact] - public void SetValid_Water_CreatesWaterCache() - { - var map = Map.Felucca; - var pos = new Point3D(100, 100, 0); - - SectorSpawnCacheManager.SetValid(map, pos, isWater: true); - - Assert.Equal(1, SectorSpawnCacheManager.CachedSectorCount); - Assert.Equal(0, SectorSpawnCacheManager.LandCacheCount); - Assert.Equal(1, SectorSpawnCacheManager.WaterCacheCount); - } - - [Fact] - public void SetValid_LandAndWater_SeparateCaches() - { - var map = Map.Felucca; - - // Same sector, different cache types - SectorSpawnCacheManager.SetValid(map, new Point3D(100, 100, 0), isWater: false); - SectorSpawnCacheManager.SetValid(map, new Point3D(105, 105, 0), isWater: true); - - Assert.Equal(2, SectorSpawnCacheManager.CachedSectorCount); - Assert.Equal(1, SectorSpawnCacheManager.LandCacheCount); - Assert.Equal(1, SectorSpawnCacheManager.WaterCacheCount); - } - - [Fact] - public void SetValid_MultipleSameSector_OnlyOneCacheEntry() - { - var map = Map.Felucca; - - // All positions in same 16x16 sector (sector 6,6 for coords 96-111) - SectorSpawnCacheManager.SetValid(map, new Point3D(96, 96, 0), isWater: false); - SectorSpawnCacheManager.SetValid(map, new Point3D(100, 100, 0), isWater: false); - SectorSpawnCacheManager.SetValid(map, new Point3D(111, 111, 0), isWater: false); - - Assert.Equal(1, SectorSpawnCacheManager.CachedSectorCount); - } - - [Fact] - public void SetValid_DifferentSectors_MultipleCacheEntries() - { - var map = Map.Felucca; - - // Different sectors - SectorSpawnCacheManager.SetValid(map, new Point3D(0, 0, 0), isWater: false); // Sector 0,0 - SectorSpawnCacheManager.SetValid(map, new Point3D(16, 0, 0), isWater: false); // Sector 1,0 - SectorSpawnCacheManager.SetValid(map, new Point3D(0, 16, 0), isWater: false); // Sector 0,1 - - Assert.Equal(3, SectorSpawnCacheManager.CachedSectorCount); - } - - [Fact] - public void TryGetRandomPosition_EmptyCache_ReturnsFalse() - { - var map = Map.Felucca; - var bounds = new Rectangle3D(0, 0, 0, 100, 100, 20); - - var result = SectorSpawnCacheManager.TryGetRandomPosition(map, bounds, isWater: false, out _); - - Assert.False(result); - } - - [Fact] - public void TryGetRandomPosition_WithCachedPosition_ReturnsTrue() - { - var map = Map.Felucca; - var cachedPos = new Point3D(50, 50, 0); - var bounds = new Rectangle3D(0, 0, -128, 100, 100, 256); - - SectorSpawnCacheManager.SetValid(map, cachedPos, isWater: false); - - var result = SectorSpawnCacheManager.TryGetRandomPosition(map, bounds, isWater: false, out var pos); - - Assert.True(result); - Assert.Equal(cachedPos.X, pos.X); - Assert.Equal(cachedPos.Y, pos.Y); - } - - [Fact] - public void TryGetRandomPosition_WaterVsLand_Separated() - { - var map = Map.Felucca; - - var landPos = new Point3D(50, 50, 0); - var waterPos = new Point3D(60, 60, 0); - var bounds = new Rectangle3D(0, 0, -128, 100, 100, 256); - - SectorSpawnCacheManager.SetValid(map, landPos, isWater: false); - SectorSpawnCacheManager.SetValid(map, waterPos, isWater: true); - - // Request land position - var landResult = SectorSpawnCacheManager.TryGetRandomPosition(map, bounds, isWater: false, out var pos1); - Assert.True(landResult); - Assert.Equal(landPos.X, pos1.X); - Assert.Equal(landPos.Y, pos1.Y); - - // Request water position - var waterResult = SectorSpawnCacheManager.TryGetRandomPosition(map, bounds, isWater: true, out var pos2); - Assert.True(waterResult); - Assert.Equal(waterPos.X, pos2.X); - Assert.Equal(waterPos.Y, pos2.Y); - } - - [Fact] - public void TryGetRandomPosition_OutsideBounds_ReturnsFalse() - { - var map = Map.Felucca; - var cachedPos = new Point3D(200, 200, 0); - var bounds = new Rectangle3D(0, 0, -128, 100, 100, 256); // Only covers 0-99 - - SectorSpawnCacheManager.SetValid(map, cachedPos, isWater: false); - - var result = SectorSpawnCacheManager.TryGetRandomPosition(map, bounds, isWater: false, out _); - - Assert.False(result); - } - - [Fact] - public void InvalidateSectors_RemovesCachedData() - { - var map = Map.Felucca; - - SectorSpawnCacheManager.SetValid(map, new Point3D(50, 50, 0), isWater: false); - Assert.Equal(1, SectorSpawnCacheManager.CachedSectorCount); - - SectorSpawnCacheManager.InvalidateSectors(map, new Rectangle2D(0, 0, 100, 100)); - - Assert.Equal(0, SectorSpawnCacheManager.CachedSectorCount); - } - - [Fact] - public void InvalidateSectors_RemovesBothLandAndWater() - { - var map = Map.Felucca; - - // Same sector, both land and water - SectorSpawnCacheManager.SetValid(map, new Point3D(50, 50, 0), isWater: false); - SectorSpawnCacheManager.SetValid(map, new Point3D(55, 55, 0), isWater: true); - Assert.Equal(2, SectorSpawnCacheManager.CachedSectorCount); - - SectorSpawnCacheManager.InvalidateSectors(map, new Rectangle2D(0, 0, 100, 100)); - - Assert.Equal(0, SectorSpawnCacheManager.CachedSectorCount); - Assert.Equal(0, SectorSpawnCacheManager.LandCacheCount); - Assert.Equal(0, SectorSpawnCacheManager.WaterCacheCount); - } - - [Fact] - public void InvalidateSectors_OnlyAffectsSpecifiedArea() - { - var map = Map.Felucca; - - // Cache in two different areas - SectorSpawnCacheManager.SetValid(map, new Point3D(50, 50, 0), isWater: false); // Sector 3,3 - SectorSpawnCacheManager.SetValid(map, new Point3D(500, 500, 0), isWater: false); // Sector 31,31 - - Assert.Equal(2, SectorSpawnCacheManager.CachedSectorCount); - - // Only invalidate first area - SectorSpawnCacheManager.InvalidateSectors(map, new Rectangle2D(0, 0, 100, 100)); - - Assert.Equal(1, SectorSpawnCacheManager.CachedSectorCount); - } - - [Fact] - public void InvalidateSectors_DifferentMaps_Independent() - { - var map1 = Map.Felucca; - var map2 = Map.Internal; - - SectorSpawnCacheManager.SetValid(map1, new Point3D(50, 50, 0), isWater: false); - SectorSpawnCacheManager.SetValid(map2, new Point3D(50, 50, 0), isWater: false); - - Assert.Equal(2, SectorSpawnCacheManager.CachedSectorCount); - - // Only invalidate first map - SectorSpawnCacheManager.InvalidateSectors(map1, new Rectangle2D(0, 0, 100, 100)); - - Assert.Equal(1, SectorSpawnCacheManager.CachedSectorCount); - } -} - -[Collection("SectorSpawnCache")] -public class SpiralScanTests -{ - public SpiralScanTests() - { - SectorSpawnCacheManager.ClearAll(); - } - - [Theory] - [InlineData(1, 0, -1, -1)] // Ring 1, position 0: top-left - [InlineData(1, 1, 0, -1)] // Ring 1, position 1: top - [InlineData(1, 2, 1, -1)] // Ring 1, position 2: top-right (end of top edge) - [InlineData(1, 3, 1, 0)] // Ring 1, position 3: right - [InlineData(1, 4, 1, 1)] // Ring 1, position 4: bottom-right (end of right edge) - [InlineData(1, 5, 0, 1)] // Ring 1, position 5: bottom - [InlineData(1, 6, -1, 1)] // Ring 1, position 6: bottom-left (end of bottom edge) - [InlineData(1, 7, -1, 0)] // Ring 1, position 7: left - public void GetSpiralOffset_Ring1_CorrectOffsets(int ring, int position, int expectedDx, int expectedDy) - { - var (dx, dy) = SectorSpawnCacheManager.GetSpiralOffset(ring, position); - - Assert.Equal(expectedDx, dx); - Assert.Equal(expectedDy, dy); - } - - [Fact] - public void SpiralPattern_Ring1Has8Positions() - { - // Ring N has 8*N positions - const int ring1Positions = 1 * 8; - Assert.Equal(8, ring1Positions); - } - - [Fact] - public void SpiralPattern_Ring2Has16Positions() - { - const int ring2Positions = 2 * 8; - Assert.Equal(16, ring2Positions); - } - - [Fact] - public void SpiralPattern_Ring10Has80Positions() - { - const int ring10Positions = 10 * 8; - Assert.Equal(80, ring10Positions); - } - - [Fact] - public void SpiralPattern_Ring1_CoversAllAdjacentTiles() - { - // Ring 1 should cover all 8 adjacent tiles - var expectedOffsets = new HashSet<(int dx, int dy)> - { - (-1, -1), (0, -1), (1, -1), // Top row - (1, 0), // Right - (1, 1), (0, 1), (-1, 1), // Bottom row - (-1, 0) // Left - }; - - var actualOffsets = new HashSet<(int dx, int dy)>(); - - for (var position = 0; position < 8; position++) - { - actualOffsets.Add(SectorSpawnCacheManager.GetSpiralOffset(1, position)); - } - - Assert.Equal(expectedOffsets.Count, actualOffsets.Count); - foreach (var expected in expectedOffsets) - { - Assert.Contains(expected, actualOffsets); - } - } - - [Fact] - public void SpiralPattern_Ring2_CoversAllExpectedTiles() - { - // Ring 2 should cover all tiles at distance 2 - var actualOffsets = new HashSet<(int dx, int dy)>(); - - for (var position = 0; position < 16; position++) - { - actualOffsets.Add(SectorSpawnCacheManager.GetSpiralOffset(2, position)); - } - - // Should have 16 unique positions - Assert.Equal(16, actualOffsets.Count); - - // All positions should be at Chebyshev distance 2 - foreach (var (dx, dy) in actualOffsets) - { - var chebyshevDist = System.Math.Max(System.Math.Abs(dx), System.Math.Abs(dy)); - Assert.Equal(2, chebyshevDist); - } - } -} - -public class SpawnPositionStateTests -{ - [Fact] - public void SpawnPositionState_InitialState_ZeroCounts() - { - var state = new SpawnPositionState(); - - Assert.False(state.SpiralComplete); - Assert.Equal(0, state.SpiralRing); - Assert.Equal(0, state.SpiralRingPosition); - } - - [Fact] - public void Reset_ClearsAllState() - { - var state = new SpawnPositionState(); - state.SpiralRing = 5; - state.SpiralRingPosition = 10; - state.SpiralComplete = true; - state.RecordNonTransientFailure(); - - state.Reset(); - - Assert.False(state.SpiralComplete); - Assert.Equal(0, state.SpiralRing); - Assert.Equal(0, state.SpiralRingPosition); - } - - [Fact] - public void ShouldCachePositions_Enabled_AlwaysTrue() - { - var state = new SpawnPositionState(); - - Assert.True(state.ShouldCachePositions(SpawnPositionMode.Enabled)); - } - - [Fact] - public void ShouldCachePositions_Disabled_AlwaysFalse() - { - var state = new SpawnPositionState(); - state.RecordNonTransientFailure(); - - Assert.False(state.ShouldCachePositions(SpawnPositionMode.Disabled)); - } - - [Fact] - public void ShouldCachePositions_Automatic_FalseWithNoFailures() - { - var state = new SpawnPositionState(); - - Assert.False(state.ShouldCachePositions(SpawnPositionMode.Automatic)); - } - - [Fact] - public void ShouldCachePositions_Automatic_TrueAfterThresholdFailures() - { - var state = new SpawnPositionState(); - - // FailureThreshold is 5, so need more than 5 failures - for (var i = 0; i < 6; i++) - { - state.RecordNonTransientFailure(); - } - - Assert.True(state.ShouldCachePositions(SpawnPositionMode.Automatic)); - } - - [Fact] - public void ShouldCachePositions_Automatic_FalseWithFewerThanThresholdFailures() - { - var state = new SpawnPositionState(); - - // FailureThreshold is 5, so 5 or fewer failures should not trigger caching - for (var i = 0; i < 5; i++) - { - state.RecordNonTransientFailure(); - } - - Assert.False(state.ShouldCachePositions(SpawnPositionMode.Automatic)); - } - - [Fact] - public void ShouldAbandon_FalseWhenSpiralNotComplete() - { - var state = new SpawnPositionState(); - - // Record 25 useless results - for (var i = 0; i < 25; i++) - { - state.RecordUselessResult(); - } - - Assert.False(state.ShouldAbandon()); // Spiral not complete - } - - [Fact] - public void ShouldAbandon_TrueAfterThresholdUselessResults() - { - var state = new SpawnPositionState(); - state.SpiralComplete = true; - - // Record 25 useless results (cache miss or Location-only) - for (var i = 0; i < 25; i++) - { - state.RecordUselessResult(); - } - - Assert.True(state.ShouldAbandon()); - } - - [Fact] - public void ShouldAbandon_FalseWithUsefulCacheHit() - { - var state = new SpawnPositionState(); - state.SpiralComplete = true; - - // Record 24 useless results - for (var i = 0; i < 24; i++) - { - state.RecordUselessResult(); - } - - // Record a useful cache hit - resets counter - state.RecordUsefulCacheHit(); - - // Record a few more useless results - for (var i = 0; i < 5; i++) - { - state.RecordUselessResult(); - } - - Assert.False(state.ShouldAbandon()); // Counter was reset, only 5 now - } - - [Fact] - public void RecordUsefulCacheHit_ResetsUselessCounter() - { - var state = new SpawnPositionState(); - state.SpiralComplete = true; - - // Record 20 useless results - for (var i = 0; i < 20; i++) - { - state.RecordUselessResult(); - } - - // Useful cache hit resets counter - state.RecordUsefulCacheHit(); - - // Need 25 more to trigger abandon - for (var i = 0; i < 24; i++) - { - state.RecordUselessResult(); - } - - Assert.False(state.ShouldAbandon()); // Only 24 after reset - - state.RecordUselessResult(); // 25th - Assert.True(state.ShouldAbandon()); - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePacketTests.cs b/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePacketTests.cs index 1dad7688a..42f43b207 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePacketTests.cs @@ -7,19 +7,18 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] public class CharacterStatuePacketTests { [Theory] [InlineData(0x1024u, 1, 100, 200)] public void TestSendStatueAnimation(uint s, int status, int anim, int frame) { - var expected = new UpdateStatueAnimation((Serial)s, status, anim, frame).Compile(); + var expected = new UpdateStatueAnimation((Serial)s, status, anim, frame).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendStatueAnimation((Serial)s, status, anim, frame); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendStatueAnimation((Serial)s, status, anim, frame); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/Packets.cs b/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/Packets.cs index d28be2e51..a98e69cbc 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Veteran Rewards/Character Statue Maker/Packets.cs @@ -4,16 +4,16 @@ public class UpdateStatueAnimation : Packet { public UpdateStatueAnimation(Serial serial, int status, int animation, int frame) : base(0xBF, 17) { - Stream.Write((short)0x11); - Stream.Write((short)0x19); - Stream.Write((byte)0x5); - Stream.Write(serial); - Stream.Write((byte)0); - Stream.Write((byte)0xFF); - Stream.Write((byte)status); - Stream.Write((byte)0); - Stream.Write((byte)animation); - Stream.Write((byte)0); - Stream.Write((byte)frame); - } -} + Stream.Write((short)0x11); + Stream.Write((short)0x19); + Stream.Write((byte)0x5); + Stream.Write(serial); + Stream.Write((byte)0); + Stream.Write((byte)0xFF); + Stream.Write((byte)status); + Stream.Write((byte)0); + Stream.Write((byte)animation); + Stream.Write((byte)0); + Stream.Write((byte)frame); + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Gumps/GridLayoutTests.cs b/Projects/UOContent.Tests/Tests/Gumps/GridLayoutTests.cs deleted file mode 100644 index 0f4518289..000000000 --- a/Projects/UOContent.Tests/Tests/Gumps/GridLayoutTests.cs +++ /dev/null @@ -1,302 +0,0 @@ -using System; -using Server.Gumps; -using Xunit; - -namespace Server.Tests.Gumps; - -public class GridLayoutTests -{ - [Fact] - public void TestGridSizeSpec_ParseAbsolute() - { - var spec = GridSizeSpec.Parse("100"); - Assert.Equal(GridSizeType.Absolute, spec.Type); - Assert.Equal(100, spec.Value); - } - - [Fact] - public void TestGridSizeSpec_ParseStar() - { - var spec = GridSizeSpec.Parse("*"); - Assert.Equal(GridSizeType.Star, spec.Type); - Assert.Equal(1, spec.Value); - } - - [Fact] - public void TestGridSizeSpec_ParsePercent() - { - var spec = GridSizeSpec.Parse("10*"); - Assert.Equal(GridSizeType.Percent, spec.Type); - Assert.Equal(10, spec.Value); - } - - [Fact] - public void TestGridSizeSpec_ParseAll() - { - Span specs = stackalloc GridSizeSpec[4]; - var count = GridSizeSpec.ParseAll("10* * 100 20*", specs); - - Assert.Equal(4, count); - Assert.Equal(GridSizeType.Percent, specs[0].Type); - Assert.Equal(10, specs[0].Value); - Assert.Equal(GridSizeType.Star, specs[1].Type); - Assert.Equal(GridSizeType.Absolute, specs[2].Type); - Assert.Equal(100, specs[2].Value); - Assert.Equal(GridSizeType.Percent, specs[3].Type); - Assert.Equal(20, specs[3].Value); - } - - [Fact] - public void TestGridCalculator_UniformTracks() - { - Span positions = stackalloc int[4]; - Span sizes = stackalloc int[4]; - - GridCalculator.ComputeUniformTrackSizes(4, 400, 0, positions, sizes); - - Assert.Equal(0, positions[0]); - Assert.Equal(100, positions[1]); - Assert.Equal(200, positions[2]); - Assert.Equal(300, positions[3]); - - Assert.Equal(100, sizes[0]); - Assert.Equal(100, sizes[1]); - Assert.Equal(100, sizes[2]); - Assert.Equal(100, sizes[3]); - } - - [Fact] - public void TestGridCalculator_UniformTracksWithOrigin() - { - Span positions = stackalloc int[2]; - Span sizes = stackalloc int[2]; - - GridCalculator.ComputeUniformTrackSizes(2, 200, 50, positions, sizes); - - Assert.Equal(50, positions[0]); - Assert.Equal(150, positions[1]); - Assert.Equal(100, sizes[0]); - Assert.Equal(100, sizes[1]); - } - - [Fact] - public void TestGridCalculator_MixedTracks() - { - // "10* * 100" = 10%, star, 100px absolute - Span positions = stackalloc int[3]; - Span sizes = stackalloc int[3]; - - var count = GridCalculator.ComputeFromSpec("10* * 100", 1000, 0, positions, sizes); - - Assert.Equal(3, count); - - // 10% of 1000 = 100 - Assert.Equal(0, positions[0]); - Assert.Equal(100, sizes[0]); - - // 100px absolute - Assert.Equal(900, positions[2]); - Assert.Equal(100, sizes[2]); - - // Star gets remaining: 1000 - 100 - 100 = 800 - Assert.Equal(100, positions[1]); - Assert.Equal(800, sizes[1]); - } - - [Fact] - public void TestGridCell_Properties() - { - var cell = new GridCell(10, 20, 100, 50); - - Assert.Equal(10, cell.X); - Assert.Equal(20, cell.Y); - Assert.Equal(100, cell.Width); - Assert.Equal(50, cell.Height); - Assert.Equal(110, cell.Right); - Assert.Equal(70, cell.Bottom); - Assert.Equal(60, cell.CenterX); - Assert.Equal(45, cell.CenterY); - } - - [Fact] - public void TestGridCell_Inset() - { - var cell = new GridCell(10, 20, 100, 50); - var inset = cell.Inset(5); - - Assert.Equal(15, inset.X); - Assert.Equal(25, inset.Y); - Assert.Equal(90, inset.Width); - Assert.Equal(40, inset.Height); - } - - [Fact] - public void TestGridCell_Offset() - { - var cell = new GridCell(10, 20, 100, 50); - var offset = cell.Offset(5, 10); - - Assert.Equal(15, offset.X); - Assert.Equal(30, offset.Y); - Assert.Equal(100, offset.Width); - Assert.Equal(50, offset.Height); - } - - [Fact] - public void TestListViewLayout_Pagination() - { - Span colPos = stackalloc int[3]; - Span colWidths = stackalloc int[3]; - - // 100 items, row height 20, header 10, total height 210 (10 items per page) - var layout = ListViewLayout.Create( - 0, 0, 300, 210, - 100, // total items - 0, // current page - 20, // row height - 10, // header height - "* * *", - colPos, colWidths); - - Assert.Equal(100, layout.TotalItems); - Assert.Equal(10, layout.ItemsPerPage); - Assert.Equal(0, layout.CurrentPage); - Assert.Equal(10, layout.TotalPages); - Assert.Equal(0, layout.StartIndex); - Assert.Equal(10, layout.VisibleCount); - Assert.False(layout.CanGoBack); - Assert.True(layout.CanGoNext); - } - - [Fact] - public void TestListViewLayout_MiddlePage() - { - Span colPos = stackalloc int[3]; - Span colWidths = stackalloc int[3]; - - var layout = ListViewLayout.Create( - 0, 0, 300, 210, - 100, // total items - 5, // current page (middle) - 20, // row height - 10, // header height - "* * *", - colPos, colWidths); - - Assert.Equal(5, layout.CurrentPage); - Assert.Equal(50, layout.StartIndex); - Assert.Equal(10, layout.VisibleCount); - Assert.True(layout.CanGoBack); - Assert.True(layout.CanGoNext); - } - - [Fact] - public void TestListViewLayout_LastPage() - { - Span colPos = stackalloc int[3]; - Span colWidths = stackalloc int[3]; - - // 95 items, 10 per page = 10 pages, last page has 5 items - var layout = ListViewLayout.Create( - 0, 0, 300, 210, - 95, // total items - 9, // last page - 20, // row height - 10, // header height - "* * *", - colPos, colWidths); - - Assert.Equal(9, layout.CurrentPage); - Assert.Equal(90, layout.StartIndex); - Assert.Equal(5, layout.VisibleCount); // Only 5 items on last page - Assert.True(layout.CanGoBack); - Assert.False(layout.CanGoNext); - } - - [Fact] - public void TestListViewLayout_EmptyList() - { - Span colPos = stackalloc int[3]; - Span colWidths = stackalloc int[3]; - - var layout = ListViewLayout.Create( - 0, 0, 300, 210, - 0, // no items - 0, // current page - 20, // row height - 10, // header height - "* * *", - colPos, colWidths); - - Assert.Equal(0, layout.TotalItems); - Assert.Equal(1, layout.TotalPages); - Assert.Equal(0, layout.StartIndex); - Assert.Equal(0, layout.VisibleCount); - Assert.False(layout.CanGoBack); - Assert.False(layout.CanGoNext); - } - - [Fact] - public void TestListViewLayout_GetHeaderCell() - { - Span colPos = stackalloc int[3]; - Span colWidths = stackalloc int[3]; - - var layout = ListViewLayout.Create( - 10, 20, 300, 200, - 50, 0, 20, 30, - "100 100 100", - colPos, colWidths); - - var headerCell = layout.GetHeaderCell(1, colPos, colWidths); - - Assert.Equal(110, headerCell.X); // 10 (origin) + 100 (col 0 width) - Assert.Equal(20, headerCell.Y); // origin Y - Assert.Equal(100, headerCell.Width); - Assert.Equal(30, headerCell.Height); // header height - } - - [Fact] - public void TestListViewLayout_GetRowCell() - { - Span colPos = stackalloc int[3]; - Span colWidths = stackalloc int[3]; - - var layout = ListViewLayout.Create( - 0, 0, 300, 200, - 50, 0, 20, 30, - "100 100 100", - colPos, colWidths); - - var rowCell = layout.GetRowCell(2, 1, colPos, colWidths); - - Assert.Equal(100, rowCell.X); // column 1 position - Assert.Equal(70, rowCell.Y); // 0 (origin) + 30 (header) + 2*20 (rows) - Assert.Equal(100, rowCell.Width); - Assert.Equal(20, rowCell.Height); - } - - [Fact] - public void TestListViewLayout_ColumnSpec() - { - Span colPos = stackalloc int[12]; - Span colWidths = stackalloc int[12]; - - // The SpawnerControllerGump column spec - var layout = ListViewLayout.Create( - 0, 80, 1000, 620, - 100, 0, 45, 30, - "6* 8* 18* 6* 12* 6* 5* 5* 8* 8* 9* *", - colPos, colWidths); - - Assert.Equal(12, layout.ColumnCount); - - // First column: 6% of 1000 = 60 - Assert.Equal(0, colPos[0]); - Assert.Equal(60, colWidths[0]); - - // Second column: 8% = 80 - Assert.Equal(60, colPos[1]); - Assert.Equal(80, colWidths[1]); - } -} diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs index fd3dafc56..9ad3e0ae2 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs @@ -6,8 +6,6 @@ public class DynamicTestGump : DynamicGump { private readonly string _petName; - public bool HasVisualElementsForTest => HasVisualElements; - public DynamicTestGump(string petName) : base(50, 50) { _petName = petName; @@ -29,7 +27,7 @@ public class DynamicTestGump : DynamicGump builder.AddItem(218, 95, 0xCB0); builder.AddHtml(30, 30, 150, 75, "
Wilt thou sanctify the resurrection of:
"); - builder.AddHtml(30, 70, 150, 25, _petName, align: TextAlignment.Center, background: true); + builder.AddHtml(30, 70, 150, 25, $"
{_petName}
", true); builder.AddButton(40, 105, 0x81A, 0x81B, 0x1); // Okay builder.AddButton(110, 105, 0x819, 0x818, 0x2); // Cancel diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/EmptyTestGumps.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/EmptyTestGumps.cs deleted file mode 100644 index b99de1700..000000000 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/EmptyTestGumps.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Server.Gumps; - -namespace Server.Tests.Gumps; - -public sealed class EmptyLegacyTestGump : Gump -{ - public bool HasVisualElementsForTest => HasVisualElements; - - public EmptyLegacyTestGump() : base(0, 0) - { - } -} - -public sealed class EmptyDynamicTestGump : DynamicGump -{ - public bool HasVisualElementsForTest => HasVisualElements; - - public EmptyDynamicTestGump() : base(0, 0) - { - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - } -} - -public sealed class EmptyStaticTestGump : StaticGump -{ - public bool HasVisualElementsForTest => HasVisualElements; - - public EmptyStaticTestGump() : base(0, 0) - { - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.SetNoClose(); - } -} diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/LegacyTestGump.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/LegacyTestGump.cs index e45514c38..668d7e8ae 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/LegacyTestGump.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/LegacyTestGump.cs @@ -4,8 +4,6 @@ namespace Server.Tests.Gumps; public sealed class LegacyTestGump : Gump { - public bool HasVisualElementsForTest => HasVisualElements; - public LegacyTestGump(string petName) : base(50, 50) { Serial = (Serial)0x123; diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticLayoutTestGump.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticLayoutTestGump.cs index 7e1859161..06679a48e 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticLayoutTestGump.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticLayoutTestGump.cs @@ -35,6 +35,6 @@ public class StaticLayoutTestGump : StaticGump protected override void BuildStrings(ref GumpStringsBuilder builder) { - builder.SetHtmlText("petName", _petName, align: TextAlignment.Center); + builder.SetStringSlot("petName", $"
{_petName}
"); } } diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs index 66d80c963..ce616116e 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs @@ -4,8 +4,6 @@ namespace Server.Tests.Gumps; public class StaticTestGump : StaticGump { - public bool HasVisualElementsForTest => HasVisualElements; - public StaticTestGump() : base(50, 50) { Serial = (Serial)0x123; @@ -26,7 +24,7 @@ public class StaticTestGump : StaticGump builder.AddItem(218, 95, 0xCB0); builder.AddHtml(30, 30, 150, 75, "
Wilt thou sanctify the resurrection of:
"); - builder.AddHtml(30, 70, 150, 25, "Test", align: TextAlignment.Center, background: true); + builder.AddHtml(30, 70, 150, 25, "
Test
", true); builder.AddButton(40, 105, 0x81A, 0x81B, 0x1); // Okay builder.AddButton(110, 105, 0x819, 0x818, 0x2); // Cancel diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestLayoutGumps.cs b/Projects/UOContent.Tests/Tests/Gumps/TestLayoutGumps.cs index 804408fc7..465f08c80 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestLayoutGumps.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestLayoutGumps.cs @@ -7,7 +7,7 @@ using Xunit; namespace Server.Tests.Gumps; -[Collection("Sequential UOContent Tests")] +[Collection("Sequential Tests")] public class TestLayoutGumps { [Fact] @@ -73,32 +73,6 @@ public class TestLayoutGumps AssertThat.Equal(writer.Span, packet); } - [Fact] - public void TestEmptyGumpsHaveNoVisualElements() - { - Assert.False(Compile(new EmptyLegacyTestGump()).HasVisualElementsForTest); - Assert.False(Compile(new EmptyDynamicTestGump()).HasVisualElementsForTest); - Assert.False(Compile(new EmptyStaticTestGump()).HasVisualElementsForTest); - Assert.False(Compile(new EmptyStaticTestGump()).HasVisualElementsForTest); - } - - [Fact] - public void TestVisibleGumpsHaveVisualElements() - { - Assert.True(Compile(new LegacyTestGump("Test")).HasVisualElementsForTest); - Assert.True(Compile(new DynamicTestGump("Test")).HasVisualElementsForTest); - Assert.True(Compile(new StaticTestGump()).HasVisualElementsForTest); - Assert.True(Compile(new StaticTestGump()).HasVisualElementsForTest); - } - - private static T Compile(T gump) where T : BaseGump - { - var buffer = GC.AllocateUninitializedArray(512); - var writer = new SpanWriter(buffer); - gump.Compile(ref writer); - return gump; - } - private static void InternalTestStaticGump(ReadOnlySpan expectedLayout, StaticGump staticGump, string[] strings) where T : StaticGump { diff --git a/Projects/UOContent.Tests/Tests/Items/Books/BookPacketTests.cs b/Projects/UOContent.Tests/Tests/Items/Books/BookPacketTests.cs index 20eabc940..d8025bcf4 100644 --- a/Projects/UOContent.Tests/Tests/Items/Books/BookPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/Books/BookPacketTests.cs @@ -15,79 +15,78 @@ public class TestBook : BaseBook public TestBook(int itemID, string title, string author, int pageCount, bool writable) : base(itemID, title, author, pageCount, writable) { - } + } public TestBook(int itemID, bool writable) : base(itemID, writable) { - } + } public TestBook(Serial serial) : base(serial) { - Pages = new BookPageInfo[20]; + Pages = new BookPageInfo[20]; - for (var i = 0; i < Pages.Length; ++i) - { - Pages[i] = new BookPageInfo(); + for (var i = 0; i < Pages.Length; ++i) + { + Pages[i] = new BookPageInfo(); + } } - } } -[Collection("Sequential UOContent Tests")] -public class BookPacketTests +public class BookPacketTests : IClassFixture { [Theory] [InlineData("🅵🅰🅽🅲🆈 🆃🅴🆇🆃 Author", "🅵🅰🅽🅲🆈 🆃🅴🆇🆃 Title")] public void TestBookCover(string author, string title) { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var serial = (Serial)0x1001; - var book = new TestBook(serial) { Author = author, Title = title }; + Serial serial = (Serial)0x1001; + var book = new TestBook(serial) { Author = author, Title = title }; - var expected = new BookHeader(m, book).Compile(); + var expected = new BookHeader(m, book).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendBookCover(m, book); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendBookCover(m, book); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Fact] public void TestBookContent() { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var serial = (Serial)0x1001; - var book = new TestBook(serial) { Author = "Some Author", Title = "Some Title" }; - book.Pages[0].Lines = new[] - { - "Some books start with actual content", - "This book does not have any actual content", - "Instead it has several pages of useless text" - }; + Serial serial = (Serial)0x1001; + var book = new TestBook(serial) { Author = "Some Author", Title = "Some Title" }; + book.Pages[0].Lines = new[] + { + "Some books start with actual content", + "This book does not have any actual content", + "Instead it has several pages of useless text" + }; - book.Pages[1].Lines = new[] - { - "Another page exists but this page:", - "Has lots of: 🅵🅰🅽🅲🆈 🆃🅴🆇🆃", - "And just more: 🅵🅰🅽🅲🆈 🆃🅴🆇🆃", - "So everyone can read: 🅵🅰🅽🅲🆈 🆃🅴🆇🆃" - }; + book.Pages[1].Lines = new[] + { + "Another page exists but this page:", + "Has lots of: 🅵🅰🅽🅲🆈 🆃🅴🆇🆃", + "And just more: 🅵🅰🅽🅲🆈 🆃🅴🆇🆃", + "So everyone can read: 🅵🅰🅽🅲🆈 🆃🅴🆇🆃" + }; - book.Pages[2].Lines = new[] - { - "The end" - }; + book.Pages[2].Lines = new[] + { + "The end" + }; - var expected = new BookPageDetails(book).Compile(); + var expected = new BookPageDetails(book).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendBookContent(book); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendBookContent(book); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } } diff --git a/Projects/UOContent.Tests/Tests/Items/Books/Packets.cs b/Projects/UOContent.Tests/Tests/Items/Books/Packets.cs index 4a7b175d3..c098729c6 100644 --- a/Projects/UOContent.Tests/Tests/Items/Books/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Items/Books/Packets.cs @@ -7,52 +7,52 @@ public sealed class BookPageDetails : Packet { public BookPageDetails(BaseBook book) : base(0x66) { - EnsureCapacity(256); + EnsureCapacity(256); - Stream.Write(book.Serial); - Stream.Write((ushort)book.PagesCount); + Stream.Write(book.Serial); + Stream.Write((ushort)book.PagesCount); - for (var i = 0; i < book.PagesCount; ++i) - { - var page = book.Pages[i]; - - Stream.Write((ushort)(i + 1)); - Stream.Write((ushort)page.Lines.Length); - - for (var j = 0; j < page.Lines.Length; ++j) + for (var i = 0; i < book.PagesCount; ++i) { - var buffer = page.Lines[j].GetBytesUtf8(); + var page = book.Pages[i]; - Stream.Write(buffer, 0, buffer.Length); - Stream.Write((byte)0); + Stream.Write((ushort)(i + 1)); + Stream.Write((ushort)page.Lines.Length); + + for (var j = 0; j < page.Lines.Length; ++j) + { + var buffer = page.Lines[j].GetBytesUtf8(); + + Stream.Write(buffer, 0, buffer.Length); + Stream.Write((byte)0); + } } } - } } public sealed class BookHeader : Packet { public BookHeader(Mobile from, BaseBook book) : base(0xD4) { - var title = book.Title ?? ""; - var author = book.Author ?? ""; + var title = book.Title ?? ""; + var author = book.Author ?? ""; - var titleBuffer = title.GetBytesUtf8(); - var authorBuffer = author.GetBytesUtf8(); + var titleBuffer = title.GetBytesUtf8(); + var authorBuffer = author.GetBytesUtf8(); - EnsureCapacity(15 + titleBuffer.Length + authorBuffer.Length); + EnsureCapacity(15 + titleBuffer.Length + authorBuffer.Length); - Stream.Write(book.Serial); - Stream.Write(true); - Stream.Write(book.Writable && from.InRange(book.GetWorldLocation(), 1)); - Stream.Write((ushort)book.PagesCount); + Stream.Write(book.Serial); + Stream.Write(true); + Stream.Write(book.Writable && from.InRange(book.GetWorldLocation(), 1)); + Stream.Write((ushort)book.PagesCount); - Stream.Write((ushort)(titleBuffer.Length + 1)); - Stream.Write(titleBuffer, 0, titleBuffer.Length); - Stream.Write((byte)0); // terminate + Stream.Write((ushort)(titleBuffer.Length + 1)); + Stream.Write(titleBuffer, 0, titleBuffer.Length); + Stream.Write((byte)0); // terminate - Stream.Write((ushort)(authorBuffer.Length + 1)); - Stream.Write(authorBuffer, 0, authorBuffer.Length); - Stream.Write((byte)0); // terminate - } -} + Stream.Write((ushort)(authorBuffer.Length + 1)); + Stream.Write(authorBuffer, 0, authorBuffer.Length); + Stream.Write((byte)0); // terminate + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Items/Bulletin Boards/BulletinBoardPacketTests.cs b/Projects/UOContent.Tests/Tests/Items/Bulletin Boards/BulletinBoardPacketTests.cs index 99976ad84..cf2719d89 100644 --- a/Projects/UOContent.Tests/Tests/Items/Bulletin Boards/BulletinBoardPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/Bulletin Boards/BulletinBoardPacketTests.cs @@ -7,8 +7,8 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] -public class BulletinBoardPacketTests +[Collection("Sequential Tests")] +public class BulletinBoardPacketTests : IClassFixture { [Theory] [InlineData("Test Name")] @@ -16,16 +16,16 @@ public class BulletinBoardPacketTests [InlineData("🅵🅰🅽🅲🆈 🆃🅴🆇🆃")] public void TestSendBBDisplayBoard(string boardName) { - var bb = new TestBulletinBoard(0x234) { BoardName = boardName }; + var bb = new TestBulletinBoard(0x234) { BoardName = boardName }; - var expected = new BBDisplayBoard(bb).Compile(); + var expected = new BBDisplayBoard(bb).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendBBDisplayBoard(bb); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendBBDisplayBoard(bb); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData("The Subject", false, "First Line", "Second Line", "Third Line")] @@ -36,32 +36,32 @@ public class BulletinBoardPacketTests [InlineData("🅵🅰🅽🅲🆈 🆃🅴🆇🆃", true, "First Line", "Second Line")] public void TestSendBBHeaderMessage(string subject, bool content, params string[] lines) { - var poster = new Mobile((Serial)0x1024u) { Name = "Kamron" }; - poster.DefaultMobileInit(); + var poster = new Mobile((Serial)0x1024u) { Name = "Kamron" }; + poster.DefaultMobileInit(); - var bb = new TestBulletinBoard(0x234); - bb.PostMessage(poster, null, subject, lines); + var bb = new TestBulletinBoard(0x234); + bb.PostMessage(poster, null, subject, lines); - var msg = bb.Items[0] as BulletinMessage; + var msg = bb.Items[0] as BulletinMessage; - var expected = (content ? - (Packet)new BBMessageContent(bb, msg) : new BBMessageHeader(bb, msg)).Compile(); + var expected = (content ? + (Packet)new BBMessageContent(bb, msg) : new BBMessageHeader(bb, msg)).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendBBMessage(bb, msg, content); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendBBMessage(bb, msg, content); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } } internal class TestBulletinBoard : BaseBulletinBoard { public TestBulletinBoard(int itemID) : base(itemID) { - } + } public TestBulletinBoard(Serial serial) : base(serial) { - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Items/Bulletin Boards/Packets.cs b/Projects/UOContent.Tests/Tests/Items/Bulletin Boards/Packets.cs index a6badc78e..2776a3a4f 100644 --- a/Projects/UOContent.Tests/Tests/Items/Bulletin Boards/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Items/Bulletin Boards/Packets.cs @@ -1,148 +1,149 @@ using Server.Items; using Server.Text; -namespace Server.Network; - -public class BBDisplayBoard : Packet +namespace Server.Network { - public BBDisplayBoard(BaseBulletinBoard board) : base(0x71) + public class BBDisplayBoard : Packet { - EnsureCapacity(38); - - var buffer = (board.BoardName ?? "").GetBytesUtf8(); - - Stream.Write((byte)0x00); // Packet ID - Stream.Write(board.Serial); // Bulletin board serial - - // Bulletin board name - if (buffer.Length >= 29) + public BBDisplayBoard(BaseBulletinBoard board) : base(0x71) { - Stream.Write(buffer, 0, 29); + EnsureCapacity(38); + + var buffer = (board.BoardName ?? "").GetBytesUtf8(); + + Stream.Write((byte)0x00); // Packet ID + Stream.Write(board.Serial); // Bulletin board serial + + // Bulletin board name + if (buffer.Length >= 29) + { + Stream.Write(buffer, 0, 29); + Stream.Write((byte)0); + } + else + { + Stream.Write(buffer, 0, buffer.Length); + Stream.Fill(30 - buffer.Length); + } + } + } + + public class BBMessageHeader : Packet + { + public BBMessageHeader(BaseBulletinBoard board, BulletinMessage msg) : base(0x71) + { + var poster = SafeString(msg.PostedName); + var subject = SafeString(msg.Subject); + var time = SafeString(msg.GetTimeAsString()); + + EnsureCapacity(22 + poster.Length + subject.Length + time.Length); + + Stream.Write((byte)0x01); // Packet ID + Stream.Write(board.Serial); // Bulletin board serial + Stream.Write(msg.Serial); // Message serial + + Stream.Write(msg.Thread?.Serial ?? Serial.Zero); // Thread serial--parent + + WriteString(poster); + WriteString(subject); + WriteString(time); + } + + public void WriteString(string v) + { + var buffer = v.GetBytesUtf8(); + var len = buffer.Length + 1; + + if (len > 255) + { + len = 255; + } + + Stream.Write((byte)len); + Stream.Write(buffer, 0, len - 1); Stream.Write((byte)0); } - else + + public string SafeString(string v) => v ?? string.Empty; + } + + public class BBMessageContent : Packet + { + public BBMessageContent(BaseBulletinBoard board, BulletinMessage msg) : base(0x71) { - Stream.Write(buffer, 0, buffer.Length); - Stream.Fill(30 - buffer.Length); + var poster = SafeString(msg.PostedName); + var subject = SafeString(msg.Subject); + var time = SafeString(msg.GetTimeAsString()); + + EnsureCapacity(22 + poster.Length + subject.Length + time.Length); + + Stream.Write((byte)0x02); // Packet ID + Stream.Write(board.Serial); // Bulletin board serial + Stream.Write(msg.Serial); // Message serial + + WriteString(poster); + WriteString(subject); + WriteString(time); + + Stream.Write((short)msg.PostedBody); + Stream.Write((short)msg.PostedHue); + + var len = msg.PostedEquip.Length; + + if (len > 255) + { + len = 255; + } + + Stream.Write((byte)len); + + for (var i = 0; i < len; ++i) + { + var eq = msg.PostedEquip[i]; + + Stream.Write((short)eq._itemID); + Stream.Write((short)eq._hue); + } + + len = msg.Lines.Length; + + if (len > 255) + { + len = 255; + } + + Stream.Write((byte)len); + + for (var i = 0; i < len; ++i) + { + WriteString(msg.Lines[i], true); + } } + + public void WriteString(string v, bool padding = false) + { + var buffer = v.GetBytesUtf8(); + var tail = padding ? 2 : 1; + var len = buffer.Length + tail; + + if (len > 255) + { + len = 255; + } + + Stream.Write((byte)len); + Stream.Write(buffer, 0, len - tail); + + if (padding) + { + Stream.Write((short)0); // padding compensates for a client bug + } + else + { + Stream.Write((byte)0); + } + } + + public string SafeString(string v) => v ?? string.Empty; } } - -public class BBMessageHeader : Packet -{ - public BBMessageHeader(BaseBulletinBoard board, BulletinMessage msg) : base(0x71) - { - var poster = SafeString(msg.PostedName); - var subject = SafeString(msg.Subject); - var time = SafeString(msg.GetTimeAsString()); - - EnsureCapacity(22 + poster.Length + subject.Length + time.Length); - - Stream.Write((byte)0x01); // Packet ID - Stream.Write(board.Serial); // Bulletin board serial - Stream.Write(msg.Serial); // Message serial - - Stream.Write(msg.Thread?.Serial ?? Serial.Zero); // Thread serial--parent - - WriteString(poster); - WriteString(subject); - WriteString(time); - } - - public void WriteString(string v) - { - var buffer = v.GetBytesUtf8(); - var len = buffer.Length + 1; - - if (len > 255) - { - len = 255; - } - - Stream.Write((byte)len); - Stream.Write(buffer, 0, len - 1); - Stream.Write((byte)0); - } - - public string SafeString(string v) => v ?? string.Empty; -} - -public class BBMessageContent : Packet -{ - public BBMessageContent(BaseBulletinBoard board, BulletinMessage msg) : base(0x71) - { - var poster = SafeString(msg.PostedName); - var subject = SafeString(msg.Subject); - var time = SafeString(msg.GetTimeAsString()); - - EnsureCapacity(22 + poster.Length + subject.Length + time.Length); - - Stream.Write((byte)0x02); // Packet ID - Stream.Write(board.Serial); // Bulletin board serial - Stream.Write(msg.Serial); // Message serial - - WriteString(poster); - WriteString(subject); - WriteString(time); - - Stream.Write((short)msg.PostedBody); - Stream.Write((short)msg.PostedHue); - - var len = msg.PostedEquip.Length; - - if (len > 255) - { - len = 255; - } - - Stream.Write((byte)len); - - for (var i = 0; i < len; ++i) - { - var eq = msg.PostedEquip[i]; - - Stream.Write((short)eq._itemID); - Stream.Write((short)eq._hue); - } - - len = msg.Lines.Length; - - if (len > 255) - { - len = 255; - } - - Stream.Write((byte)len); - - for (var i = 0; i < len; ++i) - { - WriteString(msg.Lines[i], true); - } - } - - public void WriteString(string v, bool padding = false) - { - var buffer = v.GetBytesUtf8(); - var tail = padding ? 2 : 1; - var len = buffer.Length + tail; - - if (len > 255) - { - len = 255; - } - - Stream.Write((byte)len); - Stream.Write(buffer, 0, len - tail); - - if (padding) - { - Stream.Write((short)0); // padding compensates for a client bug - } - else - { - Stream.Write((byte)0); - } - } - - public string SafeString(string v) => v ?? string.Empty; -} diff --git a/Projects/UOContent.Tests/Tests/Items/Games/Mahjong/MahjongPacketTests.cs b/Projects/UOContent.Tests/Tests/Items/Games/Mahjong/MahjongPacketTests.cs index af764e668..d5e230c68 100644 --- a/Projects/UOContent.Tests/Tests/Items/Games/Mahjong/MahjongPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/Games/Mahjong/MahjongPacketTests.cs @@ -6,42 +6,42 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] -public class MahjongPacketTests +[Collection("Sequential Tests")] +public class MahjongPacketTests : IClassFixture { [Fact] public void TestMahjongJoinGame() { - var game = (Serial)0x1024u; + Serial game = (Serial)0x1024u; - var expected = new MahjongJoinGame(game).Compile(); + var expected = new MahjongJoinGame(game).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMahjongJoinGame(game); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMahjongJoinGame(game); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(true)] [InlineData(false)] public void TestMahjongPlayersInfo(bool showScores) { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var game = new MahjongGame { ShowScores = showScores }; - game.Players.Join(m); + var game = new MahjongGame { ShowScores = showScores }; + game.Players.Join(m); - var expected = new MahjongPlayersInfo(game, m).Compile(); + var expected = new MahjongPlayersInfo(game, m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMahjongPlayersInfo(game, m); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMahjongPlayersInfo(game, m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(true, true)] @@ -50,68 +50,68 @@ public class MahjongPacketTests [InlineData(false, false)] public void TestMahjongGeneralInfo(bool showScores, bool spectatorVision) { - var game = new MahjongGame { ShowScores = showScores, SpectatorVision = spectatorVision}; + var game = new MahjongGame { ShowScores = showScores, SpectatorVision = spectatorVision}; - var expected = new MahjongGeneralInfo(game).Compile(); + var expected = new MahjongGeneralInfo(game).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMahjongGeneralInfo(game); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMahjongGeneralInfo(game); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(true)] [InlineData(false)] public void TestMahjongTilesInfo(bool spectatorVision) { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var game = new MahjongGame { SpectatorVision = spectatorVision }; - game.Players.Join(m); + var game = new MahjongGame { SpectatorVision = spectatorVision }; + game.Players.Join(m); - var expected = new MahjongTilesInfo(game, m).Compile(); + var expected = new MahjongTilesInfo(game, m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMahjongTilesInfo(game, m); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMahjongTilesInfo(game, m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(true)] [InlineData(false)] public void TestMahjongTileInfo(bool spectatorVision) { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var game = new MahjongGame { SpectatorVision = spectatorVision }; - game.Players.Join(m); + var game = new MahjongGame { SpectatorVision = spectatorVision }; + game.Players.Join(m); - var expected = new MahjongTileInfo(game.Tiles[0], m).Compile(); + var expected = new MahjongTileInfo(game.Tiles[0], m).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMahjongTileInfo(game.Tiles[0], m); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMahjongTileInfo(game.Tiles[0], m); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Fact] public void TestMahjongRelieve() { - var game = (Serial)0x1024u; + Serial game = (Serial)0x1024u; - var expected = new MahjongRelieve(game).Compile(); + var expected = new MahjongRelieve(game).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMahjongRelieve(game); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMahjongRelieve(game); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Items/Games/Mahjong/Packets.cs b/Projects/UOContent.Tests/Tests/Items/Games/Mahjong/Packets.cs index d6fd17f74..6e9f83a80 100644 --- a/Projects/UOContent.Tests/Tests/Items/Games/Mahjong/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Items/Games/Mahjong/Packets.cs @@ -7,132 +7,176 @@ public sealed class MahjongJoinGame : Packet { public MahjongJoinGame(Serial game) : base(0xDA) { - EnsureCapacity(9); + EnsureCapacity(9); - Stream.Write(game); - Stream.Write((byte)0); - Stream.Write((byte)0x19); - } + Stream.Write(game); + Stream.Write((byte)0); + Stream.Write((byte)0x19); + } } public sealed class MahjongPlayersInfo : Packet { public MahjongPlayersInfo(MahjongGame game, Mobile to) : base(0xDA) { - var players = game.Players; + var players = game.Players; - EnsureCapacity(11 + 45 * players.Seats); + EnsureCapacity(11 + 45 * players.Seats); - Stream.Write(game.Serial); - Stream.Write((byte)0); - Stream.Write((byte)0x2); + Stream.Write(game.Serial); + Stream.Write((byte)0); + Stream.Write((byte)0x2); - Stream.Write((byte)0); - Stream.Write((byte)players.Seats); + Stream.Write((byte)0); + Stream.Write((byte)players.Seats); - var n = 0; - for (var i = 0; i < players.Seats; i++) - { - var mobile = players.GetPlayer(i); - - if (mobile != null) + var n = 0; + for (var i = 0; i < players.Seats; i++) { - Stream.Write(mobile.Serial); - Stream.Write(players.DealerPosition == i ? (byte)0x1 : (byte)0x2); - Stream.Write((byte)i); + var mobile = players.GetPlayer(i); - if (game.ShowScores || mobile == to) + if (mobile != null) { - Stream.Write(players.GetScore(i)); + Stream.Write(mobile.Serial); + Stream.Write(players.DealerPosition == i ? (byte)0x1 : (byte)0x2); + Stream.Write((byte)i); + + if (game.ShowScores || mobile == to) + { + Stream.Write(players.GetScore(i)); + } + else + { + Stream.Write(0); + } + + Stream.Write((short)0); + Stream.Write((byte)0); + + Stream.Write(players.IsPublic(i)); + + Stream.WriteAsciiFixed(mobile.Name, 30); + Stream.Write(!players.IsInGamePlayer(i)); + + n++; } - else + else if (game.ShowScores) { Stream.Write(0); + Stream.Write((byte)0x2); + Stream.Write((byte)i); + + Stream.Write(players.GetScore(i)); + + Stream.Write((short)0); + Stream.Write((byte)0); + + Stream.Write(players.IsPublic(i)); + + Stream.WriteAsciiFixed("", 30); + Stream.Write(true); + + n++; } - - Stream.Write((short)0); - Stream.Write((byte)0); - - Stream.Write(players.IsPublic(i)); - - Stream.WriteAsciiFixed(mobile.Name, 30); - Stream.Write(!players.IsInGamePlayer(i)); - - n++; } - else if (game.ShowScores) + + if (n != players.Seats) { - Stream.Write(0); - Stream.Write((byte)0x2); - Stream.Write((byte)i); - - Stream.Write(players.GetScore(i)); - - Stream.Write((short)0); - Stream.Write((byte)0); - - Stream.Write(players.IsPublic(i)); - - Stream.WriteAsciiFixed("", 30); - Stream.Write(true); - - n++; + Stream.Seek(10, SeekOrigin.Begin); + Stream.Write((byte)n); } } - - if (n != players.Seats) - { - Stream.Seek(10, SeekOrigin.Begin); - Stream.Write((byte)n); - } - } } public sealed class MahjongGeneralInfo : Packet { public MahjongGeneralInfo(MahjongGame game) : base(0xDA) { - EnsureCapacity(13); + EnsureCapacity(13); - Stream.Write(game.Serial); - Stream.Write((byte)0); - Stream.Write((byte)0x5); + Stream.Write(game.Serial); + Stream.Write((byte)0); + Stream.Write((byte)0x5); - Stream.Write((short)0); - Stream.Write((byte)0); + Stream.Write((short)0); + Stream.Write((byte)0); - Stream.Write((byte)((game.ShowScores ? 0x1 : 0x0) | (game.SpectatorVision ? 0x2 : 0x0))); + Stream.Write((byte)((game.ShowScores ? 0x1 : 0x0) | (game.SpectatorVision ? 0x2 : 0x0))); - Stream.Write((byte)game.Dices.First); - Stream.Write((byte)game.Dices.Second); + Stream.Write((byte)game.Dices.First); + Stream.Write((byte)game.Dices.Second); - Stream.Write((byte)game.DealerIndicator.Wind); - Stream.Write((short)game.DealerIndicator.Position.Y); - Stream.Write((short)game.DealerIndicator.Position.X); - Stream.Write((byte)game.DealerIndicator.Direction); + Stream.Write((byte)game.DealerIndicator.Wind); + Stream.Write((short)game.DealerIndicator.Position.Y); + Stream.Write((short)game.DealerIndicator.Position.X); + Stream.Write((byte)game.DealerIndicator.Direction); - Stream.Write((short)game.WallBreakIndicator.Position.Y); - Stream.Write((short)game.WallBreakIndicator.Position.X); - } + Stream.Write((short)game.WallBreakIndicator.Position.Y); + Stream.Write((short)game.WallBreakIndicator.Position.X); + } } public sealed class MahjongTilesInfo : Packet { public MahjongTilesInfo(MahjongGame game, Mobile to) : base(0xDA) { - var tiles = game.Tiles; - var players = game.Players; + var tiles = game.Tiles; + var players = game.Players; - EnsureCapacity(11 + 9 * tiles.Length); + EnsureCapacity(11 + 9 * tiles.Length); - Stream.Write(game.Serial); - Stream.Write((byte)0); - Stream.Write((byte)0x4); + Stream.Write(game.Serial); + Stream.Write((byte)0); + Stream.Write((byte)0x4); - Stream.Write((short)tiles.Length); + Stream.Write((short)tiles.Length); + + foreach (var tile in tiles) + { + Stream.Write((byte)tile.Number); + + if (tile.Flipped) + { + var hand = tile.Dimensions.GetHandArea(); + + if (hand < 0 || players.IsPublic(hand) || players.GetPlayer(hand) == to || + game.SpectatorVision && players.IsSpectator(to)) + { + Stream.Write((byte)tile.Value); + } + else + { + Stream.Write((byte)0); + } + } + else + { + Stream.Write((byte)0); + } + + Stream.Write((short)tile.Position.Y); + Stream.Write((short)tile.Position.X); + Stream.Write((byte)tile.StackLevel); + Stream.Write((byte)tile.Direction); + + Stream.Write(tile.Flipped ? (byte)0x10 : (byte)0x0); + } + } +} + +public sealed class MahjongTileInfo : Packet +{ + public MahjongTileInfo(MahjongTile tile, Mobile to) : base(0xDA) + { + var game = tile.Game; + var players = game.Players; + + EnsureCapacity(18); + + Stream.Write(tile.Game.Serial); + Stream.Write((byte)0); + Stream.Write((byte)0x3); - foreach (var tile in tiles) - { Stream.Write((byte)tile.Number); if (tile.Flipped) @@ -161,60 +205,16 @@ public sealed class MahjongTilesInfo : Packet Stream.Write(tile.Flipped ? (byte)0x10 : (byte)0x0); } - } -} - -public sealed class MahjongTileInfo : Packet -{ - public MahjongTileInfo(MahjongTile tile, Mobile to) : base(0xDA) - { - var game = tile.Game; - var players = game.Players; - - EnsureCapacity(18); - - Stream.Write(tile.Game.Serial); - Stream.Write((byte)0); - Stream.Write((byte)0x3); - - Stream.Write((byte)tile.Number); - - if (tile.Flipped) - { - var hand = tile.Dimensions.GetHandArea(); - - if (hand < 0 || players.IsPublic(hand) || players.GetPlayer(hand) == to || - game.SpectatorVision && players.IsSpectator(to)) - { - Stream.Write((byte)tile.Value); - } - else - { - Stream.Write((byte)0); - } - } - else - { - Stream.Write((byte)0); - } - - Stream.Write((short)tile.Position.Y); - Stream.Write((short)tile.Position.X); - Stream.Write((byte)tile.StackLevel); - Stream.Write((byte)tile.Direction); - - Stream.Write(tile.Flipped ? (byte)0x10 : (byte)0x0); - } } public sealed class MahjongRelieve : Packet { public MahjongRelieve(Serial game) : base(0xDA) { - EnsureCapacity(9); + EnsureCapacity(9); - Stream.Write(game); - Stream.Write((byte)0); - Stream.Write((byte)0x1A); - } -} + Stream.Write(game); + Stream.Write((byte)0); + Stream.Write((byte)0x1A); + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Items/Jewels/T2AJewelGemCraftTests.cs b/Projects/UOContent.Tests/Tests/Items/Jewels/T2AJewelGemCraftTests.cs deleted file mode 100644 index c4a944cd0..000000000 --- a/Projects/UOContent.Tests/Tests/Items/Jewels/T2AJewelGemCraftTests.cs +++ /dev/null @@ -1,134 +0,0 @@ -using Server; -using Server.Engines.Craft; -using Server.Items; -using Server.Mobiles; -using Xunit; - -namespace UOContent.Tests; - -[Collection("Sequential UOContent Tests")] -public class T2AJewelGemCraftTests -{ - // Y=500 keeps us inside Felucca bounds; X offset avoids other sequential tests. - private static PlayerMobile CreatePlayerMobile(Map map, Point3D location) - { - var m = new PlayerMobile(World.NewMobile); - m.DefaultMobileInit(); - m.MoveToWorld(location, map); - m.AddItem(new Backpack()); - return m; - } - - // Builds a minimal tinkering ring recipe (2 iron ingots) without relying on - // DefTinkering.InitCraftList, which is gated on the feature flag at startup. - private static CraftItem MakeRingRecipe() - { - var item = new CraftItem(typeof(GoldRing), "ring", "gold ring"); - item.AddRes(typeof(IronIngot), "iron ingot", 2, "You do not have enough ingots."); - return item; - } - - // Ensures DefTinkering.CraftSystem is available (not called by the test fixture). - private static CraftSystem GetOrInitTinkeringSystem() - { - if (DefTinkering.CraftSystem == null) - { - DefTinkering.Initialize(); - } - - return DefTinkering.CraftSystem; - } - - [Fact] - public void OnCraft_ConsumesEntireTargetedGemStack_AndNamesByCount() - { - var map = Map.Felucca; - var player = CreatePlayerMobile(map, new Point3D(4100, 500, 0)); - var ring = new GoldRing(); - - try - { - var pack = player.Backpack; - pack.AddItem(new IronIngot(10)); - pack.AddItem(new Diamond(50)); // a stack of 50 diamonds - - var system = GetOrInitTinkeringSystem(); - var context = system.GetContext(player); - context.PendingGemType = GemType.Diamond; - context.PendingGemCount = 50; - - ring.OnCraft(1, false, player, system, typeof(IronIngot), null, MakeRingRecipe(), 0); - - Assert.Equal(0, pack.GetAmount(typeof(Diamond))); // all 50 consumed - Assert.Equal(GemType.Diamond, ring.GemType); - Assert.Equal(50, ring.GemCount); - // pending state cleared so the next craft starts fresh - Assert.Equal(GemType.None, context.PendingGemType); - Assert.Equal(0, context.PendingGemCount); - } - finally - { - ring.Delete(); - player.Delete(); - } - } - - [Fact] - public void OnCraft_WithUnsetGemContext_LeavesPlainPiece() - { - var map = Map.Felucca; - var player = CreatePlayerMobile(map, new Point3D(4120, 500, 0)); - var ring = new GoldRing(); - - try - { - player.Backpack.AddItem(new IronIngot(10)); - - var system = GetOrInitTinkeringSystem(); - var context = system.GetContext(player); - context.PendingGemType = GemType.None; // no gem targeted - context.PendingGemCount = 0; - - ring.OnCraft(1, false, player, system, typeof(IronIngot), null, MakeRingRecipe(), 0); - - Assert.Equal(GemType.None, ring.GemType); - Assert.Equal(0, ring.GemCount); - } - finally - { - ring.Delete(); - player.Delete(); - } - } - - [Fact] - public void OnCraft_WhenGemsUnavailableAtCraftTime_CraftsPlainPiece() - { - var map = Map.Felucca; - var player = CreatePlayerMobile(map, new Point3D(4140, 500, 0)); - var ring = new GoldRing(); - - try - { - player.Backpack.AddItem(new IronIngot(10)); - // deliberately do NOT add any diamonds - - var system = GetOrInitTinkeringSystem(); - var context = system.GetContext(player); - context.PendingGemType = GemType.Diamond; - context.PendingGemCount = 5; - - ring.OnCraft(1, false, player, system, typeof(IronIngot), null, MakeRingRecipe(), 0); - - Assert.Equal(GemType.None, ring.GemType); - Assert.Equal(0, ring.GemCount); - Assert.Equal(GemType.None, context.PendingGemType); - Assert.Equal(0, context.PendingGemCount); - } - finally - { - ring.Delete(); - player.Delete(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Items/Maps/Packets.cs b/Projects/UOContent.Tests/Tests/Items/Maps/Packets.cs index 07c1e7f4f..5198abcbf 100644 --- a/Projects/UOContent.Tests/Tests/Items/Maps/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Items/Maps/Packets.cs @@ -6,62 +6,62 @@ public sealed class MapDetails : Packet { public MapDetails(MapItem map) : base(0x90, 19) { - Stream.Write(map.Serial); - Stream.Write((short)0x139D); - Stream.Write((short)map.Bounds.Start.X); - Stream.Write((short)map.Bounds.Start.Y); - Stream.Write((short)map.Bounds.End.X); - Stream.Write((short)map.Bounds.End.Y); - Stream.Write((short)map.Width); - Stream.Write((short)map.Height); - } + Stream.Write(map.Serial); + Stream.Write((short)0x139D); + Stream.Write((short)map.Bounds.Start.X); + Stream.Write((short)map.Bounds.Start.Y); + Stream.Write((short)map.Bounds.End.X); + Stream.Write((short)map.Bounds.End.Y); + Stream.Write((short)map.Width); + Stream.Write((short)map.Height); + } } public sealed class MapDetailsNew : Packet { public MapDetailsNew(MapItem map) : base(0xF5, 21) { - Stream.Write(map.Serial); - Stream.Write((short)0x139D); - Stream.Write((short)map.Bounds.Start.X); - Stream.Write((short)map.Bounds.Start.Y); - Stream.Write((short)map.Bounds.End.X); - Stream.Write((short)map.Bounds.End.Y); - Stream.Write((short)map.Width); - Stream.Write((short)map.Height); - Stream.Write((short)(map.Facet?.MapID ?? 0)); - } + Stream.Write(map.Serial); + Stream.Write((short)0x139D); + Stream.Write((short)map.Bounds.Start.X); + Stream.Write((short)map.Bounds.Start.Y); + Stream.Write((short)map.Bounds.End.X); + Stream.Write((short)map.Bounds.End.Y); + Stream.Write((short)map.Width); + Stream.Write((short)map.Height); + Stream.Write((short)(map.Facet?.MapID ?? 0)); + } } public class MapCommand : Packet { public MapCommand(MapItem map, int command, int number, int x, int y) : base(0x56, 11) { - Stream.Write(map.Serial); - Stream.Write((byte)command); - Stream.Write((byte)number); - Stream.Write((short)x); - Stream.Write((short)y); - } + Stream.Write(map.Serial); + Stream.Write((byte)command); + Stream.Write((byte)number); + Stream.Write((short)x); + Stream.Write((short)y); + } } public sealed class MapDisplay : MapCommand { public MapDisplay(MapItem map) : base(map, 5, 0, 0, 0) { - } + } } public sealed class MapAddPin : MapCommand { public MapAddPin(MapItem map, Point2D point) : base(map, 1, 0, point.X, point.Y) { - } + } } public sealed class MapSetEditable : MapCommand { public MapSetEditable(MapItem map, bool editable) : base(map, 7, editable ? 1 : 0, 0, 0) { - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Items/Maps/TestMapItemPackets.cs b/Projects/UOContent.Tests/Tests/Items/Maps/TestMapItemPackets.cs index 0ce96e82a..67e1a6188 100644 --- a/Projects/UOContent.Tests/Tests/Items/Maps/TestMapItemPackets.cs +++ b/Projects/UOContent.Tests/Tests/Items/Maps/TestMapItemPackets.cs @@ -7,26 +7,26 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] -public class TestMapItemPackets +[Collection("Sequential Tests")] +public class TestMapItemPackets : IClassFixture { [Theory] [InlineData(ProtocolChanges.NewCharacterList)] [InlineData(ProtocolChanges.None)] public void TestSendMapDetails(ProtocolChanges changes) { - var mapItem = new MapItem(Map.Trammel); + var mapItem = new MapItem(Map.Trammel); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = changes; + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = changes; - var expected = (ns.NewCharacterList ? - (Packet)new MapDetailsNew(mapItem) : new MapDetails(mapItem)).Compile(); - ns.SendMapDetails(mapItem); + var expected = (ns.NewCharacterList ? + (Packet)new MapDetailsNew(mapItem) : new MapDetails(mapItem)).Compile(); + ns.SendMapDetails(mapItem); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(5, 0, 0, 0)] @@ -35,14 +35,14 @@ public class TestMapItemPackets [InlineData(7, 0, 0, 0)] public void TestSendMapCommand(int command, int number, int x, int y) { - var mapItem = new MapItem(Map.Trammel); + var mapItem = new MapItem(Map.Trammel); - var expected = new MapCommand(mapItem, command, number, x, y).Compile(); + var expected = new MapCommand(mapItem, command, number, x, y).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendMapCommand(mapItem, command, x, y, number > 0); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendMapCommand(mapItem, command, x, y, number > 0); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpseHairMigrationTests.cs b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpseHairMigrationTests.cs deleted file mode 100644 index 3538ca931..000000000 --- a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpseHairMigrationTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using Server; -using Server.Items; -using Xunit; - -namespace UOContent.Tests; - -// Verifies the migration read type LegacyHairInfo consumes the exact legacy on-disk -// VirtualHairInfo block the way the generated Corpse migration does: -// [bool present] then, if present, [int version][int itemId][int hue]. -// Dropping/over-reading these bytes would trip the loader's exact-length validation, -// so full consumption (no leftover bytes) is the load-bearing assertion. -public class CorpseHairMigrationTests -{ - private static byte[] WriteLegacyHairTail(bool present, int itemId, int hue) - { - var writer = new BufferWriter(true); - writer.Write(present); - if (present) - { - writer.Write(0); // legacy VirtualHairInfo serialization version - writer.Write(itemId); - writer.Write(hue); - } - - var buffer = new byte[writer.Position]; - writer.Buffer.AsSpan(0, (int)writer.Position).CopyTo(buffer); - return buffer; - } - - [Theory] - [InlineData(0x203B, 1102)] - [InlineData(0x2049, 0)] - public void LegacyHairTail_Present_RoundTrips(int itemId, int hue) - { - var buffer = WriteLegacyHairTail(true, itemId, hue); - var reader = new BufferReader(buffer); - - Assert.True(reader.ReadBool()); - var hair = new LegacyHairInfo(); - hair.Deserialize(reader); - - Assert.Equal(itemId, hair.ItemId); - Assert.Equal(hue, hair.Hue); - // The entire block must be consumed - the loader validates exact byte length. - Assert.Equal(buffer.Length, reader.Position); - } - - [Fact] - public void LegacyHairTail_Absent_ConsumesOnlyPresenceBool() - { - var buffer = WriteLegacyHairTail(false, 0, 0); - var reader = new BufferReader(buffer); - - Assert.False(reader.ReadBool()); - // Absent case: nothing else to read, and the presence bool was the only byte. - Assert.Equal(buffer.Length, reader.Position); - } -} diff --git a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpsePacketTests.cs b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpsePacketTests.cs index 40ad7c6d7..874574f19 100644 --- a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpsePacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpsePacketTests.cs @@ -7,50 +7,50 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] -public class CorpsePacketTests +[Collection("Sequential Tests")] +public class CorpsePacketTests : IClassFixture { [Fact] public void TestCorpseEquipPacket() { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var weapon = new VikingSword(); - m.EquipItem(weapon); + var weapon = new VikingSword(); + m.EquipItem(weapon); - var c = new Corpse(m, m.Items); + var c = new Corpse(m, m.Items); - var expected = new CorpseEquip(m, c).Compile(); + var expected = new CorpseEquip(m, c).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendCorpseEquip(m, c); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendCorpseEquip(m, c); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(ProtocolChanges.None)] [InlineData(ProtocolChanges.ContainerGridLines)] public void TestCorpseContainerPacket(ProtocolChanges changes) { - var m = new Mobile((Serial)0x1); - m.DefaultMobileInit(); + var m = new Mobile((Serial)0x1); + m.DefaultMobileInit(); - var weapon = new VikingSword(); - m.EquipItem(weapon); + var weapon = new VikingSword(); + m.EquipItem(weapon); - var c = new Corpse(m, m.Items); + var c = new Corpse(m, m.Items); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = changes; + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = changes; - var expected = (ns.ContainerGridLines ? (Packet)new CorpseContent6017(m, c) : new CorpseContent(m, c)).Compile(); + var expected = (ns.ContainerGridLines ? (Packet)new CorpseContent6017(m, c) : new CorpseContent(m, c)).Compile(); - ns.SendCorpseContent(m, c); + ns.SendCorpseContent(m, c); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/Packets.cs b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/Packets.cs index 4049707a5..6dd973e58 100644 --- a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/Packets.cs @@ -10,13 +10,15 @@ public sealed class CorpseEquip : Packet var list = beheld.EquipItems; var count = list.Count; + var hair = beheld.Hair; + var facialHair = beheld.FacialHair; - if (beheld.HairItemId > 0) + if (hair != null) { count++; } - if (beheld.FacialHairItemId > 0) + if (facialHair != null) { count++; } @@ -36,16 +38,16 @@ public sealed class CorpseEquip : Packet } } - if (beheld.HairItemId > 0) + if (hair?.ItemId > 0) { Stream.Write((byte)(Layer.Hair + 1)); - Stream.Write(beheld.HairSerial); + Stream.Write(hair.VirtualSerial); } - if (beheld.FacialHairItemId > 0) + if (facialHair?.ItemId > 0) { Stream.Write((byte)(Layer.FacialHair + 1)); - Stream.Write(beheld.FacialHairSerial); + Stream.Write(facialHair.VirtualSerial); } Stream.Write((byte)Layer.Invalid); @@ -59,13 +61,15 @@ public sealed class CorpseContent : Packet { var items = beheld.EquipItems; var count = items.Count; + var hair = beheld.Hair; + var facialHair = beheld.FacialHair; - if (beheld.HairItemId > 0) + if (hair != null) { count++; } - if (beheld.FacialHairItemId > 0) + if (facialHair != null) { count++; } @@ -97,30 +101,30 @@ public sealed class CorpseContent : Packet } } - if (beheld.HairItemId > 0) + if (hair?.ItemId > 0) { - Stream.Write(beheld.HairSerial); - Stream.Write((ushort)beheld.HairItemId); + Stream.Write(hair.VirtualSerial); + Stream.Write((ushort)hair.ItemId); Stream.Write((byte)0); // signed, itemID offset Stream.Write((ushort)1); Stream.Write((short)0); Stream.Write((short)0); Stream.Write(beheld.Serial); - Stream.Write((ushort)beheld.HairHue); + Stream.Write((ushort)hair.Hue); ++written; } - if (beheld.FacialHairItemId > 0) + if (facialHair?.ItemId > 0) { - Stream.Write(beheld.FacialHairSerial); - Stream.Write((ushort)beheld.FacialHairItemId); + Stream.Write(facialHair.VirtualSerial); + Stream.Write((ushort)facialHair.ItemId); Stream.Write((byte)0); // signed, itemID offset Stream.Write((ushort)1); Stream.Write((short)0); Stream.Write((short)0); Stream.Write(beheld.Serial); - Stream.Write((ushort)beheld.FacialHairHue); + Stream.Write((ushort)facialHair.Hue); ++written; } @@ -137,13 +141,15 @@ public sealed class CorpseContent6017 : Packet { var items = beheld.EquipItems; var count = items.Count; + var hair = beheld.Hair; + var facialHair = beheld.FacialHair; - if (beheld.HairItemId > 0) + if (hair != null) { count++; } - if (beheld.FacialHairItemId > 0) + if (facialHair != null) { count++; } @@ -176,32 +182,32 @@ public sealed class CorpseContent6017 : Packet } } - if (beheld.HairItemId > 0) + if (hair?.ItemId > 0) { - Stream.Write(beheld.HairSerial); - Stream.Write((ushort)beheld.HairItemId); + Stream.Write(hair.VirtualSerial); + Stream.Write((ushort)hair.ItemId); Stream.Write((byte)0); // signed, itemID offset Stream.Write((ushort)1); Stream.Write((short)0); Stream.Write((short)0); Stream.Write((byte)0); // Grid Location? Stream.Write(beheld.Serial); - Stream.Write((ushort)beheld.HairHue); + Stream.Write((ushort)hair.Hue); ++written; } - if (beheld.FacialHairItemId > 0) + if (facialHair?.ItemId > 0) { - Stream.Write(beheld.FacialHairSerial); - Stream.Write((ushort)beheld.FacialHairItemId); + Stream.Write(facialHair.VirtualSerial); + Stream.Write((ushort)facialHair.ItemId); Stream.Write((byte)0); // signed, itemID offset Stream.Write((ushort)1); Stream.Write((short)0); Stream.Write((short)0); Stream.Write((byte)0); // Grid Location? Stream.Write(beheld.Serial); - Stream.Write((ushort)beheld.FacialHairHue); + Stream.Write((ushort)facialHair.Hue); ++written; } diff --git a/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureChestDecayTests.cs b/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureChestDecayTests.cs deleted file mode 100644 index 31a839ca0..000000000 --- a/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureChestDecayTests.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using Server; -using Server.Items; -using Server.Tests; -using Xunit; - -namespace UOContent.Tests; - -[Collection("Sequential UOContent Tests")] -public class TreasureChestDecayTests -{ - public static TheoryData ChestTypes => - [ - typeof(TreasureChestLevel1), - typeof(TreasureChestLevel2), - typeof(TreasureChestLevel3), - typeof(TreasureChestLevel4) - ]; - - // DecayScheduler reads ScheduledDecayTime repeatedly and needs the reads to agree, so the - // random interval must be rolled once per chest rather than per read. - [Theory] - [MemberData(nameof(ChestTypes))] - public void DecayTimeIsStableAcrossReads(Type chestType) - { - var chest = chestType.CreateInstance(); - - try - { - var expected = chest.DecayTime; - - for (var i = 0; i < 100; i++) - { - Assert.Equal(expected, chest.DecayTime); - } - } - finally - { - chest.Delete(); - } - } - - [Theory] - [MemberData(nameof(ChestTypes))] - public void ScheduledDecayTimeIsStableAcrossReads(Type chestType) - { - var chest = chestType.CreateInstance(); - - try - { - chest.MoveToWorld(new Point3D(150, 150, 0), Map.Felucca); - - var expected = chest.ScheduledDecayTime; - - for (var i = 0; i < 100; i++) - { - Assert.Equal(expected, chest.ScheduledDecayTime); - } - } - finally - { - chest.Delete(); - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureMapChestLiftTests.cs b/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureMapChestLiftTests.cs deleted file mode 100644 index f9b8763e7..000000000 --- a/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureMapChestLiftTests.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Server; -using Server.Items; -using Server.Mobiles; -using Server.Tests; -using Xunit; - -namespace UOContent.Tests; - -[Collection("Sequential UOContent Tests")] -public class TreasureMapChestLiftTests -{ - // Coordinates chosen to avoid overlap with Tracking (1000-4000, 1000-4000) and - // DetectHidden (1000-2400, 500) test areas. - - [Fact] - public void PartialLift_MarksSplitRemainderAsLifted() - { - using var rng = new PredictableRandom(10); // RandomDouble() = 0.5, no spawn roll fires - var map = Map.Felucca; - var location = new Point3D(5000, 600, 0); - var player = CreatePlayerMobile(map, location); - var chest = new TreasureMapChest(1); - - try - { - chest.MoveToWorld(location, map); - chest.Locked = false; - - var gold = FindGold(chest, null); - Assert.NotNull(gold); - - player.Lift(gold, 1, out var rejected, out _); - Assert.False(rejected); - - // The stack split re-adds the remainder as a brand-new item. It must count as - // already lifted, otherwise every 1-coin pull grants a fresh guardian spawn roll. - var remainder = FindGold(chest, gold); - Assert.NotNull(remainder); - Assert.Contains(remainder, chest.Lifted); - Assert.Contains(gold, chest.Lifted); - } - finally - { - player.Holding?.Delete(); - player.Delete(); - chest.Delete(); - } - } - - [Fact] - public void ItemAddedAfterFill_IsMarkedLifted() - { - using var rng = new PredictableRandom(10); - var chest = new TreasureMapChest(1); - var packed = new Gold(500); - - try - { - // Anything entering the chest after the initial fill (packed-back gold, split - // remainders, GM drops) was never part of the original loot and must not - // grant spawn rolls when lifted back out. - chest.DropItem(packed); - - Assert.Contains(packed, chest.Lifted); - } - finally - { - chest.Delete(); - } - } - - [Fact] - public void OriginalFillLoot_IsNotMarkedLifted() - { - using var rng = new PredictableRandom(10); - var chest = new TreasureMapChest(1); - - try - { - // The original loot must stay roll-eligible for its first lift. - Assert.True(chest.Lifted == null || chest.Lifted.Count == 0); - } - finally - { - chest.Delete(); - } - } - - private static Gold FindGold(TreasureMapChest chest, Gold except) - { - var items = chest.Items; - - for (var i = 0; i < items.Count; i++) - { - if (items[i] is Gold gold && gold != except) - { - return gold; - } - } - - return null; - } - - private static PlayerMobile CreatePlayerMobile(Map map, Point3D location) - { - var mobile = new PlayerMobile(World.NewMobile); - mobile.DefaultMobileInit(); - mobile.MoveToWorld(location, map); - return mobile; - } -} diff --git a/Projects/UOContent.Tests/Tests/Items/Weapons/Abilities/WeaponAbilityPacketTests.cs b/Projects/UOContent.Tests/Tests/Items/Weapons/Abilities/WeaponAbilityPacketTests.cs index 1919ab2e5..ecc1b4115 100644 --- a/Projects/UOContent.Tests/Tests/Items/Weapons/Abilities/WeaponAbilityPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/Weapons/Abilities/WeaponAbilityPacketTests.cs @@ -5,7 +5,6 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] public class WeaponAbilityPacketTests { [Theory] @@ -15,24 +14,24 @@ public class WeaponAbilityPacketTests [InlineData(1000, false)] public void TestSpecialAbility(int abilityId, bool active) { - var expected = new ToggleSpecialAbility(abilityId, active).Compile(); + var expected = new ToggleSpecialAbility(abilityId, active).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendToggleSpecialAbility(abilityId, active); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendToggleSpecialAbility(abilityId, active); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Fact] public void TestClearAbility() { - var expected = new ClearWeaponAbility().Compile(); + var expected = new ClearWeaponAbility().Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendClearWeaponAbility(); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendClearWeaponAbility(); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Items/Weapons/Abilities/WeaponAbilityPackets.cs b/Projects/UOContent.Tests/Tests/Items/Weapons/Abilities/WeaponAbilityPackets.cs index adae27c5b..5a8592f08 100644 --- a/Projects/UOContent.Tests/Tests/Items/Weapons/Abilities/WeaponAbilityPackets.cs +++ b/Projects/UOContent.Tests/Tests/Items/Weapons/Abilities/WeaponAbilityPackets.cs @@ -6,13 +6,13 @@ public sealed class ToggleSpecialAbility : Packet { public ToggleSpecialAbility(int abilityID, bool active) : base(0xBF) { - EnsureCapacity(7); + EnsureCapacity(7); - Stream.Write((short)0x25); + Stream.Write((short)0x25); - Stream.Write((short)abilityID); - Stream.Write(active); - } + Stream.Write((short)abilityID); + Stream.Write(active); + } } public sealed class ClearWeaponAbility : Packet @@ -21,8 +21,8 @@ public sealed class ClearWeaponAbility : Packet public ClearWeaponAbility() : base(0xBF) { - EnsureCapacity(5); + EnsureCapacity(5); - Stream.Write((short)0x21); - } -} + Stream.Write((short)0x21); + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Items/Weapons/Throwing/ThrowingTests.cs b/Projects/UOContent.Tests/Tests/Items/Weapons/Throwing/ThrowingTests.cs deleted file mode 100644 index da896ba8b..000000000 --- a/Projects/UOContent.Tests/Tests/Items/Weapons/Throwing/ThrowingTests.cs +++ /dev/null @@ -1,390 +0,0 @@ -using System; -using Server; -using Server.Items; -using Server.Mobiles; -using Server.Tests; -using Xunit; - -namespace UOContent.Tests; - -[Collection("Sequential UOContent Tests")] -public class ThrowingTests -{ - // Exposes protected ModifyHitChance so the math can be verified directly. - private class TestThrown : BaseThrown - { - public TestThrown() : base(0x8FF) { } - - public override int MinThrowRange => 4; - - public double TestModifyHitChance(Mobile attacker, Mobile defender, double chance) => - ModifyHitChance(attacker, defender, chance); - - public int TestModifyDamage(Mobile attacker, Mobile defender, int damage) => - ModifyDamage(attacker, defender, damage); - } - - // Hit chance modifiers - - /// At optimal range with no shield the chance should not change. - [Fact] - public void ModifyHitChance_OptimalRange_NoShield_NoChange() - { - var map = Map.Felucca; - var attacker = CreateMobile(map, new Point3D(5000, 500, 0)); - var defender = CreateMobile(map, new Point3D(5005, 500, 0)); // distance 5, within range 4..7 - var weapon = new TestThrown(); - - try - { - attacker.Skills.Throwing.BaseFixedPoint = 0; - attacker.RawDex = 10; - - var result = weapon.TestModifyHitChance(attacker, defender, 0.8); - - Assert.Equal(0.8, result, 10); - } - finally - { - attacker.Delete(); - defender.Delete(); - weapon.Delete(); - } - } - - /// - /// At distance 1 with no throwing skill and minimum dex, close to the full -12% applies. - /// Elf race avoids the Human Jack-of-All-Trades 20.0 skill floor. - /// RawDex clamps to 1 minimum, so mitigation = (0+1)/20 = 0.05 -> penalty = 0.1195. - /// - [Fact] - public void ModifyHitChance_CloseQuarters_MinimumSkill_AppliesNearFullPenalty() - { - var map = Map.Felucca; - var attacker = CreateMobile(map, new Point3D(5100, 500, 0)); - var defender = CreateMobile(map, new Point3D(5101, 500, 0)); // distance 1 - var weapon = new TestThrown(); - - try - { - attacker.Race = Race.Elf; // humans have Jack-of-All-Trades (+20.0 floor on all skills) - attacker.Skills.Throwing.BaseFixedPoint = 0; - attacker.RawDex = 1; // minimum - - var result = weapon.TestModifyHitChance(attacker, defender, 0.8); - - Assert.Equal(0.6805, result, 10); - } - finally - { - attacker.Delete(); - defender.Delete(); - weapon.Delete(); - } - } - - /// At distance 0 with 120 skill and 120 dex, (120+120)/20 = 12 caps mitigation, no penalty. - [Fact] - public void ModifyHitChance_CloseQuarters_MaxSkillAndDex_NoChange() - { - var map = Map.Felucca; - var attacker = CreateMobile(map, new Point3D(5200, 500, 0)); - var defender = CreateMobile(map, new Point3D(5200, 500, 0)); // same tile, distance 0 - var weapon = new TestThrown(); - - try - { - attacker.Skills.Throwing.BaseFixedPoint = 1200; // 120.0 - attacker.RawDex = 120; - - var result = weapon.TestModifyHitChance(attacker, defender, 0.8); - - Assert.Equal(0.8, result, 10); - } - finally - { - attacker.Delete(); - defender.Delete(); - weapon.Delete(); - } - } - - /// Distance 2 is above melee range but below MinThrowRange of 4, so the flat -12% applies. - [Fact] - public void ModifyHitChance_BelowMinRange_AppliesFlatPenalty() - { - var map = Map.Felucca; - var attacker = CreateMobile(map, new Point3D(5300, 500, 0)); - var defender = CreateMobile(map, new Point3D(5302, 500, 0)); // distance 2 - var weapon = new TestThrown(); - - try - { - var result = weapon.TestModifyHitChance(attacker, defender, 0.8); - - Assert.Equal(0.68, result, 10); - } - finally - { - attacker.Delete(); - defender.Delete(); - weapon.Delete(); - } - } - - /// - /// Shield at 100 Parry: 1200/100 = 12% penalty, chance * 0.88. - /// Layer must be set explicitly since tile data is not loaded in tests. - /// - [Fact] - public void ModifyHitChance_Shield_100Parry_AppliesCorrectPenalty() - { - var map = Map.Felucca; - var attacker = CreateMobile(map, new Point3D(5400, 500, 0)); - var defender = CreateMobile(map, new Point3D(5405, 500, 0)); // distance 5, optimal - var weapon = new TestThrown(); - var shield = new Buckler { Layer = Layer.TwoHanded }; // tile data not loaded in tests - - try - { - attacker.Skills.Parry.BaseFixedPoint = 1000; // 100.0 - attacker.AddItem(shield); - - var result = weapon.TestModifyHitChance(attacker, defender, 0.8); - - Assert.Equal(0.704, result, 10); - } - finally - { - attacker.Delete(); - defender.Delete(); - weapon.Delete(); - } - } - - /// Shield at 120 Parry: 1200/120 = 10% penalty, chance * 0.90. - [Fact] - public void ModifyHitChance_Shield_120Parry_AppliesCorrectPenalty() - { - var map = Map.Felucca; - var attacker = CreateMobile(map, new Point3D(5500, 500, 0)); - var defender = CreateMobile(map, new Point3D(5505, 500, 0)); // distance 5, optimal - var weapon = new TestThrown(); - var shield = new Buckler { Layer = Layer.TwoHanded }; - - try - { - attacker.Skills.Parry.BaseFixedPoint = 1200; // 120.0 - attacker.AddItem(shield); - - var result = weapon.TestModifyHitChance(attacker, defender, 0.8); - - Assert.Equal(0.72, result, 10); - } - finally - { - attacker.Delete(); - defender.Delete(); - weapon.Delete(); - } - } - - /// - /// Shield at 0 Parry hits the 90% cap, chance * 0.10. - /// Elf race prevents JOAT from raising Parry to 20.0. - /// - [Fact] - public void ModifyHitChance_Shield_ZeroParry_CapsAt90Percent() - { - var map = Map.Felucca; - var attacker = CreateMobile(map, new Point3D(5600, 500, 0)); - var defender = CreateMobile(map, new Point3D(5605, 500, 0)); // distance 5, optimal - var weapon = new TestThrown(); - var shield = new Buckler { Layer = Layer.TwoHanded }; - - try - { - attacker.Race = Race.Elf; // humans have Jack-of-All-Trades (+20.0 floor on all skills) - attacker.Skills.Parry.BaseFixedPoint = 0; - attacker.AddItem(shield); - - var result = weapon.TestModifyHitChance(attacker, defender, 0.8); - - Assert.Equal(0.08, result, 10); - } - finally - { - attacker.Delete(); - defender.Delete(); - weapon.Delete(); - } - } - - // DefMaxRange STR scaling (uses SoulGlaive: StrReq 60, Min 8, Max 11) - - [Fact] - public void DefMaxRange_AtStrReq_EqualsMinThrowRange() - { - var weapon = new SoulGlaive(); - var attacker = CreateMobile(Map.Felucca, new Point3D(5800, 500, 0)); - try - { - attacker.RawStr = 60; // == AosStrengthReq - attacker.AddItem(weapon); - Assert.Equal(weapon.MinThrowRange, weapon.DefMaxRange); // 8 - } - finally - { - weapon.Delete(); - attacker.Delete(); - } - } - - [Fact] - public void DefMaxRange_At140Str_EqualsMaxThrowRange() - { - var weapon = new SoulGlaive(); - var attacker = CreateMobile(Map.Felucca, new Point3D(5810, 500, 0)); - try - { - attacker.RawStr = 140; - attacker.AddItem(weapon); - Assert.Equal(weapon.MaxThrowRange, weapon.DefMaxRange); // 11 - } - finally - { - weapon.Delete(); - attacker.Delete(); - } - } - - [Fact] - public void DefMaxRange_AboveMaxStr_IsCappedAtMaxThrowRange() - { - var weapon = new SoulGlaive(); - var attacker = CreateMobile(Map.Felucca, new Point3D(5820, 500, 0)); - try - { - attacker.RawStr = 200; // uncapped formula would give 13 - attacker.AddItem(weapon); - Assert.Equal(weapon.MaxThrowRange, weapon.DefMaxRange); // 11, not 13 - } - finally - { - weapon.Delete(); - attacker.Delete(); - } - } - - [Fact] - public void DefMaxRange_BelowStrReq_FlooredAtMinThrowRange() - { - var weapon = new SoulGlaive(); - var attacker = CreateMobile(Map.Felucca, new Point3D(5830, 500, 0)); - try - { - attacker.RawStr = 10; // below StrReq 60 - attacker.AddItem(weapon); - Assert.Equal(weapon.MinThrowRange, weapon.DefMaxRange); // 8, not 6 - } - finally - { - weapon.Delete(); - attacker.Delete(); - } - } - - /// At the outermost admissible tile (dist == MaxRange) a throw loses 47% damage. - [Fact] - public void ModifyDamage_AtMaxRange_Reduces47Percent() - { - var map = Map.Felucca; - var attacker = CreateMobile(map, new Point3D(5900, 500, 0)); - var weapon = new TestThrown(); // MinThrowRange 4 -> MaxThrowRange 7 - try - { - attacker.RawStr = 140; // MaxRange == MaxThrowRange == 7 - attacker.AddItem(weapon); - var defender = CreateMobile(map, new Point3D(5907, 500, 0)); // distance 7 == MaxRange - try - { - Assert.Equal(53, weapon.TestModifyDamage(attacker, defender, 100)); - } - finally - { - defender.Delete(); - } - } - finally - { - weapon.Delete(); - attacker.Delete(); - } - } - - /// Inside max range, damage is unchanged. - [Fact] - public void ModifyDamage_WithinRange_NoChange() - { - var map = Map.Felucca; - var attacker = CreateMobile(map, new Point3D(5920, 500, 0)); - var weapon = new TestThrown(); - try - { - attacker.RawStr = 140; // MaxRange 7 - attacker.AddItem(weapon); - var defender = CreateMobile(map, new Point3D(5925, 500, 0)); // distance 5 < 7 - try - { - Assert.Equal(100, weapon.TestModifyDamage(attacker, defender, 100)); - } - finally - { - defender.Delete(); - } - } - finally - { - weapon.Delete(); - attacker.Delete(); - } - } - - /// Close-quarters mitigation uses RawDex, so a Dex debuff does not change the penalty. - [Fact] - public void ModifyHitChance_CloseQuarters_UsesRawDex_IgnoresStatMods() - { - var map = Map.Felucca; - var attacker = CreateMobile(map, new Point3D(5940, 500, 0)); - var defender = CreateMobile(map, new Point3D(5941, 500, 0)); // distance 1 - var weapon = new TestThrown(); - try - { - attacker.Race = Race.Elf; // avoid Human JOAT skill floor - attacker.Skills.Throwing.BaseFixedPoint = 0; - attacker.RawDex = 40; - // RawDex-based mitigation = (0 + 40)/20 = 2.0 -> penalty = (12 - 2)/100 = 0.10 - var baseline = weapon.TestModifyHitChance(attacker, defender, 0.8); // 0.70 - - attacker.AddStatMod(new StatMod(StatType.Dex, "curse", -30, TimeSpan.Zero)); // effective Dex 10 - var withMod = weapon.TestModifyHitChance(attacker, defender, 0.8); - - Assert.Equal(0.70, baseline, 10); - Assert.Equal(baseline, withMod, 10); // unchanged because RawDex is used - } - finally - { - attacker.Delete(); - defender.Delete(); - weapon.Delete(); - } - } - - private static PlayerMobile CreateMobile(Map map, Point3D location) - { - var mobile = new PlayerMobile(World.NewMobile); - mobile.DefaultMobileInit(); - mobile.MoveToWorld(location, map); - return mobile; - } -} diff --git a/Projects/UOContent.Tests/Tests/Misc/NameVerificationTests.cs b/Projects/UOContent.Tests/Tests/Misc/NameVerificationTests.cs deleted file mode 100644 index a9e4f5c27..000000000 --- a/Projects/UOContent.Tests/Tests/Misc/NameVerificationTests.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System.Buffers; -using Server.Misc; -using Xunit; - -namespace Server.Tests; - -public class NameVerificationTests -{ - [Theory] - [InlineData("John")] - [InlineData("Mary Ann")] - [InlineData("Bob-Jones")] - [InlineData("O'Malley")] - public void ValidatePlayerName_ValidNames_ReturnsTrue(string name) - { - Assert.True(NameVerification.ValidatePlayerName(name)); - } - - [Theory] - [InlineData("Rex")] - [InlineData("MrWhiskers")] - [InlineData("Fido")] - public void ValidatePetName_ValidNames_ReturnsTrue(string name) - { - Assert.True(NameVerification.ValidatePetName(name)); - } - - [Theory] - [InlineData("Mrs-Smith")] - [InlineData("Mr. Whiskers")] - [InlineData("Dog123")] - public void ValidatePetName_InvalidNames_ReturnsFalse(string name) - { - Assert.False(NameVerification.ValidatePetName(name)); - } - - [Theory] - [InlineData("Blacksmith12")] - [InlineData("Baker")] - [InlineData("Innkeeper")] - public void ValidateVendorName_ValidNames_ReturnsTrue(string name) - { - Assert.True(NameVerification.ValidateVendorName(name)); - } - - [Theory] - [InlineData("Blacksmith 123")] - [InlineData("Baker-Smith")] - [InlineData("*Innkeeper*")] - public void ValidateVendorName_ValidNames_ReturnsFalse(string name) - { - Assert.False(NameVerification.ValidateVendorName(name)); - } - - [Fact] - public void Validate_MinimumLengthName_ReturnsTrue() - { - Assert.True(NameVerification.Validate("Ab", 2, 16, true, false)); - } - - [Fact] - public void Validate_MaximumLengthName_ReturnsTrue() - { - Assert.True(NameVerification.Validate("AbcdefghijklmnopqrsT", 1, 20, true, true)); - } - - [Fact] - public void Validate_MaxExceptions_ReturnsTrue() - { - var exceptions = SearchValues.Create(' ', '-', '.'); - Assert.True(NameVerification.Validate("A-B.C D", 2, 16, true, false, false, 3, exceptions)); - } - - [Fact] - public void Validate_BoundaryOfDisallowedWord_ReturnsTrueWhenNotActuallyDisallowed() - { - // "ass" is in disallowed, but "class" has proper boundaries - Assert.True(NameVerification.ValidatePlayerName("Class")); - } - - // Negative Tests - [Fact] - public void Validate_EmptyName_ReturnsFalse() - { - Assert.False(NameVerification.Validate("", 1, 20, true, true)); - } - - [Fact] - public void Validate_TooShortName_ReturnsFalse() - { - Assert.False(NameVerification.Validate("A", 2, 16, true, false)); - } - - [Fact] - public void Validate_TooLongName_ReturnsFalse() - { - Assert.False(NameVerification.Validate("AbcdefghijklmnopqrstuvwxyzABCDEF", 2, 16, true, false)); - } - - [Theory] - [InlineData("ass")] - [InlineData("GodDamn Fine")] - [InlineData("Fuck")] - public void Validate_DisallowedWords_ReturnsFalse(string name) - { - Assert.False(NameVerification.ValidatePlayerName(name)); - } - - [Theory] - [InlineData("GMJohn")] - [InlineData("LordBob")] - [InlineData("SeerMagic")] - public void Validate_DisallowedPrefixes_ReturnsFalse(string name) - { - Assert.False(NameVerification.ValidatePlayerName(name)); - } - - [Fact] - public void Validate_TooManyExceptions_ReturnsFalse() - { - var exceptions = SearchValues.Create(' ', '-', '.'); - Assert.False(NameVerification.Validate("A-B.C D-E", 2, 16, true, false, false, 1, exceptions)); - } - - [Fact] - public void Validate_ExceptionAtStartWhenNotAllowed_ReturnsFalse() - { - var exceptions = SearchValues.Create(' ', '-', '.'); - Assert.False(NameVerification.Validate("-John", 2, 16, true, false, true, 1, exceptions)); - } - - [Fact] - public void Validate_DisallowedCharacters_ReturnsFalse() - { - Assert.False(NameVerification.Validate("John123", 2, 16, true, false)); - } -} diff --git a/Projects/UOContent.Tests/Tests/Misc/ProfanityProtectionTests.cs b/Projects/UOContent.Tests/Tests/Misc/ProfanityProtectionTests.cs deleted file mode 100644 index 012d649b8..000000000 --- a/Projects/UOContent.Tests/Tests/Misc/ProfanityProtectionTests.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Server.Misc; -using Xunit; - -namespace Server.Tests; - -public class ProfanityProtectionTests -{ - [Theory] - [InlineData("Hello world")] - [InlineData("This is a normal conversation")] - [InlineData("I would like to trade with you")] - public void Speech_WithoutProfanity_PassesValidation(string speech) - { - Assert.False(ProfanityProtection.ContainsProfanity(speech)); - } - - [Fact] - public void Speech_Empty_PassesValidation() - { - Assert.False(ProfanityProtection.ContainsProfanity("")); - } - - [Theory] - [InlineData("I'm going to class tomorrow")] // contains "ass" but in "class" - [InlineData("This assignment is hard")] // contains "ass" but in "assignment" - [InlineData("That's a nice cocktail")] // contains "cock" but in "cocktail" - public void Speech_WithWordsThatLookLikeProfanity_PassesValidation(string speech) - { - Assert.False(ProfanityProtection.ContainsProfanity(speech)); - } - - [Theory] - [InlineData("This is ass")] - [InlineData("What the fuck")] - [InlineData("You're a bitch")] - public void Speech_WithProfanity_FailsValidation(string speech) - { - Assert.True(ProfanityProtection.ContainsProfanity(speech)); - } - - [Theory] - [InlineData("ass")] // standalone profanity - [InlineData("an ass joke")] // profanity with word boundaries - [InlineData("ass.")] // profanity followed by punctuation - public void ContainsDisallowedWord_DetectsProfanityWithBoundaries(string speech) - { - Assert.True(NameVerification.ContainsDisallowedWord( - speech, - ProfanityProtection.Disallowed, - ProfanityProtection.DisallowedSearchValues - )); - } -} diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/ApproachTargetTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/ApproachTargetTests.cs deleted file mode 100644 index 923f4aff9..000000000 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/ApproachTargetTests.cs +++ /dev/null @@ -1,285 +0,0 @@ -using System.Collections.Generic; -using Server.Engines.Pathing.Cache; -using Server.Mobiles; -using Xunit; - -namespace Server.Tests.Mobiles.AI; - -// AI movement tests drive the real BaseAI primitives against live map statics. The -// pathfinder uses non-reentrant static buffers, so we share the pathfinding sequential -// collection to avoid parallel interference. -[Collection("Sequential Pathfinding Tests")] -public class ApproachTargetTests -{ - private sealed class FollowerStub : BaseCreature - { - // Serial ctor + DefaultMobileInit bypasses NPCSpeeds JSON (absent in tests). - public FollowerStub(Serial serial) : base(serial) => Body = 0xC9; - } - - private sealed class TargetStub : Mobile - { - public TargetStub() => Body = 0xC9; - } - - private static (FollowerStub bc, BaseAI ai) NewFollower(Map map, Point3D loc) - { - var bc = new FollowerStub(World.NewMobile); - bc.DefaultMobileInit(); - bc.MoveToWorld(loc, map); - BaseAI ai = new AnimalAI(bc); - ai.AITimer?.Stop(); // we drive movement manually; no background timer - return (bc, ai); - } - - // Drives the pet follow primitive once per tick; returns true once it reaches within - // arriveDist tiles. Uses InRange (Chebyshev) to match the game's own range semantics — - // a diagonal neighbor at Euclidean ~2.24 is "within 2" to the engine. - private static bool DriveFollow(BaseAI ai, Mobile bc, Mobile target, int arriveDist, int maxTicks) - { - for (var i = 0; i < maxTicks; i++) - { - ai.NextMove = 0; - ai.WalkMobileRange(target, 1, false, 1, 2); - if (bc.InRange(target, arriveDist)) - { - return true; - } - } - return false; - } - - private static ushort FirstImpassableItemId() - { - for (ushort id = 1; id < TileData.MaxItemValue; id++) - { - if (TileData.ItemTable[id].ImpassableSurface) - { - return id; - } - } - return 0; - } - - [Fact] - public void OpenTerrain_ReachesTarget_WithoutPathfinding() - { - var map = Map.Maps[1]; - Assert.NotNull(map); - map.GetAverageZ(1500, 1600, out _, out var z, out _); - - var (bc, ai) = NewFollower(map, new Point3D(1500, 1600, (sbyte)z)); - var target = new TargetStub(); - target.MoveToWorld(new Point3D(1495, 1600, (sbyte)z), map); // 5 tiles west, open - - StepCache.Instance.Clear(); - var buildsBefore = StepCache.Instance.GetStats().BuildsTotal; - - var arrived = DriveFollow(ai, bc, target, 2, 40); - - var buildsAfter = StepCache.Instance.GetStats().BuildsTotal; - bc.Delete(); - target.Delete(); - - Assert.True(arrived, "creature should reach an open-terrain target by stepping"); - Assert.Equal(buildsBefore, buildsAfter); // greedy fast path: no chunk builds - } - - [Fact] - public void BritainInnDesk_PetReachesMaster() - { - var map = Map.Maps[1]; - Assert.NotNull(map); - - // Exact in-game repro: pet south of the L-desk, master north of it. - var (bc, ai) = NewFollower(map, new Point3D(1493, 1614, 20)); - var target = new TargetStub(); - target.MoveToWorld(new Point3D(1494, 1605, 21), map); - - StepCache.Instance.Clear(); - - // 200 ticks is generous for the ~17-step detour at one step per tick. - var arrived = DriveFollow(ai, bc, target, 2, 200); - - bc.Delete(); - target.Delete(); - - Assert.True(arrived, "pet must navigate around the L-desk to reach the master"); - } - - [Fact] - public void MoveTo_ReachesTargetAroundDesk() - { - var map = Map.Maps[1]; - Assert.NotNull(map); - - var (bc, ai) = NewFollower(map, new Point3D(1493, 1614, 20)); - var target = new TargetStub(); - target.MoveToWorld(new Point3D(1494, 1605, 21), map); - - StepCache.Instance.Clear(); - - var arrived = false; - for (var i = 0; i < 200; i++) - { - ai.NextMove = 0; - ai.MoveTo(target, false, 1); - if (bc.InRange(target, 1)) - { - arrived = true; - break; - } - } - - bc.Delete(); - target.Delete(); - - Assert.True(arrived, "MoveTo chaser must reach the target around the desk"); - } - - [Fact] - public void MoveTo_CatchesTargetWalkingAway_OpenTerrain() - { - var map = Map.Maps[1]; - Assert.NotNull(map); - map.GetAverageZ(1500, 1600, out _, out var z, out _); - - var (bc, ai) = NewFollower(map, new Point3D(1500, 1600, (sbyte)z)); - var target = new TargetStub(); - target.MoveToWorld(new Point3D(1498, 1600, (sbyte)z), map); - - StepCache.Instance.Clear(); - - var caught = false; - for (var i = 0; i < 60; i++) - { - ai.NextMove = 0; - ai.MoveTo(target, true, 1); - - // Target walks west every other tick for its first several steps, then stops, - // so a same-speed chaser eventually closes the gap. - if (i % 2 == 0 && i < 16 && target.X > 1490) - { - target.MoveToWorld(new Point3D(target.X - 1, target.Y, target.Z), map); - } - - if (bc.InRange(target, 1)) - { - caught = true; - break; - } - } - - bc.Delete(); - target.Delete(); - - Assert.True(caught, "chaser must catch a target that walks away then stops"); - } - - [SkippableFact] - public void UnreachableTarget_GivesUp_AndIdles() - { - TileDataRequirement.SkipIfMissing(); - var map = Map.Maps[1]; - Assert.NotNull(map); - map.GetAverageZ(1500, 1601, out _, out var z, out _); - - var (bc, ai) = NewFollower(map, new Point3D(1500, 1601, (sbyte)z)); - var target = new TargetStub(); - target.MoveToWorld(new Point3D(1500, 1596, (sbyte)z), map); - - // Full impassable ring around the target cell -> no path to within range 1. - var id = FirstImpassableItemId(); - Assert.NotEqual(0, id); - var ring = new List(); - for (var x = 1499; x <= 1501; x++) - { - for (var y = 1595; y <= 1597; y++) - { - if (x == 1500 && y == 1596) - { - continue; // leave the target's own cell - } - map.GetAverageZ(x, y, out _, out var rz, out _); - ring.Add(new Item(World.NewItem) - { - ItemID = id, Map = map, Location = new Point3D(x, y, (sbyte)rz) - }); - } - } - - StepCache.Instance.Clear(); - - // Run well past the stuck window so give-up engages. - for (var i = 0; i < 120; i++) - { - ai.NextMove = 0; - ai.MoveTo(target, false, 1); - } - - // After giving up, the creature must idle (not oscillate) while the goal is still. - var idleStart = bc.Location; - var stayedIdle = true; - for (var i = 0; i < 20; i++) - { - ai.NextMove = 0; - ai.MoveTo(target, false, 1); - if (bc.Location != idleStart) - { - stayedIdle = false; - break; - } - } - - var reached = bc.InRange(target, 1); - - bc.Delete(); - target.Delete(); - foreach (var it in ring) - { - it.Delete(); - } - - Assert.False(reached, "walled-off target must not be reached"); - Assert.True(stayedIdle, "after giving up, the creature must idle, not shuffle"); - } - - [SkippableFact] - public void WallBetween_RoutesAround_ReachesTarget() - { - TileDataRequirement.SkipIfMissing(); - var map = Map.Maps[1]; - Assert.NotNull(map); - map.GetAverageZ(1500, 1600, out _, out var z, out _); - - var (bc, ai) = NewFollower(map, new Point3D(1500, 1600, (sbyte)z)); - var target = new TargetStub(); - target.MoveToWorld(new Point3D(1500, 1595, (sbyte)z), map); // 5 tiles north - - // Impassable wall on Y=1598 from X=1497..1503: blocks the straight line; ends open. - var id = FirstImpassableItemId(); - Assert.NotEqual(0, id); - var wall = new List(); - for (var x = 1497; x <= 1503; x++) - { - map.GetAverageZ(x, 1598, out _, out var wz, out _); - wall.Add(new Item(World.NewItem) - { - ItemID = id, Map = map, Location = new Point3D(x, 1598, (sbyte)wz) - }); - } - - StepCache.Instance.Clear(); - - var arrived = DriveFollow(ai, bc, target, 2, 200); - - bc.Delete(); - target.Delete(); - foreach (var it in wall) - { - it.Delete(); - } - - Assert.True(arrived, "creature must route around the wall to reach the target"); - } -} diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/MoveSpeedTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/MoveSpeedTests.cs deleted file mode 100644 index 7d9243a29..000000000 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/MoveSpeedTests.cs +++ /dev/null @@ -1,219 +0,0 @@ -using System; -using System.Collections.Generic; -using Server; -using Server.Mobiles; -using Xunit; - -namespace UOContent.Tests.Mobiles.AI; - -// Pins the CurrentMoveSpeed classification (verbatim active/passive maps to the matching -// move value; bespoke stays fused), SetSpeed's one-clock guarantee, and the v22 tail. -[Collection("Sequential UOContent Tests")] -public class MoveSpeedTests : IDisposable -{ - // Delete spawned stubs so they don't linger in the shared static World. - private readonly List _created = new(); - - public void Dispose() - { - for (var i = 0; i < _created.Count; i++) - { - _created[i].Delete(); - } - } - - private sealed class SpeedStub : BaseCreature - { - // Stands in for the npc-speeds table (unconfigured in the test fixture). - public double TableActiveMove; - public double TablePassiveMove; - - public SpeedStub() : base(AIType.AI_Animal) => Body = 0xC9; - - public SpeedStub(Serial serial) : base(serial) => Body = 0xC9; - - public override void GetSpeeds(out double activeSpeed, out double passiveSpeed) - { - activeSpeed = 0.3; - passiveSpeed = 0.6; - } - - public override void GetMoveSpeeds(out double activeMoveSpeed, out double passiveMoveSpeed) - { - activeMoveSpeed = TableActiveMove; - passiveMoveSpeed = TablePassiveMove; - } - } - - private SpeedStub NewCreature() - { - var bc = new SpeedStub(); - _created.Add(bc); - return bc; - } - - [Fact] - public void MoveSpeeds_InheritThinkValues_ByDefault() - { - var bc = NewCreature(); - - Assert.Equal(0.3, bc.ActiveMoveSpeed); - Assert.Equal(0.6, bc.PassiveMoveSpeed); - Assert.Equal(bc.CurrentSpeed, bc.CurrentMoveSpeed); - } - - [Fact] - public void CurrentMoveSpeed_ResolvesPerMode_WhenOverridden() - { - var bc = NewCreature(); - bc.SetMoveSpeed(0.45, 0.9); - - // SetSpeed left the creature passive; the think clock is untouched. - Assert.Equal(0.6, bc.CurrentSpeed); - Assert.Equal(0.9, bc.CurrentMoveSpeed); - - bc.SetCurrentSpeedToActive(); - Assert.Equal(0.3, bc.CurrentSpeed); - Assert.Equal(0.45, bc.CurrentMoveSpeed); - } - - [Fact] - public void CurrentMoveSpeed_BespokePace_StaysFused() - { - var bc = NewCreature(); - bc.SetMoveSpeed(0.45, 0.9); - - // Neither think value verbatim, so both clocks run it. - bc.CurrentSpeed = 0.11; - Assert.Equal(0.11, bc.CurrentMoveSpeed); - } - - [Fact] - public void SetSpeed_ClearsMoveOverrides() - { - var bc = NewCreature(); - bc.SetMoveSpeed(0.45, 0.9); - - bc.SetSpeed(0.2, 0.4); - - Assert.Equal(0.2, bc.ActiveMoveSpeed); - Assert.Equal(0.4, bc.PassiveMoveSpeed); - } - - [Fact] - public void NonPositiveMoveSpeed_ClearsThatOverride() - { - var bc = NewCreature(); - bc.SetMoveSpeed(0.45, 0.9); - - bc.ActiveMoveSpeed = 0; - - Assert.Equal(0.3, bc.ActiveMoveSpeed); // inheriting again - Assert.Equal(0.9, bc.PassiveMoveSpeed); // other override untouched - } - - [Fact] - public void ScaleMoveSpeed_ScalesOverrides_LeavesInheritAlone() - { - var bc = NewCreature(); - bc.ActiveMoveSpeed = 0.6; // passive left inheriting - - bc.ScaleMoveSpeed(1.0 / 1.2); - - Assert.Equal(0.5, bc.ActiveMoveSpeed); - Assert.Equal(bc.PassiveSpeed, bc.PassiveMoveSpeed); // still inheriting, not 0 * scalar - } - - [Fact] - public void Herding_DrivesMoveClock_ThinkUntouched() - { - var bc = NewCreature(); // think 0.3/0.6, passive - bc.SetMoveSpeed(0.45, 1.05); - - bc.TargetLocation = new Point2D(10, 10); - - Assert.Equal(0.6, bc.CurrentSpeed); // think clock unaffected by herding - Assert.Equal(0.3, bc.CurrentMoveSpeed); // fixed herding pace, not 1.05 - - bc.TargetLocation = null; - Assert.Equal(1.05, bc.CurrentMoveSpeed); - } - - [Fact] - public void SnapSpeedsToTable_UndoesScalingDrift_KeepsTunedValues() - { - var bc = NewCreature(); - bc.TableActiveMove = 0.45; - bc.TablePassiveMove = 0.9; - bc.SetMoveSpeed(0.45, 0.9); - - // 0.45 and 0.9 do not survive /1.2 then *1.2 bit-exactly. - bc.ScaleMoveSpeed(1.0 / 1.2); - bc.ScaleMoveSpeed(1.2); - Assert.NotEqual(0.45, bc.ActiveMoveSpeed); - - bc.SnapSpeedsToTable(); - Assert.Equal(0.45, bc.ActiveMoveSpeed); - Assert.Equal(0.9, bc.PassiveMoveSpeed); - - // A hand-tuned value is nowhere near the epsilon and must keep. - bc.SetMoveSpeed(0.7, 0.9); - bc.SnapSpeedsToTable(); - Assert.Equal(0.7, bc.ActiveMoveSpeed); - } - - [Fact] - public void Migration_MatchingThinkSpeeds_AdoptTableMoveValues() - { - var bc = NewCreature(); // think 0.3/0.6, matching its table entry - bc.TableActiveMove = 0.45; - bc.TablePassiveMove = 0.9; - - bc.MigrateMoveSpeeds(); - - Assert.Equal(0.45, bc.ActiveMoveSpeed); - Assert.Equal(0.9, bc.PassiveMoveSpeed); - } - - [Fact] - public void Migration_TunedThinkSpeeds_KeepInheriting() - { - var bc = NewCreature(); - bc.SetSpeed(0.35, 0.6); // hand-tuned: no longer matches the table entry - bc.TableActiveMove = 0.45; - bc.TablePassiveMove = 0.9; - - bc.MigrateMoveSpeeds(); - - Assert.Equal(0.35, bc.ActiveMoveSpeed); - Assert.Equal(0.6, bc.PassiveMoveSpeed); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void MoveSpeedOverrides_SurviveSerialization(bool overridden) - { - var bc = NewCreature(); - if (overridden) - { - bc.SetMoveSpeed(0.45, 0.9); - } - - var writer = new BufferWriter(true); - bc.Serialize(writer); - - var buffer = new byte[writer.Position]; - writer.Buffer.AsSpan(0, (int)writer.Position).CopyTo(buffer); - - var copy = new SpeedStub(World.NewMobile); - _created.Add(copy); - var reader = new BufferReader(buffer); - copy.Deserialize(reader); - - // The v22 tail is the last block; exact consumption catches any offset mistake. - Assert.Equal(buffer.Length, reader.Position); - Assert.Equal(overridden ? 0.45 : 0.3, copy.ActiveMoveSpeed); - Assert.Equal(overridden ? 0.9 : 0.6, copy.PassiveMoveSpeed); - } -} diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/PetOrderTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/PetOrderTests.cs deleted file mode 100644 index e0fe22a39..000000000 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/PetOrderTests.cs +++ /dev/null @@ -1,236 +0,0 @@ -using System; -using System.Collections.Generic; -using Server; -using Server.Mobiles; -using Xunit; - -namespace UOContent.Tests.Mobiles.AI; - -[Collection("Sequential UOContent Tests")] -public class PetOrderTests : IDisposable -{ - // Track and delete every mobile we spawn so they don't linger in the shared static World - // and pollute other tests in this collection (e.g. Tracking's nearby-mobile scan). - private readonly List _created = new(); - - private (PlayerMobile master, PetTestStub pet) Spawn(Point3D masterLoc, Point3D petLoc) - { - var pair = PetTestSetup.SpawnControlledPet(masterLoc, petLoc); - _created.Add(pair.master); - _created.Add(pair.pet); - return pair; - } - - public void Dispose() - { - foreach (var m in _created) - { - m?.Delete(); - } - - _created.Clear(); - } - [Fact] - public void SetPersistentOrder_Stay_AnchorsHomeToCurrentLocation() - { - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1005, 1000, 0)); - - pet.AIObject.SetPersistentOrder(OrderType.Stay); - - Assert.Equal(OrderType.Stay, pet.AIObject.PersistentOrder); - Assert.Equal(pet.Location, pet.Home); - } - - [Fact] - public void SetPersistentOrder_Follow_ClearsAnchor() - { - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1005, 1000, 0)); - pet.Home = new Point3D(900, 900, 0); // stale anchor - - pet.AIObject.SetPersistentOrder(OrderType.Follow); - - Assert.Equal(OrderType.Follow, pet.AIObject.PersistentOrder); - Assert.Equal(Point3D.Zero, pet.Home); - } - - [Fact] - public void Stop_WhileAttacking_FallsBackToPersistentFollow() - { - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1002, 1000, 0)); - pet.ControlOrder = OrderType.Follow; // persistent = Follow - pet.ControlOrder = OrderType.Attack; // transient - Assert.Equal(OrderType.Follow, pet.AIObject.PersistentOrder); - - pet.ControlOrder = OrderType.Stop; - - Assert.Equal(OrderType.Follow, pet.ControlOrder); // resumed standing order - Assert.Equal(OrderType.Follow, pet.AIObject.PersistentOrder); - } - - [Fact] - public void Stop_WhileFollowing_CancelsToIdleNone() - { - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1002, 1000, 0)); - pet.ControlOrder = OrderType.Follow; - - pet.ControlOrder = OrderType.Stop; - - Assert.Equal(OrderType.None, pet.ControlOrder); - Assert.Equal(OrderType.None, pet.AIObject.PersistentOrder); - Assert.Equal(pet.Location, pet.Home); // idle anchor = where stopped - } - - [Fact] - public void Stop_WhileStaying_RemainsStayingAtOriginalPost() - { - var post = new Point3D(1005, 1005, 0); - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), post); - pet.ControlOrder = OrderType.Stay; // Home = post - Assert.Equal(post, pet.Home); - - pet.ControlOrder = OrderType.Stop; - - Assert.Equal(OrderType.Stay, pet.ControlOrder); - Assert.Equal(OrderType.Stay, pet.AIObject.PersistentOrder); - Assert.Equal(post, pet.Home); // post unchanged - } - - [Fact] - public void Stay_ThenFollow_ThenStop_DoesNotReturnToOldStayAnchor() // report 2 - { - var postA = new Point3D(1005, 1005, 0); - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), postA); - pet.ControlOrder = OrderType.Stay; // Home = A - pet.ControlOrder = OrderType.Follow; // Home cleared to Zero - pet.MoveToWorld(new Point3D(1050, 1050, 0), pet.Map); // walked to B - pet.ControlOrder = OrderType.Stop; // stop while following - - Assert.NotEqual(postA, pet.Home); // never re-acquires A - Assert.Equal(pet.Location, pet.Home); // idles at B - } - - [Fact] - public void AttackTargetLost_ResumesPersistentFollow() - { - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1002, 1000, 0)); - pet.ControlOrder = OrderType.Follow; // persistent = Follow - pet.ControlOrder = OrderType.Attack; - pet.ControlTarget = null; // target gone - - pet.AIObject.DoOrderAttack(); // invalid-target path - - Assert.Equal(OrderType.Follow, pet.ControlOrder); - } - - [Fact] - public void StayPet_AttacksThenTargetLost_ReturnsToOriginalPost() // report 1 - { - var post = new Point3D(1005, 1005, 0); - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), post); - pet.ControlOrder = OrderType.Stay; // persistent = Stay, Home = post - pet.ControlOrder = OrderType.Attack; - pet.MoveToWorld(new Point3D(1060, 1060, 0), pet.Map); // chased far to the "corpse" - pet.ControlTarget = null; - - pet.AIObject.DoOrderAttack(); - - Assert.Equal(OrderType.Stay, pet.ControlOrder); - Assert.Equal(post, pet.Home); // anchor still the original post, not the corpse - } - - // NOTE: the test fixture does not load tile data, so Mobile.Move is blocked and Location - // never changes here. DoMoveImpl still sets Mobile.Direction before the (blocked) move, - // so an *attempted* wander is observable via Direction. The subjective wander cadence is - // covered by manual QA; these tests verify the gate/frozen wiring deterministically. - [Fact] - public void IdlePet_DoesNotAttemptToMove_WhileResting() - { - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1002, 1000, 0)); - pet.ControlOrder = OrderType.Follow; - pet.ControlOrder = OrderType.Stop; // -> idle None - Assert.Equal(OrderType.None, pet.ControlOrder); - - pet.ForceIdle = true; // CheckIdle() reports resting -> idle wander must be skipped - pet.Direction = Direction.North; - for (var i = 0; i < 40; i++) - { - pet.AIObject.DoOrderNone(); - } - - Assert.Equal(Direction.North, pet.Direction); // gated by CheckIdle -> never attempts a step - } - - [Fact] - public void StayingPet_DoesNotAttemptToMove() - { - var post = new Point3D(1005, 1005, 0); - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), post); - pet.ControlOrder = OrderType.Stay; - pet.Direction = Direction.North; - - for (var i = 0; i < 40; i++) - { - pet.AIObject.DoOrderStay(); - } - - Assert.Equal(Direction.North, pet.Direction); // frozen -> no wander attempts - } - - [Fact] - public void Release_WithoutSpawner_AnchorsHomeToCurrentLocation() - { - var loc = new Point3D(1010, 1010, 0); - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), loc); - pet.ControlOrder = OrderType.Stay; // sets Home to loc - pet.Home = new Point3D(800, 800, 0); // simulate a stale anchor - pet.Spawner = null; - - pet.AIObject.DoOrderRelease(); - - Assert.Equal(loc, pet.Home); // released where it stands, not the stale point - } - - [Fact] - public void Login_NearMaster_DerivesFollow() - { - var (master, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1001, 1000, 0)); - Assert.Equal(OrderType.None, pet.AIObject.PersistentOrder); - - PetLoginHandler.DeriveFollowerOrders(master); - - Assert.Equal(OrderType.Follow, pet.AIObject.PersistentOrder); - } - - [Fact] - public void Login_FarFromMaster_DerivesStay() - { - var (master, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1040, 1000, 0)); - - PetLoginHandler.DeriveFollowerOrders(master); - - Assert.Equal(OrderType.Stay, pet.AIObject.PersistentOrder); - } - - [Fact] - public void Stop_WhileFollowing_CancelsToIdle_NonML() - { - var previous = Core.Expansion; - try - { - Core.Expansion = Expansion.SE; // pre-ML: Core.ML is false - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1002, 1000, 0)); - pet.ControlOrder = OrderType.Follow; - - pet.ControlOrder = OrderType.Stop; - - // Stop/idle resolution is era-independent. - Assert.Equal(OrderType.None, pet.ControlOrder); - Assert.Equal(OrderType.None, pet.AIObject.PersistentOrder); - Assert.Equal(pet.Location, pet.Home); - } - finally - { - Core.Expansion = previous; - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/PetTestStub.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/PetTestStub.cs deleted file mode 100644 index a759cc024..000000000 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/PetTestStub.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Server; -using Server.Mobiles; - -namespace UOContent.Tests.Mobiles.AI; - -// Minimal tameable creature for AI-state tests. Uses the AIType constructor so the -// creature gets a real AIObject (the Serial ctor does not initialize AI). -public class PetTestStub : BaseCreature -{ - // When true, CheckIdle() reports "resting" so the idle-wander path must not move. - public bool ForceIdle { get; set; } - - public PetTestStub() : base(AIType.AI_Animal, FightMode.Closest, 10, 1) - { - Body = 0xC8; // dog - } - - // NPCSpeeds isn't configured in the test fixture; provide fixed speeds so the - // AIType constructor doesn't hit the unconfigured speed table. - public override void GetSpeeds(out double activeSpeed, out double passiveSpeed) - { - activeSpeed = 0.2; - passiveSpeed = 0.4; - } - - public override bool CheckIdle() => ForceIdle || base.CheckIdle(); - - public PetTestStub(Serial serial) : base(serial) - { - } -} - -public static class PetTestSetup -{ - // Places a player master and a controlled pet on Felucca and returns both. - public static (PlayerMobile master, PetTestStub pet) SpawnControlledPet( - Point3D masterLoc, Point3D petLoc) - { - var map = Map.Felucca; - - var master = new PlayerMobile(World.NewMobile); - master.DefaultMobileInit(); - master.MoveToWorld(masterLoc, map); - - var pet = new PetTestStub(); - pet.MoveToWorld(petLoc, map); - pet.SetControlMaster(master); // sets Controlled, Home=Zero, ControlOrder=Come - - return (master, pet); - } -} diff --git a/Projects/UOContent.Tests/Tests/Multis/Boats/BoatPacketTests.cs b/Projects/UOContent.Tests/Tests/Multis/Boats/BoatPacketTests.cs index 4f5e567b3..01fdddd77 100644 --- a/Projects/UOContent.Tests/Tests/Multis/Boats/BoatPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Multis/Boats/BoatPacketTests.cs @@ -11,8 +11,7 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] -public class BoatPacketTests +public class BoatPacketTests : IClassFixture { [Theory] [InlineData(Direction.West, 10, 100, 200)] @@ -53,13 +52,13 @@ public class BoatPacketTests beholder.CanSeeEntities.Add(boat); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.ProtocolChanges = ProtocolChanges.HighSeas; var expected = new MoveBoatHS(beholder, boat, d, speed, list, xOffset, yOffset).Compile(); ns.SendMoveBoatHS(boat, list, d, speed, xOffset, yOffset); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -96,14 +95,14 @@ public class BoatPacketTests beholder.CanSeeEntities.Add(boat); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.ProtocolChanges = ProtocolChanges.HighSeas; var expected = new DisplayBoatHS(beholder, boat).Compile(); ns.SendDisplayBoatHS(beholder, boat); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } diff --git a/Projects/UOContent.Tests/Tests/Multis/Boats/Packets.cs b/Projects/UOContent.Tests/Tests/Multis/Boats/Packets.cs index 2f8baf560..1dcd9adf4 100644 --- a/Projects/UOContent.Tests/Tests/Multis/Boats/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Multis/Boats/Packets.cs @@ -15,117 +15,117 @@ public sealed class MoveBoatHS : Packet int yOffset ) : base(0xF6) { - EnsureCapacity(3 + 15 + ents.Count * 10); + EnsureCapacity(3 + 15 + ents.Count * 10); - Stream.Write(boat.Serial); - Stream.Write((byte)speed); - Stream.Write((byte)d); - Stream.Write((byte)boat.Facing); - Stream.Write((short)(boat.X + xOffset)); - Stream.Write((short)(boat.Y + yOffset)); - Stream.Write((short)boat.Z); - Stream.Write((short)0); // count placeholder + Stream.Write(boat.Serial); + Stream.Write((byte)speed); + Stream.Write((byte)d); + Stream.Write((byte)boat.Facing); + Stream.Write((short)(boat.X + xOffset)); + Stream.Write((short)(boat.Y + yOffset)); + Stream.Write((short)boat.Z); + Stream.Write((short)0); // count placeholder - var count = 0; + var count = 0; - foreach (var ent in ents) - { - Stream.Write(ent.Serial); - Stream.Write((short)(ent.X + xOffset)); - Stream.Write((short)(ent.Y + yOffset)); - Stream.Write((short)ent.Z); - ++count; + foreach (var ent in ents) + { + Stream.Write(ent.Serial); + Stream.Write((short)(ent.X + xOffset)); + Stream.Write((short)(ent.Y + yOffset)); + Stream.Write((short)ent.Z); + ++count; + } + + Stream.Seek(16, SeekOrigin.Begin); + Stream.Write((short)count); } - - Stream.Seek(16, SeekOrigin.Begin); - Stream.Write((short)count); - } } public sealed class DisplayBoatHS : Packet { public DisplayBoatHS(Mobile beholder, BaseBoat boat) : base(0xF7) { - var ents = boat.GetMovingEntities(true); + var ents = boat.GetMovingEntities(true); - EnsureCapacity(3 + 2 + 5 * 26); + EnsureCapacity(3 + 2 + 5 * 26); - Stream.Write((short)0); // count placeholder + Stream.Write((short)0); // count placeholder - var count = 0; + var count = 0; - foreach (var ent in ents) - { - if (!beholder.CanSee(ent)) + foreach (var ent in ents) { - continue; + if (!beholder.CanSee(ent)) + { + continue; + } + + // Embedded WorldItemHS packets + Stream.Write((byte)0xF3); + Stream.Write((short)0x1); + + if (ent is BaseMulti bm) + { + Stream.Write((byte)0x02); + Stream.Write(bm.Serial); + // TODO: Mask no longer needed, merge with Item case? + Stream.Write((ushort)(bm.ItemID & 0x3FFF)); + Stream.Write((byte)0); + + Stream.Write((short)bm.Amount); + Stream.Write((short)bm.Amount); + + Stream.Write((short)(bm.X & 0x7FFF)); + Stream.Write((short)(bm.Y & 0x3FFF)); + Stream.Write((sbyte)bm.Z); + + Stream.Write((byte)bm.Light); + Stream.Write((short)bm.Hue); + Stream.Write((byte)bm.GetPacketFlags()); + } + else if (ent is Mobile m) + { + Stream.Write((byte)0x01); + Stream.Write(m.Serial); + Stream.Write((short)m.Body); + Stream.Write((byte)0); + + Stream.Write((short)1); + Stream.Write((short)1); + + Stream.Write((short)(m.X & 0x7FFF)); + Stream.Write((short)(m.Y & 0x3FFF)); + Stream.Write((sbyte)m.Z); + + Stream.Write((byte)m.Direction); + Stream.Write((short)m.Hue); + Stream.Write((byte)m.GetPacketFlags(true)); + } + else if (ent is Item item) + { + Stream.Write((byte)0x00); + Stream.Write(item.Serial); + Stream.Write((ushort)(item.ItemID & 0xFFFF)); + Stream.Write((byte)0); + + Stream.Write((short)item.Amount); + Stream.Write((short)item.Amount); + + Stream.Write((short)(item.X & 0x7FFF)); + Stream.Write((short)(item.Y & 0x3FFF)); + Stream.Write((sbyte)item.Z); + + Stream.Write((byte)item.Light); + Stream.Write((short)item.Hue); + Stream.Write((byte)item.GetPacketFlags()); + } + + Stream.Write((short)0x00); + ++count; } - // Embedded WorldItemHS packets - Stream.Write((byte)0xF3); - Stream.Write((short)0x1); - - if (ent is BaseMulti bm) - { - Stream.Write((byte)0x02); - Stream.Write(bm.Serial); - // TODO: Mask no longer needed, merge with Item case? - Stream.Write((ushort)(bm.ItemID & 0x3FFF)); - Stream.Write((byte)0); - - Stream.Write((short)bm.Amount); - Stream.Write((short)bm.Amount); - - Stream.Write((short)(bm.X & 0x7FFF)); - Stream.Write((short)(bm.Y & 0x3FFF)); - Stream.Write((sbyte)bm.Z); - - Stream.Write((byte)bm.Light); - Stream.Write((short)bm.Hue); - Stream.Write((byte)bm.GetPacketFlags()); - } - else if (ent is Mobile m) - { - Stream.Write((byte)0x01); - Stream.Write(m.Serial); - Stream.Write((short)m.Body); - Stream.Write((byte)0); - - Stream.Write((short)1); - Stream.Write((short)1); - - Stream.Write((short)(m.X & 0x7FFF)); - Stream.Write((short)(m.Y & 0x3FFF)); - Stream.Write((sbyte)m.Z); - - Stream.Write((byte)m.Direction); - Stream.Write((short)m.Hue); - Stream.Write((byte)m.GetPacketFlags(true)); - } - else if (ent is Item item) - { - Stream.Write((byte)0x00); - Stream.Write(item.Serial); - Stream.Write((ushort)(item.ItemID & 0xFFFF)); - Stream.Write((byte)0); - - Stream.Write((short)item.Amount); - Stream.Write((short)item.Amount); - - Stream.Write((short)(item.X & 0x7FFF)); - Stream.Write((short)(item.Y & 0x3FFF)); - Stream.Write((sbyte)item.Z); - - Stream.Write((byte)item.Light); - Stream.Write((short)item.Hue); - Stream.Write((byte)item.GetPacketFlags()); - } - - Stream.Write((short)0x00); - ++count; + Stream.Seek(3, SeekOrigin.Begin); + Stream.Write((short)count); } - - Stream.Seek(3, SeekOrigin.Begin); - Stream.Write((short)count); - } -} +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Multis/Houses/HousePacketTests.cs b/Projects/UOContent.Tests/Tests/Multis/Houses/HousePacketTests.cs index 912570c11..66d36bbd8 100644 --- a/Projects/UOContent.Tests/Tests/Multis/Houses/HousePacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Multis/Houses/HousePacketTests.cs @@ -8,73 +8,72 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] public class HousePacketTests { [Theory] [InlineData(0x1001u)] public void TestBeginHouseCustomization(uint serial) { - var expected = new BeginHouseCustomization((Serial)serial).Compile(); + var expected = new BeginHouseCustomization((Serial)serial).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendBeginHouseCustomization((Serial)serial); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendBeginHouseCustomization((Serial)serial); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(0x1001u)] public void TestEndHouseCustomization(uint serial) { - var expected = new EndHouseCustomization((Serial)serial).Compile(); + var expected = new EndHouseCustomization((Serial)serial).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendEndHouseCustomization((Serial)serial); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendEndHouseCustomization((Serial)serial); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(0x1001u, 0)] [InlineData(0x1001u, 100)] public void TestDesignStateGeneral(uint serial, int revision) { - var expected = new DesignStateGeneral((Serial)serial, revision).Compile(); + var expected = new DesignStateGeneral((Serial)serial, revision).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendDesignStateGeneral((Serial)serial, revision); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendDesignStateGeneral((Serial)serial, revision); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Fact] public void TestHouseDesignStateDetailed() { - var serial = (Serial)0x40000001; - var revision = 10; - var tiles = new MultiTileEntry[250]; - for (var i = 0; i < tiles.Length; i++) - { - tiles[i] = new MultiTileEntry( - (ushort)i, - (byte)i, - (byte)i, - (byte)(i / 50), - TileFlag.None - ); + Serial serial = (Serial)0x40000001; + var revision = 10; + var tiles = new MultiTileEntry[250]; + for (var i = 0; i < tiles.Length; i++) + { + tiles[i] = new MultiTileEntry( + (ushort)i, + (byte)i, + (byte)i, + (byte)(i / 50), + TileFlag.None + ); + } + var mcl = new MultiComponentList(tiles.ToList()); + + var expected = new DesignStateDetailed( + serial, revision, mcl.Min.X, mcl.Min.Y, mcl.Max.X, mcl.Max.Y, tiles + ).Compile(); + + var actual = HousePackets.CreateHouseDesignStateDetailed(serial, revision, mcl); + + AssertThat.Equal(actual, expected); } - var mcl = new MultiComponentList(tiles.ToList()); - - var expected = new DesignStateDetailed( - serial, revision, mcl.Min.X, mcl.Min.Y, mcl.Max.X, mcl.Max.Y, tiles - ).Compile(); - - var actual = HousePackets.CreateHouseDesignStateDetailed(serial, revision, mcl); - - AssertThat.Equal(actual, expected); - } -} +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Multis/Houses/HousePackets.cs b/Projects/UOContent.Tests/Tests/Multis/Houses/HousePackets.cs index b981d9f86..2290178a7 100644 --- a/Projects/UOContent.Tests/Tests/Multis/Houses/HousePackets.cs +++ b/Projects/UOContent.Tests/Tests/Multis/Houses/HousePackets.cs @@ -3,319 +3,320 @@ using System.Buffers; using System.IO; using Server.Compression; -namespace Server.Network; - -public class BeginHouseCustomization : Packet +namespace Server.Network { - public BeginHouseCustomization(Serial house) : base(0xBF) + public class BeginHouseCustomization : Packet { - EnsureCapacity(17); - - Stream.Write((short)0x20); - Stream.Write(house); - Stream.Write((byte)0x04); - Stream.Write((ushort)0x0000); - Stream.Write((ushort)0xFFFF); - Stream.Write((ushort)0xFFFF); - Stream.Write((byte)0xFF); - } -} - -public class EndHouseCustomization : Packet -{ - public EndHouseCustomization(Serial house) : base(0xBF) - { - EnsureCapacity(17); - - Stream.Write((short)0x20); - Stream.Write(house); - Stream.Write((byte)0x05); - Stream.Write((ushort)0x0000); - Stream.Write((ushort)0xFFFF); - Stream.Write((ushort)0xFFFF); - Stream.Write((byte)0xFF); - } -} - -public sealed class DesignStateGeneral : Packet -{ - public DesignStateGeneral(Serial house, int revision) : base(0xBF) - { - EnsureCapacity(13); - - Stream.Write((short)0x1D); - Stream.Write(house); - Stream.Write(revision); - } -} - -public sealed class DesignStateDetailed : Packet -{ - public const int MaxItemsPerStairBuffer = 750; - - private readonly bool[] m_PlaneUsed = new bool[9]; - private readonly byte[] m_PrimBuffer = new byte[4]; - - public DesignStateDetailed(Serial serial, int revision, int xMin, int yMin, int xMax, int yMax, MultiTileEntry[] tiles) - : base(0xD8) - { - EnsureCapacity(17 + tiles.Length * 5); - - Write((byte)0x03); // Compression Type - Write((byte)0x00); // Unknown - Write(serial.Value); - Write(revision); - Write((short)tiles.Length); - Write((short)0); // Buffer length : reserved - Write((byte)0); // Plane count : reserved - - var totalLength = 1; // includes plane count - - var width = xMax - xMin + 1; - var height = yMax - yMin + 1; - - var planeBuffers = new byte[9][]; - - for (var i = 0; i < planeBuffers.Length; ++i) + public BeginHouseCustomization(Serial house) : base(0xBF) { - planeBuffers[i] = ArrayPool.Shared.Rent(0x400); + EnsureCapacity(17); + + Stream.Write((short)0x20); + Stream.Write(house); + Stream.Write((byte)0x04); + Stream.Write((ushort)0x0000); + Stream.Write((ushort)0xFFFF); + Stream.Write((ushort)0xFFFF); + Stream.Write((byte)0xFF); + } + } + + public class EndHouseCustomization : Packet + { + public EndHouseCustomization(Serial house) : base(0xBF) + { + EnsureCapacity(17); + + Stream.Write((short)0x20); + Stream.Write(house); + Stream.Write((byte)0x05); + Stream.Write((ushort)0x0000); + Stream.Write((ushort)0xFFFF); + Stream.Write((ushort)0xFFFF); + Stream.Write((byte)0xFF); + } + } + + public sealed class DesignStateGeneral : Packet + { + public DesignStateGeneral(Serial house, int revision) : base(0xBF) + { + EnsureCapacity(13); + + Stream.Write((short)0x1D); + Stream.Write(house); + Stream.Write(revision); + } + } + + public sealed class DesignStateDetailed : Packet + { + public const int MaxItemsPerStairBuffer = 750; + + private readonly bool[] m_PlaneUsed = new bool[9]; + private readonly byte[] m_PrimBuffer = new byte[4]; + + public DesignStateDetailed(Serial serial, int revision, int xMin, int yMin, int xMax, int yMax, MultiTileEntry[] tiles) + : base(0xD8) + { + EnsureCapacity(17 + tiles.Length * 5); + + Write((byte)0x03); // Compression Type + Write((byte)0x00); // Unknown + Write(serial.Value); + Write(revision); + Write((short)tiles.Length); + Write((short)0); // Buffer length : reserved + Write((byte)0); // Plane count : reserved + + var totalLength = 1; // includes plane count + + var width = xMax - xMin + 1; + var height = yMax - yMin + 1; + + var planeBuffers = new byte[9][]; + + for (var i = 0; i < planeBuffers.Length; ++i) + { + planeBuffers[i] = ArrayPool.Shared.Rent(0x400); + } + + var stairBuffers = new byte[6][]; + + for (var i = 0; i < stairBuffers.Length; ++i) + { + stairBuffers[i] = ArrayPool.Shared.Rent(MaxItemsPerStairBuffer * 5); + } + + Clear(planeBuffers[0], width * height * 2); + + for (var i = 0; i < 4; ++i) + { + Clear(planeBuffers[1 + i], (width - 1) * (height - 2) * 2); + Clear(planeBuffers[5 + i], width * (height - 1) * 2); + } + + var totalStairsUsed = 0; + + for (var i = 0; i < tiles.Length; ++i) + { + var mte = tiles[i]; + var x = mte.OffsetX - xMin; + var y = mte.OffsetY - yMin; + int z = mte.OffsetZ; + var floor = TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Height <= 0; + int plane, size; + + switch (z) + { + case 0: + plane = 0; + break; + case 7: + plane = 1; + break; + case 27: + plane = 2; + break; + case 47: + plane = 3; + break; + case 67: + plane = 4; + break; + default: + { + var stairBufferIndex = totalStairsUsed / MaxItemsPerStairBuffer; + var stairBuffer = stairBuffers[stairBufferIndex]; + + var byteIndex = totalStairsUsed % MaxItemsPerStairBuffer * 5; + + stairBuffer[byteIndex++] = (byte)(mte.ItemId >> 8); + stairBuffer[byteIndex++] = (byte)mte.ItemId; + + stairBuffer[byteIndex++] = (byte)mte.OffsetX; + stairBuffer[byteIndex++] = (byte)mte.OffsetY; + stairBuffer[byteIndex] = (byte)mte.OffsetZ; + + ++totalStairsUsed; + + continue; + } + } + + if (plane == 0) + { + size = height; + } + else if (floor) + { + size = height - 2; + x -= 1; + y -= 1; + } + else + { + size = height - 1; + plane += 4; + } + + var index = (x * size + y) * 2; + + if (x < 0 || y < 0 || y >= size || index + 1 >= 0x400) + { + var stairBufferIndex = totalStairsUsed / MaxItemsPerStairBuffer; + var stairBuffer = stairBuffers[stairBufferIndex]; + + var byteIndex = totalStairsUsed % MaxItemsPerStairBuffer * 5; + + stairBuffer[byteIndex++] = (byte)(mte.ItemId >> 8); + stairBuffer[byteIndex++] = (byte)mte.ItemId; + + stairBuffer[byteIndex++] = (byte)mte.OffsetX; + stairBuffer[byteIndex++] = (byte)mte.OffsetY; + stairBuffer[byteIndex] = (byte)mte.OffsetZ; + + ++totalStairsUsed; + } + else + { + m_PlaneUsed[plane] = true; + planeBuffers[plane][index] = (byte)(mte.ItemId >> 8); + planeBuffers[plane][index + 1] = (byte)mte.ItemId; + } + } + + var planeCount = 0; + + var deflatedBuffer = ArrayPool.Shared.Rent(0x2000); + + for (var i = 0; i < planeBuffers.Length; ++i) + { + if (!m_PlaneUsed[i]) + { + ArrayPool.Shared.Return(planeBuffers[i]); + continue; + } + + ++planeCount; + + int size = i switch + { + 0 => width * height * 2, + < 5 => (width - 1) * (height - 2) * 2, + _ => width * (height - 1) * 2 + }; + + var inflatedBuffer = planeBuffers[i]; + + var deflatedLength = Deflate.Standard.Pack( + deflatedBuffer, + inflatedBuffer.AsSpan(0, size) + ); + + if (deflatedLength == 0) + { + Console.WriteLine("Compression error"); + } + + Write((byte)(0x20 | i)); + Write((byte)size); + Write((byte)deflatedLength); + Write((byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF))); + Write(deflatedBuffer, 0, deflatedLength); + + totalLength += 4 + deflatedLength; + ArrayPool.Shared.Return(inflatedBuffer); + } + + var totalStairBuffersUsed = (totalStairsUsed + (MaxItemsPerStairBuffer - 1)) / MaxItemsPerStairBuffer; + + for (var i = 0; i < totalStairBuffersUsed; ++i) + { + ++planeCount; + + var count = Math.Min(MaxItemsPerStairBuffer, totalStairsUsed - i * MaxItemsPerStairBuffer); + + var size = count * 5; + + var inflatedBuffer = stairBuffers[i]; + + var deflatedLength = Deflate.Standard.Pack( + deflatedBuffer, + inflatedBuffer.AsSpan(0, size) + ); + + if (deflatedLength == 0) + { + Console.WriteLine("Compression error"); + } + + Write((byte)(9 + i)); + Write((byte)size); + Write((byte)deflatedLength); + Write((byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF))); + Write(deflatedBuffer, 0, deflatedLength); + + totalLength += 4 + deflatedLength; + } + + for (var i = 0; i < stairBuffers.Length; ++i) + { + ArrayPool.Shared.Return(stairBuffers[i]); + } + + ArrayPool.Shared.Return(deflatedBuffer); + + Stream.Seek(15, SeekOrigin.Begin); + + Write((short)totalLength); // Buffer length + Write((byte)planeCount); // Plane count } - var stairBuffers = new byte[6][]; - - for (var i = 0; i < stairBuffers.Length; ++i) + public void Write(int value) { - stairBuffers[i] = ArrayPool.Shared.Rent(MaxItemsPerStairBuffer * 5); + m_PrimBuffer[0] = (byte)(value >> 24); + m_PrimBuffer[1] = (byte)(value >> 16); + m_PrimBuffer[2] = (byte)(value >> 8); + m_PrimBuffer[3] = (byte)value; + + Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 4); } - Clear(planeBuffers[0], width * height * 2); - - for (var i = 0; i < 4; ++i) + public void Write(uint value) { - Clear(planeBuffers[1 + i], (width - 1) * (height - 2) * 2); - Clear(planeBuffers[5 + i], width * (height - 1) * 2); + m_PrimBuffer[0] = (byte)(value >> 24); + m_PrimBuffer[1] = (byte)(value >> 16); + m_PrimBuffer[2] = (byte)(value >> 8); + m_PrimBuffer[3] = (byte)value; + + Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 4); } - var totalStairsUsed = 0; - - for (var i = 0; i < tiles.Length; ++i) + public void Write(short value) { - var mte = tiles[i]; - var x = mte.OffsetX - xMin; - var y = mte.OffsetY - yMin; - int z = mte.OffsetZ; - var floor = TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Height <= 0; - int plane, size; + m_PrimBuffer[0] = (byte)(value >> 8); + m_PrimBuffer[1] = (byte)value; - switch (z) - { - case 0: - plane = 0; - break; - case 7: - plane = 1; - break; - case 27: - plane = 2; - break; - case 47: - plane = 3; - break; - case 67: - plane = 4; - break; - default: - { - var stairBufferIndex = totalStairsUsed / MaxItemsPerStairBuffer; - var stairBuffer = stairBuffers[stairBufferIndex]; - - var byteIndex = totalStairsUsed % MaxItemsPerStairBuffer * 5; - - stairBuffer[byteIndex++] = (byte)(mte.ItemId >> 8); - stairBuffer[byteIndex++] = (byte)mte.ItemId; - - stairBuffer[byteIndex++] = (byte)mte.OffsetX; - stairBuffer[byteIndex++] = (byte)mte.OffsetY; - stairBuffer[byteIndex] = (byte)mte.OffsetZ; - - ++totalStairsUsed; - - continue; - } - } - - if (plane == 0) - { - size = height; - } - else if (floor) - { - size = height - 2; - x -= 1; - y -= 1; - } - else - { - size = height - 1; - plane += 4; - } - - var index = (x * size + y) * 2; - - if (x < 0 || y < 0 || y >= size || index + 1 >= 0x400) - { - var stairBufferIndex = totalStairsUsed / MaxItemsPerStairBuffer; - var stairBuffer = stairBuffers[stairBufferIndex]; - - var byteIndex = totalStairsUsed % MaxItemsPerStairBuffer * 5; - - stairBuffer[byteIndex++] = (byte)(mte.ItemId >> 8); - stairBuffer[byteIndex++] = (byte)mte.ItemId; - - stairBuffer[byteIndex++] = (byte)mte.OffsetX; - stairBuffer[byteIndex++] = (byte)mte.OffsetY; - stairBuffer[byteIndex] = (byte)mte.OffsetZ; - - ++totalStairsUsed; - } - else - { - m_PlaneUsed[plane] = true; - planeBuffers[plane][index] = (byte)(mte.ItemId >> 8); - planeBuffers[plane][index + 1] = (byte)mte.ItemId; - } + Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 2); } - var planeCount = 0; - - var deflatedBuffer = ArrayPool.Shared.Rent(0x2000); - - for (var i = 0; i < planeBuffers.Length; ++i) + public void Write(byte value) { - if (!m_PlaneUsed[i]) - { - ArrayPool.Shared.Return(planeBuffers[i]); - continue; - } - - ++planeCount; - - var size = i switch - { - 0 => width * height * 2, - < 5 => (width - 1) * (height - 2) * 2, - _ => width * (height - 1) * 2 - }; - - var inflatedBuffer = planeBuffers[i]; - - var deflatedLength = Deflate.Standard.Pack( - deflatedBuffer, - inflatedBuffer.AsSpan(0, size) - ); - - if (deflatedLength == 0) - { - Console.WriteLine("Compression error"); - } - - Write((byte)(0x20 | i)); - Write((byte)size); - Write((byte)deflatedLength); - Write((byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF))); - Write(deflatedBuffer, 0, deflatedLength); - - totalLength += 4 + deflatedLength; - ArrayPool.Shared.Return(inflatedBuffer); + Stream.UnderlyingStream.WriteByte(value); } - var totalStairBuffersUsed = (totalStairsUsed + (MaxItemsPerStairBuffer - 1)) / MaxItemsPerStairBuffer; - - for (var i = 0; i < totalStairBuffersUsed; ++i) + public void Write(byte[] buffer, int offset, int size) { - ++planeCount; + Stream.UnderlyingStream.Write(buffer, offset, size); + } - var count = Math.Min(MaxItemsPerStairBuffer, totalStairsUsed - i * MaxItemsPerStairBuffer); - - var size = count * 5; - - var inflatedBuffer = stairBuffers[i]; - - var deflatedLength = Deflate.Standard.Pack( - deflatedBuffer, - inflatedBuffer.AsSpan(0, size) - ); - - if (deflatedLength == 0) + public static void Clear(byte[] buffer, int size) + { + for (var i = 0; i < size; ++i) { - Console.WriteLine("Compression error"); + buffer[i] = 0; } - - Write((byte)(9 + i)); - Write((byte)size); - Write((byte)deflatedLength); - Write((byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF))); - Write(deflatedBuffer, 0, deflatedLength); - - totalLength += 4 + deflatedLength; - } - - for (var i = 0; i < stairBuffers.Length; ++i) - { - ArrayPool.Shared.Return(stairBuffers[i]); - } - - ArrayPool.Shared.Return(deflatedBuffer); - - Stream.Seek(15, SeekOrigin.Begin); - - Write((short)totalLength); // Buffer length - Write((byte)planeCount); // Plane count - } - - public void Write(int value) - { - m_PrimBuffer[0] = (byte)(value >> 24); - m_PrimBuffer[1] = (byte)(value >> 16); - m_PrimBuffer[2] = (byte)(value >> 8); - m_PrimBuffer[3] = (byte)value; - - Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 4); - } - - public void Write(uint value) - { - m_PrimBuffer[0] = (byte)(value >> 24); - m_PrimBuffer[1] = (byte)(value >> 16); - m_PrimBuffer[2] = (byte)(value >> 8); - m_PrimBuffer[3] = (byte)value; - - Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 4); - } - - public void Write(short value) - { - m_PrimBuffer[0] = (byte)(value >> 8); - m_PrimBuffer[1] = (byte)value; - - Stream.UnderlyingStream.Write(m_PrimBuffer, 0, 2); - } - - public void Write(byte value) - { - Stream.UnderlyingStream.WriteByte(value); - } - - public void Write(byte[] buffer, int offset, int size) - { - Stream.UnderlyingStream.Write(buffer, offset, size); - } - - public static void Clear(byte[] buffer, int size) - { - for (var i = 0; i < size; ++i) - { - buffer[i] = 0; } } } diff --git a/Projects/UOContent.Tests/Tests/Multis/SpreadsheetTests.cs b/Projects/UOContent.Tests/Tests/Multis/SpreadsheetTests.cs deleted file mode 100644 index 3be9d9a0d..000000000 --- a/Projects/UOContent.Tests/Tests/Multis/SpreadsheetTests.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.IO; -using Server.Multis; -using Xunit; - -namespace UOContent.Tests; - -public class SpreadsheetTests : IDisposable -{ - // misc.txt's shape: 13 columns, the last being the Comment the client never reads. - private const string Types = "int\tint\tint\tint\tint\tint\tint\tint\tint\tint\tint\tint\tstring"; - - private const string Names = - "Category\tStyle\tTID\tPiece1\tPiece2\tPiece3\tPiece4\tPiece5\tPiece6\tPiece7\tPiece8\tFeatureMask\tComment"; - - private readonly string _path = Path.Combine(Path.GetTempPath(), $"muo-sheet-{Guid.NewGuid():N}.txt"); - - public void Dispose() - { - if (File.Exists(_path)) - { - File.Delete(_path); - } - } - - private Spreadsheet Write(params string[] rows) - { - var lines = new string[rows.Length + 2]; - lines[0] = Types; - lines[1] = Names; - rows.CopyTo(lines, 2); - - File.WriteAllLines(_path, lines); - return new Spreadsheet(_path); - } - - [Fact] - public void RowMissingTrailingCommentIsStillRead() - { - // An empty Comment written without a trailing tab leaves the row one field short. - var ss = Write("0\t0\t1060056\t44\t0\t41\t40\t42\t0\t43\t29\t8"); - - var record = Assert.Single(ss.Records); - - Assert.Equal(44, record.GetInt32(ss.GetColumnID("Piece1"))); - Assert.Equal(29, record.GetInt32(ss.GetColumnID("Piece8"))); - Assert.Equal(8, record.GetInt32(ss.GetColumnID("FeatureMask"))); - } - - [Fact] - public void ShortRowDoesNotDropLaterRows() - { - var ss = Write( - "0\t0\t1060056\t44\t0\t41\t40\t42\t0\t43\t29\t0", - "0\t1\t1060057\t45\t0\t0\t0\t0\t0\t0\t0\t0\tFieldstone Arches" - ); - - Assert.Equal(2, ss.Records.Length); - Assert.Equal(44, ss.Records[0].GetInt32(ss.GetColumnID("Piece1"))); - Assert.Equal(45, ss.Records[1].GetInt32(ss.GetColumnID("Piece1"))); - } - - [Fact] - public void TabOnlySeparatorLinesAreNotMistakenForHeaderRows() - { - // The retail client's doors.txt separates its header rows this way. - var separator = new string('\t', 12); - - File.WriteAllLines( - _path, - [ - Types, - separator, - Names, - separator, - "0\t0\t1060056\t44\t0\t41\t40\t42\t0\t43\t29\t0\tFieldstone Archways" - ] - ); - - var ss = new Spreadsheet(_path); - - Assert.Equal(3, ss.GetColumnID("Piece1")); - Assert.Equal(11, ss.GetColumnID("FeatureMask")); - - var record = Assert.Single(ss.Records); - Assert.Equal(44, record.GetInt32(ss.GetColumnID("Piece1"))); - } - - [Fact] - public void MissingHeaderRowsThrowsInsteadOfNullReference() - { - File.WriteAllLines(_path, [Types]); - - Assert.Throws(() => new Spreadsheet(_path)); - } - - [Fact] - public void HeaderWithFewerNamesThanTypesIsTolerated() - { - File.WriteAllLines( - _path, - [ - Types, - "Category\tStyle\tTID\tPiece1", - "0\t0\t1060056\t44\t0\t41\t40\t42\t0\t43\t29\t0\tFieldstone Archways" - ] - ); - - var ss = new Spreadsheet(_path); - - Assert.Equal(44, ss.Records[0].GetInt32(ss.GetColumnID("Piece1"))); - Assert.Equal(-1, ss.GetColumnID("FeatureMask")); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/AutoDenylist/AutoDenylistTests.cs b/Projects/UOContent.Tests/Tests/Network/AutoDenylist/AutoDenylistTests.cs deleted file mode 100644 index ae926794d..000000000 --- a/Projects/UOContent.Tests/Tests/Network/AutoDenylist/AutoDenylistTests.cs +++ /dev/null @@ -1,206 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: AutoDenylistTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Net; -using Server.Network; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests.Network.AutoDenylists; - -// Static store, so every test resets it first and none may run alongside another. -// Addresses come from TEST-NET-2 (198.51.100.0/24). -[Collection("Sequential UOContent Tests")] -public class AutoDenylistTests -{ - private const long DurationMs = 900_000; // 15 minutes, the shipped default - private const long Now = 1_000_000; - - private static void Reset(bool enabled = true, int maxEntries = 1024) => - AutoDenylist.LoadForTesting(enabled, DurationMs, maxEntries); - - [Fact] - public void Behavioral_detection_is_held_then_lapses() - { - Reset(); - var ip = IPAddress.Parse("198.51.100.10"); - - Assert.True(AutoDenylist.Hold(ip, BanReasons.InvalidSeed, Now)); - - Assert.True(AutoDenylist.IsDenied(ip, Now)); - Assert.True(AutoDenylist.IsDenied(ip, Now + DurationMs - 1)); - - // Expiry is decided on read, so the hold lapses without waiting for a sweep. - Assert.False(AutoDenylist.IsDenied(ip, Now + DurationMs)); - } - - [Fact] - public void Manual_and_list_verdicts_are_not_held() - { - Reset(); - var manual = IPAddress.Parse("198.51.100.11"); - var listed = IPAddress.Parse("198.51.100.12"); - - // A manual ban belongs in the firewall; a blocklist match is enforced by the blocklist's own filter. - Assert.False(AutoDenylist.Hold(manual, BanReasons.Manual, Now)); - Assert.False(AutoDenylist.Hold(listed, BanReasons.Blocklist, Now)); - - Assert.False(AutoDenylist.IsDenied(manual, Now)); - Assert.False(AutoDenylist.IsDenied(listed, Now)); - Assert.Equal(0, AutoDenylist.Count); - } - - // Not refreshed on purpose: it is what keeps insertion order equal to expiry order, so retiring lapsed - // entries costs the number expiring instead of the number held. A flooder whose hold lapses trips the - // rate limiter on its next attempt -- which runs ahead of the connection filters -- and is held again. - [Fact] - public void Repeat_detection_does_not_extend_the_hold() - { - Reset(); - var ip = IPAddress.Parse("198.51.100.13"); - - AutoDenylist.Hold(ip, BanReasons.SilentConnect, Now); - AutoDenylist.Hold(ip, BanReasons.SilentConnect, Now + DurationMs - 1); - - Assert.Equal(1, AutoDenylist.Count); // no duplicate - Assert.True(AutoDenylist.IsDenied(ip, Now + DurationMs - 1)); - Assert.False(AutoDenylist.IsDenied(ip, Now + DurationMs + 1)); // lapses from the FIRST detection - } - - // The ring carries the expiry and the set carries membership; if they ever disagree, an address is - // either denied forever or retired early. - [Fact] - public void Ring_and_set_stay_in_step() - { - Reset(maxEntries: 4); - - for (var i = 0; i < 8; i++) - { - AutoDenylist.Hold(IPAddress.Parse($"198.51.100.{70 + i}"), BanReasons.InvalidSeed, Now); - } - - Assert.Equal(4, AutoDenylist.Count); - Assert.Equal(AutoDenylist.Count, AutoDenylist.RingCount); - - AutoDenylist.Release(IPAddress.Parse("198.51.100.71")); - Assert.Equal(3, AutoDenylist.Count); - Assert.Equal(AutoDenylist.Count, AutoDenylist.RingCount); - - AutoDenylist.Drain(Now + DurationMs + 1); - Assert.Equal(0, AutoDenylist.Count); - Assert.Equal(0, AutoDenylist.RingCount); - } - - // The ring grows in doublings but is capped at maxEntries, which is not a power of two. Filling exactly - // to it must land on the last slot rather than off the end. - [Fact] - public void Ring_fills_exactly_to_a_non_power_of_two_cap() - { - Reset(maxEntries: 100); - - for (var i = 0; i < 120; i++) - { - AutoDenylist.Hold(IPAddress.Parse($"198.51.100.{i}"), BanReasons.InvalidSeed, Now); - } - - Assert.Equal(100, AutoDenylist.Count); - Assert.Equal(100, AutoDenylist.RingCount); - - // And the whole ring still drains, so no slot was stranded by a wrapped write. - AutoDenylist.Drain(Now + DurationMs + 1); - Assert.Equal(0, AutoDenylist.Count); - Assert.Equal(0, AutoDenylist.RingCount); - } - - // Releasing leaves no ring record behind, so a re-detection is not retired by the old one. - [Fact] - public void Release_then_re_hold_is_not_retired_by_the_stale_record() - { - Reset(); - var ip = IPAddress.Parse("198.51.100.15"); - - AutoDenylist.Hold(ip, BanReasons.RateLimit, Now); - AutoDenylist.Release(ip); - - var later = Now + DurationMs - 1; - AutoDenylist.Hold(ip, BanReasons.RateLimit, later); - - // The first hold's expiry has passed; the second must survive it. - Assert.True(AutoDenylist.IsDenied(ip, Now + DurationMs + 1)); - Assert.Equal(1, AutoDenylist.RingCount); - } - - [Fact] - public void Release_drops_the_hold_immediately() - { - Reset(); - var ip = IPAddress.Parse("198.51.100.14"); - - AutoDenylist.Hold(ip, BanReasons.RateLimit, Now); - AutoDenylist.Release(ip); - - Assert.False(AutoDenylist.IsDenied(ip, Now)); - } - - [Fact] - public void Cap_is_enforced_so_a_distinct_source_flood_cannot_grow_it() - { - Reset(maxEntries: 4); - - for (var i = 0; i < 10; i++) - { - AutoDenylist.Hold(IPAddress.Parse($"198.51.100.{100 + i}"), BanReasons.InvalidSeed, Now); - } - - Assert.Equal(4, AutoDenylist.Count); - - // The first four are still held; the rest were disconnected by their gate but not tracked. - Assert.True(AutoDenylist.IsDenied(IPAddress.Parse("198.51.100.100"), Now)); - Assert.False(AutoDenylist.IsDenied(IPAddress.Parse("198.51.100.109"), Now)); - } - - [Fact] - public void Reaching_the_cap_reclaims_lapsed_entries_first() - { - Reset(maxEntries: 2); - - AutoDenylist.Hold(IPAddress.Parse("198.51.100.20"), BanReasons.InvalidSeed, Now); - AutoDenylist.Hold(IPAddress.Parse("198.51.100.21"), BanReasons.InvalidSeed, Now); - - // Once those two have lapsed, a new detection sweeps them out rather than being refused. - var later = Now + DurationMs + 1; - Assert.True(AutoDenylist.Hold(IPAddress.Parse("198.51.100.22"), BanReasons.InvalidSeed, later)); - Assert.True(AutoDenylist.IsDenied(IPAddress.Parse("198.51.100.22"), later)); - } - - [Fact] - public void Disabled_store_denies_nobody() - { - Reset(enabled: false); - var ip = IPAddress.Parse("198.51.100.30"); - - Assert.False(AutoDenylist.Hold(ip, BanReasons.InvalidSeed, Now)); - Assert.False(AutoDenylist.IsDenied(ip, Now)); - } - - [Fact] - public void Null_address_is_handled() - { - Reset(); - - Assert.False(AutoDenylist.Hold(null, BanReasons.InvalidSeed, Now)); - Assert.False(AutoDenylist.IsDenied(null, Now)); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs b/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs deleted file mode 100644 index b38364373..000000000 --- a/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs +++ /dev/null @@ -1,122 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BanExemptionsTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Net; -using System.Text; -using Server.Network; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests.Network.Exemptions; - -// Addresses come from TEST-NET-1 (192.0.2.0/24) to stay clear of the other ban test classes if xUnit runs -// them concurrently. -public class BanExemptionsTests -{ - private static readonly IPAddress _listed = IPAddress.Parse("192.0.2.10"); - private static readonly IPAddress _unlisted = IPAddress.Parse("192.0.2.11"); - - private static void WithManualAllowlist(string contents) => - ManualAllowlist.LoadForTesting(BlocklistSnapshot.Build(Encoding.ASCII.GetBytes(contents), out _, out _)); - - private static void WithEmptyManualAllowlist() => ManualAllowlist.LoadForTesting(BlocklistSnapshot.Empty); - - [Fact] - public void Manual_allowlist_exempts_behavioral_contributions() - { - WithManualAllowlist("192.0.2.10"); - - // Subtracting from the blocklist does nothing for behavioural detections, which never consult it. - Assert.True(BanExemptions.IsExempt(_listed, BanReasons.ForeignProtocol, NeverCalled)); - Assert.True(BanExemptions.IsExempt(_listed, BanReasons.RateLimit, NeverCalled)); - Assert.True(BanExemptions.IsExempt(_listed, BanReasons.SilentConnect, NeverCalled)); - Assert.True(BanExemptions.IsExempt(_listed, BanReasons.InvalidSeed, NeverCalled)); - } - - [Fact] - public void Manual_allowlist_covers_cidr_entries() - { - // Carve-outs are CIDRs, so a shared-CGNAT player is only covered if ranges work here. - WithManualAllowlist("192.0.2.0/24"); - - Assert.True(BanExemptions.IsExempt(_listed, BanReasons.RateLimit, NeverCalled)); - Assert.True(BanExemptions.IsExempt(IPAddress.Parse("192.0.2.254"), BanReasons.RateLimit, NeverCalled)); - Assert.False(BanExemptions.IsExempt(IPAddress.Parse("192.0.3.1"), BanReasons.RateLimit, AlwaysFalse)); - } - - [Fact] - public void Manual_bans_are_never_exempt_even_when_allowlisted() - { - WithManualAllowlist("192.0.2.10"); - - // An explicit decision outranks the operator's own carve-out, and must not cost a strike. - Assert.False(BanExemptions.IsExempt(_listed, BanReasons.Manual, NeverCalled)); - } - - [Fact] - public void Unopted_reasons_are_never_exempt() - { - WithManualAllowlist("192.0.2.10"); - - Assert.False(BanExemptions.IsExempt(_listed, BanReasons.Blocklist, NeverCalled)); - Assert.False(BanExemptions.IsExempt(_listed, "some-future-reason", NeverCalled)); - } - - [Fact] - public void Manual_allowlist_does_not_spend_the_earned_lists_strikes() - { - WithManualAllowlist("192.0.2.10"); - - // Unconditional, so the revocable list must not be consulted -- that would burn a strike. - Assert.True(BanExemptions.IsExempt(_listed, BanReasons.RateLimit, NeverCalled)); - } - - [Fact] - public void Falls_through_to_the_login_allowlist_when_not_file_listed() - { - WithEmptyManualAllowlist(); - - var consulted = 0; - - var result = BanExemptions.IsExempt( - _unlisted, - BanReasons.RateLimit, - (_, _) => - { - consulted++; - return true; - } - ); - - Assert.True(result); - Assert.Equal(1, consulted); - } - - [Fact] - public void Null_address_is_never_exempt() - { - WithEmptyManualAllowlist(); - - Assert.False(BanExemptions.IsExempt(null, BanReasons.RateLimit, NeverCalled)); - } - - private static bool NeverCalled(IPAddress address, string reason) - { - Assert.Fail("The login allowlist must not be consulted once the answer is already decided."); - return false; - } - - private static bool AlwaysFalse(IPAddress address, string reason) => false; -} diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs deleted file mode 100644 index 3d81c8d87..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BlocklistConfigurationTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Text.Json; -using Server.Json; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests.Network.Bans.Blocklist; - -public class BlocklistConfigurationTests -{ - // Locks the JsonConfig casing contract: JsonConfig's options are case-SENSITIVE, so every settings - // member must carry an explicit [JsonPropertyName("camelCase")] or it silently binds nothing. - [Fact] - public void BlocklistSettings_RoundTripsThroughJsonConfig() - { - var original = new BlocklistSettings - { - Enabled = true, - File = "D:/shared/ip-blocklist.txt", - ReloadInterval = TimeSpan.FromMinutes(5), - ReportHits = false, - BanDuration = TimeSpan.FromHours(2), - PromoteSuppression = TimeSpan.FromSeconds(30) - }; - - var json = JsonConfig.Serialize(original); - - Assert.Contains("\"enabled\"", json); - Assert.Contains("\"file\"", json); - Assert.Contains("\"reloadInterval\"", json); - Assert.Contains("\"reportHits\"", json); - Assert.Contains("\"banDuration\"", json); - Assert.Contains("\"promoteSuppression\"", json); - - var restored = JsonSerializer.Deserialize(json, JsonConfig.DefaultOptions); - - Assert.NotNull(restored); - Assert.Equal(original.Enabled, restored.Enabled); - Assert.Equal(original.File, restored.File); - Assert.Equal(original.ReloadInterval, restored.ReloadInterval); - Assert.Equal(original.ReportHits, restored.ReportHits); - Assert.Equal(original.BanDuration, restored.BanDuration); - Assert.Equal(original.PromoteSuppression, restored.PromoteSuppression); - } - - // The point of the flag: a shard that never opts in must not start the reload poll. - [Fact] - public void Blocklist_is_off_by_default() - { - Assert.False(new BlocklistSettings().Enabled); - } - - // The generator (tools/Export-IpBlocklist.ps1) writes to this path by default; if one side moves - // without the other, a shard silently enforces nothing. - [Fact] - public void Default_file_matches_the_generator_output_path() - { - Assert.Equal("Configuration/ip-blocklist.txt", new BlocklistSettings().File); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistFileTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistFileTests.cs deleted file mode 100644 index 1b7a7772c..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistFileTests.cs +++ /dev/null @@ -1,85 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BlocklistFileTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.IO; -using System.Net; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests.Network.Bans.Blocklist; - -public class BlocklistFileTests -{ - private static string WriteTemp(string content) - { - var p = Path.Combine(Path.GetTempPath(), "bl-" + Guid.NewGuid().ToString("N") + ".txt"); - File.WriteAllText(p, content); - return p; - } - - [Fact] - public void Reads_header_generated_and_count() - { - var p = WriteTemp("# modernuo-blocklist v1 generated=2026-07-21T09:24:23Z count=2\n1.2.3.4\n5.6.7.0/24\n"); - Assert.True(BlocklistFile.TryReadHeader(p, out var h)); - Assert.True(h.Present); - Assert.Equal("2026-07-21T09:24:23Z", h.Generated); - Assert.Equal(2, h.Count); - File.Delete(p); - } - - [Fact] - public void Missing_file_reports_absent_and_loads_empty() - { - var p = Path.Combine(Path.GetTempPath(), "does-not-exist-" + Guid.NewGuid().ToString("N")); - Assert.False(BlocklistFile.TryReadHeader(p, out var h)); - Assert.False(h.Present); - var snap = BlocklistFile.Load(p, out _, out _); - Assert.False(snap.IsBanned(IPAddress.Parse("1.2.3.4"))); - } - - // Pins the exact line tools/Export-IpBlocklist.ps1 emits: the producer adds informational tokens the - // reader doesn't know about, and the reload detector breaks silently if generated= stops being read. - [Fact] - public void Reads_generator_header_with_extra_tokens() - { - var p = WriteTemp( - "# modernuo-blocklist generated=2026-07-25T18:03:11Z count=12442 ipv4=7868 cidr=4574 feeds=2\n" + - "2.181.183.77\n223.169.0.0/16\n" - ); - - Assert.True(BlocklistFile.TryReadHeader(p, out var h)); - Assert.Equal("2026-07-25T18:03:11Z", h.Generated); - Assert.Equal(12442, h.Count); - - var snap = BlocklistFile.Load(p, out var parsed, out var skipped); - Assert.Equal(2, parsed); - Assert.Equal(0, skipped); - Assert.True(snap.IsBanned(IPAddress.Parse("2.181.183.77"))); - Assert.True(snap.IsBanned(IPAddress.Parse("223.169.4.9"))); - File.Delete(p); - } - - [Fact] - public void Load_parses_body() - { - var p = WriteTemp("# generated=x count=1\n8.8.8.0/24\n"); - var snap = BlocklistFile.Load(p, out var parsed, out _); - Assert.Equal(1, parsed); - Assert.True(snap.IsBanned(IPAddress.Parse("8.8.8.8"))); - File.Delete(p); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistFilterTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistFilterTests.cs deleted file mode 100644 index 4f3ee5ad1..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistFilterTests.cs +++ /dev/null @@ -1,94 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BlocklistFilterTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Net; -using System.Text; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests.Network.Bans.Blocklist; - -// No [Collection] and no static reset hook: the filter is an instance, so each test owns its own -// snapshot and promote-guard. That is the point of it no longer being a static class. -public class BlocklistFilterTests -{ - private static BlocklistFilter WithList(string list, bool reportHits = true, long suppressionMs = 5000) - { - var filter = new BlocklistFilter(); - filter.LoadForTesting( - BlocklistSnapshot.Build(Encoding.ASCII.GetBytes(list), out _, out _), - reportHits, - suppressionMs - ); - - return filter; - } - - [Fact] - public void Listed_address_is_denied_and_reported_once_per_window() - { - var filter = WithList("1.2.3.4"); - var ip = IPAddress.Parse("1.2.3.4"); - - var deny1 = filter.Evaluate(ip, 1000, out var report1); - var deny2 = filter.Evaluate(ip, 1500, out var report2); - var deny3 = filter.Evaluate(ip, 1000 + 5001, out var report3); - - Assert.True(deny1); - Assert.True(report1); - - Assert.True(deny2); - Assert.False(report2); // denied again, but promotion suppressed inside the window - - Assert.True(deny3); - Assert.True(report3); // window elapsed, promotion may be retried - } - - [Fact] - public void Unlisted_address_passes() - { - var filter = WithList("1.2.3.4"); - - Assert.False(filter.Evaluate(IPAddress.Parse("9.9.9.9"), 1, out var report)); - Assert.False(report); - } - - [Fact] - public void Cidr_membership_is_honored() - { - var filter = WithList("10.20.30.0/24"); - - Assert.True(filter.Evaluate(IPAddress.Parse("10.20.30.255"), 1, out _)); - Assert.False(filter.Evaluate(IPAddress.Parse("10.20.31.0"), 1, out _)); - } - - [Fact] - public void ReportHits_disabled_still_denies_but_never_promotes() - { - var filter = WithList("1.2.3.4", reportHits: false); - - Assert.True(filter.Evaluate(IPAddress.Parse("1.2.3.4"), 1, out var report)); - Assert.False(report); - } - - [Fact] - public void Unconfigured_filter_denies_nothing() - { - var filter = new BlocklistFilter(); - - Assert.Equal(0, filter.Count); - Assert.False(filter.ShouldDeny(IPAddress.Parse("8.8.8.8"))); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistSnapshotTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistSnapshotTests.cs deleted file mode 100644 index abdac1d11..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistSnapshotTests.cs +++ /dev/null @@ -1,98 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BlocklistSnapshotTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Net; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests.Network.Bans.Blocklist; - -public class BlocklistSnapshotTests -{ - private static BlocklistSnapshot Build(params string[] lines) => - BlocklistSnapshot.Build(System.Text.Encoding.ASCII.GetBytes(string.Join('\n', lines)), out _, out _); - - [Fact] - public void Single_ip_is_matched() - { - var s = Build("1.2.3.4"); - Assert.True(s.IsBanned(IPAddress.Parse("1.2.3.4"))); - Assert.False(s.IsBanned(IPAddress.Parse("1.2.3.5"))); - } - - [Fact] - public void Cidr_contains_and_excludes_boundaries() - { - var s = Build("10.0.0.0/24"); - Assert.True(s.IsBanned(IPAddress.Parse("10.0.0.0"))); - Assert.True(s.IsBanned(IPAddress.Parse("10.0.0.255"))); - Assert.False(s.IsBanned(IPAddress.Parse("10.0.1.0"))); - Assert.False(s.IsBanned(IPAddress.Parse("9.255.255.255"))); - } - - [Fact] - public void Comments_blanks_and_garbage_are_skipped_not_thrown() - { - var s = BlocklistSnapshot.Build( - System.Text.Encoding.ASCII.GetBytes( - string.Join('\n', "# header generated=x", "", "not-an-ip", "1.2.3.4", "::1", "5.6.7.0/24")), - out var parsed, out var skipped); - Assert.Equal(3, parsed); // 1.2.3.4 + ::1 (valid loopback) + 5.6.7.0/24 - Assert.True(skipped >= 1); // "not-an-ip"; blank/comment lines are silently skipped, not counted - Assert.True(s.IsBanned(IPAddress.Parse("5.6.7.200"))); - } - - [Fact] - public void Empty_snapshot_matches_nothing() - { - Assert.False(BlocklistSnapshot.Empty.IsBanned(IPAddress.Parse("1.2.3.4"))); - } - - [Fact] - public void Ipv6_single_and_cidr_are_matched() - { - var s = Build("2001:db8::1", "2001:db8:1::/48"); - Assert.True(s.IsBanned(IPAddress.Parse("2001:db8::1"))); - Assert.True(s.IsBanned(IPAddress.Parse("2001:db8:1::abcd"))); - Assert.False(s.IsBanned(IPAddress.Parse("2001:db8:2::1"))); - } - - [Fact] - public void Ipv4_mapped_ipv6_is_normalized_to_v4() - { - var s = Build("1.2.3.4"); - Assert.True(s.IsBanned(IPAddress.Parse("::ffff:1.2.3.4"))); // must not bypass the v4 set - } - - [Fact] - public void Nested_cidr_intervals_are_coalesced() - { - // A /32 nested inside a /24: InRange's binary search only inspects the - // rightmost interval starting <= ip, so without coalescing an IP inside - // the /24 but outside the /32 would land on the /32 and wrongly pass. - var s = Build("10.0.0.0/24", "10.0.0.5/32"); - Assert.True(s.IsBanned(IPAddress.Parse("10.0.0.100"))); // inside /24, outside /32 - Assert.True(s.IsBanned(IPAddress.Parse("10.0.0.5"))); // the nested /32 itself - Assert.False(s.IsBanned(IPAddress.Parse("10.0.1.0"))); // genuinely outside both - } - - [Fact] - public void Overlapping_cidr_intervals_are_coalesced() - { - var s = Build("10.0.0.0/25", "10.0.0.64/25"); - Assert.True(s.IsBanned(IPAddress.Parse("10.0.0.100"))); // covered by the second /25 - Assert.False(s.IsBanned(IPAddress.Parse("10.0.0.200"))); // outside both - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/PromotedGuardTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/PromotedGuardTests.cs deleted file mode 100644 index 7c19b8d38..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/PromotedGuardTests.cs +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: PromotedGuardTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests.Network.Bans.Blocklist; - -public class PromotedGuardTests -{ - [Fact] - public void First_mark_true_then_suppressed_until_ttl() - { - var g = new PromotedGuard(); - Assert.True(g.TryMark((UInt128)42, 1000, 5000)); - Assert.False(g.TryMark((UInt128)42, 2000, 5000)); // within TTL - Assert.True(g.TryMark((UInt128)42, 6001, 5000)); // expired → re-mark - } - - [Fact] - public void Sweep_removes_expired_entries_allowing_remark() - { - var g = new PromotedGuard(); - Assert.True(g.TryMark((UInt128)7, 0, 1000)); - Assert.False(g.TryMark((UInt128)7, 500, 1000)); // still within TTL - - g.Sweep(500); // not yet expired, sweep should not remove it - Assert.False(g.TryMark((UInt128)7, 999, 1000)); - - g.Sweep(1001); // now expired, sweep removes it - Assert.True(g.TryMark((UInt128)7, 1002, 1000)); // fresh mark, not "still marked" - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecAlertClientTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecAlertClientTests.cs deleted file mode 100644 index 756e19b1d..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecAlertClientTests.cs +++ /dev/null @@ -1,62 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: CrowdSecAlertClientTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Net; -using Server.Network.Bans.CrowdSec; -using Xunit; - -namespace Server.Tests.Network.Bans; - -public class CrowdSecAlertClientTests -{ - /// - /// LAPI's default watcher profile matches the crowdsec/ prefix and answers 401 without it, so - /// this is a protocol constraint rather than a cosmetic product string. - /// - [Fact] - public void UserAgent_IsPrefixedForTheWatcherProfile() - { - Assert.StartsWith("crowdsec/", CrowdSecAlertClient.UserAgent, StringComparison.Ordinal); - } - - [Fact] - public void BuildDeleteQuery_EscapesOrigin() - { - // origin is operator-controlled config (crowdsec.json), so it must be treated as untrusted - // input going into the URL, same as any other interpolated value. - var query = CrowdSecAlertClient.BuildDeleteQuery("modern uo/test&x=1", IPAddress.Parse("1.2.3.4")); - - Assert.Equal("/v1/decisions?origin=modern%20uo%2Ftest%26x%3D1&ip=1.2.3.4", query); - } - - [Fact] - public void BuildDeleteQuery_PlainOrigin_Ipv4() - { - var query = CrowdSecAlertClient.BuildDeleteQuery("modernuo", IPAddress.Parse("192.168.1.1")); - - Assert.Equal("/v1/decisions?origin=modernuo&ip=192.168.1.1", query); - } - - [Fact] - public void BuildDeleteQuery_Ipv6_IsEscaped() - { - // IPv6 textual form contains ':', which Uri.EscapeDataString percent-encodes like any other - // reserved character — confirms the escaping is applied uniformly, not just for IPv4. - var query = CrowdSecAlertClient.BuildDeleteQuery("modernuo", IPAddress.Parse("2001:db8::1")); - - Assert.Equal("/v1/decisions?origin=modernuo&ip=2001%3Adb8%3A%3A1", query); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecConfigurationTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecConfigurationTests.cs deleted file mode 100644 index dd331ac6d..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecConfigurationTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: CrowdSecConfigurationTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Text.Json; -using Server.Json; -using Server.Network.Bans.CrowdSec; -using Xunit; - -namespace Server.Tests.Network.Bans; - -public class CrowdSecConfigurationTests -{ - // Locks the JsonConfig casing/converter contract: JsonConfig's options are case-SENSITIVE, so - // every settings member must carry an explicit [JsonPropertyName("camelCase")] or it silently - // binds nothing. These tests round-trip through the exact options the loader uses. - - [Fact] - public void CrowdSecSettings_RoundTripsThroughJsonConfig() - { - var original = new CrowdSecSettings - { - LapiUrl = "http://10.0.0.5:9090", - MachineId = "shard", - Password = "secret", - Origin = "modernuo", - ManualBanDuration = TimeSpan.FromHours(168), - FlushInterval = TimeSpan.FromSeconds(1), - MaxQueue = 10000 - }; - - var json = JsonConfig.Serialize(original); - - // camelCase property names must be present (not PascalCase) or the case-sensitive reader binds nothing. - Assert.Contains("\"lapiUrl\"", json); - Assert.Contains("\"machineId\"", json); - Assert.Contains("\"password\"", json); - Assert.Contains("\"origin\"", json); - Assert.Contains("\"manualBanDuration\"", json); - Assert.Contains("\"flushInterval\"", json); - Assert.Contains("\"maxQueue\"", json); - - var restored = JsonSerializer.Deserialize(json, JsonConfig.DefaultOptions); - - Assert.NotNull(restored); - Assert.Equal(original.LapiUrl, restored.LapiUrl); - Assert.Equal(original.MachineId, restored.MachineId); - Assert.Equal(original.Password, restored.Password); - Assert.Equal(original.Origin, restored.Origin); - Assert.Equal(original.ManualBanDuration, restored.ManualBanDuration); // TimeSpan survives - Assert.Equal(original.FlushInterval, restored.FlushInterval); // TimeSpan survives - Assert.Equal(original.MaxQueue, restored.MaxQueue); - Assert.True(restored.ReportingEnabled); - } - - [Fact] - public void CrowdSecSettings_Defaults_AreReportingDisabledLocalLoopback() - { - var settings = new CrowdSecSettings(); - - Assert.Equal("http://127.0.0.1:8080", settings.LapiUrl); - Assert.Equal("", settings.MachineId); - Assert.Equal("", settings.Password); - Assert.Equal("modernuo", settings.Origin); - Assert.Equal(TimeSpan.FromHours(168), settings.ManualBanDuration); - Assert.Equal(TimeSpan.FromSeconds(1), settings.FlushInterval); - Assert.Equal(10000, settings.MaxQueue); - Assert.False(settings.ReportingEnabled); // empty machineId/password => inert - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecReporterTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecReporterTests.cs deleted file mode 100644 index 6ad850f34..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecReporterTests.cs +++ /dev/null @@ -1,285 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: CrowdSecReporterTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Net; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Server.Network.Bans.CrowdSec; -using Xunit; - -namespace Server.Tests.Network.Bans; - -public class CrowdSecReporterTests -{ - private static CrowdSecSettings Settings() => new() - { - MachineId = "shard", - Password = "secret", - Origin = "modernuo", - ManualBanDuration = TimeSpan.FromHours(168) - }; - - [Fact] - public void BuildAlerts_DedupsByIp() - { - var now = DateTime.UnixEpoch; - var items = new List - { - new(IPAddress.Parse("1.1.1.1"), TimeSpan.FromHours(1), "rate-limit", false), - new(IPAddress.Parse("1.1.1.1"), TimeSpan.FromHours(1), "rate-limit", false), - new(IPAddress.Parse("2.2.2.2"), TimeSpan.FromHours(1), "rate-limit", false) - }; - - var alerts = CrowdSecReporter.BuildAlerts(items, Settings(), now); - - Assert.Equal(2, alerts.Count); - Assert.All(alerts, a => Assert.Single(a.Decisions)); - Assert.Contains(alerts, a => a.Source.Value == "1.1.1.1"); - Assert.Contains(alerts, a => a.Source.Value == "2.2.2.2"); - } - - [Fact] - public void BuildAlerts_ScenarioFromReason_OriginFromSettings() - { - var alerts = CrowdSecReporter.BuildAlerts( - [new(IPAddress.Parse("3.3.3.3"), TimeSpan.FromHours(1), "manual", false)], - Settings(), - DateTime.UnixEpoch); - - var decision = Assert.Single(alerts).Decisions[0]; - Assert.Equal("modernuo/manual", alerts[0].Scenario); - Assert.Equal("modernuo", decision.Origin); - Assert.Equal("ban", decision.Type); - Assert.Equal("Ip", decision.Scope); - Assert.Equal("3.3.3.3", decision.Value); - } - - [Fact] - public void BuildAlerts_ScenarioFromReason_Blocklist() - { - var alerts = CrowdSecReporter.BuildAlerts( - [new(IPAddress.Parse("5.5.5.5"), TimeSpan.FromHours(1), "blocklist", false)], - Settings(), - DateTime.UnixEpoch); - - var decision = Assert.Single(alerts).Decisions[0]; - Assert.Equal("modernuo/blocklist", alerts[0].Scenario); - Assert.Equal("modernuo/blocklist", decision.Scenario); - } - - /// - /// LAPI dereferences scenario_hash/scenario_version unconditionally when persisting an alert, so an - /// omitted field is a 500, not a validation error. Asserted on the serialized payload rather than the - /// DTO because that is what actually goes on the wire. - /// - [Fact] - public void BuildAlerts_SerializedPayload_CarriesRequiredScenarioFields() - { - var alerts = CrowdSecReporter.BuildAlerts( - [new(IPAddress.Parse("9.9.9.9"), TimeSpan.FromHours(1), "rate-limit", false)], - Settings(), - DateTime.UnixEpoch); - - var payload = JsonSerializer.SerializeToNode(alerts)!.AsArray()[0]!.AsObject(); - - Assert.True(payload.ContainsKey("scenario_hash")); - Assert.True(payload.ContainsKey("scenario_version")); - Assert.Equal(JsonValueKind.String, payload["scenario_hash"]!.GetValue().ValueKind); - Assert.Equal(JsonValueKind.String, payload["scenario_version"]!.GetValue().ValueKind); - Assert.Equal(1, payload["capacity"]!.GetValue()); - } - - /// - /// ':' is the time-separator specifier in a custom .NET format string, so a shard under fi-FI used to - /// emit "T00.00.00.000Z" — which Go's time.RFC3339 rejects, and LAPI answers 500 for. th-TH additionally - /// shifts the year via the Buddhist calendar. - /// - [Theory] - [InlineData("fi-FI")] - [InlineData("th-TH")] - [InlineData("ar-SA")] - public void FormatTimestamp_IsIso8601_RegardlessOfCulture(string culture) - { - var previous = CultureInfo.CurrentCulture; - try - { - CultureInfo.CurrentCulture = new CultureInfo(culture); - Assert.Equal("1970-01-01T00:00:00.000Z", CrowdSecReporter.FormatTimestamp(DateTime.UnixEpoch)); - } - finally - { - CultureInfo.CurrentCulture = previous; - } - } - - /// A non-UTC input must still be stamped as UTC — the trailing 'Z' is a literal, not a claim. - [Fact] - public void FormatTimestamp_ConvertsNonUtcInput() - { - var local = new DateTimeOffset(1970, 1, 1, 2, 0, 0, TimeSpan.FromHours(2)).LocalDateTime; - - Assert.Equal("1970-01-01T00:00:00.000Z", CrowdSecReporter.FormatTimestamp(local)); - } - - [Fact] - public void FormatDuration_UsesSeconds_FloorsAtOne() - { - Assert.Equal("3600s", CrowdSecReporter.FormatDuration(TimeSpan.FromHours(1))); - Assert.Equal("1s", CrowdSecReporter.FormatDuration(TimeSpan.Zero)); - Assert.Equal("1s", CrowdSecReporter.FormatDuration(TimeSpan.FromMilliseconds(10))); - } - - [Fact] - public void Report_WhenQueueFull_DropsAndCounts() - { - var reporter = new CrowdSecReporter(new NullAlertClient(), new CrowdSecSettings - { - MachineId = "shard", - Password = "secret", - MaxQueue = 2 - }); - // Do NOT Start() the drain — so the queue fills and overflows deterministically. - - for (var i = 0; i < 10; i++) - { - reporter.Report(IPAddress.Parse("4.4.4." + i), TimeSpan.FromHours(1), "rate-limit"); - } - - Assert.True(reporter.DroppedCount >= 8); - } - - // Stop() without a prior Start() drives FlushRemainingOnStop() synchronously (no drain task, no - // Task.Delay backoff involved), so this is deterministic — no wall-clock timing dependency. - [Fact] - public void Stop_FlushesQueuedReports_ViaClient() - { - var client = new RecordingAlertClient(); - var reporter = new CrowdSecReporter(client, Settings()); - - reporter.Report(IPAddress.Parse("6.6.6.6"), TimeSpan.FromHours(1), "rate-limit"); - reporter.Stop(); - - var posted = Assert.Single(client.Posted); - Assert.Equal("6.6.6.6", Assert.Single(posted).Source.Value); - Assert.Equal(0, reporter.SendFailureCount); - } - - [Fact] - public void Stop_FlushesQueuedRetracts_ViaClient() - { - var client = new RecordingAlertClient(); - var reporter = new CrowdSecReporter(client, Settings()); - - reporter.Retract(IPAddress.Parse("8.8.8.8")); - reporter.Stop(); - - Assert.Equal(IPAddress.Parse("8.8.8.8"), Assert.Single(client.Deleted)); - Assert.Equal(0, reporter.SendFailureCount); - } - - [Fact] - public void Stop_WhenFlushSendFails_CountsSendFailure() - { - var reporter = new CrowdSecReporter(new ThrowingAlertClient(), Settings()); - - reporter.Report(IPAddress.Parse("7.7.7.7"), TimeSpan.FromHours(1), "rate-limit"); - reporter.Stop(); - - Assert.Equal(1, reporter.SendFailureCount); - } - - [Fact] - public void Stop_WithEmptyQueue_DoesNotInvokeClientOrFail() - { - var client = new RecordingAlertClient(); - var reporter = new CrowdSecReporter(client, Settings()); - - reporter.Stop(); - - Assert.Empty(client.Posted); - Assert.Equal(0, reporter.SendFailureCount); - } - - /// - /// The drain task must track the loop's lifetime, not just its first await — a ValueTask-returning - /// drain loop passed to Task.Run yields a Task<ValueTask> that completes immediately, which makes - /// Stop()'s drain-exited handshake a no-op. With an empty queue the loop parks on WaitToReadAsync, - /// so a correctly unwrapped task cannot win this race; a slow pool only under-detects. - /// - [Fact] - public async Task Start_DrainTaskSpansLoopLifetime_NotJustTheFirstAwait() - { - var reporter = new CrowdSecReporter(new NullAlertClient(), Settings()); - using var cts = new CancellationTokenSource(); - - reporter.Start(cts.Token); - - var drain = reporter.DrainTaskForTesting; - Assert.NotNull(drain); - - var first = await Task.WhenAny(drain, Task.Delay(TimeSpan.FromMilliseconds(500))); - Assert.False(ReferenceEquals(first, drain), "drain task completed while the loop was still running"); - - reporter.Stop(); - - Assert.True(drain.IsCompleted, "Stop() returned before the drain loop exited"); - } - - private sealed class NullAlertClient : ICrowdSecAlertClient - { - public ValueTask PostAlertsAsync(IReadOnlyList alerts, CancellationToken token) => - ValueTask.CompletedTask; - - public ValueTask DeleteDecisionsAsync(string origin, IPAddress ip, CancellationToken token) => - ValueTask.CompletedTask; - - public void Dispose() { } - } - - private sealed class RecordingAlertClient : ICrowdSecAlertClient - { - public List> Posted { get; } = []; - public List Deleted { get; } = []; - - public ValueTask PostAlertsAsync(IReadOnlyList alerts, CancellationToken token) - { - Posted.Add(alerts); - return ValueTask.CompletedTask; - } - - public ValueTask DeleteDecisionsAsync(string origin, IPAddress ip, CancellationToken token) - { - Deleted.Add(ip); - return ValueTask.CompletedTask; - } - - public void Dispose() { } - } - - private sealed class ThrowingAlertClient : ICrowdSecAlertClient - { - public ValueTask PostAlertsAsync(IReadOnlyList alerts, CancellationToken token) => - throw new InvalidOperationException("simulated LAPI outage"); - - public ValueTask DeleteDecisionsAsync(string origin, IPAddress ip, CancellationToken token) => - ValueTask.CompletedTask; - - public void Dispose() { } - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallPersistenceTests.cs b/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallPersistenceTests.cs deleted file mode 100644 index 01691e223..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallPersistenceTests.cs +++ /dev/null @@ -1,72 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: FirewallPersistenceTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Net; -using Server.Network; -using Xunit; - -namespace Server.Tests.Network.Firewall; - -[Collection("Sequential UOContent Tests")] -public class FirewallPersistenceTests -{ - [Fact] - public void ToSettings_RoundTrips_PermanentAndTtl() - { - Server.Network.Firewall.ResetForTesting(); - Server.Network.Firewall.Add(new SingleIpFirewallEntry(IPAddress.Parse("1.2.3.4"))); - Server.Network.Firewall.Add(new SingleIpFirewallEntry(IPAddress.Parse("2.2.2.2")), TimeSpan.FromHours(1)); - - var settings = Server.Network.Firewall.ToSettings(); - - Server.Network.Firewall.ResetForTesting(); - Server.Network.Firewall.LoadFrom(settings); - - Assert.True(Server.Network.Firewall.IsBlocked(IPAddress.Parse("1.2.3.4"))); - Assert.True(Server.Network.Firewall.IsBlocked(IPAddress.Parse("2.2.2.2"))); - } - - [Fact] - public void LoadFrom_SkipsAlreadyExpired() - { - Server.Network.Firewall.ResetForTesting(); - var settings = new FirewallSettings - { - Entries = - [ - // Core.Now, matching the clock LoadFrom compares against. - new FirewallEntryRecord { Value = "9.9.9.9", Expires = Core.Now.AddHours(-1) } - ] - }; - - Server.Network.Firewall.LoadFrom(settings); - - Assert.False(Server.Network.Firewall.IsBlocked(IPAddress.Parse("9.9.9.9"))); - } - - [Fact] - public void ToSettings_OmitsExpiryForPermanent() - { - Server.Network.Firewall.ResetForTesting(); - Server.Network.Firewall.Add(new SingleIpFirewallEntry(IPAddress.Parse("1.2.3.4"))); - - var settings = Server.Network.Firewall.ToSettings(); - - Assert.Single(settings.Entries); - Assert.Null(settings.Entries[0].Expires); - Assert.Equal("1.2.3.4", settings.Entries[0].Value); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallTests.cs b/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallTests.cs deleted file mode 100644 index fabf6d4ba..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallTests.cs +++ /dev/null @@ -1,89 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: FirewallTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Net; -using Server.Network; -using Xunit; - -namespace Server.Tests.Network.Firewall; - -[Collection("Sequential UOContent Tests")] -public class FirewallTests -{ - private static IPAddress Ip(string s) => IPAddress.Parse(s); - - [Fact] - public void Add_ThenIsBlocked_SingleIp() - { - Server.Network.Firewall.ResetForTesting(); - Assert.True(Server.Network.Firewall.Add(new SingleIpFirewallEntry(Ip("1.2.3.4")))); - Assert.True(Server.Network.Firewall.IsBlocked(Ip("1.2.3.4"))); - Assert.False(Server.Network.Firewall.IsBlocked(Ip("1.2.3.5"))); - } - - [Fact] - public void Add_Range_BlocksInside_NotOutside() - { - Server.Network.Firewall.ResetForTesting(); - Server.Network.Firewall.Add(new CidrFirewallEntry(Ip("10.0.0.0"), Ip("10.0.0.255"))); - Assert.True(Server.Network.Firewall.IsBlocked(Ip("10.0.0.7"))); - Assert.False(Server.Network.Firewall.IsBlocked(Ip("10.0.1.0"))); - } - - [Fact] - public void Remove_Unblocks() - { - Server.Network.Firewall.ResetForTesting(); - var entry = new SingleIpFirewallEntry(Ip("1.2.3.4")); - Server.Network.Firewall.Add(entry); - Assert.True(Server.Network.Firewall.Remove(entry)); - Assert.False(Server.Network.Firewall.IsBlocked(Ip("1.2.3.4"))); - } - - [Fact] - public void ExpireEntries_RemovesExpired_KeepsPermanent() - { - Server.Network.Firewall.ResetForTesting(); - var permanent = new SingleIpFirewallEntry(Ip("1.1.1.1")); - var temporary = new SingleIpFirewallEntry(Ip("2.2.2.2")); - Server.Network.Firewall.Add(permanent); // no ttl - Server.Network.Firewall.Add(temporary, TimeSpan.FromMilliseconds(50)); // ttl - - Server.Network.Firewall.ExpireEntries(Core.TickCount + 100); // past the ttl - - Assert.True(Server.Network.Firewall.IsBlocked(Ip("1.1.1.1"))); - Assert.False(Server.Network.Firewall.IsBlocked(Ip("2.2.2.2"))); - } - - [Fact] - public void ToFirewallEntry_ParsesForms() - { - Assert.IsType(Server.Network.Firewall.ToFirewallEntry("1.2.3.4")); - Assert.IsType(Server.Network.Firewall.ToFirewallEntry("10.0.0.0/24")); - Assert.IsType(Server.Network.Firewall.ToFirewallEntry("10.0.0.0-10.0.0.255")); - Assert.Null(Server.Network.Firewall.ToFirewallEntry("not-an-ip")); - } - - [Fact] - public void ReadFirewallSet_SurfacesAddedEntries() - { - Server.Network.Firewall.ResetForTesting(); - var entry = new SingleIpFirewallEntry(Ip("1.2.3.4")); - Server.Network.Firewall.Add(entry); - - Server.Network.Firewall.ReadFirewallSet(set => Assert.Contains(entry, set)); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/LoginAllowlist/LoginAllowlistTests.cs b/Projects/UOContent.Tests/Tests/Network/LoginAllowlist/LoginAllowlistTests.cs deleted file mode 100644 index 7f3780e58..000000000 --- a/Projects/UOContent.Tests/Tests/Network/LoginAllowlist/LoginAllowlistTests.cs +++ /dev/null @@ -1,218 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: LoginAllowlistTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Net; -using Server.Network; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests.Network.LoginAllowlists; - -// The list is static, so every test resets it first. Addresses come from TEST-NET-3 (203.0.113.0/24) so they -// cannot collide with the blocklist tests if xUnit runs the two classes at the same time. -public class LoginAllowlistTests -{ - private const long Ttl = 90 * 24 * 60 * 60; // 90 days, the shipped default - private const long Window = 3600; - private const long Now = 1_800_000_000; - - private static void Reset(bool enabled = true, int strikes = 0) => - LoginAllowlist.LoadForTesting(enabled, Ttl, strikes, Window); - - [Fact] - public void Recent_login_allows_its_address() - { - Reset(); - var ip = IPAddress.Parse("203.0.113.10"); - - LoginAllowlist.RecordLogin(ip, Now); - - Assert.True(LoginAllowlist.IsAllowed(ip, Now)); - Assert.True(LoginAllowlist.IsAllowed(ip, Now + Ttl / 2)); - } - - [Fact] - public void Entry_lapses_once_past_the_ttl() - { - Reset(); - var ip = IPAddress.Parse("203.0.113.11"); - - LoginAllowlist.RecordLogin(ip, Now); - - Assert.True(LoginAllowlist.IsAllowed(ip, Now + Ttl)); // the boundary still counts - Assert.False(LoginAllowlist.IsAllowed(ip, Now + Ttl + 1)); // a second later it does not - } - - [Fact] - public void Logging_in_again_renews_the_window() - { - Reset(); - var ip = IPAddress.Parse("203.0.113.12"); - - LoginAllowlist.RecordLogin(ip, Now); - LoginAllowlist.RecordLogin(ip, Now + Ttl); // still allowed here, so the entry is refreshed - - Assert.True(LoginAllowlist.IsAllowed(ip, Now + Ttl + Ttl)); - } - - [Fact] - public void Unknown_address_is_not_allowed() - { - Reset(); - LoginAllowlist.RecordLogin(IPAddress.Parse("203.0.113.13"), Now); - - Assert.False(LoginAllowlist.IsAllowed(IPAddress.Parse("203.0.113.14"), Now)); - } - - [Fact] - public void Private_addresses_are_never_recorded() - { - Reset(); - - // A LAN or loopback login says nothing about the public internet. - LoginAllowlist.RecordLogin(IPAddress.Parse("10.0.0.5"), Now); - LoginAllowlist.RecordLogin(IPAddress.Loopback, Now); - - Assert.False(LoginAllowlist.IsAllowed(IPAddress.Parse("10.0.0.5"), Now)); - Assert.False(LoginAllowlist.IsAllowed(IPAddress.Loopback, Now)); - Assert.Equal(0, LoginAllowlist.Count); - } - - [Fact] - public void Disabled_list_allows_nobody() - { - Reset(enabled: false); - var ip = IPAddress.Parse("203.0.113.15"); - - LoginAllowlist.RecordLogin(ip, Now); - - Assert.False(LoginAllowlist.IsAllowed(ip, Now)); - Assert.Equal(0, LoginAllowlist.Count); - } - - [Fact] - public void Null_address_is_handled() - { - Reset(); - - LoginAllowlist.RecordLogin(null, Now); - - Assert.False(LoginAllowlist.IsAllowed(null, Now)); - } - - // ----- escalation ------------------------------------------------------------------------------- - - [Fact] - public void Manual_bans_are_never_exempt() - { - Reset(); - var ip = IPAddress.Parse("203.0.113.20"); - LoginAllowlist.RecordLogin(ip, Now); - - // An explicit decision must reach the reporters even for an allowlisted address. - Assert.False(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.Manual, Now)); - } - - [Fact] - public void Unopted_reasons_are_never_exempt() - { - Reset(); - var ip = IPAddress.Parse("203.0.113.21"); - LoginAllowlist.RecordLogin(ip, Now); - - // A reason nobody opted into IsBehavioral escalates normally rather than inheriting an exemption. - Assert.False(LoginAllowlist.IsExemptFromEscalation(ip, "some-future-reason", Now)); - Assert.False(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.Blocklist, Now)); - } - - [Fact] - public void Behavioral_reasons_are_exempt_while_allowed() - { - Reset(); - var ip = IPAddress.Parse("203.0.113.22"); - LoginAllowlist.RecordLogin(ip, Now); - - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.SilentConnect, Now)); - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.InvalidSeed, Now)); - } - - [Fact] - public void Entry_is_revoked_once_the_strikes_run_out() - { - Reset(strikes: 3); - var ip = IPAddress.Parse("203.0.113.23"); - LoginAllowlist.RecordLogin(ip, Now); - - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - - // The third strike is the one that escalates, and it takes the entry with it. - Assert.False(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - Assert.False(LoginAllowlist.IsAllowed(ip, Now)); - - // Still revoked afterwards, so everything from here escalates too. - Assert.False(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - } - - [Fact] - public void Quiet_window_clears_the_tally() - { - Reset(strikes: 3); - var ip = IPAddress.Parse("203.0.113.24"); - LoginAllowlist.RecordLogin(ip, Now); - - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - - // Past the window the count restarts, so an occasional tripper never accumulates to revocation. - var later = Now + Window + 1; - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, later)); - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, later)); - Assert.True(LoginAllowlist.IsAllowed(ip, later)); - } - - [Fact] - public void Logging_in_again_forgives_accumulated_strikes() - { - Reset(strikes: 3); - var ip = IPAddress.Parse("203.0.113.25"); - LoginAllowlist.RecordLogin(ip, Now); - - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - - LoginAllowlist.RecordLogin(ip, Now); // someone proved they hold an account again - - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - Assert.True(LoginAllowlist.IsAllowed(ip, Now)); - } - - [Fact] - public void Zero_threshold_disables_revocation() - { - Reset(strikes: 0); - var ip = IPAddress.Parse("203.0.113.26"); - LoginAllowlist.RecordLogin(ip, Now); - - for (var i = 0; i < 50; i++) - { - Assert.True(LoginAllowlist.IsExemptFromEscalation(ip, BanReasons.RateLimit, Now)); - } - - Assert.True(LoginAllowlist.IsAllowed(ip, Now)); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/ManualAllowlist/ManualAllowlistConfigurationTests.cs b/Projects/UOContent.Tests/Tests/Network/ManualAllowlist/ManualAllowlistConfigurationTests.cs deleted file mode 100644 index 1146414b5..000000000 --- a/Projects/UOContent.Tests/Tests/Network/ManualAllowlist/ManualAllowlistConfigurationTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ManualAllowlistConfigurationTests.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Text.Json; -using Server.Json; -using Server.Network.Bans; -using Xunit; - -namespace Server.Tests.Network.ManualAllowlists; - -public class ManualAllowlistConfigurationTests -{ - // Locks the JsonConfig casing contract: JsonConfig's options are case-SENSITIVE, so every settings - // member must carry an explicit [JsonPropertyName("camelCase")] or it silently binds nothing. - [Fact] - public void ManualAllowlistSettings_RoundTripsThroughJsonConfig() - { - var original = new ManualAllowlistSettings - { - Enabled = true, - Files = ["D:/shared/ip-allowlist*.txt"], - ReloadInterval = TimeSpan.FromMinutes(5) - }; - - var json = JsonConfig.Serialize(original); - - Assert.Contains("\"enabled\"", json); - Assert.Contains("\"files\"", json); - Assert.Contains("\"reloadInterval\"", json); - - var restored = JsonSerializer.Deserialize(json, JsonConfig.DefaultOptions); - - Assert.NotNull(restored); - Assert.Equal(original.Enabled, restored.Enabled); - Assert.Equal(original.Files, restored.Files); - Assert.Equal(original.ReloadInterval, restored.ReloadInterval); - } - - // The point of the flag: a shard that never opts in must not start the reload poll. - [Fact] - public void Manual_allowlist_is_off_by_default() - { - Assert.False(new ManualAllowlistSettings().Enabled); - } - - // The generator creates ip-allowlist.txt beside the blocklist; the wildcard is what picks up a - // carve-out file (-RefreshCarveouts writes ip-allowlist-starlink.txt) with no config edit. - [Fact] - public void Default_pattern_matches_the_generator_output_path() - { - Assert.Equal(["Configuration/ip-allowlist*.txt"], new ManualAllowlistSettings().Files); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Packets/ArrowPacketTests.cs b/Projects/UOContent.Tests/Tests/Network/Packets/ArrowPacketTests.cs index 6fb16c4f9..0f1b01b39 100644 --- a/Projects/UOContent.Tests/Tests/Network/Packets/ArrowPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/Packets/ArrowPacketTests.cs @@ -3,20 +3,19 @@ using Xunit; namespace Server.Tests.Network; -[Collection("Sequential UOContent Tests")] public class ArrowPacketTests { [Fact] public void TestCancelArrow() { - var expected = new CancelArrow().Compile(); + var expected = new CancelArrow().Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendCancelArrow(0, 0, Serial.Zero); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendCancelArrow(0, 0, Serial.Zero); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(0, 0)] @@ -24,14 +23,14 @@ public class ArrowPacketTests [InlineData(100000, 100000)] public void TestSetArrow(int x, int y) { - var expected = new SetArrow(x, y).Compile(); + var expected = new SetArrow(x, y).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendSetArrow(x, y, Serial.Zero); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendSetArrow(x, y, Serial.Zero); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(0, 0)] @@ -39,17 +38,17 @@ public class ArrowPacketTests [InlineData(100000, 100000)] public void TestCancelArrowHS(int x, int y) { - var serial = (Serial)0x1024; + Serial serial = (Serial)0x1024; - var expected = new CancelArrowHS(x, y, serial).Compile(); + var expected = new CancelArrowHS(x, y, serial).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = ProtocolChanges.HighSeas; - ns.SendCancelArrow(x, y, serial); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = ProtocolChanges.HighSeas; + ns.SendCancelArrow(x, y, serial); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Theory] [InlineData(0, 0)] @@ -57,15 +56,15 @@ public class ArrowPacketTests [InlineData(100000, 100000)] public void TestSetArrowHS(int x, int y) { - var serial = (Serial)0x1024; + Serial serial = (Serial)0x1024; - var expected = new SetArrowHS(x, y, serial).Compile(); + var expected = new SetArrowHS(x, y, serial).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.ProtocolChanges = ProtocolChanges.HighSeas; - ns.SendSetArrow(x, y, serial); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.ProtocolChanges = ProtocolChanges.HighSeas; + ns.SendSetArrow(x, y, serial); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Network/Packets/ArrowPackets.cs b/Projects/UOContent.Tests/Tests/Network/Packets/ArrowPackets.cs index cb5b0019a..5fea8c156 100644 --- a/Projects/UOContent.Tests/Tests/Network/Packets/ArrowPackets.cs +++ b/Projects/UOContent.Tests/Tests/Network/Packets/ArrowPackets.cs @@ -4,40 +4,40 @@ public sealed class CancelArrow : Packet { public CancelArrow() : base(0xBA, 6) { - Stream.Write((byte)0); - Stream.Write((short)-1); - Stream.Write((short)-1); - } + Stream.Write((byte)0); + Stream.Write((short)-1); + Stream.Write((short)-1); + } } public sealed class SetArrow : Packet { public SetArrow(int x, int y) : base(0xBA, 6) { - Stream.Write((byte)1); - Stream.Write((short)x); - Stream.Write((short)y); - } + Stream.Write((byte)1); + Stream.Write((short)x); + Stream.Write((short)y); + } } public sealed class CancelArrowHS : Packet { public CancelArrowHS(int x, int y, Serial s) : base(0xBA, 10) { - Stream.Write((byte)0); - Stream.Write((short)x); - Stream.Write((short)y); - Stream.Write(s); - } + Stream.Write((byte)0); + Stream.Write((short)x); + Stream.Write((short)y); + Stream.Write(s); + } } public sealed class SetArrowHS : Packet { public SetArrowHS(int x, int y, Serial s) : base(0xBA, 10) { - Stream.Write((byte)1); - Stream.Write((short)x); - Stream.Write((short)y); - Stream.Write(s); - } -} + Stream.Write((byte)1); + Stream.Write((short)x); + Stream.Write((short)y); + Stream.Write(s); + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Network/Packets/AuthIdTests.cs b/Projects/UOContent.Tests/Tests/Network/Packets/AuthIdTests.cs deleted file mode 100644 index 456eb174a..000000000 --- a/Projects/UOContent.Tests/Tests/Network/Packets/AuthIdTests.cs +++ /dev/null @@ -1,382 +0,0 @@ -using System; -using System.Net; -using Server.Accounting; -using Server.Accounting.Security; -using Server.Network; -using Server.Tests.Network; -using Xunit; - -namespace Server.Tests.Network.Packets; - -[Collection("Sequential UOContent Tests")] -public class AuthIdTests : IDisposable -{ - private static readonly IPAddress AddressX = IPAddress.Parse("203.0.113.10"); - private static readonly IPAddress AddressY = IPAddress.Parse("203.0.113.11"); - - private readonly PasswordProtectionAlgorithm _originalAlgorithm = AccountSecurity.CurrentAlgorithm; - - public AuthIdTests() - { - AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; - IncomingAccountPackets.ClearAuthIdWindow(); - } - - public void Dispose() - { - IncomingAccountPackets.ClearAuthIdWindow(); - AccountSecurity.CurrentAlgorithm = _originalAlgorithm; - } - - private static IAccount CreateAccount(string username) => - Accounts.GetAccount(username) ?? new Account(username, "hunter2"); - - private static int Register(IAccount account, IPAddress address) => - IncomingAccountPackets.RegisterAuthId(account, address, new ClientVersion(7, 0, 0, 0)); - - [Fact] - public void VouchesForTheAccountAndAddressItWasIssuedTo() - { - var account = CreateAccount("authid-match-user"); - var authId = Register(account, AddressX); - - var result = IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out var entry); - - Assert.Equal(IncomingAccountPackets.AuthIdResult.Vouched, result); - Assert.Same(account, entry.Account); - } - - [Fact] - public void RejectsADifferentAccount() - { - var issued = CreateAccount("authid-owner-user"); - var other = CreateAccount("authid-other-user"); - var authId = Register(issued, AddressX); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Rejected, - IncomingAccountPackets.ConsumeAuthId(authId, other.Username, AddressX, out _) - ); - } - - [Fact] - public void RejectsADifferentAddress() - { - var account = CreateAccount("authid-switch-user"); - var authId = Register(account, AddressX); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Rejected, - IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressY, out _) - ); - } - - [Fact] - public void MatchesTheUsernameCaseInsensitively() - { - var account = CreateAccount("AuthId-Case-User"); - var authId = Register(account, AddressX); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Vouched, - IncomingAccountPackets.ConsumeAuthId(authId, "authid-case-user", AddressX, out _) - ); - } - - [Fact] - public void MatchesAnIPv4MappedIPv6Address() - { - var account = CreateAccount("authid-mapped-user"); - var authId = Register(account, AddressX); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Vouched, - IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX.MapToIPv6(), out _) - ); - } - - [Fact] - public void RejectsAnUnknownAuthId() - { - var account = CreateAccount("authid-unknown-user"); - var authId = Register(account, AddressX); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Rejected, - IncomingAccountPackets.ConsumeAuthId(authId + 1, account.Username, AddressX, out _) - ); - } - - [Fact] - public void IsSingleUseAfterASuccess() - { - var account = CreateAccount("authid-once-user"); - var authId = Register(account, AddressX); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Vouched, - IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out _) - ); - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Rejected, - IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out _) - ); - } - - // A rejected attempt must not consume the id, or anyone landing on a live one could burn it and - // force its owner to log in again. - [Fact] - public void SurvivesAnAttemptFromTheWrongAddress() - { - var account = CreateAccount("authid-not-burned-address-user"); - var authId = Register(account, AddressX); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Rejected, - IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressY, out _) - ); - - Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Vouched, - IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out _) - ); - } - - [Fact] - public void SurvivesAnAttemptForTheWrongAccount() - { - var account = CreateAccount("authid-not-burned-account-user"); - var authId = Register(account, AddressX); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Rejected, - IncomingAccountPackets.ConsumeAuthId(authId, "not-the-owner", AddressX, out _) - ); - - Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Vouched, - IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out _) - ); - } - - [Fact] - public void ARejectedAttemptYieldsNoEntry() - { - var account = CreateAccount("authid-no-leak-user"); - var authId = Register(account, AddressX); - - IncomingAccountPackets.ConsumeAuthId(authId, "not-the-owner", AddressX, out var entry); - - Assert.Null(entry.Account); - } - - [Fact] - public void AnExpiredIdIsSpentByItsOwner() - { - var account = CreateAccount("authid-expired-spent-user"); - var authId = Register(account, AddressX); - - var now = Core._now; - - try - { - Core._now = now + TimeSpan.FromMinutes(30.0); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Expired, - IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out _) - ); - Assert.Equal(0, IncomingAccountPackets.AuthIdWindowCount); - } - finally - { - Core._now = now; - } - } - - // Expiry is not a lockout. The game login always verified the password before any of this - // existed, so falling back to that verify is the behaviour we started from. - [Fact] - public void ExpiresIntoAPasswordVerifyRatherThanARejection() - { - var account = CreateAccount("authid-expired-user"); - var authId = Register(account, AddressX); - - var now = Core._now; - - try - { - Core._now = now + TimeSpan.FromMinutes(30.0); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Expired, - IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressX, out var entry) - ); - - // Still carries the client version the game login needs. - Assert.Equal(new ClientVersion(7, 0, 0, 0), entry.Version); - } - finally - { - Core._now = now; - } - } - - [Fact] - public void AnExpiredIdFromAnotherAddressIsStillRejected() - { - var account = CreateAccount("authid-expired-elsewhere-user"); - var authId = Register(account, AddressX); - - var now = Core._now; - - try - { - Core._now = now + TimeSpan.FromMinutes(30.0); - - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Rejected, - IncomingAccountPackets.ConsumeAuthId(authId, account.Username, AddressY, out _) - ); - } - finally - { - Core._now = now; - } - } - - private static int Ensure(int existingAuthId, IAccount account, IPAddress address) => - IncomingAccountPackets.EnsureAuthId( - existingAuthId, - account, - address, - new ClientVersion(7, 0, 0, 0) - ); - - [Fact] - public void IssuesAnIdWhenTheConnectionHasNone() - { - var account = CreateAccount("authid-first-select-user"); - - var authId = Ensure(0, account, AddressX); - - Assert.NotEqual(0, authId); - Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); - } - - // Handing the same id back rather than minting another is what makes an orphan impossible, - // instead of something to clean up afterwards. - [Fact] - public void ReSelectingReturnsTheSameIdAndAddsNothingToTheWindow() - { - var account = CreateAccount("authid-reselect-user"); - var first = Ensure(0, account, AddressX); - - for (var i = 0; i < 10; i++) - { - Assert.Equal(first, Ensure(first, account, AddressX)); - } - - Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Vouched, - IncomingAccountPackets.ConsumeAuthId(first, account.Username, AddressX, out _) - ); - } - - [Fact] - public void AbandonedIdsAreSweptWhenNewOnesAreIssued() - { - var abandoned = CreateAccount("authid-abandoned-user"); - var live = CreateAccount("authid-live-user"); - - var now = Core._now; - - try - { - for (var i = 0; i < 128; i++) - { - Register(abandoned, AddressX); - } - - Assert.Equal(128, IncomingAccountPackets.AuthIdWindowCount); - - Core._now = now + TimeSpan.FromMinutes(30.0); - - var liveId = Register(live, AddressX); - - Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Vouched, - IncomingAccountPackets.ConsumeAuthId(liveId, live.Username, AddressX, out _) - ); - } - finally - { - Core._now = now; - } - } - - // A login rush is not a backlog. Every id belongs to a client on its way to redeem it, so none - // may be discarded to hold the window at some arbitrary size. - [Fact] - public void ALoginRushDoesNotEvictAnyonesAuthId() - { - var account = CreateAccount("authid-rush-user"); - var ids = new int[800]; - - for (var i = 0; i < ids.Length; i++) - { - ids[i] = Register(account, AddressX); - } - - Assert.Equal(ids.Length, IncomingAccountPackets.AuthIdWindowCount); - - // Every id issued during the rush is still redeemable, including the first one. - for (var i = 0; i < ids.Length; i++) - { - Assert.Equal( - IncomingAccountPackets.AuthIdResult.Vouched, - IncomingAccountPackets.ConsumeAuthId(ids[i], account.Username, AddressX, out _) - ); - } - } - - [Fact] - public void PreAuthenticatedGameLogin_SkipsThePasswordCheck() - { - var account = CreateAccount("authid-preauth-user"); - using var ns = PacketTestUtilities.CreateTestNetState(); - - // A wrong password is accepted only because the auth id already vouched for the account. - var e = new GameServer.GameLoginEventArgs(ns, account.Username, "wrong-password", true); - GameServer.GameServerLoginEvent(e); - - Assert.True(e.Accepted); - } - - [Fact] - public void GameLoginWithoutPreAuthentication_StillChecksThePassword() - { - var account = CreateAccount("authid-nopreauth-user"); - using var ns = PacketTestUtilities.CreateTestNetState(); - - var wrong = new GameServer.GameLoginEventArgs(ns, account.Username, "wrong-password", false); - GameServer.GameServerLoginEvent(wrong); - Assert.False(wrong.Accepted); - - var right = new GameServer.GameLoginEventArgs(ns, account.Username, "hunter2", false); - GameServer.GameServerLoginEvent(right); - Assert.True(right.Accepted); - } - - [Fact] - public void GeneratesDistinctAuthIds() - { - var account = CreateAccount("authid-distinct-user"); - - Assert.NotEqual(Register(account, AddressX), Register(account, AddressX)); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/Packets/BuffIconPacketTests.cs b/Projects/UOContent.Tests/Tests/Network/Packets/BuffIconPacketTests.cs index a815bd08b..71aa2fa10 100644 --- a/Projects/UOContent.Tests/Tests/Network/Packets/BuffIconPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/Packets/BuffIconPacketTests.cs @@ -1,6 +1,5 @@ using System; using Server; -using Server.Engines.BuffIcons; using Server.Network; using Server.Tests; using Server.Tests.Network; @@ -8,7 +7,6 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] public class BuffIconPacketTests { [Theory] @@ -16,29 +14,29 @@ public class BuffIconPacketTests [InlineData(0x2048u, BuffIcon.Disguised, 500102, 300203, null, 9000)] public void TestAddBuffIcon(uint mob, BuffIcon iconID, int titleCliloc, int secondaryCliloc, string args, int ts) { - var timeSpan = new TimeSpan(ts); - var expected = new AddBuffPacket( - (Serial)mob, iconID, titleCliloc, secondaryCliloc, args, timeSpan - ).Compile(); + var timeSpan = new TimeSpan(ts); + var expected = new AddBuffPacket( + (Serial)mob, iconID, titleCliloc, secondaryCliloc, args, timeSpan + ).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendAddBuffPacket((Serial)mob, iconID, titleCliloc, secondaryCliloc, args, (int)timeSpan.TotalMilliseconds); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendAddBuffPacket((Serial)mob, iconID, titleCliloc, secondaryCliloc, args, (int)timeSpan.TotalMilliseconds); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } + } [Fact] public void TestRemoveBuffIcon() { - var m = (Serial)0x1024; - var buffIcon = BuffIcon.Disguised; - var expected = new RemoveBuffPacket(m, buffIcon).Compile(); + Serial m = (Serial)0x1024; + var buffIcon = BuffIcon.Disguised; + var expected = new RemoveBuffPacket(m, buffIcon).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); - ns.SendRemoveBuffPacket(m, buffIcon); + var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendRemoveBuffPacket(m, buffIcon); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); - } -} + } +} \ No newline at end of file diff --git a/Projects/UOContent.Tests/Tests/Network/Packets/BuffIconPackets.cs b/Projects/UOContent.Tests/Tests/Network/Packets/BuffIconPackets.cs index 35a677ae0..208d15207 100644 --- a/Projects/UOContent.Tests/Tests/Network/Packets/BuffIconPackets.cs +++ b/Projects/UOContent.Tests/Tests/Network/Packets/BuffIconPackets.cs @@ -1,83 +1,85 @@ using System; -using Server.Engines.BuffIcons; -namespace Server.Network; - -public sealed class AddBuffPacket : Packet +namespace Server.Network { - public AddBuffPacket(Serial m, BuffInfo info) - : this( - m, - info.ID, - info.TitleCliloc, - info.SecondaryCliloc, - info.Args, - info.Duration + public sealed class AddBuffPacket : Packet + { + public AddBuffPacket(Serial m, BuffInfo info) + : this( + m, + info.ID, + info.TitleCliloc, + info.SecondaryCliloc, + info.Args, + info.TimeStart != 0 ? + TimeSpan.FromMilliseconds(info.TimeStart + (long)info.TimeLength.TotalMilliseconds - Core.TickCount) : + TimeSpan.Zero + ) + { + } + + public AddBuffPacket( + Serial mob, BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args, + TimeSpan length ) - { - } - - public AddBuffPacket( - Serial mob, BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args, - TimeSpan length - ) - : base(0xDF) - { - var hasArgs = args != null; - - EnsureCapacity(hasArgs ? 48 + args.ToString().Length * 2 : 44); - Stream.Write(mob); - - Stream.Write((short)iconID); // ID - Stream.Write((short)0x1); // Type 0 for removal. 1 for add 2 for Data - - Stream.Fill(4); - - Stream.Write((short)iconID); // ID - Stream.Write((short)0x01); // Type 0 for removal. 1 for add 2 for Data - - Stream.Fill(4); - - Stream.Write((short)Math.Max(length.TotalSeconds, 0)); // Time in seconds - - Stream.Fill(3); - Stream.Write(titleCliloc); - Stream.Write(secondaryCliloc); - - if (!hasArgs) - { - // m_Stream.Fill( 2 ); - Stream.Fill(10); - } - else + : base(0xDF) { + var hasArgs = args != null; + + EnsureCapacity(hasArgs ? 48 + args.ToString().Length * 2 : 44); + Stream.Write(mob); + + Stream.Write((short)iconID); // ID + Stream.Write((short)0x1); // Type 0 for removal. 1 for add 2 for Data + Stream.Fill(4); - Stream.Write((short)0x1); // Unknown -> Possibly something saying 'hey, I have more data!'? - Stream.Fill(2); - // m_Stream.WriteLittleUniNull( "\t#1018280" ); - Stream.WriteLittleUniNull($"\t{args}"); + Stream.Write((short)iconID); // ID + Stream.Write((short)0x01); // Type 0 for removal. 1 for add 2 for Data - Stream.Write((short)0x1); // Even more Unknown -> Possibly something saying 'hey, I have more data!'? - Stream.Fill(2); + Stream.Fill(4); + + Stream.Write((short)Math.Max(length.TotalSeconds, 0)); // Time in seconds + + Stream.Fill(3); + Stream.Write(titleCliloc); + Stream.Write(secondaryCliloc); + + if (!hasArgs) + { + // m_Stream.Fill( 2 ); + Stream.Fill(10); + } + else + { + Stream.Fill(4); + Stream.Write((short)0x1); // Unknown -> Possibly something saying 'hey, I have more data!'? + Stream.Fill(2); + + // m_Stream.WriteLittleUniNull( "\t#1018280" ); + Stream.WriteLittleUniNull($"\t{args}"); + + Stream.Write((short)0x1); // Even more Unknown -> Possibly something saying 'hey, I have more data!'? + Stream.Fill(2); + } + } + } + + public sealed class RemoveBuffPacket : Packet + { + public RemoveBuffPacket(Serial mob, BuffInfo info) : this(mob, info.ID) + { + } + + public RemoveBuffPacket(Serial mob, BuffIcon iconID) : base(0xDF) + { + EnsureCapacity(13); + Stream.Write(mob); + + Stream.Write((short)iconID); // ID + Stream.Write((short)0x0); // Type 0 for removal. 1 for add 2 for Data + + Stream.Fill(4); } } } - -public sealed class RemoveBuffPacket : Packet -{ - public RemoveBuffPacket(Serial mob, BuffInfo info) : this(mob, info.ID) - { - } - - public RemoveBuffPacket(Serial mob, BuffIcon iconID) : base(0xDF) - { - EnsureCapacity(13); - Stream.Write(mob); - - Stream.Write((short)iconID); // ID - Stream.Write((short)0x0); // Type 0 for removal. 1 for add 2 for Data - - Stream.Fill(4); - } -} diff --git a/Projects/UOContent.Tests/Tests/Skills/DetectHiddenTests.cs b/Projects/UOContent.Tests/Tests/Skills/DetectHiddenTests.cs deleted file mode 100644 index bc416d148..000000000 --- a/Projects/UOContent.Tests/Tests/Skills/DetectHiddenTests.cs +++ /dev/null @@ -1,213 +0,0 @@ -using Server; -using Server.Mobiles; -using Server.SkillHandlers; -using Server.Tests; -using Xunit; - -namespace UOContent.Tests; - -[Collection("Sequential UOContent Tests")] -public class DetectHiddenTests -{ - // All coordinates use Y=500 to stay well within Felucca's 7168x4096 boundary. - // X values are spread 200 tiles apart to avoid overlap with each other and - // with the Tracking tests that use coordinates around (1000-4000, 1000-4000). - - /// - /// A detector with higher Detect Hidden than the stealther's Hiding should reveal them. - /// With PredictableRandom both rolls get the same offset, so detection succeeds - /// when detectSkill >= hiding. - /// - [Fact] - public void TryDetectStealther_Reveals_WhenDetectSkillExceedsHiding() - { - using var rng = new PredictableRandom(10); - DetectHidden.ClearDebounceCache(); - var map = Map.Felucca; - var detector = CreatePlayerMobile(map, new Point3D(1000, 500, 0)); - var stealther = CreatePlayerMobile(map, new Point3D(1001, 500, 0)); - - try - { - detector.Skills.DetectHidden.BaseFixedPoint = 600; // 60.0 - stealther.Skills.Hiding.BaseFixedPoint = 500; // 50.0 - stealther.Hidden = true; - - var result = DetectHidden.TryDetectStealther(detector, stealther); - - Assert.True(result); - Assert.False(stealther.Hidden); - } - finally - { - detector.Delete(); - stealther.Delete(); - DetectHidden.ClearDebounceCache(); - } - } - - /// - /// When detect skill exactly equals hiding, detection succeeds (ss >= ts). - /// - [Fact] - public void TryDetectStealther_Reveals_WhenSkillsAreEqual() - { - using var rng = new PredictableRandom(10); - DetectHidden.ClearDebounceCache(); - var map = Map.Felucca; - var detector = CreatePlayerMobile(map, new Point3D(1200, 500, 0)); - var stealther = CreatePlayerMobile(map, new Point3D(1201, 500, 0)); - - try - { - detector.Skills.DetectHidden.BaseFixedPoint = 500; // 50.0 - stealther.Skills.Hiding.BaseFixedPoint = 500; // 50.0 - stealther.Hidden = true; - - var result = DetectHidden.TryDetectStealther(detector, stealther); - - Assert.True(result); - Assert.False(stealther.Hidden); - } - finally - { - detector.Delete(); - stealther.Delete(); - DetectHidden.ClearDebounceCache(); - } - } - - /// - /// When hiding skill exceeds detect skill, detection fails. - /// - [Fact] - public void TryDetectStealther_DoesNotReveal_WhenHidingExceedsDetectSkill() - { - using var rng = new PredictableRandom(10); - DetectHidden.ClearDebounceCache(); - var map = Map.Felucca; - var detector = CreatePlayerMobile(map, new Point3D(1400, 500, 0)); - var stealther = CreatePlayerMobile(map, new Point3D(1401, 500, 0)); - - try - { - detector.Skills.DetectHidden.BaseFixedPoint = 500; // 50.0 - stealther.Skills.Hiding.BaseFixedPoint = 600; // 60.0 - stealther.Hidden = true; - - var result = DetectHidden.TryDetectStealther(detector, stealther); - - Assert.False(result); - Assert.True(stealther.Hidden); - } - finally - { - detector.Delete(); - stealther.Delete(); - DetectHidden.ClearDebounceCache(); - } - } - - /// - /// A detector with zero Detect Hidden skill should never passively detect anyone. - /// Uses Elf race since Humans get a 20.0 racial bonus (Jack of All Trades). - /// - [Fact] - public void TryDetectStealther_DoesNotReveal_WhenDetectorHasNoSkill() - { - using var rng = new PredictableRandom(10); - DetectHidden.ClearDebounceCache(); - var map = Map.Felucca; - var detector = CreatePlayerMobile(map, new Point3D(1600, 500, 0)); - var stealther = CreatePlayerMobile(map, new Point3D(1601, 500, 0)); - - try - { - detector.Race = Race.Elf; - detector.Skills.DetectHidden.BaseFixedPoint = 0; // 0.0 - stealther.Skills.Hiding.BaseFixedPoint = 0; // 0.0 - stealther.Hidden = true; - - var result = DetectHidden.TryDetectStealther(detector, stealther); - - Assert.False(result); - Assert.True(stealther.Hidden); - } - finally - { - detector.Delete(); - stealther.Delete(); - DetectHidden.ClearDebounceCache(); - } - } - - /// - /// TryDetectStealther is a no-op when the target is not actually hidden. - /// - [Fact] - public void TryDetectStealther_DoesNothing_WhenStealtherIsNotHidden() - { - using var rng = new PredictableRandom(10); - DetectHidden.ClearDebounceCache(); - var map = Map.Felucca; - var detector = CreatePlayerMobile(map, new Point3D(1800, 500, 0)); - var stealther = CreatePlayerMobile(map, new Point3D(1801, 500, 0)); - - try - { - detector.Skills.DetectHidden.BaseFixedPoint = 1000; - stealther.Hidden = false; - - var result = DetectHidden.TryDetectStealther(detector, stealther); - - Assert.False(result); - Assert.False(stealther.Hidden); - } - finally - { - detector.Delete(); - stealther.Delete(); - DetectHidden.ClearDebounceCache(); - } - } - - /// - /// Passive detection only works on Felucca. A stealther on Trammel - /// should never be revealed by passive detection. - /// - [Fact] - public void TryDetectStealther_DoesNotReveal_WhenNotOnFelucca() - { - using var rng = new PredictableRandom(10); - DetectHidden.ClearDebounceCache(); - var map = Map.Trammel; - var detector = CreatePlayerMobile(map, new Point3D(2000, 500, 0)); - var stealther = CreatePlayerMobile(map, new Point3D(2001, 500, 0)); - - try - { - detector.Skills.DetectHidden.BaseFixedPoint = 1000; // 100.0 - stealther.Skills.Hiding.BaseFixedPoint = 0; // 0.0 - stealther.Hidden = true; - - var result = DetectHidden.TryDetectStealther(detector, stealther); - - Assert.False(result); - Assert.True(stealther.Hidden); - } - finally - { - detector.Delete(); - stealther.Delete(); - DetectHidden.ClearDebounceCache(); - } - } - - private static PlayerMobile CreatePlayerMobile(Map map, Point3D location) - { - var mobile = new PlayerMobile(World.NewMobile); - mobile.DefaultMobileInit(); - mobile.MoveToWorld(location, map); - return mobile; - } -} diff --git a/Projects/UOContent.Tests/Tests/Skills/SkillPacketsTests.cs b/Projects/UOContent.Tests/Tests/Skills/SkillPacketsTests.cs index 8dd4e2d25..764e25bb6 100644 --- a/Projects/UOContent.Tests/Tests/Skills/SkillPacketsTests.cs +++ b/Projects/UOContent.Tests/Tests/Skills/SkillPacketsTests.cs @@ -6,8 +6,7 @@ using Xunit; namespace UOContent.Tests; -[Collection("Sequential UOContent Tests")] -public class SkillPacketsTests +public class SkillPacketsTests : IClassFixture { [Theory] [InlineData(SkillName.Alchemy, 0, 1)] @@ -24,10 +23,10 @@ public class SkillPacketsTests var expected = new SkillChange(skill).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendSkillChange(skill); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } @@ -42,10 +41,10 @@ public class SkillPacketsTests var expected = new SkillUpdate(skills).Compile(); - using var ns = PacketTestUtilities.CreateTestNetState(); + var ns = PacketTestUtilities.CreateTestNetState(); ns.SendSkillsUpdate(skills); - var result = ns.SendBuffer.GetReadSpan(); + var result = ns.SendPipe.Reader.AvailableToRead(); AssertThat.Equal(result, expected); } } diff --git a/Projects/UOContent.Tests/Tests/Skills/Tracking/TrackingGumpTests.cs b/Projects/UOContent.Tests/Tests/Skills/Tracking/TrackingGumpTests.cs new file mode 100644 index 000000000..c55cf740c --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Skills/Tracking/TrackingGumpTests.cs @@ -0,0 +1,28 @@ +using Server.Gumps; +using Server.Mobiles; +using Server.SkillHandlers; +using Server.Tests; +using Server.Tests.Network; +using Xunit; + +namespace UOContent.Tests; + +public class TrackingGumpTests : IClassFixture +{ + [Fact] + // Regression test used to identify an issue with AddItem compilation of the packet + public void TestTrackingGump() + { + var pm = new PlayerMobile(); + pm.Skills.Tracking.BaseFixedPoint = 1000; + var g = new TrackWhatGump(pm); + + var ns = PacketTestUtilities.CreateTestNetState(); + + var expected = g.Compile(ns).Compile(); + ns.SendGump(g); + + var result = ns.SendPipe.Reader.AvailableToRead(); + AssertThat.Equal(result, expected); + } +} diff --git a/Projects/UOContent.Tests/Tests/Skills/TrackingTests.cs b/Projects/UOContent.Tests/Tests/Skills/TrackingTests.cs deleted file mode 100644 index 1c436fd49..000000000 --- a/Projects/UOContent.Tests/Tests/Skills/TrackingTests.cs +++ /dev/null @@ -1,299 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Server; -using Server.Mobiles; -using Server.SkillHandlers; -using Xunit; - -namespace UOContent.Tests; - -[Collection("Sequential UOContent Tests")] -public class TrackingTests -{ - /// - /// Tests that tracking correctly finds the closest mobiles when there are more than 12 available. - /// This validates the GetClosestMobs logic, especially the early exit optimization. - /// - [Fact] - public void Tracking_FindsClosestMobiles_WhenManyAvailable() - { - var map = Map.Felucca; - var center = new Point3D(1000, 1000, 0); - - var tracker = CreatePlayerMobile(map, center); - tracker.Skills.Tracking.BaseFixedPoint = 1000; // 100.0 skill = 110 range - - var mobiles = new List(); - - try - { - // Create 20 animals at various distances - // First 12 should be the closest ones we find - for (var i = 0; i < 20; i++) - { - var distance = i + 1; // Distance from 1 to 20 - var location = new Point3D(center.X + distance, center.Y, 0); - var animal = CreateAnimal(map, location); - mobiles.Add(animal); - } - - // Invoke tracking through the skill system - // We can't directly test GetClosestMobs since it's private, but we can verify - // the behavior by checking what the gump would show - - // Use reflection to test the private method - var method = typeof(TrackWhoGump).GetMethod( - "GetClosestMobs", - System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static - ); - - Assert.NotNull(method); - - var range = Math.Clamp(10 + (int)tracker.Skills.Tracking.Value, 0, 100); - var result = (Mobile[])method.Invoke(null, new object[] { tracker, range, 0 }); // 0 = animals - - // Should return at most 12 mobiles - Assert.True(result.Length <= 12); - - // Should return the 12 closest (distances 1-12) - Assert.Equal(12, result.Length); - - // Verify they are sorted by distance - for (var i = 0; i < result.Length - 1; i++) - { - var dist1 = result[i].GetDistanceToSqrt(center); - var dist2 = result[i + 1].GetDistanceToSqrt(center); - Assert.True(dist1 <= dist2, $"Mobiles not sorted: {dist1} > {dist2}"); - } - - // Verify the first mobile is the closest (distance 1) - Assert.Equal(mobiles[0], result[0]); - - // Verify the last mobile is at distance 12 - Assert.Equal(mobiles[11], result[11]); - - // Verify mobile at distance 13 is NOT included - Assert.DoesNotContain(mobiles[12], result); - } - finally - { - tracker?.Delete(); - foreach (var mob in mobiles) - { - mob?.Delete(); - } - } - } - - /// - /// Tests that tracking correctly handles the case where there are exactly 12 mobiles available. - /// - [Fact] - public void Tracking_FindsAllMobiles_WhenExactly12Available() - { - var map = Map.Felucca; - var center = new Point3D(2000, 2000, 0); - - var tracker = CreatePlayerMobile(map, center); - tracker.Skills.Tracking.BaseFixedPoint = 1000; // 100.0 skill = 110 range - - var mobiles = new List(); - - try - { - // Create exactly 12 animals - for (var i = 0; i < 12; i++) - { - var distance = i + 1; - var location = new Point3D(center.X + distance, center.Y, 0); - var animal = CreateAnimal(map, location); - mobiles.Add(animal); - } - - var method = typeof(TrackWhoGump).GetMethod( - "GetClosestMobs", - System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static - ); - - var range = Math.Clamp(10 + (int)tracker.Skills.Tracking.Value, 0, 100); - var result = (Mobile[])method.Invoke(null, new object[] { tracker, range, 0 }); - - Assert.Equal(12, result.Length); - - // Verify all mobiles are included - foreach (var mob in mobiles) - { - Assert.Contains(mob, result); - } - } - finally - { - tracker?.Delete(); - foreach (var mob in mobiles) - { - mob?.Delete(); - } - } - } - - /// - /// Tests that tracking correctly handles the case where there are fewer than 12 mobiles available. - /// - [Fact] - public void Tracking_FindsAllMobiles_WhenFewerThan12Available() - { - var map = Map.Felucca; - var center = new Point3D(3000, 3000, 0); - - var tracker = CreatePlayerMobile(map, center); - tracker.Skills.Tracking.BaseFixedPoint = 1000; - - var mobiles = new List(); - - try - { - // Create only 5 animals - for (var i = 0; i < 5; i++) - { - var distance = i + 1; - var location = new Point3D(center.X + distance, center.Y, 0); - var animal = CreateAnimal(map, location); - mobiles.Add(animal); - } - - var method = typeof(TrackWhoGump).GetMethod( - "GetClosestMobs", - System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static - ); - - var range = Math.Clamp(10 + (int)tracker.Skills.Tracking.Value, 0, 100); - var result = (Mobile[])method.Invoke(null, new object[] { tracker, range, 0 }); - - Assert.Equal(5, result.Length); - - // Verify all mobiles are included - foreach (var mob in mobiles) - { - Assert.Contains(mob, result); - } - } - finally - { - tracker?.Delete(); - foreach (var mob in mobiles) - { - mob?.Delete(); - } - } - } - - /// - /// Tests that the early exit optimization works correctly when mobiles in farther sectors - /// are closer than mobiles in nearer sectors (worst case for the optimization). - /// - [Fact] - public void Tracking_EarlyExitWorksCorrectly_WithFarSectorNearMobiles() - { - var map = Map.Felucca; - // Use a location that puts mobiles in different sectors - var center = new Point3D(1500, 1500, 0); - - var tracker = CreatePlayerMobile(map, center); - tracker.Skills.Tracking.BaseFixedPoint = 1000; - - var mobiles = new List(); - - try - { - // Create 15 animals where some farther ones might be in closer proximity - // but in different sectors - for (var i = 0; i < 15; i++) - { - var distance = i + 1; - // Alternate between X and Y to potentially cross sector boundaries - var location = i % 2 == 0 - ? new Point3D(center.X + distance, center.Y, 0) - : new Point3D(center.X, center.Y + distance, 0); - var animal = CreateAnimal(map, location); - mobiles.Add(animal); - } - - var method = typeof(TrackWhoGump).GetMethod( - "GetClosestMobs", - System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static - ); - - var range = Math.Clamp(10 + (int)tracker.Skills.Tracking.Value, 0, 100); - var result = (Mobile[])method.Invoke(null, new object[] { tracker, range, 0 }); - - Assert.True(result.Length <= 12); - - // Get the actual 12 closest by brute force - var allDistances = mobiles - .Select(m => (Mobile: m, Distance: m.GetDistanceToSqrt(center))) - .OrderBy(x => x.Distance) - .ToList(); - - var expected12Closest = allDistances.Take(12).ToList(); - - // The result should match the actual 12 closest - Assert.Equal(expected12Closest.Count, result.Length); - - // Verify all returned mobiles are in the expected 12 closest - foreach (var mob in result) - { - Assert.Contains(mob, expected12Closest.Select(x => x.Mobile)); - } - - // Verify they are sorted by distance (main invariant) - for (var i = 0; i < result.Length - 1; i++) - { - var dist1 = result[i].GetDistanceToSqrt(center); - var dist2 = result[i + 1].GetDistanceToSqrt(center); - Assert.True(dist1 <= dist2, $"Mobiles not sorted by distance: {dist1} > {dist2}"); - } - - // Verify we got the closest mobiles (not just any 12) - var maxResultDistance = result.Max(m => m.GetDistanceToSqrt(center)); - var minExcludedDistance = allDistances.Skip(12).Any() - ? allDistances.Skip(12).Min(x => x.Distance) - : double.MaxValue; - Assert.True(maxResultDistance <= minExcludedDistance, - $"Found a closer excluded mobile: max in result={maxResultDistance}, min excluded={minExcludedDistance}"); - } - finally - { - tracker?.Delete(); - foreach (var mob in mobiles) - { - mob?.Delete(); - } - } - } - - private static PlayerMobile CreatePlayerMobile(Map map, Point3D location) - { - var mobile = new PlayerMobile(World.NewMobile); - mobile.DefaultMobileInit(); - mobile.MoveToWorld(location, map); - return mobile; - } - - private static TestAnimal CreateAnimal(Map map, Point3D location) - { - var animal = new TestAnimal(World.NewMobile); - animal.DefaultMobileInit(); - animal.MoveToWorld(location, map); - return animal; - } - - private class TestAnimal : BaseCreature - { - public TestAnimal(Serial serial) : base(serial) - { - Body = 0xD8; // Llama body - an animal body - } - } -} - diff --git a/Projects/UOContent.Tests/Tests/Spells/Necromancy/BloodOathSpellTests.cs b/Projects/UOContent.Tests/Tests/Spells/Necromancy/BloodOathSpellTests.cs deleted file mode 100644 index 0264a282e..000000000 --- a/Projects/UOContent.Tests/Tests/Spells/Necromancy/BloodOathSpellTests.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using Server.Mobiles; -using Server.Spells.Necromancy; -using Xunit; - -namespace Server.Tests.Spells.Necromancy; - -[Collection("Sequential UOContent Tests")] -public class BloodOathSpellTests -{ - private static Mobile NewMobile() - { - var m = new Mobile(World.NewMobile); - m.DefaultMobileInit(); - return m; - } - - // Issue #1690: the real OSI duration is ((SpiritSpeak - Resist) / 8) + 8 seconds. - // ModernUO previously used /80 (matching the bugged in-game tooltip), which made - // Spirit Speak almost irrelevant to duration. RunUO/ServUO and the code's own - // fixed-point comment both use /8. - [Theory] - [InlineData(120.0, 0.0, 23.0)] // GM Spirit Speak, no resist - [InlineData(120.0, 120.0, 8.0)] // equal skills -> baseline 8s - [InlineData(100.0, 20.0, 18.0)] // (100-20)/8 + 8 - [InlineData(20.0, 0.0, 10.5)] // minimum skill - public void GetDurationSeconds_UsesDivideByEight(double ss, double resist, double expected) - { - Assert.Equal(expected, BloodOathSpell.GetDurationSeconds(ss, resist), 3); - } - - // Player caster: Publish 48 resist mitigation does NOT apply; the attacker takes the - // full reflected (original, un-bonused) damage. - [Fact] - public void ComputeReflectedDamage_NoMitigation_ReturnsOriginal() - { - Assert.Equal(40, BloodOathSpell.ComputeReflectedDamage(40, 120.0, applyResistMitigation: false)); - } - - // Creature caster (Publish 48 / SA+): ((Resist * 10) / 20) + 10 = % of reflected damage resisted. - [Theory] - [InlineData(40, 0.0, 36)] // 10% resisted -> 40 * 0.90 - [InlineData(40, 100.0, 16)] // 60% resisted -> 40 * 0.40 - [InlineData(40, 120.0, 12)] // 70% resisted -> 40 * 0.30 - public void ComputeReflectedDamage_WithMitigation_ReducesByResist(int dmg, double resist, int expected) - { - Assert.Equal(expected, BloodOathSpell.ComputeReflectedDamage(dmg, resist, applyResistMitigation: true)); - } - - // The cursed target reflects to the caster; the caster attacking other mobiles must not reflect. - [Fact] - public void RegisterOath_BindsTargetToCaster_NotCasterToSelf() - { - var caster = NewMobile(); - var target = NewMobile(); - - BloodOathSpell.RegisterOath(caster, target, TimeSpan.FromMinutes(5)); - - Assert.Equal(caster, BloodOathSpell.GetBloodOath(target)); - Assert.Null(BloodOathSpell.GetBloodOath(caster)); - - BloodOathSpell.RemoveCurse(target); - caster.Delete(); - target.Delete(); - } - - // Death/delete of either party removes the oath, so RemoveCurse must resolve from either - // the caster or the target key (the death hooks call it with `this`). - [Fact] - public void RemoveCurse_ByCaster_ClearsBothEntries() - { - var caster = NewMobile(); - var target = NewMobile(); - BloodOathSpell.RegisterOath(caster, target, TimeSpan.FromMinutes(5)); - - Assert.True(BloodOathSpell.RemoveCurse(caster)); - - Assert.Null(BloodOathSpell.GetBloodOath(target)); - Assert.False(BloodOathSpell.RemoveCurse(target)); // already removed - - caster.Delete(); - target.Delete(); - } - - [Fact] - public void RemoveCurse_NotCursed_ReturnsFalse() - { - var m = NewMobile(); - Assert.False(BloodOathSpell.RemoveCurse(m)); - m.Delete(); - } - - // Issue #1690: death/delete of either party must break the oath immediately. The oath is wired - // to the central PlayerMobile/BaseCreature death+delete events instead of a polling timer. - [Fact] - public void PlayerDeletedEvent_BreaksOath() - { - var caster = new PlayerMobile(World.NewMobile); - caster.DefaultMobileInit(); - var target = new PlayerMobile(World.NewMobile); - target.DefaultMobileInit(); - - BloodOathSpell.RegisterOath(caster, target, TimeSpan.FromMinutes(5)); - - PlayerMobile.PlayerDeletedEvent(caster); // central handler breaks the oath from the caster side - - Assert.Null(BloodOathSpell.GetBloodOath(target)); - Assert.False(BloodOathSpell.RemoveCurse(target)); - - caster.Delete(); - target.Delete(); - } - - [Fact] - public void CreatureDeletedEvent_BreaksOath() - { - var caster = new PlayerMobile(World.NewMobile); - caster.DefaultMobileInit(); - var target = new TestCreature(World.NewMobile); - target.DefaultMobileInit(); - - BloodOathSpell.RegisterOath(caster, target, TimeSpan.FromMinutes(5)); - - BaseCreature.CreatureDeletedEvent(target); // central handler breaks the oath from the target side - - Assert.Null(BloodOathSpell.GetBloodOath(target)); - Assert.False(BloodOathSpell.RemoveCurse(caster)); - - caster.Delete(); - target.Delete(); - } - - private class TestCreature : BaseCreature - { - // Serial ctor skips AI/speed-table setup, which the test fixture does not configure. - public TestCreature(Serial serial) : base(serial) - { - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Spells/Ninjitsu/AnimalFormTests.cs b/Projects/UOContent.Tests/Tests/Spells/Ninjitsu/AnimalFormTests.cs deleted file mode 100644 index 34a665495..000000000 --- a/Projects/UOContent.Tests/Tests/Spells/Ninjitsu/AnimalFormTests.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using Server.Mobiles; -using Server.Spells.Ninjitsu; -using Xunit; - -namespace Server.Tests.Spells.Ninjitsu; - -[Collection("Sequential UOContent Tests")] -public class AnimalFormTests -{ - private static int IndexOf(Type type) => Array.FindIndex(AnimalForm.Entries, e => e.Type == type); - - private static Mobile NewMobileWithNinjitsu(double skill) - { - var m = new Mobile(World.NewMobile); - m.DefaultMobileInit(); - m.Skills.Ninjitsu.Base = skill; - return m; - } - - // Issue #2452: with only 30 Ninjitsu the gump showed every form (including Dog, which needs 40) - // because the selectable check compared Skill.Fixed (value x10) against the raw ReqSkill. - [Fact] - public void CanSelectEntry_FormAboveSkill_IsNotSelectable() - { - var m = NewMobileWithNinjitsu(30.0); - - Assert.False(AnimalForm.CanSelectEntry(m, AnimalForm.Entries[IndexOf(typeof(Dog))])); // ReqSkill 40 - Assert.False(AnimalForm.CanSelectEntry(m, AnimalForm.Entries[IndexOf(typeof(Cat))])); // ReqSkill 40 - - m.Delete(); - } - - [Fact] - public void CanSelectEntry_FormAtOrBelowSkill_IsSelectable() - { - var m = NewMobileWithNinjitsu(40.0); - - Assert.True(AnimalForm.CanSelectEntry(m, AnimalForm.Entries[IndexOf(typeof(Dog))])); // ReqSkill 40 (== boundary) - Assert.True(AnimalForm.CanSelectEntry(m, AnimalForm.Entries[IndexOf(typeof(Rat))])); // ReqSkill 20 - Assert.True(AnimalForm.CanSelectEntry(m, AnimalForm.Entries[IndexOf(typeof(Rabbit))])); // ReqSkill 20 - - m.Delete(); - } - - // Talisman-gated forms are never selectable without the matching talisman, regardless of skill. - [Fact] - public void CanSelectEntry_TalismanGatedForm_RequiresTalisman() - { - var m = NewMobileWithNinjitsu(120.0); - - Assert.False(AnimalForm.CanSelectEntry(m, AnimalForm.Entries[IndexOf(typeof(Squirrel))])); - Assert.False(AnimalForm.CanSelectEntry(m, AnimalForm.Entries[IndexOf(typeof(Ferret))])); - - m.Delete(); - } - - // Issue #2452: attempting a form above your skill must report NoSkill (so the caller can - // refrain from charging mana) and must not actually transform the caster. - [Fact] - public void Morph_InsufficientSkill_ReturnsNoSkillAndDoesNotTransform() - { - var m = NewMobileWithNinjitsu(30.0); - - var result = AnimalForm.Morph(m, IndexOf(typeof(Dog))); // needs 40 - - Assert.Equal(AnimalForm.MorphResult.NoSkill, result); - Assert.Null(AnimalForm.GetContext(m)); - Assert.Equal(0, (int)m.BodyMod); - - m.Delete(); - } - - [Fact] - public void Morph_SufficientSkill_TransformsCaster() - { - // >= ReqSkill + 37.5 guarantees the success-chance roll is skipped. - var m = NewMobileWithNinjitsu(60.0); - - var result = AnimalForm.Morph(m, IndexOf(typeof(Rat))); // needs 20 - - Assert.Equal(AnimalForm.MorphResult.Success, result); - Assert.NotNull(AnimalForm.GetContext(m)); - - AnimalForm.RemoveContext(m); // stop the form timer / clear state - m.Delete(); - } -} diff --git a/Projects/UOContent.Tests/Tests/Utilities/TryParseTests.cs b/Projects/UOContent.Tests/Tests/Utilities/TryParseTests.cs index 314290264..518146d27 100644 --- a/Projects/UOContent.Tests/Tests/Utilities/TryParseTests.cs +++ b/Projects/UOContent.Tests/Tests/Utilities/TryParseTests.cs @@ -1,4 +1,3 @@ -using System; using Xunit; namespace Server.Tests.Utility; @@ -11,7 +10,7 @@ public class TryParseTests [InlineData("Alakazam", "Not a valid boolean string.", true)] public void TestTryParseBool(string value, string returned, bool parsedAs) { - var actualReturned = Server.Types.TryParse(typeof(bool), value, out var constructed); + string actualReturned = Server.Types.TryParse(typeof(bool), value, out object constructed); Assert.Equal(returned, actualReturned); if (returned == null) @@ -19,29 +18,4 @@ public class TryParseTests Assert.Equal(parsedAs, constructed); } } - - [Theory] - // Parsed directly into the target type (INumber.TryParse), not via ulong + Convert.ChangeType. - [InlineData(typeof(int), "42", true, 42)] - [InlineData(typeof(int), "-5", true, -5)] // signed values parse directly now - [InlineData(typeof(int), "0xFF", true, 255)] // hex - [InlineData(typeof(int), "notanumber", false, null)] - [InlineData(typeof(byte), "255", true, (byte)255)] - [InlineData(typeof(byte), "256", false, null)] // out of the byte range - [InlineData(typeof(uint), "4294967295", true, 4294967295u)] - [InlineData(typeof(long), "-9000000000", true, -9000000000L)] - public void TestTryParseNumeric(Type type, string value, bool success, object expected) - { - var error = Server.Types.TryParse(type, value, out var constructed); - - if (success) - { - Assert.Null(error); - Assert.Equal(expected, constructed); - } - else - { - Assert.NotNull(error); - } - } } diff --git a/Projects/UOContent.Tests/Tests/WorldSaves/ArchiveJournalTests.cs b/Projects/UOContent.Tests/Tests/WorldSaves/ArchiveJournalTests.cs deleted file mode 100644 index ede98a27f..000000000 --- a/Projects/UOContent.Tests/Tests/WorldSaves/ArchiveJournalTests.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Server.Saves; -using Xunit; - -namespace Server.Tests; - -[Collection("Sequential UOContent Tests")] -public class ArchiveJournalTests : IDisposable -{ - private readonly string _testDir; - - public ArchiveJournalTests() - { - _testDir = Path.Combine(Path.GetTempPath(), $"modernuo-journal-test-{Guid.NewGuid():N}"); - Directory.CreateDirectory(_testDir); - } - - public void Dispose() - { - if (Directory.Exists(_testDir)) - { - Directory.Delete(_testDir, true); - } - } - - [Fact] - public void Configure_LoadsCleanState() - { - // Arrange & Act - ArchiveJournal.Configure(_testDir); - - // Assert - fresh directory should have no operations - // Note: ArchiveJournal is static, so we check the journal file doesn't exist - var journalPath = Path.Combine(_testDir, ".archive-journal.json"); - Assert.False(File.Exists(journalPath)); - } - - [Fact] - public void BeginOperation_CreatesStartedEntry() - { - // Arrange - ArchiveJournal.Configure(_testDir); - - // Act - var entry = ArchiveJournal.BeginOperation( - ArchivePeriod.Hourly, - new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc), - "/tmp/archive.tar.zst.tmp", - "/archives/hourly/2026-03-22-14.tar.zst", - ["backup1", "backup2"] - ); - - // Assert - Assert.Equal(ArchiveOperationState.Started, entry.State); - Assert.Equal(ArchivePeriod.Hourly, entry.Period); - Assert.Equal("/tmp/archive.tar.zst.tmp", entry.TempFile); - Assert.Equal(2, entry.SourceDirectories.Count); - Assert.Contains(entry, ArchiveJournal.Operations); - } - - [Fact] - public void RecordArchived_TransitionsState() - { - // Arrange - ArchiveJournal.Configure(_testDir); - var entry = ArchiveJournal.BeginOperation( - ArchivePeriod.Daily, - new DateTime(2026, 3, 22, 0, 0, 0, DateTimeKind.Utc), - "/tmp/test.tmp", - "/archives/daily/2026-03-22.tar.zst", - ["backup1"] - ); - - // Act - ArchiveJournal.RecordArchived(entry, 42, 1024000); - - // Assert - Assert.Equal(ArchiveOperationState.Archived, entry.State); - Assert.Equal(42, entry.EntryCount); - Assert.Equal(1024000, entry.ArchiveBytes); - Assert.Null(entry.TempFile); // Temp file cleared after rename - } - - [Fact] - public void RecordDistributed_StoresResults() - { - // Arrange - ArchiveJournal.Configure(_testDir); - var entry = ArchiveJournal.BeginOperation( - ArchivePeriod.Monthly, - new DateTime(2026, 3, 1, 0, 0, 0, DateTimeKind.Utc), - "/tmp/test.tmp", - "/archives/monthly/2026-03.tar.zst", - ["backup1"] - ); - ArchiveJournal.RecordArchived(entry, 100, 5000000); - - var results = new Dictionary - { - { "Local Filesystem", true }, - { "S3 us-east-1", false } - }; - - // Act - ArchiveJournal.RecordDistributed(entry, results); - - // Assert - Assert.Equal(ArchiveOperationState.Distributed, entry.State); - Assert.True(entry.DestinationResults["Local Filesystem"]); - Assert.False(entry.DestinationResults["S3 us-east-1"]); - } - - [Fact] - public void RecordCompleted_IsTerminal() - { - // Arrange - ArchiveJournal.Configure(_testDir); - var entry = ArchiveJournal.BeginOperation( - ArchivePeriod.Hourly, - new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc), - "/tmp/test.tmp", - "/archives/hourly/test.tar.zst", - ["backup1"] - ); - ArchiveJournal.RecordArchived(entry, 10, 500); - ArchiveJournal.RecordDistributed(entry, new Dictionary()); - - // Act - ArchiveJournal.RecordCompleted(entry); - - // Assert - Assert.Equal(ArchiveOperationState.Completed, entry.State); - Assert.NotEqual(DateTime.MaxValue, entry.CompletedAt); - } - - [Fact] - public void RecordFailure_RecordsReason() - { - // Arrange - ArchiveJournal.Configure(_testDir); - var entry = ArchiveJournal.BeginOperation( - ArchivePeriod.Hourly, - new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc), - "/tmp/test.tmp", - "/archives/hourly/test.tar.zst", - ["backup1"] - ); - - // Act - ArchiveJournal.RecordFailure(entry, "Disk full"); - - // Assert - Assert.Equal(ArchiveOperationState.Failed, entry.State); - Assert.Equal("Disk full", entry.FailureReason); - Assert.NotEqual(DateTime.MaxValue, entry.CompletedAt); - } - - [Fact] - public void Journal_PersistsToJsonFile() - { - // Arrange - ArchiveJournal.Configure(_testDir); - ArchiveJournal.BeginOperation( - ArchivePeriod.Hourly, - new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc), - "/tmp/test.tmp", - "/archives/hourly/test.tar.zst", - ["backup1"] - ); - - // Assert - journal file should exist - var journalPath = Path.Combine(_testDir, ".archive-journal.json"); - Assert.True(File.Exists(journalPath)); - - var content = File.ReadAllText(journalPath); - Assert.Contains("Hourly", content); - Assert.Contains("Started", content); - } - - [Fact] - public void RecoverInterrupted_CleansUpStartedOperations() - { - // Arrange - ArchiveJournal.Configure(_testDir); - - // Create a temp file that simulates an interrupted archive - var tempFile = Path.Combine(_testDir, "interrupted.tar.zst.tmp"); - File.WriteAllText(tempFile, "partial archive data"); - - var entry = ArchiveJournal.BeginOperation( - ArchivePeriod.Hourly, - new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc), - tempFile, - Path.Combine(_testDir, "final.tar.zst"), - ["backup1"] - ); - - // Act - ArchiveJournal.RecoverInterrupted(); - - // Assert - Assert.Equal(ArchiveOperationState.Failed, entry.State); - Assert.Equal("Interrupted during archive creation", entry.FailureReason); - Assert.False(File.Exists(tempFile)); // Temp file should be cleaned up - } - - [Fact] - public void RecoverInterrupted_CompletesDistributedOperations() - { - // Arrange - ArchiveJournal.Configure(_testDir); - var entry = ArchiveJournal.BeginOperation( - ArchivePeriod.Daily, - new DateTime(2026, 3, 22, 0, 0, 0, DateTimeKind.Utc), - "/tmp/test.tmp", - "/archives/daily/test.tar.zst", - ["backup1"] - ); - ArchiveJournal.RecordArchived(entry, 50, 2000); - ArchiveJournal.RecordDistributed(entry, new Dictionary { { "Local", true } }); - - // Act - simulate restart - ArchiveJournal.RecoverInterrupted(); - - // Assert - should be marked completed - Assert.Equal(ArchiveOperationState.Completed, entry.State); - } -} diff --git a/Projects/UOContent.Tests/Tests/WorldSaves/AutoArchiveHelperTests.cs b/Projects/UOContent.Tests/Tests/WorldSaves/AutoArchiveHelperTests.cs deleted file mode 100644 index b0d2b99d4..000000000 --- a/Projects/UOContent.Tests/Tests/WorldSaves/AutoArchiveHelperTests.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using Server.Saves; -using Xunit; - -namespace Server.Tests; - -[Collection("Sequential UOContent Tests")] -public class AutoArchiveHelperTests -{ - [Fact] - public void ArchiveDestinationRegistry_RegisterAndRetrieve() - { - // Arrange - var dest = new LocalArchiveDestination(24, 30, 12); - - // Act - ArchiveDestinationRegistry.Register(dest); - - // Assert - Assert.Contains(dest, ArchiveDestinationRegistry.Destinations); - - // Cleanup - ArchiveDestinationRegistry.Unregister(dest); - Assert.DoesNotContain(dest, ArchiveDestinationRegistry.Destinations); - } - - [Fact] - public void LocalArchiveDestination_SendArchive_ReturnsTrue() - { - // Arrange - var dest = new LocalArchiveDestination(24, 30, 12); - - // Act - var result = dest.SendArchive("/fake/path.tar.zst", ArchivePeriod.Hourly, DateTime.UtcNow); - - // Assert - Assert.True(result); - } - - [Theory] - [InlineData(ArchivePeriod.Hourly, 24)] - [InlineData(ArchivePeriod.Daily, 30)] - [InlineData(ArchivePeriod.Monthly, 12)] - public void LocalArchiveDestination_RetentionCounts(ArchivePeriod period, int expected) - { - // Arrange - var dest = new LocalArchiveDestination(24, 30, 12); - - // Act & Assert - Assert.Equal(expected, dest.GetRetentionCount(period)); - } - - [Fact] - public void ArchiveCompletedEventArgs_StoresValues() - { - // Arrange & Act - var args = new ArchiveCompletedEventArgs( - "/archives/hourly/test.tar.zst", - ArchivePeriod.Hourly, - new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc), - 1048576, - 2.5 - ); - - // Assert - Assert.Equal("/archives/hourly/test.tar.zst", args.ArchiveFilePath); - Assert.Equal(ArchivePeriod.Hourly, args.Period); - Assert.Equal(1048576, args.FileSizeBytes); - Assert.Equal(2.5, args.ElapsedSeconds); - } - - [Fact] - public void ArchiveFailedEventArgs_StoresException() - { - // Arrange - var ex = new InvalidOperationException("disk full"); - - // Act - var args = new ArchiveFailedEventArgs( - ArchivePeriod.Daily, - new DateTime(2026, 3, 22, 0, 0, 0, DateTimeKind.Utc), - ex - ); - - // Assert - Assert.Equal(ArchivePeriod.Daily, args.Period); - Assert.Same(ex, args.Exception); - } - - [Fact] - public void ArchiveJournalEntry_DefaultState() - { - // Arrange & Act - var entry = new ArchiveJournalEntry(); - - // Assert - Assert.NotNull(entry.SourceDirectories); - Assert.Empty(entry.SourceDirectories); - Assert.NotNull(entry.DestinationResults); - Assert.Empty(entry.DestinationResults); - Assert.Null(entry.FailureReason); - Assert.Equal(DateTime.MaxValue, entry.CompletedAt); - } -} diff --git a/Projects/UOContent.Tests/Tests/WorldSaves/ManagedArchiveTests.cs b/Projects/UOContent.Tests/Tests/WorldSaves/ManagedArchiveTests.cs deleted file mode 100644 index b5a2c0dc9..000000000 --- a/Projects/UOContent.Tests/Tests/WorldSaves/ManagedArchiveTests.cs +++ /dev/null @@ -1,325 +0,0 @@ -using System; -using System.Formats.Tar; -using System.IO; -using System.IO.Compression; -using Server.Compression; -using Xunit; - -namespace Server.Tests; - -[Collection("Sequential UOContent Tests")] -public class ManagedArchiveTests : IDisposable -{ - private readonly string _testDir; - - public ManagedArchiveTests() - { - _testDir = Path.Combine(Path.GetTempPath(), $"modernuo-test-{Guid.NewGuid():N}"); - Directory.CreateDirectory(_testDir); - } - - public void Dispose() - { - if (Directory.Exists(_testDir)) - { - Directory.Delete(_testDir, true); - } - } - - private string CreateTestDirectory(string name, params (string fileName, string content)[] files) - { - var dir = Path.Combine(_testDir, name); - Directory.CreateDirectory(dir); - - foreach (var (fileName, content) in files) - { - var filePath = Path.Combine(dir, fileName); - var fileDir = Path.GetDirectoryName(filePath); - if (fileDir != null) - { - Directory.CreateDirectory(fileDir); - } - - File.WriteAllText(filePath, content); - } - - return dir; - } - - [Fact] - public void CreateTarZstd_SingleDirectory_RoundTrips() - { - // Arrange - var sourceDir = CreateTestDirectory("source", - ("file1.txt", "Hello World"), - ("file2.bin", "Binary content here"), - ("subdir/nested.txt", "Nested file") - ); - - var archivePath = Path.Combine(_testDir, "test.tar.zst"); - - // Act - var entryCount = ManagedArchive.CreateTarZstd( - [sourceDir], archivePath, _testDir, compressionLevel: 1 - ); - - // Assert - Assert.True(entryCount > 0); - Assert.True(File.Exists(archivePath)); - Assert.True(new FileInfo(archivePath).Length > 0); - } - - [Fact] - public void CreateTarZstd_MultipleDirectories_IncludesAll() - { - // Arrange - var dir1 = CreateTestDirectory("backup1", - ("items.bin", "items data"), - ("items.idx", "items index") - ); - var dir2 = CreateTestDirectory("backup2", - ("mobiles.bin", "mobiles data"), - ("mobiles.idx", "mobiles index") - ); - - var archivePath = Path.Combine(_testDir, "multi.tar.zst"); - - // Act - var entryCount = ManagedArchive.CreateTarZstd( - [dir1, dir2], archivePath, _testDir, compressionLevel: 1 - ); - - // Assert - Assert.Equal(4, entryCount); - } - - [Fact] - public void ExtractTarZstd_RecreatesFiles() - { - // Arrange - var sourceDir = CreateTestDirectory("source", - ("data.txt", "Test data content"), - ("config/settings.json", "{\"key\":\"value\"}") - ); - - var archivePath = Path.Combine(_testDir, "extract-test.tar.zst"); - ManagedArchive.CreateTarZstd([sourceDir], archivePath, _testDir, compressionLevel: 1); - - var extractDir = Path.Combine(_testDir, "extracted"); - - // Act - var success = ManagedArchive.ExtractTarZstd(archivePath, extractDir); - - // Assert - Assert.True(success); - Assert.True(Directory.Exists(extractDir)); - - // Verify extracted content matches - var extractedDataPath = Path.Combine(extractDir, "source", "data.txt"); - Assert.True(File.Exists(extractedDataPath)); - Assert.Equal("Test data content", File.ReadAllText(extractedDataPath)); - - var extractedConfigPath = Path.Combine(extractDir, "source", "config", "settings.json"); - Assert.True(File.Exists(extractedConfigPath)); - Assert.Equal("{\"key\":\"value\"}", File.ReadAllText(extractedConfigPath)); - } - - [Fact] - public void CountEntries_MatchesCreateCount() - { - // Arrange - var sourceDir = CreateTestDirectory("source", - ("a.txt", "a"), - ("b.txt", "b"), - ("c/d.txt", "d"), - ("c/e.txt", "e") - ); - - var archivePath = Path.Combine(_testDir, "count-test.tar.zst"); - var createCount = ManagedArchive.CreateTarZstd( - [sourceDir], archivePath, _testDir, compressionLevel: 1 - ); - - // Act - var verifyCount = ManagedArchive.CountEntries(archivePath); - - // Assert - Assert.Equal(createCount, verifyCount); - } - - [Fact] - public void CreateTarZstd_EmptyDirectory_ReturnsZero() - { - // Arrange - var sourceDir = Path.Combine(_testDir, "empty"); - Directory.CreateDirectory(sourceDir); - - var archivePath = Path.Combine(_testDir, "empty.tar.zst"); - - // Act - var entryCount = ManagedArchive.CreateTarZstd( - [sourceDir], archivePath, _testDir, compressionLevel: 1 - ); - - // Assert - Assert.Equal(0, entryCount); - } - - [Fact] - public void CreateTarZstd_NonexistentSource_SkipsGracefully() - { - // Arrange - var archivePath = Path.Combine(_testDir, "nonexistent.tar.zst"); - - // Act - var entryCount = ManagedArchive.CreateTarZstd( - [Path.Combine(_testDir, "does-not-exist")], archivePath, _testDir, compressionLevel: 1 - ); - - // Assert - Assert.Equal(0, entryCount); - } - - [Fact] - public void CreateTarZstd_TempFileCleanedUp() - { - // Arrange - var sourceDir = CreateTestDirectory("source", ("file.txt", "content")); - var archivePath = Path.Combine(_testDir, "cleanup-test.tar.zst"); - - // Act - ManagedArchive.CreateTarZstd([sourceDir], archivePath, _testDir, compressionLevel: 1); - - // Assert - no .tmp file should remain - Assert.False(File.Exists(archivePath + ".tmp")); - Assert.True(File.Exists(archivePath)); - } - - [Fact] - public void ExtractTarZstd_InvalidFile_ReturnsFalse() - { - // Arrange - var badFile = Path.Combine(_testDir, "bad.tar.zst"); - File.WriteAllText(badFile, "this is not a valid archive"); - var extractDir = Path.Combine(_testDir, "extract-bad"); - - // Act - var result = ManagedArchive.ExtractTarZstd(badFile, extractDir); - - // Assert - Assert.False(result); - } - - [Fact] - public void CountEntries_InvalidFile_ReturnsNegative() - { - // Arrange - var badFile = Path.Combine(_testDir, "bad.tar.zst"); - File.WriteAllText(badFile, "not a valid archive"); - - // Act - var count = ManagedArchive.CountEntries(badFile); - - // Assert - Assert.Equal(-1, count); - } - - [Theory] - [InlineData(1)] - [InlineData(3)] - [InlineData(10)] - public void CreateTarZstd_DifferentCompressionLevels_AllWork(int level) - { - // Arrange - var sourceDir = CreateTestDirectory($"level-{level}", ("data.txt", new string('X', 10000))); - var archivePath = Path.Combine(_testDir, $"level-{level}.tar.zst"); - - // Act - var entryCount = ManagedArchive.CreateTarZstd( - [sourceDir], archivePath, _testDir, compressionLevel: level - ); - - // Assert - Assert.Equal(1, entryCount); - Assert.True(File.Exists(archivePath)); - - // Verify we can extract it back - var extractDir = Path.Combine(_testDir, $"extract-level-{level}"); - Assert.True(ManagedArchive.ExtractTarZstd(archivePath, extractDir)); - } - - [Fact] - public void ExtractTarGz_RoundTrips() - { - // Arrange — create a .tar.gz using built-in .NET APIs - var sourceDir = CreateTestDirectory("gz-source", - ("data.txt", "GZip test data"), - ("sub/nested.txt", "Nested content") - ); - - var archivePath = Path.Combine(_testDir, "test.tar.gz"); - - using (var fileStream = new FileStream(archivePath, FileMode.Create)) - using (var gzStream = new GZipStream(fileStream, System.IO.Compression.CompressionLevel.Fastest)) - using (var tarWriter = new TarWriter(gzStream, TarEntryFormat.Pax, leaveOpen: true)) - { - foreach (var file in new DirectoryInfo(sourceDir).EnumerateFiles("*", SearchOption.AllDirectories)) - { - var entryName = Path.GetRelativePath(sourceDir, file.FullName).Replace('\\', '/'); - tarWriter.WriteEntry(file.FullName, entryName); - } - } - - var extractDir = Path.Combine(_testDir, "gz-extracted"); - - // Act - var success = ManagedArchive.ExtractTarGz(archivePath, extractDir); - - // Assert - Assert.True(success); - Assert.Equal("GZip test data", File.ReadAllText(Path.Combine(extractDir, "data.txt"))); - Assert.Equal("Nested content", File.ReadAllText(Path.Combine(extractDir, "sub", "nested.txt"))); - } - - [Fact] - public void ExtractTar_PlainTarRoundTrips() - { - // Arrange — create a plain .tar using built-in .NET APIs - var sourceDir = CreateTestDirectory("tar-source", - ("plain.txt", "Plain tar test data") - ); - - var archivePath = Path.Combine(_testDir, "test.tar"); - - using (var fileStream = new FileStream(archivePath, FileMode.Create)) - using (var tarWriter = new TarWriter(fileStream, TarEntryFormat.Pax, leaveOpen: true)) - { - foreach (var file in new DirectoryInfo(sourceDir).EnumerateFiles("*", SearchOption.AllDirectories)) - { - var entryName = Path.GetRelativePath(sourceDir, file.FullName).Replace('\\', '/'); - tarWriter.WriteEntry(file.FullName, entryName); - } - } - - var extractDir = Path.Combine(_testDir, "tar-extracted"); - - // Act - var success = ManagedArchive.ExtractTar(archivePath, extractDir); - - // Assert - Assert.True(success); - Assert.Equal("Plain tar test data", File.ReadAllText(Path.Combine(extractDir, "plain.txt"))); - } - - [Fact] - public void ExtractTarGz_InvalidFile_ReturnsFalse() - { - // Arrange - var badFile = Path.Combine(_testDir, "bad.tar.gz"); - File.WriteAllText(badFile, "not a valid gzip archive"); - var extractDir = Path.Combine(_testDir, "extract-bad-gz"); - - // Act & Assert - Assert.False(ManagedArchive.ExtractTarGz(badFile, extractDir)); - } -} diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj index 1c73f6eb4..2e3acc315 100644 --- a/Projects/UOContent.Tests/UOContent.Tests.csproj +++ b/Projects/UOContent.Tests/UOContent.Tests.csproj @@ -4,15 +4,13 @@ Debug;Release;Analyze - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + diff --git a/Projects/UOContent/Accounting/Account.Migrations.cs b/Projects/UOContent/Accounting/Account.Migrations.cs index d89389305..cd28d22d9 100644 --- a/Projects/UOContent/Accounting/Account.Migrations.cs +++ b/Projects/UOContent/Accounting/Account.Migrations.cs @@ -86,28 +86,28 @@ public partial class Account var length = reader.ReadInt(); _mobiles = new Mobile[length]; - for (var i = 0; i < length; i++) + for (int i = 0; i < length; i++) { _mobiles[i] = reader.ReadEntity(); } length = reader.ReadInt(); _comments = length > 0 ? new List(length) : null; - for (var i = 0; i < length; i++) + for (int i = 0; i < length; i++) { _comments!.Add(new AccountComment(reader)); } length = reader.ReadInt(); _tags = length > 0 ? new List(length) : null; - for (var i = 0; i < length; i++) + for (int i = 0; i < length; i++) { _tags!.Add(new AccountTag(reader)); } length = reader.ReadInt(); _loginIPs = new IPAddress[length]; - for (var i = 0; i < length; i++) + for (int i = 0; i < length; i++) { if (version < 2) { @@ -123,7 +123,7 @@ public partial class Account } length = reader.ReadInt(); - for (var i = 0; i < length; i++) + for (int i = 0; i < length; i++) { reader.ReadString(); // IP Restrictions } diff --git a/Projects/UOContent/Accounting/Account.cs b/Projects/UOContent/Accounting/Account.cs index b583585ee..73519eb04 100644 --- a/Projects/UOContent/Accounting/Account.cs +++ b/Projects/UOContent/Accounting/Account.cs @@ -10,11 +10,9 @@ using Server.Misc; using Server.Mobiles; using Server.Multis; using Server.Network; -using Server.Systems.FeatureFlags; namespace Server.Accounting; -[PropertyObject] [SerializationGenerator(6)] public partial class Account : IAccount, IComparable { @@ -23,7 +21,7 @@ public partial class Account : IAccount, IComparable public static readonly TimeSpan EmptyInactiveDuration = TimeSpan.FromDays(30.0); [InternString] - [SerializableField(0, setter: "private")] + [SerializableField(0)] private string _username; [SerializableField(1)] @@ -251,7 +249,7 @@ public partial class Account : IAccount, IComparable ///
public bool Young { - get => ContentFeatureFlags.YoungPlayerSystem && !GetFlag(1); + get => !GetFlag(1); set { SetFlag(1, !value); @@ -287,6 +285,10 @@ public partial class Account : IAccount, IComparable public Serial Serial { get; set; } + public byte SerializedThread { get; set; } + public int SerializedPosition { get; set; } + public int SerializedLength { get; set; } + [AfterDeserialization(false)] private void AfterDeserialization() { @@ -363,69 +365,30 @@ public partial class Account : IAccount, IComparable public bool Deleted { get; private set; } - public bool TrySetUsername(string username) - { - if (username == _username || Accounts.GetAccount(username) != null) - { - return false; - } - - Accounts.Remove(this); - Username = username; - Accounts.Add(this); - return true; - } - public void SetPassword(string plainPassword) { + var phrase = _passwordAlgorithm is PasswordProtectionAlgorithm.SHA1 or PasswordProtectionAlgorithm.SHA2 + ? $"{_username}{plainPassword}" + : plainPassword; + + Password = AccountSecurity.CurrentPasswordProtection.EncryptPassword(phrase); PasswordAlgorithm = AccountSecurity.CurrentAlgorithm; - Password = AccountSecurity.CurrentPasswordProtection.EncryptPassword( - AccountSecurity.DerivePhrase(PasswordAlgorithm, _username, plainPassword) - ); - } - - /// The phrase that verifies against the currently stored hash. - internal string GetVerifyPhrase(string plainPassword) => - AccountSecurity.DerivePhrase(_passwordAlgorithm, _username, plainPassword); - - /// The phrase a rehash to the configured algorithm would be derived from. - internal string GetRehashPhrase(string plainPassword) => - AccountSecurity.DerivePhrase(AccountSecurity.CurrentAlgorithm, _username, plainPassword); - - /// - /// Whether a successful login should rewrite the stored hash, because the algorithm changed or - /// its cost parameters moved. - /// - internal bool NeedsPasswordUpgrade() => - _passwordAlgorithm != AccountSecurity.CurrentAlgorithm || - AccountSecurity.CurrentPasswordProtection.NeedsRehash(Password); - - /// - /// Applies a hash derived off the game loop. Distinct from the private UpgradePassword - /// below, which adopts a legacy hash when loading pre-binary XML accounts. - /// - /// Unguarded: dispatch is on the loop, one worker drains FIFO, and results return through the - /// loop context in that order, so last dispatched is last applied. A second worker would need - /// ordering reintroduced here. - /// - internal void ApplyPasswordWrite(string newEncrypted, PasswordProtectionAlgorithm algorithm) - { - PasswordAlgorithm = algorithm; - Password = newEncrypted; } public bool CheckPassword(string plainPassword) { - var ok = AccountSecurity.GetPasswordProtection(_passwordAlgorithm) - .ValidatePassword(Password, GetVerifyPhrase(plainPassword)); + var phrase = _passwordAlgorithm is PasswordProtectionAlgorithm.SHA1 or PasswordProtectionAlgorithm.SHA2 + ? $"{_username}{plainPassword}" + : plainPassword; + var ok = AccountSecurity.GetPasswordProtection(_passwordAlgorithm).ValidatePassword(Password, phrase); if (!ok) { return false; } // Upgrade the password protection in case we change the algorithm - if (NeedsPasswordUpgrade()) + if (_passwordAlgorithm != AccountSecurity.CurrentAlgorithm) { SetPassword(plainPassword); } @@ -873,6 +836,40 @@ public partial class Account : IAccount, IComparable return true; } + /// + /// Deserializes a list of string values from an xml element. Null values are not added to the list. + /// + /// The XmlElement from which to deserialize. + /// String list. Value will never be null. + private static string[] LoadAccessCheck(XmlElement node) + { + string[] stringList; + var accessCheck = node["accessCheck"]; + + if (accessCheck != null) + { + var list = new List(); + + foreach (XmlElement ip in accessCheck.GetElementsByTagName("ip")) + { + var text = Utility.GetText(ip, null); + + if (text != null) + { + list.Add(text); + } + } + + stringList = list.ToArray(); + } + else + { + stringList = []; + } + + return stringList; + } + /// /// Deserializes a list of IPAddress values from an xml element. /// @@ -1157,7 +1154,7 @@ public partial class Account : IAccount, IComparable [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { - var localList = _mobiles; + Mobile[] localList = _mobiles; while ((uint)_index < (uint)localList.Length) { diff --git a/Projects/UOContent/Accounting/AccountHandler.cs b/Projects/UOContent/Accounting/AccountHandler.cs index eea0dc069..fce134355 100644 --- a/Projects/UOContent/Accounting/AccountHandler.cs +++ b/Projects/UOContent/Accounting/AccountHandler.cs @@ -1,11 +1,8 @@ using System; -using System.Buffers; using System.Collections.Generic; using System.Net; -using System.Runtime.CompilerServices; using ModernUO.CodeGeneratedEvents; using Server.Accounting; -using Server.Accounting.Security; using Server.Engines.CharacterCreation; using Server.Engines.Help; using Server.Logging; @@ -20,13 +17,13 @@ public static class AccountHandler private static int MaxAccountsPerIP; private static bool AutoAccountCreation; - private static readonly bool RestrictDeletion = !TestCenter.Enabled; - private static readonly TimeSpan DeleteDelay = TimeSpan.FromDays(7.0); + private static bool RestrictDeletion = !TestCenter.Enabled; + private static TimeSpan DeleteDelay = TimeSpan.FromDays(7.0); private static bool PasswordCommandEnabled; private static Dictionary m_IPTable; - private static readonly SearchValues ForbiddenChars = SearchValues.Create("<>:\"/\\|?*"); + private static char[] m_ForbiddenChars = { '<', '>', ':', '"', '/', '\\', '|', '?', '*' }; public static AccessLevel LockdownLevel { get; set; } @@ -70,9 +67,6 @@ public static class AccountHandler public static void Initialize() { EventSink.AccountLogin += EventSink_AccountLogin; - - EventSink.Shutdown += PasswordWorker.Stop; - EventSink.ServerCrashed += PasswordWorker.OnCrashed; } [Usage("Password ")] @@ -143,12 +137,8 @@ public static class AccountHandler if (accessList[0].MatchClassC(ipAddress)) { - // Confirmed from the callback: off-loop the write has not landed yet here. - PasswordWorker.SetPassword( - acct, - pass, - _ => from.SendMessage("The password to your account has changed.") - ); + acct.SetPassword(pass); + from.SendMessage("The password to your account has changed."); } else { @@ -250,19 +240,41 @@ public static class AccountHandler public static bool CanCreate(IPAddress ip) => !IPTable.TryGetValue(ip, out var result) || result < MaxAccountsPerIP; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidUsername(ReadOnlySpan username) => - username.Length > 0 && - // Usernames must not start with a space, end with a space, or end with a period - !username.StartsWith(' ') && !username.EndsWith(' ') && !username.EndsWith('.') && - !username.ContainsAny(ForbiddenChars); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidPassword(ReadOnlySpan password) => password.Length > 0; - - private static Account CreateAccount(NetState state, string username, string password) + private static bool IsForbiddenChar(char c) { - if (!IsValidUsername(username) || !IsValidPassword(password)) + for (var i = 0; i < m_ForbiddenChars.Length; ++i) + { + if (c == m_ForbiddenChars[i]) + { + return true; + } + } + + return false; + } + + private static Account CreateAccount(NetState state, string un, string pw) + { + if (un.Length == 0 || pw.Length == 0) + { + return null; + } + + var isSafe = !(un.StartsWithOrdinal(" ") || + un.EndsWithOrdinal(" ") || + un.EndsWithOrdinal(".")); + + for (var i = 0; isSafe && i < un.Length; ++i) + { + isSafe = un[i] >= 0x20 && un[i] < 0x7F && !IsForbiddenChar(un[i]); + } + + for (var i = 0; isSafe && i < pw.Length; ++i) + { + isSafe = pw[i] >= 0x20 && pw[i] < 0x7F; + } + + if (!isSafe) { return null; } @@ -272,16 +284,18 @@ public static class AccountHandler logger.Information( $"Login: {{NetState}} Account '{{Username}}' not created, ip already has {{AccountCount}} account{(MaxAccountsPerIP == 1 ? "" : "s")}.", state, - username, + un, MaxAccountsPerIP ); return null; } - logger.Information("Login: {NetState}: Creating new account '{Username}'", state, username); + logger.Information("Login: {NetState}: Creating new account '{Username}'", state, un); - return new Account(username, password); + var a = new Account(un, pw); + + return a; } public static void EventSink_AccountLogin(AccountLoginEventArgs e) @@ -315,129 +329,24 @@ public static class AccountHandler logger.Information("Login: {NetState} Access denied for '{Username}'", e.State, un); e.RejectReason = LockdownLevel > AccessLevel.Player ? ALRReason.BadComm : ALRReason.BadPass; } - else + else if (!acct.CheckPassword(pw)) { - HandlePasswordCheck(e, acct, pw); - } - } - - /// - /// Separate from the caller's else-if chain because two outcomes are not verdicts: the off-loop - /// path has none yet, and a full queue must reject rather than fall through and verify. - /// - private static void HandlePasswordCheck(AccountLoginEventArgs e, Account acct, string pw) - { - switch (DispatchPasswordCheck(e, acct, pw)) - { - case PasswordCheckDispatch.Deferred: - { - e.Deferred = true; - return; - } - case PasswordCheckDispatch.Saturated: - { - // Reject rather than verify inline: steering work back onto the loop is what a - // flood wants. - logger.Warning( - "Login: {NetState} Password verification queue full, rejecting '{Username}'", - e.State, - acct.Username - ); - - e.RejectReason = ALRReason.BadComm; - return; - } - } - - if (!acct.CheckPassword(pw)) - { - logger.Information("Login: {NetState} Invalid password for '{Username}'", e.State, acct.Username); + logger.Information("Login: {NetState} Invalid password for '{Username}'", e.State, un); e.RejectReason = ALRReason.BadPass; - return; } - - ApplyVerifiedLogin(e, acct); - } - - /// Everything after the password is known good, shared so an off-loop verdict lands - /// in the same state as an inline one. - private static void ApplyVerifiedLogin(AccountLoginEventArgs e, Account acct) - { - if (acct.Banned) + else if (acct.Banned) { - logger.Information("Login: {NetState} Banned account '{Username}'", e.State, acct.Username); + logger.Information("Login: {NetState} Banned account '{Username}'", e.State, un); e.RejectReason = ALRReason.Blocked; - return; - } - - logger.Information("Login: {NetState} Valid credentials for '{Username}'", e.State, acct.Username); - e.State.Account = acct; - e.Accepted = true; - - acct.LogAccess(e.State); - LoginAllowlist.RecordLogin(e.State?.Address); - } - - private enum PasswordCheckDispatch - { - /// Verify on the loop. - Inline, - - /// Handed to the worker; no verdict yet. - Deferred, - - /// The queue is full. - Saturated - } - - /// - /// Hands the password check to the worker, whatever algorithm it uses. Every protection is safe - /// off the loop, so there is no carve-out, and a cheap digest does not need one either: - /// AccountSecurity.Configure refuses anything below SHA2 as the configured algorithm, so - /// MD5 and SHA1 only appear as a stored hash awaiting migration. That makes - /// NeedsPasswordUpgrade true, and the upgrade hash dominates the job. - /// - private static PasswordCheckDispatch DispatchPasswordCheck(AccountLoginEventArgs e, Account acct, string pw) - { - if (!PasswordWorker.Enabled) - { - return PasswordCheckDispatch.Inline; - } - - var job = new PasswordJob - { - Account = acct, - State = e.State, - StoredHash = acct.Password, - StoredAlgorithm = acct.PasswordAlgorithm, - VerifyPhrase = acct.GetVerifyPhrase(pw), - HashPhrase = acct.NeedsPasswordUpgrade() ? acct.GetRehashPhrase(pw) : null, - TargetAlgorithm = AccountSecurity.CurrentAlgorithm, - OnComplete = static (j, outcome) => - CompleteDeferredAccountLogin(j.State, j.Account, outcome.Verified) - }; - - return PasswordWorker.TryEnqueue(job) - ? PasswordCheckDispatch.Deferred - : PasswordCheckDispatch.Saturated; - } - - /// Resumes a login whose password check ran on the verification thread. - internal static void CompleteDeferredAccountLogin(NetState state, Account acct, bool verified) - { - var e = new AccountLoginEventArgs(state, acct.Username, null); - - if (verified) - { - ApplyVerifiedLogin(e, acct); } else { - logger.Information("Login: {NetState} Invalid password for '{Username}'", state, acct.Username); - e.RejectReason = ALRReason.BadPass; - } + logger.Information("Login: {NetState} Valid credentials for '{Username}'", e.State, un); + e.State.Account = acct; + e.Accepted = true; - IncomingAccountPackets.CompleteAccountLogin(state, e.Accepted, e.RejectReason); + acct.LogAccess(e.State); + } } [OnEvent(nameof(GameServer.GameServerLoginEvent))] @@ -455,9 +364,7 @@ public static class AccountHandler logger.Information("Login: {NetState} Access denied for '{Username}'", e.State, un); e.Accepted = false; } - // The auth id was only issued after the account login packet verified this password, so - // re-deriving the hash costs a second Argon2 verify to answer the same question. - else if (!e.PreAuthenticated && !acct.CheckPassword(pw)) + else if (!acct.CheckPassword(pw)) { logger.Information("Login: {NetState} Invalid password for '{Username}'", e.State, un); e.Accepted = false; @@ -470,7 +377,6 @@ public static class AccountHandler else { acct.LogAccess(e.State); - LoginAllowlist.RecordLogin(e.State?.Address); logger.Information("Login: {NetState} Account '{Username}' at character list", e.State, un); e.State.Account = acct; diff --git a/Projects/UOContent/Accounting/IPasswordProtection.cs b/Projects/UOContent/Accounting/IPasswordProtection.cs index f5590a148..0fdd6ced9 100644 --- a/Projects/UOContent/Accounting/IPasswordProtection.cs +++ b/Projects/UOContent/Accounting/IPasswordProtection.cs @@ -4,12 +4,5 @@ namespace Server.Accounting { string EncryptPassword(string plainPassword); bool ValidatePassword(string encryptedPassword, string plainPassword); - - /// - /// True when was produced with parameters that differ - /// from the ones this protection currently uses, so a successful login should rewrite it. - /// Algorithms whose cost is not embedded in the stored value never need this. - /// - bool NeedsRehash(string encryptedPassword) => false; } } diff --git a/Projects/UOContent/Accounting/Security/AccountSecurity.cs b/Projects/UOContent/Accounting/Security/AccountSecurity.cs index deae4c8e0..22860d370 100644 --- a/Projects/UOContent/Accounting/Security/AccountSecurity.cs +++ b/Projects/UOContent/Accounting/Security/AccountSecurity.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: AccountSecurity.cs * * * @@ -15,66 +15,56 @@ using System; -namespace Server.Accounting.Security; - -public enum PasswordProtectionAlgorithm +namespace Server.Accounting.Security { - // Obsolete algorithms from RunUO. These are not secure! - // They are included for password upgrades only. - None, - MD5, - SHA1, - - // Supported algorithms - SHA2, // ServUO compatibility - PBKDF2, - Argon2 // Recommended algorithm for real security. -} - -public static class AccountSecurity -{ - public static PasswordProtectionAlgorithm CurrentAlgorithm { get; set; } - - public static IPasswordProtection CurrentPasswordProtection => GetPasswordProtection(CurrentAlgorithm); - - public static void Configure() + public enum PasswordProtectionAlgorithm { - CurrentAlgorithm = - ServerConfiguration.GetOrUpdateSetting( - "accountSecurity.encryptionAlgorithm", - PasswordProtectionAlgorithm.Argon2 - ); + // Obsolete algorithms from RunUO. These are not secure! + // They are included for password upgrades only. + None, + MD5, + SHA1, - if (CurrentAlgorithm < PasswordProtectionAlgorithm.SHA2) + // Supported algorithms + SHA2, // ServUO compatibility + PBKDF2, + Argon2 // Recommended algorithm for real security. + } + + public static class AccountSecurity + { + public static PasswordProtectionAlgorithm CurrentAlgorithm { get; set; } + + public static IPasswordProtection CurrentPasswordProtection => GetPasswordProtection(CurrentAlgorithm); + + public static void Configure() { - throw new Exception($"Security: {CurrentAlgorithm} is obsolete and not secure. Do not use it."); + CurrentAlgorithm = + ServerConfiguration.GetOrUpdateSetting( + "accountSecurity.encryptionAlgorithm", + PasswordProtectionAlgorithm.Argon2 + ); + + if (CurrentAlgorithm < PasswordProtectionAlgorithm.SHA2) + { + throw new Exception($"Security: {CurrentAlgorithm} is obsolete and not secure. Do not use it."); + } + } + + public static IPasswordProtection GetPasswordProtection(PasswordProtectionAlgorithm algorithm) + { + var passwordProtection = algorithm switch + { + PasswordProtectionAlgorithm.MD5 => HashAlgorithmPasswordProtection.MD5Instance, + PasswordProtectionAlgorithm.SHA1 => HashAlgorithmPasswordProtection.SHA1Instance, + PasswordProtectionAlgorithm.SHA2 => HashAlgorithmPasswordProtection.SHA2Instance, + PasswordProtectionAlgorithm.PBKDF2 => PBKDF2PasswordProtection.Instance, + PasswordProtectionAlgorithm.Argon2 => Argon2PasswordProtection.Instance, + PasswordProtectionAlgorithm.None => throw new Exception("Do not use PasswordProtectionAlgorithm.None"), + _ => throw new Exception("No algorithm") + }; + + return passwordProtection; } } - - /// - /// The string actually fed to the KDF. SHA1 and SHA2 salt by username; everything else hashes - /// the password alone. Verification must derive with the algorithm the stored hash was made - /// with, and a rehash with the one it is moving to -- deriving with the wrong one produces a - /// hash that verifies once and never again. - /// - public static string DerivePhrase(PasswordProtectionAlgorithm algorithm, string username, string plainPassword) - => algorithm is PasswordProtectionAlgorithm.SHA1 or PasswordProtectionAlgorithm.SHA2 - ? $"{username}{plainPassword}" - : plainPassword; - - public static IPasswordProtection GetPasswordProtection(PasswordProtectionAlgorithm algorithm) - { - var passwordProtection = algorithm switch - { - PasswordProtectionAlgorithm.MD5 => HashAlgorithmPasswordProtection.MD5Instance, - PasswordProtectionAlgorithm.SHA1 => HashAlgorithmPasswordProtection.SHA1Instance, - PasswordProtectionAlgorithm.SHA2 => HashAlgorithmPasswordProtection.SHA2Instance, - PasswordProtectionAlgorithm.PBKDF2 => PBKDF2PasswordProtection.Instance, - PasswordProtectionAlgorithm.Argon2 => Argon2PasswordProtection.Instance, - PasswordProtectionAlgorithm.None => throw new Exception("Do not use PasswordProtectionAlgorithm.None"), - _ => throw new Exception("No algorithm") - }; - - return passwordProtection; - } } diff --git a/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs b/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs index 0a952117d..f49d2a040 100644 --- a/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Argon2PasswordProtection.cs * * * @@ -15,43 +15,18 @@ using System.Security.Cryptography; -namespace Server.Accounting.Security; - -public class Argon2PasswordProtection : IPasswordProtection +namespace Server.Accounting.Security { - public static IPasswordProtection Instance = new Argon2PasswordProtection(); - - // 16 MiB at t=1 is cheaper than 8 MiB at t=3 (8.5 ms vs 10.1 ms) and twice as memory-hard, which - // is what resists GPU and ASIC cracking. p=1: native argon2 spawns a thread per lane. - private readonly Argon2PasswordHasher _passwordHasher = new( - time: 1, - memory: 16384, - parallel: 1, - type: Argon2Type.Argon2id, - rng: RandomNumberGenerator.Create() - ); - - public string EncryptPassword(string plainPassword) => - _passwordHasher.Hash(plainPassword); - - public bool ValidatePassword(string encryptedPassword, string plainPassword) => - _passwordHasher.Verify(encryptedPassword, plainPassword); - - // Verification uses the parameters embedded in the PHC string, not the configured ones, so - // comparing them is what lets a parameter change reach existing accounts. - public bool NeedsRehash(string encryptedPassword) + public class Argon2PasswordProtection : IPasswordProtection { - // Unparseable but verified: a format this build does not understand, so rewrite it. - if (!Argon2PasswordHasher.TryExtractMetadataValues(encryptedPassword, out var values)) - { - return true; - } + public static IPasswordProtection Instance = new Argon2PasswordProtection(); - return values.ArgonType != _passwordHasher.ArgonType - || values.MemoryCost != _passwordHasher.MemoryCost - || values.TimeCost != _passwordHasher.TimeCost - || values.Parallelism != _passwordHasher.Parallelism - || values.HashLength != (int)_passwordHasher.HashLength - || values.SaltLength != (int)_passwordHasher.SaltLength; + private readonly Argon2PasswordHasher m_PasswordHasher = new(rng: BuiltInSecureRng.Generator); + + public string EncryptPassword(string plainPassword) => + m_PasswordHasher.Hash(plainPassword); + + public bool ValidatePassword(string encryptedPassword, string plainPassword) => + m_PasswordHasher.Verify(encryptedPassword, plainPassword); } } diff --git a/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs b/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs index 63d0ac7a8..978adbbf8 100644 --- a/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: HashAlgorithmPasswordProtection.cs * * * @@ -17,51 +17,24 @@ using System; using System.Security.Cryptography; using Server.Text; -namespace Server.Accounting.Security; - -/// -/// The obsolete unsalted digests, kept only so imported accounts can log in once and be upgraded. -/// -/// Hashing goes through the one-shot static APIs rather than a retained . -/// A instance carries the running digest across HashCore/HashFinal, so -/// two threads sharing one corrupt each other's result -- and these are process-wide singletons. -/// The static form has no such state, allocates nothing, and produces identical bytes. -/// -public class HashAlgorithmPasswordProtection : IPasswordProtection +namespace Server.Accounting.Security { - private enum Kind + public class HashAlgorithmPasswordProtection : IPasswordProtection { - MD5, - SHA1, - SHA512 - } + public static IPasswordProtection MD5Instance = new HashAlgorithmPasswordProtection(MD5.Create()); + public static IPasswordProtection SHA1Instance = new HashAlgorithmPasswordProtection(SHA1.Create()); + public static IPasswordProtection SHA2Instance = new HashAlgorithmPasswordProtection(SHA512.Create()); + private readonly HashAlgorithm _hashAlgorithm; - public static readonly IPasswordProtection MD5Instance = new HashAlgorithmPasswordProtection(Kind.MD5); - public static readonly IPasswordProtection SHA1Instance = new HashAlgorithmPasswordProtection(Kind.SHA1); - public static readonly IPasswordProtection SHA2Instance = new HashAlgorithmPasswordProtection(Kind.SHA512); + public HashAlgorithmPasswordProtection(HashAlgorithm hashAlgorithm) => _hashAlgorithm = hashAlgorithm; - private const int MaxDigestLength = 64; // SHA512, the largest of the three. - - private readonly Kind _kind; - - private HashAlgorithmPasswordProtection(Kind kind) => _kind = kind; - - public string EncryptPassword(string plainPassword) - { - var bytes = plainPassword.AsSpan(0, Math.Min(256, plainPassword.Length)).GetBytesAscii(); - - Span digest = stackalloc byte[MaxDigestLength]; - - var written = _kind switch + public string EncryptPassword(string plainPassword) { - Kind.MD5 => MD5.HashData(bytes, digest), - Kind.SHA1 => SHA1.HashData(bytes, digest), - _ => SHA512.HashData(bytes, digest) - }; + byte[] bytes = plainPassword.AsSpan(0, Math.Min(256, plainPassword.Length)).GetBytesAscii(); + return _hashAlgorithm.ComputeHash(bytes).ToHexString(); + } - return digest[..written].ToHexString(); + public bool ValidatePassword(string encryptedPassword, string plainPassword) => + EncryptPassword(plainPassword) == encryptedPassword; } - - public bool ValidatePassword(string encryptedPassword, string plainPassword) => - EncryptPassword(plainPassword) == encryptedPassword; } diff --git a/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs b/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs index 96706c030..e9fd8b05f 100644 --- a/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: PBKDF2PasswordProtection.cs * * * @@ -18,44 +18,42 @@ using System.Buffers.Binary; using System.Security.Cryptography; using Server.Text; -namespace Server.Accounting.Security; - -public class PBKDF2PasswordProtection : IPasswordProtection +namespace Server.Accounting.Security { - private const ushort MinIterations = 1024; - private const ushort MaxIterations = 1536; - private const int SaltSize = 8; - private const int HashSize = 32; - private const int OutputSize = 2 + SaltSize + HashSize; - public static readonly IPasswordProtection Instance = new PBKDF2PasswordProtection(); - - public string EncryptPassword(string plainPassword) + public class PBKDF2PasswordProtection : IPasswordProtection { - Span output = stackalloc byte[OutputSize]; + private const ushort m_MinIterations = 1024; + private const ushort m_MaxIterations = 1536; + private const int m_SaltSize = 8; + private const int m_HashSize = 32; + private const int m_OutputSize = 2 + m_SaltSize + m_HashSize; + public static readonly IPasswordProtection Instance = new PBKDF2PasswordProtection(); - var iterations = RandomNumberGenerator.GetInt32(MinIterations, MaxIterations + 1); - BinaryPrimitives.WriteUInt16LittleEndian(output[..2], (ushort)iterations); + public string EncryptPassword(string plainPassword) + { + Span output = stackalloc byte[m_OutputSize]; + var iterations = Utility.RandomMinMax(m_MinIterations, m_MaxIterations); + BinaryPrimitives.WriteUInt16LittleEndian(output[..2], (ushort)iterations); - var salt = output.Slice(2, SaltSize); - RandomNumberGenerator.Fill(salt); + var rfc2898 = new Rfc2898DeriveBytes(plainPassword, m_SaltSize, iterations, HashAlgorithmName.SHA256); + rfc2898.Salt.CopyTo(output.Slice(2, m_SaltSize)); + rfc2898.GetBytes(m_HashSize).CopyTo(output[(m_SaltSize + 2)..]); - var hash = output.Slice(2 + SaltSize, HashSize); - Rfc2898DeriveBytes.Pbkdf2(plainPassword, salt, hash, iterations, HashAlgorithmName.SHA256); + return output.ToHexString(); + } - return output.ToHexString(); - } + public bool ValidatePassword(string encryptedPassword, string plainPassword) + { + Span encryptedBytes = stackalloc byte[m_OutputSize]; + encryptedPassword.GetBytes(encryptedBytes); - public bool ValidatePassword(string encryptedPassword, string plainPassword) - { - Span encryptedBytes = stackalloc byte[OutputSize]; - encryptedPassword.GetBytes(encryptedBytes); + var iterations = BinaryPrimitives.ReadUInt16LittleEndian(encryptedBytes[..2]); + var salt = encryptedBytes.Slice(2, m_SaltSize); - var iterations = BinaryPrimitives.ReadUInt16LittleEndian(encryptedBytes[..2]); - var salt = encryptedBytes.Slice(2, SaltSize); + ReadOnlySpan hash = + new Rfc2898DeriveBytes(plainPassword, salt.ToArray(), iterations, HashAlgorithmName.SHA256).GetBytes(m_HashSize); - Span hash = stackalloc byte[HashSize]; - Rfc2898DeriveBytes.Pbkdf2(plainPassword, salt, hash, iterations, HashAlgorithmName.SHA256); - - return hash.SequenceEqual(encryptedBytes[(SaltSize + 2)..]); + return hash.SequenceEqual(encryptedBytes[(m_SaltSize + 2)..]); + } } } diff --git a/Projects/UOContent/Accounting/Security/PasswordWorker.cs b/Projects/UOContent/Accounting/Security/PasswordWorker.cs deleted file mode 100644 index 83e9207f1..000000000 --- a/Projects/UOContent/Accounting/Security/PasswordWorker.cs +++ /dev/null @@ -1,293 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: PasswordWorker.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Concurrent; -using System.Threading; -using Server.Logging; -using Server.Network; - -namespace Server.Accounting.Security; - -/// -/// Work handed to the password thread, which reads no game state and writes none. -/// -/// Verify and hash are independently optional: a login verifies and may rehash, an explicit change -/// only hashes. -/// -internal sealed class PasswordJob -{ - public Account Account; - - /// Ties the job to a connection. Null when the work is not gated on one, such as a - /// password change by an admin. - public NetState State; - - /// Hash to verify against, with . - public string StoredHash; - - /// Algorithm was written with. Both algorithms are resolved on - /// the loop; AccountSecurity.CurrentAlgorithm is mutable state the worker must not read. - public PasswordProtectionAlgorithm StoredAlgorithm; - - /// Phrase to verify, or null to skip verification. - public string VerifyPhrase; - - /// Phrase to hash, or null when nothing needs writing. - public string HashPhrase; - - public PasswordProtectionAlgorithm TargetAlgorithm; - - /// Runs on the game loop with the result. Free to touch game state. - public Action OnComplete; -} - -internal readonly struct PasswordOutcome -{ - /// True when no verification was asked for, or it succeeded. - public readonly bool Verified; - - /// The derived hash, or null when nothing was hashed or verification failed. - public readonly string Hash; - - public PasswordOutcome(bool verified, string hash) - { - Verified = verified; - Hash = hash; - } -} - -/// -/// Runs password hashing off the game loop. An Argon2 verify costs ~8.9 ms of frozen world per -/// login attempt, successful or not. -/// -/// Exactly one worker, and that is load-bearing three times over. It cannot cost the loop more than -/// an inline verify under any scheduling regime, because at worst it takes an equal share of one -/// core -- which is what lets the measurement hold on hardware we cannot inspect. It caps live -/// hashing arenas at one. And writes apply in dispatch order only because a single thread drains -/// FIFO, so a second would need ordering reintroduced. -/// -/// ~110 verifies/sec, which is ample: only loop time matters, not login latency. -/// -internal sealed class PasswordWorker -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(PasswordWorker)); - - /// - /// Backstop, not a flood defense. SentFirstPacket holds a connection to one pending - /// verify and the engine caps connections at 4096 (NetState.Network.cs), so this matches - /// that bound and can only trip if that invariant breaks. A cap low enough to blunt an attack - /// would reject real players first; flood defense belongs at the connection layer. - /// - private const int MaxPending = 4096; - - // Nothing signals the worker when a save freeze ends, so it re-checks on this interval -- but - // only while a save is in progress, never in steady state. - private const int SaveGatePollMs = 50; - - private static PasswordWorker _instance; - - // Needs a spare core to move work to, which a 1-2 core host does not have. Off in DEBUG, where - // logins are rare and the inline path is easier to follow. - internal static readonly bool Enabled = -#if DEBUG - false; -#else - Environment.ProcessorCount >= 4; -#endif - - private readonly Thread _thread; - private readonly AutoResetEvent _work = new(false); - private readonly ConcurrentQueue _queue = []; - - private int _pending; - private volatile bool _exit; - - private PasswordWorker() - { - _thread = new Thread(Execute) - { - IsBackground = true, - Name = "Password Worker" - }; - - _thread.Start(); - } - - private static PasswordWorker Instance => _instance ??= new PasswordWorker(); - - /// Queues a job. False when full, and the caller must then reject without verifying. - internal static bool TryEnqueue(PasswordJob job) => Instance.TryEnqueueCore(job); - - private bool TryEnqueueCore(PasswordJob job) - { - if (Volatile.Read(ref _pending) >= MaxPending) - { - return false; - } - - Interlocked.Increment(ref _pending); - _queue.Enqueue(job); - _work.Set(); - - return true; - } - - /// - /// Checked before each job, which bounds a save overlap to whichever hash was already running: - /// the freeze holds the loop, so nothing new can be queued during it. PendingSave counts too -- - /// the serialization threads are already awake and spinning on an empty queue by then. - /// - private static bool CanRunNow() => World.WorldState is WorldState.Running or WorldState.WritingSave; - - private void Execute() - { - while (!_exit) - { - if (_queue.IsEmpty) - { - // A kernel block at zero CPU. Set() during a hash leaves the event signalled, so a - // wake arriving mid-job is not lost. - _work.WaitOne(); - continue; - } - - if (!CanRunNow()) - { - _work.WaitOne(SaveGatePollMs); - continue; - } - - if (!_queue.TryDequeue(out var job)) - { - continue; - } - - Interlocked.Decrement(ref _pending); - - // Gone while it waited: skip it rather than hash for a verdict nobody receives. Running - // only goes true -> false, so a stale read wastes a hash but never skips a live one. A - // null State is a job with no connection to lose, and still runs. - if (job.State?.Running == false) - { - continue; - } - - PasswordOutcome outcome; - - try - { - outcome = Compute(job); - } - catch (Exception ex) - { - // A verdict must still come back, or the connection never gets a reply. - logger.Error(ex, "Password work failed for {Username}", job.Account?.Username); - outcome = new PasswordOutcome(false, null); - } - - Core.LoopContext.Post(() => Apply(job, outcome)); - } - } - - private static PasswordOutcome Compute(PasswordJob job) - { - if (job.VerifyPhrase != null && - !AccountSecurity.GetPasswordProtection(job.StoredAlgorithm) - .ValidatePassword(job.StoredHash, job.VerifyPhrase)) - { - return new PasswordOutcome(false, null); - } - - return new PasswordOutcome( - true, - job.HashPhrase == null - ? null : AccountSecurity.GetPasswordProtection(job.TargetAlgorithm).EncryptPassword(job.HashPhrase) - ); - } - - private static void Apply(PasswordJob job, PasswordOutcome outcome) - { - // Re-checked: a connection can drop while the result sits in the loop queue. - if (job.State?.Running == false) - { - return; - } - - if (outcome.Verified && outcome.Hash != null) - { - job.Account.ApplyPasswordWrite(outcome.Hash, job.TargetAlgorithm); - } - - job.OnComplete?.Invoke(job, outcome); - } - - /// - /// Sets a password, off the loop where available and inline otherwise, invoking - /// on the loop either way. - /// - /// Confirm from , not the call site: off-loop the write has not - /// happened when this returns. - /// - internal static void SetPassword(Account account, string plainPassword, Action onDone) - { - if (!Enabled) - { - account.SetPassword(plainPassword); - onDone?.Invoke(true); - return; - } - - var job = new PasswordJob - { - Account = account, - HashPhrase = account.GetRehashPhrase(plainPassword), - TargetAlgorithm = AccountSecurity.CurrentAlgorithm, - OnComplete = (_, outcome) => onDone?.Invoke(outcome.Hash != null) - }; - - if (!TryEnqueue(job)) - { - // Saturated. Unlike a login, a password change must not be dropped, so it pays the - // hash on the loop instead. - account.SetPassword(plainPassword); - onDone?.Invoke(true); - } - } - - /// Runs a job on the calling thread. The seam the tests drive. - internal static PasswordOutcome ComputeInline(PasswordJob job) => Compute(job); - - /// - /// Stops the worker on shutdown or crash. Pending jobs are dropped rather than finished: - /// nothing saves the world after this point, so a write applied here would reach no disk. - /// - /// Draining the loop context is not this type's business either. That belongs in the core - /// shutdown path, before subscriber events run -- a subscriber pumping the shared context would - /// execute other subscribers' work at an arbitrary point in the event order. - /// - internal static void Stop() => _instance?.StopThread(); - - // HandleClosed skips InvokeShutdown when the server crashed, so the crash path needs its own - // subscription. - internal static void OnCrashed(ServerCrashedEventArgs e) => Stop(); - - private void StopThread() - { - _exit = true; - _work.Set(); - _thread.Join(TimeSpan.FromSeconds(5)); - } -} diff --git a/Projects/UOContent/Assistants/AssistantHandler.cs b/Projects/UOContent/Assistants/AssistantHandler.cs index 12ffd2d32..a42e50224 100644 --- a/Projects/UOContent/Assistants/AssistantHandler.cs +++ b/Projects/UOContent/Assistants/AssistantHandler.cs @@ -79,7 +79,7 @@ public static class AssistantHandler private static void HandshakeResponse(NetState state, SpanReader reader) { - var m = state.Mobile; + Mobile m = state.Mobile; if (m == null || !_handshakes.TryGetValue(m, out var t)) { diff --git a/Projects/UOContent/Commands/AddonGenerator.cs b/Projects/UOContent/Commands/AddonGenerator.cs index f8b8020a6..001363e28 100644 --- a/Projects/UOContent/Commands/AddonGenerator.cs +++ b/Projects/UOContent/Commands/AddonGenerator.cs @@ -1,5 +1,4 @@ using System; -using System.Buffers; using System.IO; using System.Collections.Generic; using Server.Collections; @@ -36,7 +35,7 @@ public class AddonGenerator [SerializationGenerator(0)] public class {name}Addon : BaseAddon { - public override BaseAddonDeed Deed => new {name}AddonDeed(); + public override BaseAddonDeed Deed => {name}AddonDeed(); [Constructible] public {name}Addon() @@ -58,51 +57,8 @@ public class AddonGenerator } """; - private static readonly SearchValues _templatePlaceholders = SearchValues.Create(["{serverItemsNamespace}", "{namespace}", "{name}", "{components}"], StringComparison.Ordinal); - - private static string ExpandTemplate( - ReadOnlySpan serverItemsNamespace, - ReadOnlySpan ns, - ReadOnlySpan name, - ReadOnlySpan components - ) - { - using var output = ValueStringBuilder.Create(); - - var remaining = _template.AsSpan(); - - int index; - while ((index = remaining.IndexOfAny(_templatePlaceholders)) >= 0) - { - output.Append(remaining[..index]); - - var placeholder = remaining[index..]; - if (placeholder.StartsWith("{serverItemsNamespace}")) - { - output.Append(serverItemsNamespace); - remaining = placeholder["{serverItemsNamespace}".Length..]; - } - else if (placeholder.StartsWith("{namespace}")) - { - output.Append(ns); - remaining = placeholder["{namespace}".Length..]; - } - else if (placeholder.StartsWith("{name}")) - { - output.Append(name); - remaining = placeholder["{name}".Length..]; - } - else // {components} - { - output.Append(components); - remaining = placeholder["{components}".Length..]; - } - } - - output.Append(remaining); - - return output.ToString(); - } + // TODO: .NET 9 - Use search values + // private static SearchValues _templatePlaceholders = SearchValues.Create("{serverItemsNamespace}", "{namespace}", "{name}", "{components}"); public static void Configure() { @@ -157,64 +113,54 @@ public class AddonGenerator (start.Y, end.Y) = (end.Y, start.Y); } - var startPoint2D = new Point2D(start.X, start.Y); - var endPoint2D = new Point2D(end.X, end.Y); - var bounds = new Rectangle2D(startPoint2D, endPoint2D); + Point2D startPoint2D = new Point2D(start.X, start.Y); + Point2D endPoint2D = new Point2D(end.X, end.Y); + Rectangle2D bounds = new Rectangle2D(startPoint2D, endPoint2D); - var name = pickerState.Name; - var ns = pickerState.Namespace; - var items = pickerState.Items; - var statics = pickerState.Statics; - var range = pickerState.Range; - var min = pickerState.Min; - var max = pickerState.Max; + string name = pickerState.Name; + string ns = pickerState.Namespace; + bool items = pickerState.Items; + bool statics = pickerState.Statics; + bool range = pickerState.Range; + sbyte min = pickerState.Min; + sbyte max = pickerState.Max; // Get center - var center = new Point3D(); + Point3D center = new Point3D(); center.Z = 127; - var x1 = bounds.End.X; - var y1 = bounds.End.Y; - var x2 = bounds.Start.X; - var y2 = bounds.Start.Y; + int x1 = bounds.End.X; + int y1 = bounds.End.Y; + int x2 = bounds.Start.X; + int y2 = bounds.Start.Y; - var tiles = new Dictionary>(); + Dictionary> tiles = new Dictionary>(); - if (statics || items) + if (statics) { - for (var x = start.X; x <= end.X; x++) + for (int x = start.X; x <= end.X; x++) { - for (var y = start.Y; y <= end.Y; y++) + for (int y = start.Y; y <= end.Y; y++) { - var tileMatrix = map.Tiles; + TileMatrix tileMatrix = map.Tiles; List list = []; if (items) { - foreach (var item in map.GetItemsAt(x, y)) + foreach (Item item in map.GetItemsAt(x, y)) { - // Placed Static items are captured (with hue/light) by the - // GetItemsInBounds pass below; skip them here to avoid duplicates. - if (item is Static) - { - continue; - } - - if (!range || item.Z >= min && item.Z <= max) + if (range && item.Z >= min && item.Z <= max) { list.Add(new StaticTile((ushort)item.ItemID, (sbyte)item.Z)); } } } - if (statics) + foreach (var staticTile in tileMatrix.GetStaticTiles(x, y)) { - foreach (var staticTile in tileMatrix.GetStaticTiles(x, y)) + if (range && staticTile.Z >= min && staticTile.Z <= max) { - if (!range || staticTile.Z >= min && staticTile.Z <= max) - { - list.Add(staticTile); - } + list.Add(staticTile); } } @@ -228,7 +174,7 @@ public class AddonGenerator using var target = PooledRefList.Create(); - foreach (var s in map.GetItemsInBounds(bounds)) + foreach (Static s in map.GetItemsInBounds(bounds)) { if (!range || s.Z >= min && s.Z <= max) { @@ -243,7 +189,7 @@ public class AddonGenerator } // Get correct bounds - foreach (var item in target) + foreach (Item item in target) { if (item.Z < center.Z) { @@ -256,11 +202,11 @@ public class AddonGenerator y2 = Math.Max(y2, item.Y); } - foreach (var p in tiles.Keys) + foreach (Point2D p in tiles.Keys) { - var list = tiles[p]; + List list = tiles[p]; - foreach (var t in list) + foreach (StaticTile t in list) { if (t.Z < center.Z) { @@ -279,59 +225,63 @@ public class AddonGenerator using var sb = ValueStringBuilder.Create(); - foreach (var p in tiles.Keys) + foreach (Point2D p in tiles.Keys) { - var list = tiles[p]; + List list = tiles[p]; - var xOffset = p.X - center.X; - var yOffset = p.Y - center.Y; + int xOffset = p.X - center.X; + int yOffset = p.Y - center.Y; - foreach (var t in list) + foreach (StaticTile t in list) { - var zOffset = t.Z - center.Z; - var id = t.ID - 16384; + int zOffset = t.Z - center.Z; + int id = t.ID - 16384; sb.Append($" AddComponent(new AddonComponent({id}), {xOffset}, {yOffset}, {zOffset});\n"); } } - foreach (var item in target) + foreach (Item item in target) { - var xOffset = item.X - center.X; - var yOffset = item.Y - center.Y; - var zOffset = item.Z - center.Z; + int xOffset = item.X - center.X; + int yOffset = item.Y - center.Y; + int zOffset = item.Z - center.Z; var light = (item.ItemData.Flags & TileFlag.LightSource) == TileFlag.LightSource ? item.Light : LightType.Empty; var hue = item.Hue; - sb.Append($" AddComponent(new AddonComponent({item.ItemID})"); + sb.Append(" AddComponent(\n"); if (hue != 0 || light != LightType.Empty) { - sb.Append(" {"); + sb.Append($" new AddonComponent({item.ItemID})\n"); + sb.Append(" {\n"); if (light != LightType.Empty) { - sb.Append($" Light = LightType.{light}"); - if (hue != 0) - { - sb.Append($", Hue = {hue}"); - } + sb.Append($" Light = LightType.{light},\n"); } - else + if (hue != 0) { - sb.Append($" Hue = {hue}"); + sb.Append($" Hue = {hue},\n"); } - sb.Append(" }"); + sb.Append(" },\n"); } - sb.Append($", {xOffset}, {yOffset}, {zOffset});\n"); + else + { + sb.Append($" new AddonComponent({item.ItemID}),\n"); + } + sb.Append($" {xOffset},\n"); + sb.Append($" {yOffset},\n"); + sb.Append($" {zOffset},\n"); + sb.Append(" )\n"); } - // Only pull in Server.Items when the generated addon lives in a different namespace. - var serverItemsNamespace = ns == "Server.Items" ? "" : "\nusing Server.Items;"; - var output = ExpandTemplate(serverItemsNamespace, ns, name, sb.AsSpan()); + string output = _template.Replace("{name}", name); + output = output.Replace("{namespace}", ns); + output = output.Replace("{components}", sb.ToString()); var path = Path.Combine(_outputDirectory, $"{name}Addon.cs"); - var folder = Path.GetDirectoryName(path); + string folder = Path.GetDirectoryName(path); PathUtility.EnsureDirectory(folder); using var writer = new StreamWriter(path, false); @@ -393,11 +343,11 @@ public class AddonGenerator AddCheck(20, 135, 2510, 2511, _state.Range, 2); AddLabel(40, 135, LabelHue, "Specify Z Range"); - AddLabel(50, 160, LabelHue, "Min"); + AddLabel(50, 160, LabelHue, "Max"); AddImageTiled(85, 175, 60, 1, 9304); AddTextEntry(85, 155, 60, 20, LabelHue, 2, _state.Min.ToString()); - AddLabel(160, 160, LabelHue, "Max"); + AddLabel(160, 160, LabelHue, "Min"); AddImageTiled(200, 175, 60, 1, 9304); AddTextEntry(200, 155, 60, 20, LabelHue, 3, _state.Max.ToString()); @@ -424,13 +374,13 @@ public class AddonGenerator var fail = !_state.Items && !_state.Statics; - if (!sbyte.TryParse(info.GetTextEntry(2), out var min)) + if (!sbyte.TryParse(info.GetTextEntry(2), out sbyte min)) { min = sbyte.MinValue; fail = true; } - if (!sbyte.TryParse(info.GetTextEntry(3), out var max)) + if (!sbyte.TryParse(info.GetTextEntry(3), out sbyte max)) { max = sbyte.MaxValue; fail = true; diff --git a/Projects/UOContent/Commands/BoundingBoxPicker.cs b/Projects/UOContent/Commands/BoundingBoxPicker.cs index 62bd16d11..4212c87fa 100644 --- a/Projects/UOContent/Commands/BoundingBoxPicker.cs +++ b/Projects/UOContent/Commands/BoundingBoxPicker.cs @@ -42,7 +42,7 @@ namespace Server return; } - var p = ip switch + Point3D p = ip switch { Item item => item.GetWorldTop(), Mobile m => m.Location, diff --git a/Projects/UOContent/Commands/ClearCommands.cs b/Projects/UOContent/Commands/ClearCommands.cs index c509e3981..42fd9d21e 100644 --- a/Projects/UOContent/Commands/ClearCommands.cs +++ b/Projects/UOContent/Commands/ClearCommands.cs @@ -11,7 +11,6 @@ public static class ClearCommands { CommandSystem.Register("ClearFacet", AccessLevel.Owner, ClearFacet_OnCommand); CommandSystem.Register("ClearAll", AccessLevel.Owner, ClearAll_OnCommand); - CommandSystem.Register("ClearXY", AccessLevel.Owner, ClearXY_OnCommand); } [Usage("ClearFacet")] @@ -25,8 +24,8 @@ public static class ClearCommands DeleteObjects(list, from, map.Name); } - [Usage("ClearAll")] - [Description("Deletes all items and mobiles in all facets. Players and their inventory will not be deleted.")] + [Usage( "ClearAll" )] + [Description( "Deletes all items and mobiles in all facets. Players and their inventory will not be deleted." )] public static void ClearAll_OnCommand(CommandEventArgs e) { var from = e.Mobile; @@ -35,52 +34,6 @@ public static class ClearCommands DeleteObjects(list, from, "globally"); } - [Usage("ClearXY x1 y1 x2 y2")] - [Description("Deletes all items and mobiles in the specified rectangular area on your facet. Players and their inventory will not be deleted.")] - public static void ClearXY_OnCommand(CommandEventArgs e) - { - if (e.Arguments.Length != 4 - || !int.TryParse(e.Arguments[0], out var x1) - || !int.TryParse(e.Arguments[1], out var y1) - || !int.TryParse(e.Arguments[2], out var x2) - || !int.TryParse(e.Arguments[3], out var y2)) - { - e.Mobile.SendMessage("Format: ClearXY x1 y1 x2 y2"); - return; - } - - var from = e.Mobile; - var map = from.Map; - - if (x2 < x1) - { - (x1, x2) = (x2, x1); - } - - if (y2 < y1) - { - (y1, y2) = (y2, y1); - } - - var rect = new Rectangle2D(x1, y1, x2 - x1 + 1, y2 - y1 + 1); - List list = []; - - foreach (var item in map.GetItemsInBounds(rect)) - { - list.Add(item); - } - - foreach (var mob in map.GetMobilesInBounds(rect)) - { - if (!mob.Player) - { - list.Add(mob); - } - } - - DeleteObjects(list, from, $"({x1}, {y1}) to ({x2}, {y2}) in {map.Name}"); - } - private static List GetObjects(Predicate predicate = null) { List list = []; diff --git a/Projects/UOContent/Commands/DragEffects.cs b/Projects/UOContent/Commands/DragEffects.cs index 4f4d2b96f..dac60f8d0 100644 --- a/Projects/UOContent/Commands/DragEffects.cs +++ b/Projects/UOContent/Commands/DragEffects.cs @@ -15,11 +15,11 @@ namespace Server.Commands { if (Mobile.DragEffects) { - e.Mobile.SendMessage("Drag effects are currently enabled."); + e.Mobile.SendMessage($"Drag effects are currently enabled."); } else { - e.Mobile.SendMessage("Drag effects are currently disabled."); + e.Mobile.SendMessage($"Drag effects are currently disabled."); } } else @@ -28,11 +28,11 @@ namespace Server.Commands if (Mobile.DragEffects) { - e.Mobile.SendMessage("Drag effects have been enabled."); + e.Mobile.SendMessage($"Drag effects have been enabled."); } else { - e.Mobile.SendMessage("Drag effects have been disabled."); + e.Mobile.SendMessage($"Drag effects have been disabled."); } } } diff --git a/Projects/UOContent/Commands/Dupe.cs b/Projects/UOContent/Commands/Dupe.cs index 2d715714e..e38a114f3 100644 --- a/Projects/UOContent/Commands/Dupe.cs +++ b/Projects/UOContent/Commands/Dupe.cs @@ -120,18 +120,6 @@ public static class Dupe for (var j = 0; j < src.Items.Count; j++) { - c = src.Items[j].GetType().GetConstructor(out var paramCount); - if (c == null) - { - continue; - } - - args = paramCount == 0 ? null : new object[paramCount]; - if (args != null) - { - Array.Fill(args, Type.Missing); - } - var subItem = DoDupe(src.Items[j], c, args, from); newItem.AddItem(subItem); } diff --git a/Projects/UOContent/Commands/GenCommands.cs b/Projects/UOContent/Commands/GenCommands.cs index 95a606c7f..0892e1539 100644 --- a/Projects/UOContent/Commands/GenCommands.cs +++ b/Projects/UOContent/Commands/GenCommands.cs @@ -57,18 +57,15 @@ public static class GenCommands options ); - // Template is in Distribution/Data/commands - var templatePath = Path.Combine(Core.BaseDirectory, "Data", "commands", "template.html"); - - var template = File.ReadAllText(templatePath) + var template = File.ReadAllText(Path.Combine(Core.BaseDirectory, "Data", "commands.html")) .Replace("", commandsJson.EscapeHtml()) .Replace("", modifiersJson.EscapeHtml()); - // Output to Distribution/web folder (typically ignored by servers for external systems) + // Write the new file to the web folder var webFolder = Path.Combine(Core.BaseDirectory, "web"); - Directory.CreateDirectory(webFolder); - var outputPath = Path.Combine(webFolder, "commands.html"); - File.WriteAllText(outputPath, template, Encoding.UTF8); - e.Mobile.SendMessage($"Commands webpage generated at {outputPath}."); + PathUtility.EnsureDirectory(webFolder); + var webPath = Path.Combine(webFolder, "commands.html"); + File.WriteAllText(webPath, template, Encoding.UTF8); + e.Mobile.SendMessage($"Commands webpage generated at {webPath}."); } } diff --git a/Projects/UOContent/Commands/Generic/Commands/BaseCommand.cs b/Projects/UOContent/Commands/Generic/Commands/BaseCommand.cs index 581189698..4d5a79d92 100644 --- a/Projects/UOContent/Commands/Generic/Commands/BaseCommand.cs +++ b/Projects/UOContent/Commands/Generic/Commands/BaseCommand.cs @@ -36,7 +36,7 @@ namespace Server.Commands.Generic return true; } - var mob = obj switch + Mobile mob = obj switch { Mobile m => m, Item item => item.RootParent as Mobile, diff --git a/Projects/UOContent/Commands/Generic/Commands/Commands.cs b/Projects/UOContent/Commands/Generic/Commands/Commands.cs index 102d5a2aa..5150595dc 100644 --- a/Projects/UOContent/Commands/Generic/Commands/Commands.cs +++ b/Projects/UOContent/Commands/Generic/Commands/Commands.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Reflection; using Server.Accounting; using Server.Engines.Help; using Server.Factions; @@ -9,7 +8,6 @@ using Server.Items; using Server.Mobiles; using Server.Multis; using Server.Network; -using Server.Network.Bans; using Server.Spells; namespace Server.Commands.Generic @@ -513,39 +511,36 @@ namespace Server.Commands.Generic public override bool ValidateArgs(BaseCommandImplementor impl, CommandEventArgs e) { - if (e.Length < 1) + if (e.Length >= 1) + { + var t = AssemblyHandler.FindTypeByName(e.GetString(0)); + + if (t == null) + { + e.Mobile.SendMessage("No type with that name was found."); + + var match = e.GetString(0).Trim(); + + if (match.Length < 3) + { + e.Mobile.SendMessage("Invalid search string."); + e.Mobile.SendGump(new AddGump(match, 0, Type.EmptyTypes, false)); + } + else + { + e.Mobile.SendGump(new AddGump(match, 0, AddGump.Match(match), true)); + } + } + else + { + return true; + } + } + else { e.Mobile.SendGump(new CategorizedAddGump(e.Mobile)); - return false; } - var match = e.GetString(0).Trim(); - if (e.GetContext("exactMatch", out var exactMatch)) - { - return true; - } - - exactMatch = AddGump.ExactMatch(match); - if (exactMatch != null) - { - e.SetContext("exactMatch", exactMatch); - return true; - } - - if (match.Length < 3) - { - e.Mobile.SendMessage("Invalid search string."); - e.Mobile.SendGump(new AddGump(match, 0, [], false)); - return false; - } - - if (!e.GetContext("matches", out var matches)) - { - matches = e.SetContext("matches", AddGump.MatchEmptyCtor(match)); - } - - e.Mobile.SendMessage("No type with that name was found."); - e.Mobile.SendGump(new AddGump(match, 0, matches, true)); return false; } @@ -556,20 +551,14 @@ namespace Server.Commands.Generic return; } - var p = ip switch + Point3D p = ip switch { Item item => item.GetWorldTop(), Mobile m => m.Location, _ => new Point3D(ip) }; - if (e.GetContext("exactMatch", out var exactMatch)) - { - Add.Invoke(e.Mobile, p, p, exactMatch, e.Arguments.AsSpan(1)); - return; - } - - e.Mobile.SendMessage("No type with that name was found."); + Add.Invoke(e.Mobile, p, p, e.Arguments); } } @@ -1155,8 +1144,7 @@ namespace Server.Commands.Generic try { - Firewall.Add(new SingleIpFirewallEntry(state.Address)); - BanChannel.Report(state.Address, TimeSpan.Zero, BanReasons.Manual); + AdminFirewall.Add(state.Address); AddResponse("They have been firewalled."); } catch (Exception ex) @@ -1211,7 +1199,7 @@ namespace Server.Commands.Generic { CommandLogging.WriteLine( from, - $"{from.AccessLevel} {CommandLogging.Format(from)} {(m_Ban ? "banning" : "kicking")} {CommandLogging.Format(targ)}" + $"{from.AccessLevel} {CommandLogging.Format(from)} {(m_Ban ? "banning" : "kicking")} {(CommandLogging.Format(targ))}" ); targ.Say(m_Ban ? "I've been banned." : "I've been kicked"); diff --git a/Projects/UOContent/Commands/Generic/Commands/DesignInsert.cs b/Projects/UOContent/Commands/Generic/Commands/DesignInsert.cs index c8d294a8f..345ff37c9 100644 --- a/Projects/UOContent/Commands/Generic/Commands/DesignInsert.cs +++ b/Projects/UOContent/Commands/Generic/Commands/DesignInsert.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Server.Collections; using Server.Gumps; using Server.Items; using Server.Multis; @@ -113,7 +112,7 @@ namespace Server.Commands.Generic if (okay) { - using var foundations = PooledRefQueue.Create(); + var foundations = new List(); flushToLog = list.Count > 20; for (var i = 0; i < list.Count; ++i) @@ -128,7 +127,7 @@ namespace Server.Commands.Generic if (!foundations.Contains(house)) { - foundations.Enqueue(house); + foundations.Add(house); } break; @@ -147,9 +146,9 @@ namespace Server.Commands.Generic } } - while (foundations.Count > 0) + foreach (var house in foundations) { - foundations.Dequeue().Delta(ItemDelta.Update); + house.Delta(ItemDelta.Update); } } else diff --git a/Projects/UOContent/Commands/Generic/Extensions/Compilers/ConditionalCompiler.cs b/Projects/UOContent/Commands/Generic/Extensions/Compilers/ConditionalCompiler.cs index 6f5af48d4..18e2a6505 100644 --- a/Projects/UOContent/Commands/Generic/Extensions/Compilers/ConditionalCompiler.cs +++ b/Projects/UOContent/Commands/Generic/Extensions/Compilers/ConditionalCompiler.cs @@ -267,7 +267,7 @@ namespace Server.Commands.Generic { var inverse = false; - var type = m_IgnoreCase ? typeof(InsensitiveStringHelpers) : typeof(OrdinalStringHelpers); + Type type = m_IgnoreCase ? typeof(InsensitiveStringHelpers) : typeof(OrdinalStringHelpers); string methodName; switch (m_Operator) @@ -279,25 +279,53 @@ namespace Server.Commands.Generic } case StringOperator.Equal: { - methodName = m_IgnoreCase ? "InsensitiveEquals" : "EqualsOrdinal"; + if (m_IgnoreCase) + { + methodName = "InsensitiveEquals"; + } + else + { + methodName = "EqualsOrdinal"; + } break; } case StringOperator.Contains: { - methodName = m_IgnoreCase ? "InsensitiveContains" : "ContainsOrdinal"; + if (m_IgnoreCase) + { + methodName = "InsensitiveContains"; + } + else + { + methodName = "ContainsOrdinal"; + } break; } case StringOperator.StartsWith: { - methodName = m_IgnoreCase ? "InsensitiveStartsWith" : "StartsWithOrdinal"; + if (m_IgnoreCase) + { + methodName = "InsensitiveStartsWith"; + } + else + { + methodName = "StartsWithOrdinal"; + } break; } case StringOperator.EndsWith: { - methodName = m_IgnoreCase ? "InsensitiveEndsWith" : "EndsWithOrdinal"; + if (m_IgnoreCase) + { + methodName = "InsensitiveEndsWith"; + } + else + { + methodName = "EndsWithOrdinal"; + } break; } @@ -314,7 +342,11 @@ namespace Server.Commands.Generic methodName, BindingFlags.Public | BindingFlags.Static, null, - [typeof(string), typeof(string)], + new[] + { + typeof(string), + typeof(string) + }, null ) ); @@ -348,9 +380,12 @@ namespace Server.Commands.Generic emitter.BeginCall( type.GetMethod( methodName, - BindingFlags.Public | BindingFlags.Static, + BindingFlags.Public | BindingFlags.Instance, null, - [typeof(string), typeof(string)], + new[] + { + typeof(string) + }, null ) ); diff --git a/Projects/UOContent/Commands/Generic/Implementors/BaseCommandImplementor.cs b/Projects/UOContent/Commands/Generic/Implementors/BaseCommandImplementor.cs index a8cae3741..bce4737a3 100644 --- a/Projects/UOContent/Commands/Generic/Implementors/BaseCommandImplementor.cs +++ b/Projects/UOContent/Commands/Generic/Implementors/BaseCommandImplementor.cs @@ -213,6 +213,8 @@ namespace Server.Commands.Generic public void RunCommand(Mobile from, object obj, BaseCommand command, string[] args) { + // try + // { var e = new CommandEventArgs(from, command.Commands[0], GenerateArgString(args), args); if (!command.ValidateArgs(this, e)) @@ -254,6 +256,11 @@ namespace Server.Commands.Generic } command.Flush(from, flushToLog); + // } + // catch ( Exception ex ) + // { + // from.SendMessage( ex.Message ); + // } } public virtual void Process(Mobile from, BaseCommand command, string[] args) diff --git a/Projects/UOContent/Commands/Generic/Implementors/ContainedCommandImplementor.cs b/Projects/UOContent/Commands/Generic/Implementors/ContainedCommandImplementor.cs index 7b76cbc14..225871532 100644 --- a/Projects/UOContent/Commands/Generic/Implementors/ContainedCommandImplementor.cs +++ b/Projects/UOContent/Commands/Generic/Implementors/ContainedCommandImplementor.cs @@ -67,8 +67,7 @@ namespace Server.Commands.Generic var list = new List(); - using var queue = cont.EnumerateItems(true, ext.IsValid); - foreach (var item in queue) + foreach (var item in cont.EnumerateItems(true, ext.IsValid)) { list.Add(item); } diff --git a/Projects/UOContent/Commands/Generic/Implementors/RegionCommandImplementor.cs b/Projects/UOContent/Commands/Generic/Implementors/RegionCommandImplementor.cs index eb4a3f8d9..c866e41dc 100644 --- a/Projects/UOContent/Commands/Generic/Implementors/RegionCommandImplementor.cs +++ b/Projects/UOContent/Commands/Generic/Implementors/RegionCommandImplementor.cs @@ -33,10 +33,8 @@ namespace Server.Commands.Generic if (mobiles) { - using var mobileList = reg.GetMobilesPooled(); - for (var i = 0; i < mobileList.Count; i++) + foreach (var mob in reg.GetMobiles()) { - var mob = mobileList[i]; if (BaseCommand.IsAccessible(from, mob) && ext.IsValid(mob)) { list.Add(mob); @@ -46,8 +44,7 @@ namespace Server.Commands.Generic if (items) { - using var itemList = reg.GetItemsPooled(); - foreach (var item in itemList) + foreach (var item in reg.GetItems()) { if (BaseCommand.IsAccessible(from, item) && ext.IsValid(item)) { diff --git a/Projects/UOContent/Commands/Generic/Implementors/SerialCommandImplementor.cs b/Projects/UOContent/Commands/Generic/Implementors/SerialCommandImplementor.cs index f9b1032a3..fca4324dd 100644 --- a/Projects/UOContent/Commands/Generic/Implementors/SerialCommandImplementor.cs +++ b/Projects/UOContent/Commands/Generic/Implementors/SerialCommandImplementor.cs @@ -15,7 +15,7 @@ namespace Server.Commands.Generic { if (e.Length >= 2) { - var serial = (Serial)e.GetUInt32(0); + Serial serial = (Serial)e.GetUInt32(0); object obj = null; diff --git a/Projects/UOContent/Commands/Generic/Implementors/SingleCommandImplementor.cs b/Projects/UOContent/Commands/Generic/Implementors/SingleCommandImplementor.cs index 033cb73f5..3220cd28b 100644 --- a/Projects/UOContent/Commands/Generic/Implementors/SingleCommandImplementor.cs +++ b/Projects/UOContent/Commands/Generic/Implementors/SingleCommandImplementor.cs @@ -36,7 +36,7 @@ namespace Server.Commands.Generic { e.Mobile.SendMessage("You do not have access to that command."); } - else + else if (command.ValidateArgs(this, e)) { Process(e.Mobile, command, e.Arguments); } diff --git a/Projects/UOContent/Commands/Handlers.cs b/Projects/UOContent/Commands/Handlers.cs index d3a9d3350..de1e831e2 100644 --- a/Projects/UOContent/Commands/Handlers.cs +++ b/Projects/UOContent/Commands/Handlers.cs @@ -1,6 +1,7 @@ +using System; using System.Collections.Generic; +using System.Text; using Server.Commands.Generic; -using Server.Text; using Server.Engines.Help; using Server.Gumps; using Server.Items; @@ -88,7 +89,7 @@ namespace Server.Commands if (!reg.IsDefault) { - using var builder = ValueStringBuilder.Create(256); + var builder = new StringBuilder(); builder.Append(reg); reg = reg.Parent; @@ -99,7 +100,7 @@ namespace Server.Commands reg = reg.Parent; } - from.SendMessage($"Your region is {builder.AsSpan()}."); + from.SendMessage($"Your region is {builder}."); } } } @@ -238,7 +239,7 @@ namespace Server.Commands for (var i = 0; i < pets.Count; ++i) { - var pet = pets[i]; + Mobile pet = pets[i]; if (pet is IMount mount) { @@ -299,7 +300,7 @@ namespace Server.Commands $"{m.AccessLevel} {CommandLogging.Format(m)} playing sound {index} (toAll={toAll})" ); - var buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); foreach (var state in m.GetClientsInRange(12)) { @@ -594,7 +595,7 @@ namespace Server.Commands list.Sort(); - using var sb = ValueStringBuilder.Create(256); + var sb = new StringBuilder(); if (list.Count > 0) { @@ -608,7 +609,7 @@ namespace Server.Commands if (sb.Length + 1 + v.Length >= 256) { m.SendAsciiMessage(0x482, sb.ToString()); - sb.Reset(); + sb = new StringBuilder(); sb.Append(v); } else @@ -665,11 +666,11 @@ namespace Server.Commands if (m.AutoPageNotify) { - m.SendMessage("Your auto-page-notify has been turned on."); + m.SendMessage($"Your auto-page-notify has been turned on."); } else { - m.SendMessage("Your auto-page-notify has been turned off."); + m.SendMessage($"Your auto-page-notify has been turned off."); } } diff --git a/Projects/UOContent/Commands/HelpInfo.cs b/Projects/UOContent/Commands/HelpInfo.cs index 0b0328458..0361fbadc 100644 --- a/Projects/UOContent/Commands/HelpInfo.cs +++ b/Projects/UOContent/Commands/HelpInfo.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Reflection; using Server.Commands.Generic; @@ -12,7 +11,6 @@ public static class HelpInfo { private static List _sortedHelpInfo; private static Dictionary _helpInfos; - private static Dictionary> _accessLevelCache; public static Dictionary HelpInfos { @@ -40,28 +38,6 @@ public static class HelpInfo } } - public static List GetCommandsForAccessLevel(AccessLevel accessLevel) - { - _accessLevelCache ??= []; - - if (_accessLevelCache.TryGetValue(accessLevel, out var cached)) - { - return cached; - } - - var list = new List(); - foreach (var c in SortedHelpInfo) - { - if (accessLevel >= c.AccessLevel) - { - list.Add(c); - } - } - - _accessLevelCache[accessLevel] = list; - return list; - } - public static void Configure() { CommandSystem.Register("HelpInfo", AccessLevel.Player, HelpInfo_OnCommand); @@ -98,7 +74,7 @@ public static class HelpInfo e.Mobile.SendMessage($"Command '{arg}' not found!"); } - e.Mobile.SendGump(new CommandListGump(0, e.Mobile)); + e.Mobile.SendGump(new CommandListGump(0, e.Mobile, null)); } public static void FillTable() @@ -112,30 +88,23 @@ public static class HelpInfo var mi = e.Handler.Method; - var usageAttr = mi.GetCustomAttribute(false); + var usageAttr = mi.GetCustomAttribute(typeof(UsageAttribute), false) as UsageAttribute; var usage = usageAttr?.Usage; - var descAttr = mi.GetCustomAttribute(false); - var desc = descAttr?.Description.FixHtml(); + var descAttr = mi.GetCustomAttribute(typeof(DescriptionAttribute), false) as DescriptionAttribute; + var desc = descAttr?.Description + .ReplaceOrdinal("<", "(") + .ReplaceOrdinal(">", ")"); if (e.Handler.Target != null && usage == null && desc == null) { continue; } - var aliasesAttr = mi.GetCustomAttribute(false); + var aliasesAttr = mi.GetCustomAttribute(typeof(AliasesAttribute), false) as AliasesAttribute; var aliases = aliasesAttr?.Aliases; - list.Add( - new CommandInfo( - e.AccessLevel, - e.Command, - aliases, - usage ?? e.Command, - null, - desc ?? "No description available." - ) - ); + list.Add(new CommandInfo(e.AccessLevel, e.Command, aliases, usage ?? e.Command, null, desc ?? "No description available.")); } for (var i = 0; i < TargetCommands.AllCommands.Count; ++i) @@ -159,6 +128,10 @@ public static class HelpInfo aliases[j] = cmds[j + 1]; } + desc = desc + .ReplaceOrdinal("<", "(") + .ReplaceOrdinal(">", ")") ?? "No description available."; + List modsList = []; foreach (var baseImpl in BaseCommandImplementor.Implementors) @@ -171,16 +144,7 @@ public static class HelpInfo var modifiers = modsList.ToArray(); - list.Add( - new CommandInfo( - command.AccessLevel, - cmd, - aliases, - usage, - modifiers, - desc.FixHtml() ?? "No description available." - ) - ); + list.Add(new CommandInfo(command.AccessLevel, cmd, aliases, usage, modifiers, desc)); } var commandImpls = BaseCommandImplementor.Implementors; @@ -201,7 +165,10 @@ public static class HelpInfo aliases[j] = cmds[j + 1]; } - desc = desc.FixHtml(); + desc = desc + .ReplaceOrdinal("<", ")") + .ReplaceOrdinal(">", ")"); + list.Add(new CommandInfo(command.AccessLevel, cmd, aliases, usage, null, desc)); } @@ -216,196 +183,163 @@ public static class HelpInfo } } - public class CommandListGump : DynamicGump + public class CommandListGump : BaseGridGump { private const int EntriesPerPage = 15; + private readonly List _list; - // Layout constants matching BaseGridGump defaults - private const int ContentWidth = 360; // 20 + 320 + 20 - private const int MaxRows = 16; // 1 header + 15 entries - private const string ColumnSpec = "20 320 20"; + private readonly int _page; - private static readonly GridEntryStyle Style = GridEntryStyle.Default; - - private AccessLevel _accessLevel; - private int _page; - - public override bool Singleton => true; - - public CommandListGump(int page, Mobile from) : base(30, 30) + public CommandListGump(int page, Mobile from, List list) : base(30, 30) { _page = page; - _accessLevel = from.AccessLevel; - } - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var list = GetCommandsForAccessLevel(_accessLevel); - var totalWidth = Style.GetTotalWidth(ContentWidth); - var totalHeight = Style.GetTotalHeight(MaxRows); + if (list == null) + { + _list = []; - // Calculate column positions with gaps - Span colPos = stackalloc int[3]; - Span colWidths = stackalloc int[3]; - GridCalculator.ComputeFromSpec( - ColumnSpec, - ContentWidth, - Style.ContentOriginX, - Style.OffsetSize, - colPos, - colWidths - ); + foreach (var c in SortedHelpInfo) + { + if (from.AccessLevel >= c.AccessLevel) + { + _list.Add(c); + } + } + } + else + { + _list = list; + } - // Background - builder.AddGridBackground(totalWidth, totalHeight, Style); - - var rowY = Style.ContentOriginY; - var totalPages = (list.Count + EntriesPerPage - 1) / EntriesPerPage; - - // Header row - var headerCell0 = new GridCell(colPos[0], rowY, colWidths[0], Style.EntryHeight); - var headerCell1 = new GridCell(colPos[1], rowY, colWidths[1], Style.EntryHeight); - var headerCell2 = new GridCell(colPos[2], rowY, colWidths[2], Style.EntryHeight); + AddNewPage(); if (_page > 0) { - builder.AddEntryArrowLeft(headerCell0, Style, 1); + AddEntryButton(20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight); } else { - builder.AddEntryHeader(headerCell0, Style); + AddEntryHeader(20); } - builder.AddImageTiled(headerCell1, Style.EntryGumpID); - builder.AddHtml(headerCell1, $"Page {_page + 1} of {totalPages}", Style.TextOffsetX, 0, align: TextAlignment.Center); + AddEntryHtml( + 320, + Center( + $"Page {_page + 1} of {(_list.Count + EntriesPerPage - 1) / EntriesPerPage}" + ) + ); - if ((_page + 1) * EntriesPerPage < list.Count) + if ((_page + 1) * EntriesPerPage < _list.Count) { - builder.AddEntryArrowRight(headerCell2, Style, 2); + AddEntryButton(20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight); } else { - builder.AddEntryHeader(headerCell2, Style); + AddEntryHeader(20); } - // Data rows var last = (int)AccessLevel.Player - 1; - var dataContentWidth = colWidths[0] + Style.OffsetSize + colWidths[1]; - // Extract the StringBuilder to avoid repeated allocations - using var sb = ValueStringBuilder.Create(); - - for (int i = _page * EntriesPerPage, line = 0; line < EntriesPerPage && i < list.Count; ++i, ++line) + for (int i = _page * EntriesPerPage, line = 0; line < EntriesPerPage && i < _list.Count; ++i, ++line) { - var c = list[i]; - - // Access level separator - if ((int)c.AccessLevel != last) + var c = _list[i]; + if (from.AccessLevel >= c.AccessLevel) { - rowY += Style.EntryHeight + Style.OffsetSize; - var sepContentCell = new GridCell(colPos[0], rowY, dataContentWidth, Style.EntryHeight); - var sepButtonCell = new GridCell(colPos[2], rowY, colWidths[2], Style.EntryHeight); - - builder.AddImageTiled(sepContentCell, Style.EntryGumpID); - builder.AddHtml(sepContentCell, $"{c.AccessLevel}", Style.TextOffsetX, 0, GumpTextColors.BrightRed); - builder.AddEntryHeader(sepButtonCell, Style); - line++; - } - - last = (int)c.AccessLevel; - - // Entry row - rowY += Style.EntryHeight + Style.OffsetSize; - var contentCell = new GridCell(colPos[0], rowY, dataContentWidth, Style.EntryHeight); - var buttonCell = new GridCell(colPos[2], rowY, colWidths[2], Style.EntryHeight); - - builder.AddImageTiled(contentCell, Style.EntryGumpID); - - if (c.Aliases?.Length > 0) - { - sb.Append($"{c.Name} ("); - for (var j = 0; j < c.Aliases.Length; ++j) + if ((int)c.AccessLevel != last) { - if (j != 0) - { - sb.Append(", "); - } - sb.Append(c.Aliases[j]); + AddNewLine(); + AddEntryHtml(20 + OffsetSize + 320, Color(c.AccessLevel.ToString(), 0xFF0000)); + AddEntryHeader(20); + line++; } - sb.Append(")"); - builder.AddHtml(contentCell, sb.RawChars[..sb.Length], Style.TextOffsetX, 0); - // Reset the SB for the next loop. - sb.Reset(); - } - else - { - builder.AddHtml(contentCell, c.Name, Style.TextOffsetX, 0); - } + last = (int)c.AccessLevel; - builder.AddEntryArrowRight(buttonCell, Style, 3 + i); + AddNewLine(); + string name; + if (c.Aliases?.Length > 0) + { + using var sb = ValueStringBuilder.Create(); + sb.Append($"{c.Name} ("); + for (var j = 0; j < c.Aliases.Length; ++j) + { + if (j != 0) + { + sb.Append(", "); + } + + sb.Append(c.Aliases[j]); + } + + sb.Append(")"); + name = sb.ToString(); + } + else + { + name = c.Name; + } + + AddEntryHtml(20 + OffsetSize + 320, name); + AddEntryButton(20, ArrowRightID1, ArrowRightID2, 3 + i, ArrowRightWidth, ArrowRightHeight); + } } + + FinishPage(); } public override void OnResponse(NetState sender, in RelayInfo info) { var m = sender.Mobile; var gumps = m.GetGumps(); - var list = GetCommandsForAccessLevel(_accessLevel); switch (info.ButtonID) { case 0: { gumps.Close(); - return; + break; } case 1: { - if (_page <= 0) + if (_page > 0) { - return; + gumps.Send(new CommandListGump(_page - 1, m, _list)); } - _page--; break; } case 2: { - if ((_page + 1) * EntriesPerPage >= list.Count) + if ((_page + 1) * EntriesPerPage < SortedHelpInfo.Count) { - return; + gumps.Send(new CommandListGump(_page + 1, m, _list)); } - _page++; break; } default: { var v = info.ButtonID - 3; - if (v < 0 || v >= list.Count) + if (v >= 0 && v < _list.Count) { - return; - } + var c = _list[v]; - var c = list[v]; - - if (m.AccessLevel >= c.AccessLevel) - { - gumps.Send(new CommandInfoGump(c)); - } - else - { - m.SendMessage("You no longer have access to that command."); - _accessLevel = m.AccessLevel; + if (m.AccessLevel >= c.AccessLevel) + { + gumps.Send(new CommandInfoGump(c)); + gumps.Send(new CommandListGump(_page, m, _list)); + } + else + { + m.SendMessage("You no longer have access to that command."); + gumps.Send(new CommandListGump(_page, m, null)); + } } break; } } - - m.SendGump(this); } } @@ -427,15 +361,15 @@ public static class HelpInfo builder.AddPage(); builder.AddBackground(0, 0, _width, _height, 5054); - builder.AddHtml(10, 10, _width - 20, 20, _info.Name, color: "#FF0000", align: TextAlignment.Center); + builder.AddHtml(10, 10, _width - 20, 20, _info.Name.Center(0xFF0000)); using var sb = ValueStringBuilder.Create(); sb.Append("Usage: "); - // Extends the StringBuilder and returns the Span for custom writing - var usage = sb.AppendSpan(_info.Usage.Length); - _info.Usage.CopyTo(usage); - usage.FixHtml(); + var usage = _info.Usage + .ReplaceOrdinal("<", "(") + .ReplaceOrdinal(">", ")"); + sb.Append(usage); sb.Append("
"); var aliases = _info.Aliases; @@ -458,7 +392,7 @@ public static class HelpInfo } sb.Append("AccessLevel: "); - sb.Append($"{_info.AccessLevel}"); + sb.Append(_info.AccessLevel.ToString()); sb.Append("
"); if (_info.Modifiers?.Length > 0) @@ -473,14 +407,12 @@ public static class HelpInfo sb.Append(_info.Modifiers[i]); } - sb.Append("
"); sb.Append("
"); } - sb.Append(_info.Description); - builder.AddHtml(10, 40, _width - 20, _height - 80, sb.AsSpan(true), background: false, scrollbar: true); + builder.AddHtml(10, 40, _width - 20, _height - 80, sb.ToString(), false, true); } } } diff --git a/Projects/UOContent/Commands/Logging.cs b/Projects/UOContent/Commands/Logging.cs index f505ae1d9..0b4111d3a 100644 --- a/Projects/UOContent/Commands/Logging.cs +++ b/Projects/UOContent/Commands/Logging.cs @@ -1,6 +1,6 @@ using System.IO; +using System.Text; using Server.Accounting; -using Server.Text; namespace Server.Commands { @@ -110,12 +110,11 @@ namespace Server.Commands return ip; } - using var sb = ValueStringBuilder.Create(ip.Length); - sb.Append(ip); + var sb = new StringBuilder(ip); for (var i = 0; i < m_NotSafe.Length; ++i) { - sb.Replace(m_NotSafe[i], '_', 0, sb.Length); + sb.Replace(m_NotSafe[i], '_'); } return sb.ToString(); diff --git a/Projects/UOContent/Commands/LoopStats.cs b/Projects/UOContent/Commands/LoopStats.cs deleted file mode 100644 index 012de68d8..000000000 --- a/Projects/UOContent/Commands/LoopStats.cs +++ /dev/null @@ -1,117 +0,0 @@ -#if EVENT_LOOP_PROFILING -using System; -using System.Globalization; -using System.IO; -using Server.Logging; - -namespace Server.Commands; - -/// -/// Reports the event-loop time decomposition recorded by . -/// Only compiled when the server is built with -p:EventLoopProfiling=true. -/// See dev-docs/debugging-event-loop.md for how to read the output. -/// -public static class LoopStats -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(LoopStats)); - - public static void Configure() - { - CommandSystem.Register("LoopStats", AccessLevel.Administrator, LoopStats_OnCommand); - } - - [Usage("LoopStats")] - [Description("Summarizes the last minute of event-loop time accounting and writes the full history to a CSV.")] - private static void LoopStats_OnCommand(CommandEventArgs e) - { - var history = EventLoopProfiler.History(); - if (history.Length == 0) - { - e.Mobile.SendMessage("No samples recorded yet."); - return; - } - - var window = Math.Min(60, history.Length); - - double wall = 0, sleep = 0, gc = 0, stolen = 0, stolenMax = 0; - long iterations = 0, sleeps = 0, lateWakes = 0, wheelLagMax = 0; - Span phases = stackalloc double[EventLoopProfiler.PhaseCount]; - Span phaseMax = stackalloc double[EventLoopProfiler.PhaseCount]; - - for (var i = history.Length - window; i < history.Length; i++) - { - ref var s = ref history[i]; - wall += s.WallMs; - sleep += s.SleepMs; - gc += s.GcPauseMs; - stolen += s.StolenMs; - iterations += s.Iterations; - sleeps += s.Sleeps; - lateWakes += s.LateWakes; - - if (s.StolenMs > stolenMax) - { - stolenMax = s.StolenMs; - } - - if (s.WheelLagMaxMs > wheelLagMax) - { - wheelLagMax = s.WheelLagMaxMs; - } - - for (var p = 0; p < EventLoopProfiler.PhaseCount; p++) - { - phases[p] += s.Phases[p]; - if (s.Phases[p] > phaseMax[p]) - { - phaseMax[p] = s.Phases[p]; - } - } - } - - e.Mobile.SendMessage($"Loop, last {window}s of wall time {wall:F0}ms:"); - e.Mobile.SendMessage($" sleep {100 * sleep / wall:F1}%, gc {100 * gc / wall:F1}%, stolen {100 * stolen / wall:F1}% (worst {stolenMax:F0}ms/s)"); - - for (var p = 0; p < EventLoopProfiler.PhaseCount; p++) - { - e.Mobile.SendMessage($" {(LoopPhase)p}: {100 * phases[p] / wall:F1}% (worst {phaseMax[p]:F0}ms/s)"); - } - - e.Mobile.SendMessage($" {iterations} iterations, {sleeps} sleeps, {lateWakes} late wakes, worst wheel lag {wheelLagMax}ms"); - - var path = Path.Combine(Core.BaseDirectory, $"loopstats-{Core.Now:yyyyMMdd-HHmmss}.csv"); - WriteCsv(path, history); - e.Mobile.SendMessage($"Full history ({history.Length} samples) written to {path}"); - logger.Information("Loop stats dumped to {Path}", path); - } - - private static void WriteCsv(string path, EventLoopProfiler.Sample[] history) - { - using var writer = new StreamWriter(path); - writer.Write("wallStart,wallMs,iterations,sleeps,sleepMs,sleepOvershootMaxMs,lateWakes,wheelLagMaxMs,wakesIssued,wakesElided,gcPauseMs,gen0,gen1,gen2,stolenMs"); - for (var p = 0; p < EventLoopProfiler.PhaseCount; p++) - { - writer.Write(','); - writer.Write((LoopPhase)p); - } - - writer.WriteLine(); - - for (var i = 0; i < history.Length; i++) - { - ref var s = ref history[i]; - writer.Write(string.Create( - CultureInfo.InvariantCulture, - $"{s.WallStart},{s.WallMs},{s.Iterations},{s.Sleeps},{s.SleepMs:F2},{s.SleepOvershootMaxMs:F2},{s.LateWakes},{s.WheelLagMaxMs},{s.WakesIssued},{s.WakesElided},{s.GcPauseMs:F2},{s.Gen0},{s.Gen1},{s.Gen2},{s.StolenMs:F2}" - )); - for (var p = 0; p < EventLoopProfiler.PhaseCount; p++) - { - writer.Write(','); - writer.Write(string.Create(CultureInfo.InvariantCulture, $"{s.Phases[p]:F2}")); - } - - writer.WriteLine(); - } - } -} -#endif diff --git a/Projects/UOContent/Commands/MovementDebugCommands.cs b/Projects/UOContent/Commands/MovementDebugCommands.cs deleted file mode 100644 index 90dbb0596..000000000 --- a/Projects/UOContent/Commands/MovementDebugCommands.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Server.Network; -using Server.Targeting; - -namespace Server.Commands; - -public static class MovementDebugCommands -{ - public static void Configure() - { - CommandSystem.Register("MovementDebug", AccessLevel.GameMaster, MovementDebug_OnCommand); - CommandSystem.Register("MovementStats", AccessLevel.GameMaster, MovementStats_OnCommand); - - // Subscribe to speed hack detection events and broadcast to online staff - MovementThrottle.OnSpeedHackDetected += OnSpeedHackDetected; - } - - private static void OnSpeedHackDetected( - NetState ns, - Mobile mobile, - float rate, - MovementThrottle.DetectionVerdict verdict, - string urgency) - { - if (mobile == null) - { - return; - } - - var hue = urgency switch - { - "HIGH" => 0x25, // Red - "MEDIUM" => 0x35, // Orange - _ => 0x3B // Yellow - }; - - CommandHandlers.BroadcastMessage( - AccessLevel.Counselor, - hue, - $"[{urgency}] Speed hack: {mobile.RawName} ({ns.Account?.Username}) " + - $"Rate:{rate:F2} Verdict:{verdict} @ {mobile.Location} {mobile.Map?.Name}" - ); - } - - [Usage("MovementDebug")] - [Description("Target a player to toggle verbose movement logging for their connection.")] - private static void MovementDebug_OnCommand(CommandEventArgs e) - { - e.Mobile.BeginTarget(-1, false, TargetFlags.None, MovementDebug_OnTarget); - e.Mobile.SendMessage("Target a player to toggle movement debug logging."); - } - - private static void MovementDebug_OnTarget(Mobile from, object obj) - { - if (obj is not Mobile target || target.NetState == null) - { - from.SendMessage("That is not a valid online player."); - return; - } - - var ns = target.NetState; - ns.MovementLogging = !ns.MovementLogging; - - var state = ns.MovementLogging ? "ENABLED" : "DISABLED"; - from.SendMessage($"Movement debug logging {state} for {target.RawName}."); - } - - [Usage("MovementStats")] - [Description("Target a player to see their current movement throttle state.")] - private static void MovementStats_OnCommand(CommandEventArgs e) - { - e.Mobile.BeginTarget(-1, false, TargetFlags.None, MovementStats_OnTarget); - e.Mobile.SendMessage("Target a player to view their movement stats."); - } - - private static void MovementStats_OnTarget(Mobile from, object obj) - { - if (obj is not Mobile target || target.NetState == null) - { - from.SendMessage("That is not a valid online player."); - return; - } - - var stats = MovementThrottle.GetMovementStats(target.NetState); - - from.SendMessage($"--- Movement Stats for {target.RawName} ---"); - from.SendMessage($"Rate: {stats.Rate:F3} ({stats.SampleCount} samples) | Verdict: {stats.Verdict} | Confidence: {stats.Confidence:P0}"); - from.SendMessage($"RTT: avg={stats.AverageRtt}ms last={stats.LastRtt}ms var={stats.RttVariance} stable={stats.StableConnection} samples={stats.RttSampleCount}"); - from.SendMessage($"Queue: depth={stats.QueueDepth} | Credit: {stats.MovementCredit}ms"); - from.SendMessage($"Packet rate: {stats.CurrentPacketRate}/s (peak: {stats.PeakPacketRate}/s)"); - from.SendMessage($"Burst: size={stats.BurstSize} gap={stats.PrecedingGap}ms | Sustained: {stats.SustainedSeconds}s"); - from.SendMessage($"Debug logging: {(stats.DebugLogging ? "ON" : "OFF")}"); - } -} diff --git a/Projects/UOContent/Commands/Object Creation/Add.cs b/Projects/UOContent/Commands/Object Creation/Add.cs index bfa6e9d1f..240d8dbd4 100644 --- a/Projects/UOContent/Commands/Object Creation/Add.cs +++ b/Projects/UOContent/Commands/Object Creation/Add.cs @@ -2,761 +2,698 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; -using Server.Gumps; +using System.Text; using Server.Items; -using Server.Text; using static Server.Attributes; using static Server.Types; -namespace Server.Commands; - -public static class Add +namespace Server.Commands { - public static void Configure() + public static class Add { - CommandSystem.Register("Tile", AccessLevel.GameMaster, Tile_OnCommand); - CommandSystem.Register("TileRXYZ", AccessLevel.GameMaster, TileRXYZ_OnCommand); - CommandSystem.Register("TileXYZ", AccessLevel.GameMaster, TileXYZ_OnCommand); - CommandSystem.Register("TileZ", AccessLevel.GameMaster, TileZ_OnCommand); - CommandSystem.Register("TileAvg", AccessLevel.GameMaster, TileAvg_OnCommand); - - CommandSystem.Register("Outline", AccessLevel.GameMaster, Outline_OnCommand); - CommandSystem.Register("OutlineRXYZ", AccessLevel.GameMaster, OutlineRXYZ_OnCommand); - CommandSystem.Register("OutlineXYZ", AccessLevel.GameMaster, OutlineXYZ_OnCommand); - CommandSystem.Register("OutlineZ", AccessLevel.GameMaster, OutlineZ_OnCommand); - CommandSystem.Register("OutlineAvg", AccessLevel.GameMaster, OutlineAvg_OnCommand); - } - - public static void Invoke( - Mobile from, Point3D start, Point3D end, string[] args, List packs = null, - bool outline = false, bool mapAvg = false - ) - { - var type = AddGump.ExactMatch(args[0]); - - if (type == null) + public static void Configure() { - from.SendMessage("No type with that name was found."); - return; + CommandSystem.Register("Tile", AccessLevel.GameMaster, Tile_OnCommand); + CommandSystem.Register("TileRXYZ", AccessLevel.GameMaster, TileRXYZ_OnCommand); + CommandSystem.Register("TileXYZ", AccessLevel.GameMaster, TileXYZ_OnCommand); + CommandSystem.Register("TileZ", AccessLevel.GameMaster, TileZ_OnCommand); + CommandSystem.Register("TileAvg", AccessLevel.GameMaster, TileAvg_OnCommand); + + CommandSystem.Register("Outline", AccessLevel.GameMaster, Outline_OnCommand); + CommandSystem.Register("OutlineRXYZ", AccessLevel.GameMaster, OutlineRXYZ_OnCommand); + CommandSystem.Register("OutlineXYZ", AccessLevel.GameMaster, OutlineXYZ_OnCommand); + CommandSystem.Register("OutlineZ", AccessLevel.GameMaster, OutlineZ_OnCommand); + CommandSystem.Register("OutlineAvg", AccessLevel.GameMaster, OutlineAvg_OnCommand); } - Invoke(from, start, end, type, args.AsSpan(1), packs, outline, mapAvg); - } - - public static void Invoke(Mobile from, Point3D start, Point3D end, ConstructorInfo ctor) - { - using var sb = ValueStringBuilder.Create(); - - sb.Append($"{from.AccessLevel} {CommandLogging.Format(from)} building "); - - if (start == end) + public static void Invoke( + Mobile from, Point3D start, Point3D end, string[] args, List packs = null, + bool outline = false, bool mapAvg = false + ) { - sb.Append($"at {start} in {from.Map}"); - } - else - { - sb.Append($"from {start} to {end} in {from.Map}"); - } + var sb = new StringBuilder(); - sb.Append($": {ctor.DeclaringType!.Name}"); + sb.Append($"{from.AccessLevel} {CommandLogging.Format(from)} building "); - CommandLogging.WriteLine(from, sb.ToString()); - - var watch = new Stopwatch(); - watch.Start(); - - Utility.FixPoints(ref start, ref end); - - // Handle optional parameters - var paramList = ctor.GetParameters(); - var parms = paramList.Length == 0 ? Array.Empty() : new object[paramList.Length]; - for (var i = 0; i < parms.Length; i++) - { - parms[i] = Type.Missing; - } - - var built = Build(from, start, end, ctor, parms, null, null, null); - - if (built <= 0) - { - SendUsage(ctor, from); - return; - } - - watch.Stop(); - - if (built == 1) - { - from.SendMessage($"{built} object generated in {watch.Elapsed.TotalSeconds:F2} seconds."); - } - else - { - from.SendMessage($"{built} objects generated in {watch.Elapsed.TotalSeconds:F2} seconds."); - } - } - - public static void Invoke( - Mobile from, Point3D start, Point3D end, Type type, - ReadOnlySpan args = default, List packs = null, bool outline = false, bool mapAvg = false - ) - { - using var sb = ValueStringBuilder.Create(); - - sb.Append($"{from.AccessLevel} {CommandLogging.Format(from)} building "); - - if (start == end) - { - sb.Append($"at {start} in {from.Map}"); - } - else - { - sb.Append($"from {start} to {end} in {from.Map}"); - } - - sb.Append($": {type.Name}"); - - string[,] props = null; - - var setArgsIndex = -1; - for (var i = 0; i < args.Length; ++i) - { - sb.Append($" \"{args[i]}\""); - - if (setArgsIndex != -1 || !args[i].InsensitiveEquals("set")) + if (start == end) { - continue; + sb.Append($"at {start} in {from.Map}"); + } + else + { + sb.Append($"from {start} to {end} in {from.Map}"); } - setArgsIndex = i; - var remains = args.Length - i - 1; + sb.Append(':'); - if (remains < 2) + for (var i = 0; i < args.Length; ++i) { - continue; + sb.Append($" \"{args[i]}\""); } - props = new string[remains / 2, 2]; + CommandLogging.WriteLine(from, sb.ToString()); - remains /= 2; + var name = args[0]; - for (var j = 0; j < remains; ++j) + FixArgs(ref args); + + string[,] props = null; + + for (var i = 0; i < args.Length; ++i) { - props[j, 0] = args[i + j * 2 + 1]; - props[j, 1] = args[i + j * 2 + 2]; - } - } - - CommandLogging.WriteLine(from, sb.ToString()); - - ReadOnlySpan ctorArgs = setArgsIndex != -1 ? args[..setArgsIndex] : args; - - var watch = new Stopwatch(); - watch.Start(); - - var built = BuildObjects(from, type, start, end, ctorArgs, props, packs, outline, mapAvg); - - if (built <= 0) - { - SendUsage(type, from); - return; - } - - watch.Stop(); - - if (built == 1) - { - from.SendMessage($"{built} object generated in {watch.Elapsed.TotalSeconds:F2} seconds."); - } - else - { - from.SendMessage($"{built} objects generated in {watch.Elapsed.TotalSeconds:F2} seconds."); - } - } - - public static int BuildObjects( - Mobile from, Type type, Point3D start, Point3D end, ReadOnlySpan args, string[,] props, - List packs, bool outline = false, bool mapAvg = false - ) - { - Utility.FixPoints(ref start, ref end); - - PropertyInfo[] realProps = null; - - if (props != null) - { - realProps = new PropertyInfo[props.GetLength(0)]; - - var allProps = - type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public); - - for (var i = 0; i < realProps.Length; ++i) - { - var propName = props[i, 0]; - var thisProp = Properties.GetPropertyInfoByName(from, allProps, propName, PropertyAccess.Write, out var failReason); - - if (failReason == null) + if (args[i].InsensitiveEquals("set")) { - realProps[i] = thisProp; + var remains = args.Length - i - 1; + + if (remains >= 2) + { + props = new string[remains / 2, 2]; + + remains /= 2; + + for (var j = 0; j < remains; ++j) + { + props[j, 0] = args[i + j * 2 + 1]; + props[j, 1] = args[i + j * 2 + 2]; + } + + FixSetString(ref args, i); + } + + break; + } + } + + var type = AssemblyHandler.FindTypeByName(name); + + if (!IsEntity(type)) + { + from.SendMessage("No type with that name was found."); + return; + } + + var watch = new Stopwatch(); + watch.Start(); + + var built = BuildObjects(from, type, start, end, args, props, packs, outline, mapAvg); + + if (built > 0) + { + watch.Stop(); + if (built == 1) + { + from.SendMessage($"{built} object generated in {watch.Elapsed.TotalSeconds:F2} seconds."); } else { - from.SendMessage(failReason); + from.SendMessage($"{built} objects generated in {watch.Elapsed.TotalSeconds:F2} seconds."); } } + else + { + SendUsage(type, from); + } } - var ctors = type.GetConstructors(); - - for (var i = 0; i < ctors.Length; ++i) + public static void FixSetString(ref string[] args, int index) { - var ctor = ctors[i]; + var old = args; + args = GC.AllocateUninitializedArray(index); - if (!IsConstructible(ctor, from.AccessLevel)) - { - continue; - } + Array.Copy(old, 0, args, 0, index); + } - // Handle optional constructors - var paramList = ctor.GetParameters(); - var totalParams = 0; - for (var j = 0; j < paramList.Length; j++) + public static void FixArgs(ref string[] args) + { + var old = args; + args = GC.AllocateUninitializedArray(args.Length - 1); + + Array.Copy(old, 1, args, 0, args.Length); + } + + public static int BuildObjects( + Mobile from, Type type, Point3D start, Point3D end, string[] args, string[,] props, + List packs, bool outline = false, bool mapAvg = false + ) + { + Utility.FixPoints(ref start, ref end); + + PropertyInfo[] realProps = null; + + if (props != null) { - if (!paramList[j].HasDefaultValue) + realProps = new PropertyInfo[props.GetLength(0)]; + + var allProps = + type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public); + + for (var i = 0; i < realProps.Length; ++i) { - totalParams++; + var propName = props[i, 0]; + var thisProp = Properties.GetPropertyInfoByName(from, allProps, propName, PropertyAccess.Write, out var failReason); + + if (failReason == null) + { + realProps[i] = thisProp; + } + else + { + from.SendMessage(failReason); + } } } - if (args.Length >= totalParams && args.Length <= paramList.Length) - { - var paramValues = ParseValues(paramList, args); + var ctors = type.GetConstructors(); - if (paramValues == null) + for (var i = 0; i < ctors.Length; ++i) + { + var ctor = ctors[i]; + + if (!IsConstructible(ctor, from.AccessLevel)) { continue; } - var built = Build(from, start, end, ctor, paramValues, props, realProps, packs, outline, mapAvg); - - if (built > 0) + // Handle optional constructors + var paramList = ctor.GetParameters(); + var totalParams = 0; + for (var j = 0; j < paramList.Length; j++) { - return built; - } - } - } - - return 0; - } - - public static object[] ParseValues(ParameterInfo[] paramList, ReadOnlySpan args) - { - var values = new object[paramList.Length]; - - for (int i = 0, a = 0; i < paramList.Length; i++) - { - var param = paramList[i]; - TryParse(param.ParameterType, a < args.Length ? args[a++] : null, out var value); - - if (value != null) - { - values[i] = value; - } - else if (param.HasDefaultValue) - { - values[i] = Type.Missing; - } - else - { - return null; - } - } - - return values; - } - - public static IEntity Build( - Mobile from, ConstructorInfo ctor, object[] values, string[,] props, - PropertyInfo[] realProps, ref bool sendError - ) - { - var built = ctor.Invoke(values); - - if (realProps != null) - { - var hadError = false; - - for (var i = 0; i < realProps.Length; ++i) - { - if (realProps[i] == null) - { - continue; - } - - var result = - Properties.InternalSetValue(from, built, built, realProps[i], props[i, 1], props[i, 1], false); - - if (result != "Property has been set.") - { - if (sendError) + if (!paramList[j].HasDefaultValue) { - from.SendMessage(result); + totalParams++; + } + } + + if (args.Length >= totalParams && args.Length <= paramList.Length) + { + var paramValues = ParseValues(paramList, args); + + if (paramValues == null) + { + continue; } - hadError = true; - } - } + var built = Build(from, start, end, ctor, paramValues, props, realProps, packs, outline, mapAvg); - if (hadError) - { - sendError = false; - } - } - - return (IEntity)built; - } - - public static int Build( - Mobile from, Point3D start, Point3D end, ConstructorInfo ctor, object[] values, - string[,] props, PropertyInfo[] realProps, List packs, bool outline = false, bool mapAvg = false - ) - { - try - { - var map = from.Map; - - var width = end.X - start.X + 1; - var height = end.Y - start.Y + 1; - - if (outline && (width < 3 || height < 3)) - { - outline = false; - } - - int objectCount; - - if (packs != null) - { - objectCount = packs.Count; - } - else if (outline) - { - objectCount = (width + height - 2) * 2; - } - else - { - objectCount = width * height; - } - - if (objectCount >= 20) - { - from.SendMessage($"Constructing {objectCount} objects, please wait."); - } - - var sendError = true; - - using var sb = ValueStringBuilder.Create(); - sb.Append("Serials: "); - - if (packs != null) - { - for (var i = 0; i < packs.Count; ++i) - { - var built = Build(from, ctor, values, props, realProps, ref sendError); - - sb.Append($"{built.Serial}; "); - - if (built is Item item) + if (built > 0) { - packs[i].DropItem(item); - } - else if (built is Mobile m) - { - m.MoveToWorld(new Point3D(start.X, start.Y, start.Z), map); + return built; } } } - else - { - var z = start.Z; - for (var x = start.X; x <= end.X; ++x) + return 0; + } + + public static object[] ParseValues(ParameterInfo[] paramList, string[] args) + { + var values = new object[paramList.Length]; + + for (int i = 0, a = 0; i < paramList.Length; i++) + { + var param = paramList[i]; + TryParse(param.ParameterType, a < args.Length ? args[a++] : null, out var value); + + if (value != null) { - for (var y = start.Y; y <= end.Y; ++y) + values[i] = value; + } + else if (param.HasDefaultValue) + { + values[i] = Type.Missing; + } + else + { + return null; + } + } + + return values; + } + + public static IEntity Build( + Mobile from, ConstructorInfo ctor, object[] values, string[,] props, + PropertyInfo[] realProps, ref bool sendError + ) + { + var built = ctor.Invoke(values); + + if (realProps != null) + { + var hadError = false; + + for (var i = 0; i < realProps.Length; ++i) + { + if (realProps[i] == null) { - if (outline && x != start.X && x != end.X && y != start.Y && y != end.Y) + continue; + } + + var result = + Properties.InternalSetValue(from, built, built, realProps[i], props[i, 1], props[i, 1], false); + + if (result != "Property has been set.") + { + if (sendError) { - continue; + from.SendMessage(result); } - if (mapAvg) - { - z = map.GetAverageZ(x, y); - } + hadError = true; + } + } + if (hadError) + { + sendError = false; + } + } + + return (IEntity)built; + } + + public static int Build( + Mobile from, Point3D start, Point3D end, ConstructorInfo ctor, object[] values, + string[,] props, PropertyInfo[] realProps, List packs, bool outline = false, bool mapAvg = false + ) + { + try + { + var map = from.Map; + + var width = end.X - start.X + 1; + var height = end.Y - start.Y + 1; + + if (outline && (width < 3 || height < 3)) + { + outline = false; + } + + int objectCount; + + if (packs != null) + { + objectCount = packs.Count; + } + else if (outline) + { + objectCount = (width + height - 2) * 2; + } + else + { + objectCount = width * height; + } + + if (objectCount >= 20) + { + from.SendMessage($"Constructing {objectCount} objects, please wait."); + } + + var sendError = true; + + var sb = new StringBuilder(); + sb.Append("Serials: "); + + if (packs != null) + { + for (var i = 0; i < packs.Count; ++i) + { var built = Build(from, ctor, values, props, realProps, ref sendError); sb.Append($"{built.Serial}; "); if (built is Item item) { - item.MoveToWorld(new Point3D(x, y, z), map); + packs[i].DropItem(item); } else if (built is Mobile m) { - m.MoveToWorld(new Point3D(x, y, z), map); + m.MoveToWorld(new Point3D(start.X, start.Y, start.Z), map); } } } + else + { + var z = start.Z; + + for (var x = start.X; x <= end.X; ++x) + { + for (var y = start.Y; y <= end.Y; ++y) + { + if (outline && x != start.X && x != end.X && y != start.Y && y != end.Y) + { + continue; + } + + if (mapAvg) + { + z = map.GetAverageZ(x, y); + } + + var built = Build(from, ctor, values, props, realProps, ref sendError); + + sb.Append($"{built.Serial}; "); + + if (built is Item item) + { + item.MoveToWorld(new Point3D(x, y, z), map); + } + else if (built is Mobile m) + { + m.MoveToWorld(new Point3D(x, y, z), map); + } + } + } + } + + CommandLogging.WriteLine(from, sb.ToString()); + + return objectCount; } - - CommandLogging.WriteLine(from, sb.ToString()); - - return objectCount; - } - catch (Exception ex) - { - Console.WriteLine(ex); - return 0; - } - } - - public static void SendUsage(Type type, Mobile from) - { - var ctors = type.GetConstructors(); - var foundCtor = false; - - for (var i = 0; i < ctors.Length; ++i) - { - var ctor = ctors[i]; - - if (!IsConstructible(ctor, from.AccessLevel)) + catch (Exception ex) { - continue; + Console.WriteLine(ex); + return 0; + } + } + + public static void SendUsage(Type type, Mobile from) + { + var ctors = type.GetConstructors(); + var foundCtor = false; + + for (var i = 0; i < ctors.Length; ++i) + { + var ctor = ctors[i]; + + if (!IsConstructible(ctor, from.AccessLevel)) + { + continue; + } + + if (!foundCtor) + { + foundCtor = true; + from.SendMessage("Usage:"); + } + + SendCtor(type, ctor, from); } if (!foundCtor) { - foundCtor = true; - from.SendMessage("Usage:"); + from.SendMessage("That type is not marked constructible."); } - - SendCtor(ctor, from); } - if (!foundCtor) + public static void SendCtor(Type type, ConstructorInfo ctor, Mobile from) { - from.SendMessage("That type is not marked constructible."); - } - } + var paramList = ctor.GetParameters(); - public static void SendUsage(ConstructorInfo ctor, Mobile from) - { - if (!IsConstructible(ctor, from.AccessLevel)) - { - from.SendMessage("That type is not marked constructible."); - return; - } + var sb = new StringBuilder(); - from.SendMessage("Usage:"); - SendCtor(ctor, from); - } + sb.Append(type.Name); - public static void SendCtor(ConstructorInfo ctor, Mobile from) - { - var paramList = ctor.GetParameters(); - - using var sb = ValueStringBuilder.Create(); - - sb.Append(ctor.DeclaringType!.Name); - - for (var i = 0; i < paramList.Length; ++i) - { - if (i != 0) + for (var i = 0; i < paramList.Length; ++i) { - sb.Append(','); - } - - sb.Append(' '); - - sb.Append(paramList[i].ParameterType.Name); - sb.Append(' '); - sb.Append(paramList[i].Name); - } - - from.SendMessage(sb.ToString()); - } - - private static void TileBox_Callback(Mobile from, Point3D start, Point3D end, TileState ts) - { - var mapAvg = false; - - switch (ts.m_ZType) - { - case TileZType.Fixed: + if (i != 0) { - start.Z = end.Z = ts.m_FixedZ; - break; + sb.Append(','); } - case TileZType.MapAverage: + + sb.Append(' '); + + sb.Append(paramList[i].ParameterType.Name); + sb.Append(' '); + sb.Append(paramList[i].Name); + } + + from.SendMessage(sb.ToString()); + } + + private static void TileBox_Callback(Mobile from, Point3D start, Point3D end, TileState ts) + { + var mapAvg = false; + + switch (ts.m_ZType) + { + case TileZType.Fixed: + { + start.Z = end.Z = ts.m_FixedZ; + break; + } + case TileZType.MapAverage: + { + mapAvg = true; + break; + } + } + + Invoke(from, start, end, ts.m_Args, null, ts.m_Outline, mapAvg); + } + + private static void Internal_OnCommand(CommandEventArgs e, bool outline) + { + var from = e.Mobile; + + if (e.Length >= 1) + { + BoundingBoxPicker.Begin( + from, + (map, start, end) => + TileBox_Callback(from, start, end, new TileState(TileZType.Start, 0, e.Arguments, outline)) + ); + } + else + { + if (outline) { - mapAvg = true; - break; + from.SendMessage($"Format: Outline [params] [set {{ ...}}]"); } + else + { + from.SendMessage($"Format: Tile [params] [set {{ ...}}]"); + } + } } - Invoke(from, start, end, ts.m_Args, null, ts.m_Outline, mapAvg); - } - - private static void Internal_OnCommand(CommandEventArgs e, bool outline) - { - var from = e.Mobile; - - if (e.Length >= 1) + private static void InternalRXYZ_OnCommand(CommandEventArgs e, bool outline) { - BoundingBoxPicker.Begin( - from, - (_, start, end) => - TileBox_Callback(from, start, end, new TileState(TileZType.Start, 0, e.Arguments, outline)) - ); - } - else - { - if (outline) + if (e.Length >= 6) { - from.SendMessage("Format: Outline [params] [set { ...}]"); + var p = new Point3D(e.Mobile.X + e.GetInt32(0), e.Mobile.Y + e.GetInt32(1), e.Mobile.Z + e.GetInt32(4)); + var p2 = new Point3D(p.X + e.GetInt32(2) - 1, p.Y + e.GetInt32(3) - 1, p.Z); + + var subArgs = new string[e.Length - 5]; + + for (var i = 0; i < subArgs.Length; ++i) + { + subArgs[i] = e.Arguments[i + 5]; + } + + Invoke(e.Mobile, p, p2, subArgs, null, outline); } else { - from.SendMessage("Format: Tile [params] [set { ...}]"); + if (outline) + { + e.Mobile.SendMessage($"Format: OutlineRXYZ [params] [set {{ ...}}]"); + } + else + { + e.Mobile.SendMessage($"Format: TileRXYZ [params] [set {{ ...}}]"); + } } } - } - private static void InternalRXYZ_OnCommand(CommandEventArgs e, bool outline) - { - if (e.Length >= 6) + private static void InternalXYZ_OnCommand(CommandEventArgs e, bool outline) { - var p = new Point3D(e.Mobile.X + e.GetInt32(0), e.Mobile.Y + e.GetInt32(1), e.Mobile.Z + e.GetInt32(4)); - var p2 = new Point3D(p.X + e.GetInt32(2) - 1, p.Y + e.GetInt32(3) - 1, p.Z); - - var subArgs = new string[e.Length - 5]; - - for (var i = 0; i < subArgs.Length; ++i) + if (e.Length >= 6) { - subArgs[i] = e.Arguments[i + 5]; - } + var p = new Point3D(e.GetInt32(0), e.GetInt32(1), e.GetInt32(4)); + var p2 = new Point3D(p.X + e.GetInt32(2) - 1, p.Y + e.GetInt32(3) - 1, e.GetInt32(4)); - Invoke(e.Mobile, p, p2, subArgs, null, outline); - } - else - { - if (outline) - { - e.Mobile.SendMessage("Format: OutlineRXYZ [params] [set { ...}]"); + var subArgs = new string[e.Length - 5]; + + for (var i = 0; i < subArgs.Length; ++i) + { + subArgs[i] = e.Arguments[i + 5]; + } + + Invoke(e.Mobile, p, p2, subArgs, null, outline); } else { - e.Mobile.SendMessage("Format: TileRXYZ [params] [set { ...}]"); + if (outline) + { + e.Mobile.SendMessage($"Format: OutlineXYZ [params] [set {{ ...}}]" ); + } + else + { + e.Mobile.SendMessage($"Format: TileXYZ [params] [set {{ ...}}]"); + } } } - } - private static void InternalXYZ_OnCommand(CommandEventArgs e, bool outline) - { - if (e.Length >= 6) + private static void InternalZ_OnCommand(CommandEventArgs e, bool outline) { - var p = new Point3D(e.GetInt32(0), e.GetInt32(1), e.GetInt32(4)); - var p2 = new Point3D(p.X + e.GetInt32(2) - 1, p.Y + e.GetInt32(3) - 1, e.GetInt32(4)); + var from = e.Mobile; - var subArgs = new string[e.Length - 5]; - - for (var i = 0; i < subArgs.Length; ++i) + if (e.Length >= 2) { - subArgs[i] = e.Arguments[i + 5]; - } + var subArgs = new string[e.Length - 1]; - Invoke(e.Mobile, p, p2, subArgs, null, outline); - } - else - { - if (outline) - { - e.Mobile.SendMessage("Format: OutlineXYZ [params] [set { ...}]" ); + for (var i = 0; i < subArgs.Length; ++i) + { + subArgs[i] = e.Arguments[i + 1]; + } + + BoundingBoxPicker.Begin( + from, + (map, start, end) => + TileBox_Callback(from, start, end, new TileState(TileZType.Fixed, e.GetInt32(0), subArgs, outline)) + ); } else { - e.Mobile.SendMessage("Format: TileXYZ [params] [set { ...}]"); + if (outline) + { + from.SendMessage($"Format: OutlineZ [params] [set {{ ...}}]"); + } + else + { + from.SendMessage($"Format: TileZ [params] [set {{ ...}}]"); + } } } - } - private static void InternalZ_OnCommand(CommandEventArgs e, bool outline) - { - var from = e.Mobile; - - if (e.Length >= 2) + private static void InternalAvg_OnCommand(CommandEventArgs e, bool outline) { - var subArgs = new string[e.Length - 1]; + var from = e.Mobile; - for (var i = 0; i < subArgs.Length; ++i) + if (e.Length >= 1) { - subArgs[i] = e.Arguments[i + 1]; - } - - BoundingBoxPicker.Begin( - from, - (_, start, end) => - TileBox_Callback(from, start, end, new TileState(TileZType.Fixed, e.GetInt32(0), subArgs, outline)) - ); - } - else - { - if (outline) - { - from.SendMessage("Format: OutlineZ [params] [set { ...}]"); + BoundingBoxPicker.Begin( + from, + (map, start, end) => + TileBox_Callback(from, start, end, new TileState(TileZType.MapAverage, 0, e.Arguments, outline)) + ); } else { - from.SendMessage("Format: TileZ [params] [set { ...}]"); + if (outline) + { + from.SendMessage($"Format: OutlineAvg [params] [set {{ ...}}]"); + } + else + { + from.SendMessage($"Format: TileAvg [params] [set {{ ...}}]"); + } } } - } - private static void InternalAvg_OnCommand(CommandEventArgs e, bool outline) - { - var from = e.Mobile; - - if (e.Length >= 1) + [Usage("Tile [params] [set { ...}]"), Description( + "Tiles an item or npc by name into a targeted bounding box. Optional constructor parameters. Optional set property list." + )] + public static void Tile_OnCommand(CommandEventArgs e) { - BoundingBoxPicker.Begin( - from, - (_, start, end) => - TileBox_Callback(from, start, end, new TileState(TileZType.MapAverage, 0, e.Arguments, outline)) - ); + Internal_OnCommand(e, false); } - else + + [Usage("TileRXYZ [params] [set { ...}]"), Description( + "Tiles an item or npc by name into a given bounding box, (x, y) parameters are relative to your characters position. Optional constructor parameters. Optional set property list." + )] + public static void TileRXYZ_OnCommand(CommandEventArgs e) { - if (outline) + InternalRXYZ_OnCommand(e, false); + } + + [Usage("TileXYZ [params] [set { ...}]"), Description( + "Tiles an item or npc by name into a given bounding box. Optional constructor parameters. Optional set property list." + )] + public static void TileXYZ_OnCommand(CommandEventArgs e) + { + InternalXYZ_OnCommand(e, false); + } + + [Usage("TileZ [params] [set { ...}]"), Description( + "Tiles an item or npc by name into a targeted bounding box at a fixed Z location. Optional constructor parameters. Optional set property list." + )] + public static void TileZ_OnCommand(CommandEventArgs e) + { + InternalZ_OnCommand(e, false); + } + + [Usage("TileAvg [params] [set { ...}]"), Description( + "Tiles an item or npc by name into a targeted bounding box on the map's average Z elevation. Optional constructor parameters. Optional set property list." + )] + public static void TileAvg_OnCommand(CommandEventArgs e) + { + InternalAvg_OnCommand(e, false); + } + + [Usage("Outline [params] [set { ...}]"), Description( + "Tiles an item or npc by name around a targeted bounding box. Optional constructor parameters. Optional set property list." + )] + public static void Outline_OnCommand(CommandEventArgs e) + { + Internal_OnCommand(e, true); + } + + [Usage("OutlineRXYZ [params] [set { ...}]"), Description( + "Tiles an item or npc by name around a given bounding box, (x, y) parameters are relative to your characters position. Optional constructor parameters. Optional set property list." + )] + public static void OutlineRXYZ_OnCommand(CommandEventArgs e) + { + InternalRXYZ_OnCommand(e, true); + } + + [Usage("OutlineXYZ [params] [set { ...}]"), Description( + "Tiles an item or npc by name around a given bounding box. Optional constructor parameters. Optional set property list." + )] + public static void OutlineXYZ_OnCommand(CommandEventArgs e) + { + InternalXYZ_OnCommand(e, true); + } + + [Usage("OutlineZ [params] [set { ...}]"), Description( + "Tiles an item or npc by name around a targeted bounding box at a fixed Z location. Optional constructor parameters. Optional set property list." + )] + public static void OutlineZ_OnCommand(CommandEventArgs e) + { + InternalZ_OnCommand(e, true); + } + + [Usage("OutlineAvg [params] [set { ...}]"), Description( + "Tiles an item or npc by name around a targeted bounding box on the map's average Z elevation. Optional constructor parameters. Optional set property list." + )] + public static void OutlineAvg_OnCommand(CommandEventArgs e) + { + InternalAvg_OnCommand(e, true); + } + + private enum TileZType + { + Start, + Fixed, + MapAverage + } + + private class TileState + { + public readonly string[] m_Args; + public readonly int m_FixedZ; + public readonly bool m_Outline; + public readonly TileZType m_ZType; + + public TileState(TileZType zType, int fixedZ, string[] args, bool outline) { - from.SendMessage("Format: OutlineAvg [params] [set { ...}]"); + m_ZType = zType; + m_FixedZ = fixedZ; + m_Args = args; + m_Outline = outline; } - else - { - from.SendMessage("Format: TileAvg [params] [set { ...}]"); - } - } - } - - [Usage("Tile [params] [set { ...}]")] - [Description( - "Tiles an item or npc by name into a targeted bounding box. Optional constructor parameters. Optional set property list." - )] - public static void Tile_OnCommand(CommandEventArgs e) - { - Internal_OnCommand(e, false); - } - - [Usage("TileRXYZ [params] [set { ...}]")] - [Description( - "Tiles an item or npc by name into a given bounding box, (x, y) parameters are relative to your characters position. Optional constructor parameters. Optional set property list." - )] - public static void TileRXYZ_OnCommand(CommandEventArgs e) - { - InternalRXYZ_OnCommand(e, false); - } - - [Usage("TileXYZ [params] [set { ...}]")] - [Description( - "Tiles an item or npc by name into a given bounding box. Optional constructor parameters. Optional set property list." - )] - public static void TileXYZ_OnCommand(CommandEventArgs e) - { - InternalXYZ_OnCommand(e, false); - } - - [Usage("TileZ [params] [set { ...}]")] - [Description( - "Tiles an item or npc by name into a targeted bounding box at a fixed Z location. Optional constructor parameters. Optional set property list." - )] - public static void TileZ_OnCommand(CommandEventArgs e) - { - InternalZ_OnCommand(e, false); - } - - [Usage("TileAvg [params] [set { ...}]")] - [Description( - "Tiles an item or npc by name into a targeted bounding box on the map's average Z elevation. Optional constructor parameters. Optional set property list." - )] - public static void TileAvg_OnCommand(CommandEventArgs e) - { - InternalAvg_OnCommand(e, false); - } - - [Usage("Outline [params] [set { ...}]")] - [Description( - "Tiles an item or npc by name around a targeted bounding box. Optional constructor parameters. Optional set property list." - )] - public static void Outline_OnCommand(CommandEventArgs e) - { - Internal_OnCommand(e, true); - } - - [Usage("OutlineRXYZ [params] [set { ...}]")] - [Description( - "Tiles an item or npc by name around a given bounding box, (x, y) parameters are relative to your characters position. Optional constructor parameters. Optional set property list." - )] - public static void OutlineRXYZ_OnCommand(CommandEventArgs e) - { - InternalRXYZ_OnCommand(e, true); - } - - [Usage("OutlineXYZ [params] [set { ...}]")] - [Description( - "Tiles an item or npc by name around a given bounding box. Optional constructor parameters. Optional set property list." - )] - public static void OutlineXYZ_OnCommand(CommandEventArgs e) - { - InternalXYZ_OnCommand(e, true); - } - - [Usage("OutlineZ [params] [set { ...}]")] - [Description( - "Tiles an item or npc by name around a targeted bounding box at a fixed Z location. Optional constructor parameters. Optional set property list." - )] - public static void OutlineZ_OnCommand(CommandEventArgs e) - { - InternalZ_OnCommand(e, true); - } - - [Usage("OutlineAvg [params] [set { ...}]")] - [Description( - "Tiles an item or npc by name around a targeted bounding box on the map's average Z elevation. Optional constructor parameters. Optional set property list." - )] - public static void OutlineAvg_OnCommand(CommandEventArgs e) - { - InternalAvg_OnCommand(e, true); - } - - private enum TileZType - { - Start, - Fixed, - MapAverage - } - - private class TileState - { - public readonly string[] m_Args; - public readonly int m_FixedZ; - public readonly bool m_Outline; - public readonly TileZType m_ZType; - - public TileState(TileZType zType, int fixedZ, string[] args, bool outline) - { - m_ZType = zType; - m_FixedZ = fixedZ; - m_Args = args; - m_Outline = outline; } } } diff --git a/Projects/UOContent/Commands/Object Creation/AddGump.cs b/Projects/UOContent/Commands/Object Creation/AddGump.cs index fc38b9ad1..7e8bfb33e 100644 --- a/Projects/UOContent/Commands/Object Creation/AddGump.cs +++ b/Projects/UOContent/Commands/Object Creation/AddGump.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Reflection; using Server.Network; using Server.Targeting; @@ -11,13 +10,13 @@ public class AddGump : DynamicGump private static readonly Type _typeofItem = typeof(Item); private static readonly Type _typeofMobile = typeof(Mobile); private readonly int _page; - private readonly ConstructorInfo[] _searchResults; + private readonly Type[] _searchResults; private readonly string _searchString; private readonly bool _explicitSearch; public override bool Singleton => true; - public AddGump(string searchString, int page, ConstructorInfo[] searchResults, bool explicitSearch) : base(50, 50) + public AddGump(string searchString, int page, Type[] searchResults, bool explicitSearch) : base(50, 50) { _searchString = searchString; _searchResults = searchResults; @@ -51,7 +50,7 @@ public class AddGump : DynamicGump { var index = i % 10; - builder.AddLabel(44, 39 + index * 20, 0x480, _searchResults[i].DeclaringType!.Name); + builder.AddLabel(44, 39 + index * 20, 0x480, _searchResults[i].Name); builder.AddButton(10, 39 + index * 20, 4023, 4025, 4 + i); } } @@ -103,83 +102,42 @@ public class AddGump : DynamicGump private static void AddMenu_OnCommand(CommandEventArgs e) { var val = e.ArgString.Trim(); - ConstructorInfo[] ctors; + Type[] types; var explicitSearch = false; if (val.Length == 0) { - ctors = []; + types = Type.EmptyTypes; } else if (val.Length < 3) { e.Mobile.SendMessage("Invalid search string."); - ctors = []; + types = Type.EmptyTypes; } else { - ctors = MatchEmptyCtor(val); + types = Match(val); explicitSearch = true; } - e.Mobile.SendGump(new AddGump(val, 0, ctors, explicitSearch)); + e.Mobile.SendGump(new AddGump(val, 0, types, explicitSearch)); } - private static bool ExactMatch(string match, Assembly assembly, out Type type) + private static void Match(string match, Type[] types, HashSet results) { - if (!_mobileItemTypes.TryGetValue(assembly, out var mobileItemTypes)) + if (match.Length == 0) { - List typeList = []; - var types = AssemblyHandler.GetTypeCache(assembly).Types; - for (var i = 0; i < types.Length; i++) - { - var t = types[i]; - if (_typeofMobile.IsAssignableFrom(t) || _typeofItem.IsAssignableFrom(t)) - { - typeList.Add(t); - } - } - - _mobileItemTypes[assembly] = mobileItemTypes = typeList.ToArray(); + return; } - for (var i = 0; i < mobileItemTypes.Length; i++) + match = match.ToLower(); + + for (var i = 0; i < types.Length; i++) { - var t = mobileItemTypes[i]; + var t = types[i]; - if (t.Name.InsensitiveEquals(match)) - { - type = t; - return true; - } - } - - type = null; - return false; - } - - private static void MatchEmptyCtor(string match, Assembly assembly, List results) - { - if (!_mobileItemTypes.TryGetValue(assembly, out var mobileItemTypes)) - { - List typeList = []; - var types = AssemblyHandler.GetTypeCache(assembly).Types; - for (var i = 0; i < types.Length; i++) - { - var t = types[i]; - if (_typeofMobile.IsAssignableFrom(t) || _typeofItem.IsAssignableFrom(t)) - { - typeList.Add(t); - } - } - - _mobileItemTypes[assembly] = mobileItemTypes = typeList.ToArray(); - } - - for (var i = 0; i < mobileItemTypes.Length; i++) - { - var t = mobileItemTypes[i]; - - if (!t.Name.InsensitiveContains(match)) + if (!(_typeofMobile.IsAssignableFrom(t) || _typeofItem.IsAssignableFrom(t)) || + !t.Name.InsensitiveContains(match) || results.Contains(t)) { continue; } @@ -200,70 +158,47 @@ public class AddGump : DynamicGump } } - if (isEmptyCtor && ctor.GetCustomAttributes(Types.OfConstructible, false).Length > 0) + if (isEmptyCtor && ctors[j].IsDefined(typeof(ConstructibleAttribute), false)) { - results.Add(ctor); + results.Add(t); break; } } } } - private static readonly Dictionary _mobileItemTypes = []; - - public static ConstructorInfo[] MatchEmptyCtor(string match) + public static Type[] Match(string match) { - if (string.IsNullOrWhiteSpace(match)) + var results = new HashSet(); + Type[] types; + + var asms = AssemblyHandler.Assemblies; + + for (var i = 0; i < asms.Length; ++i) { - return []; + types = AssemblyHandler.GetTypeCache(asms[i]).Types; + Match(match, types, results); } - match = match.ToLower(); - - List results = []; - MatchEmptyCtor(match, Core.Assembly, results); - - for (var i = 0; i < AssemblyHandler.Assemblies.Length; ++i) - { - MatchEmptyCtor(match, AssemblyHandler.Assemblies[i], results); - } + types = AssemblyHandler.GetTypeCache(Core.Assembly).Types; + Match(match, types, results); if (results.Count == 0) { - return []; + return Array.Empty(); } - var finalResults = results.ToArray(); - Array.Sort(finalResults, ConstructorNameComparer.Instance); + var finalResults = new Type[results.Count]; + var index = 0; + foreach (var t in results) + { + finalResults[index++] = t; + } + Array.Sort(finalResults, TypeNameComparer.Instance); return finalResults; } - public static Type ExactMatch(string match) - { - if (string.IsNullOrWhiteSpace(match)) - { - return null; - } - - match = match.ToLower(); - - if (ExactMatch(match, Core.Assembly, out var type)) - { - return type; - } - - for (var i = 0; i < AssemblyHandler.Assemblies.Length; ++i) - { - if (ExactMatch(match, AssemblyHandler.Assemblies[i], out type)) - { - return type; - } - } - - return null; - } - public override void OnResponse(NetState sender, in RelayInfo info) { var from = sender.Mobile; @@ -281,7 +216,7 @@ public class AddGump : DynamicGump } else { - from.SendGump(new AddGump(match, 0, MatchEmptyCtor(match), true)); + from.SendGump(new AddGump(match, 0, Match(match), true)); } break; @@ -324,30 +259,29 @@ public class AddGump : DynamicGump } } - private class ConstructorNameComparer : IComparer + private class TypeNameComparer : IComparer { - public static readonly ConstructorNameComparer Instance = new(); - public int Compare(ConstructorInfo x, ConstructorInfo y) => - x?.DeclaringType!.Name.CompareOrdinal(y?.DeclaringType!.Name) ?? 0; + public static readonly TypeNameComparer Instance = new(); + public int Compare(Type x, Type y) => string.CompareOrdinal(x?.Name, y?.Name); } public class InternalTarget : Target { - private readonly int _page; - private readonly ConstructorInfo[] _searchResults; - private readonly string _searchString; - private readonly ConstructorInfo _ctor; + private readonly int m_Page; + private readonly Type[] m_SearchResults; + private readonly string m_SearchString; + private readonly Type m_Type; - public InternalTarget(ConstructorInfo ctor, ConstructorInfo[] searchResults, string searchString, int page) : base( + public InternalTarget(Type type, Type[] searchResults, string searchString, int page) : base( -1, true, TargetFlags.None ) { - _ctor = ctor; - _searchResults = searchResults; - _searchString = searchString; - _page = page; + m_Type = type; + m_SearchResults = searchResults; + m_SearchString = searchString; + m_Page = page; } protected override void OnTarget(Mobile from, object o) @@ -357,22 +291,23 @@ public class AddGump : DynamicGump return; } - var p = ip switch + Point3D p = ip switch { Item item => item.GetWorldTop(), Mobile m => m.Location, _ => new Point3D(ip) }; - Commands.Add.Invoke(from, new Point3D(p), new Point3D(p), _ctor); - from.Target = new InternalTarget(_ctor, _searchResults, _searchString, _page); + Commands.Add.Invoke(from, new Point3D(p), new Point3D(p), new[] { m_Type.Name }); + + from.Target = new InternalTarget(m_Type, m_SearchResults, m_SearchString, m_Page); } protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) { if (cancelType == TargetCancelType.Canceled) { - from.SendGump(new AddGump(_searchString, _page, _searchResults, true)); + from.SendGump(new AddGump(m_SearchString, m_Page, m_SearchResults, true)); } } } diff --git a/Projects/UOContent/Commands/Object Creation/CAGCategory.cs b/Projects/UOContent/Commands/Object Creation/CAGCategory.cs index f07a09d60..3e3c062e0 100644 --- a/Projects/UOContent/Commands/Object Creation/CAGCategory.cs +++ b/Projects/UOContent/Commands/Object Creation/CAGCategory.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CAGCategory.cs * * * diff --git a/Projects/UOContent/Commands/Object Creation/CAGLoader.cs b/Projects/UOContent/Commands/Object Creation/CAGLoader.cs index cbded692c..add061da8 100644 --- a/Projects/UOContent/Commands/Object Creation/CAGLoader.cs +++ b/Projects/UOContent/Commands/Object Creation/CAGLoader.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CAGLoader.cs * * * @@ -118,7 +118,7 @@ public static class CAGLoader if (cagObj.Hue == null) { - var hue = item.Hue & 0x7FFF; + int hue = item.Hue & 0x7FFF; hue = (hue & 0x4000) != 0 ? 0 : hue; cagObj.Hue = hue == 0 ? null : hue; } @@ -133,7 +133,7 @@ public static class CAGLoader if (cagObj.Hue == null) { - var hue = m.Hue & 0x7FFF; + int hue = m.Hue & 0x7FFF; hue = (hue & 0x4000) != 0 ? 0 : hue; cagObj.Hue = hue == 0 ? null : hue; } diff --git a/Projects/UOContent/Commands/Object Creation/CAGNode.cs b/Projects/UOContent/Commands/Object Creation/CAGNode.cs index 122b7266d..f0c2d0886 100644 --- a/Projects/UOContent/Commands/Object Creation/CAGNode.cs +++ b/Projects/UOContent/Commands/Object Creation/CAGNode.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CAGNode.cs * * * diff --git a/Projects/UOContent/Commands/Object Creation/CAGObject.cs b/Projects/UOContent/Commands/Object Creation/CAGObject.cs index 0a5f931dc..44ab6d049 100644 --- a/Projects/UOContent/Commands/Object Creation/CAGObject.cs +++ b/Projects/UOContent/Commands/Object Creation/CAGObject.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CAGObject.cs * * * diff --git a/Projects/UOContent/Commands/Object Creation/CategorizedAddGump.cs b/Projects/UOContent/Commands/Object Creation/CategorizedAddGump.cs index 57bb5d837..b46812115 100644 --- a/Projects/UOContent/Commands/Object Creation/CategorizedAddGump.cs +++ b/Projects/UOContent/Commands/Object Creation/CategorizedAddGump.cs @@ -8,18 +8,26 @@ namespace Server.Gumps { public class CategorizedAddGump : Gump { - private const int EntryHeight = PropsConfig.EntryHeight + 4; + private static readonly int EntryHeight = PropsConfig.EntryHeight + 4; - private const int SetOffsetY = PropsConfig.SetOffsetY + (EntryHeight - 20) / 2 / 2; + private static readonly int SetOffsetY = PropsConfig.SetOffsetY + (EntryHeight - 20) / 2 / 2; - private const int PrevOffsetY = PropsConfig.PrevOffsetY + (EntryHeight - 20) / 2 / 2; + private static readonly int PrevOffsetY = PropsConfig.PrevOffsetY + (EntryHeight - 20) / 2 / 2; - private const int NextOffsetY = PropsConfig.NextOffsetY + (EntryHeight - 20) / 2 / 2; + private static readonly int NextOffsetY = PropsConfig.NextOffsetY + (EntryHeight - 20) / 2 / 2; - private const int EntryWidth = 180; - private const int EntryCount = 15; + private static readonly int PrevLabelOffsetX = PrevWidth + 1; + private static readonly int PrevLabelOffsetY = 0; - private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; + + private static readonly int EntryWidth = 180; + private static readonly int EntryCount = 15; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; private readonly CAGCategory m_Category; @@ -48,21 +56,107 @@ namespace Server.Gumps var count = Math.Clamp(nodes.Length - page * EntryCount, 0, EntryCount); + var totalHeight = OffsetSize + (EntryHeight + OffsetSize) * (count + 1); + AddPage(0); - this.AddPropsFrame(TotalWidth, count + 1, out var x, out var y, EntryHeight); - this.AddPropsHeaderWithBack( - TotalWidth, ref x, ref y, m_Category.Title, - m_Category.Parent != null, 1, - page > 0, 2, - (page + 1) * EntryCount < nodes.Length, 3, - nextType: GumpButtonType.Reply, nextParam: 1, - entryHeight: EntryHeight + AddBackground(0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + totalHeight, + OffsetGumpID ); + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; + + if (OldStyle) + { + AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID); + } + else + { + AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID); + } + + if (m_Category.Parent != null) + { + AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1); + + if (PrevLabel) + { + AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous"); + } + } + + x += PrevWidth + OffsetSize; + + var emptyWidth = TotalWidth - PrevWidth * 2 - NextWidth - OffsetSize * 5 - + (OldStyle ? SetWidth + OffsetSize : 0); + + if (!OldStyle) + { + AddImageTiled( + x - (OldStyle ? OffsetSize : 0), + y, + emptyWidth + (OldStyle ? OffsetSize * 2 : 0), + EntryHeight, + EntryGumpID + ); + } + + AddHtml( + x + TextOffsetX, + y + (EntryHeight - 20) / 2, + emptyWidth - TextOffsetX, + EntryHeight, + $"
{m_Category.Title}
" + ); + + x += emptyWidth + OffsetSize; + + if (OldStyle) + { + AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID); + } + else + { + AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID); + } + + if (page > 0) + { + AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 2); + + if (PrevLabel) + { + AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous"); + } + } + + x += PrevWidth + OffsetSize; + + if (!OldStyle) + { + AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID); + } + + if ((page + 1) * EntryCount < nodes.Length) + { + AddButton(x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 3, GumpButtonType.Reply, 1); + + if (NextLabel) + { + AddLabel(x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next"); + } + } + for (int i = 0, index = page * EntryCount; i < EntryCount && index < nodes.Length; ++i, ++index) { - PropsLayout.NextRow(ref x, ref y, EntryHeight); + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; var node = nodes[index]; @@ -93,7 +187,7 @@ namespace Server.Gumps Console.WriteLine("Type {0} does not have a valid item id or shrink table entry.", obj.Type); } - var bounds = ItemBounds.Bounds[itemID]; + var bounds = ItemBounds.Table[itemID]; if (itemID != 1 && bounds.Height < EntryHeight * 2) { diff --git a/Projects/UOContent/Commands/Object Creation/Decorate.cs b/Projects/UOContent/Commands/Object Creation/Decorate.cs index b41a7c758..8746fbf46 100644 --- a/Projects/UOContent/Commands/Object Creation/Decorate.cs +++ b/Projects/UOContent/Commands/Object Creation/Decorate.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using CommunityToolkit.HighPerformance; using Server.Collections; -using Server.Engines.PlayerMurderSystem; using Server.Engines.Quests.Haven; using Server.Engines.Quests.Necro; using Server.Engines.Spawners; @@ -41,11 +40,6 @@ namespace Server.Commands Generate("Data/Decoration/Malas", Map.Malas); Generate("Data/Decoration/Tokuno", Map.Tokuno); - if (PlayerMurderSystem.BountiesEnabled) - { - Generate("Data/Decoration/BountyBoards", Map.Felucca); - } - m_Mobile.SendMessage($"World generating complete. {m_Count} items were generated."); } @@ -594,15 +588,6 @@ namespace Server.Commands sp.HomeRange = Utility.ToInt32(m_Params[i].AsSpan()[++indexOf..]); } } - else if (m_Params[i].StartsWithOrdinal("WalkingRange")) - { - var indexOf = m_Params[i].IndexOfOrdinal('='); - - if (indexOf >= 0) - { - sp.WalkingRange = Utility.ToInt32(m_Params[i].AsSpan()[++indexOf..]); - } - } else if (m_Params[i].StartsWithOrdinal("Running")) { var indexOf = m_Params[i].IndexOfOrdinal('='); @@ -885,77 +870,6 @@ namespace Server.Commands kt.ItemID = m_ItemID; } } - else if (item is InteractionTeleporter itp) - { - itp.ItemID = m_ItemID; - - for (var i = 0; i < m_Params.Length; ++i) - { - if (m_Params[i].StartsWithOrdinal("PointDest")) - { - var indexOf = m_Params[i].IndexOfOrdinal('='); - - if (indexOf >= 0) - { - itp.PointDest = Point3D.Parse(m_Params[i][++indexOf..]); - } - } - else if (m_Params[i].StartsWithOrdinal("MapDest")) - { - var indexOf = m_Params[i].IndexOfOrdinal('='); - - if (indexOf >= 0) - { - itp.MapDest = Map.Parse(m_Params[i][++indexOf..]); - } - } - else if (m_Params[i].StartsWithOrdinal("SourceEffect")) - { - var indexOf = m_Params[i].IndexOfOrdinal('='); - - if (indexOf >= 0) - { - itp.SourceEffect = Utility.ToBoolean(m_Params[i][++indexOf..]); - } - } - else if (m_Params[i].StartsWithOrdinal("DestEffect")) - { - var indexOf = m_Params[i].IndexOfOrdinal('='); - - if (indexOf >= 0) - { - itp.DestEffect = Utility.ToBoolean(m_Params[i][++indexOf..]); - } - } - else if (m_Params[i].StartsWithOrdinal("SoundID")) - { - var indexOf = m_Params[i].IndexOfOrdinal('='); - - if (indexOf >= 0) - { - itp.SoundID = Utility.ToInt32(m_Params[i].AsSpan()[++indexOf..]); - } - } - else if (m_Params[i].StartsWithOrdinal("Delay")) - { - var indexOf = m_Params[i].IndexOfOrdinal('='); - - if (indexOf >= 0) - { - itp.Delay = TimeSpan.Parse(m_Params[i][++indexOf..]); - } - } - else if (m_Params[i].StartsWithOrdinal("Name")) - { - var indexOf = m_Params[i].IndexOfOrdinal('='); - - if (indexOf >= 0) - { - item.Name = m_Params[i][++indexOf..]; - } - } - } - } else if (item is Teleporter tp) { for (var i = 0; i < m_Params.Length; ++i) diff --git a/Projects/UOContent/Commands/StaffAccess.cs b/Projects/UOContent/Commands/StaffAccess.cs index 69387baee..af355866d 100644 --- a/Projects/UOContent/Commands/StaffAccess.cs +++ b/Projects/UOContent/Commands/StaffAccess.cs @@ -65,7 +65,7 @@ public static class StaffAccess } var accessLevelArgument = e.GetString(0)?.Trim().ToLower(); - var newAccessLevel = AccessLevel.Player; + AccessLevel newAccessLevel = AccessLevel.Player; var validAccessLevel = !string.IsNullOrEmpty(accessLevelArgument) && _accessLevelByString.TryGetValue(accessLevelArgument, out newAccessLevel); @@ -85,7 +85,7 @@ public static class StaffAccess if ((originalAccessLevel ?? m.AccessLevel) < newAccessLevel) { - m.SendMessage($"You cannot set your staff access to {newAccessLevel}."); + m.SendMessage($"You cannot set your staff access to {newAccessLevel.ToString()}."); return; } @@ -96,6 +96,6 @@ public static class StaffAccess } m.AccessLevel = newAccessLevel; - m.SendMessage($"Staff access set to {newAccessLevel}."); + m.SendMessage($"Staff access set to {newAccessLevel.ToString()}."); } } diff --git a/Projects/UOContent/Commands/VisibilityList.cs b/Projects/UOContent/Commands/VisibilityList.cs index ddad258c2..0026398bb 100644 --- a/Projects/UOContent/Commands/VisibilityList.cs +++ b/Projects/UOContent/Commands/VisibilityList.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; using Server.Mobiles; @@ -73,7 +74,7 @@ namespace Server.Commands if (list.Count > 0) { - var removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); + Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); for (var i = 0; i < list.Count; ++i) { @@ -103,8 +104,9 @@ namespace Server.Commands { var list = pm.VisibilityList; - if (list.Remove(targ)) + if (list.Contains(targ)) { + list.Remove(targ); pm.SendMessage($"{targ.Name} has been removed from your visibility list."); } else diff --git a/Projects/UOContent/Compression/ManagedArchive.cs b/Projects/UOContent/Compression/ManagedArchive.cs deleted file mode 100644 index 32142d9e4..000000000 --- a/Projects/UOContent/Compression/ManagedArchive.cs +++ /dev/null @@ -1,279 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ManagedArchive.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Formats.Tar; -using System.IO; -using System.IO.Compression; -using Server.Logging; -using ZstdNet; - -namespace Server.Compression; - -/// -/// Cross-platform archive creation and extraction using System.Formats.Tar + ZstdNet (native libzstd). -/// Single-pass streaming: TarWriter → CompressionStream → FileStream. -/// -public static class ManagedArchive -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ManagedArchive)); - - /// - /// Creates a .tar.zst archive from one or more source directories. - /// Writes to a temp file first, then atomically renames to the destination. - /// - /// Directories to include in the archive. - /// Final .tar.zst file path. - /// Base path for computing relative entry names. - /// ZSTD compression level (1-22, default 3). - /// The number of entries written, or -1 on failure. - public static int CreateTarZstd( - IEnumerable sourcePaths, - string destinationFile, - string relativeTo, - int compressionLevel = 3) - { - var tempFile = destinationFile + ".tmp"; - - try - { - new FileInfo(destinationFile).EnsureDirectory(); - - using var fileStream = new FileStream( - tempFile, - FileMode.Create, - FileAccess.Write, - FileShare.None, - bufferSize: 81920 - ); - - using var compressionOptions = new CompressionOptions(compressionLevel); - using var zstdStream = new CompressionStream(fileStream, compressionOptions); - using var tarWriter = new TarWriter(zstdStream, TarEntryFormat.Pax, leaveOpen: true); - - var entryCount = 0; - long totalBytes = 0; - - foreach (var sourcePath in sourcePaths) - { - if (!Directory.Exists(sourcePath)) - { - logger.Warning("Source path does not exist, skipping: {Path}", sourcePath); - continue; - } - - var sourceDir = new DirectoryInfo(sourcePath); - var relativeName = Path.GetRelativePath(relativeTo, sourcePath); - - foreach (var file in sourceDir.EnumerateFiles("*", SearchOption.AllDirectories)) - { - var entryName = Path.Combine(relativeName, Path.GetRelativePath(sourcePath, file.FullName)) - .Replace('\\', '/'); - - tarWriter.WriteEntry(file.FullName, entryName); - entryCount++; - totalBytes += file.Length; - - if (entryCount % 100 == 0) - { - logger.Debug( - "Archive progress: {Count} entries, {Size:F1} MB", - entryCount, - totalBytes / 1048576.0 - ); - } - } - } - - // Flush tar → zstd → file - tarWriter.Dispose(); - zstdStream.Dispose(); - fileStream.Dispose(); - - // Atomic rename on same filesystem - if (File.Exists(destinationFile)) - { - File.Delete(destinationFile); - } - - File.Move(tempFile, destinationFile); - - logger.Information( - "Created archive at {Path} ({Count} entries, {Size:F1} MB)", - destinationFile, - entryCount, - totalBytes / 1048576.0 - ); - - return entryCount; - } - catch (Exception ex) - { - logger.Error(ex, "Failed to create archive at {Path}", destinationFile); - - try - { - if (File.Exists(tempFile)) - { - File.Delete(tempFile); - } - } - catch - { - // Best-effort cleanup - } - - return -1; - } - } - - /// - /// Extracts a .tar.zst archive to a directory. - /// - public static bool ExtractTarZstd(string archiveFile, string outputDirectory) - { - try - { - Directory.CreateDirectory(outputDirectory); - - using var fileStream = new FileStream( - archiveFile, - FileMode.Open, - FileAccess.Read, - FileShare.Read, - bufferSize: 81920 - ); - - using var zstdStream = new DecompressionStream(fileStream); - return ExtractTarFromStream(zstdStream, outputDirectory); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to extract archive {File}", archiveFile); - return false; - } - } - - /// - /// Extracts a .tar.gz archive to a directory. - /// - public static bool ExtractTarGz(string archiveFile, string outputDirectory) - { - try - { - Directory.CreateDirectory(outputDirectory); - - using var fileStream = new FileStream( - archiveFile, - FileMode.Open, - FileAccess.Read, - FileShare.Read, - bufferSize: 81920 - ); - - using var gzStream = new GZipStream(fileStream, CompressionMode.Decompress); - return ExtractTarFromStream(gzStream, outputDirectory); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to extract archive {File}", archiveFile); - return false; - } - } - - /// - /// Extracts a plain .tar archive to a directory. - /// - public static bool ExtractTar(string archiveFile, string outputDirectory) - { - try - { - Directory.CreateDirectory(outputDirectory); - - using var fileStream = new FileStream( - archiveFile, - FileMode.Open, - FileAccess.Read, - FileShare.Read, - bufferSize: 81920 - ); - - return ExtractTarFromStream(fileStream, outputDirectory); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to extract archive {File}", archiveFile); - return false; - } - } - - private static bool ExtractTarFromStream(Stream stream, string outputDirectory) - { - using var tarReader = new TarReader(stream); - - while (tarReader.GetNextEntry() is { } entry) - { - var destPath = Path.Combine(outputDirectory, entry.Name); - - var destDir = Path.GetDirectoryName(destPath); - if (destDir != null) - { - Directory.CreateDirectory(destDir); - } - - if (entry.EntryType is TarEntryType.RegularFile or TarEntryType.V7RegularFile) - { - entry.ExtractToFile(destPath, overwrite: true); - } - } - - return true; - } - - /// - /// Counts entries in a .tar.zst archive without extracting content. - /// Used for verification after archive creation. - /// - public static int CountEntries(string archiveFile) - { - try - { - using var fileStream = new FileStream( - archiveFile, - FileMode.Open, - FileAccess.Read, - FileShare.Read, - bufferSize: 81920 - ); - - using var zstdStream = new DecompressionStream(fileStream); - using var tarReader = new TarReader(zstdStream); - - var count = 0; - while (tarReader.GetNextEntry() is not null) - { - count++; - } - - return count; - } - catch (Exception ex) - { - logger.Error(ex, "Failed to count entries in archive {File}", archiveFile); - return -1; - } - } -} diff --git a/Projects/UOContent/Compression/TarArchive.cs b/Projects/UOContent/Compression/TarArchive.cs new file mode 100755 index 000000000..db37a7de5 --- /dev/null +++ b/Projects/UOContent/Compression/TarArchive.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Net.Http; +using Server.Text; + +namespace Server.Compression +{ + public static class TarArchive + { + private const string _libArchiveWindowsUrl = @"https://www.libarchive.org/downloads/libarchive-v3.5.2-win64.zip"; + private static string _pathToTar; + + private static string GetPathToTar() + { + if (!Core.IsWindows || File.Exists(@"C:\Windows\system32\tar.exe")) + { + return "tar"; + } + + var tarPath = Path.Combine(Core.BaseDirectory, "bsdtar/bsdtar.exe"); + return File.Exists(tarPath) ? tarPath : DownloadTarForWindows(); + } + + private static int RunTar(string arguments, string compressionProgramPath) + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = _pathToTar, + Arguments = arguments + } + }; + + if (compressionProgramPath != null) + { + var envPaths = $"{process.StartInfo.EnvironmentVariables["PATH"]}:{compressionProgramPath}"; + process.StartInfo.EnvironmentVariables["PATH"] = envPaths; + } + + process.Start(); + process.WaitForExit(); + + return process.ExitCode; + } + + private static string DownloadTarForWindows() + { + var tempDir = PathUtility.EnsureRandomPath(Path.GetTempPath()); + + var libarchiveFile = Path.Combine(tempDir, "libarchive.zip"); + // This isn't called often so we don't need to optimize + using (HttpClient hc = new HttpClient()) + { + var result = hc.Send(new HttpRequestMessage(HttpMethod.Get, new Uri(_libArchiveWindowsUrl))); + using var stream = result.Content.ReadAsStream(); + using FileStream fs = new FileStream(libarchiveFile, FileMode.Create, FileAccess.Write, FileShare.None); + stream.CopyTo(fs); + } + + ZipFile.ExtractToDirectory(libarchiveFile, tempDir); + var libArchivePath = Path.Combine(tempDir, "libarchive"); + PathUtility.MoveDirectoryContents(Path.Combine(libArchivePath, "bin"), Path.Combine(Core.BaseDirectory, "bsdtar")); + File.Delete(libarchiveFile); + + return Path.Combine(Core.BaseDirectory, "bsdtar/bsdtar.exe"); + } + + public static bool ExtractToDirectory( + string fileNamePath, + string outputDirectory, + string compressCommand = null, + string compressionProgramPath = null + ) + { + _pathToTar ??= GetPathToTar(); + + var useExternalCompression = compressCommand != null ? $"--use-compress-program \"{compressCommand}\" " : ""; + var arguments = $"{useExternalCompression} -xf \"{fileNamePath}\" -C \"{outputDirectory}\""; + + return RunTar(arguments, compressionProgramPath) == 0; + } + + public static bool CreateFromPaths( + IEnumerable paths, + string destinationArchiveFileName, + string relativeTo, + string compressCommand = null, + string compressionProgramPath = null + ) + { + _pathToTar ??= GetPathToTar(); + + new FileInfo(destinationArchiveFileName).EnsureDirectory(); + + var sb = ValueStringBuilder.Create(); + var i = 0; + foreach (var path in paths) + { + var relativePath = Path.GetRelativePath(relativeTo, path); + if (i++ > 0) + { + sb.Append($" \"{relativePath}\""); + } + else + { + sb.Append($"\"{relativePath}\""); + } + } + var pathsToCompress = sb.ToString(); + sb.Dispose(); + + var tarFlags = compressCommand == null ? "-acf" : "-cf"; + var useExternalCompression = compressCommand != null ? $"--use-compress-program \"{compressCommand}\" " : ""; + var arguments = $"{useExternalCompression}{tarFlags} \"{destinationArchiveFileName}\" -C \"{relativeTo}\" {pathsToCompress}"; + + return RunTar(arguments, compressionProgramPath) == 0; + } + } +} diff --git a/Projects/UOContent/Compression/ZstdArchive.cs b/Projects/UOContent/Compression/ZstdArchive.cs new file mode 100755 index 000000000..35fd533d5 --- /dev/null +++ b/Projects/UOContent/Compression/ZstdArchive.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +namespace Server.Compression +{ + public static class ZstdArchive + { + private static readonly string _pathToZstd = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory?.FullName; + + public static bool ExtractToDirectory(string fileNamePath, string outputDirectory) + { + // bsdtar has a bug and hangs, so we are doing it in two steps. + if (Core.IsWindows) + { + var tempDir = PathUtility.EnsureRandomPath(Path.GetTempPath()); + var tempTarArchive = Path.Combine(tempDir, "temp-file.tar"); + + try + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = Path.Combine(_pathToZstd, "zstd.exe"), + Arguments = $"-q -d \"{fileNamePath}\" -o \"{tempTarArchive}\"" + } + }; + + process.Start(); + process.WaitForExit(); + + return process.ExitCode == 0 && TarArchive.ExtractToDirectory(tempTarArchive, outputDirectory); + } + catch + { + return false; + } + finally + { + File.Delete(tempTarArchive); + } + } + + return TarArchive.ExtractToDirectory(fileNamePath, outputDirectory, "zstd -q -d", _pathToZstd); + } + + public static bool CreateFromPaths( + IEnumerable paths, + string destinationArchiveFileName, + string relativeTo + ) + { + // bsdtar has a bug and hangs, so we are doing it in two steps. + if (Core.IsWindows) + { + var tempDir = PathUtility.EnsureRandomPath(Path.GetTempPath()); + var tempTarArchive = Path.Combine(tempDir, "temp-file.tar"); + + try + { + if (!TarArchive.CreateFromPaths(paths, tempTarArchive, relativeTo)) + { + return false; + } + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = Path.Combine(_pathToZstd, "zstd.exe"), + Arguments = $"-q \"{tempTarArchive}\" -o \"{destinationArchiveFileName}\"" + } + }; + + process.Start(); + process.WaitForExit(); + + return process.ExitCode == 0; + } + catch + { + return false; + } + finally + { + Directory.Delete(tempDir, true); + } + } + + return TarArchive.CreateFromPaths(paths, destinationArchiveFileName, relativeTo, "zstd -q", _pathToZstd); + } + } +} diff --git a/Projects/UOContent/Configuration/EmailConfiguration.cs b/Projects/UOContent/Configuration/EmailConfiguration.cs index caea6b566..2aa927175 100644 --- a/Projects/UOContent/Configuration/EmailConfiguration.cs +++ b/Projects/UOContent/Configuration/EmailConfiguration.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: EmailConfiguration.cs * * * diff --git a/Projects/UOContent/Configuration/ExpansionConfiguration.cs b/Projects/UOContent/Configuration/ExpansionConfiguration.cs index 50fec1dc3..2399a58b5 100644 --- a/Projects/UOContent/Configuration/ExpansionConfiguration.cs +++ b/Projects/UOContent/Configuration/ExpansionConfiguration.cs @@ -1,4 +1,3 @@ -using Server.Engines.Craft.T2A; using Server.Network; namespace Server @@ -7,12 +6,12 @@ namespace Server { public static void Configure() { + Mobile.InsuranceEnabled = ServerConfiguration.GetSetting("insurance.enable", Core.AOS); ObjectPropertyList.Enabled = ServerConfiguration.GetSetting("opl.enable", Core.AOS); var visibleDamage = ServerConfiguration.GetSetting("visibleDamage", Core.AOS); Mobile.VisibleDamageType = visibleDamage ? VisibleDamageType.Related : VisibleDamageType.None; Mobile.GuildClickMessage = ServerConfiguration.GetSetting("guildClickMessage", !Core.AOS); Mobile.AsciiClickMessage = ServerConfiguration.GetSetting("asciiClickMessage", !Core.AOS); - T2ACraftSystem.Enabled = ServerConfiguration.GetSetting("t2aCraftMenus", !Core.UOTD); Mobile.ActionDelay = ServerConfiguration.GetSetting("actionDelay", Core.AOS ? 1000 : 500); diff --git a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs index 7f72c02b2..6b3048b89 100644 --- a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs +++ b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs @@ -8,7 +8,6 @@ using Server.Commands; using Server.Commands.Generic; using Server.Engines.Spawners; using Server.Gumps; -using Server.Logging; using Server.Network; using Server.Saves; @@ -29,19 +28,11 @@ public enum AdvancedSearchGumpOptions : long public class AdvancedSearchGump : Gump { - private static readonly ILogger _logger = LogFactory.GetLogger(typeof(AdvancedSearchGump)); - private const int MaxEntries = 18; private static int _threadId; private static AdvancedSearchThreadWorker[] _threadWorkers; - private static int _searchInProgress; - - internal static bool IsSearchInProgress => Volatile.Read(ref _searchInProgress) == 1; - internal static bool TryBeginSearch() => Interlocked.CompareExchange(ref _searchInProgress, 1, 0) == 0; - internal static void EndSearch() => Volatile.Write(ref _searchInProgress, 0); - private static void Configure() { EventSink.Shutdown += Shutdown; @@ -133,10 +124,6 @@ public class AdvancedSearchGump : Gump public AdvancedSearchGump() : base(50, 50) => Build(); - // Entries on the current page — bounds the paging loop so a partial last page can't read past the end. - internal static int VisibleCount(int total, int displayFrom, int maxEntries) => - Math.Clamp(total - displayFrom, 0, maxEntries); - private void Build() { const int height = 500; @@ -314,8 +301,8 @@ public class AdvancedSearchGump : Gump ); // Count the number of selected objects - var count = 0; - foreach (var e in SearchResults) + int count = 0; + foreach (AdvancedSearchResult e in SearchResults) { if (e.Selected) { @@ -332,14 +319,16 @@ public class AdvancedSearchGump : Gump var allDisplayedSelected = true; - // Bound to this page's real entries so a partial last page still renders in descending mode. - var visibleCount = VisibleCount(SearchResults.Length, DisplayFrom, MaxEntries); - - for (var i = 0; i < visibleCount; i++) + for (int i = 0; i < MaxEntries; i++) { - var offset = SortDescending ? visibleCount - 1 - i : i; + var offset = SortDescending ? MaxEntries - 1 - i : i; var index = offset + DisplayFrom; + if (index >= SearchResults.Length) + { + break; + } + var entry = SearchResults[index]; if (!entry.Selected) @@ -750,103 +739,78 @@ public class AdvancedSearchGump : Gump return; } - if (!TryBeginSearch()) + var autoSave = AutoSave.SavesEnabled; + if (autoSave) { - from.SendMessage("A search is already running. Please wait for it to finish."); - return; + AutoSave.SavesEnabled = false; } - _threadId = 0; + _threadWorkers ??= new AdvancedSearchThreadWorker[Math.Max(Environment.ProcessorCount - 1, 1)]; - var autoSave = AutoSave.SavesEnabled; - AutoSave.SavesEnabled = false; + var ignoreQueue = new ConcurrentQueue(); + var results = new ConcurrentQueue(); + var worldLocation = new WorldLocation(from.Location, from.Map); - try + for (var i = 0; i < _threadWorkers.Length; i++) { - _threadWorkers ??= new AdvancedSearchThreadWorker[Math.Max(Environment.ProcessorCount - 1, 1)]; + (_threadWorkers[i] ??= new AdvancedSearchThreadWorker()).Wake(worldLocation, Filter, results, ignoreQueue); + } - var ignoreQueue = new ConcurrentQueue(); - var results = new ConcurrentQueue(); - var worldLocation = new WorldLocation(from.Location, from.Map); + var type = Filter.FilterType ? Filter.Type : null; + // Push the entities + foreach (var item in World.Items.Values) + { + if (type == null || type.IsInstanceOfType(item)) + { + PushToWorkers(item); + } + } + + foreach (var m in World.Mobiles.Values) + { + if (type == null || type.IsInstanceOfType(m)) + { + PushToWorkers(m); + } + } + + ThreadPool.QueueUserWorkItem(state => + { + // Block until everything is processed for (var i = 0; i < _threadWorkers.Length; i++) { - (_threadWorkers[i] ??= new AdvancedSearchThreadWorker()).Wake(worldLocation, Filter, results, ignoreQueue); + _threadWorkers[i].Sleep(); } - var type = Filter.FilterType ? Filter.Type : null; + var ignoredEntities = new HashSet(ignoreQueue); - // Push the entities. Workers read entity state concurrently with the main loop — - // see AdvancedSearchThreadWorker for the accepted, bounded race. - foreach (var item in World.Items.Values) + // Force the GC to collect the ignored entities + ignoreQueue.Clear(); + + var resultsList = new List(results.Count); + foreach (var result in results) { - if (type == null || type.IsInstanceOfType(item)) + if (!ignoredEntities.Contains(result.Entity)) { - PushToWorkers(item); + resultsList.Add(result); } } - foreach (var m in World.Mobiles.Values) - { - if (type == null || type.IsInstanceOfType(m)) + SearchResults = resultsList.ToArray(); + + // Force the GC to collect the results + resultsList.Clear(); + resultsList.TrimExcess(); + + // Send the gump on the main thread + Core.LoopContext.Post( + autoSaveState => { - PushToWorkers(m); - } - } - - ThreadPool.QueueUserWorkItem(state => - { - try - { - // Block until everything is processed - for (var i = 0; i < _threadWorkers.Length; i++) - { - _threadWorkers[i].Sleep(); - } - - var ignoredEntities = new HashSet(ignoreQueue); - - // Force the GC to collect the ignored entities - ignoreQueue.Clear(); - - var resultsList = new List(results.Count); - foreach (var result in results) - { - if (!ignoredEntities.Contains(result.Entity)) - { - resultsList.Add(result); - } - } - - SearchResults = resultsList.ToArray(); - - // Force the GC to collect the results - resultsList.Clear(); - resultsList.TrimExcess(); - - // Send the gump on the main thread - Core.LoopContext.Post(() => Resend(from)); - } - catch (Exception ex) - { - // A drain-phase throw here would terminate the process; the finally still - // restores autosave and releases the guard. - _logger.Warning(ex, "AdvancedSearch: search drain failed"); - } - finally - { - AutoSave.SavesEnabled = (bool)state!; - EndSearch(); - } - }, autoSave); - } - catch - { - // Setup failed before the work item took ownership of the release. - AutoSave.SavesEnabled = autoSave; - EndSearch(); - throw; - } + AutoSave.SavesEnabled = (bool)autoSaveState!; + Resend(from); + }, state); + }, autoSave); } private void SetSortSwitches(int radioSwitch) @@ -932,7 +896,7 @@ public class AdvancedSearchGump : Gump var commandSpan = commandString.AsSpan().Trim(); string command = null; - var commandArgs = new string[commandSpan.Count(" ")]; + string[] commandArgs = new string[commandSpan.Count(" ")]; var index = 0; foreach (var part in commandSpan.Tokenize(' ')) @@ -969,9 +933,9 @@ public class AdvancedSearchGump : Gump return; } - var cmdEventArgs = new CommandEventArgs(from, command, commandString, commandArgs); + CommandEventArgs cmdEventArgs = new CommandEventArgs(from, command, commandString, commandArgs); - var flushToLog = false; + bool flushToLog = false; // execute the command on the objects in the list diff --git a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchThreadWorker.cs b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchThreadWorker.cs index 5206477c9..fd042d9ea 100644 --- a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchThreadWorker.cs +++ b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchThreadWorker.cs @@ -3,26 +3,13 @@ using System.Collections.Concurrent; using System.Reflection; using System.Threading; using Server.Items; -using Server.Logging; using Server.Mobiles; using Server.Multis; namespace Server.Engines.AdvancedSearch; -/// -/// Filters entities on a background thread while the main loop keeps mutating them — an -/// intentional, bounded race. Reads of live / state are -/// unsynchronized, so a torn read may report stale coordinates, and any -/// getter that throws mid-read is caught per-entity in and skipped. -/// Results are best-effort and may omit a concurrently modified entity, but never fault or corrupt -/// server state. Eliminating the race would require snapshotting each read field onto the main -/// thread before handing entities off; that is deferred. -/// public class AdvancedSearchThreadWorker { - private static readonly ILogger _logger = LogFactory.GetLogger(typeof(AdvancedSearchThreadWorker)); - private static readonly ConcurrentDictionary _propCache = new(); - private readonly Thread _thread; private readonly AutoResetEvent _startEvent; // Main thread tells the thread to start working private readonly AutoResetEvent _stopEvent; // Main thread waits for the worker finish draining @@ -39,10 +26,7 @@ public class AdvancedSearchThreadWorker _startEvent = new AutoResetEvent(false); _stopEvent = new AutoResetEvent(false); _entities = new ConcurrentQueue(); - _thread = new Thread(Execute) - { - IsBackground = true - }; + _thread = new Thread(Execute); _thread.Start(this); } @@ -68,16 +52,10 @@ public class AdvancedSearchThreadWorker public void Exit() { - Volatile.Write(ref _exit, true); + _exit = true; Wake(WorldLocation.Zero, null, null, null); - - // Tolerate a worker that has already terminated (e.g. Core.Closing raced us) so - // shutdown can't deadlock waiting on a stopEvent that will never be set. - if (_thread.IsAlive) - { - Sleep(); - } + Sleep(); } public void Push(IEntity entity) @@ -87,7 +65,7 @@ public class AdvancedSearchThreadWorker private static void Execute(object obj) { - var worker = (AdvancedSearchThreadWorker)obj; + AdvancedSearchThreadWorker worker = (AdvancedSearchThreadWorker)obj; var reader = worker._entities; @@ -95,7 +73,7 @@ public class AdvancedSearchThreadWorker { while (true) { - var pauseRequested = Volatile.Read(ref worker._pause); + bool pauseRequested = Volatile.Read(ref worker._pause); if (reader.TryDequeue(out var entity)) { var result = worker.DoEntitySearch(entity); @@ -110,22 +88,12 @@ public class AdvancedSearchThreadWorker worker._filter = null; break; } - else - { - // Transiently empty but not yet paused: yield rather than busy-spin. - Thread.Yield(); - } } - // The owning thread may start another cycle the moment _stopEvent is set (Exit does exactly - // that). Clear _pause and sample the exit condition before signaling, or the new cycle's - // pause request is clobbered / its Sleep orphaned. Matches SerializationThreadWorker. - var exiting = Core.Closing || Volatile.Read(ref worker._exit); - Volatile.Write(ref worker._pause, false); - worker._stopEvent.Set(); // Allow the main thread to continue now that we are finished + worker._pause = false; - if (exiting) + if (Core.Closing || worker._exit) { return; } @@ -134,28 +102,9 @@ public class AdvancedSearchThreadWorker private AdvancedSearchResult DoEntitySearch(IEntity entity) { - if (entity == null || entity.Deleted) + if (_filter.HideValidInternalMap) { - return null; - } - - try - { - return DoEntitySearchCore(entity); - } - catch (Exception ex) - { - _logger.Warning(ex, "AdvancedSearch: filter threw for {Entity}; skipping", entity); - return null; - } - } - - private AdvancedSearchResult DoEntitySearchCore(IEntity entity) - { - if (_filter == null) - { - // Exit() clears the filter; a straggler entity dequeued after teardown bails here. - return null; + HandleValidInternal(entity); } // Check for valid map @@ -189,12 +138,6 @@ public class AdvancedSearchThreadWorker return null; } - // After the cheap filters, so non-qualifying entities skip the house/keyring enumeration. - if (_filter.HideValidInternalMap) - { - HandleValidInternal(entity); - } - if (entity is Mobile mobile) { return DoMobileSearch(mobile); @@ -239,7 +182,7 @@ public class AdvancedSearchThreadWorker private AdvancedSearchResult DoItemSearch(Item item) { - if (_filter.FilterName && !string.IsNullOrWhiteSpace(_filter.Name) && !(item.Name ?? item.ItemData.Name).InsensitiveContains(_filter.Name)) + if (_filter.FilterName && !string.IsNullOrWhiteSpace(_filter.Name) && !item.Name.InsensitiveEquals(_filter.Name)) { return null; } @@ -263,7 +206,7 @@ public class AdvancedSearchThreadWorker private AdvancedSearchResult DoMobileSearch(Mobile mobile) { - if (_filter.FilterName && !string.IsNullOrWhiteSpace(_filter.Name) && !mobile.Name.InsensitiveContains(_filter.Name)) + if (_filter.FilterName && !string.IsNullOrWhiteSpace(_filter.Name) && !mobile.Name.InsensitiveEquals(_filter.Name)) { return null; } @@ -325,7 +268,7 @@ public class AdvancedSearchThreadWorker // Keys don't have a backreference, so we just ignore them for now if (entity is KeyRing keyring && keyring.Keys?.Count > 0) { - foreach (var k in keyring.Keys) + foreach (Key k in keyring.Keys) { _ignoreQueue.Enqueue(k); } @@ -335,7 +278,7 @@ public class AdvancedSearchThreadWorker if (entity is BaseHouse house) { - foreach (var relEntity in house.RelocatedEntities) + foreach (RelocatedEntity relEntity in house.RelocatedEntities) { if (relEntity.Entity is Item) { @@ -343,9 +286,9 @@ public class AdvancedSearchThreadWorker } } - foreach (var inventory in house.VendorInventories) + foreach (VendorInventory inventory in house.VendorInventories) { - foreach (var subItem in inventory.Items) + foreach (Item subItem in inventory.Items) { _ignoreQueue.Enqueue(subItem); } @@ -353,17 +296,27 @@ public class AdvancedSearchThreadWorker } } - private static bool EvaluateRecursive(IEntity entity, ReadOnlySpan span) => - AdvancedSearchUtilities.EvaluateBoolean(span, entity, static (e, leaf) => EvaluateSingleExpression(e, leaf)); + private static bool EvaluateRecursive(IEntity entity, ReadOnlySpan span) + { + int atIndex = span.IndexOf('@'); + int orIndex = span.IndexOf('|'); + + if (atIndex == -1 && orIndex == -1) + { + return EvaluateSingleExpression(entity, span); + } + + bool result = atIndex != -1; + int splitIndex = result ? atIndex : orIndex; + + var left = EvaluateRecursive(entity, span.Slice(0, splitIndex)); + var right = EvaluateRecursive(entity, span.Slice(splitIndex + 1)); + + return result ? left && right : left || right; + } private static bool EvaluateSingleExpression(IEntity entity, ReadOnlySpan expression) { - expression = expression.Trim(); - if (expression.Length == 0) - { - return false; - } - var negate = false; if (expression[0] == '~') { @@ -385,7 +338,7 @@ public class AdvancedSearchThreadWorker return false; } - var properties = _propCache.GetOrAdd(entity.GetType(), static t => t.GetProperties()); + var properties = entity.GetType().GetProperties(); PropertyInfo property = null; for (var i = 0; i < properties.Length; ++i) { @@ -403,7 +356,7 @@ public class AdvancedSearchThreadWorker } var propertyValue = property.GetValue(entity); - var result = AdvancedSearchUtilities.CompareValues(property.PropertyType, propertyValue, valuePart, operatorSpan); + bool result = AdvancedSearchUtilities.CompareValues(property.PropertyType, propertyValue, valuePart, operatorSpan); return negate ? !result : result; } diff --git a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs index 6775dcaef..68813cba6 100644 --- a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs +++ b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs @@ -1,5 +1,6 @@ using System; using System.Buffers; +using System.Collections.Generic; using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; @@ -42,80 +43,77 @@ public static class AdvancedSearchUtilities if (propertyType == typeof(long)) { - return TryParseValue(valuePart, out var parsedValue) && - CompareNumeric((long)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return CompareNumeric((long)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(ulong)) { - return TryParseValue(valuePart, out var parsedValue) && - CompareNumeric((ulong)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return CompareNumeric((ulong)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(int)) { - return TryParseValue(valuePart, out var parsedValue) && - CompareNumeric((int)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return CompareNumeric((int)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(uint)) { - return TryParseValue(valuePart, out var parsedValue) && - CompareNumeric((uint)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return CompareNumeric((uint)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(short)) { - return TryParseValue(valuePart, out var parsedValue) && - CompareNumeric((short)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return CompareNumeric((short)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(ushort)) { - return TryParseValue(valuePart, out var parsedValue) && - CompareNumeric((ushort)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return CompareNumeric((ushort)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(sbyte)) { - return TryParseValue(valuePart, out var parsedValue) && - CompareNumeric((sbyte)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return CompareNumeric((sbyte)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(byte)) { - return TryParseValue(valuePart, out var parsedValue) && - CompareNumeric((byte)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return CompareNumeric((byte)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(float)) { - return TryParseValue(valuePart, out var parsedValue) && - Compare((float)propertyValue!, parsedValue, valuePart, operatorSpan); + var parsedValue = ParseValue(valuePart); + return Compare((float)propertyValue!, parsedValue, valuePart, operatorSpan); } if (propertyType == typeof(double)) { - return TryParseValue(valuePart, out var parsedValue) && - Compare((double)propertyValue!, parsedValue, valuePart, operatorSpan); + var parsedValue = ParseValue(valuePart); + return Compare((double)propertyValue!, parsedValue, valuePart, operatorSpan); } if (propertyType == typeof(string)) { - return TryParseValue(valuePart, out var parsedValue) && - Compare((string)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return Compare((string)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(TimeSpan)) { - return TryParseValue(valuePart, out var parsedValue) && - Compare((TimeSpan)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return Compare((TimeSpan)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(DateTime)) { - return TryParseValue(valuePart, out var parsedValue) && - Compare((DateTime)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return Compare((DateTime)propertyValue!, parsedValue, operatorSpan); } if (propertyType == typeof(bool)) { - return TryParseValue(valuePart, out var parsedValue) && - Compare((bool)propertyValue!, parsedValue, operatorSpan); + var parsedValue = ParseValue(valuePart); + return Compare((bool)propertyValue!, parsedValue, operatorSpan); } if (propertyType.IsEnum) { - if (!Enum.TryParse(propertyType, valuePart.ToString(), true, out var valueEnum) || valueEnum == null) - { - return false; - } + var valueEnum = Enum.Parse(propertyType, valuePart, false); return GetEnumSize(propertyType) switch { @@ -123,17 +121,15 @@ public static class AdvancedSearchUtilities 2 => CompareNumeric((short)propertyValue!, (short)valueEnum, operatorSpan), 4 => CompareNumeric((int)propertyValue!, (int)valueEnum, operatorSpan), 8 => CompareNumeric((long)propertyValue!, (long)valueEnum, operatorSpan), - _ => false }; } - // Anything the hot typed paths above didn't handle — reference types (Poison, Map, entity - // properties resolved by serial), IParsable value types (Guid, decimal, ...), and legacy - // RunUO types with a static Parse(string) (Faction, Town, ...). Delegate to the shared, - // thread-safe Types converter so the target is parsed into the property's real type, then - // compare by value. A string is allocated here, but this is the uncommon path; the common - // types never reach it. Types returns a non-null message when it can't parse -> no match. - return Types.TryParse(propertyType, valuePart.ToString(), out var parsed) == null && - CompareReference(propertyValue!, parsed, operatorSpan); + if (!propertyType.IsValueType) + { + var parsedValue = ParseValue(valuePart); + return CompareReference(propertyValue!, parsedValue, operatorSpan); + } + + return false; } public static bool CompareNumeric(T propertyValue, T parsedValue, ReadOnlySpan operatorSpan) where T : INumber => @@ -155,7 +151,7 @@ public static class AdvancedSearchUtilities ReadOnlySpan operatorSpan ) { - var epsilon = CalculateEpsilon(originalValue); + double epsilon = CalculateEpsilon(originalValue); return operatorSpan switch { @@ -171,7 +167,7 @@ public static class AdvancedSearchUtilities public static double CalculateEpsilon(ReadOnlySpan value) { - var decimalPlace = value.IndexOf('.'); + int decimalPlace = value.IndexOf('.'); if (decimalPlace == -1) { @@ -240,190 +236,94 @@ public static class AdvancedSearchUtilities _ => false }; - public static bool CompareReference(T propertyValue, T parsedValue, ReadOnlySpan operatorSpan) - { - switch (operatorSpan) + public static bool CompareReference(T propertyValue, T parsedValue, ReadOnlySpan operatorSpan) => + operatorSpan switch { - case "=": - case "==": return Equals(propertyValue, parsedValue); - case "!": - case "!=": return !Equals(propertyValue, parsedValue); - } + "=" or "==" => propertyValue.Equals(parsedValue), + "!" or "!=" => !propertyValue.Equals(parsedValue), + ">" => Comparer.Default.Compare(propertyValue, parsedValue) > 0, + "<" => Comparer.Default.Compare(propertyValue, parsedValue) < 0, + ">=" => Comparer.Default.Compare(propertyValue, parsedValue) >= 0, + "<=" => Comparer.Default.Compare(propertyValue, parsedValue) <= 0, + _ => false + }; - if (propertyValue is IComparable cmp && parsedValue != null) - { - try - { - var c = cmp.CompareTo(parsedValue); - return operatorSpan switch - { - ">" => c > 0, - "<" => c < 0, - ">=" => c >= 0, - "<=" => c <= 0, - _ => false - }; - } - catch - { - return false; - } - } - - return false; - } - - internal static bool TryParseValue(ReadOnlySpan valuePart, out T value) + public static T ParseValue(ReadOnlySpan valuePart) { // Special handling for boolean and hexadecimal values if (typeof(T) == typeof(bool)) { - var val = valuePart.ToString().ToLower(); + string val = valuePart.ToString().ToLower(); if (val is "true" or "1" or "enabled" or "on") { - value = (T)(object)true; - return true; + return (T)(object)true; } if (val is "false" or "0" or "disabled" or "off") { - value = (T)(object)false; - return true; + return (T)(object)false; } - - value = default; - return false; } if (typeof(T) == typeof(long)) { - return TryParseNumericValue(valuePart, out value); + return ParseNumericValue(valuePart); } if (typeof(T) == typeof(ulong)) { - return TryParseNumericValue(valuePart, out value); + return ParseNumericValue(valuePart); } if (typeof(T) == typeof(int)) { - return TryParseNumericValue(valuePart, out value); + return ParseNumericValue(valuePart); } if (typeof(T) == typeof(uint)) { - return TryParseNumericValue(valuePart, out value); + return ParseNumericValue(valuePart); } if (typeof(T) == typeof(short)) { - return TryParseNumericValue(valuePart, out value); + return ParseNumericValue(valuePart); } if (typeof(T) == typeof(ushort)) { - return TryParseNumericValue(valuePart, out value); + return ParseNumericValue(valuePart); } if (typeof(T) == typeof(sbyte)) { - return TryParseNumericValue(valuePart, out value); + return ParseNumericValue(valuePart); } if (typeof(T) == typeof(byte)) { - return TryParseNumericValue(valuePart, out value); + return ParseNumericValue(valuePart); } if (typeof(T) == typeof(float)) { - return TryParseNumericValue(valuePart, out value); + return ParseNumericValue(valuePart); } if (typeof(T) == typeof(double)) { - return TryParseNumericValue(valuePart, out value); + return ParseNumericValue(valuePart); } - // string needs no parsing — the span itself is the value. - if (typeof(T) == typeof(string)) - { - value = (T)(object)valuePart.ToString(); - return true; - } - - // Remaining supported types (TimeSpan, DateTime) parse straight from the span via - // ISpanParsable — no allocation, no reflection, and unlike Convert.ChangeType it handles - // TimeSpan, which is not IConvertible and previously failed silently. - if (typeof(T) == typeof(TimeSpan)) - { - return TryParseSpanParsable(valuePart, out value); - } - - if (typeof(T) == typeof(DateTime)) - { - return TryParseSpanParsable(valuePart, out value); - } - - value = default; - return false; - } - - // Parses U (a value type exposing ISpanParsable) from the span and reinterprets it as T. The - // two type params mirror TryParseNumericValue: the caller dispatches on typeof(T), so U == T at - // every call site and the (T)(object) cast is always valid. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryParseSpanParsable(ReadOnlySpan valuePart, out T value) where U : ISpanParsable - { - if (U.TryParse(valuePart, null, out var parsed)) - { - value = (T)(object)parsed; - return true; - } - - value = default; - return false; + // Default parsing for other types + return (T)Convert.ChangeType(valuePart.ToString(), typeof(T)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryParseNumericValue(ReadOnlySpan valuePart, out R value) where T : INumber - { - var ok = valuePart.StartsWith("0x") - ? T.TryParse(valuePart[2..], NumberStyles.HexNumber, null, out var parsed) - : T.TryParse(valuePart, null, out parsed); - - if (ok) - { - value = (R)(object)parsed; - return true; - } - - value = default; - return false; - } - - // Evaluates one trimmed leaf atom against caller-supplied state. A custom delegate is required - // because ReadOnlySpan cannot be a Func<> type argument; passing state avoids a per-call - // capturing closure, so the recursion allocates neither a string nor a closure. - internal delegate bool LeafEvaluator(TState state, ReadOnlySpan leaf); - - // OR ('|') binds looser than AND ('@'); split on the outermost OR first, then AND. - internal static bool EvaluateBoolean(ReadOnlySpan expr, TState state, LeafEvaluator evalLeaf) - { - var orIndex = expr.IndexOf('|'); - if (orIndex != -1) - { - return EvaluateBoolean(expr[..orIndex], state, evalLeaf) || EvaluateBoolean(expr[(orIndex + 1)..], state, evalLeaf); - } - - var andIndex = expr.IndexOf('@'); - if (andIndex != -1) - { - return EvaluateBoolean(expr[..andIndex], state, evalLeaf) && EvaluateBoolean(expr[(andIndex + 1)..], state, evalLeaf); - } - - return evalLeaf(state, expr.Trim()); - } + private static R ParseNumericValue(ReadOnlySpan valuePart) where T : INumber => + valuePart.StartsWith("0x") + ? (R)(object)T.Parse(valuePart[2..], NumberStyles.HexNumber, null) + : (R)(object)T.Parse(valuePart, null); private static int GetEnumSize(Type enumType) => Type.GetTypeCode(Enum.GetUnderlyingType(enumType)) switch diff --git a/Projects/UOContent/Engines/BuffIcons/BuffIcon.cs b/Projects/UOContent/Engines/BuffIcons/BuffIcon.cs deleted file mode 100644 index 6ddb44ae5..000000000 --- a/Projects/UOContent/Engines/BuffIcons/BuffIcon.cs +++ /dev/null @@ -1,192 +0,0 @@ -namespace Server.Engines.BuffIcons; - -public enum BuffIcon : short -{ - DismountPrevention = 0x3E9, - NoRearm = 0x3EA, - //Currently, no 0x3EB or 0x3EC - NightSight = 0x3ED, //* - DeathStrike, - EvilOmen, - HonoredDebuff, - AchievePerfection, - DivineFury, //* - EnemyOfOne, //* - HidingAndOrStealth, //* - ActiveMeditation, //* - BloodOathCaster, //* - BloodOathCurse, //* - CorpseSkin, //* - Mindrot, //* - PainSpike, //* - Strangle, - GiftOfRenewal, //* - AttuneWeapon, //* - Thunderstorm, //* - EssenceOfWind, //* - EtherealVoyage, //* - GiftOfLife, //* - ArcaneEmpowerment, //* - MortalStrike, - ReactiveArmor, //* - Protection, //* - ArchProtection, - MagicReflection, //* - Incognito, //* - Disguised, - AnimalForm, - Polymorph, - Invisibility, //* - Paralyze, //* - Poison, - Bleed, - Clumsy, //* - FeebleMind, //* - Weaken, //* - Curse, //* - MassCurse, - Agility, //* - Cunning, //* - Strength, //* - Bless, //* - Sleep, - StoneForm, - SpellPlague, - Berserk, - MassSleep, - Fly, - Inspire, - Invigorate, - Resilience, - Perseverance, - TribulationTarget, - DespairTarget, - FishPie = 0x426, - HitLowerAttack, - HitLowerDefense, - DualWield, - Block, - DefenseMastery, - DespairCaster, - Healing, - SpellFocusingBuff, - SpellFocusingDebuff, - RageFocusingDebuff, - RageFocusingBuff, - Warding, - TribulationCaster, - ForceArrow, - Disarm, - Surge, - Feint, - TalonStrike, - PsychicAttack, - ConsecrateWeapon, - GrapesOfWrath, - EnemyOfOneDebuff, - HorrificBeast, - LichForm, - VampiricEmbrace, - CurseWeapon, - ReaperForm, - ImmolatingWeapon, - Enchant, - HonorableExecution, - Confidence, - Evasion, - CounterAttack, - LightningStrike, - MomentumStrike, - OrangePetals, - RoseOfTrinsic, - PoisonImmunity, - Veterinary, - Perfection, - Honored, - ManaPhase, - FanDancerFanFire, - Rage, - Webbing, - MedusaStone, - TrueFear, - AuraOfNausea, - HowlOfCacophony, - GazeDespair, - HiryuPhysicalResistance, - RuneBeetleCorruption, - BloodwormAnemia, - RotwormBloodDisease, - SkillUseDelay, - FactionStatLoss, - HeatOfBattleStatus, - CriminalStatus, - ArmorPierce, - SplinteringEffect, - SwingSpeedDebuff, - WraithForm, - CityTradeDeal = 0x466, - HumilityDebuff = 0x467, - Spirituality, - Humility, - // Skill Masteries - Rampage, - Stagger, // Debuff - Toughness, - Thrust, - Pierce, // Debuff - PlayingTheOdds, - FocusedEye, - Onslaught, // Debuff - ElementalFury, - ElementalFuryDebuff, // Debuff - CalledShot, - Knockout, - SavingThrow, - Conduit, - EtherealBurst, - MysticWeapon, - ManaShield, - AnticipateHit, - Warcry, - Shadow, - WhiteTigerForm, - Bodyguard, - HeightenedSenses, - Tolerance, - DeathRay, - DeathRayDebuff, - Intuition, - EnchantedSummoning, - ShieldBash, - Whispering, - CombatTraining, - InjectedStrikeDebuff, - InjectedStrike, - UnknownTomato, - PlayingTheOddsDebuff, - DragonTurtleDebuff, - Boarding, - Potency, - ThrustDebuff, - FistsOfFury, // 1169 - BarrabHemolymphConcentrate, - JukariBurnPoiltice, - KurakAmbushersEssence, - BarakoDraftOfMight, - UraliTranceTonic, - SakkhraProphylaxis, // 1175 - Sparks, - Swarm, - BoneBreaker, - Unknown2, - SwarmImmune, - BoneBreakerImmune, - UnknownGoblin, - UnknownRedDrop, - UnknownStar, - FeintDebuff, - CaddelliteInfused, - PotionGloriousFortune, - MysticalPolymorphTotem, - UnknownDebuff, -} diff --git a/Projects/UOContent/Engines/BuffIcons/BuffInfo.cs b/Projects/UOContent/Engines/BuffIcons/BuffInfo.cs deleted file mode 100644 index 5a6fd5dd3..000000000 --- a/Projects/UOContent/Engines/BuffIcons/BuffInfo.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using Server.Mobiles; - -namespace Server.Engines.BuffIcons; - -public class BuffInfo -{ - private TimerExecutionToken _timerToken; - - public BuffInfo( - BuffIcon iconID, int titleCliloc, TimeSpan duration = default, TextDefinition args = null, - bool retainThroughDeath = false - ) : this(iconID, titleCliloc, titleCliloc + 1, duration, args, retainThroughDeath) - { - } - - public BuffInfo( - BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan duration = default, TextDefinition args = null, - bool retainThroughDeath = false - ) - { - ID = iconID; - TitleCliloc = titleCliloc; - SecondaryCliloc = secondaryCliloc; - Duration = duration; - Args = args; - RetainThroughDeath = retainThroughDeath; - } - - public static bool Enabled { get; private set; } - - public BuffIcon ID { get; } - - public int TitleCliloc { get; } - - public int SecondaryCliloc { get; } - - public DateTime StartTime { get; private set; } - - public TimeSpan Duration { get; } - - public bool RetainThroughDeath { get; } - - public TextDefinition Args { get; } - - public static void Configure() - { - Enabled = ServerConfiguration.GetOrUpdateSetting("buffIcons.enable", Core.ML); - } - - public void StartTimer(PlayerMobile m) - { - if (Duration != TimeSpan.Zero) - { - StartTime = Core.Now; - var id = ID; - Timer.StartTimer(Duration, () => m.RemoveBuff(id), out _timerToken); - } - } - - public void StopTimer() - { - _timerToken.Cancel(); - } -} diff --git a/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs b/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs index a95ca63b5..20aa0a3ec 100644 --- a/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs @@ -14,9 +14,11 @@ namespace Server.Engines.BulkOrders _material = material; } - public BaseBOD() : base(Core.AOS ? 0x2258 : 0x14EF) => LootType = LootType.Blessed; - - public override double DefaultWeight => 1.0; + public BaseBOD() : base(Core.AOS ? 0x2258 : 0x14EF) + { + Weight = 1.0; + LootType = LootType.Blessed; + } public abstract bool Complete { get; } diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BOBEntries.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BOBEntries.cs index d798a91a6..ce3c02e8c 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BOBEntries.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BOBEntries.cs @@ -17,5 +17,5 @@ public class BOBEntries : GenericEntityPersistence public static void Add(IBOBEntry entity) => _bobEntriesPersistence.AddEntity(entity); - public static void Remove(IBOBEntry entity) => _bobEntriesPersistence.RemoveEntity(entity); + public static void Remove(IBOBEntry entity) => _bobEntriesPersistence.AddEntity(entity); } diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BOBFilter.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BOBFilter.cs index f63e4f8d4..962c3d8e3 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BOBFilter.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BOBFilter.cs @@ -6,27 +6,27 @@ namespace Server.Engines.BulkOrders; public partial class BOBFilter { [SerializableField(0)] - [SaveFlag(nameof(ShouldSerializeType))] private int _type; + [SerializableFieldSaveFlag(0)] private bool ShouldSerializeType() => _type != 0; [SerializableField(1)] - [SaveFlag(nameof(ShouldSerializeQuality))] private int _quality; + [SerializableFieldSaveFlag(1)] private bool ShouldSerializeQuality() => _quality != 0; [SerializableField(2)] - [SaveFlag(nameof(ShouldSerializeMaterial))] private int _material; + [SerializableFieldSaveFlag(2)] private bool ShouldSerializeMaterial() => _material != 0; [SerializableField(3)] - [SaveFlag(nameof(ShouldSerializeQuantity))] private int _quantity; + [SerializableFieldSaveFlag(3)] private bool ShouldSerializeQuantity() => _quantity != 0; private void Deserialize(IGenericReader reader, int version) diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs index 7be389e49..28ee596c2 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs @@ -6,598 +6,603 @@ using Server.Mobiles; using Server.Network; using Server.Prompts; -namespace Server.Engines.BulkOrders; - -public class BOBGump : DynamicGump +namespace Server.Engines.BulkOrders { - private const int LabelColor = 0x7FFF; - - private readonly PlayerMobile _from; - private int _page; - - public BulkOrderBook Book { get; } - public List List { get; private set; } - public override bool Singleton => true; - - public BOBGump(PlayerMobile from, BulkOrderBook book) : base(12, 24) + public class BOBGump : Gump { - _from = from; - Book = book; - } + private const int LabelColor = 0x7FFF; + private readonly BulkOrderBook _book; + private readonly PlayerMobile _from; + private readonly List _list; - public void ResetList() => List = null; + private int _page; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - if (List == null) + public override bool Singleton => true; + + public BOBGump(PlayerMobile from, BulkOrderBook book, int page = 0, List list = null) : base(12, 24) { - List = new List(Book.Entries.Count); + _from = from; + _book = book; + _page = page; - for (var i = 0; i < Book.Entries.Count; ++i) + if (list == null) { - var entry = Book.Entries[i]; + list = new List(book.Entries.Count); - if (CheckFilter(entry)) + for (var i = 0; i < book.Entries.Count; ++i) { - List.Add(entry); + var entry = book.Entries[i]; + + if (CheckFilter(entry)) + { + list.Add(entry); + } } } - } - var index = GetIndexForPage(_page); - var count = GetCountForIndex(index); + _list = list; - var tableIndex = 0; + var index = GetIndexForPage(page); + var count = GetCountForIndex(index); - var canDrop = Book.IsChildOf(_from.Backpack); - var pv = Book.RootParent as PlayerVendor; - var canBuy = pv != null; - var canPrice = canDrop || canBuy; + var tableIndex = 0; - if (canBuy) - { - var vi = pv.GetVendorItem(Book); + var pv = book.RootParent as PlayerVendor; - canBuy = vi?.IsForSale == false; - } - - var width = 600; - - if (!canPrice) - { - width = 516; - } - - X = (624 - width) / 2; - - builder.AddPage(); - - builder.AddBackground(10, 10, width, 439, 5054); - builder.AddImageTiled(18, 20, width - 17, 420, 2624); - - if (canPrice) - { - builder.AddImageTiled(573, 64, 24, 352, 200); - builder.AddImageTiled(493, 64, 78, 352, 1416); - } - - if (canDrop) - { - builder.AddImageTiled(24, 64, 32, 352, 1416); - } - - builder.AddImageTiled(58, 64, 36, 352, 200); - builder.AddImageTiled(96, 64, 133, 352, 1416); - builder.AddImageTiled(231, 64, 80, 352, 200); - builder.AddImageTiled(313, 64, 100, 352, 1416); - builder.AddImageTiled(415, 64, 76, 352, 200); - - for (var i = index; i < index + count && i >= 0 && i < List.Count; ++i) - { - var entry = List[i]; - - if (!CheckFilter(entry)) - { - continue; - } - - builder.AddImageTiled(24, 94 + tableIndex * 32, canPrice ? 573 : 489, 2, 2624); - tableIndex += entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; - } - - builder.AddAlphaRegion(18, 20, width - 17, 420); - builder.AddImage(5, 5, 10460); - builder.AddImage(width - 15, 5, 10460); - builder.AddImage(5, 424, 10460); - builder.AddImage(width - 15, 424, 10460); - - builder.AddHtmlLocalized(canPrice ? 266 : 224, 32, 200, 32, 1062220, LabelColor); // Bulk Order Book - builder.AddHtmlLocalized(63, 64, 200, 32, 1062213, LabelColor); // Type - builder.AddHtmlLocalized(147, 64, 200, 32, 1062214, LabelColor); // Item - builder.AddHtmlLocalized(246, 64, 200, 32, 1062215, LabelColor); // Quality - builder.AddHtmlLocalized(336, 64, 200, 32, 1062216, LabelColor); // Material - builder.AddHtmlLocalized(429, 64, 200, 32, 1062217, LabelColor); // Amount - - builder.AddButton(35, 32, 4005, 4007, 1); - builder.AddHtmlLocalized(70, 32, 200, 32, 1062476, LabelColor); // Set Filter - - var f = _from.UseOwnFilter ? _from.BOBFilter : Book.Filter; - - if (f.IsDefault) - { - builder.AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062475, 16927); // Using No Filter - } - else if (_from.UseOwnFilter) - { - builder.AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062451, 16927); // Using Your Filter - } - else - { - builder.AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062230, 16927); // Using Book Filter - } - - builder.AddButton(375, 416, 4017, 4018, 0); - builder.AddHtmlLocalized(410, 416, 120, 20, 1011441, LabelColor); // EXIT - - if (canDrop) - { - builder.AddHtmlLocalized(26, 64, 50, 32, 1062212, LabelColor); // Drop - } - - if (canPrice) - { - builder.AddHtmlLocalized(516, 64, 200, 32, 1062218, LabelColor); // Price + var canDrop = book.IsChildOf(from.Backpack); + var canBuy = pv != null; + var canPrice = canDrop || canBuy; if (canBuy) { - builder.AddHtmlLocalized(576, 64, 200, 32, 1062219, LabelColor); // Buy + var vi = pv.GetVendorItem(book); + + canBuy = vi?.IsForSale == false; } - else + + var width = 600; + + if (!canPrice) { - builder.AddHtmlLocalized(576, 64, 200, 32, 1062227, LabelColor); // Set - - builder.AddButton(450, 416, 4005, 4007, 4); - builder.AddHtml(485, 416, 120, 20, "Price all"); + width = 516; } - } - tableIndex = 0; + X = (624 - width) / 2; - if (_page > 0) - { - builder.AddButton(75, 416, 4014, 4016, 2); - builder.AddHtmlLocalized(110, 416, 150, 20, 1011067, LabelColor); // Previous page - } + AddPage(0); - if (GetIndexForPage(_page + 1) < List.Count) - { - builder.AddButton(225, 416, 4005, 4007, 3); - builder.AddHtmlLocalized(260, 416, 150, 20, 1011066, LabelColor); // Next page - } + AddBackground(10, 10, width, 439, 5054); + AddImageTiled(18, 20, width - 17, 420, 2624); - for (var i = index; i < index + count && i >= 0 && i < List.Count; ++i) - { - var entry = List[i]; - - if (!CheckFilter(entry)) + if (canPrice) { - continue; + AddImageTiled(573, 64, 24, 352, 200); + AddImageTiled(493, 64, 78, 352, 1416); } - if (entry is BOBLargeEntry largeEntry) + if (canDrop) { - var y = 96 + tableIndex * 32; - - if (canDrop) - { - builder.AddButton(35, y + 2, 5602, 5606, 5 + i * 2); - } - - if (canDrop || canBuy && entry.Price > 0) - { - builder.AddButton(579, y + 2, 2117, 2118, 6 + i * 2); - builder.AddLabel(495, y, 1152, entry.Price.ToString()); - } - - builder.AddHtmlLocalized(61, y, 50, 32, 1062225, LabelColor); // Large - - for (var j = 0; j < largeEntry.Entries.Length; ++j) - { - var sub = largeEntry.Entries[j]; - - builder.AddHtmlLocalized(103, y, 130, 32, sub.Number, LabelColor); - - builder.AddHtmlLocalized( - 235, - y, - 80, - 20, - entry.RequireExceptional - ? 1060636 // exceptional - : 1011542, // normal - LabelColor - ); - - var name = GetMaterialName(entry.Material, entry.DeedType, sub.ItemType); - - name.AddHtmlText( - ref builder, - 316, - y, - 100, - 20, - false, - false, - 1152, - LabelColor - ); - - builder.AddLabel(421, y, 1152, $"{sub.AmountCur} / {entry.AmountMax}"); - - ++tableIndex; - y += 32; - } - } - else - { - var smallEntry = (BOBSmallEntry)entry; - - var y = 96 + tableIndex++ * 32; - - if (canDrop) - { - builder.AddButton(35, y + 2, 5602, 5606, 5 + i * 2); - } - - if (canDrop || canBuy && smallEntry.Price > 0) - { - builder.AddButton(579, y + 2, 2117, 2118, 6 + i * 2); - builder.AddLabel(495, y, 1152, $"{smallEntry.Price}"); - } - - builder.AddHtmlLocalized(61, y, 50, 32, 1062224, LabelColor); // Small - - builder.AddHtmlLocalized(103, y, 130, 32, smallEntry.Number, LabelColor); - - builder.AddHtmlLocalized( - 235, - y, - 80, - 20, - smallEntry.RequireExceptional - ? 1060636 // exceptional - : 1011542, // normal - LabelColor - ); - - var name = GetMaterialName(smallEntry.Material, smallEntry.DeedType, smallEntry.ItemType); - - name.AddHtmlText( - ref builder, - 316, - y, - 100, - 20, - false, - false, - 1152, - LabelColor - ); - - builder.AddLabel(421, y, 1152, $"{smallEntry.AmountCur} / {smallEntry.AmountMax}"); - } - } - } - - public bool CheckFilter(IBOBEntry entry) - { - if (entry is BOBLargeEntry largeEntry) - { - return CheckFilter( - entry.Material, - entry.AmountMax, - true, - entry.RequireExceptional, - entry.DeedType, - largeEntry.Entries.Length > 0 ? largeEntry.Entries[0].ItemType : null - ); - } - - if (entry is BOBSmallEntry smallEntry) - { - return CheckFilter( - entry.Material, - entry.AmountMax, - false, - entry.RequireExceptional, - entry.DeedType, - smallEntry.ItemType - ); - } - - return false; - } - - public bool CheckFilter( - BulkMaterialType mat, int amountMax, bool isLarge, bool reqExc, BODType deedType, - Type itemType - ) - { - var f = _from.UseOwnFilter ? _from.BOBFilter : Book.Filter; - - if (f.IsDefault) - { - return true; - } - - if (f.Quality == 1 && reqExc) - { - return false; - } - - if (f.Quality == 2 && !reqExc) - { - return false; - } - - if (f.Quantity == 1 && amountMax != 10) - { - return false; - } - - if (f.Quantity == 2 && amountMax != 15) - { - return false; - } - - if (f.Quantity == 3 && amountMax != 20) - { - return false; - } - - if (f.Type == 1 && isLarge) - { - return false; - } - - if (f.Type == 2 && !isLarge) - { - return false; - } - - return f.Material switch - { - 1 => deedType == BODType.Smith, - 2 => deedType == BODType.Tailor, - 3 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Iron, - 4 => mat == BulkMaterialType.DullCopper, - 5 => mat == BulkMaterialType.ShadowIron, - 6 => mat == BulkMaterialType.Copper, - 7 => mat == BulkMaterialType.Bronze, - 8 => mat == BulkMaterialType.Gold, - 9 => mat == BulkMaterialType.Agapite, - 10 => mat == BulkMaterialType.Verite, - 11 => mat == BulkMaterialType.Valorite, - 12 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Cloth, - 13 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Leather, - 14 => mat == BulkMaterialType.Spined, - 15 => mat == BulkMaterialType.Horned, - 16 => mat == BulkMaterialType.Barbed, - _ => true - }; - } - - public int GetIndexForPage(int page) - { - var index = 0; - - while (page-- > 0) - { - index += GetCountForIndex(index); - } - - return index; - } - - public int GetCountForIndex(int index) - { - var slots = 0; - var count = 0; - - var list = List; - - for (var i = index; i >= 0 && i < list.Count; ++i) - { - var entry = list[i]; - - if (CheckFilter(entry)) - { - var add = entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; - - if (slots + add > 10) - { - break; - } - - slots += add; + AddImageTiled(24, 64, 32, 352, 1416); } - ++count; - } + AddImageTiled(58, 64, 36, 352, 200); + AddImageTiled(96, 64, 133, 352, 1416); + AddImageTiled(231, 64, 80, 352, 200); + AddImageTiled(313, 64, 100, 352, 1416); + AddImageTiled(415, 64, 76, 352, 200); - return count; - } - - public int GetPageForIndex(int index, int sizeDropped) - { - if (index <= 0) - { - return 0; - } - - var count = 0; - var page = 0; - int i; - - var list = List; - for (i = 0; i < index && i < list.Count; i++) - { - var entry = list[i]; - if (!CheckFilter(entry)) - { - continue; - } - - var add = entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; - count += add; - if (count > 10) - { - page++; - count = add; - } - } - - /* now we are on the page of the bod preceding the dropped one. - * next step: checking whether we have to remain where we are. - * The counter i needs to be incremented as the bod to this very moment - * has not yet been removed from m_List */ - i++; - - /* if, for instance, a big bod of size 6 has been removed, smaller bods - * might fall back into this page. Depending on their sizes, the page needs - * to be adjusted accordingly. This is done now. - */ - if (count + sizeDropped > 10) - { - while (i < list.Count && count <= 10) + for (var i = index; i < index + count && i >= 0 && i < list.Count; ++i) { var entry = list[i]; - if (CheckFilter(entry)) + + if (!CheckFilter(entry)) { - count += entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; + continue; } - i++; + AddImageTiled(24, 94 + tableIndex * 32, canPrice ? 573 : 489, 2, 2624); + tableIndex += entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; } - if (count > 10) + AddAlphaRegion(18, 20, width - 17, 420); + AddImage(5, 5, 10460); + AddImage(width - 15, 5, 10460); + AddImage(5, 424, 10460); + AddImage(width - 15, 424, 10460); + + AddHtmlLocalized(canPrice ? 266 : 224, 32, 200, 32, 1062220, LabelColor); // Bulk Order Book + AddHtmlLocalized(63, 64, 200, 32, 1062213, LabelColor); // Type + AddHtmlLocalized(147, 64, 200, 32, 1062214, LabelColor); // Item + AddHtmlLocalized(246, 64, 200, 32, 1062215, LabelColor); // Quality + AddHtmlLocalized(336, 64, 200, 32, 1062216, LabelColor); // Material + AddHtmlLocalized(429, 64, 200, 32, 1062217, LabelColor); // Amount + + AddButton(35, 32, 4005, 4007, 1); + AddHtmlLocalized(70, 32, 200, 32, 1062476, LabelColor); // Set Filter + + var f = from.UseOwnFilter ? from.BOBFilter : book.Filter; + + if (f.IsDefault) { - page++; + AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062475, 16927); // Using No Filter + } + else if (from.UseOwnFilter) + { + AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062451, 16927); // Using Your Filter + } + else + { + AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062230, 16927); // Using Book Filter + } + + AddButton(375, 416, 4017, 4018, 0); + AddHtmlLocalized(410, 416, 120, 20, 1011441, LabelColor); // EXIT + + if (canDrop) + { + AddHtmlLocalized(26, 64, 50, 32, 1062212, LabelColor); // Drop + } + + if (canPrice) + { + AddHtmlLocalized(516, 64, 200, 32, 1062218, LabelColor); // Price + + if (canBuy) + { + AddHtmlLocalized(576, 64, 200, 32, 1062219, LabelColor); // Buy + } + else + { + AddHtmlLocalized(576, 64, 200, 32, 1062227, LabelColor); // Set + + AddButton(450, 416, 4005, 4007, 4); + AddHtml(485, 416, 120, 20, "Price all"); + } + } + + tableIndex = 0; + + if (page > 0) + { + AddButton(75, 416, 4014, 4016, 2); + AddHtmlLocalized(110, 416, 150, 20, 1011067, LabelColor); // Previous page + } + + if (GetIndexForPage(page + 1) < list.Count) + { + AddButton(225, 416, 4005, 4007, 3); + AddHtmlLocalized(260, 416, 150, 20, 1011066, LabelColor); // Next page + } + + for (var i = index; i < index + count && i >= 0 && i < list.Count; ++i) + { + var entry = list[i]; + + if (!CheckFilter(entry)) + { + continue; + } + + if (entry is BOBLargeEntry largeEntry) + { + var y = 96 + tableIndex * 32; + + if (canDrop) + { + AddButton(35, y + 2, 5602, 5606, 5 + i * 2); + } + + if (canDrop || canBuy && entry.Price > 0) + { + AddButton(579, y + 2, 2117, 2118, 6 + i * 2); + AddLabel(495, y, 1152, entry.Price.ToString()); + } + + AddHtmlLocalized(61, y, 50, 32, 1062225, LabelColor); // Large + + for (var j = 0; j < largeEntry.Entries.Length; ++j) + { + var sub = largeEntry.Entries[j]; + + AddHtmlLocalized(103, y, 130, 32, sub.Number, LabelColor); + + if (entry.RequireExceptional) + { + AddHtmlLocalized(235, y, 80, 20, 1060636, LabelColor); // exceptional + } + else + { + AddHtmlLocalized(235, y, 80, 20, 1011542, LabelColor); // normal + } + + var name = GetMaterialName(entry.Material, entry.DeedType, sub.ItemType); + + if (name.Number > 0) + { + AddHtmlLocalized(316, y, 100, 20, name, LabelColor); + } + else + { + AddLabel(316, y, 1152, name); + } + + AddLabel(421, y, 1152, $"{sub.AmountCur} / {entry.AmountMax}"); + + ++tableIndex; + y += 32; + } + } + else + { + var smallEntry = (BOBSmallEntry)entry; + + var y = 96 + tableIndex++ * 32; + + if (canDrop) + { + AddButton(35, y + 2, 5602, 5606, 5 + i * 2); + } + + if (canDrop || canBuy && smallEntry.Price > 0) + { + AddButton(579, y + 2, 2117, 2118, 6 + i * 2); + AddLabel(495, y, 1152, smallEntry.Price.ToString()); + } + + AddHtmlLocalized(61, y, 50, 32, 1062224, LabelColor); // Small + + AddHtmlLocalized(103, y, 130, 32, smallEntry.Number, LabelColor); + + if (smallEntry.RequireExceptional) + { + AddHtmlLocalized(235, y, 80, 20, 1060636, LabelColor); // exceptional + } + else + { + AddHtmlLocalized(235, y, 80, 20, 1011542, LabelColor); // normal + } + + var name = GetMaterialName(smallEntry.Material, smallEntry.DeedType, smallEntry.ItemType); + + if (name.Number > 0) + { + AddHtmlLocalized(316, y, 100, 20, name, LabelColor); + } + else + { + AddLabel(316, y, 1152, name); + } + + AddLabel(421, y, 1152, $"{smallEntry.AmountCur} / {smallEntry.AmountMax}"); + } } } - return page; - } - - public static TextDefinition GetMaterialName(BulkMaterialType mat, BODType type, Type itemType) => - type switch + public bool CheckFilter(IBOBEntry entry) { - BODType.Smith => mat switch + if (entry is BOBLargeEntry largeEntry) { - BulkMaterialType.None => 1062226, - BulkMaterialType.DullCopper => 1018332, - BulkMaterialType.ShadowIron => 1018333, - BulkMaterialType.Copper => 1018334, - BulkMaterialType.Bronze => 1018335, - BulkMaterialType.Gold => 1018336, - BulkMaterialType.Agapite => 1018337, - BulkMaterialType.Verite => 1018338, - BulkMaterialType.Valorite => 1018339, - _ => 1062226 - }, - BODType.Tailor => mat switch + return CheckFilter( + entry.Material, + entry.AmountMax, + true, + entry.RequireExceptional, + entry.DeedType, + largeEntry.Entries.Length > 0 ? largeEntry.Entries[0].ItemType : null + ); + } + + if (entry is BOBSmallEntry smallEntry) { - BulkMaterialType.Spined => 1062236, - BulkMaterialType.Horned => 1062237, - BulkMaterialType.Barbed => 1062238, - _ when itemType.IsSubclassOf(typeof(BaseArmor)) || - itemType.IsSubclassOf(typeof(BaseShoes)) => 1062235, - _ => 1044286 - }, - _ => TextDefinition.Empty - }; + return CheckFilter( + entry.Material, + entry.AmountMax, + false, + entry.RequireExceptional, + entry.DeedType, + smallEntry.ItemType + ); + } - public override void SendTo(NetState ns) - { - ns.CloseGump(); + return false; + } - base.SendTo(ns); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var index = info.ButtonID; - - switch (index) + public bool CheckFilter( + BulkMaterialType mat, int amountMax, bool isLarge, bool reqExc, BODType deedType, + Type itemType + ) { - case 0: // EXIT - { - break; - } - case 1: // Set Filter - { - _from.SendGump(new BOBFilterGump(_from, Book)); + var f = _from.UseOwnFilter ? _from.BOBFilter : _book.Filter; - break; - } - case 2: // Previous page + if (f.IsDefault) + { + return true; + } + + if (f.Quality == 1 && reqExc) + { + return false; + } + + if (f.Quality == 2 && !reqExc) + { + return false; + } + + if (f.Quantity == 1 && amountMax != 10) + { + return false; + } + + if (f.Quantity == 2 && amountMax != 15) + { + return false; + } + + if (f.Quantity == 3 && amountMax != 20) + { + return false; + } + + if (f.Type == 1 && isLarge) + { + return false; + } + + if (f.Type == 2 && !isLarge) + { + return false; + } + + return f.Material switch + { + 1 => deedType == BODType.Smith, + 2 => deedType == BODType.Tailor, + 3 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Iron, + 4 => mat == BulkMaterialType.DullCopper, + 5 => mat == BulkMaterialType.ShadowIron, + 6 => mat == BulkMaterialType.Copper, + 7 => mat == BulkMaterialType.Bronze, + 8 => mat == BulkMaterialType.Gold, + 9 => mat == BulkMaterialType.Agapite, + 10 => mat == BulkMaterialType.Verite, + 11 => mat == BulkMaterialType.Valorite, + 12 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Cloth, + 13 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Leather, + 14 => mat == BulkMaterialType.Spined, + 15 => mat == BulkMaterialType.Horned, + 16 => mat == BulkMaterialType.Barbed, + _ => true + }; + } + + public int GetIndexForPage(int page) + { + var index = 0; + + while (page-- > 0) + { + index += GetCountForIndex(index); + } + + return index; + } + + public int GetCountForIndex(int index) + { + var slots = 0; + var count = 0; + + var list = _list; + + for (var i = index; i >= 0 && i < list.Count; ++i) + { + var entry = list[i]; + + if (CheckFilter(entry)) { - if (_page > 0) - { - _page--; - _from.SendGump(this); - } + var add = entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; - return; - } - case 3: // Next page - { - if (GetIndexForPage(_page + 1) < List.Count) - { - _page++; - _from.SendGump(this); - } - - break; - } - case 4: // Price all - { - if (Book.IsChildOf(_from.Backpack)) - { - _from.Prompt = new SetPricePrompt(this, null); - _from.SendMessage("Type in a price for all deeds in the book:"); - } - - break; - } - default: - { - index -= 5; - - var type = index % 2; - index /= 2; - - if (index < 0 || index >= List.Count) + if (slots + add > 10) { break; } - var bobEntry = List[index]; + slots += add; + } - if (!Book.Entries.Contains(bobEntry)) + ++count; + } + + return count; + } + + public int GetPageForIndex(int index, int sizeDropped) + { + if (index <= 0) + { + return 0; + } + + var count = 0; + var page = 0; + int i; + + var list = _list; + for (i = 0; i < index && i < list.Count; i++) + { + var entry = list[i]; + if (!CheckFilter(entry)) + { + continue; + } + + var add = entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; + count += add; + if (count > 10) + { + page++; + count = add; + } + } + + /* now we are on the page of the bod preceding the dropped one. + * next step: checking whether we have to remain where we are. + * The counter i needs to be incremented as the bod to this very moment + * has not yet been removed from m_List */ + i++; + + /* if, for instance, a big bod of size 6 has been removed, smaller bods + * might fall back into this page. Depending on their sizes, the page needs + * to be adjusted accordingly. This is done now. + */ + if (count + sizeDropped > 10) + { + while (i < list.Count && count <= 10) + { + var entry = list[i]; + if (CheckFilter(entry)) { - _from.SendLocalizedMessage(1062382); // The deed selected is not available. - break; + count += entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; } - if (Book.IsChildOf(_from.Backpack)) + i++; + } + + if (count > 10) + { + page++; + } + } + + return page; + } + + public TextDefinition GetMaterialName(BulkMaterialType mat, BODType type, Type itemType) + { + switch (type) + { + case BODType.Smith: { + switch (mat) + { + case BulkMaterialType.None: return 1062226; + case BulkMaterialType.DullCopper: return 1018332; + case BulkMaterialType.ShadowIron: return 1018333; + case BulkMaterialType.Copper: return 1018334; + case BulkMaterialType.Bronze: return 1018335; + case BulkMaterialType.Gold: return 1018336; + case BulkMaterialType.Agapite: return 1018337; + case BulkMaterialType.Verite: return 1018338; + case BulkMaterialType.Valorite: return 1018339; + } + + break; + } + case BODType.Tailor: + { + switch (mat) + { + case BulkMaterialType.None: + { + if (itemType.IsSubclassOf(typeof(BaseArmor)) || itemType.IsSubclassOf(typeof(BaseShoes))) + { + return 1062235; + } + + return 1044286; + } + case BulkMaterialType.Spined: return 1062236; + case BulkMaterialType.Horned: return 1062237; + case BulkMaterialType.Barbed: return 1062238; + } + + break; + } + } + + return "Invalid"; + } + + public override void SendTo(NetState ns) + { + ns.CloseGump(); + + base.SendTo(ns); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + var index = info.ButtonID; + + switch (index) + { + case 0: // EXIT + { + break; + } + case 1: // Set Filter + { + _from.SendGump(new BOBFilterGump(_from, _book)); + + break; + } + case 2: // Previous page + { + if (_page > 0) + { + _from.SendGump(new BOBGump(_from, _book, _page - 1, _list)); + } + + return; + } + case 3: // Next page + { + if (GetIndexForPage(_page + 1) < _list.Count) + { + _from.SendGump(new BOBGump(_from, _book, _page + 1, _list)); + } + + break; + } + case 4: // Price all + { + if (_book.IsChildOf(_from.Backpack)) + { + _from.Prompt = new SetPricePrompt(_book, null, _page, _list); + _from.SendMessage("Type in a price for all deeds in the book:"); + } + + break; + } + default: + { + index -= 5; + + var type = index % 2; + index /= 2; + + if (index < 0 || index >= _list.Count) + { + break; + } + + var bobEntry = _list[index]; + + if (!_book.Entries.Contains(bobEntry)) + { + _from.SendLocalizedMessage(1062382); // The deed selected is not available. + break; + } + if (type == 0) // Drop { - var item = bobEntry.Reconstruct(); + if (_book.IsChildOf(_from.Backpack)) + { + var item = bobEntry.Reconstruct(); - var pack = _from.Backpack; - if (pack?.CheckHold( + var pack = _from.Backpack; + if (pack?.CheckHold( _from, item, true, @@ -605,126 +610,147 @@ public class BOBGump : DynamicGump 0, item.PileWeight + item.TotalWeight ) != true) - { - _from.SendLocalizedMessage(503204); // You do not have room in your backpack for this - ResetList(); - _from.SendGump(this); - } - else - { - var sizeOfDroppedBod = bobEntry is BOBLargeEntry entry ? entry.Entries.Length : 1; - - _from.AddToBackpack(item); - - // The bulk order deed has been placed in your backpack. - _from.SendLocalizedMessage(1045152); - - Book.RemoveEntry(bobEntry); - - if (Book.Entries.Count / 5 < Book.ItemCount) { - Book.ItemCount--; - Book.InvalidateItems(); - } - - if (Book.Entries.Count > 0) - { - _page = GetPageForIndex(index, sizeOfDroppedBod); - ResetList(); - _from.SendGump(this); + _from.SendLocalizedMessage(503204); // You do not have room in your backpack for this + _from.SendGump(new BOBGump(_from, _book, _page)); } else { - _from.SendLocalizedMessage(1062381); // The book is empty. + if (_book.IsChildOf(_from.Backpack)) + { + var sizeOfDroppedBod = bobEntry is BOBLargeEntry entry ? entry.Entries.Length : 1; + + _from.AddToBackpack(item); + + // The bulk order deed has been placed in your backpack. + _from.SendLocalizedMessage(1045152); + + _book.Entries.Remove(bobEntry); + _book.InvalidateProperties(); + + if (_book.Entries.Count / 5 < _book.ItemCount) + { + _book.ItemCount--; + _book.InvalidateItems(); + } + + if (_book.Entries.Count > 0) + { + _page = GetPageForIndex(index, sizeOfDroppedBod); + _from.SendGump(new BOBGump(_from, _book, _page)); + } + else + { + _from.SendLocalizedMessage(1062381); // The book is empty. + } + } } } } else // Set Price | Buy { - _from.Prompt = new SetPricePrompt(this, bobEntry); - _from.SendLocalizedMessage(1062383); // Type in a price for the deed: + if (_book.IsChildOf(_from.Backpack)) + { + _from.Prompt = new SetPricePrompt(_book, bobEntry, _page, _list); + _from.SendLocalizedMessage(1062383); // Type in a price for the deed: + } + else if (_book.RootParent is PlayerVendor pv) + { + var vi = pv.GetVendorItem(_book); + + if (vi?.IsForSale != false) + { + return; + } + + var sizeOfDroppedBod = bobEntry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; + var price = bobEntry.Price; + + if (price == 0) + { + _from.SendLocalizedMessage(1062382); // The deed selected is not available. + } + else + { + if (_book.Entries.Count > 0) + { + _page = GetPageForIndex(index, sizeOfDroppedBod); + _from.SendGump(new BODBuyGump(_from, _book, bobEntry, _page, price)); + } + else + { + _from.SendLocalizedMessage(1062381); // The book is emptz + } + } + } } + + break; } - else if (Book.RootParent is PlayerVendor pv) - { - var vi = pv.GetVendorItem(Book); - - if (vi?.IsForSale != false) - { - return; - } - - var sizeOfDroppedBod = bobEntry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; - var price = bobEntry.Price; - - if (price == 0) - { - _from.SendLocalizedMessage(1062382); // The deed selected is not available. - } - else if (Book.Entries.Count > 0) - { - _page = GetPageForIndex(index, sizeOfDroppedBod); - _from.SendGump(new BODBuyGump(this, bobEntry, price)); - } - else - { - _from.SendLocalizedMessage(1062381); // The book is empty - } - } - - break; - } - } - } - - private class SetPricePrompt : Prompt - { - private readonly BOBGump _gump; - private readonly IBOBEntry _entry; - - public SetPricePrompt(BOBGump gump, IBOBEntry entry) - { - _gump = gump; - _entry = entry; + } } - public override void OnResponse(Mobile from, string text) + private class SetPricePrompt : Prompt { - if (_entry != null && !_gump.Book.Entries.Contains(_entry)) + private readonly BulkOrderBook m_Book; + private readonly IBOBEntry m_Entry; + private readonly List m_List; + private readonly int m_Page; + + public SetPricePrompt(BulkOrderBook book, IBOBEntry entry, int page, List list) { - from.SendLocalizedMessage(1062382); // The deed selected is not available. - return; + m_Book = book; + m_Entry = entry; + m_Page = page; + m_List = list; } - var price = Utility.ToInt32(text); - - if (price is < 0 or > 250000000) + public override void OnResponse(Mobile from, string text) { - from.SendLocalizedMessage(1062390); // The price you requested is outrageous! - return; - } - - if (_entry == null) - { - for (var i = 0; i < _gump.List.Count; ++i) + if (m_Entry != null && !m_Book.Entries.Contains(m_Entry)) { - var entry = _gump.List[i]; + from.SendLocalizedMessage(1062382); // The deed selected is not available. + return; + } - if (!_gump.Book.Entries.Contains(entry)) + var price = Utility.ToInt32(text); + + if (price is < 0 or > 250000000) + { + from.SendLocalizedMessage(1062390); // The price you requested is outrageous! + } + else if (m_Entry == null) + { + for (var i = 0; i < m_List.Count; ++i) { - continue; + var entry = m_List[i]; + + if (!m_Book.Entries.Contains(entry)) + { + continue; + } + + entry.Price = price; } - entry.Price = price; + // Deed price set. + from.SendLocalizedMessage(1062384); + + if (from is PlayerMobile mobile) + { + mobile.SendGump(new BOBGump(mobile, m_Book, m_Page, m_List)); + } + } + else + { + m_Entry.Price = price; + from.SendLocalizedMessage(1062384); // Deed price set. + if (from is PlayerMobile mobile) + { + mobile.SendGump(new BOBGump(mobile, m_Book, m_Page, m_List)); + } } } - else - { - _entry.Price = price; - } - - from.SendLocalizedMessage(1062384); // Deed price set. - from.SendGump(_gump); } } } diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BODBuyGump.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BODBuyGump.cs index 7f1120b8f..06ab0f72d 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BODBuyGump.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BODBuyGump.cs @@ -3,142 +3,135 @@ using Server.Items; using Server.Mobiles; using Server.Network; -namespace Server.Engines.BulkOrders; - -public class BODBuyGump : StaticGump +namespace Server.Engines.BulkOrders { - private BOBGump _gump; - private readonly IBOBEntry _entry; - private readonly int _price; - - public BODBuyGump(BOBGump gump, IBOBEntry entry, int price) : base(100, 200) + public class BODBuyGump : Gump { - _gump = gump; - _entry = entry; - _price = price; - } + private readonly BulkOrderBook m_Book; + private readonly IBOBEntry m_Entry; + private readonly PlayerMobile m_From; + private readonly int m_Page; + private readonly int m_Price; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(100, 10, 300, 150, 5054); - - builder.AddHtmlLocalized(125, 20, 250, 24, 1019070); // You have agreed to purchase: - builder.AddHtmlLocalized(125, 45, 250, 24, 1045151); // a bulk order deed - - builder.AddHtmlLocalized(125, 70, 250, 24, 1019071); // for the amount of: - builder.AddLabelPlaceholder(125, 95, 0, "price"); - - builder.AddButton(250, 130, 4005, 4007, 1); - builder.AddHtmlLocalized(282, 130, 100, 24, 1011012); // CANCEL - - builder.AddButton(120, 130, 4005, 4007, 2); - builder.AddHtmlLocalized(152, 130, 100, 24, 1011036); // OKAY - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - builder.SetStringSlot("price", $"{_price:N0}"); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (sender.Mobile is not PlayerMobile pm) + public BODBuyGump(PlayerMobile from, BulkOrderBook book, IBOBEntry entry, int page, int price) : base(100, 200) { - return; + m_From = from; + m_Book = book; + m_Entry = entry; + m_Price = price; + m_Page = page; + + AddPage(0); + + AddBackground(100, 10, 300, 150, 5054); + + AddHtmlLocalized(125, 20, 250, 24, 1019070); // You have agreed to purchase: + AddHtmlLocalized(125, 45, 250, 24, 1045151); // a bulk order deed + + AddHtmlLocalized(125, 70, 250, 24, 1019071); // for the amount of: + AddLabel(125, 95, 0, price.ToString()); + + AddButton(250, 130, 4005, 4007, 1); + AddHtmlLocalized(282, 130, 100, 24, 1011012); // CANCEL + + AddButton(120, 130, 4005, 4007, 2); + AddHtmlLocalized(152, 130, 100, 24, 1011036); // OKAY } - if (info.ButtonID != 2) + public override void OnResponse(NetState sender, in RelayInfo info) { - pm.SendLocalizedMessage(503207); // Cancelled purchase. - return; - } + if (info.ButtonID != 2) + { + m_From.SendLocalizedMessage(503207); // Cancelled purchase. + return; + } - var book = _gump.Book; + if (m_Book.RootParent is not PlayerVendor pv) + { + m_From.SendLocalizedMessage(1062382); // The deed selected is not available. + return; + } - if (book.RootParent is not PlayerVendor pv) - { - pm.SendLocalizedMessage(1062382); // The deed selected is not available. - return; - } + if (!m_Book.Entries.Contains(m_Entry)) + { + pv.SayTo(m_From, 1062382); // The deed selected is not available. + return; + } - if (!book.Entries.Contains(_entry)) - { - pv.SayTo(pm, 1062382); // The deed selected is not available. - return; - } + var price = 0; - var price = 0; + if (pv.GetVendorItem(m_Book)?.IsForSale == false) + { + price = m_Entry.Price; + } - if (pv.GetVendorItem(book)?.IsForSale == false) - { - price = _entry.Price; - } + if (price != m_Price) + { + pv.SayTo( + m_From, + "The price has been been changed. If you like, you may offer to purchase the item again." + ); + return; + } - if (price != _price) - { - pv.SayTo( - pm, - "The price has been been changed. If you like, you may offer to purchase the item again." - ); - return; - } + if (price == 0) + { + pv.SayTo(m_From, 1062382); // The deed selected is not available. + return; + } - if (price == 0) - { - pv.SayTo(pm, 1062382); // The deed selected is not available. - return; - } + var item = m_Entry.Reconstruct(); - var item = _entry.Reconstruct(); + pv.Say(m_From.Name); - pv.Say(pm.Name); + var pack = m_From.Backpack; - var pack = pm.Backpack; - - if (pack?.CheckHold( - pm, + if (pack?.CheckHold( + m_From, item, true, true, 0, item.PileWeight + item.TotalWeight ) != true) - { - pv.SayTo(pm, 503204); // You do not have room in your backpack for this - pm.SendGump(_gump); - item.Delete(); - } - else if (pack.ConsumeTotal(typeof(Gold), price) || Banker.Withdraw(pm, price)) - { - book.RemoveEntry(_entry); - pv.HoldGold += price; - pm.AddToBackpack(item); - - // The bulk order deed has been placed in your backpack. - pm.SendLocalizedMessage(1045152); - - if (book.Entries.Count / 5 < book.ItemCount) { - book.ItemCount--; - book.InvalidateItems(); - } - - if (book.Entries.Count > 0) - { - _gump.ResetList(); - pm.SendGump(_gump); + pv.SayTo(m_From, 503204); // You do not have room in your backpack for this + m_From.SendGump(new BOBGump(m_From, m_Book, m_Page)); + item.Delete(); } else { - pm.SendLocalizedMessage(1062381); // The book is empty. + if (pack.ConsumeTotal(typeof(Gold), price) || Banker.Withdraw(m_From, price)) + { + m_Book.RemoveEntry(m_Entry); + m_Book.InvalidateProperties(); + pv.HoldGold += price; + m_From.AddToBackpack(item); + + // The bulk order deed has been placed in your backpack. + m_From.SendLocalizedMessage(1045152); + + if (m_Book.Entries.Count / 5 < m_Book.ItemCount) + { + m_Book.ItemCount--; + m_Book.InvalidateItems(); + } + + if (m_Book.Entries.Count > 0) + { + m_From.SendGump(new BOBGump(m_From, m_Book, m_Page)); + } + else + { + m_From.SendLocalizedMessage(1062381); // The book is empty. + } + } + else + { + pv.SayTo(m_From, 503205); // You cannot afford this item. + item.Delete(); + } } } - else - { - pv.SayTo(pm, 503205); // You cannot afford this item. - item.Delete(); - } } } diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BaseBOBEntry.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BaseBOBEntry.cs index d3bbadb45..163dc3a32 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BaseBOBEntry.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BaseBOBEntry.cs @@ -26,6 +26,10 @@ public abstract partial class BaseBOBEntry : IBOBEntry public Serial Serial { get; } + public byte SerializedThread { get; set; } + public int SerializedPosition { get; set; } + public int SerializedLength { get; set; } + public bool Deleted { get; private set; } public BaseBOBEntry() diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs index a0c17c575..4e67ca620 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs @@ -40,6 +40,7 @@ public partial class BulkOrderBook : Item, ISecurable [Constructible] public BulkOrderBook() : base(0x2259) { + Weight = 1.0; LootType = LootType.Blessed; _entries = []; @@ -48,8 +49,6 @@ public partial class BulkOrderBook : Item, ISecurable _level = SecureLevel.CoOwners; } - public override double DefaultWeight => 1.0; - public override void OnAfterDuped(Item newItem) { if (newItem is not BulkOrderBook book) diff --git a/Projects/UOContent/Engines/Bulk Orders/LargeBOD.cs b/Projects/UOContent/Engines/Bulk Orders/LargeBOD.cs index c648e7390..faca67cf9 100644 --- a/Projects/UOContent/Engines/Bulk Orders/LargeBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/LargeBOD.cs @@ -1,7 +1,6 @@ using ModernUO.Serialization; using Server.Gumps; using Server.Mobiles; -using Server.Systems.FeatureFlags; namespace Server.Engines.BulkOrders { @@ -14,7 +13,8 @@ namespace Server.Engines.BulkOrders public LargeBOD( int hue, int amountMax, bool requireExeptional, BulkMaterialType material, LargeBulkEntry[] entries - ) : base(hue, amountMax, requireExeptional, material) => _entries = entries; + ) : base(hue, amountMax, requireExeptional, material) => + _entries = entries; public LargeBOD() { @@ -76,19 +76,14 @@ namespace Server.Engines.BulkOrders public override void OnDoubleClick(Mobile from) { - if (!(IsChildOf(from.Backpack) || InSecureTrade || RootParent is PlayerVendor)) + if (IsChildOf(from.Backpack) || InSecureTrade || RootParent is PlayerVendor) + { + from.SendGump(new LargeBODGump(this)); + } + else { from.SendLocalizedMessage(1045156); // You must have the deed in your backpack to use it. - return; } - - if (!ContentFeatureFlags.BulkOrders && from.AccessLevel < AccessLevel.Administrator) - { - from.SendMessage(0x22, "Bulk orders are temporarily disabled."); - return; - } - - from.SendGump(new LargeBODGump(this)); } public override void EndCombine(Mobile from, Item item) diff --git a/Projects/UOContent/Engines/Bulk Orders/LargeBODAcceptGump.cs b/Projects/UOContent/Engines/Bulk Orders/LargeBODAcceptGump.cs index e9abcab53..074f11cf2 100644 --- a/Projects/UOContent/Engines/Bulk Orders/LargeBODAcceptGump.cs +++ b/Projects/UOContent/Engines/Bulk Orders/LargeBODAcceptGump.cs @@ -81,7 +81,7 @@ namespace Server.Engines.BulkOrders public override void OnResponse(NetState sender, in RelayInfo info) { - var from = sender.Mobile; + Mobile from = sender.Mobile; if (info.ButtonID == 1) // Ok { diff --git a/Projects/UOContent/Engines/Bulk Orders/LargeBODGump.cs b/Projects/UOContent/Engines/Bulk Orders/LargeBODGump.cs index b66335a4a..4c5938e54 100644 --- a/Projects/UOContent/Engines/Bulk Orders/LargeBODGump.cs +++ b/Projects/UOContent/Engines/Bulk Orders/LargeBODGump.cs @@ -89,7 +89,7 @@ namespace Server.Engines.BulkOrders public override void OnResponse(NetState sender, in RelayInfo info) { - var from = sender.Mobile; + Mobile from = sender.Mobile; if (_deed.Deleted || !_deed.IsChildOf(from.Backpack)) { diff --git a/Projects/UOContent/Engines/Bulk Orders/SmallBOD.cs b/Projects/UOContent/Engines/Bulk Orders/SmallBOD.cs index 6356780de..3a989b9e0 100644 --- a/Projects/UOContent/Engines/Bulk Orders/SmallBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/SmallBOD.cs @@ -1,168 +1,232 @@ using System; -using ModernUO.Serialization; using Server.Gumps; using Server.Items; using Server.Mobiles; -using Server.Systems.FeatureFlags; -namespace Server.Engines.BulkOrders; - -[SerializationGenerator(0, false)] -public abstract partial class SmallBOD : BaseBOD +namespace Server.Engines.BulkOrders { - [InvalidateProperties] - [SerializableField(0)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _amountCur; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Type _type; - - [InvalidateProperties] - [SerializableField(2)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _number; - - [SerializableField(3)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _graphic; - - public SmallBOD( - int hue, int amountCur, int amountMax, Type type, int number, int graphic, bool requireExeptional, - BulkMaterialType material - ) : base(hue, amountMax, requireExeptional, material) + public abstract class SmallBOD : BaseBOD { - _type = type; - _graphic = graphic; - _amountCur = amountCur; - _number = number; - } + private int m_AmountCur; + private int m_Number; - public SmallBOD() - { - } - - [CommandProperty(AccessLevel.GameMaster)] - public override bool Complete => _amountCur == AmountMax; - - public override int LabelNumber => 1045151; // a bulk order deed - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - - list.Add(1060654); // small bulk order - - if (RequireExceptional) + public SmallBOD( + int hue, int amountCur, int amountMax, Type type, int number, int graphic, bool requireExeptional, + BulkMaterialType material + ) : base(hue, amountMax, requireExeptional, material) { - list.Add(1045141); // All items must be exceptional. + Type = type; + Graphic = graphic; + m_AmountCur = amountCur; + m_Number = number; } - if (Material != BulkMaterialType.None) + public SmallBOD() { - list.Add(SmallBODGump.GetMaterialNumberFor(Material)); // All items must be made with x material. } - list.Add(1060656, AmountMax); // amount to make: ~1_val~ - list.Add(1060658, $"{_number:#}\t{_amountCur}"); // ~1_val~: ~2_val~ - } - - public override void OnDoubleClick(Mobile from) - { - if (!(IsChildOf(from.Backpack) || InSecureTrade || RootParent is PlayerVendor)) + public SmallBOD(Serial serial) : base(serial) { - from.SendLocalizedMessage(1045156); // You must have the deed in your backpack to use it. - return; } - if (!ContentFeatureFlags.BulkOrders && from.AccessLevel < AccessLevel.Administrator) + [CommandProperty(AccessLevel.GameMaster)] + public int AmountCur { - from.SendMessage(0x22, "Bulk orders are temporarily disabled."); - return; - } - - from.SendGump(new SmallBODGump(this)); - } - - public override void OnDoubleClickNotAccessible(Mobile from) - { - OnDoubleClick(from); - } - - public override void OnDoubleClickSecureTrade(Mobile from) - { - OnDoubleClick(from); - } - - public static BulkMaterialType GetMaterial(CraftResource resource) - { - return resource switch - { - CraftResource.DullCopper => BulkMaterialType.DullCopper, - CraftResource.ShadowIron => BulkMaterialType.ShadowIron, - CraftResource.Copper => BulkMaterialType.Copper, - CraftResource.Bronze => BulkMaterialType.Bronze, - CraftResource.Gold => BulkMaterialType.Gold, - CraftResource.Agapite => BulkMaterialType.Agapite, - CraftResource.Verite => BulkMaterialType.Verite, - CraftResource.Valorite => BulkMaterialType.Valorite, - CraftResource.SpinedLeather => BulkMaterialType.Spined, - CraftResource.HornedLeather => BulkMaterialType.Horned, - CraftResource.BarbedLeather => BulkMaterialType.Barbed, - _ => BulkMaterialType.None - }; - } - - public override void EndCombine(Mobile from, Item item) - { - var objectType = item.GetType(); - - if (_amountCur >= AmountMax) - { - // The maximum amount of requested items have already been combined to this deed. - from.SendLocalizedMessage(1045166); - return; - } - var armor = item as BaseArmor; - var clothing = item as BaseClothing; - var weapon = item as BaseWeapon; - - - if (Type == null || objectType != Type && !objectType.IsSubclassOf(Type) || armor == null && clothing == null && weapon == null) - { - from.SendLocalizedMessage(1045169); // The item is not in the request. - } - else - { - var material = GetMaterial(armor?.Resource ?? clothing?.Resource ?? CraftResource.None); - - if (Material >= BulkMaterialType.DullCopper && Material <= BulkMaterialType.Valorite && material != Material) + get => m_AmountCur; + set { - from.SendLocalizedMessage(1045168); // The item is not made from the requested ore. + m_AmountCur = value; + InvalidateProperties(); } - else if (Material >= BulkMaterialType.Spined && Material <= BulkMaterialType.Barbed && material != Material) + } + + [CommandProperty(AccessLevel.GameMaster)] + public Type Type { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public int Number + { + get => m_Number; + set { - from.SendLocalizedMessage(1049352); // The item is not made from the requested leather type. + m_Number = value; + InvalidateProperties(); } - else if (RequireExceptional && armor?.Quality != ArmorQuality.Exceptional && - clothing?.Quality != ClothingQuality.Exceptional && - weapon?.Quality != WeaponQuality.Exceptional) + } + + [CommandProperty(AccessLevel.GameMaster)] + public int Graphic { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public override bool Complete => m_AmountCur == AmountMax; + + public override int LabelNumber => 1045151; // a bulk order deed + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + list.Add(1060654); // small bulk order + + if (RequireExceptional) { - from.SendLocalizedMessage(1045167); // The item must be exceptional. + list.Add(1045141); // All items must be exceptional. + } + + if (Material != BulkMaterialType.None) + { + list.Add(SmallBODGump.GetMaterialNumberFor(Material)); // All items must be made with x material. + } + + list.Add(1060656, AmountMax); // amount to make: ~1_val~ + list.Add(1060658, $"{m_Number:#}\t{m_AmountCur}"); // ~1_val~: ~2_val~ + } + + public override void OnDoubleClick(Mobile from) + { + if (IsChildOf(from.Backpack) || InSecureTrade || RootParent is PlayerVendor) + { + from.SendGump(new SmallBODGump(this)); } else { - item.Delete(); - ++AmountCur; + from.SendLocalizedMessage(1045156); // You must have the deed in your backpack to use it. + } + } - from.SendLocalizedMessage(1045170); // The item has been combined with the deed. - from.SendGump(new SmallBODGump(this)); + public override void OnDoubleClickNotAccessible(Mobile from) + { + OnDoubleClick(from); + } - if (_amountCur < AmountMax) + public override void OnDoubleClickSecureTrade(Mobile from) + { + OnDoubleClick(from); + } + + public static BulkMaterialType GetMaterial(CraftResource resource) + { + return resource switch + { + CraftResource.DullCopper => BulkMaterialType.DullCopper, + CraftResource.ShadowIron => BulkMaterialType.ShadowIron, + CraftResource.Copper => BulkMaterialType.Copper, + CraftResource.Bronze => BulkMaterialType.Bronze, + CraftResource.Gold => BulkMaterialType.Gold, + CraftResource.Agapite => BulkMaterialType.Agapite, + CraftResource.Verite => BulkMaterialType.Verite, + CraftResource.Valorite => BulkMaterialType.Valorite, + CraftResource.SpinedLeather => BulkMaterialType.Spined, + CraftResource.HornedLeather => BulkMaterialType.Horned, + CraftResource.BarbedLeather => BulkMaterialType.Barbed, + _ => BulkMaterialType.None + }; + } + + public override void EndCombine(Mobile from, Item item) + { + var objectType = item.GetType(); + + if (m_AmountCur >= AmountMax) + { + // The maximum amount of requested items have already been combined to this deed. + from.SendLocalizedMessage(1045166); + } + else if (Type == null || objectType != Type && !objectType.IsSubclassOf(Type) || + item is not BaseWeapon && item is not BaseArmor && item is not BaseClothing) + { + from.SendLocalizedMessage(1045169); // The item is not in the request. + } + else + { + var armor = item as BaseArmor; + var clothing = item as BaseClothing; + + var material = GetMaterial(armor?.Resource ?? clothing?.Resource ?? CraftResource.None); + + if (Material >= BulkMaterialType.DullCopper && Material <= BulkMaterialType.Valorite && + material != Material) { - BeginCombine(from); + from.SendLocalizedMessage(1045168); // The item is not made from the requested ore. } + else if (Material >= BulkMaterialType.Spined && Material <= BulkMaterialType.Barbed && + material != Material) + { + from.SendLocalizedMessage(1049352); // The item is not made from the requested leather type. + } + else + { + bool isExceptional; + + if (item is BaseWeapon weapon) + { + isExceptional = weapon.Quality == WeaponQuality.Exceptional; + } + else if (armor != null) + { + isExceptional = armor.Quality == ArmorQuality.Exceptional; + } + else + { + isExceptional = clothing.Quality == ClothingQuality.Exceptional; + } + + if (RequireExceptional && !isExceptional) + { + from.SendLocalizedMessage(1045167); // The item must be exceptional. + } + else + { + item.Delete(); + ++AmountCur; + + from.SendLocalizedMessage(1045170); // The item has been combined with the deed. + from.SendGump(new SmallBODGump(this)); + + if (m_AmountCur < AmountMax) + { + BeginCombine(from); + } + } + } + } + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + + writer.Write(m_AmountCur); + writer.Write(Type?.FullName); + writer.Write(m_Number); + writer.Write(Graphic); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: + { + m_AmountCur = reader.ReadInt(); + + var type = reader.ReadString(); + + if (type != null) + { + Type = AssemblyHandler.FindTypeByFullName(type); + } + + m_Number = reader.ReadInt(); + Graphic = reader.ReadInt(); + break; + } } } } diff --git a/Projects/UOContent/Engines/Bulk Orders/SmallBODAcceptGump.cs b/Projects/UOContent/Engines/Bulk Orders/SmallBODAcceptGump.cs index 63f3c97e7..4c7bd2ee2 100644 --- a/Projects/UOContent/Engines/Bulk Orders/SmallBODAcceptGump.cs +++ b/Projects/UOContent/Engines/Bulk Orders/SmallBODAcceptGump.cs @@ -68,7 +68,7 @@ namespace Server.Engines.BulkOrders public override void OnResponse(NetState sender, in RelayInfo info) { - var from = sender.Mobile; + Mobile from = sender.Mobile; if (info.ButtonID == 1) // Ok { diff --git a/Projects/UOContent/Engines/Bulk Orders/SmallBODGump.cs b/Projects/UOContent/Engines/Bulk Orders/SmallBODGump.cs index de4c40eea..d7d25aaa3 100644 --- a/Projects/UOContent/Engines/Bulk Orders/SmallBODGump.cs +++ b/Projects/UOContent/Engines/Bulk Orders/SmallBODGump.cs @@ -68,7 +68,7 @@ namespace Server.Engines.BulkOrders public override void OnResponse(NetState sender, in RelayInfo info) { - var from = sender.Mobile; + Mobile from = sender.Mobile; if (_deed.Deleted || !_deed.IsChildOf(from.Backpack)) { diff --git a/Projects/UOContent/Engines/Bulk Orders/SmallSmithBOD.cs b/Projects/UOContent/Engines/Bulk Orders/SmallSmithBOD.cs index caf495db0..d91f2a84e 100644 --- a/Projects/UOContent/Engines/Bulk Orders/SmallSmithBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/SmallSmithBOD.cs @@ -85,7 +85,7 @@ namespace Server.Engines.BulkOrders var theirSkill = m.Skills.Blacksmith.Base; - var amountMax = theirSkill switch + int amountMax = theirSkill switch { >= 70.1 => Utility.RandomList(10, 15, 20, 20), >= 50.1 => Utility.RandomList(10, 15, 15, 20), diff --git a/Projects/UOContent/Engines/Bulk Orders/SmallTailorBOD.cs b/Projects/UOContent/Engines/Bulk Orders/SmallTailorBOD.cs index 4e4fc6427..9a0c6ed13 100644 --- a/Projects/UOContent/Engines/Bulk Orders/SmallTailorBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/SmallTailorBOD.cs @@ -83,7 +83,7 @@ namespace Server.Engines.BulkOrders if (entries.Length > 0) { - var amountMax = theirSkill switch + int amountMax = theirSkill switch { >= 70.1 => Utility.RandomList(10, 15, 20, 20), >= 50.1 => Utility.RandomList(10, 15, 15, 20), diff --git a/Projects/UOContent/Engines/CannedEvil/CannedEvilTimer.cs b/Projects/UOContent/Engines/CannedEvil/CannedEvilTimer.cs index 7f5fed0c8..d7ca9e1b7 100644 --- a/Projects/UOContent/Engines/CannedEvil/CannedEvilTimer.cs +++ b/Projects/UOContent/Engines/CannedEvil/CannedEvilTimer.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CannedEvilTimer.cs * * * @@ -15,7 +15,6 @@ using System; using System.Collections.Generic; -using Server.Collections; using Server.Misc; namespace Server.Engines.CannedEvil @@ -30,8 +29,8 @@ namespace Server.Engines.CannedEvil Instance.OnTick(); } - private static readonly HashSet _dungeonSpawns = new(); - private static readonly HashSet _lostLandsSpawns = new(); + private static readonly HashSet _dungeonSpawns = new(); + private static readonly HashSet _lostLandsSpawns = new(); private static DateTime _sliceTime; public static CannedEvilTimer Instance { get; private set; } @@ -39,25 +38,25 @@ namespace Server.Engines.CannedEvil public static void AddSpawn(DungeonChampionSpawn spawn) { _dungeonSpawns.Add(spawn); - OnSlice(_dungeonSpawns, false); + Instance?.OnSlice(_dungeonSpawns, false); } public static void AddSpawn(LLChampionSpawn spawn) { _lostLandsSpawns.Add(spawn); - OnSlice(_lostLandsSpawns, false); + Instance?.OnSlice(_lostLandsSpawns, false); } public static void RemoveSpawn(DungeonChampionSpawn spawn) { _dungeonSpawns.Remove(spawn); - OnSlice(_dungeonSpawns, false); + Instance?.OnSlice(_dungeonSpawns, false); } public static void RemoveSpawn(LLChampionSpawn spawn) { _lostLandsSpawns.Remove(spawn); - OnSlice(_lostLandsSpawns, false); + Instance?.OnSlice(_lostLandsSpawns, false); } public CannedEvilTimer() : base(TimeSpan.Zero, TimeSpan.FromMinutes(1.0)) @@ -65,34 +64,32 @@ namespace Server.Engines.CannedEvil _sliceTime = Core.Now; } - public static void OnSlice(HashSet spawns, bool rotate = true) + public void OnSlice(ICollection list, bool rotate = true) where T : ChampionSpawn { - if (spawns.Count <= 0) + if (list.Count > 0) { - return; - } + List valid = new List(); - using var queue = rotate ? PooledRefQueue.Create() : default; - - foreach (var spawn in spawns) - { - if (spawn.AlwaysActive && !spawn.Active) + foreach (T spawn in list) { - spawn.ReadyToActivate = true; + if (spawn.AlwaysActive && !spawn.Active) + { + spawn.ReadyToActivate = true; + } + else if (rotate && (!spawn.Active || spawn.Kills == 0 && spawn.Level == 0)) + { + spawn.Active = false; + spawn.ReadyToActivate = false; + + valid.Add(spawn); + } } - else if (rotate && (!spawn.Active || spawn.Kills == 0 && spawn.Level == 0)) + + if (valid.Count > 0) { - spawn.Active = false; - spawn.ReadyToActivate = false; - - queue.Enqueue(spawn); + valid[Utility.Random(valid.Count)].ReadyToActivate = true; } } - - if (rotate && queue.Count > 0) - { - ((ChampionSpawn)queue.PeekRandom()).ReadyToActivate = true; - } } protected override void OnTick() diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionAltar.cs b/Projects/UOContent/Engines/CannedEvil/ChampionAltar.cs index ce9243fe0..0d3376904 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionAltar.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionAltar.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ChampionAltar.cs * * * diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionCommands.cs b/Projects/UOContent/Engines/CannedEvil/ChampionCommands.cs index 802e5d05f..772e03e34 100644 --- a/Projects/UOContent/Engines/CannedEvil/ChampionCommands.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionCommands.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ChampionCommands.cs * * * @@ -46,7 +46,7 @@ namespace Server.Engines.CannedEvil return; } - var spawn = targ switch + ChampionSpawn spawn = targ switch { ChampionSpawn championSpawn => championSpawn, IdolOfTheChampion champion => champion.Spawn, diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionPlatform.cs b/Projects/UOContent/Engines/CannedEvil/ChampionPlatform.cs index d43008860..97c78fea3 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionPlatform.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionPlatform.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ChampionPlatform.cs * * * @@ -61,7 +61,7 @@ public partial class ChampionPlatform : BaseAddon public void AddComponent(int id, int x, int y, int z) { - var ac = new AddonComponent(id) { Hue = 0x497 }; + AddonComponent ac = new AddonComponent(id) { Hue = 0x497 }; AddComponent(ac, x, y, z); } diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSkull.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSkull.cs index 4123c928c..294ff52f9 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSkull.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSkull.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ChampionSkull.cs * * * diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSkullBrazier.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSkullBrazier.cs index 2632fd954..88a11da08 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSkullBrazier.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSkullBrazier.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ChampionSkullBrazier.cs * * * @@ -33,13 +33,17 @@ public partial class ChampionSkullBrazier : AddonComponent [SerializedCommandProperty(AccessLevel.GameMaster)] private ChampionSkullPlatform _platform; - [SerializableField(2, fieldChanged: nameof(OnSkullChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Item _skull; - - private void OnSkullChanged(Item oldValue, Item newValue) + [SerializableProperty(2)] + [CommandProperty(AccessLevel.GameMaster)] + public Item Skull { - _platform?.Validate(); + get => _skull; + set + { + _skull = value; + this.MarkDirty(); + _platform?.Validate(); + } } public override int LabelNumber => 1049489 + (int)_type; diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSkullPlatform.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSkullPlatform.cs index 1b5da7825..1fc815dda 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSkullPlatform.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSkullPlatform.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ChampionSkullPlatform.cs * * * diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSkullType.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSkullType.cs index 4d642ea2d..f8457efcc 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSkullType.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSkullType.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ChampionSkullType.cs * * * diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs index 9daea0ba3..1aca46e31 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ChampionSpawn.cs * * * @@ -27,44 +27,16 @@ using Server.Logging; namespace Server.Engines.CannedEvil; -[SerializationGenerator(11, false)] +[SerializationGenerator(10, false)] public partial class ChampionSpawn : Item { - private void MigrateFrom(V10Content content) - { - _level = content.Level; - _activatedByProximity = content.ActivatedByProximity; - _nextProximityTime = content.NextProximityTime; - _maxLevel = content.MaxLevel; - _activatedByValor = content.ActivatedByValor; - _damageEntries = content.DamageEntries; - _confinedRoaming = content.ConfinedRoaming; - _idol = content.Idol; - _hasBeenAdvanced = content.HasBeenAdvanced; - _spawnArea = content.SpawnArea; - _randomizeType = content.RandomizeType; - _kills = content.Kills; - _active = content.Active; - _type = content.Type; - _creatures = content.Creatures; - _redSkulls = content.RedSkulls; - _whiteSkulls = content.WhiteSkulls; - _platform = content.Platform; - _altar = content.Altar; - _expireDelay = content.ExpireDelay; - _expireTime = content.ExpireTime; - _champion = content.Champion; - _restartDelay = content.RestartDelay; - _restartTime = content.RestartTime; - } - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ChampionSpawn)); [SerializableField(1)] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _activatedByProximity; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(2)] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _nextProximityTime; @@ -124,7 +96,7 @@ public partial class ChampionSpawn : Item [SerializedCommandProperty(AccessLevel.GameMaster)] private TimeSpan _expireDelay; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(20)] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _expireTime; @@ -137,7 +109,7 @@ public partial class ChampionSpawn : Item [SerializedCommandProperty(AccessLevel.GameMaster)] private TimeSpan _restartDelay; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(23, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _restartTime; @@ -212,15 +184,15 @@ public partial class ChampionSpawn : Item get => _level; set { - for (var i = _redSkulls.Count - 1; i >= value; --i) + for (int i = _redSkulls.Count - 1; i >= value; --i) { _redSkulls[i].Delete(); _redSkulls.RemoveAt(i); } - for (var i = _redSkulls.Count; i < Math.Min(value, 16); ++i) + for (int i = _redSkulls.Count; i < Math.Min(value, 16); ++i) { - var skull = new Item(0x1854) { Hue = 0x26, Movable = false, Light = LightType.Circle150 }; + Item skull = new Item(0x1854) { Hue = 0x26, Movable = false, Light = LightType.Circle150 }; skull.MoveToWorld(GetRedSkullLocation(i), Map); _redSkulls.Add(skull); } @@ -231,38 +203,47 @@ public partial class ChampionSpawn : Item } } - [SerializableField(3, allowFieldChange: nameof(AllowMaxLevelChange))] - [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] - private int _maxLevel; - - private bool AllowMaxLevelChange(ref int value) + [SerializableProperty(3)] + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] + public int MaxLevel { - value = Math.Clamp(value, 0, 18); - return true; + get => _maxLevel; + set => _maxLevel = Math.Clamp(value, 0, 18); } - [SerializableField(9, fieldChanged: nameof(OnSpawnAreaChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private Rectangle2D _spawnArea; - - private void OnSpawnAreaChanged(Rectangle2D oldValue, Rectangle2D newValue) + [SerializableProperty(9)] + [CommandProperty(AccessLevel.GameMaster)] + public Rectangle2D SpawnArea { - UpdateRegion(); - } - - [SerializableField(11, fieldChanged: nameof(OnKillsChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _kills; - - private void OnKillsChanged(int oldValue, int newValue) - { - var n = _kills / (double)MaxKills; - var p = (int)(n * 100); - if (p < 90) + get => _spawnArea; + set { - SetWhiteSkullCount(p / 20); + _spawnArea = value; + this.MarkDirty(); + InvalidateProperties(); + UpdateRegion(); + } + } + + [SerializableProperty(11)] + [CommandProperty(AccessLevel.GameMaster)] + public int Kills + { + get => _kills; + set + { + _kills = value; + this.MarkDirty(); + + double n = _kills / (double)MaxKills; + int p = (int)(n * 100); + + if (p < 90) + { + SetWhiteSkullCount(p / 20); + } + + InvalidateProperties(); } } @@ -311,15 +292,15 @@ public partial class ChampionSpawn : Item public void SetWhiteSkullCount(int val) { - for (var i = _whiteSkulls.Count - 1; i >= val; --i) + for (int i = _whiteSkulls.Count - 1; i >= val; --i) { _whiteSkulls[i].Delete(); _whiteSkulls.RemoveAt(i); } - for (var i = _whiteSkulls.Count; i < val; ++i) + for (int i = _whiteSkulls.Count; i < val; ++i) { - var skull = new Item(0x1854) + Item skull = new Item(0x1854) { Movable = false, Light = LightType.Circle150 @@ -473,7 +454,7 @@ public partial class ChampionSpawn : Item // Justice reward var pm = (PlayerMobile)killer; var prot = JusticeVirtue.GetProtector(pm); - if (prot == null || prot.Map != killer.Map || prot.Murderer || prot.Criminal || + if (prot == null || prot.Map != killer.Map || prot.Kills >= 5 || prot.Criminal || !JusticeVirtue.CheckMapRegion(killer, prot)) { return; @@ -550,11 +531,11 @@ public partial class ChampionSpawn : Item return; } - var kills = _kills; + int kills = _kills; for (var i = 0; i < _creatures.Count; ++i) { - var m = _creatures[i]; + Mobile m = _creatures[i]; if (!m.Deleted) { @@ -570,7 +551,7 @@ public partial class ChampionSpawn : Item --i; ++_kills; - var killer = m.FindMostRecentDamager(false); + Mobile killer = m.FindMostRecentDamager(false); RegisterDamageTo(m); @@ -584,13 +565,13 @@ public partial class ChampionSpawn : Item continue; } - var mobSubLevel = GetSubLevelfor(m) + 1; + int mobSubLevel = GetSubLevelfor(m) + 1; if (mobSubLevel >= 0) { - var gainedPath = false; + bool gainedPath = false; - var pointsToGain = mobSubLevel * 40; + int pointsToGain = mobSubLevel * 40; if (VirtueSystem.Award(pm, VirtueName.Valor, pointsToGain, ref gainedPath)) { @@ -650,8 +631,8 @@ public partial class ChampionSpawn : Item InvalidateProperties(); } - var n = _kills / (double)MaxKills; - var p = (int)(n * 100); + double n = _kills / (double)MaxKills; + int p = (int)(n * 100); if (p >= 99) { @@ -742,13 +723,13 @@ public partial class ChampionSpawn : Item bc.Home = bc.Location; bc.HomeMap = bc.Map; - var xWall1 = new Point2D(_spawnArea.X, bc.Y); - var xWall2 = new Point2D(_spawnArea.X + _spawnArea.Width, bc.Y); - var yWall1 = new Point2D(bc.X, _spawnArea.Y); - var yWall2 = new Point2D(bc.X, _spawnArea.Y + _spawnArea.Height); + Point2D xWall1 = new Point2D(_spawnArea.X, bc.Y); + Point2D xWall2 = new Point2D(_spawnArea.X + _spawnArea.Width, bc.Y); + Point2D yWall1 = new Point2D(bc.X, _spawnArea.Y); + Point2D yWall2 = new Point2D(bc.X, _spawnArea.Y + _spawnArea.Height); - var minXDist = Math.Min(bc.GetDistanceToSqrt(xWall1), bc.GetDistanceToSqrt(xWall2)); - var minYDist = Math.Min(bc.GetDistanceToSqrt(yWall1), bc.GetDistanceToSqrt(yWall2)); + double minXDist = Math.Min(bc.GetDistanceToSqrt(xWall1), bc.GetDistanceToSqrt(xWall2)); + double minYDist = Math.Min(bc.GetDistanceToSqrt(yWall1), bc.GetDistanceToSqrt(yWall2)); bc.RangeHome = (int)Math.Min(minXDist, minYDist); } @@ -771,14 +752,14 @@ public partial class ChampionSpawn : Item while (_creatures.Count < MaxSpawn) { - var m = Spawn(); + Mobile m = Spawn(); if (m == null) { return; } - var loc = GetSpawnLocation(); + Point3D loc = GetSpawnLocation(); // Allow creatures to turn into Paragons at Ilshenar champions. m.OnBeforeSpawn(loc, Map); @@ -801,13 +782,13 @@ public partial class ChampionSpawn : Item bc.Home = bc.Location; bc.HomeMap = bc.Map; - var xWall1 = new Point2D(_spawnArea.X, bc.Y); - var xWall2 = new Point2D(_spawnArea.X + _spawnArea.Width, bc.Y); - var yWall1 = new Point2D(bc.X, _spawnArea.Y); - var yWall2 = new Point2D(bc.X, _spawnArea.Y + _spawnArea.Height); + Point2D xWall1 = new Point2D(_spawnArea.X, bc.Y); + Point2D xWall2 = new Point2D(_spawnArea.X + _spawnArea.Width, bc.Y); + Point2D yWall1 = new Point2D(bc.X, _spawnArea.Y); + Point2D yWall2 = new Point2D(bc.X, _spawnArea.Y + _spawnArea.Height); - var minXDist = Math.Min(bc.GetDistanceToSqrt(xWall1), bc.GetDistanceToSqrt(xWall2)); - var minYDist = Math.Min(bc.GetDistanceToSqrt(yWall1), bc.GetDistanceToSqrt(yWall2)); + double minXDist = Math.Min(bc.GetDistanceToSqrt(xWall1), bc.GetDistanceToSqrt(xWall2)); + double minYDist = Math.Min(bc.GetDistanceToSqrt(yWall1), bc.GetDistanceToSqrt(yWall2)); bc.RangeHome = (int)Math.Min(minXDist, minYDist); } @@ -817,7 +798,7 @@ public partial class ChampionSpawn : Item public Point3D GetSpawnLocation() { - var map = Map; + Map map = Map; if (map == null) { @@ -825,12 +806,12 @@ public partial class ChampionSpawn : Item } // Try 20 times to find a spawnable location. - for (var i = 0; i < 20; i++) + for (int i = 0; i < 20; i++) { - var x = Utility.Random(_spawnArea.X, _spawnArea.Width); - var y = Utility.Random(_spawnArea.Y, _spawnArea.Height); + int x = Utility.Random(_spawnArea.X, _spawnArea.Width); + int y = Utility.Random(_spawnArea.Y, _spawnArea.Height); - var z = Map.GetAverageZ(x, y); + int z = Map.GetAverageZ(x, y); if (Map.CanSpawnMobile(new Point2D(x, y), z)) { @@ -847,21 +828,21 @@ public partial class ChampionSpawn : Item public int GetSubLevel() { - var level = Level; + int level = Level; return level <= Level1 ? 0 : level <= Level2 ? 1 : level <= Level3 ? 2 : 3; } public int GetSubLevelfor (Mobile m) { - var types = ChampionSpawnInfo.GetInfo(_type).SpawnTypes; - var t = m.GetType(); + Type[][] types = ChampionSpawnInfo.GetInfo(_type).SpawnTypes; + Type t = m.GetType(); - for (var i = 0; i < types.GetLength(0); i++) + for (int i = 0; i < types.GetLength(0); i++) { - var individualTypes = types[i]; + Type[] individualTypes = types[i]; - for (var j = 0; j < individualTypes.Length; j++) + for (int j = 0; j < individualTypes.Length; j++) { if (t == individualTypes[j]) { @@ -875,9 +856,9 @@ public partial class ChampionSpawn : Item public Mobile Spawn() { - var types = ChampionSpawnInfo.GetInfo(_type).SpawnTypes; + Type[][] types = ChampionSpawnInfo.GetInfo(_type).SpawnTypes; - var v = GetSubLevel(); + int v = GetSubLevel(); if (v >= 0 && v < types.Length) { @@ -1051,7 +1032,7 @@ public partial class ChampionSpawn : Item if (_whiteSkulls != null) { - for (var i = 0; i < _whiteSkulls.Count; ++i) + for (int i = 0; i < _whiteSkulls.Count; ++i) { _whiteSkulls[i].Location = GetWhiteSkullLocation(i); } @@ -1087,7 +1068,7 @@ public partial class ChampionSpawn : Item if (_redSkulls != null) { - for (var i = 0; i < _redSkulls.Count; ++i) + for (int i = 0; i < _redSkulls.Count; ++i) { _redSkulls[i].Map = Map; } @@ -1095,7 +1076,7 @@ public partial class ChampionSpawn : Item if (_whiteSkulls != null) { - for (var i = 0; i < _whiteSkulls.Count; ++i) + for (int i = 0; i < _whiteSkulls.Count; ++i) { _whiteSkulls[i].Map = Map; } @@ -1116,7 +1097,7 @@ public partial class ChampionSpawn : Item if (_redSkulls != null) { - for (var i = 0; i < _redSkulls.Count; ++i) + for (int i = 0; i < _redSkulls.Count; ++i) { _redSkulls[i].Delete(); } @@ -1126,7 +1107,7 @@ public partial class ChampionSpawn : Item if (_whiteSkulls != null) { - for (var i = 0; i < _whiteSkulls.Count; ++i) + for (int i = 0; i < _whiteSkulls.Count; ++i) { _whiteSkulls[i].Delete(); } @@ -1158,9 +1139,9 @@ public partial class ChampionSpawn : Item { if (_creatures != null) { - for (var i = 0; i < _creatures.Count; ++i) + for (int i = 0; i < _creatures.Count; ++i) { - var mob = _creatures[i]; + Mobile mob = _creatures[i]; if (!mob.Player) { @@ -1179,15 +1160,15 @@ public partial class ChampionSpawn : Item return; } - foreach (var de in m.DamageEntries) + foreach (DamageEntry de in m.DamageEntries) { if (de.HasExpired) { continue; } - var damager = de.Damager; - var master = damager.GetDamageMaster(m); + Mobile damager = de.Damager; + Mobile master = damager.GetDamageMaster(m); if (master != null) { @@ -1224,9 +1205,9 @@ public partial class ChampionSpawn : Item return; } - var totalDamage = 0; + int totalDamage = 0; - var validEntries = new Dictionary(); + Dictionary validEntries = new Dictionary(); foreach (var (key, value) in DamageEntries) { @@ -1237,13 +1218,13 @@ public partial class ChampionSpawn : Item } } - var artifactGiven = false; + bool artifactGiven = false; do { - var randomDamage = Utility.RandomMinMax(1, totalDamage); + int randomDamage = Utility.RandomMinMax(1, totalDamage); - var checkDamage = 0; + int checkDamage = 0; foreach (var (key, value) in validEntries) { @@ -1279,7 +1260,7 @@ public partial class ChampionSpawn : Item return false; } - var pack = to.Backpack; + Container pack = to.Backpack; if (pack?.TryDropItem(to, artifact, false) != true) { @@ -1304,8 +1285,8 @@ public partial class ChampionSpawn : Item _maxLevel = reader.ReadInt(); _activatedByValor = reader.ReadBool(); - var entries = reader.ReadInt(); - for (var i = 0; i < entries; ++i) + int entries = reader.ReadInt(); + for (int i = 0; i < entries; ++i) { var m = reader.ReadEntity(); var damage = reader.ReadInt(); @@ -1383,8 +1364,6 @@ public class ChampionSpawnRegion : BaseRegion global = Math.Max(global, 1 + Spawn.Level); //This is a guesstimate. TODO: Verify & get exact values // OSI testing: at 2 red skulls, light = 0x3 ; 1 red = 0x3.; 3 = 8; 9 = 0xD 8 = 0xD 12 = 0x12 10 = 0xD } - private static readonly HashSet _addresses = []; - public override void OnEnter(Mobile m) { if (!m.Player || m.AccessLevel != AccessLevel.Player || Spawn.Active) @@ -1392,6 +1371,8 @@ public class ChampionSpawnRegion : BaseRegion return; } + Region parent = Parent ?? this; + if (Spawn.ReadyToActivate) { Spawn.Start(); @@ -1403,29 +1384,27 @@ public class ChampionSpawnRegion : BaseRegion return; } - using var players = (Parent ?? this).GetPlayersPooled(); - + List players = parent.GetPlayers(); + List addresses = new List(); for (var i = 0; i < players.Count; i++) { - var player = players[i]; - if (player.AccessLevel == AccessLevel.Player && player.NetState != null && !((PlayerMobile)player).Young) + if (players[i].AccessLevel == AccessLevel.Player && players[i].NetState != null && + !addresses.Contains(players[i].NetState.Address) && !((PlayerMobile)players[i]).Young) { - _addresses.Add(player.NetState.Address); + addresses.Add(players[i].NetState.Address); } } - if (_addresses.Count >= 15) + if (addresses.Count >= 15) { - for (var i = 0; i < players.Count; i++) + foreach (Mobile player in players) { - players[i].SendMessage(0x20, Spawn.BroadcastMessage); + player.SendMessage(0x20, Spawn.BroadcastMessage); } Spawn.ActivatedByProximity = true; Spawn.BeginRestart(TimeSpan.FromMinutes(5.0)); } - - _addresses.Clear(); } public override bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation) diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionTitleContext.cs b/Projects/UOContent/Engines/CannedEvil/ChampionTitleContext.cs index 644e2b965..bfd00c0bf 100644 --- a/Projects/UOContent/Engines/CannedEvil/ChampionTitleContext.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionTitleContext.cs @@ -51,9 +51,9 @@ public partial class ChampionTitleContext } [SerializableField(1)] - [SaveFlag(nameof(ShouldSerializeAbyss))] private ChampionTitle _abyss; + [SerializableFieldSaveFlag(1)] private bool ShouldSerializeAbyss() => _abyss != null; [CommandProperty(AccessLevel.GameMaster)] @@ -71,9 +71,9 @@ public partial class ChampionTitleContext } [SerializableField(2)] - [SaveFlag(nameof(ShouldSerializeArachnid))] private ChampionTitle _arachnid; + [SerializableFieldSaveFlag(2)] private bool ShouldSerializeArachnid() => _arachnid != null; [CommandProperty(AccessLevel.GameMaster)] @@ -91,9 +91,9 @@ public partial class ChampionTitleContext } [SerializableField(3)] - [SaveFlag(nameof(ShouldSerializeColdBlood))] private ChampionTitle _coldBlood; + [SerializableFieldSaveFlag(3)] private bool ShouldSerializeColdBlood() => _coldBlood != null; [CommandProperty(AccessLevel.GameMaster)] @@ -111,9 +111,9 @@ public partial class ChampionTitleContext } [SerializableField(4)] - [SaveFlag(nameof(ShouldSerializeForestLord))] private ChampionTitle _forestLord; + [SerializableFieldSaveFlag(4)] private bool ShouldSerializeForestLord() => _forestLord != null; [CommandProperty(AccessLevel.GameMaster)] @@ -131,9 +131,9 @@ public partial class ChampionTitleContext } [SerializableField(5)] - [SaveFlag(nameof(ShouldSerializeVerminHorde))] private ChampionTitle _verminHorde; + [SerializableFieldSaveFlag(5)] private bool ShouldSerializeVerminHorde() => _verminHorde != null; [CommandProperty(AccessLevel.GameMaster)] @@ -151,9 +151,9 @@ public partial class ChampionTitleContext } [SerializableField(6)] - [SaveFlag(nameof(ShouldSerializeUnholyTerror))] private ChampionTitle _unholyTerror; + [SerializableFieldSaveFlag(6)] private bool ShouldSerializeUnholyTerror() => _unholyTerror != null; [CommandProperty(AccessLevel.GameMaster)] @@ -171,9 +171,9 @@ public partial class ChampionTitleContext } [SerializableField(7)] - [SaveFlag(nameof(ShouldSerializeSleepingDragon))] private ChampionTitle _sleepingDragon; + [SerializableFieldSaveFlag(7)] private bool ShouldSerializeSleepingDragon() => _sleepingDragon != null; [CommandProperty(AccessLevel.GameMaster)] @@ -191,9 +191,9 @@ public partial class ChampionTitleContext } [SerializableField(8)] - [SaveFlag(nameof(ShouldSerializeCorrupt))] private ChampionTitle _corrupt; + [SerializableFieldSaveFlag(8)] private bool ShouldSerializeCorrupt() => _corrupt != null; [CommandProperty(AccessLevel.GameMaster)] @@ -211,9 +211,9 @@ public partial class ChampionTitleContext } [SerializableField(9)] - [SaveFlag(nameof(ShouldSerializeGlade))] private ChampionTitle _glade; + [SerializableFieldSaveFlag(9)] private bool ShouldSerializeGlade() => _glade != null; [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionTitleSystem.cs b/Projects/UOContent/Engines/CannedEvil/ChampionTitleSystem.cs index 6160c4595..d82bfaf15 100644 --- a/Projects/UOContent/Engines/CannedEvil/ChampionTitleSystem.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionTitleSystem.cs @@ -167,13 +167,20 @@ public class ChampionTitleSystem : GenericPersistence return; } + using var queue = PooledRefQueue.Create(); + foreach (var context in _championTitleContexts.Values) { if (!context.CheckAtrophy()) { - _championTitleContexts.Remove(context.Player); + queue.Enqueue(context.Player); } } + + while (queue.Count > 0) + { + _championTitleContexts.Remove((PlayerMobile)queue.Dequeue()); + } } } } diff --git a/Projects/UOContent/Engines/CannedEvil/DungeonChampionSpawn.cs b/Projects/UOContent/Engines/CannedEvil/DungeonChampionSpawn.cs index 457ca67df..534168848 100644 --- a/Projects/UOContent/Engines/CannedEvil/DungeonChampionSpawn.cs +++ b/Projects/UOContent/Engines/CannedEvil/DungeonChampionSpawn.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: DungeonChampionSpawn.cs * * * @@ -26,6 +26,11 @@ public partial class DungeonChampionSpawn : ChampionSpawn CannedEvilTimer.AddSpawn(this); } + public DungeonChampionSpawn(Serial serial) : base(serial) + { + CannedEvilTimer.AddSpawn(this); + } + public override bool ProximitySpawn => true; public override bool AlwaysActive => false; @@ -34,7 +39,4 @@ public partial class DungeonChampionSpawn : ChampionSpawn base.OnAfterDelete(); CannedEvilTimer.RemoveSpawn(this); } - - [AfterDeserialization] - private void AfterDeserialization() => CannedEvilTimer.AddSpawn(this); } diff --git a/Projects/UOContent/Engines/CannedEvil/GenChampEntry.cs b/Projects/UOContent/Engines/CannedEvil/GenChampEntry.cs index 84e06fae2..148ac59d7 100644 --- a/Projects/UOContent/Engines/CannedEvil/GenChampEntry.cs +++ b/Projects/UOContent/Engines/CannedEvil/GenChampEntry.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GenChampEntry.cs * * * @@ -15,34 +15,35 @@ using System; -namespace Server.Engines.CannedEvil; - -public class ChampionEntry +namespace Server.Engines.CannedEvil { - public readonly bool _randomizeType; - public readonly ChampionSpawnType _type; - public readonly Point3D _signLocation; - public readonly Type _champType; - public readonly Map _map; - public readonly Point3D _ejectLocation; - public readonly Map _ejectMap; - - public ChampionEntry(Type champtype, Point3D signloc, Map map, Point3D ejectloc, Map ejectmap) : - this(champtype, ChampionSpawnType.Abyss, signloc, map, ejectloc, ejectmap, true) + public record ChampionEntry { - } + public readonly bool m_RandomizeType; + public readonly ChampionSpawnType m_Type; + public readonly Point3D m_SignLocation; + public readonly Type m_ChampType; + public readonly Map m_Map; + public readonly Point3D m_EjectLocation; + public readonly Map m_EjectMap; - public ChampionEntry( - Type champtype, ChampionSpawnType type, Point3D signloc, Map map, Point3D ejectloc, Map ejectmap, - bool randomizetype = false - ) - { - _champType = champtype; - _randomizeType = randomizetype; - _type = type; - _signLocation = signloc; - _map = map; - _ejectLocation = ejectloc; - _ejectMap = ejectmap; + public ChampionEntry(Type champtype, Point3D signloc, Map map, Point3D ejectloc, Map ejectmap) : + this(champtype, ChampionSpawnType.Abyss, signloc, map, ejectloc, ejectmap, true) + { + } + + public ChampionEntry( + Type champtype, ChampionSpawnType type, Point3D signloc, Map map, Point3D ejectloc, Map ejectmap, + bool randomizetype = false + ) + { + m_ChampType = champtype; + m_RandomizeType = randomizetype; + m_Type = type; + m_SignLocation = signloc; + m_Map = map; + m_EjectLocation = ejectloc; + m_EjectMap = ejectmap; + } } } diff --git a/Projects/UOContent/Engines/CannedEvil/GenChamps.cs b/Projects/UOContent/Engines/CannedEvil/GenChamps.cs index ea022724a..0a3b526ea 100644 --- a/Projects/UOContent/Engines/CannedEvil/GenChamps.cs +++ b/Projects/UOContent/Engines/CannedEvil/GenChamps.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GenChamps.cs * * * @@ -14,109 +14,108 @@ *************************************************************************/ using System; -using Server.Collections; +using System.Collections.Generic; using Server.Logging; -namespace Server.Engines.CannedEvil; - -public static class ChampionGenerator +namespace Server.Engines.CannedEvil { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ChampionGenerator)); - - public static void Configure() + public static class ChampionGenerator { - CommandSystem.Register("GenChamps", AccessLevel.Developer, ChampGen_OnCommand); - } + private static readonly ILogger logger = LogFactory.GetLogger(typeof(ChampionGenerator)); - private static readonly ChampionEntry[] LLLocations = - [ - new(typeof(LLChampionSpawn), new Point3D(5511, 2360, 42), Map.Felucca, new Point3D(5439, 2323, 26), Map.Felucca), - new(typeof(LLChampionSpawn), new Point3D(6038, 2401, 47), Map.Felucca, new Point3D(5988, 2340, 24), Map.Felucca), - new(typeof(LLChampionSpawn), new Point3D(5549, 2640, 16), Map.Felucca, new Point3D(5645, 2696, -8), Map.Felucca), - new(typeof(LLChampionSpawn), new Point3D(5636, 2916, 37), Map.Felucca, new Point3D(5721, 2949, 28), Map.Felucca), - new(typeof(LLChampionSpawn), new Point3D(6035, 2943, 50), Map.Felucca, new Point3D(6098, 2997, 17), Map.Felucca), - new(typeof(LLChampionSpawn), new Point3D(5265, 3171, 105), Map.Felucca, new Point3D(5314, 3232, 2), Map.Felucca), - new(typeof(LLChampionSpawn), new Point3D(5282, 3368, 50), Map.Felucca, new Point3D(5215, 3318, 3), Map.Felucca), - new(typeof(LLChampionSpawn), new Point3D(5207, 3637, 20), Map.Felucca, new Point3D(5263, 3687, 0), Map.Felucca), - new(typeof(LLChampionSpawn), new Point3D(5954, 3475, 25), Map.Felucca, new Point3D(6013, 3529, 0), Map.Felucca), - new(typeof(LLChampionSpawn), new Point3D(5982, 3882, 20), Map.Felucca, new Point3D(5929, 3820, -1), Map.Felucca), - new(typeof(LLChampionSpawn), new Point3D(5724, 3991, 41), Map.Felucca, new Point3D(5774, 4041, 26), Map.Felucca), - new(typeof(LLChampionSpawn), ChampionSpawnType.ForestLord, new Point3D(5559, 3757, 21), Map.Felucca, new Point3D(5513, 3878, 3), Map.Felucca) - ]; - - private static readonly ChampionEntry[] DungeonLocations = - [ - new(typeof(DungeonChampionSpawn), ChampionSpawnType.UnholyTerror, new Point3D(5179, 709, 20), Map.Felucca, new Point3D(4111, 432, 5), Map.Felucca), - new(typeof(DungeonChampionSpawn), ChampionSpawnType.VerminHorde, new Point3D(5557, 827, 65), Map.Felucca, new Point3D(5580, 632, 30), Map.Felucca), - new(typeof(DungeonChampionSpawn), ChampionSpawnType.ColdBlood, new Point3D(5259, 837, 64), Map.Felucca, new Point3D(1176, 2637, 0), Map.Felucca), - new(typeof(DungeonChampionSpawn), ChampionSpawnType.Abyss, new Point3D(5815, 1352, 5), Map.Felucca, new Point3D(2923, 3406, 8), Map.Felucca), - new(typeof(DungeonChampionSpawn), ChampionSpawnType.Arachnid, new Point3D(5190, 1607, 20), Map.Felucca, new Point3D(5482, 3161, -54), Map.Felucca) - ]; - - [Usage("GenChamps")] - [Description("Generates champions for Felucca Dungeons & Lost Lands.")] - private static void ChampGen_OnCommand(CommandEventArgs e) - { - /* - //We take the assumption that we are spawning managed champions - for (int i = CannedEvilTimer.DungeonSpawns.Count - 1; i >= 0; i--) - CannedEvilTimer.DungeonSpawns[i].Delete(); - - for (int i = CannedEvilTimer.LLSpawns.Count - 1; i >= 0; i--) - CannedEvilTimer.LLSpawns[i].Delete(); - */ - - //We assume that all champion spawns are generated here. - using var spawns = PooledRefQueue.Create(); - foreach (var item in World.Items.Values) + public static void Configure() { - if (item is ChampionSpawn spawn) - { - spawns.Enqueue(spawn); - } + CommandSystem.Register("GenChamps", AccessLevel.Developer, ChampGen_OnCommand); } - while (spawns.Count > 0) + private static readonly ChampionEntry[] LLLocations = { + new(typeof(LLChampionSpawn), new Point3D(5511, 2360, 42), Map.Felucca, new Point3D(5439, 2323, 26 ), Map.Felucca), + new(typeof(LLChampionSpawn), new Point3D(6038, 2401, 47), Map.Felucca, new Point3D(5988, 2340, 24), Map.Felucca), + new(typeof(LLChampionSpawn), new Point3D(5549, 2640, 16), Map.Felucca, new Point3D(5645, 2696, -8), Map.Felucca), + new(typeof(LLChampionSpawn), new Point3D(5636, 2916, 37), Map.Felucca, new Point3D(5721, 2949, 28), Map.Felucca), + new(typeof(LLChampionSpawn), new Point3D(6035, 2943, 50), Map.Felucca, new Point3D(6098, 2997, 17), Map.Felucca), + new(typeof(LLChampionSpawn), new Point3D(5265, 3171, 105), Map.Felucca, new Point3D(5314, 3232, 2), Map.Felucca), + new(typeof(LLChampionSpawn), new Point3D(5282, 3368, 50), Map.Felucca, new Point3D(5215, 3318, 3), Map.Felucca), + new(typeof(LLChampionSpawn), new Point3D(5207, 3637, 20), Map.Felucca, new Point3D(5263, 3687, 0), Map.Felucca), + new(typeof(LLChampionSpawn), new Point3D(5954, 3475, 25), Map.Felucca, new Point3D(6013, 3529, 0), Map.Felucca), + new(typeof(LLChampionSpawn), new Point3D(5982, 3882, 20), Map.Felucca, new Point3D(5929, 3820, -1), Map.Felucca), + new(typeof(LLChampionSpawn), new Point3D(5724, 3991, 41), Map.Felucca, new Point3D(5774, 4041, 26), Map.Felucca), + new(typeof(LLChampionSpawn), ChampionSpawnType.ForestLord, new Point3D(5559, 3757, 21), Map.Felucca, new Point3D(5513, 3878, 3), Map.Felucca), + }; + + private static readonly ChampionEntry[] DungeonLocations = { + new(typeof(DungeonChampionSpawn), ChampionSpawnType.UnholyTerror, new Point3D(5179, 709, 20), Map.Felucca, new Point3D(4111, 432, 5), Map.Felucca), + new(typeof(DungeonChampionSpawn), ChampionSpawnType.VerminHorde, new Point3D(5557, 827, 65), Map.Felucca, new Point3D(5580, 632, 30), Map.Felucca), + new(typeof(DungeonChampionSpawn), ChampionSpawnType.ColdBlood, new Point3D(5259, 837, 64), Map.Felucca, new Point3D(1176, 2637, 0), Map.Felucca), + new(typeof(DungeonChampionSpawn), ChampionSpawnType.Abyss, new Point3D(5815, 1352, 5), Map.Felucca, new Point3D(2923, 3406, 8), Map.Felucca), + new(typeof(DungeonChampionSpawn), ChampionSpawnType.Arachnid, new Point3D(5190, 1607, 20), Map.Felucca, new Point3D(5482, 3161, -54), Map.Felucca), + }; + + [Usage("GenChamps")] + [Description("Generates champions for Felucca Dungeons & Lost Lands.")] + private static void ChampGen_OnCommand(CommandEventArgs e) { - spawns.Dequeue().Delete(); - } + /* + //We take the assumption that we are spawning managed champions + for (int i = CannedEvilTimer.DungeonSpawns.Count - 1; i >= 0; i--) + CannedEvilTimer.DungeonSpawns[i].Delete(); - Process(DungeonLocations); - Process(LLLocations); - //ProcessIlshenar(); - //ProcessTokuno(); - } + for (int i = CannedEvilTimer.LLSpawns.Count - 1; i >= 0; i--) + CannedEvilTimer.LLSpawns[i].Delete(); + */ - private static void Process(ChampionEntry[] entries) - { - for (var i = 0; i < entries.Length; i++) - { - var entry = entries[i]; - - try + //We assume that all champion spawns are generated here. + List spawns = new List(); + foreach (Item item in World.Items.Values) { - if (Activator.CreateInstance(entry._champType) is ChampionSpawn spawn) + if (item is ChampionSpawn spawn) { - spawn.RandomizeType = entry._randomizeType; - spawn.Type = entry._type; - spawn.MoveToWorld(entry._signLocation, entry._map); - spawn.EjectLocation = entry._ejectLocation; - spawn.EjectMap = entry._ejectMap; - if (spawn.AlwaysActive) - { - spawn.ReadyToActivate = true; - } + spawns.Add(spawn); } } - catch (Exception e) + + for (int i = spawns.Count - 1; i >= 0; i--) { - logger.Error( - e, - "Failed to generate champion \"{Type}\" at {Location} ({Map}).", - entry._champType.FullName, - entry._signLocation, - entry._map - ); + spawns[i].Delete(); + } + + Process(DungeonLocations); + Process(LLLocations); + //ProcessIlshenar(); + //ProcessTokuno(); + } + + private static void Process(ChampionEntry[] entries) + { + for (int i = 0; i < entries.Length; i++) + { + ChampionEntry entry = entries[i]; + + try + { + if (Activator.CreateInstance(entry.m_ChampType) is ChampionSpawn spawn) + { + spawn.RandomizeType = entry.m_RandomizeType; + spawn.Type = entry.m_Type; + spawn.MoveToWorld(entry.m_SignLocation, entry.m_Map); + spawn.EjectLocation = entry.m_EjectLocation; + spawn.EjectMap = entry.m_EjectMap; + if (spawn.AlwaysActive) + { + spawn.ReadyToActivate = true; + } + } + } + catch (Exception e) + { + logger.Error( + e, + "Failed to generate champion \"{Type}\" at {Location} ({Map}).", + entry.m_ChampType.FullName, + entry.m_SignLocation, + entry.m_Map + ); + } } } } diff --git a/Projects/UOContent/Engines/CannedEvil/LLChampionSpawn.cs b/Projects/UOContent/Engines/CannedEvil/LLChampionSpawn.cs index 38abe90aa..17768f81e 100644 --- a/Projects/UOContent/Engines/CannedEvil/LLChampionSpawn.cs +++ b/Projects/UOContent/Engines/CannedEvil/LLChampionSpawn.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: LLChampionSpawn.cs * * * @@ -28,6 +28,11 @@ public partial class LLChampionSpawn : ChampionSpawn CannedEvilTimer.AddSpawn(this); } + public LLChampionSpawn(Serial serial) : base(serial) + { + CannedEvilTimer.AddSpawn(this); + } + public override bool AlwaysActive => false; public override void OnAfterDelete() @@ -35,7 +40,4 @@ public partial class LLChampionSpawn : ChampionSpawn base.OnAfterDelete(); CannedEvilTimer.RemoveSpawn(this); } - - [AfterDeserialization] - private void AfterDeserialization() => CannedEvilTimer.AddSpawn(this); } diff --git a/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs b/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs index 88acd587e..a5426e7a5 100644 --- a/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs +++ b/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs @@ -3,21 +3,15 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(2, false)] +[SerializationGenerator(1, false)] public partial class StarRoomGate : Moongate { - private void MigrateFrom(V1Content content) - { - _decays = content.Decays; - _decayTime = content.DecayTime; - } - private static TimeSpan GateDuration = TimeSpan.FromMinutes(2.0); [SerializableField(0)] private bool _decays; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(1)] private DateTime _decayTime; diff --git a/Projects/UOContent/Engines/Character Creation/CharacterCreation.cs b/Projects/UOContent/Engines/Character Creation/CharacterCreation.cs index 7189e38e3..b743b9da7 100644 --- a/Projects/UOContent/Engines/Character Creation/CharacterCreation.cs +++ b/Projects/UOContent/Engines/Character Creation/CharacterCreation.cs @@ -348,7 +348,7 @@ public static partial class CharacterCreation } case "samurai": { - var haotisAndTokunoAccessible = + bool haotisAndTokunoAccessible = (flags & ClientFlags.Tokuno) == ClientFlags.Tokuno && (flags & ClientFlags.Malas) == ClientFlags.Malas && availableMaps.Includes(MapSelectionFlags.Malas | MapSelectionFlags.Tokuno); @@ -370,7 +370,7 @@ public static partial class CharacterCreation } case "ninja": { - var enimosAndTokunoAccessible = + bool enimosAndTokunoAccessible = (flags & ClientFlags.Tokuno) == ClientFlags.Tokuno && (flags & ClientFlags.Malas) == ClientFlags.Malas && availableMaps.Includes(MapSelectionFlags.Malas | MapSelectionFlags.Tokuno); @@ -418,7 +418,7 @@ public static partial class CharacterCreation { name = name.Trim(); - if (!NameVerification.ValidatePlayerName(name)) + if (!NameVerification.Validate(name, 2, 16, true, false, true, 1, NameVerification.SpaceDashPeriodQuote)) { name = "Generic Player"; } @@ -552,7 +552,7 @@ public static partial class CharacterCreation } // animate dead, evil omen, pain spike, summon familiar, wraith form - m.PackItem(new NecromancerSpellbook(0x8981ul)); + m.PackItem(new NecromancerSpellbook(0x8981ul) { LootType = LootType.Blessed }); return; } case "paladin": @@ -590,7 +590,7 @@ public static partial class CharacterCreation EquipItem(m, new BodySash(0xCF)); } - m.PackItem(new BookOfChivalry()); + m.PackItem(new BookOfChivalry { LootType = LootType.Blessed }); return; } case "samurai": @@ -721,7 +721,7 @@ public static partial class CharacterCreation return; } - if (!Core.AOS && item.LootType == LootType.Regular) + if (!Core.AOS) { item.LootType = LootType.Newbied; } @@ -745,7 +745,7 @@ public static partial class CharacterCreation private static void PackItem(this Mobile m, Item item) { - if (!Core.AOS && item.LootType == LootType.Regular) + if (!Core.AOS) { item.LootType = LootType.Newbied; } @@ -993,7 +993,7 @@ public static partial class CharacterCreation } case SkillName.ArmsLore: { - var item = Utility.Random(3) switch + Item item = Utility.Random(3) switch { 0 => FencingWeapon(raceFlag), 1 => MacingWeapon(raceFlag), @@ -1192,7 +1192,7 @@ public static partial class CharacterCreation m.PackScroll(1); m.PackScroll(2); - EquipItem(m, new Spellbook(0x382A8C38ul)); + EquipItem(m, new Spellbook(0x382A8C38ul) { LootType = LootType.Blessed }); EquipItem(m, Robe(raceFlag, female, Utility.RandomBlueHue())); if (elf) diff --git a/Projects/UOContent/Engines/Chat/Channel.cs b/Projects/UOContent/Engines/Chat/Channel.cs index 69f33ddcf..09b8f6cb3 100644 --- a/Projects/UOContent/Engines/Chat/Channel.cs +++ b/Projects/UOContent/Engines/Chat/Channel.cs @@ -146,8 +146,15 @@ namespace Server.Engines.Chat m_Users.Remove(user); user.CurrentChannel = null; - m_Moderators.Remove(user); - m_Voices.Remove(user); + if (m_Moderators.Contains(user)) + { + m_Moderators.Remove(user); + } + + if (m_Voices.Contains(user)) + { + m_Voices.Remove(user); + } SendCommand(ChatCommand.RemoveUserFromChannel, user, user.Username); ChatSystem.SendCommandTo(user.Mobile, ChatCommand.LeaveChannel); @@ -176,7 +183,10 @@ namespace Server.Engines.Chat public void RemoveBan(ChatUser user) { - m_Banned.Remove(user); + if (m_Banned.Contains(user)) + { + m_Banned.Remove(user); + } } public void Kick(ChatUser user, ChatUser moderator = null) diff --git a/Projects/UOContent/Engines/Chat/ChatPackets.cs b/Projects/UOContent/Engines/Chat/ChatPackets.cs index b0fce023f..56583cfcd 100644 --- a/Projects/UOContent/Engines/Chat/ChatPackets.cs +++ b/Projects/UOContent/Engines/Chat/ChatPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ChatPackets.cs * * * diff --git a/Projects/UOContent/Engines/ConPVP/AcceptDuelGump.cs b/Projects/UOContent/Engines/ConPVP/AcceptDuelGump.cs index 9359f1084..00a2bf29e 100644 --- a/Projects/UOContent/Engines/ConPVP/AcceptDuelGump.cs +++ b/Projects/UOContent/Engines/ConPVP/AcceptDuelGump.cs @@ -4,319 +4,314 @@ using Server.Gumps; using Server.Mobiles; using Server.Network; -namespace Server.Engines.ConPVP; - -public class AcceptDuelGump : DynamicGump +namespace Server.Engines.ConPVP { - private const int LabelColor32 = 0xFFFFFF; - private const int BlackColor32 = 0x000008; - - private static readonly Dictionary> _ignoreLists = new(); - - private readonly Mobile _challenged; - private readonly Mobile _challenger; - private readonly DuelContext _context; - private readonly Participant _participant; - private readonly int _slot; - - private bool _active = true; - - public override bool Singleton => true; - - private AcceptDuelGump(Mobile challenger, Mobile challenged, DuelContext context, Participant p, int slot) - : base(50, 50) + public class AcceptDuelGump : Gump { - _challenger = challenger; - _challenged = challenged; - _context = context; - _participant = p; - _slot = slot; + private const int LabelColor32 = 0xFFFFFF; + private const int BlackColor32 = 0x000008; - Timer.StartTimer(TimeSpan.FromSeconds(15.0), AutoReject); - } + private static readonly Dictionary> m_IgnoreLists = + new(); - public static void DisplayTo(Mobile challenger, Mobile challenged, DuelContext context, Participant p, int slot) - { - if (challenged?.NetState == null || challenger == null || context == null || p == null) + private readonly Mobile m_Challenged; + private readonly Mobile m_Challenger; + private readonly DuelContext m_Context; + private readonly Participant m_Participant; + private readonly int m_Slot; + + private bool m_Active = true; + + public override bool Singleton => true; + + public AcceptDuelGump(Mobile challenger, Mobile challenged, DuelContext context, Participant p, int slot) : base( + 50, + 50 + ) { - return; + m_Challenger = challenger; + m_Challenged = challenged; + m_Context = context; + m_Participant = p; + m_Slot = slot; + + Closable = false; + + AddPage(0); + + // AddBackground( 0, 0, 400, 220, 9150 ); + AddBackground(1, 1, 398, 218, 3600); + // AddBackground( 16, 15, 369, 189, 9100 ); + + AddImageTiled(16, 15, 369, 189, 3604); + AddAlphaRegion(16, 15, 369, 189); + + AddImage(215, -43, 0xEE40); + // AddImage( 330, 141, 0x8BA ); + + var duelChallengeBlack = "Duel Challenge".Center(BlackColor32); + AddHtml(22 - 1, 22, 294, 20, duelChallengeBlack); + AddHtml(22 + 1, 22, 294, 20, duelChallengeBlack); + AddHtml(22, 22 - 1, 294, 20, duelChallengeBlack); + AddHtml(22, 22 + 1, 294, 20, duelChallengeBlack); + AddHtml(22, 22, 294, 20, "Duel Challenge".Center(LabelColor32)); + + var accept = p.Contains(challenger) + ? $"You have been asked to join sides with {challenger.Name} in a duel. Do you accept?" + : $"You have been challenged to a duel from {challenger.Name}. Do you accept?"; + + var acceptBlack = accept.Color(BlackColor32); + + AddHtml(22 - 1, 50, 294, 40, acceptBlack); + AddHtml(22 + 1, 50, 294, 40, acceptBlack); + AddHtml(22, 50 - 1, 294, 40, acceptBlack); + AddHtml(22, 50 + 1, 294, 40, acceptBlack); + AddHtml(22, 50, 294, 40, accept.Color(0xB0C868)); + + AddImageTiled(32, 88, 264, 1, 9107); + AddImageTiled(42, 90, 264, 1, 9157); + + var yesBlack = "Yes, I will fight this duel.".Color(BlackColor32); + + AddRadio(24, 100, 9727, 9730, true, 1); + AddHtml(60 - 1, 105, 250, 20, yesBlack); + AddHtml(60 + 1, 105, 250, 20, yesBlack); + AddHtml(60, 105 - 1, 250, 20, yesBlack); + AddHtml(60, 105 + 1, 250, 20, yesBlack); + AddHtml(60, 105, 250, 20, "Yes, I will fight this duel.".Color(LabelColor32)); + + var noBlack = "No, I do not wish to fight.".Color(BlackColor32); + + AddRadio(24, 135, 9727, 9730, false, 2); + AddHtml(60 - 1, 140, 250, 20, noBlack); + AddHtml(60 + 1, 140, 250, 20, noBlack); + AddHtml(60, 140 - 1, 250, 20, noBlack); + AddHtml(60, 140 + 1, 250, 20, noBlack); + AddHtml(60, 140, 250, 20, "No, I do not wish to fight.".Color(LabelColor32)); + + var noAskBlack = "No, knave. Do not ask again.".Color(BlackColor32); + + AddRadio(24, 170, 9727, 9730, false, 3); + AddHtml(60 - 1, 175, 250, 20, noAskBlack); + AddHtml(60 + 1, 175, 250, 20, noAskBlack); + AddHtml(60, 175 - 1, 250, 20, noAskBlack); + AddHtml(60, 175 + 1, 250, 20, noAskBlack); + AddHtml(60, 175, 250, 20, "No, knave. Do not ask again.".Color(LabelColor32)); + + AddButton(314, 173, 247, 248, 1); + + Timer.StartTimer(TimeSpan.FromSeconds(15.0), AutoReject); } - challenged.SendGump(new AcceptDuelGump(challenger, challenged, context, p, slot)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); - - builder.AddPage(); - - builder.AddBackground(1, 1, 398, 218, 3600); - - builder.AddImageTiled(16, 15, 369, 189, 3604); - builder.AddAlphaRegion(16, 15, 369, 189); - - builder.AddImage(215, -43, 0xEE40); - - var duelChallengeBlack = "Duel Challenge".Center(BlackColor32); - builder.AddHtml(22 - 1, 22, 294, 20, duelChallengeBlack); - builder.AddHtml(22 + 1, 22, 294, 20, duelChallengeBlack); - builder.AddHtml(22, 22 - 1, 294, 20, duelChallengeBlack); - builder.AddHtml(22, 22 + 1, 294, 20, duelChallengeBlack); - builder.AddHtml(22, 22, 294, 20, "Duel Challenge".Center(LabelColor32)); - - var accept = _participant.Contains(_challenger) - ? $"You have been asked to join sides with {_challenger.Name} in a duel. Do you accept?" - : $"You have been challenged to a duel from {_challenger.Name}. Do you accept?"; - - var acceptBlack = accept.Color(BlackColor32); - - builder.AddHtml(22 - 1, 50, 294, 40, acceptBlack); - builder.AddHtml(22 + 1, 50, 294, 40, acceptBlack); - builder.AddHtml(22, 50 - 1, 294, 40, acceptBlack); - builder.AddHtml(22, 50 + 1, 294, 40, acceptBlack); - builder.AddHtml(22, 50, 294, 40, accept.Color(0xB0C868)); - - builder.AddImageTiled(32, 88, 264, 1, 9107); - builder.AddImageTiled(42, 90, 264, 1, 9157); - - var yesBlack = "Yes, I will fight this duel.".Color(BlackColor32); - - builder.AddRadio(24, 100, 9727, 9730, true, 1); - builder.AddHtml(60 - 1, 105, 250, 20, yesBlack); - builder.AddHtml(60 + 1, 105, 250, 20, yesBlack); - builder.AddHtml(60, 105 - 1, 250, 20, yesBlack); - builder.AddHtml(60, 105 + 1, 250, 20, yesBlack); - builder.AddHtml(60, 105, 250, 20, "Yes, I will fight this duel.".Color(LabelColor32)); - - var noBlack = "No, I do not wish to fight.".Color(BlackColor32); - - builder.AddRadio(24, 135, 9727, 9730, false, 2); - builder.AddHtml(60 - 1, 140, 250, 20, noBlack); - builder.AddHtml(60 + 1, 140, 250, 20, noBlack); - builder.AddHtml(60, 140 - 1, 250, 20, noBlack); - builder.AddHtml(60, 140 + 1, 250, 20, noBlack); - builder.AddHtml(60, 140, 250, 20, "No, I do not wish to fight.".Color(LabelColor32)); - - var noAskBlack = "No, knave. Do not ask again.".Color(BlackColor32); - - builder.AddRadio(24, 170, 9727, 9730, false, 3); - builder.AddHtml(60 - 1, 175, 250, 20, noAskBlack); - builder.AddHtml(60 + 1, 175, 250, 20, noAskBlack); - builder.AddHtml(60, 175 - 1, 250, 20, noAskBlack); - builder.AddHtml(60, 175 + 1, 250, 20, noAskBlack); - builder.AddHtml(60, 175, 250, 20, "No, knave. Do not ask again.".Color(LabelColor32)); - - builder.AddButton(314, 173, 247, 248, 1); - } - - public void AutoReject() - { - if (!_active) + public void AutoReject() { - return; - } - - _active = false; - - _challenged.CloseGump(); - - _challenger.SendMessage($"{_challenged.Name} seems unresponsive."); - _challenged.SendMessage("You decline the challenge."); - } - - public static void BeginIgnore(Mobile source, Mobile toIgnore) - { - if (!_ignoreLists.TryGetValue(source, out var list)) - { - _ignoreLists[source] = list = new List(); - } - - for (var i = 0; i < list.Count; ++i) - { - var ie = list[i]; - - if (ie.Ignored == toIgnore) + if (!m_Active) { - ie.Refresh(); return; } - if (ie.Expired) - { - list.RemoveAt(i--); - } + m_Active = false; + + m_Challenged.CloseGump(); + + m_Challenger.SendMessage($"{m_Challenged.Name} seems unresponsive."); + m_Challenged.SendMessage("You decline the challenge."); } - list.Add(new IgnoreEntry(toIgnore)); - } - - public static bool IsIgnored(Mobile source, Mobile check) - { - if (!_ignoreLists.TryGetValue(source, out var list)) + public static void BeginIgnore(Mobile source, Mobile toIgnore) { + if (!m_IgnoreLists.TryGetValue(source, out var list)) + { + m_IgnoreLists[source] = list = new List(); + } + + for (var i = 0; i < list.Count; ++i) + { + var ie = list[i]; + + if (ie.Ignored == toIgnore) + { + ie.Refresh(); + return; + } + + if (ie.Expired) + { + list.RemoveAt(i--); + } + } + + list.Add(new IgnoreEntry(toIgnore)); + } + + public static bool IsIgnored(Mobile source, Mobile check) + { + if (!m_IgnoreLists.TryGetValue(source, out var list)) + { + return false; + } + + for (var i = 0; i < list.Count; ++i) + { + var ie = list[i]; + + if (ie.Expired) + { + list.RemoveAt(i--); + } + else if (ie.Ignored == check) + { + return true; + } + } + return false; } - for (var i = 0; i < list.Count; ++i) + public override void OnResponse(NetState sender, in RelayInfo info) { - var ie = list[i]; - - if (ie.Expired) - { - list.RemoveAt(i--); - } - else if (ie.Ignored == check) - { - return true; - } - } - - return false; - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID != 1 || !_active || !_context.Registered) - { - return; - } - - _active = false; - - if (!_context.Participants.Contains(_participant)) - { - return; - } - - if (info.IsSwitched(1)) - { - if (_challenged is not PlayerMobile pm) + if (info.ButtonID != 1 || !m_Active || !m_Context.Registered) { return; } - if (pm.DuelContext != null) - { - if (pm.DuelContext.Initiator == pm) - { - pm.SendMessage(0x22, "You have already started a duel."); - } - else - { - pm.SendMessage(0x22, "You have already been challenged in a duel."); - } + m_Active = false; - _challenger.SendMessage($"{pm.Name} cannot fight because they are already assigned to another duel."); + if (!m_Context.Participants.Contains(m_Participant)) + { + return; } - else if (DuelContext.CheckCombat(pm)) - { - pm.SendMessage( - 0x22, - "You have recently been in combat with another player and must wait before starting a duel." - ); - _challenger.SendMessage( - $"{pm.Name} cannot fight because they have recently been in combat with another player." - ); - } - else if (TournamentController.IsActive) - { - pm.SendMessage(0x22, "A tournament is currently active and you may not duel."); - _challenger.SendMessage(0x22, "A tournament is currently active and you may not duel."); - } - else - { - var added = false; - if (_slot >= 0 && _slot < _participant.Players.Length && _participant.Players[_slot] == null) + if (info.IsSwitched(1)) + { + if (m_Challenged is not PlayerMobile pm) { - added = true; - _participant.Players[_slot] = new DuelPlayer(_challenged, _participant); - } - else - { - for (var i = 0; i < _participant.Players.Length; ++i) - { - if (_participant.Players[i] == null) - { - added = true; - _participant.Players[i] = new DuelPlayer(_challenged, _participant); - break; - } - } + return; } - if (added) + if (pm.DuelContext != null) { - _challenger.SendMessage($"{_challenged.Name} has accepted the request."); - _challenged.SendMessage($"You have accepted the request from {_challenger.Name}."); - - foreach (var g in _challenger.GetGumps()) + if (pm.DuelContext.Initiator == pm) { - if (g is ParticipantGump pg && pg.Participant == _participant) - { - ParticipantGump.DisplayTo(_challenger, _context, _participant); - break; - } - - if (g is DuelContextGump dcg && dcg.Context == _context) - { - DuelContextGump.DisplayTo(_challenger, _context); - break; - } - } - } - else - { - _challenger.SendMessage( - $"The participant list was full and so {_challenged.Name} could not join." - ); - - if (_participant.Contains(_challenger)) - { - _challenged.SendMessage($"The participant list was full and so you could not join the fight with {_challenger.Name}."); + pm.SendMessage(0x22, "You have already started a duel."); } else { - _challenged.SendMessage($"The participant list was full and so you could not join the fight against {_challenger.Name}."); + pm.SendMessage(0x22, "You have already been challenged in a duel."); + } + + m_Challenger.SendMessage($"{pm.Name} cannot fight because they are already assigned to another duel."); + } + else if (DuelContext.CheckCombat(pm)) + { + pm.SendMessage( + 0x22, + "You have recently been in combat with another player and must wait before starting a duel." + ); + m_Challenger.SendMessage( + $"{pm.Name} cannot fight because they have recently been in combat with another player." + ); + } + else if (TournamentController.IsActive) + { + pm.SendMessage(0x22, "A tournament is currently active and you may not duel."); + m_Challenger.SendMessage(0x22, "A tournament is currently active and you may not duel."); + } + else + { + var added = false; + + if (m_Slot >= 0 && m_Slot < m_Participant.Players.Length && m_Participant.Players[m_Slot] == null) + { + added = true; + m_Participant.Players[m_Slot] = new DuelPlayer(m_Challenged, m_Participant); + } + else + { + for (var i = 0; i < m_Participant.Players.Length; ++i) + { + if (m_Participant.Players[i] == null) + { + added = true; + m_Participant.Players[i] = new DuelPlayer(m_Challenged, m_Participant); + break; + } + } + } + + if (added) + { + m_Challenger.SendMessage($"{m_Challenged.Name} has accepted the request."); + m_Challenged.SendMessage($"You have accepted the request from {m_Challenger.Name}."); + + foreach (var g in m_Challenger.GetGumps()) + { + if (g is ParticipantGump pg && pg.Participant == m_Participant) + { + m_Challenger.SendGump(new ParticipantGump(m_Challenger, m_Context, m_Participant)); + break; + } + + if (g is DuelContextGump dcg && dcg.Context == m_Context) + { + m_Challenger.SendGump(new DuelContextGump(m_Challenger, m_Context)); + break; + } + } + } + else + { + m_Challenger.SendMessage( + $"The participant list was full and so {m_Challenged.Name} could not join." + ); + + if (m_Participant.Contains(m_Challenger)) + { + m_Challenged.SendMessage($"The participant list was full and so you could not join the fight with {m_Challenger.Name}."); + } + else + { + m_Challenged.SendMessage($"The participant list was full and so you could not join the fight against {m_Challenger.Name}."); + } } } } - } - else - { - if (info.IsSwitched(3)) - { - BeginIgnore(_challenged, _challenger); - } - - _challenger.SendMessage($"{_challenged.Name} does not wish to fight."); - - if (_participant.Contains(_challenger)) - { - _challenged.SendMessage($"You chose not to fight with {_challenger.Name}."); - } else { - _challenged.SendMessage($"You chose not to fight against {_challenger.Name}."); + if (info.IsSwitched(3)) + { + BeginIgnore(m_Challenged, m_Challenger); + } + + m_Challenger.SendMessage($"{m_Challenged.Name} does not wish to fight."); + + if (m_Participant.Contains(m_Challenger)) + { + m_Challenged.SendMessage($"You chose not to fight with {m_Challenger.Name}."); + } + else + { + m_Challenged.SendMessage($"You chose not to fight against {m_Challenger.Name}."); + } } } - } - private class IgnoreEntry - { - private static readonly TimeSpan ExpireDelay = TimeSpan.FromMinutes(15.0); - private DateTime _expire; - - public IgnoreEntry(Mobile ignored) + private class IgnoreEntry { - Ignored = ignored; - Refresh(); + private static readonly TimeSpan ExpireDelay = TimeSpan.FromMinutes(15.0); + public readonly Mobile m_Ignored; + public DateTime m_Expire; + + public IgnoreEntry(Mobile ignored) + { + m_Ignored = ignored; + Refresh(); + } + + public Mobile Ignored => m_Ignored; + public bool Expired => Core.Now >= m_Expire; + + public void Refresh() => m_Expire = Core.Now + ExpireDelay; } - - public Mobile Ignored { get; } - public bool Expired => Core.Now >= _expire; - - public void Refresh() => _expire = Core.Now + ExpireDelay; } } diff --git a/Projects/UOContent/Engines/ConPVP/DuelContext.cs b/Projects/UOContent/Engines/ConPVP/DuelContext.cs index 2a62259f8..15fd66b07 100644 --- a/Projects/UOContent/Engines/ConPVP/DuelContext.cs +++ b/Projects/UOContent/Engines/ConPVP/DuelContext.cs @@ -2,7 +2,6 @@ using System; using System.Collections; using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; -using ModernUO.Serialization; using Server.Engines.PartySystem; using Server.Factions; using Server.Gumps; @@ -12,7 +11,6 @@ using Server.Regions; using Server.Spells; using Server.Spells.Bushido; using Server.Spells.Chivalry; -using Server.Spells.First; using Server.Spells.Fourth; using Server.Spells.Necromancy; using Server.Spells.Ninjitsu; @@ -21,2361 +19,2245 @@ using Server.Spells.Seventh; using Server.Spells.Spellweaving; using Server.Targeting; -namespace Server.Engines.ConPVP; - -public delegate void CountdownCallback(int count); - -public partial class DuelContext +namespace Server.Engines.ConPVP { - private static readonly TimeSpan CombatDelay = TimeSpan.FromSeconds(30.0); - private static readonly TimeSpan AutoTieDelay = TimeSpan.FromMinutes(15.0); + public delegate void CountdownCallback(int count); - private readonly List m_Walls = new(); - - private TimerExecutionToken _autoTieTimerToken; - private TimerExecutionToken _countdownTimerToken; - private TimerExecutionToken _SdWarnTimerToken; - private TimerExecutionToken _SdActivateTimerToken; - - public EventGame m_EventGame; - private Map m_GateFacet; - - private Point3D m_GatePoint; - public TourneyMatch m_Match; - - public Arena m_OverrideArena; - - public Tournament m_Tournament; - - private bool m_Yielding; - - public DuelContext(Mobile initiator, RulesetLayout layout, bool addNew = true) + public class DuelContext { - Initiator = initiator; - Participants = new List(); - Ruleset = new Ruleset(layout); - Ruleset.ApplyDefault(layout.Defaults[0]); + private static readonly TimeSpan CombatDelay = TimeSpan.FromSeconds(30.0); + private static readonly TimeSpan AutoTieDelay = TimeSpan.FromMinutes(15.0); - if (addNew) + private readonly List m_Walls = new(); + + private TimerExecutionToken _autoTieTimerToken; + private TimerExecutionToken _countdownTimerToken; + private TimerExecutionToken _SdWarnTimerToken; + private TimerExecutionToken _SdActivateTimerToken; + + public EventGame m_EventGame; + private Map m_GateFacet; + + private Point3D m_GatePoint; + public TourneyMatch m_Match; + + public Arena m_OverrideArena; + + public Tournament m_Tournament; + + private bool m_Yielding; + + public DuelContext(Mobile initiator, RulesetLayout layout, bool addNew = true) { - Participants.Add(new Participant(this, 1)); - Participants.Add(new Participant(this, 1)); - Participants[0].Add(initiator); - } - } + Initiator = initiator; + Participants = new List(); + Ruleset = new Ruleset(layout); + Ruleset.ApplyDefault(layout.Defaults[0]); - public bool Rematch { get; private set; } - - public bool ReadyWait { get; private set; } - - public int ReadyCount { get; private set; } - - public bool Registered { get; private set; } = true; - - public bool Finished { get; private set; } - - public bool Started { get; private set; } - - public Mobile Initiator { get; } - - public List Participants { get; } - - public Ruleset Ruleset { get; private set; } - - public Arena Arena { get; private set; } - - public bool Tied { get; private set; } - - public bool IsSuddenDeath { get; set; } - - public bool IsOneVsOne => Participants.Count == 2 && Participants[0].Players.Length == 1 && - Participants[1].Players.Length == 1; - - public bool StartedBeginCountdown { get; private set; } - - public bool StartedReadyCountdown { get; private set; } - - public Tournament Tournament => m_Tournament; - - private bool CantDoAnything(Mobile mob) => m_EventGame?.CantDoAnything(mob) == true; - - public static bool IsFreeConsume(Mobile mob) - { - if (mob is not PlayerMobile pm || pm.DuelContext?.m_EventGame == null) - { - return false; - } - - return pm.DuelContext.m_EventGame.FreeConsume; - } - - public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) - { - Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); - } - - public static bool AllowSpecialMove(Mobile from, string name, SpecialMove move) => - (from as PlayerMobile)?.DuelContext?.InstAllowSpecialMove(from, name, move) != false; - - public bool InstAllowSpecialMove(Mobile from, string name, SpecialMove move) - { - if (!StartedBeginCountdown) - { - return true; - } - - var pl = Find(from); - - if (pl?.Eliminated != false) - { - return true; - } - - if (CantDoAnything(from)) - { - return false; - } - - var title = move switch - { - NinjaMove => "Bushido", - SamuraiMove => "Ninjitsu", - _ => null - }; - - if (title == null || name == null || Ruleset.GetOption(title, name)) - { - return true; - } - - from.SendMessage("The dueling ruleset prevents you from using this move."); - return false; - } - - public bool AllowSpellCast(Mobile from, Spell spell) - { - if (!StartedBeginCountdown) - { - return true; - } - - if (Find(from)?.Eliminated != false) - { - return true; - } - - if (CantDoAnything(from)) - { - return false; - } - - if (spell is RecallSpell) - { - from.SendMessage("You may not cast this spell."); - } - - string title; - string option; - - switch (spell) - { - case ArcanistSpell _: - title = "Spellweaving"; - option = spell.Name; - break; - case PaladinSpell _: - title = "Chivalry"; - option = spell.Name; - break; - case NecromancerSpell _: - title = "Necromancy"; - option = spell.Name; - break; - case NinjaSpell _: - title = "Ninjitsu"; - option = spell.Name; - break; - case SamuraiSpell _: - title = "Bushido"; - option = spell.Name; - break; - case MagerySpell magerySpell: - title = magerySpell.Circle switch - { - SpellCircle.First => "1st Circle", - SpellCircle.Second => "2nd Circle", - SpellCircle.Third => "3rd Circle", - SpellCircle.Fourth => "4th Circle", - SpellCircle.Fifth => "5th Circle", - SpellCircle.Sixth => "6th Circle", - SpellCircle.Seventh => "7th Circle", - SpellCircle.Eighth => "8th Circle", - _ => null - }; - - option = magerySpell.Name; - break; - default: - title = "Other Spell"; - option = spell.Name; - break; - } - - if (title == null || option == null || Ruleset.GetOption(title, option)) - { - return true; - } - - from.SendMessage("The dueling ruleset prevents you from casting this spell."); - return false; - } - - public bool AllowItemEquip(Mobile from, Item item) - { - if (!StartedBeginCountdown) - { - return true; - } - - var pl = Find(from); - - if (pl?.Eliminated != false) - { - return true; - } - - if (item is Dagger || CheckItemEquip(from, item)) - { - return true; - } - - from.SendMessage("The dueling ruleset prevents you from equipping this item."); - return false; - } - - public static bool AllowSpecialAbility(Mobile from, string name, bool message) - { - if (from is not PlayerMobile pm) - { - return true; - } - - var dc = pm.DuelContext; - - // No DuelContext or InstAllowSpecialAbility - return dc?.InstAllowSpecialAbility(from, name, message) != false; - } - - public bool InstAllowSpecialAbility(Mobile from, string name, bool message) - { - if (!StartedBeginCountdown) - { - return true; - } - - var pl = Find(from); - - if (pl?.Eliminated != false) - { - return true; - } - - if (CantDoAnything(from)) - { - return false; - } - - if (Ruleset.GetOption("Combat Abilities", name)) - { - return true; - } - - if (message) - { - from.SendMessage("The dueling ruleset prevents you from using this combat ability."); - } - - return false; - } - - public bool CheckItemEquip(Mobile from, Item item) - { - if (item is Fists) - { - if (!Ruleset.GetOption("Weapons", "Wrestling")) + if (addNew) { - return false; + Participants.Add(new Participant(this, 1)); + Participants.Add(new Participant(this, 1)); + Participants[0].Add(initiator); } } - else if (item is BaseArmor armor) + + public bool Rematch { get; private set; } + + public bool ReadyWait { get; private set; } + + public int ReadyCount { get; private set; } + + public bool Registered { get; private set; } = true; + + public bool Finished { get; private set; } + + public bool Started { get; private set; } + + public Mobile Initiator { get; } + + public List Participants { get; } + + public Ruleset Ruleset { get; private set; } + + public Arena Arena { get; private set; } + + public bool Tied { get; private set; } + + public bool IsSuddenDeath { get; set; } + + public bool IsOneVsOne => Participants.Count == 2 && Participants[0].Players.Length == 1 && + Participants[1].Players.Length == 1; + + public bool StartedBeginCountdown { get; private set; } + + public bool StartedReadyCountdown { get; private set; } + + public Tournament Tournament => m_Tournament; + + private bool CantDoAnything(Mobile mob) => m_EventGame?.CantDoAnything(mob) == true; + + public static bool IsFreeConsume(Mobile mob) { - if (armor.ProtectionLevel > ArmorProtectionLevel.Regular && !Ruleset.GetOption("Armor", "Magical")) + if (mob is not PlayerMobile pm || pm.DuelContext?.m_EventGame == null) { return false; } - if (!Core.AOS && armor.Resource != armor.DefaultResource && !Ruleset.GetOption("Armor", "Colored")) - { - return false; - } - - if (armor is BaseShield && !Ruleset.GetOption("Armor", "Shields")) - { - return false; - } - } - else if (item is BaseWeapon weapon) - { - if ((weapon.DamageLevel > WeaponDamageLevel.Regular || weapon.AccuracyLevel > WeaponAccuracyLevel.Regular) && - !Ruleset.GetOption("Weapons", "Magical")) - { - return false; - } - - if (!Core.AOS && weapon.Resource != CraftResource.Iron && weapon.Resource != CraftResource.None && - !Ruleset.GetOption("Weapons", "Runics")) - { - return false; - } - - if (weapon is BaseRanged && !Ruleset.GetOption("Weapons", "Ranged")) - { - return false; - } - - if (weapon is not BaseRanged && !Ruleset.GetOption("Weapons", "Melee")) - { - return false; - } - - if (weapon.PoisonCharges > 0 && weapon.Poison != null && !Ruleset.GetOption("Weapons", "Poisoned")) - { - return false; - } - - if (weapon is BaseWand && !Ruleset.GetOption("Items", "Wands")) - { - return false; - } + return pm.DuelContext.m_EventGame.FreeConsume; } - return true; - } - - public bool AllowSkillUse(Mobile from, SkillName skill) - { - if (!StartedBeginCountdown) + public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) { - return true; + Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); } - var pl = Find(from); + public static bool AllowSpecialMove(Mobile from, string name, SpecialMove move) => + (from as PlayerMobile)?.DuelContext?.InstAllowSpecialMove(from, name, move) != false; - if (pl?.Eliminated != false) + public bool InstAllowSpecialMove(Mobile from, string name, SpecialMove move) { - return true; - } - - if (CantDoAnything(from)) - { - return false; - } - - var id = (int)skill; - - if (id >= 0 && id < SkillInfo.Table.Length) - { - if (Ruleset.GetOption("Skills", SkillInfo.Table[id].Name)) + if (!StartedBeginCountdown) { return true; } - } - from.SendMessage("The dueling ruleset prevents you from using this skill."); - return false; - } + var pl = Find(from); - public bool AllowItemUse(Mobile from, Item item) - { - if (!StartedBeginCountdown) - { - return true; - } + if (pl?.Eliminated != false) + { + return true; + } - var pl = Find(from); - - if (pl?.Eliminated != false) - { - return true; - } - - if (item is not BaseRefreshPotion) - { if (CantDoAnything(from)) { return false; } - } - string title = null, option = null; + string title = move switch + { + NinjaMove => "Bushido", + SamuraiMove => "Ninjitsu", + _ => null + }; - if (item is BasePotion) - { - title = "Potions"; + if (title == null || name == null || Ruleset.GetOption(title, name)) + { + return true; + } - if (item is BaseAgilityPotion) - { - option = "Agility"; - } - else if (item is BaseCurePotion) - { - option = "Cure"; - } - else if (item is BaseHealPotion) - { - option = "Heal"; - } - else if (item is NightSightPotion) - { - option = "Nightsight"; - } - else if (item is BasePoisonPotion) - { - option = "Poison"; - } - else if (item is BaseStrengthPotion) - { - option = "Strength"; - } - else if (item is BaseExplosionPotion) - { - option = "Explosion"; - } - else if (item is BaseRefreshPotion) - { - option = "Refresh"; - } - } - else if (item is Bandage) - { - title = "Items"; - option = "Bandages"; - } - else if (item is TrappableContainer container) - { - if (container.TrapType != TrapType.None) - { - title = "Items"; - option = "Trapped Containers"; - } - } - else if (item is Bola) - { - title = "Items"; - option = "Bolas"; - } - else if (item is OrangePetals) - { - title = "Items"; - option = "Orange Petals"; - } - else if (item is EtherealMount || item.Layer == Layer.Mount) - { - title = "Items"; - option = "Mounts"; - } - else if (item is LeatherNinjaBelt) - { - title = "Items"; - option = "Shurikens"; - } - else if (item is Fukiya) - { - title = "Items"; - option = "Fukiya Darts"; - } - else if (item is FireHorn) - { - title = "Items"; - option = "Fire Horns"; - } - else if (item is BaseWand) - { - title = "Items"; - option = "Wands"; - } - - if (title != null && option != null && StartedBeginCountdown && !Started) - { - from.SendMessage("You may not use this item before the duel begins."); + from.SendMessage("The dueling ruleset prevents you from using this move."); return false; } - if (item is BasePotion && item is not BaseExplosionPotion && item is not BaseRefreshPotion && IsSuddenDeath) + public bool AllowSpellCast(Mobile from, Spell spell) { - from.SendMessage(0x22, "You may not drink potions in sudden death."); - return false; - } - - if (item is Bandage && IsSuddenDeath) - { - from.SendMessage(0x22, "You may not use bandages in sudden death."); - return false; - } - - if (title == null || option == null || Ruleset.GetOption(title, option)) - { - return true; - } - - from.SendMessage("The dueling ruleset prevents you from using this item."); - return false; - } - - private void DelayBounce_Callback(Mobile mob, Container corpse) - { - RemoveAggressions(mob); - SendOutside(mob); - Refresh(mob, corpse); - Debuff(mob); - CancelSpell(mob); - mob.Frozen = false; - } - - public void OnMapChanged(Mobile mob) - { - OnLocationChanged(mob); - } - - public void OnLocationChanged(Mobile mob) - { - if (!Registered || !StartedBeginCountdown || Finished) - { - return; - } - - var arena = Arena; - - if (arena == null) - { - return; - } - - if (mob.Map == arena.Facet && arena.Bounds.Contains(mob.Location)) - { - return; - } - - var pl = Find(mob); - - if (pl?.Eliminated != false) - { - return; - } - - if (mob.Map == Map.Internal) - { - if (mob.LogoutMap == arena.Facet && arena.Bounds.Contains(mob.LogoutLocation)) + if (!StartedBeginCountdown) { - mob.LogoutLocation = arena.Outside; + return true; } - } - pl.Eliminated = true; + if (Find(from)?.Eliminated != false) + { + return true; + } - mob.LocalOverheadMessage(MessageType.Regular, 0x22, false, "You have forfeited your position in the duel."); - mob.NonlocalOverheadMessage( - MessageType.Regular, - 0x22, - false, - $"{mob.Name} has forfeited by leaving the dueling arena." - ); - - var winner = CheckCompletion(); - - if (winner != null) - { - Finish(winner); - } - } - - [OnEvent(nameof(PlayerMobile.PlayerDeathEvent))] - public static void OnPlayerDeath(PlayerMobile m) => m.DuelContext?.OnDeath(m, m.Corpse); - - public void OnDeath(Mobile mob, Container corpse) - { - if (!Registered || !Started) - { - return; - } - - var pl = Find(mob); - - if (pl?.Eliminated != false || m_EventGame?.OnDeath(mob, corpse) == false) - { - return; - } - - pl.Eliminated = true; - - if (mob.Poison != null) - { - mob.Poison = null; - } - - Requip(mob, corpse); - DelayBounce(TimeSpan.FromSeconds(4.0), mob, corpse); - - var winner = CheckCompletion(); - - if (winner != null) - { - Finish(winner); - } - else if (!m_Yielding) - { - mob.LocalOverheadMessage(MessageType.Regular, 0x22, false, "You have been defeated."); - mob.NonlocalOverheadMessage(MessageType.Regular, 0x22, false, $"{mob.Name} has been defeated."); - } - } - - public bool CheckFull() - { - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - if (p.HasOpenSlot) + if (CantDoAnything(from)) { return false; } - } - return true; - } - - public void Requip(Mobile from, Container cont) - { - if (cont is not Corpse corpse) - { - return; - } - - var items = new List(corpse.Items); - - var didntFit = false; - - var pack = from.Backpack; - - for (var i = 0; !didntFit && i < items.Count; ++i) - { - var item = items[i]; - - if (item.Layer is Layer.Hair or Layer.FacialHair || !item.Movable) + if (spell is RecallSpell) { - continue; + from.SendMessage("You may not cast this spell."); } - if (pack != null) + string title; + string option; + + switch (spell) { - pack.DropItem(item); + case ArcanistSpell _: + title = "Spellweaving"; + option = spell.Name; + break; + case PaladinSpell _: + title = "Chivalry"; + option = spell.Name; + break; + case NecromancerSpell _: + title = "Necromancy"; + option = spell.Name; + break; + case NinjaSpell _: + title = "Ninjitsu"; + option = spell.Name; + break; + case SamuraiSpell _: + title = "Bushido"; + option = spell.Name; + break; + case MagerySpell magerySpell: + title = magerySpell.Circle switch + { + SpellCircle.First => "1st Circle", + SpellCircle.Second => "2nd Circle", + SpellCircle.Third => "3rd Circle", + SpellCircle.Fourth => "4th Circle", + SpellCircle.Fifth => "5th Circle", + SpellCircle.Sixth => "6th Circle", + SpellCircle.Seventh => "7th Circle", + SpellCircle.Eighth => "8th Circle", + _ => null + }; + + option = magerySpell.Name; + break; + default: + title = "Other Spell"; + option = spell.Name; + break; + } + + if (title == null || option == null || Ruleset.GetOption(title, option)) + { + return true; + } + + from.SendMessage("The dueling ruleset prevents you from casting this spell."); + return false; + } + + public bool AllowItemEquip(Mobile from, Item item) + { + if (!StartedBeginCountdown) + { + return true; + } + + var pl = Find(from); + + if (pl?.Eliminated != false) + { + return true; + } + + if (item is Dagger || CheckItemEquip(from, item)) + { + return true; + } + + from.SendMessage("The dueling ruleset prevents you from equipping this item."); + return false; + } + + public static bool AllowSpecialAbility(Mobile from, string name, bool message) + { + if (from is not PlayerMobile pm) + { + return true; + } + + var dc = pm.DuelContext; + + // No DuelContext or InstAllowSpecialAbility + return dc?.InstAllowSpecialAbility(from, name, message) != false; + } + + public bool InstAllowSpecialAbility(Mobile from, string name, bool message) + { + if (!StartedBeginCountdown) + { + return true; + } + + var pl = Find(from); + + if (pl?.Eliminated != false) + { + return true; + } + + if (CantDoAnything(from)) + { + return false; + } + + if (Ruleset.GetOption("Combat Abilities", name)) + { + return true; + } + + if (message) + { + from.SendMessage("The dueling ruleset prevents you from using this combat ability."); + } + + return false; + } + + public bool CheckItemEquip(Mobile from, Item item) + { + if (item is Fists) + { + if (!Ruleset.GetOption("Weapons", "Wrestling")) + { + return false; + } + } + else if (item is BaseArmor armor) + { + if (armor.ProtectionLevel > ArmorProtectionLevel.Regular && !Ruleset.GetOption("Armor", "Magical")) + { + return false; + } + + if (!Core.AOS && armor.Resource != armor.DefaultResource && !Ruleset.GetOption("Armor", "Colored")) + { + return false; + } + + if (armor is BaseShield && !Ruleset.GetOption("Armor", "Shields")) + { + return false; + } + } + else if (item is BaseWeapon weapon) + { + if ((weapon.DamageLevel > WeaponDamageLevel.Regular || weapon.AccuracyLevel > WeaponAccuracyLevel.Regular) && + !Ruleset.GetOption("Weapons", "Magical")) + { + return false; + } + + if (!Core.AOS && weapon.Resource != CraftResource.Iron && weapon.Resource != CraftResource.None && + !Ruleset.GetOption("Weapons", "Runics")) + { + return false; + } + + if (weapon is BaseRanged && !Ruleset.GetOption("Weapons", "Ranged")) + { + return false; + } + + if (weapon is not BaseRanged && !Ruleset.GetOption("Weapons", "Melee")) + { + return false; + } + + if (weapon.PoisonCharges > 0 && weapon.Poison != null && !Ruleset.GetOption("Weapons", "Poisoned")) + { + return false; + } + + if (weapon is BaseWand && !Ruleset.GetOption("Items", "Wands")) + { + return false; + } + } + + return true; + } + + public bool AllowSkillUse(Mobile from, SkillName skill) + { + if (!StartedBeginCountdown) + { + return true; + } + + var pl = Find(from); + + if (pl?.Eliminated != false) + { + return true; + } + + if (CantDoAnything(from)) + { + return false; + } + + var id = (int)skill; + + if (id >= 0 && id < SkillInfo.Table.Length) + { + if (Ruleset.GetOption("Skills", SkillInfo.Table[id].Name)) + { + return true; + } + } + + from.SendMessage("The dueling ruleset prevents you from using this skill."); + return false; + } + + public bool AllowItemUse(Mobile from, Item item) + { + if (!StartedBeginCountdown) + { + return true; + } + + var pl = Find(from); + + if (pl?.Eliminated != false) + { + return true; + } + + if (item is not BaseRefreshPotion) + { + if (CantDoAnything(from)) + { + return false; + } + } + + string title = null, option = null; + + if (item is BasePotion) + { + title = "Potions"; + + if (item is BaseAgilityPotion) + { + option = "Agility"; + } + else if (item is BaseCurePotion) + { + option = "Cure"; + } + else if (item is BaseHealPotion) + { + option = "Heal"; + } + else if (item is NightSightPotion) + { + option = "Nightsight"; + } + else if (item is BasePoisonPotion) + { + option = "Poison"; + } + else if (item is BaseStrengthPotion) + { + option = "Strength"; + } + else if (item is BaseExplosionPotion) + { + option = "Explosion"; + } + else if (item is BaseRefreshPotion) + { + option = "Refresh"; + } + } + else if (item is Bandage) + { + title = "Items"; + option = "Bandages"; + } + else if (item is TrappableContainer container) + { + if (container.TrapType != TrapType.None) + { + title = "Items"; + option = "Trapped Containers"; + } + } + else if (item is Bola) + { + title = "Items"; + option = "Bolas"; + } + else if (item is OrangePetals) + { + title = "Items"; + option = "Orange Petals"; + } + else if (item is EtherealMount || item.Layer == Layer.Mount) + { + title = "Items"; + option = "Mounts"; + } + else if (item is LeatherNinjaBelt) + { + title = "Items"; + option = "Shurikens"; + } + else if (item is Fukiya) + { + title = "Items"; + option = "Fukiya Darts"; + } + else if (item is FireHorn) + { + title = "Items"; + option = "Fire Horns"; + } + else if (item is BaseWand) + { + title = "Items"; + option = "Wands"; + } + + if (title != null && option != null && StartedBeginCountdown && !Started) + { + from.SendMessage("You may not use this item before the duel begins."); + return false; + } + + if (item is BasePotion && item is not BaseExplosionPotion && item is not BaseRefreshPotion && IsSuddenDeath) + { + from.SendMessage(0x22, "You may not drink potions in sudden death."); + return false; + } + + if (item is Bandage && IsSuddenDeath) + { + from.SendMessage(0x22, "You may not use bandages in sudden death."); + return false; + } + + if (title == null || option == null || Ruleset.GetOption(title, option)) + { + return true; + } + + from.SendMessage("The dueling ruleset prevents you from using this item."); + return false; + } + + private void DelayBounce_Callback(Mobile mob, Container corpse) + { + RemoveAggressions(mob); + SendOutside(mob); + Refresh(mob, corpse); + Debuff(mob); + CancelSpell(mob); + mob.Frozen = false; + } + + public void OnMapChanged(Mobile mob) + { + OnLocationChanged(mob); + } + + public void OnLocationChanged(Mobile mob) + { + if (!Registered || !StartedBeginCountdown || Finished) + { + return; + } + + var arena = Arena; + + if (arena == null) + { + return; + } + + if (mob.Map == arena.Facet && arena.Bounds.Contains(mob.Location)) + { + return; + } + + var pl = Find(mob); + + if (pl?.Eliminated != false) + { + return; + } + + if (mob.Map == Map.Internal) + { + if (mob.LogoutMap == arena.Facet && arena.Bounds.Contains(mob.LogoutLocation)) + { + mob.LogoutLocation = arena.Outside; + } + } + + pl.Eliminated = true; + + mob.LocalOverheadMessage(MessageType.Regular, 0x22, false, "You have forfeited your position in the duel."); + mob.NonlocalOverheadMessage( + MessageType.Regular, + 0x22, + false, + $"{mob.Name} has forfeited by leaving the dueling arena." + ); + + var winner = CheckCompletion(); + + if (winner != null) + { + Finish(winner); + } + } + + [OnEvent(nameof(PlayerMobile.PlayerDeathEvent))] + public static void OnPlayerDeath(PlayerMobile m) => m.DuelContext?.OnDeath(m, m.Corpse); + + public void OnDeath(Mobile mob, Container corpse) + { + if (!Registered || !Started) + { + return; + } + + var pl = Find(mob); + + if (pl?.Eliminated != false || m_EventGame?.OnDeath(mob, corpse) == false) + { + return; + } + + pl.Eliminated = true; + + if (mob.Poison != null) + { + mob.Poison = null; + } + + Requip(mob, corpse); + DelayBounce(TimeSpan.FromSeconds(4.0), mob, corpse); + + var winner = CheckCompletion(); + + if (winner != null) + { + Finish(winner); + } + else if (!m_Yielding) + { + mob.LocalOverheadMessage(MessageType.Regular, 0x22, false, "You have been defeated."); + mob.NonlocalOverheadMessage(MessageType.Regular, 0x22, false, $"{mob.Name} has been defeated."); + } + } + + public bool CheckFull() + { + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + if (p.HasOpenSlot) + { + return false; + } + } + + return true; + } + + public void Requip(Mobile from, Container cont) + { + if (cont is not Corpse corpse) + { + return; + } + + var items = new List(corpse.Items); + + var didntFit = false; + + var pack = from.Backpack; + + for (var i = 0; !didntFit && i < items.Count; ++i) + { + var item = items[i]; + + if (item.Layer is Layer.Hair or Layer.FacialHair || !item.Movable) + { + continue; + } + + if (pack != null) + { + pack.DropItem(item); + } + else + { + didntFit = true; + } + } + + corpse.Carved = true; + + if (corpse.ItemID == 0x2006) + { + corpse.ProcessDelta(); + corpse.SendRemovePacket(); + corpse.ItemID = Utility.Random(0xECA, 9); // bone graphic + corpse.Hue = 0; + corpse.ProcessDelta(); + + var killer = from.FindMostRecentDamager(false); + + if (killer?.Player == true) + { + killer.AddToBackpack(new Head(m_Tournament == null ? HeadType.Duel : HeadType.Tournament, from.Name)); + } + } + + from.PlaySound(0x3E3); + + if (didntFit) + { + from.SendLocalizedMessage(1062472); // You gather some of your belongings. The rest remain on the corpse. } else { - didntFit = true; + from.SendLocalizedMessage(1062471); // You quickly gather all of your belongings. } } - corpse.Carved = true; - - if (corpse.ItemID == 0x2006) + public void Refresh(Mobile mob, Container cont) { - corpse.ProcessDelta(); - corpse.SendRemovePacket(); - corpse.ItemID = Utility.Random(0xECA, 9); // bone graphic - corpse.Hue = 0; - corpse.ProcessDelta(); - - var killer = from.FindMostRecentDamager(false); - - if (killer?.Player == true) + if (!mob.Alive) { - killer.AddToBackpack(new Head(m_Tournament == null ? HeadType.Duel : HeadType.Tournament, from.Name)); - } - } + mob.Resurrect(); - from.PlaySound(0x3E3); + mob.FindItemOnLayer(Layer.OuterTorso)?.Delete(); - if (didntFit) - { - from.SendLocalizedMessage(1062472); // You gather some of your belongings. The rest remain on the corpse. - } - else - { - from.SendLocalizedMessage(1062471); // You quickly gather all of your belongings. - } - } - - public void Refresh(Mobile mob, Container cont) - { - if (!mob.Alive) - { - mob.Resurrect(); - - mob.FindItemOnLayer(Layer.OuterTorso)?.Delete(); - - if (cont is Corpse corpse) - { - for (var i = 0; i < corpse.EquipItems.Count; ++i) + if (cont is Corpse corpse) { - var item = corpse.EquipItems[i]; - - if (item.Movable && item.Layer != Layer.Hair && item.Layer != Layer.FacialHair && - item.IsChildOf(mob.Backpack)) + for (var i = 0; i < corpse.EquipItems.Count; ++i) { - mob.EquipItem(item); + var item = corpse.EquipItems[i]; + + if (item.Movable && item.Layer != Layer.Hair && item.Layer != Layer.FacialHair && + item.IsChildOf(mob.Backpack)) + { + mob.EquipItem(item); + } } } } + + mob.Hits = mob.HitsMax; + mob.Stam = mob.StamMax; + mob.Mana = mob.ManaMax; + + mob.Poison = null; } - mob.Hits = mob.HitsMax; - mob.Stam = mob.StamMax; - mob.Mana = mob.ManaMax; - - mob.Poison = null; - } - - public void SendOutside(Mobile mob) - { - if (Arena == null) + public void SendOutside(Mobile mob) { - return; - } - - mob.Combatant = null; - mob.MoveToWorld(Arena.Outside, Arena.Facet); - } - - public void Finish(Participant winner) - { - if (Finished) - { - return; - } - - _autoTieTimerToken.Cancel(); - StopSDTimers(); - - Finished = true; - - for (var i = 0; i < winner.Players.Length; ++i) - { - var pl = winner.Players[i]; - - if (pl?.Eliminated == false) + if (Arena == null) { - DelayBounce(TimeSpan.FromSeconds(8.0), pl.Mobile, null); + return; } + + mob.Combatant = null; + mob.MoveToWorld(Arena.Outside, Arena.Facet); } - winner.Broadcast( - 0x59, - null, - winner.Players.Length == 1 ? "{0} has won the duel." : "{0} and {1} team have won the duel.", - winner.Players.Length == 1 ? "You have won the duel." : "Your team has won the duel." - ); - - if (m_Tournament != null && winner.TourneyPart != null) + public void Finish(Participant winner) { - m_Match.Winner = winner.TourneyPart; - winner.TourneyPart.WonMatch(m_Match); - m_Tournament.HandleWon(Arena, m_Match, winner.TourneyPart); - } - - for (var i = 0; i < Participants.Count; ++i) - { - var loser = Participants[i]; - - if (loser != winner) + if (Finished) { - loser.Broadcast( - 0x22, - null, - loser.Players.Length == 1 ? "{0} has lost the duel." : "{0} and {1} team have lost the duel.", - loser.Players.Length == 1 ? "You have lost the duel." : "Your team has lost the duel." - ); + return; + } - if (m_Tournament != null) + _autoTieTimerToken.Cancel(); + StopSDTimers(); + + Finished = true; + + for (var i = 0; i < winner.Players.Length; ++i) + { + var pl = winner.Players[i]; + + if (pl?.Eliminated == false) { - loser.TourneyPart?.LostMatch(m_Match); + DelayBounce(TimeSpan.FromSeconds(8.0), pl.Mobile, null); } } - for (var j = 0; j < loser.Players.Length; ++j) + winner.Broadcast( + 0x59, + null, + winner.Players.Length == 1 ? "{0} has won the duel." : "{0} and {1} team have won the duel.", + winner.Players.Length == 1 ? "You have won the duel." : "Your team has won the duel." + ); + + if (m_Tournament != null && winner.TourneyPart != null) { - if (loser.Players[j] != null) + m_Match.Winner = winner.TourneyPart; + winner.TourneyPart.WonMatch(m_Match); + m_Tournament.HandleWon(Arena, m_Match, winner.TourneyPart); + } + + for (var i = 0; i < Participants.Count; ++i) + { + var loser = Participants[i]; + + if (loser != winner) { - RemoveAggressions(loser.Players[j].Mobile); - loser.Players[j].Mobile.Delta(MobileDelta.Noto); - loser.Players[j].Mobile.CloseGump(); + loser.Broadcast( + 0x22, + null, + loser.Players.Length == 1 ? "{0} has lost the duel." : "{0} and {1} team have lost the duel.", + loser.Players.Length == 1 ? "You have lost the duel." : "Your team has lost the duel." + ); if (m_Tournament != null) { - loser.Players[j].Mobile.SendEverything(); + loser.TourneyPart?.LostMatch(m_Match); } } - } - } - if (IsOneVsOne) - { - var dp1 = Participants[0].Players[0]; - var dp2 = Participants[1].Players[0]; - - if (dp1 != null && dp2 != null) - { - Award(dp1.Mobile, dp2.Mobile, dp1.Participant == winner); - Award(dp2.Mobile, dp1.Mobile, dp2.Participant == winner); - } - } - - m_EventGame?.OnStop(); - - Timer.StartTimer(TimeSpan.FromSeconds(9.0), UnregisterRematch); - } - - public void Award(Mobile us, Mobile them, bool won) - { - var ladder = Arena == null ? Ladder.Instance : Arena.AcquireLadder(); - - if (ladder == null) - { - return; - } - - var ourEntry = ladder.Find(us); - var theirEntry = ladder.Find(them); - - if (ourEntry == null || theirEntry == null) - { - return; - } - - var xpGain = Ladder.GetExperienceGain(ourEntry, theirEntry, won); - - if (xpGain == 0) - { - return; - } - - if (m_Tournament != null) - { - xpGain *= xpGain > 0 ? 5 : 2; - } - - if (won) - { - ++ourEntry.Wins; - } - else - { - ++ourEntry.Losses; - } - - var oldLevel = Ladder.GetLevel(ourEntry.Experience); - - ourEntry.Experience += xpGain; - - if (ourEntry.Experience < 0) - { - ourEntry.Experience = 0; - } - - ladder.UpdateEntry(ourEntry); - - var newLevel = Ladder.GetLevel(ourEntry.Experience); - - if (newLevel > oldLevel) - { - us.SendMessage(0x59, $"You have achieved level {newLevel}!"); - } - else if (newLevel < oldLevel) - { - us.SendMessage(0x22, $"You have lost a level. You are now at {newLevel}."); - } - } - - public void UnregisterRematch() - { - Unregister(true); - } - - public void Unregister() - { - Unregister(false); - } - - public void Unregister(bool queryRematch) - { - DestroyWall(); - - if (!Registered) - { - return; - } - - Registered = false; - - Arena?.Evict(); - - StopSDTimers(); - - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - if (pl == null) + for (var j = 0; j < loser.Players.Length; ++j) { - continue; - } - - if (pl.Mobile is PlayerMobile mobile) - { - mobile.DuelPlayer = null; - } - - CloseAllGumps(pl); - } - } - - if (queryRematch && m_Tournament == null) - { - QueryRematch(); - } - } - - public void QueryRematch() - { - var dc = new DuelContext(Initiator, Ruleset.Layout, false); - - dc.Ruleset = Ruleset; - dc.Rematch = true; - - dc.Participants.Clear(); - - for (var i = 0; i < Participants.Count; ++i) - { - var oldPart = Participants[i]; - var newPart = new Participant(dc, oldPart.Players.Length); - - for (var j = 0; j < oldPart.Players.Length; ++j) - { - var oldPlayer = oldPart.Players[j]; - - if (oldPlayer != null) - { - newPart.Players[j] = new DuelPlayer(oldPlayer.Mobile, newPart); - } - } - - dc.Participants.Add(newPart); - } - - dc.CloseAllGumps(); - dc.SendReadyUpGump(); - } - - public DuelPlayer Find(Mobile mob) - { - if (mob is PlayerMobile pm) - { - if (pm.DuelContext == this) - { - return pm.DuelPlayer; - } - - return null; - } - - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - var pl = p.Find(mob); - - if (pl != null) - { - return pl; - } - } - - return null; - } - - public bool IsAlly(Mobile m1, Mobile m2) - { - var pl1 = Find(m1); - var pl2 = Find(m2); - - return pl1 != null && pl1.Participant == pl2?.Participant; - } - - public Participant CheckCompletion() - { - Participant winner = null; - - var hasWinner = false; - var eliminated = 0; - - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - if (p.Eliminated) - { - ++eliminated; - - if (eliminated == Participants.Count - 1) - { - hasWinner = true; - } - } - else - { - winner = p; - } - } - - return hasWinner ? winner ?? Participants[0] : null; - } - - public void StartCountdown(int count, CountdownCallback cb) - { - Timer.StartTimer( - TimeSpan.FromSeconds(1.0), - count, - () => Countdown_Callback(cb), - out _countdownTimerToken - ); - } - - public void StopCountdown() => _countdownTimerToken.Cancel(); - - private void Countdown_Callback(CountdownCallback cb) - { - var count = _countdownTimerToken.RemainingCount; - - if (count == 0) - { - StopCountdown(); - } - - cb(count); - } - - public void StopSDTimers() - { - _SdWarnTimerToken.Cancel(); - _SdActivateTimerToken.Cancel(); - } - - public void StartSuddenDeath(TimeSpan timeUntilActive) - { - _SdWarnTimerToken.Cancel(); - Timer.StartTimer(TimeSpan.FromMinutes(timeUntilActive.TotalMinutes * 0.9), WarnSuddenDeath, out _SdWarnTimerToken); - - _SdActivateTimerToken.Cancel(); - Timer.StartTimer(timeUntilActive, ActivateSuddenDeath, out _SdActivateTimerToken); - } - - public void WarnSuddenDeath() - { - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - if (pl?.Eliminated != false) - { - continue; - } - - pl.Mobile.SendSound(0x1E1); - pl.Mobile.SendMessage(0x22, "Warning! Warning! Warning!"); - pl.Mobile.SendMessage(0x22, "Sudden death will be active soon!"); - } - } - - m_Tournament?.Alert(Arena, "Sudden death will be active soon!"); - - _SdWarnTimerToken.Cancel(); - } - - public static bool CheckSuddenDeath(Mobile mob) => mob is PlayerMobile pm && pm.DuelPlayer?.Eliminated == false && - pm.DuelContext?.IsSuddenDeath == true; - - public void ActivateSuddenDeath() - { - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - if (pl?.Eliminated != false) - { - continue; - } - - pl.Mobile.SendSound(0x1E1); - pl.Mobile.SendMessage(0x22, "Warning! Warning! Warning!"); - pl.Mobile.SendMessage( - 0x22, - "Sudden death has ACTIVATED. You are now unable to perform any beneficial actions." - ); - } - } - - m_Tournament?.Alert(Arena, "Sudden death has been activated!"); - - IsSuddenDeath = true; - - _SdActivateTimerToken.Cancel(); - } - - public void BeginAutoTie() - { - var ts = m_Tournament == null || m_Tournament.TourneyType == TourneyType.Standard - ? AutoTieDelay - : TimeSpan.FromMinutes(90.0); - - _autoTieTimerToken.Cancel(); - Timer.StartTimer(ts, InvokeAutoTie, out _autoTieTimerToken); - } - - public void InvokeAutoTie() - { - _autoTieTimerToken.Cancel(); - - if (!Started || Finished) - { - return; - } - - Tied = true; - Finished = true; - - StopSDTimers(); - - var remaining = new List(); - - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - if (p.Eliminated) - { - p.Broadcast( - 0x22, - null, - p.Players.Length == 1 ? "{0} has lost the duel." : "{0} and {1} team have lost the duel.", - p.Players.Length == 1 ? "You have lost the duel." : "Your team has lost the duel." - ); - } - else - { - p.Broadcast( - 0x59, - null, - p.Players.Length == 1 - ? "{0} has tied the duel due to time expiration." - : "{0} and {1} team have tied the duel due to time expiration.", - p.Players.Length == 1 - ? "You have tied the duel due to time expiration." - : "Your team has tied the duel due to time expiration." - ); - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - if (pl?.Eliminated == false) + if (loser.Players[j] != null) { - DelayBounce(TimeSpan.FromSeconds(8.0), pl.Mobile, null); + RemoveAggressions(loser.Players[j].Mobile); + loser.Players[j].Mobile.Delta(MobileDelta.Noto); + loser.Players[j].Mobile.CloseGump(); + + if (m_Tournament != null) + { + loser.Players[j].Mobile.SendEverything(); + } } } - - if (p.TourneyPart != null) - { - remaining.Add(p.TourneyPart); - } } - for (var j = 0; j < p.Players.Length; ++j) + if (IsOneVsOne) { - var pl = p.Players[j]; + var dp1 = Participants[0].Players[0]; + var dp2 = Participants[1].Players[0]; - if (pl != null) + if (dp1 != null && dp2 != null) { - pl.Mobile.Delta(MobileDelta.Noto); - pl.Mobile.SendEverything(); + Award(dp1.Mobile, dp2.Mobile, dp1.Participant == winner); + Award(dp2.Mobile, dp1.Mobile, dp2.Participant == winner); } } + + m_EventGame?.OnStop(); + + Timer.StartTimer(TimeSpan.FromSeconds(9.0), UnregisterRematch); } - m_Tournament?.HandleTie(Arena, m_Match, remaining); - - Timer.StartTimer(TimeSpan.FromSeconds(10.0), Unregister); - } - - public static void Configure() - { - EventSink.Speech += EventSink_Speech; - - CommandSystem.Register("vli", AccessLevel.GameMaster, vli_oc); - } - - [Usage("vli")] - [Aliases("ViewLadderInfo")] - [Description("View ladder information.")] - private static void vli_oc(CommandEventArgs e) - { - e.Mobile.BeginTarget(-1, false, TargetFlags.None, vli_ot); - } - - private static void vli_ot(Mobile from, object obj) - { - if (obj is PlayerMobile pm) + public void Award(Mobile us, Mobile them, bool won) { - var ladder = Ladder.Instance; + var ladder = Arena == null ? Ladder.Instance : Arena.AcquireLadder(); if (ladder == null) { return; } - var entry = ladder.Find(pm); + var ourEntry = ladder.Find(us); + var theirEntry = ladder.Find(them); - if (entry != null) + if (ourEntry == null || theirEntry == null) { - from.SendGump(new PropertiesGump(from, entry)); - } - } - } - - public static bool CheckCombat(Mobile m) - { - foreach (var info in m.Aggressed) - { - if (info.Defender.Player && Core.Now - info.LastCombatTime < CombatDelay) - { - return true; - } - } - - foreach (var info in m.Aggressors) - { - if (info.Attacker.Player && Core.Now - info.LastCombatTime < CombatDelay) - { - return true; - } - } - - return false; - } - - [OnEvent(nameof(PlayerMobile.PlayerLoginEvent))] - public static void OnLogin(PlayerMobile pm) - { - var dc = pm.DuelContext; - - if (dc == null) - { - return; - } - - if (dc.ReadyWait && pm.DuelPlayer.Ready && !dc.Started && !dc.StartedBeginCountdown && !dc.Finished) - { - if (dc.m_Tournament == null) - { - ReadyGump.DisplayTo(pm, dc, dc.ReadyCount); - } - } - else if (dc.ReadyWait && !dc.StartedBeginCountdown && !dc.Started && !dc.Finished) - { - if (dc.m_Tournament == null) - { - ReadyUpGump.DisplayTo(pm, dc); - } - } - else if (dc.Initiator == pm && !dc.ReadyWait && !dc.StartedBeginCountdown && !dc.Started && !dc.Finished) - { - DuelContextGump.DisplayTo(pm, dc); - } - } - - private static void ViewLadder_OnTarget(Mobile from, object obj, Ladder ladder) - { - if (obj is PlayerMobile pm) - { - var entry = ladder.Find(pm); - - if (entry == null) - { - return; // sanity + return; } - if (from == pm) + var xpGain = Ladder.GetExperienceGain(ourEntry, theirEntry, won); + + if (xpGain == 0) { - pm.PrivateOverheadMessage( - MessageType.Regular, - pm.SpeechHue, - true, - $"You are ranked {LadderGump.Rank(entry.Index + 1)} at level {Ladder.GetLevel(entry.Experience)}.", - from.NetState - ); + return; + } + + if (m_Tournament != null) + { + xpGain *= xpGain > 0 ? 5 : 2; + } + + if (won) + { + ++ourEntry.Wins; } else { - pm.PrivateOverheadMessage( - MessageType.Regular, - pm.SpeechHue, - true, - $"They are ranked {LadderGump.Rank(entry.Index + 1)} at level {Ladder.GetLevel(entry.Experience)}.", - from.NetState - ); + ++ourEntry.Losses; } - } - else if (obj is Mobile mob) - { - if (mob.Body.IsHuman) - { - mob.PrivateOverheadMessage( - MessageType.Regular, - mob.SpeechHue, - false, - "I'm not a duelist, and quite frankly, I resent the implication.", - from.NetState - ); - } - else - { - mob.PrivateOverheadMessage( - MessageType.Regular, - 0x3B2, - true, - "It's probably better than you.", - from.NetState - ); - } - } - else - { - from.SendMessage("That's not a player."); - } - } - private static void EventSink_Speech(SpeechEventArgs e) - { - if (e.Handled) - { - return; + var oldLevel = Ladder.GetLevel(ourEntry.Experience); + + ourEntry.Experience += xpGain; + + if (ourEntry.Experience < 0) + { + ourEntry.Experience = 0; + } + + ladder.UpdateEntry(ourEntry); + + var newLevel = Ladder.GetLevel(ourEntry.Experience); + + if (newLevel > oldLevel) + { + us.SendMessage(0x59, $"You have achieved level {newLevel}!"); + } + else if (newLevel < oldLevel) + { + us.SendMessage(0x22, $"You have lost a level. You are now at {newLevel}."); + } } - if (e.Mobile is not PlayerMobile pm) + public void UnregisterRematch() { - return; + Unregister(true); } - if (e.Speech.InsensitiveContains("i wish to duel")) + public void Unregister() { - if (!pm.CheckAlive()) + Unregister(false); + } + + public void Unregister(bool queryRematch) + { + DestroyWall(); + + if (!Registered) { + return; } - else if (pm.Region.IsPartOf()) + + Registered = false; + + Arena?.Evict(); + + StopSDTimers(); + + for (var i = 0; i < Participants.Count; ++i) { - } - else if (CheckCombat(pm)) - { - e.Mobile.SendMessage( - 0x22, - "You have recently been in combat with another player and must wait before starting a duel." - ); - } - else if (pm.DuelContext != null) - { - if (pm.DuelContext.Initiator == pm) + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) { - e.Mobile.SendMessage(0x22, "You have already started a duel."); - } - else - { - e.Mobile.SendMessage(0x22, "You have already been challenged in a duel."); - } - } - else if (TournamentController.IsActive) - { - e.Mobile.SendMessage(0x22, "You may not start a duel while a tournament is active."); - } - else - { - DuelContextGump.DisplayTo(pm, new DuelContext(pm, RulesetLayout.Root)); - e.Handled = true; - } - } - else if (e.Speech.InsensitiveEquals("change arena preferences")) - { - if (!pm.CheckAlive()) - { - } - else - { - var prefs = Preferences.Instance; + var pl = p.Players[j]; - if (prefs != null) - { - PreferencesGump.DisplayTo(e.Mobile, prefs); - } - } - } - else if (e.Speech.InsensitiveEquals("showladder")) - { - e.Blocked = true; - if (!pm.CheckAlive()) - { - } - else - { - var instance = Ladder.Instance; - - if (instance == null) - { - // pm.SendMessage( "Ladder not yet initialized." ); - } - else - { - var entry = instance.Find(pm); - - if (entry == null) + if (pl == null) { - return; // sanity + continue; } - pm.LocalOverheadMessage( - MessageType.Regular, - pm.SpeechHue, - true, - $"You are ranked {LadderGump.Rank(entry.Index + 1)} at level {Ladder.GetLevel(entry.Experience)}." - ); - pm.NonlocalOverheadMessage( - MessageType.Regular, - pm.SpeechHue, - true, - $"{pm.Name} is ranked {LadderGump.Rank(entry.Index + 1)} at level {Ladder.GetLevel(entry.Experience)}." - ); + if (pl.Mobile is PlayerMobile mobile) + { + mobile.DuelPlayer = null; + } - // pm.PublicOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( "Level {0} with {1} win{2} and {3} loss{4}.", Ladder.GetLevel( entry.Experience ), entry.Wins, entry.Wins==1?"":"s", entry.Losses, entry.Losses==1?"":"es" ) ); - // pm.PublicOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( "Level {0} with {1} win{2} and {3} loss{4}.", Ladder.GetLevel( entry.Experience ), entry.Wins, entry.Wins==1?"":"s", entry.Losses, entry.Losses==1?"":"es" ) ); + CloseAllGumps(pl); } } - } - else if (e.Speech.InsensitiveEquals("viewladder")) - { - e.Blocked = true; - if (!pm.CheckAlive()) + if (queryRematch && m_Tournament == null) { + QueryRematch(); } - else - { - var instance = Ladder.Instance; + } - if (instance == null) + public void QueryRematch() + { + var dc = new DuelContext(Initiator, Ruleset.Layout, false); + + dc.Ruleset = Ruleset; + dc.Rematch = true; + + dc.Participants.Clear(); + + for (var i = 0; i < Participants.Count; ++i) + { + var oldPart = Participants[i]; + var newPart = new Participant(dc, oldPart.Players.Length); + + for (var j = 0; j < oldPart.Players.Length; ++j) { - // pm.SendMessage( "Ladder not yet initialized." ); + var oldPlayer = oldPart.Players[j]; + + if (oldPlayer != null) + { + newPart.Players[j] = new DuelPlayer(oldPlayer.Mobile, newPart); + } + } + + dc.Participants.Add(newPart); + } + + dc.CloseAllGumps(); + dc.SendReadyUpGump(); + } + + public DuelPlayer Find(Mobile mob) + { + if (mob is PlayerMobile pm) + { + if (pm.DuelContext == this) + { + return pm.DuelPlayer; + } + + return null; + } + + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + var pl = p.Find(mob); + + if (pl != null) + { + return pl; + } + } + + return null; + } + + public bool IsAlly(Mobile m1, Mobile m2) + { + var pl1 = Find(m1); + var pl2 = Find(m2); + + return pl1 != null && pl1.Participant == pl2?.Participant; + } + + public Participant CheckCompletion() + { + Participant winner = null; + + var hasWinner = false; + var eliminated = 0; + + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + if (p.Eliminated) + { + ++eliminated; + + if (eliminated == Participants.Count - 1) + { + hasWinner = true; + } } else { - pm.SendMessage("Target a player to view their ranking and level."); - pm.BeginTarget(16, false, TargetFlags.None, ViewLadder_OnTarget, instance); + winner = p; + } + } + + return hasWinner ? winner ?? Participants[0] : null; + } + + public void StartCountdown(int count, CountdownCallback cb) + { + Timer.StartTimer( + TimeSpan.FromSeconds(1.0), + count, + () => Countdown_Callback(cb), + out _countdownTimerToken + ); + } + + public void StopCountdown() => _countdownTimerToken.Cancel(); + + private void Countdown_Callback(CountdownCallback cb) + { + var count = _countdownTimerToken.RemainingCount; + + if (count == 0) + { + StopCountdown(); + } + + cb(count); + } + + public void StopSDTimers() + { + _SdWarnTimerToken.Cancel(); + _SdActivateTimerToken.Cancel(); + } + + public void StartSuddenDeath(TimeSpan timeUntilActive) + { + _SdWarnTimerToken.Cancel(); + Timer.StartTimer(TimeSpan.FromMinutes(timeUntilActive.TotalMinutes * 0.9), WarnSuddenDeath, out _SdWarnTimerToken); + + _SdActivateTimerToken.Cancel(); + Timer.StartTimer(timeUntilActive, ActivateSuddenDeath, out _SdActivateTimerToken); + } + + public void WarnSuddenDeath() + { + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + if (pl?.Eliminated != false) + { + continue; + } + + pl.Mobile.SendSound(0x1E1); + pl.Mobile.SendMessage(0x22, "Warning! Warning! Warning!"); + pl.Mobile.SendMessage(0x22, "Sudden death will be active soon!"); + } + } + + m_Tournament?.Alert(Arena, "Sudden death will be active soon!"); + + _SdWarnTimerToken.Cancel(); + } + + public static bool CheckSuddenDeath(Mobile mob) => mob is PlayerMobile pm && pm.DuelPlayer?.Eliminated == false && + pm.DuelContext?.IsSuddenDeath == true; + + public void ActivateSuddenDeath() + { + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + if (pl?.Eliminated != false) + { + continue; + } + + pl.Mobile.SendSound(0x1E1); + pl.Mobile.SendMessage(0x22, "Warning! Warning! Warning!"); + pl.Mobile.SendMessage( + 0x22, + "Sudden death has ACTIVATED. You are now unable to perform any beneficial actions." + ); + } + } + + m_Tournament?.Alert(Arena, "Sudden death has been activated!"); + + IsSuddenDeath = true; + + _SdActivateTimerToken.Cancel(); + } + + public void BeginAutoTie() + { + var ts = m_Tournament == null || m_Tournament.TourneyType == TourneyType.Standard + ? AutoTieDelay + : TimeSpan.FromMinutes(90.0); + + _autoTieTimerToken.Cancel(); + Timer.StartTimer(ts, InvokeAutoTie, out _autoTieTimerToken); + } + + public void InvokeAutoTie() + { + _autoTieTimerToken.Cancel(); + + if (!Started || Finished) + { + return; + } + + Tied = true; + Finished = true; + + StopSDTimers(); + + var remaining = new List(); + + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + if (p.Eliminated) + { + p.Broadcast( + 0x22, + null, + p.Players.Length == 1 ? "{0} has lost the duel." : "{0} and {1} team have lost the duel.", + p.Players.Length == 1 ? "You have lost the duel." : "Your team has lost the duel." + ); + } + else + { + p.Broadcast( + 0x59, + null, + p.Players.Length == 1 + ? "{0} has tied the duel due to time expiration." + : "{0} and {1} team have tied the duel due to time expiration.", + p.Players.Length == 1 + ? "You have tied the duel due to time expiration." + : "Your team has tied the duel due to time expiration." + ); + + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + if (pl?.Eliminated == false) + { + DelayBounce(TimeSpan.FromSeconds(8.0), pl.Mobile, null); + } + } + + if (p.TourneyPart != null) + { + remaining.Add(p.TourneyPart); + } + } + + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + if (pl != null) + { + pl.Mobile.Delta(MobileDelta.Noto); + pl.Mobile.SendEverything(); + } + } + } + + m_Tournament?.HandleTie(Arena, m_Match, remaining); + + Timer.StartTimer(TimeSpan.FromSeconds(10.0), Unregister); + } + + public static void Configure() + { + EventSink.Speech += EventSink_Speech; + + CommandSystem.Register("vli", AccessLevel.GameMaster, vli_oc); + } + + [Usage("vli")] + [Aliases("ViewLadderInfo")] + [Description("View ladder information.")] + private static void vli_oc(CommandEventArgs e) + { + e.Mobile.BeginTarget(-1, false, TargetFlags.None, vli_ot); + } + + private static void vli_ot(Mobile from, object obj) + { + if (obj is PlayerMobile pm) + { + var ladder = Ladder.Instance; + + if (ladder == null) + { + return; + } + + var entry = ladder.Find(pm); + + if (entry != null) + { + from.SendGump(new PropertiesGump(from, entry)); } } } - else if (e.Speech.InsensitiveContains("i yield")) - { - if (!pm.CheckAlive()) - { - } - else if (pm.DuelContext == null) - { - } - else if (pm.DuelContext.Finished) - { - e.Mobile.SendMessage(0x22, "The duel is already finished."); - } - else if (!pm.DuelContext.Started) - { - var dc = pm.DuelContext; - var init = dc.Initiator; - if (pm.DuelContext.StartedBeginCountdown) + public static bool CheckCombat(Mobile m) + { + foreach (var info in m.Aggressed) + { + if (info.Defender.Player && Core.Now - info.LastCombatTime < CombatDelay) { - e.Mobile.SendMessage(0x22, "The duel has not yet started."); + return true; + } + } + + foreach (var info in m.Aggressors) + { + if (info.Attacker.Player && Core.Now - info.LastCombatTime < CombatDelay) + { + return true; + } + } + + return false; + } + + [OnEvent(nameof(PlayerMobile.PlayerLoginEvent))] + public static void OnLogin(PlayerMobile pm) + { + var dc = pm.DuelContext; + + if (dc == null) + { + return; + } + + if (dc.ReadyWait && pm.DuelPlayer.Ready && !dc.Started && !dc.StartedBeginCountdown && !dc.Finished) + { + if (dc.m_Tournament == null) + { + pm.SendGump(new ReadyGump(pm, dc, dc.ReadyCount)); + } + } + else if (dc.ReadyWait && !dc.StartedBeginCountdown && !dc.Started && !dc.Finished) + { + if (dc.m_Tournament == null) + { + pm.SendGump(new ReadyUpGump(pm, dc)); + } + } + else if (dc.Initiator == pm && !dc.ReadyWait && !dc.StartedBeginCountdown && !dc.Started && !dc.Finished) + { + pm.SendGump(new DuelContextGump(pm, dc)); + } + } + + private static void ViewLadder_OnTarget(Mobile from, object obj, Ladder ladder) + { + if (obj is PlayerMobile pm) + { + var entry = ladder.Find(pm); + + if (entry == null) + { + return; // sanity + } + + var text = + $"{{0}} are ranked {LadderGump.Rank(entry.Index + 1)} at level {Ladder.GetLevel(entry.Experience)}."; + + pm.PrivateOverheadMessage( + MessageType.Regular, + pm.SpeechHue, + true, + string.Format(text, from == pm ? "You" : "They"), + from.NetState + ); + } + else if (obj is Mobile mob) + { + if (mob.Body.IsHuman) + { + mob.PrivateOverheadMessage( + MessageType.Regular, + mob.SpeechHue, + false, + "I'm not a duelist, and quite frankly, I resent the implication.", + from.NetState + ); + } + else + { + mob.PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + true, + "It's probably better than you.", + from.NetState + ); + } + } + else + { + from.SendMessage("That's not a player."); + } + } + + private static void EventSink_Speech(SpeechEventArgs e) + { + if (e.Handled) + { + return; + } + + if (e.Mobile is not PlayerMobile pm) + { + return; + } + + if (e.Speech.InsensitiveContains("i wish to duel")) + { + if (!pm.CheckAlive()) + { + } + else if (pm.Region.IsPartOf()) + { + } + else if (CheckCombat(pm)) + { + e.Mobile.SendMessage( + 0x22, + "You have recently been in combat with another player and must wait before starting a duel." + ); + } + else if (pm.DuelContext != null) + { + if (pm.DuelContext.Initiator == pm) + { + e.Mobile.SendMessage(0x22, "You have already started a duel."); + } + else + { + e.Mobile.SendMessage(0x22, "You have already been challenged in a duel."); + } + } + else if (TournamentController.IsActive) + { + e.Mobile.SendMessage(0x22, "You may not start a duel while a tournament is active."); + } + else + { + pm.SendGump(new DuelContextGump(pm, new DuelContext(pm, RulesetLayout.Root))); + e.Handled = true; + } + } + else if (e.Speech.InsensitiveEquals("change arena preferences")) + { + if (!pm.CheckAlive()) + { + } + else + { + var prefs = Preferences.Instance; + + if (prefs != null) + { + e.Mobile.SendGump(new PreferencesGump(e.Mobile, prefs)); + } + } + } + else if (e.Speech.InsensitiveEquals("showladder")) + { + e.Blocked = true; + if (!pm.CheckAlive()) + { + } + else + { + var instance = Ladder.Instance; + + if (instance == null) + { + // pm.SendMessage( "Ladder not yet initialized." ); + } + else + { + var entry = instance.Find(pm); + + if (entry == null) + { + return; // sanity + } + + var text = + $"{{0}} {{1}} ranked {LadderGump.Rank(entry.Index + 1)} at level {Ladder.GetLevel(entry.Experience)}."; + + pm.LocalOverheadMessage(MessageType.Regular, pm.SpeechHue, true, string.Format(text, "You", "are")); + pm.NonlocalOverheadMessage( + MessageType.Regular, + pm.SpeechHue, + true, + string.Format(text, pm.Name, "is") + ); + + // pm.PublicOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( "Level {0} with {1} win{2} and {3} loss{4}.", Ladder.GetLevel( entry.Experience ), entry.Wins, entry.Wins==1?"":"s", entry.Losses, entry.Losses==1?"":"es" ) ); + // pm.PublicOverheadMessage( MessageType.Regular, pm.SpeechHue, true, String.Format( "Level {0} with {1} win{2} and {3} loss{4}.", Ladder.GetLevel( entry.Experience ), entry.Wins, entry.Wins==1?"":"s", entry.Losses, entry.Losses==1?"":"es" ) ); + } + } + } + else if (e.Speech.InsensitiveEquals("viewladder")) + { + e.Blocked = true; + + if (!pm.CheckAlive()) + { + } + else + { + var instance = Ladder.Instance; + + if (instance == null) + { + // pm.SendMessage( "Ladder not yet initialized." ); + } + else + { + pm.SendMessage("Target a player to view their ranking and level."); + pm.BeginTarget(16, false, TargetFlags.None, ViewLadder_OnTarget, instance); + } + } + } + else if (e.Speech.InsensitiveContains("i yield")) + { + if (!pm.CheckAlive()) + { + } + else if (pm.DuelContext == null) + { + } + else if (pm.DuelContext.Finished) + { + e.Mobile.SendMessage(0x22, "The duel is already finished."); + } + else if (!pm.DuelContext.Started) + { + var dc = pm.DuelContext; + var init = dc.Initiator; + + if (pm.DuelContext.StartedBeginCountdown) + { + e.Mobile.SendMessage(0x22, "The duel has not yet started."); + } + else + { + var pl = pm.DuelContext.Find(pm); + + if (pl == null) + { + return; + } + + var p = pl.Participant; + + if (!pm.DuelContext.ReadyWait) // still setting stuff up + { + p.Broadcast(0x22, null, "{0} has yielded.", "You have yielded."); + + if (init == pm) + { + dc.Unregister(); + } + else + { + p.Nullify(pl); + pm.DuelPlayer = null; + + foreach (var g in init.GetGumps()) + { + if (g is ParticipantGump pg && pg.Participant == p) + { + init.SendGump(new ParticipantGump(init, dc, p)); + break; + } + + if (g is DuelContextGump dcg && dcg.Context == dc) + { + init.SendGump(new DuelContextGump(init, dc)); + break; + } + } + } + } + else if (!pm.DuelContext.StartedReadyCountdown) // at ready stage + { + p.Broadcast(0x22, null, "{0} has yielded.", "You have yielded."); + + dc.m_Yielding = true; + dc.RejectReady(pm, null); + dc.m_Yielding = false; + + if (init == pm) + { + dc.Unregister(); + } + else if (dc.Registered) + { + p.Nullify(pl); + pm.DuelPlayer = null; + + var send = true; + + foreach (var g in init.GetGumps()) + { + if (g is ParticipantGump pg && pg.Participant == p) + { + init.SendGump(new ParticipantGump(init, dc, p)); + send = false; + break; + } + + if (g is DuelContextGump dcg && dcg.Context == dc) + { + init.SendGump(new DuelContextGump(init, dc)); + send = false; + break; + } + } + + if (send) + { + init.SendGump(new DuelContextGump(init, dc)); + } + } + } + else + { + pm.DuelContext.StopCountdown(); + pm.DuelContext.StartedReadyCountdown = false; + p.Broadcast(0x22, null, "{0} has yielded.", "You have yielded."); + + dc.m_Yielding = true; + dc.RejectReady(pm, null); + dc.m_Yielding = false; + + if (init == pm) + { + dc.Unregister(); + } + else if (dc.Registered) + { + p.Nullify(pl); + pm.DuelPlayer = null; + + var send = true; + + foreach (var g in init.GetGumps()) + { + if (g is ParticipantGump pg && pg.Participant == p) + { + init.SendGump(new ParticipantGump(init, dc, p)); + send = false; + break; + } + + if (g is DuelContextGump dcg && dcg.Context == dc) + { + init.SendGump(new DuelContextGump(init, dc)); + send = false; + break; + } + } + + if (send) + { + init.SendGump(new DuelContextGump(init, dc)); + } + } + } + } } else { var pl = pm.DuelContext.Find(pm); - if (pl == null) + if (pl != null) { - return; - } - - var p = pl.Participant; - - if (!pm.DuelContext.ReadyWait) // still setting stuff up - { - p.Broadcast(0x22, null, "{0} has yielded.", "You have yielded."); - - if (init == pm) + if (pm.DuelContext.IsOneVsOne) { - dc.Unregister(); + e.Mobile.SendMessage(0x22, "You may not yield a 1 on 1 match."); + } + else if (pl.Eliminated) + { + e.Mobile.SendMessage(0x22, "You have already been eliminated."); } else { - p.Nullify(pl); - pm.DuelPlayer = null; + pm.LocalOverheadMessage(MessageType.Regular, 0x22, false, "You have yielded."); + pm.NonlocalOverheadMessage(MessageType.Regular, 0x22, false, $"{pm.Name} has yielded."); - foreach (var g in init.GetGumps()) + pm.DuelContext.m_Yielding = true; + pm.Kill(); + pm.DuelContext.m_Yielding = false; + + if (pm.Alive) // invul, ... { - if (g is ParticipantGump pg && pg.Participant == p) + pl.Eliminated = true; + + pm.DuelContext.RemoveAggressions(pm); + pm.DuelContext.SendOutside(pm); + pm.DuelContext.Refresh(pm, null); + Debuff(pm); + CancelSpell(pm); + pm.Frozen = false; + + var winner = pm.DuelContext.CheckCompletion(); + + if (winner != null) { - ParticipantGump.DisplayTo(init, dc, p); - break; + pm.DuelContext.Finish(winner); } - - if (g is DuelContextGump dcg && dcg.Context == dc) - { - DuelContextGump.DisplayTo(init, dc); - break; - } - } - } - } - else if (!pm.DuelContext.StartedReadyCountdown) // at ready stage - { - p.Broadcast(0x22, null, "{0} has yielded.", "You have yielded."); - - dc.m_Yielding = true; - dc.RejectReady(pm, null); - dc.m_Yielding = false; - - if (init == pm) - { - dc.Unregister(); - } - else if (dc.Registered) - { - p.Nullify(pl); - pm.DuelPlayer = null; - - var send = true; - - foreach (var g in init.GetGumps()) - { - if (g is ParticipantGump pg && pg.Participant == p) - { - ParticipantGump.DisplayTo(init, dc, p); - send = false; - break; - } - - if (g is DuelContextGump dcg && dcg.Context == dc) - { - DuelContextGump.DisplayTo(init, dc); - send = false; - break; - } - } - - if (send) - { - DuelContextGump.DisplayTo(init, dc); } } } else { - pm.DuelContext.StopCountdown(); - pm.DuelContext.StartedReadyCountdown = false; - p.Broadcast(0x22, null, "{0} has yielded.", "You have yielded."); - - dc.m_Yielding = true; - dc.RejectReady(pm, null); - dc.m_Yielding = false; - - if (init == pm) - { - dc.Unregister(); - } - else if (dc.Registered) - { - p.Nullify(pl); - pm.DuelPlayer = null; - - var send = true; - - foreach (var g in init.GetGumps()) - { - if (g is ParticipantGump pg && pg.Participant == p) - { - ParticipantGump.DisplayTo(init, dc, p); - send = false; - break; - } - - if (g is DuelContextGump dcg && dcg.Context == dc) - { - DuelContextGump.DisplayTo(init, dc); - send = false; - break; - } - } - - if (send) - { - DuelContextGump.DisplayTo(init, dc); - } - } + e.Mobile.SendMessage(0x22, "BUG: Unable to find duel context."); } } } - else + } + + public void CloseAllGumps(DuelPlayer pl) + { + var gumps = pl.Mobile.GetGumps(); + + gumps.Close(); + gumps.Close(); + gumps.Close(); + gumps.Close(); + gumps.Close(); + gumps.Close(); + gumps.Close(); + } + + public void CloseAllGumps() + { + for (var i = 0; i < Participants.Count; ++i) { - var pl = pm.DuelContext.Find(pm); - - if (pl != null) - { - if (pm.DuelContext.IsOneVsOne) - { - e.Mobile.SendMessage(0x22, "You may not yield a 1 on 1 match."); - } - else if (pl.Eliminated) - { - e.Mobile.SendMessage(0x22, "You have already been eliminated."); - } - else - { - pm.LocalOverheadMessage(MessageType.Regular, 0x22, false, "You have yielded."); - pm.NonlocalOverheadMessage(MessageType.Regular, 0x22, false, $"{pm.Name} has yielded."); - - pm.DuelContext.m_Yielding = true; - pm.Kill(); - pm.DuelContext.m_Yielding = false; - - if (pm.Alive) // invul, ... - { - pl.Eliminated = true; - - pm.DuelContext.RemoveAggressions(pm); - pm.DuelContext.SendOutside(pm); - pm.DuelContext.Refresh(pm, null); - Debuff(pm); - CancelSpell(pm); - pm.Frozen = false; - - var winner = pm.DuelContext.CheckCompletion(); - - if (winner != null) - { - pm.DuelContext.Finish(winner); - } - } - } - } - else - { - e.Mobile.SendMessage(0x22, "BUG: Unable to find duel context."); - } - } - } - } - - public void CloseAllGumps(DuelPlayer pl) - { - var gumps = pl.Mobile.GetGumps(); - - gumps.Close(); - gumps.Close(); - gumps.Close(); - gumps.Close(); - gumps.Close(); - gumps.Close(); - gumps.Close(); - } - - public void CloseAllGumps() - { - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - if (pl != null) - { - CloseAllGumps(pl); - } - } - } - } - - public void RejectReady(Mobile rejector, string page) - { - if (StartedReadyCountdown) - { - return; // sanity - } - - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - if (pl == null) - { - continue; - } - - pl.Ready = false; - - var mob = pl.Mobile; - - if (page == null) // yield - { - if (mob != rejector) - { - mob.SendMessage(0x22, $"{rejector.Name} has yielded."); - } - } - else - { - if (mob == rejector) - { - if (Rematch) - { - mob.SendMessage(0x22, "You have rejected the rematch."); - } - else - { - mob.SendMessage(0x22, $"You have rejected the {page}."); - } - } - else - { - if (Rematch) - { - mob.SendMessage(0x22, $"{rejector.Name} has rejected the rematch."); - } - else - { - mob.SendMessage(0x22, $"{rejector.Name} has rejected the {page}."); - } - } - } - - // Close all of them? - var gumps = mob.GetGumps(); - - gumps.Close(); - gumps.Close(); - gumps.Close(); - } - } - - if (Rematch) - { - Unregister(); - } - else if (!m_Yielding) - { - DuelContextGump.DisplayTo(Initiator, this); - } - - ReadyWait = false; - ReadyCount = 0; - } - - public void SendReadyGump() - { - SendReadyGump(-1); - } - - public static void Debuff(Mobile mob) - { - mob.RemoveStatMod("[Magic] Str Buff"); - mob.RemoveStatMod("[Magic] Dex Buff"); - mob.RemoveStatMod("[Magic] Int Buff"); - mob.RemoveStatMod("[Magic] Str Curse"); - mob.RemoveStatMod("[Magic] Dex Curse"); - mob.RemoveStatMod("[Magic] Int Curse"); - mob.RemoveStatMod("Concussion"); - mob.RemoveStatMod("blood-rose"); - mob.RemoveStatMod("clarity-potion"); - mob.RemoveStatMod("RoseOfTrinsicPetal"); - mob.RemoveStatMod("Holy Bless"); - mob.RemoveStatMod("Holy Curse"); - - OrangePetals.RemoveEffect(mob); - - mob.Paralyzed = false; - mob.Hidden = false; - - if (!Core.AOS) - { - if (!Core.UOR) - { - ReactiveArmorSpell.RemoveEffect(mob); - ProtectionSpell.RemoveT2AProtection(mob); - } - - mob.MagicDamageAbsorb = 0; - mob.MeleeDamageAbsorb = 0; - ProtectionSpell.Registry.Remove(mob); - - ArchProtectionSpell.RemoveEntry(mob); - - mob.EndAction(); - } - - TransformationSpellHelper.RemoveContext(mob, true); - AnimalForm.RemoveContext(mob); - DisguisePersistence.StopTimer(mob); - - if (!mob.CanBeginAction()) - { - mob.BodyMod = 0; - mob.HueMod = -1; - mob.EndAction(); - } - - BaseArmor.ValidateMobile(mob); - BaseClothing.ValidateMobile(mob); - - mob.Hits = mob.HitsMax; - mob.Stam = mob.StamMax; - mob.Mana = mob.ManaMax; - - mob.Poison = null; - } - - public static void CancelSpell(Mobile mob) - { - if (mob.Spell is Spell spell) - { - spell.Disturb(DisturbType.Kill); - } - - Target.Cancel(mob); - } - - public void DestroyWall() - { - for (var i = 0; i < m_Walls.Count; ++i) - { - m_Walls[i].Delete(); - } - - m_Walls.Clear(); - } - - public void CreateWall() - { - if (Arena == null) - { - return; - } - - var start = Arena.Points.EdgeWest; - var wall = Arena.Wall; - - var dx = start.X - wall.X; - var dy = start.Y - wall.Y; - var rx = dx - dy; - var ry = dx + dy; - - var eastToWest = rx == 0 && ry >= 0 || rx >= 0 && ry == 0; - - Effects.PlaySound(wall, Arena.Facet, 0x1F6); - - for (var i = -1; i <= 1; ++i) - { - var loc = new Point3D(eastToWest ? wall.X + i : wall.X, eastToWest ? wall.Y : wall.Y + i, wall.Z); - - var created = new InternalWall(); - - created.Appear(loc, Arena.Facet); - - m_Walls.Add(created); - } - } - - public void BuildParties() - { - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - if (p.Players.Length > 1) - { - var players = new List(); + var p = Participants[i]; for (var j = 0; j < p.Players.Length; ++j) { - var dp = p.Players[j]; + var pl = p.Players[j]; - if (dp == null) + if (pl != null) + { + CloseAllGumps(pl); + } + } + } + } + + public void RejectReady(Mobile rejector, string page) + { + if (StartedReadyCountdown) + { + return; // sanity + } + + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + if (pl == null) { continue; } - players.Add(dp.Mobile); - } + pl.Ready = false; - if (players.Count > 1) - { - for (var leaderIndex = 0; leaderIndex + 1 < players.Count; leaderIndex += Party.Capacity) + var mob = pl.Mobile; + + if (page == null) // yield { - var leader = players[leaderIndex]; - var party = Party.Get(leader); - - if (party == null) + if (mob != rejector) { - leader.Party = party = new Party(leader); + mob.SendMessage(0x22, $"{rejector.Name} has yielded."); } - else if (party.Leader != leader) + } + else + { + if (mob == rejector) { - party.SendPublicMessage(leader, "I leave this party to fight in a duel."); - party.Remove(leader); - leader.Party = party = new Party(leader); - } - - for (var j = leaderIndex + 1; j < players.Count && j < leaderIndex + Party.Capacity; ++j) - { - var player = players[j]; - var existing = Party.Get(player); - - if (existing == party) + if (Rematch) { - continue; - } - - if (party.Members.Count + party.Candidates.Count >= Party.Capacity) - { - player.SendMessage( - "You could not be added to the team party because it is at full capacity." - ); - leader.SendMessage( - "{0} could not be added to the team party because it is at full capacity." - ); + mob.SendMessage(0x22, $"You have rejected the rematch."); } else { - if (existing != null) - { - existing.SendPublicMessage(player, "I leave this party to fight in a duel."); - existing.Remove(player); - } - - party.OnAccept(player, true); + mob.SendMessage(0x22, $"You have rejected the {page}."); + } + } + else + { + if (Rematch) + { + mob.SendMessage(0x22, $"{rejector.Name} has rejected the rematch."); + } + else + { + mob.SendMessage(0x22, $"{rejector.Name} has rejected the {page}."); } } } + + // Close all of them? + var gumps = mob.GetGumps(); + + gumps.Close(); + gumps.Close(); + gumps.Close(); } } - } - } - public void ClearIllegalItems() - { - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) + if (Rematch) { - var pl = p.Players[j]; - - if (pl == null) - { - continue; - } - - ClearIllegalItems(pl.Mobile); + Unregister(); } - } - } - - public void ClearIllegalItems(Mobile mob) - { - if (mob.StunReady && !AllowSpecialAbility(mob, "Stun", false)) - { - mob.StunReady = false; - } - - if (mob.DisarmReady && !AllowSpecialAbility(mob, "Disarm", false)) - { - mob.DisarmReady = false; - } - - var pack = mob.Backpack; - - if (pack == null) - { - return; - } - - for (var i = mob.Items.Count - 1; i >= 0; --i) - { - if (i >= mob.Items.Count) + else if (!m_Yielding) { - continue; // sanity + Initiator.SendGump(new DuelContextGump(Initiator, this)); } - var item = mob.Items[i]; + ReadyWait = false; + ReadyCount = 0; + } - if (!CheckItemEquip(mob, item)) + public void SendReadyGump() + { + SendReadyGump(-1); + } + + public static void Debuff(Mobile mob) + { + mob.RemoveStatMod("[Magic] Str Buff"); + mob.RemoveStatMod("[Magic] Dex Buff"); + mob.RemoveStatMod("[Magic] Int Buff"); + mob.RemoveStatMod("[Magic] Str Curse"); + mob.RemoveStatMod("[Magic] Dex Curse"); + mob.RemoveStatMod("[Magic] Int Curse"); + mob.RemoveStatMod("Concussion"); + mob.RemoveStatMod("blood-rose"); + mob.RemoveStatMod("clarity-potion"); + mob.RemoveStatMod("RoseOfTrinsicPetal"); + mob.RemoveStatMod("Holy Bless"); + mob.RemoveStatMod("Holy Curse"); + + OrangePetals.RemoveEffect(mob); + + mob.Paralyzed = false; + mob.Hidden = false; + + if (!Core.AOS) { - pack.DropItem(item); + mob.MagicDamageAbsorb = 0; + mob.MeleeDamageAbsorb = 0; + ProtectionSpell.Registry.Remove(mob); - // TODO: Use Layer instead of Type - var number = item switch - { - BaseWeapon _ => 1062001, // You can no longer wield your ~1_WEAPON~ - not BaseShield when item is BaseArmor or BaseClothing => 1062002, // You can no longer wear your ~1_ARMOR~ - _ => 1062003 // You can no longer equip your ~1_SHIELD~ - }; + ArchProtectionSpell.RemoveEntry(mob); - mob.SendLocalizedMessage(number, item.Name ?? $"#{item.LabelNumber}"); + mob.EndAction(); + } + + TransformationSpellHelper.RemoveContext(mob, true); + AnimalForm.RemoveContext(mob); + DisguisePersistence.StopTimer(mob); + + if (!mob.CanBeginAction()) + { + mob.BodyMod = 0; + mob.HueMod = -1; + mob.EndAction(); + } + + BaseArmor.ValidateMobile(mob); + BaseClothing.ValidateMobile(mob); + + mob.Hits = mob.HitsMax; + mob.Stam = mob.StamMax; + mob.Mana = mob.ManaMax; + + mob.Poison = null; + } + + public static void CancelSpell(Mobile mob) + { + if (mob.Spell is Spell spell) + { + spell.Disturb(DisturbType.Kill); + } + + Target.Cancel(mob); + } + + public void DestroyWall() + { + for (var i = 0; i < m_Walls.Count; ++i) + { + m_Walls[i].Delete(); + } + + m_Walls.Clear(); + } + + public void CreateWall() + { + if (Arena == null) + { + return; + } + + var start = Arena.Points.EdgeWest; + var wall = Arena.Wall; + + var dx = start.X - wall.X; + var dy = start.Y - wall.Y; + var rx = dx - dy; + var ry = dx + dy; + + bool eastToWest = rx == 0 && ry >= 0 || rx >= 0 && ry == 0; + + Effects.PlaySound(wall, Arena.Facet, 0x1F6); + + for (var i = -1; i <= 1; ++i) + { + var loc = new Point3D(eastToWest ? wall.X + i : wall.X, eastToWest ? wall.Y : wall.Y + i, wall.Z); + + var created = new InternalWall(); + + created.Appear(loc, Arena.Facet); + + m_Walls.Add(created); } } - var inHand = mob.Holding; - - if (inHand != null && !CheckItemEquip(mob, inHand)) + public void BuildParties() { - mob.Holding = null; - - var bi = inHand.GetBounce(); - - if (bi.Parent == mob) + for (var i = 0; i < Participants.Count; ++i) { - pack.DropItem(inHand); - } - else - { - inHand.Bounce(mob); - } + var p = Participants[i]; - inHand.ClearBounce(); - } - } - - private void MessageRuleset(Mobile mob) - { - if (Ruleset == null) - { - return; - } - - var ruleset = Ruleset; - var basedef = ruleset.Base; - - mob.SendMessage($"Ruleset: {basedef.Title}"); - - BitArray defs; - - if (ruleset.Flavors.Count > 0) - { - defs = new BitArray(basedef.Options); - - for (var i = 0; i < ruleset.Flavors.Count; ++i) - { - defs.Or(ruleset.Flavors[i].Options); - - mob.SendMessage($" + {ruleset.Flavors[i].Title}"); - } - } - else - { - defs = basedef.Options; - } - - var changes = 0; - - var opts = ruleset.Options; - - for (var i = 0; i < opts.Length; ++i) - { - if (defs[i] != opts[i]) - { - var name = ruleset.Layout.FindByIndex(i); - - if (name != null) // sanity + if (p.Players.Length > 1) { - ++changes; - - if (changes == 1) - { - mob.SendMessage("Modifications:"); - } - - if (opts[i]) - { - mob.SendMessage($"{name}: enabled"); - } - else - { - mob.SendMessage($"{name}: disabled"); - } - } - } - } - } - - public void SendBeginGump(int count) - { - if (!Registered || Finished) - { - return; - } - - if (count == 10) - { - CreateWall(); - BuildParties(); - ClearIllegalItems(); - } - else if (count == 0) - { - DestroyWall(); - } - - StartedBeginCountdown = true; - - if (count == 0) - { - Started = true; - BeginAutoTie(); - } - - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - if (pl == null) - { - continue; - } - - var mob = pl.Mobile; - - if (count > 0) - { - if (count == 10) - { - var gumps = mob.GetGumps(); - - gumps.Close(); - gumps.Close(); - gumps.Close(); - BeginGump.DisplayTo(mob, count); - } - - mob.Frozen = true; - } - else - { - mob.CloseGump(); - mob.Frozen = false; - } - } - } - } - - public void RemoveAggressions(Mobile mob) - { - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var dp = p.Players[j]; - - if (dp == null || dp.Mobile == mob) - { - continue; - } - - mob.RemoveAggressed(dp.Mobile); - mob.RemoveAggressor(dp.Mobile); - dp.Mobile.RemoveAggressed(mob); - dp.Mobile.RemoveAggressor(mob); - } - } - } - - public void SendReadyUpGump() - { - if (!Registered) - { - return; - } - - ReadyWait = true; - ReadyCount = -1; - - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - var mob = pl?.Mobile; - - if (mob != null && m_Tournament == null) - { - ReadyUpGump.DisplayTo(mob, this); - } - } - } - } - - public string ValidateStart() - { - if (m_Tournament == null && TournamentController.IsActive) - { - return "a tournament is active"; - } - - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var dp = p.Players[j]; - - if (dp == null) - { - return "a slot is empty"; - } - - if (dp.Mobile.Region.IsPartOf()) - { - return $"{dp.Mobile.Name} is in jail"; - } - - if (Sigil.ExistsOn(dp.Mobile)) - { - return $"{dp.Mobile.Name} is holding a sigil"; - } - - if (!dp.Mobile.Alive) - { - if (m_Tournament == null) - { - return $"{dp.Mobile.Name} is dead"; - } - - dp.Mobile.Resurrect(); - } - - if (m_Tournament == null && CheckCombat(dp.Mobile)) - { - return $"{dp.Mobile.Name} is in combat"; - } - - if (dp.Mobile.Mounted) - { - var mount = dp.Mobile.Mount; - - if (m_Tournament != null && mount != null) - { - mount.Rider = null; - } - else - { - return $"{dp.Mobile.Name} is mounted"; - } - } - } - } - - return null; - } - - public void SendReadyGump(int count) - { - if (!Registered) - { - return; - } - - if (count != -1) - { - StartedReadyCountdown = true; - } - - ReadyCount = count; - - if (count == 0) - { - var error = ValidateStart(); - - if (error != null) - { - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; + var players = new List(); for (var j = 0; j < p.Players.Length; ++j) { var dp = p.Players[j]; - dp?.Mobile.SendMessage($"The duel could not be started because {error}."); + if (dp == null) + { + continue; + } + + players.Add(dp.Mobile); + } + + if (players.Count > 1) + { + for (var leaderIndex = 0; leaderIndex + 1 < players.Count; leaderIndex += Party.Capacity) + { + var leader = players[leaderIndex]; + var party = Party.Get(leader); + + if (party == null) + { + leader.Party = party = new Party(leader); + } + else if (party.Leader != leader) + { + party.SendPublicMessage(leader, "I leave this party to fight in a duel."); + party.Remove(leader); + leader.Party = party = new Party(leader); + } + + for (var j = leaderIndex + 1; j < players.Count && j < leaderIndex + Party.Capacity; ++j) + { + var player = players[j]; + var existing = Party.Get(player); + + if (existing == party) + { + continue; + } + + if (party.Members.Count + party.Candidates.Count >= Party.Capacity) + { + player.SendMessage( + "You could not be added to the team party because it is at full capacity." + ); + leader.SendMessage( + "{0} could not be added to the team party because it is at full capacity." + ); + } + else + { + if (existing != null) + { + existing.SendPublicMessage(player, "I leave this party to fight in a duel."); + existing.Remove(player); + } + + party.OnAccept(player, true); + } + } + } } } + } + } - StartCountdown(10, SendReadyGump); + public void ClearIllegalItems() + { + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + if (pl == null) + { + continue; + } + + ClearIllegalItems(pl.Mobile); + } + } + } + + public void ClearIllegalItems(Mobile mob) + { + if (mob.StunReady && !AllowSpecialAbility(mob, "Stun", false)) + { + mob.StunReady = false; + } + + if (mob.DisarmReady && !AllowSpecialAbility(mob, "Disarm", false)) + { + mob.DisarmReady = false; + } + + var pack = mob.Backpack; + + if (pack == null) + { return; } - ReadyWait = false; + for (var i = mob.Items.Count - 1; i >= 0; --i) + { + if (i >= mob.Items.Count) + { + continue; // sanity + } - var players = new List(); + var item = mob.Items[i]; + + if (!CheckItemEquip(mob, item)) + { + pack.DropItem(item); + + // TODO: Use Layer instead of Type + int number = item switch + { + BaseWeapon _ => 1062001, // You can no longer wield your ~1_WEAPON~ + not BaseShield when item is BaseArmor or BaseClothing => 1062002, // You can no longer wear your ~1_ARMOR~ + _ => 1062003 // You can no longer equip your ~1_SHIELD~ + }; + + mob.SendLocalizedMessage(number, item.Name ?? $"#{item.LabelNumber}"); + } + } + + var inHand = mob.Holding; + + if (inHand != null && !CheckItemEquip(mob, inHand)) + { + mob.Holding = null; + + var bi = inHand.GetBounce(); + + if (bi.Parent == mob) + { + pack.DropItem(inHand); + } + else + { + inHand.Bounce(mob); + } + + inHand.ClearBounce(); + } + } + + private void MessageRuleset(Mobile mob) + { + if (Ruleset == null) + { + return; + } + + var ruleset = Ruleset; + var basedef = ruleset.Base; + + mob.SendMessage($"Ruleset: {basedef.Title}"); + + BitArray defs; + + if (ruleset.Flavors.Count > 0) + { + defs = new BitArray(basedef.Options); + + for (var i = 0; i < ruleset.Flavors.Count; ++i) + { + defs.Or(ruleset.Flavors[i].Options); + + mob.SendMessage($" + {ruleset.Flavors[i].Title}"); + } + } + else + { + defs = basedef.Options; + } + + var changes = 0; + + var opts = ruleset.Options; + + for (var i = 0; i < opts.Length; ++i) + { + if (defs[i] != opts[i]) + { + var name = ruleset.Layout.FindByIndex(i); + + if (name != null) // sanity + { + ++changes; + + if (changes == 1) + { + mob.SendMessage("Modifications:"); + } + + if (opts[i]) + { + mob.SendMessage($"{name}: enabled"); + } + else + { + mob.SendMessage($"{name}: disabled"); + } + } + } + } + } + + public void SendBeginGump(int count) + { + if (!Registered || Finished) + { + return; + } + + if (count == 10) + { + CreateWall(); + BuildParties(); + ClearIllegalItems(); + } + else if (count == 0) + { + DestroyWall(); + } + + StartedBeginCountdown = true; + + if (count == 0) + { + Started = true; + BeginAutoTie(); + } + + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + if (pl == null) + { + continue; + } + + var mob = pl.Mobile; + + if (count > 0) + { + if (count == 10) + { + var gumps = mob.GetGumps(); + + gumps.Close(); + gumps.Close(); + gumps.Close(); + gumps.Send(new BeginGump(count)); + } + + mob.Frozen = true; + } + else + { + mob.CloseGump(); + mob.Frozen = false; + } + } + } + } + + public void RemoveAggressions(Mobile mob) + { + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var dp = p.Players[j]; + + if (dp == null || dp.Mobile == mob) + { + continue; + } + + mob.RemoveAggressed(dp.Mobile); + mob.RemoveAggressor(dp.Mobile); + dp.Mobile.RemoveAggressed(mob); + dp.Mobile.RemoveAggressor(mob); + } + } + } + + public void SendReadyUpGump() + { + if (!Registered) + { + return; + } + + ReadyWait = true; + ReadyCount = -1; + + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + var mob = pl?.Mobile; + + if (mob != null && m_Tournament == null) + { + mob.SendGump(new ReadyUpGump(mob, this), true); + } + } + } + } + + public string ValidateStart() + { + if (m_Tournament == null && TournamentController.IsActive) + { + return "a tournament is active"; + } for (var i = 0; i < Participants.Count; ++i) { @@ -2385,429 +2267,562 @@ public partial class DuelContext { var dp = p.Players[j]; - if (dp != null) + if (dp == null) { - players.Add(dp.Mobile); + return "a slot is empty"; } - } - } - var arena = m_OverrideArena ?? Arena.FindArena(players); - - if (arena == null) - { - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) + if (dp.Mobile.Region.IsPartOf()) { - var dp = p.Players[j]; - - dp?.Mobile.SendMessage( - "The duel could not be started because there are no arenas. If you want to stop waiting for a free arena, yield the duel." - ); + return $"{dp.Mobile.Name} is in jail"; } - } - StartCountdown(10, SendReadyGump); - return; - } - - if (!arena.IsOccupied) - { - Arena = arena; - - if (Initiator.Map == Map.Internal) - { - m_GatePoint = Initiator.LogoutLocation; - m_GateFacet = Initiator.LogoutMap; - } - else - { - m_GatePoint = Initiator.Location; - m_GateFacet = Initiator.Map; - } - - if (arena.Teleporter is not ExitTeleporter tp) - { - arena.Teleporter = tp = new ExitTeleporter(); - tp.MoveToWorld(arena.GateOut == Point3D.Zero ? arena.Outside : arena.GateOut, arena.Facet); - } - - var mg = new ArenaMoongate( - arena.GateIn == Point3D.Zero ? arena.Outside : arena.GateIn, - arena.Facet, - tp - ); - - StartedBeginCountdown = true; - - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) + if (Sigil.ExistsOn(dp.Mobile)) { - var pl = p.Players[j]; + return $"{dp.Mobile.Name} is holding a sigil"; + } - if (pl == null) + if (!dp.Mobile.Alive) + { + if (m_Tournament == null) { - continue; + return $"{dp.Mobile.Name} is dead"; } - tp.Register(pl.Mobile); - - pl.Mobile.Frozen = false; // reset timer just in case - pl.Mobile.Frozen = true; - - Debuff(pl.Mobile); - CancelSpell(pl.Mobile); - - pl.Mobile.Delta(MobileDelta.Noto); + dp.Mobile.Resurrect(); } - arena.MoveInside(p.Players, i); - } - - m_EventGame?.OnStart(); - - StartCountdown(10, SendBeginGump); - - mg.Appear(m_GatePoint, m_GateFacet); - } - else - { - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) + if (m_Tournament == null && CheckCombat(dp.Mobile)) { - var dp = p.Players[j]; - - dp?.Mobile.SendMessage( - "The duel could not be started because all arenas are full. If you want to stop waiting for a free arena, yield the duel." - ); + return $"{dp.Mobile.Name} is in combat"; } - } - StartCountdown(10, SendReadyGump); - } - - return; - } - - ReadyWait = true; - - var isAllReady = true; - - for (var i = 0; i < Participants.Count; ++i) - { - var p = Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - if (pl == null) - { - continue; - } - - var mob = pl.Mobile; - - if (pl.Ready) - { - if (m_Tournament == null) + if (dp.Mobile.Mounted) { - ReadyGump.DisplayTo(mob, this, count); + var mount = dp.Mobile.Mount; + + if (m_Tournament != null && mount != null) + { + mount.Rider = null; + } + else + { + return $"{dp.Mobile.Name} is mounted"; + } } } - else - { - isAllReady = false; - } - } - } - - if (count == -1 && isAllReady) - { - StartCountdown(3, SendReadyGump); - } - } - - [SerializationGenerator(0, false)] - private partial class InternalWall : Item - { - public InternalWall() : base(0x80) => Movable = false; - - public void Appear(Point3D loc, Map map) - { - MoveToWorld(loc, map); - - Effects.SendLocationParticles(this, 0x376A, 9, 10, 5025); - } - - public override bool SkipSerialization => true; - } - - private class ReturnEntry - { - private DateTime m_Expire; - - public ReturnEntry(Mobile mob) - { - Mobile = mob; - - Update(); - } - - public ReturnEntry(Mobile mob, Point3D loc, Map facet) - { - Mobile = mob; - Location = loc; - Facet = facet; - m_Expire = Core.Now + TimeSpan.FromMinutes(30.0); - } - - public Mobile Mobile { get; } - - public Point3D Location { get; private set; } - - public Map Facet { get; private set; } - - public bool Expired => Core.Now >= m_Expire; - - public void Return() - { - if (Facet == Map.Internal || Facet == null) - { - return; - } - - if (Mobile.Map == Map.Internal) - { - Mobile.LogoutLocation = Location; - Mobile.LogoutMap = Facet; - } - else - { - Mobile.Location = Location; - Mobile.Map = Facet; - } - } - - public void Update() - { - m_Expire = Core.Now + TimeSpan.FromMinutes(30.0); - - if (Mobile.Map == Map.Internal) - { - Facet = Mobile.LogoutMap; - Location = Mobile.LogoutLocation; - } - else - { - Facet = Mobile.Map; - Location = Mobile.Location; - } - } - } - - private class ExitTeleporter : Item - { - private List m_Entries; - - public ExitTeleporter() : base(0x1822) - { - m_Entries = new List(); - - Hue = 0x482; - Movable = false; - } - - public ExitTeleporter(Serial serial) : base(serial) - { - } - - public override string DefaultName => "return teleporter"; - - public void Register(Mobile mob) - { - var entry = Find(mob); - - if (entry != null) - { - entry.Update(); - return; - } - - m_Entries.Add(new ReturnEntry(mob)); - } - - private ReturnEntry Find(Mobile mob) - { - for (var i = 0; i < m_Entries.Count; ++i) - { - var entry = m_Entries[i]; - - if (entry.Mobile == mob) - { - return entry; - } - - if (entry.Expired) - { - m_Entries.RemoveAt(i--); - } } return null; } - public override bool OnMoveOver(Mobile m) + public void SendReadyGump(int count) { - if (!base.OnMoveOver(m)) + if (!Registered) { - return false; + return; } - var entry = Find(m); - - if (entry != null) + if (count != -1) { - entry.Return(); - - Effects.PlaySound(GetWorldLocation(), Map, 0x1FE); - Effects.PlaySound(m.Location, m.Map, 0x1FE); - - m_Entries.Remove(entry); - - return false; + StartedReadyCountdown = true; } - m.SendLocalizedMessage(1049383); // The teleporter doesn't seem to work for you. - return true; - } + ReadyCount = count; - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - - writer.WriteEncodedInt(m_Entries.Count); - - for (var i = 0; i < m_Entries.Count; ++i) + if (count == 0) { - var entry = m_Entries[i]; + var error = ValidateStart(); - writer.Write(entry.Mobile); - writer.Write(entry.Location); - writer.Write(entry.Facet); - - if (entry.Expired) + if (error != null) { - m_Entries.RemoveAt(i--); - } - } - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 0: + for (var i = 0; i < Participants.Count; ++i) { - var count = reader.ReadEncodedInt(); + var p = Participants[i]; - m_Entries = new List(count); - - for (var i = 0; i < count; ++i) + for (var j = 0; j < p.Players.Length; ++j) { - var mob = reader.ReadEntity(); - var loc = reader.ReadPoint3D(); - var map = reader.ReadMap(); + var dp = p.Players[j]; - m_Entries.Add(new ReturnEntry(mob, loc, map)); + dp?.Mobile.SendMessage($"The duel could not be started because {error}."); + } + } + + StartCountdown(10, SendReadyGump); + + return; + } + + ReadyWait = false; + + var players = new List(); + + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var dp = p.Players[j]; + + if (dp != null) + { + players.Add(dp.Mobile); + } + } + } + + var arena = m_OverrideArena ?? Arena.FindArena(players); + + if (arena == null) + { + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var dp = p.Players[j]; + + dp?.Mobile.SendMessage( + "The duel could not be started because there are no arenas. If you want to stop waiting for a free arena, yield the duel." + ); + } + } + + StartCountdown(10, SendReadyGump); + return; + } + + if (!arena.IsOccupied) + { + Arena = arena; + + if (Initiator.Map == Map.Internal) + { + m_GatePoint = Initiator.LogoutLocation; + m_GateFacet = Initiator.LogoutMap; + } + else + { + m_GatePoint = Initiator.Location; + m_GateFacet = Initiator.Map; + } + + if (arena.Teleporter is not ExitTeleporter tp) + { + arena.Teleporter = tp = new ExitTeleporter(); + tp.MoveToWorld(arena.GateOut == Point3D.Zero ? arena.Outside : arena.GateOut, arena.Facet); + } + + var mg = new ArenaMoongate( + arena.GateIn == Point3D.Zero ? arena.Outside : arena.GateIn, + arena.Facet, + tp + ); + + StartedBeginCountdown = true; + + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + if (pl == null) + { + continue; + } + + tp.Register(pl.Mobile); + + pl.Mobile.Frozen = false; // reset timer just in case + pl.Mobile.Frozen = true; + + Debuff(pl.Mobile); + CancelSpell(pl.Mobile); + + pl.Mobile.Delta(MobileDelta.Noto); } - break; + arena.MoveInside(p.Players, i); } - } - } - } - [SerializationGenerator(0, false)] - private partial class ArenaMoongate : ConfirmationMoongate - { - private readonly ExitTeleporter m_Teleporter; + m_EventGame?.OnStart(); - public ArenaMoongate(Point3D target, Map map, ExitTeleporter tp) : base(target, map) - { - m_Teleporter = tp; + StartCountdown(10, SendBeginGump); - ItemID = 0x1FD4; - Dispellable = false; - - GumpWidth = 300; - GumpHeight = 150; - MessageColor = 0xFFC000; - Message = "Are you sure you wish to spectate this duel?"; - TitleColor = 0x7800; - TitleNumber = 1062051; // Gate Warning - - Timer.StartTimer(TimeSpan.FromSeconds(10.0), Delete); - } - - public override bool SkipSerialization => true; - - public override string DefaultName => "spectator moongate"; - - public override void CheckGate(Mobile m, int range) - { - if (CheckCombat(m)) - { - m.SendMessage( - 0x22, - "You have recently been in combat with another player and cannot use this moongate." - ); - } - else - { - base.CheckGate(m, range); - } - } - - public override void UseGate(Mobile m) - { - if (CheckCombat(m)) - { - m.SendMessage( - 0x22, - "You have recently been in combat with another player and cannot use this moongate." - ); - } - else - { - if (m_Teleporter?.Deleted == false) + mg.Appear(m_GatePoint, m_GateFacet); + } + else { - m_Teleporter.Register(m); + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var dp = p.Players[j]; + + dp?.Mobile.SendMessage( + "The duel could not be started because all arenas are full. If you want to stop waiting for a free arena, yield the duel." + ); + } + } + + StartCountdown(10, SendReadyGump); } - base.UseGate(m); + return; + } + + ReadyWait = true; + + var isAllReady = true; + + for (var i = 0; i < Participants.Count; ++i) + { + var p = Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + if (pl == null) + { + continue; + } + + var mob = pl.Mobile; + + if (pl.Ready) + { + if (m_Tournament == null) + { + mob.SendGump(new ReadyGump(mob, this, count), true); + } + } + else + { + isAllReady = false; + } + } + } + + if (count == -1 && isAllReady) + { + StartCountdown(3, SendReadyGump); } } - public void Appear(Point3D loc, Map map) + private class InternalWall : Item { - Effects.PlaySound(loc, map, 0x20E); - MoveToWorld(loc, map); + public InternalWall() : base(0x80) => Movable = false; + + public InternalWall(Serial serial) : base(serial) + { + } + + public void Appear(Point3D loc, Map map) + { + MoveToWorld(loc, map); + + Effects.SendLocationParticles(this, 0x376A, 9, 10, 5025); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + Delete(); + } + } + + private class ReturnEntry + { + private DateTime m_Expire; + + public ReturnEntry(Mobile mob) + { + Mobile = mob; + + Update(); + } + + public ReturnEntry(Mobile mob, Point3D loc, Map facet) + { + Mobile = mob; + Location = loc; + Facet = facet; + m_Expire = Core.Now + TimeSpan.FromMinutes(30.0); + } + + public Mobile Mobile { get; } + + public Point3D Location { get; private set; } + + public Map Facet { get; private set; } + + public bool Expired => Core.Now >= m_Expire; + + public void Return() + { + if (Facet == Map.Internal || Facet == null) + { + return; + } + + if (Mobile.Map == Map.Internal) + { + Mobile.LogoutLocation = Location; + Mobile.LogoutMap = Facet; + } + else + { + Mobile.Location = Location; + Mobile.Map = Facet; + } + } + + public void Update() + { + m_Expire = Core.Now + TimeSpan.FromMinutes(30.0); + + if (Mobile.Map == Map.Internal) + { + Facet = Mobile.LogoutMap; + Location = Mobile.LogoutLocation; + } + else + { + Facet = Mobile.Map; + Location = Mobile.Location; + } + } + } + + private class ExitTeleporter : Item + { + private List m_Entries; + + public ExitTeleporter() : base(0x1822) + { + m_Entries = new List(); + + Hue = 0x482; + Movable = false; + } + + public ExitTeleporter(Serial serial) : base(serial) + { + } + + public override string DefaultName => "return teleporter"; + + public void Register(Mobile mob) + { + var entry = Find(mob); + + if (entry != null) + { + entry.Update(); + return; + } + + m_Entries.Add(new ReturnEntry(mob)); + } + + private ReturnEntry Find(Mobile mob) + { + for (var i = 0; i < m_Entries.Count; ++i) + { + var entry = m_Entries[i]; + + if (entry.Mobile == mob) + { + return entry; + } + + if (entry.Expired) + { + m_Entries.RemoveAt(i--); + } + } + + return null; + } + + public override bool OnMoveOver(Mobile m) + { + if (!base.OnMoveOver(m)) + { + return false; + } + + var entry = Find(m); + + if (entry != null) + { + entry.Return(); + + Effects.PlaySound(GetWorldLocation(), Map, 0x1FE); + Effects.PlaySound(m.Location, m.Map, 0x1FE); + + m_Entries.Remove(entry); + + return false; + } + + m.SendLocalizedMessage(1049383); // The teleporter doesn't seem to work for you. + return true; + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + + writer.WriteEncodedInt(m_Entries.Count); + + for (var i = 0; i < m_Entries.Count; ++i) + { + var entry = m_Entries[i]; + + writer.Write(entry.Mobile); + writer.Write(entry.Location); + writer.Write(entry.Facet); + + if (entry.Expired) + { + m_Entries.RemoveAt(i--); + } + } + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: + { + var count = reader.ReadEncodedInt(); + + m_Entries = new List(count); + + for (var i = 0; i < count; ++i) + { + var mob = reader.ReadEntity(); + var loc = reader.ReadPoint3D(); + var map = reader.ReadMap(); + + m_Entries.Add(new ReturnEntry(mob, loc, map)); + } + + break; + } + } + } + } + + private class ArenaMoongate : ConfirmationMoongate + { + private readonly ExitTeleporter m_Teleporter; + + public ArenaMoongate(Point3D target, Map map, ExitTeleporter tp) : base(target, map) + { + m_Teleporter = tp; + + ItemID = 0x1FD4; + Dispellable = false; + + GumpWidth = 300; + GumpHeight = 150; + MessageColor = 0xFFC000; + Message = "Are you sure you wish to spectate this duel?"; + TitleColor = 0x7800; + TitleNumber = 1062051; // Gate Warning + + Timer.StartTimer(TimeSpan.FromSeconds(10.0), Delete); + } + + public ArenaMoongate(Serial serial) : base(serial) + { + } + + public override string DefaultName => "spectator moongate"; + + public override void CheckGate(Mobile m, int range) + { + if (CheckCombat(m)) + { + m.SendMessage( + 0x22, + "You have recently been in combat with another player and cannot use this moongate." + ); + } + else + { + base.CheckGate(m, range); + } + } + + public override void UseGate(Mobile m) + { + if (CheckCombat(m)) + { + m.SendMessage( + 0x22, + "You have recently been in combat with another player and cannot use this moongate." + ); + } + else + { + if (m_Teleporter?.Deleted == false) + { + m_Teleporter.Register(m); + } + + base.UseGate(m); + } + } + + public void Appear(Point3D loc, Map map) + { + Effects.PlaySound(loc, map, 0x20E); + MoveToWorld(loc, map); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + Delete(); + } } } } diff --git a/Projects/UOContent/Engines/ConPVP/DuelTeleporterAddon.cs b/Projects/UOContent/Engines/ConPVP/DuelTeleporterAddon.cs index ca92d598b..a8761cd34 100644 --- a/Projects/UOContent/Engines/ConPVP/DuelTeleporterAddon.cs +++ b/Projects/UOContent/Engines/ConPVP/DuelTeleporterAddon.cs @@ -1,68 +1,93 @@ -using ModernUO.Serialization; using Server.Items; -namespace Server.Engines.ConPVP; - -public enum DuelTeleporterType +namespace Server.Engines.ConPVP { - Squares = 6095, - Buds = 6104, - Flowers = 6113, - Spikes = 6122, - Arrows = 6140, - Links = 6149 -} - -[SerializationGenerator(0, false)] -public partial class DuelTeleporterAddon : BaseAddon -{ - [Constructible] - public DuelTeleporterAddon(DuelTeleporterType type = DuelTeleporterType.Squares) + public enum DuelTeleporterType { - var itemID = (int)type; - - AddComponent(new AddonComponent(itemID + 0), -1, -1, 5); - AddComponent(new AddonComponent(itemID + 1), -1, 0, 5); - AddComponent(new AddonComponent(itemID + 2), 0, -1, 5); - AddComponent(new AddonComponent(itemID + 3), -1, +1, 5); - AddComponent(new AddonComponent(itemID + 4), 0, 0, 5); - AddComponent(new AddonComponent(itemID + 5), +1, -1, 5); - AddComponent(new AddonComponent(itemID + 6), 0, +1, 5); - AddComponent(new AddonComponent(itemID + 7), +1, 0, 5); - AddComponent(new AddonComponent(itemID + 8), +1, +1, 5); - - AddComponent(new AddonComponent(0x759), -2, -2, 0); - AddComponent(new AddonComponent(0x75A), +2, +2, 0); - AddComponent(new AddonComponent(0x75B), -2, +2, 0); - AddComponent(new AddonComponent(0x75C), +2, -2, 0); - - AddComponent(new AddonComponent(0x751), -1, +2, 0); - AddComponent(new AddonComponent(0x751), 0, +2, 0); - AddComponent(new AddonComponent(0x751), +1, +2, 0); - - AddComponent(new AddonComponent(0x752), +2, -1, 0); - AddComponent(new AddonComponent(0x752), +2, 0, 0); - AddComponent(new AddonComponent(0x752), +2, +1, 0); - - AddComponent(new AddonComponent(0x753), -1, -2, 0); - AddComponent(new AddonComponent(0x753), 0, -2, 0); - AddComponent(new AddonComponent(0x753), +1, -2, 0); - - AddComponent(new AddonComponent(0x754), -2, -1, 0); - AddComponent(new AddonComponent(0x754), -2, 0, 0); - AddComponent(new AddonComponent(0x754), -2, +1, 0); + Squares = 6095, + Buds = 6104, + Flowers = 6113, + Spikes = 6122, + Arrows = 6140, + Links = 6149 } - [CommandProperty(AccessLevel.GameMaster)] - public DuelTeleporterType Type + public class DuelTeleporterAddon : BaseAddon { - get => Components.Count > 0 ? (DuelTeleporterType)Components[0].ItemID : DuelTeleporterType.Squares; - set + [Constructible] + public DuelTeleporterAddon(DuelTeleporterType type = DuelTeleporterType.Squares) { - for (var i = 0; i < Components.Count && i < 9; ++i) + var itemID = (int)type; + + AddComponent(new AddonComponent(itemID + 0), -1, -1, 5); + AddComponent(new AddonComponent(itemID + 1), -1, 0, 5); + AddComponent(new AddonComponent(itemID + 2), 0, -1, 5); + AddComponent(new AddonComponent(itemID + 3), -1, +1, 5); + AddComponent(new AddonComponent(itemID + 4), 0, 0, 5); + AddComponent(new AddonComponent(itemID + 5), +1, -1, 5); + AddComponent(new AddonComponent(itemID + 6), 0, +1, 5); + AddComponent(new AddonComponent(itemID + 7), +1, 0, 5); + AddComponent(new AddonComponent(itemID + 8), +1, +1, 5); + + AddComponent(new AddonComponent(0x759), -2, -2, 0); + AddComponent(new AddonComponent(0x75A), +2, +2, 0); + AddComponent(new AddonComponent(0x75B), -2, +2, 0); + AddComponent(new AddonComponent(0x75C), +2, -2, 0); + + AddComponent(new AddonComponent(0x751), -1, +2, 0); + AddComponent(new AddonComponent(0x751), 0, +2, 0); + AddComponent(new AddonComponent(0x751), +1, +2, 0); + + AddComponent(new AddonComponent(0x752), +2, -1, 0); + AddComponent(new AddonComponent(0x752), +2, 0, 0); + AddComponent(new AddonComponent(0x752), +2, +1, 0); + + AddComponent(new AddonComponent(0x753), -1, -2, 0); + AddComponent(new AddonComponent(0x753), 0, -2, 0); + AddComponent(new AddonComponent(0x753), +1, -2, 0); + + AddComponent(new AddonComponent(0x754), -2, -1, 0); + AddComponent(new AddonComponent(0x754), -2, 0, 0); + AddComponent(new AddonComponent(0x754), -2, +1, 0); + } + + public DuelTeleporterAddon(Serial serial) : base(serial) + { + } + + [CommandProperty(AccessLevel.GameMaster)] + public DuelTeleporterType Type + { + get { - Components[i].ItemID = i + (int)value; + if (Components.Count > 0) + { + return (DuelTeleporterType)Components[0].ItemID; + } + + return DuelTeleporterType.Squares; } + set + { + for (var i = 0; i < Components.Count && i < 9; ++i) + { + Components[i].ItemID = i + (int)value; + } + } + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); } } } diff --git a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs index 6ab4fffea..71311ba2d 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs @@ -1,770 +1,804 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; -using ModernUO.Serialization; +using System.Text; using Server.Gumps; using Server.Items; using Server.Mobiles; using Server.Targeting; -using Server.Text; -namespace Server.Engines.ConPVP; - -[SerializationGenerator(0, false)] -public partial class BRBomb : Item +namespace Server.Engines.ConPVP { - private readonly BRGame m_Game; - - private readonly List m_Helpers; - - private readonly Point3DList m_Path = new(); - private readonly EffectTimer m_Timer; - private bool m_Flying; - private int m_PathIdx; - - public BRBomb(BRGame game) : - base(0x103C) // 0x103C = bread, 0x1042 = pie, 0x1364 = rock, 0x13a8 = pillow, 0x2256 = bagball + public class BRBomb : Item { - Movable = false; - Hue = 0x35; + private readonly BRGame m_Game; - m_Game = game; + private readonly List m_Helpers; - m_Helpers = new List(); + private readonly Point3DList m_Path = new(); + private readonly EffectTimer m_Timer; + private bool m_Flying; + private int m_PathIdx; - m_Timer = new EffectTimer(this); - m_Timer.Start(); - } - - public override bool SkipSerialization => true; - - public override string DefaultName => "da bomb"; - - public Mobile Thrower { get; private set; } - - private static Mobile FindOwner(IEntity parent) - { - if (parent is Item item) + public BRBomb(BRGame game) : + base(0x103C) // 0x103C = bread, 0x1042 = pie, 0x1364 = rock, 0x13a8 = pillow, 0x2256 = bagball { - return item.RootParent as Mobile; + Movable = false; + Hue = 0x35; + + m_Game = game; + + m_Helpers = new List(); + + m_Timer = new EffectTimer(this); + m_Timer.Start(); } - if (parent is Mobile mobile) + public BRBomb(Serial serial) : base(serial) { - return mobile; } - return null; - } + public override string DefaultName => "da bomb"; - public override void OnAdded(IEntity parent) - { - base.OnAdded(parent); + public Mobile Thrower { get; private set; } - var mob = FindOwner(parent); - - mob?.SolidHueOverride = 0x0499; - } - - public override void OnRemoved(IEntity parent) - { - base.OnRemoved(parent); - - var mob = FindOwner(parent); - - if (mob != null && m_Game != null) + private Mobile FindOwner(IEntity parent) { - mob.SolidHueOverride = m_Game.GetColor(mob); - } - } - - public void DropTo(Mobile mob, Mobile killer) - { - if (mob?.Deleted == false) - { - MoveToWorld(mob.Location, mob.Map); - } - else if (killer?.Deleted == false) - { - MoveToWorld(killer.Location, killer.Map); - } - else - { - m_Game?.ReturnBomb(); - } - } - - public override bool OnMoveOver(Mobile m) - { - if (m_Flying || !Visible || m_Game == null || m?.Alive != true) - { - return true; - } - - var useTeam = m_Game.GetTeamInfo(m); - return useTeam == null || TakeBomb(m, useTeam, "picked up"); - } - - public override void OnLocationChange(Point3D oldLocation) - { - base.OnLocationChange(oldLocation); - - if (m_Flying || !Visible || m_Game == null || Parent != null) - { - return; - } - - foreach (var ns in GetClientsAt()) - { - var m = ns.Mobile; - - if (m?.Player != true || !m.Alive) + if (parent is Item item) { - continue; + return item.RootParent as Mobile; } - var useTeam = m_Game.GetTeamInfo(m); - if (useTeam != null) + if (parent is Mobile mobile) { - TakeBomb(m, useTeam, "got"); + return mobile; + } + + return null; + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: + { + break; + } + } + + Timer.StartTimer(Delete); // delete this after the world loads + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void OnAdded(IEntity parent) + { + base.OnAdded(parent); + + var mob = FindOwner(parent); + + if (mob != null) + { + mob.SolidHueOverride = 0x0499; } } - } - public override void OnAfterDelete() - { - base.OnAfterDelete(); - - m_Timer?.Stop(); - } - - public override void OnDoubleClick(Mobile m) - { - if (m_Game == null || !Visible || m?.Alive != true) + public override void OnRemoved(IEntity parent) { - return; - } + base.OnRemoved(parent); - if (!m_Flying && IsChildOf(m.Backpack)) - { - m.Target = new BombTarget(this, m); - } - else if (Parent == null) - { - if (m.InRange(Location, 1) && m.Location.Z != Z) + var mob = FindOwner(parent); + + if (mob != null && m_Game != null) { - var useTeam = m_Game.GetTeamInfo(m); - if (useTeam == null) - { - return; - } - - TakeBomb(m, useTeam, "grabbed"); + mob.SolidHueOverride = m_Game.GetColor(mob); } } - } - private bool OnBombTarget(Mobile from, object obj) - { - if (m_Game == null) + public void DropTo(Mobile mob, Mobile killer) { - return true; - } - - if (!IsChildOf(from.Backpack)) - { - return true; - } - - // don't let them throw it to themselves - if (obj == from) - { - return false; - } - - if (obj is not IPoint3D) - { - return false; - } - - var pt = new Point3D((IPoint3D)obj); - - if (obj is Mobile) - { - pt.Z += 10; - } - else if (obj is Item item) - { - pt.Z += item.ItemData.CalcHeight + 1; - } - - m_Flying = true; - Visible = false; - Thrower = from; - MoveToWorld(GetWorldLocation(), from.Map); - - BeginFlight(pt); - return true; - } - - private void HitObject(Point3D ballLoc, int objZ, int objHeight) - { - DoAnim(GetWorldLocation(), ballLoc, Map); - MoveToWorld(ballLoc); - - m_Path.Clear(); - m_PathIdx = 0; - - Timer.StartTimer(TimeSpan.FromSeconds(0.05), ContinueFlight); - } - - private bool CheckCatch(Mobile m, Point3D myLoc) - { - if (m?.Alive != true || !m.Player || m_Game == null) - { - return false; - } - - if (m_Game.GetTeamInfo(m) == null) - { - return false; - } - - var zdiff = myLoc.Z - m.Z; - - return zdiff >= 0 && (zdiff < 12 || zdiff < 16 && Utility.RandomBool()); - } - - private void DoAnim(Point3D start, Point3D end, Map map) - { - Effects.SendMovingEffect( - new Entity(Serial.Zero, start, map), - new Entity(Serial.Zero, end, map), - ItemID, - 15, - 0, - false, - false, - Hue - ); - } - - private void DoCatch(Mobile m) - { - m_Flying = false; - Visible = true; - - if (m?.Alive != true || !m.Player || m_Game == null) - { - return; - } - - var useTeam = m_Game.GetTeamInfo(m); - - if (useTeam == null) - { - return; - } - - DoAnim(GetWorldLocation(), m.Location, m.Map); - - var verb = "caught"; - - if (Thrower != null && m_Game.GetTeamInfo(Thrower) != useTeam) - { - verb = "intercepted"; - } - - if (!TakeBomb(m, useTeam, verb)) - { - MoveToWorld(m.Location, m.Map); - } - } - - private void BeginFlight(Point3D dest) - { - var org = GetWorldLocation(); - - org.Z += 10; // always add 10 at the start cause we're coming from a mobile's eye level - - /*if (org.X > dest.X || ( org.X == dest.X && org.Y > dest.Y ) || ( org.X == dest.X && org.Y == dest.Y && org.Z > dest.Z )) - { - Point3D swap = org; - org = dest; - dest = swap; - }*/ - - var list = new List(); - - var xd = dest.X - org.X; - var yd = dest.Y - org.Y; - var zd = dest.Z - org.Z; - var dist2d = Math.Sqrt(xd * xd + yd * yd); - var dist3d = zd == 0 ? dist2d : Math.Sqrt(dist2d * dist2d + zd * zd); - - var rise = yd / dist3d; - var run = xd / dist3d; - var zslp = zd / dist3d; - - double x = org.X; - double y = org.Y; - double z = org.Z; - while (Utility.NumberBetween(x, dest.X, org.X, 0.5) && Utility.NumberBetween(y, dest.Y, org.Y, 0.5) && - Utility.NumberBetween(z, dest.Z, org.Z, 0.5)) - { - var ix = (int)Math.Round(x); - var iy = (int)Math.Round(y); - var iz = (int)Math.Round(z); - - if (list.Count > 0) + if (mob?.Deleted == false) { - var p = list[^1]; - - if (p.X != ix || p.Y != iy || p.Z != iz) - { - list.Add(new Point3D(ix, iy, iz)); - } + MoveToWorld(mob.Location, mob.Map); + } + else if (killer?.Deleted == false) + { + MoveToWorld(killer.Location, killer.Map); } else { - list.Add(new Point3D(ix, iy, iz)); - } - - x += run; - y += rise; - z += zslp; - } - - if (list.Count > 0 && list[^1] != dest) - { - list.Add(dest); - } - - /*if (dist3d > 4 && ( dest.X != org.X || dest.Y != org.Y )) - { - int count = list.Count; - int i; - int climb = count / 2; - if (climb > 3) - climb = 3; - - for ( i = 0; i < climb; i++ ) - { - p = ((Point3D)list[i]); - p.Z += (i+1) * 4; - list[i] = p; - } - - for ( ; i < count - climb; i++ ) - { - p = ((Point3D)list[i]); - p.Z += 16; - list[i] = p; - } - - for ( i = climb; i > 0; i-- ) - { - p = ((Point3D)list[i]); - p.Z += i * 4; - list[i] = p; - } - }*/ - - if (dist2d > 1) - { - var count = list.Count; - var height = count * 2 * (Utility.RandomDouble() * 0.40 + 0.10); // 10 - 50% - var coeff = -height / (count * count / 4.0); - - for (var i = 0; i < count; i++) - { - var p = list[i]; - - var xp = i - count / 2; - - p.Z += (int)Math.Ceiling(coeff * xp * xp + height); - - list[i] = p; + m_Game?.ReturnBomb(); } } - m_Path.Clear(); - for (var i = 0; i < list.Count; i++) + public override bool OnMoveOver(Mobile m) { - m_Path.Add(list[i]); + if (m_Flying || !Visible || m_Game == null || m?.Alive != true) + { + return true; + } + + var useTeam = m_Game.GetTeamInfo(m); + return useTeam == null || TakeBomb(m, useTeam, "picked up"); } - m_PathIdx = 0; - - ContinueFlight(); - } - - private void ContinueFlight() - { - int height; - var found = false; - - if (m_PathIdx < m_Path.Count && Map?.Tiles != null && Map != Map.Internal) + public override void OnLocationChange(Point3D oldLocation) { - var pathCheckEnd = m_PathIdx + 5; + base.OnLocationChange(oldLocation); - if (m_Path.Count < pathCheckEnd) + if (m_Flying || !Visible || m_Game == null || Parent != null) { - pathCheckEnd = m_Path.Count; + return; } - Visible = false; - - if (m_PathIdx > 0) // move to the next location + foreach (var ns in GetClientsAt()) { - MoveToWorld(m_Path[m_PathIdx - 1]); - } + var m = ns.Mobile; - Point3D pTop = new(GetWorldLocation()), pBottom = new(m_Path[pathCheckEnd - 1]); - Utility.FixPoints(ref pTop, ref pBottom); - - for (var i = m_PathIdx; i < pathCheckEnd; i++) - { - var point = m_Path[i]; - - var landTile = Map.Tiles.GetLandTile(point.X, point.Y); - Map.GetAverageZ(point.X, point.Y, out var landZ, out _, out var landTop); - - if (landZ <= point.Z && landTop >= point.Z && !landTile.Ignored) + if (m?.Player != true || !m.Alive) { - HitObject(point, landTop, 0); - return; + continue; } - var foundStatics = false; - foreach (var t in Map.Tiles.GetStaticAndMultiTiles(point.X, point.Y)) + var useTeam = m_Game.GetTeamInfo(m); + if (useTeam != null) { - foundStatics = true; + TakeBomb(m, useTeam, "got"); + } + } + } - var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; - height = id.CalcHeight; + public override void OnAfterDelete() + { + base.OnAfterDelete(); - if (t.Z <= point.Z && t.Z + height >= point.Z && - (id.Flags & (TileFlag.Impassable | TileFlag.Wall | TileFlag.NoShoot)) != 0) + m_Timer?.Stop(); + } + + public override void OnDoubleClick(Mobile m) + { + if (m_Game == null || !Visible || m?.Alive != true) + { + return; + } + + if (!m_Flying && IsChildOf(m.Backpack)) + { + m.Target = new BombTarget(this, m); + } + else if (Parent == null) + { + if (m.InRange(Location, 1) && m.Location.Z != Z) + { + var useTeam = m_Game.GetTeamInfo(m); + if (useTeam == null) { - if (i > m_PathIdx) - { - point = m_Path[i - 1]; - } - else - { - point = GetWorldLocation(); - } - - HitObject(point, t.Z, height); return; } + + TakeBomb(m, useTeam, "grabbed"); + } + } + } + + private bool OnBombTarget(Mobile from, object obj) + { + if (m_Game == null) + { + return true; + } + + if (!IsChildOf(from.Backpack)) + { + return true; + } + + // don't let them throw it to themselves + if (obj == from) + { + return false; + } + + if (obj is not IPoint3D) + { + return false; + } + + var pt = new Point3D((IPoint3D)obj); + + if (obj is Mobile) + { + pt.Z += 10; + } + else if (obj is Item item) + { + pt.Z += item.ItemData.CalcHeight + 1; + } + + m_Flying = true; + Visible = false; + Thrower = from; + MoveToWorld(GetWorldLocation(), from.Map); + + BeginFlight(pt); + return true; + } + + private void HitObject(Point3D ballLoc, int objZ, int objHeight) + { + DoAnim(GetWorldLocation(), ballLoc, Map); + MoveToWorld(ballLoc); + + m_Path.Clear(); + m_PathIdx = 0; + + Timer.StartTimer(TimeSpan.FromSeconds(0.05), ContinueFlight); + } + + private bool CheckCatch(Mobile m, Point3D myLoc) + { + if (m?.Alive != true || !m.Player || m_Game == null) + { + return false; + } + + if (m_Game.GetTeamInfo(m) == null) + { + return false; + } + + var zdiff = myLoc.Z - m.Z; + + return zdiff >= 0 && (zdiff < 12 || zdiff < 16 && Utility.RandomBool()); + } + + private void DoAnim(Point3D start, Point3D end, Map map) + { + Effects.SendMovingEffect( + new Entity(Serial.Zero, start, map), + new Entity(Serial.Zero, end, map), + ItemID, + 15, + 0, + false, + false, + Hue + ); + } + + private void DoCatch(Mobile m) + { + m_Flying = false; + Visible = true; + + if (m?.Alive != true || !m.Player || m_Game == null) + { + return; + } + + var useTeam = m_Game.GetTeamInfo(m); + + if (useTeam == null) + { + return; + } + + DoAnim(GetWorldLocation(), m.Location, m.Map); + + var verb = "caught"; + + if (Thrower != null && m_Game.GetTeamInfo(Thrower) != useTeam) + { + verb = "intercepted"; + } + + if (!TakeBomb(m, useTeam, verb)) + { + MoveToWorld(m.Location, m.Map); + } + } + + private void BeginFlight(Point3D dest) + { + var org = GetWorldLocation(); + + org.Z += 10; // always add 10 at the start cause we're coming from a mobile's eye level + + /*if (org.X > dest.X || ( org.X == dest.X && org.Y > dest.Y ) || ( org.X == dest.X && org.Y == dest.Y && org.Z > dest.Z )) + { + Point3D swap = org; + org = dest; + dest = swap; + }*/ + + var list = new List(); + + var xd = dest.X - org.X; + var yd = dest.Y - org.Y; + var zd = dest.Z - org.Z; + var dist2d = Math.Sqrt(xd * xd + yd * yd); + var dist3d = zd == 0 ? dist2d : Math.Sqrt(dist2d * dist2d + zd * zd); + + var rise = yd / dist3d; + var run = xd / dist3d; + var zslp = zd / dist3d; + + double x = org.X; + double y = org.Y; + double z = org.Z; + while (Utility.NumberBetween(x, dest.X, org.X, 0.5) && Utility.NumberBetween(y, dest.Y, org.Y, 0.5) && + Utility.NumberBetween(z, dest.Z, org.Z, 0.5)) + { + var ix = (int)Math.Round(x); + var iy = (int)Math.Round(y); + var iz = (int)Math.Round(z); + + if (list.Count > 0) + { + var p = list[^1]; + + if (p.X != ix || p.Y != iy || p.Z != iz) + { + list.Add(new Point3D(ix, iy, iz)); + } + } + else + { + list.Add(new Point3D(ix, iy, iz)); } - if (landTile.ID == 0x244 && foundStatics) // 0x244 = invalid land tile - { - var empty = true; - foreach (var item in Map.GetItemsAt(point)) - { - if (item != this) - { - empty = false; - break; - } - } + x += run; + y += rise; + z += zslp; + } - if (empty) + if (list.Count > 0 && list[^1] != dest) + { + list.Add(dest); + } + + /*if (dist3d > 4 && ( dest.X != org.X || dest.Y != org.Y )) + { + int count = list.Count; + int i; + int climb = count / 2; + if (climb > 3) + climb = 3; + + for ( i = 0; i < climb; i++ ) + { + p = ((Point3D)list[i]); + p.Z += (i+1) * 4; + list[i] = p; + } + + for ( ; i < count - climb; i++ ) + { + p = ((Point3D)list[i]); + p.Z += 16; + list[i] = p; + } + + for ( i = climb; i > 0; i-- ) + { + p = ((Point3D)list[i]); + p.Z += i * 4; + list[i] = p; + } + }*/ + + if (dist2d > 1) + { + var count = list.Count; + var height = count * 2 * (Utility.RandomDouble() * 0.40 + 0.10); // 10 - 50% + var coeff = -height / (count * count / 4.0); + + for (var i = 0; i < count; i++) + { + var p = list[i]; + + var xp = i - count / 2; + + p.Z += (int)Math.Ceiling(coeff * xp * xp + height); + + list[i] = p; + } + } + + m_Path.Clear(); + for (var i = 0; i < list.Count; i++) + { + m_Path.Add(list[i]); + } + + m_PathIdx = 0; + + ContinueFlight(); + } + + private void ContinueFlight() + { + int height; + var found = false; + + if (m_PathIdx < m_Path.Count && Map?.Tiles != null && Map != Map.Internal) + { + var pathCheckEnd = m_PathIdx + 5; + + if (m_Path.Count < pathCheckEnd) + { + pathCheckEnd = m_Path.Count; + } + + Visible = false; + + if (m_PathIdx > 0) // move to the next location + { + MoveToWorld(m_Path[m_PathIdx - 1]); + } + + Point3D pTop = new(GetWorldLocation()), pBottom = new(m_Path[pathCheckEnd - 1]); + Utility.FixPoints(ref pTop, ref pBottom); + + for (var i = m_PathIdx; i < pathCheckEnd; i++) + { + var point = m_Path[i]; + + var landTile = Map.Tiles.GetLandTile(point.X, point.Y); + Map.GetAverageZ(point.X, point.Y, out var landZ, out _, out var landTop); + + if (landZ <= point.Z && landTop >= point.Z && !landTile.Ignored) { HitObject(point, landTop, 0); return; } - } - } - var rect = new Rectangle2D(pTop.X, pTop.Y, pBottom.X - pTop.X + 1, pBottom.Y - pTop.Y + 1); + var foundStatics = false; + foreach (var t in Map.Tiles.GetStaticAndMultiTiles(point.X, point.Y)) + { + foundStatics = true; - foreach (var i in Map.GetItemsInBounds(rect)) - { - if (i == this || i.ItemID >= 0x4000) - { - continue; + var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; + height = id.CalcHeight; + + if (t.Z <= point.Z && t.Z + height >= point.Z && + (id.Flags & (TileFlag.Impassable | TileFlag.Wall | TileFlag.NoShoot)) != 0) + { + if (i > m_PathIdx) + { + point = m_Path[i - 1]; + } + else + { + point = GetWorldLocation(); + } + + HitObject(point, t.Z, height); + return; + } + } + + if (landTile.ID == 0x244 && foundStatics) // 0x244 = invalid land tile + { + var empty = true; + foreach (var item in Map.GetItemsAt(point)) + { + if (item != this) + { + empty = false; + break; + } + } + + if (empty) + { + HitObject(point, landTop, 0); + return; + } + } } - if (i is BRGoal) + var rect = new Rectangle2D(pTop.X, pTop.Y, pBottom.X - pTop.X + 1, pBottom.Y - pTop.Y + 1); + + foreach (var i in Map.GetItemsInBounds(rect)) { - height = 17; - } - else if (i is Blocker) - { - height = 20; - } - else - { - var id = i.ItemData; - if ((id.Flags & (TileFlag.Impassable | TileFlag.Wall | TileFlag.NoShoot)) == 0) + if (i == this || i.ItemID >= 0x4000) { continue; } - height = id.CalcHeight; - } - - var point = i.Location; - var loc = i.Location; - for (var j = m_PathIdx; j < pathCheckEnd; j++) - { - point = m_Path[j]; - - if (loc.X == point.X && loc.Y == point.Y && - (i is Blocker || loc.Z <= point.Z && loc.Z + height >= point.Z)) + if (i is BRGoal) { - found = true; - if (j > m_PathIdx) + height = 17; + } + else if (i is Blocker) + { + height = 20; + } + else + { + var id = i.ItemData; + if ((id.Flags & (TileFlag.Impassable | TileFlag.Wall | TileFlag.NoShoot)) == 0) { - point = m_Path[j - 1]; + continue; + } + + height = id.CalcHeight; + } + + var point = i.Location; + var loc = i.Location; + for (var j = m_PathIdx; j < pathCheckEnd; j++) + { + point = m_Path[j]; + + if (loc.X == point.X && loc.Y == point.Y && + (i is Blocker || loc.Z <= point.Z && loc.Z + height >= point.Z)) + { + found = true; + if (j > m_PathIdx) + { + point = m_Path[j - 1]; + } + else + { + point = GetWorldLocation(); + } + + break; + } + } + + if (!found) + { + continue; + } + + if (i is BRGoal goal) + { + var oldLoc = new Point3D(GetWorldLocation()); + if (CheckScore(goal, Thrower, 3)) + { + DoAnim(oldLoc, point, Map); } else { - point = GetWorldLocation(); + HitObject(point, loc.Z, height); } - - break; - } - } - - if (!found) - { - continue; - } - - if (i is BRGoal goal) - { - var oldLoc = new Point3D(GetWorldLocation()); - if (CheckScore(goal, Thrower, 3)) - { - DoAnim(oldLoc, point, Map); } else { HitObject(point, loc.Z, height); } - } - else - { - HitObject(point, loc.Z, height); + + return; } - return; - } - - foreach (var ns in Map.GetClientsInBounds(rect)) - { - var m = ns.Mobile; - - if (m == null || m == Thrower) + foreach (var ns in Map.GetClientsInBounds(rect)) { - continue; - } + var m = ns.Mobile; - Point3D point; - var loc = m.Location; - - for (var j = m_PathIdx; j < pathCheckEnd && !found; j++) - { - point = m_Path[j]; - - if (loc.X == point.X && loc.Y == point.Y && - loc.Z <= point.Z && loc.Z + 16 >= point.Z) + if (m == null || m == Thrower) { - found = CheckCatch(m, point); + continue; + } + + Point3D point; + var loc = m.Location; + + for (var j = m_PathIdx; j < pathCheckEnd && !found; j++) + { + point = m_Path[j]; + + if (loc.X == point.X && loc.Y == point.Y && + loc.Z <= point.Z && loc.Z + 16 >= point.Z) + { + found = CheckCatch(m, point); + } + } + + if (!found) + { + continue; + } + + // TODO: probably need to change this a lot... + DoCatch(m); + + return; + } + + m_PathIdx = pathCheckEnd; + + if (m_PathIdx > 0 && m_PathIdx - 1 < m_Path.Count) + { + DoAnim(GetWorldLocation(), m_Path[m_PathIdx - 1], Map); + } + + Timer.StartTimer(TimeSpan.FromSeconds(0.1), ContinueFlight); + } + else + { + if (m_PathIdx > 0 && m_PathIdx - 1 < m_Path.Count) + { + MoveToWorld(m_Path[m_PathIdx - 1]); + } + else if (m_Path.Count > 0) + { + MoveToWorld(m_Path.Last); + } + + var myZ = Map?.GetAverageZ(X, Y) ?? 0; + + if (Map?.Tiles != null) + { + foreach (var t in Map.Tiles.GetStaticAndMultiTiles(X, Y)) + { + var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; + height = id.CalcHeight; + + if (t.Z + height > myZ && t.Z + height <= Z) + { + myZ = t.Z + height; + } } } - if (!found) + foreach (var item in GetItemsAt()) { - continue; + if (item.Visible && item != this) + { + height = item.ItemData.CalcHeight; + if (item.Z + height > myZ && item.Z + height <= Z) + { + myZ = item.Z + height; + } + } } - // TODO: probably need to change this a lot... - DoCatch(m); + Z = myZ; + m_Flying = false; + Visible = true; - return; + m_Path.Clear(); + m_PathIdx = 0; } - - m_PathIdx = pathCheckEnd; - - if (m_PathIdx > 0 && m_PathIdx - 1 < m_Path.Count) - { - DoAnim(GetWorldLocation(), m_Path[m_PathIdx - 1], Map); - } - - Timer.StartTimer(TimeSpan.FromSeconds(0.1), ContinueFlight); } - else + + public bool CheckScore(BRGoal goal, Mobile m, int points) { - if (m_PathIdx > 0 && m_PathIdx - 1 < m_Path.Count) + if (m_Game == null || m == null || goal == null) { - MoveToWorld(m_Path[m_PathIdx - 1]); - } - else if (m_Path.Count > 0) - { - MoveToWorld(m_Path.Last); + return false; } - var myZ = Map?.GetAverageZ(X, Y) ?? 0; - - if (Map?.Tiles != null) + var team = m_Game.GetTeamInfo(m); + if (team == null || goal.Team == null || team == goal.Team) { - foreach (var t in Map.Tiles.GetStaticAndMultiTiles(X, Y)) + return false; + } + + if (points > 3) + { + m_Game.Alert($"Touchdown {team.Name} ({m.Name})!"); + } + else + { + m_Game.Alert($"Field goal {team.Name} ({m.Name})!"); + } + + for (var i = m_Helpers.Count - 1; i >= 0; i--) + { + var mob = m_Helpers[i]; + + var pi = team[mob]; + if (pi != null) { - var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; - height = id.CalcHeight; - - if (t.Z + height > myZ && t.Z + height <= Z) + if (mob == m) { - myZ = t.Z + height; + pi.Captures += points; } + + pi.Score += points + 1; + + points /= 2; } } - foreach (var item in GetItemsAt()) - { - if (item.Visible && item != this) - { - height = item.ItemData.CalcHeight; - if (item.Z + height > myZ && item.Z + height <= Z) - { - myZ = item.Z + height; - } - } - } + m_Game.ReturnBomb(); - Z = myZ; m_Flying = false; Visible = true; - m_Path.Clear(); m_PathIdx = 0; - } - } - public bool CheckScore(BRGoal goal, Mobile m, int points) - { - if (m_Game == null || m == null || goal == null) - { - return false; - } - - var team = m_Game.GetTeamInfo(m); - if (team == null || goal.Team == null || team == goal.Team) - { - return false; - } - - if (points > 3) - { - m_Game.Alert($"Touchdown {team.Name} ({m.Name})!"); - } - else - { - m_Game.Alert($"Field goal {team.Name} ({m.Name})!"); - } - - for (var i = m_Helpers.Count - 1; i >= 0; i--) - { - var mob = m_Helpers[i]; - - var pi = team[mob]; - if (pi != null) - { - if (mob == m) - { - pi.Captures += points; - } - - pi.Score += points + 1; - - points /= 2; - } - } - - m_Game.ReturnBomb(); - - m_Flying = false; - Visible = true; - m_Path.Clear(); - m_PathIdx = 0; - - Target.Cancel(m); - - return true; - } - - private bool TakeBomb(Mobile m, BRTeamInfo team, string verb) - { - if (!m.Player || !m.Alive || m.NetState == null) - { - return false; - } - - if (m.PlaceInBackpack(this)) - { - m.RevealingAction(); - - m.LocalOverheadMessage(MessageType.Regular, 0x59, false, "You got the bomb!"); - m_Game.Alert($"{verb} ({m.Name}) {team.Name} the bomb!"); - - m.Target = new BombTarget(this, m); - - m_Helpers.Remove(m); - - if (m_Helpers.Count > 0) - { - var last = m_Helpers[0]; - - if (m_Game.GetTeamInfo(last) != team) - { - m_Helpers.Clear(); - } - } - - m_Helpers.Add(m); + Target.Cancel(m); return true; } - return false; - } - - private class EffectTimer : Timer - { - private readonly BRBomb m_Bomb; - private int m_Count; - - public EffectTimer(BRBomb bomb) : base(TimeSpan.Zero, TimeSpan.FromSeconds(1.0)) + private bool TakeBomb(Mobile m, BRTeamInfo team, string verb) { - m_Bomb = bomb; - m_Count = 0; - } - - protected override void OnTick() - { - if (m_Bomb.Parent == null && m_Bomb.m_Game?.Controller != null) + if (!m.Player || !m.Alive || m.NetState == null) { - if (!m_Bomb.m_Flying && m_Bomb.Map != Map.Internal) + return false; + } + + if (m.PlaceInBackpack(this)) + { + m.RevealingAction(); + + m.LocalOverheadMessage(MessageType.Regular, 0x59, false, "You got the bomb!"); + m_Game.Alert($"{verb} ({m.Name}) {team.Name} the bomb!"); + + m.Target = new BombTarget(this, m); + + if (m_Helpers.Contains(m)) { - Effects.SendLocationEffect(m_Bomb.GetWorldLocation(), m_Bomb.Map, 0x377A, 16, 10, m_Bomb.Hue); + m_Helpers.Remove(m); } - if (m_Bomb.Location != m_Bomb.m_Game.Controller.BombHome) + if (m_Helpers.Count > 0) { - if (++m_Count >= 30) - { - m_Bomb.m_Game.ReturnBomb(); - m_Bomb.m_Game.Alert("The bomb has been returned to it's starting point."); + var last = m_Helpers[0]; + if (m_Game.GetTeamInfo(last) != team) + { + m_Helpers.Clear(); + } + } + + m_Helpers.Add(m); + + return true; + } + + return false; + } + + private class EffectTimer : Timer + { + private readonly BRBomb m_Bomb; + private int m_Count; + + public EffectTimer(BRBomb bomb) : base(TimeSpan.Zero, TimeSpan.FromSeconds(1.0)) + { + m_Bomb = bomb; + m_Count = 0; + } + + protected override void OnTick() + { + if (m_Bomb.Parent == null && m_Bomb.m_Game?.Controller != null) + { + if (!m_Bomb.m_Flying && m_Bomb.Map != Map.Internal) + { + Effects.SendLocationEffect(m_Bomb.GetWorldLocation(), m_Bomb.Map, 0x377A, 16, 10, m_Bomb.Hue); + } + + if (m_Bomb.Location != m_Bomb.m_Game.Controller.BombHome) + { + if (++m_Count >= 30) + { + m_Bomb.m_Game.ReturnBomb(); + m_Bomb.m_Game.Alert("The bomb has been returned to it's starting point."); + + m_Count = 0; + m_Bomb.m_Helpers.Clear(); + } + } + else + { m_Count = 0; - m_Bomb.m_Helpers.Clear(); } } else @@ -772,92 +806,223 @@ public partial class BRBomb : Item m_Count = 0; } } + } + + private class BombTarget : Target + { + private readonly BRBomb m_Bomb; + private readonly Mobile m_Mob; + private bool m_Resend = true; + + public BombTarget(BRBomb bomb, Mobile from) : base(10, true, TargetFlags.None) + { + CheckLOS = false; + + m_Bomb = bomb; + m_Mob = from; + + m_Mob.SendMessage(0x26, "Where do you want to throw it?"); + } + + protected override void OnTarget(Mobile from, object targeted) + { + m_Resend = !m_Bomb.OnBombTarget(from, targeted); + } + + protected override void OnTargetUntargetable(Mobile from, object targeted) + { + m_Resend = !m_Bomb.OnBombTarget(from, targeted); + } + + protected override void OnTargetFinish(Mobile from) + { + base.OnTargetFinish(from); + + // has to be delayed in case some other target canceled us... + if (m_Resend) + { + Timer.StartTimer(ResendBombTarget); + } + } + + private void ResendBombTarget() + { + // Make sure they still have the bomb, then give them the target back + if (m_Bomb?.Deleted == false && m_Mob?.Deleted == false && m_Mob.Alive && m_Bomb.IsChildOf(m_Mob)) + { + m_Mob.Target = new BombTarget(m_Bomb, m_Mob); + } + } + } + } + + public class BRGoal : BaseAddon + { + private bool m_North; + + private BRTeamInfo m_Team; + + [Constructible] + public BRGoal() + { + ItemID = 0x51D; + Hue = 0x84C; + Visible = true; + + Remake(); + } + + public BRGoal(Serial serial) : base(serial) + { + } + + [CommandProperty(AccessLevel.GameMaster)] + public bool North + { + get => m_North; + set + { + m_North = value; + Remake(); + } + } + + public override string DefaultName => "Bombing Run Goal"; + + public override bool ShareHue => false; + + public BRTeamInfo Team + { + get => m_Team; + set + { + m_Team = value; + if (m_Team != null && m_Team.Color != 0) + { + Hue = m_Team.Color; + } + else + { + Hue = 0x84C; + } + } + } + + private static AddonComponent SetHue(AddonComponent ac, int hue) + { + ac.Hue = hue; + return ac; + } + + private void Remake() + { + foreach (var ac in Components) + { + ac.Addon = null; + ac.Delete(); + } + + ClearComponents(); + + // stairs + AddComponent(new AddonComponent(0x74D), -1, +1, -5); + AddComponent(new AddonComponent(0x71F), 0, +1, -5); + AddComponent(new AddonComponent(0x74B), +1, +1, -5); + AddComponent(new AddonComponent(0x736), +1, 0, -5); + AddComponent(new AddonComponent(0x74C), +1, -1, -5); + AddComponent(new AddonComponent(0x737), 0, -1, -5); + AddComponent(new AddonComponent(0x74A), -1, -1, -5); + AddComponent(new AddonComponent(0x749), -1, 0, -5); + + // Center Sparkle + AddComponent(new AddonComponent(0x375A), 0, 0, -1); + + if (!m_North) + { + // Pillars + AddComponent(new AddonComponent(0x0CE), 0, +1, -2); + AddComponent(new AddonComponent(0x0CC), 0, -1, -2); + AddComponent(new AddonComponent(0x0D0), 0, 0, -2); + + // Yellow parts + AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), 0, +1, 7); + AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), 0, 0, 16); + AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), 0, -1, 7); + + // Blue Sparkles + AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), 0, +1, 12); + AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), 0, +1, -1); + AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), 0, -1, 12); + AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), 0, -1, -1); + } else { - m_Count = 0; + // Pillars + AddComponent(new AddonComponent(0x0CF), +1, 0, -2); + AddComponent(new AddonComponent(0x0CC), -1, 0, -2); + AddComponent(new AddonComponent(0x0D1), 0, 0, -2); + + // Yellow parts + AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), +1, 0, 7); + AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), 0, 0, 16); + AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), -1, 0, 7); + + // Blue Sparkles + AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), +1, 0, 12); + AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), +1, 0, -1); + AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), -1, 0, 12); + AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), -1, 0, -1); } } - } - private class BombTarget : Target - { - private readonly BRBomb m_Bomb; - private readonly Mobile m_Mob; - private bool m_Resend = true; - - public BombTarget(BRBomb bomb, Mobile from) : base(10, true, TargetFlags.None) + public override void Serialize(IGenericWriter writer) { - CheckLOS = false; + base.Serialize(writer); - m_Bomb = bomb; - m_Mob = from; + writer.Write(1); // version - m_Mob.SendMessage(0x26, "Where do you want to throw it?"); + writer.Write(m_North); } - protected override void OnTarget(Mobile from, object targeted) + public override void Deserialize(IGenericReader reader) { - m_Resend = !m_Bomb.OnBombTarget(from, targeted); - } + base.Deserialize(reader); - protected override void OnTargetUntargetable(Mobile from, object targeted) - { - m_Resend = !m_Bomb.OnBombTarget(from, targeted); - } + var version = reader.ReadInt(); - protected override void OnTargetFinish(Mobile from) - { - base.OnTargetFinish(from); - - // has to be delayed in case some other target canceled us... - if (m_Resend) + switch (version) { - Timer.StartTimer(ResendBombTarget); + case 1: + { + m_North = reader.ReadBool(); + goto case 0; + } + case 0: + { + break; + } } + + Hue = 0x84C; } - private void ResendBombTarget() + public override bool OnMoveOver(Mobile m) { - // Make sure they still have the bomb, then give them the target back - if (m_Bomb?.Deleted == false && m_Mob?.Deleted == false && m_Mob.Alive && m_Bomb.IsChildOf(m_Mob)) + if (!Visible) { - m_Mob.Target = new BombTarget(m_Bomb, m_Mob); + return true; } - } - } -} -[SerializationGenerator(0, false)] -public partial class BRGoal : BaseAddon -{ - [SerializableField(0, fieldChanged: nameof(OnNorthChanged))] - private bool _north; + if (m?.Player != true || !m.Alive || m.Backpack == null || m_Team?.Game == null) + { + return true; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void OnNorthChanged(bool oldValue, bool newValue) => Remake(); + if (!base.OnMoveOver(m)) + { + return false; + } - private BRTeamInfo m_Team; - - [Constructible] - public BRGoal() - { - ItemID = 0x51D; - Hue = 0x84C; - Visible = true; - - Remake(); - } - - public override string DefaultName => "Bombing Run Goal"; - - public override bool ShareHue => false; - - public BRTeamInfo Team - { - get => m_Team; - set - { - m_Team = value; if (m_Team != null && m_Team.Color != 0) { Hue = m_Team.Color; @@ -866,1020 +1031,942 @@ public partial class BRGoal : BaseAddon { Hue = 0x84C; } - } - } - private static AddonComponent SetHue(AddonComponent ac, int hue) - { - ac.Hue = hue; - return ac; - } - - private void Remake() - { - for (var i = 0; i < Components.Count; i++) - { - var ac = Components[i]; - ac.Addon = null; - ac.Delete(); - } - - ClearComponents(); - - // stairs - AddComponent(new AddonComponent(0x74D), -1, +1, -5); - AddComponent(new AddonComponent(0x71F), 0, +1, -5); - AddComponent(new AddonComponent(0x74B), +1, +1, -5); - AddComponent(new AddonComponent(0x736), +1, 0, -5); - AddComponent(new AddonComponent(0x74C), +1, -1, -5); - AddComponent(new AddonComponent(0x737), 0, -1, -5); - AddComponent(new AddonComponent(0x74A), -1, -1, -5); - AddComponent(new AddonComponent(0x749), -1, 0, -5); - - // Center Sparkle - AddComponent(new AddonComponent(0x375A), 0, 0, -1); - - if (!_north) - { - // Pillars - AddComponent(new AddonComponent(0x0CE), 0, +1, -2); - AddComponent(new AddonComponent(0x0CC), 0, -1, -2); - AddComponent(new AddonComponent(0x0D0), 0, 0, -2); - - // Yellow parts - AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), 0, +1, 7); - AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), 0, 0, 16); - AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), 0, -1, 7); - - // Blue Sparkles - AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), 0, +1, 12); - AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), 0, +1, -1); - AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), 0, -1, 12); - AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), 0, -1, -1); - } - else - { - // Pillars - AddComponent(new AddonComponent(0x0CF), +1, 0, -2); - AddComponent(new AddonComponent(0x0CC), -1, 0, -2); - AddComponent(new AddonComponent(0x0D1), 0, 0, -2); - - // Yellow parts - AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), +1, 0, 7); - AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), 0, 0, 16); - AddComponent(SetHue(new AddonComponent(0x0DF), 0x499), -1, 0, 7); - - // Blue Sparkles - AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), +1, 0, 12); - AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), +1, 0, -1); - AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), -1, 0, 12); - AddComponent(SetHue(new AddonComponent(0x377A), 0x84C), -1, 0, -1); - } - } - - public override bool OnMoveOver(Mobile m) - { - if (!Visible) - { + m.Backpack.FindItemByType()?.CheckScore(this, m, 7); return true; } - - if (m?.Player != true || !m.Alive || m.Backpack == null || m_Team?.Game == null) - { - return true; - } - - if (!base.OnMoveOver(m)) - { - return false; - } - - if (m_Team != null && m_Team.Color != 0) - { - Hue = m_Team.Color; - } - else - { - Hue = 0x84C; - } - - m.Backpack.FindItemByType()?.CheckScore(this, m, 7); - return true; - } -} - -[SerializationGenerator(0, false)] -public sealed partial class BRBoard : Item -{ - public BRTeamInfo m_TeamInfo; - - [Constructible] - public BRBoard() : base(7774) => Movable = false; - - public override string DefaultName => "Scoreboard"; - - public override void OnDoubleClick(Mobile from) - { - if (m_TeamInfo?.Game != null) - { - BRBoardGump.DisplayTo(from, m_TeamInfo.Game); - } - } -} - -public class BRBoardGump : DynamicGump -{ - private const int LabelColor32 = 0xFFFFFF; - private const int BlackColor32 = 0x000000; - - private readonly Mobile _mob; - private readonly BRGame _game; - private readonly BRTeamInfo _section; - - public override bool Singleton => true; - - private BRBoardGump(Mobile mob, BRGame game, BRTeamInfo section) : base(60, 60) - { - _mob = mob; - _game = game; - _section = section; } - public static void DisplayTo(Mobile mob, BRGame game, BRTeamInfo section = null) + public sealed class BRBoard : Item { - if (mob?.NetState == null || game == null) + public BRTeamInfo m_TeamInfo; + + [Constructible] + public BRBoard() + : base(7774) => + Movable = false; + + public BRBoard(Serial serial) + : base(serial) { - return; } - mob.SendGump(new BRBoardGump(mob, game, section)); - } + public override string DefaultName => "Scoreboard"; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var ourTeam = _game.GetTeamInfo(_mob); - - var entries = new List(); - var total = 0; - - if (_section == null) + public override void OnDoubleClick(Mobile from) { - for (var i = 0; i < _game.Context.Participants.Count; ++i) + if (m_TeamInfo?.Game != null) { - var teamInfo = _game.Controller.TeamInfo[i % _game.Controller.TeamInfo.Length]; + from.SendGump(new BRBoardGump(from, m_TeamInfo.Game)); + } + } - if (teamInfo == null) + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } + } + + public class BRBoardGump : Gump + { + private const int LabelColor32 = 0xFFFFFF; + private const int BlackColor32 = 0x000000; + + public override bool Singleton => true; + + public BRBoardGump(Mobile mob, BRGame game, BRTeamInfo section = null) : base(60, 60) + { + // m_Game = game; + + var ourTeam = game.GetTeamInfo(mob); + + var entries = new List(); + var total = 0; + + if (section == null) + { + for (var i = 0; i < game.Context.Participants.Count; ++i) { - continue; - } + var teamInfo = game.Controller.TeamInfo[i % game.Controller.TeamInfo.Length]; - entries.Add(teamInfo); - } - - total = entries.Count; - } - else - { - foreach (var player in _section.Players.Values) - { - if (player.Score > 0) - { - total++; - } - } - } - - entries.Sort(); - - var height = 0; - - if (_section == null) - { - height = 73 + entries.Count * 75 + 28; - } - - builder.SetNoClose(); - - builder.AddPage(); - - builder.AddBackground(1, 1, 398, height, 3600); - - builder.AddImageTiled(16, 15, 369, height - 29, 3604); - - for (var i = 0; i < total; i += 1) - { - builder.AddImageTiled(22, 58 + i * 75, 357, 70, 0x2430); - } - - builder.AddAlphaRegion(16, 15, 369, height - 29); - - builder.AddImage(215, -45, 0xEE40); - - AddBorderedText(ref builder, 22, 22, 294, 20, "BR Scoreboard".Center(), LabelColor32, BlackColor32); - - builder.AddImageTiled(32, 50, 264, 1, 9107); - builder.AddImageTiled(42, 52, 264, 1, 9157); - - if (_section == null) - { - for (var i = 0; i < entries.Count; ++i) - { - var teamInfo = entries[i]; - - builder.AddImage(30, 70 + i * 75, 10152); - builder.AddImage(30, 85 + i * 75, 10151); - builder.AddImage(30, 100 + i * 75, 10151); - builder.AddImage(30, 106 + i * 75, 10154); - - builder.AddImage(24, 60 + i * 75, teamInfo == ourTeam ? 9730 : 9727, teamInfo.Color - 1); - - var borderColor = teamInfo.Color == 0x455 ? LabelColor32 : BlackColor32; - var nameColor = teamInfo.Color switch - { - 0x47E => 0xFFFFFF, - 0x4F2 => 0x3399FF, - 0x4F7 => 0x33FF33, - 0x4FC => 0xFF00FF, - 0x021 => 0xFF3333, - 0x01A => 0xFF66FF, - 0x455 => 0x333333, - _ => LabelColor32 - }; - - AddBorderedText( - ref builder, - 60, - 65 + i * 75, - 250, - 20, - $"{LadderGump.Rank(1 + i)}: {teamInfo.Name}", - nameColor, - borderColor - ); - - AddBorderedText(ref builder, 50 + 10, 85 + i * 75, 100, 20, "Score:", 0xFFC000, BlackColor32); - AddBorderedText(ref builder, 50 + 15, 105 + i * 75, 100, 20, $"{teamInfo.Score:N0}", 0xFFC000, BlackColor32); - - AddBorderedText(ref builder, 110 + 10, 85 + i * 75, 100, 20, "Kills:", 0xFFC000, BlackColor32); - AddBorderedText(ref builder, 110 + 15, 105 + i * 75, 100, 20, $"{teamInfo.Kills:N0}", 0xFFC000, BlackColor32); - - AddBorderedText(ref builder, 160 + 10, 85 + i * 75, 100, 20, "Points:", 0xFFC000, BlackColor32); - AddBorderedText( - ref builder, - 160 + 15, - 105 + i * 75, - 100, - 20, - $"{teamInfo.Captures:N0}", - 0xFFC000, - BlackColor32 - ); - - var pl = teamInfo.Leader; - - AddBorderedText(ref builder, 235 + 10, 85 + i * 75, 250, 20, "Leader:", 0xFFC000, BlackColor32); - - if (pl != null) - { - AddBorderedText(ref builder, 235 + 15, 105 + i * 75, 250, 20, pl.Player.Name, 0xFFC000, BlackColor32); - } - } - } - - builder.AddButton(314, height - 42, 247, 248, 1); - } - - private static void AddBorderedText( - ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color, int borderColor - ) - { - AddColoredText(ref builder, x - 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x - 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x, y, width, height, text, color); - } - - private static void AddColoredText( - ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color - ) => builder.AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); -} - -public sealed class BRPlayerInfo : IRankedCTF, IComparable -{ - private readonly BRTeamInfo m_TeamInfo; - private int m_Captures; - - private int m_Kills; - - private int m_Score; - - public BRPlayerInfo(BRTeamInfo teamInfo, Mobile player) - { - m_TeamInfo = teamInfo; - Player = player; - } - - public Mobile Player { get; } - - public int CompareTo(BRPlayerInfo pi) - { - var res = pi.Captures.CompareTo(Captures); - if (res != 0) - { - return res; - } - - res = pi.Score.CompareTo(Score); - - if (res == 0) - { - res = pi.Kills.CompareTo(Kills); - } - - return res; - } - - public string Name => Player.Name; - - public int Kills - { - get => m_Kills; - set - { - m_TeamInfo.Kills += value - m_Kills; - m_Kills = value; - } - } - - public int Captures - { - get => m_Captures; - set - { - m_TeamInfo.Captures += value - m_Captures; - m_Captures = value; - } - } - - public int Score - { - get => m_Score; - set - { - m_TeamInfo.Score += value - m_Score; - m_Score = value; - - if (m_TeamInfo.Leader == null || m_Score > m_TeamInfo.Leader.Score) - { - m_TeamInfo.Leader = this; - } - } - } -} - -[PropertyObject] -public sealed class BRTeamInfo : IRankedCTF, IComparable -{ - private BRGoal m_Goal; - - public BRTeamInfo(int teamID) - { - TeamID = teamID; - Players = new Dictionary(); - } - - public BRTeamInfo(int teamID, IGenericReader ip) - { - TeamID = teamID; - Players = new Dictionary(); - - var version = ip.ReadEncodedInt(); - - switch (version) - { - case 0: - { - Board = ip.ReadEntity(); - TeamName = ip.ReadString(); - Color = ip.ReadEncodedInt(); - m_Goal = ip.ReadEntity(); - break; - } - } - } - - public BRGame Game { get; set; } - - public int TeamID { get; } - - public BRPlayerInfo Leader { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public BRBoard Board { get; set; } - - public Dictionary Players { get; } - - public BRPlayerInfo this[Mobile mob] - { - get - { - if (mob == null) - { - return null; - } - - if (!Players.TryGetValue(mob, out var val)) - { - Players[mob] = val = new BRPlayerInfo(this, mob); - } - - return val; - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public int Color { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public string TeamName { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public BRGoal Goal - { - get => m_Goal; - set - { - m_Goal = value; - m_Goal?.Team = this; - } - } - - public int CompareTo(BRTeamInfo ti) - { - var res = ti.Captures.CompareTo(Captures); - if (res == 0) - { - res = ti.Score.CompareTo(Score); - - if (res == 0) - { - res = ti.Kills.CompareTo(Kills); - } - } - - return res; - } - - public string Name => $"{TeamName} Team"; - - public int Kills { get; set; } - - public int Captures { get; set; } - - public int Score { get; set; } - - public void Reset() - { - Kills = 0; - Captures = 0; - Score = 0; - - Leader = null; - - Players.Clear(); - - Board?.m_TeamInfo = this; - - m_Goal?.Team = this; - } - - public void Serialize(IGenericWriter op) - { - op.WriteEncodedInt(0); // version - - op.Write(Board); - - op.Write(TeamName); - - op.WriteEncodedInt(Color); - - op.Write(m_Goal); - } - - public override string ToString() => TeamName != null ? $"({Name}) ..." : "..."; -} - -public sealed class BRController : EventController -{ - [Constructible] - public BRController() - { - Visible = false; - Movable = false; - - Duration = TimeSpan.FromMinutes(30.0); - - BombHome = Point3D.Zero; - - TeamInfo = new BRTeamInfo[4]; - - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i] = new BRTeamInfo(i); - } - } - - public BRController(Serial serial) - : base(serial) - { - } - - public BRTeamInfo[] TeamInfo { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public BRTeamInfo Team1 => TeamInfo[0]; - - [CommandProperty(AccessLevel.GameMaster)] - public BRTeamInfo Team2 => TeamInfo[1]; - - [CommandProperty(AccessLevel.GameMaster)] - public BRTeamInfo Team3 => TeamInfo[2]; - - [CommandProperty(AccessLevel.GameMaster)] - public BRTeamInfo Team4 => TeamInfo[3]; - - [CommandProperty(AccessLevel.GameMaster)] - public TimeSpan Duration { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public Point3D BombHome { get; set; } - - public override string Title => "Bombing Run"; - public override string DefaultName => "Bombing Run Controller"; - - public override string GetTeamName(int teamID) => TeamInfo[teamID % TeamInfo.Length].Name; - - public override EventGame Construct(DuelContext context) => new BRGame(this, context); - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - - writer.Write(BombHome); - - writer.Write(Duration); - - writer.WriteEncodedInt(TeamInfo.Length); - - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i].Serialize(writer); - } - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - BombHome = reader.ReadPoint3D(); - - Duration = reader.ReadTimeSpan(); - - TeamInfo = new BRTeamInfo[reader.ReadEncodedInt()]; - - for (var i = 0; i < TeamInfo.Length; ++i) + if (teamInfo == null) { - TeamInfo[i] = new BRTeamInfo(i, reader); + continue; } - break; + entries.Add(teamInfo); } - } - } -} -public sealed class BRGame : EventGame -{ - private BRBomb m_Bomb; - - private TimerExecutionToken _finishTimerToken; - - public BRGame(BRController controller, DuelContext context) : base(context) => Controller = controller; - - public BRController Controller { get; } - - public Map Facet => m_Context.Arena != null ? m_Context.Arena.Facet : Controller.Map; - - public override bool CantDoAnything(Mobile mob) => - mob.Backpack?.FindItemByType() != null && GetTeamInfo(mob) != null; - - public void ReturnBomb() - { - if (m_Bomb != null && Controller != null) - { - m_Bomb.Visible = false; - m_Bomb.MoveToWorld(Controller.BombHome, Controller.Map); - Timer.StartTimer(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 15)), UnhideBomb); - } - } - - private void UnhideBomb() - { - if (m_Bomb != null) - { - m_Bomb.Visible = true; - m_Bomb.OnLocationChange(m_Bomb.Location); - } - } - - public void Alert(string text) - { - m_Context.m_Tournament?.Alert(text); - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var p = m_Context.Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - if (p.Players[j] != null) - { - p.Players[j].Mobile.SendMessage(0x35, text); - } - } - } - } - - public BRTeamInfo GetTeamInfo(Mobile mob) - { - var teamID = GetTeamID(mob); - - if (teamID >= 0) - { - return Controller.TeamInfo[teamID % Controller.TeamInfo.Length]; - } - - return null; - } - - public int GetTeamID(Mobile mob) - { - if (mob is not PlayerMobile pm) - { - return mob is BaseCreature creature ? creature.Team - 1 : -1; - } - - if (pm.DuelContext == null || pm.DuelContext != m_Context) - { - return -1; - } - - if (pm.DuelPlayer?.Eliminated != false) - { - return -1; - } - - return pm.DuelContext.Participants.IndexOf(pm.DuelPlayer.Participant); - } - - public int GetColor(Mobile mob) => GetTeamInfo(mob)?.Color ?? -1; - - private void ApplyHues(Participant p, int hueOverride) - { - for (var i = 0; i < p.Players.Length; ++i) - { - if (p.Players[i] != null) - { - p.Players[i].Mobile.SolidHueOverride = hueOverride; - } - } - } - - public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) - { - Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); - } - - private void DelayBounce_Callback(Mobile mob, Container corpse) - { - var dp = (mob as PlayerMobile)?.DuelPlayer; - - m_Context.RemoveAggressions(mob); - - if (dp?.Eliminated == false) - { - mob.MoveToWorld(m_Context.Arena.GetBaseStartPoint(GetTeamID(mob)), Facet); - } - else - { - m_Context.SendOutside(mob); - } - - m_Context.Refresh(mob, corpse); - DuelContext.Debuff(mob); - DuelContext.CancelSpell(mob); - mob.Frozen = false; - } - - public override bool OnDeath(Mobile mob, Container corpse) - { - var killer = mob.FindMostRecentDamager(false); - - var hadBomb = false; - - using (var queue = corpse.EnumerateItemsByType(false)) - { - foreach (var bomb in queue) - { - hadBomb = true; - bomb.DropTo(mob, killer); - } - } - - if (mob.Backpack != null) - { - using var queue = mob.Backpack.EnumerateItemsByType(false); - foreach (var bomb in queue) - { - hadBomb = true; - bomb.DropTo(mob, killer); - } - } - - if (killer?.Player == true) - { - var teamInfo = GetTeamInfo(killer); - var victInfo = GetTeamInfo(mob); - - if (teamInfo != null && teamInfo != victInfo) - { - var playerInfo = teamInfo[killer]; - - if (playerInfo != null) - { - playerInfo.Kills += 1; - playerInfo.Score += 1; // base frag - - if (hadBomb) - { - playerInfo.Score += 4; // fragged bomb carrier - } - } - } - } - - BRBoardGump.DisplayTo(mob, this); - - m_Context.Requip(mob, corpse); - DelayBounce(TimeSpan.FromSeconds(30.0), mob, corpse); - - return false; - } - - public override void OnStart() - { - for (var i = 0; i < Controller.TeamInfo.Length; ++i) - { - var teamInfo = Controller.TeamInfo[i]; - - teamInfo.Game = this; - teamInfo.Reset(); - } - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - ApplyHues( - m_Context.Participants[i], - Controller.TeamInfo[i % Controller.TeamInfo.Length].Color - ); - } - - _finishTimerToken.Cancel(); - - m_Bomb = new BRBomb(this); - ReturnBomb(); - - Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); - } - - private void Finish_Callback() - { - var teams = new List(); - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var teamInfo = Controller.TeamInfo[i % Controller.TeamInfo.Length]; - - if (teamInfo != null) - { - teams.Add(teamInfo); - } - } - - teams.Sort(); - - var tourney = m_Context.m_Tournament; - - using var sb = ValueStringBuilder.Create(256); - - if (tourney != null) - { - if (tourney.TourneyType == TourneyType.FreeForAll) - { - sb.Append(m_Context.Participants.Count * tourney.PlayersPerParticipant); - sb.Append("-man FFA"); - } - else if (tourney.TourneyType == TourneyType.RandomTeam) - { - sb.Append(tourney.ParticipantsPerMatch); - sb.Append("-team"); - } - else if (tourney.TourneyType == TourneyType.RedVsBlue) - { - sb.Append("Red v Blue"); - } - else if (tourney.TourneyType == TourneyType.Faction) - { - sb.Append(tourney.ParticipantsPerMatch); - sb.Append("-team Faction"); + total = entries.Count; } else { - for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) + foreach (var player in section.Players.Values) { - if (sb.Length > 0) + if (player.Score > 0) { - sb.Append('v'); + total++; } + } + } - sb.Append(tourney.PlayersPerParticipant); + entries.Sort(); + + var height = 0; + + if (section == null) + { + height = 73 + entries.Count * 75 + 28; + } + + Closable = false; + + AddPage(0); + + AddBackground(1, 1, 398, height, 3600); + + AddImageTiled(16, 15, 369, height - 29, 3604); + + for (var i = 0; i < total; i += 1) + { + AddImageTiled(22, 58 + i * 75, 357, 70, 0x2430); + } + + AddAlphaRegion(16, 15, 369, height - 29); + + AddImage(215, -45, 0xEE40); + // AddImage( 330, 141, 0x8BA ); + + AddBorderedText(22, 22, 294, 20, "BR Scoreboard".Center(), LabelColor32, BlackColor32); + + AddImageTiled(32, 50, 264, 1, 9107); + AddImageTiled(42, 52, 264, 1, 9157); + + if (section == null) + { + for (var i = 0; i < entries.Count; ++i) + { + var teamInfo = entries[i]; + + AddImage(30, 70 + i * 75, 10152); + AddImage(30, 85 + i * 75, 10151); + AddImage(30, 100 + i * 75, 10151); + AddImage(30, 106 + i * 75, 10154); + + AddImage(24, 60 + i * 75, teamInfo == ourTeam ? 9730 : 9727, teamInfo.Color - 1); + + var borderColor = teamInfo.Color == 0x455 ? LabelColor32 : BlackColor32; + var nameColor = teamInfo.Color switch + { + 0x47E => 0xFFFFFF, + 0x4F2 => 0x3399FF, + 0x4F7 => 0x33FF33, + 0x4FC => 0xFF00FF, + 0x021 => 0xFF3333, + 0x01A => 0xFF66FF, + 0x455 => 0x333333, + _ => LabelColor32 + }; + + AddBorderedText( + 60, + 65 + i * 75, + 250, + 20, + $"{LadderGump.Rank(1 + i)}: {teamInfo.Name}", + nameColor, + borderColor + ); + + AddBorderedText(50 + 10, 85 + i * 75, 100, 20, "Score:", 0xFFC000, BlackColor32); + AddBorderedText(50 + 15, 105 + i * 75, 100, 20, teamInfo.Score.ToString("N0"), 0xFFC000, BlackColor32); + + AddBorderedText(110 + 10, 85 + i * 75, 100, 20, "Kills:", 0xFFC000, BlackColor32); + AddBorderedText(110 + 15, 105 + i * 75, 100, 20, teamInfo.Kills.ToString("N0"), 0xFFC000, BlackColor32); + + AddBorderedText(160 + 10, 85 + i * 75, 100, 20, "Points:", 0xFFC000, BlackColor32); + AddBorderedText( + 160 + 15, + 105 + i * 75, + 100, + 20, + teamInfo.Captures.ToString("N0"), + 0xFFC000, + BlackColor32 + ); + + var pl = teamInfo.Leader; + + AddBorderedText(235 + 10, 85 + i * 75, 250, 20, "Leader:", 0xFFC000, BlackColor32); + + if (pl != null) + { + AddBorderedText(235 + 15, 105 + i * 75, 250, 20, pl.Player.Name, 0xFFC000, BlackColor32); + } + } + } + + AddButton(314, height - 42, 247, 248, 1); + } + + private void AddBorderedText(int x, int y, int width, int height, string text, int color, int borderColor) + { + AddColoredText(x - 1, y - 1, width, height, text, borderColor); + AddColoredText(x - 1, y + 1, width, height, text, borderColor); + AddColoredText(x + 1, y - 1, width, height, text, borderColor); + AddColoredText(x + 1, y + 1, width, height, text, borderColor); + AddColoredText(x, y, width, height, text, color); + } + + private void AddColoredText(int x, int y, int width, int height, string text, int color) + { + AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); + } + } + + public sealed class BRPlayerInfo : IRankedCTF, IComparable + { + private readonly BRTeamInfo m_TeamInfo; + private int m_Captures; + + private int m_Kills; + + private int m_Score; + + public BRPlayerInfo(BRTeamInfo teamInfo, Mobile player) + { + m_TeamInfo = teamInfo; + Player = player; + } + + public Mobile Player { get; } + + public int CompareTo(BRPlayerInfo pi) + { + var res = pi.Captures.CompareTo(Captures); + if (res != 0) + { + return res; + } + + res = pi.Score.CompareTo(Score); + + if (res == 0) + { + res = pi.Kills.CompareTo(Kills); + } + + return res; + } + + public string Name => Player.Name; + + public int Kills + { + get => m_Kills; + set + { + m_TeamInfo.Kills += value - m_Kills; + m_Kills = value; + } + } + + public int Captures + { + get => m_Captures; + set + { + m_TeamInfo.Captures += value - m_Captures; + m_Captures = value; + } + } + + public int Score + { + get => m_Score; + set + { + m_TeamInfo.Score += value - m_Score; + m_Score = value; + + if (m_TeamInfo.Leader == null || m_Score > m_TeamInfo.Leader.Score) + { + m_TeamInfo.Leader = this; + } + } + } + } + + [PropertyObject] + public sealed class BRTeamInfo : IRankedCTF, IComparable + { + private BRGoal m_Goal; + + public BRTeamInfo(int teamID) + { + TeamID = teamID; + Players = new Dictionary(); + } + + public BRTeamInfo(int teamID, IGenericReader ip) + { + TeamID = teamID; + Players = new Dictionary(); + + var version = ip.ReadEncodedInt(); + + switch (version) + { + case 0: + { + Board = ip.ReadEntity(); + TeamName = ip.ReadString(); + Color = ip.ReadEncodedInt(); + m_Goal = ip.ReadEntity(); + break; + } + } + } + + public BRGame Game { get; set; } + + public int TeamID { get; } + + public BRPlayerInfo Leader { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public BRBoard Board { get; set; } + + public Dictionary Players { get; } + + public BRPlayerInfo this[Mobile mob] + { + get + { + if (mob == null) + { + return null; + } + + if (!Players.TryGetValue(mob, out var val)) + { + Players[mob] = val = new BRPlayerInfo(this, mob); + } + + return val; + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public int Color { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public string TeamName { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public BRGoal Goal + { + get => m_Goal; + set + { + m_Goal = value; + if (m_Goal != null) + { + m_Goal.Team = this; } } } - if (Controller != null) + public int CompareTo(BRTeamInfo ti) { - sb.Append(' '); - sb.Append(Controller.Title); + var res = ti.Captures.CompareTo(Captures); + if (res == 0) + { + res = ti.Score.CompareTo(Score); + + if (res == 0) + { + res = ti.Kills.CompareTo(Kills); + } + } + + return res; } - var title = sb.ToString(); + public string Name => $"{TeamName} Team"; - var winner = teams.Count > 0 ? teams[0] : null; + public int Kills { get; set; } - for (var i = 0; i < teams.Count; ++i) + public int Captures { get; set; } + + public int Score { get; set; } + + public void Reset() { - var rank = i switch + Kills = 0; + Captures = 0; + Score = 0; + + Leader = null; + + Players.Clear(); + + if (Board != null) { - 0 => TrophyRank.Gold, - 1 => TrophyRank.Silver, - _ => TrophyRank.Bronze - }; + Board.m_TeamInfo = this; + } - var leader = teams[i].Leader; - - foreach (var pl in teams[i].Players.Values) + if (m_Goal != null) { - var mob = pl.Player; + m_Goal.Team = this; + } + } - if (mob == null) - { - continue; - } + public void Serialize(IGenericWriter op) + { + op.WriteEncodedInt(0); // version - sb.Reset(); + op.Write(Board); - sb.Append(title); + op.Write(TeamName); - if (pl == leader) - { - sb.Append(" Leader"); - } + op.WriteEncodedInt(Color); - if (pl.Score > 0) - { - sb.Append(": "); + op.Write(m_Goal); + } - // sb.Append( pl.Score.ToString( "N0" ) ); - // sb.Append( pl.Score == 1 ? " point" : " points" ); + public override string ToString() + { + if (TeamName != null) + { + return $"({Name}) ..."; + } - sb.Append(pl.Kills.ToString("N0")); - sb.Append(pl.Kills == 1 ? " kill" : " kills"); + return "..."; + } + } - if (pl.Captures > 0) + public sealed class BRController : EventController + { + [Constructible] + public BRController() + { + Visible = false; + Movable = false; + + Duration = TimeSpan.FromMinutes(30.0); + + BombHome = Point3D.Zero; + + TeamInfo = new BRTeamInfo[4]; + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i] = new BRTeamInfo(i); + } + } + + public BRController(Serial serial) + : base(serial) + { + } + + public BRTeamInfo[] TeamInfo { get; private set; } + + [CommandProperty(AccessLevel.GameMaster)] + public BRTeamInfo Team1 => TeamInfo[0]; + + [CommandProperty(AccessLevel.GameMaster)] + public BRTeamInfo Team2 => TeamInfo[1]; + + [CommandProperty(AccessLevel.GameMaster)] + public BRTeamInfo Team3 => TeamInfo[2]; + + [CommandProperty(AccessLevel.GameMaster)] + public BRTeamInfo Team4 => TeamInfo[3]; + + [CommandProperty(AccessLevel.GameMaster)] + public TimeSpan Duration { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public Point3D BombHome { get; set; } + + public override string Title => "Bombing Run"; + public override string DefaultName => "Bombing Run Controller"; + + public override string GetTeamName(int teamID) => TeamInfo[teamID % TeamInfo.Length].Name; + + public override EventGame Construct(DuelContext context) => new BRGame(this, context); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + + writer.Write(BombHome); + + writer.Write(Duration); + + writer.WriteEncodedInt(TeamInfo.Length); + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i].Serialize(writer); + } + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: { - sb.Append(", "); - sb.Append(pl.Captures.ToString("N0")); - sb.Append(pl.Captures == 1 ? " point" : " points"); + BombHome = reader.ReadPoint3D(); + + Duration = reader.ReadTimeSpan(); + + TeamInfo = new BRTeamInfo[reader.ReadEncodedInt()]; + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i] = new BRTeamInfo(i, reader); + } + + break; + } + } + } + } + + public sealed class BRGame : EventGame + { + private BRBomb m_Bomb; + + private TimerExecutionToken _finishTimerToken; + + public BRGame(BRController controller, DuelContext context) : base(context) => Controller = controller; + + public BRController Controller { get; } + + public Map Facet => m_Context.Arena != null ? m_Context.Arena.Facet : Controller.Map; + + public override bool CantDoAnything(Mobile mob) => + mob.Backpack?.FindItemByType() != null && GetTeamInfo(mob) != null; + + public void ReturnBomb() + { + if (m_Bomb != null && Controller != null) + { + m_Bomb.Visible = false; + m_Bomb.MoveToWorld(Controller.BombHome, Controller.Map); + Timer.StartTimer(TimeSpan.FromSeconds(Utility.RandomMinMax(5, 15)), UnhideBomb); + } + } + + private void UnhideBomb() + { + if (m_Bomb != null) + { + m_Bomb.Visible = true; + m_Bomb.OnLocationChange(m_Bomb.Location); + } + } + + public void Alert(string text) + { + m_Context.m_Tournament?.Alert(text); + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var p = m_Context.Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + if (p.Players[j] != null) + { + p.Players[j].Mobile.SendMessage(0x35, text); } } + } + } - Item item = new Trophy(sb.ToString(), rank); + public BRTeamInfo GetTeamInfo(Mobile mob) + { + var teamID = GetTeamID(mob); - if (pl == leader) + if (teamID >= 0) + { + return Controller.TeamInfo[teamID % Controller.TeamInfo.Length]; + } + + return null; + } + + public int GetTeamID(Mobile mob) + { + if (mob is not PlayerMobile pm) + { + return mob is BaseCreature creature ? creature.Team - 1 : -1; + } + + if (pm.DuelContext == null || pm.DuelContext != m_Context) + { + return -1; + } + + if (pm.DuelPlayer?.Eliminated != false) + { + return -1; + } + + return pm.DuelContext.Participants.IndexOf(pm.DuelPlayer.Participant); + } + + public int GetColor(Mobile mob) => GetTeamInfo(mob)?.Color ?? -1; + + private void ApplyHues(Participant p, int hueOverride) + { + for (var i = 0; i < p.Players.Length; ++i) + { + if (p.Players[i] != null) { - item.ItemID = 4810; + p.Players[i].Mobile.SolidHueOverride = hueOverride; } + } + } - item.Name = $"{item.Name}, {teams[i].Name.ToLower()} team"; + public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) + { + Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); + } - if (!mob.PlaceInBackpack(item)) + private void DelayBounce_Callback(Mobile mob, Container corpse) + { + var dp = (mob as PlayerMobile)?.DuelPlayer; + + m_Context.RemoveAggressions(mob); + + if (dp?.Eliminated == false) + { + mob.MoveToWorld(m_Context.Arena.GetBaseStartPoint(GetTeamID(mob)), Facet); + } + else + { + m_Context.SendOutside(mob); + } + + m_Context.Refresh(mob, corpse); + DuelContext.Debuff(mob); + DuelContext.CancelSpell(mob); + mob.Frozen = false; + } + + public override bool OnDeath(Mobile mob, Container corpse) + { + var killer = mob.FindMostRecentDamager(false); + + var hadBomb = false; + + foreach (var bomb in corpse.EnumerateItemsByType(false)) + { + hadBomb = true; + bomb.DropTo(mob, killer); + } + + if (mob.Backpack != null) + { + foreach (var bomb in mob.Backpack.EnumerateItemsByType(false)) { - mob.BankBox.DropItem(item); + hadBomb = true; + bomb.DropTo(mob, killer); } + } - var cash = pl.Score * 250; + if (killer?.Player == true) + { + var teamInfo = GetTeamInfo(killer); + var victInfo = GetTeamInfo(mob); - if (cash > 0) + if (teamInfo != null && teamInfo != victInfo) { - item = new BankCheck(cash); + var playerInfo = teamInfo[killer]; + + if (playerInfo != null) + { + playerInfo.Kills += 1; + playerInfo.Score += 1; // base frag + + if (hadBomb) + { + playerInfo.Score += 4; // fragged bomb carrier + } + } + } + } + + mob.SendGump(new BRBoardGump(mob, this)); + + m_Context.Requip(mob, corpse); + DelayBounce(TimeSpan.FromSeconds(30.0), mob, corpse); + + return false; + } + + public override void OnStart() + { + for (var i = 0; i < Controller.TeamInfo.Length; ++i) + { + var teamInfo = Controller.TeamInfo[i]; + + teamInfo.Game = this; + teamInfo.Reset(); + } + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + ApplyHues( + m_Context.Participants[i], + Controller.TeamInfo[i % Controller.TeamInfo.Length].Color + ); + } + + _finishTimerToken.Cancel(); + + m_Bomb = new BRBomb(this); + ReturnBomb(); + + Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); + } + + private void Finish_Callback() + { + var teams = new List(); + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var teamInfo = Controller.TeamInfo[i % Controller.TeamInfo.Length]; + + if (teamInfo != null) + { + teams.Add(teamInfo); + } + } + + teams.Sort(); + + var tourney = m_Context.m_Tournament; + + var sb = new StringBuilder(); + + if (tourney != null) + { + if (tourney.TourneyType == TourneyType.FreeForAll) + { + sb.Append(m_Context.Participants.Count * tourney.PlayersPerParticipant); + sb.Append("-man FFA"); + } + else if (tourney.TourneyType == TourneyType.RandomTeam) + { + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-team"); + } + else if (tourney.TourneyType == TourneyType.RedVsBlue) + { + sb.Append("Red v Blue"); + } + else if (tourney.TourneyType == TourneyType.Faction) + { + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-team Faction"); + } + else + { + for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) + { + if (sb.Length > 0) + { + sb.Append('v'); + } + + sb.Append(tourney.PlayersPerParticipant); + } + } + } + + if (Controller != null) + { + sb.Append(' ').Append(Controller.Title); + } + + var title = sb.ToString(); + + var winner = teams.Count > 0 ? teams[0] : null; + + for (var i = 0; i < teams.Count; ++i) + { + var rank = i switch + { + 0 => TrophyRank.Gold, + 1 => TrophyRank.Silver, + _ => TrophyRank.Bronze + }; + + var leader = teams[i].Leader; + + foreach (var pl in teams[i].Players.Values) + { + var mob = pl.Player; + + if (mob == null) + { + continue; + } + + sb = new StringBuilder(); + + sb.Append(title); + + if (pl == leader) + { + sb.Append(" Leader"); + } + + if (pl.Score > 0) + { + sb.Append(": "); + + // sb.Append( pl.Score.ToString( "N0" ) ); + // sb.Append( pl.Score == 1 ? " point" : " points" ); + + sb.Append(pl.Kills.ToString("N0")); + sb.Append(pl.Kills == 1 ? " kill" : " kills"); + + if (pl.Captures > 0) + { + sb.Append(", "); + sb.Append(pl.Captures.ToString("N0")); + sb.Append(pl.Captures == 1 ? " point" : " points"); + } + } + + Item item = new Trophy(sb.ToString(), rank); + + if (pl == leader) + { + item.ItemID = 4810; + } + + item.Name = $"{item.Name}, {teams[i].Name.ToLower()} team"; if (!mob.PlaceInBackpack(item)) { mob.BankBox.DropItem(item); } - mob.SendMessage( //There is no formatting flag for Lowercase, we may need a custom interface to get rid of it - $"You have been awarded a {rank.LowerName()} trophy and {cash:N0}gp for your participation in this tournament." - ); + var cash = pl.Score * 250; + + if (cash > 0) + { + item = new BankCheck(cash); + + if (!mob.PlaceInBackpack(item)) + { + mob.BankBox.DropItem(item); + } + + mob.SendMessage( //There is no formatting flag for Lowercase, we may need a custom interface to get rid of it + $"You have been awarded a {rank.ToString().ToLower()} trophy and {cash:N0}gp for your participation in this tournament." + ); + } + else + { + mob.SendMessage( + $"You have been awarded a {rank.ToString().ToLower()} trophy for your participation in this tournament." + ); + } } - else + } + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var p = m_Context.Participants[i]; + + if (p?.Players == null) { - mob.SendMessage( - $"You have been awarded a {rank.LowerName()} trophy for your participation in this tournament." - ); + continue; } - } - } - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var p = m_Context.Participants[i]; - - if (p?.Players == null) - { - continue; - } - - for (var j = 0; j < p.Players.Length; ++j) - { - var dp = p.Players[j]; - - if (dp?.Mobile != null) - { - BRBoardGump.DisplayTo(dp.Mobile, this); - } - } - - if (i == winner?.TeamID) - { - continue; - } - - if (p.Players != null) - { for (var j = 0; j < p.Players.Length; ++j) { - p.Players[j]?.Eliminated = true; + var dp = p.Players[j]; + + if (dp?.Mobile != null) + { + dp.Mobile.SendGump(new BRBoardGump(dp.Mobile, this)); + } } + + if (i == winner?.TeamID) + { + continue; + } + + if (p.Players != null) + { + for (var j = 0; j < p.Players.Length; ++j) + { + if (p.Players[j] != null) + { + p.Players[j].Eliminated = true; + } + } + } + } + + if (winner != null) + { + m_Context.Finish(m_Context.Participants[winner.TeamID]); } } - if (winner != null) + public override void OnStop() { - m_Context.Finish(m_Context.Participants[winner.TeamID]); + for (var i = 0; i < Controller.TeamInfo.Length; ++i) + { + var teamInfo = Controller.TeamInfo[i]; + + if (teamInfo.Board != null) + { + teamInfo.Board.m_TeamInfo = null; + } + + teamInfo.Game = null; + } + + ReturnBomb(); + + m_Bomb?.Delete(); + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + ApplyHues(m_Context.Participants[i], -1); + } + + _finishTimerToken.Cancel(); } } - - public override void OnStop() - { - for (var i = 0; i < Controller.TeamInfo.Length; ++i) - { - var teamInfo = Controller.TeamInfo[i]; - - teamInfo.Board?.m_TeamInfo = null; - - teamInfo.Game = null; - } - - ReturnBomb(); - - m_Bomb?.Delete(); - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - ApplyHues(m_Context.Participants[i], -1); - } - - _finishTimerToken.Cancel(); - } } diff --git a/Projects/UOContent/Engines/ConPVP/Games/CTF.cs b/Projects/UOContent/Engines/ConPVP/Games/CTF.cs index d9e879820..033475c38 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/CTF.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/CTF.cs @@ -1,1267 +1,1313 @@ using System; using System.Collections.Generic; -using ModernUO.Serialization; +using System.Text; using Server.Gumps; using Server.Items; using Server.Mobiles; using Server.Network; using Server.Targeting; -using Server.Text; -namespace Server.Engines.ConPVP; - -[SerializationGenerator(0, false)] -public sealed partial class CTFBoard : Item +namespace Server.Engines.ConPVP { - public CTFTeamInfo m_TeamInfo; - - [Constructible] - public CTFBoard() : base(7774) => Movable = false; - - public override string DefaultName => "scoreboard"; - - public override void OnDoubleClick(Mobile from) + public sealed class CTFBoard : Item { - if (m_TeamInfo?.Game != null) + public CTFTeamInfo m_TeamInfo; + + [Constructible] + public CTFBoard() + : base(7774) => + Movable = false; + + public CTFBoard(Serial serial) + : base(serial) { - CTFBoardGump.DisplayTo(from, m_TeamInfo.Game); - } - } -} - -public class CTFBoardGump : DynamicGump -{ - private const int LabelColor32 = 0xFFFFFF; - private const int BlackColor32 = 0x000000; - - private readonly Mobile _mob; - private readonly CTFGame _game; - private readonly CTFTeamInfo _section; - - public override bool Singleton => true; - - private CTFBoardGump(Mobile mob, CTFGame game, CTFTeamInfo section) - : base(60, 60) - { - _mob = mob; - _game = game; - _section = section; - } - - public static void DisplayTo(Mobile mob, CTFGame game, CTFTeamInfo section = null) - { - if (mob?.NetState == null || game == null) - { - return; } - mob.SendGump(new CTFBoardGump(mob, game, section)); - } + public override string DefaultName => "scoreboard"; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var ourTeam = _game.GetTeamInfo(_mob); - - var entries = new List(); - - if (_section == null) + public override void OnDoubleClick(Mobile from) { - for (var i = 0; i < _game.Context.Participants.Count; ++i) + if (m_TeamInfo?.Game != null) { - var teamInfo = _game.Controller.TeamInfo[i % 8]; + from.SendGump(new CTFBoardGump(from, m_TeamInfo.Game)); + } + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } + } + + public class CTFBoardGump : Gump + { + private const int LabelColor32 = 0xFFFFFF; + private const int BlackColor32 = 0x000000; + + private CTFGame m_Game; + + public override bool Singleton => true; + + public CTFBoardGump(Mobile mob, CTFGame game, CTFTeamInfo section = null) + : base(60, 60) + { + m_Game = game; + + var ourTeam = game.GetTeamInfo(mob); + + var entries = new List(); + + if (section == null) + { + for (var i = 0; i < game.Context.Participants.Count; ++i) + { + var teamInfo = game.Controller.TeamInfo[i % 8]; + + if (teamInfo?.Flag == null) + { + continue; + } + + entries.Add(teamInfo); + } + } + else + { + foreach (var player in section.Players.Values) + { + if (player.Score > 0) + { + entries.Add(player); + } + } + } + + entries.Sort((a, b) => b.Score - a.Score); + + var height = 0; + + if (section == null) + { + height = 73 + entries.Count * 75 + 28; + } + + Closable = false; + + AddPage(0); + + AddBackground(1, 1, 398, height, 3600); + + AddImageTiled(16, 15, 369, height - 29, 3604); + + for (var i = 0; i < entries.Count; i += 1) + { + AddImageTiled(22, 58 + i * 75, 357, 70, 0x2430); + } + + AddAlphaRegion(16, 15, 369, height - 29); + + AddImage(215, -45, 0xEE40); + // AddImage( 330, 141, 0x8BA ); + + AddBorderedText(22, 22, 294, 20, "CTF Scoreboard".Center(), LabelColor32, BlackColor32); + + AddImageTiled(32, 50, 264, 1, 9107); + AddImageTiled(42, 52, 264, 1, 9157); + + if (section == null) + { + for (var i = 0; i < entries.Count; ++i) + { + var teamInfo = entries[i] as CTFTeamInfo; + + if (teamInfo == null) + { + continue; + } + + AddImage(30, 70 + i * 75, 10152); + AddImage(30, 85 + i * 75, 10151); + AddImage(30, 100 + i * 75, 10151); + AddImage(30, 106 + i * 75, 10154); + + AddImage(24, 60 + i * 75, teamInfo == ourTeam ? 9730 : 9727, teamInfo.Color - 1); + + var borderColor = teamInfo.Color == 0x455 ? LabelColor32 : BlackColor32; + var nameColor = teamInfo.Color switch + { + 0x47E => 0xFFFFFF, + 0x4F2 => 0x3399FF, + 0x4F7 => 0x33FF33, + 0x4FC => 0xFF00FF, + 0x021 => 0xFF3333, + 0x01A => 0xFF66FF, + 0x455 => 0x333333, + _ => LabelColor32 + }; + + AddBorderedText( + 60, + 65 + i * 75, + 250, + 20, + $"{LadderGump.Rank(1 + i)}: {teamInfo.Name} Team", + nameColor, + borderColor + ); + + AddBorderedText(50 + 10, 85 + i * 75, 100, 20, "Score:", 0xFFC000, BlackColor32); + AddBorderedText(50 + 15, 105 + i * 75, 100, 20, teamInfo.Score.ToString("N0"), 0xFFC000, BlackColor32); + + AddBorderedText(110 + 10, 85 + i * 75, 100, 20, "Kills:", 0xFFC000, BlackColor32); + AddBorderedText(110 + 15, 105 + i * 75, 100, 20, teamInfo.Kills.ToString("N0"), 0xFFC000, BlackColor32); + + AddBorderedText(160 + 10, 85 + i * 75, 100, 20, "Captures:", 0xFFC000, BlackColor32); + AddBorderedText( + 160 + 15, + 105 + i * 75, + 100, + 20, + teamInfo.Captures.ToString("N0"), + 0xFFC000, + BlackColor32 + ); + + var pl = teamInfo.Leader; + + AddBorderedText(235 + 10, 85 + i * 75, 250, 20, "Leader:", 0xFFC000, BlackColor32); + + if (pl != null) + { + AddBorderedText(235 + 15, 105 + i * 75, 250, 20, pl.Player.Name, 0xFFC000, BlackColor32); + } + } + } + + AddButton(314, height - 42, 247, 248, 1); + } + + private void AddBorderedText(int x, int y, int width, int height, string text, int color, int borderColor) + { + AddColoredText(x - 1, y - 1, width, height, text, borderColor); + AddColoredText(x - 1, y + 1, width, height, text, borderColor); + AddColoredText(x + 1, y - 1, width, height, text, borderColor); + AddColoredText(x + 1, y + 1, width, height, text, borderColor); + AddColoredText(x, y, width, height, text, color); + } + + private void AddColoredText(int x, int y, int width, int height, string text, int color) + { + AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); + } + } + + public sealed class CTFFlag : Item + { + public Mobile m_Fragger; + public DateTime m_FragTime; + + private int m_ReturnCount; + + public Mobile m_Returner; + public DateTime m_ReturnTime; + private TimerExecutionToken _returnTimerToken; + public CTFTeamInfo m_TeamInfo; + + [Constructible] + public CTFFlag() + : base(5643) => + Movable = false; + + public CTFFlag(Serial serial) + : base(serial) + { + } + + public override string DefaultName => "old people cookies"; + + public override void OnDoubleClick(Mobile from) + { + if (m_TeamInfo?.Game != null) + { + var ourTeam = m_TeamInfo; + var useTeam = m_TeamInfo.Game.GetTeamInfo(from); + + if (ourTeam == null || useTeam == null) + { + return; + } + + if (IsChildOf(from.Backpack)) + { + from.BeginTarget(1, false, TargetFlags.None, Flag_OnTarget); + } + else if (!from.InRange(this, 1) || !from.InLOS(this)) + { + from.LocalOverheadMessage(MessageType.Regular, 0x26, 1019045); // I can't reach that + } + else if (ourTeam == useTeam) + { + if (Location == m_TeamInfo.Origin && Map == m_TeamInfo.Game.Facet) + { + from.NetState.SendMessage( + Serial, + ItemID, + MessageType.Regular, + 0x3B2, + 3, + false, + "ENU", + Name, + "Touch me not for I am chaste." + ); + } + else + { + var playerInfo = useTeam[from]; + + if (playerInfo != null) + { + playerInfo.Score += 4; // return + } + + m_Returner = from; + m_ReturnTime = Core.Now; + + SendHome(); + + from.LocalOverheadMessage(MessageType.Regular, 0x59, false, "You returned the cookies!"); + m_TeamInfo.Game.Alert($"The {from.Name} cookies have been returned by {ourTeam.Name}."); + } + } + else if (!from.PlaceInBackpack(this)) + { + from.LocalOverheadMessage(MessageType.Regular, 0x26, false, "I can't hold that."); + } + else + { + from.RevealingAction(); + + from.LocalOverheadMessage(MessageType.Regular, 0x59, false, "You stole the cookies!"); + m_TeamInfo.Game.Alert($"The {ourTeam.Name} cookies have been stolen by {from.Name} ({useTeam.Name})."); + + BeginCountdown(120); + } + } + } + + public override void Delete() + { + if (Parent != null) + { + SendHome(); + return; + } + + base.Delete(); + } + + public void DropTo(Mobile mob, Mobile killer) + { + m_Fragger = killer; + m_FragTime = Core.Now; + + if (mob != null) + { + MoveToWorld(new Point3D(mob.X, mob.Y, mob.Z + 2), mob.Map); + + m_ReturnCount = Math.Min(m_ReturnCount, 10); + } + else + { + SendHome(); + } + } + + private void BeginCountdown(int returnCount) + { + _returnTimerToken.Cancel(); + + Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), Countdown_OnTick, out _returnTimerToken); + m_ReturnCount = returnCount; + } + + private void Countdown_OnTick() + { + var owner = RootParent as Mobile; + + switch (m_ReturnCount) + { + case 60: + case 30: + case 15: + case 10: + case 5: + case 4: + case 3: + case 2: + case 1: + { + if (m_ReturnCount == 1) + { + owner?.SendMessage(0x26, $"You have {m_ReturnCount} second to capture the cookies!"); + } + else + { + owner?.SendMessage(0x26, $"You have {m_ReturnCount} seconds to capture the cookies!"); + } + + break; + } + + case 0: + { + if (owner != null) + { + owner.SendMessage(0x26, "You have taken too long to capture the cookies!"); + owner.Kill(); + } + + SendHome(); + + m_TeamInfo?.Game?.Alert($"The {m_TeamInfo.Name} cookies have been returned."); + + return; + } + } + + --m_ReturnCount; + } + + private void Flag_OnTarget(Mobile from, object obj) + { + if (m_TeamInfo == null) + { + return; + } + + if (!IsChildOf(from.Backpack)) + { + return; + } + + var ourTeam = m_TeamInfo; + var useTeam = m_TeamInfo.Game.GetTeamInfo(from); + + if (obj is CTFFlag) + { + if (obj == useTeam.Flag) + { + from.LocalOverheadMessage(MessageType.Regular, 0x59, false, "You captured the cookies!"); + m_TeamInfo.Game.Alert($"{from.Name} captured the {ourTeam.Name} cookies!"); + + SendHome(); + + var playerInfo = useTeam[from]; + + if (playerInfo != null) + { + playerInfo.Captures += 1; + playerInfo.Score += 50; // capture + + var teamFlag = useTeam.Flag; + + if (teamFlag.m_Fragger != null && + Core.Now < teamFlag.m_FragTime + TimeSpan.FromSeconds(5.0) && + m_TeamInfo.Game.GetTeamInfo(teamFlag.m_Fragger) == useTeam) + { + var assistInfo = useTeam[teamFlag.m_Fragger]; + + if (assistInfo != null) + { + assistInfo.Score += 6; // frag assist + } + } + + if (teamFlag.m_Returner != null && + Core.Now < teamFlag.m_ReturnTime + TimeSpan.FromSeconds(5.0)) + { + var assistInfo = useTeam[teamFlag.m_Returner]; + + if (assistInfo != null) + { + assistInfo.Score += 4; // return assist + } + } + } + } + else + { + from.LocalOverheadMessage(MessageType.Regular, 0x26, false, "Those are not my cookies."); + } + } + else if (obj is Mobile passTo) + { + var passTeam = m_TeamInfo.Game.GetTeamInfo(passTo); + + if (passTo == from) + { + from.LocalOverheadMessage(MessageType.Regular, 0x26, false, "I can't pass to them."); + } + else if (passTeam == useTeam && passTo.PlaceInBackpack(this)) + { + passTo.LocalOverheadMessage( + MessageType.Regular, + 0x59, + false, + $"{from.Name} has passed you the cookies!" + ); + } + else + { + from.LocalOverheadMessage(MessageType.Regular, 0x26, false, "I can't pass to them."); + } + } + } + + public void SendHome() + { + _returnTimerToken.Cancel(); + + if (m_TeamInfo == null) + { + return; + } + + MoveToWorld(m_TeamInfo.Origin, m_TeamInfo.Game.Facet); + } + + private Mobile FindOwner(IEntity parent) + { + if (parent is Item item) + { + return item.RootParent as Mobile; + } + + if (parent is Mobile mobile) + { + return mobile; + } + + return null; + } + + public override void OnAdded(IEntity parent) + { + base.OnAdded(parent); + + var mob = FindOwner(parent); + + if (mob != null) + { + mob.SolidHueOverride = 0x4001; + } + } + + public override void OnRemoved(IEntity parent) + { + base.OnRemoved(parent); + + var mob = FindOwner(parent); + + if (mob != null) + { + mob.SolidHueOverride = m_TeamInfo?.Game.GetColor(mob) ?? -1; + } + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } + } + + public interface IRankedCTF + { + int Kills { get; } + int Captures { get; } + int Score { get; } + string Name { get; } + } + + public sealed class CTFPlayerInfo : IRankedCTF + { + private readonly CTFTeamInfo m_TeamInfo; + private int m_Captures; + + private int m_Kills; + + private int m_Score; + + public CTFPlayerInfo(CTFTeamInfo teamInfo, Mobile player) + { + m_TeamInfo = teamInfo; + Player = player; + } + + public Mobile Player { get; } + + string IRankedCTF.Name => Player.Name; + + public int Kills + { + get => m_Kills; + set + { + m_TeamInfo.Kills += value - m_Kills; + m_Kills = value; + } + } + + public int Captures + { + get => m_Captures; + set + { + m_TeamInfo.Captures += value - m_Captures; + m_Captures = value; + } + } + + public int Score + { + get => m_Score; + set + { + m_TeamInfo.Score += value - m_Score; + m_Score = value; + + if (m_TeamInfo.Leader == null || m_Score > m_TeamInfo.Leader.Score) + { + m_TeamInfo.Leader = this; + } + } + } + } + + [PropertyObject] + public sealed class CTFTeamInfo : IRankedCTF + { + public CTFTeamInfo(int teamID) + { + TeamID = teamID; + Players = new Dictionary(); + } + + public CTFTeamInfo(int teamID, IGenericReader ip) + { + TeamID = teamID; + Players = new Dictionary(); + + var version = ip.ReadEncodedInt(); + + switch (version) + { + case 2: + { + Board = ip.ReadEntity(); + + goto case 1; + } + case 1: + { + Name = ip.ReadString(); + + goto case 0; + } + case 0: + { + Color = ip.ReadEncodedInt(); + + Flag = ip.ReadEntity(); + Origin = ip.ReadPoint3D(); + break; + } + } + } + + public CTFGame Game { get; set; } + + public int TeamID { get; } + + public CTFPlayerInfo Leader { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public CTFBoard Board { get; set; } + + public Dictionary Players { get; } + + public CTFPlayerInfo this[Mobile mob] + { + get + { + if (mob == null) + { + return null; + } + + if (!Players.TryGetValue(mob, out var val)) + { + Players[mob] = val = new CTFPlayerInfo(this, mob); + } + + return val; + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public int Color { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public string Name { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public CTFFlag Flag { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public Point3D Origin { get; set; } + + string IRankedCTF.Name => $"{Name} Team"; + + public int Kills { get; set; } + + public int Captures { get; set; } + + public int Score { get; set; } + + public void Reset() + { + Kills = 0; + Captures = 0; + + Score = 0; + + Leader = null; + + Players.Clear(); + + if (Flag != null) + { + Flag.m_TeamInfo = this; + Flag.Hue = Color; + Flag.SendHome(); + } + + if (Board != null) + { + Board.m_TeamInfo = this; + } + } + + public void Serialize(IGenericWriter op) + { + op.WriteEncodedInt(2); // version + + op.Write(Board); + + op.Write(Name); + + op.WriteEncodedInt(Color); + + op.Write(Flag); + op.Write(Origin); + } + + public override string ToString() => "..."; + } + + public sealed class CTFController : EventController + { + [Constructible] + public CTFController() + { + Visible = false; + Movable = false; + + Duration = TimeSpan.FromMinutes(30.0); + + TeamInfo = new CTFTeamInfo[8]; + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i] = new CTFTeamInfo(i); + } + } + + public CTFController(Serial serial) + : base(serial) + { + } + + public CTFTeamInfo[] TeamInfo { get; private set; } + + [CommandProperty(AccessLevel.GameMaster)] + public CTFTeamInfo Team1 => TeamInfo[0]; + + [CommandProperty(AccessLevel.GameMaster)] + public CTFTeamInfo Team2 => TeamInfo[1]; + + [CommandProperty(AccessLevel.GameMaster)] + public CTFTeamInfo Team3 => TeamInfo[2]; + + [CommandProperty(AccessLevel.GameMaster)] + public CTFTeamInfo Team4 => TeamInfo[3]; + + [CommandProperty(AccessLevel.GameMaster)] + public CTFTeamInfo Team5 => TeamInfo[4]; + + [CommandProperty(AccessLevel.GameMaster)] + public CTFTeamInfo Team6 => TeamInfo[5]; + + [CommandProperty(AccessLevel.GameMaster)] + public CTFTeamInfo Team7 => TeamInfo[6]; + + [CommandProperty(AccessLevel.GameMaster)] + public CTFTeamInfo Team8 => TeamInfo[7]; + + [CommandProperty(AccessLevel.GameMaster)] + public TimeSpan Duration { get; set; } + + public override string Title => "CTF"; + + public override string GetTeamName(int teamID) => TeamInfo[teamID % TeamInfo.Length].Name; + + public override EventGame Construct(DuelContext context) => new CTFGame(this, context); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(2); + + writer.Write(Duration); + + writer.WriteEncodedInt(TeamInfo.Length); + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i].Serialize(writer); + } + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 2: + { + Duration = reader.ReadTimeSpan(); + + goto case 1; + } + case 1: + { + TeamInfo = new CTFTeamInfo[reader.ReadEncodedInt()]; + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i] = new CTFTeamInfo(i, reader); + } + + break; + } + case 0: + { + TeamInfo = new CTFTeamInfo[8]; + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i] = new CTFTeamInfo(i); + } + + break; + } + } + + if (version < 2) + { + Duration = TimeSpan.FromMinutes(30.0); + } + } + } + + public sealed class CTFGame : EventGame + { + private TimerExecutionToken _finishTimerToken; + + public CTFGame(CTFController controller, DuelContext context) : base(context) => Controller = controller; + + public CTFController Controller { get; } + + public Map Facet + { + get + { + if (m_Context.Arena != null) + { + return m_Context.Arena.Facet; + } + + return Controller.Map; + } + } + + public static void Initialize() + { + for (var i = 0x7C9; i <= 0x7D0; ++i) + { + TileData.ItemTable[i].Flags |= TileFlag.NoShoot; + } + } + + public void Alert(string text) + { + m_Context.m_Tournament?.Alert(text); + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var p = m_Context.Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + if (p.Players[j] != null) + { + p.Players[j].Mobile.SendMessage(0x35, text); + } + } + } + } + + public CTFTeamInfo GetTeamInfo(Mobile mob) + { + var teamID = GetTeamID(mob); + + if (teamID >= 0) + { + return Controller.TeamInfo[teamID % Controller.TeamInfo.Length]; + } + + return null; + } + + public int GetTeamID(Mobile mob) + { + if (mob is not PlayerMobile pm) + { + return -1; + } + + if (pm.DuelContext == null || pm.DuelContext != m_Context) + { + return -1; + } + + if (pm.DuelPlayer?.Eliminated != false) + { + return -1; + } + + return pm.DuelContext.Participants.IndexOf(pm.DuelPlayer.Participant); + } + + public int GetColor(Mobile mob) + { + var teamInfo = GetTeamInfo(mob); + + if (teamInfo != null) + { + return teamInfo.Color; + } + + return -1; + } + + private void ApplyHues(Participant p, int hueOverride) + { + for (var i = 0; i < p.Players.Length; ++i) + { + if (p.Players[i] != null) + { + p.Players[i].Mobile.SolidHueOverride = hueOverride; + } + } + } + + public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) + { + Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); + } + + private void DelayBounce_Callback(Mobile mob, Container corpse) + { + var dp = (mob as PlayerMobile)?.DuelPlayer; + + m_Context.RemoveAggressions(mob); + + if (dp?.Eliminated == false) + { + mob.MoveToWorld(m_Context.Arena.GetBaseStartPoint(GetTeamID(mob)), Facet); + } + else + { + m_Context.SendOutside(mob); + } + + m_Context.Refresh(mob, corpse); + DuelContext.Debuff(mob); + DuelContext.CancelSpell(mob); + mob.Frozen = false; + } + + public override bool OnDeath(Mobile mob, Container corpse) + { + var killer = mob.FindMostRecentDamager(false); + + var hadFlag = false; + + foreach (var flag in corpse.EnumerateItemsByType(false)) + { + hadFlag = true; + flag.DropTo(mob, killer); + } + + if (mob.Backpack != null) + { + foreach (var flag in mob.Backpack.EnumerateItemsByType(false)) + { + hadFlag = true; + flag.DropTo(mob, killer); + } + } + + if (killer?.Player == true) + { + var teamInfo = GetTeamInfo(killer); + var victInfo = GetTeamInfo(mob); + + if (teamInfo != null && teamInfo != victInfo) + { + var playerInfo = teamInfo[killer]; + + if (playerInfo != null) + { + playerInfo.Kills += 1; + playerInfo.Score += 1; // base frag + + if (hadFlag) + { + playerInfo.Score += 4; // fragged flag carrier + } + + if (mob.InRange(teamInfo.Origin, 24) && mob.Map == Facet) + { + playerInfo.Score += 1; // fragged in base -- guarding + } + + for (var i = 0; i < Controller.TeamInfo.Length; ++i) + { + if (Controller.TeamInfo[i] == teamInfo) + { + continue; + } + + Mobile ourFlagCarrier = null; + + if (Controller.TeamInfo[i].Flag != null) + { + ourFlagCarrier = Controller.TeamInfo[i].Flag.RootParent as Mobile; + } + + if (ourFlagCarrier != null && GetTeamInfo(ourFlagCarrier) == teamInfo) + { + foreach (var aggr in ourFlagCarrier.Aggressors) + { + if (aggr.Defender == ourFlagCarrier && aggr.Attacker == mob) + { + playerInfo.Score += 2; // helped defend guy capturing enemy flag + break; + } + } + + if (mob.Map == ourFlagCarrier.Map && ourFlagCarrier.InRange(mob, 12)) + { + playerInfo.Score += 1; // helped defend guy capturing enemy flag + } + } + } + } + } + } + + mob.SendGump(new CTFBoardGump(mob, this)); + + m_Context.Requip(mob, corpse); + DelayBounce(TimeSpan.FromSeconds(30.0), mob, corpse); + + return false; + } + + public override void OnStart() + { + for (var i = 0; i < Controller.TeamInfo.Length; ++i) + { + var teamInfo = Controller.TeamInfo[i]; + + teamInfo.Game = this; + teamInfo.Reset(); + } + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + ApplyHues(m_Context.Participants[i], Controller.TeamInfo[i % 8].Color); + } + + _finishTimerToken.Cancel(); + Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); + } + + private void Finish_Callback() + { + var teams = new List(); + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var teamInfo = Controller.TeamInfo[i % 8]; if (teamInfo?.Flag == null) { continue; } - entries.Add(teamInfo); + teams.Add(teamInfo); } - } - else - { - foreach (var player in _section.Players.Values) + + teams.Sort((a, b) => b.Score - a.Score); + + var tourney = m_Context.m_Tournament; + + var sb = new StringBuilder(); + + if (tourney != null) { - if (player.Score > 0) + if (tourney.TourneyType == TourneyType.FreeForAll) { - entries.Add(player); + sb.Append(m_Context.Participants.Count * tourney.PlayersPerParticipant); + sb.Append("-man FFA"); } - } - } - - entries.Sort((a, b) => b.Score - a.Score); - - var height = 0; - - if (_section == null) - { - height = 73 + entries.Count * 75 + 28; - } - - builder.SetNoClose(); - - builder.AddPage(); - - builder.AddBackground(1, 1, 398, height, 3600); - - builder.AddImageTiled(16, 15, 369, height - 29, 3604); - - for (var i = 0; i < entries.Count; i += 1) - { - builder.AddImageTiled(22, 58 + i * 75, 357, 70, 0x2430); - } - - builder.AddAlphaRegion(16, 15, 369, height - 29); - - builder.AddImage(215, -45, 0xEE40); - - AddBorderedText(ref builder, 22, 22, 294, 20, "CTF Scoreboard".Center(), LabelColor32, BlackColor32); - - builder.AddImageTiled(32, 50, 264, 1, 9107); - builder.AddImageTiled(42, 52, 264, 1, 9157); - - if (_section == null) - { - for (var i = 0; i < entries.Count; ++i) - { - if (entries[i] is not CTFTeamInfo teamInfo) + else if (tourney.TourneyType == TourneyType.RandomTeam) { - continue; + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-team"); } - - builder.AddImage(30, 70 + i * 75, 10152); - builder.AddImage(30, 85 + i * 75, 10151); - builder.AddImage(30, 100 + i * 75, 10151); - builder.AddImage(30, 106 + i * 75, 10154); - - builder.AddImage(24, 60 + i * 75, teamInfo == ourTeam ? 9730 : 9727, teamInfo.Color - 1); - - var borderColor = teamInfo.Color == 0x455 ? LabelColor32 : BlackColor32; - var nameColor = teamInfo.Color switch + else if (tourney.TourneyType == TourneyType.RedVsBlue) { - 0x47E => 0xFFFFFF, - 0x4F2 => 0x3399FF, - 0x4F7 => 0x33FF33, - 0x4FC => 0xFF00FF, - 0x021 => 0xFF3333, - 0x01A => 0xFF66FF, - 0x455 => 0x333333, - _ => LabelColor32 - }; - - AddBorderedText( - ref builder, - 60, - 65 + i * 75, - 250, - 20, - $"{LadderGump.Rank(1 + i)}: {teamInfo.Name} Team", - nameColor, - borderColor - ); - - AddBorderedText(ref builder, 50 + 10, 85 + i * 75, 100, 20, "Score:", 0xFFC000, BlackColor32); - AddBorderedText(ref builder, 50 + 15, 105 + i * 75, 100, 20, $"{teamInfo.Score:N0}", 0xFFC000, BlackColor32); - - AddBorderedText(ref builder, 110 + 10, 85 + i * 75, 100, 20, "Kills:", 0xFFC000, BlackColor32); - AddBorderedText(ref builder, 110 + 15, 105 + i * 75, 100, 20, $"{teamInfo.Kills:N0}", 0xFFC000, BlackColor32); - - AddBorderedText(ref builder, 160 + 10, 85 + i * 75, 100, 20, "Captures:", 0xFFC000, BlackColor32); - AddBorderedText( - ref builder, - 160 + 15, - 105 + i * 75, - 100, - 20, - $"{teamInfo.Captures:N0}", - 0xFFC000, - BlackColor32 - ); - - var pl = teamInfo.Leader; - - AddBorderedText(ref builder, 235 + 10, 85 + i * 75, 250, 20, "Leader:", 0xFFC000, BlackColor32); - - if (pl != null) - { - AddBorderedText(ref builder, 235 + 15, 105 + i * 75, 250, 20, pl.Player.Name, 0xFFC000, BlackColor32); + sb.Append("Red v Blue"); } - } - } - - builder.AddButton(314, height - 42, 247, 248, 1); - } - - private static void AddBorderedText(ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color, int borderColor) - { - AddColoredText(ref builder, x - 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x - 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x, y, width, height, text, color); - } - - private static void AddColoredText(ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color) - { - builder.AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); - } -} - -[SerializationGenerator(0, false)] -public sealed partial class CTFFlag : Item -{ - public Mobile m_Fragger; - public DateTime m_FragTime; - - private int m_ReturnCount; - - public Mobile m_Returner; - public DateTime m_ReturnTime; - private TimerExecutionToken _returnTimerToken; - public CTFTeamInfo m_TeamInfo; - - [Constructible] - public CTFFlag() : base(5643) => Movable = false; - - public override string DefaultName => "old people cookies"; - - public override void OnDoubleClick(Mobile from) - { - if (m_TeamInfo?.Game != null) - { - var ourTeam = m_TeamInfo; - var useTeam = m_TeamInfo.Game.GetTeamInfo(from); - - if (ourTeam == null || useTeam == null) - { - return; - } - - if (IsChildOf(from.Backpack)) - { - from.BeginTarget(1, false, TargetFlags.None, Flag_OnTarget); - } - else if (!from.InRange(this, 1) || !from.InLOS(this)) - { - from.LocalOverheadMessage(MessageType.Regular, 0x26, 1019045); // I can't reach that - } - else if (ourTeam == useTeam) - { - if (Location == m_TeamInfo.Origin && Map == m_TeamInfo.Game.Facet) + else if (tourney.TourneyType == TourneyType.Faction) { - from.NetState.SendMessage( - Serial, - ItemID, - MessageType.Regular, - 0x3B2, - 3, - false, - "ENU", - Name, - "Touch me not for I am chaste." - ); + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-team Faction"); } else { - var playerInfo = useTeam[from]; - - playerInfo?.Score += 4; // return - - m_Returner = from; - m_ReturnTime = Core.Now; - - SendHome(); - - from.LocalOverheadMessage(MessageType.Regular, 0x59, false, "You returned the cookies!"); - m_TeamInfo.Game.Alert($"The {from.Name} cookies have been returned by {ourTeam.Name}."); - } - } - else if (!from.PlaceInBackpack(this)) - { - from.LocalOverheadMessage(MessageType.Regular, 0x26, false, "I can't hold that."); - } - else - { - from.RevealingAction(); - - from.LocalOverheadMessage(MessageType.Regular, 0x59, false, "You stole the cookies!"); - m_TeamInfo.Game.Alert($"The {ourTeam.Name} cookies have been stolen by {from.Name} ({useTeam.Name})."); - - BeginCountdown(120); - } - } - } - - public override void Delete() - { - if (Parent != null) - { - SendHome(); - return; - } - - base.Delete(); - } - - public void DropTo(Mobile mob, Mobile killer) - { - m_Fragger = killer; - m_FragTime = Core.Now; - - if (mob != null) - { - MoveToWorld(new Point3D(mob.X, mob.Y, mob.Z + 2), mob.Map); - - m_ReturnCount = Math.Min(m_ReturnCount, 10); - } - else - { - SendHome(); - } - } - - private void BeginCountdown(int returnCount) - { - _returnTimerToken.Cancel(); - - Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), Countdown_OnTick, out _returnTimerToken); - m_ReturnCount = returnCount; - } - - private void Countdown_OnTick() - { - var owner = RootParent as Mobile; - - switch (m_ReturnCount) - { - case 60: - case 30: - case 15: - case 10: - case 5: - case 4: - case 3: - case 2: - case 1: - { - if (m_ReturnCount == 1) + for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) { - owner?.SendMessage(0x26, $"You have {m_ReturnCount} second to capture the cookies!"); - } - else - { - owner?.SendMessage(0x26, $"You have {m_ReturnCount} seconds to capture the cookies!"); - } - - break; - } - - case 0: - { - if (owner != null) - { - owner.SendMessage(0x26, "You have taken too long to capture the cookies!"); - owner.Kill(); - } - - SendHome(); - - m_TeamInfo?.Game?.Alert($"The {m_TeamInfo.Name} cookies have been returned."); - - return; - } - } - - --m_ReturnCount; - } - - private void Flag_OnTarget(Mobile from, object obj) - { - if (m_TeamInfo == null) - { - return; - } - - if (!IsChildOf(from.Backpack)) - { - return; - } - - var ourTeam = m_TeamInfo; - var useTeam = m_TeamInfo.Game.GetTeamInfo(from); - - if (obj is CTFFlag) - { - if (obj == useTeam.Flag) - { - from.LocalOverheadMessage(MessageType.Regular, 0x59, false, "You captured the cookies!"); - m_TeamInfo.Game.Alert($"{from.Name} captured the {ourTeam.Name} cookies!"); - - SendHome(); - - var playerInfo = useTeam[from]; - - if (playerInfo != null) - { - playerInfo.Captures += 1; - playerInfo.Score += 50; // capture - - var teamFlag = useTeam.Flag; - - if (teamFlag.m_Fragger != null && - Core.Now < teamFlag.m_FragTime + TimeSpan.FromSeconds(5.0) && - m_TeamInfo.Game.GetTeamInfo(teamFlag.m_Fragger) == useTeam) - { - var assistInfo = useTeam[teamFlag.m_Fragger]; - - assistInfo?.Score += 6; // frag assist - } - - if (teamFlag.m_Returner != null && - Core.Now < teamFlag.m_ReturnTime + TimeSpan.FromSeconds(5.0)) - { - var assistInfo = useTeam[teamFlag.m_Returner]; - - assistInfo?.Score += 4; // return assist - } - } - } - else - { - from.LocalOverheadMessage(MessageType.Regular, 0x26, false, "Those are not my cookies."); - } - } - else if (obj is Mobile passTo) - { - var passTeam = m_TeamInfo.Game.GetTeamInfo(passTo); - - if (passTo == from) - { - from.LocalOverheadMessage(MessageType.Regular, 0x26, false, "I can't pass to them."); - } - else if (passTeam == useTeam && passTo.PlaceInBackpack(this)) - { - passTo.LocalOverheadMessage( - MessageType.Regular, - 0x59, - false, - $"{from.Name} has passed you the cookies!" - ); - } - else - { - from.LocalOverheadMessage(MessageType.Regular, 0x26, false, "I can't pass to them."); - } - } - } - - public void SendHome() - { - _returnTimerToken.Cancel(); - - if (m_TeamInfo == null) - { - return; - } - - MoveToWorld(m_TeamInfo.Origin, m_TeamInfo.Game.Facet); - } - - private Mobile FindOwner(IEntity parent) - { - if (parent is Item item) - { - return item.RootParent as Mobile; - } - - if (parent is Mobile mobile) - { - return mobile; - } - - return null; - } - - public override void OnAdded(IEntity parent) - { - base.OnAdded(parent); - - var mob = FindOwner(parent); - - mob?.SolidHueOverride = 0x4001; - } - - public override void OnRemoved(IEntity parent) - { - base.OnRemoved(parent); - - var mob = FindOwner(parent); - - mob?.SolidHueOverride = m_TeamInfo?.Game.GetColor(mob) ?? -1; - } -} - -public interface IRankedCTF -{ - int Kills { get; } - int Captures { get; } - int Score { get; } - string Name { get; } -} - -public sealed class CTFPlayerInfo : IRankedCTF -{ - private readonly CTFTeamInfo m_TeamInfo; - private int m_Captures; - - private int m_Kills; - - private int m_Score; - - public CTFPlayerInfo(CTFTeamInfo teamInfo, Mobile player) - { - m_TeamInfo = teamInfo; - Player = player; - } - - public Mobile Player { get; } - - string IRankedCTF.Name => Player.Name; - - public int Kills - { - get => m_Kills; - set - { - m_TeamInfo.Kills += value - m_Kills; - m_Kills = value; - } - } - - public int Captures - { - get => m_Captures; - set - { - m_TeamInfo.Captures += value - m_Captures; - m_Captures = value; - } - } - - public int Score - { - get => m_Score; - set - { - m_TeamInfo.Score += value - m_Score; - m_Score = value; - - if (m_TeamInfo.Leader == null || m_Score > m_TeamInfo.Leader.Score) - { - m_TeamInfo.Leader = this; - } - } - } -} - -[PropertyObject] -public sealed class CTFTeamInfo : IRankedCTF -{ - public CTFTeamInfo(int teamID) - { - TeamID = teamID; - Players = new Dictionary(); - } - - public CTFTeamInfo(int teamID, IGenericReader ip) - { - TeamID = teamID; - Players = new Dictionary(); - - var version = ip.ReadEncodedInt(); - - switch (version) - { - case 2: - { - Board = ip.ReadEntity(); - - goto case 1; - } - case 1: - { - Name = ip.ReadString(); - - goto case 0; - } - case 0: - { - Color = ip.ReadEncodedInt(); - - Flag = ip.ReadEntity(); - Origin = ip.ReadPoint3D(); - break; - } - } - } - - public CTFGame Game { get; set; } - - public int TeamID { get; } - - public CTFPlayerInfo Leader { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public CTFBoard Board { get; set; } - - public Dictionary Players { get; } - - public CTFPlayerInfo this[Mobile mob] - { - get - { - if (mob == null) - { - return null; - } - - if (!Players.TryGetValue(mob, out var val)) - { - Players[mob] = val = new CTFPlayerInfo(this, mob); - } - - return val; - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public int Color { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public string Name { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public CTFFlag Flag { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public Point3D Origin { get; set; } - - string IRankedCTF.Name => $"{Name} Team"; - - public int Kills { get; set; } - - public int Captures { get; set; } - - public int Score { get; set; } - - public void Reset() - { - Kills = 0; - Captures = 0; - - Score = 0; - - Leader = null; - - Players.Clear(); - - if (Flag != null) - { - Flag.m_TeamInfo = this; - Flag.Hue = Color; - Flag.SendHome(); - } - - Board?.m_TeamInfo = this; - } - - public void Serialize(IGenericWriter op) - { - op.WriteEncodedInt(2); // version - - op.Write(Board); - - op.Write(Name); - - op.WriteEncodedInt(Color); - - op.Write(Flag); - op.Write(Origin); - } - - public override string ToString() => "..."; -} - -public sealed class CTFController : EventController -{ - [Constructible] - public CTFController() - { - Visible = false; - Movable = false; - - Duration = TimeSpan.FromMinutes(30.0); - - TeamInfo = new CTFTeamInfo[8]; - - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i] = new CTFTeamInfo(i); - } - } - - public CTFController(Serial serial) - : base(serial) - { - } - - public CTFTeamInfo[] TeamInfo { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public CTFTeamInfo Team1 => TeamInfo[0]; - - [CommandProperty(AccessLevel.GameMaster)] - public CTFTeamInfo Team2 => TeamInfo[1]; - - [CommandProperty(AccessLevel.GameMaster)] - public CTFTeamInfo Team3 => TeamInfo[2]; - - [CommandProperty(AccessLevel.GameMaster)] - public CTFTeamInfo Team4 => TeamInfo[3]; - - [CommandProperty(AccessLevel.GameMaster)] - public CTFTeamInfo Team5 => TeamInfo[4]; - - [CommandProperty(AccessLevel.GameMaster)] - public CTFTeamInfo Team6 => TeamInfo[5]; - - [CommandProperty(AccessLevel.GameMaster)] - public CTFTeamInfo Team7 => TeamInfo[6]; - - [CommandProperty(AccessLevel.GameMaster)] - public CTFTeamInfo Team8 => TeamInfo[7]; - - [CommandProperty(AccessLevel.GameMaster)] - public TimeSpan Duration { get; set; } - - public override string Title => "CTF"; - - public override string GetTeamName(int teamID) => TeamInfo[teamID % TeamInfo.Length].Name; - - public override EventGame Construct(DuelContext context) => new CTFGame(this, context); - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(2); - - writer.Write(Duration); - - writer.WriteEncodedInt(TeamInfo.Length); - - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i].Serialize(writer); - } - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 2: - { - Duration = reader.ReadTimeSpan(); - - goto case 1; - } - case 1: - { - TeamInfo = new CTFTeamInfo[reader.ReadEncodedInt()]; - - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i] = new CTFTeamInfo(i, reader); - } - - break; - } - case 0: - { - TeamInfo = new CTFTeamInfo[8]; - - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i] = new CTFTeamInfo(i); - } - - break; - } - } - - if (version < 2) - { - Duration = TimeSpan.FromMinutes(30.0); - } - } -} - -public sealed class CTFGame : EventGame -{ - private TimerExecutionToken _finishTimerToken; - - public CTFGame(CTFController controller, DuelContext context) : base(context) => Controller = controller; - - public CTFController Controller { get; } - - public Map Facet - { - get - { - if (m_Context.Arena != null) - { - return m_Context.Arena.Facet; - } - - return Controller.Map; - } - } - - public static void Initialize() - { - for (var i = 0x7C9; i <= 0x7D0; ++i) - { - TileData.ItemTable[i].Flags |= TileFlag.NoShoot; - } - } - - public void Alert(string text) - { - m_Context.m_Tournament?.Alert(text); - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var p = m_Context.Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - p.Players[j]?.Mobile.SendMessage(0x35, text); - } - } - } - - public CTFTeamInfo GetTeamInfo(Mobile mob) - { - var teamID = GetTeamID(mob); - - if (teamID >= 0) - { - return Controller.TeamInfo[teamID % Controller.TeamInfo.Length]; - } - - return null; - } - - public int GetTeamID(Mobile mob) - { - if (mob is not PlayerMobile pm) - { - return -1; - } - - if (pm.DuelContext == null || pm.DuelContext != m_Context) - { - return -1; - } - - if (pm.DuelPlayer?.Eliminated != false) - { - return -1; - } - - return pm.DuelContext.Participants.IndexOf(pm.DuelPlayer.Participant); - } - - public int GetColor(Mobile mob) - { - var teamInfo = GetTeamInfo(mob); - - if (teamInfo != null) - { - return teamInfo.Color; - } - - return -1; - } - - private void ApplyHues(Participant p, int hueOverride) - { - for (var i = 0; i < p.Players.Length; ++i) - { - p.Players[i]?.Mobile.SolidHueOverride = hueOverride; - } - } - - public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) - { - Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); - } - - private void DelayBounce_Callback(Mobile mob, Container corpse) - { - var dp = (mob as PlayerMobile)?.DuelPlayer; - - m_Context.RemoveAggressions(mob); - - if (dp?.Eliminated == false) - { - mob.MoveToWorld(m_Context.Arena.GetBaseStartPoint(GetTeamID(mob)), Facet); - } - else - { - m_Context.SendOutside(mob); - } - - m_Context.Refresh(mob, corpse); - DuelContext.Debuff(mob); - DuelContext.CancelSpell(mob); - mob.Frozen = false; - } - - public override bool OnDeath(Mobile mob, Container corpse) - { - var killer = mob.FindMostRecentDamager(false); - - var hadFlag = false; - - using (var queue = corpse.EnumerateItemsByType(false)) - { - foreach (var flag in queue) - { - hadFlag = true; - flag.DropTo(mob, killer); - } - } - - if (mob.Backpack != null) - { - using var queue = mob.Backpack.EnumerateItemsByType(false); - foreach (var flag in queue) - { - hadFlag = true; - flag.DropTo(mob, killer); - } - } - - if (killer?.Player == true) - { - var teamInfo = GetTeamInfo(killer); - var victInfo = GetTeamInfo(mob); - - if (teamInfo != null && teamInfo != victInfo) - { - var playerInfo = teamInfo[killer]; - - if (playerInfo != null) - { - playerInfo.Kills += 1; - playerInfo.Score += 1; // base frag - - if (hadFlag) - { - playerInfo.Score += 4; // fragged flag carrier - } - - if (mob.InRange(teamInfo.Origin, 24) && mob.Map == Facet) - { - playerInfo.Score += 1; // fragged in base -- guarding - } - - for (var i = 0; i < Controller.TeamInfo.Length; ++i) - { - if (Controller.TeamInfo[i] == teamInfo) + if (sb.Length > 0) { - continue; + sb.Append('v'); } - Mobile ourFlagCarrier = null; + sb.Append(tourney.PlayersPerParticipant); + } + } + } - if (Controller.TeamInfo[i].Flag != null) + if (Controller != null) + { + sb.Append(' ').Append(Controller.Title); + } + + var title = sb.ToString(); + + var winner = teams.Count > 0 ? teams[0] : null; + + for (var i = 0; i < teams.Count; ++i) + { + var rank = TrophyRank.Bronze; + + if (i == 0) + { + rank = TrophyRank.Gold; + } + else if (i == 1) + { + rank = TrophyRank.Silver; + } + + var leader = teams[i].Leader; + + foreach (var pl in teams[i].Players.Values) + { + var mob = pl.Player; + + if (mob == null) + { + continue; + } + + // "Red v Blue CTF Champion" + + sb = new StringBuilder(); + + sb.Append(title); + + if (pl == leader) + { + sb.Append(" Leader"); + } + + if (pl.Score > 0) + { + sb.Append(": "); + + sb.Append(pl.Score.ToString("N0")); + sb.Append(pl.Score == 1 ? " point" : " points"); + + if (pl.Kills > 0) { - ourFlagCarrier = Controller.TeamInfo[i].Flag.RootParent as Mobile; + sb.Append(", "); + sb.Append(pl.Kills.ToString("N0")); + sb.Append(pl.Kills == 1 ? " kill" : " kills"); } - if (ourFlagCarrier != null && GetTeamInfo(ourFlagCarrier) == teamInfo) + if (pl.Captures > 0) { - foreach (var aggr in ourFlagCarrier.Aggressors) - { - if (aggr.Defender == ourFlagCarrier && aggr.Attacker == mob) - { - playerInfo.Score += 2; // helped defend guy capturing enemy flag - break; - } - } - - if (mob.Map == ourFlagCarrier.Map && ourFlagCarrier.InRange(mob, 12)) - { - playerInfo.Score += 1; // helped defend guy capturing enemy flag - } + sb.Append(", "); + sb.Append(pl.Captures.ToString("N0")); + sb.Append(pl.Captures == 1 ? " capture" : " captures"); } } - } - } - } - CTFBoardGump.DisplayTo(mob, this); + Item item = new Trophy(sb.ToString(), rank); - m_Context.Requip(mob, corpse); - DelayBounce(TimeSpan.FromSeconds(30.0), mob, corpse); - - return false; - } - - public override void OnStart() - { - for (var i = 0; i < Controller.TeamInfo.Length; ++i) - { - var teamInfo = Controller.TeamInfo[i]; - - teamInfo.Game = this; - teamInfo.Reset(); - } - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - ApplyHues(m_Context.Participants[i], Controller.TeamInfo[i % 8].Color); - } - - _finishTimerToken.Cancel(); - Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); - } - - private void Finish_Callback() - { - var teams = new List(); - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var teamInfo = Controller.TeamInfo[i % 8]; - - if (teamInfo?.Flag == null) - { - continue; - } - - teams.Add(teamInfo); - } - - teams.Sort((a, b) => b.Score - a.Score); - - var tourney = m_Context.m_Tournament; - - using var sb = ValueStringBuilder.Create(256); - - if (tourney != null) - { - if (tourney.TourneyType == TourneyType.FreeForAll) - { - sb.Append(m_Context.Participants.Count * tourney.PlayersPerParticipant); - sb.Append("-man FFA"); - } - else if (tourney.TourneyType == TourneyType.RandomTeam) - { - sb.Append(tourney.ParticipantsPerMatch); - sb.Append("-team"); - } - else if (tourney.TourneyType == TourneyType.RedVsBlue) - { - sb.Append("Red v Blue"); - } - else if (tourney.TourneyType == TourneyType.Faction) - { - sb.Append(tourney.ParticipantsPerMatch); - sb.Append("-team Faction"); - } - else - { - for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) - { - if (sb.Length > 0) + if (pl == leader) { - sb.Append('v'); + item.ItemID = 4810; } - sb.Append(tourney.PlayersPerParticipant); - } - } - } - - if (Controller != null) - { - sb.Append(' '); - sb.Append(Controller.Title); - } - - var title = sb.ToString(); - - var winner = teams.Count > 0 ? teams[0] : null; - - for (var i = 0; i < teams.Count; ++i) - { - var rank = TrophyRank.Bronze; - - if (i == 0) - { - rank = TrophyRank.Gold; - } - else if (i == 1) - { - rank = TrophyRank.Silver; - } - - var leader = teams[i].Leader; - - foreach (var pl in teams[i].Players.Values) - { - var mob = pl.Player; - - if (mob == null) - { - continue; - } - - // "Red v Blue CTF Champion" - - sb.Reset(); - - sb.Append(title); - - if (pl == leader) - { - sb.Append(" Leader"); - } - - if (pl.Score > 0) - { - sb.Append(": "); - - sb.Append(pl.Score.ToString("N0")); - sb.Append(pl.Score == 1 ? " point" : " points"); - - if (pl.Kills > 0) - { - sb.Append(", "); - sb.Append(pl.Kills.ToString("N0")); - sb.Append(pl.Kills == 1 ? " kill" : " kills"); - } - - if (pl.Captures > 0) - { - sb.Append(", "); - sb.Append(pl.Captures.ToString("N0")); - sb.Append(pl.Captures == 1 ? " capture" : " captures"); - } - } - - Item item = new Trophy(sb.ToString(), rank); - - if (pl == leader) - { - item.ItemID = 4810; - } - - item.Name = $"{item.Name}, {teams[i].Name.ToLower()} team"; - - if (!mob.PlaceInBackpack(item)) - { - mob.BankBox.DropItem(item); - } - - var cash = pl.Score * 250; - - if (cash > 0) - { - item = new BankCheck(cash); + item.Name = $"{item.Name}, {teams[i].Name.ToLower()} team"; if (!mob.PlaceInBackpack(item)) { mob.BankBox.DropItem(item); } - mob.SendMessage( - $"You have been awarded a {rank.LowerName()} trophy and {cash:N0}gp for your participation in this tournament." - ); + var cash = pl.Score * 250; + + if (cash > 0) + { + item = new BankCheck(cash); + + if (!mob.PlaceInBackpack(item)) + { + mob.BankBox.DropItem(item); + } + + mob.SendMessage( + $"You have been awarded a {rank.ToString().ToLower()} trophy and {cash:N0}gp for your participation in this tournament." + ); + } + else + { + mob.SendMessage( + $"You have been awarded a {rank.ToString().ToLower()} trophy for your participation in this tournament." + ); + } } - else + } + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var p = m_Context.Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) { - mob.SendMessage( - $"You have been awarded a {rank.LowerName()} trophy for your participation in this tournament." - ); + var dp = p.Players[j]; + + if (dp?.Mobile != null) + { + dp.Mobile.SendGump(new CTFBoardGump(dp.Mobile, this)); + } } - } - } - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var p = m_Context.Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var dp = p.Players[j]; - - if (dp?.Mobile != null) + if (i == winner?.TeamID) { - CTFBoardGump.DisplayTo(dp.Mobile, this); + continue; } - } - if (i == winner?.TeamID) - { - continue; - } - - for (var j = 0; j < p.Players.Length; ++j) - { - if (p.Players[j] != null) + for (var j = 0; j < p.Players.Length; ++j) { - p.Players[j].Eliminated = true; + if (p.Players[j] != null) + { + p.Players[j].Eliminated = true; + } } } - } - if (winner != null) - { - m_Context.Finish(m_Context.Participants[winner.TeamID]); - } - } - - public override void OnStop() - { - for (var i = 0; i < Controller.TeamInfo.Length; ++i) - { - var teamInfo = Controller.TeamInfo[i]; - - if (teamInfo.Flag != null) + if (winner != null) { - teamInfo.Flag.SendHome(); - teamInfo.Flag.m_TeamInfo = null; + m_Context.Finish(m_Context.Participants[winner.TeamID]); + } + } + + public override void OnStop() + { + for (var i = 0; i < Controller.TeamInfo.Length; ++i) + { + var teamInfo = Controller.TeamInfo[i]; + + if (teamInfo.Flag != null) + { + teamInfo.Flag.SendHome(); + teamInfo.Flag.m_TeamInfo = null; + } + + if (teamInfo.Board != null) + { + teamInfo.Board.m_TeamInfo = null; + } + + teamInfo.Game = null; } - teamInfo.Board?.m_TeamInfo = null; + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + ApplyHues(m_Context.Participants[i], -1); + } - teamInfo.Game = null; + _finishTimerToken.Cancel(); } - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - ApplyHues(m_Context.Participants[i], -1); - } - - _finishTimerToken.Cancel(); } } diff --git a/Projects/UOContent/Engines/ConPVP/Games/DoubleDom.cs b/Projects/UOContent/Engines/ConPVP/Games/DoubleDom.cs index b53cc33cb..e06bc2864 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/DoubleDom.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/DoubleDom.cs @@ -1,1088 +1,1151 @@ using System; using System.Collections.Generic; -using ModernUO.Serialization; +using System.Text; using Server.Gumps; using Server.Items; using Server.Mobiles; -using Server.Text; -namespace Server.Engines.ConPVP; - -[SerializationGenerator(0, false)] -public sealed partial class DDBoard : Item +namespace Server.Engines.ConPVP { - public DDTeamInfo m_TeamInfo; - - [Constructible] - public DDBoard() : base(7774) => Movable = false; - - public override string DefaultName => "scoreboard"; - - public override void OnDoubleClick(Mobile from) + public sealed class DDBoard : Item { - if (m_TeamInfo?.Game != null) + public DDTeamInfo m_TeamInfo; + + [Constructible] + public DDBoard() + : base(7774) => + Movable = false; + + public DDBoard(Serial serial) + : base(serial) { - DDBoardGump.DisplayTo(from, m_TeamInfo.Game); - } - } -} - -public class DDBoardGump : DynamicGump -{ - private const int LabelColor32 = 0xFFFFFF; - private const int BlackColor32 = 0x000000; - - private readonly Mobile _mob; - private readonly DDGame _game; - private readonly DDTeamInfo _section; - - public override bool Singleton => true; - - private DDBoardGump(Mobile mob, DDGame game, DDTeamInfo section) - : base(60, 60) - { - _mob = mob; - _game = game; - _section = section; - } - - public static void DisplayTo(Mobile mob, DDGame game, DDTeamInfo section = null) - { - if (mob?.NetState == null || game == null) - { - return; } - mob.SendGump(new DDBoardGump(mob, game, section)); - } + public override string DefaultName => "scoreboard"; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var ourTeam = _game.GetTeamInfo(_mob); - - var entries = new List(); - - if (_section == null) + public override void OnDoubleClick(Mobile from) { - for (var i = 0; i < _game.Context.Participants.Count; ++i) + if (m_TeamInfo?.Game != null) { - var teamInfo = _game.Controller.TeamInfo[i % _game.Controller.TeamInfo.Length]; - - if (teamInfo != null) - { - entries.Add(teamInfo); - } - } - } - else - { - foreach (var player in _section.Players.Values) - { - if (player.Score > 0) - { - entries.Add(player); - } + from.SendGump(new DDBoardGump(from, m_TeamInfo.Game)); } } - entries.Sort((a, b) => b.Score - a.Score); - - var height = 0; - - if (_section == null) + public override void Serialize(IGenericWriter writer) { - height = 73 + entries.Count * 75 + 28; + base.Serialize(writer); + + writer.Write(0); } - builder.SetNoClose(); - - builder.AddPage(); - - builder.AddBackground(1, 1, 398, height, 3600); - - builder.AddImageTiled(16, 15, 369, height - 29, 3604); - - for (var i = 0; i < entries.Count; i += 1) + public override void Deserialize(IGenericReader reader) { - builder.AddImageTiled(22, 58 + i * 75, 357, 70, 0x2430); + base.Deserialize(reader); + + var version = reader.ReadInt(); } + } - builder.AddAlphaRegion(16, 15, 369, height - 29); + public class DDBoardGump : Gump + { + private const int LabelColor32 = 0xFFFFFF; + private const int BlackColor32 = 0x000000; - builder.AddImage(215, -45, 0xEE40); + public override bool Singleton => true; - AddBorderedText(ref builder, 22, 22, 294, 20, "DD Scoreboard".Center(), LabelColor32, BlackColor32); - - builder.AddImageTiled(32, 50, 264, 1, 9107); - builder.AddImageTiled(42, 52, 264, 1, 9157); - - if (_section == null) + public DDBoardGump(Mobile mob, DDGame game, DDTeamInfo section = null) + : base(60, 60) { - for (var i = 0; i < entries.Count; ++i) + // m_Game = game; + + var ourTeam = game.GetTeamInfo(mob); + + var entries = new List(); + + if (section == null) { - if (entries[i] is not DDTeamInfo teamInfo) + for (var i = 0; i < game.Context.Participants.Count; ++i) { - continue; - } + var teamInfo = game.Controller.TeamInfo[i % game.Controller.TeamInfo.Length]; - builder.AddImage(30, 70 + i * 75, 10152); - builder.AddImage(30, 85 + i * 75, 10151); - builder.AddImage(30, 100 + i * 75, 10151); - builder.AddImage(30, 106 + i * 75, 10154); - - builder.AddImage(24, 60 + i * 75, teamInfo == ourTeam ? 9730 : 9727, teamInfo.Color - 1); - - var borderColor = teamInfo.Color == 0x455 ? LabelColor32 : BlackColor32; - var nameColor = teamInfo.Color switch - { - 0x47E => 0xFFFFFF, - 0x4F2 => 0x3399FF, - 0x4F7 => 0x33FF33, - 0x4FC => 0xFF00FF, - 0x021 => 0xFF3333, - 0x01A => 0xFF66FF, - 0x455 => 0x333333, - _ => LabelColor32 - }; - - AddBorderedText( - ref builder, - 60, - 65 + i * 75, - 250, - 20, - $"{LadderGump.Rank(1 + i)}: {teamInfo.Name}", - nameColor, - borderColor - ); - - AddBorderedText(ref builder, 50 + 10, 85 + i * 75, 100, 20, "Score:", 0xFFC000, BlackColor32); - AddBorderedText(ref builder, 50 + 15, 105 + i * 75, 100, 20, $"{teamInfo.Score:N0}", 0xFFC000, BlackColor32); - - AddBorderedText(ref builder, 110 + 10, 85 + i * 75, 100, 20, "Kills:", 0xFFC000, BlackColor32); - AddBorderedText(ref builder, 110 + 15, 105 + i * 75, 100, 20, $"{teamInfo.Kills:N0}", 0xFFC000, BlackColor32); - - AddBorderedText(ref builder, 160 + 10, 85 + i * 75, 100, 20, "Captures:", 0xFFC000, BlackColor32); - AddBorderedText( - ref builder, - 160 + 15, - 105 + i * 75, - 100, - 20, - $"{teamInfo.Captures:N0}", - 0xFFC000, - BlackColor32 - ); - - var pl = teamInfo.Leader; - - AddBorderedText(ref builder, 235 + 10, 85 + i * 75, 250, 20, "Leader:", 0xFFC000, BlackColor32); - - if (pl != null) - { - AddBorderedText(ref builder, 235 + 15, 105 + i * 75, 250, 20, pl.Player.Name, 0xFFC000, BlackColor32); - } - } - } - - builder.AddButton(314, height - 42, 247, 248, 1); - } - - private static void AddBorderedText(ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color, int borderColor) - { - AddColoredText(ref builder, x - 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x - 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x, y, width, height, text, color); - } - - private static void AddColoredText(ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color) - { - builder.AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); - } -} - -public sealed class DDPlayerInfo : IRankedCTF -{ - private readonly DDTeamInfo m_TeamInfo; - private int m_Captures; - - private int m_Kills; - - private int m_Score; - - public DDPlayerInfo(DDTeamInfo teamInfo, Mobile player) - { - m_TeamInfo = teamInfo; - Player = player; - } - - public Mobile Player { get; } - - public string Name => Player.Name; - - public int Kills - { - get => m_Kills; - set - { - m_TeamInfo.Kills += value - m_Kills; - m_Kills = value; - } - } - - public int Captures - { - get => m_Captures; - set - { - m_TeamInfo.Captures += value - m_Captures; - m_Captures = value; - } - } - - public int Score - { - get => m_Score; - set - { - m_TeamInfo.Score += value - m_Score; - m_Score = value; - - if (m_TeamInfo.Leader == null || m_Score > m_TeamInfo.Leader.Score) - { - m_TeamInfo.Leader = this; - } - } - } -} - -[PropertyObject] -public sealed class DDTeamInfo : IRankedCTF -{ - public DDTeamInfo(int teamID) - { - TeamID = teamID; - Players = new Dictionary(); - } - - public DDTeamInfo(int teamID, IGenericReader ip) - { - TeamID = teamID; - Players = new Dictionary(); - - var version = ip.ReadEncodedInt(); - - switch (version) - { - case 0: - { - Board = ip.ReadEntity(); - TeamName = ip.ReadString(); - Color = ip.ReadEncodedInt(); - Origin = ip.ReadPoint3D(); - break; - } - } - } - - public DDGame Game { get; set; } - - public int TeamID { get; } - - public DDPlayerInfo Leader { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public DDBoard Board { get; set; } - - public Dictionary Players { get; } - - public DDPlayerInfo this[Mobile mob] - { - get - { - if (mob == null) - { - return null; - } - - if (!Players.TryGetValue(mob, out var val)) - { - Players[mob] = val = new DDPlayerInfo(this, mob); - } - - return val; - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public int Color { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public string TeamName { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public Point3D Origin { get; set; } - - public string Name => $"{TeamName} Team"; - - public int Kills { get; set; } - - public int Captures { get; set; } - - public int Score { get; set; } - - public void Reset() - { - Kills = 0; - Captures = 0; - - Score = 0; - - Leader = null; - - Players.Clear(); - - Board?.m_TeamInfo = this; - } - - public void Serialize(IGenericWriter op) - { - op.WriteEncodedInt(0); // version - - op.Write(Board); - op.Write(TeamName); - op.WriteEncodedInt(Color); - op.Write(Origin); - } - - public override string ToString() => "..."; -} - -public sealed class DDController : EventController -{ - [Constructible] - public DDController() - { - Visible = false; - Movable = false; - - Duration = TimeSpan.FromMinutes(30.0); - - TeamInfo = new DDTeamInfo[2]; - - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i] = new DDTeamInfo(i); - } - } - - public DDController(Serial serial) - : base(serial) - { - } - - public DDTeamInfo[] TeamInfo { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public DDTeamInfo Team1 => TeamInfo[0]; - - [CommandProperty(AccessLevel.GameMaster)] - public DDTeamInfo Team2 => TeamInfo[1]; - - [CommandProperty(AccessLevel.GameMaster)] - public DDWayPoint PointA { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public DDWayPoint PointB { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public TimeSpan Duration { get; set; } - - public override string Title => "DoubleDom"; - - public override string DefaultName => "DD Controller"; - - public override string GetTeamName(int teamID) => TeamInfo[teamID % TeamInfo.Length].Name; - - public override EventGame Construct(DuelContext context) => new DDGame(this, context); - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - - writer.Write(Duration); - - writer.WriteEncodedInt(TeamInfo.Length); - - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i].Serialize(writer); - } - - writer.Write(PointA); - writer.Write(PointB); - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - Duration = reader.ReadTimeSpan(); - TeamInfo = new DDTeamInfo[reader.ReadEncodedInt()]; - - for (var i = 0; i < TeamInfo.Length; ++i) + if (teamInfo != null) { - TeamInfo[i] = new DDTeamInfo(i, reader); - } - - PointA = reader.ReadEntity(); - PointB = reader.ReadEntity(); - - break; - } - } - } -} - -public sealed class DDGame : EventGame -{ - private int m_CapStage; - - private bool m_Capturable = true; - private TimerExecutionToken _captureTimerToken; - private TimerExecutionToken _finishTimerToken; - private TimerExecutionToken _uncaptureTimerToken; - - public DDGame(DDController controller, DuelContext context) : base(context) => Controller = controller; - - public DDController Controller { get; } - - public Map Facet => m_Context.Arena?.Facet ?? Controller.Map; - - public void Alert(string text) - { - m_Context.m_Tournament?.Alert(text); - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var p = m_Context.Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - p.Players[j]?.Mobile.SendMessage(0x35, text); - } - } - } - - public DDTeamInfo GetTeamInfo(Mobile mob) - { - var teamID = GetTeamID(mob); - - if (teamID >= 0) - { - return Controller.TeamInfo[teamID % Controller.TeamInfo.Length]; - } - - return null; - } - - public int GetTeamID(Mobile mob) - { - if (mob is not PlayerMobile pm) - { - return -1; - } - - if (pm.DuelContext == null || pm.DuelContext != m_Context) - { - return -1; - } - - if (pm.DuelPlayer?.Eliminated != false) - { - return -1; - } - - return pm.DuelContext.Participants.IndexOf(pm.DuelPlayer.Participant); - } - - public int GetColor(Mobile mob) => GetTeamInfo(mob)?.Color ?? -1; - - private void ApplyHues(Participant p, int hueOverride) - { - for (var i = 0; i < p.Players.Length; ++i) - { - p.Players[i]?.Mobile.SolidHueOverride = hueOverride; - } - } - - public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) - { - Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); - } - - private void DelayBounce_Callback(Mobile mob, Container corpse) - { - var dp = (mob as PlayerMobile)?.DuelPlayer; - - m_Context.RemoveAggressions(mob); - - if (dp?.Eliminated == false) - { - mob.MoveToWorld(m_Context.Arena.GetBaseStartPoint(GetTeamID(mob)), Facet); - } - else - { - m_Context.SendOutside(mob); - } - - m_Context.Refresh(mob, corpse); - DuelContext.Debuff(mob); - DuelContext.CancelSpell(mob); - mob.Frozen = false; - } - - public override bool OnDeath(Mobile mob, Container corpse) - { - var killer = mob.FindMostRecentDamager(false); - - if (killer?.Player == true) - { - var teamInfo = GetTeamInfo(killer); - var victInfo = GetTeamInfo(mob); - - if (teamInfo != null && teamInfo != victInfo) - { - var playerInfo = teamInfo[killer]; - - if (playerInfo != null) - { - playerInfo.Kills += 1; - playerInfo.Score += 1; // base frag - - // extra points for killing someone on the waypoint - if (Controller.PointA != null) - { - if (mob.InRange(Controller.PointA, 2)) - { - playerInfo.Score += 1; - } - } - - if (Controller.PointB != null) - { - if (mob.InRange(Controller.PointB, 2)) - { - playerInfo.Score += 1; - } + entries.Add(teamInfo); } } - - playerInfo = victInfo[mob]; - playerInfo?.Score -= 1; - } - } - - DDBoardGump.DisplayTo(mob, this); - - m_Context.Requip(mob, corpse); - DelayBounce(TimeSpan.FromSeconds(30.0), mob, corpse); - - return false; - } - - public override void OnStart() - { - m_Capturable = true; - - _captureTimerToken.Cancel(); - _uncaptureTimerToken.Cancel(); - - for (var i = 0; i < Controller.TeamInfo.Length; ++i) - { - var teamInfo = Controller.TeamInfo[i]; - - teamInfo.Game = this; - teamInfo.Reset(); - } - - Controller.PointA?.Game = this; - - Controller.PointB?.Game = this; - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - ApplyHues( - m_Context.Participants[i], - Controller.TeamInfo[i % Controller.TeamInfo.Length].Color - ); - } - - _finishTimerToken.Cancel(); - Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); - } - - private void Finish_Callback() - { - var teams = new List(); - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var teamInfo = Controller.TeamInfo[i % Controller.TeamInfo.Length]; - - if (teamInfo != null) - { - teams.Add(teamInfo); - } - } - - teams.Sort((a, b) => b.Score - a.Score); - - var tourney = m_Context.m_Tournament; - - using var sb = ValueStringBuilder.Create(256); - - if (tourney != null) - { - if (tourney.TourneyType == TourneyType.FreeForAll) - { - sb.Append(m_Context.Participants.Count * tourney.PlayersPerParticipant); - sb.Append("-man FFA"); - } - else if (tourney.TourneyType == TourneyType.RandomTeam) - { - sb.Append(tourney.ParticipantsPerMatch); - sb.Append("-team"); - } - else if (tourney.TourneyType == TourneyType.RedVsBlue) - { - sb.Append("Red v Blue"); - } - else if (tourney.TourneyType == TourneyType.Faction) - { - sb.Append(tourney.ParticipantsPerMatch); - sb.Append("-team Faction"); } else { - for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) + foreach (var player in section.Players.Values) { - if (sb.Length > 0) + if (player.Score > 0) { - sb.Append('v'); + entries.Add(player); + } + } + } + + entries.Sort((a, b) => b.Score - a.Score); + + var height = 0; + + if (section == null) + { + height = 73 + entries.Count * 75 + 28; + } + + Closable = false; + + AddPage(0); + + AddBackground(1, 1, 398, height, 3600); + + AddImageTiled(16, 15, 369, height - 29, 3604); + + for (var i = 0; i < entries.Count; i += 1) + { + AddImageTiled(22, 58 + i * 75, 357, 70, 0x2430); + } + + AddAlphaRegion(16, 15, 369, height - 29); + + AddImage(215, -45, 0xEE40); + // AddImage( 330, 141, 0x8BA ); + + AddBorderedText(22, 22, 294, 20, "DD Scoreboard".Center(), LabelColor32, BlackColor32); + + AddImageTiled(32, 50, 264, 1, 9107); + AddImageTiled(42, 52, 264, 1, 9157); + + if (section == null) + { + for (var i = 0; i < entries.Count; ++i) + { + if (entries[i] is not DDTeamInfo teamInfo) + { + continue; } - sb.Append(tourney.PlayersPerParticipant); + AddImage(30, 70 + i * 75, 10152); + AddImage(30, 85 + i * 75, 10151); + AddImage(30, 100 + i * 75, 10151); + AddImage(30, 106 + i * 75, 10154); + + AddImage(24, 60 + i * 75, teamInfo == ourTeam ? 9730 : 9727, teamInfo.Color - 1); + + var borderColor = teamInfo.Color == 0x455 ? LabelColor32 : BlackColor32; + var nameColor = teamInfo.Color switch + { + 0x47E => 0xFFFFFF, + 0x4F2 => 0x3399FF, + 0x4F7 => 0x33FF33, + 0x4FC => 0xFF00FF, + 0x021 => 0xFF3333, + 0x01A => 0xFF66FF, + 0x455 => 0x333333, + _ => LabelColor32 + }; + + AddBorderedText( + 60, + 65 + i * 75, + 250, + 20, + $"{LadderGump.Rank(1 + i)}: {teamInfo.Name}", + nameColor, + borderColor + ); + + AddBorderedText(50 + 10, 85 + i * 75, 100, 20, "Score:", 0xFFC000, BlackColor32); + AddBorderedText(50 + 15, 105 + i * 75, 100, 20, teamInfo.Score.ToString("N0"), 0xFFC000, BlackColor32); + + AddBorderedText(110 + 10, 85 + i * 75, 100, 20, "Kills:", 0xFFC000, BlackColor32); + AddBorderedText(110 + 15, 105 + i * 75, 100, 20, teamInfo.Kills.ToString("N0"), 0xFFC000, BlackColor32); + + AddBorderedText(160 + 10, 85 + i * 75, 100, 20, "Captures:", 0xFFC000, BlackColor32); + AddBorderedText( + 160 + 15, + 105 + i * 75, + 100, + 20, + teamInfo.Captures.ToString("N0"), + 0xFFC000, + BlackColor32 + ); + + var pl = teamInfo.Leader; + + AddBorderedText(235 + 10, 85 + i * 75, 250, 20, "Leader:", 0xFFC000, BlackColor32); + + if (pl != null) + { + AddBorderedText(235 + 15, 105 + i * 75, 250, 20, pl.Player.Name, 0xFFC000, BlackColor32); + } + } + } + + AddButton(314, height - 42, 247, 248, 1); + } + + private void AddBorderedText(int x, int y, int width, int height, string text, int color, int borderColor) + { + AddColoredText(x - 1, y - 1, width, height, text, borderColor); + AddColoredText(x - 1, y + 1, width, height, text, borderColor); + AddColoredText(x + 1, y - 1, width, height, text, borderColor); + AddColoredText(x + 1, y + 1, width, height, text, borderColor); + AddColoredText(x, y, width, height, text, color); + } + + private void AddColoredText(int x, int y, int width, int height, string text, int color) + { + AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); + } + } + + public sealed class DDPlayerInfo : IRankedCTF + { + private readonly DDTeamInfo m_TeamInfo; + private int m_Captures; + + private int m_Kills; + + private int m_Score; + + public DDPlayerInfo(DDTeamInfo teamInfo, Mobile player) + { + m_TeamInfo = teamInfo; + Player = player; + } + + public Mobile Player { get; } + + public string Name => Player.Name; + + public int Kills + { + get => m_Kills; + set + { + m_TeamInfo.Kills += value - m_Kills; + m_Kills = value; + } + } + + public int Captures + { + get => m_Captures; + set + { + m_TeamInfo.Captures += value - m_Captures; + m_Captures = value; + } + } + + public int Score + { + get => m_Score; + set + { + m_TeamInfo.Score += value - m_Score; + m_Score = value; + + if (m_TeamInfo.Leader == null || m_Score > m_TeamInfo.Leader.Score) + { + m_TeamInfo.Leader = this; } } } + } - if (Controller != null) + [PropertyObject] + public sealed class DDTeamInfo : IRankedCTF + { + public DDTeamInfo(int teamID) { - sb.Append(' '); - sb.Append(Controller.Title); + TeamID = teamID; + Players = new Dictionary(); } - var title = sb.ToString(); - - var winner = teams.Count > 0 ? teams[0] : null; - - for (var i = 0; i < teams.Count; ++i) + public DDTeamInfo(int teamID, IGenericReader ip) { - var rank = TrophyRank.Bronze; + TeamID = teamID; + Players = new Dictionary(); - if (i == 0) + var version = ip.ReadEncodedInt(); + + switch (version) { - rank = TrophyRank.Gold; + case 0: + { + Board = ip.ReadEntity(); + TeamName = ip.ReadString(); + Color = ip.ReadEncodedInt(); + Origin = ip.ReadPoint3D(); + break; + } } - else if (i == 1) + } + + public DDGame Game { get; set; } + + public int TeamID { get; } + + public DDPlayerInfo Leader { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public DDBoard Board { get; set; } + + public Dictionary Players { get; } + + public DDPlayerInfo this[Mobile mob] + { + get { - rank = TrophyRank.Silver; - } - - var leader = teams[i].Leader; - - foreach (var pl in teams[i].Players.Values) - { - var mob = pl.Player; - if (mob == null) { - continue; + return null; } - // "Red v Blue DD Champion" - - sb.Reset(); - - sb.Append(title); - - if (pl == leader) + if (!Players.TryGetValue(mob, out var val)) { - sb.Append(" Leader"); + Players[mob] = val = new DDPlayerInfo(this, mob); } - if (pl.Score > 0) - { - sb.Append(": "); + return val; + } + } - sb.Append(pl.Score.ToString("N0")); - sb.Append(pl.Score == 1 ? " point" : " points"); + [CommandProperty(AccessLevel.GameMaster)] + public int Color { get; set; } - if (pl.Kills > 0) + [CommandProperty(AccessLevel.GameMaster)] + public string TeamName { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public Point3D Origin { get; set; } + + public string Name => $"{TeamName} Team"; + + public int Kills { get; set; } + + public int Captures { get; set; } + + public int Score { get; set; } + + public void Reset() + { + Kills = 0; + Captures = 0; + + Score = 0; + + Leader = null; + + Players.Clear(); + + if (Board != null) + { + Board.m_TeamInfo = this; + } + } + + public void Serialize(IGenericWriter op) + { + op.WriteEncodedInt(0); // version + + op.Write(Board); + op.Write(TeamName); + op.WriteEncodedInt(Color); + op.Write(Origin); + } + + public override string ToString() => "..."; + } + + public sealed class DDController : EventController + { + [Constructible] + public DDController() + { + Visible = false; + Movable = false; + + Duration = TimeSpan.FromMinutes(30.0); + + TeamInfo = new DDTeamInfo[2]; + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i] = new DDTeamInfo(i); + } + } + + public DDController(Serial serial) + : base(serial) + { + } + + public DDTeamInfo[] TeamInfo { get; private set; } + + [CommandProperty(AccessLevel.GameMaster)] + public DDTeamInfo Team1 => TeamInfo[0]; + + [CommandProperty(AccessLevel.GameMaster)] + public DDTeamInfo Team2 => TeamInfo[1]; + + [CommandProperty(AccessLevel.GameMaster)] + public DDWayPoint PointA { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public DDWayPoint PointB { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public TimeSpan Duration { get; set; } + + public override string Title => "DoubleDom"; + + public override string DefaultName => "DD Controller"; + + public override string GetTeamName(int teamID) => TeamInfo[teamID % TeamInfo.Length].Name; + + public override EventGame Construct(DuelContext context) => new DDGame(this, context); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + + writer.Write(Duration); + + writer.WriteEncodedInt(TeamInfo.Length); + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i].Serialize(writer); + } + + writer.Write(PointA); + writer.Write(PointB); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: { - sb.Append(", "); - sb.Append(pl.Kills.ToString("N0")); - sb.Append(pl.Kills == 1 ? " kill" : " kills"); + Duration = reader.ReadTimeSpan(); + TeamInfo = new DDTeamInfo[reader.ReadEncodedInt()]; + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i] = new DDTeamInfo(i, reader); + } + + PointA = reader.ReadEntity(); + PointB = reader.ReadEntity(); + + break; } + } + } + } - if (pl.Captures > 0) + public sealed class DDGame : EventGame + { + private int m_CapStage; + + private bool m_Capturable = true; + private TimerExecutionToken _captureTimerToken; + private TimerExecutionToken _finishTimerToken; + private TimerExecutionToken _uncaptureTimerToken; + + public DDGame(DDController controller, DuelContext context) : base(context) => Controller = controller; + + public DDController Controller { get; } + + public Map Facet => m_Context.Arena?.Facet ?? Controller.Map; + + public void Alert(string text) + { + m_Context.m_Tournament?.Alert(text); + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var p = m_Context.Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + if (p.Players[j] != null) { - sb.Append(", "); - sb.Append(pl.Captures.ToString("N0")); - sb.Append(pl.Captures == 1 ? " capture" : " captures"); + p.Players[j].Mobile.SendMessage(0x35, text); } } + } + } - Item item = new Trophy(sb.ToString(), rank); + public DDTeamInfo GetTeamInfo(Mobile mob) + { + var teamID = GetTeamID(mob); - if (pl == leader) + if (teamID >= 0) + { + return Controller.TeamInfo[teamID % Controller.TeamInfo.Length]; + } + + return null; + } + + public int GetTeamID(Mobile mob) + { + if (mob is not PlayerMobile pm) + { + return -1; + } + + if (pm.DuelContext == null || pm.DuelContext != m_Context) + { + return -1; + } + + if (pm.DuelPlayer?.Eliminated != false) + { + return -1; + } + + return pm.DuelContext.Participants.IndexOf(pm.DuelPlayer.Participant); + } + + public int GetColor(Mobile mob) => GetTeamInfo(mob)?.Color ?? -1; + + private void ApplyHues(Participant p, int hueOverride) + { + for (var i = 0; i < p.Players.Length; ++i) + { + if (p.Players[i] != null) { - item.ItemID = 4810; + p.Players[i].Mobile.SolidHueOverride = hueOverride; + } + } + } + + public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) + { + Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); + } + + private void DelayBounce_Callback(Mobile mob, Container corpse) + { + var dp = (mob as PlayerMobile)?.DuelPlayer; + + m_Context.RemoveAggressions(mob); + + if (dp?.Eliminated == false) + { + mob.MoveToWorld(m_Context.Arena.GetBaseStartPoint(GetTeamID(mob)), Facet); + } + else + { + m_Context.SendOutside(mob); + } + + m_Context.Refresh(mob, corpse); + DuelContext.Debuff(mob); + DuelContext.CancelSpell(mob); + mob.Frozen = false; + } + + public override bool OnDeath(Mobile mob, Container corpse) + { + var killer = mob.FindMostRecentDamager(false); + + if (killer?.Player == true) + { + var teamInfo = GetTeamInfo(killer); + var victInfo = GetTeamInfo(mob); + + if (teamInfo != null && teamInfo != victInfo) + { + var playerInfo = teamInfo[killer]; + + if (playerInfo != null) + { + playerInfo.Kills += 1; + playerInfo.Score += 1; // base frag + + // extra points for killing someone on the waypoint + if (Controller.PointA != null) + { + if (mob.InRange(Controller.PointA, 2)) + { + playerInfo.Score += 1; + } + } + + if (Controller.PointB != null) + { + if (mob.InRange(Controller.PointB, 2)) + { + playerInfo.Score += 1; + } + } + } + + playerInfo = victInfo[mob]; + if (playerInfo != null) + { + playerInfo.Score -= 1; + } + } + } + + mob.SendGump(new DDBoardGump(mob, this)); + + m_Context.Requip(mob, corpse); + DelayBounce(TimeSpan.FromSeconds(30.0), mob, corpse); + + return false; + } + + public override void OnStart() + { + m_Capturable = true; + + _captureTimerToken.Cancel(); + _uncaptureTimerToken.Cancel(); + + for (var i = 0; i < Controller.TeamInfo.Length; ++i) + { + var teamInfo = Controller.TeamInfo[i]; + + teamInfo.Game = this; + teamInfo.Reset(); + } + + if (Controller.PointA != null) + { + Controller.PointA.Game = this; + } + + if (Controller.PointB != null) + { + Controller.PointB.Game = this; + } + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + ApplyHues( + m_Context.Participants[i], + Controller.TeamInfo[i % Controller.TeamInfo.Length].Color + ); + } + + _finishTimerToken.Cancel(); + Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); + } + + private void Finish_Callback() + { + var teams = new List(); + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var teamInfo = Controller.TeamInfo[i % Controller.TeamInfo.Length]; + + if (teamInfo != null) + { + teams.Add(teamInfo); + } + } + + teams.Sort((a, b) => b.Score - a.Score); + + var tourney = m_Context.m_Tournament; + + var sb = new StringBuilder(); + + if (tourney != null) + { + if (tourney.TourneyType == TourneyType.FreeForAll) + { + sb.Append(m_Context.Participants.Count * tourney.PlayersPerParticipant); + sb.Append("-man FFA"); + } + else if (tourney.TourneyType == TourneyType.RandomTeam) + { + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-team"); + } + else if (tourney.TourneyType == TourneyType.RedVsBlue) + { + sb.Append("Red v Blue"); + } + else if (tourney.TourneyType == TourneyType.Faction) + { + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-team Faction"); + } + else + { + for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) + { + if (sb.Length > 0) + { + sb.Append('v'); + } + + sb.Append(tourney.PlayersPerParticipant); + } + } + } + + if (Controller != null) + { + sb.Append(' ').Append(Controller.Title); + } + + var title = sb.ToString(); + + var winner = teams.Count > 0 ? teams[0] : null; + + for (var i = 0; i < teams.Count; ++i) + { + var rank = TrophyRank.Bronze; + + if (i == 0) + { + rank = TrophyRank.Gold; + } + else if (i == 1) + { + rank = TrophyRank.Silver; } - item.Name = $"{item.Name}, {teams[i].Name.ToLower()} team"; + var leader = teams[i].Leader; - if (!mob.PlaceInBackpack(item)) + foreach (var pl in teams[i].Players.Values) { - mob.BankBox.DropItem(item); - } + var mob = pl.Player; - var cash = pl.Score * 250; + if (mob == null) + { + continue; + } - if (cash > 0) - { - item = new BankCheck(cash); + // "Red v Blue DD Champion" + + sb = new StringBuilder(); + + sb.Append(title); + + if (pl == leader) + { + sb.Append(" Leader"); + } + + if (pl.Score > 0) + { + sb.Append(": "); + + sb.Append(pl.Score.ToString("N0")); + sb.Append(pl.Score == 1 ? " point" : " points"); + + if (pl.Kills > 0) + { + sb.Append(", "); + sb.Append(pl.Kills.ToString("N0")); + sb.Append(pl.Kills == 1 ? " kill" : " kills"); + } + + if (pl.Captures > 0) + { + sb.Append(", "); + sb.Append(pl.Captures.ToString("N0")); + sb.Append(pl.Captures == 1 ? " capture" : " captures"); + } + } + + Item item = new Trophy(sb.ToString(), rank); + + if (pl == leader) + { + item.ItemID = 4810; + } + + item.Name = $"{item.Name}, {teams[i].Name.ToLower()} team"; if (!mob.PlaceInBackpack(item)) { mob.BankBox.DropItem(item); } - mob.SendMessage( - $"You have been awarded a {rank.LowerName()} trophy and {cash:N0}gp for your participation in this tournament." - ); + var cash = pl.Score * 250; + + if (cash > 0) + { + item = new BankCheck(cash); + + if (!mob.PlaceInBackpack(item)) + { + mob.BankBox.DropItem(item); + } + + mob.SendMessage( + $"You have been awarded a {rank.ToString().ToLower()} trophy and {cash:N0}gp for your participation in this tournament." + ); + } + else + { + mob.SendMessage( + $"You have been awarded a {rank.ToString().ToLower()} trophy for your participation in this tournament." + ); + } } - else + } + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var p = m_Context.Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) { - mob.SendMessage( - $"You have been awarded a {rank.LowerName()} trophy for your participation in this tournament." - ); + var dp = p.Players[j]; + + if (dp?.Mobile != null) + { + dp.Mobile.SendGump(new DDBoardGump(dp.Mobile, this)); + } } - } - } - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var p = m_Context.Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - var dp = p.Players[j]; - - if (dp?.Mobile != null) + if (i == winner?.TeamID) { - DDBoardGump.DisplayTo(dp.Mobile, this); + continue; + } + + for (var j = 0; j < p.Players.Length; ++j) + { + if (p.Players[j] != null) + { + p.Players[j].Eliminated = true; + } } } - if (i == winner?.TeamID) + if (winner != null) { - continue; - } - - for (var j = 0; j < p.Players.Length; ++j) - { - p.Players[j]?.Eliminated = true; + m_Context.Finish(m_Context.Participants[winner.TeamID]); } } - if (winner != null) + public override void OnStop() { - m_Context.Finish(m_Context.Participants[winner.TeamID]); - } - } + for (var i = 0; i < Controller.TeamInfo.Length; ++i) + { + var teamInfo = Controller.TeamInfo[i]; - public override void OnStop() - { - for (var i = 0; i < Controller.TeamInfo.Length; ++i) - { - var teamInfo = Controller.TeamInfo[i]; + if (teamInfo.Board != null) + { + teamInfo.Board.m_TeamInfo = null; + } - teamInfo.Board?.m_TeamInfo = null; + teamInfo.Game = null; + } - teamInfo.Game = null; - } + if (Controller.PointA != null) + { + Controller.PointA.Game = null; + } - Controller.PointA?.Game = null; - - Controller.PointB?.Game = null; - - m_Capturable = false; - - _captureTimerToken.Cancel(); - _uncaptureTimerToken.Cancel(); - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - ApplyHues(m_Context.Participants[i], -1); - } - - _finishTimerToken.Cancel(); - } - - public void Dominate(DDWayPoint point, Mobile from, DDTeamInfo team) - { - if (point == null || from == null || team == null || !m_Capturable) - { - return; - } - - var wasDom = Controller.PointA?.TeamOwner == Controller.PointB?.TeamOwner && - Controller.PointA?.TeamOwner != null; - - point.TeamOwner = team; - Alert($"{team.Name} has captured {point.Name}!"); - - var isDom = Controller.PointA?.TeamOwner == Controller.PointB?.TeamOwner && Controller.PointA?.TeamOwner != null; - - if (wasDom && !isDom) - { - Alert("Domination averted!"); - - Controller.PointA?.SetNonCaptureHue(); - Controller.PointB?.SetNonCaptureHue(); - _captureTimerToken.Cancel(); - } - - if (!wasDom && isDom) - { - m_CapStage = 0; - Timer.StartTimer(TimeSpan.Zero, TimeSpan.FromSeconds(1.0), CaptureTick, out _captureTimerToken); - } - } - - private void CaptureTick() - { - var team = Controller.PointA?.TeamOwner ?? Controller.PointB?.TeamOwner; - - if (team == null) - { - m_Capturable = true; - _captureTimerToken.Cancel(); - return; - } - - if (++m_CapStage < 10) - { - Alert($"{team.Name} is dominating... {10 - m_CapStage}"); - - Controller.PointA?.SetCaptureHue(m_CapStage); - Controller.PointB?.SetCaptureHue(m_CapStage); - } - else - { - Alert($"{team.Name} has scored!"); - - team.Score += 100; - team.Captures += 1; + if (Controller.PointB != null) + { + Controller.PointB.Game = null; + } m_Capturable = false; - m_CapStage = 0; + _captureTimerToken.Cancel(); + _uncaptureTimerToken.Cancel(); + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + ApplyHues(m_Context.Participants[i], -1); + } + + _finishTimerToken.Cancel(); + } + + public void Dominate(DDWayPoint point, Mobile from, DDTeamInfo team) + { + if (point == null || from == null || team == null || !m_Capturable) + { + return; + } + + var wasDom = Controller.PointA?.TeamOwner == Controller.PointB?.TeamOwner && + Controller.PointA?.TeamOwner != null; + + point.TeamOwner = team; + Alert($"{team.Name} has captured {point.Name}!"); + + var isDom = Controller.PointA?.TeamOwner == Controller.PointB?.TeamOwner && Controller.PointA?.TeamOwner != null; + + if (wasDom && !isDom) + { + Alert("Domination averted!"); + + Controller.PointA?.SetNonCaptureHue(); + Controller.PointB?.SetNonCaptureHue(); + _captureTimerToken.Cancel(); + } + + if (!wasDom && isDom) + { + m_CapStage = 0; + Timer.StartTimer(TimeSpan.Zero, TimeSpan.FromSeconds(1.0), CaptureTick, out _captureTimerToken); + } + } + + private void CaptureTick() + { + var team = Controller.PointA?.TeamOwner ?? Controller.PointB?.TeamOwner; + + if (team == null) + { + m_Capturable = true; + _captureTimerToken.Cancel(); + return; + } + + if (++m_CapStage < 10) + { + Alert($"{team.Name} is dominating... {10 - m_CapStage}"); + + Controller.PointA?.SetCaptureHue(m_CapStage); + Controller.PointB?.SetCaptureHue(m_CapStage); + } + else + { + Alert($"{team.Name} has scored!"); + + team.Score += 100; + team.Captures += 1; + + m_Capturable = false; + m_CapStage = 0; + _captureTimerToken.Cancel(); + + if (Controller.PointA != null) + { + Controller.PointA.TeamOwner = null; + Controller.PointA.SetUncapturableHue(); + } + + if (Controller.PointB != null) + { + Controller.PointB.TeamOwner = null; + Controller.PointB.SetUncapturableHue(); + } + + Timer.StartTimer(TimeSpan.FromSeconds(30.0), UncaptureTick, out _uncaptureTimerToken); + } + } + + private void UncaptureTick() + { + m_Capturable = true; + + _captureTimerToken.Cancel(); + _uncaptureTimerToken.Cancel(); if (Controller.PointA != null) { Controller.PointA.TeamOwner = null; - Controller.PointA.SetUncapturableHue(); + Controller.PointA.SetNonCaptureHue(); } if (Controller.PointB != null) { Controller.PointB.TeamOwner = null; - Controller.PointB.SetUncapturableHue(); + Controller.PointB.SetNonCaptureHue(); } - - Timer.StartTimer(TimeSpan.FromSeconds(30.0), UncaptureTick, out _uncaptureTimerToken); } } - private void UncaptureTick() + public class DDWayPoint : BaseAddon { - m_Capturable = true; + public const int UncapturableHue = 0x497; + public const int NonCapturedHue = 0x38A; + private DDGame m_Game; + private DDTeamInfo m_TeamOwner; - _captureTimerToken.Cancel(); - _uncaptureTimerToken.Cancel(); - - if (Controller.PointA != null) + [Constructible] + public DDWayPoint() { - Controller.PointA.TeamOwner = null; - Controller.PointA.SetNonCaptureHue(); + ItemID = 0x519; + Visible = true; + Name = "SET MY NAME"; + + AddComponent(new DDStep(0x7A8), -1, -1, -5); + AddComponent(new DDStep(0x7A6), 0, -1, -5); + AddComponent(new DDStep(0x7AA), 1, -1, -5); + + AddComponent(new DDStep(0x7A5), 1, 0, -5); + + AddComponent(new DDStep(0x7A9), 1, 1, -5); + AddComponent(new DDStep(0x7A4), 0, 1, -5); + AddComponent(new DDStep(0x7AB), -1, 1, -5); + + AddComponent(new DDStep(0x7A7), -1, 0, -5); + + SetUncapturableHue(); } - if (Controller.PointB != null) + public DDWayPoint(Serial serial) : base(serial) { - Controller.PointB.TeamOwner = null; - Controller.PointB.SetNonCaptureHue(); } - } -} -[SerializationGenerator(0, false)] -public partial class DDWayPoint : BaseAddon -{ - public const int UncapturableHue = 0x497; - public const int NonCapturedHue = 0x38A; - private DDGame m_Game; - private DDTeamInfo m_TeamOwner; + public override bool ShareHue => false; - [Constructible] - public DDWayPoint() - { - ItemID = 0x519; - Visible = true; - Name = "SET MY NAME"; - - AddComponent(new DDStep(0x7A8), -1, -1, -5); - AddComponent(new DDStep(0x7A6), 0, -1, -5); - AddComponent(new DDStep(0x7AA), 1, -1, -5); - - AddComponent(new DDStep(0x7A5), 1, 0, -5); - - AddComponent(new DDStep(0x7A9), 1, 1, -5); - AddComponent(new DDStep(0x7A4), 0, 1, -5); - AddComponent(new DDStep(0x7AB), -1, 1, -5); - - AddComponent(new DDStep(0x7A7), -1, 0, -5); - - SetUncapturableHue(); - } - - public override bool ShareHue => false; - - public DDGame Game - { - get => m_Game; - set + public DDGame Game { - m_Game = value; - m_TeamOwner = null; - - if (m_Game != null) + get => m_Game; + set { + m_Game = value; + m_TeamOwner = null; + + if (m_Game != null) + { + SetNonCaptureHue(); + } + else + { + SetUncapturableHue(); + } + } + } + + public DDTeamInfo TeamOwner + { + get => m_TeamOwner; + set + { + m_TeamOwner = value; + SetNonCaptureHue(); } - else + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } + + public void SetUncapturableHue() + { + for (var i = 0; i < Components.Count; i++) { - SetUncapturableHue(); + Components[i].Hue = UncapturableHue; } - } - } - public DDTeamInfo TeamOwner - { - get => m_TeamOwner; - set - { - m_TeamOwner = value; - - SetNonCaptureHue(); - } - } - - public void SetUncapturableHue() - { - for (var i = 0; i < Components.Count; i++) - { - Components[i].Hue = UncapturableHue; + Hue = UncapturableHue; } - Hue = UncapturableHue; - } - - public void SetNonCaptureHue() - { - for (var i = 0; i < Components.Count; i++) + public void SetNonCaptureHue() { - Components[i].Hue = NonCapturedHue; - } - - if (m_TeamOwner != null) - { - Hue = m_TeamOwner.Color; - } - else - { - Hue = NonCapturedHue; - } - } - - public void SetCaptureHue(int stage) - { - if (m_TeamOwner == null) - { - return; - } - - Hue = m_TeamOwner.Color; - - for (var i = 0; i < Components.Count; i++) - { - if (i < stage) - { - Components[i].Hue = m_TeamOwner.Color; - } - else + for (var i = 0; i < Components.Count; i++) { Components[i].Hue = NonCapturedHue; } - } - } - public override bool OnMoveOver(Mobile from) - { - if (m_Game == null) - { - SetUncapturableHue(); - } - else if (from.Alive) - { - var team = m_Game.GetTeamInfo(from); - - if (team != null && team != TeamOwner) + if (m_TeamOwner != null) { - m_Game.Dominate(this, from, team); + Hue = m_TeamOwner.Color; + } + else + { + Hue = NonCapturedHue; } } - return true; - } + public void SetCaptureHue(int stage) + { + if (m_TeamOwner == null) + { + return; + } - [SerializationGenerator(0, false)] - public partial class DDStep : AddonComponent - { - public DDStep(int itemID) : base(itemID) => Visible = true; + Hue = m_TeamOwner.Color; - public override bool OnMoveOver(Mobile m) => Addon.OnMoveOver(m); + for (var i = 0; i < Components.Count; i++) + { + if (i < stage) + { + Components[i].Hue = m_TeamOwner.Color; + } + else + { + Components[i].Hue = NonCapturedHue; + } + } + } + + public override bool OnMoveOver(Mobile from) + { + if (m_Game == null) + { + SetUncapturableHue(); + } + else if (from.Alive) + { + var team = m_Game.GetTeamInfo(from); + + if (team != null && team != TeamOwner) + { + m_Game.Dominate(this, from, team); + } + } + + return true; + } + + public class DDStep : AddonComponent + { + public DDStep(int itemID) : base(itemID) => Visible = true; + + public DDStep(Serial serial) : base(serial) + { + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override bool OnMoveOver(Mobile m) => Addon.OnMoveOver(m); + } } } diff --git a/Projects/UOContent/Engines/ConPVP/Games/EventGame.cs b/Projects/UOContent/Engines/ConPVP/Games/EventGame.cs index 623a25f72..fecc2fdd4 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/EventGame.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/EventGame.cs @@ -1,51 +1,70 @@ -using ModernUO.Serialization; using Server.Gumps; using Server.Items; -namespace Server.Engines.ConPVP; - -[SerializationGenerator(0, false)] -public abstract partial class EventController : Item +namespace Server.Engines.ConPVP { - public EventController() : base(0x1B7A) + public abstract class EventController : Item { - Visible = false; - Movable = false; + public EventController() + : base(0x1B7A) + { + Visible = false; + Movable = false; + } + + public EventController(Serial serial) + : base(serial) + { + } + + public abstract string Title { get; } + public abstract EventGame Construct(DuelContext dc); + + public abstract string GetTeamName(int teamID); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } + + public override void OnDoubleClick(Mobile from) + { + if (from.AccessLevel >= AccessLevel.GameMaster) + { + from.SendGump(new PropertiesGump(from, this)); + } + } } - public abstract string Title { get; } - public abstract EventGame Construct(DuelContext dc); - - public abstract string GetTeamName(int teamID); - - public override void OnDoubleClick(Mobile from) + public abstract class EventGame { - if (from.AccessLevel >= AccessLevel.GameMaster) + protected DuelContext m_Context; + + public EventGame(DuelContext context) => m_Context = context; + + public DuelContext Context => m_Context; + + public virtual bool FreeConsume => true; + + public virtual bool OnDeath(Mobile mob, Container corpse) => true; + + public virtual bool CantDoAnything(Mobile mob) => false; + + public virtual void OnStart() + { + } + + public virtual void OnStop() { - from.SendGump(new PropertiesGump(from, this)); } } } - -public abstract class EventGame -{ - protected DuelContext m_Context; - - public EventGame(DuelContext context) => m_Context = context; - - public DuelContext Context => m_Context; - - public virtual bool FreeConsume => true; - - public virtual bool OnDeath(Mobile mob, Container corpse) => true; - - public virtual bool CantDoAnything(Mobile mob) => false; - - public virtual void OnStart() - { - } - - public virtual void OnStop() - { - } -} diff --git a/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs b/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs index 6aec6f328..f1d20e90f 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/KingOfTheHill.cs @@ -1,1189 +1,1260 @@ using System; using System.Collections.Generic; -using ModernUO.Serialization; +using System.Text; using Server.Gumps; using Server.Items; using Server.Mobiles; -using Server.Text; -namespace Server.Engines.ConPVP; - -[SerializationGenerator(0, false)] -public partial class HillOfTheKing : Item +namespace Server.Engines.ConPVP { - private KHGame m_Game; - - [SerializedCommandProperty(AccessLevel.GameMaster)] - private KingTimer m_KingTimer; - - [EncodedInt] - [SerializableField(0)] - private int _scoreInterval; - - [Constructible] - public HillOfTheKing() : base(0x520) + public class HillOfTheKing : Item { - m_Game = null; - King = null; - Movable = false; + private KHGame m_Game; + private KingTimer m_KingTimer; - Name = "the hill"; - } - - public Mobile King { get; private set; } - - public KHGame Game - { - get => m_Game; - set + [Constructible] + public HillOfTheKing() + : base(0x520) { - if (m_Game != value) + ScoreInterval = 10; + m_Game = null; + King = null; + Movable = false; + + Name = "the hill"; + } + + public HillOfTheKing(Serial s) : base(s) + { + } + + public Mobile King { get; private set; } + + public KHGame Game + { + get => m_Game; + set { - m_KingTimer?.Stop(); - m_Game = value; - King = null; - } - } - } - - public int CapturesSoFar => m_KingTimer?.Captures ?? 0; - - private bool CanBeKing(Mobile m) - { - // Game running? - if (m_Game == null) - { - return false; - } - - // Mobile exists and is alive and is a player? - if (m?.Deleted != false || !m.Alive || !m.Player) - { - return false; - } - - // Not current king (or they are the current king) - if (King != null && King != m) - { - return false; - } - - // They are on a team - return m_Game.GetTeamInfo(m) != null; - } - - public override bool OnMoveOver(Mobile m) - { - if (m_Game == null || m?.Alive != true) - { - return base.OnMoveOver(m); - } - - if (CanBeKing(m)) - { - if (base.OnMoveOver(m)) - { - ReKingify(m); - return true; - } - } - else - { - // Decrease their stam a little so they don't keep pushing someone out of the way - if (m.AccessLevel == AccessLevel.Player && m.Stam >= m.StamMax) - { - m.Stam -= 5; - } - } - - return false; - } - - public override bool OnMoveOff(Mobile m) - { - if (base.OnMoveOff(m)) - { - if (King == m) - { - DeKingify(); - } - - return true; - } - - return false; - } - - public virtual void OnKingDied(Mobile king, KHTeamInfo kingTeam, Mobile killer, KHTeamInfo killerTeam) - { - if (m_Game != null && CapturesSoFar > 0 && killer != null && king != null && kingTeam != null && - killerTeam != null) - { - var kingName = king.Name ?? ""; - var killerName = killer.Name ?? ""; - - m_Game.Alert($"{kingName} ({kingTeam.Name}) was dethroned by {killerName} ({killerTeam.Name})!"); - } - - DeKingify(); - } - - private void DeKingify() - { - PublicOverheadMessage(MessageType.Regular, 0x0481, false, "Free!"); - - m_KingTimer?.Stop(); - - King = null; - } - - private void ReKingify(Mobile m) - { - if (m_Game == null || m == null) - { - return; - } - - if (m_Game.GetTeamInfo(m) == null) - { - return; - } - - King = m; - - m_KingTimer ??= new KingTimer(this); - m_KingTimer.Stop(); - m_KingTimer.StartHillTicker(); - - if (King.Name != null) - { - PublicOverheadMessage(MessageType.Regular, 0x0481, false, $"Taken by {King.Name}!"); - } - } - - private class KingTimer : Timer - { - private readonly HillOfTheKing m_Hill; - private int m_Counter; - - public KingTimer(HillOfTheKing hill) - : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0)) - { - m_Hill = hill; - Captures = 0; - m_Counter = 0; - } - - public int Captures { get; private set; } - - public void StartHillTicker() - { - Captures = 0; - m_Counter = 0; - - Start(); - } - - protected override void OnTick() - { - KHPlayerInfo pi = null; - - if (m_Hill?.Deleted != false || m_Hill.Game == null) - { - Stop(); - return; - } - - if (m_Hill.King?.Deleted != false || !m_Hill.King.Alive) - { - m_Hill.DeKingify(); - Stop(); - return; - } - - var ti = m_Hill.Game.GetTeamInfo(m_Hill.King); - if (ti != null) - { - pi = ti[m_Hill.King]; - } - - if (ti == null || pi == null) - { - // error, bail - m_Hill.DeKingify(); - Stop(); - return; - } - - m_Counter++; - - m_Hill.King.RevealingAction(); - - if (m_Counter >= m_Hill.ScoreInterval) - { - var hill = m_Hill.Name.DefaultIfNullOrEmpty("the hill"); - var king = m_Hill.King.Name ?? ""; - - m_Hill.Game.Alert($"{king} ({ti.Name}) is king of {hill}!"); - - m_Hill.PublicOverheadMessage(MessageType.Regular, 0x0481, false, "Capture!"); - - pi.Captures++; - Captures++; - - pi.Score += m_Counter; - - m_Counter = 0; - } - else - { - m_Hill.PublicOverheadMessage( - MessageType.Regular, - 0x0481, - false, - (m_Hill.ScoreInterval - m_Counter).ToString() - ); - } - } - } -} - -[SerializationGenerator(0, false)] -public partial class KHBoard : Item -{ - [SerializedCommandProperty(AccessLevel.GameMaster)] - [SerializableField(0, fieldChanged: nameof(OnControllerChanged))] - private KHController _controller; - - private void OnControllerChanged(KHController oldValue, KHController newValue) - { - oldValue?.RemoveBoard(this); - newValue?.AddBoard(this); - } - - public KHGame m_Game; - - [Constructible] - public KHBoard() : base(7774) - { - Name = "King of the Hill Scoreboard"; - Movable = false; - } - - public override void OnDoubleClick(Mobile from) - { - if (m_Game != null) - { - KHBoardGump.DisplayTo(from, m_Game); - } - else - { - from.SendMessage("There is no King of the Hill game in progress."); - } - } -} - -public sealed class KHBoardGump : DynamicGump -{ - private const int LabelColor32 = 0xFFFFFF; - private const int BlackColor32 = 0x000000; - - private readonly Mobile _mob; - private readonly KHGame _game; - - public override bool Singleton => true; - - private KHBoardGump(Mobile mob, KHGame game) : base(60, 60) - { - _mob = mob; - _game = game; - } - - public static void DisplayTo(Mobile mob, KHGame game) - { - if (mob?.NetState == null || game == null) - { - return; - } - - mob.SendGump(new KHBoardGump(mob, game)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var ourTeam = _game.GetTeamInfo(_mob); - - var entries = new List(); - - for (var i = 0; i < _game.Context.Participants.Count; ++i) - { - var teamInfo = _game.Controller.TeamInfo[i % _game.Controller.TeamInfo.Length]; - - if (teamInfo != null) - { - entries.Add(teamInfo); - } - } - - entries.Sort(); - - var height = 73 + entries.Count * 75 + 28; - - builder.SetNoClose(); - - builder.AddPage(); - - builder.AddBackground(1, 1, 398, height, 3600); - - builder.AddImageTiled(16, 15, 369, height - 29, 3604); - - for (var i = 0; i < entries.Count; i += 1) - { - builder.AddImageTiled(22, 58 + i * 75, 357, 70, 0x2430); - } - - builder.AddAlphaRegion(16, 15, 369, height - 29); - - builder.AddImage(215, -45, 0xEE40); - - AddBorderedText(ref builder, 22, 22, 294, 20, "King of the Hill Scoreboard".Center(), LabelColor32, BlackColor32); - - builder.AddImageTiled(32, 50, 264, 1, 9107); - builder.AddImageTiled(42, 52, 264, 1, 9157); - - for (var i = 0; i < entries.Count; ++i) - { - var teamInfo = entries[i]; - - builder.AddImage(30, 70 + i * 75, 10152); - builder.AddImage(30, 85 + i * 75, 10151); - builder.AddImage(30, 100 + i * 75, 10151); - builder.AddImage(30, 106 + i * 75, 10154); - - builder.AddImage(24, 60 + i * 75, teamInfo == ourTeam ? 9730 : 9727, teamInfo.Color - 1); - - var borderColor = teamInfo.Color == 0x455 ? LabelColor32 : BlackColor32; - var nameColor = teamInfo.Color switch - { - 0x47E => 0xFFFFFF, - 0x4F2 => 0x3399FF, - 0x4F7 => 0x33FF33, - 0x4FC => 0xFF00FF, - 0x021 => 0xFF3333, - 0x01A => 0xFF66FF, - 0x455 => 0x333333, - _ => LabelColor32 - }; - - AddBorderedText( - ref builder, - 60, - 65 + i * 75, - 250, - 20, - $"{LadderGump.Rank(1 + i)}: {teamInfo.Name}", - nameColor, - borderColor - ); - - AddBorderedText(ref builder, 50 + 10, 85 + i * 75, 100, 20, "Score:", 0xFFC000, BlackColor32); - AddBorderedText(ref builder, 50 + 15, 105 + i * 75, 100, 20, $"{teamInfo.Score:N0}", 0xFFC000, BlackColor32); - - AddBorderedText(ref builder, 110 + 10, 85 + i * 75, 100, 20, "Kills:", 0xFFC000, BlackColor32); - AddBorderedText(ref builder, 110 + 15, 105 + i * 75, 100, 20, $"{teamInfo.Kills:N0}", 0xFFC000, BlackColor32); - - AddBorderedText(ref builder, 160 + 10, 85 + i * 75, 100, 20, "Captures:", 0xFFC000, BlackColor32); - AddBorderedText(ref builder, 160 + 15, 105 + i * 75, 100, 20, $"{teamInfo.Captures:N0}", 0xFFC000, BlackColor32); - - var leader = teamInfo.Leader?.Name ?? "(none)"; - - AddBorderedText(ref builder, 235 + 10, 85 + i * 75, 250, 20, "Leader:", 0xFFC000, BlackColor32); - AddBorderedText(ref builder, 235 + 15, 105 + i * 75, 250, 20, leader, 0xFFC000, BlackColor32); - } - - builder.AddButton(314, height - 42, 247, 248, 1); - } - - private static void AddBorderedText(ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color, int borderColor) - { - AddColoredText(ref builder, x - 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x - 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x, y, width, height, text, color); - } - - private static void AddColoredText(ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color) - { - builder.AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); - } -} - -public sealed class KHPlayerInfo : IRankedCTF, IComparable -{ - private readonly KHTeamInfo m_TeamInfo; - private int m_Captures; - - private int m_Kills; - private int m_Score; - - public KHPlayerInfo(KHTeamInfo teamInfo, Mobile player) - { - m_TeamInfo = teamInfo; - Player = player; - } - - public Mobile Player { get; } - - public int CompareTo(KHPlayerInfo pi) - { - var res = pi.Score.CompareTo(Score); - if (res != 0) - { - return res; - } - - res = pi.Captures.CompareTo(Captures); - - return res != 0 ? res : pi.Kills.CompareTo(Kills); - } - - public string Name => Player.Name ?? ""; - - public int Kills - { - get => m_Kills; - set - { - m_TeamInfo.Kills += value - m_Kills; - m_Kills = value; - } - } - - public int Captures - { - get => m_Captures; - set - { - m_TeamInfo.Captures += value - m_Captures; - m_Captures = value; - } - } - - public int Score - { - get => m_Score; - set - { - m_TeamInfo.Score += value - m_Score; - m_Score = value; - - if (m_TeamInfo.Leader == null || m_Score > m_TeamInfo.Leader.Score) - { - m_TeamInfo.Leader = this; - } - } - } -} - -[PropertyObject] -public sealed class KHTeamInfo : IRankedCTF, IComparable -{ - public KHTeamInfo(int teamID) - { - TeamID = teamID; - Players = new Dictionary(); - } - - public KHTeamInfo(int teamID, IGenericReader ip) - { - TeamID = teamID; - Players = new Dictionary(); - - var version = ip.ReadEncodedInt(); - - switch (version) - { - case 0: + if (m_Game != value) { - TeamName = ip.ReadString(); - Color = ip.ReadEncodedInt(); - break; + m_KingTimer?.Stop(); + m_Game = value; + King = null; } - } - } - - public KHGame Game { get; set; } - - public int TeamID { get; } - - public KHPlayerInfo Leader { get; set; } - - public Dictionary Players { get; } - - public KHPlayerInfo this[Mobile mob] - { - get - { - if (mob == null) - { - return null; } + } - if (!Players.TryGetValue(mob, out var val)) + [CommandProperty(AccessLevel.GameMaster)] + public int ScoreInterval { get; set; } + + public int CapturesSoFar => m_KingTimer?.Captures ?? 0; + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) { - Players[mob] = val = new KHPlayerInfo(this, mob); - } - - return val; - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public int Color { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public string TeamName { get; set; } - - public int CompareTo(KHTeamInfo ti) - { - var res = ti.Score.CompareTo(Score); - if (res != 0) - { - return res; - } - - res = ti.Captures.CompareTo(Captures); - - if (res == 0) - { - res = ti.Kills.CompareTo(Kills); - } - - return res; - } - - public string Name => $"{TeamName ?? "(none)"} Team"; - - public int Kills { get; set; } - - public int Captures { get; set; } - - public int Score { get; set; } - - public void Reset() - { - Kills = 0; - Captures = 0; - Score = 0; - - Leader = null; - - Players.Clear(); - } - - public void Serialize(IGenericWriter op) - { - op.WriteEncodedInt(0); // version - - op.Write(TeamName); - op.WriteEncodedInt(Color); - } - - public override string ToString() => TeamName != null ? $"({Name}) ..." : "..."; -} - -public sealed class KHController : EventController -{ - private int m_ScoreInterval; - - [Constructible] - public KHController() - { - Visible = false; - Movable = false; - - Name = "King of the Hill Controller"; - - Duration = TimeSpan.FromMinutes(30.0); - Boards = new List(); - Hills = new HillOfTheKing[4]; - TeamInfo = new KHTeamInfo[8]; - - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i] = new KHTeamInfo(i); - } - } - - public KHController(Serial serial) - : base(serial) - { - } - - public KHTeamInfo[] TeamInfo { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public KHTeamInfo Team1_W => TeamInfo[0]; - - [CommandProperty(AccessLevel.GameMaster)] - public KHTeamInfo Team2_E => TeamInfo[1]; - - [CommandProperty(AccessLevel.GameMaster)] - public KHTeamInfo Team3_N => TeamInfo[2]; - - [CommandProperty(AccessLevel.GameMaster)] - public KHTeamInfo Team4_S => TeamInfo[3]; - - [CommandProperty(AccessLevel.GameMaster)] - public KHTeamInfo Team5_NW => TeamInfo[4]; - - [CommandProperty(AccessLevel.GameMaster)] - public KHTeamInfo Team6_SE => TeamInfo[5]; - - [CommandProperty(AccessLevel.GameMaster)] - public KHTeamInfo Team7_SW => TeamInfo[6]; - - [CommandProperty(AccessLevel.GameMaster)] - public KHTeamInfo Team8_NE => TeamInfo[7]; - - public HillOfTheKing[] Hills { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public HillOfTheKing Hill1 - { - get => Hills[0]; - set => Hills[0] = value; - } - - [CommandProperty(AccessLevel.GameMaster)] - public HillOfTheKing Hill2 - { - get => Hills[1]; - set => Hills[1] = value; - } - - [CommandProperty(AccessLevel.GameMaster)] - public HillOfTheKing Hill3 - { - get => Hills[2]; - set => Hills[2] = value; - } - - [CommandProperty(AccessLevel.GameMaster)] - public HillOfTheKing Hill4 - { - get => Hills[3]; - set => Hills[3] = value; - } - - public List Boards { get; private set; } - - [CommandProperty(AccessLevel.GameMaster)] - public TimeSpan Duration { get; set; } - - public override string Title => "King of the Hill"; - - public override string GetTeamName(int teamID) => TeamInfo[teamID % TeamInfo.Length].Name; - - public override EventGame Construct(DuelContext context) => new KHGame(this, context); - - public void RemoveBoard(KHBoard b) - { - if (b != null) - { - Boards.Remove(b); - b.m_Game = null; - } - } - - public void AddBoard(KHBoard b) - { - if (b != null) - { - Boards.Add(b); - } - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); - - writer.WriteEncodedInt(m_ScoreInterval); - writer.Write(Duration); - - Boards.Tidy(); - writer.Write(Boards); - - writer.WriteEncodedInt(Hills.Length); - for (var i = 0; i < Hills.Length; ++i) - { - writer.Write(Hills[i]); - } - - writer.WriteEncodedInt(TeamInfo.Length); - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i].Serialize(writer); - } - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - m_ScoreInterval = reader.ReadEncodedInt(); - - Duration = reader.ReadTimeSpan(); - - Boards = reader.ReadEntityList(); - - Hills = new HillOfTheKing[reader.ReadEncodedInt()]; - for (var i = 0; i < Hills.Length; ++i) + case 0: { - Hills[i] = reader.ReadEntity(); + ScoreInterval = reader.ReadEncodedInt(); + break; } - - TeamInfo = new KHTeamInfo[reader.ReadEncodedInt()]; - for (var i = 0; i < TeamInfo.Length; ++i) - { - TeamInfo[i] = new KHTeamInfo(i, reader); - } - - break; - } + } } - } -} -public sealed class KHGame : EventGame -{ - private TimerExecutionToken _finishTimerToken; - - public KHGame(KHController controller, DuelContext context) : base(context) => Controller = controller; - - public KHController Controller { get; } - - public Map Facet - { - get + public override void Serialize(IGenericWriter writer) { - if (m_Context?.Arena != null) + base.Serialize(writer); + + writer.Write(0); // version + + writer.WriteEncodedInt(ScoreInterval); + } + + private bool CanBeKing(Mobile m) + { + // Game running? + if (m_Game == null) { - return m_Context.Arena.Facet; + return false; } - return Controller.Map; - } - } - - public override bool CantDoAnything(Mobile mob) - { - if (mob != null && GetTeamInfo(mob) != null && Controller != null) - { - for (var i = 0; i < Controller.Hills.Length; i++) + // Mobile exists and is alive and is a player? + if (m?.Deleted != false || !m.Alive || !m.Player) { - if (Controller.Hills[i]?.King == mob) + return false; + } + + // Not current king (or they are the current king) + if (King != null && King != m) + { + return false; + } + + // They are on a team + return m_Game.GetTeamInfo(m) != null; + } + + public override bool OnMoveOver(Mobile m) + { + if (m_Game == null || m?.Alive != true) + { + return base.OnMoveOver(m); + } + + if (CanBeKing(m)) + { + if (base.OnMoveOver(m)) { + ReKingify(m); return true; } } - } - - return false; - } - - public void Alert(string text) - { - m_Context.m_Tournament?.Alert(text); - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var p = m_Context.Participants[i]; - - for (var j = 0; j < p.Players.Length; ++j) - { - p.Players[j]?.Mobile.SendMessage(0x35, text); - } - } - } - - public KHTeamInfo GetTeamInfo(Mobile mob) - { - var teamID = GetTeamID(mob); - - if (teamID >= 0) - { - return Controller.TeamInfo[teamID % Controller.TeamInfo.Length]; - } - - return null; - } - - public int GetTeamID(Mobile mob) - { - if (mob is not PlayerMobile pm) - { - return mob is BaseCreature creature ? creature.Team - 1 : -1; - } - - if (pm.DuelContext == null || pm.DuelContext != m_Context) - { - return -1; - } - - if (pm.DuelPlayer?.Eliminated != false) - { - return -1; - } - - return pm.DuelContext.Participants.IndexOf(pm.DuelPlayer.Participant); - } - - public int GetColor(Mobile mob) => GetTeamInfo(mob)?.Color ?? -1; - - private void ApplyHues(Participant p, int hueOverride) - { - for (var i = 0; i < p.Players.Length; ++i) - { - p.Players[i]?.Mobile.SolidHueOverride = hueOverride; - } - } - - public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) - { - Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); - } - - private void DelayBounce_Callback(Mobile mob, Container corpse) - { - var dp = (mob as PlayerMobile)?.DuelPlayer; - - m_Context.RemoveAggressions(mob); - - if (dp?.Eliminated == false) - { - mob.MoveToWorld(m_Context.Arena.GetBaseStartPoint(GetTeamID(mob)), Facet); - } - else - { - m_Context.SendOutside(mob); - } - - m_Context.Refresh(mob, corpse); - DuelContext.Debuff(mob); - DuelContext.CancelSpell(mob); - mob.Frozen = false; - - if (corpse?.Deleted == false) - { - Timer.StartTimer(TimeSpan.FromSeconds(30), corpse.Delete); - } - } - - public override bool OnDeath(Mobile mob, Container corpse) - { - var killer = mob.FindMostRecentDamager(false); - KHTeamInfo teamInfo = null; - var victInfo = GetTeamInfo(mob); - var bonus = 0; - - if (killer?.Player == true) - { - teamInfo = GetTeamInfo(killer); - } - - for (var i = 0; i < Controller.Hills.Length; i++) - { - if (Controller.Hills[i] == null) - { - continue; - } - - if (Controller.Hills[i].King == mob) - { - bonus += Controller.Hills[i].CapturesSoFar; - Controller.Hills[i].OnKingDied(mob, victInfo, killer, teamInfo); - } - - if (Controller.Hills[i].King == killer) - { - bonus += 2; - } - } - - if (teamInfo != null && teamInfo != victInfo) - { - var playerInfo = teamInfo[killer]; - - if (playerInfo != null) - { - playerInfo.Kills += 1; - playerInfo.Score += 1 + bonus; - } - } - - KHBoardGump.DisplayTo(mob, this); - - m_Context.Requip(mob, corpse); - DelayBounce(TimeSpan.FromSeconds(30.0), mob, corpse); - - return false; - } - - public override void OnStart() - { - for (var i = 0; i < Controller.TeamInfo.Length; ++i) - { - var teamInfo = Controller.TeamInfo[i]; - - teamInfo.Game = this; - teamInfo.Reset(); - } - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - ApplyHues( - m_Context.Participants[i], - Controller.TeamInfo[i % Controller.TeamInfo.Length].Color - ); - } - - for (var i = 0; i < Controller.Hills.Length; i++) - { - Controller.Hills[i]?.Game = this; - } - - foreach (var board in Controller.Boards) - { - if (board?.Deleted == false) - { - board.m_Game = this; - } - } - - _finishTimerToken.Cancel(); - Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); - } - - private void Finish_Callback() - { - var teams = new List(); - - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var teamInfo = Controller.TeamInfo[i % Controller.TeamInfo.Length]; - - if (teamInfo != null) - { - teams.Add(teamInfo); - } - } - - teams.Sort(); - - var tourney = m_Context.m_Tournament; - - using var sb = ValueStringBuilder.Create(256); - - if (tourney != null) - { - if (tourney.TourneyType == TourneyType.FreeForAll) - { - sb.Append(m_Context.Participants.Count * tourney.PlayersPerParticipant); - sb.Append("-man FFA"); - } - else if (tourney.TourneyType == TourneyType.RandomTeam) - { - sb.Append(tourney.ParticipantsPerMatch); - sb.Append("-team"); - } - else if (tourney.TourneyType == TourneyType.RedVsBlue) - { - sb.Append("Red v Blue"); - } else { - for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) + // Decrease their stam a little so they don't keep pushing someone out of the way + if (m.AccessLevel == AccessLevel.Player && m.Stam >= m.StamMax) { - if (sb.Length > 0) - { - sb.Append('v'); - } + m.Stam -= 5; + } + } - sb.Append(tourney.PlayersPerParticipant); + return false; + } + + public override bool OnMoveOff(Mobile m) + { + if (base.OnMoveOff(m)) + { + if (King == m) + { + DeKingify(); + } + + return true; + } + + return false; + } + + public virtual void OnKingDied(Mobile king, KHTeamInfo kingTeam, Mobile killer, KHTeamInfo killerTeam) + { + if (m_Game != null && CapturesSoFar > 0 && killer != null && king != null && kingTeam != null && + killerTeam != null) + { + var kingName = king.Name ?? ""; + var killerName = killer.Name ?? ""; + + m_Game.Alert($"{kingName} ({kingTeam.Name}) was dethroned by {killerName} ({killerTeam.Name})!"); + } + + DeKingify(); + } + + private void DeKingify() + { + PublicOverheadMessage(MessageType.Regular, 0x0481, false, "Free!"); + + m_KingTimer?.Stop(); + + King = null; + } + + private void ReKingify(Mobile m) + { + if (m_Game == null || m == null) + { + return; + } + + if (m_Game.GetTeamInfo(m) == null) + { + return; + } + + King = m; + + m_KingTimer ??= new KingTimer(this); + m_KingTimer.Stop(); + m_KingTimer.StartHillTicker(); + + if (King.Name != null) + { + PublicOverheadMessage(MessageType.Regular, 0x0481, false, $"Taken by {King.Name}!"); + } + } + + private class KingTimer : Timer + { + private readonly HillOfTheKing m_Hill; + private int m_Counter; + + public KingTimer(HillOfTheKing hill) + : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0)) + { + m_Hill = hill; + Captures = 0; + m_Counter = 0; + } + + public int Captures { get; private set; } + + public void StartHillTicker() + { + Captures = 0; + m_Counter = 0; + + Start(); + } + + protected override void OnTick() + { + KHPlayerInfo pi = null; + + if (m_Hill?.Deleted != false || m_Hill.Game == null) + { + Stop(); + return; + } + + if (m_Hill.King?.Deleted != false || !m_Hill.King.Alive) + { + m_Hill.DeKingify(); + Stop(); + return; + } + + var ti = m_Hill.Game.GetTeamInfo(m_Hill.King); + if (ti != null) + { + pi = ti[m_Hill.King]; + } + + if (ti == null || pi == null) + { + // error, bail + m_Hill.DeKingify(); + Stop(); + return; + } + + m_Counter++; + + m_Hill.King.RevealingAction(); + + if (m_Counter >= m_Hill.ScoreInterval) + { + var hill = m_Hill.Name.DefaultIfNullOrEmpty("the hill"); + var king = m_Hill.King.Name ?? ""; + + m_Hill.Game.Alert($"{king} ({ti.Name}) is king of {hill}!"); + + m_Hill.PublicOverheadMessage(MessageType.Regular, 0x0481, false, "Capture!"); + + pi.Captures++; + Captures++; + + pi.Score += m_Counter; + + m_Counter = 0; + } + else + { + m_Hill.PublicOverheadMessage( + MessageType.Regular, + 0x0481, + false, + (m_Hill.ScoreInterval - m_Counter).ToString() + ); + } + } + } + } + + public class KHBoard : Item + { + private KHController m_Controller; + public KHGame m_Game; + + [Constructible] + public KHBoard() + : base(7774) + { + Name = "King of the Hill Scoreboard"; + Movable = false; + } + + public KHBoard(Serial serial) + : base(serial) + { + } + + [CommandProperty(AccessLevel.GameMaster)] + public KHController Controller + { + get => m_Controller; + set + { + if (m_Controller != value) + { + m_Controller?.RemoveBoard(this); + m_Controller = value; + m_Controller?.AddBoard(this); } } } - if (Controller != null) + public override void OnDoubleClick(Mobile from) { - sb.Append(' '); - sb.Append(Controller.Title); + if (m_Game != null) + { + from.SendGump(new KHBoardGump(from, m_Game)); + } + else + { + from.SendMessage("There is no King of the Hill game in progress."); + } } - var title = sb.ToString(); - - var winner = teams.Count > 0 ? teams[0] : null; - - for (var i = 0; i < teams.Count; ++i) + public override void Serialize(IGenericWriter writer) { - var rank = TrophyRank.Bronze; + base.Serialize(writer); - if (i == 0) + writer.Write(0); + + writer.Write(m_Controller); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) { - rank = TrophyRank.Gold; + case 0: + { + m_Controller = reader.ReadEntity(); + break; + } } - else if (i == 1) + } + } + + public sealed class KHBoardGump : Gump + { + private const int LabelColor32 = 0xFFFFFF; + private const int BlackColor32 = 0x000000; + + private KHGame m_Game; + + public override bool Singleton => true; + + public KHBoardGump(Mobile mob, KHGame game) + : base(60, 60) + { + m_Game = game; + + var ourTeam = game.GetTeamInfo(mob); + + var entries = new List(); + + for (var i = 0; i < game.Context.Participants.Count; ++i) { - rank = TrophyRank.Silver; + var teamInfo = game.Controller.TeamInfo[i % game.Controller.TeamInfo.Length]; + + if (teamInfo != null) + { + entries.Add(teamInfo); + } } - var leader = teams[i].Leader; - - foreach (var pl in teams[i].Players.Values) + entries.Sort(); + /* + delegate( IRankedCTF a, IRankedCTF b ) { - var mob = pl.Player; + return b.Score - a.Score; + } );*/ + var height = 73 + entries.Count * 75 + 28; + + Closable = false; + + AddPage(0); + + AddBackground(1, 1, 398, height, 3600); + + AddImageTiled(16, 15, 369, height - 29, 3604); + + for (var i = 0; i < entries.Count; i += 1) + { + AddImageTiled(22, 58 + i * 75, 357, 70, 0x2430); + } + + AddAlphaRegion(16, 15, 369, height - 29); + + AddImage(215, -45, 0xEE40); + // AddImage( 330, 141, 0x8BA ); + + AddBorderedText(22, 22, 294, 20, "King of the Hill Scoreboard".Center(), LabelColor32, BlackColor32); + + AddImageTiled(32, 50, 264, 1, 9107); + AddImageTiled(42, 52, 264, 1, 9157); + + for (var i = 0; i < entries.Count; ++i) + { + var teamInfo = entries[i]; + + AddImage(30, 70 + i * 75, 10152); + AddImage(30, 85 + i * 75, 10151); + AddImage(30, 100 + i * 75, 10151); + AddImage(30, 106 + i * 75, 10154); + + AddImage(24, 60 + i * 75, teamInfo == ourTeam ? 9730 : 9727, teamInfo.Color - 1); + + var borderColor = teamInfo.Color == 0x455 ? LabelColor32 : BlackColor32; + var nameColor = teamInfo.Color switch + { + 0x47E => 0xFFFFFF, + 0x4F2 => 0x3399FF, + 0x4F7 => 0x33FF33, + 0x4FC => 0xFF00FF, + 0x021 => 0xFF3333, + 0x01A => 0xFF66FF, + 0x455 => 0x333333, + _ => LabelColor32 + }; + + AddBorderedText( + 60, + 65 + i * 75, + 250, + 20, + $"{LadderGump.Rank(1 + i)}: {teamInfo.Name}", + nameColor, + borderColor + ); + + AddBorderedText(50 + 10, 85 + i * 75, 100, 20, "Score:", 0xFFC000, BlackColor32); + AddBorderedText(50 + 15, 105 + i * 75, 100, 20, teamInfo.Score.ToString("N0"), 0xFFC000, BlackColor32); + + AddBorderedText(110 + 10, 85 + i * 75, 100, 20, "Kills:", 0xFFC000, BlackColor32); + AddBorderedText(110 + 15, 105 + i * 75, 100, 20, teamInfo.Kills.ToString("N0"), 0xFFC000, BlackColor32); + + AddBorderedText(160 + 10, 85 + i * 75, 100, 20, "Captures:", 0xFFC000, BlackColor32); + AddBorderedText(160 + 15, 105 + i * 75, 100, 20, teamInfo.Captures.ToString("N0"), 0xFFC000, BlackColor32); + + var leader = teamInfo.Leader?.Name ?? "(none)"; + + AddBorderedText(235 + 10, 85 + i * 75, 250, 20, "Leader:", 0xFFC000, BlackColor32); + AddBorderedText(235 + 15, 105 + i * 75, 250, 20, leader, 0xFFC000, BlackColor32); + } + + AddButton(314, height - 42, 247, 248, 1); + } + + private void AddBorderedText(int x, int y, int width, int height, string text, int color, int borderColor) + { + AddColoredText(x - 1, y - 1, width, height, text, borderColor); + AddColoredText(x - 1, y + 1, width, height, text, borderColor); + AddColoredText(x + 1, y - 1, width, height, text, borderColor); + AddColoredText(x + 1, y + 1, width, height, text, borderColor); + AddColoredText(x, y, width, height, text, color); + } + + private void AddColoredText(int x, int y, int width, int height, string text, int color) + { + AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); + } + } + + public sealed class KHPlayerInfo : IRankedCTF, IComparable + { + private readonly KHTeamInfo m_TeamInfo; + private int m_Captures; + + private int m_Kills; + private int m_Score; + + public KHPlayerInfo(KHTeamInfo teamInfo, Mobile player) + { + m_TeamInfo = teamInfo; + Player = player; + } + + public Mobile Player { get; } + + public int CompareTo(KHPlayerInfo pi) + { + var res = pi.Score.CompareTo(Score); + if (res != 0) + { + return res; + } + + res = pi.Captures.CompareTo(Captures); + + return res != 0 ? res : pi.Kills.CompareTo(Kills); + } + + public string Name => Player.Name ?? ""; + + public int Kills + { + get => m_Kills; + set + { + m_TeamInfo.Kills += value - m_Kills; + m_Kills = value; + } + } + + public int Captures + { + get => m_Captures; + set + { + m_TeamInfo.Captures += value - m_Captures; + m_Captures = value; + } + } + + public int Score + { + get => m_Score; + set + { + m_TeamInfo.Score += value - m_Score; + m_Score = value; + + if (m_TeamInfo.Leader == null || m_Score > m_TeamInfo.Leader.Score) + { + m_TeamInfo.Leader = this; + } + } + } + } + + [PropertyObject] + public sealed class KHTeamInfo : IRankedCTF, IComparable + { + public KHTeamInfo(int teamID) + { + TeamID = teamID; + Players = new Dictionary(); + } + + public KHTeamInfo(int teamID, IGenericReader ip) + { + TeamID = teamID; + Players = new Dictionary(); + + var version = ip.ReadEncodedInt(); + + switch (version) + { + case 0: + { + TeamName = ip.ReadString(); + Color = ip.ReadEncodedInt(); + break; + } + } + } + + public KHGame Game { get; set; } + + public int TeamID { get; } + + public KHPlayerInfo Leader { get; set; } + + public Dictionary Players { get; } + + public KHPlayerInfo this[Mobile mob] + { + get + { if (mob == null) + { + return null; + } + + if (!Players.TryGetValue(mob, out var val)) + { + Players[mob] = val = new KHPlayerInfo(this, mob); + } + + return val; + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public int Color { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public string TeamName { get; set; } + + public int CompareTo(KHTeamInfo ti) + { + var res = ti.Score.CompareTo(Score); + if (res != 0) + { + return res; + } + + res = ti.Captures.CompareTo(Captures); + + if (res == 0) + { + res = ti.Kills.CompareTo(Kills); + } + + return res; + } + + public string Name => $"{TeamName ?? "(none)"} Team"; + + public int Kills { get; set; } + + public int Captures { get; set; } + + public int Score { get; set; } + + public void Reset() + { + Kills = 0; + Captures = 0; + Score = 0; + + Leader = null; + + Players.Clear(); + } + + public void Serialize(IGenericWriter op) + { + op.WriteEncodedInt(0); // version + + op.Write(TeamName); + op.WriteEncodedInt(Color); + } + + public override string ToString() => TeamName != null ? $"({Name}) ..." : "..."; + } + + public sealed class KHController : EventController + { + private int m_ScoreInterval; + + [Constructible] + public KHController() + { + Visible = false; + Movable = false; + + Name = "King of the Hill Controller"; + + Duration = TimeSpan.FromMinutes(30.0); + Boards = new List(); + Hills = new HillOfTheKing[4]; + TeamInfo = new KHTeamInfo[8]; + + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i] = new KHTeamInfo(i); + } + } + + public KHController(Serial serial) + : base(serial) + { + } + + public KHTeamInfo[] TeamInfo { get; private set; } + + [CommandProperty(AccessLevel.GameMaster)] + public KHTeamInfo Team1_W => TeamInfo[0]; + + [CommandProperty(AccessLevel.GameMaster)] + public KHTeamInfo Team2_E => TeamInfo[1]; + + [CommandProperty(AccessLevel.GameMaster)] + public KHTeamInfo Team3_N => TeamInfo[2]; + + [CommandProperty(AccessLevel.GameMaster)] + public KHTeamInfo Team4_S => TeamInfo[3]; + + [CommandProperty(AccessLevel.GameMaster)] + public KHTeamInfo Team5_NW => TeamInfo[4]; + + [CommandProperty(AccessLevel.GameMaster)] + public KHTeamInfo Team6_SE => TeamInfo[5]; + + [CommandProperty(AccessLevel.GameMaster)] + public KHTeamInfo Team7_SW => TeamInfo[6]; + + [CommandProperty(AccessLevel.GameMaster)] + public KHTeamInfo Team8_NE => TeamInfo[7]; + + public HillOfTheKing[] Hills { get; private set; } + + [CommandProperty(AccessLevel.GameMaster)] + public HillOfTheKing Hill1 + { + get => Hills[0]; + set => Hills[0] = value; + } + + [CommandProperty(AccessLevel.GameMaster)] + public HillOfTheKing Hill2 + { + get => Hills[1]; + set => Hills[1] = value; + } + + [CommandProperty(AccessLevel.GameMaster)] + public HillOfTheKing Hill3 + { + get => Hills[2]; + set => Hills[2] = value; + } + + [CommandProperty(AccessLevel.GameMaster)] + public HillOfTheKing Hill4 + { + get => Hills[3]; + set => Hills[3] = value; + } + + public List Boards { get; private set; } + + [CommandProperty(AccessLevel.GameMaster)] + public TimeSpan Duration { get; set; } + + public override string Title => "King of the Hill"; + + public override string GetTeamName(int teamID) => TeamInfo[teamID % TeamInfo.Length].Name; + + public override EventGame Construct(DuelContext context) => new KHGame(this, context); + + public void RemoveBoard(KHBoard b) + { + if (b != null) + { + Boards.Remove(b); + b.m_Game = null; + } + } + + public void AddBoard(KHBoard b) + { + if (b != null) + { + Boards.Add(b); + } + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + + writer.WriteEncodedInt(m_ScoreInterval); + writer.Write(Duration); + + Boards.Tidy(); + writer.Write(Boards); + + writer.WriteEncodedInt(Hills.Length); + for (var i = 0; i < Hills.Length; ++i) + { + writer.Write(Hills[i]); + } + + writer.WriteEncodedInt(TeamInfo.Length); + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i].Serialize(writer); + } + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: + { + m_ScoreInterval = reader.ReadEncodedInt(); + + Duration = reader.ReadTimeSpan(); + + Boards = reader.ReadEntityList(); + + Hills = new HillOfTheKing[reader.ReadEncodedInt()]; + for (var i = 0; i < Hills.Length; ++i) + { + Hills[i] = reader.ReadEntity(); + } + + TeamInfo = new KHTeamInfo[reader.ReadEncodedInt()]; + for (var i = 0; i < TeamInfo.Length; ++i) + { + TeamInfo[i] = new KHTeamInfo(i, reader); + } + + break; + } + } + } + } + + public sealed class KHGame : EventGame + { + private TimerExecutionToken _finishTimerToken; + + public KHGame(KHController controller, DuelContext context) : base(context) => Controller = controller; + + public KHController Controller { get; } + + public Map Facet + { + get + { + if (m_Context?.Arena != null) + { + return m_Context.Arena.Facet; + } + + return Controller.Map; + } + } + + public override bool CantDoAnything(Mobile mob) + { + if (mob != null && GetTeamInfo(mob) != null && Controller != null) + { + for (var i = 0; i < Controller.Hills.Length; i++) + { + if (Controller.Hills[i]?.King == mob) + { + return true; + } + } + } + + return false; + } + + public void Alert(string text) + { + m_Context.m_Tournament?.Alert(text); + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var p = m_Context.Participants[i]; + + for (var j = 0; j < p.Players.Length; ++j) + { + if (p.Players[j] != null) + { + p.Players[j].Mobile.SendMessage(0x35, text); + } + } + } + } + + public KHTeamInfo GetTeamInfo(Mobile mob) + { + var teamID = GetTeamID(mob); + + if (teamID >= 0) + { + return Controller.TeamInfo[teamID % Controller.TeamInfo.Length]; + } + + return null; + } + + public int GetTeamID(Mobile mob) + { + if (mob is not PlayerMobile pm) + { + return mob is BaseCreature creature ? creature.Team - 1 : -1; + } + + if (pm.DuelContext == null || pm.DuelContext != m_Context) + { + return -1; + } + + if (pm.DuelPlayer?.Eliminated != false) + { + return -1; + } + + return pm.DuelContext.Participants.IndexOf(pm.DuelPlayer.Participant); + } + + public int GetColor(Mobile mob) => GetTeamInfo(mob)?.Color ?? -1; + + private void ApplyHues(Participant p, int hueOverride) + { + for (var i = 0; i < p.Players.Length; ++i) + { + if (p.Players[i] != null) + { + p.Players[i].Mobile.SolidHueOverride = hueOverride; + } + } + } + + public void DelayBounce(TimeSpan ts, Mobile mob, Container corpse) + { + Timer.StartTimer(ts, () => DelayBounce_Callback(mob, corpse)); + } + + private void DelayBounce_Callback(Mobile mob, Container corpse) + { + var dp = (mob as PlayerMobile)?.DuelPlayer; + + m_Context.RemoveAggressions(mob); + + if (dp?.Eliminated == false) + { + mob.MoveToWorld(m_Context.Arena.GetBaseStartPoint(GetTeamID(mob)), Facet); + } + else + { + m_Context.SendOutside(mob); + } + + m_Context.Refresh(mob, corpse); + DuelContext.Debuff(mob); + DuelContext.CancelSpell(mob); + mob.Frozen = false; + + if (corpse?.Deleted == false) + { + Timer.StartTimer(TimeSpan.FromSeconds(30), corpse.Delete); + } + } + + public override bool OnDeath(Mobile mob, Container corpse) + { + var killer = mob.FindMostRecentDamager(false); + KHTeamInfo teamInfo = null; + var victInfo = GetTeamInfo(mob); + var bonus = 0; + + if (killer?.Player == true) + { + teamInfo = GetTeamInfo(killer); + } + + for (var i = 0; i < Controller.Hills.Length; i++) + { + if (Controller.Hills[i] == null) { continue; } - sb.Reset(); - - sb.Append(title); - - if (pl == leader) + if (Controller.Hills[i].King == mob) { - sb.Append(" Leader"); + bonus += Controller.Hills[i].CapturesSoFar; + Controller.Hills[i].OnKingDied(mob, victInfo, killer, teamInfo); } - if (pl.Score > 0) + if (Controller.Hills[i].King == killer) { - sb.Append(": "); + bonus += 2; + } + } - sb.Append(pl.Score.ToString("N0")); - sb.Append(pl.Score == 1 ? " point" : " points"); + if (teamInfo != null && teamInfo != victInfo) + { + var playerInfo = teamInfo[killer]; - sb.Append(", "); - sb.Append(pl.Kills.ToString("N0")); - sb.Append(pl.Kills == 1 ? " kill" : " kills"); + if (playerInfo != null) + { + playerInfo.Kills += 1; + playerInfo.Score += 1 + bonus; + } + } - if (pl.Captures > 0) + mob.SendGump(new KHBoardGump(mob, this)); + + m_Context.Requip(mob, corpse); + DelayBounce(TimeSpan.FromSeconds(30.0), mob, corpse); + + return false; + } + + public override void OnStart() + { + for (var i = 0; i < Controller.TeamInfo.Length; ++i) + { + var teamInfo = Controller.TeamInfo[i]; + + teamInfo.Game = this; + teamInfo.Reset(); + } + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + ApplyHues( + m_Context.Participants[i], + Controller.TeamInfo[i % Controller.TeamInfo.Length].Color + ); + } + + for (var i = 0; i < Controller.Hills.Length; i++) + { + if (Controller.Hills[i] != null) + { + Controller.Hills[i].Game = this; + } + } + + foreach (var board in Controller.Boards) + { + if (board?.Deleted == false) + { + board.m_Game = this; + } + } + + _finishTimerToken.Cancel(); + Timer.StartTimer(Controller.Duration, Finish_Callback, out _finishTimerToken); + } + + private void Finish_Callback() + { + var teams = new List(); + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var teamInfo = Controller.TeamInfo[i % Controller.TeamInfo.Length]; + + if (teamInfo != null) + { + teams.Add(teamInfo); + } + } + + teams.Sort(); + + var tourney = m_Context.m_Tournament; + + var sb = new StringBuilder(); + + if (tourney != null) + { + if (tourney.TourneyType == TourneyType.FreeForAll) + { + sb.Append(m_Context.Participants.Count * tourney.PlayersPerParticipant); + sb.Append("-man FFA"); + } + else if (tourney.TourneyType == TourneyType.RandomTeam) + { + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-team"); + } + else if (tourney.TourneyType == TourneyType.RedVsBlue) + { + sb.Append("Red v Blue"); + } + else + { + for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) { - sb.Append(", "); - sb.Append(pl.Captures.ToString("N0")); - sb.Append(pl.Captures == 1 ? " capture" : " captures"); + if (sb.Length > 0) + { + sb.Append('v'); + } + + sb.Append(tourney.PlayersPerParticipant); } } + } - Item item = new Trophy(sb.ToString(), rank); + if (Controller != null) + { + sb.Append(' ').Append(Controller.Title); + } - if (pl == leader) + var title = sb.ToString(); + + var winner = teams.Count > 0 ? teams[0] : null; + + for (var i = 0; i < teams.Count; ++i) + { + var rank = TrophyRank.Bronze; + + if (i == 0) { - item.ItemID = 4810; + rank = TrophyRank.Gold; + } + else if (i == 1) + { + rank = TrophyRank.Silver; } - item.Name = $"{item.Name}, {teams[i].Name.ToLower()}"; + var leader = teams[i].Leader; - if (!mob.PlaceInBackpack(item)) + foreach (var pl in teams[i].Players.Values) { - mob.BankBox.DropItem(item); - } + var mob = pl.Player; - var cash = pl.Score * 250; + if (mob == null) + { + continue; + } - if (cash > 0) - { - item = new BankCheck(cash); + sb = new StringBuilder(); + + sb.Append(title); + + if (pl == leader) + { + sb.Append(" Leader"); + } + + if (pl.Score > 0) + { + sb.Append(": "); + + sb.Append(pl.Score.ToString("N0")); + sb.Append(pl.Score == 1 ? " point" : " points"); + + sb.Append(", "); + sb.Append(pl.Kills.ToString("N0")); + sb.Append(pl.Kills == 1 ? " kill" : " kills"); + + if (pl.Captures > 0) + { + sb.Append(", "); + sb.Append(pl.Captures.ToString("N0")); + sb.Append(pl.Captures == 1 ? " capture" : " captures"); + } + } + + Item item = new Trophy(sb.ToString(), rank); + + if (pl == leader) + { + item.ItemID = 4810; + } + + item.Name = $"{item.Name}, {teams[i].Name.ToLower()}"; if (!mob.PlaceInBackpack(item)) { mob.BankBox.DropItem(item); } - mob.SendMessage( - $"You have been awarded a {rank.LowerName()} trophy and {cash:N0}gp for your participation in this game." - ); + var cash = pl.Score * 250; + + if (cash > 0) + { + item = new BankCheck(cash); + + if (!mob.PlaceInBackpack(item)) + { + mob.BankBox.DropItem(item); + } + + mob.SendMessage( + $"You have been awarded a {rank.ToString().ToLower()} trophy and {cash:N0}gp for your participation in this game." + ); + } + else + { + mob.SendMessage( + $"You have been awarded a {rank.ToString().ToLower()} trophy for your participation in this game." + ); + } } - else + } + + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + var p = m_Context.Participants[i]; + if (p.Players == null) { - mob.SendMessage( - $"You have been awarded a {rank.LowerName()} trophy for your participation in this game." - ); + continue; } - } - } - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - var p = m_Context.Participants[i]; - if (p.Players == null) - { - continue; - } - - for (var j = 0; j < p.Players.Length; ++j) - { - var dp = p.Players[j]; - - if (dp?.Mobile != null) - { - KHBoardGump.DisplayTo(dp.Mobile, this); - } - } - - if (i == winner?.TeamID) - { - continue; - } - - if (p.Players != null) - { for (var j = 0; j < p.Players.Length; ++j) { - p.Players[j]?.Eliminated = true; + var dp = p.Players[j]; + + if (dp?.Mobile != null) + { + dp.Mobile.SendGump(new KHBoardGump(dp.Mobile, this)); + } } + + if (i == winner?.TeamID) + { + continue; + } + + if (p.Players != null) + { + for (var j = 0; j < p.Players.Length; ++j) + { + if (p.Players[j] != null) + { + p.Players[j].Eliminated = true; + } + } + } + } + + if (winner != null) + { + m_Context.Finish(m_Context.Participants[winner.TeamID]); } } - if (winner != null) + public override void OnStop() { - m_Context.Finish(m_Context.Participants[winner.TeamID]); - } - } + for (var i = 0; i < Controller.TeamInfo.Length; ++i) + { + Controller.TeamInfo[i].Game = null; + } - public override void OnStop() - { - for (var i = 0; i < Controller.TeamInfo.Length; ++i) - { - Controller.TeamInfo[i].Game = null; - } + for (var i = 0; i < Controller.Hills.Length; ++i) + { + if (Controller.Hills[i] != null) + { + Controller.Hills[i].Game = null; + } + } - for (var i = 0; i < Controller.Hills.Length; ++i) - { - Controller.Hills[i]?.Game = null; - } + foreach (var board in Controller.Boards) + { + if (board != null) + { + board.m_Game = null; + } + } - foreach (var board in Controller.Boards) - { - board?.m_Game = null; - } + for (var i = 0; i < m_Context.Participants.Count; ++i) + { + ApplyHues(m_Context.Participants[i], -1); + } - for (var i = 0; i < m_Context.Participants.Count; ++i) - { - ApplyHues(m_Context.Participants[i], -1); + _finishTimerToken.Cancel(); } - - _finishTimerToken.Cancel(); } } diff --git a/Projects/UOContent/Engines/ConPVP/Games/TourneyMatch.cs b/Projects/UOContent/Engines/ConPVP/Games/TourneyMatch.cs index 76e1afa92..2925e28a3 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/TourneyMatch.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/TourneyMatch.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using System.Text; using Server.Network; -using Server.Text; namespace Server.Engines.ConPVP { @@ -11,18 +11,21 @@ namespace Server.Engines.ConPVP { Participants = participants; - using var sb = new ValueStringBuilder(stackalloc char[256]); - for (var i = 0; i < participants.Count; ++i) { var part = participants[i]; - sb.Reset(); + var sb = new StringBuilder(); + sb.Append("Matched in a duel against "); if (participants.Count > 2) { - sb.Append($"{participants.Count - 1} other {(part.Players.Count == 1 ? "players" : "teams")}: "); + sb.AppendFormat( + "{0} other {1}: ", + participants.Count - 1, + part.Players.Count == 1 ? "players" : "teams" + ); } var hasAppended = false; diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs index 4dd60c9ab..d7a93a578 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs @@ -1,439 +1,428 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Text; using Server.Gumps; using Server.Mobiles; using Server.Network; -using Server.Text; -namespace Server.Engines.ConPVP; - -public class AcceptTeamGump : DynamicGump +namespace Server.Engines.ConPVP { - private const int BlackColor32 = 0x000008; - private const int LabelColor32 = 0xFFFFFF; - - private readonly Mobile _from; - private readonly List _players; - private readonly Mobile _registrar; - private readonly Mobile _requested; - private readonly Tournament _tournament; - private bool _active; - - public override bool Singleton => true; - - private AcceptTeamGump( - Mobile from, Mobile requested, Tournament tourney, Mobile registrar, List players - ) : base(50, 50) + public class AcceptTeamGump : Gump { - _from = from; - _requested = requested; - _tournament = tourney; - _registrar = registrar; - _players = players; + private const int BlackColor32 = 0x000008; + private const int LabelColor32 = 0xFFFFFF; - _active = true; + private readonly Mobile m_From; + private readonly List m_Players; + private readonly Mobile m_Registrar; + private readonly Mobile m_Requested; + private readonly Tournament m_Tournament; + private bool m_Active; - Timer.StartTimer(TimeSpan.FromSeconds(15.0), AutoReject); - } - - public static void DisplayTo(Mobile from, Mobile requested, Tournament tourney, Mobile registrar, List players) - { - if (requested?.NetState != null && from != null && tourney != null && players != null) + public AcceptTeamGump( + Mobile from, Mobile requested, Tournament tourney, Mobile registrar, List players + ) : base(50, 50) { - requested.SendGump(new AcceptTeamGump(from, requested, tourney, registrar, players)); - } - } + m_From = from; + m_Requested = requested; + m_Tournament = tourney; + m_Registrar = registrar; + m_Players = players; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); + m_Active = true; - var ruleset = _tournament.Ruleset; - var basedef = ruleset.Base; + var ruleset = tourney.Ruleset; + var basedef = ruleset.Base; - var height = 185 + 35 + 60 + 12; + var height = 185 + 35 + 60 + 12; - var changes = 0; + var changes = 0; - BitArray defs; + BitArray defs; - if (ruleset.Flavors.Count > 0) - { - defs = new BitArray(basedef.Options); - - for (var i = 0; i < ruleset.Flavors.Count; ++i) + if (ruleset.Flavors.Count > 0) { - defs.Or(ruleset.Flavors[i].Options); - } + defs = new BitArray(basedef.Options); - height += ruleset.Flavors.Count * 18; - } - else - { - defs = basedef.Options; - } - - var opts = ruleset.Options; - - for (var i = 0; i < opts.Length; ++i) - { - if (defs[i] != opts[i]) - { - ++changes; - } - } - - height += changes * 22; - - height += 10 + 22 + 25 + 25; - - builder.AddPage(); - - builder.AddBackground(1, 1, 398, height, 3600); - - builder.AddImageTiled(16, 15, 369, height - 29, 3604); - builder.AddAlphaRegion(16, 15, 369, height - 29); - - builder.AddImage(215, -43, 0xEE40); - - using var sb = new ValueStringBuilder(stackalloc char[64]); - - if (_tournament.TourneyType == TourneyType.FreeForAll) - { - sb.Append("FFA"); - } - else if (_tournament.TourneyType == TourneyType.RandomTeam) - { - sb.Append($"{_tournament.ParticipantsPerMatch}-Team"); - } - else if (_tournament.TourneyType == TourneyType.Faction) - { - sb.Append($"{_tournament.ParticipantsPerMatch}-Team Faction"); - } - else if (_tournament.TourneyType == TourneyType.RedVsBlue) - { - sb.Append("Red v Blue"); - } - else - { - for (var i = 0; i < _tournament.ParticipantsPerMatch; ++i) - { - if (sb.Length > 0) + for (var i = 0; i < ruleset.Flavors.Count; ++i) { - sb.Append('v'); + defs.Or(ruleset.Flavors[i].Options); } - sb.Append(_tournament.PlayersPerParticipant); + height += ruleset.Flavors.Count * 18; + } + else + { + defs = basedef.Options; } - } - if (_tournament.EventController != null) - { - sb.Append($" {_tournament.EventController.Title}"); - } - - sb.Append(" Tournament Invitation"); - - AddBorderedText(ref builder, 22, 22, 294, 20, sb.AsSpan().Center(), LabelColor32, BlackColor32); - - AddBorderedText( - ref builder, - 22, - 50, - 294, - 40, - $"You have been asked to partner with {_from.Name} in a tournament. Do you accept?", - 0xB0C868, - BlackColor32 - ); - - builder.AddImageTiled(32, 88, 264, 1, 9107); - builder.AddImageTiled(42, 90, 264, 1, 9157); - - var y = 100; - - var groupText = _tournament.GroupType switch - { - GroupingType.HighVsLow => "High vs Low", - GroupingType.Nearest => "Closest opponent", - GroupingType.Random => "Random", - _ => null - }; - - AddBorderedText(ref builder, 35, y, 190, 20, $"Grouping: {groupText}", LabelColor32, BlackColor32); - y += 20; - - var tieText = _tournament.TieType switch - { - TieType.Random => "Random", - TieType.Highest => "Highest advances", - TieType.Lowest => "Lowest advances", - TieType.FullAdvancement => _tournament.ParticipantsPerMatch == 2 ? "Both advance" : "Everyone advances", - TieType.FullElimination => _tournament.ParticipantsPerMatch == 2 ? "Both eliminated" : "Everyone eliminated", - _ => null - }; - - AddBorderedText(ref builder, 35, y, 190, 20, $"Tiebreaker: {tieText}", LabelColor32, BlackColor32); - y += 20; - - string sdText; - - if (_tournament.SuddenDeath > TimeSpan.Zero) - { - sdText = _tournament.SuddenDeathRounds > 0 ? - $"Sudden Death: {(int)_tournament.SuddenDeath.TotalMinutes}:{_tournament.SuddenDeath.Seconds:D2} (first {_tournament.SuddenDeathRounds} rounds)" : - $"Sudden Death: {(int)_tournament.SuddenDeath.TotalMinutes}:{_tournament.SuddenDeath.Seconds:D2} (all rounds)"; - } - else - { - sdText = "Sudden Death: Off"; - } - - AddBorderedText(ref builder, 35, y, 240, 20, sdText, LabelColor32, BlackColor32); - y += 20; - - y += 6; - builder.AddImageTiled(32, y - 1, 264, 1, 9107); - builder.AddImageTiled(42, y + 1, 264, 1, 9157); - y += 6; - - AddBorderedText(ref builder, 35, y, 190, 20, $"Ruleset: {basedef.Title}", LabelColor32, BlackColor32); - y += 20; - - for (var i = 0; i < ruleset.Flavors.Count; ++i, y += 18) - { - AddBorderedText(ref builder, 35, y, 190, 20, $" + {ruleset.Flavors[i].Title}", LabelColor32, BlackColor32); - } - - y += 4; - - if (changes > 0) - { - AddBorderedText(ref builder, 35, y, 190, 20, "Modifications:", LabelColor32, BlackColor32); - y += 20; + var opts = ruleset.Options; for (var i = 0; i < opts.Length; ++i) { if (defs[i] != opts[i]) { - var name = ruleset.Layout.FindByIndex(i); - - if (name != null) // sanity - { - builder.AddImage(35, y, opts[i] ? 0xD3 : 0xD2); - AddBorderedText(ref builder, 60, y, 165, 22, name, LabelColor32, BlackColor32); - } - - y += 22; + ++changes; } } - } - else - { - AddBorderedText(ref builder, 35, y, 190, 20, "Modifications: None", LabelColor32, BlackColor32); + + height += changes * 22; + + height += 10 + 22 + 25 + 25; + + Closable = false; + + AddPage(0); + + AddBackground(1, 1, 398, height, 3600); + + AddImageTiled(16, 15, 369, height - 29, 3604); + AddAlphaRegion(16, 15, 369, height - 29); + + AddImage(215, -43, 0xEE40); + + var sb = new StringBuilder(); + + if (tourney.TourneyType == TourneyType.FreeForAll) + { + sb.Append("FFA"); + } + else if (tourney.TourneyType == TourneyType.RandomTeam) + { + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-Team"); + } + else if (tourney.TourneyType == TourneyType.Faction) + { + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-Team Faction"); + } + else if (tourney.TourneyType == TourneyType.RedVsBlue) + { + sb.Append("Red v Blue"); + } + else + { + for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) + { + if (sb.Length > 0) + { + sb.Append('v'); + } + + sb.Append(tourney.PlayersPerParticipant); + } + } + + if (tourney.EventController != null) + { + sb.Append(' ').Append(tourney.EventController.Title); + } + + sb.Append(" Tournament Invitation"); + + AddBorderedText(22, 22, 294, 20, sb.ToString().Center(), LabelColor32, BlackColor32); + + AddBorderedText( + 22, + 50, + 294, + 40, + $"You have been asked to partner with {from.Name} in a tournament. Do you accept?", + 0xB0C868, + BlackColor32 + ); + + AddImageTiled(32, 88, 264, 1, 9107); + AddImageTiled(42, 90, 264, 1, 9157); + + var y = 100; + + var groupText = tourney.GroupType switch + { + GroupingType.HighVsLow => "High vs Low", + GroupingType.Nearest => "Closest opponent", + GroupingType.Random => "Random", + _ => null + }; + + AddBorderedText(35, y, 190, 20, $"Grouping: {groupText}", LabelColor32, BlackColor32); y += 20; + + var tieText = tourney.TieType switch + { + TieType.Random => "Random", + TieType.Highest => "Highest advances", + TieType.Lowest => "Lowest advances", + TieType.FullAdvancement => tourney.ParticipantsPerMatch == 2 ? "Both advance" : "Everyone advances", + TieType.FullElimination => tourney.ParticipantsPerMatch == 2 ? "Both eliminated" : "Everyone eliminated", + _ => null + }; + + AddBorderedText(35, y, 190, 20, $"Tiebreaker: {tieText}", LabelColor32, BlackColor32); + y += 20; + + string sdText; + + if (tourney.SuddenDeath > TimeSpan.Zero) + { + sdText = tourney.SuddenDeathRounds > 0 ? + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (first {tourney.SuddenDeathRounds} rounds)" : + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (all rounds)"; + } + else + { + sdText = "Sudden Death: Off"; + } + + AddBorderedText(35, y, 240, 20, sdText, LabelColor32, BlackColor32); + y += 20; + + y += 6; + AddImageTiled(32, y - 1, 264, 1, 9107); + AddImageTiled(42, y + 1, 264, 1, 9157); + y += 6; + + AddBorderedText(35, y, 190, 20, $"Ruleset: {basedef.Title}", LabelColor32, BlackColor32); + y += 20; + + for (var i = 0; i < ruleset.Flavors.Count; ++i, y += 18) + { + AddBorderedText(35, y, 190, 20, $" + {ruleset.Flavors[i].Title}", LabelColor32, BlackColor32); + } + + y += 4; + + if (changes > 0) + { + AddBorderedText(35, y, 190, 20, "Modifications:", LabelColor32, BlackColor32); + y += 20; + + for (var i = 0; i < opts.Length; ++i) + { + if (defs[i] != opts[i]) + { + var name = ruleset.Layout.FindByIndex(i); + + if (name != null) // sanity + { + AddImage(35, y, opts[i] ? 0xD3 : 0xD2); + AddBorderedText(60, y, 165, 22, name, LabelColor32, BlackColor32); + } + + y += 22; + } + } + } + else + { + AddBorderedText(35, y, 190, 20, "Modifications: None", LabelColor32, BlackColor32); + y += 20; + } + + y += 8; + AddImageTiled(32, y - 1, 264, 1, 9107); + AddImageTiled(42, y + 1, 264, 1, 9157); + y += 8; + + AddRadio(24, y, 9727, 9730, true, 1); + AddBorderedText(60, y + 5, 250, 20, "Yes, I will join them.", LabelColor32, BlackColor32); + y += 35; + + AddRadio(24, y, 9727, 9730, false, 2); + AddBorderedText(60, y + 5, 250, 20, "No, I do not wish to fight.", LabelColor32, BlackColor32); + y += 35; + + AddRadio(24, y, 9727, 9730, false, 3); + AddBorderedText(60, y + 5, 270, 20, "No, most certainly not. Do not ask again.", LabelColor32, BlackColor32); + y += 35; + + y -= 3; + AddButton(314, y, 247, 248, 1); + + Timer.StartTimer(TimeSpan.FromSeconds(15.0), AutoReject); } - y += 8; - builder.AddImageTiled(32, y - 1, 264, 1, 9107); - builder.AddImageTiled(42, y + 1, 264, 1, 9157); - y += 8; - - builder.AddRadio(24, y, 9727, 9730, true, 1); - AddBorderedText(ref builder, 60, y + 5, 250, 20, "Yes, I will join them.", LabelColor32, BlackColor32); - y += 35; - - builder.AddRadio(24, y, 9727, 9730, false, 2); - AddBorderedText(ref builder, 60, y + 5, 250, 20, "No, I do not wish to fight.", LabelColor32, BlackColor32); - y += 35; - - builder.AddRadio(24, y, 9727, 9730, false, 3); - AddBorderedText(ref builder, 60, y + 5, 270, 20, "No, most certainly not. Do not ask again.", LabelColor32, BlackColor32); - y += 35; - - y -= 3; - builder.AddButton(314, y, 247, 248, 1); - } - - private static void AddBorderedText(ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color, int borderColor) - { - AddColoredText(ref builder, x - 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x - 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x, y, width, height, text, color); - } - - private static void AddColoredText(ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color) - { - builder.AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); - } - - public void AutoReject() - { - if (!_active) + private void AddBorderedText(int x, int y, int width, int height, string text, int color, int borderColor) { - return; + AddColoredText(x - 1, y - 1, width, height, text, borderColor); + AddColoredText(x - 1, y + 1, width, height, text, borderColor); + AddColoredText(x + 1, y - 1, width, height, text, borderColor); + AddColoredText(x + 1, y + 1, width, height, text, borderColor); + AddColoredText(x, y, width, height, text, color); } - _active = false; - - _requested.CloseGump(); - ConfirmSignupGump.DisplayTo(_from, _registrar, _tournament, _players); - - if (_registrar != null) + private void AddColoredText(int x, int y, int width, int height, string text, int color) { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - $"{_requested.Name} seems unresponsive.", - _from.NetState - ); - - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - $"You have declined the partnership with {_from.Name}.", - _requested.NetState - ); - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = _from; - var mob = _requested; - - if (info.ButtonID != 1 || !_active) - { - return; + AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); } - _active = false; - - if (info.IsSwitched(1)) + public void AutoReject() { - if (mob is not PlayerMobile pm) + if (!m_Active) { return; } - if (AcceptDuelGump.IsIgnored(mob, from) || mob.Blessed) - { - ConfirmSignupGump.DisplayTo(_from, _registrar, _tournament, _players); + m_Active = false; - _registrar?.PrivateOverheadMessage( + m_Requested.CloseGump(); + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + if (m_Registrar != null) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, 0x22, false, - "They ignore your invitation.", - from.NetState + $"{m_Requested.Name} seems unresponsive.", + m_From.NetState + ); + + m_Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + $"You have declined the partnership with {m_From.Name}.", + m_Requested.NetState ); } - else if (pm.DuelContext != null) - { - ConfirmSignupGump.DisplayTo(_from, _registrar, _tournament, _players); + } - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "They are already assigned to another duel.", - from.NetState - ); + public override void OnResponse(NetState sender, in RelayInfo info) + { + var from = m_From; + var mob = m_Requested; + + if (info.ButtonID != 1 || !m_Active) + { + return; } - else if (_players.Contains(mob)) - { - ConfirmSignupGump.DisplayTo(_from, _registrar, _tournament, _players); - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "You have already named them as a team member.", - from.NetState - ); - } - else if (_tournament.HasParticipant(mob)) - { - ConfirmSignupGump.DisplayTo(_from, _registrar, _tournament, _players); + m_Active = false; - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "They have already entered this tournament.", - from.NetState - ); - } - else if (_players.Count >= _tournament.PlayersPerParticipant) + if (info.IsSwitched(1)) { - ConfirmSignupGump.DisplayTo(_from, _registrar, _tournament, _players); + if (mob is not PlayerMobile pm) + { + return; + } - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "Your team is full.", - from.NetState - ); + if (AcceptDuelGump.IsIgnored(mob, from) || mob.Blessed) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "They ignore your invitation.", + from.NetState + ); + } + else if (pm.DuelContext != null) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "They are already assigned to another duel.", + from.NetState + ); + } + else if (m_Players.Contains(mob)) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "You have already named them as a team member.", + from.NetState + ); + } + else if (m_Tournament.HasParticipant(mob)) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "They have already entered this tournament.", + from.NetState + ); + } + else if (m_Players.Count >= m_Tournament.PlayersPerParticipant) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "Your team is full.", + from.NetState + ); + } + else + { + m_Players.Add(mob); + + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + if (m_Registrar != null) + { + m_Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x59, + false, + $"{mob.Name} has accepted your offer of partnership.", + from.NetState + ); + + m_Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x59, + false, + $"You have accepted the partnership with {from.Name}.", + mob.NetState + ); + } + } } else { - _players.Add(mob); - - ConfirmSignupGump.DisplayTo(_from, _registrar, _tournament, _players); - - if (_registrar != null) + if (info.IsSwitched(3)) { - _registrar.PrivateOverheadMessage( + AcceptDuelGump.BeginIgnore(m_Requested, m_From); + } + + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + if (m_Registrar != null) + { + m_Registrar.PrivateOverheadMessage( MessageType.Regular, - 0x59, + 0x22, false, - $"{mob.Name} has accepted your offer of partnership.", + $"{mob.Name} has declined your offer of partnership.", from.NetState ); - _registrar.PrivateOverheadMessage( + m_Registrar.PrivateOverheadMessage( MessageType.Regular, - 0x59, + 0x22, false, - $"You have accepted the partnership with {from.Name}.", + $"You have declined the partnership with {from.Name}.", mob.NetState ); } } } - else - { - if (info.IsSwitched(3)) - { - AcceptDuelGump.BeginIgnore(_requested, _from); - } - - ConfirmSignupGump.DisplayTo(_from, _registrar, _tournament, _players); - - if (_registrar != null) - { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - $"{mob.Name} has declined your offer of partnership.", - from.NetState - ); - - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - $"You have declined the partnership with {from.Name}.", - mob.NetState - ); - } - } } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/ArenaGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/ArenaGump.cs index 0bda5d1fe..67f8a0047 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/ArenaGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/ArenaGump.cs @@ -1,284 +1,296 @@ using System.Collections.Generic; -using ModernUO.Serialization; +using System.Text; using Server.Gumps; using Server.Mobiles; using Server.Network; -using Server.Text; -namespace Server.Engines.ConPVP; - -[SerializationGenerator(0, false)] -public partial class ArenasMoongate : Item +namespace Server.Engines.ConPVP { - [Constructible] - public ArenasMoongate() : base(0x1FD4) + public class ArenasMoongate : Item { - Movable = false; - Light = LightType.Circle300; - } - - public override string DefaultName => "arena moongate"; - - public bool UseGate(Mobile from) - { - if (DuelContext.CheckCombat(from)) + [Constructible] + public ArenasMoongate() : base(0x1FD4) { - from.SendMessage(0x22, "You have recently been in combat with another player and cannot use this moongate."); - return false; + Movable = false; + Light = LightType.Circle300; } - if (from.Spell != null) + public ArenasMoongate(Serial serial) : base(serial) { - from.SendLocalizedMessage(1049616); // You are too busy to do that at the moment. - return false; } - ArenaGump.DisplayTo(from, this); + public override string DefaultName => "arena moongate"; - if (!from.Hidden || from.AccessLevel == AccessLevel.Player) + public override void Serialize(IGenericWriter writer) { - Effects.PlaySound(from.Location, from.Map, 0x20E); + base.Serialize(writer); + + writer.Write(0); } - return true; - } - - public override void OnDoubleClick(Mobile from) - { - if (from.InRange(GetWorldLocation(), 1)) + public override void Deserialize(IGenericReader reader) { - UseGate(from); - } - else - { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that - } - } + base.Deserialize(reader); - public override bool OnMoveOver(Mobile m) => !m.Player || UseGate(m); -} - -public class ArenaGump : DynamicGump -{ - private readonly List _arenas; - private readonly ArenasMoongate _gate; - - private int _columnX; - - public override bool Singleton => true; - - private ArenaGump(ArenasMoongate gate) : base(50, 50) - { - _gate = gate; - _arenas = Arena.Arenas; - } - - public static void DisplayTo(Mobile from, ArenasMoongate gate) - { - if (from?.NetState != null && gate != null) - { - from.SendGump(new ArenaGump(gate)); - } - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - _columnX = 12; - - builder.AddPage(); - - var height = 12 + 20 + _arenas.Count * 31 + 24 + 12; - - builder.AddBackground(0, 0, 499 + 40, height, 0x2436); - - var list = _arenas; - - for (var i = 1; i < list.Count; i += 2) - { - builder.AddImageTiled(12, 32 + i * 31, 475 + 40, 30, 0x2430); + var version = reader.ReadInt(); + Light = LightType.Circle300; } - builder.AddAlphaRegion(10, 10, 479 + 40, height - 20); - - AddColumnHeader(ref builder, 35, null); - AddColumnHeader(ref builder, 115, "Arena"); - AddColumnHeader(ref builder, 325, "Participants"); - AddColumnHeader(ref builder, 40, "Obs"); - - builder.AddButton(499 + 40 - 12 - 63 - 4 - 63, height - 12 - 24, 247, 248, 1); - builder.AddButton(499 + 40 - 12 - 63, height - 12 - 24, 241, 242, 2); - - var sb = new ValueStringBuilder(stackalloc char[256]); - - for (var i = 0; i < list.Count; ++i) + public bool UseGate(Mobile from) { - var ar = list[i]; - - var x = 12; - var y = 32 + i * 31; - - var color = ar.Players.Count > 0 ? 0xCCFFCC : 0xCCCCCC; - - builder.AddRadio(x + 3, y + 1, 9727, 9730, false, i); - x += 35; - - AddBorderedText(ref builder, x + 5, y + 5, 115 - 5, ar.Name ?? "(no name)", color); - x += 115; - - sb.Reset(); - - if (ar.Players.Count > 0) + if (DuelContext.CheckCombat(from)) { - var ladder = Ladder.Instance; + from.SendMessage(0x22, "You have recently been in combat with another player and cannot use this moongate."); + return false; + } - if (ladder == null) - { - continue; - } + if (from.Spell != null) + { + from.SendLocalizedMessage(1049616); // You are too busy to do that at the moment. + return false; + } - LadderEntry p1 = null, p2 = null, p3 = null, p4 = null; + from.SendGump(new ArenaGump(from, this)); - for (var j = 0; j < ar.Players.Count; ++j) - { - var mob = ar.Players[j]; - var c = ladder.Find(mob); + if (!from.Hidden || from.AccessLevel == AccessLevel.Player) + { + Effects.PlaySound(from.Location, from.Map, 0x20E); + } - if (p1 == null || c.Index < p1.Index) - { - p4 = p3; - p3 = p2; - p2 = p1; - p1 = c; - } - else if (p2 == null || c.Index < p2.Index) - { - p4 = p3; - p3 = p2; - p2 = c; - } - else if (p3 == null || c.Index < p3.Index) - { - p4 = p3; - p3 = c; - } - else if (p4 == null || c.Index < p4.Index) - { - p4 = c; - } - } + return true; + } - Append(ref sb, p1); - Append(ref sb, p2); - Append(ref sb, p3); - Append(ref sb, p4); - - if (ar.Players.Count > 4) - { - sb.Append(", ..."); - } + public override void OnDoubleClick(Mobile from) + { + if (from.InRange(GetWorldLocation(), 1)) + { + UseGate(from); } else { - sb.Append("Empty"); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that + } + } + + public override bool OnMoveOver(Mobile m) => !m.Player || UseGate(m); + } + + public class ArenaGump : Gump + { + private readonly List m_Arenas; + private readonly Mobile m_From; + private readonly ArenasMoongate m_Gate; + + private int m_ColumnX = 12; + + public override bool Singleton => true; + + public ArenaGump(Mobile from, ArenasMoongate gate) : base(50, 50) + { + m_From = from; + m_Gate = gate; + m_Arenas = Arena.Arenas; + + AddPage(0); + + var height = 12 + 20 + m_Arenas.Count * 31 + 24 + 12; + + AddBackground(0, 0, 499 + 40, height, 0x2436); + + var list = m_Arenas; + + for (var i = 1; i < list.Count; i += 2) + { + AddImageTiled(12, 32 + i * 31, 475 + 40, 30, 0x2430); } - AddBorderedText(ref builder, x + 5, y + 5, 325 - 5, sb.ToString(), color); - x += 325; + AddAlphaRegion(10, 10, 479 + 40, height - 20); - AddBorderedText(ref builder, x, y + 5, 40, Html.Center($"{ar.Spectators}"), color); + AddColumnHeader(35, null); + AddColumnHeader(115, "Arena"); + AddColumnHeader(325, "Participants"); + AddColumnHeader(40, "Obs"); + + AddButton(499 + 40 - 12 - 63 - 4 - 63, height - 12 - 24, 247, 248, 1); + AddButton(499 + 40 - 12 - 63, height - 12 - 24, 241, 242, 2); + + for (var i = 0; i < list.Count; ++i) + { + var ar = list[i]; + + var x = 12; + var y = 32 + i * 31; + + var color = ar.Players.Count > 0 ? 0xCCFFCC : 0xCCCCCC; + + AddRadio(x + 3, y + 1, 9727, 9730, false, i); + x += 35; + + AddBorderedText(x + 5, y + 5, 115 - 5, ar.Name ?? "(no name)", color, 0); + x += 115; + + var sb = new StringBuilder(); + + if (ar.Players.Count > 0) + { + var ladder = Ladder.Instance; + + if (ladder == null) + { + continue; + } + + LadderEntry p1 = null, p2 = null, p3 = null, p4 = null; + + for (var j = 0; j < ar.Players.Count; ++j) + { + var mob = ar.Players[j]; + var c = ladder.Find(mob); + + if (p1 == null || c.Index < p1.Index) + { + p4 = p3; + p3 = p2; + p2 = p1; + p1 = c; + } + else if (p2 == null || c.Index < p2.Index) + { + p4 = p3; + p3 = p2; + p2 = c; + } + else if (p3 == null || c.Index < p3.Index) + { + p4 = p3; + p3 = c; + } + else if (p4 == null || c.Index < p4.Index) + { + p4 = c; + } + } + + Append(sb, p1); + Append(sb, p2); + Append(sb, p3); + Append(sb, p4); + + if (ar.Players.Count > 4) + { + sb.Append(", ..."); + } + } + else + { + sb.Append("Empty"); + } + + AddBorderedText(x + 5, y + 5, 325 - 5, sb.ToString(), color, 0); + x += 325; + + AddBorderedText(x, y + 5, 40, ar.Spectators.ToString().Center(), color, 0); + } } - sb.Dispose(); - } - - private static void Append(ref ValueStringBuilder sb, LadderEntry le) - { - if (le == null) + private void Append(StringBuilder sb, LadderEntry le) { - return; + if (le == null) + { + return; + } + + if (sb.Length > 0) + { + sb.Append(", "); + } + + sb.Append(le.Mobile.Name); } - if (sb.Length > 0) + public override void OnResponse(NetState sender, in RelayInfo info) { - sb.Append(", "); + if (info.ButtonID != 1) + { + return; + } + + var switches = info.Switches; + + if (switches.Length == 0) + { + return; + } + + var opt = switches[0]; + + if (opt < 0 || opt >= m_Arenas.Count) + { + return; + } + + var arena = m_Arenas[opt]; + + if (!m_From.InRange(m_Gate.GetWorldLocation(), 1) || m_From.Map != m_Gate.Map) + { + m_From.SendLocalizedMessage(1019002); // You are too far away to use the gate. + } + else if (DuelContext.CheckCombat(m_From)) + { + m_From.SendMessage( + 0x22, + "You have recently been in combat with another player and cannot use this moongate." + ); + } + else if (m_From.Spell != null) + { + m_From.SendLocalizedMessage(1049616); // You are too busy to do that at the moment. + } + else if (m_From.Map == arena.Facet && arena.Zone.Contains(m_From.Location)) + { + m_From.SendLocalizedMessage(1019003); // You are already there. + } + else + { + BaseCreature.TeleportPets(m_From, arena.GateIn, arena.Facet); + + m_From.Combatant = null; + m_From.Warmode = false; + m_From.Hidden = true; + + m_From.MoveToWorld(arena.GateIn, arena.Facet); + + Effects.PlaySound(arena.GateIn, arena.Facet, 0x1FE); + } } - sb.Append(le.Mobile.Name); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID != 1) + private void AddBorderedText(int x, int y, int width, string text, int color, int borderColor) { - return; + /*AddColoredText( x - 1, y, width, text, borderColor ); + AddColoredText( x + 1, y, width, text, borderColor ); + AddColoredText( x, y - 1, width, text, borderColor ); + AddColoredText( x, y + 1, width, text, borderColor );*/ + /*AddColoredText( x - 1, y - 1, width, text, borderColor ); + AddColoredText( x + 1, y + 1, width, text, borderColor );*/ + AddColoredText(x, y, width, text, color); } - var switches = info.Switches; - - if (switches.Length == 0) + private void AddColoredText(int x, int y, int width, string text, int color) { - return; + AddHtml(x, y, width, 20, color == 0 ? text : text.Color(color)); } - var opt = switches[0]; - - if (opt < 0 || opt >= _arenas.Count) + private void AddColumnHeader(int width, string name) { - return; + AddBackground(m_ColumnX, 12, width, 20, 0x242C); + AddImageTiled(m_ColumnX + 2, 14, width - 4, 16, 0x2430); + + if (name != null) + { + AddBorderedText(m_ColumnX, 13, width, name.Center(), 0xFFFFFF, 0); + } + + m_ColumnX += width; } - - var arena = _arenas[opt]; - var from = sender.Mobile; - - if (!from.InRange(_gate.GetWorldLocation(), 1) || from.Map != _gate.Map) - { - from.SendLocalizedMessage(1019002); // You are too far away to use the gate. - } - else if (DuelContext.CheckCombat(from)) - { - from.SendMessage( - 0x22, - "You have recently been in combat with another player and cannot use this moongate." - ); - } - else if (from.Spell != null) - { - from.SendLocalizedMessage(1049616); // You are too busy to do that at the moment. - } - else if (from.Map == arena.Facet && arena.Zone.Contains(from.Location)) - { - from.SendLocalizedMessage(1019003); // You are already there. - } - else - { - BaseCreature.TeleportPets(from, arena.GateIn, arena.Facet); - - from.Combatant = null; - from.Warmode = false; - from.Hidden = true; - - from.MoveToWorld(arena.GateIn, arena.Facet); - - Effects.PlaySound(arena.GateIn, arena.Facet, 0x1FE); - } - } - - private static void AddBorderedText(ref DynamicGumpBuilder builder, int x, int y, int width, string text, int color) => - AddColoredText(ref builder, x, y, width, text, color); - - private static void AddColoredText(ref DynamicGumpBuilder builder, int x, int y, int width, string text, int color) => - builder.AddHtml(x, y, width, 20, color == 0 ? text : text.Color(color)); - - private void AddColumnHeader(ref DynamicGumpBuilder builder, int width, string name) - { - builder.AddBackground(_columnX, 12, width, 20, 0x242C); - builder.AddImageTiled(_columnX + 2, 14, width - 4, 16, 0x2430); - - if (name != null) - { - AddBorderedText(ref builder, _columnX, 13, width, name.Center(), 0xFFFFFF); - } - - _columnX += width; } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/BeginGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/BeginGump.cs index 92f3bbf3d..c4dce0189 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/BeginGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/BeginGump.cs @@ -1,65 +1,50 @@ using Server.Gumps; -namespace Server.Engines.ConPVP; - -public class BeginGump : StaticGump +namespace Server.Engines.ConPVP { - private const int LabelColor32 = 0xFFFFFF; - private const int BlackColor32 = 0x000008; - - public override bool Singleton => true; - - private BeginGump() : base(50, 50) + public class BeginGump : Gump { - } + private const int LabelColor32 = 0xFFFFFF; + private const int BlackColor32 = 0x000008; - public static void DisplayTo(Mobile from, int count) - { - if (from?.NetState == null) + public BeginGump(int count) : base(50, 50) { - return; + AddPage(0); + + const int offset = 50; + + AddBackground(1, 1, 398, 202 - offset, 3600); + + AddImageTiled(16, 15, 369, 173 - offset, 3604); + AddAlphaRegion(16, 15, 369, 173 - offset); + + AddImage(215, -43, 0xEE40); + + var duelCountdownBlack = "Duel Countdown".Center(BlackColor32); + AddHtml(22 - 1, 22, 294, 20, duelCountdownBlack); + AddHtml(22 + 1, 22, 294, 20, duelCountdownBlack); + AddHtml(22, 22 - 1, 294, 20, duelCountdownBlack); + AddHtml(22, 22 + 1, 294, 20, duelCountdownBlack); + AddHtml(22, 22, 294, 20, "Duel Countdown".Center(LabelColor32)); + + var beginMessageBlack = + "The arranged duel is about to begin. During this countdown period you may not cast spells and you may not move. This message will close automatically when the period ends." + .Color(BlackColor32); + + AddHtml(22 - 1, 50, 294, 80, beginMessageBlack); + AddHtml(22 + 1, 50, 294, 80, beginMessageBlack); + AddHtml(22, 50 - 1, 294, 80, beginMessageBlack); + AddHtml(22, 50 + 1, 294, 80, beginMessageBlack); + AddHtml( + 22, + 50, + 294, + 80, + "The arranged duel is about to begin. During this countdown period you may not cast spells and you may not move. This message will close automatically when the period ends." + .Color(0xFFCC66) + ); + + AddButton(314 - 50, 157 - offset, 247, 248, 1); } - - from.SendGump(new BeginGump()); - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - const int offset = 50; - - builder.AddBackground(1, 1, 398, 202 - offset, 3600); - - builder.AddImageTiled(16, 15, 369, 173 - offset, 3604); - builder.AddAlphaRegion(16, 15, 369, 173 - offset); - - builder.AddImage(215, -43, 0xEE40); - - var duelCountdownBlack = "Duel Countdown".Center(BlackColor32); - builder.AddHtml(22 - 1, 22, 294, 20, duelCountdownBlack); - builder.AddHtml(22 + 1, 22, 294, 20, duelCountdownBlack); - builder.AddHtml(22, 22 - 1, 294, 20, duelCountdownBlack); - builder.AddHtml(22, 22 + 1, 294, 20, duelCountdownBlack); - builder.AddHtml(22, 22, 294, 20, "Duel Countdown".Center(LabelColor32)); - - var beginMessageBlack = - "The arranged duel is about to begin. During this countdown period you may not cast spells and you may not move. This message will close automatically when the period ends." - .Color(BlackColor32); - - builder.AddHtml(22 - 1, 50, 294, 80, beginMessageBlack); - builder.AddHtml(22 + 1, 50, 294, 80, beginMessageBlack); - builder.AddHtml(22, 50 - 1, 294, 80, beginMessageBlack); - builder.AddHtml(22, 50 + 1, 294, 80, beginMessageBlack); - builder.AddHtml( - 22, - 50, - 294, - 80, - "The arranged duel is about to begin. During this countdown period you may not cast spells and you may not move. This message will close automatically when the period ends." - .Color(0xFFCC66) - ); - - builder.AddButton(314 - 50, 157 - offset, 247, 248, 1); } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs index ee02edf60..376042f72 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs @@ -1,674 +1,689 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Text; using Server.Factions; using Server.Gumps; using Server.Mobiles; using Server.Network; using Server.Targeting; -using Server.Text; -namespace Server.Engines.ConPVP; - -public class ConfirmSignupGump : DynamicGump +namespace Server.Engines.ConPVP { - private const int BlackColor32 = 0x000008; - private const int LabelColor32 = 0xFFFFFF; - private readonly List _players; - private readonly Mobile _registrar; - private readonly Tournament _tournament; - - public override bool Singleton => true; - - private ConfirmSignupGump(Mobile registrar, Tournament tourney, List players) : base(50, 50) + public class ConfirmSignupGump : Gump { - _registrar = registrar; - _tournament = tourney; - _players = players; - } + private const int BlackColor32 = 0x000008; + private const int LabelColor32 = 0xFFFFFF; + private readonly Mobile m_From; + private readonly List m_Players; + private readonly Mobile m_Registrar; + private readonly Tournament m_Tournament; - public static void DisplayTo(Mobile from, Mobile registrar, Tournament tourney, List players) - { - if (from?.NetState == null || tourney == null || players == null) + public override bool Singleton => true; + + public ConfirmSignupGump(Mobile from, Mobile registrar, Tournament tourney, List players) : base(50, 50) { - return; - } + m_From = from; + m_Registrar = registrar; + m_Tournament = tourney; + m_Players = players; - var gumps = from.GetGumps(); - gumps.Close(); - gumps.Close(); - gumps.Close(); + var gumps = m_From.GetGumps(); - from.SendGump(new ConfirmSignupGump(registrar, tourney, players)); - } + gumps.Close(); + gumps.Close(); + gumps.Close(); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); + var ruleset = tourney.Ruleset; + var basedef = ruleset.Base; - var ruleset = _tournament.Ruleset; - var basedef = ruleset.Base; + var height = 185 + 60 + 12; - var height = 185 + 60 + 12; + var changes = 0; - var changes = 0; + BitArray defs; - BitArray defs; - - if (ruleset.Flavors.Count > 0) - { - defs = new BitArray(basedef.Options); - - for (var i = 0; i < ruleset.Flavors.Count; ++i) + if (ruleset.Flavors.Count > 0) { - defs.Or(ruleset.Flavors[i].Options); - } + defs = new BitArray(basedef.Options); - height += ruleset.Flavors.Count * 18; - } - else - { - defs = basedef.Options; - } - - var opts = ruleset.Options; - - for (var i = 0; i < opts.Length; ++i) - { - if (defs[i] != opts[i]) - { - ++changes; - } - } - - height += changes * 22; - - height += 10 + 22 + 25 + 25; - - if (_tournament.PlayersPerParticipant > 1) - { - height += 36 + _tournament.PlayersPerParticipant * 20; - } - - builder.AddPage(); - - builder.AddBackground(1, 1, 398, height, 3600); - - builder.AddImageTiled(16, 15, 369, height - 29, 3604); - builder.AddAlphaRegion(16, 15, 369, height - 29); - - builder.AddImage(215, -43, 0xEE40); - - using var sb = new ValueStringBuilder(stackalloc char[64]); - - if (_tournament.TourneyType == TourneyType.FreeForAll) - { - sb.Append("FFA"); - } - else if (_tournament.TourneyType == TourneyType.RandomTeam) - { - sb.Append($"{_tournament.ParticipantsPerMatch}-Team"); - } - else if (_tournament.TourneyType == TourneyType.Faction) - { - sb.Append($"{_tournament.ParticipantsPerMatch}-Team Faction"); - } - else if (_tournament.TourneyType == TourneyType.RedVsBlue) - { - sb.Append("Red v Blue"); - } - else - { - for (var i = 0; i < _tournament.ParticipantsPerMatch; ++i) - { - if (sb.Length > 0) + for (var i = 0; i < ruleset.Flavors.Count; ++i) { - sb.Append('v'); + defs.Or(ruleset.Flavors[i].Options); } - sb.Append(_tournament.PlayersPerParticipant); + height += ruleset.Flavors.Count * 18; + } + else + { + defs = basedef.Options; } - } - if (_tournament.EventController != null) - { - sb.Append($" {_tournament.EventController.Title}"); - } - - sb.Append(" Tournament Signup"); - - AddBorderedText(ref builder, 22, 22, 294, 20, sb.AsSpan().Center(), LabelColor32, BlackColor32); - AddBorderedText( - ref builder, - 22, - 50, - 294, - 40, - "You have requested to join the tournament. Do you accept the rules?", - 0xB0C868, - BlackColor32 - ); - - builder.AddImageTiled(32, 88, 264, 1, 9107); - builder.AddImageTiled(42, 90, 264, 1, 9157); - - var y = 100; - - var groupText = _tournament.GroupType switch - { - GroupingType.HighVsLow => "High vs Low", - GroupingType.Nearest => "Closest opponent", - GroupingType.Random => "Random", - _ => null - }; - - AddBorderedText(ref builder, 35, y, 190, 20, $"Grouping: {groupText}", LabelColor32, BlackColor32); - y += 20; - - var tieText = _tournament.TieType switch - { - TieType.Random => "Random", - TieType.Highest => "Highest advances", - TieType.Lowest => "Lowest advances", - TieType.FullAdvancement => _tournament.ParticipantsPerMatch == 2 ? "Both advance" : "Everyone advances", - TieType.FullElimination => _tournament.ParticipantsPerMatch == 2 ? "Both eliminated" : "Everyone eliminated", - _ => null - }; - - AddBorderedText(ref builder, 35, y, 190, 20, $"Tiebreaker: {tieText}", LabelColor32, BlackColor32); - y += 20; - - string sdText; - - if (_tournament.SuddenDeath > TimeSpan.Zero) - { - sdText = _tournament.SuddenDeathRounds > 0 ? - $"Sudden Death: {(int)_tournament.SuddenDeath.TotalMinutes}:{_tournament.SuddenDeath.Seconds:D2} (first {_tournament.SuddenDeathRounds} rounds)" : - $"Sudden Death: {(int)_tournament.SuddenDeath.TotalMinutes}:{_tournament.SuddenDeath.Seconds:D2} (all rounds)"; - } - else - { - sdText = "Sudden Death: Off"; - } - - AddBorderedText(ref builder, 35, y, 240, 20, sdText, LabelColor32, BlackColor32); - y += 20; - - y += 6; - builder.AddImageTiled(32, y - 1, 264, 1, 9107); - builder.AddImageTiled(42, y + 1, 264, 1, 9157); - y += 6; - - AddBorderedText(ref builder, 35, y, 190, 20, $"Ruleset: {basedef.Title}", LabelColor32, BlackColor32); - y += 20; - - for (var i = 0; i < ruleset.Flavors.Count; ++i, y += 18) - { - AddBorderedText(ref builder, 35, y, 190, 20, $" + {ruleset.Flavors[i].Title}", LabelColor32, BlackColor32); - } - - y += 4; - - if (changes > 0) - { - AddBorderedText(ref builder, 35, y, 190, 20, "Modifications:", LabelColor32, BlackColor32); - y += 20; + var opts = ruleset.Options; for (var i = 0; i < opts.Length; ++i) { if (defs[i] != opts[i]) { - var name = ruleset.Layout.FindByIndex(i); - - if (name != null) // sanity - { - builder.AddImage(35, y, opts[i] ? 0xD3 : 0xD2); - AddBorderedText(ref builder, 60, y, 165, 22, name, LabelColor32, BlackColor32); - } - - y += 22; - } - } - } - else - { - AddBorderedText(ref builder, 35, y, 190, 20, "Modifications: None", LabelColor32, BlackColor32); - y += 20; - } - - if (_tournament.PlayersPerParticipant > 1) - { - y += 8; - builder.AddImageTiled(32, y - 1, 264, 1, 9107); - builder.AddImageTiled(42, y + 1, 264, 1, 9157); - y += 8; - - AddBorderedText(ref builder, 35, y, 190, 20, "Your Team", LabelColor32, BlackColor32); - y += 20; - - for (var i = 0; i < _players.Count; ++i, y += 20) - { - if (i == 0) - { - builder.AddImage(35, y, 0xD2); - } - else - { - AddGoldenButton(ref builder, 35, y, 1 + i); - } - - AddBorderedText(ref builder, 60, y, 200, 20, _players[i].Name, LabelColor32, BlackColor32); - } - - for (var i = _players.Count; i < _tournament.PlayersPerParticipant; ++i, y += 20) - { - if (i == 0) - { - builder.AddImage(35, y, 0xD2); - } - else - { - AddGoldenButton(ref builder, 35, y, 1 + i); - } - - AddBorderedText(ref builder, 60, y, 200, 20, "(Empty)", LabelColor32, BlackColor32); - } - } - - y += 8; - builder.AddImageTiled(32, y - 1, 264, 1, 9107); - builder.AddImageTiled(42, y + 1, 264, 1, 9157); - y += 8; - - builder.AddRadio(24, y, 9727, 9730, true, 1); - AddBorderedText(ref builder, 60, y + 5, 250, 20, "Yes, I wish to join the tournament.", LabelColor32, BlackColor32); - y += 35; - - builder.AddRadio(24, y, 9727, 9730, false, 2); - AddBorderedText(ref builder, 60, y + 5, 250, 20, "No, I do not wish to join.", LabelColor32, BlackColor32); - y += 35; - - y -= 3; - builder.AddButton(314, y, 247, 248, 1); - } - - private static void AddBorderedText(ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color, int borderColor) - { - AddColoredText(ref builder, x - 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x - 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y - 1, width, height, text, borderColor); - AddColoredText(ref builder, x + 1, y + 1, width, height, text, borderColor); - AddColoredText(ref builder, x, y, width, height, text, color); - } - - private static void AddColoredText(ref DynamicGumpBuilder builder, int x, int y, int width, int height, string text, int color) - { - builder.AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); - } - - private static void AddGoldenButton(ref DynamicGumpBuilder builder, int x, int y, int bid) - { - builder.AddButton(x, y, 0xD2, 0xD2, bid); - builder.AddButton(x + 3, y + 3, 0xD8, 0xD8, bid); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; - - if (info.ButtonID != 1 || !info.IsSwitched(1)) - { - if (info.ButtonID > 1) - { - var index = info.ButtonID - 1; - - if (index > 0 && index < _players.Count) - { - _players.RemoveAt(index); - from.SendGump(this); // refresh-via-this - } - else if (_players.Count < _tournament.PlayersPerParticipant) - { - from.BeginTarget(12, false, TargetFlags.None, AddPlayer_OnTarget); - from.SendGump(this); // refresh-via-this + ++changes; } } - return; - } + height += changes * 22; - switch (_tournament.Stage) - { - case TournamentStage.Fighting: - { - if (_registrar != null) - { - if (_tournament.HasParticipant(from)) - { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "Excuse me? You are already signed up.", - from.NetState - ); - } - else - { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "The tournament has already begun. You are too late to signup now.", - from.NetState - ); - } - } + height += 10 + 22 + 25 + 25; - break; - } - case TournamentStage.Inactive: - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "The tournament is closed.", - from.NetState - ); - - break; - } - case TournamentStage.Signup: - { - if (_players.Count != _tournament.PlayersPerParticipant) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "You have not yet chosen your team.", - from.NetState - ); - - from.SendGump(this); // refresh-via-this - break; - } - - var ladder = Ladder.Instance; - - for (var i = 0; i < _players.Count; ++i) - { - var mob = _players[i]; - - var entry = ladder?.Find(mob); - - if (entry != null && Ladder.GetLevel(entry.Experience) < _tournament.LevelRequirement) - { - if (_registrar != null) - { - if (mob == from) - { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "You have not yet proven yourself a worthy dueler.", - from.NetState - ); - } - else - { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - $"{mob.Name} has not yet proven themselves a worthy dueler.", - from.NetState - ); - } - } - } - else if (_tournament.IsFactionRestricted && Faction.Find(mob) == null) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "Only those who have declared their faction allegiance may participate.", - from.NetState - ); - } - else if (_tournament.HasParticipant(mob)) - { - if (_registrar != null) - { - if (mob == from) - { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "You have already entered this tournament.", - from.NetState - ); - } - else - { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - $"{mob.Name} has already entered this tournament.", - from.NetState - ); - } - } - } - else if (mob is PlayerMobile mobile && mobile.DuelContext != null) - { - if (mob == from) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "You are already assigned to a duel. You must yield it before joining this tournament.", - from.NetState - ); - } - else - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - $"{mobile.Name} is already assigned to a duel. They must yield it before joining this tournament.", - from.NetState - ); - } - } - else - { - continue; - } - - from.SendGump(this); // refresh-via-this - return; - } - - if (_registrar != null) - { - var minutesUntil = (int)Math.Round( - (_tournament.SignupStart + _tournament.SignupPeriod - Core.Now) - .TotalMinutes - ); - - var timeUntil = minutesUntil == 0 - ? "momentarily" - : $"in {minutesUntil} minute{(minutesUntil == 1 ? "" : "s")}"; - - var title = from.Female ? "Lady" : "Lord"; - - switch (_tournament.PlayersPerParticipant) - { - case 1: - { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - $"As you say m'{title}. I've written your name to the bracket. The tournament will begin {timeUntil}.", - from.NetState - ); - break; - } - case 2: - { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - $"As you wish m'{title}. The tournament will begin {timeUntil}, but first you must name your partner.", - from.NetState - ); - break; - } - default: - { - _registrar.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - $"As you wish m'{title}. The tournament will begin {timeUntil}, but first you must name your team.", - from.NetState - ); - break; - } - } - } - - var part = new TourneyParticipant(from); - part.Players.Clear(); - part.Players.AddRange(_players); - - _tournament.Participants.Add(part); - - break; - } - } - } - - private void AddPlayer_OnTarget(Mobile from, object obj) - { - if (obj is not Mobile mob || mob == from) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "Excuse me?", - from.NetState - ); - } - else if (!mob.Player) - { - if (mob.Body.IsHuman) + if (tourney.PlayersPerParticipant > 1) { - mob.SayTo(from, 1005443); // Nay, I would rather stay here and watch a nail rust. + height += 36 + tourney.PlayersPerParticipant * 20; + } + + Closable = false; + + AddPage(0); + + // AddBackground( 0, 0, 400, 220, 9150 ); + AddBackground(1, 1, 398, height, 3600); + // AddBackground( 16, 15, 369, 189, 9100 ); + + AddImageTiled(16, 15, 369, height - 29, 3604); + AddAlphaRegion(16, 15, 369, height - 29); + + AddImage(215, -43, 0xEE40); + // AddImage( 330, 141, 0x8BA ); + + var sb = new StringBuilder(); + + if (tourney.TourneyType == TourneyType.FreeForAll) + { + sb.Append("FFA"); + } + else if (tourney.TourneyType == TourneyType.RandomTeam) + { + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-Team"); + } + else if (tourney.TourneyType == TourneyType.Faction) + { + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-Team Faction"); + } + else if (tourney.TourneyType == TourneyType.RedVsBlue) + { + sb.Append("Red v Blue"); } else { - mob.SayTo(from, 1005444); // The creature ignores your offer. + for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) + { + if (sb.Length > 0) + { + sb.Append('v'); + } + + sb.Append(tourney.PlayersPerParticipant); + } + } + + if (tourney.EventController != null) + { + sb.Append(' ').Append(tourney.EventController.Title); + } + + sb.Append(" Tournament Signup"); + + AddBorderedText(22, 22, 294, 20, sb.ToString().Center(), LabelColor32, BlackColor32); + AddBorderedText( + 22, + 50, + 294, + 40, + "You have requested to join the tournament. Do you accept the rules?", + 0xB0C868, + BlackColor32 + ); + + AddImageTiled(32, 88, 264, 1, 9107); + AddImageTiled(42, 90, 264, 1, 9157); + + var y = 100; + + var groupText = tourney.GroupType switch + { + GroupingType.HighVsLow => "High vs Low", + GroupingType.Nearest => "Closest opponent", + GroupingType.Random => "Random", + _ => null + }; + + AddBorderedText(35, y, 190, 20, $"Grouping: {groupText}", LabelColor32, BlackColor32); + y += 20; + + var tieText = tourney.TieType switch + { + TieType.Random => "Random", + TieType.Highest => "Highest advances", + TieType.Lowest => "Lowest advances", + TieType.FullAdvancement => tourney.ParticipantsPerMatch == 2 ? "Both advance" : "Everyone advances", + TieType.FullElimination => tourney.ParticipantsPerMatch == 2 ? "Both eliminated" : "Everyone eliminated", + _ => null + }; + + AddBorderedText(35, y, 190, 20, $"Tiebreaker: {tieText}", LabelColor32, BlackColor32); + y += 20; + + string sdText; + + if (tourney.SuddenDeath > TimeSpan.Zero) + { + sdText = tourney.SuddenDeathRounds > 0 ? + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (first {tourney.SuddenDeathRounds} rounds)" : + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (all rounds)"; + } + else + { + sdText = "Sudden Death: Off"; + } + + AddBorderedText(35, y, 240, 20, sdText, LabelColor32, BlackColor32); + y += 20; + + y += 6; + AddImageTiled(32, y - 1, 264, 1, 9107); + AddImageTiled(42, y + 1, 264, 1, 9157); + y += 6; + + AddBorderedText(35, y, 190, 20, $"Ruleset: {basedef.Title}", LabelColor32, BlackColor32); + y += 20; + + for (var i = 0; i < ruleset.Flavors.Count; ++i, y += 18) + { + AddBorderedText(35, y, 190, 20, $" + {ruleset.Flavors[i].Title}", LabelColor32, BlackColor32); + } + + y += 4; + + if (changes > 0) + { + AddBorderedText(35, y, 190, 20, "Modifications:", LabelColor32, BlackColor32); + y += 20; + + for (var i = 0; i < opts.Length; ++i) + { + if (defs[i] != opts[i]) + { + var name = ruleset.Layout.FindByIndex(i); + + if (name != null) // sanity + { + AddImage(35, y, opts[i] ? 0xD3 : 0xD2); + AddBorderedText(60, y, 165, 22, name, LabelColor32, BlackColor32); + } + + y += 22; + } + } + } + else + { + AddBorderedText(35, y, 190, 20, "Modifications: None", LabelColor32, BlackColor32); + y += 20; + } + + if (tourney.PlayersPerParticipant > 1) + { + y += 8; + AddImageTiled(32, y - 1, 264, 1, 9107); + AddImageTiled(42, y + 1, 264, 1, 9157); + y += 8; + + AddBorderedText(35, y, 190, 20, "Your Team", LabelColor32, BlackColor32); + y += 20; + + for (var i = 0; i < players.Count; ++i, y += 20) + { + if (i == 0) + { + AddImage(35, y, 0xD2); + } + else + { + AddGoldenButton(35, y, 1 + i); + } + + AddBorderedText(60, y, 200, 20, players[i].Name, LabelColor32, BlackColor32); + } + + for (var i = players.Count; i < tourney.PlayersPerParticipant; ++i, y += 20) + { + if (i == 0) + { + AddImage(35, y, 0xD2); + } + else + { + AddGoldenButton(35, y, 1 + i); + } + + AddBorderedText(60, y, 200, 20, "(Empty)", LabelColor32, BlackColor32); + } + } + + y += 8; + AddImageTiled(32, y - 1, 264, 1, 9107); + AddImageTiled(42, y + 1, 264, 1, 9157); + y += 8; + + AddRadio(24, y, 9727, 9730, true, 1); + AddBorderedText(60, y + 5, 250, 20, "Yes, I wish to join the tournament.", LabelColor32, BlackColor32); + y += 35; + + AddRadio(24, y, 9727, 9730, false, 2); + AddBorderedText(60, y + 5, 250, 20, "No, I do not wish to join.", LabelColor32, BlackColor32); + y += 35; + + y -= 3; + AddButton(314, y, 247, 248, 1); + } + + private void AddBorderedText(int x, int y, int width, int height, string text, int color, int borderColor) + { + AddColoredText(x - 1, y - 1, width, height, text, borderColor); + AddColoredText(x - 1, y + 1, width, height, text, borderColor); + AddColoredText(x + 1, y - 1, width, height, text, borderColor); + AddColoredText(x + 1, y + 1, width, height, text, borderColor); + AddColoredText(x, y, width, height, text, color); + } + + private void AddColoredText(int x, int y, int width, int height, string text, int color) + { + AddHtml(x, y, width, height, color == 0 ? text : text.Color(color)); + } + + public void AddGoldenButton(int x, int y, int bid) + { + AddButton(x, y, 0xD2, 0xD2, bid); + AddButton(x + 3, y + 3, 0xD8, 0xD8, bid); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (info.ButtonID == 1 && info.IsSwitched(1)) + { + var tourney = m_Tournament; + var from = m_From; + + switch (tourney.Stage) + { + case TournamentStage.Fighting: + { + if (m_Registrar != null) + { + if (m_Tournament.HasParticipant(from)) + { + m_Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "Excuse me? You are already signed up.", + from.NetState + ); + } + else + { + m_Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "The tournament has already begun. You are too late to signup now.", + from.NetState + ); + } + } + + break; + } + case TournamentStage.Inactive: + { + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "The tournament is closed.", + from.NetState + ); + + break; + } + case TournamentStage.Signup: + { + if (m_Players.Count != tourney.PlayersPerParticipant) + { + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "You have not yet chosen your team.", + from.NetState + ); + + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + break; + } + + var ladder = Ladder.Instance; + + for (var i = 0; i < m_Players.Count; ++i) + { + var mob = m_Players[i]; + + var entry = ladder?.Find(mob); + + if (entry != null && Ladder.GetLevel(entry.Experience) < tourney.LevelRequirement) + { + if (m_Registrar != null) + { + if (mob == from) + { + m_Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "You have not yet proven yourself a worthy dueler.", + from.NetState + ); + } + else + { + m_Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + $"{mob.Name} has not yet proven themselves a worthy dueler.", + from.NetState + ); + } + } + + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + return; + } + + if (tourney.IsFactionRestricted && Faction.Find(mob) == null) + { + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "Only those who have declared their faction allegiance may participate.", + from.NetState + ); + + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + return; + } + + if (tourney.HasParticipant(mob)) + { + if (m_Registrar != null) + { + if (mob == from) + { + m_Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "You have already entered this tournament.", + from.NetState + ); + } + else + { + m_Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + $"{mob.Name} has already entered this tournament.", + from.NetState + ); + } + } + + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + return; + } + + if (mob is PlayerMobile mobile && mobile.DuelContext != null) + { + if (mob == from) + { + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "You are already assigned to a duel. You must yield it before joining this tournament.", + from.NetState + ); + } + else + { + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + $"{mobile.Name} is already assigned to a duel. They must yield it before joining this tournament.", + from.NetState + ); + } + + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + return; + } + } + + if (m_Registrar != null) + { + string fmt; + + if (tourney.PlayersPerParticipant == 1) + { + fmt = + "As you say m'{0}. I've written your name to the bracket. The tournament will begin {1}."; + } + else if (tourney.PlayersPerParticipant == 2) + { + fmt = + "As you wish m'{0}. The tournament will begin {1}, but first you must name your partner."; + } + else + { + fmt = + "As you wish m'{0}. The tournament will begin {1}, but first you must name your team."; + } + + string timeUntil; + var minutesUntil = (int)Math.Round( + (tourney.SignupStart + tourney.SignupPeriod - Core.Now) + .TotalMinutes + ); + + if (minutesUntil == 0) + { + timeUntil = "momentarily"; + } + else + { + timeUntil = $"in {minutesUntil} minute{(minutesUntil == 1 ? "" : "s")}"; + } + + m_Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + string.Format(fmt, from.Female ? "Lady" : "Lord", timeUntil), + from.NetState + ); + } + + var part = new TourneyParticipant(from); + part.Players.Clear(); + part.Players.AddRange(m_Players); + + tourney.Participants.Add(part); + + break; + } + } + } + else if (info.ButtonID > 1) + { + var index = info.ButtonID - 1; + + if (index > 0 && index < m_Players.Count) + { + m_Players.RemoveAt(index); + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + } + else if (m_Players.Count < m_Tournament.PlayersPerParticipant) + { + m_From.BeginTarget(12, false, TargetFlags.None, AddPlayer_OnTarget); + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + } } } - else if (AcceptDuelGump.IsIgnored(mob, from) || mob.Blessed) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "They ignore your invitation.", - from.NetState - ); - } - else if (mob is not PlayerMobile pm) - { - return; - } - else if (pm.DuelContext != null) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "They are already assigned to another duel.", - from.NetState - ); - } - else if (mob.HasGump()) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "They have already been offered a partnership.", - from.NetState - ); - } - else if (mob.HasGump()) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "They are already trying to join this tournament.", - from.NetState - ); - } - else if (_players.Contains(mob)) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "You have already named them as a team member.", - from.NetState - ); - } - else if (_tournament.HasParticipant(mob)) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "They have already entered this tournament.", - from.NetState - ); - } - else if (_players.Count >= _tournament.PlayersPerParticipant) - { - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "Your team is full.", - from.NetState - ); - } - else - { - AcceptTeamGump.DisplayTo(from, mob, _tournament, _registrar, _players); - _registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x59, - false, - $"As you command m'{(from.Female ? "Lady" : "Lord")}. I've given your offer to {mob.Name}.", - from.NetState - ); - } + private void AddPlayer_OnTarget(Mobile from, object obj) + { + if (obj is not Mobile mob || mob == from) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); - from.SendGump(this); // refresh-via-this + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "Excuse me?", + from.NetState + ); + } + else if (!mob.Player) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + if (mob.Body.IsHuman) + { + mob.SayTo(from, 1005443); // Nay, I would rather stay here and watch a nail rust. + } + else + { + mob.SayTo(from, 1005444); // The creature ignores your offer. + } + } + else if (AcceptDuelGump.IsIgnored(mob, from) || mob.Blessed) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "They ignore your invitation.", + from.NetState + ); + } + else + { + if (mob is not PlayerMobile pm) + { + return; + } + + if (pm.DuelContext != null) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "They are already assigned to another duel.", + from.NetState + ); + } + else if (mob.HasGump()) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "They have already been offered a partnership.", + from.NetState + ); + } + else if (mob.HasGump()) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "They are already trying to join this tournament.", + from.NetState + ); + } + else if (m_Players.Contains(mob)) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "You have already named them as a team member.", + from.NetState + ); + } + else if (m_Tournament.HasParticipant(mob)) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "They have already entered this tournament.", + from.NetState + ); + } + else if (m_Players.Count >= m_Tournament.PlayersPerParticipant) + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "Your team is full.", + from.NetState + ); + } + else + { + m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players)); + mob.SendGump(new AcceptTeamGump(from, mob, m_Tournament, m_Registrar, m_Players)); + + m_Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x59, + false, + $"As you command m'{(from.Female ? "Lady" : "Lord")}. I've given your offer to {mob.Name}.", + from.NetState + ); + } + } + } } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/DuelContextGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/DuelContextGump.cs index ecb1e62cb..ac1b11556 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/DuelContextGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/DuelContextGump.cs @@ -1,165 +1,160 @@ using Server.Gumps; using Server.Network; -namespace Server.Engines.ConPVP; - -public class DuelContextGump : DynamicGump +namespace Server.Engines.ConPVP { - public override bool Singleton => true; - - private DuelContextGump(DuelContext context) : base(50, 50) => Context = context; - - public static void DisplayTo(Mobile from, DuelContext context) + public class DuelContextGump : Gump { - if (from?.NetState == null || context == null) + public override bool Singleton => true; + + public DuelContextGump(Mobile from, DuelContext context) : base(50, 50) { - return; - } + From = from; + Context = context; - var gumps = from.GetGumps(); - gumps.Close(); - gumps.Close(); + var gumps = from.GetGumps(); - from.SendGump(new DuelContextGump(context)); - } + gumps.Close(); + gumps.Close(); - public DuelContext Context { get; } + var count = context.Participants.Count; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var count = Context.Participants.Count; + if (count < 3) + { + count = 3; + } - if (count < 3) - { - count = 3; - } + var height = 35 + 10 + 22 + 30 + 22 + 22 + 2 + count * 22 + 2 + 30; - var height = 35 + 10 + 22 + 30 + 22 + 22 + 2 + count * 22 + 2 + 30; + AddPage(0); - builder.AddPage(); + AddBackground(0, 0, 300, height, 9250); + AddBackground(10, 10, 280, height - 20, 0xDAC); - builder.AddBackground(0, 0, 300, height, 9250); - builder.AddBackground(10, 10, 280, height - 20, 0xDAC); + AddHtml(35, 25, 230, 20, Center("Duel Setup")); - builder.AddHtml(35, 25, 230, 20, Center("Duel Setup")); + var x = 35; + var y = 47; - var x = 35; - var y = 47; - - AddGoldenButtonLabeled(ref builder, x, y, 1, "Rules"); - y += 22; - AddGoldenButtonLabeled(ref builder, x, y, 2, "Start"); - y += 22; - AddGoldenButtonLabeled(ref builder, x, y, 3, "Add Participant"); - y += 30; - - builder.AddHtml(35, y, 230, 20, Center("Participants")); - y += 22; - - for (var i = 0; i < Context.Participants.Count; ++i) - { - var p = Context.Participants[i]; - - AddGoldenButtonLabeled( - ref builder, - x, - y, - 4 + i, - string.Format( - p.Count == 1 ? "Player {0}: {3}" : "Team {0}: {1}/{2}: {3}", - 1 + i, - p.FilledSlots, - p.Count, - p.NameList - ) - ); + AddGoldenButtonLabeled(x, y, 1, "Rules"); y += 22; - } - } + AddGoldenButtonLabeled(x, y, 2, "Start"); + y += 22; + AddGoldenButtonLabeled(x, y, 3, "Add Participant"); + y += 30; - private static string Center(string text) => $"
{text}
"; + AddHtml(35, y, 230, 20, Center("Participants")); + y += 22; - private static void AddGoldenButton(ref DynamicGumpBuilder builder, int x, int y, int bid) - { - builder.AddButton(x, y, 0xD2, 0xD2, bid); - builder.AddButton(x + 3, y + 3, 0xD8, 0xD8, bid); - } + for (var i = 0; i < context.Participants.Count; ++i) + { + var p = context.Participants[i]; - private static void AddGoldenButtonLabeled(ref DynamicGumpBuilder builder, int x, int y, int bid, string text) - { - AddGoldenButton(ref builder, x, y, bid); - builder.AddHtml(x + 25, y, 200, 20, text); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (!Context.Registered) - { - return; + AddGoldenButtonLabeled( + x, + y, + 4 + i, + string.Format( + p.Count == 1 ? "Player {0}: {3}" : "Team {0}: {1}/{2}: {3}", + 1 + i, + p.FilledSlots, + p.Count, + p.NameList + ) + ); + y += 22; + } } - var from = sender.Mobile; - var index = info.ButtonID; + public Mobile From { get; } - switch (index) + public DuelContext Context { get; } + + public string Center(string text) => $"
{text}
"; + + public void AddGoldenButton(int x, int y, int bid) { - case -1: // CloseGump - { - break; - } - case 0: // closed - { - Context.Unregister(); - break; - } - case 1: // Rules - { - PickRulesetGump.DisplayTo(from, Context, Context.Ruleset); - break; - } - case 2: // Start - { - if (Context.CheckFull()) - { - Context.CloseAllGumps(); - Context.SendReadyUpGump(); - } - else - { - from.SendMessage( - "You cannot start the duel before all participating players have been assigned." - ); - from.SendGump(this); // refresh-via-this - } + AddButton(x, y, 0xD2, 0xD2, bid); + AddButton(x + 3, y + 3, 0xD8, 0xD8, bid); + } - break; - } - case 3: // New Participant - { - if (Context.Participants.Count < 10) + public void AddGoldenButtonLabeled(int x, int y, int bid, string text) + { + AddGoldenButton(x, y, bid); + AddHtml(x + 25, y, 200, 20, text); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (!Context.Registered) + { + return; + } + + var index = info.ButtonID; + + switch (index) + { + case -1: // CloseGump { - Context.Participants.Add(new Participant(Context, 1)); + break; } - else + case 0: // closed { - from.SendMessage("The number of participating parties may not be increased further."); + Context.Unregister(); + break; } - - from.SendGump(this); // refresh-via-this - - break; - } - default: // Participant - { - index -= 4; - - if (index >= 0 && index < Context.Participants.Count) + case 1: // Rules { - ParticipantGump.DisplayTo(from, Context, Context.Participants[index]); + // m_From.SendGump( new RulesetGump( m_From, m_Context.Ruleset, m_Context.Ruleset.Layout, m_Context ) ); + From.SendGump(new PickRulesetGump(From, Context, Context.Ruleset)); + break; } + case 2: // Start + { + if (Context.CheckFull()) + { + Context.CloseAllGumps(); + Context.SendReadyUpGump(); + // m_Context.SendReadyGump(); + } + else + { + From.SendMessage( + "You cannot start the duel before all participating players have been assigned." + ); + From.SendGump(new DuelContextGump(From, Context)); + } - break; - } + break; + } + case 3: // New Participant + { + if (Context.Participants.Count < 10) + { + Context.Participants.Add(new Participant(Context, 1)); + } + else + { + From.SendMessage("The number of participating parties may not be increased further."); + } + + From.SendGump(new DuelContextGump(From, Context)); + + break; + } + default: // Participant + { + index -= 4; + + if (index >= 0 && index < Context.Participants.Count) + { + From.SendGump(new ParticipantGump(From, Context, Context.Participants[index])); + } + + break; + } + } } } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs index 430351c0e..088d26789 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs @@ -1,228 +1,259 @@ using System; -using ModernUO.Serialization; +using System.Collections.Generic; using Server.Gumps; using Server.Network; -namespace Server.Engines.ConPVP; - -[SerializationGenerator(0, false)] -public partial class LadderItem : Item +namespace Server.Engines.ConPVP { - [SerializedCommandProperty(AccessLevel.GameMaster)] - [SerializableField(0)] - private LadderController _ladder; - - [Constructible] - public LadderItem() : base(0x117F) => Movable = false; - - public override string DefaultName => "1v1 leaderboard"; - - public override void OnDoubleClick(Mobile from) + public class LadderItem : Item { - if (from.InRange(GetWorldLocation(), 2)) - { - var ladder = ConPVP.Ladder.Instance ?? Ladder.Ladder; + [Constructible] + public LadderItem() : base(0x117F) => Movable = false; - if (ladder != null) + public LadderItem(Serial serial) : base(serial) + { + } + + [CommandProperty(AccessLevel.GameMaster)] + public LadderController Ladder { get; set; } + + public override string DefaultName => "1v1 leaderboard"; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(1); + + writer.Write(Ladder); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) { - LadderGump.DisplayTo(from, ladder); + case 1: + { + Ladder = reader.ReadEntity(); + break; + } } } - else + + public override void OnDoubleClick(Mobile from) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that - } - } -} - -public class LadderGump : DynamicGump -{ - private readonly Ladder _ladder; - - private int _page; - private int _columnX; - - public override bool Singleton => true; - - private LadderGump(Ladder ladder, int page) : base(50, 50) - { - _ladder = ladder; - _page = page; - } - - public static void DisplayTo(Mobile from, Ladder ladder, int page = 0) - { - if (from?.NetState == null || ladder == null) - { - return; - } - - from.SendGump(new LadderGump(ladder, page), true); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - _columnX = 12; - - builder.AddPage(); - - var list = _ladder.Entries; - - var lc = Math.Min(list.Count, 150); - - var start = _page * 15; - var end = start + 15; - - if (end > lc) - { - end = lc; - } - - var ct = end - start; - - var height = 12 + 20 + ct * 20 + 23 + 12; - - builder.AddBackground(0, 0, 499, height, 0x2436); - - for (var i = start + 1; i < end; i += 2) - { - builder.AddImageTiled(12, 32 + (i - start) * 20, 475, 20, 0x2430); - } - - builder.AddAlphaRegion(10, 10, 479, height - 20); - - if (_page > 0) - { - builder.AddButton(446, height - 12 - 2 - 16, 0x15E3, 0x15E7, 1); - } - else - { - builder.AddImage(446, height - 12 - 2 - 16, 0x2626); - } - - if ((_page + 1) * 15 < lc) - { - builder.AddButton(466, height - 12 - 2 - 16, 0x15E1, 0x15E5, 2); - } - else - { - builder.AddImage(466, height - 12 - 2 - 16, 0x2622); - } - - builder.AddHtml( - 16, - height - 12 - 2 - 18, - 400, - 20, - Html.Color($"Top {lc} of {list.Count:N0} duelists, page {_page + 1} of {(lc + 14) / 15}", 0xFFC000) - ); - - AddColumnHeader(ref builder, 75, "Rank"); - AddColumnHeader(ref builder, 115, "Level"); - AddColumnHeader(ref builder, 50, "Guild"); - AddColumnHeader(ref builder, 115, "Name"); - AddColumnHeader(ref builder, 60, "Wins"); - AddColumnHeader(ref builder, 60, "Losses"); - - for (var i = start; i < end && i < lc; ++i) - { - var entry = list[i]; - - var y = 32 + (i - start) * 20; - var x = 12; - - AddBorderedText(ref builder, x, y, 75, Rank(i + 1).Center(), 0xFFFFFF); - x += 75; - - builder.AddImage(x + 3, y + 4, 0x805); - - var xp = entry.Experience; - var level = Ladder.GetLevel(xp); - - Ladder.GetLevelInfo(level, out var xpBase, out var xpAdvance); - - int width; - - var xpOffset = xp - xpBase; - - if (xpOffset >= xpAdvance) + if (from.InRange(GetWorldLocation(), 2)) { - width = 109; // level 50 + var ladder = ConPVP.Ladder.Instance ?? Ladder.Ladder; + + if (ladder != null) + { + from.SendGump(new LadderGump(ladder), true); + } } else { - width = (109 * xpOffset + xpAdvance / 2) / (xpAdvance - 1); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that } + } + } - builder.AddImageTiled(x + 3, y + 4, width, 11, 0x806); - AddBorderedText(ref builder, x, y, 115, Html.Center($"{level}"), 0xFFFFFF); - x += 115; + public class LadderGump : Gump + { + private readonly Ladder m_Ladder; - var mob = entry.Mobile; + private readonly List m_List; + private readonly int m_Page; + private int m_ColumnX = 12; - if (mob.Guild != null) + public LadderGump(Ladder ladder, int page = 0) : base(50, 50) + { + m_Ladder = ladder; + m_Page = page; + + AddPage(0); + + m_List = new List(ladder.Entries); + + var lc = Math.Min(m_List.Count, 150); + + var start = page * 15; + var end = start + 15; + + if (end > lc) { - AddBorderedText(ref builder, x, y, 50, mob.Guild.Abbreviation.Center(), 0xFFFFFF); + end = lc; } - x += 50; + var ct = end - start; - AddBorderedText(ref builder, x + 5, y, 115 - 5, mob.Name, 0xFFFFFF); - x += 115; + var height = 12 + 20 + ct * 20 + 23 + 12; - AddBorderedText(ref builder, x, y, 60, Html.Center($"{entry.Wins}"), 0xFFFFFF); - x += 60; + AddBackground(0, 0, 499, height, 0x2436); - AddBorderedText(ref builder, x, y, 60, Html.Center($"{entry.Losses}"), 0xFFFFFF); - } - } + for (var i = start + 1; i < end; i += 2) + { + AddImageTiled(12, 32 + (i - start) * 20, 475, 20, 0x2430); + } - public static string Rank(int num) - { - var numStr = num.ToString("N0"); + AddAlphaRegion(10, 10, 479, height - 20); - if (num % 100 > 10 && num % 100 < 20) - { - return $"{numStr}th"; + if (page > 0) + { + AddButton(446, height - 12 - 2 - 16, 0x15E3, 0x15E7, 1); + } + else + { + AddImage(446, height - 12 - 2 - 16, 0x2626); + } + + if ((page + 1) * 15 < lc) + { + AddButton(466, height - 12 - 2 - 16, 0x15E1, 0x15E5, 2); + } + else + { + AddImage(466, height - 12 - 2 - 16, 0x2622); + } + + AddHtml( + 16, + height - 12 - 2 - 18, + 400, + 20, + $"Top {lc} of {m_List.Count:N0} duelists, page {page + 1} of {(lc + 14) / 15}".Color(0xFFC000) + ); + + AddColumnHeader(75, "Rank"); + AddColumnHeader(115, "Level"); + AddColumnHeader(50, "Guild"); + AddColumnHeader(115, "Name"); + AddColumnHeader(60, "Wins"); + AddColumnHeader(60, "Losses"); + + for (var i = start; i < end && i < lc; ++i) + { + var entry = m_List[i]; + + var y = 32 + (i - start) * 20; + var x = 12; + + AddBorderedText(x, y, 75, Rank(i + 1).Center(), 0xFFFFFF, 0); + x += 75; + + /*AddImage( 20, y + 5, 0x2616, 0x96C ); + AddImage( 22, y + 5, 0x2616, 0x96C ); + AddImage( 20, y + 7, 0x2616, 0x96C ); + AddImage( 22, y + 7, 0x2616, 0x96C ); + + AddImage( 21, y + 6, 0x2616, 0x454 );*/ + + AddImage(x + 3, y + 4, 0x805); + + var xp = entry.Experience; + var level = Ladder.GetLevel(xp); + + Ladder.GetLevelInfo(level, out var xpBase, out var xpAdvance); + + int width; + + var xpOffset = xp - xpBase; + + if (xpOffset >= xpAdvance) + { + width = 109; // level 50 + } + else + { + width = (109 * xpOffset + xpAdvance / 2) / (xpAdvance - 1); + } + + // AddImageTiled( 21, y + 6, width, 8, 0x2617 ); + AddImageTiled(x + 3, y + 4, width, 11, 0x806); + AddBorderedText(x, y, 115, level.ToString().Center(), 0xFFFFFF, 0); + x += 115; + + var mob = entry.Mobile; + + if (mob.Guild != null) + { + AddBorderedText(x, y, 50, mob.Guild.Abbreviation.Center(), 0xFFFFFF, 0); + } + + x += 50; + + AddBorderedText(x + 5, y, 115 - 5, mob.Name, 0xFFFFFF, 0); + x += 115; + + AddBorderedText(x, y, 60, entry.Wins.ToString().Center(), 0xFFFFFF, 0); + x += 60; + + AddBorderedText(x, y, 60, entry.Losses.ToString().Center(), 0xFFFFFF, 0); + x += 60; + + // AddBorderedText( 292 + 15, y, 115 - 30, String.Format( "{0}
/
{1}
", entry.Wins, entry.Losses ), 0xFFC000, 0 ); + } } - return (num % 10) switch + public static string Rank(int num) { - 1 => $"{numStr}st", - 2 => $"{numStr}nd", - 3 => $"{numStr}rd", - _ => $"{numStr}th" - }; - } + var numStr = num.ToString("N0"); - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; + if (num % 100 > 10 && num % 100 < 20) + { + return $"{numStr}th"; + } - if (info.ButtonID == 1 && _page > 0) - { - _page--; - } - else if (info.ButtonID == 2 && (_page + 1) * 15 < Math.Min(_ladder.Entries.Count, 150)) - { - _page++; + return (num % 10) switch + { + 1 => $"{numStr}st", + 2 => $"{numStr}nd", + 3 => $"{numStr}rd", + _ => $"{numStr}th" + }; } - from.SendGump(this); // refresh-via-this - } + public override void OnResponse(NetState sender, in RelayInfo info) + { + var from = sender.Mobile; - private static void AddBorderedText(ref DynamicGumpBuilder builder, int x, int y, int width, string text, int color) => - AddColoredText(ref builder, x, y, width, text, color); + if (info.ButtonID == 1 && m_Page > 0) + { + from.SendGump(new LadderGump(m_Ladder, m_Page - 1)); + } + else if (info.ButtonID == 2 && (m_Page + 1) * 15 < Math.Min(m_List.Count, 150)) + { + from.SendGump(new LadderGump(m_Ladder, m_Page + 1)); + } + } - private static void AddColoredText(ref DynamicGumpBuilder builder, int x, int y, int width, string text, int color) => - builder.AddHtml(x, y, width, 20, color == 0 ? text : text.Color(color)); + private void AddBorderedText(int x, int y, int width, string text, int color, int borderColor) + { + /*AddColoredText( x - 1, y, width, text, borderColor ); + AddColoredText( x + 1, y, width, text, borderColor ); + AddColoredText( x, y - 1, width, text, borderColor ); + AddColoredText( x, y + 1, width, text, borderColor );*/ + /*AddColoredText( x - 1, y - 1, width, text, borderColor ); + AddColoredText( x + 1, y + 1, width, text, borderColor );*/ + AddColoredText(x, y, width, text, color); + } - private void AddColumnHeader(ref DynamicGumpBuilder builder, int width, string name) - { - builder.AddBackground(_columnX, 12, width, 20, 0x242C); - builder.AddImageTiled(_columnX + 2, 14, width - 4, 16, 0x2430); - AddBorderedText(ref builder, _columnX, 13, width, name.Center(), 0xFFFFFF); + private void AddColoredText(int x, int y, int width, string text, int color) + { + AddHtml(x, y, width, 20, color == 0 ? text : text.Color(color)); + } - _columnX += width; + private void AddColumnHeader(int width, string name) + { + AddBackground(m_ColumnX, 12, width, 20, 0x242C); + AddImageTiled(m_ColumnX + 2, 14, width - 4, 16, 0x2430); + AddBorderedText(m_ColumnX, 13, width, name.Center(), 0xFFFFFF, 0); + + m_ColumnX += width; + } } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/ParticipantGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/ParticipantGump.cs index 10aa49976..6aad2ce69 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/ParticipantGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/ParticipantGump.cs @@ -3,257 +3,283 @@ using Server.Mobiles; using Server.Network; using Server.Targeting; -namespace Server.Engines.ConPVP; - -public class ParticipantGump : DynamicGump +namespace Server.Engines.ConPVP { - public override bool Singleton => true; - - private ParticipantGump(DuelContext context, Participant p) : base(50, 50) + public class ParticipantGump : Gump { - Context = context; - Participant = p; - } + public override bool Singleton => true; - public static void DisplayTo(Mobile from, DuelContext context, Participant p) - { - if (from?.NetState == null || context == null || p == null) + public ParticipantGump(Mobile from, DuelContext context, Participant p) : base(50, 50) { - return; - } + From = from; + Context = context; + Participant = p; - var gumps = from.GetGumps(); - gumps.Close(); - gumps.Close(); + var gumps = from.GetGumps(); - from.SendGump(new ParticipantGump(context, p)); - } + gumps.Close(); + gumps.Close(); - public DuelContext Context { get; } + var count = p.Players.Length; - public Participant Participant { get; } + if (count < 4) + { + count = 4; + } - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var count = Participant.Players.Length; + AddPage(0); - if (count < 4) - { - count = 4; - } + var height = 35 + 10 + 22 + 22 + 30 + 22 + 2 + count * 22 + 2 + 30; - builder.AddPage(); + AddBackground(0, 0, 300, height, 9250); + AddBackground(10, 10, 280, height - 20, 0xDAC); - var height = 35 + 10 + 22 + 22 + 30 + 22 + 2 + count * 22 + 2 + 30; + AddButton(240, 25, 0xFB1, 0xFB3, 3); - builder.AddBackground(0, 0, 300, height, 9250); - builder.AddBackground(10, 10, 280, height - 20, 0xDAC); + // AddButton( 223, 54, 0x265A, 0x265A, 4, ); - builder.AddButton(240, 25, 0xFB1, 0xFB3, 3); + AddHtml(35, 25, 230, 20, Center("Participant Setup")); - builder.AddHtml(35, 25, 230, 20, Center("Participant Setup")); + var x = 35; + var y = 47; - var x = 35; - var y = 47; - - builder.AddHtml(x, y, 200, 20, $"Team Size: {Participant.Players.Length}"); - y += 22; - - AddGoldenButtonLabeled(ref builder, x + 20, y, 1, "Increase"); - y += 22; - AddGoldenButtonLabeled(ref builder, x + 20, y, 2, "Decrease"); - y += 30; - - builder.AddHtml(35, y, 230, 20, Center("Players")); - y += 22; - - for (var i = 0; i < Participant.Players.Length; ++i) - { - var pl = Participant.Players[i]; - - AddGoldenButtonLabeled(ref builder, x, y, 5 + i, $"{1 + i}: {(pl == null ? "Empty" : pl.Mobile.Name)}"); + AddHtml(x, y, 200, 20, $"Team Size: {p.Players.Length}"); y += 22; - } - } - private static string Center(string text) => $"
{text}
"; + AddGoldenButtonLabeled(x + 20, y, 1, "Increase"); + y += 22; + AddGoldenButtonLabeled(x + 20, y, 2, "Decrease"); + y += 30; - private static void AddGoldenButton(ref DynamicGumpBuilder builder, int x, int y, int bid) - { - builder.AddButton(x, y, 0xD2, 0xD2, bid); - builder.AddButton(x + 3, y + 3, 0xD8, 0xD8, bid); - } + AddHtml(35, y, 230, 20, Center("Players")); + y += 22; - private static void AddGoldenButtonLabeled(ref DynamicGumpBuilder builder, int x, int y, int bid, string text) - { - AddGoldenButton(ref builder, x, y, bid); - builder.AddHtml(x + 25, y, 200, 20, text); - } + for (var i = 0; i < p.Players.Length; ++i) + { + var pl = p.Players[i]; - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (!Context.Registered) - { - return; + AddGoldenButtonLabeled(x, y, 5 + i, $"{1 + i}: {(pl == null ? "Empty" : pl.Mobile.Name)}"); + y += 22; + } } - var from = sender.Mobile; - var bid = info.ButtonID; + public Mobile From { get; } - switch (bid) + public DuelContext Context { get; } + + public Participant Participant { get; } + + public string Center(string text) => $"
{text}
"; + + public void AddGoldenButton(int x, int y, int bid) { - case 0: - { - DuelContextGump.DisplayTo(from, Context); - return; - } - case 1 when Participant.Count < 8: + AddButton(x, y, 0xD2, 0xD2, bid); + AddButton(x + 3, y + 3, 0xD8, 0xD8, bid); + } + + public void AddGoldenButtonLabeled(int x, int y, int bid, string text) + { + AddGoldenButton(x, y, bid); + AddHtml(x + 25, y, 200, 20, text); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (!Context.Registered) + { + return; + } + + var bid = info.ButtonID; + + if (bid == 0) + { + From.SendGump(new DuelContextGump(From, Context)); + } + else if (bid == 1) + { + if (Participant.Count < 8) { Participant.Resize(Participant.Count + 1); - break; } - case 1: + else { - from.SendMessage("You may not raise the team size any further."); - break; + From.SendMessage("You may not raise the team size any further."); } - case 2 when Participant.Count > 1 && Participant.Count > Participant.FilledSlots: + + From.SendGump(new ParticipantGump(From, Context, Participant)); + } + else if (bid == 2) + { + if (Participant.Count > 1 && Participant.Count > Participant.FilledSlots) { Participant.Resize(Participant.Count - 1); - break; } - case 2: + else { - from.SendMessage("You may not lower the team size any further."); - break; + From.SendMessage("You may not lower the team size any further."); } - case 3 when Participant.FilledSlots > 0: + + From.SendGump(new ParticipantGump(From, Context, Participant)); + } + else if (bid == 3) + { + if (Participant.FilledSlots > 0) { - from.SendMessage("There is at least one currently active player. You must remove them first."); - break; + From.SendMessage("There is at least one currently active player. You must remove them first."); + From.SendGump(new ParticipantGump(From, Context, Participant)); } - case 3 when Context.Participants.Count > 2: + else if (Context.Participants.Count > 2) { + /*Container cont = m_Participant.Stakes; + + if (cont != null) + cont.Delete();*/ + Context.Participants.Remove(Participant); - DuelContextGump.DisplayTo(from, Context); - return; + From.SendGump(new DuelContextGump(From, Context)); } - case 3: + else { - from.SendMessage("Duels must have at least two participating parties."); - break; + From.SendMessage("Duels must have at least two participating parties."); + From.SendGump(new ParticipantGump(From, Context, Participant)); } - default: - { - bid -= 5; + } + /*else if (bid == 4) + { + m_From.SendGump( new ParticipantGump( m_From, m_Context, m_Participant ) ); - if (bid >= 0 && bid < Participant.Players.Length) + Container cont = m_Participant.Stakes; + + if (cont != null && !cont.Deleted) + { + cont.DisplayTo( m_From ); + + Item[] checks = cont.FindItemsByType( typeof( BankCheck ) ); + + int gold = cont.TotalGold; + + for ( int i = 0; i < checks.Length; ++i ) + gold += ((BankCheck)checks[i]).Worth; + + m_From.SendMessage( "This container has {0} item{1} and {2} stone{3}. In gold or check form there is a total of {4:D}gp.", cont.TotalItems, cont.TotalItems==1?"":"s", cont.TotalWeight, cont.TotalWeight==1?"":"s", gold ); + } + }*/ + else + { + bid -= 5; + + if (bid >= 0 && bid < Participant.Players.Length) + { + if (Participant.Players[bid] == null) + { + From.Target = new ParticipantTarget(Context, Participant, bid); + From.SendMessage("Target a player."); + } + else { - if (Participant.Players[bid] == null) - { - from.Target = new ParticipantTarget(Context, Participant, bid); - from.SendMessage("Target a player."); - return; - } - Participant.Players[bid].Mobile.SendMessage("You have been removed from the duel."); - if (Participant.Players[bid].Mobile is PlayerMobile playerMobile) + if (Participant.Players[bid].Mobile is PlayerMobile) { - playerMobile.DuelPlayer = null; + ((PlayerMobile)Participant.Players[bid].Mobile).DuelPlayer = null; } Participant.Players[bid] = null; - from.SendMessage("They have been removed from the duel."); + From.SendMessage("They have been removed from the duel."); + From.SendGump(new ParticipantGump(From, Context, Participant)); + } + } + } + } + + private class ParticipantTarget : Target + { + private readonly DuelContext m_Context; + private readonly int m_Index; + private readonly Participant m_Participant; + + public ParticipantTarget(DuelContext context, Participant p, int index) : base(12, false, TargetFlags.None) + { + m_Context = context; + m_Participant = p; + m_Index = index; + } + + protected override void OnTarget(Mobile from, object targeted) + { + if (!m_Context.Registered) + { + return; + } + + var index = m_Index; + + if (index < 0 || index >= m_Participant.Players.Length) + { + return; + } + + if (targeted is not Mobile mob) + { + from.SendMessage("That is not a player."); + } + else if (!mob.Player) + { + if (mob.Body.IsHuman) + { + mob.SayTo(from, 1005443); // Nay, I would rather stay here and watch a nail rust. } else + { + mob.SayTo(from, 1005444); // The creature ignores your offer. + } + } + else if (AcceptDuelGump.IsIgnored(mob, from) || mob.Blessed) + { + from.SendMessage("They ignore your offer."); + } + else + { + if (mob is not PlayerMobile pm) { return; } - break; - } - } + if (pm.DuelContext != null) + { + from.SendMessage($"{pm.Name} cannot fight because they are already assigned to another duel."); + } + else if (DuelContext.CheckCombat(pm)) + { + from.SendMessage( + $"{pm.Name} cannot fight because they have recently been in combat with another player." + ); + } + else if (mob.HasGump()) + { + from.SendMessage($"{mob.Name} has already been offered a duel."); + } + else + { + if (m_Participant.Find(from) == null) + { + from.SendMessage($"You send a challenge to {mob.Name}."); + } + else + { + from.SendMessage($"You send an invitation to {mob.Name}."); + } - from.SendGump(this); // refresh-via-this - } - - private class ParticipantTarget : Target - { - private readonly DuelContext _context; - private readonly int _index; - private readonly Participant _participant; - - public ParticipantTarget(DuelContext context, Participant p, int index) : base(12, false, TargetFlags.None) - { - _context = context; - _participant = p; - _index = index; - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (!_context.Registered) - { - return; - } - - if (_index < 0 || _index >= _participant.Players.Length) - { - return; - } - - if (targeted is not Mobile mob) - { - from.SendMessage("That is not a player."); - } - else if (!mob.Player) - { - if (mob.Body.IsHuman) - { - mob.SayTo(from, 1005443); // Nay, I would rather stay here and watch a nail rust. - } - else - { - mob.SayTo(from, 1005444); // The creature ignores your offer. + mob.SendGump(new AcceptDuelGump(from, mob, m_Context, m_Participant, m_Index)); + } } } - else if (AcceptDuelGump.IsIgnored(mob, from) || mob.Blessed) - { - from.SendMessage("They ignore your offer."); - } - else if (mob is not PlayerMobile pm) - { - } - else if (pm.DuelContext != null) - { - from.SendMessage($"{pm.Name} cannot fight because they are already assigned to another duel."); - } - else if (DuelContext.CheckCombat(pm)) - { - from.SendMessage( - $"{pm.Name} cannot fight because they have recently been in combat with another player." - ); - } - else if (mob.HasGump()) - { - from.SendMessage($"{mob.Name} has already been offered a duel."); - } - else - { - if (_participant.Find(from) == null) - { - from.SendMessage($"You send a challenge to {mob.Name}."); - } - else - { - from.SendMessage($"You send an invitation to {mob.Name}."); - } - AcceptDuelGump.DisplayTo(from, mob, _context, _participant, _index); + protected override void OnTargetFinish(Mobile from) + { + from.SendGump(new ParticipantGump(from, m_Context, m_Participant)); } } - - protected override void OnTargetFinish(Mobile from) => DisplayTo(from, _context, _participant); } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/PickRulesetGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/PickRulesetGump.cs index 9f3638611..81e3d3d11 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/PickRulesetGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/PickRulesetGump.cs @@ -1,154 +1,141 @@ using Server.Gumps; using Server.Network; -namespace Server.Engines.ConPVP; - -public class PickRulesetGump : DynamicGump +namespace Server.Engines.ConPVP { - private readonly DuelContext _context; - private readonly Ruleset[] _defaults; - private readonly Ruleset[] _flavors; - private readonly Ruleset _ruleset; - - public override bool Singleton => true; - - private PickRulesetGump(DuelContext context, Ruleset ruleset) : base(50, 50) + public class PickRulesetGump : Gump { - _context = context; - _ruleset = ruleset; - _defaults = ruleset.Layout.Defaults; - _flavors = ruleset.Layout.Flavors; - } + private readonly DuelContext m_Context; + private readonly Ruleset[] m_Defaults; + private readonly Ruleset[] m_Flavors; + private readonly Mobile m_From; + private readonly Ruleset m_Ruleset; - public static void DisplayTo(Mobile from, DuelContext context, Ruleset ruleset) - { - if (from?.NetState == null || ruleset == null) + public PickRulesetGump(Mobile from, DuelContext context, Ruleset ruleset) : base(50, 50) { - return; - } + m_From = from; + m_Context = context; + m_Ruleset = ruleset; + m_Defaults = ruleset.Layout.Defaults; + m_Flavors = ruleset.Layout.Flavors; - from.SendGump(new PickRulesetGump(context, ruleset)); - } + var height = 25 + 20 + (m_Defaults.Length + 1) * 22 + 6 + 20 + m_Flavors.Length * 22 + 25; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var height = 25 + 20 + (_defaults.Length + 1) * 22 + 6 + 20 + _flavors.Length * 22 + 25; + AddPage(0); - builder.AddPage(); + AddBackground(0, 0, 260, height, 9250); + AddBackground(10, 10, 240, height - 20, 0xDAC); - builder.AddBackground(0, 0, 260, height, 9250); - builder.AddBackground(10, 10, 240, height - 20, 0xDAC); + AddHtml(35, 25, 190, 20, Center("Rules")); - builder.AddHtml(35, 25, 190, 20, Center("Rules")); + var y = 25 + 20; - var y = 25 + 20; - - for (var i = 0; i < _defaults.Length; ++i) - { - var cur = _defaults[i]; - - builder.AddHtml(35 + 14, y, 176, 20, cur.Title); - - if (_ruleset.Base == cur && !_ruleset.Changed) + for (var i = 0; i < m_Defaults.Length; ++i) { - builder.AddImage(35, y + 4, 0x939); - } - else if (_ruleset.Base == cur) - { - builder.AddButton(35, y + 4, 0x93A, 0x939, 2 + i); - } - else - { - builder.AddButton(35, y + 4, 0x938, 0x939, 2 + i); + var cur = m_Defaults[i]; + + AddHtml(35 + 14, y, 176, 20, cur.Title); + + if (ruleset.Base == cur && !ruleset.Changed) + { + AddImage(35, y + 4, 0x939); + } + else if (ruleset.Base == cur) + { + AddButton(35, y + 4, 0x93A, 0x939, 2 + i); + } + else + { + AddButton(35, y + 4, 0x938, 0x939, 2 + i); + } + + y += 22; } + AddHtml(35 + 14, y, 176, 20, "Custom"); + AddButton(35, y + 4, ruleset.Changed ? 0x939 : 0x938, 0x939, 1); + y += 22; + y += 6; + + AddHtml(35, y, 190, 20, Center("Flavors")); + y += 20; + + for (var i = 0; i < m_Flavors.Length; ++i) + { + var cur = m_Flavors[i]; + + AddHtml(35 + 14, y, 176, 20, cur.Title); + + if (ruleset.Flavors.Contains(cur)) + { + AddButton(35, y + 4, 0x939, 0x938, 2 + m_Defaults.Length + i); + } + else + { + AddButton(35, y + 4, 0x938, 0x939, 2 + m_Defaults.Length + i); + } + + y += 22; + } } - builder.AddHtml(35 + 14, y, 176, 20, "Custom"); - builder.AddButton(35, y + 4, _ruleset.Changed ? 0x939 : 0x938, 0x939, 1); + public string Center(string text) => $"
{text}
"; - y += 22; - y += 6; - - builder.AddHtml(35, y, 190, 20, Center("Flavors")); - y += 20; - - for (var i = 0; i < _flavors.Length; ++i) + public override void OnResponse(NetState sender, in RelayInfo info) { - var cur = _flavors[i]; - - builder.AddHtml(35 + 14, y, 176, 20, cur.Title); - - if (_ruleset.Flavors.Contains(cur)) + if (m_Context?.Registered == false) { - builder.AddButton(35, y + 4, 0x939, 0x938, 2 + _defaults.Length + i); - } - else - { - builder.AddButton(35, y + 4, 0x938, 0x939, 2 + _defaults.Length + i); + return; } - y += 22; - } - } - - private static string Center(string text) => $"
{text}
"; - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (_context?.Registered == false) - { - return; - } - - var from = sender.Mobile; - switch (info.ButtonID) - { - case 0: // closed - { - if (_context != null) + switch (info.ButtonID) + { + case 0: // closed { - DuelContextGump.DisplayTo(from, _context); - } - - break; - } - case 1: // customize - { - RulesetGump.DisplayTo(from, _ruleset, _ruleset.Layout, _context); - break; - } - default: - { - var idx = info.ButtonID - 2; - - if (idx >= 0 && idx < _defaults.Length) - { - _ruleset.ApplyDefault(_defaults[idx]); - from.SendGump(this); // refresh-via-this - } - else - { - idx -= _defaults.Length; - - if (idx >= 0 && idx < _flavors.Length) + if (m_Context != null) { - if (_ruleset.Flavors.Contains(_flavors[idx])) - { - _ruleset.RemoveFlavor(_flavors[idx]); - } - else - { - _ruleset.AddFlavor(_flavors[idx]); - } - - from.SendGump(this); // refresh-via-this + m_From.SendGump(new DuelContextGump(m_From, m_Context)); } - } - break; - } + break; + } + case 1: // customize + { + m_From.SendGump(new RulesetGump(m_From, m_Ruleset, m_Ruleset.Layout, m_Context)); + break; + } + default: + { + var idx = info.ButtonID - 2; + + if (idx >= 0 && idx < m_Defaults.Length) + { + m_Ruleset.ApplyDefault(m_Defaults[idx]); + m_From.SendGump(new PickRulesetGump(m_From, m_Context, m_Ruleset)); + } + else + { + idx -= m_Defaults.Length; + + if (idx >= 0 && idx < m_Flavors.Length) + { + if (m_Ruleset.Flavors.Contains(m_Flavors[idx])) + { + m_Ruleset.RemoveFlavor(m_Flavors[idx]); + } + else + { + m_Ruleset.AddFlavor(m_Flavors[idx]); + } + + m_From.SendGump(new PickRulesetGump(m_From, m_Context, m_Ruleset)); + } + } + + break; + } + } } } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/ReadyGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/ReadyGump.cs index 6a2d97568..4e8bcf0f1 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/ReadyGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/ReadyGump.cs @@ -1,119 +1,109 @@ using Server.Gumps; using Server.Network; -namespace Server.Engines.ConPVP; - -public class ReadyGump : DynamicGump +namespace Server.Engines.ConPVP { - private readonly DuelContext _context; - private readonly int _count; - - public override bool Singleton => true; - - private ReadyGump(DuelContext context, int count) : base(50, 50) + public class ReadyGump : Gump { - _context = context; - _count = count; - } + private DuelContext m_Context; + private int m_Count; + private Mobile m_From; - public static void DisplayTo(Mobile from, DuelContext context, int count) - { - if (from?.NetState == null || context == null) + public ReadyGump(Mobile from, DuelContext context, int count) : base(50, 50) { - return; - } + m_From = from; + m_Context = context; + m_Count = count; - from.SendGump(new ReadyGump(context, count), true); - } + var parts = context.Participants; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); - builder.SetNoMove(); + var height = 25 + 20; - builder.AddPage(); - - var parts = _context.Participants; - - var height = 25 + 20; - - for (var i = 0; i < parts.Count; ++i) - { - var p = parts[i]; - - height += 4; - - if (p.Players.Length > 1) + for (var i = 0; i < parts.Count; ++i) { - height += 22; - } + var p = parts[i]; - height += p.Players.Length * 22; - } + height += 4; - height += 25; - - builder.AddBackground(0, 0, 260, height, 9250); - builder.AddBackground(10, 10, 240, height - 20, 0xDAC); - - if (_count == -1) - { - builder.AddHtml(35, 25, 190, 20, "Ready".Center()); - } - else - { - builder.AddHtml(35, 25, 190, 20, "Starting".Center()); - builder.AddHtml(35, 25, 190, 20, $"
{_count}"); - } - - var y = 25 + 20; - - for (var i = 0; i < parts.Count; ++i) - { - var p = parts[i]; - - y += 4; - - var isAllReady = true; - var yStore = y; - var offset = 0; - - if (p.Players.Length > 1) - { - builder.AddHtml(35 + 14, y, 176, 20, $"Participant #{i + 1}"); - y += 22; - offset = 10; - } - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - if (pl?.Ready == true) + if (p.Players.Length > 1) { - builder.AddImage(35 + offset, y + 4, 0x939); - } - else - { - builder.AddImage(35 + offset, y + 4, 0x938); - isAllReady = false; + height += 22; } - var name = pl == null ? "(Empty)" : pl.Mobile.Name; - - builder.AddHtml(35 + offset + 14, y, 166, 20, name); - - y += 22; + height += p.Players.Length * 22; } - if (p.Players.Length > 1) + height += 25; + + Closable = false; + Draggable = false; + + AddPage(0); + + AddBackground(0, 0, 260, height, 9250); + AddBackground(10, 10, 240, height - 20, 0xDAC); + + if (count == -1) { - builder.AddImage(35, yStore + 4, isAllReady ? 0x939 : 0x938); + AddHtml(35, 25, 190, 20, Center("Ready")); + } + else + { + AddHtml(35, 25, 190, 20, Center("Starting")); + AddHtml(35, 25, 190, 20, $"
{count}"); + } + + var y = 25 + 20; + + for (var i = 0; i < parts.Count; ++i) + { + var p = parts[i]; + + y += 4; + + var isAllReady = true; + var yStore = y; + var offset = 0; + + if (p.Players.Length > 1) + { + AddHtml(35 + 14, y, 176, 20, $"Participant #{i + 1}"); + y += 22; + offset = 10; + } + + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + if (pl?.Ready == true) + { + AddImage(35 + offset, y + 4, 0x939); + } + else + { + AddImage(35 + offset, y + 4, 0x938); + isAllReady = false; + } + + var name = pl == null ? "(Empty)" : pl.Mobile.Name; + + AddHtml(35 + offset + 14, y, 166, 20, name); + + y += 22; + } + + if (p.Players.Length > 1) + { + AddImage(35, yStore + 4, isAllReady ? 0x939 : 0x938); + } } } - } - public override void OnResponse(NetState sender, in RelayInfo info) - { + public string Center(string text) => $"
{text}
"; + + public override void OnResponse(NetState sender, in RelayInfo info) + { + } } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/ReadyUpGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/ReadyUpGump.cs index c640946d4..e11fa00f5 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/ReadyUpGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/ReadyUpGump.cs @@ -3,239 +3,235 @@ using Server.Gumps; using Server.Mobiles; using Server.Network; -namespace Server.Engines.ConPVP; - -public class ReadyUpGump : DynamicGump +namespace Server.Engines.ConPVP { - private readonly DuelContext _context; - - public override bool Singleton => true; - - private ReadyUpGump(DuelContext context) : base(50, 50) => _context = context; - - public static void DisplayTo(Mobile from, DuelContext context) + public class ReadyUpGump : Gump { - if (from?.NetState == null || context == null) + private readonly DuelContext m_Context; + private readonly Mobile m_From; + + public ReadyUpGump(Mobile from, DuelContext context) : base(50, 50) { - return; - } + m_From = from; + m_Context = context; - from.SendGump(new ReadyUpGump(context), true); - } + Closable = false; + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); - builder.AddPage(); - - if (_context.Rematch) - { - var height = 25 + 20 + 10 + 22 + 25; - - builder.AddBackground(0, 0, 210, height, 9250); - builder.AddBackground(10, 10, 190, height - 20, 0xDAC); - - builder.AddHtml(35, 25, 140, 20, "Rematch?".Center()); - - builder.AddButton(35, 55, 247, 248, 1); - builder.AddButton(115, 55, 242, 241, 2); - } - else - { - builder.AddPage(1); - - var parts = _context.Participants; - - var height = 25 + 20; - - for (var i = 0; i < parts.Count; ++i) + if (context.Rematch) { - var p = parts[i]; + var height = 25 + 20 + 10 + 22 + 25; - height += 4; + AddBackground(0, 0, 210, height, 9250); + AddBackground(10, 10, 190, height - 20, 0xDAC); - if (p.Players.Length > 1) - { - height += 22; - } + AddHtml(35, 25, 140, 20, Center("Rematch?")); - height += p.Players.Length * 22; - } - - height += 10 + 22 + 25; - - builder.AddBackground(0, 0, 260, height, 9250); - builder.AddBackground(10, 10, 240, height - 20, 0xDAC); - - builder.AddHtml(35, 25, 190, 20, "Participants".Center()); - - var y = 20 + 25; - - for (var i = 0; i < parts.Count; ++i) - { - var p = parts[i]; - - y += 4; - - var offset = 0; - - if (p.Players.Length > 1) - { - builder.AddHtml(35, y, 176, 20, $"Team #{i + 1}"); - y += 22; - offset = 10; - } - - for (var j = 0; j < p.Players.Length; ++j) - { - var pl = p.Players[j]; - - var name = pl == null ? "(Empty)" : pl.Mobile.Name; - - builder.AddHtml(35 + offset, y, 166, 20, name); - - y += 22; - } - } - - y += 8; - - builder.AddHtml(35, y, 176, 20, "Continue?"); - - y -= 2; - - builder.AddButton(102, y, 247, 248, 0, GumpButtonType.Page, 2); - builder.AddButton(169, y, 242, 241, 2); - - builder.AddPage(2); - - var ruleset = _context.Ruleset; - var basedef = ruleset.Base; - - height = 25 + 20 + 5 + 20 + 20 + 4; - - var changes = 0; - - BitArray defs; - - if (ruleset.Flavors.Count > 0) - { - defs = new BitArray(basedef.Options); - - for (var i = 0; i < ruleset.Flavors.Count; ++i) - { - defs.Or(ruleset.Flavors[i].Options); - } - - height += ruleset.Flavors.Count * 18; + AddButton(35, 55, 247, 248, 1); + AddButton(115, 55, 242, 241, 2); } else { - defs = basedef.Options; - } + AddPage(1); - var opts = ruleset.Options; + var parts = context.Participants; - for (var i = 0; i < opts.Length; ++i) - { - if (defs[i] != opts[i]) + var height = 25 + 20; + + for (var i = 0; i < parts.Count; ++i) { - ++changes; + var p = parts[i]; + + height += 4; + + if (p.Players.Length > 1) + { + height += 22; + } + + height += p.Players.Length * 22; } - } - height += changes * 22; + height += 10 + 22 + 25; - height += 10 + 22 + 25; + AddBackground(0, 0, 260, height, 9250); + AddBackground(10, 10, 240, height - 20, 0xDAC); - builder.AddBackground(0, 0, 260, height, 9250); - builder.AddBackground(10, 10, 240, height - 20, 0xDAC); + AddHtml(35, 25, 190, 20, Center("Participants")); - builder.AddHtml(35, 25, 190, 20, "Rules".Center()); + var y = 20 + 25; - builder.AddHtml(35, 50, 190, 20, $"Set: {basedef.Title}"); + for (var i = 0; i < parts.Count; ++i) + { + var p = parts[i]; - y = 70; + y += 4; - for (var i = 0; i < ruleset.Flavors.Count; ++i, y += 18) - { - builder.AddHtml(35, y, 190, 20, $" + {ruleset.Flavors[i].Title}"); - } + var offset = 0; - y += 4; + if (p.Players.Length > 1) + { + AddHtml(35, y, 176, 20, $"Team #{i + 1}"); + y += 22; + offset = 10; + } - if (changes > 0) - { - builder.AddHtml(35, y, 190, 20, "Modifications:"); - y += 20; + for (var j = 0; j < p.Players.Length; ++j) + { + var pl = p.Players[j]; + + var name = pl == null ? "(Empty)" : pl.Mobile.Name; + + AddHtml(35 + offset, y, 166, 20, name); + + y += 22; + } + } + + y += 8; + + AddHtml(35, y, 176, 20, "Continue?"); + + y -= 2; + + AddButton(102, y, 247, 248, 0, GumpButtonType.Page, 2); + AddButton(169, y, 242, 241, 2); + + AddPage(2); + + var ruleset = context.Ruleset; + var basedef = ruleset.Base; + + height = 25 + 20 + 5 + 20 + 20 + 4; + + var changes = 0; + + BitArray defs; + + if (ruleset.Flavors.Count > 0) + { + defs = new BitArray(basedef.Options); + + for (var i = 0; i < ruleset.Flavors.Count; ++i) + { + defs.Or(ruleset.Flavors[i].Options); + } + + height += ruleset.Flavors.Count * 18; + } + else + { + defs = basedef.Options; + } + + var opts = ruleset.Options; for (var i = 0; i < opts.Length; ++i) { if (defs[i] != opts[i]) { - var name = ruleset.Layout.FindByIndex(i); + ++changes; + } + } - if (name != null) // sanity + height += changes * 22; + + height += 10 + 22 + 25; + + AddBackground(0, 0, 260, height, 9250); + AddBackground(10, 10, 240, height - 20, 0xDAC); + + AddHtml(35, 25, 190, 20, Center("Rules")); + + AddHtml(35, 50, 190, 20, $"Set: {basedef.Title}"); + + y = 70; + + for (var i = 0; i < ruleset.Flavors.Count; ++i, y += 18) + { + AddHtml(35, y, 190, 20, $" + {ruleset.Flavors[i].Title}"); + } + + y += 4; + + if (changes > 0) + { + AddHtml(35, y, 190, 20, "Modifications:"); + y += 20; + + for (var i = 0; i < opts.Length; ++i) + { + if (defs[i] != opts[i]) { - builder.AddImage(35, y, opts[i] ? 0xD3 : 0xD2); - builder.AddHtml(60, y, 165, 22, name); + var name = ruleset.Layout.FindByIndex(i); + + if (name != null) // sanity + { + AddImage(35, y, opts[i] ? 0xD3 : 0xD2); + AddHtml(60, y, 165, 22, name); + } + + y += 22; + } + } + } + else + { + AddHtml(35, y, 190, 20, "Modifications: None"); + y += 20; + } + + y += 8; + + AddHtml(35, y, 176, 20, "Continue?"); + + y -= 2; + + AddButton(102, y, 247, 248, 1); + AddButton(169, y, 242, 241, 3); + } + } + + public string Center(string text) => $"
{text}
"; + + public void AddGoldenButton(int x, int y, int bid) + { + AddButton(x, y, 0xD2, 0xD2, bid); + AddButton(x + 3, y + 3, 0xD8, 0xD8, bid); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (!m_Context.Registered || !m_Context.ReadyWait) + { + return; + } + + switch (info.ButtonID) + { + case 1: // okay + { + if (m_From is not PlayerMobile pm) + { + break; } - y += 22; - } - } - } - else - { - builder.AddHtml(35, y, 190, 20, "Modifications: None"); - y += 20; - } + pm.DuelPlayer.Ready = true; + m_Context.SendReadyGump(); - y += 8; - - builder.AddHtml(35, y, 176, 20, "Continue?"); - - y -= 2; - - builder.AddButton(102, y, 247, 248, 1); - builder.AddButton(169, y, 242, 241, 3); - } - } - - private static string Center(string text) => $"
{text}
"; - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (!_context.Registered || !_context.ReadyWait) - { - return; - } - - var from = sender.Mobile; - switch (info.ButtonID) - { - case 1: // okay - { - if (from is not PlayerMobile pm) - { break; } - - pm.DuelPlayer.Ready = true; - _context.SendReadyGump(); - - break; - } - case 2: // reject participants - { - _context.RejectReady(from, "participants"); - break; - } - case 3: // reject rules - { - _context.RejectReady(from, "rules"); - break; - } + case 2: // reject participants + { + m_Context.RejectReady(m_From, "participants"); + break; + } + case 3: // reject rules + { + m_Context.RejectReady(m_From, "rules"); + break; + } + } } } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/RulesetGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/RulesetGump.cs index 22b1eb995..96397455e 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/RulesetGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/RulesetGump.cs @@ -2,156 +2,143 @@ using System.Collections; using Server.Gumps; using Server.Network; -namespace Server.Engines.ConPVP; - -public class RulesetGump : DynamicGump +namespace Server.Engines.ConPVP { - private readonly DuelContext _duelContext; - private readonly RulesetLayout _page; - private readonly bool _readOnly; - private readonly Ruleset _ruleset; - - public override bool Singleton => true; - - private RulesetGump(Ruleset ruleset, RulesetLayout page, DuelContext duelContext, bool readOnly) - : base(readOnly ? 310 : 50, 50) + public class RulesetGump : Gump { - _ruleset = ruleset; - _page = page; - _duelContext = duelContext; - _readOnly = readOnly; - } + private readonly DuelContext m_DuelContext; + private readonly Mobile m_From; + private readonly RulesetLayout m_Page; + private readonly bool m_ReadOnly; + private readonly Ruleset m_Ruleset; - public static void DisplayTo( - Mobile from, Ruleset ruleset, RulesetLayout page, DuelContext duelContext, bool readOnly = false - ) - { - if (from?.NetState == null || ruleset == null || page == null) + public override bool Singleton => true; + + public RulesetGump(Mobile from, Ruleset ruleset, RulesetLayout page, DuelContext duelContext, bool readOnly = false) + : base(readOnly ? 310 : 50, 50) { - return; - } + m_From = from; + m_Ruleset = ruleset; + m_Page = page; + m_DuelContext = duelContext; + m_ReadOnly = readOnly; - var gumps = from.GetGumps(); - gumps.Close(); - gumps.Close(); + Draggable = !readOnly; - from.SendGump(new RulesetGump(ruleset, page, duelContext, readOnly)); - } + var gumps = from.GetGumps(); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - if (_readOnly) - { - builder.SetNoMove(); - } + gumps.Close(); + gumps.Close(); - var depthCounter = _page; + var depthCounter = page; - while (depthCounter != null) - { - depthCounter = depthCounter.Parent; - } - - var count = _page.Children.Length + _page.Options.Length; - - builder.AddPage(); - - var height = 35 + 10 + 2 + count * 22 + 2 + 30; - - builder.AddBackground(0, 0, 260, height, 9250); - builder.AddBackground(10, 10, 240, height - 20, 0xDAC); - - builder.AddHtml(35, 25, 190, 20, _page.Title.Center()); - - var x = 35; - var y = 47; - - for (var i = 0; i < _page.Children.Length; ++i) - { - AddGoldenButton(ref builder, x, y, 1 + i); - builder.AddHtml(x + 25, y, 250, 22, _page.Children[i].Title); - - y += 22; - } - - for (var i = 0; i < _page.Options.Length; ++i) - { - var enabled = _ruleset.Options[_page.Offset + i]; - - if (_readOnly) + while (depthCounter != null) { - builder.AddImage(x, y, enabled ? 0xD3 : 0xD2); + depthCounter = depthCounter.Parent; + } + + var count = page.Children.Length + page.Options.Length; + + AddPage(0); + + var height = 35 + 10 + 2 + count * 22 + 2 + 30; + + AddBackground(0, 0, 260, height, 9250); + AddBackground(10, 10, 240, height - 20, 0xDAC); + + AddHtml(35, 25, 190, 20, Center(page.Title)); + + var x = 35; + var y = 47; + + for (var i = 0; i < page.Children.Length; ++i) + { + AddGoldenButton(x, y, 1 + i); + AddHtml(x + 25, y, 250, 22, page.Children[i].Title); + + y += 22; + } + + for (var i = 0; i < page.Options.Length; ++i) + { + var enabled = ruleset.Options[page.Offset + i]; + + if (readOnly) + { + AddImage(x, y, enabled ? 0xD3 : 0xD2); + } + else + { + AddCheck(x, y, 0xD2, 0xD3, enabled, i); + } + + AddHtml(x + 25, y, 250, 22, page.Options[i]); + + y += 22; + } + } + + public string Center(string text) => $"
{text}
"; + + public void AddGoldenButton(int x, int y, int bid) + { + AddButton(x, y, 0xD2, 0xD2, bid); + AddButton(x + 3, y + 3, 0xD8, 0xD8, bid); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (m_DuelContext?.Registered == false) + { + return; + } + + if (!m_ReadOnly) + { + var opts = new BitArray(m_Page.Options.Length); + + for (var i = 0; i < info.Switches.Length; ++i) + { + var sid = info.Switches[i]; + + if (sid >= 0 && sid < m_Page.Options.Length) + { + opts[sid] = true; + } + } + + for (var i = 0; i < opts.Length; ++i) + { + if (m_Ruleset.Options[m_Page.Offset + i] != opts[i]) + { + m_Ruleset.Options[m_Page.Offset + i] = opts[i]; + m_Ruleset.Changed = true; + } + } + } + + var bid = info.ButtonID; + + if (bid == 0) + { + if (m_Page.Parent != null) + { + m_From.SendGump(new RulesetGump(m_From, m_Ruleset, m_Page.Parent, m_DuelContext, m_ReadOnly)); + } + else if (!m_ReadOnly) + { + m_From.SendGump(new PickRulesetGump(m_From, m_DuelContext, m_Ruleset)); + } } else { - builder.AddCheckbox(x, y, 0xD2, 0xD3, enabled, i); - } + bid -= 1; - builder.AddHtml(x + 25, y, 250, 22, _page.Options[i]); - - y += 22; - } - } - - private static void AddGoldenButton(ref DynamicGumpBuilder builder, int x, int y, int bid) - { - builder.AddButton(x, y, 0xD2, 0xD2, bid); - builder.AddButton(x + 3, y + 3, 0xD8, 0xD8, bid); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (_duelContext?.Registered == false) - { - return; - } - - if (!_readOnly) - { - var opts = new BitArray(_page.Options.Length); - - for (var i = 0; i < info.Switches.Length; ++i) - { - var sid = info.Switches[i]; - - if (sid >= 0 && sid < _page.Options.Length) + if (bid >= 0 && bid < m_Page.Children.Length) { - opts[sid] = true; + m_From.SendGump(new RulesetGump(m_From, m_Ruleset, m_Page.Children[bid], m_DuelContext, m_ReadOnly)); } } - - for (var i = 0; i < opts.Length; ++i) - { - if (_ruleset.Options[_page.Offset + i] != opts[i]) - { - _ruleset.Options[_page.Offset + i] = opts[i]; - _ruleset.Changed = true; - } - } - } - - var from = sender.Mobile; - var bid = info.ButtonID; - - if (bid == 0) - { - if (_page.Parent != null) - { - DisplayTo(from, _ruleset, _page.Parent, _duelContext, _readOnly); - } - else if (!_readOnly) - { - PickRulesetGump.DisplayTo(from, _duelContext, _ruleset); - } - } - else - { - bid -= 1; - - if (bid >= 0 && bid < _page.Children.Length) - { - DisplayTo(from, _ruleset, _page.Children[bid], _duelContext, _readOnly); - } } } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs index 13dc1d7ba..ae7d3daac 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs @@ -1,531 +1,633 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Text; using Server.Gumps; using Server.Mobiles; using Server.Network; -using Server.Text; -namespace Server.Engines.ConPVP; - -public enum TourneyBracketGumpType +namespace Server.Engines.ConPVP { - Index, - Rules_Info, - Participant_List, - Participant_Info, - Round_List, - Round_Info, - Match_Info, - Player_Info -} - -public class TournamentBracketGump : DynamicGump -{ - private const int BlackColor32 = 0x000008; - private List _list; - private object _object; - private int _page; - private readonly Tournament _tournament; - private TourneyBracketGumpType _type; - private int _perPage; - - public override bool Singleton => true; - - private TournamentBracketGump(Tournament tourney, TourneyBracketGumpType type, List list, int page, object obj) - : base(50, 50) + public enum TourneyBracketGumpType { - _tournament = tourney; - _type = type; - _list = list; - _page = page; - _object = obj; - _perPage = 12; + Index, + Rules_Info, + Participant_List, + Participant_Info, + Round_List, + Round_Info, + Match_Info, + Player_Info } - public static void DisplayTo( - Mobile from, Tournament tourney, TourneyBracketGumpType type, - List list = null, int page = 0, object obj = null - ) + public class TournamentBracketGump : Gump { - if (from?.NetState == null || tourney == null) + private const int BlackColor32 = 0x000008; + private readonly Mobile m_From; + private List m_List; + private readonly object m_Object; + private readonly int m_Page; + private readonly Tournament m_Tournament; + private readonly TourneyBracketGumpType m_Type; + private int m_PerPage; + + public TournamentBracketGump( + Mobile from, Tournament tourney, TourneyBracketGumpType type, + List list = null, int page = 0, object obj = null + ) : base(50, 50) { - return; - } + m_From = from; + m_Tournament = tourney; + m_Type = type; + m_List = list; + m_Page = page; + m_Object = obj; + m_PerPage = 12; - from.SendGump(new TournamentBracketGump(tourney, type, list, page, obj), true); - } + switch (type) + { + case TourneyBracketGumpType.Index: + { + AddPage(0); + AddBackground(0, 0, 300, 300, 9380); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - _perPage = 12; + var sb = new StringBuilder(); - switch (_type) - { - case TourneyBracketGumpType.Index: - { - builder.AddPage(); - builder.AddBackground(0, 0, 300, 300, 9380); - - using var sb = ValueStringBuilder.Create(128); - - if (_tournament.TourneyType == TourneyType.FreeForAll) - { - sb.Append("FFA"); - } - else if (_tournament.TourneyType == TourneyType.RandomTeam) - { - sb.Append($"{_tournament.ParticipantsPerMatch}-Team"); - } - else if (_tournament.TourneyType == TourneyType.RedVsBlue) - { - sb.Append("Red v Blue"); - } - else if (_tournament.TourneyType == TourneyType.Faction) - { - sb.Append($"{_tournament.ParticipantsPerMatch}-Team Faction"); - } - else - { - for (var i = 0; i < _tournament.ParticipantsPerMatch; ++i) + if (tourney.TourneyType == TourneyType.FreeForAll) { - if (sb.Length > 0) - { - sb.Append('v'); - } - - sb.Append(_tournament.PlayersPerParticipant); + sb.Append("FFA"); } - } - - if (_tournament.EventController != null) - { - sb.Append($" {_tournament.EventController.Title}"); - } - - sb.Append(" Tournament Bracket"); - - builder.AddHtml(25, 35, 250, 20, sb.AsSpan().Center()); - - AddRightArrow(ref builder, 25, 53, ToButtonID(0, 4), "Rules"); - AddRightArrow(ref builder, 25, 71, ToButtonID(0, 1), "Participants"); - - if (_tournament.Stage == TournamentStage.Signup) - { - var until = _tournament.SignupStart + _tournament.SignupPeriod - Core.Now; - string text; - var secs = (int)Math.Round(until.TotalSeconds); - - if (secs > 0) + else if (tourney.TourneyType == TourneyType.RandomTeam) { - var mins = Math.DivRem(secs, 60, out secs); + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-Team"); + } + else if (tourney.TourneyType == TourneyType.RedVsBlue) + { + sb.Append("Red v Blue"); + } + else if (tourney.TourneyType == TourneyType.Faction) + { + sb.Append(tourney.ParticipantsPerMatch); + sb.Append("-Team Faction"); + } + else + { + for (var i = 0; i < tourney.ParticipantsPerMatch; ++i) + { + if (sb.Length > 0) + { + sb.Append('v'); + } - if (mins > 0 && secs > 0) - { - text = - $"The tournament will begin in {mins} minute{(mins == 1 ? "" : "s")} and {secs} second{(secs == 1 ? "" : "s")}."; + sb.Append(tourney.PlayersPerParticipant); } - else if (mins > 0) + } + + if (tourney.EventController != null) + { + sb.Append(' ').Append(tourney.EventController.Title); + } + + sb.Append(" Tournament Bracket"); + + AddHtml(25, 35, 250, 20, sb.ToString().Center()); + + AddRightArrow(25, 53, ToButtonID(0, 4), "Rules"); + AddRightArrow(25, 71, ToButtonID(0, 1), "Participants"); + + if (m_Tournament.Stage == TournamentStage.Signup) + { + var until = m_Tournament.SignupStart + m_Tournament.SignupPeriod - Core.Now; + string text; + var secs = (int)Math.Round(until.TotalSeconds); + + if (secs > 0) { - text = $"The tournament will begin in {mins} minute{(mins == 1 ? "" : "s")}."; - } - else if (secs > 0) - { - text = $"The tournament will begin in {secs} second{(secs == 1 ? "" : "s")}."; + var mins = Math.DivRem(secs, 60, out secs); + + if (mins > 0 && secs > 0) + { + text = + $"The tournament will begin in {mins} minute{(mins == 1 ? "" : "s")} and {secs} second{(secs == 1 ? "" : "s")}."; + } + else if (mins > 0) + { + text = $"The tournament will begin in {mins} minute{(mins == 1 ? "" : "s")}."; + } + else if (secs > 0) + { + text = $"The tournament will begin in {secs} second{(secs == 1 ? "" : "s")}."; + } + else + { + text = "The tournament will begin shortly."; + } } else { text = "The tournament will begin shortly."; } + + AddHtml(25, 92, 250, 40, text); } else { - text = "The tournament will begin shortly."; + AddRightArrow(25, 89, ToButtonID(0, 2), "Rounds"); } - builder.AddHtml(25, 92, 250, 40, text); + break; } - else + case TourneyBracketGumpType.Rules_Info: { - AddRightArrow(ref builder, 25, 89, ToButtonID(0, 2), "Rounds"); - } + var ruleset = tourney.Ruleset; + var basedef = ruleset.Base; - break; - } - case TourneyBracketGumpType.Rules_Info: - { - var ruleset = _tournament.Ruleset; - var basedef = ruleset.Base; + BitArray defs; - BitArray defs; - - if (ruleset.Flavors.Count > 0) - { - defs = new BitArray(basedef.Options); - - for (var i = 0; i < ruleset.Flavors.Count; ++i) + if (ruleset.Flavors.Count > 0) { - defs.Or(ruleset.Flavors[i].Options); + defs = new BitArray(basedef.Options); + + for (var i = 0; i < ruleset.Flavors.Count; ++i) + { + defs.Or(ruleset.Flavors[i].Options); + } } - } - else - { - defs = basedef.Options; - } - - var changes = 0; - - var opts = ruleset.Options; - - for (var i = 0; i < opts.Length; ++i) - { - if (defs[i] != opts[i]) + else { - ++changes; + defs = basedef.Options; } - } - builder.AddPage(); - builder.AddBackground( - 0, - 0, - 300, - 60 + 18 + 20 + 20 + 20 + 8 + 20 + ruleset.Flavors.Count * 18 + 4 + 20 + changes * 22 + 6, - 9380 - ); + var changes = 0; - AddLeftArrow(ref builder, 25, 11, ToButtonID(0, 0)); - builder.AddHtml(25, 35, 250, 20, "Rules".Center()); - - var y = 53; - - var groupText = _tournament.GroupType switch - { - GroupingType.HighVsLow => "High vs Low", - GroupingType.Nearest => "Closest opponent", - GroupingType.Random => "Random", - _ => null - }; - - builder.AddHtml(35, y, 190, 20, $"Grouping: {groupText}"); - y += 20; - - var tieText = _tournament.TieType switch - { - TieType.Random => "Random", - TieType.Highest => "Highest advances", - TieType.Lowest => "Lowest advances", - TieType.FullAdvancement => _tournament.ParticipantsPerMatch == 2 - ? "Both advance" - : "Everyone advances", - TieType.FullElimination => _tournament.ParticipantsPerMatch == 2 - ? "Both eliminated" - : "Everyone eliminated", - _ => null - }; - - builder.AddHtml(35, y, 190, 20, $"Tiebreaker: {tieText}"); - y += 20; - - string sdText; - - if (_tournament.SuddenDeath > TimeSpan.Zero) - { - sdText = _tournament.SuddenDeathRounds > 0 ? - $"Sudden Death: {(int)_tournament.SuddenDeath.TotalMinutes}:{_tournament.SuddenDeath.Seconds:D2} (first {_tournament.SuddenDeathRounds} rounds)" : - $"Sudden Death: {(int)_tournament.SuddenDeath.TotalMinutes}:{_tournament.SuddenDeath.Seconds:D2} (all rounds)"; - } - else - { - sdText = "Sudden Death: Off"; - } - - builder.AddHtml(35, y, 240, 20, sdText); - y += 20; - - y += 8; - - builder.AddHtml(35, y, 190, 20, $"Ruleset: {basedef.Title}"); - y += 20; - - for (var i = 0; i < ruleset.Flavors.Count; ++i, y += 18) - { - builder.AddHtml(35, y, 190, 20, $" + {ruleset.Flavors[i].Title}"); - } - - y += 4; - - if (changes > 0) - { - builder.AddHtml(35, y, 190, 20, "Modifications:"); - y += 20; + var opts = ruleset.Options; for (var i = 0; i < opts.Length; ++i) { if (defs[i] != opts[i]) { - var name = ruleset.Layout.FindByIndex(i); - - if (name != null) // sanity - { - builder.AddImage(35, y, opts[i] ? 0xD3 : 0xD2); - builder.AddHtml(60, y, 165, 22, name); - } - - y += 22; - } - } - } - else - { - builder.AddHtml(35, y, 190, 20, "Modifications: None"); - } - - break; - } - case TourneyBracketGumpType.Participant_List: - { - builder.AddPage(); - builder.AddBackground(0, 0, 300, 300, 9380); - - var pList = _list?.SafeConvertList() - ?? new List(_tournament.Participants); - - AddLeftArrow(ref builder, 25, 11, ToButtonID(0, 0)); - builder.AddHtml(25, 35, 250, 20, Html.Center($"{pList.Count} Participant{(pList.Count == 1 ? "" : "s")}")); - - StartPage(ref builder, out var index, out var count, out var y, 12); - - for (var i = 0; i < count; ++i, y += 18) - { - var part = pList[index + i]; - var name = part.NameList; - - if (_tournament.TourneyType != TourneyType.Standard && part.Players.Count == 1) - { - if (part.Players[0] is PlayerMobile pm && pm.DuelPlayer != null) - { - name = name.Color(pm.DuelPlayer.Eliminated ? 0x663333 : 0x336666); + ++changes; } } - AddRightArrow(ref builder, 25, y, ToButtonID(2, index + i), name); - } + AddPage(0); + AddBackground( + 0, + 0, + 300, + 60 + 18 + 20 + 20 + 20 + 8 + 20 + ruleset.Flavors.Count * 18 + 4 + 20 + changes * 22 + 6, + 9380 + ); - break; - } - case TourneyBracketGumpType.Participant_Info: - { - if (_object is not TourneyParticipant part) - { - break; - } + AddLeftArrow(25, 11, ToButtonID(0, 0)); + AddHtml(25, 35, 250, 20, "Rules".Center()); - builder.AddPage(); - builder.AddBackground(0, 0, 300, 60 + 18 + 20 + part.Players.Count * 18 + 20 + 20 + 160, 9380); + var y = 53; - AddLeftArrow(ref builder, 25, 11, ToButtonID(0, 1)); - builder.AddHtml(25, 35, 250, 20, "Participants".Center()); - - var y = 53; - - builder.AddHtml(25, y, 200, 20, part.Players.Count == 1 ? "Players" : "Team"); - y += 20; - - for (var i = 0; i < part.Players.Count; ++i) - { - var mob = part.Players[i]; - var name = mob.Name; - - if (_tournament.TourneyType != TourneyType.Standard) + var groupText = tourney.GroupType switch { - if (mob is PlayerMobile pm && pm.DuelPlayer != null) + GroupingType.HighVsLow => "High vs Low", + GroupingType.Nearest => "Closest opponent", + GroupingType.Random => "Random", + _ => null + }; + + AddHtml(35, y, 190, 20, $"Grouping: {groupText}"); + y += 20; + + var tieText = tourney.TieType switch + { + TieType.Random => "Random", + TieType.Highest => "Highest advances", + TieType.Lowest => "Lowest advances", + TieType.FullAdvancement => tourney.ParticipantsPerMatch == 2 + ? "Both advance" + : "Everyone advances", + TieType.FullElimination => tourney.ParticipantsPerMatch == 2 + ? "Both eliminated" + : "Everyone eliminated", + _ => null + }; + + AddHtml(35, y, 190, 20, $"Tiebreaker: {tieText}"); + y += 20; + + string sdText; + + if (tourney.SuddenDeath > TimeSpan.Zero) + { + sdText = tourney.SuddenDeathRounds > 0 ? + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (first {tourney.SuddenDeathRounds} rounds)" : + $"Sudden Death: {(int)tourney.SuddenDeath.TotalMinutes}:{tourney.SuddenDeath.Seconds:D2} (all rounds)"; + } + else + { + sdText = "Sudden Death: Off"; + } + + AddHtml(35, y, 240, 20, sdText); + y += 20; + + y += 8; + + AddHtml(35, y, 190, 20, $"Ruleset: {basedef.Title}"); + y += 20; + + for (var i = 0; i < ruleset.Flavors.Count; ++i, y += 18) + { + AddHtml(35, y, 190, 20, $" + {ruleset.Flavors[i].Title}"); + } + + y += 4; + + if (changes > 0) + { + AddHtml(35, y, 190, 20, "Modifications:"); + y += 20; + + for (var i = 0; i < opts.Length; ++i) { - name = name.Color(pm.DuelPlayer.Eliminated ? 0x663333 : 0x336666); - } - } - - AddRightArrow(ref builder, 35, y, ToButtonID(4, i), name); - y += 18; - } - - builder.AddHtml( - 25, - y, - 200, - 20, - $"Free Advances: {(part.FreeAdvances == 0 ? "None" : part.FreeAdvances.ToString())}" - ); - y += 20; - - builder.AddHtml(25, y, 200, 20, "Log:"); - y += 20; - - using var sb = ValueStringBuilder.Create(); - - for (var i = 0; i < part.Log.Count; ++i) - { - if (sb.Length > 0) - { - sb.Append("
"); - } - - sb.Append(part.Log[i]); - } - - if (sb.Length == 0) - { - sb.Append("Nothing logged yet."); - } - - builder.AddHtml(25, y, 250, 150, sb.AsSpan().Color(BlackColor32), background: false, scrollbar: true); - - break; - } - case TourneyBracketGumpType.Player_Info: - { - builder.AddPage(); - builder.AddBackground(0, 0, 300, 300, 9380); - - AddLeftArrow(ref builder, 25, 11, ToButtonID(0, 3)); - builder.AddHtml(25, 35, 250, 20, "Participants".Center()); - - if (_object is not Mobile mob) - { - break; - } - - var ladder = Ladder.Instance; - var entry = ladder?.Find(mob); - - builder.AddHtml(25, 53, 250, 20, $"Name: {mob.Name}"); - builder.AddHtml( - 25, - 73, - 250, - 20, - $"Guild: {(mob.Guild == null ? "None" : $"{mob.Guild.Name} [{mob.Guild.Abbreviation}]")}" - ); - builder.AddHtml(25, 93, 250, 20, $"Rank: {(entry == null ? "N/A" : LadderGump.Rank(entry.Index + 1))}"); - builder.AddHtml(25, 113, 250, 20, $"Level: {(entry == null ? 0 : Ladder.GetLevel(entry.Experience))}"); - builder.AddHtml(25, 133, 250, 20, $"Wins: {entry?.Wins ?? 0:N0}"); - builder.AddHtml(25, 153, 250, 20, $"Losses: {entry?.Losses ?? 0:N0}"); - - break; - } - case TourneyBracketGumpType.Round_List: - { - builder.AddPage(); - builder.AddBackground(0, 0, 300, 300, 9380); - - AddLeftArrow(ref builder, 25, 11, ToButtonID(0, 0)); - builder.AddHtml(25, 35, 250, 20, "Rounds".Center()); - - StartPage(ref builder, out var index, out var count, out var y, 12); - - for (var i = 0; i < count; ++i, y += 18) - { - AddRightArrow(ref builder, 25, y, ToButtonID(3, index + i), $"Round #{index + i + 1}"); - } - - break; - } - case TourneyBracketGumpType.Round_Info: - { - builder.AddPage(); - builder.AddBackground(0, 0, 300, 300, 9380); - - AddLeftArrow(ref builder, 25, 11, ToButtonID(0, 2)); - builder.AddHtml(25, 35, 250, 20, "Rounds".Center()); - - if (_object is not PyramidLevel level) - { - break; - } - - var matchesList = _list != null - ? _list.SafeConvertList() - : new List(level.Matches); - - AddRightArrow( - ref builder, - 25, - 53, - ToButtonID(5, 0), - $"Free Advance: {(level.FreeAdvance == null ? "None" : level.FreeAdvance.NameList)}" - ); - - builder.AddHtml(25, 73, 200, 20, $"{matchesList.Count} Match{(matchesList.Count == 1 ? "" : "es")}"); - - StartPage(ref builder, out var index, out var count, out var y, 10); - - for (var i = 0; i < count; ++i, y += 18) - { - var match = matchesList[index + i]; - - var color = -1; - - if (match.InProgress) - { - color = 0x336666; - } - else if (match.Context != null && match.Winner == null) - { - color = 0x666666; - } - - using var sbMatch = ValueStringBuilder.Create(512); - - if (_tournament.TourneyType == TourneyType.Standard) - { - for (var j = 0; j < match.Participants.Count; ++j) - { - if (sbMatch.Length > 0) + if (defs[i] != opts[i]) { - sbMatch.Append(" vs "); - } + var name = ruleset.Layout.FindByIndex(i); - var part = match.Participants[j]; - var txt = part.NameList; - - if (color == -1 && match.Context != null && match.Winner == part) - { - txt = txt.Color(0x336633); - } - else if (color == -1 && match.Context != null) - { - txt = txt.Color(0x663333); - } - - sbMatch.Append(txt); - } - } - else if (_tournament.EventController != null || - _tournament.TourneyType is TourneyType.RandomTeam or TourneyType.RedVsBlue or TourneyType.Faction) - { - for (var j = 0; j < match.Participants.Count; ++j) - { - if (sbMatch.Length > 0) - { - sbMatch.Append(" vs "); - } - - var part = match.Participants[j]; - string txt; - - if (_tournament.EventController != null) - { - txt = $"Team {_tournament.EventController.GetTeamName(j)} ({part.Players.Count})"; - } - else if (_tournament.TourneyType == TourneyType.RandomTeam) - { - txt = $"Team {j + 1} ({part.Players.Count})"; - } - else if (_tournament.TourneyType == TourneyType.Faction) - { - if (_tournament.ParticipantsPerMatch == 4) + if (name != null) // sanity { - var name = j switch + AddImage(35, y, opts[i] ? 0xD3 : 0xD2); + AddHtml(60, y, 165, 22, name); + } + + y += 22; + } + } + } + else + { + AddHtml(35, y, 190, 20, "Modifications: None"); + } + + break; + } + case TourneyBracketGumpType.Participant_List: + { + AddPage(0); + AddBackground(0, 0, 300, 300, 9380); + + var pList = m_List?.SafeConvertList() + ?? new List(tourney.Participants); + + AddLeftArrow(25, 11, ToButtonID(0, 0)); + AddHtml(25, 35, 250, 20, $"{pList.Count} Participant{(pList.Count == 1 ? "" : "s")}".Center()); + + StartPage(out var index, out var count, out var y, 12); + + for (var i = 0; i < count; ++i, y += 18) + { + var part = pList[index + i]; + var name = part.NameList; + + if (m_Tournament.TourneyType != TourneyType.Standard && part.Players.Count == 1) + { + if (part.Players[0] is PlayerMobile pm && pm.DuelPlayer != null) + { + name = name.Color(pm.DuelPlayer.Eliminated ? 0x663333 : 0x336666); + } + } + + AddRightArrow(25, y, ToButtonID(2, index + i), name); + } + + break; + } + case TourneyBracketGumpType.Participant_Info: + { + if (obj is not TourneyParticipant part) + { + break; + } + + AddPage(0); + AddBackground(0, 0, 300, 60 + 18 + 20 + part.Players.Count * 18 + 20 + 20 + 160, 9380); + + AddLeftArrow(25, 11, ToButtonID(0, 1)); + AddHtml(25, 35, 250, 20, "Participants".Center()); + + var y = 53; + + AddHtml(25, y, 200, 20, part.Players.Count == 1 ? "Players" : "Team"); + y += 20; + + for (var i = 0; i < part.Players.Count; ++i) + { + var mob = part.Players[i]; + var name = mob.Name; + + if (m_Tournament.TourneyType != TourneyType.Standard) + { + if (mob is PlayerMobile pm && pm.DuelPlayer != null) + { + name = name.Color(pm.DuelPlayer.Eliminated ? 0x663333 : 0x336666); + } + } + + AddRightArrow(35, y, ToButtonID(4, i), name); + y += 18; + } + + AddHtml( + 25, + y, + 200, + 20, + $"Free Advances: {(part.FreeAdvances == 0 ? "None" : part.FreeAdvances.ToString())}" + ); + y += 20; + + AddHtml(25, y, 200, 20, "Log:"); + y += 20; + + var sb = new StringBuilder(); + + for (var i = 0; i < part.Log.Count; ++i) + { + if (sb.Length > 0) + { + sb.Append("
"); + } + + sb.Append(part.Log[i]); + } + + if (sb.Length == 0) + { + sb.Append("Nothing logged yet."); + } + + AddHtml(25, y, 250, 150, sb.ToString().Color(BlackColor32), false, true); + + break; + } + case TourneyBracketGumpType.Player_Info: + { + AddPage(0); + AddBackground(0, 0, 300, 300, 9380); + + AddLeftArrow(25, 11, ToButtonID(0, 3)); + AddHtml(25, 35, 250, 20, "Participants".Center()); + + if (obj is not Mobile mob) + { + break; + } + + var ladder = Ladder.Instance; + var entry = ladder?.Find(mob); + + AddHtml(25, 53, 250, 20, $"Name: {mob.Name}"); + AddHtml( + 25, + 73, + 250, + 20, + $"Guild: {(mob.Guild == null ? "None" : $"{mob.Guild.Name} [{mob.Guild.Abbreviation}]")}" + ); + AddHtml(25, 93, 250, 20, $"Rank: {(entry == null ? "N/A" : LadderGump.Rank(entry.Index + 1))}"); + AddHtml(25, 113, 250, 20, $"Level: {(entry == null ? 0 : Ladder.GetLevel(entry.Experience))}"); + AddHtml(25, 133, 250, 20, $"Wins: {entry?.Wins ?? 0:N0}"); + AddHtml(25, 153, 250, 20, $"Losses: {entry?.Losses ?? 0:N0}"); + + break; + } + case TourneyBracketGumpType.Round_List: + { + AddPage(0); + AddBackground(0, 0, 300, 300, 9380); + + AddLeftArrow(25, 11, ToButtonID(0, 0)); + AddHtml(25, 35, 250, 20, "Rounds".Center()); + + StartPage(out var index, out var count, out var y, 12); + + for (var i = 0; i < count; ++i, y += 18) + { + AddRightArrow(25, y, ToButtonID(3, index + i), $"Round #{index + i + 1}"); + } + + break; + } + case TourneyBracketGumpType.Round_Info: + { + AddPage(0); + AddBackground(0, 0, 300, 300, 9380); + + AddLeftArrow(25, 11, ToButtonID(0, 2)); + AddHtml(25, 35, 250, 20, "Rounds".Center()); + + if (m_Object is not PyramidLevel level) + { + break; + } + + var matchesList = m_List != null + ? m_List.SafeConvertList() + : new List(level.Matches); + + AddRightArrow( + 25, + 53, + ToButtonID(5, 0), + $"Free Advance: {(level.FreeAdvance == null ? "None" : level.FreeAdvance.NameList)}" + ); + + AddHtml(25, 73, 200, 20, $"{matchesList.Count} Match{(matchesList.Count == 1 ? "" : "es")}"); + + StartPage(out var index, out var count, out var y, 10); + + for (var i = 0; i < count; ++i, y += 18) + { + var match = matchesList[index + i]; + + var color = -1; + + if (match.InProgress) + { + color = 0x336666; + } + else if (match.Context != null && match.Winner == null) + { + color = 0x666666; + } + + var sb = new StringBuilder(); + + if (m_Tournament.TourneyType == TourneyType.Standard) + { + for (var j = 0; j < match.Participants.Count; ++j) + { + if (sb.Length > 0) + { + sb.Append(" vs "); + } + + var part = match.Participants[j]; + var txt = part.NameList; + + if (color == -1 && match.Context != null && match.Winner == part) + { + txt = txt.Color(0x336633); + } + else if (color == -1 && match.Context != null) + { + txt = txt.Color(0x663333); + } + + sb.Append(txt); + } + } + else if (m_Tournament.EventController != null || + m_Tournament.TourneyType is TourneyType.RandomTeam or TourneyType.RedVsBlue or TourneyType.Faction) + { + for (var j = 0; j < match.Participants.Count; ++j) + { + if (sb.Length > 0) + { + sb.Append(" vs "); + } + + var part = match.Participants[j]; + string txt; + + if (m_Tournament.EventController != null) + { + txt = $"Team {m_Tournament.EventController.GetTeamName(j)} ({part.Players.Count})"; + } + else if (m_Tournament.TourneyType == TourneyType.RandomTeam) + { + txt = $"Team {j + 1} ({part.Players.Count})"; + } + else if (m_Tournament.TourneyType == TourneyType.Faction) + { + if (m_Tournament.ParticipantsPerMatch == 4) + { + var name = j switch + { + 0 => "Minax", + 1 => "Council of Mages", + 2 => "True Britannians", + 3 => "Shadowlords", + _ => "(null)" + }; + + txt = $"{name} ({part.Players.Count})"; + } + else if (m_Tournament.ParticipantsPerMatch == 2) + { + txt = $"{(j == 0 ? "Evil" : "Hero")} Team ({part.Players.Count})"; + } + else + { + txt = $"Team {j + 1} ({part.Players.Count})"; + } + } + else + { + txt = $"Team {(j == 0 ? "Red" : "Blue")} ({part.Players.Count})"; + } + + if (color == -1 && match.Context != null && match.Winner == part) + { + txt = txt.Color(0x336633); + } + else if (color == -1 && match.Context != null) + { + txt = txt.Color(0x663333); + } + + sb.Append(txt); + } + } + else if (m_Tournament.TourneyType == TourneyType.FreeForAll) + { + sb.Append("Free For All"); + } + + var str = sb.ToString(); + + if (color >= 0) + { + str = str.Color(color); + } + + AddRightArrow(25, y, ToButtonID(5, index + i + 1), str); + } + + break; + } + case TourneyBracketGumpType.Match_Info: + { + if (obj is not TourneyMatch match) + { + break; + } + + var ct = m_Tournament.TourneyType == TourneyType.FreeForAll ? 2 : match.Participants.Count; + + AddPage(0); + AddBackground(0, 0, 300, 60 + 18 + 20 + 20 + 20 + ct * 18 + 6, 9380); + + AddLeftArrow(25, 11, ToButtonID(0, 5)); + AddHtml(25, 35, 250, 20, "Rounds".Center()); + + AddHtml(25, 53, 250, 20, $"Winner: {(match.Winner == null ? "N/A" : match.Winner.NameList)}"); + AddHtml( + 25, + 73, + 250, + 20, + $"State: {(match.InProgress ? "In progress" : match.Context != null ? "Complete" : "Waiting")}" + ); + AddHtml(25, 93, 250, 20, "Participants:"); + + if (m_Tournament.TourneyType == TourneyType.Standard) + { + for (var i = 0; i < match.Participants.Count; ++i) + { + var part = match.Participants[i]; + + AddRightArrow(25, 113 + i * 18, ToButtonID(6, i), part.NameList); + } + } + else if (m_Tournament.EventController != null || + m_Tournament.TourneyType is TourneyType.RandomTeam or TourneyType.RedVsBlue or TourneyType.Faction) + { + for (var i = 0; i < match.Participants.Count; ++i) + { + var part = match.Participants[i]; + + if (m_Tournament.EventController != null) + { + AddRightArrow( + 25, + 113 + i * 18, + ToButtonID(6, i), + $"Team {m_Tournament.EventController.GetTeamName(i)} ({part.Players.Count})" + ); + } + else if (m_Tournament.TourneyType == TourneyType.RandomTeam) + { + AddRightArrow( + 25, + 113 + i * 18, + ToButtonID(6, i), + $"Team {i + 1} ({part.Players.Count})" + ); + } + else if (m_Tournament.TourneyType == TourneyType.Faction) + { + if (m_Tournament.ParticipantsPerMatch == 4) + { + var name = i switch { 0 => "Minax", 1 => "Council of Mages", @@ -534,463 +636,415 @@ public class TournamentBracketGump : DynamicGump _ => "(null)" }; - txt = $"{name} ({part.Players.Count})"; + AddRightArrow( + 25, + 113 + i * 18, + ToButtonID(6, i), + $"{name} ({part.Players.Count})" + ); } - else if (_tournament.ParticipantsPerMatch == 2) + else if (m_Tournament.ParticipantsPerMatch == 2) { - txt = $"{(j == 0 ? "Evil" : "Hero")} Team ({part.Players.Count})"; + AddRightArrow( + 25, + 113 + i * 18, + ToButtonID(6, i), + $"{(i == 0 ? "Evil" : "Hero")} Team ({part.Players.Count})" + ); } else { - txt = $"Team {j + 1} ({part.Players.Count})"; + AddRightArrow( + 25, + 113 + i * 18, + ToButtonID(6, i), + $"Team {i + 1} ({part.Players.Count})" + ); } } else { - txt = $"Team {(j == 0 ? "Red" : "Blue")} ({part.Players.Count})"; + AddRightArrow( + 25, + 113 + i * 18, + ToButtonID(6, i), + $"Team {(i == 0 ? "Red" : "Blue")} ({part.Players.Count})" + ); } - - if (color == -1 && match.Context != null && match.Winner == part) - { - txt = txt.Color(0x336633); - } - else if (color == -1 && match.Context != null) - { - txt = txt.Color(0x663333); - } - - sbMatch.Append(txt); } } - else if (_tournament.TourneyType == TourneyType.FreeForAll) + else if (m_Tournament.TourneyType == TourneyType.FreeForAll) { - sbMatch.Append("Free For All"); + AddHtml(25, 113, 250, 20, "Free For All"); } - var str = sbMatch.ToString(); - - if (color >= 0) - { - str = str.Color(color); - } - - AddRightArrow(ref builder, 25, y, ToButtonID(5, index + i + 1), str); - } - - break; - } - case TourneyBracketGumpType.Match_Info: - { - if (_object is not TourneyMatch match) - { break; } + } + } - var ct = _tournament.TourneyType == TourneyType.FreeForAll ? 2 : match.Participants.Count; + private void AddBorderedText(int x, int y, int width, int height, string text, int color, int borderColor) + { + AddColoredText(x - 1, y - 1, width, height, text, borderColor); + AddColoredText(x - 1, y + 1, width, height, text, borderColor); + AddColoredText(x + 1, y - 1, width, height, text, borderColor); + AddColoredText(x + 1, y + 1, width, height, text, borderColor); + AddColoredText(x, y, width, height, text, color); + } - builder.AddPage(); - builder.AddBackground(0, 0, 300, 60 + 18 + 20 + 20 + 20 + ct * 18 + 6, 9380); + private void AddColoredText(int x, int y, int width, int height, string text, int color) + { + if (color == 0) + { + AddHtml(x, y, width, height, text); + } + else + { + AddHtml(x, y, width, height, text.Color(color)); + } + } - AddLeftArrow(ref builder, 25, 11, ToButtonID(0, 5)); - builder.AddHtml(25, 35, 250, 20, "Rounds".Center()); + public void AddRightArrow(int x, int y, int bid, string text) + { + AddButton(x, y, 0x15E1, 0x15E5, bid); - builder.AddHtml(25, 53, 250, 20, $"Winner: {(match.Winner == null ? "N/A" : match.Winner.NameList)}"); - builder.AddHtml( - 25, - 73, - 250, - 20, - $"State: {(match.InProgress ? "In progress" : match.Context != null ? "Complete" : "Waiting")}" - ); - builder.AddHtml(25, 93, 250, 20, "Participants:"); + if (text != null) + { + AddHtml(x + 20, y - 1, 230, 20, text); + } + } - if (_tournament.TourneyType == TourneyType.Standard) + public void AddRightArrow(int x, int y, int bid) + { + AddRightArrow(x, y, bid, null); + } + + public void AddLeftArrow(int x, int y, int bid, string text) + { + AddButton(x, y, 0x15E3, 0x15E7, bid); + + if (text != null) + { + AddHtml(x + 20, y - 1, 230, 20, text); + } + } + + public void AddLeftArrow(int x, int y, int bid) + { + AddLeftArrow(x, y, bid, null); + } + + public int ToButtonID(int type, int index) => 1 + index * 7 + type; + + public bool FromButtonID(int bid, out int type, out int index) + { + type = (bid - 1) % 7; + index = (bid - 1) / 7; + return bid >= 1; + } + + public void StartPage(out int index, out int count, out int y, int perPage) + { + m_PerPage = perPage; + + index = Math.Max(m_Page * perPage, 0); + count = Math.Clamp(m_List.Count - index, 0, perPage); + + y = 53 + (12 - perPage) * 18; + + if (m_Page > 0) + { + AddLeftArrow(242, 35, ToButtonID(1, 0)); + } + + if ((m_Page + 1) * perPage < m_List.Count) + { + AddRightArrow(260, 35, ToButtonID(1, 1)); + } + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (!FromButtonID(info.ButtonID, out var type, out var index)) + { + return; + } + + switch (type) + { + case 0: { - for (var i = 0; i < match.Participants.Count; ++i) + switch (index) { - var part = match.Participants[i]; - - AddRightArrow(ref builder, 25, 113 + i * 18, ToButtonID(6, i), part.NameList); - } - } - else if (_tournament.EventController != null || - _tournament.TourneyType is TourneyType.RandomTeam or TourneyType.RedVsBlue or TourneyType.Faction) - { - for (var i = 0; i < match.Participants.Count; ++i) - { - var part = match.Participants[i]; - - if (_tournament.EventController != null) - { - AddRightArrow( - ref builder, - 25, - 113 + i * 18, - ToButtonID(6, i), - $"Team {_tournament.EventController.GetTeamName(i)} ({part.Players.Count})" + case 0: + m_From.SendGump( + new TournamentBracketGump(m_From, m_Tournament, TourneyBracketGumpType.Index) ); - } - else if (_tournament.TourneyType == TourneyType.RandomTeam) - { - AddRightArrow( - ref builder, - 25, - 113 + i * 18, - ToButtonID(6, i), - $"Team {i + 1} ({part.Players.Count})" + break; + case 1: + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + TourneyBracketGumpType.Participant_List + ) ); - } - else if (_tournament.TourneyType == TourneyType.Faction) - { - if (_tournament.ParticipantsPerMatch == 4) + break; + case 2: + m_From.SendGump( + new TournamentBracketGump(m_From, m_Tournament, TourneyBracketGumpType.Round_List) + ); + break; + case 4: + m_From.SendGump( + new TournamentBracketGump(m_From, m_Tournament, TourneyBracketGumpType.Rules_Info) + ); + break; + case 3: { - var name = i switch + var mob = m_Object as Mobile; + + for (var i = 0; i < m_Tournament.Participants.Count; ++i) { - 0 => "Minax", - 1 => "Council of Mages", - 2 => "True Britannians", - 3 => "Shadowlords", - _ => "(null)" - }; + var part = m_Tournament.Participants[i]; - AddRightArrow( - ref builder, - 25, - 113 + i * 18, - ToButtonID(6, i), - $"{name} ({part.Players.Count})" - ); + if (part.Players.Contains(mob)) + { + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + TourneyBracketGumpType.Participant_Info, + null, + 0, + part + ) + ); + break; + } + } + + break; } - else if (_tournament.ParticipantsPerMatch == 2) + case 5: { - AddRightArrow( - ref builder, - 25, - 113 + i * 18, - ToButtonID(6, i), - $"{(i == 0 ? "Evil" : "Hero")} Team ({part.Players.Count})" - ); + if (m_Object is not TourneyMatch match) + { + break; + } + + for (var i = 0; i < m_Tournament.Pyramid.Levels.Count; ++i) + { + var level = m_Tournament.Pyramid.Levels[i]; + + if (level.Matches.Contains(match)) + { + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + TourneyBracketGumpType.Round_Info, + null, + 0, + level + ) + ); + } + } + + break; } - else + } + + break; + } + case 1: + { + switch (index) + { + case 0: { - AddRightArrow( - ref builder, - 25, - 113 + i * 18, - ToButtonID(6, i), - $"Team {i + 1} ({part.Players.Count})" - ); + if (m_List != null && m_Page > 0) + { + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + m_Type, + m_List, + m_Page - 1, + m_Object + ) + ); + } + + break; } + case 1: + { + if (m_List != null && (m_Page + 1) * m_PerPage < m_List.Count) + { + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + m_Type, + m_List, + m_Page + 1, + m_Object + ) + ); + } + + break; + } + } + + break; + } + case 2: + { + if (m_Type != TourneyBracketGumpType.Participant_List) + { + break; + } + + if (index >= 0 && index < m_List.Count) + { + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + TourneyBracketGumpType.Participant_Info, + null, + 0, + m_List[index] + ) + ); + } + + break; + } + case 3: + { + if (m_Type != TourneyBracketGumpType.Round_List) + { + break; + } + + if (index >= 0 && index < m_List.Count) + { + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + TourneyBracketGumpType.Round_Info, + null, + 0, + m_List[index] + ) + ); + } + + break; + } + case 4: + { + if (m_Type != TourneyBracketGumpType.Participant_Info) + { + break; + } + + if (m_Object is TourneyParticipant part && index >= 0 && index < part.Players.Count) + { + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + TourneyBracketGumpType.Player_Info, + null, + 0, + part.Players[index] + ) + ); + } + + break; + } + case 5: + { + if (m_Type != TourneyBracketGumpType.Round_Info) + { + break; + } + + if (m_Object is not PyramidLevel level) + { + break; + } + + if (index == 0) + { + if (level.FreeAdvance != null) + { + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + TourneyBracketGumpType.Participant_Info, + null, + 0, + level.FreeAdvance + ) + ); } else { - AddRightArrow( - ref builder, - 25, - 113 + i * 18, - ToButtonID(6, i), - $"Team {(i == 0 ? "Red" : "Blue")} ({part.Players.Count})" + m_From.SendGump( + new TournamentBracketGump(m_From, m_Tournament, m_Type, m_List, m_Page, m_Object) ); } } - } - else if (_tournament.TourneyType == TourneyType.FreeForAll) - { - builder.AddHtml(25, 113, 250, 20, "Free For All"); - } - - break; - } - } - } - - private static void AddRightArrow(ref DynamicGumpBuilder builder, int x, int y, int bid, string text) - { - builder.AddButton(x, y, 0x15E1, 0x15E5, bid); - - if (text != null) - { - builder.AddHtml(x + 20, y - 1, 230, 20, text); - } - } - - private static void AddRightArrow(ref DynamicGumpBuilder builder, int x, int y, int bid) - { - AddRightArrow(ref builder, x, y, bid, null); - } - - private static void AddLeftArrow(ref DynamicGumpBuilder builder, int x, int y, int bid, string text) - { - builder.AddButton(x, y, 0x15E3, 0x15E7, bid); - - if (text != null) - { - builder.AddHtml(x + 20, y - 1, 230, 20, text); - } - } - - private static void AddLeftArrow(ref DynamicGumpBuilder builder, int x, int y, int bid) - { - AddLeftArrow(ref builder, x, y, bid, null); - } - - private static int ToButtonID(int type, int index) => 1 + index * 7 + type; - - private static bool FromButtonID(int bid, out int type, out int index) - { - type = (bid - 1) % 7; - index = (bid - 1) / 7; - return bid >= 1; - } - - private void StartPage(ref DynamicGumpBuilder builder, out int index, out int count, out int y, int perPage) - { - _perPage = perPage; - - index = Math.Max(_page * perPage, 0); - count = Math.Clamp(_list.Count - index, 0, perPage); - - y = 53 + (12 - perPage) * 18; - - if (_page > 0) - { - AddLeftArrow(ref builder, 242, 35, ToButtonID(1, 0)); - } - - if ((_page + 1) * perPage < _list.Count) - { - AddRightArrow(ref builder, 260, 35, ToButtonID(1, 1)); - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (!FromButtonID(info.ButtonID, out var type, out var index)) - { - return; - } - - var from = sender.Mobile; - - switch (type) - { - case 0: - { - switch (index) - { - case 0: - { - _type = TourneyBracketGumpType.Index; - _list = null; - _page = 0; - _object = null; - break; - } - case 1: - { - _type = TourneyBracketGumpType.Participant_List; - _list = null; - _page = 0; - _object = null; - break; - } - case 2: - { - _type = TourneyBracketGumpType.Round_List; - _list = null; - _page = 0; - _object = null; - break; - } - case 4: - { - _type = TourneyBracketGumpType.Rules_Info; - _list = null; - _page = 0; - _object = null; - break; - } - case 3: - { - var mob = _object as Mobile; - bool sendGump = false; - - for (var i = 0; i < _tournament.Participants.Count; ++i) - { - var part = _tournament.Participants[i]; - - if (part.Players.Contains(mob)) - { - _type = TourneyBracketGumpType.Participant_Info; - _list = null; - _page = 0; - _object = part; - sendGump = true; - break; - } - } - - if (!sendGump) - { - return; - } - - break; - } - case 5: - { - if (_object is not TourneyMatch match) - { - break; - } - - var sendGump = false; - for (var i = 0; i < _tournament.Pyramid.Levels.Count; ++i) - { - var level = _tournament.Pyramid.Levels[i]; - - if (level.Matches.Contains(match)) - { - _type = TourneyBracketGumpType.Round_Info; - _list = null; - _page = 0; - _object = level; - sendGump = true; - } - } - - if (!sendGump) - { - return; - } - break; - } - } - - break; - } - case 1: - { - switch (index) - { - case 0: - { - if (_list != null && _page > 0) - { - _page--; - } - else - { - return; - } - - break; - } - case 1: - { - if (_list != null && (_page + 1) * _perPage < _list.Count) - { - _page++; - } - else - { - return; - } - - break; - } - } - - break; - } - case 2: - { - if (_type != TourneyBracketGumpType.Participant_List || index < 0 || index >= _list.Count) - { - return; - } - - _type = TourneyBracketGumpType.Participant_Info; - _object = _list[index]; - _list = null; - _page = 0; - - break; - } - case 3: - { - if (_type != TourneyBracketGumpType.Round_List || index < 0 || index >= _list.Count) - { - return; - } - - _type = TourneyBracketGumpType.Round_Info; - _object = _list[index]; - _list = null; - _page = 0; - - break; - } - case 4: - { - if (_type != TourneyBracketGumpType.Participant_Info || _object is not TourneyParticipant part || - index < 0 || index >= part.Players.Count) - { - return; - } - - _type = TourneyBracketGumpType.Player_Info; - _object = part.Players[index]; - _list = null; - _page = 0; - - break; - } - case 5: - { - if (_type != TourneyBracketGumpType.Round_Info || _object is not PyramidLevel level) - { - return; - } - - if (index == 0) - { - if (level.FreeAdvance != null) + else if (index >= 1 && index <= level.Matches.Count) { - _type = TourneyBracketGumpType.Participant_Info; - _object = level.FreeAdvance; - _list = null; - _page = 0; + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + TourneyBracketGumpType.Match_Info, + null, + 0, + level.Matches[index - 1] + ) + ); } + + break; } - else if (index >= 1 && index <= level.Matches.Count) + case 6: { - _type = TourneyBracketGumpType.Match_Info; - _object = level.Matches[index - 1]; - _list = null; - _page = 0; + if (m_Type != TourneyBracketGumpType.Match_Info) + { + break; + } + + if (m_Object is TourneyMatch match && index >= 0 && index < match.Participants.Count) + { + m_From.SendGump( + new TournamentBracketGump( + m_From, + m_Tournament, + TourneyBracketGumpType.Participant_Info, + null, + 0, + match.Participants[index] + ) + ); + } + + break; } - - break; - } - case 6: - { - if (_type != TourneyBracketGumpType.Match_Info || _object is not TourneyMatch match || index < 0 || - index >= match.Participants.Count) - { - return; - } - - _type = TourneyBracketGumpType.Participant_Info; - _object = match.Participants[index]; - _list = null; - _page = 0; - from.SendGump(this); // refresh-via-this - - break; - } + } } - - from.SendGump(this); // refresh-via-this } } diff --git a/Projects/UOContent/Engines/ConPVP/Participant.cs b/Projects/UOContent/Engines/ConPVP/Participant.cs index 54563bbdc..26d68a569 100644 --- a/Projects/UOContent/Engines/ConPVP/Participant.cs +++ b/Projects/UOContent/Engines/ConPVP/Participant.cs @@ -1,6 +1,6 @@ using System; +using System.Text; using Server.Mobiles; -using Server.Text; namespace Server.Engines.ConPVP { @@ -74,7 +74,7 @@ namespace Server.Engines.ConPVP { get { - using var sb = ValueStringBuilder.Create(256); + var sb = new StringBuilder(); for (var i = 0; i < Players.Length; ++i) { @@ -135,7 +135,6 @@ namespace Server.Engines.ConPVP if (nonLocalOverhead != null) { - // Phase 3 audit: Format string passed by caller; restructuring requires changing all callers — out of scope for this PR. Players[i] .Mobile.NonlocalOverheadMessage( MessageType.Regular, diff --git a/Projects/UOContent/Engines/ConPVP/Preferences.cs b/Projects/UOContent/Engines/ConPVP/Preferences.cs index d2cbf05bc..1a63a87e8 100644 --- a/Projects/UOContent/Engines/ConPVP/Preferences.cs +++ b/Projects/UOContent/Engines/ConPVP/Preferences.cs @@ -174,56 +174,42 @@ namespace Server.Engines.ConPVP } } - public class PreferencesGump : DynamicGump + public class PreferencesGump : Gump { - private readonly PreferencesEntry _entry; - private int _columnX; + private readonly PreferencesEntry m_Entry; + private int m_ColumnX = 12; public override bool Singleton => true; - private PreferencesGump(PreferencesEntry entry) : base(50, 50) => _entry = entry; - - public static void DisplayTo(Mobile from, Preferences prefs) + public PreferencesGump(Mobile from, Preferences prefs) : base(50, 50) { - if (from?.NetState == null || prefs == null) + m_Entry = prefs.Find(from); + + if (m_Entry == null) { return; } - var entry = prefs.Find(from); - - if (entry == null) - { - return; - } - - from.SendGump(new PreferencesGump(entry)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - _columnX = 12; - var arenas = Arena.Arenas; - builder.AddPage(); + AddPage(0); var height = 12 + 20 + arenas.Count * 31 + 24 + 12; - builder.AddBackground(0, 0, 499 + 40 - 365, height, 0x2436); + AddBackground(0, 0, 499 + 40 - 365, height, 0x2436); for (var i = 1; i < arenas.Count; i += 2) { - builder.AddImageTiled(12, 32 + i * 31, 475 + 40 - 365, 30, 0x2430); + AddImageTiled(12, 32 + i * 31, 475 + 40 - 365, 30, 0x2430); } - builder.AddAlphaRegion(10, 10, 479 + 40 - 365, height - 20); + AddAlphaRegion(10, 10, 479 + 40 - 365, height - 20); - AddColumnHeader(ref builder, 35, null); - AddColumnHeader(ref builder, 115, "Arena"); + AddColumnHeader(35, null); + AddColumnHeader(115, "Arena"); - builder.AddButton(499 + 40 - 365 - 12 - 63 - 4 - 63, height - 12 - 24, 247, 248, 1); - builder.AddButton(499 + 40 - 365 - 12 - 63, height - 12 - 24, 241, 242, 2); + AddButton(499 + 40 - 365 - 12 - 63 - 4 - 63, height - 12 - 24, 247, 248, 1); + AddButton(499 + 40 - 365 - 12 - 63, height - 12 - 24, 241, 242, 2); for (var i = 0; i < arenas.Count; ++i) { @@ -236,16 +222,16 @@ namespace Server.Engines.ConPVP var color = 0xCCFFCC; - builder.AddCheckbox(x + 3, y + 1, 9730, 9727, _entry.Disliked.Contains(name), i); + AddCheck(x + 3, y + 1, 9730, 9727, m_Entry.Disliked.Contains(name), i); x += 35; - AddBorderedText(ref builder, x + 5, y + 5, 115 - 5, name, color, 0); + AddBorderedText(x + 5, y + 5, 115 - 5, name, color, 0); } } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_entry == null) + if (m_Entry == null) { return; } @@ -255,7 +241,7 @@ namespace Server.Engines.ConPVP return; } - _entry.Disliked.Clear(); + m_Entry.Disliked.Clear(); var arenas = Arena.Arenas; @@ -265,32 +251,32 @@ namespace Server.Engines.ConPVP if (idx >= 0 && idx < arenas.Count) { - _entry.Disliked.Add(arenas[idx].Name); + m_Entry.Disliked.Add(arenas[idx].Name); } } } - private static void AddBorderedText(ref DynamicGumpBuilder builder, int x, int y, int width, string text, int color, int borderColor) + private void AddBorderedText(int x, int y, int width, string text, int color, int borderColor) { - AddColoredText(ref builder, x, y, width, text, color); + AddColoredText(x, y, width, text, color); } - private static void AddColoredText(ref DynamicGumpBuilder builder, int x, int y, int width, string text, int color) + private void AddColoredText(int x, int y, int width, string text, int color) { - builder.AddHtml(x, y, width, 20, color == 0 ? text : text.Color(color)); + AddHtml(x, y, width, 20, color == 0 ? text : text.Color(color)); } - private void AddColumnHeader(ref DynamicGumpBuilder builder, int width, string name) + private void AddColumnHeader(int width, string name) { - builder.AddBackground(_columnX, 12, width, 20, 0x242C); - builder.AddImageTiled(_columnX + 2, 14, width - 4, 16, 0x2430); + AddBackground(m_ColumnX, 12, width, 20, 0x242C); + AddImageTiled(m_ColumnX + 2, 14, width - 4, 16, 0x2430); if (name != null) { - AddBorderedText(ref builder, _columnX, 13, width, name.Center(), 0xFFFFFF, 0); + AddBorderedText(m_ColumnX, 13, width, name.Center(), 0xFFFFFF, 0); } - _columnX += width; + m_ColumnX += width; } } } diff --git a/Projects/UOContent/Engines/ConPVP/Ruleset.cs b/Projects/UOContent/Engines/ConPVP/Ruleset.cs index 895496c43..fb4a1ec8b 100644 --- a/Projects/UOContent/Engines/ConPVP/Ruleset.cs +++ b/Projects/UOContent/Engines/ConPVP/Ruleset.cs @@ -56,11 +56,12 @@ namespace Server.Engines.ConPVP public void RemoveFlavor(Ruleset flavor) { - if (!Flavors.Remove(flavor)) + if (!Flavors.Contains(flavor)) { return; } + Flavors.Remove(flavor); Options.And(flavor.Options.Not()); flavor.Options.Not(); } diff --git a/Projects/UOContent/Engines/ConPVP/Tournament.cs b/Projects/UOContent/Engines/ConPVP/Tournament.cs index e0be60c6f..e2cea6352 100644 --- a/Projects/UOContent/Engines/ConPVP/Tournament.cs +++ b/Projects/UOContent/Engines/ConPVP/Tournament.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using System.Text; using Server.Factions; using Server.Items; using Server.Regions; -using Server.Text; namespace Server.Engines.ConPVP { @@ -237,7 +237,7 @@ namespace Server.Engines.ConPVP return; } - using var sb = ValueStringBuilder.Create(512); + var sb = new StringBuilder(); sb.Append("The match has ended in a tie "); @@ -284,7 +284,7 @@ namespace Server.Engines.ConPVP { case TieType.FullAdvancement: { - sb.Append($"In accordance with the rules, {whole} parties are advanced."); + sb.AppendFormat("In accordance with the rules, {0} parties are advanced.", whole); break; } case TieType.FullElimination: @@ -294,7 +294,7 @@ namespace Server.Engines.ConPVP Undefeated.Remove(remaining[j]); } - sb.Append($"In accordance with the rules, {whole} parties are eliminated."); + sb.AppendFormat("In accordance with the rules, {0} parties are eliminated.", whole); break; } case TieType.Random: @@ -311,7 +311,11 @@ namespace Server.Engines.ConPVP if (advanced != null) { - sb.Append($"In accordance with the rules, {advanced.NameList} {(advanced.Players.Count == 1 ? "is" : "are")} advanced."); + sb.AppendFormat( + "In accordance with the rules, {0} {1} advanced.", + advanced.NameList, + advanced.Players.Count == 1 ? "is" : "are" + ); } break; @@ -340,7 +344,11 @@ namespace Server.Engines.ConPVP if (advanced != null) { - sb.Append($"In accordance with the rules, {advanced.NameList} {(advanced.Players.Count == 1 ? "is" : "are")} advanced."); + sb.AppendFormat( + "In accordance with the rules, {0} {1} advanced.", + advanced.NameList, + advanced.Players.Count == 1 ? "is" : "are" + ); } break; @@ -369,7 +377,11 @@ namespace Server.Engines.ConPVP if (advanced != null) { - sb.Append($"In accordance with the rules, {advanced.NameList} {(advanced.Players.Count == 1 ? "is" : "are")} advanced."); + sb.AppendFormat( + "In accordance with the rules, {0} {1} advanced.", + advanced.NameList, + advanced.Players.Count == 1 ? "is" : "are" + ); } break; @@ -420,7 +432,7 @@ namespace Server.Engines.ConPVP public void HandleWon(Arena arena, TourneyMatch match, TourneyParticipant winner) { - using var sb = ValueStringBuilder.Create(512); + var sb = new StringBuilder(); sb.Append("The match is complete. "); sb.Append(winner.NameList); @@ -436,7 +448,11 @@ namespace Server.Engines.ConPVP if (match.Participants.Count > 2) { - sb.Append($"{match.Participants.Count - 1} other {(winner.Players.Count == 1 ? "players" : "teams")}: "); + sb.AppendFormat( + "{0} other {1}: ", + match.Participants.Count - 1, + winner.Players.Count == 1 ? "players" : "teams" + ); } var hasAppended = false; @@ -576,7 +592,7 @@ namespace Server.Engines.ConPVP cash /= 1000; cash *= 1000; - using var sb = ValueStringBuilder.Create(256); + var sb = new StringBuilder(); if (TourneyType == TourneyType.FreeForAll) { @@ -612,8 +628,7 @@ namespace Server.Engines.ConPVP if (EventController != null) { - sb.Append(' '); - sb.Append(EventController.Title); + sb.Append(' ').Append(EventController.Title); } sb.Append(" Champion"); @@ -646,13 +661,13 @@ namespace Server.Engines.ConPVP } mob.SendMessage( - $"You have been awarded a {rank.LowerName()} trophy and {cash:N0}gp for your participation in this tournament." + $"You have been awarded a {rank.ToString().ToLower()} trophy and {cash:N0}gp for your participation in this tournament." ); } else { mob.SendMessage( - $"You have been awarded a {rank.LowerName()} trophy for your participation in this tournament." + $"You have been awarded a {rank.ToString().ToLower()} trophy for your participation in this tournament." ); } } diff --git a/Projects/UOContent/Engines/ConPVP/TournamentBracketItem.cs b/Projects/UOContent/Engines/ConPVP/TournamentBracketItem.cs index f4a8ebdbd..5fc71cb29 100644 --- a/Projects/UOContent/Engines/ConPVP/TournamentBracketItem.cs +++ b/Projects/UOContent/Engines/ConPVP/TournamentBracketItem.cs @@ -1,32 +1,60 @@ -using ModernUO.Serialization; +using Server.Gumps; -namespace Server.Engines.ConPVP; - -[SerializationGenerator(0, false)] -public partial class TournamentBracketItem : Item +namespace Server.Engines.ConPVP { - [SerializableField(0)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private TournamentController _tournament; - - [Constructible] - public TournamentBracketItem() : base(3774) => Movable = false; - - public override string DefaultName => "tournament bracket"; - - public override void OnDoubleClick(Mobile from) + public class TournamentBracketItem : Item { - if (!from.InRange(GetWorldLocation(), 2)) - { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that - } - else - { - var tourney = Tournament?.Tournament; + [Constructible] + public TournamentBracketItem() : base(3774) => Movable = false; - if (tourney != null) + public TournamentBracketItem(Serial serial) : base(serial) + { + } + + [CommandProperty(AccessLevel.GameMaster)] + public TournamentController Tournament { get; set; } + + public override string DefaultName => "tournament bracket"; + + public override void OnDoubleClick(Mobile from) + { + if (!from.InRange(GetWorldLocation(), 2)) { - TournamentBracketGump.DisplayTo(from, tourney, TourneyBracketGumpType.Index); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that + } + else + { + var tourney = Tournament?.Tournament; + + if (tourney != null) + { + from.SendGump(new TournamentBracketGump(from, tourney, TourneyBracketGumpType.Index), true); + } + } + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + + writer.Write(Tournament); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: + { + Tournament = reader.ReadEntity(); + break; + } } } } diff --git a/Projects/UOContent/Engines/ConPVP/TournamentController.cs b/Projects/UOContent/Engines/ConPVP/TournamentController.cs index 3d313c3e0..4cf5c8adb 100644 --- a/Projects/UOContent/Engines/ConPVP/TournamentController.cs +++ b/Projects/UOContent/Engines/ConPVP/TournamentController.cs @@ -70,7 +70,7 @@ namespace Server.Engines.ConPVP gumps.Close(); gumps.Close(); - PickRulesetGump.DisplayTo(from, null, Tournament.Ruleset); + gumps.Send(new PickRulesetGump(from, null, Tournament.Ruleset)); } } diff --git a/Projects/UOContent/Engines/ConPVP/TournamentPyramid.cs b/Projects/UOContent/Engines/ConPVP/TournamentPyramid.cs index c889cfc33..0f1e9d646 100644 --- a/Projects/UOContent/Engines/ConPVP/TournamentPyramid.cs +++ b/Projects/UOContent/Engines/ConPVP/TournamentPyramid.cs @@ -42,7 +42,7 @@ namespace Server.Engines.ConPVP { var mob = players[j]; - if (mob.Murderer) + if (mob.Kills >= 5) { parts[0].Players.Add(mob); } diff --git a/Projects/UOContent/Engines/ConPVP/TournamentRegistrar.cs b/Projects/UOContent/Engines/ConPVP/TournamentRegistrar.cs index edd92fb94..c128e6ff2 100644 --- a/Projects/UOContent/Engines/ConPVP/TournamentRegistrar.cs +++ b/Projects/UOContent/Engines/ConPVP/TournamentRegistrar.cs @@ -1,83 +1,108 @@ using System; -using ModernUO.Serialization; using Server.Factions; using Server.Mobiles; -namespace Server.Engines.ConPVP; - -[SerializationGenerator(0, false)] -public partial class TournamentRegistrar : Banker +namespace Server.Engines.ConPVP { - [SerializedCommandProperty(AccessLevel.GameMaster)] - [SerializableField(0)] - private TournamentController _tournament; - - [Constructible] - public TournamentRegistrar() => - Timer.DelayCall(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), Announce_Callback); - - private void Announce_Callback() + public class TournamentRegistrar : Banker { - var tourney = Tournament?.Tournament; - - if (tourney?.Stage == TournamentStage.Signup) + [Constructible] + public TournamentRegistrar() { - PublicOverheadMessage( - MessageType.Regular, - 0x35, - false, - "Come one, come all! Do you aspire to be a fighter of great renown? Join this tournament and show the world your abilities." - ); + Timer.StartTimer(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), Announce_Callback); } - } - public override void OnMovement(Mobile m, Point3D oldLocation) - { - base.OnMovement(m, oldLocation); - - var tourney = Tournament?.Tournament; - - if (InRange(m, 4) && !InRange(oldLocation, 4) && tourney?.Stage == TournamentStage.Signup && m.CanBeginAction(this)) + public TournamentRegistrar(Serial serial) : base(serial) { - var ladder = Ladder.Instance; - - var entry = ladder?.Find(m); - - if (entry != null && Ladder.GetLevel(entry.Experience) < tourney.LevelRequirement) - { - return; - } - - if (tourney.IsFactionRestricted && Faction.Find(m) == null) - { - return; - } - - if (tourney.HasParticipant(m)) - { - return; - } - - PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - $"Hello m'{(m.Female ? "Lady" : "Lord")}. Dost thou wish to enter this tournament? You need only to write your name in this book.", - m.NetState - ); - m.BeginAction(this); - Timer.StartTimer(TimeSpan.FromSeconds(10.0), () => ReleaseLock_Callback(m)); } - } - public void ReleaseLock_Callback(Mobile m) - { - m.EndAction(this); - } + [CommandProperty(AccessLevel.GameMaster)] + public TournamentController Tournament { get; set; } - [AfterDeserialization] - private void AfterDeserialization() - { - Timer.DelayCall(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), Announce_Callback); + private void Announce_Callback() + { + var tourney = Tournament?.Tournament; + + if (tourney?.Stage == TournamentStage.Signup) + { + PublicOverheadMessage( + MessageType.Regular, + 0x35, + false, + "Come one, come all! Do you aspire to be a fighter of great renown? Join this tournament and show the world your abilities." + ); + } + } + + public override void OnMovement(Mobile m, Point3D oldLocation) + { + base.OnMovement(m, oldLocation); + + var tourney = Tournament?.Tournament; + + if (InRange(m, 4) && !InRange(oldLocation, 4) && tourney?.Stage == TournamentStage.Signup && m.CanBeginAction(this)) + { + var ladder = Ladder.Instance; + + var entry = ladder?.Find(m); + + if (entry != null && Ladder.GetLevel(entry.Experience) < tourney.LevelRequirement) + { + return; + } + + if (tourney.IsFactionRestricted && Faction.Find(m) == null) + { + return; + } + + if (tourney.HasParticipant(m)) + { + return; + } + + PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + $"Hello m'{(m.Female ? "Lady" : "Lord")}. Dost thou wish to enter this tournament? You need only to write your name in this book.", + m.NetState + ); + m.BeginAction(this); + Timer.StartTimer(TimeSpan.FromSeconds(10.0), () => ReleaseLock_Callback(m)); + } + } + + public void ReleaseLock_Callback(Mobile m) + { + m.EndAction(this); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + + writer.Write(Tournament); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: + { + Tournament = reader.ReadEntity(); + break; + } + } + + Timer.StartTimer(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), Announce_Callback); + } } } diff --git a/Projects/UOContent/Engines/ConPVP/TournamentSignupItem.cs b/Projects/UOContent/Engines/ConPVP/TournamentSignupItem.cs index 909e5f836..40733e15f 100644 --- a/Projects/UOContent/Engines/ConPVP/TournamentSignupItem.cs +++ b/Projects/UOContent/Engines/ConPVP/TournamentSignupItem.cs @@ -1,162 +1,193 @@ using System.Collections.Generic; -using ModernUO.Serialization; using Server.Factions; using Server.Gumps; using Server.Mobiles; -namespace Server.Engines.ConPVP; - -[SerializationGenerator(0, false)] -public partial class TournamentSignupItem : Item +namespace Server.Engines.ConPVP { - [SerializedCommandProperty(AccessLevel.GameMaster)] - [SerializableField(0)] - private TournamentController _tournament; - - [SerializedCommandProperty(AccessLevel.GameMaster)] - [SerializableField(1)] - private Mobile _registrar; - - [Constructible] - public TournamentSignupItem() : base(4029) => Movable = false; - - public override string DefaultName => "tournament signup book"; - - public override void OnDoubleClick(Mobile from) + public class TournamentSignupItem : Item { - if (!from.InRange(GetWorldLocation(), 2)) + [Constructible] + public TournamentSignupItem() : base(4029) => Movable = false; + + public TournamentSignupItem(Serial serial) : base(serial) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that } - else + + [CommandProperty(AccessLevel.GameMaster)] + public TournamentController Tournament { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public Mobile Registrar { get; set; } + + public override string DefaultName => "tournament signup book"; + + public override void OnDoubleClick(Mobile from) { - var tourney = Tournament?.Tournament; - - if (tourney == null) + if (!from.InRange(GetWorldLocation(), 2)) { - return; + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that } - - Registrar?.Direction = Registrar.GetDirectionTo(this); - - switch (tourney.Stage) + else { - case TournamentStage.Fighting: - { - if (Registrar != null) + var tourney = Tournament?.Tournament; + + if (tourney == null) + { + return; + } + + if (Registrar != null) + { + Registrar.Direction = Registrar.GetDirectionTo(this); + } + + switch (tourney.Stage) + { + case TournamentStage.Fighting: { - if (tourney.HasParticipant(from)) + if (Registrar != null) { - Registrar.PrivateOverheadMessage( + if (tourney.HasParticipant(from)) + { + Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "Excuse me? You are already signed up.", + from.NetState + ); + } + else + { + Registrar.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "The tournament has already begun. You are too late to signup now.", + from.NetState + ); + } + } + + break; + } + case TournamentStage.Inactive: + { + Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "The tournament is closed.", + from.NetState + ); + + break; + } + case TournamentStage.Signup: + { + var ladder = Ladder.Instance; + var entry = ladder?.Find(from); + + if (entry != null && Ladder.GetLevel(entry.Experience) < tourney.LevelRequirement) + { + Registrar?.PrivateOverheadMessage( MessageType.Regular, 0x35, false, - "Excuse me? You are already signed up.", + "You have not yet proven yourself a worthy dueler.", from.NetState ); + + break; } - else + + if (tourney.IsFactionRestricted && Faction.Find(from) == null) { - Registrar.PrivateOverheadMessage( + Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "Only those who have declared their faction allegiance may participate.", + from.NetState + ); + + break; + } + + if (from.HasGump()) + { + Registrar?.PrivateOverheadMessage( MessageType.Regular, 0x22, false, - "The tournament has already begun. You are too late to signup now.", + "You must first respond to the offer I've given you.", + from.NetState + ); + } + else if (from.HasGump()) + { + Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "You must first cancel your duel offer.", + from.NetState + ); + } + else if (from is PlayerMobile mobile && mobile.DuelContext != null) + { + Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x22, + false, + "You are already participating in a duel.", + mobile.NetState + ); + } + else if (!tourney.HasParticipant(from)) + { + from.SendGump(new ConfirmSignupGump(from, Registrar, tourney, new List { from })); + } + else + { + Registrar?.PrivateOverheadMessage( + MessageType.Regular, + 0x35, + false, + "You have already entered this tournament.", from.NetState ); } - } - - break; - } - case TournamentStage.Inactive: - { - Registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "The tournament is closed.", - from.NetState - ); - - break; - } - case TournamentStage.Signup: - { - var ladder = Ladder.Instance; - var entry = ladder?.Find(from); - - if (entry != null && Ladder.GetLevel(entry.Experience) < tourney.LevelRequirement) - { - Registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "You have not yet proven yourself a worthy dueler.", - from.NetState - ); break; } + } + } + } - if (tourney.IsFactionRestricted && Faction.Find(from) == null) - { - Registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "Only those who have declared their faction allegiance may participate.", - from.NetState - ); + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); - break; - } + writer.Write(0); - if (from.HasGump()) - { - Registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "You must first respond to the offer I've given you.", - from.NetState - ); - } - else if (from.HasGump()) - { - Registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "You must first cancel your duel offer.", - from.NetState - ); - } - else if (from is PlayerMobile mobile && mobile.DuelContext != null) - { - Registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x22, - false, - "You are already participating in a duel.", - mobile.NetState - ); - } - else if (!tourney.HasParticipant(from)) - { - ConfirmSignupGump.DisplayTo(from, Registrar, tourney, new List { from }); - } - else - { - Registrar?.PrivateOverheadMessage( - MessageType.Regular, - 0x35, - false, - "You have already entered this tournament.", - from.NetState - ); - } + writer.Write(Tournament); + writer.Write(Registrar); + } + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: + { + Tournament = reader.ReadEntity(); + Registrar = reader.ReadEntity(); break; } } diff --git a/Projects/UOContent/Engines/ConPVP/TourneyParticipant.cs b/Projects/UOContent/Engines/ConPVP/TourneyParticipant.cs index c401e409e..fa4a87a97 100644 --- a/Projects/UOContent/Engines/ConPVP/TourneyParticipant.cs +++ b/Projects/UOContent/Engines/ConPVP/TourneyParticipant.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Server.Text; +using System.Text; namespace Server.Engines.ConPVP { @@ -56,7 +56,7 @@ namespace Server.Engines.ConPVP { get { - using var sb = ValueStringBuilder.Create(256); + var sb = new StringBuilder(); for (var i = 0; i < Players.Count; ++i) { diff --git a/Projects/UOContent/Engines/ConPVP/Trophy.cs b/Projects/UOContent/Engines/ConPVP/Trophy.cs index 603b52ef3..56e92daf3 100644 --- a/Projects/UOContent/Engines/ConPVP/Trophy.cs +++ b/Projects/UOContent/Engines/ConPVP/Trophy.cs @@ -10,22 +10,6 @@ public enum TrophyRank Gold } -public static class TrophyRankExtensions -{ - /// - /// Returns a static lowercase string for the given value. - /// Avoids the two-allocation rank.ToString().ToLower() pattern at interpolation sites. - /// - public static string LowerName(this TrophyRank rank) => - rank switch - { - TrophyRank.Bronze => "bronze", - TrophyRank.Silver => "silver", - TrophyRank.Gold => "gold", - _ => rank.ToString().ToLowerInvariant() - }; -} - [Flippable(5020, 4647)] [SerializationGenerator(2, false)] public partial class Trophy : Item @@ -64,13 +48,17 @@ public partial class Trophy : Item UpdateStyle(); } - [SerializableField(1, fieldChanged: nameof(OnRankChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private TrophyRank _rank; - - private void OnRankChanged(TrophyRank oldValue, TrophyRank newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public TrophyRank Rank { - UpdateStyle(); + get => _rank; + set + { + _rank = value; + UpdateStyle(); + this.MarkDirty(); + } } private void Deserialize(IGenericReader reader, int version) @@ -134,4 +122,3 @@ public partial class Trophy : Item }; } } - diff --git a/Projects/UOContent/Engines/Craft/Core/CraftContext.cs b/Projects/UOContent/Engines/Craft/Core/CraftContext.cs index 8bd215839..a0877fa6e 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftContext.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftContext.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Server.Items; namespace Server.Engines.Craft { @@ -32,13 +31,6 @@ namespace Server.Engines.Craft public CraftMarkOption MarkOption { get; set; } - // T2A: last hue used for hue-aware crafting (tailoring cloth) - public int LastHue { get; set; } = -1; - - // T2A jewelry: transient gem info set by GemSelectTarget, consumed by BaseJewel.OnCraft - public GemType PendingGemType { get; set; } - public int PendingGemCount { get; set; } - public CraftItem LastMade { get diff --git a/Projects/UOContent/Engines/Craft/Core/CraftGump.cs b/Projects/UOContent/Engines/Craft/Core/CraftGump.cs index 8becdb80d..c9c55bfb7 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftGump.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftGump.cs @@ -148,11 +148,11 @@ public class CraftGump : DynamicGump if (nameNumber > 0) { - builder.AddHtmlLocalized(50, 365, 250, 18, nameNumber, $"{resourceCount}", LabelColor); + builder.AddHtmlLocalized(50, 365, 250, 18, nameNumber, resourceCount.ToString(), LabelColor); } else { - builder.AddLabel(50, 365, LabelHue, $"{nameString} ({resourceCount} Available)"); + builder.AddLabel(50, 362, LabelHue, $"{nameString} ({resourceCount} Available)"); } } // **************************************** @@ -193,7 +193,7 @@ public class CraftGump : DynamicGump if (nameNumber > 0) { - builder.AddHtmlLocalized(50, 385, 250, 18, nameNumber, $"{resourceCount}", LabelColor); + builder.AddHtmlLocalized(50, 385, 250, 18, nameNumber, resourceCount.ToString(), LabelColor); } else { @@ -279,13 +279,13 @@ public class CraftGump : DynamicGump 250, 18, subResource.Name.Number, - $"{resourceCount}", + resourceCount.ToString(), LabelColor ); } else { - builder.AddLabel(255, 63 + index * 20, LabelHue, $"{subResource.Name.String} ({resourceCount})"); + builder.AddLabel(255, 60 + index * 20, LabelHue, $"{subResource.Name.String} ({resourceCount})"); } } } @@ -334,7 +334,7 @@ public class CraftGump : DynamicGump } else { - builder.AddLabel(255, 63 + index * 20, LabelHue, craftItem.NameString); + builder.AddLabel(255, 60 + index * 20, LabelHue, craftItem.NameString); } builder.AddButton(480, 60 + index * 20, 4011, 4012, GetButtonID(4, i)); @@ -388,7 +388,7 @@ public class CraftGump : DynamicGump } else { - builder.AddLabel(255, 63 + index * 20, LabelHue, craftItem.NameString); + builder.AddLabel(255, 60 + index * 20, LabelHue, craftItem.NameString); } builder.AddButton(480, 60 + index * 20, 4011, 4012, GetButtonID(2, i)); @@ -414,7 +414,7 @@ public class CraftGump : DynamicGump } else { - builder.AddLabel(50, 83 + i * 20, LabelHue, craftGroup.NameString); + builder.AddLabel(50, 80 + i * 20, LabelHue, craftGroup.NameString); } } } @@ -673,6 +673,7 @@ public class CraftGump : DynamicGump } context.DoNotColor = !context.DoNotColor; + _from.SendGump(new CraftGump(_from, _craftSystem, _tool, null, _page)); break; diff --git a/Projects/UOContent/Engines/Craft/Core/CraftGumpItem.cs b/Projects/UOContent/Engines/Craft/Core/CraftGumpItem.cs index c8e9a5fc2..a8938b511 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftGumpItem.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftGumpItem.cs @@ -4,340 +4,333 @@ using Server.Items; using Server.Mobiles; using Server.Network; -namespace Server.Engines.Craft; - -public class CraftGumpItem : DynamicGump +namespace Server.Engines.Craft { - private const int LabelHue = 0x480; // 0x384 - private const int RedLabelHue = 0x20; - - private const int LabelColor = 0x7FFF; - private const int RedLabelColor = 0x6400; - - private const int GreyLabelColor = 0x3DEF; - - private static readonly Type typeofBlankScroll = typeof(BlankScroll); - private static readonly Type typeofSpellScroll = typeof(SpellScroll); - - private readonly Mobile _from; - private readonly CraftItem _craftItem; - private readonly CraftSystem _craftSystem; - private readonly BaseTool _tool; - - public override bool Singleton => true; - - public CraftGumpItem(Mobile from, CraftSystem craftSystem, CraftItem craftItem, BaseTool tool) : base(40, 40) + public class CraftGumpItem : Gump { - _from = from; - _craftSystem = craftSystem; - _craftItem = craftItem; - _tool = tool; - } + private const int LabelHue = 0x480; // 0x384 + private const int RedLabelHue = 0x20; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 530, 417, 5054); - builder.AddImageTiled(10, 10, 510, 22, 2624); - builder.AddImageTiled(10, 37, 150, 148, 2624); - builder.AddImageTiled(165, 37, 355, 90, 2624); - builder.AddImageTiled(10, 190, 155, 22, 2624); - builder.AddImageTiled(10, 217, 150, 53, 2624); - builder.AddImageTiled(165, 132, 355, 80, 2624); - builder.AddImageTiled(10, 275, 155, 22, 2624); - builder.AddImageTiled(10, 302, 150, 53, 2624); - builder.AddImageTiled(165, 217, 355, 80, 2624); - builder.AddImageTiled(10, 360, 155, 22, 2624); - builder.AddImageTiled(165, 302, 355, 80, 2624); - builder.AddImageTiled(10, 387, 510, 22, 2624); - builder.AddAlphaRegion(10, 10, 510, 399); + private const int LabelColor = 0x7FFF; + private const int RedLabelColor = 0x6400; - builder.AddHtmlLocalized(170, 40, 150, 20, 1044053, LabelColor); // ITEM - builder.AddHtmlLocalized(10, 192, 150, 22, 1044054, LabelColor); //
SKILLS
- builder.AddHtmlLocalized(10, 277, 150, 22, 1044055, LabelColor); //
MATERIALS
- builder.AddHtmlLocalized(10, 362, 150, 22, 1044056, LabelColor); //
OTHER
+ private const int GreyLabelColor = 0x3DEF; - if (_craftSystem.GumpTitle.Number > 0) + private static readonly Type typeofBlankScroll = typeof(BlankScroll); + private static readonly Type typeofSpellScroll = typeof(SpellScroll); + private readonly CraftItem m_CraftItem; + private readonly CraftSystem m_CraftSystem; + private readonly Mobile m_From; + private readonly BaseTool m_Tool; + + private int m_OtherCount; + + private bool m_ShowExceptionalChance; + + public override bool Singleton => true; + + public CraftGumpItem(Mobile from, CraftSystem craftSystem, CraftItem craftItem, BaseTool tool) : base(40, 40) { - builder.AddHtmlLocalized(10, 12, 510, 20, _craftSystem.GumpTitle.Number, LabelColor); - } - else - { - builder.AddHtml(10, 12, 510, 20, _craftSystem.GumpTitle.String); - } + m_From = from; + m_CraftSystem = craftSystem; + m_CraftItem = craftItem; + m_Tool = tool; - builder.AddButton(15, 387, 4014, 4016, 0); - builder.AddHtmlLocalized(50, 390, 150, 18, 1044150, LabelColor); // BACK + AddPage(0); + AddBackground(0, 0, 530, 417, 5054); + AddImageTiled(10, 10, 510, 22, 2624); + AddImageTiled(10, 37, 150, 148, 2624); + AddImageTiled(165, 37, 355, 90, 2624); + AddImageTiled(10, 190, 155, 22, 2624); + AddImageTiled(10, 217, 150, 53, 2624); + AddImageTiled(165, 132, 355, 80, 2624); + AddImageTiled(10, 275, 155, 22, 2624); + AddImageTiled(10, 302, 150, 53, 2624); + AddImageTiled(165, 217, 355, 80, 2624); + AddImageTiled(10, 360, 155, 22, 2624); + AddImageTiled(165, 302, 355, 80, 2624); + AddImageTiled(10, 387, 510, 22, 2624); + AddAlphaRegion(10, 10, 510, 399); - var needsRecipe = _craftItem.Recipe != null && _from is PlayerMobile mobile && - !mobile.HasRecipe(_craftItem.Recipe); + AddHtmlLocalized(170, 40, 150, 20, 1044053, LabelColor); // ITEM + AddHtmlLocalized(10, 192, 150, 22, 1044054, LabelColor); //
SKILLS
+ AddHtmlLocalized(10, 277, 150, 22, 1044055, LabelColor); //
MATERIALS
+ AddHtmlLocalized(10, 362, 150, 22, 1044056, LabelColor); //
OTHER
- if (needsRecipe) - { - builder.AddButton(270, 387, 4005, 4007, 0, GumpButtonType.Page); - builder.AddHtmlLocalized(305, 390, 150, 18, 1044151, GreyLabelColor); // MAKE NOW - } - else - { - builder.AddButton(270, 387, 4005, 4007, 1); - builder.AddHtmlLocalized(305, 390, 150, 18, 1044151, LabelColor); // MAKE NOW - } - - if (_craftItem.NameNumber > 0) - { - builder.AddHtmlLocalized(330, 40, 180, 18, _craftItem.NameNumber, LabelColor); - } - else - { - builder.AddLabel(330, 40, LabelHue, _craftItem.NameString); - } - - if (_craftItem.UseAllRes) - { - builder.AddHtmlLocalized( - 170, - 302, - 310, - 18, - 1048176, // Makes as many as possible at once - LabelColor - ); - } - - var otherCount = 1; - - DrawItem(ref builder, ref otherCount, out var showExceptionalChance); - DrawSkill(ref builder, showExceptionalChance); - DrawResource(ref builder, ref otherCount); - - if (_craftItem.RequiredExpansion != Expansion.None) - { - var supportsEx = _from.NetState?.SupportsExpansion(_craftItem.RequiredExpansion) == true; - RequiredExpansionMessage(_craftItem.RequiredExpansion).AddHtmlText( - ref builder, - 170, - 302 + otherCount++ * 20, - 310, - 18, - false, - false, - supportsEx ? LabelColor : RedLabelColor, - supportsEx ? LabelHue : RedLabelHue - ); - } - - if (needsRecipe) - { - builder.AddHtmlLocalized( - 170, - 302 + otherCount++ * 20, - 310, - 18, - 1073620, // You have not learned this recipe. - RedLabelColor - ); - } - } - - private static TextDefinition RequiredExpansionMessage(Expansion expansion) - { - return expansion switch - { - Expansion.SE => 1063363, // * Requires the "Samurai Empire" expansion - Expansion.ML => 1072651, // * Requires the "Mondain's Legacy" expansion - _ => $"* Requires the \"{ExpansionInfo.GetInfo(expansion).Name}\" expansion" - }; - } - - public void DrawItem(ref DynamicGumpBuilder builder, ref int otherCount, out bool showExceptionalChance) - { - var type = _craftItem.ItemType; - - builder.AddItem(20, 50, _craftItem.ItemId, _craftItem.ItemHue); - - if (_craftItem.IsMarkable(type)) - { - builder.AddHtmlLocalized( - 170, - 302 + otherCount++ * 20, - 310, - 18, - 1044059, // This item may hold its maker's mark - LabelColor - ); - - showExceptionalChance = true; - } - else - { - showExceptionalChance = false; - } - } - - public void DrawSkill(ref DynamicGumpBuilder builder, bool showExceptionalChance) - { - for (var i = 0; i < _craftItem.Skills.Count; i++) - { - var skill = _craftItem.Skills[i]; - var minSkill = Math.Max(skill.MinSkill, 0); - - builder.AddHtmlLocalized(170, 132 + i * 20, 200, 18, AosSkillBonuses.GetLabel(skill.SkillToMake), LabelColor); - builder.AddLabel(430, 132 + i * 20, LabelHue, $"{minSkill:F1}"); - } - - var res = _craftItem.UseSubRes2 ? _craftSystem.CraftSubRes2 : _craftSystem.CraftSubRes; - var resIndex = -1; - - var context = _craftSystem.GetContext(_from); - - if (context != null) - { - resIndex = _craftItem.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; - } - - var chance = _craftItem.GetSuccessChance( - _from, - resIndex > -1 ? res.GetAt(resIndex).ItemType : null, - _craftSystem, - false, - out _ - ); - - builder.AddHtmlLocalized(170, 80, 250, 18, 1044057, LabelColor); // Success Chance: - builder.AddLabel(430, 80, LabelHue, $"{Math.Clamp(chance, 0, 1) * 100:F1}%"); - - if (showExceptionalChance) - { - var exceptChance = Math.Clamp(_craftItem.GetExceptionalChance(_craftSystem, chance, _from), 0, 1.0); - - builder.AddHtmlLocalized(170, 100, 250, 18, 1044058, 32767); // Exceptional Chance: - builder.AddLabel(430, 100, LabelHue, $"{exceptChance * 100:F1}%"); - } - } - - public void DrawResource(ref DynamicGumpBuilder builder, ref int otherCount) - { - var retainedColor = false; - - var context = _craftSystem.GetContext(_from); - - var res = _craftItem.UseSubRes2 ? _craftSystem.CraftSubRes2 : _craftSystem.CraftSubRes; - var resIndex = -1; - - if (context != null) - { - resIndex = _craftItem.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; - } - - var cropScroll = _craftItem.Resources.Count > 1 - && _craftItem.Resources[^1].ItemType == typeofBlankScroll - && typeofSpellScroll.IsAssignableFrom(_craftItem.ItemType); - - for (var i = 0; i < _craftItem.Resources.Count - (cropScroll ? 1 : 0) && i < 4; i++) - { - var craftResource = _craftItem.Resources[i]; - - var type = craftResource.ItemType; - var nameString = craftResource.Name.String; - var nameNumber = craftResource.Name.Number; - - // Resource Mutation - if (type == res.ResType && resIndex > -1) + if (craftSystem.GumpTitle.Number > 0) { - var subResource = res.GetAt(resIndex); - - type = subResource.ItemType; - - nameString = subResource.Name.String; - nameNumber = subResource.GenericNameNumber; - - if (nameNumber <= 0) - { - nameNumber = subResource.Name.Number; - } + AddHtmlLocalized(10, 12, 510, 20, craftSystem.GumpTitle.Number, LabelColor); } - // ****************** - - if (!retainedColor && _craftItem.RetainsColorFrom(_craftSystem, type)) + else { - retainedColor = true; - builder.AddHtmlLocalized( + AddHtml(10, 12, 510, 20, craftSystem.GumpTitle.String); + } + + AddButton(15, 387, 4014, 4016, 0); + AddHtmlLocalized(50, 390, 150, 18, 1044150, LabelColor); // BACK + + var needsRecipe = craftItem.Recipe != null && from is PlayerMobile mobile && + !mobile.HasRecipe(craftItem.Recipe); + + if (needsRecipe) + { + AddButton(270, 387, 4005, 4007, 0, GumpButtonType.Page); + AddHtmlLocalized(305, 390, 150, 18, 1044151, GreyLabelColor); // MAKE NOW + } + else + { + AddButton(270, 387, 4005, 4007, 1); + AddHtmlLocalized(305, 390, 150, 18, 1044151, LabelColor); // MAKE NOW + } + + if (craftItem.NameNumber > 0) + { + AddHtmlLocalized(330, 40, 180, 18, craftItem.NameNumber, LabelColor); + } + else + { + AddLabel(330, 40, LabelHue, craftItem.NameString); + } + + if (craftItem.UseAllRes) + { + AddHtmlLocalized( 170, - 302 + otherCount++ * 20, + 302 + m_OtherCount++ * 20, 310, 18, - 1044152, // * The item retains the color of this material + 1048176, // Makes as many as possible at once LabelColor ); - builder.AddLabel(500, 219 + i * 20, LabelHue, "*"); } - if (nameNumber > 0) + DrawItem(); + DrawSkill(); + DrawResource(); + + if (craftItem.RequiredExpansion != Expansion.None) { - builder.AddHtmlLocalized(170, 219 + i * 20, 310, 18, nameNumber, LabelColor); - } - else - { - builder.AddLabel(170, 219 + i * 20, LabelHue, nameString); + var supportsEx = from.NetState?.SupportsExpansion(craftItem.RequiredExpansion) == true; + RequiredExpansionMessage(craftItem.RequiredExpansion).AddHtmlText( + this, + 170, + 302 + m_OtherCount++ * 20, + 310, + 18, + false, + false, + supportsEx ? LabelColor : RedLabelColor, + supportsEx ? LabelHue : RedLabelHue + ); } - builder.AddLabel(430, 219 + i * 20, LabelHue, craftResource.Amount.ToString()); + if (needsRecipe) + { + AddHtmlLocalized( + 170, + 302 + m_OtherCount++ * 20, + 310, + 18, + 1073620, // You have not learned this recipe. + RedLabelColor + ); + } } - if (_craftItem.NameNumber == 1041267) // runebook + private static TextDefinition RequiredExpansionMessage(Expansion expansion) { - builder.AddHtmlLocalized(170, 219 + _craftItem.Resources.Count * 20, 310, 18, 1044447, LabelColor); - builder.AddLabel(430, 219 + _craftItem.Resources.Count * 20, LabelHue, "1"); + return expansion switch + { + Expansion.SE => 1063363, // * Requires the "Samurai Empire" expansion + Expansion.ML => 1072651, // * Requires the "Mondain's Legacy" expansion + _ => $"* Requires the \"{ExpansionInfo.GetInfo(expansion).Name}\" expansion" + }; } - if (cropScroll) + public void DrawItem() { - builder.AddHtmlLocalized( - 170, - 302 + otherCount++ * 20, - 360, - 18, - 1044379, // Inscribing scrolls also requires a blank scroll and mana. - LabelColor + var type = m_CraftItem.ItemType; + + AddItem(20, 50, m_CraftItem.ItemId, m_CraftItem.ItemHue); + + if (m_CraftItem.IsMarkable(type)) + { + AddHtmlLocalized( + 170, + 302 + m_OtherCount++ * 20, + 310, + 18, + 1044059, // This item may hold its maker's mark + LabelColor + ); + m_ShowExceptionalChance = true; + } + } + + public void DrawSkill() + { + for (var i = 0; i < m_CraftItem.Skills.Count; i++) + { + var skill = m_CraftItem.Skills[i]; + var minSkill = Math.Max(skill.MinSkill, 0); + + AddHtmlLocalized(170, 132 + i * 20, 200, 18, AosSkillBonuses.GetLabel(skill.SkillToMake), LabelColor); + AddLabel(430, 132 + i * 20, LabelHue, $"{minSkill:F1}"); + } + + var res = m_CraftItem.UseSubRes2 ? m_CraftSystem.CraftSubRes2 : m_CraftSystem.CraftSubRes; + var resIndex = -1; + + var context = m_CraftSystem.GetContext(m_From); + + if (context != null) + { + resIndex = m_CraftItem.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; + } + + var chance = m_CraftItem.GetSuccessChance( + m_From, + resIndex > -1 ? res.GetAt(resIndex).ItemType : null, + m_CraftSystem, + false, + out _ ); - } - } - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; + AddHtmlLocalized(170, 80, 250, 18, 1044057, LabelColor); // Success Chance: + AddLabel(430, 80, LabelHue, $"{Math.Clamp(chance, 0, 1) * 100:F1}%"); - // Back Button - if (info.ButtonID == 0) - { - CraftItem.ShowCraftMenu(from, _craftSystem, _tool, null); - } - else // Make Button - { - var num = _craftSystem.CanCraft(from, _tool, _craftItem.ItemType); - - if (num > 0) + if (m_ShowExceptionalChance) { - CraftItem.ShowCraftMenu(from, _craftSystem, _tool, num); + var exceptChance = Math.Clamp(m_CraftItem.GetExceptionalChance(m_CraftSystem, chance, m_From), 0, 1.0); + + AddHtmlLocalized(170, 100, 250, 18, 1044058, 32767); // Exceptional Chance: + AddLabel(430, 100, LabelHue, $"{exceptChance * 100:F1}%"); } - else + } + + public void DrawResource() + { + var retainedColor = false; + + var context = m_CraftSystem.GetContext(m_From); + + var res = m_CraftItem.UseSubRes2 ? m_CraftSystem.CraftSubRes2 : m_CraftSystem.CraftSubRes; + var resIndex = -1; + + if (context != null) { - Type type = null; + resIndex = m_CraftItem.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; + } - var context = _craftSystem.GetContext(from); + var cropScroll = m_CraftItem.Resources.Count > 1 + && m_CraftItem.Resources[^1].ItemType == typeofBlankScroll + && typeofSpellScroll.IsAssignableFrom(m_CraftItem.ItemType); - if (context != null) + for (var i = 0; i < m_CraftItem.Resources.Count - (cropScroll ? 1 : 0) && i < 4; i++) + { + var craftResource = m_CraftItem.Resources[i]; + + var type = craftResource.ItemType; + var nameString = craftResource.Name.String; + var nameNumber = craftResource.Name.Number; + + // Resource Mutation + if (type == res.ResType && resIndex > -1) { - var res = _craftItem.UseSubRes2 ? _craftSystem.CraftSubRes2 : _craftSystem.CraftSubRes; - var resIndex = _craftItem.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; + var subResource = res.GetAt(resIndex); - if (resIndex > -1) + type = subResource.ItemType; + + nameString = subResource.Name.String; + nameNumber = subResource.GenericNameNumber; + + if (nameNumber <= 0) { - type = res.GetAt(resIndex).ItemType; + nameNumber = subResource.Name.Number; } } + // ****************** - _craftSystem.CreateItem(from, _craftItem.ItemType, type, _tool, _craftItem); + if (!retainedColor && m_CraftItem.RetainsColorFrom(m_CraftSystem, type)) + { + retainedColor = true; + AddHtmlLocalized( + 170, + 302 + m_OtherCount++ * 20, + 310, + 18, + 1044152, // * The item retains the color of this material + LabelColor + ); + AddLabel(500, 219 + i * 20, LabelHue, "*"); + } + + if (nameNumber > 0) + { + AddHtmlLocalized(170, 219 + i * 20, 310, 18, nameNumber, LabelColor); + } + else + { + AddLabel(170, 219 + i * 20, LabelHue, nameString); + } + + AddLabel(430, 219 + i * 20, LabelHue, craftResource.Amount.ToString()); + } + + if (m_CraftItem.NameNumber == 1041267) // runebook + { + AddHtmlLocalized(170, 219 + m_CraftItem.Resources.Count * 20, 310, 18, 1044447, LabelColor); + AddLabel(430, 219 + m_CraftItem.Resources.Count * 20, LabelHue, "1"); + } + + if (cropScroll) + { + AddHtmlLocalized( + 170, + 302 + m_OtherCount++ * 20, + 360, + 18, + 1044379, // Inscribing scrolls also requires a blank scroll and mana. + LabelColor + ); + } + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + // Back Button + if (info.ButtonID == 0) + { + var craftGump = new CraftGump(m_From, m_CraftSystem, m_Tool, null); + m_From.SendGump(craftGump); + } + else // Make Button + { + var num = m_CraftSystem.CanCraft(m_From, m_Tool, m_CraftItem.ItemType); + + if (num > 0) + { + m_From.SendGump(new CraftGump(m_From, m_CraftSystem, m_Tool, num)); + } + else + { + Type type = null; + + var context = m_CraftSystem.GetContext(m_From); + + if (context != null) + { + var res = m_CraftItem.UseSubRes2 ? m_CraftSystem.CraftSubRes2 : m_CraftSystem.CraftSubRes; + var resIndex = m_CraftItem.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; + + if (resIndex > -1) + { + type = res.GetAt(resIndex).ItemType; + } + } + + m_CraftSystem.CreateItem(m_From, m_CraftItem.ItemType, type, m_Tool, m_CraftItem); + } } } } diff --git a/Projects/UOContent/Engines/Craft/Core/CraftItem.cs b/Projects/UOContent/Engines/Craft/Core/CraftItem.cs index e99760bbc..42e7856f7 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftItem.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftItem.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; -using Server.Collections; using Server.Commands; using Server.Factions; using Server.Gumps; using Server.Items; using Server.Logging; using Server.Mobiles; -using Server.Engines.Craft.T2A; namespace Server.Engines.Craft { @@ -61,37 +59,25 @@ namespace Server.Engines.Craft 0x192C, 0x192D, 0x192E, 0x129F, 0x1930, 0x1931, 0x1932, 0x1934 }; - private static readonly Type[][] m_TypesTable = InitTypesTable(); - - private static Type[][] InitTypesTable() + private static readonly Type[][] m_TypesTable = { - List types = - [ - [typeof(Log), typeof(Board)], - [typeof(HeartwoodLog), typeof(HeartwoodBoard)], - [typeof(BloodwoodLog), typeof(BloodwoodBoard)], - [typeof(FrostwoodLog), typeof(FrostwoodBoard)], - [typeof(OakLog), typeof(OakBoard)], - [typeof(AshLog), typeof(AshBoard)], - [typeof(YewLog), typeof(YewBoard)], - [typeof(Leather), typeof(Hides)], - [typeof(SpinedLeather), typeof(SpinedHides)], - [typeof(HornedLeather), typeof(HornedHides)], - [typeof(BarbedLeather), typeof(BarbedHides)], - [typeof(Cloth), typeof(UncutCloth)], - [typeof(CheeseWheel), typeof(CheeseWedge)], - [typeof(Pumpkin), typeof(SmallPumpkin)], - [typeof(WoodenBowlOfPeas), typeof(PewterBowlOfPeas)] - ]; - - // Gump-based crafting allows blank scrolls as a substitute for blank maps in cartography - if (!T2ACraftSystem.Enabled) - { - types.Add([typeof(BlankMap), typeof(BlankScroll)]); - } - - return types.ToArray(); - } + new[] { typeof(Log), typeof(Board) }, + new[] { typeof(HeartwoodLog), typeof(HeartwoodBoard) }, + new[] { typeof(BloodwoodLog), typeof(BloodwoodBoard) }, + new[] { typeof(FrostwoodLog), typeof(FrostwoodBoard) }, + new[] { typeof(OakLog), typeof(OakBoard) }, + new[] { typeof(AshLog), typeof(AshBoard) }, + new[] { typeof(YewLog), typeof(YewBoard) }, + new[] { typeof(Leather), typeof(Hides) }, + new[] { typeof(SpinedLeather), typeof(SpinedHides) }, + new[] { typeof(HornedLeather), typeof(HornedHides) }, + new[] { typeof(BarbedLeather), typeof(BarbedHides) }, + new[] { typeof(BlankMap), typeof(BlankScroll) }, + new[] { typeof(Cloth), typeof(UncutCloth) }, + new[] { typeof(CheeseWheel), typeof(CheeseWedge) }, + new[] { typeof(Pumpkin), typeof(SmallPumpkin) }, + new[] { typeof(WoodenBowlOfPeas), typeof(PewterBowlOfPeas) } + }; private static readonly Type[] m_ColoredItemTable = { @@ -446,11 +432,11 @@ namespace Server.Engines.Craft public static bool IsQuantityType(Type[][] types) { - for (var i = 0; i < types.Length; ++i) + for (int i = 0; i < types.Length; ++i) { - var check = types[i]; + Type[] check = types[i]; - for (var j = 0; j < check.Length; ++j) + for (int j = 0; j < check.Length; ++j) { if (typeof(IHasQuantity).IsAssignableFrom(check[j])) { @@ -682,10 +668,6 @@ namespace Server.Engines.Craft { amounts[i] = 0; } - else if (isFailure && !Core.UOTD) - { - amounts[i] -= amounts[i] / 2; - } } // We adjust the amount of each resource to consume the max possible @@ -901,7 +883,7 @@ namespace Server.Engines.Craft var minChance = craftSystem.GetChanceAtMin(this); - var chance = minChance + (valMainSkill - minMainSkill) / (maxMainSkill - minMainSkill) * (1.0 - minChance); + double chance = minChance + (valMainSkill - minMainSkill) / (maxMainSkill - minMainSkill) * (1.0 - minChance); if (from.Talisman is BaseTalisman talisman && talisman.Skill == craftSystem.MainSkill) { @@ -939,22 +921,14 @@ namespace Server.Engines.Craft if (!allRequiredSkills || chance <= 0.0) { from.EndAction(); - if (T2ACraftSystem.Enabled) - { - from.SendAsciiMessage("You lack the required skill to craft this item."); - } - else - { - from.SendGump( - new CraftGump( - from, - craftSystem, - tool, - 1044153 // You don't have the required skills to attempt this item. - ) - ); - } - + from.SendGump( + new CraftGump( + from, + craftSystem, + tool, + 1044153 // You don't have the required skills to attempt this item. + ) + ); return; } @@ -977,7 +951,7 @@ namespace Server.Engines.Craft if (badCraft > 0) { from.EndAction(); - ShowCraftMenu(from, craftSystem, tool, badCraft); + from.SendGump(new CraftGump(from, craftSystem, tool, badCraft)); return; } @@ -988,7 +962,7 @@ namespace Server.Engines.Craft if (!ConsumeRes(from, typeRes, craftSystem, ref resHue, ref maxAmount, ConsumeType.None, ref message)) { from.EndAction(); - ShowCraftMenu(from, craftSystem, tool, message); + from.SendGump(new CraftGump(from, craftSystem, tool, message)); return; } @@ -997,7 +971,7 @@ namespace Server.Engines.Craft if (!ConsumeAttributes(from, ref message, false)) { from.EndAction(); - ShowCraftMenu(from, craftSystem, tool, message); + from.SendGump(new CraftGump(from, craftSystem, tool, message)); return; } @@ -1012,293 +986,6 @@ namespace Server.Engines.Craft new InternalTimer(from, craftSystem, this, typeRes, tool, iRandom).Start(); } - /// - /// Hue-aware craft entry point. Used by tailoring to carry the targeted resource hue - /// through the craft timer to CompleteCraft, ensuring only matching-hue resources are consumed. - /// - public void Craft(Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, int resHue) - { - if (!from.BeginAction()) - { - from.SendLocalizedMessage(500119); // You must wait to perform another action - return; - } - - if (RequiredExpansion != Expansion.None && from.NetState?.SupportsExpansion(RequiredExpansion) != true) - { - from.EndAction(); - from.SendGump( - new CraftGump( - from, - craftSystem, - tool, - RequiredExpansionMessage(RequiredExpansion) - ) - ); - return; - } - - var chance = GetSuccessChance(from, typeRes, craftSystem, false, out var allRequiredSkills); - - if (!allRequiredSkills || chance <= 0.0) - { - from.EndAction(); - from.SendAsciiMessage("You lack the required skill to craft this item."); - return; - } - - if (Recipe != null && (from as PlayerMobile)?.HasRecipe(Recipe) == false) - { - from.EndAction(); - from.SendGump( - new CraftGump( - from, - craftSystem, - tool, - 1072847 // You must learn that recipe from a scroll. - ) - ); - return; - } - - var badCraft = craftSystem.CanCraft(from, tool, ItemType); - - if (badCraft > 0) - { - from.EndAction(); - ShowCraftMenu(from, craftSystem, tool, badCraft); - return; - } - - // Dry run: check hued resources are available - if (!CheckHuedRes(from, typeRes, craftSystem, resHue)) - { - from.EndAction(); - // You don't have the resources required to make that item. - from.SendLocalizedMessage(502925); - return; - } - - TextDefinition message = null; - if (!ConsumeAttributes(from, ref message, false)) - { - from.EndAction(); - ShowCraftMenu(from, craftSystem, tool, message); - return; - } - - var context = craftSystem.GetContext(from); - context?.OnMade(this); - - var iMin = craftSystem.MinCraftEffect; - var iMax = craftSystem.MaxCraftEffect - iMin + 1; - var iRandom = Utility.Random(iMax); - iRandom += iMin + 1; - new InternalTimer(from, craftSystem, this, typeRes, tool, iRandom, resHue).Start(); - } - - /// - /// Checks if the backpack has enough of the specified resource type matching the target hue. - /// Used as a dry-run check before starting the craft timer. - /// - private bool CheckHuedRes(Mobile from, Type typeRes, CraftSystem craftSystem, int targetHue) - { - var ourPack = from.Backpack; - if (ourPack == null) - { - return false; - } - - var resCol = UseSubRes2 ? craftSystem.CraftSubRes2 : craftSystem.CraftSubRes; - - for (var i = 0; i < Resources.Count; i++) - { - var craftRes = Resources[i]; - var baseType = craftRes.ItemType; - - // Resource mutation - if (baseType == resCol.ResType && typeRes != null) - { - baseType = typeRes; - } - - // For the primary resource, count only items matching the target hue - if (targetHue >= 0 && i == 0) - { - var amount = GetHuedAmount(ourPack, baseType, targetHue); - if (amount < craftRes.Amount) - { - return false; - } - } - else - { - if (ourPack.GetAmount(baseType) < craftRes.Amount) - { - return false; - } - } - } - - return true; - } - - /// - /// Consumes resources from the backpack, restricting the primary resource to items matching the target hue. - /// Returns true on success. On success, the exact targetHue is used as the resHue output. - /// - private bool ConsumeHuedRes( - Mobile from, Type typeRes, CraftSystem craftSystem, int targetHue, - ref int resHue, ConsumeType consumeType - ) - { - var ourPack = from.Backpack; - if (ourPack == null) - { - return false; - } - - if (NeedHeat && !Find(from, m_HeatSources)) - { - return false; - } - - if (NeedOven && !Find(from, m_Ovens)) - { - return false; - } - - if (NeedMill && !Find(from, m_Mills)) - { - return false; - } - - var resCol = UseSubRes2 ? craftSystem.CraftSubRes2 : craftSystem.CraftSubRes; - - for (var i = 0; i < Resources.Count; i++) - { - var craftRes = Resources[i]; - var baseType = craftRes.ItemType; - var amount = craftRes.Amount; - - // Resource mutation - if (baseType == resCol.ResType && typeRes != null) - { - baseType = typeRes; - - var subResource = resCol.SearchFor(baseType); - if (subResource != null && from.Skills[craftSystem.MainSkill].Base < subResource.RequiredSkill) - { - return false; - } - } - - if (consumeType == ConsumeType.Half) - { - amount = Math.Max(1, amount / 2); - } - - // For the primary resource, filter by hue - if (targetHue >= 0 && i == 0) - { - if (consumeType == ConsumeType.None) - { - // Dry run: just check amount - if (GetHuedAmount(ourPack, baseType, targetHue) < amount) - { - return false; - } - } - else - { - // Actual consumption: consume only matching-hue items - if (!ConsumeHuedAmount(ourPack, baseType, targetHue, amount)) - { - return false; - } - } - } - else - { - // Non-hued resource: normal consumption - if (consumeType == ConsumeType.None) - { - if (ourPack.GetAmount(baseType) < amount) - { - return false; - } - } - else - { - if (!ourPack.ConsumeTotal(baseType, amount)) - { - return false; - } - } - } - } - - resHue = targetHue; - return true; - } - - private static int GetHuedAmount(Container pack, Type type, int hue) - { - var total = 0; - - foreach (var item in pack.FindItems(true)) - { - if (item.Hue == hue && type.IsInstanceOfType(item)) - { - total += item.Amount; - } - } - - return total; - } - - private static bool ConsumeHuedAmount(Container pack, Type type, int hue, int amount) - { - var remaining = amount; - using var toDelete = PooledRefList.Create(); - - foreach (var item in pack.FindItems(true)) - { - if (remaining <= 0) - { - break; - } - - if (item.Hue != hue || !type.IsInstanceOfType(item)) - { - continue; - } - - if (item.Amount <= remaining) - { - remaining -= item.Amount; - toDelete.Add(item); - } - else - { - item.Amount -= remaining; - remaining = 0; - } - } - - if (remaining > 0) - { - return false; - } - - for (var i = 0; i < toDelete.Count; i++) - { - toDelete[i].Delete(); - } - - return true; - } - private static TextDefinition RequiredExpansionMessage(Expansion expansion) { return expansion switch @@ -1320,7 +1007,7 @@ namespace Server.Engines.Craft { if (tool?.Deleted == false && tool.UsesRemaining > 0) { - ShowCraftMenu(from, craftSystem, tool, badCraft); + from.SendGump(new CraftGump(from, craftSystem, tool, badCraft)); } else { @@ -1349,7 +1036,7 @@ namespace Server.Engines.Craft { if (tool?.Deleted == false && tool.UsesRemaining > 0) { - ShowCraftMenu(from, craftSystem, tool, checkMessage); + from.SendGump(new CraftGump(from, craftSystem, tool, checkMessage)); } else if (checkMessage.Number > 0) { @@ -1389,7 +1076,7 @@ namespace Server.Engines.Craft { if (tool?.Deleted == false && tool.UsesRemaining > 0) { - ShowCraftMenu(from, craftSystem, tool, message); + from.SendGump(new CraftGump(from, craftSystem, tool, message)); } else if (message != null) { @@ -1406,32 +1093,29 @@ namespace Server.Engines.Craft return; } - if (tool != null) - { - tool.UsesRemaining--; + tool.UsesRemaining--; - if (craftSystem is DefBlacksmithy) + if (craftSystem is DefBlacksmithy) + { + var hammer = from.FindItemOnLayer(Layer.OneHanded); + if (hammer != null && hammer != tool) { - var hammer = from.FindItemOnLayer(Layer.OneHanded); - if (hammer != null && hammer != tool) + hammer.UsesRemaining--; + if (hammer.UsesRemaining < 1) { - hammer.UsesRemaining--; - if (hammer.UsesRemaining < 1) - { - hammer.Delete(); - } + hammer.Delete(); } } + } - if (tool.UsesRemaining < 1 && tool.BreakOnDepletion) - { - toolBroken = true; - } + if (tool.UsesRemaining < 1 && tool.BreakOnDepletion) + { + toolBroken = true; + } - if (toolBroken) - { - tool.Delete(); - } + if (toolBroken) + { + tool.Delete(); } Item item; @@ -1451,9 +1135,6 @@ namespace Server.Engines.Craft if (item != null) { - // Stamped here, not in OnCraft: most craftables do not implement ICraftable. - item.PlayerConstructed = true; - if (item is ICraftable craftable) { endquality = craftable.OnCraft(quality, makersMark, from, craftSystem, typeRes, tool, this, resHue); @@ -1548,18 +1229,7 @@ namespace Server.Engines.Craft } else if (tool?.Deleted == false && tool.UsesRemaining > 0) { - if (T2ACraftSystem.Enabled) - { - if (num > 0) - { - from.SendLocalizedMessage(num); - } - ShowCraftMenu(from, craftSystem, tool); - } - else - { - ShowCraftMenu(from, craftSystem, tool, num); - } + from.SendGump(new CraftGump(from, craftSystem, tool, num)); } else if (num > 0) { @@ -1573,12 +1243,13 @@ namespace Server.Engines.Craft { if (tool?.Deleted == false && tool.UsesRemaining > 0) { - from.SendAsciiMessage("You lack the required skill to craft this item."); + from.SendGump(new CraftGump(from, craftSystem, tool, 1044153)); } else { from.SendLocalizedMessage(1044153); // You don't have the required skills to attempt this item. } + return; } @@ -1589,7 +1260,7 @@ namespace Server.Engines.Craft { if (tool?.Deleted == false && tool.UsesRemaining > 0) { - ShowCraftMenu(from, craftSystem, tool, message); + from.SendGump(new CraftGump(from, craftSystem, tool, message)); } else if (message != null) { @@ -1606,229 +1277,24 @@ namespace Server.Engines.Craft return; } - if (tool != null) + tool.UsesRemaining--; + + if (tool.UsesRemaining < 1 && tool.BreakOnDepletion) { - tool.UsesRemaining--; + toolBroken = true; + } - if (tool.UsesRemaining < 1 && tool.BreakOnDepletion) - { - toolBroken = true; - } - - if (toolBroken) - { - tool.Delete(); - } + if (toolBroken) + { + tool.Delete(); } // SkillCheck failed. num = craftSystem.PlayEndingEffect(from, true, true, toolBroken, endquality, false, this); - if (tool?.Deleted == false && tool.UsesRemaining > 0) + if (!tool.Deleted && tool.UsesRemaining > 0) { - ShowCraftMenu(from, craftSystem, tool, num); - } - else if (num > 0) - { - from.SendLocalizedMessage(num); - } - } - - /// - /// Hue-aware CompleteCraft. Uses ConsumeHuedRes to consume only resources matching - /// the target hue, and applies that hue to the crafted item. - /// - public void CompleteCraft( - int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, - BaseTool tool, CustomCraft customCraft, int targetHue - ) - { - var badCraft = craftSystem.CanCraft(from, tool, ItemType); - - if (badCraft > 0) - { - if (tool?.Deleted == false && tool.UsesRemaining > 0) - { - ShowCraftMenu(from, craftSystem, tool, badCraft); - } - else - { - from.SendLocalizedMessage(badCraft); - } - - return; - } - - // Dry-run check with hue filtering - var checkResHue = 0; - if (!ConsumeHuedRes(from, typeRes, craftSystem, targetHue, ref checkResHue, ConsumeType.None)) - { - if (tool?.Deleted == false && tool.UsesRemaining > 0) - { - // You don't have the resources required to make that item. - ShowCraftMenu(from, craftSystem, tool, 502925); - } - else - { - from.SendLocalizedMessage(502925); - } - - return; - } - - TextDefinition checkMessage = null; - if (!ConsumeAttributes(from, ref checkMessage, false)) - { - return; - } - - var toolBroken = false; - var endquality = 1; - var resHue = 0; - var num = 0; - - if (CheckSkills(from, typeRes, craftSystem, ref quality, out var allRequiredSkills)) - { - var consumeType = UseAllRes ? ConsumeType.Half : ConsumeType.All; - - if (!ConsumeHuedRes(from, typeRes, craftSystem, targetHue, ref resHue, consumeType)) - { - if (tool?.Deleted == false && tool.UsesRemaining > 0) - { - ShowCraftMenu(from, craftSystem, tool, 502925); - } - else - { - from.SendLocalizedMessage(502925); - } - - return; - } - - if (tool != null) - { - tool.UsesRemaining--; - - if (craftSystem is DefBlacksmithy) - { - var hammer = from.FindItemOnLayer(Layer.OneHanded); - if (hammer != null && hammer != tool) - { - hammer.UsesRemaining--; - if (hammer.UsesRemaining < 1) - { - hammer.Delete(); - } - } - } - - if (tool.UsesRemaining < 1 && tool.BreakOnDepletion) - { - toolBroken = true; - } - - if (toolBroken) - { - tool.Delete(); - } - } - - Item item; - if (customCraft != null) - { - item = customCraft.CompleteCraft(out num); - } - else - { - item = ItemType.CreateInstance(); - } - - if (item != null) - { - // Stamped here, not in OnCraft: most craftables do not implement ICraftable. - item.PlayerConstructed = true; - - if (item is ICraftable craftable) - { - endquality = craftable.OnCraft(quality, makersMark, from, craftSystem, typeRes, tool, this, resHue); - } - else if (item.Hue == 0) - { - item.Hue = resHue; - } - - from.AddToBackpack(item); - - num = craftSystem.PlayEndingEffect(from, false, true, toolBroken, endquality, false, this); - - if (T2ACraftSystem.Enabled) - { - if (tool?.Deleted == false && tool.UsesRemaining > 0) - { - if (num > 0) - { - from.SendLocalizedMessage(num); - } - - ShowCraftMenu(from, craftSystem, tool); - } - else if (num > 0) - { - from.SendLocalizedMessage(num); - } - } - else if (tool?.Deleted == false && tool.UsesRemaining > 0) - { - ShowCraftMenu(from, craftSystem, tool, num); - } - else if (num > 0) - { - from.SendLocalizedMessage(num); - } - - return; - } - } - - if (!allRequiredSkills) - { - if (tool?.Deleted == false && tool.UsesRemaining > 0) - { - from.SendAsciiMessage("You lack the required skill to craft this item."); - } - else - { - from.SendLocalizedMessage(1044153); - } - - return; - } - - var failConsumeType = UseAllRes ? ConsumeType.Half : ConsumeType.All; - - // Failure: consume resources (half on failure) - ConsumeHuedRes(from, typeRes, craftSystem, targetHue, ref resHue, failConsumeType); - - if (tool != null) - { - tool.UsesRemaining--; - - if (tool.UsesRemaining < 1 && tool.BreakOnDepletion) - { - toolBroken = true; - } - - if (toolBroken) - { - tool.Delete(); - } - } - - num = craftSystem.PlayEndingEffect(from, true, true, toolBroken, endquality, false, this); - - if (tool?.Deleted == false && tool.UsesRemaining > 0) - { - ShowCraftMenu(from, craftSystem, tool, num); + from.SendGump(new CraftGump(from, craftSystem, tool, num)); } else if (num > 0) { @@ -1844,12 +1310,11 @@ namespace Server.Engines.Craft private readonly int m_iCountMax; private readonly BaseTool m_Tool; private readonly Type m_TypeRes; - private readonly int m_ResHue; private int m_iCount; public InternalTimer( Mobile from, CraftSystem craftSystem, CraftItem craftItem, Type typeRes, BaseTool tool, - int iCountMax, int resHue = -1 + int iCountMax ) : base(TimeSpan.Zero, TimeSpan.FromSeconds(craftSystem.Delay), iCountMax) { m_From = from; @@ -1859,7 +1324,6 @@ namespace Server.Engines.Craft m_CraftSystem = craftSystem; m_TypeRes = typeRes; m_Tool = tool; - m_ResHue = resHue; } protected override void OnTick() @@ -1882,7 +1346,7 @@ namespace Server.Engines.Craft { if (m_Tool?.Deleted == false && m_Tool.UsesRemaining > 0) { - ShowCraftMenu(m_From, m_CraftSystem, m_Tool, badCraft); + m_From.SendGump(new CraftGump(m_From, m_CraftSystem, m_Tool, badCraft)); } else { @@ -1931,18 +1395,16 @@ namespace Server.Engines.Craft makersMark = m_CraftItem.IsMarkable(m_CraftItem.ItemType); } - // T2A menus always prompt for maker's mark (no auto-mark/don't-mark options) - if (makersMark && - (T2ACraftSystem.Enabled || context.MarkOption == CraftMarkOption.PromptForMark)) + if (makersMark && context.MarkOption == CraftMarkOption.PromptForMark) { m_From.SendGump( new QueryMakersMarkGump( quality, + m_From, m_CraftItem, m_CraftSystem, m_TypeRes, - m_Tool, - m_ResHue + m_Tool ) ); } @@ -1953,41 +1415,9 @@ namespace Server.Engines.Craft makersMark = false; } - if (m_ResHue >= 0) - { - m_CraftItem.CompleteCraft( - quality, makersMark, m_From, m_CraftSystem, m_TypeRes, m_Tool, null, m_ResHue - ); - } - else - { - m_CraftItem.CompleteCraft(quality, makersMark, m_From, m_CraftSystem, m_TypeRes, m_Tool, null); - } + m_CraftItem.CompleteCraft(quality, makersMark, m_From, m_CraftSystem, m_TypeRes, m_Tool, null); } } } - - public static void ShowCraftMenu(Mobile from, CraftSystem system, BaseTool tool, TextDefinition message = null) - { - if (T2ACraftSystem.Enabled) - { - // T2A: Don't reopen menu. Player double-clicks tool to restart. - if (message != null) - { - if (message.Number > 0) - { - from.SendLocalizedMessage(message.Number); - } - else if (!string.IsNullOrEmpty(message.String)) - { - from.SendMessage(message.String); - } - } - - return; - } - - from.SendGump(new CraftGump(from, system, tool, message)); - } } } diff --git a/Projects/UOContent/Engines/Craft/Core/CraftSystem.cs b/Projects/UOContent/Engines/Craft/Core/CraftSystem.cs index 0760c1979..1f4250bbb 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftSystem.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftSystem.cs @@ -54,8 +54,6 @@ namespace Server.Engines.Craft public virtual CraftECA ECA => CraftECA.ChanceMinusSixty; - public virtual bool RequiresTool => true; - public bool Resmelt { get; set; } public bool Repair { get; set; } @@ -105,17 +103,6 @@ namespace Server.Engines.Craft } } - public void CreateItem( - Mobile from, Type type, Type typeRes, BaseTool tool, CraftItem realCraftItem, int hue - ) - { - // Verify if the type is in the list of the craftable item - if (CraftItems.SearchFor(type) != null) - { - realCraftItem.Craft(from, this, typeRes, tool, hue); - } - } - public int RandomRecipe() { if (m_Recipes.Count == 0) diff --git a/Projects/UOContent/Engines/Craft/Core/Enhance.cs b/Projects/UOContent/Engines/Craft/Core/Enhance.cs index 44a78c854..58f4280cc 100644 --- a/Projects/UOContent/Engines/Craft/Core/Enhance.cs +++ b/Projects/UOContent/Engines/Craft/Core/Enhance.cs @@ -1,4 +1,5 @@ using System; +using Server.Gumps; using Server.Items; using Server.Targeting; @@ -84,8 +85,8 @@ namespace Server.Engines.Craft return EnhanceResult.BadResource; } - var resHue = 0; - var maxAmount = 0; + int resHue = 0; + int maxAmount = 0; var consumeRes = craftItem.ConsumeRes( from, @@ -347,7 +348,7 @@ namespace Server.Engines.Craft if (from.Skills[craftSystem.MainSkill].Value < res.RequiredSkill) { - CraftItem.ShowCraftMenu(from, craftSystem, tool, res.Message); + from.SendGump(new CraftGump(from, craftSystem, tool, res.Message)); } else { @@ -361,13 +362,29 @@ namespace Server.Engines.Craft } else { - CraftItem.ShowCraftMenu(from, craftSystem, tool, 1061010); + from.SendGump( + new CraftGump( + from, + craftSystem, + tool, + // You must select a special material in order to enhance an item with its properties. + 1061010 + ) + ); } } } else { - CraftItem.ShowCraftMenu(from, craftSystem, tool, 1061010); + from.SendGump( + new CraftGump( + from, + craftSystem, + tool, + // You must select a special material in order to enhance an item with its properties. + 1061010 + ) + ); } } @@ -418,7 +435,7 @@ namespace Server.Engines.Craft _ => message }; - CraftItem.ShowCraftMenu(from, m_CraftSystem, m_Tool, message); + from.SendGump(new CraftGump(from, m_CraftSystem, m_Tool, message)); } } } diff --git a/Projects/UOContent/Engines/Craft/Core/QueryMakersMarkGump.cs b/Projects/UOContent/Engines/Craft/Core/QueryMakersMarkGump.cs index b249fd4ce..60f6fec45 100644 --- a/Projects/UOContent/Engines/Craft/Core/QueryMakersMarkGump.cs +++ b/Projects/UOContent/Engines/Craft/Core/QueryMakersMarkGump.cs @@ -3,68 +3,59 @@ using Server.Gumps; using Server.Items; using Server.Network; -namespace Server.Engines.Craft; - -public class QueryMakersMarkGump : StaticGump +namespace Server.Engines.Craft { - private readonly CraftItem _craftItem; - private readonly CraftSystem _craftSystem; - private readonly int _quality; - private readonly BaseTool _tool; - private readonly Type _typeRes; - private readonly int _resHue; - - public override bool Singleton => true; - - public QueryMakersMarkGump( - int quality, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, int resHue = -1 - ) : base(100, 200) + public class QueryMakersMarkGump : Gump { - _quality = quality; - _craftItem = craftItem; - _craftSystem = craftSystem; - _typeRes = typeRes; - _tool = tool; - _resHue = resHue; - } + private readonly CraftItem m_CraftItem; + private readonly CraftSystem m_CraftSystem; + private readonly Mobile m_From; + private readonly int m_Quality; + private readonly BaseTool m_Tool; + private readonly Type m_TypeRes; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + public override bool Singleton => true; - builder.AddBackground(0, 0, 220, 170, 5054); - builder.AddBackground(10, 10, 200, 150, 3000); - - builder.AddHtmlLocalized(20, 20, 180, 80, 1018317); // Do you wish to place your maker's mark on this item? - - builder.AddHtmlLocalized(55, 100, 140, 25, 1011011); // CONTINUE - builder.AddButton(20, 100, 4005, 4007, 1); - - builder.AddHtmlLocalized(55, 125, 140, 25, 1011012); // CANCEL - builder.AddButton(20, 125, 4005, 4007, 0); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; - var makersMark = info.ButtonID == 1; - - if (makersMark) + public QueryMakersMarkGump( + int quality, Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, + BaseTool tool + ) : base(100, 200) { - from.SendLocalizedMessage(501808); // You mark the item. - } - else - { - from.SendLocalizedMessage(501809); // Cancelled mark. + m_Quality = quality; + m_From = from; + m_CraftItem = craftItem; + m_CraftSystem = craftSystem; + m_TypeRes = typeRes; + m_Tool = tool; + + AddPage(0); + + AddBackground(0, 0, 220, 170, 5054); + AddBackground(10, 10, 200, 150, 3000); + + AddHtmlLocalized(20, 20, 180, 80, 1018317); // Do you wish to place your maker's mark on this item? + + AddHtmlLocalized(55, 100, 140, 25, 1011011); // CONTINUE + AddButton(20, 100, 4005, 4007, 1); + + AddHtmlLocalized(55, 125, 140, 25, 1011012); // CANCEL + AddButton(20, 125, 4005, 4007, 0); } - if (_resHue >= 0) + public override void OnResponse(NetState sender, in RelayInfo info) { - _craftItem.CompleteCraft(_quality, makersMark, from, _craftSystem, _typeRes, _tool, null, _resHue); - } - else - { - _craftItem.CompleteCraft(_quality, makersMark, from, _craftSystem, _typeRes, _tool, null); + var makersMark = info.ButtonID == 1; + + if (makersMark) + { + m_From.SendLocalizedMessage(501808); // You mark the item. + } + else + { + m_From.SendLocalizedMessage(501809); // Cancelled mark. + } + + m_CraftItem.CompleteCraft(m_Quality, makersMark, m_From, m_CraftSystem, m_TypeRes, m_Tool, null); } } } diff --git a/Projects/UOContent/Engines/Craft/Core/Repair.cs b/Projects/UOContent/Engines/Craft/Core/Repair.cs index ed988c459..d8bb2ae7e 100644 --- a/Projects/UOContent/Engines/Craft/Core/Repair.cs +++ b/Projects/UOContent/Engines/Craft/Core/Repair.cs @@ -1,5 +1,5 @@ using System; -using Server.Engines.Craft.T2A; +using Server.Gumps; using Server.Items; using Server.Mobiles; using Server.Targeting; @@ -454,7 +454,7 @@ namespace Server.Engines.Craft if (from.Skills[skill].Value >= 50.0) { - scroll.Consume(); + scroll.Consume(1); var deed = new RepairDeed( RepairDeed.GetTypeFor(m_CraftSystem), from.Skills[skill].Value, @@ -482,15 +482,8 @@ namespace Server.Engines.Craft if (!usingDeed) { - if (T2ACraftSystem.Enabled) - { - from.SendLocalizedMessage(number); - CraftItem.ShowCraftMenu(from, m_CraftSystem, m_Tool); - } - else - { - CraftItem.ShowCraftMenu(from, m_CraftSystem, m_Tool, number); - } + var context = m_CraftSystem.GetContext(from); + from.SendGump(new CraftGump(from, m_CraftSystem, m_Tool, number)); } else { diff --git a/Projects/UOContent/Engines/Craft/Core/Resmelt.cs b/Projects/UOContent/Engines/Craft/Core/Resmelt.cs index 8c7bbc814..8ea625822 100644 --- a/Projects/UOContent/Engines/Craft/Core/Resmelt.cs +++ b/Projects/UOContent/Engines/Craft/Core/Resmelt.cs @@ -1,4 +1,5 @@ using Server.Ethics; +using Server.Gumps; using Server.Items; using Server.Targeting; @@ -19,7 +20,7 @@ namespace Server.Engines.Craft if (num > 0 && num != 1044267) { - CraftItem.ShowCraftMenu(from, craftSystem, tool, num); + from.SendGump(new CraftGump(from, craftSystem, tool, num)); } else { @@ -141,7 +142,7 @@ namespace Server.Engines.Craft } } - CraftItem.ShowCraftMenu(from, m_CraftSystem, m_Tool, num); + from.SendGump(new CraftGump(from, m_CraftSystem, m_Tool, num)); } else { @@ -172,7 +173,7 @@ namespace Server.Engines.Craft _ => 1044272 }; - CraftItem.ShowCraftMenu(from, m_CraftSystem, m_Tool, message); + from.SendGump(new CraftGump(from, m_CraftSystem, m_Tool, message)); } } } diff --git a/Projects/UOContent/Engines/Craft/DefAlchemy.cs b/Projects/UOContent/Engines/Craft/DefAlchemy.cs index ef7e4ad8a..f2cbd5a35 100644 --- a/Projects/UOContent/Engines/Craft/DefAlchemy.cs +++ b/Projects/UOContent/Engines/Craft/DefAlchemy.cs @@ -9,11 +9,6 @@ public class DefAlchemy : CraftSystem public static void Initialize() { - if (CraftSystem != null) - { - return; // Already initialized - } - CraftSystem = new DefAlchemy(); } diff --git a/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs b/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs index 51b26876a..58001d606 100644 --- a/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs +++ b/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs @@ -667,18 +667,6 @@ public class DefBlacksmithy : CraftSystem index = AddCraft(typeof(DragonChest), 1053114, 1029793, 85.0, 135.0, typeof(RedScales), 1060883, 36, 1060884); SetUseSubRes2(index, true); - if (Core.SA) - { - index = AddCraft(typeof(Boomerang), 1079508, 1095359, 75.0, 125.0, typeof(IronIngot), 1044036, 5, 1044037); - SetNeededExpansion(index, Expansion.SA); - - index = AddCraft(typeof(Cyclone), 1079508, 1095364, 75.0, 125.0, typeof(IronIngot), 1044036, 9, 1044037); - SetNeededExpansion(index, Expansion.SA); - - index = AddCraft(typeof(SoulGlaive), 1079508, 1095363, 75.0, 125.0, typeof(IronIngot), 1044036, 9, 1044037); - SetNeededExpansion(index, Expansion.SA); - } - // Set the overridable material SetSubRes(typeof(IronIngot), 1044022); diff --git a/Projects/UOContent/Engines/Craft/DefBowFletching.cs b/Projects/UOContent/Engines/Craft/DefBowFletching.cs index cd75829a1..eb6e04166 100644 --- a/Projects/UOContent/Engines/Craft/DefBowFletching.cs +++ b/Projects/UOContent/Engines/Craft/DefBowFletching.cs @@ -258,15 +258,5 @@ public class DefBowFletching : CraftSystem MarkOption = true; Repair = Core.AOS; - - SetSubRes(typeof(Log), 1072643); - - AddSubRes(typeof(Log), 1072643, 0.0, 1044041, 1072652); - AddSubRes(typeof(OakLog), 1072644, 65.0, 1044041, 1072652); - AddSubRes(typeof(AshLog), 1072645, 80.0, 1044041, 1072652); - AddSubRes(typeof(YewLog), 1072646, 95.0, 1044041, 1072652); - AddSubRes(typeof(HeartwoodLog), 1072647, 100.0, 1044041, 1072652); - AddSubRes(typeof(BloodwoodLog), 1072648, 100.0, 1044041, 1072652); - AddSubRes(typeof(FrostwoodLog), 1072649, 100.0, 1044041, 1072652); } } diff --git a/Projects/UOContent/Engines/Craft/DefCartography.cs b/Projects/UOContent/Engines/Craft/DefCartography.cs index 1442b5676..6b020e491 100644 --- a/Projects/UOContent/Engines/Craft/DefCartography.cs +++ b/Projects/UOContent/Engines/Craft/DefCartography.cs @@ -1,5 +1,4 @@ using System; -using Server.Engines.Craft.T2A; using Server.Items; namespace Server.Engines.Craft; @@ -21,23 +20,18 @@ public class DefCartography : CraftSystem public static CraftSystem CraftSystem { get; private set; } - public override bool RequiresTool => !T2ACraftSystem.Enabled; - public override double GetChanceAtMin(CraftItem item) => 0.0; public override int CanCraft(Mobile from, BaseTool tool, Type itemType) { - if (RequiresTool) + if (tool?.Deleted != false || tool.UsesRemaining < 0) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } + return 1044038; // You have worn out your tool! + } - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } + if (!BaseTool.CheckAccessible(tool, from)) + { + return 1044263; // The tool must be on your person to use. } return 0; diff --git a/Projects/UOContent/Engines/Craft/DefInscription.cs b/Projects/UOContent/Engines/Craft/DefInscription.cs index 34f421a8f..d4b33b717 100644 --- a/Projects/UOContent/Engines/Craft/DefInscription.cs +++ b/Projects/UOContent/Engines/Craft/DefInscription.cs @@ -1,6 +1,5 @@ using System; using Server.Engines.BulkOrders; -using Server.Engines.Craft.T2A; using Server.Items; using Server.Spells; @@ -40,23 +39,18 @@ public class DefInscription : CraftSystem public static CraftSystem CraftSystem { get; private set; } - public override bool RequiresTool => !T2ACraftSystem.Enabled; - public override double GetChanceAtMin(CraftItem item) => 0.0; public override int CanCraft(Mobile from, BaseTool tool, Type typeItem) { - if (RequiresTool) + if (tool?.Deleted != false || tool.UsesRemaining < 0) { - if (tool?.Deleted != false || tool.UsesRemaining < 0) - { - return 1044038; // You have worn out your tool! - } + return 1044038; // You have worn out your tool! + } - if (!BaseTool.CheckAccessible(tool, from)) - { - return 1044263; // The tool must be on your person to use. - } + if (!BaseTool.CheckAccessible(tool, from)) + { + return 1044263; // The tool must be on your person to use. } var scroll = typeItem?.CreateEntityInstance(); diff --git a/Projects/UOContent/Engines/Craft/DefTailoring.cs b/Projects/UOContent/Engines/Craft/DefTailoring.cs index 423896f9b..3aedde3fb 100644 --- a/Projects/UOContent/Engines/Craft/DefTailoring.cs +++ b/Projects/UOContent/Engines/Craft/DefTailoring.cs @@ -664,13 +664,6 @@ public class DefTailoring : CraftSystem AddSubRes(typeof(HornedLeather), 1049152, 80.0, 1044462, 1049311); AddSubRes(typeof(BarbedLeather), 1049153, 99.0, 1044462, 1049311); - // Add Bolt of Cloth for pre-AOS expansions only - if (!Core.AOS) - { - index = AddCraft(typeof(BoltOfCloth), 1015283, 1044286, 0.0, 25.0, typeof(Cloth), 1044286, 50, 1044287); - // 1015283: group (Sashes & Aprons), 1044286: name (Cloth), 0.0-25.0: skill, 50: amount, 1044287: message - } - MarkOption = true; Repair = Core.AOS; CanEnhance = Core.AOS; diff --git a/Projects/UOContent/Engines/Craft/DefTinkering.cs b/Projects/UOContent/Engines/Craft/DefTinkering.cs index 4c41d52df..836ca0978 100644 --- a/Projects/UOContent/Engines/Craft/DefTinkering.cs +++ b/Projects/UOContent/Engines/Craft/DefTinkering.cs @@ -1,6 +1,6 @@ using System; -using Server.Engines.Craft.T2A; using Server.Factions; +using Server.Gumps; using Server.Items; using Server.Targeting; @@ -71,7 +71,7 @@ public class DefTinkering : CraftSystem public override bool RetainsColorFrom(CraftItem item, Type type) { - if (!Core.UOTD || !type.IsSubclassOf(typeof(BaseIngot))) + if (!type.IsSubclassOf(typeof(BaseIngot))) { return false; } @@ -377,30 +377,15 @@ public class DefTinkering : CraftSystem SetNeededExpansion(index, Expansion.SE); } - if (!T2ACraftSystem.Enabled) - { - // AOS+ jewelry with gem resources - AddJewelrySet(GemType.StarSapphire, typeof(StarSapphire)); - AddJewelrySet(GemType.Emerald, typeof(Emerald)); - AddJewelrySet(GemType.Sapphire, typeof(Sapphire)); - AddJewelrySet(GemType.Ruby, typeof(Ruby)); - AddJewelrySet(GemType.Citrine, typeof(Citrine)); - AddJewelrySet(GemType.Amethyst, typeof(Amethyst)); - AddJewelrySet(GemType.Tourmaline, typeof(Tourmaline)); - AddJewelrySet(GemType.Amber, typeof(Amber)); - AddJewelrySet(GemType.Diamond, typeof(Diamond)); - } - else - { - // T2A jewelry — ingot-only resources, gem selected via targeting - AddCraft(typeof(GoldNecklace), 1044049, "gold necklace", 40.0, 90.0, typeof(IronIngot), 1044036, 2, 1044037); - AddCraft(typeof(SilverNecklace), 1044049, "silver necklace", 40.0, 90.0, typeof(IronIngot), 1044036, 2, 1044037); - AddCraft(typeof(GoldEarrings), 1044049, "gold earrings", 40.0, 90.0, typeof(IronIngot), 1044036, 2, 1044037); - AddCraft(typeof(SilverEarrings), 1044049, "silver earrings", 40.0, 90.0, typeof(IronIngot), 1044036, 2, 1044037); - AddCraft(typeof(GoldRing), 1044049, "gold ring", 40.0, 90.0, typeof(IronIngot), 1044036, 2, 1044037); - AddCraft(typeof(SilverRing), 1044049, "silver ring", 40.0, 90.0, typeof(IronIngot), 1044036, 2, 1044037); - AddCraft(typeof(WeddingRing), 1044049, "wedding ring", 40.0, 90.0, typeof(IronIngot), 1044036, 2, 1044037); - } + AddJewelrySet(GemType.StarSapphire, typeof(StarSapphire)); + AddJewelrySet(GemType.Emerald, typeof(Emerald)); + AddJewelrySet(GemType.Sapphire, typeof(Sapphire)); + AddJewelrySet(GemType.Ruby, typeof(Ruby)); + AddJewelrySet(GemType.Citrine, typeof(Citrine)); + AddJewelrySet(GemType.Amethyst, typeof(Amethyst)); + AddJewelrySet(GemType.Tourmaline, typeof(Tourmaline)); + AddJewelrySet(GemType.Amber, typeof(Amber)); + AddJewelrySet(GemType.Diamond, typeof(Diamond)); index = AddCraft(typeof(AxleGears), 1044051, 1024177, 0.0, 0.0, typeof(Axle), 1044169, 1, 1044253); AddRes(index, typeof(Gears), 1044254, 1, 1044253); @@ -729,7 +714,7 @@ public abstract class TrapCraft : CustomCraft if (tool?.Deleted == false && tool.UsesRemaining > 0) { - CraftItem.ShowCraftMenu(from, m_TrapCraft.CraftSystem, tool, message); + from.SendGump(new CraftGump(from, m_TrapCraft.CraftSystem, tool, message)); } else if (message > 0) { diff --git a/Projects/UOContent/Engines/Craft/T2A/AlchemyMenu.cs b/Projects/UOContent/Engines/Craft/T2A/AlchemyMenu.cs deleted file mode 100644 index fedc0639f..000000000 --- a/Projects/UOContent/Engines/Craft/T2A/AlchemyMenu.cs +++ /dev/null @@ -1,262 +0,0 @@ -using System; -using Server.Items; -using Server.Menus.ItemLists; -using Server.Network; - -namespace Server.Engines.Craft.T2A; - -public class AlchemyMenu : ItemListMenu -{ - private enum Category - { - Main, - Refresh, - Agility, - NightSight, - Heal, - Strength, - Poison, - Cure, - Explosion - } - - private static readonly Type[] RefreshTypes = [typeof(RefreshPotion), typeof(TotalRefreshPotion)]; - - private static readonly Type[] AgilityTypes = [typeof(AgilityPotion), typeof(GreaterAgilityPotion)]; - - private static readonly Type[] NightSightTypes = [typeof(NightSightPotion)]; - - private static readonly Type[] HealTypes = [typeof(LesserHealPotion), typeof(HealPotion), typeof(GreaterHealPotion)]; - - private static readonly Type[] StrengthTypes = [typeof(StrengthPotion), typeof(GreaterStrengthPotion)]; - - private static readonly Type[] PoisonTypes = - [ - typeof(LesserPoisonPotion), typeof(PoisonPotion), typeof(GreaterPoisonPotion), typeof(DeadlyPoisonPotion) - ]; - - private static readonly Type[] CureTypes = [typeof(LesserCurePotion), typeof(CurePotion), typeof(GreaterCurePotion)]; - - private static readonly Type[] ExplosionTypes = - [ - typeof(LesserExplosionPotion), typeof(ExplosionPotion), typeof(GreaterExplosionPotion) - ]; - - private static ItemListEntry[] _mainEntries; - private static ItemListEntry[] _refreshEntries; - private static ItemListEntry[] _agilityEntries; - private static ItemListEntry[] _nightSightEntries; - private static ItemListEntry[] _healEntries; - private static ItemListEntry[] _strengthEntries; - private static ItemListEntry[] _poisonEntries; - private static ItemListEntry[] _cureEntries; - private static ItemListEntry[] _explosionEntries; - - private readonly Category _category; - private readonly BaseTool _tool; - - public AlchemyMenu(Mobile from, BaseTool tool) : this(from, tool, Category.Main) - { - } - - private static string GetQuestion(Category category) => category switch - { - Category.Main => "What kind of potion?", - _ => "Which potion would you like to make?" - }; - - private AlchemyMenu(Mobile from, BaseTool tool, Category category) - : base(GetQuestion(category), BuildFilteredEntries(from, category)) - { - _tool = tool; - _category = category; - } - - private static string FormatItemName(Type type) - { - var name = type.Name; - - if (name.EndsWith("Potion")) - { - name = name[..^6]; - } - - Span buffer = stackalloc char[name.Length * 2]; - var pos = 0; - - for (var i = 0; i < name.Length; i++) - { - if (i > 0 && char.IsUpper(name[i])) - { - buffer[pos++] = ' '; - } - - buffer[pos++] = char.ToLower(name[i]); - } - - return new string(buffer[..pos]); - } - - private static ItemListEntry[] BuildStaticEntries(Type[] types) - { - var entries = new ItemListEntry[types.Length]; - var count = 0; - var craftItems = DefAlchemy.CraftSystem.CraftItems; - - for (var i = 0; i < types.Length; i++) - { - var itemDef = craftItems.SearchFor(types[i]); - if (itemDef == null) - { - continue; - } - - entries[count++] = new ItemListEntry(FormatItemName(types[i]), itemDef.ItemId, 0, i); - } - - if (count < entries.Length) - { - Array.Resize(ref entries, count); - } - - return entries; - } - - private static ItemListEntry[] GetStaticEntries(Category category) => category switch - { - Category.Main => Main(), - Category.Refresh => _refreshEntries ??= BuildStaticEntries(RefreshTypes), - Category.Agility => _agilityEntries ??= BuildStaticEntries(AgilityTypes), - Category.NightSight => _nightSightEntries ??= BuildStaticEntries(NightSightTypes), - Category.Heal => _healEntries ??= BuildStaticEntries(HealTypes), - Category.Strength => _strengthEntries ??= BuildStaticEntries(StrengthTypes), - Category.Poison => _poisonEntries ??= BuildStaticEntries(PoisonTypes), - Category.Cure => _cureEntries ??= BuildStaticEntries(CureTypes), - Category.Explosion => _explosionEntries ??= BuildStaticEntries(ExplosionTypes), - _ => null - }; - - private static Type[] GetTypes(Category category) => category switch - { - Category.Refresh => RefreshTypes, - Category.Agility => AgilityTypes, - Category.NightSight => NightSightTypes, - Category.Heal => HealTypes, - Category.Strength => StrengthTypes, - Category.Poison => PoisonTypes, - Category.Cure => CureTypes, - Category.Explosion => ExplosionTypes, - _ => null - }; - - public static ItemListEntry[] Main() => _mainEntries ??= - [ - new ItemListEntry("Refresh", 0xF0B, 0, (int)Category.Refresh), - new ItemListEntry("Agility", 0xF08, 0, (int)Category.Agility), - new ItemListEntry("Night Sight", 0xF06, 0, (int)Category.NightSight), - new ItemListEntry("Heal", 0xF0C, 0, (int)Category.Heal), - new ItemListEntry("Strength", 0xF09, 0, (int)Category.Strength), - new ItemListEntry("Poison", 0xF0A, 0, (int)Category.Poison), - new ItemListEntry("Cure", 0xF07, 0, (int)Category.Cure), - new ItemListEntry("Explosion", 0xF0D, 0, (int)Category.Explosion) - ]; - - private static ItemListEntry[] BuildFilteredEntries(Mobile from, Category category) - { - if (category == Category.Main) - { - return BuildFilteredMainEntries(from); - } - - var types = GetTypes(category); - var staticEntries = GetStaticEntries(category); - if (types == null || staticEntries == null) - { - return []; - } - - return T2ACraftSystem.FilterEntries(from, staticEntries, types, DefAlchemy.CraftSystem); - } - - private static ItemListEntry[] BuildFilteredMainEntries(Mobile from) - { - var system = DefAlchemy.CraftSystem; - var mainStatic = Main(); - var filtered = new ItemListEntry[mainStatic.Length]; - var count = 0; - - for (var i = 0; i < mainStatic.Length; i++) - { - var entry = mainStatic[i]; - var types = GetTypes((Category)entry.CraftIndex); - if (types != null && T2ACraftSystem.AnyCraftableInCategory(from, types, system)) - { - filtered[count++] = entry; - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - private void CraftPotion(Mobile from, Type potionType) - { - if ((from.Backpack?.GetAmount(typeof(Bottle)) ?? 0) == 0) - { - from.SendAsciiMessage("You need an empty bottle to make a potion."); - return; - } - - var itemDef = DefAlchemy.CraftSystem.CraftItems.SearchFor(potionType); - - if (itemDef == null) - { - return; - } - - var num = DefAlchemy.CraftSystem.CanCraft(from, _tool, itemDef.ItemType); - - if (num > 0) - { - from.SendLocalizedMessage(num); - return; - } - - var res = itemDef.Resources[0]; - DefAlchemy.CraftSystem.CreateItem(from, itemDef.ItemType, res.ItemType, _tool, itemDef); - } - - public override void OnResponse(NetState state, int index) - { - var from = state.Mobile; - var craftIndex = Entries[index].CraftIndex; - - if (_category == Category.Main) - { - var menu = new AlchemyMenu(from, _tool, (Category)craftIndex); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything in that category."); - return; - } - - from.SendMenu(menu); - return; - } - - var types = GetTypes(_category); - if (types != null && craftIndex >= 0 && craftIndex < types.Length) - { - CraftPotion(from, types[craftIndex]); - } - } -} diff --git a/Projects/UOContent/Engines/Craft/T2A/BlacksmithMenu.cs b/Projects/UOContent/Engines/Craft/T2A/BlacksmithMenu.cs deleted file mode 100644 index a711e540e..000000000 --- a/Projects/UOContent/Engines/Craft/T2A/BlacksmithMenu.cs +++ /dev/null @@ -1,624 +0,0 @@ -using System; -using Server.Items; -using Server.Menus.ItemLists; -using Server.Network; -using Server.Targeting; - -namespace Server.Engines.Craft.T2A; - -public class BlacksmithMenu : ItemListMenu -{ - private enum Category - { - Main, - Shields, - Weapons, - Armor, - Blades, - Axes, - Maces, - Polearms, - Platemail, - Chainmail, - Ringmail, - Helmets - } - - private static readonly Type[] RingmailTypes = - [ - typeof(RingmailGloves), typeof(RingmailLegs), typeof(RingmailArms), typeof(RingmailChest) - ]; - - private static readonly Type[] ChainmailTypes = - [ - typeof(ChainCoif), typeof(ChainLegs), typeof(ChainChest) - ]; - - private static readonly Type[] PlatemailTypes = - [ - typeof(Bascinet), typeof(CloseHelm), typeof(Helmet), typeof(NorseHelm), typeof(PlateHelm), - typeof(PlateArms), typeof(PlateGloves), typeof(PlateGorget), typeof(PlateLegs), - typeof(PlateChest), typeof(FemalePlateChest) - ]; - - private static readonly Type[] ShieldTypes = - [ - typeof(Buckler), typeof(BronzeShield), typeof(HeaterShield), typeof(MetalShield), - typeof(MetalKiteShield), typeof(WoodenKiteShield) - ]; - - private static readonly Type[] BladeTypes = - [ - typeof(Broadsword), typeof(Cutlass), typeof(Dagger), typeof(Katana), - typeof(Kryss), typeof(Longsword), typeof(Scimitar), typeof(VikingSword) - ]; - - private static readonly Type[] AxeTypes = - [ - typeof(Axe), typeof(BattleAxe), typeof(DoubleAxe), typeof(ExecutionersAxe), - typeof(LargeBattleAxe), typeof(TwoHandedAxe), typeof(WarAxe) - ]; - - private static readonly Type[] PolearmTypes = - [ - typeof(Bardiche), typeof(Halberd), typeof(WarFork), typeof(ShortSpear), typeof(Spear) - ]; - - private static readonly Type[] MaceTypes = - [ - typeof(HammerPick), typeof(Mace), typeof(Maul), typeof(WarMace), typeof(WarHammer) - ]; - - // Non-category actions at the top of the main menu (Repair, Smelt). - // Category CraftIndex values are offset by this count. - private const int MainActionCount = 2; - - private static ItemListEntry[] _mainEntries; - private static ItemListEntry[] _weaponEntries; - private static ItemListEntry[] _armorEntries; - private static ItemListEntry[] _shieldEntries; - private static ItemListEntry[] _bladeEntries; - private static ItemListEntry[] _axeEntries; - private static ItemListEntry[] _maceEntries; - private static ItemListEntry[] _polearmEntries; - private static ItemListEntry[] _platemailEntries; - private static ItemListEntry[] _chainmailEntries; - private static ItemListEntry[] _ringmailEntries; - - private readonly Category _category; - private readonly BaseTool _tool; - - // Resource is always selected before any menu is shown (Lost Lands flow) - public BlacksmithMenu(Mobile from, BaseTool tool) : this(from, tool, Category.Main) - { - } - - private static string GetQuestion(Category category) => category switch - { - Category.Main => "What would you like to do?", - Category.Armor => "What kind of armor?", - Category.Shields => "What kind of shield?", - Category.Weapons => "What kind of weapon?", - Category.Blades => "What kind of blade?", - Category.Axes => "What kind of axe?", - Category.Polearms => "What kind of pole arm?", - Category.Maces => "What kind of bludgeoning weapon?", - Category.Ringmail => "What kind of ring armor?", - Category.Chainmail => "What kind of chain armor?", - Category.Platemail => "What kind of plate armor?", - _ => "What would you like to make?" - }; - - private BlacksmithMenu(Mobile from, BaseTool tool, Category category) - : base(GetQuestion(category), BuildFilteredEntries(from, category)) - { - _tool = tool; - _category = category; - } - - private static string FormatItemName(Type type) - { - var name = type.Name; - Span buffer = stackalloc char[name.Length * 2]; - var pos = 0; - for (var i = 0; i < name.Length; i++) - { - if (i > 0 && char.IsUpper(name[i])) - { - buffer[pos++] = ' '; - } - - buffer[pos++] = char.ToLower(name[i]); - } - - return new string(buffer[..pos]); - } - - private static ItemListEntry[] BuildStaticEntries(Type[] types, string resourceName) - { - var entries = new ItemListEntry[types.Length]; - var count = 0; - var craftItems = DefBlacksmithy.CraftSystem.CraftItems; - - for (var i = 0; i < types.Length; i++) - { - var itemDef = craftItems.SearchFor(types[i]); - if (itemDef == null) - { - continue; - } - - var name = FormatItemName(types[i]); - var res = itemDef.Resources[0]; - var itemId = itemDef.ItemId; - - if (itemId == 7033) - { - itemId = 7032; - } - - entries[count++] = new ItemListEntry($"{name} ({res.Amount} {resourceName})", itemId, 0, i); - } - - if (count < entries.Length) - { - Array.Resize(ref entries, count); - } - - return entries; - } - - private static ItemListEntry[] GetStaticEntries(Category category) => category switch - { - Category.Main => Main(), - Category.Weapons => Weapons(), - Category.Armor => Armor(), - Category.Shields => Shields(), - Category.Blades => Blades(), - Category.Axes => Axes(), - Category.Maces => Maces(), - Category.Polearms => Polearms(), - Category.Platemail => Platemail(), - Category.Chainmail => Chainmail(), - Category.Ringmail => Ringmail(), - _ => null - }; - - private static Type[] GetTypes(Category category) => category switch - { - Category.Shields => ShieldTypes, - Category.Blades => BladeTypes, - Category.Axes => AxeTypes, - Category.Maces => MaceTypes, - Category.Polearms => PolearmTypes, - Category.Platemail => PlatemailTypes, - Category.Chainmail => ChainmailTypes, - Category.Ringmail => RingmailTypes, - _ => null - }; - - // Leaf categories under Weapons - private static readonly Category[] WeaponLeafCategories = - [ - Category.Blades, Category.Axes, Category.Maces, Category.Polearms - ]; - - // Leaf categories under Armor - private static readonly Category[] ArmorLeafCategories = - [ - Category.Ringmail, Category.Chainmail, Category.Platemail - ]; - - public static ItemListEntry[] Main() => _mainEntries ??= - [ - new ItemListEntry("Repair", 0x0FAF, 0, 0), - new ItemListEntry("Smelt", 0x0FB1, 0, 1), - new ItemListEntry("Build Armor", 0x13EC, 0, (int)Category.Armor + MainActionCount), - new ItemListEntry("Build Shield", 0x1B74, 0, (int)Category.Shields + MainActionCount), - new ItemListEntry("Build Weapons", 0xF45, 0, (int)Category.Weapons + MainActionCount) - ]; - - public static ItemListEntry[] Weapons() => _weaponEntries ??= - [ - new ItemListEntry("Build Blades", 0xF61, 0, (int)Category.Blades), - new ItemListEntry("Build Axes", 0x13FB, 0, (int)Category.Axes), - new ItemListEntry("Build Pole Arms", 0xF4D, 0, (int)Category.Polearms), - new ItemListEntry("Build Bludgeoning Weapons", 0x1407, 0, (int)Category.Maces) - ]; - - public static ItemListEntry[] Armor() => _armorEntries ??= - [ - new ItemListEntry("Build Ring Armor", 0x13EC, 0, (int)Category.Ringmail), - new ItemListEntry("Build Chain Armor", 0x13BF, 0, (int)Category.Chainmail), - new ItemListEntry("Build Plate Armor", 0x1415, 0, (int)Category.Platemail) - ]; - - public static ItemListEntry[] Shields() => _shieldEntries ??= BuildStaticEntries(ShieldTypes, "ingots"); - public static ItemListEntry[] Blades() => _bladeEntries ??= BuildStaticEntries(BladeTypes, "ingots"); - public static ItemListEntry[] Axes() => _axeEntries ??= BuildStaticEntries(AxeTypes, "ingots"); - public static ItemListEntry[] Maces() => _maceEntries ??= BuildStaticEntries(MaceTypes, "ingots"); - public static ItemListEntry[] Polearms() => _polearmEntries ??= BuildStaticEntries(PolearmTypes, "ingots"); - public static ItemListEntry[] Platemail() => _platemailEntries ??= BuildStaticEntries(PlatemailTypes, "ingots"); - public static ItemListEntry[] Chainmail() => _chainmailEntries ??= BuildStaticEntries(ChainmailTypes, "ingots"); - public static ItemListEntry[] Ringmail() => _ringmailEntries ??= BuildStaticEntries(RingmailTypes, "ingots"); - - private static Type GetSelectedResourceType(Mobile from) - { - var context = DefBlacksmithy.CraftSystem.GetContext(from); - if (context?.LastResourceIndex >= 0) - { - var res = DefBlacksmithy.CraftSystem.CraftSubRes; - if (context.LastResourceIndex < res.Count) - { - return res[context.LastResourceIndex].ItemType; - } - } - - return null; - } - - private static ItemListEntry[] BuildFilteredEntries(Mobile from, Category category) - { - // Resource is always selected before any menu is shown - var selectedResType = GetSelectedResourceType(from); - - if (category == Category.Main) - { - return BuildFilteredMainEntries(from, selectedResType); - } - - if (category == Category.Weapons) - { - return BuildFilteredMidEntries(from, Weapons(), WeaponLeafCategories, selectedResType); - } - - if (category == Category.Armor) - { - return BuildFilteredMidEntries(from, Armor(), ArmorLeafCategories, selectedResType); - } - - // Leaf category: filter individual items - var types = GetTypes(category); - var staticEntries = GetStaticEntries(category); - if (types == null || staticEntries == null) - { - return []; - } - - return T2ACraftSystem.FilterEntries(from, staticEntries, types, DefBlacksmithy.CraftSystem, selectedResType); - } - - private static ItemListEntry[] BuildFilteredMainEntries(Mobile from, Type selectedResType) - { - var system = DefBlacksmithy.CraftSystem; - var mainStatic = Main(); - var filtered = new ItemListEntry[mainStatic.Length]; - var count = 0; - - for (var i = 0; i < mainStatic.Length; i++) - { - var entry = mainStatic[i]; - - // Repair and Smelt are always shown - if (entry.CraftIndex < MainActionCount) - { - filtered[count++] = entry; - continue; - } - - var cat = (Category)(entry.CraftIndex - MainActionCount); - - if (cat == Category.Shields) - { - if (T2ACraftSystem.AnyCraftableInCategory(from, ShieldTypes, system, selectedResType)) - { - filtered[count++] = entry; - } - } - else if (cat == Category.Weapons) - { - if (AnyLeafCraftable(from, system, WeaponLeafCategories, selectedResType)) - { - filtered[count++] = entry; - } - } - else if (cat == Category.Armor) - { - if (AnyLeafCraftable(from, system, ArmorLeafCategories, selectedResType)) - { - filtered[count++] = entry; - } - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - private static ItemListEntry[] BuildFilteredMidEntries( - Mobile from, ItemListEntry[] staticEntries, Category[] leafCategories, Type selectedResType - ) - { - var system = DefBlacksmithy.CraftSystem; - var filtered = new ItemListEntry[staticEntries.Length]; - var count = 0; - - for (var i = 0; i < staticEntries.Length; i++) - { - var entry = staticEntries[i]; - var leafCat = (Category)entry.CraftIndex; - var types = GetTypes(leafCat); - if (types != null && T2ACraftSystem.AnyCraftableInCategory(from, types, system, selectedResType)) - { - filtered[count++] = entry; - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - private static bool AnyLeafCraftable( - Mobile from, CraftSystem system, Category[] leafCategories, Type selectedResType = null - ) - { - for (var i = 0; i < leafCategories.Length; i++) - { - var types = GetTypes(leafCategories[i]); - if (types != null && T2ACraftSystem.AnyCraftableInCategory(from, types, system, selectedResType)) - { - return true; - } - } - - return false; - } - - private void CraftItemFromType(Mobile from, Type itemType) - { - var itemDef = DefBlacksmithy.CraftSystem.CraftItems.SearchFor(itemType); - - if (itemDef == null) - { - return; - } - - var num = DefBlacksmithy.CraftSystem.CanCraft(from, _tool, itemDef.ItemType); - if (num > 0) - { - from.SendLocalizedMessage(num); - return; - } - - var context = DefBlacksmithy.CraftSystem.GetContext(from); - var res = itemDef.UseSubRes2 - ? DefBlacksmithy.CraftSystem.CraftSubRes2 - : DefBlacksmithy.CraftSystem.CraftSubRes; - var resIndex = itemDef.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; - var type = resIndex > -1 ? res[resIndex].ItemType : null; - - DefBlacksmithy.CraftSystem.CreateItem(from, itemDef.ItemType, type, _tool, itemDef); - } - - public override void OnResponse(NetState state, int index) - { - var from = state.Mobile; - var craftIndex = Entries[index].CraftIndex; - - if (_category == Category.Main) - { - switch (craftIndex) - { - case 0: - Repair.Do(from, DefBlacksmithy.CraftSystem, _tool); - return; - case 1: - Resmelt.Do(from, DefBlacksmithy.CraftSystem, _tool); - return; - } - - var menu = new BlacksmithMenu(from, _tool, (Category)(craftIndex - MainActionCount)); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything in that category."); - return; - } - - from.SendMenu(menu); - return; - } - - if (_category is Category.Weapons or Category.Armor) - { - var menu = new BlacksmithMenu(from, _tool, (Category)craftIndex); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything in that category."); - return; - } - - from.SendMenu(menu); - return; - } - - var types = GetTypes(_category); - if (types != null && craftIndex >= 0 && craftIndex < types.Length) - { - CraftItemFromType(from, types[craftIndex]); - } - } - - public override void OnCancel(NetState state) - { - base.OnCancel(state); - } - - public static void ResourceSelection( - Mobile from, BaseTool tool, Action afterSelect, Item preTarget = null - ) - { - var res = DefBlacksmithy.CraftSystem.CraftSubRes; - - // Validate preTarget first — reject invalid targets before any auto-selection - if (preTarget != null) - { - if (TrySelectResource(from, preTarget, res, afterSelect, tool)) - { - // Valid ingot — resource selected, menu will open - return; - } - - if (preTarget is BaseArmor or BaseWeapon - && DefBlacksmithy.CraftSystem.CraftItems.SearchForSubclass(preTarget.GetType()) != null) - { - // Repairable/smeltable item — auto-select default resource and open menu - SelectDefaultResource(from, res); - afterSelect(from, tool); - return; - } - - // Invalid target — prompt for ingots - from.SendMessage("Target the ingots you wish to use."); - from.Target = new BlacksmithResourceTarget(tool, afterSelect); - return; - } - - // No target (make-last failure path) — auto-select if only one ingot type available - var availableCount = 0; - var lastAvailable = -1; - - for (var i = 0; i < res.Count; ++i) - { - if ((from.Backpack?.GetAmount(res[i].ItemType) ?? 0) > 0) - { - availableCount++; - lastAvailable = i; - } - } - - if (availableCount <= 1) - { - var context = DefBlacksmithy.CraftSystem.GetContext(from); - if (context != null && lastAvailable != -1) - { - context.LastResourceIndex = lastAvailable; - } - - afterSelect(from, tool); - } - else - { - from.SendMessage("Target the ingots you wish to use."); - from.Target = new BlacksmithResourceTarget(tool, afterSelect); - } - } - - private static void SelectDefaultResource(Mobile from, CraftSubResCol res) - { - var context = DefBlacksmithy.CraftSystem.GetContext(from); - if (context == null) - { - return; - } - - var pack = from.Backpack; - var firstAvailable = -1; - - for (var i = 0; i < res.Count; ++i) - { - if ((pack?.GetAmount(res[i].ItemType) ?? 0) > 0) - { - if (res[i].ItemType == typeof(IronIngot)) - { - context.LastResourceIndex = i; - return; - } - - if (firstAvailable == -1) - { - firstAvailable = i; - } - } - } - - if (firstAvailable != -1) - { - context.LastResourceIndex = firstAvailable; - } - } - - private static bool TrySelectResource( - Mobile from, Item item, CraftSubResCol res, Action afterSelect, BaseTool tool - ) - { - for (var i = 0; i < res.Count; ++i) - { - if (item.GetType() == res[i].ItemType) - { - var context = DefBlacksmithy.CraftSystem.GetContext(from); - if (context != null) - { - context.LastResourceIndex = i; - } - - afterSelect(from, tool); - return true; - } - } - - return false; - } -} - -public class BlacksmithResourceTarget : Target -{ - private readonly BaseTool _tool; - private readonly Action _afterSelect; - - public BlacksmithResourceTarget(BaseTool tool, Action afterSelect) - : base(2, false, TargetFlags.None) - { - _tool = tool; - _afterSelect = afterSelect; - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is Item item) - { - var res = DefBlacksmithy.CraftSystem.CraftSubRes; - for (var i = 0; i < res.Count; ++i) - { - if (item.GetType() == res[i].ItemType) - { - var context = DefBlacksmithy.CraftSystem.GetContext(from); - context?.LastResourceIndex = i; - - _afterSelect(from, _tool); - return; - } - } - } - - from.SendMessage("That is not a valid ingot."); - from.Target = new BlacksmithResourceTarget(_tool, _afterSelect); - } -} diff --git a/Projects/UOContent/Engines/Craft/T2A/BowFletchingMenu.cs b/Projects/UOContent/Engines/Craft/T2A/BowFletchingMenu.cs deleted file mode 100644 index 88818c233..000000000 --- a/Projects/UOContent/Engines/Craft/T2A/BowFletchingMenu.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using Server.Items; -using Server.Menus.ItemLists; -using Server.Network; - -namespace Server.Engines.Craft.T2A; - -public class BowFletchingMenu : ItemListMenu -{ - private static readonly Type[] ItemTypes = - [ - typeof(Kindling), typeof(Shaft), typeof(Arrow), typeof(Bolt), - typeof(Bow), typeof(Crossbow), typeof(HeavyCrossbow) - ]; - - private static readonly string[] ItemNames = - [ - "Kindling", "Shafts", "Arrows", "Bolts", "Bow", "Crossbow", "Heavy Crossbow" - ]; - - private static readonly int[] ItemGraphics = [0xDE1, 0x1BD4, 0xF3F, 0x1BFB, 0x13B2, 0xF50, 0x13FD]; - - private static ItemListEntry[] _cachedEntries; - - private readonly BaseTool _tool; - - public BowFletchingMenu(Mobile from, BaseTool tool) - : base("What would you like to make?", BuildFilteredEntries(from)) - { - _tool = tool; - } - - public static ItemListEntry[] Main() - { - if (_cachedEntries != null) - { - return _cachedEntries; - } - - var entries = new ItemListEntry[ItemTypes.Length]; - var count = 0; - var craftItems = DefBowFletching.CraftSystem.CraftItems; - - for (var i = 0; i < ItemTypes.Length; i++) - { - if (craftItems.SearchFor(ItemTypes[i]) != null) - { - entries[count++] = new ItemListEntry(ItemNames[i], ItemGraphics[i], 0, i); - } - } - - if (count < entries.Length) - { - Array.Resize(ref entries, count); - } - - _cachedEntries = entries; - return entries; - } - - private static ItemListEntry[] BuildFilteredEntries(Mobile from) - { - return T2ACraftSystem.FilterEntries(from, Main(), ItemTypes, DefBowFletching.CraftSystem); - } - - public override void OnResponse(NetState state, int index) - { - var from = state.Mobile; - var craftIndex = Entries[index].CraftIndex; - - if (craftIndex < 0 || craftIndex >= ItemTypes.Length) - { - return; - } - - var itemDef = DefBowFletching.CraftSystem.CraftItems.SearchFor(ItemTypes[craftIndex]); - if (itemDef == null) - { - return; - } - - var num = DefBowFletching.CraftSystem.CanCraft(from, _tool, itemDef.ItemType); - if (num > 0) - { - from.SendLocalizedMessage(num); - return; - } - - var context = DefBowFletching.CraftSystem.GetContext(from); - var res = itemDef.UseSubRes2 ? DefBowFletching.CraftSystem.CraftSubRes2 : DefBowFletching.CraftSystem.CraftSubRes; - var resIndex = itemDef.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; - var type = resIndex > -1 ? res[resIndex].ItemType : null; - DefBowFletching.CraftSystem.CreateItem(from, itemDef.ItemType, type, _tool, itemDef); - } -} diff --git a/Projects/UOContent/Engines/Craft/T2A/CarpentryMenu.cs b/Projects/UOContent/Engines/Craft/T2A/CarpentryMenu.cs deleted file mode 100644 index f3da3f80f..000000000 --- a/Projects/UOContent/Engines/Craft/T2A/CarpentryMenu.cs +++ /dev/null @@ -1,298 +0,0 @@ -using System; -using Server.Items; -using Server.Menus.ItemLists; -using Server.Network; - -namespace Server.Engines.Craft.T2A; - -public class CarpentryMenu : ItemListMenu -{ - private enum Category - { - Main, - Furniture, - Containers, - Weapons, - Instruments, - Misc, - Addons - } - - private static readonly Type[] FurnitureTypes = - [ - typeof(FootStool), typeof(Stool), typeof(BambooChair), typeof(WoodenChair), - typeof(FancyWoodenChairCushion), typeof(WoodenChairCushion), - typeof(WoodenBench), typeof(WoodenThrone), typeof(Throne), - typeof(Nightstand), typeof(WritingTable), typeof(YewWoodTable), typeof(LargeTable) - ]; - - private static readonly Type[] ContainerTypes = - [ - typeof(WoodenBox), typeof(SmallCrate), typeof(MediumCrate), typeof(LargeCrate), - typeof(WoodenChest), typeof(EmptyBookcase), typeof(FancyArmoire), typeof(Armoire), - typeof(Keg) - ]; - - private static readonly Type[] WeaponTypes = - [ - typeof(ShepherdsCrook), typeof(QuarterStaff), typeof(GnarledStaff), typeof(WoodenShield) - ]; - - private static readonly Type[] InstrumentTypes = - [ - typeof(LapHarp), typeof(Harp), typeof(Drums), typeof(Lute), - typeof(Tambourine), typeof(TambourineTassel) - ]; - - private static readonly Type[] MiscItemTypes = - [ - typeof(FishingPole), typeof(BarrelStaves), typeof(BarrelLid), - typeof(ShortMusicStand), typeof(TallMusicStand), typeof(Easel) - ]; - - private static readonly Type[] AddonTypes = - [ - typeof(SmallBedSouthDeed), typeof(SmallBedEastDeed), - typeof(LargeBedSouthDeed), typeof(LargeBedEastDeed), - typeof(DartBoardSouthDeed), typeof(DartBoardEastDeed), - typeof(BallotBoxDeed), - typeof(PentagramDeed), typeof(AbbatoirDeed), - typeof(SmallForgeDeed), typeof(LargeForgeEastDeed), typeof(LargeForgeSouthDeed), - typeof(AnvilEastDeed), typeof(AnvilSouthDeed), - typeof(TrainingDummyEastDeed), typeof(TrainingDummySouthDeed), - typeof(PickpocketDipEastDeed), typeof(PickpocketDipSouthDeed), - typeof(Dressform), - typeof(SpinningWheelEastDeed), typeof(SpinningWheelSouthDeed), - typeof(LoomEastDeed), typeof(LoomSouthDeed), - typeof(StoneOvenEastDeed), typeof(StoneOvenSouthDeed), - typeof(FlourMillEastDeed), typeof(FlourMillSouthDeed), - typeof(WaterTroughEastDeed), typeof(WaterTroughSouthDeed) - ]; - - private static ItemListEntry[] _mainEntries; - private static ItemListEntry[] _furnitureEntries; - private static ItemListEntry[] _containerEntries; - private static ItemListEntry[] _weaponEntries; - private static ItemListEntry[] _instrumentEntries; - private static ItemListEntry[] _miscEntries; - private static ItemListEntry[] _addonEntries; - - private readonly Category _category; - private readonly BaseTool _tool; - - public CarpentryMenu(Mobile from, BaseTool tool) : this(from, tool, Category.Main) - { - } - - private static string GetQuestion(Category category) => category switch - { - Category.Main => "What would you like to make?", - Category.Furniture => "What kind of furniture?", - Category.Containers => "What kind of container?", - Category.Weapons => "What kind of weapon?", - Category.Instruments => "What kind of instrument?", - Category.Addons => "What kind of add-on?", - _ => "What would you like to make?" - }; - - private CarpentryMenu(Mobile from, BaseTool tool, Category category) - : base(GetQuestion(category), BuildFilteredEntries(from, category)) - { - _tool = tool; - _category = category; - } - - private static string FormatItemName(Type type) - { - var name = type.Name; - Span buffer = stackalloc char[name.Length * 2]; - var pos = 0; - - for (var i = 0; i < name.Length; i++) - { - if (i > 0 && char.IsUpper(name[i])) - { - buffer[pos++] = ' '; - } - - buffer[pos++] = char.ToLower(name[i]); - } - - return new string(buffer[..pos]); - } - - private static ItemListEntry[] BuildStaticEntries(Type[] types, string resourceName) - { - var entries = new ItemListEntry[types.Length]; - var count = 0; - var craftItems = DefCarpentry.CraftSystem.CraftItems; - - for (var i = 0; i < types.Length; i++) - { - var itemDef = craftItems.SearchFor(types[i]); - if (itemDef == null) - { - continue; - } - - var name = FormatItemName(types[i]); - var res = itemDef.Resources; - - string label; - if (res.Count > 1) - { - var secondName = res[1].ItemType == typeof(IronIngot) ? "ingots" : "cloth"; - label = $"{name} ({res[0].Amount} {resourceName}, {res[1].Amount} {secondName})"; - } - else - { - label = $"{name} ({res[0].Amount} {resourceName})"; - } - - entries[count++] = new ItemListEntry(label, itemDef.ItemId, 0, i); - } - - if (count < entries.Length) - { - Array.Resize(ref entries, count); - } - - return entries; - } - - private static ItemListEntry[] GetStaticEntries(Category category) => category switch - { - Category.Main => Main(), - Category.Furniture => Furniture(), - Category.Containers => Containers(), - Category.Weapons => Weapons(), - Category.Instruments => Instruments(), - Category.Misc => Misc(), - Category.Addons => Addons(), - _ => null - }; - - private static Type[] GetTypes(Category category) => category switch - { - Category.Furniture => FurnitureTypes, - Category.Containers => ContainerTypes, - Category.Weapons => WeaponTypes, - Category.Instruments => InstrumentTypes, - Category.Misc => MiscItemTypes, - Category.Addons => AddonTypes, - _ => null - }; - - public static ItemListEntry[] Main() => _mainEntries ??= - [ - new ItemListEntry("Furniture", 0xB57, 0, (int)Category.Furniture), - new ItemListEntry("Containers", 0x9AA, 0, (int)Category.Containers), - new ItemListEntry("Weapons", 0xE89, 0, (int)Category.Weapons), - new ItemListEntry("Instruments", 0xEB3, 0, (int)Category.Instruments), - new ItemListEntry("Miscellaneous", 0xDC0, 0, (int)Category.Misc), - new ItemListEntry("Add-Ons", 0x14F0, 0, (int)Category.Addons) - ]; - - public static ItemListEntry[] Furniture() => _furnitureEntries ??= BuildStaticEntries(FurnitureTypes, "wood"); - public static ItemListEntry[] Containers() => _containerEntries ??= BuildStaticEntries(ContainerTypes, "wood"); - public static ItemListEntry[] Weapons() => _weaponEntries ??= BuildStaticEntries(WeaponTypes, "wood"); - public static ItemListEntry[] Instruments() => _instrumentEntries ??= BuildStaticEntries(InstrumentTypes, "wood"); - public static ItemListEntry[] Misc() => _miscEntries ??= BuildStaticEntries(MiscItemTypes, "wood"); - public static ItemListEntry[] Addons() => _addonEntries ??= BuildStaticEntries(AddonTypes, "wood"); - - private static ItemListEntry[] BuildFilteredEntries(Mobile from, Category category) - { - if (category == Category.Main) - { - return BuildFilteredMainEntries(from); - } - - var types = GetTypes(category); - var staticEntries = GetStaticEntries(category); - if (types == null || staticEntries == null) - { - return []; - } - - return T2ACraftSystem.FilterEntries(from, staticEntries, types, DefCarpentry.CraftSystem); - } - - private static ItemListEntry[] BuildFilteredMainEntries(Mobile from) - { - var system = DefCarpentry.CraftSystem; - var mainStatic = Main(); - var filtered = new ItemListEntry[mainStatic.Length]; - var count = 0; - - for (var i = 0; i < mainStatic.Length; i++) - { - var entry = mainStatic[i]; - var types = GetTypes((Category)entry.CraftIndex); - if (types != null && T2ACraftSystem.AnyCraftableInCategory(from, types, system)) - { - filtered[count++] = entry; - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - private void CraftSelectedItem(Mobile from, Type itemType) - { - var itemDef = DefCarpentry.CraftSystem.CraftItems.SearchFor(itemType); - - if (itemDef == null) - { - return; - } - - var num = DefCarpentry.CraftSystem.CanCraft(from, _tool, itemDef.ItemType); - - if (num > 0) - { - from.SendLocalizedMessage(num); - return; - } - - var context = DefCarpentry.CraftSystem.GetContext(from); - var res = itemDef.UseSubRes2 ? DefCarpentry.CraftSystem.CraftSubRes2 : DefCarpentry.CraftSystem.CraftSubRes; - var resIndex = itemDef.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; - var type = resIndex > -1 ? res[resIndex].ItemType : null; - DefCarpentry.CraftSystem.CreateItem(from, itemDef.ItemType, type, _tool, itemDef); - } - - public override void OnResponse(NetState state, int index) - { - var from = state.Mobile; - var craftIndex = Entries[index].CraftIndex; - - if (_category == Category.Main) - { - var menu = new CarpentryMenu(from, _tool, (Category)craftIndex); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything in that category."); - return; - } - - from.SendMenu(menu); - return; - } - - var types = GetTypes(_category); - if (types != null && craftIndex >= 0 && craftIndex < types.Length) - { - CraftSelectedItem(from, types[craftIndex]); - } - } -} diff --git a/Projects/UOContent/Engines/Craft/T2A/CartographyMenu.cs b/Projects/UOContent/Engines/Craft/T2A/CartographyMenu.cs deleted file mode 100644 index 926b8ad4e..000000000 --- a/Projects/UOContent/Engines/Craft/T2A/CartographyMenu.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using Server.Items; -using Server.Menus.ItemLists; -using Server.Network; - -namespace Server.Engines.Craft.T2A; - -public class CartographyMenu : ItemListMenu -{ - private static ItemListEntry[] _cachedEntries; - - private readonly BaseTool _tool; - - public CartographyMenu(Mobile from, BaseTool tool) - : base("What kind of map?", BuildFilteredEntries(from)) - { - _tool = tool; - } - - public static ItemListEntry[] Main() - { - if (_cachedEntries != null) - { - return _cachedEntries; - } - - var craftItems = DefCartography.CraftSystem.CraftItems; - var entries = new ItemListEntry[craftItems.Count]; - var count = 0; - - for (var i = 0; i < craftItems.Count; i++) - { - var name = i switch - { - 0 => "A map of the local environs.", - 1 => "A map suitable for cities.", - 2 => "A moderately sized sea chart.", - 3 => "A map of the world.", - _ => craftItems[i].ItemType.Name - }; - - entries[count++] = new ItemListEntry(name, 6511 + i, 0, i); - } - - if (count < entries.Length) - { - Array.Resize(ref entries, count); - } - - _cachedEntries = entries; - return entries; - } - - private static ItemListEntry[] BuildFilteredEntries(Mobile from) - { - var staticEntries = Main(); - var craftItems = DefCartography.CraftSystem.CraftItems; - - var filtered = new ItemListEntry[staticEntries.Length]; - var count = 0; - - for (var i = 0; i < staticEntries.Length; i++) - { - var entry = staticEntries[i]; - var craftIndex = entry.CraftIndex; - if (craftIndex >= 0 && craftIndex < craftItems.Count) - { - var itemDef = craftItems[craftIndex]; - if (T2ACraftSystem.CanCraftItem(from, itemDef, DefCartography.CraftSystem)) - { - filtered[count++] = entry; - } - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - public override void OnResponse(NetState state, int index) - { - var from = state.Mobile; - var craftIndex = Entries[index].CraftIndex; - var craftItems = DefCartography.CraftSystem.CraftItems; - - if (craftIndex < 0 || craftIndex >= craftItems.Count) - { - return; - } - - var itemDef = craftItems[craftIndex]; - var num = DefCartography.CraftSystem.CanCraft(from, _tool, itemDef.ItemType); - if (num > 0) - { - from.SendLocalizedMessage(num); - return; - } - - DefCartography.CraftSystem.CreateItem(from, itemDef.ItemType, typeof(BlankMap), _tool, itemDef); - } -} diff --git a/Projects/UOContent/Engines/Craft/T2A/InscriptionMenu.cs b/Projects/UOContent/Engines/Craft/T2A/InscriptionMenu.cs deleted file mode 100644 index e68580577..000000000 --- a/Projects/UOContent/Engines/Craft/T2A/InscriptionMenu.cs +++ /dev/null @@ -1,334 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Server.Items; -using Server.Menus.ItemLists; -using Server.Network; - -namespace Server.Engines.Craft.T2A; - -public class InscriptionMenu : ItemListMenu -{ - private enum Category - { - Main, - Circle1, - Circle2, - Circle3, - Circle4, - Circle5, - Circle6, - Circle7, - Circle8, - Runebook - } - - private static readonly ItemListEntry[][] _circleEntries = new ItemListEntry[8][]; - - private static ItemListEntry[] _mainEntries; - - private readonly Category _category; - private readonly BaseTool _tool; - - public InscriptionMenu(Mobile from, BaseTool tool) : this(from, tool, Category.Main) - { - } - - private static string GetQuestion(Category category) => category switch - { - Category.Main => "Which circle of spells?", - _ => "Which spell would you like to scribe?" - }; - - private InscriptionMenu(Mobile from, BaseTool tool, Category category) - : base(GetQuestion(category), BuildFilteredEntries(from, category)) - { - _tool = tool; - _category = category; - } - - private static string FormatScrollName(Type type) - { - var name = type.Name; - if (name.EndsWith("Scroll")) - { - name = name[..^6]; - } - - Span buffer = stackalloc char[name.Length * 2]; - var pos = 0; - for (var i = 0; i < name.Length; i++) - { - if (i > 0 && char.IsUpper(name[i])) - { - buffer[pos++] = ' '; - } - - buffer[pos++] = char.ToLower(name[i]); - } - - return new string(buffer[..pos]); - } - - public static ItemListEntry[] Main() => _mainEntries ??= - [ - new ItemListEntry("Runebook", 0xEFA, 0, (int)Category.Runebook), - new ItemListEntry("First Circle", 8384, 0, (int)Category.Circle1), - new ItemListEntry("Second Circle", 8385, 0, (int)Category.Circle2), - new ItemListEntry("Third Circle", 8386, 0, (int)Category.Circle3), - new ItemListEntry("Fourth Circle", 8387, 0, (int)Category.Circle4), - new ItemListEntry("Fifth Circle", 8388, 0, (int)Category.Circle5), - new ItemListEntry("Sixth Circle", 8389, 0, (int)Category.Circle6), - new ItemListEntry("Seventh Circle", 8390, 0, (int)Category.Circle7), - new ItemListEntry("Eighth Circle", 8391, 0, (int)Category.Circle8) - ]; - - private static ItemListEntry[] BuildCircleEntries(int circleIndex) - { - var offset = circleIndex * 8; - var craftItems = DefInscription.CraftSystem.CraftItems; - var entries = new ItemListEntry[8]; - var count = 0; - - for (var i = 0; i < 8; i++) - { - var itemDef = craftItems[offset + i]; - var name = FormatScrollName(itemDef.ItemType); - entries[count++] = new ItemListEntry(name, 8320 + offset + i, 0, i); - } - - if (count < entries.Length) - { - Array.Resize(ref entries, count); - } - - return entries; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ItemListEntry[] GetCircleEntries(int circleIndex) => - _circleEntries[circleIndex] ??= BuildCircleEntries(circleIndex); - - private static Dictionary _spellIds; - private static object[] _args; - - private static bool HasSpellInBook(Mobile from, Type scrollType) - { - if (scrollType == null) - { - return false; - } - - _spellIds ??= []; - if (!_spellIds.TryGetValue(scrollType, out var spellId)) - { - try - { - _args ??= [1]; - var scroll = scrollType.CreateInstance(_args); - if (scroll != null) - { - spellId = _spellIds[scrollType] = scroll.SpellID; - scroll.Delete(); - } - else - { - return false; - } - } - catch - { - return false; - } - } - - var book = Spellbook.Find(from, spellId); - return book?.HasSpell(spellId) ?? false; - } - - private static bool CanScribeScroll(Mobile from, CraftItem itemDef) - { - // Skill check - if (!T2ACraftSystem.CanCraftItem(from, itemDef, DefInscription.CraftSystem)) - { - return false; - } - - // Must have blank scrolls - if ((from.Backpack?.GetAmount(typeof(BlankScroll)) ?? 0) == 0) - { - return false; - } - - // Must have the spell in spellbook - return HasSpellInBook(from, itemDef.ItemType); - } - - private static ItemListEntry[] BuildFilteredEntries(Mobile from, Category category) - { - if (category == Category.Main) - { - return BuildFilteredMainEntries(from); - } - - var circleIndex = (int)category - 1; - var staticEntries = GetCircleEntries(circleIndex); - var craftItems = DefInscription.CraftSystem.CraftItems; - var offset = circleIndex * 8; - - var filtered = new ItemListEntry[staticEntries.Length]; - var count = 0; - - for (var i = 0; i < staticEntries.Length; i++) - { - var entry = staticEntries[i]; - var scrollIndex = entry.CraftIndex; - var itemDef = craftItems[offset + scrollIndex]; - if (CanScribeScroll(from, itemDef)) - { - filtered[count++] = entry; - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - private static ItemListEntry[] BuildFilteredMainEntries(Mobile from) - { - var craftItems = DefInscription.CraftSystem.CraftItems; - var system = DefInscription.CraftSystem; - var mainStatic = Main(); - var filtered = new ItemListEntry[mainStatic.Length]; - var count = 0; - - for (var i = 0; i < mainStatic.Length; i++) - { - var entry = mainStatic[i]; - var category = (Category)entry.CraftIndex; - - if (category == Category.Runebook) - { - if (T2ACraftSystem.CanCraftItem(from, typeof(Runebook), system)) - { - filtered[count++] = entry; - } - - continue; - } - - var circleIndex = (int)category - 1; - var offset = circleIndex * 8; - - var hasAny = false; - for (var j = 0; j < 8; j++) - { - var itemDef = craftItems[offset + j]; - if (CanScribeScroll(from, itemDef)) - { - hasAny = true; - break; - } - } - - if (hasAny) - { - filtered[count++] = entry; - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - private void CraftScroll(Mobile from, Category circle, int scrollIndex) - { - var itemIndex = ((int)circle - 1) * 8 + scrollIndex; - var craftItems = DefInscription.CraftSystem.CraftItems; - var itemDef = craftItems[itemIndex]; - - if (!HasSpellInBook(from, itemDef.ItemType)) - { - from.SendAsciiMessage("You do not have that spell in your spellbook."); - return; - } - - var context = DefInscription.CraftSystem.GetContext(from); - var res = itemDef.UseSubRes2 ? DefInscription.CraftSystem.CraftSubRes2 : DefInscription.CraftSystem.CraftSubRes; - var resIndex = itemDef.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; - var type = resIndex > -1 ? res[resIndex].ItemType : null; - DefInscription.CraftSystem.CreateItem(from, itemDef.ItemType, type, _tool, itemDef); - } - - public override void OnResponse(NetState state, int index) - { - var from = state.Mobile; - - if ((from.Backpack?.GetAmount(typeof(BlankScroll)) ?? 0) == 0) - { - from.SendAsciiMessage("You do not have enough blank scrolls to make that."); - return; - } - - var craftIndex = Entries[index].CraftIndex; - if (_category == Category.Main) - { - var category = (Category)craftIndex; - - if (category == Category.Runebook) - { - CraftRunebook(from); - return; - } - - var menu = new InscriptionMenu(from, _tool, category); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to scribe anything in that circle."); - return; - } - - from.SendMenu(menu); - return; - } - - CraftScroll(from, _category, craftIndex); - } - - private void CraftRunebook(Mobile from) - { - var system = DefInscription.CraftSystem; - var itemDef = system.CraftItems.SearchFor(typeof(Runebook)); - if (itemDef == null) - { - return; - } - - var num = system.CanCraft(from, _tool, itemDef.ItemType); - if (num > 0) - { - from.SendLocalizedMessage(num); - return; - } - - system.CreateItem(from, itemDef.ItemType, null, _tool, itemDef); - } -} diff --git a/Projects/UOContent/Engines/Craft/T2A/T2ACraftSystem.cs b/Projects/UOContent/Engines/Craft/T2A/T2ACraftSystem.cs deleted file mode 100644 index 785a319ad..000000000 --- a/Projects/UOContent/Engines/Craft/T2A/T2ACraftSystem.cs +++ /dev/null @@ -1,351 +0,0 @@ -// T2A crafting system: packet-based, not gump-based - -using System; -using Server.Items; -using Server.Menus.ItemLists; -using Server.Targeting; - -namespace Server.Engines.Craft.T2A; - -public static class T2ACraftSystem -{ - /// - /// Whether T2A packet-based crafting menus are active. Set once at startup from the - /// "t2aCraftMenus" server setting (default: !Core.UOTD). Not flippable at runtime. - /// - public static bool Enabled { get; set; } - - public static void ShowMenu(Mobile from, CraftSystem craftSystem, BaseTool tool, Item preTarget = null) - { - if (!Enabled) - { - return; - } - - if (craftSystem == DefBlacksmithy.CraftSystem) - { - // Lost Lands flow: resource selection first, then menu - BlacksmithMenu.ResourceSelection(from, tool, (mob, t) => - { - var menu = new BlacksmithMenu(mob, t); - if (menu.Entries.Length == 0) - { - mob.SendAsciiMessage("You lack the skill and materials to craft anything."); - return; - } - - mob.SendMenu(menu); - }, preTarget); - } - else if (craftSystem == DefAlchemy.CraftSystem) - { - if (preTarget is BaseReagent or Bottle || preTarget == null) - { - ShowMenuDirect(from, tool); - } - else - { - PromptForResource(from, tool, craftSystem, "Target a reagent or empty bottle.", - item => item is BaseReagent or Bottle); - } - } - else if (craftSystem == DefBowFletching.CraftSystem) - { - if (preTarget is Log or Board or Feather or Shaft || preTarget == null) - { - ShowMenuDirect(from, tool); - } - else - { - PromptForResource(from, tool, craftSystem, "Target the wood or feathers you wish to use.", - item => item is Log or Board or Feather or Shaft); - } - } - else if (craftSystem == DefCarpentry.CraftSystem) - { - if (preTarget is Log or Board || preTarget == null) - { - ShowMenuDirect(from, tool); - } - else - { - PromptForResource(from, tool, craftSystem, "Target the wood you wish to use.", - item => item is Log or Board); - } - } - else if (craftSystem == DefCartography.CraftSystem) - { - if (preTarget is BlankMap || preTarget == null) - { - ShowMenuDirect(from, tool); - } - else - { - PromptForResource(from, tool, craftSystem, "Target a blank map.", - item => item is BlankMap); - } - } - else if (craftSystem == DefInscription.CraftSystem) - { - if (preTarget is BlankScroll or BaseReagent or RecallRune || preTarget == null) - { - ShowMenuDirect(from, tool); - } - else - { - PromptForResource(from, tool, craftSystem, "Target the blank scrolls you wish to use.", - item => item is BlankScroll or BaseReagent or RecallRune); - } - } - else if (craftSystem == DefTailoring.CraftSystem) - { - TailoringMenu.ResourceSelection(from, tool, preTarget); - } - else if (craftSystem == DefTinkering.CraftSystem) - { - TinkeringMenu.ResourceSelection(from, tool, preTarget); - } - } - - private static void ShowMenuDirect(Mobile from, BaseTool tool) where T : ItemListMenu - { - var menu = (T)Activator.CreateInstance(typeof(T), from, tool); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything."); - return; - } - - from.SendMenu(menu); - } - - private static void PromptForResource( - Mobile from, BaseTool tool, CraftSystem system, string message, Func isValid - ) - { - from.SendAsciiMessage(message); - from.Target = new CraftResourceTarget(tool, system, message, isValid); - } - - private class CraftResourceTarget : Target - { - private readonly BaseTool _tool; - private readonly CraftSystem _system; - private readonly string _message; - private readonly Func _isValid; - - public CraftResourceTarget( - BaseTool tool, CraftSystem system, string message, Func isValid - ) : base(12, false, TargetFlags.None) - { - _tool = tool; - _system = system; - _message = message; - _isValid = isValid; - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is Item item && _isValid(item)) - { - ShowMenu(from, _system, _tool, item); - return; - } - - from.SendAsciiMessage(_message); - from.Target = new CraftResourceTarget(_tool, _system, _message, _isValid); - } - } - - /// - /// Persists the selected resource type as a LastResourceIndex on the craft context, - /// so that make-last can recall which resource was used. - /// - public static void SetLastResourceIndex(Mobile from, CraftSystem system, Type selectedResourceType) - { - if (selectedResourceType == null) - { - return; - } - - var context = system.GetContext(from); - var resCol = system.CraftSubRes; - - if (context == null || !resCol.Init) - { - return; - } - - for (var i = 0; i < resCol.Count; i++) - { - if (resCol[i].ItemType == selectedResourceType) - { - context.LastResourceIndex = i; - return; - } - } - } - - /// - /// Checks if a player can craft a specific item, accounting for sub-resource types. - /// When is non-null, checks against that specific sub-resource. - /// When null, checks against ANY available sub-resource the player has sufficient skill and materials for. - /// - public static bool CanCraftItem( - Mobile from, CraftItem itemDef, CraftSystem system, Type selectedResourceType = null - ) - { - var pack = from.Backpack; - if (pack == null) - { - return false; - } - - var chance = itemDef.GetSuccessChance(from, selectedResourceType, system, false, out var allRequiredSkills); - if (!allRequiredSkills || chance <= 0.0) - { - return false; - } - - var resCol = system.CraftSubRes; - - for (var i = 0; i < itemDef.Resources.Count; i++) - { - var res = itemDef.Resources[i]; - var resType = res.ItemType; - - // If this resource is the base sub-resource type (e.g. IronIngot for blacksmithing), - // handle sub-resource substitution - if (resCol.Init && resType == resCol.ResType) - { - if (selectedResourceType != null) - { - // Specific resource selected — check skill gate for this sub-resource - var subRes = resCol.SearchFor(selectedResourceType); - if (subRes != null && from.Skills[system.MainSkill].Value < subRes.RequiredSkill) - { - return false; - } - - // Check if player has enough of the selected resource - if (GetResourceAmount(pack, selectedResourceType) < res.Amount) - { - return false; - } - } - else if (!HasAnySufficientSubResource(from, pack, res.Amount, system, resCol)) - { - return false; - } - } - else if (GetResourceAmount(pack, resType) < res.Amount) - { - return false; - } - } - - return true; - } - - /// - /// Convenience overload that resolves a Type to a CraftItem first. - /// - public static bool CanCraftItem(Mobile from, Type itemType, CraftSystem system, Type selectedResourceType = null) - { - var itemDef = system.CraftItems.SearchFor(itemType); - return itemDef != null && CanCraftItem(from, itemDef, system, selectedResourceType); - } - - /// - /// Filters static template entries to only those the player can craft. - /// - public static ItemListEntry[] FilterEntries( - Mobile from, ItemListEntry[] staticEntries, Type[] types, CraftSystem system, Type selectedResourceType = null - ) - { - var filtered = new ItemListEntry[staticEntries.Length]; - var count = 0; - - for (var i = 0; i < staticEntries.Length; i++) - { - var entry = staticEntries[i]; - var typeIndex = entry.CraftIndex; - if (typeIndex >= 0 && typeIndex < types.Length && - CanCraftItem(from, types[typeIndex], system, selectedResourceType)) - { - filtered[count++] = entry; - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - /// - /// Returns true if at least one item in the type array is craftable. - /// - public static bool AnyCraftableInCategory( - Mobile from, Type[] types, CraftSystem system, Type selectedResourceType = null - ) - { - for (var i = 0; i < types.Length; i++) - { - if (CanCraftItem(from, types[i], system, selectedResourceType)) - { - return true; - } - } - - return false; - } - - /// - /// Equivalent type pairs mirroring CraftItem.m_TypesTable — used so that menu filtering - /// counts boards when checking for logs, hides when checking for leather, etc. - /// - private static readonly Type[][] _equivalentTypes = - [ - [typeof(Log), typeof(Board)], - [typeof(Cloth), typeof(UncutCloth)], - [typeof(Items.Leather), typeof(Hides)] - ]; - - private static int GetResourceAmount(Container pack, Type type) - { - for (var i = 0; i < _equivalentTypes.Length; i++) - { - if (_equivalentTypes[i][0] == type) - { - return pack.GetAmount(_equivalentTypes[i]); - } - } - - return pack.GetAmount(type); - } - - private static bool HasAnySufficientSubResource( - Mobile from, Container pack, int amountNeeded, CraftSystem system, CraftSubResCol resCol - ) - { - for (var j = 0; j < resCol.Count; j++) - { - var subRes = resCol[j]; - if (from.Skills[system.MainSkill].Value >= subRes.RequiredSkill && - GetResourceAmount(pack, subRes.ItemType) >= amountNeeded) - { - return true; - } - } - - return false; - } -} diff --git a/Projects/UOContent/Engines/Craft/T2A/T2ACraftToolTarget.cs b/Projects/UOContent/Engines/Craft/T2A/T2ACraftToolTarget.cs deleted file mode 100644 index d7398f000..000000000 --- a/Projects/UOContent/Engines/Craft/T2A/T2ACraftToolTarget.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Server.Items; -using Server.Targeting; - -namespace Server.Engines.Craft.T2A; - -public class T2ACraftToolTarget : Target -{ - private readonly BaseTool _tool; - private readonly CraftSystem _system; - - public T2ACraftToolTarget(BaseTool tool, CraftSystem system) : base(2, false, TargetFlags.None) - { - _tool = tool; - _system = system; - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted == _tool) - { - // Make Last: repeat last crafted item - var context = _system.GetContext(from); - var lastMade = context?.LastMade; - - if (lastMade != null) - { - var num = _system.CanCraft(from, _tool, lastMade.ItemType); - - if (num > 0) - { - from.SendLocalizedMessage(num); - return; - } - - var res = lastMade.UseSubRes2 ? _system.CraftSubRes2 : _system.CraftSubRes; - var resIndex = lastMade.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; - var type = resIndex > -1 ? res[resIndex].ItemType : null; - - // Jewelry requires gem targeting — re-prompt instead of crafting directly - if (typeof(BaseJewel).IsAssignableFrom(lastMade.ItemType)) - { - context.PendingGemType = GemType.None; - context.PendingGemCount = 0; - from.SendAsciiMessage("Target the gemstone you wish to use."); - from.Target = new TinkeringMenu.GemSelectTarget(from, _tool, lastMade.ItemType, type); - return; - } - - if (context.LastHue >= 0) - { - _system.CreateItem(from, lastMade.ItemType, type, _tool, lastMade, context.LastHue); - } - else - { - _system.CreateItem(from, lastMade.ItemType, type, _tool, lastMade); - } - } - else - { - from.SendAsciiMessage("You have not yet crafted anything."); - T2ACraftSystem.ShowMenu(from, _system, _tool); - } - } - else - { - // Normal flow: pass the targeted object through so resource selection - // can use it directly instead of requiring a second target. - T2ACraftSystem.ShowMenu(from, _system, _tool, targeted as Item); - } - } -} diff --git a/Projects/UOContent/Engines/Craft/T2A/TailoringMenu.cs b/Projects/UOContent/Engines/Craft/T2A/TailoringMenu.cs deleted file mode 100644 index 80f950ab8..000000000 --- a/Projects/UOContent/Engines/Craft/T2A/TailoringMenu.cs +++ /dev/null @@ -1,375 +0,0 @@ -using System; -using Server.Items; -using Server.Menus.ItemLists; -using Server.Network; -using Server.Targeting; - -namespace Server.Engines.Craft.T2A; - -public class TailoringMenu : ItemListMenu -{ - private enum Category - { - Main, - LeatherMain, - Hats, - Shirts, - Pants, - Misc, - Footwear, - Leather, - Studded, - Female - } - - private static readonly Type[] HatsTypes = - [ - typeof(SkullCap), typeof(Bandana), typeof(FloppyHat), typeof(Cap), typeof(WideBrimHat), - typeof(StrawHat), typeof(TallStrawHat), typeof(WizardsHat), typeof(Bonnet), - typeof(FeatheredHat), typeof(TricorneHat), typeof(JesterHat) - ]; - - private static readonly Type[] ShirtsTypes = - [ - typeof(Doublet), typeof(Shirt), typeof(FancyShirt), typeof(Tunic), typeof(Surcoat), typeof(PlainDress) - ]; - - private static readonly Type[] PantsTypes = [typeof(ShortPants), typeof(LongPants), typeof(Kilt) ]; - - private static readonly Type[] MiscTypes = - [ - typeof(Skirt), typeof(Cloak), typeof(Robe), typeof(JesterSuit), typeof(FancyDress), - typeof(BodySash), typeof(HalfApron), typeof(FullApron) - ]; - - private static readonly Type[] FootwearTypes = [typeof(Sandals), typeof(Shoes), typeof(Boots), typeof(ThighBoots)]; - - private static readonly Type[] LeatherArmorTypes = - [ - typeof(LeatherChest), typeof(LeatherGorget), typeof(LeatherGloves), typeof(LeatherCap), - typeof(LeatherArms), typeof(LeatherLegs) - ]; - - private static readonly Type[] StuddedArmorTypes = - [ - typeof(StuddedChest), typeof(StuddedGorget), typeof(StuddedGloves), typeof(StuddedArms), typeof(StuddedLegs) - ]; - - private static readonly Type[] FemaleArmorTypes = - [ - typeof(FemaleLeatherChest), typeof(FemaleStuddedChest), typeof(LeatherBustierArms), typeof(StuddedBustierArms), - typeof(FemalePlateChest), typeof(LeatherShorts), typeof(LeatherSkirt) - ]; - - private static ItemListEntry[] _mainEntries; - private static ItemListEntry[] _leatherMainEntries; - private static ItemListEntry[] _hatsEntries; - private static ItemListEntry[] _shirtsEntries; - private static ItemListEntry[] _pantsEntries; - private static ItemListEntry[] _miscEntries; - private static ItemListEntry[] _footwearEntries; - private static ItemListEntry[] _leatherEntries; - private static ItemListEntry[] _studdedEntries; - private static ItemListEntry[] _femaleEntries; - - private readonly Category _category; - private readonly BaseTool _tool; - private readonly int _hue; - - private static string GetQuestion(Category category) => category switch - { - Category.Main => "What would you like to make?", - Category.LeatherMain => "What would you like to make?", - Category.Hats => "What kind of hat?", - Category.Shirts => "What kind of shirt?", - Category.Pants => "What kind of pants?", - Category.Misc => "What would you like to make?", - Category.Footwear => "What kind of footwear?", - Category.Leather => "What kind of leather armor?", - Category.Studded => "What kind of studded armor?", - Category.Female => "What kind of female leather?", - _ => "What would you like to make?" - }; - - private TailoringMenu(Mobile from, BaseTool tool, Category category, int hue = -1) - : base(GetQuestion(category), BuildFilteredEntries(from, category)) - { - _tool = tool; - _category = category; - _hue = hue; - } - - private static string FormatItemName(Type type) - { - var name = type.Name; - Span buffer = stackalloc char[name.Length * 2]; - var pos = 0; - - for (var i = 0; i < name.Length; i++) - { - if (i > 0 && char.IsUpper(name[i])) - { - buffer[pos++] = ' '; - } - - buffer[pos++] = char.ToLower(name[i]); - } - - return new string(buffer[..pos]); - } - - private static ItemListEntry[] BuildStaticEntries(Type[] types, string resourceName) - { - var entries = new ItemListEntry[types.Length]; - var count = 0; - var craftItems = DefTailoring.CraftSystem.CraftItems; - - for (var i = 0; i < types.Length; i++) - { - var itemDef = craftItems.SearchFor(types[i]); - if (itemDef == null) - { - continue; - } - - var name = FormatItemName(types[i]); - var res = itemDef.Resources[0]; - entries[count++] = new ItemListEntry($"{name} ({res.Amount} {resourceName})", itemDef.ItemId, 0, i); - } - - if (count < entries.Length) - { - Array.Resize(ref entries, count); - } - - return entries; - } - - private static ItemListEntry[] GetStaticEntries(Category category) => category switch - { - Category.Main => Main(), - Category.LeatherMain => LeatherMain(), - Category.Hats => Hats(), - Category.Shirts => Shirts(), - Category.Pants => Pants(), - Category.Misc => Misc(), - Category.Footwear => Footwear(), - Category.Leather => Leather(), - Category.Studded => Studded(), - Category.Female => Female(), - _ => null - }; - - private static Type[] GetTypes(Category category) => category switch - { - Category.Hats => HatsTypes, - Category.Shirts => ShirtsTypes, - Category.Pants => PantsTypes, - Category.Misc => MiscTypes, - Category.Footwear => FootwearTypes, - Category.Leather => LeatherArmorTypes, - Category.Studded => StuddedArmorTypes, - Category.Female => FemaleArmorTypes, - _ => null - }; - - public static ItemListEntry[] Main() => _mainEntries ??= - [ - new ItemListEntry("Build Hats", 0x1718, 0, (int)Category.Hats), - new ItemListEntry("Build Shirts", 0x1517, 0, (int)Category.Shirts), - new ItemListEntry("Build Pants", 0x1539, 0, (int)Category.Pants), - new ItemListEntry("Build Misc", 0x153D, 0, (int)Category.Misc) - ]; - - public static ItemListEntry[] LeatherMain() => _leatherMainEntries ??= - [ - new ItemListEntry("Build Shoes", 0x170f, 0, (int)Category.Footwear), - new ItemListEntry("Build Leather Armor", 0x13cc, 0, (int)Category.Leather), - new ItemListEntry("Build Studded Armor", 0x13db, 0, (int)Category.Studded), - new ItemListEntry("Build Female Armor", 0x1c06, 0, (int)Category.Female) - ]; - - public static ItemListEntry[] Hats() => _hatsEntries ??= BuildStaticEntries(HatsTypes, "cloth"); - public static ItemListEntry[] Shirts() => _shirtsEntries ??= BuildStaticEntries(ShirtsTypes, "cloth"); - public static ItemListEntry[] Pants() => _pantsEntries ??= BuildStaticEntries(PantsTypes, "cloth"); - public static ItemListEntry[] Misc() => _miscEntries ??= BuildStaticEntries(MiscTypes, "cloth"); - public static ItemListEntry[] Footwear() => _footwearEntries ??= BuildStaticEntries(FootwearTypes, "leather"); - public static ItemListEntry[] Leather() => _leatherEntries ??= BuildStaticEntries(LeatherArmorTypes, "leather"); - public static ItemListEntry[] Studded() => _studdedEntries ??= BuildStaticEntries(StuddedArmorTypes, "leather"); - public static ItemListEntry[] Female() => _femaleEntries ??= BuildStaticEntries(FemaleArmorTypes, "leather"); - - private static ItemListEntry[] BuildFilteredEntries(Mobile from, Category category) - { - if (category is Category.Main or Category.LeatherMain) - { - return BuildFilteredMainEntries(from, category); - } - - var types = GetTypes(category); - var staticEntries = GetStaticEntries(category); - if (types == null || staticEntries == null) - { - return []; - } - - return T2ACraftSystem.FilterEntries(from, staticEntries, types, DefTailoring.CraftSystem); - } - - private static ItemListEntry[] BuildFilteredMainEntries(Mobile from, Category mainCategory) - { - var system = DefTailoring.CraftSystem; - var mainStatic = GetStaticEntries(mainCategory); - if (mainStatic == null) - { - return []; - } - - var filtered = new ItemListEntry[mainStatic.Length]; - var count = 0; - - for (var i = 0; i < mainStatic.Length; i++) - { - var entry = mainStatic[i]; - var types = GetTypes((Category)entry.CraftIndex); - if (types != null && T2ACraftSystem.AnyCraftableInCategory(from, types, system)) - { - filtered[count++] = entry; - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - private void CraftItem(Mobile from, Type itemType) - { - var itemDef = DefTailoring.CraftSystem.CraftItems.SearchFor(itemType); - if (itemDef == null) - { - return; - } - - var num = DefTailoring.CraftSystem.CanCraft(from, _tool, itemDef.ItemType); - if (num > 0) - { - from.SendLocalizedMessage(num); - return; - } - - var context = DefTailoring.CraftSystem.GetContext(from); - var res = itemDef.UseSubRes2 ? DefTailoring.CraftSystem.CraftSubRes2 : DefTailoring.CraftSystem.CraftSubRes; - var resIndex = itemDef.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex; - var resourceType = resIndex > -1 ? res[resIndex].ItemType : null; - - if (_hue >= 0) - { - // Pipeline B: hue-aware crafting — only consumes resources matching this hue - context.LastHue = _hue; - DefTailoring.CraftSystem.CreateItem(from, itemDef.ItemType, resourceType, _tool, itemDef, _hue); - } - else - { - DefTailoring.CraftSystem.CreateItem(from, itemDef.ItemType, resourceType, _tool, itemDef); - } - } - - public override void OnResponse(NetState state, int index) - { - var from = state.Mobile; - var craftIndex = Entries[index].CraftIndex; - - if (_category is Category.Main or Category.LeatherMain) - { - // Carry hue through to child menus - var menu = new TailoringMenu(from, _tool, (Category)craftIndex, _hue); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything in that category."); - return; - } - - from.SendMenu(menu); - return; - } - - var types = GetTypes(_category); - if (types != null && craftIndex >= 0 && craftIndex < types.Length) - { - CraftItem(from, types[craftIndex]); - } - } - - public static void ResourceSelection(Mobile from, BaseTool tool, Item preTarget = null) - { - if (preTarget != null && TrySelectResource(from, tool, preTarget)) - { - return; - } - - from.SendAsciiMessage("Select the resource you wish to use (cloth, leather, or hides)."); - from.Target = new ResourceSelectTarget(from, tool); - } - - private static bool TrySelectResource(Mobile from, BaseTool tool, Item targeted) - { - if (targeted is Cloth or UncutCloth) - { - var menu = new TailoringMenu(from, tool, Category.Main, targeted.Hue); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything."); - return true; - } - - from.SendMenu(menu); - return true; - } - - if (targeted is Items.Leather or Hides) - { - var menu = new TailoringMenu(from, tool, Category.LeatherMain); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything."); - return true; - } - - from.SendMenu(menu); - return true; - } - - return false; - } - - private class ResourceSelectTarget : Target - { - private readonly Mobile _from; - private readonly BaseTool _tool; - - public ResourceSelectTarget(Mobile from, BaseTool tool) : base(12, false, TargetFlags.None) - { - _from = from; - _tool = tool; - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is Item item && TrySelectResource(from, _tool, item)) - { - return; - } - - from.SendAsciiMessage("That is not a valid resource. Please select cloth, leather, or hides."); - from.Target = new ResourceSelectTarget(_from, _tool); - } - } -} diff --git a/Projects/UOContent/Engines/Craft/T2A/TinkeringMenu.cs b/Projects/UOContent/Engines/Craft/T2A/TinkeringMenu.cs deleted file mode 100644 index e823d68ee..000000000 --- a/Projects/UOContent/Engines/Craft/T2A/TinkeringMenu.cs +++ /dev/null @@ -1,529 +0,0 @@ -using System; -using Server.Items; -using Server.Menus.ItemLists; -using Server.Network; -using Server.Targeting; - -namespace Server.Engines.Craft.T2A; - -public class TinkeringMenu : ItemListMenu -{ - private enum Category - { - Main, - Wood, - Tools, - Parts, - Utensils, - Traps, - Misc, - Jewelry, - Necklaces, - Earrings, - Rings, - Keg - } - - private static readonly Type[] WoodItemTypes = - [ - typeof(JointingPlane), typeof(MouldingPlane), typeof(SmoothingPlane), - typeof(ClockFrame), typeof(Axle), typeof(RollingPin) - ]; - - private static readonly Type[] ToolTypes = - [ - typeof(SewingKit), typeof(TinkerTools), - typeof(DrawKnife), typeof(Froe), typeof(Inshave), typeof(Scorp), - typeof(Scissors), typeof(Tongs), - typeof(DovetailSaw), typeof(Saw), typeof(Hammer), - typeof(SmithHammer), typeof(SledgeHammer), typeof(Shovel), - typeof(MortarPestle), typeof(Hatchet), typeof(Pickaxe), typeof(Lockpick) - ]; - - private static readonly Type[] PartTypes = - [ - typeof(Gears), typeof(Springs), typeof(Hinge), - typeof(ClockParts), typeof(SextantParts), - typeof(BarrelTap), typeof(BarrelHoops), typeof(AxleGears) - ]; - - private static readonly Type[] UtensilTypes = - [ - typeof(ButcherKnife), typeof(Plate), typeof(Cleaver), - typeof(KnifeLeft), typeof(KnifeRight), typeof(SkinningKnife), - typeof(ForkLeft), typeof(ForkRight), - typeof(SpoonLeft), typeof(SpoonRight), - typeof(Goblet), typeof(PewterMug) - ]; - - private static readonly Type[] TrapTypes = - [ - typeof(DartTrapCraft), typeof(ExplosionTrapCraft), typeof(PoisonTrapCraft) - ]; - - private static readonly Type[] MiscTypes = - [ - typeof(KeyRing), typeof(Key), - typeof(Scales), typeof(Spyglass), typeof(Lantern), typeof(HeatingStand), - typeof(Globe), typeof(Candelabra), typeof(Sextant), - typeof(ClockRight), typeof(ClockLeft) - ]; - - private static readonly Type[] NecklaceTypes = [typeof(GoldNecklace), typeof(SilverNecklace)]; - private static readonly Type[] EarringTypes = [typeof(GoldEarrings), typeof(SilverEarrings)]; - private static readonly Type[] RingTypes = [typeof(GoldRing), typeof(SilverRing), typeof(WeddingRing)]; - - // Combined for AnyCraftableInCategory check on the Jewelry parent entry - private static readonly Type[] AllJewelryTypes = - [ - typeof(GoldNecklace), typeof(SilverNecklace), - typeof(GoldEarrings), typeof(SilverEarrings), - typeof(GoldRing), typeof(SilverRing), typeof(WeddingRing) - ]; - - private static readonly Type[] KegItemTypes = [typeof(PotionKeg)]; - - private static ItemListEntry[] _mainEntries; - private static ItemListEntry[] _woodEntries; - private static ItemListEntry[] _toolEntries; - private static ItemListEntry[] _partEntries; - private static ItemListEntry[] _utensilEntries; - private static ItemListEntry[] _trapEntries; - private static ItemListEntry[] _miscEntries; - private static ItemListEntry[] _necklaceEntries; - private static ItemListEntry[] _earringEntries; - private static ItemListEntry[] _ringEntries; - private static ItemListEntry[] _kegEntries; - - private readonly Category _category; - private readonly BaseTool _tool; - private readonly Type _selectedResourceType; - - private static string GetQuestion(Category category) => category switch - { - Category.Main => "What would you like to make?", - Category.Wood => "What kind of wooden item?", - Category.Tools => "What kind of tool?", - Category.Parts => "What kind of part?", - Category.Utensils => "What kind of utensil?", - Category.Traps => "What kind of trap?", - Category.Misc => "What would you like to make?", - Category.Jewelry => "What kind of jewelry?", - Category.Necklaces => "What kind of necklace?", - Category.Earrings => "What kind of earrings?", - Category.Rings => "What kind of ring?", - Category.Keg => "What would you like to make?", - _ => "What would you like to make?" - }; - - private TinkeringMenu(Mobile from, BaseTool tool, Category category, Type selectedResourceType) - : base(GetQuestion(category), BuildFilteredEntries(from, category)) - { - _tool = tool; - _category = category; - _selectedResourceType = selectedResourceType; - } - - private static string FormatItemName(Type type) - { - var name = type.Name; - Span buffer = stackalloc char[name.Length * 2]; - var pos = 0; - - for (var i = 0; i < name.Length; i++) - { - if (i > 0 && char.IsUpper(name[i])) - { - buffer[pos++] = ' '; - } - - buffer[pos++] = char.ToLower(name[i]); - } - - return new string(buffer[..pos]); - } - - private static ItemListEntry[] BuildStaticEntries(Type[] types, string resourceName) - { - var entries = new ItemListEntry[types.Length]; - var count = 0; - var craftItems = DefTinkering.CraftSystem.CraftItems; - - for (var i = 0; i < types.Length; i++) - { - var itemDef = craftItems.SearchFor(types[i]); - if (itemDef == null) - { - continue; - } - - var name = FormatItemName(types[i]); - var res = itemDef.Resources[0]; - entries[count++] = new ItemListEntry($"{name} ({res.Amount} {resourceName})", itemDef.ItemId, 0, i); - } - - if (count < entries.Length) - { - Array.Resize(ref entries, count); - } - - return entries; - } - - private static ItemListEntry[] GetStaticEntries(Category category) => category switch - { - Category.Main => Main(), - Category.Wood => Wood(), - Category.Tools => Tools(), - Category.Parts => Parts(), - Category.Utensils => Utensils(), - Category.Traps => Traps(), - Category.Misc => Misc(), - Category.Necklaces => Necklaces(), - Category.Earrings => Earrings(), - Category.Rings => Rings(), - Category.Keg => KegItems(), - _ => null - }; - - private static Type[] GetTypes(Category category) => category switch - { - Category.Wood => WoodItemTypes, - Category.Tools => ToolTypes, - Category.Parts => PartTypes, - Category.Utensils => UtensilTypes, - Category.Traps => TrapTypes, - Category.Misc => MiscTypes, - Category.Jewelry => AllJewelryTypes, - Category.Necklaces => NecklaceTypes, - Category.Earrings => EarringTypes, - Category.Rings => RingTypes, - Category.Keg => KegItemTypes, - _ => null - }; - - public static ItemListEntry[] Main() => _mainEntries ??= - [ - new ItemListEntry("Wooden Items", 0x1BDD, 0, (int)Category.Wood), - new ItemListEntry("Tools", 0x1EB8, 0, (int)Category.Tools), - new ItemListEntry("Parts", 0x1053, 0, (int)Category.Parts), - new ItemListEntry("Utensils", 0x9D7, 0, (int)Category.Utensils), - new ItemListEntry("Traps", 0x1BFC, 0, (int)Category.Traps), - new ItemListEntry("Miscellaneous", 0xA25, 0, (int)Category.Misc), - new ItemListEntry("Jewelry", 0x1088, 0, (int)Category.Jewelry) - ]; - - public static ItemListEntry[] Wood() => _woodEntries ??= BuildStaticEntries(WoodItemTypes, "logs"); - public static ItemListEntry[] Tools() => _toolEntries ??= BuildStaticEntries(ToolTypes, "ingots"); - public static ItemListEntry[] Parts() => _partEntries ??= BuildStaticEntries(PartTypes, "ingots"); - public static ItemListEntry[] Utensils() => _utensilEntries ??= BuildStaticEntries(UtensilTypes, "ingots"); - public static ItemListEntry[] Traps() => _trapEntries ??= BuildStaticEntries(TrapTypes, "ingots"); - public static ItemListEntry[] Misc() => _miscEntries ??= BuildStaticEntries(MiscTypes, "ingots"); - public static ItemListEntry[] Necklaces() => _necklaceEntries ??= BuildStaticEntries(NecklaceTypes, "ingots"); - public static ItemListEntry[] Earrings() => _earringEntries ??= BuildStaticEntries(EarringTypes, "ingots"); - public static ItemListEntry[] Rings() => _ringEntries ??= BuildStaticEntries(RingTypes, "ingots"); - public static ItemListEntry[] KegItems() => _kegEntries ??= BuildStaticEntries(KegItemTypes, "kegs"); - - private static ItemListEntry[] BuildFilteredEntries(Mobile from, Category category) - { - if (category == Category.Main) - { - return BuildFilteredMainEntries(from); - } - - if (category == Category.Jewelry) - { - return BuildFilteredJewelryEntries(from); - } - - var types = GetTypes(category); - var staticEntries = GetStaticEntries(category); - if (types == null || staticEntries == null) - { - return []; - } - - return T2ACraftSystem.FilterEntries(from, staticEntries, types, DefTinkering.CraftSystem); - } - - private static readonly ItemListEntry[] JewelrySubcategoryEntries = - [ - new("Necklaces", 0x1088, 0, (int)Category.Necklaces), - new("Earrings", 0x1087, 0, (int)Category.Earrings), - new("Rings", 0x108a, 0, (int)Category.Rings) - ]; - - private static ItemListEntry[] BuildFilteredJewelryEntries(Mobile from) - { - var system = DefTinkering.CraftSystem; - var filtered = new ItemListEntry[JewelrySubcategoryEntries.Length]; - var count = 0; - - for (var i = 0; i < JewelrySubcategoryEntries.Length; i++) - { - var entry = JewelrySubcategoryEntries[i]; - var types = GetTypes((Category)entry.CraftIndex); - if (types != null && T2ACraftSystem.AnyCraftableInCategory(from, types, system)) - { - filtered[count++] = entry; - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - private static ItemListEntry[] BuildFilteredMainEntries(Mobile from) - { - var system = DefTinkering.CraftSystem; - var mainStatic = Main(); - var filtered = new ItemListEntry[mainStatic.Length]; - var count = 0; - - for (var i = 0; i < mainStatic.Length; i++) - { - var entry = mainStatic[i]; - var types = GetTypes((Category)entry.CraftIndex); - if (types != null && T2ACraftSystem.AnyCraftableInCategory(from, types, system)) - { - filtered[count++] = entry; - } - } - - if (count == 0) - { - return []; - } - - if (count < filtered.Length) - { - Array.Resize(ref filtered, count); - } - - return filtered; - } - - public override void OnResponse(NetState state, int index) - { - var from = state.Mobile; - var craftIndex = Entries[index].CraftIndex; - - // Navigation categories: Main → subcategory, Jewelry → subcategory - if (_category is Category.Main or Category.Jewelry) - { - var childCategory = (Category)craftIndex; - var menu = new TinkeringMenu(from, _tool, childCategory, _selectedResourceType); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything in that category."); - return; - } - - from.SendMenu(menu); - return; - } - - // Jewelry leaf categories: prompt for gem targeting - if (_category is Category.Necklaces or Category.Earrings or Category.Rings) - { - var types = GetTypes(_category); - if (types == null || craftIndex < 0 || craftIndex >= types.Length) - { - return; - } - - var itemType = types[craftIndex]; - if (DefTinkering.CraftSystem.CraftItems.SearchFor(itemType) == null) - { - return; - } - - // Clear stale gem state from any previous craft attempt (e.g. failed skill check) - var ctx = DefTinkering.CraftSystem.GetContext(from); - if (ctx != null) - { - ctx.PendingGemType = GemType.None; - ctx.PendingGemCount = 0; - } - - from.SendAsciiMessage("Target the gemstone you wish to use."); - from.Target = new GemSelectTarget(from, _tool, itemType, _selectedResourceType); - return; - } - - // Leaf categories: craft directly - var leafTypes = GetTypes(_category); - if (leafTypes == null || craftIndex < 0 || craftIndex >= leafTypes.Length) - { - return; - } - - var leafItemType = leafTypes[craftIndex]; - var system = DefTinkering.CraftSystem; - var itemDef = system.CraftItems.SearchFor(leafItemType); - if (itemDef == null || _selectedResourceType == null) - { - return; - } - - // Persist selected resource index so make-last remembers it - T2ACraftSystem.SetLastResourceIndex(from, system, _selectedResourceType); - - itemDef.Craft(from, system, _selectedResourceType, _tool); - } - - public static void ResourceSelection(Mobile from, BaseTool tool, Item preTarget = null) - { - if (preTarget != null && TrySelectResource(from, tool, preTarget)) - { - return; - } - - from.SendAsciiMessage("Select the resource you wish to use (wood or ingots)."); - from.Target = new ResourceSelectTarget(from, tool); - } - - private static bool TrySelectResource(Mobile from, BaseTool tool, Item targeted) - { - if (targeted is Log or Board) - { - var menu = new TinkeringMenu(from, tool, Category.Wood, typeof(Log)); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything."); - return true; - } - - from.SendMenu(menu); - return true; - } - - if (targeted is BaseIngot) - { - var menu = new TinkeringMenu(from, tool, Category.Main, targeted.GetType()); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything."); - return true; - } - - from.SendMenu(menu); - return true; - } - - if (targeted is Keg) - { - var menu = new TinkeringMenu(from, tool, Category.Keg, typeof(Keg)); - if (menu.Entries.Length == 0) - { - from.SendAsciiMessage("You lack the skill and materials to craft anything."); - return true; - } - - from.SendMenu(menu); - return true; - } - - return false; - } - - private class ResourceSelectTarget : Target - { - private readonly Mobile _from; - private readonly BaseTool _tool; - - public ResourceSelectTarget(Mobile from, BaseTool tool) : base(12, false, TargetFlags.None) - { - _from = from; - _tool = tool; - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is Item item && TrySelectResource(from, _tool, item)) - { - return; - } - - from.SendAsciiMessage("That is not a valid resource. Please select wood or ingots."); - from.Target = new ResourceSelectTarget(_from, _tool); - } - } - - internal class GemSelectTarget : Target - { - private readonly Mobile _from; - private readonly BaseTool _tool; - private readonly Type _itemType; - private readonly Type _selectedResourceType; - - public GemSelectTarget(Mobile from, BaseTool tool, Type itemType, Type selectedResourceType) - : base(12, false, TargetFlags.None) - { - _from = from; - _tool = tool; - _itemType = itemType; - _selectedResourceType = selectedResourceType; - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is not Item gemItem) - { - from.SendAsciiMessage("That is not a gemstone."); - return; - } - - var gemType = BaseJewel.GetGemType(gemItem); - if (gemType == GemType.None) - { - from.SendAsciiMessage("That is not a gemstone."); - return; - } - - var amount = gemItem.Amount; - if (amount < 1) - { - from.SendAsciiMessage("That gemstone stack is empty."); - return; - } - - var system = DefTinkering.CraftSystem; - var itemDef = system.CraftItems.SearchFor(_itemType); - if (itemDef == null) - { - return; - } - - // Store pending gem info in craft context - var ctx = system.GetContext(from); - if (ctx == null) - { - return; - } - - ctx.PendingGemType = gemType; - ctx.PendingGemCount = amount; - - T2ACraftSystem.SetLastResourceIndex(from, system, _selectedResourceType); - itemDef.Craft(from, system, _selectedResourceType, _tool); - } - - protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) - { - if (cancelType == TargetCancelType.Canceled) - { - CraftItem.ShowCraftMenu(from, DefTinkering.CraftSystem, _tool); - } - } - } -} diff --git a/Projects/UOContent/Engines/Doom/GenGauntlet.cs b/Projects/UOContent/Engines/Doom/GenGauntlet.cs index 96c7a6f74..c770afa85 100644 --- a/Projects/UOContent/Engines/Doom/GenGauntlet.cs +++ b/Projects/UOContent/Engines/Doom/GenGauntlet.cs @@ -1,4 +1,4 @@ -using Server.Collections; +using System.Collections.Generic; using Server.Items; using Server.Mobiles; @@ -163,8 +163,8 @@ namespace Server.Engines.Doom public static BaseDoor CreateDoorSet(int xDoor, int yDoor, bool doorEastToWest, int hue) { - var hiDoor = new MetalDoor(doorEastToWest ? DoorFacing.NorthCCW : DoorFacing.WestCW); - var loDoor = new MetalDoor(doorEastToWest ? DoorFacing.SouthCW : DoorFacing.EastCCW); + BaseDoor hiDoor = new MetalDoor(doorEastToWest ? DoorFacing.NorthCCW : DoorFacing.WestCW); + BaseDoor loDoor = new MetalDoor(doorEastToWest ? DoorFacing.SouthCW : DoorFacing.EastCCW); hiDoor.MoveToWorld(new Point3D(xDoor, yDoor, -1), Map.Malas); loDoor.MoveToWorld( @@ -272,23 +272,18 @@ namespace Server.Engines.Doom FacialHairHue = 0x482 }; - using var toDelete = PooledRefQueue.Create(); + var items = new List(dealer.Items); - for (var i = 0; i < dealer.Items.Count; ++i) + for (var i = 0; i < items.Count; ++i) { - var item = dealer.Items[i]; + var item = items[i]; if (item.Layer is not Layer.ShopBuy and not Layer.ShopResale and not Layer.ShopSell) { - toDelete.Enqueue(item); + item.Delete(); } } - while (toDelete.Count > 0) - { - toDelete.Dequeue().Delete(); - } - dealer.AddItem(new FloppyHat(1)); dealer.AddItem(new Robe(1)); dealer.AddItem(new LanternOfSouls()); diff --git a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs index 598032120..8af67df58 100644 --- a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs +++ b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs @@ -321,8 +321,8 @@ public partial class LeverPuzzleController : Item { for (var i = 0; i < 4; i++) { - var l = GetLever(i); - if (l != null) + Item l; + if ((l = GetLever(i)) != null) { l.ItemID = 0x108E; Effects.PlaySound(l.Location, Map, 0x3E8); @@ -361,41 +361,43 @@ public partial class LeverPuzzleController : Item { PuzzleStatus(1050004); // The circle is the key... } - else if (TheirKey == MyKey) - { - GenKey(); - Successful = GetOccupant(0); - if (Successful != null) - { - SendLocationEffect(lp_Center, 0x1153, 0, 60, 1); - PlaySounds(lp_Center, cs1); - - Effects.SendBoltEffect(Successful); - Successful.MoveToWorld(lr_Enter, Map.Malas); - - m_Timer = new LampRoomTimer(this); - m_Timer.Start(); - Enabled = false; - } - } else { - for (var i = 0; i < 16; i++) /* Count matching SET bits, ie correct codes */ + Mobile player; + if (TheirKey == MyKey) { - if (((MyKey >> i) & 1) == 1 && ((TheirKey >> i) & 1) == 1) + GenKey(); + if ((Successful = player = GetOccupant(0)) != null) { - correct++; + SendLocationEffect(lp_Center, 0x1153, 0, 60, 1); + PlaySounds(lp_Center, cs1); + + Effects.SendBoltEffect(player); + player.MoveToWorld(lr_Enter, Map.Malas); + + m_Timer = new LampRoomTimer(this); + m_Timer.Start(); + Enabled = false; } } - - PuzzleStatus(Statue_Msg[correct], correct > 0 ? correct.ToString() : null); - - for (var i = 0; i < 5; i++) + else { - var player = GetOccupant(i); - if (player != null) + for (var i = 0; i < 16; i++) /* Count matching SET bits, ie correct codes */ { - new RockTimer(player).Start(); + if (((MyKey >> i) & 1) == 1 && ((TheirKey >> i) & 1) == 1) + { + correct++; + } + } + + PuzzleStatus(Statue_Msg[correct], correct > 0 ? correct.ToString() : null); + + for (var i = 0; i < 5; i++) + { + if ((player = GetOccupant(i)) != null) + { + new RockTimer(player).Start(); + } } } } @@ -502,7 +504,7 @@ public partial class LeverPuzzleController : Item /* I cant find any better way to send "speech" using fonts other than default */ public static void POHMessage(Mobile from, int index) { - var buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(Msgs[index])].InitializePacket(); + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(Msgs[index])].InitializePacket(); foreach (var state in from.Map.GetClientsInRange(from.Location)) { @@ -667,13 +669,12 @@ public partial class LeverPuzzleController : Item protected override void OnTick() { ticks++; - using var mobiles = m_Controller._lampRoom.GetMobilesPooled(); + var mobiles = m_Controller._lampRoom.GetMobiles(); if (ticks >= 71 || m_Controller._lampRoom.GetPlayerCount() == 0) { - for (var i = 0; i < mobiles.Count; i++) + foreach (var mobile in mobiles) { - var mobile = mobiles[i]; if (mobile?.Deleted == false && !mobile.IsDeadBondedPet) { mobile.Kill(); @@ -690,36 +691,33 @@ public partial class LeverPuzzleController : Item level++; } - for (var i = 0; i < mobiles.Count; i++) + foreach (var mobile in mobiles) { - var mobile = mobiles[i]; - if (!IsValidDamagable(mobile)) + if (IsValidDamagable(mobile)) { - continue; - } - - if (ticks % 2 == 0 && level == 5) - { - if (mobile.Player) + if (ticks % 2 == 0 && level == 5) { - mobile.Say(1062092); - if (AniSafe(mobile)) + if (mobile.Player) { - mobile.Animate(32, 5, 1, true, false, 0); + mobile.Say(1062092); + if (AniSafe(mobile)) + { + mobile.Animate(32, 5, 1, true, false, 0); + } } + + DoDamage(mobile, 15, 20, true); } - DoDamage(mobile, 15, 20, true); - } + if (Utility.Random((int)(level & ~0xfffffffc), 3) == 3) + { + mobile.ApplyPoison(mobile, PA2[level]); + } - if (Utility.Random((int)(level & ~0xfffffffc), 3) == 3) - { - mobile.ApplyPoison(mobile, PA2[level]); - } - - if (ticks % 12 == 0 && level > 0 && mobile.Player) - { - mobile.SendLocalizedMessage(PA[level][0], null, PA[level][1]); + if (ticks % 12 == 0 && level > 0 && mobile.Player) + { + mobile.SendLocalizedMessage(PA[level][0], null, PA[level][1]); + } } } diff --git a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleItems.cs b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleItems.cs index a3af7e72c..7c8362c5e 100644 --- a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleItems.cs +++ b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleItems.cs @@ -10,7 +10,7 @@ public partial class LampRoomBox : Item { [SerializableField(0)] private LeverPuzzleController _controller; - private WandererOfTheVoid _wanderer; + private Mobile _wanderer; public LampRoomBox(LeverPuzzleController controller) : base(0xe80) { diff --git a/Projects/UOContent/Engines/Ethics/Core/EthicsEntity.cs b/Projects/UOContent/Engines/Ethics/Core/EthicsEntity.cs index 5ce677cb8..aaf5af3be 100644 --- a/Projects/UOContent/Engines/Ethics/Core/EthicsEntity.cs +++ b/Projects/UOContent/Engines/Ethics/Core/EthicsEntity.cs @@ -16,6 +16,10 @@ public partial class EthicsEntity : ISerializable public Serial Serial { get; } + public byte SerializedThread { get; set; } + public int SerializedPosition { get; set; } + public int SerializedLength { get; set; } + public bool Deleted { get; private set; } public void Delete() diff --git a/Projects/UOContent/Engines/Ethics/Core/Player.cs b/Projects/UOContent/Engines/Ethics/Core/Player.cs index d8aad9eb3..f21fe9eb9 100644 --- a/Projects/UOContent/Engines/Ethics/Core/Player.cs +++ b/Projects/UOContent/Engines/Ethics/Core/Player.cs @@ -5,20 +5,9 @@ using Server.Mobiles; namespace Server.Ethics; [PropertyObject] -[SerializationGenerator(2)] +[SerializationGenerator(1)] public partial class Player : EthicsEntity { - private void MigrateFrom(V1Content content) - { - _mobile = content.Mobile; - _power = content.Power; - _history = content.History; - _steed = content.Steed; - _familiar = content.Familiar; - _shield = content.Shield; - _ethic = content.Ethic; - } - [SerializableField(0, setter: "private")] private Mobile _mobile; @@ -38,7 +27,7 @@ public partial class Player : EthicsEntity [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private Mobile _familiar; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(5, setter: "private")] private DateTime _shield; diff --git a/Projects/UOContent/Engines/Ethics/Evil/Powers/UnholySense.cs b/Projects/UOContent/Engines/Ethics/Evil/Powers/UnholySense.cs index 3ddbe1bec..56cfc3f84 100644 --- a/Projects/UOContent/Engines/Ethics/Evil/Powers/UnholySense.cs +++ b/Projects/UOContent/Engines/Ethics/Evil/Powers/UnholySense.cs @@ -45,7 +45,7 @@ public sealed class UnholySense : Power ++enemyCount; } - using var sb = new ValueStringBuilder(stackalloc char[96]); + using var sb = ValueStringBuilder.Create(); sb.Append($"You sense {(enemyCount == 0 ? "no" : enemyCount.ToString())} {(enemyCount == 1 ? "enemy" : "enemies")}"); if (primary != null) diff --git a/Projects/UOContent/Engines/Ethics/Hero/Ethic.cs b/Projects/UOContent/Engines/Ethics/Hero/Ethic.cs index f6720bfab..df7a8ec8e 100644 --- a/Projects/UOContent/Engines/Ethics/Hero/Ethic.cs +++ b/Projects/UOContent/Engines/Ethics/Hero/Ethic.cs @@ -31,7 +31,7 @@ public sealed partial class HeroEthic : Ethic } } - public override bool IsEligible(Mobile mob) => !mob.Murderer && Faction.Find(mob) is TrueBritannians or CouncilOfMages; + public override bool IsEligible(Mobile mob) => mob.Kills < 5 && Faction.Find(mob) is TrueBritannians or CouncilOfMages; [AfterDeserialization] private void AfterDeserialization() diff --git a/Projects/UOContent/Engines/Ethics/Hero/Powers/HolySense.cs b/Projects/UOContent/Engines/Ethics/Hero/Powers/HolySense.cs index d0dbc09d9..b6bcea48e 100644 --- a/Projects/UOContent/Engines/Ethics/Hero/Powers/HolySense.cs +++ b/Projects/UOContent/Engines/Ethics/Hero/Powers/HolySense.cs @@ -45,7 +45,7 @@ public sealed class HolySense : Power ++enemyCount; } - using var sb = new ValueStringBuilder(stackalloc char[96]); + using var sb = ValueStringBuilder.Create(); sb.Append($"You sense {(enemyCount == 0 ? "no" : enemyCount.ToString())} {(enemyCount == 1 ? "enemy" : "enemies")}"); if (primary != null) diff --git a/Projects/UOContent/Engines/Events/AllowedDays.cs b/Projects/UOContent/Engines/Events/AllowedDays.cs deleted file mode 100644 index d662a2e11..000000000 --- a/Projects/UOContent/Engines/Events/AllowedDays.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Server.Engines.Events; - -[Flags] -public enum AllowedDays : byte -{ - None = 0, - Sunday = 1 << 0, - Monday = 1 << 1, - Tuesday = 1 << 2, - Wednesday = 1 << 3, - Thursday = 1 << 4, - Friday = 1 << 5, - Saturday = 1 << 6, - All = Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday -} - -public static class DaysOfWeekExtension -{ - public static AllowedDays ToDaysOfWeek(this DayOfWeek dayOfWeek) => (AllowedDays)(1 << (int)dayOfWeek); -} diff --git a/Projects/UOContent/Engines/Events/AllowedMonths.cs b/Projects/UOContent/Engines/Events/AllowedMonths.cs deleted file mode 100644 index 1103e7b9e..000000000 --- a/Projects/UOContent/Engines/Events/AllowedMonths.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace Server.Engines.Events; - -[Flags] -public enum AllowedMonths -{ - None = 0, - January = 1 << 0, - February = 1 << 1, - March = 1 << 2, - April = 1 << 3, - May = 1 << 4, - June = 1 << 5, - July = 1 << 6, - August = 1 << 7, - September = 1 << 8, - October = 1 << 9, - November = 1 << 10, - December = 1 << 11, - - All = January | February | March | April | May | June | July | August | September | October | November | December -} diff --git a/Projects/UOContent/Engines/Events/BaseScheduledEvent.cs b/Projects/UOContent/Engines/Events/BaseScheduledEvent.cs deleted file mode 100644 index 279de8502..000000000 --- a/Projects/UOContent/Engines/Events/BaseScheduledEvent.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using Server.Logging; - -namespace Server.Engines.Events; - -public abstract class BaseScheduledEvent -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(BaseScheduledEvent)); - - public TimeZoneInfo TimeZone { get; private set; } = TimeZoneInfo.Utc; - public DateTime NextOccurrence { get; private set; } = DateTime.MaxValue; - public bool Cancelled { get; private set; } - public EventScheduler Scheduler { get; private set; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Schedule(DateTime startAfter, TimeZoneInfo timeZone = null) => - Schedule(EventScheduler.Shared, startAfter, timeZone); - - public void Schedule(EventScheduler scheduler, DateTime startAfter, TimeZoneInfo timeZone = null) - { - Cancel(); - Scheduler = scheduler; - TimeZone = timeZone ?? TimeZoneInfo.Utc; - Schedule(startAfter, timeZone, true); - } - - private void Schedule(DateTime startAfter, TimeZoneInfo timeZone, bool isFirst) - { - Cancelled = false; - var afterUtc = startAfter.Kind == DateTimeKind.Utc ? startAfter : startAfter.LocalToUtc(timeZone); - - var next = GetNextOccurrence(afterUtc); - - // For the first occurrence, we should set it to the startAfter date if we have no recurrence. - NextOccurrence = next == DateTime.MaxValue && isFirst ? afterUtc : next; - - if (NextOccurrence != DateTime.MaxValue) - { - Scheduler.ScheduleEvent(this); - } - } - - protected abstract DateTime GetNextOccurrence(DateTime after); - - public void Cancel() - { - Cancelled = true; - Scheduler?.UnscheduleEvent(this); - } - - public virtual void Advance() - { - try - { - OnEvent(); - } - catch (Exception ex) - { - logger.Error(ex, "OnEvent failed to execute."); - } - - Schedule(NextOccurrence, TimeZone, false); - } - - public abstract void OnEvent(); -} diff --git a/Projects/UOContent/Engines/Events/BroadcastEvent.cs b/Projects/UOContent/Engines/Events/BroadcastEvent.cs new file mode 100644 index 000000000..a900d263b --- /dev/null +++ b/Projects/UOContent/Engines/Events/BroadcastEvent.cs @@ -0,0 +1,30 @@ +namespace Server.Engines.Events +{ + public class BroadcastEvent : IEvent + { + private readonly int _hue; + private readonly string _text; + + public BroadcastEvent(int hue, string text) + { + _hue = hue; + _text = text; + } + + public void OnEventScheduled() + { + World.Broadcast(_hue, true, _text); + } + + public static void Initialize() + { + /* + EventScheduler.Instance.ScheduleEvent(new BroadcastEvent(22, "Test Message Please Ignore 2min"), 0, 0, TimeSpan.FromMinutes(2.0)); + EventScheduler.Instance.ScheduleEvent(new BroadcastEvent(33, "Test Message Please Ignore 3min"), 0, 0, TimeSpan.FromMinutes(3.0)); + EventScheduler.Instance.ScheduleEvent(new BroadcastEvent(44, "Test Message Please Ignore 4min"), 0, 0, TimeSpan.FromMinutes(4.0)); + */ + } + + public override string ToString() => $"Broadcast: {_text}"; + } +} diff --git a/Projects/UOContent/Engines/Events/CallbackScheduledEvent.cs b/Projects/UOContent/Engines/Events/CallbackScheduledEvent.cs deleted file mode 100644 index cab4e08fe..000000000 --- a/Projects/UOContent/Engines/Events/CallbackScheduledEvent.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace Server.Engines.Events; - -public sealed class CallbackScheduledEvent : ScheduledEvent -{ - private readonly Action _callback; - - public CallbackScheduledEvent( - TimeOnly time, - Action callback, - IRecurrencePattern recurrencePattern = null - ) : base(time, recurrencePattern) => _callback = callback ?? throw new ArgumentNullException(nameof(callback)); - - public CallbackScheduledEvent( - TimeOnly time, - DateTime endOn, - Action callback, - IRecurrencePattern recurrencePattern = null - ) : base(time, endOn, recurrencePattern) => _callback = callback ?? throw new ArgumentNullException(nameof(callback)); - - public override void OnEvent() => _callback(); -} diff --git a/Projects/UOContent/Engines/Events/CommonRecurrencePatterns.cs b/Projects/UOContent/Engines/Events/CommonRecurrencePatterns.cs deleted file mode 100644 index f60358f3f..000000000 --- a/Projects/UOContent/Engines/Events/CommonRecurrencePatterns.cs +++ /dev/null @@ -1,204 +0,0 @@ -namespace Server.Engines.Events; - -using System; - -public class HourlyRecurrencePattern : IRecurrencePattern -{ - public int IntervalHours { get; } - - public HourlyRecurrencePattern(int intervalHours = 1) => IntervalHours = Math.Max(1, intervalHours); - - public DateTime GetNextOccurrence(DateTime afterUtc, TimeOnly time, TimeZoneInfo timeZone) - { - var local = TimeZoneInfo.ConvertTimeFromUtc(afterUtc, timeZone); - return new DateTime(local.Year, local.Month, local.Day, local.Hour, time.Minute, 0) - .LocalToUtc(timeZone) - .AddHours(IntervalHours); - } -} - -public class DailyRecurrencePattern : IRecurrencePattern -{ - public int IntervalDays { get; } - - public DailyRecurrencePattern(int intervalDays = 1) => IntervalDays = Math.Max(1, intervalDays); - - public DateTime GetNextOccurrence(DateTime afterUtc, TimeOnly time, TimeZoneInfo timeZone) - { - var local = TimeZoneInfo.ConvertTimeFromUtc(afterUtc, timeZone); - return new DateTime(local.Year, local.Month, local.Day, time.Hour, time.Minute, 0) - .LocalToUtc(timeZone) - .AddDays(IntervalDays); - } -} - -public class WeeklyRecurrencePattern : IRecurrencePattern -{ - public int IntervalWeeks { get; } - public AllowedDays AllowedDays { get; } - public AllowedMonths AllowedMonths { get; } - - public WeeklyRecurrencePattern(int intervalWeeks = 1, AllowedMonths allowedMonths = AllowedMonths.All, AllowedDays allowedDays = AllowedDays.None) - { - IntervalWeeks = Math.Max(1, intervalWeeks); - AllowedDays = allowedDays == AllowedDays.None ? AllowedDays.All : allowedDays; - AllowedMonths = allowedMonths == AllowedMonths.None ? AllowedMonths.All : allowedMonths; - } - - public DateTime GetNextOccurrence(DateTime afterUtc, TimeOnly time, TimeZoneInfo timeZone) - { - var local = TimeZoneInfo.ConvertTimeFromUtc(afterUtc, timeZone); - var daysOfWeek = AllowedDays == AllowedDays.None ? local.DayOfWeek.ToDaysOfWeek() : AllowedDays; - - var weekStart = local.Date.AddDays(-(int)local.DayOfWeek); - - // No more days in this week, jump IntervalWeeks ahead - for (var week = 0; week <= 52; week++) - { - var nextWeekStart = weekStart.AddDays(7 * IntervalWeeks * week); - var weekEnd = nextWeekStart.AddDays(6); - - var startMonth = (AllowedMonths)(1 << (nextWeekStart.Month - 1)); - var endMonth = (AllowedMonths)(1 << (weekEnd.Month - 1)); - - // Skip the entire week if the start and end months are not in the allowed months - if ((AllowedMonths & (startMonth | endMonth)) == 0) - { - continue; - } - - for (var i = 0; i < 7; i++) - { - var day = nextWeekStart.AddDays(i); - - var currentMonth = (AllowedMonths)(1 << (day.Month - 1)); - if ((AllowedMonths & currentMonth) == 0) - { - continue; - } - - var dayOfWeekFlag = (AllowedDays)(1 << (int)day.DayOfWeek); - if ((daysOfWeek & dayOfWeekFlag) != 0) - { - var candidate = new DateTime(day.Year, day.Month, day.Day, time.Hour, time.Minute, 0); - if (candidate > local && !timeZone.IsInvalidTime(candidate)) - { - return candidate.LocalToUtc(timeZone); - } - } - } - } - - return DateTime.MaxValue; - } -} - -public class MonthlyRecurrencePattern : IRecurrencePattern -{ - public int DayOfMonth { get; } - public int IntervalMonths { get; } - - public MonthlyRecurrencePattern(int dayOfMonth = -1, int intervalMonths = 1) - { - DayOfMonth = dayOfMonth != -1 ? Math.Clamp(dayOfMonth, 1, 31) : -1; - IntervalMonths = Math.Max(1, intervalMonths); - } - - public DateTime GetNextOccurrence(DateTime afterUtc, TimeOnly time, TimeZoneInfo timeZone) - { - var local = TimeZoneInfo.ConvertTimeFromUtc(afterUtc, timeZone); - var year = local.Year; - var month = local.Month; - var day = DayOfMonth == -1 ? local.Day : DayOfMonth; - - for (var i = 0; i < 100; i++) - { - var nextMonth = month + IntervalMonths * i; - var candidate = new DateTime(year, 1, 1) - .AddMonths(nextMonth - 1) - .AddDays(day - 1) - .Add(time.ToTimeSpan()); - - // Some months may not have that day of the month, if not, we skip to the next interval - if (candidate > local && candidate.Day == day && !timeZone.IsInvalidTime(candidate)) - { - return candidate.LocalToUtc(timeZone); - } - } - - return DateTime.MaxValue; - } -} - -public class MonthlyOrdinalRecurrencePattern : IRecurrencePattern -{ - public int IntervalMonths { get; } - public OrdinalDayOccurrence Ordinal { get; } - public DayOfWeek DayOfWeek { get; } - - public MonthlyOrdinalRecurrencePattern(OrdinalDayOccurrence ordinal, DayOfWeek dayOfWeek, int intervalMonths = 1) - { - IntervalMonths = Math.Max(1, intervalMonths); - Ordinal = ordinal; - DayOfWeek = dayOfWeek; - } - - public DateTime GetNextOccurrence(DateTime afterUtc, TimeOnly time, TimeZoneInfo timeZone) - { - var local = TimeZoneInfo.ConvertTimeFromUtc(afterUtc, timeZone); - var year = local.Year; - var month = local.Month; - - for (var i = 0; i < 100; i++) - { - var nextMonth = month + IntervalMonths * i; - var candidateYearOffset = Math.DivRem(nextMonth - 1, 12, out var candidateMonthOffset); - var candidateYear = year + candidateYearOffset; - var candidateMonth = candidateMonthOffset + 1; - - DateTime candidate; - if (Ordinal >= OrdinalDayOccurrence.First) - { - // Find the first day of the month - var firstOfMonth = new DateTime(candidateYear, candidateMonth, 1) - .Add(time.ToTimeSpan()); - - // Find the first occurrence of the desired day - var daysOffset = ((int)DayOfWeek - (int)firstOfMonth.DayOfWeek + 7) % 7; - if (daysOffset > 7) - { - daysOffset -= 7; - } - candidate = firstOfMonth.AddDays(daysOffset + 7 * (int)Ordinal); - - // If candidate is not in the same month, skip - if (candidate.Month != candidateMonth) - { - continue; - } - } - else - { - // Find the last day of the month - var daysInMonth = DateTime.DaysInMonth(candidateYear, candidateMonth); - var lastOfMonth = new DateTime(candidateYear, candidateMonth, daysInMonth) - .Add(time.ToTimeSpan()); - - // Find the last occurrence of the desired day - var daysOffset = (int)lastOfMonth.DayOfWeek - (int)DayOfWeek + 7; - if (daysOffset >= 7) - { - daysOffset -= 7; - } - candidate = lastOfMonth.AddDays(-daysOffset); - } - - if (candidate > local && !timeZone.IsInvalidTime(candidate)) - { - return candidate.LocalToUtc(timeZone); - } - } - - return DateTime.MaxValue; - } -} diff --git a/Projects/UOContent/Engines/Events/EventScheduler.cs b/Projects/UOContent/Engines/Events/EventScheduler.cs index 992110371..363bfa320 100644 --- a/Projects/UOContent/Engines/Events/EventScheduler.cs +++ b/Projects/UOContent/Engines/Events/EventScheduler.cs @@ -1,146 +1,95 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -namespace Server.Engines.Events; - -public interface IRecurrencePattern +namespace Server.Engines.Events { - /// - /// Get the next occurrence of the event. - /// DateTime of the next occurence in UTC or DateTime.MaxValue - /// - DateTime GetNextOccurrence(DateTime afterUtc, TimeOnly time, TimeZoneInfo timeZone); -} - -public enum OrdinalDayOccurrence { Last = -1, First, Second, Third, Fourth, Fifth } - -public class EventScheduler : Timer -{ - private readonly PriorityQueue _schedule = new(); - - public static EventScheduler Shared { get; private set; } - - public static IRecurrencePattern Hourly => new HourlyRecurrencePattern(); - - public static IRecurrencePattern Daily => new DailyRecurrencePattern(); - - // Recur every week, on the same day/time as the first occurence - public static IRecurrencePattern Weekly => new WeeklyRecurrencePattern(); - - // Recur every two weeks, on the same day/time as the first occurence - public static IRecurrencePattern Biweekly => new WeeklyRecurrencePattern(2); - - public static IRecurrencePattern Monthly => new MonthlyRecurrencePattern(); - - public static IRecurrencePattern Yearly => new MonthlyRecurrencePattern(-1, 12); - - // For each of the days of the week - private static readonly Dictionary _monthlyRecurrenceByDay = []; - - public static IRecurrencePattern GetMonthlyRecurrence(int dayOfMonth) + public interface IEvent { - ref var pattern = ref CollectionsMarshal.GetValueRefOrAddDefault(_monthlyRecurrenceByDay, dayOfMonth, out var exists); - if (!exists) + void OnEventScheduled(); + } + + public class EventScheduleEntry + { + private readonly IEvent _event; + private TimeSpan _offset; + + public EventScheduleEntry(IEvent e, DateTime firstSpawn, TimeSpan interval, TimeSpan offset) { - pattern = new MonthlyRecurrencePattern(dayOfMonth); + _offset = offset; + _event = e; + Interval = interval; + NextOccurrence = firstSpawn; } - return pattern; - } + public DateTime NextOccurrence { get; private set; } + public TimeSpan Interval { get; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScheduledEvent HourlyAt(DateTime startOn, Action action, TimeZoneInfo timeZone = null) => - Shared.ScheduleEvent(startOn, action, Hourly, timeZone); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScheduledEvent DailyAt(DateTime startOn, Action action, TimeZoneInfo timeZone = null) => - Shared.ScheduleEvent(startOn, action, Daily, timeZone); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScheduledEvent WeeklyAt(DateTime startOn, Action action, TimeZoneInfo timeZone = null) => - Shared.ScheduleEvent(startOn, action, Weekly, timeZone); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScheduledEvent BiweeklyAt(DateTime startOn, Action action, TimeZoneInfo timeZone = null) => - Shared.ScheduleEvent(startOn, action, Biweekly, timeZone); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScheduledEvent MonthlyAt(DateTime startOn, Action action, TimeZoneInfo timeZone = null) => - Shared.ScheduleEvent(startOn, action, GetMonthlyRecurrence(startOn.Day), timeZone); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScheduledEvent YearlyAt(DateTime startOn, Action action, TimeZoneInfo timeZone = null) => - Shared.ScheduleEvent(startOn, action, GetMonthlyRecurrence(startOn.Day), timeZone); - - public static void Configure() - { - Shared ??= new EventScheduler(); - Shared.Start(); - } - - private EventScheduler() : base(TimeSpan.Zero, TimeSpan.FromSeconds(1.0)) - { - } - - public ScheduledEvent ScheduleEvent( - DateTime startOn, - Action callback, - IRecurrencePattern recurrencePattern = null, - TimeZoneInfo timeZone = null - ) => ScheduleEvent(startOn, TimeOnly.FromDateTime(startOn), callback, recurrencePattern, timeZone); - - public ScheduledEvent ScheduleEvent( - DateTime after, - TimeOnly time, - Action callback, - IRecurrencePattern recurrencePattern = null, - TimeZoneInfo timeZone = null - ) - { - var scheduledEvent = new CallbackScheduledEvent(time, callback, recurrencePattern); - scheduledEvent.Schedule(this, after, timeZone); - return scheduledEvent; - } - - public void ScheduleEvent(BaseScheduledEvent entry) - { - if (entry != null && entry.NextOccurrence < DateTime.MaxValue) + public void Occur() { - _schedule.Enqueue(entry, entry.NextOccurrence); + NextOccurrence += Interval; + + _event?.OnEventScheduled(); } + + public override string ToString() => _event?.ToString(); } - public void UnscheduleEvent(BaseScheduledEvent entry) + public class EventScheduler : Timer { - if (entry != null) + private static EventScheduler _instance; + private readonly List _schedule = new(); + + private EventScheduler() : base(TimeSpan.Zero, TimeSpan.FromSeconds(1.0)) { - _schedule.Remove(entry, out _, out _); } - } - protected override void OnTick() - { - var now = Core.Now; + public static EventScheduler Instance => _instance ??= new EventScheduler(); + public static List AvailableEvents { get; } = new(); - while (_schedule.Count > 0) + public static void Initialize() { - var entry = _schedule.Peek(); - var cancelled = entry.Cancelled; - if (!cancelled && entry.NextOccurrence > now) + Instance.Start(); + } + + public void ScheduleEvent(IEvent e, int hour, int min) + { + ScheduleEvent(e, hour, min, TimeSpan.FromDays(1.0)); + } + + public void ScheduleEvent(IEvent e, int hour, int min, TimeSpan interval) + { + var now = Core.Now; + var firstRun = new DateTime(now.Year, now.Month, now.Day, hour, min, 0); + + while (now > firstRun) { - break; + firstRun += interval; } - _schedule.Dequeue(); + ScheduleEvent( + new EventScheduleEntry(e, firstRun, interval, TimeSpan.FromHours(hour) + TimeSpan.FromMinutes(min)) + ); + } - if (cancelled) + public void ScheduleEvent(EventScheduleEntry e) + { + _schedule.Add(e); + } + + public void RemoveEvent(EventScheduleEntry entry) + { + _schedule.Remove(entry); + } + + protected override void OnTick() + { + foreach (var entry in _schedule) { - continue; + if (entry.NextOccurrence <= Core.Now) + { + entry.Occur(); + } } - - entry.Advance(); // Advances the event and self queues if necessary } } } diff --git a/Projects/UOContent/Engines/Events/MonthDay.cs b/Projects/UOContent/Engines/Events/MonthDay.cs deleted file mode 100644 index e4b4e7e34..000000000 --- a/Projects/UOContent/Engines/Events/MonthDay.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; - -namespace Server.Engines.Events; - -public record struct MonthDay : IComparable -{ - public byte Month { get; } - public byte Day { get; } - - public MonthDay(int year, int month, int day) - { - if (month is < 1 or > 12) - { - throw new ArgumentOutOfRangeException(nameof(month), "Month must be between 1 and 12."); - } - - var daysInMonth = DateTime.DaysInMonth(year, month); - - if (day < 1 || day > daysInMonth) - { - throw new ArgumentOutOfRangeException(nameof(day), $"Day must be between 1 and {daysInMonth} for month {month}."); - } - - Month = (byte)month; - Day = (byte)day; - } - - public int CompareTo(MonthDay other) - { - var monthComparison = Month.CompareTo(other.Month); - return monthComparison != 0 ? monthComparison : Day.CompareTo(other.Day); - } - - public static bool operator <(MonthDay left, MonthDay right) => left.CompareTo(right) < 0; - - public static bool operator >(MonthDay left, MonthDay right) => left.CompareTo(right) > 0; - - public static bool operator <=(MonthDay left, MonthDay right) => left.CompareTo(right) <= 0; - - public static bool operator >=(MonthDay left, MonthDay right) => left.CompareTo(right) >= 0; -} - -public static class MonthDayExtensions -{ - public static bool IsBetween(this DateTime dateTime, MonthDay start, MonthDay end) - { - var checkMonth = dateTime.Month; - var checkDay = dateTime.Day; - - var startMonth = start.Month; - var startDay = start.Day; - var endMonth = end.Month; - var endDay = end.Day; - - var isAfterStart = checkMonth > startMonth || checkMonth == startMonth && checkDay >= startDay; - var isBeforeEnd = checkMonth < endMonth || checkMonth == endMonth && checkDay <= endDay; - - if (startMonth < endMonth || startMonth == endMonth && startDay <= endDay) - { - return isAfterStart && isBeforeEnd; - } - - // Complex case: spans year boundary (e.g., Nov 15 - Feb 15) - return isAfterStart || isBeforeEnd; - } -} diff --git a/Projects/UOContent/Engines/Events/ScheduledEvent.cs b/Projects/UOContent/Engines/Events/ScheduledEvent.cs deleted file mode 100644 index 3f90227cb..000000000 --- a/Projects/UOContent/Engines/Events/ScheduledEvent.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace Server.Engines.Events; - -public abstract class ScheduledEvent : BaseScheduledEvent -{ - public IRecurrencePattern Recurrence { get; } - public TimeOnly Time { get; } - public DateTime EndDate { get; } - - public ScheduledEvent(TimeOnly time, IRecurrencePattern recurrence = null) - : this(time, DateTime.MaxValue, recurrence) - { - } - - public ScheduledEvent( - TimeOnly time, - DateTime endOn, - IRecurrencePattern recurrence = null - ) - { - Time = time; - Recurrence = recurrence; - EndDate = endOn == DateTime.MaxValue || endOn.Kind == DateTimeKind.Utc ? endOn : endOn.LocalToUtc(TimeZone); - } - - protected override DateTime GetNextOccurrence(DateTime after) - { - var next = Recurrence?.GetNextOccurrence(after, Time, TimeZone) ?? DateTime.MaxValue; - return next >= EndDate ? DateTime.MaxValue : next; - } -} diff --git a/Projects/UOContent/Engines/Events/YearlyCallbackScheduledEvent.cs b/Projects/UOContent/Engines/Events/YearlyCallbackScheduledEvent.cs deleted file mode 100644 index f4ca34f65..000000000 --- a/Projects/UOContent/Engines/Events/YearlyCallbackScheduledEvent.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; - -namespace Server.Engines.Events; - -public class YearlyCallbackScheduledEvent : YearlyScheduledEvent -{ - private readonly Action _callback; - - protected YearlyCallbackScheduledEvent( - TimeOnly time, - MonthDay yearlyStart, - MonthDay yearlyEnd, - Action callback, - IRecurrencePattern recurrence - ) : this(time, yearlyStart, yearlyEnd, DateTime.MaxValue, callback, recurrence) - { - } - - protected YearlyCallbackScheduledEvent( - TimeOnly time, - MonthDay yearlyStart, - MonthDay yearlyEnd, - DateTime endOn, - Action callback, - IRecurrencePattern recurrence - ) : base(time, yearlyStart, yearlyEnd, endOn, recurrence) => _callback = callback; - - public override void OnEvent() => _callback(); -} diff --git a/Projects/UOContent/Engines/Events/YearlyScheduledEvent.cs b/Projects/UOContent/Engines/Events/YearlyScheduledEvent.cs deleted file mode 100644 index 0deceaca8..000000000 --- a/Projects/UOContent/Engines/Events/YearlyScheduledEvent.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; - -namespace Server.Engines.Events; - -public abstract class YearlyScheduledEvent : ScheduledEvent -{ - public MonthDay YearlyStart { get; } - - public MonthDay YearlyEnd { get; } - - protected YearlyScheduledEvent( - TimeOnly time, - MonthDay yearlyStart, - MonthDay yearlyEnd, - IRecurrencePattern recurrence - ) : this( time, yearlyStart, yearlyEnd, DateTime.MaxValue, recurrence) - { - } - - protected YearlyScheduledEvent( - TimeOnly time, - MonthDay yearlyStart, - MonthDay yearlyEnd, - DateTime endOn, - IRecurrencePattern recurrence - ) : base(time, endOn, recurrence) - { - YearlyStart = yearlyStart; - YearlyEnd = yearlyEnd; - } - - protected override DateTime GetNextOccurrence(DateTime after) - { - var next = base.GetNextOccurrence(after); - - if (next == DateTime.MaxValue) - { - return DateTime.MaxValue; - } - - var localNext = TimeZoneInfo.ConvertTimeFromUtc(next, TimeZone); - - if (localNext.IsBetween(YearlyStart, YearlyEnd)) - { - return next; - } - - int yearToUse; - - // If we're after the end of this year's range but before the start of next year's range - if (YearlyStart > YearlyEnd && localNext.Month > YearlyEnd.Month) - { - // We're in the same calendar year, targeting this year's start - yearToUse = localNext.Year; - } - else - { - // Either we're in a non-spanning range, or we're in the early part of next year - // In either case, we need to advance to the next year's start - yearToUse = localNext.Year + 1; - } - - var nextYearStartUtc = new DateTime(yearToUse, YearlyStart.Month, YearlyStart.Day).LocalToUtc(TimeZone); - - return Recurrence!.GetNextOccurrence(nextYearStartUtc, Time, TimeZone); - } -} diff --git a/Projects/UOContent/Engines/Factions/Core/Faction.cs b/Projects/UOContent/Engines/Factions/Core/Faction.cs index 6c440584b..7368ebe05 100644 --- a/Projects/UOContent/Engines/Factions/Core/Faction.cs +++ b/Projects/UOContent/Engines/Factions/Core/Faction.cs @@ -16,7 +16,7 @@ using Server.Targeting; namespace Server.Factions; [CustomEnum(["Minax", "Council of Mages", "True Britannians", "Shadowlords"])] -public abstract class Faction : IComparable, ISpanParsable +public abstract class Faction : IComparable { public const int StabilityFactor = 300; // 300% greater (3 times) than smallest faction public const int StabilityActivation = 200; // Stability code goes into effect when largest faction has > 200 people @@ -242,11 +242,7 @@ public abstract class Faction : IComparable, ISpanParsable public virtual void AddMember(Mobile mob) { - var state = new PlayerState(mob, this, Members); - Members.Insert(ZeroRankOffset, state); - - // Ranked after the insert: the ctor ran while Owner was still short a member. - state.UpdateRank(); + Members.Insert(ZeroRankOffset, new PlayerState(mob, this, Members)); mob.AddToBackpack(FactionItem.Imbue(new Robe(), this, false, Definition.HuePrimary)); mob.SendLocalizedMessage(1010374); // You have been granted a robe which signifies your faction @@ -287,8 +283,8 @@ public abstract class Faction : IComparable, ISpanParsable public static bool IsNearType(Mobile mob, Type[] types, int range) { - var mobs = false; - var items = false; + bool mobs = false; + bool items = false; for (var i = 0; !(mobs && items) && i < types.Length; i++) { var type = types[i]; @@ -414,8 +410,7 @@ public abstract class Faction : IComparable, ISpanParsable if (mob.Backpack != null) { - using var queue = mob.Backpack.EnumerateItemsByType(); - foreach (var sigil in queue) + foreach (var sigil in mob.Backpack.EnumerateItemsByType()) { sigil.ReturnHome(); } @@ -1078,8 +1073,7 @@ public abstract class Faction : IComparable, ISpanParsable if (victim.Backpack != null) { - using var queue = victim.Backpack.EnumerateItemsByType(); - foreach (var sigil in queue) + foreach (var sigil in victim.Backpack.EnumerateItemsByType()) { if (killerState == null || killerPack == null) { @@ -1260,8 +1254,7 @@ public abstract class Faction : IComparable, ISpanParsable { if (m.Backpack != null) { - using var queue = m.Backpack.EnumerateItemsByType(); - foreach (var sigil in queue) + foreach (var sigil in m.Backpack.EnumerateItemsByType()) { sigil.ReturnHome(); } @@ -1317,27 +1310,7 @@ public abstract class Faction : IComparable, ISpanParsable return null; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Faction Parse(string s) => Parse(s, null); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Faction Parse(string s, IFormatProvider provider) => Parse(s.AsSpan(), provider); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryParse(string s, IFormatProvider provider, out Faction result) => - TryParse(s.AsSpan(), provider, out result); - - public static Faction Parse(ReadOnlySpan s, IFormatProvider provider) - { - if (TryParse(s, provider, out var result)) - { - return result; - } - - throw new FormatException($"The input string '{s}' was not in a correct format."); - } - - public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, out Faction result) + public static Faction Parse(string name) { var factions = Factions; @@ -1345,15 +1318,13 @@ public abstract class Faction : IComparable, ISpanParsable { var faction = factions[i]; - if (s.InsensitiveEquals(faction.Definition.FriendlyName)) + if (faction.Definition.FriendlyName.InsensitiveEquals(name)) { - result = faction; - return true; + return faction; } } - result = null; - return false; + return null; } public static bool InSkillLoss(Mobile mob) => m_SkillLoss.ContainsKey(mob); diff --git a/Projects/UOContent/Engines/Factions/Core/FactionItem.cs b/Projects/UOContent/Engines/Factions/Core/FactionItem.cs index 42b3adc00..ad874e936 100644 --- a/Projects/UOContent/Engines/Factions/Core/FactionItem.cs +++ b/Projects/UOContent/Engines/Factions/Core/FactionItem.cs @@ -87,7 +87,10 @@ public class FactionItem item.FactionItemState = null; } - Faction?.State.FactionItems.Remove(this); + if (Faction?.State.FactionItems.Contains(this) == true) + { + Faction.State.FactionItems.Remove(this); + } } public void Serialize(IGenericWriter writer) diff --git a/Projects/UOContent/Engines/Factions/Core/FactionState.cs b/Projects/UOContent/Engines/Factions/Core/FactionState.cs index b8eba5091..0170c718c 100644 --- a/Projects/UOContent/Engines/Factions/Core/FactionState.cs +++ b/Projects/UOContent/Engines/Factions/Core/FactionState.cs @@ -114,13 +114,6 @@ public class FactionState } } - // The loop above only assigns RankIndex to members with kill points, and nothing - // computes rank on read, so rank everyone now that the ordering has settled. - foreach (var player in Members) - { - player.UpdateRank(); - } - FactionItems = []; if (version >= 2) diff --git a/Projects/UOContent/Engines/Factions/Core/Generator.cs b/Projects/UOContent/Engines/Factions/Core/Generator.cs index 1932eca8a..7e950ab95 100644 --- a/Projects/UOContent/Engines/Factions/Core/Generator.cs +++ b/Projects/UOContent/Engines/Factions/Core/Generator.cs @@ -46,11 +46,7 @@ public static class Generator { var mono = new TownMonolith(town); mono.MoveToWorld(def.Monolith, facet); - - if (!SigilExistsForTown(town)) - { - mono.Sigil = new Sigil(town); - } + mono.Sigil = new Sigil(town); } if (!CheckExistence(def.TownStone, facet, typeof(TownStone))) @@ -100,19 +96,4 @@ public static class Generator return false; } - - private static bool SigilExistsForTown(Town town) - { - var sigils = Sigil.Sigils; - - for (var i = 0; i < sigils.Count; i++) - { - if (!sigils[i].Deleted && sigils[i].Town == town) - { - return true; - } - } - - return false; - } } \ No newline at end of file diff --git a/Projects/UOContent/Engines/Factions/Core/PlayerState.cs b/Projects/UOContent/Engines/Factions/Core/PlayerState.cs index e420836ce..792d2b4e3 100644 --- a/Projects/UOContent/Engines/Factions/Core/PlayerState.cs +++ b/Projects/UOContent/Engines/Factions/Core/PlayerState.cs @@ -8,6 +8,7 @@ public class PlayerState : IComparable { private Town m_Finance; + private bool m_InvalidateRank = true; private int m_KillPoints; private MerchantTitle m_MerchantTitle; private RankDefinition m_Rank; @@ -21,10 +22,6 @@ public class PlayerState : IComparable Faction = faction; Owner = owner; - // Owner does not contain this state yet, so the count is short by one; the caller ranks it - // after inserting. - SeedLowestRank(); - Attach(); Invalidate(); } @@ -57,9 +54,6 @@ public class PlayerState : IComparable } } - // Members are still being read; FactionState ranks everyone once the ordering settles. - SeedLowestRank(); - Attach(); } @@ -122,8 +116,6 @@ public class PlayerState : IComparable Owner.Remove(this); Owner.Insert(Faction.ZeroRankOffset, this); - // Direct, not through RankIndex: ZeroRankOffset is mid-update. The - // UpdateRank() at the end of this setter covers it. m_RankIndex = Faction.ZeroRankOffset; Faction.ZeroRankOffset++; } @@ -188,7 +180,6 @@ public class PlayerState : IComparable } m_KillPoints = value; - UpdateRank(); Invalidate(); } } @@ -202,72 +193,49 @@ public class PlayerState : IComparable if (m_RankIndex != value) { m_RankIndex = value; + m_InvalidateRank = true; + } + } + } + + public RankDefinition Rank + { + get + { + if (m_InvalidateRank) + { + var ranks = Faction.Definition.Ranks; + int percent; + + if (Owner.Count == 1) + { + percent = 1000; + } + else if (m_RankIndex == -1) + { + percent = 0; + } + else + { + percent = (Faction.ZeroRankOffset - m_RankIndex) * 1000 / Faction.ZeroRankOffset; + } + + for (var i = 0; i < ranks.Length; i++) + { + var check = ranks[i]; + + if (percent >= check.Required) + { + m_Rank = check; + m_InvalidateRank = false; + break; + } + } - UpdateRank(); Invalidate(); } - } - } - /// - /// Read from PlayerMobile.GetProperties, so it must stay a plain field read -- recomputing or - /// invalidating here re-enters the property list build. Maintained by . - /// - public RankDefinition Rank => m_Rank; - - // Lowest rank (Required 0): correct for an unranked member, and never null, so Rank.Title - // cannot NRE before the first UpdateRank(). - private void SeedLowestRank() - { - var ranks = Faction.Definition.Ranks; - - if (ranks.Length > 0) - { - m_Rank = ranks[^1]; - } - } - - /// - /// Recomputes the cached rank. Call whenever , the faction's - /// ZeroRankOffset, or the member count changes -- and only once they have settled. - /// - public void UpdateRank() - { - var ranks = Faction.Definition.Ranks; - - if (ranks.Length == 0) - { - return; - } - - int percent; - - if (Owner.Count == 1) - { - percent = 1000; - } - else if (m_RankIndex == -1 || Faction.ZeroRankOffset <= 0) - { - percent = 0; - } - else - { - percent = (Faction.ZeroRankOffset - m_RankIndex) * 1000 / Faction.ZeroRankOffset; - } - - // Ranks run Required-descending ending at 0, so anything >= 0 matches below. A negative - // percent (RankIndex out of sync with ZeroRankOffset) would otherwise leave it null. - m_Rank = ranks[^1]; - - for (var i = 0; i < ranks.Length; i++) - { - var check = ranks[i]; - - if (percent >= check.Required) - { - m_Rank = check; - break; - } + return m_Rank; } } diff --git a/Projects/UOContent/Engines/Factions/Core/StrongholdRegion.cs b/Projects/UOContent/Engines/Factions/Core/StrongholdRegion.cs index 51049811f..8397e9e0e 100644 --- a/Projects/UOContent/Engines/Factions/Core/StrongholdRegion.cs +++ b/Projects/UOContent/Engines/Factions/Core/StrongholdRegion.cs @@ -13,7 +13,6 @@ public class StrongholdRegion : BaseRegion ) { Faction = faction; - GoLocation = faction.Definition.Stronghold.FactionStone; Register(); } diff --git a/Projects/UOContent/Engines/Factions/Core/Town.cs b/Projects/UOContent/Engines/Factions/Core/Town.cs index a30dc23a6..2dcca8c78 100644 --- a/Projects/UOContent/Engines/Factions/Core/Town.cs +++ b/Projects/UOContent/Engines/Factions/Core/Town.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using Server.Targeting; namespace Server.Factions; [CustomEnum(["Britain", "Magincia", "Minoc", "Moonglow", "Skara Brae", "Trinsic", "Vesper", "Yew"])] -public abstract class Town : IComparable, ISpanParsable +public abstract class Town : IComparable { public const int SilverCaptureBonus = 10000; @@ -351,8 +350,28 @@ public abstract class Town : IComparable, ISpanParsable return true; } - public bool UnregisterGuard(BaseFactionGuard guard) => - guard != null && FindGuardList(guard.GetType())?.Guards.Remove(guard) == true; + public bool UnregisterGuard(BaseFactionGuard guard) + { + if (guard == null) + { + return false; + } + + var guardList = FindGuardList(guard.GetType()); + + if (guardList == null) + { + return false; + } + + if (!guardList.Guards.Contains(guard)) + { + return false; + } + + guardList.Guards.Remove(guard); + return true; + } public bool RegisterVendor(BaseFactionVendor vendor) { @@ -483,27 +502,7 @@ public abstract class Town : IComparable, ISpanParsable return null; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Town Parse(string s) => Parse(s, null); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Town Parse(string s, IFormatProvider provider) => Parse(s.AsSpan(), provider); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryParse(string s, IFormatProvider provider, out Town result) => - TryParse(s.AsSpan(), provider, out result); - - public static Town Parse(ReadOnlySpan s, IFormatProvider provider) - { - if (TryParse(s, provider, out var result)) - { - return result; - } - - throw new FormatException($"The input string '{s}' was not in a correct format."); - } - - public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, out Town result) + public static Town Parse(string name) { var towns = Towns; @@ -511,15 +510,13 @@ public abstract class Town : IComparable, ISpanParsable { var town = towns[i]; - if (s.InsensitiveEquals(town.Definition.FriendlyName)) + if (town.Definition.FriendlyName.InsensitiveEquals(name)) { - result = town; - return true; + return town; } } - result = null; - return false; + return null; } [Usage("GrantTownSilver ")] diff --git a/Projects/UOContent/Engines/Factions/Gumps/ElectionManagementGump.cs b/Projects/UOContent/Engines/Factions/Gumps/ElectionManagementGump.cs index 723864983..5c9a6fad5 100644 --- a/Projects/UOContent/Engines/Factions/Gumps/ElectionManagementGump.cs +++ b/Projects/UOContent/Engines/Factions/Gumps/ElectionManagementGump.cs @@ -32,7 +32,7 @@ public class ElectionManagementGump : Gump AddHtml(145, 35, 100, 20, (candidate.Mobile == null ? "null" : candidate.Mobile.Name).Color(LabelColor)); AddHtml(45, 55, 100, 20, "Vote Count:".Color(LabelColor)); - AddHtml(145, 55, 100, 20, Html.Color($"{candidate.Votes}", LabelColor)); + AddHtml(145, 55, 100, 20, candidate.Votes.ToString().Color(LabelColor)); AddButton(12, 73, 4005, 4007, 1); AddHtml(45, 75, 100, 20, "Drop Candidate".Color(LabelColor)); @@ -88,9 +88,9 @@ public class ElectionManagementGump : Gump AddHtml(x + 2, 140 + idx * 20, 150, 20, mobile.Name.Color(LabelColor)); x += 150; } - else if (obj is IPAddress ip) + else if (obj is IPAddress) { - AddHtml(x, 140 + idx * 20, 100, 20, Html.Center($"{ip}", LabelColor)); + AddHtml(x, 140 + idx * 20, 100, 20, obj.ToString().Center(LabelColor)); x += 100; } else if (obj is DateTime time) @@ -106,7 +106,7 @@ public class ElectionManagementGump : Gump } else if (obj is int i1) { - AddHtml(x, 140 + idx * 20, 60, 20, Html.Center($"{i1}%", LabelColor)); + AddHtml(x, 140 + idx * 20, 60, 20, $"{i1}%".Center(LabelColor)); x += 60; } } @@ -120,7 +120,7 @@ public class ElectionManagementGump : Gump AddHtml(10, 10, 268, 20, "Election Management".Center(LabelColor)); AddHtml(45, 35, 100, 20, "Current State:".Color(LabelColor)); - AddHtml(145, 35, 100, 20, Html.Color($"{election.State}", LabelColor)); + AddHtml(145, 35, 100, 20, election.State.ToString().Color(LabelColor)); AddButton(12, 53, 4005, 4007, 1); AddHtml(45, 55, 100, 20, "Transition Time:".Color(LabelColor)); @@ -147,7 +147,7 @@ public class ElectionManagementGump : Gump AddButton(13, 118 + i * 20, 4005, 4007, 2 + i); AddHtml(47, 120 + i * 20, 150, 20, mob.Name.Color(LabelColor)); - AddHtml(195, 120 + i * 20, 80, 20, Html.Center($"{cd.Votes}", LabelColor)); + AddHtml(195, 120 + i * 20, 80, 20, cd.Votes.ToString().Center(LabelColor)); } } } @@ -200,4 +200,4 @@ public class ElectionManagementGump : Gump } } } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Engines/Factions/Gumps/FactionImbueGump.cs b/Projects/UOContent/Engines/Factions/Gumps/FactionImbueGump.cs index 776b65f26..eec829a08 100644 --- a/Projects/UOContent/Engines/Factions/Gumps/FactionImbueGump.cs +++ b/Projects/UOContent/Engines/Factions/Gumps/FactionImbueGump.cs @@ -96,7 +96,7 @@ public class FactionImbueGump : FactionGump if (m_Tool?.Deleted == false && m_Tool.UsesRemaining > 0) { - CraftItem.ShowCraftMenu(m_Mobile, m_CraftSystem, m_Tool, m_Notice); + m_Mobile.SendGump(new CraftGump(m_Mobile, m_CraftSystem, m_Tool, m_Notice)); } else if (m_Notice != null) { diff --git a/Projects/UOContent/Engines/Factions/Gumps/SheriffGump.cs b/Projects/UOContent/Engines/Factions/Gumps/SheriffGump.cs index 112b6dc4b..f5a466259 100644 --- a/Projects/UOContent/Engines/Factions/Gumps/SheriffGump.cs +++ b/Projects/UOContent/Engines/Factions/Gumps/SheriffGump.cs @@ -118,7 +118,7 @@ public class SheriffGump : FactionGump private void CenterItem(int itemID, int x, int y, int w, int h) { - var rc = ItemBounds.Bounds[itemID]; + var rc = ItemBounds.Table[itemID]; AddItem(x + (w - rc.Width) / 2 - rc.X, y + (h - rc.Height) / 2 - rc.Y, itemID); } diff --git a/Projects/UOContent/Engines/Factions/Items/BaseMonolith.cs b/Projects/UOContent/Engines/Factions/Items/BaseMonolith.cs index 031bf9d79..465798370 100644 --- a/Projects/UOContent/Engines/Factions/Items/BaseMonolith.cs +++ b/Projects/UOContent/Engines/Factions/Items/BaseMonolith.cs @@ -32,14 +32,8 @@ public abstract class BaseMonolith : BaseSystemController return; } - var oldSigil = m_Sigil; m_Sigil = value; - if (oldSigil != null) - { - oldSigil.Light = LightType.Empty; - } - if (m_Sigil?.LastMonolith != null && m_Sigil.LastMonolith != this && m_Sigil.LastMonolith.Sigil == m_Sigil) { m_Sigil.LastMonolith.Sigil = null; @@ -97,7 +91,6 @@ public abstract class BaseMonolith : BaseSystemController return; } - m_Sigil.Light = LightType.Circle225; m_Sigil.MoveToWorld(new Point3D(X, Y, Z + 18), Map); } diff --git a/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrap.cs b/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrap.cs index 932489008..17b77d69c 100644 --- a/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrap.cs +++ b/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrap.cs @@ -252,7 +252,11 @@ public abstract class BaseFactionTrap : BaseTrap public override void OnDelete() { - Faction?.Traps.Remove(this); + if (Faction?.Traps.Contains(this) == true) + { + Faction.Traps.Remove(this); + } + base.OnDelete(); } @@ -282,4 +286,4 @@ public abstract class BaseFactionTrap : BaseTrap return faction != Faction; } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs b/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs index 6a5afe534..2104d2eb3 100644 --- a/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs +++ b/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs @@ -10,6 +10,7 @@ public abstract class BaseFactionTrapDeed : Item, ICraftable public BaseFactionTrapDeed(int itemID = 0x14F0) : base(itemID) { + Weight = 1.0; LootType = LootType.Blessed; } @@ -17,8 +18,6 @@ public abstract class BaseFactionTrapDeed : Item, ICraftable { } - public override double DefaultWeight => 1.0; - public abstract Type TrapType { get; } [CommandProperty(AccessLevel.GameMaster)] @@ -118,4 +117,4 @@ public abstract class BaseFactionTrapDeed : Item, ICraftable m_Faction = Faction.ReadReference(reader); } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs index bed521a5c..acb63a3c6 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs @@ -137,39 +137,17 @@ namespace Server.Factions { Direction = GetDirectionTo(m); - switch (Utility.Random(6)) + var warning = Utility.Random(6) switch { - case 0: - { - Say($"I warn you, {m.Name}, you would do well to leave this area before someone shows you the world of gray."); - break; - } - case 1: - { - Say($"It would be wise to leave this area, {m.Name}, lest your head become my commanders' trophy."); - break; - } - case 2: - { - Say($"You are bold, {m.Name}, for one of the meager {Faction.Find(m)?.Definition.FriendlyName ?? "civilians"}. Leave now, lest you be taught the taste of dirt."); - break; - } - case 3: - { - Say($"Your presence here is an insult, {m.Name}. Be gone now, knave."); - break; - } - case 4: - { - Say($"Dost thou wish to be hung by your toes, {m.Name}? Nay? Then come no closer."); - break; - } - default: // 5 - { - Say($"Hey, {m.Name}. Yeah, you. Get out of here before I beat you with a stick."); - break; - } - } + 0 => $"I warn you, {m.Name}, you would do well to leave this area before someone shows you the world of gray.", + 1 => $"It would be wise to leave this area, {m.Name}, lest your head become my commanders' trophy.", + 2 => $"You are bold, {m.Name}, for one of the meager {Faction.Find(m)?.Definition.FriendlyName ?? "civilians"}. Leave now, lest you be taught the taste of dirt.", + 3 => $"Your presence here is an insult, {m.Name}. Be gone now, knave.", + 4 => $"Dost thou wish to be hung by your toes, {m.Name}? Nay? Then come no closer.", + _ => $"Hey, {m.Name}. Yeah, you. Get out of here before I beat you with a stick." // 5 + }; + + Say(warning); } } diff --git a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs index 32e8566cb..804ddbef7 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs @@ -1,5 +1,5 @@ using System; -using Server.Collections; +using System.Collections.Generic; using Server.Factions.AI; using Server.Items; using Server.Mobiles; @@ -234,26 +234,26 @@ namespace Server.Factions public Mobile FindDispelTarget(bool activeOnly) { - if (Mobile.Deleted || Mobile.Int < 95 || CanDispel(Mobile) || Mobile.AutoDispel) + if (m_Mobile.Deleted || m_Mobile.Int < 95 || CanDispel(m_Mobile) || m_Mobile.AutoDispel) { return null; } if (activeOnly) { - var aggressed = Mobile.Aggressed; - var aggressors = Mobile.Aggressors; + var aggressed = m_Mobile.Aggressed; + var aggressors = m_Mobile.Aggressors; Mobile active = null; var activePrio = 0.0; - var comb = Mobile.Combatant; + var comb = m_Mobile.Combatant; - if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && Mobile.InRange(comb, 12) && + if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && m_Mobile.InRange(comb, 12) && CanDispel(comb)) { active = comb; - activePrio = Mobile.GetDistanceToSqrt(comb); + activePrio = m_Mobile.GetDistanceToSqrt(comb); if (activePrio <= 2) { @@ -266,9 +266,9 @@ namespace Server.Factions var info = aggressed[i]; var m = info.Defender; - if (m != comb && m.Combatant == Mobile && Mobile.InRange(m, 12) && CanDispel(m)) + if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, 12) && CanDispel(m)) { - var prio = Mobile.GetDistanceToSqrt(m); + var prio = m_Mobile.GetDistanceToSqrt(m); if (active == null || prio < activePrio) { @@ -288,9 +288,9 @@ namespace Server.Factions var info = aggressors[i]; var m = info.Attacker; - if (m != comb && m.Combatant == Mobile && Mobile.InRange(m, 12) && CanDispel(m)) + if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, 12) && CanDispel(m)) { - var prio = Mobile.GetDistanceToSqrt(m); + var prio = m_Mobile.GetDistanceToSqrt(m); if (active == null || prio < activePrio) { @@ -308,26 +308,26 @@ namespace Server.Factions return active; } - var map = Mobile.Map; + var map = m_Mobile.Map; if (map != null) { Mobile active = null, inactive = null; double actPrio = 0.0, inactPrio = 0.0; - var comb = Mobile.Combatant; + var comb = m_Mobile.Combatant; if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && CanDispel(comb)) { active = inactive = comb; - actPrio = inactPrio = Mobile.GetDistanceToSqrt(comb); + actPrio = inactPrio = m_Mobile.GetDistanceToSqrt(comb); } - foreach (var m in Mobile.GetMobilesInRange(12)) + foreach (var m in m_Mobile.GetMobilesInRange(12)) { - if (m != Mobile && CanDispel(m)) + if (m != m_Mobile && CanDispel(m)) { - var prio = Mobile.GetDistanceToSqrt(m); + var prio = m_Mobile.GetDistanceToSqrt(m); if (inactive == null || prio < inactPrio) { @@ -335,7 +335,7 @@ namespace Server.Factions inactPrio = prio; } - if ((Mobile.Combatant == m || m.Combatant == Mobile) && (active == null || prio < actPrio)) + if ((m_Mobile.Combatant == m || m.Combatant == m_Mobile) && (active == null || prio < actPrio)) { active = m; actPrio = prio; @@ -350,7 +350,7 @@ namespace Server.Factions } public bool CanDispel(Mobile m) => - m is BaseCreature creature && creature.Summoned && Mobile.CanBeHarmful(creature, false) && + m is BaseCreature creature && creature.Summoned && m_Mobile.CanBeHarmful(creature, false) && !creature.IsAnimatedDead; public void RunTo(Mobile m) @@ -364,14 +364,14 @@ namespace Server.Factions } else {*/ - if (!Mobile.InRange(m, Mobile.RangeFight)) + if (!m_Mobile.InRange(m, m_Mobile.RangeFight)) { if (!MoveTo(m, true, 1)) { OnFailedMove(); } } - else if (Mobile.InRange(m, Mobile.RangeFight - 1)) + else if (m_Mobile.InRange(m, m_Mobile.RangeFight - 1)) { RunFrom(m); } @@ -381,7 +381,7 @@ namespace Server.Factions public void RunFrom(Mobile m) { - Run((Mobile.GetDirectionTo(m) - 4) & Direction.Mask); + Run((m_Mobile.GetDirectionTo(m) - 4) & Direction.Mask); } public void OnFailedMove() @@ -393,33 +393,36 @@ namespace Server.Factions new TeleportSpell( m_Mobile, null ).Cast(); - DebugSay( "I am stuck, I'm going to try teleporting away" ); + m_Mobile.DebugSay( "I am stuck, I'm going to try teleporting away" ); } else*/ - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - this.DebugSayFormatted($"My move is blocked, so I am going to attack {Mobile.FocusMob.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"My move is blocked, so I am going to attack {m_Mobile.FocusMob.Name}"); + } - Mobile.Combatant = Mobile.FocusMob; + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } - else + else if (m_Mobile.Debug) { - DebugSay("I am stuck"); + m_Mobile.DebugSay("I am stuck"); } } public void Run(Direction d) { - if (Mobile.Spell?.IsCasting == true || Mobile.Paralyzed || Mobile.Frozen || - Mobile.DisallowAllMoves) + if (m_Mobile.Spell?.IsCasting == true || m_Mobile.Paralyzed || m_Mobile.Frozen || + m_Mobile.DisallowAllMoves) { return; } - Mobile.Direction = d | Direction.Running; + m_Mobile.Direction = d | Direction.Running; - if (!DoMove(Mobile.Direction, true)) + if (!DoMove(m_Mobile.Direction, true)) { OnFailedMove(); } @@ -427,7 +430,7 @@ namespace Server.Factions public override bool Think() { - if (Mobile.Deleted) + if (m_Mobile.Deleted) { return false; } @@ -435,32 +438,32 @@ namespace Server.Factions var combatant = m_Guard.Combatant; if (combatant?.Deleted != false || !combatant.Alive || combatant.IsDeadBondedPet || - !Mobile.CanSee(combatant) || !Mobile.CanBeHarmful(combatant, false) || combatant.Map != Mobile.Map) + !m_Mobile.CanSee(combatant) || !m_Mobile.CanBeHarmful(combatant, false) || combatant.Map != m_Mobile.Map) { // Our combatant is deleted, dead, hidden, or we cannot hurt them // Try to find another combatant - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - Mobile.Combatant = combatant = Mobile.FocusMob; - Mobile.FocusMob = null; + m_Mobile.Combatant = combatant = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; } else { - Mobile.Combatant = combatant = null; + m_Mobile.Combatant = combatant = null; } } - if (combatant != null && (!Mobile.InLOS(combatant) || !Mobile.InRange(combatant, 12))) + if (combatant != null && (!m_Mobile.InLOS(combatant) || !m_Mobile.InRange(combatant, 12))) { - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - Mobile.Combatant = combatant = Mobile.FocusMob; - Mobile.FocusMob = null; + m_Mobile.Combatant = combatant = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; } - else if (!Mobile.InRange(combatant, 36)) + else if (!m_Mobile.InRange(combatant, 36)) { - Mobile.Combatant = combatant = null; + m_Mobile.Combatant = combatant = null; } } @@ -541,7 +544,7 @@ namespace Server.Factions Action = ActionType.Combat; } - Mobile.SetCurrentSpeedToActive(); + m_Mobile.SetCurrentSpeedToActive(); m_Guard.Warmode = true; RunTo(toFollow); @@ -553,7 +556,7 @@ namespace Server.Factions Action = ActionType.Wander; } - Mobile.SetCurrentSpeedToPassive(); + m_Mobile.SetCurrentSpeedToPassive(); m_Guard.Warmode = false; WalkRandomInHome(2, 2, 1); @@ -579,9 +582,9 @@ namespace Server.Factions } } - var spell = Mobile.Spell as Spell; + var spell = m_Mobile.Spell as Spell; - if (spell == null && Core.TickCount - Mobile.NextSpellTime >= 0) + if (spell == null && Core.TickCount - m_Mobile.NextSpellTime >= 0) { var toRelease = DateTime.MinValue; @@ -692,42 +695,41 @@ namespace Server.Factions var dexMod = GetStatMod(m_Guard, StatType.Dex); var intMod = GetStatMod(m_Guard, StatType.Int); - using var spellTypes = PooledRefQueue.Create(); + var types = new List(); if (strMod <= 0) { - spellTypes.Enqueue(typeof(StrengthSpell)); + types.Add(typeof(StrengthSpell)); } if (dexMod <= 0 && IsAllowed(GuardAI.Melee)) { - spellTypes.Enqueue(typeof(AgilitySpell)); + types.Add(typeof(AgilitySpell)); } if (intMod <= 0 && IsAllowed(GuardAI.Magic)) { - spellTypes.Enqueue(typeof(CunningSpell)); + types.Add(typeof(CunningSpell)); } if (IsAllowed(GuardAI.Bless)) { - if (spellTypes.Count > 1) + if (types.Count > 1) { spell = new BlessSpell(m_Guard); } - else if (spellTypes.Count == 1) + else if (types.Count == 1) { - spell = spellTypes.Dequeue().CreateInstance(m_Guard, null); + spell = types[0].CreateInstance(m_Guard, null); } } - else if (spellTypes.Count > 0) + else if (types.Count > 0) { - var spellType = spellTypes.Dequeue(); - if (spellType == typeof(StrengthSpell)) + if (types[0] == typeof(StrengthSpell)) { UseItemByType(typeof(BaseStrengthPotion)); } - else if (spellType == typeof(AgilitySpell)) + else if (types[0] == typeof(AgilitySpell)) { UseItemByType(typeof(BaseAgilityPotion)); } @@ -747,30 +749,30 @@ namespace Server.Factions var dexMod = GetStatMod(combatant, StatType.Dex); var intMod = GetStatMod(combatant, StatType.Int); - using var spellTypes = PooledRefQueue.Create(); + var types = new List(); if (strMod >= 0) { - spellTypes.Enqueue(typeof(WeakenSpell)); + types.Add(typeof(WeakenSpell)); } if (dexMod >= 0 && IsAllowed(GuardAI.Melee)) { - spellTypes.Enqueue(typeof(ClumsySpell)); + types.Add(typeof(ClumsySpell)); } if (intMod >= 0 && IsAllowed(GuardAI.Magic)) { - spellTypes.Enqueue(typeof(FeeblemindSpell)); + types.Add(typeof(FeeblemindSpell)); } - if (spellTypes.Count > 1) + if (types.Count > 1) { spell = new CurseSpell(m_Guard); } - else if (spellTypes.Count == 1) + else if (types.Count == 1) { - spell = spellTypes.Dequeue().CreateInstance(m_Guard, null); + spell = types[0].CreateInstance(m_Guard, null); } } } @@ -778,7 +780,7 @@ namespace Server.Factions if (spell != null && m_Guard.HitsMax - m_Guard.Hits + 10 > Utility.Random(100)) { - var type = spell switch + Type type = spell switch { GreaterHealSpell _ => typeof(BaseHealPotion), CureSpell _ => typeof(BaseCurePotion), diff --git a/Projects/UOContent/Engines/FeatureFlags/ContentFeatureFlags.cs b/Projects/UOContent/Engines/FeatureFlags/ContentFeatureFlags.cs deleted file mode 100644 index 5b960d76d..000000000 --- a/Projects/UOContent/Engines/FeatureFlags/ContentFeatureFlags.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Server.Systems.FeatureFlags; - -/// -/// Static boolean flags for UOContent hot paths. -/// Values are synced by FeatureFlagManager when flags change. -/// -public static class ContentFeatureFlags -{ - public static bool VendorPurchase { get; set; } = true; - public static bool VendorSell { get; set; } = true; - public static bool PlayerVendors { get; set; } = true; - public static bool HousePlacement { get; set; } = true; - public static bool BoatPlacement { get; set; } = true; - public static bool BulkOrders { get; set; } = true; - public static bool PassiveDetectHidden { get; set; } = true; - public static bool YoungPlayerSystem { get; set; } = true; - public static bool BitmapPathfindingCache { get; set; } = true; -} diff --git a/Projects/UOContent/Engines/FeatureFlags/FeatureFlagAdminGump.cs b/Projects/UOContent/Engines/FeatureFlags/FeatureFlagAdminGump.cs deleted file mode 100644 index b90c235ed..000000000 --- a/Projects/UOContent/Engines/FeatureFlags/FeatureFlagAdminGump.cs +++ /dev/null @@ -1,464 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Server.Collections; -using Server.Gumps; -using Server.Network; - -namespace Server.Systems.FeatureFlags; - -public sealed class FeatureFlagAdminGump : DynamicGump -{ - public enum FeatureFlagPage - { - Flags, - GumpBlocks, - ItemBlocks, - SkillBlocks, - SpellBlocks - } - - private FeatureFlagPage _currentPage; - private int _pageIndex; - private int _displayedCount; - private readonly FeatureFlag[] _displayedFlags = new FeatureFlag[FlagsPerPage]; - private readonly FeatureFlagBlockEntry[] _displayedBlocks = new FeatureFlagBlockEntry[BlocksPerPage]; - private const int FlagsPerPage = 10; - private const int BlocksPerPage = 7; - private const int FlagRowHeight = 25; - private const int BlockRowHeight = 45; - - public FeatureFlagAdminGump(FeatureFlagPage page = FeatureFlagPage.Flags, int pageIndex = 0) : base(50, 50) - { - _currentPage = page; - _pageIndex = pageIndex; - } - - private void Resend(Mobile from, FeatureFlagPage page, int pageIndex = 0) - { - _currentPage = page; - _pageIndex = pageIndex; - from.SendGump(this); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - // Background - builder.AddBackground(0, 0, 820, 500, 9270); - - // Title - builder.AddHtml(0, 15, 820, 25, "Feature Flag Administration".Center("#00FF00")); - - // Tab buttons - var flagsColor = _currentPage == FeatureFlagPage.Flags ? GumpTextColors.Yellow : GumpTextColors.White; - var gumpsColor = _currentPage == FeatureFlagPage.GumpBlocks ? GumpTextColors.Yellow : GumpTextColors.White; - var itemsColor = _currentPage == FeatureFlagPage.ItemBlocks ? GumpTextColors.Yellow : GumpTextColors.White; - var skillsColor = _currentPage == FeatureFlagPage.SkillBlocks ? GumpTextColors.Yellow : GumpTextColors.White; - var spellsColor = _currentPage == FeatureFlagPage.SpellBlocks ? GumpTextColors.Yellow : GumpTextColors.White; - - builder.AddButton(20, 45, 4005, 4007, 1); - builder.AddHtml(55, 47, 80, 20, "Flags".Color(flagsColor)); - - builder.AddButton(140, 45, 4005, 4007, 2); - builder.AddHtml(175, 47, 100, 20, "Gumps".Color(gumpsColor)); - - builder.AddButton(270, 45, 4005, 4007, 3); - builder.AddHtml(305, 47, 80, 20, "Items".Color(itemsColor)); - - builder.AddButton(390, 45, 4005, 4007, 4); - builder.AddHtml(425, 47, 80, 20, "Skills".Color(skillsColor)); - - builder.AddButton(510, 45, 4005, 4007, 5); - builder.AddHtml(545, 47, 80, 20, "Spells".Color(spellsColor)); - - // Content area - builder.AddAlphaRegion(15, 75, 790, 380); - - if (_currentPage == FeatureFlagPage.Flags) - { - BuildFlagsPage(ref builder); - } - else if (_currentPage == FeatureFlagPage.GumpBlocks) - { - BuildBlockPage( - ref builder, - "Gump Type", - FeatureFlagManager.GetAllGumpBlocks(), - "Use [BlockGump to add new blocks" - ); - } - else if (_currentPage == FeatureFlagPage.ItemBlocks) - { - BuildItemBlockPage( - ref builder, - FeatureFlagManager.GetAllItemBlocks(), - "Use [BlockItem to add new blocks" - ); - } - else if (_currentPage == FeatureFlagPage.SkillBlocks) - { - BuildBlockPage( - ref builder, - "Skill", - FeatureFlagManager.GetAllSkillBlocks(), - "Use [BlockSkill to add new blocks" - ); - } - else if (_currentPage == FeatureFlagPage.SpellBlocks) - { - BuildBlockPage( - ref builder, - "Spell Type", - FeatureFlagManager.GetAllSpellBlocks(), - "Use [BlockSpell to add new blocks" - ); - } - - // Close button - builder.AddButton(770, 460, 4017, 4019, 0); - builder.AddHtml(720, 462, 50, 20, "Close".Color(GumpTextColors.White)); - - // Save button - builder.AddButton(20, 460, 4023, 4025, 100); - builder.AddHtml(55, 462, 50, 20, "Save".Color(GumpTextColors.White)); - - // Refresh button - builder.AddButton(120, 460, 4014, 4016, 101); - builder.AddHtml(155, 462, 60, 20, "Refresh".Color(GumpTextColors.White)); - } - - private void BuildFlagsPage(ref DynamicGumpBuilder builder) - { - builder.AddHtml(20, 80, 150, 20, "Flag".Color(GumpTextColors.White)); - builder.AddHtml(180, 80, 150, 20, "Category".Color(GumpTextColors.White)); - builder.AddHtml(275, 80, 350, 20, "Description".Color(GumpTextColors.White)); - builder.AddHtml(690, 80, 60, 20, "Status".Color(GumpTextColors.White)); - - var flags = new List(FeatureFlagManager.GetAllFlags()); - flags.Sort((a, b) => - { - var cmp = a.Category.InsensitiveCompare(b.Category); - return cmp != 0 ? cmp : a.Key.InsensitiveCompare(b.Key); - }); - - var startIndex = _pageIndex * FlagsPerPage; - var endIndex = Math.Min(startIndex + FlagsPerPage, flags.Count); - var totalPages = Math.Max(1, (int)Math.Ceiling(flags.Count / (double)FlagsPerPage)); - - _displayedCount = 0; - var y = 105; - for (var i = startIndex; i < endIndex; i++) - { - var flag = flags[i]; - _displayedFlags[_displayedCount] = flag; - var statusColor = flag.Enabled ? GumpTextColors.Green : GumpTextColors.Red; - - builder.AddButton(20, y, flag.Enabled ? 2154 : 2151, flag.Enabled ? 2151 : 2154, 1000 + _displayedCount); - builder.AddHtml(60, y + 3, 130, 20, flag.Key.Color(GumpTextColors.White)); - builder.AddHtml(180, y + 3, 150, 20, (flag.Category ?? "").Color(GumpTextColors.LightGray)); - builder.AddHtml(275, y + 3, 350, 20, (flag.Description ?? "").Color(GumpTextColors.LightGray)); - builder.AddHtml(690, y + 3, 60, 20, (flag.Enabled ? "ON" : "OFF").Color(statusColor)); - - _displayedCount++; - y += FlagRowHeight; - } - - AddPagination(ref builder, totalPages); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void BuildBlockPage( - ref DynamicGumpBuilder builder, - string headerLabel, - IReadOnlyCollection blocks, - string helpText - ) - { - using var list = PooledRefList.Create(); - foreach (var b in blocks) - { - if (b != null) - { - list.Add(b); - } - } - BuildBlockPage(ref builder, headerLabel, list.AsSpan(), helpText); - } - - private void BuildBlockPage( - ref DynamicGumpBuilder builder, - string headerLabel, - ReadOnlySpan blocks, - string helpText - ) - { - builder.AddHtml(20, 80, 180, 20, headerLabel.Color(GumpTextColors.White)); - builder.AddHtml(200, 80, 400, 20, "Reason".Color(GumpTextColors.White)); - builder.AddHtml(690, 80, 60, 20, "Status".Color(GumpTextColors.White)); - builder.AddHtml(750, 80, 60, 20, "Remove".Color(GumpTextColors.White)); - - var startIndex = _pageIndex * BlocksPerPage; - var count = 0; - - _displayedCount = 0; - var skipped = 0; - var y = 105; - - for (var i = 0; i < blocks.Length; i++) - { - var block = blocks[i]; - if (block == null) - { - continue; - } - - count++; - - if (skipped < startIndex) - { - skipped++; - continue; - } - - if (_displayedCount < BlocksPerPage) - { - _displayedBlocks[_displayedCount] = block; - var statusColor = block.Active ? GumpTextColors.Red : GumpTextColors.Green; - - builder.AddButton(20, y, block.Active ? 2151 : 2154, block.Active ? 2154 : 2151, 2000 + _displayedCount); - builder.AddHtml(60, y + 3, 150, 20, block.DisplayName.Color(GumpTextColors.White)); - builder.AddHtml(200, y + 3, 400, 40, (block.Reason ?? "(default)").Color(GumpTextColors.LightGray)); - builder.AddHtml(690, y + 3, 60, 20, (block.Active ? "OFF" : "ON").Color(statusColor)); - builder.AddButton(750, y + 3, 4017, 4019, 3000 + _displayedCount); - - _displayedCount++; - y += BlockRowHeight; - } - } - - var totalPages = Math.Max(1, (int)Math.Ceiling(count / (double)BlocksPerPage)); - AddPagination(ref builder, totalPages); - builder.AddHtml(20, 430, 400, 20, helpText.Color(GumpTextColors.LightGray)); - } - - private void BuildItemBlockPage( - ref DynamicGumpBuilder builder, - IReadOnlyCollection blocks, - string helpText - ) - { - builder.AddHtml(20, 80, 150, 20, "Item Type".Color(GumpTextColors.White)); - builder.AddHtml(180, 80, 280, 20, "Reason".Color(GumpTextColors.White)); - builder.AddHtml(530, 80, 50, 20, "Use".Color(GumpTextColors.White)); - builder.AddHtml(580, 80, 50, 20, "Equip".Color(GumpTextColors.White)); - builder.AddHtml(640, 80, 50, 20, "Open".Color(GumpTextColors.White)); - builder.AddHtml(700, 80, 40, 20, "Edit".Color(GumpTextColors.White)); - builder.AddHtml(750, 80, 60, 20, "Remove".Color(GumpTextColors.White)); - - var startIndex = _pageIndex * BlocksPerPage; - - _displayedCount = 0; - var skipped = 0; - var y = 105; - - foreach (var block in blocks) - { - if (skipped < startIndex) - { - skipped++; - continue; - } - - if (_displayedCount >= BlocksPerPage) - { - break; - } - - _displayedBlocks[_displayedCount] = block; - - builder.AddButton(20, y, block.Active ? 2151 : 2154, block.Active ? 2154 : 2151, 2000 + _displayedCount); - builder.AddHtml(60, y + 3, 120, 20, block.DisplayName.Color(GumpTextColors.White)); - builder.AddHtml(180, y + 3, 280, 40, (block.Reason ?? "(default)").Color(GumpTextColors.LightGray)); - - var useColor = block.BlockUse ? GumpTextColors.Red : GumpTextColors.Green; - var equipColor = block.BlockEquip ? GumpTextColors.Red : GumpTextColors.Green; - var containerColor = block.BlockContainerAccess ? GumpTextColors.Red : GumpTextColors.Green; - - builder.AddHtml(530, y + 3, 50, 20, (block.BlockUse ? "X" : "-").Color(useColor)); - builder.AddHtml(580, y + 3, 50, 20, (block.BlockEquip ? "X" : "-").Color(equipColor)); - builder.AddHtml(640, y + 3, 50, 20, (block.BlockContainerAccess ? "X" : "-").Color(containerColor)); - - builder.AddButton(700, y + 3, 4011, 4013, 4000 + _displayedCount); - builder.AddButton(750, y + 3, 4017, 4019, 3000 + _displayedCount); - - _displayedCount++; - y += BlockRowHeight; - } - - var totalCount = blocks.Count; - var totalPages = Math.Max(1, (int)Math.Ceiling(totalCount / (double)BlocksPerPage)); - AddPagination(ref builder, totalPages); - builder.AddHtml(20, 430, 400, 20, helpText.Color(GumpTextColors.LightGray)); - } - - private void AddPagination(ref DynamicGumpBuilder builder, int totalPages) - { - if (totalPages <= 1) - { - return; - } - - if (_pageIndex > 0) - { - builder.AddButton(300, 425, 4014, 4016, 102); - builder.AddHtml(335, 425, 50, 20, "Prev".Color(GumpTextColors.White)); - } - - builder.AddHtml(370, 425, 60, 20, $"{_pageIndex + 1}/{totalPages}".Color(GumpTextColors.White)); - - if (_pageIndex < totalPages - 1) - { - builder.AddButton(420, 425, 4005, 4007, 103); - builder.AddHtml(455, 425, 50, 20, "Next".Color(GumpTextColors.White)); - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; - var buttonId = info.ButtonID; - - if (buttonId == 0) - { - return; - } - - if (buttonId is >= (int)(FeatureFlagPage.Flags + 1) and <= (int)(FeatureFlagPage.SpellBlocks + 1)) - { - Resend(from, (FeatureFlagPage)(buttonId - 1)); - return; - } - - if (buttonId == 100) - { - FeatureFlagManager.Save(); - from.SendMessage(0x35, "Feature flags saved to disk."); - from.SendGump(this); - return; - } - - if (buttonId == 101) - { - from.SendGump(this); - return; - } - - if (buttonId == 102) - { - Resend(from, _currentPage, _pageIndex - 1); - return; - } - - if (buttonId == 103) - { - Resend(from, _currentPage, _pageIndex + 1); - return; - } - - switch (buttonId) - { - // Toggle feature flags - case >= 1000 and < 1000 + FlagsPerPage: - { - var index = buttonId - 1000; - if (index < _displayedCount) - { - var flag = _displayedFlags[index]; - FeatureFlagManager.SetFlag(flag.Key, !flag.Enabled, from.Name); - } - break; - } - // Toggle block - case >= 2000 and < 2000 + BlocksPerPage: - { - var index = buttonId - 2000; - if (index < _displayedCount) - { - var block = _displayedBlocks[index]; - switch (_currentPage) - { - case FeatureFlagPage.GumpBlocks: - { - FeatureFlagManager.SetGumpBlockActive(block.ResolvedType, !block.Active, from.Name); - break; - } - case FeatureFlagPage.ItemBlocks: - { - FeatureFlagManager.SetItemBlockActive(block.ResolvedType, !block.Active, from.Name); - break; - } - case FeatureFlagPage.SkillBlocks: - { - FeatureFlagManager.SetSkillBlockActive(((SkillBlockEntry)block).Skill, !block.Active, from.Name); - break; - } - case FeatureFlagPage.SpellBlocks: - { - FeatureFlagManager.SetSpellBlockActive(((SpellBlockEntry)block).SpellId, !block.Active, from.Name); - break; - } - } - } - - break; - } - // Remove block - case >= 3000 and < 3000 + BlocksPerPage: - { - var index = buttonId - 3000; - if (index < _displayedCount) - { - var block = _displayedBlocks[index]; - switch (_currentPage) - { - case FeatureFlagPage.GumpBlocks: - { - FeatureFlagManager.UnblockGump(block.ResolvedType, from.Name); - break; - } - case FeatureFlagPage.ItemBlocks: - { - FeatureFlagManager.RemoveItemBlock(block.ResolvedType, from.Name); - break; - } - case FeatureFlagPage.SkillBlocks: - { - FeatureFlagManager.UnblockSkill(((SkillBlockEntry)block).Skill, from.Name); - break; - } - case FeatureFlagPage.SpellBlocks: - { - FeatureFlagManager.UnblockSpell(block.ResolvedType, from.Name); - break; - } - } - } - break; - } - // Edit item block (PropertiesGump) - case >= 4000 and < 4000 + BlocksPerPage: - { - var index = buttonId - 4000; - if (index < _displayedCount && _currentPage == FeatureFlagPage.ItemBlocks) - { - from.SendGump(new PropertiesGump(from, _displayedBlocks[index])); - } - break; - } - } - - from.SendGump(this); - } -} diff --git a/Projects/UOContent/Engines/FeatureFlags/FeatureFlagCommands.cs b/Projects/UOContent/Engines/FeatureFlags/FeatureFlagCommands.cs deleted file mode 100644 index af63881ec..000000000 --- a/Projects/UOContent/Engines/FeatureFlags/FeatureFlagCommands.cs +++ /dev/null @@ -1,602 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Gumps; -using Server.Targeting; - -namespace Server.Systems.FeatureFlags; - -public static class FeatureFlagCommands -{ - public static void Configure() - { - CommandSystem.Register("FeatureFlag", AccessLevel.Administrator, FeatureFlag_OnCommand); - CommandSystem.Register("FF", AccessLevel.Administrator, FeatureFlag_OnCommand); - CommandSystem.Register("BlockGump", AccessLevel.Administrator, BlockGump_OnCommand); - CommandSystem.Register("UnblockGump", AccessLevel.Administrator, UnblockGump_OnCommand); - CommandSystem.Register("BlockItemUse", AccessLevel.Administrator, BlockItemUse_OnCommand); - CommandSystem.Register("BlockItemEquip", AccessLevel.Administrator, BlockItemEquip_OnCommand); - CommandSystem.Register("BlockItemContainer", AccessLevel.Administrator, BlockItemContainer_OnCommand); - CommandSystem.Register("UnblockItemUse", AccessLevel.Administrator, UnblockItemUse_OnCommand); - CommandSystem.Register("UnblockItemEquip", AccessLevel.Administrator, UnblockItemEquip_OnCommand); - CommandSystem.Register("UnblockItemContainer", AccessLevel.Administrator, UnblockItemContainer_OnCommand); - CommandSystem.Register("BlockSkill", AccessLevel.Administrator, BlockSkill_OnCommand); - CommandSystem.Register("UnblockSkill", AccessLevel.Administrator, UnblockSkill_OnCommand); - CommandSystem.Register("BlockSpell", AccessLevel.Administrator, BlockSpell_OnCommand); - CommandSystem.Register("UnblockSpell", AccessLevel.Administrator, UnblockSpell_OnCommand); - CommandSystem.Register("FeatureList", AccessLevel.GameMaster, FeatureList_OnCommand); - CommandSystem.Register("FeatureAdmin", AccessLevel.Administrator, FeatureAdmin_OnCommand); - CommandSystem.Register("ListGumps", AccessLevel.GameMaster, ListGumps_OnCommand); - } - - [Usage("FeatureFlag [on|off|toggle|info|create|delete]")] - [Aliases("FF")] - [Description("Manage feature flags. Use without arguments to open admin gump.")] - private static void FeatureFlag_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - - if (e.Arguments.Length == 0) - { - from.SendGump(new FeatureFlagAdminGump()); - return; - } - - var flagKey = e.Arguments[0].ToLowerInvariant(); - var action = e.Arguments.Length > 1 ? e.Arguments[1].ToLowerInvariant() : "info"; - - if (action is "on" or "enable" or "true" or "1") - { - if (FeatureFlagManager.SetFlag(flagKey, true, from.Name)) - { - from.SendMessage(0x35, $"Feature flag '{flagKey}' has been ENABLED."); - } - else - { - from.SendMessage(0x22, $"Feature flag '{flagKey}' not found."); - } - } - else if (action is "off" or "disable" or "false" or "0") - { - if (FeatureFlagManager.SetFlag(flagKey, false, from.Name)) - { - from.SendMessage(0x35, $"Feature flag '{flagKey}' has been DISABLED."); - } - else - { - from.SendMessage(0x22, $"Feature flag '{flagKey}' not found."); - } - } - else if (action == "toggle") - { - var flag = FeatureFlagManager.GetFlag(flagKey); - if (flag != null) - { - FeatureFlagManager.SetFlag(flagKey, !flag.Enabled, from.Name); - from.SendMessage(0x35, $"Feature flag '{flagKey}' toggled to {(flag.Enabled ? "DISABLED" : "ENABLED")}."); - } - else - { - from.SendMessage(0x22, $"Feature flag '{flagKey}' not found."); - } - } - else if (action == "create") - { - if (e.Arguments.Length < 4) - { - from.SendMessage("Usage: [FeatureFlag create "); - return; - } - - var category = e.Arguments[2]; - var description = string.Join(" ", e.Arguments, 3, e.Arguments.Length - 3); - FeatureFlagManager.CreateOrUpdateFlag(flagKey, description, category, true, from.Name); - from.SendMessage(0x35, $"Feature flag '{flagKey}' created."); - } - else if (action is "delete" or "remove") - { - if (FeatureFlagManager.RemoveFlag(flagKey, from.Name)) - { - from.SendMessage(0x35, $"Feature flag '{flagKey}' removed."); - } - else - { - from.SendMessage(0x22, $"Feature flag '{flagKey}' not found."); - } - } - else - { - var infoFlag = FeatureFlagManager.GetFlag(flagKey); - if (infoFlag != null) - { - from.SendMessage(0x35, $"=== Feature Flag: {infoFlag.Key} ==="); - from.SendMessage($"Enabled: {(infoFlag.Enabled ? "Yes" : "No")}"); - from.SendMessage($"Default: {(infoFlag.DefaultEnabled ? "Yes" : "No")}"); - from.SendMessage($"Category: {infoFlag.Category}"); - from.SendMessage($"Description: {infoFlag.Description}"); - from.SendMessage($"Last Modified: {infoFlag.LastModified:G} by {infoFlag.LastModifiedBy}"); - } - else - { - from.SendMessage(0x22, $"Feature flag '{flagKey}' not found."); - from.SendMessage("Use [FeatureList to see all available flags."); - } - } - } - - [Usage("BlockGump [reason]")] - [Description("Block a gump type from being displayed to players.")] - private static void BlockGump_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - - if (e.Arguments.Length == 0) - { - from.SendMessage("Usage: [BlockGump [reason]"); - from.SendMessage("Example: [BlockGump CraftGump Crafting temporarily disabled"); - return; - } - - var typeName = e.Arguments[0]; - var reason = e.Arguments.Length > 1 - ? string.Join(" ", e.Arguments, 1, e.Arguments.Length - 1) - : null; - - if (FeatureFlagManager.BlockGumpByName(typeName, reason, from.Name)) - { - from.SendMessage(0x35, $"Gump '{typeName}' has been BLOCKED."); - if (reason != null) - { - from.SendMessage($"Reason: {reason}"); - } - } - else - { - from.SendMessage(0x22, $"Could not find gump type '{typeName}'."); - from.SendMessage("Make sure you're using the correct type name (e.g., CraftGump, HelpGump, etc.)"); - } - } - - [Usage("UnblockGump ")] - [Description("Remove a gump type block, allowing it to be displayed again.")] - private static void UnblockGump_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - - if (e.Arguments.Length == 0) - { - from.SendMessage("Usage: [UnblockGump "); - from.SendMessage("Use [FeatureList gumps to see blocked gumps."); - return; - } - - var typeName = e.Arguments[0]; - - if (FeatureFlagManager.UnblockGumpByName(typeName, from.Name)) - { - from.SendMessage(0x35, $"Gump block for '{typeName}' has been REMOVED."); - } - else - { - from.SendMessage(0x22, $"No block found for gump '{typeName}'."); - } - } - - [Usage("BlockItemUse [reason]")] - [Description("Block an item type from being used by players.")] - private static void BlockItemUse_OnCommand(CommandEventArgs e) => - HandleBlockItem(e, "Use", FeatureFlagManager.BlockItemUse, FeatureFlagManager.BlockItemUseByName); - - [Usage("BlockItemEquip [reason]")] - [Description("Block an item type from being equipped by players.")] - private static void BlockItemEquip_OnCommand(CommandEventArgs e) => - HandleBlockItem(e, "Equip", FeatureFlagManager.BlockItemEquip, FeatureFlagManager.BlockItemEquipByName); - - [Usage("BlockItemContainer [reason]")] - [Description("Block a container type from being opened by players.")] - private static void BlockItemContainer_OnCommand(CommandEventArgs e) => - HandleBlockItem(e, "Container", FeatureFlagManager.BlockItemContainer, FeatureFlagManager.BlockItemContainerByName); - - private static void HandleBlockItem( - CommandEventArgs e, string action, - Action blockByType, - Func blockByName) - { - var from = e.Mobile; - - if (e.Arguments.Length == 0) - { - from.SendMessage($"Usage: [BlockItem{action} [reason]"); - from.SendMessage($"Or target an item: [BlockItem{action} target [reason]"); - return; - } - - if (e.Arguments[0].Equals("target", StringComparison.OrdinalIgnoreCase)) - { - var reason = e.Arguments.Length > 1 - ? string.Join(" ", e.Arguments, 1, e.Arguments.Length - 1) - : null; - - from.SendMessage("Target the item to block:"); - from.Target = new BlockItemTarget(action, reason, blockByType); - return; - } - - var typeName = e.Arguments[0]; - var blockReason = e.Arguments.Length > 1 - ? string.Join(" ", e.Arguments, 1, e.Arguments.Length - 1) - : null; - - if (blockByName(typeName, blockReason, from.Name)) - { - from.SendMessage(0x35, $"Item '{typeName}' {action} has been BLOCKED."); - if (blockReason != null) - { - from.SendMessage($"Reason: {blockReason}"); - } - } - else - { - from.SendMessage(0x22, $"Could not find item type '{typeName}'."); - } - } - - private sealed class BlockItemTarget : Target - { - private readonly string _action; - private readonly string _reason; - private readonly Action _blockByType; - - public BlockItemTarget(string action, string reason, Action blockByType) - : base(-1, false, TargetFlags.None) - { - _action = action; - _reason = reason; - _blockByType = blockByType; - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is Item item) - { - var type = item.GetType(); - _blockByType(type, _reason, from.Name); - from.SendMessage(0x35, $"Item '{type.Name}' {_action} has been BLOCKED."); - if (_reason != null) - { - from.SendMessage($"Reason: {_reason}"); - } - } - else - { - from.SendMessage(0x22, "That is not an item."); - } - } - } - - [Usage("UnblockItemUse ")] - [Description("Remove the use block for an item type.")] - private static void UnblockItemUse_OnCommand(CommandEventArgs e) => - HandleUnblockItem(e, "Use", FeatureFlagManager.UnblockItemUseByName); - - [Usage("UnblockItemEquip ")] - [Description("Remove the equip block for an item type.")] - private static void UnblockItemEquip_OnCommand(CommandEventArgs e) => - HandleUnblockItem(e, "Equip", FeatureFlagManager.UnblockItemEquipByName); - - [Usage("UnblockItemContainer ")] - [Description("Remove the container access block for an item type.")] - private static void UnblockItemContainer_OnCommand(CommandEventArgs e) => - HandleUnblockItem(e, "Container", FeatureFlagManager.UnblockItemContainerByName); - - private static void HandleUnblockItem( - CommandEventArgs e, string action, Func unblockByName) - { - var from = e.Mobile; - - if (e.Arguments.Length == 0) - { - from.SendMessage($"Usage: [UnblockItem{action} "); - from.SendMessage("Use [FeatureList items to see blocked items."); - return; - } - - var typeName = e.Arguments[0]; - - if (unblockByName(typeName, from.Name)) - { - from.SendMessage(0x35, $"Item '{typeName}' {action} block has been REMOVED."); - } - else - { - from.SendMessage(0x22, $"No {action} block found for item '{typeName}'."); - } - } - - [Usage("BlockSkill [reason]")] - [Description("Block a skill from being used by players.")] - private static void BlockSkill_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - - if (e.Arguments.Length == 0) - { - from.SendMessage("Usage: [BlockSkill [reason]"); - from.SendMessage("Example: [BlockSkill Magery Investigating exploit"); - return; - } - - if (!Enum.TryParse(e.Arguments[0], true, out var skill)) - { - from.SendMessage(0x22, $"Unknown skill name '{e.Arguments[0]}'."); - from.SendMessage("Valid skills: Alchemy, Anatomy, Magery, Mining, etc."); - return; - } - - var reason = e.Arguments.Length > 1 - ? string.Join(" ", e.Arguments, 1, e.Arguments.Length - 1) - : null; - - FeatureFlagManager.BlockSkill(skill, reason, from.Name); - from.SendMessage(0x35, $"Skill '{skill}' has been BLOCKED."); - if (reason != null) - { - from.SendMessage($"Reason: {reason}"); - } - } - - [Usage("UnblockSkill ")] - [Description("Remove a skill block.")] - private static void UnblockSkill_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - - if (e.Arguments.Length == 0) - { - from.SendMessage("Usage: [UnblockSkill "); - from.SendMessage("Use [FeatureList skills to see blocked skills."); - return; - } - - if (!Enum.TryParse(e.Arguments[0], true, out var skill)) - { - from.SendMessage(0x22, $"Unknown skill name '{e.Arguments[0]}'."); - return; - } - - if (FeatureFlagManager.UnblockSkill(skill, from.Name)) - { - from.SendMessage(0x35, $"Skill block for '{skill}' has been REMOVED."); - } - else - { - from.SendMessage(0x22, $"No block found for skill '{skill}'."); - } - } - - [Usage("BlockSpell [reason]")] - [Description("Block a spell from being cast by players.")] - private static void BlockSpell_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - - if (e.Arguments.Length == 0) - { - from.SendMessage("Usage: [BlockSpell [reason]"); - from.SendMessage("Example: [BlockSpell RecallSpell Investigating exploit"); - return; - } - - var typeName = e.Arguments[0]; - var reason = e.Arguments.Length > 1 - ? string.Join(" ", e.Arguments, 1, e.Arguments.Length - 1) - : null; - - if (FeatureFlagManager.BlockSpellByName(typeName, reason, from.Name)) - { - from.SendMessage(0x35, $"Spell '{typeName}' has been BLOCKED."); - if (reason != null) - { - from.SendMessage($"Reason: {reason}"); - } - } - else - { - from.SendMessage(0x22, $"Could not find spell type '{typeName}'."); - from.SendMessage("Make sure you're using the correct type name (e.g., RecallSpell, GateTravelSpell, etc.)"); - } - } - - [Usage("UnblockSpell ")] - [Description("Remove a spell block.")] - private static void UnblockSpell_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - - if (e.Arguments.Length == 0) - { - from.SendMessage("Usage: [UnblockSpell "); - from.SendMessage("Use [FeatureList spells to see blocked spells."); - return; - } - - var typeName = e.Arguments[0]; - - if (FeatureFlagManager.UnblockSpellByName(typeName, from.Name)) - { - from.SendMessage(0x35, $"Spell block for '{typeName}' has been REMOVED."); - } - else - { - from.SendMessage(0x22, $"No block found for spell '{typeName}'."); - } - } - - [Usage("FeatureList [flags|gumps|items|skills|spells|all]")] - [Description("List all feature flags and blocks.")] - private static void FeatureList_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - var filter = e.Arguments.Length > 0 ? e.Arguments[0].ToLowerInvariant() : "all"; - - if (filter is "flags" or "all") - { - var flags = new List(FeatureFlagManager.GetAllFlags()); - flags.Sort((a, b) => - { - var cmp = string.Compare(a.Category, b.Category, StringComparison.OrdinalIgnoreCase); - return cmp != 0 ? cmp : string.Compare(a.Key, b.Key, StringComparison.OrdinalIgnoreCase); - }); - from.SendMessage(0x35, $"=== Feature Flags ({flags.Count}) ==="); - foreach (var flag in flags) - { - var status = flag.Enabled ? "[ON]" : "[OFF]"; - from.SendMessage($" {status} {flag.Key} ({flag.Category}): {flag.Description}"); - } - } - - if (filter is "gumps" or "all") - { - var gumpBlocks = FeatureFlagManager.GetAllGumpBlocks(); - from.SendMessage(0x35, $"=== Blocked Gumps ({gumpBlocks.Count}) ==="); - foreach (var block in gumpBlocks) - { - var status = block.Active ? "[OFF]" : "[ON]"; - from.SendMessage($" {status} {block.DisplayName}: {block.Reason ?? FeatureFlagSettings.DefaultGumpBlockedMessage}"); - } - } - - if (filter is "items" or "all") - { - var itemBlocks = FeatureFlagManager.GetAllItemBlocks(); - from.SendMessage(0x35, $"=== Blocked Items ({itemBlocks.Count}) ==="); - foreach (var block in itemBlocks) - { - var actions = new List(3); - if (block.BlockUse) actions.Add("Use"); - if (block.BlockEquip) actions.Add("Equip"); - if (block.BlockContainerAccess) actions.Add("Container"); - - var actionsStr = actions.Count > 0 ? $"[{string.Join("][", actions)}]" : "[none]"; - var status = block.Active ? "[OFF]" : "[ON]"; - from.SendMessage($" {status} {block.DisplayName} {actionsStr}: {block.Reason ?? FeatureFlagSettings.DefaultItemUseBlockedMessage}"); - } - } - - if (filter is "skills" or "all") - { - var skillBlocks = FeatureFlagManager.GetAllSkillBlocks(); - from.SendMessage(0x35, "=== Blocked Skills ==="); - for (var i = 0; i < skillBlocks.Length; i++) - { - var block = skillBlocks[i]; - if (block == null) - { - continue; - } - - var status = block.Active ? "[OFF]" : "[ON]"; - from.SendMessage($" {status} {block.DisplayName}: {block.Reason ?? FeatureFlagSettings.DefaultSkillDisabledMessage}"); - } - } - - if (filter is "spells" or "all") - { - var spellBlocks = FeatureFlagManager.GetAllSpellBlocks(); - from.SendMessage(0x35, "=== Blocked Spells ==="); - foreach (var block in spellBlocks) - { - if (block == null) - { - continue; - } - - var status = block.Active ? "[OFF]" : "[ON]"; - from.SendMessage($" {status} {block.DisplayName}: {block.Reason ?? FeatureFlagSettings.DefaultSpellDisabledMessage}"); - } - } - - if (filter != "flags" && filter != "gumps" && filter != "items" && filter != "skills" && filter != "spells" && filter != "all") - { - from.SendMessage("Usage: [FeatureList [flags|gumps|items|skills|spells|all]"); - } - } - - [Usage("FeatureAdmin")] - [Description("Open the feature flag administration gump.")] - private static void FeatureAdmin_OnCommand(CommandEventArgs e) - { - e.Mobile.SendGump(new FeatureFlagAdminGump()); - } - - [Usage("ListGumps")] - [Description("List all open gumps for yourself or a targeted player. Useful for finding gump names to block.")] - private static void ListGumps_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - - if (e.Arguments.Length > 0 && e.Arguments[0].Equals("self", StringComparison.OrdinalIgnoreCase)) - { - ListGumpsFor(from, from); - } - else - { - from.SendMessage("Target a player to list their open gumps (or use [ListGumps self):"); - from.Target = new ListGumpsTarget(); - } - } - - private static void ListGumpsFor(Mobile from, Mobile target) - { - if (target?.NetState == null) - { - from.SendMessage(0x22, "That player is not online."); - return; - } - - var gumps = target.GetGumps(); - var count = 0; - var gumpList = new List<(string shortName, string fullName)>(); - - foreach (var gump in gumps) - { - var type = gump.GetType(); - var fullName = type.FullName ?? type.Name; - var shortName = type.Name; - gumpList.Add((shortName, fullName)); - count++; - } - - if (count == 0) - { - from.SendMessage(0x35, $"{target.Name} has no gumps open."); - return; - } - - from.SendMessage(0x35, $"=== Open Gumps for {target.Name} ({count}) ==="); - foreach (var (shortName, fullName) in gumpList) - { - from.SendMessage($" • {shortName}"); - from.SendMessage($" Full: {fullName}"); - from.SendMessage(0x3B2, $" Block with: [BlockGump {shortName}"); - } - } - - private sealed class ListGumpsTarget : Target - { - public ListGumpsTarget() : base(-1, false, TargetFlags.None) - { - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is Mobile m) - { - ListGumpsFor(from, m); - } - else - { - from.SendMessage(0x22, "That is not a player."); - } - } - } -} diff --git a/Projects/UOContent/Engines/FeatureFlags/FeatureFlagManager.cs b/Projects/UOContent/Engines/FeatureFlags/FeatureFlagManager.cs deleted file mode 100644 index e599aeeb6..000000000 --- a/Projects/UOContent/Engines/FeatureFlags/FeatureFlagManager.cs +++ /dev/null @@ -1,1043 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Server.Gumps; -using Server.Json; -using Server.Logging; -using Server.Spells; - -namespace Server.Systems.FeatureFlags; - -public static class FeatureFlagManager -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(FeatureFlagManager)); - - // Primary storage - private static readonly Dictionary _flags = new(StringComparer.OrdinalIgnoreCase); - private static readonly Dictionary _gumpBlocks = new(); - private static readonly Dictionary _itemBlocks = new(); - private static readonly SpellBlockEntry[] _spellBlocks = new SpellBlockEntry[SpellRegistry.Types.Length]; - private static readonly SkillBlockEntry[] _skillBlocks = new SkillBlockEntry[58]; - - // Fast bailout flags - private static bool _hasActiveGumpBlocks; - private static bool _hasActiveItemBlocks; - private static bool _hasActiveSkillBlocks; - private static bool _hasActiveSpellBlocks; - - private static bool _initialized; - - public static void Initialize() - { - if (_initialized) - { - return; - } - - var savePath = FeatureFlagSettings.SavePath; - if (!Directory.Exists(savePath)) - { - Directory.CreateDirectory(savePath); - } - - // Load predefined flags from JSON, then overlay runtime state - LoadDefaultFlags(); - Load(); - - _initialized = true; - logger.Information( - "Feature Flag system initialized with {FlagCount} flags, {GumpBlockCount} gump blocks, {ItemBlockCount} item blocks, {SkillBlockCount} skill blocks, {SpellBlockCount} spell blocks", - _flags.Count, _gumpBlocks.Count, _itemBlocks.Count, CountActiveSkillBlocks(), CountActiveSpellBlocks()); - } - - private static int CountActiveSkillBlocks() - { - var count = 0; - for (var i = 0; i < _skillBlocks.Length; i++) - { - if (_skillBlocks[i] != null) - { - count++; - } - } - return count; - } - - private static int CountActiveSpellBlocks() - { - var count = 0; - for (var i = 0; i < _spellBlocks.Length; i++) - { - if (_spellBlocks[i] != null) - { - count++; - } - } - return count; - } - - public static bool IsEnabled(string flagKey) => _flags.TryGetValue(flagKey, out var flag) && flag.Enabled; - - public static FeatureFlag GetFlag(string flagKey) => _flags.GetValueOrDefault(flagKey); - - public static IReadOnlyCollection GetAllFlags() => _flags.Values; - - public static bool SetFlag(string flagKey, bool enabled, string modifiedBy = "System") - { - if (!_flags.TryGetValue(flagKey, out var flag)) - { - return false; - } - - var previousState = flag.Enabled; - flag.Enabled = enabled; - flag.LastModified = Core.Now; - flag.LastModifiedBy = modifiedBy; - - SyncStaticFlag(flagKey, enabled); - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Feature flag '{FlagKey}' changed from {Previous} to {Current} by {ModifiedBy}", - flagKey, previousState, enabled, modifiedBy); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Feature Flag] '{flagKey}' {(enabled ? "ENABLED" : "DISABLED")} by {modifiedBy}"); - } - - SaveFlags(); - return true; - } - - public static FeatureFlag CreateOrUpdateFlag(string key, string description, string category, bool defaultEnabled, string createdBy = "System") - { - var flag = new FeatureFlag - { - Key = key, - Description = description, - Category = category, - DefaultEnabled = defaultEnabled, - Enabled = defaultEnabled, - LastModified = Core.Now, - LastModifiedBy = createdBy - }; - - _flags[key] = flag; - - SyncStaticFlag(key, flag.Enabled); - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Feature flag '{FlagKey}' created/updated by {CreatedBy}", key, createdBy); - } - - SaveFlags(); - return flag; - } - - public static bool RemoveFlag(string flagKey, string removedBy = "System") - { - if (_flags.Remove(flagKey)) - { - SyncStaticFlag(flagKey, true); // Reset to default enabled - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Feature flag '{FlagKey}' removed by {RemovedBy}", flagKey, removedBy); - } - - SaveFlags(); - return true; - } - return false; - } - - public static bool IsGumpBlocked() where T : BaseGump => - _hasActiveGumpBlocks && _gumpBlocks.TryGetValue(typeof(T), out var entry) && entry.Active; - - public static bool IsGumpBlocked(Type gumpType) => - _hasActiveGumpBlocks && _gumpBlocks.TryGetValue(gumpType, out var entry) && entry.Active; - - public static GumpBlockEntry GetGumpBlockEntry(Type gumpType) => - _hasActiveGumpBlocks ? _gumpBlocks.GetValueOrDefault(gumpType) : null; - - public static IReadOnlyCollection GetAllGumpBlocks() => _gumpBlocks.Values; - - public static void BlockGump(string reason, string blockedBy = "System") where T : BaseGump => - BlockGump(typeof(T), reason, blockedBy); - - public static void BlockGump(Type gumpType, string reason, string blockedBy = "System") - { - var entry = new GumpBlockEntry - { - ResolvedType = gumpType, - Reason = reason, - Active = true, - CreatedAt = Core.Now, - CreatedBy = blockedBy - }; - - _gumpBlocks[gumpType] = entry; - UpdateGumpBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Warning("Gump '{GumpType}' BLOCKED by {BlockedBy}. Reason: {Reason}", gumpType.FullName, blockedBy, reason); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Gump Block] '{gumpType.Name}' BLOCKED by {blockedBy}. Reason: {reason}"); - } - - SaveGumpBlocks(); - } - - public static bool BlockGumpByName(string typeName, string reason, string blockedBy = "System") - { - var type = ResolveType(typeName); - if (type == null || !typeof(BaseGump).IsAssignableFrom(type)) - { - return false; - } - - BlockGump(type, reason, blockedBy); - return true; - } - - public static bool UnblockGump(string unblockedBy = "System") where T : BaseGump => - UnblockGump(typeof(T), unblockedBy); - - public static bool UnblockGump(Type gumpType, string unblockedBy = "System") - { - if (!_gumpBlocks.Remove(gumpType)) - { - return false; - } - - UpdateGumpBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Gump '{GumpType}' UNBLOCKED by {UnblockedBy}", gumpType.FullName, unblockedBy); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Gump Block] '{gumpType.Name}' UNBLOCKED by {unblockedBy}"); - } - - SaveGumpBlocks(); - return true; - } - - public static bool UnblockGumpByName(string typeName, string unblockedBy = "System") - { - var type = ResolveType(typeName); - return type != null && UnblockGump(type, unblockedBy); - } - - public static bool SetGumpBlockActive(string typeName, bool active, string modifiedBy = "System") - { - var type = ResolveType(typeName); - return type != null && SetGumpBlockActive(type, active, modifiedBy); - } - - public static bool SetGumpBlockActive(Type gumpType, bool active, string modifiedBy = "System") - { - if (!_gumpBlocks.TryGetValue(gumpType, out var entry)) - { - return false; - } - - entry.Active = active; - UpdateGumpBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Gump block '{GumpType}' set to {Active} by {ModifiedBy}", gumpType.FullName, active ? "ACTIVE" : "INACTIVE", modifiedBy); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Gump Block] '{gumpType.Name}' set to {(active ? "ACTIVE" : "INACTIVE")} by {modifiedBy}"); - } - - SaveGumpBlocks(); - return true; - } - - public static bool IsItemUseBlocked(Type itemType, out string reason) - { - if (_hasActiveItemBlocks && _itemBlocks.TryGetValue(itemType, out var entry) && entry.Active && - entry.BlockUse) - { - reason = entry.Reason ?? FeatureFlagSettings.DefaultItemUseBlockedMessage; - return true; - } - - reason = null; - return false; - } - - public static bool IsItemEquipBlocked(Type itemType, out string reason) - { - if (_hasActiveItemBlocks && _itemBlocks.TryGetValue(itemType, out var entry) && entry.Active && - entry.BlockEquip) - { - reason = entry.Reason ?? FeatureFlagSettings.DefaultItemEquipBlockedMessage; - return true; - } - - reason = null; - return false; - } - - public static bool IsContainerAccessBlocked(Type containerType, out string reason) - { - if (_hasActiveItemBlocks && _itemBlocks.TryGetValue(containerType, out var entry) && entry.Active && - entry.BlockContainerAccess) - { - reason = entry.Reason ?? FeatureFlagSettings.DefaultContainerBlockedMessage; - return true; - } - - reason = null; - return false; - } - - public static ItemBlockEntry GetItemBlockEntry(Type itemType) => - _hasActiveItemBlocks ? _itemBlocks.GetValueOrDefault(itemType) : null; - - public static IReadOnlyCollection GetAllItemBlocks() => _itemBlocks.Values; - - public static void BlockItemUse(Type itemType, string reason, string blockedBy = "System") => - SetItemBlockFlag(itemType, "Use", reason, blockedBy, (e, v) => e.BlockUse = v); - - public static void BlockItemEquip(Type itemType, string reason, string blockedBy = "System") => - SetItemBlockFlag(itemType, "Equip", reason, blockedBy, (e, v) => e.BlockEquip = v); - - public static void BlockItemContainer(Type itemType, string reason, string blockedBy = "System") => - SetItemBlockFlag(itemType, "Container", reason, blockedBy, (e, v) => e.BlockContainerAccess = v); - - public static bool BlockItemUseByName(string typeName, string reason, string blockedBy = "System") => - ResolveItemType(typeName, out var type) && Apply(() => BlockItemUse(type, reason, blockedBy)); - - public static bool BlockItemEquipByName(string typeName, string reason, string blockedBy = "System") => - ResolveItemType(typeName, out var type) && Apply(() => BlockItemEquip(type, reason, blockedBy)); - - public static bool BlockItemContainerByName(string typeName, string reason, string blockedBy = "System") => - ResolveItemType(typeName, out var type) && Apply(() => BlockItemContainer(type, reason, blockedBy)); - - public static bool UnblockItemUse(Type itemType, string unblockedBy = "System") => - ClearItemBlockFlag(itemType, "Use", unblockedBy, (e, v) => e.BlockUse = v, e => e.BlockUse); - - public static bool UnblockItemEquip(Type itemType, string unblockedBy = "System") => - ClearItemBlockFlag(itemType, "Equip", unblockedBy, (e, v) => e.BlockEquip = v, e => e.BlockEquip); - - public static bool UnblockItemContainer(Type itemType, string unblockedBy = "System") => - ClearItemBlockFlag(itemType, "Container", unblockedBy, (e, v) => e.BlockContainerAccess = v, e => e.BlockContainerAccess); - - public static bool UnblockItemUseByName(string typeName, string unblockedBy = "System") => - ResolveItemType(typeName, out var type) && UnblockItemUse(type, unblockedBy); - - public static bool UnblockItemEquipByName(string typeName, string unblockedBy = "System") => - ResolveItemType(typeName, out var type) && UnblockItemEquip(type, unblockedBy); - - public static bool UnblockItemContainerByName(string typeName, string unblockedBy = "System") => - ResolveItemType(typeName, out var type) && UnblockItemContainer(type, unblockedBy); - - private static bool ResolveItemType(string typeName, out Type type) - { - type = ResolveType(typeName); - return type != null && typeof(Item).IsAssignableFrom(type); - } - - private static bool Apply(Action action) - { - action(); - return true; - } - - private static void SetItemBlockFlag( - Type itemType, string action, string reason, string blockedBy, - Action setter) - { - if (!_itemBlocks.TryGetValue(itemType, out var entry)) - { - entry = new ItemBlockEntry - { - ResolvedType = itemType, - Active = true, - CreatedAt = Core.Now, - CreatedBy = blockedBy - }; - _itemBlocks[itemType] = entry; - } - - setter(entry, true); - - if (reason != null) - { - entry.Reason = reason; - } - - UpdateItemBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Warning("Item '{ItemType}' {Action} BLOCKED by {BlockedBy}. Reason: {Reason}", itemType.FullName, action, blockedBy, reason); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Item Block] '{itemType.Name}' {action} BLOCKED by {blockedBy}. Reason: {reason}"); - } - - SaveItemBlocks(); - } - - private static bool ClearItemBlockFlag( - Type itemType, string action, string unblockedBy, - Action setter, Func getter) - { - if (!_itemBlocks.TryGetValue(itemType, out var entry) || !getter(entry)) - { - return false; - } - - setter(entry, false); - - // Remove entry entirely if no flags remain - if (!entry.BlockUse && !entry.BlockEquip && !entry.BlockContainerAccess) - { - _itemBlocks.Remove(itemType); - } - - UpdateItemBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Item '{ItemType}' {Action} UNBLOCKED by {UnblockedBy}", itemType.FullName, action, unblockedBy); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Item Block] '{itemType.Name}' {action} UNBLOCKED by {unblockedBy}"); - } - - SaveItemBlocks(); - return true; - } - - public static bool RemoveItemBlock(Type itemType, string removedBy = "System") - { - if (!_itemBlocks.Remove(itemType)) - { - return false; - } - - UpdateItemBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Item '{ItemType}' block REMOVED by {RemovedBy}", itemType.FullName, removedBy); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Item Block] '{itemType.Name}' REMOVED by {removedBy}"); - } - - SaveItemBlocks(); - return true; - } - - public static bool SetItemBlockActive(Type itemType, bool active, string modifiedBy = "System") - { - if (!_itemBlocks.TryGetValue(itemType, out var entry)) - { - return false; - } - - entry.Active = active; - UpdateItemBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Item block '{ItemType}' set to {Active} by {ModifiedBy}", itemType.FullName, active ? "ACTIVE" : "INACTIVE", modifiedBy); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Item Block] '{itemType.Name}' set to {(active ? "ACTIVE" : "INACTIVE")} by {modifiedBy}"); - } - - SaveItemBlocks(); - return true; - } - - public static bool IsSkillBlocked(SkillName skill, out string reason) - { - var index = (int)skill; - if (!_hasActiveSkillBlocks || index < 0 || index >= _skillBlocks.Length - || _skillBlocks[index] is not { Active: true } entry) - { - reason = null; - return false; - } - - reason = entry.Reason ?? FeatureFlagSettings.DefaultSkillDisabledMessage; - return entry is { Active: true }; - } - - public static SkillBlockEntry GetSkillBlockEntry(SkillName skill) - { - var index = (int)skill; - return index >= 0 && index < _skillBlocks.Length ? _skillBlocks[index] : null; - } - - // NOTE: Will contain nulls! - public static ReadOnlySpan GetAllSkillBlocks() => _skillBlocks; - - public static void BlockSkill(SkillName skill, string reason, string blockedBy = "System") - { - var index = (int)skill; - if (index < 0 || index >= _skillBlocks.Length) - { - return; - } - - var entry = new SkillBlockEntry - { - Skill = skill, - Reason = reason, - Active = true, - CreatedAt = Core.Now, - CreatedBy = blockedBy - }; - - _skillBlocks[index] = entry; - UpdateSkillBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Warning("Skill '{Skill}' BLOCKED by {BlockedBy}. Reason: {Reason}", skill, blockedBy, reason); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Skill Block] '{skill}' BLOCKED by {blockedBy}. Reason: {reason}"); - } - - SaveSkillBlocks(); - } - - public static bool UnblockSkill(SkillName skill, string unblockedBy = "System") - { - var index = (int)skill; - if (index < 0 || index >= _skillBlocks.Length) - { - return false; - } - - if (_skillBlocks[index] != null) - { - _skillBlocks[index] = null; - UpdateSkillBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Skill '{Skill}' UNBLOCKED by {UnblockedBy}", skill, unblockedBy); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Skill Block] '{skill}' UNBLOCKED by {unblockedBy}"); - } - - SaveSkillBlocks(); - return true; - } - - return false; - } - - public static bool SetSkillBlockActive(SkillName skill, bool active, string modifiedBy = "System") - { - var index = (int)skill; - if (index < 0 || index >= _skillBlocks.Length) - { - return false; - } - - var entry = _skillBlocks[index]; - if (entry != null) - { - entry.Active = active; - UpdateSkillBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Skill block '{Skill}' set to {Active} by {ModifiedBy}", skill, active ? "ACTIVE" : "INACTIVE", modifiedBy); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Skill Block] '{skill}' set to {(active ? "ACTIVE" : "INACTIVE")} by {modifiedBy}"); - } - - SaveSkillBlocks(); - return true; - } - return false; - } - - public static bool IsSpellBlocked(int spellId, out string reason) - { - if (!_hasActiveSpellBlocks || spellId < 0 || spellId >= _spellBlocks.Length || - _spellBlocks[spellId] is not { Active: true } entry) - { - reason = null; - return false; - } - - reason = entry.Reason ?? FeatureFlagSettings.DefaultSpellDisabledMessage; - return true; - } - - public static bool IsSpellBlocked(Type spellType) - { - if (!_hasActiveSpellBlocks) - { - return false; - } - - var id = SpellRegistry.GetRegistryNumber(spellType); - return id >= 0 && id < _spellBlocks.Length && _spellBlocks[id] is { Active: true }; - } - - public static SpellBlockEntry GetSpellBlockEntry(int spellId) => - !_hasActiveSpellBlocks || spellId >= 0 && spellId >= _spellBlocks.Length ? null : _spellBlocks[spellId]; - - public static SpellBlockEntry GetSpellBlockEntry(Type spellType) - { - if (!_hasActiveSpellBlocks) - { - return null; - } - - var id = SpellRegistry.GetRegistryNumber(spellType); - return id >= 0 && id < _spellBlocks.Length ? _spellBlocks[id] : null; - } - - // NOTE: Will contain nulls! - public static ReadOnlySpan GetAllSpellBlocks() => _spellBlocks; - - public static void BlockSpell(Type spellType, string reason, string blockedBy = "System") - { - var spellId = SpellRegistry.GetRegistryNumber(spellType); - if (spellId < 0 || spellId >= _spellBlocks.Length) - { - return; - } - - var entry = new SpellBlockEntry - { - ResolvedType = spellType, - SpellId = spellId, - Reason = reason, - Active = true, - CreatedAt = Core.Now, - CreatedBy = blockedBy - }; - - _spellBlocks[spellId] = entry; - UpdateSpellBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Warning("Spell '{SpellType}' BLOCKED by {BlockedBy}. Reason: {Reason}", spellType.FullName, blockedBy, reason); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Spell Block] '{spellType.Name}' BLOCKED by {blockedBy}. Reason: {reason}"); - } - - SaveSpellBlocks(); - } - - public static bool BlockSpellByName(string typeName, string reason, string blockedBy = "System") - { - var type = ResolveType(typeName); - if (type != null) - { - BlockSpell(type, reason, blockedBy); - return true; - } - - return false; - } - - public static bool UnblockSpell(Type spellType, string unblockedBy = "System") - { - var spellId = SpellRegistry.GetRegistryNumber(spellType); - if (spellId < 0 || spellId >= _spellBlocks.Length) - { - return false; - } - - if (_spellBlocks[spellId] != null) - { - _spellBlocks[spellId] = null; - UpdateSpellBlocksFlag(); - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Spell '{SpellType}' UNBLOCKED by {UnblockedBy}", spellType.Name, unblockedBy); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Spell Block] '{spellType.Name}' UNBLOCKED by {unblockedBy}"); - } - - SaveSpellBlocks(); - return true; - } - return false; - } - - public static bool UnblockSpellByName(string typeName, string unblockedBy = "System") - { - var type = ResolveType(typeName); - return type != null && UnblockSpell(type, unblockedBy); - } - - public static bool SetSpellBlockActive(int spellId, bool active, string modifiedBy = "System") - { - if (spellId < 0 || spellId >= _spellBlocks.Length) - { - return false; - } - - var entry = _spellBlocks[spellId]; - if (entry != null) - { - entry.Active = active; - UpdateSpellBlocksFlag(); - - var typeName = entry.DisplayName; - - if (FeatureFlagSettings.LogChanges) - { - logger.Information("Spell block '{SpellType}' set to {Active} by {ModifiedBy}", typeName, active ? "ACTIVE" : "INACTIVE", modifiedBy); - } - - if (FeatureFlagSettings.BroadcastChangesToStaff) - { - World.BroadcastStaff($"[Spell Block] '{typeName}' set to {(active ? "ACTIVE" : "INACTIVE")} by {modifiedBy}"); - } - - SaveSpellBlocks(); - return true; - } - - return false; - } - - private static void LoadDefaultFlags() - { - var defaultFlagsPath = Path.Combine(Core.BaseDirectory, "Configuration", "FeatureFlags", "default-flags.json"); - var defaultFlags = JsonConfig.Deserialize>(defaultFlagsPath); - if (defaultFlags != null) - { - foreach (var flag in defaultFlags) - { - _flags.TryAdd(flag.Key, flag); - } - } - } - - public static void Save() - { - SaveFlags(); - SaveGumpBlocks(); - SaveItemBlocks(); - SaveSkillBlocks(); - SaveSpellBlocks(); - } - - private static void SaveFlags() - { - try - { - var flagsList = new List(_flags.Values); - JsonConfig.Serialize(Path.Combine(FeatureFlagSettings.SavePath, "flags.json"), flagsList); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to save flags"); - } - } - - private static void SaveGumpBlocks() - { - try - { - var list = new List(_gumpBlocks.Values); - JsonConfig.Serialize(Path.Combine(FeatureFlagSettings.SavePath, "gump-blocks.json"), list); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to save gump blocks"); - } - } - - private static void SaveItemBlocks() - { - try - { - var list = new List(_itemBlocks.Values); - JsonConfig.Serialize(Path.Combine(FeatureFlagSettings.SavePath, "item-blocks.json"), list); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to save item blocks"); - } - } - - private static void SaveSkillBlocks() - { - try - { - var list = new List(); - for (var i = 0; i < _skillBlocks.Length; i++) - { - if (_skillBlocks[i] != null) - { - list.Add(_skillBlocks[i]); - } - } - JsonConfig.Serialize(Path.Combine(FeatureFlagSettings.SavePath, "skill-blocks.json"), list); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to save skill blocks"); - } - } - - private static void SaveSpellBlocks() - { - try - { - var list = new List(); - for (var i = 0; i < _spellBlocks.Length; i++) - { - if (_spellBlocks[i] != null) - { - list.Add(_spellBlocks[i]); - } - } - JsonConfig.Serialize(Path.Combine(FeatureFlagSettings.SavePath, "spell-blocks.json"), list); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to save spell blocks"); - } - } - - public static void Load() - { - try - { - var savePath = FeatureFlagSettings.SavePath; - - // Load flags - var flags = JsonConfig.Deserialize>(Path.Combine(savePath, "flags.json")); - if (flags != null) - { - foreach (var flag in flags) - { - _flags[flag.Key] = flag; - } - } - - // Load gump blocks (TypeConverter resolves Type from JSON) - var gumpBlocks = JsonConfig.Deserialize>(Path.Combine(savePath, "gump-blocks.json")); - if (gumpBlocks != null) - { - for (var i = 0; i < gumpBlocks.Count; i++) - { - var entry = gumpBlocks[i]; - if (entry.ResolvedType != null) - { - _gumpBlocks[entry.ResolvedType] = entry; - } - } - } - - // Load item blocks (or migrate from old format) - var itemBlocksPath = Path.Combine(savePath, "item-blocks.json"); - var itemBlocks = JsonConfig.Deserialize>(itemBlocksPath); - if (itemBlocks != null) - { - for (var i = 0; i < itemBlocks.Count; i++) - { - var entry = itemBlocks[i]; - if (entry.ResolvedType != null) - { - _itemBlocks[entry.ResolvedType] = entry; - } - } - } - - // Load skill blocks (JsonStringEnumConverter deserializes SkillName) - var skillBlocks = JsonConfig.Deserialize>(Path.Combine(savePath, "skill-blocks.json")); - if (skillBlocks != null) - { - for (var i = 0; i < skillBlocks.Count; i++) - { - var entry = skillBlocks[i]; - var index = (int)entry.Skill; - if (index >= 0 && index < _skillBlocks.Length) - { - _skillBlocks[index] = entry; - } - } - } - - // Load spell blocks (TypeConverter resolves Type, then look up SpellId) - var spellBlocks = JsonConfig.Deserialize>(Path.Combine(savePath, "spell-blocks.json")); - if (spellBlocks != null) - { - for (var i = 0; i < spellBlocks.Count; i++) - { - var entry = spellBlocks[i]; - if (entry.ResolvedType == null) - { - continue; - } - - var spellId = SpellRegistry.GetRegistryNumber(entry.ResolvedType); - if (spellId < 0 || spellId >= _spellBlocks.Length) - { - logger.Warning( - "Spell type '{SpellType}' has no registered spell ID, skipping", - entry.ResolvedType.FullName - ); - continue; - } - - entry.SpellId = spellId; - _spellBlocks[spellId] = entry; - } - } - - // Update fast-bailout flags - UpdateGumpBlocksFlag(); - UpdateItemBlocksFlag(); - UpdateSkillBlocksFlag(); - UpdateSpellBlocksFlag(); - - // Sync static bool flags - SyncAllStaticFlags(); - - logger.Debug("Feature flags loaded successfully"); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to load feature flags"); - } - } - - private static Type ResolveType(string typeName) => - AssemblyHandler.FindTypeByFullName(typeName) ?? AssemblyHandler.FindTypeByName(typeName); - - private static void SyncStaticFlag(string key, bool enabled) - { - _ = key.ToLowerInvariant() switch - { - // Server project flags - "player_trading" => ServerFeatureFlags.PlayerTrading = enabled, - "pvp_combat" => ServerFeatureFlags.PvPCombat = enabled, - "bank_access" => ServerFeatureFlags.BankAccess = enabled, - "speedhack_detection" => ServerFeatureFlags.SpeedhackDetection = enabled, - "insurance" => ServerFeatureFlags.InsuranceEnabled = enabled, - - // UOContent flags - "vendor_purchase" => ContentFeatureFlags.VendorPurchase = enabled, - "vendor_sell" => ContentFeatureFlags.VendorSell = enabled, - "player_vendors" => ContentFeatureFlags.PlayerVendors = enabled, - "house_placement" => ContentFeatureFlags.HousePlacement = enabled, - "boat_placement" => ContentFeatureFlags.BoatPlacement = enabled, - "bulk_orders" => ContentFeatureFlags.BulkOrders = enabled, - "passive_detect_hidden" => ContentFeatureFlags.PassiveDetectHidden = enabled, - "young_player_system" => ContentFeatureFlags.YoungPlayerSystem = enabled, - "bitmap_pathfinding_cache" => ContentFeatureFlags.BitmapPathfindingCache = enabled, - }; - } - - private static void SyncAllStaticFlags() - { - foreach (var flag in _flags.Values) - { - SyncStaticFlag(flag.Key, flag.Enabled); - } - } - - private static void UpdateGumpBlocksFlag() - { - foreach (var entry in _gumpBlocks.Values) - { - if (entry.Active) - { - _hasActiveGumpBlocks = true; - return; - } - } - _hasActiveGumpBlocks = false; - } - - private static void UpdateItemBlocksFlag() - { - foreach (var entry in _itemBlocks.Values) - { - if (entry.Active) - { - _hasActiveItemBlocks = true; - return; - } - } - _hasActiveItemBlocks = false; - } - - private static void UpdateSkillBlocksFlag() - { - for (var i = 0; i < _skillBlocks.Length; i++) - { - if (_skillBlocks[i] is { Active: true }) - { - _hasActiveSkillBlocks = true; - return; - } - } - _hasActiveSkillBlocks = false; - } - - private static void UpdateSpellBlocksFlag() - { - for (var i = 0; i < _spellBlocks.Length; i++) - { - if (_spellBlocks[i] is { Active: true }) - { - _hasActiveSpellBlocks = true; - return; - } - } - _hasActiveSpellBlocks = false; - } -} diff --git a/Projects/UOContent/Engines/FeatureFlags/FeatureFlagSettings.cs b/Projects/UOContent/Engines/FeatureFlags/FeatureFlagSettings.cs deleted file mode 100644 index 6003eda42..000000000 --- a/Projects/UOContent/Engines/FeatureFlags/FeatureFlagSettings.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.IO; -using System.Text.Json.Serialization; - -namespace Server.Systems.FeatureFlags; - -public sealed class FeatureFlag -{ - public string Key { get; init; } - public string Description { get; init; } - public bool Enabled { get; set; } - public bool DefaultEnabled { get; init; } - public string Category { get; init; } - public DateTime LastModified { get; set; } - public string LastModifiedBy { get; set; } -} - -[PropertyObject] -public class FeatureFlagBlockEntry -{ - [CommandProperty(AccessLevel.Administrator)] - public Type ResolvedType { get; set; } - - [CommandProperty(AccessLevel.Administrator)] - public string Reason { get; set; } - - [CommandProperty(AccessLevel.Administrator)] - public bool Active { get; set; } - - [CommandProperty(AccessLevel.Administrator, readOnly: true)] - public DateTime CreatedAt { get; init; } - - [CommandProperty(AccessLevel.Administrator, readOnly: true)] - public string CreatedBy { get; init; } - - [JsonIgnore] - public virtual string DisplayName => ResolvedType?.Name; -} - -public sealed class GumpBlockEntry : FeatureFlagBlockEntry; - -public sealed class ItemBlockEntry : FeatureFlagBlockEntry -{ - [CommandProperty(AccessLevel.Administrator)] - public bool BlockUse { get; set; } - - [CommandProperty(AccessLevel.Administrator)] - public bool BlockEquip { get; set; } - - [CommandProperty(AccessLevel.Administrator)] - public bool BlockContainerAccess { get; set; } -} - -public sealed class SkillBlockEntry : FeatureFlagBlockEntry -{ - public SkillName Skill { get; set; } - - [JsonIgnore] - public override string DisplayName => Skill.ToString(); -} - -public sealed class SpellBlockEntry : FeatureFlagBlockEntry -{ - [JsonIgnore] - public int SpellId { get; set; } -} - -public static class FeatureFlagSettings -{ - public const string DefaultGumpBlockedMessage = "This feature is temporarily disabled."; - public const string DefaultItemUseBlockedMessage = "This item cannot be used at this time."; - public const string DefaultItemEquipBlockedMessage = "This item cannot be equipped at this time."; - public const string DefaultContainerBlockedMessage = "This container cannot be opened at this time."; - public const string DefaultSkillDisabledMessage = "This skill is temporarily disabled."; - public const string DefaultSpellDisabledMessage = "This spell is temporarily disabled."; - - public static AccessLevel RequiredAccessLevel { get; set; } = AccessLevel.Administrator; - public static bool LogChanges { get; set; } = true; - public static bool BroadcastChangesToStaff { get; set; } = true; - - public static string SavePath => Path.Combine(Core.BaseDirectory, "Configuration", "FeatureFlags"); -} diff --git a/Projects/UOContent/Engines/Harvest/Fishing.cs b/Projects/UOContent/Engines/Harvest/Fishing.cs index 6b3ef49ae..df46fe9fc 100644 --- a/Projects/UOContent/Engines/Harvest/Fishing.cs +++ b/Projects/UOContent/Engines/Harvest/Fishing.cs @@ -194,7 +194,7 @@ namespace Server.Engines.Harvest public override bool CheckResources(Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed) { - var pack = from.Backpack; + Container pack = from.Backpack; if (pack != null) { diff --git a/Projects/UOContent/Engines/Harvest/Lumberjacking.cs b/Projects/UOContent/Engines/Harvest/Lumberjacking.cs index 9640f68d8..b9275cda0 100644 --- a/Projects/UOContent/Engines/Harvest/Lumberjacking.cs +++ b/Projects/UOContent/Engines/Harvest/Lumberjacking.cs @@ -175,8 +175,6 @@ namespace Server.Engines.Harvest } } - public override object GetLock(Mobile from, Item tool, HarvestDefinition def, object toHarvest) => this; - public override void OnHarvestStarted(Mobile from, Item tool, HarvestDefinition def, object toHarvest) { base.OnHarvestStarted(from, tool, def, toHarvest); diff --git a/Projects/UOContent/Engines/Harvest/Mining.cs b/Projects/UOContent/Engines/Harvest/Mining.cs index dd7f5c747..e79de9fed 100644 --- a/Projects/UOContent/Engines/Harvest/Mining.cs +++ b/Projects/UOContent/Engines/Harvest/Mining.cs @@ -435,8 +435,6 @@ namespace Server.Engines.Harvest } } - public override object GetLock(Mobile from, Item tool, HarvestDefinition def, object toHarvest) => this; - public override bool BeginHarvesting(Mobile from, Item tool) { if (!base.BeginHarvesting(from, tool)) diff --git a/Projects/UOContent/Engines/Help/HelpGump.cs b/Projects/UOContent/Engines/Help/HelpGump.cs index 2c11641d9..d23552a35 100644 --- a/Projects/UOContent/Engines/Help/HelpGump.cs +++ b/Projects/UOContent/Engines/Help/HelpGump.cs @@ -307,7 +307,7 @@ public sealed class HelpGump : DynamicGump } else if (from is PlayerMobile mobile && mobile.CanUseStuckMenu() && mobile.Region.CanUseStuckMenu(mobile) && !CheckCombat(mobile) && !mobile.Frozen && - !mobile.Criminal && (Core.AOS || !mobile.Murderer)) + !mobile.Criminal && (Core.AOS || mobile.Kills < 5)) { var menu = new StuckMenu(mobile, mobile, true); diff --git a/Projects/UOContent/Engines/Help/HelpTopic.cs b/Projects/UOContent/Engines/Help/HelpTopic.cs index 1cbb0555e..2cd59b075 100644 --- a/Projects/UOContent/Engines/Help/HelpTopic.cs +++ b/Projects/UOContent/Engines/Help/HelpTopic.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: HelpTopic.cs * * * diff --git a/Projects/UOContent/Engines/Help/PageDiscord.cs b/Projects/UOContent/Engines/Help/PageDiscord.cs deleted file mode 100644 index d483cd1c9..000000000 --- a/Projects/UOContent/Engines/Help/PageDiscord.cs +++ /dev/null @@ -1,222 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Server.Logging; -using System.Collections.Generic; -using System.Net.Http.Json; - -namespace Server.Engines.Help; - -public static class PageDiscord -{ - private static HttpClient _httpClient; - private static readonly ILogger _logger = LogFactory.GetLogger(typeof(PageDiscord)); - private static string _webhookUrl; - - public static void Configure() - { - _webhookUrl = ServerConfiguration.GetOrUpdateSetting("pages.discordWebhookUrl", null); - - if (IsEnabled) - { - _logger.Information("Relaying pages to discord enabled."); - } - } - - public static bool IsEnabled => !string.IsNullOrEmpty(_webhookUrl); - - public static async ValueTask SendPageNotificationAsync(PageEntry entry) - { - if (!IsEnabled) - { - return; - } - - var pageTypeName = PageQueue.GetPageTypeName(entry.Type); - var locationInfo = $"{entry.PageLocation} ({entry.PageMap?.Name ?? "Unknown"})"; - var fieldsList = new List - { - new { name = "Player", value = entry.Sender?.Name ?? "Unknown", inline = true }, - new { name = "Account", value = entry.Sender?.Account?.Username ?? "Unknown", inline = true }, - new { name = "Page Type", value = pageTypeName, inline = true }, - new { name = "Location", value = locationInfo, inline = true }, - new { name = "Time Sent", value = FormatDiscordTimestamp(entry.Sent), inline = true }, - new { name = "Queue Position", value = $"#{PageQueue.List.Count}", inline = true }, - new { name = "Message", value = TruncateMessage(entry.Message), inline = false } - }; - - if (entry.SpeechLog != null && entry.SpeechLog.Count > 0) - { - fieldsList.Add(new - { - name = "Speech Log Available", - value = $"Contains {entry.SpeechLog.Count} speech entries", - inline = false - }); - } - - var embed = new - { - title = $"New {pageTypeName} Page", - color = GetPageTypeColor(entry.Type), - fields = fieldsList.ToArray(), - footer = new { text = "UO Page System" }, - timestamp = entry.Sent.ToString("o") - }; - - var payload = new - { - username = "UO Pages", - embeds = new[] { embed } - }; - - try - { - _httpClient ??= new HttpClient(); - await _httpClient.PostAsJsonAsync(_webhookUrl, payload); - - _logger.Debug("Discord page notification sent for {Player} - {PageType}", entry.Sender?.Name, pageTypeName); - } - catch (Exception ex) - { - _logger.Error(ex, "Discord page webhook failed for {Player} - {PageType}", - entry.Sender?.Name, entry.Type); - } - } - - public static async ValueTask SendPageHandlerUpdateAsync(PageEntry entry, Mobile oldHandler, Mobile newHandler) - { - if (!IsEnabled || oldHandler != null && newHandler == null && PageQueue.IndexOf(entry) == -1) - { - return; - } - - var pageTypeName = PageQueue.GetPageTypeName(entry.Type); - string actionText; - int color; - - if (oldHandler == null && newHandler != null) - { - actionText = $"🛠️ **{newHandler.Name}** is now handling the page"; - color = 0x00FF00; // green - } - else if (oldHandler != null && newHandler == null) - { - actionText = $"❌ **{oldHandler.Name}** stopped handling the page"; - color = 0xFF0000; // red - } - else - { - return; - } - - var embed = new - { - title = $"{pageTypeName} Page Update", - description = actionText, - color, - fields = new[] - { - new { name = "Player", value = entry.Sender?.Name ?? "Unknown", inline = true }, - new { name = "Page Type", value = pageTypeName, inline = true }, - new { name = "Queue Position", value = $"#{PageQueue.IndexOf(entry) + 1}", inline = true } - }, - footer = new { text = "UO Page System" }, - timestamp = Core.Now.ToString("o") - }; - - var payload = new - { - username = "UO Pages", - embeds = new[] { embed } - }; - - try - { - _httpClient ??= new HttpClient(); - await _httpClient.PostAsJsonAsync(_webhookUrl, payload); - - _logger.Debug("Discord page handler update sent for {Player} - {PageType}", entry.Sender?.Name, pageTypeName); - } - catch (Exception ex) - { - _logger.Error(ex, "Discord page handler update webhook failed for {Player} - {PageType}", - entry.Sender?.Name, entry.Type); - } - } - - public static async ValueTask SendPageCompletedAsync(PageEntry entry) - { - if (!IsEnabled) - { - return; - } - - var pageTypeName = PageQueue.GetPageTypeName(entry.Type); - var handlerName = entry.Handler?.Name ?? "Staff"; - var elapsed = Core.Now - entry.Sent; - var elapsedText = elapsed.TotalMinutes < 1 - ? "< 1 minute" - : $"{elapsed.TotalMinutes:F0} minutes"; - - var embed = new - { - title = $"✅ {pageTypeName} Page Completed", - color = 0x00AA00, // dark green - fields = new[] - { - new { name = "Player", value = entry.Sender?.Name ?? "Unknown", inline = true }, - new { name = "Handled By", value = handlerName, inline = true }, - new { name = "Response Time", value = elapsedText, inline = true } - }, - footer = new { text = "UO Page System" }, - timestamp = Core.Now.ToString("yyyy-MM-ddTHH:mm:ssZ") - }; - - var payload = new - { - username = "UO Pages", - embeds = new[] { embed } - }; - - try - { - _httpClient ??= new HttpClient(); - await _httpClient.PostAsJsonAsync(_webhookUrl, payload); - - _logger.Debug("Discord page completed notification sent for {Player} - {PageType}", - entry.Sender?.Name, pageTypeName); - } - catch (Exception ex) - { - _logger.Error(ex, "Discord page completed webhook failed for {Player} - {PageType}", - entry.Sender?.Name, entry.Type); - } - } - - private static int GetPageTypeColor(PageType type) => type switch - { - PageType.Bug => 0xFF0000, // red - PageType.Stuck => 0xFFFF00, // yellow - PageType.Account => 0x0000FF, // blue - PageType.Question => 0x00FFFF, // cyan - PageType.Suggestion => 0x00FF00, // green - PageType.VerbalHarassment => 0xFF00FF, // magenta - PageType.PhysicalHarassment => 0x800080, // purple - _ => 0x888888 // gray for other - }; - - private static string FormatDiscordTimestamp(DateTime utcTime, char style = 'f') => - $""; - - private static string TruncateMessage(string message) - { - if (string.IsNullOrEmpty(message)) - { - return "*(no message)*"; - } - - const int maxLength = 1000; // apparently the discord field limit is 1024 - return message.Length <= maxLength ? message : $"{message[..(maxLength - 3)]}..."; - } -} diff --git a/Projects/UOContent/Engines/Help/PagePromptGump.cs b/Projects/UOContent/Engines/Help/PagePromptGump.cs index a03612be4..7814ffedd 100644 --- a/Projects/UOContent/Engines/Help/PagePromptGump.cs +++ b/Projects/UOContent/Engines/Help/PagePromptGump.cs @@ -30,7 +30,7 @@ public sealed class PagePromptGump : StaticGump builder.AddTextEntry(120, 168, 400, 200, 1153, 0, ""); builder.AddButton(175, 355, 2074, 2075, 1); // Okay - builder.AddButton(405, 355, 2073, 2072, 0); // Cancel + builder. AddButton(405, 355, 2073, 2072, 0); // Cancel } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Engines/Help/PageQueue.cs b/Projects/UOContent/Engines/Help/PageQueue.cs index eda4bb00d..ea0c418cd 100644 --- a/Projects/UOContent/Engines/Help/PageQueue.cs +++ b/Projects/UOContent/Engines/Help/PageQueue.cs @@ -127,7 +127,6 @@ namespace Server.Engines.Help public static void Configure() { CommandSystem.Register("Pages", AccessLevel.Counselor, Pages_OnCommand); - PageDiscord.Configure(); } public static bool CheckAllowedToPage(Mobile from) @@ -175,8 +174,6 @@ namespace Server.Engines.Help m_KeyedByHandler[value] = entry; } - _ = PageDiscord.SendPageHandlerUpdateAsync(entry, old, value); - if (old == null || value == null) { return; @@ -217,11 +214,6 @@ namespace Server.Engines.Help e.Stop(); - if (e.Handler != null) - { - _ = PageDiscord.SendPageCompletedAsync(e); - } - List.Remove(e); m_KeyedBySender.Remove(e.Sender); @@ -269,7 +261,7 @@ namespace Server.Engines.Help if (!isStaffOnline) { entry.Sender.SendMessage( - "We are sorry, but no staff members are currently available to assist you. Your page will remain in the queue until one becomes available, or until you cancel it manually." + "We are sorry, but no staff members are currently available to assist you. Your page will remain in the queue until one becomes available, or until you cancel it manually." ); } @@ -278,9 +270,7 @@ namespace Server.Engines.Help Email.SendQueueEmail(entry, GetPageTypeName(entry.Type)); } - _ = PageDiscord.SendPageNotificationAsync(entry); - HelpEvents.InvokePageEnqueued(entry); } } -} \ No newline at end of file +} diff --git a/Projects/UOContent/Engines/Help/PageResponseGump.cs b/Projects/UOContent/Engines/Help/PageResponseGump.cs index ec00f5849..106eb4131 100644 --- a/Projects/UOContent/Engines/Help/PageResponseGump.cs +++ b/Projects/UOContent/Engines/Help/PageResponseGump.cs @@ -25,7 +25,7 @@ public sealed class PageResponseGump : StaticGump //
Ultima Online Help Response
builder.AddHtmlLocalized(150, 40, 360, 40, 1062610); - builder.AddHtml(80, 90, 480, 290, $"{_name} tells {_from.Name}: {_text}", background: true, scrollbar: true); + builder.AddHtml(80, 90, 480, 290, $"{_name} tells {_from.Name}: {_text}", true, true); // Clicking the OKAY button will remove the response you have received. builder.AddHtmlLocalized(80, 390, 480, 40, 1062611); diff --git a/Projects/UOContent/Engines/Help/SpeechLogGump.cs b/Projects/UOContent/Engines/Help/SpeechLogGump.cs index edd5b3fc4..54a6aa6f3 100644 --- a/Projects/UOContent/Engines/Help/SpeechLogGump.cs +++ b/Projects/UOContent/Engines/Help/SpeechLogGump.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using System.Text; using Server.Accounting; -using Server.Text; using Server.Gumps; using Server.Network; @@ -46,7 +46,7 @@ namespace Server.Engines.Help 10, 280, 20, - Html.Center($"SPEECH LOG - {playerName} ({playerAccount.FixHtmlFormattable()})", 0xA0A0FF) + $"SPEECH LOG - {playerName} ({playerAccount.FixHtmlFormattable()})".Center(0xA0A0FF) ); var lastPage = (log.Count - 1) / MaxEntriesPerPage; @@ -62,7 +62,7 @@ namespace Server.Engines.Help var max = log.Count - (lastPage - page) * MaxEntriesPerPage; var min = Math.Max(max - MaxEntriesPerPage, 0); - using var builder = ValueStringBuilder.Create(); + var builder = new StringBuilder(); for (var i = min; i < max; i++) { diff --git a/Projects/UOContent/Engines/Help/StuckMenu.cs b/Projects/UOContent/Engines/Help/StuckMenu.cs index a53d71db8..4fc57aed2 100644 --- a/Projects/UOContent/Engines/Help/StuckMenu.cs +++ b/Projects/UOContent/Engines/Help/StuckMenu.cs @@ -300,7 +300,7 @@ namespace Server.Menus.Questions } else { - destMap = m_Mobile.Murderer ? Map.Felucca : Map.Trammel; + destMap = m_Mobile.Kills >= 5 ? Map.Felucca : Map.Trammel; } BaseCreature.TeleportPets(m_Mobile, dest, destMap); diff --git a/Projects/UOContent/Engines/Insurance/Gumps/CancelRenewInventoryInsuranceGump.cs b/Projects/UOContent/Engines/Insurance/Gumps/CancelRenewInventoryInsuranceGump.cs deleted file mode 100644 index e5cad5e68..000000000 --- a/Projects/UOContent/Engines/Insurance/Gumps/CancelRenewInventoryInsuranceGump.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Server.Mobiles; -using Server.Network; - -namespace Server.Gumps; - -public class CancelRenewInventoryInsuranceGump : StaticGump -{ - private readonly ItemInsuranceMenuGump _insuranceGump; - - public override bool Singleton => true; - - public CancelRenewInventoryInsuranceGump(ItemInsuranceMenuGump insuranceGump) : base(250, 200) => - _insuranceGump = insuranceGump; - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddBackground(0, 0, 240, 142, 0x13BE); - builder.AddImageTiled(6, 6, 228, 100, 0xA40); - builder.AddImageTiled(6, 116, 228, 20, 0xA40); - builder.AddAlphaRegion(6, 6, 228, 142); - - // You are about to disable inventory insurance auto-renewal. - builder.AddHtmlLocalized(8, 8, 228, 100, 1071021, 0x7FFF); - - builder.AddButton(6, 116, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(40, 118, 450, 20, 1060051, 0x7FFF); // CANCEL - - builder.AddButton(114, 116, 0xFA5, 0xFA7, 1); - builder.AddHtmlLocalized(148, 118, 450, 20, 1071022, 0x7FFF); // DISABLE IT! - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (sender.Mobile is not PlayerMobile pm || !pm.CheckAlive()) - { - return; - } - - if (info.ButtonID == 1) - { - // You have cancelled automatically reinsuring all insured items upon death - pm.SendLocalizedMessage(1061075, "", 0x23); - pm.AutoRenewInsurance = false; - } - else - { - pm.SendLocalizedMessage(1042021); // Cancelled. - } - - if (_insuranceGump != null) - { - pm.SendGump(_insuranceGump); - } - } -} diff --git a/Projects/UOContent/Engines/Insurance/Gumps/ItemInsuranceMenuConfirmGump.cs b/Projects/UOContent/Engines/Insurance/Gumps/ItemInsuranceMenuConfirmGump.cs deleted file mode 100644 index 4fd597cf6..000000000 --- a/Projects/UOContent/Engines/Insurance/Gumps/ItemInsuranceMenuConfirmGump.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Server.Mobiles; -using Server.Network; - -namespace Server.Gumps; - -public class ItemInsuranceMenuConfirmGump : StaticGump -{ - private readonly ItemInsuranceMenuGump _insuranceGump; - - public ItemInsuranceMenuConfirmGump(ItemInsuranceMenuGump insuranceGump) : base(250, 200) => - _insuranceGump = insuranceGump; - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddBackground(0, 0, 240, 142, 0x13BE); - builder.AddImageTiled(6, 6, 228, 100, 0xA40); - builder.AddImageTiled(6, 116, 228, 20, 0xA40); - builder.AddAlphaRegion(6, 6, 228, 142); - - builder.AddHtmlLocalized(8, 8, 228, 100, 1114300, 0x7FFF); // Do you wish to insure all newly selected items? - - builder.AddButton(6, 116, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(40, 118, 450, 20, 1060051, 0x7FFF); // CANCEL - - builder.AddButton(114, 116, 0xFA5, 0xFA7, 1); - builder.AddHtmlLocalized(148, 118, 450, 20, 1073996, 0x7FFF); // ACCEPT - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (sender.Mobile is not PlayerMobile pm || !pm.CheckAlive()) - { - return; - } - - if (info.ButtonID == 1) - { - _insuranceGump.ToggleSelected(); - } - else - { - pm.SendLocalizedMessage(1042021); // Cancelled. - pm.SendGump(_insuranceGump); - } - } -} diff --git a/Projects/UOContent/Engines/Insurance/Gumps/ItemInsuranceMenuGump.cs b/Projects/UOContent/Engines/Insurance/Gumps/ItemInsuranceMenuGump.cs deleted file mode 100644 index 2d17f63ab..000000000 --- a/Projects/UOContent/Engines/Insurance/Gumps/ItemInsuranceMenuGump.cs +++ /dev/null @@ -1,207 +0,0 @@ -using Server.Engines.Insurance; -using Server.Mobiles; -using Server.Network; - -namespace Server.Gumps; - -public class ItemInsuranceMenuGump : DynamicGump -{ - private readonly PlayerMobile _from; - private readonly bool[] _insure; - private readonly Item[] _items; - private int _page; - - public override bool Singleton => true; - - public ItemInsuranceMenuGump(PlayerMobile from, Item[] items) : base(25, 50) - { - _from = from; - _items = items; - _insure = new bool[items.Length]; - - for (var i = 0; i < items.Length; ++i) - { - _insure[i] = items[i].Insured; - } - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 520, 510, 0x13BE); - builder.AddImageTiled(10, 10, 500, 30, 0xA40); - builder.AddImageTiled(10, 50, 500, 355, 0xA40); - builder.AddImageTiled(10, 415, 500, 80, 0xA40); - builder.AddAlphaRegion(10, 10, 500, 485); - - builder.AddButton(15, 470, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(50, 472, 80, 20, 1011012, 0x7FFF); // CANCEL - - if (_from.AutoRenewInsurance) - { - builder.AddButton(360, 10, 9723, 9724, 1); - } - else - { - builder.AddButton(360, 10, 9720, 9722, 1); - } - - builder.AddHtmlLocalized(395, 14, 105, 20, 1114122, 0x7FFF); // AUTO REINSURE - - builder.AddButton(395, 470, 0xFA5, 0xFA6, 2); - builder.AddHtmlLocalized(430, 472, 50, 20, 1006044, 0x7FFF); // OK - - builder.AddHtmlLocalized(10, 14, 150, 20, 1114121, 0x7FFF); //
ITEM INSURANCE MENU
- - builder.AddHtmlLocalized(45, 54, 70, 20, 1062214, 0x7FFF); // Item - builder.AddHtmlLocalized(250, 54, 70, 20, 1061038, 0x7FFF); // Cost - builder.AddHtmlLocalized(400, 54, 70, 20, 1114311, 0x7FFF); // Insured - - var balance = Banker.GetBalance(_from); - var cost = 0; - - for (var i = 0; i < _items.Length; ++i) - { - if (_insure[i]) - { - cost += Insurance.GetInsuranceCost(_from, _items[i]); - } - } - - builder.AddHtmlLocalized(15, 420, 300, 20, 1114310, 0x7FFF); // GOLD AVAILABLE: - builder.AddLabel(215, 420, 0x481, $"{balance}"); - builder.AddHtmlLocalized(15, 435, 300, 20, 1114123, 0x7FFF); // TOTAL COST OF INSURANCE: - builder.AddLabel(215, 435, 0x481, $"{cost}"); - - if (cost != 0) - { - builder.AddHtmlLocalized(15, 450, 300, 20, 1114125, 0x7FFF); // NUMBER OF DEATHS PAYABLE: - builder.AddLabel(215, 450, 0x481, $"{balance / cost}"); - } - - for (int i = _page * 4, y = 72; i < (_page + 1) * 4 && i < _items.Length; ++i, y += 75) - { - var item = _items[i]; - var b = ItemBounds.Bounds[item.ItemID]; - - builder.AddImageTiledButton( - 40, - y, - 0x918, - 0x918, - 0, - GumpButtonType.Page, - 0, - item.ItemID, - item.Hue, - 40 - b.Width / 2 - b.X, - 30 - b.Height / 2 - b.Y - ); - builder.AddItemProperty(item.Serial); - - if (_insure[i]) - { - builder.AddButton(400, y, 9723, 9724, 100 + i); - builder.AddLabel(250, y, 0x481, $"{Insurance.GetInsuranceCost(_from, item)}"); - } - else - { - builder.AddButton(400, y, 9720, 9722, 100 + i); - builder.AddLabel(250, y, 0x66C, $"{Insurance.GetInsuranceCost(_from, item)}"); - } - } - - if (_page >= 1) - { - builder.AddButton(15, 380, 0xFAE, 0xFAF, 3); - builder.AddHtmlLocalized(50, 380, 450, 20, 1044044, 0x7FFF); // PREV PAGE - } - - if ((_page + 1) * 4 < _items.Length) - { - builder.AddButton(400, 380, 0xFA5, 0xFA7, 4); - builder.AddHtmlLocalized(435, 380, 70, 20, 1044045, 0x7FFF); // NEXT PAGE - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID == 0 || !_from.CheckAlive()) - { - return; - } - - switch (info.ButtonID) - { - case 1: // Auto Reinsure - { - if (_from.AutoRenewInsurance) - { - _from.SendGump(new CancelRenewInventoryInsuranceGump(this)); - } - else - { - Insurance.AutoRenewInventoryInsurance(_from); - _from.SendGump(this); - } - - break; - } - case 2: // OK - { - _from.SendGump(new ItemInsuranceMenuConfirmGump(this)); - - break; - } - case 3: // Prev - { - if (_page >= 1) - { - _page--; - _from.SendGump(this); - } - - break; - } - case 4: // Next - { - if ((_page + 1) * 4 < _items.Length) - { - _page++; - _from.SendGump(this); - } - - break; - } - default: - { - var idx = info.ButtonID - 100; - - if (idx >= 0 && idx < _items.Length) - { - _insure[idx] = !_insure[idx]; - } - - _from.SendGump(this); - - break; - } - } - } - - public void ToggleSelected() - { - var items = _items; - var insure = _insure; - for (var i = 0; i < items.Length; ++i) - { - var item = items[i]; - - if (item.Insured != insure[i]) - { - Insurance.ToggleItemInsurance(_from, item, false); - } - } - } -} diff --git a/Projects/UOContent/Engines/Insurance/Insurance.cs b/Projects/UOContent/Engines/Insurance/Insurance.cs deleted file mode 100644 index 8ba3985e5..000000000 --- a/Projects/UOContent/Engines/Insurance/Insurance.cs +++ /dev/null @@ -1,323 +0,0 @@ -using System.Collections.Generic; -using Server.Collections; -using Server.Factions; -using Server.Gumps; -using Server.Items; -using Server.Mobiles; -using Server.Targeting; - -namespace Server.Engines.Insurance; - -public static class Insurance -{ - public static bool Enabled => ServerFeatureFlags.InsuranceEnabled; - private static readonly Dictionary _insuranceContexts = []; - - public static void Configure() - { - // Legacy, but not expected to be used anymore in favor of the feature flag system - ServerFeatureFlags.InsuranceEnabled = ServerConfiguration.GetSetting("insurance.enable", Core.AOS); - } - - public static int GetInsuranceCost(Mobile from, Item item) => 600; - - public static void CheckInsuranceBeforeDeath(Mobile from) - { - if (!Enabled) - { - return; - } - - var recentDamager = from.FindMostRecentDamager(false); - if (recentDamager == null) - { - return; - } - - if (recentDamager is BaseCreature creature) - { - recentDamager = creature.GetMaster(); - } - - if (recentDamager != from && recentDamager is PlayerMobile insuranceReward) - { - _insuranceContexts[from] = new InsuranceContext(insuranceReward); - } - } - - public static void CheckInsuranceOnDeath(Mobile from) - { - if (!Enabled || !_insuranceContexts.Remove(from, out var context)) - { - return; - } - - if (context.MissedAutoRenewal) - { - from.SendLocalizedMessage(1061115); // You do not have the gold to automatically reinsure all your items. - } - - if (context.InsuranceReward != null && context.InsuranceBonus > 0 && - Banker.Deposit(context.InsuranceReward, context.InsuranceBonus)) - { - // ~1_AMOUNT~ gold has been deposited into your bank box. - context.InsuranceReward.SendLocalizedMessage(1060397, $"{context.InsuranceBonus}"); - } - } - - public static bool CheckItemInsuranceOnDeath(PlayerMobile from, Item item) - { - if (!Enabled || !item.Insured) - { - return false; - } - - if (from.DuelContext?.Registered == true && from.DuelContext.Started && from.DuelPlayer?.Eliminated != true) - { - return true; - } - - if (!_insuranceContexts.TryGetValue(from, out var context)) - { - _insuranceContexts[from] = context = new InsuranceContext(); - } - - if (from.AutoRenewInsurance) - { - var cost = Insurance.GetInsuranceCost(from, item); - - if (context.InsuranceReward != null) - { - cost /= 2; - } - - if (Banker.Withdraw(from, cost)) - { - item.PaidInsurance = true; - // ~1_AMOUNT~ gold has been withdrawn from your bank box. - from.SendLocalizedMessage(1060398, $"{cost}"); - } - else - { - // TODO: Should this spam? - // from.SendLocalizedMessage(1061079, "", 0x23); // You lack the funds to purchase the insurance - item.PaidInsurance = false; - item.Insured = false; - context.MissedAutoRenewal = true; - } - } - else - { - item.PaidInsurance = false; - item.Insured = false; - } - - context.InsuranceBonus += 300; - return true; - } - - public static bool CanInsure(Mobile from, Item item) - { - if (!Enabled) - { - return false; - } - - if (item is Container && item is not BaseQuiver || item is BagOfSending or KeyRing or PotionKeg or Sigil) - { - return false; - } - - if (item.Stackable) - { - return false; - } - - if (item.LootType == LootType.Cursed) - { - return false; - } - - if (item.ItemID == 0x204E) // death shroud - { - return false; - } - - if (item.Layer == Layer.Mount) - { - return false; - } - - return item.LootType != LootType.Blessed && item.LootType != LootType.Newbied && item.BlessedFor != from; - } - - public static void ToggleItemInsurance(Mobile from) - { - if (!from.CheckAlive()) - { - return; - } - - from.BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance); - from.SendLocalizedMessage(1060868); // Target the item you wish to toggle insurance status on to cancel - } - - public static void ToggleItemInsurance(Mobile from, object obj) - { - if (!from.CheckAlive()) - { - return; - } - - ToggleItemInsurance(from, obj as Item, true); - } - - public static void ToggleItemInsurance(Mobile from, Item item, bool target) - { - if (item?.IsChildOf(from) != true) - { - if (target) - { - from.BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance); - } - - // You can only insure items that you have equipped or that are in your backpack - from.SendLocalizedMessage(1060871, "", 0x23); - } - else if (item.Insured) - { - item.Insured = false; - - from.SendLocalizedMessage(1060874, "", 0x35); // You cancel the insurance on the item - - if (target) - { - from.BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance); - // Target the item you wish to toggle insurance status on to cancel - from.SendLocalizedMessage(1060868, "", 0x23); - } - } - else if (!CanInsure(from, item)) - { - if (target) - { - from.BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance); - } - - from.SendLocalizedMessage(1060869, "", 0x23); // You cannot insure that - } - else - { - if (!item.PaidInsurance) - { - var cost = GetInsuranceCost(from, item); - - if (Banker.Withdraw(from, cost)) - { - // ~1_AMOUNT~ gold has been withdrawn from your bank box. - from.SendLocalizedMessage(1060398, $"{cost}"); - item.PaidInsurance = true; - } - else - { - from.SendLocalizedMessage(1061079, "", 0x23); // You lack the funds to purchase the insurance - return; - } - } - - item.Insured = true; - - from.SendLocalizedMessage(1060873, "", 0x23); // You have insured the item - - if (target) - { - from.BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance); - // Target the item you wish to toggle insurance status on to cancel - from.SendLocalizedMessage(1060868, "", 0x23); - } - } - } - - public static void AutoRenewInventoryInsurance(Mobile from) - { - if (!from.CheckAlive()) - { - return; - } - - // You have selected to automatically reinsure all insured items upon death - from.SendLocalizedMessage(1060881, "", 0x23); - (from as PlayerMobile)?.AutoRenewInsurance = true; - } - - public static void CancelRenewInventoryInsurance(Mobile from) - { - if (!from.CheckAlive()) - { - return; - } - - if (Core.SE) - { - from.SendGump(new CancelRenewInventoryInsuranceGump(null)); - } - else - { - // You have cancelled automatically reinsuring all insured items upon death - from.SendLocalizedMessage(1061075, "", 0x23); - (from as PlayerMobile)?.AutoRenewInsurance = false; - } - } - - public static void OpenItemInsuranceMenu(Mobile from) - { - if (!from.CheckAlive() || from.NetState == null) - { - return; - } - - using var queue = PooledRefQueue.Create(128); - - foreach (var item in from.Items) - { - if (DisplayInItemInsuranceGump(from, item)) - { - queue.Enqueue(item); - } - } - - var pack = from.Backpack; - - if (pack != null) - { - foreach (var item in pack.FindItems()) - { - if (DisplayInItemInsuranceGump(from, item)) - { - queue.Enqueue(item); - } - } - } - - if (queue.Count == 0) - { - // None of your current items meet the requirements for insurance. - from.SendLocalizedMessage(1114915, "", 0x35); - } - else if (from is PlayerMobile pm) - { - // TODO: Investigate item sorting - from.SendGump(new ItemInsuranceMenuGump(pm, queue.ToArray())); - } - } - - private static bool DisplayInItemInsuranceGump(Mobile from, Item item) => - (item.Visible || from.AccessLevel >= AccessLevel.GameMaster) && (item.Insured || CanInsure(from, item)); - - private class InsuranceContext(PlayerMobile insuranceReward = null) - { - public PlayerMobile InsuranceReward = insuranceReward; - public int InsuranceBonus { get; set; } - public bool MissedAutoRenewal { get; set; } - } -} diff --git a/Projects/UOContent/Engines/Khaldun/Books/LysanderNotebook.cs b/Projects/UOContent/Engines/Khaldun/Books/LysanderNotebook.cs index 98d8e4a3a..8bbb685d5 100644 --- a/Projects/UOContent/Engines/Khaldun/Books/LysanderNotebook.cs +++ b/Projects/UOContent/Engines/Khaldun/Books/LysanderNotebook.cs @@ -1,9 +1,6 @@ -using ModernUO.Serialization; - namespace Server.Items; -[SerializationGenerator(0)] -public partial class LysanderNotebook1 : BaseBook +public class LysanderNotebook1 : BaseBook { public static readonly BookContent Content = new( "Lysander's Notebook", @@ -38,11 +35,28 @@ public partial class LysanderNotebook1 : BaseBook { } + public LysanderNotebook1(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class LysanderNotebook2 : BaseBook +public class LysanderNotebook2 : BaseBook { public static readonly BookContent Content = new( "Lysander's Notebook", @@ -110,11 +124,28 @@ public partial class LysanderNotebook2 : BaseBook { } + public LysanderNotebook2(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class LysanderNotebook3 : BaseBook +public class LysanderNotebook3 : BaseBook { public static readonly BookContent Content = new( "Lysander's Notebook", @@ -176,11 +207,28 @@ public partial class LysanderNotebook3 : BaseBook { } + public LysanderNotebook3(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class LysanderNotebook7 : BaseBook +public class LysanderNotebook7 : BaseBook { public static readonly BookContent Content = new( "Lysander's Notebook", @@ -257,11 +305,28 @@ public partial class LysanderNotebook7 : BaseBook { } + public LysanderNotebook7(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class LysanderNotebook8 : BaseBook +public class LysanderNotebook8 : BaseBook { public static readonly BookContent Content = new( "Lysander's Notebook", @@ -329,11 +394,28 @@ public partial class LysanderNotebook8 : BaseBook { } + public LysanderNotebook8(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class LysanderNotebook11 : BaseBook +public class LysanderNotebook11 : BaseBook { public static readonly BookContent Content = new( "Lysander's Notebook", @@ -398,5 +480,23 @@ public partial class LysanderNotebook11 : BaseBook { } + public LysanderNotebook11(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; -} + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } +} \ No newline at end of file diff --git a/Projects/UOContent/Engines/Khaldun/Books/TavarasJournal.cs b/Projects/UOContent/Engines/Khaldun/Books/TavarasJournal.cs index 490dc95e2..8ae117fd0 100644 --- a/Projects/UOContent/Engines/Khaldun/Books/TavarasJournal.cs +++ b/Projects/UOContent/Engines/Khaldun/Books/TavarasJournal.cs @@ -1,9 +1,6 @@ -using ModernUO.Serialization; - namespace Server.Items; -[SerializationGenerator(0)] -public partial class TavarasJournal1 : BaseBook +public class TavarasJournal1 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -69,11 +66,28 @@ public partial class TavarasJournal1 : BaseBook { } + public TavarasJournal1(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal2 : BaseBook +public class TavarasJournal2 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -172,11 +186,28 @@ public partial class TavarasJournal2 : BaseBook { } + public TavarasJournal2(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal3 : BaseBook +public class TavarasJournal3 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -246,11 +277,28 @@ public partial class TavarasJournal3 : BaseBook { } + public TavarasJournal3(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal6 : BaseBook +public class TavarasJournal6 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -332,11 +380,28 @@ public partial class TavarasJournal6 : BaseBook { } + public TavarasJournal6(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal7 : BaseBook +public class TavarasJournal7 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -407,11 +472,28 @@ public partial class TavarasJournal7 : BaseBook { } + public TavarasJournal7(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal8 : BaseBook +public class TavarasJournal8 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -500,11 +582,28 @@ public partial class TavarasJournal8 : BaseBook { } + public TavarasJournal8(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal9 : BaseBook +public class TavarasJournal9 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -571,11 +670,28 @@ public partial class TavarasJournal9 : BaseBook { } + public TavarasJournal9(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal11 : BaseBook +public class TavarasJournal11 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -664,11 +780,28 @@ public partial class TavarasJournal11 : BaseBook { } + public TavarasJournal11(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal14 : BaseBook +public class TavarasJournal14 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -749,11 +882,28 @@ public partial class TavarasJournal14 : BaseBook { } + public TavarasJournal14(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal16 : BaseBook +public class TavarasJournal16 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -845,11 +995,28 @@ public partial class TavarasJournal16 : BaseBook { } + public TavarasJournal16(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal16b : BaseBook +public class TavarasJournal16b : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -931,11 +1098,28 @@ public partial class TavarasJournal16b : BaseBook { } + public TavarasJournal16b(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal17 : BaseBook +public class TavarasJournal17 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -981,11 +1165,28 @@ public partial class TavarasJournal17 : BaseBook { } + public TavarasJournal17(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class TavarasJournal19 : BaseBook +public class TavarasJournal19 : BaseBook { public static readonly BookContent Content = new( "Journal: Discovery of the Tomb", @@ -1021,5 +1222,23 @@ public partial class TavarasJournal19 : BaseBook { } + public TavarasJournal19(Serial serial) : base(serial) + { + } + public override BookContent DefaultContent => Content; -} + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } +} \ No newline at end of file diff --git a/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs b/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs index 53932c8f0..48a1edd9d 100644 --- a/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs +++ b/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using ModernUO.Serialization; -using Server.Collections; using Server.Gumps; using Server.Network; @@ -20,62 +18,67 @@ namespace Server.Items Yellow = 0x1870 } - [SerializationGenerator(1)] - public partial class PuzzleChestSolution + public class PuzzleChestSolution { - [SerializableField(0)] - private PuzzleChestCylinder[] _cylinders; - public const int Length = 5; - public PuzzleChestSolution() => - _cylinders = [RandomCylinder(), RandomCylinder(), RandomCylinder(), RandomCylinder(), RandomCylinder()]; - - public PuzzleChestSolution( - PuzzleChestCylinder first, PuzzleChestCylinder second, PuzzleChestCylinder third, - PuzzleChestCylinder fourth, PuzzleChestCylinder fifth - ) => _cylinders = [first, second, third, fourth, fifth]; - - public PuzzleChestSolution(PuzzleChestSolution solution) + public PuzzleChestSolution() { - _cylinders = new PuzzleChestCylinder[Length]; - solution.Cylinders.AsSpan().CopyTo(Cylinders); - } - - private void Deserialize(IGenericReader reader, int version) - { - var length = reader.ReadEncodedInt(); - Cylinders = new PuzzleChestCylinder[length]; - for (var i = 0; i < length; i++) - { - Cylinders[i] = (PuzzleChestCylinder)reader.ReadInt(); - } - } - - [AfterDeserialization] - private void AfterDeserialization() - { - if (Cylinders.Length == Length) - { - return; - } - - var cylinders = Cylinders; - Cylinders = new PuzzleChestCylinder[Length]; - - if (cylinders.Length > Length) - { - Cylinders = cylinders[..Length]; - return; - } - - cylinders.CopyTo(Cylinders); - for (var i = cylinders.Length; i < Length; i++) + for (var i = 0; i < Cylinders.Length; i++) { Cylinders[i] = RandomCylinder(); } } + public PuzzleChestSolution( + PuzzleChestCylinder first, PuzzleChestCylinder second, PuzzleChestCylinder third, + PuzzleChestCylinder fourth, PuzzleChestCylinder fifth + ) + { + First = first; + Second = second; + Third = third; + Fourth = fourth; + Fifth = fifth; + } + + public PuzzleChestSolution(PuzzleChestSolution solution) + { + for (var i = 0; i < Cylinders.Length; i++) + { + Cylinders[i] = solution.Cylinders[i]; + } + } + + public PuzzleChestSolution(IGenericReader reader) + { + var version = reader.ReadEncodedInt(); + + var length = reader.ReadEncodedInt(); + for (var i = 0;; i++) + { + if (i < length) + { + var cylinder = (PuzzleChestCylinder)reader.ReadInt(); + + if (i < Cylinders.Length) + { + Cylinders[i] = cylinder; + } + } + else if (i < Cylinders.Length) + { + Cylinders[i] = RandomCylinder(); + } + else + { + break; + } + } + } + + public PuzzleChestCylinder[] Cylinders { get; } = new PuzzleChestCylinder[Length]; + public PuzzleChestCylinder First { get => Cylinders[0]; @@ -126,8 +129,8 @@ namespace Server.Items cylinders = 0; colors = 0; - Span matchesSrc = stackalloc bool[solution.Cylinders.Length]; - Span matchesDst = stackalloc bool[solution.Cylinders.Length]; + var matchesSrc = new bool[solution.Cylinders.Length]; + var matchesDst = new bool[solution.Cylinders.Length]; for (var i = 0; i < Cylinders.Length; i++) { @@ -142,147 +145,124 @@ namespace Server.Items for (var i = 0; i < Cylinders.Length; i++) { - if (matchesSrc[i]) + if (!matchesSrc[i]) { - continue; - } - - for (var j = 0; j < solution.Cylinders.Length; j++) - { - if (Cylinders[i] == solution.Cylinders[j] && !matchesDst[j]) + for (var j = 0; j < solution.Cylinders.Length; j++) { - colors++; + if (Cylinders[i] == solution.Cylinders[j] && !matchesDst[j]) + { + colors++; - matchesDst[j] = true; + matchesDst[j] = true; + } } } } return cylinders == Cylinders.Length; } + + public virtual void Serialize(IGenericWriter writer) + { + writer.WriteEncodedInt(0); // version + + writer.WriteEncodedInt(Cylinders.Length); + for (var i = 0; i < Cylinders.Length; i++) + { + writer.Write((int)Cylinders[i]); + } + } } - [SerializationGenerator(1)] - public partial class PuzzleChestSolutionAndTime : PuzzleChestSolution + public class PuzzleChestSolutionAndTime : PuzzleChestSolution { - private void MigrateFrom(V0Content content) + public PuzzleChestSolutionAndTime(DateTime when, PuzzleChestSolution solution) : base(solution) => When = when; + + public PuzzleChestSolutionAndTime(IGenericReader reader) : base(reader) { - _when = content.When; + var version = reader.ReadEncodedInt(); + + When = reader.ReadDeltaTime(); } - [AnchoredDateTime] - [SerializableField(0)] - private DateTime _when; + public DateTime When { get; } - public PuzzleChestSolutionAndTime(DateTime when, PuzzleChestSolution solution) : base(solution) => _when = when; - - // For serialization - public PuzzleChestSolutionAndTime() + public override void Serialize(IGenericWriter writer) { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + + writer.WriteDeltaTime(When); } } - [SerializationGenerator(1)] - public abstract partial class PuzzleChest : BaseTreasureChest + public abstract class PuzzleChest : BaseTreasureChest { public const int HintsCount = 3; - public static readonly TimeSpan CleanupTime = TimeSpan.FromHours(1.0); + public readonly TimeSpan CleanupTime = TimeSpan.FromHours(1.0); - private TimerExecutionToken _cleanupTimerToken; + private readonly Dictionary m_Guesses = + new(); - [SerializableField(1, setter: "private")] - private PuzzleChestCylinder[] _hints; + private PuzzleChestSolution m_Solution; - [CanBeNull] - [Tidy] - [SerializableField(2)] - private Dictionary _guesses; - - public PuzzleChest(int itemID) : base(itemID) => _hints = new PuzzleChestCylinder[HintsCount]; - - [AfterDeserialization] - private void AfterDeserialization() + public PuzzleChest(int itemID) : base(itemID) { - // Validate hints array length - regenerate if mismatched - if (_hints.Length != HintsCount) + } + + public PuzzleChest(Serial serial) : base(serial) + { + } + + public PuzzleChestSolution Solution + { + get => m_Solution; + set { + m_Solution = value; InitHints(); } - - StartCleanupTimer(); } - private void Deserialize(IGenericReader reader, int version) - { - _solution = new PuzzleChestSolution(); - _solution.Deserialize(reader); - - var length = reader.ReadEncodedInt(); - for (var i = 0; i < length; i++) - { - var cylinder = (PuzzleChestCylinder)reader.ReadInt(); - - if (length == _hints.Length) - { - _hints[i] = cylinder; - } - } - - var guessCount = reader.ReadEncodedInt(); - if (guessCount <= 0) - { - return; - } - - _guesses = new Dictionary(guessCount); - for (var i = 0; i < guessCount; i++) - { - var m = reader.ReadEntity(); - (_guesses[m] = new PuzzleChestSolutionAndTime()).Deserialize(reader); - } - } - - [SerializableField(0, fieldChanged: nameof(OnSolutionChanged))] - private PuzzleChestSolution _solution; - - private void OnSolutionChanged(PuzzleChestSolution oldValue, PuzzleChestSolution newValue) - { - InitHints(); - } + public PuzzleChestCylinder[] Hints { get; private set; } = new PuzzleChestCylinder[HintsCount]; public PuzzleChestCylinder FirstHint { - get => _hints[0]; - set => _hints[0] = value; + get => Hints[0]; + set => Hints[0] = value; } public PuzzleChestCylinder SecondHint { - get => _hints[1]; - set => _hints[1] = value; + get => Hints[1]; + set => Hints[1] = value; } public PuzzleChestCylinder ThirdHint { - get => _hints[2]; - set => _hints[2] = value; + get => Hints[2]; + set => Hints[2] = value; } public override string DefaultName => null; private void InitHints() { - Span cylinders = stackalloc PuzzleChestCylinder[Solution.Cylinders.Length - 1]; - Solution.Cylinders.AsSpan(1).CopyTo(cylinders); + var list = new List(Solution.Cylinders.Length - 1); + for (var i = 1; i < Solution.Cylinders.Length; i++) + { + list.Add(Solution.Cylinders[i]); + } - cylinders.Shuffle(); - _hints = cylinders[..HintsCount].ToArray(); - } + Hints = new PuzzleChestCylinder[HintsCount]; - public override void OnDelete() - { - StopCleanupTimer(); - base.OnDelete(); + for (var i = 0; i < Hints.Length; i++) + { + var random = list.RandomElement(); + Hints[i] = random; + list.Remove(random); + } } protected override void SetLockLevel() @@ -310,14 +290,23 @@ namespace Server.Items ); } - from.SendGump(new PuzzleGump(from, this, solution)); + var gumps = from.GetGumps(); + + gumps.Close(); + gumps.Close(); + gumps.Send(new PuzzleGump(from, this, solution, 0)); + return true; } return false; } - public PuzzleChestSolutionAndTime GetLastGuess(Mobile m) => _guesses?.GetValueOrDefault(m); + public PuzzleChestSolutionAndTime GetLastGuess(Mobile m) + { + m_Guesses.TryGetValue(m, out var pcst); + return pcst; + } public void SubmitSolution(Mobile m, PuzzleChestSolution solution) { @@ -329,8 +318,7 @@ namespace Server.Items } else { - (_guesses ??= []).Add(m, new PuzzleChestSolutionAndTime(Core.Now, solution)); - StartCleanupTimer(); + m_Guesses[m] = new PuzzleChestSolutionAndTime(Core.Now, solution); m.SendGump(new StatusGump(correctCylinders, correctColors)); @@ -338,7 +326,7 @@ namespace Server.Items } } - public static void DoDamage(Mobile to) + public void DoDamage(Mobile to) { switch (Utility.Random(4)) { @@ -389,8 +377,7 @@ namespace Server.Items { base.LockPick(from); - StopCleanupTimer(); - _guesses = null; + m_Guesses.Clear(); } private static void GetRandomAOSStats(out int attributeCount, out int min, out int max) @@ -433,15 +420,14 @@ namespace Server.Items { DropItem(new Gold(600, 900)); - using var gems = PooledRefList.Create(); + var gems = new List(); for (var i = 0; i < 9; i++) { var gem = Loot.RandomGem(); var gemType = gem.GetType(); - for (var j = 0; j < gems.Count; j++) + foreach (var listGem in gems) { - var listGem = gems[j]; if (listGem.GetType() == gemType) { listGem.Amount++; @@ -456,9 +442,8 @@ namespace Server.Items } } - for (var i = 0; i < gems.Count; i++) + foreach (var gem in gems) { - var gem = gems[i]; DropItem(gem); } @@ -469,7 +454,16 @@ namespace Server.Items for (var i = 0; i < 2; i++) { - var item = Core.AOS ? Loot.RandomArmorOrShieldOrWeaponOrJewelry() : Loot.RandomArmorOrShieldOrWeapon(); + Item item; + + if (Core.AOS) + { + item = Loot.RandomArmorOrShieldOrWeaponOrJewelry(); + } + else + { + item = Loot.RandomArmorOrShieldOrWeapon(); + } if (item is BaseWeapon weapon) { @@ -528,309 +522,362 @@ namespace Server.Items Solution = new PuzzleChestSolution(); } - private void StartCleanupTimer() + public void CleanupGuesses() { - if (_cleanupTimerToken.Running || _guesses == null || _guesses.Count == 0) + var toDelete = new List(); + + foreach (var kvp in m_Guesses) { - return; - } - - Timer.StartTimer(CleanupTime, CleanupTime, CleanupGuesses, out _cleanupTimerToken); - } - - private void StopCleanupTimer() - { - _cleanupTimerToken.Cancel(); - } - - private void CleanupGuesses() - { - if (_guesses == null || _guesses.Count == 0) - { - StopCleanupTimer(); - return; - } - - foreach (var (key, value) in _guesses) - { - if (Core.Now - value.When > CleanupTime) + if (Core.Now - kvp.Value.When > CleanupTime) { - _guesses.Remove(key); + toDelete.Add(kvp.Key); } } - if (_guesses.Count == 0) + foreach (var m in toDelete) { - _guesses = null; - StopCleanupTimer(); + m_Guesses.Remove(m); } } - private class PuzzleGump : DynamicGump + public override void Serialize(IGenericWriter writer) { - private int _check; - private readonly PuzzleChest _chest; - private readonly PuzzleChestSolution _solution; - private readonly double _lockpicking; - private readonly PuzzleChestSolution _lastGuess; + CleanupGuesses(); - public override bool Singleton => true; + base.Serialize(writer); - public PuzzleGump(Mobile from, PuzzleChest chest, PuzzleChestSolution solution) : base(50, 50) + writer.WriteEncodedInt(0); // version + + m_Solution.Serialize(writer); + + writer.WriteEncodedInt(Hints.Length); + for (var i = 0; i < Hints.Length; i++) { - _chest = chest; - _solution = solution; - _lockpicking = from.Skills.Lockpicking.Base; - _lastGuess = chest.GetLastGuess(from); + writer.Write((int)Hints[i]); } - protected override void BuildLayout(ref DynamicGumpBuilder builder) + writer.WriteEncodedInt(m_Guesses.Count); + foreach (var kvp in m_Guesses) { - builder.SetNoDispose(); + writer.Write(kvp.Key); + kvp.Value.Serialize(writer); + } + } - builder.AddBackground(25, 0, 500, 410, 0x53); + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); - builder.AddImage(62, 20, 0x67); + var version = reader.ReadEncodedInt(); - builder.AddHtmlLocalized(80, 36, 110, 70, 1018309, true); // A Puzzle Lock + m_Solution = new PuzzleChestSolution(reader); + + var length = reader.ReadEncodedInt(); + for (var i = 0; i < length; i++) + { + var cylinder = (PuzzleChestCylinder)reader.ReadInt(); + + if (length == Hints.Length) + { + Hints[i] = cylinder; + } + } + + if (length != Hints.Length) + { + InitHints(); + } + + var guesses = reader.ReadEncodedInt(); + for (var i = 0; i < guesses; i++) + { + var m = reader.ReadEntity(); + var sol = new PuzzleChestSolutionAndTime(reader); + + m_Guesses[m] = sol; + } + } + + private class PuzzleGump : Gump + { + private readonly PuzzleChest m_Chest; + private readonly Mobile m_From; + private readonly PuzzleChestSolution m_Solution; + + public PuzzleGump(Mobile from, PuzzleChest chest, PuzzleChestSolution solution, int check) : base(50, 50) + { + m_From = from; + m_Chest = chest; + m_Solution = solution; + + Draggable = false; + + AddBackground(25, 0, 500, 410, 0x53); + + AddImage(62, 20, 0x67); + + AddHtmlLocalized(80, 36, 110, 70, 1018309, true); // A Puzzle Lock /* Correctly choose the sequence of cylinders needed to open the latch. Each cylinder * may potentially be used more than once. Beware! A false attempt could be deadly! */ - builder.AddHtmlLocalized(214, 26, 270, 90, 1018310, true, true); + AddHtmlLocalized(214, 26, 270, 90, 1018310, true, true); - AddLeftCylinderButton(ref builder, 62, 130, PuzzleChestCylinder.LightBlue, 10); - AddLeftCylinderButton(ref builder, 62, 180, PuzzleChestCylinder.Blue, 11); - AddLeftCylinderButton(ref builder, 62, 230, PuzzleChestCylinder.Green, 12); - AddLeftCylinderButton(ref builder, 62, 280, PuzzleChestCylinder.Orange, 13); + AddLeftCylinderButton(62, 130, PuzzleChestCylinder.LightBlue, 10); + AddLeftCylinderButton(62, 180, PuzzleChestCylinder.Blue, 11); + AddLeftCylinderButton(62, 230, PuzzleChestCylinder.Green, 12); + AddLeftCylinderButton(62, 280, PuzzleChestCylinder.Orange, 13); - AddRightCylinderButton(ref builder, 451, 130, PuzzleChestCylinder.Purple, 14); - AddRightCylinderButton(ref builder, 451, 180, PuzzleChestCylinder.Red, 15); - AddRightCylinderButton(ref builder, 451, 230, PuzzleChestCylinder.DarkBlue, 16); - AddRightCylinderButton(ref builder, 451, 280, PuzzleChestCylinder.Yellow, 17); + AddRightCylinderButton(451, 130, PuzzleChestCylinder.Purple, 14); + AddRightCylinderButton(451, 180, PuzzleChestCylinder.Red, 15); + AddRightCylinderButton(451, 230, PuzzleChestCylinder.DarkBlue, 16); + AddRightCylinderButton(451, 280, PuzzleChestCylinder.Yellow, 17); - if (_lockpicking >= 60.0) + var lockpicking = from.Skills.Lockpicking.Base; + if (lockpicking >= 60.0) { - builder.AddHtmlLocalized(160, 125, 230, 24, 1018308); // Lockpicking hint: + AddHtmlLocalized(160, 125, 230, 24, 1018308); // Lockpicking hint: - builder.AddBackground(159, 150, 230, 95, 0x13EC); + AddBackground(159, 150, 230, 95, 0x13EC); - if (_lockpicking >= 80.0) + if (lockpicking >= 80.0) { - builder.AddHtmlLocalized(165, 157, 200, 40, 1018312); // In the first slot: - AddCylinder(ref builder, 350, 165, _chest.Solution.First); + AddHtmlLocalized(165, 157, 200, 40, 1018312); // In the first slot: + AddCylinder(350, 165, chest.Solution.First); - builder.AddHtmlLocalized(165, 197, 200, 40, 1018313); // Used in unknown slot: - AddCylinder(ref builder, 350, 200, _chest.FirstHint); + AddHtmlLocalized(165, 197, 200, 40, 1018313); // Used in unknown slot: + AddCylinder(350, 200, chest.FirstHint); - if (_lockpicking >= 90.0) + if (lockpicking >= 90.0) { - AddCylinder(ref builder, 350, 212, _chest.SecondHint); + AddCylinder(350, 212, chest.SecondHint); } - if (_lockpicking >= 100.0) + if (lockpicking >= 100.0) { - AddCylinder(ref builder, 350, 224, _chest.ThirdHint); + AddCylinder(350, 224, chest.ThirdHint); } } else { - builder.AddHtmlLocalized(165, 157, 200, 40, 1018313); // Used in unknown slot: - AddCylinder(ref builder, 350, 160, _chest.FirstHint); + AddHtmlLocalized(165, 157, 200, 40, 1018313); // Used in unknown slot: + AddCylinder(350, 160, chest.FirstHint); - if (_lockpicking >= 70.0) + if (lockpicking >= 70.0) { - AddCylinder(ref builder, 350, 172, _chest.SecondHint); + AddCylinder(350, 172, chest.SecondHint); } } } - if (_lastGuess != null) + PuzzleChestSolution lastGuess = chest.GetLastGuess(from); + if (lastGuess != null) { - builder.AddHtmlLocalized(127, 249, 170, 20, 1018311); // Thy previous guess: + AddHtmlLocalized(127, 249, 170, 20, 1018311); // Thy previous guess: - builder.AddBackground(290, 247, 115, 25, 0x13EC); + AddBackground(290, 247, 115, 25, 0x13EC); - AddCylinder(ref builder, 281, 254, _lastGuess.First); - AddCylinder(ref builder, 303, 254, _lastGuess.Second); - AddCylinder(ref builder, 325, 254, _lastGuess.Third); - AddCylinder(ref builder, 347, 254, _lastGuess.Fourth); - AddCylinder(ref builder, 369, 254, _lastGuess.Fifth); + AddCylinder(281, 254, lastGuess.First); + AddCylinder(303, 254, lastGuess.Second); + AddCylinder(325, 254, lastGuess.Third); + AddCylinder(347, 254, lastGuess.Fourth); + AddCylinder(369, 254, lastGuess.Fifth); } - AddPedestal(ref builder, 140, 270, _solution.First, 0, _check == 0); - AddPedestal(ref builder, 195, 270, _solution.Second, 1, _check == 1); - AddPedestal(ref builder, 250, 270, _solution.Third, 2, _check == 2); - AddPedestal(ref builder, 305, 270, _solution.Fourth, 3, _check == 3); - AddPedestal(ref builder, 360, 270, _solution.Fifth, 4, _check == 4); + AddPedestal(140, 270, solution.First, 0, check == 0); + AddPedestal(195, 270, solution.Second, 1, check == 1); + AddPedestal(250, 270, solution.Third, 2, check == 2); + AddPedestal(305, 270, solution.Fourth, 3, check == 3); + AddPedestal(360, 270, solution.Fifth, 4, check == 4); - builder.AddButton(258, 370, 0xFA5, 0xFA7, 1); + AddButton(258, 370, 0xFA5, 0xFA7, 1); } - private static void AddLeftCylinderButton( - ref DynamicGumpBuilder builder, int x, int y, PuzzleChestCylinder cylinder, int buttonID - ) + private void AddLeftCylinderButton(int x, int y, PuzzleChestCylinder cylinder, int buttonID) { - builder.AddBackground(x, y, 30, 30, 0x13EC); - AddCylinder(ref builder, x - 7, y + 10, cylinder); - builder.AddButton(x + 38, y + 9, 0x13A8, 0x4B9, buttonID); + AddBackground(x, y, 30, 30, 0x13EC); + AddCylinder(x - 7, y + 10, cylinder); + AddButton(x + 38, y + 9, 0x13A8, 0x4B9, buttonID); } - private static void AddRightCylinderButton( - ref DynamicGumpBuilder builder, int x, int y, PuzzleChestCylinder cylinder, int buttonID - ) + private void AddRightCylinderButton(int x, int y, PuzzleChestCylinder cylinder, int buttonID) { - builder.AddBackground(x, y, 30, 30, 0x13EC); - AddCylinder(ref builder, x - 7, y + 10, cylinder); - builder.AddButton(x - 26, y + 9, 0x13A8, 0x4B9, buttonID); + AddBackground(x, y, 30, 30, 0x13EC); + AddCylinder(x - 7, y + 10, cylinder); + AddButton(x - 26, y + 9, 0x13A8, 0x4B9, buttonID); } - private static void AddPedestal( - ref DynamicGumpBuilder builder, int x, int y, PuzzleChestCylinder cylinder, int switchID, bool initialState - ) + private void AddPedestal(int x, int y, PuzzleChestCylinder cylinder, int switchID, bool initialState) { - builder.AddItem(x, y, 0xB10); - builder.AddItem(x - 23, y + 12, 0xB12); - builder.AddItem(x + 23, y + 12, 0xB13); - builder.AddItem(x, y + 23, 0xB11); + AddItem(x, y, 0xB10); + AddItem(x - 23, y + 12, 0xB12); + AddItem(x + 23, y + 12, 0xB13); + AddItem(x, y + 23, 0xB11); if (cylinder != PuzzleChestCylinder.None) { - builder.AddItem(x, y + 2, 0x51A); - AddCylinder(ref builder, x - 1, y + 19, cylinder); + AddItem(x, y + 2, 0x51A); + AddCylinder(x - 1, y + 19, cylinder); } else { - builder.AddItem(x, y + 2, 0x521); + AddItem(x, y + 2, 0x521); } - builder.AddRadio(x + 7, y + 65, 0x867, 0x86A, initialState, switchID); + AddRadio(x + 7, y + 65, 0x867, 0x86A, initialState, switchID); } - private static void AddCylinder(ref DynamicGumpBuilder builder, int x, int y, PuzzleChestCylinder cylinder) + private void AddCylinder(int x, int y, PuzzleChestCylinder cylinder) { if (cylinder != PuzzleChestCylinder.None) { - builder.AddItem(x, y, (int)cylinder); + AddItem(x, y, (int)cylinder); } else { - builder.AddItem(x + 9, y, (int)cylinder); + AddItem(x + 9, y, (int)cylinder); } } public override void OnResponse(NetState sender, in RelayInfo info) { - var from = sender.Mobile; - - if (_chest.Deleted || info.ButtonID == 0 || !from.CheckAlive()) + if (m_Chest.Deleted || info.ButtonID == 0 || !m_From.CheckAlive()) { return; } - if (from.AccessLevel == AccessLevel.Player && - (from.Map != _chest.Map || !from.InRange(_chest.GetWorldLocation(), 2))) + if (m_From.AccessLevel == AccessLevel.Player && + (m_From.Map != m_Chest.Map || !m_From.InRange(m_Chest.GetWorldLocation(), 2))) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500446); // That is too far away. + m_From.LocalOverheadMessage(MessageType.Regular, 0x3B2, 500446); // That is too far away. return; } if (info.ButtonID == 1) { - _chest.SubmitSolution(from, _solution); - return; + m_Chest.SubmitSolution(m_From, m_Solution); } - - if (info.Switches.Length == 0) + else { - return; + if (info.Switches.Length == 0) + { + return; + } + + var pedestal = info.Switches[0]; + if (pedestal < 0 || pedestal >= m_Solution.Cylinders.Length) + { + return; + } + + PuzzleChestCylinder cylinder; + switch (info.ButtonID) + { + case 10: + cylinder = PuzzleChestCylinder.LightBlue; + break; + case 11: + cylinder = PuzzleChestCylinder.Blue; + break; + case 12: + cylinder = PuzzleChestCylinder.Green; + break; + case 13: + cylinder = PuzzleChestCylinder.Orange; + break; + case 14: + cylinder = PuzzleChestCylinder.Purple; + break; + case 15: + cylinder = PuzzleChestCylinder.Red; + break; + case 16: + cylinder = PuzzleChestCylinder.DarkBlue; + break; + case 17: + cylinder = PuzzleChestCylinder.Yellow; + break; + default: return; + } + + m_Solution.Cylinders[pedestal] = cylinder; + + m_From.SendGump(new PuzzleGump(m_From, m_Chest, m_Solution, pedestal)); } - - var pedestal = info.Switches[0]; - if (pedestal < 0 || pedestal >= _solution.Cylinders.Length) - { - return; - } - - PuzzleChestCylinder cylinder = info.ButtonID switch - { - 10 => PuzzleChestCylinder.LightBlue, - 11 => PuzzleChestCylinder.Blue, - 12 => PuzzleChestCylinder.Green, - 13 => PuzzleChestCylinder.Orange, - 14 => PuzzleChestCylinder.Purple, - 15 => PuzzleChestCylinder.Red, - 16 => PuzzleChestCylinder.DarkBlue, - 17 => PuzzleChestCylinder.Yellow, - _ => PuzzleChestCylinder.None - }; - - if (cylinder == PuzzleChestCylinder.None) - { - return; - } - - _solution.Cylinders[pedestal] = cylinder; - - _check = pedestal; - from.SendGump(this); } } - private class StatusGump : StaticGump + private class StatusGump : Gump { - private readonly int _correctCylinders; - private readonly int _correctColors; - - public override bool Singleton => true; - public StatusGump(int correctCylinders, int correctColors) : base(50, 50) { - _correctCylinders = correctCylinders; - _correctColors = correctColors; - } + AddBackground(15, 250, 305, 163, 0x53); + AddBackground(28, 265, 280, 133, 0xBB8); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddBackground(15, 250, 305, 163, 0x53); - builder.AddBackground(28, 265, 280, 133, 0xBB8); + AddHtmlLocalized(35, 271, 270, 24, 1018314); // Thou hast failed to solve the puzzle! - builder.AddHtmlLocalized(35, 271, 270, 24, 1018314); // Thou hast failed to solve the puzzle! + AddHtmlLocalized(35, 297, 250, 24, 1018315); // Correctly placed colors: + AddLabel(285, 297, 0x44, correctCylinders.ToString()); - builder.AddHtmlLocalized(35, 297, 250, 24, 1018315); // Correctly placed colors: - builder.AddLabelPlaceholder(285, 297, 0x44, "cylinders"); + AddHtmlLocalized(35, 323, 250, 24, 1018316); // Used colors in wrong slots: + AddLabel(285, 323, 0x44, correctColors.ToString()); - builder.AddHtmlLocalized(35, 323, 250, 24, 1018316); // Used colors in wrong slots: - builder.AddLabelPlaceholder(285, 323, 0x44, "colors"); - - builder.AddButton(152, 369, 0xFA5, 0xFA7, 0); // Close - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - builder.SetStringSlot("cylinders", $"{_correctCylinders}"); - builder.SetStringSlot("colors", $"{_correctColors}"); + AddButton(152, 369, 0xFA5, 0xFA7, 0); } } } [Flippable(0xE41, 0xE40)] - [SerializationGenerator(0)] - public partial class MetalGoldenPuzzleChest : PuzzleChest + public class MetalGoldenPuzzleChest : PuzzleChest { [Constructible] public MetalGoldenPuzzleChest() : base(0xE41) { } + + public MetalGoldenPuzzleChest(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } [Flippable(0xE80, 0x9A8)] - [SerializationGenerator(0)] - public partial class StrongBoxPuzzle : PuzzleChest + public class StrongBoxPuzzle : PuzzleChest { [Constructible] public StrongBoxPuzzle() : base(0xE80) { } + + public StrongBoxPuzzle(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } } diff --git a/Projects/UOContent/Engines/Khaldun/RaiseSwitch.cs b/Projects/UOContent/Engines/Khaldun/RaiseSwitch.cs index e277630d3..f967f9ce8 100644 --- a/Projects/UOContent/Engines/Khaldun/RaiseSwitch.cs +++ b/Projects/UOContent/Engines/Khaldun/RaiseSwitch.cs @@ -101,7 +101,7 @@ public partial class RaiseSwitch : Item } } - [AfterDeserialization(false)] + [AfterDeserialization] private void AfterDeserialization() { Reset(); @@ -109,31 +109,38 @@ public partial class RaiseSwitch : Item private class ResetTimer : Timer { - private readonly RaiseSwitch _raiseSwitch; + private readonly RaiseSwitch m_RaiseSwitch; - public ResetTimer(RaiseSwitch raiseSwitch, TimeSpan delay) : base(delay) => _raiseSwitch = raiseSwitch; + public ResetTimer(RaiseSwitch raiseSwitch, TimeSpan delay) : base(delay) + { + m_RaiseSwitch = raiseSwitch; + } protected override void OnTick() { - if (_raiseSwitch.Deleted) + if (m_RaiseSwitch.Deleted) { return; } - _raiseSwitch._resetTimer = null; - _raiseSwitch.Reset(); + m_RaiseSwitch._resetTimer = null; + + m_RaiseSwitch.Reset(); } } } -[SerializationGenerator(0)] -public partial class DisappearingRaiseSwitch : RaiseSwitch +public class DisappearingRaiseSwitch : RaiseSwitch { [Constructible] public DisappearingRaiseSwitch() : base(0x108F) { } + public DisappearingRaiseSwitch(Serial serial) : base(serial) + { + } + public int CurrentRange => Visible ? 3 : 2; public override bool HandlesOnMovement => true; @@ -170,7 +177,6 @@ public partial class DisappearingRaiseSwitch : RaiseSwitch } } - [AfterDeserialization(false)] public void Refresh() { foreach (var mob in GetMobilesInRange(CurrentRange)) @@ -182,4 +188,25 @@ public partial class DisappearingRaiseSwitch : RaiseSwitch } } } + + public override void Serialize(IGenericWriter writer) + { + if (RaisableItem?.Deleted == true) + { + RaisableItem = null; + } + + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + + Timer.StartTimer(Refresh); + } } diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Heartwood.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Heartwood.cs index 4bf21fdf0..2ac1e889f 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Heartwood.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Heartwood.cs @@ -2158,10 +2158,9 @@ public partial class Landy : BaseCreature AddItem(new ShortPants(Utility.RandomYellowHue())); AddItem(new Tunic(Utility.RandomYellowHue())); - AddItem(new LeafGloves - { - Hue = Utility.RandomYellowHue() - }); + Item gloves = new LeafGloves(); + gloves.Hue = Utility.RandomYellowHue(); + AddItem(gloves); } public override bool IsInvulnerable => true; @@ -2721,10 +2720,11 @@ public partial class Tholef : BaseCreature AddItem(new ShortPants(0x28C)); AddItem(new Shirt(0x28C)); - AddItem(new LeafArms - { - Hue = 0x28C - }); + Item item; + + item = new LeafArms(); + item.Hue = 0x28C; + AddItem(item); } public override bool IsInvulnerable => true; @@ -2807,10 +2807,11 @@ public partial class Waelian : BaseCreature AddItem(new LongPants(0x340)); AddItem(new GemmedCirclet()); - AddItem(new LeafChest - { - Hue = 0x344 - }); + Item item; + + item = new LeafChest(); + item.Hue = 0x344; + AddItem(item); } public override bool IsInvulnerable => true; @@ -3056,10 +3057,11 @@ public partial class Lohn : BaseCreature AddItem(new SmithHammer()); AddItem(new GemmedCirclet()); - AddItem(new LeafChest - { - Hue = 0x359 - }); + Item item; + + item = new LeafChest(); + item.Hue = 0x359; + AddItem(item); } public override bool IsInvulnerable => true; @@ -3403,10 +3405,11 @@ public partial class ElderVicaie : BaseCreature AddItem(new ElvenBoots()); AddItem(new Tunic(0x732)); - AddItem(new LeafLegs - { - Hue = 0x3B2 - }); + Item item; + + item = new LeafLegs(); + item.Hue = 0x3B2; + AddItem(item); } public override bool IsInvulnerable => true; @@ -3570,10 +3573,11 @@ public partial class Bolaevin : BaseCreature AddItem(new LeafChest()); AddItem(new LeafArms()); - AddItem(new LeafLegs - { - Hue = 0x1BB - }); + Item item; + + item = new LeafLegs(); + item.Hue = 0x1BB; + AddItem(item); } public override bool IsInvulnerable => true; diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Heritage.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Heritage.cs index 6be0871d2..e87626844 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Heritage.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Heritage.cs @@ -202,8 +202,7 @@ public class TheJoysOfLife : MLQuest } [QuesterName("Maul")] -[SerializationGenerator(0, false)] -public partial class MaulTheBear : GrizzlyBear +public class MaulTheBear : GrizzlyBear { [Constructible] public MaulTheBear() @@ -213,12 +212,30 @@ public partial class MaulTheBear : GrizzlyBear Tamable = false; } + public MaulTheBear(Serial serial) + : base(serial) + { + } + public override bool IsInvulnerable => true; public override string DefaultName => "Maul"; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } -[SerializationGenerator(0, false)] -public partial class Strongroot : Treefellow +public class Strongroot : Treefellow { [Constructible] public Strongroot() @@ -227,8 +244,27 @@ public partial class Strongroot : Treefellow FightMode = FightMode.None; } + public Strongroot(Serial serial) + : base(serial) + { + } + public override bool IsInvulnerable => true; public override string DefaultName => "Strongroot"; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } [SerializationGenerator(0, false)] diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Ilshenar.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Ilshenar.cs index 8e24c61cd..ad86f9645 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Ilshenar.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Ilshenar.cs @@ -104,14 +104,18 @@ public class GuileIrkAndSpite : MLQuest } } -[SerializationGenerator(0, false)] -public partial class Lissbet : BaseEscortable +public class Lissbet : BaseEscortable { [Constructible] public Lissbet() { } + public Lissbet(Serial serial) + : base(serial) + { + } + public override bool StaticMLQuester => true; public override bool InitialInnocent => true; public override string DefaultName => "Lissbet"; @@ -152,6 +156,20 @@ public partial class Lissbet : BaseEscortable AddItem(new FancyShirt(Utility.RandomYellowHue())); AddItem(new Sandals()); } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } [SerializationGenerator(0, false)] diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/LostItems.cs b/Projects/UOContent/Engines/ML Quests/Definitions/LostItems.cs index ff1c023cd..cfcee86f3 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/LostItems.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/LostItems.cs @@ -1,49 +1,69 @@ using System; -using ModernUO.Serialization; using Server.Engines.MLQuests.Items; using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Rewards; -namespace Server.Engines.MLQuests.Definitions; -// TODO: Assassination Contract, Evidence, Lost in Transit, Last Words - -public class LostAndFound : MLQuest +namespace Server.Engines.MLQuests.Definitions { - public LostAndFound() + // TODO: Assassination Contract, Evidence, Lost in Transit, Last Words + + public class LostAndFound : MLQuest { - Activated = true; - Title = 1072370; // Lost and Found - Description = - 1072589; // The battered, old bucket is inscribed with barely legible writing that indicates it belongs to someone named "Dallid". Maybe they'd pay for its return? - RefusalMessage = - 1072590; // You're right, who cares if Dallid might pay for his battered old bucket back. This way you can carry it around with you! - InProgressMessage = 1072591; // Whoever this "Dallid" might be, he's probably looking for his bucket. - CompletionMessage = - 1074580; // Is that my bucket? I had to ditch my favorite bucket when a group of ratmen jumped me! + public LostAndFound() + { + Activated = true; + Title = 1072370; // Lost and Found + Description = + 1072589; // The battered, old bucket is inscribed with barely legible writing that indicates it belongs to someone named "Dallid". Maybe they'd pay for its return? + RefusalMessage = + 1072590; // You're right, who cares if Dallid might pay for his battered old bucket back. This way you can carry it around with you! + InProgressMessage = 1072591; // Whoever this "Dallid" might be, he's probably looking for his bucket. + CompletionMessage = + 1074580; // Is that my bucket? I had to ditch my favorite bucket when a group of ratmen jumped me! - Objectives.Add( - new TimedDeliverObjective( - TimeSpan.FromSeconds(600), - typeof(BatteredBucket), - 1, - "battered bucket", - typeof(Dallid), - false - ) - ); + Objectives.Add( + new TimedDeliverObjective( + TimeSpan.FromSeconds(600), + typeof(BatteredBucket), + 1, + "battered bucket", + typeof(Dallid), + false + ) + ); - Rewards.Add(ItemReward.BagOfTrinkets); + Rewards.Add(ItemReward.BagOfTrinkets); + } + } + + public class BatteredBucket : TransientQuestGiverItem + { + [Constructible] + public BatteredBucket() + : base(0x2004, TimeSpan.FromMinutes(10)) => + LootType = LootType.Blessed; + + public BatteredBucket(Serial serial) + : base(serial) + { + } + // Original label, doesn't fit the expiration message well + // public override int LabelNumber => 1073129; // A battered bucket. + + public override string DefaultName => "battered bucket"; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } } - -[SerializationGenerator(0, false)] -public partial class BatteredBucket : TransientQuestGiverItem -{ - [Constructible] - public BatteredBucket() : base(0x2004, TimeSpan.FromMinutes(10)) => LootType = LootType.Blessed; - - // Original label, doesn't fit the expiration message well - // public override int LabelNumber => 1073129; // A battered bucket. - - public override string DefaultName => "battered bucket"; -} diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenSkillTraining.cs b/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenSkillTraining.cs index e0a051e0c..fd0401b94 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenSkillTraining.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenSkillTraining.cs @@ -642,8 +642,7 @@ public class ItsHammerTime : MLQuest } } -[SerializationGenerator(0, false)] -public partial class Aelorn : KeeperOfChivalry +public class Aelorn : KeeperOfChivalry { [Constructible] public Aelorn() @@ -668,6 +667,11 @@ public partial class Aelorn : KeeperOfChivalry SetSkill(SkillName.Chivalry, 120.0); } + public Aelorn(Serial serial) + : base(serial) + { + } + public override string DefaultName => "Aelorn"; public override bool CanShout => true; @@ -687,6 +691,20 @@ public partial class Aelorn : KeeperOfChivalry AddItem(new PlateGorget()); AddItem(new OrderShield()); } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } [SerializationGenerator(0, false)] @@ -1256,10 +1274,11 @@ public partial class Mithneral : BaseCreature AddItem(new Backpack()); AddItem(new Sandals()); - AddItem(new GustarShroud - { - Hue = 0x51C - }); + Item item; + + item = new GustarShroud(); + item.Hue = 0x51C; + AddItem(item); } public override bool IsInvulnerable => true; @@ -1857,9 +1876,10 @@ public partial class GeorgeHephaestus : Blacksmith AddItem(new Bascinet()); AddItem(new FullApron(0x8AB)); - AddItem(new SmithHammer - { - Hue = 0x8AB - }); + Item item; + + item = new SmithHammer(); + item.Hue = 0x8AB; + AddItem(item); } } diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenTraining.cs b/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenTraining.cs index 324898047..67cf71bb1 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenTraining.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/NewHavenTraining.cs @@ -472,10 +472,11 @@ public partial class Gervis : BaseCreature AddItem(new Doublet(0x652)); AddItem(new SmithHammer()); - AddItem(new LeatherGloves - { - Hue = 0x3B2 - }); + Item item; + + item = new LeatherGloves(); + item.Hue = 0x3B2; + AddItem(item); } public override bool IsInvulnerable => true; @@ -749,10 +750,11 @@ public partial class Hargrove : BaseCreature AddItem(new Bandana(0x20)); AddItem(new BattleAxe()); - AddItem(new PlateGloves - { - Hue = 0x21E - }); + Item item; + + item = new PlateGloves(); + item.Hue = 0x21E; + AddItem(item); } public override bool IsInvulnerable => true; diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Sanctuary.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Sanctuary.cs index 605fca1c6..1e03fc986 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Sanctuary.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Sanctuary.cs @@ -784,10 +784,11 @@ public partial class Beotham : BaseCreature AddItem(new ShortPants(0x522)); AddItem(new FancyShirt(0x515)); - AddItem(new LeafGloves - { - Hue = 0x901 - }); + Item item; + + item = new LeafGloves(); + item.Hue = 0x901; + AddItem(item); } public override bool IsInvulnerable => true; @@ -1013,10 +1014,11 @@ public partial class LorekeeperRollarn : BaseCreature AddItem(new Circlet()); AddItem(new LeafChest()); - AddItem(new LeafLegs - { - Hue = 0x71A - }); + Item item; + + item = new LeafLegs(); + item.Hue = 0x71A; + AddItem(item); } public override bool IsInvulnerable => true; diff --git a/Projects/UOContent/Engines/ML Quests/Definitions/Spellweaving.cs b/Projects/UOContent/Engines/ML Quests/Definitions/Spellweaving.cs index 96c9431ea..fabd58935 100644 --- a/Projects/UOContent/Engines/ML Quests/Definitions/Spellweaving.cs +++ b/Projects/UOContent/Engines/ML Quests/Definitions/Spellweaving.cs @@ -599,10 +599,9 @@ public partial class Synaeva : BaseCreature SetSkill(SkillName.Meditation, 60.0, 80.0); SetSkill(SkillName.Focus, 60.0, 80.0); - AddItem(new RavenHelm - { - Hue = Utility.RandomGreenHue() - }); + Item item = new RavenHelm(); + item.Hue = Utility.RandomGreenHue(); + AddItem(item); AddItem(new FemaleLeafChest()); AddItem(new LeafArms()); diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/BaseQuestGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/BaseQuestGump.cs index befc69610..4651454ba 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/BaseQuestGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/BaseQuestGump.cs @@ -24,92 +24,84 @@ namespace Server.Engines.MLQuests.Gumps Resign = 0x2EF5 } - public abstract class BaseMLQuestGump : DynamicGump + public abstract class BaseQuestGump : Gump { - private readonly List _buttons; - private readonly int _label; - private int _maxPages; - private int _page; - private string _title; + private readonly List m_Buttons; + private int m_Label; + private int m_MaxPages; + + private int m_Page; + private string m_Title; // RunUO optimized version - protected BaseMLQuestGump(int label) : base(75, 25) + public BaseQuestGump(int label) + : base(75, 25) { - _label = label; - _page = 0; - _maxPages = 0; - _title = null; - _buttons = new List(2); + m_Page = 0; + m_MaxPages = 0; + m_Label = label; + m_Title = null; + m_Buttons = new List(2); + + Closable = false; + + AddPage(0); + + AddImageTiled(50, 20, 400, 460, 0x1404); + AddImageTiled(50, 29, 30, 450, 0x28DC); + AddImageTiled(34, 140, 17, 339, 0x242F); + AddImage(48, 135, 0x28AB); + AddImage(-16, 285, 0x28A2); + AddImage(0, 10, 0x28B5); + AddImage(25, 0, 0x28B4); + AddImageTiled(83, 15, 350, 15, 0x280A); + AddImage(34, 479, 0x2842); + AddImage(442, 479, 0x2840); + AddImageTiled(51, 479, 392, 17, 0x2775); + AddImageTiled(415, 29, 44, 450, 0xA2D); + AddImageTiled(415, 29, 30, 450, 0x28DC); + // AddLabel( 100, 50, 0x481, "" ); + AddImage(370, 50, 0x589); + AddImage(379, 60, 0x15A9); + AddImage(425, 0, 0x28C9); + AddImage(90, 33, 0x232D); + AddHtmlLocalized(130, 45, 270, 16, label, 0x7FFF); + AddImageTiled(130, 65, 175, 1, 0x238D); } - protected override void BuildLayout(ref DynamicGumpBuilder builder) + public void BuildPage() { - builder.SetNoClose(); + AddPage(++m_Page); - builder.AddPage(); - - builder.AddImageTiled(50, 20, 400, 460, 0x1404); - builder.AddImageTiled(50, 29, 30, 450, 0x28DC); - builder.AddImageTiled(34, 140, 17, 339, 0x242F); - builder.AddImage(48, 135, 0x28AB); - builder.AddImage(-16, 285, 0x28A2); - builder.AddImage(0, 10, 0x28B5); - builder.AddImage(25, 0, 0x28B4); - builder.AddImageTiled(83, 15, 350, 15, 0x280A); - builder.AddImage(34, 479, 0x2842); - builder.AddImage(442, 479, 0x2840); - builder.AddImageTiled(51, 479, 392, 17, 0x2775); - builder.AddImageTiled(415, 29, 44, 450, 0xA2D); - builder.AddImageTiled(415, 29, 30, 450, 0x28DC); - builder.AddImage(370, 50, 0x589); - builder.AddImage(379, 60, 0x15A9); - builder.AddImage(425, 0, 0x28C9); - builder.AddImage(90, 33, 0x232D); - builder.AddHtmlLocalized(130, 45, 270, 16, _label, 0x7FFF); - builder.AddImageTiled(130, 65, 175, 1, 0x238D); - - // Reset paging for subclass content - _page = 0; - - BuildContent(ref builder); - } - - protected abstract void BuildContent(ref DynamicGumpBuilder builder); - - protected void BuildPage(ref DynamicGumpBuilder builder) - { - builder.AddPage(++_page); - - if (_page > 1) + if (m_Page > 1) { - builder.AddButton( + AddButton( 130, 430, (int)ButtonGraphic.Previous, (int)ButtonGraphic.Previous + 2, 0, GumpButtonType.Page, - _page - 1 + m_Page - 1 ); } - if (_page < _maxPages) + if (m_Page < m_MaxPages) { - builder.AddButton( + AddButton( 275, 430, (int)ButtonGraphic.Continue, (int)ButtonGraphic.Continue + 2, 0, GumpButtonType.Page, - _page + 1 + m_Page + 1 ); } - for (var i = 0; i < _buttons.Count; i++) + foreach (var button in m_Buttons) { - var button = _buttons[i]; - builder.AddButton( + AddButton( button.Position == ButtonPosition.Left ? 95 : 313, 455, (int)button.Graphic, @@ -118,37 +110,37 @@ namespace Server.Engines.MLQuests.Gumps ); } - if (_title != null) + if (m_Title != null) { - builder.AddHtmlLocalized(130, 68, 220, 48, 1114513, _title, 0x2710); //
~1_TOKEN~
+ AddHtmlLocalized(130, 68, 220, 48, 1114513, m_Title, 0x2710); //
~1_TOKEN~
} } - protected void SetPageCount(int maxPages) + public void SetPageCount(int maxPages) { - _maxPages = maxPages; + m_MaxPages = maxPages; } - protected void SetTitle(TextDefinition def) + public void SetTitle(TextDefinition def) { if (def.Number > 0) { - _title = $"#{def.Number}"; // OSI does "@@#{0}" instead, why? KR client related? + m_Title = $"#{def.Number}"; // OSI does "@@#{0}" instead, why? KR client related? } else { - _title = def.String; + m_Title = def.String; } } - protected void RegisterButton(ButtonPosition position, ButtonGraphic graphic, int buttonID) + public void RegisterButton(ButtonPosition position, ButtonGraphic graphic, int buttonID) { - _buttons.Add(new ButtonInfo(position, graphic, buttonID)); + m_Buttons.Add(new ButtonInfo(position, graphic, buttonID)); } - protected static void AddDescription(ref DynamicGumpBuilder builder, MLQuest quest) + public void AddDescription(MLQuest quest) { - builder.AddHtmlLocalized( + AddHtmlLocalized( 98, 140, 312, @@ -157,14 +149,14 @@ namespace Server.Engines.MLQuests.Gumps 0x2710 ); - quest.Description.AddHtmlText(ref builder, 98, 156, 312, 240, false, true, 0x5F90, 0xBDE784); + quest.Description.AddHtmlText(this, 98, 156, 312, 240, false, true, 0x5F90, 0xBDE784); } - protected static void AddObjectives(ref DynamicGumpBuilder builder, MLQuest quest) + public void AddObjectives(MLQuest quest) { - builder.AddHtmlLocalized(98, 140, 312, 16, 1049073, 0x2710); // Objective: + AddHtmlLocalized(98, 140, 312, 16, 1049073, 0x2710); // Objective: // All of the following / Only one of the following - builder.AddHtmlLocalized( + AddHtmlLocalized( 98, 156, 312, @@ -177,7 +169,7 @@ namespace Server.Engines.MLQuests.Gumps foreach (var objective in quest.Objectives) { - objective.WriteToGump(ref builder, ref y); + objective.WriteToGump(this, ref y); if (objective.IsTimed) { @@ -186,18 +178,18 @@ namespace Server.Engines.MLQuests.Gumps y -= 16; } - BaseObjectiveInstance.WriteTimeRemaining(ref builder, ref y, objective.Duration); + BaseObjectiveInstance.WriteTimeRemaining(this, ref y, objective.Duration); } } } - protected static void AddObjectivesProgress(ref DynamicGumpBuilder builder, MLQuestInstance instance) + public void AddObjectivesProgress(MLQuestInstance instance) { var quest = instance.Quest; - builder.AddHtmlLocalized(98, 140, 312, 16, 1049073, 0x2710); // Objective: + AddHtmlLocalized(98, 140, 312, 16, 1049073, 0x2710); // Objective: // All of the following / Only one of the following - builder.AddHtmlLocalized( + AddHtmlLocalized( 98, 156, 312, @@ -210,55 +202,55 @@ namespace Server.Engines.MLQuests.Gumps foreach (var objInstance in instance.Objectives) { - objInstance.WriteToGump(ref builder, ref y); + objInstance.WriteToGump(this, ref y); } } - protected static void AddRewardsPage(ref DynamicGumpBuilder builder, MLQuest quest) // For the quest log/offer gumps + public void AddRewardsPage(MLQuest quest) // For the quest log/offer gumps { - builder.AddHtmlLocalized(98, 140, 312, 16, 1072201, 0x2710); // Reward + AddHtmlLocalized(98, 140, 312, 16, 1072201, 0x2710); // Reward var y = 162; if (quest.Rewards.Count > 1) { // TODO: Is this what this is for? Does "Only one of the following" occur? - builder.AddHtmlLocalized(98, 156, 312, 16, 1072208, 0x2710); // All of the following + AddHtmlLocalized(98, 156, 312, 16, 1072208, 0x2710); // All of the following y += 16; } - AddRewards(ref builder, quest, 105, y, 16); + AddRewards(quest, 105, y, 16); } - protected static void AddRewards(ref DynamicGumpBuilder builder, MLQuest quest) // For the claim rewards gump + public void AddRewards(MLQuest quest) // For the claim rewards gump { var y = 146; if (quest.Rewards.Count > 1) { // TODO: Is this what this is for? Does "Only one of the following" occur? - builder.AddHtmlLocalized(100, 140, 312, 16, 1072208, 0x2710); // All of the following + AddHtmlLocalized(100, 140, 312, 16, 1072208, 0x2710); // All of the following y += 16; } - AddRewards(ref builder, quest, 107, y, 26); + AddRewards(quest, 107, y, 26); } - protected static void AddRewards(ref DynamicGumpBuilder builder, MLQuest quest, int x, int y, int spacing) + public void AddRewards(MLQuest quest, int x, int y, int spacing) { var xReward = x + 28; foreach (var reward in quest.Rewards) { - builder.AddImage(x, y + 1, 0x4B9); - reward.WriteToGump(ref builder, xReward, ref y); + AddImage(x, y + 1, 0x4B9); + reward.WriteToGump(this, xReward, ref y); y += spacing; } } - protected static void AddConversation(ref DynamicGumpBuilder builder, TextDefinition text) + public void AddConversation(TextDefinition text) { - text.AddHtmlText(ref builder, 98, 140, 312, 180, false, true, 0x5F90, 0xBDE784); + text.AddHtmlText(this, 98, 140, 312, 180, false, true, 0x5F90, 0xBDE784); } /* OSI gump IDs: @@ -266,7 +258,7 @@ namespace Server.Engines.MLQuests.Gumps * 801 - QuestCancelConfirmGump * 802 - ?? (gets closed by Toggle Quest Item) * 803 - QuestRewardGump - * 804 - ?? (gets closed by Toggle Quest Item and most quest gumps) + * 804 - ?? (gets closed by Toggle Quest Item) * 805 - QuestLogGump * 806 - QuestConversationGump (refuse / in progress) * 807 - ?? (gets closed by Toggle Quest Item and most quest gumps) @@ -285,7 +277,7 @@ namespace Server.Engines.MLQuests.Gumps gumps.Close(); } - private readonly struct ButtonInfo + private struct ButtonInfo { public ButtonPosition Position { get; } diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/InfoNPCGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/InfoNPCGump.cs index acf4e3b42..b48733edb 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/InfoNPCGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/InfoNPCGump.cs @@ -1,40 +1,17 @@ -using Server.Gumps; - namespace Server.Engines.MLQuests.Gumps { - public class InfoNPCGump : BaseMLQuestGump + public class InfoNPCGump : BaseQuestGump { - private readonly TextDefinition _title; - private readonly TextDefinition _message; - - public override bool Singleton => true; - - private InfoNPCGump(TextDefinition title, TextDefinition message) + public InfoNPCGump(TextDefinition title, TextDefinition message) : base(1060668) // INFORMATION { - _title = title; - _message = message; - RegisterButton(ButtonPosition.Left, ButtonGraphic.Close, 3); SetPageCount(1); - } - public static void DisplayTo(Mobile from, TextDefinition title, TextDefinition message) - { - if (from?.NetState == null) - { - return; - } - - from.SendGump(new InfoNPCGump(title, message)); - } - - protected override void BuildContent(ref DynamicGumpBuilder builder) - { - BuildPage(ref builder); - _title.AddHtmlText(ref builder, 160, 108, 250, 16, false, false, 0x2710, 0x4AC684); - _message.AddHtmlText(ref builder, 98, 156, 312, 180, false, true, 0x5F90, 0xBDE784); + BuildPage(); + title.AddHtmlText(this, 160, 108, 250, 16, false, false, 0x2710, 0x4AC684); + message.AddHtmlText(this, 98, 156, 312, 180, false, true, 0x5F90, 0xBDE784); } } } diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/QuestCancelConfirmGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/QuestCancelConfirmGump.cs index 20bbfbc25..ecf52d572 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/QuestCancelConfirmGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/QuestCancelConfirmGump.cs @@ -3,66 +3,52 @@ using Server.Network; namespace Server.Engines.MLQuests.Gumps { - public class QuestCancelConfirmGump : DynamicGump + public class QuestCancelConfirmGump : Gump { - private readonly bool _closeGumps; - private readonly MLQuestInstance _instance; + private readonly bool m_CloseGumps; + private readonly MLQuestInstance m_Instance; - public override bool Singleton => true; - - private QuestCancelConfirmGump(MLQuestInstance instance, bool closeGumps) : base(120, 50) + public QuestCancelConfirmGump(MLQuestInstance instance, bool closeGumps = true) + : base(120, 50) { - _instance = instance; - _closeGumps = closeGumps; - } - - public static void DisplayTo(Mobile from, MLQuestInstance instance, bool closeGumps = true) - { - if (from?.NetState == null || instance == null) - { - return; - } + m_Instance = instance; + m_CloseGumps = closeGumps; if (closeGumps) { - BaseMLQuestGump.CloseOtherGumps(instance.Player); + BaseQuestGump.CloseOtherGumps(instance.Player); } - from.SendGump(new QuestCancelConfirmGump(instance, closeGumps)); - } + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); + Closable = false; - builder.AddPage(); + AddImageTiled(0, 0, 348, 262, 0xA8E); + AddAlphaRegion(0, 0, 348, 262); - builder.AddImageTiled(0, 0, 348, 262, 0xA8E); - builder.AddAlphaRegion(0, 0, 348, 262); + AddImage(0, 15, 0x27A8); + AddImageTiled(0, 30, 17, 200, 0x27A7); + AddImage(0, 230, 0x27AA); - builder.AddImage(0, 15, 0x27A8); - builder.AddImageTiled(0, 30, 17, 200, 0x27A7); - builder.AddImage(0, 230, 0x27AA); + AddImage(15, 0, 0x280C); + AddImageTiled(30, 0, 300, 17, 0x280A); + AddImage(315, 0, 0x280E); - builder.AddImage(15, 0, 0x280C); - builder.AddImageTiled(30, 0, 300, 17, 0x280A); - builder.AddImage(315, 0, 0x280E); + AddImage(15, 244, 0x280C); + AddImageTiled(30, 244, 300, 17, 0x280A); + AddImage(315, 244, 0x280E); - builder.AddImage(15, 244, 0x280C); - builder.AddImageTiled(30, 244, 300, 17, 0x280A); - builder.AddImage(315, 244, 0x280E); + AddImage(330, 15, 0x27A8); + AddImageTiled(330, 30, 17, 200, 0x27A7); + AddImage(330, 230, 0x27AA); - builder.AddImage(330, 15, 0x27A8); - builder.AddImageTiled(330, 30, 17, 200, 0x27A7); - builder.AddImage(330, 230, 0x27AA); + AddImage(333, 2, 0x2716); + AddImage(333, 248, 0x2716); + AddImage(2, 248, 0x2716); + AddImage(2, 2, 0x2716); - builder.AddImage(333, 2, 0x2716); - builder.AddImage(333, 248, 0x2716); - builder.AddImage(2, 248, 0x2716); - builder.AddImage(2, 2, 0x2716); - - builder.AddHtmlLocalized(25, 22, 200, 20, 1049000, 0x7D00); // Confirm Quest Cancellation - builder.AddImage(25, 40, 0xBBF); + AddHtmlLocalized(25, 22, 200, 20, 1049000, 0x7D00); // Confirm Quest Cancellation + AddImage(25, 40, 0xBBF); /* * This quest will give you valuable information, skills @@ -71,28 +57,28 @@ namespace Server.Engines.MLQuests.Gumps *
* Are you certain you wish to cancel at this time? */ - builder.AddHtmlLocalized(25, 55, 300, 120, 1060836, 0x7FFF); + AddHtmlLocalized(25, 55, 300, 120, 1060836, 0x7FFF); - var quest = _instance.Quest; + var quest = instance.Quest; if (quest.IsChainTriggered || quest.NextQuest != null) { - builder.AddRadio(25, 145, 0x25F8, 0x25FB, false, 2); - builder.AddHtmlLocalized(60, 150, 280, 20, 1075023, 0x7FFF); // Yes, I want to quit this entire chain! + AddRadio(25, 145, 0x25F8, 0x25FB, false, 2); + AddHtmlLocalized(60, 150, 280, 20, 1075023, 0x7FFF); // Yes, I want to quit this entire chain! } - builder.AddRadio(25, 180, 0x25F8, 0x25FB, true, 1); - builder.AddHtmlLocalized(60, 185, 280, 20, 1049005, 0x7FFF); // Yes, I really want to quit this quest! + AddRadio(25, 180, 0x25F8, 0x25FB, true, 1); + AddHtmlLocalized(60, 185, 280, 20, 1049005, 0x7FFF); // Yes, I really want to quit this quest! - builder.AddRadio(25, 215, 0x25F8, 0x25FB, false, 0); - builder.AddHtmlLocalized(60, 220, 280, 20, 1049006, 0x7FFF); // No, I don't want to quit. + AddRadio(25, 215, 0x25F8, 0x25FB, false, 0); + AddHtmlLocalized(60, 220, 280, 20, 1049006, 0x7FFF); // No, I don't want to quit. - builder.AddButton(265, 220, 0xF7, 0xF8, 7); + AddButton(265, 220, 0xF7, 0xF8, 7); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_instance.Removed) + if (m_Instance.Removed) { return; } @@ -103,14 +89,14 @@ namespace Server.Engines.MLQuests.Gumps { if (info.IsSwitched(2)) { - _instance.Cancel(true); + m_Instance.Cancel(true); } else if (info.IsSwitched(1)) { - _instance.Cancel(false); + m_Instance.Cancel(false); } - QuestLogGump.DisplayTo(sender.Mobile, _instance.Player, _closeGumps); + sender.Mobile.SendGump(new QuestLogGump(m_Instance.Player, m_CloseGumps)); break; } } diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/QuestConversationGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/QuestConversationGump.cs index 9454c2d4a..9004264a3 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/QuestConversationGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/QuestConversationGump.cs @@ -1,41 +1,21 @@ -using Server.Gumps; using Server.Mobiles; namespace Server.Engines.MLQuests.Gumps { - public class QuestConversationGump : BaseMLQuestGump + public class QuestConversationGump : BaseQuestGump { - private readonly TextDefinition _text; - - public override bool Singleton => true; - - private QuestConversationGump(MLQuest quest, TextDefinition text) + public QuestConversationGump(MLQuest quest, PlayerMobile pm, TextDefinition text) : base(3006156) // Quest Conversation { - _text = text; + CloseOtherGumps(pm); SetTitle(quest.Title); RegisterButton(ButtonPosition.Right, ButtonGraphic.Close, 3); SetPageCount(1); - } - public static void DisplayTo(PlayerMobile pm, MLQuest quest, TextDefinition text) - { - if (pm?.NetState == null || quest == null) - { - return; - } - - CloseOtherGumps(pm); - - pm.SendGump(new QuestConversationGump(quest, text)); - } - - protected override void BuildContent(ref DynamicGumpBuilder builder) - { - BuildPage(ref builder); - AddConversation(ref builder, _text); + BuildPage(); + AddConversation(text); } } } diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/QuestLogDetailedGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/QuestLogDetailedGump.cs index b3c6f5c51..b4f0da864 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/QuestLogDetailedGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/QuestLogDetailedGump.cs @@ -3,63 +3,51 @@ using Server.Network; namespace Server.Engines.MLQuests.Gumps { - public class QuestLogDetailedGump : BaseMLQuestGump + public class QuestLogDetailedGump : BaseQuestGump { - private readonly bool _closeGumps; - private readonly MLQuestInstance _instance; + private readonly bool m_CloseGumps; + private readonly MLQuestInstance m_Instance; public override bool Singleton => true; - private QuestLogDetailedGump(MLQuestInstance instance, bool closeGumps) + public QuestLogDetailedGump(MLQuestInstance instance, bool closeGumps = true) : base(1046026) // Quest Log { - _instance = instance; - _closeGumps = closeGumps; + m_Instance = instance; + m_CloseGumps = closeGumps; - SetTitle(instance.Quest.Title); + var pm = instance.Player; + var quest = instance.Quest; + + if (closeGumps) + { + CloseOtherGumps(pm); + } + + SetTitle(quest.Title); RegisterButton(ButtonPosition.Left, ButtonGraphic.Resign, 1); RegisterButton(ButtonPosition.Right, ButtonGraphic.Okay, 2); SetPageCount(3); - } - public static void DisplayTo(Mobile from, MLQuestInstance instance, bool closeGumps = true) - { - if (from?.NetState == null || instance == null) + BuildPage(); + AddDescription(quest); + + if (instance.Failed) // only displayed on the first page { - return; + AddHtmlLocalized(160, 80, 250, 16, 500039, 0x3C00); // Failed! } - if (closeGumps) - { - CloseOtherGumps(instance.Player); - } + BuildPage(); + AddObjectivesProgress(instance); - from.SendGump(new QuestLogDetailedGump(instance, closeGumps)); - } - - protected override void BuildContent(ref DynamicGumpBuilder builder) - { - var quest = _instance.Quest; - - BuildPage(ref builder); - AddDescription(ref builder, quest); - - if (_instance.Failed) // only displayed on the first page - { - builder.AddHtmlLocalized(160, 80, 250, 16, 500039, 0x3C00); // Failed! - } - - BuildPage(ref builder); - AddObjectivesProgress(ref builder, _instance); - - BuildPage(ref builder); - AddRewardsPage(ref builder, quest); + BuildPage(); + AddRewardsPage(quest); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_instance.Removed) + if (m_Instance.Removed) { return; } @@ -69,17 +57,17 @@ namespace Server.Engines.MLQuests.Gumps case 1: // Resign { // TODO: Custom reward loss protection? OSI doesn't have this - // if (_instance.ClaimReward) + // if (m_Instance.ClaimReward) // pm.SendMessage( "You cannot cancel a quest with rewards pending." ); // else - QuestCancelConfirmGump.DisplayTo(sender.Mobile, _instance, _closeGumps); + sender.Mobile.SendGump(new QuestCancelConfirmGump(m_Instance, m_CloseGumps)); break; } case 2: // Okay { - QuestLogGump.DisplayTo(sender.Mobile, _instance.Player, _closeGumps); + sender.Mobile.SendGump(new QuestLogGump(m_Instance.Player, m_CloseGumps)); break; } diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/QuestLogGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/QuestLogGump.cs index 3dc5f9b6a..6927ce544 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/QuestLogGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/QuestLogGump.cs @@ -4,57 +4,33 @@ using Server.Network; namespace Server.Engines.MLQuests.Gumps { - public class QuestLogGump : BaseMLQuestGump + public class QuestLogGump : BaseQuestGump { - private readonly bool _closeGumps; - private readonly PlayerMobile _owner; + private readonly bool m_CloseGumps; + private readonly PlayerMobile m_Owner; public override bool Singleton => true; - private QuestLogGump(PlayerMobile pm, bool closeGumps) + public QuestLogGump(PlayerMobile pm, bool closeGumps = true) : base(1046026) // Quest Log { - _owner = pm; - _closeGumps = closeGumps; - - RegisterButton(ButtonPosition.Right, ButtonGraphic.Okay, 3); - - SetPageCount(1); - } - - public static void DisplayTo(Mobile from, PlayerMobile pm, bool closeGumps = true) - { - if (from?.NetState == null || pm == null) - { - return; - } + m_Owner = pm; + m_CloseGumps = closeGumps; if (closeGumps) { pm.CloseGump(); } - from.SendGump(new QuestLogGump(pm, closeGumps)); - } + RegisterButton(ButtonPosition.Right, ButtonGraphic.Okay, 3); - public static void DisplayTo(PlayerMobile pm) - { - if (pm?.NetState == null) - { - return; - } + SetPageCount(1); - pm.CloseGump(); - pm.SendGump(new QuestLogGump(pm, true)); - } - - protected override void BuildContent(ref DynamicGumpBuilder builder) - { - BuildPage(ref builder); + BuildPage(); int numberColor, stringColor; - var context = MLQuestSystem.GetContext(_owner); + var context = MLQuestSystem.GetContext(pm); if (context != null) { @@ -73,7 +49,7 @@ namespace Server.Engines.MLQuests.Gumps } instances[i].Quest.Title.AddHtmlText( - ref builder, + this, 98, 140 + 21 * i, 270, @@ -83,7 +59,7 @@ namespace Server.Engines.MLQuests.Gumps numberColor, stringColor ); - builder.AddButton(368, 140 + 21 * i, 0x26B0, 0x26B1, 6 + i, GumpButtonType.Reply, 1); + AddButton(368, 140 + 21 * i, 0x26B0, 0x26B1, 6 + i, GumpButtonType.Reply, 1); } } } @@ -95,7 +71,7 @@ namespace Server.Engines.MLQuests.Gumps return; } - var context = MLQuestSystem.GetContext(_owner); + var context = MLQuestSystem.GetContext(m_Owner); if (context == null) { @@ -110,7 +86,7 @@ namespace Server.Engines.MLQuests.Gumps return; } - QuestLogDetailedGump.DisplayTo(sender.Mobile, instances[index], _closeGumps); + sender.Mobile.SendGump(new QuestLogDetailedGump(instances[index], m_CloseGumps)); } } } diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/QuestOfferGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/QuestOfferGump.cs index 7d984efdb..76314649f 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/QuestOfferGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/QuestOfferGump.cs @@ -4,48 +4,35 @@ using Server.Network; namespace Server.Engines.MLQuests.Gumps { - public class QuestOfferGump : BaseMLQuestGump + public class QuestOfferGump : BaseQuestGump { - private readonly MLQuest _quest; - private readonly IQuestGiver _quester; + private readonly MLQuest m_Quest; + private readonly IQuestGiver m_Quester; public override bool Singleton => true; - private QuestOfferGump(MLQuest quest, IQuestGiver quester) + public QuestOfferGump(MLQuest quest, IQuestGiver quester, PlayerMobile pm) : base(1049010) // Quest Offer { - _quest = quest; - _quester = quester; + m_Quest = quest; + m_Quester = quester; + + CloseOtherGumps(pm); SetTitle(quest.Title); RegisterButton(ButtonPosition.Left, ButtonGraphic.Accept, 1); RegisterButton(ButtonPosition.Right, ButtonGraphic.Refuse, 2); SetPageCount(3); - } - public static void DisplayTo(PlayerMobile pm, MLQuest quest, IQuestGiver quester) - { - if (pm?.NetState == null || quest == null) - { - return; - } + BuildPage(); + AddDescription(quest); - CloseOtherGumps(pm); + BuildPage(); + AddObjectives(quest); - pm.SendGump(new QuestOfferGump(quest, quester)); - } - - protected override void BuildContent(ref DynamicGumpBuilder builder) - { - BuildPage(ref builder); - AddDescription(ref builder, _quest); - - BuildPage(ref builder); - AddObjectives(ref builder, _quest); - - BuildPage(ref builder); - AddRewardsPage(ref builder, _quest); + BuildPage(); + AddRewardsPage(quest); } public override void OnResponse(NetState sender, in RelayInfo info) @@ -59,12 +46,12 @@ namespace Server.Engines.MLQuests.Gumps { case 1: // Accept { - _quest.OnAccept(_quester, pm); + m_Quest.OnAccept(m_Quester, pm); break; } case 2: // Refuse { - _quest.OnRefuse(_quester, pm); + m_Quest.OnRefuse(m_Quester, pm); break; } } diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/QuestReportBackGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/QuestReportBackGump.cs index b49df9d40..d80a5dd72 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/QuestReportBackGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/QuestReportBackGump.cs @@ -3,48 +3,36 @@ using Server.Network; namespace Server.Engines.MLQuests.Gumps { - public class QuestReportBackGump : BaseMLQuestGump + public class QuestReportBackGump : BaseQuestGump { - private readonly MLQuestInstance _instance; + private readonly MLQuestInstance m_Instance; - public override bool Singleton => true; - - private QuestReportBackGump(MLQuestInstance instance) + public QuestReportBackGump(MLQuestInstance instance) : base(3006156) // Quest Conversation { - _instance = instance; + m_Instance = instance; - SetTitle(instance.Quest.Title); + var quest = instance.Quest; + var pm = instance.Player; + + // TODO: Check close sequence + CloseOtherGumps(pm); + + SetTitle(quest.Title); RegisterButton(ButtonPosition.Left, ButtonGraphic.Continue, 4); RegisterButton(ButtonPosition.Right, ButtonGraphic.Close, 3); SetPageCount(1); - } - public static void DisplayTo(Mobile from, MLQuestInstance instance) - { - if (from?.NetState == null || instance == null) - { - return; - } - - // TODO: Check close sequence - CloseOtherGumps(instance.Player); - - from.SendGump(new QuestReportBackGump(instance)); - } - - protected override void BuildContent(ref DynamicGumpBuilder builder) - { - BuildPage(ref builder); - AddConversation(ref builder, _instance.Quest.CompletionMessage); + BuildPage(); + AddConversation(quest.CompletionMessage); } public override void OnResponse(NetState sender, in RelayInfo info) { if (info.ButtonID == 4) { - _instance.ContinueReportBack(true); + m_Instance.ContinueReportBack(true); } } } diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/QuestRewardGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/QuestRewardGump.cs index dbc4993a9..8e917d8a4 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/QuestRewardGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/QuestRewardGump.cs @@ -3,46 +3,34 @@ using Server.Network; namespace Server.Engines.MLQuests.Gumps { - public class QuestRewardGump : BaseMLQuestGump + public class QuestRewardGump : BaseQuestGump { - private readonly MLQuestInstance _instance; + private readonly MLQuestInstance m_Instance; - public override bool Singleton => true; - - private QuestRewardGump(MLQuestInstance instance) + public QuestRewardGump(MLQuestInstance instance) : base(1072201) // Reward { - _instance = instance; + m_Instance = instance; - SetTitle(instance.Quest.Title); + var quest = instance.Quest; + var pm = instance.Player; + + CloseOtherGumps(pm); + + SetTitle(quest.Title); RegisterButton(ButtonPosition.Left, ButtonGraphic.Accept, 1); SetPageCount(1); - } - public static void DisplayTo(Mobile from, MLQuestInstance instance) - { - if (from?.NetState == null || instance == null) - { - return; - } - - CloseOtherGumps(instance.Player); - - from.SendGump(new QuestRewardGump(instance)); - } - - protected override void BuildContent(ref DynamicGumpBuilder builder) - { - BuildPage(ref builder); - AddRewards(ref builder, _instance.Quest); + BuildPage(); + AddRewards(quest); } public override void OnResponse(NetState sender, in RelayInfo info) { if (info.ButtonID == 1) { - _instance.ClaimRewards(); + m_Instance.ClaimRewards(); } } } diff --git a/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs b/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs index a0de2461d..42d161a8a 100644 --- a/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs +++ b/Projects/UOContent/Engines/ML Quests/Gumps/RaceChangeGump.cs @@ -19,70 +19,58 @@ namespace Server.Engines.MLQuests.Gumps void OnCancel(PlayerMobile from); } - public class RaceChangeConfirmGump : DynamicGump + public class RaceChangeConfirmGump : Gump { private static Dictionary m_Pending; + private readonly PlayerMobile m_From; - private readonly IRaceChanger _owner; - private readonly Race _race; + private readonly IRaceChanger m_Owner; + private readonly Race m_Race; public override bool Singleton => true; - private RaceChangeConfirmGump(IRaceChanger owner, Race targetRace) : base(50, 50) + public RaceChangeConfirmGump(IRaceChanger owner, PlayerMobile from, Race targetRace) + : base(50, 50) { - _owner = owner; - _race = targetRace; - } + m_Owner = owner; + m_From = from; + m_Race = targetRace; - public static void DisplayTo(PlayerMobile from, IRaceChanger owner, Race targetRace) - { - if (from?.NetState == null || targetRace == null) + AddPage(0); + AddBackground(0, 0, 240, 135, 0x2422); + + if (targetRace == Race.Human) { - return; + AddHtmlLocalized(15, 15, 210, 75, 1073643, 0); // Are you sure you wish to embrace your humanity? } - - from.SendGump(new RaceChangeConfirmGump(owner, targetRace)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 240, 135, 0x2422); - - if (_race == Race.Human) + else if (targetRace == Race.Elf) { - builder.AddHtmlLocalized(15, 15, 210, 75, 1073643, 0); // Are you sure you wish to embrace your humanity? - } - else if (_race == Race.Elf) - { - builder.AddHtmlLocalized(15, 15, 210, 75, 1073642, 0); // Are you sure you want to follow the elven ways? + AddHtmlLocalized(15, 15, 210, 75, 1073642, 0); // Are you sure you want to follow the elven ways? } else { - builder.AddHtml(15, 15, 210, 75, $"Are you sure you want to change your race to {_race.Name}?"); + AddHtml(15, 15, 210, 75, $"Are you sure you want to change your race to {targetRace.Name}?"); } - builder.AddButton(160, 95, 0xF7, 0xF8, 1); - builder.AddButton(90, 95, 0xF2, 0xF1, 0); + AddButton(160, 95, 0xF7, 0xF8, 1); + AddButton(90, 95, 0xF2, 0xF1, 0); } public override void OnResponse(NetState sender, in RelayInfo info) { - var from = sender.Mobile; - switch (info.ButtonID) { case 0: // Cancel { - _owner?.OnCancel((PlayerMobile)from); + m_Owner?.OnCancel(m_From); break; } case 1: // Okay { - if (_owner?.CheckComplete((PlayerMobile)from) != false) + if (m_Owner?.CheckComplete(m_From) != false) { - Offer(_owner, (PlayerMobile)from, _race); + Offer(m_Owner, m_From, m_Race); } break; @@ -116,9 +104,10 @@ namespace Server.Engines.MLQuests.Gumps private static void CloseCurrent(NetState ns) { - if (m_Pending.Remove(ns, out var state)) + if (m_Pending.TryGetValue(ns, out var state)) { state._timeoutToken.Cancel(); + m_Pending.Remove(ns); } ns.SendCloseRaceChanger(); @@ -340,7 +329,7 @@ namespace Server.Engines.MLQuests.Gumps if (CheckComplete(pm)) { - RaceChangeConfirmGump.DisplayTo(pm, this, pm.Race == Race.Human ? Race.Elf : Race.Human); + pm.SendGump(new RaceChangeConfirmGump(this, pm, pm.Race == Race.Human ? Race.Elf : Race.Human)); } } } diff --git a/Projects/UOContent/Engines/ML Quests/Items/PrismaticCrystal.cs b/Projects/UOContent/Engines/ML Quests/Items/PrismaticCrystal.cs index ed5da7386..8559e3cc8 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/PrismaticCrystal.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/PrismaticCrystal.cs @@ -24,30 +24,32 @@ public partial class PrismaticCrystal : Item return; } - if (!pm.InRange(GetWorldLocation(), 2)) + if (pm.InRange(GetWorldLocation(), 2)) { - pm.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } + if (MLQuestSystem.GetContext(pm)?.IsDoingQuest(typeof(UnfadingMemoriesPartOne)) == true && + pm.Backpack.FindItemByType(false) == null) + { + Item amber = new PrismaticAmber(); - if (MLQuestSystem.GetContext(pm)?.IsDoingQuest(typeof(UnfadingMemoriesPartOne)) != true || - pm.Backpack.FindItemByType(false) != null) - { - pm.SendLocalizedMessage(1075464); // You already have as many of those as you need. - return; - } - - var amber = new PrismaticAmber(); - - if (pm.PlaceInBackpack(amber)) - { - MLQuestSystem.MarkQuestItem(pm, amber); - Delete(); + if (pm.PlaceInBackpack(amber)) + { + MLQuestSystem.MarkQuestItem(pm, amber); + Delete(); + } + else + { + pm.SendLocalizedMessage(502385); // Your pack cannot hold this item. + amber.Delete(); + } + } + else + { + pm.SendLocalizedMessage(1075464); // You already have as many of those as you need. + } } else { - pm.SendLocalizedMessage(502385); // Your pack cannot hold this item. - amber.Delete(); + pm.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } } diff --git a/Projects/UOContent/Engines/ML Quests/Items/RewardBags.cs b/Projects/UOContent/Engines/ML Quests/Items/RewardBags.cs index 2adeef397..a5b743379 100644 --- a/Projects/UOContent/Engines/ML Quests/Items/RewardBags.cs +++ b/Projects/UOContent/Engines/ML Quests/Items/RewardBags.cs @@ -25,7 +25,7 @@ public static class RewardBag 1 => Loot.RandomArmor(false, true), 2 => Loot.RandomRangedWeapon(false, true), 3 => Loot.RandomJewelry(), - _ => Loot.RandomHat() // 4 + _ => Loot.RandomHat(false) // 4 }; if (loot == null) diff --git a/Projects/UOContent/Engines/ML Quests/MLQuest.cs b/Projects/UOContent/Engines/ML Quests/MLQuest.cs index 4dde39f6c..b0ec81a0d 100644 --- a/Projects/UOContent/Engines/ML Quests/MLQuest.cs +++ b/Projects/UOContent/Engines/ML Quests/MLQuest.cs @@ -5,6 +5,7 @@ using Server.Engines.MLQuests.Gumps; using Server.Engines.MLQuests.Objectives; using Server.Engines.MLQuests.Rewards; using Server.Engines.Spawners; +using Server.Gumps; using Server.Mobiles; namespace Server.Engines.MLQuests @@ -183,7 +184,7 @@ namespace Server.Engines.MLQuests public virtual void SendOffer(IQuestGiver quester, PlayerMobile pm) { - QuestOfferGump.DisplayTo(pm, this, quester); + pm.SendGump(new QuestOfferGump(this, quester, pm)); } public virtual void OnAccept(IQuestGiver quester, PlayerMobile pm) @@ -212,7 +213,7 @@ namespace Server.Engines.MLQuests public virtual void OnRefuse(IQuestGiver quester, PlayerMobile pm) { - QuestConversationGump.DisplayTo(pm, this, RefusalMessage); + pm.SendGump(new QuestConversationGump(this, pm, RefusalMessage)); } public virtual void GetRewards(MLQuestInstance instance) diff --git a/Projects/UOContent/Engines/ML Quests/MLQuestEntry.cs b/Projects/UOContent/Engines/ML Quests/MLQuestEntry.cs index abb108999..167780444 100644 --- a/Projects/UOContent/Engines/ML Quests/MLQuestEntry.cs +++ b/Projects/UOContent/Engines/ML Quests/MLQuestEntry.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Server.Engines.MLQuests.Gumps; using Server.Engines.MLQuests.Objectives; +using Server.Gumps; using Server.Mobiles; namespace Server.Engines.MLQuests @@ -233,7 +234,7 @@ namespace Server.Engines.MLQuests public void SendProgressGump() { - QuestConversationGump.DisplayTo(Player, Quest, Quest.InProgressMessage); + Player.SendGump(new QuestConversationGump(Quest, Player, Quest.InProgressMessage)); } public void SendRewardOffer() @@ -259,7 +260,7 @@ namespace Server.Engines.MLQuests } else { - QuestRewardGump.DisplayTo(Player, this); + Player.SendGump(new QuestRewardGump(this)); } } @@ -271,7 +272,7 @@ namespace Server.Engines.MLQuests } else { - QuestReportBackGump.DisplayTo(Player, this); + Player.SendGump(new QuestReportBackGump(this)); } } diff --git a/Projects/UOContent/Engines/ML Quests/MLQuestPackets.cs b/Projects/UOContent/Engines/ML Quests/MLQuestPackets.cs index 5f2f0f9d3..b33d782d5 100644 --- a/Projects/UOContent/Engines/ML Quests/MLQuestPackets.cs +++ b/Projects/UOContent/Engines/ML Quests/MLQuestPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MLQuestPackets.cs * * * diff --git a/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs b/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs index d3e39c09f..5d846d595 100644 --- a/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs +++ b/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs @@ -19,7 +19,7 @@ namespace Server.Engines.MLQuests { base.Serialize(writer); - writer.Write(3); // version + writer.Write(2); // version writer.Write(MLQuestSystem.Contexts.Count); foreach (var context in MLQuestSystem.Contexts.Values) diff --git a/Projects/UOContent/Engines/ML Quests/MLQuestSystem.cs b/Projects/UOContent/Engines/ML Quests/MLQuestSystem.cs index edde59c32..cfe129675 100644 --- a/Projects/UOContent/Engines/ML Quests/MLQuestSystem.cs +++ b/Projects/UOContent/Engines/ML Quests/MLQuestSystem.cs @@ -263,11 +263,11 @@ namespace Server.Engines.MLQuests if (enable) { - m.SendMessage("Serialization for all quests is now enabled."); + m.SendMessage($"Serialization for all quests is now enabled."); } else { - m.SendMessage("Serialization for all quests is now disabled."); + m.SendMessage($"Serialization for all quests is now disabled."); } if (AutoGenerateNew && !enable) @@ -657,7 +657,7 @@ namespace Server.Engines.MLQuests return; } - QuestLogGump.DisplayTo(pm); + pm.SendGump(new QuestLogGump(pm)); } public static MLQuest RandomStarterQuest(IQuestGiver quester, PlayerMobile pm, MLQuestContext context) @@ -730,7 +730,7 @@ namespace Server.Engines.MLQuests } else if (quester is Item item) { - item.SendLocalizedMessageTo(pm, cliloc, SpeechColor, args); + item.SendLocalizedMessageTo(pm, cliloc, args, SpeechColor); } else { @@ -833,7 +833,7 @@ namespace Server.Engines.MLQuests from, $"{from.AccessLevel} {CommandLogging.Format(from)} viewing quest overview of {CommandLogging.Format(pm)}" ); - QuestLogGump.DisplayTo(from, pm, false); + from.SendGump(new QuestLogGump(pm, false)); } } diff --git a/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs b/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs index c2e8c29a2..6dd4e4782 100644 --- a/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs +++ b/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs @@ -118,7 +118,8 @@ public abstract partial class DoneQuestCollector : BaseCreature, IRaceChanger } else { - List conversation = [..Incomplete]; + var conversation = new List(); + conversation.AddRange(Incomplete); var context = MLQuestSystem.GetContext(pm); @@ -306,7 +307,7 @@ public partial class Darius : DoneQuestCollector public override void OnComplete(PlayerMobile from) { - RaceChangeConfirmGump.DisplayTo(from, this, Race.Elf); + from.SendGump(new RaceChangeConfirmGump(this, from, Race.Elf)); } } @@ -382,6 +383,6 @@ public partial class Nedrick : DoneQuestCollector public override void OnComplete(PlayerMobile from) { - RaceChangeConfirmGump.DisplayTo(from, this, Race.Human); + from.SendGump(new RaceChangeConfirmGump(this, from, Race.Human)); } } diff --git a/Projects/UOContent/Engines/ML Quests/Mobiles/SirHelper.cs b/Projects/UOContent/Engines/ML Quests/Mobiles/SirHelper.cs index 024eb5704..87f69716a 100644 --- a/Projects/UOContent/Engines/ML Quests/Mobiles/SirHelper.cs +++ b/Projects/UOContent/Engines/ML Quests/Mobiles/SirHelper.cs @@ -1,128 +1,144 @@ using System; -using ModernUO.Serialization; using Server.Engines.MLQuests.Gumps; +using Server.Gumps; using Server.Items; using Server.Mobiles; using Server.Network; -namespace Server.Engines.MLQuests.Mobiles; - -[SerializationGenerator(0)] -public partial class SirHelper : Mage +namespace Server.Engines.MLQuests.Mobiles { - private static readonly TimeSpan m_ShoutDelay = TimeSpan.FromSeconds(20); - - private static readonly TimeSpan - m_ShoutCooldown = TimeSpan.FromDays(1); // TODO: Verify, could be a lot longer... or until a restart even - - private DateTime m_NextShout; - - [Constructible] - public SirHelper() + public class SirHelper : Mage { - Title = "the Profession Guide"; // TODO: Don't display in paperdoll + private static readonly Gump m_Gump = new InfoNPCGump(1078029, 1078028); + private static readonly TimeSpan m_ShoutDelay = TimeSpan.FromSeconds(20); - Hue = 0x83EA; + private static readonly TimeSpan + m_ShoutCooldown = TimeSpan.FromDays(1); // TODO: Verify, could be a lot longer... or until a restart even - Direction = Direction.South; - Frozen = true; - } + private DateTime m_NextShout; - public override string DefaultName => "Sir Helper"; - public override bool IsActiveVendor => false; - - public override void InitSBInfo() - { - } - - public override bool GetGender() => false; - - public override void CheckMorph() - { - } - - public override void InitOutfit() - { - HairItemID = 0x203C; - FacialHairItemID = 0x204D; - HairHue = FacialHairHue = 0x8A7; - - AddItem(new Sandals()); - - AddItem(new Cloak + [Constructible] + public SirHelper() { - ItemID = 0x26AD, - Hue = 0x455 - }); + Title = "the Profession Guide"; // TODO: Don't display in paperdoll - AddItem(new Robe - { - ItemID = 0x26AE, - Hue = 0x4AB - }); + Hue = 0x83EA; - AddItem(new Backpack - { - Movable = false - }); - } - - public override void OnDoubleClick(Mobile from) - { - if (from.BeginAction(this)) - { - Timer.StartTimer(m_ShoutCooldown, () => EndLock(from)); + Direction = Direction.South; + Frozen = true; } - MLQuestSystem.TurnToFace(this, from); - InfoNPCGump.DisplayTo(from, 1078029, 1078028); - - // Paperdoll doesn't open - // base.OnDoubleClick( from ); - } - - public override void OnThink() - { - base.OnThink(); - - if (m_NextShout > Core.Now) + public SirHelper(Serial serial) + : base(serial) { - return; } - var buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedLength("")].InitializePacket(); + public override string DefaultName => "Sir Helper"; + public override bool IsActiveVendor => false; - foreach (var state in GetClientsInRange(12)) + public override void InitSBInfo() { - var m = state.Mobile; + } - if (m.CanSee(this) && m.InLOS(this) && m.CanBeginAction(this)) + public override bool GetGender() => false; + + public override void CheckMorph() + { + } + + public override void InitOutfit() + { + HairItemID = 0x203C; + FacialHairItemID = 0x204D; + HairHue = FacialHairHue = 0x8A7; + + AddItem(new Sandals()); + + Item item; + + item = new Cloak(); + item.ItemID = 0x26AD; + item.Hue = 0x455; + AddItem(item); + + item = new Robe(); + item.ItemID = 0x26AE; + item.Hue = 0x4AB; + AddItem(item); + + item = new Backpack(); + item.Movable = false; + AddItem(item); + } + + public override void OnDoubleClick(Mobile from) + { + if (from.CanBeginAction(this)) { - // Double Click On Me For Help! - var length = OutgoingMessagePackets.CreateMessageLocalized( - buffer, Serial, Body, MessageType.Regular, 946, 3, 1078099, Name - ); - - if (length != buffer.Length) - { - buffer = buffer[..length]; - } - - state.Send(buffer); + from.BeginAction(this); + Timer.StartTimer(m_ShoutCooldown, () => EndLock(from)); } + + MLQuestSystem.TurnToFace(this, from); + from.SendGump(m_Gump); + + // Paperdoll doesn't open + // base.OnDoubleClick( from ); } - m_NextShout = Core.Now + m_ShoutDelay; - } + public override void OnThink() + { + base.OnThink(); - private void EndLock(Mobile m) - { - m.EndAction(this); - } + if (m_NextShout > Core.Now) + { + return; + } - [AfterDeserialization] - private void AfterDeserialization() - { - Frozen = true; + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedLength("")].InitializePacket(); + + foreach (var state in GetClientsInRange(12)) + { + var m = state.Mobile; + + if (m.CanSee(this) && m.InLOS(this) && m.CanBeginAction(this)) + { + // Double Click On Me For Help! + var length = OutgoingMessagePackets.CreateMessageLocalized( + buffer, Serial, Body, MessageType.Regular, 946, 3, 1078099, Name + ); + + if (length != buffer.Length) + { + buffer = buffer[..length]; + } + + state.Send(buffer); + } + } + + m_NextShout = Core.Now + m_ShoutDelay; + } + + private void EndLock(Mobile m) + { + m.EndAction(this); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + Frozen = true; + } } } diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs index 77c3350f5..d658f0d5e 100644 --- a/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs +++ b/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs @@ -11,7 +11,7 @@ namespace Server.Engines.MLQuests.Objectives public virtual bool CanOffer(IQuestGiver quester, PlayerMobile pm, bool message) => true; - public abstract void WriteToGump(ref DynamicGumpBuilder builder, ref int y); + public abstract void WriteToGump(Gump g, ref int y); public virtual BaseObjectiveInstance CreateInstance(MLQuestInstance instance) => null; } @@ -46,18 +46,18 @@ namespace Server.Engines.MLQuests.Objectives public virtual DataType ExtraDataType => DataType.None; - public virtual void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public virtual void WriteToGump(Gump g, ref int y) { if (IsTimed) { - WriteTimeRemaining(ref builder, ref y, Utility.Max(EndTime - Core.Now, TimeSpan.Zero)); + WriteTimeRemaining(g, ref y, Utility.Max(EndTime - Core.Now, TimeSpan.Zero)); } } - public static void WriteTimeRemaining(ref DynamicGumpBuilder builder, ref int y, TimeSpan timeRemaining) + public static void WriteTimeRemaining(Gump g, ref int y, TimeSpan timeRemaining) { - builder.AddHtmlLocalized(103, y, 120, 16, 1062379, 0x5F90); // Est. time remaining: - builder.AddLabel(223, y, 0x481, $"{timeRemaining.TotalSeconds:F0}"); + g.AddHtmlLocalized(103, y, 120, 16, 1062379, 0x5F90); // Est. time remaining: + g.AddLabel(223, y, 0x481, timeRemaining.TotalSeconds.ToString("F0")); y += 16; } @@ -119,7 +119,7 @@ namespace Server.Engines.MLQuests.Objectives if (IsTimed) { writer.Write(true); - writer.WriteAnchoredTime(EndTime); + writer.WriteDeltaTime(EndTime); } else { @@ -135,7 +135,7 @@ namespace Server.Engines.MLQuests.Objectives { if (reader.ReadBool()) { - var endTime = version >= 3 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime(); + var endTime = reader.ReadDeltaTime(); if (objInstance != null) { diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/CollectObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/CollectObjective.cs index 2b882f4ee..5f3f23904 100644 --- a/Projects/UOContent/Engines/ML Quests/Objectives/CollectObjective.cs +++ b/Projects/UOContent/Engines/ML Quests/Objectives/CollectObjective.cs @@ -44,34 +44,34 @@ namespace Server.Engines.MLQuests.Objectives return label - 1078872; } - public override void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public override void WriteToGump(Gump g, ref int y) { if (ShowDetailed) { var amount = DesiredAmount.ToString(); - builder.AddHtmlLocalized(98, y, 350, 16, 1072205, 0x5F90); // Obtain - builder.AddLabel(143, y, 0x481, amount); + g.AddHtmlLocalized(98, y, 350, 16, 1072205, 0x5F90); // Obtain + g.AddLabel(143, y, 0x481, amount); if (Name.Number > 0) { - builder.AddHtmlLocalized(143 + amount.Length * 15, y, 190, 18, Name.Number, 0x77BF); - builder.AddItem(350, y, LabelToItemID(Name.Number)); + g.AddHtmlLocalized(143 + amount.Length * 15, y, 190, 18, Name.Number, 0x77BF); + g.AddItem(350, y, LabelToItemID(Name.Number)); } else if (Name.String != null) { - builder.AddLabel(143 + amount.Length * 15, y, 0x481, Name.String); + g.AddLabel(143 + amount.Length * 15, y, 0x481, Name.String); } } else { if (Name.Number > 0) { - builder.AddHtmlLocalized(98, y, 312, 32, Name.Number, 0x5F90); + g.AddHtmlLocalized(98, y, 312, 32, Name.Number, 0x5F90); } else if (Name.String != null) { - builder.AddLabel(98, y, 0x481, Name.String); + g.AddLabel(98, y, 0x481, Name.String); } } @@ -163,8 +163,7 @@ namespace Server.Engines.MLQuests.Objectives var left = Objective.DesiredAmount; - using var queue = pack.EnumerateItemsByType(false, ClaimTypePredicate); - foreach (var item in queue) + foreach (var item in pack.EnumerateItemsByType(false, ClaimTypePredicate)) { if (item.QuestItem && Objective.CheckItem(item)) { @@ -199,21 +198,21 @@ namespace Server.Engines.MLQuests.Objectives // No message } - public override void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public override void WriteToGump(Gump g, ref int y) { - Objective.WriteToGump(ref builder, ref y); + Objective.WriteToGump(g, ref y); y -= 16; if (Objective.ShowDetailed) { - base.WriteToGump(ref builder, ref y); + base.WriteToGump(g, ref y); - builder.AddHtmlLocalized(103, y, 120, 16, 3000087, 0x5F90); // Total - builder.AddLabel(223, y, 0x481, $"{GetCurrentTotal()}"); + g.AddHtmlLocalized(103, y, 120, 16, 3000087, 0x5F90); // Total + g.AddLabel(223, y, 0x481, GetCurrentTotal().ToString()); y += 16; - builder.AddHtmlLocalized(103, y, 120, 16, 1074782, 0x5F90); // Return to - builder.AddLabel(223, y, 0x481, QuesterNameAttribute.GetQuesterNameFor(Instance.QuesterType)); + g.AddHtmlLocalized(103, y, 120, 16, 1074782, 0x5F90); // Return to + g.AddLabel(223, y, 0x481, QuesterNameAttribute.GetQuesterNameFor(Instance.QuesterType)); y += 16; } } diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs index b29f18a98..e8d689028 100644 --- a/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs +++ b/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs @@ -1,5 +1,5 @@ using System; -using Server.Collections; +using System.Collections.Generic; using Server.Gumps; using Server.Items; using Server.Logging; @@ -46,7 +46,7 @@ namespace Server.Engines.MLQuests.Objectives return; } - using var delivery = PooledRefQueue.Create(); + var delivery = new List(); for (var i = 0; i < Amount; ++i) { @@ -54,7 +54,7 @@ namespace Server.Engines.MLQuests.Objectives if (item != null) { - delivery.Enqueue(item); + delivery.Add(item); if (item.Stackable && Amount > 1) { @@ -64,33 +64,33 @@ namespace Server.Engines.MLQuests.Objectives } } - while (delivery.Count > 0) + foreach (var item in delivery) { - pack.DropItem(delivery.Dequeue()); // Confirmed: on OSI items are added even if your pack is full + pack.DropItem(item); // Confirmed: on OSI items are added even if your pack is full } } - public override void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public override void WriteToGump(Gump g, ref int y) { var amount = Amount.ToString(); - builder.AddHtmlLocalized(98, y, 312, 16, 1072207, 0x5F90); // Deliver - builder.AddLabel(143, y, 0x481, amount); + g.AddHtmlLocalized(98, y, 312, 16, 1072207, 0x5F90); // Deliver + g.AddLabel(143, y, 0x481, amount); if (Name.Number > 0) { - builder.AddHtmlLocalized(143 + amount.Length * 15, y, 190, 18, Name.Number, 0x77BF); - builder.AddItem(350, y, CollectObjective.LabelToItemID(Name.Number)); + g.AddHtmlLocalized(143 + amount.Length * 15, y, 190, 18, Name.Number, 0x77BF); + g.AddItem(350, y, CollectObjective.LabelToItemID(Name.Number)); } else if (Name.String != null) { - builder.AddLabel(143 + amount.Length * 15, y, 0x481, Name.String); + g.AddLabel(143 + amount.Length * 15, y, 0x481, Name.String); } y += 32; - builder.AddHtmlLocalized(103, y, 120, 16, 1072379, 0x5F90); // Deliver to - builder.AddLabel(223, y, 0x481, QuesterNameAttribute.GetQuesterNameFor(Destination)); + g.AddHtmlLocalized(103, y, 120, 16, 1072379, 0x5F90); // Deliver to + g.AddLabel(223, y, 0x481, QuesterNameAttribute.GetQuesterNameFor(Destination)); y += 16; } @@ -192,8 +192,7 @@ namespace Server.Engines.MLQuests.Objectives var left = Objective.Amount; - using var queue = pack.EnumerateItems(false, ClaimTypePredicate); - foreach (var item in queue) + foreach (var item in pack.EnumerateItems(false, ClaimTypePredicate)) { if (left == 0) { @@ -225,11 +224,11 @@ namespace Server.Engines.MLQuests.Objectives Instance.Player.SendLocalizedMessage(1074813); // You have failed to complete your delivery. } - public override void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public override void WriteToGump(Gump g, ref int y) { - Objective.WriteToGump(ref builder, ref y); + Objective.WriteToGump(g, ref y); - base.WriteToGump(ref builder, ref y); + base.WriteToGump(g, ref y); // No extra instance stuff printed for this objective } diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/EscortObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/EscortObjective.cs index 7e163db6c..6a62ecb01 100644 --- a/Projects/UOContent/Engines/ML Quests/Objectives/EscortObjective.cs +++ b/Projects/UOContent/Engines/ML Quests/Objectives/EscortObjective.cs @@ -63,17 +63,17 @@ namespace Server.Engines.MLQuests.Objectives return true; } - public override void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public override void WriteToGump(Gump g, ref int y) { - builder.AddHtmlLocalized(98, y, 312, 16, 1072206, 0x5F90); // Escort to + g.AddHtmlLocalized(98, y, 312, 16, 1072206, 0x5F90); // Escort to if (Destination.Name.Number > 0) { - builder.AddHtmlLocalized(173, y, 312, 20, Destination.Name.Number, 0x7FFF); + g.AddHtmlLocalized(173, y, 312, 20, Destination.Name.Number, 0x7FFF); } else if (Destination.Name.String != null) { - builder.AddLabel(173, y, 0x481, Destination.Name.String); + g.AddLabel(173, y, 0x481, Destination.Name.String); } y += 16; @@ -275,11 +275,11 @@ namespace Server.Engines.MLQuests.Objectives Abandon(); } - public override void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public override void WriteToGump(Gump g, ref int y) { - m_Objective.WriteToGump(ref builder, ref y); + m_Objective.WriteToGump(g, ref y); - base.WriteToGump(ref builder, ref y); + base.WriteToGump(g, ref y); // No extra instance stuff printed for this objective } diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/GainSkillObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/GainSkillObjective.cs index 4a0719129..19c51d466 100644 --- a/Projects/UOContent/Engines/ML Quests/Objectives/GainSkillObjective.cs +++ b/Projects/UOContent/Engines/ML Quests/Objectives/GainSkillObjective.cs @@ -68,12 +68,12 @@ namespace Server.Engines.MLQuests.Objectives return true; } - public override void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public override void WriteToGump(Gump g, ref int y) { var skillLabel = AosSkillBonuses.GetLabel(Skill); var args = $"#{skillLabel}\t{ThresholdFixed / 10.0:0.#}"; - builder.AddHtmlLocalized(98, y, 312, 16, 1077485, args, 0x5F90); // Increase ~1_SKILL~ to ~2_VALUE~ + g.AddHtmlLocalized(98, y, 312, 16, 1077485, args, 0x5F90); // Increase ~1_SKILL~ to ~2_VALUE~ y += 16; } @@ -150,15 +150,15 @@ namespace Server.Engines.MLQuests.Objectives OnQuestCancelled(); } - public override void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public override void WriteToGump(Gump g, ref int y) { - Objective.WriteToGump(ref builder, ref y); + Objective.WriteToGump(g, ref y); - base.WriteToGump(ref builder, ref y); + base.WriteToGump(g, ref y); if (IsCompleted()) { - builder.AddHtmlLocalized(113, y, 312, 20, 1055121, 0x7FFF); // Complete + g.AddHtmlLocalized(113, y, 312, 20, 1055121, 0x7FFF); // Complete y += 16; } } diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/KillObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/KillObjective.cs index 26147f4e7..ca57e58c4 100644 --- a/Projects/UOContent/Engines/ML Quests/Objectives/KillObjective.cs +++ b/Projects/UOContent/Engines/ML Quests/Objectives/KillObjective.cs @@ -23,35 +23,35 @@ namespace Server.Engines.MLQuests.Objectives public QuestArea Area { get; set; } - public override void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public override void WriteToGump(Gump g, ref int y) { var amount = DesiredAmount.ToString(); - builder.AddHtmlLocalized(98, y, 312, 16, 1072204, 0x5F90); // Slay - builder.AddLabel(133, y, 0x481, amount); + g.AddHtmlLocalized(98, y, 312, 16, 1072204, 0x5F90); // Slay + g.AddLabel(133, y, 0x481, amount); if (Name.Number > 0) { - builder.AddHtmlLocalized(133 + amount.Length * 15, y, 190, 18, Name.Number, 0x77BF); + g.AddHtmlLocalized(133 + amount.Length * 15, y, 190, 18, Name.Number, 0x77BF); } else if (Name.String != null) { - builder.AddLabel(133 + amount.Length * 15, y, 0x481, Name.String); + g.AddLabel(133 + amount.Length * 15, y, 0x481, Name.String); } y += 16; if (Area != null) { - builder.AddHtmlLocalized(103, y, 312, 20, 1018327, 0x5F90); // Location + g.AddHtmlLocalized(103, y, 312, 20, 1018327, 0x5F90); // Location if (Area.Name.Number > 0) { - builder.AddHtmlLocalized(223, y, 312, 20, Area.Name.Number, 0x7FFF); + g.AddHtmlLocalized(223, y, 312, 20, Area.Name.Number, 0x7FFF); } else if (Area.Name.String != null) { - builder.AddLabel(223, y, 0x481, Area.Name.String); + g.AddLabel(223, y, 0x481, Area.Name.String); } y += 16; @@ -121,18 +121,18 @@ namespace Server.Engines.MLQuests.Objectives public override bool IsCompleted() => Slain >= Objective.DesiredAmount; - public override void WriteToGump(ref DynamicGumpBuilder builder, ref int y) + public override void WriteToGump(Gump g, ref int y) { - Objective.WriteToGump(ref builder, ref y); + Objective.WriteToGump(g, ref y); - base.WriteToGump(ref builder, ref y); + base.WriteToGump(g, ref y); - builder.AddHtmlLocalized(103, y, 120, 16, 3000087, 0x5F90); // Total - builder.AddLabel(223, y, 0x481, $"{Slain}"); + g.AddHtmlLocalized(103, y, 120, 16, 3000087, 0x5F90); // Total + g.AddLabel(223, y, 0x481, Slain.ToString()); y += 16; - builder.AddHtmlLocalized(103, y, 120, 16, 1074782, 0x5F90); // Return to - builder.AddLabel(223, y, 0x481, QuesterNameAttribute.GetQuesterNameFor(Instance.QuesterType)); + g.AddHtmlLocalized(103, y, 120, 16, 1074782, 0x5F90); // Return to + g.AddLabel(223, y, 0x481, QuesterNameAttribute.GetQuesterNameFor(Instance.QuesterType)); y += 16; } diff --git a/Projects/UOContent/Engines/ML Quests/Rewards/BaseReward.cs b/Projects/UOContent/Engines/ML Quests/Rewards/BaseReward.cs index f9292d42b..46a811bd6 100644 --- a/Projects/UOContent/Engines/ML Quests/Rewards/BaseReward.cs +++ b/Projects/UOContent/Engines/ML Quests/Rewards/BaseReward.cs @@ -12,9 +12,9 @@ namespace Server.Engines.MLQuests.Rewards protected virtual int LabelHeight => 16; - public void WriteToGump(ref DynamicGumpBuilder builder, int x, ref int y) + public void WriteToGump(Gump g, int x, ref int y) { - Name.AddHtmlText(ref builder, x, y, 280, LabelHeight, false, false, 0x5F90, 0xBDE784); + Name.AddHtmlText(g, x, y, 280, LabelHeight, false, false, 0x5F90, 0xBDE784); } public abstract void AddRewardItems(PlayerMobile pm, List rewards); diff --git a/Projects/UOContent/Engines/Party/Party.cs b/Projects/UOContent/Engines/Party/Party.cs index ab8d4e762..d20d80948 100644 --- a/Projects/UOContent/Engines/Party/Party.cs +++ b/Projects/UOContent/Engines/Party/Party.cs @@ -52,7 +52,7 @@ namespace Server.Engines.PartySystem public void OnStamChanged(Mobile m) { - var p = stackalloc byte[OutgoingMobilePackets.MobileAttributePacketLength].InitializePacket(); + Span p = stackalloc byte[OutgoingMobilePackets.MobileAttributePacketLength].InitializePacket(); OutgoingMobilePackets.CreateMobileStam(p, m, true); for (var i = 0; i < Members.Count; ++i) @@ -69,7 +69,7 @@ namespace Server.Engines.PartySystem public void OnManaChanged(Mobile m) { - var p = stackalloc byte[OutgoingMobilePackets.MobileAttributePacketLength].InitializePacket(); + Span p = stackalloc byte[OutgoingMobilePackets.MobileAttributePacketLength].InitializePacket(); OutgoingMobilePackets.CreateMobileMana(p, m, true); for (var i = 0; i < Members.Count; ++i) @@ -123,8 +123,9 @@ namespace Server.Engines.PartySystem { from.SendMessage("They are not in a party."); } - else if (p.m_Listeners.Remove(from)) + else if (p.m_Listeners.Contains(from)) { + p.m_Listeners.Remove(from); from.SendMessage("You are no longer listening to that party."); } else @@ -197,9 +198,9 @@ namespace Server.Engines.PartySystem Members.Add(new PartyMemberInfo(m)); m.Party = this; - var memberList = + Span memberList = stackalloc byte[PartyPackets.GetPartyMemberListPacketLength(Count)].InitializePacket(); - var attrsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributesPacketLength].InitializePacket(); + Span attrsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributesPacketLength].InitializePacket(); for (var i = 0; i < Members.Count; ++i) { @@ -229,7 +230,7 @@ namespace Server.Engines.PartySystem return; } - var buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedAffixLength(from.Name, "")] + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedAffixLength(from.Name, "")] .InitializePacket(); var length = OutgoingMessagePackets.CreateMessageLocalizedAffix( buffer, @@ -301,7 +302,7 @@ namespace Server.Engines.PartySystem if (removed) { - var removeMember = + Span removeMember = stackalloc byte[PartyPackets.GetPartyRemoveMemberPacketLength(Count)].InitializePacket(); // Send empty party to the player removed @@ -385,7 +386,7 @@ namespace Server.Engines.PartySystem public void SendToAll(int number, string args = "", int hue = 0x3B2) { - var buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedLength(args)].InitializePacket(); + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedLength(args)].InitializePacket(); var length = OutgoingMessagePackets.CreateMessageLocalized( buffer, @@ -400,7 +401,7 @@ namespace Server.Engines.PartySystem public void SendPublicMessage(Mobile from, string text) { - var textMessagePacket = + Span textMessagePacket = stackalloc byte[PartyPackets.GetPartyTextMessagePacketLength(text)].InitializePacket(); PartyPackets.CreatePartyTextMessage(textMessagePacket, from.Serial, text, true); SendToAll(textMessagePacket); @@ -418,7 +419,7 @@ namespace Server.Engines.PartySystem private void SendToStaffMessage(Mobile from, string text) { - var buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); foreach (var ns in from.GetClientsInRange(8)) { @@ -457,7 +458,7 @@ namespace Server.Engines.PartySystem return; } - var buffer = + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); OutgoingMessagePackets.CreateMessage( buffer, Serial.MinusOne, -1, MessageType.Regular, 0x3B2, @@ -503,9 +504,9 @@ namespace Server.Engines.PartySystem m_Mobile.SendLocalizedMessage(1005437); // You have rejoined the party. m_Mobile.NetState.SendPartyMemberList(p); - var buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedAffixLength(m_Mobile.Name, "")] + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedAffixLength(m_Mobile.Name, "")] .InitializePacket(); - var attrsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributesPacketLength].InitializePacket(); + Span attrsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributesPacketLength].InitializePacket(); var ns = m_Mobile.NetState; diff --git a/Projects/UOContent/Engines/Party/PartyCommands.cs b/Projects/UOContent/Engines/Party/PartyCommands.cs index 520a07a0f..ae249f963 100644 --- a/Projects/UOContent/Engines/Party/PartyCommands.cs +++ b/Projects/UOContent/Engines/Party/PartyCommands.cs @@ -40,7 +40,7 @@ namespace Server.Engines.PartySystem from.SendLocalizedMessage(1005455); // Who would you like to remove from your party? from.Target = new RemovePartyTarget(); } - else if (p.Leader == from || from == target) + else if ((p.Leader == from || from == target) && p.Contains(target)) { p.Remove(target); } diff --git a/Projects/UOContent/Engines/Party/PartyPackets.cs b/Projects/UOContent/Engines/Party/PartyPackets.cs index dfb8d5592..b27162c4a 100644 --- a/Projects/UOContent/Engines/Party/PartyPackets.cs +++ b/Projects/UOContent/Engines/Party/PartyPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: PartyPackets.cs * * * @@ -54,7 +54,7 @@ namespace Server.Engines.PartySystem return; } - var buffer = stackalloc byte[GetPartyMemberListPacketLength(p.Count)].InitializePacket(); + Span buffer = stackalloc byte[GetPartyMemberListPacketLength(p.Count)].InitializePacket(); CreatePartyMemberList(buffer, p); ns.Send(buffer); @@ -70,7 +70,7 @@ namespace Server.Engines.PartySystem return; } - var buffer = stackalloc byte[GetPartyRemoveMemberPacketLength(p?.Count ?? 0)].InitializePacket(); + Span buffer = stackalloc byte[GetPartyRemoveMemberPacketLength(p?.Count ?? 0)].InitializePacket(); CreatePartyRemoveMember(buffer, m, p); ns.Send(buffer); @@ -110,7 +110,7 @@ namespace Server.Engines.PartySystem return; } - var buffer = stackalloc byte[GetPartyTextMessagePacketLength(text)].InitializePacket(); + Span buffer = stackalloc byte[GetPartyTextMessagePacketLength(text)].InitializePacket(); CreatePartyTextMessage(buffer, m, text, toAll); ns.Send(buffer); diff --git a/Projects/UOContent/Engines/Pathing/BitmapAStarAlgorithm.cs b/Projects/UOContent/Engines/Pathing/BitmapAStarAlgorithm.cs deleted file mode 100644 index 2c830db20..000000000 --- a/Projects/UOContent/Engines/Pathing/BitmapAStarAlgorithm.cs +++ /dev/null @@ -1,572 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BitmapAStarAlgorithm.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Server.Engines.Pathing; -using Server.Engines.Pathing.Cache; -using Server.Mobiles; -using Server.Systems.FeatureFlags; -using CalcMoves = Server.Movement.Movement; -using MoveImpl = Server.Movement.MovementImpl; - -namespace Server.PathAlgorithms; - -/// -/// A* pathfinder that expands a cell with a single lookup, -/// which returns all 8 directions' walkability and destination Zs at once. Where the cache can't -/// answer — a fallthrough on that cell, or a flying creature the static cache can't model — -/// runs the per-direction -/// loop for that one cell instead, so a partial cache miss costs only the cells it affects. -/// -public class BitmapAStarAlgorithm : PathAlgorithm -{ - private struct PathNode - { - public int cost; - public int total; - public int parent; - public int z; - } - - private const int AreaSize = 38; - - private const int NodeCount = AreaSize * AreaSize * PlaneCount; - - private const int PlaneOffset = 128; - private const int PlaneCount = 13; - private const int PlaneHeight = 20; - // The shared default. A differently-configured variant is just another instance. - public static readonly BitmapAStarAlgorithm Instance = new(); - - // Scratch reused across every Find on this instance — roughly 320 KB of it, so create - // instances once and hold them, never per call. Per-instance rather than static so two - // differently-configured algorithms don't share state. Reuse is safe because the game loop is - // single-threaded and Find never re-enters. - private readonly Direction[] _path = new Direction[AreaSize * AreaSize]; - private readonly PathNode[] _nodes = new PathNode[NodeCount]; - private readonly byte[] _nodeStates = new byte[NodeCount]; - private readonly int[] _successors = new int[8]; - private readonly PriorityQueue _openQueue = new(); - - // Expansion budget: the search gives up and returns null past this many nodes. It bounds the - // cost of an unreachable goal, which would otherwise exhaust the whole search window. A - // successful search stops when it finds the goal, so the budget only binds on hard or hopeless - // routes — it needs to stay high enough to solve walled-off indoor ones. - public int MaxSearchNodes { get; set; } = 1000; - - private int _xOffset; - private int _yOffset; - - // Every expansion goes to the slow path: the creature can fly, and arbitrary Z-jumping is - // outside what a static cache can model. - private bool _currentMobileNeedsSlowPath; - - // Diagonal corner-cut uses the strict rule — both cardinal partners walkable, not just one. - // Non-GM players only. The cache still applies; the partner bits are in the same mask byte. - private bool _currentMobilePlayerStrict; - - // Capability overlay on the cache's two rule sets, applied per cell as - // effective = (walkMask & !cantWalk) | (wetMask & canSwim) - private bool _currentMobileCanSwim; - private bool _currentMobileCantWalk; - - // Per-mobile flags for the dynamic-obstacle pass, derived once in Find rather than per cell. - private bool _currentMobileIgnoreDoors; - private bool _currentMobileIgnoreSpellFields; - private bool _currentMobileIgnoreMovableImpassables; - - public static void Configure() - { - // Shard-tunable expansion budget for the shared instance; see MaxSearchNodes. Written back - // to server.cfg on first boot so it's discoverable. - Instance.MaxSearchNodes = ServerConfiguration.GetOrUpdateSetting( - "pathfinding.maxSearchNodes", - 1000 - ); - } - - private Point3D _goal; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Heuristic(int x, int y, int z) - { - x -= _goal.X - _xOffset; - y -= _goal.Y - _yOffset; - z -= _goal.Z; - - x *= 11; - y *= 11; - - return x * x + y * y + z * z; - } - - public override bool CheckCondition(Mobile m, Map map, Point3D start, Point3D goal) => - Utility.InRange(start, goal, AreaSize); - - public override Direction[] Find(Mobile m, Map map, Point3D start, Point3D goal) - { - if (!Utility.InRange(start, goal, AreaSize)) - { - return null; - } - - // The frontier probes a given chunk dozens of times over one search; opening a generation - // is what makes the cache's promotion gate count all of that as a single touch. - StepCache.Instance.BeginFindGeneration(); - - PathfindRecorder.RecordIfEnabled(m, map, start, goal); - - _currentMobileNeedsSlowPath = RequiresSlowPath(m); - _currentMobilePlayerStrict = m.Player && m.AccessLevel < AccessLevel.GameMaster; - if (m is BaseCreature creature) - { - _currentMobileCanSwim = creature.CanSwim; - _currentMobileCantWalk = creature.CantWalk; - _currentMobileIgnoreDoors = creature.CanOpenDoors; - _currentMobileIgnoreMovableImpassables = creature.CanMoveOverObstacles; - } - else - { - _currentMobileCanSwim = false; - _currentMobileCantWalk = false; - _currentMobileIgnoreDoors = false; - _currentMobileIgnoreMovableImpassables = false; - } - - // Dead and spectral mobiles pass through doors too. Mirrors MovementImpl. - _currentMobileIgnoreDoors |= !m.Alive || m.Body.BodyID == 0x3DB || m.IsDeadBondedPet; - _currentMobileIgnoreSpellFields = m is PlayerMobile && map != Map.Felucca; - - Array.Clear(_nodeStates); - - _goal = goal; - - _xOffset = (start.X + goal.X - AreaSize) / 2; - _yOffset = (start.Y + goal.Y - AreaSize) / 2; - - var fromNode = GetIndex(start.X, start.Y, start.Z); - var destNode = GetIndex(goal.X, goal.Y, goal.Z); - - _nodes[fromNode].cost = 0; - _nodes[fromNode].total = Heuristic(start.X - _xOffset, start.Y - _yOffset, start.Z); - _nodes[fromNode].parent = -1; - _nodes[fromNode].z = start.Z; - - _openQueue.Enqueue(fromNode, _nodes[fromNode].total); - _nodeStates[fromNode] = 1; - - var bc = m as BaseCreature; - - int backtrack = 0, depth = 0; - - var path = _path; - - while (_openQueue.Count > 0) - { - if (++depth > MaxSearchNodes) - { - break; - } - - if (!_openQueue.TryDequeue(out var bestNode, out var bestTotal)) - { - break; - } - - // Duplicate, lower priority - if (_nodeStates[bestNode] == 2 || _nodes[bestNode].total != bestTotal) - { - continue; - } - - _nodeStates[bestNode] = 2; - - // MovementImpl reads these statics, so a slow-path fallthrough on any cell below needs - // them set for this mobile. - if (bc != null) - { - MoveImpl.AlwaysIgnoreDoors = bc.CanOpenDoors; - MoveImpl.IgnoreMovableImpassables = bc.CanMoveOverObstacles; - } - - MoveImpl.Goal = goal; - - var vals = _successors; - var count = GetSuccessors(bestNode, m, map); - - MoveImpl.AlwaysIgnoreDoors = false; - MoveImpl.IgnoreMovableImpassables = false; - MoveImpl.Goal = Point3D.Zero; - - if (count == 0) - { - continue; - } - - for (var i = 0; i < count; ++i) - { - var newNode = vals[i]; - - // Skip if the node is already closed - if (_nodeStates[newNode] == 2) - { - continue; - } - - var isDiagonal = i % 2 == 1; - var moveCost = isDiagonal ? 14 : 10; - var newCost = _nodes[bestNode].cost + moveCost; - var newTotal = newCost + Heuristic( - newNode % AreaSize, - newNode / AreaSize % AreaSize, - _nodes[newNode].z - ); - - if (_nodeStates[newNode] == 0 || newTotal < _nodes[newNode].total) - { - _nodes[newNode].parent = bestNode; - _nodes[newNode].cost = newCost; - _nodes[newNode].total = newTotal; - - // Requeue (duplicates allowed), and mark as open - _openQueue.Enqueue(newNode, newTotal); - _nodeStates[newNode] = 1; - } - - if (newNode != destNode) - { - continue; - } - - var pathCount = 0; - var parent = _nodes[newNode].parent; - - while (parent != -1) - { - path[pathCount++] = GetDirection( - parent % AreaSize, - parent / AreaSize % AreaSize, - newNode % AreaSize, - newNode / AreaSize % AreaSize - ); - newNode = parent; - parent = _nodes[newNode].parent; - - if (newNode == fromNode) - { - break; - } - } - - var dirs = new Direction[pathCount]; - - while (pathCount > 0) - { - dirs[backtrack++] = path[--pathCount]; - } - - _openQueue.Clear(); - _currentMobileNeedsSlowPath = false; - _currentMobilePlayerStrict = false; - _currentMobileCanSwim = false; - _currentMobileCantWalk = false; - _currentMobileIgnoreDoors = false; - _currentMobileIgnoreSpellFields = false; - _currentMobileIgnoreMovableImpassables = false; - return dirs; - } - } - - _openQueue.Clear(); - _currentMobileNeedsSlowPath = false; - _currentMobilePlayerStrict = false; - _currentMobileCanSwim = false; - _currentMobileCantWalk = false; - _currentMobileIgnoreDoors = false; - _currentMobileIgnoreSpellFields = false; - _currentMobileIgnoreMovableImpassables = false; - return null; - } - - private int GetIndex(int x, int y, int z) - { - x -= _xOffset; - y -= _yOffset; - z += PlaneOffset; - z /= PlaneHeight; - - return x + y * AreaSize + z * AreaSize * AreaSize; - } - - /// - /// Expands one cell into its walkable neighbours. A single cache lookup covers all 8 - /// directions, including the partner bits the diagonal corner-cut needs, so no neighbouring - /// cell has to be consulted. Falls back to for this cell - /// alone when the cache can't answer. - /// - private int GetSuccessors(int p, Mobile m, Map map) - { - var px = p % AreaSize; - var py = p / AreaSize % AreaSize; - var pz = _nodes[p].z; - - var p3D = new Point3D(px + _xOffset, py + _yOffset, pz); - - var vals = _successors; - - if (_currentMobileNeedsSlowPath || !ContentFeatureFlags.BitmapPathfindingCache) - { - return GetSuccessorsSlowPath(m, map, px, py, p3D, vals); - } - - var count = 0; - - var lookup = StepCache.Instance.TryGetMask(map, p3D.X, p3D.Y, (sbyte)p3D.Z); - - if (!lookup.IsHit) - { - // A multi-covered cell still gets a whole-cell mask, synthesized over the house or boat - // components rather than looked up. That keeps it out of the slow path's 8 separate - // CheckMovement calls, and the result flows through the same overlay, corner-cut and - // dynamic-obstacle logic below as a cache hit would. - if (lookup.HitKind == CacheHitKind.Fallthrough_Multi) - { - lookup = MultiMaskCache.Instance.GetMask(map, p3D.X, p3D.Y, (sbyte)p3D.Z); - } - else - { - return GetSuccessorsSlowPath(m, map, px, py, p3D, vals); - } - } - - // Overlay the mobile's capabilities onto the cache's two rule sets. The corner-cut below - // reads its partner bits from this effective mask, not the raw one. - var walkBits = _currentMobileCantWalk ? (byte)0 : lookup.WalkMask; - var swimBits = _currentMobileCanSwim ? lookup.WetMask : (byte)0; - var mask = (byte)(walkBits | swimBits); - - for (var i = 0; i < 8; ++i) - { - var x = px; - var y = py; - CalcMoves.Offset((Direction)i, ref x, ref y); - - if (x is < 0 or >= AreaSize || y is < 0 or >= AreaSize) - { - continue; - } - - if ((mask & (1 << i)) == 0) - { - continue; - } - - // Diagonal corner-cut: a creature needs at least one of the two flanking cardinals to - // be walkable, a non-GM player needs both. - if ((i & 1) == 1) - { - var leftBit = 1 << ((i - 1) & 0x7); - var rightBit = 1 << ((i + 1) & 0x7); - if (_currentMobilePlayerStrict - ? (mask & leftBit) == 0 || (mask & rightBit) == 0 - : (mask & leftBit) == 0 && (mask & rightBit) == 0) - { - continue; - } - } - - // Walking wins over swimming where both are possible. MovementImpl picks the surface - // closest to the start Z, and for a creature standing on land that is always the walk - // surface. - var useWalkZ = (walkBits & (1 << i)) != 0; - var z = useWalkZ - ? i switch - { - 0 => lookup.WalkZ_N, - 1 => lookup.WalkZ_NE, - 2 => lookup.WalkZ_E, - 3 => lookup.WalkZ_SE, - 4 => lookup.WalkZ_S, - 5 => lookup.WalkZ_SW, - 6 => lookup.WalkZ_W, - 7 => lookup.WalkZ_NW, - _ => (sbyte)0 - } - : i switch - { - 0 => lookup.SwimZ_N, - 1 => lookup.SwimZ_NE, - 2 => lookup.SwimZ_E, - 3 => lookup.SwimZ_SE, - 4 => lookup.SwimZ_S, - 5 => lookup.SwimZ_SW, - 6 => lookup.SwimZ_W, - 7 => lookup.SwimZ_NW, - _ => (sbyte)0 - }; - - var absX = x + _xOffset; - var absY = y + _yOffset; - - // The cache only knows static terrain, so items and mobiles at the target cell have to - // be checked live. - if (IsBlockedByDynamic(m, map, absX, absY, z)) - { - continue; - } - - var idx = GetIndex(absX, absY, z); - - if (idx >= 0 && idx < NodeCount) - { - _nodes[idx].z = z; - vals[count++] = idx; - } - } - - return count; - } - - private const int PersonHeightConst = 16; - private const int MobileHeight = 15; - - /// - /// MovementImpl's item and mobile collision phase for one target cell: impassable items - /// overlapping the mobile's vertical envelope block it, subject to the capability overrides, - /// as does any other mobile it can't move over. - /// - private bool IsBlockedByDynamic(Mobile m, Map map, int x, int y, int z) - { - var ourTop = z + PersonHeightConst; - - foreach (var item in map.GetItemsAt(x, y)) - { - var itemData = item.ItemData; - if (!itemData.ImpassableSurface) - { - continue; - } - - if (_currentMobileIgnoreMovableImpassables && item.Movable) - { - continue; - } - - var itemId = item.ItemID & TileData.MaxItemValue; - if (_currentMobileIgnoreDoors - && (itemData.Door - || itemId is 0x692 or 0x846 or 0x873 - || itemId >= 0x6F5 && itemId <= 0x6F6)) - { - continue; - } - - if (_currentMobileIgnoreSpellFields && itemId is 0x82 or 0x3946 or 0x3956) - { - continue; - } - - var checkZ = item.Z; - var checkTop = checkZ + itemData.CalcHeight; - if (checkTop > z && ourTop > checkZ) - { - return true; - } - } - - // The goal cell is usually occupied by whatever the mobile is chasing, so blocking on it - // would fail every pursuit. The follower stops short of the goal anyway. Every other cell - // still blocks on mobiles. - var skipMobCheck = x == MoveImpl.Goal.X && y == MoveImpl.Goal.Y; - - if (!skipMobCheck) - { - foreach (var mob in map.GetMobilesAt(x, y)) - { - if (mob == m) - { - continue; - } - - if (mob.Z + MobileHeight > z && z + MobileHeight > mob.Z && !CanMoveOver(m, mob)) - { - return true; - } - } - } - - return false; - } - - /// - /// True when m can step onto t's cell — a corpse, hidden staff, and so on. Mirrors - /// MovementImpl.CanMoveOver. - /// - private static bool CanMoveOver(Mobile m, Mobile t) => - !t.Alive || !m.Alive || t.IsDeadBondedPet || m.IsDeadBondedPet - || t.Hidden && t.AccessLevel > AccessLevel.Player; - - /// - /// Expands one cell the long way, with a CheckMovement call per direction. The same-cell - /// mobile check is layered on top because MovementImpl doesn't iterate those. - /// - private int GetSuccessorsSlowPath(Mobile m, Map map, int px, int py, Point3D p3D, int[] vals) - { - var count = 0; - - for (var i = 0; i < 8; ++i) - { - var x = px; - var y = py; - CalcMoves.Offset((Direction)i, ref x, ref y); - - if (x is < 0 or >= AreaSize || y is < 0 or >= AreaSize) - { - continue; - } - - if (!CalcMoves.CheckMovement(m, map, p3D, (Direction)i, out var z)) - { - continue; - } - - var absX = x + _xOffset; - var absY = y + _yOffset; - if (IsBlockedByDynamic(m, map, absX, absY, z)) - { - continue; - } - - var idx = GetIndex(absX, absY, z); - if (idx >= 0 && idx < NodeCount) - { - _nodes[idx].z = z; - vals[count++] = idx; - } - } - - return count; - } - - /// - /// True for creatures the static cache can't model at all. Only flying ones qualify: they - /// Z-jump freely, so the cache's source-Z guard would reject nearly every cell anyway. Swim - /// and cant-walk are handled by the capability overlay, and the door / obstacle capabilities - /// only affect dynamic items, so none of those disqualify the cache. - /// - private static bool RequiresSlowPath(Mobile m) => m is BaseCreature bc && bc.CanFly; -} diff --git a/Projects/UOContent/Engines/Pathing/Cache/CacheEvictionTimer.cs b/Projects/UOContent/Engines/Pathing/Cache/CacheEvictionTimer.cs deleted file mode 100644 index e1cdd2261..000000000 --- a/Projects/UOContent/Engines/Pathing/Cache/CacheEvictionTimer.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; - -namespace Server.Engines.Pathing.Cache; - -/// -/// Periodic backstop that enforces 's resident-chunk cap. Costs a -/// single early-return unless the cache has overflowed MaxResidentChunks. -/// -public class CacheEvictionTimer : Timer -{ - private static CacheEvictionTimer _instance; - - public static void Configure() - { - if (_instance != null) - { - return; - } - - _instance = new CacheEvictionTimer(); - _instance.Start(); - } - - private CacheEvictionTimer() : base(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(120)) { } - - protected override void OnTick() - { - StepCache.Instance.EnforceLruCap(); - } -} diff --git a/Projects/UOContent/Engines/Pathing/Cache/CacheHitKind.cs b/Projects/UOContent/Engines/Pathing/Cache/CacheHitKind.cs deleted file mode 100644 index f851559b1..000000000 --- a/Projects/UOContent/Engines/Pathing/Cache/CacheHitKind.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Server.Engines.Pathing.Cache; - -/// -/// Outcome of . Drives both telemetry and the caller's -/// decision to fall back to the slow path. Ordering is load-bearing: 0-2 are usable answers, -/// 3+ are fallthroughs, and tests that boundary. -/// -public enum CacheHitKind : byte -{ - Hit = 0, // served from the resident chunk - Miss_NotBuilt = 1, // chunk wasn't resident; built and returned - Miss_DirtyRebuild = 2, // chunk was stale; rebuilt and returned - Fallthrough_MultiZ = 3, // stacked walkable surfaces, none matching the query Z - Fallthrough_OffMap = 4, // out of bounds - Fallthrough_SourceZMismatch = 5, // |query Z - baked SourceZ| > StepHeight; a cached answer would diverge - Fallthrough_NotBuilt = 6, // first touch of an unbuilt chunk; the promotion gate defers the build - Fallthrough_Multi = 7, // a multi (house/boat) covers this cell or its halo -} diff --git a/Projects/UOContent/Engines/Pathing/Cache/CacheStats.cs b/Projects/UOContent/Engines/Pathing/Cache/CacheStats.cs deleted file mode 100644 index 419481e87..000000000 --- a/Projects/UOContent/Engines/Pathing/Cache/CacheStats.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Server.Engines.Pathing.Cache; - -/// -/// Snapshot of 's counters, as returned by GetStats() and reported by -/// the [PathCacheStats command. Every counter is monotonic except ResidentChunks, and all of -/// them reset on Clear() — they count since the last clear, not since startup. -/// -public readonly struct CacheStats( - int residentChunks, - long hits, - long missesNotBuilt, - long missesDirtyRebuild, - long fallthroughMultiZ, - long fallthroughOffMap, - long fallthroughSourceZMismatch, - long fallthroughNotBuilt, - long fallthroughMulti, - long multiLocalHits, - long multiMaskCacheHits, - long evictionsByLruCap, - long buildsTotal -) -{ - public readonly int ResidentChunks = residentChunks; - public readonly long Hits = hits; - public readonly long MissesNotBuilt = missesNotBuilt; - public readonly long MissesDirtyRebuild = missesDirtyRebuild; - public readonly long FallthroughMultiZ = fallthroughMultiZ; - public readonly long FallthroughOffMap = fallthroughOffMap; - public readonly long FallthroughSourceZMismatch = fallthroughSourceZMismatch; - public readonly long FallthroughNotBuilt = fallthroughNotBuilt; - public readonly long FallthroughMulti = fallthroughMulti; - public readonly long MultiLocalHits = multiLocalHits; - public readonly long MultiMaskCacheHits = multiMaskCacheHits; - public readonly long EvictionsByLruCap = evictionsByLruCap; - public readonly long BuildsTotal = buildsTotal; -} diff --git a/Projects/UOContent/Engines/Pathing/Cache/MultiMaskCache.cs b/Projects/UOContent/Engines/Pathing/Cache/MultiMaskCache.cs deleted file mode 100644 index 9cfd32f8c..000000000 --- a/Projects/UOContent/Engines/Pathing/Cache/MultiMaskCache.cs +++ /dev/null @@ -1,344 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Items; -using Server.Multis; - -namespace Server.Engines.Pathing.Cache; - -/// -/// Caches walkability masks for the interior cells of a multi, shared across every instance of the -/// same multiID. -/// -/// An interior cell — one whose 8 neighbours are all covered by the multi — has no terrain -/// neighbour, so its mask depends only on the multi's own component tiles and is identical at every -/// position the design is placed. That makes it cacheable in the multi's local frame and reusable -/// across instances; perimeter cells are not, and fall back to . -/// -/// The catch is terrain intruding into the multi's floor envelope, which would make a cell's mask -/// position-dependent after all. rules that out per instance, -/// once: if the whole footprint's terrain sits below the lowest floor, no interior cell can see it. -/// A dirty instance, or a (whose design mutates at runtime), always -/// synthesizes live rather than risk serving a wrong mask. -/// -public sealed class MultiMaskCache -{ - public static MultiMaskCache Instance { get; } = new(); - - private const int StepHeight = 2; - - private readonly Dictionary _byMultiId = []; - - public void Clear() => _byMultiId.Clear(); - - /// - /// The multi-aware mask for a covered cell, always usable (HitKind is always Hit). Served from - /// the shared cache when the cell is a clean interior one, synthesized live otherwise. - /// - public StepMask GetMask(Map map, int x, int y, sbyte sourceZ) - { - if (!TryResolveCoveringMulti(map, x, y, out var multi, out var lx, out var ly) - || multi is HouseFoundation) // its DesignState MCL changes at runtime - { - return LiveSynth(map, x, y, sourceZ); - } - - // Boats are cached despite moving: a deck mask is built in the local frame and is invariant - // under translation, and the clean gate plus the ItemID/location/map resets cover turning. - - if (multi.PathInteriorCacheState == MultiInteriorCacheState.Unknown) - { - multi.PathInteriorCacheState = - ComputeFootprintClean(map, multi) ? MultiInteriorCacheState.Clean : MultiInteriorCacheState.Dirty; - } - - if (multi.PathInteriorCacheState != MultiInteriorCacheState.Clean) - { - return LiveSynth(map, x, y, sourceZ); - } - - var mcl = multi.Components; - var local = GetOrCreate(multi.ItemID & 0x3FFF, mcl.Width, mcl.Height); - var state = local.GetState(lx, ly); - - if (state == MultiLocalMask.CellState.Cached) - { - // A clean footprint rules terrain out, so the source-Z match is the only guard left. - var worldFloorZ = local.FloorZAt(lx, ly) + multi.Z; - if (Math.Abs(sourceZ - worldFloorZ) <= StepHeight) - { - StepCache.Instance.RecordMultiMaskCacheHit(); - return ToWorldZ(local.MaskAt(lx, ly), multi.Z); - } - - return LiveSynth(map, x, y, sourceZ); - } - - if (state == MultiLocalMask.CellState.NonInterior) - { - return LiveSynth(map, x, y, sourceZ); - } - - // First touch of this cell: synthesize, then classify and cache if it's interior. - var mask = LiveSynth(map, x, y, sourceZ); - if (IsInteriorLocalCell(mcl, lx, ly) - && TryToLocalZ(mask, multi.Z, out var localMask) - && sourceZ - multi.Z is >= sbyte.MinValue and <= sbyte.MaxValue) - { - local.SetCached(lx, ly, localMask, (sbyte)(sourceZ - multi.Z)); - } - else - { - local.SetNonInterior(lx, ly); - } - - return mask; - } - - private static StepMask LiveSynth(Map map, int x, int y, sbyte sourceZ) - { - StepCache.Instance.RecordMultiLocalHit(); - return StepProbe.ComputeMultiMaskAt(map, x, y, sourceZ); - } - - private MultiLocalMask GetOrCreate(int key, int width, int height) - { - if (!_byMultiId.TryGetValue(key, out var m)) - { - m = new MultiLocalMask(width, height); - _byMultiId[key] = m; - } - - return m; - } - - /// - /// Finds the multi covering (x,y) and the cell's indices into its MCL, or false if none does. - /// - public static bool TryResolveCoveringMulti(Map map, int x, int y, out BaseMulti multi, out int lx, out int ly) - { - foreach (var candidate in map.GetMultisInSector(x, y)) - { - var mcl = candidate.Components; - var cx = x - candidate.X - mcl.Min.X; - var cy = y - candidate.Y - mcl.Min.Y; - if (cx >= 0 && cy >= 0 && cx < mcl.Width && cy < mcl.Height && mcl.Tiles[cx][cy].Length > 0) - { - multi = candidate; - lx = cx; - ly = cy; - return true; - } - } - - multi = null; - lx = ly = 0; - return false; - } - - /// - /// True when (lx,ly) and all 8 of its neighbours carry MCL tiles. Such a cell has no terrain - /// neighbour, so the multi alone determines its transitions and its mask is position-invariant. - /// A pure function of the MCL. - /// - public static bool IsInteriorLocalCell(MultiComponentList mcl, int lx, int ly) - { - for (var dy = -1; dy <= 1; dy++) - { - for (var dx = -1; dx <= 1; dx++) - { - var nx = lx + dx; - var ny = ly + dy; - if (nx < 0 || ny < 0 || nx >= mcl.Width || ny >= mcl.Height || mcl.Tiles[nx][ny].Length == 0) - { - return false; - } - } - } - - return true; - } - - /// - /// Rebases a world-frame mask's per-direction Zs into the multi's local frame. Returns false - /// when a local Z overflows sbyte — only reachable at extreme |multiZ| — and the caller must - /// then leave the cell uncached. - /// - public static bool TryToLocalZ(StepMask world, int multiZ, out StepMask local) - { - local = default; - Span w = stackalloc sbyte[8]; - Span s = stackalloc sbyte[8]; - for (var d = 0; d < 8; d++) - { - var lw = world.GetWalkZ((Direction)d) - multiZ; - var ls = world.GetSwimZ((Direction)d) - multiZ; - if (lw < sbyte.MinValue || lw > sbyte.MaxValue || ls < sbyte.MinValue || ls > sbyte.MaxValue) - { - return false; - } - w[d] = (sbyte)lw; - s[d] = (sbyte)ls; - } - - local = new StepMask( - world.WalkMask, world.WetMask, - w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7], - s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7] - ); - return true; - } - - /// - /// True when all terrain (land + statics) at (x,y) sits strictly below , - /// so a creature standing on the multi's floor never sees terrain in its envelope. - /// - public static bool TerrainTopBelow(Map map, int x, int y, sbyte floorZ) - { - map.GetAverageZ(x, y, out _, out _, out var landTop); - if (landTop >= floorZ) - { - return false; - } - - foreach (var tile in map.Tiles.GetStaticTiles(x, y)) - { - var data = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - var top = tile.Z + data.CalcHeight; - if (top >= floorZ) - { - return false; - } - } - - return true; - } - - /// Highest terrain (land + statics) top at (x,y). - public static int TerrainTop(Map map, int x, int y) - { - map.GetAverageZ(x, y, out _, out _, out var top); - foreach (var tile in map.Tiles.GetStaticTiles(x, y)) - { - var data = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - var t = tile.Z + data.CalcHeight; - if (t > top) - { - top = t; - } - } - - return top; - } - - /// - /// True when the multi's entire footprint terrain sits below its lowest standable floor. No - /// covered cell's terrain — nor any neighbour's — can then intrude into a creature's floor - /// envelope, which is what makes this instance's interior cells safe to serve from the shared - /// per-multiID cache. Evaluated once per instance and cached on the multi. - /// - public static bool ComputeFootprintClean(Map map, BaseMulti multi) - { - var mcl = multi.Components; - var minFloorLocal = int.MaxValue; - var maxTerrain = int.MinValue; - - for (var lx = 0; lx < mcl.Width; lx++) - { - for (var ly = 0; ly < mcl.Height; ly++) - { - var col = mcl.Tiles[lx][ly]; - if (col.Length == 0) - { - continue; - } - - foreach (var tile in col) - { - var data = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - if (data.Surface && !data.Impassable) - { - var top = tile.Z + data.CalcHeight; - if (top < minFloorLocal) - { - minFloorLocal = top; - } - } - } - - var terrain = TerrainTop(map, multi.X + mcl.Min.X + lx, multi.Y + mcl.Min.Y + ly); - if (terrain > maxTerrain) - { - maxTerrain = terrain; - } - } - } - - if (minFloorLocal == int.MaxValue) - { - return false; // no standable floor anywhere; refuse to cache rather than guess - } - - return maxTerrain < minFloorLocal + multi.Z; - } - - /// Inverse of : add multiZ back to recover world Zs. - public static StepMask ToWorldZ(StepMask local, int multiZ) - { - Span w = stackalloc sbyte[8]; - Span s = stackalloc sbyte[8]; - for (var d = 0; d < 8; d++) - { - w[d] = (sbyte)(local.GetWalkZ((Direction)d) + multiZ); - s[d] = (sbyte)(local.GetSwimZ((Direction)d) + multiZ); - } - - return new StepMask( - local.WalkMask, local.WetMask, - w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7], - s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7] - ); - } -} - -/// -/// One multiID's grid of interior-cell masks, filled in as cells are first touched. A cell is -/// Unknown until classified, then either Cached (interior — the mask is valid) or NonInterior -/// (perimeter — synthesize live). -/// -internal sealed class MultiLocalMask -{ - public enum CellState : byte { Unknown = 0, Cached = 1, NonInterior = 2 } - - private readonly int _width; - private readonly int _height; - private readonly CellState[] _state; - private readonly StepMask[] _mask; // local-frame mask, valid only when state == Cached - private readonly sbyte[] _floorZ; // local floor Z, valid only when state == Cached - - public MultiLocalMask(int width, int height) - { - _width = width; - _height = height; - _state = new CellState[width * height]; - _mask = new StepMask[width * height]; - _floorZ = new sbyte[width * height]; - } - - public int Width => _width; - public int Height => _height; - - public CellState GetState(int lx, int ly) => _state[ly * _width + lx]; - - public void SetCached(int lx, int ly, StepMask localMask, sbyte localFloorZ) - { - var i = ly * _width + lx; - _mask[i] = localMask; - _floorZ[i] = localFloorZ; - _state[i] = CellState.Cached; - } - - public void SetNonInterior(int lx, int ly) => _state[ly * _width + lx] = CellState.NonInterior; - - public StepMask MaskAt(int lx, int ly) => _mask[ly * _width + lx]; - public sbyte FloorZAt(int lx, int ly) => _floorZ[ly * _width + lx]; -} diff --git a/Projects/UOContent/Engines/Pathing/Cache/StepCache.cs b/Projects/UOContent/Engines/Pathing/Cache/StepCache.cs deleted file mode 100644 index b4c5eba7c..000000000 --- a/Projects/UOContent/Engines/Pathing/Cache/StepCache.cs +++ /dev/null @@ -1,972 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Server.Buffers; -using Server.Collections; -using Server.Logging; - -namespace Server.Engines.Pathing.Cache; - -/// -/// Singleton store of static walkability, keyed by 16x16 chunk (one per map sector). Chunks build -/// on demand and memory stays bounded by MaxResidentChunks through probabilistic LRU eviction, so -/// the cache is usable with no on-disk bake at all; a baked .swb file only removes the first-touch -/// build cost. -/// -/// The cache answers for a default walker on static terrain. Anything outside that — a multi -/// covering the cell, a query Z that doesn't match what the cell was baked at, stacked surfaces -/// with no matching stratum — returns a Fallthrough_* kind, and the caller resolves that cell -/// through MovementImpl instead. Callers must check . -/// -public sealed class StepCache -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(StepCache)); - - public static StepCache Instance { get; } = new(); - - private readonly Dictionary _chunks = []; - // Keys of _chunks, kept in lockstep with it, so eviction can sample a random resident chunk - // in O(1). Appended on insert, swap-and-popped on eviction. - private readonly List _keysList = []; - - // Promotion gate. A chunk's first miss returns Fallthrough_NotBuilt and the caller takes the - // slow path; only once misses reach MissPromotionThreshold within MissPromotionWindowMs does - // the chunk get built and served. This keeps one-off traffic — a pet trailing a player across - // the map — from building chunks nothing will query again, while a creature working a fixed - // territory still warms the chunks it revisits. - // - // The gate counts distinct Finds, not TryGetMask calls: A* probes each chunk it visits dozens - // of times within a single pathfind, so per-call counting would cross any threshold instantly - // and gate nothing. - private readonly Dictionary _chunkMissTracker = []; - private const int MaxMissTrackerEntries = 4096; - - private struct ChunkMissState - { - public byte MissCount; - public uint LastMissTickStamp; - public uint LastFindGeneration; - } - - // Telemetry counters - private long _hits; - private long _missesNotBuilt; - private long _missesDirtyRebuild; - private long _fallthroughMultiZ; - private long _fallthroughOffMap; - private long _fallthroughSourceZMismatch; - private long _fallthroughNotBuilt; - private long _fallthroughMulti; - private long _multiLocalHits; - private long _multiMaskCacheHits; - private long _evictionsByLruCap; - private long _buildsTotal; - - private StepCache() { } - - public void RecordMultiLocalHit() => _multiLocalHits++; - - public void RecordMultiMaskCacheHit() => _multiMaskCacheHits++; - - /// Hard cap on resident chunk count. Default 8192. Override for tests / ops. - public int MaxResidentChunks { get; set; } = 8192; - - /// - /// When true, immediately materializes every chunk in - /// the .swb file into the resident set, paying the file-load cost upfront at boot - /// instead of on first query. Trades ~25–50ms boot time per fully-baked map for zero - /// first-touch latency in production. Default off — preserves the lazy memory profile. - /// - public bool PreloadOnLazyOpen { get; set; } - - /// - /// Misses on the same chunk, within , needed to build it. - /// 1 builds eagerly on first touch; the default 2 waits for a second Find to show interest. - /// - public int MissPromotionThreshold { get; set; } = 2; - - /// - /// How long misses on a chunk accumulate toward promotion. A gap wider than this restarts - /// the count. - /// - public uint MissPromotionWindowMs { get; set; } = 30_000; - - /// - /// Opens a new pathfind for the promotion gate. Call once per pathfind: the gate counts - /// distinct Finds, so without this every cell expansion would count separately and the - /// threshold would be met immediately. Wraps back to 1, since 0 means "no Find open". - /// - public void BeginFindGeneration() - { - unchecked { CurrentFindGeneration++; } - - if (CurrentFindGeneration == 0) - { - CurrentFindGeneration = 1; - } - } - - /// The open pathfind's generation, or 0 if none. See . - internal uint CurrentFindGeneration { get; private set; } - - /// - /// Packs (mapId, chunkX, chunkY) into one key: [reserved 16][mapId 16][chunkX 16][chunkY 16]. - /// - internal static long EncodeKey(int mapId, int chunkX, int chunkY) => - ((long)(mapId & 0xFFFF) << 32) | ((long)(chunkX & 0xFFFF) << 16) | (long)(chunkY & 0xFFFF); - - public CacheStats GetStats() => new( - residentChunks: _chunks.Count, - hits: _hits, - missesNotBuilt: _missesNotBuilt, - missesDirtyRebuild: _missesDirtyRebuild, - fallthroughMultiZ: _fallthroughMultiZ, - fallthroughOffMap: _fallthroughOffMap, - fallthroughSourceZMismatch: _fallthroughSourceZMismatch, - fallthroughNotBuilt: _fallthroughNotBuilt, - fallthroughMulti: _fallthroughMulti, - multiLocalHits: _multiLocalHits, - multiMaskCacheHits: _multiMaskCacheHits, - evictionsByLruCap: _evictionsByLruCap, - buildsTotal: _buildsTotal - ); - - /// - /// Returns the cache to a cold-start state: drops every chunk, closes the .swb readers, and - /// zeroes the counters. - /// - public void Clear() - { - ClearResidentChunks(); - CloseLazyReaders(); - MultiMaskCache.Instance.Clear(); - } - - /// - /// without the file-handle teardown: drops the resident chunks and zeroes - /// the counters, but leaves the .swb readers open so the next query can refill from them. - /// - public void ClearResidentChunks() - { - _chunks.Clear(); - _keysList.Clear(); - _chunkMissTracker.Clear(); - CurrentFindGeneration = 0; - _hits = 0; - _missesNotBuilt = 0; - _missesDirtyRebuild = 0; - _fallthroughMultiZ = 0; - _fallthroughOffMap = 0; - _fallthroughSourceZMismatch = 0; - _fallthroughNotBuilt = 0; - _fallthroughMulti = 0; - _multiLocalHits = 0; - _multiMaskCacheHits = 0; - _evictionsByLruCap = 0; - _buildsTotal = 0; - } - - // Open .swb readers, one per map. Chunks are pulled from them on demand, so resident memory - // stays bounded by MaxResidentChunks no matter how large the file is. - private readonly Dictionary _lazyReaders = []; - - /// - /// Builds every chunk in the map and writes them to , returning the - /// number written. Blocks the caller for many seconds on a full-size map — run it offline or - /// during maintenance, not on a live shard at peak. - /// - public int BakeMap(int mapId, string path) - { - var map = Map.Maps[mapId]; - if (map == null || map == Map.Internal) - { - return 0; - } - - // A bake touches each chunk exactly once, so the promotion gate would defer every one of - // them and write an empty file. Baking is an explicit decision to populate everything, so - // build eagerly for the duration. - var prevThreshold = MissPromotionThreshold; - MissPromotionThreshold = 1; - try - { - var chunkCols = (map.Width + ChunkSize - 1) / ChunkSize; - var chunkRows = (map.Height + ChunkSize - 1) / ChunkSize; - var logEvery = Math.Max(1, chunkRows / 32); - - logger.Information( - "PathBake map {MapId}: walking {Cols}x{Rows} = {Total} chunks (synchronous; no eviction during the walk)...", - mapId, chunkCols, chunkRows, chunkCols * chunkRows - ); - - var stopWatch = Stopwatch.StartNew(); - for (var cy = 0; cy < chunkRows; cy++) - { - for (var cx = 0; cx < chunkCols; cx++) - { - // The sourceZ is irrelevant here: the chunk gets built on first access whether - // the query ends up a Hit or a Fallthrough_SourceZMismatch. - TryGetMask(map, cx * ChunkSize, cy * ChunkSize, sourceZ: 0); - } - - if ((cy + 1) % logEvery == 0 || cy == chunkRows - 1) - { - logger.Information( - "PathBake map {MapId}: row {Row}/{Rows} ({Pct}%), {Resident} chunks resident, {Elapsed:F1}s, {HeapMB} MB heap", - mapId, cy + 1, chunkRows, (cy + 1) * 100 / chunkRows, - _chunks.Count, stopWatch.ElapsedMilliseconds / 1000.0, GC.GetTotalMemory(false) >> 20 - ); - } - } - - logger.Information( - "PathBake map {MapId}: walk complete in {Elapsed:F1}s, writing {Resident} chunks to disk...", - mapId, stopWatch.ElapsedMilliseconds / 1000.0, _chunks.Count - ); - } - finally - { - MissPromotionThreshold = prevThreshold; - } - - return SaveToFile(path, mapId); - } - - /// - /// Writes the map's resident chunks to a .swb file and returns the count. The file carries a - /// fingerprint of the tile and map data, so a bake made before a client patch is detected and - /// rejected when it is next opened. - /// - public int SaveToFile(string path, int mapId) - { - using var chunks = PooledRefList<(int chunkX, int chunkY, StepChunk chunk)>.Create(); - - foreach (var key in _keysList) - { - DecodeKey(key, out var keyMapId, out var chunkX, out var chunkY); - if (keyMapId == mapId) - { - chunks.Add((chunkX, chunkY, _chunks[key])); - } - } - - StepCacheFile.Write(path, (uint)mapId, chunks.AsSpan()); - return chunks.Count; - } - - /// - /// Opens a .swb file as a backing store for the map, reading only the header and chunk index - /// up front; records are pulled as queries ask for them. Returns false if the file is missing, - /// unreadable, or a stale bake whose fingerprint no longer matches the live tile data. - /// - public bool TryOpenLazyReader(string path, int mapId) - { - var reader = StepCacheFile.OpenForLazy(path); - if (reader == null) - { - return false; - } - - if (reader.MapId != (uint)mapId) - { - logger.Warning( - "StepCache: {Path} declares mapId {FileMapId} but caller requested {RequestedMapId}; ignoring", - path, reader.MapId, mapId - ); - reader.Dispose(); - return false; - } - - if (_lazyReaders.TryGetValue(mapId, out var existing)) - { - existing.Dispose(); - } - _lazyReaders[mapId] = reader; - - // Debug: opening is the expected case. A rebuild is the interesting one, and BakeMap logs it. - logger.Debug( - "StepCache: opened {Path} ({ChunkCount} chunks indexed) for map {MapId}", - path, reader.IndexedChunkCount, mapId - ); - - if (PreloadOnLazyOpen) - { - PreloadFromLazyReader(mapId, reader); - } - - return true; - } - - /// - /// Loads every chunk in the file into the resident set, for . - /// - private void PreloadFromLazyReader(int mapId, StepCacheFile.LazyReader reader) - { - var map = Map.Maps[mapId]; - if (map == null || map == Map.Internal) - { - return; - } - - var loaded = 0; - foreach (var (chunkX, chunkY) in reader.EnumerateChunkCoords()) - { - var key = EncodeKey(mapId, chunkX, chunkY); - if (_chunks.ContainsKey(key)) - { - continue; - } - - var chunk = TryLoadFromLazyReader(map, chunkX, chunkY); - if (chunk == null) - { - continue; - } - - _chunks[key] = chunk; - _keysList.Add(key); - loaded++; - } - - logger.Information( - "StepCache: preloaded {Loaded} chunks from .swb for map {MapId}", loaded, mapId - ); - } - - /// Number of .swb readers currently open. - public int OpenLazyReaderCount => _lazyReaders.Count; - - /// - /// True when a .swb reader is open for the map. A reader only opens after its fingerprint - /// validates against the live tile data, so this already answers "is there an up-to-date bake - /// for this map?" — the boot prebake leans on that to skip maps rather than fingerprint them - /// a second time. - /// - public bool HasLazyReader(int mapId) => _lazyReaders.ContainsKey(mapId); - - /// Diagnostic: does the map's .swb hold a record for (chunkX, chunkY)? - internal bool LazyReaderHasChunk(int mapId, int chunkX, int chunkY) => - _lazyReaders.TryGetValue(mapId, out var r) && r.Has(chunkX, chunkY); - - /// - /// Diagnostic: the resident chunk covering (chunkX, chunkY), or null if it isn't resident. - /// Exposed so tests can inspect and inject chunk state without reflecting into the internals. - /// - internal StepChunk GetResidentChunk(int mapId, int chunkX, int chunkY) => - _chunks.GetValueOrDefault(EncodeKey(mapId, chunkX, chunkY)); - - /// - /// Diagnostic: whether the eviction key list still mirrors the resident set exactly. A desync - /// breaks sampled eviction — a stale key throws on lookup, a missing one pins a chunk resident - /// forever — and it is invisible from the outside, so tests assert on it directly. - /// - internal bool ResidentIndexInSync() - { - if (_keysList.Count != _chunks.Count) - { - return false; - } - - foreach (var key in _keysList) - { - if (!_chunks.ContainsKey(key)) - { - return false; - } - } - - return true; - } - - /// Closes every open .swb reader, releasing the underlying file streams. - public void CloseLazyReaders() - { - foreach (var reader in _lazyReaders.Values) - { - reader.Dispose(); - } - _lazyReaders.Clear(); - } - - /// - /// How many random resident chunks each eviction samples before dropping the oldest of them. - /// Sampling approximates true LRU closely enough at a fraction of the cost, since it needs no - /// sort and no access-ordered structure. Raising it trades speed for accuracy. - /// - private const int LruSampleSize = 5; - - /// - /// Evicts chunks until the resident count is back within MaxResidentChunks. Each eviction costs - /// O() regardless of how many chunks are resident, so sustained cap - /// pressure doesn't degrade. Driven by . - /// - public void EnforceLruCap() - { - var overflow = _chunks.Count - MaxResidentChunks; - if (overflow <= 0) - { - return; - } - - while (overflow-- > 0 && _keysList.Count > 0) - { - var oldestIdx = -1; - long oldestTouched = long.MaxValue; - long oldestKey = 0; - - // Sampling with replacement: a repeated key just wastes one sample, it can't pick a - // wrong victim. - var samples = Math.Min(LruSampleSize, _keysList.Count); - for (var s = 0; s < samples; s++) - { - var idx = Utility.Random(_keysList.Count); - var k = _keysList[idx]; - var touched = _chunks[k].LastTouchedTicks; - if (touched < oldestTouched) - { - oldestTouched = touched; - oldestKey = k; - oldestIdx = idx; - } - } - - _chunks.Remove(oldestKey); - // Swap-and-pop _keysList[oldestIdx] with the tail; O(1) regardless of position. - var last = _keysList.Count - 1; - if (oldestIdx != last) - { - _keysList[oldestIdx] = _keysList[last]; - } - _keysList.RemoveAt(last); - _evictionsByLruCap++; - } - } - - internal static void DecodeKey(long key, out int mapId, out int chunkX, out int chunkY) - { - mapId = (int)((key >> 32) & 0xFFFF); - chunkX = (int)((key >> 16) & 0xFFFF); - chunkY = (int)(key & 0xFFFF); - } - - private const int ChunkSize = 16; - - /// - /// All-zero mask carrying a Fallthrough_* kind. is false for - /// these, so the caller ignores the payload and takes the slow path. - /// - private static StepMask Fallthrough(CacheHitKind kind) => - new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, kind); - - /// Bumps the telemetry counter matching a served (non-fallthrough) hit kind. - private void RecordServed(CacheHitKind kind) - { - switch (kind) - { - case CacheHitKind.Miss_NotBuilt: { _missesNotBuilt++; break; } - case CacheHitKind.Miss_DirtyRebuild: { _missesDirtyRebuild++; break; } - case CacheHitKind.Hit: { _hits++; break; } - } - } - - /// - /// True when a multi covers (x, y) or any of its 8 neighbours. The halo matters because a - /// cell's mask encodes the edges TO its neighbours, so a wall one cell over has to block those - /// edges. Since a chunk is a sector, an interior cell only inspects its own sector's HasMultis - /// flag; edge and corner cells additionally check whichever adjacent sectors the halo reaches. - /// - private static bool MultiInfluence(Map map, int x, int y) - { - var sx = x >> 4; - var sy = y >> 4; - if (map.GetRealSector(sx, sy).HasMultis) - { - return true; - } - - var west = (x & 15) == 0; - var east = (x & 15) == 15; - var north = (y & 15) == 0; - var south = (y & 15) == 15; - if (!(west || east || north || south)) - { - return false; // interior cell: its whole halo lies in this sector, which has no multis - } - - return west && map.GetRealSector(sx - 1, sy).HasMultis - || east && map.GetRealSector(sx + 1, sy).HasMultis - || north && map.GetRealSector(sx, sy - 1).HasMultis - || south && map.GetRealSector(sx, sy + 1).HasMultis - || west && north && map.GetRealSector(sx - 1, sy - 1).HasMultis - || east && north && map.GetRealSector(sx + 1, sy - 1).HasMultis - || west && south && map.GetRealSector(sx - 1, sy + 1).HasMultis - || east && south && map.GetRealSector(sx + 1, sy + 1).HasMultis; - } - - /// - /// The hot-path query: one lookup yields the cell's 8-direction mask, its 8 destination Zs, - /// and the hit kind. Check before trusting the payload — on any - /// fallthrough it is all zeroes and the caller must resolve the cell through MovementImpl. - /// - public StepMask TryGetMask(Map map, int x, int y, sbyte sourceZ) - { - if (map == null || map == Map.Internal || x < 0 || y < 0 || x >= map.Width || y >= map.Height) - { - _fallthroughOffMap++; - return Fallthrough(CacheHitKind.Fallthrough_OffMap); - } - - // Multis are dynamic, so they are never baked into a chunk. Cells they touch go to the - // multi-aware path instead. The check is gated on Sector.HasMultis, so the multi-free - // majority of the map pays one sector lookup for it. - if (MultiInfluence(map, x, y)) - { - _fallthroughMulti++; - return Fallthrough(CacheHitKind.Fallthrough_Multi); - } - - var chunkX = x >> 4; - var chunkY = y >> 4; - var key = EncodeKey(map.MapID, chunkX, chunkY); - - var hitKindResult = CacheHitKind.Hit; - if (!_chunks.TryGetValue(key, out var chunk)) - { - // The .swb is consulted before the promotion gate: a baked chunk is already an explicit - // decision to keep this area warm, and loading it is far cheaper than building it. - chunk = TryLoadFromLazyReader(map, chunkX, chunkY); - if (chunk != null) - { - _chunks[key] = chunk; - _keysList.Add(key); - hitKindResult = CacheHitKind.Miss_NotBuilt; - } - else if (ShouldPromoteAfterMiss(key)) - { - chunk = BuildChunk(map, chunkX, chunkY); - _chunks[key] = chunk; - _keysList.Add(key); - hitKindResult = CacheHitKind.Miss_NotBuilt; - } - else - { - _fallthroughNotBuilt++; - return Fallthrough(CacheHitKind.Fallthrough_NotBuilt); - } - } - - // No staleness check: a resident chunk holds only static terrain, and every cell a multi - // could have changed already fell through above. - chunk.LastTouchedTicks = Core.TickCount; - - var cellIndex = ((y - (chunkY << 4)) << 4) | (x - (chunkX << 4)); - - if (chunk.IsCellMultiZ(cellIndex)) - { - // Stacked surfaces: pick the stratum baked nearest the query Z. Multi-Z cells are - // served only from strata, never from the main mask. - if (TryStratumHit(chunk, cellIndex, sourceZ, hitKindResult, out var stratumResult)) - { - RecordServed(hitKindResult); - return stratumResult; - } - - _fallthroughMultiZ++; - return Fallthrough(CacheHitKind.Fallthrough_MultiZ); - } - - // Source-Z guard. A cell holds one answer, baked at one standing Z, so a query from too far - // above or below it would get an answer that doesn't apply. The StepHeight tolerance - // absorbs ordinary Z jitter and cannot be widened: reachability flips at exactly that - // boundary, so a looser guard would serve answers that disagree with MovementImpl. - if (Math.Abs(sourceZ - chunk.SourceZ[cellIndex]) > StepHeight) - { - // Unless this is a shore cell and the query is coming from the water, in which case the - // swim layer holds the answer baked from the water surface. - if (chunk.HasSwimLayer) - { - var swimSrc = chunk.SwimSourceZ[cellIndex]; - if (swimSrc != StepChunk.NoSwimLayerCell && Math.Abs(sourceZ - swimSrc) <= StepHeight) - { - RecordServed(hitKindResult); - return new StepMask( - 0, chunk.SwimMask[cellIndex], - 0, 0, 0, 0, 0, 0, 0, 0, - chunk.SwimZN_Layer[cellIndex], - chunk.SwimZNE_Layer[cellIndex], - chunk.SwimZE_Layer[cellIndex], - chunk.SwimZSE_Layer[cellIndex], - chunk.SwimZS_Layer[cellIndex], - chunk.SwimZSW_Layer[cellIndex], - chunk.SwimZW_Layer[cellIndex], - chunk.SwimZNW_Layer[cellIndex], - hitKindResult - ); - } - } - - _fallthroughSourceZMismatch++; - return Fallthrough(CacheHitKind.Fallthrough_SourceZMismatch); - } - - RecordServed(hitKindResult); - - return new StepMask( - chunk.WalkMask[cellIndex], - chunk.WetMask[cellIndex], - chunk.WalkZN[cellIndex], - chunk.WalkZNE[cellIndex], - chunk.WalkZE[cellIndex], - chunk.WalkZSE[cellIndex], - chunk.WalkZS[cellIndex], - chunk.WalkZSW[cellIndex], - chunk.WalkZW[cellIndex], - chunk.WalkZNW[cellIndex], - chunk.SwimZN[cellIndex], - chunk.SwimZNE[cellIndex], - chunk.SwimZE[cellIndex], - chunk.SwimZSE[cellIndex], - chunk.SwimZS[cellIndex], - chunk.SwimZSW[cellIndex], - chunk.SwimZW[cellIndex], - chunk.SwimZNW[cellIndex], - hitKindResult - ); - } - - /// - /// Loads a chunk from the map's .swb, or null if no reader is open or the file has no record at - /// (chunkX, chunkY). No staleness check is needed here — the fingerprint was validated when the - /// file was opened, and the chunks are static-only. - /// - private StepChunk TryLoadFromLazyReader(Map map, int chunkX, int chunkY) => - _lazyReaders.TryGetValue(map.MapID, out var reader) ? reader.TryReadChunk(chunkX, chunkY) : null; - - /// - /// Records a miss and answers whether the chunk has now earned a build. True means build and - /// serve; false means return Fallthrough_NotBuilt and let the caller take the slow path. - /// - /// A miss only counts once per Find (see ). With no Find open - /// — a direct caller, or a bake — every call counts separately. - /// - private bool ShouldPromoteAfterMiss(long chunkKey) - { - // Environment.TickCount rather than Core.TickCount: the window is wall-clock, and test and - // benchmark fixtures don't necessarily advance the game loop's tick. - var now = (uint)Environment.TickCount; - var gen = CurrentFindGeneration; - - // One hash lookup for the whole update — the entry is mutated through the ref instead - // of being re-hashed and re-probed by an indexer assignment. Safe to hold across the - // Remove below only because nothing reads it afterwards. - ref var state = ref CollectionsMarshal.GetValueRefOrNullRef(_chunkMissTracker, chunkKey); - if (!Unsafe.IsNullRef(ref state)) - { - // Same Find generation as the last touch — A* expansion is probing this chunk - // multiple times in one pathfind. Don't increment; the gate counts distinct - // Finds. Skip when gen==0 (no Find started) so legacy single-call tests still - // see incrementing behavior. - if (gen != 0 && state.LastFindGeneration == gen) - { - return false; - } - - var elapsed = now - state.LastMissTickStamp; - if (elapsed > MissPromotionWindowMs) - { - // Outside the window — restart the count. Never promotes on this call, even at - // threshold 1, matching the pre-existing gate semantics. - state.MissCount = 1; - state.LastMissTickStamp = now; - state.LastFindGeneration = gen; - return false; - } - - var newCount = (byte)Math.Min(state.MissCount + 1, byte.MaxValue); - if (newCount >= MissPromotionThreshold) - { - _chunkMissTracker.Remove(chunkKey); - return true; - } - - state.MissCount = newCount; - state.LastMissTickStamp = now; - state.LastFindGeneration = gen; - return false; - } - - if (MissPromotionThreshold <= 1) - { - return true; - } - - if (_chunkMissTracker.Count >= MaxMissTrackerEntries) - { - PruneMissTracker(now); - } - - _chunkMissTracker[chunkKey] = new ChunkMissState - { - MissCount = 1, - LastMissTickStamp = now, - LastFindGeneration = gen - }; - return false; - } - - /// - /// Drops tracker entries that have aged out of the promotion window, once the tracker hits its - /// capacity ceiling. When nothing has aged out, the whole tracker is cleared to enforce the cap - /// — that costs a few extra Fallthrough_NotBuilt returns while traffic re-establishes the hot - /// chunks, which is cheaper than letting the tracker grow without bound. - /// - private void PruneMissTracker(uint now) - { - var window = MissPromotionWindowMs; - var beforeCount = _chunkMissTracker.Count; - - foreach (var kvp in _chunkMissTracker) - { - if (now - kvp.Value.LastMissTickStamp > window) - { - _chunkMissTracker.Remove(kvp.Key); - } - } - - if (_chunkMissTracker.Count == beforeCount) - { - _chunkMissTracker.Clear(); - } - } - - private StepChunk BuildChunk(Map map, int chunkX, int chunkY) - { - var chunk = new StepChunk(); - - var baseX = chunkX << 4; - var baseY = chunkY << 4; - - // Strata accumulator, created on the first multi-Z cell so single-Z chunks pay nothing. - // strataData is rented scratch; the chunk receives an exact-size copy, so the pooled array - // never escapes this method. - ushort[] strataOffsetByCell = null; - byte[] strataData = null; - var strataLen = 0; - - // Standable surface Zs for the current cell. 16 slots is generous: clearance forces - // surfaces at least PersonHeight apart, so an sbyte Z range can't hold more than ~16. - Span surfaceZs = stackalloc sbyte[16]; - - for (var dy = 0; dy < ChunkSize; dy++) - { - for (var dx = 0; dx < ChunkSize; dx++) - { - var x = baseX + dx; - var y = baseY + dy; - var cell = (dy << 4) | dx; - - map.GetAverageZ(x, y, out _, out var avgZ, out _); - - // Anchor the cell at the surface a creature stands on, not the land average. On - // open terrain those coincide, but on static-over-land geometry — walkways, - // bridges, stair treads, upper floors — the walkable surface is the static, and - // anchoring at the land below it would make every query fall through the source-Z - // guard. Surfaces come back ascending and the lowest is the anchor; A* tracks its - // per-cell Z to match. A cell with no standable surface at all (deep water, solid - // rock) falls back to the land average so its swim data still bakes. - var surfaceCount = StepProbe.ComputeStandableSurfaceZs(map, x, y, surfaceZs); - var standingZ = surfaceCount > 0 ? surfaceZs[0] : (sbyte)Math.Clamp(avgZ, sbyte.MinValue, sbyte.MaxValue); - - var result = StepProbe.ComputeMaskAt(map, x, y, standingZ); - - chunk.WalkMask[cell] = result.WalkMask; - chunk.WetMask[cell] = result.WetMask; - chunk.SourceZ[cell] = standingZ; - chunk.WalkZN[cell] = result.WalkZ_N; - chunk.WalkZNE[cell] = result.WalkZ_NE; - chunk.WalkZE[cell] = result.WalkZ_E; - chunk.WalkZSE[cell] = result.WalkZ_SE; - chunk.WalkZS[cell] = result.WalkZ_S; - chunk.WalkZSW[cell] = result.WalkZ_SW; - chunk.WalkZW[cell] = result.WalkZ_W; - chunk.WalkZNW[cell] = result.WalkZ_NW; - chunk.SwimZN[cell] = result.SwimZ_N; - chunk.SwimZNE[cell] = result.SwimZ_NE; - chunk.SwimZE[cell] = result.SwimZ_E; - chunk.SwimZSE[cell] = result.SwimZ_SE; - chunk.SwimZS[cell] = result.SwimZ_S; - chunk.SwimZSW[cell] = result.SwimZ_SW; - chunk.SwimZW[cell] = result.SwimZ_W; - chunk.SwimZNW[cell] = result.SwimZ_NW; - - // Shore cell: a walkable surface and a water surface more than StepHeight apart. - // The main mask is baked at the walk surface, so a swimmer querying from the water - // would fail the source-Z guard; bake it a second answer from the water surface. - // An empty swim mask means the water is unreachable anyway — a dock or pier with - // too little clearance for a swimmer's body — so leave those cells at the - // NoSwimLayerCell sentinel rather than store an answer that always says "blocked". - var swimZRaw = StepProbe.ComputeSwimStandingZ(map, x, y); - if (swimZRaw != int.MinValue && Math.Abs(swimZRaw - standingZ) > StepHeight) - { - var swimSrc = (sbyte)Math.Clamp(swimZRaw, sbyte.MinValue + 1, sbyte.MaxValue); - var swimResult = StepProbe.ComputeMaskAt(map, x, y, swimSrc); - if (swimResult.WetMask != 0) - { - if (chunk.SwimSourceZ == null) - { - chunk.AllocateSwimLayer(); - } - chunk.SwimSourceZ[cell] = swimSrc; - chunk.SwimMask[cell] = swimResult.WetMask; - chunk.SwimZN_Layer[cell] = swimResult.SwimZ_N; - chunk.SwimZNE_Layer[cell] = swimResult.SwimZ_NE; - chunk.SwimZE_Layer[cell] = swimResult.SwimZ_E; - chunk.SwimZSE_Layer[cell] = swimResult.SwimZ_SE; - chunk.SwimZS_Layer[cell] = swimResult.SwimZ_S; - chunk.SwimZSW_Layer[cell] = swimResult.SwimZ_SW; - chunk.SwimZW_Layer[cell] = swimResult.SwimZ_W; - chunk.SwimZNW_Layer[cell] = swimResult.SwimZ_NW; - } - } - - // Stacked walkable surfaces — a bridge over a path, the floors of a building — - // need one stratum each so a query at any of their Zs finds an answer. Every - // surface goes in, including the lowest, because a multi-Z cell is served only - // from its strata and never from the main mask baked above. - if (surfaceCount >= 2) - { - if (strataOffsetByCell == null) - { - strataOffsetByCell = new ushort[StepChunk.CellsPerChunk]; - strataOffsetByCell.AsSpan().Fill(StepChunk.NoStrata); - // NoStrata bounds the packed data to NoStrata bytes (see StepChunk), so - // renting that much up front leaves the record guard below as the only - // bound the writes need. - strataData = STArrayPool.Shared.Rent(StepChunk.NoStrata); - } - - // One count byte plus a record per surface. A cell whose record won't fit stays - // single-Z: it keeps the main mask and falls through off its anchor surface. - var recordLength = 1 + surfaceCount * StepChunk.StratumByteLength; - if (strataLen + recordLength <= StepChunk.NoStrata) - { - strataOffsetByCell[cell] = (ushort)strataLen; - strataData[strataLen++] = (byte)surfaceCount; - for (var i = 0; i < surfaceCount; i++) - { - var sz = surfaceZs[i]; - WriteStratum(strataData, ref strataLen, sz, StepProbe.ComputeMaskAt(map, x, y, sz)); - } - } - } - } - } - - if (strataOffsetByCell != null) - { - chunk.SetStrata(strataOffsetByCell, strataData.AsSpan(0, strataLen).ToArray()); - STArrayPool.Shared.Return(strataData); - } - - _buildsTotal++; - return chunk; - } - - /// - /// Finds the cell's stratum matching — the first whose zCenter is - /// within StepHeight — and builds its mask. False when the cell has no strata or none of them - /// sit near enough, in which case the caller falls through. Reads the layout - /// writes. - /// - private static bool TryStratumHit( - StepChunk chunk, int cellIndex, sbyte sourceZ, CacheHitKind hitKind, out StepMask result - ) - { - var off = chunk.GetStrataOffset(cellIndex); - if (off == StepChunk.NoStrata) - { - result = default; - return false; - } - - var data = chunk.StrataData; - if (off >= data.Length) - { - result = default; - return false; - } - - var count = data[off]; - var entryStart = off + 1; - for (var i = 0; i < count; i++) - { - var entryOff = entryStart + i * StepChunk.StratumByteLength; - if (entryOff + StepChunk.StratumByteLength > data.Length) - { - break; - } - var zCenter = (sbyte)data[entryOff]; - if (Math.Abs(sourceZ - zCenter) > StepHeight) - { - continue; - } - result = new StepMask( - /* walkMask */ data[entryOff + 1], - /* wetMask */ data[entryOff + 2], - (sbyte)data[entryOff + 3], - (sbyte)data[entryOff + 4], - (sbyte)data[entryOff + 5], - (sbyte)data[entryOff + 6], - (sbyte)data[entryOff + 7], - (sbyte)data[entryOff + 8], - (sbyte)data[entryOff + 9], - (sbyte)data[entryOff + 10], - (sbyte)data[entryOff + 11], - (sbyte)data[entryOff + 12], - (sbyte)data[entryOff + 13], - (sbyte)data[entryOff + 14], - (sbyte)data[entryOff + 15], - (sbyte)data[entryOff + 16], - (sbyte)data[entryOff + 17], - (sbyte)data[entryOff + 18], - hitKind - ); - return true; - } - - result = default; - return false; - } - - /// - /// Packs one stratum into at , advancing it by - /// . Layout must stay in lockstep with - /// and . - /// - private static void WriteStratum(Span dst, ref int pos, sbyte zCenter, in StepMask mask) - { - dst[pos++] = (byte)zCenter; - dst[pos++] = mask.WalkMask; - dst[pos++] = mask.WetMask; - dst[pos++] = (byte)mask.WalkZ_N; - dst[pos++] = (byte)mask.WalkZ_NE; - dst[pos++] = (byte)mask.WalkZ_E; - dst[pos++] = (byte)mask.WalkZ_SE; - dst[pos++] = (byte)mask.WalkZ_S; - dst[pos++] = (byte)mask.WalkZ_SW; - dst[pos++] = (byte)mask.WalkZ_W; - dst[pos++] = (byte)mask.WalkZ_NW; - dst[pos++] = (byte)mask.SwimZ_N; - dst[pos++] = (byte)mask.SwimZ_NE; - dst[pos++] = (byte)mask.SwimZ_E; - dst[pos++] = (byte)mask.SwimZ_SE; - dst[pos++] = (byte)mask.SwimZ_S; - dst[pos++] = (byte)mask.SwimZ_SW; - dst[pos++] = (byte)mask.SwimZ_W; - dst[pos++] = (byte)mask.SwimZ_NW; - } - - private const int StepHeight = 2; -} diff --git a/Projects/UOContent/Engines/Pathing/Cache/StepCacheFile.cs b/Projects/UOContent/Engines/Pathing/Cache/StepCacheFile.cs deleted file mode 100644 index 538807448..000000000 --- a/Projects/UOContent/Engines/Pathing/Cache/StepCacheFile.cs +++ /dev/null @@ -1,776 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Runtime.InteropServices; -using Server.Compression; - -namespace Server.Engines.Pathing.Cache; - -/// -/// Binary serializer and reader for the step cache, so a shard can warm-start instead of building -/// chunks on the first pathfind through each region. Opening a file reads only the header and chunk -/// index; a chunk record is seeked and inflated when the cache actually asks for it, which keeps -/// resident memory bounded by MaxResidentChunks no matter how large the file is. -/// -/// File layout (little-endian, BufferWriter / BufferReader convention): -/// -/// Header (40 bytes): -/// u32 Magic = 0x42575300 ('SWB\0') -/// u32 Version = FormatVersion -/// u32 MapId -/// u64 Fingerprint XxHash3 over tiledata.mul and the map's own .mul / .uop files. -/// Detects both a client patch that shifts tile flags and a map edit -/// that rewrites the terrain; see ComputeFingerprint. The .mul format -/// carries no CRC of its own, so hashing is the only way to catch either. -/// u64 BakeTimestamp DateTime.UtcNow.Ticks at write time. Informational. -/// u32 ChunkCount -/// u64 IndexOffset Where the index trailer begins. -/// -/// Per chunk (ChunkCount times, variable size): -/// u32 UncompressedLen Size of the inflated record body below. -/// byte[] Payload The record body, libdeflate-compressed — or stored raw when -/// compression didn't shrink it, as happens with tiny Uniform -/// records. The reader tells the two apart by comparing the payload -/// length against UncompressedLen. -/// -/// Record body (after inflate): -/// u16 ChunkX -/// u16 ChunkY -/// u32 BuiltMultisVersion Reserved, always 0 — chunks are static-only. -/// u8 Kind 0 = Full; 2 = Uniform -/// // Uniform (Kind == 2): ~28-byte record — all 256 cells share these single values: -/// byte walkMask, wetMask; sbyte sourceZ; sbyte walkZ_N..NW (8); sbyte swimZ_N..NW (8) -/// // Full (Kind == 0) body: -/// u8 HasStrata 0 = single-Z chunk (no strata trailer); 1 = strata trailer follows -/// u8 HasSwimLayer 0 = no shore cells (no swim trailer); 1 = swim trailer follows -/// u16 ZArrayMask bit d set => base directional Z array d is present below as a -/// residual[256] block; cleared => array equals its prediction and is -/// omitted (synthesized at read). bits 0-7 = WalkZ N..NW (predicted via -/// WalkMask), bits 8-15 = SwimZ N..NW (predicted via WetMask). -/// byte WalkMask[256] -/// byte WetMask[256] -/// sbyte SourceZ[256] -/// // For each d in 0..15 with ZArrayMask bit d set, in N,NE,E,SE,S,SW,W,NW order -/// // (walk arrays first, then swim): -/// sbyte residual_d[256] reconstruct: Z_d[c] = (mask bit set ? SourceZ[c] : 0) + residual_d[c] -/// // Swim layer trailer — only when HasSwimLayer == 1 (chunks containing shore cells): -/// sbyte SwimSourceZ[256] (NoSwimLayerCell sentinel = sbyte.MinValue) -/// byte SwimMask[256] (per-cell swim mask baked at SwimSourceZ) -/// sbyte SwimZN_Layer[256]..SwimZNW_Layer[256] (8 arrays, dest-Z at swim perspective) -/// // Strata trailer — only when HasStrata == 1: -/// u16 StrataOffsetByCell[256] (NoStrata sentinel = 0xFFFF) -/// u32 StrataDataLength -/// byte StrataData[StrataDataLength] -/// For each multi-Z cell: u8 stratumCount, then stratumCount × Stratum (19 bytes): -/// sbyte zCenter -/// byte walkMask, wetMask -/// sbyte walkZ_N..NW (8) -/// sbyte swimZ_N..NW (8) -/// -/// Index trailer (8 × ChunkCount bytes), in record write order: -/// For each chunk: { u32 packedKey = (ChunkX << 16) | ChunkY, u32 recordLength } -/// The file offset is not stored — reconstructed as a cumulative sum of recordLength -/// starting at HeaderSize (the first record sits immediately after the header). -/// -/// A chunk's fixed portion runs ~783 bytes, and each directional-Z array that survives prediction -/// adds 256 more, so a Full record lands between ~783 bytes and ~4 KB. The strata trailer adds -/// 516 bytes plus roughly 30 per multi-Z cell. LastTouchedTicks is deliberately not persisted — -/// LRU state means nothing across a restart. -/// -/// Files below are treated as missing and overwritten on the -/// next save. The cache regenerates from the map data, so a format bump only costs a one-time -/// re-bake. -/// -internal static class StepCacheFile -{ - public const uint Magic = 0x42575300; // 'SWB\0' - - public const uint FormatVersion = 9; - - /// - /// Oldest format this binary will load. Anything older is treated as missing rather than - /// migrated: the cache is fully regenerable from the map data, so a re-bake is always - /// available and always correct. - /// - public const uint MinSupportedVersion = 9; - - // Record discriminator. 1 is reserved. - private const byte KindFull = 0; - private const byte KindUniform = 2; - - private const int HeaderSize = - sizeof(uint) // Magic - + sizeof(uint) // Version - + sizeof(uint) // MapId - + sizeof(ulong) // Fingerprint - + sizeof(ulong) // BakeTimestamp - + sizeof(uint) // ChunkCount - + sizeof(ulong); // IndexOffset - - // One index entry: u32 packedKey ((chunkX << 16) | chunkY) + u32 recordLength. The file offset - // isn't stored — entries sit in record write order, so the reader rebuilds each offset as a - // running sum of the lengths before it, starting at HeaderSize. - private const int IndexEntryBytes = sizeof(uint) + sizeof(uint); - - /// A chunk record minus its optional strata and swim trailers. - private const int BytesPerChunkBase = - sizeof(ushort) + sizeof(ushort) + sizeof(uint) - + sizeof(byte) + sizeof(byte) + sizeof(byte) // Kind + HasStrata + HasSwimLayer - + sizeof(ushort) // ZArrayMask - + StepChunk.CellsPerChunk // WalkMask - + StepChunk.CellsPerChunk // WetMask - + StepChunk.CellsPerChunk // SourceZ - + 8 * StepChunk.CellsPerChunk // WalkZ[8] - + 8 * StepChunk.CellsPerChunk; // SwimZ[8] - - /// - /// Reads just a .swb file's fingerprint — 20 bytes, no chunk data. False if the file is - /// missing, isn't a .swb, or is a version this binary can't load. - /// - public static bool TryReadFingerprint(string path, out ulong fingerprint) - { - fingerprint = 0; - if (!File.Exists(path)) - { - return false; - } - - try - { - using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete); - Span buf = stackalloc byte[20]; - if (stream.Read(buf) < 20) - { - return false; - } - if (BinaryPrimitives.ReadUInt32LittleEndian(buf) != Magic) - { - return false; - } - var version = BinaryPrimitives.ReadUInt32LittleEndian(buf[4..]); - if (version < MinSupportedVersion || version > FormatVersion) - { - return false; - } - // mapId is at buf[8..12], we skip; hash is at buf[12..20]. - fingerprint = BinaryPrimitives.ReadUInt64LittleEndian(buf[12..]); - return true; - } - catch - { - return false; - } - } - - /// - /// Hashes the inputs a bake depends on: tiledata.mul and the map's own .mul / .uop - /// files. A file carrying a stale hash is refused at open time, which is what catches a client - /// patch that shifts tile flags or a map editor that rewrites the terrain. Neither format has a - /// CRC of its own, so hashing is the only signal available. - /// - /// This must hash the FILES, never the in-memory / - /// . The server patches those tables at runtime (ItemFixes, - /// LOSBlocker, PotionKeg, CTF), so a hash of the live tables changes depending on when it is - /// taken — useless as a fingerprint. Those server-side patches apply identically every boot and - /// deliberately do NOT invalidate the cache; if you change one, run [PathCacheClear or bump - /// yourself. - /// - public static ulong ComputeFingerprint(int mapId) - { - var hasher = HashUtility.CreateXxHash3(); - - Span tileDataBytes = stackalloc byte[sizeof(ulong)]; - BinaryPrimitives.WriteUInt64LittleEndian(tileDataBytes, TileDataFileFingerprint()); - hasher.Append(tileDataBytes); - - // TileMatrix already streamed the map files through XxHash3 when it was built; reuse that - // rather than re-reading them. - var map = Map.Maps[mapId]; - if (map != null && map != Map.Internal && map.Tiles != null) - { - Span mapHashBytes = stackalloc byte[sizeof(ulong)]; - BinaryPrimitives.WriteUInt64LittleEndian(mapHashBytes, map.Tiles.MapFilesFingerprint); - hasher.Append(mapHashBytes); - } - - return hasher.GetCurrentHashAsUInt64(); - } - - private static ulong _tileDataFileFingerprint; - private static bool _tileDataFileFingerprintComputed; - - /// - /// XxHash3 of the raw tiledata.mul bytes, computed once — the file can't change while - /// the server runs. Returns 0 when the file is absent, which only happens in stripped test - /// hosts; a real server can't boot without it, and 0 is a fine deterministic stand-in. - /// - private static ulong TileDataFileFingerprint() - { - if (_tileDataFileFingerprintComputed) - { - return _tileDataFileFingerprint; - } - - var path = Core.FindDataFile("tiledata.mul", false); - if (path != null) - { - using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); - var hasher = HashUtility.CreateXxHash3(); - hasher.Append(fs); - _tileDataFileFingerprint = hasher.GetCurrentHashAsUInt64(); - } - - _tileDataFileFingerprintComputed = true; - return _tileDataFileFingerprint; - } - - /// - /// Writes the map's chunks to : header, then one record per chunk, then - /// the index trailer. IndexOffset isn't known until the records are down, so it goes in as a - /// placeholder and gets patched by seeking back to it. - /// - public static void Write(string path, uint mapId, ReadOnlySpan<(int chunkX, int chunkY, StepChunk chunk)> chunks) - { - Directory.CreateDirectory(Path.GetDirectoryName(path) ?? "."); - - // A rough estimate: the base record plus a small strata budget per chunk. Coastline chunks - // run ~2.5 KB over it for their swim layer, but they're a small share of any map, and the - // writer grows on overflow — under-estimating costs a few reallocs during a bake, nothing more. - var capacity = HeaderSize + (BytesPerChunkBase + 256 + IndexEntryBytes) * chunks.Length; - var w = new BufferWriter(new byte[capacity], prefixStr: false); - - w.Write(Magic); - w.Write(FormatVersion); - w.Write(mapId); - w.Write(ComputeFingerprint((int)mapId)); - w.Write((ulong)DateTime.UtcNow.Ticks); - w.Write((uint)chunks.Length); - var indexOffsetPosition = w.Position; - w.Write(0UL); // patched below, once the records are written and the index position is known - - // Each record is built into recordScratch, compressed into compScratch, then framed as - // [u32 uncompressedLen][payload]. - var packer = Deflate.Maximum; - var recordScratch = new byte[BytesPerChunkBase + 1024]; - var compScratch = new byte[packer.MaxPackSize(recordScratch.Length)]; - - // Record lengths only — the index stores no offsets, so the reader rebuilds them by - // summing these in order. - var lengths = new uint[chunks.Length]; - for (var i = 0; i < chunks.Length; i++) - { - var (chunkX, chunkY, chunk) = chunks[i]; - var start = w.Position; - WriteChunk(w, chunkX, chunkY, chunk, packer, ref recordScratch, ref compScratch); - lengths[i] = (uint)(w.Position - start); - } - - var indexOffset = (ulong)w.Position; - for (var i = 0; i < chunks.Length; i++) - { - var (chunkX, chunkY, _) = chunks[i]; - w.Write((uint)((chunkX & 0xFFFF) << 16 | chunkY & 0xFFFF)); - w.Write(lengths[i]); - } - - var totalBytes = (int)w.Position; - - w.Seek(indexOffsetPosition, SeekOrigin.Begin); - w.Write(indexOffset); - - // w.Buffer, not the array handed to the constructor: BufferWriter reallocates on growth, - // which leaves that original reference pointing at a stale array. - File.WriteAllBytes(path, w.Buffer.AsSpan(0, totalBytes).ToArray()); - } - - /// - /// Opens a .swb file, reading only its header and chunk index. Null if the file is missing, - /// isn't a loadable .swb, or is a stale bake whose fingerprint no longer matches the live tile - /// and map data. The caller owns the returned reader. - /// - public static LazyReader OpenForLazy(string path) - { - if (!File.Exists(path)) - { - return null; - } - - FileStream stream = null; - try - { - stream = new FileStream( - path, - FileMode.Open, - FileAccess.Read, - FileShare.Read | FileShare.Delete - ); - - Span headerBuf = stackalloc byte[HeaderSize]; - if (stream.Read(headerBuf) != HeaderSize) - { - stream.Dispose(); - return null; - } - - var magic = BinaryPrimitives.ReadUInt32LittleEndian(headerBuf); - if (magic != Magic) - { - stream.Dispose(); - return null; - } - var version = BinaryPrimitives.ReadUInt32LittleEndian(headerBuf[4..]); - if (version < MinSupportedVersion || version > FormatVersion) - { - stream.Dispose(); - return null; - } - - var mapId = BinaryPrimitives.ReadUInt32LittleEndian(headerBuf[8..]); - var fingerprint = BinaryPrimitives.ReadUInt64LittleEndian(headerBuf[12..]); - var bakeTimestamp = BinaryPrimitives.ReadUInt64LittleEndian(headerBuf[20..]); - var chunkCount = BinaryPrimitives.ReadUInt32LittleEndian(headerBuf[28..]); - var indexOffset = BinaryPrimitives.ReadUInt64LittleEndian(headerBuf[32..]); - - if (fingerprint != ComputeFingerprint((int)mapId)) - { - stream.Dispose(); - return null; - } - - // Pull the whole index in one read. - var indexBytes = (int)chunkCount * IndexEntryBytes; - var indexBuf = new byte[indexBytes]; - stream.Position = (long)indexOffset; - if (stream.Read(indexBuf, 0, indexBytes) != indexBytes) - { - stream.Dispose(); - return null; - } - - // Entries are in record write order and carry no offset, so rebuild each one as a - // running sum of the record lengths, starting just past the header. - var offsets = new Dictionary((int)chunkCount); - var runningOffset = (ulong)HeaderSize; - for (var i = 0; i < chunkCount; i++) - { - var entry = indexBuf.AsSpan(i * IndexEntryBytes); - var packedKey = BinaryPrimitives.ReadUInt32LittleEndian(entry); - var len = BinaryPrimitives.ReadUInt32LittleEndian(entry[4..]); - var key = PackChunkKey((int)(packedKey >> 16), (int)(packedKey & 0xFFFF)); - offsets[key] = (runningOffset, len); - runningOffset += len; - } - - return new LazyReader(stream, mapId, fingerprint, bakeTimestamp, chunkCount, offsets); - } - catch - { - stream?.Dispose(); - return null; - } - } - - private static ulong PackChunkKey(int chunkX, int chunkY) => ((ulong)(uint)chunkX << 32) | (uint)chunkY; - - /// - /// Guesses a cell's destination Z for one direction: on flat ground a step lands at the Z you - /// left from, so predict SourceZ where the direction is passable and 0 where it isn't. The - /// zero matches the baker, which only writes a slot on a successful step and leaves the rest - /// cleared. Most terrain is flat, so most predictions are exact and most residuals are 0 — - /// which is what makes the residual arrays compress away to nothing. - /// - internal static sbyte Predict(byte dirMaskByte, int bit, sbyte sourceZ) => - (dirMaskByte >> bit & 1) != 0 ? sourceZ : (sbyte)0; - - /// - /// A destination Z's difference from its prediction. Wraps deliberately: two's-complement - /// round-trips exactly for every sbyte input, so no value range is off-limits. - /// - internal static sbyte EncodeResidual(sbyte z, sbyte predict) => unchecked((sbyte)(z - predict)); - - /// Inverse of . - internal static sbyte DecodeZ(sbyte predict, sbyte residual) => unchecked((sbyte)(predict + residual)); - - /// - /// The destination-Z array for direction index d, in the canonical order the format stores them: - /// walk N..NW as 0-7, then swim N..NW as 8-15. - /// - private static sbyte[] GetBaseZArray(StepChunk c, int d) => d switch - { - 0 => c.WalkZN, 1 => c.WalkZNE, 2 => c.WalkZE, 3 => c.WalkZSE, - 4 => c.WalkZS, 5 => c.WalkZSW, 6 => c.WalkZW, 7 => c.WalkZNW, - 8 => c.SwimZN, 9 => c.SwimZNE, 10 => c.SwimZE, 11 => c.SwimZSE, - 12 => c.SwimZS, 13 => c.SwimZSW, 14 => c.SwimZW, 15 => c.SwimZNW, - _ => throw new ArgumentOutOfRangeException(nameof(d)) - }; - - /// - /// Builds one chunk's record, compresses it, and frames it as [u32 uncompressedLen][payload]. - /// When compression fails to shrink the record — as it does on the tiny Uniform ones — the raw - /// record is stored instead, and the reader tells the two apart by payload length. - /// - private static void WriteChunk( - BufferWriter w, int chunkX, int chunkY, StepChunk chunk, - LibDeflateBinding packer, ref byte[] recordScratch, ref byte[] compScratch - ) - { - var rw = new BufferWriter(recordScratch, prefixStr: false); - BuildRecord(rw, chunkX, chunkY, chunk); - recordScratch = rw.Buffer; // may have grown; hold onto the larger buffer for the next chunk - var recordLen = (int)rw.Position; - - var bound = packer.MaxPackSize(recordLen); - if (compScratch.Length < bound) - { - compScratch = new byte[bound]; - } - - var compLen = packer.Pack(compScratch, recordScratch.AsSpan(0, recordLen)); - - w.Write((uint)recordLen); - if (compLen > 0 && compLen < recordLen) - { - w.Write(compScratch.AsSpan(0, compLen)); - } - else - { - // Compression didn't help, so store the record raw. Payload length == uncompressedLen - // is how the reader recognizes that. - w.Write(recordScratch.AsSpan(0, recordLen)); - } - } - - private static void BuildRecord(BufferWriter w, int chunkX, int chunkY, StepChunk chunk) - { - w.Write((ushort)chunkX); - w.Write((ushort)chunkY); - w.Write((uint)chunk.BuiltMultisVersion); - - // A uniform chunk — every cell identical — collapses to one cell's worth of data, ~28 bytes. - // Open water and solid rock make up a lot of a map, so this is worth the branch. - if (chunk.IsUniform()) - { - w.Write(KindUniform); - w.Write(chunk.WalkMask[0]); - w.Write(chunk.WetMask[0]); - w.Write((byte)chunk.SourceZ[0]); - w.Write((byte)chunk.WalkZN[0]); - w.Write((byte)chunk.WalkZNE[0]); - w.Write((byte)chunk.WalkZE[0]); - w.Write((byte)chunk.WalkZSE[0]); - w.Write((byte)chunk.WalkZS[0]); - w.Write((byte)chunk.WalkZSW[0]); - w.Write((byte)chunk.WalkZW[0]); - w.Write((byte)chunk.WalkZNW[0]); - w.Write((byte)chunk.SwimZN[0]); - w.Write((byte)chunk.SwimZNE[0]); - w.Write((byte)chunk.SwimZE[0]); - w.Write((byte)chunk.SwimZSE[0]); - w.Write((byte)chunk.SwimZS[0]); - w.Write((byte)chunk.SwimZSW[0]); - w.Write((byte)chunk.SwimZW[0]); - w.Write((byte)chunk.SwimZNW[0]); - return; - } - - w.Write(KindFull); - - var strataOffsetByCell = chunk.GetStrataOffsetByCellForSerialization(); - var strataData = chunk.GetStrataDataForSerialization(); - var hasStrata = strataOffsetByCell != null; - var hasSwimLayer = chunk.HasSwimLayer; - w.Write((byte)(hasStrata ? 1 : 0)); - w.Write((byte)(hasSwimLayer ? 1 : 0)); - - // Each destination-Z array is stored as residuals against its prediction (see Predict). An - // array that matches its prediction everywhere — the common case on flat terrain — is - // omitted entirely, and its ZArrayMask bit stays clear so the reader synthesizes it. - ushort zArrayMask = 0; - for (var d = 0; d < 16; d++) - { - var z = GetBaseZArray(chunk, d); - var dirMask = d < 8 ? chunk.WalkMask : chunk.WetMask; - var bit = d & 7; - for (var cell = 0; cell < StepChunk.CellsPerChunk; cell++) - { - if (z[cell] != Predict(dirMask[cell], bit, chunk.SourceZ[cell])) - { - zArrayMask |= (ushort)(1 << d); - break; - } - } - } - w.Write(zArrayMask); - - w.Write(chunk.WalkMask); - w.Write(chunk.WetMask); - WriteSBytes(w, chunk.SourceZ); - - Span residual = stackalloc sbyte[StepChunk.CellsPerChunk]; - for (var d = 0; d < 16; d++) - { - if ((zArrayMask >> d & 1) == 0) - { - continue; - } - var z = GetBaseZArray(chunk, d); - var dirMask = d < 8 ? chunk.WalkMask : chunk.WetMask; - var bit = d & 7; - for (var cell = 0; cell < StepChunk.CellsPerChunk; cell++) - { - residual[cell] = EncodeResidual(z[cell], Predict(dirMask[cell], bit, chunk.SourceZ[cell])); - } - w.Write(MemoryMarshal.Cast(residual)); - } - - if (hasSwimLayer) - { - WriteSBytes(w, chunk.SwimSourceZ); - w.Write(chunk.SwimMask); - WriteSBytes(w, chunk.SwimZN_Layer); - WriteSBytes(w, chunk.SwimZNE_Layer); - WriteSBytes(w, chunk.SwimZE_Layer); - WriteSBytes(w, chunk.SwimZSE_Layer); - WriteSBytes(w, chunk.SwimZS_Layer); - WriteSBytes(w, chunk.SwimZSW_Layer); - WriteSBytes(w, chunk.SwimZW_Layer); - WriteSBytes(w, chunk.SwimZNW_Layer); - } - - if (hasStrata) - { - for (var i = 0; i < StepChunk.CellsPerChunk; i++) - { - w.Write(strataOffsetByCell[i]); - } - var dataLen = (uint)(strataData?.Length ?? 0); - w.Write(dataLen); - if (dataLen > 0) - { - w.Write(strataData); - } - } - } - - private static StepChunk ReadChunk(byte[] buffer) - { - var r = new BufferReader(buffer); - // ChunkX + ChunkY — already known from the index lookup that got us here. - r.ReadUShort(); - r.ReadUShort(); - var multisVersion = (int)r.ReadUInt(); - var kind = r.ReadByte(); - - var chunk = new StepChunk { BuiltMultisVersion = multisVersion }; - - if (kind == KindUniform) // one cell's values, broadcast to all 256 - { - Array.Fill(chunk.WalkMask, r.ReadByte()); - Array.Fill(chunk.WetMask, r.ReadByte()); - Array.Fill(chunk.SourceZ, (sbyte)r.ReadByte()); - Array.Fill(chunk.WalkZN, (sbyte)r.ReadByte()); - Array.Fill(chunk.WalkZNE, (sbyte)r.ReadByte()); - Array.Fill(chunk.WalkZE, (sbyte)r.ReadByte()); - Array.Fill(chunk.WalkZSE, (sbyte)r.ReadByte()); - Array.Fill(chunk.WalkZS, (sbyte)r.ReadByte()); - Array.Fill(chunk.WalkZSW, (sbyte)r.ReadByte()); - Array.Fill(chunk.WalkZW, (sbyte)r.ReadByte()); - Array.Fill(chunk.WalkZNW, (sbyte)r.ReadByte()); - Array.Fill(chunk.SwimZN, (sbyte)r.ReadByte()); - Array.Fill(chunk.SwimZNE, (sbyte)r.ReadByte()); - Array.Fill(chunk.SwimZE, (sbyte)r.ReadByte()); - Array.Fill(chunk.SwimZSE, (sbyte)r.ReadByte()); - Array.Fill(chunk.SwimZS, (sbyte)r.ReadByte()); - Array.Fill(chunk.SwimZSW, (sbyte)r.ReadByte()); - Array.Fill(chunk.SwimZW, (sbyte)r.ReadByte()); - Array.Fill(chunk.SwimZNW, (sbyte)r.ReadByte()); - return chunk; - } - - var hasStrata = r.ReadByte() != 0; - var hasSwimLayer = r.ReadByte() != 0; - var zArrayMask = r.ReadUShort(); - - r.Read(chunk.WalkMask); - r.Read(chunk.WetMask); - ReadSBytes(r, chunk.SourceZ); - - // Inverse of the write path: a stored array carries residuals to add back to the - // prediction, an omitted one IS the prediction. - Span residual = stackalloc sbyte[StepChunk.CellsPerChunk]; - for (var d = 0; d < 16; d++) - { - var z = GetBaseZArray(chunk, d); - var dirMask = d < 8 ? chunk.WalkMask : chunk.WetMask; - var bit = d & 7; - if ((zArrayMask >> d & 1) != 0) - { - r.Read(MemoryMarshal.Cast(residual)); - for (var cell = 0; cell < StepChunk.CellsPerChunk; cell++) - { - z[cell] = DecodeZ(Predict(dirMask[cell], bit, chunk.SourceZ[cell]), residual[cell]); - } - } - else - { - for (var cell = 0; cell < StepChunk.CellsPerChunk; cell++) - { - z[cell] = Predict(dirMask[cell], bit, chunk.SourceZ[cell]); - } - } - } - - if (hasSwimLayer) - { - chunk.AllocateSwimLayer(); - ReadSBytes(r, chunk.SwimSourceZ); - r.Read(chunk.SwimMask); - ReadSBytes(r, chunk.SwimZN_Layer); - ReadSBytes(r, chunk.SwimZNE_Layer); - ReadSBytes(r, chunk.SwimZE_Layer); - ReadSBytes(r, chunk.SwimZSE_Layer); - ReadSBytes(r, chunk.SwimZS_Layer); - ReadSBytes(r, chunk.SwimZSW_Layer); - ReadSBytes(r, chunk.SwimZW_Layer); - ReadSBytes(r, chunk.SwimZNW_Layer); - } - - if (hasStrata) - { - var offsets = new ushort[StepChunk.CellsPerChunk]; - for (var i = 0; i < offsets.Length; i++) - { - offsets[i] = r.ReadUShort(); - } - var dataLen = (int)r.ReadUInt(); - var data = new byte[dataLen]; - if (dataLen > 0) - { - r.Read(data); - } - chunk.SetStrata(offsets, data); - } - - return chunk; - } - - private static void WriteSBytes(BufferWriter w, sbyte[] arr) => - w.Write(MemoryMarshal.Cast(arr.AsSpan())); - - private static void ReadSBytes(BufferReader r, sbyte[] arr) => - r.Read(MemoryMarshal.Cast(arr.AsSpan())); - - /// - /// An open .swb file: the stream plus the chunk index. Only the records actually asked for are - /// ever read or inflated. Dispose releases the stream. - /// - internal sealed class LazyReader : IDisposable - { - private FileStream _stream; - private readonly Dictionary _offsets; - private byte[] _buffer; // the raw framed record as it sits on disk - private byte[] _bodyBuffer; // that record, inflated, ready for ReadChunk - - public uint MapId { get; } - public ulong Fingerprint { get; } - public ulong BakeTimestamp { get; } - public uint ChunkCount { get; } - public int IndexedChunkCount => _offsets.Count; - - public bool Has(int chunkX, int chunkY) => _offsets.ContainsKey(PackChunkKey(chunkX, chunkY)); - - /// Every (chunkX, chunkY) the file holds. Used to preload the whole file. - public IEnumerable<(int chunkX, int chunkY)> EnumerateChunkCoords() - { - foreach (var key in _offsets.Keys) - { - yield return ((int)(key >> 32), (int)(key & 0xFFFFFFFF)); - } - } - - internal LazyReader( - FileStream stream, uint mapId, ulong fingerprint, ulong bakeTimestamp, - uint chunkCount, Dictionary offsets - ) - { - _stream = stream; - MapId = mapId; - Fingerprint = fingerprint; - BakeTimestamp = bakeTimestamp; - ChunkCount = chunkCount; - _offsets = offsets; - _buffer = new byte[BytesPerChunkBase]; - _bodyBuffer = new byte[BytesPerChunkBase]; - } - - /// - /// Reads one chunk from the file, or null if the file has no record for it. One seek and - /// one read, sized to the record's indexed length. - /// - public StepChunk TryReadChunk(int chunkX, int chunkY) - { - if (_stream == null) - { - return null; - } - - var key = PackChunkKey(chunkX, chunkY); - if (!_offsets.TryGetValue(key, out var entry)) - { - return null; - } - - if (entry.length > _buffer.Length) - { - _buffer = new byte[entry.length]; - } - - _stream.Position = (long)entry.offset; - var read = _stream.Read(_buffer, 0, (int)entry.length); - if (read < (int)entry.length || entry.length < sizeof(uint)) - { - return null; - } - - // [u32 uncompressedLen][payload], where the payload is compressed unless its length - // already equals uncompressedLen — then it was stored raw. - var uncompressedLen = (int)BinaryPrimitives.ReadUInt32LittleEndian(_buffer); - var payloadLen = (int)entry.length - sizeof(uint); - if (_bodyBuffer.Length < uncompressedLen) - { - _bodyBuffer = new byte[uncompressedLen]; - } - - if (payloadLen == uncompressedLen) - { - Array.Copy(_buffer, sizeof(uint), _bodyBuffer, 0, uncompressedLen); - } - else - { - // Deflate.Standard, not .Maximum: the level only affects packing, and inflate has - // to accept whatever the writer produced regardless. - var result = Deflate.Standard.Unpack( - _bodyBuffer.AsSpan(0, uncompressedLen), - _buffer.AsSpan(sizeof(uint), payloadLen), - out var produced - ); - if (result != LibDeflateResult.Success || produced != uncompressedLen) - { - return null; - } - } - - return ReadChunk(_bodyBuffer); - } - - public void Dispose() - { - _stream?.Dispose(); - _stream = null; - _buffer = null; - _bodyBuffer = null; - } - } -} diff --git a/Projects/UOContent/Engines/Pathing/Cache/StepChunk.cs b/Projects/UOContent/Engines/Pathing/Cache/StepChunk.cs deleted file mode 100644 index 80d42f8e0..000000000 --- a/Projects/UOContent/Engines/Pathing/Cache/StepChunk.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System; - -namespace Server.Engines.Pathing.Cache; - -/// -/// Per-chunk storage backing : walk + swim masks and destination Zs for -/// each of the 256 cells in a 16x16 chunk, plus the optional multi-Z strata and swim layers and -/// the LRU timestamp. -/// -internal sealed class StepChunk -{ - public const int CellsPerChunk = 256; // 16 x 16 - - /// Bit i of WalkMask[c]: a default walker can step from cell c to neighbour (Direction)i. - /// Raw — no diagonal corner-cut applied, so callers must AND the partner bits themselves. - public readonly byte[] WalkMask = new byte[CellsPerChunk]; - - /// Bit i of WetMask[c]: a swim-only mob can step from cell c to neighbour (Direction)i. - public readonly byte[] WetMask = new byte[CellsPerChunk]; - - public readonly sbyte[] SourceZ = new sbyte[CellsPerChunk]; - - public readonly sbyte[] WalkZN = new sbyte[CellsPerChunk]; - public readonly sbyte[] WalkZNE = new sbyte[CellsPerChunk]; - public readonly sbyte[] WalkZE = new sbyte[CellsPerChunk]; - public readonly sbyte[] WalkZSE = new sbyte[CellsPerChunk]; - public readonly sbyte[] WalkZS = new sbyte[CellsPerChunk]; - public readonly sbyte[] WalkZSW = new sbyte[CellsPerChunk]; - public readonly sbyte[] WalkZW = new sbyte[CellsPerChunk]; - public readonly sbyte[] WalkZNW = new sbyte[CellsPerChunk]; - - public readonly sbyte[] SwimZN = new sbyte[CellsPerChunk]; - public readonly sbyte[] SwimZNE = new sbyte[CellsPerChunk]; - public readonly sbyte[] SwimZE = new sbyte[CellsPerChunk]; - public readonly sbyte[] SwimZSE = new sbyte[CellsPerChunk]; - public readonly sbyte[] SwimZS = new sbyte[CellsPerChunk]; - public readonly sbyte[] SwimZSW = new sbyte[CellsPerChunk]; - public readonly sbyte[] SwimZW = new sbyte[CellsPerChunk]; - public readonly sbyte[] SwimZNW = new sbyte[CellsPerChunk]; - - /// - /// Marks a cell with no swim-layer entry, in a chunk that has the layer. - /// - /// The swim layer exists only on chunks holding at least one shore cell — a cell with both a - /// walkable surface and a water surface more than StepHeight apart. A swim query there sits - /// too far from the primary SourceZ to pass the source-Z guard, so the layer carries a second - /// mask and destination-Z set computed from the water surface instead. Chunks with no shore - /// cells leave every swim-layer array null. - /// - public const sbyte NoSwimLayerCell = sbyte.MinValue; - - private sbyte[] _swimSourceZ; - private byte[] _swimMask; - private sbyte[] _swimZN_extra; - private sbyte[] _swimZNE_extra; - private sbyte[] _swimZE_extra; - private sbyte[] _swimZSE_extra; - private sbyte[] _swimZS_extra; - private sbyte[] _swimZSW_extra; - private sbyte[] _swimZW_extra; - private sbyte[] _swimZNW_extra; - - /// True when this chunk has at least one shore cell with a populated swim layer. - public bool HasSwimLayer => _swimSourceZ != null; - - /// Per-cell water-surface standing Z (or ). Null when chunk has no swim layer. - public sbyte[] SwimSourceZ => _swimSourceZ; - /// Per-cell swim mask computed at . Null when chunk has no swim layer. - public byte[] SwimMask => _swimMask; - public sbyte[] SwimZN_Layer => _swimZN_extra; - public sbyte[] SwimZNE_Layer => _swimZNE_extra; - public sbyte[] SwimZE_Layer => _swimZE_extra; - public sbyte[] SwimZSE_Layer => _swimZSE_extra; - public sbyte[] SwimZS_Layer => _swimZS_extra; - public sbyte[] SwimZSW_Layer => _swimZSW_extra; - public sbyte[] SwimZW_Layer => _swimZW_extra; - public sbyte[] SwimZNW_Layer => _swimZNW_extra; - - /// - /// Allocates the swim-layer arrays and seeds with - /// . Called on the first shore cell found in this chunk. - /// - internal void AllocateSwimLayer() - { - if (_swimSourceZ != null) - { - return; - } - _swimSourceZ = new sbyte[CellsPerChunk]; - _swimMask = new byte[CellsPerChunk]; - _swimZN_extra = new sbyte[CellsPerChunk]; - _swimZNE_extra = new sbyte[CellsPerChunk]; - _swimZE_extra = new sbyte[CellsPerChunk]; - _swimZSE_extra = new sbyte[CellsPerChunk]; - _swimZS_extra = new sbyte[CellsPerChunk]; - _swimZSW_extra = new sbyte[CellsPerChunk]; - _swimZW_extra = new sbyte[CellsPerChunk]; - _swimZNW_extra = new sbyte[CellsPerChunk]; - for (var i = 0; i < CellsPerChunk; i++) - { - _swimSourceZ[i] = NoSwimLayerCell; - } - } - - /// - /// Marks a single-Z cell: no strata, read the main Walk/Wet arrays instead. Because this - /// takes ushort.MaxValue, a real strata offset is at most NoStrata - 1, which bounds - /// to NoStrata bytes. - /// - public const ushort NoStrata = ushort.MaxValue; - - /// - /// Length-256 table mapping a cell to the byte offset in where its - /// strata begin, or . Null when no cell in the chunk is multi-Z. - /// - private ushort[] _strataOffsetByCell; - - /// - /// Packed strata for the multi-Z cells. Per cell: u8 stratumCount, then stratumCount records - /// of bytes — sbyte zCenter, byte walkMask, byte wetMask, - /// sbyte walkZ_N..NW (8), sbyte swimZ_N..NW (8). - /// - private byte[] _strataData; - - /// Reserved. Chunks are static-only, so this is always 0. - public int BuiltMultisVersion; - - /// Refreshed on every query that reaches this chunk. Drives LRU eviction. - public long LastTouchedTicks; - - /// Size in bytes of one Stratum record in StrataData. - public const int StratumByteLength = 1 + 1 + 1 + 8 + 8; - - public bool IsCellMultiZ(int cellIndex) => GetStrataOffset(cellIndex) != NoStrata; - - public ushort GetStrataOffset(int cellIndex) => _strataOffsetByCell == null ? NoStrata : _strataOffsetByCell[cellIndex]; - - public ReadOnlySpan StrataData => - _strataData == null ? ReadOnlySpan.Empty : _strataData.AsSpan(); - - /// - /// Sets the chunk's strata in one shot. must be length 256, - /// carrying for single-Z cells. Pass null/null to clear. - /// - internal void SetStrata(ushort[] offsetByCell, byte[] data) - { - _strataOffsetByCell = offsetByCell; - _strataData = data; - } - - /// Serialization hook: the raw offset array, or null if the chunk has no strata. - internal ushort[] GetStrataOffsetByCellForSerialization() => _strataOffsetByCell; - - /// Serialization hook: the raw data array, or null if the chunk has no strata. - internal byte[] GetStrataDataForSerialization() => _strataData; - - /// - /// True when all 256 cells share one value across WalkMask, WetMask, SourceZ and every - /// directional-Z array, with no strata and no swim layer — open water or solid rock, mostly. - /// collapses such a chunk to a ~28-byte record. A swim layer - /// disqualifies a chunk outright: its per-cell shore data would not survive the collapse. - /// - internal bool IsUniform() => _strataOffsetByCell == null - && !HasSwimLayer - && AllSame(WalkMask) && AllSame(WetMask) && AllSame(SourceZ) - && AllSame(WalkZN) && AllSame(WalkZNE) && AllSame(WalkZE) && AllSame(WalkZSE) - && AllSame(WalkZS) && AllSame(WalkZSW) && AllSame(WalkZW) && AllSame(WalkZNW) - && AllSame(SwimZN) && AllSame(SwimZNE) && AllSame(SwimZE) && AllSame(SwimZSE) - && AllSame(SwimZS) && AllSame(SwimZSW) && AllSame(SwimZW) && AllSame(SwimZNW); - - // "All 256 cells equal" via SIMD-accelerated ContainsAnyExcept (skip cell 0, the reference). - private static bool AllSame(byte[] a) => a.Length < 2 || !a.AsSpan(1).ContainsAnyExcept(a[0]); - - private static bool AllSame(sbyte[] a) => a.Length < 2 || !a.AsSpan(1).ContainsAnyExcept(a[0]); -} diff --git a/Projects/UOContent/Engines/Pathing/Cache/StepMask.cs b/Projects/UOContent/Engines/Pathing/Cache/StepMask.cs deleted file mode 100644 index 6817f9ae0..000000000 --- a/Projects/UOContent/Engines/Pathing/Cache/StepMask.cs +++ /dev/null @@ -1,85 +0,0 @@ -namespace Server.Engines.Pathing.Cache; - -/// -/// Per-cell, per-direction walkability baked by and stored by -/// . Two rule sets travel together: WalkMask + WalkZ_* for a default -/// walker (cantWalk=false, canSwim=false), WetMask + SwimZ_* for a swim-only mob -/// (cantWalk=true, canSwim=true). Callers overlay whichever applies to the mobile. -/// -public readonly struct StepMask( - byte walkMask, - byte wetMask, - sbyte walkZN, - sbyte walkZNE, - sbyte walkZE, - sbyte walkZSE, - sbyte walkZS, - sbyte walkZSW, - sbyte walkZW, - sbyte walkZNW, - sbyte swimZN, - sbyte swimZNE, - sbyte swimZE, - sbyte swimZSE, - sbyte swimZS, - sbyte swimZSW, - sbyte swimZW, - sbyte swimZNW, - CacheHitKind hitKind = CacheHitKind.Hit -) -{ - public readonly byte WalkMask = walkMask; - public readonly byte WetMask = wetMask; - public readonly sbyte WalkZ_N = walkZN; - public readonly sbyte WalkZ_NE = walkZNE; - public readonly sbyte WalkZ_E = walkZE; - public readonly sbyte WalkZ_SE = walkZSE; - public readonly sbyte WalkZ_S = walkZS; - public readonly sbyte WalkZ_SW = walkZSW; - public readonly sbyte WalkZ_W = walkZW; - public readonly sbyte WalkZ_NW = walkZNW; - public readonly sbyte SwimZ_N = swimZN; - public readonly sbyte SwimZ_NE = swimZNE; - public readonly sbyte SwimZ_E = swimZE; - public readonly sbyte SwimZ_SE = swimZSE; - public readonly sbyte SwimZ_S = swimZS; - public readonly sbyte SwimZ_SW = swimZSW; - public readonly sbyte SwimZ_W = swimZW; - public readonly sbyte SwimZ_NW = swimZNW; - public readonly CacheHitKind HitKind = hitKind; - - /// - /// True when the cache produced a usable answer. False on any Fallthrough_*, where the - /// payload is all zeroes and the caller must resolve this cell via the slow path. - /// - public bool IsHit => HitKind <= CacheHitKind.Miss_DirtyRebuild; - - public bool IsWalkable(Direction d) => (WalkMask & (1 << (int)d)) != 0; - public bool IsSwimmable(Direction d) => (WetMask & (1 << (int)d)) != 0; - - public sbyte GetWalkZ(Direction d) => d switch - { - Direction.North => WalkZ_N, - Direction.Right => WalkZ_NE, - Direction.East => WalkZ_E, - Direction.Down => WalkZ_SE, - Direction.South => WalkZ_S, - Direction.Left => WalkZ_SW, - Direction.West => WalkZ_W, - Direction.Up => WalkZ_NW, - _ => 0 - }; - - public sbyte GetSwimZ(Direction d) => d switch - { - Direction.North => SwimZ_N, - Direction.Right => SwimZ_NE, - Direction.East => SwimZ_E, - Direction.Down => SwimZ_SE, - Direction.South => SwimZ_S, - Direction.Left => SwimZ_SW, - Direction.West => SwimZ_W, - Direction.Up => SwimZ_NW, - _ => 0 - }; -} diff --git a/Projects/UOContent/Engines/Pathing/Cache/StepProbe.cs b/Projects/UOContent/Engines/Pathing/Cache/StepProbe.cs deleted file mode 100644 index 54a880b51..000000000 --- a/Projects/UOContent/Engines/Pathing/Cache/StepProbe.cs +++ /dev/null @@ -1,435 +0,0 @@ -using System; -using CalcMoves = Server.Movement.Movement; - -namespace Server.Engines.Pathing.Cache; - -/// -/// Computes the 8-direction "can step" mask and destination Zs for a single cell from land and -/// statics alone. Mirrors .Check minus the item and mobile collision -/// phases, which belong to the caller's dynamic-obstacle pass. -/// -/// Multis (houses, boats) are excluded from the static bake because they are dynamic content; -/// cells they cover route to the live movement path via 's multi halo. -/// is the opt-in exception for those cells. -/// -/// Each call bakes both rule sets: walker (canSwim=false, cantWalk=false) and swim-only -/// (canSwim=true, cantWalk=true). Diagonal corner-cut is not applied — callers hold the partner -/// bits in the same mask byte and combine them at query time. -/// -public static class StepProbe -{ - private const int PersonHeight = 16; - private const int StepHeight = 2; - - /// - /// Writes the surface Zs at (x, y) a default walker can actually stand on into - /// , ascending, and returns the count. A candidate surface — the - /// walkable land centre, or any walkable static's top — qualifies only if a PersonHeight - /// envelope above it is clear of impassable statics. - /// - /// The clearance test is what makes this the exact set of standing Zs the slow path can - /// resolve to: it drops surfaces a creature cannot occupy, like the land beneath a sewer - /// walkway or a low bridge. That in turn means two surviving surfaces are always at least - /// PersonHeight apart (an upper surface any closer would have taken the lower one's - /// clearance away), so one ascending pass with a duplicate skip suffices. - /// - /// The baker anchors each cell here so static-over-land geometry — walkways, bridges, raised - /// foundations, upper floors — bakes at the Z a creature stands on rather than the land average. - /// - public static int ComputeStandableSurfaceZs(Map map, int x, int y, Span zs) - { - if (map == null || map == Map.Internal) - { - return 0; - } - if (x < 0 || y < 0 || x >= map.Width || y >= map.Height) - { - return 0; - } - - Span cand = stackalloc int[16]; - var count = 0; - - var landTile = map.Tiles.GetLandTile(x, y); - var landFlags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; - if (!landTile.Ignored && (landFlags & TileFlag.Impassable) == 0) - { - map.GetAverageZ(x, y, out _, out var landCenter, out _); - cand[count++] = landCenter; - } - - foreach (var tile in map.Tiles.GetStaticTiles(x, y)) - { - if (count >= cand.Length) - { - break; - } - var data = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - if (!data.Surface || data.Impassable) - { - continue; - } - cand[count++] = tile.Z + data.CalcHeight; - } - - if (count == 0) - { - return 0; - } - - cand[..count].Sort(); - - var n = 0; - for (var i = 0; i < count && n < zs.Length; i++) - { - var cz = (sbyte)Math.Clamp(cand[i], sbyte.MinValue + 1, sbyte.MaxValue); - if (n > 0 && zs[n - 1] == cz) - { - continue; - } - // Standable iff the creature's PersonHeight body envelope above this surface is - // free of impassable statics. The surface itself never blocks (its top == cz, - // which is the envelope floor, not inside it). - if (StaticsBlockAt(map, x, y, cz, cz + PersonHeight)) - { - continue; - } - zs[n++] = cz; - } - - return n; - } - - public static StepMask ComputeMaskAt(Map map, int x, int y, sbyte sourceZ) => - ComputeMaskCore(map, x, y, sourceZ, includeMultis: false); - - /// - /// Multi-aware counterpart to , for cells a multi covers or - /// neighbours: folds house/boat component tiles into the same surface/step logic. Builds the - /// whole 8-direction mask in one pass, where the slow path would run CheckMovement eight times. - /// - public static StepMask ComputeMultiMaskAt(Map map, int x, int y, sbyte sourceZ) => - ComputeMaskCore(map, x, y, sourceZ, includeMultis: true); - - /// - /// Shared 8-direction mask builder behind and - /// . is the only difference: - /// it swaps the tile source to GetStaticAndMultiTiles so house and boat components participate. - /// - private static StepMask ComputeMaskCore(Map map, int x, int y, sbyte sourceZ, bool includeMultis) - { - if (map == null || map == Map.Internal) - { - return default; - } - - var srcTiles = includeMultis ? map.Tiles.GetStaticAndMultiTiles(x, y) : map.Tiles.GetStaticTiles(x, y); - - GetStaticStartZ(map, x, y, sourceZ, srcTiles, canSwim: false, cantWalk: false, - out var walkStartZ, out var walkStartTop, out _); - GetStaticStartZ(map, x, y, sourceZ, srcTiles, canSwim: true, cantWalk: true, - out var swimStartZ, out var swimStartTop, out _); - - byte walkMask = 0; - byte wetMask = 0; - Span walkZs = stackalloc sbyte[8]; - Span swimZs = stackalloc sbyte[8]; - // stackalloc is not zero-initialized, and the loop below writes a slot only where the - // step succeeds, so blocked directions would otherwise carry stack garbage. - walkZs.Clear(); - swimZs.Clear(); - - for (var d = 0; d < 8; d++) - { - var dx = x; - var dy = y; - CalcMoves.Offset((Direction)d, ref dx, ref dy); - - var dTiles = includeMultis ? map.Tiles.GetStaticAndMultiTiles(dx, dy) : map.Tiles.GetStaticTiles(dx, dy); - - if (CheckStaticStep(map, dx, dy, dTiles, walkStartZ, walkStartTop, - canSwim: false, cantWalk: false, out var walkZ)) - { - walkMask |= (byte)(1 << d); - walkZs[d] = (sbyte)walkZ; - } - - if (CheckStaticStep(map, dx, dy, dTiles, swimStartZ, swimStartTop, - canSwim: true, cantWalk: true, out var swimZ)) - { - wetMask |= (byte)(1 << d); - swimZs[d] = (sbyte)swimZ; - } - } - - return new StepMask( - walkMask, wetMask, - walkZs[0], walkZs[1], walkZs[2], walkZs[3], - walkZs[4], walkZs[5], walkZs[6], walkZs[7], - swimZs[0], swimZs[1], swimZs[2], swimZs[3], - swimZs[4], swimZs[5], swimZs[6], swimZs[7] - ); - } - - /// - /// The standing-Z a default walker at (x, y) resolves to under the slow path's - /// surface-selection rules: paver Z+1 over paver-on-ground, land centre on bare land. - /// The baker anchors cells with instead, which is - /// clearance-aware; this remains the direct MovementImpl equivalent for parity checks. - /// - public static int ComputeStandingZ(Map map, int x, int y, int locZ) - { - GetStaticStartZ(map, x, y, locZ, map.Tiles.GetStaticTiles(x, y), canSwim: false, cantWalk: false, out _, out _, out var zCenter); - - return zCenter; - } - - /// - /// Returns the water-surface standing Z at (x, y) — the Z a swim-only mob would stand - /// at on this cell — or if no water surface exists. Used - /// by to detect shore cells (cells with both walk and swim - /// surfaces separated by > StepHeight) and bake their swim layer at swim-perspective Z. - /// - public static int ComputeSwimStandingZ(Map map, int x, int y) - { - if (map == null || map == Map.Internal || x < 0 || y < 0 || x >= map.Width || y >= map.Height) - { - return int.MinValue; - } - - // Land tile flagged Wet — its center Z is the swim surface. - var landTile = map.Tiles.GetLandTile(x, y); - var landFlags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; - if (!landTile.Ignored && (landFlags & TileFlag.Wet) != 0) - { - map.GetAverageZ(x, y, out _, out var landCenter, out _); - return landCenter; - } - - // Otherwise scan statics for a wet surface. - foreach (var tile in map.Tiles.GetStaticTiles(x, y)) - { - var data = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - if (data.Wet) - { - return tile.Z + data.CalcHeight; - } - } - - return int.MinValue; - } - - /// - /// Mirrors GetStartZ from MovementImpl, parameterized by canSwim / cantWalk. - /// - private static void GetStaticStartZ( - Map map, int x, int y, int locZ, Map.StaticTileEnumerable tiles, - bool canSwim, bool cantWalk, out int zLow, out int zTop, out int zCenter - ) - { - var landTile = map.Tiles.GetLandTile(x, y); - var flags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; - var impassable = (flags & TileFlag.Impassable) != 0; - - // Mirrors MovementImpl: impassable + swim on water is OK; otherwise block on - // cantWalk or impassable. - var landBlocks = (cantWalk || impassable) && !(impassable && canSwim && (flags & TileFlag.Wet) != 0); - - map.GetAverageZ(x, y, out var landZ, out var landCenter, out var landTop); - - var considerLand = !landTile.Ignored; - - zCenter = zLow = zTop = 0; - var isSet = false; - - if (considerLand && !landBlocks && locZ >= landCenter) - { - zLow = landZ; - zCenter = landCenter; - zTop = landTop; - isSet = true; - } - - foreach (var tile in tiles) - { - var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - var calcTop = tile.Z + id.CalcHeight; - - if (isSet && calcTop < zCenter || locZ < calcTop || !id.Surface && !(canSwim && id.Wet)) - { - continue; - } - - zLow = tile.Z; - zCenter = calcTop; - - var top = tile.Z + id.Height; - - if (!isSet || top > zTop) - { - zTop = top; - } - - isSet = true; - } - - if (!isSet) - { - zLow = zTop = locZ; - } - else if (locZ > zTop) - { - zTop = locZ; - } - } - - /// - /// Mirrors MovementImpl.Check for static tiles only, parameterized by canSwim / cantWalk. - /// Items and mobile collision phases are omitted. - /// - private static bool CheckStaticStep( - Map map, int x, int y, Map.StaticTileEnumerable tiles, int startZ, int startTop, - bool canSwim, bool cantWalk, out int newZ - ) - { - newZ = 0; - - if (x < 0 || y < 0 || x >= map.Width || y >= map.Height) - { - return false; - } - - var landTile = map.Tiles.GetLandTile(x, y); - var flags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; - var impassable = (flags & TileFlag.Impassable) != 0; - - var landBlocks = (cantWalk || impassable) && !(impassable && canSwim && (flags & TileFlag.Wet) != 0); - - var considerLand = !landTile.Ignored; - - map.GetAverageZ(x, y, out var landZ, out var landCenter, out _); - - var moveIsOk = false; - - var stepTop = startTop + StepHeight; - var checkTop = startZ + PersonHeight; - - int testTop; - - foreach (var tile in tiles) - { - var itemData = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - var notWater = !itemData.Wet; - - // Mirrors MovementImpl: skip if not a passable surface AND not swimmable water, - // OR if the mobile can't walk and this isn't water. - if ((!itemData.Surface || itemData.Impassable) && (!canSwim || notWater) - || cantWalk && notWater) - { - continue; - } - - var itemZ = tile.Z; - var itemTop = itemZ; - var ourZ = itemZ + itemData.CalcHeight; - testTop = checkTop; - - if (moveIsOk) - { - var cmp = Math.Abs(ourZ - startZ) - Math.Abs(newZ - startZ); - - if (cmp > 0 || cmp == 0 && ourZ > newZ) - { - continue; - } - } - - if (ourZ + PersonHeight > testTop) - { - testTop = ourZ + PersonHeight; - } - - if (!itemData.Bridge) - { - itemTop += itemData.Height; - } - - if (stepTop < itemTop) - { - continue; - } - - var landCheck = itemZ + Math.Min(itemData.Height, StepHeight); - - if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) - { - continue; - } - - if (StaticsBlockAt(map, x, y, ourZ, testTop)) - { - continue; - } - - newZ = ourZ; - moveIsOk = true; - } - - if (!considerLand || landBlocks || stepTop < landZ) - { - return moveIsOk; - } - - testTop = checkTop; - - if (landCenter + PersonHeight > testTop) - { - testTop = landCenter + PersonHeight; - } - - var shouldCheck = true; - - if (moveIsOk) - { - var cmp = Math.Abs(landCenter - startZ) - Math.Abs(newZ - startZ); - - if (cmp > 0 || cmp == 0 && landCenter > newZ) - { - shouldCheck = false; - } - } - - if (shouldCheck && !StaticsBlockAt(map, x, y, landCenter, testTop)) - { - newZ = landCenter; - moveIsOk = true; - } - - return moveIsOk; - } - - /// - /// Mirrors the static-tile portion of IsOk: returns true if any static tile at (x,y) - /// has ImpassableSurface and overlaps the vertical range (ourZ, testTop). - /// - private static bool StaticsBlockAt(Map map, int x, int y, int ourZ, int testTop) - { - foreach (var check in map.Tiles.GetStaticAndMultiTiles(x, y)) - { - var itemData = TileData.ItemTable[check.ID & TileData.MaxItemValue]; - - if (itemData.ImpassableSurface) - { - var checkZ = check.Z; - var checkTop = checkZ + itemData.CalcHeight; - - if (checkTop > ourZ && testTop > checkZ) - { - return true; - } - } - } - - return false; - } -} diff --git a/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs b/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs new file mode 100644 index 000000000..cd3e4cca4 --- /dev/null +++ b/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs @@ -0,0 +1,352 @@ +using System.Collections; +using Server.Mobiles; +using CalcMoves = Server.Movement.Movement; +using MoveImpl = Server.Movement.MovementImpl; + +namespace Server.PathAlgorithms.FastAStar +{ + public struct PathNode + { + public int cost, total; + public int parent, next, prev; + public int z; + } + + public class FastAStarAlgorithm : PathAlgorithm + { + private const int MaxDepth = 300; + private const int AreaSize = 38; + + private const int NodeCount = AreaSize * AreaSize * PlaneCount; + + private const int PlaneOffset = 128; + private const int PlaneCount = 13; + private const int PlaneHeight = 20; + public static PathAlgorithm Instance = new FastAStarAlgorithm(); + + private static readonly Direction[] _path = new Direction[AreaSize * AreaSize]; + private static readonly PathNode[] _nodes = new PathNode[NodeCount]; + private static readonly BitArray _touched = new(NodeCount); + private static readonly BitArray _onOpen = new(NodeCount); + private static readonly int[] _successors = new int[8]; + + private static int _xOffset; + private static int _yOffset; + private static int _openList; + + private Point3D _goal; + + public int Heuristic(int x, int y, int z) + { + x -= _goal.X - _xOffset; + y -= _goal.Y - _yOffset; + z -= _goal.Z; + + x *= 11; + y *= 11; + + return x * x + y * y + z * z; + } + + public override bool CheckCondition(Mobile m, Map map, Point3D start, Point3D goal) => + Utility.InRange(start, goal, AreaSize); + + private void RemoveFromChain(int node) + { + if (node is < 0 or >= NodeCount) + { + return; + } + + if (!_touched[node] || !_onOpen[node]) + { + return; + } + + var prev = _nodes[node].prev; + var next = _nodes[node].next; + + if (_openList == node) + { + _openList = next; + } + + if (prev != -1) + { + _nodes[prev].next = next; + } + + if (next != -1) + { + _nodes[next].prev = prev; + } + + _nodes[node].prev = -1; + _nodes[node].next = -1; + } + + private void AddToChain(int node) + { + if (node is < 0 or >= NodeCount) + { + return; + } + + RemoveFromChain(node); + + if (_openList != -1) + { + _nodes[_openList].prev = node; + } + + _nodes[node].next = _openList; + _nodes[node].prev = -1; + + _openList = node; + + _touched[node] = true; + _onOpen[node] = true; + } + + public override Direction[] Find(Mobile m, Map map, Point3D start, Point3D goal) + { + if (!Utility.InRange(start, goal, AreaSize)) + { + return null; + } + + _touched.SetAll(false); + _onOpen.SetAll(false); + + _goal = goal; + + _xOffset = (start.X + goal.X - AreaSize) / 2; + _yOffset = (start.Y + goal.Y - AreaSize) / 2; + + var fromNode = GetIndex(start.X, start.Y, start.Z); + var destNode = GetIndex(goal.X, goal.Y, goal.Z); + + _openList = fromNode; + + _nodes[_openList].cost = 0; + _nodes[_openList].total = Heuristic(start.X - _xOffset, start.Y - _yOffset, start.Z); + _nodes[_openList].parent = -1; + _nodes[_openList].next = -1; + _nodes[_openList].prev = -1; + _nodes[_openList].z = start.Z; + + _onOpen[_openList] = true; + _touched[_openList] = true; + + var bc = m as BaseCreature; + + int backtrack = 0, depth = 0; + + var path = _path; + + while (_openList != -1) + { + var bestNode = FindBest(_openList); + + if (++depth > MaxDepth) + { + break; + } + + if (bc != null) + { + MoveImpl.AlwaysIgnoreDoors = bc.CanOpenDoors; + MoveImpl.IgnoreMovableImpassables = bc.CanMoveOverObstacles; + } + + MoveImpl.Goal = goal; + + var vals = _successors; + var count = GetSuccessors(bestNode, m, map); + + MoveImpl.AlwaysIgnoreDoors = false; + MoveImpl.IgnoreMovableImpassables = false; + MoveImpl.Goal = Point3D.Zero; + + if (count == 0) + { + break; + } + + for (var i = 0; i < count; ++i) + { + var newNode = vals[i]; + + var wasTouched = _touched[newNode]; + + if (wasTouched) + { + continue; + } + + var newCost = _nodes[bestNode].cost + 1; + var newTotal = newCost + Heuristic( + newNode % AreaSize, + newNode / AreaSize % AreaSize, + _nodes[newNode].z + ); + + _nodes[newNode].parent = bestNode; + _nodes[newNode].cost = newCost; + _nodes[newNode].total = newTotal; + + if (_onOpen[newNode]) + { + continue; + } + + AddToChain(newNode); + + if (newNode != destNode) + { + continue; + } + + var pathCount = 0; + var parent = _nodes[newNode].parent; + + while (parent != -1) + { + path[pathCount++] = GetDirection( + parent % AreaSize, + parent / AreaSize % AreaSize, + newNode % AreaSize, + newNode / AreaSize % AreaSize + ); + newNode = parent; + parent = _nodes[newNode].parent; + + if (newNode == fromNode) + { + break; + } + } + + var dirs = new Direction[pathCount]; + + while (pathCount > 0) + { + dirs[backtrack++] = path[--pathCount]; + } + + return dirs; + } + } + + return null; + } + + private int GetIndex(int x, int y, int z) + { + x -= _xOffset; + y -= _yOffset; + z += PlaneOffset; + z /= PlaneHeight; + + return x + y * AreaSize + z * AreaSize * AreaSize; + } + + private int FindBest(int node) + { + var least = _nodes[node].total; + var leastNode = node; + + while (node != -1) + { + if (_nodes[node].total < least) + { + least = _nodes[node].total; + leastNode = node; + } + + node = _nodes[node].next; + } + + RemoveFromChain(leastNode); + + _touched[leastNode] = true; + _onOpen[leastNode] = false; + + return leastNode; + } + + public int GetSuccessors(int p, Mobile m, Map map) + { + var px = p % AreaSize; + var py = p / AreaSize % AreaSize; + var pz = _nodes[p].z; + + var p3D = new Point3D(px + _xOffset, py + _yOffset, pz); + + var vals = _successors; + var count = 0; + + for (var i = 0; i < 8; ++i) + { + int x; + int y; + switch (i) + { + default: // 0 + x = 0; + y = -1; + break; + case 1: + x = 1; + y = -1; + break; + case 2: + x = 1; + y = 0; + break; + case 3: + x = 1; + y = 1; + break; + case 4: + x = 0; + y = 1; + break; + case 5: + x = -1; + y = 1; + break; + case 6: + x = -1; + y = 0; + break; + case 7: + x = -1; + y = -1; + break; + } + + x += px; + y += py; + + if (x is < 0 or >= AreaSize || y is < 0 or >= AreaSize) + { + continue; + } + + if (CalcMoves.CheckMovement(m, map, p3D, (Direction)i, out var z)) + { + var idx = GetIndex(x + _xOffset, y + _yOffset, z); + + if (idx >= 0 && idx < NodeCount) + { + _nodes[idx].z = z; + vals[count++] = idx; + } + } + } + + return count; + } + } +} diff --git a/Projects/UOContent/Engines/Pathing/MoveResult.cs b/Projects/UOContent/Engines/Pathing/MoveResult.cs index 6eb0fcdfb..cd9b91ddd 100644 --- a/Projects/UOContent/Engines/Pathing/MoveResult.cs +++ b/Projects/UOContent/Engines/Pathing/MoveResult.cs @@ -1,11 +1,12 @@ -namespace Server; - -public delegate MoveResult MoveMethod(Direction d, bool badStateOk); - -public enum MoveResult +namespace Server { - BadState, - Blocked, - Success, - SuccessAutoTurn -} \ No newline at end of file + public delegate MoveResult MoveMethod(Direction d, bool badStateOk); + + public enum MoveResult + { + BadState, + Blocked, + Success, + SuccessAutoTurn + } +} diff --git a/Projects/UOContent/Engines/Pathing/Movement.cs b/Projects/UOContent/Engines/Pathing/Movement.cs index 1cb077a30..3f1b263f2 100644 --- a/Projects/UOContent/Engines/Pathing/Movement.cs +++ b/Projects/UOContent/Engines/Pathing/Movement.cs @@ -4,578 +4,579 @@ using System.Runtime.CompilerServices; using Server.Items; using Server.Mobiles; -namespace Server.Movement; - -public class MovementImpl : IMovementImpl +namespace Server.Movement { - private const int PersonHeight = 16; - private const int StepHeight = 2; - - private const TileFlag ImpassableSurface = TileFlag.Impassable | TileFlag.Surface; - - private static Point3D _goal; - - public static void Configure() + public class MovementImpl : IMovementImpl { - Movement.Impl = new MovementImpl(); - } + private const int PersonHeight = 16; + private const int StepHeight = 2; - private readonly List[] _mobPools = { new(), new(), new() }; + private const TileFlag ImpassableSurface = TileFlag.Impassable | TileFlag.Surface; - private readonly List[] _pools = { new(), new(), new(), new() }; + private static Point3D _goal; - private MovementImpl() - { - } - - public static bool AlwaysIgnoreDoors { get; set; } - public static bool IgnoreMovableImpassables { get; set; } - - public static Point3D Goal - { - get => _goal; - set => _goal = value; - } - - public bool CheckMovement(Mobile m, Map map, Point3D loc, Direction d, out int newZ) - { - if (map == null || map == Map.Internal) + public static void Configure() { - newZ = 0; - return false; + Movement.Impl = new MovementImpl(); } - var xStart = loc.X; - var yStart = loc.Y; + private readonly List[] _mobPools = { new(), new(), new() }; - int xForward = xStart, yForward = yStart; - int xRight = xStart, yRight = yStart; - int xLeft = xStart, yLeft = yStart; + private readonly List[] _pools = { new(), new(), new(), new() }; - var checkDiagonals = ((int)d & 0x1) == 0x1; - - Movement.Offset(d, ref xForward, ref yForward); - Movement.Offset((Direction)(((int)d - 1) & 0x7), ref xLeft, ref yLeft); - Movement.Offset((Direction)(((int)d + 1) & 0x7), ref xRight, ref yRight); - - if (xForward < 0 || yForward < 0 || xForward >= map.Width || yForward >= map.Height) + private MovementImpl() { - newZ = 0; - return false; } - var itemsStart = _pools[0]; - var itemsForward = _pools[1]; - var itemsLeft = _pools[2]; - var itemsRight = _pools[3]; + public static bool AlwaysIgnoreDoors { get; set; } + public static bool IgnoreMovableImpassables { get; set; } - var ignoreMovableImpassables = IgnoreMovableImpassables; - var reqFlags = ImpassableSurface; - - if (m.CanSwim) + public static Point3D Goal { - reqFlags |= TileFlag.Wet; + get => _goal; + set => _goal = value; } - var mobsForward = _mobPools[0]; - var mobsLeft = _mobPools[1]; - var mobsRight = _mobPools[2]; - - var checkMobs = (m as BaseCreature)?.Controlled == false && (xForward != _goal.X || yForward != _goal.Y); - - if (checkMobs) + public bool CheckMovement(Mobile m, Map map, Point3D loc, Direction d, out int newZ) { - foreach (var mob in map.GetMobilesInRange(loc, 1)) + if (map == null || map == Map.Internal) { - if (mob.AtPoint(xForward, yForward)) + newZ = 0; + return false; + } + + var xStart = loc.X; + var yStart = loc.Y; + + int xForward = xStart, yForward = yStart; + int xRight = xStart, yRight = yStart; + int xLeft = xStart, yLeft = yStart; + + var checkDiagonals = ((int)d & 0x1) == 0x1; + + Movement.Offset(d, ref xForward, ref yForward); + Movement.Offset((Direction)(((int)d - 1) & 0x7), ref xLeft, ref yLeft); + Movement.Offset((Direction)(((int)d + 1) & 0x7), ref xRight, ref yRight); + + if (xForward < 0 || yForward < 0 || xForward >= map.Width || yForward >= map.Height) + { + newZ = 0; + return false; + } + + var itemsStart = _pools[0]; + var itemsForward = _pools[1]; + var itemsLeft = _pools[2]; + var itemsRight = _pools[3]; + + var ignoreMovableImpassables = IgnoreMovableImpassables; + var reqFlags = ImpassableSurface; + + if (m.CanSwim) + { + reqFlags |= TileFlag.Wet; + } + + var mobsForward = _mobPools[0]; + var mobsLeft = _mobPools[1]; + var mobsRight = _mobPools[2]; + + var checkMobs = (m as BaseCreature)?.Controlled == false && (xForward != _goal.X || yForward != _goal.Y); + + if (checkMobs) + { + foreach (var mob in map.GetMobilesInRange(loc, 1)) { - mobsForward.Add(mob); - } - else if (checkDiagonals && mob.AtPoint(xLeft, yLeft)) - { - mobsLeft.Add(mob); - } - else if (checkDiagonals && mob.AtPoint(xRight, yRight)) - { - mobsRight.Add(mob); - } - } - } - - foreach (var item in map.GetItemsInRange(loc, 1)) - { - if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface) - { - continue; - } - - if (!item.ItemData[reqFlags] || item.ItemID > TileData.MaxItemValue || item.Parent != null) - { - continue; - } - - if (item is BaseMulti) - { - continue; - } - - if (item.AtPoint(xStart, yStart)) - { - itemsStart.Add(item); - } - else if (item.AtPoint(xForward, yForward)) - { - itemsForward.Add(item); - } - else if (checkDiagonals && item.AtPoint(xLeft, yLeft)) - { - itemsLeft.Add(item); - } - else if (checkDiagonals && item.AtPoint(xRight, yRight)) - { - itemsRight.Add(item); - } - } - - GetStartZ(m, map, loc, itemsStart, out var startZ, out var startTop); - - var moveIsOk = Check(map, m, itemsForward, mobsForward, xForward, yForward, startTop, startZ, out newZ); - - if (moveIsOk && checkDiagonals) - { - if (m.Player && m.AccessLevel < AccessLevel.GameMaster) - { - if (!Check(map, m, itemsLeft, mobsLeft, xLeft, yLeft, startTop, startZ, out _) || - !Check(map, m, itemsRight, mobsRight, xRight, yRight, startTop, startZ, out _)) - { - moveIsOk = false; - } - } - else - { - if (!Check(map, m, itemsLeft, mobsLeft, xLeft, yLeft, startTop, startZ, out _) && - !Check(map, m, itemsRight, mobsRight, xRight, yRight, startTop, startZ, out _)) - { - moveIsOk = false; - } - } - } - - for (int i = 0, c = checkDiagonals ? 4 : 2; i < c; ++i) - { - _pools[i].Clear(); - } - - for (int i = 0, c = checkDiagonals ? 3 : 1; i < c; ++i) - { - _mobPools[i].Clear(); - } - - if (!moveIsOk) - { - newZ = startZ; - } - - return moveIsOk; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CheckMovement(Mobile m, Direction d, out int newZ) => CheckMovement(m, m.Map, m.Location, d, out newZ); - - private static bool IsOk( - bool ignoreDoors, bool ignoreSpellFields, int ourZ, int ourTop, Map map, int x, int y, List items - ) - { - foreach (var check in map.Tiles.GetStaticAndMultiTiles(x, y)) - { - var itemData = TileData.ItemTable[check.ID & TileData.MaxItemValue]; - - if (itemData.ImpassableSurface) - { - var checkZ = check.Z; - var checkTop = checkZ + itemData.CalcHeight; - - if (checkTop > ourZ && ourTop > checkZ) - { - return false; - } - } - } - - for (var i = 0; i < items.Count; ++i) - { - var item = items[i]; - var itemID = item.ItemID & TileData.MaxItemValue; - var itemData = TileData.ItemTable[itemID]; - - if (itemData.ImpassableSurface) - { - if (ignoreDoors && (itemData.Door || itemID is 0x692 or 0x846 or 0x873 || itemID >= 0x6F5 && itemID <= 0x6F6)) - { - continue; - } - - if (ignoreSpellFields && itemID is 0x82 or 0x3946 or 0x3956) - { - continue; - } - - var checkZ = item.Z; - var checkTop = checkZ + itemData.CalcHeight; - - if (checkTop > ourZ && ourTop > checkZ) - { - return false; - } - } - } - - return true; - } - - private static bool Check( - Map map, - Mobile m, - List items, - List mobiles, - int x, - int y, - int startTop, - int startZ, - out int newZ - ) - { - newZ = 0; - - var cantWalk = m.CantWalk; - var canSwim = m.CanSwim; - var landTile = map.Tiles.GetLandTile(x, y); - var flags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; - var impassable = (flags & TileFlag.Impassable) != 0; - - // Impassable + swim on water is ok, otherwise block if cannot walk or impassable - var landBlocks = (cantWalk || impassable) && !(impassable && canSwim && (flags & TileFlag.Wet) != 0); - - var considerLand = !landTile.Ignored; - - map.GetAverageZ(x, y, out var landZ, out var landCenter, out _); - - var moveIsOk = false; - - var stepTop = startTop + StepHeight; - var checkTop = startZ + PersonHeight; - - var ignoreDoors = AlwaysIgnoreDoors || !m.Alive || m.Body.BodyID == 0x3DB || m.IsDeadBondedPet; - var ignoreSpellFields = m is PlayerMobile && map != Map.Felucca; - - int testTop; - - foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y)) - { - var itemData = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - - if (m.Flying && itemData.Name.InsensitiveEquals("hover over")) - { - newZ = tile.Z; - return true; - } - - // Stygian Dragon - if (m.Body == 826 && map == Map.TerMur) - { - if (x is >= 307 and <= 354 && y is >= 126 and <= 192) - { - if (tile.Z > newZ) + if (mob.AtPoint(xForward, yForward)) { - newZ = tile.Z; + mobsForward.Add(mob); } - - moveIsOk = true; - } - else if (x is >= 42 and <= 89 && y is >= 333 and <= 399 or >= 531 and <= 597 or >= 739 and <= 805) - { - if (tile.Z > newZ) + else if (checkDiagonals && mob.AtPoint(xLeft, yLeft)) { - newZ = tile.Z; + mobsLeft.Add(mob); + } + else if (checkDiagonals && mob.AtPoint(xRight, yRight)) + { + mobsRight.Add(mob); } - - moveIsOk = true; } } - var notWater = !itemData.Wet; - - /* - * To move we must satisfy the following: - * 1. Item is a _passable_ surface and Mob can walk -or- - * 2. Item is water and Mob can swim - */ - if ( - (!itemData.Surface || itemData.Impassable) && (!canSwim || notWater) || - cantWalk && notWater - ) + foreach (var item in map.GetItemsInRange(loc, 1)) { - continue; - } - - var itemZ = tile.Z; - var itemTop = itemZ; - var ourZ = itemZ + itemData.CalcHeight; - testTop = checkTop; - - if (moveIsOk) - { - var cmp = (ourZ - m.Z).Abs() - (newZ - m.Z).Abs(); - - if (cmp > 0 || cmp == 0 && ourZ > newZ) + if (ignoreMovableImpassables && item.Movable && item.ItemData.ImpassableSurface) { continue; } - } - if (ourZ + PersonHeight > testTop) - { - testTop = ourZ + PersonHeight; - } - - if (!itemData.Bridge) - { - itemTop += itemData.Height; - } - - if (stepTop < itemTop) - { - continue; - } - - var landCheck = itemZ + Math.Min(itemData.Height, StepHeight); - - if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) - { - continue; - } - - if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, map, x, y, items)) - { - newZ = ourZ; - moveIsOk = true; - } - } - - for (var i = 0; i < items.Count; ++i) - { - var item = items[i]; - var itemData = item.ItemData; - - if (m.Flying && itemData.Name.InsensitiveEquals("hover over")) - { - newZ = item.Z; - return true; - } - - var notWater = !itemData.Wet; - - /* - * To move we must satisfy the following: - * 1. Item is not movable - * 2. Item is a _passable_ surface and Mob can walk -or- - * Item is water and Mob can swim - */ - if ( - item.Movable || - (!itemData.Surface || itemData.Impassable) && (!canSwim || notWater) || - cantWalk && notWater - ) - { - continue; - } - - var itemZ = item.Z; - var itemTop = itemZ; - var ourZ = itemZ + itemData.CalcHeight; - testTop = checkTop; - - if (moveIsOk) - { - var cmp = (ourZ - m.Z).Abs() - (newZ - m.Z).Abs(); - - if (cmp > 0 || cmp == 0 && ourZ > newZ) + if (!item.ItemData[reqFlags] || item.ItemID > TileData.MaxItemValue || item.Parent != null) { continue; } + + if (item is BaseMulti) + { + continue; + } + + if (item.AtPoint(xStart, yStart)) + { + itemsStart.Add(item); + } + else if (item.AtPoint(xForward, yForward)) + { + itemsForward.Add(item); + } + else if (checkDiagonals && item.AtPoint(xLeft, yLeft)) + { + itemsLeft.Add(item); + } + else if (checkDiagonals && item.AtPoint(xRight, yRight)) + { + itemsRight.Add(item); + } } - if (ourZ + PersonHeight > testTop) + GetStartZ(m, map, loc, itemsStart, out var startZ, out var startTop); + + var moveIsOk = Check(map, m, itemsForward, mobsForward, xForward, yForward, startTop, startZ, out newZ); + + if (moveIsOk && checkDiagonals) { - testTop = ourZ + PersonHeight; + if (m.Player && m.AccessLevel < AccessLevel.GameMaster) + { + if (!Check(map, m, itemsLeft, mobsLeft, xLeft, yLeft, startTop, startZ, out _) || + !Check(map, m, itemsRight, mobsRight, xRight, yRight, startTop, startZ, out _)) + { + moveIsOk = false; + } + } + else + { + if (!Check(map, m, itemsLeft, mobsLeft, xLeft, yLeft, startTop, startZ, out _) && + !Check(map, m, itemsRight, mobsRight, xRight, yRight, startTop, startZ, out _)) + { + moveIsOk = false; + } + } } - if (!itemData.Bridge) + for (int i = 0, c = checkDiagonals ? 4 : 2; i < c; ++i) { - itemTop += itemData.Height; + _pools[i].Clear(); } - if (stepTop < itemTop) + for (int i = 0, c = checkDiagonals ? 3 : 1; i < c; ++i) { - continue; + _mobPools[i].Clear(); } - var landCheck = itemZ + Math.Min(itemData.Height, StepHeight); - - if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) + if (!moveIsOk) { - continue; + newZ = startZ; } - if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, map, x, y, items)) - { - newZ = ourZ; - moveIsOk = true; - } - } - - if (!considerLand || landBlocks || stepTop < landZ) - { return moveIsOk; } - testTop = checkTop; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool CheckMovement(Mobile m, Direction d, out int newZ) => CheckMovement(m, m.Map, m.Location, d, out newZ); - if (landCenter + PersonHeight > testTop) + private static bool IsOk( + bool ignoreDoors, bool ignoreSpellFields, int ourZ, int ourTop, Map map, int x, int y, List items + ) { - testTop = landCenter + PersonHeight; - } - - var shouldCheck = true; - - if (moveIsOk) - { - var cmp = (landCenter - m.Z).Abs() - (newZ - m.Z).Abs(); - - if (cmp > 0 || cmp == 0 && landCenter > newZ) + foreach (var check in map.Tiles.GetStaticAndMultiTiles(x, y)) { - shouldCheck = false; - } - } + var itemData = TileData.ItemTable[check.ID & TileData.MaxItemValue]; - if (shouldCheck && IsOk(ignoreDoors, ignoreSpellFields, landCenter, testTop, map, x, y, items)) - { - newZ = landCenter; - moveIsOk = true; - } - - if (moveIsOk) - { - for (var i = 0; moveIsOk && i < mobiles.Count; ++i) - { - var mob = mobiles[i]; - - if (mob != m && mob.Z + 15 > newZ && newZ + 15 > mob.Z && !CanMoveOver(m, mob)) + if (itemData.ImpassableSurface) { - moveIsOk = false; + var checkZ = check.Z; + var checkTop = checkZ + itemData.CalcHeight; + + if (checkTop > ourZ && ourTop > checkZ) + { + return false; + } } } + + for (var i = 0; i < items.Count; ++i) + { + var item = items[i]; + var itemID = item.ItemID & TileData.MaxItemValue; + var itemData = TileData.ItemTable[itemID]; + + if (itemData.ImpassableSurface) + { + if (ignoreDoors && (itemData.Door || itemID is 0x692 or 0x846 or 0x873 || itemID >= 0x6F5 && itemID <= 0x6F6)) + { + continue; + } + + if (ignoreSpellFields && itemID is 0x82 or 0x3946 or 0x3956) + { + continue; + } + + var checkZ = item.Z; + var checkTop = checkZ + itemData.CalcHeight; + + if (checkTop > ourZ && ourTop > checkZ) + { + return false; + } + } + } + + return true; } - return moveIsOk; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool CanMoveOver(Mobile m, Mobile t) => - !t.Alive || !m.Alive || t.IsDeadBondedPet || m.IsDeadBondedPet || t.Hidden && t.AccessLevel > AccessLevel.Player; - - private static void GetStartZ(Mobile m, Map map, Point3D loc, List itemList, out int zLow, out int zTop) - { - int xCheck = loc.X, yCheck = loc.Y; - - var landTile = map.Tiles.GetLandTile(xCheck, yCheck); - var flags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; - var impassable = (flags & TileFlag.Impassable) != 0; - - // Impassable + swim on water is ok, otherwise block if cannot walk or impassable - var landBlocks = (m.CantWalk || impassable) && !(impassable && m.CanSwim && (flags & TileFlag.Wet) != 0); - - map.GetAverageZ(xCheck, yCheck, out var landZ, out var landCenter, out var landTop); - - var considerLand = !landTile.Ignored; - - var zCenter = zLow = zTop = 0; - var isSet = false; - - if (considerLand && !landBlocks && loc.Z >= landCenter) + private static bool Check( + Map map, + Mobile m, + List items, + List mobiles, + int x, + int y, + int startTop, + int startZ, + out int newZ + ) { - zLow = landZ; - zCenter = landCenter; + newZ = 0; - zTop = landTop; + var cantWalk = m.CantWalk; + var canSwim = m.CanSwim; + var landTile = map.Tiles.GetLandTile(x, y); + var flags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; + var impassable = (flags & TileFlag.Impassable) != 0; - isSet = true; + // Impassable + swim on water is ok, otherwise block if cannot walk or impassable + var landBlocks = (cantWalk || impassable) && !(impassable && canSwim && (flags & TileFlag.Wet) != 0); + + var considerLand = !landTile.Ignored; + + map.GetAverageZ(x, y, out var landZ, out var landCenter, out _); + + var moveIsOk = false; + + var stepTop = startTop + StepHeight; + var checkTop = startZ + PersonHeight; + + var ignoreDoors = AlwaysIgnoreDoors || !m.Alive || m.Body.BodyID == 0x3DB || m.IsDeadBondedPet; + var ignoreSpellFields = m is PlayerMobile && map != Map.Felucca; + + int testTop; + + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y)) + { + var itemData = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; + + if (m.Flying && itemData.Name.InsensitiveEquals("hover over")) + { + newZ = tile.Z; + return true; + } + + // Stygian Dragon + if (m.Body == 826 && map == Map.TerMur) + { + if (x is >= 307 and <= 354 && y is >= 126 and <= 192) + { + if (tile.Z > newZ) + { + newZ = tile.Z; + } + + moveIsOk = true; + } + else if (x is >= 42 and <= 89 && y is >= 333 and <= 399 or >= 531 and <= 597 or >= 739 and <= 805) + { + if (tile.Z > newZ) + { + newZ = tile.Z; + } + + moveIsOk = true; + } + } + + var notWater = !itemData.Wet; + + /* + * To move we must satisfy the following: + * 1. Item is a _passable_ surface and Mob can walk -or- + * 2. Item is water and Mob can swim + */ + if ( + (!itemData.Surface || itemData.Impassable) && (!canSwim || notWater) || + cantWalk && notWater + ) + { + continue; + } + + var itemZ = tile.Z; + var itemTop = itemZ; + var ourZ = itemZ + itemData.CalcHeight; + testTop = checkTop; + + if (moveIsOk) + { + var cmp = (ourZ - m.Z).Abs() - (newZ - m.Z).Abs(); + + if (cmp > 0 || cmp == 0 && ourZ > newZ) + { + continue; + } + } + + if (ourZ + PersonHeight > testTop) + { + testTop = ourZ + PersonHeight; + } + + if (!itemData.Bridge) + { + itemTop += itemData.Height; + } + + if (stepTop < itemTop) + { + continue; + } + + var landCheck = itemZ + Math.Min(itemData.Height, StepHeight); + + if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) + { + continue; + } + + if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, map, x, y, items)) + { + newZ = ourZ; + moveIsOk = true; + } + } + + for (var i = 0; i < items.Count; ++i) + { + var item = items[i]; + var itemData = item.ItemData; + + if (m.Flying && itemData.Name.InsensitiveEquals("hover over")) + { + newZ = item.Z; + return true; + } + + var notWater = !itemData.Wet; + + /* + * To move we must satisfy the following: + * 1. Item is not movable + * 2. Item is a _passable_ surface and Mob can walk -or- + * Item is water and Mob can swim + */ + if ( + item.Movable || + (!itemData.Surface || itemData.Impassable) && (!canSwim || notWater) || + cantWalk && notWater + ) + { + continue; + } + + var itemZ = item.Z; + var itemTop = itemZ; + var ourZ = itemZ + itemData.CalcHeight; + testTop = checkTop; + + if (moveIsOk) + { + var cmp = (ourZ - m.Z).Abs() - (newZ - m.Z).Abs(); + + if (cmp > 0 || cmp == 0 && ourZ > newZ) + { + continue; + } + } + + if (ourZ + PersonHeight > testTop) + { + testTop = ourZ + PersonHeight; + } + + if (!itemData.Bridge) + { + itemTop += itemData.Height; + } + + if (stepTop < itemTop) + { + continue; + } + + var landCheck = itemZ + Math.Min(itemData.Height, StepHeight); + + if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) + { + continue; + } + + if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, map, x, y, items)) + { + newZ = ourZ; + moveIsOk = true; + } + } + + if (!considerLand || landBlocks || stepTop < landZ) + { + return moveIsOk; + } + + testTop = checkTop; + + if (landCenter + PersonHeight > testTop) + { + testTop = landCenter + PersonHeight; + } + + var shouldCheck = true; + + if (moveIsOk) + { + var cmp = (landCenter - m.Z).Abs() - (newZ - m.Z).Abs(); + + if (cmp > 0 || cmp == 0 && landCenter > newZ) + { + shouldCheck = false; + } + } + + if (shouldCheck && IsOk(ignoreDoors, ignoreSpellFields, landCenter, testTop, map, x, y, items)) + { + newZ = landCenter; + moveIsOk = true; + } + + if (moveIsOk) + { + for (var i = 0; moveIsOk && i < mobiles.Count; ++i) + { + var mob = mobiles[i]; + + if (mob != m && mob.Z + 15 > newZ && newZ + 15 > mob.Z && !CanMoveOver(m, mob)) + { + moveIsOk = false; + } + } + } + + return moveIsOk; } - foreach (var tile in map.Tiles.GetStaticAndMultiTiles(xCheck, yCheck)) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool CanMoveOver(Mobile m, Mobile t) => + !t.Alive || !m.Alive || t.IsDeadBondedPet || m.IsDeadBondedPet || t.Hidden && t.AccessLevel > AccessLevel.Player; + + private static void GetStartZ(Mobile m, Map map, Point3D loc, List itemList, out int zLow, out int zTop) { - var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - var calcTop = tile.Z + id.CalcHeight; + int xCheck = loc.X, yCheck = loc.Y; - if (isSet && calcTop < zCenter || loc.Z < calcTop || !id.Surface && !(m.CanSwim && id.Wet)) + var landTile = map.Tiles.GetLandTile(xCheck, yCheck); + var flags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; + var impassable = (flags & TileFlag.Impassable) != 0; + + // Impassable + swim on water is ok, otherwise block if cannot walk or impassable + var landBlocks = (m.CantWalk || impassable) && !(impassable && m.CanSwim && (flags & TileFlag.Wet) != 0); + + map.GetAverageZ(xCheck, yCheck, out var landZ, out var landCenter, out var landTop); + + var considerLand = !landTile.Ignored; + + var zCenter = zLow = zTop = 0; + var isSet = false; + + if (considerLand && !landBlocks && loc.Z >= landCenter) { - continue; + zLow = landZ; + zCenter = landCenter; + + zTop = landTop; + + isSet = true; } - if (m.CantWalk && !id.Wet) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(xCheck, yCheck)) { - continue; + var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; + var calcTop = tile.Z + id.CalcHeight; + + if (isSet && calcTop < zCenter || loc.Z < calcTop || !id.Surface && !(m.CanSwim && id.Wet)) + { + continue; + } + + if (m.CantWalk && !id.Wet) + { + continue; + } + + zLow = tile.Z; + zCenter = calcTop; + + var top = tile.Z + id.Height; + + if (!isSet || top > zTop) + { + zTop = top; + } + + isSet = true; } - zLow = tile.Z; - zCenter = calcTop; - - var top = tile.Z + id.Height; - - if (!isSet || top > zTop) + for (var i = 0; i < itemList.Count; ++i) { - zTop = top; + var item = itemList[i]; + var id = item.ItemData; + var calcTop = item.Z + id.CalcHeight; + + if (isSet && calcTop < zCenter || loc.Z < calcTop || !id.Surface && !(m.CanSwim && id.Wet)) + { + continue; + } + + if (m.CantWalk && !id.Wet) + { + continue; + } + + zLow = item.Z; + zCenter = calcTop; + + var top = item.Z + id.Height; + + if (!isSet || top > zTop) + { + zTop = top; + } + + isSet = true; } - isSet = true; - } - - for (var i = 0; i < itemList.Count; ++i) - { - var item = itemList[i]; - var id = item.ItemData; - var calcTop = item.Z + id.CalcHeight; - - if (isSet && calcTop < zCenter || loc.Z < calcTop || !id.Surface && !(m.CanSwim && id.Wet)) + if (!isSet) { - continue; + zLow = zTop = loc.Z; } - - if (m.CantWalk && !id.Wet) + else if (loc.Z > zTop) { - continue; + zTop = loc.Z; } - - zLow = item.Z; - zCenter = calcTop; - - var top = item.Z + id.Height; - - if (!isSet || top > zTop) - { - zTop = top; - } - - isSet = true; - } - - if (!isSet) - { - zLow = zTop = loc.Z; - } - else if (loc.Z > zTop) - { - zTop = loc.Z; } } } diff --git a/Projects/UOContent/Engines/Pathing/MovementPath.cs b/Projects/UOContent/Engines/Pathing/MovementPath.cs index d1ef874fc..2ae2e714b 100644 --- a/Projects/UOContent/Engines/Pathing/MovementPath.cs +++ b/Projects/UOContent/Engines/Pathing/MovementPath.cs @@ -2,115 +2,117 @@ using System; using System.Diagnostics; using Server.Items; using Server.PathAlgorithms; +using Server.PathAlgorithms.FastAStar; using Server.Spells; using Server.Targeting; -namespace Server; - -public sealed class MovementPath +namespace Server { - public MovementPath(Mobile m, Point3D goal) + public sealed class MovementPath { - var start = m.Location; - var map = m.Map; - - Map = map; - Start = start; - Goal = goal; - - if (map == null || map == Map.Internal) + public MovementPath(Mobile m, Point3D goal) { - return; - } + var start = m.Location; + var map = m.Map; - if (Utility.InRange(start, goal, 1)) - { - return; - } + Map = map; + Start = start; + Goal = goal; - try - { - var alg = OverrideAlgorithm ?? BitmapAStarAlgorithm.Instance; - - if (alg?.CheckCondition(m, map, start, goal) == true) + if (map == null || map == Map.Internal) { - Directions = alg.Find(m, map, start, goal); + return; + } + + if (Utility.InRange(start, goal, 1)) + { + return; + } + + try + { + var alg = OverrideAlgorithm ?? FastAStarAlgorithm.Instance; + + if (alg?.CheckCondition(m, map, start, goal) == true) + { + Directions = alg.Find(m, map, start, goal); + } + } + catch (Exception e) + { + Console.WriteLine("Warning: {0}: Pathing error from {1} to {2}", e.GetType().Name, start, goal); } } - catch (Exception e) + + public Map Map { get; } + + public Point3D Start { get; } + + public Point3D Goal { get; } + + public Direction[] Directions { get; } + + public bool Success => Directions?.Length > 0; + + public static PathAlgorithm OverrideAlgorithm { get; set; } + + public static void Configure() { - Console.WriteLine("Warning: {0}: Pathing error from {1} to {2}", e.GetType().Name, start, goal); + CommandSystem.Register("Path", AccessLevel.GameMaster, Path_OnCommand); } - } - public Map Map { get; } - - public Point3D Start { get; } - - public Point3D Goal { get; } - - public Direction[] Directions { get; } - - public bool Success => Directions?.Length > 0; - - public static PathAlgorithm OverrideAlgorithm { get; set; } - - public static void Configure() - { - CommandSystem.Register("Path", AccessLevel.GameMaster, Path_OnCommand); - } - - [Usage("Path")] - [Description("Draws a path from your current location to a targeted location.")] - public static void Path_OnCommand(CommandEventArgs e) - { - e.Mobile.BeginTarget(-1, true, TargetFlags.None, Path_OnTarget); - e.Mobile.SendMessage("Target a location and a path will be drawn there."); - } - - private static void Path(Mobile from, IPoint3D p, PathAlgorithm alg, string name, int zOffset) - { - OverrideAlgorithm = alg; - - var watch = new Stopwatch(); - watch.Start(); - var path = new MovementPath(from, new Point3D(p)); - watch.Stop(); - - if (!path.Success) + [Usage("Path")] + [Description("Draws a path from your current location to a targeted location.")] + public static void Path_OnCommand(CommandEventArgs e) { - from.SendMessage($"{name} path failed: {watch.ElapsedMilliseconds}ms"); + e.Mobile.BeginTarget(-1, true, TargetFlags.None, Path_OnTarget); + e.Mobile.SendMessage("Target a location and a path will be drawn there."); } - else + + private static void Path(Mobile from, IPoint3D p, PathAlgorithm alg, string name, int zOffset) { - from.SendMessage($"{name} path success: {watch.ElapsedMilliseconds}ms"); + OverrideAlgorithm = alg; - var x = from.X; - var y = from.Y; - var z = from.Z; + var watch = new Stopwatch(); + watch.Start(); + var path = new MovementPath(from, new Point3D(p)); + watch.Stop(); - WayPoint waypoint = null; - - for (var i = 0; i < path.Directions.Length; ++i) + if (!path.Success) { - Movement.Movement.Offset(path.Directions[i], ref x, ref y); + from.SendMessage($"{name} path failed: {watch.ElapsedMilliseconds}ms"); + } + else + { + from.SendMessage($"{name} path success: {watch.ElapsedMilliseconds}ms"); - waypoint = new WayPoint(waypoint); - waypoint.MoveToWorld(new Point3D(x, y, z + zOffset), from.Map); + var x = from.X; + var y = from.Y; + var z = from.Z; + + WayPoint waypoint = null; + + for (var i = 0; i < path.Directions.Length; ++i) + { + Movement.Movement.Offset(path.Directions[i], ref x, ref y); + + waypoint = new WayPoint(waypoint); + waypoint.MoveToWorld(new Point3D(x, y, z + zOffset), from.Map); + } } } - } - public static void Path_OnTarget(Mobile from, object targeted) - { - if (targeted is not IPoint3D p) + public static void Path_OnTarget(Mobile from, object targeted) { - return; + if (targeted is not IPoint3D p) + { + return; + } + + SpellHelper.GetSurfaceTop(ref p); + + Path(from, p, FastAStarAlgorithm.Instance, "Fast", 0); + OverrideAlgorithm = null; } - - SpellHelper.GetSurfaceTop(ref p); - - Path(from, p, BitmapAStarAlgorithm.Instance, "Bitmap", 0); - OverrideAlgorithm = null; } } diff --git a/Projects/UOContent/Engines/Pathing/PathAlgorithm.cs b/Projects/UOContent/Engines/Pathing/PathAlgorithm.cs index faba9d2a0..40ec83fcd 100644 --- a/Projects/UOContent/Engines/Pathing/PathAlgorithm.cs +++ b/Projects/UOContent/Engines/Pathing/PathAlgorithm.cs @@ -1,29 +1,35 @@ -namespace Server.PathAlgorithms; - -public abstract class PathAlgorithm +namespace Server.PathAlgorithms { - private static readonly Direction[] _calcDirections = + public abstract class PathAlgorithm { - Direction.Up, - Direction.North, - Direction.Right, - Direction.West, - Direction.North, - Direction.East, - Direction.Left, - Direction.South, - Direction.Down - }; + private static readonly Direction[] m_CalcDirections = + { + Direction.Up, + Direction.North, + Direction.Right, + Direction.West, + Direction.North, + Direction.East, + Direction.Left, + Direction.South, + Direction.Down + }; - public abstract bool CheckCondition(Mobile m, Map map, Point3D start, Point3D goal); - public abstract Direction[] Find(Mobile m, Map map, Point3D start, Point3D goal); + public abstract bool CheckCondition(Mobile m, Map map, Point3D start, Point3D goal); + public abstract Direction[] Find(Mobile m, Map map, Point3D start, Point3D goal); - public static Direction GetDirection(int xSource, int ySource, int xDest, int yDest) - { - var x = xDest + 1 - xSource; - var y = yDest + 1 - ySource; - var v = y * 3 + x; + public Direction GetDirection(int xSource, int ySource, int xDest, int yDest) + { + var x = xDest + 1 - xSource; + var y = yDest + 1 - ySource; + var v = y * 3 + x; - return v is < 0 or >= 9 ? Direction.North : _calcDirections[v]; + if (v is < 0 or >= 9) + { + return Direction.North; + } + + return m_CalcDirections[v]; + } } } diff --git a/Projects/UOContent/Engines/Pathing/PathCacheCommands.cs b/Projects/UOContent/Engines/Pathing/PathCacheCommands.cs deleted file mode 100644 index 7f8fee270..000000000 --- a/Projects/UOContent/Engines/Pathing/PathCacheCommands.cs +++ /dev/null @@ -1,312 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using Server.Engines.Pathing.Cache; -using Server.Logging; - -namespace Server.Engines.Pathing; - -/// -/// Admin commands for inspecting and operating the pathfinding step cache. -/// [PathCacheStats — resident-chunk count and hit/miss/eviction telemetry. -/// [PathCacheClear — drop all cached chunks, close the .swb readers, zero the counters. -/// [PathBake — build a map's full static cache and save it. -/// [PathCacheSave — persist the resident chunks to Data/Pathfinding/<mapId>.swb. -/// [PathCacheLoad — open those files as backing stores. Also runs at startup. -/// [PathRecord — toggle capture of pathfind telemetry. -/// -/// None of this is required: the cache builds chunks on demand as creatures path, with or without -/// a .swb on disk. Baking one is purely an optimization that trades disk and a few minutes of bake -/// time for the removal of first-pathfind-after-boot latency. -/// -public static class PathCacheCommands -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(PathCacheCommands)); - - // When set, Initialize() bakes any missing or stale .swb at startup. ConfigurePrompts() asks - // for it on first boot. - private const string PrebakeSetting = "pathfinding.prebakeMaps"; - - private static string PathFor(int mapId) => - Path.Combine(Core.BaseDirectory, "Data", "Pathfinding", $"{mapId}.swb"); - - public static void Configure() - { - // Resident-chunk cap, shard-tunable — the default works out to roughly 40 MB. Written back - // to server.cfg on first boot so it's discoverable. - StepCache.Instance.MaxResidentChunks = ServerConfiguration.GetOrUpdateSetting( - "pathfinding.maxResidentChunks", - 8192 - ); - - CommandSystem.Register("PathCacheStats", AccessLevel.Administrator, OnPathCacheStats); - CommandSystem.Register("PathCacheClear", AccessLevel.Administrator, OnPathCacheClear); - CommandSystem.Register("PathBake", AccessLevel.Administrator, OnPathBake); - CommandSystem.Register("PathCacheSave", AccessLevel.Administrator, OnPathCacheSave); - CommandSystem.Register("PathCacheLoad", AccessLevel.Administrator, OnPathCacheLoad); - CommandSystem.Register("PathRecord", AccessLevel.Administrator, OnPathRecord); - } - - /// - /// Asks, once, whether to pre-bake the .swb cache; does the work later. - /// The answer persists, so the question is never repeated, and it's skipped entirely when input - /// is redirected — a headless or CI boot sets directly instead. - /// - /// Runs in the ConfigurePrompts phase because that's the one window where content can prompt: - /// assemblies are loaded, but Serilog hasn't started, so console output won't interleave with - /// async log writes. - /// - public static void ConfigurePrompts() - { - if (ServerConfiguration.GetSetting(PrebakeSetting, (string)null) != null || Console.IsInputRedirected) - { - return; - } - - Console.WriteLine(); - Console.WriteLine("Pre-bake the pathfinding cache for your selected maps now?"); - Console.WriteLine(" Bakes each map's .swb so there is zero first-pathfind-after-boot latency."); - Console.WriteLine(" Takes several minutes and ~tens of MB of disk per facet. You can also do"); - Console.WriteLine(" this later at runtime with [PathBake."); - Console.Write("Pre-bake now? [y/N] "); - - var answer = Console.ReadLine()?.Trim(); - var prebake = answer?.StartsWith("y", StringComparison.OrdinalIgnoreCase) == true; - - ServerConfiguration.SetSetting(PrebakeSetting, prebake); - } - - /// - /// Opens the existing .swb files, then — when is set — - /// bakes any that are missing or stale. An up-to-date cache makes the bake a no-op, so the cost - /// lands only on a first boot or after a client or map update moves the fingerprint. - /// - /// Both halves run here rather than in Configure: the fingerprint hashes the map files, so - /// opening a .swb forces the lazy property. In Configure that would - /// build every TileMatrix ahead of TileMatrixLoader, possibly before - /// TileMatrix.Configure() settles Pre6000ClientSupport — both sit at the default - /// call priority and the phase sort is unstable. - /// - /// A reader only opens once its fingerprint validates, so an open reader is proof of a good - /// bake and the map is skipped without fingerprinting it again. - /// - public static void Initialize() - { - AutoLoadAtStartup(); - - if (!ServerConfiguration.GetSetting(PrebakeSetting, false)) - { - return; - } - - var baked = 0; - for (var i = 0; i < Map.Maps.Length; i++) - { - var map = Map.Maps[i]; - if (map == null || map == Map.Internal) - { - continue; - } - - if (StepCache.Instance.HasLazyReader(map.MapID)) - { - continue; // already has a fingerprint-valid .swb open - } - - var path = PathFor(map.MapID); - - logger.Information( - "PathBake: pre-baking map {MapId} (pathfinding.prebakeMaps) — this can take several minutes...", - map.MapID - ); - StepCache.Instance.BakeMap(map.MapID, path); - StepCache.Instance.ClearResidentChunks(); - - // Just this map: a blanket AutoLoadAtStartup() would reopen every reader already open. - StepCache.Instance.TryOpenLazyReader(path, map.MapID); - baked++; - } - - if (baked > 0) - { - logger.Information("PathBake: pre-bake complete ({Count} map(s) written).", baked); - } - } - - /// - /// Opens Data/Pathfinding/<mapId>.swb as a backing store for every map. Only the header and - /// index are read up front; chunk records are fetched as the cache asks for them, so resident - /// memory stays bounded by the LRU cap however large the files are. - /// - private static void AutoLoadAtStartup() - { - for (var i = 0; i < Map.Maps.Length; i++) - { - var map = Map.Maps[i]; - if (map == null || map == Map.Internal) - { - continue; - } - StepCache.Instance.TryOpenLazyReader(PathFor(map.MapID), map.MapID); - } - } - - [Usage("PathCacheStats")] - [Description("Reports StepCache resident-chunk count and hit/miss/eviction telemetry.")] - private static void OnPathCacheStats(CommandEventArgs e) - { - var stats = StepCache.Instance.GetStats(); - var from = e.Mobile; - - from.SendMessage($"StepCache: {stats.ResidentChunks} chunks resident"); - from.SendMessage($" builds={stats.BuildsTotal} hits={stats.Hits}"); - from.SendMessage($" miss(notBuilt)={stats.MissesNotBuilt} miss(dirty)={stats.MissesDirtyRebuild}"); - from.SendMessage($" fallthru(multiZ)={stats.FallthroughMultiZ} fallthru(offMap)={stats.FallthroughOffMap} fallthru(srcZ)={stats.FallthroughSourceZMismatch}"); - from.SendMessage($" fallthru(multi)={stats.FallthroughMulti} fallthru(notBuilt)={stats.FallthroughNotBuilt}"); - from.SendMessage($" multiLocalHits={stats.MultiLocalHits}"); - from.SendMessage($" evictions(lruCap)={stats.EvictionsByLruCap}"); - } - - [Usage("PathCacheClear")] - [Description("Drops all StepCache resident chunks and zeros the telemetry counters.")] - private static void OnPathCacheClear(CommandEventArgs e) - { - var residentBefore = StepCache.Instance.GetStats().ResidentChunks; - StepCache.Instance.Clear(); - e.Mobile.SendMessage($"StepCache cleared: {residentBefore} chunks dropped, counters reset."); - } - - [Usage("PathBake [mapId]")] - [Description("Walks every chunk of the given map (or all loaded maps) building the full static step cache, then saves it to Data/Pathfinding/.swb so a future boot has zero first-pathfind latency. WARNING: blocks the game loop for several seconds and transiently uses hundreds of MB per map — run during maintenance, not peak hours.")] - private static void OnPathBake(CommandEventArgs e) - { - var from = e.Mobile; - int? only = e.Arguments.Length > 0 && int.TryParse(e.Arguments[0], out var parsed) ? parsed : null; - - from.SendMessage("PathBake: building the static step cache. The server will pause briefly per map..."); - - var totalChunks = 0; - var totalMaps = 0; - var sw = Stopwatch.StartNew(); - - for (var i = 0; i < Map.Maps.Length; i++) - { - var map = Map.Maps[i]; - if (map == null || map == Map.Internal || only.HasValue && map.MapID != only.Value) - { - continue; - } - - // BakeMap leaves every chunk it built resident. Drop them between maps so peak memory - // is one map's worth rather than all of them, and the footprint afterwards is back - // under the LRU cap. - var written = StepCache.Instance.BakeMap(map.MapID, PathFor(map.MapID)); - StepCache.Instance.ClearResidentChunks(); - - if (written > 0) - { - totalChunks += written; - totalMaps++; - from.SendMessage($" map {map.MapID}: {written} chunks → {PathFor(map.MapID)}"); - } - } - - sw.Stop(); - - if (totalMaps == 0) - { - from.SendMessage(only.HasValue ? $"PathBake: map {only.Value} not loaded." : "PathBake: no maps to bake."); - return; - } - - // Reopen what we just wrote, so the bake is usable immediately without a restart. - AutoLoadAtStartup(); - from.SendMessage($"PathBake: {totalChunks} chunks across {totalMaps} map(s) in {sw.Elapsed.TotalSeconds:F1}s; lazy readers reopened."); - } - - [Usage("PathCacheSave")] - [Description("Persists resident StepCache chunks for every loaded map to Data/Pathfinding/.swb.")] - private static void OnPathCacheSave(CommandEventArgs e) - { - var totalChunks = 0; - var totalMaps = 0; - for (var i = 0; i < Map.Maps.Length; i++) - { - var map = Map.Maps[i]; - if (map == null || map == Map.Internal) - { - continue; - } - var path = PathFor(map.MapID); - var written = StepCache.Instance.SaveToFile(path, map.MapID); - if (written > 0) - { - totalChunks += written; - totalMaps++; - e.Mobile.SendMessage($" map {map.MapID}: {written} chunks → {path}"); - } - } - e.Mobile.SendMessage($"StepCache saved: {totalChunks} chunks across {totalMaps} map(s)."); - } - - [Usage("PathCacheLoad")] - [Description("Opens Data/Pathfinding/.swb as a lazy backing store for every map. Chunks are fetched on demand, so RAM stays bounded by the LRU cap regardless of file size.")] - private static void OnPathCacheLoad(CommandEventArgs e) - { - var openedMaps = 0; - for (var i = 0; i < Map.Maps.Length; i++) - { - var map = Map.Maps[i]; - if (map == null || map == Map.Internal) - { - continue; - } - if (StepCache.Instance.TryOpenLazyReader(PathFor(map.MapID), map.MapID)) - { - openedMaps++; - } - } - e.Mobile.SendMessage( - $"StepCache: opened {openedMaps} map(s) for lazy loading (total readers open: {StepCache.Instance.OpenLazyReaderCount})." - ); - } - - [Usage("PathRecord [on|off|flush|status]")] - [Description("Toggles PathfindRecorder. With no arg, reports state. 'on' enables JSONL capture of every Find call; 'off' disables and flushes; 'flush' forces a buffer flush without disabling.")] - private static void OnPathRecord(CommandEventArgs e) - { - var arg = (e.Arguments.Length > 0 ? e.Arguments[0] : "status").ToLowerInvariant(); - var from = e.Mobile; - switch (arg) - { - case "on": - { - PathfindRecorder.SetEnabled(true); - from.SendMessage(PathfindRecorder.Enabled - ? $"PathRecord: ON, writing to {PathfindRecorder.OutputPath}" - : "PathRecord: enable failed (see server log)"); - break; - } - case "off": - { - PathfindRecorder.SetEnabled(false); - from.SendMessage("PathRecord: OFF"); - break; - } - case "flush": - { - PathfindRecorder.Flush(); - from.SendMessage($"PathRecord: flushed ({PathfindRecorder.RecordsWritten} records this session)"); - break; - } - default: - { - from.SendMessage( - $"PathRecord: {(PathfindRecorder.Enabled ? "ON" : "OFF")}, " - + $"path={PathfindRecorder.OutputPath}, records={PathfindRecorder.RecordsWritten}" - ); - break; - } - } - } -} diff --git a/Projects/UOContent/Engines/Pathing/PathDiag.cs b/Projects/UOContent/Engines/Pathing/PathDiag.cs deleted file mode 100644 index 38bd36bc4..000000000 --- a/Projects/UOContent/Engines/Pathing/PathDiag.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using Server.Engines.Pathing.Cache; -using Server.PathAlgorithms; -using Server.Targeting; - -namespace Server.Engines.Pathing; - -/// -/// Diagnoses why the step cache does or doesn't serve a given route. Stand where the creature -/// would start, run [PathDiag, target the goal; the full report lands in -/// Logs/pathdiag.log and a summary goes to the client. It reports the tile makeup of the -/// start and goal cells alongside the standable surfaces the baker anchors to, the cache -/// hit/fallthrough breakdown for one warm Find, and warm timings. -/// -/// The fallthrough fraction is the number to read: a high one means the cache is paying for a -/// lookup on every cell and then taking the slow path anyway. That usually points at -/// static-over-land geometry — dungeon walkways, bridges, stairs, stacked floors — baking at the -/// wrong Z, which is why this is most useful when bringing up a custom map or facet. -/// -/// The promotion gate is forced eager for the duration, so the numbers reflect the cache's best -/// case rather than an artifact of chunks not having been built yet. -/// -public static class PathDiag -{ - private const int TimingIterations = 200; - - private static string LogPath => Path.Combine(Core.BaseDirectory, "Logs", "pathdiag.log"); - - public static void Configure() - { - CommandSystem.Register("PathDiag", AccessLevel.Administrator, OnPathDiag); - } - - [Usage("PathDiag")] - [Description("Diagnoses the step cache for a route (results appended to Logs/pathdiag.log): target a tile to record start/goal tile makeup, the per-Find cache hit/fallthrough breakdown, and warm timing.")] - private static void OnPathDiag(CommandEventArgs e) - { - var start = e.Mobile.Location; - e.Mobile.SendMessage("PathDiag: target the goal tile."); - e.Mobile.BeginTarget(-1, true, TargetFlags.None, (from, targeted) => OnTarget(from, start, targeted)); - } - - private static void OnTarget(Mobile from, Point3D start, object targeted) - { - if (targeted is not IPoint3D p) - { - return; - } - - var map = from.Map; - var goal = new Point3D(p.X, p.Y, p.Z); - - if (!Utility.InRange(start, goal, 38)) - { - from.SendMessage("PathDiag: goal is outside the A* search window (38 tiles); aborting."); - return; - } - - var cache = StepCache.Instance; - var previousThreshold = cache.MissPromotionThreshold; - cache.MissPromotionThreshold = 1; // build eagerly, so we measure the cache's best case - - StreamWriter log = null; - try - { - Directory.CreateDirectory(Path.GetDirectoryName(LogPath)!); - log = new StreamWriter(new FileStream(LogPath, FileMode.Append, FileAccess.Write, FileShare.Read)); - - log.WriteLine($"===== [{Core.Now:yyyy-MM-dd HH:mm:ss}] PathDiag ({start.X},{start.Y},{start.Z}) -> ({goal.X},{goal.Y},{goal.Z}) on {map} ====="); - - DumpCell(log, map, start.X, start.Y, start.Z, "start"); - DumpCell(log, map, goal.X, goal.Y, goal.Z, "goal"); - var find = RunInstrumentedFind(log, from, map, start, goal); - var (minUs, avgUs) = TimeWarm(log, from, map, start, goal); - log.WriteLine(); - - from.SendMessage($"PathDiag ({start.X},{start.Y},{start.Z})->({goal.X},{goal.Y},{goal.Z}): {find.result}"); - from.SendMessage($" cache fallthrough {find.fallthroughPct:F1}% of {find.total}; warm min={minUs:F1}us avg={avgUs:F1}us"); - from.SendMessage($" full report appended to Logs/pathdiag.log"); - } - catch (IOException ex) - { - from.SendMessage($"PathDiag: failed to write {LogPath}: {ex.Message}"); - } - finally - { - log?.Dispose(); - cache.MissPromotionThreshold = previousThreshold; - } - } - - /// - /// Dumps one cell's tiles and the surfaces the baker anchors to. A wide gap between the query Z - /// and every standable surface is the signature of a cell the cache can't serve. - /// - private static void DumpCell(TextWriter log, Map map, int x, int y, int queryZ, string label) - { - map.GetAverageZ(x, y, out var landZ, out var avgZ, out var landTop); - var landTile = map.Tiles.GetLandTile(x, y); - var landFlags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; - var landImpassable = (landFlags & TileFlag.Impassable) != 0; - var landWet = (landFlags & TileFlag.Wet) != 0; - - Span surfaces = stackalloc sbyte[16]; - var surfaceCount = StepProbe.ComputeStandableSurfaceZs(map, x, y, surfaces); - - log.WriteLine($"{label} cell ({x},{y}) queryZ={queryZ}:"); - log.WriteLine($" land: avgZ={avgZ} landZ={landZ} landTop={landTop} impassable={landImpassable} wet={landWet} ignored={landTile.Ignored}"); - - var sb = new System.Text.StringBuilder(); - for (var i = 0; i < surfaceCount; i++) - { - sb.Append(i == 0 ? "" : ",").Append(surfaces[i]); - } - log.WriteLine($" standable surfaces={surfaceCount} [{sb}] -> {(surfaceCount >= 2 ? "multi-Z (strata)" : "single-Z anchor")}"); - - log.WriteLine(" static/multi surfaces:"); - foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y)) - { - var data = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; - log.WriteLine($" id=0x{tile.ID:X4} z={tile.Z} top={tile.Z + data.CalcHeight} h={data.Height} surface={data.Surface} impass={data.Impassable} bridge={data.Bridge} wet={data.Wet}"); - } - } - - /// - /// Runs one Find against a warm cache and reports the counter delta it produced — the - /// hit/fallthrough mix for that single pathfind. - /// - private static (string result, double fallthroughPct, long total) RunInstrumentedFind( - TextWriter log, Mobile from, Map map, Point3D start, Point3D goal - ) - { - var cache = StepCache.Instance; - - // Build every chunk the route touches first, so the measured Find below reports steady-state - // behaviour rather than first-touch misses. - for (var i = 0; i < 3; i++) - { - BitmapAStarAlgorithm.Instance.Find(from, map, start, goal); - } - - var before = cache.GetStats(); - var path = BitmapAStarAlgorithm.Instance.Find(from, map, start, goal); - var after = cache.GetStats(); - - var served = after.Hits - before.Hits - + (after.MissesNotBuilt - before.MissesNotBuilt) - + (after.MissesDirtyRebuild - before.MissesDirtyRebuild); - var fallthrough = after.FallthroughMultiZ - before.FallthroughMultiZ - + (after.FallthroughSourceZMismatch - before.FallthroughSourceZMismatch) - + (after.FallthroughOffMap - before.FallthroughOffMap) - + (after.FallthroughNotBuilt - before.FallthroughNotBuilt); - var total = served + fallthrough; - var pct = total == 0 ? 0 : 100.0 * fallthrough / total; - var result = path == null ? "NO PATH" : $"{path.Length} steps"; - - log.WriteLine($"warm Find: {result}"); - log.WriteLine($" cache-served={served} fallthrough={fallthrough} ({pct:F1}% of {total} probes)"); - log.WriteLine($" fallthrough breakdown: multiZ={after.FallthroughMultiZ - before.FallthroughMultiZ} " + - $"srcZ={after.FallthroughSourceZMismatch - before.FallthroughSourceZMismatch} " + - $"offMap={after.FallthroughOffMap - before.FallthroughOffMap} " + - $"notBuilt={after.FallthroughNotBuilt - before.FallthroughNotBuilt}"); - if (path == null) - { - log.WriteLine(" NO PATH: goal unreachable within the 38-tile window (or not standable). This is an A* scope limit, independent of the cache."); - } - - return (result, pct, total); - } - - private static (double minUs, double avgUs) TimeWarm(TextWriter log, Mobile from, Map map, Point3D start, Point3D goal) - { - var sw = new Stopwatch(); - var minTicks = long.MaxValue; - long totalTicks = 0; - - for (var i = 0; i < TimingIterations; i++) - { - sw.Restart(); - BitmapAStarAlgorithm.Instance.Find(from, map, start, goal); - sw.Stop(); - totalTicks += sw.ElapsedTicks; - if (sw.ElapsedTicks < minTicks) - { - minTicks = sw.ElapsedTicks; - } - } - - var usPerTick = 1_000_000.0 / Stopwatch.Frequency; - var minUs = minTicks * usPerTick; - var avgUs = totalTicks * usPerTick / TimingIterations; - log.WriteLine($"timing over {TimingIterations} warm Finds: min={minUs:F1}us avg={avgUs:F1}us"); - return (minUs, avgUs); - } -} diff --git a/Projects/UOContent/Engines/Pathing/PathFollower.cs b/Projects/UOContent/Engines/Pathing/PathFollower.cs index 3a0a56fa5..fa2557f48 100644 --- a/Projects/UOContent/Engines/Pathing/PathFollower.cs +++ b/Projects/UOContent/Engines/Pathing/PathFollower.cs @@ -1,152 +1,155 @@ using System; using CalcMoves = Server.Movement.Movement; -namespace Server; - -public class PathFollower +namespace Server { - private static bool Enabled; - private static readonly TimeSpan RepathDelay = TimeSpan.FromSeconds(2.0); - - private readonly Mobile m_From; - private int m_Index; - private DateTime m_LastPathTime; - private Point3D m_Next, m_LastGoalLoc; - private MovementPath m_Path; - - public PathFollower(Mobile from, IPoint3D goal) + public class PathFollower { - m_From = from; - Goal = goal; - } + private static bool Enabled; + private static readonly TimeSpan RepathDelay = TimeSpan.FromSeconds(2.0); - public MoveMethod Mover { get; set; } + private readonly Mobile m_From; + private int m_Index; + private DateTime m_LastPathTime; + private Point3D m_Next, m_LastGoalLoc; + private MovementPath m_Path; - public IPoint3D Goal { get; } - - public static void Configure() - { - Enabled = ServerConfiguration.GetOrUpdateSetting("pathfinding.enable", true); - } - - public MoveResult Move(Direction d) => - Mover?.Invoke(d, true) ?? (m_From.Move(d) ? MoveResult.Success : MoveResult.Blocked); - - public Point3D GetGoalLocation() => (Goal as Item)?.GetWorldLocation() ?? new Point3D(Goal); - - public void Advance(ref Point3D p, int index) - { - if (m_Path?.Success == true) + public PathFollower(Mobile from, IPoint3D goal) { - var dirs = m_Path.Directions; + m_From = from; + Goal = goal; + } - if (index >= 0 && index < dirs.Length) + public MoveMethod Mover { get; set; } + + public IPoint3D Goal { get; } + + public static void Configure() + { + Enabled = ServerConfiguration.GetOrUpdateSetting("pathfinding.enable", true); + } + + public MoveResult Move(Direction d) => + Mover?.Invoke(d, true) ?? (m_From.Move(d) ? MoveResult.Success : MoveResult.Blocked); + + public Point3D GetGoalLocation() => (Goal as Item)?.GetWorldLocation() ?? new Point3D(Goal); + + public void Advance(ref Point3D p, int index) + { + if (m_Path?.Success == true) { - CalcMoves.Offset(dirs[index], ref p); + var dirs = m_Path.Directions; + + if (index >= 0 && index < dirs.Length) + { + CalcMoves.Offset(dirs[index], ref p); + } } } - } - public void ForceRepath() - { - m_Path = null; - } - - public bool CheckPath() - { - if (!Enabled) + public void ForceRepath() { - return false; + m_Path = null; } - var goal = GetGoalLocation(); - - if (m_Path != null && (m_Path.Success && goal == m_LastGoalLoc || m_LastPathTime + RepathDelay > Core.Now) && - !(m_Path.Success && Check(m_From.Location, m_LastGoalLoc, 0))) + public bool CheckPath() { - return false; - } + if (!Enabled) + { + return false; + } - m_LastPathTime = Core.Now; - m_LastGoalLoc = goal; + var goal = GetGoalLocation(); - m_Path = new MovementPath(m_From, goal); + if (m_Path != null && (m_Path.Success && goal == m_LastGoalLoc || m_LastPathTime + RepathDelay > Core.Now) && + !(m_Path.Success && Check(m_From.Location, m_LastGoalLoc, 0))) + { + return false; + } - m_Index = 0; - m_Next = m_From.Location; + m_LastPathTime = Core.Now; + m_LastGoalLoc = goal; - Advance(ref m_Next, m_Index); + m_Path = new MovementPath(m_From, goal); - return true; - } + m_Index = 0; + m_Next = m_From.Location; - public static bool Check(Point3D loc, Point3D goal, int range) => - Utility.InRange(loc, goal, range) && (range > 1 || (loc.Z - goal.Z).Abs() < 16); + Advance(ref m_Next, m_Index); - public bool Follow(bool run, int range) - { - var goal = GetGoalLocation(); - Direction d; - - if (Check(m_From.Location, goal, range)) - { return true; } - var repathed = CheckPath(); + public static bool Check(Point3D loc, Point3D goal, int range) => + Utility.InRange(loc, goal, range) && (range > 1 || (loc.Z - goal.Z).Abs() < 16); - if (!(Enabled && m_Path.Success)) + public bool Follow(bool run, int range) { - d = m_From.GetDirectionTo(goal, run); - m_From.SetDirection(d); + var goal = GetGoalLocation(); + Direction d; - return Move(d) is MoveResult.Success or MoveResult.SuccessAutoTurn && Check(m_From.Location, goal, range); - } - - d = m_From.GetDirectionTo(m_Next, run); - m_From.SetDirection(d); - var res = Move(d); - - if (res == MoveResult.Blocked) - { - if (repathed) + if (Check(m_From.Location, goal, range)) { - return false; + return true; } - m_Path = null; - CheckPath(); + var repathed = CheckPath(); - if (!m_Path!.Success) + if (!(Enabled && m_Path.Success)) { - d = m_From.GetDirectionTo(goal); + d = m_From.GetDirectionTo(goal, run); m_From.SetDirection(d); - return Move(d) is MoveResult.Success or MoveResult.SuccessAutoTurn && Check(m_From.Location, goal, range); + return Move(d) is MoveResult.Success or MoveResult.SuccessAutoTurn + && Check(m_From.Location, goal, range); } - d = m_From.GetDirectionTo(m_Next); + d = m_From.GetDirectionTo(m_Next, run); m_From.SetDirection(d); + var res = Move(d); - if (Move(d) == MoveResult.Blocked) + if (res == MoveResult.Blocked) { - return false; - } - } + if (repathed) + { + return false; + } - if (m_From.X == m_Next.X && m_From.Y == m_Next.Y) - { - if (m_From.Z == m_Next.Z) - { - ++m_Index; - Advance(ref m_Next, m_Index); - } - else - { m_Path = null; - } - } + CheckPath(); - return Check(m_From.Location, goal, range); + if (!m_Path!.Success) + { + d = m_From.GetDirectionTo(goal); + m_From.SetDirection(d); + + return Move(d) is MoveResult.Success or MoveResult.SuccessAutoTurn + && Check(m_From.Location, goal, range); + } + + d = m_From.GetDirectionTo(m_Next); + m_From.SetDirection(d); + + if (Move(d) == MoveResult.Blocked) + { + return false; + } + } + + if (m_From.X == m_Next.X && m_From.Y == m_Next.Y) + { + if (m_From.Z == m_Next.Z) + { + ++m_Index; + Advance(ref m_Next, m_Index); + } + else + { + m_Path = null; + } + } + + return Check(m_From.Location, goal, range); + } } } diff --git a/Projects/UOContent/Engines/Pathing/PathfindRecorder.cs b/Projects/UOContent/Engines/Pathing/PathfindRecorder.cs deleted file mode 100644 index c3490d235..000000000 --- a/Projects/UOContent/Engines/Pathing/PathfindRecorder.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System.IO; -using System.Text; -using Server.Logging; -using Server.Mobiles; -using Server.Text; - -namespace Server.Engines.Pathing; - -/// -/// Appends one JSONL record per pathfind, capturing the inputs — start, goal, map, capability -/// flags — needed to replay it later in a benchmark. Toggled at runtime with [PathRecord; -/// only seeds the initial state from server.cfg. -/// -/// Meant for short bursts: turn it on, walk the region or trigger the scenario, turn it off. The -/// writes go through a StreamWriter's buffer on the game thread, so a busy shard doing hundreds of -/// pathfinds a second can saturate that buffer and stall the loop on disk I/O. Sustained capture -/// would need an async sink with backpressure. -/// -public static class PathfindRecorder -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(PathfindRecorder)); - - private static StreamWriter _writer; - - public static bool Enabled { get; private set; } - public static string OutputPath { get; set; } - public static long RecordsWritten { get; private set; } - - public static void Configure() - { - OutputPath = ServerConfiguration.GetOrUpdateSetting( - "pathfinding.recorder.path", - Path.Combine(Core.BaseDirectory, "Data", "Pathfinding", "recordings", "pathfinds.jsonl") - ); - - var startEnabled = ServerConfiguration.GetOrUpdateSetting("pathfinding.recorder.enable", false); - if (startEnabled) - { - SetEnabled(true); - } - } - - /// - /// Toggles recording, opening the file on enable and flushing and closing it on disable. - /// Idempotent. - /// - public static void SetEnabled(bool enabled) - { - if (enabled == Enabled) - { - return; - } - - if (enabled) - { - try - { - Directory.CreateDirectory(Path.GetDirectoryName(OutputPath) ?? "."); - var stream = new FileStream(OutputPath, FileMode.Append, FileAccess.Write, FileShare.Read); - _writer = new StreamWriter(stream, new UTF8Encoding(false)); - Enabled = true; - logger.Information("PathfindRecorder enabled, writing to {Path}", OutputPath); - } - catch (IOException ex) - { - logger.Warning(ex, "PathfindRecorder: failed to open {Path} for write", OutputPath); - _writer = null; - Enabled = false; - } - } - else - { - Enabled = false; - try - { - _writer?.Flush(); - _writer?.Dispose(); - } - catch (IOException ex) - { - logger.Warning(ex, "PathfindRecorder: error closing {Path}", OutputPath); - } - _writer = null; - logger.Information("PathfindRecorder disabled ({Count} records this session)", RecordsWritten); - } - } - - /// - /// Pushes the writer's buffer to disk, so a capture can be inspected without disabling first. - /// No-op when disabled. - /// - public static void Flush() - { - try - { - _writer?.Flush(); - } - catch (IOException ex) - { - logger.Warning(ex, "PathfindRecorder: flush failed for {Path}", OutputPath); - } - } - - /// - /// Records one Find. Sits on the pathfinding hot path, so it costs a single bool check when - /// disabled. - /// - public static void RecordIfEnabled(Mobile m, Map map, Point3D start, Point3D goal) - { - if (!Enabled || _writer == null || m == null || map == null) - { - return; - } - - var canSwim = false; - var canFly = false; - var canOpenDoors = false; - var canMoveOverObstacles = false; - if (m is BaseCreature bc) - { - canSwim = bc.CanSwim; - canFly = bc.CanFly; - canOpenDoors = bc.CanOpenDoors; - canMoveOverObstacles = bc.CanMoveOverObstacles; - } - - try - { - // One interpolation covers every numeric field without a per-field ToString. The bools - // are appended as literals because JSON wants lowercase and bool.ToString() capitalizes. - using var vsb = ValueStringBuilder.Create(192); - vsb.Append( - $"{{\"Name\":\"recorded\",\"MapId\":{map.MapID},\"StartX\":{start.X},\"StartY\":{start.Y},\"StartZ\":{start.Z},\"GoalX\":{goal.X},\"GoalY\":{goal.Y},\"GoalZ\":{goal.Z},\"CanSwim\":" - ); - vsb.Append(canSwim ? "true" : "false"); - vsb.Append(",\"CanFly\":"); - vsb.Append(canFly ? "true" : "false"); - vsb.Append(",\"CanOpenDoors\":"); - vsb.Append(canOpenDoors ? "true" : "false"); - vsb.Append(",\"CanMoveOverObstacles\":"); - vsb.Append(canMoveOverObstacles ? "true" : "false"); - vsb.Append("}\n"); - _writer.Write(vsb.AsSpan()); - RecordsWritten++; - } - catch (IOException ex) - { - logger.Warning(ex, "PathfindRecorder: write failed, disabling"); - SetEnabled(false); - } - } -} diff --git a/Projects/UOContent/Engines/Plants/EmptyTheBowlGump.cs b/Projects/UOContent/Engines/Plants/EmptyTheBowlGump.cs index 539cf3036..1447c7070 100644 --- a/Projects/UOContent/Engines/Plants/EmptyTheBowlGump.cs +++ b/Projects/UOContent/Engines/Plants/EmptyTheBowlGump.cs @@ -2,125 +2,125 @@ using Server.Engines.Help; using Server.Gumps; using Server.Network; -namespace Server.Engines.Plants; - -public class EmptyTheBowlGump : DynamicGump +namespace Server.Engines.Plants { - private readonly PlantItem _plant; - - public override bool Singleton => true; - - private EmptyTheBowlGump(PlantItem plant) : base(20, 20) + public class EmptyTheBowlGump : Gump { - _plant = plant; - } + private readonly PlantItem m_Plant; - public static void DisplayTo(Mobile from, PlantItem plant) - { - if (from?.NetState == null || plant == null || plant.Deleted) + public EmptyTheBowlGump(PlantItem plant) : base(20, 20) { - return; + m_Plant = plant; + + DrawBackground(); + + AddLabel(90, 70, 0x44, "Empty the bowl?"); + + DrawPicture(); + + AddButton(98, 150, 0x47E, 0x480, 1); // Cancel + + AddButton(138, 151, 0xD2, 0xD2, 2); // Help + AddLabel(143, 151, 0x835, "?"); + + AddButton(168, 150, 0x481, 0x483, 3); // Ok } - from.SendGump(new EmptyTheBowlGump(plant)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(50, 50, 200, 150, 0xE10); - - builder.AddItem(45, 45, 0xCEF); - builder.AddItem(45, 118, 0xCF0); - - builder.AddItem(211, 45, 0xCEB); - builder.AddItem(211, 118, 0xCEC); - - builder.AddLabel(90, 70, 0x44, "Empty the bowl?"); - - builder.AddItem(90, 100, 0x1602); - builder.AddImage(140, 102, 0x15E1); - builder.AddItem(160, 100, 0x15FD); - - if (_plant.PlantStatus != PlantStatus.BowlOfDirt && _plant.PlantStatus < PlantStatus.Plant) + private void DrawBackground() { - builder.AddItem(156, 130, 0xDCF); // Seed + AddBackground(50, 50, 200, 150, 0xE10); + + AddItem(45, 45, 0xCEF); + AddItem(45, 118, 0xCF0); + + AddItem(211, 45, 0xCEB); + AddItem(211, 118, 0xCEC); } - builder.AddButton(98, 150, 0x47E, 0x480, 1); // Cancel - - builder.AddButton(138, 151, 0xD2, 0xD2, 2); // Help - builder.AddLabel(143, 151, 0x835, "?"); - - builder.AddButton(168, 150, 0x481, 0x483, 3); // Ok - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; - - if (info.ButtonID == 0 || _plant.Deleted || _plant.PlantStatus >= PlantStatus.DecorativePlant) + private void DrawPicture() { - return; + AddItem(90, 100, 0x1602); + AddImage(140, 102, 0x15E1); + AddItem(160, 100, 0x15FD); + + if (m_Plant.PlantStatus != PlantStatus.BowlOfDirt && m_Plant.PlantStatus < PlantStatus.Plant) + { + AddItem(156, 130, 0xDCF); // Seed + } } - if (info.ButtonID == 3 && !from.InRange(_plant.GetWorldLocation(), 3)) + public override void OnResponse(NetState sender, in RelayInfo info) { - from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away. - return; - } + var from = sender.Mobile; - if (!_plant.IsUsableBy(from)) - { - _plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it. - return; - } + if (info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus >= PlantStatus.DecorativePlant) + { + return; + } - switch (info.ButtonID) - { - case 1: // Cancel - { - MainPlantGump.DisplayTo(from, _plant); - break; - } - case 2: // Help - { - from.NetState.SendDisplayHelpTopic(HelpTopic.EmptyingBowl); - from.SendGump(this); - break; - } - case 3: // Ok - { - var bowl = new PlantBowl(); + if (info.ButtonID == 3 && !from.InRange(m_Plant.GetWorldLocation(), 3)) + { + from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away. + return; + } - if (!from.PlaceInBackpack(bowl)) + if (!m_Plant.IsUsableBy(from)) + { + m_Plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it. + return; + } + + switch (info.ButtonID) + { + case 1: // Cancel { - bowl.Delete(); + from.SendGump(new MainPlantGump(m_Plant)); - _plant.LabelTo(from, 1053047); // You cannot empty a bowl with a full pack! - MainPlantGump.DisplayTo(from, _plant); break; } - - if (_plant.PlantStatus != PlantStatus.BowlOfDirt && _plant.PlantStatus < PlantStatus.Plant) + case 2: // Help { - var seed = new Seed(_plant.PlantType, _plant.PlantHue, _plant.ShowType); + from.NetState.SendDisplayHelpTopic(HelpTopic.EmptyingBowl); - if (!from.PlaceInBackpack(seed)) + from.SendGump(new EmptyTheBowlGump(m_Plant)); + + break; + } + case 3: // Ok + { + var bowl = new PlantBowl(); + + if (!from.PlaceInBackpack(bowl)) { bowl.Delete(); - seed.Delete(); - _plant.LabelTo(from, 1053047); // You cannot empty a bowl with a full pack! - MainPlantGump.DisplayTo(from, _plant); + m_Plant.LabelTo(from, 1053047); // You cannot empty a bowl with a full pack! + from.SendGump(new MainPlantGump(m_Plant)); + break; } - } - _plant.Delete(); - break; - } + if (m_Plant.PlantStatus != PlantStatus.BowlOfDirt && m_Plant.PlantStatus < PlantStatus.Plant) + { + var seed = new Seed(m_Plant.PlantType, m_Plant.PlantHue, m_Plant.ShowType); + + if (!from.PlaceInBackpack(seed)) + { + bowl.Delete(); + seed.Delete(); + + m_Plant.LabelTo(from, 1053047); // You cannot empty a bowl with a full pack! + from.SendGump(new MainPlantGump(m_Plant)); + + break; + } + } + + m_Plant.Delete(); + + break; + } + } } } } diff --git a/Projects/UOContent/Engines/Plants/MainPlantGump.cs b/Projects/UOContent/Engines/Plants/MainPlantGump.cs index 464886ca5..0e831898b 100644 --- a/Projects/UOContent/Engines/Plants/MainPlantGump.cs +++ b/Projects/UOContent/Engines/Plants/MainPlantGump.cs @@ -1,468 +1,475 @@ using System; -using System.Runtime.CompilerServices; using Server.Engines.Help; using Server.Gumps; using Server.Items; using Server.Network; -namespace Server.Engines.Plants; - -public class MainPlantGump : DynamicGump +namespace Server.Engines.Plants { - private readonly PlantItem _plant; - - public override bool Singleton => true; - - private MainPlantGump(PlantItem plant) : base(20, 20) + public class MainPlantGump : Gump { - _plant = plant; - } + private readonly PlantItem m_Plant; - public static void DisplayTo(Mobile from, PlantItem plant) - { - if (from?.NetState == null || plant == null || plant.Deleted) + public MainPlantGump(PlantItem plant) : base(20, 20) { - return; + m_Plant = plant; + + DrawBackground(); + + DrawPlant(); + + AddButton(71, 67, 0xD4, 0xD4, 1); // Reproduction menu + AddItem(59, 68, 0xD08); + + var system = plant.PlantSystem; + + AddButton(71, 91, 0xD4, 0xD4, 2); // Infestation + AddItem(8, 96, 0x372); + AddPlus(95, 92, system.Infestation); + + AddButton(71, 115, 0xD4, 0xD4, 3); // Fungus + AddItem(58, 115, 0xD16); + AddPlus(95, 116, system.Fungus); + + AddButton(71, 139, 0xD4, 0xD4, 4); // Poison + AddItem(59, 143, 0x1AE4); + AddPlus(95, 140, system.Poison); + + AddButton(71, 163, 0xD4, 0xD4, 5); // Disease + AddItem(55, 167, 0x1727); + AddPlus(95, 164, system.Disease); + + AddButton(209, 67, 0xD2, 0xD2, 6); // Water + AddItem(193, 67, 0x1F9D); + AddPlusMinus(196, 67, system.Water); + + AddButton(209, 91, 0xD4, 0xD4, 7); // Poison potion + AddItem(201, 91, 0xF0A); + AddLevel(196, 91, system.PoisonPotion); + + AddButton(209, 115, 0xD4, 0xD4, 8); // Cure potion + AddItem(201, 115, 0xF07); + AddLevel(196, 115, system.CurePotion); + + AddButton(209, 139, 0xD4, 0xD4, 9); // Heal potion + AddItem(201, 139, 0xF0C); + AddLevel(196, 139, system.HealPotion); + + AddButton(209, 163, 0xD4, 0xD4, 10); // Strength potion + AddItem(201, 163, 0xF09); + AddLevel(196, 163, system.StrengthPotion); + + AddImage(48, 47, 0xD2); + AddLevel(54, 47, (int)m_Plant.PlantStatus); + + AddImage(232, 47, 0xD2); + AddGrowthIndicator(239, 47); + + AddButton(48, 183, 0xD2, 0xD2, 11); // Help + AddLabel(54, 183, 0x835, "?"); + + AddButton(232, 183, 0xD4, 0xD4, 12); // Empty the bowl + AddItem(219, 180, 0x15FD); } - from.SendGump(new MainPlantGump(plant)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - DrawBackground(ref builder); - DrawPlant(ref builder); - - builder.AddButton(71, 67, 0xD4, 0xD4, 1); // Reproduction menu - builder.AddItem(59, 68, 0xD08); - - var system = _plant.PlantSystem; - - builder.AddButton(71, 91, 0xD4, 0xD4, 2); // Infestation - builder.AddItem(8, 96, 0x372); - AddPlus(ref builder, 95, 92, system.Infestation); - - builder.AddButton(71, 115, 0xD4, 0xD4, 3); // Fungus - builder.AddItem(58, 115, 0xD16); - AddPlus(ref builder, 95, 116, system.Fungus); - - builder.AddButton(71, 139, 0xD4, 0xD4, 4); // Poison - builder.AddItem(59, 143, 0x1AE4); - AddPlus(ref builder, 95, 140, system.Poison); - - builder.AddButton(71, 163, 0xD4, 0xD4, 5); // Disease - builder.AddItem(55, 167, 0x1727); - AddPlus(ref builder, 95, 164, system.Disease); - - builder.AddButton(209, 67, 0xD2, 0xD2, 6); // Water - builder.AddItem(193, 67, 0x1F9D); - AddPlusMinus(ref builder, 196, 67, system.Water); - - builder.AddButton(209, 91, 0xD4, 0xD4, 7); // Poison potion - builder.AddItem(201, 91, 0xF0A); - AddLevel(ref builder, 196, 91, system.PoisonPotion); - - builder.AddButton(209, 115, 0xD4, 0xD4, 8); // Cure potion - builder.AddItem(201, 115, 0xF07); - AddLevel(ref builder, 196, 115, system.CurePotion); - - builder.AddButton(209, 139, 0xD4, 0xD4, 9); // Heal potion - builder.AddItem(201, 139, 0xF0C); - AddLevel(ref builder, 196, 139, system.HealPotion); - - builder.AddButton(209, 163, 0xD4, 0xD4, 10); // Strength potion - builder.AddItem(201, 163, 0xF09); - AddLevel(ref builder, 196, 163, system.StrengthPotion); - - builder.AddImage(48, 47, 0xD2); - AddLevel(ref builder, 54, 47, (int)_plant.PlantStatus); - - builder.AddImage(232, 47, 0xD2); - AddGrowthIndicator(ref builder, 239, 47); - - builder.AddButton(48, 183, 0xD2, 0xD2, 11); // Help - builder.AddLabel(54, 183, 0x835, "?"); - - builder.AddButton(232, 183, 0xD4, 0xD4, 12); // Empty the bowl - builder.AddItem(219, 180, 0x15FD); - } - - private static void DrawBackground(ref DynamicGumpBuilder builder) - { - builder.AddBackground(50, 50, 200, 150, 0xE10); - - builder.AddItem(45, 45, 0xCEF); - builder.AddItem(45, 118, 0xCF0); - - builder.AddItem(211, 45, 0xCEB); - builder.AddItem(211, 118, 0xCEC); - } - - private void DrawPlant(ref DynamicGumpBuilder builder) - { - var status = _plant.PlantStatus; - - if (status < PlantStatus.FullGrownPlant) + private void DrawBackground() { - builder.AddImage(110, 85, 0x589); + AddBackground(50, 50, 200, 150, 0xE10); - builder.AddItem(122, 94, 0x914); - builder.AddItem(135, 94, 0x914); - builder.AddItem(120, 112, 0x914); - builder.AddItem(135, 112, 0x914); + AddItem(45, 45, 0xCEF); + AddItem(45, 118, 0xCF0); - if (status >= PlantStatus.Stage2) - { - builder.AddItem(127, 112, 0xC62); - } - - if (status is PlantStatus.Stage3 or PlantStatus.Stage4) - { - builder.AddItem(129, 85, 0xC7E); - } - - if (status >= PlantStatus.Stage4) - { - builder.AddItem(121, 117, 0xC62); - builder.AddItem(133, 117, 0xC62); - } - - if (status >= PlantStatus.Stage5) - { - builder.AddItem(110, 100, 0xC62); - builder.AddItem(140, 100, 0xC62); - builder.AddItem(110, 130, 0xC62); - builder.AddItem(140, 130, 0xC62); - } - - if (status >= PlantStatus.Stage6) - { - builder.AddItem(105, 115, 0xC62); - builder.AddItem(145, 115, 0xC62); - builder.AddItem(125, 90, 0xC62); - builder.AddItem(125, 135, 0xC62); - } + AddItem(211, 45, 0xCEB); + AddItem(211, 118, 0xCEC); } - else - { - var typeInfo = PlantTypeInfo.GetInfo(_plant.PlantType); - var hueInfo = PlantHueInfo.GetInfo(_plant.PlantHue); - // The large images for these trees trigger a client crash, so use a smaller, generic tree. - if (_plant.PlantType is PlantType.CypressTwisted or PlantType.CypressStraight) + private void DrawPlant() + { + var status = m_Plant.PlantStatus; + + if (status < PlantStatus.FullGrownPlant) { - builder.AddItem(130 + typeInfo.OffsetX, 96 + typeInfo.OffsetY, 0x0CCA, hueInfo.Hue); + AddImage(110, 85, 0x589); + + AddItem(122, 94, 0x914); + AddItem(135, 94, 0x914); + AddItem(120, 112, 0x914); + AddItem(135, 112, 0x914); + + if (status >= PlantStatus.Stage2) + { + AddItem(127, 112, 0xC62); + } + + if (status is PlantStatus.Stage3 or PlantStatus.Stage4) + { + AddItem(129, 85, 0xC7E); + } + + if (status >= PlantStatus.Stage4) + { + AddItem(121, 117, 0xC62); + AddItem(133, 117, 0xC62); + } + + if (status >= PlantStatus.Stage5) + { + AddItem(110, 100, 0xC62); + AddItem(140, 100, 0xC62); + AddItem(110, 130, 0xC62); + AddItem(140, 130, 0xC62); + } + + if (status >= PlantStatus.Stage6) + { + AddItem(105, 115, 0xC62); + AddItem(145, 115, 0xC62); + AddItem(125, 90, 0xC62); + AddItem(125, 135, 0xC62); + } } else { - builder.AddItem(130 + typeInfo.OffsetX, 96 + typeInfo.OffsetY, typeInfo.ItemID, hueInfo.Hue); + var typeInfo = PlantTypeInfo.GetInfo(m_Plant.PlantType); + var hueInfo = PlantHueInfo.GetInfo(m_Plant.PlantHue); + + // The large images for these trees trigger a client crash, so use a smaller, generic tree. + if (m_Plant.PlantType is PlantType.CypressTwisted or PlantType.CypressStraight) + { + AddItem(130 + typeInfo.OffsetX, 96 + typeInfo.OffsetY, 0x0CCA, hueInfo.Hue); + } + else + { + AddItem(130 + typeInfo.OffsetX, 96 + typeInfo.OffsetY, typeInfo.ItemID, hueInfo.Hue); + } } - } - if (status != PlantStatus.BowlOfDirt) - { - var message = _plant.PlantSystem.GetLocalizedHealth(); - - switch (_plant.PlantSystem.Health) + if (status != PlantStatus.BowlOfDirt) { - case PlantHealth.Dying: - { - builder.AddItem(92, 167, 0x1B9D); - builder.AddItem(161, 167, 0x1B9D); + var message = m_Plant.PlantSystem.GetLocalizedHealth(); - builder.AddHtmlLocalized(136, 167, 42, 20, message, 0xFC00); - break; - } - case PlantHealth.Wilted: - { - builder.AddItem(91, 164, 0x18E6); - builder.AddItem(161, 164, 0x18E6); - - builder.AddHtmlLocalized(132, 167, 42, 20, message, 0xC207); - break; - } - case PlantHealth.Healthy: - { - builder.AddItem(96, 168, 0xC61); - builder.AddItem(162, 168, 0xC61); - - builder.AddHtmlLocalized(129, 167, 42, 20, message, 0x8200); - break; - } - case PlantHealth.Vibrant: - { - builder.AddItem(93, 162, 0x1A99); - builder.AddItem(162, 162, 0x1A99); - - builder.AddHtmlLocalized(129, 167, 42, 20, message, 0x83E0); - break; - } - } - } - } - - private static void AddPlus(ref DynamicGumpBuilder builder, int x, int y, int value) - { - switch (value) - { - case 1: + switch (m_Plant.PlantSystem.Health) { - builder.AddLabel(x, y, 0x35, "+"); - break; - } - case 2: - { - builder.AddLabel(x, y, 0x21, "+"); - break; - } - } - } - - private static void AddPlusMinus(ref DynamicGumpBuilder builder, int x, int y, int value) - { - switch (value) - { - case 0: - { - builder.AddLabel(x, y, 0x21, "-"); - break; - } - case 1: - { - builder.AddLabel(x, y, 0x35, "-"); - break; - } - case 3: - { - builder.AddLabel(x, y, 0x35, "+"); - break; - } - case 4: - { - builder.AddLabel(x, y, 0x21, "+"); - break; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddLevel(ref DynamicGumpBuilder builder, int x, int y, int value) => - builder.AddLabel(x, y, 0x835, $"{value}"); - - private void AddGrowthIndicator(ref DynamicGumpBuilder builder, int x, int y) - { - if (!_plant.IsGrowable) - { - return; - } - - switch (_plant.PlantSystem.GrowthIndicator) - { - default: - case PlantGrowthIndicator.None: - case PlantGrowthIndicator.InvalidLocation: - { - builder.AddLabel(x, y, 0x21, "!"); - break; - } - case PlantGrowthIndicator.NotHealthy: - { - builder.AddLabel(x, y, 0x21, "-"); - break; - } - case PlantGrowthIndicator.Delay: - { - builder.AddLabel(x, y, 0x35, "-"); - break; - } - case PlantGrowthIndicator.Grown: - { - builder.AddLabel(x, y, 0x3, "+"); - break; - } - case PlantGrowthIndicator.DoubleGrown: - { - builder.AddLabel(x, y, 0x3F, "+"); - break; - } - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; - - if (info.ButtonID == 0 || _plant.Deleted || _plant.PlantStatus >= PlantStatus.DecorativePlant) - { - return; - } - - if ((info.ButtonID >= 6 && info.ButtonID <= 10 || info.ButtonID == 12) && - !from.InRange(_plant.GetWorldLocation(), 3)) - { - from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away. - return; - } - - if (!_plant.IsUsableBy(from)) - { - _plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it. - return; - } - - switch (info.ButtonID) - { - case 1: // Reproduction menu - { - if (_plant.PlantStatus > PlantStatus.BowlOfDirt) - { - ReproductionGump.DisplayTo(from, _plant); - } - else - { - from.SendLocalizedMessage(1061885); // You need to plant a seed in the bowl first. - from.SendGump(this); - } - break; - } - case 2: // Infestation - { - from.NetState.SendDisplayHelpTopic(HelpTopic.InfestationLevel); - from.SendGump(this); - break; - } - case 3: // Fungus - { - from.NetState.SendDisplayHelpTopic(HelpTopic.FungusLevel); - from.SendGump(this); - break; - } - case 4: // Poison - { - from.NetState.SendDisplayHelpTopic(HelpTopic.PoisonLevel); - from.SendGump(this); - break; - } - case 5: // Disease - { - from.NetState.SendDisplayHelpTopic(HelpTopic.DiseaseLevel); - from.SendGump(this); - break; - } - case 6: // Water - { - BaseBeverage bev = null; - - foreach (var beverage in from.Backpack.FindItemsByType()) - { - if (beverage.IsEmpty && beverage.Pourable && beverage.Content == BeverageType.Water) + case PlantHealth.Dying: { - bev = beverage; + AddItem(92, 167, 0x1B9D); + AddItem(161, 167, 0x1B9D); + + AddHtmlLocalized(136, 167, 42, 20, message, 0xFC00); + break; } - } + case PlantHealth.Wilted: + { + AddItem(91, 164, 0x18E6); + AddItem(161, 164, 0x18E6); - if (bev == null) - { - from.Target = new PlantPourTarget(_plant); + AddHtmlLocalized(132, 167, 42, 20, message, 0xC207); - // Target the container you wish to use to water the ~1_val~. - from.SendLocalizedMessage(1060808, $"#{_plant.GetLocalizedPlantStatus()}"); - } - else - { - _plant.Pour(from, bev); - } + break; + } + case PlantHealth.Healthy: + { + AddItem(96, 168, 0xC61); + AddItem(162, 168, 0xC61); - from.SendGump(this); - break; - } - case 7: // Poison potion - { - AddPotion(from, PotionEffect.PoisonGreater, PotionEffect.PoisonDeadly); - break; - } - case 8: // Cure potion - { - AddPotion(from, PotionEffect.CureGreater); - break; - } - case 9: // Heal potion - { - AddPotion(from, PotionEffect.HealGreater); - break; - } - case 10: // Strength potion - { - AddPotion(from, PotionEffect.StrengthGreater); - break; - } - case 11: // Help - { - from.NetState.SendDisplayHelpTopic(HelpTopic.PlantGrowing); - from.SendGump(this); - break; - } - case 12: // Empty the bowl - { - EmptyTheBowlGump.DisplayTo(from, _plant); - break; + AddHtmlLocalized(129, 167, 42, 20, message, 0x8200); + + break; + } + case PlantHealth.Vibrant: + { + AddItem(93, 162, 0x1A99); + AddItem(162, 162, 0x1A99); + + AddHtmlLocalized(129, 167, 42, 20, message, 0x83E0); + + break; + } } + } } - } - private void AddPotion(Mobile from, params PotionEffect[] effects) - { - var item = GetPotion(from, effects); - - if (item != null) + private void AddPlus(int x, int y, int value) { - _plant.Pour(from, item); - } - else - { - if (_plant.ApplyPotion(effects[0], true, out var message)) + switch (value) { - from.SendLocalizedMessage(1061884); // You don't have any strong potions of that type in your pack. + case 1: + { + AddLabel(x, y, 0x35, "+"); + break; + } + case 2: + { + AddLabel(x, y, 0x21, "+"); + break; + } + } + } - from.Target = new PlantPourTarget(_plant); + private void AddPlusMinus(int x, int y, int value) + { + switch (value) + { + case 0: + { + AddLabel(x, y, 0x21, "-"); + break; + } + case 1: + { + AddLabel(x, y, 0x35, "-"); + break; + } + case 3: + { + AddLabel(x, y, 0x35, "+"); + break; + } + case 4: + { + AddLabel(x, y, 0x21, "+"); + break; + } + } + } - // Target the container you wish to use to water the ~1_val~. - from.SendLocalizedMessage(1060808, $"#{_plant.GetLocalizedPlantStatus()}"); + private void AddLevel(int x, int y, int value) + { + AddLabel(x, y, 0x835, value.ToString()); + } + private void AddGrowthIndicator(int x, int y) + { + if (!m_Plant.IsGrowable) + { return; } - _plant.LabelTo(from, message); - } - - from.SendGump(this); - } - - public static Item GetPotion(Mobile from, PotionEffect[] effects) - { - if (from.Backpack == null) - { - return null; - } - - foreach (var item in from.Backpack.FindItems()) - { - if (item is BasePotion potion) + switch (m_Plant.PlantSystem.GrowthIndicator) { - if (Array.IndexOf(effects, potion.PotionEffect) >= 0) + default: + case PlantGrowthIndicator.None: + case PlantGrowthIndicator.InvalidLocation: + { + AddLabel(x, y, 0x21, "!"); + break; + } + case PlantGrowthIndicator.NotHealthy: + { + AddLabel(x, y, 0x21, "-"); + break; + } + case PlantGrowthIndicator.Delay: + { + AddLabel(x, y, 0x35, "-"); + break; + } + case PlantGrowthIndicator.Grown: + { + AddLabel(x, y, 0x3, "+"); + break; + } + case PlantGrowthIndicator.DoubleGrown: + { + AddLabel(x, y, 0x3F, "+"); + break; + } + } + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + var from = sender.Mobile; + + if (info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus >= PlantStatus.DecorativePlant) + { + return; + } + + if ((info.ButtonID >= 6 && info.ButtonID <= 10 || info.ButtonID == 12) && + !from.InRange(m_Plant.GetWorldLocation(), 3)) + { + from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away. + return; + } + + if (!m_Plant.IsUsableBy(from)) + { + m_Plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it. + return; + } + + switch (info.ButtonID) + { + case 1: // Reproduction menu + { + if (m_Plant.PlantStatus > PlantStatus.BowlOfDirt) + { + from.SendGump(new ReproductionGump(m_Plant)); + } + else + { + from.SendLocalizedMessage(1061885); // You need to plant a seed in the bowl first. + + from.SendGump(new MainPlantGump(m_Plant)); + } + + break; + } + case 2: // Infestation + { + from.NetState.SendDisplayHelpTopic(HelpTopic.InfestationLevel); + + from.SendGump(new MainPlantGump(m_Plant)); + + break; + } + case 3: // Fungus + { + from.NetState.SendDisplayHelpTopic(HelpTopic.FungusLevel); + + from.SendGump(new MainPlantGump(m_Plant)); + + break; + } + case 4: // Poison + { + from.NetState.SendDisplayHelpTopic(HelpTopic.PoisonLevel); + + from.SendGump(new MainPlantGump(m_Plant)); + + break; + } + case 5: // Disease + { + from.NetState.SendDisplayHelpTopic(HelpTopic.DiseaseLevel); + + from.SendGump(new MainPlantGump(m_Plant)); + + break; + } + case 6: // Water + { + BaseBeverage bev = null; + + foreach (var beverage in from.Backpack.FindItemsByType()) + { + if (beverage.IsEmpty && beverage.Pourable && beverage.Content == BeverageType.Water) + { + bev = beverage; + break; + } + } + + if (bev == null) + { + from.Target = new PlantPourTarget(m_Plant); + + // Target the container you wish to use to water the ~1_val~. + from.SendLocalizedMessage(1060808, $"#{m_Plant.GetLocalizedPlantStatus()}"); + } + else + { + m_Plant.Pour(from, bev); + } + + from.SendGump(new MainPlantGump(m_Plant)); + + break; + } + case 7: // Poison potion + { + AddPotion(from, PotionEffect.PoisonGreater, PotionEffect.PoisonDeadly); + + break; + } + case 8: // Cure potion + { + AddPotion(from, PotionEffect.CureGreater); + + break; + } + case 9: // Heal potion + { + AddPotion(from, PotionEffect.HealGreater); + + break; + } + case 10: // Strength potion + { + AddPotion(from, PotionEffect.StrengthGreater); + + break; + } + case 11: // Help + { + from.NetState.SendDisplayHelpTopic(HelpTopic.PlantGrowing); + + from.SendGump(new MainPlantGump(m_Plant)); + + break; + } + case 12: // Empty the bowl + { + from.SendGump(new EmptyTheBowlGump(m_Plant)); + + break; + } + } + } + + private void AddPotion(Mobile from, params PotionEffect[] effects) + { + var item = GetPotion(from, effects); + + if (item != null) + { + m_Plant.Pour(from, item); + } + else + { + if (m_Plant.ApplyPotion(effects[0], true, out var message)) { - return potion; + from.SendLocalizedMessage(1061884); // You don't have any strong potions of that type in your pack. + + from.Target = new PlantPourTarget(m_Plant); + + // Target the container you wish to use to water the ~1_val~. + from.SendLocalizedMessage(1060808, $"#{m_Plant.GetLocalizedPlantStatus()}"); + + return; + } + + m_Plant.LabelTo(from, message); + } + + from.SendGump(new MainPlantGump(m_Plant)); + } + + public static Item GetPotion(Mobile from, PotionEffect[] effects) + { + if (from.Backpack == null) + { + return null; + } + + foreach (var item in from.Backpack.FindItems()) + { + if (item is BasePotion potion) + { + if (Array.IndexOf(effects, potion.PotionEffect) >= 0) + { + return potion; + } + } + else if (item is PotionKeg keg && keg.Held > 0 && Array.IndexOf(effects, keg.Type) >= 0) + { + return keg; } } - else if (item is PotionKeg keg && keg.Held > 0 && Array.IndexOf(effects, keg.Type) >= 0) - { - return keg; - } - } - return null; + return null; + } } } diff --git a/Projects/UOContent/Engines/Plants/MiscItems/FertileDirt.cs b/Projects/UOContent/Engines/Plants/MiscItems/FertileDirt.cs index 32a4d0cb1..311eae3f6 100644 --- a/Projects/UOContent/Engines/Plants/MiscItems/FertileDirt.cs +++ b/Projects/UOContent/Engines/Plants/MiscItems/FertileDirt.cs @@ -9,8 +9,7 @@ public partial class FertileDirt : Item public FertileDirt(int amount = 1) : base(0xF81) { Stackable = true; + Weight = 1.0; Amount = amount; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Engines/Plants/MiscItems/GreenThorns.cs b/Projects/UOContent/Engines/Plants/MiscItems/GreenThorns.cs index efae770d7..e3db73227 100644 --- a/Projects/UOContent/Engines/Plants/MiscItems/GreenThorns.cs +++ b/Projects/UOContent/Engines/Plants/MiscItems/GreenThorns.cs @@ -12,12 +12,11 @@ public partial class GreenThorns : Item public GreenThorns(int amount = 1) : base(0xF42) { Stackable = true; + Weight = 1.0; Hue = 0x42; Amount = amount; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1060837; // green thorns public override void OnDoubleClick(Mobile from) @@ -510,7 +509,7 @@ public class FurrowsGreenThornsEffect : GreenThornsEffect // * A magical bunny leaps out of its hole, disturbed by the thorn's effect! * dummy.PublicOverheadMessage(MessageType.Regular, 0x3B2, 1114428); - var spawn = new VorpalBunny(); + BaseCreature spawn = new VorpalBunny(); if (!SpawnCreature(spawn)) { spawn.Delete(); @@ -565,7 +564,7 @@ public class SwampGreenThornsEffect : GreenThornsEffect dummy.PublicOverheadMessage(MessageType.Regular, 0x3B2, 1114429); Effects.PlaySound(Location, Map, 0x2B0); - var spawn = new WhippingVine(); + BaseCreature spawn = new WhippingVine(); if (!SpawnCreature(spawn)) { spawn.Delete(); @@ -619,7 +618,7 @@ public class SnowGreenThornsEffect : GreenThornsEffect // * Slithering ice serpents rise to the surface to investigate the disturbance! * dummy.PublicOverheadMessage(MessageType.Regular, 0x3B2, 1114430); - var spawn = new GiantIceWorm(); + BaseCreature spawn = new GiantIceWorm(); if (!SpawnCreature(spawn)) { spawn.Delete(); @@ -627,7 +626,7 @@ public class SnowGreenThornsEffect : GreenThornsEffect for (var i = 0; i < 3; i++) { - var snake = new IceSnake(); + BaseCreature snake = new IceSnake(); if (!SpawnCreature(snake)) { snake.Delete(); @@ -703,8 +702,6 @@ public partial class GreenThornsSHTeleporter : Item public override string DefaultName => "a hole"; - public override bool SkipSerialization => true; - public static void Create(Point3D location, Map map) { var tele = new GreenThornsSHTeleporter(); @@ -728,6 +725,12 @@ public partial class GreenThornsSHTeleporter : Item } } + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } + private class InternalTimer : Timer { private readonly GreenThornsSHTeleporter _teleporter; diff --git a/Projects/UOContent/Engines/Plants/PlantBowl.cs b/Projects/UOContent/Engines/Plants/PlantBowl.cs index 5028b6db8..65489d385 100644 --- a/Projects/UOContent/Engines/Plants/PlantBowl.cs +++ b/Projects/UOContent/Engines/Plants/PlantBowl.cs @@ -56,11 +56,7 @@ public partial class PlantBowl : Item }; [Constructible] - public PlantBowl() : base(0x15FD) - { - } - - public override double DefaultWeight => 1.0; + public PlantBowl() : base(0x15FD) => Weight = 1.0; public override int LabelNumber => 1060834; // a plant bowl diff --git a/Projects/UOContent/Engines/Plants/PlantItem.cs b/Projects/UOContent/Engines/Plants/PlantItem.cs index 5f66b1a7a..3bc098ffe 100644 --- a/Projects/UOContent/Engines/Plants/PlantItem.cs +++ b/Projects/UOContent/Engines/Plants/PlantItem.cs @@ -37,18 +37,18 @@ public partial class PlantItem : Item, ISecurable [SerializedIgnoreDupe] [SerializableField(0)] - [SaveFlag(nameof(ShouldSerializeSecureLevel))] [SerializedCommandProperty(AccessLevel.GameMaster)] private SecureLevel _level; + [SerializableFieldSaveFlag(0)] private bool ShouldSerializeSecureLevel() => (int)_level != 0; [SerializedIgnoreDupe] [SerializableField(5, setter: "private")] - [SaveFlag(nameof(ShouldSerializePlantSystem))] private PlantSystem _plantSystem; - private bool ShouldSerializePlantSystem() => _plantStatus < PlantStatus.DecorativePlant; + [SerializableFieldSaveFlag(5)] + private bool ShouldSerializePlantSystem() => _plantStatus != PlantStatus.DecorativePlant; // For clients older than 7.0.12.0 private ObjectPropertyList _oldClientPropertyList; @@ -56,6 +56,8 @@ public partial class PlantItem : Item, ISecurable [Constructible] public PlantItem(bool fertileDirt = false) : base(0x1602) { + Weight = 1.0; + _plantStatus = PlantStatus.BowlOfDirt; _plantSystem = new PlantSystem(this) { @@ -67,8 +69,6 @@ public partial class PlantItem : Item, ISecurable Plants.Add(this); } - public override double DefaultWeight => 1.0; - public ObjectPropertyList OldClientPropertyList { get @@ -82,7 +82,6 @@ public partial class PlantItem : Item, ISecurable [CommandProperty(AccessLevel.GameMaster)] [SerializableProperty(1)] - [SaveFlag(nameof(ShouldSerializePlantStatus))] public PlantStatus PlantStatus { get => _plantStatus; @@ -121,38 +120,53 @@ public partial class PlantItem : Item, ISecurable } } + [SerializableFieldSaveFlag(1)] private bool ShouldSerializePlantStatus() => _plantStatus != PlantStatus.BowlOfDirt; - [SerializableField(2, fieldChanged: nameof(OnPlantTypeChanged))] - [SaveFlag(nameof(ShouldSerializePlantType))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private PlantType _plantType; - - private void OnPlantTypeChanged(PlantType oldValue, PlantType newValue) + [SerializableProperty(2)] + [CommandProperty(AccessLevel.GameMaster)] + public PlantType PlantType { - Update(); + get => _plantType; + set + { + _plantType = value; + Update(); + } } + [SerializableFieldSaveFlag(2)] private bool ShouldSerializePlantType() => (int)_plantType != 0; - [SerializableField(3, fieldChanged: nameof(OnPlantHueChanged))] - [SaveFlag(nameof(ShouldSerializePlantHue))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private PlantHue _plantHue; - - private void OnPlantHueChanged(PlantHue oldValue, PlantHue newValue) + [SerializableProperty(3)] + [CommandProperty(AccessLevel.GameMaster)] + public PlantHue PlantHue { - Update(); + get => _plantHue; + set + { + _plantHue = value; + Update(); + } } + [SerializableFieldSaveFlag(3)] private bool ShouldSerializePlantHue() => _plantHue != PlantHue.None; - [SerializableField(4)] - [SaveFlag(nameof(ShouldSerializeShowType))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private bool _showType; + [SerializableProperty(4)] + [CommandProperty(AccessLevel.GameMaster)] + public bool ShowType + { + get => _showType; + set + { + _showType = value; + InvalidateProperties(); + this.MarkDirty(); + } + } + [SerializableFieldSaveFlag(4)] private bool ShouldSerializeShowType() => _showType; [CommandProperty(AccessLevel.GameMaster)] @@ -453,15 +467,12 @@ public partial class PlantItem : Item, ISecurable return; } - if (!IsChildOf(from)) - { - var loc = GetWorldLocation(); + var loc = GetWorldLocation(); - if (!from.InLOS(loc) || !from.InRange(loc, 2)) - { - from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 1019045); // I can't reach that - return; - } + if (!from.InLOS(loc) || !from.InRange(loc, 2)) + { + from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 1019045); // I can't reach that. + return; } if (!IsUsableBy(from)) @@ -470,7 +481,7 @@ public partial class PlantItem : Item, ISecurable return; } - MainPlantGump.DisplayTo(from, this); + from.SendGump(new MainPlantGump(this)); } public void PlantSeed(Mobile from, Seed seed) diff --git a/Projects/UOContent/Engines/Plants/PlantPourTarget.cs b/Projects/UOContent/Engines/Plants/PlantPourTarget.cs index 0e59d79a1..6be83a241 100644 --- a/Projects/UOContent/Engines/Plants/PlantPourTarget.cs +++ b/Projects/UOContent/Engines/Plants/PlantPourTarget.cs @@ -1,3 +1,4 @@ +using Server.Gumps; using Server.Targeting; namespace Server.Engines.Plants @@ -21,7 +22,7 @@ namespace Server.Engines.Plants if (!m_Plant.Deleted && m_Plant.PlantStatus < PlantStatus.DecorativePlant && from.InRange(m_Plant.GetWorldLocation(), 3) && m_Plant.IsUsableBy(from)) { - MainPlantGump.DisplayTo(from, m_Plant); + from.SendGump(new MainPlantGump(m_Plant), true); } } } diff --git a/Projects/UOContent/Engines/Plants/PlantSystem.cs b/Projects/UOContent/Engines/Plants/PlantSystem.cs index 4b202ef63..61d017c0f 100644 --- a/Projects/UOContent/Engines/Plants/PlantSystem.cs +++ b/Projects/UOContent/Engines/Plants/PlantSystem.cs @@ -1,6 +1,7 @@ using System; using ModernUO.CodeGeneratedEvents; using ModernUO.Serialization; +using Server.Items; using Server.Misc; using Server.Mobiles; @@ -33,24 +34,24 @@ namespace Server.Engines.Plants private PlantItem _plant; [SerializableField(0)] - [SaveFlag(nameof(ShouldSerializeFertileDirt))] private bool _fertileDirt; + [SerializableFieldSaveFlag(0)] private bool ShouldSerializeFertileDirt() => _fertileDirt; [SerializableField(1)] private DateTime _nextGrowth; [SerializableField(2, setter: "private")] - [SaveFlag(nameof(ShouldSerializeGrowthIndicator))] private PlantGrowthIndicator _growthIndicator; + [SerializableFieldSaveFlag(2)] private bool ShouldSerializeGrowthIndicator() => _growthIndicator != PlantGrowthIndicator.None; [SerializableField(13)] - [SaveFlag(nameof(ShouldSerializePollinated))] private bool _pollinated; + [SerializableFieldSaveFlag(13)] private bool ShouldSerializePollinated() => _pollinated; public PlantSystem(PlantItem plant) @@ -97,42 +98,45 @@ namespace Server.Engines.Plants public bool IsFullWater => _water >= 4; - [SerializableField(3, fieldChanged: nameof(OnWaterChanged), allowFieldChange: nameof(AllowWaterChange))] - [SaveFlag(nameof(ShouldSerializeWater))] - private int _water; - - private bool AllowWaterChange(ref int value) + [SerializableProperty(3)] + public int Water { - value = Math.Clamp(value, 0, 4); - return true; - } - - private void OnWaterChanged(int oldValue, int newValue) - { - Plant.InvalidateProperties(); + get => _water; + set + { + _water = Math.Clamp(value, 0, 4); + Plant.InvalidateProperties(); + MarkDirty(); + } } + [SerializableFieldSaveFlag(3)] private bool ShouldSerializeWater() => _water != 0; - [SerializableField(4, fieldChanged: nameof(OnHitsChanged), allowFieldChange: nameof(AllowHitsChange))] - [SaveFlag(nameof(ShouldSerializeHits))] - private int _hits; - - private bool AllowHitsChange(ref int value) + [SerializableProperty(4)] + public int Hits { - value = Math.Clamp(value, 0, MaxHits); - return true; - } - - private void OnHitsChanged(int oldValue, int newValue) - { - if (_hits == 0) + get => _hits; + set { - Plant.Die(); + if (_hits == value) + { + return; + } + + _hits = Math.Clamp(value, 0, MaxHits); + + if (_hits == 0) + { + Plant.Die(); + } + + Plant.InvalidateProperties(); + MarkDirty(); } - Plant.InvalidateProperties(); } + [SerializableFieldSaveFlag(4)] private bool ShouldSerializeHits() => _hits != 0; public int MaxHits => 10 + (int)Plant.PlantStatus * 2; @@ -146,108 +150,124 @@ namespace Server.Engines.Plants _ => PlantHealth.Vibrant }; - [SerializableField(5, allowFieldChange: nameof(AllowInfestationChange))] - [SaveFlag(nameof(ShouldSerializeInfestation))] - private int _infestation; - - private bool AllowInfestationChange(ref int value) + [SerializableProperty(5)] + public int Infestation { - value = Math.Clamp(value, 0, 2); - return true; + get => _infestation; + set + { + _infestation = Math.Clamp(value, 0, 2); + MarkDirty(); + } } + [SerializableFieldSaveFlag(5)] private bool ShouldSerializeInfestation() => _infestation != 0; - [SerializableField(6, allowFieldChange: nameof(AllowFungusChange))] - [SaveFlag(nameof(ShouldSerializeFungus))] - private int _fungus; - - private bool AllowFungusChange(ref int value) + [SerializableProperty(6)] + public int Fungus { - value = Math.Clamp(value, 0, 2); - return true; + get => _fungus; + set + { + _fungus = Math.Clamp(value, 0, 2); + MarkDirty(); + } } + [SerializableFieldSaveFlag(6)] private bool ShouldSerializeFungus() => _fungus != 0; - [SerializableField(7, allowFieldChange: nameof(AllowPoisonChange))] - [SaveFlag(nameof(ShouldSerializePoison))] - private int _poison; - - private bool AllowPoisonChange(ref int value) + [SerializableProperty(7)] + public int Poison { - value = Math.Clamp(value, 0, 2); - return true; + get => _poison; + set + { + _poison = Math.Clamp(value, 0, 2); + MarkDirty(); + } } + [SerializableFieldSaveFlag(7)] private bool ShouldSerializePoison() => _poison != 0; - [SerializableField(8, allowFieldChange: nameof(AllowDiseaseChange))] - [SaveFlag(nameof(ShouldSerializeDisease))] - private int _disease; - - private bool AllowDiseaseChange(ref int value) + [SerializableProperty(8)] + public int Disease { - value = Math.Clamp(value, 0, 2); - return true; + get => _disease; + set + { + _disease = Math.Clamp(value, 0, 2); + MarkDirty(); + } } + [SerializableFieldSaveFlag(8)] private bool ShouldSerializeDisease() => _disease != 0; public bool IsFullPoisonPotion => _poisonPotion >= 2; - [SerializableField(9, allowFieldChange: nameof(AllowPoisonPotionChange))] - [SaveFlag(nameof(ShouldSerializePoisonPotion))] - private int _poisonPotion; - - private bool AllowPoisonPotionChange(ref int value) + [SerializableProperty(9)] + public int PoisonPotion { - value = Math.Clamp(value, 0, 2); - return true; + get => _poisonPotion; + set + { + _poisonPotion = Math.Clamp(value, 0, 2); + MarkDirty(); + } } + [SerializableFieldSaveFlag(9)] private bool ShouldSerializePoisonPotion() => _poisonPotion != 0; public bool IsFullCurePotion => _curePotion >= 2; - [SerializableField(10, allowFieldChange: nameof(AllowCurePotionChange))] - [SaveFlag(nameof(ShouldSerializeCurePotion))] - private int _curePotion; - - private bool AllowCurePotionChange(ref int value) + [SerializableProperty(10)] + public int CurePotion { - value = Math.Clamp(value, 0, 2); - return true; + get => _curePotion; + set + { + _curePotion = Math.Clamp(value, 0, 2); + MarkDirty(); + } } + [SerializableFieldSaveFlag(10)] private bool ShouldSerializeCurePotion() => _curePotion != 0; public bool IsFullHealPotion => _healPotion >= 2; - [SerializableField(11, allowFieldChange: nameof(AllowHealPotionChange))] - [SaveFlag(nameof(ShouldSerializeHealPotion))] - private int _healPotion; - - private bool AllowHealPotionChange(ref int value) + [SerializableProperty(11)] + public int HealPotion { - value = Math.Clamp(value, 0, 2); - return true; + get => _healPotion; + set + { + _healPotion = Math.Clamp(value, 0, 2); + MarkDirty(); + } } + [SerializableFieldSaveFlag(11)] private bool ShouldSerializeHealPotion() => _healPotion != 0; public bool IsFullStrengthPotion => _strengthPotion >= 2; - [SerializableField(12, allowFieldChange: nameof(AllowStrengthPotionChange))] - [SaveFlag(nameof(ShouldSerializeStrengthPotion))] - private int _strengthPotion; - - private bool AllowStrengthPotionChange(ref int value) + [SerializableProperty(12)] + public int StrengthPotion { - value = Math.Clamp(value, 0, 2); - return true; + get => _strengthPotion; + set + { + _strengthPotion = Math.Clamp(value, 0, 2); + MarkDirty(); + } } + [SerializableFieldSaveFlag(12)] private bool ShouldSerializeStrengthPotion() => _strengthPotion != 0; public bool HasMaladies => Infestation > 0 || Fungus > 0 || Poison > 0 || Disease > 0 || Water != 2; @@ -255,7 +275,6 @@ namespace Server.Engines.Plants public bool PollenProducing => Plant.IsCrossable && Plant.PlantStatus >= PlantStatus.FullGrownPlant; [SerializableProperty(14)] - [SaveFlag(nameof(ShouldSerializeSeedType))] public PlantType SeedType { get => Pollinated ? _seedType : Plant.PlantType; @@ -266,10 +285,10 @@ namespace Server.Engines.Plants } } + [SerializableFieldSaveFlag(14)] private bool ShouldSerializeSeedType() => _pollinated; [SerializableProperty(15)] - [SaveFlag(nameof(ShouldSerializeSeedHue))] public PlantHue SeedHue { get => Pollinated ? _seedHue : Plant.PlantHue; @@ -280,59 +299,48 @@ namespace Server.Engines.Plants } } + [SerializableFieldSaveFlag(15)] private bool ShouldSerializeSeedHue() => _pollinated; - [SerializableField(16, allowFieldChange: nameof(AllowAvailableSeedsChange))] - [SaveFlag(nameof(ShouldSerializeAvailableSeeds))] - private int _availableSeeds; - - private bool AllowAvailableSeedsChange(ref int value) + [SerializableProperty(16)] + public int AvailableSeeds { - value = Math.Max(value, 0); - return true; + get => _availableSeeds; + set => _availableSeeds = Math.Max(value, 0); } + [SerializableFieldSaveFlag(16)] private bool ShouldSerializeAvailableSeeds() => _availableSeeds != 0; - [SerializableField(17, allowFieldChange: nameof(AllowLeftSeedsChange))] - [SaveFlag(nameof(ShouldSerializeLeftSeeds), nameof(LeftSeedsDefaultValue))] - private int _leftSeeds; - - private bool AllowLeftSeedsChange(ref int value) + [SerializableProperty(17)] + public int LeftSeeds { - value = Math.Max(value, 0); - return true; + get => _leftSeeds; + set => _leftSeeds = Math.Max(value, 0); } - private bool ShouldSerializeLeftSeeds() => _leftSeeds != 8; + [SerializableFieldSaveFlag(17)] + private bool ShouldSerializeLeftSeeds() => _leftSeeds != 0; - private int LeftSeedsDefaultValue() => 8; - - [SerializableField(18, allowFieldChange: nameof(AllowAvailableResourcesChange))] - [SaveFlag(nameof(ShouldSerializeAvailableResources))] - private int _availableResources; - - private bool AllowAvailableResourcesChange(ref int value) + [SerializableProperty(18)] + public int AvailableResources { - value = Math.Max(value, 0); - return true; + get => _availableResources; + set => _availableResources = Math.Max(value, 0); } + [SerializableFieldSaveFlag(18)] private bool ShouldSerializeAvailableResources() => _availableResources != 0; - [SerializableField(19, allowFieldChange: nameof(AllowLeftResourcesChange))] - [SaveFlag(nameof(ShouldSerializeLeftResources), nameof(LeftResourcesDefaultValue))] - private int _leftResources; - - private bool AllowLeftResourcesChange(ref int value) + [SerializableProperty(19)] + public int LeftResources { - value = Math.Max(value, 0); - return true; + get => _leftResources; + set => _leftResources = Math.Max(value, 0); } - private bool ShouldSerializeLeftResources() => _leftResources != 8; - - private int LeftResourcesDefaultValue() => 8; + [SerializableFieldSaveFlag(19)] + private bool ShouldSerializeLeftResources() => _leftResources != 0; public void Reset(bool potions) { @@ -429,7 +437,7 @@ namespace Server.Engines.Plants [OnEvent(nameof(PlayerMobile.PlayerLoginEvent))] public static void OnLogin(PlayerMobile from) { - var cont = from.Backpack; + Container cont = from.Backpack; if (cont != null) { foreach (var plant in cont.FindItemsByType()) diff --git a/Projects/UOContent/Engines/Plants/PollinateTarget.cs b/Projects/UOContent/Engines/Plants/PollinateTarget.cs index 5b846cbcd..720060afc 100644 --- a/Projects/UOContent/Engines/Plants/PollinateTarget.cs +++ b/Projects/UOContent/Engines/Plants/PollinateTarget.cs @@ -1,3 +1,4 @@ +using Server.Gumps; using Server.Targeting; namespace Server.Engines.Plants @@ -83,7 +84,7 @@ namespace Server.Engines.Plants m_Plant.PlantStatus != PlantStatus.BowlOfDirt && from.InRange(m_Plant.GetWorldLocation(), 3) && m_Plant.IsUsableBy(from)) { - ReproductionGump.DisplayTo(from, m_Plant); + from.SendGump(new ReproductionGump(m_Plant)); } } } diff --git a/Projects/UOContent/Engines/Plants/ReproductionGump.cs b/Projects/UOContent/Engines/Plants/ReproductionGump.cs index 0c31d74a0..4feba68ac 100644 --- a/Projects/UOContent/Engines/Plants/ReproductionGump.cs +++ b/Projects/UOContent/Engines/Plants/ReproductionGump.cs @@ -2,288 +2,293 @@ using Server.Engines.Help; using Server.Gumps; using Server.Network; -namespace Server.Engines.Plants; - -public class ReproductionGump : DynamicGump +namespace Server.Engines.Plants { - private readonly PlantItem _plant; - - public override bool Singleton => true; - - private ReproductionGump(PlantItem plant) : base(20, 20) + public class ReproductionGump : Gump { - _plant = plant; - } + private readonly PlantItem m_Plant; - public static void DisplayTo(Mobile from, PlantItem plant) - { - if (from?.NetState == null || plant == null || plant.Deleted) + public ReproductionGump(PlantItem plant) : base(20, 20) { - return; + m_Plant = plant; + + DrawBackground(); + + AddButton(70, 67, 0xD4, 0xD4, 1); // Main menu + AddItem(57, 65, 0x1600); + + AddLabel(108, 67, 0x835, "Reproduction"); + + if (m_Plant.PlantStatus == PlantStatus.Stage9) + { + AddButton(212, 67, 0xD4, 0xD4, 2); // Set to decorative + AddItem(202, 68, 0xC61); + AddLabel(216, 66, 0x21, "/"); + } + + AddButton(80, 116, 0xD4, 0xD4, 3); // Pollination + AddItem(66, 117, 0x1AA2); + AddPollinationState(106, 116); + + AddButton(128, 116, 0xD4, 0xD4, 4); // Resources + AddItem(113, 120, 0x1021); + AddResourcesState(149, 116); + + AddButton(177, 116, 0xD4, 0xD4, 5); // Seeds + AddItem(160, 121, 0xDCF); + AddSeedsState(199, 116); + + AddButton(70, 163, 0xD2, 0xD2, 6); // Gather pollen + AddItem(56, 164, 0x1AA2); + + AddButton(138, 163, 0xD2, 0xD2, 7); // Gather resources + AddItem(123, 167, 0x1021); + + AddButton(212, 163, 0xD2, 0xD2, 8); // Gather seeds + AddItem(195, 168, 0xDCF); } - from.SendGump(new ReproductionGump(plant)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(50, 50, 200, 150, 0xE10); - - builder.AddImage(60, 90, 0xE17); - builder.AddImage(120, 90, 0xE17); - - builder.AddImage(60, 145, 0xE17); - builder.AddImage(120, 145, 0xE17); - - builder.AddItem(45, 45, 0xCEF); - builder.AddItem(45, 118, 0xCF0); - - builder.AddItem(211, 45, 0xCEB); - builder.AddItem(211, 118, 0xCEC); - - builder.AddButton(70, 67, 0xD4, 0xD4, 1); // Main menu - builder.AddItem(57, 65, 0x1600); - - builder.AddLabel(108, 67, 0x835, "Reproduction"); - - if (_plant.PlantStatus == PlantStatus.Stage9) + private void DrawBackground() { - builder.AddButton(212, 67, 0xD4, 0xD4, 2); // Set to decorative - builder.AddItem(202, 68, 0xC61); - builder.AddLabel(216, 66, 0x21, "/"); + AddBackground(50, 50, 200, 150, 0xE10); + + AddImage(60, 90, 0xE17); + AddImage(120, 90, 0xE17); + + AddImage(60, 145, 0xE17); + AddImage(120, 145, 0xE17); + + AddItem(45, 45, 0xCEF); + AddItem(45, 118, 0xCF0); + + AddItem(211, 45, 0xCEB); + AddItem(211, 118, 0xCEC); } - builder.AddButton(80, 116, 0xD4, 0xD4, 3); // Pollination - builder.AddItem(66, 117, 0x1AA2); - AddPollinationState(ref builder, 106, 116); - - builder.AddButton(128, 116, 0xD4, 0xD4, 4); // Resources - builder.AddItem(113, 120, 0x1021); - AddResourcesState(ref builder, 149, 116); - - builder.AddButton(177, 116, 0xD4, 0xD4, 5); // Seeds - builder.AddItem(160, 121, 0xDCF); - AddSeedsState(ref builder, 199, 116); - - builder.AddButton(70, 163, 0xD2, 0xD2, 6); // Gather pollen - builder.AddItem(56, 164, 0x1AA2); - - builder.AddButton(138, 163, 0xD2, 0xD2, 7); // Gather resources - builder.AddItem(123, 167, 0x1021); - - builder.AddButton(212, 163, 0xD2, 0xD2, 8); // Gather seeds - builder.AddItem(195, 168, 0xDCF); - } - - private void AddPollinationState(ref DynamicGumpBuilder builder, int x, int y) - { - var system = _plant.PlantSystem; - - if (!system.PollenProducing) + private void AddPollinationState(int x, int y) { - builder.AddLabel(x, y, 0x35, "-"); - } - else if (!system.Pollinated) - { - builder.AddLabel(x, y, 0x21, "!"); - } - else - { - builder.AddLabel(x, y, 0x3F, "+"); - } - } + var system = m_Plant.PlantSystem; - private void AddResourcesState(ref DynamicGumpBuilder builder, int x, int y) - { - var resInfo = PlantResourceInfo.GetInfo(_plant.PlantType, _plant.PlantHue); - - var system = _plant.PlantSystem; - var totalResources = system.AvailableResources + system.LeftResources; - - if (resInfo == null || totalResources == 0) - { - builder.AddLabel(x + 5, y, 0x21, "X"); - } - else - { - builder.AddLabel( - x, - y, - PlantHueInfo.GetInfo(_plant.PlantHue).GumpHue, - $"{system.AvailableResources}/{totalResources}" - ); - } - } - - private void AddSeedsState(ref DynamicGumpBuilder builder, int x, int y) - { - var system = _plant.PlantSystem; - var totalSeeds = system.AvailableSeeds + system.LeftSeeds; - - if (!_plant.Reproduces || totalSeeds == 0) - { - builder.AddLabel(x + 5, y, 0x21, "X"); - } - else - { - builder.AddLabel( - x, - y, - PlantHueInfo.GetInfo(system.SeedHue).GumpHue, - $"{system.AvailableSeeds}/{totalSeeds}" - ); - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; - - if (info.ButtonID == 0 || _plant.Deleted || _plant.PlantStatus is >= PlantStatus.DecorativePlant or PlantStatus.BowlOfDirt) - { - return; + if (!system.PollenProducing) + { + AddLabel(x, y, 0x35, "-"); + } + else if (!system.Pollinated) + { + AddLabel(x, y, 0x21, "!"); + } + else + { + AddLabel(x, y, 0x3F, "+"); + } } - if (info.ButtonID >= 6 && info.ButtonID <= 8 && !from.InRange(_plant.GetWorldLocation(), 3)) + private void AddResourcesState(int x, int y) { - from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away. - return; + var resInfo = PlantResourceInfo.GetInfo(m_Plant.PlantType, m_Plant.PlantHue); + + var system = m_Plant.PlantSystem; + var totalResources = system.AvailableResources + system.LeftResources; + + if (resInfo == null || totalResources == 0) + { + AddLabel(x + 5, y, 0x21, "X"); + } + else + { + AddLabel( + x, + y, + PlantHueInfo.GetInfo(m_Plant.PlantHue).GumpHue, + $"{system.AvailableResources}/{totalResources}" + ); + } } - if (!_plant.IsUsableBy(from)) + private void AddSeedsState(int x, int y) { - _plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it. - return; + var system = m_Plant.PlantSystem; + var totalSeeds = system.AvailableSeeds + system.LeftSeeds; + + if (!m_Plant.Reproduces || totalSeeds == 0) + { + AddLabel(x + 5, y, 0x21, "X"); + } + else + { + AddLabel( + x, + y, + PlantHueInfo.GetInfo(system.SeedHue).GumpHue, + $"{system.AvailableSeeds}/{totalSeeds}" + ); + } } - switch (info.ButtonID) + public override void OnResponse(NetState sender, in RelayInfo info) { - case 1: // Main menu - { - MainPlantGump.DisplayTo(from, _plant); - return; - } - case 2: // Set to decorative - { - if (_plant.PlantStatus == PlantStatus.Stage9) + var from = sender.Mobile; + + if (info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus is >= PlantStatus.DecorativePlant or PlantStatus.BowlOfDirt) + { + return; + } + + if (info.ButtonID >= 6 && info.ButtonID <= 8 && !from.InRange(m_Plant.GetWorldLocation(), 3)) + { + from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away. + return; + } + + if (!m_Plant.IsUsableBy(from)) + { + m_Plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it. + return; + } + + switch (info.ButtonID) + { + case 1: // Main menu { - SetToDecorativeGump.DisplayTo(from, _plant); + from.SendGump(new MainPlantGump(m_Plant)); + + break; } - return; - } - case 3: // Pollination - { - from.NetState.SendDisplayHelpTopic(HelpTopic.PollinationState); - break; - } - case 4: // Resources - { - from.NetState.SendDisplayHelpTopic(HelpTopic.ResourceProduction); - break; - } - case 5: // Seeds - { - from.NetState.SendDisplayHelpTopic(HelpTopic.SeedProduction); - break; - } - case 6: // Gather pollen - { - if (!_plant.IsCrossable) + case 2: // Set to decorative { - _plant.LabelTo(from, 1053050); // You cannot gather pollen from a mutated plant! - } - else if (!_plant.PlantSystem.PollenProducing) - { - // You cannot gather pollen from a plant in this stage of development! - _plant.LabelTo(from, 1053051); - } - else if (_plant.PlantSystem.Health < PlantHealth.Healthy) - { - _plant.LabelTo(from, 1053052); // You cannot gather pollen from an unhealthy plant! - } - else - { - from.Target = new PollinateTarget(_plant); - from.SendLocalizedMessage(1053054); // Target the plant you wish to cross-pollinate to. - return; - } - - break; - } - case 7: // Gather resources - { - var resInfo = PlantResourceInfo.GetInfo(_plant.PlantType, _plant.PlantHue); - var system = _plant.PlantSystem; - - if (resInfo == null) - { - if (_plant.IsCrossable) + if (m_Plant.PlantStatus == PlantStatus.Stage9) { - _plant.LabelTo(from, 1053056); // This plant has no resources to gather! + from.SendGump(new SetToDecorativeGump(m_Plant)); + } + + break; + } + case 3: // Pollination + { + from.NetState.SendDisplayHelpTopic(HelpTopic.PollinationState); + + from.SendGump(new ReproductionGump(m_Plant)); + + break; + } + case 4: // Resources + { + from.NetState.SendDisplayHelpTopic(HelpTopic.ResourceProduction); + + from.SendGump(new ReproductionGump(m_Plant)); + + break; + } + case 5: // Seeds + { + from.NetState.SendDisplayHelpTopic(HelpTopic.SeedProduction); + + from.SendGump(new ReproductionGump(m_Plant)); + + break; + } + case 6: // Gather pollen + { + if (!m_Plant.IsCrossable) + { + m_Plant.LabelTo(from, 1053050); // You cannot gather pollen from a mutated plant! + } + else if (!m_Plant.PlantSystem.PollenProducing) + { + // You cannot gather pollen from a plant in this stage of development! + m_Plant.LabelTo(from, 1053051); + } + else if (m_Plant.PlantSystem.Health < PlantHealth.Healthy) + { + m_Plant.LabelTo(from, 1053052); // You cannot gather pollen from an unhealthy plant! } else { - _plant.LabelTo(from, 1053055); // Mutated plants do not produce resources! - } - } - else if (system.AvailableResources == 0) - { - _plant.LabelTo(from, 1053056); // This plant has no resources to gather! - } - else - { - var resource = resInfo.CreateResource(); + from.Target = new PollinateTarget(m_Plant); + from.SendLocalizedMessage(1053054); // Target the plant you wish to cross-pollinate to. - if (from.PlaceInBackpack(resource)) + break; + } + + from.SendGump(new ReproductionGump(m_Plant)); + + break; + } + case 7: // Gather resources + { + var resInfo = PlantResourceInfo.GetInfo(m_Plant.PlantType, m_Plant.PlantHue); + var system = m_Plant.PlantSystem; + + if (resInfo == null) { - system.AvailableResources--; - _plant.LabelTo(from, 1053059); // You gather resources from the plant. + if (m_Plant.IsCrossable) + { + m_Plant.LabelTo(from, 1053056); // This plant has no resources to gather! + } + else + { + m_Plant.LabelTo(from, 1053055); // Mutated plants do not produce resources! + } + } + else if (system.AvailableResources == 0) + { + m_Plant.LabelTo(from, 1053056); // This plant has no resources to gather! } else { - resource.Delete(); - // You attempt to gather as many resources as you can hold, but your backpack is full. - _plant.LabelTo(from, 1053058); + var resource = resInfo.CreateResource(); + + if (from.PlaceInBackpack(resource)) + { + system.AvailableResources--; + m_Plant.LabelTo(from, 1053059); // You gather resources from the plant. + } + else + { + resource.Delete(); + // You attempt to gather as many resources as you can hold, but your backpack is full. + m_Plant.LabelTo(from, 1053058); + } } - } - break; - } - case 8: // Gather seeds - { - var system = _plant.PlantSystem; + from.SendGump(new ReproductionGump(m_Plant)); - if (!_plant.Reproduces) - { - _plant.LabelTo(from, 1053060); // Mutated plants do not produce seeds! + break; } - else if (system.AvailableSeeds == 0) + case 8: // Gather seeds { - _plant.LabelTo(from, 1053061); // This plant has no seeds to gather! - } - else - { - var seed = new Seed(system.SeedType, system.SeedHue, true); + var system = m_Plant.PlantSystem; - if (from.PlaceInBackpack(seed)) + if (!m_Plant.Reproduces) { - system.AvailableSeeds--; - _plant.LabelTo(from, 1053063); // You gather seeds from the plant. + m_Plant.LabelTo(from, 1053060); // Mutated plants do not produce seeds! + } + else if (system.AvailableSeeds == 0) + { + m_Plant.LabelTo(from, 1053061); // This plant has no seeds to gather! } else { - seed.Delete(); - // You attempt to gather as many seeds as you can hold, but your backpack is full. - _plant.LabelTo(from, 1053062); - } - } + var seed = new Seed(system.SeedType, system.SeedHue, true); - break; - } + if (from.PlaceInBackpack(seed)) + { + system.AvailableSeeds--; + m_Plant.LabelTo(from, 1053063); // You gather seeds from the plant. + } + else + { + seed.Delete(); + // You attempt to gather as many seeds as you can hold, but your backpack is full. + m_Plant.LabelTo(from, 1053062); + } + } + + from.SendGump(new ReproductionGump(m_Plant)); + + break; + } + } } - - from.SendGump(this); } } diff --git a/Projects/UOContent/Engines/Plants/Seed.cs b/Projects/UOContent/Engines/Plants/Seed.cs index ed6f13b7b..043ad445b 100644 --- a/Projects/UOContent/Engines/Plants/Seed.cs +++ b/Projects/UOContent/Engines/Plants/Seed.cs @@ -24,6 +24,7 @@ public partial class Seed : Item [Constructible] public Seed(PlantType plantType, PlantHue plantHue, bool showType = false) : base(0xDCF) { + Weight = 1.0; Stackable = Core.SA; _plantType = plantType; @@ -33,16 +34,18 @@ public partial class Seed : Item Hue = PlantHueInfo.GetInfo(plantHue).Hue; } - public override double DefaultWeight => 1.0; - - [SerializableField(1, fieldChanged: nameof(OnPlantHueChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private PlantHue _plantHue; - - private void OnPlantHueChanged(PlantHue oldValue, PlantHue newValue) + [CommandProperty(AccessLevel.GameMaster)] + [SerializableProperty(1)] + public PlantHue PlantHue { - Hue = PlantHueInfo.GetInfo(newValue).Hue; + get => _plantHue; + set + { + _plantHue = value; + Hue = PlantHueInfo.GetInfo(value).Hue; + InvalidateProperties(); + this.MarkDirty(); + } } public override int LabelNumber => 1060810; // seed diff --git a/Projects/UOContent/Engines/Plants/SetToDecorativeGump.cs b/Projects/UOContent/Engines/Plants/SetToDecorativeGump.cs index 7f49d4665..f0bde3c0e 100644 --- a/Projects/UOContent/Engines/Plants/SetToDecorativeGump.cs +++ b/Projects/UOContent/Engines/Plants/SetToDecorativeGump.cs @@ -2,94 +2,87 @@ using Server.Engines.Help; using Server.Gumps; using Server.Network; -namespace Server.Engines.Plants; - -public class SetToDecorativeGump : StaticGump +namespace Server.Engines.Plants { - private readonly PlantItem _plant; - - public override bool Singleton => true; - - private SetToDecorativeGump(PlantItem plant) : base(20, 20) + public class SetToDecorativeGump : Gump { - _plant = plant; - } + private readonly PlantItem m_Plant; - public static void DisplayTo(Mobile from, PlantItem plant) - { - if (from?.NetState == null || plant == null || plant.Deleted) + public SetToDecorativeGump(PlantItem plant) : base(20, 20) { - return; + m_Plant = plant; + + DrawBackground(); + + AddLabel(115, 85, 0x44, "Set plant"); + AddLabel(82, 105, 0x44, "to decorative mode?"); + + AddButton(98, 140, 0x47E, 0x480, 1); // Cancel + + AddButton(138, 141, 0xD2, 0xD2, 2); // Help + AddLabel(143, 141, 0x835, "?"); + + AddButton(168, 140, 0x481, 0x483, 3); // Ok } - from.SendGump(new SetToDecorativeGump(plant)); - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(50, 50, 200, 150, 0xE10); - - builder.AddItem(25, 45, 0xCEB); - builder.AddItem(25, 118, 0xCEC); - - builder.AddItem(227, 45, 0xCEF); - builder.AddItem(227, 118, 0xCF0); - - builder.AddLabel(115, 85, 0x44, "Set plant"); - builder.AddLabel(82, 105, 0x44, "to decorative mode?"); - - builder.AddButton(98, 140, 0x47E, 0x480, 1); // Cancel - - builder.AddButton(138, 141, 0xD2, 0xD2, 2); // Help - builder.AddLabel(143, 141, 0x835, "?"); - - builder.AddButton(168, 140, 0x481, 0x483, 3); // Ok - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; - - if (info.ButtonID == 0 || _plant.Deleted || _plant.PlantStatus != PlantStatus.Stage9) + private void DrawBackground() { - return; + AddBackground(50, 50, 200, 150, 0xE10); + + AddItem(25, 45, 0xCEB); + AddItem(25, 118, 0xCEC); + + AddItem(227, 45, 0xCEF); + AddItem(227, 118, 0xCF0); } - if (info.ButtonID == 3 && !from.InRange(_plant.GetWorldLocation(), 3)) + public override void OnResponse(NetState sender, in RelayInfo info) { - from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away. - return; - } + var from = sender.Mobile; - if (!_plant.IsUsableBy(from)) - { - _plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it. - return; - } + if (info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus != PlantStatus.Stage9) + { + return; + } - switch (info.ButtonID) - { - case 1: // Cancel - { - ReproductionGump.DisplayTo(from, _plant); - break; - } - case 2: // Help - { - from.NetState.SendDisplayHelpTopic(HelpTopic.DecorativeMode); - from.SendGump(this); - break; - } - case 3: // Ok - { - _plant.PlantStatus = PlantStatus.DecorativePlant; - // You prune the plant. - // This plant will no longer produce resources or seeds, but will require no upkeep. - _plant.LabelTo(from, 1053077); - break; - } + if (info.ButtonID == 3 && !from.InRange(m_Plant.GetWorldLocation(), 3)) + { + from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away. + return; + } + + if (!m_Plant.IsUsableBy(from)) + { + m_Plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it. + return; + } + + switch (info.ButtonID) + { + case 1: // Cancel + { + from.SendGump(new ReproductionGump(m_Plant)); + + break; + } + case 2: // Help + { + from.NetState.SendDisplayHelpTopic(HelpTopic.DecorativeMode); + + from.SendGump(new SetToDecorativeGump(m_Plant)); + + break; + } + case 3: // Ok + { + m_Plant.PlantStatus = PlantStatus.DecorativePlant; + // You prune the plant. + // This plant will no longer produce resources or seeds, but will require no upkeep. + m_Plant.LabelTo(from, 1053077); + + break; + } + } } } } diff --git a/Projects/UOContent/Engines/Player Murder System/BountyBoard.cs b/Projects/UOContent/Engines/Player Murder System/BountyBoard.cs deleted file mode 100644 index 98538ac1b..000000000 --- a/Projects/UOContent/Engines/Player Murder System/BountyBoard.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Buffers; -using ModernUO.Serialization; -using Server.Engines.PlayerMurderSystem; -using Server.Mobiles; -using Server.Network; - -namespace Server.Items; - -[SerializationGenerator(0, false)] -public partial class BountyBoard : BaseBulletinBoard -{ - // Offset added to player serials (max 0x3FFFFFFF) to produce synthetic serials - // in the unused range (0x80000001–0xBFFFFFFF) for BB packets. Reversible by subtraction. - private const uint SyntheticSerialBase = 0x80000000; - - [Constructible] - public BountyBoard() : base(0x1E5E) => BoardName = "bounty board"; - - public override void OnSingleClick(Mobile from) - { - var count = PlayerMurderSystem.GetActiveBountyCount(); - if (count > 0) - { - LabelTo(from, $"a bounty board with {count} posted {(count == 1 ? "bounty" : "bounties")}"); - } - else - { - LabelTo(from, 1042679); // a bounty board with no bounties posted. - } - } - - public override void PostMessage(Mobile from, BulletinMessage thread, string subject, string[] lines) - { - from.SendLocalizedMessage(1062398); // You are not allowed to post to this bulletin board. - } - - public override void OnDoubleClick(Mobile from) - { - if (!CheckRange(from)) - { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } - - var state = from.NetState; - state.SendBBDisplayBoard(this); - BountyMessage.SendBountyContainerContent(state, this, SyntheticSerialBase); - } - - public override bool HandleBBRequest(int packetID, Mobile from, SpanReader reader) - { - switch (packetID) - { - case 3: // Request content - case 4: // Request header - { - var syntheticSerial = reader.ReadUInt32(); - var playerSerial = (Serial)(syntheticSerial - SyntheticSerialBase); - - if (World.FindMobile(playerSerial) is PlayerMobile player && - PlayerMurderSystem.GetMurderContext(player, out var context) && - context.Bounty > 0) - { - BountyMessage.SendBountyBBMessage( - from.NetState, - this, - (Serial)syntheticSerial, - player, - context.Bounty, - context.LastMurderTime, - packetID == 3 - ); - } - - return true; - } - case 5: // Post - blocked - { - from.SendLocalizedMessage(1062398); // You are not allowed to post to this bulletin board. - return true; - } - case 6: // Remove - blocked - { - return true; - } - } - - return false; - } -} diff --git a/Projects/UOContent/Engines/Player Murder System/BountyMessage.cs b/Projects/UOContent/Engines/Player Murder System/BountyMessage.cs deleted file mode 100644 index 46c6b0ab0..000000000 --- a/Projects/UOContent/Engines/Player Murder System/BountyMessage.cs +++ /dev/null @@ -1,393 +0,0 @@ -using System; -using System.Buffers; -using System.IO; -using Server.Items; -using Server.Mobiles; -using Server.Network; -using Server.Text; - -namespace Server.Engines.PlayerMurderSystem; - -public static class BountyMessage -{ - /// - /// Sends a synthetic container content packet (0x3C) using offset player serials. - /// The client displays these as bulletin board message entries. When clicked, - /// HandleBBRequest reverses the offset to recover the real player serial. - /// - public static void SendBountyContainerContent(NetState ns, BaseBulletinBoard board, uint syntheticSerialBase) - { - if (ns.CannotSendPackets()) - { - return; - } - - var bounties = PlayerMurderSystem.GetActiveBounties(); - var entrySize = ns.ContainerGridLines ? 20 : 19; - var totalSize = 5 + bounties.Count * entrySize; - - var writer = totalSize > 1024 ? new SpanWriter(totalSize) : new SpanWriter(stackalloc byte[totalSize]); - writer.Write((byte)0x3C); // Packet ID - writer.Seek(4, SeekOrigin.Current); // Length & count placeholder - - var written = 0; - - foreach (var (player, _) in bounties) - { - writer.Write((Serial)(syntheticSerialBase + (uint)player.Serial)); - writer.Write((ushort)0xEB0); // BulletinMessage ItemID - writer.Write((byte)0); // signed, itemID offset - writer.Write((ushort)1); // Amount - writer.Write((short)0); // X - writer.Write((short)0); // Y - if (ns.ContainerGridLines) - { - writer.Write((byte)0); // Grid location - } - writer.Write(board.Serial); // Container = this board - writer.Write((ushort)0); // Hue - written++; - } - - writer.Seek(1, SeekOrigin.Begin); - writer.Write((ushort)writer.BytesWritten); - writer.Write((ushort)written); - writer.Seek(0, SeekOrigin.End); - - ns.Send(writer.Span); - - writer.Dispose(); - } - - /// - /// Sends a synthetic BB message packet (header or content) for a bounty entry, - /// built entirely from live MurderSystem data — no real BulletinMessage item required. - /// Uses a resizable SpanWriter for single-pass writing with zero intermediate allocations. - /// - public static void SendBountyBBMessage(NetState ns, BaseBulletinBoard board, Serial messageSerial, PlayerMobile player, - int bounty, DateTime lastMurderTime, bool content) - { - if (ns.CannotSendPackets()) - { - return; - } - - // Build subject and time without heap allocation - using var subjectBuilder = new ValueStringBuilder(stackalloc char[32]); - subjectBuilder.Append(bounty); - subjectBuilder.Append(" gold"); - - using var timeBuilder = new ValueStringBuilder(stackalloc char[16]); - timeBuilder.Append(lastMurderTime, "MMM dd, yyyy"); - - // Reusable UTF-8 encoding buffer (768 = GetMaxByteCount(255), covers any WriteString call) - Span textBuffer = stackalloc byte[768]; - - // Single-pass writing with resizable SpanWriter — auto-grows if needed - var writer = new SpanWriter(stackalloc byte[1024], resize: true); - writer.Write((byte)0x71); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write((byte)(content ? 0x02 : 0x01)); // Command - writer.Write(board.Serial); - writer.Write(messageSerial); // Synthetic serial — must match the 0x3C entry - if (!content) - { - writer.Write(Serial.Zero); // Thread serial (all root-level) - } - - writer.WriteString(player.Name.AsSpan(), textBuffer); - writer.WriteString(subjectBuilder.AsSpan(), textBuffer); - writer.WriteString(timeBuilder.AsSpan(), textBuffer); - - if (content) - { - writer.Write((short)player.Body); - writer.Write((short)player.Hue); - - // Write equipment directly from player items (no intermediate array) - var equipCount = 0; - for (var i = 0; i < player.Items.Count; i++) - { - if (player.Items[i].Layer >= Layer.OneHanded && player.Items[i].Layer <= Layer.Mount) - { - equipCount++; - } - } - - var equipLength = Math.Min(255, equipCount); - writer.Write((byte)equipLength); - - var equipWritten = 0; - for (var i = 0; i < player.Items.Count && equipWritten < equipLength; i++) - { - var item = player.Items[i]; - if (item.Layer >= Layer.OneHanded && item.Layer <= Layer.Mount) - { - writer.Write((short)item.ItemID); - writer.Write((short)item.Hue); - equipWritten++; - } - } - - WriteContentLines(ref writer, textBuffer, player, bounty); - } - - writer.WritePacketLength(); - ns.Send(writer.Span); - - writer.Dispose(); - } - - /// - /// Writes all content lines directly to the packet writer, avoiding intermediate - /// string/list allocations. Uses ValueStringBuilder for line construction and - /// span slicing for word wrapping. - /// - private static void WriteContentLines(ref SpanWriter writer, Span textBuffer, PlayerMobile player, int bounty) - { - var isFemale = player.Body.IsFemale; - var pronoun = isFemale ? "she" : "he"; - var possessive = isFemale ? "her" : "his"; - var objective = isFemale ? "her" : "him"; - - // Placeholder for line count — patched after all lines are written - var lineCountPos = writer.Position; - writer.Write((byte)0); - var lineCount = 0; - - // Reusable builder for constructing each line - using var lineBuilder = new ValueStringBuilder(stackalloc char[64]); - - // Title line (random, 6 variants matching uo98 bountyboard.m) - switch (Utility.Random(6)) - { - case 0: - { - lineBuilder.Append($"Bounty for {player.RawName}!"); break; - } - case 1: - { - lineBuilder.Append($"{player.RawName} must die!"); break; - } - case 2: - { - lineBuilder.Append($"A price on {player.RawName}!"); break; - } - case 3: - { - lineBuilder.Append($"{player.RawName} outlawed!"); break; - } - case 4: - { - lineBuilder.Append($"Execute {player.RawName}!"); break; - } - default: - { - lineBuilder.Append($"WANTED: {player.RawName}!"); break; - } - } - - writer.WriteString(lineBuilder.AsSpan(), textBuffer, true); - lineCount++; - - // Blank line - writer.WriteString(ReadOnlySpan.Empty, textBuffer, true); - lineCount++; - - // Build main paragraph with ValueStringBuilder - var paraBuilder = new ValueStringBuilder(stackalloc char[256]); - - // Random verb phrase (18 variants matching uo98 bountyboard.m) - var verb = Utility.Random(18) switch - { - 0 => "hath murdered one too many!", - 1 => "shall not slay again!", - 2 => "hath slain too many!", - 3 => "cannot continue to kill!", - 4 => "must be stopped.", - 5 => "is a bloodthirsty monster.", - 6 => "is a killer of the worst sort.", - 7 => "hath no conscience!", - 8 => "hath cowardly slain many.", - 9 => "must die for all our sakes.", - 10 => "sheds innocent blood!", - 11 => "must fall to preserve us.", - 12 => "must be taken care of.", - 13 => "is a thug and must die.", - 14 => "cannot be redeemed.", - 15 => "is a shameless butcher.", - 16 => "is a callous monster.", - _ => "is a cruel, casual killer." - }; - - // Random bounty intro phrase (7 variants matching uo98 bountyboard.m) - var intro = Utility.Random(7) switch - { - 0 => " A bounty is hereby offered", - 1 => " Lord British sets a price", - 2 => " Claim the reward! 'Tis", - 3 => " Lord Blackthorn set a price", - 4 => " The Paladins set a price", - 5 => " The Merchants set a price", - _ => " Lord British's bounty " - }; - - paraBuilder.Append($"The foul scum known as {player.RawName} {verb} For {pronoun} is responsible for {player.Kills} murders. {intro} of {bounty} gold pieces for {possessive} head!"); - - // Word-wrap at 28 chars and write each line directly (matching uo98 bountyboard.m CONST:28) - WriteWordWrappedLines(ref writer, paraBuilder.AsSpan(), 28, textBuffer, ref lineCount); - paraBuilder.Dispose(); - - // Physical description - writer.WriteString(ReadOnlySpan.Empty, textBuffer, true); - lineCount++; - - writer.WriteString(" A description:", textBuffer, true); - lineCount++; - - lineBuilder.Reset(); - lineBuilder.Append($" - {GetHairStyle(player.HairItemID)}"); - writer.WriteString(lineBuilder.AsSpan(), textBuffer, true); - lineCount++; - - lineBuilder.Reset(); - lineBuilder.Append($" - {GetHairColor(player.HairHue)} hair"); - writer.WriteString(lineBuilder.AsSpan(), textBuffer, true); - lineCount++; - - lineBuilder.Reset(); - lineBuilder.Append($" - {GetSkinTone(player.Hue)} skin"); - writer.WriteString(lineBuilder.AsSpan(), textBuffer, true); - lineCount++; - - // Closing instructions - writer.WriteString(ReadOnlySpan.Empty, textBuffer, true); - lineCount++; - - lineBuilder.Reset(); - lineBuilder.Append($"If you kill {objective}, remove the"); - writer.WriteString(lineBuilder.AsSpan(), textBuffer, true); - lineCount++; - - writer.WriteString("head, and give it to a guard", textBuffer, true); - lineCount++; - - writer.WriteString("to claim your reward.", textBuffer, true); - lineCount++; - - // Patch line count - var endPos = writer.Position; - writer.Seek(lineCountPos, SeekOrigin.Begin); - writer.Write((byte)Math.Min(255, lineCount)); - writer.Seek(endPos, SeekOrigin.Begin); - } - - /// - /// Word-wraps text at maxWidth characters and writes each line directly to the packet. - /// Uses span slicing instead of Substring to avoid allocations. - /// - private static void WriteWordWrappedLines(ref SpanWriter writer, scoped ReadOnlySpan text, int maxWidth, - Span textBuffer, ref int lineCount) - { - // Pre-allocate buffer for last segment (needs trailing space) - Span lastSegmentBuf = stackalloc char[maxWidth + 2]; - var current = 0; - - while (current < text.Length) - { - var remaining = text.Length - current; - - if (remaining > maxWidth) - { - var length = maxWidth; - - while (length > 0 && text[current + length] != ' ') - { - length--; - } - - if (length == 0) - { - length = maxWidth; // hard break — no space found - } - else - { - length++; // include the space - } - - writer.WriteString(text.Slice(current, length), textBuffer, true); - current += length; - } - else - { - // Last segment — append trailing space (matching original behavior) - text.Slice(current, remaining).CopyTo(lastSegmentBuf); - lastSegmentBuf[remaining] = ' '; - writer.WriteString(lastSegmentBuf[..(remaining + 1)], textBuffer, true); - current += remaining; - } - - lineCount++; - } - } - - // Maps hair item IDs to style descriptions (from uo98 bountyboard.m lookup table) - private static string GetHairStyle(int itemID) => itemID switch - { - 0x203B => "hair worn short", - 0x203C => "hair worn long", - 0x203D => "hair tied back", - 0x2044 => "a mohawk hairstyle", - 0x2045 => "pageboy hair", - 0x2046 => "hair tied in buns", - 0x2047 => "curly hair", - 0x2048 => "receding hairline", - 0x2049 => "hair in two pigtails", - 0x204A => "shaved head and topknot", - _ => "bald" - }; - - // Maps hair hues to color descriptions (from uo98 bountyboard.m lookup table) - // Human hair hues: 0x44E–0x47D. Index 0 (hue 0) = indeterminate, index 1+ maps to 0x44E+ - private static string GetHairColor(int hue) => hue switch - { - 0 => "indeterminate color", - 0x44E or 0x44F or 0x450 => "white", - 0x451 or 0x452 or 0x453 => "graying", - 0x454 or 0x455 => "black", - 0x456 or 0x457 or 0x458 => "copper", - 0x459 or 0x45A or 0x45B or 0x45C => "brown", - 0x45D => "reddish brown", - 0x45E or 0x45F or 0x460 => "blonde", - 0x461 or 0x462 or 0x463 => "light brown", - 0x464 or 0x465 => "golden brown", - 0x466 or 0x467 or 0x468 => "golden", - 0x469 or 0x46A or 0x46B => "bronze", - 0x46C or 0x46D => "dark brown", - 0x46E or 0x46F => "sandy", - 0x470 or 0x471 or 0x472 => "honey-colored", - 0x473 or 0x474 or 0x475 => "red", - 0x476 or 0x477 or 0x478 => "nut brown", - 0x479 or 0x47A or 0x47B => "rich brown", - 0x47C or 0x47D => "very dark brown", - _ => "outlandishly colored" - }; - - // Maps body hues to skin tone descriptions (from uo98 bountyboard.m lookup table) - // Human skin hues: 0x3EA–0x422 (may have 0x8000 flag). Index 1 = 0x3EA. - private static string GetSkinTone(int hue) => (hue & 0x7FFF) switch - { - 0x3F1 or 0x3F2 or 0x3F8 or 0x3FF or 0x400 or 0x406 or 0x407 or 0x408 - or 0x40D or 0x40E or 0x415 or 0x416 => "pale", - 0x3EA or 0x3EB or 0x3F9 or 0x3FA or 0x3FB or 0x417 => "fair", - 0x3F3 or 0x3F4 or 0x3FC or 0x401 or 0x402 or 0x409 or 0x40F - or 0x410 or 0x411 or 0x418 => "tanned", - 0x3EC or 0x3ED or 0x3EE or 0x3F5 or 0x403 or 0x412 or 0x413 - or 0x419 or 0x421 => "copper", - 0x3EF or 0x3F0 or 0x3F6 or 0x3F7 or 0x3FD or 0x3FE or 0x404 - or 0x405 or 0x40A or 0x40B or 0x40C or 0x414 or 0x41A - or 0x41B or 0x420 => "dark", - 0x41C or 0x41D or 0x41E => "yellow", - _ => "deathly" - }; -} diff --git a/Projects/UOContent/Engines/Player Murder System/BountyReportMurdererGump.cs b/Projects/UOContent/Engines/Player Murder System/BountyReportMurdererGump.cs deleted file mode 100644 index 2f68a22e8..000000000 --- a/Projects/UOContent/Engines/Player Murder System/BountyReportMurdererGump.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Gumps; -using Server.Mobiles; -using Server.Network; - -namespace Server.Engines.PlayerMurderSystem; - -public class BountyReportMurdererGump : DynamicGump -{ - private readonly List _killers; - private readonly PlayerMobile _victim; - private int _idx; - - public BountyReportMurdererGump(PlayerMobile victim, List killers, int idx = 0) : base(0, 0) - { - _victim = victim; - _killers = killers; - _idx = idx; - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); - builder.SetNoResize(); - - builder.AddBackground(265, 205, 393, 270, 70000); - builder.AddImage(265, 205, 1140); - - builder.AddPage(); - - builder.AddHtml(325, 255, 300, 60, - $"Would you like to report {_killers[_idx].Name} as a murderer?"); - - var bountyMax = Banker.GetBalance(_victim); - - if (_killers[_idx].Kills >= 4 && bountyMax > 0) - { - builder.AddHtml(325, 325, 300, 60, $"Optional Bounty: [{bountyMax} max] "); - builder.AddImage(323, 343, 0x475); - builder.AddTextEntry(329, 346, 311, 16, 0, 1); - } - - builder.AddButton(385, 395, 0x47B, 0x47D, 1); - builder.AddButton(465, 395, 0x478, 0x47A, 2); - } - - public override void OnResponse(NetState state, in RelayInfo info) - { - var from = (PlayerMobile)state.Mobile; - - if (info.ButtonID == 1) - { - var killer = _killers[_idx]; - - if (PlayerMurderSystem.ReportMurder(from, killer) && killer is PlayerMobile pk) - { - var text = info.GetTextEntry(1); - if (!string.IsNullOrWhiteSpace(text) && int.TryParse(text, out var requested) && requested > 0) - { - var bounty = Math.Min(requested, Banker.GetBalance(from)); - if (bounty > 0 && Banker.Withdraw(from, bounty)) - { - PlayerMurderSystem.AddBounty(pk, bounty); - - pk.SendMessage( - $"{from.Name} has placed a bounty of {bounty} {(bounty == 1 ? "gold piece" : "gold pieces")} on your head!" - ); - - from.SendMessage($"You place a bounty of {bounty}gp on {pk.Name}'s head."); - } - } - } - } - - _idx++; - if (_idx < _killers.Count) - { - from.SendGump(new BountyReportMurdererGump(from, _killers, _idx)); - } - } -} diff --git a/Projects/UOContent/Engines/Player Murder System/MurderContext.cs b/Projects/UOContent/Engines/Player Murder System/MurderContext.cs index 384c91c98..b9584dd8a 100644 --- a/Projects/UOContent/Engines/Player Murder System/MurderContext.cs +++ b/Projects/UOContent/Engines/Player Murder System/MurderContext.cs @@ -5,7 +5,7 @@ using Server.Mobiles; namespace Server.Engines.PlayerMurderSystem; -[SerializationGenerator(2)] +[SerializationGenerator(0)] public partial class MurderContext { [SerializableField(0)] @@ -16,46 +16,12 @@ public partial class MurderContext [SerializedCommandProperty(AccessLevel.GameMaster)] private TimeSpan _longTermElapse; - [SerializableField(2, allowFieldChange: nameof(AllowShortTermMurdersChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _shortTermMurders; - - private bool AllowShortTermMurdersChange(ref int value) + [SerializableProperty(2)] + [CommandProperty(AccessLevel.GameMaster)] + public int ShortTermMurders { - value = Math.Max(value, 0); - return true; - } - - [SerializableField(3)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _pingPongs; - - [SerializableField(4)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _bounty; - - [SerializableField(5)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private DateTime _lastMurderTime; - - private void MigrateFrom(V0Content content) - { - _shortTermElapse = content.ShortTermElapse; - _longTermElapse = content.LongTermElapse; - _shortTermMurders = content.ShortTermMurders; - // Players already at >= 5 kills have crossed the threshold at least once - _pingPongs = _player.Kills >= 5 ? 1 : 0; - } - - private void MigrateFrom(V1Content content) - { - _shortTermElapse = content.ShortTermElapse; - _longTermElapse = content.LongTermElapse; - _shortTermMurders = content.ShortTermMurders; - _pingPongs = content.PingPongs; - // _bounty defaults to 0 - // Default to now so the bounty board date display is sensible for migrated contexts - _lastMurderTime = Core.Now; + get => _shortTermMurders; + set => _shortTermMurders = Math.Max(value, 0); } public PlayerMobile _player; @@ -99,8 +65,6 @@ public partial class MurderContext } } - public bool CanRemove() => _pingPongs <= 0 && _shortTermMurders <= 0 && _player.Kills <= 0; - public bool CheckStart() { _nextElapse = DateTime.MaxValue; @@ -115,7 +79,11 @@ public partial class MurderContext if (_player.Kills > 0) { - _nextElapse = Utility.Min(_nextElapse, now + (LongTermElapse - gameTime)); + var timeUntilLong = now + (LongTermElapse - gameTime); + if (_nextElapse > timeUntilLong) + { + _nextElapse = timeUntilLong; + } } return _nextElapse != DateTime.MaxValue; diff --git a/Projects/UOContent/Engines/Player Murder System/PlayerMurderSystem.cs b/Projects/UOContent/Engines/Player Murder System/PlayerMurderSystem.cs index d1be82aa9..8a654a697 100644 --- a/Projects/UOContent/Engines/Player Murder System/PlayerMurderSystem.cs +++ b/Projects/UOContent/Engines/Player Murder System/PlayerMurderSystem.cs @@ -5,9 +5,7 @@ using System.Runtime.InteropServices; using ModernUO.CodeGeneratedEvents; using Server.Collections; using Server.Logging; -using Server.Misc; using Server.Mobiles; -using Server.SkillHandlers; namespace Server.Engines.PlayerMurderSystem; @@ -23,9 +21,6 @@ public class PlayerMurderSystem : GenericPersistence // Only the players that are online private static readonly HashSet _contextTerms = new(MurderContext.EqualityComparer.Default); - private static readonly HashSet<(Mobile, Mobile)> _recentlyReported = new(); - private static TimeSpan _recentlyReportedDelay; - private static TimeSpan _shortTermMurderDuration; private static TimeSpan _longTermMurderDuration; @@ -34,19 +29,10 @@ public class PlayerMurderSystem : GenericPersistence public static TimeSpan LongTermMurderDuration => _longTermMurderDuration; - public static bool PingPongEnabled => Core.T2A && !Core.LBR; - - public static bool BountiesEnabled { get; private set; } - - private static TimeSpan _bountyExpiry; - public static void Configure() { _shortTermMurderDuration = ServerConfiguration.GetOrUpdateSetting("murderSystem.shortTermMurderDuration", TimeSpan.FromHours(8)); _longTermMurderDuration = ServerConfiguration.GetOrUpdateSetting("murderSystem.longTermMurderDuration", TimeSpan.FromHours(40)); - BountiesEnabled = ServerConfiguration.GetOrUpdateSetting("murderSystem.bountiesEnabled", !Core.LBR); - _recentlyReportedDelay = ServerConfiguration.GetOrUpdateSetting("murderSystem.recentlyReportedDelay", TimeSpan.FromMinutes(10)); - _bountyExpiry = ServerConfiguration.GetOrUpdateSetting("murderSystem.bountyExpiry", TimeSpan.FromDays(14)); _playerMurderPersistence = new PlayerMurderSystem(); } @@ -108,27 +94,16 @@ public class PlayerMurderSystem : GenericPersistence } else { + _murderContexts.Remove(pm); _contextTerms.Remove(context); - if (context.CanRemove()) - { - _murderContexts.Remove(pm); - } } } private static void OnDisconnected(Mobile m) { - if (m is not PlayerMobile pm || !_murderContexts.TryGetValue(pm, out var context)) + if (m is PlayerMobile pm && _murderContexts.Remove(pm, out var context)) { - return; - } - - context.DecayKills(); - _contextTerms.Remove(context); - - if (context.CanRemove()) - { - _murderContexts.Remove(pm); + _contextTerms.Remove(context); } } @@ -185,76 +160,10 @@ public class PlayerMurderSystem : GenericPersistence return context; } - public static int GetBounty(PlayerMobile player) => - GetMurderContext(player, out var context) ? context.Bounty : 0; - - public static void AddBounty(PlayerMobile player, int amount) - { - if (GetMurderContext(player, out var context)) - { - context.Bounty += amount; - } - } - - public static void ClearBounty(PlayerMobile player) - { - if (GetMurderContext(player, out var context)) - { - context.Bounty = 0; - } - } - - public static int GetActiveBountyCount() - { - var neverExpire = _bountyExpiry == TimeSpan.Zero; - var cutoff = neverExpire ? DateTime.MinValue : Core.Now - _bountyExpiry; - var count = 0; - - foreach (var (player, context) in _murderContexts) - { - if (player.Murderer && context.Bounty > 0 && (neverExpire || context.LastMurderTime >= cutoff)) - { - count++; - } - } - - return count; - } - - // Reused across calls — safe because the server is single-threaded. - private static readonly List<(PlayerMobile Player, int Bounty)> _activeBountyCache = []; - - public static List<(PlayerMobile Player, int Bounty)> GetActiveBounties() - { - _activeBountyCache.Clear(); - var neverExpire = _bountyExpiry == TimeSpan.Zero; - var cutoff = neverExpire ? DateTime.MinValue : Core.Now - _bountyExpiry; - - foreach (var (player, context) in _murderContexts) - { - if (player.Murderer && context.Bounty > 0 && (neverExpire || context.LastMurderTime >= cutoff)) - { - _activeBountyCache.Add((player, context.Bounty)); - } - } - - _activeBountyCache.Sort(static (a, b) => b.Bounty.CompareTo(a.Bounty)); - return _activeBountyCache; - } - - public static void ManuallySetPingPong(PlayerMobile player, int pingPong) - { - var context = GetOrCreateMurderContext(player); - context.PingPongs = Math.Max(pingPong, 0); - UpdateMurderContext(context); - } - public static void ManuallySetShortTermMurders(PlayerMobile player, int shortTermMurders) { var context = GetOrCreateMurderContext(player); context.ShortTermMurders = shortTermMurders; - - context.ResetKillTime(); UpdateMurderContext(context); } @@ -264,69 +173,17 @@ public class PlayerMurderSystem : GenericPersistence context.ShortTermMurders++; player.Kills++; - if (PingPongEnabled && player.Kills == 5) - { - context.PingPongs++; - } - - context.LastMurderTime = Core.Now; context.ResetKillTime(); UpdateMurderContext(context); } - public static bool IsRecentlyReported(Mobile reporter, Mobile killer) => - _recentlyReported.Contains((reporter, killer)); - - public static bool ReportMurder(PlayerMobile reporter, Mobile killer) - { - if (killer?.Deleted != false || killer is not PlayerMobile pk) - { - return false; - } - - if (!_recentlyReported.Add((reporter, killer))) - { - return false; - } - - Timer.DelayCall( - _recentlyReportedDelay, - static (f, k) => _recentlyReported.Remove((f, k)), - reporter, - killer - ); - - var wasMurderer = killer.Murderer; - OnPlayerMurder(pk); - - Titles.SetKarma(pk, pk.Kills * (Core.AOS ? -2000 : -1000), true); - - pk.SendLocalizedMessage(1049067); // You have been reported for murder! - - if (!wasMurderer && killer.Murderer) - { - pk.SendLocalizedMessage(502134); // You are now known as a murderer! - } - - // with the introduction of PingPongs, a red can technically have 1 kill. - if (Stealing.SuspendOnMurder && pk.Kills == 1 && pk.NpcGuild == NpcGuild.ThievesGuild) - { - pk.SendLocalizedMessage(501562); // You have been suspended by the Thieves Guild. - } - - return true; - } - private static void UpdateMurderContext(MurderContext context) { var player = context.Player; if (!context.CheckStart()) { - if (context.CanRemove()) - { - _murderContexts.Remove(player); - } + _murderContexts.Remove(player); _contextTerms.Remove(context); } else if (player.NetState != null) @@ -335,56 +192,6 @@ public class PlayerMurderSystem : GenericPersistence } } - public static void ReportKillsToSelf(PlayerMobile player) - { - if (Core.SA) - { - player.SendLocalizedMessage(1114370, $"{player.ShortTermMurders}\t{player.Kills}"); - } - else if (Core.SE) - { - player.SendMessage($"Short Term Murders: {player.ShortTermMurders} Long Term Murders: {player.Kills}"); - } - else if (Core.AOS) - { - player.SendMessage($"Short Term Murders: {player.ShortTermMurders}"); - player.SendMessage($"Long Term Murders: {player.Kills}"); - if (PingPongEnabled) - { - player.SendMessage($"Ping Pongs: {player.PingPongs}"); - } - } - else if (!Core.T2A) - { - // No "i must consider my sins" before t2a - } - else if (PingPongEnabled && player.PingPongs >= 5) - { - // Thou art known throughout the land as a murderous brigand. - player.SendLocalizedMessage(502123, "", player.ShortTermMurders >= 5 ? 0x22 : 0x59); - } - else if (player.ShortTermMurders >= 5) - { - // If thou should return to the land of the living, the innocent shall wreak havoc upon thy soul - player.SendLocalizedMessage(502126, "", 0x22); - } - else if (player.ShortTermMurders > 0) - { - // Although thou hast slain the innocent, thy deeds shall not bring retribution upon thy return to the living - player.SendLocalizedMessage(502125, "", 0x59); - } - else if (player.Kills > 0) - { - // Fear not, thou hast not slain the innocent in some time... - player.SendLocalizedMessage(502124, "", 0x59); - } - else // no kills - { - // Fear not, thou hast not slain the innocent. - player.SendLocalizedMessage(502122, "", 0x59); - } - } - private class MurdererTimer : Timer { public MurdererTimer() : base(TimeSpan.FromMinutes(5.0), TimeSpan.FromMinutes(5.0)) @@ -403,20 +210,22 @@ public class PlayerMurderSystem : GenericPersistence return; } + using var queue = PooledRefQueue.Create(); + foreach (var context in _contextTerms) { context.DecayKills(); if (!context.CheckStart()) { - var pm = context.Player; - if (_murderContexts.TryGetValue(pm, out var ctx)) - { - if (ctx.CanRemove()) - { - _murderContexts.Remove(pm); - } - _contextTerms.Remove(ctx); - } + queue.Enqueue(context.Player); + } + } + + while (queue.Count > 0) + { + if (_murderContexts.Remove((PlayerMobile)queue.Dequeue(), out var context)) + { + _contextTerms.Remove(context); } } } diff --git a/Projects/UOContent/Engines/Player Murder System/ReportMurdererGump.cs b/Projects/UOContent/Engines/Player Murder System/ReportMurdererGump.cs index 2ef3e4073..31b4a6bc3 100644 --- a/Projects/UOContent/Engines/Player Murder System/ReportMurdererGump.cs +++ b/Projects/UOContent/Engines/Player Murder System/ReportMurdererGump.cs @@ -5,11 +5,16 @@ using Server.Gumps; using Server.Misc; using Server.Mobiles; using Server.Network; +using Server.SkillHandlers; namespace Server.Engines.PlayerMurderSystem; public class ReportMurdererGump : StaticGump { + // Recently reported + private static TimeSpan _recentlyReportedDelay; + private static readonly HashSet<(Mobile, Mobile)> _recentlyReported = new(); + private readonly List _killers; private int _idx; @@ -19,6 +24,11 @@ public class ReportMurdererGump : StaticGump _idx = idx; } + public static void Initialize() + { + _recentlyReportedDelay = ServerConfiguration.GetOrUpdateSetting("murderSystem.recentlyReportedDelay", TimeSpan.FromMinutes(10)); + } + [OnEvent(nameof(PlayerMobile.PlayerDeathEvent))] public static void OnPlayerDeathEvent(PlayerMobile m) { @@ -26,20 +36,23 @@ public class ReportMurdererGump : StaticGump HashSet toGive = null; // Guards won't take reports of the death of a thief! - var notInThievesGuild = m.NpcGuild != NpcGuild.ThievesGuild; + bool notInThievesGuild = m.NpcGuild != NpcGuild.ThievesGuild; foreach (var ai in m.Aggressors) { - if (ai.Attacker.Player && ai.CanReportMurder && !ai.Reported && !PlayerMurderSystem.IsRecentlyReported(m, ai.Attacker)) + if (ai.Attacker.Player && ai.CanReportMurder && !ai.Reported) { - if (notInThievesGuild) + if (!Core.SE || !_recentlyReported.Contains((m, ai.Attacker))) { - killers ??= new List(); - killers.Add(ai.Attacker); - } + if (notInThievesGuild) + { + killers ??= new List(); + killers.Add(ai.Attacker); + } - ai.Reported = true; - ai.CanReportMurder = false; + ai.Reported = true; + ai.CanReportMurder = false; + } } if (ai.Attacker.Player && Core.Now - ai.LastCombatTime < TimeSpan.FromSeconds(30.0)) @@ -60,11 +73,28 @@ public class ReportMurdererGump : StaticGump if (toGive?.Count > 0) { - var (fameAward, _) = Titles.ComputeKillAwards(m, m.Map); - foreach (var g in toGive) { + var n = Notoriety.Compute(g, m); + + var ourKarma = g.Karma; + var innocent = n == Notoriety.Innocent; + var criminal = n is Notoriety.Criminal or Notoriety.Murderer; + + var fameAward = m.Fame / 200; + var karmaAward = 0; + + if (innocent) + { + karmaAward = ourKarma > -2500 ? -850 : -110 - m.Karma / 100; + } + else if (criminal) + { + karmaAward = 50; + } + Titles.AwardFame(g, fameAward, false); + Titles.AwardKarma(g, karmaAward, true); } } @@ -117,7 +147,40 @@ public class ReportMurdererGump : StaticGump { case 1: { - PlayerMurderSystem.ReportMurder(from, _killers[_idx]); + var killer = _killers[_idx]; + if (killer?.Deleted == false) + { + if (Core.SE) + { + if (_recentlyReported.Add((from, killer))) + { + Timer.DelayCall( + _recentlyReportedDelay, + static (f, k) => _recentlyReported.Remove((f, k)), + from, + killer + ); + } + } + + if (killer is PlayerMobile pk) + { + // Increment their short term murders, their kills, and reset the murder decay time + PlayerMurderSystem.OnPlayerMurder(pk); + + pk.SendLocalizedMessage(1049067); // You have been reported for murder! + + if (pk.Kills == 5) + { + pk.SendLocalizedMessage(502134); // You are now known as a murderer! + } + else if (Stealing.SuspendOnMurder && pk.Kills == 1 && pk.NpcGuild == NpcGuild.ThievesGuild) + { + pk.SendLocalizedMessage(501562); // You have been suspended by the Thieves Guild. + } + } + } + break; } case 2: @@ -146,14 +209,7 @@ public class ReportMurdererGump : StaticGump protected override void OnTick() { - if (PlayerMurderSystem.BountiesEnabled && _victim is PlayerMobile pm) - { - pm.SendGump(new BountyReportMurdererGump(pm, _killers)); - } - else - { - _victim.SendGump(new ReportMurdererGump(_killers)); - } + _victim.SendGump(new ReportMurdererGump(_killers)); } } } diff --git a/Projects/UOContent/Engines/Quests/Ambitious Solen Queen/AmbitiousQueenQuest.cs b/Projects/UOContent/Engines/Quests/Ambitious Solen Queen/AmbitiousQueenQuest.cs index 095dc75f1..fff90c899 100644 --- a/Projects/UOContent/Engines/Quests/Ambitious Solen Queen/AmbitiousQueenQuest.cs +++ b/Projects/UOContent/Engines/Quests/Ambitious Solen Queen/AmbitiousQueenQuest.cs @@ -70,7 +70,7 @@ namespace Server.Engines.Quests.Ambitious { if (bagOfSending) { - var reward = new BagOfSending(); + Item reward = new BagOfSending(); if (player.PlaceInBackpack(reward)) { @@ -85,7 +85,7 @@ namespace Server.Engines.Quests.Ambitious if (powderOfTranslocation) { - var reward = new PowderOfTranslocation(Utility.RandomMinMax(10, 12)); + Item reward = new PowderOfTranslocation(Utility.RandomMinMax(10, 12)); if (player.PlaceInBackpack(reward)) { @@ -100,7 +100,7 @@ namespace Server.Engines.Quests.Ambitious if (gold) { - var reward = new Gold(Utility.RandomMinMax(250, 350)); + Item reward = new Gold(Utility.RandomMinMax(250, 350)); if (player.PlaceInBackpack(reward)) { diff --git a/Projects/UOContent/Engines/Quests/Ambitious Solen Queen/Objectives.cs b/Projects/UOContent/Engines/Quests/Ambitious Solen Queen/Objectives.cs index 418152227..326a9e8d4 100644 --- a/Projects/UOContent/Engines/Quests/Ambitious Solen Queen/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Ambitious Solen Queen/Objectives.cs @@ -1,4 +1,3 @@ -using Server.Gumps; using Server.Items; using Server.Mobiles; @@ -10,12 +9,12 @@ namespace Server.Engines.Quests.Ambitious public override int MaxProgress => 5; - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { // Red/Black Solen Queens killed: - builder.AddHtmlLocalized( + gump.AddHtmlLocalized( 70, 260, 270, @@ -23,13 +22,13 @@ namespace Server.Engines.Quests.Ambitious ((AmbitiousQueenQuest)System).RedSolen ? 1054064 : 1054065, BaseQuestGump.Blue ); - builder.AddLabel(70, 280, 0x64, $"{CurProgress}"); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, $"{MaxProgress}"); + gump.AddLabel(70, 280, 0x64, CurProgress.ToString()); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, MaxProgress.ToString()); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } diff --git a/Projects/UOContent/Engines/Quests/Collector/Items/EnchantedPaints.cs b/Projects/UOContent/Engines/Quests/Collector/Items/EnchantedPaints.cs index d9e13d5b9..5b6ab0bb2 100644 --- a/Projects/UOContent/Engines/Quests/Collector/Items/EnchantedPaints.cs +++ b/Projects/UOContent/Engines/Quests/Collector/Items/EnchantedPaints.cs @@ -8,9 +8,11 @@ namespace Server.Engines.Quests.Collector; public partial class EnchantedPaints : QuestItem { [Constructible] - public EnchantedPaints() : base(0xFC1) => LootType = LootType.Blessed; - - public override double DefaultWeight => 1.0; + public EnchantedPaints() : base(0xFC1) + { + LootType = LootType.Blessed; + Weight = 1.0; + } public override bool CanDrop(PlayerMobile player) => player.Quest is not CollectorQuest; diff --git a/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs b/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs index c52752714..7c9aa4da8 100644 --- a/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs +++ b/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs @@ -14,12 +14,12 @@ public partial class PaintedImage : Item [Constructible] public PaintedImage(ImageType image) : base(0xFF3) { + Weight = 1.0; Hue = 0x8FD; + _image = image; } - public override double DefaultWeight => 1.0; - public override void AddNameProperty(IPropertyList list) { var info = ImageTypeInfo.Get(_image); @@ -40,37 +40,19 @@ public partial class PaintedImage : Item return; } - PaintedImageGump.DisplayTo(from, _image); + from.SendGump(new InternalGump(_image)); } -} -public class PaintedImageGump : DynamicGump -{ - private readonly ImageType _image; - - public override bool Singleton => true; - - private PaintedImageGump(ImageType image) : base(75, 25) => _image = image; - - public static void DisplayTo(Mobile from, ImageType image) + private class InternalGump : Gump { - if (from?.NetState == null) + public InternalGump(ImageType image) : base(75, 25) { - return; + var info = ImageTypeInfo.Get(image); + + AddBackground(45, 20, 100, 100, 0xA3C); + AddBackground(52, 29, 86, 82, 0xBB8); + + AddItem(info.X, info.Y, info.Figurine); } - - from.SendGump(new PaintedImageGump(image)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - var info = ImageTypeInfo.Get(_image); - - builder.AddBackground(45, 20, 100, 100, 0xA3C); - builder.AddBackground(52, 29, 86, 82, 0xBB8); - - builder.AddItem(info.X, info.Y, info.Figurine); } } diff --git a/Projects/UOContent/Engines/Quests/Collector/Mobiles/Impresario.cs b/Projects/UOContent/Engines/Quests/Collector/Mobiles/Impresario.cs index 30bfae77f..68cec5725 100644 --- a/Projects/UOContent/Engines/Quests/Collector/Mobiles/Impresario.cs +++ b/Projects/UOContent/Engines/Quests/Collector/Mobiles/Impresario.cs @@ -55,7 +55,7 @@ public partial class Impresario : BaseQuester if (obj.IsInRightTheater()) { - SheetMusicOfferGump.DisplayTo(player); + player.SendGump(new SheetMusicOfferGump()); } else { @@ -69,60 +69,44 @@ public class SheetMusicOfferGump : BaseQuestGump { public override bool Singleton => true; - private SheetMusicOfferGump() : base(75, 25) + public SheetMusicOfferGump() : base(75, 25) { - } + Closable = false; - public static void DisplayTo(Mobile from) - { - if (from?.NetState == null) - { - return; - } + AddImage(349, 10, 0x24B0); + AddImageTiled(349, 130, 100, 120, 0x24B3); + AddImageTiled(149, 10, 200, 140, 0x24AF); + AddImageTiled(149, 300, 200, 140, 0x24B5); + AddImage(349, 300, 0x24B6); + AddImage(35, 10, 0x24AE); + AddImageTiled(35, 150, 120, 100, 0x24B1); + AddImage(35, 300, 0x24B4); - from.SendGump(new SheetMusicOfferGump()); - } + AddHtmlLocalized(110, 60, 200, 20, 1049069, White); // Conversation Event - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); + AddImage(65, 14, 0x2776); + AddImageTiled(81, 14, 349, 17, 0x2775); + AddImage(426, 14, 0x2778); - builder.AddPage(); + AddImageTiled(50, 37, 400, 376, 0xA40); + AddAlphaRegion(50, 37, 400, 376); - builder.AddImage(349, 10, 0x24B0); - builder.AddImageTiled(349, 130, 100, 120, 0x24B3); - builder.AddImageTiled(149, 10, 200, 140, 0x24AF); - builder.AddImageTiled(149, 300, 200, 140, 0x24B5); - builder.AddImage(349, 300, 0x24B6); - builder.AddImage(35, 10, 0x24AE); - builder.AddImageTiled(35, 150, 120, 100, 0x24B1); - builder.AddImage(35, 300, 0x24B4); + AddImage(0, 0, 0x28C8); - builder.AddHtmlLocalized(110, 60, 200, 20, 1049069, White); // Conversation Event - - builder.AddImage(65, 14, 0x2776); - builder.AddImageTiled(81, 14, 349, 17, 0x2775); - builder.AddImage(426, 14, 0x2778); - - builder.AddImageTiled(50, 37, 400, 376, 0xA40); - builder.AddAlphaRegion(50, 37, 400, 376); - - builder.AddImage(0, 0, 0x28C8); - - builder.AddImageTiled(75, 90, 200, 1, 0x238D); - builder.AddImage(75, 58, 0x2635); - builder.AddImage(380, 45, 0xDF); + AddImageTiled(75, 90, 200, 1, 0x238D); + AddImage(75, 58, 0x2635); + AddImage(380, 45, 0xDF); // Sure, I have some sheet music for a Gabriel Piete song. I'd be happy to sell you a copy for 10 gold. - builder.AddHtmlLocalized(98, 140, 312, 200, 1055107, LightGreen, false, true); + AddHtmlLocalized(98, 140, 312, 200, 1055107, LightGreen, false, true); - builder.AddRadio(85, 350, 0x25F8, 0x25FB, true, 1); - builder.AddHtmlLocalized(120, 356, 280, 20, 1014088, White); // I accept. + AddRadio(85, 350, 0x25F8, 0x25FB, true, 1); + AddHtmlLocalized(120, 356, 280, 20, 1014088, White); // I accept. - builder.AddRadio(85, 385, 0x25F8, 0x25FB, false, 0); - builder.AddHtmlLocalized(120, 391, 280, 20, 1049012, White); // No thanks, I decline. + AddRadio(85, 385, 0x25F8, 0x25FB, false, 0); + AddHtmlLocalized(120, 391, 280, 20, 1049012, White); // No thanks, I decline. - builder.AddButton(340, 390, 0xF7, 0xF8, 1); + AddButton(340, 390, 0xF7, 0xF8, 1); } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Engines/Quests/Collector/Objectives.cs b/Projects/UOContent/Engines/Quests/Collector/Objectives.cs index ab9ae2edd..5dc87dfb7 100644 --- a/Projects/UOContent/Engines/Quests/Collector/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Collector/Objectives.cs @@ -1,5 +1,4 @@ using System; -using Server.Gumps; namespace Server.Engines.Quests.Collector { @@ -9,20 +8,20 @@ namespace Server.Engines.Quests.Collector public override int MaxProgress => 6; - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { // Rainbow pearls collected: - BaseQuestGump.AddHtmlObject(ref builder, 70, 260, 270, 100, 1055085, BaseQuestGump.Blue, false, false); + gump.AddHtmlObject(70, 260, 270, 100, 1055085, BaseQuestGump.Blue, false, false); - builder.AddLabel(70, 280, 0x64, $"{CurProgress}"); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, $"{MaxProgress}"); + gump.AddLabel(70, 280, 0x64, CurProgress.ToString()); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, MaxProgress.ToString()); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } @@ -312,7 +311,7 @@ namespace Server.Engines.Quests.Collector return CaptureResponse.Invalid; } - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { @@ -320,10 +319,9 @@ namespace Server.Engines.Quests.Collector { var info = ImageTypeInfo.Get(m_Images[i]); - BaseQuestGump.AddHtmlObject(ref builder, 70, 260 + 20 * i, 200, 100, info.Name, BaseQuestGump.Blue, false, false); - builder.AddLabel(200, 260 + 20 * i, 0x64, " : "); - BaseQuestGump.AddHtmlObject( - ref builder, + gump.AddHtmlObject(70, 260 + 20 * i, 200, 100, info.Name, BaseQuestGump.Blue, false, false); + gump.AddLabel(200, 260 + 20 * i, 0x64, " : "); + gump.AddHtmlObject( 220, 260 + 20 * i, 100, @@ -337,7 +335,7 @@ namespace Server.Engines.Quests.Collector } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } diff --git a/Projects/UOContent/Engines/Quests/Core/Items/EnchantedSextant.cs b/Projects/UOContent/Engines/Quests/Core/Items/EnchantedSextant.cs index 99b0243cf..142cb42ab 100644 --- a/Projects/UOContent/Engines/Quests/Core/Items/EnchantedSextant.cs +++ b/Projects/UOContent/Engines/Quests/Core/Items/EnchantedSextant.cs @@ -73,11 +73,7 @@ public partial class EnchantedSextant : Item }; [Constructible] - public EnchantedSextant() : base(0x1058) - { - } - - public override double DefaultWeight => 2.0; + public EnchantedSextant() : base(0x1058) => Weight = 2.0; public override int LabelNumber => 1046226; // an enchanted sextant @@ -154,7 +150,7 @@ public partial class EnchantedSextant : Item } } - var moonMsg = moongateDistance switch + int moonMsg = moongateDistance switch { double.MaxValue => 1048021, // The sextant fails to find a Moongate nearby. > LongDistance => 1046449 + (int)from.GetDirectionTo(closestMoongate), // A moongate is * from here @@ -164,7 +160,7 @@ public partial class EnchantedSextant : Item from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, 0x482, 3, moonMsg); - var bankMsg = bankDistance switch + int bankMsg = bankDistance switch { double.MaxValue => 1048020, // The sextant fails to find a Bank nearby. > LongDistance => 1046462 + (int)from.GetDirectionTo(closestBank), // A town is * from here diff --git a/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs b/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs index b63196c8f..8bada24bd 100644 --- a/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs +++ b/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs @@ -27,11 +27,10 @@ public partial class HornOfRetreat : Item public HornOfRetreat() : base(0xFC4) { Hue = 0x482; + Weight = 1.0; _charges = 10; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1049117; // Horn of Retreat public virtual bool ValidateUse(Mobile from) => true; @@ -115,8 +114,6 @@ public partial class HornOfRetreatMoongate : Moongate public override int LabelNumber => 1049114; // Sanctuary Gate - public override bool SkipSerialization => true; - public override void BeginConfirmation(Mobile from) { EndConfirmation(from); @@ -134,4 +131,10 @@ public partial class HornOfRetreatMoongate : Moongate Delete(); } } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } diff --git a/Projects/UOContent/Engines/Quests/Core/QuestConversation.cs b/Projects/UOContent/Engines/Quests/Core/QuestConversation.cs index 1fae5467f..e78b2a1dc 100644 --- a/Projects/UOContent/Engines/Quests/Core/QuestConversation.cs +++ b/Projects/UOContent/Engines/Quests/Core/QuestConversation.cs @@ -58,91 +58,74 @@ namespace Server.Engines.Quests public class QuestConversationsGump : BaseQuestGump { - private readonly List _conversations; + private readonly List m_Conversations; - private QuestConversationsGump(List conversations) : base(30, 50) => - _conversations = conversations; - - public static void DisplayTo(Mobile from, QuestConversation conv) + public QuestConversationsGump(QuestConversation conv) : this(new List { conv }) { - if (from?.NetState == null || conv == null) - { - return; - } - - from.SendGump(new QuestConversationsGump(new List { conv })); } - public static void DisplayTo(Mobile from, List conversations) + public QuestConversationsGump(List conversations) : base(30, 50) { - if (from?.NetState == null || conversations == null || conversations.Count == 0) + m_Conversations = conversations; + + Closable = false; + + AddPage(0); + + AddImage(349, 10, 9392); + AddImageTiled(349, 130, 100, 120, 9395); + AddImageTiled(149, 10, 200, 140, 9391); + AddImageTiled(149, 250, 200, 140, 9397); + AddImage(349, 250, 9398); + AddImage(35, 10, 9390); + AddImageTiled(35, 150, 120, 100, 9393); + AddImage(35, 250, 9396); + + AddHtmlLocalized(110, 60, 200, 20, 1049069, White); // Conversation Event + + AddImage(65, 14, 10102); + AddImageTiled(81, 14, 349, 17, 10101); + AddImage(426, 14, 10104); + + AddImageTiled(55, 40, 388, 323, 2624); + AddAlphaRegion(55, 40, 388, 323); + + AddImageTiled(75, 90, 200, 1, 9101); + AddImage(75, 58, 9781); + AddImage(380, 45, 223); + + AddButton(220, 335, 2313, 2312, 1); + AddImage(0, 0, 10440); + + AddPage(1); + + for (var i = 0; i < conversations.Count; ++i) { - return; - } - - from.SendGump(new QuestConversationsGump(conversations)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); - - builder.AddPage(); - - builder.AddImage(349, 10, 9392); - builder.AddImageTiled(349, 130, 100, 120, 9395); - builder.AddImageTiled(149, 10, 200, 140, 9391); - builder.AddImageTiled(149, 250, 200, 140, 9397); - builder.AddImage(349, 250, 9398); - builder.AddImage(35, 10, 9390); - builder.AddImageTiled(35, 150, 120, 100, 9393); - builder.AddImage(35, 250, 9396); - - builder.AddHtmlLocalized(110, 60, 200, 20, 1049069, White); // Conversation Event - - builder.AddImage(65, 14, 10102); - builder.AddImageTiled(81, 14, 349, 17, 10101); - builder.AddImage(426, 14, 10104); - - builder.AddImageTiled(55, 40, 388, 323, 2624); - builder.AddAlphaRegion(55, 40, 388, 323); - - builder.AddImageTiled(75, 90, 200, 1, 9101); - builder.AddImage(75, 58, 9781); - builder.AddImage(380, 45, 223); - - builder.AddButton(220, 335, 2313, 2312, 1); - builder.AddImage(0, 0, 10440); - - builder.AddPage(1); - - for (var i = 0; i < _conversations.Count; ++i) - { - var conv = _conversations[_conversations.Count - 1 - i]; + var conv = conversations[conversations.Count - 1 - i]; if (i > 0) { - builder.AddButton(65, 366, 9909, 9911, 0, GumpButtonType.Page, 1 + i); - builder.AddHtmlLocalized(90, 367, 50, 20, 1043354, Black); // Previous + AddButton(65, 366, 9909, 9911, 0, GumpButtonType.Page, 1 + i); + AddHtmlLocalized(90, 367, 50, 20, 1043354, Black); // Previous - builder.AddPage(1 + i); + AddPage(1 + i); } - AddHtmlObject(ref builder, 70, 110, 365, 220, conv.Message, LightGreen, false, true); + AddHtmlObject(70, 110, 365, 220, conv.Message, LightGreen, false, true); if (i > 0) { - builder.AddButton(420, 366, 9903, 9905, 0, GumpButtonType.Page, i); - builder.AddHtmlLocalized(370, 367, 50, 20, 1043353, Black); // Next + AddButton(420, 366, 9903, 9905, 0, GumpButtonType.Page, i); + AddHtmlLocalized(370, 367, 50, 20, 1043353, Black); // Next } } } public override void OnResponse(NetState sender, in RelayInfo info) { - for (var i = _conversations.Count - 1; i >= 0; --i) + for (var i = m_Conversations.Count - 1; i >= 0; --i) { - var qc = _conversations[i]; + var qc = m_Conversations[i]; if (!qc.HasBeenRead) { diff --git a/Projects/UOContent/Engines/Quests/Core/QuestItemInfo.cs b/Projects/UOContent/Engines/Quests/Core/QuestItemInfo.cs index 78fbe25fc..d26d51d6c 100644 --- a/Projects/UOContent/Engines/Quests/Core/QuestItemInfo.cs +++ b/Projects/UOContent/Engines/Quests/Core/QuestItemInfo.cs @@ -1,5 +1,3 @@ -using Server.Gumps; - namespace Server.Engines.Quests { public class QuestItemInfo @@ -17,56 +15,42 @@ namespace Server.Engines.Quests public class QuestItemInfoGump : BaseQuestGump { - private readonly QuestItemInfo[] _info; - - private QuestItemInfoGump(QuestItemInfo[] info) : base(485, 75) => _info = info; - - public static void DisplayTo(Mobile from, QuestItemInfo[] info) + public QuestItemInfoGump(QuestItemInfo[] info) : base(485, 75) { - if (from?.NetState == null || info == null || info.Length == 0) + var height = 100 + info.Length * 75; + + AddPage(0); + + AddBackground(5, 10, 145, height, 5054); + + AddImageTiled(13, 20, 125, 10, 2624); + AddAlphaRegion(13, 20, 125, 10); + + AddImageTiled(13, height - 10, 128, 10, 2624); + AddAlphaRegion(13, height - 10, 128, 10); + + AddImageTiled(13, 20, 10, height - 30, 2624); + AddAlphaRegion(13, 20, 10, height - 30); + + AddImageTiled(131, 20, 10, height - 30, 2624); + AddAlphaRegion(131, 20, 10, height - 30); + + AddHtmlLocalized(67, 35, 120, 20, 1011233, White); // INFO + + AddImage(62, 52, 9157); + AddImage(72, 52, 9157); + AddImage(82, 52, 9157); + + AddButton(25, 31, 1209, 1210, 777); + + AddPage(1); + + for (var i = 0; i < info.Length; ++i) { - return; - } + var cur = info[i]; - from.SendGump(new QuestItemInfoGump(info)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var height = 100 + _info.Length * 75; - - builder.AddPage(); - - builder.AddBackground(5, 10, 145, height, 5054); - - builder.AddImageTiled(13, 20, 125, 10, 2624); - builder.AddAlphaRegion(13, 20, 125, 10); - - builder.AddImageTiled(13, height - 10, 128, 10, 2624); - builder.AddAlphaRegion(13, height - 10, 128, 10); - - builder.AddImageTiled(13, 20, 10, height - 30, 2624); - builder.AddAlphaRegion(13, 20, 10, height - 30); - - builder.AddImageTiled(131, 20, 10, height - 30, 2624); - builder.AddAlphaRegion(131, 20, 10, height - 30); - - builder.AddHtmlLocalized(67, 35, 120, 20, 1011233, White); // INFO - - builder.AddImage(62, 52, 9157); - builder.AddImage(72, 52, 9157); - builder.AddImage(82, 52, 9157); - - builder.AddButton(25, 31, 1209, 1210, 777); - - builder.AddPage(1); - - for (var i = 0; i < _info.Length; ++i) - { - var cur = _info[i]; - - AddHtmlObject(ref builder, 25, 65 + i * 75, 110, 20, cur.Name, 1153, false, false); - builder.AddItem(45, 85 + i * 75, cur.ItemID); + AddHtmlObject(25, 65 + i * 75, 110, 20, cur.Name, 1153, false, false); + AddItem(45, 85 + i * 75, cur.ItemID); } } } diff --git a/Projects/UOContent/Engines/Quests/Core/QuestObjective.cs b/Projects/UOContent/Engines/Quests/Core/QuestObjective.cs index dd90d9b0e..f5abbe49b 100644 --- a/Projects/UOContent/Engines/Quests/Core/QuestObjective.cs +++ b/Projects/UOContent/Engines/Quests/Core/QuestObjective.cs @@ -84,24 +84,14 @@ namespace Server.Engines.Quests CurProgress = MaxProgress; } - public virtual void RenderMessage(ref DynamicGumpBuilder builder) + public virtual void RenderMessage(BaseQuestGump gump) { - BaseQuestGump.AddHtmlObject(ref builder, 70, 130, 300, 100, Message, BaseQuestGump.Blue, false, false); + gump.AddHtmlObject(70, 130, 300, 100, Message, BaseQuestGump.Blue, false, false); } - public virtual void RenderProgress(ref DynamicGumpBuilder builder) + public virtual void RenderProgress(BaseQuestGump gump) { - BaseQuestGump.AddHtmlObject( - ref builder, - 70, - 260, - 270, - 100, - Completed ? 1049077 : 1049078, - BaseQuestGump.Blue, - false, - false - ); + gump.AddHtmlObject(70, 260, 270, 100, Completed ? 1049077 : 1049078, BaseQuestGump.Blue, false, false); } public virtual void CheckCompletionStatus() @@ -138,142 +128,116 @@ namespace Server.Engines.Quests public class QuestLogUpdatedGump : BaseQuestGump { - private readonly QuestSystem _system; + private readonly QuestSystem m_System; public override bool Singleton => true; - private QuestLogUpdatedGump(QuestSystem system) : base(3, 30) => _system = system; - - public static void DisplayTo(Mobile from, QuestSystem system) + public QuestLogUpdatedGump(QuestSystem system) : base(3, 30) { - if (from?.NetState == null || system == null) - { - return; - } + m_System = system; - from.SendGump(new QuestLogUpdatedGump(system)); - } + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddImage(20, 5, 1417); - builder.AddImage(20, 5, 1417); + AddHtmlLocalized(0, 78, 120, 40, 1049079, White); // Quest Log Updated - builder.AddHtmlLocalized(0, 78, 120, 40, 1049079, White); // Quest Log Updated + AddImageTiled(0, 78, 120, 40, 2624); + AddAlphaRegion(0, 78, 120, 40); - builder.AddImageTiled(0, 78, 120, 40, 2624); - builder.AddAlphaRegion(0, 78, 120, 40); - - builder.AddButton(30, 15, 5575, 5576, 1); + AddButton(30, 15, 5575, 5576, 1); } public override void OnResponse(NetState sender, in RelayInfo info) { if (info.ButtonID == 1) { - _system.ShowQuestLog(); + m_System.ShowQuestLog(); } } } public class QuestObjectivesGump : BaseQuestGump { - private readonly List _objectives; + private readonly List m_Objectives; - private QuestObjectivesGump(List objectives) : base(90, 50) => _objectives = objectives; - - public static void DisplayTo(Mobile from, QuestObjective obj) + public QuestObjectivesGump(QuestObjective obj) : this(new List { obj }) { - if (from?.NetState == null || obj == null) - { - return; - } - - from.SendGump(new QuestObjectivesGump(new List { obj })); } - public static void DisplayTo(Mobile from, List objectives) + public QuestObjectivesGump(List objectives) : base(90, 50) { - if (from?.NetState == null || objectives == null || objectives.Count == 0) + m_Objectives = objectives; + + Closable = false; + + AddPage(0); + + AddImage(0, 0, 3600); + AddImageTiled(0, 14, 15, 375, 3603); + AddImageTiled(380, 14, 14, 375, 3605); + AddImage(0, 376, 3606); + AddImageTiled(15, 376, 370, 16, 3607); + AddImageTiled(15, 0, 370, 16, 3601); + AddImage(380, 0, 3602); + AddImage(380, 376, 3608); + + AddImageTiled(15, 15, 365, 365, 2624); + AddAlphaRegion(15, 15, 365, 365); + + AddImage(20, 87, 1231); + AddImage(75, 62, 9307); + + AddHtmlLocalized(117, 35, 230, 20, 1046026, Blue); // Quest Log + + AddImage(77, 33, 9781); + AddImage(65, 110, 2104); + + AddHtmlLocalized(79, 106, 230, 20, 1049073, Blue); // Objective: + + AddImageTiled(68, 125, 120, 1, 9101); + AddImage(65, 240, 2104); + + AddHtmlLocalized(79, 237, 230, 20, 1049076, Blue); // Progress details: + + AddImageTiled(68, 255, 120, 1, 9101); + AddButton(175, 355, 2313, 2312, 1); + + AddImage(341, 15, 10450); + AddImage(341, 330, 10450); + AddImage(15, 330, 10450); + AddImage(15, 15, 10450); + + AddPage(1); + + for (var i = 0; i < objectives.Count; ++i) { - return; - } - - from.SendGump(new QuestObjectivesGump(objectives)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); - - builder.AddPage(); - - builder.AddImage(0, 0, 3600); - builder.AddImageTiled(0, 14, 15, 375, 3603); - builder.AddImageTiled(380, 14, 14, 375, 3605); - builder.AddImage(0, 376, 3606); - builder.AddImageTiled(15, 376, 370, 16, 3607); - builder.AddImageTiled(15, 0, 370, 16, 3601); - builder.AddImage(380, 0, 3602); - builder.AddImage(380, 376, 3608); - - builder.AddImageTiled(15, 15, 365, 365, 2624); - builder.AddAlphaRegion(15, 15, 365, 365); - - builder.AddImage(20, 87, 1231); - builder.AddImage(75, 62, 9307); - - builder.AddHtmlLocalized(117, 35, 230, 20, 1046026, Blue); // Quest Log - - builder.AddImage(77, 33, 9781); - builder.AddImage(65, 110, 2104); - - builder.AddHtmlLocalized(79, 106, 230, 20, 1049073, Blue); // Objective: - - builder.AddImageTiled(68, 125, 120, 1, 9101); - builder.AddImage(65, 240, 2104); - - builder.AddHtmlLocalized(79, 237, 230, 20, 1049076, Blue); // Progress details: - - builder.AddImageTiled(68, 255, 120, 1, 9101); - builder.AddButton(175, 355, 2313, 2312, 1); - - builder.AddImage(341, 15, 10450); - builder.AddImage(341, 330, 10450); - builder.AddImage(15, 330, 10450); - builder.AddImage(15, 15, 10450); - - builder.AddPage(1); - - for (var i = 0; i < _objectives.Count; ++i) - { - var obj = _objectives[_objectives.Count - 1 - i]; + var obj = objectives[objectives.Count - 1 - i]; if (i > 0) { - builder.AddButton(55, 346, 9909, 9911, 0, GumpButtonType.Page, 1 + i); - builder.AddHtmlLocalized(82, 347, 50, 20, 1043354, White); // Previous + AddButton(55, 346, 9909, 9911, 0, GumpButtonType.Page, 1 + i); + AddHtmlLocalized(82, 347, 50, 20, 1043354, White); // Previous - builder.AddPage(1 + i); + AddPage(1 + i); } - obj.RenderMessage(ref builder); - obj.RenderProgress(ref builder); + obj.RenderMessage(this); + obj.RenderProgress(this); if (i > 0) { - builder.AddButton(317, 346, 9903, 9905, 0, GumpButtonType.Page, i); - builder.AddHtmlLocalized(278, 347, 50, 20, 1043353, White); // Next + AddButton(317, 346, 9903, 9905, 0, GumpButtonType.Page, i); + AddHtmlLocalized(278, 347, 50, 20, 1043353, White); // Next } } } public override void OnResponse(NetState sender, in RelayInfo info) { - for (var i = _objectives.Count - 1; i >= 0; --i) + for (var i = m_Objectives.Count - 1; i >= 0; --i) { - var obj = _objectives[i]; + var obj = m_Objectives[i]; if (!obj.HasBeenRead) { diff --git a/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs b/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs index bd62e957c..2b0ace340 100644 --- a/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs +++ b/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs @@ -244,7 +244,7 @@ namespace Server.Engines.Quests public virtual void SendOffer() { - QuestOfferGump.DisplayTo(From, this); + From.SendGump(new QuestOfferGump(this)); } public virtual void GetContextMenuEntries(ref PooledRefList list) @@ -264,7 +264,7 @@ namespace Server.Engines.Quests public virtual void ShowQuestLogUpdated() { - QuestLogUpdatedGump.DisplayTo(From, this); + From.SendGump(new QuestLogUpdatedGump(this)); } public virtual void ShowQuestLog() @@ -277,13 +277,13 @@ namespace Server.Engines.Quests gumps.Close(); gumps.Close(); - QuestObjectivesGump.DisplayTo(From, Objectives); + gumps.Send(new QuestObjectivesGump(Objectives)); var last = Objectives[^1]; if (last.Info != null) { - QuestItemInfoGump.DisplayTo(From, last.Info); + gumps.Send(new QuestItemInfoGump(last.Info)); } } } @@ -298,20 +298,20 @@ namespace Server.Engines.Quests gumps.Close(); gumps.Close(); - QuestConversationsGump.DisplayTo(From, Conversations); + gumps.Send(new QuestConversationsGump(Conversations)); var last = Conversations[^1]; if (last.Info != null) { - QuestItemInfoGump.DisplayTo(From, last.Info); + From.SendGump(new QuestItemInfoGump(last.Info)); } } } public virtual void BeginCancelQuest() { - QuestCancelGump.DisplayTo(From, this); + From.SendGump(new QuestCancelGump(this)); } public virtual void EndCancelQuest(bool shouldCancel) @@ -394,19 +394,11 @@ namespace Server.Engines.Quests var gumps = From.GetGumps(); gumps.Close(); gumps.Close(); - - if (conv.Logged) - { - QuestConversationsGump.DisplayTo(From, Conversations); - } - else - { - QuestConversationsGump.DisplayTo(From, conv); - } + gumps.Send(conv.Logged ? new QuestConversationsGump(Conversations) : new QuestConversationsGump(conv)); if (conv.Info != null) { - QuestItemInfoGump.DisplayTo(From, conv.Info); + From.SendGump(new QuestItemInfoGump(conv.Info)); } } @@ -527,166 +519,142 @@ namespace Server.Engines.Quests public class QuestCancelGump : BaseQuestGump { - private readonly QuestSystem _system; + private readonly QuestSystem m_System; - public override bool Singleton => true; - - private QuestCancelGump(QuestSystem system) : base(120, 50) => _system = system; - - public static void DisplayTo(Mobile from, QuestSystem system) + public QuestCancelGump(QuestSystem system) : base(120, 50) { - if (from?.NetState == null || system == null) + m_System = system; + + Closable = false; + + AddPage(0); + + AddImageTiled(0, 0, 348, 262, 2702); + AddAlphaRegion(0, 0, 348, 262); + + AddImage(0, 15, 10152); + AddImageTiled(0, 30, 17, 200, 10151); + AddImage(0, 230, 10154); + + AddImage(15, 0, 10252); + AddImageTiled(30, 0, 300, 17, 10250); + AddImage(315, 0, 10254); + + AddImage(15, 244, 10252); + AddImageTiled(30, 244, 300, 17, 10250); + AddImage(315, 244, 10254); + + AddImage(330, 15, 10152); + AddImageTiled(330, 30, 17, 200, 10151); + AddImage(330, 230, 10154); + + AddImage(333, 2, 10006); + AddImage(333, 248, 10006); + AddImage(2, 248, 10006); + AddImage(2, 2, 10006); + + AddHtmlLocalized(25, 22, 200, 20, 1049000, 32000); // Confirm Quest Cancellation + AddImage(25, 40, 3007); + + if (system.IsTutorial) { - return; - } - - from.SendGump(new QuestCancelGump(system)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); - - builder.AddPage(); - - builder.AddImageTiled(0, 0, 348, 262, 2702); - builder.AddAlphaRegion(0, 0, 348, 262); - - builder.AddImage(0, 15, 10152); - builder.AddImageTiled(0, 30, 17, 200, 10151); - builder.AddImage(0, 230, 10154); - - builder.AddImage(15, 0, 10252); - builder.AddImageTiled(30, 0, 300, 17, 10250); - builder.AddImage(315, 0, 10254); - - builder.AddImage(15, 244, 10252); - builder.AddImageTiled(30, 244, 300, 17, 10250); - builder.AddImage(315, 244, 10254); - - builder.AddImage(330, 15, 10152); - builder.AddImageTiled(330, 30, 17, 200, 10151); - builder.AddImage(330, 230, 10154); - - builder.AddImage(333, 2, 10006); - builder.AddImage(333, 248, 10006); - builder.AddImage(2, 248, 10006); - builder.AddImage(2, 2, 10006); - - builder.AddHtmlLocalized(25, 22, 200, 20, 1049000, 32000); // Confirm Quest Cancellation - builder.AddImage(25, 40, 3007); - - if (_system.IsTutorial) - { - builder.AddHtmlLocalized( + AddHtmlLocalized( 25, 55, 300, 120, 1060836, - BaseQuestGump.White + White ); // This quest will give you valuable information, skills and equipment that will help you advance in the game at a quicker pace.

Are you certain you wish to cancel at this time? } else { - builder.AddHtmlLocalized(25, 60, 300, 20, 1049001, BaseQuestGump.White); // You have chosen to abort your quest: - builder.AddImage(25, 81, 0x25E7); - BaseQuestGump.AddHtmlObject(ref builder, 48, 80, 280, 20, _system.Name, BaseQuestGump.DarkGreen, false, false); + AddHtmlLocalized(25, 60, 300, 20, 1049001, White); // You have chosen to abort your quest: + AddImage(25, 81, 0x25E7); + AddHtmlObject(48, 80, 280, 20, system.Name, DarkGreen, false, false); - builder.AddHtmlLocalized(25, 120, 280, 20, 1049002, BaseQuestGump.White); // Can this quest be restarted after quitting? - builder.AddImage(25, 141, 0x25E7); - builder.AddHtmlLocalized( + AddHtmlLocalized(25, 120, 280, 20, 1049002, White); // Can this quest be restarted after quitting? + AddImage(25, 141, 0x25E7); + AddHtmlLocalized( 48, 140, 280, 20, - _system.RestartDelay < TimeSpan.MaxValue ? 1049016 : 1049017, - BaseQuestGump.DarkGreen + system.RestartDelay < TimeSpan.MaxValue ? 1049016 : 1049017, + DarkGreen ); // Yes/No } - builder.AddRadio(25, 175, 9720, 9723, true, 1); - builder.AddHtmlLocalized(60, 180, 280, 20, 1049005, BaseQuestGump.White); // Yes, I really want to quit! + AddRadio(25, 175, 9720, 9723, true, 1); + AddHtmlLocalized(60, 180, 280, 20, 1049005, White); // Yes, I really want to quit! - builder.AddRadio(25, 210, 9720, 9723, false, 0); - builder.AddHtmlLocalized(60, 215, 280, 20, 1049006, BaseQuestGump.White); // No, I don't want to quit. + AddRadio(25, 210, 9720, 9723, false, 0); + AddHtmlLocalized(60, 215, 280, 20, 1049006, White); // No, I don't want to quit. - builder.AddButton(265, 220, 247, 248, 1); + AddButton(265, 220, 247, 248, 1); } public override void OnResponse(NetState sender, in RelayInfo info) { if (info.ButtonID == 1) { - _system.EndCancelQuest(info.IsSwitched(1)); + m_System.EndCancelQuest(info.IsSwitched(1)); } } } public class QuestOfferGump : BaseQuestGump { - private readonly QuestSystem _system; + private readonly QuestSystem m_System; - public override bool Singleton => true; - - private QuestOfferGump(QuestSystem system) : base(75, 25) => _system = system; - - public static void DisplayTo(Mobile from, QuestSystem system) + public QuestOfferGump(QuestSystem system) : base(75, 25) { - if (from?.NetState == null || system == null) - { - return; - } + m_System = system; - from.SendGump(new QuestOfferGump(system)); - } + Closable = false; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); + AddPage(0); - builder.AddPage(); + AddImageTiled(50, 20, 400, 400, 2624); + AddAlphaRegion(50, 20, 400, 400); - builder.AddImageTiled(50, 20, 400, 400, 2624); - builder.AddAlphaRegion(50, 20, 400, 400); + AddImage(90, 33, 9005); + AddHtmlLocalized(130, 45, 270, 20, 1049010, White); // Quest Offer + AddImageTiled(130, 65, 175, 1, 9101); - builder.AddImage(90, 33, 9005); - builder.AddHtmlLocalized(130, 45, 270, 20, 1049010, BaseQuestGump.White); // Quest Offer - builder.AddImageTiled(130, 65, 175, 1, 9101); + AddImage(140, 110, 1209); + AddHtmlObject(160, 108, 250, 20, system.Name, DarkGreen, false, false); - builder.AddImage(140, 110, 1209); - BaseQuestGump.AddHtmlObject(ref builder, 160, 108, 250, 20, _system.Name, BaseQuestGump.DarkGreen, false, false); + AddHtmlObject(98, 140, 312, 200, system.OfferMessage, LightGreen, false, true); - BaseQuestGump.AddHtmlObject(ref builder, 98, 140, 312, 200, _system.OfferMessage, BaseQuestGump.LightGreen, false, true); + AddRadio(85, 350, 9720, 9723, true, 1); + AddHtmlLocalized(120, 356, 280, 20, 1049011, White); // I accept! - builder.AddRadio(85, 350, 9720, 9723, true, 1); - builder.AddHtmlLocalized(120, 356, 280, 20, 1049011, BaseQuestGump.White); // I accept! + AddRadio(85, 385, 9720, 9723, false, 0); + AddHtmlLocalized(120, 391, 280, 20, 1049012, White); // No thanks, I decline. - builder.AddRadio(85, 385, 9720, 9723, false, 0); - builder.AddHtmlLocalized(120, 391, 280, 20, 1049012, BaseQuestGump.White); // No thanks, I decline. + AddButton(340, 390, 247, 248, 1); - builder.AddButton(340, 390, 247, 248, 1); + AddImageTiled(50, 29, 30, 390, 10460); + AddImageTiled(34, 140, 17, 279, 9263); - builder.AddImageTiled(50, 29, 30, 390, 10460); - builder.AddImageTiled(34, 140, 17, 279, 9263); + AddImage(48, 135, 10411); + AddImage(-16, 285, 10402); + AddImage(0, 10, 10421); + AddImage(25, 0, 10420); - builder.AddImage(48, 135, 10411); - builder.AddImage(-16, 285, 10402); - builder.AddImage(0, 10, 10421); - builder.AddImage(25, 0, 10420); + AddImageTiled(83, 15, 350, 15, 10250); - builder.AddImageTiled(83, 15, 350, 15, 10250); + AddImage(34, 419, 10306); + AddImage(442, 419, 10304); + AddImageTiled(51, 419, 392, 17, 10101); - builder.AddImage(34, 419, 10306); - builder.AddImage(442, 419, 10304); - builder.AddImageTiled(51, 419, 392, 17, 10101); + AddImageTiled(415, 29, 44, 390, 2605); + AddImageTiled(415, 29, 30, 390, 10460); + AddImage(425, 0, 10441); - builder.AddImageTiled(415, 29, 44, 390, 2605); - builder.AddImageTiled(415, 29, 30, 390, 10460); - builder.AddImage(425, 0, 10441); - - builder.AddImage(370, 50, 1417); - builder.AddImage(379, 60, _system.Picture); + AddImage(370, 50, 1417); + AddImage(379, 60, system.Picture); } public override void OnResponse(NetState sender, in RelayInfo info) @@ -695,17 +663,17 @@ namespace Server.Engines.Quests { if (info.IsSwitched(1)) { - _system.Accept(); + m_System.Accept(); } else { - _system.Decline(); + m_System.Decline(); } } } } - public abstract class BaseQuestGump : DynamicGump + public abstract class BaseQuestGump : Gump { public const int Black = 0x0000; public const int White = 0x7FFF; @@ -713,29 +681,19 @@ namespace Server.Engines.Quests public const int LightGreen = 90000; public const int Blue = 19777215; - protected BaseQuestGump(int x, int y) : base(x, y) + public BaseQuestGump(int x, int y) : base(x, y) { } - public static void AddHtmlObject( - ref DynamicGumpBuilder builder, - int x, - int y, - int width, - int height, - object message, - int color, - bool back, - bool scroll - ) + public void AddHtmlObject(int x, int y, int width, int height, object message, int color, bool back, bool scroll) { if (message is int html) { - builder.AddHtmlLocalized(x, y, width, height, html, color.C16216(), back, scroll); + AddHtmlLocalized(x, y, width, height, html, color.C16216(), back, scroll); } else { - builder.AddHtml(x, y, width, height, Html.Color($"{message}", color.C16216()), background: back, scrollbar: scroll); + AddHtml(x, y, width, height, message.ToString().Color(color.C16216()), back, scroll); } } } diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScroll.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScroll.cs index 2b2fe9f3f..76b349706 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScroll.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScroll.cs @@ -12,9 +12,11 @@ public partial class KronusScroll : QuestItem private static readonly Map m_WellOfTearsMap = Map.Malas; [Constructible] - public KronusScroll() : base(0x227A) => Hue = 0x44E; - - public override double DefaultWeight => 1.0; + public KronusScroll() : base(0x227A) + { + Weight = 1.0; + Hue = 0x44E; + } public override int LabelNumber => 1060149; // Calling of Kronus diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScrollBox.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScrollBox.cs index 8451bc1b5..4cd34c687 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScrollBox.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScrollBox.cs @@ -33,7 +33,7 @@ public partial class KronusScrollBox : MetalBox if (obj?.Completed == false || DarkTidesQuest.HasLostCallingScroll(from)) { - var scroll = new KronusScroll(); + Item scroll = new KronusScroll(); if (pm.PlaceInBackpack(scroll)) { diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs index 5d92f5b9f..29bbf37e3 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs @@ -8,11 +8,7 @@ namespace Server.Engines.Quests.Necro; public partial class ScrollOfAbraxus : QuestItem { [Constructible] - public ScrollOfAbraxus() : base(0x227B) - { - } - - public override double DefaultWeight => 1.0; + public ScrollOfAbraxus() : base(0x227B) => Weight = 1.0; public override int LabelNumber => 1028827; // Scroll of Abraxus @@ -42,7 +38,7 @@ public partial class ScrollOfAbraxus : QuestItem { if (IsChildOf(from.Backpack)) { - ScrollOfAbraxusGump.DisplayTo(from); + from.SendGump(new ScrollOfAbraxusGump()); if (from is PlayerMobile pm) { @@ -66,30 +62,14 @@ public partial class ScrollOfAbraxus : QuestItem } } -public class ScrollOfAbraxusGump : StaticGump +public class ScrollOfAbraxusGump : Gump { - public override bool Singleton => true; - - private ScrollOfAbraxusGump() : base(150, 50) + public ScrollOfAbraxusGump() : base(150, 50) { - } + AddPage(0); - public static void DisplayTo(Mobile from) - { - if (from?.NetState == null) - { - return; - } - - from.SendGump(new ScrollOfAbraxusGump()); - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddImage(0, 0, 1228); - builder.AddImage(340, 255, 9005); + AddImage(0, 0, 1228); + AddImage(340, 255, 9005); /* Security at the Crystal Cave

* @@ -127,6 +107,6 @@ public class ScrollOfAbraxusGump : StaticGump * * - Frater Melkeer */ - builder.AddHtmlLocalized(25, 36, 350, 210, 1060116, 1, false, true); + AddHtmlLocalized(25, 36, 350, 210, 1060116, 1, false, true); } } diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/Mardoth.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/Mardoth.cs index 76eb59361..d5086fbd2 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/Mardoth.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/Mardoth.cs @@ -64,15 +64,13 @@ public partial class Mardoth : BaseQuester HairItemID = 0x203C; HairHue = 0x482; - AddItem(new BoneGloves - { - Hue = 0x66D - }); + Item gloves = new BoneGloves(); + gloves.Hue = 0x66D; + AddItem(gloves); - AddItem(new PlateGorget - { - Hue = 0x1 - }); + Item gorget = new PlateGorget(); + gorget.Hue = 0x1; + AddItem(gorget); } public override int GetAutoTalkRange(PlayerMobile m) => 3; diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs index 6199f3046..b0ac5d41c 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs @@ -86,7 +86,7 @@ public partial class SummonedPaladin : BaseCreature Timer.StartTimer(TimeSpan.FromSeconds(5.0), Delete); } - else if (_necromancer.Map != Map || this.GetDistanceToSqrt(_necromancer) > RangePerception + 1) + else if (_necromancer.Map != Map || GetDistanceToSqrt(_necromancer) > RangePerception + 1) { Effects.SendLocationParticles( EffectItem.Create(Location, Map, EffectItem.DefaultDuration), diff --git a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs index c8ac8f799..decb00f71 100644 --- a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs +++ b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs @@ -7,11 +7,7 @@ namespace Server.Engines.Quests.Ninja; public partial class EminosKatana : QuestItem { [Constructible] - public EminosKatana() : base(0x13FF) - { - } - - public override double DefaultWeight => 1.0; + public EminosKatana() : base(0x13FF) => Weight = 1.0; public override int LabelNumber => 1063214; // Daimyo Emino's Katana diff --git a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatanaChest.cs b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatanaChest.cs index b6df0126e..31d895834 100644 --- a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatanaChest.cs +++ b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatanaChest.cs @@ -49,7 +49,7 @@ public partial class EminosKatanaChest : WoodenChest { if (EminosUndertakingQuest.HasLostEminosKatana(from)) { - var katana = new EminosKatana(); + Item katana = new EminosKatana(); if (!player.PlaceInBackpack(katana)) { @@ -64,7 +64,7 @@ public partial class EminosKatanaChest : WoodenChest if (obj?.Completed == false) { - var katana = new EminosKatana(); + Item katana = new EminosKatana(); if (player.PlaceInBackpack(katana)) { diff --git a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs index 6f21e2f76..f40372617 100644 --- a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs +++ b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs @@ -7,9 +7,11 @@ namespace Server.Engines.Quests.Ninja; public partial class NoteForZoel : QuestItem { [Constructible] - public NoteForZoel() : base(0x14EF) => Hue = 0x6B9; - - public override double DefaultWeight => 1.0; + public NoteForZoel() : base(0x14EF) + { + Weight = 1.0; + Hue = 0x6B9; + } public override int LabelNumber => 1063186; // A Note for Zoel diff --git a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Mobiles/Emino.cs b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Mobiles/Emino.cs index bb4e5d464..2e4826d87 100644 --- a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Mobiles/Emino.cs +++ b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Mobiles/Emino.cs @@ -58,7 +58,7 @@ public partial class Emino : BaseQuester if (EminosUndertakingQuest.HasLostNoteForZoel(player)) { - var note = new NoteForZoel(); + Item note = new NoteForZoel(); if (player.PlaceInBackpack(note)) { diff --git a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Objectives.cs b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Objectives.cs index edb919f94..184c89546 100644 --- a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Objectives.cs @@ -1,4 +1,3 @@ -using Server.Gumps; using Server.Items; using Server.Mobiles; @@ -188,19 +187,19 @@ namespace Server.Engines.Quests.Ninja public override int MaxProgress => 3; - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { // Henchmen killed: - builder.AddHtmlLocalized(70, 260, 270, 100, 1063207, BaseQuestGump.Blue); - builder.AddLabel(70, 280, 0x64, $"{CurProgress}"); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, $"{MaxProgress}"); + gump.AddHtmlLocalized(70, 260, 270, 100, 1063207, BaseQuestGump.Blue); + gump.AddLabel(70, 280, 0x64, CurProgress.ToString()); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, MaxProgress.ToString()); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs index db023a3da..900e6e0a5 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs @@ -7,11 +7,7 @@ namespace Server.Engines.Quests.Samurai; public partial class HaochisKatana : QuestItem { [Constructible] - public HaochisKatana() : base(0x13FF) - { - } - - public override double DefaultWeight => 1.0; + public HaochisKatana() : base(0x13FF) => Weight = 1.0; public override int LabelNumber => 1063165; // Daimyo Haochi's Katana diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatanaGenerator.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatanaGenerator.cs index 5ac52c164..991c8ff3a 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatanaGenerator.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatanaGenerator.cs @@ -31,7 +31,7 @@ public partial class HaochisKatanaGenerator : Item if (HaochisTrialsQuest.HasLostHaochisKatana(player)) { - var katana = new HaochisKatana(); + Item katana = new HaochisKatana(); if (!player.PlaceInBackpack(katana)) { @@ -46,7 +46,7 @@ public partial class HaochisKatanaGenerator : Item if (obj?.Completed == false) { - var katana = new HaochisKatana(); + Item katana = new HaochisKatana(); if (player.PlaceInBackpack(katana)) { diff --git a/Projects/UOContent/Engines/Quests/Regions/QuestOfferRegion.cs b/Projects/UOContent/Engines/Quests/Regions/QuestOfferRegion.cs index b9707bfd8..6cd9ea3aa 100644 --- a/Projects/UOContent/Engines/Quests/Regions/QuestOfferRegion.cs +++ b/Projects/UOContent/Engines/Quests/Regions/QuestOfferRegion.cs @@ -32,7 +32,7 @@ public class QuestOfferRegion : BaseRegion { try { - var qs = Quest.CreateInstance(player); + QuestSystem qs = Quest.CreateInstance(player); qs.SendOffer(); } catch (Exception ex) diff --git a/Projects/UOContent/Engines/Quests/Solen Matriarch/Objectives.cs b/Projects/UOContent/Engines/Quests/Solen Matriarch/Objectives.cs index 76bb2b7e3..224a8f233 100644 --- a/Projects/UOContent/Engines/Quests/Solen Matriarch/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Solen Matriarch/Objectives.cs @@ -1,4 +1,3 @@ -using Server.Gumps; using Server.Items; using Server.Mobiles; @@ -10,12 +9,12 @@ namespace Server.Engines.Quests.Matriarch public override int MaxProgress => 7; - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { // Black/Red Solen Infiltrators killed: - builder.AddHtmlLocalized( + gump.AddHtmlLocalized( 70, 260, 270, @@ -23,13 +22,13 @@ namespace Server.Engines.Quests.Matriarch ((SolenMatriarchQuest)System).RedSolen ? 1054088 : 1054087, BaseQuestGump.Blue ); - builder.AddLabel(70, 280, 0x64, $"{CurProgress}"); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, $"{MaxProgress}"); + gump.AddLabel(70, 280, 0x64, CurProgress.ToString()); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, MaxProgress.ToString()); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } @@ -92,18 +91,18 @@ namespace Server.Engines.Quests.Matriarch public override int MaxProgress => 40; - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { - builder.AddHtmlLocalized(70, 260, 270, 100, 1054093, BaseQuestGump.Blue); // Gallons of Water gathered: - builder.AddLabel(70, 280, 0x64, (CurProgress / 5).ToString()); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, (MaxProgress / 5).ToString()); + gump.AddHtmlLocalized(70, 260, 270, 100, 1054093, BaseQuestGump.Blue); // Gallons of Water gathered: + gump.AddLabel(70, 280, 0x64, (CurProgress / 5).ToString()); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, (MaxProgress / 5).ToString()); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } diff --git a/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs b/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs index 19468c67f..2a14b34db 100644 --- a/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs +++ b/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs @@ -40,7 +40,7 @@ namespace Server.Engines.Quests.Naturalist { get { - var count = 0; + int count = 0; foreach (var area in m_Areas) { if (!area.Special) diff --git a/Projects/UOContent/Engines/Quests/Study of the Solen Hive/Objectives.cs b/Projects/UOContent/Engines/Quests/Study of the Solen Hive/Objectives.cs index cc432ed1b..7dbb8b6f9 100644 --- a/Projects/UOContent/Engines/Quests/Study of the Solen Hive/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Study of the Solen Hive/Objectives.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Server.Gumps; namespace Server.Engines.Quests.Naturalist { @@ -118,18 +117,18 @@ namespace Server.Engines.Quests.Naturalist } } - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { - builder.AddHtmlLocalized(70, 260, 270, 100, 1054055, BaseQuestGump.Blue); // Solen Nests Studied : - builder.AddLabel(70, 280, 0x64, $"{CurProgress}"); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, $"{MaxProgress}"); + gump.AddHtmlLocalized(70, 260, 270, 100, 1054055, BaseQuestGump.Blue); // Solen Nests Studied : + gump.AddLabel(70, 280, 0x64, CurProgress.ToString()); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, MaxProgress.ToString()); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } @@ -177,12 +176,14 @@ namespace Server.Engines.Quests.Naturalist { public override int Message => 1054048; - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { - builder.AddHtmlLocalized(70, 260, 270, 100, 1054055, BaseQuestGump.Blue); // Solen Nests Studied : - builder.AddLabel(70, 280, 0x64, $"{NestArea.NonSpecialCount}"); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, $"{NestArea.NonSpecialCount}"); + var count = NestArea.NonSpecialCount.ToString(); + + gump.AddHtmlLocalized(70, 260, 270, 100, 1054055, BaseQuestGump.Blue); // Solen Nests Studied : + gump.AddLabel(70, 280, 0x64, count); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, count); } } } diff --git a/Projects/UOContent/Engines/Quests/Terrible Hatchlings/Objectives.cs b/Projects/UOContent/Engines/Quests/Terrible Hatchlings/Objectives.cs index 379fe24f3..53e56da91 100644 --- a/Projects/UOContent/Engines/Quests/Terrible Hatchlings/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Terrible Hatchlings/Objectives.cs @@ -1,4 +1,3 @@ -using Server.Gumps; using Server.Items; using Server.Mobiles; @@ -8,20 +7,20 @@ namespace Server.Engines.Quests.Zento { public override int Message => 1063316; - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { // Deathwatch Beetle Hatchlings killed: - builder.AddHtmlLocalized(70, 260, 270, 100, 1063318, 0xC6BF); + gump.AddHtmlLocalized(70, 260, 270, 100, 1063318, 0xC6BF); - builder.AddLabel(70, 280, 0x64, "0"); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, "10"); + gump.AddLabel(70, 280, 0x64, "0"); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, "10"); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } @@ -43,20 +42,20 @@ namespace Server.Engines.Quests.Zento { public override int Message => 1063320; - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { // Deathwatch Beetle Hatchlings killed: - builder.AddHtmlLocalized(70, 260, 270, 100, 1063318, 0xC6BF); + gump.AddHtmlLocalized(70, 260, 270, 100, 1063318, 0xC6BF); - builder.AddLabel(70, 280, 0x64, "1"); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, "10"); + gump.AddLabel(70, 280, 0x64, "1"); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, "10"); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } @@ -91,20 +90,20 @@ namespace Server.Engines.Quests.Zento public override int MaxProgress => 10; - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { // Deathwatch Beetle Hatchlings killed: - builder.AddHtmlLocalized(70, 260, 270, 100, 1063318, 0xC6BF); + gump.AddHtmlLocalized(70, 260, 270, 100, 1063318, 0xC6BF); - builder.AddLabel(70, 280, 0x64, $"{CurProgress}"); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, "10"); + gump.AddLabel(70, 280, 0x64, CurProgress.ToString()); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, "10"); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } diff --git a/Projects/UOContent/Engines/Quests/The Summoning/Items/GoldenSkull.cs b/Projects/UOContent/Engines/Quests/The Summoning/Items/GoldenSkull.cs index 1a899f766..0fff2f1b1 100644 --- a/Projects/UOContent/Engines/Quests/The Summoning/Items/GoldenSkull.cs +++ b/Projects/UOContent/Engines/Quests/The Summoning/Items/GoldenSkull.cs @@ -8,11 +8,10 @@ public partial class GoldenSkull : Item [Constructible] public GoldenSkull() : base(Utility.Random(0x1AE2, 3)) { + Weight = 1.0; Hue = 0x8A5; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1061619; // a golden skull } diff --git a/Projects/UOContent/Engines/Quests/The Summoning/Items/GrandGrimoire.cs b/Projects/UOContent/Engines/Quests/The Summoning/Items/GrandGrimoire.cs index c7ac9bc3b..b646070d5 100644 --- a/Projects/UOContent/Engines/Quests/The Summoning/Items/GrandGrimoire.cs +++ b/Projects/UOContent/Engines/Quests/The Summoning/Items/GrandGrimoire.cs @@ -8,12 +8,11 @@ public partial class GrandGrimoire : Item [Constructible] public GrandGrimoire() : base(0xEFA) { + Weight = 1.0; Hue = 0x835; Layer = Layer.OneHanded; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1060801; // The Grand Grimoire } diff --git a/Projects/UOContent/Engines/Quests/The Summoning/Items/SummoningAltar.cs b/Projects/UOContent/Engines/Quests/The Summoning/Items/SummoningAltar.cs index 4e3e5e237..d5ec6f24a 100644 --- a/Projects/UOContent/Engines/Quests/The Summoning/Items/SummoningAltar.cs +++ b/Projects/UOContent/Engines/Quests/The Summoning/Items/SummoningAltar.cs @@ -12,12 +12,16 @@ public partial class SummoningAltar : AbbatoirAddon { } - [SerializableField(0, fieldChanged: nameof(OnDaemonChanged))] - private BoneDemon _daemon; - - private void OnDaemonChanged(BoneDemon oldValue, BoneDemon newValue) + [SerializableProperty(0)] + public BoneDemon Daemon { - CheckDaemon(); + get => _daemon; + set + { + _daemon = value; + CheckDaemon(); + this.MarkDirty(); + } } public void CheckDaemon() diff --git a/Projects/UOContent/Engines/Quests/The Summoning/Mobiles/Chyloth.cs b/Projects/UOContent/Engines/Quests/The Summoning/Mobiles/Chyloth.cs index b70cbe9a8..9a333909f 100644 --- a/Projects/UOContent/Engines/Quests/The Summoning/Mobiles/Chyloth.cs +++ b/Projects/UOContent/Engines/Quests/The Summoning/Mobiles/Chyloth.cs @@ -220,7 +220,7 @@ public partial class Chyloth : BaseQuester AngryAt = null; } - member.SendGump(new ChylothPartyGump(from)); + member.SendGump(new ChylothPartyGump(from, member)); } } @@ -244,89 +244,82 @@ public partial class Chyloth : BaseQuester } } -public class ChylothPartyGump : StaticGump +public class ChylothPartyGump : Gump { private readonly Mobile _leader; + private readonly Mobile _member; - public override bool Singleton => true; - - public ChylothPartyGump(Mobile leader) : base(150, 50) => _leader = leader; - - protected override void BuildLayout(ref StaticGumpBuilder builder) + public ChylothPartyGump(Mobile leader, Mobile member) : base(150, 50) { - builder.SetNoClose(); + _leader = leader; + _member = member; - builder.AddPage(); + Closable = false; - builder.AddImage(0, 0, 3600); + AddPage(0); - builder.AddImageTiled(0, 14, 15, 200, 3603); - builder.AddImageTiled(380, 14, 14, 200, 3605); - builder.AddImage(0, 201, 3606); - builder.AddImageTiled(15, 201, 370, 16, 3607); - builder.AddImageTiled(15, 0, 370, 16, 3601); - builder.AddImage(380, 0, 3602); - builder.AddImage(380, 201, 3608); - builder.AddImageTiled(15, 15, 365, 190, 2624); + AddImage(0, 0, 3600); - builder.AddRadio(30, 140, 9727, 9730, true, 1); - builder.AddHtmlLocalized(65, 145, 300, 25, 1050050, 0x7FFF); // Yes, let's go! + AddImageTiled(0, 14, 15, 200, 3603); + AddImageTiled(380, 14, 14, 200, 3605); + AddImage(0, 201, 3606); + AddImageTiled(15, 201, 370, 16, 3607); + AddImageTiled(15, 0, 370, 16, 3601); + AddImage(380, 0, 3602); + AddImage(380, 201, 3608); + AddImageTiled(15, 15, 365, 190, 2624); - builder.AddRadio(30, 175, 9727, 9730, false, 0); - builder.AddHtmlLocalized(65, 178, 300, 25, 1050049, 0x7FFF); // No thanks, I'd rather stay here. + AddRadio(30, 140, 9727, 9730, true, 1); + AddHtmlLocalized(65, 145, 300, 25, 1050050, 0x7FFF); // Yes, let's go! + + AddRadio(30, 175, 9727, 9730, false, 0); + AddHtmlLocalized(65, 178, 300, 25, 1050049, 0x7FFF); // No thanks, I'd rather stay here. // Another player has paid Chyloth for your passage across lake Mortis: - builder.AddHtmlLocalized(30, 20, 360, 35, 1050047, 0x7FFF); + AddHtmlLocalized(30, 20, 360, 35, 1050047, 0x7FFF); - builder.AddHtmlLocalized(30, 105, 345, 40, 1050048, 0x5B2D); // Do you wish to accept their invitation at this time? + AddHtmlLocalized(30, 105, 345, 40, 1050048, 0x5B2D); // Do you wish to accept their invitation at this time? - builder.AddImage(65, 72, 5605); + AddImage(65, 72, 5605); - builder.AddImageTiled(80, 90, 200, 1, 9107); - builder.AddImageTiled(95, 92, 200, 1, 9157); + AddImageTiled(80, 90, 200, 1, 9107); + AddImageTiled(95, 92, 200, 1, 9157); - builder.AddLabelPlaceholder(90, 70, 1645, "leader"); + AddLabel(90, 70, 1645, leader.Name); - builder.AddButton(290, 175, 247, 248, 2); + AddButton(290, 175, 247, 248, 2); - builder.AddImageTiled(15, 14, 365, 1, 9107); - builder.AddImageTiled(380, 14, 1, 190, 9105); - builder.AddImageTiled(15, 205, 365, 1, 9107); - builder.AddImageTiled(15, 14, 1, 190, 9105); - builder.AddImageTiled(0, 0, 395, 1, 9157); - builder.AddImageTiled(394, 0, 1, 217, 9155); - builder.AddImageTiled(0, 216, 395, 1, 9157); - builder.AddImageTiled(0, 0, 1, 217, 9155); - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - builder.SetStringSlot("leader", _leader.Name); + AddImageTiled(15, 14, 365, 1, 9107); + AddImageTiled(380, 14, 1, 190, 9105); + AddImageTiled(15, 205, 365, 1, 9107); + AddImageTiled(15, 14, 1, 190, 9105); + AddImageTiled(0, 0, 395, 1, 9157); + AddImageTiled(394, 0, 1, 217, 9155); + AddImageTiled(0, 216, 395, 1, 9157); + AddImageTiled(0, 0, 1, 217, 9155); } public override void OnResponse(NetState sender, in RelayInfo info) { - var member = sender.Mobile; - if (info.ButtonID == 2 && info.IsSwitched(1)) { - if (member.Region.IsPartOf("Doom")) + if (_member.Region.IsPartOf("Doom")) { // ~1_NAME~ has accepted your invitation to cross lake Mortis. - _leader.SendLocalizedMessage(1050054, member.Name); + _leader.SendLocalizedMessage(1050054, _member.Name); - Chyloth.TeleportToFerry(member); + Chyloth.TeleportToFerry(_member); } else { - member.SendLocalizedMessage(1050051); // The invitation has been revoked. + _member.SendLocalizedMessage(1050051); // The invitation has been revoked. } } else { - member.SendLocalizedMessage(1050052); // You have declined their invitation. + _member.SendLocalizedMessage(1050052); // You have declined their invitation. // ~1_NAME~ has declined your invitation to cross lake Mortis. - _leader.SendLocalizedMessage(1050053, member.Name); + _leader.SendLocalizedMessage(1050053, _member.Name); } } } diff --git a/Projects/UOContent/Engines/Quests/The Summoning/Objectives.cs b/Projects/UOContent/Engines/Quests/The Summoning/Objectives.cs index 7be17228b..879d95921 100644 --- a/Projects/UOContent/Engines/Quests/The Summoning/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/The Summoning/Objectives.cs @@ -1,4 +1,3 @@ -using Server.Gumps; using Server.Items; using Server.Mobiles; @@ -43,33 +42,33 @@ namespace Server.Engines.Quests.Doom } } - public override void RenderMessage(ref DynamicGumpBuilder builder) + public override void RenderMessage(BaseQuestGump gump) { if (CurProgress > 0 && CurProgress < MaxProgress) { // Victoria has accepted the Daemon bones, but the requirement is not yet met. - BaseQuestGump.AddHtmlObject(ref builder, 70, 130, 300, 100, 1050028, BaseQuestGump.Blue, false, false); + gump.AddHtmlObject(70, 130, 300, 100, 1050028, BaseQuestGump.Blue, false, false); } else { - base.RenderMessage(ref builder); + base.RenderMessage(gump); } } - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (CurProgress > 0 && CurProgress < MaxProgress) { // Number of bones collected: - BaseQuestGump.AddHtmlObject(ref builder, 70, 260, 270, 100, 1050019, BaseQuestGump.Blue, false, false); + gump.AddHtmlObject(70, 260, 270, 100, 1050019, BaseQuestGump.Blue, false, false); - builder.AddLabel(70, 280, 100, $"{CurProgress}"); - builder.AddLabel(100, 280, 100, "/"); - builder.AddLabel(130, 280, 100, $"{MaxProgress}"); + gump.AddLabel(70, 280, 100, CurProgress.ToString()); + gump.AddLabel(100, 280, 100, "/"); + gump.AddLabel(130, 280, 100, MaxProgress.ToString()); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } } diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/DaemonBloodChest.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/DaemonBloodChest.cs index a9abd82de..256cdcb80 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/DaemonBloodChest.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/DaemonBloodChest.cs @@ -34,7 +34,7 @@ public partial class DaemonBloodChest : MetalChest return; } - var vial = new QuestDaemonBlood(); + Item vial = new QuestDaemonBlood(); if (player.PlaceInBackpack(vial)) { diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs index a65a50073..0e4b6c729 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs @@ -7,11 +7,7 @@ namespace Server.Engines.Quests.Haven; public partial class QuestDaemonBlood : QuestItem { [Constructible] - public QuestDaemonBlood() : base(0xF7D) - { - } - - public override double DefaultWeight => 1.0; + public QuestDaemonBlood() : base(0xF7D) => Weight = 1.0; public override bool CanDrop(PlayerMobile player) => player.Quest is not UzeraanTurmoilQuest; } diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs index 9da029d1c..72fdff2f7 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs @@ -7,11 +7,7 @@ namespace Server.Engines.Quests.Haven; public partial class QuestDaemonBone : QuestItem { [Constructible] - public QuestDaemonBone() : base(0xF80) - { - } - - public override double DefaultWeight => 1.0; + public QuestDaemonBone() : base(0xF80) => Weight = 1.0; public override bool CanDrop(PlayerMobile player) => player.Quest is not UzeraanTurmoilQuest; } diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs index c51e6a9dc..e29e91f82 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs @@ -7,11 +7,7 @@ namespace Server.Engines.Quests.Haven; public partial class QuestFertileDirt : QuestItem { [Constructible] - public QuestFertileDirt() : base(0xF81) - { - } - - public override double DefaultWeight => 1.0; + public QuestFertileDirt() : base(0xF81) => Weight = 1.0; public override bool CanDrop(PlayerMobile player) => player.Quest is not UzeraanTurmoilQuest; } diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs index a81c3dddd..33793a202 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs @@ -10,18 +10,13 @@ namespace Server.Engines.Quests.Haven; [SerializationGenerator(0, false)] public partial class SchmendrickApprenticeCorpse : Corpse { + private static int _hairHue; + [SerializableField(0, setter: "private")] private Lantern _lantern; [Constructible] - public SchmendrickApprenticeCorpse() : base( - GetOwner(), - Race.Human.RandomHair(false), - Race.Human.RandomHairHue(), - Race.Human.RandomFacialHair(false), - Race.Human.RandomHairHue(), - GetEquipment() - ) + public SchmendrickApprenticeCorpse() : base(GetOwner(), GetHair(), GetFacialHair(), GetEquipment()) { Direction = Direction.West; @@ -32,8 +27,6 @@ public partial class SchmendrickApprenticeCorpse : Corpse _lantern = new Lantern { Movable = false, Protected = true }; _lantern.Ignite(); - - Owner = null; } private static Mobile GetOwner() @@ -58,6 +51,19 @@ public partial class SchmendrickApprenticeCorpse : Corpse new Spellbook() ]; + private static VirtualHairInfo GetHair() + { + _hairHue = Race.Human.RandomHairHue(); + return new VirtualHairInfo(Race.Human.RandomHair(false), _hairHue); + } + + private static VirtualHairInfo GetFacialHair() + { + _hairHue = Race.Human.RandomHairHue(); + + return new VirtualHairInfo(Race.Human.RandomFacialHair(false), _hairHue); + } + public override void AddNameProperty(IPropertyList list) { if (ItemID == 0x2006) // Corpse form @@ -146,7 +152,5 @@ public partial class SchmendrickApprenticeCorpse : Corpse { _lantern.Delete(); } - - Owner?.Delete(); } } diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs index 87eb5c8af..ba69b74b5 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs @@ -6,9 +6,11 @@ namespace Server.Engines.Quests.Haven; [SerializationGenerator(0, false)] public partial class SchmendrickScrollOfPower : QuestItem { - public SchmendrickScrollOfPower() : base(0xE34) => Hue = 0x34D; - - public override double DefaultWeight => 1.0; + public SchmendrickScrollOfPower() : base(0xE34) + { + Weight = 1.0; + Hue = 0x34D; + } public override int LabelNumber => 1049118; // a scroll with ancient markings diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/Dryad.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/Dryad.cs index 8831e9a9a..108687c8b 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/Dryad.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/Dryad.cs @@ -78,7 +78,7 @@ public partial class Dryad : BaseQuester { FocusTo(player); - var fertileDirt = new QuestFertileDirt(); + Item fertileDirt = new QuestFertileDirt(); if (!player.PlaceInBackpack(fertileDirt)) { @@ -111,7 +111,7 @@ public partial class Dryad : BaseQuester FocusTo(from); - var fertileDirt = new QuestFertileDirt(); + Item fertileDirt = new QuestFertileDirt(); if (!player.PlaceInBackpack(fertileDirt)) { diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs index a95eaf7f5..af38fbb09 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs @@ -84,14 +84,10 @@ public partial class MilitiaFighter : BaseCreature [SerializationGenerator(0, false)] public partial class MilitiaFighterCorpse : Corpse { - public MilitiaFighterCorpse( - Mobile owner, int hairItemId, int hairHue, int facialHairItemId, int facialHairHue, List equipItems - ) : base( + public MilitiaFighterCorpse(Mobile owner, VirtualHairInfo hair, VirtualHairInfo facialhair, List equipItems) : base( owner, - hairItemId, - hairHue, - facialHairItemId, - facialHairHue, + hair, + facialhair, equipItems ) { diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Objectives.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Objectives.cs index 8bcace21f..e69e4c14a 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Objectives.cs @@ -1,5 +1,4 @@ using Server.Engines.Help; -using Server.Gumps; using Server.Items; using Server.Mobiles; @@ -139,16 +138,16 @@ namespace Server.Engines.Quests.Haven } } - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { - BaseQuestGump.AddHtmlObject(ref builder, 70, 260, 270, 100, 1049090, BaseQuestGump.Blue, false, false); // Horde Minions killed: - builder.AddLabel(70, 280, 0x64, $"{CurProgress}"); + gump.AddHtmlObject(70, 260, 270, 100, 1049090, BaseQuestGump.Blue, false, false); // Horde Minions killed: + gump.AddLabel(70, 280, 0x64, CurProgress.ToString()); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } @@ -329,7 +328,7 @@ namespace Server.Engines.Quests.Haven 1049330 ); // You have been ambushed! Fight for your honor!!! - var creature = new HordeMinion(); + BaseCreature creature = new HordeMinion(); creature.MoveToWorld(new Point3D(x, y, z), Map.Trammel); creature.Combatant = player; } diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/Cauldron.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/Cauldron.cs index 2658b7070..443ab6a69 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/Cauldron.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/Cauldron.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class Cauldron : Item { [Constructible] - public Cauldron() : base(0x9ED) - { - } - - public override double DefaultWeight => 1.0; + public Cauldron() : base(0x9ED) => Weight = 1.0; public override string DefaultName => "a cauldron"; } diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs index edb838167..633607aa8 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs @@ -13,13 +13,12 @@ public partial class HangoverCure : Item [Constructible] public HangoverCure() : base(0xE2B) { + Weight = 1.0; Hue = 0x2D; _uses = 20; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1055060; // Grizelda's Extra Strength Hangover Cure public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs index 4040cb35c..0a72e067d 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs @@ -6,11 +6,7 @@ namespace Server.Engines.Quests.Hag; public partial class MoonfireBrew : Item { [Constructible] - public MoonfireBrew() : base(0xF04) - { - } - - public override double DefaultWeight => 1.0; + public MoonfireBrew() : base(0xF04) => Weight = 1.0; public override int LabelNumber => 1055065; // a bottle of magical moonfire brew } diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Mobiles/Zeefzorpul.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Mobiles/Zeefzorpul.cs index 33a3fc42f..b1db0e4e1 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Mobiles/Zeefzorpul.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Mobiles/Zeefzorpul.cs @@ -12,8 +12,6 @@ public partial class Zeefzorpul : BaseQuester public override string DefaultName => "Zeefzorpul"; - public override bool SkipSerialization => true; - public override void InitBody() { Body = 0x4A; @@ -24,4 +22,10 @@ public partial class Zeefzorpul : BaseQuester public override void OnTalk(PlayerMobile player, bool contextMenu) { } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs index 1421c5119..723a129f0 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Objectives.cs @@ -1,5 +1,4 @@ using System; -using Server.Gumps; using Server.Items; using Server.Mobiles; @@ -53,7 +52,7 @@ namespace Server.Engines.Quests.Hag Effects.SendLocationEffect(m_CorpseLocation, map, 0x3728, 10); Effects.PlaySound(m_CorpseLocation, map, 0x1FE); - var imp = new Zeefzorpul(); + Mobile imp = new Zeefzorpul(); imp.MoveToWorld(m_CorpseLocation, map); // * You see a strange imp stealing a scrap of paper from the bloodied corpse * @@ -217,7 +216,7 @@ namespace Server.Engines.Quests.Hag Effects.SendLocationEffect(ImpLocation, map, 0x3728, 10); Effects.PlaySound(ImpLocation, map, 0x1FE); - var imp = new Zeefzorpul(); + Mobile imp = new Zeefzorpul(); imp.MoveToWorld(ImpLocation, map); imp.Direction = imp.GetDirectionTo(from); @@ -344,20 +343,20 @@ namespace Server.Engines.Quests.Hag public int Step => Ingredients.Length; public bool BlackheartMet { get; private set; } - public override void RenderProgress(ref DynamicGumpBuilder builder) + public override void RenderProgress(BaseQuestGump gump) { if (!Completed) { var info = IngredientInfo.Get(Ingredient); - builder.AddHtmlLocalized(70, 260, 270, 100, info.Name, BaseQuestGump.Blue); - builder.AddLabel(70, 280, 0x64, $"{CurProgress}"); - builder.AddLabel(100, 280, 0x64, "/"); - builder.AddLabel(130, 280, 0x64, $"{info.Quantity}"); + gump.AddHtmlLocalized(70, 260, 270, 100, info.Name, BaseQuestGump.Blue); + gump.AddLabel(70, 280, 0x64, CurProgress.ToString()); + gump.AddLabel(100, 280, 0x64, "/"); + gump.AddLabel(130, 280, 0x64, info.Quantity.ToString()); } else { - base.RenderProgress(ref builder); + base.RenderProgress(gump); } } diff --git a/Projects/UOContent/Engines/Spawners/BaseSpawner.Dto.cs b/Projects/UOContent/Engines/Spawners/BaseSpawner.Dto.cs deleted file mode 100644 index 5c0917086..000000000 --- a/Projects/UOContent/Engines/Spawners/BaseSpawner.Dto.cs +++ /dev/null @@ -1,117 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BaseSpawner.Dto.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Engines.Spawners; - -public abstract partial class BaseSpawner -{ - /// Applies the common DTO fields to this freshly-created spawner (import path). - internal void ApplyDto(SpawnerDto dto) - { - _guid = dto.Guid == Guid.Empty ? Guid.NewGuid() : dto.Guid; - - if (!string.IsNullOrEmpty(dto.Name)) - { - Name = dto.Name; - } - - // Compact homeRange -> spawnBounds (Map not available yet; use the DTO location). - if (dto.HomeRange >= 0) - { - SpawnBounds = BoundsFromHomeRange(dto.Location, dto.HomeRange); - } - - InitSpawn(dto.Count, dto.MinDelay, dto.MaxDelay, dto.Team, SpawnBounds); - - _walkingRange = dto.WalkingRange; - _spawnLocationIsHome = dto.SpawnLocationIsHome; - _spawnPositionMode = dto.SpawnPositionMode; - _maxSpawnAttempts = dto.MaxSpawnAttempts; - - if (dto.Entries != null) - { - for (var i = 0; i < dto.Entries.Count; i++) - { - var entry = dto.Entries[i]; - AddEntry(entry.SpawnedName, entry.SpawnedProbability, entry.SpawnedMaxCount, false, entry.Properties, entry.Parameters); - } - } - } - - /// The square spawn bounds a homeRange radius represents (centered on the location). - private protected static Rectangle3D BoundsFromHomeRange(Point3D location, int homeRange) - { - int z; - int depth; - if (homeRange == 0) - { - z = location.Z; - depth = 0; - } - else - { - z = -128; - depth = 256; - } - - return new Rectangle3D( - location.X - homeRange, - location.Y - homeRange, - z, - homeRange * 2 + 1, - homeRange * 2 + 1, - depth - ); - } - - private protected string DtoName - { - get - { - var name = Name; - return string.IsNullOrEmpty(name) || name == DefaultName ? null : name; - } - } - - // ToDto helpers for values whose export form differs from the public property. (Guid/MinDelay/ - // MaxDelay/Team/SpawnLocationIsHome match their property and are referenced directly in ToDto.) - - // Raw field; the public WalkingRange is computed (falls back to HomeRange). - private protected int DtoWalkingRange => _walkingRange; - - // Abandoned is a transient runtime state, not persisted -> map to Automatic (omitted). - private protected SpawnPositionMode DtoSpawnPositionMode => - _spawnPositionMode == SpawnPositionMode.Abandoned ? SpawnPositionMode.Automatic : _spawnPositionMode; - - // Runtime treats 0 as DefaultMaxSpawnAttempts -> map the default to 0 (omitted). - private protected int DtoMaxSpawnAttempts => _maxSpawnAttempts == DefaultMaxSpawnAttempts ? 0 : _maxSpawnAttempts; - - // The radius if SpawnBounds is exactly what it reconstructs (lossless square); otherwise -1. - private protected int DtoHomeRange - { - get - { - if (SpawnBounds == default || !IsHomeRangeStyle) - { - return -1; - } - - var homeRange = HomeRange; - return SpawnBounds == BoundsFromHomeRange(Location, homeRange) ? homeRange : -1; - } - } -} diff --git a/Projects/UOContent/Engines/Spawners/BaseSpawner.Migrations.cs b/Projects/UOContent/Engines/Spawners/BaseSpawner.Migrations.cs deleted file mode 100644 index 9c5c4f002..000000000 --- a/Projects/UOContent/Engines/Spawners/BaseSpawner.Migrations.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.Collections.Generic; -using Server.Items; - -namespace Server.Engines.Spawners; - -public abstract partial class BaseSpawner -{ - // Static dictionary to track pending HomeRange migrations from v10 - // Keyed by spawner instance, stores the HomeRange value to convert to SpawnBounds - private static readonly Dictionary _pendingHomeRangeMigrations = new(); - - private void MigrateFrom(V10Content content) - { - _guid = content.Guid; - _returnOnDeactivate = content.ReturnOnDeactivate; - _entries = content.Entries; - _walkingRange = content.WalkingRange; - _wayPoint = content.WayPoint; - _group = content.Group; - _minDelay = content.MinDelay; - _maxDelay = content.MaxDelay; - _count = content.Count; - _team = content.Team; - _running = content.Running; - _end = _running ? content.End : Core.Now; - - // Defer SpawnBounds calculation to AfterDeserialization when Location is available - if (content.HomeRange > 0) - { - _pendingHomeRangeMigrations[this] = content.HomeRange; - } - - _spawnLocationIsHome = false; - - // New v12 fields default to automatic/default - _spawnPositionMode = SpawnPositionMode.Automatic; - _maxSpawnAttempts = DefaultMaxSpawnAttempts; - } - - private void MigrateFrom(V11Content content) - { - _guid = content.Guid; - _returnOnDeactivate = content.ReturnOnDeactivate; - _entries = content.Entries; - _walkingRange = content.WalkingRange; - _wayPoint = content.WayPoint; - _group = content.Group; - _minDelay = content.MinDelay; - _maxDelay = content.MaxDelay; - _count = content.Count; - _team = content.Team; - - // Moved to Spawner - if (this is Spawner spawner) - { - spawner.SpawnBounds = content.SpawnBounds; - } - - _running = content.Running; - _spawnLocationIsHome = content.SpawnLocationIsHome; - _end = _running ? content.End : Core.Now; - - // New v12 fields default to automatic/default - _spawnPositionMode = SpawnPositionMode.Automatic; - _maxSpawnAttempts = DefaultMaxSpawnAttempts; - } - - private void Deserialize(IGenericReader reader, int version) - { - _guid = reader.ReadGuid(); - _returnOnDeactivate = reader.ReadBool(); - - var count = reader.ReadInt(); - _entries = new List(count); - - for (var i = 0; i < count; ++i) - { - var entry = new SpawnerEntry(this); - entry.Deserialize(reader); - _entries.Add(entry); - } - - _walkingRange = reader.ReadInt(); - _wayPoint = reader.ReadEntity(); - _group = reader.ReadBool(); - _minDelay = reader.ReadTimeSpan(); - _maxDelay = reader.ReadTimeSpan(); - _count = reader.ReadInt(); - _team = reader.ReadInt(); - - // Store homeRange for migration in AfterDeserialization when Location is available - var homeRange = reader.ReadInt(); - if (homeRange > 0) - { - _pendingHomeRangeMigrations[this] = homeRange; - } - - _running = reader.ReadBool(); - _end = _running ? reader.ReadDeltaTime() : Core.Now; - } -} diff --git a/Projects/UOContent/Engines/Spawners/BaseSpawner.cs b/Projects/UOContent/Engines/Spawners/BaseSpawner.cs index 8efc54b8c..28e423313 100644 --- a/Projects/UOContent/Engines/Spawners/BaseSpawner.cs +++ b/Projects/UOContent/Engines/Spawners/BaseSpawner.cs @@ -1,63 +1,26 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Text.Json; using ModernUO.Serialization; using Server.Commands; using Server.Gumps; using Server.Items; -using Server.Logging; +using Server.Json; using Server.Mobiles; using static Server.Attributes; namespace Server.Engines.Spawners; -/// -/// Controls how a spawner handles spawn position optimization. -/// -public enum SpawnPositionMode : byte -{ - /// - /// Auto-detect if optimization is needed based on failure patterns. - /// Only engages lazy caching after non-transient spawn failures. - /// - Automatic = 0, - - /// - /// Force optimization on. Always cache successful spawn positions. - /// - Enabled = 1, - - /// - /// Force optimization off. Use only random position attempts. - /// - Disabled = 2, - - /// - /// Spawner has given up due to 100% failure rate. - /// Skips all spawn position logic and returns spawner location. - /// Admin can reset via [props. - /// - Abandoned = 3 -} - -[SerializationGenerator(12, false)] +[SerializationGenerator(10, false)] public abstract partial class BaseSpawner : Item, ISpawner { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(BaseSpawner)); - - // Default values for serialization optimization - private static readonly TimeSpan DefaultMinDelay = TimeSpan.FromMinutes(5); - private static readonly TimeSpan DefaultMaxDelay = TimeSpan.FromMinutes(10); - [SerializedIgnoreDupe] [SerializableField(0)] [SerializedCommandProperty(AccessLevel.Developer)] private Guid _guid; - private bool ShouldSerializeReturnOnDeactivate() => _returnOnDeactivate; - [SerializableField(1)] - [SaveFlag(nameof(ShouldSerializeReturnOnDeactivate))] [SerializedCommandProperty(AccessLevel.Developer)] private bool _returnOnDeactivate; @@ -65,204 +28,47 @@ public abstract partial class BaseSpawner : Item, ISpawner [SerializableField(2, setter: "private")] private List _entries; + [InvalidateProperties] + [SerializableField(3)] + [SerializedCommandProperty(AccessLevel.Developer)] private int _walkingRange = -1; - private bool ShouldSerializeWayPoint() => _wayPoint != null; - [SerializableField(4)] - [SaveFlag(nameof(ShouldSerializeWayPoint))] [SerializedCommandProperty(AccessLevel.Developer)] private WayPoint _wayPoint; - private bool ShouldSerializeGroup() => _group; - [InvalidateProperties] [SerializableField(5)] - [SaveFlag(nameof(ShouldSerializeGroup))] [SerializedCommandProperty(AccessLevel.Developer)] private bool _group; - private bool ShouldSerializeMinDelay() => _minDelay != DefaultMinDelay; - - private TimeSpan MinDelayDefault() => DefaultMinDelay; - [InvalidateProperties] [SerializableField(6)] - [SaveFlag(nameof(ShouldSerializeMinDelay), nameof(MinDelayDefault))] [SerializedCommandProperty(AccessLevel.Developer)] private TimeSpan _minDelay; - private bool ShouldSerializeMaxDelay() => _maxDelay != DefaultMaxDelay; - - private TimeSpan MaxDelayDefault() => DefaultMaxDelay; - [InvalidateProperties] [SerializableField(7)] - [SaveFlag(nameof(ShouldSerializeMaxDelay), nameof(MaxDelayDefault))] [SerializedCommandProperty(AccessLevel.Developer)] private TimeSpan _maxDelay; - private bool ShouldSerializeTeam() => _team != 0; - [InvalidateProperties] [SerializableField(9)] - [SaveFlag(nameof(ShouldSerializeTeam))] [SerializedCommandProperty(AccessLevel.Developer)] private int _team; - /// - /// The spawn bounds for this spawner. Abstract to allow derived classes to manage their own storage. - /// - [CommandProperty(AccessLevel.Developer)] - public abstract Rectangle3D SpawnBounds { get; set; } - - /// Builds the JSON DTO for this spawner (export path). - public abstract SpawnerDto ToDto(); - - /// - /// If true, the home location of the spawn is the location where it spawned - /// If false, the home location of the spawn is the location of the spawner - /// - private bool ShouldSerializeSpawnLocationIsHome() => _spawnLocationIsHome; - [InvalidateProperties] - [SerializableField(11)] - [SaveFlag(nameof(ShouldSerializeSpawnLocationIsHome))] + [SerializableField(10)] [SerializedCommandProperty(AccessLevel.Developer)] - private bool _spawnLocationIsHome; - - private bool ShouldSerializeEnd() => _end != default; + private int _homeRange; [SerializableField(12)] - [SaveFlag(nameof(ShouldSerializeEnd))] [SerializedCommandProperty(AccessLevel.Developer)] private DateTime _end; - /// - /// Controls how spawn position optimization is handled. - /// - private bool ShouldSerializeSpawnPositionMode() => - _spawnPositionMode is not SpawnPositionMode.Automatic and not SpawnPositionMode.Abandoned; - - [SerializableField(13)] - [SaveFlag(nameof(ShouldSerializeSpawnPositionMode))] - [SerializedCommandProperty(AccessLevel.Developer)] - private SpawnPositionMode _spawnPositionMode; - - private const int DefaultMaxSpawnAttempts = 10; - - /// - /// Maximum number of random position attempts before engaging optimization. - /// - private bool ShouldSerializeMaxSpawnAttempts() => _maxSpawnAttempts != DefaultMaxSpawnAttempts; - - private int MaxSpawnAttemptsDefault() => DefaultMaxSpawnAttempts; - - [SerializableField(14)] - [SaveFlag(nameof(ShouldSerializeMaxSpawnAttempts), nameof(MaxSpawnAttemptsDefault))] - [SerializedCommandProperty(AccessLevel.Developer)] - private int _maxSpawnAttempts; - - // Non-serialized: Runtime state for spawn position optimization - private SpawnPositionState _spawnPositionState; - private InternalTimer _timer; - /// - /// Gets the distance from the spawner to the nearest edge of the spawn bounds. - /// Setting this creates a square spawn area centered on the spawner location. - /// - [CommandProperty(AccessLevel.Developer)] - public virtual int HomeRange - { - get - { - if (SpawnBounds == default) - { - return 0; - } - - // Distance from spawner location to nearest edge - var distToMinX = Math.Abs(Location.X - SpawnBounds.Start.X); - var distToMaxX = Math.Abs(SpawnBounds.End.X - Location.X); - var distToMinY = Math.Abs(Location.Y - SpawnBounds.Start.Y); - var distToMaxY = Math.Abs(SpawnBounds.End.Y - Location.Y); - - // Return smallest distance to any edge - return Math.Min(Math.Min(distToMinX, distToMaxX), Math.Min(distToMinY, distToMaxY)); - } - set - { - int z; - int depth; - if (SpawnBounds != default) - { - z = SpawnBounds.Start.Z; - depth = SpawnBounds.Depth; - } - else if (value == 0) - { - z = Location.Z; - depth = 0; - } - else - { - z = -128; - depth = 256; - } - - // Create square bounds centered on spawner, Z range from surface to surface + 16 - SpawnBounds = new Rectangle3D( - Location.X - value, - Location.Y - value, - z, - value * 2 + 1, - value * 2 + 1, - depth - ); - this.MarkDirty(); - InvalidateProperties(); - } - } - - /// - /// Returns true if SpawnBounds represents a HomeRange-style square centered on the spawner. - /// - public bool IsHomeRangeStyle => IsHomeRangeStyleAt(Location); - - /// - /// Returns true if SpawnBounds represents a HomeRange-style square centered on the given location. - /// - public bool IsHomeRangeStyleAt(Point3D location) - { - if (SpawnBounds == default) - { - return true; // No bounds = default HomeRange behavior - } - - // Must be square - if (SpawnBounds.Width != SpawnBounds.Height) - { - return false; - } - - // Given location must be at center - var centerX = SpawnBounds.Start.X + SpawnBounds.Width / 2; - var centerY = SpawnBounds.Start.Y + SpawnBounds.Height / 2; - - return centerX == location.X && centerY == location.Y; - } - - /// - /// Checks if the given location is within the spawn bounds. - /// Virtual to allow RegionSpawner to override with region-based logic. - /// - public virtual bool IsInSpawnBounds(Point3D location) - { - return SpawnBounds == default || SpawnBounds.Contains(location); - } - - public BaseSpawner() : this(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10)) + public BaseSpawner() : this(1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), 0, 4) { } @@ -270,28 +76,70 @@ public abstract partial class BaseSpawner : Item, ISpawner 1, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), - spawnedNames: spawnedName + 0, + 4, + spawnedName ) { } public BaseSpawner( - int amount, - TimeSpan minDelay, - TimeSpan maxDelay, - int team = 0, - Rectangle3D spawnBounds = default, - params ReadOnlySpan spawnedNames + int amount, int minDelay, int maxDelay, int team, int homeRange, + params string[] spawnedNames + ) : this( + amount, + TimeSpan.FromMinutes(minDelay), + TimeSpan.FromMinutes(maxDelay), + team, + homeRange, + spawnedNames + ) + { + } + + public BaseSpawner( + int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, + params string[] spawnedNames ) : base(0x1f13) { _guid = Guid.NewGuid(); - InitSpawn(amount, minDelay, maxDelay, team, spawnBounds); + InitSpawn(amount, minDelay, maxDelay, team, homeRange); for (var i = 0; i < spawnedNames.Length; i++) { AddEntry(spawnedNames[i], 100, amount, false); } } + public BaseSpawner(DynamicJson json, JsonSerializerOptions options) : base(0x1f13) + { + if (!json.GetProperty("guid", options, out _guid)) + { + _guid = Guid.NewGuid(); + } + + if (json.GetProperty("name", options, out string name)) + { + Name = name; + } + + json.GetProperty("count", options, out int amount); + json.GetProperty("minDelay", options, out TimeSpan minDelay); + json.GetProperty("maxDelay", options, out TimeSpan maxDelay); + json.GetProperty("team", options, out int team); + json.GetProperty("homeRange", options, out int homeRange); + json.GetProperty("walkingRange", options, out int walkingRange); + _walkingRange = walkingRange; + + InitSpawn(amount, minDelay, maxDelay, team, homeRange); + + json.GetProperty("entries", options, out List entries); + + foreach (var entry in entries) + { + AddEntry(entry.SpawnedName, entry.SpawnedProbability, entry.SpawnedMaxCount, false, entry.Properties, entry.Parameters); + } + } + public override string DefaultName => "Spawner"; public bool IsFull => Spawned?.Count >= _count; public bool IsEmpty => Spawned?.Count == 0; @@ -299,36 +147,30 @@ public abstract partial class BaseSpawner : Item, ISpawner [IgnoreDupe] public Dictionary Spawned { get; private set; } + [SerializableProperty(8)] [CommandProperty(AccessLevel.Developer)] - [SerializableProperty(3, nameof(_walkingRange))] - public int WalkingRange + public int Count { - get => _walkingRange > 0 ? _walkingRange : HomeRange; + get => _count; set { - _walkingRange = value; + _count = value; + + if (IsFull) + { + _timer?.Stop(); + } + else if (_timer?.Running != true) + { + DoTimer(); + } + InvalidateProperties(); + this.MarkDirty(); } } - [SerializableField(8, fieldChanged: nameof(OnCountChanged))] - [SerializedCommandProperty(AccessLevel.Developer)] - [InvalidateProperties] - private int _count; - - private void OnCountChanged(int oldValue, int newValue) - { - if (IsFull) - { - _timer?.Stop(); - } - else if (_timer?.Running != true) - { - DoTimer(); - } - } - - [SerializableProperty(10)] + [SerializableProperty(11)] [CommandProperty(AccessLevel.Developer)] public bool Running { @@ -366,37 +208,20 @@ public abstract partial class BaseSpawner : Item, ISpawner public virtual Point3D HomeLocation => Location; public bool UnlinkOnTaming => true; - public abstract Region Region { get; } + Region ISpawner.Region => Region.Find(Location, Map); - /// - /// Returns the bounds to use for a single spawn attempt. - /// Called for each random attempt in Phase 1. - /// Spawner: returns SpawnBounds - /// RegionSpawner: picks a weighted random rectangle from the region - /// - protected abstract Rectangle3D GetBoundsForSpawnAttempt(); - - /// - /// Returns all possible spawn bounds for cache operations. - /// Used in Phase 3 to search for cached positions. - /// Spawner: returns single-element array with SpawnBounds - /// RegionSpawner: returns all region rectangles - /// - protected abstract ReadOnlySpan GetAllSpawnBounds(); - - /// - /// Whether this spawner supports spiral scanning. - /// Only makes sense for contiguous bounds (Spawner). - /// Disjoint rectangles (RegionSpawner) should return false. - /// - protected virtual bool SupportsSpiralScan => false; + public void UpdateEntries(List entries) + { + Entries = entries; + } public void Remove(ISpawnable spawn) { Defrag(); - if (spawn != null && Spawned?.Remove(spawn, out var entry) == true) + if (spawn != null) { + Spawned.Remove(spawn, out var entry); entry?.RemoveFromSpawned(spawn); } @@ -424,215 +249,23 @@ public abstract partial class BaseSpawner : Item, ISpawner RemoveSpawns(); } - public virtual Point3D GetSpawnPosition(ISpawnable spawned, Map map) + public virtual void ToJson(DynamicJson json, JsonSerializerOptions options) { - // Abandoned spawners skip all work - if (map == null || map == Map.Internal || _spawnPositionMode == SpawnPositionMode.Abandoned) - { - return Location; - } - - // Ensure state is initialized - _spawnPositionState ??= new SpawnPositionState(); - - // Determine mob type for caching - var isMobile = spawned is Mobile; - var canSwim = isMobile && ((Mobile)spawned).CanSwim; - var cantWalk = isMobile && ((Mobile)spawned).CantWalk; - var isWaterMob = canSwim && cantWalk; - var hasNonTransientFailure = false; - - // Phase 1: Random attempts (always first - maintains randomness) - var maxAttempts = _maxSpawnAttempts > 0 ? _maxSpawnAttempts : DefaultMaxSpawnAttempts; - for (var i = 0; i < maxAttempts; i++) - { - var rawBounds = GetBoundsForSpawnAttempt(); - - // No bounds = spawn at spawner location - if (rawBounds == default) - { - return Location; - } - - // Normalize to ensure Start <= End in all dimensions - var bounds = rawBounds.Normalized; - - var x = Utility.RandomMinMax(bounds.Start.X, bounds.End.X - 1); - var y = Utility.RandomMinMax(bounds.Start.Y, bounds.End.Y - 1); - var minZ = bounds.Start.Z; - var maxZ = Math.Max(minZ, bounds.End.Z - 1); - - bool success; - int spawnZ; - SpawnFailureReason failureReason; - - if (isMobile) - { - success = map.CanSpawnMobile(x, y, minZ, maxZ, canSwim, cantWalk, out spawnZ, out failureReason); - } - else - { - success = map.CanSpawnItem(x, y, minZ, maxZ, out spawnZ); - failureReason = success ? SpawnFailureReason.None : SpawnFailureReason.NonTransientBlocker; - } - - if (success) - { - // Check if blocked by a private house (non-transient) - if (isMobile && SectorSpawnCacheManager.IsBlockedByHouse(map, x, y, spawnZ)) - { - hasNonTransientFailure = true; - } - else - { - // Lazy cache: add successful position to global sector cache - if (_spawnPositionState.ShouldCachePositions(_spawnPositionMode)) - { - SectorSpawnCacheManager.SetValid(map, new Point3D(x, y, spawnZ), isWaterMob); - } - - return new Point3D(x, y, spawnZ); - } - } - else if ((failureReason & SpawnFailureReason.NonTransientBlocker) != 0) - { - hasNonTransientFailure = true; - } - } - - // Phase 2: Check if optimization should engage - if (_spawnPositionMode == SpawnPositionMode.Disabled) - { - return Location; - } - - var useOptimization = _spawnPositionMode == SpawnPositionMode.Enabled - || _spawnPositionMode == SpawnPositionMode.Automatic && hasNonTransientFailure; - - if (!useOptimization) - { - // Transient failure only - just use spawner location - return Location; - } - - _spawnPositionState.RecordNonTransientFailure(); - - var allBounds = GetAllSpawnBounds(); - - // Phase 3: Continue spiral scan to populate cache (before selecting from it) - if (!SupportsSpiralScan) - { - // Mark spiral complete for non-spiral spawners so ShouldAbandon() can trigger - _spawnPositionState.SpiralComplete = true; - } - else if (!_spawnPositionState.SpiralComplete) - { - // Use the first bounds for spiral center/range - var rawPrimaryBounds = allBounds.Length > 0 ? allBounds[0] : default; - if (rawPrimaryBounds != default) - { - var primaryBounds = rawPrimaryBounds.Normalized; - var minZ = primaryBounds.Start.Z; - var maxZ = Math.Max(minZ, primaryBounds.End.Z - 1); - - // Scan more rings initially (3), fewer once cache has positions (1) - var ringsPerTick = _spawnPositionState.SpiralRing == 0 ? 3 : 1; - - _spawnPositionState.SpiralComplete = SectorSpawnCacheManager.ContinueSpiralScan( - map, - Location, - primaryBounds, - minZ, - maxZ, - canSwim, - cantWalk, - ref _spawnPositionState.SpiralRing, - ref _spawnPositionState.SpiralRingPosition, - ringsPerTick - ); - } - } - - // Phase 4: Try cached positions from global sector cache (uses deduplicated sectors) - if (TryGetVerifiedCachedPosition(map, allBounds, isMobile, isWaterMob, canSwim, cantWalk, out var spawnPos)) - { - // Check if cache returned a useful position (not just spawner's own location) - if (spawnPos != Location) - { - _spawnPositionState.RecordUsefulCacheHit(); - return spawnPos; - } - } - - // Cache miss or returned Location only - _spawnPositionState.RecordUselessResult(); - - // Phase 5: Check for abandoned state (only auto-abandon from Automatic mode) - if (_spawnPositionMode == SpawnPositionMode.Automatic && _spawnPositionState.ShouldAbandon()) - { - SpawnPositionMode = SpawnPositionMode.Abandoned; - _spawnPositionState = null; - logger.Warning( - "Spawner {Serial} at {Location} ({Map}) marked abandoned - no valid spawn positions found after spiral scan.", - Serial, - Location, - map.Name ?? "null" - ); - } - - return Location; + json.Type = GetType().Name; + json.SetProperty("name", options, Name); + json.SetProperty("guid", options, _guid); + json.SetProperty("location", options, Location); + json.SetProperty("map", options, Map); + json.SetProperty("count", options, Count); + json.SetProperty("minDelay", options, MinDelay); + json.SetProperty("maxDelay", options, MaxDelay); + json.SetProperty("team", options, Team); + json.SetProperty("homeRange", options, HomeRange); + json.SetProperty("walkingRange", options, WalkingRange); + json.SetProperty("entries", options, Entries); } - /// - /// Attempts to get a verified spawn position from the sector cache across all bounds. - /// Uses deduplicated sector lookup for uniform distribution. - /// - private static bool TryGetVerifiedCachedPosition( - Map map, - ReadOnlySpan allBounds, - bool isMobile, - bool isWaterMob, - bool canSwim, - bool cantWalk, - out Point3D spawnPos) - { - if (!SectorSpawnCacheManager.TryGetRandomPosition( - map, - allBounds, - isWaterMob, - out var cachedPos, - out var containingBounds - )) - { - spawnPos = default; - return false; - } - - // Re-verify in 3D using the bounds that contains this position - var normalizedBounds = containingBounds.Normalized; - var minZ = normalizedBounds.Start.Z; - var maxZ = Math.Max(minZ, normalizedBounds.End.Z - 1); - - var verified = isMobile - ? map.CanSpawnMobile(cachedPos.X, cachedPos.Y, minZ, maxZ, canSwim, cantWalk, out var verifiedZ) - : map.CanSpawnItem(cachedPos.X, cachedPos.Y, minZ, maxZ, out verifiedZ); - - if (!verified) - { - spawnPos = default; - return false; - } - - // Skip positions inside private houses - if (isMobile && SectorSpawnCacheManager.IsBlockedByHouse(map, cachedPos.X, cachedPos.Y, verifiedZ)) - { - spawnPos = default; - return false; - } - - spawnPos = new Point3D(cachedPos.X, cachedPos.Y, verifiedZ); - return true; - } + public abstract Point3D GetSpawnPosition(ISpawnable spawned, Map map); public override void OnAfterDuped(Item newItem) { @@ -657,46 +290,6 @@ public abstract partial class BaseSpawner : Item, ISpawner } } - public override void OnMapChange() - { - base.OnMapChange(); - - if (IsHomeRangeStyleAt(Location)) - { - ShowSpawnerBordersCommand.RemoveProjection(this); - } - } - - public override void OnLocationChange(Point3D oldLocation) - { - base.OnLocationChange(oldLocation); - - // Recalculate HomeRange-style bounds when spawner moves - if (IsHomeRangeStyleAt(oldLocation)) - { - HomeRange = SpawnBounds.Width / 2; - ShowSpawnerBordersCommand.RemoveProjection(this); - } - - // Reset spawn position optimization state when spawner moves - ResetSpawnPositionState(); - } - - /// - /// Resets the spawn position optimization state. - /// Called when spawner moves or bounds change. - /// - protected void ResetSpawnPositionState() - { - _spawnPositionState?.Reset(); - - // If we were abandoned, reset to automatic to give it another chance - if (_spawnPositionMode == SpawnPositionMode.Abandoned) - { - _spawnPositionMode = SpawnPositionMode.Automatic; - } - } - public SpawnerEntry AddEntry( string creaturename, int probability = 100, @@ -716,7 +309,7 @@ public abstract partial class BaseSpawner : Item, ISpawner return entry; } - public void InitSpawn(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team = 0, Rectangle3D spawnBounds = default) + public void InitSpawn(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange) { Visible = false; Movable = false; @@ -726,16 +319,7 @@ public abstract partial class BaseSpawner : Item, ISpawner _maxDelay = maxDelay; _count = amount; _team = team; - - if (spawnBounds != default) - { - SpawnBounds = spawnBounds; - } - else - { - HomeRange = 4; - } - + _homeRange = homeRange; Entries = []; Spawned = new Dictionary(); @@ -764,10 +348,7 @@ public abstract partial class BaseSpawner : Item, ISpawner list.Add(1060742); // active list.Add(1060656, _count); // amount to make: ~1_val~ - if (SpawnBounds != default) - { - list.Add(1061169, SpawnBounds.ToString()); // range ~1_val~ - } + list.Add(1061169, _homeRange); // range ~1_val~ list.Add(1050039, $"{"walking range:"}\t{_walkingRange}"); // ~1_NUMBER~ ~2_ITEMNAME~ list.Add(1053099, $"{"group:"}\t{_group}"); // ~1_oretype~: ~2_armortype~ list.Add(1060847, $"{"team:"}\t{_team}"); // ~1_val~ ~2_val~ @@ -1099,16 +680,16 @@ public abstract partial class BaseSpawner : Item, ISpawner Spawned.Add(m, entry); entry.AddToSpawned(m); - // var spawnLocation = m is BaseVendor ? Location : GetSpawnPosition(m, map); + var loc = m is BaseVendor ? Location : GetSpawnPosition(m, map); - var spawnLocation = GetSpawnPosition(m, map); - - m.OnBeforeSpawn(spawnLocation, map); - m.MoveToWorld(spawnLocation, map); + m.OnBeforeSpawn(loc, map); + m.MoveToWorld(loc, map); if (m is BaseCreature c) { - c.RangeHome = WalkingRange; + var walkrange = GetWalkingRange(); + + c.RangeHome = walkrange >= 0 ? walkrange : _homeRange; c.CurrentWayPoint = WayPoint; if (_team > 0) @@ -1116,18 +697,8 @@ public abstract partial class BaseSpawner : Item, ISpawner c.Team = _team; } - // If true, the home location of the mob is the location where it spawned - // If false, the home location of the mob is the location of the spawner - if (_spawnLocationIsHome) - { - c.Home = spawnLocation; - c.HomeMap = map; - } - else - { - c.Home = Location; - c.HomeMap = Map; - } + c.Home = Location; + c.HomeMap = Map; } m.Spawner = this; @@ -1165,6 +736,8 @@ public abstract partial class BaseSpawner : Item, ISpawner return true; } + public virtual int GetWalkingRange() => _walkingRange; + public virtual Map GetSpawnMap() => Map; public void DoTimer() @@ -1278,7 +851,7 @@ public abstract partial class BaseSpawner : Item, ISpawner if (e != null) { - Spawned?.Remove(e); + Spawned.Remove(e); entry.Spawned.RemoveAt(j); e.Delete(); } @@ -1311,22 +884,37 @@ public abstract partial class BaseSpawner : Item, ISpawner RemoveSpawns(); } + private void Deserialize(IGenericReader reader, int version) + { + _guid = reader.ReadGuid(); + _returnOnDeactivate = reader.ReadBool(); + + var count = reader.ReadInt(); + _entries = new List(count); + + for (var i = 0; i < count; ++i) + { + var entry = new SpawnerEntry(this); + entry.Deserialize(reader); + _entries.Add(entry); + } + + _walkingRange = reader.ReadInt(); + _wayPoint = reader.ReadEntity(); + _group = reader.ReadBool(); + _minDelay = reader.ReadTimeSpan(); + _maxDelay = reader.ReadTimeSpan(); + _count = reader.ReadInt(); + _team = reader.ReadInt(); + _homeRange = reader.ReadInt(); + _running = reader.ReadBool(); + + _end = _running ? reader.ReadDeltaTime() : Core.Now; + } + [AfterDeserialization] private void AfterDeserialization() { - // Handle v10 migration - convert HomeRange to SpawnBounds now that Location is available - if (_pendingHomeRangeMigrations.Remove(this, out var homeRange)) - { - SpawnBounds = new Rectangle3D( - Location.X - homeRange, - Location.Y - homeRange, - -128, - homeRange * 2 + 1, - homeRange * 2 + 1, - 256 - ); - } - Spawned = new Dictionary(); foreach (var entry in Entries) diff --git a/Projects/UOContent/Engines/Spawners/Commands/EditSpawnerCommand.cs b/Projects/UOContent/Engines/Spawners/Commands/EditSpawnerCommand.cs index 067a7f762..a51188f74 100644 --- a/Projects/UOContent/Engines/Spawners/Commands/EditSpawnerCommand.cs +++ b/Projects/UOContent/Engines/Spawners/Commands/EditSpawnerCommand.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: EditSpawnerCommand.cs * * * diff --git a/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs b/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs index abaa3f9dc..9b596b546 100644 --- a/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs +++ b/Projects/UOContent/Engines/Spawners/Commands/ExportSpawnersCommand.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ExportSpawnersCommand.cs * * * @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; using System.IO; using Server.Commands.Generic; +using Server.Json; using Server.Network; namespace Server.Engines.Spawners; @@ -41,8 +42,8 @@ public class ExportSpawnersCommand : BaseCommand public override void ExecuteList(CommandEventArgs e, List list) { - var path = e.Arguments.Length == 0 ? string.Empty : e.Arguments[0].Trim(); - var condition = e.Arguments.Length == 2 ? e.Arguments[1].Trim() : string.Empty; + string path = e.Arguments.Length == 0 ? string.Empty : e.Arguments[0].Trim(); + string condition = e.Arguments.Length == 2 ? e.Arguments[1].Trim() : string.Empty; if (string.IsNullOrEmpty(path)) { @@ -65,7 +66,9 @@ public class ExportSpawnersCommand : BaseCommand NetState.FlushAll(); - var spawnRecords = new List(list.Count); + var options = JsonConfig.GetOptions(new TextDefinitionConverterFactory()); + + var spawnRecords = new List(list.Count); for (var i = 0; i < list.Count; i++) { // Not a spawner, not on a valid map, or is in a container @@ -82,7 +85,9 @@ public class ExportSpawnersCommand : BaseCommand continue; } - spawnRecords.Add(spawner.ToDto()); + var dynamicJson = DynamicJson.Create(spawner.GetType()); + spawner.ToJson(dynamicJson, options); + spawnRecords.Add(dynamicJson); } if (spawnRecords.Count == 0) @@ -93,9 +98,7 @@ public class ExportSpawnersCommand : BaseCommand e.Mobile.SendMessage("Exporting spawners..."); - // Compact layout (UTF-8, no BOM, LF) keeps re-exports diff-friendly. - PathUtility.EnsureDirectory(Path.GetDirectoryName(path)); - File.WriteAllText(path, SpawnerJsonSerializer.SerializeCompact(spawnRecords)); + JsonConfig.Serialize(path, spawnRecords, options); e.Mobile.SendMessage($"Spawners exported to {path}"); } diff --git a/Projects/UOContent/Engines/Spawners/Commands/ImportSpawnersCommand.cs b/Projects/UOContent/Engines/Spawners/Commands/ImportSpawnersCommand.cs index 28dd7f613..cba03feaf 100644 --- a/Projects/UOContent/Engines/Spawners/Commands/ImportSpawnersCommand.cs +++ b/Projects/UOContent/Engines/Spawners/Commands/ImportSpawnersCommand.cs @@ -117,10 +117,10 @@ public static class ImportSpawnersCommand ref int totalFailures ) { - var doc = new XmlDocument(); + XmlDocument doc = new XmlDocument(); doc.Load(file.FullName); - var root = doc["spawners"]; + XmlElement root = doc["spawners"]; if (root == null) { @@ -132,15 +132,15 @@ public static class ImportSpawnersCommand { try { - var count = int.Parse(Utility.GetText(node["count"], "1")); - var homeRange = int.Parse(Utility.GetText(node["homerange"], "4")); + int count = int.Parse(Utility.GetText(node["count"], "1")); + int homeRange = int.Parse(Utility.GetText(node["homerange"], "4")); - var walkingRange = int.Parse(Utility.GetText(node["walkingrange"], "-1")); + int walkingRange = int.Parse(Utility.GetText(node["walkingrange"], "-1")); - var team = int.Parse(Utility.GetText(node["team"], "0")); + int team = int.Parse(Utility.GetText(node["team"], "0")); - var maxDelay = TimeSpan.Parse(Utility.GetText(node["maxdelay"], "10:00")); - var minDelay = TimeSpan.Parse(Utility.GetText(node["mindelay"], "05:00")); + TimeSpan maxDelay = TimeSpan.Parse(Utility.GetText(node["maxdelay"], "10:00")); + TimeSpan minDelay = TimeSpan.Parse(Utility.GetText(node["mindelay"], "05:00")); var creaturesNameNode = node["creaturesname"]; List creatureNames = []; if (creaturesNameNode != null) @@ -154,11 +154,11 @@ public static class ImportSpawnersCommand } } - var name = Utility.GetText(node["name"], "Spawner"); - var location = Point3D.Parse(Utility.GetText(node["location"], "Error")); - var map = Map.Parse(Utility.GetText(node["map"], "Error")); + string name = Utility.GetText(node["name"], "Spawner"); + Point3D location = Point3D.Parse(Utility.GetText(node["location"], "Error")); + Map map = Map.Parse(Utility.GetText(node["map"], "Error")); - var spawner = new Spawner(count, minDelay, maxDelay, team, spawnedNames: creatureNames.ToArray()); + Spawner spawner = new Spawner(count, minDelay, maxDelay, team, homeRange, creatureNames.ToArray()); if (walkingRange >= 0) { spawner.WalkingRange = walkingRange; @@ -166,7 +166,6 @@ public static class ImportSpawnersCommand spawner.Name = name; spawner.MoveToWorld(location, map); - spawner.HomeRange = homeRange; if (spawner.Map == Map.Internal) { spawner.Delete(); @@ -190,15 +189,6 @@ public static class ImportSpawnersCommand } } - // Test seam: wraps ImportJsonSpawners without requiring a live Mobile (from = null is safe - // because from is only used in the JsonException path, which won't be hit for valid files). - internal static void ImportFile(FileInfo file, Dictionary allSpawners) - { - var generated = 0; - var failures = 0; - ImportJsonSpawners(null, file, allSpawners, ref generated, ref failures); - } - private static void ImportJsonSpawners( Mobile from, FileInfo file, @@ -207,89 +197,71 @@ public static class ImportSpawnersCommand ref int totalFailures ) { - List dtos; + var options = JsonConfig.GetOptions(); try { - // DTO deserialization constructs NO world objects — a malformed file fails as GC only. - dtos = JsonConfig.Deserialize>(file.FullName, SpawnerJsonSerializer.Options); + var spawners = JsonConfig.Deserialize>(file.FullName); + using var queue = PooledRefQueue.Create(); + for (var i = 0; i < spawners.Count; i++) + { + var json = spawners[i]; + var type = AssemblyHandler.FindTypeByName(json.Type); + + if (type == null || !typeof(BaseSpawner).IsAssignableFrom(type)) + { + logger.Error($"Invalid spawner type {json.Type ?? "(-null-)"} ({i})."); + totalFailures++; + continue; + } + + json.GetProperty("location", options, out Point3D location); + json.GetProperty("map", options, out Map map); + + // Delete all spawners at this location. + // Probably shouldn't do this outside of migrations? Is there a better way to find/fix spawners? + foreach (var spawner in map.GetItemsAt(location)) + { + if (spawner.GetType() == type) + { + queue.Enqueue(spawner); + allSpawners.Remove(spawner.Guid); + } + } + + while (queue.Count > 0) + { + queue.Dequeue().Delete(); + } + + try + { + var spawner = type.CreateInstance(json, options); + + spawner!.MoveToWorld(location, map); + spawner!.Respawn(); + + if (allSpawners.Remove(spawner.Guid, out var oldSpawner)) + { + oldSpawner.Delete(); + } + + allSpawners.Add(spawner.Guid, spawner); + totalGenerated++; + } + catch (Exception ex) + { + json.GetProperty("guid", options, out Guid guid); + TraceException(ex, $"Failed to generate spawner {guid}."); + + totalFailures++; + } + } } catch (JsonException) { - from?.SendMessage( + from.SendMessage( $"GenerateSpawners: Exception parsing {file.FullName}, file may not be in the correct format." ); - return; - } - - if (dtos == null || dtos.Count == 0) - { - from?.SendMessage($"GenerateSpawners: Skipping empty spawner file {file.Name}"); - logger.Information("{User} is skipping empty spawner file {File}", from, file.FullName); - return; - } - - using var queue = PooledRefQueue.Create(); - for (var i = 0; i < dtos.Count; i++) - { - var dto = dtos[i]; - var location = dto.Location; - var map = dto.Map; - - if (map == null || map == Map.Internal) - { - logger.Error("Spawner {Guid} ({Index}) has no valid map; skipping.", dto.Guid, i); - totalFailures++; - continue; - } - - BaseSpawner spawner; - try - { - spawner = dto.ToSpawner(); // constructs the single Item, now referenced - } - catch (Exception ex) - { - TraceException(ex, $"Failed to build spawner {dto.Guid}."); - totalFailures++; - continue; - } - - var type = spawner.GetType(); - - // Delete all existing spawners of the same concrete type at this location. - foreach (var existing in map.GetItemsAt(location)) - { - if (existing.GetType() == type && existing != spawner) - { - queue.Enqueue(existing); - allSpawners.Remove(existing.Guid); - } - } - - while (queue.Count > 0) - { - queue.Dequeue().Delete(); - } - - try - { - spawner.MoveToWorld(location, map); - spawner.Respawn(); - - if (allSpawners.Remove(spawner.Guid, out var oldSpawner)) - { - oldSpawner.Delete(); - } - - allSpawners.Add(spawner.Guid, spawner); - totalGenerated++; - } - catch (Exception ex) - { - TraceException(ex, $"Failed to generate spawner {spawner.Guid}."); - spawner.Delete(); - totalFailures++; - } } } @@ -330,9 +302,9 @@ public static class ImportSpawnersCommand { var lines = File.ReadAllLines(file.FullName); - var minTimeOverride = TimeSpan.MinValue; - var maxTimeOverride = TimeSpan.MinValue; - var mapIdOverride = -1; + TimeSpan minTimeOverride = TimeSpan.MinValue; + TimeSpan maxTimeOverride = TimeSpan.MinValue; + int mapIdOverride = -1; string spawnerIdOverride = null; for (var i = 0; i < lines.Length; i++) @@ -375,7 +347,7 @@ public static class ImportSpawnersCommand var maxDelay = GetTimeSpan(maxTimeOverride, parts[12]); var homeRange = int.Parse(parts[14]); - var spawner = new Spawner(totalCount, minDelay, maxDelay) + var spawner = new Spawner(totalCount, minDelay, maxDelay, 0, homeRange) { WalkingRange = int.Parse(parts[13]), Name = $"Spawner ({spawnerIdOverride ?? parts[15]})" @@ -390,7 +362,6 @@ public static class ImportSpawnersCommand new Point3D(int.Parse(parts[7]), int.Parse(parts[8]), int.Parse(parts[9])), map ); - spawner.HomeRange = homeRange; spawner.Respawn(); allSpawners.Add(spawner.Guid, spawner); diff --git a/Projects/UOContent/Engines/Spawners/Commands/RespawnCommand.cs b/Projects/UOContent/Engines/Spawners/Commands/RespawnCommand.cs index 4bc7b4465..97c00fc81 100644 --- a/Projects/UOContent/Engines/Spawners/Commands/RespawnCommand.cs +++ b/Projects/UOContent/Engines/Spawners/Commands/RespawnCommand.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: RespawnCommand.cs * * * diff --git a/Projects/UOContent/Engines/Spawners/Commands/ShowSpawnerBordersCommand.cs b/Projects/UOContent/Engines/Spawners/Commands/ShowSpawnerBordersCommand.cs deleted file mode 100644 index 6c59ac4f6..000000000 --- a/Projects/UOContent/Engines/Spawners/Commands/ShowSpawnerBordersCommand.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System.Collections.Generic; -using ModernUO.Serialization; -using Server.Commands.Generic; -using Server.Items; - -namespace Server.Engines.Spawners; - -public class ShowSpawnerBordersCommand : BaseCommand -{ - // ItemID constants for border pieces - private const int SouthEastCorner = 0xF8; // Bottom-right corner - private const int NorthSouthWall = 0xF9; // Horizontal walls - private const int EastWestWall = 0xFA; // Vertical walls - private const int NorthWestCorner = 0xFB; // Top-left corner - - private static readonly Dictionary> _projectedSpawners = []; - - public static void Configure() - { - TargetCommands.Register(new ShowSpawnerBordersCommand()); - } - - public ShowSpawnerBordersCommand() - { - AccessLevel = AccessLevel.Developer; - Supports = CommandSupport.Simple; - Commands = ["ShowSpawnerBorders"]; - ObjectTypes = ObjectTypes.Items; - Usage = "ShowSpawnerBorders"; - Description = "Toggles a projection of the targeted spawner's borders."; - } - - public override void Execute(CommandEventArgs e, object obj) - { - if (obj is not BaseSpawner spawner) - { - LogFailure("That is not a spawner."); - return; - } - - if (_projectedSpawners.Remove(spawner, out var borders)) - { - foreach (var border in borders) - { - border.Delete(); - } - - e.Mobile.SendMessage("Spawner projection removed."); - return; - } - - var bounds = spawner.SpawnBounds; - if (bounds == default) - { - LogFailure("Spawner has no spawn bounds defined."); - return; - } - - borders = CreateBorders(spawner, bounds); - if (borders.Count == 0) - { - LogFailure("Could not create borders for spawner bounds."); - return; - } - - _projectedSpawners[spawner] = borders; - e.Mobile.SendMessage($"Spawner projection created with {borders.Count} border tiles."); - } - - private static List CreateBorders(Item spawner, Rectangle3D bounds) - { - var borders = new List(); - var map = spawner.Map; - var z = spawner.Z; - - var minX = bounds.Start.X; - var minY = bounds.Start.Y; - var maxX = bounds.End.X; // Exclusive - var maxY = bounds.End.Y; // Exclusive - - // NW Corner at (minX - 1, minY - 1) - borders.Add(CreateBorder(spawner, NorthWestCorner, minX - 1, minY - 1, z, map)); - - // SE Corner at (maxX - 1, maxY - 1) - borders.Add(CreateBorder(spawner, SouthEastCorner, maxX - 1, maxY - 1, z, map)); - - // North bound: y = minY - 1, x from minX to maxX - 1 - for (var x = minX; x < maxX; x++) - { - borders.Add(CreateBorder(spawner, NorthSouthWall, x, minY - 1, z, map)); - } - - // South bound: y = maxY - 1, x from minX to maxX - 2 (excluding SE corner) - for (var x = minX; x < maxX - 1; x++) - { - borders.Add(CreateBorder(spawner, NorthSouthWall, x, maxY - 1, z, map)); - } - - // West bound: x = minX - 1, y from minY to maxY - 1 - for (var y = minY; y < maxY; y++) - { - borders.Add(CreateBorder(spawner, EastWestWall, minX - 1, y, z, map)); - } - - // East bound: x = maxX - 1, y from minY to maxY - 2 (excluding SE corner) - for (var y = minY; y < maxY - 1; y++) - { - borders.Add(CreateBorder(spawner, EastWestWall, maxX - 1, y, z, map)); - } - - return borders; - } - - private static SpawnerBorder CreateBorder(Item spawner, int itemId, int x, int y, int z, Map map) - { - var border = new SpawnerBorder(itemId) - { - Spawner = spawner - }; - border.MoveToWorld(new Point3D(x, y, z), map); - return border; - } - - public static void RemoveProjection(Item spawner) - { - if (_projectedSpawners.Remove(spawner, out var borders)) - { - foreach (var border in borders) - { - border.Delete(); - } - } - } - - public static void RemoveBorderFromProjection(Item spawner, SpawnerBorder border) - { - if (_projectedSpawners.TryGetValue(spawner, out var borders)) - { - borders.Remove(border); - if (borders.Count == 0) - { - _projectedSpawners.Remove(spawner); - } - } - } -} - -[SerializationGenerator(0)] -public partial class SpawnerBorder : ProjectedItem -{ - public override string DefaultName => "Spawn Border"; - - [CommandProperty(AccessLevel.Developer, readOnly: true)] - public Item Spawner { get; set; } - - [Constructible] - public SpawnerBorder(int effectItemId) : base(effectItemId) - { - Hue = 0x3F; - MinimumVisible = AccessLevel.Developer; - } - - public override bool SkipSerialization => true; - - protected override bool SendEffect() - { - // Check if spawner was deleted - if (Spawner.Deleted) - { - ShowSpawnerBordersCommand.RemoveBorderFromProjection(Spawner, this); - Spawner = null; - Delete(); - return false; - } - - return base.SendEffect(); - } -} diff --git a/Projects/UOContent/Engines/Spawners/Commands/SpawnPropsCommand.cs b/Projects/UOContent/Engines/Spawners/Commands/SpawnPropsCommand.cs index 7eeaa06fc..dd1d834ee 100644 --- a/Projects/UOContent/Engines/Spawners/Commands/SpawnPropsCommand.cs +++ b/Projects/UOContent/Engines/Spawners/Commands/SpawnPropsCommand.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SpawnPropsCommand.cs * * * @@ -69,4 +69,4 @@ public class SpawnPropsCommand : BaseCommand from.SendGump(new SpawnPropsGump(from, targeted, _list)); } } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Engines/Spawners/Json/Point3DArrayConverter.cs b/Projects/UOContent/Engines/Spawners/Json/Point3DArrayConverter.cs deleted file mode 100644 index 0b6edb738..000000000 --- a/Projects/UOContent/Engines/Spawners/Json/Point3DArrayConverter.cs +++ /dev/null @@ -1,43 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Point3DArrayConverter.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Text.Json; -using System.Text.Json.Serialization; -using Server.Json; - -namespace Server.Engines.Spawners; - -/// -/// Writes a as the compact array form [x, y, z] used by the spawn -/// files (the default writes an object). Reading delegates to the -/// default converter, so array/object/string inputs all still load. -/// -public sealed class Point3DArrayConverter : JsonConverter -{ - private static readonly Point3DConverter _inner = new(); - - public override Point3D Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - _inner.Read(ref reader, typeToConvert, options); - - public override void Write(Utf8JsonWriter writer, Point3D value, JsonSerializerOptions options) - { - writer.WriteStartArray(); - writer.WriteNumberValue(value.X); - writer.WriteNumberValue(value.Y); - writer.WriteNumberValue(value.Z); - writer.WriteEndArray(); - } -} diff --git a/Projects/UOContent/Engines/Spawners/Json/SpawnerDto.cs b/Projects/UOContent/Engines/Spawners/Json/SpawnerDto.cs deleted file mode 100644 index dc5ef63f4..000000000 --- a/Projects/UOContent/Engines/Spawners/Json/SpawnerDto.cs +++ /dev/null @@ -1,217 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SpawnerDto.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; -using Server.Json; -using Server.Regions; - -namespace Server.Engines.Spawners; - -/// -/// Plain data carrier for spawner JSON. STJ deserializes into these records (never a live Item), -/// so a malformed file fails as GC only; builds the spawner from a -/// validated DTO. Options use WhenWritingDefault; mandatory fields force-write via -/// [JsonIgnore(Condition = Never)] and [JsonPropertyOrder] sets the on-disk order. -/// -public abstract record SpawnerDto -{ - // Mandatory: always written. - [JsonPropertyName("guid")] - [JsonPropertyOrder(0)] - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public Guid Guid { get; init; } - - [JsonPropertyName("name")] - [JsonPropertyOrder(1)] - public string Name { get; init; } - - [JsonPropertyName("location")] - [JsonPropertyOrder(2)] - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - [JsonConverter(typeof(Point3DArrayConverter))] - public Point3D Location { get; init; } - - [JsonPropertyName("map")] - [JsonPropertyOrder(3)] - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public Map Map { get; init; } - - [JsonPropertyName("count")] - [JsonPropertyOrder(4)] - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public int Count { get; init; } - - [JsonPropertyName("minDelay")] - [JsonPropertyOrder(5)] - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public TimeSpan MinDelay { get; init; } - - [JsonPropertyName("maxDelay")] - [JsonPropertyOrder(6)] - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public TimeSpan MaxDelay { get; init; } - - // Optional: omitted at default (spawnBounds/region live on the subtypes at order 8). - [JsonPropertyName("team")] - [JsonPropertyOrder(7)] - public int Team { get; init; } - - [JsonPropertyName("walkingRange")] - [JsonPropertyOrder(9)] - public int WalkingRange { get; init; } - - [JsonPropertyName("entries")] - [JsonPropertyOrder(10)] - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public List Entries { get; init; } - - [JsonPropertyName("spawnLocationIsHome")] - [JsonPropertyOrder(11)] - public bool SpawnLocationIsHome { get; init; } - - [JsonPropertyName("spawnPositionMode")] - [JsonPropertyOrder(12)] - public SpawnPositionMode SpawnPositionMode { get; init; } - - [JsonPropertyName("maxSpawnAttempts")] - [JsonPropertyOrder(13)] - public int MaxSpawnAttempts { get; init; } - - // Compact square-bounds form, -1 when absent. Written only when >= 0 (ShouldSerialize, since 0 - // is a valid radius WhenWritingDefault cannot emit); reconstructs SpawnBounds in ApplyDto. - [JsonPropertyName("homeRange")] - [JsonPropertyOrder(8)] - public int HomeRange { get; init; } = -1; - - /// Constructs the empty concrete spawner Item for this DTO. - protected abstract BaseSpawner CreateEmpty(); - - /// Builds and populates the live spawner. Override to apply subtype fields after base. - public virtual BaseSpawner ToSpawner() - { - var spawner = CreateEmpty(); - try - { - spawner.ApplyDto(this); - return spawner; - } - catch - { - // CreateEmpty registered the Item; delete it if population throws so no orphan escapes. - spawner.Delete(); - throw; - } - } -} - -[JsonDiscoverableType("Spawner")] -public sealed record SpawnerDataDto : SpawnerDto -{ - [JsonPropertyName("spawnBounds")] - [JsonPropertyOrder(8)] - public Rectangle3D SpawnBounds { get; init; } - - protected override BaseSpawner CreateEmpty() => new Spawner(); - - public override BaseSpawner ToSpawner() - { - var spawner = (Spawner)base.ToSpawner(); - try - { - if (SpawnBounds != default) - { - spawner.SpawnBounds = SpawnBounds; - } - - return spawner; - } - catch - { - spawner.Delete(); - throw; - } - } -} - -[JsonDiscoverableType("RegionSpawner")] -public sealed record RegionSpawnerDto : SpawnerDto -{ - [JsonPropertyName("region")] - [JsonPropertyOrder(8)] - public string Region { get; init; } - - protected override BaseSpawner CreateEmpty() => new RegionSpawner(); - - public override BaseSpawner ToSpawner() - { - var spawner = (RegionSpawner)base.ToSpawner(); - try - { - spawner.SpawnRegion = Server.Region.Find(Region, Map) as BaseRegion; - return spawner; - } - catch - { - spawner.Delete(); - throw; - } - } -} - -[JsonDiscoverableType("ProximitySpawner")] -public sealed record ProximitySpawnerDto : SpawnerDto -{ - [JsonPropertyName("spawnBounds")] - [JsonPropertyOrder(8)] - public Rectangle3D SpawnBounds { get; init; } - - [JsonPropertyName("triggerRange")] - [JsonPropertyOrder(14)] - public int TriggerRange { get; init; } - - [JsonPropertyName("spawnMessage")] - [JsonPropertyOrder(15)] - public TextDefinition SpawnMessage { get; init; } - - [JsonPropertyName("instant")] - [JsonPropertyOrder(16)] - public bool Instant { get; init; } - - protected override BaseSpawner CreateEmpty() => new ProximitySpawner(); - - public override BaseSpawner ToSpawner() - { - var spawner = (ProximitySpawner)base.ToSpawner(); - try - { - if (SpawnBounds != default) - { - spawner.SpawnBounds = SpawnBounds; - } - - spawner.TriggerRange = TriggerRange; - spawner.SpawnMessage = SpawnMessage; - spawner.InstantFlag = Instant; - return spawner; - } - catch - { - spawner.Delete(); - throw; - } - } -} diff --git a/Projects/UOContent/Engines/Spawners/ProximitySpawner.Dto.cs b/Projects/UOContent/Engines/Spawners/ProximitySpawner.Dto.cs deleted file mode 100644 index 219e91943..000000000 --- a/Projects/UOContent/Engines/Spawners/ProximitySpawner.Dto.cs +++ /dev/null @@ -1,45 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ProximitySpawner.Dto.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -namespace Server.Engines.Spawners; - -public partial class ProximitySpawner -{ - public override SpawnerDto ToDto() - { - var homeRange = DtoHomeRange; - return new ProximitySpawnerDto - { - Guid = Guid, - Name = DtoName, - Location = Location, - Map = Map, - Count = Count, - MinDelay = MinDelay, - MaxDelay = MaxDelay, - Team = Team, - WalkingRange = DtoWalkingRange, - Entries = Entries, - SpawnLocationIsHome = SpawnLocationIsHome, - SpawnPositionMode = DtoSpawnPositionMode, - MaxSpawnAttempts = DtoMaxSpawnAttempts, - HomeRange = homeRange, - SpawnBounds = homeRange >= 0 ? default : SpawnBounds, - TriggerRange = TriggerRange, - SpawnMessage = SpawnMessage, - Instant = InstantFlag - }; - } -} diff --git a/Projects/UOContent/Engines/Spawners/ProximitySpawner.cs b/Projects/UOContent/Engines/Spawners/ProximitySpawner.cs index 05395d7d1..ad1dde122 100644 --- a/Projects/UOContent/Engines/Spawners/ProximitySpawner.cs +++ b/Projects/UOContent/Engines/Spawners/ProximitySpawner.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ProximitySpawner.cs * * * @@ -14,7 +14,9 @@ *************************************************************************/ using System; +using System.Text.Json; using ModernUO.Serialization; +using Server.Json; using Server.Mobiles; namespace Server.Engines.Spawners; @@ -44,28 +46,65 @@ public partial class ProximitySpawner : Spawner { } + [Constructible(AccessLevel.Developer)] + public ProximitySpawner(int amount, int minDelay, int maxDelay, int team, int homeRange, string spawnName) + : base(amount, minDelay, maxDelay, team, homeRange, spawnName) + { + } + [Constructible(AccessLevel.Developer)] public ProximitySpawner( - int amount, - TimeSpan minDelay, - TimeSpan maxDelay, - int team = 0, - Rectangle3D spawnBounds = default, - int triggerRange = 0, - TextDefinition spawnMessage = null, - bool instantFlag = false, - params ReadOnlySpan spawnedNames - ) : base(amount, minDelay, maxDelay, team, spawnBounds, spawnedNames) + int amount, int minDelay, int maxDelay, int team, int homeRange, int triggerRange, + string spawnMessage, bool instantFlag, string spawnName + ) : base(amount, minDelay, maxDelay, team, homeRange, spawnName) + { + TriggerRange = triggerRange; + SpawnMessage = TextDefinition.Parse(spawnMessage); + InstantFlag = instantFlag; + } + + [Constructible(AccessLevel.Developer)] + public ProximitySpawner( + int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, + params string[] spawnedNames + ) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames) + { + } + + [Constructible(AccessLevel.Developer)] + public ProximitySpawner( + int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, int triggerRange, + TextDefinition spawnMessage, bool instantFlag, params string[] spawnedNames + ) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames) { TriggerRange = triggerRange; SpawnMessage = spawnMessage; InstantFlag = instantFlag; } + public ProximitySpawner(DynamicJson json, JsonSerializerOptions options) : base(json, options) + { + json.GetProperty("triggerRange", options, out int triggerRange); + json.GetProperty("spawnMessage", options, out TextDefinition spawnMessage); + json.GetProperty("instant", options, out bool instant); + + TriggerRange = triggerRange; + SpawnMessage = spawnMessage; + InstantFlag = instant; + } + public override string DefaultName => "Proximity Spawner"; public override bool HandlesOnMovement => Running; + public override void ToJson(DynamicJson json, JsonSerializerOptions options) + { + base.ToJson(json, options); + json.SetProperty("triggerRange", options, TriggerRange); + json.SetProperty("spawnMessage", options, SpawnMessage); + json.SetProperty("instant", options, InstantFlag); + } + public override void DoTimer(TimeSpan delay) { if (!Running) diff --git a/Projects/UOContent/Engines/Spawners/RegionSpawner.Dto.cs b/Projects/UOContent/Engines/Spawners/RegionSpawner.Dto.cs deleted file mode 100644 index 3d9614247..000000000 --- a/Projects/UOContent/Engines/Spawners/RegionSpawner.Dto.cs +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: RegionSpawner.Dto.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -namespace Server.Engines.Spawners; - -public partial class RegionSpawner -{ - // RegionSpawner spawns from region rectangles, so it writes neither homeRange nor spawnBounds. - public override SpawnerDto ToDto() => new RegionSpawnerDto - { - Guid = Guid, - Name = DtoName, - Location = Location, - Map = Map, - Count = Count, - MinDelay = MinDelay, - MaxDelay = MaxDelay, - Team = Team, - WalkingRange = DtoWalkingRange, - Entries = Entries, - SpawnLocationIsHome = SpawnLocationIsHome, - SpawnPositionMode = DtoSpawnPositionMode, - MaxSpawnAttempts = DtoMaxSpawnAttempts, - Region = SpawnRegion?.Name - }; -} diff --git a/Projects/UOContent/Engines/Spawners/RegionSpawner.cs b/Projects/UOContent/Engines/Spawners/RegionSpawner.cs index 37b3d098e..90deae17f 100644 --- a/Projects/UOContent/Engines/Spawners/RegionSpawner.cs +++ b/Projects/UOContent/Engines/Spawners/RegionSpawner.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: RegionSpawner.cs * * * @@ -14,7 +14,9 @@ *************************************************************************/ using System; +using System.Text.Json; using ModernUO.Serialization; +using Server.Json; using Server.Regions; namespace Server.Engines.Spawners; @@ -27,8 +29,6 @@ public partial class RegionSpawner : Spawner private BaseRegion _spawnRegion; - public override Region Region => _spawnRegion; - [Constructible(AccessLevel.Developer)] public RegionSpawner() { @@ -41,15 +41,36 @@ public partial class RegionSpawner : Spawner [Constructible(AccessLevel.Developer)] public RegionSpawner( - int amount, - TimeSpan minDelay, - TimeSpan maxDelay, - int team = 0, - params ReadOnlySpan spawnedNames - ) : base(amount, minDelay, maxDelay, team, spawnedNames: spawnedNames) + int amount, int minDelay, int maxDelay, int team, int homeRange, + params string[] spawnedNames + ) : this( + amount, + TimeSpan.FromMinutes(minDelay), + TimeSpan.FromMinutes(maxDelay), + team, + homeRange, + spawnedNames + ) { } + [Constructible(AccessLevel.Developer)] + public RegionSpawner( + int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, + params string[] spawnedNames + ) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames) + { + } + + public RegionSpawner(DynamicJson json, JsonSerializerOptions options) : base(json, options) + { + json.GetProperty("map", options, out Map map); + json.GetProperty("region", options, out string spawnRegion); + + _spawnRegion = Region.Find(spawnRegion, map) as BaseRegion; + _spawnRegion?.InitRectangles(); + } + [CommandProperty(AccessLevel.Developer)] public BaseRegion SpawnRegion { @@ -63,46 +84,10 @@ public partial class RegionSpawner : Spawner } } - // RegionSpawner does not support spiral scan (disjoint rectangles make it ineffective) - protected override bool SupportsSpiralScan => false; - - protected override Rectangle3D GetBoundsForSpawnAttempt() + public override void ToJson(DynamicJson json, JsonSerializerOptions options) { - if (_spawnRegion == null || _spawnRegion.TotalWeight <= 0) - { - return default; - } - - // Pick a weighted random rectangle from the region - var rand = Utility.Random(_spawnRegion.TotalWeight); - - for (var j = 0; j < _spawnRegion.RectangleWeights.Length; j++) - { - var curWeight = _spawnRegion.RectangleWeights[j]; - - if (rand < curWeight) - { - return _spawnRegion.Rectangles[j]; - } - - rand -= curWeight; - } - - return default; - } - - protected override ReadOnlySpan GetAllSpawnBounds() => _spawnRegion?.Rectangles; - - public override Point3D GetSpawnPosition(ISpawnable spawned, Map map) - { - // Check for region/map mismatch before delegating to base - if (_spawnRegion == null || map == null || map == Map.Internal || - map != _spawnRegion.Map || _spawnRegion.TotalWeight <= 0) - { - return Location; - } - - return base.GetSpawnPosition(spawned, map); + base.ToJson(json, options); + json.SetProperty("region", options, SpawnRegion.Name); } public override void GetSpawnerProperties(IPropertyList list) @@ -115,6 +100,84 @@ public partial class RegionSpawner : Spawner } } + public override Point3D GetSpawnPosition(ISpawnable spawned, Map map) + { + if (_spawnRegion == null || map == null || map == Map.Internal || map != _spawnRegion.Map || + _spawnRegion.TotalWeight <= 0) + { + return Location; + } + + bool waterMob, waterOnlyMob; + + if (spawned is Mobile mob) + { + waterMob = mob.CanSwim; + waterOnlyMob = mob.CanSwim && mob.CantWalk; + } + else + { + waterMob = false; + waterOnlyMob = false; + } + + // Try 10 times to find a valid location. + for (var i = 0; i < 10; i++) + { + var rand = Utility.Random(_spawnRegion.TotalWeight); + + var x = int.MinValue; + var y = int.MinValue; + + for (var j = 0; j < _spawnRegion.RectangleWeights.Length; j++) + { + var curWeight = _spawnRegion.RectangleWeights[j]; + + if (rand < curWeight) + { + var rect = _spawnRegion.Rectangles[j]; + + x = rect.Start.X + rand % rect.Width; + y = rect.Start.Y + rand / rect.Width; + + break; + } + + rand -= curWeight; + } + + var mapZ = map.GetAverageZ(x, y); + + if (waterMob) + { + if (IsValidWater(map, x, y, Z)) + { + return new Point3D(x, y, Z); + } + + if (IsValidWater(map, x, y, mapZ)) + { + return new Point3D(x, y, mapZ); + } + } + + if (!waterOnlyMob) + { + if (map.CanSpawnMobile(x, y, Z)) + { + return new Point3D(x, y, Z); + } + + if (map.CanSpawnMobile(x, y, mapZ)) + { + return new Point3D(x, y, mapZ); + } + } + } + + return HomeLocation; + } + [AfterDeserialization(false)] private void AfterDeserialization() { diff --git a/Projects/UOContent/Engines/Spawners/SectorSpawnCache.cs b/Projects/UOContent/Engines/Spawners/SectorSpawnCache.cs deleted file mode 100644 index 42fc71fc2..000000000 --- a/Projects/UOContent/Engines/Spawners/SectorSpawnCache.cs +++ /dev/null @@ -1,376 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SectorSpawnCache.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Server.Collections; -using Server.Regions; - -namespace Server.Engines.Spawners; - -/// -/// Global manager for sector-based spawn position caching. -/// Shared across all spawners for efficient memory usage and house invalidation. -/// Uses separate caches for land and water to minimize memory usage since most -/// sectors are either all land or all water. -/// -public static class SectorSpawnCacheManager -{ - private static readonly Dictionary<(Map, int, int), BitMask256> _landCaches = []; - private static readonly Dictionary<(Map, int, int), BitMask256> _waterCaches = []; - - /// - /// Marks a position as valid for spawning in the global cache. - /// - /// The map containing the position - /// The valid spawn position - /// True for water mob, false for land mob - public static void SetValid(Map map, Point3D pos, bool isWater) - { - var sectorX = pos.X >> Map.SectorShift; - var sectorY = pos.Y >> Map.SectorShift; - var bitIndex = (pos.X & (Map.SectorSize - 1)) + ((pos.Y & (Map.SectorSize - 1)) << Map.SectorShift); - - var caches = isWater ? _waterCaches : _landCaches; - ref var cache = ref CollectionsMarshal.GetValueRefOrAddDefault(caches, (map, sectorX, sectorY), out _); - cache.SetBit(bitIndex); - } - - /// - /// Attempts to get a random valid position from cached sectors within the specified bounds. - /// - /// The map to search - /// The spawn bounds to search within - /// True for water mob, false for land mob - /// The selected position (X, Y only - caller must verify Z) - /// True if a cached position was found - public static bool TryGetRandomPosition(Map map, Rectangle3D bounds, bool isWater, out Point2D pos) - { - ReadOnlySpan singleBounds = [bounds]; - return TryGetRandomPosition(map, singleBounds, isWater, out pos, out _); - } - - /// - /// Attempts to get a random valid position from cached sectors across multiple bounds. - /// Deduplicates overlapping sectors for uniform distribution. - /// - /// The map to search - /// All spawn bounds to search within - /// True for water mob, false for land mob - /// The selected position (X, Y only - caller must verify Z) - /// The bounds rectangle containing the selected position - /// Maximum retries if selected position is outside bounds - /// True if a cached position was found - public static bool TryGetRandomPosition( - Map map, - ReadOnlySpan allBounds, - bool isWater, - out Point2D pos, - out Rectangle3D containingBounds, - int maxRetries = 5) - { - pos = Point2D.Zero; - containingBounds = default; - - if (allBounds.Length == 0) - { - return false; - } - - var caches = isWater ? _waterCaches : _landCaches; - - // Collect unique sectors and their counts - using var sectorList = PooledRefList<(int sx, int sy, int count)>.Create(); - var totalPositions = 0; - - for (var i = 0; i < allBounds.Length; i++) - { - var bounds = allBounds[i]; - var startSectorX = bounds.Start.X >> Map.SectorShift; - var startSectorY = bounds.Start.Y >> Map.SectorShift; - var endSectorX = (bounds.End.X - 1) >> Map.SectorShift; - var endSectorY = (bounds.End.Y - 1) >> Map.SectorShift; - - for (var sx = startSectorX; sx <= endSectorX; sx++) - { - for (var sy = startSectorY; sy <= endSectorY; sy++) - { - // Check if we've already added this sector - var alreadyAdded = false; - for (var j = 0; j < sectorList.Count; j++) - { - var (checkSX, checkSY, _) = sectorList[j]; - if (checkSX == sx && checkSY == sy) - { - alreadyAdded = true; - break; - } - } - - if (alreadyAdded) - { - continue; - } - - if (caches.TryGetValue((map, sx, sy), out var cache)) - { - var count = cache.PopCount(); - if (count > 0) - { - sectorList.Add((sx, sy, count)); - totalPositions += count; - } - } - } - } - } - - if (totalPositions == 0) - { - return false; - } - - // Retry loop for when selected position is outside all bounds - for (var attempt = 0; attempt <= maxRetries; attempt++) - { - // Pick a random position - var targetIndex = Utility.Random(totalPositions); - - // Find the sector containing that index - var currentIndex = 0; - for (var i = 0; i < sectorList.Count; i++) - { - var (sx, sy, sectorCount) = sectorList[i]; - if (targetIndex < currentIndex + sectorCount) - { - // Target is in this sector - look up cache again - if (!caches.TryGetValue((map, sx, sy), out var cache)) - { - break; // Should not happen, try again - } - - var indexInSector = targetIndex - currentIndex; - var bitPosition = cache.GetNthSetBit(indexInSector); - - var localX = bitPosition & (Map.SectorSize - 1); - var localY = bitPosition >> Map.SectorShift; - - pos = new Point2D((sx << Map.SectorShift) + localX, (sy << Map.SectorShift) + localY); - - // Find which bounds contains this position - for (var j = 0; j < allBounds.Length; j++) - { - var bounds = allBounds[j]; - if (pos.X >= bounds.Start.X && pos.X < bounds.End.X && - pos.Y >= bounds.Start.Y && pos.Y < bounds.End.Y) - { - containingBounds = bounds; - return true; - } - } - - // Position outside all bounds - retry - break; - } - - currentIndex += sectorCount; - } - } - - return false; - } - - /// - /// Checks if a position is blocked by a private house. - /// Public AoS houses (with unlocked doors) allow spawning. - /// - /// The map to check - /// X coordinate - /// Y coordinate - /// Z coordinate - /// True if blocked by a private house - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsBlockedByHouse(Map map, int x, int y, int z) - { - if (Region.Find(new Point3D(x, y, z), map) is HouseRegion houseRegion) - { - var house = houseRegion.House; - // Allow spawning in public AoS houses (unlocked doors, free entry) - return !(house.IsAosRules && house.Public); - } - - return false; - } - - /// - /// Invalidates all cached data for sectors within the specified bounds. - /// Called when houses are placed or demolished. - /// - /// The map to invalidate - /// The affected area - public static void InvalidateSectors(Map map, Rectangle2D bounds) - { - var startSectorX = bounds.Start.X >> Map.SectorShift; - var startSectorY = bounds.Start.Y >> Map.SectorShift; - var endSectorX = bounds.End.X >> Map.SectorShift; - var endSectorY = bounds.End.Y >> Map.SectorShift; - - for (var sx = startSectorX; sx <= endSectorX; sx++) - { - for (var sy = startSectorY; sy <= endSectorY; sy++) - { - var key = (map, sx, sy); - _landCaches.Remove(key); - _waterCaches.Remove(key); - } - } - } - - /// - /// Performs incremental spiral scanning to find and cache valid spawn positions. - /// - /// The map to scan - /// The center point to spiral from - /// The spawn bounds to stay within - /// Minimum Z for spawn checks - /// Maximum Z for spawn checks - /// Whether to find water positions - /// Whether the mob can't walk (water-only) - /// Current ring being scanned (updated on return) - /// Position within current ring (updated on return) - /// Number of rings to scan per call - /// True if scan is complete (exhausted bounds) - public static bool ContinueSpiralScan( - Map map, - Point3D center, - Rectangle3D bounds, - int minZ, - int maxZ, - bool canSwim, - bool cantWalk, - ref int currentRing, - ref int ringPosition, - int ringsPerTick = 3) - { - var maxRing = Math.Max(bounds.Width, bounds.Height) / 2 + 1; - var ringsToScan = Math.Min(currentRing + ringsPerTick, maxRing); - - while (currentRing < ringsToScan) - { - // Ring 0 is just the center point - if (currentRing == 0) - { - CheckAndCachePosition(map, center.X, center.Y, minZ, maxZ, bounds, canSwim, cantWalk); - } - else - { - // Ring N has 8*N positions - var positionsInRing = currentRing * 8; - for (var p = 0; p < positionsInRing; p++) - { - var (dx, dy) = GetSpiralOffset(currentRing, p); - var x = center.X + dx; - var y = center.Y + dy; - - CheckAndCachePosition(map, x, y, minZ, maxZ, bounds, canSwim, cantWalk); - } - } - - currentRing++; - } - - ringPosition = 0; - return currentRing >= maxRing; - } - - private static void CheckAndCachePosition( - Map map, - int x, int y, - int minZ, int maxZ, - Rectangle3D bounds, - bool canSwim, - bool cantWalk) - { - // Check bounds - if (x < bounds.Start.X || x >= bounds.End.X || - y < bounds.Start.Y || y >= bounds.End.Y) - { - return; - } - - // Check if position is valid for spawning - if (map.CanSpawnMobile(x, y, minZ, maxZ, canSwim, cantWalk, out var spawnZ)) - { - // Skip positions inside private houses - if (IsBlockedByHouse(map, x, y, spawnZ)) - { - return; - } - - var pos = new Point3D(x, y, spawnZ); - var isWater = canSwim && cantWalk; - SetValid(map, pos, isWater); - } - } - - /// - /// Gets the X,Y offset for a position in a spiral ring. - /// Ring 0 = center (no offset) - /// Ring 1 = 8 positions around center - /// Ring N = 8*N positions - /// - public static (int dx, int dy) GetSpiralOffset(int ring, int position) - { - // Each ring has 4 sides, each side has ring*2 positions - var sideLength = ring * 2; - var side = position / sideLength; - var sidePos = position % sideLength; - - return side switch - { - 0 => (-ring + sidePos, -ring), // Top edge, left to right - 1 => (ring, -ring + sidePos), // Right edge, top to bottom - 2 => (ring - sidePos, ring), // Bottom edge, right to left - 3 => (-ring, ring - sidePos), // Left edge, bottom to top - _ => (0, 0) - }; - } - - /// - /// Clears all cached data. Used for testing or server restart. - /// - public static void ClearAll() - { - _landCaches.Clear(); - _waterCaches.Clear(); - } - - /// - /// Gets the number of sectors currently cached (land + water). - /// - public static int CachedSectorCount => _landCaches.Count + _waterCaches.Count; - - /// - /// Gets the number of land sectors currently cached. - /// - public static int LandCacheCount => _landCaches.Count; - - /// - /// Gets the number of water sectors currently cached. - /// - public static int WaterCacheCount => _waterCaches.Count; -} diff --git a/Projects/UOContent/Engines/Spawners/SpawnPositionState.cs b/Projects/UOContent/Engines/Spawners/SpawnPositionState.cs deleted file mode 100644 index 9d8dad696..000000000 --- a/Projects/UOContent/Engines/Spawners/SpawnPositionState.cs +++ /dev/null @@ -1,80 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SpawnPositionState.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -namespace Server.Engines.Spawners; - -/// -/// Runtime state for spawn position optimization. -/// Not serialized - resets on server restart. -/// -public sealed class SpawnPositionState -{ - // Tunable thresholds for auto-detection - private const int FailureThreshold = 5; - private const int AbandonThreshold = 25; - - // Failure tracking for auto-detection - private int _nonTransientFailures; - - // Abandon tracking - counts consecutive cache misses or Location-only results - private int _consecutiveUselessResults; - - // Spiral scan state - public int SpiralRing; - public int SpiralRingPosition; - public bool SpiralComplete; - - /// - /// Resets all state. Called when spawner moves or bounds change. - /// - public void Reset() - { - _nonTransientFailures = 0; - _consecutiveUselessResults = 0; - SpiralRing = 0; - SpiralRingPosition = 0; - SpiralComplete = false; - } - - /// - /// Records a useful cache hit (position other than spawner's own location). - /// Resets the abandon counter. - /// - public void RecordUsefulCacheHit() => _consecutiveUselessResults = 0; - - /// - /// Records a useless result (cache miss or cache returned spawner's location). - /// Counts toward abandonment threshold. - /// - public void RecordUselessResult() => _consecutiveUselessResults++; - - /// - /// Records a non-transient spawn failure for auto-detection. - /// - public void RecordNonTransientFailure() => _nonTransientFailures++; - - /// - /// Returns true if the spawner should cache successful positions. - /// - public bool ShouldCachePositions(SpawnPositionMode mode) => - mode == SpawnPositionMode.Enabled || mode == SpawnPositionMode.Automatic && _nonTransientFailures > FailureThreshold; - - /// - /// Returns true if the spawner should be marked as abandoned. - /// Triggers after spiral completes, and we get consecutive useless results - /// (cache misses or cache only returning spawner's own location). - /// - public bool ShouldAbandon() => SpiralComplete && _consecutiveUselessResults >= AbandonThreshold; -} diff --git a/Projects/UOContent/Engines/Spawners/SpawnPropsGump.cs b/Projects/UOContent/Engines/Spawners/SpawnPropsGump.cs index 4a6732ec9..f5a4e81f8 100644 --- a/Projects/UOContent/Engines/Spawners/SpawnPropsGump.cs +++ b/Projects/UOContent/Engines/Spawners/SpawnPropsGump.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GlobalPropsGump.cs * * * @@ -41,14 +41,24 @@ public class SpawnPropsGump : PropertiesGump private List _spawners; - public SpawnPropsGump(Mobile mobile, object o, List spawners) : base(mobile, o) => _spawners = spawners; + public SpawnPropsGump(Mobile mobile, object o, List spawners) : base(mobile, o) + { + _spawners = spawners; + } - public SpawnPropsGump(Mobile mobile, object o, Stack stack, StackEntry parent, List spawners) - : base(mobile, o, stack, parent) => _spawners = spawners; + public SpawnPropsGump(Mobile mobile, object o, Stack stack, StackEntry parent, List spawners) : base( + mobile, o, stack, parent + ) + { + _spawners = spawners; + } - public SpawnPropsGump( - Mobile mobile, object o, Stack stack, List list, int page, List spawners - ) : base(mobile, o, stack, list, page) => _spawners = spawners; + public SpawnPropsGump(Mobile mobile, object o, Stack stack, List list, int page, List spawners) : base( + mobile, o, stack, list, page + ) + { + _spawners = spawners; + } protected override int TotalHeight => base.TotalHeight + PropsConfig.ApplySize; @@ -57,7 +67,7 @@ public class SpawnPropsGump : PropertiesGump base.Initialize(page); var totalHeight = TotalHeight - PropsConfig.ApplySize; - AddButton(BackWidth / 3, PropsConfig.BorderSize * 2 + totalHeight, 5204, 5205, 3); + AddButton(BackWidth / 3, PropsConfig.BorderSize + totalHeight + PropsConfig.BorderSize, 5204, 5205, 3); } public override void SendPropertiesGump() => @@ -86,7 +96,7 @@ public class SpawnPropsGump : PropertiesGump case 3: // Apply { using var propsBuilder = ValueStringBuilder.Create(); - var first = true; + bool first = true; foreach (var attr in MobileAttributes) { var prop = GetPropValue(m_Object, attr); @@ -101,7 +111,9 @@ public class SpawnPropsGump : PropertiesGump propsBuilder.Append(' '); } - propsBuilder.Append($"{attr} {prop}"); + propsBuilder.Append(attr); + propsBuilder.Append(' '); + propsBuilder.Append(prop.ToString()); // TODO: Replace with ZString, or IFormatter code } } diff --git a/Projects/UOContent/Engines/Spawners/Spawner.Dto.cs b/Projects/UOContent/Engines/Spawners/Spawner.Dto.cs deleted file mode 100644 index a291a4854..000000000 --- a/Projects/UOContent/Engines/Spawners/Spawner.Dto.cs +++ /dev/null @@ -1,42 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Spawner.Dto.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -namespace Server.Engines.Spawners; - -public partial class Spawner -{ - public override SpawnerDto ToDto() - { - var homeRange = DtoHomeRange; - return new SpawnerDataDto - { - Guid = Guid, - Name = DtoName, - Location = Location, - Map = Map, - Count = Count, - MinDelay = MinDelay, - MaxDelay = MaxDelay, - Team = Team, - WalkingRange = DtoWalkingRange, - Entries = Entries, - SpawnLocationIsHome = SpawnLocationIsHome, - SpawnPositionMode = DtoSpawnPositionMode, - MaxSpawnAttempts = DtoMaxSpawnAttempts, - HomeRange = homeRange, - SpawnBounds = homeRange >= 0 ? default : SpawnBounds - }; - } -} diff --git a/Projects/UOContent/Engines/Spawners/Spawner.cs b/Projects/UOContent/Engines/Spawners/Spawner.cs index db25cab55..5a5b1d1f2 100644 --- a/Projects/UOContent/Engines/Spawners/Spawner.cs +++ b/Projects/UOContent/Engines/Spawners/Spawner.cs @@ -1,38 +1,13 @@ using System; +using System.Text.Json; using ModernUO.Serialization; +using Server.Json; namespace Server.Engines.Spawners; -[SerializationGenerator(1)] +[SerializationGenerator(0)] public partial class Spawner : BaseSpawner { - /// - /// When true, enables proactive spiral scanning to find valid spawn positions. - /// Only relevant when SpawnPositionMode is Automatic or Enabled. - /// - private bool ShouldSerializeUseSpiralScan() => _useSpiralScan; - - [SerializableField(0)] - [SaveFlag(nameof(ShouldSerializeUseSpiralScan))] - [SerializedCommandProperty(AccessLevel.Developer)] - private bool _useSpiralScan; - - private bool ShouldSerializeSpawnBounds() => _spawnBounds != default; - - [SerializableProperty(1)] - [SaveFlag(nameof(ShouldSerializeSpawnBounds))] - [CommandProperty(AccessLevel.Developer)] - public override Rectangle3D SpawnBounds - { - get => _spawnBounds; - set - { - _spawnBounds = value; - InvalidateProperties(); - this.MarkDirty(); - } - } - [Constructible(AccessLevel.Developer)] public Spawner() { @@ -45,26 +20,126 @@ public partial class Spawner : BaseSpawner [Constructible(AccessLevel.Developer)] public Spawner( - int amount, - TimeSpan minDelay, - TimeSpan maxDelay, - int team = 0, - Rectangle3D spawnBounds = default, - params ReadOnlySpan spawnedNames - ) : base(amount, minDelay, maxDelay, team, spawnBounds, spawnedNames) + int amount, int minDelay, int maxDelay, int team, int homeRange, + params string[] spawnedNames + ) : this( + amount, + TimeSpan.FromMinutes(minDelay), + TimeSpan.FromMinutes(maxDelay), + team, + homeRange, + spawnedNames + ) { } - public override Region Region => Region.Find(Location, Map); - - protected override bool SupportsSpiralScan => _useSpiralScan; - - protected override Rectangle3D GetBoundsForSpawnAttempt() => SpawnBounds; - - protected override ReadOnlySpan GetAllSpawnBounds() => new(ref _spawnBounds); - - private void MigrateFrom(V0Content content) + [Constructible(AccessLevel.Developer)] + public Spawner( + int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, + params string[] spawnedNames + ) : base(amount, minDelay, maxDelay, team, homeRange, spawnedNames) { - // V0 had no fields in Spawner, new v1 field _useSpiralScan defaults to false + } + + public Spawner(DynamicJson json, JsonSerializerOptions options) : base(json, options) + { + } + + public static bool IsValidWater(Map map, int x, int y, int z) + { + if (!Region.Find(new Point3D(x, y, z), map).AllowSpawn() || !map.CanFit(x, y, z, 16, false, true, false)) + { + return false; + } + + var landTile = map.Tiles.GetLandTile(x, y); + + if (landTile.Z == z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Wet) != 0) + { + return true; + } + + foreach (var staticTile in map.Tiles.GetStaticAndMultiTiles(x, y)) + { + if (staticTile.Z == z && TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Wet) + { + return true; + } + } + + return false; + } + + /* + public override bool OnDefragSpawn(ISpawnable spawned, bool remove) + { + // To despawn a mob that was lured 4x away from its spawner + // TODO: Move this to a config + if (spawned is BaseCreature c && c.Combatant == null && c.GetDistanceToSqrt( Location ) > c.RangeHome * 4) + { + c.Delete(); + remove = true; + } + + return base.OnDefragSpawn(entry, spawned, remove); + } + */ + + public override Point3D GetSpawnPosition(ISpawnable spawned, Map map) + { + if (map == null || map == Map.Internal) + { + return Location; + } + + bool waterMob, waterOnlyMob; + + if (spawned is Mobile mob) + { + waterMob = mob.CanSwim; + waterOnlyMob = mob.CanSwim && mob.CantWalk; + } + else + { + waterMob = false; + waterOnlyMob = false; + } + + // Try 10 times to find a valid location. + for (var i = 0; i < 10; i++) + { + var x = Location.X + (Utility.Random(HomeRange * 2 + 1) - HomeRange); + var y = Location.Y + (Utility.Random(HomeRange * 2 + 1) - HomeRange); + + var mapZ = map.GetAverageZ(x, y); + + if (waterMob) + { + if (IsValidWater(map, x, y, Z)) + { + return new Point3D(x, y, Z); + } + + if (IsValidWater(map, x, y, mapZ)) + { + return new Point3D(x, y, mapZ); + } + } + + if (!waterOnlyMob) + { + if (map.CanSpawnMobile(x, y, Z)) + { + return new Point3D(x, y, Z); + } + + if (map.CanSpawnMobile(x, y, mapZ)) + { + return new Point3D(x, y, mapZ); + } + } + } + + return HomeLocation; } } diff --git a/Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs b/Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs index 787ef9ab4..8440c5a65 100644 --- a/Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs +++ b/Projects/UOContent/Engines/Spawners/SpawnerControllerGump.cs @@ -1,22 +1,7 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SpawnerControllerGump.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - using System; using Server.Collections; -using Server.Engines.Spawners; using Server.Network; +using Server.Engines.Spawners; namespace Server.Gumps; @@ -27,34 +12,23 @@ public enum SpawnSearchType Props, Name } - public class SpawnSearch { public SpawnSearchType Type { get; set; } = SpawnSearchType.Creature; public string SearchPattern { get; set; } = ""; } -public class SpawnerControllerGump : DynamicGump +public class SpawnerControllerGump : GumpGrid { - // Grid dimensions - private const int GumpWidth = 1000; - private const int GumpHeight = 800; - private const int RowHeight = 45; - private const int HeaderHeight = 28; - private const int EntryCount = 5; - private const int TypeCount = 15; - - // Column spec for the 12-column list - private const string ColumnSpec = "6* 8* 18* 6* 12* 6* 5* 5* 8* 8* 9* *"; - // Row spec for main grid: search (10%), list (*), footer (100px) - private const string MainRowSpec = "10* * 100"; - - private readonly Mobile _mobile; - private int _page; - private readonly SpawnSearch _search; private BaseSpawner _copy; - private BaseSpawner[] _spawners = Array.Empty(); + private int _page; + private const int EntryCount = 5; + private Mobile _mobile; + private SpawnSearch _search; private int _lineCount; + private BaseSpawner[] _spawners = Array.Empty(); + private Grid _main; + private const int TypeCount = 15; public static int GetButtonID(int type, int index) => 1 + type + index * TypeCount; @@ -71,339 +45,97 @@ public class SpawnerControllerGump : DynamicGump e.Mobile.SendGump(new SpawnerControllerGump(e.Mobile)); } - public SpawnerControllerGump(Mobile mobile, int page = 0, BaseSpawner copy = null, SpawnSearch search = null) - : base(20, 30) + public void AddBlackAlpha(int x, int y, int width, int height) { - _mobile = mobile; - _page = page; - _copy = copy; - _search = search ?? new SpawnSearch(); - - SearchSpawner(); + AddImageTiled(x, y, width, height, 2624); + AddAlphaRegion(x, y, width, height); } - protected override void BuildLayout(ref DynamicGumpBuilder builder) + public void DrawBorder(ListView list) { - builder.AddPage(); + var row1Y = _main.Rows[1].Y; + AddImageTiled(0, row1Y, _main.Width, 3, 9357); + AddAlphaRegion(0, row1Y, _main.Width, 3); - // Background - builder.AddBackground(0, 0, GumpWidth, GumpHeight, 5054); - builder.AddImageTiled(0, 0, GumpWidth, GumpHeight, 2624); - builder.AddAlphaRegion(0, 0, GumpWidth, GumpHeight); + AddImageTiled(0, row1Y + 21, _main.Width, 3, 9357); + AddAlphaRegion(0, row1Y + 21, _main.Width, 3); - // Calculate main grid: 1 column x 3 rows - Span mainRowPos = stackalloc int[3]; - Span mainRowHeights = stackalloc int[3]; - - Span mainRowSpecs = stackalloc GridSizeSpec[3]; - GridSizeSpec.ParseAll(MainRowSpec, mainRowSpecs); - GridCalculator.ComputeTrackSizes(mainRowSpecs, GumpHeight, 0, mainRowPos, mainRowHeights); - - // Calculate list view layout - Span listColPos = stackalloc int[12]; - Span listColWidths = stackalloc int[12]; - - var listView = ListViewLayout.Create( - 0, - mainRowPos[1], - GumpWidth, - mainRowHeights[1] - 7, // marginY adjustment - _spawners.Length, - _page, - RowHeight, - HeaderHeight, - ColumnSpec, - listColPos, - listColWidths - ); - - _lineCount = listView.ItemsPerPage; - - // Draw borders - var row1Y = mainRowPos[1]; - var row2Y = mainRowPos[2]; - builder.AddImageTiled(0, row1Y, GumpWidth, 3, 9357); - builder.AddAlphaRegion(0, row1Y, GumpWidth, 3); - builder.AddImageTiled(0, row1Y + 21, GumpWidth, 3, 9357); - builder.AddAlphaRegion(0, row1Y + 21, GumpWidth, 3); - - for (var i = 0; i < listView.ColumnCount - 1; i++) + for (int i = 0; i < list.ColsCount - 1; i++) { - var x = listColPos[i] + listColWidths[i] - 8; - var y = mainRowPos[1] + 3; - builder.AddImageTiled(x, y, 3, 20, 9355); - builder.AddAlphaRegion(x, y, 3, 20); + var x = list.Header.Cols[i].HEnd - 8; + var y = list.Header.Y + 10; + var height = 20; + AddImageTiled(x, y, 3, height, 9355); + AddAlphaRegion(x, y, 3, height); } - builder.AddImageTiled(0, row2Y - 11, GumpWidth, 6, 9357); - builder.AddAlphaRegion(0, row2Y - 11, GumpWidth, 6); - builder.AddImageTiled(0, row2Y + 55, GumpWidth, 6, 9357); - builder.AddAlphaRegion(0, row2Y + 55, GumpWidth, 6); - - // Draw search area - DrawSearch(ref builder, mainRowPos[0], mainRowHeights[0]); - - // Draw column headers - ReadOnlySpan headers = - [ - "Copy", "Paste", "Name/Serial", "Map", "Coords", - "Entry", "Walk", "Home", "Min Delay", "Max Delay", - "Next Spawn", "Actions" - ]; - - for (var i = 0; i < listView.ColumnCount && i < headers.Length; i++) - { - var cell = listView.GetHeaderCell(i, listColPos, listColWidths); - var offsetX = i >= 4 ? -4 : 0; - builder.AddHtml( - cell.X + offsetX, - cell.Y + 3, - cell.Width, - 30, - headers[i], - GumpTextColors.Gold, - 4, - align: TextAlignment.Center - ); - } - - // Draw spawner rows - const int vCenter = 10; - for (var i = 0; i < listView.VisibleCount; i++) - { - var dataIndex = listView.GetDataIndex(i); - var spawner = _spawners[dataIndex]; - var rowY = listView.GetRowY(i); - - // Column 0: Copy button - var col0X = listColPos[0]; - var col0Width = listColWidths[0]; - builder.AddButton(col0X + 15, rowY, 4011, 4012, GetButtonID(2, dataIndex)); - builder.AddHtml(col0X, rowY + 21, col0Width, 30, "copy", GumpTextColors.White, 3, align: TextAlignment.Center); - - // Column 1: Paste buttons - var col1X = listColPos[1]; - var col1Width = listColWidths[1]; - var col1HCenter = col1X + col1Width / 2; - builder.AddButton(col1X + 2, rowY, 4029, 4031, GetButtonID(3, dataIndex)); - builder.AddHtml(col1X + 3, rowY + 21, col1Width, 30, "props", GumpTextColors.White, 3); - - builder.AddButton(col1HCenter - 2, rowY, 4029, 4031, GetButtonID(4, dataIndex)); - builder.AddHtml(col1HCenter - 3, rowY + 21, 55, 30, "entry", GumpTextColors.White, 3); - - // Column 2: Name/Serial - var col2X = listColPos[2]; - var col2Width = listColWidths[2]; - var entryIndex = i * EntryCount; - - builder.AddImageTiled(col2X + 6, rowY + 13, col2Width - 8, 1, 9357); - builder.AddTextEntry(col2X + 8, rowY - 3, col2Width - 8, 80, GumpHues.White, entryIndex, spawner.Name); - builder.AddButton(col2X + 8, rowY + 19, 5837, 5838, GetButtonID(6, dataIndex)); - builder.AddHtml(col2X + 54, rowY + 19, col2Width, 30, $"{spawner.Serial}", GumpTextColors.White, 4); - - // Column 3: Map - var col3X = listColPos[3]; - var col3Width = listColWidths[3]; - builder.AddHtml( - col3X, - rowY + vCenter, - col3Width, - 30, - $"{spawner.Map}", - GumpTextColors.White, - 4, - align: TextAlignment.Center - ); - - // Column 4: Coords with teleport button - var col4X = listColPos[4]; - var col4Width = listColWidths[4]; - builder.AddButton(col4X + 3, rowY + vCenter, 2062, 2062, GetButtonID(5, dataIndex)); - builder.AddImage(col4X + 3, rowY + vCenter, 2062, 936); - builder.AddHtml( - col4X + 12, - rowY + vCenter, - col4Width, - 30, - $"X {spawner.Location.X} Y {spawner.Location.Y}", - GumpTextColors.White, - 4 - ); - - // Column 5: Entry count with button - var col5X = listColPos[5]; - var col5Width = listColWidths[5]; - builder.AddButton(col5X + 6, rowY + vCenter - 4, 0x2635, 0x2635, GetButtonID(7, dataIndex)); - builder.AddImage(col5X + 6, rowY + vCenter - 7, 0x2635, 827); - builder.AddHtml( - col5X, - rowY + vCenter, - col5Width, - 30, - $"{spawner.Entries?.Count ?? 0}", - GumpTextColors.White, - 4, - align: TextAlignment.Center - ); - - // Column 6: Walk Range - var col6X = listColPos[6]; - var col6Width = listColWidths[6]; - builder.AddImageTiled(col6X + 8, rowY + 24, col6Width / 2, 1, 9357); - builder.AddTextEntry( - col6X + 12, - rowY + 8, - col6Width, - 80, - GumpHues.White, - entryIndex + 1, - $"{spawner.WalkingRange}" - ); - - // Column 7: Home Range - var col7X = listColPos[7]; - var col7Width = listColWidths[7]; - if (spawner.IsHomeRangeStyle) - { - builder.AddImageTiled(col7X + 8, rowY + 24, col7Width / 2, 1, 9357); - builder.AddTextEntry(col7X + 12, rowY + 8, col7Width, 80, GumpHues.White, entryIndex + 2, $"{spawner.HomeRange}"); - } - else - { - builder.AddHtml(col7X - 6, rowY + 8, col7Width, 30, "Custom", GumpTextColors.Yellow, 4, align: TextAlignment.Center); - } - - // Column 8: Min Delay - var col8X = listColPos[8]; - var col8Width = listColWidths[8]; - builder.AddImageTiled(col8X + 6, rowY + 24, col8Width - 20, 1, 9357); - builder.AddTextEntry(col8X + 8, rowY + 8, col8Width, 80, GumpHues.White, entryIndex + 3, $"{spawner.MinDelay}"); - - // Column 9: Max Delay - var col9X = listColPos[9]; - var col9Width = listColWidths[9]; - builder.AddImageTiled(col9X + 6, rowY + 24, col9Width - 20, 1, 9357); - builder.AddTextEntry(col9X + 8, rowY + 8, col9Width, 80, GumpHues.White, entryIndex + 4, $"{spawner.MaxDelay}"); - - // Column 10: Next Spawn - var col10X = listColPos[10]; - var col10Width = listColWidths[10]; - builder.AddHtml( - col10X, - rowY + vCenter, - col10Width, - 30, - $@"{spawner.NextSpawn:hh\:mm\:ss}", - GumpTextColors.White, - 4, - align: TextAlignment.Center - ); - - // Column 11: Actions - var col11X = listColPos[11]; - builder.AddButton(col11X, rowY, 4023, 4025, GetButtonID(8, dataIndex)); - builder.AddHtml(col11X + 4, rowY + 21, 55, 30, "save", GumpTextColors.White, 3); - - builder.AddButton(col11X + RowHeight, rowY, 4017, 4018, GetButtonID(9, dataIndex)); - builder.AddHtml(col11X + RowHeight, rowY + 21, 55, 30, "delete", GumpTextColors.White, 3); - - // Row separator - builder.AddImageTiled(0, rowY + RowHeight, GumpWidth, 1, 9357); - builder.AddAlphaRegion(0, rowY + RowHeight, GumpWidth, 1); - } - - // Draw footer - const int col0HCenter = GumpWidth / 2; - var row2VCenter = row2Y + mainRowHeights[2] / 2; - - if (listView.CanGoNext) - { - builder.AddButton(col0HCenter + 60, row2VCenter + 16, 4005, 4006, GetButtonID(10, 0)); - } - - if (listView.CanGoBack) - { - builder.AddButton(col0HCenter - 60, row2VCenter + 16, 4014, 4015, GetButtonID(11, 0)); - } - - builder.AddButton(15, row2Y, 4029, 4031, GetButtonID(1, 5)); - builder.AddHtml(55, row2Y + 4, GumpWidth, 20, $"Paste props for {_spawners.Length} spawners", GumpTextColors.Orange, 4); - - builder.AddButton(15, row2Y + 28, 4029, 4031, GetButtonID(1, 6)); - builder.AddHtml(55, row2Y + 32, GumpWidth, 20, $"Paste entry for {_spawners.Length} spawners", GumpTextColors.Orange, 4); - - builder.AddButton(275, row2Y, 4029, 4031, GetButtonID(1, 7)); - builder.AddHtml(315, row2Y + 4, GumpWidth, 20, "Paste copy to the ground", GumpTextColors.Yellow, 4); - - builder.AddHtml( - 16, - row2VCenter + 17, - GumpWidth, - 20, - $"Pages {listView.CurrentPage + 1}/{listView.TotalPages}", - GumpTextColors.White, - 4 - ); - builder.AddHtml(120, row2VCenter + 17, GumpWidth, 20, $"Total Spawners {_spawners.Length}", GumpTextColors.White, 4); + AddImageTiled(0, _main.Rows[2].Y - 11, _main.Width, 6, 9357); + AddAlphaRegion(0, _main.Rows[2].Y - 11, _main.Width, 6); + AddImageTiled(0, _main.Rows[2].Y + 55, _main.Width, 6, 9357); + AddAlphaRegion(0, _main.Rows[2].Y + 55, _main.Width, 6); } - private void DrawSearch(ref DynamicGumpBuilder builder, int row0Y, int searchHeight) + private static string ExtractCoords(Point3D cord) => $"X {cord.X} Y {cord.Y}"; + + public void DrawSearch() { - // Create 2-column subgrid for search area - Span searchColPos = stackalloc int[2]; - Span searchColWidths = stackalloc int[2]; - GridCalculator.ComputeUniformTrackSizes(2, GumpWidth, 0, searchColPos, searchColWidths); - - var col0X = searchColPos[0]; - var col1X = searchColPos[1]; - var col1HCenter = col1X + searchColWidths[1] / 2; - var row0VCenter = row0Y + searchHeight / 2; - const int deltaX = 140; + var search = SubGrid("search", _main.Name, 0, 0, 2, 1); - // Labels - builder.AddHtml(col0X, row0Y + 8, searchColWidths[0], 30, "Search type", GumpTextColors.Gold, 4, align: TextAlignment.Center); - builder.AddHtml(col1X, row0Y + 8, searchColWidths[1], 30, "Search Match", GumpTextColors.Gold, 4, align: TextAlignment.Center); + var col0X = search.Columns[0].X; + var col1X = search.Columns[1].X; + var col1HCenter = search.Columns[1].HCenter; + var row0Y = search.Rows[0].Y; + var row0VCenter = search.Rows[0].VCenter; - // Separators - builder.AddImageTiled(0, row0Y + 30, GumpWidth, 3, 9357); - builder.AddAlphaRegion(0, row0Y + 30, GumpWidth, 3); - builder.AddImageTiled(col1X, row0Y, 3, searchHeight, 9355); - builder.AddAlphaRegion(col1X, row0Y, 3, searchHeight); + AddLabelHtml(col0X, row0Y + 8, col1X, 30, "Search type", GridColors.Gold); + AddLabelHtml(col1X, row0Y + 8, col1X, 30, "Search Match", GridColors.Gold); + + AddImageTiled(0, row0Y + 30, _main.Width, 3, 9357); + AddAlphaRegion(0, row0Y + 30, _main.Width, 3); + + AddImageTiled(col1X, row0Y, 3, search.Height, 9355); + AddAlphaRegion(col1X, row0Y, 3, search.Height); - // Search type buttons var creature = _search.Type == SpawnSearchType.Creature ? 9021 : 9020; - var coords = _search.Type == SpawnSearchType.Coords ? 9021 : 9020; + var cords = _search.Type == SpawnSearchType.Coords ? 9021 : 9020; var props = _search.Type == SpawnSearchType.Props ? 9021 : 9020; - var name = _search.Type == SpawnSearchType.Name ? 9021 : 9020; + var args = _search.Type == SpawnSearchType.Name ? 9021 : 9020; - builder.AddButton(col0X + 10, row0Y + 45, creature, creature, GetButtonID(1, 0)); - builder.AddHtml(col0X + 15, row0Y + 48, 100, 30, "Creature", GumpTextColors.White, 4, align: TextAlignment.Center); + AddButton(col0X + 10, row0Y + 45, creature, creature, GetButtonID(1, 0)); + AddLabelHtml(col0X + 15, row0Y + 48, 100, 30, "Creature", GridColors.White); - builder.AddButton(col0X + 10 + deltaX - 40, row0Y + 45, coords, coords, GetButtonID(1, 1)); - builder.AddHtml(col0X + 40 + deltaX - 40, row0Y + 48, 150, 30, "Range", GumpTextColors.White, 4); - builder.AddButton(col0X + 10 + deltaX * 2 - 100, row0Y + 45, props, props, GetButtonID(1, 2)); - builder.AddHtml(col0X + 40 + deltaX * 2 - 100, row0Y + 48, 150, 30, "Creature Props", GumpTextColors.White, 4); + AddButton(col0X + 10 + deltaX - 40, row0Y + 45, cords, cords, GetButtonID(1, 1)); + AddLabelHtml(col0X + 40 + deltaX - 40, row0Y + 48, 150, 30, "Range", GridColors.White, 4, false); - builder.AddButton(col0X + 10 + deltaX * 3 - 100, row0Y + 45, name, name, GetButtonID(1, 3)); - builder.AddHtml(col0X + 40 + deltaX * 3 - 100, row0Y + 48, 150, 30, "Spawner Name", GumpTextColors.White, 4); + AddButton(col0X + 10 + deltaX * 2 - 100, row0Y + 45, props, props, GetButtonID(1, 2)); + AddLabelHtml(col0X + 40 + deltaX * 2 - 100, row0Y + 48, 150, 30, "Creature Props", GridColors.White, 4, false); - // Search text entry - builder.AddImageTiled(col1HCenter - 100, row0VCenter + 16, 200, 1, 9357); - builder.AddTextEntry(col1HCenter - 100, row0VCenter, 200, 20, GumpHues.White, 0xFFFF, _search.SearchPattern); - builder.AddButton(col1HCenter + 120, row0VCenter, 4023, 4024, GetButtonID(1, 4)); + AddButton(col0X + 10 + deltaX * 3 - 100, row0Y + 45, args, args, GetButtonID(1, 3)); + AddLabelHtml(col0X + 40 + deltaX * 3 - 100, row0Y + 48, 150, 30, "Spawner Name", GridColors.White, 4, false); - // Search type description - var description = (int)_search.Type switch + AddImageTiled(col1HCenter - 100, row0VCenter + 16, 200, 1, 9357); + AddTextEntry(col1HCenter - 100, row0VCenter, 200, 20, (int)GridHues.White, 0xFFFF, _search.SearchPattern, GetButtonID(1, 4)); + AddButton(col1HCenter + 120, row0VCenter, 4023, 4024, GetButtonID(1, 4)); + + var type = (int)_search.Type; + if (type == 0) { - 0 => "Search by creature name", - 1 => "Search by range (number)", - 2 => "Search by entry property field", - 3 => "Search by spawner name", - _ => "" - }; - builder.AddHtml(col1X, row0VCenter + 20, searchColWidths[1], 30, description, GumpTextColors.White, 4, align: TextAlignment.Center); + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by creature name", GridColors.White); + } + else if (type == 1) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by range (number)", GridColors.White); + } + else if (type == 2) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by entry property field", GridColors.White); + } + else if (type == 3) + { + AddLabelHtml(col1X, row0VCenter + 20, search.Columns[1].Width, 30, "Search by spawner name", GridColors.White); + } } private void SearchSpawner() @@ -415,7 +147,7 @@ public class SpawnerControllerGump : DynamicGump using var queue = PooledRefQueue.Create(); - if (_search.Type != SpawnSearchType.Coords || !int.TryParse(_search.SearchPattern, out var range)) + if (!(_search.Type == SpawnSearchType.Coords && int.TryParse(_search.SearchPattern, out var range))) { range = -1; } @@ -427,7 +159,7 @@ public class SpawnerControllerGump : DynamicGump continue; } - var enqueue = _search.Type switch + bool enqueue = _search.Type switch { SpawnSearchType.Creature => SearchSpawnerCreatures(spawner, _search.SearchPattern), SpawnSearchType.Coords => _mobile.InRange(spawner.Location, range), @@ -460,9 +192,8 @@ public class SpawnerControllerGump : DynamicGump private static bool SearchSpawnerProperties(BaseSpawner spawner, string searchPattern) { - for (var i = 0; i < spawner.Entries.Count; i++) + foreach (var entry in spawner.Entries) { - var entry = spawner.Entries[i]; if (entry.Properties?.InsensitiveContains(searchPattern) == true) { return true; @@ -472,66 +203,217 @@ public class SpawnerControllerGump : DynamicGump return false; } - public static void CopyProperty(BaseSpawner spawner, BaseSpawner target) + private void DrawFooter(ListView list) + { + if (list.CanNext) + { + AddButton(_main.Columns[0].HCenter + 60, _main.Rows[2].VCenter + 16, 4005, 4006, GetButtonID(10, 0)); + } + + if (list.CanBack) + { + AddButton(_main.Columns[0].HCenter + -60, _main.Rows[2].VCenter + 16, 4014, 4015, GetButtonID(11, 0)); + } + + var col0X = _main.Columns[0].X; + var col0Width = _main.Columns[0].Width; + var row2Y = _main.Rows[2].Y; + + //past to all spawners + AddButton(col0X + 15, row2Y, 4029, 4031, GetButtonID(1, 5)); + AddLabelHtml(col0X + 55, row2Y + 4, col0Width, 20, $"Paste props for {_spawners.Length} spawners", GridColors.Orange, 4, false); + + //paste target + AddButton(col0X + 15, row2Y + 28, 4029, 4031, GetButtonID(1, 6)); + AddLabelHtml(col0X + 55, row2Y + 32, col0Width, 20, $"Paste entry for {_spawners.Length} spawners", GridColors.Orange, 4, false); + + //paste ground + AddButton(col0X + 15 + 260, row2Y, 4029, 4031, GetButtonID(1, 7)); + AddLabelHtml(col0X + 55 + 260, row2Y + 4, col0Width, 20, "Paste copy to the ground", GridColors.Yellow, 4, false); + + AddLabelHtml(col0X + 16, _main.Rows[2].VCenter + 17, col0Width, 20, $"Pages {list.Page + 1}/{list.TotalPages + 1}", GridColors.White); + AddLabelHtml(col0X + 20, _main.Rows[2].VCenter + 17, col0Width, 20, $"Total Spawners {_spawners.Length}", GridColors.White, 4, false); + } + + + public void DrawSpawner(ListView list) + { + for (int i = 0; i < list.Items.Length; i++) + { + const int vCenter = 15; + const int perItem = 45; + + var spawner = _spawners[list.Items[i].Index]; + var item = list.Items[i]; + + AddButton(item.Cols[0].X + 15, list.Items[i].Y + 5, 4011, 4012, GetButtonID(2, item.Index)); + AddLabelHtml(item.Cols[0].X, list.Items[i].Y + 26, item.Cols[0].Width, 30, "copy", GridColors.White, 3); + + AddButton(item.Cols[1].X + 2, list.Items[i].Y + 5, 4029, 4031, GetButtonID(3, item.Index)); + AddLabelHtml(item.Cols[1].X + 3, list.Items[i].Y + 26, item.Cols[1].Width, 30, "props", GridColors.White, 3, false); + + AddButton(item.Cols[1].HCenter - 2, list.Items[i].Y + 5, 4029, 4031, GetButtonID(4, item.Index)); + AddLabelHtml(item.Cols[1].HCenter - 3, list.Items[i].Y + 26, 55, 30, "entry", GridColors.White, 3, false); + + //props + AddLabelHtml(item.Cols[2].X, list.Items[i].Y + vCenter + 10, list.Header.Cols[2].Width, 30, spawner.Serial.ToString(), GridColors.White); + + //map + AddLabelHtml(item.Cols[3].X, list.Items[i].Y + vCenter, list.Header.Cols[3].Width, 30, spawner.Map.ToString(), GridColors.White); + + //teleport button + AddButton(item.Cols[4].X - 5, list.Items[i].Y + vCenter, 2062, 2062, GetButtonID(5, item.Index)); + AddImage(item.Cols[4].X - 5, list.Items[i].Y + vCenter, 2062, 936); + AddLabelHtml(item.Cols[4].X - 7, list.Items[i].Y + vCenter, list.Header.Cols[4].Width, 30, ExtractCoords(spawner.Location), GridColors.White); + + //open entry button + AddButton(item.Cols[5].X + 6, list.Items[i].Y + vCenter - 4, 0x2635, 0x2635, GetButtonID(7, item.Index)); + AddImage(item.Cols[5].X + 6, list.Items[i].Y + vCenter - 7, 0x2635, 827); + AddLabelHtml(item.Cols[5].X, list.Items[i].Y + vCenter, list.Header.Cols[5].Width, 30, spawner.Entries?.Count.ToString(), GridColors.White); + + + //entry + var entry = i * EntryCount; + + AddImageTiled(item.Cols[2].X - 2, list.Items[i].Y + 18, item.Cols[2].Width, 1, 9357); + AddTextEntry(item.Cols[2].X, list.Items[i].Y + 2, list.Header.Cols[2].Width, 80, (int)GridHues.White, entry, spawner.Name, 20); + AddButton(item.Cols[2].X, list.Items[i].Y + vCenter + 9, 5837, 5838, GetButtonID(6, item.Index)); + + AddImageTiled(item.Cols[6].X + 8, list.Items[i].Y + 29, item.Cols[6].Width / 2, 1, 9357); + AddTextEntry(item.Cols[6].X + 12, list.Items[i].Y + 13, list.Header.Cols[6].Width, 80, (int)GridHues.White, entry + 1, spawner.WalkingRange.ToString(), 2); + + AddImageTiled(item.Cols[7].X + 8, list.Items[i].Y + 29, item.Cols[7].Width / 2, 1, 9357); + AddTextEntry(item.Cols[7].X + 12, list.Items[i].Y + 13, list.Header.Cols[7].Width, 80, (int)GridHues.White, entry + 2, spawner.HomeRange.ToString(), 2); + + AddImageTiled(item.Cols[8].X + 6, list.Items[i].Y + 29, item.Cols[8].Width - 20, 1, 9357); + AddTextEntry(item.Cols[8].X + 8, list.Items[i].Y + 13, list.Header.Cols[8].Width, 80, (int)GridHues.White, entry + 3, spawner.MinDelay.ToString(), 8); + + AddImageTiled(item.Cols[9].X + 6, list.Items[i].Y + 29, item.Cols[9].Width - 20, 1, 9357); + AddTextEntry(item.Cols[9].X + 8, list.Items[i].Y + 13, list.Header.Cols[9].Width, 80, (int)GridHues.White, entry + 4, spawner.MaxDelay.ToString(), 8); + //end entry + + AddLabelHtml(item.Cols[10].X, list.Items[i].Y + vCenter, list.Header.Cols[10].Width, 30, spawner.NextSpawn.ToString(@"hh\:mm\:ss"), GridColors.White); + + AddButton(item.Cols[11].X, list.Items[i].Y + 5, 4023, 4025, GetButtonID(8, item.Index)); + AddLabelHtml(item.Cols[11].X + 4, list.Items[i].Y + 26, 55, 30, "save", GridColors.White, 3, false); + + AddButton(item.Cols[11].X + perItem, list.Items[i].Y + 5, 4017, 4018, GetButtonID(9, item.Index)); + AddLabelHtml(item.Cols[11].X + perItem, list.Items[i].Y + 26, 55, 30, "delete", GridColors.White, 3, false); + + AddImageTiled(0, list.Items[i].Y + list.ColHeight, _main.Width, 1, 9357); + AddAlphaRegion(0, list.Items[i].Y + list.ColHeight, _main.Width, 1); + } + } + + public void DrawColumnName(ListView list) + { + AddLabelHtml(list.Header.Cols[0].X, list.Header.Y + 10, list.Header.Cols[0].Width, 30, "Copy", GridColors.Gold); + AddLabelHtml(list.Header.Cols[1].X, list.Header.Y + 10, list.Header.Cols[1].Width, 30, "Paste", GridColors.Gold); + AddLabelHtml(list.Header.Cols[2].X, list.Header.Y + 10, list.Header.Cols[2].Width, 30, "Name/Serial", GridColors.Gold); + AddLabelHtml(list.Header.Cols[3].X, list.Header.Y + 10, list.Header.Cols[3].Width, 30, "Map", GridColors.Gold); + AddLabelHtml(list.Header.Cols[4].X - 4, list.Header.Y + 10, list.Header.Cols[4].Width, 30, "Coords", GridColors.Gold); + AddLabelHtml(list.Header.Cols[5].X - 4, list.Header.Y + 10, list.Header.Cols[5].Width, 30, "Entry", GridColors.Gold); + AddLabelHtml(list.Header.Cols[6].X - 4, list.Header.Y + 10, list.Header.Cols[6].Width, 30, "Walk", GridColors.Gold); + AddLabelHtml(list.Header.Cols[7].X - 4, list.Header.Y + 10, list.Header.Cols[7].Width, 30, "Home", GridColors.Gold); + AddLabelHtml(list.Header.Cols[8].X - 4, list.Header.Y + 10, list.Header.Cols[8].Width, 30, "Min Delay", GridColors.Gold); + AddLabelHtml(list.Header.Cols[9].X - 4, list.Header.Y + 10, list.Header.Cols[9].Width, 30, "Max Delay", GridColors.Gold); + AddLabelHtml(list.Header.Cols[10].X - 4, list.Header.Y + 10, list.Header.Cols[10].Width, 30, "Next Spawn", GridColors.Gold); + AddLabelHtml(list.Header.Cols[11].X, list.Header.Y + 10, list.Header.Cols[11].Width, 30, "Actions", GridColors.Gold); + } + + public SpawnerControllerGump(Mobile mobile, int page = 0, BaseSpawner copy = null, SpawnSearch search = null) : base(20, 30) + { + _main = Grid("main", 1000, 800, 1, 3, rowSize: "10* * 100"); + + AddBackground(0, 0, 1000, 800, 5054); + AddBlackAlpha(0, 0, 1000, 800); + + _mobile = mobile; + _page = page; + _copy = copy; + _search = search ?? new SpawnSearch(); + + SearchSpawner(); + + var _list = AddListView( + _main.Name, + 0, + 1, + _spawners.Length, + page, + 45, + 12, + colSize: "6* 8* 18* 6* 12* 6* 5* 5* 8* 8* 9* *", + headerHeight: 30, + marginY: -7 + ); + + _lineCount = _list.LineCount; + DrawBorder(_list); + DrawSearch(); + DrawSpawner(_list); + DrawColumnName(_list); + DrawFooter(_list); + + } + public void CopyProperty(BaseSpawner spawner, BaseSpawner target) { target.Name = spawner.Name; target.MinDelay = spawner.MinDelay; target.MaxDelay = spawner.MaxDelay; target.WalkingRange = spawner.WalkingRange; - target.SpawnBounds = spawner.SpawnBounds; - target.SpawnLocationIsHome = spawner.SpawnLocationIsHome; + target.HomeRange = spawner.HomeRange; target.Group = spawner.Group; target.Count = spawner.Count; } - - public static void CopyEntry(BaseSpawner spawner, BaseSpawner target) + public void CopyEntry(BaseSpawner spawner, BaseSpawner target) { if (spawner.Entries?.Count > 0) { target.Entries?.Clear(); - for (var i = 0; i < spawner.Entries.Count; i++) + for (int i = 0; i < spawner.Entries.Count; i++) { var item = spawner.Entries[i]; var targetEntry = target.AddEntry(item.SpawnedName, item.SpawnedProbability, item.SpawnedMaxCount); targetEntry.Properties = item.Properties; targetEntry.Parameters = item.Parameters; } + } } - public void FullCopy(BaseSpawner spawner, BaseSpawner target) { CopyProperty(spawner, target); CopyEntry(spawner, target); } - - public void Refresh(Mobile mobile, bool reSearch = true) + public void Refresh(bool update = true) { - if (reSearch) + if (update) { - SearchSpawner(); + _mobile.SendGump(new SpawnerControllerGump(_mobile, _page, _copy, _search)); + } + else + { + _mobile.SendGump(this); } - - mobile.SendGump(this); } public override void OnResponse(NetState sender, in RelayInfo info) { - var mobile = sender.Mobile; - if (info.ButtonID == 0) - { - return; - } - var buttonID = info.ButtonID - 1; var type = buttonID % TypeCount; var index = buttonID / TypeCount; switch (type) { + case -1: + { + return; + } case 1: { + _page = 0; if (index < 4) { _search.Type = (SpawnSearchType)index; @@ -544,6 +426,7 @@ public class SpawnerControllerGump : DynamicGump else if (index == 4) { var entry = info.GetTextEntry(0xFFFF); + if (entry?.Length > 0) { _search.SearchPattern = entry; @@ -566,141 +449,146 @@ public class SpawnerControllerGump : DynamicGump else if (index == 7 && _copy != null) { var newSpawner = new Spawner(); + FullCopy(_copy, newSpawner); - newSpawner.Map = mobile.Map; - newSpawner.Location = mobile.Location; + newSpawner.Map = _mobile.Map; + newSpawner.Location = _mobile.Location; newSpawner.Stop(); newSpawner.Start(); newSpawner.Respawn(); } - _page = 0; break; } - + //copy case 2: { if (index < _spawners.Length) { _copy = _spawners[index]; } + break; } - + //paste props case 3: { if (index < _spawners.Length && _copy != null) { CopyProperty(_copy, _spawners[index]); } + break; } - + //paste entry case 4: { if (index < _spawners.Length && _copy != null) { CopyEntry(_copy, _spawners[index]); } + break; } + //save + case 8: + { + if (index < _spawners.Length) + { + var spawner = _spawners[index]; + var indexEntry = index >= _lineCount ? (index - _lineCount * _page) * EntryCount : index * EntryCount; + + var name = info.GetTextEntry(indexEntry); + if (name?.Length > 0) + { + spawner.Name = name; + } + + if (int.TryParse(info.GetTextEntry(indexEntry + 1), out var walkRange)) + { + spawner.WalkingRange = walkRange; + } + + if (int.TryParse(info.GetTextEntry(indexEntry + 2), out var homeHange)) + { + spawner.HomeRange = homeHange; + } + + if (TimeSpan.TryParse(info.GetTextEntry(indexEntry + 3), out var minDelay)) + { + spawner.MinDelay = minDelay; + } + + if (TimeSpan.TryParse(info.GetTextEntry(indexEntry + 4), out var maxDelay)) + { + spawner.MaxDelay = maxDelay; + } + } + + break; + } + //go case 5: { if (index < _spawners.Length) { var spawner = _spawners[index]; - mobile.Map = spawner.Map; - mobile.Location = spawner.Location; + _mobile.Map = spawner.Map; + _mobile.Location = spawner.Location; } - Refresh(mobile, reSearch: false); + Refresh(false); return; } - + //open entry case 6: case 7: { if (index < _spawners.Length) { var spawner = _spawners[index]; - Refresh(mobile); + Refresh(); if (type == 7) { - mobile.SendGump(new SpawnerGump(spawner)); + _mobile.SendGump(new SpawnerGump(spawner)); } else { - mobile.SendGump(new PropertiesGump(mobile, spawner)); + _mobile.SendGump(new PropertiesGump(_mobile, spawner)); } + return; } + break; } - - case 8: - { - if (index < _spawners.Length) - { - var spawner = _spawners[index]; - var entryIndex = index >= _lineCount ? (index - _lineCount * _page) * EntryCount : index * EntryCount; - - var name = info.GetTextEntry(entryIndex); - if (name?.Length > 0) - { - spawner.Name = name; - } - - if (int.TryParse(info.GetTextEntry(entryIndex + 1), out var walkRange)) - { - spawner.WalkingRange = walkRange; - } - - if (int.TryParse(info.GetTextEntry(entryIndex + 2), out var homeRange)) - { - spawner.HomeRange = homeRange; - } - - if (TimeSpan.TryParse(info.GetTextEntry(entryIndex + 3), out var minDelay)) - { - spawner.MinDelay = minDelay; - } - - if (TimeSpan.TryParse(info.GetTextEntry(entryIndex + 4), out var maxDelay)) - { - spawner.MaxDelay = maxDelay; - } - } - break; - } - + //delete case 9: { if (index < _spawners.Length) { _spawners[index].Delete(); } + break; } - case 10: { _page++; - Refresh(mobile, reSearch: false); - return; + break; } - case 11: { if (_page > 0) { _page--; } - Refresh(mobile, reSearch: false); - return; + + break; } } - Refresh(mobile); + Refresh(); } } diff --git a/Projects/UOContent/Engines/Spawners/SpawnerGump.cs b/Projects/UOContent/Engines/Spawners/SpawnerGump.cs index e22782228..e7f9cf1be 100644 --- a/Projects/UOContent/Engines/Spawners/SpawnerGump.cs +++ b/Projects/UOContent/Engines/Spawners/SpawnerGump.cs @@ -1,4 +1,3 @@ -using System; using Server.Collections; using Server.Gumps; using Server.Network; @@ -158,16 +157,18 @@ public class SpawnerGump : Gump } var totalSpawned = 0; + var totalSpawns = 0; var totalWeight = 0; - foreach (var spawnerEntry in _spawner.Entries) + foreach (SpawnerEntry spawnerEntry in _spawner.Entries) { + totalSpawns += spawnerEntry.SpawnedMaxCount; totalSpawned += spawner.CountSpawns(spawnerEntry); totalWeight += spawnerEntry.SpawnedProbability; } - AddHtml(232, 308 + offset, 35, 20, Html.Center($"{totalSpawned}", 0xF4F4F4)); - AddHtml(270, 308 + offset, 35, 20, Html.Center($"{totalWeight}", 0xF4F4F4)); + AddHtml(270, 308 + offset, 35, 20, totalSpawns.ToString().Center(0xF4F4F4)); + AddHtml(308, 308 + offset, 35, 20, totalWeight.ToString().Center(0xF4F4F4)); AddHtml(5, 1, 161, 20, $"{spawner.Name} ({totalSpawned}/{spawner.Count})"); @@ -234,18 +235,18 @@ public class SpawnerGump : Gump var index = i * 5; var entryindex = _page * 13 + i; - var typeEntry = info.GetTextEntry(index); - var maxSpawns = info.GetTextEntry(index + 1); - var probability = info.GetTextEntry(index + 2); - var customParams = info.GetTextEntry(index + 3); - var customPrompts = info.GetTextEntry(index + 4); + var cte = info.GetTextEntry(index); + var mte = info.GetTextEntry(index + 1); + var poste = info.GetTextEntry(index + 2); + var parmte = info.GetTextEntry(index + 3); + var propte = info.GetTextEntry(index + 4); - if (typeEntry == null) + if (cte == null) { continue; } - var str = typeEntry.Trim().ToLower(); + var str = cte.Trim().ToLower(); if (str.Length > 0) { @@ -264,32 +265,42 @@ public class SpawnerGump : Gump entry = spawner.Entries[entryindex]; entry.SpawnedName = str; - if (maxSpawns != null) + if (mte != null) { - entry.SpawnedMaxCount = Utility.ToInt32(maxSpawns.AsSpan().Trim()); + entry.SpawnedMaxCount = Utility.ToInt32(mte.Trim()); } - if (probability != null) + if (poste != null) { - entry.SpawnedProbability = Utility.ToInt32(probability.AsSpan().Trim()); + entry.SpawnedProbability = Utility.ToInt32(poste.Trim()); } } else { - var maxcount = maxSpawns != null ? Utility.ToInt32(maxSpawns.AsSpan().Trim()) : 1; - var probcount = probability != null ? Utility.ToInt32(probability.AsSpan().Trim()) : 100; + var maxcount = 1; + var probcount = 100; + + if (mte != null) + { + maxcount = Utility.ToInt32(mte.Trim()); + } + + if (poste != null) + { + probcount = Utility.ToInt32(poste.Trim()); + } entry = spawner.AddEntry(str, probcount, maxcount); } - if (customParams != null) + if (parmte != null) { - entry.Parameters = customParams.Trim(); + entry.Parameters = parmte.Trim(); } - if (customPrompts != null) + if (propte != null) { - entry.Properties = customPrompts.Trim(); + entry.Properties = propte.Trim(); } } else if (entryindex < ocount && spawner.Entries[entryindex] != null) diff --git a/Projects/UOContent/Engines/Spawners/SpawnerJsonSerializer.cs b/Projects/UOContent/Engines/Spawners/SpawnerJsonSerializer.cs deleted file mode 100644 index 61213b05a..000000000 --- a/Projects/UOContent/Engines/Spawners/SpawnerJsonSerializer.cs +++ /dev/null @@ -1,346 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SpawnerJsonSerializer.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Text.Encodings.Web; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; -using Server.Json; -using Server.Logging; -using Server.Text; - -namespace Server.Engines.Spawners; - -public static class SpawnerJsonSerializer -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(SpawnerJsonSerializer)); - - private static JsonDerivedType[] _derivedTypes; - private static JsonSerializerOptions _options; - - /// - /// Invoked automatically during the Configure bootstrap phase - /// (AssemblyHandler.Invoke("Configure")). Discovers every concrete SpawnerDto subclass - /// marked with [JsonDiscoverableType] and registers it for STJ polymorphism. - /// - public static void Configure() - { - var discovered = new List(); - var byDiscriminator = new Dictionary(); - - foreach (var asm in AssemblyHandler.Assemblies) - { - Collect(AssemblyHandler.GetTypeCache(asm).Types, discovered, byDiscriminator); - } - - Collect(AssemblyHandler.GetTypeCache(Core.Assembly).Types, discovered, byDiscriminator); - - _derivedTypes = discovered.ToArray(); - _options = null; // force rebuild with the discovered types - - logger.Information("Discovered {Count} spawner JSON type(s)", _derivedTypes.Length); - } - - private static void Collect(Type[] types, List discovered, Dictionary byDiscriminator) - { - for (var i = 0; i < types.Length; i++) - { - var type = types[i]; - if (type.IsAbstract || !type.IsAssignableTo(typeof(SpawnerDto))) - { - continue; - } - - var attr = (JsonDiscoverableTypeAttribute)Attribute.GetCustomAttribute( - type, typeof(JsonDiscoverableTypeAttribute), false); - if (attr == null) - { - continue; - } - - var (discriminator, derived) = Validate(type, byDiscriminator); - byDiscriminator[discriminator] = type; - discovered.Add(derived); - } - } - - internal static (string discriminator, JsonDerivedType derived) Validate( - Type type, Dictionary byDiscriminator) - { - if (!IsJsonConstructible(type)) - { - throw new Exception( - $"SpawnerDto type '{type.FullName}' is marked [JsonDiscoverableType] but System.Text.Json cannot construct it. " + - "Add a public parameterless constructor or use a record with init-only properties." - ); - } - - var attr = (JsonDiscoverableTypeAttribute)Attribute.GetCustomAttribute( - type, typeof(JsonDiscoverableTypeAttribute), false); - - var discriminator = attr?.Discriminator ?? type.Name; - if (byDiscriminator.TryGetValue(discriminator, out var existing)) - { - throw new Exception( - $"Spawner JSON discriminator '{discriminator}' is claimed by both '{existing.FullName}' and " + - $"'{type.FullName}'. Set an explicit discriminator via [JsonDiscoverableType(\"...\")] on one." - ); - } - - return (discriminator, new JsonDerivedType(type, discriminator)); - } - - private static bool IsJsonConstructible(Type type) - { - foreach (var ctor in type.GetConstructors()) - { - if (ctor.GetParameters().Length == 0) - { - return true; - } - - if (Attribute.IsDefined(ctor, typeof(JsonConstructorAttribute))) - { - return true; - } - } - - return false; - } - - public static JsonSerializerOptions Options => - _options ??= new JsonSerializerOptions(JsonConfig.GetOptions()) - { - // Optional fields omit at default; mandatory ones force-write via [JsonIgnore(Never)]. - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, - TypeInfoResolver = new DefaultJsonTypeInfoResolver - { - Modifiers = - { - AddPolymorphism, - ConfigureHomeRange - } - } - }; - - private static void AddPolymorphism(JsonTypeInfo typeInfo) - { - if (typeInfo.Type != typeof(SpawnerDto)) - { - return; - } - - typeInfo.PolymorphismOptions = new JsonPolymorphismOptions(); - for (var i = 0; i < _derivedTypes.Length; i++) - { - typeInfo.PolymorphismOptions.DerivedTypes.Add(_derivedTypes[i]); - } - } - - // homeRange writes only when >= 0 (0 is a valid radius WhenWritingDefault could not emit). - private static void ConfigureHomeRange(JsonTypeInfo typeInfo) - { - if (!typeInfo.Type.IsAssignableTo(typeof(SpawnerDto))) - { - return; - } - - for (var i = 0; i < typeInfo.Properties.Count; i++) - { - if (typeInfo.Properties[i].Name == "homeRange") - { - typeInfo.Properties[i].ShouldSerialize = static (_, value) => value is int hr && hr >= 0; - } - } - } - - // --- Compact writer (matches the on-disk spawn-file layout used by [ExportSpawners) --- - private const int CompactPrintWidth = 125; - - private static readonly JsonSerializerOptions _scalarOptions = new() - { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - /// - /// Serializes to the compact spawn-file layout: a container is inline only when all its values - /// are scalars and the line fits within 125 characters; otherwise it expands (2-space indent). - /// UTF-8, LF. Admin/cold path. - /// - public static string SerializeCompact(T value) - { - var node = JsonSerializer.SerializeToNode(value, Options); - var sb = ValueStringBuilder.Create(); - try - { - WriteNode(node, ref sb, 0, 0); - sb.Append("\n"); - return sb.ToString(); - } - finally - { - sb.Dispose(); - } - } - - private static void WriteNode(JsonNode node, ref ValueStringBuilder sb, int indent, int column) - { - if (node is not JsonArray and not JsonObject) - { - sb.Append(node?.ToJsonString(_scalarOptions) ?? "null"); - return; - } - - if (AllScalarChildren(node)) - { - var inline = Inline(node); - if (column + inline.Length <= CompactPrintWidth) - { - sb.Append(inline); - return; - } - } - - var childIndent = indent + 1; - var childColumn = childIndent * 2; - - if (node is JsonArray arr) - { - sb.Append("[\n"); - for (var i = 0; i < arr.Count; i++) - { - sb.Append(' ', childColumn); - WriteNode(arr[i], ref sb, childIndent, childColumn); - sb.Append(i < arr.Count - 1 ? ",\n" : "\n"); - } - - sb.Append(' ', indent * 2); - sb.Append("]"); - return; - } - - var obj = (JsonObject)node; - sb.Append("{\n"); - var index = 0; - var count = obj.Count; - foreach (var pair in obj) - { - sb.Append(' ', childColumn); - var prefix = $"\"{pair.Key}\": "; - sb.Append(prefix); - WriteNode(pair.Value, ref sb, childIndent, childColumn + prefix.Length); - sb.Append(++index < count ? ",\n" : "\n"); - } - - sb.Append(' ', indent * 2); - sb.Append("}"); - } - - private static bool AllScalarChildren(JsonNode node) - { - if (node is JsonArray arr) - { - foreach (var element in arr) - { - if (element is JsonArray or JsonObject) - { - return false; - } - } - - return true; - } - - foreach (var pair in (JsonObject)node) - { - if (pair.Value is JsonArray or JsonObject) - { - return false; - } - } - - return true; - } - - private static string Inline(JsonNode node) - { - var sb = ValueStringBuilder.Create(); - try - { - AppendInline(node, ref sb); - return sb.ToString(); - } - finally - { - sb.Dispose(); - } - } - - private static void AppendInline(JsonNode node, ref ValueStringBuilder sb) - { - switch (node) - { - case JsonArray arr: - { - sb.Append("["); - for (var i = 0; i < arr.Count; i++) - { - if (i > 0) - { - sb.Append(", "); - } - - AppendInline(arr[i], ref sb); - } - - sb.Append("]"); - break; - } - case JsonObject obj: - { - if (obj.Count == 0) - { - sb.Append("{}"); - break; - } - - sb.Append("{ "); - var first = true; - foreach (var pair in obj) - { - if (!first) - { - sb.Append(", "); - } - - first = false; - sb.Append("\""); - sb.Append(pair.Key); - sb.Append("\": "); - AppendInline(pair.Value, ref sb); - } - - sb.Append(" }"); - break; - } - default: - sb.Append(node?.ToJsonString(_scalarOptions) ?? "null"); - break; - } - } -} diff --git a/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs b/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs index 4cd8d8c63..1ac218ad2 100644 --- a/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs +++ b/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs @@ -170,10 +170,7 @@ public class StealableArtifacts : GenericPersistence new(Map.Malas, new Point3D(157, 608, -1), 18432, 27648, typeof(SwordDisplay5WestArtifact)), new(Map.Malas, new Point3D(187, 643, 1), 18432, 27648, typeof(Painting6NorthArtifact)), new(Map.Malas, new Point3D(146, 623, 1), 18432, 27648, typeof(Painting6WestArtifact)), - new(Map.Malas, new Point3D(178, 629, -1), 18432, 27648, typeof(ManStatuetteEastArtifact)), - - // Stygian Abyss (Ter Mur) - Artifact rarity 5 - new(Map.TerMur, new Point3D(843, 665, 27), 1152, 1728, typeof(ValkyriesGlaive)) + new(Map.Malas, new Point3D(178, 629, -1), 18432, 27648, typeof(ManStatuetteEastArtifact)) }; public static Type[] TypesOfEntries @@ -248,7 +245,7 @@ public class StealableArtifacts : GenericPersistence public override void Serialize(IGenericWriter writer) { - writer.WriteEncodedInt(2); // version + writer.WriteEncodedInt(1); // version writer.Write(_enabled); @@ -261,7 +258,7 @@ public class StealableArtifacts : GenericPersistence var si = _artifacts[i]; writer.Write(si.Item); - writer.WriteAnchoredTime(si.NextRespawn); + writer.WriteDeltaTime(si.NextRespawn); } } } @@ -282,7 +279,7 @@ public class StealableArtifacts : GenericPersistence for (var i = 0; i < length; i++) { var item = reader.ReadEntity(); - var nextRespawn = version >= 2 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime(); + var nextRespawn = reader.ReadDeltaTime(); if (i < _artifacts.Length) { diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs b/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs index 803a88a5d..9f63e9722 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs @@ -72,11 +72,13 @@ public abstract partial class BasePigmentsOfTokuno : Item, IUsesRemaining public BasePigmentsOfTokuno() : base(0xEFF) { + Weight = 1.0; _usesRemaining = 1; } public BasePigmentsOfTokuno(int uses) : base(0xEFF) { + Weight = 1.0; _usesRemaining = uses; } @@ -84,8 +86,6 @@ public abstract partial class BasePigmentsOfTokuno : Item, IUsesRemaining { } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1070933; // Pigments of Tokuno protected TextDefinition Label diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs b/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs index c42cfae4e..a9fa2d7ca 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs @@ -330,24 +330,33 @@ public partial class PigmentsOfTokuno : BasePigmentsOfTokuno } [Constructible] - public PigmentsOfTokuno(PigmentType type, int uses) : base(uses) => Type = type; - - [SerializableField(0, fieldChanged: nameof(OnTypeChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private PigmentType _type; - - private void OnTypeChanged(PigmentType oldValue, PigmentType newValue) + public PigmentsOfTokuno(PigmentType type, int uses) : base(uses) { - var v = (int)_type; - if (v >= 0 && v < _table.Length) + Weight = 1.0; + Type = type; + } + + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public PigmentType Type + { + get => _type; + set { - Hue = _table[v][0]; - Label = _table[v][1]; - } - else - { - Hue = 0; - Label = -1; + _type = value; + + var v = (int)_type; + + if (v >= 0 && v < _table.Length) + { + Hue = _table[v][0]; + Label = _table[v][1]; + } + else + { + Hue = 0; + Label = -1; + } } } diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs b/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs index 22c6af344..19ea73319 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs @@ -354,9 +354,11 @@ public partial class AncientUrn : Item } [Constructible] - public AncientUrn(string urnName) : base(0x241D) => _urnName = urnName; - - public override double DefaultWeight => 1.0; + public AncientUrn(string urnName) : base(0x241D) + { + _urnName = urnName; + Weight = 1.0; + } public static string[] Names { get; } = { @@ -430,9 +432,12 @@ public partial class HonorableSwords : Item } [Constructible] - public HonorableSwords(string swordsName) : base(0x2853) => _swordsName = swordsName; + public HonorableSwords(string swordsName) : base(0x2853) + { + _swordsName = swordsName; - public override double DefaultWeight => 5.0; + Weight = 5.0; + } public override int LabelNumber => 1071015; // Honorable Swords @@ -615,24 +620,33 @@ public partial class LesserPigmentsOfTokuno : BasePigmentsOfTokuno } [Constructible] - public LesserPigmentsOfTokuno(LesserPigmentType type) : base(1) => Type = type; - - [SerializableField(0, fieldChanged: nameof(OnTypeChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private LesserPigmentType _type; - - private void OnTypeChanged(LesserPigmentType oldValue, LesserPigmentType newValue) + public LesserPigmentsOfTokuno(LesserPigmentType type) : base(1) { - var v = (int)_type; - if (v >= 0 && v < _table.Length) + Weight = 1.0; + Type = type; + } + + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public LesserPigmentType Type + { + get => _type; + set { - Hue = _table[v][0]; - Label = _table[v][1]; - } - else - { - Hue = 0; - Label = -1; + _type = value; + + var v = (int)_type; + + if (v >= 0 && v < _table.Length) + { + Hue = _table[v][0]; + Label = _table[v][1]; + } + else + { + Hue = 0; + Label = -1; + } } } diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs b/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs index 0b105e7b6..353016c07 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs @@ -279,10 +279,10 @@ namespace Server.Mobiles AddItem(new Backpack()); AddItem(new Kamishimo(0x483)); - AddItem(new LightPlateJingasa - { - Hue = 0x711 - }); + Item item = new LightPlateJingasa(); + item.Hue = 0x711; + + AddItem(item); } public override bool CanBeDamaged() => false; diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs index 7a62cc735..5a8b128ab 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs @@ -79,33 +79,45 @@ public partial class CharacterStatue : Mobile, IRewardItem InvalidateHues(); } - [SerializableField(0, fieldChanged: nameof(OnStatueTypeChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private StatueType _statueType; - - private void OnStatueTypeChanged(StatueType oldValue, StatueType newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public StatueType StatueType { - InvalidateHues(); - InvalidatePose(); + get => _statueType; + set + { + _statueType = value; + InvalidateHues(); + InvalidatePose(); + this.MarkDirty(); + } } - [SerializableField(1, fieldChanged: nameof(OnPoseChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private StatuePose _pose; - - private void OnPoseChanged(StatuePose oldValue, StatuePose newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public StatuePose Pose { - InvalidatePose(); + get => _pose; + set + { + _pose = value; + InvalidatePose(); + this.MarkDirty(); + } } - [SerializableField(2, fieldChanged: nameof(OnMaterialChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private StatueMaterial _material; - - private void OnMaterialChanged(StatueMaterial oldValue, StatueMaterial newValue) + [SerializableProperty(2)] + [CommandProperty(AccessLevel.GameMaster)] + public StatueMaterial Material { - InvalidateHues(); - InvalidatePose(); + get => _material; + set + { + _material = value; + InvalidateHues(); + InvalidatePose(); + this.MarkDirty(); + } } public override void OnDoubleClick(Mobile from) @@ -358,7 +370,7 @@ public partial class CharacterStatue : Mobile, IRewardItem } ProcessDelta(); - var animPacket = stackalloc byte[CharacterStatuePackets.StatueAnimationPacketLength].InitializePacket(); + Span animPacket = stackalloc byte[CharacterStatuePackets.StatueAnimationPacketLength].InitializePacket(); foreach (var state in Map.GetClientsInRange(Location)) { @@ -407,10 +419,9 @@ public partial class CharacterStatueDeed : Item, IRewardItem } LootType = LootType.Blessed; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - public override int LabelNumber { get @@ -580,7 +591,7 @@ public class CharacterStatueTarget : Target _maker.Delete(); statue.Sculpt(from); - CharacterStatueGump.DisplayTo(from, _maker, statue); + from.SendGump(new CharacterStatueGump(_maker, statue, from), true); return; } diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs index 48ae30ae9..0e8ea37b6 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs @@ -19,19 +19,22 @@ public partial class CharacterStatueMaker : Item, IRewardItem InvalidateHue(); LootType = LootType.Blessed; + Weight = 5.0; } - public override double DefaultWeight => 5.0; - public override int LabelNumber => 1076173; // Character Statue Maker - [SerializableField(1, fieldChanged: nameof(OnStatueTypeChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private StatueType _statueType; - - private void OnStatueTypeChanged(StatueType oldValue, StatueType newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public StatueType StatueType { - InvalidateHue(); + get => _statueType; + set + { + _statueType = value; + InvalidateHue(); + this.MarkDirty(); + } } public override void OnDoubleClick(Mobile from) @@ -81,29 +84,80 @@ public partial class CharacterStatueMaker : Item, IRewardItem } } -[SerializationGenerator(0)] -public partial class MarbleStatueMaker : CharacterStatueMaker +public class MarbleStatueMaker : CharacterStatueMaker { [Constructible] public MarbleStatueMaker() : base(StatueType.Marble) { } + + public MarbleStatueMaker(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class JadeStatueMaker : CharacterStatueMaker +public class JadeStatueMaker : CharacterStatueMaker { [Constructible] public JadeStatueMaker() : base(StatueType.Jade) { } + + public JadeStatueMaker(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } -[SerializationGenerator(0)] -public partial class BronzeStatueMaker : CharacterStatueMaker +public class BronzeStatueMaker : CharacterStatueMaker { [Constructible] public BronzeStatueMaker() : base(StatueType.Bronze) { } + + public BronzeStatueMaker(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + } } diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs index cc3102536..a1788057a 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CharacterStatuePackets.cs * * * @@ -52,7 +52,7 @@ namespace Server.Engines.VeteranRewards return; } - var buffer = stackalloc byte[StatueAnimationPacketLength].InitializePacket(); + Span buffer = stackalloc byte[StatueAnimationPacketLength].InitializePacket(); CreateStatueAnimation(buffer, serial, status, anim, frame); ns.Send(buffer); } diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePlinth.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePlinth.cs index 69938c379..6298eeb46 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePlinth.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePlinth.cs @@ -77,7 +77,7 @@ public partial class CharacterStatuePlinth : Static, IAddon { if (_statue != null) { - CharacterPlinthGump.DisplayTo(from, _statue); + from.SendGump(new CharacterPlinthGump(_statue)); } } @@ -98,40 +98,31 @@ public partial class CharacterStatuePlinth : Static, IAddon } } - private class CharacterPlinthGump : DynamicGump + private class CharacterPlinthGump : Gump { - private readonly CharacterStatue _statue; - - public override bool Singleton => true; - - private CharacterPlinthGump(CharacterStatue statue) : base(60, 30) => _statue = statue; - - public static void DisplayTo(Mobile from, CharacterStatue statue) + public CharacterPlinthGump(CharacterStatue statue) : base(60, 30) { - if (from?.NetState == null || statue == null || statue.Deleted) - { - return; - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - from.SendGump(new CharacterPlinthGump(statue)); + AddPage(0); + AddImage(0, 0, 0x24F4); + AddHtml(55, 50, 150, 20, statue.Name); + AddHtml(55, 75, 150, 20, statue.SculptedOn.ToString()); + AddHtmlLocalized(55, 100, 150, 20, GetTypeNumber(statue.StatueType), 0); } - protected override void BuildLayout(ref DynamicGumpBuilder builder) + public static int GetTypeNumber(StatueType type) { - builder.AddPage(); - builder.AddImage(0, 0, 0x24F4); - builder.AddHtml(55, 50, 150, 20, _statue.Name); - builder.AddHtml(55, 75, 150, 20, _statue.SculptedOn.ToString()); - builder.AddHtmlLocalized(55, 100, 150, 20, GetTypeNumber(_statue.StatueType), 0); - } - - public static int GetTypeNumber(StatueType type) => - type switch + return type switch { StatueType.Marble => 1076181, StatueType.Jade => 1076180, StatueType.Bronze => 1076230, _ => 1076181 }; + } } } diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/Gumps/CharacterStatueGump.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/Gumps/CharacterStatueGump.cs index 1c446b126..87bc2d088 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/Gumps/CharacterStatueGump.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/Gumps/CharacterStatueGump.cs @@ -1,195 +1,207 @@ using Server.Mobiles; using Server.Network; -namespace Server.Gumps; - -public class CharacterStatueGump : DynamicGump +namespace Server.Gumps { - private readonly Item _maker; - private readonly CharacterStatue _statue; - - public override bool Singleton => true; - - private CharacterStatueGump(Item maker, CharacterStatue statue) : base(60, 36) + public class CharacterStatueGump : Gump { - _maker = maker; - _statue = statue; - } + private readonly Item m_Maker; + private readonly Mobile m_Owner; + private readonly CharacterStatue m_Statue; - public static void DisplayTo(Mobile from, Item maker, CharacterStatue statue) - { - if (from?.NetState == null || statue == null || statue.Deleted) + public CharacterStatueGump(Item maker, CharacterStatue statue, Mobile owner) : base(60, 36) { - return; - } + m_Maker = maker; + m_Statue = statue; + m_Owner = owner; - from.SendGump(new CharacterStatueGump(maker, statue)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 327, 324, 0x13BE); - builder.AddImageTiled(10, 10, 307, 20, 0xA40); - builder.AddImageTiled(10, 40, 307, 244, 0xA40); - builder.AddImageTiled(10, 294, 307, 20, 0xA40); - builder.AddAlphaRegion(10, 10, 307, 304); - builder.AddHtmlLocalized(14, 12, 327, 20, 1076156, 0x7FFF); // Character Statue Maker - - // pose - builder.AddHtmlLocalized(133, 41, 120, 20, 1076168, 0x7FFF); // Choose Pose - builder.AddHtmlLocalized(133, 61, 120, 20, 1076208 + (int)_statue.Pose, 0x77E); - builder.AddButton(163, 81, 0xFA5, 0xFA7, (int)Buttons.PoseNext); - builder.AddButton(133, 81, 0xFAE, 0xFB0, (int)Buttons.PosePrev); - - // direction - builder.AddHtmlLocalized(133, 126, 120, 20, 1076170, 0x7FFF); // Choose Direction - builder.AddHtmlLocalized(133, 146, 120, 20, GetDirectionNumber(_statue.Direction), 0x77E); - builder.AddButton(163, 167, 0xFA5, 0xFA7, (int)Buttons.DirNext); - builder.AddButton(133, 167, 0xFAE, 0xFB0, (int)Buttons.DirPrev); - - // material - builder.AddHtmlLocalized(133, 211, 120, 20, 1076171, 0x7FFF); // Choose Material - builder.AddHtmlLocalized(133, 231, 120, 20, GetMaterialNumber(_statue.StatueType, _statue.Material), 0x77E); - builder.AddButton(163, 253, 0xFA5, 0xFA7, (int)Buttons.MatNext); - builder.AddButton(133, 253, 0xFAE, 0xFB0, (int)Buttons.MatPrev); - - // cancel - builder.AddButton(10, 294, 0xFB1, 0xFB2, (int)Buttons.Close); - builder.AddHtmlLocalized(45, 294, 80, 20, 1006045, 0x7FFF); // Cancel - - // sculpt - builder.AddButton(234, 294, 0xFB7, 0xFB9, (int)Buttons.Sculpt); - builder.AddHtmlLocalized(269, 294, 80, 20, 1076174, 0x7FFF); // Sculpt - - // restore - if (_maker is CharacterStatueDeed) - { - builder.AddButton(107, 294, 0xFAB, 0xFAD, (int)Buttons.Restore); - builder.AddHtmlLocalized(142, 294, 80, 20, 1076193, 0x7FFF); // Restore - } - } - - private static int GetMaterialNumber(StatueType type, StatueMaterial material) => - material switch - { - StatueMaterial.Antique => type switch + if (m_Statue == null) { - StatueType.Bronze => 1076187, - StatueType.Jade => 1076186, - StatueType.Marble => 1076182, - _ => 1076187 - }, - StatueMaterial.Dark => type == StatueType.Marble ? 1076183 : 1076182, - StatueMaterial.Medium => 1076184, - StatueMaterial.Light => 1076185, - _ => 1076187 - }; - - private static int GetDirectionNumber(Direction direction) => - direction switch - { - Direction.North => 1075389, - Direction.Right => 1075388, - Direction.East => 1075387, - Direction.Down => 1076204, - Direction.South => 1075386, - Direction.Left => 1075391, - Direction.West => 1075390, - Direction.Up => 1076205, - _ => 1075386 - }; - - public override void OnResponse(NetState state, in RelayInfo info) - { - if (_statue?.Deleted != false) - { - return; - } - - var sendGump = false; - - if (info.ButtonID == (int)Buttons.Sculpt) - { - if (_maker is CharacterStatueDeed deed) - { - var backup = deed.Statue; - - backup?.Delete(); + return; } - _maker?.Delete(); + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - _statue.Sculpt(state.Mobile); - } - else if (info.ButtonID == (int)Buttons.PosePrev) - { - _statue.Pose = (StatuePose)(((int)_statue.Pose + 5) % 6); - sendGump = true; - } - else if (info.ButtonID == (int)Buttons.PoseNext) - { - _statue.Pose = (StatuePose)(((int)_statue.Pose + 1) % 6); - sendGump = true; - } - else if (info.ButtonID == (int)Buttons.DirPrev) - { - _statue.Direction = (Direction)(((int)_statue.Direction + 7) % 8); - _statue.InvalidatePose(); - sendGump = true; - } - else if (info.ButtonID == (int)Buttons.DirNext) - { - _statue.Direction = (Direction)(((int)_statue.Direction + 1) % 8); - _statue.InvalidatePose(); - sendGump = true; - } - else if (info.ButtonID == (int)Buttons.MatPrev) - { - _statue.Material = (StatueMaterial)(((int)_statue.Material + 3) % 4); - sendGump = true; - } - else if (info.ButtonID == (int)Buttons.MatNext) - { - _statue.Material = (StatueMaterial)(((int)_statue.Material + 1) % 4); - sendGump = true; - } - else if (info.ButtonID == (int)Buttons.Restore) - { - if (_maker is CharacterStatueDeed deed) + AddPage(0); + + AddBackground(0, 0, 327, 324, 0x13BE); + AddImageTiled(10, 10, 307, 20, 0xA40); + AddImageTiled(10, 40, 307, 244, 0xA40); + AddImageTiled(10, 294, 307, 20, 0xA40); + AddAlphaRegion(10, 10, 307, 304); + AddHtmlLocalized(14, 12, 327, 20, 1076156, 0x7FFF); // Character Statue Maker + + // pose + AddHtmlLocalized(133, 41, 120, 20, 1076168, 0x7FFF); // Choose Pose + AddHtmlLocalized(133, 61, 120, 20, 1076208 + (int)m_Statue.Pose, 0x77E); + AddButton(163, 81, 0xFA5, 0xFA7, (int)Buttons.PoseNext); + AddButton(133, 81, 0xFAE, 0xFB0, (int)Buttons.PosePrev); + + // direction + AddHtmlLocalized(133, 126, 120, 20, 1076170, 0x7FFF); // Choose Direction + AddHtmlLocalized(133, 146, 120, 20, GetDirectionNumber(m_Statue.Direction), 0x77E); + AddButton(163, 167, 0xFA5, 0xFA7, (int)Buttons.DirNext); + AddButton(133, 167, 0xFAE, 0xFB0, (int)Buttons.DirPrev); + + // material + AddHtmlLocalized(133, 211, 120, 20, 1076171, 0x7FFF); // Choose Material + AddHtmlLocalized(133, 231, 120, 20, GetMaterialNumber(m_Statue.StatueType, m_Statue.Material), 0x77E); + AddButton(163, 253, 0xFA5, 0xFA7, (int)Buttons.MatNext); + AddButton(133, 253, 0xFAE, 0xFB0, (int)Buttons.MatPrev); + + // cancel + AddButton(10, 294, 0xFB1, 0xFB2, (int)Buttons.Close); + AddHtmlLocalized(45, 294, 80, 20, 1006045, 0x7FFF); // Cancel + + // sculpt + AddButton(234, 294, 0xFB7, 0xFB9, (int)Buttons.Sculpt); + AddHtmlLocalized(269, 294, 80, 20, 1076174, 0x7FFF); // Sculpt + + // restore + if (m_Maker is CharacterStatueDeed) { - var backup = deed.Statue; + AddButton(107, 294, 0xFAB, 0xFAD, (int)Buttons.Restore); + AddHtmlLocalized(142, 294, 80, 20, 1076193, 0x7FFF); // Restore + } + } - if (backup != null) + private int GetMaterialNumber(StatueType type, StatueMaterial material) + { + switch (material) + { + case StatueMaterial.Antique: + + return type switch + { + StatueType.Bronze => 1076187, + StatueType.Jade => 1076186, + StatueType.Marble => 1076182, + _ => 1076187 + }; + + case StatueMaterial.Dark: + + if (type == StatueType.Marble) + { + return 1076183; + } + + return 1076182; + case StatueMaterial.Medium: return 1076184; + case StatueMaterial.Light: return 1076185; + default: return 1076187; + } + } + + private int GetDirectionNumber(Direction direction) + { + return direction switch + { + Direction.North => 1075389, + Direction.Right => 1075388, + Direction.East => 1075387, + Direction.Down => 1076204, + Direction.South => 1075386, + Direction.Left => 1075391, + Direction.West => 1075390, + Direction.Up => 1076205, + _ => 1075386 + }; + } + + public override void OnResponse(NetState state, in RelayInfo info) + { + if (m_Statue?.Deleted != false) + { + return; + } + + var sendGump = false; + + if (info.ButtonID == (int)Buttons.Sculpt) + { + if (m_Maker is CharacterStatueDeed deed) { - _statue.Restore(backup); + var backup = deed.Statue; + + backup?.Delete(); } + + m_Maker?.Delete(); + + m_Statue.Sculpt(state.Mobile); + } + else if (info.ButtonID == (int)Buttons.PosePrev) + { + m_Statue.Pose = (StatuePose)(((int)m_Statue.Pose + 5) % 6); + sendGump = true; + } + else if (info.ButtonID == (int)Buttons.PoseNext) + { + m_Statue.Pose = (StatuePose)(((int)m_Statue.Pose + 1) % 6); + sendGump = true; + } + else if (info.ButtonID == (int)Buttons.DirPrev) + { + m_Statue.Direction = (Direction)(((int)m_Statue.Direction + 7) % 8); + m_Statue.InvalidatePose(); + sendGump = true; + } + else if (info.ButtonID == (int)Buttons.DirNext) + { + m_Statue.Direction = (Direction)(((int)m_Statue.Direction + 1) % 8); + m_Statue.InvalidatePose(); + sendGump = true; + } + else if (info.ButtonID == (int)Buttons.MatPrev) + { + m_Statue.Material = (StatueMaterial)(((int)m_Statue.Material + 3) % 4); + sendGump = true; + } + else if (info.ButtonID == (int)Buttons.MatNext) + { + m_Statue.Material = (StatueMaterial)(((int)m_Statue.Material + 1) % 4); + sendGump = true; + } + else if (info.ButtonID == (int)Buttons.Restore) + { + if (m_Maker is CharacterStatueDeed deed) + { + var backup = deed.Statue; + + if (backup != null) + { + m_Statue.Restore(backup); + } + } + + sendGump = true; + } + else // Close + { + sendGump = !m_Statue.Demolish(state.Mobile); } - sendGump = true; - } - else // Close - { - sendGump = !_statue.Demolish(state.Mobile); + if (sendGump) + { + state.Mobile.SendGump(new CharacterStatueGump(m_Maker, m_Statue, m_Owner)); + } } - if (sendGump) + private enum Buttons { - state.Mobile.SendGump(this); + Close, + Sculpt, + PosePrev, + PoseNext, + DirPrev, + DirNext, + MatPrev, + MatNext, + Restore } } - - private enum Buttons - { - Close, - Sculpt, - PosePrev, - PoseNext, - DirPrev, - DirNext, - MatPrev, - MatNext, - Restore - } } diff --git a/Projects/UOContent/Engines/Veteran Rewards/RewardChoiceGump.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardChoiceGump.cs index 2f41bad1e..887d728a6 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/RewardChoiceGump.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/RewardChoiceGump.cs @@ -2,227 +2,217 @@ using System; using Server.Gumps; using Server.Network; -namespace Server.Engines.VeteranRewards; - -public class RewardChoiceGump : DynamicGump +namespace Server.Engines.VeteranRewards { - private readonly Mobile _from; - - public override bool Singleton => true; - - private RewardChoiceGump(Mobile from) : base(0, 0) => _from = from; - - public static void DisplayTo(Mobile from) + public class RewardChoiceGump : Gump { - if (from?.NetState == null) + private readonly Mobile m_From; + + public override bool Singleton => true; + + public RewardChoiceGump(Mobile from) : base(0, 0) { - return; + m_From = from; + + RenderBackground(); + RenderCategories(); } - from.SendGump(new RewardChoiceGump(from)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - RenderBackground(ref builder); - RenderCategories(ref builder); - } - - private static void RenderBackground(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(10, 10, 600, 450, 2600); - - builder.AddButton(530, 415, 4017, 4019, 0); - - builder.AddButton(60, 415, 4014, 4016, 0, GumpButtonType.Page, 1); - builder.AddHtmlLocalized(95, 415, 200, 20, 1049755); // Main Menu - } - - private void RenderCategories(ref DynamicGumpBuilder builder) - { - var rewardInterval = RewardSystem.RewardInterval; - - string intervalAsString; - - if (rewardInterval == TimeSpan.FromDays(30.0)) + private void RenderBackground() { - intervalAsString = "month"; - } - else if (rewardInterval == TimeSpan.FromDays(60.0)) - { - intervalAsString = "two months"; - } - else if (rewardInterval == TimeSpan.FromDays(90.0)) - { - intervalAsString = "three months"; - } - else if (rewardInterval == TimeSpan.FromDays(365.0)) - { - intervalAsString = "year"; - } - else - { - intervalAsString = $"{rewardInterval.TotalDays} day{(rewardInterval.TotalDays == 1 ? "" : "s")}"; + AddPage(0); + + AddBackground(10, 10, 600, 450, 2600); + + AddButton(530, 415, 4017, 4019, 0); + + AddButton(60, 415, 4014, 4016, 0, GumpButtonType.Page, 1); + AddHtmlLocalized(95, 415, 200, 20, 1049755); // Main Menu } - builder.AddPage(1); - - builder.AddHtml( - 60, - 35, - 500, - 70, - $"Ultima Online Rewards Program
Thank you for being a part of the Ultima Online community for a full {intervalAsString}. As a token of our appreciation, you may select from the following in-game reward items listed below. The gift items will be attributed to the character you have logged-in with on the shard you are on when you chose the item(s). The number of rewards you are entitled to are listed below and are for your entire account. To read more about these rewards before making a selection, feel free to visit the uo.com site at https://uo.com/wiki/ultima-online-wiki/items/veteran-rewards.", - background: true, - scrollbar: true - ); - - RewardSystem.ComputeRewardInfo(_from, out var cur, out var max); - - builder.AddHtmlLocalized(60, 105, 300, 35, 1006006); // Your current total of rewards to choose: - builder.AddLabel(370, 107, 50, $"{max - cur}"); - - builder.AddHtmlLocalized(60, 140, 300, 35, 1006007); // You have already chosen: - builder.AddLabel(370, 142, 50, $"{cur}"); - - var categories = RewardSystem.Categories; - - var page = 2; - - for (var i = 0; i < categories.Length; ++i) + private void RenderCategories() { - var cat = categories[i]; - if (!RewardSystem.HasAccess(_from, cat)) + var rewardInterval = RewardSystem.RewardInterval; + + string intervalAsString; + + if (rewardInterval == TimeSpan.FromDays(30.0)) { - page += 1; - continue; + intervalAsString = "month"; } - - builder.AddButton(100, 180 + i * 40, 4005, 4005, 0, GumpButtonType.Page, page); - - page += PagesPerCategory(cat); - - if (cat.NameString != null) + else if (rewardInterval == TimeSpan.FromDays(60.0)) { - builder.AddHtml(135, 180 + i * 40, 300, 20, cat.NameString); + intervalAsString = "two months"; + } + else if (rewardInterval == TimeSpan.FromDays(90.0)) + { + intervalAsString = "three months"; + } + else if (rewardInterval == TimeSpan.FromDays(365.0)) + { + intervalAsString = "year"; } else { - builder.AddHtmlLocalized(135, 180 + i * 40, 300, 20, cat.Name); - } - } - - page = 2; - - for (var i = 0; i < categories.Length; ++i) - { - RenderCategory(ref builder, categories[i], i, ref page); - } - } - - private int PagesPerCategory(RewardCategory category) - { - var entries = category.Entries; - var i = 0; - - for (var j = 0; j < entries.Count; j++) - { - if (RewardSystem.HasAccess(_from, entries[j])) - { - i++; - } - } - - return (int)Math.Ceiling(i / 24.0); - } - - private static int GetButtonID(int type, int index) => 2 + index * 20 + type; - - private void RenderCategory(ref DynamicGumpBuilder builder, RewardCategory category, int index, ref int page) - { - builder.AddPage(page); - - var entries = category.Entries; - - var i = 0; - - for (var j = 0; j < entries.Count; ++j) - { - var entry = entries[j]; - - if (!RewardSystem.HasAccess(_from, entry)) - { - continue; + intervalAsString = $"{rewardInterval.TotalDays} day{(rewardInterval.TotalDays == 1 ? "" : "s")}"; } - if (i == 24) - { - builder.AddButton(305, 415, 0xFA5, 0xFA7, 0, GumpButtonType.Page, ++page); - builder.AddHtmlLocalized(340, 415, 200, 20, 1011066); // Next page + AddPage(1); - builder.AddPage(page); + AddHtml( + 60, + 35, + 500, + 70, + $"Ultima Online Rewards Program
Thank you for being a part of the Ultima Online community for a full {intervalAsString}. As a token of our appreciation, you may select from the following in-game reward items listed below. The gift items will be attributed to the character you have logged-in with on the shard you are on when you chose the item(s). The number of rewards you are entitled to are listed below and are for your entire account. To read more about these rewards before making a selection, feel free to visit the uo.com site at http://www.uo.com/rewards.", + true, + true + ); - builder.AddButton(270, 415, 0xFAE, 0xFB0, 0, GumpButtonType.Page, page - 1); - builder.AddHtmlLocalized(185, 415, 200, 20, 1011067); // Previous page + RewardSystem.ComputeRewardInfo(m_From, out var cur, out var max); - i = 0; - } + AddHtmlLocalized(60, 105, 300, 35, 1006006); // Your current total of rewards to choose: + AddLabel(370, 107, 50, (max - cur).ToString()); - builder.AddButton(55 + i / 12 * 250, 80 + i % 12 * 25, 5540, 5541, GetButtonID(index, j)); - - if (entry.NameString != null) - { - builder.AddHtml(80 + i / 12 * 250, 80 + i % 12 * 25, 250, 20, entry.NameString); - } - else - { - builder.AddHtmlLocalized(80 + i / 12 * 250, 80 + i % 12 * 25, 250, 20, entry.Name); - } - - ++i; - } - - page += 1; - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var buttonID = info.ButtonID - 1; - - if (buttonID == 0) - { - RewardSystem.ComputeRewardInfo(_from, out var cur, out var max); - - if (cur < max) - { - RewardNoticeGump.DisplayTo(_from); - } - } - else - { - --buttonID; - - var type = buttonID % 20; - var index = buttonID / 20; + AddHtmlLocalized(60, 140, 300, 35, 1006007); // You have already chosen: + AddLabel(370, 142, 50, cur.ToString()); var categories = RewardSystem.Categories; - if (type >= 0 && type < categories.Length) + var page = 2; + + for (var i = 0; i < categories.Length; ++i) { - var category = categories[type]; - - if (index >= 0 && index < category.Entries.Count) + if (!RewardSystem.HasAccess(m_From, categories[i])) { - var entry = category.Entries[index]; + page += 1; + continue; + } - if (!RewardSystem.HasAccess(_from, entry)) + AddButton(100, 180 + i * 40, 4005, 4005, 0, GumpButtonType.Page, page); + + page += PagesPerCategory(categories[i]); + + if (categories[i].NameString != null) + { + AddHtml(135, 180 + i * 40, 300, 20, categories[i].NameString); + } + else + { + AddHtmlLocalized(135, 180 + i * 40, 300, 20, categories[i].Name); + } + } + + page = 2; + + for (var i = 0; i < categories.Length; ++i) + { + RenderCategory(categories[i], i, ref page); + } + } + + private int PagesPerCategory(RewardCategory category) + { + var entries = category.Entries; + var i = 0; + + for (var j = 0; j < entries.Count; j++) + { + if (RewardSystem.HasAccess(m_From, entries[j])) + { + i++; + } + } + + return (int)Math.Ceiling(i / 24.0); + } + + private int GetButtonID(int type, int index) => 2 + index * 20 + type; + + private void RenderCategory(RewardCategory category, int index, ref int page) + { + AddPage(page); + + var entries = category.Entries; + + var i = 0; + + for (var j = 0; j < entries.Count; ++j) + { + var entry = entries[j]; + + if (!RewardSystem.HasAccess(m_From, entry)) + { + continue; + } + + if (i == 24) + { + AddButton(305, 415, 0xFA5, 0xFA7, 0, GumpButtonType.Page, ++page); + AddHtmlLocalized(340, 415, 200, 20, 1011066); // Next page + + AddPage(page); + + AddButton(270, 415, 0xFAE, 0xFB0, 0, GumpButtonType.Page, page - 1); + AddHtmlLocalized(185, 415, 200, 20, 1011067); // Previous page + + i = 0; + } + + AddButton(55 + i / 12 * 250, 80 + i % 12 * 25, 5540, 5541, GetButtonID(index, j)); + + if (entry.NameString != null) + { + AddHtml(80 + i / 12 * 250, 80 + i % 12 * 25, 250, 20, entry.NameString); + } + else + { + AddHtmlLocalized(80 + i / 12 * 250, 80 + i % 12 * 25, 250, 20, entry.Name); + } + + ++i; + } + + page += 1; + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + var buttonID = info.ButtonID - 1; + + if (buttonID == 0) + { + RewardSystem.ComputeRewardInfo(m_From, out var cur, out var max); + + if (cur < max) + { + m_From.SendGump(new RewardNoticeGump(m_From)); + } + } + else + { + --buttonID; + + var type = buttonID % 20; + var index = buttonID / 20; + + var categories = RewardSystem.Categories; + + if (type >= 0 && type < categories.Length) + { + var category = categories[type]; + + if (index >= 0 && index < category.Entries.Count) { - return; - } + var entry = category.Entries[index]; - RewardConfirmGump.DisplayTo(_from, entry); + if (!RewardSystem.HasAccess(m_From, entry)) + { + return; + } + + m_From.SendGump(new RewardConfirmGump(m_From, entry)); + } } } } diff --git a/Projects/UOContent/Engines/Veteran Rewards/RewardConfirmGump.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardConfirmGump.cs index 1db1aa39d..200f73d91 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/RewardConfirmGump.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/RewardConfirmGump.cs @@ -2,97 +2,90 @@ using Server.Gumps; using Server.Items; using Server.Network; -namespace Server.Engines.VeteranRewards; - -public class RewardConfirmGump : DynamicGump +namespace Server.Engines.VeteranRewards { - private readonly RewardEntry _entry; - private readonly Mobile _from; - - public override bool Singleton => true; - - private RewardConfirmGump(Mobile from, RewardEntry entry) : base(0, 0) + public class RewardConfirmGump : Gump { - _from = from; - _entry = entry; - } + private readonly RewardEntry m_Entry; + private readonly Mobile m_From; - public static void DisplayTo(Mobile from, RewardEntry entry) - { - if (from?.NetState == null || entry == null) + public override bool Singleton => true; + + public RewardConfirmGump(Mobile from, RewardEntry entry) : base(0, 0) { - return; - } + m_From = from; + m_Entry = entry; - from.SendGump(new RewardConfirmGump(from, entry)); - } + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddBackground(10, 10, 500, 300, 2600); - builder.AddBackground(10, 10, 500, 300, 2600); + AddHtmlLocalized(30, 55, 300, 35, 1006000); // You have selected: - builder.AddHtmlLocalized(30, 55, 300, 35, 1006000); // You have selected: - - if (_entry.NameString != null) - { - builder.AddHtml(335, 55, 150, 35, _entry.NameString); - } - else - { - builder.AddHtmlLocalized(335, 55, 150, 35, _entry.Name); - } - - builder.AddHtmlLocalized(30, 95, 300, 35, 1006001); // This will be assigned to this character: - builder.AddLabel(335, 95, 0, _from.RawName); - - // Are you sure you wish to select this reward for this character? - // You will not be able to transfer this reward to another character on another shard. - // Click 'ok' below to confirm your selection or 'cancel' to go back to the selection screen. - builder.AddHtmlLocalized(35, 160, 450, 90, 1006002, true, true); - - builder.AddButton(60, 265, 4005, 4007, 1); - builder.AddHtmlLocalized(95, 266, 150, 35, 1006044); // Ok - - builder.AddButton(295, 265, 4017, 4019, 0); - builder.AddHtmlLocalized(330, 266, 150, 35, 1006045); // Cancel - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID == 1) - { - if (!RewardSystem.HasAccess(_from, _entry)) + if (entry.NameString != null) { - return; + AddHtml(335, 55, 150, 35, entry.NameString); + } + else + { + AddHtmlLocalized(335, 55, 150, 35, entry.Name); } - var item = _entry.Construct(); + AddHtmlLocalized(30, 95, 300, 35, 1006001); // This will be assigned to this character: + AddLabel(335, 95, 0, from.Name); - if (item != null) - { - if (item is RedSoulstone soulstone) - { - soulstone.Account = _from.Account.Username; - } + AddHtmlLocalized( + 35, + 160, + 450, + 90, + 1006002, + true, + true + ); // Are you sure you wish to select this reward for this character? You will not be able to transfer this reward to another character on another shard. Click 'ok' below to confirm your selection or 'cancel' to go back to the selection screen. - if (RewardSystem.ConsumeRewardPoint(_from)) - { - _from.AddToBackpack(item); - } - else - { - item.Delete(); - } - } + AddButton(60, 265, 4005, 4007, 1); + AddHtmlLocalized(95, 266, 150, 35, 1006044); // Ok + + AddButton(295, 265, 4017, 4019, 0); + AddHtmlLocalized(330, 266, 150, 35, 1006045); // Cancel } - RewardSystem.ComputeRewardInfo(_from, out var cur, out var max); - - if (cur < max) + public override void OnResponse(NetState sender, in RelayInfo info) { - RewardNoticeGump.DisplayTo(_from); + if (info.ButtonID == 1) + { + if (!RewardSystem.HasAccess(m_From, m_Entry)) + { + return; + } + + var item = m_Entry.Construct(); + + if (item != null) + { + if (item is RedSoulstone soulstone) + { + soulstone.Account = m_From.Account.Username; + } + + if (RewardSystem.ConsumeRewardPoint(m_From)) + { + m_From.AddToBackpack(item); + } + else + { + item.Delete(); + } + } + } + + RewardSystem.ComputeRewardInfo(m_From, out var cur, out var max); + + if (cur < max) + { + m_From.SendGump(new RewardNoticeGump(m_From)); + } } } } diff --git a/Projects/UOContent/Engines/Veteran Rewards/RewardDemolitionGump.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardDemolitionGump.cs index 2fe6a222b..33df28c6e 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/RewardDemolitionGump.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/RewardDemolitionGump.cs @@ -2,89 +2,78 @@ using Server.Items; using Server.Multis; using Server.Network; -namespace Server.Gumps; - -public class RewardDemolitionGump : DynamicGump +namespace Server.Gumps { - private readonly IAddon _addon; - private readonly int _question; - - public override bool Singleton => true; - - private RewardDemolitionGump(IAddon addon, int question) : base(150, 50) + public class RewardDemolitionGump : Gump { - _addon = addon; - _question = question; - } + private readonly IAddon m_Addon; - public static void DisplayTo(Mobile from, IAddon addon, int question) - { - if (from?.NetState == null || addon is not Item item || item.Deleted) + public override bool Singleton => true; + + public RewardDemolitionGump(IAddon addon, int question) : base(150, 50) { - return; + m_Addon = addon; + + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; + + AddBackground(0, 0, 220, 170, 0x13BE); + AddBackground(10, 10, 200, 150, 0xBB8); + + AddHtmlLocalized(20, 30, 180, 60, question); // Do you wish to re-deed this decoration? + + AddHtmlLocalized(55, 100, 150, 25, 1011011); // CONTINUE + AddButton(20, 100, 0xFA5, 0xFA7, (int)Buttons.Confirm); + + AddHtmlLocalized(55, 125, 150, 25, 1011012); // CANCEL + AddButton(20, 125, 0xFA5, 0xFA7, (int)Buttons.Cancel); } - from.SendGump(new RewardDemolitionGump(addon, question)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 220, 170, 0x13BE); - builder.AddBackground(10, 10, 200, 150, 0xBB8); - - builder.AddHtmlLocalized(20, 30, 180, 60, _question); // Do you wish to re-deed this decoration? - - builder.AddHtmlLocalized(55, 100, 150, 25, 1011011); // CONTINUE - builder.AddButton(20, 100, 0xFA5, 0xFA7, (int)Buttons.Confirm); - - builder.AddHtmlLocalized(55, 125, 150, 25, 1011012); // CANCEL - builder.AddButton(20, 125, 0xFA5, 0xFA7, (int)Buttons.Cancel); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (_addon is not Item item || item.Deleted) + public override void OnResponse(NetState sender, in RelayInfo info) { - return; - } - - if (info.ButtonID != (int)Buttons.Confirm) - { - return; - } - - var m = sender.Mobile; - var house = BaseHouse.FindHouseAt(m); - - if (house?.IsOwner(m) != true) - { - // You can only re-deed this decoration if you are the house owner or originally placed the decoration. - m.SendLocalizedMessage(1049784); - return; - } - - if (m.InRange(item.Location, 2)) - { - var deed = _addon.Deed; - - if (deed != null) + if (m_Addon is not Item item || item.Deleted) { - m.AddToBackpack(deed); - house.Addons.Remove(item); - item.Delete(); + return; + } + + if (info.ButtonID != (int)Buttons.Confirm) + { + return; + } + + var m = sender.Mobile; + var house = BaseHouse.FindHouseAt(m); + + if (house?.IsOwner(m) != true) + { + // You can only re-deed this decoration if you are the house owner or originally placed the decoration. + m.SendLocalizedMessage(1049784); + return; + } + + if (m.InRange(item.Location, 2)) + { + var deed = m_Addon.Deed; + + if (deed != null) + { + m.AddToBackpack(deed); + house.Addons.Remove(item); + item.Delete(); + } + } + else + { + m.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } - else + + private enum Buttons { - m.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + Cancel, + Confirm } } - - private enum Buttons - { - Cancel, - Confirm - } } diff --git a/Projects/UOContent/Engines/Veteran Rewards/RewardNoticeGump.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardNoticeGump.cs index 358affb48..dc1294822 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/RewardNoticeGump.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/RewardNoticeGump.cs @@ -1,49 +1,40 @@ using Server.Gumps; using Server.Network; -namespace Server.Engines.VeteranRewards; - -public class RewardNoticeGump : StaticGump +namespace Server.Engines.VeteranRewards { - public override bool Singleton => true; - - private RewardNoticeGump() : base(0, 0) + public class RewardNoticeGump : Gump { - } + private readonly Mobile m_From; - public static void DisplayTo(Mobile from) - { - if (from?.NetState == null) + public override bool Singleton => true; + + public RewardNoticeGump(Mobile from) : base(0, 0) { - return; + m_From = from; + + AddPage(0); + + AddBackground(10, 10, 500, 135, 2600); + + /* You have reward items available. + * Click 'ok' below to get the selection menu or 'cancel' to be prompted upon your next login. + */ + AddHtmlLocalized(52, 35, 420, 55, 1006046, true, true); + + AddButton(60, 95, 4005, 4007, 1); + AddHtmlLocalized(95, 96, 150, 35, 1006044); // Ok + + AddButton(285, 95, 4017, 4019, 0); + AddHtmlLocalized(320, 96, 150, 35, 1006045); // Cancel } - from.SendGump(new RewardNoticeGump()); - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(10, 10, 500, 135, 2600); - - /* You have reward items available. - * Click 'ok' below to get the selection menu or 'cancel' to be prompted upon your next login. - */ - builder.AddHtmlLocalized(52, 35, 420, 55, 1006046, true, true); - - builder.AddButton(60, 95, 4005, 4007, 1); - builder.AddHtmlLocalized(95, 96, 150, 35, 1006044); // Ok - - builder.AddButton(285, 95, 4017, 4019, 0); - builder.AddHtmlLocalized(320, 96, 150, 35, 1006045); // Cancel - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID == 1) + public override void OnResponse(NetState sender, in RelayInfo info) { - RewardChoiceGump.DisplayTo(sender.Mobile); + if (info.ButtonID == 1) + { + m_From.SendGump(new RewardChoiceGump(m_From)); + } } } } diff --git a/Projects/UOContent/Engines/Veteran Rewards/RewardOptionGump.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardOptionGump.cs index 1d26d1fa0..1b2b0bb7b 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/RewardOptionGump.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/RewardOptionGump.cs @@ -1,115 +1,101 @@ using System.Collections.Generic; using Server.Network; -namespace Server.Gumps; - -public interface IRewardOption +namespace Server.Gumps { - void GetOptions(RewardOptionList list); - void OnOptionSelected(Mobile from, int choice); -} - -public class RewardOptionGump : DynamicGump -{ - private readonly IRewardOption _option; - private readonly RewardOptionList _options = new(); - private readonly int _title; - - public override bool Singleton => true; - - private RewardOptionGump(IRewardOption option, int title) : base(60, 36) + public interface IRewardOption { - _option = option; - _title = title; - - _option?.GetOptions(_options); + void GetOptions(RewardOptionList list); + void OnOptionSelected(Mobile from, int choice); } - public static void DisplayTo(Mobile from, IRewardOption option, int title = 0) + public class RewardOptionGump : Gump { - if (from?.NetState == null || option == null) + private readonly IRewardOption m_Option; + private readonly RewardOptionList m_Options = new(); + + public override bool Singleton => true; + + public RewardOptionGump(IRewardOption option, int title = 0) : base(60, 36) { - return; - } + m_Option = option; - from.SendGump(new RewardOptionGump(option, title)); - } + m_Option?.GetOptions(m_Options); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(0, 0, 273, 324, 0x13BE); - builder.AddImageTiled(10, 10, 253, 20, 0xA40); - builder.AddImageTiled(10, 40, 253, 244, 0xA40); - builder.AddImageTiled(10, 294, 253, 20, 0xA40); - builder.AddAlphaRegion(10, 10, 253, 304); + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); - builder.AddButton(10, 294, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL - if (_title > 0) - { - builder.AddHtmlLocalized(14, 12, 273, 20, _title, 0x7FFF); - } - else - { - builder.AddHtmlLocalized(14, 12, 273, 20, 1080392, 0x7FFF); // Select your choice from the menu below. - } - - builder.AddPage(1); - - for (var i = 0; i < _options.Count; i++) - { - builder.AddButton(19, 49 + i * 24, 0x845, 0x846, _options[i].ID); - builder.AddHtmlLocalized(44, 47 + i * 24, 213, 20, _options[i].Cliloc, 0x7FFF); - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (_option != null && Contains(info.ButtonID)) - { - _option.OnOptionSelected(sender.Mobile, info.ButtonID); - } - } - - private bool Contains(int chosen) - { - if (_options == null) - { - return false; - } - - foreach (var option in _options) - { - if (option.ID == chosen) + if (title > 0) { - return true; + AddHtmlLocalized(14, 12, 273, 20, title, 0x7FFF); + } + else + { + AddHtmlLocalized(14, 12, 273, 20, 1080392, 0x7FFF); // Select your choice from the menu below. + } + + AddPage(1); + + for (var i = 0; i < m_Options.Count; i++) + { + AddButton(19, 49 + i * 24, 0x845, 0x846, m_Options[i].ID); + AddHtmlLocalized(44, 47 + i * 24, 213, 20, m_Options[i].Cliloc, 0x7FFF); } } - return false; - } -} + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (m_Option != null && Contains(info.ButtonID)) + { + m_Option.OnOptionSelected(sender.Mobile, info.ButtonID); + } + } -public class RewardOption -{ - public RewardOption(int id, int cliloc) + private bool Contains(int chosen) + { + if (m_Options == null) + { + return false; + } + + foreach (var option in m_Options) + { + if (option.ID == chosen) + { + return true; + } + } + + return false; + } + } + + public class RewardOption { - ID = id; - Cliloc = cliloc; + public RewardOption(int id, int cliloc) + { + ID = id; + Cliloc = cliloc; + } + + public int ID { get; } + + public int Cliloc { get; } } - public int ID { get; } - - public int Cliloc { get; } -} - -public class RewardOptionList : List -{ - public void Add(int id, int cliloc) + public class RewardOptionList : List { - Add(new RewardOption(id, cliloc)); + public void Add(int id, int cliloc) + { + Add(new RewardOption(id, cliloc)); + } } } diff --git a/Projects/UOContent/Engines/Veteran Rewards/RewardSystem.cs b/Projects/UOContent/Engines/Veteran Rewards/RewardSystem.cs index 4121a5520..7b802f4a3 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/RewardSystem.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/RewardSystem.cs @@ -1,6 +1,7 @@ using System; using ModernUO.CodeGeneratedEvents; using Server.Accounting; +using Server.Gumps; using Server.Items; using Server.Mobiles; @@ -594,7 +595,7 @@ namespace Server.Engines.VeteranRewards if (cur < max) { - RewardNoticeGump.DisplayTo(pm); + pm.SendGump(new RewardNoticeGump(pm)); } } } diff --git a/Projects/UOContent/Engines/Virtues/Honor.cs b/Projects/UOContent/Engines/Virtues/Honor.cs index 45a909947..51fe093ac 100644 --- a/Projects/UOContent/Engines/Virtues/Honor.cs +++ b/Projects/UOContent/Engines/Virtues/Honor.cs @@ -75,7 +75,7 @@ public static class HonorVirtue var duration = GetHonorDuration(pm); var virtues = VirtueSystem.GetOrCreateVirtues(pm); - var usedPoints = virtues.Honor switch + int usedPoints = virtues.Honor switch { < 4399 => 400, < 10599 => 600, diff --git a/Projects/UOContent/Engines/Virtues/Justice.cs b/Projects/UOContent/Engines/Virtues/Justice.cs index 47d7bb14f..25fc6a79d 100644 --- a/Projects/UOContent/Engines/Virtues/Justice.cs +++ b/Projects/UOContent/Engines/Virtues/Justice.cs @@ -179,7 +179,7 @@ public class JusticeVirtue { protector.SendLocalizedMessage(1049372); // You cannot use this ability here. } - else if (pm == protector || pm.Criminal || pm.Murderer) + else if (pm == protector || pm.Criminal || pm.Kills >= 5) { protector.SendLocalizedMessage(1049436); // That player cannot be protected. } @@ -219,7 +219,7 @@ public class JusticeVirtue { protector.SendLocalizedMessage(1049372); // You cannot use this ability here. } - else if (protectee == protector || protectee.Criminal || protectee.Murderer) + else if (protectee == protector || protectee.Criminal || protectee.Kills >= 5) { protector.SendLocalizedMessage(1049436); // That player cannot be protected. } diff --git a/Projects/UOContent/Engines/Virtues/Sacrifice.cs b/Projects/UOContent/Engines/Virtues/Sacrifice.cs index 51d48ed91..0cf9d612f 100644 --- a/Projects/UOContent/Engines/Virtues/Sacrifice.cs +++ b/Projects/UOContent/Engines/Virtues/Sacrifice.cs @@ -130,7 +130,7 @@ public static class SacrificeVirtue } else { - var toGain = from.Fame switch + int toGain = from.Fame switch { < 5000 => 500, < 10000 => 1000, diff --git a/Projects/UOContent/Engines/Virtues/VirtueContext.cs b/Projects/UOContent/Engines/Virtues/VirtueContext.cs index 7524f77ed..3b26b5294 100644 --- a/Projects/UOContent/Engines/Virtues/VirtueContext.cs +++ b/Projects/UOContent/Engines/Virtues/VirtueContext.cs @@ -5,121 +5,102 @@ using Server.Mobiles; namespace Server.Engines.Virtues; [PropertyObject] -[SerializationGenerator(1)] +[SerializationGenerator(0)] public partial class VirtueContext { - private void MigrateFrom(V0Content content) - { - // Save-flagged values arrive as nullables; unset flags fall back to the same - // defaults the old deserialize left in place. - _lastSacrificeGain = content.LastSacrificeGain ?? default; - _lastSacrificeLoss = content.LastSacrificeLoss ?? default; - _availableResurrects = content.AvailableResurrects ?? 0; - _lastJusticeLoss = content.LastJusticeLoss ?? default; - _lastCompassionLoss = content.LastCompassionLoss ?? default; - _nextCompassionDay = content.NextCompassionDay ?? default; - _compassionGains = content.CompassionGains ?? 0; - _lastValorLoss = content.LastValorLoss ?? default; - _lastHonorUse = content.LastHonorUse ?? default; - _honorActive = content.HonorActive; - _justiceProtection = content.JusticeProtection; - _justiceStatus = content.JusticeStatus ?? JusticeProtectorStatus.None; - _values = content.Values; - } - - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(0)] - [SaveFlag(nameof(ShouldSerializeLastSacrificeGain))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private DateTime _lastSacrificeGain; + [SerializableFieldSaveFlag(0)] private bool ShouldSerializeLastSacrificeGain() => !SacrificeVirtue.CanGain(this); - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(1)] - [SaveFlag(nameof(ShouldSerializeLastSacrificeLoss))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private DateTime _lastSacrificeLoss; + [SerializableFieldSaveFlag(1)] private bool ShouldSerializeLastSacrificeLoss() => !SacrificeVirtue.CanAtrophy(this); [SerializableField(2)] - [SaveFlag(nameof(ShouldSerializeAvailableResurrects))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _availableResurrects; + [SerializableFieldSaveFlag(2)] private bool ShouldSerializeAvailableResurrects() => _availableResurrects > 0; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(3)] - [SaveFlag(nameof(ShouldSerializeLastJusticeLoss))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private DateTime _lastJusticeLoss; + [SerializableFieldSaveFlag(3)] private bool ShouldSerializeLastJusticeLoss() => !JusticeVirtue.CanAtrophy(this); - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(4)] - [SaveFlag(nameof(ShouldSerializeLastCompassionLoss))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private DateTime _lastCompassionLoss; + [SerializableFieldSaveFlag(4)] private bool ShouldSerializeLastCompassionLoss() => !CompassionVirtue.CanAtrophy(this); - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(5)] - [SaveFlag(nameof(ShouldSerializeNextCompassionDay))] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _nextCompassionDay; + [SerializableFieldSaveFlag(5)] private bool ShouldSerializeNextCompassionDay() => _nextCompassionDay > Core.Now; [SerializableField(6)] - [SaveFlag(nameof(ShouldSerializeCompassionGains))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _compassionGains; + [SerializableFieldSaveFlag(6)] private bool ShouldSerializeCompassionGains() => _compassionGains > 0; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(7)] - [SaveFlag(nameof(ShouldSerializeValorLoss))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private DateTime _lastValorLoss; + [SerializableFieldSaveFlag(7)] private bool ShouldSerializeValorLoss() => !ValorVirtue.CanAtrophy(this); - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(8)] - [SaveFlag(nameof(ShouldSerializeLastHonorUse))] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _lastHonorUse; + [SerializableFieldSaveFlag(8)] private bool ShouldSerializeLastHonorUse() => !HonorVirtue.CanUse(this); [SerializableField(9)] - [SaveFlag(nameof(ShouldSerializeHonorActive))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] private bool _honorActive; + [SerializableFieldSaveFlag(9)] private bool ShouldSerializeHonorActive() => _honorActive; [SerializableField(10)] - [SaveFlag(nameof(ShouldSerializeJusticeProtection))] private PlayerMobile _justiceProtection; + [SerializableFieldSaveFlag(10)] private bool ShouldSerializeJusticeProtection() => _justiceProtection != null && _justiceStatus != JusticeProtectorStatus.None; [SerializableField(11)] - [SaveFlag(nameof(ShouldSerializeJusticeStatus))] private JusticeProtectorStatus _justiceStatus; + [SerializableFieldSaveFlag(11)] private bool ShouldSerializeJusticeStatus() => _justiceProtection != null && _justiceStatus != JusticeProtectorStatus.None; [SerializableField(12, setter: "private")] - [SaveFlag(nameof(ShouldSerializeValues))] private int[] _values; + [SerializableFieldSaveFlag(12)] private bool ShouldSerializeValues() { if (_values == null) diff --git a/Projects/UOContent/Engines/Virtues/VirtueGump.cs b/Projects/UOContent/Engines/Virtues/VirtueGump.cs index 08c5aff96..7beb2f401 100644 --- a/Projects/UOContent/Engines/Virtues/VirtueGump.cs +++ b/Projects/UOContent/Engines/Virtues/VirtueGump.cs @@ -7,7 +7,7 @@ namespace Server.Engines.Virtues; public delegate void OnVirtueUsed(PlayerMobile from); -public class VirtueGump : DynamicGump +public class VirtueGump : Gump { private static readonly Dictionary _callbacks = new(); @@ -23,19 +23,6 @@ public class VirtueGump : DynamicGump 0x0543, 0x0547, 0x0061 }; - private readonly PlayerMobile _beheld; - private readonly PlayerMobile _beholder; - - public override bool Singleton => true; - - private VirtueGump(PlayerMobile beholder, PlayerMobile beheld) : base(0, 0) - { - _beholder = beholder; - _beheld = beheld; - - Serial = beheld.Serial; - } - public static void Register(int gumpID, OnVirtueUsed callback) { _callbacks[gumpID] = callback; @@ -43,13 +30,13 @@ public class VirtueGump : DynamicGump public static void RequestVirtueGump(PlayerMobile beholder, PlayerMobile beheld) { - if (beholder == beheld && beholder.Murderer) + if (beholder == beheld && beholder.Kills >= 5) { beholder.SendLocalizedMessage(1049609); // Murderers cannot invoke this virtue. } else if (beholder.Map == beheld.Map && beholder.InRange(beheld, 12)) { - beholder.SendGump(new VirtueGump(beholder, beheld)); + beholder.SendGump(new VirtueGump(beholder, beheld), true); } } @@ -62,7 +49,7 @@ public class VirtueGump : DynamicGump beholder.CloseGump(); - if (beholder.Murderer) + if (beholder.Kills >= 5) { beholder.SendLocalizedMessage(1049609); // Murderers cannot invoke this virtue. return; @@ -91,33 +78,41 @@ public class VirtueGump : DynamicGump RequestVirtueItem(beholder, beholder, virtueID); } - protected override void BuildLayout(ref DynamicGumpBuilder builder) + private readonly PlayerMobile _beheld; + private readonly PlayerMobile _beholder; + + public VirtueGump(PlayerMobile beholder, PlayerMobile beheld) : base(0, 0) { - builder.AddPage(); + _beholder = beholder; + _beheld = beheld; - builder.AddImage(30, 40, 104); + Serial = beheld.Serial; - builder.AddPage(1); + AddPage(0); - builder.AddImage(61, 71, 108, GetHueFor(0), "VirtueGumpItem"); // Humility - builder.AddImage(123, 46, 112, GetHueFor(4), "VirtueGumpItem"); // Valor - builder.AddImage(187, 70, 107, GetHueFor(5), "VirtueGumpItem"); // Honor - builder.AddImage(35, 135, 110, GetHueFor(1), "VirtueGumpItem"); // Sacrifice - builder.AddImage(211, 133, 105, GetHueFor(2), "VirtueGumpItem"); // Compassion - builder.AddImage(61, 195, 111, GetHueFor(3), "VirtueGumpItem"); // Spirituality - builder.AddImage(186, 195, 109, GetHueFor(6), "VirtueGumpItem"); // Justice - builder.AddImage(121, 221, 106, GetHueFor(7), "VirtueGumpItem"); // Honesty + AddImage(30, 40, 104); + + AddPage(1); + + Add(new VirtueGumpItem(61, 71, 108, GetHueFor(0))); // Humility + Add(new VirtueGumpItem(123, 46, 112, GetHueFor(4))); // Valor + Add(new VirtueGumpItem(187, 70, 107, GetHueFor(5))); // Honor + Add(new VirtueGumpItem(35, 135, 110, GetHueFor(1))); // Sacrifice + Add(new VirtueGumpItem(211, 133, 105, GetHueFor(2))); // Compassion + Add(new VirtueGumpItem(61, 195, 111, GetHueFor(3))); // Spirituality + Add(new VirtueGumpItem(186, 195, 109, GetHueFor(6))); // Justice + Add(new VirtueGumpItem(121, 221, 106, GetHueFor(7))); // Honesty if (_beholder == _beheld) { - builder.AddButton(57, 269, 2027, 2027, 1); - builder.AddButton(186, 269, 2071, 2071, 2); + AddButton(57, 269, 2027, 2027, 1); + AddButton(186, 269, 2071, 2071, 2); } } private int GetHueFor(int index) { - var value = VirtueSystem.GetVirtues(_beheld)?.GetValue(index) ?? 0; + var value = VirtueSystem.GetVirtues((_beheld))?.GetValue(index) ?? 0; if (value < 4000) { @@ -129,7 +124,7 @@ public class VirtueGump : DynamicGump value = 20000; } - var vl = value switch + int vl = value switch { < 10000 => 0, >= 20000 when index == 5 => 2, @@ -145,7 +140,14 @@ public class VirtueGump : DynamicGump { if (info.ButtonID == 1 && _beholder == _beheld) { - VirtueStatusGump.DisplayTo(_beholder); + _beholder.SendGump(new VirtueStatusGump(_beholder)); + } + } + + private class VirtueGumpItem : GumpImage + { + public VirtueGumpItem(int x, int y, int gumpID, int hue) : base(x, y, gumpID, hue, "VirtueGumpItem") + { } } } diff --git a/Projects/UOContent/Engines/Virtues/VirtueInfoGump.cs b/Projects/UOContent/Engines/Virtues/VirtueInfoGump.cs index 04246dcc3..ea76e26d6 100644 --- a/Projects/UOContent/Engines/Virtues/VirtueInfoGump.cs +++ b/Projects/UOContent/Engines/Virtues/VirtueInfoGump.cs @@ -4,47 +4,32 @@ using Server.Network; namespace Server.Engines.Virtues; -public class VirtueInfoGump : DynamicGump +public class VirtueInfoGump : Gump { private readonly PlayerMobile _beholder; private readonly int _desc; private readonly string _site; private readonly VirtueName _virtue; - public override bool Singleton => true; - - private VirtueInfoGump(PlayerMobile beholder, VirtueName virtue, int description, string webPage) : base(0, 0) + public VirtueInfoGump(PlayerMobile beholder, VirtueName virtue, int description, string webPage = null) : base(0, 0) { _beholder = beholder; _virtue = virtue; _desc = description; _site = webPage; - } - public static void DisplayTo(PlayerMobile beholder, VirtueName virtue, int description, string webPage = null) - { - if (beholder?.NetState == null) - { - return; - } + var value = VirtueSystem.GetVirtues(beholder)?.GetValue((int)virtue) ?? 0; - beholder.SendGump(new VirtueInfoGump(beholder, virtue, description, webPage)); - } + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var value = VirtueSystem.GetVirtues(_beholder)?.GetValue((int)_virtue) ?? 0; + AddImage(30, 40, 2080); + AddImage(47, 77, 2081); + AddImage(47, 147, 2081); + AddImage(47, 217, 2081); + AddImage(47, 267, 2083); + AddImage(70, 213, 2091); - builder.AddPage(); - - builder.AddImage(30, 40, 2080); - builder.AddImage(47, 77, 2081); - builder.AddImage(47, 147, 2081); - builder.AddImage(47, 217, 2081); - builder.AddImage(47, 267, 2083); - builder.AddImage(70, 213, 2091); - - builder.AddPage(1); + AddPage(1); var maxValue = VirtueSystem.GetMaxAmount(_virtue); @@ -70,7 +55,7 @@ public class VirtueInfoGump : DynamicGump for (var i = 0; i < 10; ++i) { - builder.AddImage(95 + i * 17, 50, i < dots ? 2362 : 2360); + AddImage(95 + i * 17, 50, i < dots ? 2362 : 2360); } if (value < 1) @@ -110,16 +95,16 @@ public class VirtueInfoGump : DynamicGump valueDesc = 1052050; // You have achieved the highest path in this Virtue. } - builder.AddHtmlLocalized(157, 73, 200, 40, 1051000 + (int)_virtue); - builder.AddHtmlLocalized(75, 95, 220, 140, _desc); - builder.AddHtmlLocalized(70, 224, 229, 60, valueDesc); + AddHtmlLocalized(157, 73, 200, 40, 1051000 + (int)virtue); + AddHtmlLocalized(75, 95, 220, 140, description); + AddHtmlLocalized(70, 224, 229, 60, valueDesc); - builder.AddButton(65, 277, 1209, 1209, 1); + AddButton(65, 277, 1209, 1209, 1); - builder.AddButton(280, 43, 4014, 4014, 2); + AddButton(280, 43, 4014, 4014, 2); // This virtue is not yet defined. OR -click to learn more (opens webpage) - builder.AddHtmlLocalized(83, 275, 400, 40, _site == null ? 1052055 : 1052052); + AddHtmlLocalized(83, 275, 400, 40, webPage == null ? 1052055 : 1052052); } public override void OnResponse(NetState state, in RelayInfo info) @@ -128,7 +113,7 @@ public class VirtueInfoGump : DynamicGump { case 1: { - _beholder.SendGump(this); + _beholder.SendGump(new VirtueInfoGump(_beholder, _virtue, _desc, _site)); if (_site != null) { @@ -139,7 +124,7 @@ public class VirtueInfoGump : DynamicGump } case 2: { - VirtueStatusGump.DisplayTo(_beholder); + _beholder.SendGump(new VirtueStatusGump(_beholder)); break; } } diff --git a/Projects/UOContent/Engines/Virtues/VirtueStatusGump.cs b/Projects/UOContent/Engines/Virtues/VirtueStatusGump.cs index 7b14855c7..d673e62f9 100644 --- a/Projects/UOContent/Engines/Virtues/VirtueStatusGump.cs +++ b/Projects/UOContent/Engines/Virtues/VirtueStatusGump.cs @@ -4,63 +4,48 @@ using Server.Network; namespace Server.Engines.Virtues; -public class VirtueStatusGump : StaticGump +public class VirtueStatusGump : Gump { private readonly PlayerMobile _beholder; - public override bool Singleton => true; - - private VirtueStatusGump(PlayerMobile beholder) : base(0, 0) + public VirtueStatusGump(PlayerMobile beholder) : base(0, 0) { _beholder = beholder; - } - public static void DisplayTo(PlayerMobile beholder) - { - if (beholder?.NetState == null) - { - return; - } + AddPage(0); - beholder.SendGump(new VirtueStatusGump(beholder)); - } + AddImage(30, 40, 2080); + AddImage(47, 77, 2081); + AddImage(47, 147, 2081); + AddImage(47, 217, 2081); + AddImage(47, 267, 2083); + AddImage(70, 213, 2091); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddPage(1); - builder.AddImage(30, 40, 2080); - builder.AddImage(47, 77, 2081); - builder.AddImage(47, 147, 2081); - builder.AddImage(47, 217, 2081); - builder.AddImage(47, 267, 2083); - builder.AddImage(70, 213, 2091); + AddHtmlLocalized(140, 73, 200, 20, 1077972); // The Virtues - builder.AddPage(1); + AddHtmlLocalized(80, 100, 100, 40, 1051000); // Humility + AddHtmlLocalized(80, 129, 100, 40, 1051001); // Sacrifice + AddHtmlLocalized(80, 159, 100, 40, 1051002); // Compassion + AddHtmlLocalized(80, 189, 100, 40, 1051003); // Spirituality + AddHtmlLocalized(200, 100, 200, 40, 1051004); // Valor + AddHtmlLocalized(200, 129, 200, 40, 1051005); // Honor + AddHtmlLocalized(200, 159, 200, 40, 1051006); // Justice + AddHtmlLocalized(200, 189, 200, 40, 1051007); // Honesty - builder.AddHtmlLocalized(140, 73, 200, 20, 1077972); // The Virtues + AddHtmlLocalized(75, 224, 220, 60, 1052062); // Click on a blue gem to view your status in that virtue. - builder.AddHtmlLocalized(80, 100, 100, 40, 1051000); // Humility - builder.AddHtmlLocalized(80, 129, 100, 40, 1051001); // Sacrifice - builder.AddHtmlLocalized(80, 159, 100, 40, 1051002); // Compassion - builder.AddHtmlLocalized(80, 189, 100, 40, 1051003); // Spirituality - builder.AddHtmlLocalized(200, 100, 200, 40, 1051004); // Valor - builder.AddHtmlLocalized(200, 129, 200, 40, 1051005); // Honor - builder.AddHtmlLocalized(200, 159, 200, 40, 1051006); // Justice - builder.AddHtmlLocalized(200, 189, 200, 40, 1051007); // Honesty + AddButton(60, 100, 1210, 1210, 1); // Humility + AddButton(60, 129, 1210, 1210, 2); // Sacrifice + AddButton(60, 159, 1210, 1210, 3); // Compassion + AddButton(60, 189, 1210, 1210, 4); // Spirituality + AddButton(180, 100, 1210, 1210, 5); // Valor + AddButton(180, 129, 1210, 1210, 6); // Honor + AddButton(180, 159, 1210, 1210, 7); // Justice + AddButton(180, 189, 1210, 1210, 8); // Honesty - builder.AddHtmlLocalized(75, 224, 220, 60, 1052062); // Click on a blue gem to view your status in that virtue. - - builder.AddButton(60, 100, 1210, 1210, 1); // Humility - builder.AddButton(60, 129, 1210, 1210, 2); // Sacrifice - builder.AddButton(60, 159, 1210, 1210, 3); // Compassion - builder.AddButton(60, 189, 1210, 1210, 4); // Spirituality - builder.AddButton(180, 100, 1210, 1210, 5); // Valor - builder.AddButton(180, 129, 1210, 1210, 6); // Honor - builder.AddButton(180, 159, 1210, 1210, 7); // Justice - builder.AddButton(180, 189, 1210, 1210, 8); // Honesty - - builder.AddButton(280, 43, 4014, 4014, 9); + AddButton(280, 43, 4014, 4014, 9); } private static int GetVirtueDescription(VirtueName virtue) => @@ -81,7 +66,7 @@ public class VirtueStatusGump : StaticGump { if (info.ButtonID == 9) { - VirtueGump.RequestVirtueGump(_beholder, _beholder); + _beholder.SendGump(new VirtueGump(_beholder, _beholder)); return; } @@ -92,11 +77,11 @@ public class VirtueStatusGump : StaticGump var virtue = (VirtueName)(info.ButtonID - 1); - VirtueInfoGump.DisplayTo( + _beholder.SendGump(new VirtueInfoGump( _beholder, virtue, GetVirtueDescription(virtue), @$"https://uo.com/wiki/ultima-online-wiki/gameplay/the-virtues/#{VirtueSystem.GetLowerCaseName(virtue)}" - ); + )); } } diff --git a/Projects/UOContent/Engines/Virtues/VirtueSystem.cs b/Projects/UOContent/Engines/Virtues/VirtueSystem.cs index e57a2d386..b42020703 100644 --- a/Projects/UOContent/Engines/Virtues/VirtueSystem.cs +++ b/Projects/UOContent/Engines/Virtues/VirtueSystem.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; -using ModernUO.CodeGeneratedEvents; using Server.Collections; using Server.Logging; using Server.Mobiles; @@ -76,9 +75,6 @@ public class VirtueSystem : GenericPersistence } } - [OnEvent(nameof(PlayerMobile.PlayerDeletedEvent))] - public static void OnPlayerDeleted(PlayerMobile pm) => _playerVirtues.Remove(pm); - public override void Serialize(IGenericWriter writer) { writer.WriteEncodedInt(0); // version @@ -102,7 +98,7 @@ public class VirtueSystem : GenericPersistence var virtues = new VirtueContext(); virtues.Deserialize(reader); - if (player != null && virtues.IsUsed()) + if (virtues.IsUsed()) { _playerVirtues.Add(player, virtues); } @@ -119,7 +115,7 @@ public class VirtueSystem : GenericPersistence return null; } - ref var context = ref CollectionsMarshal.GetValueRefOrAddDefault(_playerVirtues, from, out var exists); + ref VirtueContext context = ref CollectionsMarshal.GetValueRefOrAddDefault(_playerVirtues, from, out bool exists); if (!exists) { context = new VirtueContext(); @@ -372,6 +368,8 @@ public class VirtueSystem : GenericPersistence return; } + using var queue = PooledRefQueue.Create(); + // This is not particularly efficient. If it gets too slow, then use a different architecture. foreach (var (player, virtues) in _playerVirtues) { @@ -379,9 +377,14 @@ public class VirtueSystem : GenericPersistence if (!virtues.IsUsed()) { - _playerVirtues.Remove(player); + queue.Enqueue(player); } } + + while (queue.Count > 0) + { + _playerVirtues.Remove((PlayerMobile)queue.Dequeue()); + } } ~VirtueTimer() diff --git a/Projects/UOContent/Gumps/AdminGump.cs b/Projects/UOContent/Gumps/AdminGump.cs index a4fbe0f18..816d1967b 100644 --- a/Projects/UOContent/Gumps/AdminGump.cs +++ b/Projects/UOContent/Gumps/AdminGump.cs @@ -5,14 +5,12 @@ using System.Net; using System.Runtime.InteropServices; using System.Threading; using Server.Accounting; -using Server.Accounting.Security; using Server.Collections; using Server.Commands; using Server.Maps; using Server.Misc; using Server.Multis; using Server.Network; -using Server.Network.Bans; using Server.Prompts; using Server.Saves; using Server.Text; @@ -61,7 +59,7 @@ namespace Server.Gumps private const int RedHue = 0x20; private static readonly string[] m_AccessLevelStrings = - [ + { "Player", "Counselor", "Game Master", @@ -69,7 +67,7 @@ namespace Server.Gumps "Administrator", "Developer", "Owner" - ]; + }; private readonly Mobile m_From; private readonly List m_List; @@ -94,12 +92,12 @@ namespace Server.Gumps AddPage(0); - AddBackground(0, 0, 420, 480, 5054); + AddBackground(0, 0, 420, 440, 5054); AddBlackAlpha(10, 10, 170, 100); AddBlackAlpha(190, 10, 220, 100); - AddBlackAlpha(10, 120, 400, 300); - AddBlackAlpha(10, 430, 400, 40); + AddBlackAlpha(10, 120, 400, 260); + AddBlackAlpha(10, 390, 400, 40); AddPageButton( 10, @@ -143,7 +141,7 @@ namespace Server.Gumps if (notice != null) { - AddHtml(20, 440, 396, 36, notice.Color(LabelColor32)); + AddHtml(12, 392, 396, 36, notice.Color(LabelColor32)); } switch (pageType) @@ -172,7 +170,7 @@ namespace Server.Gumps AddLabel(150, 150, LabelHue, banned.ToString()); AddLabel(20, 170, LabelHue, "Firewalled:"); - AddLabel(150, 170, LabelHue, Firewall.FirewallSetCount.ToString()); + AddLabel(150, 170, LabelHue, Firewall.FirewallSet.Count.ToString()); AddLabel(20, 190, LabelHue, "Clients:"); AddLabel(150, 190, LabelHue, NetState.Instances.Count.ToString()); @@ -227,12 +225,9 @@ namespace Server.Gumps } case AdminGumpPage.Information_Perf: { - var loopStatus = Core.IdleSleepUnsupported ? "Spinning - host cannot honor short waits" : - Core.EventLoopIdleWaitMs == 0 ? "Spinning (configured)" : - Core.IdleSleepSuspended ? "Sleep suspended - host returning waits late" : "Healthy"; - - AddLabel(20, 130, LabelHue, "Event Loop:"); - AddLabel(40, 150, LabelHue, loopStatus); + AddLabel(20, 130, LabelHue, "Cycles Per Second:"); + AddLabel(40, 150, LabelHue, $"Current: {Core.CyclesPerSecond:N2}"); + AddLabel(40, 170, LabelHue, $"Average: {Core.AverageCPS:N2}"); using var sb = ValueStringBuilder.Create(); @@ -624,14 +619,6 @@ namespace Server.Gumps AddButtonLabeled(20, y, GetButtonID(7, 12), "Kill"); AddButtonLabeled(200, y, GetButtonID(7, 13), "Resurrect"); - y += 20; - - AddButtonLabeled(20, y, GetButtonID(7, 15), "Jail"); - AddButtonLabeled(200, y, GetButtonID(7, 16), "Unjail"); - y += 25; - - AddLabel(20, y, LabelHue, "Jail Reason:"); - AddTextField(100, y, 300, 20, 1); break; } @@ -708,7 +695,7 @@ namespace Server.Gumps } case AdminGumpPage.Accounts: { - m_List ??= []; + m_List ??= new List(); var rads = state as List; @@ -1040,7 +1027,7 @@ namespace Server.Gumps for (int i = 0, index = listPage * 6; i < 6 && index >= 0 && index < m_List.Count; ++i, ++index) { - AddHtml(18, 243 + i * 22, 114, 20, Html.Color($"{m_List[index]}", LabelColor32)); + AddHtml(18, 243 + i * 22, 114, 20, m_List[index].ToString().Color(LabelColor32)); AddButton(130, 242 + i * 22, 0xFA2, 0xFA4, GetButtonID(8, index)); AddButton(160, 242 + i * 22, 0xFA8, 0xFAA, GetButtonID(9, index)); AddButton(190, 242 + i * 22, 0xFB1, 0xFB3, GetButtonID(10, index)); @@ -1124,7 +1111,12 @@ namespace Server.Gumps } var c = a.Comments[i]; - sb.Append($"[{c.AddedBy} on {c.LastModified}]
{c.Content}"); + sb.Append('['); + sb.Append(c.AddedBy); + sb.Append(" on "); + sb.Append(c.LastModified.ToString()); + sb.Append("]
"); + sb.Append(c.Content); } AddHtml(20, 180, 380, 190, sb.ToString(), true, true); @@ -1159,7 +1151,9 @@ namespace Server.Gumps var tag = a.Tags[i]; - sb.Append($"{tag.Name} = {tag.Value}"); + sb.Append(tag.Name); + sb.Append(" = "); + sb.Append(tag.Value); } AddHtml(20, 180, 380, 190, sb.ToString(), true, true); @@ -1171,16 +1165,7 @@ namespace Server.Gumps { AddFirewallHeader(); - if (m_List == null) - { - Firewall.ReadFirewallSet(firewallSet => - { - list = new List(firewallSet.Count); - list.AddRange(firewallSet); - }); - - m_List = list; - } + m_List ??= Firewall.FirewallSet.ToList(); AddLabelCropped(12, 120, 358, 20, LabelHue, "IP Address"); @@ -1193,7 +1178,7 @@ namespace Server.Gumps AddImage(375, 122, 0x25EA); } - if ((listPage + 1) * 12 < m_List!.Count) + if ((listPage + 1) * 12 < m_List.Count) { AddButton(392, 122, 0x15E1, 0x15E5, GetButtonID(1, 1)); } @@ -1235,7 +1220,7 @@ namespace Server.Gumps break; } - AddHtml(10, 125, 400, 20, Html.Center($"{firewallEntry}", LabelColor32)); + AddHtml(10, 125, 400, 20, firewallEntry.ToString().Center(LabelColor32)); AddButtonLabeled(20, 150, GetButtonID(6, 3), "Remove"); @@ -1328,7 +1313,7 @@ namespace Server.Gumps public void AddPageButton( int x, int y, int buttonID, string text, AdminGumpPage page, - params ReadOnlySpan subPages + params AdminGumpPage[] subPages ) { var isSelection = m_PageType == page; @@ -1417,7 +1402,7 @@ namespace Server.Gumps } default: { - if (m.Murderer) + if (m.Kills >= 5) { return 0x21; } @@ -1505,7 +1490,7 @@ namespace Server.Gumps // If we don't have a list, create one if (!acctExists) { - accts = []; + accts = new List(); } accts.Add(acct); @@ -1748,8 +1733,7 @@ namespace Server.Gumps { for (var i = 0; i < a.LoginIPs.Length; ++i) { - Firewall.Add(new SingleIpFirewallEntry(a.LoginIPs[i])); - BanChannel.Report(a.LoginIPs[i], TimeSpan.Zero, BanReasons.Manual); + AdminFirewall.Add(a.LoginIPs[i]); } notice = "All addresses in the list have been firewalled."; @@ -1773,13 +1757,7 @@ namespace Server.Gumps if (okay) { - var firewallEntry = Firewall.ToFirewallEntry(toFirewall); - Firewall.Add(firewallEntry); - - if (firewallEntry.MinIpAddress == firewallEntry.MaxIpAddress) - { - BanChannel.Report(firewallEntry.MinIpAddress.ToIpAddress(), TimeSpan.Zero, BanReasons.Manual); - } + AdminFirewall.Add(toFirewall); notice = $"{toFirewall} : Added to firewall."; } @@ -1841,7 +1819,7 @@ namespace Server.Gumps --AccountHandler.IPTable[ips[0]]; } - a.LoginIPs = []; + a.LoginIPs = Array.Empty(); notice = "All addresses in the list have been removed."; } @@ -1877,14 +1855,17 @@ namespace Server.Gumps { var acct = (Account)m_List[v]; - if (!info.IsSwitched(v)) + if (info.IsSwitched(v)) + { + if (!rads.Contains(acct)) + { + rads.Add(acct); + } + } + else if (rads.Contains(acct)) { rads.Remove(acct); } - else if (!rads.Contains(acct)) - { - rads.Add(acct); - } } } } @@ -2781,7 +2762,7 @@ namespace Server.Gumps } else { - results = []; + results = new List(); foreach (var acct in Accounts.GetAccounts()) { if (acct.Username.InsensitiveContains(match)) @@ -2907,7 +2888,7 @@ namespace Server.Gumps else { notice = "The password has been changed."; - PasswordWorker.SetPassword(a, password, null); + a.SetPassword(password); page = AdminGumpPage.AccountDetails_Information; CommandLogging.WriteLine( from, @@ -3489,22 +3470,19 @@ namespace Server.Gumps if (string.IsNullOrEmpty(match)) { - notice = "You must enter an IP to search."; + notice = "You must enter a username to search."; } else { - Firewall.ReadFirewallSet(firewallSet => + foreach (var check in Firewall.FirewallSet) { - foreach (var check in firewallSet) - { - var checkStr = check.ToString(); + var checkStr = check.ToString(); - if (checkStr.ContainsOrdinal(match)) - { - results.Add(check); - } + if (checkStr.ContainsOrdinal(match)) + { + results.Add(check); } - }); + } } if (results.Count == 1) @@ -3571,7 +3549,7 @@ namespace Server.Gumps IFirewallEntry firewallEntry; try { - firewallEntry = Firewall.ToFirewallEntry(text); + firewallEntry = AdminFirewall.ToFirewallEntry(text); } catch { @@ -3593,17 +3571,7 @@ namespace Server.Gumps $"{from.AccessLevel} {CommandLogging.Format(from)} firewalling {firewallEntry}" ); - Firewall.Add(firewallEntry); - - if (firewallEntry.MinIpAddress == firewallEntry.MaxIpAddress) - { - BanChannel.Report( - firewallEntry.MinIpAddress.ToIpAddress(), - TimeSpan.Zero, - BanReasons.Manual - ); - } - + AdminFirewall.Add(firewallEntry); from.SendGump( new AdminGump( from, @@ -3642,13 +3610,7 @@ namespace Server.Gumps $"{from.AccessLevel} {CommandLogging.Format(from)} removing {m_State} from firewall list" ); - Firewall.Remove(m_State as IFirewallEntry); - - if (m_State is IFirewallEntry fe && fe.MinIpAddress == fe.MaxIpAddress) - { - BanChannel.Retract(fe.MinIpAddress.ToIpAddress()); - } - + AdminFirewall.Remove(m_State); from.SendGump( new AdminGump( from, @@ -3827,33 +3789,6 @@ namespace Server.Gumps sendGump = false; break; } - case 15: - { - var reason = info.GetTextEntry(1)?.Trim(); - - if (string.IsNullOrWhiteSpace(reason)) - { - reason = ""; - } - - CommandLogging.WriteLine( - from, - $"{from.AccessLevel} {CommandLogging.Format(from)} jailing {CommandLogging.Format(m)} - Reason: {reason}" - ); - InvokeCommand($"Jail {m.Name} \"{reason}\""); - notice = $"Player has been sent to jail. Reason: {reason}"; - break; - } - case 16: - { - CommandLogging.WriteLine( - from, - $"{from.AccessLevel} {CommandLogging.Format(from)} unjailing {CommandLogging.Format(m)}" - ); - InvokeCommand($"Unjail {m.Name}"); - notice = "Player has been unjailed."; - break; - } } if (sendGump) @@ -4003,37 +3938,31 @@ namespace Server.Gumps if (Core.SA && availableMaps.Includes(MapSelectionFlags.TerMur)) { InvokeCommand("GenerateSpawners Data/Spawns/post-uoml/termur/**.json"); - InvokeCommand("GenerateSpawners Data/Spawns/shared/termur/**.json"); } if (availableMaps.Includes(MapSelectionFlags.Malas)) { InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/malas/**.json"); - InvokeCommand("GenerateSpawners Data/Spawns/shared/malas/**.json"); } if (availableMaps.Includes(MapSelectionFlags.Tokuno)) { InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/tokuno/**.json"); - InvokeCommand("GenerateSpawners Data/Spawns/shared/tokuno/**.json"); } if (availableMaps.Includes(MapSelectionFlags.Ilshenar)) { InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/ilshenar/**.json"); - InvokeCommand("GenerateSpawners Data/Spawns/shared/ilshenar/**.json"); } if (availableMaps.Includes(MapSelectionFlags.Trammel)) { InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/trammel/**.json"); - InvokeCommand("GenerateSpawners Data/Spawns/shared/trammel/**.json"); } if (availableMaps.Includes(MapSelectionFlags.Felucca)) { InvokeCommand($"GenerateSpawners Data/Spawns/{folder}/felucca/**.json"); - InvokeCommand("GenerateSpawners Data/Spawns/shared/felucca/**.json"); } } @@ -4119,15 +4048,15 @@ namespace Server.Gumps { if (x is not KeyValuePair> a) { - return 1; + return -1; } if (y is not KeyValuePair> b) { - return -1; + return 1; } - return b.Value.Count - a.Value.Count; + return a.Value.Count - b.Value.Count; } } diff --git a/Projects/UOContent/Gumps/BanDurationGump.cs b/Projects/UOContent/Gumps/BanDurationGump.cs index a23fd679d..dc2bb9e3b 100644 --- a/Projects/UOContent/Gumps/BanDurationGump.cs +++ b/Projects/UOContent/Gumps/BanDurationGump.cs @@ -9,7 +9,7 @@ namespace Server.Gumps { private readonly List m_List; - public BanDurationGump(Account a) : this([a]) + public BanDurationGump(Account a) : this(new List { a }) { } @@ -218,10 +218,7 @@ namespace Server.Gumps break; } - default: - { - return; - } + default: return; } if (shouldSet) diff --git a/Projects/UOContent/Gumps/Base/BaseGump.cs b/Projects/UOContent/Gumps/Base/BaseGump.cs index 05770608c..a3b393b7a 100644 --- a/Projects/UOContent/Gumps/Base/BaseGump.cs +++ b/Projects/UOContent/Gumps/Base/BaseGump.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BaseGump.cs * * * @@ -13,7 +13,6 @@ * along with this program. If not, see . * *************************************************************************/ -using Server.Logging; using Server.Network; using System; using System.Buffers; @@ -24,12 +23,10 @@ namespace Server.Gumps; public abstract class BaseGump { private static readonly byte[] _packetBuffer = GC.AllocateUninitializedArray(0x10000); - private static readonly ILogger _logger = LogFactory.GetLogger(typeof(BaseGump)); private static Serial nextSerial = (Serial)1; public int TypeID { get; protected set; } public Serial Serial { get; protected set; } - protected bool HasVisualElements { get; set; } public abstract int Switches { get; } public abstract int TextEntries { get; } @@ -59,11 +56,6 @@ public abstract class BaseGump var writer = new SpanWriter(_packetBuffer); Compile(ref writer); - if (!HasVisualElements) - { - _logger.Warning("Sending empty gump {GumpType}", GetType().FullName); - } - ns.Send(writer.Span); writer.Dispose(); @@ -94,37 +86,4 @@ public abstract class BaseGump return hash == 461 ? hash * primeMulti : hash; } } - - public static Point2D GetItemGraphicOffset(int itemId) - { - var (width, height) = ItemBounds.Sizes[itemId]; - var x = 0; - var y = 0; - - if (width > 44) - { - x -= (width - 44) / 2; - } - else if (width < 44) - { - x += (44 - width) / 2; - } - - if (height > 44) - { - y -= height - 44; - } - else if (height < 44) - { - y += 44 - height; - } - - return new Point2D(x, y); - } - - public static Point2D GetGumpOffsetForItemGraphic(int itemId, int relativeX, int relativeY) - { - var offset = GetItemGraphicOffset(itemId); - return new Point2D(relativeX * 22 - relativeY * 22 + offset.X, relativeX * 22 + relativeY * 22 + offset.Y); - } } diff --git a/Projects/UOContent/Gumps/Base/DynamicGump.cs b/Projects/UOContent/Gumps/Base/DynamicGump.cs index de894da68..34f580050 100644 --- a/Projects/UOContent/Gumps/Base/DynamicGump.cs +++ b/Projects/UOContent/Gumps/Base/DynamicGump.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: DynamicGump.cs * * * @@ -43,11 +43,10 @@ public abstract class DynamicGump : BaseGump writer.Write(X); writer.Write(Y); - var gumpBuilder = new DynamicGumpBuilder(); + DynamicGumpBuilder gumpBuilder = new DynamicGumpBuilder(); BuildLayout(ref gumpBuilder); gumpBuilder.FinalizeLayout(); - HasVisualElements = gumpBuilder.HasVisualElements; _switches = gumpBuilder.Switches; _textEntries = gumpBuilder.TextEntries; diff --git a/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs b/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs index 9238e6e04..afe345bb9 100644 --- a/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs +++ b/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: DynamicGumpBuilder.cs * * * @@ -36,7 +36,6 @@ public ref struct DynamicGumpBuilder public int Switches => _gumpBuilder._switches; public int TextEntries => _gumpBuilder._textEntries; - internal bool HasVisualElements => _gumpBuilder._hasVisualElements; [MethodImpl(MethodImplOptions.AggressiveInlining)] public DynamicGumpBuilder() @@ -79,33 +78,114 @@ public ref struct DynamicGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtml( - int x, int y, int width, int height, ReadOnlySpan text, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, - TextAlignment align = TextAlignment.Left, bool background = false, bool scrollbar = false + int x, + int y, + int width, + int height, + ReadOnlySpan text, + bool background = false, + bool scrollbar = false ) { - if (color.Length > 0 || size != -1 || fontStyle != 0 || align != TextAlignment.Left) - { - var arr = STArrayPool.Shared.Rent(Html.BuildCharCount(text, color)); - var bytesWritten = Html.Build(text, arr.AsSpan(), color, size, fontStyle, align); - - WriteInternalizedString(arr.AsSpan(0, bytesWritten)); - STArrayPool.Shared.Return(arr); - } - else - { - WriteInternalizedString(text); - } - + WriteInternalizedString(text); _gumpBuilder.AddHtml(x, y, width, height, _stringsCount++, background, scrollbar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtml( - int x, int y, int width, int height, ref RawInterpolatedStringHandler handler, ReadOnlySpan color = default, int size = -1, - byte fontStyle = 0, TextAlignment align = TextAlignment.Left, bool background = false, bool scrollbar = false + int x, int y, int width, int height, ref RawInterpolatedStringHandler handler, + bool background = false, bool scrollbar = false ) { - AddHtml(x, y, width, height, handler.Text, color, size, fontStyle, align, background, scrollbar); + AddHtml(x, y, width, height, handler.Text, background, scrollbar); + handler.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtml( + int x, + int y, + int width, + int height, + int color, + ReadOnlySpan text, + bool background = false, + bool scrollbar = false + ) + { + var handler = text.Color(color); + AddHtml(x, y, width, height, ref handler, background, scrollbar); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtml( + int x, + int y, + int width, + int height, + int color, + ref RawInterpolatedStringHandler handler, + bool background = false, + bool scrollbar = false + ) + { + AddHtml(x, y, width, height, color, handler.Text, background, scrollbar); + handler.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtmlCentered( + int x, int y, int width, int height, ReadOnlySpan text, bool background = false, bool scrollbar = false + ) + { + var handler = text.Center(); + AddHtml(x, y, width, height, ref handler, background, scrollbar); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtmlCentered( + int x, + int y, + int width, + int height, + ref RawInterpolatedStringHandler handler, + bool background = false, + bool scrollbar = false + ) + { + AddHtmlCentered(x, y, width, height, handler.Text, background, scrollbar); + handler.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtmlCentered( + int x, + int y, + int width, + int height, + int color, + ReadOnlySpan text, + bool background = false, + bool scrollbar = false + ) + { + var handler = text.Center(color); + AddHtml(x, y, width, height, ref handler, background, scrollbar); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtmlCentered( + int x, + int y, + int width, + int height, + int color, + ref RawInterpolatedStringHandler handler, + bool background = false, + bool scrollbar = false + ) + { + AddHtmlCentered(x, y, width, height, color, handler.Text, background, scrollbar); handler.Clear(); } @@ -121,7 +201,14 @@ public ref struct DynamicGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtmlLocalized( - int x, int y, int width, int height, int number, ReadOnlySpan args, int color, bool background = false, + int x, + int y, + int width, + int height, + int number, + ReadOnlySpan args, + int color, + bool background = false, bool scrollbar = false ) => _gumpBuilder.AddHtmlLocalized(x, y, width, height, number, args, color, background, scrollbar); @@ -145,8 +232,18 @@ public ref struct DynamicGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddImageTiledButton( - int x, int y, int normalId, int pressedId, int buttonId, GumpButtonType type, int param, int itemId, int hue, - int width, int height, int localizedTooltip = -1 + int x, + int y, + int normalId, + int pressedId, + int buttonId, + GumpButtonType type, + int param, + int itemId, + int hue, + int width, + int height, + int localizedTooltip = -1 ) => _gumpBuilder.AddImageTiledButton( x, y, normalId, pressedId, buttonId, type, param, itemId, hue, width, height, localizedTooltip ); @@ -279,11 +376,11 @@ public ref struct DynamicGumpBuilder } var newSize = Math.Max(_stringBytesWritten + needed, _stringsBuffer.Length * 2); - var poolArray = STArrayPool.Shared.Rent(newSize); + byte[] poolArray = STArrayPool.Shared.Rent(newSize); _stringsBuffer.AsSpan(0, _stringBytesWritten).CopyTo(poolArray); - var toReturn = _stringsBuffer; + byte[] toReturn = _stringsBuffer; _stringsBuffer = poolArray; if (toReturn != null && toReturn != _staticStringsBuffer) diff --git a/Projects/UOContent/Gumps/Base/Grid/Grid.cs b/Projects/UOContent/Gumps/Base/Grid/Grid.cs new file mode 100644 index 000000000..7dd7a5372 --- /dev/null +++ b/Projects/UOContent/Gumps/Base/Grid/Grid.cs @@ -0,0 +1,77 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Grid.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System.Collections.Generic; + +namespace Server.Gumps; + +public enum GridHues +{ + White = 2049, // Change to 0x480 if you have old hues.mul + Black = 0, +} + +public static class GridColors +{ + public const string Gold = "#ffc959"; + public const string White = "#ffffff"; + public const string Blue = "#1D63DC"; + public const string LightBlue = "#7799EE"; + public const string Green = "#80E732"; + public const string Orange = "#F28B00"; + public const string Purple = "#F3ACF6"; + public const string Red = "#B52B2D"; + public const string LightGreen = "#E6FFC0"; + public const string Yellow = "#FFFFBB"; +} + +public class ListItem +{ + public int X { get; set; } + public int Y { get; set; } + public int Index { get; set; } + public Col[] Cols { get; set; } +} + +public class Col +{ + public int X { get; set; } + public int Width { get; set; } + public int HCenter => X + Width / 2; + public int HEnd => X + Width; +} + +public class Row +{ + public int Y { get; set; } + public int Height { get; set; } + public int VCenter => Y + Height / 2; + public int VEnd => Y + Height; +} + +public class Grid +{ + public string Name { get; set; } + public int Height { get; set; } + public int Width { get; set; } + public List Columns { get; set; } = new(); + public List Rows { get; set; } = new(); +} + +public class Swap +{ + public int Index { get; set; } + public int Size { get; set; } +} diff --git a/Projects/UOContent/Gumps/Base/Grid/GumpGrid.cs b/Projects/UOContent/Gumps/Base/Grid/GumpGrid.cs new file mode 100644 index 000000000..6b5e80b05 --- /dev/null +++ b/Projects/UOContent/Gumps/Base/Grid/GumpGrid.cs @@ -0,0 +1,525 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: GumpGrid.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using Server.Collections; + +namespace Server.Gumps; + +public class GumpGrid : Gump +{ + private Dictionary _grids = new(); + + public GumpGrid(int x, int y) : base(x, y) + { + } + + private void AddColumn(string name, int x, int width) + { + _grids[name].Columns.Add(new Col { X = x, Width = width, }); + } + + private void AddRow(string name, int y, int height) + { + _grids[name].Rows.Add(new Row { Y = y, Height = height }); + } + + private void InitColumn(string name, int column, int w, int x) + { + var width = w / column; + for (int i = 0; i < column; i++) + { + AddColumn(name, i * width + x, width); + } + } + + private void InitRow(string name, int row, int h, int y) + { + var height = h / row; + for (int i = 0; i < row; i++) + { + AddRow(name, i * height + y, height); + } + } + + private static Swap Exist(ref PooledRefList list, int index) + { + for (int i = 0; i < list.Count; i++) + { + var item = list[i]; + if (item.Index == index) + { + return item; + } + } + + return null; + } + + private static int CalculateCustom(int column, int len, string[] sizes, ref PooledRefList list) + { + var cropSize = 0; + + for (int i = 0; i < column; i++) + { + if (sizes[i] == "*") + { + continue; + } + + //percent + var percent = sizes[i].RemoveOrdinal("*"); + var size = sizes[i] != percent ? len / 100 * int.Parse(percent) : int.Parse(percent); + + list.Add(new Swap { Index = i, Size = size }); + cropSize += size; + } + + return cropSize; + } + private void InitCustomSizeRow(string name, int height, int row, string[] sizes, int y) + { + var list = PooledRefList.Create(); + var cropHeight = CalculateCustom(row, height, sizes, ref list); + var Height = height - cropHeight; + var factor = row - list.Count; + for (int i = 0; i < row; i++) + { + var swap = Exist(ref list, i); + if (swap != null) + { + var size = swap.Size; + if (i == 0) + { + AddRow(name, y, size); + } + else + { + var col = _grids[name].Rows[i - 1]; + AddRow(name, col.Height + col.Y + y, size); + } + } + else + { + var size = Height / factor; + if (i == 0) + { + AddRow(name, y, size); + } + else + { + var col = _grids[name].Rows[i - 1]; + AddRow(name, col.Height + col.Y + y, size); + } + } + } + + list.Dispose(); + } + + private void InitCustomSizeColumn(string name, int width, int column, string[] sizes, int x) + { + var list = PooledRefList.Create(); + var cropWidth = CalculateCustom(column, width, sizes, ref list); + var Width = width - cropWidth; + var factor = column - list.Count; + + for (int i = 0; i < column; i++) + { + var swap = Exist(ref list, i); + if (swap != null) + { + var size = swap.Size; + if (i == 0) + { + AddColumn(name, x, size); + } + else + { + var col = _grids[name].Columns[i - 1]; + AddColumn(name, col.Width + col.X + x, size); + } + } + else + { + var size = Width / factor; + if (i == 0) + { + AddColumn(name, x, size); + } + else + { + var col = _grids[name].Columns[i - 1]; + AddColumn(name, col.Width + col.X + x, size); + } + } + } + } + + public Grid SubGrid( + string name, + string destGridName, + int columnStart, + int rowStart, + int createColumnsCount, + int createRowsCount, + int columnSpan = 0, + int rowSpan = 0, + string createColumnSize = "", + string createRowSize = "", + int marginX = 0, + int marginY = 0 + ) + { + if (CalculateCord( + destGridName, + columnStart, + rowStart, + columnSpan, + rowSpan, + out var x, + out var y, + out var width, + out var height + )) + { + Grid( + name, + width, + height, + createColumnsCount, + createRowsCount, + createColumnSize, + createRowSize, + x + marginX, + y + marginY + ); + } + + return CreateGrid(name, 0, 0); + } + + public Grid CreateGrid(string name, int width, int height) + { + if (!_grids.TryGetValue(name, out var grid)) + { + grid = new Grid { Name = name, Width = width, Height = height }; + _grids.Add(name, grid); + } + + return grid; + } + + public Grid Grid( + string name, + int width, + int height, + int columns, + int rows, + string columnSize = "", + string rowSize = "", + int x = 0, + int y = 0 + ) + { + if (columns < 1) + { + throw new Exception(nameof(columns)); + } + + if (rows < 1) + { + throw new Exception(nameof(columns)); + } + + var Grid = CreateGrid(name, width, height); + + if (columnSize.Length > 0) + { + var buffer = columnSize.Split(' '); + if (buffer.Length == columns) + { + InitCustomSizeColumn(name, width, columns, buffer, x); + } + else + { + throw new Exception(nameof(columnSize)); + } + } + else + { + InitColumn(name, columns, width, x); + } + + if (rowSize.Length > 0) + { + var buffer = rowSize.Split(' '); + if (buffer.Length == rows) + { + InitCustomSizeRow(name, height, rows, buffer, y); + } + else + { + throw new Exception(nameof(rowSize)); + } + } + else + { + InitRow(name, rows, height, y); + } + + return Grid; + } + + public bool CalculateCord( + string name, + int column, + int row, + int columnSpan, + int rowSpan, + out int x, + out int y, + out int width, + out int height + ) + { + x = 0; y = 0; width = 0; height = 0; + if (_grids.TryGetValue(name, out var grid)) + { + var count = columnSpan == 0 ? column + 1 : row + columnSpan; + for (int i = column; i < count; i++) + { + if (i >= grid.Columns.Count) + { + break; + } + + var dcol = grid.Columns[i]; + if (i == column) + { + x = dcol.X; + } + + width += dcol.Width; + } + + count = rowSpan == 0 ? row + 1 : row + rowSpan; + for (int i = row; i < count; i++) + { + if (i >= grid.Rows.Count) + { + break; + } + + var drow = grid.Rows[i]; + if (i == row) + { + y = drow.Y; + } + + height += drow.Height; + } + + if (width == 0) + { + width = grid.Width; + } + + if (height == 0) + { + height = grid.Height; + } + + return true; + + } + + return false; + } + + public class ListView + { + public int LineCount { get; } + public int ItemsCount { get; } + public ListItem[] Items { get; } + public ListItem Header { get; } + public int Page { get; } + public int TotalPages { get; } + public int ColHeight { get; } + public bool CanNext { get; } + public bool CanBack { get; } + public int ColsCount { get; } + + public ListView( + int itemsCount, + int columns, + int heightPerItem, + int page, + int x, + int y, + int height, + int pageItemCount, + int[] ColWidth, + int marginX = 0, + int marginY = 0, + int headerHeight = 0 + ) + { + ItemsCount = itemsCount; + var itemsPerPageCount = pageItemCount == 0 ? (height - marginY) / heightPerItem : pageItemCount; + LineCount = itemsPerPageCount; + + if (itemsCount > 0) + { + TotalPages = itemsCount / itemsPerPageCount; + if (itemsCount % itemsPerPageCount == 0) + { + TotalPages--; + } + } + + Page = page > TotalPages ? TotalPages : page; + ColsCount = columns; + ColHeight = heightPerItem; + + var index = Page * itemsPerPageCount > 0 ? Page * itemsPerPageCount : 0; + var nowPageItems = index + itemsPerPageCount; + + if (index > 0) + { + CanBack = true; + } + + if (nowPageItems < itemsCount) + { + CanNext = true; + } + + if (nowPageItems > itemsCount) + { + itemsPerPageCount = Page > 0 ? itemsCount - Page * itemsPerPageCount : itemsCount; + } + + //header + Header = new ListItem + { + X = x, + Y = y + marginY, + Index = -1, + Cols = new Col[columns], + }; + + var length = x + marginX; + for (int col = 0; col < columns; col++) + { + Header.Cols[col] = new Col(); + Header.Cols[col].Width = ColWidth[col]; + Header.Cols[col].X = length; + length += ColWidth[col]; + } + + Items = new ListItem[itemsPerPageCount]; + + for (int i = 0; i < itemsPerPageCount; i++) + { + Items[i] = new ListItem + { + X = x, + Y = y + i * heightPerItem + marginY + headerHeight, + Index = index + i, + Cols = Header.Cols + }; + } + } + } + + public ListView AddListView( + string name, + int column, + int row, + int itemsCount, + int page, + int heightPerItem, + int createColumn, + int columnSpan = 0, + int rowSpan = 0, + int width = 0, + int height = 0, + int pageItemCount = 0, + int marginX = 0, + int marginY = 0, + int headerHeight = 0, + string colSize = "" + ) + { + if (CalculateCord(name, column, row, columnSpan, rowSpan, out var x, out var y, out var w, out var h)) + { + w = width > 0 ? width : w; + h = height > 0 ? height : h; + + int[] colWidth = new int[createColumn]; + + if (colSize == string.Empty) + { + for (int i = 0; i < createColumn; i++) + { + colWidth[i] = w / createColumn; + } + } + else + { + var buffer = colSize.Split(' '); + + if (buffer.Length > 0) + { + const string listName = "listView"; + _grids.Add(listName, new Grid()); + + if (createColumn < buffer.Length) + { + Array.Resize(ref buffer, createColumn); + } + + InitCustomSizeColumn(listName, w, createColumn, buffer, x); + var cols = _grids[listName].Columns; + for (var i = 0; i < createColumn; i++) + { + colWidth[i] = cols[i].Width; + } + + _grids.Remove(listName); + } + } + + return new ListView( + itemsCount, + createColumn, + heightPerItem, + page, + x, + y, + h, + pageItemCount, + colWidth, + marginX, + marginY, + headerHeight + ); + } + + return null; + } +} diff --git a/Projects/UOContent/Gumps/Base/GridLayout/GridBuilderExtensions.cs b/Projects/UOContent/Gumps/Base/GridLayout/GridBuilderExtensions.cs deleted file mode 100644 index e4c822ae0..000000000 --- a/Projects/UOContent/Gumps/Base/GridLayout/GridBuilderExtensions.cs +++ /dev/null @@ -1,512 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: GridBuilderExtensions.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using Server.Buffers; - -namespace Server.Gumps; - -/// -/// Extension methods for DynamicGumpBuilder that accept GridCell for positioning. -/// Provides ergonomic grid-based gump building with zero allocations. -/// -public static class GridBuilderExtensions -{ - extension(ref DynamicGumpBuilder builder) - { - /// - /// Adds a background element filling the entire cell. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddBackground(in GridCell cell, int gumpId) => - builder.AddBackground(cell.X, cell.Y, cell.Width, cell.Height, gumpId); - - /// - /// Adds an alpha region filling the entire cell. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddAlphaRegion(in GridCell cell) => - builder.AddAlphaRegion(cell.X, cell.Y, cell.Width, cell.Height); - - /// - /// Adds a tiled image filling the entire cell. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddImageTiled(in GridCell cell, int gumpId) => - builder.AddImageTiled(cell.X, cell.Y, cell.Width, cell.Height, gumpId); - - /// - /// Adds an image at the cell position with optional offset. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddImage( - in GridCell cell, - int gumpId, - int hue = 0, - int offsetX = 0, - int offsetY = 0 - ) => builder.AddImage(cell.X + offsetX, cell.Y + offsetY, gumpId, hue); - - /// - /// Adds an item display at the cell position with optional offset. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddItem( - in GridCell cell, - int itemId, - int hue = 0, - int offsetX = 0, - int offsetY = 0 - ) => builder.AddItem(cell.X + offsetX, cell.Y + offsetY, itemId, hue); - - /// - /// Adds a label at the cell position with optional offset. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddLabel( - in GridCell cell, - int hue, - ReadOnlySpan text, - int offsetX = 0, - int offsetY = 0 - ) => builder.AddLabel(cell.X + offsetX, cell.Y + offsetY, hue, text); - - /// - /// Adds a label at the cell position with optional offset using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddLabel( - in GridCell cell, - int hue, - ref RawInterpolatedStringHandler handler, - int offsetX = 0, - int offsetY = 0 - ) - { - builder.AddLabel(cell.X + offsetX, cell.Y + offsetY, hue, handler.Text); - handler.Clear(); - } - - /// - /// Adds a cropped label filling the cell with optional offset. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddLabelCropped( - in GridCell cell, - int hue, - ReadOnlySpan text, - int offsetX = 0, - int offsetY = 0 - ) => builder.AddLabelCropped( - cell.X + offsetX, - cell.Y + offsetY, - cell.Width - offsetX, - cell.Height - offsetY, - hue, - text - ); - - /// - /// Adds a cropped label filling the cell with optional offset using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddLabelCropped( - in GridCell cell, - int hue, - ref RawInterpolatedStringHandler handler, - int offsetX = 0, - int offsetY = 0 - ) - { - builder.AddLabelCropped( - cell.X + offsetX, - cell.Y + offsetY, - cell.Width - offsetX, - cell.Height - offsetY, - hue, - handler.Text - ); - handler.Clear(); - } - - /// - /// Adds HTML text filling the entire cell. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtml( - in GridCell cell, - ReadOnlySpan text, - ReadOnlySpan color = default, - int size = -1, - byte fontStyle = 0, - TextAlignment align = TextAlignment.Left, - bool background = false, - bool scrollbar = false - ) => builder.AddHtml( - cell.X, - cell.Y, - cell.Width, - cell.Height, - text, - color, - size, - fontStyle, - align, - background, - scrollbar - ); - - /// - /// Adds HTML text filling the entire cell using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtml( - in GridCell cell, - ref RawInterpolatedStringHandler handler, - ReadOnlySpan color = default, - int size = -1, - byte fontStyle = 0, - TextAlignment align = TextAlignment.Left, - bool background = false, - bool scrollbar = false - ) - { - builder.AddHtml( - cell.X, - cell.Y, - cell.Width, - cell.Height, - handler.Text, - color, - size, - fontStyle, - align, - background, - scrollbar - ); - handler.Clear(); - } - - /// - /// Adds HTML text filling the cell with offset. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtml( - in GridCell cell, - ReadOnlySpan text, - int offsetX, - int offsetY, - ReadOnlySpan color = default, - int size = -1, - byte fontStyle = 0, - TextAlignment align = TextAlignment.Left, - bool background = false, - bool scrollbar = false - ) => builder.AddHtml( - cell.X + offsetX, - cell.Y + offsetY, - cell.Width - offsetX, - cell.Height - offsetY, - text, - color, - size, - fontStyle, - align, - background, - scrollbar - ); - - /// - /// Adds HTML text filling the cell with offset using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtml( - in GridCell cell, - ref RawInterpolatedStringHandler handler, - int offsetX, - int offsetY, - ReadOnlySpan color = default, - int size = -1, - byte fontStyle = 0, - TextAlignment align = TextAlignment.Left, - bool background = false, - bool scrollbar = false - ) - { - builder.AddHtml( - cell.X + offsetX, - cell.Y + offsetY, - cell.Width - offsetX, - cell.Height - offsetY, - handler.Text, - color, - size, - fontStyle, - align, - background, - scrollbar - ); - handler.Clear(); - } - - /// - /// Adds a localized HTML element filling the cell. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlLocalized( - in GridCell cell, - int number, - bool background = false, - bool scrollbar = false - ) => builder.AddHtmlLocalized(cell.X, cell.Y, cell.Width, cell.Height, number, background, scrollbar); - - /// - /// Adds a localized HTML element with color. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlLocalized( - in GridCell cell, - int number, - int color, - bool background = false, - bool scrollbar = false - ) => builder.AddHtmlLocalized(cell.X, cell.Y, cell.Width, cell.Height, number, color, background, scrollbar); - - /// - /// Adds a localized HTML element with arguments. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlLocalized( - in GridCell cell, - int number, - ReadOnlySpan args, - int color, - bool background = false, - bool scrollbar = false - ) => builder.AddHtmlLocalized(cell.X, cell.Y, cell.Width, cell.Height, number, args, color, background, scrollbar); - - /// - /// Adds a localized HTML element with arguments using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlLocalized( - in GridCell cell, - int number, - ref RawInterpolatedStringHandler handler, - int color, - bool background = false, - bool scrollbar = false - ) => builder.AddHtmlLocalized(cell.X, cell.Y, cell.Width, cell.Height, number, ref handler, color, background, scrollbar); - - /// - /// Adds a button at the cell position with optional offset. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddButton( - in GridCell cell, - int normalId, - int pressedId, - int buttonId, - GumpButtonType type = GumpButtonType.Reply, - int param = 0, - int offsetX = 0, - int offsetY = 0 - ) => builder.AddButton(cell.X + offsetX, cell.Y + offsetY, normalId, pressedId, buttonId, type, param); - - /// - /// Adds a checkbox at the cell position with optional offset. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddCheckbox( - in GridCell cell, - int inactiveId, - int activeId, - bool selected, - int switchId, - int offsetX = 0, - int offsetY = 0 - ) => builder.AddCheckbox(cell.X + offsetX, cell.Y + offsetY, inactiveId, activeId, selected, switchId); - - /// - /// Adds a radio button at the cell position with optional offset. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddRadio( - in GridCell cell, - int inactiveId, - int activeId, - bool selected, - int switchId, - int offsetX = 0, - int offsetY = 0 - ) => builder.AddRadio(cell.X + offsetX, cell.Y + offsetY, inactiveId, activeId, selected, switchId); - - /// - /// Adds a text entry filling the cell. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddTextEntry( - in GridCell cell, - int hue, - int entryId, - ReadOnlySpan initialText = default - ) => builder.AddTextEntry(cell.X, cell.Y, cell.Width, cell.Height, hue, entryId, initialText); - - /// - /// Adds a text entry filling the cell using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddTextEntry( - in GridCell cell, - int hue, - int entryId, - ref RawInterpolatedStringHandler handler - ) - { - builder.AddTextEntry(cell.X, cell.Y, cell.Width, cell.Height, hue, entryId, handler.Text); - handler.Clear(); - } - - /// - /// Adds a text entry with offset. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddTextEntry( - in GridCell cell, - int hue, - int entryId, - int offsetX, - int offsetY, - ReadOnlySpan initialText = default - ) => builder.AddTextEntry( - cell.X + offsetX, - cell.Y + offsetY, - cell.Width - offsetX, - cell.Height - offsetY, - hue, - entryId, - initialText - ); - - /// - /// Adds a text entry with offset using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddTextEntry( - in GridCell cell, - int hue, - int entryId, - int offsetX, - int offsetY, - ref RawInterpolatedStringHandler handler - ) - { - builder.AddTextEntry( - cell.X + offsetX, - cell.Y + offsetY, - cell.Width - offsetX, - cell.Height - offsetY, - hue, - entryId, - handler.Text - ); - handler.Clear(); - } - - /// - /// Adds a limited text entry filling the cell. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddTextEntryLimited( - in GridCell cell, - int hue, - int entryId, - int size, - ReadOnlySpan initialText = default - ) => builder.AddTextEntryLimited(cell.X, cell.Y, cell.Width, cell.Height, hue, entryId, initialText, size); - - /// - /// Adds a limited text entry filling the cell using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddTextEntryLimited( - in GridCell cell, - int hue, - int entryId, - int size, - ref RawInterpolatedStringHandler handler - ) - { - builder.AddTextEntryLimited(cell.X, cell.Y, cell.Width, cell.Height, hue, entryId, handler.Text, size); - handler.Clear(); - } - - /// - /// Adds an image-tiled button at the cell position. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddImageTiledButton( - in GridCell cell, - int normalId, - int pressedId, - int buttonId, - GumpButtonType type, - int param, - int itemId, - int hue, - int localizedTooltip = -1 - ) => builder.AddImageTiledButton( - cell.X, - cell.Y, - normalId, - pressedId, - buttonId, - type, - param, - itemId, - hue, - cell.Width, - cell.Height, - localizedTooltip - ); - - /// - /// Adds a sprite image using the cell dimensions. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddSpriteImage( - in GridCell cell, - int gumpId, - int sx, - int sy - ) => builder.AddSpriteImage(cell.X, cell.Y, gumpId, cell.Width, cell.Height, sx, sy); - - /// - /// Adds a sprite image at the cell position with optional offset. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddSpriteImage( - in GridCell cell, - int gumpId, - int width, - int height, - int sx, - int sy, - int offsetX = 0, - int offsetY = 0 - ) => builder.AddSpriteImage(cell.X + offsetX, cell.Y + offsetY, gumpId, width, height, sx, sy); - } -} diff --git a/Projects/UOContent/Gumps/Base/GridLayout/GridCalculator.cs b/Projects/UOContent/Gumps/Base/GridLayout/GridCalculator.cs deleted file mode 100644 index 18a790a73..000000000 --- a/Projects/UOContent/Gumps/Base/GridLayout/GridCalculator.cs +++ /dev/null @@ -1,242 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: GridCalculator.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; - -namespace Server.Gumps; - -/// -/// Static helper for calculating grid cell positions without heap allocations. -/// All results are written to caller-provided spans using stackalloc. -/// -public static class GridCalculator -{ - /// - /// Maximum supported columns or rows per grid. - /// - public const int MaxTracks = 32; - - /// - /// Computes track sizes from size specifications. - /// Writes positions and sizes to the provided spans. - /// - /// The size specifications for each track. - /// The total available size (width or height). - /// The starting position (x or y offset). - /// Output: position of each track. - /// Output: size of each track. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ComputeTrackSizes( - ReadOnlySpan specs, - int totalSize, - int origin, - Span positions, - Span sizes - ) - { - var trackCount = specs.Length; - - // First pass: calculate absolute/percent sizes and count star tracks - var remainingSize = totalSize; - var starCount = 0; - - for (var i = 0; i < trackCount; i++) - { - var spec = specs[i]; - switch (spec.Type) - { - case GridSizeType.Absolute: - { - sizes[i] = spec.Value; - remainingSize -= spec.Value; - break; - } - case GridSizeType.Percent: - { - sizes[i] = totalSize * spec.Value / 100; - remainingSize -= sizes[i]; - break; - } - case GridSizeType.Star: - { - starCount++; - sizes[i] = -1; // Mark as star for second pass - break; - } - } - } - - // Second pass: distribute remaining space to star tracks - if (starCount > 0 && remainingSize > 0) - { - var starSize = remainingSize / starCount; - for (var i = 0; i < trackCount; i++) - { - if (sizes[i] == -1) - { - sizes[i] = starSize; - } - } - } - else - { - // No star tracks or no remaining space - set any marked stars to 0 - for (var i = 0; i < trackCount; i++) - { - if (sizes[i] < 0) - { - sizes[i] = 0; - } - } - } - - // Third pass: calculate positions - var pos = origin; - for (var i = 0; i < trackCount; i++) - { - positions[i] = pos; - pos += sizes[i]; - } - } - - /// - /// Computes uniform track sizes (equal-sized cells). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ComputeUniformTrackSizes( - int trackCount, - int totalSize, - int origin, - Span positions, - Span sizes - ) - { - var trackSize = totalSize / trackCount; - var pos = origin; - - for (var i = 0; i < trackCount; i++) - { - positions[i] = pos; - sizes[i] = trackSize; - pos += trackSize; - } - } - - /// - /// Parses a size specification string and computes track sizes. - /// - /// Space-separated size specification (e.g., "10* * 100"). - /// The total available size. - /// The starting position. - /// Output: position of each track. - /// Output: size of each track. - /// The number of tracks computed. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int ComputeFromSpec( - ReadOnlySpan sizeSpec, - int totalSize, - int origin, - Span positions, - Span sizes - ) => ComputeFromSpec(sizeSpec, totalSize, origin, 0, positions, sizes); - - /// - /// Parses a size specification string and computes track sizes with gaps between tracks. - /// - /// Space-separated size specification (e.g., "10* * 100"). - /// The total available size. - /// The starting position. - /// The gap size between tracks. - /// Output: position of each track. - /// Output: size of each track. - /// The number of tracks computed. - public static int ComputeFromSpec( - ReadOnlySpan sizeSpec, - int totalSize, - int origin, - int gap, - Span positions, - Span sizes) - { - Span specs = stackalloc GridSizeSpec[MaxTracks]; - var trackCount = GridSizeSpec.ParseAll(sizeSpec, specs); - - if (trackCount > 0) - { - // Subtract total gap space from available size before computing track sizes - var totalGapSpace = gap * (trackCount - 1); - var availableSize = totalSize - totalGapSpace; - - ComputeTrackSizes(specs[..trackCount], availableSize, origin, positions, sizes); - - // Adjust positions to account for gaps - if (gap > 0) - { - for (var i = 1; i < trackCount; i++) - { - positions[i] += gap * i; - } - } - } - - return trackCount; - } - - /// - /// Gets a cell at the specified column and row from pre-computed grid arrays. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static GridCell GetCell( - ReadOnlySpan columnPositions, - ReadOnlySpan columnWidths, - ReadOnlySpan rowPositions, - ReadOnlySpan rowHeights, - int column, - int row - ) => new(columnPositions[column], rowPositions[row], columnWidths[column], rowHeights[row]); - - /// - /// Gets a cell spanning multiple columns and/or rows from pre-computed grid arrays. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static GridCell GetCell( - ReadOnlySpan columnPositions, - ReadOnlySpan columnWidths, - ReadOnlySpan rowPositions, - ReadOnlySpan rowHeights, - int column, - int row, - int columnSpan, - int rowSpan - ) - { - var width = 0; - var endCol = Math.Min(column + columnSpan, columnWidths.Length); - for (var c = column; c < endCol; c++) - { - width += columnWidths[c]; - } - - var height = 0; - var endRow = Math.Min(row + rowSpan, rowHeights.Length); - for (var r = row; r < endRow; r++) - { - height += rowHeights[r]; - } - - return new GridCell(columnPositions[column], rowPositions[row], width, height); - } -} diff --git a/Projects/UOContent/Gumps/Base/GridLayout/GridCell.cs b/Projects/UOContent/Gumps/Base/GridLayout/GridCell.cs deleted file mode 100644 index 9f7969d44..000000000 --- a/Projects/UOContent/Gumps/Base/GridLayout/GridCell.cs +++ /dev/null @@ -1,114 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: GridCell.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Runtime.CompilerServices; - -namespace Server.Gumps; - -/// -/// Represents a pre-calculated cell position within a grid layout. -/// This is a value type with zero heap allocations. -/// -public ref struct GridCell -{ - public readonly int X; - public readonly int Y; - public readonly int Width; - public readonly int Height; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GridCell(int x, int y, int width, int height) - { - X = x; - Y = y; - Width = width; - Height = height; - } - - /// - /// Gets the right edge X coordinate (X + Width). - /// - public int Right - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => X + Width; - } - - /// - /// Gets the bottom edge Y coordinate (Y + Height). - /// - public int Bottom - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Y + Height; - } - - /// - /// Gets the horizontal center X coordinate. - /// - public int CenterX - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => X + Width / 2; - } - - /// - /// Gets the vertical center Y coordinate. - /// - public int CenterY - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Y + Height / 2; - } - - /// - /// Creates a new cell with uniform margin inset on all sides. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GridCell Inset(int margin) => - new(X + margin, Y + margin, Width - margin * 2, Height - margin * 2); - - /// - /// Creates a new cell with separate horizontal and vertical margins. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GridCell Inset(int horizontal, int vertical) => - new(X + horizontal, Y + vertical, Width - horizontal * 2, Height - vertical * 2); - - /// - /// Creates a new cell with individual margins for each side. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GridCell Inset(int left, int top, int right, int bottom) => - new(X + left, Y + top, Width - left - right, Height - top - bottom); - - /// - /// Creates a new cell with an offset applied to the position. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GridCell Offset(int offsetX, int offsetY) => new(X + offsetX, Y + offsetY, Width, Height); - - /// - /// Creates a new cell with a different width. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GridCell WithWidth(int width) => new(X, Y, width, Height); - - /// - /// Creates a new cell with a different height. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GridCell WithHeight(int height) => new(X, Y, Width, height); -} diff --git a/Projects/UOContent/Gumps/Base/GridLayout/GridEntryExtensions.cs b/Projects/UOContent/Gumps/Base/GridLayout/GridEntryExtensions.cs deleted file mode 100644 index 268336e85..000000000 --- a/Projects/UOContent/Gumps/Base/GridLayout/GridEntryExtensions.cs +++ /dev/null @@ -1,222 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: GridEntryExtensions.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using Server.Buffers; - -namespace Server.Gumps; - -/// -/// Extension methods for DynamicGumpBuilder that provide BaseGridGump-style entry methods. -/// These combine a tiled background with content, matching the AddEntry* pattern. -/// -public static class GridEntryExtensions -{ - extension(ref DynamicGumpBuilder builder) - { - /// - /// Adds an entry with label (EntryGumpID background + cropped label). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryLabel( - in GridCell cell, - in GridEntryStyle style, - ReadOnlySpan text - ) - { - builder.AddImageTiled(cell, style.EntryGumpID); - builder.AddLabelCropped(cell, style.TextHue, text, style.TextOffsetX); - } - - /// - /// Adds an entry with label using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryLabel( - in GridCell cell, - in GridEntryStyle style, - ref RawInterpolatedStringHandler handler - ) - { - builder.AddImageTiled(cell, style.EntryGumpID); - builder.AddLabelCropped(cell, style.TextHue, ref handler, style.TextOffsetX); - } - - /// - /// Adds an entry with HTML (EntryGumpID background + HTML text). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryHtml( - in GridCell cell, - in GridEntryStyle style, - ReadOnlySpan text - ) - { - builder.AddImageTiled(cell, style.EntryGumpID); - builder.AddHtml(cell, text, style.TextOffsetX, 0); - } - - /// - /// Adds an entry with HTML using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryHtml( - in GridCell cell, - in GridEntryStyle style, - ref RawInterpolatedStringHandler handler - ) - { - builder.AddImageTiled(cell, style.EntryGumpID); - builder.AddHtml(cell, ref handler, style.TextOffsetX, 0); - } - - /// - /// Adds a header entry (HeaderGumpID background only). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryHeader( - in GridCell cell, - in GridEntryStyle style - ) - { - builder.AddImageTiled(cell, style.HeaderGumpID); - } - - /// - /// Adds an entry with a centered button (HeaderGumpID background + centered button). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryButton( - in GridCell cell, - in GridEntryStyle style, - int normalId, - int pressedId, - int buttonId, - int buttonWidth, - int buttonHeight - ) - { - builder.AddImageTiled(cell, style.HeaderGumpID); - builder.AddButton( - cell, normalId, pressedId, buttonId, - offsetX: (cell.Width - buttonWidth) / 2, - offsetY: (cell.Height - buttonHeight) / 2 - ); - } - - /// - /// Adds an entry with arrow left button. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryArrowLeft( - in GridCell cell, - in GridEntryStyle style, - int buttonId - ) - { - builder.AddEntryButton( - cell, style, - GridEntryStyle.ArrowLeftID1, - GridEntryStyle.ArrowLeftID2, - buttonId, - GridEntryStyle.ArrowWidth, - GridEntryStyle.ArrowHeight - ); - } - - /// - /// Adds an entry with arrow right button. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryArrowRight( - in GridCell cell, - in GridEntryStyle style, - int buttonId - ) - { - builder.AddEntryButton( - cell, style, - GridEntryStyle.ArrowRightID1, - GridEntryStyle.ArrowRightID2, - buttonId, - GridEntryStyle.ArrowWidth, - GridEntryStyle.ArrowHeight - ); - } - - /// - /// Adds an entry with text input (EntryGumpID background + text entry). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryText( - in GridCell cell, - in GridEntryStyle style, - int entryId, - ReadOnlySpan initialText = default - ) - { - builder.AddImageTiled(cell, style.EntryGumpID); - builder.AddTextEntry(cell, style.TextHue, entryId, style.TextOffsetX, 0, initialText); - } - - /// - /// Adds an entry with text input using interpolated string. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryText( - in GridCell cell, - in GridEntryStyle style, - int entryId, - ref RawInterpolatedStringHandler handler - ) - { - builder.AddImageTiled(cell, style.EntryGumpID); - builder.AddTextEntry(cell, style.TextHue, entryId, style.TextOffsetX, 0, ref handler); - } - - /// - /// Adds a blank line (BackGumpID + 4 background across full width). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEntryBlank( - in GridCell cell, - in GridEntryStyle style - ) - { - builder.AddImageTiled(cell, style.BackGumpID + 4); - } - - /// - /// Adds the standard grid background (outer background + inner offset region). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddGridBackground( - int width, - int height, - in GridEntryStyle style - ) - { - builder.AddBackground(0, 0, width, height, style.BackGumpID); - builder.AddImageTiled( - style.BorderSize, - style.BorderSize, - width - style.BorderSize * 2, - height - style.BorderSize * 2, - style.OffsetGumpID - ); - } - } -} diff --git a/Projects/UOContent/Gumps/Base/GridLayout/GridEntryStyle.cs b/Projects/UOContent/Gumps/Base/GridLayout/GridEntryStyle.cs deleted file mode 100644 index a2c08ea39..000000000 --- a/Projects/UOContent/Gumps/Base/GridLayout/GridEntryStyle.cs +++ /dev/null @@ -1,165 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: GridEntryStyle.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -namespace Server.Gumps; - -/// -/// Defines the visual style for grid-based entry layouts. -/// Mirrors the virtual properties from BaseGridGump for easy migration. -/// -public readonly struct GridEntryStyle -{ - /// - /// Default style matching BaseGridGump defaults. - /// - public static readonly GridEntryStyle Default = new( - entryGumpID: 0x0BBC, - headerGumpID: 0x0E14, - offsetGumpID: 0x0A40, - backGumpID: 0x13BE, - textHue: 0, - textOffsetX: 2, - entryHeight: 20, - borderSize: 10, - offsetSize: 1 - ); - - /// - /// Background gump ID for data entry cells. - /// - public readonly int EntryGumpID; - - /// - /// Background gump ID for header/button cells. - /// - public readonly int HeaderGumpID; - - /// - /// Gump ID for the offset/separator region. - /// - public readonly int OffsetGumpID; - - /// - /// Gump ID for the main background. - /// - public readonly int BackGumpID; - - /// - /// Text hue for labels. - /// - public readonly int TextHue; - - /// - /// Horizontal offset for text within cells. - /// - public readonly int TextOffsetX; - - /// - /// Height of each entry row. - /// - public readonly int EntryHeight; - - /// - /// Size of the outer border. - /// - public readonly int BorderSize; - - /// - /// Size of the offset/gap between cells. - /// - public readonly int OffsetSize; - - // Arrow button constants (matching BaseGridGump) - public const int ArrowLeftID1 = 0x15E3; - public const int ArrowLeftID2 = 0x15E7; - public const int ArrowRightID1 = 0x15E1; - public const int ArrowRightID2 = 0x15E5; - public const int ArrowWidth = 16; - public const int ArrowHeight = 16; - - public GridEntryStyle( - int entryGumpID, - int headerGumpID, - int offsetGumpID, - int backGumpID, - int textHue, - int textOffsetX, - int entryHeight, - int borderSize, - int offsetSize - ) - { - EntryGumpID = entryGumpID; - HeaderGumpID = headerGumpID; - OffsetGumpID = offsetGumpID; - BackGumpID = backGumpID; - TextHue = textHue; - TextOffsetX = textOffsetX; - EntryHeight = entryHeight; - BorderSize = borderSize; - OffsetSize = offsetSize; - } - - /// - /// Creates a copy with modified values. - /// - public GridEntryStyle With( - int entryGumpID = -1, - int headerGumpID = -1, - int offsetGumpID = -1, - int backGumpID = -1, - int textHue = -1, - int textOffsetX = -1, - int entryHeight = -1, - int borderSize = -1, - int offsetSize = -1 - ) => new( - entryGumpID < 0 ? EntryGumpID : entryGumpID, - headerGumpID < 0 ? HeaderGumpID : headerGumpID, - offsetGumpID < 0 ? OffsetGumpID : offsetGumpID, - backGumpID < 0 ? BackGumpID : backGumpID, - textHue < 0 ? TextHue : textHue, - textOffsetX < 0 ? TextOffsetX : textOffsetX, - entryHeight < 0 ? EntryHeight : entryHeight, - borderSize < 0 ? BorderSize : borderSize, - offsetSize < 0 ? OffsetSize : offsetSize - ); - - /// - /// Calculates the content origin X (inside borders). - /// - public int ContentOriginX => BorderSize + OffsetSize; - - /// - /// Calculates the content origin Y (inside borders). - /// - public int ContentOriginY => BorderSize + OffsetSize; - - /// - /// Calculates total width given content width. - /// - public int GetTotalWidth(int contentWidth) => contentWidth + BorderSize * 2 + OffsetSize * 2; - - /// - /// Calculates total height given number of rows. - /// - public int GetTotalHeight(int rowCount) => - BorderSize * 2 + OffsetSize * 2 + rowCount * EntryHeight + (rowCount - 1) * OffsetSize; - - /// - /// Calculates content height given number of rows. - /// - public int GetContentHeight(int rowCount) => rowCount * EntryHeight + (rowCount - 1) * OffsetSize; -} diff --git a/Projects/UOContent/Gumps/Base/GridLayout/GridSizeSpec.cs b/Projects/UOContent/Gumps/Base/GridLayout/GridSizeSpec.cs deleted file mode 100644 index 19ce11da8..000000000 --- a/Projects/UOContent/Gumps/Base/GridLayout/GridSizeSpec.cs +++ /dev/null @@ -1,162 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: GridSizeSpec.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; - -namespace Server.Gumps; - -/// -/// Specifies how a grid column or row should be sized. -/// Supports absolute pixels, percentage, and star (proportional) sizing. -/// -public readonly struct GridSizeSpec -{ - /// - /// The sizing type. - /// - public readonly GridSizeType Type; - - /// - /// The value - meaning depends on Type: - /// - Absolute: pixel size - /// - Percent: percentage of total size (1-100) - /// - Star: proportional weight (1 = equal share) - /// - public readonly int Value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private GridSizeSpec(GridSizeType type, int value) - { - Type = type; - Value = value; - } - - /// - /// Creates an absolute pixel size specification. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static GridSizeSpec Absolute(int pixels) => new(GridSizeType.Absolute, pixels); - - /// - /// Creates a star (proportional) size specification. - /// Star-sized tracks share the remaining space equally (after absolute/percent are allocated). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static GridSizeSpec Star() => new(GridSizeType.Star, 1); - - /// - /// Creates a percentage size specification. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static GridSizeSpec Percent(int percent) => new(GridSizeType.Percent, percent); - - /// - /// Parses a single size specification token. - /// Formats: - /// - "*" = star (equal share of remaining space) - /// - "10*" = 10% of total size - /// - "100" = 100 pixels absolute - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static GridSizeSpec Parse(ReadOnlySpan token) - { - token = token.Trim(); - - if (token.IsEmpty) - { - return Star(); - } - - // Check for star notation - var starIndex = token.IndexOf('*'); - - if (starIndex == -1) - { - // No star - absolute pixel value - return int.TryParse(token, out var pixels) ? Absolute(pixels) : Star(); - } - - if (starIndex == 0 && token.Length == 1) - { - // Just "*" - equal share star - return Star(); - } - - // "N*" format - percentage - var percentPart = token[..starIndex]; - return int.TryParse(percentPart, out var percent) ? Percent(percent) : Star(); - } - - /// - /// Parses a space-separated size specification string into a span of GridSizeSpec values. - /// Returns the number of specs parsed. - /// - public static int ParseAll(ReadOnlySpan sizeSpec, Span destination) - { - var count = 0; - var remaining = sizeSpec; - - while (!remaining.IsEmpty && count < destination.Length) - { - // Skip leading whitespace - remaining = remaining.TrimStart(); - if (remaining.IsEmpty) - { - break; - } - - // Find end of current token - var spaceIndex = remaining.IndexOf(' '); - ReadOnlySpan token; - - if (spaceIndex < 0) - { - token = remaining; - remaining = default; - } - else - { - token = remaining[..spaceIndex]; - remaining = remaining[(spaceIndex + 1)..]; - } - - destination[count++] = Parse(token); - } - - return count; - } -} - -/// -/// Specifies the type of grid size specification. -/// -public enum GridSizeType : byte -{ - /// - /// Fixed pixel size. - /// - Absolute, - - /// - /// Star sizing - shares remaining space equally with other star-sized tracks. - /// - Star, - - /// - /// Percentage of total available size. - /// - Percent -} diff --git a/Projects/UOContent/Gumps/Base/GridLayout/ListViewLayout.cs b/Projects/UOContent/Gumps/Base/GridLayout/ListViewLayout.cs deleted file mode 100644 index 964c4bfe9..000000000 --- a/Projects/UOContent/Gumps/Base/GridLayout/ListViewLayout.cs +++ /dev/null @@ -1,239 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ListViewLayout.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; - -namespace Server.Gumps; - -/// -/// Calculates pagination and provides helper methods for a paginated list view. -/// This struct does NOT hold column data - column positions/widths are passed -/// separately via spans to avoid ref struct limitations. -/// -public ref struct ListViewLayout -{ - private readonly int _originY; - private readonly int _headerHeight; - private readonly int _rowHeight; - - /// - /// The total number of items in the list. - /// - public readonly int TotalItems; - - /// - /// The number of items that can be displayed per page. - /// - public readonly int ItemsPerPage; - - /// - /// The current page index (0-based). - /// - public readonly int CurrentPage; - - /// - /// The total number of pages. - /// - public readonly int TotalPages; - - /// - /// The index of the first item on the current page. - /// - public readonly int StartIndex; - - /// - /// The number of items visible on the current page. - /// - public readonly int VisibleCount; - - /// - /// Whether there is a previous page. - /// - public readonly bool CanGoBack; - - /// - /// Whether there is a next page. - /// - public readonly bool CanGoNext; - - /// - /// The number of columns in the list. - /// - public readonly int ColumnCount; - - /// - /// The height of each data row. - /// - public int RowHeight - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _rowHeight; - } - - /// - /// The height of the header row. - /// - public int HeaderHeight - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _headerHeight; - } - - /// - /// Creates a new ListViewLayout and computes column positions into the provided spans. - /// - /// The X origin of the list area. - /// The Y origin of the list area. - /// The total width of the list area. - /// The total height of the list area. - /// The total number of items in the data source. - /// The current page index (0-based). - /// The height of each data row. - /// The height of the header row (0 for no header). - /// Space-separated column size specification. - /// Span to receive calculated column X positions. - /// Span to receive calculated column widths. - /// The configured ListViewLayout. - public static ListViewLayout Create( - int originX, - int originY, - int width, - int height, - int totalItems, - int currentPage, - int rowHeight, - int headerHeight, - ReadOnlySpan columnSpec, - Span columnPositions, - Span columnWidths - ) - { - // Parse and compute column widths - var columnCount = GridCalculator.ComputeFromSpec(columnSpec, width, originX, columnPositions, columnWidths); - - return new ListViewLayout( - originY, - headerHeight, rowHeight, - totalItems, currentPage, - height, - columnCount - ); - } - - private ListViewLayout( - int originY, - int headerHeight, - int rowHeight, - int totalItems, - int currentPage, - int totalHeight, - int columnCount - ) - { - _originY = originY; - _headerHeight = headerHeight; - _rowHeight = rowHeight; - ColumnCount = columnCount; - - TotalItems = totalItems; - - // Calculate items per page based on available height - var contentHeight = totalHeight - headerHeight; - ItemsPerPage = contentHeight / rowHeight; - - // Calculate pagination - if (totalItems > 0 && ItemsPerPage > 0) - { - TotalPages = (totalItems + ItemsPerPage - 1) / ItemsPerPage; - CurrentPage = Math.Clamp(currentPage, 0, TotalPages - 1); - StartIndex = CurrentPage * ItemsPerPage; - VisibleCount = Math.Min(ItemsPerPage, totalItems - StartIndex); - CanGoBack = CurrentPage > 0; - CanGoNext = CurrentPage < TotalPages - 1; - } - else - { - TotalPages = 1; - CurrentPage = 0; - StartIndex = 0; - VisibleCount = 0; - CanGoBack = false; - CanGoNext = false; - } - } - - /// - /// Gets the header cell for the specified column using pre-computed column data. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GridCell GetHeaderCell(int column, ReadOnlySpan columnPositions, ReadOnlySpan columnWidths) => - new(columnPositions[column], _originY, columnWidths[column], _headerHeight); - - /// - /// Gets the data cell for the specified visible row and column. - /// - /// The 0-based index within visible rows (not data index). - /// The column index. - /// Pre-computed column X positions. - /// Pre-computed column widths. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GridCell GetRowCell( - int visibleRowIndex, int column, ReadOnlySpan columnPositions, ReadOnlySpan columnWidths - ) => new( - columnPositions[column], - _originY + _headerHeight + visibleRowIndex * _rowHeight, - columnWidths[column], - _rowHeight - ); - - /// - /// Gets the data index for a visible row. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetDataIndex(int visibleRowIndex) => StartIndex + visibleRowIndex; - - /// - /// Gets the Y position for a visible row. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetRowY(int visibleRowIndex) => _originY + _headerHeight + visibleRowIndex * _rowHeight; - - /// - /// Gets the X position of a column. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetColumnX(ReadOnlySpan columnPositions, int column) => columnPositions[column]; - - /// - /// Gets the width of a column. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetColumnWidth(ReadOnlySpan columnWidths, int column) => columnWidths[column]; - - /// - /// Gets the horizontal center of a column. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetColumnCenterX(ReadOnlySpan columnPositions, ReadOnlySpan columnWidths, int column) => - columnPositions[column] + columnWidths[column] / 2; - - /// - /// Gets the right edge of a column. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetColumnRightX(ReadOnlySpan columnPositions, ReadOnlySpan columnWidths, int column) => - columnPositions[column] + columnWidths[column]; -} diff --git a/Projects/UOContent/Gumps/Base/GumpColors.cs b/Projects/UOContent/Gumps/Base/GumpColors.cs deleted file mode 100644 index 35b5b9041..000000000 --- a/Projects/UOContent/Gumps/Base/GumpColors.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Server.Gumps; - -public class GumpTextColors -{ - public const string Gold = "#ffc959"; - public const string White = "#ffffff"; - public const string Blue = "#1D63DC"; - public const string LightBlue = "#7799EE"; - public const string Green = "#80E732"; - public const string Orange = "#F28B00"; - public const string Purple = "#F3ACF6"; - public const string Red = "#B52B2D"; - public const string BrightRed = "#FF0000"; - public const string LightGreen = "#E6FFC0"; - public const string Yellow = "#FFFFBB"; - public const string LightGray = "#CCCCCC"; -} - -public class GumpHues -{ - public const int Black = 0; - public const int White = 2049; -} diff --git a/Projects/UOContent/Gumps/Base/GumpFlags.cs b/Projects/UOContent/Gumps/Base/GumpFlags.cs index b417e0cd1..437e2b5ab 100644 --- a/Projects/UOContent/Gumps/Base/GumpFlags.cs +++ b/Projects/UOContent/Gumps/Base/GumpFlags.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpFlags.cs * * * diff --git a/Projects/UOContent/Gumps/Base/GumpLayoutBuilder.cs b/Projects/UOContent/Gumps/Base/GumpLayoutBuilder.cs index 4bd5c7d8f..8732b74bf 100644 --- a/Projects/UOContent/Gumps/Base/GumpLayoutBuilder.cs +++ b/Projects/UOContent/Gumps/Base/GumpLayoutBuilder.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpLayoutBuilder.cs * * * @@ -30,7 +30,6 @@ public ref struct GumpLayoutBuilder internal GumpFlags _flags; internal int _switches; internal int _textEntries; - internal bool _hasVisualElements; internal Span LayoutData => _layoutBuffer.AsSpan(0, _bytesWritten); @@ -96,7 +95,6 @@ public ref struct GumpLayoutBuilder public void AddBackground(int x, int y, int width, int height, int gumpId) { - _hasVisualElements = true; GrowIfNeeded(9 + 9 + 45); WriteStart("resizepic"u8); WriteValue(x); @@ -111,7 +109,6 @@ public ref struct GumpLayoutBuilder int x, int y, int normalId, int pressedId, int buttonId, GumpButtonType type = GumpButtonType.Reply, int param = 0 ) { - _hasVisualElements = true; GrowIfNeeded(11 + 6 + 54 + 2); WriteStart("button"u8); WriteValue(x); @@ -126,7 +123,6 @@ public ref struct GumpLayoutBuilder public void AddCheckbox(int x, int y, int inactiveId, int activeId, bool selected, int switchId) { - _hasVisualElements = true; GrowIfNeeded(10 + 8 + 45 + 2); WriteStart("checkbox"u8); WriteValue(x); @@ -158,7 +154,6 @@ public ref struct GumpLayoutBuilder public int AddHtmlPlaceholder(int x, int y, int width, int height, bool background = false, bool scrollbar = false) { - _hasVisualElements = true; GrowIfNeeded(11 + 8 + 36 + 10); WriteStart("htmlgump"u8); WriteValue(x); @@ -177,7 +172,6 @@ public ref struct GumpLayoutBuilder public void AddHtml(int x, int y, int width, int height, int text, bool background = false, bool scrollbar = false) { - _hasVisualElements = true; GrowIfNeeded(11 + 8 + 45 + 4); WriteStart("htmlgump"u8); WriteValue(x); @@ -194,7 +188,6 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int number, bool background = false, bool scrollbar = false ) { - _hasVisualElements = true; GrowIfNeeded(11 + 11 + 45 + 4); WriteStart("xmfhtmlgump"u8); WriteValue(x); @@ -211,7 +204,6 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int number, int color, bool background = false, bool scrollbar = false ) { - _hasVisualElements = true; GrowIfNeeded(12 + 16 + 45 + 5 + 4); WriteStart("xmfhtmlgumpcolor"u8); WriteValue(x); @@ -228,7 +220,6 @@ public ref struct GumpLayoutBuilder public void AddHtmlLocalized(int x, int y, int width, int height, int number, ReadOnlySpan args, int color, bool background = false, bool scrollbar = false) { - _hasVisualElements = true; GrowIfNeeded(12 + 10 + 45 + 5 + 4 + (args.Length > 0 ? 3 + args.Length : 0)); WriteStart("xmfhtmltok"u8); WriteValue(x); @@ -263,7 +254,6 @@ public ref struct GumpLayoutBuilder public void AddImage(int x, int y, int gumpId, int hue = 0, ReadOnlySpan cls = default) { - _hasVisualElements = true; GrowIfNeeded(7 + 7 + 36 + (hue != 0 ? 14 : 0) + (cls.Length > 0 ? 7 + cls.Length : 0)); WriteStart("gumppic"u8); WriteValue(x); @@ -304,7 +294,6 @@ public ref struct GumpLayoutBuilder public void AddImageTiledButton(int x, int y, int normalId, int pressedId, int buttonId, GumpButtonType type, int param, int itemId, int hue, int width, int height, int localizedTooltip = -1) { - _hasVisualElements = true; GrowIfNeeded(15 + 13 + 90 + 2); WriteStart("buttontileart"u8); WriteValue(x); @@ -330,7 +319,6 @@ public ref struct GumpLayoutBuilder public void AddImageTiled(int x, int y, int width, int height, int gumpId) { - _hasVisualElements = true; GrowIfNeeded(9 + 12 + 45); WriteStart("gumppictiled"u8); WriteValue(x); @@ -343,7 +331,6 @@ public ref struct GumpLayoutBuilder public void AddItem(int x, int y, int itemId, int hue = 0) { - _hasVisualElements = true; GrowIfNeeded(7 + 36 + (hue != 0 ? 20 : 7)); WriteStart(hue == 0 ? "tilepic"u8 : "tilepichue"u8); WriteValue(x); @@ -368,7 +355,6 @@ public ref struct GumpLayoutBuilder public int AddLabelPlaceholder(int x, int y, int hue) { - _hasVisualElements = true; GrowIfNeeded(8 + 4 + 27 + 6); WriteStart("text"u8); WriteValue(x); @@ -382,7 +368,6 @@ public ref struct GumpLayoutBuilder public void AddLabel(int x, int y, int hue, int text) { - _hasVisualElements = true; GrowIfNeeded(8 + 4 + 36 + 6); WriteStart("text"u8); WriteValue(x); @@ -394,7 +379,6 @@ public ref struct GumpLayoutBuilder public int AddLabelCroppedPlaceholder(int x, int y, int width, int height, int hue) { - _hasVisualElements = true; GrowIfNeeded(10 + 11 + 45 + 6); WriteStart("croppedtext"u8); WriteValue(x); @@ -410,7 +394,6 @@ public ref struct GumpLayoutBuilder public void AddLabelCropped(int x, int y, int width, int height, int hue, int text) { - _hasVisualElements = true; GrowIfNeeded(10 + 11 + 54 + 6); WriteStart("croppedtext"u8); WriteValue(x); @@ -447,7 +430,6 @@ public ref struct GumpLayoutBuilder public void AddRadio(int x, int y, int inactiveId, int activeId, bool selected, int switchId) { - _hasVisualElements = true; GrowIfNeeded(10 + 5 + 45 + 2); WriteStart("radio"u8); WriteValue(x); @@ -463,7 +445,6 @@ public ref struct GumpLayoutBuilder public void AddSpriteImage(int x, int y, int gumpId, int width, int height, int sx, int sy) { - _hasVisualElements = true; GrowIfNeeded(11 + 8 + 63); WriteStart("picinpic"u8); WriteValue(x); @@ -480,7 +461,6 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int hue, int entryId ) { - _hasVisualElements = true; GrowIfNeeded(11 + 9 + 54 + 6); WriteStart("textentry"u8); WriteValue(x); @@ -501,7 +481,6 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int hue, int entryId, int initialText ) { - _hasVisualElements = true; GrowIfNeeded(11 + 9 + 63 + 6); WriteStart("textentry"u8); WriteValue(x); @@ -520,7 +499,6 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int hue, int entryId, int size = 0 ) { - _hasVisualElements = true; GrowIfNeeded(12 + 16 + 63 + 6); WriteStart("textentrylimited"u8); WriteValue(x); @@ -542,7 +520,6 @@ public ref struct GumpLayoutBuilder int x, int y, int width, int height, int hue, int entryId, int initialText, int size = 0 ) { - _hasVisualElements = true; GrowIfNeeded(12 + 16 + 72); WriteStart("textentrylimited"u8); WriteValue(x); @@ -600,14 +577,14 @@ public ref struct GumpLayoutBuilder { if (!value.IsEmpty) { - _bytesWritten += value.GetBytesLatin1(_layoutBuffer.AsSpan(_bytesWritten)); + _bytesWritten += value.GetBytesAscii(_layoutBuffer.AsSpan(_bytesWritten)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteValue(T value, ReadOnlySpan format = default) where T : IUtf8SpanFormattable { - if (!value.TryFormat(_layoutBuffer.AsSpan(_bytesWritten), out var bytesWritten, format, null)) + if (!value.TryFormat(_layoutBuffer.AsSpan(_bytesWritten), out int bytesWritten, format, null)) { throw new InvalidOperationException($"Failed to format '{value}' with the given format '{format}'"); } @@ -639,11 +616,11 @@ public ref struct GumpLayoutBuilder } var newSize = Math.Max(_bytesWritten + count, _layoutBuffer.Length * 2); - var poolArray = STArrayPool.Shared.Rent(newSize); + byte[] poolArray = STArrayPool.Shared.Rent(newSize); _layoutBuffer.AsSpan(0, _bytesWritten).CopyTo(poolArray); - var toReturn = _layoutBuffer; + byte[] toReturn = _layoutBuffer; _layoutBuffer = poolArray; if (toReturn != _staticLayoutBuffer) diff --git a/Projects/UOContent/Gumps/Base/GumpStringsBuilder.cs b/Projects/UOContent/Gumps/Base/GumpStringsBuilder.cs index 799c60fae..798dc6a51 100644 --- a/Projects/UOContent/Gumps/Base/GumpStringsBuilder.cs +++ b/Projects/UOContent/Gumps/Base/GumpStringsBuilder.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpStringsBuilder.cs * * * @@ -51,11 +51,11 @@ public ref struct GumpStringsBuilder } var newSize = Math.Max(newLength, _stringsBuffer.Length * 2); - var poolArray = STArrayPool.Shared.Rent(newSize); + byte[] poolArray = STArrayPool.Shared.Rent(newSize); _stringsBuffer.AsSpan(0, _stringBytesWritten).CopyTo(poolArray); - var toReturn = _stringsBuffer; + byte[] toReturn = _stringsBuffer; _stringsBuffer = poolArray; if (toReturn != _staticStringsBuffer) @@ -64,50 +64,6 @@ public ref struct GumpStringsBuilder } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetHtmlText( - ref RawInterpolatedStringHandler slotKeyHandler, ref RawInterpolatedStringHandler handler, ReadOnlySpan color = default, int size = -1, - byte fontStyle = 0, TextAlignment align = TextAlignment.Left - ) - { - SetHtmlText(slotKeyHandler.Text, handler.Text, color, size, fontStyle, align); - slotKeyHandler.Clear(); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetHtmlText( - ref RawInterpolatedStringHandler slotKeyHandler, ReadOnlySpan value, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, - TextAlignment align = TextAlignment.Left - ) - { - SetHtmlText(slotKeyHandler.Text, value, color, size, fontStyle, align); - slotKeyHandler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetHtmlText( - ReadOnlySpan slotKey, ref RawInterpolatedStringHandler handler, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, - TextAlignment align = TextAlignment.Left - ) - { - SetHtmlText(slotKey, handler.Text, color, size, fontStyle, align); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetHtmlText( - ReadOnlySpan slotKey, ReadOnlySpan value, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, - TextAlignment align = TextAlignment.Left - ) - { - var arr = STArrayPool.Shared.Rent(Html.BuildCharCount(value, color)); - var bytesWritten = Html.Build(value, arr.AsSpan(), color, size, fontStyle, align); - - SetStringSlot(slotKey, arr.AsSpan(0, bytesWritten)); - STArrayPool.Shared.Return(arr); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetStringSlot(ReadOnlySpan slotKey, ref RawInterpolatedStringHandler handler) { @@ -115,20 +71,6 @@ public ref struct GumpStringsBuilder handler.Clear(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetStringSlot(ref RawInterpolatedStringHandler slotKeyHandler, ref RawInterpolatedStringHandler handler) - { - SetStringSlot(ref slotKeyHandler, handler.Text); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetStringSlot(ref RawInterpolatedStringHandler slotKeyHandler, ReadOnlySpan text) - { - SetStringSlot(slotKeyHandler.Text, text); - slotKeyHandler.Clear(); - } - public void SetStringSlot(ReadOnlySpan slotKey, ReadOnlySpan text) { var hash = HashUtility.ComputeHash64(slotKey); diff --git a/Projects/UOContent/Gumps/Base/GumpSystem.IncomingPackets.cs b/Projects/UOContent/Gumps/Base/GumpSystem.IncomingPackets.cs index f281cb877..b0d2427c8 100644 --- a/Projects/UOContent/Gumps/Base/GumpSystem.IncomingPackets.cs +++ b/Projects/UOContent/Gumps/Base/GumpSystem.IncomingPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingGumpPackets.cs * * * @@ -95,10 +95,10 @@ public static partial class GumpSystem return; } - var switchByteCount = switchCount * 4; + int switchByteCount = switchCount * 4; // Read all the integers - var switchBlock = + ReadOnlySpan switchBlock = MemoryMarshal.Cast(reader.Buffer.Slice(reader.Position, switchByteCount)); reader.Seek(switchByteCount, SeekOrigin.Current); diff --git a/Projects/UOContent/Gumps/Base/GumpSystem.cs b/Projects/UOContent/Gumps/Base/GumpSystem.cs index ddbb91382..c0243a08d 100644 --- a/Projects/UOContent/Gumps/Base/GumpSystem.cs +++ b/Projects/UOContent/Gumps/Base/GumpSystem.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpSystem.cs * * * @@ -14,7 +14,6 @@ *************************************************************************/ using Server.Network; -using Server.Systems.FeatureFlags; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -46,7 +45,7 @@ public static partial class GumpSystem { if (_gumps.TryGetValue(ns, out var gumps)) { - for (var i = 0; i < gumps.Count; i++) + for (int i = 0; i < gumps.Count; i++) { if (gumps[i] == gump) { @@ -64,7 +63,7 @@ public static partial class GumpSystem return new NetStateGumps(null, null); } - ref var list = ref CollectionsMarshal.GetValueRefOrAddDefault(_gumps, ns, out var exists); + ref List list = ref CollectionsMarshal.GetValueRefOrAddDefault(_gumps, ns, out bool exists); if (!exists) { @@ -106,14 +105,6 @@ public static partial class GumpSystem { ArgumentNullException.ThrowIfNull(m); - if (m.AccessLevel < FeatureFlagSettings.RequiredAccessLevel - && FeatureFlagManager.IsGumpBlocked(g.GetType())) - { - var entry = FeatureFlagManager.GetGumpBlockEntry(g.GetType()); - m.SendMessage(0x22, entry?.Reason ?? FeatureFlagSettings.DefaultGumpBlockedMessage); - return; - } - var state = m.NetState; if (state != null) diff --git a/Projects/UOContent/Gumps/Base/InvalidGumpResponseException.cs b/Projects/UOContent/Gumps/Base/InvalidGumpResponseException.cs index 72b637393..a31b01449 100644 --- a/Projects/UOContent/Gumps/Base/InvalidGumpResponseException.cs +++ b/Projects/UOContent/Gumps/Base/InvalidGumpResponseException.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: InvalidGumpResponseException.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/Gump.cs b/Projects/UOContent/Gumps/Base/Legacy/Gump.cs index 3be1ddebf..0bcd9dd3c 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/Gump.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/Gump.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Gump.cs * * * @@ -169,7 +169,7 @@ public class Gump : BaseGump public void AddLabelHtml(int x, int y, int width, int height, string text, string hue, int size = 4, bool center = true) { - AddHtml(x, y, width, height, Html.Build(text, hue, size, align: center ? TextAlignment.Center : TextAlignment.Left)); + AddHtml(x, y, width, height, center ? text.Center(hue, size) : text.Color(hue, size)); } public void AddLabelCropped(int x, int y, int width, int height, int hue, string text) @@ -226,10 +226,13 @@ public class Gump : BaseGump public void Remove(GumpEntry g) { - if (g != null && Entries.Remove(g)) + if (g == null || !Entries.Contains(g)) { - g.Parent = null; + return; } + + Entries.Remove(g); + g.Parent = null; } public int Intern(string value) @@ -249,7 +252,6 @@ public class Gump : BaseGump { _textEntries = 0; _switches = 0; - HasVisualElements = false; var layoutWriter = new SpanWriter(_layoutBuffer); @@ -275,7 +277,6 @@ public class Gump : BaseGump foreach (var entry in Entries) { - HasVisualElements |= IsVisualEntry(entry); entry.AppendTo(ref layoutWriter, _stringsList, ref _textEntries, ref _switches); } @@ -313,9 +314,6 @@ public class Gump : BaseGump } } - private static bool IsVisualEntry(GumpEntry entry) => - entry is not (GumpAlphaRegion or GumpECHandleInput or GumpGroup or GumpItemProperty or GumpMasterGump or GumpPage or GumpTooltip); - protected void Reset() { _switches = 0; diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpAlphaRegion.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpAlphaRegion.cs index e95cfde85..f3e1c1aad 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpAlphaRegion.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpAlphaRegion.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpAlphaRegion.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpBackground.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpBackground.cs index bdfa3561e..e303cf949 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpBackground.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpBackground.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpBackground.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpButton.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpButton.cs index 10ee9679b..c2418ea00 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpButton.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpButton.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpButton.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpCheck.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpCheck.cs index f11a14001..b2b71a3d5 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpCheck.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpCheck.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpCheck.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpECHandleInput.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpECHandleInput.cs index 74d227611..57d701687 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpECHandleInput.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpECHandleInput.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpECHandleInput.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpGroup.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpGroup.cs index 56527708e..2f9460db2 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpGroup.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpGroup.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpGroup.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpHtml.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpHtml.cs index 0eff76a0a..250b9f99a 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpHtml.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpHtml.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpHtml.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpHtmlLocalized.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpHtmlLocalized.cs index eafe60535..7aec85e4f 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpHtmlLocalized.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpHtmlLocalized.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpHtmlLocalized.cs * * * @@ -122,7 +122,7 @@ public class GumpHtmlLocalized : GumpEntry } case GumpHtmlLocalizedType.Args: { - writer.WriteLatin1( + writer.WriteAscii( $"{{ xmfhtmltok {X} {Y} {Width} {Height} {(Background ? "1" : "0")} {(Scrollbar ? "1" : "0")} {Color} {Number} @{Args}@ }}" ); break; diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpImage.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpImage.cs index edb8a1eae..48d925340 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpImage.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpImage.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpImage.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpImageTileButton.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpImageTileButton.cs index 1cc102466..453795d3d 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpImageTileButton.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpImageTileButton.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpImageTileButton.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpImageTiled.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpImageTiled.cs index 9a0a30364..03f685045 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpImageTiled.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpImageTiled.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpImageTiled.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpItem.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpItem.cs index baf24d678..f3b8ae57b 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpItem.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpItem.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpItem.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpItemProperty.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpItemProperty.cs index c29ce2f25..4d52fac6a 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpItemProperty.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpItemProperty.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpItemProperty.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpLabel.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpLabel.cs index 323314533..5c3e7a81b 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpLabel.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpLabel.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpLabel.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpLabelCropped.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpLabelCropped.cs index 73d9c88dd..2188ae9cd 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpLabelCropped.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpLabelCropped.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpLabelCropped.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpMasterGump.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpMasterGump.cs index 1dc2c8a13..71578ade8 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpMasterGump.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpMasterGump.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpMasterGump.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpPage.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpPage.cs index bafe18790..726c767f6 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpPage.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpPage.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpPage.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpRadio.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpRadio.cs index a0455bcf1..516c58622 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpRadio.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpRadio.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpRadio.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpSpriteImage.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpSpriteImage.cs index 6f11da106..9881b301e 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpSpriteImage.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpSpriteImage.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpSpriteImage.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpTextEntry.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpTextEntry.cs index 049bd4ebd..4889dcab6 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpTextEntry.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpTextEntry.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpTextEntry.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpTextEntryLimited.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpTextEntryLimited.cs index 03b71f8b8..f23d72ed9 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpTextEntryLimited.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpTextEntryLimited.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpTextEntryLimited.cs * * * diff --git a/Projects/UOContent/Gumps/Base/Legacy/GumpTooltip.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpTooltip.cs index 559684073..2d584eb8b 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/GumpTooltip.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpTooltip.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpTooltip.cs * * * @@ -34,13 +34,6 @@ public class GumpTooltip : GumpEntry public override void AppendTo(ref SpanWriter writer, OrderedSet strings, ref int entries, ref int switches) { - if (string.IsNullOrEmpty(Args)) - { - writer.WriteAscii($"{{ tooltip {Number} }}"); - } - else - { - writer.WriteLatin1($"{{ tooltip {Number} @{Args}@ }}"); - } + writer.WriteAscii(string.IsNullOrEmpty(Args) ? $"{{ tooltip {Number} }}" : $"{{ tooltip {Number} @{Args}@ }}"); } } diff --git a/Projects/UOContent/Gumps/Base/NetStateGumps.cs b/Projects/UOContent/Gumps/Base/NetStateGumps.cs index 77130faf9..ba21804d0 100644 --- a/Projects/UOContent/Gumps/Base/NetStateGumps.cs +++ b/Projects/UOContent/Gumps/Base/NetStateGumps.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: NetStateGumps.cs * * * @@ -43,7 +43,7 @@ public readonly ref struct NetStateGumps return false; } - for (var i = 0; i < _gumps.Count; i++) + for (int i = 0; i < _gumps.Count; i++) { if (_gumps[i] is T tGump) { @@ -65,7 +65,7 @@ public readonly ref struct NetStateGumps return null; } - for (var i = 0; i < _gumps.Count; i++) + for (int i = 0; i < _gumps.Count; i++) { if (_gumps[i] is T tGump) { @@ -87,9 +87,9 @@ public readonly ref struct NetStateGumps if (singleton || gump.Singleton) { - for (var i = 0; i < _gumps.Count; i++) + for (int i = 0; i < _gumps.Count; i++) { - var old = _gumps[i]; + BaseGump old = _gumps[i]; if (old.TypeID == gump.TypeID) { diff --git a/Projects/UOContent/Gumps/Base/OutgoingGumpPackets.cs b/Projects/UOContent/Gumps/Base/OutgoingGumpPackets.cs index 5b83127b5..9657962dd 100644 --- a/Projects/UOContent/Gumps/Base/OutgoingGumpPackets.cs +++ b/Projects/UOContent/Gumps/Base/OutgoingGumpPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingGumpPackets.cs * * * @@ -73,9 +73,9 @@ public static class OutgoingGumpPackets writer.Write(serial); writer.Write((short)gumpId); writer.Write((short)(unknown.Length + 1)); - writer.WriteLatin1Null(unknown); + writer.WriteAsciiNull(unknown); writer.Write((short)(caption.Length + 1)); - writer.WriteLatin1Null(caption); + writer.WriteAsciiNull(caption); ns.Send(writer.Span); } diff --git a/Projects/UOContent/Gumps/Base/RelayInfo.cs b/Projects/UOContent/Gumps/Base/RelayInfo.cs index 76a32881b..c92d6043c 100644 --- a/Projects/UOContent/Gumps/Base/RelayInfo.cs +++ b/Projects/UOContent/Gumps/Base/RelayInfo.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: RelayInfo.cs * * * @@ -47,7 +47,9 @@ public readonly ref struct RelayInfo public string GetTextEntry(int entryId) { - var index = _textIds.IndexOf((ushort)entryId); - return index == -1 ? null : TextEncoding.GetStringBigUni(_textBlock[_textRanges[index]], true); + int index = _textIds.IndexOf((ushort)entryId); + return index == -1 + ? default + : TextEncoding.GetString(_textBlock[_textRanges[index]], TextEncoding.Unicode, true); } } diff --git a/Projects/UOContent/Gumps/Base/StaticGump.cs b/Projects/UOContent/Gumps/Base/StaticGump.cs index 0ab2df5e0..c0f5eb920 100644 --- a/Projects/UOContent/Gumps/Base/StaticGump.cs +++ b/Projects/UOContent/Gumps/Base/StaticGump.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: StaticGump.cs * * * @@ -30,13 +30,9 @@ public abstract class StaticGump : BaseGump where TSelf : StaticGump true; - public override int Switches => _switches; public override int TextEntries => _textEntries; @@ -71,9 +67,8 @@ public abstract class StaticGump : BaseGump where TSelf : StaticGump : BaseGump where TSelf : StaticGump.Shared.Rent(stringsLength); + var buffer = STArrayPool.Shared.Rent(_staticStrings.Length + stringsData.Length); _staticStrings.CopyTo(buffer.AsSpan()); stringsData.CopyTo(buffer.AsSpan(_staticStrings.Length)); - OutgoingGumpPackets.WritePacked(buffer.AsSpan(0, stringsLength), ref writer); + OutgoingGumpPackets.WritePacked(buffer, ref writer); STArrayPool.Shared.Return(buffer); } } @@ -114,11 +107,10 @@ public abstract class StaticGump : BaseGump where TSelf : StaticGump _gumpBuilder._switches; public int TextEntries => _gumpBuilder._textEntries; - internal bool HasVisualElements => _gumpBuilder._hasVisualElements; [MethodImpl(MethodImplOptions.AggressiveInlining)] public StaticGumpBuilder() @@ -92,7 +91,13 @@ public ref struct StaticGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtmlPlaceholder( - int x, int y, int width, int height, ReadOnlySpan slotKey, bool background = false, bool scrollbar = false + int x, + int y, + int width, + int height, + ReadOnlySpan slotKey, + bool background = false, + bool scrollbar = false ) { var index = _gumpBuilder.AddHtmlPlaceholder(x, y, width, height, background, scrollbar); @@ -101,7 +106,12 @@ public ref struct StaticGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtmlPlaceholder( - int x, int y, int width, int height, ref RawInterpolatedStringHandler handler, bool background = false, + int x, + int y, + int width, + int height, + ref RawInterpolatedStringHandler handler, + bool background = false, bool scrollbar = false ) { @@ -111,33 +121,115 @@ public ref struct StaticGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtml( - int x, int y, int width, int height, ReadOnlySpan text, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, - TextAlignment align = TextAlignment.Left, bool background = false, bool scrollbar = false + int x, + int y, + int width, + int height, + ReadOnlySpan text, + bool background = false, + bool scrollbar = false ) { - if (color.Length > 0 || size != -1 || fontStyle != 0 || align != TextAlignment.Left) - { - var arr = STArrayPool.Shared.Rent(Html.BuildCharCount(text, color)); - var bytesWritten = Html.Build(text, arr.AsSpan(), color, size, fontStyle, align); - - WriteInternalizedString(arr.AsSpan(0, bytesWritten)); - STArrayPool.Shared.Return(arr); - } - else - { - WriteInternalizedString(text); - } - + WriteInternalizedString(text); _gumpBuilder.AddHtml(x, y, width, height, _stringsCount++, background, scrollbar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtml( - int x, int y, int width, int height, ref RawInterpolatedStringHandler handler, ReadOnlySpan color = default, int size = -1, - byte fontStyle = 0, TextAlignment align = TextAlignment.Left, bool background = false, bool scrollbar = false + int x, int y, int width, int height, ref RawInterpolatedStringHandler handler, + bool background = false, bool scrollbar = false ) { - AddHtml(x, y, width, height, handler.Text, color, size, fontStyle, align, background, scrollbar); + AddHtml(x, y, width, height, handler.Text, background, scrollbar); + handler.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtml( + int x, + int y, + int width, + int height, + int color, + ReadOnlySpan text, + bool background = false, + bool scrollbar = false + ) + { + var handler = text.Color(color); + AddHtml(x, y, width, height, ref handler, background, scrollbar); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtml( + int x, + int y, + int width, + int height, + int color, + ref RawInterpolatedStringHandler handler, + bool background = false, + bool scrollbar = false + ) + { + AddHtml(x, y, width, height, color, handler.Text, background, scrollbar); + handler.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtmlCentered( + int x, int y, int width, int height, ReadOnlySpan text, bool background = false, bool scrollbar = false + ) + { + var handler = text.Center(); + AddHtml(x, y, width, height, ref handler, background, scrollbar); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtmlCentered( + int x, + int y, + int width, + int height, + ref RawInterpolatedStringHandler handler, + bool background = false, + bool scrollbar = false + ) + { + var centerHandler = handler.Text.Center(); + AddHtml(x, y, width, height, ref centerHandler, background, scrollbar); + handler.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtmlCentered( + int x, + int y, + int width, + int height, + int color, + ReadOnlySpan text, + bool background = false, + bool scrollbar = false + ) + { + var handler = text.Center(color); + AddHtml(x, y, width, height, ref handler, background, scrollbar); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddHtmlCentered( + int x, + int y, + int width, + int height, + int color, + ref RawInterpolatedStringHandler handler, + bool background = false, + bool scrollbar = false + ) + { + AddHtmlCentered(x, y, width, height, color, handler.Text, background, scrollbar); handler.Clear(); } @@ -220,7 +312,7 @@ public ref struct StaticGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddLabelPlaceholder(int x, int y, int hue, ref RawInterpolatedStringHandler handler) { - AddLabelPlaceholder(x, y, hue, handler.Text); + AddHtmlPlaceholder(x, y, 0, 0, handler.Text); handler.Clear(); } @@ -390,11 +482,11 @@ public ref struct StaticGumpBuilder } var newSize = Math.Max(_stringBytesWritten + needed, _stringsBuffer.Length * 2); - var poolArray = STArrayPool.Shared.Rent(newSize); + byte[] poolArray = STArrayPool.Shared.Rent(newSize); _stringsBuffer.AsSpan(0, _stringBytesWritten).CopyTo(poolArray); - var toReturn = _stringsBuffer; + byte[] toReturn = _stringsBuffer; _stringsBuffer = poolArray; if (toReturn != _staticStringsBuffer) diff --git a/Projects/UOContent/Gumps/ClientGump.cs b/Projects/UOContent/Gumps/ClientGump.cs index d2974f6cf..6fdd0a84e 100644 --- a/Projects/UOContent/Gumps/ClientGump.cs +++ b/Projects/UOContent/Gumps/ClientGump.cs @@ -36,7 +36,7 @@ namespace Server.Gumps var line = 0; AddHtml(14, 36 + line * 20, 200, 20, "Address:".Color(LabelColor32)); - AddHtml(70, 36 + line++ * 20, 200, 20, Html.Color($"{state}", LabelColor32)); + AddHtml(70, 36 + line++ * 20, 200, 20, state.ToString().Color(LabelColor32)); AddHtml(14, 36 + line * 20, 200, 20, "Client:".Color(LabelColor32)); AddHtml( @@ -75,10 +75,10 @@ namespace Server.Gumps if (m != null) { AddHtml(14, 36 + line * 20, 200, 20, "Mobile:".Color(LabelColor32)); - AddHtml(72, 36 + line++ * 20, 200, 20, Html.Color($"{m.Name} ({m.Serial})", LabelColor32)); + AddHtml(72, 36 + line++ * 20, 200, 20, $"{m.Name} ({m.Serial})".Color(LabelColor32)); AddHtml(14, 36 + line * 20, 200, 20, "Location:".Color(LabelColor32)); - AddHtml(72, 36 + line++ * 20, 200, 20, Html.Color($"{m.Location} [{m.Map}]", LabelColor32)); + AddHtml(72, 36 + line++ * 20, 200, 20, $"{m.Location} [{m.Map}]".Color(LabelColor32)); AddButton(13, 197, 0xFAB, 0xFAD, 1); AddHtml(48, 198, 200, 20, "Send Message".Color(LabelColor32)); diff --git a/Projects/UOContent/Gumps/CommentsGump.cs b/Projects/UOContent/Gumps/CommentsGump.cs index ac86559bb..73fb4d44d 100644 --- a/Projects/UOContent/Gumps/CommentsGump.cs +++ b/Projects/UOContent/Gumps/CommentsGump.cs @@ -5,44 +5,32 @@ using Server.Targeting; namespace Server.Gumps { - public class CommentsGump : DynamicGump + public class CommentsGump : Gump { - private readonly Account _acct; + private readonly Account m_Acct; - public override bool Singleton => true; - - private CommentsGump(Account acct) : base(30, 30) => _acct = acct; - - public static void DisplayTo(Mobile from, Account acct) + public CommentsGump(Account acct) : base(30, 30) { - if (from?.NetState == null || acct == null) - { - return; - } + m_Acct = acct; - from.SendGump(new CommentsGump(acct)); - } + AddPage(0); + AddImageTiled(0, 0, 410, 448, 0xA40); + AddAlphaRegion(1, 1, 408, 446); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(0); - builder.AddImageTiled(0, 0, 410, 448, 0xA40); - builder.AddAlphaRegion(1, 1, 408, 446); - - var title = $"Comments for '{_acct.Username}'"; + var title = $"Comments for '{acct.Username}'"; var x = 205 - title.Length / 2 * 7; if (x < 120) { x = 120; } - builder.AddLabel(x, 12, 2100, title); + AddLabel(x, 12, 2100, title); - builder.AddPage(1); - builder.AddButton(12, 12, 0xFA8, 0xFAA, 0x7F); - builder.AddLabel(48, 12, 2100, "Add Comment"); + AddPage(1); + AddButton(12, 12, 0xFA8, 0xFAA, 0x7F); + AddLabel(48, 12, 2100, "Add Comment"); - var list = _acct.Comments; + var list = acct.Comments; if (list.Count > 0) { for (var i = 0; i < list.Count; ++i) @@ -51,21 +39,21 @@ namespace Server.Gumps if (i >= 5 && i % 5 == 0) { - builder.AddButton(368, 12, 0xFA5, 0xFA7, 0, GumpButtonType.Page, i / 5 + 1); - builder.AddLabel(298, 12, 2100, "Next Page"); - builder.AddPage(i / 5 + 1); - builder.AddButton(12, 12, 0xFAE, 0xFB0, 0, GumpButtonType.Page, i / 5); - builder.AddLabel(48, 12, 2100, "Prev Page"); + AddButton(368, 12, 0xFA5, 0xFA7, 0, GumpButtonType.Page, i / 5 + 1); + AddLabel(298, 12, 2100, "Next Page"); + AddPage(i / 5 + 1); + AddButton(12, 12, 0xFAE, 0xFB0, 0, GumpButtonType.Page, i / 5); + AddLabel(48, 12, 2100, "Prev Page"); } var html = $"[Added By: {comment.AddedBy} on {comment.LastModified.ToString("H:mm M/d/yy")}]
{comment.Content}"; - builder.AddHtml(12, 44 + i % 5 * 80, 386, 70, html, background: true, scrollbar: true); + AddHtml(12, 44 + i % 5 * 80, 386, 70, html, true, true); } } else { - builder.AddLabel(12, 44, 2100, "There are no comments for this account."); + AddLabel(12, 44, 2100, "There are no comments for this account."); } } @@ -95,7 +83,7 @@ namespace Server.Gumps } else { - DisplayTo(from, (Account)m.Account); + from.SendGump(new CommentsGump((Account)m.Account)); } } @@ -104,19 +92,19 @@ namespace Server.Gumps if (info.ButtonID == 0x7F) { state.Mobile.SendMessage("Enter the text for the account comment (or press [Esc] to cancel):"); - state.Mobile.Prompt = new CommentPrompt(_acct); + state.Mobile.Prompt = new CommentPrompt(m_Acct); } } public class CommentPrompt : Prompt { - private readonly Account _acct; + private readonly Account m_Acct; - public CommentPrompt(Account acct) => _acct = acct; + public CommentPrompt(Account acct) => m_Acct = acct; public override void OnCancel(Mobile from) { - DisplayTo(from, _acct); + from.SendGump(new CommentsGump(m_Acct), true); base.OnCancel(from); } @@ -124,8 +112,9 @@ namespace Server.Gumps { base.OnResponse(from, text); from.SendMessage("Comment added."); - _acct.Comments.Add(new AccountComment(from.Name, text)); - DisplayTo(from, _acct); + // m_Acct.AddComment( from.Name, text ); + m_Acct.Comments.Add(new AccountComment(from.Name, text)); + from.SendGump(new CommentsGump(m_Acct), true); } } } diff --git a/Projects/UOContent/Gumps/ConfirmHeritageGump.cs b/Projects/UOContent/Gumps/ConfirmHeritageGump.cs index c120924b7..470168e56 100644 --- a/Projects/UOContent/Gumps/ConfirmHeritageGump.cs +++ b/Projects/UOContent/Gumps/ConfirmHeritageGump.cs @@ -1,55 +1,37 @@ using System; using Server.Items; -using Server.Logging; using Server.Network; namespace Server.Gumps { - public class ConfirmHeritageGump : DynamicGump + public class ConfirmHeritageGump : Gump { - private static readonly ILogger _logger = LogFactory.GetLogger(typeof(ConfirmHeritageGump)); - - private readonly Type[] _selected; - private readonly HeritageToken _token; - private readonly int _cliloc; + private readonly Type[] m_Selected; + private readonly HeritageToken m_Token; public override bool Singleton => true; - private ConfirmHeritageGump(HeritageToken token, Type[] selected, int cliloc) : base(60, 36) + public ConfirmHeritageGump(HeritageToken token, Type[] selected, int cliloc) : base(60, 36) { - _token = token; - _selected = selected; - _cliloc = cliloc; - } + m_Token = token; + m_Selected = selected; - public static void DisplayTo(Mobile from, HeritageToken token, Type[] selected, int cliloc) - { - if (from?.NetState == null || token?.Deleted != false || selected == null || selected.Length == 0) - { - return; - } + AddPage(0); - from.SendGump(new ConfirmHeritageGump(token, selected, cliloc)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 291, 99, 0x13BE); - builder.AddImageTiled(5, 6, 280, 20, 0xA40); - builder.AddHtmlLocalized(9, 8, 280, 20, 1070972, 0x7FFF); // Click "OKAY" to redeem the following promotional item: - builder.AddImageTiled(5, 31, 280, 40, 0xA40); - builder.AddHtmlLocalized(9, 35, 272, 40, _cliloc, 0x7FFF); - builder.AddButton(180, 73, 0xFB7, 0xFB8, (int)Buttons.Okay); - builder.AddHtmlLocalized(215, 75, 100, 20, 1011036, 0x7FFF); // OKAY - builder.AddButton(5, 73, 0xFB1, 0xFB2, (int)Buttons.Cancel); - builder.AddHtmlLocalized(40, 75, 100, 20, 1060051, 0x7FFF); // CANCEL + AddBackground(0, 0, 291, 99, 0x13BE); + AddImageTiled(5, 6, 280, 20, 0xA40); + AddHtmlLocalized(9, 8, 280, 20, 1070972, 0x7FFF); // Click "OKAY" to redeem the following promotional item: + AddImageTiled(5, 31, 280, 40, 0xA40); + AddHtmlLocalized(9, 35, 272, 40, cliloc, 0x7FFF); + AddButton(180, 73, 0xFB7, 0xFB8, (int)Buttons.Okay); + AddHtmlLocalized(215, 75, 100, 20, 1011036, 0x7FFF); // OKAY + AddButton(5, 73, 0xFB1, 0xFB2, (int)Buttons.Cancel); + AddHtmlLocalized(40, 75, 100, 20, 1060051, 0x7FFF); // CANCEL } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_token?.Deleted != false) + if (m_Token?.Deleted != false) { return; } @@ -57,35 +39,32 @@ namespace Server.Gumps switch (info.ButtonID) { case (int)Buttons.Okay: + + Item item = null; + + foreach (var type in m_Selected) { - Item item = null; - - foreach (var type in _selected) + try { - try - { - item = type.CreateInstance(); - } - catch (Exception ex) - { - _logger.Error(ex, "Failed to create heritage item of type {Type}", type); - } - - if (item != null) - { - _token.Delete(); - sender.Mobile.AddToBackpack(item); - } + item = type.CreateInstance(); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + Console.WriteLine(ex.StackTrace); } - break; + if (item != null) + { + m_Token.Delete(); + sender.Mobile.AddToBackpack(item); + } } + break; case (int)Buttons.Cancel: - { - HeritageTokenGump.DisplayTo(sender.Mobile, _token); - break; - } + sender.Mobile.SendGump(new HeritageTokenGump(m_Token)); + break; } } diff --git a/Projects/UOContent/Gumps/Go/GoGump.cs b/Projects/UOContent/Gumps/Go/GoGump.cs index e33d441cb..cacc8619c 100644 --- a/Projects/UOContent/Gumps/Go/GoGump.cs +++ b/Projects/UOContent/Gumps/Go/GoGump.cs @@ -5,15 +5,23 @@ using static Server.Gumps.PropsConfig; namespace Server.Gumps; -public class GoGump : DynamicGump +public class GoGump : Gump { + private const int PrevLabelOffsetX = PrevWidth + 1; + private const int PrevLabelOffsetY = 0; + + private const int NextLabelOffsetX = -29; + private const int NextLabelOffsetY = 0; + private const int EntryWidth = 180; private const int EntryCount = 15; private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; - private GoCategory _node; - private int _page; + private const int BackWidth = BorderSize + TotalWidth + BorderSize; + + private readonly GoCategory _node; + private readonly int _page; private readonly LocationTree _tree; @@ -33,61 +41,137 @@ public class GoGump : DynamicGump _page = page; _tree = tree; _node = node; + + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; + + var count = Math.Clamp(node.Categories.Length + node.Locations.Length - page * EntryCount, 0, EntryCount); + + var totalHeight = OffsetSize + (EntryHeight + OffsetSize) * (count + 1); + + AddPage(0); + + AddBackground(0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + totalHeight, + OffsetGumpID + ); + + if (OldStyle) + { + AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID); + } + else + { + AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID); + } + + if (node.Parent != null) + { + AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1); + + if (PrevLabel) + { + AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous"); + } + } + + x += PrevWidth + OffsetSize; + + var emptyWidth = TotalWidth - PrevWidth * 2 - NextWidth - OffsetSize * 5 - + (OldStyle ? SetWidth + OffsetSize : 0); + + if (!OldStyle) + { + AddImageTiled( + x - (OldStyle ? OffsetSize : 0), + y, + emptyWidth + (OldStyle ? OffsetSize * 2 : 0), + EntryHeight, + EntryGumpID + ); + } + + AddHtml(x + TextOffsetX, y, emptyWidth - TextOffsetX, EntryHeight, $"
{node.Name}
"); + + x += emptyWidth + OffsetSize; + + if (OldStyle) + { + AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID); + } + else + { + AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID); + } + + if (page > 0) + { + AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 2); + + if (PrevLabel) + { + AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous"); + } + } + + x += PrevWidth + OffsetSize; + + if (!OldStyle) + { + AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID); + } + + if ((page + 1) * EntryCount < node.Categories.Length + node.Locations.Length) + { + AddButton(x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 3, GumpButtonType.Reply, 1); + + if (NextLabel) + { + AddLabel(x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next"); + } + } + + var totalEntryCount = node.Categories.Length + node.Locations.Length; + + for (int i = 0, index = page * EntryCount; i < EntryCount && index < totalEntryCount; ++i, ++index) + { + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + var name = index >= node.Categories.Length + ? node.Locations[index - node.Categories.Length].Name + : node.Categories[index].Name; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, name); + + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, index + 4); + } } public static void DisplayTo(Mobile from) { - if (from?.NetState == null) - { - return; - } - - var tree = GoLocations.GetLocations(from.Map); - - if (tree == null) - { - return; - } + LocationTree tree = GoLocations.GetLocations(from.Map); if (!tree.LastBranch.TryGetValue(from, out var branch)) { branch = tree.Root; } - if (branch == null) + if (branch != null) { - return; - } - - from.SendGump(new GoGump(0, from, tree, branch)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var count = Math.Clamp(_node.Categories.Length + _node.Locations.Length - _page * EntryCount, 0, EntryCount); - - builder.AddPage(); - - builder.AddPropsFrame(TotalWidth, count + 1, out var x, out var y); - builder.AddPropsHeaderWithBack( - TotalWidth, ref x, ref y, _node.Name, - _node.Parent != null, 1, - _page > 0, 2, - (_page + 1) * EntryCount < _node.Categories.Length + _node.Locations.Length, 3, - nextType: GumpButtonType.Reply, nextParam: 1 - ); - - var totalEntryCount = _node.Categories.Length + _node.Locations.Length; - - for (int i = 0, index = _page * EntryCount; i < EntryCount && index < totalEntryCount; ++i, ++index) - { - PropsLayout.NextRow(ref x, ref y); - - var name = index >= _node.Categories.Length - ? _node.Locations[index - _node.Categories.Length].Name - : _node.Categories[index].Name; - - builder.AddPropsEntryButton(ref x, ref y, EntryWidth, name, true, index + 4); + from.SendGump(new GoGump(0, from, tree, branch)); } } @@ -101,19 +185,7 @@ public class GoGump : DynamicGump { if (_node.Parent != null) { - _page = 0; - _node = _node.Parent; - - if (_node == _tree.Root) - { - _tree.LastBranch.Remove(from); - } - else - { - _tree.LastBranch[from] = _node; - } - - from.SendGump(this); + from.SendGump(new GoGump(0, from, _tree, _node.Parent)); } break; @@ -122,8 +194,7 @@ public class GoGump : DynamicGump { if (_page > 0) { - _page--; - from.SendGump(this); + from.SendGump(new GoGump(_page - 1, from, _tree, _node)); } break; @@ -132,8 +203,7 @@ public class GoGump : DynamicGump { if ((_page + 1) * EntryCount < _node.Categories.Length + _node.Locations.Length) { - _page++; - from.SendGump(this); + from.SendGump(new GoGump(_page + 1, from, _tree, _node)); } break; @@ -149,10 +219,7 @@ public class GoGump : DynamicGump if (index < _node.Categories.Length) { - _page = 0; - _node = _node.Categories[index]; - _tree.LastBranch[from] = _node; - from.SendGump(this); + from.SendGump(new GoGump(0, from, _tree, _node.Categories[index])); } else { diff --git a/Projects/UOContent/Gumps/Guilds/DeclareFealtyGump.cs b/Projects/UOContent/Gumps/Guilds/DeclareFealtyGump.cs index c9ae0de8e..249027dcf 100644 --- a/Projects/UOContent/Gumps/Guilds/DeclareFealtyGump.cs +++ b/Projects/UOContent/Gumps/Guilds/DeclareFealtyGump.cs @@ -5,36 +5,24 @@ namespace Server.Gumps { public class DeclareFealtyGump : GuildMobileListGump { - private DeclareFealtyGump(Guild guild) : base(guild, true, guild.Members) + public DeclareFealtyGump(Mobile from, Guild guild) : base(from, guild, true, guild.Members) { } - public static void DisplayTo(Mobile from, Guild guild) + protected override void Design() { - if (from?.NetState == null || guild == null) - { - return; - } + AddHtmlLocalized(20, 10, 400, 35, 1011097); // Declare your fealty - GuildGump.EnsureClosed(from); - from.SendGump(new DeclareFealtyGump(guild)); - } + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 250, 35, 1011098); // I have selected my new lord. - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtmlLocalized(20, 10, 400, 35, 1011097); // Declare your fealty - - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 250, 35, 1011098); // I have selected my new lord. - - builder.AddButton(300, 400, 4005, 4007, 0); - builder.AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL + AddButton(300, 400, 4005, 4007, 0); + AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (info.ButtonID == 0 || GuildGump.BadMember(from, _guild)) + if (GuildGump.BadMember(m_Mobile, m_Guild)) { return; } @@ -47,9 +35,9 @@ namespace Server.Gumps { var index = switches[0]; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - var m = _list[index]; + var m = m_List[index]; if (m?.Deleted == false) { @@ -59,7 +47,8 @@ namespace Server.Gumps } } - GuildGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GrantGuildTitleGump.cs b/Projects/UOContent/Gumps/Guilds/GrantGuildTitleGump.cs index 9f79cf77e..063d1fec1 100644 --- a/Projects/UOContent/Gumps/Guilds/GrantGuildTitleGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GrantGuildTitleGump.cs @@ -5,36 +5,24 @@ namespace Server.Gumps { public class GrantGuildTitleGump : GuildMobileListGump { - private GrantGuildTitleGump(Guild guild) : base(guild, true, guild.Members) + public GrantGuildTitleGump(Mobile from, Guild guild) : base(from, guild, true, guild.Members) { } - public static void DisplayTo(Mobile from, Guild guild) + protected override void Design() { - if (from?.NetState == null || guild == null) - { - return; - } + AddHtmlLocalized(20, 10, 400, 35, 1011118); // Grant a title to another member. - GuildGump.EnsureClosed(from); - from.SendGump(new GrantGuildTitleGump(guild)); - } + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 245, 30, 1011127); // I dub thee... - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtmlLocalized(20, 10, 400, 35, 1011118); // Grant a title to another member. - - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 245, 30, 1011127); // I dub thee... - - builder.AddButton(300, 400, 4005, 4007, 2); - builder.AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL + AddButton(300, 400, 4005, 4007, 2); + AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (info.ButtonID == 0 || GuildGump.BadMember(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -47,21 +35,22 @@ namespace Server.Gumps { var index = switches[0]; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - var m = _list[index]; + var m = m_List[index]; if (m?.Deleted == false) { - from.SendLocalizedMessage(1013074); // New title (20 characters max): - from.Prompt = new GuildTitlePrompt(m, _guild); + m_Mobile.SendLocalizedMessage(1013074); // New title (20 characters max): + m_Mobile.Prompt = new GuildTitlePrompt(m_Mobile, m, m_Guild); } } } } else if (info.ButtonID == 2) { - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildAbbrvPrompt.cs b/Projects/UOContent/Gumps/Guilds/GuildAbbrvPrompt.cs index d84de490b..de64beef1 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildAbbrvPrompt.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildAbbrvPrompt.cs @@ -1,4 +1,3 @@ -using System; using Server.Guilds; using Server.Prompts; @@ -6,49 +5,55 @@ namespace Server.Gumps { public class GuildAbbrvPrompt : Prompt { - private readonly Guild _guild; + private readonly Guild m_Guild; + private readonly Mobile m_Mobile; - public GuildAbbrvPrompt(Guild g) => _guild = g; + public GuildAbbrvPrompt(Mobile m, Guild g) + { + m_Mobile = m; + m_Guild = g; + } public override void OnCancel(Mobile from) { - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } public override void OnResponse(Mobile from, string text) { - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } - var textSpan = text.AsSpan().Trim(); + text = text.Trim(); - if (textSpan.Length > 3) + if (text.Length > 3) { - textSpan = textSpan[..3]; + text = text[..3]; } - if (textSpan.Length > 0) + if (text.Length > 0) { - if (BaseGuild.FindByAbbrev(textSpan) != null) + if (BaseGuild.FindByAbbrev(text) != null) { - from.SendMessage($"{textSpan} conflicts with the abbreviation of an existing guild."); + m_Mobile.SendMessage($"{text} conflicts with the abbreviation of an existing guild."); } else { - text = textSpan.ToString(); - _guild.Abbreviation = text; - _guild.GuildMessage(1018025, true, text); // Your guild abbreviation has changed: + m_Guild.Abbreviation = text; + m_Guild.GuildMessage(1018025, true, text); // Your guild abbreviation has changed: } } - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildAcceptWarGump.cs b/Projects/UOContent/Gumps/Guilds/GuildAcceptWarGump.cs index 47defcf13..a32c1543a 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildAcceptWarGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildAcceptWarGump.cs @@ -5,36 +5,24 @@ namespace Server.Gumps { public class GuildAcceptWarGump : GuildListGump { - private GuildAcceptWarGump(Guild guild) : base(guild, true, guild.WarInvitations) + public GuildAcceptWarGump(Mobile from, Guild guild) : base(from, guild, true, guild.WarInvitations) { } - public static void DisplayTo(Mobile from, Guild guild) + protected override void Design() { - if (from?.NetState == null || guild == null) - { - return; - } + AddHtmlLocalized(20, 10, 400, 35, 1011147); // Select the guild to accept the invitations: - GuildGump.EnsureClosed(from); - from.SendGump(new GuildAcceptWarGump(guild)); - } + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 245, 30, 1011100); // Accept war invitations. - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtmlLocalized(20, 10, 400, 35, 1011147); // Select the guild to accept the invitations: - - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 245, 30, 1011100); // Accept war invitations. - - builder.AddButton(300, 400, 4005, 4007, 2); - builder.AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL + AddButton(300, 400, 4005, 4007, 2); + AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -47,25 +35,27 @@ namespace Server.Gumps { var index = switches[0]; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - var g = _list[index]; + var g = m_List[index]; if (g != null) { - _guild.WarInvitations.Remove(g); - g.WarDeclarations.Remove(_guild); + m_Guild.WarInvitations.Remove(g); + g.WarDeclarations.Remove(m_Guild); - _guild.AddEnemy(g); - _guild.GuildMessage(1018020, true, $"{g.Name} ({g.Abbreviation})"); + m_Guild.AddEnemy(g); + m_Guild.GuildMessage(1018020, true, $"{g.Name} ({g.Abbreviation})"); - if (_guild.WarInvitations.Count > 0) + GuildGump.EnsureClosed(m_Mobile); + + if (m_Guild.WarInvitations.Count > 0) { - DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildAcceptWarGump(m_Mobile, m_Guild)); } else { - GuildmasterGump.DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } @@ -73,7 +63,8 @@ namespace Server.Gumps } else if (info.ButtonID == 2) { - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildAdminCandidatesGump.cs b/Projects/UOContent/Gumps/Guilds/GuildAdminCandidatesGump.cs index ada0dff6f..f9816e2a3 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildAdminCandidatesGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildAdminCandidatesGump.cs @@ -6,36 +6,24 @@ namespace Server.Gumps { public class GuildAdminCandidatesGump : GuildMobileListGump { - private GuildAdminCandidatesGump(Guild guild) : base(guild, true, guild.Candidates) + public GuildAdminCandidatesGump(Mobile from, Guild guild) : base(from, guild, true, guild.Candidates) { } - public static void DisplayTo(Mobile from, Guild guild) + protected override void Design() { - if (from?.NetState == null || guild == null) - { - return; - } + AddHtmlLocalized(20, 10, 400, 35, 1013075); // Accept or Refuse candidates for membership - GuildGump.EnsureClosed(from); - from.SendGump(new GuildAdminCandidatesGump(guild)); - } + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 245, 30, 1013076); // Accept - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtmlLocalized(20, 10, 400, 35, 1013075); // Accept or Refuse candidates for membership - - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 245, 30, 1013076); // Accept - - builder.AddButton(300, 400, 4005, 4007, 2); - builder.AddHtmlLocalized(335, 400, 100, 35, 1013077); // Refuse + AddButton(300, 400, 4005, 4007, 2); + AddHtmlLocalized(335, 400, 100, 35, 1013077); // Refuse } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -44,7 +32,9 @@ namespace Server.Gumps { case 0: { - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); + break; } case 1: // Accept @@ -55,13 +45,13 @@ namespace Server.Gumps { var index = switches[0]; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - var m = _list[index]; + var m = m_List[index]; if (m?.Deleted == false) { - var guildState = PlayerState.Find(_guild.Leader); + var guildState = PlayerState.Find(m_Guild.Leader); var targetState = PlayerState.Find(m); var guildFaction = guildState?.Faction; @@ -71,18 +61,21 @@ namespace Server.Gumps { if (guildFaction == null) { - // That player cannot join a non-faction guild. - from.SendLocalizedMessage(1013027); + m_Mobile.SendLocalizedMessage( + 1013027 + ); // That player cannot join a non-faction guild. } else if (targetFaction == null) { - // That player must be in a faction before joining this guild. - from.SendLocalizedMessage(1013026); + m_Mobile.SendLocalizedMessage( + 1013026 + ); // That player must be in a faction before joining this guild. } else { - // That person has a different faction affiliation. - from.SendLocalizedMessage(1013028); + m_Mobile.SendLocalizedMessage( + 1013028 + ); // That person has a different faction affiliation. } break; @@ -91,22 +84,24 @@ namespace Server.Gumps if (targetState?.IsLeaving == true) { // OSI does this quite strangely, so we'll just do it this way - from.SendMessage( + m_Mobile.SendMessage( "That person is quitting their faction and so you may not recruit them." ); break; } - _guild.Candidates.Remove(m); - _guild.Accepted.Add(m); + m_Guild.Candidates.Remove(m); + m_Guild.Accepted.Add(m); - if (_guild.Candidates.Count > 0) + GuildGump.EnsureClosed(m_Mobile); + + if (m_Guild.Candidates.Count > 0) { - DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildAdminCandidatesGump(m_Mobile, m_Guild)); } else { - GuildmasterGump.DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } @@ -122,21 +117,23 @@ namespace Server.Gumps { var index = switches[0]; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - var m = _list[index]; + var m = m_List[index]; if (m?.Deleted == false) { - _guild.Candidates.Remove(m); + m_Guild.Candidates.Remove(m); - if (_guild.Candidates.Count > 0) + GuildGump.EnsureClosed(m_Mobile); + + if (m_Guild.Candidates.Count > 0) { - DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildAdminCandidatesGump(m_Mobile, m_Guild)); } else { - GuildmasterGump.DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildCandidatesGump.cs b/Projects/UOContent/Gumps/Guilds/GuildCandidatesGump.cs index 7a5221e87..5d475239e 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildCandidatesGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildCandidatesGump.cs @@ -5,40 +5,29 @@ namespace Server.Gumps { public class GuildCandidatesGump : GuildMobileListGump { - private GuildCandidatesGump(Guild guild) : base(guild, false, guild.Candidates) + public GuildCandidatesGump(Mobile from, Guild guild) : base(from, guild, false, guild.Candidates) { } - public static void DisplayTo(Mobile from, Guild guild) + protected override void Design() { - if (from?.NetState == null || guild == null) - { - return; - } + AddHtmlLocalized(20, 10, 500, 35, 1013030); //
Candidates
- GuildGump.EnsureClosed(from); - from.SendGump(new GuildCandidatesGump(guild)); - } - - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtmlLocalized(20, 10, 500, 35, 1013030); //
Candidates
- - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 300, 35, 1011120); // Return to the main menu. + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 300, 35, 1011120); // Return to the main menu. } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadMember(from, _guild)) + if (GuildGump.BadMember(m_Mobile, m_Guild)) { return; } if (info.ButtonID == 1) { - GuildGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildChangeTypeGump.cs b/Projects/UOContent/Gumps/Guilds/GuildChangeTypeGump.cs index 4e876a80a..24d8b5883 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildChangeTypeGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildChangeTypeGump.cs @@ -5,57 +5,45 @@ using Server.Network; namespace Server.Gumps { - public class GuildChangeTypeGump : StaticGump + public class GuildChangeTypeGump : Gump { - private readonly Guild _guild; + private readonly Guild m_Guild; + private readonly Mobile m_Mobile; - public override bool Singleton => true; - - private GuildChangeTypeGump(Guild guild) : base(20, 30) => _guild = guild; - - public static void DisplayTo(Mobile from, Guild guild) + public GuildChangeTypeGump(Mobile from, Guild guild) : base(20, 30) { - if (from?.NetState == null || guild == null) - { - return; - } + m_Mobile = from; + m_Guild = guild; - GuildGump.EnsureClosed(from); - from.SendGump(new GuildChangeTypeGump(guild)); - } + Draggable = false; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.SetNoMove(); + AddPage(0); + AddBackground(0, 0, 550, 400, 5054); + AddBackground(10, 10, 530, 380, 3000); - builder.AddPage(); - builder.AddBackground(0, 0, 550, 400, 5054); - builder.AddBackground(10, 10, 530, 380, 3000); + AddHtmlLocalized(20, 15, 510, 30, 1013062); //
Change Guild Type Menu
- builder.AddHtmlLocalized(20, 15, 510, 30, 1013062); //
Change Guild Type Menu
+ AddHtmlLocalized(50, 50, 450, 30, 1013066); // Please select the type of guild you would like to change to - builder.AddHtmlLocalized(50, 50, 450, 30, 1013066); // Please select the type of guild you would like to change to + AddButton(20, 100, 4005, 4007, 1); + AddHtmlLocalized(85, 100, 300, 30, 1013063); // Standard guild - builder.AddButton(20, 100, 4005, 4007, 1); - builder.AddHtmlLocalized(85, 100, 300, 30, 1013063); // Standard guild + AddButton(20, 150, 4005, 4007, 2); + AddItem(50, 143, 7109); + AddHtmlLocalized(85, 150, 300, 300, 1013064); // Order guild - builder.AddButton(20, 150, 4005, 4007, 2); - builder.AddItem(50, 143, 7109); - builder.AddHtmlLocalized(85, 150, 300, 300, 1013064); // Order guild + AddButton(20, 200, 4005, 4007, 3); + AddItem(45, 200, 7107); + AddHtmlLocalized(85, 200, 300, 300, 1013065); // Chaos guild - builder.AddButton(20, 200, 4005, 4007, 3); - builder.AddItem(45, 200, 7107); - builder.AddHtmlLocalized(85, 200, 300, 300, 1013065); // Chaos guild - - builder.AddButton(300, 360, 4005, 4007, 4); - builder.AddHtmlLocalized(335, 360, 150, 30, 1011012); // CANCEL + AddButton(300, 360, 4005, 4007, 4); + AddHtmlLocalized(335, 360, 150, 30, 1011012); // CANCEL } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (Guild.NewGuildSystem && !BaseGuildGump.IsLeader(from, _guild) || - !Guild.NewGuildSystem && GuildGump.BadLeader(from, _guild)) + if (Guild.NewGuildSystem && !BaseGuildGump.IsLeader(m_Mobile, m_Guild) || + !Guild.NewGuildSystem && GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -65,40 +53,41 @@ namespace Server.Gumps 1 => GuildType.Regular, 2 => GuildType.Order, 3 => GuildType.Chaos, - _ => _guild.Type + _ => m_Guild.Type }; - if (_guild.Type != newType) + if (m_Guild.Type != newType) { - var pl = PlayerState.Find(from); + var pl = PlayerState.Find(m_Mobile); if (pl != null) { - from.SendLocalizedMessage(1010405); // You cannot change guild types while in a Faction! + m_Mobile.SendLocalizedMessage(1010405); // You cannot change guild types while in a Faction! } - else if (_guild.TypeLastChange.AddDays(7) > Core.Now) + else if (m_Guild.TypeLastChange.AddDays(7) > Core.Now) { - from.SendLocalizedMessage(1011142); // You have already changed your guild type recently. + m_Mobile.SendLocalizedMessage(1011142); // You have already changed your guild type recently. // TODO: Clilocs 1011142-1011145 suggest a timer for pending changes } else { - _guild.Type = newType; - _guild.GuildMessage(1018022, true, newType.ToString()); // Guild Message: Your guild type has changed: + m_Guild.Type = newType; + m_Guild.GuildMessage(1018022, true, newType.ToString()); // Guild Message: Your guild type has changed: } } if (Guild.NewGuildSystem) { - if (from is PlayerMobile mobile) + if (m_Mobile is PlayerMobile mobile) { - mobile.SendGump(new GuildInfoGump(mobile, _guild)); + mobile.SendGump(new GuildInfoGump(mobile, m_Guild)); } return; } - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildCharterGump.cs b/Projects/UOContent/Gumps/Guilds/GuildCharterGump.cs index 61b35f777..ee197c41f 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildCharterGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildCharterGump.cs @@ -1,83 +1,79 @@ -using System; using Server.Guilds; using Server.Network; namespace Server.Gumps { - public class GuildCharterGump : DynamicGump + public class GuildCharterGump : Gump { private const string DefaultWebsite = "https://www.modernuo.com"; - private readonly Guild _guild; + private readonly Guild m_Guild; + private readonly Mobile m_Mobile; - public override bool Singleton => true; - - private GuildCharterGump(Guild guild) : base(20, 30) => _guild = guild; - - public static void DisplayTo(Mobile from, Guild guild) + public GuildCharterGump(Mobile from, Guild guild) : base(20, 30) { - if (from?.NetState == null || guild == null) - { - return; - } + m_Mobile = from; + m_Guild = guild; - GuildGump.EnsureClosed(from); - from.SendGump(new GuildCharterGump(guild)); - } + Draggable = false; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoMove(); + AddPage(0); + AddBackground(0, 0, 550, 400, 5054); + AddBackground(10, 10, 530, 380, 3000); - builder.AddPage(); - builder.AddBackground(0, 0, 550, 400, 5054); - builder.AddBackground(10, 10, 530, 380, 3000); - - builder.AddButton(20, 360, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 360, 300, 35, 1011120); // Return to the main menu. + AddButton(20, 360, 4005, 4007, 1); + AddHtmlLocalized(55, 360, 300, 35, 1011120); // Return to the main menu. string charter; - if ((charter = _guild.Charter) == null || (charter = charter.Trim()).Length <= 0) + if ((charter = guild.Charter) == null || (charter = charter.Trim()).Length <= 0) { - builder.AddHtmlLocalized(20, 20, 400, 35, 1013032); // No charter has been defined. + AddHtmlLocalized(20, 20, 400, 35, 1013032); // No charter has been defined. } else { - builder.AddHtml(20, 20, 510, 75, charter, background: true, scrollbar: true); + AddHtml(20, 20, 510, 75, charter, true, true); } - builder.AddButton(20, 200, 4005, 4007, 2); - builder.AddHtmlLocalized(55, 200, 300, 20, 1011122); // Visit the guild website : + AddButton(20, 200, 4005, 4007, 2); + AddHtmlLocalized(55, 200, 300, 20, 1011122); // Visit the guild website : - var website = _guild.Website != null ? _guild.Website.AsSpan().Trim() : ""; - if (website.Length <= 0) + string website; + + if ((website = guild.Website) == null || (website = website.Trim()).Length <= 0) { website = DefaultWebsite; } - builder.AddHtml(55, 220, 300, 20, website); + AddHtml(55, 220, 300, 20, website); } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (info.ButtonID == 0 || GuildGump.BadMember(from, _guild)) + if (GuildGump.BadMember(m_Mobile, m_Guild)) { return; } - if (info.ButtonID == 2) + switch (info.ButtonID) { - var website = _guild.Website != null ? _guild.Website.AsSpan().Trim() : ""; - if (website.Length <= 0) - { - website = DefaultWebsite; - } + case 0: return; // Close + case 1: break; // Return to main menu + case 2: + { + string website; - from.LaunchBrowser(website); + if ((website = m_Guild.Website) == null || (website = website.Trim()).Length <= 0) + { + website = DefaultWebsite; + } + + m_Mobile.LaunchBrowser(website); + break; + } } - GuildGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildCharterPrompt.cs b/Projects/UOContent/Gumps/Guilds/GuildCharterPrompt.cs index 7d06e58be..3c9f3d919 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildCharterPrompt.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildCharterPrompt.cs @@ -1,4 +1,3 @@ -using System; using Server.Guilds; using Server.Prompts; @@ -6,43 +5,50 @@ namespace Server.Gumps { public class GuildCharterPrompt : Prompt { - private readonly Guild _guild; + private readonly Guild m_Guild; + private readonly Mobile m_Mobile; - public GuildCharterPrompt(Guild g) => _guild = g; + public GuildCharterPrompt(Mobile m, Guild g) + { + m_Mobile = m; + m_Guild = g; + } public override void OnCancel(Mobile from) { - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } public override void OnResponse(Mobile from, string text) { - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } - var textSpan = text.AsSpan().Trim(); + text = text.Trim(); - if (textSpan.Length > 50) + if (text.Length > 50) { - textSpan = textSpan[..50]; + text = text[..50]; } - if (textSpan.Length > 0) + if (text.Length > 0) { - _guild.Charter = textSpan.ToString(); + m_Guild.Charter = text; } - from.SendLocalizedMessage(1013072); // Enter the new website for the guild (50 characters max): - from.Prompt = new GuildWebsitePrompt(from, _guild); + m_Mobile.SendLocalizedMessage(1013072); // Enter the new website for the guild (50 characters max): + m_Mobile.Prompt = new GuildWebsitePrompt(m_Mobile, m_Guild); - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildDeclarePeaceGump.cs b/Projects/UOContent/Gumps/Guilds/GuildDeclarePeaceGump.cs index 054c52c9a..6f317baee 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildDeclarePeaceGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildDeclarePeaceGump.cs @@ -5,36 +5,24 @@ namespace Server.Gumps { public class GuildDeclarePeaceGump : GuildListGump { - private GuildDeclarePeaceGump(Guild guild) : base(guild, true, guild.Enemies) + public GuildDeclarePeaceGump(Mobile from, Guild guild) : base(from, guild, true, guild.Enemies) { } - public static void DisplayTo(Mobile from, Guild guild) + protected override void Design() { - if (from?.NetState == null || guild == null) - { - return; - } + AddHtmlLocalized(20, 10, 400, 35, 1011137); // Select the guild you wish to declare peace with. - GuildGump.EnsureClosed(from); - from.SendGump(new GuildDeclarePeaceGump(guild)); - } + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 245, 30, 1011138); // Send the olive branch. - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtmlLocalized(20, 10, 400, 35, 1011137); // Select the guild you wish to declare peace with. - - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 245, 30, 1011138); // Send the olive branch. - - builder.AddButton(300, 400, 4005, 4007, 2); - builder.AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL + AddButton(300, 400, 4005, 4007, 2); + AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -47,23 +35,25 @@ namespace Server.Gumps { var index = switches[0]; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - var g = _list[index]; + var g = m_List[index]; if (g != null) { - _guild.RemoveEnemy(g); + m_Guild.RemoveEnemy(g); // Guild Message: You are now at peace with this guild: - _guild.GuildMessage(1018018, true, $"{g.Name} ({g.Abbreviation})"); + m_Guild.GuildMessage(1018018, true, $"{g.Name} ({g.Abbreviation})"); - if (_guild.Enemies.Count > 0) + GuildGump.EnsureClosed(m_Mobile); + + if (m_Guild.Enemies.Count > 0) { - DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildDeclarePeaceGump(m_Mobile, m_Guild)); } else { - GuildmasterGump.DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } @@ -71,7 +61,8 @@ namespace Server.Gumps } else if (info.ButtonID == 2) { - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildDeclareWarGump.cs b/Projects/UOContent/Gumps/Guilds/GuildDeclareWarGump.cs index 4de6c1d61..a99526e21 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildDeclareWarGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildDeclareWarGump.cs @@ -7,36 +7,25 @@ namespace Server.Gumps { public class GuildDeclareWarGump : GuildListGump { - private GuildDeclareWarGump(Guild guild, List list) : base(guild, true, list) + public GuildDeclareWarGump(Mobile from, Guild guild, List list) + : base(from, guild, true, list) { } - public static void DisplayTo(Mobile from, Guild guild, List list) + protected override void Design() { - if (from?.NetState == null || guild == null || list == null || list.Count == 0) - { - return; - } + AddHtmlLocalized(20, 10, 400, 35, 1011065); // Select the guild you wish to declare war on. - GuildGump.EnsureClosed(from); - from.SendGump(new GuildDeclareWarGump(guild, list)); - } + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 245, 30, 1011068); // Send the challenge! - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtmlLocalized(20, 10, 400, 35, 1011065); // Select the guild you wish to declare war on. - - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 245, 30, 1011068); // Send the challenge! - - builder.AddButton(300, 400, 4005, 4007, 2); - builder.AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL + AddButton(300, 400, 4005, 4007, 2); + AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -49,50 +38,52 @@ namespace Server.Gumps { var index = switches[0]; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - var g = _list[index]; + var g = m_List[index]; if (g != null) { - if (g == _guild) + if (g == m_Guild) { - from.SendLocalizedMessage(501184); // You cannot declare war against yourself! + m_Mobile.SendLocalizedMessage(501184); // You cannot declare war against yourself! } - else if (g.WarInvitations.Contains(_guild) && _guild.WarDeclarations.Contains(g) || - _guild.IsWar(g)) + else if (g.WarInvitations.Contains(m_Guild) && m_Guild.WarDeclarations.Contains(g) || + m_Guild.IsWar(g)) { - from.SendLocalizedMessage(501183); // You are already at war with that guild. + m_Mobile.SendLocalizedMessage(501183); // You are already at war with that guild. } - else if (Faction.Find(_guild.Leader) != null) + else if (Faction.Find(m_Guild.Leader) != null) { - from.SendLocalizedMessage(1005288); // You cannot declare war while you are in a faction + m_Mobile.SendLocalizedMessage(1005288); // You cannot declare war while you are in a faction } else { - if (!_guild.WarDeclarations.Contains(g)) + if (!m_Guild.WarDeclarations.Contains(g)) { - _guild.WarDeclarations.Add(g); + m_Guild.WarDeclarations.Add(g); // Guild Message: Your guild has sent an invitation for war: - _guild.GuildMessage(1018019, true, $"{g.Name} ({g.Abbreviation})"); + m_Guild.GuildMessage(1018019, true, $"{g.Name} ({g.Abbreviation})"); } - if (!g.WarInvitations.Contains(_guild)) + if (!g.WarInvitations.Contains(m_Guild)) { - g.WarInvitations.Add(_guild); + g.WarInvitations.Add(m_Guild); // Guild Message: Your guild has received an invitation to war: - g.GuildMessage(1018021, true, $"{_guild.Name} ({_guild.Abbreviation})"); + g.GuildMessage(1018021, true, $"{m_Guild.Name} ({m_Guild.Abbreviation})"); } } - GuildWarAdminGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildWarAdminGump(m_Mobile, m_Guild)); } } } } else if (info.ButtonID == 2) { - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildDeclareWarPrompt.cs b/Projects/UOContent/Gumps/Guilds/GuildDeclareWarPrompt.cs index 29738e69b..323c9f981 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildDeclareWarPrompt.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildDeclareWarPrompt.cs @@ -5,46 +5,54 @@ namespace Server.Gumps { public class GuildDeclareWarPrompt : Prompt { - private readonly Guild _guild; + private readonly Guild m_Guild; + private readonly Mobile m_Mobile; - public GuildDeclareWarPrompt(Guild g) => _guild = g; + public GuildDeclareWarPrompt(Mobile m, Guild g) + { + m_Mobile = m; + m_Guild = g; + } public override void OnCancel(Mobile from) { - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } - GuildWarAdminGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildWarAdminGump(m_Mobile, m_Guild)); } public override void OnResponse(Mobile from, string text) { - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } - var textSpan = text.Trim(); + text = text.Trim(); - if (textSpan.Length >= 3) + if (text.Length >= 3) { - var guilds = BaseGuild.Search(textSpan).SafeConvertList(); + var guilds = BaseGuild.Search(text).SafeConvertList(); + + GuildGump.EnsureClosed(m_Mobile); if (guilds.Count > 0) { - GuildDeclareWarGump.DisplayTo(from, _guild, guilds); + m_Mobile.SendGump(new GuildDeclareWarGump(m_Mobile, m_Guild, guilds)); } else { - GuildWarAdminGump.DisplayTo(from, _guild); - from.SendLocalizedMessage(1018003); // No guilds found matching - try another name in the search + m_Mobile.SendGump(new GuildWarAdminGump(m_Mobile, m_Guild)); + m_Mobile.SendLocalizedMessage(1018003); // No guilds found matching - try another name in the search } } else { - from.SendMessage("Search string must be at least three letters in length."); + m_Mobile.SendMessage("Search string must be at least three letters in length."); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildDismissGump.cs b/Projects/UOContent/Gumps/Guilds/GuildDismissGump.cs index 9f42e19fb..a95d9659d 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildDismissGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildDismissGump.cs @@ -5,36 +5,24 @@ namespace Server.Gumps { public class GuildDismissGump : GuildMobileListGump { - private GuildDismissGump(Guild guild) : base(guild, true, guild.Members) + public GuildDismissGump(Mobile from, Guild guild) : base(from, guild, true, guild.Members) { } - public static void DisplayTo(Mobile from, Guild guild) + protected override void Design() { - if (from?.NetState == null || guild == null) - { - return; - } + AddHtmlLocalized(20, 10, 400, 35, 1011124); // Whom do you wish to dismiss? - GuildGump.EnsureClosed(from); - from.SendGump(new GuildDismissGump(guild)); - } + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 245, 30, 1011125); // Kick them out! - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtmlLocalized(20, 10, 400, 35, 1011124); // Whom do you wish to dismiss? - - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 245, 30, 1011125); // Kick them out! - - builder.AddButton(300, 400, 4005, 4007, 2); - builder.AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL + AddButton(300, 400, 4005, 4007, 2); + AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -47,25 +35,27 @@ namespace Server.Gumps { var index = switches[0]; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - var m = _list[index]; + var m = m_List[index]; if (m?.Deleted == false) { - _guild.RemoveMember(m); + m_Guild.RemoveMember(m); - if (from.AccessLevel >= AccessLevel.GameMaster || from == _guild.Leader) + if (m_Mobile.AccessLevel >= AccessLevel.GameMaster || m_Mobile == m_Guild.Leader) { - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } } } - else if (info.ButtonID == 2 && (from.AccessLevel >= AccessLevel.GameMaster || from == _guild.Leader)) + else if (info.ButtonID == 2 && (m_Mobile.AccessLevel >= AccessLevel.GameMaster || m_Mobile == m_Guild.Leader)) { - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildGump.cs b/Projects/UOContent/Gumps/Guilds/GuildGump.cs index 9c6252252..42e5bfa07 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildGump.cs @@ -1,120 +1,103 @@ -using System; using Server.Guilds; using Server.Network; namespace Server.Gumps { - public class GuildGump : DynamicGump + public class GuildGump : Gump { - private readonly Guild _guild; - private readonly Mobile _mobile; + private readonly Guild m_Guild; + private readonly Mobile m_Mobile; - public override bool Singleton => true; - - private GuildGump(Mobile beholder, Guild guild) : base(20, 30) + public GuildGump(Mobile beholder, Guild guild) : base(20, 30) { - _mobile = beholder; - _guild = guild; - } + m_Mobile = beholder; + m_Guild = guild; - public static void DisplayTo(Mobile from, Guild guild) - { - if (from?.NetState == null || guild == null) - { - return; - } + Draggable = false; - EnsureClosed(from); - from.SendGump(new GuildGump(from, guild)); - } + AddPage(0); + AddBackground(0, 0, 550, 400, 5054); + AddBackground(10, 10, 530, 380, 3000); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoMove(); + AddHtml(20, 15, 200, 35, guild.Name); - builder.AddPage(); - builder.AddBackground(0, 0, 550, 400, 5054); - builder.AddBackground(10, 10, 530, 380, 3000); - - builder.AddHtml(20, 15, 200, 35, _guild.Name); - - var leader = _guild.Leader; + var leader = guild.Leader; if (leader != null) { - var leadTitle = leader.GuildTitle != null ? leader.GuildTitle.AsSpan().Trim() : ReadOnlySpan.Empty; - var leadName = leader.Name; - var leadNameSpan = leadName != null ? leadName.AsSpan().Trim() : ReadOnlySpan.Empty; - var text = leadTitle.Length > 0 ? $"{leadTitle}: {leadNameSpan}" : leadNameSpan; + var leadTitle = leader.GuildTitle?.Trim(); + var leadName = (leader.Name?.Trim()).DefaultIfNullOrEmpty("(empty)"); + var text = leadTitle?.Length > 0 ? $"{leadTitle}: {leadName}" : leadName; - builder.AddHtml(220, 15, 250, 35, text); + AddHtml(220, 15, 250, 35, text); } - builder.AddButton(20, 50, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 50, 100, 20, 1013022); // Loyal to + AddButton(20, 50, 4005, 4007, 1); + AddHtmlLocalized(55, 50, 100, 20, 1013022); // Loyal to - var fealty = _mobile.GuildFealty; + var fealty = beholder.GuildFealty; - if (fealty == null || !_guild.IsMember(fealty)) + if (fealty == null || !guild.IsMember(fealty)) { fealty = leader; } - fealty ??= _mobile; + fealty ??= beholder; - if (_mobile == fealty) + var fealtyName = (fealty.Name?.Trim()).DefaultIfNullOrEmpty("(empty)"); + + if (beholder == fealty) { - builder.AddHtmlLocalized(55, 70, 470, 20, 1018002); // yourself + AddHtmlLocalized(55, 70, 470, 20, 1018002); // yourself } else { - var fealtyName = fealty.Name; - builder.AddHtml(55, 70, 470, 20, fealtyName != null ? fealtyName.AsSpan().Trim() : "(empty)"); + AddHtml(55, 70, 470, 20, fealtyName); } - builder.AddButton(215, 50, 4005, 4007, 2); - builder.AddHtmlLocalized(250, 50, 170, 20, 1013023); // Display guild abbreviation - builder.AddHtmlLocalized(250, 70, 50, 20, _mobile.DisplayGuildTitle ? 1011262 : 1011263); // on/off + AddButton(215, 50, 4005, 4007, 2); + AddHtmlLocalized(250, 50, 170, 20, 1013023); // Display guild abbreviation + AddHtmlLocalized(250, 70, 50, 20, beholder.DisplayGuildTitle ? 1011262 : 1011263); // on/off - builder.AddButton(20, 100, 4005, 4007, 3); - builder.AddHtmlLocalized(55, 100, 470, 30, 1011086); // View the current roster. + AddButton(20, 100, 4005, 4007, 3); + AddHtmlLocalized(55, 100, 470, 30, 1011086); // View the current roster. - builder.AddButton(20, 130, 4005, 4007, 4); - builder.AddHtmlLocalized(55, 130, 470, 30, 1011085); // Recruit someone into the guild. + AddButton(20, 130, 4005, 4007, 4); + AddHtmlLocalized(55, 130, 470, 30, 1011085); // Recruit someone into the guild. - if (_guild.Candidates.Count > 0) + if (guild.Candidates.Count > 0) { - builder.AddButton(20, 160, 4005, 4007, 5); - builder.AddHtmlLocalized(55, 160, 470, 30, 1011093); // View list of candidates who have been sponsored to the guild. + AddButton(20, 160, 4005, 4007, 5); + AddHtmlLocalized(55, 160, 470, 30, 1011093); // View list of candidates who have been sponsored to the guild. } else { - builder.AddImage(20, 160, 4020); - builder.AddHtmlLocalized(55, 160, 470, 30, 1013031); // There are currently no candidates for membership. + AddImage(20, 160, 4020); + AddHtmlLocalized(55, 160, 470, 30, 1013031); // There are currently no candidates for membership. } - builder.AddButton(20, 220, 4005, 4007, 6); - builder.AddHtmlLocalized(55, 220, 470, 30, 1011087); // View the guild's charter. + AddButton(20, 220, 4005, 4007, 6); + AddHtmlLocalized(55, 220, 470, 30, 1011087); // View the guild's charter. - builder.AddButton(20, 250, 4005, 4007, 7); - builder.AddHtmlLocalized(55, 250, 470, 30, 1011092); // Resign from the guild. + AddButton(20, 250, 4005, 4007, 7); + AddHtmlLocalized(55, 250, 470, 30, 1011092); // Resign from the guild. - builder.AddButton(20, 280, 4005, 4007, 8); - builder.AddHtmlLocalized(55, 280, 470, 30, 1011095); // View list of guilds you are at war with. + AddButton(20, 280, 4005, 4007, 8); + AddHtmlLocalized(55, 280, 470, 30, 1011095); // View list of guilds you are at war with. - if (_mobile.AccessLevel >= AccessLevel.GameMaster || _mobile == leader) + if (beholder.AccessLevel >= AccessLevel.GameMaster || beholder == leader) { - builder.AddButton(20, 310, 4005, 4007, 9); - builder.AddHtmlLocalized(55, 310, 470, 30, 1011094); // Access guildmaster functions. + AddButton(20, 310, 4005, 4007, 9); + AddHtmlLocalized(55, 310, 470, 30, 1011094); // Access guildmaster functions. } else { - builder.AddImage(20, 310, 4020); - builder.AddHtmlLocalized(55, 310, 470, 30, 1018013); // Reserved for guildmaster + AddImage(20, 310, 4020); + AddHtmlLocalized(55, 310, 470, 30, 1018013); // Reserved for guildmaster } - builder.AddButton(20, 360, 4005, 4007, 0); - builder.AddHtmlLocalized(55, 360, 470, 30, 1011441); // EXIT + AddButton(20, 360, 4005, 4007, 0); + AddHtmlLocalized(55, 360, 470, 30, 1011441); // EXIT } public static void EnsureClosed(Mobile m) @@ -160,7 +143,7 @@ namespace Server.Gumps public override void OnResponse(NetState sender, in RelayInfo info) { - if (BadMember(_mobile, _guild)) + if (BadMember(m_Mobile, m_Guild)) { return; } @@ -169,50 +152,66 @@ namespace Server.Gumps { case 1: // Loyalty { - DeclareFealtyGump.DisplayTo(_mobile, _guild); + EnsureClosed(m_Mobile); + m_Mobile.SendGump(new DeclareFealtyGump(m_Mobile, m_Guild)); + break; } case 2: // Toggle display abbreviation { - _mobile.DisplayGuildTitle = !_mobile.DisplayGuildTitle; - DisplayTo(_mobile, _guild); + m_Mobile.DisplayGuildTitle = !m_Mobile.DisplayGuildTitle; + + EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildGump(m_Mobile, m_Guild)); + break; } case 3: // View the current roster { - GuildRosterGump.DisplayTo(_mobile, _guild); + EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildRosterGump(m_Mobile, m_Guild)); + break; } case 4: // Recruit { - _mobile.Target = new GuildRecruitTarget(_mobile, _guild); + m_Mobile.Target = new GuildRecruitTarget(m_Mobile, m_Guild); + break; } case 5: // Membership candidates { - GuildCandidatesGump.DisplayTo(_mobile, _guild); + EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildCandidatesGump(m_Mobile, m_Guild)); + break; } case 6: // View charter { - GuildCharterGump.DisplayTo(_mobile, _guild); + EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildCharterGump(m_Mobile, m_Guild)); + break; } case 7: // Resign { - _guild.RemoveMember(_mobile); + m_Guild.RemoveMember(m_Mobile); + break; } case 8: // View wars { - GuildWarGump.DisplayTo(_mobile, _guild); + EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildWarGump(m_Mobile, m_Guild)); + break; } case 9: // Guildmaster functions { - if (_mobile.AccessLevel >= AccessLevel.GameMaster || _guild.Leader == _mobile) + if (m_Mobile.AccessLevel >= AccessLevel.GameMaster || m_Guild.Leader == m_Mobile) { - GuildmasterGump.DisplayTo(_mobile, _guild); + EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } break; diff --git a/Projects/UOContent/Gumps/Guilds/GuildListGump.cs b/Projects/UOContent/Gumps/Guilds/GuildListGump.cs index 8efa1c391..c69761f95 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildListGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildListGump.cs @@ -1,65 +1,62 @@ -using System; using System.Collections.Generic; using Server.Guilds; namespace Server.Gumps { - public abstract class GuildListGump : DynamicGump + public abstract class GuildListGump : Gump { - protected Guild _guild; - protected List _list; - private readonly bool _radio; + protected Guild m_Guild; + protected List m_List; + protected Mobile m_Mobile; - public override bool Singleton => true; - - protected GuildListGump(Guild guild, bool radio, List list) : base(20, 30) + public GuildListGump(Mobile from, Guild guild, bool radio, List list) : base(20, 30) { - _guild = guild; - _radio = radio; - _list = list; - } + m_Mobile = from; + m_Guild = guild; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoMove(); + Draggable = false; - builder.AddPage(); - builder.AddBackground(0, 0, 550, 440, 5054); - builder.AddBackground(10, 10, 530, 420, 3000); + AddPage(0); + AddBackground(0, 0, 550, 440, 5054); + AddBackground(10, 10, 530, 420, 3000); - BuildHeader(ref builder); + Design(); - for (var i = 0; i < _list.Count; ++i) + m_List = list; + + for (var i = 0; i < m_List.Count; ++i) { if (i % 11 == 0) { if (i != 0) { - builder.AddButton(300, 370, 4005, 4007, 0, GumpButtonType.Page, i / 11 + 1); - builder.AddHtmlLocalized(335, 370, 300, 35, 1011066); // Next page + AddButton(300, 370, 4005, 4007, 0, GumpButtonType.Page, i / 11 + 1); + AddHtmlLocalized(335, 370, 300, 35, 1011066); // Next page } - builder.AddPage(i / 11 + 1); + AddPage(i / 11 + 1); if (i != 0) { - builder.AddButton(20, 370, 4014, 4016, 0, GumpButtonType.Page, i / 11); - builder.AddHtmlLocalized(55, 370, 300, 35, 1011067); // Previous page + AddButton(20, 370, 4014, 4016, 0, GumpButtonType.Page, i / 11); + AddHtmlLocalized(55, 370, 300, 35, 1011067); // Previous page } } - if (_radio) + if (radio) { - builder.AddRadio(20, 35 + i % 11 * 30, 208, 209, false, i); + AddRadio(20, 35 + i % 11 * 30, 208, 209, false, i); } - var g = _list[i]; + var g = m_List[i]; - var name = g.Name; - builder.AddLabel(_radio ? 55 : 20, 35 + i % 11 * 30, 0, name != null ? name.AsSpan().Trim() : "(empty)"); + string name = g.Name?.Trim().DefaultIfNullOrEmpty("(empty)"); + AddLabel(radio ? 55 : 20, 35 + i % 11 * 30, 0, name); } } - protected abstract void BuildHeader(ref DynamicGumpBuilder builder); + protected virtual void Design() + { + } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildMobileListGump.cs b/Projects/UOContent/Gumps/Guilds/GuildMobileListGump.cs index 8880aff81..9041044e6 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildMobileListGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildMobileListGump.cs @@ -1,65 +1,63 @@ -using System; using System.Collections.Generic; using Server.Guilds; namespace Server.Gumps { - public abstract class GuildMobileListGump : DynamicGump + public abstract class GuildMobileListGump : Gump { - protected Guild _guild; - protected List _list; - private readonly bool _radio; + protected Guild m_Guild; + protected List m_List; + protected Mobile m_Mobile; - public override bool Singleton => true; - - protected GuildMobileListGump(Guild guild, bool radio, List list) : base(20, 30) + public GuildMobileListGump(Mobile from, Guild guild, bool radio, List list) + : base(20, 30) { - _guild = guild; - _radio = radio; - _list = new List(list); - } + m_Mobile = from; + m_Guild = guild; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoMove(); + Draggable = false; - builder.AddPage(); - builder.AddBackground(0, 0, 550, 440, 5054); - builder.AddBackground(10, 10, 530, 420, 3000); + AddPage(0); + AddBackground(0, 0, 550, 440, 5054); + AddBackground(10, 10, 530, 420, 3000); - BuildHeader(ref builder); + Design(); - for (var i = 0; i < _list.Count; ++i) + m_List = new List(list); + + for (var i = 0; i < m_List.Count; ++i) { if (i % 11 == 0) { if (i != 0) { - builder.AddButton(300, 370, 4005, 4007, 0, GumpButtonType.Page, i / 11 + 1); - builder.AddHtmlLocalized(335, 370, 300, 35, 1011066); // Next page + AddButton(300, 370, 4005, 4007, 0, GumpButtonType.Page, i / 11 + 1); + AddHtmlLocalized(335, 370, 300, 35, 1011066); // Next page } - builder.AddPage(i / 11 + 1); + AddPage(i / 11 + 1); if (i != 0) { - builder.AddButton(20, 370, 4014, 4016, 0, GumpButtonType.Page, i / 11); - builder.AddHtmlLocalized(55, 370, 300, 35, 1011067); // Previous page + AddButton(20, 370, 4014, 4016, 0, GumpButtonType.Page, i / 11); + AddHtmlLocalized(55, 370, 300, 35, 1011067); // Previous page } } - if (_radio) + if (radio) { - builder.AddRadio(20, 35 + i % 11 * 30, 208, 209, false, i); + AddRadio(20, 35 + i % 11 * 30, 208, 209, false, i); } - var m = _list[i]; + var m = m_List[i]; - var name = m.Name; - builder.AddLabel(_radio ? 55 : 20, 35 + i % 11 * 30, 0, name != null ? name.AsSpan().Trim() : "(empty)"); + string name = m.Name?.Trim().DefaultIfNullOrEmpty("(empty)"); + AddLabel(radio ? 55 : 20, 35 + i % 11 * 30, 0, name); } } - protected abstract void BuildHeader(ref DynamicGumpBuilder builder); + protected virtual void Design() + { + } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildNamePrompt.cs b/Projects/UOContent/Gumps/Guilds/GuildNamePrompt.cs index 2b4658d42..3e9887c9c 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildNamePrompt.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildNamePrompt.cs @@ -1,4 +1,3 @@ -using System; using Server.Guilds; using Server.Prompts; @@ -6,49 +5,55 @@ namespace Server.Gumps { public class GuildNamePrompt : Prompt { - private readonly Guild _guild; + private readonly Guild m_Guild; + private readonly Mobile m_Mobile; - public GuildNamePrompt(Mobile m, Guild g) => _guild = g; + public GuildNamePrompt(Mobile m, Guild g) + { + m_Mobile = m; + m_Guild = g; + } public override void OnCancel(Mobile from) { - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } public override void OnResponse(Mobile from, string text) { - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } - var textSpan = text.AsSpan().Trim(); + text = text.Trim(); - if (textSpan.Length > 40) + if (text.Length > 40) { - textSpan = textSpan[..40]; + text = text[..40]; } - if (textSpan.Length > 0) + if (text.Length > 0) { - if (BaseGuild.FindByName(textSpan) != null) + if (BaseGuild.FindByName(text) != null) { - from.SendMessage($"{textSpan} conflicts with the name of an existing guild."); + m_Mobile.SendMessage($"{text} conflicts with the name of an existing guild."); } else { - text = textSpan.ToString(); - _guild.Name = text; - _guild.GuildMessage(1018024, true, text); // The name of your guild has changed: + m_Guild.Name = text; + m_Guild.GuildMessage(1018024, true, text); // The name of your guild has changed: } } - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildRejectWarGump.cs b/Projects/UOContent/Gumps/Guilds/GuildRejectWarGump.cs index dcf191235..4199fa3e8 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildRejectWarGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildRejectWarGump.cs @@ -5,36 +5,24 @@ namespace Server.Gumps { public class GuildRejectWarGump : GuildListGump { - private GuildRejectWarGump(Guild guild) : base(guild, true, guild.WarInvitations) + public GuildRejectWarGump(Mobile from, Guild guild) : base(from, guild, true, guild.WarInvitations) { } - public static void DisplayTo(Mobile from, Guild guild) + protected override void Design() { - if (from?.NetState == null || guild == null) - { - return; - } + AddHtmlLocalized(20, 10, 400, 35, 1011148); // Select the guild to reject their invitations: - GuildGump.EnsureClosed(from); - from.SendGump(new GuildRejectWarGump(guild)); - } + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 245, 30, 1011101); // Reject war invitations. - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtmlLocalized(20, 10, 400, 35, 1011148); // Select the guild to reject their invitations: - - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 245, 30, 1011101); // Reject war invitations. - - builder.AddButton(300, 400, 4005, 4007, 2); - builder.AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL + AddButton(300, 400, 4005, 4007, 2); + AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -47,22 +35,24 @@ namespace Server.Gumps { var index = switches[0]; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - var g = _list[index]; + var g = m_List[index]; if (g != null) { - _guild.WarInvitations.Remove(g); - g.WarDeclarations.Remove(_guild); + m_Guild.WarInvitations.Remove(g); + g.WarDeclarations.Remove(m_Guild); - if (_guild.WarInvitations.Count > 0) + GuildGump.EnsureClosed(m_Mobile); + + if (m_Guild.WarInvitations.Count > 0) { - DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildRejectWarGump(m_Mobile, m_Guild)); } else { - GuildmasterGump.DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } @@ -70,7 +60,8 @@ namespace Server.Gumps } else if (info.ButtonID == 2) { - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildRescindDeclarationGump.cs b/Projects/UOContent/Gumps/Guilds/GuildRescindDeclarationGump.cs index db80af993..37fb37e12 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildRescindDeclarationGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildRescindDeclarationGump.cs @@ -5,36 +5,24 @@ namespace Server.Gumps { public class GuildRescindDeclarationGump : GuildListGump { - private GuildRescindDeclarationGump(Guild guild) : base(guild, true, guild.WarDeclarations) + public GuildRescindDeclarationGump(Mobile from, Guild guild) : base(from, guild, true, guild.WarDeclarations) { } - public static void DisplayTo(Mobile from, Guild guild) + protected override void Design() { - if (from?.NetState == null || guild == null) - { - return; - } + AddHtmlLocalized(20, 10, 400, 35, 1011150); // Select the guild to rescind our invitations: - GuildGump.EnsureClosed(from); - from.SendGump(new GuildRescindDeclarationGump(guild)); - } + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 245, 30, 1011102); // Rescind your war declarations. - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtmlLocalized(20, 10, 400, 35, 1011150); // Select the guild to rescind our invitations: - - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 245, 30, 1011102); // Rescind your war declarations. - - builder.AddButton(300, 400, 4005, 4007, 2); - builder.AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL + AddButton(300, 400, 4005, 4007, 2); + AddHtmlLocalized(335, 400, 100, 35, 1011012); // CANCEL } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -47,22 +35,24 @@ namespace Server.Gumps { var index = switches[0]; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - var g = _list[index]; + var g = m_List[index]; if (g != null) { - _guild.WarDeclarations.Remove(g); - g.WarInvitations.Remove(_guild); + m_Guild.WarDeclarations.Remove(g); + g.WarInvitations.Remove(m_Guild); - if (_guild.WarDeclarations.Count > 0) + GuildGump.EnsureClosed(m_Mobile); + + if (m_Guild.WarDeclarations.Count > 0) { - DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildRescindDeclarationGump(m_Mobile, m_Guild)); } else { - GuildmasterGump.DisplayTo(from, _guild); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } @@ -70,7 +60,8 @@ namespace Server.Gumps } else if (info.ButtonID == 2) { - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildRosterGump.cs b/Projects/UOContent/Gumps/Guilds/GuildRosterGump.cs index 2de9a7bd1..0122bacf8 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildRosterGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildRosterGump.cs @@ -5,40 +5,29 @@ namespace Server.Gumps { public class GuildRosterGump : GuildMobileListGump { - private GuildRosterGump(Guild guild) : base(guild, false, guild.Members) + public GuildRosterGump(Mobile from, Guild guild) : base(from, guild, false, guild.Members) { } - public static void DisplayTo(Mobile from, Guild guild) + protected override void Design() { - if (from?.NetState == null || guild == null) - { - return; - } + AddHtml(20, 10, 500, 35, $"
{m_Guild.Name}
"); - GuildGump.EnsureClosed(from); - from.SendGump(new GuildRosterGump(guild)); - } - - protected override void BuildHeader(ref DynamicGumpBuilder builder) - { - builder.AddHtml(20, 10, 500, 35, $"
{_guild.Name}
"); - - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 300, 35, 1011120); // Return to the main menu. + AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 300, 35, 1011120); // Return to the main menu. } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadMember(from, _guild)) + if (GuildGump.BadMember(m_Mobile, m_Guild)) { return; } if (info.ButtonID == 1) { - GuildGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildTitlePrompt.cs b/Projects/UOContent/Gumps/Guilds/GuildTitlePrompt.cs index 9a3875eaf..b77b97743 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildTitlePrompt.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildTitlePrompt.cs @@ -1,4 +1,3 @@ -using System; using Server.Guilds; using Server.Prompts; @@ -7,17 +6,19 @@ namespace Server.Gumps public class GuildTitlePrompt : Prompt { private readonly Guild m_Guild; + private readonly Mobile m_Leader; private readonly Mobile m_Target; - public GuildTitlePrompt(Mobile target, Guild g) + public GuildTitlePrompt(Mobile leader, Mobile target, Guild g) { + m_Leader = leader; m_Target = target; m_Guild = g; } public override void OnCancel(Mobile from) { - if (GuildGump.BadLeader(from, m_Guild)) + if (GuildGump.BadLeader(m_Leader, m_Guild)) { return; } @@ -27,12 +28,13 @@ namespace Server.Gumps return; } - GuildmasterGump.DisplayTo(from, m_Guild); + GuildGump.EnsureClosed(m_Leader); + m_Leader.SendGump(new GuildmasterGump(m_Leader, m_Guild)); } public override void OnResponse(Mobile from, string text) { - if (GuildGump.BadLeader(from, m_Guild)) + if (GuildGump.BadLeader(m_Leader, m_Guild)) { return; } @@ -42,19 +44,20 @@ namespace Server.Gumps return; } - var textSpan = text.AsSpan().Trim(); + text = text.Trim(); - if (textSpan.Length > 20) + if (text.Length > 20) { - textSpan = textSpan[..20]; + text = text[..20]; } - if (textSpan.Length > 0) + if (text.Length > 0) { - m_Target.GuildTitle = textSpan.ToString(); + m_Target.GuildTitle = text; } - GuildmasterGump.DisplayTo(from, m_Guild); + GuildGump.EnsureClosed(m_Leader); + m_Leader.SendGump(new GuildmasterGump(m_Leader, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildWarAdminGump.cs b/Projects/UOContent/Gumps/Guilds/GuildWarAdminGump.cs index 500311823..0046d23d8 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildWarAdminGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildWarAdminGump.cs @@ -3,79 +3,69 @@ using Server.Network; namespace Server.Gumps { - public class GuildWarAdminGump : DynamicGump + public class GuildWarAdminGump : Gump { - private readonly Guild _guild; + private readonly Guild m_Guild; + private readonly Mobile m_Mobile; - public override bool Singleton => true; - - private GuildWarAdminGump(Guild guild) : base(20, 30) => _guild = guild; - - public static void DisplayTo(Mobile from, Guild guild) + public GuildWarAdminGump(Mobile from, Guild guild) : base(20, 30) { - if (from?.NetState == null || guild == null) + m_Mobile = from; + m_Guild = guild; + + Draggable = false; + + AddPage(0); + AddBackground(0, 0, 550, 440, 5054); + AddBackground(10, 10, 530, 420, 3000); + + AddHtmlLocalized(20, 10, 510, 35, 1011105); //
WAR FUNCTIONS
+ + AddButton(20, 40, 4005, 4007, 1); + AddHtmlLocalized(55, 40, 400, 30, 1011099); // Declare war through guild name search. + + var count = 0; + + if (guild.Enemies.Count > 0) { - return; - } - - GuildGump.EnsureClosed(from); - from.SendGump(new GuildWarAdminGump(guild)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoMove(); - - builder.AddPage(); - builder.AddBackground(0, 0, 550, 440, 5054); - builder.AddBackground(10, 10, 530, 420, 3000); - - builder.AddHtmlLocalized(20, 10, 510, 35, 1011105); //
WAR FUNCTIONS
- - builder.AddButton(20, 40, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 40, 400, 30, 1011099); // Declare war through guild name search. - - if (_guild.Enemies.Count > 0) - { - builder.AddButton(20, 160 + 30, 4005, 4007, 2); - builder.AddHtmlLocalized(55, 160, 400, 30, 1011103); // Declare peace. + AddButton(20, 160 + count * 30, 4005, 4007, 2); + AddHtmlLocalized(55, 160 + count++ * 30, 400, 30, 1011103); // Declare peace. } else { - builder.AddHtmlLocalized(20, 160, 400, 30, 1013033); // No current wars + AddHtmlLocalized(20, 160 + count++ * 30, 400, 30, 1013033); // No current wars } - if (_guild.WarInvitations.Count > 0) + if (guild.WarInvitations.Count > 0) { - builder.AddButton(20, 190, 4005, 4007, 3); - builder.AddHtmlLocalized(55, 190, 400, 30, 1011100); // Accept war invitations. + AddButton(20, 160 + count * 30, 4005, 4007, 3); + AddHtmlLocalized(55, 160 + count++ * 30, 400, 30, 1011100); // Accept war invitations. - builder.AddButton(20, 190, 4005, 4007, 4); - builder.AddHtmlLocalized(55, 190, 400, 30, 1011101); // Reject war invitations. + AddButton(20, 160 + count * 30, 4005, 4007, 4); + AddHtmlLocalized(55, 160 + count++ * 30, 400, 30, 1011101); // Reject war invitations. } else { - builder.AddHtmlLocalized(20, 190, 400, 30, 1018012); // No current invitations received for war. + AddHtmlLocalized(20, 160 + count++ * 30, 400, 30, 1018012); // No current invitations received for war. } - if (_guild.WarDeclarations.Count > 0) + if (guild.WarDeclarations.Count > 0) { - builder.AddButton(20, 220, 4005, 4007, 5); - builder.AddHtmlLocalized(55, 220, 400, 30, 1011102); // Rescind your war declarations. + AddButton(20, 160 + count * 30, 4005, 4007, 5); + AddHtmlLocalized(55, 160 + count++ * 30, 400, 30, 1011102); // Rescind your war declarations. } else { - builder.AddHtmlLocalized(20, 220, 400, 30, 1013055); // No current war declarations + AddHtmlLocalized(20, 160 + count++ * 30, 400, 30, 1013055); // No current war declarations } - builder.AddButton(20, 400, 4005, 4007, 6); - builder.AddHtmlLocalized(55, 400, 400, 35, 1011104); // Return to the previous menu. + AddButton(20, 400, 4005, 4007, 6); + AddHtmlLocalized(55, 400, 400, 35, 1011104); // Return to the previous menu. } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -84,33 +74,44 @@ namespace Server.Gumps { case 1: // Declare war { - from.SendLocalizedMessage(1018001); // Declare war through search - Enter Guild Name: - from.Prompt = new GuildDeclareWarPrompt(_guild); + m_Mobile.SendLocalizedMessage(1018001); // Declare war through search - Enter Guild Name: + m_Mobile.Prompt = new GuildDeclareWarPrompt(m_Mobile, m_Guild); + break; } case 2: // Declare peace { - GuildDeclarePeaceGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildDeclarePeaceGump(m_Mobile, m_Guild)); + break; } case 3: // Accept war { - GuildAcceptWarGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildAcceptWarGump(m_Mobile, m_Guild)); + break; } case 4: // Reject war { - GuildRejectWarGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildRejectWarGump(m_Mobile, m_Guild)); + break; } case 5: // Rescind declarations { - GuildRescindDeclarationGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildRescindDeclarationGump(m_Mobile, m_Guild)); + break; } case 6: // Return { - GuildmasterGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); + break; } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildWarGump.cs b/Projects/UOContent/Gumps/Guilds/GuildWarGump.cs index 7da275ea5..b11c7bee8 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildWarGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildWarGump.cs @@ -3,116 +3,111 @@ using Server.Network; namespace Server.Gumps { - public class GuildWarGump : DynamicGump + public class GuildWarGump : Gump { - private readonly Guild _guild; + private readonly Guild m_Guild; + private readonly Mobile m_Mobile; - public override bool Singleton => true; - - private GuildWarGump(Guild guild) : base(20, 30) => _guild = guild; - - public static void DisplayTo(Mobile from, Guild guild) + public GuildWarGump(Mobile from, Guild guild) : base(20, 30) { - if (from?.NetState == null || guild == null) - { - return; - } + m_Mobile = from; + m_Guild = guild; - GuildGump.EnsureClosed(from); - from.SendGump(new GuildWarGump(guild)); - } + Draggable = false; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoMove(); + AddPage(0); + AddBackground(0, 0, 550, 440, 5054); + AddBackground(10, 10, 530, 420, 3000); - builder.AddPage(); - builder.AddBackground(0, 0, 550, 440, 5054); - builder.AddBackground(10, 10, 530, 420, 3000); + AddHtmlLocalized(20, 10, 500, 35, 1011133); //
WARFARE STATUS
- builder.AddHtmlLocalized(20, 10, 500, 35, 1011133); //
WARFARE STATUS
+ AddButton(20, 400, 4005, 4007, 1); + AddHtmlLocalized(55, 400, 300, 35, 1011120); // Return to the main menu. - builder.AddButton(20, 400, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 400, 300, 35, 1011120); // Return to the main menu. + AddPage(1); - builder.AddPage(1); + AddButton(375, 375, 5224, 5224, 0, GumpButtonType.Page, 2); + AddHtmlLocalized(410, 373, 100, 25, 1011066); // Next page - builder.AddButton(375, 375, 5224, 5224, 0, GumpButtonType.Page, 2); - builder.AddHtmlLocalized(410, 373, 100, 25, 1011066); // Next page + AddHtmlLocalized(20, 45, 400, 20, 1011134); // We are at war with: - builder.AddHtmlLocalized(20, 45, 400, 20, 1011134); // We are at war with: - - var enemies = _guild.Enemies; + var enemies = guild.Enemies; if (enemies.Count == 0) { - builder.AddHtmlLocalized(20, 65, 400, 20, 1013033); // No current wars + AddHtmlLocalized(20, 65, 400, 20, 1013033); // No current wars } else { for (var i = 0; i < enemies.Count; ++i) { - builder.AddHtml(20, 65 + i * 20, 300, 20, enemies[i].Name); + var g = enemies[i]; + + AddHtml(20, 65 + i * 20, 300, 20, g.Name); } } - builder.AddPage(2); + AddPage(2); - builder.AddButton(375, 375, 5224, 5224, 0, GumpButtonType.Page, 3); - builder.AddHtmlLocalized(410, 373, 100, 25, 1011066); // Next page + AddButton(375, 375, 5224, 5224, 0, GumpButtonType.Page, 3); + AddHtmlLocalized(410, 373, 100, 25, 1011066); // Next page - builder.AddButton(30, 375, 5223, 5223, 0, GumpButtonType.Page, 1); - builder.AddHtmlLocalized(65, 373, 150, 25, 1011067); // Previous page + AddButton(30, 375, 5223, 5223, 0, GumpButtonType.Page, 1); + AddHtmlLocalized(65, 373, 150, 25, 1011067); // Previous page - builder.AddHtmlLocalized(20, 45, 400, 20, 1011136); // Guilds that we have declared war on: + AddHtmlLocalized(20, 45, 400, 20, 1011136); // Guilds that we have declared war on: - var declared = _guild.WarDeclarations; + var declared = guild.WarDeclarations; if (declared.Count == 0) { - builder.AddHtmlLocalized(20, 65, 400, 20, 1018012); // No current invitations received for war. + AddHtmlLocalized(20, 65, 400, 20, 1018012); // No current invitations received for war. } else { for (var i = 0; i < declared.Count; ++i) { - builder.AddHtml(20, 65 + i * 20, 300, 20, declared[i].Name); + var g = declared[i]; + + AddHtml(20, 65 + i * 20, 300, 20, g.Name); } } - builder.AddPage(3); + AddPage(3); - builder.AddButton(30, 375, 5223, 5223, 0, GumpButtonType.Page, 2); - builder.AddHtmlLocalized(65, 373, 150, 25, 1011067); // Previous page + AddButton(30, 375, 5223, 5223, 0, GumpButtonType.Page, 2); + AddHtmlLocalized(65, 373, 150, 25, 1011067); // Previous page - builder.AddHtmlLocalized(20, 45, 400, 20, 1011135); // Guilds that have declared war on us: + AddHtmlLocalized(20, 45, 400, 20, 1011135); // Guilds that have declared war on us: - var invites = _guild.WarInvitations; + var invites = guild.WarInvitations; if (invites.Count == 0) { - builder.AddHtmlLocalized(20, 65, 400, 20, 1013055); // No current war declarations + AddHtmlLocalized(20, 65, 400, 20, 1013055); // No current war declarations } else { for (var i = 0; i < invites.Count; ++i) { - builder.AddHtml(20, 65 + i * 20, 300, 20, invites[i].Name); + var g = invites[i]; + + AddHtml(20, 65 + i * 20, 300, 20, g.Name); } } } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadMember(from, _guild)) + if (GuildGump.BadMember(m_Mobile, m_Guild)) { return; } if (info.ButtonID == 1) { - GuildGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildWebsitePrompt.cs b/Projects/UOContent/Gumps/Guilds/GuildWebsitePrompt.cs index 94ca32a25..757679db1 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildWebsitePrompt.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildWebsitePrompt.cs @@ -21,7 +21,8 @@ namespace Server.Gumps return; } - GuildmasterGump.DisplayTo(m_Mobile, m_Guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } public override void OnResponse(Mobile from, string text) @@ -43,7 +44,8 @@ namespace Server.Gumps m_Guild.Website = text; } - GuildmasterGump.DisplayTo(m_Mobile, m_Guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildmasterGump.cs b/Projects/UOContent/Gumps/Guilds/GuildmasterGump.cs index c5dfd4d08..332a3d396 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildmasterGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildmasterGump.cs @@ -4,105 +4,87 @@ using Server.Network; namespace Server.Gumps { - public class GuildmasterGump : DynamicGump + public class GuildmasterGump : Gump { - private readonly Guild _guild; + private readonly Guild m_Guild; + private readonly Mobile m_Mobile; - public override bool Singleton => true; - - private GuildmasterGump(Guild guild) : base(20, 30) => _guild = guild; - - public static void DisplayTo(Mobile from, Guild guild) + public GuildmasterGump(Mobile from, Guild guild) : base(20, 30) { - if (from?.NetState == null || guild == null) - { - return; - } + m_Mobile = from; + m_Guild = guild; - GuildGump.EnsureClosed(from); - from.SendGump(new GuildmasterGump(guild)); - } + Draggable = false; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoMove(); + AddPage(0); + AddBackground(0, 0, 550, 400, 5054); + AddBackground(10, 10, 530, 380, 3000); - builder.AddPage(); - builder.AddBackground(0, 0, 550, 400, 5054); - builder.AddBackground(10, 10, 530, 380, 3000); + AddHtmlLocalized(20, 15, 510, 35, 1011121); //
GUILDMASTER FUNCTIONS
- builder.AddHtmlLocalized(20, 15, 510, 35, 1011121); //
GUILDMASTER FUNCTIONS
+ AddButton(20, 40, 4005, 4007, 2); + AddHtmlLocalized(55, 40, 470, 30, 1011107); // Set the guild name. - builder.AddButton(20, 40, 4005, 4007, 2); - builder.AddHtmlLocalized(55, 40, 470, 30, 1011107); // Set the guild name. - - builder.AddButton(20, 70, 4005, 4007, 3); - builder.AddHtmlLocalized(55, 70, 470, 30, 1011109); // Set the guild's abbreviation. + AddButton(20, 70, 4005, 4007, 3); + AddHtmlLocalized(55, 70, 470, 30, 1011109); // Set the guild's abbreviation. if (Guild.OrderChaos) { - builder.AddButton(20, 100, 4005, 4007, 4); + AddButton(20, 100, 4005, 4007, 4); - switch (_guild.Type) + switch (m_Guild.Type) { case GuildType.Regular: - { - builder.AddHtmlLocalized(55, 100, 470, 30, 1013059); // Change guild type: Currently Standard - break; - } + AddHtmlLocalized(55, 100, 470, 30, 1013059); // Change guild type: Currently Standard + break; case GuildType.Order: - { - builder.AddHtmlLocalized(55, 100, 470, 30, 1013057); // Change guild type: Currently Order - break; - } + AddHtmlLocalized(55, 100, 470, 30, 1013057); // Change guild type: Currently Order + break; case GuildType.Chaos: - { - builder.AddHtmlLocalized(55, 100, 470, 30, 1013058); // Change guild type: Currently Chaos - break; - } + AddHtmlLocalized(55, 100, 470, 30, 1013058); // Change guild type: Currently Chaos + break; } } - builder.AddButton(20, 130, 4005, 4007, 5); - builder.AddHtmlLocalized(55, 130, 470, 30, 1011112); // Set the guild's charter. + AddButton(20, 130, 4005, 4007, 5); + AddHtmlLocalized(55, 130, 470, 30, 1011112); // Set the guild's charter. - builder.AddButton(20, 160, 4005, 4007, 6); - builder.AddHtmlLocalized(55, 160, 470, 30, 1011113); // Dismiss a member. + AddButton(20, 160, 4005, 4007, 6); + AddHtmlLocalized(55, 160, 470, 30, 1011113); // Dismiss a member. - builder.AddButton(20, 190, 4005, 4007, 7); - builder.AddHtmlLocalized(55, 190, 470, 30, 1011114); // Go to the WAR menu. + AddButton(20, 190, 4005, 4007, 7); + AddHtmlLocalized(55, 190, 470, 30, 1011114); // Go to the WAR menu. - if (_guild.Candidates.Count > 0) + if (m_Guild.Candidates.Count > 0) { - builder.AddButton(20, 220, 4005, 4007, 8); - builder.AddHtmlLocalized(55, 220, 470, 30, 1013056); // Administer the list of candidates + AddButton(20, 220, 4005, 4007, 8); + AddHtmlLocalized(55, 220, 470, 30, 1013056); // Administer the list of candidates } else { - builder.AddImage(20, 220, 4020); - builder.AddHtmlLocalized(55, 220, 470, 30, 1013031); // There are currently no candidates for membership. + AddImage(20, 220, 4020); + AddHtmlLocalized(55, 220, 470, 30, 1013031); // There are currently no candidates for membership. } - builder.AddButton(20, 250, 4005, 4007, 9); - builder.AddHtmlLocalized(55, 250, 470, 30, 1011117); // Set the guildmaster's title. + AddButton(20, 250, 4005, 4007, 9); + AddHtmlLocalized(55, 250, 470, 30, 1011117); // Set the guildmaster's title. - builder.AddButton(20, 280, 4005, 4007, 10); - builder.AddHtmlLocalized(55, 280, 470, 30, 1011118); // Grant a title to another member. + AddButton(20, 280, 4005, 4007, 10); + AddHtmlLocalized(55, 280, 470, 30, 1011118); // Grant a title to another member. - builder.AddButton(20, 310, 4005, 4007, 11); - builder.AddHtmlLocalized(55, 310, 470, 30, 1011119); // Move this guildstone. + AddButton(20, 310, 4005, 4007, 11); + AddHtmlLocalized(55, 310, 470, 30, 1011119); // Move this guildstone. - builder.AddButton(20, 360, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 360, 245, 30, 1011120); // Return to the main menu. + AddButton(20, 360, 4005, 4007, 1); + AddHtmlLocalized(55, 360, 245, 30, 1011120); // Return to the main menu. - builder.AddButton(300, 360, 4005, 4007, 0); - builder.AddHtmlLocalized(335, 360, 100, 30, 1011441); // EXIT + AddButton(300, 360, 4005, 4007, 0); + AddHtmlLocalized(335, 360, 100, 30, 1011441); // EXIT } public override void OnResponse(NetState state, in RelayInfo info) { - var from = state.Mobile; - if (GuildGump.BadLeader(from, _guild)) + if (GuildGump.BadLeader(m_Mobile, m_Guild)) { return; } @@ -111,19 +93,23 @@ namespace Server.Gumps { case 1: // Main menu { - GuildGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildGump(m_Mobile, m_Guild)); + break; } case 2: // Set guild name { - from.SendLocalizedMessage(1013060); // Enter new guild name (40 characters max): - from.Prompt = new GuildNamePrompt(from, _guild); + m_Mobile.SendLocalizedMessage(1013060); // Enter new guild name (40 characters max): + m_Mobile.Prompt = new GuildNamePrompt(m_Mobile, m_Guild); + break; } case 3: // Set guild abbreviation { - from.SendLocalizedMessage(1013061); // Enter new guild abbreviation (3 characters max): - from.Prompt = new GuildAbbrvPrompt(_guild); + m_Mobile.SendLocalizedMessage(1013061); // Enter new guild abbreviation (3 characters max): + m_Mobile.Prompt = new GuildAbbrvPrompt(m_Mobile, m_Guild); + break; } case 4: // Change guild type @@ -133,57 +119,72 @@ namespace Server.Gumps return; } - GuildChangeTypeGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildChangeTypeGump(m_Mobile, m_Guild)); + break; } case 5: // Set charter { - from.SendLocalizedMessage(1013071); // Enter the new guild charter (50 characters max): - from.Prompt = new GuildCharterPrompt(_guild); + m_Mobile.SendLocalizedMessage(1013071); // Enter the new guild charter (50 characters max): + m_Mobile.Prompt = new GuildCharterPrompt(m_Mobile, m_Guild); + break; } case 6: // Dismiss member { - GuildDismissGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildDismissGump(m_Mobile, m_Guild)); + break; } case 7: // War menu { - GuildWarAdminGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildWarAdminGump(m_Mobile, m_Guild)); + break; } case 8: // Administer candidates { - GuildAdminCandidatesGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildAdminCandidatesGump(m_Mobile, m_Guild)); + break; } case 9: // Set guildmaster's title { - from.SendLocalizedMessage(1013073); // Enter new guildmaster title (20 characters max): - from.Prompt = new GuildTitlePrompt(from, _guild); + m_Mobile.SendLocalizedMessage(1013073); // Enter new guildmaster title (20 characters max): + m_Mobile.Prompt = new GuildTitlePrompt(m_Mobile, m_Mobile, m_Guild); + break; } case 10: // Grant title { - GrantGuildTitleGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GrantGuildTitleGump(m_Mobile, m_Guild)); + break; } case 11: // Move guildstone { - if (_guild.Guildstone != null) + if (m_Guild.Guildstone != null) { - var item = new GuildTeleporter(_guild.Guildstone); + var item = new GuildTeleporter(m_Guild.Guildstone); - _guild.Teleporter?.Delete(); + m_Guild.Teleporter?.Delete(); - // Use the teleporting object placed in your backpack to move this guildstone. - from.SendLocalizedMessage(501133); + m_Mobile.SendLocalizedMessage( + 501133 + ); // Use the teleporting object placed in your backpack to move this guildstone. - from.AddToBackpack(item); - _guild.Teleporter = item; + m_Mobile.AddToBackpack(item); + m_Guild.Teleporter = item; } - DisplayTo(from, _guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildmasterGump(m_Mobile, m_Guild)); + break; } } diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/AdvancedSearch.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/AdvancedSearch.cs index 25f2a6779..1f544881a 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/AdvancedSearch.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/AdvancedSearch.cs @@ -8,53 +8,55 @@ namespace Server.Guilds public class GuildAdvancedSearchGump : BaseGuildGump { - private readonly SearchSelectionCallback _callback; - private readonly GuildDisplayType _display; + private readonly SearchSelectionCallback m_Callback; + private readonly GuildDisplayType m_Display; public GuildAdvancedSearchGump(PlayerMobile pm, Guild g, GuildDisplayType display, SearchSelectionCallback callback) : base(pm, g) { - _callback = callback; - _display = display; + m_Callback = callback; + m_Display = display; + PopulateGump(); } - protected override void BuildContent(ref DynamicGumpBuilder builder) + public override void PopulateGump() { - builder.AddHtmlLocalized(431, 43, 110, 26, 1062978, 0xF); // Diplomacy + base.PopulateGump(); - builder.AddHtmlLocalized(65, 80, 480, 26, 1063124, 0xF, true); // Advanced Search Options + AddHtmlLocalized(431, 43, 110, 26, 1062978, 0xF); // Diplomacy - // Showing All Guilds/w/Relation/Waiting Relation - builder.AddHtmlLocalized( + AddHtmlLocalized(65, 80, 480, 26, 1063124, 0xF, true); // Advanced Search Options + + AddHtmlLocalized( 65, 110, 480, 26, - 1063136 + (int)_display, + 1063136 + (int)m_Display, 0xF - ); + ); // Showing All Guilds/w/Relation/Waiting Relation - builder.AddGroup(1); - builder.AddRadio(75, 140, 0xD2, 0xD3, false, 2); - builder.AddHtmlLocalized(105, 140, 200, 26, 1063006, 0x0); // Show Guilds with Relationship - builder.AddRadio(75, 170, 0xD2, 0xD3, false, 1); - builder.AddHtmlLocalized(105, 170, 200, 26, 1063005, 0x0); // Show Guilds Awaiting Action - builder.AddRadio(75, 200, 0xD2, 0xD3, false, 0); - builder.AddHtmlLocalized(105, 200, 200, 26, 1063007, 0x0); // Show All Guilds + AddGroup(1); + AddRadio(75, 140, 0xD2, 0xD3, false, 2); + AddHtmlLocalized(105, 140, 200, 26, 1063006, 0x0); // Show Guilds with Relationship + AddRadio(75, 170, 0xD2, 0xD3, false, 1); + AddHtmlLocalized(105, 170, 200, 26, 1063005, 0x0); // Show Guilds Awaiting Action + AddRadio(75, 200, 0xD2, 0xD3, false, 0); + AddHtmlLocalized(105, 200, 200, 26, 1063007, 0x0); // Show All Guilds - builder.AddBackground(450, 370, 100, 26, 0x2486); - builder.AddButton(455, 375, 0x845, 0x846, 5); - builder.AddHtmlLocalized(480, 373, 60, 26, 1006044, 0x0); // OK - builder.AddBackground(340, 370, 100, 26, 0x2486); - builder.AddButton(345, 375, 0x845, 0x846, 0); - builder.AddHtmlLocalized(370, 373, 60, 26, 1006045, 0x0); // Cancel + AddBackground(450, 370, 100, 26, 0x2486); + AddButton(455, 375, 0x845, 0x846, 5); + AddHtmlLocalized(480, 373, 60, 26, 1006044, 0x0); // OK + AddBackground(340, 370, 100, 26, 0x2486); + AddButton(345, 375, 0x845, 0x846, 0); + AddHtmlLocalized(370, 373, 60, 26, 1006045, 0x0); // Cancel } public override void OnResponse(NetState sender, in RelayInfo info) { base.OnResponse(sender, info); - if (sender.Mobile is not PlayerMobile pm || !IsMember(pm, Guild)) + if (sender.Mobile is not PlayerMobile pm || !IsMember(pm, guild)) { return; } @@ -66,7 +68,7 @@ namespace Server.Guilds if (info.IsSwitched(i)) { var display = (GuildDisplayType)i; - _callback(display); + m_Callback(display); break; } } diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/BaseGuildGump.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/BaseGuildGump.cs index 44c33e0f0..ee4c36dce 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/BaseGuildGump.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/BaseGuildGump.cs @@ -1,4 +1,3 @@ -using System.Runtime.CompilerServices; using Server.Gumps; using Server.Misc; using Server.Mobiles; @@ -6,49 +5,39 @@ using Server.Network; namespace Server.Guilds { - public abstract class BaseGuildGump : DynamicGump + public abstract class BaseGuildGump : Gump { public override bool Singleton => true; - protected BaseGuildGump(PlayerMobile pm, Guild g, int x = 10, int y = 10) : base(x, y) + public BaseGuildGump(PlayerMobile pm, Guild g, int x = 10, int y = 10) : base(x, y) { - Guild = g; - Player = pm; + guild = g; + player = pm; } - protected Guild Guild { get; } + protected Guild guild { get; } - protected PlayerMobile Player { get; } + protected PlayerMobile player { get; } - // Subclasses that draw their own background/layout (e.g. OtherGuildInfo, - // WarDeclarationGump, GuildMemberInfoGump) can opt out of the standard - // 600x440 frame and three-button tab strip drawn at the top of the gump. - protected virtual bool ShowTabStrip => true; + // There's prolly a way to have all the vars set of inherited classes before something is called in the Ctor... but... I can't think of it right now, and I can't use Timer.DelayCall here :< - protected override void BuildLayout(ref DynamicGumpBuilder builder) + public virtual void PopulateGump() { - builder.AddPage(); + AddPage(0); - if (ShowTabStrip) - { - builder.AddBackground(0, 0, 600, 440, 0x24AE); - builder.AddBackground(66, 40, 150, 26, 0x2486); - builder.AddButton(71, 45, 0x845, 0x846, 1); - builder.AddHtmlLocalized(96, 43, 110, 26, 1063014, 0x0); // My Guild - builder.AddBackground(236, 40, 150, 26, 0x2486); - builder.AddButton(241, 45, 0x845, 0x846, 2); - builder.AddHtmlLocalized(266, 43, 110, 26, 1062974, 0x0); // Guild Roster - builder.AddBackground(401, 40, 150, 26, 0x2486); - builder.AddButton(406, 45, 0x845, 0x846, 3); - builder.AddHtmlLocalized(431, 43, 110, 26, 1062978, 0x0); // Diplomacy - builder.AddPage(1); - } - - BuildContent(ref builder); + AddBackground(0, 0, 600, 440, 0x24AE); + AddBackground(66, 40, 150, 26, 0x2486); + AddButton(71, 45, 0x845, 0x846, 1); + AddHtmlLocalized(96, 43, 110, 26, 1063014, 0x0); // My Guild + AddBackground(236, 40, 150, 26, 0x2486); + AddButton(241, 45, 0x845, 0x846, 2); + AddHtmlLocalized(266, 43, 110, 26, 1062974, 0x0); // Guild Roster + AddBackground(401, 40, 150, 26, 0x2486); + AddButton(406, 45, 0x845, 0x846, 3); + AddHtmlLocalized(431, 43, 110, 26, 1062978, 0x0); // Diplomacy + AddPage(1); } - protected abstract void BuildContent(ref DynamicGumpBuilder builder); - public override void OnResponse(NetState sender, in RelayInfo info) { if (sender.Mobile is not PlayerMobile pm) @@ -56,7 +45,7 @@ namespace Server.Guilds return; } - if (!IsMember(pm, Guild)) + if (!IsMember(pm, guild)) { return; } @@ -65,61 +54,89 @@ namespace Server.Guilds { case 1: { - pm.SendGump(new GuildInfoGump(pm, Guild)); + pm.SendGump(new GuildInfoGump(pm, guild)); break; } case 2: { - pm.SendGump(new GuildRosterGump(pm, Guild)); + pm.SendGump(new GuildRosterGump(pm, guild)); break; } case 3: { - pm.SendGump(new GuildDiplomacyGump(pm, Guild)); + pm.SendGump(new GuildDiplomacyGump(pm, guild)); break; } } } public static bool IsLeader(Mobile m, Guild g) => - !(m.Deleted || g.Disbanded || m is not PlayerMobile || m.AccessLevel < AccessLevel.GameMaster && g.Leader != m); + !(m.Deleted || g.Disbanded || m is not PlayerMobile || + m.AccessLevel < AccessLevel.GameMaster && g.Leader != m); public static bool IsMember(Mobile m, Guild g) => - !(m.Deleted || g.Disbanded || m is not PlayerMobile || m.AccessLevel < AccessLevel.GameMaster && !g.IsMember(m)); + !(m.Deleted || g.Disbanded || m is not PlayerMobile || + m.AccessLevel < AccessLevel.GameMaster && !g.IsMember(m)); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CheckProfanity(string s, int maxLength = 50) => - NameVerification.Validate( - s, - 1, - maxLength, - true, - true, - false, - 0, - ProfanityProtection.Exceptions, - ProfanityProtection.Disallowed, - ProfanityProtection.DisallowedSearchValues - ); + public static bool CheckProfanity(string s, int maxLength = 50) + { + // return NameVerification.Validate( s, 1, 50, true, true, false, int.MaxValue, ProfanityProtection.Exceptions, ProfanityProtection.Disallowed, ProfanityProtection.StartDisallowed ); //What am I doing wrong, this still allows chars like the <3 symbol... 3 AM. someone change this to use this - protected static void AddHtmlText( - ref DynamicGumpBuilder builder, - int x, - int y, - int width, - int height, - TextDefinition text, - bool back, - bool scroll - ) + // With testing on OSI, Guild stuff seems to follow a 'simpler' method of profanity protection + if (s.Length < 1 || s.Length > maxLength) + { + return false; + } + + var exceptions = ProfanityProtection.Exceptions; + + s = s.ToLower(); + + for (var i = 0; i < s.Length; ++i) + { + var c = s[i]; + + if (c is < 'a' or > 'z' && c is < '0' or > '9') + { + var except = false; + + for (var j = 0; !except && j < exceptions.Length; j++) + { + if (c == exceptions[j]) + { + except = true; + } + } + + if (!except) + { + return false; + } + } + } + + var disallowed = ProfanityProtection.Disallowed; + + for (var i = 0; i < disallowed.Length; i++) + { + if (s.IndexOfOrdinal(disallowed[i]) != -1) + { + return false; + } + } + + return true; + } + + public void AddHtmlText(int x, int y, int width, int height, TextDefinition text, bool back, bool scroll) { if (text?.Number > 0) { - builder.AddHtmlLocalized(x, y, width, height, text.Number, back, scroll); + AddHtmlLocalized(x, y, width, height, text.Number, back, scroll); } else if (text?.String != null) { - builder.AddHtml(x, y, width, height, text.String, background: back, scrollbar: scroll); + AddHtml(x, y, width, height, text.String, back, scroll); } } } diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/BaseGuildListGump.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/BaseGuildListGump.cs index c8c122a4f..8affb0cad 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/BaseGuildListGump.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/BaseGuildListGump.cs @@ -9,146 +9,137 @@ namespace Server.Guilds public abstract class BaseGuildListGump : BaseGuildGump { private const int itemsPerPage = 8; + private readonly IComparer m_Comparer; + private readonly InfoField[] m_Fields; + private readonly string m_Filter; + private bool m_Ascending; + private List m_List; + private int m_StartNumber; - private readonly InfoField[] _fields; - private readonly List _originalList; - private List _displayList; - private IComparer _comparer; - private bool _ascending; - private string _filter; - private int _startNumber; - - protected BaseGuildListGump( + public BaseGuildListGump( PlayerMobile pm, Guild g, List list, IComparer currentComparer, bool ascending, string filter, int startNumber, InfoField[] fields - ) : base(pm, g) + ) + : base(pm, g) { - _filter = filter.Trim(); - _comparer = currentComparer; - _fields = fields; - _ascending = ascending; - _startNumber = startNumber; - _originalList = list; + m_Filter = filter.Trim(); + + m_Comparer = currentComparer; + m_Fields = fields; + m_Ascending = ascending; + m_StartNumber = startNumber; + m_List = list; } - public virtual bool WillFilter => _filter.Length > 0; + public virtual bool WillFilter => m_Filter.Length > 0; - protected override void BuildContent(ref DynamicGumpBuilder builder) + public override void PopulateGump() { - // Build the filtered/sorted display list per-render so refreshes pick up - // the latest filter/sort/pagination state. + base.PopulateGump(); + + var list = m_List; if (WillFilter) { - var filtered = new List(_originalList.Count); - for (var i = 0; i < _originalList.Count; i++) + m_List = new List(); + for (var i = 0; i < list.Count; i++) { - if (!IsFiltered(_originalList[i], _filter)) + if (!IsFiltered(list[i], m_Filter)) { - filtered.Add(_originalList[i]); + m_List.Add(list[i]); } } - - _displayList = filtered; } else { - _displayList = new List(_originalList); + m_List = new List(list); } - _displayList.Sort(_comparer); - _startNumber = Math.Max(Math.Min(_startNumber, _displayList.Count - 1), 0); + m_List.Sort(m_Comparer); + m_StartNumber = Math.Max(Math.Min(m_StartNumber, m_List.Count - 1), 0); - builder.AddBackground(130, 75, 385, 30, 0xBB8); - builder.AddTextEntry(135, 80, 375, 30, 0x481, 1, _filter); - builder.AddButton(520, 75, 0x867, 0x868, 5); // Filter Button + AddBackground(130, 75, 385, 30, 0xBB8); + AddTextEntry(135, 80, 375, 30, 0x481, 1, m_Filter); + AddButton(520, 75, 0x867, 0x868, 5); // Filter Button var width = 0; - for (var i = 0; i < _fields.Length; i++) + for (var i = 0; i < m_Fields.Length; i++) { - var f = _fields[i]; + var f = m_Fields[i]; - builder.AddImageTiled(65 + width, 110, f.Width + 10, 26, 0xA40); - builder.AddImageTiled(67 + width, 112, f.Width + 6, 22, 0xBBC); - AddHtmlText(ref builder, 70 + width, 113, f.Width, 20, f.Name, false, false); + AddImageTiled(65 + width, 110, f.Width + 10, 26, 0xA40); + AddImageTiled(67 + width, 112, f.Width + 6, 22, 0xBBC); + AddHtmlText(70 + width, 113, f.Width, 20, f.Name, false, false); - var isComparer = _fields[i].Comparer.GetType() == _comparer.GetType(); + var isComparer = m_Fields[i].Comparer.GetType() == m_Comparer.GetType(); - var buttonId = isComparer ? _ascending ? 0x983 : 0x985 : 0x2716; + var ButtonID = isComparer ? m_Ascending ? 0x983 : 0x985 : 0x2716; - builder.AddButton(59 + width + f.Width, 117, buttonId, buttonId + (isComparer ? 1 : 0), 100 + i); + AddButton(59 + width + f.Width, 117, ButtonID, ButtonID + (isComparer ? 1 : 0), 100 + i); width += f.Width + 12; } - if (_startNumber <= 0) + if (m_StartNumber <= 0) { - builder.AddButton(65, 80, 0x15E3, 0x15E7, 0, GumpButtonType.Page); + AddButton(65, 80, 0x15E3, 0x15E7, 0, GumpButtonType.Page); } else { - builder.AddButton(65, 80, 0x15E3, 0x15E7, 6); // Back + AddButton(65, 80, 0x15E3, 0x15E7, 6); // Back } - if (_startNumber + itemsPerPage > _displayList.Count) + if (m_StartNumber + itemsPerPage > m_List.Count) { - builder.AddButton(95, 80, 0x15E1, 0x15E5, 0, GumpButtonType.Page); + AddButton(95, 80, 0x15E1, 0x15E5, 0, GumpButtonType.Page); } else { - builder.AddButton(95, 80, 0x15E1, 0x15E5, 7); // Forward + AddButton(95, 80, 0x15E1, 0x15E5, 7); // Forward } var itemNumber = 0; - if (_ascending) + if (m_Ascending) { - for (var i = _startNumber; i < _startNumber + itemsPerPage && i < _displayList.Count; i++) + for (var i = m_StartNumber; i < m_StartNumber + itemsPerPage && i < m_List.Count; i++) { - DrawEntry(ref builder, _displayList[i], i, itemNumber++); + DrawEntry(m_List[i], i, itemNumber++); } } else // descending, go from bottom of list to the top { - for (var i = _displayList.Count - 1 - _startNumber; - i >= 0 && i >= _displayList.Count - itemsPerPage - _startNumber; + for (var i = m_List.Count - 1 - m_StartNumber; + i >= 0 && i >= m_List.Count - itemsPerPage - m_StartNumber; i--) { - DrawEntry(ref builder, _displayList[i], i, itemNumber++); + DrawEntry(m_List[i], i, itemNumber++); } } - DrawEndingEntry(ref builder, itemNumber); - - BuildListExtras(ref builder); + DrawEndingEntry(itemNumber); } - protected virtual void BuildListExtras(ref DynamicGumpBuilder builder) - { - } - - protected virtual void DrawEndingEntry(ref DynamicGumpBuilder builder, int itemNumber) + public virtual void DrawEndingEntry(int itemNumber) { } public virtual bool HasRelationship(T o) => false; - protected virtual void DrawEntry(ref DynamicGumpBuilder builder, T o, int index, int itemNumber) + public virtual void DrawEntry(T o, int index, int itemNumber) { var width = 0; - var values = GetValuesFor(o, _fields.Length); - for (var j = 0; j < _fields.Length; j++) + for (var j = 0; j < m_Fields.Length; j++) { - var f = _fields[j]; + var f = m_Fields[j]; - builder.AddImageTiled(65 + width, 138 + itemNumber * 28, f.Width + 10, 26, 0xA40); - builder.AddImageTiled(67 + width, 140 + itemNumber * 28, f.Width + 6, 22, 0xBBC); + AddImageTiled(65 + width, 138 + itemNumber * 28, f.Width + 10, 26, 0xA40); + AddImageTiled(67 + width, 140 + itemNumber * 28, f.Width + 6, 22, 0xBBC); AddHtmlText( - ref builder, 70 + width, 141 + itemNumber * 28, f.Width, 20, - values[j], + GetValuesFor(o, m_Fields.Length)[j], false, false ); @@ -158,23 +149,22 @@ namespace Server.Guilds if (HasRelationship(o)) { - builder.AddButton(40, 143 + itemNumber * 28, 0x8AF, 0x8AF, 200 + index); // Info Button + AddButton(40, 143 + itemNumber * 28, 0x8AF, 0x8AF, 200 + index); // Info Button } else { - builder.AddButton(40, 143 + itemNumber * 28, 0x4B9, 0x4BA, 200 + index); // Info Button + AddButton(40, 143 + itemNumber * 28, 0x4B9, 0x4BA, 200 + index); // Info Button } } protected abstract TextDefinition[] GetValuesFor(T o, int aryLength); - protected abstract bool IsFiltered(T o, string filter); public override void OnResponse(NetState sender, in RelayInfo info) { base.OnResponse(sender, info); - if (sender.Mobile is not PlayerMobile pm || !IsMember(pm, Guild)) + if (sender.Mobile is not PlayerMobile pm || !IsMember(pm, guild)) { return; } @@ -186,59 +176,52 @@ namespace Server.Guilds case 5: // Filter { var t = info.GetTextEntry(1); - _filter = (t ?? "").Trim(); - _startNumber = 0; - pm.SendGump(this); - return; + pm.SendGump(GetResentGump(player, guild, m_Comparer, m_Ascending, t ?? "", 0)); + break; } case 6: // Back { - _startNumber -= itemsPerPage; - pm.SendGump(this); - return; + pm.SendGump( + GetResentGump(player, guild, m_Comparer, m_Ascending, m_Filter, m_StartNumber - itemsPerPage) + ); + break; } case 7: // Forward { - _startNumber += itemsPerPage; - pm.SendGump(this); - return; + pm.SendGump( + GetResentGump(player, guild, m_Comparer, m_Ascending, m_Filter, m_StartNumber + itemsPerPage) + ); + break; } } - if (id >= 100 && id < 100 + _fields.Length) + if (id >= 100 && id < 100 + m_Fields.Length) { - var comparer = _fields[id - 100].Comparer; + var comparer = m_Fields[id - 100].Comparer; - if (_comparer.GetType() == comparer.GetType()) + if (m_Comparer.GetType() == comparer.GetType()) { - _ascending = !_ascending; - } - else - { - _comparer = comparer; + m_Ascending = !m_Ascending; } - _startNumber = 0; - pm.SendGump(this); + pm.SendGump(GetResentGump(player, guild, comparer, m_Ascending, m_Filter, 0)); } - else if (id >= 200) + else if (id >= 200 && id < 200 + m_List.Count) { - // The display list is rebuilt every render, so we use the most recent - // build to resolve the clicked entry. - var list = _displayList ?? _originalList; - var idx = id - 200; - if (idx < list.Count) - { - pm.SendGump(GetObjectInfoGump(Player, Guild, list[idx])); - } + pm.SendGump(GetObjectInfoGump(player, guild, m_List[id - 200])); } } - public abstract BaseGump GetObjectInfoGump(PlayerMobile pm, Guild g, T o); + public abstract Gump GetResentGump( + PlayerMobile pm, Guild g, IComparer comparer, bool ascending, string filter, + int startNumber + ); + + public abstract Gump GetObjectInfoGump(PlayerMobile pm, Guild g, T o); public void ResendGump() { - Player.SendGump(this); + player.SendGump(GetResentGump(player, guild, m_Comparer, m_Ascending, m_Filter, m_StartNumber)); } } diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/CreateGuildGump.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/CreateGuildGump.cs index 21d49b365..0029f8b93 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/CreateGuildGump.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/CreateGuildGump.cs @@ -4,55 +4,43 @@ using Server.Network; namespace Server.Guilds { - public class CreateGuildGump : DynamicGump + public class CreateGuildGump : Gump { - private readonly PlayerMobile _player; - private readonly string _guildName; - private readonly string _guildAbbrev; - public override bool Singleton => true; public CreateGuildGump(PlayerMobile pm, string guildName = "Guild Name", string guildAbbrev = "") : base(10, 10) { - _player = pm; - _guildName = guildName; - _guildAbbrev = guildAbbrev; - pm.CloseGump(); - } - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(0, 0, 500, 300, 0x2422); - builder.AddHtmlLocalized(25, 20, 450, 25, 1062939, 0x0, true); //
GUILD MENU
+ AddBackground(0, 0, 500, 300, 0x2422); + AddHtmlLocalized(25, 20, 450, 25, 1062939, 0x0, true); //
GUILD MENU
- // As you are not a member of any guild, you can create your own by providing a unique guild name and - // paying the standard guild registration fee. - builder.AddHtmlLocalized(25, 60, 450, 60, 1062940, 0x0); + // As you are not a member of any guild, you can create your own by providing a unique guild name and paying the standard guild registration fee. + AddHtmlLocalized(25, 60, 450, 60, 1062940, 0x0); - builder.AddHtmlLocalized(25, 135, 120, 25, 1062941, 0x0); // Registration Fee: - builder.AddLabel(155, 135, 0x481, $"{Guild.RegistrationFee}"); - builder.AddHtmlLocalized(25, 165, 120, 25, 1011140, 0x0); // Enter Guild Name: - builder.AddBackground(155, 160, 320, 26, 0xBB8); - builder.AddTextEntry(160, 163, 315, 21, 0x481, 5, _guildName); - builder.AddHtmlLocalized(25, 191, 120, 26, 1063035, 0x0); // Abbreviation: - builder.AddBackground(155, 186, 320, 26, 0xBB8); - builder.AddTextEntry(160, 189, 315, 21, 0x481, 6, _guildAbbrev); - builder.AddButton(415, 217, 0xF7, 0xF8, 1); - builder.AddButton(345, 217, 0xF2, 0xF1, 0); + AddHtmlLocalized(25, 135, 120, 25, 1062941, 0x0); // Registration Fee: + AddLabel(155, 135, 0x481, Guild.RegistrationFee.ToString()); + AddHtmlLocalized(25, 165, 120, 25, 1011140, 0x0); // Enter Guild Name: + AddBackground(155, 160, 320, 26, 0xBB8); + AddTextEntry(160, 163, 315, 21, 0x481, 5, guildName); + AddHtmlLocalized(25, 191, 120, 26, 1063035, 0x0); // Abbreviation: + AddBackground(155, 186, 320, 26, 0xBB8); + AddTextEntry(160, 189, 315, 21, 0x481, 6, guildAbbrev); + AddButton(415, 217, 0xF7, 0xF8, 1); + AddButton(345, 217, 0xF2, 0xF1, 0); - if (_player.AcceptGuildInvites) + if (pm.AcceptGuildInvites) { - builder.AddButton(20, 260, 0xD2, 0xD3, 2); + AddButton(20, 260, 0xD2, 0xD3, 2); } else { - builder.AddButton(20, 260, 0xD3, 0xD2, 2); + AddButton(20, 260, 0xD3, 0xD2, 2); } - builder.AddHtmlLocalized(45, 260, 200, 30, 1062943, 0x0); // Ignore Guild Invites + AddHtmlLocalized(45, 260, 200, 30, 1062943, 0x0); // Ignore Guild Invites } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/DiplomacyGump.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/DiplomacyGump.cs index 2fd3cec7e..65a924461 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/DiplomacyGump.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/DiplomacyGump.cs @@ -14,8 +14,8 @@ namespace Server.Guilds public class GuildDiplomacyGump : BaseGuildListGump { - private readonly TextDefinition _lowerText; - private GuildDisplayType _display; + private readonly TextDefinition m_LowerText; + private GuildDisplayType m_Display; public GuildDiplomacyGump(PlayerMobile pm, Guild g) : this( @@ -78,15 +78,17 @@ namespace Server.Guilds ascending, filter, startNumber, - [ + new[] + { new InfoField(1062954, 280, NameComparer.Instance), // Guild Name new InfoField(1062957, 50, AbbrevComparer.Instance), // Abbrev new InfoField(1062958, 120, new StatusComparer(g)) // Guild Title - ] + } ) { - _display = display; - _lowerText = lowerText; + m_Display = display; + m_LowerText = lowerText; + PopulateGump(); } protected virtual bool AllowAdvancedSearch => true; @@ -95,7 +97,7 @@ namespace Server.Guilds { get { - if (_display == GuildDisplayType.All) + if (m_Display == GuildDisplayType.All) { return base.WillFilter; } @@ -104,23 +106,25 @@ namespace Server.Guilds } } - protected override void BuildListExtras(ref DynamicGumpBuilder builder) + public override void PopulateGump() { - builder.AddHtmlLocalized(431, 43, 110, 26, 1062978, 0xF); // Diplomacy + base.PopulateGump(); + + AddHtmlLocalized(431, 43, 110, 26, 1062978, 0xF); // Diplomacy } protected override TextDefinition[] GetValuesFor(Guild g, int aryLength) { var defs = new TextDefinition[aryLength]; - defs[0] = g == Guild ? g.Name.Color(0x006600) : g.Name; + defs[0] = g == guild ? g.Name.Color(0x006600) : g.Name; defs[1] = g.Abbreviation; defs[2] = 3000085; // Peace - if (Guild.IsAlly(g)) + if (guild.IsAlly(g)) { - if (Guild.Alliance.Leader == g) + if (guild.Alliance.Leader == g) { defs[2] = 1063237; // Alliance Leader } @@ -129,7 +133,7 @@ namespace Server.Guilds defs[2] = 1062964; // Ally } } - else if (Guild.IsWar(g)) + else if (guild.IsWar(g)) { defs[2] = 3000086; // War } @@ -139,17 +143,17 @@ namespace Server.Guilds public override bool HasRelationship(Guild g) { - if (g == Guild) + if (g == guild) { return false; } - if (Guild.FindPendingWar(g) != null) + if (guild.FindPendingWar(g) != null) { return true; } - var alliance = Guild.Alliance; + var alliance = guild.Alliance; if (alliance != null) { @@ -157,7 +161,7 @@ namespace Server.Guilds if (leader != null) { - if (Guild == leader && alliance.IsPendingMember(g) || g == leader && alliance.IsPendingMember(Guild)) + if (guild == leader && alliance.IsPendingMember(g) || g == leader && alliance.IsPendingMember(guild)) { return true; } @@ -171,22 +175,25 @@ namespace Server.Guilds return false; } - protected override void DrawEndingEntry(ref DynamicGumpBuilder builder, int itemNumber) + public override void DrawEndingEntry(int itemNumber) { - if (_lowerText?.Number > 0) + // AddHtmlLocalized( 66, 153 + itemNumber * 28, 280, 26, 1063136 + (int)m_Display, 0xF, false, false ); // Showing All Guilds/Awaiting Action/ w/Relation Ship + // AddHtmlText( 66, 153 + itemNumber * 28, 280, 26, m_LowerText, false, false ); + + if (m_LowerText?.Number > 0) { - builder.AddHtmlLocalized(66, 153 + itemNumber * 28, 280, 26, _lowerText.Number, 0xF); + AddHtmlLocalized(66, 153 + itemNumber * 28, 280, 26, m_LowerText.Number, 0xF); } - else if (_lowerText?.String != null) + else if (m_LowerText?.String != null) { - builder.AddHtml(66, 153 + itemNumber * 28, 280, 26, _lowerText.String.Color(0x99)); + AddHtml(66, 153 + itemNumber * 28, 280, 26, m_LowerText.String.Color(0x99)); } if (AllowAdvancedSearch) { - builder.AddBackground(350, 148 + itemNumber * 28, 200, 26, 0x2486); - builder.AddButton(355, 153 + itemNumber * 28, 0x845, 0x846, 8); - builder.AddHtmlLocalized(380, 151 + itemNumber * 28, 160, 26, 1063083, 0x0); // Advanced Search + AddBackground(350, 148 + itemNumber * 28, 200, 26, 0x2486); + AddButton(355, 153 + itemNumber * 28, 0x845, 0x846, 8); + AddHtmlLocalized(380, 151 + itemNumber * 28, 160, 26, 1063083, 0x0); // Advanced Search } } @@ -197,12 +204,12 @@ namespace Server.Guilds return true; } - switch (_display) + switch (m_Display) { case GuildDisplayType.Relations: { // As per OSI, only the guild leader wars show up under the sorting by relation - return !(Guild.FindActiveWar(g) != null || Guild.IsAlly(g)); + return !(guild.FindActiveWar(g) != null || guild.IsAlly(g)); } case GuildDisplayType.AwaitingAction: { @@ -213,9 +220,15 @@ namespace Server.Guilds return !(g.Name.InsensitiveContains(filter) || g.Abbreviation.InsensitiveContains(filter)); } - public override BaseGump GetObjectInfoGump(PlayerMobile pm, Guild g, Guild o) + public override Gump GetResentGump( + PlayerMobile pm, Guild g, IComparer comparer, bool ascending, + string filter, int startNumber + ) => + new GuildDiplomacyGump(pm, g, comparer, ascending, filter, startNumber, m_Display); + + public override Gump GetObjectInfoGump(PlayerMobile pm, Guild g, Guild o) { - if (Guild == o) + if (guild == o) { return new GuildInfoGump(pm, g); } @@ -227,20 +240,20 @@ namespace Server.Guilds { base.OnResponse(sender, info); - if (sender.Mobile is not PlayerMobile pm || !IsMember(pm, Guild)) + if (sender.Mobile is not PlayerMobile pm || !IsMember(pm, guild)) { return; } if (AllowAdvancedSearch && info.ButtonID == 8) { - pm.SendGump(new GuildAdvancedSearchGump(pm, Guild, _display, AdvancedSearch_Callback)); + pm.SendGump(new GuildAdvancedSearchGump(pm, guild, m_Display, AdvancedSearch_Callback)); } } public void AdvancedSearch_Callback(GuildDisplayType display) { - _display = display; + m_Display = display; ResendGump(); } @@ -271,9 +284,9 @@ namespace Server.Guilds private class StatusComparer : IComparer { - private readonly Guild _guild; + private readonly Guild m_Guild; - public StatusComparer(Guild g) => _guild = g; + public StatusComparer(Guild g) => m_Guild = g; public int Compare(Guild x, Guild y) { @@ -295,20 +308,20 @@ namespace Server.Guilds var aStatus = GuildCompareStatus.Peace; var bStatus = GuildCompareStatus.Peace; - if (_guild.IsAlly(x)) + if (m_Guild.IsAlly(x)) { aStatus = GuildCompareStatus.Ally; } - else if (_guild.IsWar(x)) + else if (m_Guild.IsWar(x)) { aStatus = GuildCompareStatus.War; } - if (_guild.IsAlly(y)) + if (m_Guild.IsAlly(y)) { bStatus = GuildCompareStatus.Ally; } - else if (_guild.IsWar(y)) + else if (m_Guild.IsWar(y)) { bStatus = GuildCompareStatus.War; } diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/GuildInfoGump.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/GuildInfoGump.cs index 35bd7f8f9..37d21beed 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/GuildInfoGump.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/GuildInfoGump.cs @@ -8,76 +8,81 @@ namespace Server.Guilds { public class GuildInfoGump : BaseGuildGump { - private readonly bool _isResigning; + private readonly bool m_IsResigning; - public GuildInfoGump(PlayerMobile pm, Guild g, bool isResigning = false) : base(pm, g) => _isResigning = isResigning; - - protected override void BuildContent(ref DynamicGumpBuilder builder) + public GuildInfoGump(PlayerMobile pm, Guild g, bool isResigning = false) : base(pm, g) { - var isLeader = IsLeader(Player, Guild); + m_IsResigning = isResigning; + PopulateGump(); + } - builder.AddHtmlLocalized(96, 43, 110, 26, 1063014, 0xF); // My Guild + public override void PopulateGump() + { + var isLeader = IsLeader(player, guild); + base.PopulateGump(); - builder.AddImageTiled(65, 80, 160, 26, 0xA40); - builder.AddImageTiled(67, 82, 156, 22, 0xBBC); - builder.AddHtmlLocalized(70, 83, 150, 20, 1062954, 0x0); // Guild Name - builder.AddHtml(233, 84, 320, 26, Guild.Name); + AddHtmlLocalized(96, 43, 110, 26, 1063014, 0xF); // My Guild - builder.AddImageTiled(65, 114, 160, 26, 0xA40); - builder.AddImageTiled(67, 116, 156, 22, 0xBBC); - builder.AddHtmlLocalized(70, 117, 150, 20, 1063025, 0x0); // Alliance + AddImageTiled(65, 80, 160, 26, 0xA40); + AddImageTiled(67, 82, 156, 22, 0xBBC); + AddHtmlLocalized(70, 83, 150, 20, 1062954, 0x0); // Guild Name + AddHtml(233, 84, 320, 26, guild.Name); - if (Guild.Alliance?.IsMember(Guild) == true) + AddImageTiled(65, 114, 160, 26, 0xA40); + AddImageTiled(67, 116, 156, 22, 0xBBC); + AddHtmlLocalized(70, 117, 150, 20, 1063025, 0x0); // Alliance + + if (guild.Alliance?.IsMember(guild) == true) { - builder.AddHtml(233, 118, 320, 26, Guild.Alliance.Name); - builder.AddButton(40, 120, 0x4B9, 0x4BA, 6); // Alliance Roster + AddHtml(233, 118, 320, 26, guild.Alliance.Name); + AddButton(40, 120, 0x4B9, 0x4BA, 6); // Alliance Roster } if (Guild.OrderChaos && isLeader) { - builder.AddButton(40, 154, 0x4B9, 0x4BA, 100); // Guild Faction + AddButton(40, 154, 0x4B9, 0x4BA, 100); // Guild Faction } - builder.AddImageTiled(65, 148, 160, 26, 0xA40); - builder.AddImageTiled(67, 150, 156, 22, 0xBBC); - builder.AddHtmlLocalized(70, 151, 150, 20, 1063084, 0x0); // Guild Faction + AddImageTiled(65, 148, 160, 26, 0xA40); + AddImageTiled(67, 150, 156, 22, 0xBBC); + AddHtmlLocalized(70, 151, 150, 20, 1063084, 0x0); // Guild Faction GuildType gt; Faction f; - if ((gt = Guild.Type) != GuildType.Regular) + if ((gt = guild.Type) != GuildType.Regular) { - builder.AddHtml(233, 152, 320, 26, $"{gt}"); + AddHtml(233, 152, 320, 26, gt.ToString()); } - else if ((f = Faction.Find(Guild.Leader)) != null) + else if ((f = Faction.Find(guild.Leader)) != null) { - builder.AddHtml(233, 152, 320, 26, $"{f}"); + AddHtml(233, 152, 320, 26, f.ToString()); } - builder.AddImageTiled(65, 196, 480, 4, 0x238D); + AddImageTiled(65, 196, 480, 4, 0x238D); - var s = Guild.Charter.DefaultIfNullOrEmpty("The guild leader has not yet set the guild charter."); + var s = guild.Charter.DefaultIfNullOrEmpty("The guild leader has not yet set the guild charter."); - builder.AddHtml(65, 216, 480, 80, s, background: true, scrollbar: true); + AddHtml(65, 216, 480, 80, s, true, true); if (isLeader) { - builder.AddButton(40, 251, 0x4B9, 0x4BA, 4); // Charter Edit button + AddButton(40, 251, 0x4B9, 0x4BA, 4); // Charter Edit button } - s = Guild.Website.DefaultIfNullOrEmpty("Guild website not yet set."); + s = guild.Website.DefaultIfNullOrEmpty("Guild website not yet set."); - builder.AddHtml(65, 306, 480, 30, s, background: true); + AddHtml(65, 306, 480, 30, s, true); if (isLeader) { - builder.AddButton(40, 313, 0x4B9, 0x4BA, 5); // Website Edit button + AddButton(40, 313, 0x4B9, 0x4BA, 5); // Website Edit button } - builder.AddCheckbox(65, 370, 0xD2, 0xD3, Player.DisplayGuildTitle, 0); - builder.AddHtmlLocalized(95, 370, 150, 26, 1063085, 0x0); // Show Guild Title - builder.AddBackground(450, 370, 100, 26, 0x2486); + AddCheck(65, 370, 0xD2, 0xD3, player.DisplayGuildTitle, 0); + AddHtmlLocalized(95, 370, 150, 26, 1063085, 0x0); // Show Guild Title + AddBackground(450, 370, 100, 26, 0x2486); - builder.AddButton(455, 375, 0x845, 0x846, 7); - builder.AddHtmlLocalized(480, 373, 60, 26, 3006115, _isResigning ? 0x5000 : 0); // Resign + AddButton(455, 375, 0x845, 0x846, 7); + AddHtmlLocalized(480, 373, 60, 26, 3006115, m_IsResigning ? 0x5000 : 0); // Resign } public override void OnResponse(NetState sender, in RelayInfo info) @@ -86,7 +91,7 @@ namespace Server.Guilds var pm = (PlayerMobile)sender.Mobile; - if (!IsMember(pm, Guild)) + if (!IsMember(pm, guild)) { return; } @@ -98,23 +103,27 @@ namespace Server.Guilds // 1-3 handled by base.OnResponse case 4: { - if (IsLeader(pm, Guild)) + if (IsLeader(pm, guild)) { pm.SendLocalizedMessage(1013071); // Enter the new guild charter (50 characters max): - // Have the same callback handle both canceling and deletion cause the 2nd callback would - // just get a text of "" - pm.BeginPrompt(SetCharter_Callback, true); + pm.BeginPrompt( + SetCharter_Callback, + true + ); // Have the same callback handle both canceling and deletion cause the 2nd callback would just get a text of "" } break; } case 5: { - if (IsLeader(pm, Guild)) + if (IsLeader(pm, guild)) { pm.SendLocalizedMessage(1013072); // Enter the new website for the guild (50 characters max): - pm.BeginPrompt(SetWebsite_Callback, true); + pm.BeginPrompt( + SetWebsite_Callback, + true + ); // Have the same callback handle both canceling and deletion cause the 2nd callback would just get a text of "" } break; @@ -122,9 +131,9 @@ namespace Server.Guilds case 6: { // Alliance Roster - if (Guild.Alliance?.IsMember(Guild) == true) + if (guild.Alliance?.IsMember(guild) == true) { - pm.SendGump(new AllianceInfo.AllianceRosterGump(pm, Guild, Guild.Alliance)); + pm.SendGump(new AllianceInfo.AllianceRosterGump(pm, guild, guild.Alliance)); } break; @@ -132,14 +141,14 @@ namespace Server.Guilds case 7: { // Resign - if (!_isResigning) + if (!m_IsResigning) { pm.SendLocalizedMessage(1063332); // Are you sure you wish to resign from your guild? - pm.SendGump(new GuildInfoGump(pm, Guild, true)); + pm.SendGump(new GuildInfoGump(pm, guild, true)); } else { - Guild.RemoveMember(pm, 1063411); // You resign from your guild. + guild.RemoveMember(pm, 1063411); // You resign from your guild. } break; @@ -147,9 +156,9 @@ namespace Server.Guilds case 100: // Custom code to support Order/Chaos in the new guild system { // Guild Faction - if (Guild.OrderChaos && IsLeader(pm, Guild)) + if (Guild.OrderChaos && IsLeader(pm, guild)) { - GuildChangeTypeGump.DisplayTo(pm, Guild); + pm.SendGump(new GuildChangeTypeGump(pm, guild), true); } break; @@ -159,7 +168,7 @@ namespace Server.Guilds public void SetCharter_Callback(Mobile from, string text) { - if (!IsLeader(from, Guild)) + if (!IsLeader(from, guild)) { return; } @@ -172,14 +181,14 @@ namespace Server.Guilds } else { - Guild.Charter = charter; + guild.Charter = charter; from.SendLocalizedMessage(1070775); // You submit a new guild charter. } } public void SetWebsite_Callback(Mobile from, string text) { - if (!IsLeader(from, Guild)) + if (!IsLeader(from, guild)) { return; } @@ -192,7 +201,7 @@ namespace Server.Guilds } else { - Guild.Website = site; + guild.Website = site; from.SendLocalizedMessage(1070778); // You submit a new guild website. } } diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/GuildInvitationRequest.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/GuildInvitationRequest.cs index 8fb342ff9..6c7922fd6 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/GuildInvitationRequest.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/GuildInvitationRequest.cs @@ -6,30 +6,39 @@ namespace Server.Guilds { public class GuildInvitationRequest : BaseGuildGump { - private readonly PlayerMobile _inviter; + private readonly PlayerMobile m_Inviter; public GuildInvitationRequest(PlayerMobile pm, Guild g, PlayerMobile inviter) : base(pm, g) { - _inviter = inviter; + m_Inviter = inviter; + + PopulateGump(); } - protected override bool ShowTabStrip => false; - - protected override void BuildContent(ref DynamicGumpBuilder builder) + public override void PopulateGump() { - builder.AddBackground(0, 0, 350, 170, 0x2422); - //
You have been invited to join a guild! (Warning: Accepting will make you attackable!)
- builder.AddHtmlLocalized(25, 20, 300, 45, 1062946, 0x0, true); - builder.AddHtml(25, 75, 300, 25, $"
{Guild.Name}
", background: true); - builder.AddButton(265, 130, 0xF7, 0xF8, 1); - builder.AddButton(195, 130, 0xF2, 0xF1, 0); - builder.AddButton(20, 130, 0xD2, 0xD3, 2); - builder.AddHtmlLocalized(45, 130, 150, 30, 1062943, 0x0); // Ignore Guild Invites + AddPage(0); + + AddBackground(0, 0, 350, 170, 0x2422); + AddHtmlLocalized( + 25, + 20, + 300, + 45, + 1062946, + 0x0, + true + ); //
You have been invited to join a guild! (Warning: Accepting will make you attackable!)
+ AddHtml(25, 75, 300, 25, $"
{guild.Name}
", true); + AddButton(265, 130, 0xF7, 0xF8, 1); + AddButton(195, 130, 0xF2, 0xF1, 0); + AddButton(20, 130, 0xD2, 0xD3, 2); + AddHtmlLocalized(45, 130, 150, 30, 1062943, 0x0); // Ignore Guild Invites } public override void OnResponse(NetState sender, in RelayInfo info) { - if (Guild.Disbanded || Player.Guild != null) + if (guild.Disbanded || player.Guild != null) { return; } @@ -38,23 +47,27 @@ namespace Server.Guilds { case 0: { - // ~1_val~ has declined your invitation to join ~2_val~. - _inviter.SendLocalizedMessage(1063250, $"{Player.Name}\t{Guild.Name}"); + m_Inviter.SendLocalizedMessage( + 1063250, + $"{player.Name}\t{guild.Name}" + ); // ~1_val~ has declined your invitation to join ~2_val~. break; } case 1: { - Guild.AddMember(Player); - Player.SendLocalizedMessage(1063056, Guild.Name); // You have joined ~1_val~. - // ~1_val~ has accepted your invitation to join ~2_val~. - _inviter.SendLocalizedMessage(1063249, $"{Player.Name}\t{Guild.Name}"); + guild.AddMember(player); + player.SendLocalizedMessage(1063056, guild.Name); // You have joined ~1_val~. + m_Inviter.SendLocalizedMessage( + 1063249, + $"{player.Name}\t{guild.Name}" + ); // ~1_val~ has accepted your invitation to join ~2_val~. break; } case 2: { - Player.AcceptGuildInvites = false; - Player.SendLocalizedMessage(1070698); // You are now ignoring guild invitations. + player.AcceptGuildInvites = false; + player.SendLocalizedMessage(1070698); // You are now ignoring guild invitations. break; } diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/GuildMemberInfoGump.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/GuildMemberInfoGump.cs index b26755187..960988946 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/GuildMemberInfoGump.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/GuildMemberInfoGump.cs @@ -7,74 +7,75 @@ namespace Server.Guilds { public class GuildMemberInfoGump : BaseGuildGump { - private readonly PlayerMobile _member; - private readonly bool _toKick; - private readonly bool _toLeader; + private readonly PlayerMobile m_Member; + private readonly bool m_toKick; + private readonly bool m_ToLeader; public GuildMemberInfoGump( PlayerMobile pm, Guild g, PlayerMobile member, bool toKick, bool toPromoteToLeader ) : base(pm, g, 10, 40) { - _toLeader = toPromoteToLeader; - _toKick = toKick; - _member = member; + m_ToLeader = toPromoteToLeader; + m_toKick = toKick; + m_Member = member; + PopulateGump(); } - protected override bool ShowTabStrip => false; - - protected override void BuildContent(ref DynamicGumpBuilder builder) + public override void PopulateGump() { - builder.AddBackground(0, 0, 350, 255, 0x242C); - builder.AddHtmlLocalized(20, 15, 310, 26, 1063018, 0x0); //
Guild Member Information
- builder.AddImageTiled(20, 40, 310, 2, 0x2711); + AddPage(0); - builder.AddHtmlLocalized(20, 50, 150, 26, 1062955, 0x0, true); // Name - builder.AddHtml(180, 53, 150, 26, _member.Name); + AddBackground(0, 0, 350, 255, 0x242C); + AddHtmlLocalized(20, 15, 310, 26, 1063018, 0x0); //
Guild Member Information
+ AddImageTiled(20, 40, 310, 2, 0x2711); - builder.AddHtmlLocalized(20, 80, 150, 26, 1062956, 0x0, true); // Rank - builder.AddHtmlLocalized(180, 83, 150, 26, _member.GuildRank.Name, 0x0); + AddHtmlLocalized(20, 50, 150, 26, 1062955, 0x0, true); // Name + AddHtml(180, 53, 150, 26, m_Member.Name); - builder.AddHtmlLocalized(20, 110, 150, 26, 1062953, 0x0, true); // Guild Title - builder.AddHtml(180, 113, 150, 26, _member.GuildTitle); - builder.AddImageTiled(20, 142, 310, 2, 0x2711); + AddHtmlLocalized(20, 80, 150, 26, 1062956, 0x0, true); // Rank + AddHtmlLocalized(180, 83, 150, 26, m_Member.GuildRank.Name, 0x0); - builder.AddBackground(20, 150, 310, 26, 0x2486); - builder.AddButton(25, 155, 0x845, 0x846, 4); - builder.AddHtmlLocalized( + AddHtmlLocalized(20, 110, 150, 26, 1062953, 0x0, true); // Guild Title + AddHtml(180, 113, 150, 26, m_Member.GuildTitle); + AddImageTiled(20, 142, 310, 2, 0x2711); + + AddBackground(20, 150, 310, 26, 0x2486); + AddButton(25, 155, 0x845, 0x846, 4); + AddHtmlLocalized( 50, 153, 270, 26, - _member == Player.GuildFealty && Guild.Leader != _member ? 1063082 : 1062996, + m_Member == player.GuildFealty && guild.Leader != m_Member ? 1063082 : 1062996, 0x0 ); // Clear/Cast Vote For This Member - builder.AddBackground(20, 180, 150, 26, 0x2486); - builder.AddButton(25, 185, 0x845, 0x846, 1); - builder.AddHtmlLocalized(50, 183, 110, 26, 1062993, _toLeader ? 0x990000 : 0); // Promote + AddBackground(20, 180, 150, 26, 0x2486); + AddButton(25, 185, 0x845, 0x846, 1); + AddHtmlLocalized(50, 183, 110, 26, 1062993, m_ToLeader ? 0x990000 : 0); // Promote - builder.AddBackground(180, 180, 150, 26, 0x2486); - builder.AddButton(185, 185, 0x845, 0x846, 3); - builder.AddHtmlLocalized(210, 183, 110, 26, 1062995, 0x0); // Set Guild Title + AddBackground(180, 180, 150, 26, 0x2486); + AddButton(185, 185, 0x845, 0x846, 3); + AddHtmlLocalized(210, 183, 110, 26, 1062995, 0x0); // Set Guild Title - builder.AddBackground(20, 210, 150, 26, 0x2486); - builder.AddButton(25, 215, 0x845, 0x846, 2); - builder.AddHtmlLocalized(50, 213, 110, 26, 1062994, 0x0); // Demote + AddBackground(20, 210, 150, 26, 0x2486); + AddButton(25, 215, 0x845, 0x846, 2); + AddHtmlLocalized(50, 213, 110, 26, 1062994, 0x0); // Demote - builder.AddBackground(180, 210, 150, 26, 0x2486); - builder.AddButton(185, 215, 0x845, 0x846, 5); - builder.AddHtmlLocalized(210, 213, 110, 26, 1062997, _toKick ? 0x5000 : 0); // Kick + AddBackground(180, 210, 150, 26, 0x2486); + AddButton(185, 215, 0x845, 0x846, 5); + AddHtmlLocalized(210, 213, 110, 26, 1062997, m_toKick ? 0x5000 : 0); // Kick } public override void OnResponse(NetState sender, in RelayInfo info) { - if (sender.Mobile is not PlayerMobile pm || !IsMember(pm, Guild) || !IsMember(_member, Guild)) + if (sender.Mobile is not PlayerMobile pm || !IsMember(pm, guild) || !IsMember(m_Member, guild)) { return; } var playerRank = pm.GuildRank; - var targetRank = _member.GuildRank; + var targetRank = m_Member.GuildRank; switch (info.ButtonID) { @@ -88,26 +89,34 @@ namespace Server.Guilds if (targetRank == RankDefinition.Leader) { - if (_toLeader) + if (m_ToLeader) { - _member.GuildRank = targetRank; - // The guild information for ~1_val~ has been updated. - pm.SendLocalizedMessage(1063156, _member.Name); - pm.SendLocalizedMessage(1063156, pm.Name); - Guild.Leader = _member; + m_Member.GuildRank = targetRank; + pm.SendLocalizedMessage( + 1063156, + m_Member.Name + ); // The guild information for ~1_val~ has been updated. + pm.SendLocalizedMessage( + 1063156, + pm.Name + ); // The guild information for ~1_val~ has been updated. + guild.Leader = m_Member; } else { - // Are you sure you wish to make this member the new guild leader? - pm.SendLocalizedMessage(1063144); - pm.SendGump(new GuildMemberInfoGump(Player, Guild, _member, false, true)); + pm.SendLocalizedMessage( + 1063144 + ); // Are you sure you wish to make this member the new guild leader? + pm.SendGump(new GuildMemberInfoGump(player, guild, m_Member, false, true)); } } else { - _member.GuildRank = targetRank; - // The guild information for ~1_val~ has been updated. - pm.SendLocalizedMessage(1063156, _member.Name); + m_Member.GuildRank = targetRank; + pm.SendLocalizedMessage( + 1063156, + m_Member.Name + ); // The guild information for ~1_val~ has been updated. } } else @@ -134,9 +143,11 @@ namespace Server.Guilds } else { - _member.GuildRank = RankDefinition.Ranks[targetRank.Rank - 1]; - // The guild information for ~1_val~ has been updated. - pm.SendLocalizedMessage(1063156, _member.Name); + m_Member.GuildRank = RankDefinition.Ranks[targetRank.Rank - 1]; + pm.SendLocalizedMessage( + 1063156, + m_Member.Name + ); // The guild information for ~1_val~ has been updated. } } else @@ -149,47 +160,52 @@ namespace Server.Guilds case 3: // Set Guild title { if (playerRank.GetFlag(RankFlags.CanSetGuildTitle) && - (playerRank.Rank > targetRank.Rank || _member == Player)) + (playerRank.Rank > targetRank.Rank || m_Member == player)) { - // Enter the new title for this guild member or 'none' to remove a title: - pm.SendLocalizedMessage(1011128); + pm.SendLocalizedMessage( + 1011128 + ); // Enter the new title for this guild member or 'none' to remove a title: pm.BeginPrompt(SetTitle_Callback); } - else if (_member.GuildTitle == null || _member.GuildTitle.Length <= 0) + else if (m_Member.GuildTitle == null || m_Member.GuildTitle.Length <= 0) { - // You don't have the permission to set that member's guild title. - pm.SendLocalizedMessage(1070746); + pm.SendLocalizedMessage( + 1070746 + ); // You don't have the permission to set that member's guild title. } else { - // You don't have permission to change this member's guild title. - pm.SendLocalizedMessage(1063148); + pm.SendLocalizedMessage( + 1063148 + ); // You don't have permission to change this member's guild title. } break; } case 4: // Vote { - if (_member == pm.GuildFealty && Guild.Leader != _member) + if (m_Member == pm.GuildFealty && guild.Leader != m_Member) { pm.SendLocalizedMessage(1063158); // You have cleared your vote for guild leader. } - else if (Guild.CanVote(_member)) + else if (guild.CanVote(m_Member)) { - if (_member == Guild.Leader) + if (m_Member == guild.Leader) { pm.SendLocalizedMessage(1063424); // You can't vote for the current guild leader. } - else if (!Guild.CanBeVotedFor(_member)) + else if (!guild.CanBeVotedFor(m_Member)) { pm.SendLocalizedMessage(1063425); // You can't vote for an inactive guild member. } else { - pm.GuildFealty = _member; - // You cast your vote for ~1_val~ for guild leader. - pm.SendLocalizedMessage(1063159, _member.Name); + pm.GuildFealty = m_Member; + pm.SendLocalizedMessage( + 1063159, + m_Member.Name + ); // You cast your vote for ~1_val~ for guild leader. } } else @@ -204,16 +220,17 @@ namespace Server.Guilds if (playerRank.GetFlag(RankFlags.RemovePlayers) && playerRank.Rank > targetRank.Rank || playerRank.GetFlag(RankFlags.RemoveLowestRank) && targetRank == RankDefinition.Lowest) { - if (_toKick) + if (m_toKick) { - Guild.RemoveMember(_member); + guild.RemoveMember(m_Member); pm.SendLocalizedMessage(1063157); // The member has been removed from your guild. } else { - // Are you sure you wish to kick this member from the guild? - pm.SendLocalizedMessage(1063152); - pm.SendGump(new GuildMemberInfoGump(Player, Guild, _member, true, false)); + pm.SendLocalizedMessage( + 1063152 + ); // Are you sure you wish to kick this member from the guild? + pm.SendGump(new GuildMemberInfoGump(player, guild, m_Member, true, false)); } } else @@ -228,24 +245,22 @@ namespace Server.Guilds public void SetTitle_Callback(Mobile from, string text) { - if (from is not PlayerMobile pm || _member == null) + if (from is not PlayerMobile pm || m_Member == null) { return; } - if (_member.Guild is not Guild g || !IsMember(pm, g) || + if (m_Member.Guild is not Guild g || !IsMember(pm, g) || !(pm.GuildRank.GetFlag(RankFlags.CanSetGuildTitle) && - (pm.GuildRank.Rank > _member.GuildRank.Rank || pm == _member))) + (pm.GuildRank.Rank > m_Member.GuildRank.Rank || pm == m_Member))) { - if (_member.GuildTitle == null || _member.GuildTitle.Length <= 0) + if (m_Member.GuildTitle == null || m_Member.GuildTitle.Length <= 0) { - // You don't have the permission to set that member's guild title. - pm.SendLocalizedMessage(1070746); + pm.SendLocalizedMessage(1070746); // You don't have the permission to set that member's guild title. } else { - // You don't have permission to change this member's guild title. - pm.SendLocalizedMessage(1063148); + pm.SendLocalizedMessage(1063148); // You don't have permission to change this member's guild title. } return; @@ -263,10 +278,16 @@ namespace Server.Guilds } else { - _member.GuildTitle = title.InsensitiveEquals("none") ? null : title; + if (title.InsensitiveEquals("none")) + { + m_Member.GuildTitle = null; + } + else + { + m_Member.GuildTitle = title; + } - // The guild information for ~1_val~ has been updated. - pm.SendLocalizedMessage(1063156, _member.Name); + pm.SendLocalizedMessage(1063156, m_Member.Name); // The guild information for ~1_val~ has been updated. } } } diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/GuildRosterGump.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/GuildRosterGump.cs index 534b237da..ede71d4d8 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/GuildRosterGump.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/GuildRosterGump.cs @@ -9,13 +9,13 @@ namespace Server.Guilds { public class GuildRosterGump : BaseGuildListGump { - private static readonly InfoField[] _fields = - [ + private static readonly InfoField[] m_Fields = + { new(1062955, 130, NameComparer.Instance), // Name new(1062956, 80, RankComparer.Instance), // Rank new(1062952, 80, LastOnComparer.Instance), // Last On new(1062953, 150, TitleComparer.Instance) // Guild Title - ]; + }; public GuildRosterGump(PlayerMobile pm, Guild g) : this(pm, g, LastOnComparer.Instance) { @@ -33,30 +33,33 @@ namespace Server.Guilds ascending, filter, startNumber, - _fields + m_Fields ) { + PopulateGump(); } - protected override void BuildListExtras(ref DynamicGumpBuilder builder) + public override void PopulateGump() { - builder.AddHtmlLocalized(266, 43, 110, 26, 1062974, 0xF); // Guild Roster + base.PopulateGump(); + + AddHtmlLocalized(266, 43, 110, 26, 1062974, 0xF); // Guild Roster } - protected override void DrawEndingEntry(ref DynamicGumpBuilder builder, int itemNumber) + public override void DrawEndingEntry(int itemNumber) { - builder.AddBackground(225, 148 + itemNumber * 28, 150, 26, 0x2486); - builder.AddButton(230, 153 + itemNumber * 28, 0x845, 0x846, 8); - builder.AddHtmlLocalized(255, 151 + itemNumber * 28, 110, 26, 1062992, 0x0); // Invite Player + AddBackground(225, 148 + itemNumber * 28, 150, 26, 0x2486); + AddButton(230, 153 + itemNumber * 28, 0x845, 0x846, 8); + AddHtmlLocalized(255, 151 + itemNumber * 28, 110, 26, 1062992, 0x0); // Invite Player } protected override TextDefinition[] GetValuesFor(PlayerMobile pm, int aryLength) { var defs = new TextDefinition[aryLength]; - var name = $"{pm.Name}{(Player.GuildFealty == pm && Player.GuildFealty != Guild.Leader ? " *" : "")}"; + var name = $"{pm.Name}{(player.GuildFealty == pm && player.GuildFealty != guild.Leader ? " *" : "")}"; - if (pm == Player) + if (pm == player) { name = name.Color(0x006600); } @@ -83,14 +86,20 @@ namespace Server.Guilds return !pm.Name.InsensitiveContains(filter); } - public override BaseGump GetObjectInfoGump(PlayerMobile pm, Guild g, PlayerMobile o) => + public override Gump GetResentGump( + PlayerMobile pm, Guild g, IComparer comparer, bool ascending, + string filter, int startNumber + ) => + new GuildRosterGump(pm, g, comparer, ascending, filter, startNumber); + + public override Gump GetObjectInfoGump(PlayerMobile pm, Guild g, PlayerMobile o) => new GuildMemberInfoGump(pm, g, o, false, false); public override void OnResponse(NetState sender, in RelayInfo info) { base.OnResponse(sender, info); - if (sender.Mobile is not PlayerMobile pm || !IsMember(pm, Guild)) + if (sender.Mobile is not PlayerMobile pm || !IsMember(pm, guild)) { return; } @@ -100,7 +109,7 @@ namespace Server.Guilds if (pm.GuildRank.GetFlag(RankFlags.CanInvitePlayer)) { pm.SendLocalizedMessage(1063048); // Whom do you wish to invite into your guild? - pm.BeginTarget(-1, false, TargetFlags.None, InvitePlayer_Callback, Guild); + pm.BeginTarget(-1, false, TargetFlags.None, InvitePlayer_Callback, guild); } else { @@ -120,7 +129,7 @@ namespace Server.Guilds var guildFaction = guildState?.Faction; var targetFaction = targetState?.Faction; - if (pm == null || !IsMember(pm, Guild) || !pm.GuildRank.GetFlag(RankFlags.CanInvitePlayer)) + if (pm == null || !IsMember(pm, guild) || !pm.GuildRank.GetFlag(RankFlags.CanInvitePlayer)) { from.SendLocalizedMessage(503301); // You don't have permission to do that. } @@ -172,7 +181,7 @@ namespace Server.Guilds else { pm.SendLocalizedMessage(1063053, targ.Name); // You invite ~1_val~ to join your guild. - targ.SendGump(new GuildInvitationRequest(targ, Guild, pm)); + targ.SendGump(new GuildInvitationRequest(targ, guild, pm)); } } diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/OtherGuildInfo.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/OtherGuildInfo.cs index c8857c8a2..efccd97cd 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/OtherGuildInfo.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/OtherGuildInfo.cs @@ -8,55 +8,55 @@ namespace Server.Guilds { public class OtherGuildInfo : BaseGuildGump { - private readonly Guild _other; + private readonly Guild m_Other; public OtherGuildInfo(PlayerMobile pm, Guild g, Guild otherGuild) : base(pm, g, 10, 40) { - _other = otherGuild; + m_Other = otherGuild; g.CheckExpiredWars(); + + PopulateGump(); } - protected override bool ShowTabStrip => false; - - private static void AddButtonAndBackground(ref DynamicGumpBuilder builder, int x, int y, int buttonID, int locNum) + public void AddButtonAndBackground(int x, int y, int buttonID, int locNum) { - builder.AddBackground(x, y, 225, 26, 0x2486); - builder.AddButton(x + 5, y + 5, 0x845, 0x846, buttonID); - builder.AddHtmlLocalized(x + 30, y + 3, 185, 26, locNum, 0x0); + AddBackground(x, y, 225, 26, 0x2486); + AddButton(x + 5, y + 5, 0x845, 0x846, buttonID); + AddHtmlLocalized(x + 30, y + 3, 185, 26, locNum, 0x0); } - protected override void BuildContent(ref DynamicGumpBuilder builder) + public override void PopulateGump() { - builder.AddBackground(0, 0, 520, 335, 0x242C); - - var g = Guild.GetAllianceLeader(Guild); - var other = Guild.GetAllianceLeader(_other); + var g = Guild.GetAllianceLeader(guild); + var other = Guild.GetAllianceLeader(m_Other); var war = g.FindPendingWar(other); var activeWar = g.FindActiveWar(other); - var alliance = Guild.Alliance; - var otherAlliance = _other.Alliance; + var alliance = guild.Alliance; + var otherAlliance = m_Other.Alliance; // NOTE TO SELF: Only only alliance leader can see pending guild alliance statuses - var pendingWar = war != null; - var activeWarFlag = activeWar != null; + var PendingWar = war != null; + var ActiveWar = activeWar != null; + AddPage(0); - builder.AddHtmlLocalized(20, 15, 480, 26, 1062975, 0x0); //
Guild Relationship
- builder.AddImageTiled(20, 40, 480, 2, 0x2711); - builder.AddHtmlLocalized(20, 50, 120, 26, 1062954, 0x0, true); // Guild Name - builder.AddHtml(150, 53, 360, 26, _other.Name); + AddBackground(0, 0, 520, 335, 0x242C); + AddHtmlLocalized(20, 15, 480, 26, 1062975, 0x0); //
Guild Relationship
+ AddImageTiled(20, 40, 480, 2, 0x2711); + AddHtmlLocalized(20, 50, 120, 26, 1062954, 0x0, true); // Guild Name + AddHtml(150, 53, 360, 26, m_Other.Name); - builder.AddHtmlLocalized(20, 80, 120, 26, 1063025, 0x0, true); // Alliance + AddHtmlLocalized(20, 80, 120, 26, 1063025, 0x0, true); // Alliance - if (otherAlliance?.IsMember(_other) == true) + if (otherAlliance?.IsMember(m_Other) == true) { - builder.AddHtml(150, 83, 360, 26, otherAlliance.Name); + AddHtml(150, 83, 360, 26, otherAlliance.Name); } - builder.AddHtmlLocalized(20, 110, 120, 26, 1063139, 0x0, true); // Abbreviation - builder.AddHtml(150, 113, 120, 26, _other.Abbreviation); + AddHtmlLocalized(20, 110, 120, 26, 1063139, 0x0, true); // Abbreviation + AddHtml(150, 113, 120, 26, m_Other.Abbreviation); var kills = "0/0"; var time = "00:00"; @@ -64,7 +64,7 @@ namespace Server.Guilds WarDeclaration otherWar; - if (activeWarFlag) + if (ActiveWar) { kills = $"{activeWar.Kills}/{activeWar.MaxKills}"; @@ -77,38 +77,38 @@ namespace Server.Guilds time = $"{timeRemaining.Hours:D2}:{DateTime.MinValue + timeRemaining:mm}"; - otherWar = _other.FindActiveWar(Guild); + otherWar = m_Other.FindActiveWar(guild); if (otherWar != null) { otherKills = $"{otherWar.Kills}/{otherWar.MaxKills}"; } } - else if (pendingWar) + else if (PendingWar) { - kills = Html.Color($"{war.Kills}/{war.MaxKills}", 0x990000); - time = Html.Color($"{war.WarLength.Hours:D2}:{DateTime.MinValue + war.WarLength:mm}", 0x990000); + kills = $"{war.Kills}/{war.MaxKills}".Color(0x990000); + time = $"{war.WarLength.Hours:D2}:{DateTime.MinValue + war.WarLength:mm}".Color(0x990000); - otherWar = _other.FindPendingWar(Guild); + otherWar = m_Other.FindPendingWar(guild); if (otherWar != null) { - otherKills = Html.Color($"{otherWar.Kills}/{otherWar.MaxKills}", 0x990000); + otherKills = $"{otherWar.Kills}/{otherWar.MaxKills}".Color(0x990000); } } - builder.AddHtmlLocalized(280, 110, 120, 26, 1062966, 0x0, true); // Your Kills - builder.AddHtml(410, 113, 120, 26, kills); + AddHtmlLocalized(280, 110, 120, 26, 1062966, 0x0, true); // Your Kills + AddHtml(410, 113, 120, 26, kills); - builder.AddHtmlLocalized(20, 140, 120, 26, 1062968, 0x0, true); // Time Remaining - builder.AddHtml(150, 143, 120, 26, time); + AddHtmlLocalized(20, 140, 120, 26, 1062968, 0x0, true); // Time Remaining + AddHtml(150, 143, 120, 26, time); - builder.AddHtmlLocalized(280, 140, 120, 26, 1062967, 0x0, true); // Their Kills - builder.AddHtml(410, 143, 120, 26, otherKills); + AddHtmlLocalized(280, 140, 120, 26, 1062967, 0x0, true); // Their Kills + AddHtml(410, 143, 120, 26, otherKills); - builder.AddImageTiled(20, 172, 480, 2, 0x2711); + AddImageTiled(20, 172, 480, 2, 0x2711); var number = 1062973; //
You are at peace with this guild.
- if (pendingWar) + if (PendingWar) { if (war.WarRequester) { @@ -118,92 +118,92 @@ namespace Server.Guilds { number = 1062969; //
This guild has challenged you to war!
- AddButtonAndBackground(ref builder, 20, 260, 5, 1062981); // Accept Challenge - AddButtonAndBackground(ref builder, 275, 260, 6, 1062983); // Modify Terms + AddButtonAndBackground(20, 260, 5, 1062981); // Accept Challenge + AddButtonAndBackground(275, 260, 6, 1062983); // Modify Terms } - AddButtonAndBackground(ref builder, 20, 290, 7, 1062982); // Dismiss Challenge + AddButtonAndBackground(20, 290, 7, 1062982); // Dismiss Challenge } - else if (activeWarFlag) + else if (ActiveWar) { number = 1062965; //
You are at war with this guild!
- AddButtonAndBackground(ref builder, 20, 290, 8, 1062980); // Surrender + AddButtonAndBackground(20, 290, 8, 1062980); // Surrender } else if (alliance != null && alliance == otherAlliance) // alliance, Same Alliance { - if (alliance.IsMember(Guild) && alliance.IsMember(_other)) // Both in Same alliance, full members + if (alliance.IsMember(guild) && alliance.IsMember(m_Other)) // Both in Same alliance, full members { number = 1062970; //
You are allied with this guild.
- if (alliance.Leader == Guild) + if (alliance.Leader == guild) { - AddButtonAndBackground(ref builder, 20, 260, 12, 1062984); // Remove Guild from Alliance + AddButtonAndBackground(20, 260, 12, 1062984); // Remove Guild from Alliance - // Note: No 'confirmation' like the other leader guild promotion things + //Note: No 'confirmation' like the other leader guild promotion things // Promote to Alliance Leader - AddButtonAndBackground(ref builder, 275, 260, 13, 1063433); + AddButtonAndBackground(275, 260, 13, 1063433); // Remove guild from alliance //Promote to Alliance Leader } // Show roster, Centered, up - AddButtonAndBackground(ref builder, 148, 215, 10, 1063164); // Show Alliance Roster + AddButtonAndBackground(148, 215, 10, 1063164); // Show Alliance Roster // Leave Alliance - AddButtonAndBackground(ref builder, 20, 290, 11, 1062985); // Leave Alliance + AddButtonAndBackground(20, 290, 11, 1062985); // Leave Alliance } - else if (alliance.Leader == Guild && alliance.IsPendingMember(_other)) + else if (alliance.Leader == guild && alliance.IsPendingMember(m_Other)) { number = 1062971; //
You have requested an alliance with this guild.
// Show Alliance Roster, Centered, down. - AddButtonAndBackground(ref builder, 148, 245, 10, 1063164); // Show Alliance Roster + AddButtonAndBackground(148, 245, 10, 1063164); // Show Alliance Roster // Withdraw Request - AddButtonAndBackground(ref builder, 20, 290, 14, 1062986); // Withdraw Request + AddButtonAndBackground(20, 290, 14, 1062986); // Withdraw Request - builder.AddHtml(150, 83, 360, 26, alliance.Name.Color(0x99)); + AddHtml(150, 83, 360, 26, alliance.Name.Color(0x99)); } - else if (alliance.Leader == _other && alliance.IsPendingMember(Guild)) + else if (alliance.Leader == m_Other && alliance.IsPendingMember(guild)) { number = 1062972; //
This guild has requested an alliance.
// Show alliance Roster, top - AddButtonAndBackground(ref builder, 148, 215, 10, 1063164); // Show Alliance Roster + AddButtonAndBackground(148, 215, 10, 1063164); // Show Alliance Roster // Deny Request // Accept Request - AddButtonAndBackground(ref builder, 20, 260, 15, 1062988); // Deny Request - AddButtonAndBackground(ref builder, 20, 290, 16, 1062987); // Accept Request + AddButtonAndBackground(20, 260, 15, 1062988); // Deny Request + AddButtonAndBackground(20, 290, 16, 1062987); // Accept Request - builder.AddHtml(150, 83, 360, 26, alliance.Name.Color(0x99)); + AddHtml(150, 83, 360, 26, alliance.Name.Color(0x99)); } } else { - AddButtonAndBackground(ref builder, 20, 260, 2, 1062990); // Request Alliance - AddButtonAndBackground(ref builder, 20, 290, 1, 1062989); // Declare War! + AddButtonAndBackground(20, 260, 2, 1062990); // Request Alliance + AddButtonAndBackground(20, 290, 1, 1062989); // Declare War! } - AddButtonAndBackground(ref builder, 275, 290, 0, 3000091); // Cancel + AddButtonAndBackground(275, 290, 0, 3000091); // Cancel - builder.AddHtmlLocalized(20, 180, 480, 30, number, 0x0, true); - builder.AddImageTiled(20, 245, 480, 2, 0x2711); + AddHtmlLocalized(20, 180, 480, 30, number, 0x0, true); + AddImageTiled(20, 245, 480, 2, 0x2711); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (!(sender.Mobile is PlayerMobile pm && IsMember(pm, Guild))) + if (!(sender.Mobile is PlayerMobile pm && IsMember(pm, guild))) { return; } var playerRank = pm.GuildRank; - var guildLeader = Guild.GetAllianceLeader(Guild); - var otherGuild = Guild.GetAllianceLeader(_other); + var guildLeader = Guild.GetAllianceLeader(guild); + var otherGuild = Guild.GetAllianceLeader(m_Other); var war = guildLeader.FindPendingWar(otherGuild); var activeWar = guildLeader.FindActiveWar(otherGuild); var otherWar = otherGuild.FindPendingWar(guildLeader); - var alliance = Guild.Alliance; + var alliance = guild.Alliance; var otherAlliance = otherGuild.Alliance; switch (info.ButtonID) @@ -216,10 +216,10 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063440); // You don't have permission to negotiate wars. } - else if (alliance != null && alliance.Leader != Guild) + else if (alliance != null && alliance.Leader != guild) { // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{Guild.Name}\t{alliance.Name}"); + pm.SendLocalizedMessage(1063239, $"{guild.Name}\t{alliance.Name}"); // You need to negotiate via ~1_val~ instead. pm.SendLocalizedMessage(1070707, alliance.Leader.Name); @@ -227,11 +227,11 @@ namespace Server.Guilds else { // Accept the war - Guild.PendingWars.Remove(war); + guild.PendingWars.Remove(war); war.WarBeginning = Core.Now; - Guild.AcceptedWars.Add(war); + guild.AcceptedWars.Add(war); - if (alliance?.IsMember(Guild) == true) + if (alliance?.IsMember(guild) == true) { // Guild Message: Your guild is now at war with ~1_GUILDNAME~ alliance.AllianceMessage(1070769, otherAlliance?.Name ?? otherGuild.Name); @@ -240,8 +240,8 @@ namespace Server.Guilds else { // Guild Message: Your guild is now at war with ~1_GUILDNAME~ - Guild.GuildMessage(1070769, otherAlliance?.Name ?? otherGuild.Name); - Guild.InvalidateMemberProperties(); + guild.GuildMessage(1070769, otherAlliance?.Name ?? otherGuild.Name); + guild.InvalidateMemberProperties(); } // Technically SHOULD say Your guild is now at war w/out any info, intentional diff. @@ -249,16 +249,16 @@ namespace Server.Guilds otherWar.WarBeginning = Core.Now; otherGuild.AcceptedWars.Add(otherWar); - if (otherAlliance != null && _other.Alliance.IsMember(_other)) + if (otherAlliance != null && m_Other.Alliance.IsMember(m_Other)) { // Guild Message: Your guild is now at war with ~1_GUILDNAME~ - otherAlliance.AllianceMessage(1070769, alliance?.Name ?? Guild.Name); + otherAlliance.AllianceMessage(1070769, alliance?.Name ?? guild.Name); otherAlliance.InvalidateMemberProperties(); } else { // Guild Message: Your guild is now at war with ~1_GUILDNAME~ - otherGuild.GuildMessage(1070769, alliance?.Name ?? Guild.Name); + otherGuild.GuildMessage(1070769, alliance?.Name ?? guild.Name); otherGuild.InvalidateMemberProperties(); } } @@ -274,17 +274,17 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063440); // You don't have permission to negotiate wars. } - else if (alliance != null && alliance.Leader != Guild) + else if (alliance != null && alliance.Leader != guild) { // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{Guild.Name}\t{alliance.Name}"); + pm.SendLocalizedMessage(1063239, $"{guild.Name}\t{alliance.Name}"); // You need to negotiate via ~1_val~ instead. pm.SendLocalizedMessage(1070707, alliance.Leader.Name); } else { - pm.SendGump(new WarDeclarationGump(pm, Guild, otherGuild)); + pm.SendGump(new WarDeclarationGump(pm, guild, otherGuild)); } } @@ -298,10 +298,10 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063440); // You don't have permission to negotiate wars. } - else if (alliance != null && alliance.Leader != Guild) + else if (alliance != null && alliance.Leader != guild) { // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{Guild.Name}\t{alliance.Name}"); + pm.SendLocalizedMessage(1063239, $"{guild.Name}\t{alliance.Name}"); // You need to negotiate via ~1_val~ instead. pm.SendLocalizedMessage(1070707, alliance.Leader.Name); @@ -309,7 +309,7 @@ namespace Server.Guilds else { // Dismiss the war - Guild.PendingWars.Remove(war); + guild.PendingWars.Remove(war); otherGuild.PendingWars.Remove(otherWar); pm.SendLocalizedMessage(1070752); // The proposal has been updated. // Messages to opposing guild? (Testing on OSI says no) @@ -324,10 +324,10 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063440); // You don't have permission to negotiate wars. } - else if (alliance != null && alliance.Leader != Guild) + else if (alliance != null && alliance.Leader != guild) { // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{Guild.Name}\t{alliance.Name}"); + pm.SendLocalizedMessage(1063239, $"{guild.Name}\t{alliance.Name}"); // You need to negotiate via ~1_val~ instead. pm.SendLocalizedMessage(1070707, alliance.Leader.Name); @@ -336,7 +336,7 @@ namespace Server.Guilds { if (activeWar != null) { - if (alliance?.IsMember(Guild) == true) + if (alliance?.IsMember(guild) == true) { // You have lost the war with ~1_val~. alliance.AllianceMessage(1070740, otherAlliance?.Name ?? otherGuild.Name); @@ -345,26 +345,26 @@ namespace Server.Guilds else { // You have lost the war with ~1_val~. - Guild.GuildMessage(1070740, otherAlliance?.Name ?? otherGuild.Name); - Guild.InvalidateMemberProperties(); + guild.GuildMessage(1070740, otherAlliance?.Name ?? otherGuild.Name); + guild.InvalidateMemberProperties(); } - Guild.AcceptedWars.Remove(activeWar); + guild.AcceptedWars.Remove(activeWar); if (otherAlliance?.IsMember(otherGuild) == true) { // You have won the war against ~1_val~! - otherAlliance.AllianceMessage(1070739, Guild.Alliance?.Name ?? Guild.Name); + otherAlliance.AllianceMessage(1070739, guild.Alliance?.Name ?? guild.Name); otherAlliance.InvalidateMemberProperties(); } else { // You have won the war against ~1_val~! - otherGuild.GuildMessage(1070739, Guild.Alliance?.Name ?? Guild.Name); + otherGuild.GuildMessage(1070739, guild.Alliance?.Name ?? guild.Name); otherGuild.InvalidateMemberProperties(); } - otherGuild.AcceptedWars.Remove(otherGuild.FindActiveWar(Guild)); + otherGuild.AcceptedWars.Remove(otherGuild.FindActiveWar(guild)); } } @@ -378,25 +378,25 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063440); // You don't have permission to negotiate wars. } - else if (alliance != null && alliance.Leader != Guild) + else if (alliance != null && alliance.Leader != guild) { // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{Guild.Name}\t{alliance.Name}"); + pm.SendLocalizedMessage(1063239, $"{guild.Name}\t{alliance.Name}"); // You need to negotiate via ~1_val~ instead. pm.SendLocalizedMessage(1070707, alliance.Leader.Name); } - else if (otherAlliance != null && otherAlliance.Leader != _other) + else if (otherAlliance != null && otherAlliance.Leader != m_Other) { // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{_other.Name}\t{otherAlliance.Name}"); + pm.SendLocalizedMessage(1063239, $"{m_Other.Name}\t{otherAlliance.Name}"); // You need to negotiate via ~1_val~ instead. pm.SendLocalizedMessage(1070707, otherAlliance.Leader.Name); } else { - pm.SendGump(new WarDeclarationGump(pm, Guild, _other)); + pm.SendGump(new WarDeclarationGump(pm, guild, m_Other)); } } @@ -411,33 +411,33 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1070747); // You don't have permission to create an alliance. } - else if (Faction.Find(Guild.Leader) != Faction.Find(_other.Leader)) + else if (Faction.Find(guild.Leader) != Faction.Find(m_Other.Leader)) { // You cannot propose an alliance to a guild with a different faction allegiance. pm.SendLocalizedMessage(1070758); } else if (otherAlliance != null) { - if (otherAlliance.IsPendingMember(_other)) + if (otherAlliance.IsPendingMember(m_Other)) { // ~1_val~ is currently considering another alliance proposal. - pm.SendLocalizedMessage(1063416, _other.Name); + pm.SendLocalizedMessage(1063416, m_Other.Name); } else { // ~1_val~ already belongs to an alliance. - pm.SendLocalizedMessage(1063426, _other.Name); + pm.SendLocalizedMessage(1063426, m_Other.Name); } } - else if (_other.AcceptedWars.Count > 0 || _other.PendingWars.Count > 0) + else if (m_Other.AcceptedWars.Count > 0 || m_Other.PendingWars.Count > 0) { // ~1_val~ is currently involved in a guild war. - pm.SendLocalizedMessage(1063427, _other.Name); + pm.SendLocalizedMessage(1063427, m_Other.Name); } - else if (Guild.AcceptedWars.Count > 0 || Guild.PendingWars.Count > 0) + else if (guild.AcceptedWars.Count > 0 || guild.PendingWars.Count > 0) { // ~1_val~ is currently involved in a guild war. - pm.SendLocalizedMessage(1063427, Guild.Name); + pm.SendLocalizedMessage(1063427, guild.Name); } else { @@ -451,40 +451,40 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063436); // You don't have permission to negotiate an alliance. } - else if (alliance.Leader != Guild) + else if (alliance.Leader != guild) { // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{Guild.Name}\t{alliance.Name}"); + pm.SendLocalizedMessage(1063239, $"{guild.Name}\t{alliance.Name}"); } else if (otherAlliance != null) { - if (otherAlliance.IsPendingMember(_other)) + if (otherAlliance.IsPendingMember(m_Other)) { // ~1_val~ is currently considering another alliance proposal. - pm.SendLocalizedMessage(1063416, _other.Name); + pm.SendLocalizedMessage(1063416, m_Other.Name); } else { // ~1_val~ already belongs to an alliance. - pm.SendLocalizedMessage(1063426, _other.Name); + pm.SendLocalizedMessage(1063426, m_Other.Name); } } - else if (alliance.IsPendingMember(Guild)) + else if (alliance.IsPendingMember(guild)) { // ~1_val~ is currently considering another alliance proposal. - pm.SendLocalizedMessage(1063416, Guild.Name); + pm.SendLocalizedMessage(1063416, guild.Name); } - else if (_other.AcceptedWars.Count > 0 || _other.PendingWars.Count > 0) + else if (m_Other.AcceptedWars.Count > 0 || m_Other.PendingWars.Count > 0) { // ~1_val~ is currently involved in a guild war. - pm.SendLocalizedMessage(1063427, _other.Name); + pm.SendLocalizedMessage(1063427, m_Other.Name); } - else if (Guild.AcceptedWars.Count > 0 || Guild.PendingWars.Count > 0) + else if (guild.AcceptedWars.Count > 0 || guild.PendingWars.Count > 0) { // ~1_val~ is currently involved in a guild war. - pm.SendLocalizedMessage(1063427, Guild.Name); + pm.SendLocalizedMessage(1063427, guild.Name); } - else if (Faction.Find(Guild.Leader) != Faction.Find(_other.Leader)) + else if (Faction.Find(guild.Leader) != Faction.Find(m_Other.Leader)) { // You cannot propose an alliance to a guild with a different faction allegiance. pm.SendLocalizedMessage(1070758); @@ -492,11 +492,12 @@ namespace Server.Guilds else { // An invitation to join your alliance has been sent to ~1_val~. - pm.SendLocalizedMessage(1070750, _other.Name); + pm.SendLocalizedMessage(1070750, m_Other.Name); - _other.GuildMessage(1070780, Guild.Name); // ~1_val~ has proposed an alliance. + m_Other.GuildMessage(1070780, guild.Name); // ~1_val~ has proposed an alliance. - _other.Alliance = alliance; // Calls addPendingGuild + m_Other.Alliance = alliance; // Calls addPendingGuild + // alliance.AddPendingGuild( m_Other ); } } @@ -506,7 +507,7 @@ namespace Server.Guilds { if (alliance != null && alliance == otherAlliance) { - pm.SendGump(new AllianceInfo.AllianceRosterGump(pm, Guild, alliance)); + pm.SendGump(new AllianceInfo.AllianceRosterGump(pm, guild, alliance)); } break; @@ -517,13 +518,14 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063436); // You don't have permission to negotiate an alliance. } - else if (alliance?.IsMember(Guild) == true) + else if (alliance?.IsMember(guild) == true) { - Guild.Alliance = null; // Calls alliance.RemoveGuild + guild.Alliance = null; // Calls alliance.RemoveGuild + // alliance.RemoveGuild( guild ); - _other.InvalidateWarNotoriety(); + m_Other.InvalidateWarNotoriety(); - Guild.InvalidateMemberNotoriety(); + guild.InvalidateMemberNotoriety(); } break; @@ -534,18 +536,18 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063436); // You don't have permission to negotiate an alliance. } - else if (alliance != null && alliance.Leader != Guild) + else if (alliance != null && alliance.Leader != guild) { // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{Guild.Name}\t{alliance.Name}"); + pm.SendLocalizedMessage(1063239, $"{guild.Name}\t{alliance.Name}"); } - else if (alliance?.IsMember(Guild) == true && alliance.IsMember(_other)) + else if (alliance?.IsMember(guild) == true && alliance.IsMember(m_Other)) { - _other.Alliance = null; + m_Other.Alliance = null; - _other.InvalidateMemberNotoriety(); + m_Other.InvalidateMemberNotoriety(); - Guild.InvalidateWarNotoriety(); + guild.InvalidateWarNotoriety(); } break; @@ -556,17 +558,17 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063436); // You don't have permission to negotiate an alliance. } - else if (alliance != null && alliance.Leader != Guild) + else if (alliance != null && alliance.Leader != guild) { // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{Guild.Name}\t{alliance.Name}"); + pm.SendLocalizedMessage(1063239, $"{guild.Name}\t{alliance.Name}"); } - else if (alliance?.IsMember(Guild) == true && alliance.IsMember(_other)) + else if (alliance?.IsMember(guild) == true && alliance.IsMember(m_Other)) { // ~1_val~ is now the leader of ~2_val~. - pm.SendLocalizedMessage(1063434, $"{_other.Name}\t{alliance.Name}"); + pm.SendLocalizedMessage(1063434, $"{m_Other.Name}\t{alliance.Name}"); - alliance.Leader = _other; + alliance.Leader = m_Other; } break; @@ -577,9 +579,9 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063436); // You don't have permission to negotiate an alliance. } - else if (alliance != null && alliance.Leader == Guild && alliance.IsPendingMember(_other)) + else if (alliance != null && alliance.Leader == guild && alliance.IsPendingMember(m_Other)) { - _other.Alliance = null; + m_Other.Alliance = null; pm.SendLocalizedMessage(1070752); // The proposal has been updated. } @@ -591,15 +593,15 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063436); // You don't have permission to negotiate an alliance. } - else if (alliance != null && otherAlliance != null && alliance.Leader == _other && - otherAlliance.IsPendingMember(Guild)) + else if (alliance != null && otherAlliance != null && alliance.Leader == m_Other && + otherAlliance.IsPendingMember(guild)) { // The proposal has been updated. - // _other.GuildMessage( 1070782 ); + // m_Other.GuildMessage( 1070782 ); // // ~1_val~ has responded to your proposal. - // // Per OSI commented out. + // //Per OSI commented out. pm.SendLocalizedMessage(1070752); - Guild.Alliance = null; + guild.Alliance = null; } break; @@ -610,15 +612,15 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1063436); // You don't have permission to negotiate an alliance. } - else if (otherAlliance != null && otherAlliance.Leader == _other && - otherAlliance.IsPendingMember(Guild)) + else if (otherAlliance != null && otherAlliance.Leader == m_Other && + otherAlliance.IsPendingMember(guild)) { pm.SendLocalizedMessage(1070752); // The proposal has been updated. // No need to verify it's in the guild or already a member, the function does this - otherAlliance.TurnToMember(_other); + otherAlliance.TurnToMember(m_Other); - otherAlliance.TurnToMember(Guild); + otherAlliance.TurnToMember(guild); } break; @@ -633,10 +635,10 @@ namespace Server.Guilds return; } - var alliance = Guild.Alliance; - var otherAlliance = _other.Alliance; + var alliance = guild.Alliance; + var otherAlliance = m_Other.Alliance; - if (!IsMember(from, Guild) || alliance != null) + if (!IsMember(from, guild) || alliance != null) { return; } @@ -647,7 +649,7 @@ namespace Server.Guilds { pm.SendLocalizedMessage(1070747); // You don't have permission to create an alliance. } - else if (Faction.Find(Guild.Leader) != Faction.Find(_other.Leader)) + else if (Faction.Find(guild.Leader) != Faction.Find(m_Other.Leader)) { // Notes about this: OSI only cares/checks when proposing, you can change your faction all you want later. // You cannot propose an alliance to a guild with a different faction allegiance. @@ -655,23 +657,23 @@ namespace Server.Guilds } else if (otherAlliance != null) { - if (otherAlliance.IsPendingMember(_other)) + if (otherAlliance.IsPendingMember(m_Other)) { // ~1_val~ is currently considering another alliance proposal. - pm.SendLocalizedMessage(1063416, _other.Name); + pm.SendLocalizedMessage(1063416, m_Other.Name); } else { - pm.SendLocalizedMessage(1063426, _other.Name); // ~1_val~ already belongs to an alliance. + pm.SendLocalizedMessage(1063426, m_Other.Name); // ~1_val~ already belongs to an alliance. } } - else if (_other.AcceptedWars.Count > 0 || _other.PendingWars.Count > 0) + else if (m_Other.AcceptedWars.Count > 0 || m_Other.PendingWars.Count > 0) { - pm.SendLocalizedMessage(1063427, _other.Name); // ~1_val~ is currently involved in a guild war. + pm.SendLocalizedMessage(1063427, m_Other.Name); // ~1_val~ is currently involved in a guild war. } - else if (Guild.AcceptedWars.Count > 0 || Guild.PendingWars.Count > 0) + else if (guild.AcceptedWars.Count > 0 || guild.PendingWars.Count > 0) { - pm.SendLocalizedMessage(1063427, Guild.Name); // ~1_val~ is currently involved in a guild war. + pm.SendLocalizedMessage(1063427, guild.Name); // ~1_val~ is currently involved in a guild war. } else { @@ -693,11 +695,11 @@ namespace Server.Guilds else { // An invitation to join your alliance has been sent to ~1_val~. - pm.SendLocalizedMessage(1070750, _other.Name); + pm.SendLocalizedMessage(1070750, m_Other.Name); - _other.GuildMessage(1070780, Guild.Name); // ~1_val~ has proposed an alliance. + m_Other.GuildMessage(1070780, guild.Name); // ~1_val~ has proposed an alliance. - new AllianceInfo(Guild, name, _other); + new AllianceInfo(guild, name, m_Other); } } } diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/WarDeclarationGump.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/WarDeclarationGump.cs index 2a2be3b73..b0a5749ae 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/WarDeclarationGump.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/WarDeclarationGump.cs @@ -7,42 +7,40 @@ namespace Server.Guilds { public class WarDeclarationGump : BaseGuildGump { - private readonly Guild _other; + private readonly Guild m_Other; - public WarDeclarationGump(PlayerMobile pm, Guild g, Guild otherGuild) : base(pm, g) => _other = otherGuild; - - protected override bool ShowTabStrip => false; - - protected override void BuildContent(ref DynamicGumpBuilder builder) + public WarDeclarationGump(PlayerMobile pm, Guild g, Guild otherGuild) : base(pm, g) { - var war = Guild.FindPendingWar(_other); + m_Other = otherGuild; + var war = g.FindPendingWar(otherGuild); - builder.AddBackground(0, 0, 500, 340, 0x24AE); - builder.AddBackground(65, 50, 370, 30, 0x2486); - //
Declaration of War
- builder.AddHtmlLocalized(75, 55, 370, 26, 1062979, 0x3C00); - builder.AddImage(410, 45, 0x232C); - builder.AddHtmlLocalized(65, 95, 200, 20, 1063009, 0x14AF); // Duration of War - builder.AddHtmlLocalized(65, 120, 400, 20, 1063010, 0x0); // Enter the number of hours the war will last. - builder.AddBackground(65, 150, 40, 30, 0x2486); - builder.AddTextEntry(70, 154, 50, 30, 0x481, 10, $"{war?.WarLength.Hours ?? 0}"); - builder.AddHtmlLocalized(65, 195, 200, 20, 1063011, 0x14AF); // Victory Condition - builder.AddHtmlLocalized(65, 220, 400, 20, 1063012, 0x0); // Enter the winning number of kills. - builder.AddBackground(65, 250, 40, 30, 0x2486); - builder.AddTextEntry(70, 254, 50, 30, 0x481, 11, $"{war?.MaxKills ?? 0}"); - builder.AddBackground(190, 270, 130, 26, 0x2486); - builder.AddButton(195, 275, 0x845, 0x846, 0); - builder.AddHtmlLocalized(220, 273, 90, 26, 1006045, 0x0); // Cancel - builder.AddBackground(330, 270, 130, 26, 0x2486); - builder.AddButton(335, 275, 0x845, 0x846, 1); - builder.AddHtmlLocalized(360, 273, 90, 26, 1062989, 0x5000); // Declare War! + AddPage(0); + + AddBackground(0, 0, 500, 340, 0x24AE); + AddBackground(65, 50, 370, 30, 0x2486); + AddHtmlLocalized(75, 55, 370, 26, 1062979, 0x3C00); //
Declaration of War
+ AddImage(410, 45, 0x232C); + AddHtmlLocalized(65, 95, 200, 20, 1063009, 0x14AF); // Duration of War + AddHtmlLocalized(65, 120, 400, 20, 1063010, 0x0); // Enter the number of hours the war will last. + AddBackground(65, 150, 40, 30, 0x2486); + AddTextEntry(70, 154, 50, 30, 0x481, 10, war?.WarLength.Hours.ToString() ?? "0"); + AddHtmlLocalized(65, 195, 200, 20, 1063011, 0x14AF); // Victory Condition + AddHtmlLocalized(65, 220, 400, 20, 1063012, 0x0); // Enter the winning number of kills. + AddBackground(65, 250, 40, 30, 0x2486); + AddTextEntry(70, 254, 50, 30, 0x481, 11, war?.MaxKills.ToString() ?? "0"); + AddBackground(190, 270, 130, 26, 0x2486); + AddButton(195, 275, 0x845, 0x846, 0); + AddHtmlLocalized(220, 273, 90, 26, 1006045, 0x0); // Cancel + AddBackground(330, 270, 130, 26, 0x2486); + AddButton(335, 275, 0x845, 0x846, 1); + AddHtmlLocalized(360, 273, 90, 26, 1062989, 0x5000); // Declare War! } public override void OnResponse(NetState sender, in RelayInfo info) { var pm = sender.Mobile as PlayerMobile; - if (!IsMember(pm, Guild)) + if (!IsMember(pm, guild)) { return; } @@ -53,38 +51,45 @@ namespace Server.Guilds { case 1: { - var alliance = Guild.Alliance; - var otherAlliance = _other.Alliance; + var alliance = guild.Alliance; + var otherAlliance = m_Other.Alliance; if (!playerRank.GetFlag(RankFlags.ControlWarStatus)) { pm.SendLocalizedMessage(1063440); // You don't have permission to negotiate wars. } - else if (alliance != null && alliance.Leader != Guild) + else if (alliance != null && alliance.Leader != guild) { - // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{Guild.Name}\t{alliance.Name}"); - // You need to negotiate via ~1_val~ instead. - pm.SendLocalizedMessage(1070707, alliance.Leader.Name); + pm.SendLocalizedMessage( + 1063239, + $"{guild.Name}\t{alliance.Name}" + ); // ~1_val~ is not the leader of the ~2_val~ alliance. + pm.SendLocalizedMessage( + 1070707, + alliance.Leader.Name + ); // You need to negotiate via ~1_val~ instead. } - else if (otherAlliance != null && otherAlliance.Leader != _other) + else if (otherAlliance != null && otherAlliance.Leader != m_Other) { - // ~1_val~ is not the leader of the ~2_val~ alliance. - pm.SendLocalizedMessage(1063239, $"{_other.Name}\t{otherAlliance.Name}"); - // You need to negotiate via ~1_val~ instead. - pm.SendLocalizedMessage(1070707, otherAlliance.Leader.Name); + pm.SendLocalizedMessage( + 1063239, + $"{m_Other.Name}\t{otherAlliance.Name}" + ); // ~1_val~ is not the leader of the ~2_val~ alliance. + pm.SendLocalizedMessage( + 1070707, + otherAlliance.Leader.Name + ); // You need to negotiate via ~1_val~ instead. } else { - var activeWar = Guild.FindActiveWar(_other); + var activeWar = guild.FindActiveWar(m_Other); if (activeWar == null) { - var war = Guild.FindPendingWar(_other); - var otherWar = _other.FindPendingWar(Guild); + var war = guild.FindPendingWar(m_Other); + var otherWar = m_Other.FindPendingWar(guild); - // Note: OSI differs from what it says on website. unlimited war = 0 kills/0 hrs. - // Not > 999. (sidenote: they both cap at 65535, 7.5 years, but, still.) + // Note: OSI differs from what it says on website. unlimited war = 0 kills/ 0 hrs. Not > 999. (sidenote: they both cap at 65535, 7.5 years, but, still.) var tKills = info.GetTextEntry(11); var tWarLength = info.GetTextEntry(10); @@ -105,7 +110,7 @@ namespace Server.Guilds } else { - Guild.PendingWars.Add(new WarDeclaration(Guild, _other, maxKills, warLength, true)); + guild.PendingWars.Add(new WarDeclaration(guild, m_Other, maxKills, warLength, true)); } if (otherWar != null) @@ -116,7 +121,7 @@ namespace Server.Guilds } else { - _other.PendingWars.Add(new WarDeclaration(_other, Guild, maxKills, warLength, false)); + m_Other.PendingWars.Add(new WarDeclaration(m_Other, guild, maxKills, warLength, false)); } if (war != null) @@ -125,22 +130,20 @@ namespace Server.Guilds } else { - // ~1_val~ has proposed a war. - _other.GuildMessage( + m_Other.GuildMessage( 1070781, - Guild.Alliance != null - ? Guild.Alliance.Name - : Guild.Name - ); + guild.Alliance != null + ? guild.Alliance.Name + : guild.Name + ); // ~1_val~ has proposed a war. } - // War proposal has been sent to ~1_val~. pm.SendLocalizedMessage( 1070751, - _other.Alliance != null - ? _other.Alliance.Name - : _other.Name - ); + m_Other.Alliance != null + ? m_Other.Alliance.Name + : m_Other.Name + ); // War proposal has been sent to ~1_val~. } } @@ -148,7 +151,7 @@ namespace Server.Guilds } default: { - pm.SendGump(new OtherGuildInfo(pm, Guild, _other)); + pm.SendGump(new OtherGuildInfo(pm, guild, m_Other)); break; } } diff --git a/Projects/UOContent/Gumps/Guilds/RecruitTarget.cs b/Projects/UOContent/Gumps/Guilds/RecruitTarget.cs index 04e6b14e9..beda0ab27 100644 --- a/Projects/UOContent/Gumps/Guilds/RecruitTarget.cs +++ b/Projects/UOContent/Gumps/Guilds/RecruitTarget.cs @@ -96,7 +96,8 @@ namespace Server.Gumps return; } - GuildGump.DisplayTo(m_Mobile, m_Guild); + GuildGump.EnsureClosed(m_Mobile); + m_Mobile.SendGump(new GuildGump(m_Mobile, m_Guild)); } } } diff --git a/Projects/UOContent/Gumps/HeritageTokenGump.cs b/Projects/UOContent/Gumps/HeritageTokenGump.cs index 8479026b6..685959bf9 100644 --- a/Projects/UOContent/Gumps/HeritageTokenGump.cs +++ b/Projects/UOContent/Gumps/HeritageTokenGump.cs @@ -1,226 +1,717 @@ using System; -using System.Collections.Generic; using Server.Items; using Server.Network; -namespace Server.Gumps; - -public class HeritageTokenGump : StaticGump +namespace Server.Gumps { - private static readonly Dictionary ButtonLookup = new() + public class HeritageTokenGump : Gump { - // 7th anniversary - { 0x64, ([typeof(LeggingsOfEmbers)], 1078147, 1062912, 0x1411, 0x2C, 18, 8) }, - { 0x65, ([typeof(RoseOfTrinsic)], 1062913, 1062914, 0x234D, 0x0, 18, 12) }, - { 0x66, ([typeof(ShaminoCrossbow)], 1062915, 1062916, 0x26C3, 0x504, 18, 8) }, - { 0x67, ([typeof(TapestryOfSosaria)], 1062917, 1062918, 0x3F1D, 0x0, 18, 8) }, - { 0x68, ([typeof(HearthOfHomeFireDeed)], 1062919, 1062920, 0x3F14, 0x0, 18, 8) }, - { 0x69, ([typeof(HolySword)], 1062921, 1062922, 0xF60, 0x482, -1, 10) }, - { 0x6A, ([typeof(SamuraiHelm)], 1062923, 1062924, 0x236C, 0x0, 18, 6) }, + private readonly HeritageToken m_Token; - // 8th anniversary - { 0x6B, ([typeof(SpiritualityHelm)], 1075188, 1075223, 0x2B10, 0x226, 18, 11) }, - { 0x6C, ([typeof(ValorGauntlets)], 1075192, 1075224, 0x2B0C, 0x226, 18, 15) }, - { 0x6D, ([typeof(DupresShield)], 1075196, 1075225, 0x2B01, 0x0, 18, 9) }, - { 0x6E, ([typeof(FountainOfLifeDeed)], 1075197, 1075226, 0x2AC6, 0x0, 29, 0) }, - { 0x6F, ([typeof(DawnsMusicBox)], 1075198, 1075227, 0x2AF9, 0x0, -4, -5) }, - { 0x70, ([typeof(OssianGrimoire)], 1078148, 1075228, 0x2253, 0x0, 18, 12) }, - { 0x71, ([typeof(FerretFormTalisman)], 1078142, 1078527, 0x2D98, 0x0, 19, 13) }, - { 0x72, ([typeof(SquirrelFormTalisman)], 1078143, 1078528, 0x2D97, 0x0, 19, 13) }, - { 0x73, ([typeof(CuSidheFormTalisman)], 1078144, 1078529, 0x2D96, 0x0, 19, 8) }, - { 0x74, ([typeof(ReptalonFormTalisman)], 1078145, 1078530, 0x2D95, 0x0, -4, 2) }, - { 0x75, ([typeof(QuiverOfInfinity)], 1075201, 1078526, 0x2B02, 0x0, -2, 9) }, - - // evil home decor - { 0x76, ( - [typeof(BoneThroneDeed), typeof(BoneCouchDeed), typeof(BoneTableDeed)], - 1074797, 1075986, 0x2A91, 0x0, 25, 5 - ) }, - { 0x77, ( - [typeof(CreepyPortraitDeed), typeof(DisturbingPortraitDeed), typeof(UnsettlingPortraitDeed)], - 1078146, 1075987, 0x2A99, 0x0, 18, 1 - ) }, - { 0x78, ( - [ - typeof(MountedPixieBlueDeed), typeof(MountedPixieGreenDeed), typeof(MountedPixieLimeDeed), - typeof(MountedPixieOrangeDeed), typeof(MountedPixieWhiteDeed) - ], - 1074799, 1075988, 0x2A71, 0x0, 13, 5 - ) }, - { 0x79, ([typeof(HaunterMirrorDeed)], 1074800, 1075990, 0x2A98, 0x0, 26, 1) }, - { 0x7A, ([typeof(BedOfNailsDeed)], 1074801, 1075989, 0x2A92, 0x0, 18, 1) }, - { 0x7B, ([typeof(SacrificialAltarDeed)], 1074818, 1075991, 0x2AB8, 0x0, 18, 1) }, - - // broken furniture - { 0x7C, ([typeof(BrokenCoveredChairDeed)], 1076257, 1076610, 0x3F26, 0x0, 18, 8) }, - { 0x7D, ([typeof(BrokenBookcaseDeed)], 1076258, 1076610, 0x3F22, 0x0, 18, 8) }, - { 0x7E, ([typeof(StandingBrokenChairDeed)], 1076259, 1076610, 0x3F24, 0x0, 18, 8) }, - { 0x7F, ([typeof(BrokenVanityDeed)], 1076260, 1076610, 0x3F25, 0x0, 18, 8) }, - { 0x80, ([typeof(BrokenChestOfDrawersDeed)], 1076261, 1076610, 0x3F23, 0x0, 18, 8) }, - { 0x81, ([typeof(BrokenArmoireDeed)], 1076262, 1076610, 0x3F21, 0x0, 18, 8) }, - { 0x82, ([typeof(BrokenBedDeed)], 1076263, 1076610, 0x3F0B, 0x0, 18, 8) }, - { 0x83, ([typeof(BrokenFallenChairDeed)], 1076264, 1076610, 0xC19, 0x0, 13, 8) }, - - // other - { 0x84, ([typeof(SuitOfGoldArmorDeed)], 1076265, 1076611, 0x3DAA, 0x0, 20, -3) }, - { 0x85, ([typeof(SuitOfSilverArmorDeed)], 1076266, 1076612, 0x151C, 0x0, -20, -3) }, - { 0x86, ([typeof(BoilingCauldronDeed)], 1076267, 1076613, 0x3DB1, 0x0, 18, 8) }, - { 0x87, ([typeof(GuillotineDeed)], 1024656, 1076614, 0x3F27, 0x0, 18, 8) }, - { 0x88, ([typeof(CherryBlossomTreeDeed)], 1076268, 1076615, 0x3F0C, 0x0, 18, 8) }, - { 0x89, ([typeof(AppleTreeDeed)], 1076269, 1076616, 0x3F07, 0x0, 18, 8) }, - { 0x8A, ([typeof(PeachTreeDeed)], 1076270, 1076617, 0x3F16, 0x0, 18, 8) }, - { 0x8B, ([typeof(HangingAxesDeed)], 1076271, 1076618, 0x3F12, 0x0, 18, 8) }, - { 0x8C, ([typeof(HangingSwordsDeed)], 1076272, 1076619, 0x3F13, 0x0, 18, 8) }, - { 0x8D, ([typeof(BlueFancyRugDeed)], 1076273, 1076620, 0x3F09, 0x0, 18, 8) }, - { 0x8E, ([typeof(WoodenCoffinDeed)], 1076274, 1076621, 0x3F0E, 0x0, 18, 8) }, - { 0x8F, ([typeof(VanityDeed)], 1074027, 1076623, 0x3F1F, 0x0, 18, 8) }, - { 0x90, ([typeof(TableWithPurpleClothDeed)], 1076635, 1076624, 0x118B, 0x0, -4, -9) }, - { 0x91, ([typeof(TableWithBlueClothDeed)], 1076636, 1076624, 0x118C, 0x0, -4, -9) }, - { 0x92, ([typeof(TableWithRedClothDeed)], 1076637, 1076624, 0x118D, 0x0, -4, -9) }, - { 0x93, ([typeof(TableWithOrangeClothDeed)], 1076638, 1076624, 0x118E, 0x0, -4, -9) }, - { 0x94, ([typeof(UnmadeBedDeed)], 1076279, 1076625, 0x3F1E, 0x0, 18, 8) }, - { 0x95, ([typeof(CurtainsDeed)], 1076280, 1076626, 0x3F0F, 0x0, 18, 8) }, - { 0x96, ([typeof(ScarecrowDeed)], 1076281, 1076627, 0x1E34, 0x0, 18, -17) }, - { 0x97, ([typeof(WallTorchDeed)], 1076282, 1076628, 0xA0C, 0x0, 18, 8) }, - { 0x98, ([typeof(FountainDeed)], 1076283, 1076629, 0x3F10, 0x0, 18, 9) }, - { 0x99, ([typeof(StoneStatueDeed)], 1076284, 1076630, 0x3F19, 0x0, 18, 8) }, - { 0x9A, ([typeof(LargeFishingNetDeed)], 1076285, 1076631, 0x1EA5, 0x0, 5, -25) }, - { 0x9B, ([typeof(SmallFishingNetDeed)], 1076286, 1076632, 0x1EA3, 0x0, 18, -27) }, - { 0x9C, ([typeof(HouseLadderDeed)], 1076287, 1076633, 0x2FDF, 0x0, 18, -36) }, - { 0x9D, ([typeof(IronMaidenDeed)], 1076288, 1076622, 0x3F15, 0x0, 18, 8) }, - { 0x9E, ([typeof(BluePlainRugDeed)], 1076585, 1076620, 0x3F0A, 0x0, 18, 8) }, - { 0x9F, ([typeof(GoldenDecorativeRugDeed)], 1076586, 1076620, 0x3F11, 0x0, 18, 8) }, - { 0xA0, ([typeof(CinnamonFancyRugDeed)], 1076587, 1076620, 0x3F0D, 0x0, 18, 8) }, - { 0xA1, ([typeof(RedPlainRugDeed)], 1076588, 1076620, 0x3F18, 0x0, 18, 8) }, - { 0xA2, ([typeof(BlueDecorativeRugDeed)], 1076589, 1076620, 0x3F08, 0x0, 18, 8) }, - { 0xA3, ([typeof(PinkFancyRugDeed)], 1076590, 1076620, 0x3F17, 0x0, 18, 8) }, - { 0xA4, ([typeof(CherryBlossomTrunkDeed)], 1076784, 1076615, 0x312A, 0x0, 18, 8) }, - { 0xA5, ([typeof(AppleTrunkDeed)], 1076785, 1076616, 0x3128, 0x0, 18, 8) }, - { 0xA6, ([typeof(PeachTrunkDeed)], 1076786, 1076617, 0x3129, 0x0, 18, 8) } - }; - - private readonly HeritageToken _token; - - public override bool Singleton => true; - - private HeritageTokenGump(HeritageToken token) : base(60, 36) => _token = token; - - public static void DisplayTo(Mobile from, HeritageToken token) - { - if (from?.NetState == null || token?.Deleted != false) + public HeritageTokenGump(HeritageToken token) : base(60, 36) { - return; + m_Token = token; + + AddPage(0); + + AddBackground(0, 0, 520, 404, 0x13BE); + AddImageTiled(10, 10, 500, 20, 0xA40); + AddImageTiled(10, 40, 500, 324, 0xA40); + AddImageTiled(10, 374, 500, 20, 0xA40); + AddAlphaRegion(10, 10, 500, 384); + AddButton(10, 374, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 376, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 500, 20, 1075576, 0x7FFF); // Choose your item from the following pages + + AddPage(1); + + AddImageTiledButton(14, 44, 0x918, 0x919, 0x64, GumpButtonType.Reply, 0, 0x1411, 0x2C, 18, 8); + AddTooltip(1062912); + AddHtmlLocalized(98, 44, 250, 60, 1078147, 0x7FFF); // Royal Leggings of Embers + AddImageTiledButton(264, 44, 0x918, 0x919, 0x65, GumpButtonType.Reply, 0, 0x234D, 0x0, 18, 12); + AddTooltip(1062914); + AddHtmlLocalized(348, 44, 250, 60, 1062913, 0x7FFF); // Rose of Trinsic + AddImageTiledButton(14, 108, 0x918, 0x919, 0x66, GumpButtonType.Reply, 0, 0x26C3, 0x504, 18, 8); + AddTooltip(1062916); + AddHtmlLocalized(98, 108, 250, 60, 1062915, 0x7FFF); // Shamino’s Best Crossbow + AddImageTiledButton(264, 108, 0x918, 0x919, 0x67, GumpButtonType.Reply, 0, 0x3F1D, 0x0, 18, 8); + AddTooltip(1062918); + AddHtmlLocalized(348, 108, 250, 60, 1062917, 0x7FFF); // The Tapestry of Sosaria + AddImageTiledButton(14, 172, 0x918, 0x919, 0x68, GumpButtonType.Reply, 0, 0x3F14, 0x0, 18, 8); + AddTooltip(1062920); + AddHtmlLocalized(98, 172, 250, 60, 1062919, 0x7FFF); // Hearth of the Home Fire + AddImageTiledButton(264, 172, 0x918, 0x919, 0x69, GumpButtonType.Reply, 0, 0xF60, 0x482, -1, 10); + AddTooltip(1062922); + AddHtmlLocalized(348, 172, 250, 60, 1062921, 0x7FFF); // The Holy Sword + AddImageTiledButton(14, 236, 0x918, 0x919, 0x6A, GumpButtonType.Reply, 0, 0x236C, 0x0, 18, 6); + AddTooltip(1062924); + AddHtmlLocalized(98, 236, 250, 60, 1062923, 0x7FFF); // Ancient Samurai Helm + AddImageTiledButton(264, 236, 0x918, 0x919, 0x6B, GumpButtonType.Reply, 0, 0x2B10, 0x226, 18, 11); + AddTooltip(1075223); + AddHtmlLocalized(348, 236, 250, 60, 1075188, 0x7FFF); // Helm of Spirituality + AddImageTiledButton(14, 300, 0x918, 0x919, 0x6C, GumpButtonType.Reply, 0, 0x2B0C, 0x226, 18, 15); + AddTooltip(1075224); + AddHtmlLocalized(98, 300, 250, 60, 1075192, 0x7FFF); // Gauntlets of Valor + AddImageTiledButton(264, 300, 0x918, 0x919, 0x6D, GumpButtonType.Reply, 0, 0x2B01, 0x0, 18, 9); + AddTooltip(1075225); + AddHtmlLocalized(348, 300, 250, 60, 1075196, 0x7FFF); // Dupre’s Shield + AddButton(400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, 2); + AddHtmlLocalized(440, 376, 60, 20, 1043353, 0x7FFF); // Next + + AddPage(2); + + AddButton(300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, 1); + AddHtmlLocalized(340, 376, 60, 20, 1011393, 0x7FFF); // Back + AddImageTiledButton(14, 44, 0x918, 0x919, 0x6E, GumpButtonType.Reply, 0, 0x2AC6, 0x0, 29, 0); + AddTooltip(1075226); + AddHtmlLocalized(98, 44, 250, 60, 1075197, 0x7FFF); // Fountain of Life + AddImageTiledButton(264, 44, 0x918, 0x919, 0x6F, GumpButtonType.Reply, 0, 0x2AF9, 0x0, -4, -5); + AddTooltip(1075227); + AddHtmlLocalized(348, 44, 250, 60, 1075198, 0x7FFF); // Dawn’s Music Box + AddImageTiledButton(14, 108, 0x918, 0x919, 0x70, GumpButtonType.Reply, 0, 0x2253, 0x0, 18, 12); + AddTooltip(1075228); + AddHtmlLocalized(98, 108, 250, 60, 1078148, 0x7FFF); // Ossian Grimoire + AddImageTiledButton(264, 108, 0x918, 0x919, 0x71, GumpButtonType.Reply, 0, 0x2D98, 0x0, 19, 13); + AddTooltip(1078527); + AddHtmlLocalized(348, 108, 250, 60, 1078142, 0x7FFF); // Talisman of the Fey:
Ferret + AddImageTiledButton(14, 172, 0x918, 0x919, 0x72, GumpButtonType.Reply, 0, 0x2D97, 0x0, 19, 13); + AddTooltip(1078528); + AddHtmlLocalized(98, 172, 250, 60, 1078143, 0x7FFF); // Talisman of the Fey:
Squirrel + AddImageTiledButton(264, 172, 0x918, 0x919, 0x73, GumpButtonType.Reply, 0, 0x2D96, 0x0, 19, 8); + AddTooltip(1078529); + AddHtmlLocalized(348, 172, 250, 60, 1078144, 0x7FFF); // Talisman of the Fey:
Cu Sidhe + AddImageTiledButton(14, 236, 0x918, 0x919, 0x74, GumpButtonType.Reply, 0, 0x2D95, 0x0, -4, 2); + AddTooltip(1078530); + AddHtmlLocalized(98, 236, 250, 60, 1078145, 0x7FFF); // Talisman of the Fey:
Reptalon + AddImageTiledButton(264, 236, 0x918, 0x919, 0x75, GumpButtonType.Reply, 0, 0x2B02, 0x0, -2, 9); + AddTooltip(1078526); + AddHtmlLocalized(348, 236, 250, 60, 1075201, 0x7FFF); // Quiver of Infinity + AddImageTiledButton(14, 300, 0x918, 0x919, 0x76, GumpButtonType.Reply, 0, 0x2A91, 0x0, 25, 5); + AddTooltip(1075986); + AddHtmlLocalized(98, 300, 250, 60, 1074797, 0x7FFF); // Bone Throne, Bone Couch
and Bone Table + AddImageTiledButton(264, 300, 0x918, 0x919, 0x77, GumpButtonType.Reply, 0, 0x2A99, 0x0, 18, 1); + AddTooltip(1075987); + AddHtmlLocalized(348, 300, 250, 60, 1078146, 0x7FFF); // Creepy Portraits + AddButton(400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, 3); + AddHtmlLocalized(440, 376, 60, 20, 1043353, 0x7FFF); // Next + + AddPage(3); + + AddButton(300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, 2); + AddHtmlLocalized(340, 376, 60, 20, 1011393, 0x7FFF); // Back + AddImageTiledButton(14, 44, 0x918, 0x919, 0x78, GumpButtonType.Reply, 0, 0x2A71, 0x0, 13, 5); + AddTooltip(1075988); + AddHtmlLocalized(98, 44, 250, 60, 1074799, 0x7FFF); // Mounted Pixies (5) + AddImageTiledButton(264, 44, 0x918, 0x919, 0x79, GumpButtonType.Reply, 0, 0x2A98, 0x0, 26, 1); + AddTooltip(1075990); + AddHtmlLocalized(348, 44, 250, 60, 1074800, 0x7FFF); // Haunted Mirror + AddImageTiledButton(14, 108, 0x918, 0x919, 0x7A, GumpButtonType.Reply, 0, 0x2A92, 0x0, 18, 1); + AddTooltip(1075989); + AddHtmlLocalized(98, 108, 250, 60, 1074801, 0x7FFF); // Bed of Nails + AddImageTiledButton(264, 108, 0x918, 0x919, 0x7B, GumpButtonType.Reply, 0, 0x2AB8, 0x0, 18, 1); + AddTooltip(1075991); + AddHtmlLocalized(348, 108, 250, 60, 1074818, 0x7FFF); // Sacrificial Altar + AddImageTiledButton(14, 172, 0x918, 0x919, 0x7C, GumpButtonType.Reply, 0, 0x3F26, 0x0, 18, 8); + AddTooltip(1076610); + AddHtmlLocalized(98, 172, 250, 60, 1076257, 0x7FFF); // Broken Covered Chair + AddImageTiledButton(264, 172, 0x918, 0x919, 0x7D, GumpButtonType.Reply, 0, 0x3F22, 0x0, 18, 8); + AddTooltip(1076610); + AddHtmlLocalized(348, 172, 250, 60, 1076258, 0x7FFF); // Broken Bookcase + AddImageTiledButton(14, 236, 0x918, 0x919, 0x7E, GumpButtonType.Reply, 0, 0x3F24, 0x0, 18, 8); + AddTooltip(1076610); + AddHtmlLocalized(98, 236, 250, 60, 1076259, 0x7FFF); // Standing Broken Chair + AddImageTiledButton(264, 236, 0x918, 0x919, 0x7F, GumpButtonType.Reply, 0, 0x3F25, 0x0, 18, 8); + AddTooltip(1076610); + AddHtmlLocalized(348, 236, 250, 60, 1076260, 0x7FFF); // Broken Vanity + AddImageTiledButton(14, 300, 0x918, 0x919, 0x80, GumpButtonType.Reply, 0, 0x3F23, 0x0, 18, 8); + AddTooltip(1076610); + AddHtmlLocalized(98, 300, 250, 60, 1076261, 0x7FFF); // Broken Chest of Drawers + AddImageTiledButton(264, 300, 0x918, 0x919, 0x81, GumpButtonType.Reply, 0, 0x3F21, 0x0, 18, 8); + AddTooltip(1076610); + AddHtmlLocalized(348, 300, 250, 60, 1076262, 0x7FFF); // Broken Armoire + AddButton(400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, 4); + AddHtmlLocalized(440, 376, 60, 20, 1043353, 0x7FFF); // Next + + AddPage(4); + + AddButton(300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, 3); + AddHtmlLocalized(340, 376, 60, 20, 1011393, 0x7FFF); // Back + AddImageTiledButton(14, 44, 0x918, 0x919, 0x82, GumpButtonType.Reply, 0, 0x3F0B, 0x0, 18, 8); + AddTooltip(1076610); + AddHtmlLocalized(98, 44, 250, 60, 1076263, 0x7FFF); // Broken Bed + AddImageTiledButton(264, 44, 0x918, 0x919, 0x83, GumpButtonType.Reply, 0, 0xC19, 0x0, 13, 8); + AddTooltip(1076610); + AddHtmlLocalized(348, 44, 250, 60, 1076264, 0x7FFF); // Broken Fallen Chair + AddImageTiledButton(14, 108, 0x918, 0x919, 0x84, GumpButtonType.Reply, 0, 0x3DAA, 0x0, 20, -3); + AddTooltip(1076611); + AddHtmlLocalized(98, 108, 250, 60, 1076265, 0x7FFF); // Suit of Gold Armor + AddImageTiledButton(264, 108, 0x918, 0x919, 0x85, GumpButtonType.Reply, 0, 0x151C, 0x0, -20, -3); + AddTooltip(1076612); + AddHtmlLocalized(348, 108, 250, 60, 1076266, 0x7FFF); // Suit of Silver Armor + AddImageTiledButton(14, 172, 0x918, 0x919, 0x86, GumpButtonType.Reply, 0, 0x3DB1, 0x0, 18, 8); + AddTooltip(1076613); + AddHtmlLocalized(98, 172, 250, 60, 1076267, 0x7FFF); // Boiling Cauldron + AddImageTiledButton(264, 172, 0x918, 0x919, 0x87, GumpButtonType.Reply, 0, 0x3F27, 0x0, 18, 8); + AddTooltip(1076614); + AddHtmlLocalized(348, 172, 250, 60, 1024656, 0x7FFF); // Guillotine + AddImageTiledButton(14, 236, 0x918, 0x919, 0x88, GumpButtonType.Reply, 0, 0x3F0C, 0x0, 18, 8); + AddTooltip(1076615); + AddHtmlLocalized(98, 236, 250, 60, 1076268, 0x7FFF); // Cherry Blossom Tree + AddImageTiledButton(264, 236, 0x918, 0x919, 0x89, GumpButtonType.Reply, 0, 0x3F07, 0x0, 18, 8); + AddTooltip(1076616); + AddHtmlLocalized(348, 236, 250, 60, 1076269, 0x7FFF); // Apple Tree + AddImageTiledButton(14, 300, 0x918, 0x919, 0x8A, GumpButtonType.Reply, 0, 0x3F16, 0x0, 18, 8); + AddTooltip(1076617); + AddHtmlLocalized(98, 300, 250, 60, 1076270, 0x7FFF); // Peach Tree + AddImageTiledButton(264, 300, 0x918, 0x919, 0x8B, GumpButtonType.Reply, 0, 0x3F12, 0x0, 18, 8); + AddTooltip(1076618); + AddHtmlLocalized(348, 300, 250, 60, 1076271, 0x7FFF); // Hanging Axes + AddButton(400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, 5); + AddHtmlLocalized(440, 376, 60, 20, 1043353, 0x7FFF); // Next + + AddPage(5); + + AddButton(300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, 4); + AddHtmlLocalized(340, 376, 60, 20, 1011393, 0x7FFF); // Back + AddImageTiledButton(14, 44, 0x918, 0x919, 0x8C, GumpButtonType.Reply, 0, 0x3F13, 0x0, 18, 8); + AddTooltip(1076619); + AddHtmlLocalized(98, 44, 250, 60, 1076272, 0x7FFF); // Hanging Swords + AddImageTiledButton(264, 44, 0x918, 0x919, 0x8D, GumpButtonType.Reply, 0, 0x3F09, 0x0, 18, 8); + AddTooltip(1076620); + AddHtmlLocalized(348, 44, 250, 60, 1076273, 0x7FFF); // Blue fancy rug + AddImageTiledButton(14, 108, 0x918, 0x919, 0x8E, GumpButtonType.Reply, 0, 0x3F0E, 0x0, 18, 8); + AddTooltip(1076621); + AddHtmlLocalized(98, 108, 250, 60, 1076274, 0x7FFF); // Coffin + AddImageTiledButton(264, 108, 0x918, 0x919, 0x8F, GumpButtonType.Reply, 0, 0x3F1F, 0x0, 18, 8); + AddTooltip(1076623); + AddHtmlLocalized(348, 108, 250, 60, 1074027, 0x7FFF); // Vanity + AddImageTiledButton(14, 172, 0x918, 0x919, 0x90, GumpButtonType.Reply, 0, 0x118B, 0x0, -4, -9); + AddTooltip(1076624); + AddHtmlLocalized(98, 172, 250, 60, 1076635, 0x7FFF); // Table With A Purple
Tablecloth + AddImageTiledButton(264, 172, 0x918, 0x919, 0x91, GumpButtonType.Reply, 0, 0x118C, 0x0, -4, -9); + AddTooltip(1076624); + AddHtmlLocalized(348, 172, 250, 60, 1076636, 0x7FFF); // Table With A Blue
Tablecloth + AddImageTiledButton(14, 236, 0x918, 0x919, 0x92, GumpButtonType.Reply, 0, 0x118D, 0x0, -4, -9); + AddTooltip(1076624); + AddHtmlLocalized(98, 236, 250, 60, 1076637, 0x7FFF); // Table With A Red
Tablecloth + AddImageTiledButton(264, 236, 0x918, 0x919, 0x93, GumpButtonType.Reply, 0, 0x118E, 0x0, -4, -9); + AddTooltip(1076624); + AddHtmlLocalized(348, 236, 250, 60, 1076638, 0x7FFF); // Table With An Orange
Tablecloth + AddImageTiledButton(14, 300, 0x918, 0x919, 0x94, GumpButtonType.Reply, 0, 0x3F1E, 0x0, 18, 8); + AddTooltip(1076625); + AddHtmlLocalized(98, 300, 250, 60, 1076279, 0x7FFF); // Unmade Bed + AddImageTiledButton(264, 300, 0x918, 0x919, 0x95, GumpButtonType.Reply, 0, 0x3F0F, 0x0, 18, 8); + AddTooltip(1076626); + AddHtmlLocalized(348, 300, 250, 60, 1076280, 0x7FFF); // Curtains + AddButton(400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, 6); + AddHtmlLocalized(440, 376, 60, 20, 1043353, 0x7FFF); // Next + + AddPage(6); + + AddButton(300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, 5); + AddHtmlLocalized(340, 376, 60, 20, 1011393, 0x7FFF); // Back + AddImageTiledButton(14, 44, 0x918, 0x919, 0x96, GumpButtonType.Reply, 0, 0x1E34, 0x0, 18, -17); + AddTooltip(1076627); + AddHtmlLocalized(98, 44, 250, 60, 1076281, 0x7FFF); // Scarecrow + AddImageTiledButton(264, 44, 0x918, 0x919, 0x97, GumpButtonType.Reply, 0, 0xA0C, 0x0, 18, 8); + AddTooltip(1076628); + AddHtmlLocalized(348, 44, 250, 60, 1076282, 0x7FFF); // Wall Torch + AddImageTiledButton(14, 108, 0x918, 0x919, 0x98, GumpButtonType.Reply, 0, 0x3F10, 0x0, 18, 9); + AddTooltip(1076629); + AddHtmlLocalized(98, 108, 250, 60, 1076283, 0x7FFF); // Fountain + AddImageTiledButton(264, 108, 0x918, 0x919, 0x99, GumpButtonType.Reply, 0, 0x3F19, 0x0, 18, 8); + AddTooltip(1076630); + AddHtmlLocalized(348, 108, 250, 60, 1076284, 0x7FFF); // Statue + AddImageTiledButton(14, 172, 0x918, 0x919, 0x9A, GumpButtonType.Reply, 0, 0x1EA5, 0x0, 5, -25); + AddTooltip(1076631); + AddHtmlLocalized(98, 172, 250, 60, 1076285, 0x7FFF); // Large Fish Net + AddImageTiledButton(264, 172, 0x918, 0x919, 0x9B, GumpButtonType.Reply, 0, 0x1EA3, 0x0, 18, -27); + AddTooltip(1076632); + AddHtmlLocalized(348, 172, 250, 60, 1076286, 0x7FFF); // Small Fish Net + AddImageTiledButton(14, 236, 0x918, 0x919, 0x9C, GumpButtonType.Reply, 0, 0x2FDF, 0x0, 18, -36); + AddTooltip(1076633); + AddHtmlLocalized(98, 236, 250, 60, 1076287, 0x7FFF); // Ladder + AddImageTiledButton(264, 236, 0x918, 0x919, 0x9D, GumpButtonType.Reply, 0, 0x3F15, 0x0, 18, 8); + AddTooltip(1076622); + AddHtmlLocalized(348, 236, 250, 60, 1076288, 0x7FFF); // Iron Maiden + AddImageTiledButton(14, 300, 0x918, 0x919, 0x9E, GumpButtonType.Reply, 0, 0x3F0A, 0x0, 18, 8); + AddTooltip(1076620); + AddHtmlLocalized(98, 300, 250, 60, 1076585, 0x7FFF); // Blue plain rug + AddImageTiledButton(264, 300, 0x918, 0x919, 0x9F, GumpButtonType.Reply, 0, 0x3F11, 0x0, 18, 8); + AddTooltip(1076620); + AddHtmlLocalized(348, 300, 250, 60, 1076586, 0x7FFF); // Golden decorative rug + AddButton(400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, 7); + AddHtmlLocalized(440, 376, 60, 20, 1043353, 0x7FFF); // Next + + AddPage(7); + + AddButton(300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, 6); + AddHtmlLocalized(340, 376, 60, 20, 1011393, 0x7FFF); // Back + AddImageTiledButton(14, 44, 0x918, 0x919, 0xA0, GumpButtonType.Reply, 0, 0x3F0D, 0x0, 18, 8); + AddTooltip(1076620); + AddHtmlLocalized(98, 44, 250, 60, 1076587, 0x7FFF); // Cinnamon fancy rug + AddImageTiledButton(264, 44, 0x918, 0x919, 0xA1, GumpButtonType.Reply, 0, 0x3F18, 0x0, 18, 8); + AddTooltip(1076620); + AddHtmlLocalized(348, 44, 250, 60, 1076588, 0x7FFF); // Red plain rug + AddImageTiledButton(14, 108, 0x918, 0x919, 0xA2, GumpButtonType.Reply, 0, 0x3F08, 0x0, 18, 8); + AddTooltip(1076620); + AddHtmlLocalized(98, 108, 250, 60, 1076589, 0x7FFF); // Blue decorative rug + AddImageTiledButton(264, 108, 0x918, 0x919, 0xA3, GumpButtonType.Reply, 0, 0x3F17, 0x0, 18, 8); + AddTooltip(1076620); + AddHtmlLocalized(348, 108, 250, 60, 1076590, 0x7FFF); // Pink fancy rug + AddImageTiledButton(14, 172, 0x918, 0x919, 0xA4, GumpButtonType.Reply, 0, 0x312A, 0x0, 18, 8); + AddTooltip(1076615); + AddHtmlLocalized(98, 172, 250, 60, 1076784, 0x7FFF); // Cherry Blossom Trunk + AddImageTiledButton(264, 172, 0x918, 0x919, 0xA5, GumpButtonType.Reply, 0, 0x3128, 0x0, 18, 8); + AddTooltip(1076616); + AddHtmlLocalized(348, 172, 250, 60, 1076785, 0x7FFF); // Apple Trunk + AddImageTiledButton(14, 236, 0x918, 0x919, 0xA6, GumpButtonType.Reply, 0, 0x3129, 0x0, 18, 8); + AddTooltip(1076617); + AddHtmlLocalized(98, 236, 250, 60, 1076786, 0x7FFF); // Peach Trunk } - from.SendGump(new HeritageTokenGump(token)); - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - // Standard static framing - builder.AddBackground(0, 0, 520, 404, 0x13BE); - builder.AddImageTiled(10, 10, 500, 20, 0xA40); - builder.AddImageTiled(10, 40, 500, 324, 0xA40); - builder.AddImageTiled(10, 374, 500, 20, 0xA40); - builder.AddAlphaRegion(10, 10, 500, 384); - builder.AddButton(10, 374, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(45, 376, 450, 20, 1060051, 0x7FFF); // CANCEL - builder.AddHtmlLocalized(14, 12, 500, 20, 1075576, 0x7FFF); // Choose your item from the following pages - - const int itemsPerPage = 10; - int totalItems = ButtonLookup.Count; - int totalPages = (int)Math.Ceiling((double)totalItems / itemsPerPage); - - int index = 0; - int page = 1; - - foreach (var (buttonId, (_, cliloc, tooltip, itemId, hue, width, height)) in ButtonLookup) + public override void OnResponse(NetState sender, in RelayInfo info) { - // When index cleanly divides by 10, we are starting a new page - if (index % itemsPerPage == 0) + if (m_Token?.Deleted != false || info.ButtonID == 0) { - builder.AddPage(page); - - // Add Next Button - if (page < totalPages) - { - builder.AddButton(400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, page + 1); - builder.AddHtmlLocalized(440, 376, 60, 20, 1043353, 0x7FFF); // Next - } - - // Add Back Button - if (page > 1) - { - builder.AddButton(300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, page - 1); - builder.AddHtmlLocalized(340, 376, 60, 20, 1011393, 0x7FFF); // Back - } + return; } - // Calculate dynamic grid placement (2 columns, 5 rows) - int posOnPage = index % itemsPerPage; - int col = posOnPage % 2; // 0 for left, 1 for right - int row = posOnPage / 2; // 0 through 4 + Type[] types; + int cliloc; - int xBase = col == 0 ? 14 : 264; - int yBase = 44 + (row * 64); - int textX = col == 0 ? 98 : 348; - - // Render the UI entries - builder.AddImageTiledButton( - xBase, - yBase, - 0x918, - 0x919, - buttonId, - GumpButtonType.Reply, - 0, - itemId, - hue, - width, - height - ); - - if (tooltip > 0) + switch (info.ButtonID) { - builder.AddTooltip(tooltip); + default: + { + types = null; + cliloc = 0; + break; + } + // 7th anniversary + case 0x64: + { + types = new [] { typeof(LeggingsOfEmbers) }; + cliloc = 1078147; + break; + } + case 0x65: + { + types = new [] { typeof(RoseOfTrinsic) }; + cliloc = 1062913; + break; + } + case 0x66: + { + types = new [] { typeof(ShaminoCrossbow) }; + cliloc = 1062915; + break; + } + case 0x67: + { + types = new [] { typeof(TapestryOfSosaria) }; + cliloc = 1062917; + break; + } + case 0x68: + { + types = new [] { typeof(HearthOfHomeFireDeed) }; + cliloc = 1062919; + break; + } + case 0x69: + { + types = new [] { typeof(HolySword) }; + cliloc = 1062921; + break; + } + case 0x6A: + { + types = new [] { typeof(SamuraiHelm) }; + cliloc = 1062923; + break; + } + + // 8th anniversary + /*case 0x6B: types = new [] { typeof( SpiritualityHelm ) }; cliloc = 1075188; break; + case 0x6C: types = new [] { typeof( ValorGauntlets ) }; cliloc = 1075192; break;*/ + case 0x6D: + { + types = new [] { typeof(DupresShield) }; + cliloc = 1075196; + break; + } + case 0x6E: + { + types = new [] { typeof(FountainOfLifeDeed) }; + cliloc = 1075197; + break; + } + case 0x6F: + { + types = new [] { typeof(DawnsMusicBox) }; + cliloc = 1075198; + break; + } + case 0x70: + { + types = new [] { typeof(OssianGrimoire) }; + cliloc = 1078148; + break; + } + case 0x71: + { + types = new [] { typeof(FerretFormTalisman) }; + cliloc = 1078142; + break; + } + case 0x72: + { + types = new [] { typeof(SquirrelFormTalisman) }; + cliloc = 1078143; + break; + } + case 0x73: + { + types = new [] { typeof(CuSidheFormTalisman) }; + cliloc = 1078144; + break; + } + case 0x74: + { + types = new [] { typeof(ReptalonFormTalisman) }; + cliloc = 1078145; + break; + } + case 0x75: + { + types = new [] { typeof(QuiverOfInfinity) }; + cliloc = 1075201; + break; + } + + // evil home decor + case 0x76: + { + types = new [] { typeof(BoneThroneDeed), typeof(BoneCouchDeed), typeof(BoneTableDeed) }; + cliloc = 1074797; + break; + } + case 0x77: + { + types = new [] + { + typeof(CreepyPortraitDeed), + typeof(DisturbingPortraitDeed), + typeof(UnsettlingPortraitDeed) + }; + cliloc = 1078146; + break; + } + case 0x78: + { + types = new [] + { + typeof(MountedPixieBlueDeed), + typeof(MountedPixieGreenDeed), + typeof(MountedPixieLimeDeed), + typeof(MountedPixieOrangeDeed), + typeof(MountedPixieWhiteDeed) + }; + cliloc = 1074799; + break; + } + case 0x79: + { + types = new [] { typeof(HaunterMirrorDeed) }; + cliloc = 1074800; + break; + } + case 0x7A: + { + types = new [] { typeof(BedOfNailsDeed) }; + cliloc = 1074801; + break; + } + case 0x7B: + { + types = new [] { typeof(SacrificialAltarDeed) }; + cliloc = 1074818; + break; + } + + // broken furniture + case 0x7C: + { + types = new [] { typeof(BrokenCoveredChairDeed) }; + cliloc = 1076257; + break; + } + case 0x7D: + { + types = new [] { typeof(BrokenBookcaseDeed) }; + cliloc = 1076258; + break; + } + case 0x7E: + { + types = new [] { typeof(StandingBrokenChairDeed) }; + cliloc = 1076259; + break; + } + case 0x7F: + { + types = new [] { typeof(BrokenVanityDeed) }; + cliloc = 1076260; + break; + } + case 0x80: + { + types = new [] { typeof(BrokenChestOfDrawersDeed) }; + cliloc = 1076261; + break; + } + case 0x81: + { + types = new [] { typeof(BrokenArmoireDeed) }; + cliloc = 1076262; + break; + } + case 0x82: + { + types = new [] { typeof(BrokenBedDeed) }; + cliloc = 1076263; + break; + } + case 0x83: + { + types = new [] { typeof(BrokenFallenChairDeed) }; + cliloc = 1076264; + break; + } + + // other + case 0x84: + { + types = new [] { typeof(SuitOfGoldArmorDeed) }; + cliloc = 1076265; + break; + } + case 0x85: + { + types = new [] { typeof(SuitOfSilverArmorDeed) }; + cliloc = 1076266; + break; + } + case 0x86: + { + types = new [] { typeof(BoilingCauldronDeed) }; + cliloc = 1076267; + break; + } + case 0x87: + { + types = new [] { typeof(GuillotineDeed) }; + cliloc = 1024656; + break; + } + case 0x88: + { + types = new [] { typeof(CherryBlossomTreeDeed) }; + cliloc = 1076268; + break; + } + case 0x89: + { + types = new [] { typeof(AppleTreeDeed) }; + cliloc = 1076269; + break; + } + case 0x8A: + { + types = new [] { typeof(PeachTreeDeed) }; + cliloc = 1076270; + break; + } + case 0x8B: + { + types = new [] { typeof(HangingAxesDeed) }; + cliloc = 1076271; + break; + } + case 0x8C: + { + types = new [] { typeof(HangingSwordsDeed) }; + cliloc = 1076272; + break; + } + case 0x8D: + { + types = new [] { typeof(BlueFancyRugDeed) }; + cliloc = 1076273; + break; + } + case 0x8E: + { + types = new [] { typeof(WoodenCoffinDeed) }; + cliloc = 1076274; + break; + } + case 0x8F: + { + types = new [] { typeof(VanityDeed) }; + cliloc = 1074027; + break; + } + case 0x90: + { + types = new [] { typeof(TableWithPurpleClothDeed) }; + cliloc = 1076635; + break; + } + case 0x91: + { + types = new [] { typeof(TableWithBlueClothDeed) }; + cliloc = 1076636; + break; + } + case 0x92: + { + types = new [] { typeof(TableWithRedClothDeed) }; + cliloc = 1076637; + break; + } + case 0x93: + { + types = new [] { typeof(TableWithOrangeClothDeed) }; + cliloc = 1076638; + break; + } + case 0x94: + { + types = new [] { typeof(UnmadeBedDeed) }; + cliloc = 1076279; + break; + } + case 0x95: + { + types = new [] { typeof(CurtainsDeed) }; + cliloc = 1076280; + break; + } + case 0x96: + { + types = new [] { typeof(ScarecrowDeed) }; + cliloc = 1076281; + break; + } + case 0x97: + { + types = new [] { typeof(WallTorchDeed) }; + cliloc = 1076282; + break; + } + case 0x98: + { + types = new [] { typeof(FountainDeed) }; + cliloc = 1076283; + break; + } + case 0x99: + { + types = new [] { typeof(StoneStatueDeed) }; + cliloc = 1076284; + break; + } + case 0x9A: + { + types = new [] { typeof(LargeFishingNetDeed) }; + cliloc = 1076285; + break; + } + case 0x9B: + { + types = new [] { typeof(SmallFishingNetDeed) }; + cliloc = 1076286; + break; + } + case 0x9C: + { + types = new [] { typeof(HouseLadderDeed) }; + cliloc = 1076287; + break; + } + case 0x9D: + { + types = new [] { typeof(IronMaidenDeed) }; + cliloc = 1076288; + break; + } + case 0x9E: + { + types = new [] { typeof(BluePlainRugDeed) }; + cliloc = 1076585; + break; + } + case 0x9F: + { + types = new [] { typeof(GoldenDecorativeRugDeed) }; + cliloc = 1076586; + break; + } + case 0xA0: + { + types = new [] { typeof(CinnamonFancyRugDeed) }; + cliloc = 1076587; + break; + } + case 0xA1: + { + types = new [] { typeof(RedPlainRugDeed) }; + cliloc = 1076588; + break; + } + case 0xA2: + { + types = new [] { typeof(BlueDecorativeRugDeed) }; + cliloc = 1076589; + break; + } + case 0xA3: + { + types = new [] { typeof(PinkFancyRugDeed) }; + cliloc = 1076590; + break; + } + case 0xA4: + { + types = new [] { typeof(CherryBlossomTrunkDeed) }; + cliloc = 1076784; + break; + } + case 0xA5: + { + types = new [] { typeof(AppleTrunkDeed) }; + cliloc = 1076785; + break; + } + case 0xA6: + { + types = new [] { typeof(PeachTrunkDeed) }; + cliloc = 1076786; + break; + } } - builder.AddHtmlLocalized(textX, yBase, 250, 60, cliloc, 0x7FFF); - - index++; - - // Bump to the next page when we fill the current one - if (index % itemsPerPage == 0) + if (types?.Length > 0 && cliloc > 0) { - page++; + sender.Mobile.SendGump(new ConfirmHeritageGump(m_Token, types, cliloc)); + } + else + { + // This option is currently disabled, while we evaluate it for game balance. + sender.Mobile.SendLocalizedMessage(501311); } - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (_token?.Deleted != false || info.ButtonID == 0) - { - return; - } - - if (!ButtonLookup.TryGetValue(info.ButtonID, out var buttonInfo)) - { - return; - } - - var (types, cliloc, _, _, _, _, _) = buttonInfo; - - if (types?.Length > 0 && cliloc > 0) - { - ConfirmHeritageGump.DisplayTo(sender.Mobile, _token, types, cliloc); - } - else - { - // This option is currently disabled, while we evaluate it for game balance. - sender.Mobile.SendLocalizedMessage(501311); } } } diff --git a/Projects/UOContent/Gumps/Houses/ConfirmResizeHouseGump.cs b/Projects/UOContent/Gumps/Houses/ConfirmResizeHouseGump.cs index 38767c37c..762caea03 100644 --- a/Projects/UOContent/Gumps/Houses/ConfirmResizeHouseGump.cs +++ b/Projects/UOContent/Gumps/Houses/ConfirmResizeHouseGump.cs @@ -62,7 +62,7 @@ public class ConfirmResizeHouseGump : StaticGump if (info.ButtonID == 0) { - HouseGumpAOS.DisplayTo(from, _house, HouseGumpPageAOS.Customize); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Customize, from, _house)); return; } diff --git a/Projects/UOContent/Gumps/Houses/HouseGump.cs b/Projects/UOContent/Gumps/Houses/HouseGump.cs index 170e1d141..f53141eb0 100644 --- a/Projects/UOContent/Gumps/Houses/HouseGump.cs +++ b/Projects/UOContent/Gumps/Houses/HouseGump.cs @@ -23,6 +23,11 @@ namespace Server.Gumps protected override void BuildLayout(ref DynamicGumpBuilder builder) { + if (_house.Deleted) + { + return; + } + builder.AddPage(); builder.AddBackground(0, 0, 420, 430, 5054); @@ -84,7 +89,7 @@ namespace Server.Gumps var from = state.Mobile; - HouseGump.DisplayTo(from, _house); + from.SendGump(new HouseGump(from, _house)); } } @@ -116,6 +121,11 @@ namespace Server.Gumps protected override void BuildLayout(ref DynamicGumpBuilder builder) { + if (_house.Deleted) + { + return; + } + builder.AddPage(); builder.AddBackground(0, 0, 420, 430, 5054); @@ -210,60 +220,50 @@ namespace Server.Gumps } } - HouseGump.DisplayTo(from, _house); + from.SendGump(new HouseGump(from, _house)); } } - public class HouseGump : DynamicGump + public class HouseGump : Gump { - private readonly Mobile _from; private readonly BaseHouse _house; public override bool Singleton => true; - private HouseGump(Mobile from, BaseHouse house) : base(20, 30) + public HouseGump(Mobile from, BaseHouse house) : base(20, 30) { - _from = from; - _house = house; - } - - public static void DisplayTo(Mobile from, BaseHouse house) - { - if (from?.NetState == null || house == null || house.Deleted) + if (house.Deleted) { return; } + _house = house; + var gumps = from.GetGumps(); gumps.Close(); gumps.Close(); - from.SendGump(new HouseGump(from, house)); - } + var isCombatRestricted = house.IsCombatRestricted(from); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var isCombatRestricted = _house.IsCombatRestricted(_from); - - var isOwner = _house.IsOwner(_from); - var isCoOwner = isOwner || _house.IsCoOwner(_from); - var isFriend = isCoOwner || _house.IsFriend(_from); + var isOwner = _house.IsOwner(from); + var isCoOwner = isOwner || _house.IsCoOwner(from); + var isFriend = isCoOwner || _house.IsFriend(from); if (isCombatRestricted) { isFriend = isCoOwner = isOwner = false; } - builder.AddPage(); + AddPage(0); if (isFriend) { - builder.AddBackground(0, 0, 420, 430, 5054); - builder.AddBackground(10, 10, 400, 410, 3000); + AddBackground(0, 0, 420, 430, 5054); + AddBackground(10, 10, 400, 410, 3000); } - builder.AddImage(130, 0, 100); + AddImage(130, 0, 100); if (_house.Sign != null) { @@ -273,7 +273,7 @@ namespace Server.Gumps { var s = lines[i]; - builder.AddLabel(130 + (143 - s.Length * 8) / 2, y, 0, s); + AddLabel(130 + (143 - s.Length * 8) / 2, y, 0, s); } } @@ -282,148 +282,148 @@ namespace Server.Gumps return; } - builder.AddHtmlLocalized(55, 103, 75, 20, 1011233); // INFO - builder.AddButton(20, 103, 4005, 4007, 0, GumpButtonType.Page, 1); + AddHtmlLocalized(55, 103, 75, 20, 1011233); // INFO + AddButton(20, 103, 4005, 4007, 0, GumpButtonType.Page, 1); - builder.AddHtmlLocalized(170, 103, 75, 20, 1011234); // FRIENDS - builder.AddButton(135, 103, 4005, 4007, 0, GumpButtonType.Page, 2); + AddHtmlLocalized(170, 103, 75, 20, 1011234); // FRIENDS + AddButton(135, 103, 4005, 4007, 0, GumpButtonType.Page, 2); - builder.AddHtmlLocalized(295, 103, 75, 20, 1011235); // OPTIONS - builder.AddButton(260, 103, 4005, 4007, 0, GumpButtonType.Page, 3); + AddHtmlLocalized(295, 103, 75, 20, 1011235); // OPTIONS + AddButton(260, 103, 4005, 4007, 0, GumpButtonType.Page, 3); - builder.AddHtmlLocalized(295, 390, 75, 20, 1011441); // EXIT - builder.AddButton(260, 390, 4005, 4007, 0); + AddHtmlLocalized(295, 390, 75, 20, 1011441); // EXIT + AddButton(260, 390, 4005, 4007, 0); - builder.AddHtmlLocalized(55, 390, 200, 20, 1011236); // Change this house's name! - builder.AddButton(20, 390, 4005, 4007, 1); + AddHtmlLocalized(55, 390, 200, 20, 1011236); // Change this house's name! + AddButton(20, 390, 4005, 4007, 1); // Info page - builder.AddPage(1); + AddPage(1); - builder.AddHtmlLocalized(20, 135, 100, 20, 1011242); // Owned by: - builder.AddHtml(120, 135, 100, 20, GetOwnerName()); + AddHtmlLocalized(20, 135, 100, 20, 1011242); // Owned by: + AddHtml(120, 135, 100, 20, GetOwnerName()); - builder.AddHtmlLocalized(20, 170, 275, 20, 1011237); // Number of locked down items: - builder.AddHtml(320, 170, 50, 20, $"{_house.LockDownCount}"); + AddHtmlLocalized(20, 170, 275, 20, 1011237); // Number of locked down items: + AddHtml(320, 170, 50, 20, _house.LockDownCount.ToString()); - builder.AddHtmlLocalized(20, 190, 275, 20, 1011238); // Maximum locked down items: - builder.AddHtml(320, 190, 50, 20, $"{_house.MaxLockDowns}"); + AddHtmlLocalized(20, 190, 275, 20, 1011238); // Maximum locked down items: + AddHtml(320, 190, 50, 20, _house.MaxLockDowns.ToString()); - builder.AddHtmlLocalized(20, 210, 275, 20, 1011239); // Number of secure containers: - builder.AddHtml(320, 210, 50, 20, $"{_house.SecureCount}"); + AddHtmlLocalized(20, 210, 275, 20, 1011239); // Number of secure containers: + AddHtml(320, 210, 50, 20, _house.SecureCount.ToString()); - builder.AddHtmlLocalized(20, 230, 275, 20, 1011240); // Maximum number of secure containers: - builder.AddHtml(320, 230, 50, 20, $"{_house.MaxSecures}"); + AddHtmlLocalized(20, 230, 275, 20, 1011240); // Maximum number of secure containers: + AddHtml(320, 230, 50, 20, _house.MaxSecures.ToString()); - builder.AddHtmlLocalized(20, 260, 400, 20, 1018032); // This house is properly placed. - builder.AddHtmlLocalized(20, 280, 400, 20, 1018035); // This house is of modern design. + AddHtmlLocalized(20, 260, 400, 20, 1018032); // This house is properly placed. + AddHtmlLocalized(20, 280, 400, 20, 1018035); // This house is of modern design. if (_house.Public) { // TODO: Validate exact placement - builder.AddHtmlLocalized(20, 305, 275, 20, 1011241); // Number of visits this building has had - builder.AddHtml(320, 305, 50, 20, $"{_house.Visits}"); + AddHtmlLocalized(20, 305, 275, 20, 1011241); // Number of visits this building has had + AddHtml(320, 305, 50, 20, _house.Visits.ToString()); } // Friends page - builder.AddPage(2); + AddPage(2); - builder.AddHtmlLocalized(45, 130, 150, 20, 1011266); // List of co-owners - builder.AddButton(20, 130, 2714, 2715, 2); + AddHtmlLocalized(45, 130, 150, 20, 1011266); // List of co-owners + AddButton(20, 130, 2714, 2715, 2); - builder.AddHtmlLocalized(45, 150, 150, 20, 1011267); // Add a co-owner - builder.AddButton(20, 150, 2714, 2715, 3); + AddHtmlLocalized(45, 150, 150, 20, 1011267); // Add a co-owner + AddButton(20, 150, 2714, 2715, 3); - builder.AddHtmlLocalized(45, 170, 150, 20, 1018036); // Remove a co-owner - builder.AddButton(20, 170, 2714, 2715, 4); + AddHtmlLocalized(45, 170, 150, 20, 1018036); // Remove a co-owner + AddButton(20, 170, 2714, 2715, 4); - builder.AddHtmlLocalized(45, 190, 150, 20, 1011268); // Clear co-owner list - builder.AddButton(20, 190, 2714, 2715, 5); + AddHtmlLocalized(45, 190, 150, 20, 1011268); // Clear co-owner list + AddButton(20, 190, 2714, 2715, 5); - builder.AddHtmlLocalized(225, 130, 155, 20, 1011243); // List of Friends - builder.AddButton(200, 130, 2714, 2715, 6); + AddHtmlLocalized(225, 130, 155, 20, 1011243); // List of Friends + AddButton(200, 130, 2714, 2715, 6); - builder.AddHtmlLocalized(225, 150, 155, 20, 1011244); // Add a Friend - builder.AddButton(200, 150, 2714, 2715, 7); + AddHtmlLocalized(225, 150, 155, 20, 1011244); // Add a Friend + AddButton(200, 150, 2714, 2715, 7); - builder.AddHtmlLocalized(225, 170, 155, 20, 1018037); // Remove a Friend - builder.AddButton(200, 170, 2714, 2715, 8); + AddHtmlLocalized(225, 170, 155, 20, 1018037); // Remove a Friend + AddButton(200, 170, 2714, 2715, 8); - builder.AddHtmlLocalized(225, 190, 155, 20, 1011245); // Clear Friends list - builder.AddButton(200, 190, 2714, 2715, 9); + AddHtmlLocalized(225, 190, 155, 20, 1011245); // Clear Friends list + AddButton(200, 190, 2714, 2715, 9); - builder.AddHtmlLocalized(120, 215, 280, 20, 1011258); // Ban someone from the house - builder.AddButton(95, 215, 2714, 2715, 10); + AddHtmlLocalized(120, 215, 280, 20, 1011258); // Ban someone from the house + AddButton(95, 215, 2714, 2715, 10); - builder.AddHtmlLocalized(120, 235, 280, 20, 1011259); // Eject someone from the house - builder.AddButton(95, 235, 2714, 2715, 11); + AddHtmlLocalized(120, 235, 280, 20, 1011259); // Eject someone from the house + AddButton(95, 235, 2714, 2715, 11); - builder.AddHtmlLocalized(120, 255, 280, 20, 1011260); // View a list of banned people - builder.AddButton(95, 255, 2714, 2715, 12); + AddHtmlLocalized(120, 255, 280, 20, 1011260); // View a list of banned people + AddButton(95, 255, 2714, 2715, 12); - builder.AddHtmlLocalized(120, 275, 280, 20, 1011261); // Lift a ban - builder.AddButton(95, 275, 2714, 2715, 13); + AddHtmlLocalized(120, 275, 280, 20, 1011261); // Lift a ban + AddButton(95, 275, 2714, 2715, 13); // Options page - builder.AddPage(3); + AddPage(3); - builder.AddHtmlLocalized(45, 150, 355, 30, 1011248); // Transfer ownership of the house - builder.AddButton(20, 150, 2714, 2715, 14); + AddHtmlLocalized(45, 150, 355, 30, 1011248); // Transfer ownership of the house + AddButton(20, 150, 2714, 2715, 14); - builder.AddHtmlLocalized(45, 180, 355, 30, 1011249); // Demolish house and get deed back - builder.AddButton(20, 180, 2714, 2715, 15); + AddHtmlLocalized(45, 180, 355, 30, 1011249); // Demolish house and get deed back + AddButton(20, 180, 2714, 2715, 15); if (!_house.Public) { - builder.AddHtmlLocalized(45, 210, 355, 30, 1011247); // Change the house locks - builder.AddButton(20, 210, 2714, 2715, 16); + AddHtmlLocalized(45, 210, 355, 30, 1011247); // Change the house locks + AddButton(20, 210, 2714, 2715, 16); - builder.AddHtmlLocalized( + AddHtmlLocalized( 45, 240, 350, 90, 1011253 ); // Declare this building to be public. This will make your front door unlockable. - builder.AddButton(20, 240, 2714, 2715, 17); + AddButton(20, 240, 2714, 2715, 17); } else { // AddHtmlLocalized( 45, 280, 350, 30, 1011250, false, false ); // Change the sign type - builder.AddHtmlLocalized(45, 210, 350, 30, 1011250); // Change the sign type - builder.AddButton(20, 210, 2714, 2715, 0, GumpButtonType.Page, 4); + AddHtmlLocalized(45, 210, 350, 30, 1011250); // Change the sign type + AddButton(20, 210, 2714, 2715, 0, GumpButtonType.Page, 4); - builder.AddHtmlLocalized(45, 240, 350, 30, 1011252); // Declare this building to be private. - builder.AddButton(20, 240, 2714, 2715, 17); + AddHtmlLocalized(45, 240, 350, 30, 1011252); // Declare this building to be private. + AddButton(20, 240, 2714, 2715, 17); // Change the sign type - builder.AddPage(4); + AddPage(4); for (var i = 0; i < 24; ++i) { - builder.AddRadio(53 + i / 4 * 50, 137 + i % 4 * 35, 210, 211, false, i + 1); - builder.AddItem(60 + i / 4 * 50, 130 + i % 4 * 35, 2980 + i * 2); + AddRadio(53 + i / 4 * 50, 137 + i % 4 * 35, 210, 211, false, i + 1); + AddItem(60 + i / 4 * 50, 130 + i % 4 * 35, 2980 + i * 2); } - builder.AddHtmlLocalized(200, 305, 129, 20, 1011254); // Guild sign choices - builder.AddButton(350, 305, 252, 253, 0, GumpButtonType.Page, 5); + AddHtmlLocalized(200, 305, 129, 20, 1011254); // Guild sign choices + AddButton(350, 305, 252, 253, 0, GumpButtonType.Page, 5); - builder.AddHtmlLocalized(200, 340, 355, 30, 1011277); // Okay that is fine. - builder.AddButton(350, 340, 4005, 4007, 18); + AddHtmlLocalized(200, 340, 355, 30, 1011277); // Okay that is fine. + AddButton(350, 340, 4005, 4007, 18); - builder.AddPage(5); + AddPage(5); for (var i = 0; i < 29; ++i) { - builder.AddRadio(53 + i / 5 * 50, 137 + i % 5 * 35, 210, 211, false, i + 25); - builder.AddItem(60 + i / 5 * 50, 130 + i % 5 * 35, 3028 + i * 2); + AddRadio(53 + i / 5 * 50, 137 + i % 5 * 35, 210, 211, false, i + 25); + AddItem(60 + i / 5 * 50, 130 + i % 5 * 35, 3028 + i * 2); } - builder.AddHtmlLocalized(200, 305, 129, 20, 1011255); // Shop sign choices - builder.AddButton(350, 305, 250, 251, 0, GumpButtonType.Page, 4); + AddHtmlLocalized(200, 305, 129, 20, 1011255); // Shop sign choices + AddButton(350, 305, 250, 251, 0, GumpButtonType.Page, 4); - builder.AddHtmlLocalized(200, 340, 355, 30, 1011277); // Okay that is fine. - builder.AddButton(350, 340, 4005, 4007, 18); + AddHtmlLocalized(200, 340, 355, 30, 1011277); // Okay that is fine. + AddButton(350, 340, 4005, 4007, 18); } } diff --git a/Projects/UOContent/Gumps/Houses/HouseGumpAOS.cs b/Projects/UOContent/Gumps/Houses/HouseGumpAOS.cs index 00af234b8..1e9ba2428 100644 --- a/Projects/UOContent/Gumps/Houses/HouseGumpAOS.cs +++ b/Projects/UOContent/Gumps/Houses/HouseGumpAOS.cs @@ -31,7 +31,7 @@ namespace Server.Gumps Vendors } - public class HouseGumpAOS : DynamicGump + public class HouseGumpAOS : Gump { private const int LabelColor = 0x7FFF; private const int SelectedColor = 0x421F; @@ -41,164 +41,93 @@ namespace Server.Gumps private const int LabelHue = 0x481; private const int HighlightedLabelHue = 0x64; - private static readonly int[] _hangerNumbers = - [ + private static readonly int[] m_HangerNumbers = + { 2968, 2970, 2972, 2974, 2976, 2978 - ]; + }; - private static readonly int[] _foundationNumbers = - Core.ML ? [20, 189, 765, 65, 101, 0x2DF7, 0x2DFB, 0x3672, 0x3676] : [20, 189, 765, 65, 101]; + private static readonly int[] m_FoundationNumbers = Core.ML + ? new[] + { + 20, 189, 765, 65, 101, 0x2DF7, 0x2DFB, 0x3672, 0x3676 + } + : new[] + { + 20, 189, 765, 65, 101 + }; - private static readonly int[] _postNumbers = - [ + private static readonly int[] m_PostNumbers = + { 9, 29, 54, 90, 147, 169, 177, 204, 251, 257, 263, 298, 347, 424, 441, 466, 514, 600, 601, 602, 603, 660, 666, 672, 898, 970, 974, 982 - ]; + }; - // Sign graphics for the Change House Sign menu. Each pair of consecutive item IDs in - // the art is the same sign with a different orientation; we expose only the even ID. - // The last two entries (2966 Library, 3140 Beekeeper) are ML-only and are appended at - // the end so the older clients can take a contiguous slice of the first 54 entries. - private static readonly int[] _houseSigns = - [ - 2980, // Bakery - 2982, // Tailor - 2984, // Tinker - 2986, // Butcher - 2988, // Healer - 2990, // Mage - 2992, // Woodworker - 2994, // Customs - 2996, // Inn - 2998, // Shipwright - 3000, // Stables - 3002, // Barber Shop - 3004, // Bard - 3006, // Fletcher - 3008, // Armourer - 3010, // Jeweler - 3012, // Tavern - 3014, // Reagent Shop - 3016, // Blacksmith - 3018, // Painter - 3020, // Provisioner - 3022, // Bowyer - 3024, // Wooden sign - 3026, // Brass sign - 3028, // Armaments Guild - 3030, // Armourers' Guild - 3032, // Blacksmiths' Guild - 3034, // Weapons Guild - 3036, // Bardic Guild - 3038, // Barters' Guild - 3040, // Provisioner Guild - 3042, // Traders' Guild - 3044, // Cooks' Guild - 3046, // Healers' Guild - 3048, // Mages' Guild - 3050, // Sorcerers' Guild - 3052, // Illusionist Guild - 3054, // Miners' Guild - 3056, // Archers' Guild - 3058, // Seamens' Guild - 3060, // Fishermens' Guild - 3062, // Sailors' Guild - 3064, // Shipwrights' Guild - 3066, // Tailors' Guild - 3068, // Thieves' Guild - 3070, // Rogues' Guild - 3072, // Assassins' Guild - 3074, // Tinkers' Guild - 3076, // Warriors' Guild - 3078, // Cavalry Guild - 3080, // Fighters' Guild - 3082, // Merchants' Guild - 3084, // Bank - 3086, // Theatre - 2966, // Library (ML) - 3140 // Beekeeper (ML) - ]; + private static readonly List _HouseSigns = new(); + private readonly BaseHouse m_House; + private readonly HouseGumpPageAOS m_Page; - private readonly BaseHouse _house; - private readonly HouseGumpPageAOS _page; - private readonly Mobile _from; - - private List _list; + private List m_List; public override bool Singleton => true; - private HouseGumpAOS(HouseGumpPageAOS page, Mobile from, BaseHouse house) : base(50, 40) + public HouseGumpAOS(HouseGumpPageAOS page, Mobile from, BaseHouse house) : base(50, 40) { - _house = house; - _page = page; - _from = from; - } + m_House = house; + m_Page = page; - public static void DisplayTo(Mobile from, BaseHouse house, HouseGumpPageAOS page = HouseGumpPageAOS.Information) - { - if (from?.NetState == null || house == null || house.Deleted) - { - return; - } + var isCombatRestricted = house.IsCombatRestricted(from); - from.SendGump(new HouseGumpAOS(page, from, house)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var isCombatRestricted = _house.IsCombatRestricted(_from); - - var isOwner = _house.IsOwner(_from); - var isCoOwner = isOwner || _house.IsCoOwner(_from); - var isFriend = isCoOwner || _house.IsFriend(_from); + var isOwner = house.IsOwner(from); + var isCoOwner = isOwner || house.IsCoOwner(from); + var isFriend = isCoOwner || house.IsFriend(from); if (isCombatRestricted) { isFriend = isCoOwner = isOwner = false; } - builder.AddPage(); + AddPage(0); - if (isFriend || _page == HouseGumpPageAOS.Vendors) + if (isFriend || page == HouseGumpPageAOS.Vendors) { - builder.AddBackground(0, 0, 420, _page != HouseGumpPageAOS.Vendors ? 440 : 420, 5054); + AddBackground(0, 0, 420, page != HouseGumpPageAOS.Vendors ? 440 : 420, 5054); - builder.AddImageTiled(10, 10, 400, 100, 2624); - builder.AddAlphaRegion(10, 10, 400, 100); + AddImageTiled(10, 10, 400, 100, 2624); + AddAlphaRegion(10, 10, 400, 100); - builder.AddImageTiled(10, 120, 400, 260, 2624); - builder.AddAlphaRegion(10, 120, 400, 260); + AddImageTiled(10, 120, 400, 260, 2624); + AddAlphaRegion(10, 120, 400, 260); - builder.AddImageTiled(10, 390, 400, _page != HouseGumpPageAOS.Vendors ? 40 : 20, 2624); - builder.AddAlphaRegion(10, 390, 400, _page != HouseGumpPageAOS.Vendors ? 40 : 20); + AddImageTiled(10, 390, 400, page != HouseGumpPageAOS.Vendors ? 40 : 20, 2624); + AddAlphaRegion(10, 390, 400, page != HouseGumpPageAOS.Vendors ? 40 : 20); - AddButtonLabeled(ref builder, 250, _page != HouseGumpPageAOS.Vendors ? 410 : 390, 0, 1060675); // CLOSE + AddButtonLabeled(250, page != HouseGumpPageAOS.Vendors ? 410 : 390, 0, 1060675); // CLOSE } - builder.AddImage(10, 10, 100); + AddImage(10, 10, 100); - var lines = _house.Sign?.GetName().Wrap(10, 6); - - if (lines != null) + if (m_House.Sign != null) { + var lines = m_House.Sign.GetName().Wrap(10, 6); + for (int i = 0, y = (114 - lines.Count * 14) / 2; i < lines.Count; ++i, y += 14) { var s = lines[i]; - builder.AddLabel(10 + (160 - s.Length * 8) / 2, y, 0, s); + AddLabel(10 + (160 - s.Length * 8) / 2, y, 0, s); } } - if (_page == HouseGumpPageAOS.Vendors) + if (page == HouseGumpPageAOS.Vendors) { - builder.AddHtmlLocalized(10, 120, 400, 20, 1062428, LabelColor); //
SHOPS
+ AddHtmlLocalized(10, 120, 400, 20, 1062428, LabelColor); //
SHOPS
- AddList(ref builder, _house.AvailableVendorsFor(_from), 1, false, false); + AddList(house.AvailableVendorsFor(from), 1, false, false, from); return; } @@ -207,208 +136,242 @@ namespace Server.Gumps return; } - if (_house.Public) + if (house.Public) { - AddButtonLabeled(ref builder, 10, 390, GetButtonID(0, 0), 1060674); // Banish - AddButtonLabeled(ref builder, 10, 410, GetButtonID(0, 1), 1011261); // Lift a Ban + AddButtonLabeled(10, 390, GetButtonID(0, 0), 1060674); // Banish + AddButtonLabeled(10, 410, GetButtonID(0, 1), 1011261); // Lift a Ban } else { - AddButtonLabeled(ref builder, 10, 390, GetButtonID(0, 2), 1060676); // Grant Access - AddButtonLabeled(ref builder, 10, 410, GetButtonID(0, 3), 1060677); // Revoke Access + AddButtonLabeled(10, 390, GetButtonID(0, 2), 1060676); // Grant Access + AddButtonLabeled(10, 410, GetButtonID(0, 3), 1060677); // Revoke Access } - AddPageButton(ref builder, 150, 10, GetButtonID(1, 0), 1060668, HouseGumpPageAOS.Information); - AddPageButton(ref builder, 150, 30, GetButtonID(1, 1), 1060669, HouseGumpPageAOS.Security); - AddPageButton(ref builder, 150, 50, GetButtonID(1, 2), 1060670, HouseGumpPageAOS.Storage); - AddPageButton(ref builder, 150, 70, GetButtonID(1, 3), 1060671, HouseGumpPageAOS.Customize); - AddPageButton(ref builder, 150, 90, GetButtonID(1, 4), 1060672, HouseGumpPageAOS.Ownership); + AddPageButton(150, 10, GetButtonID(1, 0), 1060668, HouseGumpPageAOS.Information); + AddPageButton(150, 30, GetButtonID(1, 1), 1060669, HouseGumpPageAOS.Security); + AddPageButton(150, 50, GetButtonID(1, 2), 1060670, HouseGumpPageAOS.Storage); + AddPageButton(150, 70, GetButtonID(1, 3), 1060671, HouseGumpPageAOS.Customize); + AddPageButton(150, 90, GetButtonID(1, 4), 1060672, HouseGumpPageAOS.Ownership); - switch (_page) + switch (page) { case HouseGumpPageAOS.Information: { - builder.AddHtmlLocalized(20, 130, 200, 20, 1011242, LabelColor); // Owned By: - builder.AddLabel(210, 130, LabelHue, GetOwnerName()); + AddHtmlLocalized(20, 130, 200, 20, 1011242, LabelColor); // Owned By: + AddLabel(210, 130, LabelHue, GetOwnerName()); - builder.AddHtmlLocalized(20, 170, 380, 20, 1018032, SelectedColor); // This house is properly placed. - builder.AddHtmlLocalized(20, 190, 380, 20, 1018035, SelectedColor); // This house is of modern design. - builder.AddHtmlLocalized( + AddHtmlLocalized(20, 170, 380, 20, 1018032, SelectedColor); // This house is properly placed. + AddHtmlLocalized(20, 190, 380, 20, 1018035, SelectedColor); // This house is of modern design. + AddHtmlLocalized( 20, 210, 380, 20, - _house is HouseFoundation ? 1060681 : 1060680, + house is HouseFoundation ? 1060681 : 1060680, SelectedColor ); // This is a (pre | custom)-built house. - builder.AddHtmlLocalized( + AddHtmlLocalized( 20, 230, 380, 20, - _house.Public ? 1060678 : 1060679, + house.Public ? 1060678 : 1060679, SelectedColor ); // This house is (private | open to the public). - switch (_house.DecayType) + switch (house.DecayType) { case DecayType.Ageless: case DecayType.AutoRefresh: { - // This house is Automatically refreshed. - builder.AddHtmlLocalized(20, 250, 380, 20, 1062209, SelectedColor); + AddHtmlLocalized( + 20, + 250, + 380, + 20, + 1062209, + SelectedColor + ); // This house is Automatically refreshed. break; } case DecayType.ManualRefresh: { - // This house is Grandfathered. - builder.AddHtmlLocalized(20, 250, 380, 20, 1062208, SelectedColor); + AddHtmlLocalized( + 20, + 250, + 380, + 20, + 1062208, + SelectedColor + ); // This house is Grandfathered. break; } case DecayType.Condemned: { - // This house is Condemned. - builder.AddHtmlLocalized(20, 250, 380, 20, 1062207, WarningColor); + AddHtmlLocalized( + 20, + 250, + 380, + 20, + 1062207, + WarningColor + ); // This house is Condemned. break; } } - builder.AddHtmlLocalized(20, 290, 200, 20, 1060692, SelectedColor); // Built On: - builder.AddLabel(250, 290, LabelHue, $"{_house.BuiltOn:yyyy'-'MM'-'dd HH':'mm':'ss}"); + AddHtmlLocalized(20, 290, 200, 20, 1060692, SelectedColor); // Built On: + AddLabel(250, 290, LabelHue, GetDateTime(house.BuiltOn)); - builder.AddHtmlLocalized(20, 310, 200, 20, 1060693, SelectedColor); // Last Traded: - builder.AddLabel(250, 310, LabelHue, $"{_house.LastTraded:yyyy'-'MM'-'dd HH':'mm':'ss}"); + AddHtmlLocalized(20, 310, 200, 20, 1060693, SelectedColor); // Last Traded: + AddLabel(250, 310, LabelHue, GetDateTime(house.LastTraded)); - builder.AddHtmlLocalized(20, 330, 200, 20, 1061793, SelectedColor); // House Value - builder.AddLabel(250, 330, LabelHue, $"{_house.Price}"); + AddHtmlLocalized(20, 330, 200, 20, 1061793, SelectedColor); // House Value + AddLabel(250, 330, LabelHue, house.Price.ToString()); - // Number of visits this building has had: - builder.AddHtmlLocalized(20, 360, 300, 20, 1011241, SelectedColor); - builder.AddLabel(350, 360, LabelHue, $"{_house.Visits}"); + AddHtmlLocalized( + 20, + 360, + 300, + 20, + 1011241, + SelectedColor + ); // Number of visits this building has had: + AddLabel(350, 360, LabelHue, house.Visits.ToString()); break; } case HouseGumpPageAOS.Security: { - AddButtonLabeled(ref builder, 10, 130, GetButtonID(3, 0), 1011266, isCoOwner); // View Co-Owner List - AddButtonLabeled(ref builder, 10, 150, GetButtonID(3, 1), 1011267, isOwner); // Add a Co-Owner - AddButtonLabeled(ref builder, 10, 170, GetButtonID(3, 2), 1018036, isOwner); // Remove a Co-Owner - AddButtonLabeled(ref builder, 10, 190, GetButtonID(3, 3), 1011268, isOwner); // Clear Co-Owner List + AddButtonLabeled(10, 130, GetButtonID(3, 0), 1011266, isCoOwner); // View Co-Owner List + AddButtonLabeled(10, 150, GetButtonID(3, 1), 1011267, isOwner); // Add a Co-Owner + AddButtonLabeled(10, 170, GetButtonID(3, 2), 1018036, isOwner); // Remove a Co-Owner + AddButtonLabeled(10, 190, GetButtonID(3, 3), 1011268, isOwner); // Clear Co-Owner List - AddButtonLabeled(ref builder, 10, 220, GetButtonID(3, 4), 1011243); // View Friends List - AddButtonLabeled(ref builder, 10, 240, GetButtonID(3, 5), 1011244, isCoOwner); // Add a Friend - AddButtonLabeled(ref builder, 10, 260, GetButtonID(3, 6), 1018037, isCoOwner); // Remove a Friend - AddButtonLabeled(ref builder, 10, 280, GetButtonID(3, 7), 1011245, isCoOwner); // Clear Friend List + AddButtonLabeled(10, 220, GetButtonID(3, 4), 1011243); // View Friends List + AddButtonLabeled(10, 240, GetButtonID(3, 5), 1011244, isCoOwner); // Add a Friend + AddButtonLabeled(10, 260, GetButtonID(3, 6), 1018037, isCoOwner); // Remove a Friend + AddButtonLabeled(10, 280, GetButtonID(3, 7), 1011245, isCoOwner); // Clear Friend List - if (_house.Public) + if (house.Public) { - AddButtonLabeled(ref builder, 10, 310, GetButtonID(3, 8), 1011260); // View Ban List - AddButtonLabeled(ref builder, 10, 330, GetButtonID(3, 9), 1060698); // Clear Ban List + AddButtonLabeled(10, 310, GetButtonID(3, 8), 1011260); // View Ban List + AddButtonLabeled(10, 330, GetButtonID(3, 9), 1060698); // Clear Ban List - AddButtonLabeled(ref builder, 210, 130, GetButtonID(3, 12), 1060695, isOwner); // Change to Private + AddButtonLabeled(210, 130, GetButtonID(3, 12), 1060695, isOwner); // Change to Private - builder.AddHtmlLocalized(245, 150, 240, 20, 1060694, SelectedColor); // Change to Public + AddHtmlLocalized(245, 150, 240, 20, 1060694, SelectedColor); // Change to Public } else { - AddButtonLabeled(ref builder, 10, 310, GetButtonID(3, 10), 1060699); // View Access List - AddButtonLabeled(ref builder, 10, 330, GetButtonID(3, 11), 1060700); // Clear Access List + AddButtonLabeled(10, 310, GetButtonID(3, 10), 1060699); // View Access List + AddButtonLabeled(10, 330, GetButtonID(3, 11), 1060700); // Clear Access List - builder.AddHtmlLocalized(245, 130, 240, 20, 1060695, SelectedColor); // Change to Private + AddHtmlLocalized(245, 130, 240, 20, 1060695, SelectedColor); // Change to Private - AddButtonLabeled(ref builder, 210, 150, GetButtonID(3, 13), 1060694, isOwner); // Change to Public + AddButtonLabeled(210, 150, GetButtonID(3, 13), 1060694, isOwner); // Change to Public } break; } case HouseGumpPageAOS.Storage: { - builder.AddHtmlLocalized(10, 130, 400, 20, 1060682, LabelColor); //
HOUSE STORAGE SUMMARY
+ AddHtmlLocalized(10, 130, 400, 20, 1060682, LabelColor); //
HOUSE STORAGE SUMMARY
// This is not as OSI; storage changes not yet implemented - var maxSecures = _house.GetAosMaxSecures(); - var curSecures = _house.GetAosCurSecures( + /*AddHtmlLocalized( 10, 170, 275, 20, 1011237, LabelColor, false, false ); // Number of locked down items: + AddLabel( 310, 170, LabelHue, m_House.LockDownCount.ToString() ); + + AddHtmlLocalized( 10, 190, 275, 20, 1011238, LabelColor, false, false ); // Maximum locked down items: + AddLabel( 310, 190, LabelHue, m_House.MaxLockDowns.ToString() ); + + AddHtmlLocalized( 10, 210, 275, 20, 1011239, LabelColor, false, false ); // Number of secure containers: + AddLabel( 310, 210, LabelHue, m_House.SecureCount.ToString() ); + + AddHtmlLocalized( 10, 230, 275, 20, 1011240, LabelColor, false, false ); // Maximum number of secure containers: + AddLabel( 310, 230, LabelHue, m_House.MaxSecures.ToString() );*/ + + var maxSecures = house.GetAosMaxSecures(); + var curSecures = house.GetAosCurSecures( out var fromSecures, out var fromVendors, out var fromLockdowns, out var fromMovingCrate ); - var maxLockdowns = _house.GetAosMaxLockdowns(); - var curLockdowns = _house.GetAosCurLockdowns(); + var maxLockdowns = house.GetAosMaxLockdowns(); + var curLockdowns = house.GetAosCurLockdowns(); - var bonusStorage = (int)(_house.BonusStorageScalar * 100 - 100); + var bonusStorage = (int)(house.BonusStorageScalar * 100 - 100); if (bonusStorage > 0) { - builder.AddHtmlLocalized(10, 150, 300, 20, 1072519, LabelColor); // Increased Storage - builder.AddLabel(310, 150, LabelHue, $"{bonusStorage}%"); + AddHtmlLocalized(10, 150, 300, 20, 1072519, LabelColor); // Increased Storage + AddLabel(310, 150, LabelHue, $"{bonusStorage}%"); } - builder.AddHtmlLocalized(10, 170, 300, 20, 1060683, LabelColor); // Maximum Secure Storage - builder.AddLabel(310, 170, LabelHue, $"{maxSecures}"); + AddHtmlLocalized(10, 170, 300, 20, 1060683, LabelColor); // Maximum Secure Storage + AddLabel(310, 170, LabelHue, maxSecures.ToString()); - builder.AddHtmlLocalized(10, 190, 300, 20, 1060685, LabelColor); // Used by Moving Crate - builder.AddLabel(310, 190, LabelHue, $"{fromMovingCrate}"); + AddHtmlLocalized(10, 190, 300, 20, 1060685, LabelColor); // Used by Moving Crate + AddLabel(310, 190, LabelHue, fromMovingCrate.ToString()); - builder.AddHtmlLocalized(10, 210, 300, 20, 1060686, LabelColor); // Used by Lockdowns - builder.AddLabel(310, 210, LabelHue, $"{fromLockdowns}"); + AddHtmlLocalized(10, 210, 300, 20, 1060686, LabelColor); // Used by Lockdowns + AddLabel(310, 210, LabelHue, fromLockdowns.ToString()); if (BaseHouse.NewVendorSystem) { - builder.AddHtmlLocalized(10, 230, 300, 20, 1060688, LabelColor); // Used by Secure Containers - builder.AddLabel(310, 230, LabelHue, $"{fromSecures}"); + AddHtmlLocalized(10, 230, 300, 20, 1060688, LabelColor); // Used by Secure Containers + AddLabel(310, 230, LabelHue, fromSecures.ToString()); - builder.AddHtmlLocalized(10, 250, 300, 20, 1060689, LabelColor); // Available Storage - builder.AddLabel(310, 250, LabelHue, $"{Math.Max(maxSecures - curSecures, 0)}"); + AddHtmlLocalized(10, 250, 300, 20, 1060689, LabelColor); // Available Storage + AddLabel(310, 250, LabelHue, Math.Max(maxSecures - curSecures, 0).ToString()); - builder.AddHtmlLocalized(10, 290, 300, 20, 1060690, LabelColor); // Maximum Lockdowns - builder.AddLabel(310, 290, LabelHue, $"{maxLockdowns}"); + AddHtmlLocalized(10, 290, 300, 20, 1060690, LabelColor); // Maximum Lockdowns + AddLabel(310, 290, LabelHue, maxLockdowns.ToString()); - builder.AddHtmlLocalized(10, 310, 300, 20, 1060691, LabelColor); // Available Lockdowns - builder.AddLabel(310, 310, LabelHue, $"{Math.Max(maxLockdowns - curLockdowns, 0)}"); + AddHtmlLocalized(10, 310, 300, 20, 1060691, LabelColor); // Available Lockdowns + AddLabel(310, 310, LabelHue, Math.Max(maxLockdowns - curLockdowns, 0).ToString()); - var maxVendors = _house.GetNewVendorSystemMaxVendors(); - var vendors = _house.PlayerVendors.Count + _house.VendorRentalContracts.Count; + var maxVendors = house.GetNewVendorSystemMaxVendors(); + var vendors = house.PlayerVendors.Count + house.VendorRentalContracts.Count; - builder.AddHtmlLocalized(10, 350, 300, 20, 1062391, LabelColor); // Vendor Count - builder.AddLabel(310, 350, LabelHue, $"{vendors} / {maxVendors}"); + AddHtmlLocalized(10, 350, 300, 20, 1062391, LabelColor); // Vendor Count + AddLabel(310, 350, LabelHue, $"{vendors} / {maxVendors}"); } else { - builder.AddHtmlLocalized(10, 230, 300, 20, 1060687, LabelColor); // Used by Vendors - builder.AddLabel(310, 230, LabelHue, $"{fromVendors}"); + AddHtmlLocalized(10, 230, 300, 20, 1060687, LabelColor); // Used by Vendors + AddLabel(310, 230, LabelHue, fromVendors.ToString()); - builder.AddHtmlLocalized(10, 250, 300, 20, 1060688, LabelColor); // Used by Secure Containers - builder.AddLabel(310, 250, LabelHue, $"{fromSecures}"); + AddHtmlLocalized(10, 250, 300, 20, 1060688, LabelColor); // Used by Secure Containers + AddLabel(310, 250, LabelHue, fromSecures.ToString()); - builder.AddHtmlLocalized(10, 270, 300, 20, 1060689, LabelColor); // Available Storage - builder.AddLabel(310, 270, LabelHue, $"{Math.Max(maxSecures - curSecures, 0)}"); + AddHtmlLocalized(10, 270, 300, 20, 1060689, LabelColor); // Available Storage + AddLabel(310, 270, LabelHue, Math.Max(maxSecures - curSecures, 0).ToString()); - builder.AddHtmlLocalized(10, 330, 300, 20, 1060690, LabelColor); // Maximum Lockdowns - builder.AddLabel(310, 330, LabelHue, $"{maxLockdowns}"); + AddHtmlLocalized(10, 330, 300, 20, 1060690, LabelColor); // Maximum Lockdowns + AddLabel(310, 330, LabelHue, maxLockdowns.ToString()); - builder.AddHtmlLocalized(10, 350, 300, 20, 1060691, LabelColor); // Available Lockdowns - builder.AddLabel(310, 350, LabelHue, $"{Math.Max(maxLockdowns - curLockdowns, 0)}"); + AddHtmlLocalized(10, 350, 300, 20, 1060691, LabelColor); // Available Lockdowns + AddLabel(310, 350, LabelHue, Math.Max(maxLockdowns - curLockdowns, 0).ToString()); } break; } case HouseGumpPageAOS.Customize: { - var isCustomizable = isOwner && _house is HouseFoundation; + var isCustomizable = isOwner && house is HouseFoundation; AddButtonLabeled( - ref builder, 10, 120, GetButtonID(5, 0), 1060759, - isOwner && !isCustomizable && _house.ConvertEntry != null + isOwner && !isCustomizable && house.ConvertEntry != null ); // Convert Into Customizable House AddButtonLabeled( - ref builder, 10, 160, GetButtonID(5, 1), @@ -416,16 +379,14 @@ namespace Server.Gumps isOwner && isCustomizable ); // Customize This House AddButtonLabeled( - ref builder, 10, 180, GetButtonID(5, 2), 1060760, - isOwner && _house.MovingCrate != null + isOwner && house.MovingCrate != null ); // Relocate Moving Crate - AddButtonLabeled(ref builder, 10, 210, GetButtonID(5, 3), 1060761, isOwner && _house.Public); // Change House Sign + AddButtonLabeled(10, 210, GetButtonID(5, 3), 1060761, isOwner && house.Public); // Change House Sign AddButtonLabeled( - ref builder, 10, 230, GetButtonID(5, 4), @@ -433,86 +394,97 @@ namespace Server.Gumps isOwner && isCustomizable ); // Change House Sign Hanger AddButtonLabeled( - ref builder, 10, 250, GetButtonID(5, 5), 1060763, - isOwner && isCustomizable && ((HouseFoundation)_house).Signpost != null + isOwner && isCustomizable && ((HouseFoundation)house).Signpost != null ); // Change Signpost AddButtonLabeled( - ref builder, 10, 280, GetButtonID(5, 6), 1062004, isOwner && isCustomizable - ); // Change Foundation Style - AddButtonLabeled(ref builder, 10, 310, GetButtonID(5, 7), 1060764, isCoOwner); // Rename House + ); // Change Foundation Style + AddButtonLabeled(10, 310, GetButtonID(5, 7), 1060764, isCoOwner); // Rename House break; } case HouseGumpPageAOS.Ownership: { AddButtonLabeled( - ref builder, 10, 130, GetButtonID(6, 0), 1061794, - isOwner && _house.MovingCrate == null && _house.InternalizedVendors.Count == 0 - ); // Demolish House - AddButtonLabeled(ref builder, 10, 150, GetButtonID(6, 1), 1061797, isOwner); // Trade House - AddButtonLabeled(ref builder, 10, 190, GetButtonID(6, 2), 1061798, false); // Make Primary + isOwner && house.MovingCrate == null && house.InternalizedVendors.Count == 0 + ); // Demolish House + AddButtonLabeled(10, 150, GetButtonID(6, 1), 1061797, isOwner); // Trade House + AddButtonLabeled(10, 190, GetButtonID(6, 2), 1061798, false); // Make Primary break; } case HouseGumpPageAOS.ChangeHanger: { - for (var i = 0; i < _hangerNumbers.Length; ++i) + for (var i = 0; i < m_HangerNumbers.Length; ++i) { var x = 50 + i % 3 * 100; var y = 180 + i / 3 * 80; - builder.AddButton(x, y, 4005, 4007, GetButtonID(7, i)); - builder.AddItem(x + 20, y, _hangerNumbers[i]); + AddButton(x, y, 4005, 4007, GetButtonID(7, i)); + AddItem(x + 20, y, m_HangerNumbers[i]); } break; } case HouseGumpPageAOS.ChangeFoundation: { - for (var i = 0; i < _foundationNumbers.Length; ++i) + for (var i = 0; i < m_FoundationNumbers.Length; ++i) { var x = 15 + i % 5 * 80; var y = 180 + i / 5 * 100; - builder.AddButton(x, y, 4005, 4007, GetButtonID(8, i)); - builder.AddItem(x + 25, y, _foundationNumbers[i]); + AddButton(x, y, 4005, 4007, GetButtonID(8, i)); + AddItem(x + 25, y, m_FoundationNumbers[i]); } break; } case HouseGumpPageAOS.ChangeSign: { + var index = 0; + + if (_HouseSigns.Count == 0) + { + // Add standard signs + for (var i = 0; i < 54; ++i) + { + _HouseSigns.Add(2980 + i * 2); + } + + // Add library and beekeeper signs ( ML ) + _HouseSigns.Add(2966); + _HouseSigns.Add(3140); + } + var signsPerPage = Core.ML ? 24 : 18; var totalSigns = Core.ML ? 56 : 54; var pages = (int)Math.Ceiling((double)totalSigns / signsPerPage); - var index = 0; for (var i = 0; i < pages; ++i) { - builder.AddPage(i + 1); + AddPage(i + 1); - builder.AddButton(10, 360, 4005, 4007, 0, GumpButtonType.Page, (i + 1) % pages + 1); + AddButton(10, 360, 4005, 4007, 0, GumpButtonType.Page, (i + 1) % pages + 1); for (var j = 0; j < signsPerPage && totalSigns - signsPerPage * i - j > 0; ++j) { var x = 30 + j % 6 * 60; var y = 130 + j / 6 * 60; - builder.AddButton(x, y, 4005, 4007, GetButtonID(9, index)); - builder.AddItem(x + 20, y, _houseSigns[index++]); + AddButton(x, y, 4005, 4007, GetButtonID(9, index)); + AddItem(x + 20, y, _HouseSigns[index++]); } } @@ -520,50 +492,50 @@ namespace Server.Gumps } case HouseGumpPageAOS.RemoveCoOwner: { - builder.AddHtmlLocalized(10, 120, 400, 20, 1060730, LabelColor); //
CO-OWNER LIST
- AddList(ref builder, _house.CoOwners, 10, false, true); + AddHtmlLocalized(10, 120, 400, 20, 1060730, LabelColor); //
CO-OWNER LIST
+ AddList(house.CoOwners, 10, false, true, from); break; } case HouseGumpPageAOS.ListCoOwner: { - builder.AddHtmlLocalized(10, 120, 400, 20, 1060730, LabelColor); //
CO-OWNER LIST
- AddList(ref builder, _house.CoOwners, -1, false, true); + AddHtmlLocalized(10, 120, 400, 20, 1060730, LabelColor); //
CO-OWNER LIST
+ AddList(house.CoOwners, -1, false, true, from); break; } case HouseGumpPageAOS.RemoveFriend: { - builder.AddHtmlLocalized(10, 120, 400, 20, 1060731, LabelColor); //
FRIENDS LIST
- AddList(ref builder, _house.Friends, 11, false, true); + AddHtmlLocalized(10, 120, 400, 20, 1060731, LabelColor); //
FRIENDS LIST
+ AddList(house.Friends, 11, false, true, from); break; } case HouseGumpPageAOS.ListFriend: { - builder.AddHtmlLocalized(10, 120, 400, 20, 1060731, LabelColor); //
FRIENDS LIST
- AddList(ref builder, _house.Friends, -1, false, true); + AddHtmlLocalized(10, 120, 400, 20, 1060731, LabelColor); //
FRIENDS LIST
+ AddList(house.Friends, -1, false, true, from); break; } case HouseGumpPageAOS.RemoveBan: { - builder.AddHtmlLocalized(10, 120, 400, 20, 1060733, LabelColor); //
BAN LIST
- AddList(ref builder, _house.Bans, 12, true, true); + AddHtmlLocalized(10, 120, 400, 20, 1060733, LabelColor); //
BAN LIST
+ AddList(house.Bans, 12, true, true, from); break; } case HouseGumpPageAOS.ListBan: { - builder.AddHtmlLocalized(10, 120, 400, 20, 1060733, LabelColor); //
BAN LIST
- AddList(ref builder, _house.Bans, -1, true, true); + AddHtmlLocalized(10, 120, 400, 20, 1060733, LabelColor); //
BAN LIST
+ AddList(house.Bans, -1, true, true, from); break; } case HouseGumpPageAOS.RemoveAccess: { - builder.AddHtmlLocalized(10, 120, 400, 20, 1060732, LabelColor); //
ACCESS LIST
- AddList(ref builder, _house.Access, 13, false, true); + AddHtmlLocalized(10, 120, 400, 20, 1060732, LabelColor); //
ACCESS LIST
+ AddList(house.Access, 13, false, true, from); break; } case HouseGumpPageAOS.ListAccess: { - builder.AddHtmlLocalized(10, 120, 400, 20, 1060732, LabelColor); //
ACCESS LIST
- AddList(ref builder, _house.Access, -1, false, true); + AddHtmlLocalized(10, 120, 400, 20, 1060732, LabelColor); //
ACCESS LIST
+ AddList(house.Access, -1, false, true, from); break; } case HouseGumpPageAOS.ChangePost: @@ -572,17 +544,17 @@ namespace Server.Gumps for (var i = 0; i < 2; ++i) { - builder.AddPage(i + 1); + AddPage(i + 1); - builder.AddButton(10, 360, 4005, 4007, 0, GumpButtonType.Page, (i + 1) % 2 + 1); + AddButton(10, 360, 4005, 4007, 0, GumpButtonType.Page, (i + 1) % 2 + 1); - for (var j = 0; j < 16 && index < _postNumbers.Length; ++j) + for (var j = 0; j < 16 && index < m_PostNumbers.Length; ++j) { var x = 15 + j % 8 * 50; var y = 130 + j / 8 * 110; - builder.AddButton(x, y, 4005, 4007, GetButtonID(14, index)); - builder.AddItem(x + 10, y, _postNumbers[index++]); + AddButton(x, y, 4005, 4007, GetButtonID(14, index)); + AddItem(x + 10, y, m_PostNumbers[index++]); } } @@ -593,37 +565,40 @@ namespace Server.Gumps private string GetOwnerName() { - var m = _house.Owner; + var m = m_House.Owner; return m?.Deleted != false ? "(unowned)" : m.Name.Trim().DefaultIfNullOrEmpty("(no name)"); } - private void AddPageButton(ref DynamicGumpBuilder builder, int x, int y, int buttonID, int number, HouseGumpPageAOS page) - { - var isSelection = _page == page; + private string GetDateTime(DateTime val) => + val == DateTime.MinValue ? "" : val.ToString("yyyy'-'MM'-'dd HH':'mm':'ss"); - builder.AddButton(x, y, isSelection ? 4006 : 4005, 4007, buttonID); - builder.AddHtmlLocalized(x + 45, y, 200, 20, number, isSelection ? SelectedColor : LabelColor); + public void AddPageButton(int x, int y, int buttonID, int number, HouseGumpPageAOS page) + { + var isSelection = m_Page == page; + + AddButton(x, y, isSelection ? 4006 : 4005, 4007, buttonID); + AddHtmlLocalized(x + 45, y, 200, 20, number, isSelection ? SelectedColor : LabelColor); } - private static void AddButtonLabeled(ref DynamicGumpBuilder builder, int x, int y, int buttonID, int number, bool enabled = true) + public void AddButtonLabeled(int x, int y, int buttonID, int number, bool enabled = true) { if (enabled) { - builder.AddButton(x, y, 4005, 4007, buttonID); + AddButton(x, y, 4005, 4007, buttonID); } - builder.AddHtmlLocalized(x + 35, y, 240, 20, number, enabled ? LabelColor : DisabledColor); + AddHtmlLocalized(x + 35, y, 240, 20, number, enabled ? LabelColor : DisabledColor); } - private void AddList(ref DynamicGumpBuilder builder, List list, int button, bool accountOf, bool leadingStar) + public void AddList(List list, int button, bool accountOf, bool leadingStar, Mobile from) { if (list == null) { return; } - _list = new List(list); + m_List = new List(list); var lastPage = 0; var index = 0; @@ -638,14 +613,14 @@ namespace Server.Gumps { if (lastPage != 0) { - builder.AddButton(40, 360, 4005, 4007, 0, GumpButtonType.Page, page); + AddButton(40, 360, 4005, 4007, 0, GumpButtonType.Page, page); } - builder.AddPage(page); + AddPage(page); if (lastPage != 0) { - builder.AddButton(10, 360, 4014, 4016, 0, GumpButtonType.Page, lastPage); + AddButton(10, 360, 4014, 4016, 0, GumpButtonType.Page, lastPage); } lastPage = page; @@ -660,7 +635,7 @@ namespace Server.Gumps { name = vendor.ShopName; - if (vendor.IsOwner(_from)) + if (vendor.IsOwner(from)) { labelHue = HighlightedLabelHue; } @@ -681,7 +656,7 @@ namespace Server.Gumps if (button != -1) { - builder.AddButton(10 + xoffset, 150 + yoffset, 4005, 4007, GetButtonID(button, i)); + AddButton(10 + xoffset, 150 + yoffset, 4005, 4007, GetButtonID(button, i)); } if (accountOf && m.Player && m.Account != null) @@ -694,7 +669,7 @@ namespace Server.Gumps name = $"* {name}"; } - builder.AddLabel(button > 0 ? 45 + xoffset : 10 + xoffset, 150 + yoffset, labelHue, name); + AddLabel(button > 0 ? 45 + xoffset : 10 + xoffset, 150 + yoffset, labelHue, name); ++index; } } @@ -705,7 +680,7 @@ namespace Server.Gumps { if (!house.Deleted) { - DisplayTo(from, house, HouseGumpPageAOS.Security); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Security, from, house)); } } @@ -713,7 +688,7 @@ namespace Server.Gumps { if (!house.Deleted) { - DisplayTo(from, house, HouseGumpPageAOS.Customize); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Customize, from, house)); } } @@ -731,7 +706,7 @@ namespace Server.Gumps from.SendLocalizedMessage(501333); // All co-owners have been removed from this house. } - DisplayTo(from, house, HouseGumpPageAOS.Security); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Security, from, house)); } public static void ClearFriends_Callback(Mobile from, bool okay, BaseHouse house) @@ -748,7 +723,7 @@ namespace Server.Gumps from.SendLocalizedMessage(501332); // All friends have been removed from this house. } - DisplayTo(from, house, HouseGumpPageAOS.Security); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Security, from, house)); } public static void ClearBans_Callback(Mobile from, bool okay, BaseHouse house) @@ -765,7 +740,7 @@ namespace Server.Gumps from.SendLocalizedMessage(1060754); // All bans for this house have been lifted. } - DisplayTo(from, house, HouseGumpPageAOS.Security); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Security, from, house)); } public static void ClearAccess_Callback(Mobile from, bool okay, BaseHouse house) @@ -795,7 +770,7 @@ namespace Server.Gumps from.SendLocalizedMessage(1061843); // This house's Access List has been cleared. } - DisplayTo(from, house, HouseGumpPageAOS.Security); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Security, from, house)); } public static void ConvertHouse_Callback(Mobile from, bool okay, BaseHouse house) @@ -895,7 +870,7 @@ namespace Server.Gumps } var items = house.GetItems(); - using var mobiles = house.GetMobilesPooled(); + var mobiles = house.GetMobiles(); newHouse.MoveToWorld( new Point3D( @@ -931,23 +906,23 @@ namespace Server.Gumps } } - DisplayTo(from, house, HouseGumpPageAOS.Security); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Security, from, house)); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_house.Deleted) + if (m_House.Deleted) { return; } var from = sender.Mobile; - var isCombatRestricted = _house.IsCombatRestricted(from); + var isCombatRestricted = m_House.IsCombatRestricted(from); - var isOwner = _house.IsOwner(from); - var isCoOwner = isOwner || _house.IsCoOwner(from); - var isFriend = isCoOwner || _house.IsFriend(from); + var isOwner = m_House.IsOwner(from); + var isCoOwner = isOwner || m_House.IsCoOwner(from); + var isFriend = isCoOwner || m_House.IsFriend(from); if (isCombatRestricted) { @@ -959,14 +934,14 @@ namespace Server.Gumps return; } - Item sign = _house.Sign; + Item sign = m_House.Sign; if (sign == null || from.Map != sign.Map || !from.InRange(sign.GetWorldLocation(), 18)) { return; } - var foundation = _house as HouseFoundation; + var foundation = m_House as HouseFoundation; var isCustomizable = foundation != null; var val = info.ButtonID - 1; @@ -979,11 +954,11 @@ namespace Server.Gumps var type = val % 15; var index = val / 15; - if (_page == HouseGumpPageAOS.Vendors) + if (m_Page == HouseGumpPageAOS.Vendors) { - if (index < _list.Count) + if (index < m_List.Count) { - var vendor = (PlayerVendor)_list[index]; + var vendor = (PlayerVendor)m_List[index]; if (!vendor.CanInteractWith(from, false)) { @@ -1022,40 +997,40 @@ namespace Server.Gumps { case 0: // Banish { - if (_house.Public) + if (m_House.Public) { from.SendLocalizedMessage(501325); // Target the individual to ban from this house. - from.Target = new HouseBanTarget(true, _house); + from.Target = new HouseBanTarget(true, m_House); } break; } case 1: // Lift Ban { - if (_house.Public) + if (m_House.Public) { - DisplayTo(from, _house, HouseGumpPageAOS.RemoveBan); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.RemoveBan, from, m_House)); } break; } case 2: // Grant Access { - if (!_house.Public) + if (!m_House.Public) { from.SendLocalizedMessage( 1060711 ); // Target the person you would like to grant access to. - from.Target = new HouseAccessTarget(_house); + from.Target = new HouseAccessTarget(m_House); } break; } case 3: // Revoke Access { - if (!_house.Public) + if (!m_House.Public) { - DisplayTo(from, _house, HouseGumpPageAOS.RemoveAccess); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.RemoveAccess, from, m_House)); } break; @@ -1071,37 +1046,24 @@ namespace Server.Gumps switch (index) { case 0: - { - page = HouseGumpPageAOS.Information; - break; - } + page = HouseGumpPageAOS.Information; + break; case 1: - { - page = HouseGumpPageAOS.Security; - break; - } + page = HouseGumpPageAOS.Security; + break; case 2: - { - page = HouseGumpPageAOS.Storage; - break; - } + page = HouseGumpPageAOS.Storage; + break; case 3: - { - page = HouseGumpPageAOS.Customize; - break; - } + page = HouseGumpPageAOS.Customize; + break; case 4: - { - page = HouseGumpPageAOS.Ownership; - break; - } - default: - { - return; - } + page = HouseGumpPageAOS.Ownership; + break; + default: return; } - DisplayTo(from, _house, page); + from.SendGump(new HouseGumpAOS(page, from, m_House)); break; } case 3: @@ -1112,7 +1074,7 @@ namespace Server.Gumps { if (isCoOwner) { - DisplayTo(from, _house, HouseGumpPageAOS.ListCoOwner); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.ListCoOwner, from, m_House)); } break; @@ -1124,7 +1086,7 @@ namespace Server.Gumps from.SendLocalizedMessage( 501328 ); // Target the person you wish to name a co-owner of your household. - from.Target = new CoOwnerTarget(true, _house); + from.Target = new CoOwnerTarget(true, m_House); } break; @@ -1133,7 +1095,7 @@ namespace Server.Gumps { if (isOwner) { - DisplayTo(from, _house, HouseGumpPageAOS.RemoveCoOwner); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.RemoveCoOwner, from, m_House)); } break; @@ -1144,7 +1106,7 @@ namespace Server.Gumps { from.SendGump( new RemoveAllCoOwnersWarningGump( - okay => ClearCoOwners_Callback(from, okay, _house) + okay => ClearCoOwners_Callback(from, okay, m_House) ) ); } @@ -1153,7 +1115,7 @@ namespace Server.Gumps } case 4: // View Friends List { - DisplayTo(from, _house, HouseGumpPageAOS.ListFriend); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.ListFriend, from, m_House)); break; } @@ -1164,7 +1126,7 @@ namespace Server.Gumps from.SendLocalizedMessage( 501317 ); // Target the person you wish to name a friend of your household. - from.Target = new HouseFriendTarget(true, _house); + from.Target = new HouseFriendTarget(true, m_House); } break; @@ -1173,7 +1135,7 @@ namespace Server.Gumps { if (isCoOwner) { - DisplayTo(from, _house, HouseGumpPageAOS.RemoveFriend); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.RemoveFriend, from, m_House)); } break; @@ -1184,7 +1146,7 @@ namespace Server.Gumps { from.SendGump( new RemoveAllFriendsWarningGump( - okay => ClearFriends_Callback(from, okay, _house) + okay => ClearFriends_Callback(from, okay, m_House) ) ); } @@ -1193,28 +1155,28 @@ namespace Server.Gumps } case 8: // View Ban List { - DisplayTo(from, _house, HouseGumpPageAOS.ListBan); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.ListBan, from, m_House)); break; } case 9: // Clear Ban List { from.SendGump( - new LifeAllBansWarningGump(okay => ClearBans_Callback(from, okay, _house)) + new LifeAllBansWarningGump(okay => ClearBans_Callback(from, okay, m_House)) ); break; } case 10: // View Access List { - DisplayTo(from, _house, HouseGumpPageAOS.ListAccess); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.ListAccess, from, m_House)); break; } case 11: // Clear Access List { from.SendGump( - new RevokeAccessWarning(okay => ClearAccess_Callback(from, okay, _house)) + new RevokeAccessWarning(okay => ClearAccess_Callback(from, okay, m_House)) ); break; @@ -1223,46 +1185,46 @@ namespace Server.Gumps { if (isOwner) { - if (_house.PlayerVendors.Count > 0) + if (m_House.PlayerVendors.Count > 0) { from.SendGump( new CannotConvertPrivateNoticeGump( - () => PublicPrivateNotice_Callback(from, _house) + () => PublicPrivateNotice_Callback(from, m_House) ) ); break; } - if (_house.VendorRentalContracts.Count > 0) + if (m_House.VendorRentalContracts.Count > 0) { from.SendGump( new CannotPerformActionContractsNoticeGump( - () => PublicPrivateNotice_Callback(from, _house) + () => PublicPrivateNotice_Callback(from, m_House) ) ); break; } - _house.Public = false; + m_House.Public = false; - _house.ChangeLocks(from); + m_House.ChangeLocks(from); from.SendGump( new HouseConvertedPrivateNoticeGump( - () => PublicPrivateNotice_Callback(from, _house) + () => PublicPrivateNotice_Callback(from, m_House) ) ); - var r = _house.Region; - using var list = r.GetMobilesPooled(); + var r = m_House.Region; + var list = r.GetMobiles(); for (var i = 0; i < list.Count; ++i) { var m = list[i]; - if (!_house.HasAccess(m) && _house.IsInside(m)) + if (!m_House.HasAccess(m) && m_House.IsInside(m)) { - m.Location = _house.BanLocation; + m.Location = m_House.BanLocation; } } } @@ -1273,16 +1235,16 @@ namespace Server.Gumps { if (isOwner) { - _house.Public = true; + m_House.Public = true; - _house.RemoveKeys(from); - _house.RemoveLocks(); + m_House.RemoveKeys(from); + m_House.RemoveLocks(); if (BaseHouse.NewVendorSystem) { from.SendGump( new HouseConvertedPublicNoticeGump( - () => PublicPrivateNotice_Callback(from, _house) + () => PublicPrivateNotice_Callback(from, m_House) ) ); } @@ -1290,21 +1252,21 @@ namespace Server.Gumps { from.SendGump( new HouseConvertedPublicOldSystemNoticeGump( - () => PublicPrivateNotice_Callback(from, _house) + () => PublicPrivateNotice_Callback(from, m_House) ) ); } - var r = _house.Region; - using var list = r.GetMobilesPooled(); + var r = m_House.Region; + var list = r.GetMobiles(); for (var i = 0; i < list.Count; ++i) { var m = list[i]; - if (_house.IsBanned(m) && _house.IsInside(m)) + if (m_House.IsBanned(m) && m_House.IsInside(m)) { - m.Location = _house.BanLocation; + m.Location = m_House.BanLocation; } } } @@ -1323,19 +1285,19 @@ namespace Server.Gumps { if (isOwner && !isCustomizable) { - if (_house.HasRentedVendors) + if (m_House.HasRentedVendors) { from.SendGump( new CannotPerformActionVendorsNoticeGump( - () => CustomizeNotice_Callback(from, _house) + () => CustomizeNotice_Callback(from, m_House) ) ); } - else if (_house.ConvertEntry != null) + else if (m_House.ConvertEntry != null) { from.SendGump( new ConvertToCustomHouseWarningGump( - okay => ConvertHouse_Callback(from, okay, _house) + okay => ConvertHouse_Callback(from, okay, m_House) ) ); } @@ -1347,19 +1309,19 @@ namespace Server.Gumps { if (isOwner && isCustomizable) { - if (_house.HasRentedVendors) + if (m_House.HasRentedVendors) { from.SendGump( new CannotPerformActionVendorsNoticeGump( - () => CustomizeNotice_Callback(from, _house) + () => CustomizeNotice_Callback(from, m_House) ) ); } - else if (_house.HasAddonContainers) + else if (m_House.HasAddonContainers) { from.SendGump( new CannotCustomizeAddonsNoticeGump( - () => CustomizeNotice_Callback(from, _house) + () => CustomizeNotice_Callback(from, m_House) ) ); } @@ -1373,11 +1335,11 @@ namespace Server.Gumps } case 2: // Relocate Moving Crate { - var crate = _house.MovingCrate; + var crate = m_House.MovingCrate; if (isOwner && crate != null) { - if (!_house.IsInside(from)) + if (!m_House.IsInside(from)) { from.SendLocalizedMessage(502092); // You must be in your house to do this. } @@ -1392,9 +1354,9 @@ namespace Server.Gumps } case 3: // Change House Sign { - if (isOwner && _house.Public) + if (isOwner && m_House.Public) { - DisplayTo(from, _house, HouseGumpPageAOS.ChangeSign); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.ChangeSign, from, m_House)); } break; @@ -1403,7 +1365,7 @@ namespace Server.Gumps { if (isOwner && isCustomizable) { - DisplayTo(from, _house, HouseGumpPageAOS.ChangeHanger); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.ChangeHanger, from, m_House)); } break; @@ -1412,7 +1374,7 @@ namespace Server.Gumps { if (isOwner && isCustomizable && foundation.Signpost != null) { - DisplayTo(from, _house, HouseGumpPageAOS.ChangePost); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.ChangePost, from, m_House)); } break; @@ -1421,7 +1383,7 @@ namespace Server.Gumps { if (isOwner && isCustomizable) { - DisplayTo(from, _house, HouseGumpPageAOS.ChangeFoundation); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.ChangeFoundation, from, m_House)); } break; @@ -1430,7 +1392,7 @@ namespace Server.Gumps { if (isCoOwner) { - from.Prompt = new RenamePrompt(_house); + from.Prompt = new RenamePrompt(m_House); from.SendLocalizedMessage(501302); // What dost thou wish the sign to say? } @@ -1446,16 +1408,16 @@ namespace Server.Gumps { case 0: // Demolish { - if (isOwner && _house.MovingCrate == null && _house.InternalizedVendors.Count == 0) + if (isOwner && m_House.MovingCrate == null && m_House.InternalizedVendors.Count == 0) { - if (!Guild.NewGuildSystem && _house.FindGuildstone() != null) + if (!Guild.NewGuildSystem && m_House.FindGuildstone() != null) { from.SendLocalizedMessage( 501389 ); // You cannot redeed a house with a guildstone inside. } else if (Core.ML && from.AccessLevel < AccessLevel.GameMaster && - Core.Now <= _house.BuiltOn.AddHours(1)) + Core.Now <= m_House.BuiltOn.AddHours(1)) { from.SendLocalizedMessage( 1080178 @@ -1463,7 +1425,7 @@ namespace Server.Gumps } else { - from.SendGump(new ConfirmDemolishHouseGump(_house)); + from.SendGump(new ConfirmDemolishHouseGump(m_House)); } } @@ -1473,13 +1435,13 @@ namespace Server.Gumps { if (isOwner) { - if (BaseHouse.NewVendorSystem && _house.HasPersonalVendors) + if (BaseHouse.NewVendorSystem && m_House.HasPersonalVendors) { from.SendLocalizedMessage( 1062467 ); // You cannot trade this house while you still have personal vendors inside. } - else if (_house.DecayLevel == DecayLevel.DemolitionPending) + else if (m_House.DecayLevel == DecayLevel.DemolitionPending) { from.SendLocalizedMessage( 1005321 @@ -1490,32 +1452,30 @@ namespace Server.Gumps from.SendLocalizedMessage( 501309 ); // Target the person to whom you wish to give this house. - from.Target = new HouseOwnerTarget(_house); + from.Target = new HouseOwnerTarget(m_House); } } break; } case 2: // Make Primary - { - break; - } + break; } break; } case 7: { - if (isOwner && isCustomizable && index < _hangerNumbers.Length) + if (isOwner && isCustomizable && index < m_HangerNumbers.Length) { var hanger = foundation.SignHanger; if (hanger != null) { - hanger.ItemID = _hangerNumbers[index]; + hanger.ItemID = m_HangerNumbers[index]; } - DisplayTo(from, _house, HouseGumpPageAOS.Customize); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Customize, from, m_House)); } break; @@ -1531,29 +1491,18 @@ namespace Server.Gumps switch (index) { case 5: - { - newType = FoundationType.ElvenGrey; - break; - } + newType = FoundationType.ElvenGrey; + break; case 6: - { - newType = FoundationType.ElvenNatural; - break; - } + newType = FoundationType.ElvenNatural; + break; case 7: - { - newType = FoundationType.Crystal; - break; - } + newType = FoundationType.Crystal; + break; case 8: - { - newType = FoundationType.Shadow; - break; - } - default: - { - return; - } + newType = FoundationType.Shadow; + break; + default: return; } } else @@ -1561,34 +1510,21 @@ namespace Server.Gumps switch (index) { case 0: - { - newType = FoundationType.DarkWood; - break; - } + newType = FoundationType.DarkWood; + break; case 1: - { - newType = FoundationType.LightWood; - break; - } + newType = FoundationType.LightWood; + break; case 2: - { - newType = FoundationType.Dungeon; - break; - } + newType = FoundationType.Dungeon; + break; case 3: - { - newType = FoundationType.Brick; - break; - } + newType = FoundationType.Brick; + break; case 4: - { - newType = FoundationType.Stone; - break; - } - default: - { - return; - } + newType = FoundationType.Stone; + break; + default: return; } } @@ -1608,34 +1544,34 @@ namespace Server.Gumps foundation.Delta(ItemDelta.Update); - DisplayTo(from, _house, HouseGumpPageAOS.Customize); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Customize, from, m_House)); } break; } case 9: { - if (isOwner && _house.Public && index < _houseSigns.Length) + if (isOwner && m_House.Public && index < _HouseSigns.Count) { - _house.ChangeSignType(_houseSigns[index]); - DisplayTo(from, _house, HouseGumpPageAOS.Customize); + m_House.ChangeSignType(_HouseSigns[index]); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Customize, from, m_House)); } break; } case 10: { - if (isOwner && index < _list?.Count) + if (isOwner && index < m_List?.Count) { - _house.RemoveCoOwner(from, _list[index]); + m_House.RemoveCoOwner(from, m_List[index]); - if (_house.CoOwners.Count > 0) + if (m_House.CoOwners.Count > 0) { - DisplayTo(from, _house, HouseGumpPageAOS.RemoveCoOwner); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.RemoveCoOwner, from, m_House)); } else { - DisplayTo(from, _house, HouseGumpPageAOS.Security); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Security, from, m_House)); } } @@ -1643,17 +1579,17 @@ namespace Server.Gumps } case 11: { - if (isCoOwner && index < _list?.Count) + if (isCoOwner && index < m_List?.Count) { - _house.RemoveFriend(from, _list[index]); + m_House.RemoveFriend(from, m_List[index]); - if (_house.Friends.Count > 0) + if (m_House.Friends.Count > 0) { - DisplayTo(from, _house, HouseGumpPageAOS.RemoveFriend); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.RemoveFriend, from, m_House)); } else { - DisplayTo(from, _house, HouseGumpPageAOS.Security); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Security, from, m_House)); } } @@ -1661,17 +1597,17 @@ namespace Server.Gumps } case 12: { - if (index < _list?.Count) + if (index < m_List?.Count) { - _house.RemoveBan(from, _list[index]); + m_House.RemoveBan(from, m_List[index]); - if (_house.Bans.Count > 0) + if (m_House.Bans.Count > 0) { - DisplayTo(from, _house, HouseGumpPageAOS.RemoveBan); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.RemoveBan, from, m_House)); } else { - DisplayTo(from, _house, HouseGumpPageAOS.Security); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Security, from, m_House)); } } @@ -1679,17 +1615,17 @@ namespace Server.Gumps } case 13: { - if (index < _list?.Count) + if (index < m_List?.Count) { - _house.RemoveAccess(from, _list[index]); + m_House.RemoveAccess(from, m_List[index]); - if (_house.Access.Count > 0) + if (m_House.Access.Count > 0) { - DisplayTo(from, _house, HouseGumpPageAOS.RemoveAccess); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.RemoveAccess, from, m_House)); } else { - DisplayTo(from, _house, HouseGumpPageAOS.Security); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Security, from, m_House)); } } @@ -1697,12 +1633,12 @@ namespace Server.Gumps } case 14: { - if (isOwner && isCustomizable && index < _postNumbers.Length) + if (isOwner && isCustomizable && index < m_PostNumbers.Length) { - foundation.SignpostGraphic = _postNumbers[index]; + foundation.SignpostGraphic = m_PostNumbers[index]; foundation.CheckSignpost(); - DisplayTo(from, _house, HouseGumpPageAOS.Customize); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Customize, from, m_House)); } break; diff --git a/Projects/UOContent/Gumps/PetResurrectGump.cs b/Projects/UOContent/Gumps/PetResurrectGump.cs index 75400b58d..c2c28ded4 100644 --- a/Projects/UOContent/Gumps/PetResurrectGump.cs +++ b/Projects/UOContent/Gumps/PetResurrectGump.cs @@ -39,7 +39,7 @@ public class PetResurrectGump : StaticGump protected override void BuildStrings(ref GumpStringsBuilder builder) { - builder.SetHtmlText("petName", _pet.Name, align: TextAlignment.Center); + builder.SetStringSlot("petName", $"
{_pet.Name}
"); } public override void OnResponse(NetState state, in RelayInfo info) diff --git a/Projects/UOContent/Gumps/PlayerVendorGumps.cs b/Projects/UOContent/Gumps/PlayerVendorGumps.cs index 3516ae405..df081ab12 100644 --- a/Projects/UOContent/Gumps/PlayerVendorGumps.cs +++ b/Projects/UOContent/Gumps/PlayerVendorGumps.cs @@ -7,83 +7,63 @@ using Server.Network; namespace Server.Gumps { - public class PlayerVendorBuyGump : StaticGump + public class PlayerVendorBuyGump : Gump { - private readonly PlayerVendor _vendor; - private readonly VendorItem _vi; + private readonly PlayerVendor m_Vendor; + private readonly VendorItem m_VI; public override bool Singleton => true; - private PlayerVendorBuyGump(PlayerVendor vendor, VendorItem vi) : base(100, 200) + public PlayerVendorBuyGump(PlayerVendor vendor, VendorItem vi) : base(100, 200) { - _vendor = vendor; - _vi = vi; - } + m_Vendor = vendor; + m_VI = vi; - public static void DisplayTo(Mobile from, PlayerVendor vendor, VendorItem vi) - { - if (from == null || vendor == null || vi == null) + AddBackground(100, 10, 300, 150, 5054); + + AddHtmlLocalized(125, 20, 250, 24, 1019070); // You have agreed to purchase: + + if (!string.IsNullOrEmpty(vi.Description)) { - return; + AddLabel(125, 45, 0, vi.Description); + } + else + { + AddHtmlLocalized(125, 45, 250, 24, 1019072); // an item without a description } - from.SendGump(new PlayerVendorBuyGump(vendor, vi)); - } + AddHtmlLocalized(125, 70, 250, 24, 1019071); // for the amount of: + AddLabel(125, 95, 0, vi.Price.ToString()); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddButton(250, 130, 4005, 4007, 0); + AddHtmlLocalized(282, 130, 100, 24, 1011012); // CANCEL - builder.AddBackground(100, 10, 300, 150, 5054); - - builder.AddHtmlLocalized(125, 20, 250, 24, 1019070); // You have agreed to purchase: - - builder.AddHtmlPlaceholder(125, 45, 250, 24, "description"); - - builder.AddHtmlLocalized(125, 70, 250, 24, 1019071); // for the amount of: - builder.AddLabelPlaceholder(125, 95, 0, "price"); - - builder.AddButton(250, 130, 4005, 4007, 0); - builder.AddHtmlLocalized(282, 130, 100, 24, 1011012); // CANCEL - - builder.AddButton(120, 130, 4005, 4007, 1); - builder.AddHtmlLocalized(152, 130, 100, 24, 1011036); // OKAY - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - // 1019072: an item without a description - var description = !string.IsNullOrEmpty(_vi.Description) - ? _vi.Description - : Localization.GetText(1019072) ?? "an item without a description"; - - builder.SetHtmlText("description", description); - - builder.SetStringSlot("price", $"{_vi.Price}"); + AddButton(120, 130, 4005, 4007, 1); + AddHtmlLocalized(152, 130, 100, 24, 1011036); // OKAY } public override void OnResponse(NetState state, in RelayInfo info) { var from = state.Mobile; - if (!_vendor.CanInteractWith(from, false)) + if (!m_Vendor.CanInteractWith(from, false)) { return; } - if (_vendor.IsOwner(from)) + if (m_Vendor.IsOwner(from)) { - _vendor.SayTo(from, 503212); // You own this shop, just take what you want. + m_Vendor.SayTo(from, 503212); // You own this shop, just take what you want. return; } if (info.ButtonID == 1) { - _vendor.Say(from.Name); + m_Vendor.Say(from.Name); - if (!_vi.Valid || !_vi.Item.IsChildOf(_vendor.Backpack)) + if (!m_VI.Valid || !m_VI.Item.IsChildOf(m_Vendor.Backpack)) { - _vendor.SayTo(from, 503216); // You can't buy that. + m_Vendor.SayTo(from, 503216); // You can't buy that. return; } @@ -96,17 +76,17 @@ namespace Server.Gumps totalGold += Banker.GetBalance(from); - if (totalGold < _vi.Price) + if (totalGold < m_VI.Price) { - _vendor.SayTo(from, 503205); // You cannot afford this item. + m_Vendor.SayTo(from, 503205); // You cannot afford this item. } - else if (!from.PlaceInBackpack(_vi.Item)) + else if (!from.PlaceInBackpack(m_VI.Item)) { - _vendor.SayTo(from, 503204); // You do not have room in your backpack for this. + m_Vendor.SayTo(from, 503204); // You do not have room in your backpack for this. } else { - var leftPrice = _vi.Price; + var leftPrice = m_VI.Price; if (from.Backpack != null) { @@ -118,7 +98,7 @@ namespace Server.Gumps Banker.Withdraw(from, leftPrice); } - _vendor.HoldGold += _vi.Price; + m_Vendor.HoldGold += m_VI.Price; from.SendLocalizedMessage(503201); // You take the item. } @@ -130,64 +110,49 @@ namespace Server.Gumps } } - public class PlayerVendorOwnerGump : StaticGump + public class PlayerVendorOwnerGump : Gump { - private readonly PlayerVendor _vendor; + private readonly PlayerVendor m_Vendor; - private PlayerVendorOwnerGump(PlayerVendor vendor) : base(50, 200) => _vendor = vendor; - - public static void DisplayTo(Mobile from, PlayerVendor vendor) + public PlayerVendorOwnerGump(PlayerVendor vendor) : base(50, 200) { - if (from != null && vendor != null) - { - from.SendGump(new PlayerVendorOwnerGump(vendor)); - } - } + m_Vendor = vendor; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(25, 10, 530, 140, 5054); + var perDay = m_Vendor.ChargePerDay; - builder.AddHtmlLocalized(425, 25, 120, 20, 1019068); // See goods - builder.AddButton(390, 25, 4005, 4007, 1); - builder.AddHtmlLocalized(425, 48, 120, 20, 1019069); // Customize - builder.AddButton(390, 48, 4005, 4007, 2); - builder.AddHtmlLocalized(425, 72, 120, 20, 1011012); // CANCEL - builder.AddButton(390, 71, 4005, 4007, 0); + AddPage(0); + AddBackground(25, 10, 530, 140, 5054); - builder.AddHtmlLocalized(40, 72, 260, 20, 1038321); // Gold held for you: - builder.AddLabelPlaceholder(300, 72, 0, "holdGold"); - builder.AddHtmlLocalized(40, 96, 260, 20, 1038322); // Gold held in my account: - builder.AddLabelPlaceholder(300, 96, 0, "bankAccount"); + AddHtmlLocalized(425, 25, 120, 20, 1019068); // See goods + AddButton(390, 25, 4005, 4007, 1); + AddHtmlLocalized(425, 48, 120, 20, 1019069); // Customize + AddButton(390, 48, 4005, 4007, 2); + AddHtmlLocalized(425, 72, 120, 20, 1011012); // CANCEL + AddButton(390, 71, 4005, 4007, 0); - // Client 1038324 replaced: - builder.AddHtml(40, 120, 260, 20, "My charge per day is:"); - builder.AddLabelPlaceholder(300, 120, 0, "perDay"); + AddHtmlLocalized(40, 72, 260, 20, 1038321); // Gold held for you: + AddLabel(300, 72, 0, m_Vendor.HoldGold.ToString()); + AddHtmlLocalized(40, 96, 260, 20, 1038322); // Gold held in my account: + AddLabel(300, 96, 0, m_Vendor.BankAccount.ToString()); - builder.AddHtmlLocalized(40, 25, 260, 20, 1038318); // Amount of days I can work: - builder.AddLabelPlaceholder(300, 25, 0, "days"); - builder.AddHtmlLocalized(40, 48, 260, 20, 1038319); // Earth days: - builder.AddLabelPlaceholder(300, 48, 0, "earthDays"); - } + // AddHtmlLocalized( 40, 120, 260, 20, 1038324, false, false ); // My charge per day is: + // Localization has changed, we must use a string here + AddHtml(40, 120, 260, 20, "My charge per day is:"); + AddLabel(300, 120, 0, perDay.ToString()); - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - var perDay = _vendor.ChargePerDay; - var days = (_vendor.HoldGold + _vendor.BankAccount) / (double)perDay; + var days = (m_Vendor.HoldGold + m_Vendor.BankAccount) / (double)perDay; - builder.SetStringSlot("holdGold", $"{_vendor.HoldGold}"); - builder.SetStringSlot("bankAccount", $"{_vendor.BankAccount}"); - builder.SetStringSlot("perDay", $"{perDay}"); - builder.SetStringSlot("days", $"{(int)days}"); - builder.SetStringSlot("earthDays", $"{(int)(days / 12.0)}"); + AddHtmlLocalized(40, 25, 260, 20, 1038318); // Amount of days I can work: + AddLabel(300, 25, 0, ((int)days).ToString()); + AddHtmlLocalized(40, 48, 260, 20, 1038319); // Earth days: + AddLabel(300, 48, 0, ((int)(days / 12.0)).ToString()); } public override void OnResponse(NetState state, in RelayInfo info) { var from = state.Mobile; - if (!_vendor.CanInteractWith(from, true)) + if (!m_Vendor.CanInteractWith(from, true)) { return; } @@ -196,13 +161,13 @@ namespace Server.Gumps { case 1: { - _vendor.OpenBackpack(from); + m_Vendor.OpenBackpack(from); break; } case 2: { - PlayerVendorCustomizeGump.DisplayTo(from, _vendor); + from.SendGump(new PlayerVendorCustomizeGump(m_Vendor, from)); break; } @@ -210,68 +175,56 @@ namespace Server.Gumps } } - public class NewPlayerVendorOwnerGump : DynamicGump + public class NewPlayerVendorOwnerGump : Gump { - private readonly PlayerVendor _vendor; + private readonly PlayerVendor m_Vendor; - public override bool Singleton => true; - - private NewPlayerVendorOwnerGump(PlayerVendor vendor) : base(50, 200) => _vendor = vendor; - - public static void DisplayTo(Mobile from, PlayerVendor vendor) + public NewPlayerVendorOwnerGump(PlayerVendor vendor) : base(50, 200) { - if (from != null && vendor != null) - { - from.SendGump(new NewPlayerVendorOwnerGump(vendor)); - } - } + m_Vendor = vendor; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var perRealWorldDay = _vendor.ChargePerRealWorldDay; - var goldHeld = _vendor.HoldGold; + var perRealWorldDay = vendor.ChargePerRealWorldDay; + var goldHeld = vendor.HoldGold; - builder.AddPage(); + AddBackground(25, 10, 530, 180, 0x13BE); - builder.AddBackground(25, 10, 530, 180, 0x13BE); + AddImageTiled(35, 20, 510, 160, 0xA40); + AddAlphaRegion(35, 20, 510, 160); - builder.AddImageTiled(35, 20, 510, 160, 0xA40); - builder.AddAlphaRegion(35, 20, 510, 160); - - builder.AddImage(10, 0, 0x28DC); - builder.AddImage(537, 175, 0x28DC); - builder.AddImage(10, 175, 0x28DC); - builder.AddImage(537, 0, 0x28DC); + AddImage(10, 0, 0x28DC); + AddImage(537, 175, 0x28DC); + AddImage(10, 175, 0x28DC); + AddImage(537, 0, 0x28DC); if (goldHeld < perRealWorldDay) { var goldNeeded = perRealWorldDay - goldHeld; - builder.AddHtmlLocalized(40, 35, 260, 20, 1038320, 0x7FFF); // Gold needed for 1 day of vendor salary: - builder.AddLabel(300, 35, 0x1F, $"{goldNeeded}"); + AddHtmlLocalized(40, 35, 260, 20, 1038320, 0x7FFF); // Gold needed for 1 day of vendor salary: + AddLabel(300, 35, 0x1F, goldNeeded.ToString()); } else { var days = goldHeld / perRealWorldDay; - builder.AddHtmlLocalized(40, 35, 260, 20, 1038318, 0x7FFF); // # of days Vendor salary is paid for: - builder.AddLabel(300, 35, 0x480, $"{days}"); + AddHtmlLocalized(40, 35, 260, 20, 1038318, 0x7FFF); // # of days Vendor salary is paid for: + AddLabel(300, 35, 0x480, days.ToString()); } - builder.AddHtmlLocalized(40, 58, 260, 20, 1038324, 0x7FFF); // My charge per real world day is: - builder.AddLabel(300, 58, 0x480, $"{perRealWorldDay}"); + AddHtmlLocalized(40, 58, 260, 20, 1038324, 0x7FFF); // My charge per real world day is: + AddLabel(300, 58, 0x480, perRealWorldDay.ToString()); - builder.AddHtmlLocalized(40, 82, 260, 20, 1038322, 0x7FFF); // Gold held in my account: - builder.AddLabel(300, 82, 0x480, $"{goldHeld}"); + AddHtmlLocalized(40, 82, 260, 20, 1038322, 0x7FFF); // Gold held in my account: + AddLabel(300, 82, 0x480, goldHeld.ToString()); - builder.AddHtmlLocalized(40, 108, 260, 20, 1062509, 0x7FFF); // Shop Name: - builder.AddLabel(140, 106, 0x66D, _vendor.ShopName); + AddHtmlLocalized(40, 108, 260, 20, 1062509, 0x7FFF); // Shop Name: + AddLabel(140, 106, 0x66D, vendor.ShopName); - if (_vendor is RentedVendor rentedVendor) + if (vendor is RentedVendor rentedVendor) { rentedVendor.ComputeRentalExpireDelay(out var days, out var hours); - builder.AddLabel( + AddLabel( 38, 132, 0x480, @@ -279,29 +232,29 @@ namespace Server.Gumps ); } - builder.AddButton(390, 24, 0x15E1, 0x15E5, 1); - builder.AddHtmlLocalized(408, 21, 120, 20, 1019068, 0x7FFF); // See goods + AddButton(390, 24, 0x15E1, 0x15E5, 1); + AddHtmlLocalized(408, 21, 120, 20, 1019068, 0x7FFF); // See goods - builder.AddButton(390, 44, 0x15E1, 0x15E5, 2); - builder.AddHtmlLocalized(408, 41, 120, 20, 1019069, 0x7FFF); // Customize + AddButton(390, 44, 0x15E1, 0x15E5, 2); + AddHtmlLocalized(408, 41, 120, 20, 1019069, 0x7FFF); // Customize - builder.AddButton(390, 64, 0x15E1, 0x15E5, 3); - builder.AddHtmlLocalized(408, 61, 120, 20, 1062434, 0x7FFF); // Rename Shop + AddButton(390, 64, 0x15E1, 0x15E5, 3); + AddHtmlLocalized(408, 61, 120, 20, 1062434, 0x7FFF); // Rename Shop - builder.AddButton(390, 84, 0x15E1, 0x15E5, 4); - builder.AddHtmlLocalized(408, 81, 120, 20, 3006217, 0x7FFF); // Rename Vendor + AddButton(390, 84, 0x15E1, 0x15E5, 4); + AddHtmlLocalized(408, 81, 120, 20, 3006217, 0x7FFF); // Rename Vendor - builder.AddButton(390, 104, 0x15E1, 0x15E5, 5); - builder.AddHtmlLocalized(408, 101, 120, 20, 3006123, 0x7FFF); // Open Paperdoll + AddButton(390, 104, 0x15E1, 0x15E5, 5); + AddHtmlLocalized(408, 101, 120, 20, 3006123, 0x7FFF); // Open Paperdoll - builder.AddButton(390, 124, 0x15E1, 0x15E5, 6); - builder.AddLabel(408, 121, 0x480, "Collect Gold"); + AddButton(390, 124, 0x15E1, 0x15E5, 6); + AddLabel(408, 121, 0x480, "Collect Gold"); - builder.AddButton(390, 144, 0x15E1, 0x15E5, 7); - builder.AddLabel(408, 141, 0x480, "Dismiss Vendor"); + AddButton(390, 144, 0x15E1, 0x15E5, 7); + AddLabel(408, 141, 0x480, "Dismiss Vendor"); - builder.AddButton(390, 162, 0x15E1, 0x15E5, 0); - builder.AddHtmlLocalized(408, 161, 120, 20, 1011012, 0x7FFF); // CANCEL + AddButton(390, 162, 0x15E1, 0x15E5, 0); + AddHtmlLocalized(408, 161, 120, 20, 1011012, 0x7FFF); // CANCEL } public override void OnResponse(NetState sender, in RelayInfo info) @@ -310,10 +263,10 @@ namespace Server.Gumps if (info.ButtonID is 1 or 2) // See goods or Customize { - _vendor.CheckTeleport(from); + m_Vendor.CheckTeleport(from); } - if (!_vendor.CanInteractWith(from, true)) + if (!m_Vendor.CanInteractWith(from, true)) { return; } @@ -322,43 +275,43 @@ namespace Server.Gumps { case 1: // See goods { - _vendor.OpenBackpack(from); + m_Vendor.OpenBackpack(from); break; } case 2: // Customize { - NewPlayerVendorCustomizeGump.DisplayTo(from, _vendor); + from.SendGump(new NewPlayerVendorCustomizeGump(m_Vendor)); break; } case 3: // Rename Shop { - _vendor.RenameShop(from); + m_Vendor.RenameShop(from); break; } case 4: // Rename Vendor { - _vendor.Rename(from); + m_Vendor.Rename(from); break; } case 5: // Open Paperdoll { - _vendor.DisplayPaperdollTo(from); + m_Vendor.DisplayPaperdollTo(from); break; } case 6: // Collect Gold { - _vendor.CollectGold(from); + m_Vendor.CollectGold(from); break; } case 7: // Dismiss Vendor { - _vendor.Dismiss(from); + m_Vendor.Dismiss(from); break; } @@ -366,29 +319,31 @@ namespace Server.Gumps } } - public class PlayerVendorCustomizeGump : StaticGump + public class PlayerVendorCustomizeGump : Gump { private static readonly CustomCategory[] Categories = - [ + { new( Layer.InnerTorso, 1011357, true, - [ + new[] + { // Upper Torso new CustomItem(typeof(Shirt), 1011359, 5399), new CustomItem(typeof(FancyShirt), 1011360, 7933), new CustomItem(typeof(PlainDress), 1011363, 7937), new CustomItem(typeof(FancyDress), 1011364, 7935), new CustomItem(typeof(Robe), 1011365, 7939) - ] + } ), new( Layer.MiddleTorso, 1011371, true, - [ + new[] + { // Over chest new CustomItem(typeof(Doublet), 1011358, 8059), new CustomItem(typeof(Tunic), 1011361, 8097), @@ -397,27 +352,29 @@ namespace Server.Gumps new CustomItem(typeof(Surcoat), 1011362, 8189), new CustomItem(typeof(HalfApron), 1011373, 5435), new CustomItem(typeof(FullApron), 1011374, 5437) - ] + } ), new( Layer.Shoes, 1011388, true, - [ + new[] + { // Footwear new CustomItem(typeof(Sandals), 1011389, 5901), new CustomItem(typeof(Shoes), 1011390, 5904), new CustomItem(typeof(Boots), 1011391, 5899), new CustomItem(typeof(ThighBoots), 1011392, 5906) - ] + } ), new( Layer.Helm, 1011375, true, - [ + new[] + { // Hats new CustomItem(typeof(SkullCap), 1011376, 5444), new CustomItem(typeof(Bandana), 1011377, 5440), @@ -425,14 +382,15 @@ namespace Server.Gumps new CustomItem(typeof(WideBrimHat), 1011379, 5908), new CustomItem(typeof(Cap), 1011380, 5909), new CustomItem(typeof(TallStrawHat), 1011382, 5910) - ] + } ), new( Layer.Helm, 1015319, true, - [ + new[] + { // More Hats new CustomItem(typeof(StrawHat), 1011382, 5911), new CustomItem(typeof(WizardsHat), 1011383, 5912), @@ -440,36 +398,39 @@ namespace Server.Gumps new CustomItem(typeof(FeatheredHat), 1011385, 5914), new CustomItem(typeof(TricorneHat), 1011386, 5915), new CustomItem(typeof(JesterHat), 1011387, 5916) - ] + } ), new( Layer.Pants, 1011367, true, - [ + new[] + { // Lower Torso new CustomItem(typeof(LongPants), 1011368, 5433), new CustomItem(typeof(Kilt), 1011369, 5431), new CustomItem(typeof(Skirt), 1011370, 5398) - ] + } ), new( Layer.Cloak, 1011393, true, - [ + new[] + { // Back new CustomItem(typeof(Cloak), 1011394, 5397) - ] + } ), new( Layer.Hair, 1011395, true, - [ + new[] + { // Hair new CustomItem(0x203B, 1011052), new CustomItem(0x203C, 1011053), @@ -480,14 +441,15 @@ namespace Server.Gumps new CustomItem(0x2047, 1011396), new CustomItem(0x2048, 1011048), new CustomItem(0x2049, 1011049) - ] + } ), new( Layer.FacialHair, 1015320, true, - [ + new[] + { // Facial Hair new CustomItem(0x2041, 1011062), new CustomItem(0x203F, 1011060), @@ -496,14 +458,15 @@ namespace Server.Gumps new CustomItem(0x204C, 1015322, true), new CustomItem(0x2040, 1015323), new CustomItem(0x204D, 1011401) - ] + } ), new( Layer.FirstValid, 1011397, false, - [ + new[] + { // Held items new CustomItem(typeof(FishingPole), 1011406, 3520), new CustomItem(typeof(Pickaxe), 1011407, 3717), @@ -514,14 +477,15 @@ namespace Server.Gumps new CustomItem(typeof(Hammer), 1011411, 4020), new CustomItem(typeof(Longsword), 1011412, 3936), new CustomItem(typeof(GnarledStaff), 1011413, 5113) - ] + } ), new( Layer.FirstValid, 1015325, false, - [ + new[] + { // More held items new CustomItem(typeof(Crossbow), 1011414, 3920), new CustomItem(typeof(WarMace), 1011415, 5126), @@ -529,102 +493,95 @@ namespace Server.Gumps new CustomItem(typeof(Spear), 1011417, 3939), new CustomItem(typeof(Katana), 1011418, 5118), new CustomItem(typeof(Spellbook), 1011419, 3834) - ] + } ) - ]; + }; - private readonly Mobile _vendor; + private readonly Mobile m_Vendor; public override bool Singleton => true; - private PlayerVendorCustomizeGump(Mobile v) : base(30, 40) => _vendor = v; - - public static void DisplayTo(Mobile from, Mobile vendor) + public PlayerVendorCustomizeGump(Mobile v, Mobile from) : base(30, 40) { - if (from != null && vendor != null && !vendor.Deleted) - { - from.SendGump(new PlayerVendorCustomizeGump(vendor)); - } - } + m_Vendor = v; + int x, y; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 585, 393, 5054); - builder.AddBackground(195, 36, 387, 275, 3000); - builder.AddHtmlLocalized(10, 10, 565, 18, 1011356); //
VENDOR CUSTOMIZATION MENU
- builder.AddHtmlLocalized(60, 355, 150, 18, 1011036); // OKAY - builder.AddButton(25, 355, 4005, 4007, 1); - builder.AddHtmlLocalized(320, 355, 150, 18, 1011012); // CANCEL - builder.AddButton(285, 355, 4005, 4007, 0); + AddPage(0); + AddBackground(0, 0, 585, 393, 5054); + AddBackground(195, 36, 387, 275, 3000); + AddHtmlLocalized(10, 10, 565, 18, 1011356); //
VENDOR CUSTOMIZATION MENU
+ AddHtmlLocalized(60, 355, 150, 18, 1011036); // OKAY + AddButton(25, 355, 4005, 4007, 1); + AddHtmlLocalized(320, 355, 150, 18, 1011012); // CANCEL + AddButton(285, 355, 4005, 4007, 0); - var y = 35; + y = 35; for (var i = 0; i < Categories.Length; i++) { var cat = Categories[i]; - builder.AddHtmlLocalized(5, y, 150, 25, cat.LocNumber, true); - builder.AddButton(155, y, 4005, 4007, 0, GumpButtonType.Page, i + 1); + AddHtmlLocalized(5, y, 150, 25, cat.LocNumber, true); + AddButton(155, y, 4005, 4007, 0, GumpButtonType.Page, i + 1); y += 25; } for (var i = 0; i < Categories.Length; i++) { var cat = Categories[i]; - builder.AddPage(i + 1); + AddPage(i + 1); for (var c = 0; c < cat.Entries.Length; c++) { var entry = cat.Entries[c]; - var x = 198 + c % 3 * 129; + x = 198 + c % 3 * 129; y = 38 + c / 3 * 67; - builder.AddHtmlLocalized(x, y, 100, entry.LongText ? 36 : 18, entry.LocNumber); + AddHtmlLocalized(x, y, 100, entry.LongText ? 36 : 18, entry.LocNumber); if (entry.ArtNumber != 0) { - builder.AddItem(x + 20, y + 25, entry.ArtNumber); + AddItem(x + 20, y + 25, entry.ArtNumber); } - builder.AddRadio(x, y + (entry.LongText ? 40 : 20), 210, 211, false, (c << 8) + i); + AddRadio(x, y + (entry.LongText ? 40 : 20), 210, 211, false, (c << 8) + i); } if (cat.CanDye) { - builder.AddHtmlLocalized(327, 239, 100, 18, 1011402); // Color - builder.AddRadio(327, 259, 210, 211, false, 100 + i); + AddHtmlLocalized(327, 239, 100, 18, 1011402); // Color + AddRadio(327, 259, 210, 211, false, 100 + i); } - builder.AddHtmlLocalized(456, 239, 100, 18, 1011403); // Remove - builder.AddRadio(456, 259, 210, 211, false, 200 + i); + AddHtmlLocalized(456, 239, 100, 18, 1011403); // Remove + AddRadio(456, 259, 210, 211, false, 200 + i); } } public override void OnResponse(NetState state, in RelayInfo info) { - if (_vendor.Deleted) + if (m_Vendor.Deleted) { return; } var from = state.Mobile; - if (_vendor is PlayerVendor vendor && !vendor.CanInteractWith(from, true)) + if (m_Vendor is PlayerVendor vendor && !vendor.CanInteractWith(from, true)) { return; } - if (_vendor is PlayerBarkeeper barkeeper && !barkeeper.IsOwner(from)) + if (m_Vendor is PlayerBarkeeper barkeeper && !barkeeper.IsOwner(from)) { return; } if (info.ButtonID == 0) { - if (_vendor is PlayerVendor) // do nothing for barkeeps + if (m_Vendor is PlayerVendor) // do nothing for barkeeps { - _vendor.Direction = _vendor.GetDirectionTo(from); - _vendor.Animate(32, 5, 1, true, false, 0); // bow - _vendor.SayTo(from, 1043310 + Utility.Random(12)); // a little random speech + m_Vendor.Direction = m_Vendor.GetDirectionTo(from); + m_Vendor.Animate(32, 5, 1, true, false, 0); // bow + m_Vendor.SayTo(from, 1043310 + Utility.Random(12)); // a little random speech } } else if (info.ButtonID == 1 && info.Switches.Length > 0) @@ -637,11 +594,11 @@ namespace Server.Gumps { if (ent < Categories[cat].Entries.Length && ent >= 0) { - var item = _vendor.FindItemOnLayer(Categories[cat].Layer); + var item = m_Vendor.FindItemOnLayer(Categories[cat].Layer); item?.Delete(); - var items = _vendor.Items; + var items = m_Vendor.Items; for (var i = 0; item == null && i < items.Count; ++i) { @@ -661,30 +618,30 @@ namespace Server.Gumps if (Categories[cat].Layer == Layer.FacialHair) { - if (_vendor.Female) + if (m_Vendor.Female) { from.SendLocalizedMessage(1010639); // You cannot place facial hair on a woman! } else { - var hue = _vendor.FacialHairHue; + var hue = m_Vendor.FacialHairHue; - _vendor.FacialHairItemID = 0; - _vendor.ProcessDelta(); // invalidate item ID for clients + m_Vendor.FacialHairItemID = 0; + m_Vendor.ProcessDelta(); // invalidate item ID for clients - _vendor.FacialHairItemID = Categories[cat].Entries[ent].ItemID; - _vendor.FacialHairHue = hue; + m_Vendor.FacialHairItemID = Categories[cat].Entries[ent].ItemID; + m_Vendor.FacialHairHue = hue; } } else if (Categories[cat].Layer == Layer.Hair) { - var hue = _vendor.HairHue; + var hue = m_Vendor.HairHue; - _vendor.HairItemID = 0; - _vendor.ProcessDelta(); // invalidate item ID for clients + m_Vendor.HairItemID = 0; + m_Vendor.ProcessDelta(); // invalidate item ID for clients - _vendor.HairItemID = Categories[cat].Entries[ent].ItemID; - _vendor.HairHue = hue; + m_Vendor.HairItemID = Categories[cat].Entries[ent].ItemID; + m_Vendor.HairHue = hue; } else { @@ -694,14 +651,14 @@ namespace Server.Gumps { item.Layer = Categories[cat].Layer; - if (!_vendor.EquipItem(item)) + if (!m_Vendor.EquipItem(item)) { item.Delete(); } } } - DisplayTo(from, _vendor); + from.SendGump(new PlayerVendorCustomizeGump(m_Vendor, from)); } } else @@ -716,17 +673,17 @@ namespace Server.Gumps if (category.Layer == Layer.Hair) { - new PVHairHuePicker(false, _vendor, from).SendTo(state); + new PVHairHuePicker(false, m_Vendor, from).SendTo(state); } else if (category.Layer == Layer.FacialHair) { - new PVHairHuePicker(true, _vendor, from).SendTo(state); + new PVHairHuePicker(true, m_Vendor, from).SendTo(state); } else { Item item = null; - var items = _vendor.Items; + var items = m_Vendor.Items; for (var i = 0; item == null && i < items.Count; ++i) { @@ -744,7 +701,7 @@ namespace Server.Gumps if (item != null) { - new PVHuePicker(item, _vendor, from).SendTo(state); + new PVHuePicker(item, m_Vendor, from).SendTo(state); } } } @@ -759,17 +716,17 @@ namespace Server.Gumps if (category.Layer == Layer.Hair) { - _vendor.HairItemID = 0; + m_Vendor.HairItemID = 0; } else if (category.Layer == Layer.FacialHair) { - _vendor.FacialHairItemID = 0; + m_Vendor.FacialHairItemID = 0; } else { Item item = null; - var items = _vendor.Items; + var items = m_Vendor.Items; for (var i = 0; item == null && i < items.Count; ++i) { @@ -788,7 +745,7 @@ namespace Server.Gumps item?.Delete(); } - DisplayTo(from, _vendor); + from.SendGump(new PlayerVendorCustomizeGump(m_Vendor, from)); } } } @@ -905,7 +862,7 @@ namespace Server.Gumps } m_Item.Hue = hue; - DisplayTo(m_Mob, m_Vendor); + m_Mob.SendGump(new PlayerVendorCustomizeGump(m_Vendor, m_Mob)); } } @@ -948,15 +905,15 @@ namespace Server.Gumps m_Vendor.HairHue = hue; } - DisplayTo(m_Mob, m_Vendor); + m_Mob.SendGump(new PlayerVendorCustomizeGump(m_Vendor, m_Mob)); } } } - public class NewPlayerVendorCustomizeGump : DynamicGump + public class NewPlayerVendorCustomizeGump : Gump { - private static readonly HairOrBeard[] _hairStyles = - [ + private static readonly HairOrBeard[] m_HairStyles = + { new(0x203B, 1011052), // Short new(0x203C, 1011053), // Long new(0x203D, 1011054), // Ponytail @@ -966,10 +923,10 @@ namespace Server.Gumps new(0x2047, 1011396), // Curly new(0x2048, 1011048), // Receding new(0x2049, 1011049) // 2-tails - ]; + }; - private static readonly HairOrBeard[] _beardStyles = - [ + private static readonly HairOrBeard[] m_BeardStyles = + { new(0x2041, 1011062), // Mustache new(0x203F, 1011060), // Short beard new(0x204B, 1015321), // Short Beard & Moustache @@ -977,93 +934,81 @@ namespace Server.Gumps new(0x204C, 1015322), // Long Beard & Moustache new(0x2040, 1015323), // Goatee new(0x204D, 1011401) // Vandyke - ]; + }; - private readonly PlayerVendor _vendor; + private readonly PlayerVendor m_Vendor; - public override bool Singleton => true; - - private NewPlayerVendorCustomizeGump(PlayerVendor vendor) : base(50, 50) => _vendor = vendor; - - public static void DisplayTo(Mobile from, PlayerVendor vendor) + public NewPlayerVendorCustomizeGump(PlayerVendor vendor) : base(50, 50) { - if (from != null && vendor != null && !vendor.Deleted) + m_Vendor = vendor; + + AddBackground(0, 0, 370, 370, 0x13BE); + + AddImageTiled(10, 10, 350, 20, 0xA40); + AddImageTiled(10, 40, 350, 20, 0xA40); + AddImageTiled(10, 70, 350, 260, 0xA40); + AddImageTiled(10, 340, 350, 20, 0xA40); + + AddAlphaRegion(10, 10, 350, 350); + + AddHtmlLocalized(10, 12, 350, 18, 1011356, 0x7FFF); //
VENDOR CUSTOMIZATION MENU
+ + AddHtmlLocalized(10, 42, 150, 18, 1062459, 0x421F); //
HAIR
+ + for (var i = 0; i < m_HairStyles.Length; i++) { - from.SendGump(new NewPlayerVendorCustomizeGump(vendor)); - } - } + var hair = m_HairStyles[i]; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 370, 370, 0x13BE); - - builder.AddImageTiled(10, 10, 350, 20, 0xA40); - builder.AddImageTiled(10, 40, 350, 20, 0xA40); - builder.AddImageTiled(10, 70, 350, 260, 0xA40); - builder.AddImageTiled(10, 340, 350, 20, 0xA40); - - builder.AddAlphaRegion(10, 10, 350, 350); - - builder.AddHtmlLocalized(10, 12, 350, 18, 1011356, 0x7FFF); //
VENDOR CUSTOMIZATION MENU
- - builder.AddHtmlLocalized(10, 42, 150, 18, 1062459, 0x421F); //
HAIR
- - for (var i = 0; i < _hairStyles.Length; i++) - { - var hair = _hairStyles[i]; - - builder.AddButton(10, 70 + i * 20, 0xFA5, 0xFA7, 0x100 | i); - builder.AddHtmlLocalized(45, 72 + i * 20, 110, 18, hair.Name, 0x7FFF); + AddButton(10, 70 + i * 20, 0xFA5, 0xFA7, 0x100 | i); + AddHtmlLocalized(45, 72 + i * 20, 110, 18, hair.Name, 0x7FFF); } - builder.AddButton(10, 70 + _hairStyles.Length * 20, 0xFB1, 0xFB3, 2); - builder.AddHtmlLocalized(45, 72 + _hairStyles.Length * 20, 110, 18, 1011403, 0x7FFF); // Remove + AddButton(10, 70 + m_HairStyles.Length * 20, 0xFB1, 0xFB3, 2); + AddHtmlLocalized(45, 72 + m_HairStyles.Length * 20, 110, 18, 1011403, 0x7FFF); // Remove - builder.AddButton(10, 70 + (_hairStyles.Length + 1) * 20, 0xFA5, 0xFA7, 3); - builder.AddHtmlLocalized(45, 72 + (_hairStyles.Length + 1) * 20, 110, 18, 1011402, 0x7FFF); // Color + AddButton(10, 70 + (m_HairStyles.Length + 1) * 20, 0xFA5, 0xFA7, 3); + AddHtmlLocalized(45, 72 + (m_HairStyles.Length + 1) * 20, 110, 18, 1011402, 0x7FFF); // Color - if (_vendor.Female) + if (vendor.Female) { - builder.AddButton(160, 290, 0xFA5, 0xFA7, 1); - builder.AddHtmlLocalized(195, 292, 160, 18, 1015327, 0x7FFF); // Male + AddButton(160, 290, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(195, 292, 160, 18, 1015327, 0x7FFF); // Male - builder.AddHtmlLocalized(195, 312, 160, 18, 1015328, 0x421F); // Female + AddHtmlLocalized(195, 312, 160, 18, 1015328, 0x421F); // Female } else { - builder.AddHtmlLocalized(160, 42, 210, 18, 1062460, 0x421F); //
BEARD
+ AddHtmlLocalized(160, 42, 210, 18, 1062460, 0x421F); //
BEARD
- for (var i = 0; i < _beardStyles.Length; i++) + for (var i = 0; i < m_BeardStyles.Length; i++) { - var beard = _beardStyles[i]; + var beard = m_BeardStyles[i]; - builder.AddButton(160, 70 + i * 20, 0xFA5, 0xFA7, 0x200 | i); - builder.AddHtmlLocalized(195, 72 + i * 20, 160, 18, beard.Name, 0x7FFF); + AddButton(160, 70 + i * 20, 0xFA5, 0xFA7, 0x200 | i); + AddHtmlLocalized(195, 72 + i * 20, 160, 18, beard.Name, 0x7FFF); } - builder.AddButton(160, 70 + _beardStyles.Length * 20, 0xFB1, 0xFB3, 4); - builder.AddHtmlLocalized(195, 72 + _beardStyles.Length * 20, 160, 18, 1011403, 0x7FFF); // Remove + AddButton(160, 70 + m_BeardStyles.Length * 20, 0xFB1, 0xFB3, 4); + AddHtmlLocalized(195, 72 + m_BeardStyles.Length * 20, 160, 18, 1011403, 0x7FFF); // Remove - builder.AddButton(160, 70 + (_beardStyles.Length + 1) * 20, 0xFA5, 0xFA7, 5); - builder.AddHtmlLocalized(195, 72 + (_beardStyles.Length + 1) * 20, 160, 18, 1011402, 0x7FFF); // Color + AddButton(160, 70 + (m_BeardStyles.Length + 1) * 20, 0xFA5, 0xFA7, 5); + AddHtmlLocalized(195, 72 + (m_BeardStyles.Length + 1) * 20, 160, 18, 1011402, 0x7FFF); // Color - builder.AddHtmlLocalized(195, 292, 160, 18, 1015327, 0x421F); // Male + AddHtmlLocalized(195, 292, 160, 18, 1015327, 0x421F); // Male - builder.AddButton(160, 310, 0xFA5, 0xFA7, 1); - builder.AddHtmlLocalized(195, 312, 160, 18, 1015328, 0x7FFF); // Female + AddButton(160, 310, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(195, 312, 160, 18, 1015328, 0x7FFF); // Female } - builder.AddButton(10, 340, 0xFA5, 0xFA7, 0); - builder.AddHtmlLocalized(45, 342, 305, 18, 1060675, 0x7FFF); // CLOSE + AddButton(10, 340, 0xFA5, 0xFA7, 0); + AddHtmlLocalized(45, 342, 305, 18, 1060675, 0x7FFF); // CLOSE } public override void OnResponse(NetState sender, in RelayInfo info) { var from = sender.Mobile; - if (!_vendor.CanInteractWith(from, true)) + if (!m_Vendor.CanInteractWith(from, true)) { return; } @@ -1072,69 +1017,69 @@ namespace Server.Gumps { case 0: // CLOSE { - _vendor.Direction = _vendor.GetDirectionTo(from); - _vendor.Animate(32, 5, 1, true, false, 0); // bow - _vendor.SayTo(from, 1043310 + Utility.Random(12)); // a little random speech + m_Vendor.Direction = m_Vendor.GetDirectionTo(from); + m_Vendor.Animate(32, 5, 1, true, false, 0); // bow + m_Vendor.SayTo(from, 1043310 + Utility.Random(12)); // a little random speech break; } case 1: // Female/Male { - if (_vendor.Female) + if (m_Vendor.Female) { - _vendor.Body = 400; - _vendor.Female = false; + m_Vendor.Body = 400; + m_Vendor.Female = false; } else { - _vendor.Body = 401; - _vendor.Female = true; + m_Vendor.Body = 401; + m_Vendor.Female = true; - _vendor.FacialHairItemID = 0; + m_Vendor.FacialHairItemID = 0; } - DisplayTo(from, _vendor); + from.SendGump(new NewPlayerVendorCustomizeGump(m_Vendor)); break; } case 2: // Remove hair { - _vendor.HairItemID = 0; + m_Vendor.HairItemID = 0; - DisplayTo(from, _vendor); + from.SendGump(new NewPlayerVendorCustomizeGump(m_Vendor)); break; } case 3: // Color hair { - if (_vendor.HairItemID > 0) + if (m_Vendor.HairItemID > 0) { - new PVHuePicker(_vendor, false, from).SendTo(from.NetState); + new PVHuePicker(m_Vendor, false, from).SendTo(from.NetState); } else { - DisplayTo(from, _vendor); + from.SendGump(new NewPlayerVendorCustomizeGump(m_Vendor)); } break; } case 4: // Remove beard { - _vendor.FacialHairItemID = 0; + m_Vendor.FacialHairItemID = 0; - DisplayTo(from, _vendor); + from.SendGump(new NewPlayerVendorCustomizeGump(m_Vendor)); break; } case 5: // Color beard { - if (_vendor.FacialHairItemID > 0) + if (m_Vendor.FacialHairItemID > 0) { - new PVHuePicker(_vendor, true, from).SendTo(from.NetState); + new PVHuePicker(m_Vendor, true, from).SendTo(from.NetState); } else { - DisplayTo(from, _vendor); + from.SendGump(new NewPlayerVendorCustomizeGump(m_Vendor)); } break; @@ -1147,50 +1092,50 @@ namespace Server.Gumps { var index = info.ButtonID & 0xFF; - if (index >= _hairStyles.Length) + if (index >= m_HairStyles.Length) { return; } - var hairStyle = _hairStyles[index]; + var hairStyle = m_HairStyles[index]; - hairhue = _vendor.HairHue; + hairhue = m_Vendor.HairHue; - _vendor.HairItemID = 0; - _vendor.ProcessDelta(); + m_Vendor.HairItemID = 0; + m_Vendor.ProcessDelta(); - _vendor.HairItemID = hairStyle.ItemID; + m_Vendor.HairItemID = hairStyle.ItemID; - _vendor.HairHue = hairhue; + m_Vendor.HairHue = hairhue; - DisplayTo(from, _vendor); + from.SendGump(new NewPlayerVendorCustomizeGump(m_Vendor)); } else if ((info.ButtonID & 0x200) != 0) // Beard style selected { - if (_vendor.Female) + if (m_Vendor.Female) { return; } var index = info.ButtonID & 0xFF; - if (index >= _beardStyles.Length) + if (index >= m_BeardStyles.Length) { return; } - var beardStyle = _beardStyles[index]; + var beardStyle = m_BeardStyles[index]; - hairhue = _vendor.FacialHairHue; + hairhue = m_Vendor.FacialHairHue; - _vendor.FacialHairItemID = 0; - _vendor.ProcessDelta(); + m_Vendor.FacialHairItemID = 0; + m_Vendor.ProcessDelta(); - _vendor.FacialHairItemID = beardStyle.ItemID; + m_Vendor.FacialHairItemID = beardStyle.ItemID; - _vendor.FacialHairHue = hairhue; + m_Vendor.FacialHairHue = hairhue; - DisplayTo(from, _vendor); + from.SendGump(new NewPlayerVendorCustomizeGump(m_Vendor)); } break; @@ -1240,7 +1185,7 @@ namespace Server.Gumps m_Vendor.HairHue = hue; } - DisplayTo(m_From, m_Vendor); + m_From.SendGump(new NewPlayerVendorCustomizeGump(m_Vendor)); } } } diff --git a/Projects/UOContent/Gumps/PolymorphGump.cs b/Projects/UOContent/Gumps/PolymorphGump.cs index d2a286ad1..0bd001ffe 100644 --- a/Projects/UOContent/Gumps/PolymorphGump.cs +++ b/Projects/UOContent/Gumps/PolymorphGump.cs @@ -49,7 +49,7 @@ namespace Server.Gumps public class PolymorphGump : StaticGump { private static readonly PolymorphCategory[] Categories = - [ + { new( 1015235, // Animals PolymorphEntry.Chicken, @@ -75,7 +75,7 @@ namespace Server.Gumps PolymorphEntry.Daemon, PolymorphEntry.HumanFemale ) - ]; + }; private readonly Item _scroll; @@ -137,7 +137,7 @@ namespace Server.Gumps var entries = Categories[cat]; - if (ent >= 0 && ent < entries.Entries.Length) + if (ent < 0 || ent >= entries.Entries.Length) { Spell spell = new PolymorphSpell(state.Mobile, _scroll, entries.Entries[ent].BodyID); spell.Cast(); @@ -161,7 +161,7 @@ namespace Server.Gumps public class NewPolymorphGump : StaticGump { private static readonly PolymorphEntry[] _entries = - [ + { PolymorphEntry.Chicken, PolymorphEntry.Dog, PolymorphEntry.Wolf, @@ -180,7 +180,7 @@ namespace Server.Gumps PolymorphEntry.Troll, PolymorphEntry.Ettin, PolymorphEntry.Daemon - ]; + }; private readonly Item _scroll; diff --git a/Projects/UOContent/Gumps/Props/Exceptions.cs b/Projects/UOContent/Gumps/Props/Exceptions.cs index a881fd17c..d1eccab10 100644 --- a/Projects/UOContent/Gumps/Props/Exceptions.cs +++ b/Projects/UOContent/Gumps/Props/Exceptions.cs @@ -6,28 +6,33 @@ namespace Server.Gumps { protected Property m_Property; - public PropertyException(Property property, string message) : base(message) => m_Property = property; + public PropertyException(Property property, string message) + : base(message) => + m_Property = property; public Property Property => m_Property; } public abstract class BindingException : PropertyException { - public BindingException(Property property, string message) : base(property, message) + public BindingException(Property property, string message) + : base(property, message) { } } public sealed class NotYetBoundException : BindingException { - public NotYetBoundException(Property property) : base(property, "Property has not yet been bound.") + public NotYetBoundException(Property property) + : base(property, "Property has not yet been bound.") { } } public sealed class AlreadyBoundException : BindingException { - public AlreadyBoundException(Property property) : base(property, "Property has already been bound.") + public AlreadyBoundException(Property property) + : base(property, "Property has already been bound.") { } } @@ -42,28 +47,32 @@ namespace Server.Gumps public sealed class ReadOnlyException : BindingException { - public ReadOnlyException(Property property) : base(property, "Property is read-only.") + public ReadOnlyException(Property property) + : base(property, "Property is read-only.") { } } public sealed class WriteOnlyException : BindingException { - public WriteOnlyException(Property property) : base(property, "Property is write-only.") + public WriteOnlyException(Property property) + : base(property, "Property is write-only.") { } } public abstract class AccessException : PropertyException { - public AccessException(Property property, string message) : base(property, message) + public AccessException(Property property, string message) + : base(property, message) { } } public sealed class InternalAccessException : AccessException { - public InternalAccessException(Property property) : base(property, "Property is internal.") + public InternalAccessException(Property property) + : base(property, "Property is internal.") { } } diff --git a/Projects/UOContent/Gumps/Props/PropsConfig.cs b/Projects/UOContent/Gumps/Props/PropsConfig.cs index 61737f85f..4f525bc74 100644 --- a/Projects/UOContent/Gumps/Props/PropsConfig.cs +++ b/Projects/UOContent/Gumps/Props/PropsConfig.cs @@ -2,6 +2,8 @@ namespace Server.Gumps; public static class PropsConfig { + public const bool OldStyle = false; + public const int GumpOffsetX = 30; public const int GumpOffsetY = 30; @@ -11,15 +13,14 @@ public static class PropsConfig public const int OffsetGumpID = 0x0A40; // Pure black // Light off-white, textured : Dark navy blue, textured - public const int HeaderGumpID = 0x0E14; + public const int HeaderGumpID = OldStyle ? 0x0BBC : 0x0E14; public const int EntryGumpID = 0x0BBC; // Light offwhite, textured public const int BackGumpID = 0x13BE; // Gray slate/stoney - public const int SetGumpID = 0x0E14; // Empty : Dark navy blue, textured + public const int SetGumpID = OldStyle ? 0x0000 : 0x0E14; // Empty : Dark navy blue, textured public const int SetWidth = 20; - public const int SetOffsetX = 2; - public const int SetOffsetY = 2; + public const int SetOffsetX = OldStyle ? 4 : 2, SetOffsetY = 2; public const int SetButtonID1 = 0x15E1; // Arrow pointing right public const int SetButtonID2 = 0x15E5; // " pressed @@ -38,4 +39,7 @@ public static class PropsConfig public const int EntryHeight = 20; public const int BorderSize = 10; public const int ApplySize = 30; + + public const bool PrevLabel = false; + public const bool NextLabel = false; } diff --git a/Projects/UOContent/Gumps/Props/PropsGump.cs b/Projects/UOContent/Gumps/Props/PropsGump.cs index eda11e287..381f28176 100644 --- a/Projects/UOContent/Gumps/Props/PropsGump.cs +++ b/Projects/UOContent/Gumps/Props/PropsGump.cs @@ -24,16 +24,27 @@ namespace Server.Gumps public class PropertiesGump : Gump { - private const int NameWidth = 107; - private const int ValueWidth = 128; + private static readonly bool PrevLabel = OldStyle; + private static readonly bool NextLabel = OldStyle; + private static readonly bool TypeLabel = !OldStyle; - private const int MaxEntriesPerPage = 15; + public static readonly int PrevLabelOffsetX = PrevWidth + 1; + private static readonly int PrevLabelOffsetY = 0; - private const int TypeWidth = NameWidth + OffsetSize + ValueWidth; + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; - private const int TotalWidth = OffsetSize + NameWidth + OffsetSize + ValueWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int NameWidth = 107; + private static readonly int ValueWidth = 128; - protected const int BackWidth = BorderSize + TotalWidth + BorderSize; + public static readonly int MaxEntriesPerPage = 15; + + private static readonly int TypeWidth = NameWidth + OffsetSize + ValueWidth; + + private static readonly int TotalWidth = + OffsetSize + NameWidth + OffsetSize + ValueWidth + OffsetSize + SetWidth + OffsetSize; + + protected static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; protected readonly List m_List; protected readonly Mobile m_Mobile; @@ -106,45 +117,120 @@ namespace Server.Gumps --m_EntryCount; } + var totalHeight = TotalHeight; + AddPage(0); - this.AddPropsFrame(TotalWidth, m_EntryCount + 1, out var x, out var y); - var title = m_Type != null && m_Object is ISerializable serializable - ? $"{m_Type.Name} ({serializable.Serial})".Center(0xFAFAFA) - : m_Type?.Name.Center(0xFAFAFA); - - this.AddPropsHeader( - TotalWidth, ref x, ref y, - title, - page > 0, 1, - (page + 1) * MaxEntriesPerPage < m_List.Count, 2, - nextType: GumpButtonType.Reply, nextParam: 1 + AddBackground(0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + OffsetSize + (EntryHeight + OffsetSize) * (m_EntryCount + 1), + OffsetGumpID ); + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; + + var emptyWidth = TotalWidth - PrevWidth - NextWidth - OffsetSize * 4 - (OldStyle ? SetWidth + OffsetSize : 0); + + if (OldStyle) + { + AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID); + } + else + { + AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID); + } + + if (page > 0) + { + AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1); + + if (PrevLabel) + { + AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous"); + } + } + + x += PrevWidth + OffsetSize; + + if (!OldStyle) + { + AddImageTiled(x, y, emptyWidth, EntryHeight, HeaderGumpID); + } + + if (TypeLabel && m_Type != null) + { + AddHtml( + x, + y, + emptyWidth, + EntryHeight, + m_Type.Name.Center(0xFAFAFA) + ); + } + + x += emptyWidth + OffsetSize; + + if (!OldStyle) + { + AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID); + } + + if ((page + 1) * MaxEntriesPerPage < m_List.Count) + { + AddButton(x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 2, GumpButtonType.Reply, 1); + + if (NextLabel) + { + AddLabel(x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next"); + } + } + for (int i = 0, index = page * MaxEntriesPerPage; i < m_EntryCount && index < m_List.Count; ++i, ++index) { - PropsLayout.NextRow(ref x, ref y); + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; var o = m_List[index]; if (o == null) { - this.AddPropsEntryBlank(x, y, TotalWidth); + AddImageTiled(x - OffsetSize, y, TotalWidth, EntryHeight, BackGumpID + 4); } else if (o is Type type) { - this.AddPropsEntryType(ref x, ref y, TypeWidth, type.Name); + AddImageTiled(x, y, TypeWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, TypeWidth - TextOffsetX, EntryHeight, TextHue, type.Name); + x += TypeWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } } else if (o is PropertyInfo prop) { - var cpa = GetCPA(prop); - var canModify = cpa?.ReadOnly == false && m_Mobile.AccessLevel >= cpa.WriteLevel && (prop.CanWrite || cpa.CanModify); + AddImageTiled(x, y, NameWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, NameWidth - TextOffsetX, EntryHeight, TextHue, prop.Name); + x += NameWidth + OffsetSize; + AddImageTiled(x, y, ValueWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, ValueWidth - TextOffsetX, EntryHeight, TextHue, ValueToString(prop)); + x += ValueWidth + OffsetSize; - this.AddPropsEntryNameValue( - ref x, ref y, NameWidth, ValueWidth, - prop.Name, ValueToString(prop), - canModify, i + 3 - ); + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + var cpa = GetCPA(prop); + + if (cpa?.ReadOnly == false && m_Mobile.AccessLevel >= cpa.WriteLevel && (prop.CanWrite || cpa.CanModify)) + { + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, i + 3); + } } } } @@ -213,8 +299,7 @@ namespace Server.Gumps return; } - var obj = prop.GetValue(m_Object, null); - var type = obj?.GetType() ?? prop.PropertyType; + var type = prop.PropertyType; if (IsType(type, OfEntity)) { @@ -307,6 +392,8 @@ namespace Server.Gumps } else if (HasAttribute(type, OfPropertyObject, true)) { + var obj = prop.GetValue(m_Object, null); + from.SendGump( obj != null ? new PropertiesGump(from, obj, m_Stack, new StackEntry(m_Object, prop)) @@ -366,12 +453,12 @@ namespace Server.Gumps if (attrs.Length == 0) { - return []; + return Array.Empty(); } if (attrs[0] is not CustomEnumAttribute ce) { - return []; + return Array.Empty(); } return ce.Names; @@ -541,7 +628,7 @@ namespace Server.Gumps } else { - groups[type] = [prop]; + groups[type] = new List { prop }; } } } diff --git a/Projects/UOContent/Gumps/Props/PropsLayoutExtensions.cs b/Projects/UOContent/Gumps/Props/PropsLayoutExtensions.cs deleted file mode 100644 index f237ae581..000000000 --- a/Projects/UOContent/Gumps/Props/PropsLayoutExtensions.cs +++ /dev/null @@ -1,677 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: PropsLayoutExtensions.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; - -using static Server.Gumps.PropsConfig; - -namespace Server.Gumps; - -/// -/// Shared helper for advancing to the next row in PropsConfig-style layouts. -/// Resets x to content origin and advances y by one row height. -/// -public static class PropsLayout -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void NextRow(ref int x, ref int y, int entryHeight = EntryHeight) - { - x = BorderSize + OffsetSize; - y += entryHeight + OffsetSize; - } -} - -/// -/// Extension methods for legacy Gump that provide PropsConfig-style layout building blocks. -/// Each method encapsulates a common row pattern used across staff gumps. -/// -public static class PropsLayoutExtensions -{ - extension(Gump gump) - { - /// - /// Adds the standard PropsConfig frame: outer background + inner offset region. - /// Outputs the content origin (x, y) for the first row. - /// Does NOT call AddPage — callers handle paging strategy. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsFrame( - int totalWidth, - int rowCount, - out int x, - out int y, - int entryHeight = EntryHeight - ) - { - var totalHeight = OffsetSize + (entryHeight + OffsetSize) * rowCount; - - gump.AddBackground(0, 0, BorderSize + totalWidth + BorderSize, BorderSize + totalHeight + BorderSize, BackGumpID); - gump.AddImageTiled(BorderSize, BorderSize, totalWidth, totalHeight, OffsetGumpID); - - x = BorderSize + OffsetSize; - y = BorderSize + OffsetSize; - } - - /// - /// Adds a 3-column navigation header: [Prev] [Title] [Next]. - /// All three columns use HeaderGumpID backgrounds. - /// - public void AddPropsHeader( - int totalWidth, - ref int x, - ref int y, - string title, - bool hasPrev, - int prevButtonId, - bool hasNext, - int nextButtonId, - GumpButtonType prevType = GumpButtonType.Reply, - int prevParam = 0, - GumpButtonType nextType = GumpButtonType.Reply, - int nextParam = 0, - int entryHeight = EntryHeight - ) - { - var emptyWidth = totalWidth - PrevWidth - NextWidth - OffsetSize * 4; - - gump.AddImageTiled(x, y, PrevWidth, entryHeight, HeaderGumpID); - - if (hasPrev) - { - gump.AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, prevButtonId, prevType, prevParam); - } - - x += PrevWidth + OffsetSize; - - gump.AddImageTiled(x, y, emptyWidth, entryHeight, HeaderGumpID); - - if (title != null) - { - gump.AddHtml(x, y, emptyWidth, entryHeight, title); - } - - x += emptyWidth + OffsetSize; - - gump.AddImageTiled(x, y, NextWidth, entryHeight, HeaderGumpID); - - if (hasNext) - { - gump.AddButton(x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, nextButtonId, nextType, nextParam); - } - } - - /// - /// Adds a 4-column navigation header: [Back] [Title] [Prev] [Next]. - /// Back/Prev/Next use HeaderGumpID, Title uses EntryGumpID. - /// - public void AddPropsHeaderWithBack( - int totalWidth, - ref int x, - ref int y, - string title, - bool hasBack, - int backButtonId, - bool hasPrev, - int prevButtonId, - bool hasNext, - int nextButtonId, - GumpButtonType nextType = GumpButtonType.Reply, - int nextParam = 0, - int entryHeight = EntryHeight - ) - { - var emptyWidth = totalWidth - PrevWidth * 2 - NextWidth - OffsetSize * 5; - - gump.AddImageTiled(x, y, PrevWidth, entryHeight, HeaderGumpID); - - if (hasBack) - { - gump.AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, backButtonId); - } - - x += PrevWidth + OffsetSize; - - gump.AddImageTiled(x, y, emptyWidth, entryHeight, EntryGumpID); - - if (title != null) - { - gump.AddHtml( - x + TextOffsetX, - y + (entryHeight - EntryHeight) / 2, - emptyWidth - TextOffsetX, - entryHeight, - $"
{title}
" - ); - } - - x += emptyWidth + OffsetSize; - - gump.AddImageTiled(x, y, PrevWidth, entryHeight, HeaderGumpID); - - if (hasPrev) - { - gump.AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, prevButtonId); - } - - x += PrevWidth + OffsetSize; - - gump.AddImageTiled(x, y, NextWidth, entryHeight, HeaderGumpID); - - if (hasNext) - { - gump.AddButton(x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, nextButtonId, nextType, nextParam); - } - } - - /// - /// Adds an entry row with a cropped label and optional action button. - /// Layout: [Label:EntryGumpID] [Button:SetGumpID] - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryButton( - ref int x, - ref int y, - int entryWidth, - string label, - bool hasButton, - int buttonId, - int textHue = TextHue, - int entryHeight = EntryHeight - ) - { - gump.AddImageTiled(x, y, entryWidth, entryHeight, EntryGumpID); - gump.AddLabelCropped(x + TextOffsetX, y, entryWidth - TextOffsetX, entryHeight, textHue, label); - x += entryWidth + OffsetSize; - - if (SetGumpID != 0) - { - gump.AddImageTiled(x, y, SetWidth, entryHeight, SetGumpID); - } - - if (hasButton) - { - gump.AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, buttonId); - } - } - - /// - /// Adds a two-column entry row with name, value, and optional action button. - /// Layout: [Name:EntryGumpID] [Value:EntryGumpID] [Button:SetGumpID] - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryNameValue( - ref int x, - ref int y, - int nameWidth, - int valueWidth, - string name, - string value, - bool hasButton, - int buttonId, - int entryHeight = EntryHeight - ) - { - gump.AddImageTiled(x, y, nameWidth, entryHeight, EntryGumpID); - gump.AddLabelCropped(x + TextOffsetX, y, nameWidth - TextOffsetX, entryHeight, TextHue, name); - x += nameWidth + OffsetSize; - - gump.AddImageTiled(x, y, valueWidth, entryHeight, EntryGumpID); - gump.AddLabelCropped(x + TextOffsetX, y, valueWidth - TextOffsetX, entryHeight, TextHue, value); - x += valueWidth + OffsetSize; - - if (SetGumpID != 0) - { - gump.AddImageTiled(x, y, SetWidth, entryHeight, SetGumpID); - } - - if (hasButton) - { - gump.AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, buttonId); - } - } - - /// - /// Adds an entry row with a text input field and optional action button. - /// Layout: [TextEntry:EntryGumpID] [Button:SetGumpID] - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryTextInput( - ref int x, - ref int y, - int entryWidth, - int entryId, - string initialText, - bool hasButton, - int buttonId, - int entryHeight = EntryHeight - ) - { - gump.AddImageTiled(x, y, entryWidth, entryHeight, EntryGumpID); - gump.AddTextEntry(x + TextOffsetX, y, entryWidth - TextOffsetX, entryHeight, TextHue, entryId, initialText); - x += entryWidth + OffsetSize; - - if (SetGumpID != 0) - { - gump.AddImageTiled(x, y, SetWidth, entryHeight, SetGumpID); - } - - if (hasButton) - { - gump.AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, buttonId); - } - } - - /// - /// Adds an entry row with a cropped label and no action button. - /// Layout: [Label:EntryGumpID] [Empty:SetGumpID] - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryLabel( - ref int x, - ref int y, - int entryWidth, - string label, - int entryHeight = EntryHeight - ) - { - gump.AddImageTiled(x, y, entryWidth, entryHeight, EntryGumpID); - gump.AddLabelCropped(x + TextOffsetX, y, entryWidth - TextOffsetX, entryHeight, TextHue, label); - x += entryWidth + OffsetSize; - - if (SetGumpID != 0) - { - gump.AddImageTiled(x, y, SetWidth, entryHeight, SetGumpID); - } - } - - /// - /// Adds a full-width type label row (used for PropsGump type group headers). - /// Layout: [TypeName:EntryGumpID spanning typeWidth] [Empty:SetGumpID] - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryType( - ref int x, - ref int y, - int typeWidth, - string typeName, - int entryHeight = EntryHeight - ) - { - gump.AddImageTiled(x, y, typeWidth, entryHeight, EntryGumpID); - gump.AddLabelCropped(x + TextOffsetX, y, typeWidth - TextOffsetX, entryHeight, TextHue, typeName); - x += typeWidth + OffsetSize; - - if (SetGumpID != 0) - { - gump.AddImageTiled(x, y, SetWidth, entryHeight, SetGumpID); - } - } - - /// - /// Adds a blank separator row spanning the full width. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryBlank( - int x, - int y, - int totalWidth, - int entryHeight = EntryHeight - ) - { - gump.AddImageTiled(x - OffsetSize, y, totalWidth, entryHeight, BackGumpID + 4); - } - } -} - -/// -/// Extension methods for DynamicGumpBuilder that provide PropsConfig-style layout building blocks. -/// Mirrors PropsLayoutExtensions but uses ReadOnlySpan<char> for text parameters. -/// -public static class PropsLayoutBuilderExtensions -{ - extension(ref DynamicGumpBuilder builder) - { - /// - /// Adds the standard PropsConfig frame: outer background + inner offset region. - /// Outputs the content origin (x, y) for the first row. - /// Does NOT call AddPage — callers handle paging strategy. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsFrame( - int totalWidth, - int rowCount, - out int x, - out int y, - int entryHeight = EntryHeight - ) - { - var totalHeight = OffsetSize + (entryHeight + OffsetSize) * rowCount; - - builder.AddBackground( - 0, - 0, - BorderSize + totalWidth + BorderSize, - BorderSize + totalHeight + BorderSize, - BackGumpID - ); - builder.AddImageTiled(BorderSize, BorderSize, totalWidth, totalHeight, OffsetGumpID); - - x = BorderSize + OffsetSize; - y = BorderSize + OffsetSize; - } - - /// - /// Adds a 3-column navigation header: [Prev] [Title] [Next]. - /// All three columns use HeaderGumpID backgrounds. - /// - public void AddPropsHeader( - int totalWidth, - ref int x, - ref int y, - ReadOnlySpan title, - bool hasPrev, - int prevButtonId, - bool hasNext, - int nextButtonId, - GumpButtonType prevType = GumpButtonType.Reply, - int prevParam = 0, - GumpButtonType nextType = GumpButtonType.Reply, - int nextParam = 0, - int entryHeight = EntryHeight - ) - { - var emptyWidth = totalWidth - PrevWidth - NextWidth - OffsetSize * 4; - - builder.AddImageTiled(x, y, PrevWidth, entryHeight, HeaderGumpID); - - if (hasPrev) - { - builder.AddButton( - x + PrevOffsetX, - y + PrevOffsetY, - PrevButtonID1, - PrevButtonID2, - prevButtonId, - prevType, - prevParam - ); - } - - x += PrevWidth + OffsetSize; - - builder.AddImageTiled(x, y, emptyWidth, entryHeight, HeaderGumpID); - - if (title.Length > 0) - { - builder.AddHtml( - x + TextOffsetX, - y, - emptyWidth - TextOffsetX, - entryHeight, - title, - align: TextAlignment.Center - ); - } - - x += emptyWidth + OffsetSize; - - builder.AddImageTiled(x, y, NextWidth, entryHeight, HeaderGumpID); - - if (hasNext) - { - builder.AddButton( - x + NextOffsetX, - y + NextOffsetY, - NextButtonID1, - NextButtonID2, - nextButtonId, - nextType, - nextParam - ); - } - } - - /// - /// Adds a 4-column navigation header: [Back] [Title] [Prev] [Next]. - /// Back/Prev/Next use HeaderGumpID, Title uses EntryGumpID. - /// - public void AddPropsHeaderWithBack( - int totalWidth, - ref int x, - ref int y, - ReadOnlySpan title, - bool hasBack, - int backButtonId, - bool hasPrev, - int prevButtonId, - bool hasNext, - int nextButtonId, - GumpButtonType nextType = GumpButtonType.Reply, - int nextParam = 0, - int entryHeight = EntryHeight - ) - { - var emptyWidth = totalWidth - PrevWidth * 2 - NextWidth - OffsetSize * 5; - - builder.AddImageTiled(x, y, PrevWidth, entryHeight, HeaderGumpID); - - if (hasBack) - { - builder.AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, backButtonId); - } - - x += PrevWidth + OffsetSize; - - builder.AddImageTiled(x, y, emptyWidth, entryHeight, EntryGumpID); - - if (title.Length > 0) - { - builder.AddHtml( - x + TextOffsetX, - y + (entryHeight - EntryHeight) / 2, - emptyWidth - TextOffsetX, - entryHeight, - title, - align: TextAlignment.Center - ); - } - - x += emptyWidth + OffsetSize; - - builder.AddImageTiled(x, y, PrevWidth, entryHeight, HeaderGumpID); - - if (hasPrev) - { - builder.AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, prevButtonId); - } - - x += PrevWidth + OffsetSize; - - builder.AddImageTiled(x, y, NextWidth, entryHeight, HeaderGumpID); - - if (hasNext) - { - builder.AddButton( - x + NextOffsetX, - y + NextOffsetY, - NextButtonID1, - NextButtonID2, - nextButtonId, - nextType, - nextParam - ); - } - } - - /// - /// Adds an entry row with a cropped label and optional action button. - /// Layout: [Label:EntryGumpID] [Button:SetGumpID] - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryButton( - ref int x, - ref int y, - int entryWidth, - ReadOnlySpan label, - bool hasButton, - int buttonId, - int textHue = TextHue, - int entryHeight = EntryHeight - ) - { - builder.AddImageTiled(x, y, entryWidth, entryHeight, EntryGumpID); - builder.AddLabelCropped(x + TextOffsetX, y, entryWidth - TextOffsetX, entryHeight, textHue, label); - x += entryWidth + OffsetSize; - - if (SetGumpID != 0) - { - builder.AddImageTiled(x, y, SetWidth, entryHeight, SetGumpID); - } - - if (hasButton) - { - builder.AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, buttonId); - } - } - - /// - /// Adds a two-column entry row with name, value, and optional action button. - /// Layout: [Name:EntryGumpID] [Value:EntryGumpID] [Button:SetGumpID] - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryNameValue( - ref int x, - ref int y, - int nameWidth, - int valueWidth, - ReadOnlySpan name, - ReadOnlySpan value, - bool hasButton, - int buttonId, - int entryHeight = EntryHeight - ) - { - builder.AddImageTiled(x, y, nameWidth, entryHeight, EntryGumpID); - builder.AddLabelCropped(x + TextOffsetX, y, nameWidth - TextOffsetX, entryHeight, TextHue, name); - x += nameWidth + OffsetSize; - - builder.AddImageTiled(x, y, valueWidth, entryHeight, EntryGumpID); - builder.AddLabelCropped(x + TextOffsetX, y, valueWidth - TextOffsetX, entryHeight, TextHue, value); - x += valueWidth + OffsetSize; - - if (SetGumpID != 0) - { - builder.AddImageTiled(x, y, SetWidth, entryHeight, SetGumpID); - } - - if (hasButton) - { - builder.AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, buttonId); - } - } - - /// - /// Adds an entry row with a text input field and optional action button. - /// Layout: [TextEntry:EntryGumpID] [Button:SetGumpID] - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryTextInput( - ref int x, - ref int y, - int entryWidth, - int entryId, - ReadOnlySpan initialText, - bool hasButton, - int buttonId, - int entryHeight = EntryHeight - ) - { - builder.AddImageTiled(x, y, entryWidth, entryHeight, EntryGumpID); - builder.AddTextEntry(x + TextOffsetX, y, entryWidth - TextOffsetX, entryHeight, TextHue, entryId, initialText); - x += entryWidth + OffsetSize; - - if (SetGumpID != 0) - { - builder.AddImageTiled(x, y, SetWidth, entryHeight, SetGumpID); - } - - if (hasButton) - { - builder.AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, buttonId); - } - } - - /// - /// Adds an entry row with a cropped label and no action button. - /// Layout: [Label:EntryGumpID] [Empty:SetGumpID] - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryLabel( - ref int x, - ref int y, - int entryWidth, - ReadOnlySpan label, - int entryHeight = EntryHeight - ) - { - builder.AddImageTiled(x, y, entryWidth, entryHeight, EntryGumpID); - builder.AddLabelCropped(x + TextOffsetX, y, entryWidth - TextOffsetX, entryHeight, TextHue, label); - x += entryWidth + OffsetSize; - - if (SetGumpID != 0) - { - builder.AddImageTiled(x, y, SetWidth, entryHeight, SetGumpID); - } - } - - /// - /// Adds a full-width type label row (used for PropsGump type group headers). - /// Layout: [TypeName:EntryGumpID spanning typeWidth] [Empty:SetGumpID] - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryType( - ref int x, - ref int y, - int typeWidth, - ReadOnlySpan typeName, - int entryHeight = EntryHeight - ) - { - builder.AddImageTiled(x, y, typeWidth, entryHeight, EntryGumpID); - builder.AddLabelCropped(x + TextOffsetX, y, typeWidth - TextOffsetX, entryHeight, TextHue, typeName); - x += typeWidth + OffsetSize; - - if (SetGumpID != 0) - { - builder.AddImageTiled(x, y, SetWidth, entryHeight, SetGumpID); - } - } - - /// - /// Adds a blank separator row spanning the full width. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddPropsEntryBlank( - int x, - int y, - int totalWidth, - int entryHeight = EntryHeight - ) - { - builder.AddImageTiled(x - OffsetSize, y, totalWidth, entryHeight, BackGumpID + 4); - } - } -} diff --git a/Projects/UOContent/Gumps/Props/SetCustomEnumGump.cs b/Projects/UOContent/Gumps/Props/SetCustomEnumGump.cs index 313eeee10..fdae82471 100644 --- a/Projects/UOContent/Gumps/Props/SetCustomEnumGump.cs +++ b/Projects/UOContent/Gumps/Props/SetCustomEnumGump.cs @@ -31,7 +31,7 @@ namespace Server.Gumps { try { - var info = m_Property.PropertyType.GetMethod("Parse", [typeof(string)]); + var info = m_Property.PropertyType.GetMethod("Parse", new[] { typeof(string) }); string result; @@ -43,7 +43,7 @@ namespace Server.Gumps m_Object, m_Property, m_Property.Name, - info.Invoke(null, [m_Names[index]]), + info.Invoke(null, new object[] { m_Names[index] }), true ); } diff --git a/Projects/UOContent/Gumps/Props/SetGump.cs b/Projects/UOContent/Gumps/Props/SetGump.cs index 886ab28c3..dfe6a57b6 100644 --- a/Projects/UOContent/Gumps/Props/SetGump.cs +++ b/Projects/UOContent/Gumps/Props/SetGump.cs @@ -9,10 +9,13 @@ namespace Server.Gumps { public class SetGump : Gump { - private const int EntryWidth = 212; + private static readonly int EntryWidth = 212; - private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + 2 * (EntryHeight + OffsetSize); + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; private readonly Mobile m_Mobile; private readonly object m_Object; private readonly PropertyInfo m_Property; @@ -36,25 +39,81 @@ namespace Server.Gumps _ => val.ToString() }; - var rowCount = 2 + (canNull ? 1 : 0) + (canDye ? 1 : 0); - AddPage(0); - this.AddPropsFrame(TotalWidth, rowCount, out var x, out var y); - this.AddPropsEntryLabel(ref x, ref y, EntryWidth, prop.Name); - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryTextInput(ref x, ref y, EntryWidth, 0, initialText, true, 1); + AddBackground( + 0, + 0, + BackWidth, + BackHeight + (canNull ? EntryHeight + OffsetSize : 0) + (canDye ? EntryHeight + OffsetSize : 0), + BackGumpID + ); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + TotalHeight + (canNull ? EntryHeight + OffsetSize : 0) + (canDye ? EntryHeight + OffsetSize : 0), + OffsetGumpID + ); + + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddTextEntry(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, 0, initialText); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1); if (canNull) { - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryButton(ref x, ref y, EntryWidth, "Null", true, 2); + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Null"); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2); } if (canDye) { - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryButton(ref x, ref y, EntryWidth, "Hue Picker", true, 3); + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Hue Picker"); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 3); } } @@ -85,6 +144,7 @@ namespace Server.Gumps } else { + toSet = null; shouldSet = false; } @@ -142,8 +202,8 @@ namespace Server.Gumps private readonly PropertyInfo m_Property; private readonly PropertiesGump m_PropertiesGump; - public InternalPicker(PropertyInfo prop, Mobile mobile, object o, PropertiesGump propertiesGump) - : base(((IHued)o).HuedItemID) + public InternalPicker( + PropertyInfo prop, Mobile mobile, object o, PropertiesGump propertiesGump) : base(((IHued)o).HuedItemID) { m_Property = prop; m_Mobile = mobile; diff --git a/Projects/UOContent/Gumps/Props/SetListOptionGump.cs b/Projects/UOContent/Gumps/Props/SetListOptionGump.cs index 09a8060b9..2203a5bfc 100644 --- a/Projects/UOContent/Gumps/Props/SetListOptionGump.cs +++ b/Projects/UOContent/Gumps/Props/SetListOptionGump.cs @@ -9,10 +9,21 @@ namespace Server.Gumps { public class SetListOptionGump : Gump { - private const int EntryWidth = 212; - private const int EntryCount = 13; + private static readonly int EntryWidth = 212; + private static readonly int EntryCount = 13; - private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + + private static readonly bool PrevLabel = OldStyle; + private static readonly bool NextLabel = OldStyle; + + private static readonly int PrevLabelOffsetX = PrevWidth + 1; + private static readonly int PrevLabelOffsetY = 0; + + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; private readonly object[] m_Values; protected Mobile m_Mobile; @@ -46,14 +57,82 @@ namespace Server.Gumps count = EntryCount; } - this.AddPropsFrame(TotalWidth, count + 2, out var x, out var y); - this.AddPropsHeader( - TotalWidth, ref x, ref y, null, - page > 1, 0, page < pages, 0, - prevType: GumpButtonType.Page, prevParam: page - 1, - nextType: GumpButtonType.Page, nextParam: page + 1 + var totalHeight = OffsetSize + (count + 2) * (EntryHeight + OffsetSize); + var backHeight = BorderSize + totalHeight + BorderSize; + + AddBackground(0, 0, BackWidth, backHeight, BackGumpID); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + totalHeight, + OffsetGumpID ); + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; + + var emptyWidth = TotalWidth - PrevWidth - NextWidth - OffsetSize * 4 - + (OldStyle ? SetWidth + OffsetSize : 0); + + AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID); + + if (page > 1) + { + AddButton( + x + PrevOffsetX, + y + PrevOffsetY, + PrevButtonID1, + PrevButtonID2, + 0, + GumpButtonType.Page, + page - 1 + ); + + if (PrevLabel) + { + AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous"); + } + } + + x += PrevWidth + OffsetSize; + + if (!OldStyle) + { + AddImageTiled( + x - (OldStyle ? OffsetSize : 0), + y, + emptyWidth + (OldStyle ? OffsetSize * 2 : 0), + EntryHeight, + HeaderGumpID + ); + } + + x += emptyWidth + OffsetSize; + + if (!OldStyle) + { + AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID); + } + + if (page < pages) + { + AddButton( + x + NextOffsetX, + y + NextOffsetY, + NextButtonID1, + NextButtonID2, + 0, + GumpButtonType.Page, + page + 1 + ); + + if (NextLabel) + { + AddLabel(x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next"); + } + } + AddRect(0, prop.Name, 0); for (var i = 0; i < count; ++i) diff --git a/Projects/UOContent/Gumps/Props/SetObjectGump.cs b/Projects/UOContent/Gumps/Props/SetObjectGump.cs index 788ad79fe..326370aec 100644 --- a/Projects/UOContent/Gumps/Props/SetObjectGump.cs +++ b/Projects/UOContent/Gumps/Props/SetObjectGump.cs @@ -11,9 +11,13 @@ namespace Server.Gumps { public class SetObjectGump : Gump { - private const int EntryWidth = 212; + private static readonly int EntryWidth = 212; - private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + 5 * (EntryHeight + OffsetSize); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; private readonly Mobile m_Mobile; private readonly object m_Object; private readonly PropertyInfo m_Property; @@ -34,16 +38,82 @@ namespace Server.Gumps AddPage(0); - this.AddPropsFrame(TotalWidth, 5, out var x, out var y); - this.AddPropsEntryLabel(ref x, ref y, EntryWidth, prop.Name); - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryButton(ref x, ref y, EntryWidth, initialText, true, 1); - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryButton(ref x, ref y, EntryWidth, "Change by Serial", true, 2); - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryButton(ref x, ref y, EntryWidth, "Nullify", true, 3); - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryButton(ref x, ref y, EntryWidth, "View Properties", true, 4); + AddBackground(0, 0, BackWidth, BackHeight, BackGumpID); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + TotalHeight, + OffsetGumpID + ); + + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, initialText); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Change by Serial"); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Nullify"); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 3); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "View Properties"); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 4); } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs b/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs index 35fd70103..a9703fe1e 100644 --- a/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs +++ b/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs @@ -9,16 +9,22 @@ namespace Server.Gumps { public class SetPoint2DGump : Gump { - private const int CoordWidth = 105; - private const int EntryWidth = CoordWidth + OffsetSize + CoordWidth; + private static readonly int CoordWidth = 105; + private static readonly int EntryWidth = CoordWidth + OffsetSize + CoordWidth; - private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + 4 * (EntryHeight + OffsetSize); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; private readonly Mobile m_Mobile; private readonly object m_Object; private readonly PropertyInfo m_Property; private readonly PropertiesGump m_PropertiesGump; - public SetPoint2DGump(PropertyInfo prop, Mobile mobile, object o, PropertiesGump propertiesGump) + public SetPoint2DGump( + PropertyInfo prop, Mobile mobile, object o, PropertiesGump propertiesGump + ) : base(GumpOffsetX, GumpOffsetY) { m_PropertiesGump = propertiesGump; @@ -30,13 +36,57 @@ namespace Server.Gumps AddPage(0); - this.AddPropsFrame(TotalWidth, 4, out var x, out var y); - this.AddPropsEntryLabel(ref x, ref y, EntryWidth, prop?.Name); - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryButton(ref x, ref y, EntryWidth, "Use your location", true, 1); - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryButton(ref x, ref y, EntryWidth, "Target a location", true, 2); - PropsLayout.NextRow(ref x, ref y); + AddBackground(0, 0, BackWidth, BackHeight, BackGumpID); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + TotalHeight, + OffsetGumpID + ); + + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop?.Name); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Use your location"); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Target a location"); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; AddImageTiled(x, y, CoordWidth, EntryHeight, EntryGumpID); AddLabelCropped(x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "X:"); @@ -129,8 +179,9 @@ namespace Server.Gumps private readonly PropertyInfo m_Property; private readonly PropertiesGump m_PropertiesGump; - public InternalTarget(PropertyInfo prop, Mobile mobile, object o, PropertiesGump propertiesGump) - : base(-1, true, TargetFlags.None) + public InternalTarget( + PropertyInfo prop, Mobile mobile, object o, PropertiesGump propertiesGump + ) : base(-1, true, TargetFlags.None) { m_Property = prop; m_Mobile = mobile; diff --git a/Projects/UOContent/Gumps/Props/SetPoint3DGump.cs b/Projects/UOContent/Gumps/Props/SetPoint3DGump.cs index b0707bc3d..fed371935 100644 --- a/Projects/UOContent/Gumps/Props/SetPoint3DGump.cs +++ b/Projects/UOContent/Gumps/Props/SetPoint3DGump.cs @@ -9,17 +9,23 @@ namespace Server.Gumps { public class SetPoint3DGump : Gump { - private const int CoordWidth = 70; - private const int EntryWidth = CoordWidth + OffsetSize + CoordWidth + OffsetSize + CoordWidth; + private static readonly int CoordWidth = 70; + private static readonly int EntryWidth = CoordWidth + OffsetSize + CoordWidth + OffsetSize + CoordWidth; - private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + 4 * (EntryHeight + OffsetSize); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; private readonly Mobile m_Mobile; private readonly object m_Object; private readonly PropertyInfo m_Property; private readonly PropertiesGump m_PropertiesGump; - public SetPoint3DGump(PropertyInfo prop, Mobile mobile, object o, PropertiesGump propertiesGump) + public SetPoint3DGump( + PropertyInfo prop, Mobile mobile, object o, PropertiesGump propertiesGump + ) : base(GumpOffsetX, GumpOffsetY) { m_PropertiesGump = propertiesGump; @@ -31,13 +37,57 @@ namespace Server.Gumps AddPage(0); - this.AddPropsFrame(TotalWidth, 4, out var x, out var y); - this.AddPropsEntryLabel(ref x, ref y, EntryWidth, prop?.Name); - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryButton(ref x, ref y, EntryWidth, "Use your location", true, 1); - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryButton(ref x, ref y, EntryWidth, "Target a location", true, 2); - PropsLayout.NextRow(ref x, ref y); + AddBackground(0, 0, BackWidth, BackHeight, BackGumpID); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + TotalHeight, + OffsetGumpID + ); + + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop?.Name); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Use your location"); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Target a location"); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; AddImageTiled(x, y, CoordWidth, EntryHeight, EntryGumpID); AddLabelCropped(x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "X:"); @@ -136,8 +186,9 @@ namespace Server.Gumps private readonly PropertyInfo m_Property; private readonly PropertiesGump m_PropertiesGump; - public InternalTarget(PropertyInfo prop, Mobile mobile, object o, PropertiesGump propertiesGump) - : base(-1, true, TargetFlags.None) + public InternalTarget( + PropertyInfo prop, Mobile mobile, object o, PropertiesGump propertiesGump + ) : base(-1, true, TargetFlags.None) { m_PropertiesGump = propertiesGump; m_Property = prop; diff --git a/Projects/UOContent/Gumps/Props/SetTimeSpanGump.cs b/Projects/UOContent/Gumps/Props/SetTimeSpanGump.cs index fb7a5a137..e45a1eae5 100644 --- a/Projects/UOContent/Gumps/Props/SetTimeSpanGump.cs +++ b/Projects/UOContent/Gumps/Props/SetTimeSpanGump.cs @@ -9,9 +9,13 @@ namespace Server.Gumps { public class SetTimeSpanGump : Gump { - private const int EntryWidth = 212; + private static readonly int EntryWidth = 212; - private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + 7 * (EntryHeight + OffsetSize); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; private readonly Mobile m_Mobile; private readonly object m_Object; private readonly PropertyInfo m_Property; @@ -31,7 +35,14 @@ namespace Server.Gumps AddPage(0); - this.AddPropsFrame(TotalWidth, 7, out _, out _); + AddBackground(0, 0, BackWidth, BackHeight, BackGumpID); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + TotalHeight, + OffsetGumpID + ); AddRect(0, prop?.Name, 0, -1); AddRect(1, ts.ToString(), 0, -1); diff --git a/Projects/UOContent/Gumps/ReclaimVendorGump.cs b/Projects/UOContent/Gumps/ReclaimVendorGump.cs index af767f9cc..927dd1b70 100644 --- a/Projects/UOContent/Gumps/ReclaimVendorGump.cs +++ b/Projects/UOContent/Gumps/ReclaimVendorGump.cs @@ -1,94 +1,85 @@ +using System.Collections.Generic; +using System.Linq; using Server.Multis; using Server.Network; -namespace Server.Gumps; - -public class ReclaimVendorGump : DynamicGump +namespace Server.Gumps { - private readonly BaseHouse _house; - private readonly Mobile[] _vendors; - - public override bool Singleton => true; - - private ReclaimVendorGump(BaseHouse house) : base(50, 50) + public class ReclaimVendorGump : Gump { - _house = house; - _vendors = house.InternalizedVendors.ToArray(); - } + private readonly BaseHouse m_House; + private readonly List m_Vendors; - public static void DisplayTo(Mobile from, BaseHouse house) - { - if (from?.NetState != null && house?.Deleted == false && house.InternalizedVendors.Count != 0) + public override bool Singleton => true; + + public ReclaimVendorGump(BaseHouse house) : base(50, 50) { - from.SendGump(new ReclaimVendorGump(house)); - } - } + m_House = house; + m_Vendors = house.InternalizedVendors.ToList(); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddBackground(0, 0, 170, 50 + m_Vendors.Count * 20, 0x13BE); - builder.AddBackground(0, 0, 170, 50 + _vendors.Length * 20, 0x13BE); + AddImageTiled(10, 10, 150, 20, 0xA40); + AddHtmlLocalized(10, 10, 150, 20, 1061827, 0x7FFF); //
Reclaim Vendor
- builder.AddImageTiled(10, 10, 150, 20, 0xA40); - builder.AddHtmlLocalized(10, 10, 150, 20, 1061827, 0x7FFF); //
Reclaim Vendor
+ AddImageTiled(10, 40, 150, m_Vendors.Count * 20, 0xA40); - builder.AddImageTiled(10, 40, 150, _vendors.Length * 20, 0xA40); - - for (var i = 0; i < _vendors.Length; i++) - { - var m = _vendors[i]; - - var y = 40 + i * 20; - - builder.AddButton(10, y, 0xFA5, 0xFA7, i + 1); - builder.AddLabel(45, y, 0x481, m.Name); - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; - - if (info.ButtonID == 0 || !_house.IsActive || !_house.IsInside(from) || !_house.IsOwner(from) || !from.CheckAlive()) - { - return; - } - - var index = info.ButtonID - 1; - - if (index < 0 || index >= _vendors.Length) - { - return; - } - - var mob = _vendors[index]; - - if (!_house.InternalizedVendors.Contains(mob)) - { - return; - } - - if (mob.Deleted) - { - _house.InternalizedVendors.Remove(mob); - } - else - { - BaseHouse.IsThereVendor(from.Location, from.Map, out var vendor, out var contract); - - if (vendor) + for (var i = 0; i < m_Vendors.Count; i++) { - from.SendLocalizedMessage(1062677); // You cannot place a vendor or barkeep at this location. + var m = m_Vendors[i]; + + var y = 40 + i * 20; + + AddButton(10, y, 0xFA5, 0xFA7, i + 1); + AddLabel(45, y, 0x481, m.Name); } - else if (contract) + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + var from = sender.Mobile; + + if (info.ButtonID == 0 || !m_House.IsActive || !m_House.IsInside(from) || !m_House.IsOwner(from) || + !from.CheckAlive()) { - from.SendLocalizedMessage(1062678); // You cannot place a vendor or barkeep on top of a rental contract! + return; + } + + var index = info.ButtonID - 1; + + if (index < 0 || index >= m_Vendors.Count) + { + return; + } + + var mob = m_Vendors[index]; + + if (!m_House.InternalizedVendors.Contains(mob)) + { + return; + } + + if (mob.Deleted) + { + m_House.InternalizedVendors.Remove(mob); } else { - _house.InternalizedVendors.Remove(mob); - mob.MoveToWorld(from.Location, from.Map); + BaseHouse.IsThereVendor(from.Location, from.Map, out var vendor, out var contract); + + if (vendor) + { + from.SendLocalizedMessage(1062677); // You cannot place a vendor or barkeep at this location. + } + else if (contract) + { + from.SendLocalizedMessage(1062678); // You cannot place a vendor or barkeep on top of a rental contract! + } + else + { + m_House.InternalizedVendors.Remove(mob); + mob.MoveToWorld(from.Location, from.Map); + } } } } diff --git a/Projects/UOContent/Gumps/ResurrectGump.cs b/Projects/UOContent/Gumps/ResurrectGump.cs index 44af971d0..6612db956 100644 --- a/Projects/UOContent/Gumps/ResurrectGump.cs +++ b/Projects/UOContent/Gumps/ResurrectGump.cs @@ -176,14 +176,7 @@ public class ResurrectGump : DynamicGump public override void OnResponse(NetState state, in RelayInfo info) { - if (_price > 0) - { - if (info.ButtonID != 2 || info.Switches[0] != 1) - { - return; - } - } - else if (info.ButtonID != 1) + if (info.ButtonID is not 1 and not 2) { return; } diff --git a/Projects/UOContent/Gumps/RewardGump.cs b/Projects/UOContent/Gumps/RewardGump.cs index 9a53de1f3..9d4709fba 100644 --- a/Projects/UOContent/Gumps/RewardGump.cs +++ b/Projects/UOContent/Gumps/RewardGump.cs @@ -1,227 +1,213 @@ using System; using Server.Network; -namespace Server.Gumps; - -/* - * A generic version of the EA Clean Up Britannia reward gump. - */ - -public interface IRewardEntry +namespace Server.Gumps { - int Price { get; } - int ItemID { get; } - int Hue { get; } - int Tooltip { get; } - TextDefinition Description { get; } -} + /* + * A generic version of the EA Clean Up Britannia reward gump. + */ -public delegate void RewardPickedHandler(Mobile from, int index); - -public class RewardGump : DynamicGump -{ - private readonly TextDefinition _title; - private readonly IRewardEntry[] _rewards; - private readonly int _points; - private readonly RewardPickedHandler _onPicked; - - public override bool Singleton => true; - - public TextDefinition Title => _title; - public IRewardEntry[] Rewards => _rewards; - public int Points => _points; - public RewardPickedHandler OnPicked => _onPicked; - - private RewardGump(TextDefinition title, IRewardEntry[] rewards, int points, RewardPickedHandler onPicked) - : base(250, 50) + public interface IRewardEntry { - _title = title; - _rewards = rewards; - _points = points; - _onPicked = onPicked; + int Price { get; } + int ItemID { get; } + int Hue { get; } + int Tooltip { get; } + TextDefinition Description { get; } } - public static void DisplayTo(Mobile from, TextDefinition title, IRewardEntry[] rewards, int points, RewardPickedHandler onPicked) + public delegate void RewardPickedHandler(Mobile from, int index); + + public class RewardGump : Gump { - if (from?.NetState != null && rewards != null && rewards.Length != 0 && onPicked != null) + public RewardGump(TextDefinition title, IRewardEntry[] rewards, int points, RewardPickedHandler onPicked) + : base(250, 50) { - from.SendGump(new RewardGump(title, rewards, points, onPicked)); + Title = title; + Rewards = rewards; + Points = points; + OnPicked = onPicked; + + AddPage(0); + + AddImage(0, 0, 0x1F40); + AddImageTiled(20, 37, 300, 308, 0x1F42); + AddImage(20, 325, 0x1F43); + AddImage(35, 8, 0x39); + AddImageTiled(65, 8, 257, 10, 0x3A); + AddImage(290, 8, 0x3B); + AddImage(32, 33, 0x2635); + AddImageTiled(70, 55, 230, 2, 0x23C5); + + if (Title.String != null) + { + AddHtml(70, 35, 270, 20, Title.String); + } + else if (Title.Number != 0) + { + AddHtmlLocalized(70, 35, 270, 20, Title.Number, 1); + } + + AddHtmlLocalized(50, 65, 150, 20, 1072843, 1); // Your Reward Points: + AddLabel(230, 65, 0x64, Points.ToString()); + AddImageTiled(35, 85, 270, 2, 0x23C5); + AddHtmlLocalized(35, 90, 270, 20, 1072844, 1); // Please Choose a Reward: + + AddPage(1); + + var offset = 110; + var page = 1; + + for (var i = 0; i < Rewards.Length; ++i) + { + var entry = Rewards[i]; + + var bounds = ItemBounds.Table[entry.ItemID]; + var height = Math.Max(36, bounds.Height); + + if (offset + height > 320) + { + AddHtmlLocalized(240, 335, 60, 20, 1072854, 1); //
Next
+ AddButton(300, 335, 0x15E1, 0x15E5, 51, GumpButtonType.Page, page + 1); + + AddPage(++page); + + AddButton(150, 335, 0x15E3, 0x15E7, 52, GumpButtonType.Page, page - 1); + AddHtmlLocalized(170, 335, 60, 20, 1074880, 1); // Previous + + offset = 110; + } + + var available = entry.Price <= Points; + var half = offset + height / 2; + + if (available) + { + AddButton(35, half - 6, 0x837, 0x838, 100 + i); + } + + AddItem( + 83 - bounds.Width / 2 - bounds.X, + half - bounds.Height / 2 - bounds.Y, + entry.ItemID, + available ? entry.Hue : 995 + ); + + if (entry.Tooltip != 0) + { + AddTooltip(entry.Tooltip); + } + + AddLabel(133, half - 10, available ? 0x64 : 0x21, entry.Price.ToString()); + + if (entry.Description != null) + { + if (entry.Description.String != null) + { + AddHtml(190, offset, 114, height, entry.Description.String); + } + else if (entry.Description.Number != 0) + { + AddHtmlLocalized(190, offset, 114, height, entry.Description.Number, 1); + } + } + + offset += height + 10; + } + } + + public TextDefinition Title { get; } + + public IRewardEntry[] Rewards { get; } + + public int Points { get; } + + public RewardPickedHandler OnPicked { get; } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + var choice = info.ButtonID; + + if (choice == 0) + { + return; // Close + } + + choice -= 100; + + if (choice >= 0 && choice < Rewards.Length) + { + var entry = Rewards[choice]; + + if (entry.Price <= Points) + { + sender.Mobile.SendGump(new RewardConfirmGump(this, choice, entry)); + } + } } } - protected override void BuildLayout(ref DynamicGumpBuilder builder) + public class RewardConfirmGump : Gump { - builder.AddPage(); + private readonly int m_Index; + private readonly RewardGump m_Parent; - builder.AddImage(0, 0, 0x1F40); - builder.AddImageTiled(20, 37, 300, 308, 0x1F42); - builder.AddImage(20, 325, 0x1F43); - builder.AddImage(35, 8, 0x39); - builder.AddImageTiled(65, 8, 257, 10, 0x3A); - builder.AddImage(290, 8, 0x3B); - builder.AddImage(32, 33, 0x2635); - builder.AddImageTiled(70, 55, 230, 2, 0x23C5); - - _title.AddHtmlText(ref builder, 70, 35, 270, 20, numberColor: 1); - - builder.AddHtmlLocalized(50, 65, 150, 20, 1072843, 1); // Your Reward Points: - builder.AddLabel(230, 65, 0x64, $"{_points}"); - builder.AddImageTiled(35, 85, 270, 2, 0x23C5); - builder.AddHtmlLocalized(35, 90, 270, 20, 1072844, 1); // Please Choose a Reward: - - builder.AddPage(1); - - var offset = 110; - var page = 1; - - for (var i = 0; i < _rewards.Length; ++i) + public RewardConfirmGump(RewardGump parent, int index, IRewardEntry entry) + : base(120, 50) { - var entry = _rewards[i]; + m_Parent = parent; + m_Index = index; - var bounds = ItemBounds.Bounds[entry.ItemID]; - var height = Math.Max(36, bounds.Height); + Closable = false; - if (offset + height > 320) - { - builder.AddHtmlLocalized(240, 335, 60, 20, 1072854, 1); //
Next
- builder.AddButton(300, 335, 0x15E1, 0x15E5, 51, GumpButtonType.Page, page + 1); + AddPage(0); - builder.AddPage(++page); + AddImageTiled(0, 0, 348, 262, 0xA8E); + AddAlphaRegion(0, 0, 348, 262); + AddImage(0, 15, 0x27A8); + AddImageTiled(0, 30, 17, 200, 0x27A7); + AddImage(0, 230, 0x27AA); + AddImage(15, 0, 0x280C); + AddImageTiled(30, 0, 300, 17, 0x280A); + AddImage(315, 0, 0x280E); + AddImage(15, 244, 0x280C); + AddImageTiled(30, 244, 300, 17, 0x280A); + AddImage(315, 244, 0x280E); + AddImage(330, 15, 0x27A8); + AddImageTiled(330, 30, 17, 200, 0x27A7); + AddImage(330, 230, 0x27AA); + AddImage(333, 2, 0x2716); + AddImage(333, 248, 0x2716); + AddImage(2, 248, 0x2716); + AddImage(2, 2, 0x2716); - builder.AddButton(150, 335, 0x15E3, 0x15E7, 52, GumpButtonType.Page, page - 1); - builder.AddHtmlLocalized(170, 335, 60, 20, 1074880, 1); // Previous - - offset = 110; - } - - var available = entry.Price <= _points; - var half = offset + height / 2; - - if (available) - { - builder.AddButton(35, half - 6, 0x837, 0x838, 100 + i); - } - - builder.AddItem( - 83 - bounds.Width / 2 - bounds.X, - half - bounds.Height / 2 - bounds.Y, - entry.ItemID, - available ? entry.Hue : 995 - ); + AddItem(140, 120, entry.ItemID, entry.Hue); if (entry.Tooltip != 0) { - builder.AddTooltip(entry.Tooltip); + AddTooltip(entry.Tooltip); } - builder.AddLabel(133, half - 10, available ? 0x64 : 0x21, $"{entry.Price}"); - - if (entry.Description != null) - { - if (entry.Description.String != null) - { - builder.AddHtml(190, offset, 114, height, entry.Description.String); - } - else if (entry.Description.Number != 0) - { - builder.AddHtmlLocalized(190, offset, 114, height, entry.Description.Number, 1); - } - } - - offset += height + 10; + AddHtmlLocalized(25, 22, 200, 20, 1074974, 0x7D00); // Confirm Selection + AddImage(25, 40, 0xBBF); + AddHtmlLocalized(25, 55, 300, 120, 1074975, 0x7FFF); // Are you sure you wish to select this? + AddRadio(25, 175, 0x25F8, 0x25FB, true, 1); + AddRadio(25, 210, 0x25F8, 0x25FB, false, 0); + AddHtmlLocalized(60, 180, 280, 20, 1074976, 0x7FFF); // Yes + AddHtmlLocalized(60, 215, 280, 20, 1074977, 0x7FFF); // No + AddButton(265, 220, 0xF7, 0xF8, 7); } - } - public override void OnResponse(NetState sender, in RelayInfo info) - { - var choice = info.ButtonID - 100; - - if (choice >= 0 && choice < _rewards.Length) + public override void OnResponse(NetState sender, in RelayInfo info) { - var entry = _rewards[choice]; - - if (entry.Price <= _points) + if (info.ButtonID == 7 && info.IsSwitched(1)) { - RewardConfirmGump.DisplayTo(sender.Mobile, this, choice, entry); + m_Parent.OnPicked(sender.Mobile, m_Index); + } + else + { + sender.Mobile.SendGump(new RewardGump(m_Parent.Title, m_Parent.Rewards, m_Parent.Points, m_Parent.OnPicked)); } } } } - -public class RewardConfirmGump : DynamicGump -{ - private readonly int _index; - private readonly RewardGump _parent; - private readonly IRewardEntry _entry; - - public override bool Singleton => true; - - private RewardConfirmGump(RewardGump parent, int index, IRewardEntry entry) : base(120, 50) - { - _parent = parent; - _index = index; - _entry = entry; - } - - public static void DisplayTo(Mobile from, RewardGump parent, int index, IRewardEntry entry) - { - if (from?.NetState != null && parent != null && entry != null) - { - from.SendGump(new RewardConfirmGump(parent, index, entry)); - } - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); - - builder.AddPage(); - - builder.AddImageTiled(0, 0, 348, 262, 0xA8E); - builder.AddAlphaRegion(0, 0, 348, 262); - builder.AddImage(0, 15, 0x27A8); - builder.AddImageTiled(0, 30, 17, 200, 0x27A7); - builder.AddImage(0, 230, 0x27AA); - builder.AddImage(15, 0, 0x280C); - builder.AddImageTiled(30, 0, 300, 17, 0x280A); - builder.AddImage(315, 0, 0x280E); - builder.AddImage(15, 244, 0x280C); - builder.AddImageTiled(30, 244, 300, 17, 0x280A); - builder.AddImage(315, 244, 0x280E); - builder.AddImage(330, 15, 0x27A8); - builder.AddImageTiled(330, 30, 17, 200, 0x27A7); - builder.AddImage(330, 230, 0x27AA); - builder.AddImage(333, 2, 0x2716); - builder.AddImage(333, 248, 0x2716); - builder.AddImage(2, 248, 0x2716); - builder.AddImage(2, 2, 0x2716); - - builder.AddItem(140, 120, _entry.ItemID, _entry.Hue); - - if (_entry.Tooltip != 0) - { - builder.AddTooltip(_entry.Tooltip); - } - - builder.AddHtmlLocalized(25, 22, 200, 20, 1074974, 0x7D00); // Confirm Selection - builder.AddImage(25, 40, 0xBBF); - builder.AddHtmlLocalized(25, 55, 300, 120, 1074975, 0x7FFF); // Are you sure you wish to select this? - builder.AddRadio(25, 175, 0x25F8, 0x25FB, true, 1); - builder.AddRadio(25, 210, 0x25F8, 0x25FB, false, 0); - builder.AddHtmlLocalized(60, 180, 280, 20, 1074976, 0x7FFF); // Yes - builder.AddHtmlLocalized(60, 215, 280, 20, 1074977, 0x7FFF); // No - builder.AddButton(265, 220, 0xF7, 0xF8, 7); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID == 7 && info.IsSwitched(1)) - { - _parent.OnPicked(sender.Mobile, _index); - } - else - { - RewardGump.DisplayTo(sender.Mobile, _parent.Title, _parent.Rewards, _parent.Points, _parent.OnPicked); - } - } -} diff --git a/Projects/UOContent/Gumps/SelectAddonDirectionGump.cs b/Projects/UOContent/Gumps/SelectAddonDirectionGump.cs deleted file mode 100644 index 3ef6164f2..000000000 --- a/Projects/UOContent/Gumps/SelectAddonDirectionGump.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Server.Items; -using Server.Network; - -namespace Server.Gumps; - -public abstract class SelectAddonDirectionGump : StaticGump where T : SelectAddonDirectionGump -{ - private readonly IDirectionAddonDeed _deed; - - public SelectAddonDirectionGump(IDirectionAddonDeed deed) : base(60, 63) => _deed = deed; - - public override bool Singleton => false; - - public abstract int SelectionNumber { get; } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 273, 324, 0x13BE); - builder.AddImageTiled(10, 10, 253, 20, 0xA40); - builder.AddImageTiled(10, 40, 253, 244, 0xA40); - builder.AddImageTiled(10, 294, 253, 20, 0xA40); - builder.AddAlphaRegion(10, 10, 253, 304); - - builder.AddButton(10, 294, 0xFB1, 0xFB2, 0); - - builder.AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL - builder.AddHtmlLocalized(14, 12, 273, 20, SelectionNumber, 0x7FFF); - - builder.AddPage(1); - - builder.AddButton(19, 49, 0x845, 0x846, 1); - builder.AddHtmlLocalized(44, 47, 213, 20, 1075386, 0x7FFF); // South - builder.AddButton(19, 73, 0x845, 0x846, 2); - builder.AddHtmlLocalized(44, 71, 213, 20, 1075387, 0x7FFF); // East - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (_deed?.Deleted != false || info.ButtonID == 0) - { - return; - } - - _deed.East = info.ButtonID != 1; - _deed.SendTarget(sender.Mobile); - } -} diff --git a/Projects/UOContent/Gumps/SkillsGump.cs b/Projects/UOContent/Gumps/SkillsGump.cs index d8efbb5e4..c152a1e82 100644 --- a/Projects/UOContent/Gumps/SkillsGump.cs +++ b/Projects/UOContent/Gumps/SkillsGump.cs @@ -9,9 +9,13 @@ namespace Server.Gumps { public class EditSkillGump : Gump { - private const int EntryWidth = 160; + private static readonly int EntryWidth = 160; - private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + 2 * (EntryHeight + OffsetSize); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; private readonly Mobile m_From; @@ -33,10 +37,40 @@ namespace Server.Gumps AddPage(0); - this.AddPropsFrame(TotalWidth, 2, out var x, out var y); - this.AddPropsEntryLabel(ref x, ref y, EntryWidth, skill.Name); - PropsLayout.NextRow(ref x, ref y); - this.AddPropsEntryTextInput(ref x, ref y, EntryWidth, 0, initialText, true, 1); + AddBackground(0, 0, BackWidth, BackHeight, BackGumpID); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + TotalHeight, + OffsetGumpID + ); + + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, skill.Name); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + AddTextEntry(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, 0, initialText); + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1); } public override void OnResponse(NetState sender, in RelayInfo info) @@ -77,10 +111,24 @@ namespace Server.Gumps public class SkillsGump : Gump { - private const int NameWidth = 107; - private const int ValueWidth = 128; + /* + private static bool PrevLabel = OldStyle, NextLabel = OldStyle; - private const int TotalWidth = OffsetSize + NameWidth + OffsetSize + ValueWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int PrevLabelOffsetX = PrevWidth + 1; + + private static readonly int PrevLabelOffsetY = 0; + + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; + * */ + + private static readonly int NameWidth = 107; + private static readonly int ValueWidth = 128; + + private static readonly int TotalWidth = + OffsetSize + NameWidth + OffsetSize + ValueWidth + OffsetSize + SetWidth + OffsetSize; + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; private static readonly int IndentWidth = 12; @@ -105,16 +153,52 @@ namespace Server.Gumps count += selected.Skills.Length; } + var totalHeight = OffsetSize + (EntryHeight + OffsetSize) * (count + 1); + AddPage(0); - this.AddPropsFrame(TotalWidth, count + 1, out var x, out var y); - this.AddPropsHeader( - TotalWidth, ref x, ref y, - $"{target.GetType().Name} ({target.Serial})".Center(0xFAFAFA), - false, 0, false, 0 + AddBackground(0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID); + AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + totalHeight, + OffsetGumpID ); - const int emptyWidth = TotalWidth - PrevWidth - NextWidth - OffsetSize * 4; + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; + + var emptyWidth = TotalWidth - PrevWidth - NextWidth - OffsetSize * 4 - (OldStyle ? SetWidth + OffsetSize : 0); + + if (OldStyle) + { + AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID); + } + else + { + AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID); + } + + x += PrevWidth + OffsetSize; + + if (!OldStyle) + { + AddImageTiled( + x - (OldStyle ? OffsetSize : 0), + y, + emptyWidth + (OldStyle ? OffsetSize * 2 : 0), + EntryHeight, + HeaderGumpID + ); + } + + x += emptyWidth + OffsetSize; + + if (!OldStyle) + { + AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID); + } for (var i = 0; i < m_Groups.Length; ++i) { @@ -136,10 +220,12 @@ namespace Server.Gumps x += PrevWidth + OffsetSize; - AddImageTiled(x, y, emptyWidth, EntryHeight, EntryGumpID); + x -= OldStyle ? OffsetSize : 0; + + AddImageTiled(x, y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, EntryGumpID); AddLabel(x + TextOffsetX, y, TextHue, group?.Name ?? ""); - x += emptyWidth; + x += emptyWidth + (OldStyle ? OffsetSize * 2 : 0); x += OffsetSize; if (SetGumpID != 0) @@ -152,6 +238,7 @@ namespace Server.Gumps continue; } + var indentMaskX = BorderSize; var indentMaskY = y + EntryHeight + OffsetSize; for (var j = 0; j < group!.Skills.Length; ++j) @@ -170,10 +257,18 @@ namespace Server.Gumps x += PrevWidth + OffsetSize; - AddImageTiled(x, y, emptyWidth - OffsetSize - IndentWidth, EntryHeight, EntryGumpID); + x -= OldStyle ? OffsetSize : 0; + + AddImageTiled( + x, + y, + emptyWidth + (OldStyle ? OffsetSize * 2 : 0) - OffsetSize - IndentWidth, + EntryHeight, + EntryGumpID + ); AddLabel(x + TextOffsetX, y, TextHue, sk == null ? "(null)" : sk.Name); - x += emptyWidth - OffsetSize - IndentWidth; + x += emptyWidth + (OldStyle ? OffsetSize * 2 : 0) - OffsetSize - IndentWidth; x += OffsetSize; if (SetGumpID != 0) @@ -189,29 +284,23 @@ namespace Server.Gumps switch (sk.Lock) { default: - { - buttonID1 = 0x983; - buttonID2 = 0x983; - xOffset = 6; - yOffset = 4; - break; - } + buttonID1 = 0x983; + buttonID2 = 0x983; + xOffset = 6; + yOffset = 4; + break; case SkillLock.Down: - { - buttonID1 = 0x985; - buttonID2 = 0x985; - xOffset = 6; - yOffset = 4; - break; - } + buttonID1 = 0x985; + buttonID2 = 0x985; + xOffset = 6; + yOffset = 4; + break; case SkillLock.Locked: - { - buttonID1 = 0x82C; - buttonID2 = 0x82C; - xOffset = 5; - yOffset = 2; - break; - } + buttonID1 = 0x82C; + buttonID2 = 0x82C; + xOffset = 5; + yOffset = 2; + break; } AddButton(x + xOffset, y + yOffset, buttonID1, buttonID2, GetButtonID(2, j)); @@ -242,7 +331,7 @@ namespace Server.Gumps } AddImageTiled( - BorderSize, + indentMaskX, indentMaskY, IndentWidth + OffsetSize, group.Skills.Length * (EntryHeight + OffsetSize) - (i < m_Groups.Length - 1 ? OffsetSize : 0), @@ -317,23 +406,17 @@ namespace Server.Gumps switch (sk.Lock) { case SkillLock.Up: - { - sk.SetLockNoRelay(SkillLock.Down); - sk.Update(); - break; - } + sk.SetLockNoRelay(SkillLock.Down); + sk.Update(); + break; case SkillLock.Down: - { - sk.SetLockNoRelay(SkillLock.Locked); - sk.Update(); - break; - } + sk.SetLockNoRelay(SkillLock.Locked); + sk.Update(); + break; case SkillLock.Locked: - { - sk.SetLockNoRelay(SkillLock.Up); - sk.Update(); - break; - } + sk.SetLockNoRelay(SkillLock.Up); + sk.Update(); + break; } } else @@ -368,10 +451,11 @@ namespace Server.Gumps public SkillName[] Skills { get; } public static SkillsGumpGroup[] Groups { get; } = - [ + { new( "Crafting", - [ + new[] + { SkillName.Alchemy, SkillName.Blacksmith, SkillName.Cartography, @@ -382,20 +466,22 @@ namespace Server.Gumps SkillName.Tailoring, SkillName.Tinkering, SkillName.Imbuing - ] + } ), new( "Bardic", - [ + new[] + { SkillName.Discordance, SkillName.Musicianship, SkillName.Peacemaking, SkillName.Provocation - ] + } ), new( "Magical", - [ + new[] + { SkillName.Chivalry, SkillName.EvalInt, SkillName.Magery, @@ -407,11 +493,12 @@ namespace Server.Gumps SkillName.Bushido, SkillName.Spellweaving, SkillName.Mysticism - ] + } ), new( "Miscellaneous", - [ + new[] + { SkillName.Camping, SkillName.Fishing, SkillName.Focus, @@ -422,11 +509,12 @@ namespace Server.Gumps SkillName.Mining, SkillName.Snooping, SkillName.Veterinary - ] + } ), new( "Combat Ratings", - [ + new[] + { SkillName.Archery, SkillName.Fencing, SkillName.Macing, @@ -435,11 +523,12 @@ namespace Server.Gumps SkillName.Tactics, SkillName.Wrestling, SkillName.Throwing - ] + } ), new( "Actions", - [ + new[] + { SkillName.AnimalTaming, SkillName.Begging, SkillName.DetectHidden, @@ -449,20 +538,21 @@ namespace Server.Gumps SkillName.Stealing, SkillName.Stealth, SkillName.Tracking - ] + } ), new( "Lore & Knowledge", - [ + new[] + { SkillName.Anatomy, SkillName.AnimalLore, SkillName.ArmsLore, SkillName.Forensics, SkillName.ItemID, SkillName.TasteID - ] + } ) - ]; + }; private class SkillNameComparer : IComparer { diff --git a/Projects/UOContent/Gumps/StaticWarningGump.cs b/Projects/UOContent/Gumps/StaticWarningGump.cs index ee5fc020e..ef98d4adc 100644 --- a/Projects/UOContent/Gumps/StaticWarningGump.cs +++ b/Projects/UOContent/Gumps/StaticWarningGump.cs @@ -7,14 +7,13 @@ public abstract class StaticWarningGump : StaticGump where T : StaticWarni { public virtual int Header => 1060635; //
WARNING
public virtual int HeaderColor => 0x7800; - public virtual string ContentColor => "#FFC000"; + public virtual int ContentColor => StaticLocalizedContent > 0 ? 0x7F00 : 0xFFC000; public abstract int Width { get; } public abstract int Height { get; } public virtual bool CancelButton => true; // If this is overridden, then the content will be localized and cached. public virtual int StaticLocalizedContent => 0; - public virtual int StaticLocalizedContentColor => 0x7F00; public virtual string Content => null; @@ -56,7 +55,7 @@ public abstract class StaticWarningGump : StaticGump where T : StaticWarni width - 20, height - 80, StaticLocalizedContent, - StaticLocalizedContentColor, + (short)ContentColor, false, true ); @@ -89,7 +88,7 @@ public abstract class StaticWarningGump : StaticGump where T : StaticWarni protected sealed override void BuildStrings(ref GumpStringsBuilder builder) { - builder.SetHtmlText("content", Content, ContentColor); + builder.SetStringSlot("content", Content.Color(ContentColor)); } public override void OnResponse(NetState sender, in RelayInfo info) => _callback?.Invoke(info.ButtonID == 1); diff --git a/Projects/UOContent/Gumps/TithingGump.cs b/Projects/UOContent/Gumps/TithingGump.cs index b68a4a150..c917ccff2 100644 --- a/Projects/UOContent/Gumps/TithingGump.cs +++ b/Projects/UOContent/Gumps/TithingGump.cs @@ -56,8 +56,8 @@ public class TithingGump : StaticGump // Just in case _offer = Math.Clamp(_offer, 0, totalGold); - builder.SetStringSlot("goldOffer", $"{totalGold - _offer:N0}"); - builder.SetStringSlot("titheOffer", $"{_offer:N0}"); + builder.SetStringSlot("goldOffer", (totalGold - _offer).ToString("N0")); + builder.SetStringSlot("titheOffer", _offer.ToString("N0")); } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Gumps/ToTAdminGump.cs b/Projects/UOContent/Gumps/ToTAdminGump.cs index 6efbf2650..10ea5b6b0 100644 --- a/Projects/UOContent/Gumps/ToTAdminGump.cs +++ b/Projects/UOContent/Gumps/ToTAdminGump.cs @@ -7,7 +7,7 @@ namespace Server.Gumps public class ToTAdminGump : Gump { private static readonly string[] m_ToTInfo = - [ + { // Opening Message "
Treasures of Tokuno Admin

" + "-Use the gems to switch eras
" + @@ -27,7 +27,7 @@ namespace Server.Gumps "-10 types of 1 charge Fresh Pigments drop as Lesser Artifacts
" + "-1 charge Bleach Pigment can drop as a Lesser Artifact
" + "-Leurocian's Mempo Of Fortune can drop as a Lesser Artifact" - ]; + }; private readonly int m_ToTEras; diff --git a/Projects/UOContent/Gumps/VendorInventoryGump.cs b/Projects/UOContent/Gumps/VendorInventoryGump.cs index fe2bfaa08..a8fb83a22 100644 --- a/Projects/UOContent/Gumps/VendorInventoryGump.cs +++ b/Projects/UOContent/Gumps/VendorInventoryGump.cs @@ -1,148 +1,141 @@ +using System.Collections.Generic; +using System.Linq; using Server.Mobiles; using Server.Multis; using Server.Network; -namespace Server.Gumps; - -public class VendorInventoryGump : DynamicGump +namespace Server.Gumps { - private readonly BaseHouse _house; - private readonly Mobile _from; - private readonly VendorInventory[] _inventories; - - public override bool Singleton => true; - - private VendorInventoryGump(BaseHouse house, Mobile from) : base(50, 50) + public class VendorInventoryGump : Gump { - _house = house; - _from = from; - _inventories = house.VendorInventories.ToArray(); - } + private readonly BaseHouse m_House; + private readonly List m_Inventories; - public static void DisplayTo(Mobile from, BaseHouse house) - { - if (from?.NetState != null && house?.Deleted == false && house.VendorInventories.Count != 0) + public override bool Singleton => true; + + public VendorInventoryGump(BaseHouse house, Mobile from) : base(50, 50) { - from.SendGump(new VendorInventoryGump(house, from)); - } - } + m_House = house; + m_Inventories = house.VendorInventories.ToList(); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddBackground(0, 0, 420, 50 + 20 * m_Inventories.Count, 0x13BE); - builder.AddBackground(0, 0, 420, 50 + 20 * _inventories.Length, 0x13BE); + AddImageTiled(10, 10, 400, 20, 0xA40); + AddHtmlLocalized(15, 10, 200, 20, 1062435, 0x7FFF); // Reclaim Vendor Inventory + AddHtmlLocalized(330, 10, 50, 20, 1062465, 0x7FFF); // Expires - builder.AddImageTiled(10, 10, 400, 20, 0xA40); - builder.AddHtmlLocalized(15, 10, 200, 20, 1062435, 0x7FFF); // Reclaim Vendor Inventory - builder.AddHtmlLocalized(330, 10, 50, 20, 1062465, 0x7FFF); // Expires + AddImageTiled(10, 40, 400, 20 * m_Inventories.Count, 0xA40); - builder.AddImageTiled(10, 40, 400, 20 * _inventories.Length, 0xA40); - - for (var i = 0; i < _inventories.Length; i++) - { - var inventory = _inventories[i]; - - var y = 40 + 20 * i; - - if (inventory.Owner == _from) + for (var i = 0; i < m_Inventories.Count; i++) { - builder.AddButton(10, y, 0xFA5, 0xFA7, i + 1); - } + var inventory = m_Inventories[i]; - builder.AddLabel(45, y, 0x481, $"{inventory.ShopName} ({inventory.VendorName})"); + var y = 40 + 20 * i; - var expire = inventory.ExpireTime - Core.Now; - var hours = (int)expire.TotalHours; + if (inventory.Owner == from) + { + AddButton(10, y, 0xFA5, 0xFA7, i + 1); + } - builder.AddLabel(320, y, 0x481, $"{hours}"); - builder.AddHtmlLocalized(350, y, 50, 20, 1062466, 0x7FFF); // hour(s) - } - } + AddLabel(45, y, 0x481, $"{inventory.ShopName} ({inventory.VendorName})"); - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID == 0) - { - return; - } + var expire = inventory.ExpireTime - Core.Now; + var hours = (int)expire.TotalHours; - var from = sender.Mobile; - var sign = _house.Sign; - - if (_house.Deleted || sign?.Deleted != false || !from.CheckAlive()) - { - return; - } - - if (from.Map != sign.Map || !from.InRange(sign, 5)) - { - from.SendLocalizedMessage(1062429); // You must be within five paces of the house sign to use this option. - return; - } - - var index = info.ButtonID - 1; - if (index < 0 || index >= _inventories.Length) - { - return; - } - - var inventory = _inventories[index]; - - if (inventory.Owner != from || !_house.VendorInventories.Contains(inventory)) - { - return; - } - - var totalItems = 0; - var givenToBackpack = 0; - var givenToBankBox = 0; - for (var i = inventory.Items.Count - 1; i >= 0; i--) - { - var item = inventory.Items[i]; - - if (item.Deleted) - { - inventory.Items.RemoveAt(i); - continue; - } - - totalItems += 1 + item.TotalItems; - - if (from.PlaceInBackpack(item)) - { - inventory.Items.RemoveAt(i); - givenToBackpack += 1 + item.TotalItems; - } - else if (from.BankBox.TryDropItem(from, item, false)) - { - inventory.Items.RemoveAt(i); - givenToBankBox += 1 + item.TotalItems; + AddLabel(320, y, 0x481, hours.ToString()); + AddHtmlLocalized(350, y, 50, 20, 1062466, 0x7FFF); // hour(s) } } - // The vendor you selected had ~1_COUNT~ items in its inventory, and ~2_AMOUNT~ gold in its account. - from.SendLocalizedMessage(1062436, $"{totalItems}\t{inventory.Gold}"); - - var givenGold = Banker.DepositUpTo(from, inventory.Gold); - inventory.Gold -= givenGold; - - // ~1_AMOUNT~ gold has been deposited into your bank box. - from.SendLocalizedMessage(1060397, givenGold.ToString()); - - // ~1_COUNT~ items have been removed from the shop inventory and placed in your backpack. - // ~2_BANKCOUNT~ items were removed from the shop inventory and placed in your bank box. - from.SendLocalizedMessage(1062437, $"{givenToBackpack}\t{givenToBankBox}"); - - if (inventory.Gold > 0 || inventory.Items.Count > 0) + public override void OnResponse(NetState sender, in RelayInfo info) { - // Some of the shop inventory would not fit in your backpack or bank box. Please free up some room and try again. - from.SendLocalizedMessage(1062440); - } - else - { - inventory.Delete(); - from.SendLocalizedMessage(1062438); // The shop is now empty of inventory and funds, so it has been deleted. + if (info.ButtonID == 0) + { + return; + } + + var from = sender.Mobile; + var sign = m_House.Sign; + + if (m_House.Deleted || sign?.Deleted != false || !from.CheckAlive()) + { + return; + } + + if (from.Map != sign.Map || !from.InRange(sign, 5)) + { + from.SendLocalizedMessage(1062429); // You must be within five paces of the house sign to use this option. + return; + } + + var index = info.ButtonID - 1; + if (index < 0 || index >= m_Inventories.Count) + { + return; + } + + var inventory = m_Inventories[index]; + + if (inventory.Owner != from || !m_House.VendorInventories.Contains(inventory)) + { + return; + } + + var totalItems = 0; + var givenToBackpack = 0; + var givenToBankBox = 0; + for (var i = inventory.Items.Count - 1; i >= 0; i--) + { + var item = inventory.Items[i]; + + if (item.Deleted) + { + inventory.Items.RemoveAt(i); + continue; + } + + totalItems += 1 + item.TotalItems; + + if (from.PlaceInBackpack(item)) + { + inventory.Items.RemoveAt(i); + givenToBackpack += 1 + item.TotalItems; + } + else if (from.BankBox.TryDropItem(from, item, false)) + { + inventory.Items.RemoveAt(i); + givenToBankBox += 1 + item.TotalItems; + } + } + + from.SendLocalizedMessage( + 1062436, + $"{totalItems}\t{inventory.Gold}" + ); // The vendor you selected had ~1_COUNT~ items in its inventory, and ~2_AMOUNT~ gold in its account. + + var givenGold = Banker.DepositUpTo(from, inventory.Gold); + inventory.Gold -= givenGold; + + from.SendLocalizedMessage( + 1060397, + givenGold.ToString() + ); // ~1_AMOUNT~ gold has been deposited into your bank box. + from.SendLocalizedMessage( + 1062437, + $"{givenToBackpack}\t{givenToBankBox}" + ); // ~1_COUNT~ items have been removed from the shop inventory and placed in your backpack. ~2_BANKCOUNT~ items were removed from the shop inventory and placed in your bank box. + + if (inventory.Gold > 0 || inventory.Items.Count > 0) + { + from.SendLocalizedMessage( + 1062440 + ); // Some of the shop inventory would not fit in your backpack or bank box. Please free up some room and try again. + } + else + { + inventory.Delete(); + from.SendLocalizedMessage(1062438); // The shop is now empty of inventory and funds, so it has been deleted. + } } } } diff --git a/Projects/UOContent/Gumps/VendorRentalGumps.cs b/Projects/UOContent/Gumps/VendorRentalGumps.cs index 4291cf7d3..589cdb60c 100644 --- a/Projects/UOContent/Gumps/VendorRentalGumps.cs +++ b/Projects/UOContent/Gumps/VendorRentalGumps.cs @@ -1,4 +1,3 @@ -using System; using Server.Items; using Server.Mobiles; using Server.Multis; @@ -6,756 +5,689 @@ using Server.Network; using Server.Prompts; using Server.Targeting; -namespace Server.Gumps; - -public abstract class BaseVendorRentalGump : DynamicGump +namespace Server.Gumps { - private readonly GumpType _type; - private readonly VendorRentalDuration _duration; - private readonly int _price; - private readonly int _renewalPrice; - private readonly Mobile _landlord; - private readonly Mobile _renter; - private readonly bool _landlordRenew; - private readonly bool _renterRenew; - private readonly bool _renew; - - protected BaseVendorRentalGump( - GumpType type, VendorRentalDuration duration, int price, int renewalPrice, - Mobile landlord, Mobile renter, bool landlordRenew, bool renterRenew, bool renew - ) : base(100, 100) + public abstract class BaseVendorRentalGump : Gump { - _type = type; - _duration = duration; - _price = price; - _renewalPrice = renewalPrice; - _landlord = landlord; - _renter = renter; - _landlordRenew = landlordRenew; - _renterRenew = renterRenew; - _renew = renew; - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - if (_type == GumpType.Offer) + protected BaseVendorRentalGump( + GumpType type, VendorRentalDuration duration, int price, int renewalPrice, + Mobile landlord, Mobile renter, bool landlordRenew, bool renterRenew, bool renew + ) : base(100, 100) { - builder.SetNoClose(); - } - - builder.AddPage(); - - builder.AddImage(0, 0, 0x1F40); - builder.AddImageTiled(20, 37, 300, 308, 0x1F42); - builder.AddImage(20, 325, 0x1F43); - - builder.AddImage(35, 8, 0x39); - builder.AddImageTiled(65, 8, 257, 10, 0x3A); - builder.AddImage(290, 8, 0x3B); - - builder.AddImageTiled(70, 55, 230, 2, 0x23C5); - - builder.AddImage(32, 33, 0x2635); - builder.AddHtmlLocalized(70, 35, 270, 20, 1062353, 0x1); // Vendor Rental Contract - - builder.AddPage(1); - - if (_type != GumpType.UnlockedContract) - { - builder.AddImage(65, 60, 0x827); - builder.AddHtmlLocalized(79, 58, 270, 20, 1062370, 0x1); // Landlord: - builder.AddLabel(150, 58, 0x64, _landlord?.Name ?? ""); - - builder.AddImageTiled(70, 80, 230, 2, 0x23C5); - } - - if (_type is GumpType.UnlockedContract or GumpType.LockedContract) - { - builder.AddButton(30, 96, 0x15E1, 0x15E5, 0, GumpButtonType.Page, 2); - } - - builder.AddHtmlLocalized(50, 95, 150, 20, 1062354, 0x1); // Contract Length - builder.AddHtmlLocalized(230, 95, 270, 20, _duration.Name, 0x1); - - if (_type is GumpType.UnlockedContract or GumpType.LockedContract) - { - builder.AddButton(30, 116, 0x15E1, 0x15E5, 1); - } - - builder.AddHtmlLocalized(50, 115, 150, 20, 1062356, 0x1); // Price Per Rental - if (_price > 0) - { - builder.AddLabel(230, 115, 0x64, $"{_price}"); - } - else - { - builder.AddLabel(230, 115, 0x64, "FREE"); - } - - builder.AddImageTiled(50, 160, 250, 2, 0x23BF); - - if (_type == GumpType.Offer) - { - builder.AddButton(67, 180, 0x482, 0x483, 2); - builder.AddHtmlLocalized(100, 180, 270, 20, 1049011, 0x28); // I accept! - - builder.AddButton(67, 210, 0x47F, 0x480, 0); - builder.AddHtmlLocalized(100, 210, 270, 20, 1049012, 0x28); // No thanks, I decline. - } - else - { - builder.AddImage(49, 170, 0x61); - builder.AddHtmlLocalized(60, 170, 250, 20, 1062355, 0x1); // Renew On Expiration? - - if (_type is GumpType.LockedContract or GumpType.UnlockedContract or GumpType.VendorLandlord) + if (type == GumpType.Offer) { - builder.AddButton(30, 192, 0x15E1, 0x15E5, 3); + Closable = false; } - builder.AddHtmlLocalized(85, 190, 250, 20, 1062359, 0x1); // Landlord: - builder.AddHtmlLocalized(230, 190, 270, 20, _landlordRenew ? 1049717 : 1049718, 0x1); // YES / NO + AddPage(0); - if (_type == GumpType.VendorRenter) + AddImage(0, 0, 0x1F40); + AddImageTiled(20, 37, 300, 308, 0x1F42); + AddImage(20, 325, 0x1F43); + + AddImage(35, 8, 0x39); + AddImageTiled(65, 8, 257, 10, 0x3A); + AddImage(290, 8, 0x3B); + + AddImageTiled(70, 55, 230, 2, 0x23C5); + + AddImage(32, 33, 0x2635); + AddHtmlLocalized(70, 35, 270, 20, 1062353, 0x1); // Vendor Rental Contract + + AddPage(1); + + if (type != GumpType.UnlockedContract) { - builder.AddButton(30, 212, 0x15E1, 0x15E5, 4); + AddImage(65, 60, 0x827); + AddHtmlLocalized(79, 58, 270, 20, 1062370, 0x1); // Landlord: + AddLabel(150, 58, 0x64, landlord != null ? landlord.Name : ""); + + AddImageTiled(70, 80, 230, 2, 0x23C5); } - builder.AddHtmlLocalized(85, 210, 250, 20, 1062360, 0x1); // Renter: - builder.AddHtmlLocalized(230, 210, 270, 20, _renterRenew ? 1049717 : 1049718, 0x1); // YES / NO - - if (_renew) + if (type is GumpType.UnlockedContract or GumpType.LockedContract) { - builder.AddImage(49, 233, 0x939); - builder.AddHtmlLocalized(70, 230, 250, 20, 1062482, 0x1); // Contract WILL renew + AddButton(30, 96, 0x15E1, 0x15E5, 0, GumpButtonType.Page, 2); + } + + AddHtmlLocalized(50, 95, 150, 20, 1062354, 0x1); // Contract Length + AddHtmlLocalized(230, 95, 270, 20, duration.Name, 0x1); + + if (type is GumpType.UnlockedContract or GumpType.LockedContract) + { + AddButton(30, 116, 0x15E1, 0x15E5, 1); + } + + AddHtmlLocalized(50, 115, 150, 20, 1062356, 0x1); // Price Per Rental + AddLabel(230, 115, 0x64, price > 0 ? price.ToString() : "FREE"); + + AddImageTiled(50, 160, 250, 2, 0x23BF); + + if (type == GumpType.Offer) + { + AddButton(67, 180, 0x482, 0x483, 2); + AddHtmlLocalized(100, 180, 270, 20, 1049011, 0x28); // I accept! + + AddButton(67, 210, 0x47F, 0x480, 0); + AddHtmlLocalized(100, 210, 270, 20, 1049012, 0x28); // No thanks, I decline. } else { - builder.AddImage(49, 233, 0x938); - builder.AddHtmlLocalized(70, 230, 250, 20, 1062483, 0x1); // Contract WILL NOT renew + AddImage(49, 170, 0x61); + AddHtmlLocalized(60, 170, 250, 20, 1062355, 0x1); // Renew On Expiration? + + if (type is GumpType.LockedContract or GumpType.UnlockedContract or GumpType.VendorLandlord) + { + AddButton(30, 192, 0x15E1, 0x15E5, 3); + } + + AddHtmlLocalized(85, 190, 250, 20, 1062359, 0x1); // Landlord: + AddHtmlLocalized(230, 190, 270, 20, landlordRenew ? 1049717 : 1049718, 0x1); // YES / NO + + if (type == GumpType.VendorRenter) + { + AddButton(30, 212, 0x15E1, 0x15E5, 4); + } + + AddHtmlLocalized(85, 210, 250, 20, 1062360, 0x1); // Renter: + AddHtmlLocalized(230, 210, 270, 20, renterRenew ? 1049717 : 1049718, 0x1); // YES / NO + + if (renew) + { + AddImage(49, 233, 0x939); + AddHtmlLocalized(70, 230, 250, 20, 1062482, 0x1); // Contract WILL renew + } + else + { + AddImage(49, 233, 0x938); + AddHtmlLocalized(70, 230, 250, 20, 1062483, 0x1); // Contract WILL NOT renew + } } - } - builder.AddImageTiled(30, 283, 257, 30, 0x5D); - builder.AddImage(285, 283, 0x5E); - builder.AddImage(20, 288, 0x232C); + AddImageTiled(30, 283, 257, 30, 0x5D); + AddImage(285, 283, 0x5E); + AddImage(20, 288, 0x232C); - if (_type == GumpType.LockedContract) - { - builder.AddButton(67, 295, 0x15E1, 0x15E5, 5); - builder.AddHtmlLocalized(85, 294, 270, 20, 1062358, 0x28); // Offer Contract To Someone - } - else if (_type is GumpType.VendorLandlord or GumpType.VendorRenter) - { - if (_type == GumpType.VendorLandlord) + if (type == GumpType.LockedContract) { - builder.AddButton(30, 250, 0x15E1, 0x15E1, 6); + AddButton(67, 295, 0x15E1, 0x15E5, 5); + AddHtmlLocalized(85, 294, 270, 20, 1062358, 0x28); // Offer Contract To Someone } - - builder.AddHtmlLocalized(85, 250, 250, 20, 1062499, 0x1); // Renewal Price - builder.AddLabel(230, 250, 0x64, $"{_renewalPrice}"); - - builder.AddHtmlLocalized(60, 294, 270, 20, 1062369, 0x1); // Renter: - builder.AddLabel(120, 293, 0x64, _renter != null ? _renter.Name : ""); - } - - if (_type is GumpType.UnlockedContract or GumpType.LockedContract) - { - builder.AddPage(2); - - for (var i = 0; i < VendorRentalDuration.Instances.Length; i++) + else if (type is GumpType.VendorLandlord or GumpType.VendorRenter) { - var durationItem = VendorRentalDuration.Instances[i]; + if (type == GumpType.VendorLandlord) + { + AddButton(30, 250, 0x15E1, 0x15E1, 6); + } - builder.AddButton(30, 76 + i * 20, 0x15E1, 0x15E5, 0x10 | i, GumpButtonType.Reply, 1); - builder.AddHtmlLocalized(50, 75 + i * 20, 150, 20, durationItem.Name, 0x1); + AddHtmlLocalized(85, 250, 250, 20, 1062499, 0x1); // Renewal Price + AddLabel(230, 250, 0x64, renewalPrice.ToString()); + + AddHtmlLocalized(60, 294, 270, 20, 1062369, 0x1); // Renter: + AddLabel(120, 293, 0x64, renter != null ? renter.Name : ""); } - } - } - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; - - if (!IsValidResponse(from)) - { - return; - } - - if ((info.ButtonID & 0x10) != 0) // Contract duration - { - var index = info.ButtonID & 0xF; - - if (index < VendorRentalDuration.Instances.Length) + if (type is GumpType.UnlockedContract or GumpType.LockedContract) { - SetContractDuration(from, VendorRentalDuration.Instances[index]); + AddPage(2); + + for (var i = 0; i < VendorRentalDuration.Instances.Length; i++) + { + var durationItem = VendorRentalDuration.Instances[i]; + + AddButton(30, 76 + i * 20, 0x15E1, 0x15E5, 0x10 | i, GumpButtonType.Reply, 1); + AddHtmlLocalized(50, 75 + i * 20, 150, 20, durationItem.Name, 0x1); + } } - - return; } - switch (info.ButtonID) + public override void OnResponse(NetState sender, in RelayInfo info) { - case 1: // Price Per Rental - { - SetPricePerRental(from); - break; - } + var from = sender.Mobile; - case 2: // Accept offer - { - AcceptOffer(from); - break; - } - - case 3: // Renew on expiration - landlord - { - LandlordRenewOnExpiration(from); - break; - } - - case 4: // Renew on expiration - renter - { - RenterRenewOnExpiration(from); - break; - } - - case 5: // Offer Contract To Someone - { - OfferContract(from); - break; - } - - case 6: // Renewal price - { - SetRenewalPrice(from); - break; - } - - default: - { - Cancel(from); - break; - } - } - } - - protected abstract bool IsValidResponse(Mobile from); - - protected virtual void SetContractDuration(Mobile from, VendorRentalDuration duration) - { - } - - protected virtual void SetPricePerRental(Mobile from) - { - } - - protected virtual void AcceptOffer(Mobile from) - { - } - - protected virtual void LandlordRenewOnExpiration(Mobile from) - { - } - - protected virtual void RenterRenewOnExpiration(Mobile from) - { - } - - protected virtual void OfferContract(Mobile from) - { - } - - protected virtual void SetRenewalPrice(Mobile from) - { - } - - protected virtual void Cancel(Mobile from) - { - } - - protected enum GumpType - { - UnlockedContract, - LockedContract, - Offer, - VendorLandlord, - VendorRenter - } -} - -public class VendorRentalContractGump : BaseVendorRentalGump -{ - private readonly VendorRentalContract _contract; - - private VendorRentalContractGump(VendorRentalContract contract, Mobile from) : base( - contract.IsLockedDown ? GumpType.LockedContract : GumpType.UnlockedContract, - contract.Duration, - contract.Price, - contract.Price, - from, - null, - contract.LandlordRenew, - false, - false - ) => _contract = contract; - - public static void DisplayTo(Mobile from, VendorRentalContract contract) - { - if (from?.NetState != null && contract?.Deleted == false) - { - from.SendGump(new VendorRentalContractGump(contract, from), true); - } - } - - protected override bool IsValidResponse(Mobile from) => _contract.IsUsableBy(from, true, true, true, true); - - protected override void SetContractDuration(Mobile from, VendorRentalDuration duration) - { - _contract.Duration = duration; - - DisplayTo(from, _contract); - } - - protected override void SetPricePerRental(Mobile from) - { - // Please enter the amount of gold that should be charged for this contract (ESC to cancel): - from.SendLocalizedMessage(1062365); - from.Prompt = new PricePerRentalPrompt(_contract); - } - - protected override void LandlordRenewOnExpiration(Mobile from) - { - _contract.LandlordRenew = !_contract.LandlordRenew; - - DisplayTo(from, _contract); - } - - protected override void OfferContract(Mobile from) - { - if (_contract.IsLandlord(from)) - { - from.SendLocalizedMessage(1062371); // Please target the person you wish to offer this contract to. - from.Target = new OfferContractTarget(_contract); - } - } - - private class PricePerRentalPrompt : Prompt - { - private readonly VendorRentalContract _contract; - - public PricePerRentalPrompt(VendorRentalContract contract) => _contract = contract; - - public override void OnResponse(Mobile from, string text) - { - if (!_contract.IsUsableBy(from, true, true, true, true)) + if (!IsValidResponse(from)) { return; } - if (!int.TryParse(text.AsSpan().Trim(), out var price)) + if ((info.ButtonID & 0x10) != 0) // Contract duration { - price = -1; - } + var index = info.ButtonID & 0xF; - if (price < 0) - { - from.SendLocalizedMessage(1062485); // Invalid entry. Rental fee set to 0. - _contract.Price = 0; - } - else if (price > 5000000) - { - _contract.Price = 5000000; - } - else - { - _contract.Price = price; - } - - DisplayTo(from, _contract); - } - - public override void OnCancel(Mobile from) - { - if (_contract.IsUsableBy(from, true, true, true, true)) - { - DisplayTo(from, _contract); - } - } - } - - private class OfferContractTarget : Target - { - private readonly VendorRentalContract _contract; - - public OfferContractTarget(VendorRentalContract contract) : base(-1, false, TargetFlags.None) => - _contract = contract; - - protected override void OnTarget(Mobile from, object targeted) - { - if (!_contract.IsUsableBy(from, true, false, true, true)) - { - return; - } - - if (targeted is not Mobile mob || !mob.Player || !mob.Alive || mob == from) - { - from.SendLocalizedMessage(1071984); // That is not a valid target for a rental contract! - } - else if (!mob.InRange(_contract, 5)) - { - from.SendLocalizedMessage(501853); // Target is too far away. - } - else - { - from.SendLocalizedMessage(1062372); // Please wait while that person considers your offer. - - // ~1_NAME~ is offering you a vendor rental. If you choose to accept this offer, you have 30 seconds to do so. - mob.SendLocalizedMessage(1062373, from.Name); - VendorRentalOfferGump.DisplayTo(mob, _contract, from); - - _contract.Offeree = mob; - } - } - - protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) - { - from.SendLocalizedMessage(1062380); // You decide against offering the contract to anyone. - } - } -} - -public class VendorRentalOfferGump : BaseVendorRentalGump -{ - private readonly VendorRentalContract _contract; - private readonly Mobile _landlord; - - private VendorRentalOfferGump(VendorRentalContract contract, Mobile landlord) : base( - GumpType.Offer, - contract.Duration, - contract.Price, - contract.Price, - landlord, - null, - contract.LandlordRenew, - false, - false - ) - { - _contract = contract; - _landlord = landlord; - } - - public static void DisplayTo(Mobile to, VendorRentalContract contract, Mobile landlord) - { - if (to?.NetState != null && contract?.Deleted == false && landlord != null) - { - to.SendGump(new VendorRentalOfferGump(contract, landlord)); - } - } - - protected override bool IsValidResponse(Mobile from) => - _contract.IsUsableBy(_landlord, true, false, false, false) && from.CheckAlive() && _contract.Offeree == from; - - protected override void AcceptOffer(Mobile from) - { - _contract.Offeree = null; - - if (!_contract.Map.CanFit(_contract.Location, 16, false, false)) - { - _landlord.SendLocalizedMessage(1062486); // A vendor cannot exist at that location. Please try again. - return; - } - - var house = BaseHouse.FindHouseAt(_contract); - if (house == null) - { - return; - } - - var price = _contract.Price; - int goldToGive; - - if (price > 0) - { - if (Banker.Withdraw(from, price)) - { - // ~1_AMOUNT~ gold has been withdrawn from your bank box. - from.SendLocalizedMessage(1060398, price.ToString()); - - var depositedGold = Banker.DepositUpTo(_landlord, price); - goldToGive = price - depositedGold; - - if (depositedGold > 0) + if (index < VendorRentalDuration.Instances.Length) { - // ~1_AMOUNT~ gold has been deposited into your bank box. - _landlord.SendLocalizedMessage(1060397, price.ToString()); - } - - if (goldToGive > 0) - { - _landlord.SendLocalizedMessage(500390); // Your bank box is full. + SetContractDuration(from, VendorRentalDuration.Instances[index]); } } else { - // You do not have enough gold in your bank account to cover the cost of the contract. - from.SendLocalizedMessage(1062378); - _landlord.SendLocalizedMessage(1062374, from.Name); // ~1_NAME~ has declined your vendor rental offer. + switch (info.ButtonID) + { + case 1: // Price Per Rental + SetPricePerRental(from); + break; - return; + case 2: // Accept offer + AcceptOffer(from); + break; + + case 3: // Renew on expiration - landlord + LandlordRenewOnExpiration(from); + break; + + case 4: // Renew on expiration - renter + RenterRenewOnExpiration(from); + break; + + case 5: // Offer Contract To Someone + OfferContract(from); + break; + + case 6: // Renewal price + SetRenewalPrice(from); + break; + + default: + Cancel(from); + break; + } } } - else + + protected abstract bool IsValidResponse(Mobile from); + + protected virtual void SetContractDuration(Mobile from, VendorRentalDuration duration) { - goldToGive = 0; } - var vendor = new RentedVendor(from, house, _contract.Duration, price, _contract.LandlordRenew, goldToGive); - vendor.MoveToWorld(_contract.Location, _contract.Map); - - _contract.Delete(); - - // You have accepted the offer and now own a vendor in this house. Rental contract options and details may be viewed on this vendor via the 'Contract Options' context menu. - from.SendLocalizedMessage(1062377); - - // ~1_NAME~ has accepted your vendor rental offer. Rental contract details and options may be viewed on this vendor via the 'Contract Options' context menu. - _landlord.SendLocalizedMessage(1062376, from.Name); - } - - protected override void Cancel(Mobile from) - { - _contract.Offeree = null; - - from.SendLocalizedMessage(1062375); // You decline the offer for a vendor space rental. - _landlord.SendLocalizedMessage(1062374, from.Name); // ~1_NAME~ has declined your vendor rental offer. - } -} - -public class RenterVendorRentalGump : BaseVendorRentalGump -{ - private readonly RentedVendor _vendor; - - private RenterVendorRentalGump(RentedVendor vendor) : base( - GumpType.VendorRenter, - vendor.RentalDuration, - vendor.RentalPrice, - vendor.RenewalPrice, - vendor.Landlord, - vendor.Owner, - vendor.LandlordRenew, - vendor.RenterRenew, - vendor.Renew - ) => _vendor = vendor; - - public static void DisplayTo(Mobile from, RentedVendor vendor) - { - if (from?.NetState != null && vendor?.Deleted == false) + protected virtual void SetPricePerRental(Mobile from) { - from.SendGump(new RenterVendorRentalGump(vendor), true); + } + + protected virtual void AcceptOffer(Mobile from) + { + } + + protected virtual void LandlordRenewOnExpiration(Mobile from) + { + } + + protected virtual void RenterRenewOnExpiration(Mobile from) + { + } + + protected virtual void OfferContract(Mobile from) + { + } + + protected virtual void SetRenewalPrice(Mobile from) + { + } + + protected virtual void Cancel(Mobile from) + { + } + + protected enum GumpType + { + UnlockedContract, + LockedContract, + Offer, + VendorLandlord, + VendorRenter } } - protected override bool IsValidResponse(Mobile from) => _vendor.CanInteractWith(from, true); - - protected override void RenterRenewOnExpiration(Mobile from) + public class VendorRentalContractGump : BaseVendorRentalGump { - _vendor.RenterRenew = !_vendor.RenterRenew; + private readonly VendorRentalContract m_Contract; - DisplayTo(from, _vendor); - } -} + public VendorRentalContractGump(VendorRentalContract contract, Mobile from) : base( + contract.IsLockedDown ? GumpType.LockedContract : GumpType.UnlockedContract, + contract.Duration, + contract.Price, + contract.Price, + from, + null, + contract.LandlordRenew, + false, + false + ) => + m_Contract = contract; -public class LandlordVendorRentalGump : BaseVendorRentalGump -{ - private readonly RentedVendor _vendor; + protected override bool IsValidResponse(Mobile from) => m_Contract.IsUsableBy(from, true, true, true, true); - private LandlordVendorRentalGump(RentedVendor vendor) : base( - GumpType.VendorLandlord, - vendor.RentalDuration, - vendor.RentalPrice, - vendor.RenewalPrice, - vendor.Landlord, - vendor.Owner, - vendor.LandlordRenew, - vendor.RenterRenew, - vendor.Renew - ) => _vendor = vendor; - - public static void DisplayTo(Mobile from, RentedVendor vendor) - { - if (from?.NetState != null && vendor?.Deleted == false) + protected override void SetContractDuration(Mobile from, VendorRentalDuration duration) { - from.SendGump(new LandlordVendorRentalGump(vendor), true); + m_Contract.Duration = duration; + + from.SendGump(new VendorRentalContractGump(m_Contract, from)); + } + + protected override void SetPricePerRental(Mobile from) + { + from.SendLocalizedMessage( + 1062365 + ); // Please enter the amount of gold that should be charged for this contract (ESC to cancel): + from.Prompt = new PricePerRentalPrompt(m_Contract); + } + + protected override void LandlordRenewOnExpiration(Mobile from) + { + m_Contract.LandlordRenew = !m_Contract.LandlordRenew; + + from.SendGump(new VendorRentalContractGump(m_Contract, from)); + } + + protected override void OfferContract(Mobile from) + { + if (m_Contract.IsLandlord(from)) + { + from.SendLocalizedMessage(1062371); // Please target the person you wish to offer this contract to. + from.Target = new OfferContractTarget(m_Contract); + } + } + + private class PricePerRentalPrompt : Prompt + { + private readonly VendorRentalContract m_Contract; + + public PricePerRentalPrompt(VendorRentalContract contract) => m_Contract = contract; + + public override void OnResponse(Mobile from, string text) + { + if (!m_Contract.IsUsableBy(from, true, true, true, true)) + { + return; + } + + text = text.Trim(); + + if (!int.TryParse(text, out var price)) + { + price = -1; + } + + if (price < 0) + { + from.SendLocalizedMessage(1062485); // Invalid entry. Rental fee set to 0. + m_Contract.Price = 0; + } + else if (price > 5000000) + { + m_Contract.Price = 5000000; + } + else + { + m_Contract.Price = price; + } + + from.SendGump(new VendorRentalContractGump(m_Contract, from)); + } + + public override void OnCancel(Mobile from) + { + if (m_Contract.IsUsableBy(from, true, true, true, true)) + { + from.SendGump(new VendorRentalContractGump(m_Contract, from)); + } + } + } + + private class OfferContractTarget : Target + { + private readonly VendorRentalContract m_Contract; + + public OfferContractTarget(VendorRentalContract contract) : base(-1, false, TargetFlags.None) => + m_Contract = contract; + + protected override void OnTarget(Mobile from, object targeted) + { + if (!m_Contract.IsUsableBy(from, true, false, true, true)) + { + return; + } + + if (targeted is not Mobile mob || !mob.Player || !mob.Alive || mob == from) + { + from.SendLocalizedMessage(1071984); // That is not a valid target for a rental contract! + } + else if (!mob.InRange(m_Contract, 5)) + { + from.SendLocalizedMessage(501853); // Target is too far away. + } + else + { + from.SendLocalizedMessage(1062372); // Please wait while that person considers your offer. + + mob.SendLocalizedMessage( + 1062373, + from.Name + ); // ~1_NAME~ is offering you a vendor rental. If you choose to accept this offer, you have 30 seconds to do so. + mob.SendGump(new VendorRentalOfferGump(m_Contract, from)); + + m_Contract.Offeree = mob; + } + } + + protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) + { + from.SendLocalizedMessage(1062380); // You decide against offering the contract to anyone. + } } } - protected override bool IsValidResponse(Mobile from) => - _vendor.CanInteractWith(from, false) && _vendor.IsLandlord(from); - - protected override void LandlordRenewOnExpiration(Mobile from) + public class VendorRentalOfferGump : BaseVendorRentalGump { - _vendor.LandlordRenew = !_vendor.LandlordRenew; + private readonly VendorRentalContract m_Contract; + private readonly Mobile m_Landlord; - DisplayTo(from, _vendor); - } - - protected override void SetRenewalPrice(Mobile from) - { - from.SendLocalizedMessage(1062500); // Enter contract renewal price: - - from.Prompt = new ContractRenewalPricePrompt(_vendor); - } - - private class ContractRenewalPricePrompt : Prompt - { - private readonly RentedVendor _vendor; - - public ContractRenewalPricePrompt(RentedVendor vendor) => _vendor = vendor; - - public override void OnResponse(Mobile from, string text) + public VendorRentalOfferGump(VendorRentalContract contract, Mobile landlord) : base( + GumpType.Offer, + contract.Duration, + contract.Price, + contract.Price, + landlord, + null, + contract.LandlordRenew, + false, + false + ) { - if (!_vendor.CanInteractWith(from, false) || !_vendor.IsLandlord(from)) + m_Contract = contract; + m_Landlord = landlord; + } + + protected override bool IsValidResponse(Mobile from) => + m_Contract.IsUsableBy(m_Landlord, true, false, false, false) && from.CheckAlive() && m_Contract.Offeree == from; + + protected override void AcceptOffer(Mobile from) + { + m_Contract.Offeree = null; + + if (!m_Contract.Map.CanFit(m_Contract.Location, 16, false, false)) + { + m_Landlord.SendLocalizedMessage(1062486); // A vendor cannot exist at that location. Please try again. + return; + } + + var house = BaseHouse.FindHouseAt(m_Contract); + if (house == null) { return; } - if (!int.TryParse(text.AsSpan().Trim(), out var price)) - { - price = -1; - } + var price = m_Contract.Price; + int goldToGive; - if (price < 0) + if (price > 0) { - from.SendLocalizedMessage(1062485); // Invalid entry. Rental fee set to 0. - _vendor.RenewalPrice = 0; - } - else if (price > 5000000) - { - _vendor.RenewalPrice = 5000000; + if (Banker.Withdraw(from, price)) + { + from.SendLocalizedMessage( + 1060398, + price.ToString() + ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + + var depositedGold = Banker.DepositUpTo(m_Landlord, price); + goldToGive = price - depositedGold; + + if (depositedGold > 0) + { + m_Landlord.SendLocalizedMessage( + 1060397, + price.ToString() + ); // ~1_AMOUNT~ gold has been deposited into your bank box. + } + + if (goldToGive > 0) + { + m_Landlord.SendLocalizedMessage(500390); // Your bank box is full. + } + } + else + { + from.SendLocalizedMessage( + 1062378 + ); // You do not have enough gold in your bank account to cover the cost of the contract. + m_Landlord.SendLocalizedMessage(1062374, from.Name); // ~1_NAME~ has declined your vendor rental offer. + + return; + } } else { - _vendor.RenewalPrice = price; + goldToGive = 0; } - _vendor.RenterRenew = false; + PlayerVendor vendor = new RentedVendor( + from, + house, + m_Contract.Duration, + price, + m_Contract.LandlordRenew, + goldToGive + ); + vendor.MoveToWorld(m_Contract.Location, m_Contract.Map); - DisplayTo(from, _vendor); + m_Contract.Delete(); + + from.SendLocalizedMessage( + 1062377 + ); // You have accepted the offer and now own a vendor in this house. Rental contract options and details may be viewed on this vendor via the 'Contract Options' context menu. + m_Landlord.SendLocalizedMessage( + 1062376, + from.Name + ); // ~1_NAME~ has accepted your vendor rental offer. Rental contract details and options may be viewed on this vendor via the 'Contract Options' context menu. } - public override void OnCancel(Mobile from) + protected override void Cancel(Mobile from) { - if (_vendor.CanInteractWith(from, false) && _vendor.IsLandlord(from)) + m_Contract.Offeree = null; + + from.SendLocalizedMessage(1062375); // You decline the offer for a vendor space rental. + m_Landlord.SendLocalizedMessage(1062374, from.Name); // ~1_NAME~ has declined your vendor rental offer. + } + } + + public class RenterVendorRentalGump : BaseVendorRentalGump + { + private readonly RentedVendor m_Vendor; + + public RenterVendorRentalGump(RentedVendor vendor) : base( + GumpType.VendorRenter, + vendor.RentalDuration, + vendor.RentalPrice, + vendor.RenewalPrice, + vendor.Landlord, + vendor.Owner, + vendor.LandlordRenew, + vendor.RenterRenew, + vendor.Renew + ) => + m_Vendor = vendor; + + protected override bool IsValidResponse(Mobile from) => m_Vendor.CanInteractWith(from, true); + + protected override void RenterRenewOnExpiration(Mobile from) + { + m_Vendor.RenterRenew = !m_Vendor.RenterRenew; + + from.SendGump(new RenterVendorRentalGump(m_Vendor)); + } + } + + public class LandlordVendorRentalGump : BaseVendorRentalGump + { + private readonly RentedVendor m_Vendor; + + public LandlordVendorRentalGump(RentedVendor vendor) : base( + GumpType.VendorLandlord, + vendor.RentalDuration, + vendor.RentalPrice, + vendor.RenewalPrice, + vendor.Landlord, + vendor.Owner, + vendor.LandlordRenew, + vendor.RenterRenew, + vendor.Renew + ) => + m_Vendor = vendor; + + protected override bool IsValidResponse(Mobile from) => + m_Vendor.CanInteractWith(from, false) && m_Vendor.IsLandlord(from); + + protected override void LandlordRenewOnExpiration(Mobile from) + { + m_Vendor.LandlordRenew = !m_Vendor.LandlordRenew; + + from.SendGump(new LandlordVendorRentalGump(m_Vendor)); + } + + protected override void SetRenewalPrice(Mobile from) + { + from.SendLocalizedMessage(1062500); // Enter contract renewal price: + + from.Prompt = new ContractRenewalPricePrompt(m_Vendor); + } + + private class ContractRenewalPricePrompt : Prompt + { + private readonly RentedVendor m_Vendor; + + public ContractRenewalPricePrompt(RentedVendor vendor) => m_Vendor = vendor; + + public override void OnResponse(Mobile from, string text) { - DisplayTo(from, _vendor); + if (!m_Vendor.CanInteractWith(from, false) || !m_Vendor.IsLandlord(from)) + { + return; + } + + text = text.Trim(); + + if (!int.TryParse(text, out var price)) + { + price = -1; + } + + if (price < 0) + { + from.SendLocalizedMessage(1062485); // Invalid entry. Rental fee set to 0. + m_Vendor.RenewalPrice = 0; + } + else if (price > 5000000) + { + m_Vendor.RenewalPrice = 5000000; + } + else + { + m_Vendor.RenewalPrice = price; + } + + m_Vendor.RenterRenew = false; + + from.SendGump(new LandlordVendorRentalGump(m_Vendor)); + } + + public override void OnCancel(Mobile from) + { + if (m_Vendor.CanInteractWith(from, false) && m_Vendor.IsLandlord(from)) + { + from.SendGump(new LandlordVendorRentalGump(m_Vendor)); + } + } + } + } + + public class VendorRentalRefundGump : Gump + { + private readonly Mobile m_Landlord; + private readonly int m_RefundAmount; + private readonly RentedVendor m_Vendor; + + public override bool Singleton => true; + + public VendorRentalRefundGump(RentedVendor vendor, Mobile landlord, int refundAmount) : base(50, 50) + { + m_Vendor = vendor; + m_Landlord = landlord; + m_RefundAmount = refundAmount; + + AddBackground(0, 0, 420, 320, 0x13BE); + + AddImageTiled(10, 10, 400, 300, 0xA40); + AddAlphaRegion(10, 10, 400, 300); + + /* The landlord for this vendor is offering you a partial refund of your rental fee + * in exchange for immediate termination of your rental contract.

+ * + * If you accept this offer, the vendor will be immediately dismissed. You will then + * be able to claim the inventory and any funds the vendor may be holding for you via + * a context menu on the house sign for this house. + */ + AddHtmlLocalized(10, 10, 400, 150, 1062501, 0x7FFF, false, true); + + AddHtmlLocalized(10, 180, 150, 20, 1062508, 0x7FFF); // Vendor Name: + AddLabel(160, 180, 0x480, vendor.Name); + + AddHtmlLocalized(10, 200, 150, 20, 1062509, 0x7FFF); // Shop Name: + AddLabel(160, 200, 0x480, vendor.ShopName); + + AddHtmlLocalized(10, 220, 150, 20, 1062510, 0x7FFF); // Refund Amount: + AddLabel(160, 220, 0x480, refundAmount.ToString()); + + AddButton(10, 268, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(45, 268, 350, 20, 1062511, 0x7FFF); // Agree, and dismiss vendor + + AddButton(10, 288, 0xFA5, 0xFA7, 0); + AddHtmlLocalized(45, 288, 350, 20, 1062512, 0x7FFF); // No, I want to keep my vendor + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + var from = sender.Mobile; + + if (!m_Vendor.CanInteractWith(from, true) || !m_Vendor.CanInteractWith(m_Landlord, false) || + !m_Vendor.IsLandlord(m_Landlord)) + { + return; + } + + if (info.ButtonID == 1) + { + if (Banker.Withdraw(m_Landlord, m_RefundAmount)) + { + m_Landlord.SendLocalizedMessage( + 1060398, + m_RefundAmount.ToString() + ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + + var depositedGold = Banker.DepositUpTo(from, m_RefundAmount); + + if (depositedGold > 0) + { + from.SendLocalizedMessage( + 1060397, + depositedGold.ToString() + ); // ~1_AMOUNT~ gold has been deposited into your bank box. + } + + m_Vendor.HoldGold += m_RefundAmount - depositedGold; + + m_Vendor.Destroy(false); + + from.SendLocalizedMessage(1071990); // Remember to claim your vendor's belongings from the house sign! + } + else + { + m_Landlord.SendLocalizedMessage(1062507); // You do not have that much money in your bank account. + } + } + else + { + m_Landlord.SendLocalizedMessage(1062513); // The renter declined your offer. } } } } - -public class VendorRentalRefundGump : StaticGump -{ - private readonly Mobile _landlord; - private readonly int _refundAmount; - private readonly RentedVendor _vendor; - - public override bool Singleton => true; - - private VendorRentalRefundGump(RentedVendor vendor, Mobile landlord, int refundAmount) : base(50, 50) - { - _vendor = vendor; - _landlord = landlord; - _refundAmount = refundAmount; - } - - public static void DisplayTo(Mobile to, RentedVendor vendor, Mobile landlord, int refundAmount) - { - if (to?.NetState == null || vendor?.Deleted != false || landlord == null) - { - return; - } - - to.SendGump(new VendorRentalRefundGump(vendor, landlord, refundAmount)); - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 420, 320, 0x13BE); - - builder.AddImageTiled(10, 10, 400, 300, 0xA40); - builder.AddAlphaRegion(10, 10, 400, 300); - - /* The landlord for this vendor is offering you a partial refund of your rental fee - * in exchange for immediate termination of your rental contract.

- * - * If you accept this offer, the vendor will be immediately dismissed. You will then - * be able to claim the inventory and any funds the vendor may be holding for you via - * a context menu on the house sign for this house. - */ - builder.AddHtmlLocalized(10, 10, 400, 150, 1062501, 0x7FFF, false, true); - - builder.AddHtmlLocalized(10, 180, 150, 20, 1062508, 0x7FFF); // Vendor Name: - builder.AddLabelPlaceholder(160, 180, 0x480, "vendorName"); - - builder.AddHtmlLocalized(10, 200, 150, 20, 1062509, 0x7FFF); // Shop Name: - builder.AddLabelPlaceholder(160, 200, 0x480, "shopName"); - - builder.AddHtmlLocalized(10, 220, 150, 20, 1062510, 0x7FFF); // Refund Amount: - builder.AddLabelPlaceholder(160, 220, 0x480, "refundAmount"); - - builder.AddButton(10, 268, 0xFA5, 0xFA7, 1); - builder.AddHtmlLocalized(45, 268, 350, 20, 1062511, 0x7FFF); // Agree, and dismiss vendor - - builder.AddButton(10, 288, 0xFA5, 0xFA7, 0); - builder.AddHtmlLocalized(45, 288, 350, 20, 1062512, 0x7FFF); // No, I want to keep my vendor - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - builder.SetStringSlot("vendorName", _vendor.Name); - builder.SetStringSlot("shopName", _vendor.ShopName); - builder.SetStringSlot("refundAmount", $"{_refundAmount}"); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var from = sender.Mobile; - - if (!_vendor.CanInteractWith(from, true) || !_vendor.CanInteractWith(_landlord, false) || - !_vendor.IsLandlord(_landlord)) - { - return; - } - - if (info.ButtonID != 1) - { - _landlord.SendLocalizedMessage(1062513); // The renter declined your offer. - return; - } - - if (!Banker.Withdraw(_landlord, _refundAmount)) - { - _landlord.SendLocalizedMessage(1062507); // You do not have that much money in your bank account. - return; - } - - // ~1_AMOUNT~ gold has been withdrawn from your bank box. - _landlord.SendLocalizedMessage(1060398, _refundAmount.ToString()); - - var depositedGold = Banker.DepositUpTo(from, _refundAmount); - - if (depositedGold > 0) - { - // ~1_AMOUNT~ gold has been deposited into your bank box. - from.SendLocalizedMessage(1060397, depositedGold.ToString()); - } - - _vendor.HoldGold += _refundAmount - depositedGold; - - _vendor.Destroy(false); - - from.SendLocalizedMessage(1071990); // Remember to claim your vendor's belongings from the house sign! - } -} diff --git a/Projects/UOContent/Gumps/ViewHousesGump.cs b/Projects/UOContent/Gumps/ViewHousesGump.cs index 41861645b..12082c6b1 100644 --- a/Projects/UOContent/Gumps/ViewHousesGump.cs +++ b/Projects/UOContent/Gumps/ViewHousesGump.cs @@ -64,7 +64,7 @@ namespace Server.Gumps var name = FindHouseName(list[i]); - AddHtml(15, 40 + i % 15 * 20, 20, 20, Html.Color($"{i + 1}.", White)); + AddHtml(15, 40 + i % 15 * 20, 20, 20, $"{i + 1}.".Color(White)); if (name.Number > 0) { diff --git a/Projects/UOContent/Gumps/VirtualCheckGump.cs b/Projects/UOContent/Gumps/VirtualCheckGump.cs index 19fb2d8ed..11baa1896 100644 --- a/Projects/UOContent/Gumps/VirtualCheckGump.cs +++ b/Projects/UOContent/Gumps/VirtualCheckGump.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: VirtualCheckGump.cs * * * @@ -131,7 +131,7 @@ public sealed class VirtualCheckGump : Gump, IVirtualCheckGump AddImage(10, 8, 113); AddImage(360, 8, 113); - AddHtml(40, 15, 320, 20, Html.Center($"BANK OF {User.RawName.ToUpper()}", 0x2F4F4F)); + AddHtml(40, 15, 320, 20, $"BANK OF {User.RawName.ToUpper()}".Center(0x2F4F4F)); // Platinum Row AddBackground(15, 60, 175, 20, 9300); diff --git a/Projects/UOContent/Gumps/WhoGump.cs b/Projects/UOContent/Gumps/WhoGump.cs index f6d90fa1c..e47802a71 100644 --- a/Projects/UOContent/Gumps/WhoGump.cs +++ b/Projects/UOContent/Gumps/WhoGump.cs @@ -9,10 +9,18 @@ using static Server.Gumps.PropsConfig; namespace Server.Gumps; public class WhoGump : DynamicGump { - private const int EntryWidth = 180; - private const int EntryCount = 15; + private static readonly int PrevLabelOffsetX = PrevWidth + 1; + private static readonly int PrevLabelOffsetY = 0; - private const int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; + + private static readonly int EntryWidth = 180; + private static readonly int EntryCount = 15; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; private readonly List _mobiles; private readonly int _page; @@ -75,13 +83,34 @@ public class WhoGump : DynamicGump { var count = Math.Clamp(_mobiles.Count - _page * EntryCount, 0, EntryCount); + var totalHeight = OffsetSize + (EntryHeight + OffsetSize) * (count + 1); + builder.AddPage(); - builder.AddPropsFrame(TotalWidth, count + 1, out var x, out var y); + builder.AddBackground(0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID); + builder.AddImageTiled( + BorderSize, + BorderSize, + TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), + totalHeight, + OffsetGumpID + ); - const int emptyWidth = TotalWidth - PrevWidth - NextWidth - OffsetSize * 4; + var x = BorderSize + OffsetSize; + var y = BorderSize + OffsetSize; - builder.AddImageTiled(x, y, emptyWidth, EntryHeight, EntryGumpID); + var emptyWidth = TotalWidth - PrevWidth - NextWidth - OffsetSize * 4 - (OldStyle ? SetWidth + OffsetSize : 0); + + if (!OldStyle) + { + builder.AddImageTiled( + x - (OldStyle ? OffsetSize : 0), + y, + emptyWidth + (OldStyle ? OffsetSize * 2 : 0), + EntryHeight, + EntryGumpID + ); + } builder.AddLabel( x + TextOffsetX, @@ -92,37 +121,70 @@ public class WhoGump : DynamicGump x += emptyWidth + OffsetSize; - builder.AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID); + if (OldStyle) + { + builder.AddImageTiled(x, y, TotalWidth - OffsetSize * 3 - SetWidth, EntryHeight, HeaderGumpID); + } + else + { + builder.AddImageTiled(x, y, PrevWidth, EntryHeight, HeaderGumpID); + } if (_page > 0) { builder.AddButton(x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1); + + if (PrevLabel) + { + builder.AddLabel(x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous"); + } } x += PrevWidth + OffsetSize; - builder.AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID); + if (!OldStyle) + { + builder.AddImageTiled(x, y, NextWidth, EntryHeight, HeaderGumpID); + } if ((_page + 1) * EntryCount < _mobiles.Count) { builder.AddButton(x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 2, GumpButtonType.Reply, 1); + + if (NextLabel) + { + builder.AddLabel(x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next"); + } } for (int i = 0, index = _page * EntryCount; i < EntryCount && index < _mobiles.Count; ++i, ++index) { - PropsLayout.NextRow(ref x, ref y); + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; var m = _mobiles[index]; - builder.AddPropsEntryButton( - ref x, - ref y, - EntryWidth, - m.Deleted ? "(deleted)" : m.Name, - m.NetState != null && !m.Deleted, - i + 3, - textHue: GetHueFor(m) + builder.AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); + builder.AddLabelCropped( + x + TextOffsetX, + y, + EntryWidth - TextOffsetX, + EntryHeight, + GetHueFor(m), + m.Deleted ? "(deleted)" : m.Name ); + + x += EntryWidth + OffsetSize; + + if (SetGumpID != 0) + { + builder.AddImageTiled(x, y, SetWidth, EntryHeight, SetGumpID); + } + + if (m.NetState != null && !m.Deleted) + { + builder.AddButton(x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, i + 3); + } } } @@ -131,12 +193,12 @@ public class WhoGump : DynamicGump m.AccessLevel switch { >= AccessLevel.Administrator => 0x516, - AccessLevel.Seer => 0x144, - AccessLevel.GameMaster => 0x21, - AccessLevel.Counselor => 0x2, - _ when m.Murderer => 0x21, - _ when m.Criminal => 0x3B1, - _ => 0x58 + AccessLevel.Seer => 0x144, + AccessLevel.GameMaster => 0x21, + AccessLevel.Counselor => 0x2, + _ when m.Kills >= 5 => 0x21, + _ when m.Criminal => 0x3B1, + _ => 0x58 }; public override void OnResponse(NetState state, in RelayInfo info) @@ -208,8 +270,15 @@ public class WhoGump : DynamicGump public int Compare(Mobile x, Mobile y) { - ArgumentNullException.ThrowIfNull(x); - ArgumentNullException.ThrowIfNull(y); + if (x == null) + { + throw new ArgumentNullException(nameof(x)); + } + + if (y == null) + { + throw new ArgumentNullException(nameof(y)); + } if (x.AccessLevel > y.AccessLevel) { diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/DecorativeTopiary.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/DecorativeTopiary.cs index 54431f0ad..b6a89ee3d 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/DecorativeTopiary.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/DecorativeTopiary.cs @@ -6,9 +6,11 @@ namespace Server.Items; public partial class DecorativeTopiary : Item { [Constructible] - public DecorativeTopiary() : base(0x2378) => LootType = LootType.Blessed; - - public override double DefaultWeight => 1.0; + public DecorativeTopiary() : base(0x2378) + { + Weight = 1.0; + LootType = LootType.Blessed; + } public override void OnSingleClick(Mobile from) { diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/FestiveCactus.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/FestiveCactus.cs index 4fba63754..2ceb3f9e1 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/FestiveCactus.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/FestiveCactus.cs @@ -6,9 +6,11 @@ namespace Server.Items; public partial class FestiveCactus : Item { [Constructible] - public FestiveCactus() : base(0x2376) => LootType = LootType.Blessed; - - public override double DefaultWeight => 1.0; + public FestiveCactus() : base(0x2376) + { + Weight = 1.0; + LootType = LootType.Blessed; + } public override void OnSingleClick(Mobile from) { diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/LightOfTheWinterSolstice.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/LightOfTheWinterSolstice.cs index d41019847..4e03f72cc 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/LightOfTheWinterSolstice.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/LightOfTheWinterSolstice.cs @@ -18,13 +18,12 @@ public partial class LightOfTheWinterSolstice : Item { _dipper = dipper?.Intern() ?? StaffInfo.GetRandomStaff(); + Weight = 1.0; LootType = LootType.Blessed; Light = LightType.Circle300; Hue = Utility.RandomDyedHue(); } - public override double DefaultWeight => 1.0; - public override void OnSingleClick(Mobile from) { base.OnSingleClick(from); diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/Mistletoe.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/Mistletoe.cs index 1da2d19f4..f52e49dd7 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/Mistletoe.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/Mistletoe.cs @@ -56,6 +56,7 @@ public partial class MistletoeAddon : Item, IDyable, IAddon } from.SendLocalizedMessage(500295); // You are too far away to do that. + return false; } return false; @@ -103,7 +104,7 @@ public partial class MistletoeAddon : Item, IDyable, IAddon if (from.InRange(GetWorldLocation(), 3)) { - MistletoeAddonGump.DisplayTo(from, this); + from.SendGump(new MistletoeAddonGump(from, this)); } else { @@ -111,35 +112,27 @@ public partial class MistletoeAddon : Item, IDyable, IAddon } } - private class MistletoeAddonGump : StaticGump + private class MistletoeAddonGump : Gump { private readonly MistletoeAddon _addon; + private readonly Mobile _from; public override bool Singleton => true; - private MistletoeAddonGump(MistletoeAddon addon) : base(150, 50) => _addon = addon; - - public static void DisplayTo(Mobile from, MistletoeAddon addon) + public MistletoeAddonGump(Mobile from, MistletoeAddon addon) : base(150, 50) { - if (from?.NetState == null || addon?.Deleted != false) - { - return; - } + _from = from; + _addon = addon; - from.SendGump(new MistletoeAddonGump(addon)); - } + AddPage(0); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 220, 170, 0x13BE); - builder.AddBackground(10, 10, 200, 150, 0xBB8); - builder.AddHtmlLocalized(20, 30, 180, 60, 1062839); // Do you wish to re-deed this decoration? - builder.AddHtmlLocalized(55, 100, 160, 25, 1011011); // CONTINUE - builder.AddButton(20, 100, 0xFA5, 0xFA7, 1); - builder.AddHtmlLocalized(55, 125, 160, 25, 1011012); // CANCEL - builder.AddButton(20, 125, 0xFA5, 0xFA7, 0); + AddBackground(0, 0, 220, 170, 0x13BE); + AddBackground(10, 10, 200, 150, 0xBB8); + AddHtmlLocalized(20, 30, 180, 60, 1062839); // Do you wish to re-deed this decoration? + AddHtmlLocalized(55, 100, 160, 25, 1011011); // CONTINUE + AddButton(20, 100, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(55, 125, 160, 25, 1011012); // CANCEL + AddButton(20, 125, 0xFA5, 0xFA7, 0); } public override void OnResponse(NetState sender, in RelayInfo info) @@ -149,16 +142,14 @@ public partial class MistletoeAddon : Item, IDyable, IAddon return; } - var from = sender.Mobile; - - if (from.InRange(_addon.GetWorldLocation(), 3)) + if (_from.InRange(_addon.GetWorldLocation(), 3)) { - from.AddToBackpack(_addon.Deed); + _from.AddToBackpack(_addon.Deed); _addon.Delete(); } else { - from.SendLocalizedMessage(500295); // You are too far away to do that. + _from.SendLocalizedMessage(500295); // You are too far away to do that. } } } @@ -172,11 +163,10 @@ public partial class MistletoeDeed : Item public MistletoeDeed(int hue = 0) : base(0x14F0) { Hue = hue; + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1070882; // Mistletoe Deed public override void OnSingleClick(Mobile from) @@ -236,7 +226,7 @@ public partial class MistletoeDeed : Item if (northWall && westWall) { - from.SendGump(new MistletoeDeedGump(loc, this)); + from.SendGump(new MistletoeDeedGump(from, loc, this)); } else { @@ -276,11 +266,9 @@ public partial class MistletoeDeed : Item if (itemID > 0) { - var addon = new MistletoeAddon(Hue) - { - ItemID = itemID - }; + Item addon = new MistletoeAddon(Hue); + addon.ItemID = itemID; addon.MoveToWorld(loc, from.Map); house.Addons.Add(addon); @@ -288,47 +276,47 @@ public partial class MistletoeDeed : Item } } - private class MistletoeDeedGump : StaticGump + private class MistletoeDeedGump : Gump { private readonly MistletoeDeed _deed; + private readonly Mobile _from; private readonly Point3D _loc; - public override bool Singleton => false; - - public MistletoeDeedGump(Point3D loc, MistletoeDeed deed) : base(150, 50) + public MistletoeDeedGump(Mobile from, Point3D loc, MistletoeDeed deed) : base(150, 50) { + _from = from; _loc = loc; _deed = deed; - } - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddBackground(0, 0, 300, 150, 0xA28); + AddBackground(0, 0, 300, 150, 0xA28); - builder.AddPage(); + AddPage(0); - builder.AddItem(90, 30, 0x2375); - builder.AddItem(180, 30, 0x2374); - builder.AddButton(50, 35, 0x868, 0x869, 1); - builder.AddButton(145, 35, 0x868, 0x869, 2); + AddItem(90, 30, 0x2375); + AddItem(180, 30, 0x2374); + AddButton(50, 35, 0x868, 0x869, 1); + AddButton(145, 35, 0x868, 0x869, 2); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_deed.Deleted || info.ButtonID == 0) + if (_deed.Deleted) { return; } - var from = sender.Mobile; - - if (info.ButtonID == 1) + switch (info.ButtonID) { - _deed.PlaceAddon(from, _loc, false, true); - } - else - { - _deed.PlaceAddon(from, _loc, true, false); + case 1: + { + _deed.PlaceAddon(_from, _loc, false, true); + break; + } + case 2: + { + _deed.PlaceAddon(_from, _loc, true, false); + break; + } } } } diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/PileOfGlacialSnow.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/PileOfGlacialSnow.cs index 136ee9f47..54cf71449 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/PileOfGlacialSnow.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/PileOfGlacialSnow.cs @@ -9,11 +9,10 @@ public partial class PileOfGlacialSnow : Item public PileOfGlacialSnow() : base(0x913) { Hue = 0x480; + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1070874; // a Pile of Glacial Snow public override void OnSingleClick(Mobile from) diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowPile.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowPile.cs index 2f7624dad..2b092f187 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowPile.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowPile.cs @@ -9,11 +9,10 @@ public partial class SnowPile : Item public SnowPile() : base(0x913) { Hue = 0x481; + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1005578; // a pile of snow public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowyTree.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowyTree.cs index e1a231d85..ab233720e 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowyTree.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowyTree.cs @@ -6,9 +6,11 @@ namespace Server.Items; public partial class SnowyTree : Item { [Constructible] - public SnowyTree() : base(0x2377) => LootType = LootType.Blessed; - - public override double DefaultWeight => 1.0; + public SnowyTree() : base(0x2377) + { + Weight = 1.0; + LootType = LootType.Blessed; + } public override void OnSingleClick(Mobile from) { diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs b/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs index 9ff172543..948e15196 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs @@ -47,7 +47,7 @@ public partial class Fireflies : Item, IAddon if (house?.IsOwner(from) == true) { // Do you wish to re-deed this decoration? - RewardDemolitionGump.DisplayTo(from, this, 1049783); + from.SendGump(new RewardDemolitionGump(this, 1049783)); } else { @@ -61,9 +61,11 @@ public partial class Fireflies : Item, IAddon public partial class FirefliesDeed : Item { [Constructible] - public FirefliesDeed() : base(0x14F0) => LootType = LootType.Blessed; - - public override double DefaultWeight => 1.0; + public FirefliesDeed() : base(0x14F0) + { + LootType = LootType.Blessed; + Weight = 1.0; + } public override int LabelNumber => 1150061; @@ -83,27 +85,35 @@ public partial class FirefliesDeed : Item return; } - from.SendGump(new FacingGump(this)); + from.SendGump(new FacingGump(this, from)); } - private class FacingGump : StaticGump + private class FacingGump : Gump { private readonly FirefliesDeed _deed; + private readonly Mobile _placer; public override bool Singleton => true; - public FacingGump(FirefliesDeed deed) : base(150, 50) => _deed = deed; - - protected override void BuildLayout(ref StaticGumpBuilder builder) + public FacingGump(FirefliesDeed deed, Mobile player) : base(150, 50) { - builder.AddBackground(0, 0, 300, 150, 0xA28); - builder.AddPage(); + _deed = deed; + _placer = player; - builder.AddItem(90, 30, 0x2332); - builder.AddItem(180, 30, 0x2336); + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - builder.AddButton(50, 35, 0x868, 0x869, (int)Buttons.East); - builder.AddButton(145, 35, 0x868, 0x869, (int)Buttons.South); + AddPage(0); + + AddBackground(0, 0, 300, 150, 0xA28); + + AddItem(90, 30, 0x2332); + AddItem(180, 30, 0x2336); + + AddButton(50, 35, 0x868, 0x869, (int)Buttons.East); + AddButton(145, 35, 0x868, 0x869, (int)Buttons.South); } public override void OnResponse(NetState sender, in RelayInfo info) @@ -113,19 +123,14 @@ public partial class FirefliesDeed : Item return; } - var itemId = info.ButtonID switch + int itemId = info.ButtonID switch { (int)Buttons.East => 0x2332, (int)Buttons.South => 0x2336, _ => 0 }; - if (itemId == 0) - { - return; - } - - sender.Mobile.Target = new InternalTarget(_deed, itemId); + _placer.Target = new InternalTarget(_deed, itemId); } private enum Buttons @@ -202,7 +207,7 @@ public partial class FirefliesDeed : Item return; } - foreach (var fireflies in Map.Malas.GetItemsAt(p3d)) + foreach (Fireflies fireflies in Map.Malas.GetItemsAt(p3d)) { if (fireflies.Z == p3d.Z) { diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs index 06b662922..4b6d4969c 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs @@ -1,13 +1,16 @@ using ModernUO.Serialization; -namespace Server.Items.Holiday; - -[Flippable(0x46FA, 0x46FB)] -[TypeAlias("Server.Items.AngelDecoration")] -[SerializationGenerator(0, false)] -public partial class AngelDecoration : Item +namespace Server.Items.Holiday { - public AngelDecoration() : base(0x46FA) => LootType = LootType.Blessed; + [SerializationGenerator(0, false)] + [TypeAlias("Server.Items.AngelDecoration"), Flippable(0x46FA, 0x46FB)] + public partial class AngelDecoration : Item + { + public AngelDecoration() : base(0x46FA) + { + LootType = LootType.Blessed; - public override double DefaultWeight => 30.0; + Weight = 30; + } + } } diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs index 7b5684f57..f2588cf46 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs @@ -1,13 +1,16 @@ using ModernUO.Serialization; -namespace Server.Items.Holiday; - -[Flippable(0x4214, 0x4215)] -[TypeAlias("Server.Items.RockingHorse")] -[SerializationGenerator(0, false)] -public partial class RockingHorse : Item +namespace Server.Items.Holiday { - public RockingHorse() : base(0x4214) => LootType = LootType.Blessed; + [SerializationGenerator(0, false)] + [TypeAlias("Server.Items.RockingHorse"), Flippable(0x4214, 0x4215)] + public partial class RockingHorse : Item + { + public RockingHorse() : base(0x4214) + { + LootType = LootType.Blessed; - public override double DefaultWeight => 30.0; + Weight = 30; + } + } } diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs b/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs index f2e0c1116..7976ad583 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2006/Engines/TrickOrTreat.cs @@ -1,345 +1,342 @@ using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using ModernUO.Serialization; -using Server.Collections; using Server.Events.Halloween; using Server.Items; using Server.Mobiles; using Server.Targeting; -namespace Server.Engines.Events; - -public static class TrickOrTreat +namespace Server.Engines.Events { - public static void Initialize() + public static class TrickOrTreat { - var now = Core.Now; + public static TimeSpan OneSecond = TimeSpan.FromSeconds(1); - if (now >= HolidaySettings.StartHalloween && now <= HolidaySettings.FinishHalloween) + public static void Initialize() { - EventSink.Speech += EventSink_Speech; - } - } - - private static void EventSink_Speech(SpeechEventArgs e) - { - if (e.Speech.InsensitiveContains("trick or treat")) - { - e.Mobile.Target = new TrickOrTreatTarget(); - - e.Mobile.SendLocalizedMessage(1076764); /* Pick someone to Trick or Treat. */ - } - } - - public static void Bleeding(Mobile from) - { - if (!CheckMobile(from)) - { - return; - } - - if (from.Location == Point3D.Zero) - { - return; - } - - var amount = Utility.RandomMinMax(3, 7); - - for (var i = 0; i < amount; i++) - { - new Blood(Utility.RandomMinMax(0x122C, 0x122F)).MoveToWorld( - RandomPointOneAway(from.X, from.Y, from.Z, from.Map), - from.Map - ); - } - } - - public static void RemoveHueMod(Mobile target) - { - if (target?.Deleted == false) - { - target.SolidHueOverride = -1; - } - } - - public static void SolidHueMobile(Mobile target) - { - if (CheckMobile(target)) - { - target.SolidHueOverride = Utility.RandomMinMax(2501, 2644); - - Timer.StartTimer(TimeSpan.FromSeconds(10), () => RemoveHueMod(target)); - } - } - - public static void MakeTwin(Mobile from) - { - if (!CheckMobile(from)) - { - return; - } - - var twin = new NaughtyTwin(from); - - if (twin.Deleted) - { - return; - } - - using var items = PooledRefQueue.Create(); - for (var i = 0; i < from.Items.Count; i++) - { - var item = from.Items[i]; - if (item.Layer != Layer.Backpack && item.Layer != Layer.Mount && item.Layer != Layer.Bank) - { - items.Enqueue(item); - } - } - - if (items.Count > 0) - { - while (items.Count > 0) - { - twin.AddItem(Mobile.LiftItemDupe(items.Dequeue(), 1)); - } - - for (var i = 0; i < twin.Items.Count; i++) - { - var item = twin.Items[i]; - if (item.Layer != Layer.Backpack && item.Layer != Layer.Mount && item.Layer != Layer.Bank) - { - item.Movable = false; - } - } - } - - twin.Hue = from.Hue; - twin.Body = from.Body; - twin.Kills = from.Kills; - - var point = RandomPointOneAway(from.X, from.Y, from.Z, from.Map); - - twin.MoveToWorld(from.Map.CanSpawnMobile(point) ? point : from.Location, from.Map); - - Timer.StartTimer(TimeSpan.FromSeconds(5), () => DeleteTwin(twin)); - } - - public static void DeleteTwin(Mobile twin) - { - if (CheckMobile(twin)) - { - twin.Delete(); - } - } - - public static Point3D RandomPointOneAway(int x, int y, int z, Map map) - { - var loc = new Point3D(x + Utility.Random(-1, 3), y + Utility.Random(-1, 3), 0); - - loc.Z = map.CanFit(loc, 0) ? map.GetAverageZ(loc.X, loc.Y) : z; - - return loc; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CheckMobile(Mobile mobile) => - mobile?.Map != null && !mobile.Deleted && mobile.Alive && mobile.Map != Map.Internal; - - private class TrickOrTreatTarget : Target - { - public TrickOrTreatTarget() : base(15, false, TargetFlags.None) - { - } - - protected override void OnTarget(Mobile from, object targ) - { - if (targ == null || !CheckMobile(from)) - { - return; - } - - if (targ is not Mobile) - { - from.SendLocalizedMessage(1076781); /* There is little chance of getting candy from that! */ - return; - } - - var begged = targ as BaseVendor; - - if (begged?.Deleted != false) - { - from.SendLocalizedMessage(1076765); /* That doesn't look friendly. */ - return; - } - var now = Core.Now; - if (CheckMobile(begged)) + if (now >= HolidaySettings.StartHalloween && now <= HolidaySettings.FinishHalloween) { - if (begged.NextTrickOrTreat > now) + EventSink.Speech += EventSink_Speech; + } + } + + private static void EventSink_Speech(SpeechEventArgs e) + { + if (e.Speech.InsensitiveContains("trick or treat")) + { + e.Mobile.Target = new TrickOrTreatTarget(); + + e.Mobile.SendLocalizedMessage(1076764); /* Pick someone to Trick or Treat. */ + } + } + + public static void Bleeding(Mobile m_From) + { + if (CheckMobile(m_From)) + { + if (m_From.Location != Point3D.Zero) { - from.SendLocalizedMessage(1076767); /* That doesn't appear to have any more candy. */ - return; - } + var amount = Utility.RandomMinMax(3, 7); - begged.NextTrickOrTreat = now + TimeSpan.FromMinutes(Utility.RandomMinMax(5, 10)); - - if (from.Backpack?.Deleted != false) - { - return; - } - - if (Utility.RandomDouble() < 0.90) - { - begged.Say( - Utility.Random(3) switch - { - 0 => 1076768, // Oooooh, aren't you cute! - 1 => 1076779, // All right...This better not spoil your dinner! - _ => 1076778 // Here you go! Enjoy! - } - ); - - if (Utility.RandomDouble() < 0.01 && from.Skills.Begging.Value >= 100) + for (var i = 0; i < amount; i++) { - from.AddToBackpack(HolidaySettings.RandomGMBeggerItem); + new Blood(Utility.RandomMinMax(0x122C, 0x122F)).MoveToWorld( + RandomPointOneAway(m_From.X, m_From.Y, m_From.Z, m_From.Map), + m_From.Map + ); + } + } + } + } - from.SendLocalizedMessage(1076777); /* You receive a special treat! */ + public static void RemoveHueMod(Mobile target) + { + if (target?.Deleted == false) + { + target.SolidHueOverride = -1; + } + } + + public static void SolidHueMobile(Mobile target) + { + if (CheckMobile(target)) + { + target.SolidHueOverride = Utility.RandomMinMax(2501, 2644); + + Timer.StartTimer(TimeSpan.FromSeconds(10), () => RemoveHueMod(target)); + } + } + + public static void MakeTwin(Mobile m_From) + { + var m_Items = new List(); + + if (CheckMobile(m_From)) + { + Mobile twin = new NaughtyTwin(m_From); + + if (twin.Deleted) + { + return; + } + + foreach (var item in m_From.Items) + { + if (item.Layer != Layer.Backpack && item.Layer != Layer.Mount && item.Layer != Layer.Bank) + { + m_Items.Add(item); + } + } + + if (m_Items.Count > 0) + { + for (var i = 0; i < m_Items.Count; i++) /* dupe exploits start out like this ... */ + { + twin.AddItem(Mobile.LiftItemDupe(m_Items[i], 1)); + } + + foreach (var item in twin.Items) /* ... and end like this */ + { + if (item.Layer != Layer.Backpack && item.Layer != Layer.Mount && item.Layer != Layer.Bank) + { + item.Movable = false; + } + } + } + + twin.Hue = m_From.Hue; + twin.Body = m_From.Body; + twin.Kills = m_From.Kills; + + var point = RandomPointOneAway(m_From.X, m_From.Y, m_From.Z, m_From.Map); + + twin.MoveToWorld(m_From.Map.CanSpawnMobile(point) ? point : m_From.Location, m_From.Map); + + Timer.StartTimer(TimeSpan.FromSeconds(5), () => DeleteTwin(twin)); + } + } + + public static void DeleteTwin(Mobile m_Twin) + { + if (CheckMobile(m_Twin)) + { + m_Twin.Delete(); + } + } + + public static Point3D RandomPointOneAway(int x, int y, int z, Map map) + { + var loc = new Point3D(x + Utility.Random(-1, 3), y + Utility.Random(-1, 3), 0); + + loc.Z = map.CanFit(loc, 0) ? map.GetAverageZ(loc.X, loc.Y) : z; + + return loc; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CheckMobile(Mobile mobile) => + mobile?.Map != null && !mobile.Deleted && mobile.Alive && mobile.Map != Map.Internal; + + private class TrickOrTreatTarget : Target + { + public TrickOrTreatTarget() + : base(15, false, TargetFlags.None) + { + } + + protected override void OnTarget(Mobile from, object targ) + { + if (targ == null || !CheckMobile(from)) + { + return; + } + + if (targ is not Mobile) + { + from.SendLocalizedMessage(1076781); /* There is little chance of getting candy from that! */ + return; + } + + var begged = targ as BaseVendor; + + if (begged?.Deleted != false) + { + from.SendLocalizedMessage(1076765); /* That doesn't look friendly. */ + return; + } + + var now = Core.Now; + + if (CheckMobile(begged)) + { + if (begged.NextTrickOrTreat > now) + { + from.SendLocalizedMessage(1076767); /* That doesn't appear to have any more candy. */ + return; + } + + begged.NextTrickOrTreat = now + TimeSpan.FromMinutes(Utility.RandomMinMax(5, 10)); + + if (from.Backpack?.Deleted != false) + { + return; + } + + if (Utility.RandomDouble() < 0.90) + { + begged.Say( + Utility.Random(3) switch + { + 0 => 1076768, // Oooooh, aren't you cute! + 1 => 1076779, // All right...This better not spoil your dinner! + _ => 1076778 // Here you go! Enjoy! + } + ); + + if (Utility.RandomDouble() < 0.01 && from.Skills.Begging.Value >= 100) + { + from.AddToBackpack(HolidaySettings.RandomGMBeggerItem); + + from.SendLocalizedMessage(1076777); /* You receive a special treat! */ + } + else + { + from.AddToBackpack(HolidaySettings.RandomTreat); + + from.SendLocalizedMessage(1076769); /* You receive some candy. */ + } } else { - from.AddToBackpack(HolidaySettings.RandomTreat); + begged.Say(1076770); /* TRICK! */ - from.SendLocalizedMessage(1076769); /* You receive some candy. */ + var action = Utility.Random(4); + + if (action == 0) + { + Timer.StartTimer(OneSecond, OneSecond, 10, () => Bleeding(from)); + } + else if (action == 1) + { + Timer.StartTimer(TimeSpan.FromSeconds(2), () => SolidHueMobile(from)); + } + else + { + Timer.StartTimer(TimeSpan.FromSeconds(2), () => MakeTwin(from)); + } + } + } + } + } + } + + [SerializationGenerator(0, false)] + public partial class NaughtyTwin : BaseCreature + { + private static readonly Point3D[] Felucca_Locations = + { + new(4467, 1283, 5), // Moonglow + new(1336, 1997, 5), // Britain + new(1499, 3771, 5), // Jhelom + new(771, 752, 5), // Yew + new(2701, 692, 5), // Minoc + new(1828, 2948, -20), // Trinsic + new(643, 2067, 5), // Skara Brae + new(3563, 2139, Map.Trammel.GetAverageZ(3563, 2139)) // (New) Magincia + }; + + private static readonly Point3D[] Malas_Locations = + { + new(1015, 527, -65), // Luna + new(1997, 1386, -85) // Umbra + }; + + private static readonly Point3D[] Ilshenar_Locations = + { + new(1215, 467, -13), // Compassion + new(722, 1366, -60), // Honesty + new(744, 724, -28), // Honor + new(281, 1016, 0), // Humility + new(987, 1011, -32), // Justice + new(1174, 1286, -30), // Sacrifice + new(1532, 1340, -3), // Spirituality + new(528, 216, -45), // Valor + new(1721, 218, 96) // Chaos + }; + + private static readonly Point3D[] Tokuno_Locations = + { + new(1169, 998, 41), // Isamu-Jima + new(802, 1204, 25), // Makoto-Jima + new(270, 628, 15) // Homare-Jima + }; + + private readonly Mobile m_From; + + public NaughtyTwin(Mobile from) : base(AIType.AI_Melee, FightMode.None) + { + if (TrickOrTreat.CheckMobile(from)) + { + Body = from.Body; + + m_From = from; + Name = $"{from.Name}\'s Naughty Twin"; + + Timer.StartTimer(TrickOrTreat.OneSecond, () => StealCandyOrGate(m_From)); + } + } + + public override void OnThink() + { + if (m_From?.Deleted != false) + { + Delete(); + } + } + + public static Item FindCandyTypes(Mobile target) + { + Type[] types = + { typeof(WrappedCandy), typeof(Lollipops), typeof(NougatSwirl), typeof(Taffy), typeof(JellyBeans) }; + + return TrickOrTreat.CheckMobile(target) ? target.Backpack.FindItemByType(types) : null; + } + + public static void StealCandyOrGate(Mobile target) + { + if (TrickOrTreat.CheckMobile(target)) + { + if (Utility.RandomBool()) + { + var item = FindCandyTypes(target); + + target.SendLocalizedMessage(1113967); /* Your naughty twin steals some of your candy. */ + + if (item?.Deleted == false) + { + item.Delete(); } } else { - begged.Say(1076770); /* TRICK! */ - - var action = Utility.Random(4); - - if (action == 0) - { - Timer.StartTimer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), 10, () => Bleeding(from)); - } - else if (action == 1) - { - Timer.StartTimer(TimeSpan.FromSeconds(2), () => SolidHueMobile(from)); - } - else - { - Timer.StartTimer(TimeSpan.FromSeconds(2), () => MakeTwin(from)); - } + target.SendLocalizedMessage(1113972); /* Your naughty twin teleports you away with a naughty laugh! */ + target.MoveToWorld(RandomMoongate(target), target.Map); } } } - } -} - -[SerializationGenerator(0, false)] -public partial class NaughtyTwin : BaseCreature -{ - private static readonly Point3D[] Felucca_Locations = - { - new(4467, 1283, 5), // Moonglow - new(1336, 1997, 5), // Britain - new(1499, 3771, 5), // Jhelom - new(771, 752, 5), // Yew - new(2701, 692, 5), // Minoc - new(1828, 2948, -20), // Trinsic - new(643, 2067, 5), // Skara Brae - new(3563, 2139, Map.Trammel.GetAverageZ(3563, 2139)) // (New) Magincia - }; - - private static readonly Point3D[] Malas_Locations = - { - new(1015, 527, -65), // Luna - new(1997, 1386, -85) // Umbra - }; - - private static readonly Point3D[] Ilshenar_Locations = - { - new(1215, 467, -13), // Compassion - new(722, 1366, -60), // Honesty - new(744, 724, -28), // Honor - new(281, 1016, 0), // Humility - new(987, 1011, -32), // Justice - new(1174, 1286, -30), // Sacrifice - new(1532, 1340, -3), // Spirituality - new(528, 216, -45), // Valor - new(1721, 218, 96) // Chaos - }; - - private static readonly Point3D[] Tokuno_Locations = - { - new(1169, 998, 41), // Isamu-Jima - new(802, 1204, 25), // Makoto-Jima - new(270, 628, 15) // Homare-Jima - }; - - private readonly Mobile m_From; - - public NaughtyTwin(Mobile from) : base(AIType.AI_Melee, FightMode.None) - { - if (TrickOrTreat.CheckMobile(from)) - { - Body = from.Body; - - m_From = from; - Name = $"{from.Name}\'s Naughty Twin"; - - Timer.StartTimer(TimeSpan.FromSeconds(1), () => StealCandyOrGate(m_From)); - } - } - - public override void OnThink() - { - if (m_From?.Deleted != false) - { - Delete(); - } - } - - public static Item FindCandyTypes(Mobile target) - { - Type[] types = - { typeof(WrappedCandy), typeof(Lollipops), typeof(NougatSwirl), typeof(Taffy), typeof(JellyBeans) }; - - return TrickOrTreat.CheckMobile(target) ? target.Backpack.FindItemByType(types) : null; - } - - public static void StealCandyOrGate(Mobile target) - { - if (TrickOrTreat.CheckMobile(target)) - { - if (Utility.RandomBool()) - { - var item = FindCandyTypes(target); - - target.SendLocalizedMessage(1113967); /* Your naughty twin steals some of your candy. */ - - if (item?.Deleted == false) - { - item.Delete(); - } - } - else - { - target.SendLocalizedMessage(1113972); /* Your naughty twin teleports you away with a naughty laugh! */ - target.MoveToWorld(RandomMoongate(target), target.Map); - } - } - } - - public static Point3D RandomMoongate(Mobile target) - { - return target.Map.MapID switch - { - 2 => Ilshenar_Locations.RandomElement(), - 3 => Malas_Locations.RandomElement(), - 4 => Tokuno_Locations.RandomElement(), - _ => Felucca_Locations.RandomElement() - }; + + public static Point3D RandomMoongate(Mobile target) + { + return target.Map.MapID switch + { + 2 => Ilshenar_Locations.RandomElement(), + 3 => Malas_Locations.RandomElement(), + 4 => Tokuno_Locations.RandomElement(), + _ => Felucca_Locations.RandomElement() + }; + } } } diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs index ab1f44561..9e2c2b670 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs @@ -1,18 +1,18 @@ using ModernUO.Serialization; -namespace Server.Items; - -[SerializationGenerator(0, false)] -public partial class MrPlainsCookies : Food +namespace Server.Items { - [Constructible] - public MrPlainsCookies() : base(0x160C) + [SerializationGenerator(0, false)] + public partial class MrPlainsCookies : Food { - FillFactor = 4; - Hue = 0xF4; + [Constructible] + public MrPlainsCookies() : base(0x160C) + { + Weight = 1.0; + FillFactor = 4; + Hue = 0xF4; + } + + public override string DefaultName => "Mr Plain's Cookies"; } - - public override double DefaultWeight => 1.0; - - public override string DefaultName => "Mr Plain's Cookies"; } diff --git a/Projects/UOContent/Holiday Stuff/Valentine/2011/Items/StValentinesBears.cs b/Projects/UOContent/Holiday Stuff/Valentine/2011/Items/StValentinesBears.cs index a08e5aa22..e978bcaaf 100644 --- a/Projects/UOContent/Holiday Stuff/Valentine/2011/Items/StValentinesBears.cs +++ b/Projects/UOContent/Holiday Stuff/Valentine/2011/Items/StValentinesBears.cs @@ -101,63 +101,51 @@ namespace Server.Items return; } - StValentinesBearsGump.DisplayTo(from, this); + from.SendGump(new InternalGump(this)); } - private class StValentinesBearsGump : StaticGump + private class InternalGump : Gump { - private readonly StValentinesBear _bear; + private readonly StValentinesBear m_Bear; - public override bool Singleton => true; - - protected override bool Cached => false; - - private StValentinesBearsGump(StValentinesBear bear) : base(50, 50) + public InternalGump(StValentinesBear bear) + : base(50, 50) { - _bear = bear; - } + m_Bear = bear; - public static void DisplayTo(Mobile from, StValentinesBear bear) - { - if (from?.NetState == null || bear?.Deleted != false) - { - return; - } + AddPage(0); + AddBackground(0, 0, 420, 320, 9300); + AddHtml(10, 10, 400, 21, "
St. Valentine Bear
"); + AddHtmlLocalized( + 10, + 40, + 400, + 75, + 1150293, + 0 + ); // Enter up to three lines of personalized greeting for your St. Valentine Bear. You many enter up to 25 characters per line. Once you enter text, you will only be able to correct mistakes for 10 minutes. - from.SendGump(new StValentinesBearsGump(bear)); - } + AddHtmlLocalized(10, 129, 400, 21, 1150296, 0); // Line 1: + AddBackground(10, 150, 400, 24, 9350); + AddTextEntry(15, 152, 390, 20, 0, 0, "", 25); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 420, 320, 9300); - // St. Valentine Bear - builder.AddHtmlLocalized(10, 10, 400, 21, 1154645, "#1150294", 0); + AddHtmlLocalized(10, 179, 400, 21, 1150297, 0); // Line 2: + AddBackground(10, 200, 400, 24, 9350); + AddTextEntry(15, 202, 390, 20, 0, 1, "", 25); - // Enter up to three lines of personalized greeting for your St. Valentine Bear. You many enter up to 25 characters per line. Once you enter text, you will only be able to correct mistakes for 10 minutes. - builder.AddHtmlLocalized(10, 40, 400, 75, 1150293, 0); + AddHtmlLocalized(10, 229, 400, 21, 1150298, 0); // Line 3: + AddBackground(10, 250, 400, 24, 9350); + AddTextEntry(15, 252, 390, 20, 0, 2, "", 25); - builder.AddHtmlLocalized(10, 129, 400, 21, 1150296, 0); // Line 1: - builder.AddBackground(10, 150, 400, 24, 9350); - builder.AddTextEntryLimited(15, 152, 390, 20, 0, 0, "", 25); - - builder.AddHtmlLocalized(10, 179, 400, 21, 1150297, 0); // Line 2: - builder.AddBackground(10, 200, 400, 24, 9350); - builder.AddTextEntryLimited(15, 202, 390, 20, 0, 1, "", 25); - - builder.AddHtmlLocalized(10, 229, 400, 21, 1150298, 0); // Line 3: - builder.AddBackground(10, 250, 400, 24, 9350); - builder.AddTextEntryLimited(15, 252, 390, 20, 0, 2, "", 25); - - builder.AddButton(15, 285, 242, 241, 0); - builder.AddButton(335, 285, 247, 248, 1); + AddButton(15, 285, 242, 241, 0); + AddButton(335, 285, 247, 248, 1); } public override void OnResponse(NetState sender, in RelayInfo info) { var from = sender.Mobile; - if (_bear.Deleted || !_bear.IsChildOf(from.Backpack) || !_bear.CanSign || info.ButtonID != 1) + if (m_Bear.Deleted || !m_Bear.IsChildOf(from.Backpack) || !m_Bear.CanSign || info.ButtonID != 1) { return; } @@ -182,14 +170,14 @@ namespace Server.Items return; } - if (!_bear.IsSigned) + if (!m_Bear.IsSigned) { - _bear.EditLimit = Core.Now + TimeSpan.FromMinutes(10); + m_Bear.EditLimit = Core.Now + TimeSpan.FromMinutes(10); } - _bear.Line1 = line1.FixHtml(); - _bear.Line2 = line2.FixHtml(); - _bear.Line3 = line3.FixHtml(); + m_Bear.Line1 = line1.FixHtml(); + m_Bear.Line2 = line2.FixHtml(); + m_Bear.Line3 = line3.FixHtml(); from.SendMessage("You add the personalized greeting to your St. Valentine Bear."); } diff --git a/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs b/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs index 021ae4708..d6b1b5025 100644 --- a/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs +++ b/Projects/UOContent/Holiday Stuff/Valentine/2012/Items/CupidsArrow.cs @@ -45,7 +45,7 @@ namespace Server.Items } from.SendLocalizedMessage(1152318); // You may not use this item out of season. - return true; + return false; } public override void OnSingleClick(Mobile from) diff --git a/Projects/UOContent/Items/Addons/AddonComponent.cs b/Projects/UOContent/Items/Addons/AddonComponent.cs index e19914998..ce155cec9 100644 --- a/Projects/UOContent/Items/Addons/AddonComponent.cs +++ b/Projects/UOContent/Items/Addons/AddonComponent.cs @@ -104,7 +104,7 @@ namespace Server.Items } [SerializableField(0)] - [SerializedCommandProperty(AccessLevel.GameMaster, readOnly: true)] + [SerializedCommandProperty(AccessLevel.GameMaster)] public BaseAddon _addon; [SerializableField(1)] diff --git a/Projects/UOContent/Items/Addons/AddonContainerComponent.cs b/Projects/UOContent/Items/Addons/AddonContainerComponent.cs index 6c6a3eff8..19ac684a0 100644 --- a/Projects/UOContent/Items/Addons/AddonContainerComponent.cs +++ b/Projects/UOContent/Items/Addons/AddonContainerComponent.cs @@ -91,7 +91,7 @@ namespace Server.Items public override void SendWorldPacketTo(NetState ns, ReadOnlySpan world = default) { - var buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); var length = OutgoingItemPackets.CreateWorldItem(buffer, this); // Use an itemid of a real container BinaryPrimitives.WriteUInt16BigEndian(buffer[7..9], (ushort)(_addon?.ItemID ?? 0x9AB)); diff --git a/Projects/UOContent/Items/Addons/BallotBox.cs b/Projects/UOContent/Items/Addons/BallotBox.cs index 724173740..c581a0b23 100644 --- a/Projects/UOContent/Items/Addons/BallotBox.cs +++ b/Projects/UOContent/Items/Addons/BallotBox.cs @@ -83,119 +83,102 @@ namespace Server.Items } else { - BallotBoxGump.DisplayTo(from, this, IsOwner(from)); + var isOwner = IsOwner(from); + from.SendGump(new InternalGump(this, isOwner)); } } - private class BallotBoxGump : DynamicGump + private class InternalGump : Gump { - private readonly BallotBox _box; - private readonly bool _isOwner; + private readonly BallotBox m_Box; - public override bool Singleton => true; - - private BallotBoxGump(BallotBox box, bool isOwner) : base(110, 70) + public InternalGump(BallotBox box, bool isOwner) : base(110, 70) { - _box = box; - _isOwner = isOwner; - } + m_Box = box; - public static void DisplayTo(Mobile from, BallotBox box, bool isOwner) - { - if (from?.NetState == null || box?.Deleted != false) + AddBackground(0, 0, 400, 350, 0xA28); + + if (isOwner) { - return; - } - - from.SendGump(new BallotBoxGump(box, isOwner)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 400, 350, 0xA28); - - if (_isOwner) - { - builder.AddHtmlLocalized(0, 15, 400, 35, 1011000); //
Ballot Box Owner's Menu
+ AddHtmlLocalized(0, 15, 400, 35, 1011000); //
Ballot Box Owner's Menu
} else { - builder.AddHtmlLocalized(0, 15, 400, 35, 1011001); //
Ballot Box -- Vote Here!
+ AddHtmlLocalized(0, 15, 400, 35, 1011001); //
Ballot Box -- Vote Here!
} - builder.AddHtmlLocalized(0, 50, 400, 35, 1011002); //
Topic
+ AddHtmlLocalized(0, 50, 400, 35, 1011002); //
Topic
- var lineCount = _box.Topic.Length; - builder.AddBackground(25, 90, 350, Math.Max(20 * lineCount, 20), 0x1400); + var lineCount = box.Topic.Length; + AddBackground(25, 90, 350, Math.Max(20 * lineCount, 20), 0x1400); for (var i = 0; i < lineCount; i++) { - var line = _box.Topic[i]; + var line = box.Topic[i]; if (!string.IsNullOrEmpty(line)) { - builder.AddLabelCropped(30, 90 + i * 20, 340, 20, 0x3E3, line); + AddLabelCropped(30, 90 + i * 20, 340, 20, 0x3E3, line); } } - var yesCount = _box.Yes.Count; - var noCount = _box.No.Count; + var yesCount = box.Yes.Count; + var noCount = box.No.Count; var totalVotes = yesCount + noCount; - builder.AddHtmlLocalized(0, 215, 400, 35, 1011003); //
votes
+ AddHtmlLocalized(0, 215, 400, 35, 1011003); //
votes
- if (!_isOwner) + if (!isOwner) { - builder.AddButton(20, 240, 0xFA5, 0xFA7, 3); + AddButton(20, 240, 0xFA5, 0xFA7, 3); } - builder.AddHtmlLocalized(55, 242, 25, 35, 1011004); // aye: - builder.AddLabel(78, 242, 0x0, $"[{yesCount}]"); + AddHtmlLocalized(55, 242, 25, 35, 1011004); // aye: + AddLabel(78, 242, 0x0, $"[{yesCount}]"); - if (!_isOwner) + if (!isOwner) { - builder.AddButton(20, 275, 0xFA5, 0xFA7, 4); + AddButton(20, 275, 0xFA5, 0xFA7, 4); } - builder.AddHtmlLocalized(55, 277, 25, 35, 1011005); // nay: - builder.AddLabel(78, 277, 0x0, $"[{noCount}]"); + AddHtmlLocalized(55, 277, 25, 35, 1011005); // nay: + AddLabel(78, 277, 0x0, $"[{noCount}]"); if (totalVotes > 0) { - builder.AddImageTiled(130, 242, yesCount * 225 / totalVotes, 10, 0xD6); - builder.AddImageTiled(130, 277, noCount * 225 / totalVotes, 10, 0xD6); + AddImageTiled(130, 242, yesCount * 225 / totalVotes, 10, 0xD6); + AddImageTiled(130, 277, noCount * 225 / totalVotes, 10, 0xD6); } - builder.AddButton(45, 305, 0xFA5, 0xFA7, 0); - builder.AddHtmlLocalized(80, 308, 40, 35, 1011008); // done + AddButton(45, 305, 0xFA5, 0xFA7, 0); + AddHtmlLocalized(80, 308, 40, 35, 1011008); // done - if (_isOwner) + if (isOwner) { - builder.AddButton(120, 305, 0xFA5, 0xFA7, 1); - builder.AddHtmlLocalized(155, 308, 100, 35, 1011006); // change topic + AddButton(120, 305, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(155, 308, 100, 35, 1011006); // change topic - builder.AddButton(240, 305, 0xFA5, 0xFA7, 2); - builder.AddHtmlLocalized(275, 308, 300, 100, 1011007); // reset votes + AddButton(240, 305, 0xFA5, 0xFA7, 2); + AddHtmlLocalized(275, 308, 300, 100, 1011007); // reset votes } } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_box.Deleted || info.ButtonID == 0) + if (m_Box.Deleted || info.ButtonID == 0) { return; } var from = sender.Mobile; - if (from.Map != _box.Map || !from.InRange(_box.GetWorldLocation(), 2)) + if (from.Map != m_Box.Map || !from.InRange(m_Box.GetWorldLocation(), 2)) { from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. return; } - var isOwner = _box.IsOwner(from); + var isOwner = m_Box.IsOwner(from); switch (info.ButtonID) { @@ -203,15 +186,15 @@ namespace Server.Items { if (isOwner) { - _box.ClearTopic(); - _box.ClearVotes(); + m_Box.ClearTopic(); + m_Box.ClearVotes(); from.SendLocalizedMessage( 500370, "", 0x35 ); // Enter a line of text for your ballot, and hit ENTER. Hit ESC after the last line is entered. - from.Prompt = new TopicPrompt(_box); + from.Prompt = new TopicPrompt(m_Box); } break; @@ -220,7 +203,7 @@ namespace Server.Items { if (isOwner) { - _box.ClearVotes(); + m_Box.ClearVotes(); from.SendLocalizedMessage(500371); // Votes zeroed out. } @@ -230,13 +213,13 @@ namespace Server.Items { if (!isOwner) { - if (_box.HasVoted(from)) + if (m_Box.HasVoted(from)) { from.SendLocalizedMessage(500374); // You have already voted on this ballot. } else { - _box.AddToYes(from); + m_Box.AddToYes(from); from.SendLocalizedMessage(500373); // Your vote has been registered. } } @@ -247,13 +230,13 @@ namespace Server.Items { if (!isOwner) { - if (_box.HasVoted(from)) + if (m_Box.HasVoted(from)) { from.SendLocalizedMessage(500374); // You have already voted on this ballot. } else { - _box.AddToNo(from); + m_Box.AddToNo(from); from.SendLocalizedMessage(500373); // Your vote has been registered. } } @@ -262,7 +245,7 @@ namespace Server.Items } default: { - DisplayTo(from, _box, isOwner); + from.SendGump(new InternalGump(m_Box, isOwner)); break; } } @@ -271,52 +254,52 @@ namespace Server.Items private class TopicPrompt : Prompt { - private readonly BallotBox _box; + private readonly BallotBox m_Box; - public TopicPrompt(BallotBox box) => _box = box; + public TopicPrompt(BallotBox box) => m_Box = box; public override void OnResponse(Mobile from, string text) { - if (_box.Deleted || !_box.IsOwner(from)) + if (m_Box.Deleted || !m_Box.IsOwner(from)) { return; } - if (from.Map != _box.Map || !from.InRange(_box.GetWorldLocation(), 2)) + if (from.Map != m_Box.Map || !from.InRange(m_Box.GetWorldLocation(), 2)) { from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. return; } - _box.AddLineToTopic(text.TrimEnd()); + m_Box.AddLineToTopic(text.TrimEnd()); - if (_box.Topic.Length < MaxTopicLines) + if (m_Box.Topic.Length < MaxTopicLines) { from.SendLocalizedMessage(500377, "", 0x35); // Next line or ESC to finish: - from.Prompt = new TopicPrompt(_box); + from.Prompt = new TopicPrompt(m_Box); } else { from.SendLocalizedMessage(500376, "", 0x35); // Ballot entry complete. - BallotBoxGump.DisplayTo(from, _box, true); + from.SendGump(new InternalGump(m_Box, true)); } } public override void OnCancel(Mobile from) { - if (_box.Deleted || !_box.IsOwner(from)) + if (m_Box.Deleted || !m_Box.IsOwner(from)) { return; } - if (from.Map != _box.Map || !from.InRange(_box.GetWorldLocation(), 2)) + if (from.Map != m_Box.Map || !from.InRange(m_Box.GetWorldLocation(), 2)) { from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. return; } from.SendLocalizedMessage(500376, "", 0x35); // Ballot entry complete. - BallotBoxGump.DisplayTo(from, _box, true); + from.SendGump(new InternalGump(m_Box, true)); } } } diff --git a/Projects/UOContent/Items/Addons/BaseAddon.cs b/Projects/UOContent/Items/Addons/BaseAddon.cs index f2a662f6a..340a93065 100644 --- a/Projects/UOContent/Items/Addons/BaseAddon.cs +++ b/Projects/UOContent/Items/Addons/BaseAddon.cs @@ -63,14 +63,22 @@ namespace Server.Items } } - [SerializableField(1, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(_resource); + get => _resource; + set + { + if (_resource != value) + { + _resource = value; + Hue = CraftResources.GetHue(_resource); + + InvalidateProperties(); + this.MarkDirty(); + } + } } Item IAddon.Deed => Deed; @@ -262,8 +270,7 @@ namespace Server.Items foreach (var c in Components) { - // Component can become null if the Addon property is somehow deleted, then the component itself is deleted. - c?.Delete(); + c.Delete(); } } @@ -276,12 +283,5 @@ namespace Server.Items _resource = (CraftResource)reader.ReadEncodedInt(); } } - - [AfterDeserialization] - private void AfterDeserialization() - { - // We have had issues in the past, so let's tidy it up. - _components?.Tidy(); - } } } diff --git a/Projects/UOContent/Items/Addons/BaseAddonContainer.cs b/Projects/UOContent/Items/Addons/BaseAddonContainer.cs index 8554ad4e5..ad655c24e 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonContainer.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonContainer.cs @@ -41,14 +41,22 @@ namespace Server.Items } } - [SerializableField(1, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(_resource); + get => _resource; + set + { + if (_resource != value) + { + _resource = value; + Hue = CraftResources.GetHue(_resource); + + InvalidateProperties(); + this.MarkDirty(); + } + } } public virtual bool RetainDeedHue => false; diff --git a/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs b/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs index 7d5014dee..17c9fba16 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs @@ -1,154 +1,184 @@ using System; -using ModernUO.Serialization; using Server.Engines.Craft; using Server.Multis; using Server.Spells; using Server.Targeting; -namespace Server.Items; - -[Flippable(0x14F0, 0x14EF)] -[SerializationGenerator(2)] -public abstract partial class BaseAddonContainerDeed : Item, ICraftable +namespace Server.Items { - public BaseAddonContainerDeed() : base(0x14F0) + [Flippable(0x14F0, 0x14EF)] + public abstract class BaseAddonContainerDeed : Item, ICraftable { - if (!Core.AOS) + private CraftResource m_Resource; + + public BaseAddonContainerDeed() : base(0x14F0) { - LootType = LootType.Newbied; - } - } + Weight = 1.0; - public override double DefaultWeight => 1.0; - - public abstract BaseAddonContainer Addon { get; } - - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) - { - Hue = CraftResources.GetHue(_resource); - } - - public virtual int OnCraft( - int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, - BaseTool tool, CraftItem craftItem, int resHue - ) - { - var resourceType = typeRes ?? craftItem.Resources[0].ItemType; - - Resource = CraftResources.GetFromType(resourceType); - - var context = craftSystem.GetContext(from); - - if (context?.DoNotColor == true) - { - Hue = 0; + if (!Core.AOS) + { + LootType = LootType.Newbied; + } } - return quality; - } - - private void Deserialize(IGenericReader reader, int version) - { - _resource = (CraftResource)reader.ReadInt(); - } - - public override void OnDoubleClick(Mobile from) - { - if (IsChildOf(from.Backpack)) + public BaseAddonContainerDeed(Serial serial) : base(serial) { - from.Target = new InternalTarget(this); - } - else - { - from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used. - } - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - - if (!CraftResources.IsStandard(_resource)) - { - list.Add(CraftResources.GetLocalizationNumber(_resource)); - } - } - - private class InternalTarget : Target - { - private readonly BaseAddonContainerDeed m_Deed; - - public InternalTarget(BaseAddonContainerDeed deed) : base(-1, true, TargetFlags.None) - { - m_Deed = deed; - - CheckLOS = false; } - protected override void OnTarget(Mobile from, object targeted) + public abstract BaseAddonContainer Addon { get; } + + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - var p = targeted as IPoint3D; - var map = from.Map; - - if (p == null || map == null || m_Deed.Deleted) + get => m_Resource; + set { - return; + if (m_Resource != value) + { + m_Resource = value; + Hue = CraftResources.GetHue(m_Resource); + + InvalidateProperties(); + } + } + } + + public virtual int OnCraft( + int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, + BaseTool tool, CraftItem craftItem, int resHue + ) + { + var resourceType = typeRes ?? craftItem.Resources[0].ItemType; + + Resource = CraftResources.GetFromType(resourceType); + + var context = craftSystem.GetContext(from); + + if (context?.DoNotColor == true) + { + Hue = 0; } - if (!m_Deed.IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. - return; - } + return quality; + } - var addon = m_Deed.Addon; - addon.Resource = m_Deed.Resource; + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); - SpellHelper.GetSurfaceTop(ref p); + writer.Write(1); // version - BaseHouse house = null; + // version 1 + writer.Write((int)m_Resource); + } - var res = addon.CouldFit(p, map, from, ref house); + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); - if (res == AddonFitResult.Valid) - { - addon.MoveToWorld(new Point3D(p), map); - } - else if (res == AddonFitResult.Blocked) - { - from.SendLocalizedMessage(500269); // You cannot build that there. - } - else if (res == AddonFitResult.NotInHouse) - { - from.SendLocalizedMessage(500274); // You can only place this in a house that you own! - } - else if (res == AddonFitResult.DoorsNotClosed) - { - from.SendMessage("You must close all house doors before placing this."); - } - else if (res == AddonFitResult.DoorTooClose) - { - from.SendLocalizedMessage(500271); // You cannot build near the door. - } - else if (res == AddonFitResult.NoWall) - { - from.SendLocalizedMessage(500268); // This object needs to be mounted on something. - } + var version = reader.ReadInt(); - if (res == AddonFitResult.Valid) + m_Resource = version switch { - m_Deed.Delete(); - house.Addons.Add(addon); - house.AddSecure(from, addon); + 1 => (CraftResource)reader.ReadInt(), + _ => m_Resource + }; + } + + public override void OnDoubleClick(Mobile from) + { + if (IsChildOf(from.Backpack)) + { + from.Target = new InternalTarget(this); } else { - addon.Delete(); + from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used. + } + } + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + if (!CraftResources.IsStandard(m_Resource)) + { + list.Add(CraftResources.GetLocalizationNumber(m_Resource)); + } + } + + private class InternalTarget : Target + { + private readonly BaseAddonContainerDeed m_Deed; + + public InternalTarget(BaseAddonContainerDeed deed) : base(-1, true, TargetFlags.None) + { + m_Deed = deed; + + CheckLOS = false; + } + + protected override void OnTarget(Mobile from, object targeted) + { + var p = targeted as IPoint3D; + var map = from.Map; + + if (p == null || map == null || m_Deed.Deleted) + { + return; + } + + if (m_Deed.IsChildOf(from.Backpack)) + { + var addon = m_Deed.Addon; + addon.Resource = m_Deed.Resource; + + SpellHelper.GetSurfaceTop(ref p); + + BaseHouse house = null; + + var res = addon.CouldFit(p, map, from, ref house); + + if (res == AddonFitResult.Valid) + { + addon.MoveToWorld(new Point3D(p), map); + } + else if (res == AddonFitResult.Blocked) + { + from.SendLocalizedMessage(500269); // You cannot build that there. + } + else if (res == AddonFitResult.NotInHouse) + { + from.SendLocalizedMessage(500274); // You can only place this in a house that you own! + } + else if (res == AddonFitResult.DoorsNotClosed) + { + from.SendMessage("You must close all house doors before placing this."); + } + else if (res == AddonFitResult.DoorTooClose) + { + from.SendLocalizedMessage(500271); // You cannot build near the door. + } + else if (res == AddonFitResult.NoWall) + { + from.SendLocalizedMessage(500268); // This object needs to be mounted on something. + } + + if (res == AddonFitResult.Valid) + { + m_Deed.Delete(); + house.Addons.Add(addon); + house.AddSecure(from, addon); + } + else + { + addon.Delete(); + } + } + else + { + from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. + } } } } diff --git a/Projects/UOContent/Items/Addons/BaseAddonDeed.cs b/Projects/UOContent/Items/Addons/BaseAddonDeed.cs index d6ca03ac3..49977289b 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonDeed.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonDeed.cs @@ -13,14 +13,14 @@ namespace Server.Items public BaseAddonDeed() : base(0x14F0) { + Weight = 1.0; + if (!Core.AOS) { LootType = LootType.Newbied; } } - public override double DefaultWeight => 1.0; - public abstract BaseAddon Addon { get; } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Addons/DartBoard.cs b/Projects/UOContent/Items/Addons/DartBoard.cs index 36b9f33b2..73c532091 100644 --- a/Projects/UOContent/Items/Addons/DartBoard.cs +++ b/Projects/UOContent/Items/Addons/DartBoard.cs @@ -72,7 +72,7 @@ namespace Server.Items var rand = Utility.RandomDouble(); - var message = rand switch + int message = rand switch { < 0.05 => 500752, < 0.20 => 500753, diff --git a/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs b/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs index decf3ca65..f84c959cf 100644 --- a/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs +++ b/Projects/UOContent/Items/Addons/FlourMillEastAddon.cs @@ -48,19 +48,17 @@ namespace Server.Items [CommandProperty(AccessLevel.GameMaster)] public int MaxFlour => 2; - [SerializableField(0, fieldChanged: nameof(OnCurFlourChanged), allowFieldChange: nameof(AllowCurFlourChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _curFlour; - - private bool AllowCurFlourChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int CurFlour { - value = Math.Clamp(value, 0, MaxFlour); - return true; - } - - private void OnCurFlourChanged(int oldValue, int newValue) - { - UpdateStage(); + get => _curFlour; + set + { + _curFlour = Math.Clamp(value, 0, MaxFlour); + UpdateStage(); + this.MarkDirty(); + } } public void StartWorking(Mobile from) diff --git a/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs b/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs index fa48aa83c..cbd0e2cc3 100644 --- a/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs +++ b/Projects/UOContent/Items/Addons/FlourMillSouthAddon.cs @@ -35,19 +35,16 @@ namespace Server.Items [CommandProperty(AccessLevel.GameMaster)] public int MaxFlour => 2; - [SerializableField(0, fieldChanged: nameof(OnCurFlourChanged), allowFieldChange: nameof(AllowCurFlourChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _curFlour; - - private bool AllowCurFlourChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int CurFlour { - value = Math.Max(0, Math.Min(value, MaxFlour)); - return true; - } - - private void OnCurFlourChanged(int oldValue, int newValue) - { - UpdateStage(); + get => _curFlour; + set + { + _curFlour = Math.Max(0, Math.Min(value, MaxFlour)); + UpdateStage(); + } } public void StartWorking(Mobile from) diff --git a/Projects/UOContent/Items/Addons/SHTeleporter.cs b/Projects/UOContent/Items/Addons/SHTeleporter.cs index e6ce612b4..f34f9cb85 100644 --- a/Projects/UOContent/Items/Addons/SHTeleporter.cs +++ b/Projects/UOContent/Items/Addons/SHTeleporter.cs @@ -25,27 +25,35 @@ namespace Server.Items _teleOffset = offset; } - [SerializableField(0, fieldChanged: nameof(OnActiveChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private bool _active; - - private void OnActiveChanged(bool oldValue, bool newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public bool Active { - if (Addon is SHTeleporter sourceAddon) + get => _active; + set { - sourceAddon.ChangeActive(newValue); + _active = value; + + if (Addon is SHTeleporter sourceAddon) + { + sourceAddon.ChangeActive(value); + } } } - [SerializableField(1, fieldChanged: nameof(OnTeleDestChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private SHTeleComponent _teleDest; - - private void OnTeleDestChanged(SHTeleComponent oldValue, SHTeleComponent newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public SHTeleComponent TeleDest { - if (Addon is SHTeleporter sourceAddon) + get => _teleDest; + set { - sourceAddon.ChangeDest(newValue); + _teleDest = value; + + if (Addon is SHTeleporter sourceAddon) + { + sourceAddon.ChangeDest(value); + } } } diff --git a/Projects/UOContent/Items/Aquarium/Aquarium.cs b/Projects/UOContent/Items/Aquarium/Aquarium.cs index 054fea588..34990100a 100644 --- a/Projects/UOContent/Items/Aquarium/Aquarium.cs +++ b/Projects/UOContent/Items/Aquarium/Aquarium.cs @@ -31,9 +31,9 @@ namespace Server.Items private bool m_EvaluateDay; [SerializableField(0, setter: "private")] - [DeserializeTimer(nameof(DeserializeEvaluateTimer), wallClock: true)] private Timer _evaluateTimer; + [DeserializeTimerField(0)] private void DeserializeEvaluateTimer(TimeSpan delay) { _evaluateTimer = Timer.DelayCall(delay, EvaluationInterval, Evaluate); @@ -167,8 +167,9 @@ namespace Server.Items } public virtual bool HasAccess(Mobile from) => - from?.Deleted == false && (from.AccessLevel >= AccessLevel.GameMaster || - BaseHouse.FindHouseAt(this)?.IsCoOwner(from) == true); + from?.Deleted == false && ( + from.AccessLevel >= AccessLevel.GameMaster || + BaseHouse.FindHouseAt(this)?.IsCoOwner(from) == true); public override bool OnDragDrop(Mobile from, Item dropped) { @@ -550,25 +551,25 @@ namespace Server.Items public virtual void KillFish(int amount) { - using var toKill = PooledRefList.Create(); + var toKill = new List(); for (var i = 0; i < Items.Count; i++) { - if (Items[i] is BaseFish { Dead: false } fish) + if (Items[i] is BaseFish) { - toKill.Add(fish); + var fish = (BaseFish)Items[i]; + + if (!fish.Dead) + { + toKill.Add(fish); + } } } - toKill.Shuffle(); - if (amount > toKill.Count) + while (amount > 0 && toKill.Count > 0) { - amount = toKill.Count; - } - - for (var i = 0; i < amount; i++) - { - (toKill[i] as BaseFish)!.Kill(); + var kill = toKill.TakeRandomElement(); + kill.Kill(); amount -= 1; LiveCreatures = Math.Max(LiveCreatures - 1, 0); @@ -875,7 +876,7 @@ namespace Server.Items return; } - AquariumGump.DisplayTo(from, this); + from.SendGump(new AquariumGump(this, HasAccess(from)), true); from.PlaySound(0x5A4); } @@ -1127,7 +1128,7 @@ namespace Server.Items return; } - AquariumGump.DisplayTo(from, aquarium); + from.SendGump(new AquariumGump(aquarium, true)); } } diff --git a/Projects/UOContent/Items/Aquarium/AquariumGump.cs b/Projects/UOContent/Items/Aquarium/AquariumGump.cs index ed33cf3ba..d35690a32 100644 --- a/Projects/UOContent/Items/Aquarium/AquariumGump.cs +++ b/Projects/UOContent/Items/Aquarium/AquariumGump.cs @@ -1,111 +1,105 @@ using Server.Gumps; using Server.Network; -namespace Server.Items; - -public class AquariumGump : DynamicGump +namespace Server.Items { - private readonly Aquarium _aquarium; - private readonly bool _edit; - - public override bool Singleton => true; - - private AquariumGump(Aquarium aquarium, bool edit) : base(100, 100) + public class AquariumGump : Gump { - _aquarium = aquarium; - _edit = edit; - } + private readonly Aquarium m_Aquarium; - public static void DisplayTo(Mobile from, Aquarium aquarium) - { - if (from?.NetState == null || aquarium?.Deleted != false) + public AquariumGump(Aquarium aquarium, bool edit) : base(100, 100) { - return; + m_Aquarium = aquarium; + + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; + + AddPage(0); + AddBackground(0, 0, 350, 323, 0xE10); + AddImage(0, 0, 0x2C96); + + if (m_Aquarium.Items.Count == 0) + { + return; + } + + for (var i = 1; i <= m_Aquarium.Items.Count; i++) + { + DisplayPage(i, edit); + } } - from.SendGump(new AquariumGump(aquarium, aquarium.HasAccess(from))); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 350, 323, 0xE10); - builder.AddImage(0, 0, 0x2C96); - - for (var i = 1; i <= _aquarium.Items.Count; i++) + public void DisplayPage(int page, bool edit) { - DisplayPage(ref builder, i, _edit); - } - } + AddPage(page); - private void DisplayPage(ref DynamicGumpBuilder builder, int page, bool edit) - { - builder.AddPage(page); + var item = m_Aquarium.Items[page - 1]; - var item = _aquarium.Items[page - 1]; + // item name + if (item.LabelNumber != 0) + { + AddHtmlLocalized(20, 217, 250, 20, item.LabelNumber, 0x7FFF); // Name + } - // item name - if (item.LabelNumber != 0) - { - builder.AddHtmlLocalized(20, 217, 250, 20, item.LabelNumber, 0x7FFF); // Name + // item details + if (item is BaseFish fish) + { + AddHtmlLocalized(20, 239, 315, 20, fish.GetDescription(), 0x7FFF); + } + else + { + AddHtmlLocalized(20, 239, 315, 20, 1073634, 0x7FFF); // An aquarium decoration + } + + // item image + AddItem(150, 80, item.ItemID, item.Hue); + + // item number / all items + AddHtml(20, 195, 250, 20, $"{page}/{m_Aquarium.Items.Count}".Color(0xFFFFFF)); + + // remove item + if (edit) + { + AddBackground(230, 195, 100, 26, 0x13BE); + AddButton(235, 200, 0x845, 0x846, page); + AddHtmlLocalized(260, 198, 60, 26, 1073838, 0x0); // Remove + } + + // next page + if (page < m_Aquarium.Items.Count) + { + AddButton(195, 280, 0xFA5, 0xFA7, 0, GumpButtonType.Page, page + 1); + AddHtmlLocalized(230, 283, 100, 18, 1044045, 0x7FFF); // NEXT PAGE + } + + // previous page + if (page > 1) + { + AddButton(45, 280, 0xFAE, 0xFAF, 0, GumpButtonType.Page, page - 1); + AddHtmlLocalized(80, 283, 100, 18, 1044044, 0x7FFF); // PREV PAGE + } } - // item details - if (item is BaseFish fish) + public override void OnResponse(NetState sender, in RelayInfo info) { - builder.AddHtmlLocalized(20, 239, 315, 20, fish.GetDescription(), 0x7FFF); - } - else - { - builder.AddHtmlLocalized(20, 239, 315, 20, 1073634, 0x7FFF); // An aquarium decoration - } + if (m_Aquarium?.Deleted != false) + { + return; + } - // item image - builder.AddItem(150, 80, item.ItemID, item.Hue); + var edit = m_Aquarium.HasAccess(sender.Mobile); - // item number / all items - builder.AddHtml(20, 195, 250, 20, Html.Color($"{page}/{_aquarium.Items.Count}", 0xFFFFFF)); + if (info.ButtonID > 0 && info.ButtonID <= m_Aquarium.Items.Count && edit) + { + m_Aquarium.RemoveItem(sender.Mobile, info.ButtonID - 1); + } - // remove item - if (edit) - { - builder.AddBackground(230, 195, 100, 26, 0x13BE); - builder.AddButton(235, 200, 0x845, 0x846, page); - builder.AddHtmlLocalized(260, 198, 60, 26, 1073838, 0x0); // Remove - } - - // next page - if (page < _aquarium.Items.Count) - { - builder.AddButton(195, 280, 0xFA5, 0xFA7, 0, GumpButtonType.Page, page + 1); - builder.AddHtmlLocalized(230, 283, 100, 18, 1044045, 0x7FFF); // NEXT PAGE - } - - // previous page - if (page > 1) - { - builder.AddButton(45, 280, 0xFAE, 0xFAF, 0, GumpButtonType.Page, page - 1); - builder.AddHtmlLocalized(80, 283, 100, 18, 1044044, 0x7FFF); // PREV PAGE - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (_aquarium?.Deleted != false) - { - return; - } - - var edit = _aquarium.HasAccess(sender.Mobile); - - if (info.ButtonID > 0 && info.ButtonID <= _aquarium.Items.Count && edit) - { - _aquarium.RemoveItem(sender.Mobile, info.ButtonID - 1); - } - - if (info.ButtonID > 0) - { - sender.Mobile.SendGump(this); + if (info.ButtonID > 0) + { + sender.Mobile.SendGump(new AquariumGump(m_Aquarium, edit)); + } } } } diff --git a/Projects/UOContent/Items/Aquarium/AquariumState.cs b/Projects/UOContent/Items/Aquarium/AquariumState.cs index 75fe6fc6f..cdd59d9d9 100644 --- a/Projects/UOContent/Items/Aquarium/AquariumState.cs +++ b/Projects/UOContent/Items/Aquarium/AquariumState.cs @@ -30,14 +30,19 @@ namespace Server.Items public AquariumState(Aquarium parent) => _aquarium = parent; - [SerializableField(0, allowFieldChange: nameof(AllowStateChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _state; - - private bool AllowStateChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int State { - value = Math.Clamp(value, 0, 4); - return true; + get => _state; + set + { + if (_state != value) + { + _state = Math.Clamp(value, 0, 4); + MarkDirty(); + } + } } [SerializableField(1)] diff --git a/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs b/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs index 3336ebd0b..ae50b2e81 100644 --- a/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs +++ b/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs @@ -8,15 +8,20 @@ namespace Server.Items [Constructible] public WaterloggedBoots() : base(0x1711) { - ItemID = Utility.RandomBool() + if (Utility.RandomBool()) + { // thigh boots - ? 0x1711 + ItemID = 0x1711; + Weight = 4.0; + } + else + { // boots - : 0x170B; + ItemID = 0x170B; + Weight = 3.0; + } } - public override double DefaultWeight => ItemID == 0x1711 ? 4.0 : 3.0; - public override int LabelNumber => 1074364; // Waterlogged boots public override void AddNameProperties(IPropertyList list) diff --git a/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs b/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs index f15a7fd6a..3135850a9 100644 --- a/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs +++ b/Projects/UOContent/Items/Armor/BaseArmor.Migrations.cs @@ -1,40 +1,9 @@ -using System; using AMA = Server.Items.ArmorMeditationAllowance; namespace Server.Items; public partial class BaseArmor { - // PlayerConstructed moved onto Item - private void MigrateFrom(V9Content content) - { - _attributes = content.Attributes ?? AttributesDefaultValue(); - _armorAttributes = content.ArmorAttributes ?? ArmorAttributesDefaultValue(); - _physicalBonus = content.PhysicalBonus ?? 0; - _fireBonus = content.FireBonus ?? 0; - _coldBonus = content.ColdBonus ?? 0; - _poisonBonus = content.PoisonBonus ?? 0; - _energyBonus = content.EnergyBonus ?? 0; - _identified = content.Identified; - _maxHitPoints = content.MaxHitPoints ?? 0; - _hitPoints = content.HitPoints ?? 0; - _crafter = content.Crafter; - _quality = content.Quality ?? ArmorQuality.Regular; - _durability = content.Durability ?? ArmorDurabilityLevel.Regular; - _protectionLevel = content.ProtectionLevel ?? ArmorProtectionLevel.Regular; - _resource = content.Resource ?? DefaultResource; - _armorBase = content.BaseArmorRating ?? -1; - _strBonus = content.StrBonus ?? -1; - _dexBonus = content.DexBonus ?? -1; - _intBonus = content.IntBonus ?? -1; - _strReq = content.StrRequirement ?? -1; - _dexReq = content.DexRequirement ?? -1; - _intReq = content.IntRequirement ?? -1; - _meditate = content.MeditationAllowance ?? (AMA)(-1); - _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); - PlayerConstructed = content.PlayerConstructed; - } - private void MigrateFrom(V8Content content) { _attributes = content.Attributes ?? AttributesDefaultValue(); @@ -60,7 +29,7 @@ public partial class BaseArmor _intReq = content.IntRequirement ?? -1; _meditate = content.MeditationAllowance ?? (AMA)(-1); _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); - PlayerConstructed = content.PlayerConstructed; + _playerConstructed = content.PlayerConstructed; } // Version 7 (pre-codegen) @@ -193,37 +162,4 @@ public partial class BaseArmor PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); } - - private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; - - [Flags] - private enum OldSaveFlag - { - None = 0x00000000, - Attributes = 0x00000001, - ArmorAttributes = 0x00000002, - PhysicalBonus = 0x00000004, - FireBonus = 0x00000008, - ColdBonus = 0x00000010, - PoisonBonus = 0x00000020, - EnergyBonus = 0x00000040, - Identified = 0x00000080, - MaxHitPoints = 0x00000100, - HitPoints = 0x00000200, - Crafter = 0x00000400, - Quality = 0x00000800, - Durability = 0x00001000, - Protection = 0x00002000, - Resource = 0x00004000, - BaseArmor = 0x00008000, - StrBonus = 0x00010000, - DexBonus = 0x00020000, - IntBonus = 0x00040000, - StrReq = 0x00080000, - DexReq = 0x00100000, - IntReq = 0x00200000, - MedAllowance = 0x00400000, - SkillBonuses = 0x00800000, - PlayerConstructed = 0x01000000 - } } diff --git a/Projects/UOContent/Items/Armor/BaseArmor.cs b/Projects/UOContent/Items/Armor/BaseArmor.cs index f4062326d..560c5ea52 100644 --- a/Projects/UOContent/Items/Armor/BaseArmor.cs +++ b/Projects/UOContent/Items/Armor/BaseArmor.cs @@ -12,98 +12,101 @@ using AMT = Server.Items.ArmorMaterialType; namespace Server.Items { - [SerializationGenerator(10, false)] + [SerializationGenerator(9, false)] public abstract partial class BaseArmor : Item, IScissorable, IFactionItem, ICraftable, IWearableDurability, IAosItem, IIdentifiable { [SerializedIgnoreDupe] [SerializableField(0, setter: "private")] - [SaveFlag(nameof(ShouldSerializeAosAttributes), nameof(AttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosAttributes _attributes; + [SerializableFieldSaveFlag(0)] private bool ShouldSerializeAosAttributes() => !_attributes.IsEmpty; + [SerializableFieldDefault(0)] private AosAttributes AttributesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(1, setter: "private")] - [SaveFlag(nameof(ShouldSerializeArmorAttributes), nameof(ArmorAttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosArmorAttributes _armorAttributes; + [SerializableFieldSaveFlag(1)] private bool ShouldSerializeArmorAttributes() => !_armorAttributes.IsEmpty; + [SerializableFieldDefault(1)] private AosArmorAttributes ArmorAttributesDefaultValue() => new(this); [EncodedInt] [InvalidateProperties] [SerializableField(2)] - [SaveFlag(nameof(ShouldSerializePhysicalBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _physicalBonus; + [SerializableFieldSaveFlag(2)] private bool ShouldSerializePhysicalBonus() => _physicalBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(3)] - [SaveFlag(nameof(ShouldSerializeFireBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _fireBonus; + [SerializableFieldSaveFlag(3)] private bool ShouldSerializeFireBonus() => _fireBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(4)] - [SaveFlag(nameof(ShouldSerializeColdBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _coldBonus; + [SerializableFieldSaveFlag(4)] private bool ShouldSerializeColdBonus() => _coldBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(5)] - [SaveFlag(nameof(ShouldSerializePoisonBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _poisonBonus; + [SerializableFieldSaveFlag(5)] private bool ShouldSerializePoisonBonus() => _poisonBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(6)] - [SaveFlag(nameof(ShouldSerializeEnergyBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _energyBonus; + [SerializableFieldSaveFlag(6)] private bool ShouldSerializeEnergyBonus() => _energyBonus != 0; [SerializableField(7)] - [SaveFlag(nameof(ShouldSerializeIdentified))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _identified; + [SerializableFieldSaveFlag(7)] private bool ShouldSerializeIdentified() => _identified; [EncodedInt] [SerializableField(8)] - [SaveFlag(nameof(ShouldSerializeMaxHitPoints))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxHitPoints; + [SerializableFieldSaveFlag(8)] private bool ShouldSerializeMaxHitPoints() => _maxHitPoints != 0; [InvalidateProperties] [SerializableField(10)] - [SaveFlag(nameof(ShouldSerializeCrafter))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _crafter; + [SerializableFieldSaveFlag(10)] private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter); + [SerializableFieldSaveFlag(14)] private bool ShouldSerializeResource() => _resource != DefaultResource; // Field 15 @@ -132,14 +135,22 @@ namespace Server.Items [SerializedIgnoreDupe] [SerializableField(23, setter: "private")] - [SaveFlag(nameof(ShouldSerializeSkillBonuses), nameof(SkillBonusesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] public AosSkillBonuses _skillBonuses; + [SerializableFieldSaveFlag(23)] private bool ShouldSerializeSkillBonuses() => !_skillBonuses.IsEmpty; + [SerializableFieldDefault(23)] private AosSkillBonuses SkillBonusesDefaultValue() => new(this); + [SerializableField(24)] + [SerializedCommandProperty(AccessLevel.GameMaster)] + public bool _playerConstructed; + + [SerializableFieldSaveFlag(24)] + private bool ShouldSerializePlayerConstructed() => _playerConstructed; + private FactionItem m_FactionState; public BaseArmor(int itemID) : base(itemID) @@ -186,7 +197,6 @@ namespace Server.Items public virtual int OldIntReq => 0; [SerializableProperty(11)] - [SaveFlag(nameof(ShouldSerializeArmorQuality), nameof(QualityDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public ArmorQuality Quality { @@ -199,12 +209,13 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(11)] private bool ShouldSerializeArmorQuality() => _quality != ArmorQuality.Regular; + [SerializableFieldDefault(11)] private ArmorQuality QualityDefaultValue() => ArmorQuality.Regular; [SerializableProperty(12)] - [SaveFlag(nameof(ShouldSerializeDurability))] [CommandProperty(AccessLevel.GameMaster)] public ArmorDurabilityLevel Durability { @@ -217,24 +228,33 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(12)] private bool ShouldSerializeDurability() => _durability != ArmorDurabilityLevel.Regular; - [SerializableField(13, fieldChanged: nameof(OnProtectionLevelChanged))] - [SaveFlag(nameof(ShouldSerializeProtectionLevel))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private ArmorProtectionLevel _protectionLevel; - - private void OnProtectionLevelChanged(ArmorProtectionLevel oldValue, ArmorProtectionLevel newValue) + [SerializableProperty(13)] + [CommandProperty(AccessLevel.GameMaster)] + public ArmorProtectionLevel ProtectionLevel { - Invalidate(); - (Parent as Mobile)?.UpdateResistances(); + get => _protectionLevel; + set + { + if (_protectionLevel != value) + { + _protectionLevel = value; + + Invalidate(); + InvalidateProperties(); + + (Parent as Mobile)?.UpdateResistances(); + this.MarkDirty(); + } + } } + [SerializableFieldSaveFlag(13)] private bool ShouldSerializeProtectionLevel() => _protectionLevel != ArmorProtectionLevel.Regular; [SerializableProperty(14)] - [SaveFlag(nameof(ShouldSerializeResource), nameof(ResourceDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public CraftResource Resource { @@ -260,11 +280,11 @@ namespace Server.Items } } + [SerializableFieldDefault(14)] private CraftResource ResourceDefaultValue() => DefaultResource; [EncodedInt] [SerializableProperty(15, useField: nameof(_armorBase))] - [SaveFlag(nameof(ShouldSerializeArmorBase), nameof(ArmorBaseDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int BaseArmorRating { @@ -277,8 +297,10 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(15)] private bool ShouldSerializeArmorBase() => _armorBase != -1; + [SerializableFieldDefault(15)] private int ArmorBaseDefaultValue() => -1; public double BaseArmorRatingScaled => BaseArmorRating * ArmorScalar; @@ -291,35 +313,26 @@ namespace Server.Items if (_protectionLevel != ArmorProtectionLevel.Regular) { - if (Core.UOR) - { - ar += 10; - } - - ar += 5 * (int)_protectionLevel; + ar += 10 + 5 * (int)_protectionLevel; } - // Colored armor does not give a bonus until UOR+ - if (Core.UOR) + ar += _resource switch { - ar += _resource switch - { - CraftResource.DullCopper => 2, - CraftResource.ShadowIron => 4, - CraftResource.Copper => 6, - CraftResource.Bronze => 8, - CraftResource.Gold => 10, - CraftResource.Agapite => 12, - CraftResource.Verite => 14, - CraftResource.Valorite => 16, - CraftResource.SpinedLeather => 10, - CraftResource.HornedLeather => 13, - CraftResource.BarbedLeather => 16, - _ => 0 - }; - } + CraftResource.DullCopper => 2, + CraftResource.ShadowIron => 4, + CraftResource.Copper => 6, + CraftResource.Bronze => 8, + CraftResource.Gold => 10, + CraftResource.Agapite => 12, + CraftResource.Verite => 14, + CraftResource.Valorite => 16, + CraftResource.SpinedLeather => 10, + CraftResource.HornedLeather => 13, + CraftResource.BarbedLeather => 16, + _ => 0 + }; - ar += 8 * (int)(_quality - 1); + ar += -8 + 8 * (int)_quality; return ScaleArmorByDurability(ar); } } @@ -328,7 +341,6 @@ namespace Server.Items [EncodedInt] [SerializableProperty(16, useField: nameof(_strBonus))] - [SaveFlag(nameof(ShouldSerializeStrBonus), nameof(StrBonusDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int StrBonus { @@ -341,13 +353,14 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(16)] private bool ShouldSerializeStrBonus() => _strBonus != -1; + [SerializableFieldDefault(16)] private int StrBonusDefaultValue() => -1; [EncodedInt] [SerializableProperty(17, useField: nameof(_dexBonus))] - [SaveFlag(nameof(ShouldSerializeDexBonus), nameof(DexBonusDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int DexBonus { @@ -360,13 +373,14 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(17)] private bool ShouldSerializeDexBonus() => _dexBonus != -1; + [SerializableFieldDefault(17)] private int DexBonusDefaultValue() => -1; [EncodedInt] [SerializableProperty(18, useField: nameof(_intBonus))] - [SaveFlag(nameof(ShouldSerializeIntBonus), nameof(IntBonusDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int IntBonus { @@ -379,13 +393,14 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(18)] private bool ShouldSerializeIntBonus() => _intBonus != -1; + [SerializableFieldDefault(18)] private int IntBonusDefaultValue() => -1; [EncodedInt] [SerializableProperty(19, useField: nameof(_strReq))] - [SaveFlag(nameof(ShouldSerializeStrReq), nameof(StrReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int StrRequirement { @@ -398,13 +413,14 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(19)] private bool ShouldSerializeStrReq() => _strReq != -1; + [SerializableFieldDefault(19)] private int StrReqDefaultValue() => -1; [EncodedInt] [SerializableProperty(20, useField: nameof(_dexReq))] - [SaveFlag(nameof(ShouldSerializeDexReq), nameof(DexReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int DexRequirement { @@ -417,13 +433,14 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(20)] private bool ShouldSerializeDexReq() => _dexReq != -1; + [SerializableFieldDefault(20)] private int DexReqDefaultValue() => -1; [EncodedInt] [SerializableProperty(21, useField: nameof(_intReq))] - [SaveFlag(nameof(ShouldSerializeIntReq), nameof(IntReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int IntRequirement { @@ -436,12 +453,13 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(21)] private bool ShouldSerializeIntReq() => _intReq != -1; + [SerializableFieldDefault(21)] private int IntReqDefaultValue() => -1; [SerializableProperty(22, useField: nameof(_meditate))] - [SaveFlag(nameof(ShouldSerializeMeditationAllowance))] [CommandProperty(AccessLevel.GameMaster)] public AMA MeditationAllowance { @@ -453,6 +471,7 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(22)] private bool ShouldSerializeMeditationAllowance() => _meditate >= AMA.All; public virtual double ArmorScalar @@ -527,7 +546,7 @@ namespace Server.Items } } - public virtual int OnCraft( + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) @@ -542,6 +561,7 @@ namespace Server.Items var resourceType = typeRes ?? craftItem.Resources[0].ItemType; Resource = CraftResources.GetFromType(resourceType); + PlayerConstructed = true; Identified = true; var context = craftSystem.GetContext(from); @@ -558,9 +578,8 @@ namespace Server.Items { DistributeBonuses( tool is BaseRunicTool ? 6 : - // Not sure since when, but right now 15 points are added, not 14. Core.SE ? 15 : 14 - ); + ); // Not sure since when, but right now 15 points are added, not 14. } if (Core.ML && this is not BaseShield) @@ -572,30 +591,20 @@ namespace Server.Items switch (Utility.Random(5)) { case 0: - { - PhysicalBonus++; - break; - } + PhysicalBonus++; + break; case 1: - { - FireBonus++; - break; - } + FireBonus++; + break; case 2: - { - ColdBonus++; - break; - } + ColdBonus++; + break; case 3: - { - EnergyBonus++; - break; - } + EnergyBonus++; + break; case 4: - { - PoisonBonus++; - break; - } + PoisonBonus++; + break; } } @@ -668,7 +677,6 @@ namespace Server.Items [EncodedInt] [SerializableProperty(9)] - [SaveFlag(nameof(ShouldSerializeHitPoints))] [CommandProperty(AccessLevel.GameMaster)] public int HitPoints { @@ -696,6 +704,7 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(9)] private bool ShouldSerializeHitPoints() => _hitPoints != 0; public virtual int InitMinHits => 0; @@ -796,7 +805,7 @@ namespace Server.Items public int ComputeStatReq(StatType type) { - var v = type switch + int v = type switch { StatType.Str => StrRequirement, StatType.Dex => DexRequirement, @@ -823,30 +832,20 @@ namespace Server.Items switch (Utility.Random(5)) { case 0: - { - ++PhysicalBonus; - break; - } + ++PhysicalBonus; + break; case 1: - { - ++FireBonus; - break; - } + ++FireBonus; + break; case 2: - { - ++ColdBonus; - break; - } + ++ColdBonus; + break; case 3: - { - ++PoisonBonus; - break; - } + ++PoisonBonus; + break; case 4: - { - ++EnergyBonus; - break; - } + ++EnergyBonus; + break; } } @@ -870,12 +869,9 @@ namespace Server.Items public int GetDurabilityBonus() { - if (!Core.UOR) - { - return (int)_durability * 5 + ((int)_quality - 1) * 10; - } + var bonus = _quality == ArmorQuality.Exceptional ? 20 : 0; - var bonus = _durability switch + bonus += _durability switch { ArmorDurabilityLevel.Durable => 20, ArmorDurabilityLevel.Substantial => 50, @@ -887,11 +883,23 @@ namespace Server.Items if (Core.AOS) { + bonus += ArmorAttributes.DurabilityBonus; + var resInfo = CraftResources.GetInfo(_resource); - bonus += ArmorAttributes.DurabilityBonus + (resInfo?.AttributeInfo?.ArmorDurability ?? 0); + CraftAttributeInfo attrInfo = null; + + if (resInfo != null) + { + attrInfo = resInfo.AttributeInfo; + } + + if (attrInfo != null) + { + bonus += attrInfo.ArmorDurability; + } } - return _quality == ArmorQuality.Exceptional ? bonus + 20 : bonus; + return bonus; } public static void ValidateMobile(Mobile m) @@ -997,6 +1005,8 @@ namespace Server.Items (Parent as Mobile)?.Delta(MobileDelta.Armor); // Tell them armor rating has changed } + private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; + [AfterDeserialization] private void AfterDeserialization() { @@ -1051,69 +1061,67 @@ namespace Server.Items public override bool CanEquip(Mobile from) { - if (!from.Player || from.AccessLevel >= AccessLevel.GameMaster) - { - return base.CanEquip(from); - } - if (!Ethic.CheckEquip(from, this)) { return false; } - if (!CheckRace(from)) + if (from.AccessLevel < AccessLevel.GameMaster) { - return false; - } - - if (!AllowMaleWearer && !from.Female) - { - if (AllowFemaleWearer) + if (!CheckRace(from)) { - from.SendLocalizedMessage(1010388); // Only females can wear this. - } - else - { - from.SendMessage("You may not wear this."); + return false; } - return false; - } + if (!AllowMaleWearer && !from.Female) + { + if (AllowFemaleWearer) + { + from.SendLocalizedMessage(1010388); // Only females can wear this. + } + else + { + from.SendMessage("You may not wear this."); + } - if (!AllowFemaleWearer && from.Female) - { - if (AllowMaleWearer) - { - from.SendLocalizedMessage(1063343); // Only males can wear this. - } - else - { - from.SendMessage("You may not wear this."); + return false; } - return false; - } + if (!AllowFemaleWearer && from.Female) + { + if (AllowMaleWearer) + { + from.SendLocalizedMessage(1063343); // Only males can wear this. + } + else + { + from.SendMessage("You may not wear this."); + } - int strBonus = ComputeStatBonus(StatType.Str), strReq = ComputeStatReq(StatType.Str); - int dexBonus = ComputeStatBonus(StatType.Dex), dexReq = ComputeStatReq(StatType.Dex); - int intBonus = ComputeStatBonus(StatType.Int), intReq = ComputeStatReq(StatType.Int); + return false; + } - if (from.Dex < dexReq || from.Dex + dexBonus < 1) - { - from.SendLocalizedMessage(502077); // You do not have enough dexterity to equip this item. - return false; - } + int strBonus = ComputeStatBonus(StatType.Str), strReq = ComputeStatReq(StatType.Str); + int dexBonus = ComputeStatBonus(StatType.Dex), dexReq = ComputeStatReq(StatType.Dex); + int intBonus = ComputeStatBonus(StatType.Int), intReq = ComputeStatReq(StatType.Int); - if (from.Str < strReq || from.Str + strBonus < 1) - { - from.SendLocalizedMessage(500213); // You are not strong enough to equip that. - return false; - } + if (from.Dex < dexReq || from.Dex + dexBonus < 1) + { + from.SendLocalizedMessage(502077); // You do not have enough dexterity to equip this item. + return false; + } - if (from.Int < intReq || from.Int + intBonus < 1) - { - from.SendMessage("You are not smart enough to equip that."); - return false; + if (from.Str < strReq || from.Str + strBonus < 1) + { + from.SendLocalizedMessage(500213); // You are not strong enough to equip that. + return false; + } + + if (from.Int < intReq || from.Int + intBonus < 1) + { + from.SendMessage("You are not smart enough to equip that."); + return false; + } } return base.CanEquip(from); @@ -1292,9 +1300,140 @@ namespace Server.Items list.Add(1061078, prop); // artifact rarity ~1_val~ } - ArmorAttributes.GetProperties(list); + if ((prop = Attributes.WeaponDamage) != 0) + { + list.Add(1060401, prop); // damage increase ~1_val~% + } - Attributes.GetProperties(list, luckBonus: GetLuckBonus()); + if ((prop = Attributes.DefendChance) != 0) + { + list.Add(1060408, prop); // defense chance increase ~1_val~% + } + + if ((prop = Attributes.BonusDex) != 0) + { + list.Add(1060409, prop); // dexterity bonus ~1_val~ + } + + if ((prop = Attributes.EnhancePotions) != 0) + { + list.Add(1060411, prop); // enhance potions ~1_val~% + } + + if ((prop = Attributes.CastRecovery) != 0) + { + list.Add(1060412, prop); // faster cast recovery ~1_val~ + } + + if ((prop = Attributes.CastSpeed) != 0) + { + list.Add(1060413, prop); // faster casting ~1_val~ + } + + if ((prop = Attributes.AttackChance) != 0) + { + list.Add(1060415, prop); // hit chance increase ~1_val~% + } + + if ((prop = Attributes.BonusHits) != 0) + { + list.Add(1060431, prop); // hit point increase ~1_val~ + } + + if ((prop = Attributes.BonusInt) != 0) + { + list.Add(1060432, prop); // intelligence bonus ~1_val~ + } + + if ((prop = Attributes.LowerManaCost) != 0) + { + list.Add(1060433, prop); // lower mana cost ~1_val~% + } + + if ((prop = Attributes.LowerRegCost) != 0) + { + list.Add(1060434, prop); // lower reagent cost ~1_val~% + } + + if ((prop = GetLowerStatReq()) != 0) + { + list.Add(1060435, prop); // lower requirements ~1_val~% + } + + if ((prop = GetLuckBonus() + Attributes.Luck) != 0) + { + list.Add(1060436, prop); // luck ~1_val~ + } + + if (ArmorAttributes.MageArmor != 0) + { + list.Add(1060437); // mage armor + } + + if ((prop = Attributes.BonusMana) != 0) + { + list.Add(1060439, prop); // mana increase ~1_val~ + } + + if ((prop = Attributes.RegenMana) != 0) + { + list.Add(1060440, prop); // mana regeneration ~1_val~ + } + + if (Attributes.NightSight != 0) + { + list.Add(1060441); // night sight + } + + if ((prop = Attributes.ReflectPhysical) != 0) + { + list.Add(1060442, prop); // reflect physical damage ~1_val~% + } + + if ((prop = Attributes.RegenStam) != 0) + { + list.Add(1060443, prop); // stamina regeneration ~1_val~ + } + + if ((prop = Attributes.RegenHits) != 0) + { + list.Add(1060444, prop); // hit point regeneration ~1_val~ + } + + if ((prop = ArmorAttributes.SelfRepair) != 0) + { + list.Add(1060450, prop); // self repair ~1_val~ + } + + if (Attributes.SpellChanneling != 0) + { + list.Add(1060482); // spell channeling + } + + if ((prop = Attributes.SpellDamage) != 0) + { + list.Add(1060483, prop); // spell damage increase ~1_val~% + } + + if ((prop = Attributes.BonusStam) != 0) + { + list.Add(1060484, prop); // stamina increase ~1_val~ + } + + if ((prop = Attributes.BonusStr) != 0) + { + list.Add(1060485, prop); // strength bonus ~1_val~ + } + + if ((prop = Attributes.WeaponSpeed) != 0) + { + list.Add(1060486, prop); // swing speed increase ~1_val~% + } + + if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) + { + list.Add(1075210, prop); // Increased Karma Loss ~1val~% + } AddResistanceProperties(list); @@ -1303,12 +1442,6 @@ namespace Server.Items list.Add(1060410, prop); // durability ~1_val~% } - var lowerStatReq = GetLowerStatReq(); - if (lowerStatReq != 0) - { - list.Add(1060435, lowerStatReq); // lower requirements ~1_val~% - } - if ((prop = ComputeStatReq(StatType.Str)) > 0) { list.Add(1061170, prop); // strength requirement ~1_val~ @@ -1405,7 +1538,7 @@ namespace Server.Items if (isMagicItem) { - var builder = new ValueStringBuilder(stackalloc char[128]); + var builder = ValueStringBuilder.Create(128); var durabilityText = DurabilityText(out var articleAnDurability); if (durabilityText != null) @@ -1463,7 +1596,7 @@ namespace Server.Items ArmorDurabilityLevel.Substantial => "substantial", ArmorDurabilityLevel.Massive => "massive", ArmorDurabilityLevel.Fortified => "fortified", - ArmorDurabilityLevel.Indestructible => "indestructible", + ArmorDurabilityLevel.Indestructible => "indestructable", _ => null }; } @@ -1483,5 +1616,35 @@ namespace Server.Items }; } + [Flags] + private enum OldSaveFlag + { + None = 0x00000000, + Attributes = 0x00000001, + ArmorAttributes = 0x00000002, + PhysicalBonus = 0x00000004, + FireBonus = 0x00000008, + ColdBonus = 0x00000010, + PoisonBonus = 0x00000020, + EnergyBonus = 0x00000040, + Identified = 0x00000080, + MaxHitPoints = 0x00000100, + HitPoints = 0x00000200, + Crafter = 0x00000400, + Quality = 0x00000800, + Durability = 0x00001000, + Protection = 0x00002000, + Resource = 0x00004000, + BaseArmor = 0x00008000, + StrBonus = 0x00010000, + DexBonus = 0x00020000, + IntBonus = 0x00040000, + StrReq = 0x00080000, + DexReq = 0x00100000, + IntReq = 0x00200000, + MedAllowance = 0x00400000, + SkillBonuses = 0x00800000, + PlayerConstructed = 0x01000000 + } } } diff --git a/Projects/UOContent/Items/Armor/Bone/BoneArms.cs b/Projects/UOContent/Items/Armor/Bone/BoneArms.cs index 63b817a69..a5a41d647 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneArms.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneArms.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class BoneArms : BaseArmor { [Constructible] - public BoneArms() : base(0x144E) - { - } - - public override double DefaultWeight => 2.0; + public BoneArms() : base(0x144E) => Weight = 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Bone/BoneChest.cs b/Projects/UOContent/Items/Armor/Bone/BoneChest.cs index 10416ddbe..edbb82f10 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneChest.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class BoneChest : BaseArmor { [Constructible] - public BoneChest() : base(0x144F) - { - } - - public override double DefaultWeight => 6.0; + public BoneChest() : base(0x144F) => Weight = 6.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs b/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs index 707caa759..5958198ad 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class BoneGloves : BaseArmor { [Constructible] - public BoneGloves() : base(0x1450) - { - } - - public override double DefaultWeight => 2.0; + public BoneGloves() : base(0x1450) => Weight = 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs b/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs index 6ed0e9ab2..67c3f9653 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class BoneLegs : BaseArmor { [Constructible] - public BoneLegs() : base(0x1452) - { - } - - public override double DefaultWeight => 3.0; + public BoneLegs() : base(0x1452) => Weight = 3.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Chain/ChainChest.cs b/Projects/UOContent/Items/Armor/Chain/ChainChest.cs index ec10c90bc..9d4536d80 100644 --- a/Projects/UOContent/Items/Armor/Chain/ChainChest.cs +++ b/Projects/UOContent/Items/Armor/Chain/ChainChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class ChainChest : BaseArmor { [Constructible] - public ChainChest() : base(0x13BF) - { - } - - public override double DefaultWeight => 7.0; + public ChainChest() : base(0x13BF) => Weight = 7.0; public override int BasePhysicalResistance => 4; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs b/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs index c381990b0..b5fd1431f 100644 --- a/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs +++ b/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class ChainHatsuburi : BaseArmor { [Constructible] - public ChainHatsuburi() : base(0x2774) - { - } - - public override double DefaultWeight => 7.0; + public ChainHatsuburi() : base(0x2774) => Weight = 7.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs b/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs index a91777876..edfbea1e5 100644 --- a/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs +++ b/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class ChainLegs : BaseArmor { [Constructible] - public ChainLegs() : base(0x13BE) - { - } - - public override double DefaultWeight => 7.0; + public ChainLegs() : base(0x13BE) => Weight = 7.0; public override int BasePhysicalResistance => 4; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs index 6d9d14721..0d124f8f4 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishClothArmsType1 : BaseArmor { [Constructible] - public GargishClothArmsType1() : base(0x404) - { - } - - public override double DefaultWeight => 2.0; + public GargishClothArmsType1() : base(0x404) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs index 0df21c0f1..96027ec54 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishClothArmsType2 : BaseArmor { [Constructible] - public GargishClothArmsType2() : base(0x403) - { - } - - public override double DefaultWeight => 2.0; + public GargishClothArmsType2() : base(0x403) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs index 728acca2f..76f8ac65c 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishClothChestType1 : BaseArmor { [Constructible] - public GargishClothChestType1() : base(0x406) - { - } - - public override double DefaultWeight => 2.0; + public GargishClothChestType1() : base(0x406) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs index af79906ae..0867ec5c7 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishClothChestType2 : BaseArmor { [Constructible] - public GargishClothChestType2() : base(0x405) - { - } - - public override double DefaultWeight => 2.0; + public GargishClothChestType2() : base(0x405) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs index e3f6e91b1..3200848bc 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishClothKiltType1 : BaseArmor { [Constructible] - public GargishClothKiltType1() : base(0x408) - { - } - - public override double DefaultWeight => 2.0; + public GargishClothKiltType1() : base(0x408) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs index fef197852..8f9e4a486 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class GargishClothKiltType2 : BaseArmor { [Constructible] - public GargishClothKiltType2() : base(0x407) - { - } - - public override double DefaultWeight => 2.0; + public GargishClothKiltType2() : base(0x407) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs index 152bfb2df..53e724d47 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishClothLegsType1 : BaseArmor { [Constructible] - public GargishClothLegsType1() : base(0x40A) - { - } - - public override double DefaultWeight => 2.0; + public GargishClothLegsType1() : base(0x40A) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs index 7f930a0aa..0016b886f 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishClothLegsType2 : BaseArmor { [Constructible] - public GargishClothLegsType2() : base(0x409) - { - } - - public override double DefaultWeight => 2.0; + public GargishClothLegsType2() : base(0x409) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs index 1133c8410..8c22af849 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs @@ -1,39 +1,39 @@ using ModernUO.Serialization; -namespace Server.Items; - -[SerializationGenerator(0, false)] -[Flippable(0x144e, 0x1453)] -public partial class DaemonArms : BaseArmor +namespace Server.Items { - [Constructible] - public DaemonArms() : base(0x144E) + [SerializationGenerator(0, false)] + [Flippable(0x144e, 0x1453)] + public partial class DaemonArms : BaseArmor { - Hue = 0x648; + [Constructible] + public DaemonArms() : base(0x144E) + { + Weight = 2.0; + Hue = 0x648; - ArmorAttributes.SelfRepair = 1; + ArmorAttributes.SelfRepair = 1; + } + + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 7; + public override int BasePoisonResistance => 5; + public override int BaseEnergyResistance => 7; + + public override int InitMinHits => 255; + public override int InitMaxHits => 255; + + public override int AosStrReq => 55; + public override int OldStrReq => 40; + + public override int OldDexBonus => -2; + + public override int ArmorBase => 46; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; + public override CraftResource DefaultResource => CraftResource.RegularLeather; + + public override int LabelNumber => 1041371; // daemon bone arms } - - public override double DefaultWeight => 2.0; - - public override int BasePhysicalResistance => 6; - public override int BaseFireResistance => 6; - public override int BaseColdResistance => 7; - public override int BasePoisonResistance => 5; - public override int BaseEnergyResistance => 7; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; - - public override int AosStrReq => 55; - public override int OldStrReq => 40; - - public override int OldDexBonus => -2; - - public override int ArmorBase => 46; - - public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; - public override CraftResource DefaultResource => CraftResource.RegularLeather; - - public override int LabelNumber => 1041371; // daemon bone arms } diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs index bff469f7a..e2a0ec586 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs @@ -1,39 +1,39 @@ using ModernUO.Serialization; -namespace Server.Items; - -[SerializationGenerator(0, false)] -[Flippable(0x144f, 0x1454)] -public partial class DaemonChest : BaseArmor +namespace Server.Items { - [Constructible] - public DaemonChest() : base(0x144F) + [SerializationGenerator(0, false)] + [Flippable(0x144f, 0x1454)] + public partial class DaemonChest : BaseArmor { - Hue = 0x648; + [Constructible] + public DaemonChest() : base(0x144F) + { + Weight = 6.0; + Hue = 0x648; - ArmorAttributes.SelfRepair = 1; + ArmorAttributes.SelfRepair = 1; + } + + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 7; + public override int BasePoisonResistance => 5; + public override int BaseEnergyResistance => 7; + + public override int InitMinHits => 255; + public override int InitMaxHits => 255; + + public override int AosStrReq => 60; + public override int OldStrReq => 40; + + public override int OldDexBonus => -6; + + public override int ArmorBase => 46; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; + public override CraftResource DefaultResource => CraftResource.RegularLeather; + + public override int LabelNumber => 1041372; // daemon bone armor } - - public override double DefaultWeight => 6.0; - - public override int BasePhysicalResistance => 6; - public override int BaseFireResistance => 6; - public override int BaseColdResistance => 7; - public override int BasePoisonResistance => 5; - public override int BaseEnergyResistance => 7; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; - - public override int AosStrReq => 60; - public override int OldStrReq => 40; - - public override int OldDexBonus => -6; - - public override int ArmorBase => 46; - - public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; - public override CraftResource DefaultResource => CraftResource.RegularLeather; - - public override int LabelNumber => 1041372; // daemon bone armor } diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs index 2791feba6..e7d0793cd 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs @@ -1,39 +1,39 @@ using ModernUO.Serialization; -namespace Server.Items; - -[SerializationGenerator(0, false)] -[Flippable(0x1450, 0x1455)] -public partial class DaemonGloves : BaseArmor +namespace Server.Items { - [Constructible] - public DaemonGloves() : base(0x1450) + [SerializationGenerator(0, false)] + [Flippable(0x1450, 0x1455)] + public partial class DaemonGloves : BaseArmor { - Hue = 0x648; + [Constructible] + public DaemonGloves() : base(0x1450) + { + Weight = 2.0; + Hue = 0x648; - ArmorAttributes.SelfRepair = 1; + ArmorAttributes.SelfRepair = 1; + } + + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 7; + public override int BasePoisonResistance => 5; + public override int BaseEnergyResistance => 7; + + public override int InitMinHits => 255; + public override int InitMaxHits => 255; + + public override int AosStrReq => 55; + public override int OldStrReq => 40; + + public override int OldDexBonus => -1; + + public override int ArmorBase => 46; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; + public override CraftResource DefaultResource => CraftResource.RegularLeather; + + public override int LabelNumber => 1041373; // daemon bone gloves } - - public override double DefaultWeight => 2.0; - - public override int BasePhysicalResistance => 6; - public override int BaseFireResistance => 6; - public override int BaseColdResistance => 7; - public override int BasePoisonResistance => 5; - public override int BaseEnergyResistance => 7; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; - - public override int AosStrReq => 55; - public override int OldStrReq => 40; - - public override int OldDexBonus => -1; - - public override int ArmorBase => 46; - - public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; - public override CraftResource DefaultResource => CraftResource.RegularLeather; - - public override int LabelNumber => 1041373; // daemon bone gloves } diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs index 2fee9b456..f07aeeae6 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs @@ -1,39 +1,39 @@ using ModernUO.Serialization; -namespace Server.Items; - -[SerializationGenerator(0, false)] -[Flippable(0x1452, 0x1457)] -public partial class DaemonLegs : BaseArmor +namespace Server.Items { - [Constructible] - public DaemonLegs() : base(0x1452) + [SerializationGenerator(0, false)] + [Flippable(0x1452, 0x1457)] + public partial class DaemonLegs : BaseArmor { - Hue = 0x648; + [Constructible] + public DaemonLegs() : base(0x1452) + { + Weight = 3.0; + Hue = 0x648; - ArmorAttributes.SelfRepair = 1; + ArmorAttributes.SelfRepair = 1; + } + + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 7; + public override int BasePoisonResistance => 5; + public override int BaseEnergyResistance => 7; + + public override int InitMinHits => 255; + public override int InitMaxHits => 255; + + public override int AosStrReq => 55; + public override int OldStrReq => 40; + + public override int OldDexBonus => -4; + + public override int ArmorBase => 46; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; + public override CraftResource DefaultResource => CraftResource.RegularLeather; + + public override int LabelNumber => 1041375; // daemon bone leggings } - - public override double DefaultWeight => 3.0; - - public override int BasePhysicalResistance => 6; - public override int BaseFireResistance => 6; - public override int BaseColdResistance => 7; - public override int BasePoisonResistance => 5; - public override int BaseEnergyResistance => 7; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; - - public override int AosStrReq => 55; - public override int OldStrReq => 40; - - public override int OldDexBonus => -4; - - public override int ArmorBase => 46; - - public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; - public override CraftResource DefaultResource => CraftResource.RegularLeather; - - public override int LabelNumber => 1041375; // daemon bone leggings } diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs b/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs index 9322b2114..970be0af2 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class DragonArms : BaseArmor { [Constructible] - public DragonArms() : base(0x2657) - { - } - - public override double DefaultWeight => 5.0; + public DragonArms() : base(0x2657) => Weight = 5.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs b/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs index 594834c7d..7e012296e 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class DragonChest : BaseArmor { [Constructible] - public DragonChest() : base(0x2641) - { - } - - public override double DefaultWeight => 10.0; + public DragonChest() : base(0x2641) => Weight = 10.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs b/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs index 0ce52ed2a..4d2ec9c8d 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class DragonGloves : BaseArmor { [Constructible] - public DragonGloves() : base(0x2643) - { - } - - public override double DefaultWeight => 2.0; + public DragonGloves() : base(0x2643) => Weight = 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs b/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs index 0bb900903..79190e074 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class DragonHelm : BaseArmor { [Constructible] - public DragonHelm() : base(0x2645) - { - } - - public override double DefaultWeight => 5.0; + public DragonHelm() : base(0x2645) => Weight = 5.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs b/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs index b50c99f01..2f02eea46 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class DragonLegs : BaseArmor { [Constructible] - public DragonLegs() : base(0x2647) - { - } - - public override double DefaultWeight => 6.0; + public DragonLegs() : base(0x2647) => Weight = 6.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs index e44967f0b..583f22713 100644 --- a/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs @@ -6,9 +6,11 @@ namespace Server.Items public partial class ElvenGlasses : BaseArmor { [Constructible] - public ElvenGlasses() : base(0x2FB8) => _weaponAttributes = new AosWeaponAttributes(this); - - public override double DefaultWeight => 2.0; + public ElvenGlasses() : base(0x2FB8) + { + Weight = 2; + _weaponAttributes = new AosWeaponAttributes(this); + } public override int LabelNumber => 1032216; // elven glasses @@ -31,12 +33,13 @@ namespace Server.Items public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; [SerializableField(0, setter: "private")] - [SaveFlag(nameof(ShouldSerializeWeaponAttributes), nameof(WeaponAttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] public AosWeaponAttributes _weaponAttributes; + [SerializableFieldSaveFlag(0)] private bool ShouldSerializeWeaponAttributes() => !_weaponAttributes.IsEmpty; + [SerializableFieldDefault(0)] private AosWeaponAttributes WeaponAttributesDefaultValue() => new(this); public override void AppendChildNameProperties(IPropertyList list) diff --git a/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs b/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs index 1fffb21ae..3885e397a 100644 --- a/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class Bascinet : BaseArmor { [Constructible] - public Bascinet() : base(0x140C) - { - } - - public override double DefaultWeight => 5.0; + public Bascinet() : base(0x140C) => Weight = 5.0; public override int BasePhysicalResistance => 7; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs b/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs index 8d8327706..38c8b1c2f 100644 --- a/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class BoneHelm : BaseArmor { [Constructible] - public BoneHelm() : base(0x1451) - { - } - - public override double DefaultWeight => 3.0; + public BoneHelm() : base(0x1451) => Weight = 3.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs b/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs index 6e736bacc..d1277dd77 100644 --- a/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs +++ b/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class ChainCoif : BaseArmor { [Constructible] - public ChainCoif() : base(0x13BB) - { - } - - public override double DefaultWeight => 1.0; + public ChainCoif() : base(0x13BB) => Weight = 1.0; public override int BasePhysicalResistance => 4; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Helmets/Circlet.cs b/Projects/UOContent/Items/Armor/Helmets/Circlet.cs index 8c3baeae2..7544eb134 100644 --- a/Projects/UOContent/Items/Armor/Helmets/Circlet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/Circlet.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Circlet : BaseArmor { [Constructible] - public Circlet() : base(0x2B6E) - { - } - - public override double DefaultWeight => 2.0; + public Circlet() : base(0x2B6E) => Weight = 2.0; public override int BasePhysicalResistance => 1; public override int BaseFireResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs b/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs index 4f8c3b4fd..b7a5197a4 100644 --- a/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class CloseHelm : BaseArmor { [Constructible] - public CloseHelm() : base(0x1408) - { - } - - public override double DefaultWeight => 5.0; + public CloseHelm() : base(0x1408) => Weight = 5.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs b/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs index 9ae3a8753..80f2de0b5 100644 --- a/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs @@ -10,12 +10,11 @@ namespace Server.Items public DaemonHelm() : base(0x1451) { Hue = 0x648; + Weight = 3.0; ArmorAttributes.SelfRepair = 1; } - public override double DefaultWeight => 3.0; - public override int BasePhysicalResistance => 6; public override int BaseFireResistance => 6; public override int BaseColdResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs b/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs index 62a8e99d6..3df8b821a 100644 --- a/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GemmedCirclet : BaseArmor { [Constructible] - public GemmedCirclet() : base(0x2B70) - { - } - - public override double DefaultWeight => 2.0; + public GemmedCirclet() : base(0x2B70) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Helmets/Helmet.cs b/Projects/UOContent/Items/Armor/Helmets/Helmet.cs index 2e3a22369..01b138abb 100644 --- a/Projects/UOContent/Items/Armor/Helmets/Helmet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/Helmet.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class Helmet : BaseArmor { [Constructible] - public Helmet() : base(0x140A) - { - } - - public override double DefaultWeight => 5.0; + public Helmet() : base(0x140A) => Weight = 5.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs b/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs index 84b6952b0..dfeafd35b 100644 --- a/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs +++ b/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeatherCap : BaseArmor { [Constructible] - public LeatherCap() : base(0x1DB9) - { - } - - public override double DefaultWeight => 2.0; + public LeatherCap() : base(0x1DB9) => Weight = 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs b/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs index c759b2789..84926b157 100644 --- a/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class NorseHelm : BaseArmor { [Constructible] - public NorseHelm() : base(0x140E) - { - } - - public override double DefaultWeight => 5.0; + public NorseHelm() : base(0x140E) => Weight = 5.0; public override int BasePhysicalResistance => 4; public override int BaseFireResistance => 1; diff --git a/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs b/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs index 317f6bf0c..c4e2060cf 100644 --- a/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class PlateHelm : BaseArmor { [Constructible] - public PlateHelm() : base(0x1412) - { - } - - public override double DefaultWeight => 5.0; + public PlateHelm() : base(0x1412) => Weight = 5.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs b/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs index 7c3115d08..5cd85bb25 100644 --- a/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class RavenHelm : BaseArmor { [Constructible] - public RavenHelm() : base(0x2B71) - { - } - - public override double DefaultWeight => 5.0; + public RavenHelm() : base(0x2B71) => Weight = 5.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs b/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs index 1ac6d467d..ed2b78800 100644 --- a/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class RoyalCirclet : BaseArmor { [Constructible] - public RoyalCirclet() : base(0x2B6F) - { - } - - public override double DefaultWeight => 2.0; + public RoyalCirclet() : base(0x2B6F) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs b/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs index 195ec4782..8d5e89a25 100644 --- a/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class VultureHelm : BaseArmor { [Constructible] - public VultureHelm() : base(0x2B72) - { - } - - public override double DefaultWeight => 5.0; + public VultureHelm() : base(0x2B72) => Weight = 5.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs b/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs index f4aca8eb8..91c2f3b59 100644 --- a/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class WingedHelm : BaseArmor { [Constructible] - public WingedHelm() : base(0x2B73) - { - } - - public override double DefaultWeight => 5.0; + public WingedHelm() : base(0x2B73) => Weight = 5.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs b/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs index 1e870939b..83b7bb84c 100644 --- a/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class FemaleLeafChest : BaseArmor { [Constructible] - public FemaleLeafChest() : base(0x2FCB) - { - } - - public override double DefaultWeight => 2.0; + public FemaleLeafChest() : base(0x2FCB) => Weight = 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs b/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs index 323311f20..0a6433ff9 100644 --- a/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class FemaleLeatherChest : BaseArmor { [Constructible] - public FemaleLeatherChest() : base(0x1C06) - { - } - - public override double DefaultWeight => 1.0; + public FemaleLeatherChest() : base(0x1C06) => Weight = 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs index 23d72aaf6..007d64209 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishLeatherArmsType1 : BaseArmor { [Constructible] - public GargishLeatherArmsType1() : base(0x301) - { - } - - public override double DefaultWeight => 4.0; + public GargishLeatherArmsType1() : base(0x301) => Weight = 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs index 760cc12cf..5167c8e15 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishLeatherArmsType2 : BaseArmor { [Constructible] - public GargishLeatherArmsType2() : base(0x302) - { - } - - public override double DefaultWeight => 4.0; + public GargishLeatherArmsType2() : base(0x302) => Weight = 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs index df7249273..21a38d83c 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishLeatherChestType1 : BaseArmor { [Constructible] - public GargishLeatherChestType1() : base(0x0304) - { - } - - public override double DefaultWeight => 4.0; + public GargishLeatherChestType1() : base(0x0304) => Weight = 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs index 856d5906c..5e91ef3a0 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishLeatherChestType2 : BaseArmor { [Constructible] - public GargishLeatherChestType2() : base(0x303) - { - } - - public override double DefaultWeight => 4.0; + public GargishLeatherChestType2() : base(0x303) => Weight = 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs index a585b1a6a..98d468165 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishLeatherKiltType1 : BaseArmor { [Constructible] - public GargishLeatherKiltType1() : base(0x311) - { - } - - public override double DefaultWeight => 4.0; + public GargishLeatherKiltType1() : base(0x311) => Weight = 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs index 175047183..ef7e3491c 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishLeatherKiltType2 : BaseArmor { [Constructible] - public GargishLeatherKiltType2() : base(0x310) - { - } - - public override double DefaultWeight => 4.0; + public GargishLeatherKiltType2() : base(0x310) => Weight = 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs index 4ccec8a26..6decd20a6 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishLeatherLegsType1 : BaseArmor { [Constructible] - public GargishLeatherLegsType1() : base(0x305) - { - } - - public override double DefaultWeight => 4.0; + public GargishLeatherLegsType1() : base(0x305) => Weight = 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs index 1e5f7fe68..be0e8b395 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishLeatherLegsType2 : BaseArmor { [Constructible] - public GargishLeatherLegsType2() : base(0x306) - { - } - - public override double DefaultWeight => 4.0; + public GargishLeatherLegsType2() : base(0x306) => Weight = 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs index 07aa2994e..52505040e 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishLeatherWingArmor : BaseArmor { [Constructible] - public GargishLeatherWingArmor() : base(0x457E) - { - } - - public override double DefaultWeight => 2.0; + public GargishLeatherWingArmor() : base(0x457E) => Weight = 2.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int PhysicalResistance => 0; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafArms.cs b/Projects/UOContent/Items/Armor/Leather/LeafArms.cs index 42c50d89b..2eb21fbfd 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafArms.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafArms.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeafArms : BaseArmor { [Constructible] - public LeafArms() : base(0x2FC8) - { - } - - public override double DefaultWeight => 2.0; + public LeafArms() : base(0x2FC8) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; public override int BasePhysicalResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafChest.cs b/Projects/UOContent/Items/Armor/Leather/LeafChest.cs index 08347cbbe..853aad33d 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeafChest : BaseArmor { [Constructible] - public LeafChest() : base(0x2FC5) - { - } - - public override double DefaultWeight => 2.0; + public LeafChest() : base(0x2FC5) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; public override int BasePhysicalResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs b/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs index aa023cdc3..36fb38c6e 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeafGloves : BaseArmor, IArcaneEquip { [Constructible] - public LeafGloves() : base(0x2FC6) - { - } - - public override double DefaultWeight => 2.0; + public LeafGloves() : base(0x2FC6) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; public override int BasePhysicalResistance => 2; @@ -33,26 +29,32 @@ namespace Server.Items public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; - [SerializableField(0, fieldChanged: nameof(OnCurArcaneChargesChanged))] [EncodedInt] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _curArcaneCharges; - - private void OnCurArcaneChargesChanged(int oldValue, int newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int CurArcaneCharges { - Update(); + get => _curArcaneCharges; + set + { + _curArcaneCharges = value; + InvalidateProperties(); + Update(); + } } - [SerializableField(1, fieldChanged: nameof(OnMaxArcaneChargesChanged))] [EncodedInt] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _maxArcaneCharges; - - private void OnMaxArcaneChargesChanged(int oldValue, int newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public int MaxArcaneCharges { - Update(); + get => _maxArcaneCharges; + set + { + _maxArcaneCharges = value; + InvalidateProperties(); + Update(); + } } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs b/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs index 93753a9ad..7361479ce 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeafGorget : BaseArmor { [Constructible] - public LeafGorget() : base(0x2FC7) - { - } - - public override double DefaultWeight => 2.0; + public LeafGorget() : base(0x2FC7) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; public override int BasePhysicalResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs b/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs index 54a810421..6ba3d3593 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeafLegs : BaseArmor { [Constructible] - public LeafLegs() : base(0x2FC9) - { - } - - public override double DefaultWeight => 2.0; + public LeafLegs() : base(0x2FC9) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs b/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs index 6067109aa..b627757e7 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeafTonlet : BaseArmor { [Constructible] - public LeafTonlet() : base(0x2FCA) - { - } - - public override double DefaultWeight => 2.0; + public LeafTonlet() : base(0x2FCA) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; public override int BasePhysicalResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs b/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs index 7fd2e9ce7..f092fb371 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeatherArms : BaseArmor { [Constructible] - public LeatherArms() : base(0x13CD) - { - } - - public override double DefaultWeight => 2.0; + public LeatherArms() : base(0x13CD) => Weight = 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs b/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs index 062c54aad..5e7bd8e9a 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeatherBustierArms : BaseArmor { [Constructible] - public LeatherBustierArms() : base(0x1C0A) - { - } - - public override double DefaultWeight => 1.0; + public LeatherBustierArms() : base(0x1C0A) => Weight = 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs b/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs index 23f53c223..e66f20ef1 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeatherChest : BaseArmor { [Constructible] - public LeatherChest() : base(0x13CC) - { - } - - public override double DefaultWeight => 6.0; + public LeatherChest() : base(0x13CC) => Weight = 6.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs b/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs index b453c4a77..a0450165e 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherDo : BaseArmor { [Constructible] - public LeatherDo() : base(0x27C6) - { - } - - public override double DefaultWeight => 6.0; + public LeatherDo() : base(0x27C6) => Weight = 6.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs b/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs index 297bd8ed1..cab91adcc 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeatherGloves : BaseArmor, IArcaneEquip { [Constructible] - public LeatherGloves() : base(0x13C6) - { - } - - public override double DefaultWeight => 1.0; + public LeatherGloves() : base(0x13C6) => Weight = 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; @@ -32,26 +28,32 @@ namespace Server.Items public override ArmorMeditationAllowance DefMedAllowance => ArmorMeditationAllowance.All; - [SerializableField(0, fieldChanged: nameof(OnCurArcaneChargesChanged))] [EncodedInt] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _curArcaneCharges; - - private void OnCurArcaneChargesChanged(int oldValue, int newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int CurArcaneCharges { - Update(); + get => _curArcaneCharges; + set + { + _curArcaneCharges = value; + InvalidateProperties(); + Update(); + } } - [SerializableField(1, fieldChanged: nameof(OnMaxArcaneChargesChanged))] [EncodedInt] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _maxArcaneCharges; - - private void OnMaxArcaneChargesChanged(int oldValue, int newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public int MaxArcaneCharges { - Update(); + get => _maxArcaneCharges; + set + { + _maxArcaneCharges = value; + InvalidateProperties(); + Update(); + } } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs b/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs index 0e7aca13b..42eb16daf 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherGorget : BaseArmor { [Constructible] - public LeatherGorget() : base(0x13C7) - { - } - - public override double DefaultWeight => 1.0; + public LeatherGorget() : base(0x13C7) => Weight = 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs b/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs index 83ed60c9b..a4509020d 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherHaidate : BaseArmor { [Constructible] - public LeatherHaidate() : base(0x278A) - { - } - - public override double DefaultWeight => 4.0; + public LeatherHaidate() : base(0x278A) => Weight = 4.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs b/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs index b698cab6a..6b54d1695 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherHiroSode : BaseArmor { [Constructible] - public LeatherHiroSode() : base(0x277E) - { - } - - public override double DefaultWeight => 1.0; + public LeatherHiroSode() : base(0x277E) => Weight = 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs b/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs index 31030cb6c..a37ed89c0 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherJingasa : BaseArmor { [Constructible] - public LeatherJingasa() : base(0x2776) - { - } - - public override double DefaultWeight => 3.0; + public LeatherJingasa() : base(0x2776) => Weight = 3.0; public override int BasePhysicalResistance => 4; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs b/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs index bc4d50771..2d83b9148 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeatherLegs : BaseArmor { [Constructible] - public LeatherLegs() : base(0x13CB) - { - } - - public override double DefaultWeight => 4.0; + public LeatherLegs() : base(0x13CB) => Weight = 4.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs b/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs index 4ee5720fc..5887692ab 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherMempo : BaseArmor { [Constructible] - public LeatherMempo() : base(0x277A) - { - } - - public override double DefaultWeight => 2.0; + public LeatherMempo() : base(0x277A) => Weight = 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs index c07104ed7..19d7cd049 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherNinjaHood : BaseArmor { [Constructible] - public LeatherNinjaHood() : base(0x278E) - { - } - - public override double DefaultWeight => 2.0; + public LeatherNinjaHood() : base(0x278E) => Weight = 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs index f84520e4a..c27194003 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherNinjaJacket : BaseArmor { [Constructible] - public LeatherNinjaJacket() : base(0x2793) - { - } - - public override double DefaultWeight => 5.0; + public LeatherNinjaJacket() : base(0x2793) => Weight = 5.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs index b12d00935..0112fafa3 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherNinjaMitts : BaseArmor { [Constructible] - public LeatherNinjaMitts() : base(0x2792) - { - } - - public override double DefaultWeight => 2.0; + public LeatherNinjaMitts() : base(0x2792) => Weight = 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs index 8feb71366..b21807d32 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherNinjaPants : BaseArmor { [Constructible] - public LeatherNinjaPants() : base(0x2791) - { - } - - public override double DefaultWeight => 3.0; + public LeatherNinjaPants() : base(0x2791) => Weight = 3.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs b/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs index 7742344de..b18307d99 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeatherShorts : BaseArmor { [Constructible] - public LeatherShorts() : base(0x1C00) - { - } - - public override double DefaultWeight => 3.0; + public LeatherShorts() : base(0x1C00) => Weight = 3.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs b/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs index 827dcabaa..acaaa09fb 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LeatherSkirt : BaseArmor { [Constructible] - public LeatherSkirt() : base(0x1C08) - { - } - - public override double DefaultWeight => 1.0; + public LeatherSkirt() : base(0x1C08) => Weight = 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs b/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs index b56df8973..31df6ddeb 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LeatherSuneate : BaseArmor { [Constructible] - public LeatherSuneate() : base(0x2786) - { - } - - public override double DefaultWeight => 4.0; + public LeatherSuneate() : base(0x2786) => Weight = 4.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs b/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs index 02ea392b8..e8d99a675 100644 --- a/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs +++ b/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class DecorativePlateKabuto : BaseArmor { [Constructible] - public DecorativePlateKabuto() : base(0x2778) - { - } - - public override double DefaultWeight => 6.0; + public DecorativePlateKabuto() : base(0x2778) => Weight = 6.0; public override int BasePhysicalResistance => 6; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs b/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs index 21d10ab58..76bd975b4 100644 --- a/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class FemalePlateChest : BaseArmor { [Constructible] - public FemalePlateChest() : base(0x1C04) - { - } - - public override double DefaultWeight => 4.0; + public FemalePlateChest() : base(0x1C04) => Weight = 4.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs b/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs index 538efde5c..5ac5a42ab 100644 --- a/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class FemaleElvenPlateChest : BaseArmor { [Constructible] - public FemaleElvenPlateChest() : base(0x2B6D) - { - } - - public override double DefaultWeight => 8.0; + public FemaleElvenPlateChest() : base(0x2B6D) => Weight = 8.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs b/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs index a1594b314..b35003750 100644 --- a/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs +++ b/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class HeavyPlateJingasa : BaseArmor { [Constructible] - public HeavyPlateJingasa() : base(0x2777) - { - } - - public override double DefaultWeight => 5.0; + public HeavyPlateJingasa() : base(0x2777) => Weight = 5.0; public override int BasePhysicalResistance => 7; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs b/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs index 860dbc513..1f35c50ae 100644 --- a/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs +++ b/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class LightPlateJingasa : BaseArmor { [Constructible] - public LightPlateJingasa() : base(0x2781) - { - } - - public override double DefaultWeight => 5.0; + public LightPlateJingasa() : base(0x2781) => Weight = 5.0; public override int BasePhysicalResistance => 7; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateArms.cs b/Projects/UOContent/Items/Armor/Plate/PlateArms.cs index b5428ae5b..1e4505195 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateArms.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateArms.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class PlateArms : BaseArmor { [Constructible] - public PlateArms() : base(0x1410) - { - } - - public override double DefaultWeight => 5.0; + public PlateArms() : base(0x1410) => Weight = 5.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs b/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs index 7452cb074..cc882ec5b 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class PlateBattleKabuto : BaseArmor { [Constructible] - public PlateBattleKabuto() : base(0x2785) - { - } - - public override double DefaultWeight => 6.0; + public PlateBattleKabuto() : base(0x2785) => Weight = 6.0; public override int BasePhysicalResistance => 6; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateChest.cs b/Projects/UOContent/Items/Armor/Plate/PlateChest.cs index d7efd690f..60375189a 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class PlateChest : BaseArmor { [Constructible] - public PlateChest() : base(0x1415) - { - } - - public override double DefaultWeight => 10.0; + public PlateChest() : base(0x1415) => Weight = 10.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateDo.cs b/Projects/UOContent/Items/Armor/Plate/PlateDo.cs index 29587a22e..de8ecd5f1 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateDo.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateDo.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class PlateDo : BaseArmor { [Constructible] - public PlateDo() : base(0x277D) - { - } - - public override double DefaultWeight => 10.0; + public PlateDo() : base(0x277D) => Weight = 10.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs b/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs index 828fb7d09..2303f2a83 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class PlateGloves : BaseArmor { [Constructible] - public PlateGloves() : base(0x1414) - { - } - - public override double DefaultWeight => 2.0; + public PlateGloves() : base(0x1414) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs b/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs index 612949e1d..27ea17e0f 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class PlateGorget : BaseArmor { [Constructible] - public PlateGorget() : base(0x1413) - { - } - - public override double DefaultWeight => 2.0; + public PlateGorget() : base(0x1413) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs b/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs index bbb6ca326..ae238cf3b 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class PlateHaidate : BaseArmor { [Constructible] - public PlateHaidate() : base(0x278D) - { - } - - public override double DefaultWeight => 7.0; + public PlateHaidate() : base(0x278D) => Weight = 7.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs b/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs index b9a872c6f..cb629b4ce 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class PlateHatsuburi : BaseArmor { [Constructible] - public PlateHatsuburi() : base(0x2775) - { - } - - public override double DefaultWeight => 5.0; + public PlateHatsuburi() : base(0x2775) => Weight = 5.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs b/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs index c26890a37..2f272a4a7 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class PlateHiroSode : BaseArmor { [Constructible] - public PlateHiroSode() : base(0x2780) - { - } - - public override double DefaultWeight => 3.0; + public PlateHiroSode() : base(0x2780) => Weight = 3.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs b/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs index 9a437f311..cc69bfce6 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class PlateLegs : BaseArmor { [Constructible] - public PlateLegs() : base(0x1411) - { - } - - public override double DefaultWeight => 7.0; + public PlateLegs() : base(0x1411) => Weight = 7.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs b/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs index 83b730e06..257dcce97 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class PlateMempo : BaseArmor { [Constructible] - public PlateMempo() : base(0x2779) - { - } - - public override double DefaultWeight => 3.0; + public PlateMempo() : base(0x2779) => Weight = 3.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs b/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs index 227407e5f..dce3c24fa 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class PlateSuneate : BaseArmor { [Constructible] - public PlateSuneate() : base(0x2788) - { - } - - public override double DefaultWeight => 7.0; + public PlateSuneate() : base(0x2788) => Weight = 7.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs b/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs index ad6cd96a2..3d3112286 100644 --- a/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs +++ b/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class SmallPlateJingasa : BaseArmor { [Constructible] - public SmallPlateJingasa() : base(0x2784) - { - } - - public override double DefaultWeight => 5.0; + public SmallPlateJingasa() : base(0x2784) => Weight = 5.0; public override int BasePhysicalResistance => 7; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs b/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs index 301f620c6..be98d3a65 100644 --- a/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs +++ b/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class StandardPlateKabuto : BaseArmor { [Constructible] - public StandardPlateKabuto() : base(0x2789) - { - } - - public override double DefaultWeight => 6.0; + public StandardPlateKabuto() : base(0x2789) => Weight = 6.0; public override int BasePhysicalResistance => 6; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs index 2f37cf73d..582bfed08 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class WoodlandArms : BaseArmor { [Constructible] - public WoodlandArms() : base(0x2B6C) - { - } - - public override double DefaultWeight => 5.0; + public WoodlandArms() : base(0x2B6C) => Weight = 5.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs index 81608a75a..9fb9e6567 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class WoodlandChest : BaseArmor { [Constructible] - public WoodlandChest() : base(0x2B67) - { - } - - public override double DefaultWeight => 8.0; + public WoodlandChest() : base(0x2B67) => Weight = 8.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs index 836419d06..40455c089 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class WoodlandGloves : BaseArmor { [Constructible] - public WoodlandGloves() : base(0x2B6A) - { - } - - public override double DefaultWeight => 2.0; + public WoodlandGloves() : base(0x2B6A) => Weight = 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs index 08fb63eb2..ec16588aa 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class WoodlandLegs : BaseArmor { [Constructible] - public WoodlandLegs() : base(0x2B6B) - { - } - - public override double DefaultWeight => 8.0; + public WoodlandLegs() : base(0x2B6B) => Weight = 8.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs b/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs index 1987140ec..6eb5864db 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs @@ -9,11 +9,10 @@ namespace Server.Items [Constructible] public RangerArms() : base(0x13DC) { + Weight = 4.0; Hue = 0x59C; } - public override double DefaultWeight => 4.0; - public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; public override int BaseColdResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs b/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs index d345767ef..e2a6054f5 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs @@ -9,11 +9,10 @@ namespace Server.Items [Constructible] public RangerChest() : base(0x13DB) { + Weight = 8.0; Hue = 0x59C; } - public override double DefaultWeight => 8.0; - public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; public override int BaseColdResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs b/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs index be4573cbc..b51b766e6 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs @@ -9,11 +9,10 @@ namespace Server.Items [Constructible] public RangerGloves() : base(0x13D5) { + Weight = 1.0; Hue = 0x59C; } - public override double DefaultWeight => 1.0; - public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; public override int BaseColdResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs b/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs index b6cb4c0c5..e9a4a6949 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs @@ -8,11 +8,10 @@ namespace Server.Items [Constructible] public RangerGorget() : base(0x13D6) { + Weight = 1.0; Hue = 0x59C; } - public override double DefaultWeight => 1.0; - public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; public override int BaseColdResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs b/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs index 4a728332b..7fae0424a 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs @@ -9,11 +9,10 @@ namespace Server.Items [Constructible] public RangerLegs() : base(0x13DA) { + Weight = 3.0; Hue = 0x59C; } - public override double DefaultWeight => 3.0; - public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; public override int BaseColdResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs b/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs index dfef77e71..c9d1061e0 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class RingmailArms : BaseArmor { [Constructible] - public RingmailArms() : base(0x13EE) - { - } - - public override double DefaultWeight => 15.0; + public RingmailArms() : base(0x13EE) => Weight = 15.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs b/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs index ac8275fb8..e01dd908e 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class RingmailChest : BaseArmor { [Constructible] - public RingmailChest() : base(0x13EC) - { - } - - public override double DefaultWeight => 15.0; + public RingmailChest() : base(0x13EC) => Weight = 15.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs b/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs index 450c5fe76..b57887f80 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class RingmailGloves : BaseArmor { [Constructible] - public RingmailGloves() : base(0x13EB) - { - } - - public override double DefaultWeight => 2.0; + public RingmailGloves() : base(0x13EB) => Weight = 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs b/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs index d81cb142f..189a8597d 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class RingmailLegs : BaseArmor { [Constructible] - public RingmailLegs() : base(0x13F0) - { - } - - public override double DefaultWeight => 15.0; + public RingmailLegs() : base(0x13F0) => Weight = 15.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Sets/SpiritualityHelm.cs b/Projects/UOContent/Items/Armor/Sets/SpiritualityHelm.cs deleted file mode 100644 index 571cf6fe5..000000000 --- a/Projects/UOContent/Items/Armor/Sets/SpiritualityHelm.cs +++ /dev/null @@ -1,31 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Items; - -[Flippable(0x2B10, 0x2B11)] -[SerializationGenerator(0)] -public partial class SpiritualityHelm : BaseArmor -{ - [Constructible] - public SpiritualityHelm() : base(0x2B10) - { - LootType = LootType.Blessed; - Weight = 6.0; - Hue = 0x226; - - ArmorAttributes.SelfRepair = 5; - } - - public override int LabelNumber => 1075237; // Helm of Spirituality (Virtue Armor Set) - public override int BasePhysicalResistance => 13; - public override int BaseFireResistance => 13; - public override int BaseColdResistance => 12; - public override int BasePoisonResistance => 14; - public override int BaseEnergyResistance => 13; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; - public override int AosStrReq => 25; - - public override ArmorMaterialType MaterialType => ArmorMaterialType.Plate; -} diff --git a/Projects/UOContent/Items/Armor/Sets/ValorGauntlets.cs b/Projects/UOContent/Items/Armor/Sets/ValorGauntlets.cs deleted file mode 100644 index b4e7d77f9..000000000 --- a/Projects/UOContent/Items/Armor/Sets/ValorGauntlets.cs +++ /dev/null @@ -1,36 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Items; - -[Flippable(0x2B0C, 0x2B0D)] -[SerializationGenerator(0)] -public partial class ValorGauntlets : BaseArmor -{ - [Constructible] - public ValorGauntlets() : base(0x2B0C) - { - LootType = LootType.Blessed; - Weight = 4.0; - Hue = 0x226; - - ArmorAttributes.SelfRepair = 5; - } - - public override int LabelNumber => 1075238; // Gauntlets of Valor (Virtue Armor Set) - - public override int BasePhysicalResistance => 11; - - public override int BaseFireResistance => 11; - - public override int BaseColdResistance => 13; - - public override int BasePoisonResistance => 14; - - public override int BaseEnergyResistance => 10; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; - public override int AosStrReq => 50; - - public override ArmorMaterialType MaterialType => ArmorMaterialType.Plate; -} diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs index dcd45a9b7..8e28498d0 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStoneArmsType1 : BaseArmor { [Constructible] - public GargishStoneArmsType1() : base(0x284) - { - } - - public override double DefaultWeight => 10.0; + public GargishStoneArmsType1() : base(0x284) => Weight = 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs index 9d3385ffc..9198bf879 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStoneArmsType2 : BaseArmor { [Constructible] - public GargishStoneArmsType2() : base(0x283) - { - } - - public override double DefaultWeight => 10.0; + public GargishStoneArmsType2() : base(0x283) => Weight = 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs index 39a356d53..25b39d0eb 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStoneChestType1 : BaseArmor { [Constructible] - public GargishStoneChestType1() : base(0x286) - { - } - - public override double DefaultWeight => 15.0; + public GargishStoneChestType1() : base(0x286) => Weight = 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs index 61a6f9571..81bb3b928 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStoneChestType2 : BaseArmor { [Constructible] - public GargishStoneChestType2() : base(0x285) - { - } - - public override double DefaultWeight => 15.0; + public GargishStoneChestType2() : base(0x285) => Weight = 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs index 981394574..501a9cbf2 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStoneKiltType1 : BaseArmor { [Constructible] - public GargishStoneKiltType1() : base(0x288) - { - } - - public override double DefaultWeight => 10.0; + public GargishStoneKiltType1() : base(0x288) => Weight = 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs index 953073882..43103cd72 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStoneKiltType2 : BaseArmor { [Constructible] - public GargishStoneKiltType2() : base(0x287) - { - } - - public override double DefaultWeight => 10.0; + public GargishStoneKiltType2() : base(0x287) => Weight = 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs index 74f91f340..c8a683f91 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStoneLegsType1 : BaseArmor { [Constructible] - public GargishStoneLegsType1() : base(0x28A) - { - } - - public override double DefaultWeight => 15.0; + public GargishStoneLegsType1() : base(0x28A) => Weight = 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs index 3f67f9fb7..f5ab12bbf 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStoneLegsType2 : BaseArmor { [Constructible] - public GargishStoneLegsType2() : base(0x289) - { - } - - public override double DefaultWeight => 15.0; + public GargishStoneLegsType2() : base(0x289) => Weight = 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs b/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs index 0011b1b47..9034f797a 100644 --- a/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class FemaleStuddedChest : BaseArmor { [Constructible] - public FemaleStuddedChest() : base(0x1C02) - { - } - - public override double DefaultWeight => 6.0; + public FemaleStuddedChest() : base(0x1C02) => Weight = 6.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs index 2bbf105db..9d3279ae2 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStuddedArmsType1 : BaseArmor { [Constructible] - public GargishStuddedArmsType1() : base(0x284) - { - } - - public override double DefaultWeight => 10.0; + public GargishStuddedArmsType1() : base(0x284) => Weight = 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs index aae9d4429..c30396f45 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStuddedArmsType2 : BaseArmor { [Constructible] - public GargishStuddedArmsType2() : base(0x283) - { - } - - public override double DefaultWeight => 10.0; + public GargishStuddedArmsType2() : base(0x283) => Weight = 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs index 091e8ee30..6d3305544 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStuddedChestType1 : BaseArmor { [Constructible] - public GargishStuddedChestType1() : base(0x286) - { - } - - public override double DefaultWeight => 15.0; + public GargishStuddedChestType1() : base(0x286) => Weight = 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs index 8420a9a46..ef80c6847 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStuddedChestType2 : BaseArmor { [Constructible] - public GargishStuddedChestType2() : base(0x285) - { - } - - public override double DefaultWeight => 15.0; + public GargishStuddedChestType2() : base(0x285) => Weight = 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs index 037190b79..2b61adb4d 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStuddedKiltType1 : BaseArmor { [Constructible] - public GargishStuddedKiltType1() : base(0x288) - { - } - - public override double DefaultWeight => 10.0; + public GargishStuddedKiltType1() : base(0x288) => Weight = 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs index 862df4243..a5d60040f 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStuddedKiltType2 : BaseArmor { [Constructible] - public GargishStuddedKiltType2() : base(0x287) - { - } - - public override double DefaultWeight => 10.0; + public GargishStuddedKiltType2() : base(0x287) => Weight = 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs index 86215b118..8b98f861b 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStuddedLegsType1 : BaseArmor { [Constructible] - public GargishStuddedLegsType1() : base(0x28A) - { - } - - public override double DefaultWeight => 15.0; + public GargishStuddedLegsType1() : base(0x28A) => Weight = 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs index fc94f830d..3984ad6eb 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GargishStuddedLegsType2 : BaseArmor { [Constructible] - public GargishStuddedLegsType2() : base(0x289) - { - } - - public override double DefaultWeight => 15.0; + public GargishStuddedLegsType2() : base(0x289) => Weight = 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/HideChest.cs b/Projects/UOContent/Items/Armor/Studded/HideChest.cs index 35a66edf1..b853279ba 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class HideChest : BaseArmor { [Constructible] - public HideChest() : base(0x2B74) - { - } - - public override double DefaultWeight => 6.0; + public HideChest() : base(0x2B74) => Weight = 6.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs b/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs index 3a134e729..acafa6b7f 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class HideFemaleChest : BaseArmor { [Constructible] - public HideFemaleChest() : base(0x2B79) - { - } - - public override double DefaultWeight => 6.0; + public HideFemaleChest() : base(0x2B79) => Weight = 6.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/HideGloves.cs b/Projects/UOContent/Items/Armor/Studded/HideGloves.cs index 5162e15ae..cc9bde92a 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideGloves.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideGloves.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class HideGloves : BaseArmor { [Constructible] - public HideGloves() : base(0x2B75) - { - } - - public override double DefaultWeight => 2.0; + public HideGloves() : base(0x2B75) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/HideGorget.cs b/Projects/UOContent/Items/Armor/Studded/HideGorget.cs index d47c9bc4a..831c816da 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideGorget.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideGorget.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class HideGorget : BaseArmor { [Constructible] - public HideGorget() : base(0x2B76) - { - } - - public override double DefaultWeight => 3.0; + public HideGorget() : base(0x2B76) => Weight = 3.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/HidePants.cs b/Projects/UOContent/Items/Armor/Studded/HidePants.cs index 2c24640c6..3b952d3bc 100644 --- a/Projects/UOContent/Items/Armor/Studded/HidePants.cs +++ b/Projects/UOContent/Items/Armor/Studded/HidePants.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class HidePants : BaseArmor { [Constructible] - public HidePants() : base(0x2B78) - { - } - - public override double DefaultWeight => 5.0; + public HidePants() : base(0x2B78) => Weight = 5.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs b/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs index 3fb25160a..0eb6b8b3d 100644 --- a/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs +++ b/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class HidePauldrons : BaseArmor { [Constructible] - public HidePauldrons() : base(0x2B77) - { - } - - public override double DefaultWeight => 4.0; + public HidePauldrons() : base(0x2B77) => Weight = 4.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs b/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs index 641e989f6..4fa9de780 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class StuddedArms : BaseArmor { [Constructible] - public StuddedArms() : base(0x13DC) - { - } - - public override double DefaultWeight => 4.0; + public StuddedArms() : base(0x13DC) => Weight = 4.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs b/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs index 9d0411665..3dd466c49 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class StuddedBustierArms : BaseArmor { [Constructible] - public StuddedBustierArms() : base(0x1C0C) - { - } - - public override double DefaultWeight => 1.0; + public StuddedBustierArms() : base(0x1C0C) => Weight = 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs b/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs index ff67d1e09..2698f5b09 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class StuddedChest : BaseArmor { [Constructible] - public StuddedChest() : base(0x13DB) - { - } - - public override double DefaultWeight => 8.0; + public StuddedChest() : base(0x13DB) => Weight = 8.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs b/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs index 44f788b84..692076af7 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class StuddedDo : BaseArmor { [Constructible] - public StuddedDo() : base(0x27C7) - { - } - - public override double DefaultWeight => 8.0; + public StuddedDo() : base(0x27C7) => Weight = 8.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs b/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs index f6a6052d2..aa2d585ea 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class StuddedGloves : BaseArmor { [Constructible] - public StuddedGloves() : base(0x13D5) - { - } - - public override double DefaultWeight => 1.0; + public StuddedGloves() : base(0x13D5) => Weight = 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs b/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs index 13d395382..079d99e3a 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class StuddedGorget : BaseArmor { [Constructible] - public StuddedGorget() : base(0x13D6) - { - } - - public override double DefaultWeight => 1.0; + public StuddedGorget() : base(0x13D6) => Weight = 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs b/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs index 789793aec..26b41dd45 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class StuddedHaidate : BaseArmor { [Constructible] - public StuddedHaidate() : base(0x278B) - { - } - - public override double DefaultWeight => 5.0; + public StuddedHaidate() : base(0x278B) => Weight = 5.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs b/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs index a8677a31f..0b0afd5bd 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class StuddedHiroSode : BaseArmor { [Constructible] - public StuddedHiroSode() : base(0x277F) - { - } - - public override double DefaultWeight => 1.0; + public StuddedHiroSode() : base(0x277F) => Weight = 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs b/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs index 92b63098f..85863125b 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class StuddedLegs : BaseArmor { [Constructible] - public StuddedLegs() : base(0x13DA) - { - } - - public override double DefaultWeight => 5.0; + public StuddedLegs() : base(0x13DA) => Weight = 5.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs b/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs index ab783139a..31b0b9a82 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class StuddedMempo : BaseArmor { [Constructible] - public StuddedMempo() : base(0x279D) - { - } - - public override double DefaultWeight => 2.0; + public StuddedMempo() : base(0x279D) => Weight = 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs b/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs index 36a7a2cf6..f57939ea1 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class StuddedSuneate : BaseArmor { [Constructible] - public StuddedSuneate() : base(0x27D2) - { - } - - public override double DefaultWeight => 5.0; + public StuddedSuneate() : base(0x27D2) => Weight = 5.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Body Parts/BonePile.cs b/Projects/UOContent/Items/Body Parts/BonePile.cs index 9b65e61d7..04fcadcda 100644 --- a/Projects/UOContent/Items/Body Parts/BonePile.cs +++ b/Projects/UOContent/Items/Body Parts/BonePile.cs @@ -7,9 +7,11 @@ namespace Server.Items public partial class BonePile : Item, IScissorable { [Constructible] - public BonePile() : base(0x1B09 + Utility.Random(8)) => Stackable = false; - - public override double DefaultWeight => 10.0; + public BonePile() : base(0x1B09 + Utility.Random(8)) + { + Stackable = false; + Weight = 10.0; + } public bool Scissor(Mobile from, Scissors scissors) { diff --git a/Projects/UOContent/Items/Body Parts/Head.cs b/Projects/UOContent/Items/Body Parts/Head.cs index 971c5713c..f797cdd83 100644 --- a/Projects/UOContent/Items/Body Parts/Head.cs +++ b/Projects/UOContent/Items/Body Parts/Head.cs @@ -1,6 +1,4 @@ -using System; using ModernUO.Serialization; -using Server.Mobiles; namespace Server.Items { @@ -11,7 +9,7 @@ namespace Server.Items Tournament } - [SerializationGenerator(2, false)] + [SerializationGenerator(1, false)] public partial class Head : Item { [InvalidateProperties] @@ -24,14 +22,6 @@ namespace Server.Items [SerializedCommandProperty(AccessLevel.GameMaster)] private HeadType _headType; - [SerializableField(2)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private PlayerMobile _bountyTarget; - - [SerializableField(3)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private DateTime _carvedTime; - [Constructible] public Head(string playerName) : this(HeadType.Regular, playerName) { @@ -42,7 +32,6 @@ namespace Server.Items { _headType = headType; _playerName = playerName; - _carvedTime = Core.Now; } public override string DefaultName @@ -63,21 +52,10 @@ namespace Server.Items } } - // Pre-codegen legacy data (v1 only — playerName + headType) private void Deserialize(IGenericReader reader, int version) { _playerName = reader.ReadString(); _headType = (HeadType)reader.ReadEncodedInt(); - // _bountyTarget and _carvedTime default to null / DateTime.MinValue } - - private void MigrateFrom(V1Content content) - { - _playerName = content.PlayerName; - _headType = content.HeadType; - // _bountyTarget defaults to null - // _carvedTime defaults to DateTime.MinValue (treated as expired) - } - } } diff --git a/Projects/UOContent/Items/Body Parts/RibCage.cs b/Projects/UOContent/Items/Body Parts/RibCage.cs index ecfac5be3..0b02dfce0 100644 --- a/Projects/UOContent/Items/Body Parts/RibCage.cs +++ b/Projects/UOContent/Items/Body Parts/RibCage.cs @@ -7,9 +7,11 @@ namespace Server.Items public partial class RibCage : Item, IScissorable { [Constructible] - public RibCage() : base(0x1B17 + Utility.Random(2)) => Stackable = false; - - public override double DefaultWeight => 5.0; + public RibCage() : base(0x1B17 + Utility.Random(2)) + { + Stackable = false; + Weight = 5.0; + } public bool Scissor(Mobile from, Scissors scissors) { diff --git a/Projects/UOContent/Items/Body Parts/Torso.cs b/Projects/UOContent/Items/Body Parts/Torso.cs index 3ca8e5415..8a26dbecf 100644 --- a/Projects/UOContent/Items/Body Parts/Torso.cs +++ b/Projects/UOContent/Items/Body Parts/Torso.cs @@ -6,10 +6,6 @@ namespace Server.Items public partial class Torso : Item { [Constructible] - public Torso() : base(0x1D9F) - { - } - - public override double DefaultWeight => 2.0; + public Torso() : base(0x1D9F) => Weight = 2.0; } } diff --git a/Projects/UOContent/Items/Books/BaseBook.cs b/Projects/UOContent/Items/Books/BaseBook.cs index 3d9b9f7b8..b70dcf854 100644 --- a/Projects/UOContent/Items/Books/BaseBook.cs +++ b/Projects/UOContent/Items/Books/BaseBook.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using ModernUO.Serialization; using Server.Collections; using Server.ContextMenus; @@ -19,38 +20,41 @@ namespace Server.Items [InternString] [InvalidateProperties] [SerializableField(1)] - [SaveFlag(nameof(ShouldSerializeTitle), nameof(TitleDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _title; + [SerializableFieldSaveFlag(1)] private bool ShouldSerializeTitle() => _title != DefaultContent?.Title; + [SerializableFieldDefault(1)] private string TitleDefaultValue() => DefaultContent?.Title; [InvalidateProperties] [SerializableField(2)] - [SaveFlag(nameof(ShouldSerializeAuthor), nameof(AuthorDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _author; + [SerializableFieldSaveFlag(2)] private bool ShouldSerializeAuthor() => _author != DefaultContent?.Author; + [SerializableFieldDefault(2)] private string AuthorDefaultValue() => DefaultContent?.Author; [SerializableField(3)] - [SaveFlag(nameof(ShouldSerializeWritable))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _writable; + [SerializableFieldSaveFlag(3)] private bool ShouldSerializeWritable() => _writable; [SerializedIgnoreDupe] [SerializableField(4, setter: "protected")] - [SaveFlag(nameof(ShouldSerializePages), nameof(PagesDefaultValue))] private BookPageInfo[] _pages; + [SerializableFieldSaveFlag(4)] private bool ShouldSerializePages() => DefaultContent?.IsMatch(_pages) != true; + [SerializableFieldDefault(4)] private BookPageInfo[] PagesDefaultValue() => DefaultContent?.Copy() ?? Array.Empty(); [Constructible] @@ -137,14 +141,11 @@ namespace Server.Items { get { - using var lines = PooledRefQueue.Create(256); - for (var i = 0; i < Pages.Length; i++) + var lines = new List(); + + foreach (var bpi in Pages) { - var bpi = Pages[i]; - for (var j = 0; j < bpi.Lines.Length; j++) - { - lines.Enqueue(bpi.Lines[j]); - } + lines.AddRange(bpi.Lines); } return lines.ToArray(); diff --git a/Projects/UOContent/Items/Books/BookPackets.cs b/Projects/UOContent/Items/Books/BookPackets.cs index 70ddd2ae6..806b17de2 100644 --- a/Projects/UOContent/Items/Books/BookPackets.cs +++ b/Projects/UOContent/Items/Books/BookPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BookPackets.cs * * * @@ -41,8 +41,8 @@ namespace Server.Items reader.Seek(4, SeekOrigin.Current); // Skip flags and page count - var title = reader.ReadLatin1Safe(60); - var author = reader.ReadLatin1Safe(30); + var title = reader.ReadAsciiSafe(60); + var author = reader.ReadAsciiSafe(30); book.Title = title.FixHtml(); book.Author = author.FixHtml(); diff --git a/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs b/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs index 79cf074fc..d8d785445 100644 --- a/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs +++ b/Projects/UOContent/Items/Bulletin Boards/BulletinBoard.cs @@ -1,5 +1,4 @@ using System; -using System.Buffers; using System.Runtime.CompilerServices; using ModernUO.Serialization; using Server.Collections; @@ -164,9 +163,7 @@ namespace Server.Items public virtual bool CheckRange(Mobile from) => from.AccessLevel >= AccessLevel.GameMaster || from.Map == Map && from.InRange(GetWorldLocation(), 2); - public virtual bool HandleBBRequest(int packetID, Mobile from, SpanReader reader) => false; - - public virtual void PostMessage(Mobile from, BulletinMessage thread, string subject, string[] lines) + public void PostMessage(Mobile from, BulletinMessage thread, string subject, string[] lines) { if (thread != null) { diff --git a/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs b/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs index ff4de5106..c88e5e396 100644 --- a/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs +++ b/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BulletinBoardPackets.cs * * * @@ -58,11 +58,6 @@ namespace Server.Network return; } - if (board.HandleBBRequest(packetID, from, reader)) - { - return; - } - switch (packetID) { case 3: @@ -197,16 +192,7 @@ namespace Server.Network } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void UpdateLengthCounters(this string text, ref int maxLength, ref int longestTextLine, bool pad = false) - { - var line = Math.Min(255, text.Length); - var byteCount = TextEncoding.UTF8.GetMaxByteCount(line) + (pad ? 3 : 2); // 1 + length + terminator - maxLength += byteCount; - longestTextLine = Math.Max(byteCount, longestTextLine); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void UpdateLengthCounters(this ReadOnlySpan text, ref int maxLength, ref int longestTextLine, bool pad = false) + private static void UpdateLengthCounters(this string text, ref int maxLength, ref int longestTextLine, bool pad = false) { var line = Math.Min(255, text.Length); var byteCount = TextEncoding.UTF8.GetMaxByteCount(line) + (pad ? 3 : 2); // 1 + length + terminator @@ -238,7 +224,7 @@ namespace Server.Network if (content) { maxLength += 2 + equipLength * 4; // We have an extra 4 from the thread serial - for (var i = 0; i < linesLength; i++) + for (int i = 0; i < linesLength; i++) { msg.Lines[i].UpdateLengthCounters(ref maxLength, ref longestTextLine, true); } @@ -287,24 +273,7 @@ namespace Server.Network writer.Dispose(); } - internal static void WriteString(this ref SpanWriter writer, string text, Span buffer, bool pad = false) - { - var tail = pad ? 2 : 1; - var length = Math.Min(pad ? 253 : 254, text.GetBytesUtf8(buffer)); - writer.Write((byte)(length + tail)); - writer.Write(buffer[..length]); - - if (pad) - { - writer.Write((ushort)0); // Compensating for an old client bug - } - else - { - writer.Write((byte)0); // Terminator - } - } - - internal static void WriteString(this ref SpanWriter writer, scoped ReadOnlySpan text, Span buffer, bool pad = false) + private static void WriteString(this ref SpanWriter writer, string text, Span buffer, bool pad = false) { var tail = pad ? 2 : 1; var length = Math.Min(pad ? 253 : 254, text.GetBytesUtf8(buffer)); diff --git a/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs b/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs index 8fa3ddd05..2e4500d10 100644 --- a/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs +++ b/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs @@ -1,6 +1,6 @@ using System; +using System.Collections.Generic; using ModernUO.Serialization; -using Server.Collections; using Server.Targeting; namespace Server.Items @@ -22,7 +22,7 @@ namespace Server.Items PostedHue = Poster.Hue; Lines = lines; - using var list = PooledRefQueue.Create(poster.Items.Count); + var list = new List(); for (var i = 0; i < poster.Items.Count; ++i) { @@ -30,7 +30,7 @@ namespace Server.Items if (item.Layer >= Layer.OneHanded && item.Layer <= Layer.Mount) { - list.Enqueue(new BulletinEquip(item.ItemID, item.Hue)); + list.Add(new BulletinEquip(item.ItemID, item.Hue)); } } diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs index 708bb6688..5834239ec 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs @@ -6,10 +6,6 @@ namespace Server.Items public partial class SkullPole : Item { [Constructible] - public SkullPole() : base(0x2204) - { - } - - public override double DefaultWeight => 5; + public SkullPole() : base(0x2204) => Weight = 5; } } diff --git a/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs b/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs index 7c431f076..e267cd1aa 100644 --- a/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs +++ b/Projects/UOContent/Items/Clothing/BaseClothing.Migrations.cs @@ -1,25 +1,7 @@ -using System; - namespace Server.Items; public partial class BaseClothing { - // PlayerConstructed moved onto Item - private void MigrateFrom(V7Content content) - { - _resource = content.Resource ?? DefaultResource; - _attributes = content.Attributes ?? AttributesDefaultValue(); - _clothingAttributes = content.ClothingAttributes ?? ClothingAttributesDefaultValue(); - _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); - _resistances = content.Resistances ?? ResistancesDefaultValue(); - _maxHitPoints = content.MaxHitPoints ?? 0; - _hitPoints = content.HitPoints ?? 0; - PlayerConstructed = content.PlayerConstructed; - _crafter = content.Crafter; - _quality = content.Quality ?? ClothingQuality.Regular; - _strReq = content.StrRequirement ?? -1; - } - private void MigrateFrom(V6Content content) { _resource = content.RawResource ?? DefaultResource; @@ -28,7 +10,7 @@ public partial class BaseClothing _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); _resistances = content.Resistances ?? ResistancesDefaultValue(); _maxHitPoints = content.MaxHitPoints ?? 0; - PlayerConstructed = content.PlayerConstructed; + _playerConstructed = content.PlayerConstructed; Timer.DelayCall((item, crafter) => item._crafter = crafter?.RawName, this, content.Crafter); _quality = content.Quality ?? ClothingQuality.Regular; _strReq = content.StrRequirement ?? -1; @@ -103,23 +85,4 @@ public partial class BaseClothing PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); } - - private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; - - [Flags] - private enum OldSaveFlag - { - None = 0x00000000, - Resource = 0x00000001, - Attributes = 0x00000002, - ClothingAttributes = 0x00000004, - SkillBonuses = 0x00000008, - Resistances = 0x00000010, - MaxHitPoints = 0x00000020, - HitPoints = 0x00000040, - PlayerConstructed = 0x00000080, - Crafter = 0x00000100, - Quality = 0x00000200, - StrReq = 0x00000400 - } } diff --git a/Projects/UOContent/Items/Clothing/BaseClothing.cs b/Projects/UOContent/Items/Clothing/BaseClothing.cs index 983568d2d..5b249e490 100644 --- a/Projects/UOContent/Items/Clothing/BaseClothing.cs +++ b/Projects/UOContent/Items/Clothing/BaseClothing.cs @@ -22,78 +22,90 @@ namespace Server.Items int MaxArcaneCharges { get; set; } } - [SerializationGenerator(8, false)] + [SerializationGenerator(7, false)] public abstract partial class BaseClothing : Item, IDyable, IScissorable, IFactionItem, ICraftable, IWearableDurability, IAosItem { + [SerializableFieldSaveFlag(0)] private bool ShouldSerializeResource() => _resource != DefaultResource; [SerializedIgnoreDupe] [SerializableField(1, setter: "private")] - [SaveFlag(nameof(ShouldSerializeAttributes), nameof(AttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosAttributes _attributes; + [SerializableFieldSaveFlag(1)] private bool ShouldSerializeAttributes() => !_attributes.IsEmpty; + [SerializableFieldDefault(1)] private AosAttributes AttributesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(2, setter: "private")] - [SaveFlag(nameof(ShouldSerializeClothingAttributes), nameof(ClothingAttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosArmorAttributes _clothingAttributes; + [SerializableFieldSaveFlag(2)] private bool ShouldSerializeClothingAttributes() => !_clothingAttributes.IsEmpty; + [SerializableFieldDefault(2)] private AosArmorAttributes ClothingAttributesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(3, setter: "private")] - [SaveFlag(nameof(ShouldSerializeSkillBonuses), nameof(SkillBonusesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosSkillBonuses _skillBonuses; + [SerializableFieldSaveFlag(3)] private bool ShouldSerializeSkillBonuses() => !_skillBonuses.IsEmpty; + [SerializableFieldDefault(3)] private AosSkillBonuses SkillBonusesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(4, setter: "private")] - [SaveFlag(nameof(ShouldSerializeResistances), nameof(ResistancesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosElementAttributes _resistances; + [SerializableFieldSaveFlag(4)] private bool ShouldSerializeResistances() => !_resistances.IsEmpty; + [SerializableFieldDefault(4)] private AosElementAttributes ResistancesDefaultValue() => new(this); [EncodedInt] [InvalidateProperties] [SerializableField(5)] - [SaveFlag(nameof(ShouldSerializeMaxHitPoints))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxHitPoints; + [SerializableFieldSaveFlag(5)] private bool ShouldSerializeMaxHitPoints() => _maxHitPoints != 0; - [InvalidateProperties] [SerializableField(7)] - [SaveFlag(nameof(ShouldSerializeCrafter))] [SerializedCommandProperty(AccessLevel.GameMaster)] - private string _crafter; + private bool _playerConstructed; - private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter); + [SerializableFieldSaveFlag(7)] + private bool ShouldSerializePlayerConstructed() => _playerConstructed; [InvalidateProperties] [SerializableField(8)] - [SaveFlag(nameof(ShouldSerializeQuality))] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private string _crafter; + + [SerializableFieldSaveFlag(8)] + private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter); + + [InvalidateProperties] + [SerializableField(9)] [SerializedCommandProperty(AccessLevel.GameMaster)] private ClothingQuality _quality = ClothingQuality.Regular; + [SerializableFieldSaveFlag(9)] private bool ShouldSerializeQuality() => _quality != ClothingQuality.Regular; - // Field 9 + // Field 10 private int _strReq = -1; private FactionItem _factionState; @@ -113,19 +125,21 @@ namespace Server.Items Resistances = new AosElementAttributes(this); } - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SaveFlag(nameof(ShouldSerializeResource))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(_resource); + get => _resource; + set + { + _resource = value; + Hue = CraftResources.GetHue(_resource); + InvalidateProperties(); + this.MarkDirty(); + } } - [SerializableProperty(9, useField: nameof(_strReq))] - [SaveFlag(nameof(ShouldSerializeStrReq))] + [SerializableProperty(10, useField: nameof(_strReq))] [CommandProperty(AccessLevel.GameMaster)] public int StrRequirement { @@ -138,6 +152,7 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(10)] private bool ShouldSerializeStrReq() => _strReq != -1; public virtual CraftResource DefaultResource => CraftResource.None; @@ -192,6 +207,8 @@ namespace Server.Items Hue = resHue; } + PlayerConstructed = true; + var context = craftSystem.GetContext(from); if (context?.DoNotColor == true) @@ -291,7 +308,6 @@ namespace Server.Items [EncodedInt] [SerializableProperty(6)] - [SaveFlag(nameof(ShouldSerializeHitPoints))] [CommandProperty(AccessLevel.GameMaster)] public int HitPoints { @@ -317,6 +333,7 @@ namespace Server.Items } } + [SerializableFieldSaveFlag(6)] private bool ShouldSerializeHitPoints() => _hitPoints != 0; public virtual int InitMinHits => 0; @@ -412,65 +429,63 @@ namespace Server.Items public override bool CanEquip(Mobile from) { - if (!from.Player || from.AccessLevel >= AccessLevel.GameMaster) - { - return base.CanEquip(from); - } - if (!Ethic.CheckEquip(from, this)) { return false; } - if (RequiredRace != null && from.Race != RequiredRace) + if (from.AccessLevel < AccessLevel.GameMaster) { - if (RequiredRace == Race.Elf) + if (RequiredRace != null && from.Race != RequiredRace) { - from.SendLocalizedMessage(1072203); // Only Elves may use this. - } - else - { - from.SendMessage($"Only {RequiredRace.PluralName} may use this."); + if (RequiredRace == Race.Elf) + { + from.SendLocalizedMessage(1072203); // Only Elves may use this. + } + else + { + from.SendMessage($"Only {RequiredRace.PluralName} may use this."); + } + + return false; } - return false; - } + if (!AllowMaleWearer && !from.Female) + { + if (AllowFemaleWearer) + { + from.SendLocalizedMessage(1010388); // Only females can wear this. + } + else + { + from.SendMessage("You may not wear this."); + } - if (!AllowMaleWearer && !from.Female) - { - if (AllowFemaleWearer) - { - from.SendLocalizedMessage(1010388); // Only females can wear this. - } - else - { - from.SendMessage("You may not wear this."); + return false; } - return false; - } + if (!AllowFemaleWearer && from.Female) + { + if (AllowMaleWearer) + { + from.SendLocalizedMessage(1063343); // Only males can wear this. + } + else + { + from.SendMessage("You may not wear this."); + } - if (!AllowFemaleWearer && from.Female) - { - if (AllowMaleWearer) - { - from.SendLocalizedMessage(1063343); // Only males can wear this. - } - else - { - from.SendMessage("You may not wear this."); + return false; } - return false; - } + var strBonus = ComputeStatBonus(StatType.Str); + var strReq = ComputeStatReq(StatType.Str); - var strBonus = ComputeStatBonus(StatType.Str); - var strReq = ComputeStatReq(StatType.Str); - - if (from.Str < strReq || from.Str + strBonus < 1) - { - from.SendLocalizedMessage(500213); // You are not strong enough to equip that. - return false; + if (from.Str < strReq || from.Str + strBonus < 1) + { + from.SendLocalizedMessage(500213); // You are not strong enough to equip that. + return false; + } } return base.CanEquip(from); @@ -737,9 +752,140 @@ namespace Server.Items list.Add(1061078, prop); // artifact rarity ~1_val~ } - ClothingAttributes.GetProperties(list); + if ((prop = Attributes.WeaponDamage) != 0) + { + list.Add(1060401, prop); // damage increase ~1_val~% + } - Attributes.GetProperties(list); + if ((prop = Attributes.DefendChance) != 0) + { + list.Add(1060408, prop); // defense chance increase ~1_val~% + } + + if ((prop = Attributes.BonusDex) != 0) + { + list.Add(1060409, prop); // dexterity bonus ~1_val~ + } + + if ((prop = Attributes.EnhancePotions) != 0) + { + list.Add(1060411, prop); // enhance potions ~1_val~% + } + + if ((prop = Attributes.CastRecovery) != 0) + { + list.Add(1060412, prop); // faster cast recovery ~1_val~ + } + + if ((prop = Attributes.CastSpeed) != 0) + { + list.Add(1060413, prop); // faster casting ~1_val~ + } + + if ((prop = Attributes.AttackChance) != 0) + { + list.Add(1060415, prop); // hit chance increase ~1_val~% + } + + if ((prop = Attributes.BonusHits) != 0) + { + list.Add(1060431, prop); // hit point increase ~1_val~ + } + + if ((prop = Attributes.BonusInt) != 0) + { + list.Add(1060432, prop); // intelligence bonus ~1_val~ + } + + if ((prop = Attributes.LowerManaCost) != 0) + { + list.Add(1060433, prop); // lower mana cost ~1_val~% + } + + if ((prop = Attributes.LowerRegCost) != 0) + { + list.Add(1060434, prop); // lower reagent cost ~1_val~% + } + + if ((prop = ClothingAttributes.LowerStatReq) != 0) + { + list.Add(1060435, prop); // lower requirements ~1_val~% + } + + if ((prop = Attributes.Luck) != 0) + { + list.Add(1060436, prop); // luck ~1_val~ + } + + if (ClothingAttributes.MageArmor != 0) + { + list.Add(1060437); // mage armor + } + + if ((prop = Attributes.BonusMana) != 0) + { + list.Add(1060439, prop); // mana increase ~1_val~ + } + + if ((prop = Attributes.RegenMana) != 0) + { + list.Add(1060440, prop); // mana regeneration ~1_val~ + } + + if (Attributes.NightSight != 0) + { + list.Add(1060441); // night sight + } + + if ((prop = Attributes.ReflectPhysical) != 0) + { + list.Add(1060442, prop); // reflect physical damage ~1_val~% + } + + if ((prop = Attributes.RegenStam) != 0) + { + list.Add(1060443, prop); // stamina regeneration ~1_val~ + } + + if ((prop = Attributes.RegenHits) != 0) + { + list.Add(1060444, prop); // hit point regeneration ~1_val~ + } + + if ((prop = ClothingAttributes.SelfRepair) != 0) + { + list.Add(1060450, prop); // self repair ~1_val~ + } + + if (Attributes.SpellChanneling != 0) + { + list.Add(1060482); // spell channeling + } + + if ((prop = Attributes.SpellDamage) != 0) + { + list.Add(1060483, prop); // spell damage increase ~1_val~% + } + + if ((prop = Attributes.BonusStam) != 0) + { + list.Add(1060484, prop); // stamina increase ~1_val~ + } + + if ((prop = Attributes.BonusStr) != 0) + { + list.Add(1060485, prop); // strength bonus ~1_val~ + } + + if ((prop = Attributes.WeaponSpeed) != 0) + { + list.Add(1060486, prop); // swing speed increase ~1_val~% + } + + if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) + { + list.Add(1075210, prop); // Increased Karma Loss ~1val~% + } AddResistanceProperties(list); @@ -748,12 +894,6 @@ namespace Server.Items list.Add(1060410, prop); // durability ~1_val~% } - var lowerStatReq = ClothingAttributes.LowerStatReq; - if (lowerStatReq != 0) - { - list.Add(1060435, lowerStatReq); // lower requirements ~1_val~% - } - if ((prop = ComputeStatReq(StatType.Str)) > 0) { list.Add(1061170, prop); // strength requirement ~1_val~ @@ -872,6 +1012,8 @@ namespace Server.Items InvalidateProperties(); } + private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; + [AfterDeserialization] private void AfterDeserialization() { @@ -892,5 +1034,21 @@ namespace Server.Items } } + [Flags] + private enum OldSaveFlag + { + None = 0x00000000, + Resource = 0x00000001, + Attributes = 0x00000002, + ClothingAttributes = 0x00000004, + SkillBonuses = 0x00000008, + Resistances = 0x00000010, + MaxHitPoints = 0x00000020, + HitPoints = 0x00000040, + PlayerConstructed = 0x00000080, + Crafter = 0x00000100, + Quality = 0x00000200, + StrReq = 0x00000400 + } } } diff --git a/Projects/UOContent/Items/Clothing/Cloaks.cs b/Projects/UOContent/Items/Clothing/Cloaks.cs index a92e8fc8c..e18e5a510 100644 --- a/Projects/UOContent/Items/Clothing/Cloaks.cs +++ b/Projects/UOContent/Items/Clothing/Cloaks.cs @@ -16,32 +16,36 @@ namespace Server.Items public partial class Cloak : BaseCloak, IArcaneEquip { [Constructible] - public Cloak(int hue = 0) : base(0x1515, hue) + public Cloak(int hue = 0) : base(0x1515, hue) => Weight = 5.0; + + [EncodedInt] + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int CurArcaneCharges { + get => _curArcaneCharges; + set + { + _curArcaneCharges = value; + this.MarkDirty(); + InvalidateProperties(); + Update(); + } } - public override double DefaultWeight => 5.0; - - [SerializableField(0, fieldChanged: nameof(OnCurArcaneChargesChanged))] [EncodedInt] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _curArcaneCharges; - - private void OnCurArcaneChargesChanged(int oldValue, int newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public int MaxArcaneCharges { - Update(); - } - - [SerializableField(1, fieldChanged: nameof(OnMaxArcaneChargesChanged))] - [EncodedInt] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _maxArcaneCharges; - - private void OnMaxArcaneChargesChanged(int oldValue, int newValue) - { - Update(); + get => _maxArcaneCharges; + set + { + _maxArcaneCharges = value; + this.MarkDirty(); + InvalidateProperties(); + Update(); + } } [CommandProperty(AccessLevel.GameMaster)] @@ -120,12 +124,12 @@ namespace Server.Items [Constructible] public RewardCloak(int hue = 0, int labelNumber = 0) : base(0x1515, hue) { + Weight = 5.0; LootType = LootType.Blessed; + _number = labelNumber; } - public override double DefaultWeight => 5.0; - public override int LabelNumber => _number > 0 ? _number : base.LabelNumber; public override int BasePhysicalResistance => 3; @@ -186,10 +190,6 @@ namespace Server.Items public partial class FurCape : BaseCloak { [Constructible] - public FurCape(int hue = 0) : base(0x230A, hue) - { - } - - public override double DefaultWeight => 4.0; + public FurCape(int hue = 0) : base(0x230A, hue) => Weight = 4.0; } } diff --git a/Projects/UOContent/Items/Clothing/Hats.cs b/Projects/UOContent/Items/Clothing/Hats.cs index 1d5f31643..d6bec6723 100644 --- a/Projects/UOContent/Items/Clothing/Hats.cs +++ b/Projects/UOContent/Items/Clothing/Hats.cs @@ -62,11 +62,7 @@ namespace Server.Items public partial class Kasa : BaseHat { [Constructible] - public Kasa(int hue = 0) : base(0x2798, hue) - { - } - - public override double DefaultWeight => 3.0; + public Kasa(int hue = 0) : base(0x2798, hue) => Weight = 3.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -83,11 +79,7 @@ namespace Server.Items public partial class ClothNinjaHood : BaseHat { [Constructible] - public ClothNinjaHood(int hue = 0) : base(0x278F, hue) - { - } - - public override double DefaultWeight => 2.0; + public ClothNinjaHood(int hue = 0) : base(0x278F, hue) => Weight = 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; @@ -104,11 +96,7 @@ namespace Server.Items public partial class FlowerGarland : BaseHat { [Constructible] - public FlowerGarland(int hue = 0) : base(0x2306, hue) - { - } - - public override double DefaultWeight => 1.0; + public FlowerGarland(int hue = 0) : base(0x2306, hue) => Weight = 1.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; @@ -124,11 +112,7 @@ namespace Server.Items public partial class FloppyHat : BaseHat { [Constructible] - public FloppyHat(int hue = 0) : base(0x1713, hue) - { - } - - public override double DefaultWeight => 1.0; + public FloppyHat(int hue = 0) : base(0x1713, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -144,11 +128,7 @@ namespace Server.Items public partial class WideBrimHat : BaseHat { [Constructible] - public WideBrimHat(int hue = 0) : base(0x1714, hue) - { - } - - public override double DefaultWeight => 1.0; + public WideBrimHat(int hue = 0) : base(0x1714, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -164,11 +144,7 @@ namespace Server.Items public partial class Cap : BaseHat { [Constructible] - public Cap(int hue = 0) : base(0x1715, hue) - { - } - - public override double DefaultWeight => 1.0; + public Cap(int hue = 0) : base(0x1715, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -184,11 +160,7 @@ namespace Server.Items public partial class SkullCap : BaseHat { [Constructible] - public SkullCap(int hue = 0) : base(0x1544, hue) - { - } - - public override double DefaultWeight => 1.0; + public SkullCap(int hue = 0) : base(0x1544, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 3; @@ -204,11 +176,7 @@ namespace Server.Items public partial class Bandana : BaseHat { [Constructible] - public Bandana(int hue = 0) : base(0x1540, hue) - { - } - - public override double DefaultWeight => 1.0; + public Bandana(int hue = 0) : base(0x1540, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 3; @@ -224,11 +192,7 @@ namespace Server.Items public partial class BearMask : BaseHat { [Constructible] - public BearMask(int hue = 0) : base(0x1545, hue) - { - } - - public override double DefaultWeight => 5.0; + public BearMask(int hue = 0) : base(0x1545, hue) => Weight = 5.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; @@ -250,11 +214,7 @@ namespace Server.Items public partial class DeerMask : BaseHat { [Constructible] - public DeerMask(int hue = 0) : base(0x1547, hue) - { - } - - public override double DefaultWeight => 4.0; + public DeerMask(int hue = 0) : base(0x1547, hue) => Weight = 4.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 6; @@ -276,11 +236,7 @@ namespace Server.Items public partial class HornedTribalMask : BaseHat { [Constructible] - public HornedTribalMask(int hue = 0) : base(0x1549, hue) - { - } - - public override double DefaultWeight => 2.0; + public HornedTribalMask(int hue = 0) : base(0x1549, hue) => Weight = 2.0; public override int BasePhysicalResistance => 6; public override int BaseFireResistance => 9; @@ -302,11 +258,7 @@ namespace Server.Items public partial class TribalMask : BaseHat { [Constructible] - public TribalMask(int hue = 0) : base(0x154B, hue) - { - } - - public override double DefaultWeight => 2.0; + public TribalMask(int hue = 0) : base(0x154B, hue) => Weight = 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 0; @@ -328,11 +280,7 @@ namespace Server.Items public partial class TallStrawHat : BaseHat { [Constructible] - public TallStrawHat(int hue = 0) : base(0x1716, hue) - { - } - - public override double DefaultWeight => 1.0; + public TallStrawHat(int hue = 0) : base(0x1716, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -348,11 +296,7 @@ namespace Server.Items public partial class StrawHat : BaseHat { [Constructible] - public StrawHat(int hue = 0) : base(0x1717, hue) - { - } - - public override double DefaultWeight => 1.0; + public StrawHat(int hue = 0) : base(0x1717, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -368,11 +312,7 @@ namespace Server.Items public partial class OrcishKinMask : BaseHat { [Constructible] - public OrcishKinMask(int hue = 0x8A4) : base(0x141B, hue) - { - } - - public override double DefaultWeight => 2.0; + public OrcishKinMask(int hue = 0x8A4) : base(0x141B, hue) => Weight = 2.0; public override int BasePhysicalResistance => 1; public override int BaseFireResistance => 1; @@ -427,11 +367,7 @@ namespace Server.Items } [Constructible] - public SavageMask(int hue) : base(0x154B, hue) - { - } - - public override double DefaultWeight => 2.0; + public SavageMask(int hue) : base(0x154B, hue) => Weight = 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 0; @@ -465,11 +401,7 @@ namespace Server.Items public partial class WizardsHat : BaseHat { [Constructible] - public WizardsHat(int hue = 0) : base(0x1718, hue) - { - } - - public override double DefaultWeight => 1.0; + public WizardsHat(int hue = 0) : base(0x1718, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -485,11 +417,7 @@ namespace Server.Items public partial class MagicWizardsHat : BaseHat { [Constructible] - public MagicWizardsHat(int hue = 0) : base(0x1718, hue) - { - } - - public override double DefaultWeight => 1.0; + public MagicWizardsHat(int hue = 0) : base(0x1718, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -511,11 +439,7 @@ namespace Server.Items public partial class Bonnet : BaseHat { [Constructible] - public Bonnet(int hue = 0) : base(0x1719, hue) - { - } - - public override double DefaultWeight => 1.0; + public Bonnet(int hue = 0) : base(0x1719, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -531,11 +455,7 @@ namespace Server.Items public partial class FeatheredHat : BaseHat { [Constructible] - public FeatheredHat(int hue = 0) : base(0x171A, hue) - { - } - - public override double DefaultWeight => 1.0; + public FeatheredHat(int hue = 0) : base(0x171A, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -551,11 +471,7 @@ namespace Server.Items public partial class TricorneHat : BaseHat { [Constructible] - public TricorneHat(int hue = 0) : base(0x171B, hue) - { - } - - public override double DefaultWeight => 1.0; + public TricorneHat(int hue = 0) : base(0x171B, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -571,11 +487,7 @@ namespace Server.Items public partial class JesterHat : BaseHat { [Constructible] - public JesterHat(int hue = 0) : base(0x171C, hue) - { - } - - public override double DefaultWeight => 1.0; + public JesterHat(int hue = 0) : base(0x171C, hue) => Weight = 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; diff --git a/Projects/UOContent/Items/Clothing/MiddleTorso.cs b/Projects/UOContent/Items/Clothing/MiddleTorso.cs index 31299b316..27e3bc4b6 100644 --- a/Projects/UOContent/Items/Clothing/MiddleTorso.cs +++ b/Projects/UOContent/Items/Clothing/MiddleTorso.cs @@ -15,11 +15,7 @@ namespace Server.Items public partial class BodySash : BaseMiddleTorso { [Constructible] - public BodySash(int hue = 0) : base(0x1541, hue) - { - } - - public override double DefaultWeight => 1.0; + public BodySash(int hue = 0) : base(0x1541, hue) => Weight = 1.0; } [SerializationGenerator(0, false)] @@ -27,11 +23,7 @@ namespace Server.Items public partial class FullApron : BaseMiddleTorso { [Constructible] - public FullApron(int hue = 0) : base(0x153d, hue) - { - } - - public override double DefaultWeight => 4.0; + public FullApron(int hue = 0) : base(0x153d, hue) => Weight = 4.0; } [SerializationGenerator(0, false)] @@ -39,11 +31,7 @@ namespace Server.Items public partial class Doublet : BaseMiddleTorso { [Constructible] - public Doublet(int hue = 0) : base(0x1F7B, hue) - { - } - - public override double DefaultWeight => 2.0; + public Doublet(int hue = 0) : base(0x1F7B, hue) => Weight = 2.0; } [SerializationGenerator(0, false)] @@ -51,11 +39,7 @@ namespace Server.Items public partial class Surcoat : BaseMiddleTorso { [Constructible] - public Surcoat(int hue = 0) : base(0x1FFD, hue) - { - } - - public override double DefaultWeight => 6.0; + public Surcoat(int hue = 0) : base(0x1FFD, hue) => Weight = 6.0; } [SerializationGenerator(0, false)] @@ -63,11 +47,7 @@ namespace Server.Items public partial class Tunic : BaseMiddleTorso { [Constructible] - public Tunic(int hue = 0) : base(0x1FA1, hue) - { - } - - public override double DefaultWeight => 5.0; + public Tunic(int hue = 0) : base(0x1FA1, hue) => Weight = 5.0; } [SerializationGenerator(0, false)] @@ -75,11 +55,7 @@ namespace Server.Items public partial class FormalShirt : BaseMiddleTorso { [Constructible] - public FormalShirt(int hue = 0) : base(0x2310, hue) - { - } - - public override double DefaultWeight => 1.0; + public FormalShirt(int hue = 0) : base(0x2310, hue) => Weight = 1.0; } [SerializationGenerator(0, false)] @@ -87,11 +63,7 @@ namespace Server.Items public partial class JesterSuit : BaseMiddleTorso { [Constructible] - public JesterSuit(int hue = 0) : base(0x1F9F, hue) - { - } - - public override double DefaultWeight => 4.0; + public JesterSuit(int hue = 0) : base(0x1F9F, hue) => Weight = 4.0; } [SerializationGenerator(0, false)] @@ -99,10 +71,6 @@ namespace Server.Items public partial class JinBaori : BaseMiddleTorso { [Constructible] - public JinBaori(int hue = 0) : base(0x27A1, hue) - { - } - - public override double DefaultWeight => 3.0; + public JinBaori(int hue = 0) : base(0x27A1, hue) => Weight = 3.0; } } diff --git a/Projects/UOContent/Items/Clothing/OuterLegs.cs b/Projects/UOContent/Items/Clothing/OuterLegs.cs index a3b3ba4b8..d060c306f 100644 --- a/Projects/UOContent/Items/Clothing/OuterLegs.cs +++ b/Projects/UOContent/Items/Clothing/OuterLegs.cs @@ -15,11 +15,7 @@ namespace Server.Items public partial class FurSarong : BaseOuterLegs { [Constructible] - public FurSarong(int hue = 0) : base(0x230C, hue) - { - } - - public override double DefaultWeight => 3.0; + public FurSarong(int hue = 0) : base(0x230C, hue) => Weight = 3.0; } [SerializationGenerator(0, false)] @@ -27,11 +23,7 @@ namespace Server.Items public partial class Skirt : BaseOuterLegs { [Constructible] - public Skirt(int hue = 0) : base(0x1516, hue) - { - } - - public override double DefaultWeight => 4.0; + public Skirt(int hue = 0) : base(0x1516, hue) => Weight = 4.0; } [SerializationGenerator(0, false)] @@ -39,11 +31,7 @@ namespace Server.Items public partial class Kilt : BaseOuterLegs { [Constructible] - public Kilt(int hue = 0) : base(0x1537, hue) - { - } - - public override double DefaultWeight => 2.0; + public Kilt(int hue = 0) : base(0x1537, hue) => Weight = 2.0; } [SerializationGenerator(0, false)] @@ -51,10 +39,6 @@ namespace Server.Items public partial class Hakama : BaseOuterLegs { [Constructible] - public Hakama(int hue = 0) : base(0x279A, hue) - { - } - - public override double DefaultWeight => 2.0; + public Hakama(int hue = 0) : base(0x279A, hue) => Weight = 2.0; } } diff --git a/Projects/UOContent/Items/Clothing/OuterTorso.cs b/Projects/UOContent/Items/Clothing/OuterTorso.cs index f3b0b077f..5f147fcb2 100644 --- a/Projects/UOContent/Items/Clothing/OuterTorso.cs +++ b/Projects/UOContent/Items/Clothing/OuterTorso.cs @@ -18,11 +18,7 @@ namespace Server.Items public partial class GildedDress : BaseOuterTorso { [Constructible] - public GildedDress(int hue = 0) : base(0x230E, hue) - { - } - - public override double DefaultWeight => 3.0; + public GildedDress(int hue = 0) : base(0x230E, hue) => Weight = 3.0; } [SerializationGenerator(0, false)] @@ -30,29 +26,24 @@ namespace Server.Items public partial class FancyDress : BaseOuterTorso { [Constructible] - public FancyDress(int hue = 0) : base(0x1F00, hue) - { - } - - public override double DefaultWeight => 3.0; + public FancyDress(int hue = 0) : base(0x1F00, hue) => Weight = 3.0; } - [SerializationGenerator(4, false)] + [SerializationGenerator(3, false)] public partial class DeathRobe : Robe { private static readonly TimeSpan m_DefaultDecayTime = TimeSpan.FromMinutes(1.0); + [TimerDrift] [SerializableField(0)] - [DeserializeTimer(nameof(DeserializeDecayTimer))] private Timer _decayTimer; - private void DeserializeDecayTimer(TimeSpan delay) => BeginDecay(delay); - - private void MigrateFrom(V3Content content) + [DeserializeTimerField(0)] + private void DeserializeDecayTimer(TimeSpan delay) { - if (content.DecayTimerDelay != TimeSpan.MinValue) + if (delay != TimeSpan.MinValue) { - DeserializeDecayTimer(content.DecayTimerDelay); + BeginDecay(delay); } } @@ -166,12 +157,12 @@ namespace Server.Items [Constructible] public RewardRobe(int hue = 0, int labelNumber = 0) : base(0x1F03, hue) { + Weight = 3.0; LootType = LootType.Blessed; + _number = labelNumber; } - public override double DefaultWeight => 3.0; - public override int LabelNumber => _number > 0 ? _number : base.LabelNumber; public override int BasePhysicalResistance => 3; @@ -248,12 +239,12 @@ namespace Server.Items [Constructible] public RewardDress(int hue = 0, int labelNumber = 0) : base(0x1F01, hue) { + Weight = 2.0; LootType = LootType.Blessed; + _number = labelNumber; } - public override double DefaultWeight => 2.0; - public override int LabelNumber => _number > 0 ? _number : base.LabelNumber; public override int BasePhysicalResistance => 3; @@ -319,32 +310,36 @@ namespace Server.Items public partial class Robe : BaseOuterTorso, IArcaneEquip { [Constructible] - public Robe(int hue = 0) : base(0x1F03, hue) + public Robe(int hue = 0) : base(0x1F03, hue) => Weight = 3.0; + + [EncodedInt] + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int CurArcaneCharges { + get => _curArcaneCharges; + set + { + _curArcaneCharges = value; + InvalidateProperties(); + Update(); + this.MarkDirty(); + } } - public override double DefaultWeight => 3.0; - - [SerializableField(0, fieldChanged: nameof(OnCurArcaneChargesChanged))] [EncodedInt] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _curArcaneCharges; - - private void OnCurArcaneChargesChanged(int oldValue, int newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public int MaxArcaneCharges { - Update(); - } - - [SerializableField(1, fieldChanged: nameof(OnMaxArcaneChargesChanged))] - [EncodedInt] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _maxArcaneCharges; - - private void OnMaxArcaneChargesChanged(int oldValue, int newValue) - { - Update(); + get => _maxArcaneCharges; + set + { + _maxArcaneCharges = value; + InvalidateProperties(); + Update(); + this.MarkDirty(); + } } [CommandProperty(AccessLevel.GameMaster)] @@ -411,9 +406,11 @@ namespace Server.Items public partial class MonkRobe : BaseOuterTorso { [Constructible] - public MonkRobe(int hue = 0x21E) : base(0x2687, hue) => StrRequirement = 0; - - public override double DefaultWeight => 1.0; + public MonkRobe(int hue = 0x21E) : base(0x2687, hue) + { + Weight = 1.0; + StrRequirement = 0; + } public override int LabelNumber => 1076584; // A monk's robe public override bool CanBeBlessed => false; @@ -430,11 +427,7 @@ namespace Server.Items public partial class PlainDress : BaseOuterTorso { [Constructible] - public PlainDress(int hue = 0) : base(0x1F01, hue) - { - } - - public override double DefaultWeight => 2.0; + public PlainDress(int hue = 0) : base(0x1F01, hue) => Weight = 2.0; } [Flippable(0x2799, 0x27E4)] @@ -442,11 +435,7 @@ namespace Server.Items public partial class Kamishimo : BaseOuterTorso { [Constructible] - public Kamishimo(int hue = 0) : base(0x2799, hue) - { - } - - public override double DefaultWeight => 3.0; + public Kamishimo(int hue = 0) : base(0x2799, hue) => Weight = 3.0; } [Flippable(0x279C, 0x27E7)] @@ -454,11 +443,7 @@ namespace Server.Items public partial class HakamaShita : BaseOuterTorso { [Constructible] - public HakamaShita(int hue = 0) : base(0x279C, hue) - { - } - - public override double DefaultWeight => 3.0; + public HakamaShita(int hue = 0) : base(0x279C, hue) => Weight = 3.0; } [Flippable(0x2782, 0x27CD)] @@ -466,11 +451,7 @@ namespace Server.Items public partial class MaleKimono : BaseOuterTorso { [Constructible] - public MaleKimono(int hue = 0) : base(0x2782, hue) - { - } - - public override double DefaultWeight => 3.0; + public MaleKimono(int hue = 0) : base(0x2782, hue) => Weight = 3.0; } [Flippable(0x2783, 0x27CE)] @@ -478,11 +459,7 @@ namespace Server.Items public partial class FemaleKimono : BaseOuterTorso { [Constructible] - public FemaleKimono(int hue = 0) : base(0x2783, hue) - { - } - - public override double DefaultWeight => 3.0; + public FemaleKimono(int hue = 0) : base(0x2783, hue) => Weight = 3.0; } [Flippable(0x2FB9, 0x3173)] @@ -490,11 +467,7 @@ namespace Server.Items public partial class MaleElvenRobe : BaseOuterTorso { [Constructible] - public MaleElvenRobe(int hue = 0) : base(0x2FB9, hue) - { - } - - public override double DefaultWeight => 2.0; + public MaleElvenRobe(int hue = 0) : base(0x2FB9, hue) => Weight = 2.0; } [Flippable(0x2FBA, 0x3174)] @@ -502,11 +475,7 @@ namespace Server.Items public partial class FemaleElvenRobe : BaseOuterTorso { [Constructible] - public FemaleElvenRobe(int hue = 0) : base(0x2FBA, hue) - { - } - - public override double DefaultWeight => 2.0; + public FemaleElvenRobe(int hue = 0) : base(0x2FBA, hue) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Clothing/Pants.cs b/Projects/UOContent/Items/Clothing/Pants.cs index 69fe21011..efee4e379 100644 --- a/Projects/UOContent/Items/Clothing/Pants.cs +++ b/Projects/UOContent/Items/Clothing/Pants.cs @@ -15,11 +15,7 @@ namespace Server.Items public partial class ShortPants : BasePants { [Constructible] - public ShortPants(int hue = 0) : base(0x152E, hue) - { - } - - public override double DefaultWeight => 2.0; + public ShortPants(int hue = 0) : base(0x152E, hue) => Weight = 2.0; } [Flippable(0x1539, 0x153a)] @@ -27,11 +23,7 @@ namespace Server.Items public partial class LongPants : BasePants { [Constructible] - public LongPants(int hue = 0) : base(0x1539, hue) - { - } - - public override double DefaultWeight => 2.0; + public LongPants(int hue = 0) : base(0x1539, hue) => Weight = 2.0; } [Flippable(0x279B, 0x27E6)] @@ -39,11 +31,7 @@ namespace Server.Items public partial class TattsukeHakama : BasePants { [Constructible] - public TattsukeHakama(int hue = 0) : base(0x279B, hue) - { - } - - public override double DefaultWeight => 2.0; + public TattsukeHakama(int hue = 0) : base(0x279B, hue) => Weight = 2.0; } [Flippable(0x2FC3, 0x3179)] @@ -51,11 +39,7 @@ namespace Server.Items public partial class ElvenPants : BasePants { [Constructible] - public ElvenPants(int hue = 0) : base(0x2FC3, hue) - { - } - - public override double DefaultWeight => 2.0; + public ElvenPants(int hue = 0) : base(0x2FC3, hue) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; } diff --git a/Projects/UOContent/Items/Clothing/Shirts.cs b/Projects/UOContent/Items/Clothing/Shirts.cs index bd174067e..6ba43a870 100644 --- a/Projects/UOContent/Items/Clothing/Shirts.cs +++ b/Projects/UOContent/Items/Clothing/Shirts.cs @@ -15,11 +15,7 @@ namespace Server.Items public partial class FancyShirt : BaseShirt { [Constructible] - public FancyShirt(int hue = 0) : base(0x1EFD, hue) - { - } - - public override double DefaultWeight => 2.0; + public FancyShirt(int hue = 0) : base(0x1EFD, hue) => Weight = 2.0; } [Flippable(0x1517, 0x1518)] @@ -27,11 +23,7 @@ namespace Server.Items public partial class Shirt : BaseShirt { [Constructible] - public Shirt(int hue = 0) : base(0x1517, hue) - { - } - - public override double DefaultWeight => 1.0; + public Shirt(int hue = 0) : base(0x1517, hue) => Weight = 1.0; } [Flippable(0x2794, 0x27DF)] @@ -39,20 +31,18 @@ namespace Server.Items public partial class ClothNinjaJacket : BaseShirt { [Constructible] - public ClothNinjaJacket(int hue = 0) : base(0x2794, hue) => Layer = Layer.InnerTorso; - - public override double DefaultWeight => 5.0; + public ClothNinjaJacket(int hue = 0) : base(0x2794, hue) + { + Weight = 5.0; + Layer = Layer.InnerTorso; + } } [SerializationGenerator(0)] public partial class ElvenShirt : BaseShirt { [Constructible] - public ElvenShirt(int hue = 0) : base(0x3175, hue) - { - } - - public override double DefaultWeight => 2.0; + public ElvenShirt(int hue = 0) : base(0x3175, hue) => Weight = 2.0; public override int RequiredRaces => Race.AllowElvesOnly; } @@ -61,10 +51,6 @@ namespace Server.Items public partial class ElvenDarkShirt : BaseShirt { [Constructible] - public ElvenDarkShirt(int hue = 0) : base(0x3176, hue) - { - } - - public override double DefaultWeight => 2.0; + public ElvenDarkShirt(int hue = 0) : base(0x3176, hue) => Weight = 2.0; } } diff --git a/Projects/UOContent/Items/Clothing/Shoes.cs b/Projects/UOContent/Items/Clothing/Shoes.cs index f791b3a62..8e6cb3d7a 100644 --- a/Projects/UOContent/Items/Clothing/Shoes.cs +++ b/Projects/UOContent/Items/Clothing/Shoes.cs @@ -1,4 +1,3 @@ -using System.Runtime.CompilerServices; using ModernUO.Serialization; namespace Server.Items @@ -27,11 +26,7 @@ namespace Server.Items public partial class FurBoots : BaseShoes { [Constructible] - public FurBoots(int hue = 0) : base(0x2307, hue) - { - } - - public override double DefaultWeight => 3.0; + public FurBoots(int hue = 0) : base(0x2307, hue) => Weight = 3.0; } [Flippable(0x170b, 0x170c)] @@ -39,11 +34,7 @@ namespace Server.Items public partial class Boots : BaseShoes { [Constructible] - public Boots(int hue = 0) : base(0x170B, hue) - { - } - - public override double DefaultWeight => 3.0; + public Boots(int hue = 0) : base(0x170B, hue) => Weight = 3.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; } @@ -52,33 +43,39 @@ namespace Server.Items [SerializationGenerator(2, false)] public partial class ThighBoots : BaseShoes, IArcaneEquip { - [EncodedInt] - [InvalidateProperties] - [SerializableField(0, fieldChanged: nameof(OnCurArcaneChargesChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _curArcaneCharges; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void OnCurArcaneChargesChanged(int oldValue, int newValue) => Update(); - [Constructible] - public ThighBoots(int hue = 0) : base(0x1711, hue) - { - } - - public override double DefaultWeight => 4.0; + public ThighBoots(int hue = 0) : base(0x1711, hue) => Weight = 4.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; - [SerializableField(1, fieldChanged: nameof(OnMaxArcaneChargesChanged))] [EncodedInt] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _maxArcaneCharges; - - private void OnMaxArcaneChargesChanged(int oldValue, int newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int CurArcaneCharges { - Update(); + get => _curArcaneCharges; + set + { + _curArcaneCharges = value; + InvalidateProperties(); + Update(); + this.MarkDirty(); + } + } + + [EncodedInt] + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public int MaxArcaneCharges + { + get => _maxArcaneCharges; + set + { + _maxArcaneCharges = value; + InvalidateProperties(); + Update(); + this.MarkDirty(); + } } [CommandProperty(AccessLevel.GameMaster)] @@ -146,11 +143,7 @@ namespace Server.Items public partial class Shoes : BaseShoes { [Constructible] - public Shoes(int hue = 0) : base(0x170F, hue) - { - } - - public override double DefaultWeight => 2.0; + public Shoes(int hue = 0) : base(0x170F, hue) => Weight = 2.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; } @@ -160,11 +153,7 @@ namespace Server.Items public partial class Sandals : BaseShoes { [Constructible] - public Sandals(int hue = 0) : base(0x170D, hue) - { - } - - public override double DefaultWeight => 1.0; + public Sandals(int hue = 0) : base(0x170D, hue) => Weight = 1.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; @@ -176,11 +165,7 @@ namespace Server.Items public partial class NinjaTabi : BaseShoes { [Constructible] - public NinjaTabi(int hue = 0) : base(0x2797, hue) - { - } - - public override double DefaultWeight => 2.0; + public NinjaTabi(int hue = 0) : base(0x2797, hue) => Weight = 2.0; } [Flippable(0x2796, 0x27E1)] @@ -188,11 +173,7 @@ namespace Server.Items public partial class SamuraiTabi : BaseShoes { [Constructible] - public SamuraiTabi(int hue = 0) : base(0x2796, hue) - { - } - - public override double DefaultWeight => 2.0; + public SamuraiTabi(int hue = 0) : base(0x2796, hue) => Weight = 2.0; } [Flippable(0x2796, 0x27E1)] @@ -200,11 +181,7 @@ namespace Server.Items public partial class Waraji : BaseShoes { [Constructible] - public Waraji(int hue = 0) : base(0x2796, hue) - { - } - - public override double DefaultWeight => 2.0; + public Waraji(int hue = 0) : base(0x2796, hue) => Weight = 2.0; } [Flippable(0x2FC4, 0x317A)] @@ -212,11 +189,7 @@ namespace Server.Items public partial class ElvenBoots : BaseShoes { [Constructible] - public ElvenBoots(int hue = 0) : base(0x2FC4, hue) - { - } - - public override double DefaultWeight => 2.0; + public ElvenBoots(int hue = 0) : base(0x2FC4, hue) => Weight = 2.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; diff --git a/Projects/UOContent/Items/Clothing/Waist.cs b/Projects/UOContent/Items/Clothing/Waist.cs index 10dd9dab8..45f34d768 100644 --- a/Projects/UOContent/Items/Clothing/Waist.cs +++ b/Projects/UOContent/Items/Clothing/Waist.cs @@ -15,11 +15,7 @@ namespace Server.Items public partial class HalfApron : BaseWaist { [Constructible] - public HalfApron(int hue = 0) : base(0x153b, hue) - { - } - - public override double DefaultWeight => 2.0; + public HalfApron(int hue = 0) : base(0x153b, hue) => Weight = 2.0; } [Flippable(0x27A0, 0x27EB)] @@ -27,11 +23,7 @@ namespace Server.Items public partial class Obi : BaseWaist { [Constructible] - public Obi(int hue = 0) : base(0x27A0, hue) - { - } - - public override double DefaultWeight => 1.0; + public Obi(int hue = 0) : base(0x27A0, hue) => Weight = 1.0; } [Flippable(0x2B68, 0x315F)] @@ -39,11 +31,7 @@ namespace Server.Items public partial class WoodlandBelt : BaseWaist { [Constructible] - public WoodlandBelt(int hue = 0) : base(0x2B68, hue) - { - } - - public override double DefaultWeight => 4.0; + public WoodlandBelt(int hue = 0) : base(0x2B68, hue) => Weight = 4.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Construction/Ankhs.cs b/Projects/UOContent/Items/Construction/Ankhs.cs index 80105a16a..985092f43 100644 --- a/Projects/UOContent/Items/Construction/Ankhs.cs +++ b/Projects/UOContent/Items/Construction/Ankhs.cs @@ -14,7 +14,7 @@ namespace Server.Items public static void GetContextMenuEntries(Mobile from, Item item, ref PooledRefList list) { - if (Core.UOTD && !Core.AOS && from is PlayerMobile mobile) + if (from is PlayerMobile mobile) { list.Add(new LockKarmaEntry(mobile.KarmaLocked)); } diff --git a/Projects/UOContent/Items/Construction/Chairs/Benchs.cs b/Projects/UOContent/Items/Construction/Chairs/Benchs.cs index e3ba5b2f3..f37deba0b 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Benchs.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Benchs.cs @@ -8,10 +8,6 @@ namespace Server.Items public partial class WoodenBench : Item { [Constructible] - public WoodenBench() : base(0xB2D) - { - } - - public override double DefaultWeight => 6; + public WoodenBench() : base(0xB2D) => Weight = 6; } } diff --git a/Projects/UOContent/Items/Construction/Chairs/Chairs.cs b/Projects/UOContent/Items/Construction/Chairs/Chairs.cs index 01f288d17..12b2b9ad8 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Chairs.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Chairs.cs @@ -8,11 +8,7 @@ namespace Server.Items public partial class FancyWoodenChairCushion : Item { [Constructible] - public FancyWoodenChairCushion() : base(0xB4F) - { - } - - public override double DefaultWeight => 20.0; + public FancyWoodenChairCushion() : base(0xB4F) => Weight = 20.0; } [Furniture] @@ -21,11 +17,7 @@ namespace Server.Items public partial class WoodenChairCushion : Item { [Constructible] - public WoodenChairCushion() : base(0xB53) - { - } - - public override double DefaultWeight => 20.0; + public WoodenChairCushion() : base(0xB53) => Weight = 20.0; } [Furniture] @@ -34,11 +26,7 @@ namespace Server.Items public partial class WoodenChair : Item { [Constructible] - public WoodenChair() : base(0xB57) - { - } - - public override double DefaultWeight => 20.0; + public WoodenChair() : base(0xB57) => Weight = 20.0; } [Furniture] @@ -47,11 +35,7 @@ namespace Server.Items public partial class BambooChair : Item { [Constructible] - public BambooChair() : base(0xB5B) - { - } - - public override double DefaultWeight => 20.0; + public BambooChair() : base(0xB5B) => Weight = 20.0; } [DynamicFlipping] @@ -60,11 +44,7 @@ namespace Server.Items public partial class StoneChair : Item { [Constructible] - public StoneChair() : base(0x1218) - { - } - - public override double DefaultWeight => 20; + public StoneChair() : base(0x1218) => Weight = 20; } [DynamicFlipping] @@ -73,11 +53,7 @@ namespace Server.Items public partial class OrnateElvenChair : Item { [Constructible] - public OrnateElvenChair() : base(0x2DE3) - { - } - - public override double DefaultWeight => 1.0; + public OrnateElvenChair() : base(0x2DE3) => Weight = 1.0; } [DynamicFlipping] diff --git a/Projects/UOContent/Items/Construction/Chairs/Stools.cs b/Projects/UOContent/Items/Construction/Chairs/Stools.cs index 30566f429..837b36972 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Stools.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Stools.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Stool : Item { [Constructible] - public Stool() : base(0xA2A) - { - } - - public override double DefaultWeight => 10.0; + public Stool() : base(0xA2A) => Weight = 10.0; } [Furniture] @@ -19,10 +15,6 @@ namespace Server.Items public partial class FootStool : Item { [Constructible] - public FootStool() : base(0xB5E) - { - } - - public override double DefaultWeight => 6.0; + public FootStool() : base(0xB5E) => Weight = 6.0; } } diff --git a/Projects/UOContent/Items/Construction/Chairs/Thrones.cs b/Projects/UOContent/Items/Construction/Chairs/Thrones.cs index 5bb86a2d2..de189504f 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Thrones.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Thrones.cs @@ -8,11 +8,7 @@ namespace Server.Items public partial class Throne : Item { [Constructible] - public Throne() : base(0xB33) - { - } - - public override double DefaultWeight => 1.0; + public Throne() : base(0xB33) => Weight = 1.0; } [Furniture] @@ -21,10 +17,6 @@ namespace Server.Items public partial class WoodenThrone : Item { [Constructible] - public WoodenThrone() : base(0xB2E) - { - } - - public override double DefaultWeight => 15.0; + public WoodenThrone() : base(0xB2E) => Weight = 15.0; } } diff --git a/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs b/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs index 4382653fc..d0a5c7a3e 100644 --- a/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs +++ b/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GiantReplicaAcorn : Item { [Constructible] - public GiantReplicaAcorn() : base(0x2D4A) - { - } - - public override double DefaultWeight => 1.0; + public GiantReplicaAcorn() : base(0x2D4A) => Weight = 1.0; public override int LabelNumber => 1072889; // giant replica acorn } diff --git a/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs b/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs index 4c3f11433..c3126fff4 100644 --- a/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs +++ b/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class MountedDreadHorn : Item { [Constructible] - public MountedDreadHorn() : base(0x3158) - { - } - - public override double DefaultWeight => 1.0; + public MountedDreadHorn() : base(0x3158) => Weight = 1.0; public override int LabelNumber => 1074464; // mounted Dread Horn } diff --git a/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs b/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs index ef779760b..5997ab735 100644 --- a/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs +++ b/Projects/UOContent/Items/Construction/Doors/BaseDoor.cs @@ -74,31 +74,43 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable Movable = false; } - [SerializableField(1, fieldChanged: nameof(OnOpenChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private bool _open; - - private void OnOpenChanged(bool oldValue, bool newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public bool Open { - ItemID = _open ? _openedId : _closedId; - if (_open) + get => _open; + set { - Location = new Point3D(X + _offset.X, Y + _offset.Y, Z + _offset.Z); - } - else - { - Location = new Point3D(X - _offset.X, Y - _offset.Y, Z - _offset.Z); - } - Effects.PlaySound(this, _open ? OpenedSound : ClosedSound); - if (_open) - { - _timer ??= new InternalTimer(this); - _timer.Start(); - } - else - { - _timer.Stop(); - _timer = null; + if (_open != value) + { + _open = value; + + ItemID = _open ? _openedId : _closedId; + + if (_open) + { + Location = new Point3D(X + _offset.X, Y + _offset.Y, Z + _offset.Z); + } + else + { + Location = new Point3D(X - _offset.X, Y - _offset.Y, Z - _offset.Z); + } + + Effects.PlaySound(this, _open ? OpenedSound : ClosedSound); + + if (_open) + { + _timer ??= new InternalTimer(this); + _timer.Start(); + } + else + { + _timer.Stop(); + _timer = null; + } + + this.MarkDirty(); + } } } @@ -219,8 +231,8 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable return; } - var x = m.X; - var y = m.Y; + int x = m.X; + int y = m.Y; CalcMoves.Offset(m.Direction, ref x, ref y); @@ -411,13 +423,6 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable } } - public override void OnDelete() - { - _timer?.Stop(); - _timer = null; - Link = null; - } - [AfterDeserialization] private void AfterDeserialization() { @@ -437,12 +442,6 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable protected override void OnTick() { - if (_door.Deleted) - { - Stop(); - return; - } - if (_door.Open && _door.IsFreeToClose()) { _door.Open = false; diff --git a/Projects/UOContent/Items/Construction/Misc/BarrelParts.cs b/Projects/UOContent/Items/Construction/Misc/BarrelParts.cs index f3505c680..79cc7b8ce 100644 --- a/Projects/UOContent/Items/Construction/Misc/BarrelParts.cs +++ b/Projects/UOContent/Items/Construction/Misc/BarrelParts.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class BarrelLid : Item { [Constructible] - public BarrelLid() : base(0x1DB8) - { - } - - public override double DefaultWeight => 2; + public BarrelLid() : base(0x1DB8) => Weight = 2; } [Flippable(0x1EB1, 0x1EB2, 0x1EB3, 0x1EB4)] @@ -18,22 +14,14 @@ public partial class BarrelLid : Item public partial class BarrelStaves : Item { [Constructible] - public BarrelStaves() : base(0x1EB1) - { - } - - public override double DefaultWeight => 1; + public BarrelStaves() : base(0x1EB1) => Weight = 1; } [SerializationGenerator(0, false)] public partial class BarrelHoops : Item { [Constructible] - public BarrelHoops() : base(0x1DB7) - { - } - - public override double DefaultWeight => 5; + public BarrelHoops() : base(0x1DB7) => Weight = 5; public override int LabelNumber => 1011228; // Barrel hoops } @@ -42,9 +30,5 @@ public partial class BarrelHoops : Item public partial class BarrelTap : Item { [Constructible] - public BarrelTap() : base(0x1004) - { - } - - public override double DefaultWeight => 1; + public BarrelTap() : base(0x1004) => Weight = 1; } diff --git a/Projects/UOContent/Items/Construction/Misc/Easel.cs b/Projects/UOContent/Items/Construction/Misc/Easel.cs index 238febe3a..18b3af63c 100644 --- a/Projects/UOContent/Items/Construction/Misc/Easel.cs +++ b/Projects/UOContent/Items/Construction/Misc/Easel.cs @@ -9,9 +9,5 @@ namespace Server.Items; public partial class Easel : Item { [Constructible] - public Easel() : base(0xF65) - { - } - - public override double DefaultWeight => 25.0; + public Easel() : base(0xF65) => Weight = 25.0; } diff --git a/Projects/UOContent/Items/Construction/Misc/MusicStand.cs b/Projects/UOContent/Items/Construction/Misc/MusicStand.cs index d323e1aac..b605dc5e1 100644 --- a/Projects/UOContent/Items/Construction/Misc/MusicStand.cs +++ b/Projects/UOContent/Items/Construction/Misc/MusicStand.cs @@ -8,11 +8,7 @@ namespace Server.Items; public partial class TallMusicStand : Item { [Constructible] - public TallMusicStand() : base(0xEBB) - { - } - - public override double DefaultWeight => 10.0; + public TallMusicStand() : base(0xEBB) => Weight = 10.0; } [Furniture] @@ -21,9 +17,5 @@ public partial class TallMusicStand : Item public partial class ShortMusicStand : Item { [Constructible] - public ShortMusicStand() : base(0xEB6) - { - } - - public override double DefaultWeight => 10.0; + public ShortMusicStand() : base(0xEB6) => Weight = 10.0; } diff --git a/Projects/UOContent/Items/Construction/Misc/Screens.cs b/Projects/UOContent/Items/Construction/Misc/Screens.cs index 5afffb6f1..7855f8a64 100644 --- a/Projects/UOContent/Items/Construction/Misc/Screens.cs +++ b/Projects/UOContent/Items/Construction/Misc/Screens.cs @@ -8,11 +8,7 @@ namespace Server.Items; public partial class BambooScreen : Item { [Constructible] - public BambooScreen() : base(0x24D0) - { - } - - public override double DefaultWeight => 20.0; + public BambooScreen() : base(0x24D0) => Weight = 20.0; } [Furniture] @@ -21,9 +17,5 @@ public partial class BambooScreen : Item public partial class ShojiScreen : Item { [Constructible] - public ShojiScreen() : base(0x24CB) - { - } - - public override double DefaultWeight => 20.0; + public ShojiScreen() : base(0x24CB) => Weight = 20.0; } diff --git a/Projects/UOContent/Items/Construction/Misc/Statues.cs b/Projects/UOContent/Items/Construction/Misc/Statues.cs index 80de53dc6..9288bc827 100644 --- a/Projects/UOContent/Items/Construction/Misc/Statues.cs +++ b/Projects/UOContent/Items/Construction/Misc/Statues.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class StatueSouth : Item { [Constructible] - public StatueSouth() : base(0x139A) - { - } - - public override double DefaultWeight => 10; + public StatueSouth() : base(0x139A) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -19,11 +15,7 @@ public partial class StatueSouth : Item public partial class StatueSouth2 : Item { [Constructible] - public StatueSouth2() : base(0x1227) - { - } - - public override double DefaultWeight => 10; + public StatueSouth2() : base(0x1227) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -32,11 +24,7 @@ public partial class StatueSouth2 : Item public partial class StatueNorth : Item { [Constructible] - public StatueNorth() : base(0x139B) - { - } - - public override double DefaultWeight => 10; + public StatueNorth() : base(0x139B) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -45,11 +33,7 @@ public partial class StatueNorth : Item public partial class StatueWest : Item { [Constructible] - public StatueWest() : base(0x1226) - { - } - - public override double DefaultWeight => 10; + public StatueWest() : base(0x1226) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -58,11 +42,7 @@ public partial class StatueWest : Item public partial class StatueEast : Item { [Constructible] - public StatueEast() : base(0x139C) - { - } - - public override double DefaultWeight => 10; + public StatueEast() : base(0x139C) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -71,11 +51,7 @@ public partial class StatueEast : Item public partial class StatueEast2 : Item { [Constructible] - public StatueEast2() : base(0x1224) - { - } - - public override double DefaultWeight => 10; + public StatueEast2() : base(0x1224) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -84,11 +60,7 @@ public partial class StatueEast2 : Item public partial class StatueSouthEast : Item { [Constructible] - public StatueSouthEast() : base(0x1225) - { - } - - public override double DefaultWeight => 10; + public StatueSouthEast() : base(0x1225) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -97,11 +69,7 @@ public partial class StatueSouthEast : Item public partial class BustSouth : Item { [Constructible] - public BustSouth() : base(0x12CB) - { - } - - public override double DefaultWeight => 10; + public BustSouth() : base(0x12CB) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -110,11 +78,7 @@ public partial class BustSouth : Item public partial class BustEast : Item { [Constructible] - public BustEast() : base(0x12CA) - { - } - - public override double DefaultWeight => 10; + public BustEast() : base(0x12CA) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -123,11 +87,7 @@ public partial class BustEast : Item public partial class StatuePegasus : Item { [Constructible] - public StatuePegasus() : base(0x139D) - { - } - - public override double DefaultWeight => 10; + public StatuePegasus() : base(0x139D) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -136,11 +96,7 @@ public partial class StatuePegasus : Item public partial class StatuePegasus2 : Item { [Constructible] - public StatuePegasus2() : base(0x1228) - { - } - - public override double DefaultWeight => 10; + public StatuePegasus2() : base(0x1228) => Weight = 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -149,9 +105,5 @@ public partial class StatuePegasus2 : Item public partial class SmallTowerSculpture : Item { [Constructible] - public SmallTowerSculpture() : base(0x241A) - { - } - - public override double DefaultWeight => 20.0; + public SmallTowerSculpture() : base(0x241A) => Weight = 20.0; } diff --git a/Projects/UOContent/Items/Construction/Misc/Vase.cs b/Projects/UOContent/Items/Construction/Misc/Vase.cs index 9ec2571ea..e4e3e2637 100644 --- a/Projects/UOContent/Items/Construction/Misc/Vase.cs +++ b/Projects/UOContent/Items/Construction/Misc/Vase.cs @@ -6,31 +6,19 @@ namespace Server.Items; public partial class Vase : Item { [Constructible] - public Vase() : base(0xB46) - { - } - - public override double DefaultWeight => 10; + public Vase() : base(0xB46) => Weight = 10; } [SerializationGenerator(0, false)] public partial class LargeVase : Item { [Constructible] - public LargeVase() : base(0xB45) - { - } - - public override double DefaultWeight => 15; + public LargeVase() : base(0xB45) => Weight = 15; } [SerializationGenerator(0, false)] public partial class SmallUrn : Item { [Constructible] - public SmallUrn() : base(0x241C) - { - } - - public override double DefaultWeight => 20.0; + public SmallUrn() : base(0x241C) => Weight = 20.0; } diff --git a/Projects/UOContent/Items/Construction/Misc/Vines.cs b/Projects/UOContent/Items/Construction/Misc/Vines.cs index a00fee548..2c6a20e28 100644 --- a/Projects/UOContent/Items/Construction/Misc/Vines.cs +++ b/Projects/UOContent/Items/Construction/Misc/Vines.cs @@ -1,4 +1,3 @@ -using System; using ModernUO.Serialization; namespace Server.Items; @@ -12,11 +11,16 @@ public partial class Vines : Item } [Constructible] - public Vines(int v) : base(0xCEB + Math.Clamp(v, 0, 7)) + public Vines(int v) : base(0xCEB) { - } + if (v is < 0 or > 7) + { + v = 0; + } - public override double DefaultWeight => 1.0; + ItemID += v; + Weight = 1.0; + } public override bool ForceShowProperties => ObjectPropertyList.Enabled; } diff --git a/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs b/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs index 774e222fd..c17fbe287 100644 --- a/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs +++ b/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs @@ -8,10 +8,6 @@ namespace Server.Items public partial class ElvenPodium : Item { [Constructible] - public ElvenPodium() : base(0x2DDD) - { - } - - public override double DefaultWeight => 2.0; + public ElvenPodium() : base(0x2DDD) => Weight = 2.0; } } diff --git a/Projects/UOContent/Items/Construction/Tables/Tables.cs b/Projects/UOContent/Items/Construction/Tables/Tables.cs index 0b2f17f95..b7602efaa 100644 --- a/Projects/UOContent/Items/Construction/Tables/Tables.cs +++ b/Projects/UOContent/Items/Construction/Tables/Tables.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class ElegantLowTable : Item { [Constructible] - public ElegantLowTable() : base(0x2819) - { - } - - public override double DefaultWeight => 1.0; + public ElegantLowTable() : base(0x2819) => Weight = 1.0; } [Furniture] @@ -19,11 +15,7 @@ namespace Server.Items public partial class PlainLowTable : Item { [Constructible] - public PlainLowTable() : base(0x281A) - { - } - - public override double DefaultWeight => 1.0; + public PlainLowTable() : base(0x281A) => Weight = 1.0; } [Furniture] @@ -32,11 +24,7 @@ namespace Server.Items public partial class LargeTable : Item { [Constructible] - public LargeTable() : base(0xB90) - { - } - - public override double DefaultWeight => 1.0; + public LargeTable() : base(0xB90) => Weight = 1.0; } [Furniture] @@ -45,11 +33,7 @@ namespace Server.Items public partial class Nightstand : Item { [Constructible] - public Nightstand() : base(0xB35) - { - } - - public override double DefaultWeight => 1.0; + public Nightstand() : base(0xB35) => Weight = 1.0; } [Furniture] @@ -58,10 +42,6 @@ namespace Server.Items public partial class YewWoodTable : Item { [Constructible] - public YewWoodTable() : base(0xB8F) - { - } - - public override double DefaultWeight => 1.0; + public YewWoodTable() : base(0xB8F) => Weight = 1.0; } } diff --git a/Projects/UOContent/Items/Construction/Tables/WritingTable.cs b/Projects/UOContent/Items/Construction/Tables/WritingTable.cs index 822f145a2..077978a8d 100644 --- a/Projects/UOContent/Items/Construction/Tables/WritingTable.cs +++ b/Projects/UOContent/Items/Construction/Tables/WritingTable.cs @@ -8,10 +8,6 @@ namespace Server.Items public partial class WritingTable : Item { [Constructible] - public WritingTable() : base(0xB4A) - { - } - - public override double DefaultWeight => 1.0; + public WritingTable() : base(0xB4A) => Weight = 1.0; } } diff --git a/Projects/UOContent/Items/Containers/Container.cs b/Projects/UOContent/Items/Containers/Container.cs index 164c285c4..6afb9105f 100644 --- a/Projects/UOContent/Items/Containers/Container.cs +++ b/Projects/UOContent/Items/Containers/Container.cs @@ -3,7 +3,6 @@ using Server.Collections; using Server.ContextMenus; using Server.Mobiles; using Server.Multis; -using Server.Systems.FeatureFlags; namespace Server.Items; @@ -20,18 +19,6 @@ public abstract class BaseContainer : Container public override int DefaultMaxWeight => IsSecure ? 0 : base.DefaultMaxWeight; - public override void DisplayTo(Mobile to) - { - if (to.AccessLevel < FeatureFlagSettings.RequiredAccessLevel - && FeatureFlagManager.IsItemUseBlocked(GetType(), out var reason)) - { - to.SendMessage(0x22, reason); - return; - } - - base.DisplayTo(to); - } - public override bool IsAccessibleTo(Mobile m) => BaseHouse.CheckAccessible(m, this) && base.IsAccessibleTo(m); public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) @@ -181,10 +168,9 @@ public partial class CreatureBackpack : Backpack // Used on BaseCreature Name = name; Layer = Layer.Backpack; Hue = 5; + Weight = 3.0; } - public override double DefaultWeight => 3.0; - public override void AddNameProperty(IPropertyList list) { if (Name != null) @@ -227,9 +213,11 @@ public partial class CreatureBackpack : Backpack // Used on BaseCreature public partial class StrongBackpack : Backpack // Used on Pack animals { [Constructible] - public StrongBackpack() => Layer = Layer.Backpack; - - public override double DefaultWeight => 13.0; + public StrongBackpack() + { + Layer = Layer.Backpack; + Weight = 13.0; + } public override int DefaultMaxWeight => 1600; @@ -245,9 +233,11 @@ public partial class StrongBackpack : Backpack // Used on Pack animals public partial class Backpack : BaseContainer, IDyable { [Constructible] - public Backpack() : base(0xE75) => Layer = Layer.Backpack; - - public override double DefaultWeight => 3.0; + public Backpack() : base(0xE75) + { + Layer = Layer.Backpack; + Weight = 3.0; + } public override int DefaultMaxWeight { @@ -279,21 +269,13 @@ public partial class Backpack : BaseContainer, IDyable public partial class Pouch : TrappableContainer { [Constructible] - public Pouch() : base(0xE79) - { - } - - public override double DefaultWeight => 1.0; + public Pouch() : base(0xE79) => Weight = 1.0; } [SerializationGenerator(0, false)] public abstract partial class BaseBagBall : BaseContainer, IDyable { - public BaseBagBall(int itemID) : base(itemID) - { - } - - public override double DefaultWeight => 1.0; + public BaseBagBall(int itemID) : base(itemID) => Weight = 1.0; public bool Dye(Mobile from, DyeTub sender) { @@ -330,11 +312,7 @@ public partial class LargeBagBall : BaseBagBall public partial class Bag : BaseContainer, IDyable { [Constructible] - public Bag() : base(0xE76) - { - } - - public override double DefaultWeight => 2.0; + public Bag() : base(0xE76) => Weight = 2.0; public bool Dye(Mobile from, DyeTub sender) { @@ -353,44 +331,28 @@ public partial class Bag : BaseContainer, IDyable public partial class Barrel : BaseContainer { [Constructible] - public Barrel() : base(0xE77) - { - } - - public override double DefaultWeight => 25.0; + public Barrel() : base(0xE77) => Weight = 25.0; } [SerializationGenerator(0, false)] public partial class Keg : BaseContainer { [Constructible] - public Keg() : base(0xE7F) - { - } - - public override double DefaultWeight => 15.0; + public Keg() : base(0xE7F) => Weight = 15.0; } [SerializationGenerator(0, false)] public partial class PicnicBasket : BaseContainer { [Constructible] - public PicnicBasket() : base(0xE7A) - { - } - - public override double DefaultWeight => 2.0; + public PicnicBasket() : base(0xE7A) => Weight = 2.0; } [SerializationGenerator(0, false)] public partial class Basket : BaseContainer { [Constructible] - public Basket() : base(0x990) - { - } - - public override double DefaultWeight => 1.0; + public Basket() : base(0x990) => Weight = 1.0; } [Furniture] @@ -399,11 +361,7 @@ public partial class Basket : BaseContainer public partial class WoodenBox : LockableContainer { [Constructible] - public WoodenBox() : base(0x9AA) - { - } - - public override double DefaultWeight => 4.0; + public WoodenBox() : base(0x9AA) => Weight = 4.0; } [Furniture] @@ -412,11 +370,7 @@ public partial class WoodenBox : LockableContainer public partial class SmallCrate : LockableContainer { [Constructible] - public SmallCrate() : base(0x9A9) - { - } - - public override double DefaultWeight => 2.0; + public SmallCrate() : base(0x9A9) => Weight = 2.0; } [Furniture] @@ -425,11 +379,7 @@ public partial class SmallCrate : LockableContainer public partial class MediumCrate : LockableContainer { [Constructible] - public MediumCrate() : base(0xE3F) - { - } - - public override double DefaultWeight => 2.0; + public MediumCrate() : base(0xE3F) => Weight = 2.0; } [Furniture] @@ -438,11 +388,7 @@ public partial class MediumCrate : LockableContainer public partial class LargeCrate : LockableContainer { [Constructible] - public LargeCrate() : base(0xE3D) - { - } - - public override double DefaultWeight => 1.0; + public LargeCrate() : base(0xE3D) => Weight = 1.0; } [DynamicFlipping] @@ -483,11 +429,7 @@ public partial class MetalGoldenChest : LockableContainer public partial class WoodenChest : LockableContainer { [Constructible] - public WoodenChest() : base(0xe43) - { - } - - public override double DefaultWeight => 2.0; + public WoodenChest() : base(0xe43) => Weight = 2.0; } [Furniture] diff --git a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs index da4d8b77f..a661b6e26 100644 --- a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs +++ b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainer.cs @@ -3,22 +3,19 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(3, false)] +[SerializationGenerator(2, false)] public abstract partial class FillableContainer : LockableContainer { + [TimerDrift] [SerializableField(1)] - [DeserializeTimer(nameof(DeserializeRespawnTimer))] private Timer _respawnTimer; - private void DeserializeRespawnTimer(TimeSpan delay) => _respawnTimer = Timer.DelayCall(delay, Respawn); - - private void MigrateFrom(V2Content content) + [DeserializeTimerField(1)] + private void DeserializeRespawnTimer(TimeSpan delay) { - _contentType = content.ContentType; - - if (content.RespawnTimerDelay != TimeSpan.MinValue) + if (delay > TimeSpan.MinValue) { - DeserializeRespawnTimer(content.RespawnTimerDelay); + _respawnTimer = Timer.DelayCall(delay, Respawn); } } @@ -82,23 +79,20 @@ public abstract partial class FillableContainer : LockableContainer public virtual void AcquireContent() { - if (_contentType == FillableContentType.None) + if (_contentType != FillableContentType.None) { - _contentType = FillableContent.Acquire(GetWorldLocation(), Map); + return; } + // Don't trigger serialization code + _contentType = FillableContent.Acquire(GetWorldLocation(), Map); + if (_contentType != FillableContentType.None) { Respawn(); } } - public override void LockPick(Mobile from) - { - base.LockPick(from); - CheckRespawn(); - } - public override void OnItemRemoved(Item item) { CheckRespawn(); @@ -126,13 +120,29 @@ public abstract partial class FillableContainer : LockableContainer public void CheckRespawn() { - if (_respawnTimer?.Running == true) - { - return; - } + var canSpawn = + _contentType != FillableContentType.None && + !Deleted && !Movable && Parent == null && !IsLockedDown && !IsSecure && + ( + GetItemsCount() <= SpawnThreshold || + IsLockable && !Locked || + IsTrappable && TrapType == TrapType.None + ); - var delay = TimeSpan.FromMinutes(Utility.RandomMinMax(MinRespawnMinutes, MaxRespawnMinutes)); - _respawnTimer = Timer.DelayCall(delay, Respawn); + if (canSpawn) + { + if (_respawnTimer?.Running != true) + { + var mins = Utility.RandomMinMax(MinRespawnMinutes, MaxRespawnMinutes); + var delay = TimeSpan.FromMinutes(mins); + _respawnTimer = Timer.DelayCall(delay, Respawn); + } + } + else + { + _respawnTimer?.Stop(); + _respawnTimer = null; + } } public void Respawn() @@ -172,6 +182,8 @@ public abstract partial class FillableContainer : LockableContainer TrapPower = 0; TrapLevel = 0; } + + CheckRespawn(); } protected virtual int GetSpawnCount() diff --git a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainers.cs b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainers.cs index 2ee839712..459638fc4 100644 --- a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainers.cs +++ b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContainers.cs @@ -6,25 +6,22 @@ namespace Server.Items; [SerializationGenerator(0)] public partial class LibraryBookcase : FillableContainer { - public override bool IsLockable => false; - [Constructible] - public LibraryBookcase() : base(0xA97) - { - } + public LibraryBookcase() : base(0xA97) => Weight = 1.0; - protected override int GetSpawnCount() - { - var max = 5 - GetItemsCount(); - return max <= 0 ? 0 : Utility.RandomMinMax(1, max); - } + public override bool IsLockable => false; + public override int SpawnThreshold => 5; + + protected override int GetSpawnCount() => 5 - GetItemsCount(); public override void AcquireContent() { - if (ContentType == FillableContentType.None) + if (ContentType != FillableContentType.None) { - ContentType = FillableContentType.Library; + return; } + + ContentType = FillableContentType.Library; } } @@ -33,9 +30,7 @@ public partial class LibraryBookcase : FillableContainer public partial class FillableLargeCrate : FillableContainer { [Constructible] - public FillableLargeCrate() : base(0xE3D) - { - } + public FillableLargeCrate() : base(0xE3D) => Weight = 1.0; } [Flippable(0x9A9, 0xE7E)] @@ -43,9 +38,7 @@ public partial class FillableLargeCrate : FillableContainer public partial class FillableSmallCrate : FillableContainer { [Constructible] - public FillableSmallCrate() : base(0x9A9) - { - } + public FillableSmallCrate() : base(0x9A9) => Weight = 1.0; } [Flippable(0x9AA, 0xE7D)] @@ -53,9 +46,7 @@ public partial class FillableSmallCrate : FillableContainer public partial class FillableWoodenBox : FillableContainer { [Constructible] - public FillableWoodenBox() : base(0x9AA) - { - } + public FillableWoodenBox() : base(0x9AA) => Weight = 4.0; } [Flippable(0x9A8, 0xE80)] diff --git a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.ContentTypes.cs b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.ContentTypes.cs index 9ac83ccb4..55a7b2ab1 100644 --- a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.ContentTypes.cs +++ b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.ContentTypes.cs @@ -48,8 +48,12 @@ public partial class FillableContent { private static readonly FillableContent Alchemist = new( 1, - [typeof(Alchemist)], - [ + new[] + { + typeof(Alchemist) + }, + new[] + { new FillableEntry(typeof(NightSightPotion)), new FillableEntry(typeof(LesserCurePotion)), new FillableEntry(typeof(AgilityPotion)), @@ -59,13 +63,17 @@ public partial class FillableContent new FillableEntry(typeof(LesserHealPotion)), new FillableEntry(typeof(LesserExplosionPotion)), new FillableEntry(typeof(MortarPestle)) - ] + } ); private static readonly FillableContent Armorer = new( 2, - [typeof(Armorer)], - [ + new[] + { + typeof(Armorer) + }, + new[] + { new FillableEntry(2, typeof(ChainCoif)), new FillableEntry(1, typeof(PlateGorget)), new FillableEntry(1, typeof(BronzeShield)), @@ -74,13 +82,14 @@ public partial class FillableContent new FillableEntry(2, typeof(HeaterShield)), new FillableEntry(1, typeof(WoodenShield)), new FillableEntry(1, typeof(MetalShield)) - ] + } ); private static readonly FillableContent ArtisanGuild = new( 1, - [], - [ + Array.Empty(), + new[] + { new FillableEntry(1, typeof(PaintsAndBrush)), new FillableEntry(1, typeof(SledgeHammer)), new FillableEntry(2, typeof(SmithHammer)), @@ -101,36 +110,50 @@ public partial class FillableContent new FillableEntry(4, typeof(OrcHelm)), new FillableEntry(1, typeof(TribalMask)), new FillableEntry(1, typeof(HornedTribalMask)) - ] + } ); private static readonly FillableContent Baker = new( 1, - [typeof(Baker)], - [ + new[] + { + typeof(Baker) + }, + new[] + { new FillableEntry(1, typeof(RollingPin)), new FillableEntry(2, typeof(SackFlour)), new FillableEntry(2, typeof(BreadLoaf)), new FillableEntry(1, typeof(FrenchBread)) - ] + } ); private static readonly FillableContent Bard = new( 1, - [typeof(Bard), typeof(BardGuildmaster)], - [ + new[] + { + typeof(Bard), + typeof(BardGuildmaster) + }, + new[] + { new FillableEntry(1, typeof(LapHarp)), new FillableEntry(2, typeof(Lute)), new FillableEntry(1, typeof(Drums)), new FillableEntry(1, typeof(Tambourine)), new FillableEntry(1, typeof(TambourineTassel)) - ] + } ); private static readonly FillableContent Blacksmith = new( 2, - [typeof(Blacksmith), typeof(BlacksmithGuildmaster)], - [ + new[] + { + typeof(Blacksmith), + typeof(BlacksmithGuildmaster) + }, + new[] + { new FillableEntry(8, typeof(SmithHammer)), new FillableEntry(8, typeof(Tongs)), new FillableEntry(8, typeof(SledgeHammer)), @@ -142,23 +165,31 @@ public partial class FillableContent new FillableEntry(1, typeof(CopperWire)), new FillableEntry(1, typeof(HorseShoes)), new FillableEntry(1, typeof(ForgedMetal)) - ] + } ); private static readonly FillableContent Bowyer = new( 2, - [typeof(Bowyer)], - [ + new[] + { + typeof(Bowyer) + }, + new[] + { new FillableEntry(2, typeof(Bow)), new FillableEntry(2, typeof(Crossbow)), new FillableEntry(1, typeof(Arrow)) - ] + } ); private static readonly FillableContent Butcher = new( 1, - [typeof(Butcher)], - [ + new[] + { + typeof(Butcher) + }, + new[] + { new FillableEntry(2, typeof(Cleaver)), new FillableEntry(2, typeof(SlabOfBacon)), new FillableEntry(2, typeof(Bacon)), @@ -174,13 +205,19 @@ public partial class FillableContent new FillableEntry(2, typeof(Sausage)), new FillableEntry(1, typeof(RawChickenLeg)), new FillableEntry(1, typeof(ChickenLeg)) - ] + } ); private static readonly FillableContent Carpenter = new( 1, - [typeof(Carpenter), typeof(Architect), typeof(RealEstateBroker)], - [ + new[] + { + typeof(Carpenter), + typeof(Architect), + typeof(RealEstateBroker) + }, + new[] + { new FillableEntry(1, typeof(ChiselsNorth)), new FillableEntry(1, typeof(ChiselsWest)), new FillableEntry(2, typeof(DovetailSaw)), @@ -195,13 +232,19 @@ public partial class FillableContent new FillableEntry(1, typeof(Froe)), new FillableEntry(1, typeof(Inshave)), new FillableEntry(1, typeof(Scorp)) - ] + } ); private static readonly FillableContent Clothier = new( 1, - [typeof(Tailor), typeof(Weaver), typeof(TailorGuildmaster)], - [ + new[] + { + typeof(Tailor), + typeof(Weaver), + typeof(TailorGuildmaster) + }, + new[] + { new FillableEntry(1, typeof(Cotton)), new FillableEntry(1, typeof(Wool)), new FillableEntry(1, typeof(DarkYarn)), @@ -215,36 +258,50 @@ public partial class FillableContent // new FillableEntry( 1, typeof( FoldedCloth ) ), new FillableEntry(1, typeof(Dyes)), new FillableEntry(2, typeof(Leather)) - ] + } ); private static readonly FillableContent Cobbler = new( 1, - [typeof(Cobbler)], - [ + new[] + { + typeof(Cobbler) + }, + new[] + { new FillableEntry(1, typeof(Boots)), new FillableEntry(2, typeof(Shoes)), new FillableEntry(2, typeof(Sandals)), new FillableEntry(1, typeof(ThighBoots)) - ] + } ); private static readonly FillableContent Docks = new( 1, - [typeof(Fisherman), typeof(FisherGuildmaster)], - [ + new[] + { + typeof(Fisherman), + typeof(FisherGuildmaster) + }, + new[] + { new FillableEntry(1, typeof(FishingPole)), // Two different types // new FillableEntry( 1, typeof( SmallFish ) ), // new FillableEntry( 1, typeof( SmallFish ) ), new FillableEntry(4, typeof(Fish)) - ] + } ); private static readonly FillableContent Farm = new( 1, - [typeof(Farmer), typeof(Rancher)], - [ + new[] + { + typeof(Farmer), + typeof(Rancher) + }, + new[] + { new FillableEntry(1, typeof(Shirt)), new FillableEntry(1, typeof(ShortPants)), new FillableEntry(1, typeof(Skirt)), @@ -263,45 +320,59 @@ public partial class FillableContent new FillableEntry(1, typeof(RawLambLeg)), new FillableEntry(1, typeof(SheafOfHay)), new FillableBvrge(1, typeof(Pitcher), BeverageType.Milk) - ] + } ); private static readonly FillableContent FighterGuild = new( 3, - [typeof(WarriorGuildmaster)], - [ + new[] + { + typeof(WarriorGuildmaster) + }, + new[] + { new FillableEntry(12, Loot.ArmorTypes), new FillableEntry(8, Loot.WeaponTypes), new FillableEntry(3, Loot.ShieldTypes), new FillableEntry(1, typeof(Arrow)) - ] + } ); private static readonly FillableContent Guard = new( 3, - [], - [ + Array.Empty(), + new[] + { new FillableEntry(12, Loot.ArmorTypes), new FillableEntry(8, Loot.WeaponTypes), new FillableEntry(3, Loot.ShieldTypes), new FillableEntry(1, typeof(Arrow)) - ] + } ); private static readonly FillableContent Healer = new( 1, - [typeof(Healer), typeof(HealerGuildmaster)], - [ + new[] + { + typeof(Healer), + typeof(HealerGuildmaster) + }, + new[] + { new FillableEntry(1, typeof(Bandage)), new FillableEntry(1, typeof(MortarPestle)), new FillableEntry(1, typeof(LesserHealPotion)) - ] + } ); private static readonly FillableContent Herbalist = new( 1, - [typeof(Herbalist)], - [ + new[] + { + typeof(Herbalist) + }, + new[] + { new FillableEntry(10, typeof(Garlic)), new FillableEntry(10, typeof(Ginseng)), new FillableEntry(10, typeof(MandrakeRoot)), @@ -310,23 +381,28 @@ public partial class FillableContent new FillableEntry(1, typeof(GreenDriedFlowers)), new FillableEntry(1, typeof(DriedOnions)), new FillableEntry(1, typeof(DriedHerbs)) - ] + } ); private static readonly FillableContent Inn = new( 1, - [], - [ + Array.Empty(), + new[] + { new FillableEntry(1, typeof(Candle)), new FillableEntry(1, typeof(Torch)), new FillableEntry(1, typeof(Lantern)) - ] + } ); private static readonly FillableContent Jeweler = new( 2, - [typeof(Jeweler)], - [ + new[] + { + typeof(Jeweler) + }, + new[] + { new FillableEntry(1, typeof(GoldRing)), new FillableEntry(1, typeof(GoldBracelet)), new FillableEntry(1, typeof(GoldEarrings)), @@ -335,23 +411,33 @@ public partial class FillableContent new FillableEntry(1, typeof(Necklace)), new FillableEntry(1, typeof(Beads)), new FillableEntry(9, Loot.GemTypes) - ] + } ); private static readonly FillableContent Library = new( 1, - [typeof(Scribe)], - [ + new[] + { + typeof(Scribe) + }, + new[] + { new FillableEntry(8, Loot.LibraryBookTypes), new FillableEntry(1, typeof(RedBook)), new FillableEntry(1, typeof(BlueBook)) - ] + } ); private static readonly FillableContent Mage = new( 2, - [typeof(Mage), typeof(HolyMage), typeof(MageGuildmaster)], - [ + new[] + { + typeof(Mage), + typeof(HolyMage), + typeof(MageGuildmaster) + }, + new[] + { new FillableEntry(16, typeof(BlankScroll)), new FillableEntry(14, typeof(Spellbook)), new FillableEntry(12, Loot.RegularScrollTypes, 0, 8), @@ -362,13 +448,17 @@ public partial class FillableContent new FillableEntry(7, Loot.RegularScrollTypes, 40, 8), new FillableEntry(6, Loot.RegularScrollTypes, 48, 8), new FillableEntry(5, Loot.RegularScrollTypes, 56, 8) - ] + } ); private static readonly FillableContent Merchant = new( 1, - [typeof(MerchantGuildmaster)], - [ + new[] + { + typeof(MerchantGuildmaster) + }, + new[] + { new FillableEntry(1, typeof(CheeseWheel)), new FillableEntry(1, typeof(CheeseWedge)), new FillableEntry(1, typeof(CheeseSlice)), @@ -417,52 +507,63 @@ public partial class FillableContent new FillableEntry(2, typeof(Cantaloupe)), new FillableEntry(2, typeof(Cabbage)), new FillableEntry(4, typeof(EarOfCorn)) - ] + } ); private static readonly FillableContent Mill = new( 1, - [], - [ + Array.Empty(), + new[] + { new FillableEntry(1, typeof(SackFlour)) - ] + } ); private static readonly FillableContent Mine = new( 1, - [typeof(Miner)], - [ + new[] + { + typeof(Miner) + }, + new[] + { new FillableEntry(2, typeof(Pickaxe)), new FillableEntry(2, typeof(Shovel)), new FillableEntry(2, typeof(IronIngot)), // new FillableEntry( 2, typeof( IronOre ) ), TODO: Smaller Ore new FillableEntry(1, typeof(ForgedMetal)) - ] + } ); private static readonly FillableContent Observatory = new( 1, - [], - [ + Array.Empty(), + new[] + { new FillableEntry(2, typeof(Sextant)), new FillableEntry(2, typeof(Clock)), new FillableEntry(1, typeof(Spyglass)) - ] + } ); private static readonly FillableContent Painter = new( 1, - [], - [ + Array.Empty(), + new[] + { new FillableEntry(1, typeof(PaintsAndBrush)), new FillableEntry(2, typeof(PenAndInk)) - ] + } ); private static readonly FillableContent Provisioner = new( 1, - [typeof(Provisioner)], - [ + new[] + { + typeof(Provisioner) + }, + new[] + { new FillableEntry(1, typeof(CheeseWheel)), new FillableEntry(1, typeof(CheeseWedge)), new FillableEntry(1, typeof(CheeseSlice)), @@ -518,13 +619,18 @@ public partial class FillableContent new FillableEntry(2, typeof(Cantaloupe)), new FillableEntry(2, typeof(Cabbage)), new FillableEntry(4, typeof(EarOfCorn)) - ] + } ); private static readonly FillableContent Ranger = new( 2, - [typeof(Ranger), typeof(RangerGuildmaster)], - [ + new[] + { + typeof(Ranger), + typeof(RangerGuildmaster) + }, + new[] + { new FillableEntry(2, typeof(StuddedChest)), new FillableEntry(2, typeof(StuddedLegs)), new FillableEntry(2, typeof(StuddedArms)), @@ -552,22 +658,33 @@ public partial class FillableContent new FillableEntry(2, typeof(Crossbow)), new FillableEntry(2, typeof(HeavyCrossbow)), new FillableEntry(4, typeof(Arrow)) - ] + } ); private static readonly FillableContent Stables = new( 1, - [typeof(AnimalTrainer), typeof(GypsyAnimalTrainer)], - [ + new[] + { + typeof(AnimalTrainer), + typeof(GypsyAnimalTrainer) + }, + new[] + { // new FillableEntry( 1, typeof( Wheat ) ), new FillableEntry(1, typeof(Carrot)) - ] + } ); private static readonly FillableContent Tanner = new( 2, - [typeof(Tanner), typeof(LeatherWorker), typeof(Furtrader)], - [ + new[] + { + typeof(Tanner), + typeof(LeatherWorker), + typeof(Furtrader) + }, + new[] + { new FillableEntry(1, typeof(FeatheredHat)), new FillableEntry(1, typeof(LeatherArms)), new FillableEntry(2, typeof(LeatherLegs)), @@ -575,37 +692,54 @@ public partial class FillableContent new FillableEntry(2, typeof(LeatherGloves)), new FillableEntry(1, typeof(LeatherGorget)), new FillableEntry(2, typeof(Leather)) - ] + } ); private static readonly FillableContent Tavern = new( 1, - [typeof(TavernKeeper), typeof(Barkeeper), typeof(Waiter), typeof(Cook)], - [ + new[] + { + typeof(TavernKeeper), + typeof(Barkeeper), + typeof(Waiter), + typeof(Cook) + }, + new FillableEntry[] + { new FillableBvrge(1, typeof(BeverageBottle), BeverageType.Ale), new FillableBvrge(1, typeof(BeverageBottle), BeverageType.Wine), new FillableBvrge(1, typeof(BeverageBottle), BeverageType.Liquor), new FillableBvrge(1, typeof(Jug), BeverageType.Cider) - ] + } ); private static readonly FillableContent ThiefGuild = new( 1, - [typeof(Thief), typeof(ThiefGuildmaster)], - [ + new[] + { + typeof(Thief), + typeof(ThiefGuildmaster) + }, + new[] + { new FillableEntry(1, typeof(Lockpick)), new FillableEntry(1, typeof(BearMask)), new FillableEntry(1, typeof(DeerMask)), new FillableEntry(1, typeof(TribalMask)), new FillableEntry(1, typeof(HornedTribalMask)), new FillableEntry(4, typeof(OrcHelm)) - ] + } ); private static readonly FillableContent Tinker = new( 1, - [typeof(Tinker), typeof(TinkerGuildmaster)], - [ + new[] + { + typeof(Tinker), + typeof(TinkerGuildmaster) + }, + new[] + { new FillableEntry(1, typeof(Lockpick)), // new FillableEntry( 1, typeof( KeyRing ) ), new FillableEntry(2, typeof(Clock)), @@ -623,35 +757,43 @@ public partial class FillableContent new FillableEntry(1, typeof(DecoArrowShafts)), new FillableEntry(1, typeof(Lockpicks)), new FillableEntry(1, typeof(ToolKit)) - ] + } ); private static readonly FillableContent Veterinarian = new( 1, - [typeof(Veterinarian)], - [ + new[] + { + typeof(Veterinarian) + }, + new[] + { new FillableEntry(1, typeof(Bandage)), new FillableEntry(1, typeof(MortarPestle)), new FillableEntry(1, typeof(LesserHealPotion)), // new FillableEntry( 1, typeof( Wheat ) ), new FillableEntry(1, typeof(Carrot)) - ] + } ); private static readonly FillableContent Weaponsmith = new( 2, - [typeof(Weaponsmith)], - [ + new[] + { + typeof(Weaponsmith) + }, + new[] + { new FillableEntry(8, Loot.WeaponTypes), new FillableEntry(1, typeof(Arrow)) - ] + } ); private static Dictionary _acquireTable; // This should match the FillableContentType enum private static readonly FillableContent[] ContentTypes = - [ + { Weaponsmith, Provisioner, Mage, Alchemist, Armorer, ArtisanGuild, Baker, Bard, Blacksmith, @@ -664,5 +806,5 @@ public partial class FillableContent Painter, Ranger, Stables, Tanner, Tavern, ThiefGuild, Tinker, Veterinarian - ]; + }; } diff --git a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.cs b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.cs index 482c4641f..f9752d9b7 100644 --- a/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.cs +++ b/Projects/UOContent/Items/Containers/Fillable Containers/FillableContent.cs @@ -69,7 +69,7 @@ public partial class FillableContent public static FillableContentType Acquire(Point3D loc, Map map) { - var content = FillableContentType.None; + FillableContentType content = FillableContentType.None; if (map == null || map == Map.Internal) { diff --git a/Projects/UOContent/Items/Containers/FurnitureContainer.cs b/Projects/UOContent/Items/Containers/FurnitureContainer.cs index 0b19f4979..e638ab2d7 100644 --- a/Projects/UOContent/Items/Containers/FurnitureContainer.cs +++ b/Projects/UOContent/Items/Containers/FurnitureContainer.cs @@ -10,11 +10,7 @@ namespace Server.Items; public partial class TallCabinet : BaseContainer { [Constructible] - public TallCabinet() : base(0x2815) - { - } - - public override double DefaultWeight => 1.0; + public TallCabinet() : base(0x2815) => Weight = 1.0; } [Furniture] @@ -23,11 +19,7 @@ public partial class TallCabinet : BaseContainer public partial class ShortCabinet : BaseContainer { [Constructible] - public ShortCabinet() : base(0x2817) - { - } - - public override double DefaultWeight => 1.0; + public ShortCabinet() : base(0x2817) => Weight = 1.0; } [Furniture] @@ -36,11 +28,7 @@ public partial class ShortCabinet : BaseContainer public partial class RedArmoire : BaseContainer { [Constructible] - public RedArmoire() : base(0x2857) - { - } - - public override double DefaultWeight => 1.0; + public RedArmoire() : base(0x2857) => Weight = 1.0; } [Furniture] @@ -49,11 +37,7 @@ public partial class RedArmoire : BaseContainer public partial class CherryArmoire : BaseContainer { [Constructible] - public CherryArmoire() : base(0x285D) - { - } - - public override double DefaultWeight => 1.0; + public CherryArmoire() : base(0x285D) => Weight = 1.0; } [Furniture] @@ -62,11 +46,7 @@ public partial class CherryArmoire : BaseContainer public partial class MapleArmoire : BaseContainer { [Constructible] - public MapleArmoire() : base(0x285B) - { - } - - public override double DefaultWeight => 1.0; + public MapleArmoire() : base(0x285B) => Weight = 1.0; } [Furniture] @@ -75,11 +55,7 @@ public partial class MapleArmoire : BaseContainer public partial class ElegantArmoire : BaseContainer { [Constructible] - public ElegantArmoire() : base(0x2859) - { - } - - public override double DefaultWeight => 1.0; + public ElegantArmoire() : base(0x2859) => Weight = 1.0; } [Furniture] @@ -88,11 +64,7 @@ public partial class ElegantArmoire : BaseContainer public partial class FancyElvenArmoire : BaseContainer { [Constructible] - public FancyElvenArmoire() : base(0x2D07) - { - } - - public override double DefaultWeight => 1.0; + public FancyElvenArmoire() : base(0x2D07) => Weight = 1.0; public override int DefaultGumpID => 0x4E; public override int DefaultDropSound => 0x42; } @@ -103,11 +75,7 @@ public partial class FancyElvenArmoire : BaseContainer public partial class SimpleElvenArmoire : BaseContainer { [Constructible] - public SimpleElvenArmoire() : base(0x2D05) - { - } - - public override double DefaultWeight => 1.0; + public SimpleElvenArmoire() : base(0x2D05) => Weight = 1.0; public override int DefaultGumpID => 0x4F; public override int DefaultDropSound => 0x42; } @@ -118,11 +86,7 @@ public partial class SimpleElvenArmoire : BaseContainer public partial class FullBookcase : BaseContainer { [Constructible] - public FullBookcase() : base(0xA97) - { - } - - public override double DefaultWeight => 1.0; + public FullBookcase() : base(0xA97) => Weight = 1.0; } [Furniture] @@ -142,11 +106,7 @@ public partial class EmptyBookcase : BaseContainer public partial class Drawer : BaseContainer { [Constructible] - public Drawer() : base(0xA2C) - { - } - - public override double DefaultWeight => 1.0; + public Drawer() : base(0xA2C) => Weight = 1.0; } [Furniture] @@ -155,11 +115,7 @@ public partial class Drawer : BaseContainer public partial class FancyDrawer : BaseContainer { [Constructible] - public FancyDrawer() : base(0xA30) - { - } - - public override double DefaultWeight => 1.0; + public FancyDrawer() : base(0xA30) => Weight = 1.0; } [Furniture] @@ -168,11 +124,7 @@ public partial class FancyDrawer : BaseContainer public partial class Armoire : BaseContainer { [Constructible] - public Armoire() : base(0xA4F) - { - } - - public override double DefaultWeight => 1.0; + public Armoire() : base(0xA4F) => Weight = 1.0; public override void DisplayTo(Mobile m) { @@ -195,11 +147,7 @@ public partial class Armoire : BaseContainer public partial class FancyArmoire : BaseContainer { [Constructible] - public FancyArmoire() : base(0xA4D) - { - } - - public override double DefaultWeight => 1.0; + public FancyArmoire() : base(0xA4D) => Weight = 1.0; public override void DisplayTo(Mobile m) { diff --git a/Projects/UOContent/Items/Containers/LockableContainer.cs b/Projects/UOContent/Items/Containers/LockableContainer.cs index 76287f472..eae0e25da 100644 --- a/Projects/UOContent/Items/Containers/LockableContainer.cs +++ b/Projects/UOContent/Items/Containers/LockableContainer.cs @@ -155,7 +155,7 @@ public abstract partial class LockableContainer : TrappableContainer, ILockable, var inaccessible = from.AccessLevel < AccessLevel.GameMaster; - var number = inaccessible + int number = inaccessible ? 501747 // It appears to be locked. : 502502; // That is locked, but you open it with your godly powers. diff --git a/Projects/UOContent/Items/Containers/MarkContainer.cs b/Projects/UOContent/Items/Containers/MarkContainer.cs index 04e7c89ea..fa3f7e440 100644 --- a/Projects/UOContent/Items/Containers/MarkContainer.cs +++ b/Projects/UOContent/Items/Containers/MarkContainer.cs @@ -3,13 +3,14 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(1, false)] +[SerializationGenerator(0, false)] public partial class MarkContainer : LockableContainer { + [TimerDrift] [SerializableField(1, getter: "private", setter: "private")] - [DeserializeTimer(nameof(DeserializeRelockTimer))] private InternalTimer _relockTimer; + [DeserializeTimerField(1)] private void DeserializeRelockTimer(TimeSpan delay) { if (!Locked && _autoLock) @@ -18,19 +19,6 @@ public partial class MarkContainer : LockableContainer } } - private void MigrateFrom(V0Content content) - { - _autoLock = content.AutoLock; - _targetMap = content.TargetMap; - _target = content.Target; - _description = content.Description; - - if (content.RelockTimerDelay != TimeSpan.MinValue) - { - DeserializeRelockTimer(content.RelockTimerDelay); - } - } - [SerializableField(2)] [SerializedCommandProperty(AccessLevel.GameMaster)] private Map _targetMap; @@ -62,19 +50,23 @@ public partial class MarkContainer : LockableContainer } } - [SerializableField(0, fieldChanged: nameof(OnAutoLockChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private bool _autoLock; - - private void OnAutoLockChanged(bool oldValue, bool newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public bool AutoLock { - if (!_autoLock) + get => _autoLock; + set { - StopTimer(); - } - else if (!Locked) - { - _relockTimer ??= new InternalTimer(this); + _autoLock = value; + + if (!_autoLock) + { + StopTimer(); + } + else if (!Locked) + { + _relockTimer ??= new InternalTimer(this); + } } } diff --git a/Projects/UOContent/Items/Containers/SalvageBag.cs b/Projects/UOContent/Items/Containers/SalvageBag.cs index bb20b8775..173a4d2e6 100644 --- a/Projects/UOContent/Items/Containers/SalvageBag.cs +++ b/Projects/UOContent/Items/Containers/SalvageBag.cs @@ -19,12 +19,11 @@ public partial class SalvageBag : Bag [Constructible] public SalvageBag(int hue) { + Weight = 2.0; Hue = hue; m_Failure = false; } - public override double DefaultWeight => 2.0; - public override int LabelNumber => 1079931; // Salvage Bag public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) @@ -201,8 +200,7 @@ public partial class SalvageBag : Bag var salvaged = 0; var notSalvaged = 0; - using var queue = EnumerateItems(); - foreach (var item in queue) + foreach (var item in EnumerateItems()) { if (item?.Deleted != false) { @@ -251,37 +249,31 @@ public partial class SalvageBag : Bag var salvaged = 0; var notSalvaged = 0; - using (var queue = EnumerateItems()) + foreach (var item in EnumerateItems()) { - foreach (var item in queue) + if (item is not IScissorable scissorable) { - if (item is not IScissorable scissorable) - { - continue; - } + continue; + } - if (Scissors.CanScissor(from, scissorable) && scissorable.Scissor(from, scissors)) - { - ++salvaged; - } - else - { - ++notSalvaged; - } + if (Scissors.CanScissor(from, scissorable) && scissorable.Scissor(from, scissors)) + { + ++salvaged; + } + else + { + ++notSalvaged; } } // Salvaged: ~1_COUNT~/~2_NUM~ tailored items from.SendLocalizedMessage(1079974, $"{salvaged}\t{salvaged + notSalvaged}"); - using (var queue = EnumerateItems()) + foreach (var item in EnumerateItems()) { - foreach (var item in queue) + if (item.InTypeList(_clothTypes)) { - if (item.InTypeList(_clothTypes)) - { - from.AddToBackpack(item); - } + from.AddToBackpack(item); } } } diff --git a/Projects/UOContent/Items/Containers/Strongbox.cs b/Projects/UOContent/Items/Containers/Strongbox.cs index 7d461d715..08d6778a0 100644 --- a/Projects/UOContent/Items/Containers/Strongbox.cs +++ b/Projects/UOContent/Items/Containers/Strongbox.cs @@ -1,6 +1,6 @@ using System; +using System.Collections.Generic; using ModernUO.Serialization; -using Server.Collections; using Server.Multis; namespace Server.Items; @@ -32,6 +32,10 @@ public partial class StrongBox : BaseContainer, IChoppable public override int DefaultMaxWeight => 0; + public override bool Decays => _house == null || _owner?.Deleted != false || !_house.IsCoOwner(_owner); + + public override TimeSpan DecayTime => TimeSpan.FromMinutes(30.0); + public void OnChop(Mobile from) { if (_house?.Deleted != false || _owner?.Deleted != false || from == _owner || _house.IsOwner(from)) @@ -46,13 +50,11 @@ public partial class StrongBox : BaseContainer, IChoppable Timer.StartTimer(TimeSpan.FromSeconds(1.0), Validate); } - // A strongbox is only ever its owner's. Without a house, or without an owner still co-owning - // that house, it would be a free container anyone could loot, so it goes away instead. A deleted - // owner deserializes back as null, which IsCoOwner rejects. private void Validate() { - if (_house?.IsCoOwner(_owner) != true) + if (_owner != null && _house?.IsCoOwner(_owner) == false) { + Console.WriteLine("Warning: Destroying strongbox of {0}", _owner.Name); Destroy(); } } @@ -99,16 +101,16 @@ public partial class StrongBox : BaseContainer, IChoppable public Container ConvertToStandardContainer() { - var metalBox = new MetalBox(); - using var subItems = PooledRefList.Create(Items.Count); - subItems.AddRange(Items); + Container metalBox = new MetalBox(); + var subItems = new List(Items); - for (var i = 0; i < subItems.Count; i++) + foreach (var subItem in subItems) { - metalBox.AddItem(subItems[i]); + metalBox.AddItem(subItem); } Delete(); + return metalBox; } } diff --git a/Projects/UOContent/Items/Containers/TreasureMapChest.cs b/Projects/UOContent/Items/Containers/TreasureMapChest.cs index a1aafa0b3..22c5101cf 100644 --- a/Projects/UOContent/Items/Containers/TreasureMapChest.cs +++ b/Projects/UOContent/Items/Containers/TreasureMapChest.cs @@ -9,11 +9,9 @@ using Server.Network; namespace Server.Items; -[SerializationGenerator(4, false)] +[SerializationGenerator(2, false)] public partial class TreasureMapChest : LockableContainer { - [Tidy] - [CanBeNull] [SerializableField(0, setter: "private")] private List _guardians; @@ -29,11 +27,12 @@ public partial class TreasureMapChest : LockableContainer [SerializedCommandProperty(AccessLevel.GameMaster)] private int _level; + [TimerDrift] [SerializableField(4)] [SerializedCommandProperty(AccessLevel.GameMaster)] - [DeserializeTimer(nameof(DeserializeExpireTimer))] private Timer _expireTimer; + [DeserializeTimerField(4)] private void DeserializeExpireTimer(TimeSpan delay) { if (!_temporary) @@ -42,29 +41,11 @@ public partial class TreasureMapChest : LockableContainer } } - private void MigrateFrom(V3Content content) - { - _guardians = content.Guardians; - _temporary = content.Temporary; - _owner = content.Owner; - _level = content.Level; - _lifted = content.Lifted; - - if (content.ExpireTimerDelay != TimeSpan.MinValue) - { - DeserializeExpireTimer(content.ExpireTimerDelay); - } - } - [Tidy] - [CanBeNull] [SerializableField(5, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster)] private HashSet _lifted; - // False only while the constructor fills the chest; deserialized chests are always filled. - private bool _filled; - [Constructible] public TreasureMapChest(int level) : this(null, level) { @@ -76,16 +57,16 @@ public partial class TreasureMapChest : LockableContainer _level = level; _temporary = temporary; + _guardians = new List(); _expireTimer = Timer.DelayCall(TimeSpan.FromHours(3.0), Delete); Fill(this, level); - _filled = true; } public override int LabelNumber => 3000541; public static Type[] Artifacts { get; } = - [ + { typeof(CandelabraOfSouls), typeof(GoldBricks), typeof(PhillipsWoodenSteed), typeof(ArcticDeathDealer), typeof(BlazeOfDeath), typeof(BurglarsBandana), typeof(CavortingClub), typeof(DreadPirateHat), @@ -93,7 +74,7 @@ public partial class TreasureMapChest : LockableContainer typeof(LunaLance), typeof(NightsKiss), typeof(NoxRangersHeavyCrossbow), typeof(PolarBearMask), typeof(VioletCourage), typeof(HeartOfTheLion), typeof(ColdBlood), typeof(AlchemistsBauble) - ]; + }; [CommandProperty(AccessLevel.GameMaster)] public DateTime DeleteTime => _expireTimer.Next; @@ -200,6 +181,7 @@ public partial class TreasureMapChest : LockableContainer 2 => 76, 3 => 84, 4 => 92, + 5 => 100, _ => 100 }; @@ -317,17 +299,13 @@ public partial class TreasureMapChest : LockableContainer if (_level == 0 && from.AccessLevel < AccessLevel.GameMaster) { - if (_guardians != null) + foreach (var m in _guardians) { - for (var i = 0; i < _guardians.Count; i++) + if (m.Alive) { - var m = _guardians[i]; - if (m.Alive) - { - // You must first kill the guardians before you may open this chest. - from.SendLocalizedMessage(1046448); - return true; - } + // You must first kill the guardians before you may open this chest. + from.SendLocalizedMessage(1046448); + return true; } } @@ -381,23 +359,14 @@ public partial class TreasureMapChest : LockableContainer public override bool CheckLift(Mobile from, Item item, ref LRReason reject) => CheckLoot(from, true) && base.CheckLift(from, item, ref reject); - public override void OnItemAdded(Item item) - { - base.OnItemAdded(item); - - if (_filled) - { - _lifted ??= []; - _lifted.Add(item); - } - } - public override void OnItemLifted(Mobile from, Item item) { + var notYetLifted = _lifted?.Contains(item) != true; from.RevealingAction(); - if (_lifted?.Contains(item) != true) + + if (notYetLifted) { - _lifted ??= []; + _lifted ??= new HashSet(); _lifted.Add(item); if (Utility.RandomDouble() < 0.1) // 10% chance to spawn a new monster @@ -422,6 +391,8 @@ public partial class TreasureMapChest : LockableContainer private void Deserialize(IGenericReader reader, int version) { + _guardians = new List(); + _owner = reader.ReadEntity(); _level = reader.ReadInt(); var expireTimerNext = reader.ReadDeltaTime(); @@ -429,34 +400,12 @@ public partial class TreasureMapChest : LockableContainer _lifted = reader.ReadEntitySet(); } - private void MigrateFrom(V2Content content) - { - _guardians = content.Guardians; - if (_guardians.Count == 0) - { - _guardians = null; - } - _temporary = content.Temporary; - _owner = content.Owner; - _level = content.Level; - _lifted = content.Lifted; - if (_lifted.Count == 0) - { - _lifted = null; - } - - var expireTimerDelay = content.ExpireTimerDelay; - DeserializeExpireTimer(expireTimerDelay); - } - - [AfterDeserialization] + [AfterDeserialization(false)] private void AfterDeserialization() { - _filled = true; - if (_expireTimer == null) { - Timer.DelayCall(Delete); + Delete(); } } @@ -479,7 +428,12 @@ public partial class TreasureMapChest : LockableContainer public void BeginRemove(Mobile from) { - RemoveGump.DisplayTo(from, this); + if (!from.Alive) + { + return; + } + + from.SendGump(new RemoveGump(from, this)); } public void EndRemove(Mobile from) @@ -493,50 +447,42 @@ public partial class TreasureMapChest : LockableContainer Delete(); } - private class RemoveGump : StaticGump + private class RemoveGump : Gump { private readonly TreasureMapChest _chest; + private readonly Mobile _from; public override bool Singleton => true; - private RemoveGump(TreasureMapChest chest) : base(15, 15) => _chest = chest; - - public static void DisplayTo(Mobile from, TreasureMapChest chest) + public RemoveGump(Mobile from, TreasureMapChest chest) : base(15, 15) { - if (from?.NetState == null || !from.Alive || chest == null || chest.Deleted) - { - return; - } + _from = from; + _chest = chest; - from.SendGump(new RemoveGump(chest)); - } + Closable = false; + Disposable = false; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.SetNoClose(); - builder.SetNoDispose(); - - builder.AddBackground(30, 0, 240, 240, 2620); + AddBackground(30, 0, 240, 240, 2620); // When this treasure chest is removed, any items still inside of it will be lost. - builder.AddHtmlLocalized(45, 15, 200, 80, 1048125, 0x7FFF); + AddHtmlLocalized(45, 15, 200, 80, 1048125, 0x7FFF); // Are you certain you're ready to remove this chest? - builder.AddHtmlLocalized(45, 95, 200, 60, 1048126, 0x7FFF); + AddHtmlLocalized(45, 95, 200, 60, 1048126, 0x7FFF); - builder.AddButton(40, 153, 4005, 4007, 1); - builder.AddHtmlLocalized(75, 155, 180, 40, 1048127, 0x7FFF); // Remove the Treasure Chest + AddButton(40, 153, 4005, 4007, 1); + AddHtmlLocalized(75, 155, 180, 40, 1048127, 0x7FFF); // Remove the Treasure Chest - builder.AddButton(40, 195, 4005, 4007, 2); - builder.AddHtmlLocalized(75, 197, 180, 35, 1006045, 0x7FFF); // Cancel + AddButton(40, 195, 4005, 4007, 2); + AddHtmlLocalized(75, 197, 180, 35, 1006045, 0x7FFF); // Cancel } public override void OnResponse(NetState sender, in RelayInfo info) { if (info.ButtonID == 1) { - _chest.EndRemove(sender.Mobile); + _chest.EndRemove(_from); } } } diff --git a/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs b/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs index 9b3e32c81..366a256e7 100644 --- a/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs +++ b/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs @@ -5,11 +5,7 @@ namespace Server.Items; [SerializationGenerator(0)] public abstract partial class BaseDecorationArtifact : Item { - public BaseDecorationArtifact(int itemID) : base(itemID) - { - } - - public override double DefaultWeight => 10.0; + public BaseDecorationArtifact(int itemID) : base(itemID) => Weight = 10.0; public abstract int ArtifactRarity { get; } @@ -26,11 +22,7 @@ public abstract partial class BaseDecorationArtifact : Item [SerializationGenerator(0)] public abstract partial class BaseDecorationContainerArtifact : BaseContainer { - public BaseDecorationContainerArtifact(int itemID) : base(itemID) - { - } - - public override double DefaultWeight => 10.0; + public BaseDecorationContainerArtifact(int itemID) : base(itemID) => Weight = 10.0; public abstract int ArtifactRarity { get; } diff --git a/Projects/UOContent/Items/Decoration Artifacts/SEDecorationArtifacts.cs b/Projects/UOContent/Items/Decoration Artifacts/SEDecorationArtifacts.cs index 89db33b85..70908c34b 100644 --- a/Projects/UOContent/Items/Decoration Artifacts/SEDecorationArtifacts.cs +++ b/Projects/UOContent/Items/Decoration Artifacts/SEDecorationArtifacts.cs @@ -536,21 +536,22 @@ public partial class TowerLanternArtifact : BaseDecorationArtifact public override void OnDoubleClick(Mobile from) { - if (!from.InRange(GetWorldLocation(), 2)) + if (from.InRange(GetWorldLocation(), 2)) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } - - if (IsOn) - { - IsOn = false; - from.PlaySound(0x3BE); + if (IsOn) + { + IsOn = false; + from.PlaySound(0x3BE); + } + else + { + IsOn = true; + from.PlaySound(0x47); + } } else { - IsOn = true; - from.PlaySound(0x47); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } } diff --git a/Projects/UOContent/Items/Deeds/BarkeepContract.cs b/Projects/UOContent/Items/Deeds/BarkeepContract.cs index b5bd7b4ab..2ac3b0499 100644 --- a/Projects/UOContent/Items/Deeds/BarkeepContract.cs +++ b/Projects/UOContent/Items/Deeds/BarkeepContract.cs @@ -10,11 +10,10 @@ public partial class BarkeepContract : Item [Constructible] public BarkeepContract() : base(0x14F0) { + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override string DefaultName => "a barkeep contract"; public override void OnDoubleClick(Mobile from) @@ -27,11 +26,9 @@ public partial class BarkeepContract : Item { from.SendLocalizedMessage(503248); // Your godly powers allow you to place this vendor whereever you wish. - var v = new PlayerBarkeeper(from, BaseHouse.FindHouseAt(from)) - { - Direction = from.Direction & Direction.Mask - }; + Mobile v = new PlayerBarkeeper(from, BaseHouse.FindHouseAt(from)); + v.Direction = from.Direction & Direction.Mask; v.MoveToWorld(from.Location, from.Map); Delete(); @@ -71,11 +68,9 @@ public partial class BarkeepContract : Item } else { - var v = new PlayerBarkeeper(from, house) - { - Direction = from.Direction & Direction.Mask - }; + Mobile v = new PlayerBarkeeper(from, house); + v.Direction = from.Direction & Direction.Mask; v.MoveToWorld(from.Location, from.Map); Delete(); diff --git a/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs b/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs index 25860f542..816a0cc0c 100644 --- a/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs +++ b/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs @@ -1,5 +1,4 @@ using ModernUO.Serialization; -using Server.Engines.Insurance; using Server.Targeting; namespace Server.Items; @@ -26,7 +25,7 @@ public class ClothingBlessTarget : Target // Create our targeting class (which w } // Check if its already newbied (blessed) - if (item.LootType == LootType.Blessed || item.BlessedFor == from || Insurance.Enabled && item.Insured) + if (item.LootType == LootType.Blessed || item.BlessedFor == from || Mobile.InsuranceEnabled && item.Insured) { from.SendLocalizedMessage(1045113); // That item is already blessed } @@ -59,11 +58,10 @@ public partial class ClothingBlessDeed : Item // Create the item class which is [Constructible] public ClothingBlessDeed() : base(0x14F0) { + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override string DefaultName => "a clothing bless deed"; public override bool DisplayLootType => false; diff --git a/Projects/UOContent/Items/Deeds/CommodityDeed.cs b/Projects/UOContent/Items/Deeds/CommodityDeed.cs index 163ea68cf..d073bf90b 100644 --- a/Projects/UOContent/Items/Deeds/CommodityDeed.cs +++ b/Projects/UOContent/Items/Deeds/CommodityDeed.cs @@ -19,6 +19,7 @@ public partial class CommodityDeed : Item [Constructible] public CommodityDeed(Item commodity = null) : base(0x14F0) { + Weight = 1.0; Hue = 0x47; Commodity = commodity; @@ -26,8 +27,6 @@ public partial class CommodityDeed : Item LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => Commodity == null ? 1047016 : 1047017; public bool SetCommodity(Item item) diff --git a/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs b/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs index 5ed370153..a7ac745dc 100644 --- a/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs +++ b/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs @@ -20,22 +20,21 @@ public partial class DragonBardingDeed : Item, ICraftable [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _exceptional; - public DragonBardingDeed() : base(0x14F0) - { - } - - public override double DefaultWeight => 1.0; + public DragonBardingDeed() : base(0x14F0) => Weight = 1.0; public override int LabelNumber => _exceptional ? 1053181 : 1053012; // dragon barding deed - [SerializableField(2, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(2)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(newValue); + get => _resource; + set + { + _resource = value; + Hue = CraftResources.GetHue(value); + InvalidateProperties(); + } } public int OnCraft( diff --git a/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs b/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs index 8c8559c04..d9256bf8a 100644 --- a/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs +++ b/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs @@ -11,11 +11,10 @@ public partial class HairRestylingDeed : Item [Constructible] public HairRestylingDeed() : base(0x14F0) { + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1041061; // a coupon for a free hair restyling public override void OnDoubleClick(Mobile from) @@ -26,121 +25,108 @@ public partial class HairRestylingDeed : Item } else { - HairRestylingDeedGump.DisplayTo(from, this); + from.SendGump(new InternalGump(from, this)); } } - private class HairRestylingDeedGump : DynamicGump + private class InternalGump : Gump { private static readonly int[][] ElvenArray = - [ - [0], - [1011064, 1011064, 0, 0, 0, 0], // bald - [1074386, 1074386, 0x2fc0, 0x2fc0, 0xedf5, 0xc6e5], // long feather - [1074387, 1074387, 0x2fc1, 0x2fc1, 0xedf6, 0xc6e6], // short - [1074388, 1074388, 0x2fc2, 0x2fc2, 0xedf7, 0xc6e7], // mullet - [1074391, 1074391, 0x2fce, 0x2fce, 0xeddc, 0xc6cc], // knob - [1074392, 1074392, 0x2fcf, 0x2fcf, 0xeddd, 0xc6cd], // braided - [1074394, 1074394, 0x2fd1, 0x2fd1, 0xeddf, 0xc6cf], // spiked - [1074389, 1074385, 0x2fcc, 0x2fbf, 0xedda, 0xc6e4], // flower, mid-long - [1074393, 1074390, 0x2fd0, 0x2fcd, 0xedde, 0xc6cb] // buns, long - ]; + { + new[] { 0 }, + new[] { 1011064, 1011064, 0, 0, 0, 0 }, // bald + new[] { 1074386, 1074386, 0x2fc0, 0x2fc0, 0xedf5, 0xc6e5 }, // long feather + new[] { 1074387, 1074387, 0x2fc1, 0x2fc1, 0xedf6, 0xc6e6 }, // short + new[] { 1074388, 1074388, 0x2fc2, 0x2fc2, 0xedf7, 0xc6e7 }, // mullet + new[] { 1074391, 1074391, 0x2fce, 0x2fce, 0xeddc, 0xc6cc }, // knob + new[] { 1074392, 1074392, 0x2fcf, 0x2fcf, 0xeddd, 0xc6cd }, // braided + new[] { 1074394, 1074394, 0x2fd1, 0x2fd1, 0xeddf, 0xc6cf }, // spiked + new[] { 1074389, 1074385, 0x2fcc, 0x2fbf, 0xedda, 0xc6e4 }, // flower, mid-long + new[] { 1074393, 1074390, 0x2fd0, 0x2fcd, 0xedde, 0xc6cb } // buns, long + }; /* racial arrays are: cliloc_F, cliloc_M, ItemID_F, ItemID_M, gump_img_F, gump_img_M */ private static readonly int[][] HumanArray = /* why on earth cant these utilities be consistent with hex/dec */ - [ - [0], - [1011064, 1011064, 0, 0, 0, 0], // bald - [1011052, 1011052, 0x203B, 0x203B, 0xed1c, 0xC60C], // Short - [1011053, 1011053, 0x203C, 0x203C, 0xed1d, 0xc60d], // Long - [1011054, 1011054, 0x203D, 0x203D, 0xed1e, 0xc60e], // Ponytail - [1011055, 1011055, 0x2044, 0x2044, 0xed27, 0xC60F], // Mohawk - [1011047, 1011047, 0x2045, 0x2045, 0xED26, 0xED26], // Pageboy - [1074393, 1011048, 0x2046, 0x2048, 0xed28, 0xEDE5], // Buns, Receding - [1011049, 1011049, 0x2049, 0x2049, 0xede6, 0xede6], // 2-tails - [1011050, 1011050, 0x204A, 0x204A, 0xED29, 0xED29], // Topknot - [1011396, 1011396, 0x2047, 0x2047, 0xed25, 0xc618] // Curly - ]; + { + new[] { 0 }, + new[] { 1011064, 1011064, 0, 0, 0, 0 }, // bald + new[] { 1011052, 1011052, 0x203B, 0x203B, 0xed1c, 0xC60C }, // Short + new[] { 1011053, 1011053, 0x203C, 0x203C, 0xed1d, 0xc60d }, // Long + new[] { 1011054, 1011054, 0x203D, 0x203D, 0xed1e, 0xc60e }, // Ponytail + new[] { 1011055, 1011055, 0x2044, 0x2044, 0xed27, 0xC60F }, // Mohawk + new[] { 1011047, 1011047, 0x2045, 0x2045, 0xED26, 0xED26 }, // Pageboy + new[] { 1074393, 1011048, 0x2046, 0x2048, 0xed28, 0xEDE5 }, // Buns, Receding + new[] { 1011049, 1011049, 0x2049, 0x2049, 0xede6, 0xede6 }, // 2-tails + new[] { 1011050, 1011050, 0x204A, 0x204A, 0xED29, 0xED29 }, // Topknot + new[] { 1011396, 1011396, 0x2047, 0x2047, 0xed25, 0xc618 } // Curly + }; /* gump data: bgX, bgY, htmlX, htmlY, imgX, imgY, butX, butY */ private static readonly int[][] LayoutArray = - [ - [0], /* padding: its more efficient than code to ++ the index/buttonid */ - [425, 280, 342, 295, 000, 000, 310, 292], - [235, 060, 150, 075, 168, 020, 118, 073], - [235, 115, 150, 130, 168, 070, 118, 128], - [235, 170, 150, 185, 168, 130, 118, 183], - [235, 225, 150, 240, 168, 185, 118, 238], - [425, 060, 342, 075, 358, 018, 310, 073], - [425, 115, 342, 130, 358, 075, 310, 128], - [425, 170, 342, 185, 358, 125, 310, 183], - [425, 225, 342, 240, 358, 185, 310, 238], - [235, 280, 150, 295, 168, 245, 118, 292] // slot 10, Curly - N/A for elfs. - ]; + { + new[] { 0 }, /* padding: its more efficient than code to ++ the index/buttonid */ + new[] { 425, 280, 342, 295, 000, 000, 310, 292 }, + new[] { 235, 060, 150, 075, 168, 020, 118, 073 }, + new[] { 235, 115, 150, 130, 168, 070, 118, 128 }, + new[] { 235, 170, 150, 185, 168, 130, 118, 183 }, + new[] { 235, 225, 150, 240, 168, 185, 118, 238 }, + new[] { 425, 060, 342, 075, 358, 018, 310, 073 }, + new[] { 425, 115, 342, 130, 358, 075, 310, 128 }, + new[] { 425, 170, 342, 185, 358, 125, 310, 183 }, + new[] { 425, 225, 342, 240, 358, 185, 310, 238 }, + new[] { 235, 280, 150, 295, 168, 245, 118, 292 } // slot 10, Curly - N/A for elfs. + }; - private readonly HairRestylingDeed _deed; - private readonly Mobile _from; + private readonly HairRestylingDeed m_Deed; + private readonly Mobile m_From; public override bool Singleton => true; - private HairRestylingDeedGump(Mobile from, HairRestylingDeed deed) : base(50, 50) + public InternalGump(Mobile from, HairRestylingDeed deed) : base(50, 50) { - _from = from; - _deed = deed; - } + m_From = from; + m_Deed = deed; - public static void DisplayTo(Mobile from, HairRestylingDeed deed) - { - if (from?.NetState == null || deed?.Deleted != false) + AddBackground(100, 10, 400, 385, 0xA28); + + AddHtmlLocalized(100, 25, 400, 35, 1013008); + AddButton(175, 340, 0xFA5, 0xFA7, 0x0); // CANCEL + + AddHtmlLocalized(210, 342, 90, 35, 1011012); //
HAIRSTYLE SELECTION MENU
+ + var RacialData = from.Race == Race.Human ? HumanArray : ElvenArray; + + for (var i = 1; i < RacialData.Length; i++) { - return; - } - - from.SendGump(new HairRestylingDeedGump(from, deed)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddBackground(100, 10, 400, 385, 0xA28); - - builder.AddHtmlLocalized(100, 25, 400, 35, 1013008); - builder.AddButton(175, 340, 0xFA5, 0xFA7, 0x0); // CANCEL - - builder.AddHtmlLocalized(210, 342, 90, 35, 1011012); //
HAIRSTYLE SELECTION MENU
- - var racialData = _from.Race == Race.Human ? HumanArray : ElvenArray; - - for (var i = 1; i < racialData.Length; i++) - { - builder.AddHtmlLocalized( + AddHtmlLocalized( LayoutArray[i][2], LayoutArray[i][3], i == 1 ? 125 : 80, i == 1 ? 70 : 35, - _from.Female ? racialData[i][0] : racialData[i][1] + m_From.Female ? RacialData[i][0] : RacialData[i][1] ); if (LayoutArray[i][4] != 0) { - builder.AddBackground(LayoutArray[i][0], LayoutArray[i][1], 50, 50, 0xA3C); - builder.AddImage(LayoutArray[i][4], LayoutArray[i][5], _from.Female ? racialData[i][4] : racialData[i][5]); + AddBackground(LayoutArray[i][0], LayoutArray[i][1], 50, 50, 0xA3C); + AddImage(LayoutArray[i][4], LayoutArray[i][5], m_From.Female ? RacialData[i][4] : RacialData[i][5]); } - builder.AddButton(LayoutArray[i][6], LayoutArray[i][7], 0xFA5, 0xFA7, i); + AddButton(LayoutArray[i][6], LayoutArray[i][7], 0xFA5, 0xFA7, i); } } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_from?.Alive != true) + if (m_From?.Alive != true) { return; } - if (_deed.Deleted) + if (m_Deed.Deleted) { return; } @@ -150,13 +136,13 @@ public partial class HairRestylingDeed : Item return; } - var racialData = _from.Race == Race.Human ? HumanArray : ElvenArray; + var RacialData = m_From.Race == Race.Human ? HumanArray : ElvenArray; - if (_from is PlayerMobile pm) + if (m_From is PlayerMobile pm) { pm.SetHairMods(-1, -1); // clear any hairmods (disguise kit, incognito) - pm.HairItemID = pm.Female ? racialData[info.ButtonID][2] : racialData[info.ButtonID][3]; - _deed.Delete(); + pm.HairItemID = pm.Female ? RacialData[info.ButtonID][2] : RacialData[info.ButtonID][3]; + m_Deed.Delete(); } } } diff --git a/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs b/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs index 4749ad7f2..dcb8585e1 100644 --- a/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs +++ b/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs @@ -13,11 +13,10 @@ public partial class HolidayTreeDeed : Item public HolidayTreeDeed() : base(0x14F0) { Hue = 0x488; + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1041116; // a deed for a holiday tree public bool ValidatePlacement(Mobile from, Point3D loc) @@ -104,61 +103,51 @@ public partial class HolidayTreeDeed : Item public override void OnDoubleClick(Mobile from) { - HolidayTreeChoiceGump.DisplayTo(from, this); + from.SendGump(new HolidayTreeChoiceGump(from, this)); } } -public class HolidayTreeChoiceGump : StaticGump +public class HolidayTreeChoiceGump : Gump { - private readonly HolidayTreeDeed _deed; + private readonly HolidayTreeDeed m_Deed; + private readonly Mobile m_From; public override bool Singleton => true; - private HolidayTreeChoiceGump(HolidayTreeDeed deed) : base(200, 200) => _deed = deed; - - public static void DisplayTo(Mobile from, HolidayTreeDeed deed) + public HolidayTreeChoiceGump(Mobile from, HolidayTreeDeed deed) : base(200, 200) { - if (from?.NetState == null || deed?.Deleted != false) - { - return; - } + m_From = from; + m_Deed = deed; - from.SendGump(new HolidayTreeChoiceGump(deed)); - } + AddPage(0); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddBackground(0, 0, 220, 120, 5054); + AddBackground(10, 10, 200, 100, 3000); - builder.AddBackground(0, 0, 220, 120, 5054); - builder.AddBackground(10, 10, 200, 100, 3000); + AddButton(20, 35, 4005, 4007, 1); + AddHtmlLocalized(55, 35, 145, 25, 1018322); // Classic - builder.AddButton(20, 35, 4005, 4007, 1); - builder.AddHtmlLocalized(55, 35, 145, 25, 1018322); // Classic - - builder.AddButton(20, 65, 4005, 4007, 2); - builder.AddHtmlLocalized(55, 65, 145, 25, 1018321); // Modern + AddButton(20, 65, 4005, 4007, 2); + AddHtmlLocalized(55, 65, 145, 25, 1018321); // Modern } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_deed.Deleted) + if (m_Deed.Deleted) { return; } - var from = sender.Mobile; - switch (info.ButtonID) { case 1: { - _deed.BeginPlace(from, HolidayTreeType.Classic); + m_Deed.BeginPlace(m_From, HolidayTreeType.Classic); break; } case 2: { - _deed.BeginPlace(from, HolidayTreeType.Modern); + m_Deed.BeginPlace(m_From, HolidayTreeType.Modern); break; } } diff --git a/Projects/UOContent/Items/Deeds/IDirectionAddonDeed.cs b/Projects/UOContent/Items/Deeds/IDirectionAddonDeed.cs deleted file mode 100644 index bdb438771..000000000 --- a/Projects/UOContent/Items/Deeds/IDirectionAddonDeed.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Server.Items; - -public interface IDirectionAddonDeed : IEntity -{ - public bool East { get; set; } - - public void SendTarget(Mobile m); -} diff --git a/Projects/UOContent/Items/Deeds/NameChangeDeed.cs b/Projects/UOContent/Items/Deeds/NameChangeDeed.cs index 6a4c22514..b21320604 100644 --- a/Projects/UOContent/Items/Deeds/NameChangeDeed.cs +++ b/Projects/UOContent/Items/Deeds/NameChangeDeed.cs @@ -1,4 +1,3 @@ -using System; using ModernUO.Serialization; using Server.Gumps; using Server.Misc; @@ -18,7 +17,7 @@ public partial class NameChangeDeed : Item { if (RootParent == from) { - NameChangeDeedGump.DisplayTo(from, this); + from.SendGump(new NameChangeDeedGump(this)); } else { @@ -27,76 +26,70 @@ public partial class NameChangeDeed : Item } } -public class NameChangeDeedGump : StaticGump +public class NameChangeDeedGump : Gump { - private readonly Item _sender; + private readonly Item m_Sender; public override bool Singleton => true; - private NameChangeDeedGump(Item sender) : base(50, 50) => _sender = sender; - - public static void DisplayTo(Mobile from, Item sender) + public NameChangeDeedGump(Item sender) : base(50, 50) { - if (from?.NetState == null || sender?.Deleted != false) - { - return; - } + m_Sender = sender; - from.SendGump(new NameChangeDeedGump(sender)); + Closable = true; + Draggable = true; + Resizable = false; + + AddPage(0); + + AddBlackAlpha(10, 120, 250, 85); + AddHtml(10, 125, 250, 20, "Name Change Deed".Center(0xFFFFFF)); + + AddLabel(73, 15, 1152, ""); + AddLabel(20, 150, 0x480, "New Name:"); + AddTextField(100, 150, 150, 20, 0); + + AddButtonLabeled(75, 180, 1, "Submit"); } - protected override void BuildLayout(ref StaticGumpBuilder builder) + public void AddBlackAlpha(int x, int y, int width, int height) { - builder.AddPage(); - - AddBlackAlpha(ref builder, 10, 120, 250, 85); - builder.AddHtml(10, 125, 250, 20, "Name Change Deed".Center(0xFFFFFF)); - - builder.AddLabel(73, 15, 1152, ""); - builder.AddLabel(20, 150, 0x480, "New Name:"); - AddTextField(ref builder, 100, 150, 150, 20, 0); - - AddButtonLabeled(ref builder, 75, 180, 1, "Submit"); + AddImageTiled(x, y, width, height, 2624); + AddAlphaRegion(x, y, width, height); } - private static void AddBlackAlpha(ref StaticGumpBuilder builder, int x, int y, int width, int height) + public void AddTextField(int x, int y, int width, int height, int index) { - builder.AddImageTiled(x, y, width, height, 2624); - builder.AddAlphaRegion(x, y, width, height); + AddBackground(x - 2, y - 2, width + 4, height + 4, 0x2486); + AddTextEntry(x + 2, y + 2, width - 4, height - 4, 0, index, ""); } - private static void AddTextField(ref StaticGumpBuilder builder, int x, int y, int width, int height, int index) + public void AddButtonLabeled(int x, int y, int buttonID, string text) { - builder.AddBackground(x - 2, y - 2, width + 4, height + 4, 0x2486); - builder.AddTextEntry(x + 2, y + 2, width - 4, height - 4, 0, index, ""); - } - - private static void AddButtonLabeled(ref StaticGumpBuilder builder, int x, int y, int buttonID, string text) - { - builder.AddButton(x, y - 1, 4005, 4007, buttonID); - builder.AddHtml(x + 35, y, 240, 20, text.Color(0xFFFFFF)); + AddButton(x, y - 1, 4005, 4007, buttonID); + AddHtml(x + 35, y, 240, 20, text.Color(0xFFFFFF)); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_sender?.Deleted != false || info.ButtonID != 1 || _sender.RootParent != sender.Mobile) + if (m_Sender?.Deleted != false || info.ButtonID != 1 || m_Sender.RootParent != sender.Mobile) { return; } var m = sender.Mobile; - var newName = info.GetTextEntry(0).AsSpan().Trim(); + var newName = info.GetTextEntry(0)?.Trim(); - if (!NameVerification.ValidatePlayerName(newName)) + if (!NameVerification.Validate(newName, 2, 16, true, false, true, 1, NameVerification.SpaceDashPeriodQuote)) { m.SendMessage("That name is unacceptable."); return; } - m.RawName = newName.ToString(); + m.RawName = newName; m.SendMessage("Your name has been changed!"); m.SendMessage($"You are now known as {newName}"); - _sender.Delete(); + m_Sender.Delete(); } } diff --git a/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs b/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs index 3893cbe2c..ed988dd30 100644 --- a/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs +++ b/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs @@ -15,11 +15,10 @@ public partial class NewPlayerTicket : Item [Constructible] public NewPlayerTicket() : base(0x14EF) { + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1062094; // a young player ticket public override bool DisplayLootType => false; @@ -71,8 +70,8 @@ public partial class NewPlayerTicket : Item } else { - NewPlayerTicketGump.DisplayTo(from, m_Ticket); - NewPlayerTicketGump.DisplayTo(them, theirTicket); + from.SendGump(new InternalGump(from, m_Ticket)); + them.SendGump(new InternalGump(them, theirTicket)); } } else if ((targeted as Item)?.ItemID == 0x14F0) @@ -86,56 +85,46 @@ public partial class NewPlayerTicket : Item } } - private class NewPlayerTicketGump : StaticGump + private class InternalGump : Gump { - private readonly NewPlayerTicket _ticket; + private readonly Mobile m_From; + private readonly NewPlayerTicket m_Ticket; - public override bool Singleton => true; - - private NewPlayerTicketGump(NewPlayerTicket ticket) : base(50, 50) => _ticket = ticket; - - public static void DisplayTo(Mobile from, NewPlayerTicket ticket) + public InternalGump(Mobile from, NewPlayerTicket ticket) : base(50, 50) { - if (from?.NetState == null || ticket?.Deleted != false) - { - return; - } + m_From = from; + m_Ticket = ticket; - from.SendGump(new NewPlayerTicketGump(ticket)); - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddBackground(0, 0, 400, 385, 0xA28); + AddBackground(0, 0, 400, 385, 0xA28); // Choose the gift you prefer. WARNING: if you cancel, and your partner does not, you will need to find another matching ticket! - builder.AddHtmlLocalized(30, 45, 340, 70, 1013011, true, true); + AddHtmlLocalized(30, 45, 340, 70, 1013011, true, true); - builder.AddButton(46, 128, 0xFA5, 0xFA7, 1); - builder.AddHtmlLocalized(80, 130, 320, 35, 1013012); // A sextant + AddButton(46, 128, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(80, 130, 320, 35, 1013012); // A sextant - builder.AddButton(46, 163, 0xFA5, 0xFA7, 2); - builder.AddHtmlLocalized(80, 165, 320, 35, 1013013); // A coupon for a single hair restyling + AddButton(46, 163, 0xFA5, 0xFA7, 2); + AddHtmlLocalized(80, 165, 320, 35, 1013013); // A coupon for a single hair restyling - builder.AddButton(46, 198, 0xFA5, 0xFA7, 3); - builder.AddHtmlLocalized(80, 200, 320, 35, 1013014); // A spellbook with all 1st - 4th spells. + AddButton(46, 198, 0xFA5, 0xFA7, 3); + AddHtmlLocalized(80, 200, 320, 35, 1013014); // A spellbook with all 1st - 4th spells. - builder.AddButton(46, 233, 0xFA5, 0xFA7, 4); - builder.AddHtmlLocalized(80, 235, 320, 35, 1013015); // A wand of fireworks + AddButton(46, 233, 0xFA5, 0xFA7, 4); + AddHtmlLocalized(80, 235, 320, 35, 1013015); // A wand of fireworks - builder.AddButton(46, 268, 0xFA5, 0xFA7, 5); - builder.AddHtmlLocalized(80, 270, 320, 35, 1013016); // A spyglass + AddButton(46, 268, 0xFA5, 0xFA7, 5); + AddHtmlLocalized(80, 270, 320, 35, 1013016); // A spyglass - builder.AddButton(46, 303, 0xFA5, 0xFA7, 6); - builder.AddHtmlLocalized(80, 305, 320, 35, 1013017); // Dyes and a dye tub + AddButton(46, 303, 0xFA5, 0xFA7, 6); + AddHtmlLocalized(80, 305, 320, 35, 1013017); // Dyes and a dye tub - builder.AddButton(120, 340, 0xFA5, 0xFA7, 0); - builder.AddHtmlLocalized(154, 342, 100, 35, 1011012); // CANCEL + AddButton(120, 340, 0xFA5, 0xFA7, 0); + AddHtmlLocalized(154, 342, 100, 35, 1011012); // CANCEL } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_ticket.Deleted) + if (m_Ticket.Deleted) { return; } @@ -188,15 +177,14 @@ public partial class NewPlayerTicket : Item if (item != null) { - var from = sender.Mobile; - _ticket.Delete(); + m_Ticket.Delete(); - from.SendLocalizedMessage(number); - from.AddToBackpack(item); + m_From.SendLocalizedMessage(number); + m_From.AddToBackpack(item); if (item2 != null) { - from.AddToBackpack(item2); + m_From.AddToBackpack(item2); } } } diff --git a/Projects/UOContent/Items/Deeds/VendorRentalContract.cs b/Projects/UOContent/Items/Deeds/VendorRentalContract.cs index 3863cfe88..f2f7898c8 100644 --- a/Projects/UOContent/Items/Deeds/VendorRentalContract.cs +++ b/Projects/UOContent/Items/Deeds/VendorRentalContract.cs @@ -31,13 +31,13 @@ public partial class VendorRentalContract : Item [Constructible] public VendorRentalContract() : base(0x14F0) { + Weight = 1.0; Hue = 0x672; + _duration = VendorRentalDuration.Instances[0]; Price = 1500; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1062332; // a vendor rental contract public VendorRentalDuration Duration @@ -195,7 +195,7 @@ public partial class VendorRentalContract : Item { if (from.InRange(this, 5)) { - VendorRentalContractGump.DisplayTo(from, this); + from.SendGump(new VendorRentalContractGump(this, from), true); } else { @@ -231,7 +231,7 @@ public partial class VendorRentalContract : Item { if (target is VendorRentalContract contract && contract.IsUsableBy(from, true, true, true, true)) { - VendorRentalContractGump.DisplayTo(from, contract); + from.SendGump(new VendorRentalContractGump(contract, from), true); } } } diff --git a/Projects/UOContent/Items/Farming/FarmableCabbage.cs b/Projects/UOContent/Items/Farming/FarmableCabbage.cs index 51ccff825..3b4e32d59 100644 --- a/Projects/UOContent/Items/Farming/FarmableCabbage.cs +++ b/Projects/UOContent/Items/Farming/FarmableCabbage.cs @@ -10,7 +10,7 @@ public partial class FarmableCabbage : FarmableCrop { } - public static int GetCropID() => 0x0C7B; + public static int GetCropID() => 3254; public override Item GetCropObject() => new Cabbage diff --git a/Projects/UOContent/Items/Food/Asian.cs b/Projects/UOContent/Items/Food/Asian.cs index 282f35c03..b80e5f2d9 100644 --- a/Projects/UOContent/Items/Food/Asian.cs +++ b/Projects/UOContent/Items/Food/Asian.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class Wasabi : Food { [Constructible] - public Wasabi() : base(0x24E8) - { - } - - public override double DefaultWeight => 1.0; + public Wasabi() : base(0x24E8) => Weight = 1.0; } [SerializationGenerator(0, false)] @@ -20,21 +16,17 @@ public partial class WasabiClumps : Food public WasabiClumps() : base(0x24EB) { Stackable = false; + Weight = 1.0; FillFactor = 2; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class EmptyBentoBox : Item { [Constructible] - public EmptyBentoBox() : base(0x2834) - { - } + public EmptyBentoBox() : base(0x2834) => Weight = 5.0; - public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -44,10 +36,20 @@ public partial class BentoBox : Food public BentoBox() : base(0x2836) { Stackable = false; + Weight = 5.0; FillFactor = 2; } - public override double DefaultWeight => 5.0; + public override bool Eat(Mobile from) + { + if (!base.Eat(from)) + { + return false; + } + + from.AddToBackpack(new EmptyBentoBox()); + return true; + } } [SerializationGenerator(0, false)] @@ -57,10 +59,9 @@ public partial class SushiRolls : Food public SushiRolls() : base(0x283E) { Stackable = false; + Weight = 3.0; FillFactor = 2; } - - public override double DefaultWeight => 3.0; } [SerializationGenerator(0, false)] @@ -70,21 +71,16 @@ public partial class SushiPlatter : Food public SushiPlatter() : base(0x2840) { Stackable = Core.ML; + Weight = 3.0; FillFactor = 2; } - - public override double DefaultWeight => 3.0; } [SerializationGenerator(0, false)] public partial class GreenTeaBasket : Item { [Constructible] - public GreenTeaBasket() : base(0x284B) - { - } - - public override double DefaultWeight => 10.0; + public GreenTeaBasket() : base(0x284B) => Weight = 10.0; } [SerializationGenerator(0, false)] @@ -94,10 +90,9 @@ public partial class GreenTea : Food public GreenTea() : base(0x284C) { Stackable = false; + Weight = 4.0; FillFactor = 2; } - - public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -107,10 +102,9 @@ public partial class MisoSoup : Food public MisoSoup() : base(0x284D) { Stackable = false; + Weight = 4.0; FillFactor = 2; } - - public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -120,10 +114,9 @@ public partial class WhiteMisoSoup : Food public WhiteMisoSoup() : base(0x284E) { Stackable = false; + Weight = 4.0; FillFactor = 2; } - - public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -133,10 +126,9 @@ public partial class RedMisoSoup : Food public RedMisoSoup() : base(0x284F) { Stackable = false; + Weight = 4.0; FillFactor = 2; } - - public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -146,8 +138,7 @@ public partial class AwaseMisoSoup : Food public AwaseMisoSoup() : base(0x2850) { Stackable = false; + Weight = 4.0; FillFactor = 2; } - - public override double DefaultWeight => 4.0; } diff --git a/Projects/UOContent/Items/Food/Beverage.cs b/Projects/UOContent/Items/Food/Beverage.cs index 85eecf17f..dd994bfc9 100644 --- a/Projects/UOContent/Items/Food/Beverage.cs +++ b/Projects/UOContent/Items/Food/Beverage.cs @@ -38,11 +38,7 @@ public interface IWaterSource : IHasQuantity public partial class BeverageBottle : BaseBeverage { [Constructible] - public BeverageBottle(BeverageType type) : base(type) - { - } - - public override double DefaultWeight => 1.0; + public BeverageBottle(BeverageType type) : base(type) => Weight = 1.0; public override int BaseLabelNumber => 1042959; // a bottle of Ale public override int MaxQuantity => 5; @@ -72,11 +68,7 @@ public partial class BeverageBottle : BaseBeverage public partial class Jug : BaseBeverage { [Constructible] - public Jug(BeverageType type) : base(type) - { - } - - public override double DefaultWeight => 1.0; + public Jug(BeverageType type) : base(type) => Weight = 1.0; public override int BaseLabelNumber => 1042965; // a jug of Ale public override int MaxQuantity => 10; @@ -89,16 +81,10 @@ public partial class Jug : BaseBeverage public partial class CeramicMug : BaseBeverage { [Constructible] - public CeramicMug() - { - } + public CeramicMug() => Weight = 1.0; [Constructible] - public CeramicMug(BeverageType type) : base(type) - { - } - - public override double DefaultWeight => 1.0; + public CeramicMug(BeverageType type) : base(type) => Weight = 1.0; public override int BaseLabelNumber => 1042982; // a ceramic mug of Ale public override int MaxQuantity => 1; @@ -110,16 +96,10 @@ public partial class CeramicMug : BaseBeverage public partial class PewterMug : BaseBeverage { [Constructible] - public PewterMug() - { - } + public PewterMug() => Weight = 1.0; [Constructible] - public PewterMug(BeverageType type) : base(type) - { - } - - public override double DefaultWeight => 1.0; + public PewterMug(BeverageType type) : base(type) => Weight = 1.0; public override int BaseLabelNumber => 1042994; // a pewter mug with Ale public override int MaxQuantity => 1; @@ -131,16 +111,10 @@ public partial class PewterMug : BaseBeverage public partial class Goblet : BaseBeverage { [Constructible] - public Goblet() - { - } + public Goblet() => Weight = 1.0; [Constructible] - public Goblet(BeverageType type) : base(type) - { - } - - public override double DefaultWeight => 1.0; + public Goblet(BeverageType type) : base(type) => Weight = 1.0; public override int BaseLabelNumber => 1043000; // a goblet of Ale public override int MaxQuantity => 1; @@ -160,16 +134,10 @@ public partial class Goblet : BaseBeverage public partial class GlassMug : BaseBeverage { [Constructible] - public GlassMug() - { - } + public GlassMug() => Weight = 1.0; [Constructible] - public GlassMug(BeverageType type) : base(type) - { - } - - public override double DefaultWeight => 1.0; + public GlassMug(BeverageType type) : base(type) => Weight = 1.0; public override int EmptyLabelNumber => 1022456; // mug public override int BaseLabelNumber => 1042976; // a mug of Ale @@ -208,16 +176,10 @@ public partial class GlassMug : BaseBeverage public partial class Pitcher : BaseBeverage { [Constructible] - public Pitcher() - { - } + public Pitcher() => Weight = 2.0; [Constructible] - public Pitcher(BeverageType type) : base(type) - { - } - - public override double DefaultWeight => 2.0; + public Pitcher(BeverageType type) : base(type) => Weight = 2.0; public override int BaseLabelNumber => 1048128; // a Pitcher of Ale public override int MaxQuantity => 5; @@ -240,34 +202,6 @@ public partial class Pitcher : BaseBeverage _ => 0 }; } - - /// - /// Intercepts pour actions targeting a WaterElemental to implement the Endless Decanter - /// acquisition mechanic (throw a full pitcher at the elemental for a 10% chance to receive the Endless Decanter). - /// - /// Coupling note: This override introduces a direct dependency from the generic Pitcher - /// class to a specific creature type. This is unavoidable because it is the regular Pitcher - /// and not the EndlessDecanter that the player throws. If a future creature ever uses a - /// similar pour interaction, this pattern should be revisited (e.g. an interface or event). - /// - /// - public override void Pour_OnTarget(Mobile from, object targ) - { - if (targ is not WaterElemental elemental) - { - base.Pour_OnTarget(from, targ); - return; - } - - if (Content == BeverageType.Water) - { - EndlessDecanter.HandleThrow(this, elemental, from); - } - else - { - from.SendLocalizedMessage(500846); // Can't pour it there. - } - } } [SerializationGenerator(2, false)] @@ -324,21 +258,27 @@ public abstract partial class BaseBeverage : Item, IHasQuantity [CommandProperty(AccessLevel.GameMaster)] public bool IsFull => _quantity >= MaxQuantity; - [SerializableField(2, fieldChanged: nameof(OnContentChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private BeverageType _content; - - private void OnContentChanged(BeverageType oldValue, BeverageType newValue) + [SerializableProperty(2)] + [CommandProperty(AccessLevel.GameMaster)] + public BeverageType Content { - var itemID = ComputeItemID(); - if (itemID > 0) + get => _content; + set { - ItemID = itemID; - } - else - { - Delete(); + _content = value; + + InvalidateProperties(); + + var itemID = ComputeItemID(); + + if (itemID > 0) + { + ItemID = itemID; + } + else + { + Delete(); + } } } @@ -358,7 +298,6 @@ public abstract partial class BaseBeverage : Item, IHasQuantity if (itemID > 0) { ItemID = itemID; - OnQuantityChanged(); } else { @@ -367,21 +306,6 @@ public abstract partial class BaseBeverage : Item, IHasQuantity } } - /// - /// Called after is assigned a new value and the item ID has been updated, - /// but only when ComputeItemID returns a non-zero ID. If ComputeItemID - /// returns 0 (e.g. an empty Pitcher), the item is deleted and this method is - /// never called. - /// - /// Contract for subclasses: If you override this hook and need it to fire when the - /// beverage becomes empty, you must also override ComputeItemID" to return a - /// non-zero ID for the empty state (as EndlessDecanter" does). - /// - /// - protected virtual void OnQuantityChanged() - { - } - public abstract int ComputeItemID(); public virtual int GetQuantityDescription() @@ -437,17 +361,17 @@ public abstract partial class BaseBeverage : Item, IHasQuantity } } - if (from.Map == Map && from.InRange(GetWorldLocation(), 2) && from.InLOS(this)) + if (from.Map != Map || !from.InRange(GetWorldLocation(), 2) || !from.InLOS(this)) { - return true; + if (message) + { + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + } + + return false; } - if (message) - { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - } - - return false; + return true; } public virtual void Fill_OnTarget(Mobile from, object targ) diff --git a/Projects/UOContent/Items/Food/BeverageEmpty.cs b/Projects/UOContent/Items/Food/BeverageEmpty.cs index 6c10580f1..317635af0 100644 --- a/Projects/UOContent/Items/Food/BeverageEmpty.cs +++ b/Projects/UOContent/Items/Food/BeverageEmpty.cs @@ -7,20 +7,12 @@ namespace Server.Items; public partial class Glass : Item { [Constructible] - public Glass() : base(0x1f81) - { - } - - public override double DefaultWeight => 0.1; + public Glass() : base(0x1f81) => Weight = 0.1; } [SerializationGenerator(0, false)] public partial class GlassBottle : Item { [Constructible] - public GlassBottle() : base(0xe2b) - { - } - - public override double DefaultWeight => 0.3; + public GlassBottle() : base(0xe2b) => Weight = 0.3; } diff --git a/Projects/UOContent/Items/Food/Bowls.cs b/Projects/UOContent/Items/Food/Bowls.cs index 08b7bb11f..f080b6d62 100644 --- a/Projects/UOContent/Items/Food/Bowls.cs +++ b/Projects/UOContent/Items/Food/Bowls.cs @@ -6,22 +6,14 @@ namespace Server.Items; public partial class EmptyWoodenBowl : Item { [Constructible] - public EmptyWoodenBowl() : base(0x15F8) - { - } - - public override double DefaultWeight => 1.0; + public EmptyWoodenBowl() : base(0x15F8) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class EmptyPewterBowl : Item { [Constructible] - public EmptyPewterBowl() : base(0x15FD) - { - } - - public override double DefaultWeight => 1.0; + public EmptyPewterBowl() : base(0x15FD) => Weight = 1.0; } [SerializationGenerator(0, false)] @@ -31,11 +23,10 @@ public partial class WoodenBowlOfCarrots : Food public WoodenBowlOfCarrots() : base(0x15F9) { Stackable = false; + Weight = 1.0; FillFactor = 2; } - public override double DefaultWeight => 1.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -55,11 +46,10 @@ public partial class WoodenBowlOfCorn : Food public WoodenBowlOfCorn() : base(0x15FA) { Stackable = false; + Weight = 1.0; FillFactor = 2; } - public override double DefaultWeight => 1.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -79,11 +69,10 @@ public partial class WoodenBowlOfLettuce : Food public WoodenBowlOfLettuce() : base(0x15FB) { Stackable = false; + Weight = 1.0; FillFactor = 2; } - public override double DefaultWeight => 1.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -103,11 +92,10 @@ public partial class WoodenBowlOfPeas : Food public WoodenBowlOfPeas() : base(0x15FC) { Stackable = false; + Weight = 1.0; FillFactor = 2; } - public override double DefaultWeight => 1.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -127,11 +115,10 @@ public partial class PewterBowlOfCarrots : Food public PewterBowlOfCarrots() : base(0x15FE) { Stackable = false; + Weight = 1.0; FillFactor = 2; } - public override double DefaultWeight => 1.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -151,11 +138,10 @@ public partial class PewterBowlOfCorn : Food public PewterBowlOfCorn() : base(0x15FF) { Stackable = false; + Weight = 1.0; FillFactor = 2; } - public override double DefaultWeight => 1.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -175,11 +161,10 @@ public partial class PewterBowlOfLettuce : Food public PewterBowlOfLettuce() : base(0x1600) { Stackable = false; + Weight = 1.0; FillFactor = 2; } - public override double DefaultWeight => 1.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -199,11 +184,10 @@ public partial class PewterBowlOfPeas : Food public PewterBowlOfPeas() : base(0x1601) { Stackable = false; + Weight = 1.0; FillFactor = 2; } - public override double DefaultWeight => 1.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -223,11 +207,10 @@ public partial class PewterBowlOfPotatos : Food public PewterBowlOfPotatos() : base(0x1602) { Stackable = false; + Weight = 1.0; FillFactor = 2; } - public override double DefaultWeight => 1.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -245,11 +228,7 @@ public partial class PewterBowlOfPotatos : Food public partial class EmptyWoodenTub : Item { [Constructible] - public EmptyWoodenTub() : base(0x1605) - { - } - - public override double DefaultWeight => 2.0; + public EmptyWoodenTub() : base(0x1605) => Weight = 2.0; } [TypeAlias("Server.Items.EmptyLargePewterBowl")] @@ -257,11 +236,7 @@ public partial class EmptyWoodenTub : Item public partial class EmptyPewterTub : Item { [Constructible] - public EmptyPewterTub() : base(0x1603) - { - } - - public override double DefaultWeight => 2.0; + public EmptyPewterTub() : base(0x1603) => Weight = 2.0; } [SerializationGenerator(0, false)] @@ -271,11 +246,10 @@ public partial class WoodenBowlOfStew : Food public WoodenBowlOfStew() : base(0x1604) { Stackable = false; + Weight = 2.0; FillFactor = 2; } - public override double DefaultWeight => 2.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -295,11 +269,10 @@ public partial class WoodenBowlOfTomatoSoup : Food public WoodenBowlOfTomatoSoup() : base(0x1606) { Stackable = false; + Weight = 2.0; FillFactor = 2; } - public override double DefaultWeight => 2.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) diff --git a/Projects/UOContent/Items/Food/CookableFood.cs b/Projects/UOContent/Items/Food/CookableFood.cs index e9597fd03..a1515173c 100644 --- a/Projects/UOContent/Items/Food/CookableFood.cs +++ b/Projects/UOContent/Items/Food/CookableFood.cs @@ -76,12 +76,11 @@ public partial class RawRibs : CookableFood [Constructible] public RawRibs(int amount = 1) : base(0x9F1, 10) { + Weight = 1.0; Stackable = true; Amount = amount; } - public override double DefaultWeight => 1.0; - public override Food Cook() => new Ribs(); } @@ -104,11 +103,10 @@ public partial class RawChickenLeg : CookableFood [Constructible] public RawChickenLeg() : base(0x1607, 10) { + Weight = 1.0; Stackable = true; } - public override double DefaultWeight => 1.0; - public override Food Cook() => new ChickenLeg(); } @@ -118,12 +116,11 @@ public partial class RawBird : CookableFood [Constructible] public RawBird(int amount = 1) : base(0x9B9, 10) { + Weight = 1.0; Stackable = true; Amount = amount; } - public override double DefaultWeight => 1.0; - public override Food Cook() => new CookedBird(); } @@ -131,11 +128,7 @@ public partial class RawBird : CookableFood public partial class UnbakedPeachCobbler : CookableFood { [Constructible] - public UnbakedPeachCobbler() : base(0x1042, 25) - { - } - - public override double DefaultWeight => 1.0; + public UnbakedPeachCobbler() : base(0x1042, 25) => Weight = 1.0; public override int LabelNumber => 1041335; // unbaked peach cobbler @@ -146,11 +139,7 @@ public partial class UnbakedPeachCobbler : CookableFood public partial class UnbakedFruitPie : CookableFood { [Constructible] - public UnbakedFruitPie() : base(0x1042, 25) - { - } - - public override double DefaultWeight => 1.0; + public UnbakedFruitPie() : base(0x1042, 25) => Weight = 1.0; public override int LabelNumber => 1041334; // unbaked fruit pie @@ -161,11 +150,7 @@ public partial class UnbakedFruitPie : CookableFood public partial class UnbakedMeatPie : CookableFood { [Constructible] - public UnbakedMeatPie() : base(0x1042, 25) - { - } - - public override double DefaultWeight => 1.0; + public UnbakedMeatPie() : base(0x1042, 25) => Weight = 1.0; public override int LabelNumber => 1041338; // unbaked meat pie @@ -176,11 +161,7 @@ public partial class UnbakedMeatPie : CookableFood public partial class UnbakedPumpkinPie : CookableFood { [Constructible] - public UnbakedPumpkinPie() : base(0x1042, 25) - { - } - - public override double DefaultWeight => 1.0; + public UnbakedPumpkinPie() : base(0x1042, 25) => Weight = 1.0; public override int LabelNumber => 1041342; // unbaked pumpkin pie @@ -191,11 +172,7 @@ public partial class UnbakedPumpkinPie : CookableFood public partial class UnbakedApplePie : CookableFood { [Constructible] - public UnbakedApplePie() : base(0x1042, 25) - { - } - - public override double DefaultWeight => 1.0; + public UnbakedApplePie() : base(0x1042, 25) => Weight = 1.0; public override int LabelNumber => 1041336; // unbaked apple pie @@ -207,11 +184,7 @@ public partial class UnbakedApplePie : CookableFood public partial class UncookedCheesePizza : CookableFood { [Constructible] - public UncookedCheesePizza() : base(0x1083, 20) - { - } - - public override double DefaultWeight => 1.0; + public UncookedCheesePizza() : base(0x1083, 20) => Weight = 1.0; public override int LabelNumber => 1041341; // uncooked cheese pizza @@ -222,11 +195,7 @@ public partial class UncookedCheesePizza : CookableFood public partial class UncookedSausagePizza : CookableFood { [Constructible] - public UncookedSausagePizza() : base(0x1083, 20) - { - } - - public override double DefaultWeight => 1.0; + public UncookedSausagePizza() : base(0x1083, 20) => Weight = 1.0; public override int LabelNumber => 1041337; // uncooked sausage pizza @@ -237,11 +206,7 @@ public partial class UncookedSausagePizza : CookableFood public partial class UnbakedQuiche : CookableFood { [Constructible] - public UnbakedQuiche() : base(0x1042, 25) - { - } - - public override double DefaultWeight => 1.0; + public UnbakedQuiche() : base(0x1042, 25) => Weight = 1.0; public override int LabelNumber => 1041339; // unbaked quiche @@ -254,12 +219,11 @@ public partial class Eggs : CookableFood [Constructible] public Eggs(int amount = 1) : base(0x9B5, 15) { + Weight = 1.0; Stackable = true; Amount = amount; } - public override double DefaultWeight => 1.0; - public override Food Cook() => new FriedEggs(); } @@ -269,11 +233,12 @@ public partial class BrightlyColoredEggs : CookableFood [Constructible] public BrightlyColoredEggs() : base(0x9B5, 15) { + Weight = 0.5; Hue = 3 + Utility.Random(20) * 5; } - public override double DefaultWeight => 0.5; public override string DefaultName => "brightly colored eggs"; + public override Food Cook() => new FriedEggs(); } @@ -283,11 +248,12 @@ public partial class EasterEggs : CookableFood [Constructible] public EasterEggs() : base(0x9B5, 15) { + Weight = 0.5; Hue = 3 + Utility.Random(20) * 5; } - public override double DefaultWeight => 0.5; public override int LabelNumber => 1016105; // Easter Eggs + public override Food Cook() => new FriedEggs(); } @@ -295,11 +261,7 @@ public partial class EasterEggs : CookableFood public partial class CookieMix : CookableFood { [Constructible] - public CookieMix() : base(0x103F, 20) - { - } - - public override double DefaultWeight => 1.0; + public CookieMix() : base(0x103F, 20) => Weight = 1.0; public override Food Cook() => new Cookies(); } @@ -308,11 +270,7 @@ public partial class CookieMix : CookableFood public partial class CakeMix : CookableFood { [Constructible] - public CakeMix() : base(0x103F, 40) - { - } - - public override double DefaultWeight => 1.0; + public CakeMix() : base(0x103F, 40) => Weight = 1.0; public override int LabelNumber => 1041002; // cake mix diff --git a/Projects/UOContent/Items/Food/Cooking.cs b/Projects/UOContent/Items/Food/Cooking.cs index cd010432d..9701772dd 100644 --- a/Projects/UOContent/Items/Food/Cooking.cs +++ b/Projects/UOContent/Items/Food/Cooking.cs @@ -11,9 +11,8 @@ public partial class Dough : Item public Dough() : base(0x103d) { Stackable = Core.ML; + Weight = 1.0; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -23,10 +22,10 @@ public partial class SweetDough : Item public SweetDough() : base(0x103d) { Stackable = Core.ML; + Weight = 1.0; Hue = 150; } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1041340; // sweet dough } @@ -36,32 +35,23 @@ public partial class JarHoney : Item [Constructible] public JarHoney() : base(0x9ec) { + Weight = 1.0; Stackable = true; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class BowlFlour : Item { [Constructible] - public BowlFlour() : base(0xa1e) - { - } - - public override double DefaultWeight => 1.0; + public BowlFlour() : base(0xa1e) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class WoodenBowl : Item { [Constructible] - public WoodenBowl() : base(0x15f8) - { - } - - public override double DefaultWeight => 1.0; + public WoodenBowl() : base(0x15f8) => Weight = 1.0; } [TypeAlias("Server.Items.SackFlourOpen")] @@ -71,30 +61,29 @@ public partial class SackFlour : Item, IHasQuantity [Constructible] public SackFlour() : base(0x1039) { + Weight = 5.0; _quantity = 20; } - public override double DefaultWeight => 5.0; - - [SerializableField(0, fieldChanged: nameof(OnQuantityChanged), allowFieldChange: nameof(AllowQuantityChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _quantity; - - private bool AllowQuantityChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int Quantity { - value = Math.Min(20, Math.Max(0, value)); - return true; - } + get => _quantity; + set + { + _quantity = Math.Min(20, Math.Max(0, value)); - private void OnQuantityChanged(int oldValue, int newValue) - { - if (_quantity == 0) - { - Delete(); - } - else if (_quantity < 20 && ItemID is 0x1039 or 0x1045) - { - ++ItemID; + if (_quantity == 0) + { + Delete(); + } + else if (_quantity < 20 && ItemID is 0x1039 or 0x1045) + { + ++ItemID; + } + + this.MarkDirty(); } } @@ -111,11 +100,7 @@ public partial class SackFlour : Item, IHasQuantity public partial class Eggshells : Item { [Constructible] - public Eggshells() : base(0x9b4) - { - } - - public override double DefaultWeight => 0.5; + public Eggshells() : base(0x9b4) => Weight = 0.5; } [SerializationGenerator(0, false)] @@ -124,12 +109,11 @@ public partial class WheatSheaf : Item [Constructible] public WheatSheaf(int amount = 1) : base(7869) { + Weight = 1.0; Stackable = true; Amount = amount; } - public override double DefaultWeight => 1.0; - public override void OnDoubleClick(Mobile from) { if (Movable) diff --git a/Projects/UOContent/Items/Food/EndlessDecanter.cs b/Projects/UOContent/Items/Food/EndlessDecanter.cs deleted file mode 100644 index a7703991e..000000000 --- a/Projects/UOContent/Items/Food/EndlessDecanter.cs +++ /dev/null @@ -1,222 +0,0 @@ -using ModernUO.Serialization; -using Server.Collections; -using Server.ContextMenus; -using Server.Mobiles; -using Server.Targeting; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class EndlessDecanter : Pitcher -{ - [InvalidateProperties] - [SerializableField(0)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private bool _linked; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Point3D _linkLocation; - - [SerializableField(2)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Map _linkMap; - - [Constructible] - public EndlessDecanter() : base(BeverageType.Water) - { - Hue = 0x399; - LootType = LootType.Blessed; - } - - public override double DefaultWeight => 2.0; - - public override int LabelNumber => 1115929; // Endless Decanter of Water - - public override int ComputeItemID() => 0x0FF6; - - public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) - { - base.GetContextMenuEntries(from, ref list); - - if (!from.Alive) - { - return; - } - - list.Add(new LinkEntry(this)); - - if (_linked) - { - list.Add(new UnlinkEntry(this)); - } - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - - list.Add(1115889); // Auto Water Refill - - if (_linked) - { - list.Add(1115893); // Linked - } - else - { - list.Add(1115894); // Unlinked - } - } - - /// - /// Triggered by the setter after every quantity change. - /// When the decanter becomes empty and the owner is within 10 tiles of the linked trough, - /// this method sets Quantity = MaxQuantity, which re-enters the Quantity setter - /// and calls OnQuantityChanged a second time. The second call is a no-op because - /// Quantity == 0 is false. Recursion depth is bounded to 2. - /// - protected override void OnQuantityChanged() - { - if (_linked && Content == BeverageType.Water && Quantity == 0 && RootParent is Mobile owner) - { - if (owner.Map == _linkMap && owner.InRange(_linkLocation, 10)) - { - Quantity = MaxQuantity; - owner.SendLocalizedMessage(1115901); // The decanter has automatically been filled from the linked water trough. - owner.PlaySound(0x4E); - } - else - { - owner.SendLocalizedMessage(1115972); // The decanter's refill attempt failed because the linked water trough is not in the area. - } - } - } - - public static void HandleThrow(Pitcher pitcher, WaterElemental elemental, Mobile thrower) - { - if (!pitcher.IsFull) - { - thrower.SendLocalizedMessage(1113038); // It is not full. - return; - } - - if (!thrower.InRange(elemental.Location, 5)) - { - thrower.SendLocalizedMessage(500295); // You are too far away to do that. - return; - } - - if (!elemental.HasDecanter) - { - thrower.SendLocalizedMessage(1115895); // It seems that this water elemental no longer has a magical decanter... - return; - } - - thrower.RevealingAction(); - elemental.Damage(1, thrower); - - if (0.1 > Utility.RandomDouble()) - { - elemental.HasDecanter = false; - pitcher.Delete(); - thrower.AddToBackpack(new EndlessDecanter()); - thrower.SendLocalizedMessage(1115897); // The water elemental has thrown a magical decanter back to you! - } - else - { - pitcher.Delete(); - thrower.PlaySound(0x040); - thrower.SendLocalizedMessage(1115896); // The water pitcher has shattered. - } - } - - private class LinkEntry : ContextMenuEntry - { - private readonly EndlessDecanter _decanter; - - public LinkEntry(EndlessDecanter decanter) : base(1115891, 0) => _decanter = decanter; // Link - - public override void OnClick(Mobile from, IEntity target) - { - if (_decanter.Deleted || !_decanter.Movable || !from.CheckAlive() || !_decanter.CheckItemUse(from)) - { - return; - } - - from.SendLocalizedMessage(1115892); // Target a water trough you wish to link. - from.BeginTarget(10, false, TargetFlags.None, Link_OnTarget, _decanter); - } - } - - private static void Link_OnTarget(Mobile from, object targeted, object state) - { - var decanter = (EndlessDecanter)state; - - if (decanter?.Deleted != false || !decanter.Movable || !from.CheckAlive() || !decanter.CheckItemUse(from)) - { - return; - } - - int itemID; - Point3D location; - Map map; - - if (targeted is StaticTarget st) - { - itemID = st.ItemID; - location = st.Location; - map = from.Map; - } - else if (targeted is Item item) - { - itemID = item.ItemID; - location = item.Location; - map = item.Map; - } - else - { - from.SendLocalizedMessage(1115900); // Invalid target. Please target a water trough. - return; - } - - if (itemID is >= 0xB41 and <= 0xB44) - { - decanter.Linked = true; - decanter.LinkLocation = location; - decanter.LinkMap = map; - - from.SendLocalizedMessage(1115899); // That water trough has been linked to this decanter. - - // If the decanter is already empty when linked, trigger an immediate refill attempt. - // OnQuantityChanged is called directly rather than through the Quantity setter because - // the quantity hasn't changed — we just want to evaluate the refill condition now that - // a trough location is known. - if (decanter.IsEmpty && decanter.Content == BeverageType.Water) - { - decanter.OnQuantityChanged(); - } - } - else - { - from.SendLocalizedMessage(1115900); // Invalid target. Please target a water trough. - } - } - - private class UnlinkEntry : ContextMenuEntry - { - private readonly EndlessDecanter _decanter; - - public UnlinkEntry(EndlessDecanter decanter) : base(1115930, 0) => _decanter = decanter; // Unlink - - public override void OnClick(Mobile from, IEntity target) - { - if (_decanter.Deleted || !_decanter.Movable || !from.CheckAlive() || !_decanter.CheckItemUse(from)) - { - return; - } - - from.SendLocalizedMessage(1115898); // The link between this decanter and the water trough has been removed. - _decanter.Linked = false; - } - } -} diff --git a/Projects/UOContent/Items/Food/Food.cs b/Projects/UOContent/Items/Food/Food.cs index 9824adc3f..35c3b4e5b 100644 --- a/Projects/UOContent/Items/Food/Food.cs +++ b/Projects/UOContent/Items/Food/Food.cs @@ -133,10 +133,9 @@ public partial class BreadLoaf : Food [Constructible] public BreadLoaf(int amount = 1) : base(0x103B, amount) { + Weight = 1.0; FillFactor = 3; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -145,10 +144,9 @@ public partial class Bacon : Food [Constructible] public Bacon(int amount = 1) : base(0x979, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -157,10 +155,9 @@ public partial class SlabOfBacon : Food [Constructible] public SlabOfBacon(int amount = 1) : base(0x976, amount) { + Weight = 1.0; FillFactor = 3; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -205,10 +202,9 @@ public partial class FrenchBread : Food [Constructible] public FrenchBread(int amount = 1) : base(0x98C, amount) { + Weight = 2.0; FillFactor = 3; } - - public override double DefaultWeight => 2.0; } [SerializationGenerator(0, false)] @@ -217,10 +213,9 @@ public partial class FriedEggs : Food [Constructible] public FriedEggs(int amount = 1) : base(0x9B6, amount) { + Weight = 1.0; FillFactor = 4; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -229,10 +224,9 @@ public partial class CookedBird : Food [Constructible] public CookedBird(int amount = 1) : base(0x9B7, amount) { + Weight = 1.0; FillFactor = 5; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -241,10 +235,9 @@ public partial class RoastPig : Food [Constructible] public RoastPig(int amount = 1) : base(0x9BB, amount) { + Weight = 45.0; FillFactor = 20; } - - public override double DefaultWeight => 45.0; } [SerializationGenerator(0, false)] @@ -253,10 +246,9 @@ public partial class Sausage : Food [Constructible] public Sausage(int amount = 1) : base(0x9C0, amount) { + Weight = 1.0; FillFactor = 4; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -265,10 +257,9 @@ public partial class Ham : Food [Constructible] public Ham(int amount = 1) : base(0x9C9, amount) { + Weight = 1.0; FillFactor = 5; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -278,10 +269,9 @@ public partial class Cake : Food public Cake() : base(0x9E9) { Stackable = false; + Weight = 1.0; FillFactor = 10; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -290,10 +280,9 @@ public partial class Ribs : Food [Constructible] public Ribs(int amount = 1) : base(0x9F2, amount) { + Weight = 1.0; FillFactor = 5; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -303,10 +292,9 @@ public partial class Cookies : Food public Cookies() : base(0x160b) { Stackable = Core.ML; + Weight = 1.0; FillFactor = 4; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -316,10 +304,9 @@ public partial class Muffins : Food public Muffins() : base(0x9eb) { Stackable = false; + Weight = 1.0; FillFactor = 4; } - - public override double DefaultWeight => 1.0; } [TypeAlias("Server.Items.Pizza")] @@ -330,10 +317,10 @@ public partial class CheesePizza : Food public CheesePizza() : base(0x1040) { Stackable = false; + Weight = 1.0; FillFactor = 6; } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1044516; // cheese pizza } @@ -344,10 +331,10 @@ public partial class SausagePizza : Food public SausagePizza() : base(0x1040) { Stackable = false; + Weight = 1.0; FillFactor = 6; } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1044517; // sausage pizza } @@ -358,10 +345,10 @@ public partial class FruitPie : Food public FruitPie() : base(0x1041) { Stackable = false; + Weight = 1.0; FillFactor = 5; } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1041346; // baked fruit pie } @@ -372,10 +359,10 @@ public partial class MeatPie : Food public MeatPie() : base(0x1041) { Stackable = false; + Weight = 1.0; FillFactor = 5; } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1041347; // baked meat pie } @@ -386,10 +373,10 @@ public partial class PumpkinPie : Food public PumpkinPie() : base(0x1041) { Stackable = false; + Weight = 1.0; FillFactor = 5; } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1041348; // baked pumpkin pie } @@ -400,10 +387,10 @@ public partial class ApplePie : Food public ApplePie() : base(0x1041) { Stackable = false; + Weight = 1.0; FillFactor = 5; } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1041343; // baked apple pie } @@ -414,10 +401,10 @@ public partial class PeachCobbler : Food public PeachCobbler() : base(0x1041) { Stackable = false; + Weight = 1.0; FillFactor = 5; } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1041344; // baked peach cobbler } @@ -428,10 +415,10 @@ public partial class Quiche : Food public Quiche() : base(0x1041) { Stackable = Core.ML; + Weight = 1.0; FillFactor = 5; } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1041345; // baked quiche } @@ -441,10 +428,9 @@ public partial class LambLeg : Food [Constructible] public LambLeg(int amount = 1) : base(0x160a, amount) { + Weight = 2.0; FillFactor = 5; } - - public override double DefaultWeight => 2.0; } [SerializationGenerator(0, false)] @@ -453,10 +439,9 @@ public partial class ChickenLeg : Food [Constructible] public ChickenLeg(int amount = 1) : base(0x1608, amount) { + Weight = 1.0; FillFactor = 4; } - - public override double DefaultWeight => 1.0; } [Flippable(0xC74, 0xC75)] @@ -466,10 +451,9 @@ public partial class HoneydewMelon : Food [Constructible] public HoneydewMelon(int amount = 1) : base(0xC74, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [Flippable(0xC64, 0xC65)] @@ -479,10 +463,9 @@ public partial class YellowGourd : Food [Constructible] public YellowGourd(int amount = 1) : base(0xC64, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [Flippable(0xC66, 0xC67)] @@ -492,10 +475,9 @@ public partial class GreenGourd : Food [Constructible] public GreenGourd(int amount = 1) : base(0xC66, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [Flippable(0xC7F, 0xC81)] @@ -505,10 +487,9 @@ public partial class EarOfCorn : Food [Constructible] public EarOfCorn(int amount = 1) : base(0xC81, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -517,19 +498,14 @@ public partial class Turnip : Food [Constructible] public Turnip(int amount = 1) : base(0xD3A, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SheafOfHay : Item { [Constructible] - public SheafOfHay() : base(0xF36) - { - } - - public override double DefaultWeight => 10.0; + public SheafOfHay() : base(0xF36) => Weight = 10.0; } diff --git a/Projects/UOContent/Items/Food/Fruits.cs b/Projects/UOContent/Items/Food/Fruits.cs index 2a5ea409d..9971f1d62 100644 --- a/Projects/UOContent/Items/Food/Fruits.cs +++ b/Projects/UOContent/Items/Food/Fruits.cs @@ -8,12 +8,11 @@ public partial class FruitBasket : Food [Constructible] public FruitBasket() : base(0x993) { + Weight = 2.0; FillFactor = 5; Stackable = false; } - public override double DefaultWeight => 2.0; - public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -33,10 +32,9 @@ public partial class Banana : Food [Constructible] public Banana(int amount = 1) : base(0x171f, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [Flippable(0x1721, 0x1722)] @@ -46,10 +44,9 @@ public partial class Bananas : Food [Constructible] public Bananas(int amount = 1) : base(0x1721, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -58,10 +55,9 @@ public partial class SplitCoconut : Food [Constructible] public SplitCoconut(int amount = 1) : base(0x1725, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -70,10 +66,9 @@ public partial class Lemon : Food [Constructible] public Lemon(int amount = 1) : base(0x1728, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -82,10 +77,9 @@ public partial class Lemons : Food [Constructible] public Lemons(int amount = 1) : base(0x1729, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -94,10 +88,9 @@ public partial class Lime : Food [Constructible] public Lime(int amount = 1) : base(0x172a, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -106,10 +99,9 @@ public partial class Limes : Food [Constructible] public Limes(int amount = 1) : base(0x172B, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -118,10 +110,9 @@ public partial class Coconut : Food [Constructible] public Coconut(int amount = 1) : base(0x1726, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -130,10 +121,9 @@ public partial class OpenCoconut : Food [Constructible] public OpenCoconut(int amount = 1) : base(0x1723, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -142,10 +132,9 @@ public partial class Dates : Food [Constructible] public Dates(int amount = 1) : base(0x1727, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -154,10 +143,9 @@ public partial class Grapes : Food [Constructible] public Grapes(int amount = 1) : base(0x9D1, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -166,10 +154,9 @@ public partial class Peach : Food [Constructible] public Peach(int amount = 1) : base(0x9D2, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -178,10 +165,9 @@ public partial class Pear : Food [Constructible] public Pear(int amount = 1) : base(0x994, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -190,10 +176,9 @@ public partial class Apple : Food [Constructible] public Apple(int amount = 1) : base(0x9D0, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -202,10 +187,9 @@ public partial class Watermelon : Food [Constructible] public Watermelon(int amount = 1) : base(0xC5C, amount) { + Weight = 5.0; FillFactor = 5; } - - public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -214,10 +198,9 @@ public partial class SmallWatermelon : Food [Constructible] public SmallWatermelon(int amount = 1) : base(0xC5D, amount) { + Weight = 5.0; FillFactor = 5; } - - public override double DefaultWeight => 5.0; } [Flippable(0xc72, 0xc73)] @@ -227,10 +210,9 @@ public partial class Squash : Food [Constructible] public Squash(int amount = 1) : base(0xc72, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } [Flippable(0xc79, 0xc7a)] @@ -240,8 +222,7 @@ public partial class Cantaloupe : Food [Constructible] public Cantaloupe(int amount = 1) : base(0xc79, amount) { + Weight = 1.0; FillFactor = 1; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Food/Vegetables.cs b/Projects/UOContent/Items/Food/Vegetables.cs index 49cd1fcb5..9824d23b3 100644 --- a/Projects/UOContent/Items/Food/Vegetables.cs +++ b/Projects/UOContent/Items/Food/Vegetables.cs @@ -7,9 +7,11 @@ namespace Server.Items; public partial class Carrot : Food { [Constructible] - public Carrot(int amount = 1) : base(0xc78, amount) => FillFactor = 1; - - public override double DefaultWeight => 1.0; + public Carrot(int amount = 1) : base(0xc78, amount) + { + Weight = 1.0; + FillFactor = 1; + } } [Flippable(0xc7b, 0xc7c)] @@ -17,9 +19,11 @@ public partial class Carrot : Food public partial class Cabbage : Food { [Constructible] - public Cabbage(int amount = 1) : base(0xc7b, amount) => FillFactor = 1; - - public override double DefaultWeight => 1.0; + public Cabbage(int amount = 1) : base(0xc7b, amount) + { + Weight = 1.0; + FillFactor = 1; + } } [Flippable(0xc6d, 0xc6e)] @@ -27,9 +31,11 @@ public partial class Cabbage : Food public partial class Onion : Food { [Constructible] - public Onion(int amount = 1) : base(0xc6d, amount) => FillFactor = 1; - - public override double DefaultWeight => 1.0; + public Onion(int amount = 1) : base(0xc6d, amount) + { + Weight = 1.0; + FillFactor = 1; + } } [Flippable(0xc70, 0xc71)] @@ -37,9 +43,11 @@ public partial class Onion : Food public partial class Lettuce : Food { [Constructible] - public Lettuce(int amount = 1) : base(0xc70, amount) => FillFactor = 1; - - public override double DefaultWeight => 1.0; + public Lettuce(int amount = 1) : base(0xc70, amount) + { + Weight = 1.0; + FillFactor = 1; + } } [Flippable(0xC6A, 0xC6B)] @@ -47,16 +55,20 @@ public partial class Lettuce : Food public partial class Pumpkin : Food { [Constructible] - public Pumpkin(int amount = 1) : base(0xC6A, amount) => FillFactor = 8; - - public override double DefaultWeight => 1.0; + public Pumpkin(int amount = 1) : base(0xC6A, amount) + { + Weight = 1.0; + FillFactor = 8; + } } [SerializationGenerator(0, false)] public partial class SmallPumpkin : Food { [Constructible] - public SmallPumpkin(int amount = 1) : base(0xC6C, amount) => FillFactor = 8; - - public override double DefaultWeight => 1.0; + public SmallPumpkin(int amount = 1) : base(0xC6C, amount) + { + Weight = 1.0; + FillFactor = 8; + } } diff --git a/Projects/UOContent/Items/Games/BaseBoard.cs b/Projects/UOContent/Items/Games/BaseBoard.cs index b13d7dde2..123f700dc 100644 --- a/Projects/UOContent/Items/Games/BaseBoard.cs +++ b/Projects/UOContent/Items/Games/BaseBoard.cs @@ -19,9 +19,9 @@ public abstract partial class BaseBoard : Container, ISecurable public BaseBoard(int itemID) : base(itemID) { CreatePieces(); - } - public override double DefaultWeight => 5.0; + Weight = 5.0; + } public override bool DisplaysContent => false; // Do not display (x items, y stones) @@ -71,7 +71,7 @@ public abstract partial class BaseBoard : Container, ISecurable } else { - var buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingEffectPackets.SoundPacketLength].InitializePacket(); foreach (var state in GetClientsInRange(2)) { diff --git a/Projects/UOContent/Items/Games/Dices.cs b/Projects/UOContent/Items/Games/Dices.cs index ce92158c2..37346703d 100644 --- a/Projects/UOContent/Items/Games/Dices.cs +++ b/Projects/UOContent/Items/Games/Dices.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class Dices : Item, ITelekinesisable { [Constructible] - public Dices() : base(0xFA7) - { - } - - public override double DefaultWeight => 1.0; + public Dices() : base(0xFA7) => Weight = 1.0; public void OnTelekinesis(Mobile from) { diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs index 89e4d6f8f..a4be5c613 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs @@ -43,6 +43,8 @@ public partial class MahjongGame : Item, ISecurable [Constructible] public MahjongGame() : base(0xFAA) { + Weight = 5.0; + BuildWalls(); _dealerIndicator = new MahjongDealerIndicator(this, new Point2D(300, 300), MahjongPieceDirection.Up, MahjongWind.North); @@ -53,35 +55,55 @@ public partial class MahjongGame : Item, ISecurable _level = SecureLevel.CoOwners; } - public override double DefaultWeight => 5.0; - - [SerializableField(6, fieldChanged: nameof(OnShowScoresChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private bool _showScores; - - private void OnShowScoresChanged(bool oldValue, bool newValue) + [CommandProperty(AccessLevel.GameMaster)] + [SerializableProperty(6)] + public bool ShowScores { - if (newValue) + get => _showScores; + set { - _players.SendPlayersPacket(true, true); + if (_showScores == value) + { + return; + } + + _showScores = value; + + if (value) + { + _players.SendPlayersPacket(true, true); + } + + _players.SendGeneralPacket(true, true); + _players.SendLocalizedMessage(value ? 1062777 : 1062778); // The dealer has enabled/disabled score display. + this.MarkDirty(); } - _players.SendGeneralPacket(true, true); - _players.SendLocalizedMessage(newValue ? 1062777 : 1062778); // The dealer has enabled/disabled score display. } - [SerializableField(7, fieldChanged: nameof(OnSpectatorVisionChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private bool _spectatorVision; - - private void OnSpectatorVisionChanged(bool oldValue, bool newValue) + [CommandProperty(AccessLevel.GameMaster)] + [SerializableProperty(7)] + public bool SpectatorVision { - if (_players.IsInGamePlayer(_players.DealerPosition)) + get => _spectatorVision; + set { - _players.Dealer.NetState.SendMahjongGeneralInfo(this); + if (_spectatorVision == value) + { + return; + } + + _spectatorVision = value; + + if (_players.IsInGamePlayer(_players.DealerPosition)) + { + _players.Dealer.NetState.SendMahjongGeneralInfo(this); + } + + _players.SendTilesPacket(false, true); + _players.SendLocalizedMessage(value ? 1062715 : 1062716); // The dealer has enabled/disabled Spectator Vision. + InvalidateProperties(); + this.MarkDirty(); } - _players.SendTilesPacket(false, true); - _players.SendLocalizedMessage(newValue ? 1062715 : 1062716); // The dealer has enabled/disabled Spectator Vision. } private void BuildHorizontalWall( diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs index 194dc4da3..eaf22be58 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MahjongPackets.cs * * * @@ -346,7 +346,7 @@ namespace Server.Engines.Mahjong writer.Write((byte)0); writer.Write(players.IsPublic(i)); - writer.WriteLatin1(m?.Name ?? "", 30); + writer.WriteAscii(m?.Name ?? "", 30); writer.Write(m == null || !players.IsInGamePlayer(i)); count++; @@ -465,7 +465,7 @@ namespace Server.Engines.Mahjong return; } - var buffer = stackalloc byte[MahjongGeneralInfoPacketLength].InitializePacket(); + Span buffer = stackalloc byte[MahjongGeneralInfoPacketLength].InitializePacket(); CreateMahjongGeneralInfo(buffer, game); ns.Send(buffer); @@ -508,7 +508,7 @@ namespace Server.Engines.Mahjong return; } - var buffer = stackalloc byte[MahjongRelievePacketLength].InitializePacket(); + Span buffer = stackalloc byte[MahjongRelievePacketLength].InitializePacket(); CreateMahjongRelieve(buffer, game); ns.Send(buffer); diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongPlayers.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongPlayers.cs index 82d023f13..2627ec27e 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongPlayers.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongPlayers.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using ModernUO.Serialization; using Server.Network; @@ -186,7 +187,7 @@ public partial class MahjongPlayers { var removed = false; - var relievePacket = stackalloc byte[MahjongPackets.MahjongRelievePacketLength].InitializePacket(); + Span relievePacket = stackalloc byte[MahjongPackets.MahjongRelievePacketLength].InitializePacket(); for (var i = 0; i < _players.Length; i++) { @@ -542,7 +543,7 @@ public partial class MahjongPlayers return; } - var generalInfo = stackalloc byte[MahjongPackets.MahjongGeneralInfoPacketLength].InitializePacket(); + Span generalInfo = stackalloc byte[MahjongPackets.MahjongGeneralInfoPacketLength].InitializePacket(); foreach (var mobile in mobiles) { @@ -576,7 +577,7 @@ public partial class MahjongPlayers return; } - var relievePacket = stackalloc byte[MahjongPackets.MahjongRelievePacketLength].InitializePacket(); + Span relievePacket = stackalloc byte[MahjongPackets.MahjongRelievePacketLength].InitializePacket(); foreach (var mobile in mobiles) { diff --git a/Projects/UOContent/Items/Guilds/GuildDeed.cs b/Projects/UOContent/Items/Guilds/GuildDeed.cs index 1c1d6f61a..ac0869a71 100644 --- a/Projects/UOContent/Items/Guilds/GuildDeed.cs +++ b/Projects/UOContent/Items/Guilds/GuildDeed.cs @@ -9,11 +9,7 @@ namespace Server.Items; public partial class GuildDeed : Item { [Constructible] - public GuildDeed() : base(0x14F0) - { - } - - public override double DefaultWeight => 1.0; + public GuildDeed() : base(0x14F0) => Weight = 1.0; public override int LabelNumber => 1041055; // a guild deed diff --git a/Projects/UOContent/Items/Guilds/GuildTeleporter.cs b/Projects/UOContent/Items/Guilds/GuildTeleporter.cs index 44103e998..dc471b0ad 100644 --- a/Projects/UOContent/Items/Guilds/GuildTeleporter.cs +++ b/Projects/UOContent/Items/Guilds/GuildTeleporter.cs @@ -13,12 +13,11 @@ public partial class GuildTeleporter : Item [Constructible] public GuildTeleporter(Item stone = null) : base(0x1869) { + Weight = 1.0; LootType = LootType.Blessed; _stone = stone; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1041054; // guildstone teleporter public override bool DisplayLootType => false; diff --git a/Projects/UOContent/Items/Guilds/Guildstone.cs b/Projects/UOContent/Items/Guilds/Guildstone.cs index 3ae67d00c..6c0603984 100644 --- a/Projects/UOContent/Items/Guilds/Guildstone.cs +++ b/Projects/UOContent/Items/Guilds/Guildstone.cs @@ -54,13 +54,15 @@ public partial class Guildstone : Item, IAddon, IChoppable var house = BaseHouse.FindHouseAt(this); - if (house?.IsOwner(from) == true && house.Addons.Remove(this)) + if (house?.IsOwner(from) == true && house.Addons.Contains(this)) { Effects.PlaySound(GetWorldLocation(), Map, 0x3B3); from.SendLocalizedMessage(500461); // You destroy the item. Delete(); + house.Addons.Remove(this); + var deed = Deed; if (deed != null) @@ -185,7 +187,8 @@ public partial class Guildstone : Item, IAddon, IChoppable _guild.Accepted.Remove(from); _guild.AddMember(from); - GuildGump.DisplayTo(from, Guild); + GuildGump.EnsureClosed(from); + from.SendGump(new GuildGump(from, Guild)); } else if (from.AccessLevel < AccessLevel.GameMaster && !_guild.IsMember(from)) { @@ -194,7 +197,8 @@ public partial class Guildstone : Item, IAddon, IChoppable } else { - GuildGump.DisplayTo(from, _guild); + GuildGump.EnsureClosed(from); + from.SendGump(new GuildGump(from, _guild)); } } } @@ -224,9 +228,9 @@ public partial class GuildstoneDeed : Item _guild = g; _guildName = guildName; _guildAbbrev = abbrev; - } - public override double DefaultWeight => 1.0; + Weight = 1.0; + } public override int LabelNumber => 1041233; // deed to a guildstone diff --git a/Projects/UOContent/Items/Jewels/BaseJewel.cs b/Projects/UOContent/Items/Jewels/BaseJewel.cs index e693c0aaa..9e4bf0365 100644 --- a/Projects/UOContent/Items/Jewels/BaseJewel.cs +++ b/Projects/UOContent/Items/Jewels/BaseJewel.cs @@ -18,7 +18,7 @@ public enum GemType Diamond } -[SerializationGenerator(5, false)] +[SerializationGenerator(4, false)] public abstract partial class BaseJewel : Item, ICraftable, IAosItem { [EncodedInt] @@ -47,11 +47,6 @@ public abstract partial class BaseJewel : Item, ICraftable, IAosItem [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosSkillBonuses _skillBonuses; - [EncodedInt] - [SerializableField(7)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _gemCount; - public BaseJewel(int itemID, Layer layer) : base(itemID) { _attributes = new AosAttributes(this); @@ -93,13 +88,16 @@ public abstract partial class BaseJewel : Item, ICraftable, IAosItem } } - [SerializableField(2, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(2)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(_resource); + get => _resource; + set + { + _resource = value; + Hue = CraftResources.GetHue(_resource); + } } public override int PhysicalResistance => Resistances.Physical; @@ -185,129 +183,9 @@ public abstract partial class BaseJewel : Item, ICraftable, IAosItem } } - // T2A jewelry: read gem info from craft context (set by GemSelectTarget). - // The entire targeted gem stack is consumed and the piece is named by that - // count (e.g. "a 1000 diamond ring"). - if (context is { PendingGemType: not GemType.None, PendingGemCount: > 0 }) - { - var gemItemType = GetGemItemType(context.PendingGemType); - var gemCount = context.PendingGemCount; - - if (gemItemType != null && from.Backpack?.ConsumeTotal(gemItemType, gemCount) == true) - { - GemType = context.PendingGemType; - GemCount = gemCount; - } - else - { - // Gems were no longer available (or unknown type): craft a plain piece - // rather than naming it for gems that were never consumed. - from.SendAsciiMessage("You lack the gemstones to set into this piece."); - } - - context.PendingGemType = GemType.None; - context.PendingGemCount = 0; - } - return 1; } - public override void OnSingleClick(Mobile from) - { - if (!Core.UOTD) - { - OnSingleClickPreUOTD(from); - return; - } - - base.OnSingleClick(from); - } - - public virtual void OnSingleClickPreUOTD(Mobile from) - { - var plural = _gemCount > 1; - string name; - if (this is WeddingRing) - { - name = $"a {Name}"; - } - else - { - name = Name; - - if (name == null) - { - var articleAnName = (TileData.ItemTable[ItemID].Flags & TileFlag.ArticleAn) != 0; - name = $"{(articleAnName ? "an" : "a")} {Localization.GetText(LabelNumber).ToLowerInvariant()}"; - } - } - - if (_gemType != GemType.None && _gemCount > 0) - { - var gemName = GetGemName(_gemType, plural); - LabelTo(from, plural - ? $"{name} with {_gemCount} {gemName}" - : $"{name} with {gemName}"); - } - else - { - LabelTo(from, name); - } - } - - - private static string GetGemName(GemType type, bool plural = false) => type switch - { - GemType.StarSapphire when plural => "star sapphires", - GemType.StarSapphire => "a star sapphire", - GemType.Emerald when plural => "emeralds", - GemType.Emerald => "an emerald", - GemType.Sapphire when plural => "sapphires", - GemType.Sapphire => "a sapphire", - GemType.Ruby when plural => "rubies", - GemType.Ruby => "a ruby", - GemType.Citrine when plural => "citrines", - GemType.Citrine => "a citrine", - GemType.Amethyst when plural => "amethysts", - GemType.Amethyst => "an amethyst", - GemType.Tourmaline when plural => "tourmalines", - GemType.Tourmaline => "a tourmaline", - GemType.Amber when plural => "ambers", - GemType.Amber => "an amber", - GemType.Diamond when plural => "diamonds", - GemType.Diamond => "a diamond", - _ when plural => "gems", - _ => "a gem" - }; - - internal static GemType GetGemType(Item item) => item switch - { - StarSapphire => GemType.StarSapphire, - Emerald => GemType.Emerald, - Sapphire => GemType.Sapphire, - Ruby => GemType.Ruby, - Citrine => GemType.Citrine, - Amethyst => GemType.Amethyst, - Tourmaline => GemType.Tourmaline, - Amber => GemType.Amber, - Diamond => GemType.Diamond, - _ => GemType.None - }; - - internal static Type GetGemItemType(GemType type) => type switch - { - GemType.StarSapphire => typeof(StarSapphire), - GemType.Emerald => typeof(Emerald), - GemType.Sapphire => typeof(Sapphire), - GemType.Ruby => typeof(Ruby), - GemType.Citrine => typeof(Citrine), - GemType.Amethyst => typeof(Amethyst), - GemType.Tourmaline => typeof(Tourmaline), - GemType.Amber => typeof(Amber), - GemType.Diamond => typeof(Diamond), - _ => null - }; - public override void OnAfterDuped(Item newItem) { if (newItem is not BaseJewel jewel) @@ -386,7 +264,125 @@ public abstract partial class BaseJewel : Item, ICraftable, IAosItem list.Add(1061078, prop); // artifact rarity ~1_val~ } - Attributes.GetProperties(list); + if ((prop = Attributes.WeaponDamage) != 0) + { + list.Add(1060401, prop); // damage increase ~1_val~% + } + + if ((prop = Attributes.DefendChance) != 0) + { + list.Add(1060408, prop); // defense chance increase ~1_val~% + } + + if ((prop = Attributes.BonusDex) != 0) + { + list.Add(1060409, prop); // dexterity bonus ~1_val~ + } + + if ((prop = Attributes.EnhancePotions) != 0) + { + list.Add(1060411, prop); // enhance potions ~1_val~% + } + + if ((prop = Attributes.CastRecovery) != 0) + { + list.Add(1060412, prop); // faster cast recovery ~1_val~ + } + + if ((prop = Attributes.CastSpeed) != 0) + { + list.Add(1060413, prop); // faster casting ~1_val~ + } + + if ((prop = Attributes.AttackChance) != 0) + { + list.Add(1060415, prop); // hit chance increase ~1_val~% + } + + if ((prop = Attributes.BonusHits) != 0) + { + list.Add(1060431, prop); // hit point increase ~1_val~ + } + + if ((prop = Attributes.BonusInt) != 0) + { + list.Add(1060432, prop); // intelligence bonus ~1_val~ + } + + if ((prop = Attributes.LowerManaCost) != 0) + { + list.Add(1060433, prop); // lower mana cost ~1_val~% + } + + if ((prop = Attributes.LowerRegCost) != 0) + { + list.Add(1060434, prop); // lower reagent cost ~1_val~% + } + + if ((prop = Attributes.Luck) != 0) + { + list.Add(1060436, prop); // luck ~1_val~ + } + + if ((prop = Attributes.BonusMana) != 0) + { + list.Add(1060439, prop); // mana increase ~1_val~ + } + + if ((prop = Attributes.RegenMana) != 0) + { + list.Add(1060440, prop); // mana regeneration ~1_val~ + } + + if (Attributes.NightSight != 0) + { + list.Add(1060441); // night sight + } + + if ((prop = Attributes.ReflectPhysical) != 0) + { + list.Add(1060442, prop); // reflect physical damage ~1_val~% + } + + if ((prop = Attributes.RegenStam) != 0) + { + list.Add(1060443, prop); // stamina regeneration ~1_val~ + } + + if ((prop = Attributes.RegenHits) != 0) + { + list.Add(1060444, prop); // hit point regeneration ~1_val~ + } + + if (Attributes.SpellChanneling != 0) + { + list.Add(1060482); // spell channeling + } + + if ((prop = Attributes.SpellDamage) != 0) + { + list.Add(1060483, prop); // spell damage increase ~1_val~% + } + + if ((prop = Attributes.BonusStam) != 0) + { + list.Add(1060484, prop); // stamina increase ~1_val~ + } + + if ((prop = Attributes.BonusStr) != 0) + { + list.Add(1060485, prop); // strength bonus ~1_val~ + } + + if ((prop = Attributes.WeaponSpeed) != 0) + { + list.Add(1060486, prop); // swing speed increase ~1_val~% + } + + if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) + { + list.Add(1075210, prop); // Increased Karma Loss ~1val~% + } AddResistanceProperties(list); @@ -410,18 +406,6 @@ public abstract partial class BaseJewel : Item, ICraftable, IAosItem _skillBonuses.Deserialize(reader); } - private void MigrateFrom(V4Content content) - { - _maxHitPoints = content.MaxHitPoints; - _hitPoints = content.HitPoints; - _resource = content.Resource; - _gemType = content.GemType; - _attributes = content.Attributes; - _resistances = content.Resistances; - _skillBonuses = content.SkillBonuses; - // _gemCount defaults to 0 - } - [AfterDeserialization] private void AfterDeserialization() { diff --git a/Projects/UOContent/Items/Jewels/Beads.cs b/Projects/UOContent/Items/Jewels/Beads.cs index 0558cb0d2..e60572be2 100644 --- a/Projects/UOContent/Items/Jewels/Beads.cs +++ b/Projects/UOContent/Items/Jewels/Beads.cs @@ -6,9 +6,5 @@ namespace Server.Items; public partial class Beads : Item { [Constructible] - public Beads() : base(0x108B) - { - } - - public override double DefaultWeight => 1.0; + public Beads() : base(0x108B) => Weight = 1.0; } diff --git a/Projects/UOContent/Items/Jewels/Bracelet.cs b/Projects/UOContent/Items/Jewels/Bracelet.cs index 90b79cb96..7a5aa44f8 100644 --- a/Projects/UOContent/Items/Jewels/Bracelet.cs +++ b/Projects/UOContent/Items/Jewels/Bracelet.cs @@ -16,20 +16,12 @@ public abstract partial class BaseBracelet : BaseJewel public partial class GoldBracelet : BaseBracelet { [Constructible] - public GoldBracelet() : base(0x1086) - { - } - - public override double DefaultWeight => 0.1; + public GoldBracelet() : base(0x1086) => Weight = 0.1; } [SerializationGenerator(0, false)] public partial class SilverBracelet : BaseBracelet { [Constructible] - public SilverBracelet() : base(0x1F06) - { - } - - public override double DefaultWeight => 0.1; + public SilverBracelet() : base(0x1F06) => Weight = 0.1; } diff --git a/Projects/UOContent/Items/Jewels/Earrings.cs b/Projects/UOContent/Items/Jewels/Earrings.cs index 1596a26ef..73a6ccd09 100644 --- a/Projects/UOContent/Items/Jewels/Earrings.cs +++ b/Projects/UOContent/Items/Jewels/Earrings.cs @@ -16,20 +16,12 @@ public abstract partial class BaseEarrings : BaseJewel public partial class GoldEarrings : BaseEarrings { [Constructible] - public GoldEarrings() : base(0x1087) - { - } - - public override double DefaultWeight => 0.1; + public GoldEarrings() : base(0x1087) => Weight = 0.1; } [SerializationGenerator(0, false)] public partial class SilverEarrings : BaseEarrings { [Constructible] - public SilverEarrings() : base(0x1F07) - { - } - - public override double DefaultWeight => 0.1; + public SilverEarrings() : base(0x1F07) => Weight = 0.1; } diff --git a/Projects/UOContent/Items/Jewels/Necklace.cs b/Projects/UOContent/Items/Jewels/Necklace.cs index 12a02144a..d4cd1eb9e 100644 --- a/Projects/UOContent/Items/Jewels/Necklace.cs +++ b/Projects/UOContent/Items/Jewels/Necklace.cs @@ -16,53 +16,33 @@ public abstract partial class BaseNecklace : BaseJewel public partial class Necklace : BaseNecklace { [Constructible] - public Necklace() : base(0x1085) - { - } - - public override double DefaultWeight => 0.1; + public Necklace() : base(0x1085) => Weight = 0.1; } [SerializationGenerator(0, false)] public partial class GoldNecklace : BaseNecklace { [Constructible] - public GoldNecklace() : base(0x1088) - { - } - - public override double DefaultWeight => 0.1; + public GoldNecklace() : base(0x1088) => Weight = 0.1; } [SerializationGenerator(0, false)] public partial class GoldBeadNecklace : BaseNecklace { [Constructible] - public GoldBeadNecklace() : base(0x1089) - { - } - - public override double DefaultWeight => 0.1; + public GoldBeadNecklace() : base(0x1089) => Weight = 0.1; } [SerializationGenerator(0, false)] public partial class SilverNecklace : BaseNecklace { [Constructible] - public SilverNecklace() : base(0x1F08) - { - } - - public override double DefaultWeight => 0.1; + public SilverNecklace() : base(0x1F08) => Weight = 0.1; } [SerializationGenerator(0, false)] public partial class SilverBeadNecklace : BaseNecklace { [Constructible] - public SilverBeadNecklace() : base(0x1F05) - { - } - - public override double DefaultWeight => 0.1; + public SilverBeadNecklace() : base(0x1F05) => Weight = 0.1; } diff --git a/Projects/UOContent/Items/Jewels/Ring.cs b/Projects/UOContent/Items/Jewels/Ring.cs index 8a552508a..9c142f099 100644 --- a/Projects/UOContent/Items/Jewels/Ring.cs +++ b/Projects/UOContent/Items/Jewels/Ring.cs @@ -16,33 +16,12 @@ public abstract partial class BaseRing : BaseJewel public partial class GoldRing : BaseRing { [Constructible] - public GoldRing() : base(0x108a) - { - } - - public override double DefaultWeight => 0.1; + public GoldRing() : base(0x108a) => Weight = 0.1; } [SerializationGenerator(0, false)] public partial class SilverRing : BaseRing { [Constructible] - public SilverRing() : base(0x1F09) - { - } - - public override double DefaultWeight => 0.1; -} - -[SerializationGenerator(0, false)] -public partial class WeddingRing : BaseRing -{ - public override string DefaultName => "wedding ring"; - - [Constructible] - public WeddingRing() : base(0x108a) - { - } - - public override double DefaultWeight => 0.1; + public SilverRing() : base(0x1F09) => Weight = 0.1; } diff --git a/Projects/UOContent/Items/Lights/BaseLight.cs b/Projects/UOContent/Items/Lights/BaseLight.cs index 4bd63ba63..d3bff4af5 100644 --- a/Projects/UOContent/Items/Lights/BaseLight.cs +++ b/Projects/UOContent/Items/Lights/BaseLight.cs @@ -3,7 +3,7 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(2, false)] +[SerializationGenerator(1, false)] public abstract partial class BaseLight : Item { public static readonly bool Burnout = false; @@ -16,10 +16,11 @@ public abstract partial class BaseLight : Item [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _protected; + [TimerDrift] [SerializableField(4, getter: "private", setter: "private")] - [DeserializeTimer(nameof(DeserializeTimer))] private Timer _burnTimer; + [DeserializeTimerField(4)] private void DeserializeTimer(TimeSpan delay) { if (_burning && _duration != TimeSpan.Zero) @@ -28,19 +29,6 @@ public abstract partial class BaseLight : Item } } - private void MigrateFrom(V1Content content) - { - _burntOut = content.BurntOut; - _burning = content.Burning; - _duration = content.Duration; - _protected = content.Protected; - - if (content.BurnTimerDelay != TimeSpan.MinValue) - { - DeserializeTimer(content.BurnTimerDelay); - } - } - [Constructible] public BaseLight(int itemID) : base(itemID) { diff --git a/Projects/UOContent/Items/Lights/Brazier.cs b/Projects/UOContent/Items/Lights/Brazier.cs index dd0080a29..56f834c3b 100644 --- a/Projects/UOContent/Items/Lights/Brazier.cs +++ b/Projects/UOContent/Items/Lights/Brazier.cs @@ -13,9 +13,8 @@ public partial class Brazier : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = true; Light = LightType.Circle225; + Weight = 20.0; } - public override double DefaultWeight => 20.0; - public override int LitItemID => 0xE31; } diff --git a/Projects/UOContent/Items/Lights/BrazierTall.cs b/Projects/UOContent/Items/Lights/BrazierTall.cs index 17f444fc4..c23945c50 100644 --- a/Projects/UOContent/Items/Lights/BrazierTall.cs +++ b/Projects/UOContent/Items/Lights/BrazierTall.cs @@ -13,9 +13,8 @@ public partial class BrazierTall : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = true; Light = LightType.Circle300; + Weight = 25.0; } - public override double DefaultWeight => 25.0; - public override int LitItemID => 0x19AA; } diff --git a/Projects/UOContent/Items/Lights/Candelabra.cs b/Projects/UOContent/Items/Lights/Candelabra.cs index c2d2c80aa..1558417bd 100644 --- a/Projects/UOContent/Items/Lights/Candelabra.cs +++ b/Projects/UOContent/Items/Lights/Candelabra.cs @@ -16,10 +16,9 @@ public partial class Candelabra : BaseLight, IShipwreckedItem Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle225; + Weight = 3.0; } - public override double DefaultWeight => 3.0; - public override int LitItemID => 0xB1D; public override int UnlitItemID => 0xA27; diff --git a/Projects/UOContent/Items/Lights/CandelabraStand.cs b/Projects/UOContent/Items/Lights/CandelabraStand.cs index b546947eb..00e49e521 100644 --- a/Projects/UOContent/Items/Lights/CandelabraStand.cs +++ b/Projects/UOContent/Items/Lights/CandelabraStand.cs @@ -12,9 +12,9 @@ public partial class CandelabraStand : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle225; + Weight = 20.0; } - public override double DefaultWeight => 20.0; public override int LitItemID => 0xB26; public override int UnlitItemID => 0xA29; } diff --git a/Projects/UOContent/Items/Lights/Candle.cs b/Projects/UOContent/Items/Lights/Candle.cs index 283bccb03..f0d5bd42e 100644 --- a/Projects/UOContent/Items/Lights/Candle.cs +++ b/Projects/UOContent/Items/Lights/Candle.cs @@ -14,9 +14,9 @@ public partial class Candle : BaseEquipableLight Stackable = true; Light = LightType.Circle150; + Weight = 1.0; } - public override double DefaultWeight => 1.0; public override int LitItemID => 0xA0F; public override int UnlitItemID => 0xA28; } diff --git a/Projects/UOContent/Items/Lights/CandleLarge.cs b/Projects/UOContent/Items/Lights/CandleLarge.cs index 7981e6e4c..12dfdf188 100644 --- a/Projects/UOContent/Items/Lights/CandleLarge.cs +++ b/Projects/UOContent/Items/Lights/CandleLarge.cs @@ -10,11 +10,12 @@ public partial class CandleLarge : BaseLight public CandleLarge() : base(0xA26) { Duration = Burnout ? TimeSpan.FromMinutes(25) : TimeSpan.Zero; + Burning = false; Light = LightType.Circle150; + Weight = 2.0; } - public override double DefaultWeight => 2.0; public override int LitItemID => 0xB1A; public override int UnlitItemID => 0xA26; } diff --git a/Projects/UOContent/Items/Lights/CandleLong.cs b/Projects/UOContent/Items/Lights/CandleLong.cs index 4548f95d0..39daca623 100644 --- a/Projects/UOContent/Items/Lights/CandleLong.cs +++ b/Projects/UOContent/Items/Lights/CandleLong.cs @@ -10,12 +10,12 @@ public partial class CandleLong : BaseLight public CandleLong() : base(0x1433) { Duration = Burnout ? TimeSpan.FromMinutes(30) : TimeSpan.Zero; + Burning = false; Light = LightType.Circle150; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - public override int LitItemID => 0x1430; public override int UnlitItemID => 0x1433; } diff --git a/Projects/UOContent/Items/Lights/CandleShort.cs b/Projects/UOContent/Items/Lights/CandleShort.cs index 4ad2758f2..942478f35 100644 --- a/Projects/UOContent/Items/Lights/CandleShort.cs +++ b/Projects/UOContent/Items/Lights/CandleShort.cs @@ -10,12 +10,12 @@ public partial class CandleShort : BaseLight public CandleShort() : base(0x142F) { Duration = Burnout ? TimeSpan.FromMinutes(25) : TimeSpan.Zero; + Burning = false; Light = LightType.Circle150; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - public override int LitItemID => 0x142C; public override int UnlitItemID => 0x142F; } diff --git a/Projects/UOContent/Items/Lights/CandleSkull.cs b/Projects/UOContent/Items/Lights/CandleSkull.cs index 7236f4fbc..8cd3a9f21 100644 --- a/Projects/UOContent/Items/Lights/CandleSkull.cs +++ b/Projects/UOContent/Items/Lights/CandleSkull.cs @@ -13,10 +13,9 @@ public partial class CandleSkull : BaseLight Burning = false; Light = LightType.Circle150; + Weight = 5.0; } - public override double DefaultWeight => 5.0; - public override int LitItemID => ItemID is 0x1583 or 0x1854 ? 0x1854 : 0x1858; public override int UnlitItemID => ItemID is 0x1853 or 0x1584 ? 0x1853 : 0x1857; diff --git a/Projects/UOContent/Items/Lights/HangingLantern.cs b/Projects/UOContent/Items/Lights/HangingLantern.cs index 942635a02..1ae33dcf9 100644 --- a/Projects/UOContent/Items/Lights/HangingLantern.cs +++ b/Projects/UOContent/Items/Lights/HangingLantern.cs @@ -13,9 +13,9 @@ public partial class HangingLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; + Weight = 40.0; } - public override double DefaultWeight => 40.0; public override int LitItemID => 0xA1A; public override int UnlitItemID => 0xA1D; } diff --git a/Projects/UOContent/Items/Lights/HeatingStand.cs b/Projects/UOContent/Items/Lights/HeatingStand.cs index ec9f6359c..1b1abe044 100644 --- a/Projects/UOContent/Items/Lights/HeatingStand.cs +++ b/Projects/UOContent/Items/Lights/HeatingStand.cs @@ -10,12 +10,12 @@ public partial class HeatingStand : BaseLight public HeatingStand() : base(0x1849) { Duration = Burnout ? TimeSpan.FromMinutes(25) : TimeSpan.Zero; + Burning = false; Light = LightType.Empty; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - public override int LitItemID => 0x184A; public override int UnlitItemID => 0x1849; diff --git a/Projects/UOContent/Items/Lights/LampPost1.cs b/Projects/UOContent/Items/Lights/LampPost1.cs index 3f6884618..985e6e7b1 100644 --- a/Projects/UOContent/Items/Lights/LampPost1.cs +++ b/Projects/UOContent/Items/Lights/LampPost1.cs @@ -13,9 +13,9 @@ public partial class LampPost1 : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; + Weight = 40.0; } - public override double DefaultWeight => 40.0; public override int LitItemID => 0xB20; public override int UnlitItemID => 0xB21; } diff --git a/Projects/UOContent/Items/Lights/LampPost2.cs b/Projects/UOContent/Items/Lights/LampPost2.cs index d0f630fe4..f36bdf4db 100644 --- a/Projects/UOContent/Items/Lights/LampPost2.cs +++ b/Projects/UOContent/Items/Lights/LampPost2.cs @@ -13,9 +13,9 @@ public partial class LampPost2 : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; + Weight = 40.0; } - public override double DefaultWeight => 40.0; public override int LitItemID => 0xB22; public override int UnlitItemID => 0xB23; } diff --git a/Projects/UOContent/Items/Lights/LampPost3.cs b/Projects/UOContent/Items/Lights/LampPost3.cs index 24ffa44a5..c9cb237a0 100644 --- a/Projects/UOContent/Items/Lights/LampPost3.cs +++ b/Projects/UOContent/Items/Lights/LampPost3.cs @@ -13,9 +13,9 @@ public partial class LampPost3 : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; + Weight = 40.0; } - public override double DefaultWeight => 40.0; public override int LitItemID => 0xB24; public override int UnlitItemID => 0xB25; } diff --git a/Projects/UOContent/Items/Lights/Lantern.cs b/Projects/UOContent/Items/Lights/Lantern.cs index 366728e95..b7cddd0cc 100644 --- a/Projects/UOContent/Items/Lights/Lantern.cs +++ b/Projects/UOContent/Items/Lights/Lantern.cs @@ -15,10 +15,9 @@ public partial class Lantern : BaseEquipableLight Burning = false; Light = LightType.Circle300; + Weight = 2.0; } - public override double DefaultWeight => 2.0; - public override int LitItemID => ItemID is 0xA15 or 0xA17 ? ItemID : 0xA22; public override int UnlitItemID => ItemID == 0xA18 ? ItemID : 0xA25; diff --git a/Projects/UOContent/Items/Lights/PaperLantern.cs b/Projects/UOContent/Items/Lights/PaperLantern.cs index fde95fb3e..681bf7705 100644 --- a/Projects/UOContent/Items/Lights/PaperLantern.cs +++ b/Projects/UOContent/Items/Lights/PaperLantern.cs @@ -14,9 +14,9 @@ public partial class PaperLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle150; + Weight = 3.0; } - public override double DefaultWeight => 3.0; public override int LitItemID => 0x24BD; public override int UnlitItemID => 0x24BE; } diff --git a/Projects/UOContent/Items/Lights/RedHangingLantern.cs b/Projects/UOContent/Items/Lights/RedHangingLantern.cs index 173a2e082..3643c2e12 100644 --- a/Projects/UOContent/Items/Lights/RedHangingLantern.cs +++ b/Projects/UOContent/Items/Lights/RedHangingLantern.cs @@ -14,10 +14,9 @@ public partial class RedHangingLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; + Weight = 3.0; } - public override double DefaultWeight => 3.0; - public override int LitItemID => ItemID == 0x24C2 ? 0x24C1 : 0x24C3; public override int UnlitItemID => ItemID == 0x24C1 ? 0x24C2 : 0x24C4; diff --git a/Projects/UOContent/Items/Lights/RoundPaperLantern.cs b/Projects/UOContent/Items/Lights/RoundPaperLantern.cs index d1bd3ae1e..3b028a0d3 100644 --- a/Projects/UOContent/Items/Lights/RoundPaperLantern.cs +++ b/Projects/UOContent/Items/Lights/RoundPaperLantern.cs @@ -14,10 +14,9 @@ public partial class RoundPaperLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle150; + Weight = 3.0; } - public override double DefaultWeight => 3.0; - public override int LitItemID => 0x24C9; public override int UnlitItemID => 0x24CA; } diff --git a/Projects/UOContent/Items/Lights/ShojiLantern.cs b/Projects/UOContent/Items/Lights/ShojiLantern.cs index f1482eff8..56aa5d81e 100644 --- a/Projects/UOContent/Items/Lights/ShojiLantern.cs +++ b/Projects/UOContent/Items/Lights/ShojiLantern.cs @@ -14,9 +14,9 @@ public partial class ShojiLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle150; + Weight = 3.0; } - public override double DefaultWeight => 3.0; public override int LitItemID => 0x24BB; public override int UnlitItemID => 0x24BC; } diff --git a/Projects/UOContent/Items/Lights/Torch.cs b/Projects/UOContent/Items/Lights/Torch.cs index a2f527070..75c5f0967 100644 --- a/Projects/UOContent/Items/Lights/Torch.cs +++ b/Projects/UOContent/Items/Lights/Torch.cs @@ -15,10 +15,9 @@ public partial class Torch : BaseEquipableLight Stackable = true; Light = LightType.Circle300; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - public override int LitItemID => 0xA12; public override int UnlitItemID => 0xF6B; diff --git a/Projects/UOContent/Items/Lights/WallSconce.cs b/Projects/UOContent/Items/Lights/WallSconce.cs index bf1c2576b..3268efcaa 100644 --- a/Projects/UOContent/Items/Lights/WallSconce.cs +++ b/Projects/UOContent/Items/Lights/WallSconce.cs @@ -14,10 +14,9 @@ public partial class WallSconce : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.WestBig; + Weight = 3.0; } - public override double DefaultWeight => 3.0; - public override int LitItemID => ItemID == 0x9FB ? 0x9FD : 0xA02; public override int UnlitItemID => ItemID == 0x9FD ? 0x9FB : 0xA00; diff --git a/Projects/UOContent/Items/Lights/WallTorch.cs b/Projects/UOContent/Items/Lights/WallTorch.cs index 9ed6944ec..968b0bbae 100644 --- a/Projects/UOContent/Items/Lights/WallTorch.cs +++ b/Projects/UOContent/Items/Lights/WallTorch.cs @@ -14,10 +14,9 @@ public partial class WallTorch : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.WestBig; + Weight = 3.0; } - public override double DefaultWeight => 3.0; - public override int LitItemID => ItemID == 0xA05 ? 0xA07 : 0xA0C; public override int UnlitItemID => ItemID == 0xA07 ? 0xA05 : 0xA0A; diff --git a/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs b/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs index 20837465e..7d62bde96 100644 --- a/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs +++ b/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs @@ -14,10 +14,9 @@ public partial class WhiteHangingLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; + Weight = 3.0; } - public override double DefaultWeight => 3.0; - public override int LitItemID => ItemID == 0x24C6 ? 0x24C5 : 0x24C7; public override int UnlitItemID => ItemID == 0x24C5 ? 0x24C6 : 0x24C8; diff --git a/Projects/UOContent/Items/Maps/MapItem.cs b/Projects/UOContent/Items/Maps/MapItem.cs index 0467d8220..b9ee21e53 100644 --- a/Projects/UOContent/Items/Maps/MapItem.cs +++ b/Projects/UOContent/Items/Maps/MapItem.cs @@ -42,14 +42,13 @@ public partial class MapItem : Item, ICraftable [Constructible] public MapItem(Map facet = null) : base(0x14EC) { + Weight = 1.0; _width = 200; _height = 200; _facet = facet; _pins = new List(); } - public override double DefaultWeight => 1.0; - public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue diff --git a/Projects/UOContent/Items/Maps/MapItemPackets.cs b/Projects/UOContent/Items/Maps/MapItemPackets.cs index a0887448e..18ddfc900 100644 --- a/Projects/UOContent/Items/Maps/MapItemPackets.cs +++ b/Projects/UOContent/Items/Maps/MapItemPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MapItemPackets.cs * * * diff --git a/Projects/UOContent/Items/Maps/TreasureMap.cs b/Projects/UOContent/Items/Maps/TreasureMap.cs index 25711e120..e8c853dd6 100644 --- a/Projects/UOContent/Items/Maps/TreasureMap.cs +++ b/Projects/UOContent/Items/Maps/TreasureMap.cs @@ -901,8 +901,6 @@ public partial class TreasureChestDirt : Item Timer.StartTimer(TimeSpan.FromMinutes(2.0), Delete); } - public override bool SkipSerialization => true; - public void Turn1() { ItemID = 0x913; @@ -912,4 +910,10 @@ public partial class TreasureChestDirt : Item { ItemID = 0x914; } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } diff --git a/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs b/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs index b2d48ed0f..29caacff8 100644 --- a/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs +++ b/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs @@ -9,12 +9,12 @@ public partial class RobeOfTheEclipse : BaseOuterTorso [Constructible] public RobeOfTheEclipse() : base(0x1F03, 0x486) { + Weight = 3.0; + Attributes.Luck = 95; // TODO: Supports arcane? } - public override double DefaultWeight => 3.0; - public override int LabelNumber => 1075082; // Robe of the Eclipse } diff --git a/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs b/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs index 54b4c4fe7..3bfc6a283 100644 --- a/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs +++ b/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs @@ -9,13 +9,13 @@ public partial class RobeOfTheEquinox : BaseOuterTorso [Constructible] public RobeOfTheEquinox() : base(0x1F04, 0xD6) { + Weight = 3.0; + Attributes.Luck = 95; // TODO: Supports arcane? // TODO: Elves Only } - public override double DefaultWeight => 3.0; - public override int LabelNumber => 1075042; // Robe of the Equinox } diff --git a/Projects/UOContent/Items/Misc/Acid.cs b/Projects/UOContent/Items/Misc/Acid.cs index 400df0ee1..4f730610d 100644 --- a/Projects/UOContent/Items/Misc/Acid.cs +++ b/Projects/UOContent/Items/Misc/Acid.cs @@ -39,8 +39,6 @@ public partial class Acid : Item public override string DefaultName => "a pool of acid"; - public override bool SkipSerialization => true; - public override void OnDelete() { _timerToken.Cancel(); @@ -91,4 +89,10 @@ public partial class Acid : Item { m.Damage(Utility.RandomMinMax(_minDamage, _maxDamage)); } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } diff --git a/Projects/UOContent/Items/Misc/ArcaneGem.cs b/Projects/UOContent/Items/Misc/ArcaneGem.cs index 4150deb87..ea474d086 100644 --- a/Projects/UOContent/Items/Misc/ArcaneGem.cs +++ b/Projects/UOContent/Items/Misc/ArcaneGem.cs @@ -13,10 +13,9 @@ public partial class ArcaneGem : Item public ArcaneGem() : base(0x1EA7) { Stackable = Core.ML; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - public override string DefaultName => "arcane gem"; public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Misc/BankCheck.cs b/Projects/UOContent/Items/Misc/BankCheck.cs index a3e38404c..4408d9f4f 100644 --- a/Projects/UOContent/Items/Misc/BankCheck.cs +++ b/Projects/UOContent/Items/Misc/BankCheck.cs @@ -20,14 +20,13 @@ public partial class BankCheck : Item [Constructible] public BankCheck(int worth) : base(0x14F0) { + Weight = 1.0; Hue = 0x34; LootType = LootType.Blessed; _worth = worth; } - public override double DefaultWeight => 1.0; - public override bool DisplayLootType => Core.AOS; public override int LabelNumber => 1041361; // A bank check @@ -137,69 +136,68 @@ public partial class BankCheck : Item return; } - if (AccountGold.Enabled) + var deposited = 0; + var toAdd = _worth; + + if (AccountGold.Enabled && from.Account?.DepositGold(toAdd) == true) { - if (from.Account?.DepositGold(_worth) != true) - { - return; - } - - Delete(); - - // Gold was deposited in your account: - from.SendLocalizedMessage(1042672, true, $"{_worth:N0}"); + deposited = toAdd; } - else + + while (toAdd > 0) { - // Internalize the check to free its slot for deposit. - // reserveSlots: 1 conditionally keeps a slot free for the check to bounce back - // only if the full amount won't fit — if it fits, no reservation is needed. - RecordBounce(); - Internalize(); + var amount = Math.Min(toAdd, 60000); - var deposited = Banker.DepositUpTo(from, _worth, false, 1); + var gold = new Gold(amount); - if (deposited >= _worth) + if (box.TryDropItem(from, gold, false)) { - Delete(); - } - else if (deposited > 0) - { - Worth -= deposited; - Bounce(from); + toAdd -= amount; + deposited += amount; } else { - Bounce(from); - from.SendLocalizedMessage(500390); // Your bank box is full. - return; + gold.Delete(); + break; } - - // Gold was deposited in your account: - from.SendLocalizedMessage(1042672, true, $"{deposited:N0}"); } - if (from is PlayerMobile pm) + if (deposited >= _worth) { - var qs = pm.Quest; + Delete(); + } + else + { + Worth -= deposited; + } - if (qs is DarkTidesQuest) + if (deposited > 0) + { + // Gold was deposited in your account: + from.SendLocalizedMessage(1042672, true, $"{deposited:N0}"); + + if (from is PlayerMobile pm) { - var obj = qs.FindObjective(); + var qs = pm.Quest; - if (obj?.Completed == false) + if (qs is DarkTidesQuest) { - obj.Complete(); + var obj = qs.FindObjective(); + + if (obj?.Completed == false) + { + obj.Complete(); + } } - } - if (qs is UzeraanTurmoilQuest) - { - var obj = qs.FindObjective(typeof(Engines.Quests.Haven.CashBankCheckObjective)); - - if (obj?.Completed == false) + if (qs is UzeraanTurmoilQuest) { - obj.Complete(); + var obj = qs.FindObjective(typeof(Engines.Quests.Haven.CashBankCheckObjective)); + + if (obj?.Completed == false) + { + obj.Complete(); + } } } } diff --git a/Projects/UOContent/Items/Misc/Beeswax.cs b/Projects/UOContent/Items/Misc/Beeswax.cs index 46354510a..81349d043 100644 --- a/Projects/UOContent/Items/Misc/Beeswax.cs +++ b/Projects/UOContent/Items/Misc/Beeswax.cs @@ -8,9 +8,8 @@ public partial class Beeswax : Item [Constructible] public Beeswax(int amount = 1) : base(0x1422) { + Weight = 1.0; Stackable = true; Amount = amount; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Misc/Blocker.cs b/Projects/UOContent/Items/Misc/Blocker.cs index cc1d5cf54..cadefcb1a 100644 --- a/Projects/UOContent/Items/Misc/Blocker.cs +++ b/Projects/UOContent/Items/Misc/Blocker.cs @@ -31,7 +31,7 @@ public partial class Blocker : Item private void SendGMItem(NetState ns) { // GM Packet - var buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); int length; diff --git a/Projects/UOContent/Items/Misc/Bola.cs b/Projects/UOContent/Items/Misc/Bola.cs index a2943c108..716a8e613 100644 --- a/Projects/UOContent/Items/Misc/Bola.cs +++ b/Projects/UOContent/Items/Misc/Bola.cs @@ -12,12 +12,11 @@ public partial class Bola : Item [Constructible] public Bola(int amount = 1) : base(0x26AC) { + Weight = 4.0; Stackable = true; Amount = amount; } - public override double DefaultWeight => 4.0; - public override void OnDoubleClick(Mobile from) { if (!IsChildOf(from.Backpack)) diff --git a/Projects/UOContent/Items/Misc/BolaBall.cs b/Projects/UOContent/Items/Misc/BolaBall.cs index b4737a2cb..b372b3abf 100644 --- a/Projects/UOContent/Items/Misc/BolaBall.cs +++ b/Projects/UOContent/Items/Misc/BolaBall.cs @@ -8,10 +8,9 @@ public partial class BolaBall : Item [Constructible] public BolaBall(int amount = 1) : base(0xE73) { + Weight = 4.0; Stackable = true; Amount = amount; Hue = 0x8AC; } - - public override double DefaultWeight => 4.0; } diff --git a/Projects/UOContent/Items/Misc/ClockworkAssembly.cs b/Projects/UOContent/Items/Misc/ClockworkAssembly.cs index 208775a2f..7348b6469 100644 --- a/Projects/UOContent/Items/Misc/ClockworkAssembly.cs +++ b/Projects/UOContent/Items/Misc/ClockworkAssembly.cs @@ -34,11 +34,10 @@ public partial class ClockworkAssembly : Item [Constructible] public ClockworkAssembly() : base(0x1EA8) { + Weight = 5.0; Hue = 1102; } - public override double DefaultWeight => 5.0; - public override int LabelNumber => 1073426; // Clockwork Assembly public override void OnDoubleClick(Mobile from) @@ -64,7 +63,7 @@ public partial class ClockworkAssembly : Item return; } - var scalar = tinkerSkill switch + double scalar = tinkerSkill switch { >= 100.0 => 1.0, >= 90.0 => 0.9, diff --git a/Projects/UOContent/Items/Misc/CommunicationCrystals.cs b/Projects/UOContent/Items/Misc/CommunicationCrystals.cs index 07df233e9..a52b1a12d 100644 --- a/Projects/UOContent/Items/Misc/CommunicationCrystals.cs +++ b/Projects/UOContent/Items/Misc/CommunicationCrystals.cs @@ -275,16 +275,8 @@ public partial class BroadcastCrystal : Item [SerializationGenerator(0)] public partial class ReceiverCrystal : Item { - [SerializableField(0, fieldChanged: nameof(OnSenderChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] private BroadcastCrystal _sender; - private void OnSenderChanged(BroadcastCrystal oldValue, BroadcastCrystal newValue) - { - oldValue?.RemoveReceiver(this); - newValue?.AddReceiver(this); - } - [Constructible] public ReceiverCrystal() : base(0x1ED0) => Light = LightType.Circle150; @@ -305,6 +297,20 @@ public partial class ReceiverCrystal : Item } } + [SerializableProperty(0, useField: nameof(_sender))] + [CommandProperty(AccessLevel.GameMaster)] + public BroadcastCrystal Sender + { + get => _sender; + set + { + _sender?.RemoveReceiver(this); + _sender = value; + value?.AddReceiver(this); + this.MarkDirty(); + } + } + public override void GetProperties(IPropertyList list) { base.GetProperties(list); diff --git a/Projects/UOContent/Items/Misc/Corpses/Corpse.Migrations.cs b/Projects/UOContent/Items/Misc/Corpses/Corpse.Migrations.cs deleted file mode 100644 index bc9ce047e..000000000 --- a/Projects/UOContent/Items/Misc/Corpses/Corpse.Migrations.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System; - -namespace Server.Items; - -public partial class Corpse -{ - // Decay timer and TimeOfDeath moved from delta time to anchored time - private void MigrateFrom(V18Content content) - { - _restoreEquip = content.RestoreEquip; - _flags = content.Flags; - _timeOfDeath = content.TimeOfDeath; - _restoreTable = content.RestoreTable; - _looters = content.Looters; - _killer = content.Killer; - _aggressors = content.Aggressors; - _owner = content.Owner; - _corpseName = content.CorpseName; - _accessLevel = content.AccessLevel; - _guild = content.Guild; - _equipItems = content.EquipItems; - _hairItemId = content.HairItemId; - _hairHue = content.HairHue; - _facialHairItemId = content.FacialHairItemId; - _facialHairHue = content.FacialHairHue; - - if (content.DecayTimerDelay != TimeSpan.MinValue) - { - DeserializeDecayTimer(content.DecayTimerDelay); - } - } - - // Decay timer moved from [TimerDrift]/[DeserializeTimerField] to [DeserializeTimer] - private void MigrateFrom(V17Content content) - { - _restoreEquip = content.RestoreEquip; - _flags = content.Flags; - _timeOfDeath = content.TimeOfDeath; - _restoreTable = content.RestoreTable; - _looters = content.Looters; - _killer = content.Killer; - _aggressors = content.Aggressors; - _owner = content.Owner; - _corpseName = content.CorpseName; - _accessLevel = content.AccessLevel; - _guild = content.Guild; - _equipItems = content.EquipItems; - _hairItemId = content.HairItemId; - _hairHue = content.HairHue; - _facialHairItemId = content.FacialHairItemId; - _facialHairHue = content.FacialHairHue; - - if (content.DecayTimerDelay != TimeSpan.MinValue) - { - DeserializeDecayTimer(content.DecayTimerDelay); - } - } - - // Decomposed VirtualHairInfo into discrete int fields (hair/facial hair item id + hue) - private void MigrateFrom(V16Content content) - { - _restoreEquip = content.RestoreEquip; - _flags = content.Flags; - _timeOfDeath = content.TimeOfDeath; - _restoreTable = content.RestoreTable; - _decayTimer = new InternalTimer(this, content.DecayTimerDelay); - _decayTimer.Start(); - _looters = content.Looters; - _killer = content.Killer; - _aggressors = content.Aggressors; - _owner = content.Owner; - _corpseName = content.CorpseName; - _accessLevel = content.AccessLevel; - _guild = content.Guild; - _equipItems = content.EquipItems; - if (content.Hair != null) - { - _hairItemId = content.Hair.ItemId; - _hairHue = content.Hair.Hue; - } - - if (content.FacialHair != null) - { - _facialHairItemId = content.FacialHair.ItemId; - _facialHairHue = content.FacialHair.Hue; - } - } - - // Folded Murderer bool field into CorpseFlag.Murderer - private void MigrateFrom(V15Content content) - { - _restoreEquip = content.RestoreEquip; - _flags = content.Flags; - if (content.Murderer) - { - _flags |= CorpseFlag.Murderer; - } - _timeOfDeath = content.TimeOfDeath; - _restoreTable = content.RestoreTable; - _decayTimer = new InternalTimer(this, content.DecayTimerDelay); - _decayTimer.Start(); - _looters = content.Looters; - _killer = content.Killer; - _aggressors = content.Aggressors; - _owner = content.Owner; - _corpseName = content.CorpseName; - _accessLevel = content.AccessLevel; - _guild = content.Guild; - _equipItems = content.EquipItems; - if (content.Hair != null) - { - _hairItemId = content.Hair.ItemId; - _hairHue = content.Hair.Hue; - } - - if (content.FacialHair != null) - { - _facialHairItemId = content.FacialHair.ItemId; - _facialHairHue = content.FacialHair.Hue; - } - } - - // Replaced int Kills snapshot with bool Murderer snapshot - private void MigrateFrom(V14Content content) - { - _restoreEquip = content.RestoreEquip; - _flags = content.Flags; - if (content.Kills >= 5) - { - _flags |= CorpseFlag.Murderer; - } - _timeOfDeath = content.TimeOfDeath; - _restoreTable = content.RestoreTable; - _decayTimer = new InternalTimer(this, content.DecayTimerDelay); - _decayTimer.Start(); - _looters = content.Looters; - _killer = content.Killer; - _aggressors = content.Aggressors; - _owner = content.Owner; - _corpseName = content.CorpseName; - _accessLevel = content.AccessLevel; - _guild = content.Guild; - _equipItems = content.EquipItems; - if (content.Hair != null) - { - _hairItemId = content.Hair.ItemId; - _hairHue = content.Hair.Hue; - } - - if (content.FacialHair != null) - { - _facialHairItemId = content.FacialHair.ItemId; - _facialHairHue = content.FacialHair.Hue; - } - } - - // Added corpse hair and corpse facial hair - private void MigrateFrom(V13Content content) - { - _restoreEquip = content.RestoreEquip; - _flags = content.Flags; - if (content.Kills >= 5) - { - _flags |= CorpseFlag.Murderer; - } - _timeOfDeath = content.TimeOfDeath; - _restoreTable = content.RestoreTable; - _decayTimer = new InternalTimer(this, content.DecayTimerDelay); - _decayTimer.Start(); - _looters = content.Looters; - _killer = content.Killer; - _aggressors = content.Aggressors; - _owner = content.Owner; - _corpseName = content.CorpseName; - _accessLevel = content.AccessLevel; - _guild = content.Guild; - _equipItems = content.EquipItems; - } -} diff --git a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs index 04e9806c9..8ea7d1319 100644 --- a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs +++ b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs @@ -6,7 +6,6 @@ using Server.ContextMenus; using Server.Engines.PartySystem; using Server.Engines.Quests.Doom; using Server.Engines.Quests.Haven; -using Server.Engines.PlayerMurderSystem; using Server.Guilds; using Server.Misc; using Server.Mobiles; @@ -62,31 +61,10 @@ public enum CorpseFlag /// /// Has this corpse been self looted? /// - SelfLooted = 0x00000080, - - /// - /// Was the owner a murderer when he died? - /// - Murderer = 0x00000100, - - /// - /// Was the owner a BaseCreature? Snapshot at death so notoriety still resolves after the - /// owner Mobile is deleted (BaseCreatures are deleted on death; the reference is null after restart). - /// - OwnerWasBaseCreature = 0x00000200, - - /// - /// Was the owner a summoned creature? - /// - OwnerWasSummoned = 0x00000400, - - /// - /// Was the owner an animated dead creature? - /// - OwnerWasAnimatedDead = 0x00000800 + SelfLooted = 0x00000080 } -[SerializationGenerator(19, false)] +[SerializationGenerator(14, false)] public partial class Corpse : Container, ICarvable { public static readonly TimeSpan MonsterLootRightSacrifice = TimeSpan.FromMinutes(2.0); @@ -106,7 +84,7 @@ public partial class Corpse : Container, ICarvable [SerializableField(1)] private CorpseFlag _flags; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(2)] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _timeOfDeath; @@ -114,10 +92,11 @@ public partial class Corpse : Container, ICarvable [SerializableField(3, getter: "private", setter: "private")] private Dictionary _restoreTable; + [TimerDrift] [SerializableField(4, getter: "private", setter: "private")] - [DeserializeTimer(nameof(DeserializeDecayTimer))] private Timer _decayTimer; + [DeserializeTimerField(4)] private void DeserializeDecayTimer(TimeSpan delay) => BeginDecay(delay); [SerializableField(5, setter: "private")] @@ -130,7 +109,7 @@ public partial class Corpse : Container, ICarvable [SerializableField(7, setter: "private")] private List _aggressors; - [SerializableField(8, setter: "protected")] + [SerializableField(8, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster)] private Mobile _owner; @@ -145,65 +124,32 @@ public partial class Corpse : Container, ICarvable [SerializableField(11, setter: "private")] private Guild _guild; - [SerializableField(12, setter: "private")] + [SerializableField(12)] [SerializedCommandProperty(AccessLevel.GameMaster)] - private List _equipItems; + private int _kills; [SerializableField(13, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _hairItemId; + private List _equipItems; + [CanBeNull] [SerializableField(14, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _hairHue; + private VirtualHairInfo _hair; + [CanBeNull] [SerializableField(15, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _facialHairItemId; - - [SerializableField(16, setter: "private")] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _facialHairHue; - - private Serial _hairSerial; - private Serial _facialHairSerial; - - public Serial HairSerial - { - get - { - if (_hairItemId > 0 && _hairSerial == Serial.Zero) - { - _hairSerial = World.NewVirtual; - } - - return _hairSerial; - } - } - - public Serial FacialHairSerial - { - get - { - if (_facialHairItemId > 0 && _facialHairSerial == Serial.Zero) - { - _facialHairSerial = World.NewVirtual; - } - - return _facialHairSerial; - } - } + private VirtualHairInfo _facialHair; // Why was this public? // public override bool IsPublicContainer => true; - public Corpse(Mobile owner, List equipItems) : this(owner, 0, 0, 0, 0, equipItems) + public Corpse(Mobile owner, List equipItems) : this(owner, null, null, equipItems) { } - public Corpse( - Mobile owner, int hairItemId, int hairHue, int facialHairItemId, int facialHairHue, List equipItems - ) + public Corpse(Mobile owner, VirtualHairInfo hair, VirtualHairInfo facialHair, List equipItems) : base(0x2006) { // To suppress console warnings, stackable must be true @@ -224,30 +170,17 @@ public partial class Corpse : Container, ICarvable _accessLevel = owner.AccessLevel; _guild = owner.Guild as Guild; - SetFlag(CorpseFlag.Murderer, owner.Murderer); + _kills = owner.Kills; SetFlag(CorpseFlag.Criminal, owner.Criminal); - var ownerBaseCreature = owner as BaseCreature; - - // Snapshot owner type & state. BaseCreatures are deleted after death, so the owner mobile - // reference becomes null after restart - notoriety must be derivable without it. - if (ownerBaseCreature != null) + if (hair?.ItemId > 0) { - SetFlag(CorpseFlag.OwnerWasBaseCreature, true); - SetFlag(CorpseFlag.OwnerWasSummoned, ownerBaseCreature.Summoned); - SetFlag(CorpseFlag.OwnerWasAnimatedDead, ownerBaseCreature.IsAnimatedDead); + _hair = new VirtualHairInfo(hair.ItemId, hair.Hue); } - if (hairItemId > 0) + if (facialHair?.ItemId > 0) { - _hairItemId = hairItemId; - _hairHue = hairHue; - } - - if (facialHairItemId > 0) - { - _facialHairItemId = facialHairItemId; - _facialHairHue = facialHairHue; + _facialHair = new VirtualHairInfo(facialHair.ItemId, facialHair.Hue); } // This corpse does not turn to bones if: the owner is not a player @@ -258,6 +191,8 @@ public partial class Corpse : Container, ICarvable _aggressors = new List(owner.Aggressors.Count + owner.Aggressed.Count); + var isBaseCreature = owner is BaseCreature; + var lastTime = TimeSpan.MaxValue; for (var i = 0; i < owner.Aggressors.Count; ++i) @@ -270,7 +205,7 @@ public partial class Corpse : Container, ICarvable lastTime = Core.Now - info.LastCombatTime; } - if (ownerBaseCreature == null && !info.CriminalAggression) + if (!isBaseCreature && !info.CriminalAggression) { _aggressors.Add(info.Attacker); } @@ -286,21 +221,23 @@ public partial class Corpse : Container, ICarvable lastTime = Core.Now - info.LastCombatTime; } - if (ownerBaseCreature == null) + if (!isBaseCreature) { _aggressors.Add(info.Defender); } } - if (ownerBaseCreature != null) + if (isBaseCreature) { - var master = ownerBaseCreature.GetMaster(); + var bc = (BaseCreature)owner; + + var master = bc.GetMaster(); if (master != null) { _aggressors.Add(master); } - var rights = BaseCreature.GetLootingRights(ownerBaseCreature.DamageEntries, ownerBaseCreature.HitsMax); + var rights = BaseCreature.GetLootingRights(bc.DamageEntries, bc.HitsMax); for (var i = 0; i < rights.Count; ++i) { var ds = rights[i]; @@ -317,6 +254,26 @@ public partial class Corpse : Container, ICarvable DevourCorpse(); } + // Added corpse hair and corpse facial hair + private void MigrateFrom(V13Content content) + { + _restoreEquip = content.RestoreEquip; + _flags = content.Flags; + _timeOfDeath = content.TimeOfDeath; + _restoreTable = content.RestoreTable; + _decayTimer = new InternalTimer(this, content.DecayTimerDelay); + _decayTimer.Start(); + _looters = content.Looters; + _killer = content.Killer; + _aggressors = content.Aggressors; + _owner = content.Owner; + _corpseName = content.CorpseName; + _accessLevel = content.AccessLevel; + _guild = content.Guild; + _kills = content.Kills; + _equipItems = content.EquipItems; + } + [CommandProperty(AccessLevel.GameMaster)] public virtual bool InstancedCorpse => Core.SE && Core.Now < TimeOfDeath + InstancedCorpseTime; @@ -376,22 +333,6 @@ public partial class Corpse : Container, ICarvable set => SetFlag(CorpseFlag.Criminal, value); } - [CommandProperty(AccessLevel.GameMaster)] - public bool Murderer - { - get => GetFlag(CorpseFlag.Murderer); - set => SetFlag(CorpseFlag.Murderer, value); - } - - [CommandProperty(AccessLevel.GameMaster)] - public bool OwnerWasBaseCreature => GetFlag(CorpseFlag.OwnerWasBaseCreature); - - [CommandProperty(AccessLevel.GameMaster)] - public bool OwnerWasSummoned => GetFlag(CorpseFlag.OwnerWasSummoned); - - [CommandProperty(AccessLevel.GameMaster)] - public bool OwnerWasAnimatedDead => GetFlag(CorpseFlag.OwnerWasAnimatedDead); - public override bool DisplaysContent => false; public void Carve(Mobile from, Item item) @@ -425,14 +366,7 @@ public partial class Corpse : Container, ICarvable new LeftArm().MoveToWorld(Location, Map); new RightLeg().MoveToWorld(Location, Map); new RightArm().MoveToWorld(Location, Map); - var head = new Head(dead.Name); - - if (PlayerMurderSystem.BountiesEnabled && dead is PlayerMobile bountyTarget) - { - head.BountyTarget = bountyTarget; - } - - head.MoveToWorld(Location, Map); + new Head(dead.Name).MoveToWorld(Location, Map); SetFlag(CorpseFlag.Carved, true); @@ -445,11 +379,6 @@ public partial class Corpse : Container, ICarvable if (IsCriminalAction(from)) { from.CriminalAction(true); - Titles.AwardKarma(from, -70, true); - } - else - { - Titles.AwardKarma(from, -20, true); } } else if (dead is BaseCreature creature) @@ -599,22 +528,8 @@ public partial class Corpse : Container, ICarvable public static Container Mobile_CreateCorpseHandler(Mobile owner, List initialContent, List equipItems) { var c = owner is MilitiaFighter - ? new MilitiaFighterCorpse( - owner, - owner.HairItemID, - owner.HairHue, - owner.FacialHairItemID, - owner.FacialHairHue, - equipItems - ) - : new Corpse( - owner, - owner.HairItemID, - owner.HairHue, - owner.FacialHairItemID, - owner.FacialHairHue, - equipItems - ); + ? new MilitiaFighterCorpse(owner, owner.Hair, owner.FacialHair, equipItems) + : new Corpse(owner, owner.Hair, owner.FacialHair, equipItems); owner.Corpse = c; @@ -716,10 +631,7 @@ public partial class Corpse : Container, ICarvable _accessLevel = (AccessLevel)reader.ReadInt(); reader.ReadInt(); // guild reserve - if (reader.ReadInt() >= 5) - { - _flags |= CorpseFlag.Murderer; - } + _kills = reader.ReadInt(); _equipItems = reader.ReadEntityList(); } @@ -912,28 +824,25 @@ public partial class Corpse : Container, ICarvable } var pack = from.Backpack; - using var items = PooledRefList.Create(128); if (RestoreEquip != null && pack != null) { - items.AddRange(pack.Items); + var packItems = new List(pack.Items); // Only items in the top-level pack are re-equipped - // Only items in the top-level pack are re-equipped - for (var i = 0; i < items.Count; i++) + for (var i = 0; i < packItems.Count; i++) { - var packItem = items[i]; + var packItem = packItems[i]; if (RestoreEquip.Contains(packItem) && packItem.Movable) { from.EquipItem(packItem); } } - - items.Clear(); } + var items = new List(Items); + var didntFit = false; - items.AddRange(Items); for (var i = 0; !didntFit && i < items.Count; ++i) { @@ -1003,7 +912,7 @@ public partial class Corpse : Container, ICarvable var obj = qs.FindObjective(); if (obj?.CorpseWithBone == this && (!obj.Completed || UzeraanTurmoilQuest.HasLostDaemonBone(player))) { - var bone = new QuestDaemonBone(); + Item bone = new QuestDaemonBone(); if (player.PlaceInBackpack(bone)) { diff --git a/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs b/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs index 4a629320d..2faa5d18b 100644 --- a/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs +++ b/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CorpsePackets.cs * * * @@ -49,16 +49,16 @@ public static class CorpsePackets if (beheld.Owner != null) { - if (beheld.HairItemId > 0) + if (beheld.Hair?.ItemId > 0) { writer.Write((byte)(Layer.Hair + 1)); - writer.Write(beheld.HairSerial); + writer.Write(beheld.Hair.VirtualSerial); } - if (beheld.FacialHairItemId > 0) + if (beheld.FacialHair?.ItemId > 0) { writer.Write((byte)(Layer.FacialHair + 1)); - writer.Write(beheld.FacialHairSerial); + writer.Write(beheld.FacialHair.VirtualSerial); } } @@ -76,14 +76,16 @@ public static class CorpsePackets } var list = beheld.EquipItems; + var hair = beheld.Hair; + var facialHair = beheld.FacialHair; var count = list.Count; - if (beheld.HairItemId > 0) + if (hair != null) { count++; } - if (beheld.FacialHairItemId > 0) + if (facialHair != null) { count++; } @@ -119,10 +121,10 @@ public static class CorpsePackets if (beheld.Owner != null) { - if (beheld.HairItemId > 0) + if (hair?.ItemId > 0) { - writer.Write(beheld.HairSerial); - writer.Write((ushort)beheld.HairItemId); + writer.Write(hair.VirtualSerial); + writer.Write((ushort)hair.ItemId); writer.Write((byte)0); // signed, itemID offset writer.Write((ushort)1); writer.Write(0); // X/Y @@ -131,15 +133,15 @@ public static class CorpsePackets writer.Write((byte)0); // Grid Location? } writer.Write(beheld.Serial); - writer.Write((ushort)beheld.HairHue); + writer.Write((ushort)hair.Hue); ++written; } - if (beheld.FacialHairItemId > 0) + if (facialHair?.ItemId > 0) { - writer.Write(beheld.FacialHairSerial); - writer.Write((ushort)beheld.FacialHairItemId); + writer.Write(facialHair.VirtualSerial); + writer.Write((ushort)facialHair.ItemId); writer.Write((byte)0); // signed, itemID offset writer.Write((ushort)1); writer.Write(0); // X/Y @@ -148,7 +150,7 @@ public static class CorpsePackets writer.Write((byte)0); // Grid Location? } writer.Write(beheld.Serial); - writer.Write((ushort)beheld.FacialHairHue); + writer.Write((ushort)facialHair.Hue); ++written; } diff --git a/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs b/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs index 17267a6cc..ead812e2f 100644 --- a/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs +++ b/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs @@ -3,25 +3,18 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(3, false)] +[SerializationGenerator(2, false)] public partial class DecayedCorpse : Container { private static TimeSpan _defaultDecayTime = TimeSpan.FromMinutes(7.0); + [TimerDrift] [SerializableField(0, getter: "private", setter: "private")] - [DeserializeTimer(nameof(DeserializeDecayTimer))] private Timer _decayTimer; + [DeserializeTimerField(0)] private void DeserializeDecayTimer(TimeSpan delay) => BeginDecay(delay); - private void MigrateFrom(V2Content content) - { - if (content.DecayTimerDelay != TimeSpan.MinValue) - { - DeserializeDecayTimer(content.DecayTimerDelay); - } - } - public DecayedCorpse(string name) : base(Utility.Random(0xECA, 9)) { Movable = false; diff --git a/Projects/UOContent/Items/Misc/Corpses/LegacyHairInfo.cs b/Projects/UOContent/Items/Misc/Corpses/LegacyHairInfo.cs deleted file mode 100644 index 631f9b719..000000000 --- a/Projects/UOContent/Items/Misc/Corpses/LegacyHairInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Server.Items; - -// Reads the legacy on-disk VirtualHairInfo block ([int version][int itemId][int hue]). -// Used ONLY by Corpse save migration (V14/V15/V16). It has no runtime role. -public sealed class LegacyHairInfo -{ - public int ItemId { get; private set; } - public int Hue { get; private set; } - - public void Deserialize(IGenericReader reader) - { - reader.ReadInt(); // legacy serialization version - ItemId = reader.ReadInt(); - Hue = reader.ReadInt(); - } -} diff --git a/Projects/UOContent/Items/Misc/EffectItem.cs b/Projects/UOContent/Items/Misc/EffectItem.cs index 8cc1b7db5..76c72d420 100644 --- a/Projects/UOContent/Items/Misc/EffectItem.cs +++ b/Projects/UOContent/Items/Misc/EffectItem.cs @@ -15,8 +15,6 @@ public partial class EffectItem : Item public override bool Decays => true; - public override bool SkipSerialization => true; - public static EffectItem Create(Point3D p, Map map, TimeSpan duration) { EffectItem item = null; @@ -48,6 +46,12 @@ public partial class EffectItem : Item public void BeginFree(TimeSpan duration) => new FreeTimer(this, duration).Start(); + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } + private class FreeTimer : Timer { private EffectItem _item; diff --git a/Projects/UOContent/Items/Misc/ExecutionersCap.cs b/Projects/UOContent/Items/Misc/ExecutionersCap.cs index d2e1ae395..69ba5ffe1 100644 --- a/Projects/UOContent/Items/Misc/ExecutionersCap.cs +++ b/Projects/UOContent/Items/Misc/ExecutionersCap.cs @@ -6,9 +6,5 @@ namespace Server.Items; public partial class ExecutionersCap : Item { [Constructible] - public ExecutionersCap() : base(0xF83) - { - } - - public override double DefaultWeight => 1.0; + public ExecutionersCap() : base(0xF83) => Weight = 1.0; } diff --git a/Projects/UOContent/Items/Misc/Firebomb.cs b/Projects/UOContent/Items/Misc/Firebomb.cs index fe8f8a656..89545a8ad 100644 --- a/Projects/UOContent/Items/Misc/Firebomb.cs +++ b/Projects/UOContent/Items/Misc/Firebomb.cs @@ -18,9 +18,11 @@ public partial class Firebomb : Item private HashSet _users; [Constructible] - public Firebomb(int itemID = 0x99B) : base(itemID) => Hue = 1260; - - public override double DefaultWeight => 2.0; + public Firebomb(int itemID = 0x99B) : base(itemID) + { + Weight = 2.0; + Hue = 1260; + } public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Misc/FlippableAddonAttribute.cs b/Projects/UOContent/Items/Misc/FlippableAddonAttribute.cs index 1d929904b..2a76571ea 100644 --- a/Projects/UOContent/Items/Misc/FlippableAddonAttribute.cs +++ b/Projects/UOContent/Items/Misc/FlippableAddonAttribute.cs @@ -6,7 +6,7 @@ namespace Server.Items; [AttributeUsage(AttributeTargets.Class)] public class FlippableAddonAttribute : Attribute { - private const string MethodName = "Flip"; + private static readonly string m_MethodName = "Flip"; private static readonly Type[] m_Params = { @@ -26,7 +26,7 @@ public class FlippableAddonAttribute : Attribute try { - var flipMethod = addon.GetType().GetMethod(MethodName, m_Params); + var flipMethod = addon.GetType().GetMethod(m_MethodName, m_Params); if (flipMethod == null) { diff --git a/Projects/UOContent/Items/Misc/GlassItems.cs b/Projects/UOContent/Items/Misc/GlassItems.cs index 6b86a8de2..9f3bbf070 100644 --- a/Projects/UOContent/Items/Misc/GlassItems.cs +++ b/Projects/UOContent/Items/Misc/GlassItems.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class SmallFlask : Item { [Constructible] - public SmallFlask() : base(0x182E) - { - } - - public override double DefaultWeight => 1.0; + public SmallFlask() : base(0x182E) => Weight = 1.0; } [Flippable(0x182A, 0x182B, 0x182C, 0x182D)] @@ -19,11 +15,7 @@ public partial class SmallFlask : Item public partial class MediumFlask : Item { [Constructible] - public MediumFlask() : base(0x182A) - { - } - - public override double DefaultWeight => 1.0; + public MediumFlask() : base(0x182A) => Weight = 1.0; } [Flippable(0x183B, 0x183C, 0x183D)] @@ -31,11 +23,7 @@ public partial class MediumFlask : Item public partial class LargeFlask : Item { [Constructible] - public LargeFlask() : base(0x183B) - { - } - - public override double DefaultWeight => 1.0; + public LargeFlask() : base(0x183B) => Weight = 1.0; } [Flippable(0x1832, 0x1833, 0x1834, 0x1835, 0x1836, 0x1837)] @@ -43,11 +31,7 @@ public partial class LargeFlask : Item public partial class CurvedFlask : Item { [Constructible] - public CurvedFlask() : base(0x1832) - { - } - - public override double DefaultWeight => 1.0; + public CurvedFlask() : base(0x1832) => Weight = 1.0; } [Flippable(0x1838, 0x1839, 0x183A)] @@ -55,11 +39,7 @@ public partial class CurvedFlask : Item public partial class LongFlask : Item { [Constructible] - public LongFlask() : base(0x1838) - { - } - - public override double DefaultWeight => 1.0; + public LongFlask() : base(0x1838) => Weight = 1.0; } [Flippable(0x1810, 0x1811)] @@ -67,77 +47,49 @@ public partial class LongFlask : Item public partial class SpinningHourglass : Item { [Constructible] - public SpinningHourglass() : base(0x1810) - { - } - - public override double DefaultWeight => 1.0; + public SpinningHourglass() : base(0x1810) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class GreenBottle : Item { [Constructible] - public GreenBottle() : base(0x0EFB) - { - } - - public override double DefaultWeight => 1.0; + public GreenBottle() : base(0x0EFB) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class RedBottle : Item { [Constructible] - public RedBottle() : base(0x0EFC) - { - } - - public override double DefaultWeight => 1.0; + public RedBottle() : base(0x0EFC) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class SmallBrownBottle : Item { [Constructible] - public SmallBrownBottle() : base(0x0EFD) - { - } - - public override double DefaultWeight => 1.0; + public SmallBrownBottle() : base(0x0EFD) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class SmallGreenBottle : Item { [Constructible] - public SmallGreenBottle() : base(0x0F01) - { - } - - public override double DefaultWeight => 1.0; + public SmallGreenBottle() : base(0x0F01) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class SmallVioletBottle : Item { [Constructible] - public SmallVioletBottle() : base(0x0F02) - { - } - - public override double DefaultWeight => 1.0; + public SmallVioletBottle() : base(0x0F02) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class TinyYellowBottle : Item { [Constructible] - public TinyYellowBottle() : base(0x0F03) - { - } - - public override double DefaultWeight => 1.0; + public TinyYellowBottle() : base(0x0F03) => Weight = 1.0; } // remove @@ -145,275 +97,175 @@ public partial class TinyYellowBottle : Item public partial class SmallBlueFlask : Item { [Constructible] - public SmallBlueFlask() : base(0x182A) - { - } - - public override double DefaultWeight => 1.0; + public SmallBlueFlask() : base(0x182A) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class SmallYellowFlask : Item { [Constructible] - public SmallYellowFlask() : base(0x182B) - { - } - - public override double DefaultWeight => 1.0; + public SmallYellowFlask() : base(0x182B) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class SmallRedFlask : Item { [Constructible] - public SmallRedFlask() : base(0x182C) - { - } - - public override double DefaultWeight => 1.0; + public SmallRedFlask() : base(0x182C) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class SmallEmptyFlask : Item { [Constructible] - public SmallEmptyFlask() : base(0x182D) - { - } - - public override double DefaultWeight => 1.0; + public SmallEmptyFlask() : base(0x182D) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class YellowBeaker : Item { [Constructible] - public YellowBeaker() : base(0x182E) - { - } - - public override double DefaultWeight => 1.0; + public YellowBeaker() : base(0x182E) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class RedBeaker : Item { [Constructible] - public RedBeaker() : base(0x182F) - { - } - - public override double DefaultWeight => 1.0; + public RedBeaker() : base(0x182F) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class BlueBeaker : Item { [Constructible] - public BlueBeaker() : base(0x1830) - { - } - - public override double DefaultWeight => 1.0; + public BlueBeaker() : base(0x1830) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class GreenBeaker : Item { [Constructible] - public GreenBeaker() : base(0x1831) - { - } - - public override double DefaultWeight => 1.0; + public GreenBeaker() : base(0x1831) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class EmptyCurvedFlaskW : Item { [Constructible] - public EmptyCurvedFlaskW() : base(0x1832) - { - } - - public override double DefaultWeight => 1.0; + public EmptyCurvedFlaskW() : base(0x1832) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class RedCurvedFlask : Item { [Constructible] - public RedCurvedFlask() : base(0x1833) - { - } - - public override double DefaultWeight => 1.0; + public RedCurvedFlask() : base(0x1833) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class LtBlueCurvedFlask : Item { [Constructible] - public LtBlueCurvedFlask() : base(0x1834) - { - } - - public override double DefaultWeight => 1.0; + public LtBlueCurvedFlask() : base(0x1834) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class EmptyCurvedFlaskE : Item { [Constructible] - public EmptyCurvedFlaskE() : base(0x1835) - { - } - - public override double DefaultWeight => 1.0; + public EmptyCurvedFlaskE() : base(0x1835) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class BlueCurvedFlask : Item { [Constructible] - public BlueCurvedFlask() : base(0x1836) - { - } - - public override double DefaultWeight => 1.0; + public BlueCurvedFlask() : base(0x1836) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class GreenCurvedFlask : Item { [Constructible] - public GreenCurvedFlask() : base(0x1837) - { - } - - public override double DefaultWeight => 1.0; + public GreenCurvedFlask() : base(0x1837) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class RedRibbedFlask : Item { [Constructible] - public RedRibbedFlask() : base(0x1838) - { - } - - public override double DefaultWeight => 1.0; + public RedRibbedFlask() : base(0x1838) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class VioletRibbedFlask : Item { [Constructible] - public VioletRibbedFlask() : base(0x1839) - { - } - - public override double DefaultWeight => 1.0; + public VioletRibbedFlask() : base(0x1839) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class EmptyRibbedFlask : Item { [Constructible] - public EmptyRibbedFlask() : base(0x183A) - { - } - - public override double DefaultWeight => 1.0; + public EmptyRibbedFlask() : base(0x183A) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class LargeYellowFlask : Item { [Constructible] - public LargeYellowFlask() : base(0x183B) - { - } - - public override double DefaultWeight => 1.0; + public LargeYellowFlask() : base(0x183B) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class LargeVioletFlask : Item { [Constructible] - public LargeVioletFlask() : base(0x183C) - { - } - - public override double DefaultWeight => 1.0; + public LargeVioletFlask() : base(0x183C) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class LargeEmptyFlask : Item { [Constructible] - public LargeEmptyFlask() : base(0x183D) - { - } - - public override double DefaultWeight => 1.0; + public LargeEmptyFlask() : base(0x183D) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class AniRedRibbedFlask : Item { [Constructible] - public AniRedRibbedFlask() : base(0x183E) - { - } - - public override double DefaultWeight => 1.0; + public AniRedRibbedFlask() : base(0x183E) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class AniLargeVioletFlask : Item { [Constructible] - public AniLargeVioletFlask() : base(0x1841) - { - } - - public override double DefaultWeight => 1.0; + public AniLargeVioletFlask() : base(0x1841) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class AniSmallBlueFlask : Item { [Constructible] - public AniSmallBlueFlask() : base(0x1844) - { - } - - public override double DefaultWeight => 1.0; + public AniSmallBlueFlask() : base(0x1844) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class SmallBlueBottle : Item { [Constructible] - public SmallBlueBottle() : base(0x1847) - { - } - - public override double DefaultWeight => 1.0; + public SmallBlueBottle() : base(0x1847) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class SmallGreenBottle2 : Item { [Constructible] - public SmallGreenBottle2() : base(0x1848) - { - } - - public override double DefaultWeight => 1.0; + public SmallGreenBottle2() : base(0x1848) => Weight = 1.0; } [Flippable(0x185B, 0x185C)] @@ -421,11 +273,7 @@ public partial class SmallGreenBottle2 : Item public partial class EmptyVialsWRack : Item { [Constructible] - public EmptyVialsWRack() : base(0x185B) - { - } - - public override double DefaultWeight => 1.0; + public EmptyVialsWRack() : base(0x185B) => Weight = 1.0; } [Flippable(0x185D, 0x185E)] @@ -433,53 +281,33 @@ public partial class EmptyVialsWRack : Item public partial class FullVialsWRack : Item { [Constructible] - public FullVialsWRack() : base(0x185D) - { - } - - public override double DefaultWeight => 1.0; + public FullVialsWRack() : base(0x185D) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class EmptyVial : Item { [Constructible] - public EmptyVial() : base(0x0E24) - { - } - - public override double DefaultWeight => 1.0; + public EmptyVial() : base(0x0E24) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class HourglassAni : Item { [Constructible] - public HourglassAni() : base(0x1811) - { - } - - public override double DefaultWeight => 1.0; + public HourglassAni() : base(0x1811) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class Hourglass : Item { [Constructible] - public Hourglass() : base(0x1810) - { - } - - public override double DefaultWeight => 1.0; + public Hourglass() : base(0x1810) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class TinyRedBottle : Item { [Constructible] - public TinyRedBottle() : base(0x0F04) - { - } - - public override double DefaultWeight => 1.0; + public TinyRedBottle() : base(0x0F04) => Weight = 1.0; } diff --git a/Projects/UOContent/Items/Misc/HairDye.cs b/Projects/UOContent/Items/Misc/HairDye.cs index c32e1aa4d..a66bcde2e 100644 --- a/Projects/UOContent/Items/Misc/HairDye.cs +++ b/Projects/UOContent/Items/Misc/HairDye.cs @@ -9,11 +9,7 @@ namespace Server.Items; public partial class HairDye : Item { [Constructible] - public HairDye() : base(0xEFF) - { - } - - public override double DefaultWeight => 1.0; + public HairDye() : base(0xEFF) => Weight = 1.0; public override int LabelNumber => 1041060; // Hair Dye @@ -30,65 +26,58 @@ public partial class HairDye : Item } } -public class HairDyeGump : StaticGump +public class HairDyeGump : Gump { - private static readonly (int HueStart, int HueCount)[] _entries = - [ - (1602, 26), - (1628, 27), - (1502, 32), - (1302, 32), - (1402, 32), - (1202, 24), - (2402, 29), - (2213, 6), - (1102, 8), - (1110, 8), - (1118, 16), - (1134, 16) - ]; + private static HairDyeEntry[] _entries = + { + new("*****", 1602, 26), + new("*****", 1628, 27), + new("*****", 1502, 32), + new("*****", 1302, 32), + new("*****", 1402, 32), + new("*****", 1202, 24), + new("*****", 2402, 29), + new("*****", 2213, 6), + new("*****", 1102, 8), + new("*****", 1110, 8), + new("*****", 1118, 16), + new("*****", 1134, 16) + }; - private readonly HairDye _hairDye; + private HairDye _hairDye; public override bool Singleton => true; - public HairDyeGump(HairDye dye) : base(50, 50) => _hairDye = dye; - - protected override void BuildLayout(ref StaticGumpBuilder builder) + public HairDyeGump(HairDye dye) : base(50, 50) { - builder.AddPage(); + _hairDye = dye; - builder.AddBackground(100, 10, 350, 355, 2600); - builder.AddBackground(120, 54, 110, 270, 5100); + AddPage(0); - builder.AddHtmlLocalized(70, 25, 400, 35, 1011013); //
Hair Color Selection Menu
+ AddBackground(100, 10, 350, 355, 2600); + AddBackground(120, 54, 110, 270, 5100); - builder.AddButton(149, 328, 4005, 4007, 1); - builder.AddHtmlLocalized(185, 329, 250, 35, 1011014); // Dye my hair this color! + AddHtmlLocalized(70, 25, 400, 35, 1011013); //
Hair Color Selection Menu
- ReadOnlySpan<(int HueStart, int HueCount)> entries = _entries; - for (var i = 0; i < entries.Length; ++i) + AddButton(149, 328, 4005, 4007, 1); + AddHtmlLocalized(185, 329, 250, 35, 1011014); // Dye my hair this color! + + for (var i = 0; i < _entries.Length; ++i) { - var y = 59 + i * 22; - builder.AddLabel(130, y, entries[i].HueStart - 1, "*****"); - builder.AddButton(207, y + 1, 5224, 5224, 0, GumpButtonType.Page, i + 1); + AddLabel(130, 59 + i * 22, _entries[i].HueStart - 1, _entries[i].Name); + AddButton(207, 60 + i * 22, 5224, 5224, 0, GumpButtonType.Page, i + 1); } - for (var i = 0; i < entries.Length; ++i) + for (var i = 0; i < _entries.Length; ++i) { - var (hueStart, hueCount) = entries[i]; + var e = _entries[i]; - builder.AddPage(i + 1); - var switchId = i * 100; + AddPage(i + 1); - for (var j = 0; j < hueCount; ++j) + for (var j = 0; j < e.HueCount; ++j) { - var page = Math.DivRem(j, 16, out var row); - var x = 260 + page * 80; - var y = 52 + row * 17; - - builder.AddRadio(x, y, 210, 211, false, switchId + j); - builder.AddLabel(x + 18, y, hueStart + j - 1, "*****"); + AddLabel(278 + j / 16 * 80, 52 + j % 16 * 17, e.HueStart + j - 1, "*****"); + AddRadio(260 + j / 16 * 80, 52 + j % 16 * 17, 210, 211, false, i * 100 + j); } } } @@ -109,38 +98,54 @@ public class HairDyeGump : StaticGump return; } - if (info.ButtonID == 0 || switches.Length <= 0) + if (info.ButtonID != 0 && switches.Length > 0) + { + if (m.HairItemID == 0 && m.FacialHairItemID == 0) + { + m.SendLocalizedMessage(502623); // You have no hair to dye and cannot use this + } + else + { + // To prevent this from being exploited, the hue is abstracted into an internal list + var entryIndex = Math.DivRem(switches[0], 100, out var hueOffset); + + if (entryIndex >= 0 && entryIndex < _entries.Length) + { + var e = _entries[entryIndex]; + + if (hueOffset >= 0 && hueOffset < e.HueCount) + { + var hue = e.HueStart + hueOffset; + + m.HairHue = hue; + m.FacialHairHue = hue; + + m.SendLocalizedMessage(501199); // You dye your hair + _hairDye.Delete(); + m.PlaySound(0x4E); + } + } + } + } + else { m.SendLocalizedMessage(501200); // You decide not to dye your hair - return; - } - - if (m.HairItemID == 0 && m.FacialHairItemID == 0) - { - m.SendLocalizedMessage(502623); // You have no hair to dye and cannot use this - return; - } - - // To prevent this from being exploited, the hue is abstracted into an internal list - var entryIndex = Math.DivRem(switches[0], 100, out var hueOffset); - - if (entryIndex < 0 || entryIndex >= _entries.Length) - { - return; - } - - var e = _entries[entryIndex]; - - if (hueOffset >= 0 && hueOffset < e.HueCount) - { - var hue = e.HueStart + hueOffset; - - m.HairHue = hue; - m.FacialHairHue = hue; - - m.SendLocalizedMessage(501199); // You dye your hair - _hairDye.Delete(); - m.PlaySound(0x4E); } } + + private class HairDyeEntry + { + public HairDyeEntry(string name, int hueStart, int hueCount) + { + Name = name; + HueStart = hueStart; + HueCount = hueCount; + } + + public string Name { get; } + + public int HueStart { get; } + + public int HueCount { get; } + } } diff --git a/Projects/UOContent/Items/Misc/InteriorDecorator.cs b/Projects/UOContent/Items/Misc/InteriorDecorator.cs index efb8c1840..9c197685e 100644 --- a/Projects/UOContent/Items/Misc/InteriorDecorator.cs +++ b/Projects/UOContent/Items/Misc/InteriorDecorator.cs @@ -22,11 +22,10 @@ public partial class InteriorDecorator : Item [Constructible] public InteriorDecorator() : base(0xFC1) { + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - [CommandProperty(AccessLevel.GameMaster)] public DecorateCommand Command { @@ -57,9 +56,9 @@ public partial class InteriorDecorator : Item return; } - if (!from.HasGump()) + if (!from.HasGump()) { - InteriorDecoratorGump.DisplayTo(from, this); + from.SendGump(new InternalGump(this)); } if (m_Command != DecorateCommand.None) @@ -79,36 +78,24 @@ public partial class InteriorDecorator : Item return false; } - private class InteriorDecoratorGump : DynamicGump + private class InternalGump : Gump { - private readonly InteriorDecorator _decorator; + private readonly InteriorDecorator m_Decorator; - public override bool Singleton => true; - - private InteriorDecoratorGump(InteriorDecorator decorator) : base(150, 50) => _decorator = decorator; - - public static void DisplayTo(Mobile from, InteriorDecorator decorator) + public InternalGump(InteriorDecorator decorator) : base(150, 50) { - if (from?.NetState == null || decorator?.Deleted != false) - { - return; - } + m_Decorator = decorator; - from.SendGump(new InteriorDecoratorGump(decorator)); - } + AddBackground(0, 0, 200, 200, 2600); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddBackground(0, 0, 200, 200, 2600); + AddButton(50, 45, decorator.Command == DecorateCommand.Turn ? 2154 : 2152, 2154, 1); + AddHtmlLocalized(90, 50, 70, 40, 1018323); // Turn - builder.AddButton(50, 45, _decorator.Command == DecorateCommand.Turn ? 2154 : 2152, 2154, 1); - builder.AddHtmlLocalized(90, 50, 70, 40, 1018323); // Turn + AddButton(50, 95, decorator.Command == DecorateCommand.Up ? 2154 : 2152, 2154, 2); + AddHtmlLocalized(90, 100, 70, 40, 1018324); // Up - builder.AddButton(50, 95, _decorator.Command == DecorateCommand.Up ? 2154 : 2152, 2154, 2); - builder.AddHtmlLocalized(90, 100, 70, 40, 1018324); // Up - - builder.AddButton(50, 145, _decorator.Command == DecorateCommand.Down ? 2154 : 2152, 2154, 3); - builder.AddHtmlLocalized(90, 150, 70, 40, 1018325); // Down + AddButton(50, 145, decorator.Command == DecorateCommand.Down ? 2154 : 2152, 2154, 3); + AddHtmlLocalized(90, 150, 70, 40, 1018325); // Down } public override void OnResponse(NetState sender, in RelayInfo info) @@ -123,9 +110,9 @@ public partial class InteriorDecorator : Item if (command != DecorateCommand.None) { - _decorator.Command = command; - DisplayTo(sender.Mobile, _decorator); - sender.Mobile.Target = new InternalTarget(_decorator); + m_Decorator.Command = command; + sender.Mobile.SendGump(new InternalGump(m_Decorator)); + sender.Mobile.Target = new InternalTarget(m_Decorator); } else { @@ -257,7 +244,7 @@ public partial class InteriorDecorator : Item { if (cancelType == TargetCancelType.Canceled) { - from.CloseGump(); + from.CloseGump(); } } diff --git a/Projects/UOContent/Items/Misc/Key.cs b/Projects/UOContent/Items/Misc/Key.cs index 2d8ae0fd8..4e16c55c1 100644 --- a/Projects/UOContent/Items/Misc/Key.cs +++ b/Projects/UOContent/Items/Misc/Key.cs @@ -47,13 +47,13 @@ public partial class Key : Item public Key(KeyType type, uint val = 0, Item link = null) : base((int)type) { + Weight = 1.0; + _maxRange = 3; _keyValue = val; _link = link; } - public override double DefaultWeight => 1.0; - public static uint RandomValue() => (uint)(0xFFFFFFFE * Utility.RandomDouble()) + 1; public static void RemoveKeys(Mobile m, uint keyValue) @@ -74,8 +74,7 @@ public partial class Key : Item return; } - using var queue = cont.EnumerateItems(); - foreach (var item in queue) + foreach (var item in cont.EnumerateItems()) { if (item is Key key) { diff --git a/Projects/UOContent/Items/Misc/KeyRing.cs b/Projects/UOContent/Items/Misc/KeyRing.cs index bae7d8d77..2a3c39759 100644 --- a/Projects/UOContent/Items/Misc/KeyRing.cs +++ b/Projects/UOContent/Items/Misc/KeyRing.cs @@ -15,11 +15,10 @@ public partial class KeyRing : Item [Constructible] public KeyRing() : base(0x1011) { + Weight = 1.0; // They seem to have no weight on OSI ?! _keys = new List(); } - public override double DefaultWeight => 1.0; - public override bool OnDragDrop(Mobile from, Item dropped) { if (!IsChildOf(from.Backpack)) diff --git a/Projects/UOContent/Items/Misc/LOSBlocker.cs b/Projects/UOContent/Items/Misc/LOSBlocker.cs index 3866cfa87..c439b2162 100644 --- a/Projects/UOContent/Items/Misc/LOSBlocker.cs +++ b/Projects/UOContent/Items/Misc/LOSBlocker.cs @@ -41,7 +41,7 @@ public partial class LOSBlocker : Item private void SendGMItem(NetState ns) { // GM Packet - var buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); int length; diff --git a/Projects/UOContent/Items/Misc/Labyrinth/MinotaurArtifact.cs b/Projects/UOContent/Items/Misc/Labyrinth/MinotaurArtifact.cs index f807a7d61..07b6824cf 100644 --- a/Projects/UOContent/Items/Misc/Labyrinth/MinotaurArtifact.cs +++ b/Projects/UOContent/Items/Misc/Labyrinth/MinotaurArtifact.cs @@ -8,10 +8,15 @@ public partial class MinotaurArtifact : Item [Constructible] public MinotaurArtifact() : base(Utility.RandomList(0xB46, 0xB48, 0x9ED)) { + if (ItemID == 0x9ED) + { + Weight = 30; + } + LootType = LootType.Blessed; Hue = 0x100; } public override int LabelNumber => 1074826; // Minotaur Artifact - public override double DefaultWeight => ItemID == 0x9ED ? 30.0 : 5.0; + public override double DefaultWeight => 5.0; } diff --git a/Projects/UOContent/Items/Misc/Moonstone.cs b/Projects/UOContent/Items/Misc/Moonstone.cs index 3dfcca30c..0a9a90903 100644 --- a/Projects/UOContent/Items/Misc/Moonstone.cs +++ b/Projects/UOContent/Items/Misc/Moonstone.cs @@ -20,9 +20,11 @@ public partial class Moonstone : Item private MoonstoneType _type; [Constructible] - public Moonstone(MoonstoneType type) : base(0xF8B) => _type = type; - - public override double DefaultWeight => 1.0; + public Moonstone(MoonstoneType type) : base(0xF8B) + { + Weight = 1.0; + _type = type; + } public override int LabelNumber => 1041490 + (int)_type; @@ -64,7 +66,7 @@ public partial class Moonstone : Item { mobile.SendLocalizedMessage(1049543); // You decide against traveling to Felucca while you are still young. } - else if (from.Murderer) + else if (from.Kills >= 5) { // The magic of the stone cannot be evoked by someone with blood on their hands. from.SendLocalizedMessage(1005402); diff --git a/Projects/UOContent/Items/Misc/MoonstoneGate.cs b/Projects/UOContent/Items/Misc/MoonstoneGate.cs index 313ca34e3..b0eb1d407 100644 --- a/Projects/UOContent/Items/Misc/MoonstoneGate.cs +++ b/Projects/UOContent/Items/Misc/MoonstoneGate.cs @@ -21,11 +21,9 @@ public partial class MoonstoneGate : Moongate Effects.PlaySound(loc, map, 0x20E); } - public override bool SkipSerialization => true; - public override void CheckGate(Mobile m, int range) { - if (m.Murderer) + if (m.Kills >= 5) { return; } @@ -41,7 +39,7 @@ public partial class MoonstoneGate : Moongate public override void UseGate(Mobile m) { - if (m.Murderer) + if (m.Kills >= 5) { return; } @@ -55,6 +53,12 @@ public partial class MoonstoneGate : Moongate } } + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } + private class InternalTimer : Timer { private Item _item; diff --git a/Projects/UOContent/Items/Misc/MorphItem.cs b/Projects/UOContent/Items/Misc/MorphItem.cs index 7e5a74d8b..c5dd9c0de 100644 --- a/Projects/UOContent/Items/Misc/MorphItem.cs +++ b/Projects/UOContent/Items/Misc/MorphItem.cs @@ -30,24 +30,20 @@ public partial class MorphItem : Item _outsideRange = outRange; } - [SerializableField(0, allowFieldChange: nameof(AllowOutsideRangeChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _outsideRange; - - private bool AllowOutsideRangeChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int OutsideRange { - value = Math.Clamp(value, 0, 18); - return true; + get => _outsideRange; + set => _outsideRange = Math.Clamp(value, 0, 18); } - [SerializableField(3, allowFieldChange: nameof(AllowInsideRangeChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _insideRange; - - private bool AllowInsideRangeChange(ref int value) + [SerializableProperty(3)] + [CommandProperty(AccessLevel.GameMaster)] + public int InsideRange { - value = Math.Clamp(value, 0, 18); - return true; + get => _insideRange; + set => _insideRange = Math.Clamp(value, 0, 18); } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Misc/OilFlask.cs b/Projects/UOContent/Items/Misc/OilFlask.cs index c2d621544..c890f0861 100644 --- a/Projects/UOContent/Items/Misc/OilFlask.cs +++ b/Projects/UOContent/Items/Misc/OilFlask.cs @@ -65,7 +65,7 @@ public partial class OilFlask : Item } } - Consume(); + Consume(1); } else { diff --git a/Projects/UOContent/Items/Misc/PlayerBulletinBoards.cs b/Projects/UOContent/Items/Misc/PlayerBulletinBoards.cs index 0dcd418aa..1a0c3d3bc 100644 --- a/Projects/UOContent/Items/Misc/PlayerBulletinBoards.cs +++ b/Projects/UOContent/Items/Misc/PlayerBulletinBoards.cs @@ -15,11 +15,7 @@ namespace Server.Items; public partial class PlayerBBSouth : BasePlayerBB { [Constructible] - public PlayerBBSouth() : base(0x2311) - { - } - - public override double DefaultWeight => 15.0; + public PlayerBBSouth() : base(0x2311) => Weight = 15.0; public override int LabelNumber => 1062421; // bulletin board (south) } @@ -28,11 +24,7 @@ public partial class PlayerBBSouth : BasePlayerBB public partial class PlayerBBEast : BasePlayerBB { [Constructible] - public PlayerBBEast() : base(0x2312) - { - } - - public override double DefaultWeight => 15.0; + public PlayerBBEast() : base(0x2312) => Weight = 15.0; public override int LabelNumber => 1062420; // bulletin board (east) } @@ -114,7 +106,7 @@ public abstract partial class BasePlayerBB : Item, ISecurable } else if (CheckAccess(house, from)) { - PlayerBBGump.DisplayTo(from, house, this, 0); + from.SendGump(new PlayerBBGump(from, house, this, 0)); } } @@ -198,7 +190,7 @@ public abstract partial class BasePlayerBB : Item, ISecurable } } - PlayerBBGump.DisplayTo(from, house, board, page); + from.SendGump(new PlayerBBGump(from, house, board, page)); } } @@ -256,7 +248,7 @@ public abstract partial class BasePlayerBB : Item, ISecurable board.Title = text; } - PlayerBBGump.DisplayTo(from, house, board, page); + from.SendGump(new PlayerBBGump(from, house, board, page)); } } } @@ -289,110 +281,97 @@ public partial class PlayerBBMessage } } -public class PlayerBBGump : DynamicGump +public class PlayerBBGump : Gump { private const int LabelColor = 0x7FFF; private const int LabelHue = 1153; - private readonly BasePlayerBB _board; - private readonly Mobile _from; - private readonly BaseHouse _house; + private BasePlayerBB _board; + private Mobile _from; + private BaseHouse _house; private int _page; public override bool Singleton => true; - private PlayerBBGump(Mobile from, BaseHouse house, BasePlayerBB board, int page) : base(50, 10) + public PlayerBBGump(Mobile from, BaseHouse house, BasePlayerBB board, int page) : base(50, 10) { _page = page; _from = from; _house = house; _board = board; - } - public static void DisplayTo(Mobile from, BaseHouse house, BasePlayerBB board, int page) - { - if (from?.NetState == null || house == null || board == null || board.Deleted) + AddPage(0); + + AddImage(30, 30, 5400); + + AddButton(393, 145, 2084, 2084, 4); // Scroll up + AddButton(390, 371, 2085, 2085, 5); // Scroll down + + AddButton(32, 183, 5412, 5413, 1); // Post message + + if (house.IsOwner(from)) { - return; + AddButton(63, 90, 5601, 5605, 2); + AddHtmlLocalized(81, 89, 230, 20, 1062400, LabelColor); // Set title + + AddButton(63, 109, 5601, 5605, 3); + AddHtmlLocalized(81, 108, 230, 20, 1062401, LabelColor); // Post greeting } - from.SendGump(new PlayerBBGump(from, house, board, page)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var page = _page; - - builder.AddPage(); - - builder.AddImage(30, 30, 5400); - - builder.AddButton(393, 145, 2084, 2084, 4); // Scroll up - builder.AddButton(390, 371, 2085, 2085, 5); // Scroll down - - builder.AddButton(32, 183, 5412, 5413, 1); // Post message - - if (_house.IsOwner(_from)) - { - builder.AddButton(63, 90, 5601, 5605, 2); - builder.AddHtmlLocalized(81, 89, 230, 20, 1062400, LabelColor); // Set title - - builder.AddButton(63, 109, 5601, 5605, 3); - builder.AddHtmlLocalized(81, 108, 230, 20, 1062401, LabelColor); // Post greeting - } - - var title = _board.Title; + var title = board.Title; if (title != null) { - builder.AddHtml(183, 68, 180, 23, title); + AddHtml(183, 68, 180, 23, title); } - builder.AddHtmlLocalized(385, 89, 60, 20, 1062409, LabelColor); // Post + AddHtmlLocalized(385, 89, 60, 20, 1062409, LabelColor); // Post - builder.AddLabel(440, 89, LabelHue, $"{page}"); - builder.AddLabel(455, 89, LabelHue, "/"); - builder.AddLabel(470, 89, LabelHue, $"{_board.Messages.Count}"); + AddLabel(440, 89, LabelHue, page.ToString()); + AddLabel(455, 89, LabelHue, "/"); + AddLabel(470, 89, LabelHue, board.Messages.Count.ToString()); - var message = _board.Greeting; + var message = board.Greeting; - if (page >= 1 && page <= _board.Messages.Count) + if (page >= 1 && page <= board.Messages.Count) { - message = _board.Messages[page - 1]; + message = board.Messages[page - 1]; } - builder.AddImageTiled(150, 220, 240, 1, 2700); // Separator + AddImageTiled(150, 220, 240, 1, 2700); // Separator - builder.AddHtmlLocalized(150, 180, 100, 20, 1062405, 16715); // Posted On: - builder.AddHtmlLocalized(150, 200, 100, 20, 1062406, 16715); // Posted By: + AddHtmlLocalized(150, 180, 100, 20, 1062405, 16715); // Posted On: + AddHtmlLocalized(150, 200, 100, 20, 1062406, 16715); // Posted By: if (message != null) { - builder.AddHtml(255, 180, 150, 20, $"{message.Time:yyyy-MM-dd HH:mm:ss}"); + AddHtml(255, 180, 150, 20, message.Time.ToString("yyyy-MM-dd HH:mm:ss")); var poster = message.Poster; - var posterName = poster?.Name ?? "Someone"; - builder.AddHtml(255, 200, 150, 20, posterName.AsSpan().Trim()); + var name = (poster?.Name?.Trim()).DefaultIfNullOrEmpty("Someone"); - builder.AddHtml(150, 240, 250, 100, message.Message ?? ""); + AddHtml(255, 200, 150, 20, name); - if (message != _board.Greeting && _house.IsOwner(_from)) + AddHtml(150, 240, 250, 100, message.Message ?? ""); + + if (message != board.Greeting && house.IsOwner(from)) { - builder.AddButton(130, 395, 1209, 1210, 6); - builder.AddHtmlLocalized(150, 393, 150, 20, 1062410, LabelColor); // Banish Poster + AddButton(130, 395, 1209, 1210, 6); + AddHtmlLocalized(150, 393, 150, 20, 1062410, LabelColor); // Banish Poster - builder.AddButton(310, 395, 1209, 1210, 7); - builder.AddHtmlLocalized(330, 393, 150, 20, 1062411, LabelColor); // Delete Message + AddButton(310, 395, 1209, 1210, 7); + AddHtmlLocalized(330, 393, 150, 20, 1062411, LabelColor); // Delete Message } - if (_from.AccessLevel >= AccessLevel.GameMaster) + if (from.AccessLevel >= AccessLevel.GameMaster) { - builder.AddButton(135, 242, 1209, 1210, 8); // Post props + AddButton(135, 242, 1209, 1210, 8); // Post props } } } public override void OnResponse(NetState sender, in RelayInfo info) { + var page = _page; var from = _from; var house = _house; var board = _board; @@ -419,15 +398,16 @@ public class PlayerBBGump : DynamicGump { case 1: // Post message { - from.Prompt = new BasePlayerBB.PostPrompt(_page, house, board, false); + from.Prompt = new BasePlayerBB.PostPrompt(page, house, board, false); from.SendLocalizedMessage(1062397); // Please enter your message: + break; } case 2: // Set title { if (house.IsOwner(from)) { - from.Prompt = new BasePlayerBB.SetTitlePrompt(_page, house, board); + from.Prompt = new BasePlayerBB.SetTitlePrompt(page, house, board); from.SendLocalizedMessage(1062402); // Enter new title: } @@ -437,7 +417,7 @@ public class PlayerBBGump : DynamicGump { if (house.IsOwner(from)) { - from.Prompt = new BasePlayerBB.PostPrompt(_page, house, board, true); + from.Prompt = new BasePlayerBB.PostPrompt(page, house, board, true); from.SendLocalizedMessage(1062404); // Enter new greeting (this will always be the first post): } @@ -445,38 +425,40 @@ public class PlayerBBGump : DynamicGump } case 4: // Scroll up { - if (_page == 0) + if (page == 0) { - _page = board.Messages.Count; + page = board.Messages.Count; } else { - _page -= 1; + page -= 1; } - from.SendGump(this); + from.SendGump(new PlayerBBGump(from, house, board, page)); + break; } case 5: // Scroll down { - _page += 1; - _page %= board.Messages.Count + 1; + page += 1; + page %= board.Messages.Count + 1; + + from.SendGump(new PlayerBBGump(from, house, board, page)); - from.SendGump(this); break; } case 6: // Banish poster { if (house.IsOwner(from)) { - if (_page >= 1 && _page <= board.Messages.Count) + if (page >= 1 && page <= board.Messages.Count) { - var message = board.Messages[_page - 1]; + var message = board.Messages[page - 1]; var poster = message.Poster; if (poster == null) { - from.SendGump(this); + from.SendGump(new PlayerBBGump(from, house, board, page)); return; } @@ -521,7 +503,7 @@ public class PlayerBBGump : DynamicGump } } - from.SendGump(this); + from.SendGump(new PlayerBBGump(from, house, board, page)); } break; @@ -530,13 +512,12 @@ public class PlayerBBGump : DynamicGump { if (house.IsOwner(from)) { - if (_page >= 1 && _page <= board.Messages.Count) + if (page >= 1 && page <= board.Messages.Count) { - board.RemoveFromMessagesAt(_page - 1); + board.RemoveFromMessagesAt(page - 1); } - _page = 0; - from.SendGump(this); + from.SendGump(new PlayerBBGump(from, house, board, 0)); } break; @@ -547,12 +528,12 @@ public class PlayerBBGump : DynamicGump { var message = board.Greeting; - if (_page >= 1 && _page <= board.Messages.Count) + if (page >= 1 && page <= board.Messages.Count) { - message = board.Messages[_page - 1]; + message = board.Messages[page - 1]; } - from.SendGump(this); + from.SendGump(new PlayerBBGump(from, house, board, page)); from.SendGump(new PropertiesGump(from, message)); } diff --git a/Projects/UOContent/Items/Misc/PlayerVendorDeed.cs b/Projects/UOContent/Items/Misc/PlayerVendorDeed.cs index fb5b0a4e9..ff115b1b8 100644 --- a/Projects/UOContent/Items/Misc/PlayerVendorDeed.cs +++ b/Projects/UOContent/Items/Misc/PlayerVendorDeed.cs @@ -8,11 +8,7 @@ namespace Server.Items; public partial class ContractOfEmployment : Item { [Constructible] - public ContractOfEmployment() : base(0x14F0) - { - } - - public override double DefaultWeight => 1.0; + public ContractOfEmployment() : base(0x14F0) => Weight = 1.0; public override int LabelNumber => 1041243; // a contract of employment @@ -26,11 +22,9 @@ public partial class ContractOfEmployment : Item { from.SendLocalizedMessage(503248); // Your godly powers allow you to place this vendor whereever you wish. - var v = new PlayerVendor(from, BaseHouse.FindHouseAt(from)) - { - Direction = from.Direction & Direction.Mask - }; + Mobile v = new PlayerVendor(from, BaseHouse.FindHouseAt(from)); + v.Direction = from.Direction & Direction.Mask; v.MoveToWorld(from.Location, from.Map); v.SayTo(from, 503246); // Ah! it feels good to be working again. @@ -75,11 +69,9 @@ public partial class ContractOfEmployment : Item } else { - var v = new PlayerVendor(from, house) - { - Direction = from.Direction & Direction.Mask - }; + Mobile v = new PlayerVendor(from, house); + v.Direction = from.Direction & Direction.Mask; v.MoveToWorld(from.Location, from.Map); v.SayTo(from, 503246); // Ah! it feels good to be working again. diff --git a/Projects/UOContent/Items/Misc/PowerCrystal.cs b/Projects/UOContent/Items/Misc/PowerCrystal.cs index 650a512ee..7d43ed5af 100644 --- a/Projects/UOContent/Items/Misc/PowerCrystal.cs +++ b/Projects/UOContent/Items/Misc/PowerCrystal.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class PowerCrystal : Item { [Constructible] - public PowerCrystal() : base(0x1F1C) - { - } - - public override double DefaultWeight => 1.0; + public PowerCrystal() : base(0x1F1C) => Weight = 1.0; public override string DefaultName => "power crystal"; diff --git a/Projects/UOContent/Items/Misc/PowerGenerator.cs b/Projects/UOContent/Items/Misc/PowerGenerator.cs index 71b10c6c6..c2a752956 100644 --- a/Projects/UOContent/Items/Misc/PowerGenerator.cs +++ b/Projects/UOContent/Items/Misc/PowerGenerator.cs @@ -153,7 +153,7 @@ public partial class ControlPanel : AddonComponent if (m_User != null) { - m_User.CloseGump(); + m_User.CloseGump(); m_User = null; } } @@ -176,7 +176,7 @@ public partial class ControlPanel : AddonComponent if (m_User.Deleted || m_User.Map != Map || !m_User.InRange(this, 3) || m_User.NetState == null || Core.Now - m_LastUse >= m_UseTimeout) { - m_User.CloseGump(); + m_User.CloseGump(); } else { @@ -188,7 +188,7 @@ public partial class ControlPanel : AddonComponent m_User = from; m_LastUse = Core.Now; - PowerGeneratorGump.DisplayTo(from, this, 0, false); + from.SendGump(new GameGump(this, from, 0, false)); } public void DoDamage(Mobile to) @@ -277,74 +277,57 @@ public partial class ControlPanel : AddonComponent Down } - private class PowerGeneratorGump : DynamicGump + private class GameGump : Gump { - private readonly Mobile _from; + private readonly Mobile m_From; - private readonly ControlPanel _panel; - private readonly int _step; - private readonly bool _hint; + private readonly ControlPanel m_Panel; + private readonly int m_Step; - public override bool Singleton => true; - - private PowerGeneratorGump(ControlPanel panel, Mobile from, int step, bool hint) : base(5, 30) + public GameGump(ControlPanel panel, Mobile from, int step, bool hint) : base(5, 30) { - _panel = panel; - _from = from; - _step = step; - _hint = hint; - } + m_Panel = panel; + m_From = from; + m_Step = step; - public static void DisplayTo(Mobile from, ControlPanel panel, int step, bool hint) - { - if (from?.NetState == null || panel?.Deleted != false) - { - return; - } + var sideLength = panel.SideLength; - from.SendGump(new PowerGeneratorGump(panel, from, step, hint)); - } + AddBackground(50, 0, 530, 410, 0xA28); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var sideLength = _panel.SideLength; + AddImage(0, 0, 0x28C8); + AddImage(547, 0, 0x28C9); - builder.AddBackground(50, 0, 530, 410, 0xA28); + AddBackground(95, 20, 442, 90, 0xA28); - builder.AddImage(0, 0, 0x28C8); - builder.AddImage(547, 0, 0x28C9); + AddHtml(229, 35, 300, 45, "GENERATOR CONTROL PANEL"); - builder.AddBackground(95, 20, 442, 90, 0xA28); + AddHtml(223, 60, 300, 70, "Use the Directional Controls to"); + AddHtml(253, 75, 300, 85, "Close the Grid Circuit"); - builder.AddHtml(229, 35, 300, 45, "GENERATOR CONTROL PANEL"); + AddImage(140, 40, 0x28D3); + AddImage(420, 40, 0x28D3); - builder.AddHtml(223, 60, 300, 70, "Use the Directional Controls to"); - builder.AddHtml(253, 75, 300, 85, "Close the Grid Circuit"); + AddBackground(365, 120, 178, 210, 0x1400); - builder.AddImage(140, 40, 0x28D3); - builder.AddImage(420, 40, 0x28D3); + AddImage(365, 115, 0x28D4); + AddImage(365, 288, 0x28D4); - builder.AddBackground(365, 120, 178, 210, 0x1400); + AddImage(414, 189, 0x589); + AddImage(435, 210, 0xA52); - builder.AddImage(365, 115, 0x28D4); - builder.AddImage(365, 288, 0x28D4); + AddButton(408, 222, 0x29EA, 0x29EC, 1); // Left + AddButton(448, 185, 0x29CC, 0x29CE, 2); // Up + AddButton(473, 222, 0x29D6, 0x29D8, 3); // Right + AddButton(448, 243, 0x29E0, 0x29E2, 4); // Down - builder.AddImage(414, 189, 0x589); - builder.AddImage(435, 210, 0xA52); - - builder.AddButton(408, 222, 0x29EA, 0x29EC, 1); // Left - builder.AddButton(448, 185, 0x29CC, 0x29CE, 2); // Up - builder.AddButton(473, 222, 0x29D6, 0x29D8, 3); // Right - builder.AddButton(448, 243, 0x29E0, 0x29E2, 4); // Down - - builder.AddBackground(90, 115, 30 + 40 * sideLength, 30 + 40 * sideLength, 0xA28); - builder.AddBackground(100, 125, 10 + 40 * sideLength, 10 + 40 * sideLength, 0x1400); + AddBackground(90, 115, 30 + 40 * sideLength, 30 + 40 * sideLength, 0xA28); + AddBackground(100, 125, 10 + 40 * sideLength, 10 + 40 * sideLength, 0x1400); for (var i = 0; i < sideLength; i++) { for (var j = 0; j < sideLength - 1; j++) { - builder.AddImage(120 + 40 * i, 162 + 40 * j, 0x13F9); + AddImage(120 + 40 * i, 162 + 40 * j, 0x13F9); } } @@ -352,15 +335,15 @@ public partial class ControlPanel : AddonComponent { for (var j = 0; j < sideLength; j++) { - builder.AddImage(138 + 40 * i, 147 + 40 * j, 0x13FD); + AddImage(138 + 40 * i, 147 + 40 * j, 0x13FD); } } - var path = _panel.Path; + var path = panel.Path; var hues = new NodeHue[sideLength, sideLength]; - for (var i = 0; i <= _step; i++) + for (var i = 0; i <= step; i++) { var n = path[i]; hues[n.X, n.Y] = NodeHue.Blue; @@ -373,27 +356,27 @@ public partial class ControlPanel : AddonComponent { for (var j = 0; j < sideLength; j++) { - AddNode(ref builder, 110 + 40 * i, 135 + 40 * j, hues[i, j]); + AddNode(110 + 40 * i, 135 + 40 * j, hues[i, j]); } } - var curNode = path[_step]; - builder.AddImage(118 + 40 * curNode.X, 143 + 40 * curNode.Y, 0x13A8); + var curNode = path[step]; + AddImage(118 + 40 * curNode.X, 143 + 40 * curNode.Y, 0x13A8); - if (_hint) + if (hint) { - var nextNode = path[_step + 1]; - builder.AddImage(119 + 40 * nextNode.X, 143 + 40 * nextNode.Y, 0x939); + var nextNode = path[step + 1]; + AddImage(119 + 40 * nextNode.X, 143 + 40 * nextNode.Y, 0x939); } - if (_from.Skills.Lockpicking.Value >= 65.0) + if (from.Skills.Lockpicking.Value >= 65.0) { - builder.AddButton(365, 350, 0xFA6, 0xFA7, 5); - builder.AddHtml(405, 345, 140, 40, "Attempt to Decipher the Circuit Path"); + AddButton(365, 350, 0xFA6, 0xFA7, 5); + AddHtml(405, 345, 140, 40, "Attempt to Decipher the Circuit Path"); } } - private static void AddNode(ref DynamicGumpBuilder builder, int x, int y, NodeHue hue) + private void AddNode(int x, int y, NodeHue hue) { var id = hue switch { @@ -402,51 +385,51 @@ public partial class ControlPanel : AddonComponent _ => 0x9A8 }; - builder.AddImage(x, y, id); + AddImage(x, y, id); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_panel.Deleted || info.ButtonID == 0 || !_from.CheckAlive()) + if (m_Panel.Deleted || info.ButtonID == 0 || !m_From.CheckAlive()) { - _panel.m_User = null; + m_Panel.m_User = null; return; } - if (_from.Map != _panel.Map || !_from.InRange(_panel, 3)) + if (m_From.Map != m_Panel.Map || !m_From.InRange(m_Panel, 3)) { - _from.SendLocalizedMessage(500446); // That is too far away. - _panel.m_User = null; + m_From.SendLocalizedMessage(500446); // That is too far away. + m_Panel.m_User = null; return; } - var nextNode = _panel.Path[_step + 1]; + var nextNode = m_Panel.Path[m_Step + 1]; if (info.ButtonID == 5) // Attempt to Decipher { - var lockpicking = _from.Skills.Lockpicking.Value; + var lockpicking = m_From.Skills.Lockpicking.Value; if (lockpicking < 65.0) { return; } - _from.PlaySound(0x241); + m_From.PlaySound(0x241); if (40.0 + Utility.RandomDouble() * 80.0 < lockpicking) { - DisplayTo(_from, _panel, _step, true); - _panel.m_LastUse = Core.Now; + m_From.SendGump(new GameGump(m_Panel, m_From, m_Step, true)); + m_Panel.m_LastUse = Core.Now; } else { - _panel.DoDamage(_from); - _panel.m_User = null; + m_Panel.DoDamage(m_From); + m_Panel.m_User = null; } } else { - var curNode = _panel.Path[_step]; + var curNode = m_Panel.Path[m_Step]; int newX, newY; switch (info.ButtonID) @@ -484,22 +467,22 @@ public partial class ControlPanel : AddonComponent if (nextNode.X == newX && nextNode.Y == newY) { - if (_step + 1 == _panel.Path.Length - 1) + if (m_Step + 1 == m_Panel.Path.Length - 1) { - _panel.Solve(_from); - _panel.m_User = null; + m_Panel.Solve(m_From); + m_Panel.m_User = null; } else { - _from.PlaySound(0x1F4); - DisplayTo(_from, _panel, _step + 1, false); - _panel.m_LastUse = Core.Now; + m_From.PlaySound(0x1F4); + m_From.SendGump(new GameGump(m_Panel, m_From, m_Step + 1, false)); + m_Panel.m_LastUse = Core.Now; } } else { - _panel.DoDamage(_from); - _panel.m_User = null; + m_Panel.DoDamage(m_From); + m_Panel.m_User = null; } } } diff --git a/Projects/UOContent/Items/Misc/ProjectedItem.cs b/Projects/UOContent/Items/Misc/ProjectedItem.cs deleted file mode 100644 index 03e93d0ea..000000000 --- a/Projects/UOContent/Items/Misc/ProjectedItem.cs +++ /dev/null @@ -1,183 +0,0 @@ -using System; -using System.Collections.Generic; -using ModernUO.Serialization; -using Server.Collections; -using Server.Network; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class ProjectedItem : Item -{ - private static readonly HashSet _active = []; - private static Timer _timer; - - [SerializedCommandProperty(AccessLevel.GameMaster)] - [SerializableField(0)] - private int _effectItemId; - - [SerializedCommandProperty(AccessLevel.Developer)] - [SerializableField(1)] - private AccessLevel _minimumVisible; - - private bool _oldVisible; - - [Constructible] - public ProjectedItem(int effectItemId) : base(1) - { - Movable = false; - Visible = false; - _effectItemId = effectItemId; - } - - public override bool HandlesOnMovement => true; - - public override bool CanSeeStaffOnly(Mobile from) => from.AccessLevel >= _minimumVisible; - - public override void ProcessDelta() - { - if (_oldVisible != Visible) - { - Timer.DelayCall( - TimeSpan.Zero, - i => - { - i.ItemID = i.Visible ? i._effectItemId : 1; - i._oldVisible = i.Visible; - i.Activate(); - }, - this - ); - } - - base.ProcessDelta(); - } - - public override void SendInfoTo(NetState ns, ReadOnlySpan world = default) - { - base.SendInfoTo(ns, world); - - var m = ns.Mobile; - if (m != null && !Visible && m.AccessLevel >= _minimumVisible && !_active.Contains(this)) - { - Activate(); - } - } - - public override void OnLocationChange(Point3D oldLocation) - { - base.OnLocationChange(oldLocation); - Activate(); - } - - public override void OnMapChange() - { - base.OnMapChange(); - Activate(); - } - - public override void OnDelete() - { - Deactivate(); - base.OnDelete(); - } - - [AfterDeserialization] - private void AfterDeserialization() - { - _oldVisible = Visible; - } - - public override void OnMovement(Mobile m, Point3D oldLocation) - { - base.OnMovement(m, oldLocation); - - if (!Visible && m.AccessLevel >= _minimumVisible && !_active.Contains(this)) - { - Activate(); - } - } - - private void Activate() - { - if (!_active.Add(this)) - { - return; - } - - if (_timer == null) - { - _timer = Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1.0), OnTick); - } - else if (!_timer.Running) - { - _timer.Interval = TimeSpan.FromSeconds(1.0); - _timer.Start(); - } - } - - private void Deactivate() - { - if (_active.Remove(this) && _active.Count == 0) - { - _timer?.Stop(); - } - } - - private static void OnTick() - { - foreach (var item in _active) - { - if (!item.SendEffect()) - { - _active.Remove(item); - } - } - - if (_active.Count == 0) - { - _timer?.Stop(); - } - } - - protected virtual bool SendEffect() - { - if (_oldVisible) - { - return false; - } - - var found = false; - Span buffer = new byte[OutgoingEffectPackets.HuedEffectLength].InitializePacket(); - OutgoingEffectPackets.CreateHuedEffect( - buffer, - EffectType.FixedXYZ, - Serial, - Serial, - _effectItemId, - Location, - Location, - 1, - 20, - true, - false, - Hue - 1, - 3 - ); - - foreach (var ns in GetClientsInRange(GetMaxUpdateRange())) - { - found = true; - var from = ns.Mobile; - if (ns.CannotSendPackets() || from == null || from.AccessLevel < _minimumVisible) - { - continue; - } - - from.ProcessDelta(); - ns.Send(buffer); - } - - return found; - } -} diff --git a/Projects/UOContent/Items/Misc/PromotionalToken.cs b/Projects/UOContent/Items/Misc/PromotionalToken.cs index c2d39f0d4..90547b8e0 100644 --- a/Projects/UOContent/Items/Misc/PromotionalToken.cs +++ b/Projects/UOContent/Items/Misc/PromotionalToken.cs @@ -11,10 +11,9 @@ public abstract partial class PromotionalToken : Item { LootType = LootType.Blessed; Light = LightType.Circle300; + Weight = 5.0; } - public override double DefaultWeight => 5.0; - public abstract TextDefinition ItemName { get; } public abstract TextDefinition ItemReceiveMessage { get; } public abstract TextDefinition ItemGumpName { get; } @@ -47,7 +46,7 @@ public abstract partial class PromotionalToken : Item } else { - PromotionalTokenGump.DisplayTo(from, this); + from.SendGump(new PromotionalTokenGump(this)); } } @@ -67,35 +66,25 @@ public abstract partial class PromotionalToken : Item m?.CloseGump(); } - private class PromotionalTokenGump : DynamicGump + private class PromotionalTokenGump : Gump { - private readonly PromotionalToken _token; + private readonly PromotionalToken m_Token; public override bool Singleton => true; - private PromotionalTokenGump(PromotionalToken token) : base(10, 10) => _token = token; - - public static void DisplayTo(Mobile from, PromotionalToken token) + public PromotionalTokenGump(PromotionalToken token) : base(10, 10) { - if (from?.NetState == null || token?.Deleted != false) - { - return; - } + m_Token = token; - from.SendGump(new PromotionalTokenGump(token)); - } + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 240, 135, 0x2422); + AddBackground(0, 0, 240, 135, 0x2422); // Click "OKAY" to redeem the following promotional item: - builder.AddHtmlLocalized(15, 15, 210, 75, 1070972, 0x0, true); - _token.ItemGumpName.AddHtmlText(ref builder, 15, 60, 210, 75); + AddHtmlLocalized(15, 15, 210, 75, 1070972, 0x0, true); + m_Token.ItemGumpName.AddHtmlText(this, 15, 60, 210, 75, false, false); - builder.AddButton(160, 95, 0xF7, 0xF8, 1); // Okay - builder.AddButton(90, 95, 0xF2, 0xF1, 0); // Cancel + AddButton(160, 95, 0xF7, 0xF8, 1); // Okay + AddButton(90, 95, 0xF2, 0xF1, 0); // Cancel } public override void OnResponse(NetState sender, in RelayInfo info) @@ -107,19 +96,19 @@ public abstract partial class PromotionalToken : Item var from = sender.Mobile; - if (!_token.IsChildOf(from.Backpack)) + if (!m_Token.IsChildOf(from.Backpack)) { from.SendLocalizedMessage(1062334); // This item must be in your backpack to be used. } else { - var i = _token.CreateItemFor(from); + var i = m_Token.CreateItemFor(from); if (i != null) { from.BankBox.AddItem(i); - _token.ItemReceiveMessage.SendMessageTo(from); - _token.Delete(); + m_Token.ItemReceiveMessage.SendMessageTo(from); + m_Token.Delete(); } } } diff --git a/Projects/UOContent/Items/Misc/PublicMoongate.cs b/Projects/UOContent/Items/Misc/PublicMoongate.cs index eea80ad84..8355d71e8 100644 --- a/Projects/UOContent/Items/Misc/PublicMoongate.cs +++ b/Projects/UOContent/Items/Misc/PublicMoongate.cs @@ -84,7 +84,7 @@ public partial class PublicMoongate : Item return false; } - MoongateGump.DisplayTo(m, this); + m.SendGump(new MoongateGump(m, this)); if (!m.Hidden || m.AccessLevel == AccessLevel.Player) { @@ -147,7 +147,7 @@ public partial class PublicMoongate : Item foreach (var entry in list.Entries) { - var item = new PublicMoongate(); + Item item = new PublicMoongate(); item.MoveToWorld(entry.Location, list.Map); @@ -176,12 +176,13 @@ public class PMEntry public class PMList { - public static readonly PMList Trammel = + public static PMList Trammel = new( 1012000, 1012012, Map.Trammel, - [ + new[] + { new PMEntry(new Point3D(4467, 1283, 5), 1012003), // Moonglow new PMEntry(new Point3D(1336, 1997, 5), 1012004), // Britain new PMEntry(new Point3D(1499, 3771, 5), 1012005), // Jhelom @@ -192,15 +193,16 @@ public class PMList /* Dynamic Z for Magincia to support both old and new maps. */ new PMEntry(new Point3D(3563, 2139, Map.Trammel.GetAverageZ(3563, 2139)), 1012010), // (New) Magincia new PMEntry(new Point3D(3450, 2677, 25), 1078098) // New Haven - ] + } ); - public static readonly PMList Felucca = + public static PMList Felucca = new( 1012001, 1012013, Map.Felucca, - [ + new[] + { new PMEntry(new Point3D(4467, 1283, 5), 1012003), // Moonglow new PMEntry(new Point3D(1336, 1997, 5), 1012004), // Britain new PMEntry(new Point3D(1499, 3771, 5), 1012005), // Jhelom @@ -211,15 +213,16 @@ public class PMList /* Dynamic Z for Magincia to support both old and new maps. */ new PMEntry(new Point3D(3563, 2139, Map.Felucca.GetAverageZ(3563, 2139)), 1012010), // (New) Magincia new PMEntry(new Point3D(2711, 2234, 0), 1019001) // Buccaneer's Den - ] + } ); - public static readonly PMList Ilshenar = + public static PMList Ilshenar = new( 1012002, 1012014, Map.Ilshenar, - [ + new[] + { new PMEntry(new Point3D(1215, 467, -13), 1012015), // Compassion new PMEntry(new Point3D(722, 1366, -60), 1012016), // Honesty new PMEntry(new Point3D(744, 724, -28), 1012017), // Honor @@ -229,68 +232,72 @@ public class PMList new PMEntry(new Point3D(1532, 1340, -3), 1012021), // Spirituality new PMEntry(new Point3D(528, 216, -45), 1012022), // Valor new PMEntry(new Point3D(1721, 218, 96), 1019000) // Chaos - ] + } ); - public static readonly PMList Malas = + public static PMList Malas = new( 1060643, 1062039, Map.Malas, - [ + new[] + { new PMEntry(new Point3D(1015, 527, -65), 1060641), // Luna new PMEntry(new Point3D(1997, 1386, -85), 1060642) // Umbra - ] + } ); - public static readonly PMList Tokuno = + public static PMList Tokuno = new( 1063258, 1063415, Map.Tokuno, - [ + new[] + { new PMEntry(new Point3D(1169, 998, 41), 1063412), // Isamu-Jima new PMEntry(new Point3D(802, 1204, 25), 1063413), // Makoto-Jima new PMEntry(new Point3D(270, 628, 15), 1063414) // Homare-Jima - ] + } ); - public static readonly PMList TerMur = + public static PMList TerMur = new( 1113602, 1113604, Map.TerMur, - [ - new PMEntry(new Point3D(850, 3525, -38), 1113603) // Royal City - ] + new[] + { + new PMEntry(new Point3D(850, 3525, -38), 1113603), // Royal City + } ); - public static readonly PMList TerMurEodon = + public static PMList TerMurEodon = new( 1113602, 1113604, Map.TerMur, - [ + new[] + { new PMEntry(new Point3D(850, 3525, -38), 1113603), // Royal City new PMEntry(new Point3D(719, 1863, 40), 1156262) // Valley of Eodon - ] + } ); - public static readonly PMList[] NoTrammelLists = [Felucca]; - public static readonly PMList[] T2ALists = [Trammel, Felucca]; - public static readonly PMList[] T2AListsYoung = [Trammel]; - public static readonly PMList[] LBRLists = [Trammel, Felucca, Ilshenar]; - public static readonly PMList[] LBRListsYoung = [Trammel, Ilshenar]; - public static readonly PMList[] AOSLists = [Trammel, Felucca, Ilshenar, Malas]; - public static readonly PMList[] AOSListsYoung = [Trammel, Ilshenar, Malas]; - public static readonly PMList[] SELists = [Trammel, Felucca, Ilshenar, Malas, Tokuno]; - public static readonly PMList[] SEListsYoung = [Trammel, Ilshenar, Malas, Tokuno]; - public static readonly PMList[] SALists = [Trammel, Felucca, Ilshenar, Malas, Tokuno, TerMur]; - public static readonly PMList[] SAListsYoung = [Trammel, Ilshenar, Malas, Tokuno, TerMur]; - public static readonly PMList[] TOLLists = [Trammel, Felucca, Ilshenar, Malas, Tokuno, TerMurEodon]; - public static readonly PMList[] TOLListsYoung = [Trammel, Ilshenar, Malas, Tokuno, TerMurEodon]; - public static readonly PMList[] RedLists = [Felucca]; - public static readonly PMList[] SigilLists = [Felucca]; + public static PMList[] NoTrammelLists = { Felucca }; + public static PMList[] T2ALists = { Trammel, Felucca }; + public static PMList[] T2AListsYoung = { Trammel }; + public static PMList[] LBRLists = { Trammel, Felucca, Ilshenar }; + public static PMList[] LBRListsYoung = { Trammel, Ilshenar }; + public static PMList[] AOSLists = { Trammel, Felucca, Ilshenar, Malas }; + public static PMList[] AOSListsYoung = { Trammel, Ilshenar, Malas }; + public static PMList[] SELists = { Trammel, Felucca, Ilshenar, Malas, Tokuno }; + public static PMList[] SEListsYoung = { Trammel, Ilshenar, Malas, Tokuno }; + public static PMList[] SALists = { Trammel, Felucca, Ilshenar, Malas, Tokuno, TerMur }; + public static PMList[] SAListsYoung = { Trammel, Ilshenar, Malas, Tokuno, TerMur }; + public static PMList[] TOLLists = { Trammel, Felucca, Ilshenar, Malas, Tokuno, TerMurEodon }; + public static PMList[] TOLListsYoung = { Trammel, Ilshenar, Malas, Tokuno, TerMurEodon }; + public static PMList[] RedLists = { Felucca }; + public static PMList[] SigilLists = { Felucca }; public PMList(int number, int selNumber, Map map, PMEntry[] entries) { @@ -309,27 +316,18 @@ public class PMList public PMEntry[] Entries { get; } } -public class MoongateGump : DynamicGump +public class MoongateGump : Gump { - private readonly PMList[] _lists; - private readonly PMList[] _tabOrder; - private readonly Item _moongate; + private PMList[] _lists; + private Mobile _mobile; + private Item _moongate; public override bool Singleton => true; - private MoongateGump(Item moongate, PMList[] lists, PMList[] tabOrder) : base(100, 100) + public MoongateGump(Mobile mobile, Item moongate) : base(100, 100) { + _mobile = mobile; _moongate = moongate; - _lists = lists; - _tabOrder = tabOrder; - } - - public static void DisplayTo(Mobile mobile, Item moongate) - { - if (mobile?.NetState == null || moongate == null || moongate.Deleted) - { - return; - } PMList[] checkLists; @@ -341,7 +339,7 @@ public class MoongateGump : DynamicGump { checkLists = PMList.SigilLists; } - else if (mobile.Murderer) + else if (mobile.Kills >= 5) { checkLists = PMList.RedLists; } @@ -377,81 +375,72 @@ public class MoongateGump : DynamicGump } } - var mapCount = filteredBySelectedMaps.Count; - if (mapCount == 0) + int mapCount = filteredBySelectedMaps.Count; + _lists = new PMList[mapCount]; + for (var i = 0; i < mapCount; i++) { - return; - } - - var lists = filteredBySelectedMaps.ToArray(); - var tabOrder = filteredBySelectedMaps.ToArray(); - - for (var i = 0; i < lists.Length; ++i) - { - if (lists[i].Map == mobile.Map) - { - (lists[i], lists[0]) = (lists[0], lists[i]); - break; - } - } - - mobile.SendGump(new MoongateGump(moongate, lists, tabOrder)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 380, 280, 5054); - - builder.AddButton(10, 210, 4005, 4007, 1); - builder.AddHtmlLocalized(45, 210, 140, 25, 1011036); // OKAY - - builder.AddButton(10, 235, 4005, 4007, 0); - builder.AddHtmlLocalized(45, 235, 140, 25, 1011012); // CANCEL - - builder.AddHtmlLocalized(5, 5, 200, 20, 1012011); // Pick your destination: - - // Tab buttons on page 0 — rendered in original (pre-swap) order so the active map appears at its expected slot. - for (var i = 0; i < _tabOrder.Length; ++i) - { - builder.AddButton(10, 35 + i * 25, 2117, 2118, 0, GumpButtonType.Page, Array.IndexOf(_lists, _tabOrder[i]) + 1); - builder.AddHtmlLocalized(30, 35 + i * 25, 150, 20, _tabOrder[i].Number); + _lists[i] = filteredBySelectedMaps[i]; } for (var i = 0; i < _lists.Length; ++i) { - RenderPage(ref builder, i, Array.IndexOf(_tabOrder, _lists[i])); + if (_lists[i].Map == mobile.Map) + { + (_lists[i], _lists[0]) = (_lists[0], _lists[i]); + break; + } + } + + AddPage(0); + + AddBackground(0, 0, 380, 280, 5054); + + AddButton(10, 210, 4005, 4007, 1); + AddHtmlLocalized(45, 210, 140, 25, 1011036); // OKAY + + AddButton(10, 235, 4005, 4007, 0); + AddHtmlLocalized(45, 235, 140, 25, 1011012); // CANCEL + + AddHtmlLocalized(5, 5, 200, 20, 1012011); // Pick your destination: + + for (var i = 0; i < filteredBySelectedMaps.Count; ++i) + { + AddButton(10, 35 + i * 25, 2117, 2118, 0, GumpButtonType.Page, Array.IndexOf(_lists, filteredBySelectedMaps[i]) + 1); + AddHtmlLocalized(30, 35 + i * 25, 150, 20, filteredBySelectedMaps[i].Number); + } + + for (var i = 0; i < _lists.Length; ++i) + { + RenderPage(i, filteredBySelectedMaps.IndexOf(_lists[i])); } } - private void RenderPage(ref DynamicGumpBuilder builder, int index, int offset) + private void RenderPage(int index, int offset) { var list = _lists[index]; - builder.AddPage(index + 1); + AddPage(index + 1); - builder.AddButton(10, 35 + offset * 25, 2117, 2118, 0, GumpButtonType.Page, index + 1); - builder.AddHtmlLocalized(30, 35 + offset * 25, 150, 20, list.SelNumber); + AddButton(10, 35 + offset * 25, 2117, 2118, 0, GumpButtonType.Page, index + 1); + AddHtmlLocalized(30, 35 + offset * 25, 150, 20, list.SelNumber); var entries = list.Entries; for (var i = 0; i < entries.Length; ++i) { - builder.AddRadio(200, 35 + i * 25, 210, 211, false, index * 100 + i); - builder.AddHtmlLocalized(225, 35 + i * 25, 150, 20, entries[i].Number); + AddRadio(200, 35 + i * 25, 210, 211, false, index * 100 + i); + AddHtmlLocalized(225, 35 + i * 25, 150, 20, entries[i].Number); } } public override void OnResponse(NetState state, in RelayInfo info) { - if (info.ButtonID == 0 || _moongate.Deleted) + if (info.ButtonID == 0) // Cancel { return; } - var from = state.Mobile; - if (from.Map == null) + if (_mobile.Deleted || _moongate.Deleted || _mobile.Map == null) { return; } @@ -481,40 +470,43 @@ public class MoongateGump : DynamicGump var entry = list.Entries[listEntry]; - if (!from.InRange(_moongate.GetWorldLocation(), 1) || from.Map != _moongate.Map) + if (!_mobile.InRange(_moongate.GetWorldLocation(), 1) || _mobile.Map != _moongate.Map) { - from.SendLocalizedMessage(1019002); // You are too far away to use the gate. + _mobile.SendLocalizedMessage(1019002); // You are too far away to use the gate. } - else if (from.Player && from.Murderer && list.Map != Map.Felucca || - Sigil.ExistsOn(from) && list.Map != Faction.Facet) + else if (_mobile.Player && _mobile.Kills >= 5 && list.Map != Map.Felucca) { - from.SendLocalizedMessage(1019004); // You are not allowed to travel there. + _mobile.SendLocalizedMessage(1019004); // You are not allowed to travel there. } - else if (from.Criminal) + else if (Sigil.ExistsOn(_mobile) && list.Map != Faction.Facet) { - from.SendLocalizedMessage(1005561, "", 0x22); // Thou'rt a criminal and cannot escape so easily. + _mobile.SendLocalizedMessage(1019004); // You are not allowed to travel there. } - else if (SpellHelper.CheckCombat(from)) + else if (_mobile.Criminal) { - from.SendLocalizedMessage(1005564, "", 0x22); // Wouldst thou flee during the heat of battle?? + _mobile.SendLocalizedMessage(1005561, "", 0x22); // Thou'rt a criminal and cannot escape so easily. } - else if (from.Spell != null) + else if (SpellHelper.CheckCombat(_mobile)) { - from.SendLocalizedMessage(1049616); // You are too busy to do that at the moment. + _mobile.SendLocalizedMessage(1005564, "", 0x22); // Wouldst thou flee during the heat of battle?? } - else if (from.Map == list.Map && from.InRange(entry.Location, 1)) + else if (_mobile.Spell != null) { - from.SendLocalizedMessage(1019003); // You are already there. + _mobile.SendLocalizedMessage(1049616); // You are too busy to do that at the moment. + } + else if (_mobile.Map == list.Map && _mobile.InRange(entry.Location, 1)) + { + _mobile.SendLocalizedMessage(1019003); // You are already there. } else { - BaseCreature.TeleportPets(from, entry.Location, list.Map); + BaseCreature.TeleportPets(_mobile, entry.Location, list.Map); - from.Combatant = null; - from.Warmode = false; - from.Hidden = true; + _mobile.Combatant = null; + _mobile.Warmode = false; + _mobile.Hidden = true; - from.MoveToWorld(entry.Location, list.Map); + _mobile.MoveToWorld(entry.Location, list.Map); Effects.PlaySound(entry.Location, list.Map, 0x1FE); } diff --git a/Projects/UOContent/Items/Misc/Rares.cs b/Projects/UOContent/Items/Misc/Rares.cs index 699006a7c..2a23a7cc7 100644 --- a/Projects/UOContent/Items/Misc/Rares.cs +++ b/Projects/UOContent/Items/Misc/Rares.cs @@ -9,10 +9,9 @@ public partial class Rope : Item public Rope(int amount = 1) : base(0x14F8) { Stackable = true; + Weight = 1.0; Amount = amount; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -22,10 +21,9 @@ public partial class IronWire : Item public IronWire(int amount = 1) : base(0x1876) { Stackable = true; + Weight = 5.0; Amount = amount; } - - public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -35,10 +33,9 @@ public partial class SilverWire : Item public SilverWire(int amount = 1) : base(0x1877) { Stackable = true; + Weight = 5.0; Amount = amount; } - - public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -48,10 +45,9 @@ public partial class GoldWire : Item public GoldWire(int amount = 1) : base(0x1878) { Stackable = true; + Weight = 5.0; Amount = amount; } - - public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -61,10 +57,9 @@ public partial class CopperWire : Item public CopperWire(int amount = 1) : base(0x1879) { Stackable = true; + Weight = 5.0; Amount = amount; } - - public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -74,10 +69,9 @@ public partial class WhiteDriedFlowers : Item public WhiteDriedFlowers(int amount = 1) : base(0xC3C) { Stackable = true; + Weight = 1.0; Amount = amount; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -87,10 +81,9 @@ public partial class GreenDriedFlowers : Item public GreenDriedFlowers(int amount = 1) : base(0xC3E) { Stackable = true; + Weight = 1.0; Amount = amount; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -100,10 +93,9 @@ public partial class DriedOnions : Item public DriedOnions(int amount = 1) : base(0xC40) { Stackable = true; + Weight = 1.0; Amount = amount; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -113,162 +105,105 @@ public partial class DriedHerbs : Item public DriedHerbs(int amount = 1) : base(0xC42) { Stackable = true; + Weight = 1.0; Amount = amount; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class HorseShoes : Item { [Constructible] - public HorseShoes() : base(0xFB6) - { - } - - public override double DefaultWeight => 3.0; + public HorseShoes() : base(0xFB6) => Weight = 3.0; } [SerializationGenerator(0, false)] public partial class ForgedMetal : Item { [Constructible] - public ForgedMetal() : base(0xFB8) - { - } - - public override double DefaultWeight => 5.0; + public ForgedMetal() : base(0xFB8) => Weight = 5.0; } [SerializationGenerator(0, false)] public partial class Whip : Item { [Constructible] - public Whip() : base(0x166E) - { - } - - public override double DefaultWeight => 1.0; + public Whip() : base(0x166E) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class PaintsAndBrush : Item { [Constructible] - public PaintsAndBrush() : base(0xFC1) - { - } - - public override double DefaultWeight => 1.0; + public PaintsAndBrush() : base(0xFC1) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class PenAndInk : Item { [Constructible] - public PenAndInk() : base(0xFBF) - { - } - - public override double DefaultWeight => 1.0; + public PenAndInk() : base(0xFBF) => Weight = 1.0; } [SerializationGenerator(0)] public partial class ChiselsNorth : Item { [Constructible] - public ChiselsNorth() : base(0x1026) - { - } - - public override double DefaultWeight => 1.0; + public ChiselsNorth() : base(0x1026) => Weight = 1.0; } [SerializationGenerator(0)] public partial class ChiselsWest : Item { [Constructible] - public ChiselsWest() : base(0x1027) - { - } - - public override double DefaultWeight => 1.0; + public ChiselsWest() : base(0x1027) => Weight = 1.0; } [SerializationGenerator(0)] public partial class DirtyPan : Item { [Constructible] - public DirtyPan() : base(0x9E8) - { - } - - public override double DefaultWeight => 1.0; + public DirtyPan() : base(0x9E8) => Weight = 1.0; } [SerializationGenerator(0)] public partial class DirtySmallRoundPot : Item { [Constructible] - public DirtySmallRoundPot() : base(0x9E7) - { - } - - public override double DefaultWeight => 1.0; + public DirtySmallRoundPot() : base(0x9E7) => Weight = 1.0; } [SerializationGenerator(0)] public partial class DirtyPot : Item { [Constructible] - public DirtyPot() : base(0x9E6) - { - } - - public override double DefaultWeight => 1.0; + public DirtyPot() : base(0x9E6) => Weight = 1.0; } [SerializationGenerator(0)] public partial class DirtyRoundPot : Item { [Constructible] - public DirtyRoundPot() : base(0x9DF) - { - } - - public override double DefaultWeight => 1.0; + public DirtyRoundPot() : base(0x9DF) => Weight = 1.0; } [SerializationGenerator(0)] public partial class DirtyFrypan : Item { [Constructible] - public DirtyFrypan() : base(0x9DE) - { - } - - public override double DefaultWeight => 1.0; + public DirtyFrypan() : base(0x9DE) => Weight = 1.0; } [SerializationGenerator(0)] public partial class DirtySmallPot : Item { [Constructible] - public DirtySmallPot() : base(0x9DD) - { - } - - public override double DefaultWeight => 1.0; + public DirtySmallPot() : base(0x9DD) => Weight = 1.0; } [SerializationGenerator(0)] public partial class DirtyKettle : Item { [Constructible] - public DirtyKettle() : base(0x9DC) - { - } - - public override double DefaultWeight => 1.0; + public DirtyKettle() : base(0x9DC) => Weight = 1.0; } diff --git a/Projects/UOContent/Items/Misc/Scales.cs b/Projects/UOContent/Items/Misc/Scales.cs index bd2c4003e..3fe4937df 100644 --- a/Projects/UOContent/Items/Misc/Scales.cs +++ b/Projects/UOContent/Items/Misc/Scales.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class Scales : Item { [Constructible] - public Scales() : base(0x1852) - { - } - - public override double DefaultWeight => 4.0; + public Scales() : base(0x1852) => Weight = 4.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Misc/SpecialBeardDye.cs b/Projects/UOContent/Items/Misc/SpecialBeardDye.cs index 7687a2870..6fba86026 100644 --- a/Projects/UOContent/Items/Misc/SpecialBeardDye.cs +++ b/Projects/UOContent/Items/Misc/SpecialBeardDye.cs @@ -9,9 +9,11 @@ namespace Server.Items; public partial class SpecialBeardDye : Item { [Constructible] - public SpecialBeardDye() : base(0xE26) => LootType = LootType.Newbied; - - public override double DefaultWeight => 1.0; + public SpecialBeardDye() : base(0xE26) + { + Weight = 1.0; + LootType = LootType.Newbied; + } public override int LabelNumber => 1041087; // Special Beard Dye @@ -28,59 +30,51 @@ public partial class SpecialBeardDye : Item } } -public class SpecialBeardDyeGump : StaticGump +public class SpecialBeardDyeGump : Gump { - private static readonly (int HueStart, int HueCount)[] _entries = - [ - (12, 10), - (32, 5), - (38, 8), - (54, 3), - (62, 10), - (81, 2), - (89, 2), - (1153, 2) - ]; + private static SpecialBeardDyeEntry[] _entries = + { + new("*****", 12, 10), + new("*****", 32, 5), + new("*****", 38, 8), + new("*****", 54, 3), + new("*****", 62, 10), + new("*****", 81, 2), + new("*****", 89, 2), + new("*****", 1153, 2) + }; - private readonly SpecialBeardDye _specialBeardDye; + private SpecialBeardDye _specialBeardDye; public override bool Singleton => true; - public SpecialBeardDyeGump(SpecialBeardDye dye) : base(0, 0) => _specialBeardDye = dye; - - protected override void BuildLayout(ref StaticGumpBuilder builder) + public SpecialBeardDyeGump(SpecialBeardDye dye) : base(0, 0) { - builder.AddPage(); + _specialBeardDye = dye; - builder.AddBackground(150, 60, 350, 358, 2600); - builder.AddBackground(170, 104, 110, 270, 5100); + AddPage(0); + AddBackground(150, 60, 350, 358, 2600); + AddBackground(170, 104, 110, 270, 5100); + AddHtmlLocalized(230, 75, 200, 20, 1011013); // Hair Color Selection Menu + AddHtmlLocalized(235, 380, 300, 20, 1013007); // Dye my beard this color! + AddButton(200, 380, 0xFA5, 0xFA7, 1); // DYE HAIR - builder.AddHtmlLocalized(230, 75, 200, 20, 1011013); //
Hair Color Selection Menu
- builder.AddHtmlLocalized(235, 380, 300, 20, 1013007); // Dye my beard this color! - builder.AddButton(200, 380, 0xFA5, 0xFA7, 1); // DYE HAIR - - ReadOnlySpan<(int HueStart, int HueCount)> entries = _entries; - for (var i = 0; i < entries.Length; ++i) + for (var i = 0; i < _entries.Length; ++i) { - builder.AddLabel(180, 109 + i * 22, entries[i].HueStart - 1, "*****"); - builder.AddButton(257, 110 + i * 22, 5224, 5224, 0, GumpButtonType.Page, i + 1); + AddLabel(180, 109 + i * 22, _entries[i].HueStart - 1, _entries[i].Name); + AddButton(257, 110 + i * 22, 5224, 5224, 0, GumpButtonType.Page, i + 1); } - for (var i = 0; i < entries.Length; ++i) + for (var i = 0; i < _entries.Length; ++i) { - var (hueStart, hueCount) = entries[i]; + var e = _entries[i]; - builder.AddPage(i + 1); - var switchId = i * 100; + AddPage(i + 1); - for (var j = 0; j < hueCount; ++j) + for (var j = 0; j < e.HueCount; ++j) { - var page = Math.DivRem(j, 16, out var row); - var x = 310 + page * 80; - var y = 102 + row * 17; - - builder.AddRadio(x, y, 210, 211, false, switchId + j); - builder.AddLabel(x + 18, y, hueStart + j - 1, "*****"); + AddLabel(328 + j / 16 * 80, 102 + j % 16 * 17, e.HueStart + j - 1, "*****"); + AddRadio(310 + j / 16 * 80, 102 + j % 16 * 17, 210, 211, false, i * 100 + j); } } } @@ -101,37 +95,53 @@ public class SpecialBeardDyeGump : StaticGump return; } - if (info.ButtonID == 0 || switches.Length <= 0) + if (info.ButtonID != 0 && switches.Length > 0) + { + if (m.FacialHairItemID == 0) + { + m.SendLocalizedMessage(502623); // You have no hair to dye and cannot use this + } + else + { + // To prevent this from being exploited, the hue is abstracted into an internal list + var entryIndex = Math.DivRem(switches[0], 100, out var hueOffset); + + if (entryIndex >= 0 && entryIndex < _entries.Length) + { + var e = _entries[entryIndex]; + + if (hueOffset >= 0 && hueOffset < e.HueCount) + { + var hue = e.HueStart + hueOffset; + + m.FacialHairHue = hue; + + m.SendLocalizedMessage(501199); // You dye your hair + _specialBeardDye.Delete(); + m.PlaySound(0x4E); + } + } + } + } + else { m.SendLocalizedMessage(501200); // You decide not to dye your hair - return; - } - - if (m.FacialHairItemID == 0) - { - m.SendLocalizedMessage(502623); // You have no hair to dye and cannot use this - return; - } - - // To prevent this from being exploited, the hue is abstracted into an internal list - var entryIndex = Math.DivRem(switches[0], 100, out var hueOffset); - - if (entryIndex < 0 || entryIndex >= _entries.Length) - { - return; - } - - var e = _entries[entryIndex]; - - if (hueOffset >= 0 && hueOffset < e.HueCount) - { - var hue = e.HueStart + hueOffset; - - m.FacialHairHue = hue; - - m.SendLocalizedMessage(501199); // You dye your hair - _specialBeardDye.Delete(); - m.PlaySound(0x4E); } } + + private class SpecialBeardDyeEntry + { + public SpecialBeardDyeEntry(string name, int hueStart, int hueCount) + { + Name = name; + HueStart = hueStart; + HueCount = hueCount; + } + + public string Name { get; } + + public int HueStart { get; } + + public int HueCount { get; } + } } diff --git a/Projects/UOContent/Items/Misc/SpecialHairDye.cs b/Projects/UOContent/Items/Misc/SpecialHairDye.cs index 849003e68..09a8289b6 100644 --- a/Projects/UOContent/Items/Misc/SpecialHairDye.cs +++ b/Projects/UOContent/Items/Misc/SpecialHairDye.cs @@ -9,9 +9,11 @@ namespace Server.Items; public partial class SpecialHairDye : Item { [Constructible] - public SpecialHairDye() : base(0xE26) => LootType = LootType.Newbied; - - public override double DefaultWeight => 1.0; + public SpecialHairDye() : base(0xE26) + { + Weight = 1.0; + LootType = LootType.Newbied; + } public override int LabelNumber => 1074402; @@ -19,7 +21,7 @@ public partial class SpecialHairDye : Item { if (from.InRange(GetWorldLocation(), 1)) { - SpecialHairDyeGump.DisplayTo(from, this); + from.SendGump(new SpecialHairDyeGump(this)); } else { @@ -28,7 +30,7 @@ public partial class SpecialHairDye : Item } } -public class SpecialHairDyeGump : StaticGump +public class SpecialHairDyeGump : Gump { private static readonly SpecialHairDyeEntry[] _entries = { @@ -42,47 +44,37 @@ public class SpecialHairDyeGump : StaticGump new("*****", 1153, 2) }; - private readonly SpecialHairDye _specialHairDye; + private SpecialHairDye _specialHairDye; public override bool Singleton => true; - private SpecialHairDyeGump(SpecialHairDye dye) : base(0, 0) => _specialHairDye = dye; - - public static void DisplayTo(Mobile from, SpecialHairDye dye) + public SpecialHairDyeGump(SpecialHairDye dye) : base(0, 0) { - if (from?.NetState == null || dye?.Deleted != false) - { - return; - } + _specialHairDye = dye; - from.SendGump(new SpecialHairDyeGump(dye)); - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(150, 60, 350, 358, 2600); - builder.AddBackground(170, 104, 110, 270, 5100); - builder.AddHtmlLocalized(230, 75, 200, 20, 1011013); // Hair Color Selection Menu - builder.AddHtmlLocalized(235, 380, 300, 20, 1011014); // Dye my hair this color! - builder.AddButton(200, 380, 0xFA5, 0xFA7, 1); // DYE HAIR + AddPage(0); + AddBackground(150, 60, 350, 358, 2600); + AddBackground(170, 104, 110, 270, 5100); + AddHtmlLocalized(230, 75, 200, 20, 1011013); // Hair Color Selection Menu + AddHtmlLocalized(235, 380, 300, 20, 1011014); // Dye my hair this color! + AddButton(200, 380, 0xFA5, 0xFA7, 1); // DYE HAIR for (var i = 0; i < _entries.Length; ++i) { - builder.AddLabel(180, 109 + i * 22, _entries[i].HueStart - 1, _entries[i].Name); - builder.AddButton(257, 110 + i * 22, 5224, 5224, 0, GumpButtonType.Page, i + 1); + AddLabel(180, 109 + i * 22, _entries[i].HueStart - 1, _entries[i].Name); + AddButton(257, 110 + i * 22, 5224, 5224, 0, GumpButtonType.Page, i + 1); } for (var i = 0; i < _entries.Length; ++i) { var e = _entries[i]; - builder.AddPage(i + 1); + AddPage(i + 1); for (var j = 0; j < e.HueCount; ++j) { - builder.AddLabel(328 + j / 16 * 80, 102 + j % 16 * 17, e.HueStart + j - 1, "*****"); - builder.AddRadio(310 + j / 16 * 80, 102 + j % 16 * 17, 210, 211, false, i * 100 + j); + AddLabel(328 + j / 16 * 80, 102 + j % 16 * 17, e.HueStart + j - 1, "*****"); + AddRadio(310 + j / 16 * 80, 102 + j % 16 * 17, 210, 211, false, i * 100 + j); } } } diff --git a/Projects/UOContent/Items/Misc/Teleporter.cs b/Projects/UOContent/Items/Misc/Teleporter.cs index 425032975..cecf3e544 100644 --- a/Projects/UOContent/Items/Misc/Teleporter.cs +++ b/Projects/UOContent/Items/Misc/Teleporter.cs @@ -128,8 +128,10 @@ public partial class Teleporter : Item public override int LabelNumber => 1026095; // teleporter - public virtual void AddProperties(IPropertyList list) + public override void GetProperties(IPropertyList list) { + base.GetProperties(list); + if (Active) { list.Add(1060742); // active @@ -152,12 +154,6 @@ public partial class Teleporter : Item list.Add(1060660, $"{"Creatures"}\t{(Creatures ? "Yes" : "No")}"); } - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - AddProperties(list); - } - public override void OnSingleClick(Mobile from) { base.OnSingleClick(from); @@ -381,8 +377,10 @@ public partial class SkillTeleporter : Teleporter return false; } - public override void AddProperties(IPropertyList list) + public override void GetProperties(IPropertyList list) { + base.GetProperties(list); + var skillIndex = (int)_skill; string skillName; @@ -489,8 +487,10 @@ public partial class KeywordTeleporter : Teleporter public override bool OnMoveOver(Mobile m) => true; - public override void AddProperties(IPropertyList list) + public override void GetProperties(IPropertyList list) { + base.GetProperties(list); + list.Add(1060661, $"{"Range"}\t{_range}"); if (_keyword >= 0) @@ -866,7 +866,8 @@ public partial class ConditionTeleporter : Teleporter return false; } - if (GetFlag(ConditionFlag.DenyFollowers) && (m.Followers != 0 || m is PlayerMobile { AutoStabled.Count: > 0 })) + if (GetFlag(ConditionFlag.DenyFollowers) && + (m.Followers != 0 || m is PlayerMobile mobile && mobile.AutoStabled?.Count != 0)) { m.SendLocalizedMessage(1077250); // No pets permitted beyond this point. return false; @@ -934,8 +935,10 @@ public partial class ConditionTeleporter : Teleporter return true; } - public override void AddProperties(IPropertyList list) + public override void GetProperties(IPropertyList list) { + base.GetProperties(list); + using var props = ValueStringBuilder.Create(128); if (GetFlag(ConditionFlag.DenyMounted)) @@ -1024,35 +1027,3 @@ public partial class ConditionTeleporter : Teleporter DeadOnly = 0x100 } } - -[SerializationGenerator(0, false)] -public partial class InteractionTeleporter : Teleporter -{ - [Constructible] - public InteractionTeleporter() - { - Visible = true; - } - - [Constructible] - public InteractionTeleporter(Point3D pointDest, Map mapDest = null) : base(pointDest, mapDest) - { - Visible = true; - } - - public override bool OnMoveOver(Mobile m) => true; - - public override void AddProperties(IPropertyList list) {} - - public override void OnDoubleClick(Mobile m) - { - base.OnDoubleClick(m); - - if (Active && CanTeleport(m) && m.InRange(GetWorldLocation(), 2)) - { - StartTeleport(m); - } - } - - public override void OnDoubleClickDead(Mobile m) => OnDoubleClick(m); -} diff --git a/Projects/UOContent/Items/Misc/TribalBerry.cs b/Projects/UOContent/Items/Misc/TribalBerry.cs index 313bff659..4d6f1eb8a 100644 --- a/Projects/UOContent/Items/Misc/TribalBerry.cs +++ b/Projects/UOContent/Items/Misc/TribalBerry.cs @@ -8,12 +8,11 @@ public partial class TribalBerry : Item [Constructible] public TribalBerry(int amount = 1) : base(0x9D0) { + Weight = 1.0; Stackable = true; Amount = amount; Hue = 6; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1040001; // tribal berry } diff --git a/Projects/UOContent/Items/Misc/TribalPaint.cs b/Projects/UOContent/Items/Misc/TribalPaint.cs index be4ef1ef0..8bad846bb 100644 --- a/Projects/UOContent/Items/Misc/TribalPaint.cs +++ b/Projects/UOContent/Items/Misc/TribalPaint.cs @@ -16,11 +16,10 @@ public partial class TribalPaint : Item public TribalPaint() : base(0x9EC) { Hue = 2101; + Weight = 2.0; Stackable = Core.ML; } - public override double DefaultWeight => 2.0; - public override int LabelNumber => 1040000; // savage kin paint public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Misc/WarningItem.cs b/Projects/UOContent/Items/Misc/WarningItem.cs index 3779e4900..5d0b491a4 100644 --- a/Projects/UOContent/Items/Misc/WarningItem.cs +++ b/Projects/UOContent/Items/Misc/WarningItem.cs @@ -14,16 +14,8 @@ public partial class WarningItem : Item private TextDefinition _warningMessage; // Field 1 - [SerializableField(1, allowFieldChange: nameof(AllowRangeChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] private int _range; - private bool AllowRangeChange(ref int value) - { - value = Math.Min(value, 18); - return true; - } - [SerializableField(2)] private TimeSpan _resetDelay; @@ -47,6 +39,18 @@ public partial class WarningItem : Item _range = Math.Min(range, 18); } + [CommandProperty(AccessLevel.GameMaster)] + [SerializableProperty(1, useField: nameof(_range))] + public int Range + { + get => _range; + set + { + _range = Math.Min(value, 18); + this.MarkDirty(); + } + } + public virtual bool OnlyToTriggerer => false; public virtual int NeighborRange => 5; diff --git a/Projects/UOContent/Items/PlantsFlowers/Potted/EmptyPots.cs b/Projects/UOContent/Items/PlantsFlowers/Potted/EmptyPots.cs index 45505a91e..14a6c6e87 100644 --- a/Projects/UOContent/Items/PlantsFlowers/Potted/EmptyPots.cs +++ b/Projects/UOContent/Items/PlantsFlowers/Potted/EmptyPots.cs @@ -6,20 +6,12 @@ namespace Server.Items; public partial class SmallEmptyPot : Item { [Constructible] - public SmallEmptyPot() : base(0x11C6) - { - } - - public override double DefaultWeight => 100; + public SmallEmptyPot() : base(0x11C6) => Weight = 100; } [SerializationGenerator(0, false)] public partial class LargeEmptyPot : Item { [Constructible] - public LargeEmptyPot() : base(0x11C7) - { - } - - public override double DefaultWeight => 6; + public LargeEmptyPot() : base(0x11C7) => Weight = 6; } diff --git a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedCactus.cs b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedCactus.cs index 0143f6ac3..4c0f9c91e 100644 --- a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedCactus.cs +++ b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedCactus.cs @@ -6,64 +6,40 @@ namespace Server.Items; public partial class PottedCactus : Item { [Constructible] - public PottedCactus() : base(0x1E0F) - { - } - - public override double DefaultWeight => 100; + public PottedCactus() : base(0x1E0F) => Weight = 100; } [SerializationGenerator(0, false)] public partial class PottedCactus1 : Item { [Constructible] - public PottedCactus1() : base(0x1E10) - { - } - - public override double DefaultWeight => 100; + public PottedCactus1() : base(0x1E10) => Weight = 100; } [SerializationGenerator(0, false)] public partial class PottedCactus2 : Item { [Constructible] - public PottedCactus2() : base(0x1E11) - { - } - - public override double DefaultWeight => 100; + public PottedCactus2() : base(0x1E11) => Weight = 100; } [SerializationGenerator(0, false)] public partial class PottedCactus3 : Item { [Constructible] - public PottedCactus3() : base(0x1E12) - { - } - - public override double DefaultWeight => 100; + public PottedCactus3() : base(0x1E12) => Weight = 100; } [SerializationGenerator(0, false)] public partial class PottedCactus4 : Item { [Constructible] - public PottedCactus4() : base(0x1E13) - { - } - - public override double DefaultWeight => 100; + public PottedCactus4() : base(0x1E13) => Weight = 100; } [SerializationGenerator(0, false)] public partial class PottedCactus5 : Item { [Constructible] - public PottedCactus5() : base(0x1E14) - { - } - - public override double DefaultWeight => 100; + public PottedCactus5() : base(0x1E14) => Weight = 100; } diff --git a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedPlants.cs b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedPlants.cs index 503d5b310..b6d98773d 100644 --- a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedPlants.cs +++ b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedPlants.cs @@ -6,31 +6,19 @@ namespace Server.Items; public partial class PottedPlant : Item { [Constructible] - public PottedPlant() : base(0x11CA) - { - } - - public override double DefaultWeight => 100; + public PottedPlant() : base(0x11CA) => Weight = 100; } [SerializationGenerator(0, false)] public partial class PottedPlant1 : Item { [Constructible] - public PottedPlant1() : base(0x11CB) - { - } - - public override double DefaultWeight => 100; + public PottedPlant1() : base(0x11CB) => Weight = 100; } [SerializationGenerator(0, false)] public partial class PottedPlant2 : Item { [Constructible] - public PottedPlant2() : base(0x11CC) - { - } - - public override double DefaultWeight => 100; + public PottedPlant2() : base(0x11CC) => Weight = 100; } diff --git a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedTrees.cs b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedTrees.cs index 916fa6f51..b04240614 100644 --- a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedTrees.cs +++ b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedTrees.cs @@ -6,20 +6,12 @@ namespace Server.Items; public partial class PottedTree : Item { [Constructible] - public PottedTree() : base(0x11C8) - { - } - - public override double DefaultWeight => 100; + public PottedTree() : base(0x11C8) => Weight = 100; } [SerializationGenerator(0, false)] public partial class PottedTree1 : Item { [Constructible] - public PottedTree1() : base(0x11C9) - { - } - - public override double DefaultWeight => 100; + public PottedTree1() : base(0x11C9) => Weight = 100; } diff --git a/Projects/UOContent/Items/Quivers/BaseQuiver.cs b/Projects/UOContent/Items/Quivers/BaseQuiver.cs index 70824c9f6..dfdc6c5f8 100644 --- a/Projects/UOContent/Items/Quivers/BaseQuiver.cs +++ b/Projects/UOContent/Items/Quivers/BaseQuiver.cs @@ -11,66 +11,69 @@ public partial class BaseQuiver : Container, ICraftable, IAosItem [SerializedIgnoreDupe] [SerializableField(0, setter: "private")] - [SaveFlag(nameof(ShouldSerializeAosAttributes), nameof(AttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosAttributes _attributes; + [SerializableFieldSaveFlag(0)] private bool ShouldSerializeAosAttributes() => !_attributes.IsEmpty; + [SerializableFieldDefault(0)] private AosAttributes AttributesDefaultValue() => new(this); [InvalidateProperties] [SerializableField(1)] - [SaveFlag(nameof(ShouldSerializeLowerAmmoCost))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _lowerAmmoCost; + [SerializableFieldSaveFlag(1)] private bool ShouldSerializeLowerAmmoCost() => _lowerAmmoCost != 0; [InvalidateProperties] [SerializableField(2)] - [SaveFlag(nameof(ShouldSerializeWeightReduction))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _weightReduction; + [SerializableFieldSaveFlag(2)] private bool ShouldSerializeWeightReduction() => _weightReduction != 0; [InvalidateProperties] [SerializableField(3)] - [SaveFlag(nameof(ShouldSerializeDamageIncrease))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _damageIncrease; + [SerializableFieldSaveFlag(3)] private bool ShouldSerializeDamageIncrease() => _damageIncrease != 0; [InvalidateProperties] [SerializableField(4)] - [SaveFlag(nameof(ShouldSerializeCrafter))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _crafter; + [SerializableFieldSaveFlag(4)] private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter); [InvalidateProperties] [SerializableField(5)] - [SaveFlag(nameof(ShouldSerializeQuality), nameof(QualityDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster)] private ClothingQuality _quality; + [SerializableFieldSaveFlag(5)] private bool ShouldSerializeQuality() => _quality != ClothingQuality.Regular; + [SerializableFieldDefault(5)] private ClothingQuality QualityDefaultValue() => ClothingQuality.Regular; [InvalidateProperties] [SerializableField(6)] - [SaveFlag(nameof(ShouldSerializeCapacity))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _capacity; + [SerializableFieldSaveFlag(6)] private bool ShouldSerializeCapacity() => _capacity != 0; public BaseQuiver(int itemID = 0x2FB7) : base(itemID) { + Weight = 2.0; Capacity = 500; Layer = Layer.Cloak; @@ -178,7 +181,7 @@ public partial class BaseQuiver : Container, ICraftable, IAosItem return false; } - var ammo = Ammo; + Item ammo = Ammo; return ammo?.Deleted == false && ammo.Amount + item.Amount <= _capacity; } diff --git a/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs b/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs index 8170b286d..ce772da40 100644 --- a/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs +++ b/Projects/UOContent/Items/Resources/Blacksmithing/Ingots.cs @@ -16,14 +16,22 @@ public abstract partial class BaseIngot : Item, ICommodity public override double DefaultWeight => 0.1; - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(newValue); + get => _resource; + set + { + if (_resource != value) + { + _resource = value; + Hue = CraftResources.GetHue(value); + + InvalidateProperties(); + this.MarkDirty(); + } + } } public override int LabelNumber diff --git a/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs b/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs index 6ae0c71c3..3116d7136 100644 --- a/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs +++ b/Projects/UOContent/Items/Resources/Blacksmithing/Ore.cs @@ -17,14 +17,22 @@ public abstract partial class BaseOre : Item _resource = resource; } - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(newValue); + get => _resource; + set + { + if (_resource != value) + { + _resource = value; + Hue = CraftResources.GetHue(value); + + InvalidateProperties(); + this.MarkDirty(); + } + } } public override int LabelNumber diff --git a/Projects/UOContent/Items/Resources/Blacksmithing/Scales.cs b/Projects/UOContent/Items/Resources/Blacksmithing/Scales.cs index 277ed569d..fa63d902c 100644 --- a/Projects/UOContent/Items/Resources/Blacksmithing/Scales.cs +++ b/Projects/UOContent/Items/Resources/Blacksmithing/Scales.cs @@ -14,14 +14,22 @@ public abstract partial class BaseScales : Item, ICommodity _resource = resource; } - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(newValue); + get => _resource; + set + { + if (_resource != value) + { + _resource = value; + Hue = CraftResources.GetHue(value); + + InvalidateProperties(); + this.MarkDirty(); + } + } } public override int LabelNumber => 1053139; // dragon scales diff --git a/Projects/UOContent/Items/Resources/Fishing/Fish.cs b/Projects/UOContent/Items/Resources/Fishing/Fish.cs index d7e8e4498..49f93eedd 100644 --- a/Projects/UOContent/Items/Resources/Fishing/Fish.cs +++ b/Projects/UOContent/Items/Resources/Fishing/Fish.cs @@ -9,13 +9,12 @@ public partial class Fish : Item, ICarvable public Fish(int amount = 1) : base(Utility.Random(0x09CC, 4)) { Stackable = true; + Weight = 1.0; Amount = amount; } - public override double DefaultWeight => 1.0; - public void Carve(Mobile from, Item item) { ScissorHelper(from, new RawFishSteak(), 4); } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Items/Resources/Masonry/Granite.cs b/Projects/UOContent/Items/Resources/Masonry/Granite.cs index 089181ebe..ae4f215d7 100644 --- a/Projects/UOContent/Items/Resources/Masonry/Granite.cs +++ b/Projects/UOContent/Items/Resources/Masonry/Granite.cs @@ -15,14 +15,22 @@ public abstract partial class BaseGranite : Item public override double DefaultWeight => Core.ML ? 1.0 : 10.0; - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(newValue); + get => _resource; + set + { + if (_resource != value) + { + _resource = value; + Hue = CraftResources.GetHue(value); + + InvalidateProperties(); + this.MarkDirty(); + } + } } public override int LabelNumber => 1044607; // high quality granite diff --git a/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs b/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs index 685996cba..6d3638145 100644 --- a/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs +++ b/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs @@ -11,11 +11,10 @@ public partial class BoltOfCloth : Item, IScissorable, IDyable, ICommodity public BoltOfCloth(int amount = 1) : base(0xF95) { Stackable = true; + Weight = 5.0; Amount = amount; } - public override double DefaultWeight => 5.0; - int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => true; @@ -49,4 +48,4 @@ public partial class BoltOfCloth : Item, IScissorable, IDyable, ICommodity var amount = (Amount * 50).ToString(); from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, 0x3B2, 3, number, "", amount); } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Items/Resources/Tailor/Bone.cs b/Projects/UOContent/Items/Resources/Tailor/Bone.cs index b2cb47788..590b71a37 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Bone.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Bone.cs @@ -10,10 +10,9 @@ public partial class Bone : Item, ICommodity { Stackable = true; Amount = amount; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => true; -} +} \ No newline at end of file diff --git a/Projects/UOContent/Items/Resources/Tailor/Cotton.cs b/Projects/UOContent/Items/Resources/Tailor/Cotton.cs index 9d8ce0ef6..01b4712db 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Cotton.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Cotton.cs @@ -10,11 +10,10 @@ public partial class Cotton : Item, IDyable public Cotton(int amount = 1) : base(0xDF9) { Stackable = true; + Weight = 4.0; Amount = amount; } - public override double DefaultWeight => 4.0; - public bool Dye(Mobile from, DyeTub sender) { if (Deleted) @@ -42,10 +41,10 @@ public partial class Cotton : Item, IDyable public virtual void OnSpun(ISpinningWheel wheel, Mobile from, int hue) { - from.AddToBackpack(new SpoolOfThread(6) - { - Hue = hue - }); + Item item = new SpoolOfThread(6); + item.Hue = hue; + + from.AddToBackpack(item); from.SendLocalizedMessage(1010577); // You put the spools of thread in your backpack. } @@ -90,4 +89,4 @@ public partial class Cotton : Item, IDyable } } } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Items/Resources/Tailor/Flax.cs b/Projects/UOContent/Items/Resources/Tailor/Flax.cs index 464778b14..77f684f23 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Flax.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Flax.cs @@ -42,10 +42,10 @@ public partial class Flax : Item public virtual void OnSpun(ISpinningWheel wheel, Mobile from, int hue) { - from.AddToBackpack(new SpoolOfThread(6) - { - Hue = hue - }); + Item item = new SpoolOfThread(6); + item.Hue = hue; + + from.AddToBackpack(item); from.SendLocalizedMessage(1010577); // You put the spools of thread in your backpack. } diff --git a/Projects/UOContent/Items/Resources/Tailor/Hides.cs b/Projects/UOContent/Items/Resources/Tailor/Hides.cs index d64eb2f0b..f9a6014c9 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Hides.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Hides.cs @@ -8,21 +8,29 @@ public abstract partial class BaseHides : Item, ICommodity public BaseHides(CraftResource resource, int amount = 1) : base(0x1079) { Stackable = true; + Weight = 5.0; Amount = amount; Hue = CraftResources.GetHue(resource); + _resource = resource; } - public override double DefaultWeight => 5.0; - - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(newValue); + get => _resource; + set + { + if (_resource != value) + { + _resource = value; + Hue = CraftResources.GetHue(value); + + InvalidateProperties(); + this.MarkDirty(); + } + } } public override int LabelNumber diff --git a/Projects/UOContent/Items/Resources/Tailor/Leathers.cs b/Projects/UOContent/Items/Resources/Tailor/Leathers.cs index afb757e1f..86d1861ec 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Leathers.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Leathers.cs @@ -8,21 +8,29 @@ public abstract partial class BaseLeather : Item, ICommodity public BaseLeather(CraftResource resource, int amount = 1) : base(0x1081) { Stackable = true; + Weight = 1.0; Amount = amount; Hue = CraftResources.GetHue(resource); + _resource = resource; } - public override double DefaultWeight => 1.0; - - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(newValue); + get => _resource; + set + { + if (_resource != value) + { + _resource = value; + Hue = CraftResources.GetHue(value); + + InvalidateProperties(); + this.MarkDirty(); + } + } } public override int LabelNumber diff --git a/Projects/UOContent/Items/Resources/Tailor/Wool.cs b/Projects/UOContent/Items/Resources/Tailor/Wool.cs index 2ef0ddbf7..0dcec170d 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Wool.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Wool.cs @@ -10,11 +10,10 @@ public partial class Wool : Item, IDyable public Wool(int amount = 1) : base(0xDF8) { Stackable = true; + Weight = 4.0; Amount = amount; } - public override double DefaultWeight => 4.0; - public bool Dye(Mobile from, DyeTub sender) { if (Deleted) @@ -42,10 +41,10 @@ public partial class Wool : Item, IDyable public virtual void OnSpun(ISpinningWheel wheel, Mobile from, int hue) { - from.AddToBackpack(new DarkYarn(3) - { - Hue = hue - }); + Item item = new DarkYarn(3); + item.Hue = hue; + + from.AddToBackpack(item); from.SendLocalizedMessage(1010576); // You put the balls of yarn in your backpack. } @@ -100,17 +99,16 @@ public partial class TaintedWool : Wool public TaintedWool(int amount = 1) : base(0x101F) { Stackable = true; + Weight = 4.0; Amount = amount; } - public override double DefaultWeight => 4.0; - public override void OnSpun(ISpinningWheel wheel, Mobile from, int hue) { - from.AddToBackpack(new DarkYarn - { - Hue = hue - }); + Item item = new DarkYarn(); + item.Hue = hue; + + from.AddToBackpack(item); from.SendLocalizedMessage(1010574); // You put a ball of yarn in your backpack. } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs b/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs index ea5265b6d..9d8e2766d 100644 --- a/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs +++ b/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs @@ -9,11 +9,10 @@ public abstract partial class BaseClothMaterial : Item, IDyable public BaseClothMaterial(int itemID, int amount = 1) : base(itemID) { Stackable = true; + Weight = 1.0; Amount = amount; } - public override double DefaultWeight => 1.0; - public bool Dye(Mobile from, DyeTub sender) { if (Deleted) @@ -76,10 +75,8 @@ public abstract partial class BaseClothMaterial : Item, IDyable } else { - var create = new BoltOfCloth - { - Hue = m_Material.Hue - }; + Item create = new BoltOfCloth(); + create.Hue = m_Material.Hue; m_Material.Consume(); loom.Phase = 0; @@ -129,4 +126,4 @@ public partial class SpoolOfThread : BaseClothMaterial public SpoolOfThread(int amount = 1) : base(0xFA0, amount) { } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Items/Shields/BronzeShield.cs b/Projects/UOContent/Items/Shields/BronzeShield.cs index a13442682..8261d28c0 100644 --- a/Projects/UOContent/Items/Shields/BronzeShield.cs +++ b/Projects/UOContent/Items/Shields/BronzeShield.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class BronzeShield : BaseShield { [Constructible] - public BronzeShield() : base(0x1B72) - { - } - - public override double DefaultWeight => 6.0; + public BronzeShield() : base(0x1B72) => Weight = 6.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/Buckler.cs b/Projects/UOContent/Items/Shields/Buckler.cs index df3da2861..ea720c0b8 100644 --- a/Projects/UOContent/Items/Shields/Buckler.cs +++ b/Projects/UOContent/Items/Shields/Buckler.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class Buckler : BaseShield { [Constructible] - public Buckler() : base(0x1B73) - { - } - - public override double DefaultWeight => 5.0; + public Buckler() : base(0x1B73) => Weight = 5.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/ChaosShield.cs b/Projects/UOContent/Items/Shields/ChaosShield.cs index 7878dc25b..dd7707c95 100644 --- a/Projects/UOContent/Items/Shields/ChaosShield.cs +++ b/Projects/UOContent/Items/Shields/ChaosShield.cs @@ -13,9 +13,9 @@ public partial class ChaosShield : BaseShield { LootType = LootType.Newbied; } - } - public override double DefaultWeight => 4.0; + Weight = 5.0; + } public override int BasePhysicalResistance => 1; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/GargishWoodenShield.cs b/Projects/UOContent/Items/Shields/GargishWoodenShield.cs index cc4b3d038..5bdf1157c 100644 --- a/Projects/UOContent/Items/Shields/GargishWoodenShield.cs +++ b/Projects/UOContent/Items/Shields/GargishWoodenShield.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class GargishWoodenShield : BaseShield { [Constructible] - public GargishWoodenShield() : base(0x4200) - { - } - - public override double DefaultWeight => 5.0; + public GargishWoodenShield() : base(0x4200) => Weight = 5.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/HeaterShield.cs b/Projects/UOContent/Items/Shields/HeaterShield.cs index 3e17ec22a..40dccdeb3 100644 --- a/Projects/UOContent/Items/Shields/HeaterShield.cs +++ b/Projects/UOContent/Items/Shields/HeaterShield.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class HeaterShield : BaseShield { [Constructible] - public HeaterShield() : base(0x1B76) - { - } - - public override double DefaultWeight => 8.0; + public HeaterShield() : base(0x1B76) => Weight = 8.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 1; diff --git a/Projects/UOContent/Items/Shields/MetalKiteShield.cs b/Projects/UOContent/Items/Shields/MetalKiteShield.cs index ef46ddcfa..855b9ecff 100644 --- a/Projects/UOContent/Items/Shields/MetalKiteShield.cs +++ b/Projects/UOContent/Items/Shields/MetalKiteShield.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class MetalKiteShield : BaseShield, IDyable { [Constructible] - public MetalKiteShield() : base(0x1B74) - { - } - - public override double DefaultWeight => 7.0; + public MetalKiteShield() : base(0x1B74) => Weight = 7.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/MetalShield.cs b/Projects/UOContent/Items/Shields/MetalShield.cs index 6b4a8001e..5c9a5a148 100644 --- a/Projects/UOContent/Items/Shields/MetalShield.cs +++ b/Projects/UOContent/Items/Shields/MetalShield.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class MetalShield : BaseShield { [Constructible] - public MetalShield() : base(0x1B7B) - { - } - - public override double DefaultWeight => 6.0; + public MetalShield() : base(0x1B7B) => Weight = 6.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 1; diff --git a/Projects/UOContent/Items/Shields/OrderShield.cs b/Projects/UOContent/Items/Shields/OrderShield.cs index a85a5e6a0..04ba88909 100644 --- a/Projects/UOContent/Items/Shields/OrderShield.cs +++ b/Projects/UOContent/Items/Shields/OrderShield.cs @@ -13,9 +13,9 @@ public partial class OrderShield : BaseShield { LootType = LootType.Newbied; } - } - public override double DefaultWeight => 7.0; + Weight = 7.0; + } public override int BasePhysicalResistance => 1; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/WoodenKiteShield.cs b/Projects/UOContent/Items/Shields/WoodenKiteShield.cs index fbbea1213..3a8874839 100644 --- a/Projects/UOContent/Items/Shields/WoodenKiteShield.cs +++ b/Projects/UOContent/Items/Shields/WoodenKiteShield.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class WoodenKiteShield : BaseShield { [Constructible] - public WoodenKiteShield() : base(0x1B79) - { - } - - public override double DefaultWeight => 5.0; + public WoodenKiteShield() : base(0x1B79) => Weight = 5.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/WoodenShield.cs b/Projects/UOContent/Items/Shields/WoodenShield.cs index 013f908be..46b78874d 100644 --- a/Projects/UOContent/Items/Shields/WoodenShield.cs +++ b/Projects/UOContent/Items/Shields/WoodenShield.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class WoodenShield : BaseShield { [Constructible] - public WoodenShield() : base(0x1B7A) - { - } - - public override double DefaultWeight => 5.0; + public WoodenShield() : base(0x1B7A) => Weight = 5.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs b/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs index 0b3ecca0b..dd6375720 100644 --- a/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs +++ b/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs @@ -11,11 +11,7 @@ namespace Server.Items; public partial class Bedroll : Item { [Constructible] - public Bedroll() : base(0xA57) - { - } - - public override double DefaultWeight => 5.0; + public Bedroll() : base(0xA57) => Weight = 5.0; public override void OnDoubleClick(Mobile from) { @@ -59,26 +55,23 @@ public partial class Bedroll : Item } } - private class LogoutGump : StaticGump + private class LogoutGump : Gump { - private readonly Bedroll _bedroll; + private Bedroll _dedroll; private TimerExecutionToken _closeTimerToken; - private readonly CampfireEntry _entry; + private CampfireEntry _entry; public LogoutGump(CampfireEntry entry, Bedroll bedroll) : base(100, 0) { _entry = entry; - _bedroll = bedroll; + _dedroll = bedroll; Timer.StartTimer(TimeSpan.FromSeconds(10.0), CloseGump, out _closeTimerToken); - } - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddBackground(0, 0, 400, 350, 0xA28); + AddBackground(0, 0, 400, 350, 0xA28); - builder.AddHtmlLocalized(100, 20, 200, 35, 1011015); //
Logging out via camping
+ AddHtmlLocalized(100, 20, 200, 35, 1011015); //
Logging out via camping
/* Using a bedroll in the safety of a camp will log you out of the game safely. * If this is what you wish to do choose CONTINUE and you will be logged out. @@ -86,13 +79,13 @@ public partial class Bedroll : Item * The camp will remain secure for 10 seconds at which time this window will close * and you not be logged out. */ - builder.AddHtmlLocalized(50, 55, 300, 140, 1011016, true, true); + AddHtmlLocalized(50, 55, 300, 140, 1011016, true, true); - builder.AddButton(45, 298, 0xFA5, 0xFA7, 1); - builder.AddHtmlLocalized(80, 300, 110, 35, 1011011); // CONTINUE + AddButton(45, 298, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(80, 300, 110, 35, 1011011); // CONTINUE - builder.AddButton(200, 298, 0xFA5, 0xFA7, 0); - builder.AddHtmlLocalized(235, 300, 110, 35, 1011012); // CANCEL + AddButton(200, 298, 0xFA5, 0xFA7, 0); + AddHtmlLocalized(235, 300, 110, 35, 1011012); // CANCEL } public override void OnResponse(NetState sender, in RelayInfo info) @@ -106,10 +99,10 @@ public partial class Bedroll : Item return; } - if (info.ButtonID == 1 && _entry.Safe && _bedroll.Parent == null && _bedroll.IsAccessibleTo(pm) - && _bedroll.VerifyMove(pm) && _bedroll.Map == pm.Map && pm.InRange(_bedroll, 2)) + if (info.ButtonID == 1 && _entry.Safe && _dedroll.Parent == null && _dedroll.IsAccessibleTo(pm) + && _dedroll.VerifyMove(pm) && _dedroll.Map == pm.Map && pm.InRange(_dedroll, 2)) { - pm.PlaceInBackpack(_bedroll); + pm.PlaceInBackpack(_dedroll); pm.BedrollLogout = true; sender.Disconnect("Used a bedroll to log out."); diff --git a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs index 33f82a2ab..7aa45e4c0 100644 --- a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs +++ b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs @@ -15,11 +15,11 @@ public enum CampfireStatus [SerializationGenerator(0, false)] public partial class Campfire : Item { - public const int SecureRange = 7; + public static readonly int SecureRange = 7; - private static readonly Dictionary _table = []; + private static readonly Dictionary m_Table = new(); - private readonly List _entries; + private readonly List m_Entries; private TimerExecutionToken _timerToken; @@ -28,12 +28,11 @@ public partial class Campfire : Item Movable = false; Light = LightType.Circle300; - _entries = []; + m_Entries = new List(); + Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnTick, out _timerToken); } - public override bool SkipSerialization => true; - [CommandProperty(AccessLevel.GameMaster)] public CampfireStatus Status { @@ -80,12 +79,16 @@ public partial class Campfire : Item } } - public static CampfireEntry GetEntry(Mobile player) => _table.GetValueOrDefault(player); + public static CampfireEntry GetEntry(Mobile player) + { + m_Table.TryGetValue(player, out var value); + return value; + } public static void RemoveEntry(CampfireEntry entry) { - _table.Remove(entry.Player); - entry.Fire._entries.Remove(entry); + m_Table.Remove(entry.Player); + entry.Fire.m_Entries.Remove(entry); } private void OnTick() @@ -111,9 +114,9 @@ public partial class Campfire : Item return; } - for (var i = _entries.Count - 1; i >= 0; i--) + for (var i = m_Entries.Count - 1; i >= 0; i--) { - var entry = _entries[i]; + var entry = m_Entries[i]; if (!entry.Valid || entry.Player.NetState == null) { @@ -132,8 +135,8 @@ public partial class Campfire : Item { var entry = new CampfireEntry(pm, this); - _table[pm] = entry; - _entries.Add(entry); + m_Table[pm] = entry; + m_Entries.Add(entry); pm.SendLocalizedMessage(500620); // You feel it would take a few moments to secure your camp. } @@ -142,25 +145,32 @@ public partial class Campfire : Item private void ClearEntries() { - if (_entries == null) + if (m_Entries == null) { return; } - foreach (var entry in _entries) + foreach (var entry in m_Entries) { - _table.Remove(entry.Player); + m_Table.Remove(entry.Player); } - _entries.Clear(); - _entries.TrimExcess(); + m_Entries.Clear(); + m_Entries.TrimExcess(); } public override void OnAfterDelete() { _timerToken.Cancel(); + ClearEntries(); } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } public class CampfireEntry diff --git a/Projects/UOContent/Items/Skill Items/Camping/Kindling.cs b/Projects/UOContent/Items/Skill Items/Camping/Kindling.cs index aef7d0acf..348cdbb6d 100644 --- a/Projects/UOContent/Items/Skill Items/Camping/Kindling.cs +++ b/Projects/UOContent/Items/Skill Items/Camping/Kindling.cs @@ -11,11 +11,10 @@ public partial class Kindling : Item public Kindling(int amount = 1) : base(0xDE1) { Stackable = true; + Weight = 5.0; Amount = amount; } - public override double DefaultWeight => 5.0; - public override void OnDoubleClick(Mobile from) { if (!VerifyMove(from)) diff --git a/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs b/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs index 53c8077f1..9ff0d8386 100644 --- a/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs +++ b/Projects/UOContent/Items/Skill Items/Carpenter Items/Board.cs @@ -21,14 +21,22 @@ public partial class Board : Item, ICommodity Hue = CraftResources.GetHue(resource); } - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(newValue); + get => _resource; + set + { + if (_resource != value) + { + _resource = value; + Hue = CraftResources.GetHue(value); + + InvalidateProperties(); + this.MarkDirty(); + } + } } int ICommodity.DescriptionNumber diff --git a/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs b/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs index 3713056c0..c33cf6980 100644 --- a/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs +++ b/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs @@ -22,11 +22,7 @@ public partial class TaxidermyKit : Item }; [Constructible] - public TaxidermyKit() : base(0x1EBA) - { - } - - public override double DefaultWeight => 1.0; + public TaxidermyKit() : base(0x1EBA) => Weight = 1.0; public override int LabelNumber => 1041279; // a taxidermy kit @@ -124,7 +120,7 @@ public partial class TaxidermyKit : Item from.SendLocalizedMessage(1042278); from.SendLocalizedMessage(1042602); // You use your kit up making the trophy. - var hunter = fish?.Fisher; + Mobile hunter = fish?.Fisher; var weight = (int)(fish?.Weight ?? 0); fish?.Consume(); diff --git a/Projects/UOContent/Items/Skill Items/Fishing/FishingPole.cs b/Projects/UOContent/Items/Skill Items/Fishing/FishingPole.cs index 3914f28ff..76334722e 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/FishingPole.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/FishingPole.cs @@ -9,24 +9,24 @@ namespace Server.Items; public partial class FishingPole : Item { [Constructible] - public FishingPole() : base(0x0DC0) => Layer = Layer.TwoHanded; - - public override double DefaultWeight => 8.0; + public FishingPole() : base(0x0DC0) + { + Layer = Layer.TwoHanded; + Weight = 8.0; + } public override void OnDoubleClick(Mobile from) { - if (!IsChildOf(from)) + var loc = GetWorldLocation(); + + if (!from.InLOS(loc) || !from.InRange(loc, 2)) { - var loc = GetWorldLocation(); - - if (!from.InLOS(loc) || !from.InRange(loc, 2)) - { - from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 1019045); // I can't reach that - return; - } + from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 1019045); // I can't reach that + } + else + { + Fishing.System.BeginHarvesting(from, this); } - - Fishing.System.BeginHarvesting(from, this); } public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs index bf626c2fd..8f5603a5c 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs @@ -17,22 +17,23 @@ public partial class MessageInABottle : Item [Constructible] public MessageInABottle(Map map, int level) : base(0x099F) { + Weight = 1.0; TargetMap = map ?? Map.Trammel; _level = level; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1041080; // a message in a bottle - [SerializableField(0, allowFieldChange: nameof(AllowLevelChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _level; - - private bool AllowLevelChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int Level { - value = Math.Max(1, Math.Min(value, 4)); - return true; + get => _level; + set + { + _level = Math.Max(1, Math.Min(value, 4)); + this.MarkDirty(); + } } public static int GetRandomLevel() diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs index 5374f8716..0ae8f7956 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs @@ -77,6 +77,8 @@ public partial class SOS : Item [Constructible] public SOS(Map map, int level) : base(0x14EE) { + Weight = 1.0; + _level = level; MessageIndex = Utility.Random(MessageEntries.Length); TargetMap = map ?? Map.Trammel; @@ -85,8 +87,6 @@ public partial class SOS : Item UpdateHue(); } - public override double DefaultWeight => 1.0; - public override int LabelNumber { get @@ -103,20 +103,18 @@ public partial class SOS : Item [CommandProperty(AccessLevel.GameMaster)] public bool IsAncient => _level >= 4; - [SerializableField(0, fieldChanged: nameof(OnLevelChanged), allowFieldChange: nameof(AllowLevelChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _level; - - private bool AllowLevelChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int Level { - value = Math.Max(1, Math.Min(value, 4)); - return true; - } - - private void OnLevelChanged(int oldValue, int newValue) - { - UpdateHue(); + get => _level; + set + { + _level = Math.Max(1, Math.Min(value, 4)); + UpdateHue(); + InvalidateProperties(); + this.MarkDirty(); + } } public void UpdateHue() => Hue = IsAncient ? 0x481 : 0; @@ -129,11 +127,11 @@ public partial class SOS : Item if (UseNewMessages || entry == null) { - MessageGump.DisplayTo(from, TargetMap, TargetLocation); + from.SendGump(new MessageGump(TargetMap, TargetLocation)); } else { - OldMessageGump.DisplayTo(from, entry, TargetMap, TargetLocation, from.Language); + from.SendGump(new OldMessageGump(entry, TargetMap, TargetLocation, from.Language)); } } else @@ -228,24 +226,10 @@ public partial class SOS : Item return water; } - private class MessageGump : StaticGump + private class MessageGump : Gump { - private readonly string _fmt; - - public override bool Singleton => true; - - private MessageGump(string fmt) : base(150, 50) + public MessageGump(Map map, Point3D loc) : base(150, 50) { - _fmt = fmt; - } - - public static void DisplayTo(Mobile from, Map map, Point3D loc) - { - if (from?.NetState == null) - { - return; - } - int xLong = 0, yLat = 0; int xMins = 0, yMins = 0; bool xEast = false, ySouth = false; @@ -260,55 +244,31 @@ public partial class SOS : Item fmt = "?????"; } - from.SendGump(new MessageGump(fmt)); - } + AddPage(0); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 40, 350, 300, 2520); + AddBackground(0, 40, 350, 300, 2520); /* This is a message hastily scribbled by a passenger aboard a sinking ship. * While it is probably too late to save the passengers and crew, * perhaps some treasure went down with the ship! * The message gives the ship's last known sextant co-ordinates. */ - builder.AddHtmlLocalized(30, 80, 285, 160, 1018326, true, true); + AddHtmlLocalized(30, 80, 285, 160, 1018326, true, true); - builder.AddHtmlPlaceholder(35, 240, 230, 20, "fmt"); + AddHtml(35, 240, 230, 20, fmt); - builder.AddButton(35, 265, 4005, 4007, 0); - builder.AddHtmlLocalized(70, 265, 100, 20, 1011036); // OKAY - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - builder.SetStringSlot("fmt", _fmt); + AddButton(35, 265, 4005, 4007, 0); + AddHtmlLocalized(70, 265, 100, 20, 1011036); // OKAY } } - private class OldMessageGump : StaticGump + private class OldMessageGump : Gump { private const int Width = 350; private const int Height = 300; - private readonly string _message; - - public override bool Singleton => true; - - private OldMessageGump(string message) : base((640 - Width) / 2, (480 - Height) / 2) + public OldMessageGump(TextDefinition entry, Map map, Point3D loc, string lang) : base((640 - Width) / 2, (480 - Height) / 2) { - _message = message; - } - - public static void DisplayTo(Mobile from, TextDefinition entry, Map map, Point3D loc, string lang) - { - if (from?.NetState == null || entry == null) - { - return; - } - int xLong = 0, yLat = 0; int xMins = 0, yMins = 0; bool xEast = false, ySouth = false; @@ -323,22 +283,12 @@ public partial class SOS : Item fmt = "?????"; } + AddPage(0); + AddBackground(0, 0, Width, Height, 2520); + // Gumps do not support non-ascii string arguments. Probably why this was not used on OSI var message = entry.Number > 0 ? Localization.Format(entry.Number, lang, $"{fmt}") : string.Format(entry.String, fmt); - - from.SendGump(new OldMessageGump(message)); - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, Width, Height, 2520); - builder.AddHtmlPlaceholder(38, 38, Width - 83, Height - 86, "message"); - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - builder.SetStringSlot("message", _message); + AddHtml(38, 38, Width - 83, Height - 86, message); } } } diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/Sextant.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/Sextant.cs index 4b17d808f..a9a7d4060 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/Sextant.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/Sextant.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class Sextant : Item { [Constructible] - public Sextant() : base(0x1058) - { - } - - public override double DefaultWeight => 2.0; + public Sextant() : base(0x1058) => Weight = 2.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs index 962b8752d..d812f761a 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs @@ -12,9 +12,15 @@ public partial class ShipwreckedItem : Item, IDyable, IShipwreckedItem { public ShipwreckedItem(int itemID) : base(itemID) { - } + var weight = ItemData.Weight; - public override double DefaultWeight => ItemData.Weight >= 255 ? 1.0 : ItemData.Weight; + if (weight >= 255) + { + weight = 1; + } + + Weight = weight; + } public bool Dye(Mobile from, DyeTub sender) { diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs index 7017af81f..98530468e 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs @@ -44,9 +44,11 @@ public partial class SpecialFishingNet : Item private bool _inUse; [Constructible] - public SpecialFishingNet() : base(0x0DCA) => Hue = Utility.RandomDouble() < 0.01 ? _hues.RandomElement() : 0x8A0; - - public override double DefaultWeight => 1.0; + public SpecialFishingNet() : base(0x0DCA) + { + Weight = 1.0; + Hue = Utility.RandomDouble() < 0.01 ? _hues.RandomElement() : 0x8A0; + } public override int LabelNumber => 1041079; // a special fishing net diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs index 1cf680952..52335a4d1 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs @@ -14,12 +14,11 @@ public partial class GargoylesPickaxe : BaseAxe, IUsesRemaining [Constructible] public GargoylesPickaxe(int uses) : base(0xE85 + Utility.Random(2)) { + Weight = 11.0; UsesRemaining = uses; ShowUsesRemaining = true; } - public override double DefaultWeight => 11.0; - public override int LabelNumber => 1041281; // a gargoyle's pickaxe public override HarvestSystem HarvestSystem => Mining.System; diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/ProspectorsTool.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/ProspectorsTool.cs index a5c770206..7ab9e912d 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/ProspectorsTool.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/ProspectorsTool.cs @@ -14,9 +14,11 @@ public partial class ProspectorsTool : BaseBashing, IUsesRemaining private int _usesRemaining; [Constructible] - public ProspectorsTool() : base(0xFB4) => UsesRemaining = 50; - - public override double DefaultWeight => 9.0; + public ProspectorsTool() : base(0xFB4) + { + Weight = 9.0; + UsesRemaining = 50; + } public override int LabelNumber => 1049065; // prospector's tool @@ -59,7 +61,7 @@ public partial class ProspectorsTool : BaseBashing, IUsesRemaining return; } - var system = Mining.System; + HarvestSystem system = Mining.System; if (!system.GetHarvestDetails(from, this, toProspect, out var tileID, out var map, out var loc, out var isLand)) { diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/Shovel.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/Shovel.cs index c2fc94199..73cb310bd 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/Shovel.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/Shovel.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class Shovel : BaseHarvestTool { [Constructible] - public Shovel(int uses = 50) : base(0xF39, uses) - { - } - - public override double DefaultWeight => 5.0; + public Shovel(int uses = 50) : base(0xF39, uses) => Weight = 5.0; public override HarvestSystem HarvestSystem => Mining.System; } diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs index d3d42ef41..7b8424940 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs @@ -9,13 +9,12 @@ public partial class SturdyPickaxe : BaseAxe, IUsesRemaining [Constructible] public SturdyPickaxe(int uses = 180) : base(0xE86) { + Weight = 11.0; Hue = 0x973; UsesRemaining = uses; ShowUsesRemaining = true; } - public override double DefaultWeight => 11.0; - public override int LabelNumber => 1045126; // sturdy pickaxe public override HarvestSystem HarvestSystem => Mining.System; diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyShovel.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyShovel.cs index acf576f22..94c149ea9 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyShovel.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyShovel.cs @@ -7,9 +7,12 @@ namespace Server.Items; public partial class SturdyShovel : BaseHarvestTool { [Constructible] - public SturdyShovel(int uses = 180) : base(0xF39, uses) => Hue = 0x973; + public SturdyShovel(int uses = 180) : base(0xF39, uses) + { + Weight = 5.0; + Hue = 0x973; + } - public override double DefaultWeight => 5.0; public override int LabelNumber => 1045125; // sturdy shovel public override HarvestSystem HarvestSystem => Mining.System; } diff --git a/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs b/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs index 2a06f535a..5b372abd5 100644 --- a/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs +++ b/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs @@ -20,22 +20,29 @@ public partial class Log : Item, ICommodity, IAxe public Log(CraftResource resource, int amount) : base(0x1BDD) { Stackable = true; + Weight = 2.0; Amount = amount; _resource = resource; Hue = CraftResources.GetHue(resource); } - public override double DefaultWeight => 2.0; - - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(newValue); + get => _resource; + set + { + if (_resource != value) + { + _resource = value; + Hue = CraftResources.GetHue(value); + + InvalidateProperties(); + this.MarkDirty(); + } + } } public virtual bool Axe(Mobile from, BaseAxe axe) => TryCreateBoards(from, 0, new Board()); diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/BlankScroll.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/BlankScroll.cs index 7f5876308..f5e0117cb 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/BlankScroll.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/BlankScroll.cs @@ -9,11 +9,10 @@ public partial class BlankScroll : Item, ICommodity public BlankScroll(int amount = 1) : base(0xEF3) { Stackable = true; + Weight = 1.0; Amount = amount; } - public override double DefaultWeight => 1.0; - int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => Core.ML; } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/Bottle.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/Bottle.cs index d12dc873b..7a7ccbd30 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/Bottle.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/Bottle.cs @@ -9,11 +9,10 @@ public partial class Bottle : Item, ICommodity public Bottle(int amount = 1) : base(0xF0E) { Stackable = true; + Weight = 1.0; Amount = amount; } - public override double DefaultWeight => 1.0; - int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => Core.ML; } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/Moongate.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/Moongate.cs index 990c2622c..b6b9a01c8 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/Moongate.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/Moongate.cs @@ -96,7 +96,7 @@ public partial class Moongate : Item { mobile.SendLocalizedMessage(1049543); // You decide against traveling to Felucca while you are still young. } - else if (m.Murderer && TargetMap != Map.Felucca || + else if (m.Kills >= 5 && TargetMap != Map.Felucca || TargetMap == Map.Tokuno && (flags & ClientFlags.Tokuno) == 0 || TargetMap == Map.Malas && (flags & ClientFlags.Malas) == 0 || TargetMap == Map.Ilshenar && (flags & ClientFlags.Ilshenar) == 0 || @@ -157,7 +157,7 @@ public partial class Moongate : Item from.SendSound(0x20E, from); } - MoongateConfirmGump.DisplayTo(from, this); + from.SendGump(new MoongateConfirmGump(from, this)); } else { @@ -301,80 +301,90 @@ public partial class ConfirmationMoongate : Moongate } } -public class MoongateConfirmGump : DynamicGump +public class MoongateConfirmGump : Gump { - private readonly Mobile _from; - private readonly Moongate _gate; + private Mobile _from; + private Moongate _gate; public override bool Singleton => true; - private MoongateConfirmGump(Mobile from, Moongate gate) : base(Core.AOS ? 110 : 20, Core.AOS ? 100 : 30) + public MoongateConfirmGump(Mobile from, Moongate gate) : base(Core.AOS ? 110 : 20, Core.AOS ? 100 : 30) { _from = from; _gate = gate; - } - - public static void DisplayTo(Mobile from, Moongate gate) - { - if (from?.NetState == null || gate == null || gate.Deleted) - { - return; - } - - from.SendGump(new MoongateConfirmGump(from, gate)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); if (Core.AOS) { - builder.SetNoClose(); + Closable = false; - builder.AddBackground(0, 0, 420, 280, 5054); + AddPage(0); - builder.AddImageTiled(10, 10, 400, 20, 2624); - builder.AddAlphaRegion(10, 10, 400, 20); + AddBackground(0, 0, 420, 280, 5054); - builder.AddHtmlLocalized(10, 10, 400, 20, 1062051, 30720); // Gate Warning + AddImageTiled(10, 10, 400, 20, 2624); + AddAlphaRegion(10, 10, 400, 20); - builder.AddImageTiled(10, 40, 400, 200, 2624); - builder.AddAlphaRegion(10, 40, 400, 200); + AddHtmlLocalized(10, 10, 400, 20, 1062051, 30720); // Gate Warning - if (_from.Map != Map.Felucca && _gate.TargetMap == Map.Felucca && _gate.ShowFeluccaWarning) + AddImageTiled(10, 40, 400, 200, 2624); + AddAlphaRegion(10, 40, 400, 200); + + if (from.Map != Map.Felucca && gate.TargetMap == Map.Felucca && gate.ShowFeluccaWarning) { - // This Gate goes to Felucca... Continue to enter the gate, Cancel to stay here - builder.AddHtmlLocalized(10, 40, 400, 200, 1062050, 32512, false, true); + AddHtmlLocalized( + 10, + 40, + 400, + 200, + 1062050, // This Gate goes to Felucca... Continue to enter the gate, Cancel to stay here + 32512, + false, + true + ); } else { - // Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here - builder.AddHtmlLocalized(10, 40, 400, 200, 1062049, 32512, false, true); + AddHtmlLocalized( + 10, + 40, + 400, + 200, + 1062049, // Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here + 32512, + false, + true + ); } - builder.AddImageTiled(10, 250, 400, 20, 2624); - builder.AddAlphaRegion(10, 250, 400, 20); + AddImageTiled(10, 250, 400, 20, 2624); + AddAlphaRegion(10, 250, 400, 20); - builder.AddButton(10, 250, 4005, 4007, 1); - builder.AddHtmlLocalized(40, 250, 170, 20, 1011036, 32767); // OKAY + AddButton(10, 250, 4005, 4007, 1); + AddHtmlLocalized(40, 250, 170, 20, 1011036, 32767); // OKAY - builder.AddButton(210, 250, 4005, 4007, 0); - builder.AddHtmlLocalized(240, 250, 170, 20, 1011012, 32767); // CANCEL + AddButton(210, 250, 4005, 4007, 0); + AddHtmlLocalized(240, 250, 170, 20, 1011012, 32767); // CANCEL } else { - builder.AddBackground(0, 0, 420, 400, 5054); - builder.AddBackground(10, 10, 400, 380, 3000); + AddPage(0); - // Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here - builder.AddHtmlLocalized(20, 40, 380, 60, 1062049); + AddBackground(0, 0, 420, 400, 5054); + AddBackground(10, 10, 400, 380, 3000); - builder.AddHtmlLocalized(55, 110, 290, 20, 1011012); // CANCEL - builder.AddButton(20, 110, 4005, 4007, 0); + AddHtmlLocalized( + 20, + 40, + 380, + 60, + 1062049 // Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here + ); - builder.AddHtmlLocalized(55, 140, 290, 40, 1011011); // CONTINUE - builder.AddButton(20, 140, 4005, 4007, 1); + AddHtmlLocalized(55, 110, 290, 20, 1011012); // CANCEL + AddButton(20, 110, 4005, 4007, 0); + + AddHtmlLocalized(55, 140, 290, 40, 1011011); // CONTINUE + AddButton(20, 140, 4005, 4007, 1); } } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs index a989655aa..1a6239322 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs @@ -12,17 +12,11 @@ public partial class PotionKeg : Item TileData.ItemTable[0x1940].Height = 4; } - [InvalidateProperties] - [SerializableField(0)] - [SerializedCommandProperty(AccessLevel.GameMaster)] + [InvalidateProperties] [SerializableField(0)] [SerializedCommandProperty(AccessLevel.GameMaster)] private PotionEffect _type; [Constructible] - public PotionKeg() : base(0x1940) - { - } - - public override double DefaultWeight => 20 + Math.Clamp(_held, 0, 100) * 0.8; + public PotionKeg() : base(0x1940) => UpdateWeight(); [SerializableProperty(1)] [CommandProperty(AccessLevel.GameMaster)] @@ -33,10 +27,8 @@ public partial class PotionKeg : Item { if (_held != value) { - var oldPileWeight = PileWeight; _held = value; - var newPileWeight = PileWeight; - UpdateTotal(this, TotalType.Weight, newPileWeight - oldPileWeight); + UpdateWeight(); InvalidateProperties(); this.MarkDirty(); } @@ -47,23 +39,22 @@ public partial class PotionKeg : Item { get { - if (_held <= 0) + if (_held > 0 && (int)_type >= (int)PotionEffect.Conflagration) { - return 1041641; + return 1072658 + (int)_type - (int)PotionEffect.Conflagration; } - return _type switch - { - < PotionEffect.Nightsight or > PotionEffect.FlintsPungentBrew => 1041619, - PotionEffect.FlintsPungentBrew => 1113608, - >= PotionEffect.Parasitic => 1080069 + (int)_type - (int)PotionEffect.Parasitic, - PotionEffect.Invisibility => 1080071, - >= PotionEffect.Conflagration => 1072658 + (int)_type - (int)PotionEffect.Conflagration, - _ => 1041620 + (int)_type - }; + return _held > 0 ? 1041620 + (int)_type : 1041641; } } + public virtual void UpdateWeight() + { + var held = Math.Max(0, Math.Min(_held, 100)); + + Weight = 20 + held * 80 / 100; + } + private void Deserialize(IGenericReader reader, int version) { _type = (PotionEffect)reader.ReadInt(); @@ -259,9 +250,6 @@ public partial class PotionKeg : Item PotionEffect.ConflagrationGreater => new GreaterConflagrationPotion(), PotionEffect.ConfusionBlast => new ConfusionBlastPotion(), PotionEffect.ConfusionBlastGreater => new GreaterConfusionBlastPotion(), - PotionEffect.Invisibility => new InvisibilityPotion(), - PotionEffect.Parasitic => new ParasiticPotion(), - PotionEffect.Darkglow => new DarkglowPotion(), _ => new NightSightPotion() }; @@ -292,9 +280,6 @@ public partial class PotionKeg : Item _ when type == typeof(GreaterConflagrationPotion) => PotionEffect.ConflagrationGreater, _ when type == typeof(ConfusionBlastPotion) => PotionEffect.ConfusionBlast, _ when type == typeof(GreaterConfusionBlastPotion) => PotionEffect.ConfusionBlastGreater, - _ when type == typeof(InvisibilityPotion) => PotionEffect.Invisibility, - _ when type == typeof(ParasiticPotion) => PotionEffect.Parasitic, - _ when type == typeof(DarkglowPotion) => PotionEffect.Darkglow, - _ => PotionEffect.Nightsight + _ /* when type == typeof(NightSightPotion) */ => PotionEffect.Nightsight }; } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs index 14db7ac1e..d33d5ce7a 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs @@ -21,11 +21,10 @@ public partial class RecallRune : Item [Constructible] public RecallRune() : base(0x1F14) { + Weight = 1.0; CalculateHue(); } - public override double DefaultWeight => 1.0; - [SerializableProperty(0)] [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] public BaseHouse House @@ -47,24 +46,36 @@ public partial class RecallRune : Item } } - [SerializableField(2, fieldChanged: nameof(OnMarkedChanged))] - [SerializedCommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] - [InvalidateProperties] - private bool _marked; - - private void OnMarkedChanged(bool oldValue, bool newValue) + [SerializableProperty(2)] + [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] + public bool Marked { - CalculateHue(); + get => _marked; + set + { + if (_marked != value) + { + _marked = value; + CalculateHue(); + InvalidateProperties(); + } + } } - [SerializableField(4, fieldChanged: nameof(OnTargetMapChanged))] - [SerializedCommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] - [InvalidateProperties] - private Map _targetMap; - - private void OnTargetMapChanged(Map oldValue, Map newValue) + [SerializableProperty(4)] + [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] + public Map TargetMap { - CalculateHue(); + get => _targetMap; + set + { + if (_targetMap != value) + { + _targetMap = value; + CalculateHue(); + InvalidateProperties(); + } + } } private void Deserialize(IGenericReader reader, int version) diff --git a/Projects/UOContent/Items/Skill Items/Magical/Potions/BasePotion.cs b/Projects/UOContent/Items/Skill Items/Magical/Potions/BasePotion.cs index dbbec18a0..4c62b3771 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Potions/BasePotion.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Potions/BasePotion.cs @@ -35,8 +35,7 @@ public enum PotionEffect ConfusionBlastGreater, Invisibility, Parasitic, - Darkglow, - FlintsPungentBrew + Darkglow } [SerializationGenerator(2, false)] @@ -51,10 +50,9 @@ public abstract partial class BasePotion : Item, ICraftable, ICommodity _potionEffect = effect; Stackable = Core.ML; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1041314 + (int)_potionEffect; public virtual bool RequireFreeHand => true; @@ -86,8 +84,7 @@ public abstract partial class BasePotion : Item, ICraftable, ICommodity return 1; } - using var queue = pack.EnumerateItemsByType(); - foreach (var keg in queue) + foreach (var keg in pack.EnumerateItemsByType()) { if (keg.Held is <= 0 or >= 100) { diff --git a/Projects/UOContent/Items/Skill Items/Magical/Potions/InvisibilityPotion.cs b/Projects/UOContent/Items/Skill Items/Magical/Potions/InvisibilityPotion.cs index e7b631a7c..d3a2e4e5f 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Potions/InvisibilityPotion.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Potions/InvisibilityPotion.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using ModernUO.Serialization; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Items; @@ -59,11 +57,8 @@ public partial class InvisibilityPotion : BasePotion m.Hidden = true; - if (m is PlayerMobile pm) - { - pm.RemoveBuff(BuffIcon.HidingAndOrStealth); - pm.AddBuff(new BuffInfo(BuffIcon.Invisibility, 1075825)); // Invisibility/Invisible - } + BuffInfo.RemoveBuff(m, BuffIcon.HidingAndOrStealth); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Invisibility, 1075825)); // Invisibility/Invisible RemoveTimer(m); diff --git a/Projects/UOContent/Items/Skill Items/Magical/Potions/Poison Potions/DarkglowPotion.cs b/Projects/UOContent/Items/Skill Items/Magical/Potions/Poison Potions/DarkglowPotion.cs index 92537c1f9..03cc56966 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Potions/Poison Potions/DarkglowPotion.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Potions/Poison Potions/DarkglowPotion.cs @@ -8,7 +8,8 @@ public partial class DarkglowPotion : BasePoisonPotion [Constructible] public DarkglowPotion() : base(PotionEffect.Darkglow) => Hue = 0x96; - public override Poison Poison => Poison.GreaterDarkglow; + /* public override Poison Poison => Poison.DarkGlow; // MUST be restored when prerequisites are done */ + public override Poison Poison => Poison.Greater; public override double MinPoisoningSkill => 95.0; public override double MaxPoisoningSkill => 100.0; diff --git a/Projects/UOContent/Items/Skill Items/Magical/Potions/Poison Potions/ParasiticPotion.cs b/Projects/UOContent/Items/Skill Items/Magical/Potions/Poison Potions/ParasiticPotion.cs index 08f481176..460531c9e 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Potions/Poison Potions/ParasiticPotion.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Potions/Poison Potions/ParasiticPotion.cs @@ -8,10 +8,9 @@ public partial class ParasiticPotion : BasePoisonPotion [Constructible] public ParasiticPotion() : base(PotionEffect.Parasitic) => Hue = 0x17C; - public override Poison Poison => Poison.DeadlyParasitic; + /* public override Poison Poison => Poison.Parasitic; // MUST be restored when prerequisites are done */ + public override Poison Poison => Poison.Greater; public override double MinPoisoningSkill => 95.0; public override double MaxPoisoningSkill => 100.0; - - public override int LabelNumber => 1072848; // Parasitic Poison } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs b/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs index f9ecdfa22..f8274acfa 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs @@ -39,12 +39,10 @@ public partial class Runebook : Item, ISecurable, ICraftable [SerializedCommandProperty(AccessLevel.GameMaster)] private string _description; - [InvalidateProperties] [SerializableField(5)] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _curCharges; - [InvalidateProperties] [SerializableField(6)] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxCharges; @@ -60,6 +58,7 @@ public partial class Runebook : Item, ISecurable, ICraftable [Constructible] public Runebook(int maxCharges) : base(Core.AOS ? 0x22C5 : 0xEFA) { + Weight = Core.SE ? 1.0 : 3.0; LootType = LootType.Blessed; Hue = 0x461; @@ -71,8 +70,6 @@ public partial class Runebook : Item, ISecurable, ICraftable _level = SecureLevel.CoOwners; } - public override double DefaultWeight => Core.SE ? 1.0 : 3.0; - [CommandProperty(AccessLevel.GameMaster)] public DateTime NextUse { get; set; } @@ -371,27 +368,27 @@ public partial class RunebookEntry private Runebook _runebook; [SerializableField(0)] - [SaveFlag(nameof(ShouldSerializeHouse))] private BaseHouse _house; + [SerializableFieldSaveFlag(0)] public bool ShouldSerializeHouse() => _house?.Deleted == false; [SerializableField(1)] - [SaveFlag(nameof(ShouldSerializeLocation))] private Point3D _location; + [SerializableFieldSaveFlag(1)] public bool ShouldSerializeLocation() => _house?.Deleted != false; [SerializableField(2)] - [SaveFlag(nameof(ShouldSerializeMap))] private Map _map; + [SerializableFieldSaveFlag(2)] public bool ShouldSerializeMap() => _house?.Deleted != false; [SerializableField(3)] - [SaveFlag(nameof(ShouldSerializeDesc))] private string _description; + [SerializableFieldSaveFlag(3)] public bool ShouldSerializeDesc() => _house?.Deleted != false; public RunebookEntry( diff --git a/Projects/UOContent/Items/Skill Items/Magical/Scrolls/SpellScroll.cs b/Projects/UOContent/Items/Skill Items/Magical/Scrolls/SpellScroll.cs index 818864edd..097a0a22d 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Scrolls/SpellScroll.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Scrolls/SpellScroll.cs @@ -16,13 +16,12 @@ public partial class SpellScroll : Item, ICommodity public SpellScroll(int spellID, int itemID, int amount = 1) : base(itemID) { Stackable = true; + Weight = 1.0; Amount = amount; _spellID = spellID; } - public override double DefaultWeight => 1.0; - int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => Core.ML; diff --git a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs index 9447fddfa..901a9d82e 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs @@ -4,11 +4,9 @@ using ModernUO.Serialization; using Server.Commands; using Server.Engines.Craft; using Server.Ethics; -using Server.Mobiles; using Server.Multis; using Server.Network; using Server.Spells; -using Server.Systems.FeatureFlags; using Server.Targeting; namespace Server.Items; @@ -118,6 +116,7 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem _attributes = new AosAttributes(this); _skillBonuses = new AosSkillBonuses(this); + Weight = 3.0; Layer = Layer.OneHanded; LootType = LootType.Blessed; @@ -125,27 +124,34 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem Content = content; } - public override double DefaultWeight => 3.0; - public override bool DisplayWeight => false; public virtual SpellbookType SpellbookType => SpellbookType.Regular; public virtual int BookOffset => 0; public virtual int BookCount => 64; - [SerializableField(7, fieldChanged: nameof(OnContentChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private ulong _content; - - private void OnContentChanged(ulong oldValue, ulong newValue) + [CommandProperty(AccessLevel.GameMaster)] + [SerializableProperty(7)] + public ulong Content { - // This assignment will mark it as dirty - SpellCount = 0; - while (newValue > 0) + get => _content; + set { - _spellCount += (int)(newValue & 0x1); - newValue >>= 1; + if (_content != value) + { + _content = value; + + // This assignment will mark it as dirty + SpellCount = 0; + + while (value > 0) + { + _spellCount += (int)(value & 0x1); + value >>= 1; + } + + InvalidateProperties(); + } } } @@ -299,14 +305,6 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem return; // They are customizing } - // Early rejection by spell ID before instantiation - if (from is PlayerMobile { AccessLevel: < AccessLevel.Administrator } - && FeatureFlagManager.IsSpellBlocked(spellID, out var reason)) - { - from.SendMessage(0x22, reason); - return; - } - var book = item as Spellbook; if (book?.HasSpell(spellID) != true) @@ -498,7 +496,7 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem public override bool OnDragDrop(Mobile from, Item dropped) { - if (dropped is not SpellScroll scroll) + if (dropped is not SpellScroll { Amount: 1 } scroll) { return false; } @@ -525,10 +523,10 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem InvalidateProperties(); - scroll.Consume(); + scroll.Delete(); from.SendSound(0x249, GetWorldLocation()); - return scroll.Deleted; + return true; } return false; @@ -668,7 +666,127 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem } } - _attributes.GetProperties(list); + int prop; + + if ((prop = _attributes.WeaponDamage) != 0) + { + list.Add(1060401, prop); // damage increase ~1_val~% + } + + if ((prop = _attributes.DefendChance) != 0) + { + list.Add(1060408, prop); // defense chance increase ~1_val~% + } + + if ((prop = _attributes.BonusDex) != 0) + { + list.Add(1060409, prop); // dexterity bonus ~1_val~ + } + + if ((prop = _attributes.EnhancePotions) != 0) + { + list.Add(1060411, prop); // enhance potions ~1_val~% + } + + if ((prop = _attributes.CastRecovery) != 0) + { + list.Add(1060412, prop); // faster cast recovery ~1_val~ + } + + if ((prop = _attributes.CastSpeed) != 0) + { + list.Add(1060413, prop); // faster casting ~1_val~ + } + + if ((prop = _attributes.AttackChance) != 0) + { + list.Add(1060415, prop); // hit chance increase ~1_val~% + } + + if ((prop = _attributes.BonusHits) != 0) + { + list.Add(1060431, prop); // hit point increase ~1_val~ + } + + if ((prop = _attributes.BonusInt) != 0) + { + list.Add(1060432, prop); // intelligence bonus ~1_val~ + } + + if ((prop = _attributes.LowerManaCost) != 0) + { + list.Add(1060433, prop); // lower mana cost ~1_val~% + } + + if ((prop = _attributes.LowerRegCost) != 0) + { + list.Add(1060434, prop); // lower reagent cost ~1_val~% + } + + if ((prop = _attributes.Luck) != 0) + { + list.Add(1060436, prop); // luck ~1_val~ + } + + if ((prop = _attributes.BonusMana) != 0) + { + list.Add(1060439, prop); // mana increase ~1_val~ + } + + if ((prop = _attributes.RegenMana) != 0) + { + list.Add(1060440, prop); // mana regeneration ~1_val~ + } + + if (_attributes.NightSight != 0) + { + list.Add(1060441); // night sight + } + + if ((prop = _attributes.ReflectPhysical) != 0) + { + list.Add(1060442, prop); // reflect physical damage ~1_val~% + } + + if ((prop = _attributes.RegenStam) != 0) + { + list.Add(1060443, prop); // stamina regeneration ~1_val~ + } + + if ((prop = _attributes.RegenHits) != 0) + { + list.Add(1060444, prop); // hit point regeneration ~1_val~ + } + + if (_attributes.SpellChanneling != 0) + { + list.Add(1060482); // spell channeling + } + + if ((prop = _attributes.SpellDamage) != 0) + { + list.Add(1060483, prop); // spell damage increase ~1_val~% + } + + if ((prop = _attributes.BonusStam) != 0) + { + list.Add(1060484, prop); // stamina increase ~1_val~ + } + + if ((prop = _attributes.BonusStr) != 0) + { + list.Add(1060485, prop); // strength bonus ~1_val~ + } + + if ((prop = _attributes.WeaponSpeed) != 0) + { + list.Add(1060486, prop); // swing speed increase ~1_val~% + } + + if (Core.ML && (prop = _attributes.IncreasedKarmaLoss) != 0) + { + list.Add(1075210, prop); // Increased Karma Loss ~1val~% + } list.Add(1042886, _spellCount); // ~1_NUMBERS_OF_SPELLS~ Spells } diff --git a/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs b/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs index bcf59d04d..68f9b3580 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/Bandage.cs @@ -4,7 +4,6 @@ using ModernUO.Serialization; using Server.Engines.ConPVP; using Server.Factions; using Server.Gumps; -using Server.Misc; using Server.Mobiles; using Server.Targeting; @@ -444,11 +443,6 @@ public class BandageContext : Timer Healer.CheckSkill(secondarySkill, 0.0, 120.0); Healer.CheckSkill(primarySkill, 0.0, 120.0); } - - if (Healer != Patient && Patient.Karma > 0) - { - Titles.AwardKarma(Healer, Patient.Karma / 5, true); - } } public static BandageContext BeginHeal(Mobile healer, Mobile patient) @@ -472,10 +466,6 @@ public class BandageContext : Timer { healer.SendLocalizedMessage(501042); // Target cannot be resurrected at that location. } - else if ((healer as PlayerMobile)?.Young == true && (patient as PlayerMobile)?.Young == false) - { - healer.SendLocalizedMessage(500952); // As a young player, you may not use beneficial skills on older players. - } else if (healer.CanBeBeneficial(patient, true, true)) { healer.DoBeneficial(patient); diff --git a/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs b/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs index b2c6f0c6e..68d293405 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs @@ -10,9 +10,11 @@ namespace Server.Items; public partial class FireHorn : Item { [Constructible] - public FireHorn() : base(0xFC7) => Hue = 0x466; - - public override double DefaultWeight => 1.0; + public FireHorn() : base(0xFC7) + { + Hue = 0x466; + Weight = 1.0; + } public override int LabelNumber => 1060456; // fire horn diff --git a/Projects/UOContent/Items/Skill Items/Misc/RecipeScroll.cs b/Projects/UOContent/Items/Skill Items/Misc/RecipeScroll.cs index 95f0776b0..36278b63e 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/RecipeScroll.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/RecipeScroll.cs @@ -59,29 +59,28 @@ public partial class RecipeScroll : Item var r = Recipe; - if (r == null || from is not PlayerMobile pm) + if (r != null && from is PlayerMobile pm) { - return; - } + if (!pm.HasRecipe(r)) + { + var chance = r.CraftItem.GetSuccessChance(pm, null, r.CraftSystem, false, out var allRequiredSkills); - if (pm.HasRecipe(r)) - { - pm.SendLocalizedMessage(1073427); // You already know this recipe. - return; - } - - var chance = r.CraftItem.GetSuccessChance(pm, null, r.CraftSystem, false, out var allRequiredSkills); - - if (allRequiredSkills && chance >= 0.0) - { - // You have learned a new recipe: ~1_RECIPE~ - pm.SendLocalizedMessage(1073451, r.TextDefinition.ToString()); - pm.AcquireRecipe(r); - Delete(); - } - else - { - pm.SendLocalizedMessage(1044153); // You don't have the required skills to attempt this item. + if (allRequiredSkills && chance >= 0.0) + { + // You have learned a new recipe: ~1_RECIPE~ + pm.SendLocalizedMessage(1073451, r.TextDefinition.ToString()); + pm.AcquireRecipe(r); + Delete(); + } + else + { + pm.SendLocalizedMessage(1044153); // You don't have the required skills to attempt this item. + } + } + else + { + pm.SendLocalizedMessage(1073427); // You already know this recipe. + } } } } diff --git a/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs b/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs index e4d486d10..dbb8df68c 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/RepairDeed.cs @@ -62,15 +62,17 @@ public partial class RepairDeed : Item public override bool DisplayLootType => false; - [SerializableField(1, allowFieldChange: nameof(AllowSkillLevelChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private double _skillLevel; - - private bool AllowSkillLevelChange(ref double value) + [CommandProperty(AccessLevel.GameMaster)] + [SerializableProperty(1)] + public double SkillLevel { - value = Math.Clamp(value, 0, 120.0); - return true; + get => _skillLevel; + set + { + _skillLevel = Math.Clamp(value, 0, 120.0); + InvalidateProperties(); + this.MarkDirty(); + } } public override void AddNameProperty(IPropertyList list) diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/BambooFlute.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/BambooFlute.cs index 43d6ad26f..7bf75926b 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/BambooFlute.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/BambooFlute.cs @@ -6,9 +6,5 @@ namespace Server.Items; public partial class BambooFlute : BaseInstrument { [Constructible] - public BambooFlute() : base(0x2805, 0x504, 0x503) - { - } - - public override double DefaultWeight => 2.0; + public BambooFlute() : base(0x2805, 0x504, 0x503) => Weight = 2.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs index f7716a4a1..5024891a8 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/BaseInstrument.cs @@ -52,8 +52,8 @@ public abstract partial class BaseInstrument : Item, ICraftable, ISlayer _usesRemaining = Utility.RandomMinMax(InitMinUses, InitMaxUses); } - public virtual int InitMinUses => Core.UOR ? 350 : 100; - public virtual int InitMaxUses => Core.UOR ? 450 : 150; + public virtual int InitMinUses => 350; + public virtual int InitMaxUses => 450; public virtual TimeSpan ChargeReplenishRate => TimeSpan.FromMinutes(5.0); @@ -74,13 +74,16 @@ public abstract partial class BaseInstrument : Item, ICraftable, ISlayer } } - [SerializableField(1, fieldChanged: nameof(OnLastReplenishedChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private DateTime _lastReplenished; - - private void OnLastReplenishedChanged(DateTime oldValue, DateTime newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public DateTime LastReplenished { - CheckReplenishUses(); + get => _lastReplenished; + set + { + _lastReplenished = value; + CheckReplenishUses(); + } } [SerializableProperty(3)] @@ -113,7 +116,7 @@ public abstract partial class BaseInstrument : Item, ICraftable, ISlayer } } - public virtual int OnCraft( + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) @@ -125,19 +128,6 @@ public abstract partial class BaseInstrument : Item, ICraftable, ISlayer Crafter = from?.RawName; } - // Publish 16 change - if (Core.LBR) - { - if (Quality == InstrumentQuality.Exceptional) - { - UsesRemaining += 100; - } - } - else if (Quality != InstrumentQuality.Regular) - { - UsesRemaining += (int)(UsesRemaining * ((int)Quality - 1) * 0.1); - } - return quality; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/Drums.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/Drums.cs index e1021ae5a..893768a5a 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/Drums.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/Drums.cs @@ -6,9 +6,5 @@ namespace Server.Items; public partial class Drums : BaseInstrument { [Constructible] - public Drums() : base(0xE9C, 0x38, 0x39) - { - } - - public override double DefaultWeight => 4.0; + public Drums() : base(0xE9C, 0x38, 0x39) => Weight = 4.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/Harp.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/Harp.cs index 2af28f827..69e5609dc 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/Harp.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/Harp.cs @@ -6,9 +6,5 @@ namespace Server.Items; public partial class Harp : BaseInstrument { [Constructible] - public Harp() : base(0xEB1, 0x43, 0x44) - { - } - - public override double DefaultWeight => 35.0; + public Harp() : base(0xEB1, 0x43, 0x44) => Weight = 35.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/LapHarp.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/LapHarp.cs index 60881f059..ebd96445c 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/LapHarp.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/LapHarp.cs @@ -6,9 +6,5 @@ namespace Server.Items; public partial class LapHarp : BaseInstrument { [Constructible] - public LapHarp() : base(0xEB2, 0x45, 0x46) - { - } - - public override double DefaultWeight => 10.0; + public LapHarp() : base(0xEB2, 0x45, 0x46) => Weight = 10.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/Lute.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/Lute.cs index 1a74cac3b..308394492 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/Lute.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/Lute.cs @@ -6,9 +6,5 @@ namespace Server.Items; public partial class Lute : BaseInstrument { [Constructible] - public Lute() : base(0xEB3, 0x4C, 0x4D) - { - } - - public override double DefaultWeight => 5.0; + public Lute() : base(0xEB3, 0x4C, 0x4D) => Weight = 5.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/Tambourine.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/Tambourine.cs index e2d77530a..3e768b929 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/Tambourine.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/Tambourine.cs @@ -6,9 +6,5 @@ namespace Server.Items; public partial class Tambourine : BaseInstrument { [Constructible] - public Tambourine() : base(0xE9D, 0x52, 0x53) - { - } - - public override double DefaultWeight => 1.0; + public Tambourine() : base(0xE9D, 0x52, 0x53) => Weight = 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/TambourineTassel.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/TambourineTassel.cs index ed54c07c9..7f4a5a951 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/TambourineTassel.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/TambourineTassel.cs @@ -6,9 +6,5 @@ namespace Server.Items; public partial class TambourineTassel : BaseInstrument { [Constructible] - public TambourineTassel() : base(0xE9E, 0x52, 0x53) - { - } - - public override double DefaultWeight => 1.0; + public TambourineTassel() : base(0xE9E, 0x52, 0x53) => Weight = 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/EggBomb.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/EggBomb.cs index 31170713b..d766555bb 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/EggBomb.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/EggBomb.cs @@ -11,10 +11,9 @@ public partial class EggBomb : Item { // Item ID should be 0x2809 - Temporary solution for clients 7.0.0.0 and up Stackable = Core.ML; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1030249; public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs index 5cd7270ff..6b80d40da 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs @@ -26,9 +26,11 @@ public partial class Fukiya : Item, INinjaWeapon private int _poisonCharges; [Constructible] - public Fukiya() : base(0x27AA) => Layer = Layer.OneHanded; - - public override double DefaultWeight => 1.0; + public Fukiya() : base(0x27AA) + { + Weight = 4.0; + Layer = Layer.OneHanded; + } public virtual int WrongAmmoMessage => 1063329; // You can only load fukiya darts public virtual int NoFreeHandMessage => 1063327; // You must have a free hand to use a fukiya. diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs index ad5158eff..82a7950ee 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs @@ -23,9 +23,15 @@ public partial class FukiyaDarts : Item, ICraftable, INinjaAmmo private int _poisonCharges; [Constructible] - public FukiyaDarts(int amount = 1) : base(0x2806) => _usesRemaining = amount; + public FukiyaDarts(int amount = 1) : base(0x2806) + { + Weight = 1.0; + _usesRemaining = amount; + } - public override double DefaultWeight => 1.0; + public FukiyaDarts(Serial serial) : base(serial) + { + } public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs index 7cfd9ef05..a62d7ad51 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs @@ -26,9 +26,11 @@ public partial class LeatherNinjaBelt : BaseWaist, INinjaWeapon private int _poisonCharges; [Constructible] - public LeatherNinjaBelt() : base(0x2790) => Layer = Layer.Waist; - - public override double DefaultWeight => 1.0; + public LeatherNinjaBelt() : base(0x2790) + { + Weight = 1.0; + Layer = Layer.Waist; + } public override CraftResource DefaultResource => CraftResource.RegularLeather; diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/NinjaWeapons.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/NinjaWeapons.cs index 01d6cf67d..498cd8c02 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/NinjaWeapons.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/NinjaWeapons.cs @@ -289,7 +289,7 @@ public static class NinjaWeapon { if (EvilOmenSpell.EndEffect(target)) { - target.ApplyPoison(from, Poison.IncreaseLevel(weapon.Poison)); + target.ApplyPoison(from, Poison.GetPoison(weapon.Poison.Level + 1)); } else { diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs index 82bfc59e9..7739d03d8 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs @@ -24,9 +24,11 @@ public partial class Shuriken : Item, ICraftable, INinjaAmmo private int _poisonCharges; [Constructible] - public Shuriken(int amount = 1) : base(0x27AC) => _usesRemaining = amount; - - public override double DefaultWeight => 1.0; + public Shuriken(int amount = 1) : base(0x27AC) + { + Weight = 1.0; + _usesRemaining = amount; + } public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/SmokeBomb.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/SmokeBomb.cs index d2adb24a1..7ea8c07bc 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/SmokeBomb.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/SmokeBomb.cs @@ -7,9 +7,11 @@ namespace Server.Items; public partial class SmokeBomb : Item { [Constructible] - public SmokeBomb() : base(0x2808) => Stackable = Core.ML; - - public override double DefaultWeight => 1.0; + public SmokeBomb() : base(0x2808) + { + Stackable = Core.ML; + Weight = 1.0; + } public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs index 4584259dc..4ce98103d 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class GlassblowingBook : Item { [Constructible] - public GlassblowingBook() : base(0xFF4) - { - } - - public override double DefaultWeight => 1.0; + public GlassblowingBook() : base(0xFF4) => Weight = 1.0; public override int LabelNumber => 1153528; // Crafting glass with Glassblowing diff --git a/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs index 28de22d89..57425ad2a 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class MasonryBook : Item { [Constructible] - public MasonryBook() : base(0xFBE) - { - } - - public override double DefaultWeight => 1.0; + public MasonryBook() : base(0xFBE) => Weight = 1.0; public override int LabelNumber => 1153527; // Making valuables with Stonecrafting diff --git a/Projects/UOContent/Items/Skill Items/Specialized/Sand.cs b/Projects/UOContent/Items/Skill Items/Specialized/Sand.cs index f8983ef69..5824f8bb3 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/Sand.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/Sand.cs @@ -7,9 +7,11 @@ namespace Server.Items; public partial class Sand : Item, ICommodity { [Constructible] - public Sand(int amount = 1) : base(0x11EA) => Stackable = Core.ML; - - public override double DefaultWeight => 1.0; + public Sand(int amount = 1) : base(0x11EA) + { + Stackable = Core.ML; + Weight = 1.0; + } public override int LabelNumber => 1044626; // sand int ICommodity.DescriptionNumber => LabelNumber; diff --git a/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs index 5a7642f83..ec18441ea 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class SandMiningBook : Item { [Constructible] - public SandMiningBook() : base(0xFF4) - { - } - - public override double DefaultWeight => 1.0; + public SandMiningBook() : base(0xFF4) => Weight = 1.0; public override int LabelNumber => 1153531; // Find Glass-Quality Sand public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Skill Items/Specialized/StoneMiningBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/StoneMiningBook.cs index 4955c8306..5af88288b 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/StoneMiningBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/StoneMiningBook.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class StoneMiningBook : Item { [Constructible] - public StoneMiningBook() : base(0xFBE) - { - } - - public override double DefaultWeight => 1.0; + public StoneMiningBook() : base(0xFBE) => Weight = 1.0; public override int LabelNumber => 1153530; // Mining For Quality Stone diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs index e35cca471..6ee0aa0cf 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs @@ -25,9 +25,11 @@ namespace Server.Items private bool _redyable; [Constructible] - public DyeTub() : base(0xFAB) => _redyable = true; - - public override double DefaultWeight => 10.0; + public DyeTub() : base(0xFAB) + { + Weight = 10.0; + _redyable = true; + } public virtual CustomHuePicker CustomHuePicker => null; @@ -41,13 +43,20 @@ namespace Server.Items public virtual bool AllowDyables => true; - [SerializableField(2, allowFieldChange: nameof(AllowDyedHueChange), fieldChanged: nameof(OnDyedHueChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _dyedHue; - - private bool AllowDyedHueChange(ref int value) => _redyable; - - private void OnDyedHueChanged(int oldValue, int newValue) => Hue = newValue; + [SerializableProperty(2)] + [CommandProperty(AccessLevel.GameMaster)] + public int DyedHue + { + get => _dyedHue; + set + { + if (_redyable) + { + _dyedHue = value; + Hue = value; + } + } + } // Three metallic tubs now. public virtual bool MetallicHues => false; diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dressform.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dressform.cs index a288b762c..a4b77facb 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dressform.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dressform.cs @@ -7,9 +7,5 @@ namespace Server.Items; public partial class Dressform : Item { [Constructible] - public Dressform() : base(0xec6) - { - } - - public override double DefaultWeight => 10; + public Dressform() : base(0xec6) => Weight = 10; } diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dyes.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dyes.cs index 16fdd914e..54fe45c94 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dyes.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dyes.cs @@ -11,11 +11,7 @@ public partial class Dyes : Item // TODO: Add uses remaining [Constructible] - public Dyes() : base(0xFA9) - { - } - - public override double DefaultWeight => 3.0; + public Dyes() : base(0xFA9) => Weight = 3.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs index 1d0863bab..afd5b9e23 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs @@ -13,11 +13,7 @@ public interface IScissorable public partial class Scissors : Item { [Constructible] - public Scissors() : base(0xF9F) - { - } - - public override double DefaultWeight => 1.0; + public Scissors() : base(0xF9F) => Weight = 1.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs b/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs index 319412466..1de601118 100644 --- a/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs +++ b/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs @@ -17,11 +17,7 @@ public partial class DisguiseKit : Item public override int LabelNumber => 1041078; // a disguise kit [Constructible] - public DisguiseKit() : base(0xE05) - { - } - - public override double DefaultWeight => 1.0; + public DisguiseKit() : base(0xE05) => Weight = 1.0; public bool ValidateUse(Mobile from) { @@ -35,7 +31,7 @@ public partial class DisguiseKit : Item { from.SendLocalizedMessage(501702); } - else if (Stealing.SuspendOnMurder && pm.Kills > 0) // could be perma-red: intentional + else if (Stealing.SuspendOnMurder && pm.Kills > 0) { from.SendLocalizedMessage(501703); } @@ -71,14 +67,14 @@ public partial class DisguiseKit : Item { if (ValidateUse(from)) { - DisguiseGump.DisplayTo(from, this, true, false); + from.SendGump(new DisguiseGump(from, this, true, false)); } } } -public class DisguiseGump : DynamicGump +public class DisguiseGump : Gump { - private static readonly DisguiseEntry[] _hairEntries = + private static readonly DisguiseEntry[] m_HairEntries = { new(8251, 50700, 0, 5, 1011052), // Short new(8261, 60710, 0, 3, 1011047), // Pageboy @@ -92,7 +88,7 @@ public class DisguiseGump : DynamicGump new(0, 0, 0, 0, 1011051) // None }; - private static readonly DisguiseEntry[] _beardEntries = + private static readonly DisguiseEntry[] m_BeardEntries = { new(8269, 50906, 0, 0, 1011401), // Vandyke new(8257, 50808, 0, -2, 1011062), // Mustache @@ -104,70 +100,55 @@ public class DisguiseGump : DynamicGump new(0, 0, 0, 0, 1011051) // None }; - private readonly Mobile _from; - private readonly DisguiseKit _kit; - private readonly bool _startAtHair; - private readonly bool _used; + private readonly Mobile m_From; + private readonly DisguiseKit m_Kit; + private readonly bool m_Used; public override bool Singleton => true; - private DisguiseGump(Mobile from, DisguiseKit kit, bool startAtHair, bool used) : base(50, 50) + public DisguiseGump(Mobile from, DisguiseKit kit, bool startAtHair, bool used) : base(50, 50) { - _from = from; - _kit = kit; - _startAtHair = startAtHair; - _used = used; - } + m_From = from; + m_Kit = kit; + m_Used = used; - public static void DisplayTo(Mobile from, DisguiseKit kit, bool startAtHair, bool used) - { - if (from?.NetState == null || kit == null || kit.Deleted) - { - return; - } + AddPage(0); - from.SendGump(new DisguiseGump(from, kit, startAtHair, used)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(0); - - builder.AddBackground(100, 10, 400, 385, 2600); + AddBackground(100, 10, 400, 385, 2600); //
THIEF DISGUISE KIT
- builder.AddHtmlLocalized(100, 25, 400, 35, 1011045); + AddHtmlLocalized(100, 25, 400, 35, 1011045); - builder.AddButton(140, 353, 4005, 4007, 0); - builder.AddHtmlLocalized(172, 355, 90, 35, 1011036); // OKAY + AddButton(140, 353, 4005, 4007, 0); + AddHtmlLocalized(172, 355, 90, 35, 1011036); // OKAY - builder.AddButton(257, 353, 4005, 4007, 1); - builder.AddHtmlLocalized(289, 355, 90, 35, 1011046); // APPLY + AddButton(257, 353, 4005, 4007, 1); + AddHtmlLocalized(289, 355, 90, 35, 1011046); // APPLY - if (_from.Female || _from.Body.IsFemale) + if (from.Female || from.Body.IsFemale) { - DrawEntries(ref builder, 0, 1, -1, _hairEntries, -1); + DrawEntries(0, 1, -1, m_HairEntries, -1); } - else if (_startAtHair) + else if (startAtHair) { - DrawEntries(ref builder, 0, 1, 2, _hairEntries, 1011056); - DrawEntries(ref builder, 1, 2, 1, _beardEntries, 1011059); + DrawEntries(0, 1, 2, m_HairEntries, 1011056); + DrawEntries(1, 2, 1, m_BeardEntries, 1011059); } else { - DrawEntries(ref builder, 1, 1, 2, _beardEntries, 1011059); - DrawEntries(ref builder, 0, 2, 1, _hairEntries, 1011056); + DrawEntries(1, 1, 2, m_BeardEntries, 1011059); + DrawEntries(0, 2, 1, m_HairEntries, 1011056); } } - private static void DrawEntries(ref DynamicGumpBuilder builder, int index, int page, int nextPage, DisguiseEntry[] entries, int nextNumber) + private void DrawEntries(int index, int page, int nextPage, DisguiseEntry[] entries, int nextNumber) { - builder.AddPage(page); + AddPage(page); if (nextPage != -1) { - builder.AddButton(155, 320, 250 + index * 2, 251 + index * 2, 0, GumpButtonType.Page, nextPage); - builder.AddHtmlLocalized(180, 320, 150, 35, nextNumber); + AddButton(155, 320, 250 + index * 2, 251 + index * 2, 0, GumpButtonType.Page, nextPage); + AddHtmlLocalized(180, 320, 150, 35, nextNumber); } for (var i = 0; i < entries.Length; ++i) @@ -182,14 +163,14 @@ public class DisguiseGump : DynamicGump var x = i % 2 * 205; var y = i / 2 * 55; - if (entry.GumpID != 0) + if (entry.m_GumpID != 0) { - builder.AddBackground(220 + x, 60 + y, 50, 50, 2620); - builder.AddImage(153 + x + entry.OffsetX, 15 + y + entry.OffsetY, entry.GumpID); + AddBackground(220 + x, 60 + y, 50, 50, 2620); + AddImage(153 + x + entry.m_OffsetX, 15 + y + entry.m_OffsetY, entry.m_GumpID); } - builder.AddHtmlLocalized(140 + x, 72 + y, 80, 35, entry.Number); - builder.AddRadio(118 + x, 73 + y, 208, 209, false, i * 2 + index); + AddHtmlLocalized(140 + x, 72 + y, 80, 35, entry.m_Number); + AddRadio(118 + x, 73 + y, 208, 209, false, i * 2 + index); } } @@ -197,13 +178,13 @@ public class DisguiseGump : DynamicGump { if (info.ButtonID == 0) { - if (_used) + if (m_Used) { - _from.SendLocalizedMessage(501706); // Disguises wear off after 2 hours. + m_From.SendLocalizedMessage(501706); // Disguises wear off after 2 hours. } else { - _from.SendLocalizedMessage(501707); // You're looking good. + m_From.SendLocalizedMessage(501707); // You're looking good. } return; @@ -222,7 +203,7 @@ public class DisguiseGump : DynamicGump var hair = type == 0; - var entries = hair ? _hairEntries : _beardEntries; + var entries = hair ? m_HairEntries : m_BeardEntries; if (index >= 0 && index < entries.Length) { @@ -233,54 +214,54 @@ public class DisguiseGump : DynamicGump return; } - if (!_kit.ValidateUse(_from)) + if (!m_Kit.ValidateUse(m_From)) { return; } - if (!hair && (_from.Female || _from.Body.IsFemale)) + if (!hair && (m_From.Female || m_From.Body.IsFemale)) { return; } - _from.NameMod = NameList.RandomName(_from.Female ? "female" : "male"); + m_From.NameMod = NameList.RandomName(m_From.Female ? "female" : "male"); - if (_from is PlayerMobile pm) + if (m_From is PlayerMobile pm) { if (hair) { - pm.SetHairMods(entry.ItemID, -2); + pm.SetHairMods(entry.m_ItemID, -2); } else { - pm.SetHairMods(-2, entry.ItemID); + pm.SetHairMods(-2, entry.m_ItemID); } } - DisguiseGump.DisplayTo(_from, _kit, hair, true); + m_From.SendGump(new DisguiseGump(m_From, m_Kit, hair, true)); - DisguisePersistence.RemoveTimer(_from); + DisguisePersistence.RemoveTimer(m_From); - DisguisePersistence.CreateTimer(_from, TimeSpan.FromHours(2.0)); - DisguisePersistence.StartTimer(_from); + DisguisePersistence.CreateTimer(m_From, TimeSpan.FromHours(2.0)); + DisguisePersistence.StartTimer(m_From); } } private class DisguiseEntry { - public int GumpID { get; } - public int ItemID { get; } - public int Number { get; } - public int OffsetX { get; } - public int OffsetY { get; } + public readonly int m_GumpID; + public readonly int m_ItemID; + public readonly int m_Number; + public readonly int m_OffsetX; + public readonly int m_OffsetY; public DisguiseEntry(int itemID, int gumpID, int ox, int oy, int name) { - ItemID = itemID; - GumpID = gumpID; - OffsetX = ox; - OffsetY = oy; - Number = name; + m_ItemID = itemID; + m_GumpID = gumpID; + m_OffsetX = ox; + m_OffsetY = oy; + m_Number = name; } } } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Axle.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Axle.cs index 44f0914b0..0f9a592ea 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Axle.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Axle.cs @@ -11,7 +11,6 @@ public partial class Axle : Item { Stackable = true; Amount = amount; + Weight = 1.0; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/AxleGears.cs b/Projects/UOContent/Items/Skill Items/Tinkering/AxleGears.cs index 0e303c76e..113c1392f 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/AxleGears.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/AxleGears.cs @@ -11,7 +11,6 @@ public partial class AxleGears : Item { Stackable = true; Amount = amount; + Weight = 1.0; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/ClockFrame.cs b/Projects/UOContent/Items/Skill Items/Tinkering/ClockFrame.cs index 544ca7797..df30cab2c 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/ClockFrame.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/ClockFrame.cs @@ -11,7 +11,6 @@ public partial class ClockFrame : Item { Stackable = true; Amount = amount; + Weight = 2.0; } - - public override double DefaultWeight => 2.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/ClockParts.cs b/Projects/UOContent/Items/Skill Items/Tinkering/ClockParts.cs index f53f098e7..68f0dde40 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/ClockParts.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/ClockParts.cs @@ -11,7 +11,6 @@ public partial class ClockParts : Item { Stackable = true; Amount = amount; + Weight = 1.0; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Clocks.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Clocks.cs index 82ca1e0d6..46b1dfd5b 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Clocks.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Clocks.cs @@ -25,11 +25,7 @@ public partial class Clock : Item private static readonly DateTime WorldStart = new(1997, 9, 1); [Constructible] - public Clock(int itemID = 0x104B) : base(itemID) - { - } - - public override double DefaultWeight => 3.0; + public Clock(int itemID = 0x104B) : base(itemID) => Weight = 3.0; public static DateTime ServerStart { get; private set; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Gears.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Gears.cs index 3468477ed..d4c382740 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Gears.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Gears.cs @@ -11,7 +11,6 @@ public partial class Gears : Item { Stackable = true; Amount = amount; + Weight = 1.0; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Globe.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Globe.cs index 34e4e49c2..6f1d8559c 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Globe.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Globe.cs @@ -7,9 +7,5 @@ namespace Server.Items; public partial class Globe : Item { [Constructible] - public Globe() : base(0x1047) - { - } - - public override double DefaultWeight => 3.0; + public Globe() : base(0x1047) => Weight = 3.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Hinge.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Hinge.cs index 3045bd73b..b602afa32 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Hinge.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Hinge.cs @@ -11,7 +11,6 @@ public partial class Hinge : Item { Stackable = true; Amount = amount; + Weight = 1.0; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/SextantParts.cs b/Projects/UOContent/Items/Skill Items/Tinkering/SextantParts.cs index 316ba1a9d..069ced6ff 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/SextantParts.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/SextantParts.cs @@ -11,7 +11,6 @@ public partial class SextantParts : Item { Stackable = true; Amount = amount; + Weight = 2.0; } - - public override double DefaultWeight => 2.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Springs.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Springs.cs index 92179ccf4..451706d78 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Springs.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Springs.cs @@ -11,7 +11,6 @@ public partial class Springs : Item { Stackable = true; Amount = amount; + Weight = 1.0; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Spyglass.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Spyglass.cs index f7673a060..773714837 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Spyglass.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Spyglass.cs @@ -10,11 +10,7 @@ namespace Server.Items; public partial class Spyglass : Item { [Constructible] - public Spyglass() : base(0x14F5) - { - } - - public override double DefaultWeight => 3.0; + public Spyglass() : base(0x14F5) => Weight = 3.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Utensils.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Utensils.cs index 0f26e6b73..c9449a6ed 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Utensils.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Utensils.cs @@ -7,33 +7,21 @@ namespace Server.Items; public partial class Fork : Item { [Constructible] - public Fork() : base(0x9F4) - { - } - - public override double DefaultWeight => 1.0; + public Fork() : base(0x9F4) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class ForkLeft : Item { [Constructible] - public ForkLeft() : base(0x9F4) - { - } - - public override double DefaultWeight => 1.0; + public ForkLeft() : base(0x9F4) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class ForkRight : Item { [Constructible] - public ForkRight() : base(0x9F5) - { - } - - public override double DefaultWeight => 1.0; + public ForkRight() : base(0x9F5) => Weight = 1.0; } [Flippable(0x9F8, 0x9F9, 0x9C2, 0x9C3)] @@ -41,33 +29,21 @@ public partial class ForkRight : Item public partial class Spoon : Item { [Constructible] - public Spoon() : base(0x9F8) - { - } - - public override double DefaultWeight => 1.0; + public Spoon() : base(0x9F8) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class SpoonLeft : Item { [Constructible] - public SpoonLeft() : base(0x9F8) - { - } - - public override double DefaultWeight => 1.0; + public SpoonLeft() : base(0x9F8) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class SpoonRight : Item { [Constructible] - public SpoonRight() : base(0x9F9) - { - } - - public override double DefaultWeight => 1.0; + public SpoonRight() : base(0x9F9) => Weight = 1.0; } [Flippable(0x9F6, 0x9F7, 0x9A5, 0x9A6)] @@ -75,42 +51,26 @@ public partial class SpoonRight : Item public partial class Knife : Item { [Constructible] - public Knife() : base(0x9F6) - { - } - - public override double DefaultWeight => 1.0; + public Knife() : base(0x9F6) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class KnifeLeft : Item { [Constructible] - public KnifeLeft() : base(0x9F6) - { - } - - public override double DefaultWeight => 1.0; + public KnifeLeft() : base(0x9F6) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class KnifeRight : Item { [Constructible] - public KnifeRight() : base(0x9F7) - { - } - - public override double DefaultWeight => 1.0; + public KnifeRight() : base(0x9F7) => Weight = 1.0; } [SerializationGenerator(0, false)] public partial class Plate : Item { [Constructible] - public Plate() : base(0x9D7) - { - } - - public override double DefaultWeight => 1.0; + public Plate() : base(0x9D7) => Weight = 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/BaseRunicTool.cs b/Projects/UOContent/Items/Skill Items/Tools/BaseRunicTool.cs index 595b6b11d..6010ec376 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/BaseRunicTool.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/BaseRunicTool.cs @@ -60,14 +60,17 @@ public abstract partial class BaseRunicTool : BaseTool public BaseRunicTool(CraftResource resource, int uses, int itemID) : base(uses, itemID) => _resource = resource; - [SerializableField(0, fieldChanged: nameof(OnResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _resource; - - private void OnResourceChanged(CraftResource oldValue, CraftResource newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public CraftResource Resource { - Hue = CraftResources.GetHue(_resource); + get => _resource; + set + { + _resource = value; + Hue = CraftResources.GetHue(_resource); + InvalidateProperties(); + } } private void Deserialize(IGenericReader reader, int version) diff --git a/Projects/UOContent/Items/Skill Items/Tools/BaseTool.cs b/Projects/UOContent/Items/Skill Items/Tools/BaseTool.cs index 73482ccd0..4697c5379 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/BaseTool.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/BaseTool.cs @@ -1,7 +1,7 @@ using System; using ModernUO.Serialization; using Server.Engines.Craft; -using Server.Engines.Craft.T2A; +using Server.Gumps; using Server.Network; namespace Server.Items; @@ -56,7 +56,7 @@ public abstract partial class BaseTool : Item, IUsesRemaining, ICraftable private bool ShowUsesRemaining { get; set; } = true; - public virtual int OnCraft( + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) @@ -68,18 +68,6 @@ public abstract partial class BaseTool : Item, IUsesRemaining, ICraftable Crafter = from?.RawName; } - if (Core.UOR) - { - if (Quality == ToolQuality.Exceptional) - { - UsesRemaining = 100; - } - } - else if (Quality != ToolQuality.Regular) - { - UsesRemaining += (int)(UsesRemaining * ((int)Quality - 1) * 0.2); - } - return quality; } @@ -159,14 +147,9 @@ public abstract partial class BaseTool : Item, IUsesRemaining, ICraftable { from.SendLocalizedMessage(num); } - else if (T2ACraftSystem.Enabled) - { - from.Target = new T2ACraftToolTarget(this, system); - from.SendAsciiMessage("Target this tool to make last item, or any other target to begin crafting."); - } else { - CraftItem.ShowCraftMenu(from, system, this, null); + from.SendGump(new CraftGump(from, system, this, null)); } } else diff --git a/Projects/UOContent/Items/Skill Items/Tools/Blowpipe.cs b/Projects/UOContent/Items/Skill Items/Tools/Blowpipe.cs index ddba32852..7107ffbaf 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Blowpipe.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Blowpipe.cs @@ -8,12 +8,18 @@ namespace Server.Items; public partial class Blowpipe : BaseTool { [Constructible] - public Blowpipe() : base(0xE8A) => Hue = 0x3B9; + public Blowpipe() : base(0xE8A) + { + Weight = 4.0; + Hue = 0x3B9; + } [Constructible] - public Blowpipe(int uses) : base(uses, 0xE8A) => Hue = 0x3B9; - - public override double DefaultWeight => 4.0; + public Blowpipe(int uses) : base(uses, 0xE8A) + { + Weight = 4.0; + Hue = 0x3B9; + } public override CraftSystem CraftSystem => DefGlassblowing.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/DovetailSaw.cs b/Projects/UOContent/Items/Skill Items/Tools/DovetailSaw.cs index b84101d4b..c95302be5 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/DovetailSaw.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/DovetailSaw.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class DovetailSaw : BaseTool { [Constructible] - public DovetailSaw() : base(0x1028) - { - } + public DovetailSaw() : base(0x1028) => Weight = 2.0; [Constructible] - public DovetailSaw(int uses) : base(uses, 0x1028) - { - } - - public override double DefaultWeight => 2.0; + public DovetailSaw(int uses) : base(uses, 0x1028) => Weight = 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/DrawKnife.cs b/Projects/UOContent/Items/Skill Items/Tools/DrawKnife.cs index 574861433..397d92975 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/DrawKnife.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/DrawKnife.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class DrawKnife : BaseTool { [Constructible] - public DrawKnife() : base(0x10E4) - { - } + public DrawKnife() : base(0x10E4) => Weight = 1.0; [Constructible] - public DrawKnife(int uses) : base(uses, 0x10E4) - { - } - - public override double DefaultWeight => 1.0; + public DrawKnife(int uses) : base(uses, 0x10E4) => Weight = 1.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/FletcherTools.cs b/Projects/UOContent/Items/Skill Items/Tools/FletcherTools.cs index faa6f412d..775a0bb38 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/FletcherTools.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/FletcherTools.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class FletcherTools : BaseTool { [Constructible] - public FletcherTools() : base(0x1022) - { - } + public FletcherTools() : base(0x1022) => Weight = 2.0; [Constructible] - public FletcherTools(int uses) : base(uses, 0x1022) - { - } - - public override double DefaultWeight => 2.0; + public FletcherTools(int uses) : base(uses, 0x1022) => Weight = 2.0; public override CraftSystem CraftSystem => DefBowFletching.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/FlourSifter.cs b/Projects/UOContent/Items/Skill Items/Tools/FlourSifter.cs index 33149c8f7..0794a4aef 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/FlourSifter.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/FlourSifter.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class FlourSifter : BaseTool { [Constructible] - public FlourSifter() : base(0x103E) - { - } + public FlourSifter() : base(0x103E) => Weight = 1.0; [Constructible] - public FlourSifter(int uses) : base(uses, 0x103E) - { - } - - public override double DefaultWeight => 1.0; + public FlourSifter(int uses) : base(uses, 0x103E) => Weight = 1.0; public override CraftSystem CraftSystem => DefCooking.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Froe.cs b/Projects/UOContent/Items/Skill Items/Tools/Froe.cs index 4ecb00594..3658db1f2 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Froe.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Froe.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class Froe : BaseTool { [Constructible] - public Froe() : base(0x10E5) - { - } + public Froe() : base(0x10E5) => Weight = 1.0; [Constructible] - public Froe(int uses) : base(uses, 0x10E5) - { - } - - public override double DefaultWeight => 1.0; + public Froe(int uses) : base(uses, 0x10E5) => Weight = 1.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Hammer.cs b/Projects/UOContent/Items/Skill Items/Tools/Hammer.cs index 23f4e6bd5..732648691 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Hammer.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Hammer.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class Hammer : BaseTool { [Constructible] - public Hammer() : base(0x102A) - { - } + public Hammer() : base(0x102A) => Weight = 2.0; [Constructible] - public Hammer(int uses) : base(uses, 0x102A) - { - } - - public override double DefaultWeight => 2.0; + public Hammer(int uses) : base(uses, 0x102A) => Weight = 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Inshave.cs b/Projects/UOContent/Items/Skill Items/Tools/Inshave.cs index 32e5eed36..48631a9d3 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Inshave.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Inshave.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class Inshave : BaseTool { [Constructible] - public Inshave() : base(0x10E6) - { - } + public Inshave() : base(0x10E6) => Weight = 1.0; [Constructible] - public Inshave(int uses) : base(uses, 0x10E6) - { - } - - public override double DefaultWeight => 1.0; + public Inshave(int uses) : base(uses, 0x10E6) => Weight = 1.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/JointingPlane.cs b/Projects/UOContent/Items/Skill Items/Tools/JointingPlane.cs index 3e1143bef..741840f1a 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/JointingPlane.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/JointingPlane.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class JointingPlane : BaseTool { [Constructible] - public JointingPlane() : base(0x1030) - { - } + public JointingPlane() : base(0x1030) => Weight = 2.0; [Constructible] - public JointingPlane(int uses) : base(uses, 0x1030) - { - } - - public override double DefaultWeight => 2.0; + public JointingPlane(int uses) : base(uses, 0x1030) => Weight = 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/MalletAndChisel.cs b/Projects/UOContent/Items/Skill Items/Tools/MalletAndChisel.cs index 1e35ff142..5a890e07b 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/MalletAndChisel.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/MalletAndChisel.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class MalletAndChisel : BaseTool { [Constructible] - public MalletAndChisel() : base(0x12B3) - { - } + public MalletAndChisel() : base(0x12B3) => Weight = 1.0; [Constructible] - public MalletAndChisel(int uses) : base(uses, 0x12B3) - { - } - - public override double DefaultWeight => 1.0; + public MalletAndChisel(int uses) : base(uses, 0x12B3) => Weight = 1.0; public override CraftSystem CraftSystem => DefMasonry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/MapmakersPen.cs b/Projects/UOContent/Items/Skill Items/Tools/MapmakersPen.cs index dd263e7d5..edf1e2b6c 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/MapmakersPen.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/MapmakersPen.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class MapmakersPen : BaseTool { [Constructible] - public MapmakersPen() : base(0x0FBF) - { - } + public MapmakersPen() : base(0x0FBF) => Weight = 1.0; [Constructible] - public MapmakersPen(int uses) : base(uses, 0x0FBF) - { - } - - public override double DefaultWeight => 1.0; + public MapmakersPen(int uses) : base(uses, 0x0FBF) => Weight = 1.0; public override CraftSystem CraftSystem => DefCartography.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/MortarPestle.cs b/Projects/UOContent/Items/Skill Items/Tools/MortarPestle.cs index 8c13f724e..9ed61e088 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/MortarPestle.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/MortarPestle.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class MortarPestle : BaseTool { [Constructible] - public MortarPestle() : base(0xE9B) - { - } + public MortarPestle() : base(0xE9B) => Weight = 1.0; [Constructible] - public MortarPestle(int uses) : base(uses, 0xE9B) - { - } - - public override double DefaultWeight => 1.0; + public MortarPestle(int uses) : base(uses, 0xE9B) => Weight = 1.0; public override CraftSystem CraftSystem => DefAlchemy.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/MouldingPlane.cs b/Projects/UOContent/Items/Skill Items/Tools/MouldingPlane.cs index 8a7e3e965..c04c8d3c0 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/MouldingPlane.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/MouldingPlane.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class MouldingPlane : BaseTool { [Constructible] - public MouldingPlane() : base(0x102C) - { - } + public MouldingPlane() : base(0x102C) => Weight = 2.0; [Constructible] - public MouldingPlane(int uses) : base(uses, 0x102C) - { - } - - public override double DefaultWeight => 2.0; + public MouldingPlane(int uses) : base(uses, 0x102C) => Weight = 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Nails.cs b/Projects/UOContent/Items/Skill Items/Tools/Nails.cs index c1de93d5a..7df848c14 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Nails.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Nails.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class Nails : BaseTool { [Constructible] - public Nails() : base(0x102E) - { - } + public Nails() : base(0x102E) => Weight = 2.0; [Constructible] - public Nails(int uses) : base(uses, 0x102E) - { - } - - public override double DefaultWeight => 2.0; + public Nails(int uses) : base(uses, 0x102E) => Weight = 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/RollingPin.cs b/Projects/UOContent/Items/Skill Items/Tools/RollingPin.cs index a74bd6ac3..794e85522 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RollingPin.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RollingPin.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class RollingPin : BaseTool { [Constructible] - public RollingPin() : base(0x1043) - { - } + public RollingPin() : base(0x1043) => Weight = 1.0; [Constructible] - public RollingPin(int uses) : base(uses, 0x1043) - { - } - - public override double DefaultWeight => 1.0; + public RollingPin(int uses) : base(uses, 0x1043) => Weight = 1.0; public override CraftSystem CraftSystem => DefCooking.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/RunicDovetailSaw.cs b/Projects/UOContent/Items/Skill Items/Tools/RunicDovetailSaw.cs index 0de55eaba..6cdb49049 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RunicDovetailSaw.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RunicDovetailSaw.cs @@ -7,12 +7,18 @@ namespace Server.Items; public partial class RunicDovetailSaw : BaseRunicTool { [Constructible] - public RunicDovetailSaw(CraftResource resource) : base(resource, 0x1028) => Hue = CraftResources.GetHue(resource); + public RunicDovetailSaw(CraftResource resource) : base(resource, 0x1028) + { + Weight = 2.0; + Hue = CraftResources.GetHue(resource); + } [Constructible] - public RunicDovetailSaw(CraftResource resource, int uses) : base(resource, uses, 0x1028) => Hue = CraftResources.GetHue(resource); - - public override double DefaultWeight => 2.0; + public RunicDovetailSaw(CraftResource resource, int uses) : base(resource, uses, 0x1028) + { + Weight = 2.0; + Hue = CraftResources.GetHue(resource); + } public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/RunicFletcherTool.cs b/Projects/UOContent/Items/Skill Items/Tools/RunicFletcherTool.cs index 04045b4bb..595d4e10d 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RunicFletcherTool.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RunicFletcherTool.cs @@ -7,12 +7,18 @@ namespace Server.Items; public partial class RunicFletcherTool : BaseRunicTool { [Constructible] - public RunicFletcherTool(CraftResource resource) : base(resource, 0x1022) => Hue = CraftResources.GetHue(resource); + public RunicFletcherTool(CraftResource resource) : base(resource, 0x1022) + { + Weight = 2.0; + Hue = CraftResources.GetHue(resource); + } [Constructible] - public RunicFletcherTool(CraftResource resource, int uses) : base(resource, uses, 0x1022) => Hue = CraftResources.GetHue(resource); - - public override double DefaultWeight => 2.0; + public RunicFletcherTool(CraftResource resource, int uses) : base(resource, uses, 0x1022) + { + Weight = 2.0; + Hue = CraftResources.GetHue(resource); + } public override CraftSystem CraftSystem => DefBowFletching.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs b/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs index e2e569bee..bb2581258 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs @@ -10,6 +10,7 @@ public partial class RunicHammer : BaseRunicTool [Constructible] public RunicHammer(CraftResource resource) : base(resource, 0x13E3) { + Weight = 8.0; Layer = Layer.OneHanded; Hue = CraftResources.GetHue(resource); } @@ -17,12 +18,11 @@ public partial class RunicHammer : BaseRunicTool [Constructible] public RunicHammer(CraftResource resource, int uses) : base(resource, uses, 0x13E3) { + Weight = 8.0; Layer = Layer.OneHanded; Hue = CraftResources.GetHue(resource); } - public override double DefaultWeight => 8.0; - public override CraftSystem CraftSystem => DefBlacksmithy.CraftSystem; public override int LabelNumber diff --git a/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs b/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs index 7cb39925c..42c9cbf0b 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs @@ -7,12 +7,18 @@ namespace Server.Items; public partial class RunicSewingKit : BaseRunicTool { [Constructible] - public RunicSewingKit(CraftResource resource) : base(resource, 0xF9D) => Hue = CraftResources.GetHue(resource); + public RunicSewingKit(CraftResource resource) : base(resource, 0xF9D) + { + Weight = 2.0; + Hue = CraftResources.GetHue(resource); + } [Constructible] - public RunicSewingKit(CraftResource resource, int uses) : base(resource, uses, 0xF9D) => Hue = CraftResources.GetHue(resource); - - public override double DefaultWeight => 2.0; + public RunicSewingKit(CraftResource resource, int uses) : base(resource, uses, 0xF9D) + { + Weight = 2.0; + Hue = CraftResources.GetHue(resource); + } public override CraftSystem CraftSystem => DefTailoring.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/Saw.cs b/Projects/UOContent/Items/Skill Items/Tools/Saw.cs index 01649b210..ba44b25f7 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Saw.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Saw.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class Saw : BaseTool { [Constructible] - public Saw() : base(0x1034) - { - } + public Saw() : base(0x1034) => Weight = 2.0; [Constructible] - public Saw(int uses) : base(uses, 0x1034) - { - } - - public override double DefaultWeight => 2.0; + public Saw(int uses) : base(uses, 0x1034) => Weight = 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Scorp.cs b/Projects/UOContent/Items/Skill Items/Tools/Scorp.cs index d495d9b82..d6678a2c9 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Scorp.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Scorp.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class Scorp : BaseTool { [Constructible] - public Scorp() : base(0x10E7) - { - } + public Scorp() : base(0x10E7) => Weight = 1.0; [Constructible] - public Scorp(int uses) : base(uses, 0x10E7) - { - } - - public override double DefaultWeight => 1.0; + public Scorp(int uses) : base(uses, 0x10E7) => Weight = 1.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/ScribesPen.cs b/Projects/UOContent/Items/Skill Items/Tools/ScribesPen.cs index 224632da2..2eb9c2ae4 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/ScribesPen.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/ScribesPen.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class ScribesPen : BaseTool { [Constructible] - public ScribesPen() : base(0x0FBF) - { - } + public ScribesPen() : base(0x0FBF) => Weight = 1.0; [Constructible] - public ScribesPen(int uses) : base(uses, 0x0FBF) - { - } - - public override double DefaultWeight => 1.0; + public ScribesPen(int uses) : base(uses, 0x0FBF) => Weight = 1.0; public override CraftSystem CraftSystem => DefInscription.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/SewingKit.cs b/Projects/UOContent/Items/Skill Items/Tools/SewingKit.cs index 13436d1de..97a1d2912 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/SewingKit.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/SewingKit.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class SewingKit : BaseTool { [Constructible] - public SewingKit() : base(0xF9D) - { - } + public SewingKit() : base(0xF9D) => Weight = 2.0; [Constructible] - public SewingKit(int uses) : base(uses, 0xF9D) - { - } - - public override double DefaultWeight => 2.0; + public SewingKit(int uses) : base(uses, 0xF9D) => Weight = 2.0; public override CraftSystem CraftSystem => DefTailoring.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Skillet.cs b/Projects/UOContent/Items/Skill Items/Tools/Skillet.cs index 851e0a726..f9278ce38 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Skillet.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Skillet.cs @@ -7,16 +7,10 @@ namespace Server.Items; public partial class Skillet : BaseTool { [Constructible] - public Skillet() : base(0x97F) - { - } + public Skillet() : base(0x97F) => Weight = 1.0; [Constructible] - public Skillet(int uses) : base(uses, 0x97F) - { - } - - public override double DefaultWeight => 1.0; + public Skillet(int uses) : base(uses, 0x97F) => Weight = 1.0; public override int LabelNumber => 1044567; // skillet diff --git a/Projects/UOContent/Items/Skill Items/Tools/SmithHammer.cs b/Projects/UOContent/Items/Skill Items/Tools/SmithHammer.cs index 33d17eacc..d068457c9 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/SmithHammer.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/SmithHammer.cs @@ -8,12 +8,18 @@ namespace Server.Items; public partial class SmithHammer : BaseTool { [Constructible] - public SmithHammer() : base(0x13E3) => Layer = Layer.OneHanded; + public SmithHammer() : base(0x13E3) + { + Weight = 8.0; + Layer = Layer.OneHanded; + } [Constructible] - public SmithHammer(int uses) : base(uses, 0x13E3) => Layer = Layer.OneHanded; - - public override double DefaultWeight => 8.0; + public SmithHammer(int uses) : base(uses, 0x13E3) + { + Weight = 8.0; + Layer = Layer.OneHanded; + } public override CraftSystem CraftSystem => DefBlacksmithy.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/SmoothingPlane.cs b/Projects/UOContent/Items/Skill Items/Tools/SmoothingPlane.cs index 6131b86a9..13487dffe 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/SmoothingPlane.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/SmoothingPlane.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class SmoothingPlane : BaseTool { [Constructible] - public SmoothingPlane() : base(0x1032) - { - } + public SmoothingPlane() : base(0x1032) => Weight = 1.0; [Constructible] - public SmoothingPlane(int uses) : base(uses, 0x1032) - { - } - - public override double DefaultWeight => 1.0; + public SmoothingPlane(int uses) : base(uses, 0x1032) => Weight = 1.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs b/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs index bd85b875c..d6f9fc4b8 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class TinkerTools : BaseTool { [Constructible] - public TinkerTools() : base(0x1EB8) - { - } + public TinkerTools() : base(0x1EB8) => Weight = 1.0; [Constructible] - public TinkerTools(int uses) : base(uses, 0x1EB8) - { - } - - public override double DefaultWeight => 1.0; + public TinkerTools(int uses) : base(uses, 0x1EB8) => Weight = 1.0; public override CraftSystem CraftSystem => DefTinkering.CraftSystem; } @@ -26,16 +20,10 @@ public partial class TinkerTools : BaseTool public partial class TinkersTools : BaseTool { [Constructible] - public TinkersTools() : base(0x1EBC) - { - } + public TinkersTools() : base(0x1EBC) => Weight = 1.0; [Constructible] - public TinkersTools(int uses) : base(uses, 0x1EBC) - { - } - - public override double DefaultWeight => 1.0; + public TinkersTools(int uses) : base(uses, 0x1EBC) => Weight = 1.0; public override CraftSystem CraftSystem => DefTinkering.CraftSystem; -} +} \ No newline at end of file diff --git a/Projects/UOContent/Items/Skill Items/Tools/Tongs.cs b/Projects/UOContent/Items/Skill Items/Tools/Tongs.cs index b6371b4a3..4c76847f1 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Tongs.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Tongs.cs @@ -8,16 +8,10 @@ namespace Server.Items; public partial class Tongs : BaseTool { [Constructible] - public Tongs() : base(0xFBB) - { - } + public Tongs() : base(0xFBB) => Weight = 2.0; [Constructible] - public Tongs(int uses) : base(uses, 0xFBB) - { - } - - public override double DefaultWeight => 2.0; + public Tongs(int uses) : base(uses, 0xFBB) => Weight = 2.0; public override CraftSystem CraftSystem => DefBlacksmithy.CraftSystem; } diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs index 2449e8005..f20097f7a 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs @@ -118,6 +118,8 @@ public partial class DawnsMusicBox : Item, ISecurable [Constructible] public DawnsMusicBox() : base(0x2AF9) { + Weight = 1.0; + var shuffledTracks = GetTracks(DawnsMusicRarity.Common); shuffledTracks.Shuffle(); @@ -130,8 +132,6 @@ public partial class DawnsMusicBox : Item, ISecurable ]; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1075198; // Dawn's Music Box public override void OnAfterDuped(Item newItem) @@ -212,7 +212,7 @@ public partial class DawnsMusicBox : Item, ISecurable } else { - DawnsMusicBoxGump.DisplayTo(from, this); + from.SendGump(new DawnsMusicBoxGump(this)); } } diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBoxGump.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBoxGump.cs index d979086e6..3f737b595 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBoxGump.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBoxGump.cs @@ -3,54 +3,44 @@ using Server.Network; namespace Server.Gumps; -public class DawnsMusicBoxGump : DynamicGump +public class DawnsMusicBoxGump : Gump { - private readonly DawnsMusicBox _box; + private readonly DawnsMusicBox m_Box; public override bool Singleton => true; - private DawnsMusicBoxGump(DawnsMusicBox box) : base(60, 36) => _box = box; - - public static void DisplayTo(Mobile from, DawnsMusicBox box) + public DawnsMusicBoxGump(DawnsMusicBox box) : base(60, 36) { - if (from?.NetState == null || box?.Deleted != false) - { - return; - } + m_Box = box; - from.SendGump(new DawnsMusicBoxGump(box)); - } + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 273, 324, 0x13BE); - builder.AddImageTiled(10, 10, 253, 20, 0xA40); - builder.AddImageTiled(10, 40, 253, 244, 0xA40); - builder.AddImageTiled(10, 294, 253, 20, 0xA40); - builder.AddAlphaRegion(10, 10, 253, 304); - builder.AddButton(10, 294, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL - builder.AddHtmlLocalized(14, 12, 273, 20, 1075130, 0x7FFF); // Choose a track to play + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 273, 20, 1075130, 0x7FFF); // Choose a track to play var page = 1; int i, y = 49; - builder.AddPage(page); + AddPage(page); - for (i = 0; i < _box.Tracks.Count; i++, y += 24) + for (i = 0; i < m_Box.Tracks.Count; i++, y += 24) { - var info = DawnsMusicBox.GetInfo(_box.Tracks[i]); + var info = DawnsMusicBox.GetInfo(m_Box.Tracks[i]); if (i > 0 && i % 10 == 0) { - builder.AddButton(228, 294, 0xFA5, 0xFA6, 0, GumpButtonType.Page, page + 1); + AddButton(228, 294, 0xFA5, 0xFA6, 0, GumpButtonType.Page, page + 1); - builder.AddPage(page + 1); + AddPage(page + 1); y = 49; - builder.AddButton(193, 294, 0xFAE, 0xFAF, 0, GumpButtonType.Page, page); + AddButton(193, 294, 0xFAE, 0xFAF, 0, GumpButtonType.Page, page); page++; } @@ -60,49 +50,49 @@ public class DawnsMusicBoxGump : DynamicGump continue; } - builder.AddButton(19, y, 0x845, 0x846, 100 + i); - builder.AddHtmlLocalized(44, y - 2, 213, 20, info.Name, 0x7FFF); + AddButton(19, y, 0x845, 0x846, 100 + i); + AddHtmlLocalized(44, y - 2, 213, 20, info.Name, 0x7FFF); } if (i % 10 == 0) { - builder.AddButton(228, 294, 0xFA5, 0xFA6, 0, GumpButtonType.Page, page + 1); + AddButton(228, 294, 0xFA5, 0xFA6, 0, GumpButtonType.Page, page + 1); - builder.AddPage(page + 1); + AddPage(page + 1); y = 49; - builder.AddButton(193, 294, 0xFAE, 0xFAF, 0, GumpButtonType.Page, page); + AddButton(193, 294, 0xFAE, 0xFAF, 0, GumpButtonType.Page, page); } - builder.AddButton(19, y, 0x845, 0x846, 1); - builder.AddHtmlLocalized(44, y - 2, 213, 20, 1075207, 0x7FFF); // Stop Song + AddButton(19, y, 0x845, 0x846, 1); + AddHtmlLocalized(44, y - 2, 213, 20, 1075207, 0x7FFF); // Stop Song } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_box?.Deleted != false) + if (m_Box?.Deleted != false) { return; } var m = sender.Mobile; - if (!_box.IsChildOf(m.Backpack) && !_box.IsLockedDown) + if (!m_Box.IsChildOf(m.Backpack) && !m_Box.IsLockedDown) { // You must have the item in your backpack or locked down in order to use it. m.SendLocalizedMessage(1061856); } - else if (_box.IsLockedDown && !_box.HasAccess(m)) + else if (m_Box.IsLockedDown && !m_Box.HasAccess(m)) { m.SendLocalizedMessage(502691); // You must be the owner to use this. } else if (info.ButtonID == 1) { - _box.EndMusic(m); + m_Box.EndMusic(m); } - else if (info.ButtonID >= 100 && info.ButtonID - 100 < _box.Tracks.Count) + else if (info.ButtonID >= 100 && info.ButtonID - 100 < m_Box.Tracks.Count) { - _box.PlayMusic(m, _box.Tracks[info.ButtonID - 100]); + m_Box.PlayMusic(m, m_Box.Tracks[info.ButtonID - 100]); } } } diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs index e7eb79894..a9eb5c16d 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs @@ -17,9 +17,11 @@ public partial class DawnsMusicGear : Item } [Constructible] - public DawnsMusicGear(MusicName music) : base(0x1053) => _music = music; - - public override double DefaultWeight => 1.0; + public DawnsMusicGear(MusicName music) : base(0x1053) + { + _music = music; + Weight = 1.0; + } public static DawnsMusicGear RandomCommon => new(DawnsMusicBox.RandomTrack(DawnsMusicRarity.Common)); diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/DupresShield.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/DupresShield.cs index bbeee47d9..459b14c23 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/DupresShield.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/DupresShield.cs @@ -9,6 +9,7 @@ public partial class DupresShield : BaseShield public DupresShield() : base(0x2B01) { LootType = LootType.Blessed; + Weight = 6.0; Attributes.BonusHits = 5; Attributes.RegenHits = 1; @@ -16,8 +17,6 @@ public partial class DupresShield : BaseShield SkillBonuses.SetValues(0, SkillName.Parry, 5); } - public override double DefaultWeight => 6.0; - public override int LabelNumber => 1075196; // Dupre's Shield public override int BasePhysicalResistance => 1; diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs index 404fc9012..3bdfd00fa 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/FountainOfLife.cs @@ -31,7 +31,6 @@ public partial class FountainOfLife : BaseAddonContainer public const int MaxCharges = 10; [SerializableField(1)] - [DeserializeTimer(nameof(DeserializeTimer), wallClock: true)] private Timer _timer; [Constructible] @@ -40,6 +39,7 @@ public partial class FountainOfLife : BaseAddonContainer _charges = charges; } + [DeserializeTimerField(1)] private void DeserializeTimer(TimeSpan delay) { _timer = Timer.DelayCall(Utility.Max(delay, TimeSpan.Zero), RechargeTime, Recharge); @@ -54,15 +54,17 @@ public partial class FountainOfLife : BaseAddonContainer public override int DefaultDropSound => 66; public override int DefaultMaxItems => 125; - [SerializableField(0, allowFieldChange: nameof(AllowChargesChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _charges; - - private bool AllowChargesChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int Charges { - value = Math.Min(value, MaxCharges); - return true; + get => _charges; + set + { + _charges = Math.Min(value, MaxCharges); + InvalidateProperties(); + this.MarkDirty(); + } } public override bool OnDragLift(Mobile from) => false; @@ -181,14 +183,16 @@ public partial class FountainOfLifeDeed : BaseAddonContainerDeed public override int LabelNumber => 1075197; // Fountain of Life public override BaseAddonContainer Addon => new FountainOfLife(_charges); - [SerializableField(0, allowFieldChange: nameof(AllowChargesChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _charges; - - private bool AllowChargesChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int Charges { - value = Math.Min(value, FountainOfLife.MaxCharges); - return true; + get => _charges; + set + { + _charges = Math.Min(value, FountainOfLife.MaxCharges); + InvalidateProperties(); + this.MarkDirty(); + } } } diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/QuiverOfInfinity.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/QuiverOfInfinity.cs index 0b1377d24..a13175dc4 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/QuiverOfInfinity.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/QuiverOfInfinity.cs @@ -9,6 +9,7 @@ public partial class QuiverOfInfinity : BaseQuiver public QuiverOfInfinity() : base(0x2B02) { LootType = LootType.Blessed; + Weight = 8.0; WeightReduction = 30; LowerAmmoCost = 20; @@ -16,7 +17,5 @@ public partial class QuiverOfInfinity : BaseQuiver Attributes.DefendChance = 5; } - public override double DefaultWeight => 8.0; - public override int LabelNumber => 1075201; // Quiver of Infinity } diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs index e0a25fbf9..091cc242b 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs @@ -20,10 +20,9 @@ public partial class BaseFormTalisman : Item { LootType = LootType.Blessed; Layer = Layer.Talisman; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - public virtual TalismanForm Form => TalismanForm.Squirrel; public override void AddNameProperty(IPropertyList list) diff --git a/Projects/UOContent/Items/Special/AoS Promotional/HoodedShroudOfShadows.cs b/Projects/UOContent/Items/Special/AoS Promotional/HoodedShroudOfShadows.cs index bac7674a5..caefb44c2 100644 --- a/Projects/UOContent/Items/Special/AoS Promotional/HoodedShroudOfShadows.cs +++ b/Projects/UOContent/Items/Special/AoS Promotional/HoodedShroudOfShadows.cs @@ -7,9 +7,11 @@ namespace Server.Items; public partial class HoodedShroudOfShadows : BaseOuterTorso { [Constructible] - public HoodedShroudOfShadows(int hue = 0x455) : base(0x2684, hue) => LootType = LootType.Blessed; - - public override double DefaultWeight => 3.0; + public HoodedShroudOfShadows(int hue = 0x455) : base(0x2684, hue) + { + LootType = LootType.Blessed; + Weight = 3.0; + } public override bool Dye(Mobile from, DyeTub sender) { diff --git a/Projects/UOContent/Items/Special/Broken Furniture Collection/BrokenBed.cs b/Projects/UOContent/Items/Special/Broken Furniture Collection/BrokenBed.cs index 921507f48..d1b002002 100644 --- a/Projects/UOContent/Items/Special/Broken Furniture Collection/BrokenBed.cs +++ b/Projects/UOContent/Items/Special/Broken Furniture Collection/BrokenBed.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Gumps; +using Server.Network; namespace Server.Items; @@ -29,14 +30,14 @@ public partial class BrokenBedAddon : BaseAddon } [SerializationGenerator(0)] -public partial class BrokenBedDeed : BaseAddonDeed, IDirectionAddonDeed +public partial class BrokenBedDeed : BaseAddonDeed { - public bool East { get; set; } + private bool _east; [Constructible] public BrokenBedDeed() => LootType = LootType.Blessed; - public override BaseAddon Addon => new BrokenBedAddon(East); + public override BaseAddon Addon => new BrokenBedAddon(_east); public override int LabelNumber => 1076263; // Broken Bed @@ -52,17 +53,49 @@ public partial class BrokenBedDeed : BaseAddonDeed, IDirectionAddonDeed } } - public void SendTarget(Mobile m) + private void SendTarget(Mobile m) { base.OnDoubleClick(m); } - private class InternalGump : SelectAddonDirectionGump + private class InternalGump : Gump { - public InternalGump(IDirectionAddonDeed deed) : base(deed) + private readonly BrokenBedDeed _deed; + + public override bool Singleton => true; + + public InternalGump(BrokenBedDeed deed) : base(60, 36) { + _deed = deed; + + AddPage(0); + + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 273, 20, 1076749, 0x7FFF); // Please select your broken bed position + + AddPage(1); + + AddButton(19, 49, 0x845, 0x846, 1); + AddHtmlLocalized(44, 47, 213, 20, 1075386, 0x7FFF); // South + AddButton(19, 73, 0x845, 0x846, 2); + AddHtmlLocalized(44, 71, 213, 20, 1075387, 0x7FFF); // East } - public override int SelectionNumber => 1076749; // Please select your broken bed position + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (_deed?.Deleted != false || info.ButtonID == 0) + { + return; + } + + _deed._east = info.ButtonID != 1; + _deed.SendTarget(sender.Mobile); + } } } diff --git a/Projects/UOContent/Items/Special/Broken Furniture Collection/BrokenVanity.cs b/Projects/UOContent/Items/Special/Broken Furniture Collection/BrokenVanity.cs index fb8402a8f..06c0afa27 100644 --- a/Projects/UOContent/Items/Special/Broken Furniture Collection/BrokenVanity.cs +++ b/Projects/UOContent/Items/Special/Broken Furniture Collection/BrokenVanity.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Gumps; +using Server.Network; namespace Server.Items; @@ -25,15 +26,14 @@ public partial class BrokenVanityAddon : BaseAddon } [SerializationGenerator(0)] -public partial class BrokenVanityDeed : BaseAddonDeed, IDirectionAddonDeed +public partial class BrokenVanityDeed : BaseAddonDeed { - public bool East { get; set; } + private bool _east; [Constructible] public BrokenVanityDeed() => LootType = LootType.Blessed; - public override BaseAddon Addon => new BrokenVanityAddon(East); - + public override BaseAddon Addon => new BrokenVanityAddon(_east); public override int LabelNumber => 1076260; // Broken Vanity public override void OnDoubleClick(Mobile from) @@ -48,17 +48,49 @@ public partial class BrokenVanityDeed : BaseAddonDeed, IDirectionAddonDeed } } - public void SendTarget(Mobile m) + private void SendTarget(Mobile m) { base.OnDoubleClick(m); } - private class InternalGump : SelectAddonDirectionGump + private class InternalGump : Gump { - public InternalGump(IDirectionAddonDeed deed) : base(deed) + private readonly BrokenVanityDeed _deed; + + public override bool Singleton => true; + + public InternalGump(BrokenVanityDeed deed) : base(60, 63) { + _deed = deed; + + AddPage(0); + + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 273, 20, 1076747, 0x7FFF); // Please select your broken vanity position + + AddPage(1); + + AddButton(19, 49, 0x845, 0x846, 1); + AddHtmlLocalized(44, 47, 213, 20, 1075386, 0x7FFF); // South + AddButton(19, 73, 0x845, 0x846, 2); + AddHtmlLocalized(44, 71, 213, 20, 1075387, 0x7FFF); // East } - public override int SelectionNumber => 1076747; // Please select your broken vanity position + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (_deed?.Deleted != false || info.ButtonID == 0) + { + return; + } + + _deed._east = info.ButtonID != 1; + _deed.SendTarget(sender.Mobile); + } } } diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs index 1f5d80eaf..0b7a0a89e 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs @@ -13,12 +13,11 @@ public partial class AncientSmithyHammer : BaseTool public AncientSmithyHammer(int bonus, int uses = 600) : base(uses, 0x13E4) { _bonus = bonus; + Weight = 8.0; Layer = Layer.OneHanded; Hue = 0x482; } - public override double DefaultWeight => 8.0; - [SerializableProperty(0)] [CommandProperty(AccessLevel.GameMaster)] public int Bonus diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs index ab0bf4fbd..991175beb 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs @@ -13,7 +13,6 @@ public partial class ColoredAnvil : Item { // TODO: Color weighted by rarity? Hue = CraftResources.GetRandomResource(CraftResource.DullCopper, CraftResource.Valorite)?.Hue ?? 0; + Weight = 20; } - - public override double DefaultWeight => 20.0; } diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs index c2104259a..91dcc09bd 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class LeatherGlovesOfMining : BaseGlovesOfMining { [Constructible] - public LeatherGlovesOfMining(int bonus) : base(bonus, 0x13C6) - { - } - - public override double DefaultWeight => 1; + public LeatherGlovesOfMining(int bonus) : base(bonus, 0x13C6) => Weight = 1; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; @@ -40,11 +36,7 @@ public partial class LeatherGlovesOfMining : BaseGlovesOfMining public partial class StuddedGlovesOfMining : BaseGlovesOfMining { [Constructible] - public StuddedGlovesOfMining(int bonus) : base(bonus, 0x13D5) - { - } - - public override double DefaultWeight => 2; + public StuddedGlovesOfMining(int bonus) : base(bonus, 0x13D5) => Weight = 2; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; @@ -71,11 +63,7 @@ public partial class StuddedGlovesOfMining : BaseGlovesOfMining public partial class RingmailGlovesOfMining : BaseGlovesOfMining { [Constructible] - public RingmailGlovesOfMining(int bonus) : base(bonus, 0x13EB) - { - } - - public override double DefaultWeight => 1; + public RingmailGlovesOfMining(int bonus) : base(bonus, 0x13EB) => Weight = 1; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs index 5a474ebe1..fc898fd20 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs @@ -15,12 +15,11 @@ public partial class PowderOfTemperament : Item, IUsesRemaining [Constructible] public PowderOfTemperament(int charges = 10) : base(4102) { + Weight = 1.0; Hue = 2419; UsesRemaining = charges; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1049082; // powder of fortifying bool IUsesRemaining.ShowUsesRemaining diff --git a/Projects/UOContent/Items/Special/Community Collection/JesterHatofChuckles.cs b/Projects/UOContent/Items/Special/Community Collection/JesterHatofChuckles.cs index 3cae57aec..d731396ee 100644 --- a/Projects/UOContent/Items/Special/Community Collection/JesterHatofChuckles.cs +++ b/Projects/UOContent/Items/Special/Community Collection/JesterHatofChuckles.cs @@ -11,9 +11,11 @@ public partial class JesterHatofChuckles : BaseHat } [Constructible] - public JesterHatofChuckles(int hue) : base(0x171C, hue) => Attributes.Luck = 150; - - public override double DefaultWeight => 1.0; + public JesterHatofChuckles(int hue) : base(0x171C, hue) + { + Attributes.Luck = 150; + Weight = 1.0; + } public override int LabelNumber => 1073256; // Jester Hat of Chuckles - Museum of Vesper Replica 1073256 diff --git a/Projects/UOContent/Items/Special/Gifts/HearthOfHomeFire.cs b/Projects/UOContent/Items/Special/Gifts/HearthOfHomeFire.cs index 6ba993842..a2c64d180 100644 --- a/Projects/UOContent/Items/Special/Gifts/HearthOfHomeFire.cs +++ b/Projects/UOContent/Items/Special/Gifts/HearthOfHomeFire.cs @@ -60,27 +60,25 @@ public partial class HearthOfHomeFireDeed : BaseAddonDeed base.OnDoubleClick(m); } - private class InternalGump : StaticGump + private class InternalGump : Gump { private readonly HearthOfHomeFireDeed _deed; public override bool Singleton => true; - public InternalGump(HearthOfHomeFireDeed deed) : base(150, 50) => _deed = deed; - - protected override void BuildLayout(ref StaticGumpBuilder builder) + public InternalGump(HearthOfHomeFireDeed deed) : base(150, 50) { - builder.AddBackground(0, 0, 350, 250, 0xA28); + _deed = deed; - builder.AddPage(); + AddBackground(0, 0, 350, 250, 0xA28); - builder.AddItem(90, 52, 0x2367); - builder.AddItem(112, 35, 0x2360); - builder.AddButton(70, 35, 0x868, 0x869, 1); // South + AddItem(90, 52, 0x2367); + AddItem(112, 35, 0x2360); + AddButton(70, 35, 0x868, 0x869, 1); // South - builder.AddItem(220, 35, 0x2352); - builder.AddItem(242, 52, 0x2358); - builder.AddButton(185, 35, 0x868, 0x869, 2); // East + AddItem(220, 35, 0x2352); + AddItem(242, 52, 0x2358); + AddButton(185, 35, 0x868, 0x869, 2); // East } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs b/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs index 82e0a2da2..2faee7f5f 100644 --- a/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs +++ b/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs @@ -26,13 +26,13 @@ public partial class RoseOfTrinsic : Item, ISecurable [Constructible] public RoseOfTrinsic() : base(0x234D) { + Weight = 1.0; LootType = LootType.Blessed; + _petals = 0; StartSpawnTimer(TimeSpan.FromMinutes(1.0)); } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1062913; // Rose of Trinsic public override void OnAfterDuped(Item newItem) @@ -156,11 +156,11 @@ public partial class RoseOfTrinsicPetal : Item { Stackable = true; Amount = amount; + + Weight = 1.0; Hue = 0xE; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1062926; // Petal of the Rose of Trinsic public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Special/Gifts/SamuraiHelm.cs b/Projects/UOContent/Items/Special/Gifts/SamuraiHelm.cs index 403dd2b51..57b23ccf7 100644 --- a/Projects/UOContent/Items/Special/Gifts/SamuraiHelm.cs +++ b/Projects/UOContent/Items/Special/Gifts/SamuraiHelm.cs @@ -9,6 +9,7 @@ public partial class SamuraiHelm : BaseArmor [Constructible] public SamuraiHelm() : base(0x236C) { + Weight = 5.0; LootType = LootType.Blessed; Attributes.DefendChance = 15; @@ -17,8 +18,6 @@ public partial class SamuraiHelm : BaseArmor ArmorAttributes.MageArmor = 1; } - public override double DefaultWeight => 5.0; - public override int LabelNumber => 1062923; // Ancient Samurai Helm public override int BasePhysicalResistance => 15; diff --git a/Projects/UOContent/Items/Special/Gifts/TapestryOfSosaria.cs b/Projects/UOContent/Items/Special/Gifts/TapestryOfSosaria.cs index a6e488f23..9a04138bf 100644 --- a/Projects/UOContent/Items/Special/Gifts/TapestryOfSosaria.cs +++ b/Projects/UOContent/Items/Special/Gifts/TapestryOfSosaria.cs @@ -18,11 +18,10 @@ public partial class TapestryOfSosaria : Item, ISecurable [Constructible] public TapestryOfSosaria() : base(0x234E) { + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1062917; // The Tapestry of Sosaria public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) @@ -36,7 +35,7 @@ public partial class TapestryOfSosaria : Item, ISecurable { if (from.InRange(GetWorldLocation(), 2)) { - TapestryOfSosariaGump.DisplayTo(from); + from.SendGump(new InternalGump()); } else { @@ -49,28 +48,13 @@ public partial class TapestryOfSosaria : Item, ISecurable Level = (SecureLevel)reader.ReadEncodedInt(); } - private class TapestryOfSosariaGump : StaticGump + private class InternalGump : Gump { public override bool Singleton => true; - private TapestryOfSosariaGump() : base(50, 50) + public InternalGump() : base(50, 50) { - } - - public static void DisplayTo(Mobile from) - { - if (from?.NetState == null) - { - return; - } - - from.SendGump(new TapestryOfSosariaGump()); - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddImage(0, 0, 0x2C95); + AddImage(0, 0, 0x2C95); } } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/Curtains.cs b/Projects/UOContent/Items/Special/Heritage Items/Curtains.cs index d1621b4e2..60f6cb16d 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/Curtains.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/Curtains.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Gumps; +using Server.Network; namespace Server.Items; @@ -30,20 +31,21 @@ public partial class CurtainsComponent : AddonComponent, IDyable { base.OnDoubleClick(from); - if (Addon == null || !from.InRange(Location, 1)) + if (Addon != null) { - return; - } - - foreach (var c in Addon.Components) - { - if (c is CurtainsComponent curtain) + if (from.InRange(Location, 1)) { - (curtain.ItemID, curtain.ClosedId) = (curtain.ClosedId, curtain.ItemID); - } - else - { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + foreach (var c in Addon.Components) + { + if (c is CurtainsComponent curtain) + { + (curtain.ItemID, curtain.ClosedId) = (curtain.ClosedId, curtain.ItemID); + } + else + { + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + } + } } } } @@ -76,14 +78,14 @@ public partial class CurtainsAddon : BaseAddon } [SerializationGenerator(0)] -public partial class CurtainsDeed : BaseAddonDeed, IDirectionAddonDeed +public partial class CurtainsDeed : BaseAddonDeed { - public bool East { get; set; } + private bool _east; [Constructible] public CurtainsDeed() => LootType = LootType.Blessed; - public override BaseAddon Addon => new CurtainsAddon(East); + public override BaseAddon Addon => new CurtainsAddon(_east); public override int LabelNumber => 1076280; // Curtains public override void OnDoubleClick(Mobile from) @@ -98,17 +100,49 @@ public partial class CurtainsDeed : BaseAddonDeed, IDirectionAddonDeed } } - public void SendTarget(Mobile m) + private void SendTarget(Mobile m) { base.OnDoubleClick(m); } - private class InternalGump : SelectAddonDirectionGump + private class InternalGump : Gump { - public InternalGump(IDirectionAddonDeed deed) : base(deed) + private readonly CurtainsDeed _deed; + + public override bool Singleton => true; + + public InternalGump(CurtainsDeed deed) : base(60, 36) { + _deed = deed; + + AddPage(0); + + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 273, 20, 1076581, 0x7FFF); // Please select your curtain position + + AddPage(1); + + AddButton(19, 49, 0x845, 0x846, 1); + AddHtmlLocalized(44, 47, 213, 20, 1075386, 0x7FFF); // South + AddButton(19, 73, 0x845, 0x846, 2); + AddHtmlLocalized(44, 71, 213, 20, 1075387, 0x7FFF); // East } - public override int SelectionNumber => 1076581; // Please select your curtain position + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (_deed?.Deleted != false || info.ButtonID == 0) + { + return; + } + + _deed._east = info.ButtonID != 1; + _deed.SendTarget(sender.Mobile); + } } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs b/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs index fabb756d8..a93e7dd2e 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/FruitTrees.cs @@ -14,50 +14,50 @@ public abstract partial class BaseFruitTreeAddon : BaseAddon public abstract override BaseAddonDeed Deed { get; } public abstract Item Fruit { get; } - [SerializableField(0, allowFieldChange: nameof(AllowFruitsChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _fruits; - - private bool AllowFruitsChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int Fruits { - value = Math.Max(value, 0); - return true; + get => _fruits; + set => _fruits = Math.Max(value, 0); } public override void OnComponentUsed(AddonComponent c, Mobile from) { - if (!from.InRange(c.Location, 2)) + if (from.InRange(c.Location, 2)) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } + if (_fruits > 0) + { + var fruit = Fruit; - if (_fruits <= 0) - { - from.SendLocalizedMessage(501017); // There is no more fruit on this tree - return; - } + if (fruit == null) + { + return; + } - var fruit = Fruit; + if (!from.PlaceInBackpack(fruit)) + { + fruit.Delete(); + from.SendLocalizedMessage(501015); // There is no room in your backpack for the fruit. + } + else + { + if (--Fruits == 0) + { + Timer.StartTimer(TimeSpan.FromMinutes(30), Respawn); + } - if (fruit == null) - { - return; - } - - if (!from.PlaceInBackpack(fruit)) - { - fruit.Delete(); - from.SendLocalizedMessage(501015); // There is no room in your backpack for the fruit. + from.SendLocalizedMessage(501016); // You pick some fruit and put it in your backpack. + } + } + else + { + from.SendLocalizedMessage(501017); // There is no more fruit on this tree + } } else { - if (--Fruits == 0) - { - Timer.StartTimer(TimeSpan.FromMinutes(30), Respawn); - } - - from.SendLocalizedMessage(501016); // You pick some fruit and put it in your backpack. + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/Guillotine.cs b/Projects/UOContent/Items/Special/Heritage Items/Guillotine.cs index 10a84e7cb..5890ac152 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/Guillotine.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/Guillotine.cs @@ -28,22 +28,23 @@ public partial class GuillotineAddon : BaseAddon public override void OnComponentUsed(AddonComponent c, Mobile from) { - if (!from.InRange(Location, 2)) + if (from.InRange(Location, 2)) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } + if (Utility.RandomBool()) + { + from.Location = Location; - if (Utility.RandomBool()) - { - from.Location = Location; - - Timer.StartTimer(TimeSpan.FromSeconds(0.5), () => Activate(c, from)); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), () => Activate(c, from)); + } + else + { + // Hmm... you suspect that if you used this again, it might hurt. + from.LocalOverheadMessage(MessageType.Regular, 0, 501777); + } } else { - // Hmm... you suspect that if you used this again, it might hurt. - from.LocalOverheadMessage(MessageType.Regular, 0, 501777); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/HangingAxes.cs b/Projects/UOContent/Items/Special/Heritage Items/HangingAxes.cs index d51757a3e..ce82e6ec2 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/HangingAxes.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/HangingAxes.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Gumps; +using Server.Network; namespace Server.Items; @@ -25,14 +26,14 @@ public partial class HangingAxesAddon : BaseAddon } [SerializationGenerator(0)] -public partial class HangingAxesDeed : BaseAddonDeed, IDirectionAddonDeed +public partial class HangingAxesDeed : BaseAddonDeed { - public bool East { get; set; } + private bool m_East; [Constructible] public HangingAxesDeed() => LootType = LootType.Blessed; - public override BaseAddon Addon => new HangingAxesAddon(East); + public override BaseAddon Addon => new HangingAxesAddon(m_East); public override int LabelNumber => 1076271; // Hanging Axes public override void OnDoubleClick(Mobile from) @@ -47,17 +48,49 @@ public partial class HangingAxesDeed : BaseAddonDeed, IDirectionAddonDeed } } - public void SendTarget(Mobile m) + private void SendTarget(Mobile m) { base.OnDoubleClick(m); } - private class InternalGump : SelectAddonDirectionGump + private class InternalGump : Gump { - public InternalGump(IDirectionAddonDeed deed) : base(deed) + private readonly HangingAxesDeed m_Deed; + + public override bool Singleton => true; + + public InternalGump(HangingAxesDeed deed) : base(60, 36) { + m_Deed = deed; + + AddPage(0); + + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 273, 20, 1076745, 0x7FFF); // Please select your hanging axe position + + AddPage(1); + + AddButton(19, 49, 0x845, 0x846, 1); + AddHtmlLocalized(44, 47, 213, 20, 1075386, 0x7FFF); // South + AddButton(19, 73, 0x845, 0x846, 2); + AddHtmlLocalized(44, 71, 213, 20, 1075387, 0x7FFF); // East } - public override int SelectionNumber => 1076745; // Please select your hanging axe position + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (m_Deed?.Deleted != false || info.ButtonID == 0) + { + return; + } + + m_Deed.m_East = info.ButtonID != 1; + m_Deed.SendTarget(sender.Mobile); + } } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/HangingSwords.cs b/Projects/UOContent/Items/Special/Heritage Items/HangingSwords.cs index 1a251720a..3d23fc936 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/HangingSwords.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/HangingSwords.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Gumps; +using Server.Network; namespace Server.Items; @@ -25,14 +26,14 @@ public partial class HangingSwordsAddon : BaseAddon } [SerializationGenerator(0)] -public partial class HangingSwordsDeed : BaseAddonDeed, IDirectionAddonDeed +public partial class HangingSwordsDeed : BaseAddonDeed { - public bool East { get; set; } + private bool _east; [Constructible] public HangingSwordsDeed() => LootType = LootType.Blessed; - public override BaseAddon Addon => new HangingSwordsAddon(East); + public override BaseAddon Addon => new HangingSwordsAddon(_east); public override int LabelNumber => 1076272; // Hanging Swords public override void OnDoubleClick(Mobile from) @@ -47,17 +48,49 @@ public partial class HangingSwordsDeed : BaseAddonDeed, IDirectionAddonDeed } } - public void SendTarget(Mobile m) + private void SendTarget(Mobile m) { base.OnDoubleClick(m); } - private class InternalGump : SelectAddonDirectionGump + private class InternalGump : Gump { - public InternalGump(IDirectionAddonDeed deed) : base(deed) + private readonly HangingSwordsDeed m_Deed; + + public override bool Singleton => true; + + public InternalGump(HangingSwordsDeed deed) : base(60, 36) { + m_Deed = deed; + + AddPage(0); + + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 273, 20, 1076746, 0x7FFF); // Please select your hanging sword position + + AddPage(1); + + AddButton(19, 49, 0x845, 0x846, 1); + AddHtmlLocalized(44, 47, 213, 20, 1075386, 0x7FFF); // South + AddButton(19, 73, 0x845, 0x846, 2); + AddHtmlLocalized(44, 71, 213, 20, 1075387, 0x7FFF); // East } - public override int SelectionNumber => 1076746; // Please select your hanging sword position + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (m_Deed?.Deleted != false || info.ButtonID == 0) + { + return; + } + + m_Deed._east = info.ButtonID != 1; + m_Deed.SendTarget(sender.Mobile); + } } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/HouseLadder.cs b/Projects/UOContent/Items/Special/Heritage Items/HouseLadder.cs index 19cc0b684..f1a36692e 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/HouseLadder.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/HouseLadder.cs @@ -94,47 +94,46 @@ public partial class HouseLadderDeed : BaseAddonDeed base.OnDoubleClick(m); } - private class InternalGump : StaticGump + private class InternalGump : Gump { private readonly HouseLadderDeed _deed; public override bool Singleton => true; - public InternalGump(HouseLadderDeed deed) : base(60, 36) => _deed = deed; - - protected override void BuildLayout(ref StaticGumpBuilder builder) + public InternalGump(HouseLadderDeed deed) : base(60, 36) { - builder.AddPage(); + _deed = deed; - builder.AddBackground(0, 0, 273, 324, 0x13BE); - builder.AddImageTiled(10, 10, 253, 20, 0xA40); - builder.AddImageTiled(10, 40, 253, 244, 0xA40); - builder.AddImageTiled(10, 294, 253, 20, 0xA40); - builder.AddAlphaRegion(10, 10, 253, 304); - builder.AddButton(10, 294, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddPage(0); + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL // Please select your ladder position.
Use the ladders marked (castle)
for accessing the tops of keeps
and castles. - builder.AddHtmlLocalized(14, 12, 273, 20, 1076780, 0x7FFF); + AddHtmlLocalized(14, 12, 273, 20, 1076780, 0x7FFF); - builder.AddPage(1); + AddPage(1); - builder.AddButton(19, 49, 0x845, 0x846, 1); - builder.AddHtmlLocalized(44, 47, 213, 20, 1076794, 0x7FFF); // South (Castle) - builder.AddButton(19, 73, 0x845, 0x846, 2); - builder.AddHtmlLocalized(44, 71, 213, 20, 1076795, 0x7FFF); // East (Castle) - builder.AddButton(19, 97, 0x845, 0x846, 3); - builder.AddHtmlLocalized(44, 95, 213, 20, 1076792, 0x7FFF); // North (Castle) - builder.AddButton(19, 121, 0x845, 0x846, 4); - builder.AddHtmlLocalized(44, 119, 213, 20, 1076793, 0x7FFF); // West (Castle) - builder.AddButton(19, 145, 0x845, 0x846, 5); - builder.AddHtmlLocalized(44, 143, 213, 20, 1075386, 0x7FFF); // South - builder.AddButton(19, 169, 0x845, 0x846, 6); - builder.AddHtmlLocalized(44, 167, 213, 20, 1075387, 0x7FFF); // East - builder.AddButton(19, 193, 0x845, 0x846, 7); - builder.AddHtmlLocalized(44, 191, 213, 20, 1075389, 0x7FFF); // North - builder.AddButton(19, 217, 0x845, 0x846, 8); - builder.AddHtmlLocalized(44, 215, 213, 20, 1075390, 0x7FFF); // West + AddButton(19, 49, 0x845, 0x846, 1); + AddHtmlLocalized(44, 47, 213, 20, 1076794, 0x7FFF); // South (Castle) + AddButton(19, 73, 0x845, 0x846, 2); + AddHtmlLocalized(44, 71, 213, 20, 1076795, 0x7FFF); // East (Castle) + AddButton(19, 97, 0x845, 0x846, 3); + AddHtmlLocalized(44, 95, 213, 20, 1076792, 0x7FFF); // North (Castle) + AddButton(19, 121, 0x845, 0x846, 4); + AddHtmlLocalized(44, 119, 213, 20, 1076793, 0x7FFF); // West (Castle) + AddButton(19, 145, 0x845, 0x846, 5); + AddHtmlLocalized(44, 143, 213, 20, 1075386, 0x7FFF); // South + AddButton(19, 169, 0x845, 0x846, 6); + AddHtmlLocalized(44, 167, 213, 20, 1075387, 0x7FFF); // East + AddButton(19, 193, 0x845, 0x846, 7); + AddHtmlLocalized(44, 191, 213, 20, 1075389, 0x7FFF); // North + AddButton(19, 217, 0x845, 0x846, 8); + AddHtmlLocalized(44, 215, 213, 20, 1075390, 0x7FFF); // West } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Items/Special/Heritage Items/IronMaiden.cs b/Projects/UOContent/Items/Special/Heritage Items/IronMaiden.cs index ca8cf1a47..d4fc4c2cd 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/IronMaiden.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/IronMaiden.cs @@ -16,23 +16,24 @@ public partial class IronMaidenAddon : BaseAddon public override void OnComponentUsed(AddonComponent c, Mobile from) { - if (!from.InRange(GetWorldLocation(), 2) || !from.InLOS(GetWorldLocation())) + if (from.InRange(GetWorldLocation(), 2) && from.InLOS(GetWorldLocation())) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } + if (Utility.RandomBool()) + { + from.Location = Location; + c.ItemID = 0x124A; - if (Utility.RandomBool()) - { - from.Location = Location; - c.ItemID = 0x124A; - - Timer.StartTimer(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 3, () => Activate(c, from)); + Timer.StartTimer(TimeSpan.FromSeconds(0.5), TimeSpan.FromSeconds(0.5), 3, () => Activate(c, from)); + } + else + { + // Hmm... you suspect that if you used this again, it might hurt. + from.LocalOverheadMessage(MessageType.Regular, 0, 501777); + } } else { - // Hmm... you suspect that if you used this again, it might hurt. - from.LocalOverheadMessage(MessageType.Regular, 0, 501777); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/Statue.cs b/Projects/UOContent/Items/Special/Heritage Items/Statue.cs index 3ef76af94..a07b7980f 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/Statue.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/Statue.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Gumps; +using Server.Network; namespace Server.Items; @@ -27,14 +28,14 @@ public partial class StoneStatueAddon : BaseAddon } [SerializationGenerator(0)] -public partial class StoneStatueDeed : BaseAddonDeed, IDirectionAddonDeed +public partial class StoneStatueDeed : BaseAddonDeed { - public bool East { get; set; } + private bool _east; [Constructible] public StoneStatueDeed() => LootType = LootType.Blessed; - public override BaseAddon Addon => new StoneStatueAddon(East); + public override BaseAddon Addon => new StoneStatueAddon(_east); public override int LabelNumber => 1076284; // Statue public override void OnDoubleClick(Mobile from) @@ -49,17 +50,49 @@ public partial class StoneStatueDeed : BaseAddonDeed, IDirectionAddonDeed } } - public void SendTarget(Mobile m) + private void SendTarget(Mobile m) { base.OnDoubleClick(m); } - private class InternalGump : SelectAddonDirectionGump + private class InternalGump : Gump { - public InternalGump(IDirectionAddonDeed deed) : base(deed) + private readonly StoneStatueDeed _deed; + + public override bool Singleton => true; + + public InternalGump(StoneStatueDeed deed) : base(60, 36) { + _deed = deed; + + AddPage(0); + + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 273, 20, 1076579, 0x7FFF); // Please select your statue position + + AddPage(1); + + AddButton(19, 49, 0x845, 0x846, 1); + AddHtmlLocalized(44, 47, 213, 20, 1075386, 0x7FFF); // South + AddButton(19, 73, 0x845, 0x846, 2); + AddHtmlLocalized(44, 71, 213, 20, 1075387, 0x7FFF); // East } - public override int SelectionNumber => 1076579; // Please select your statue position + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (_deed?.Deleted != false || info.ButtonID == 0) + { + return; + } + + _deed._east = info.ButtonID != 1; + _deed.SendTarget(sender.Mobile); + } } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/UnmadeBed.cs b/Projects/UOContent/Items/Special/Heritage Items/UnmadeBed.cs index 2fa5b5b6f..ca28c4312 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/UnmadeBed.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/UnmadeBed.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Gumps; +using Server.Network; namespace Server.Items; @@ -29,14 +30,14 @@ public partial class UnmadeBedAddon : BaseAddon } [SerializationGenerator(0)] -public partial class UnmadeBedDeed : BaseAddonDeed, IDirectionAddonDeed +public partial class UnmadeBedDeed : BaseAddonDeed { - public bool East { get; set; } + private bool _east; [Constructible] public UnmadeBedDeed() => LootType = LootType.Blessed; - public override BaseAddon Addon => new UnmadeBedAddon(East); + public override BaseAddon Addon => new UnmadeBedAddon(_east); public override int LabelNumber => 1076279; // Unmade Bed public override void OnDoubleClick(Mobile from) @@ -51,18 +52,49 @@ public partial class UnmadeBedDeed : BaseAddonDeed, IDirectionAddonDeed } } - public void SendTarget(Mobile m) + private void SendTarget(Mobile m) { base.OnDoubleClick(m); } - private class InternalGump : SelectAddonDirectionGump + private class InternalGump : Gump { - public InternalGump(IDirectionAddonDeed deed) : base(deed) + private readonly UnmadeBedDeed _deed; + + public override bool Singleton => true; + + public InternalGump(UnmadeBedDeed deed) : base(60, 36) { + _deed = deed; + + AddPage(0); + + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 273, 20, 1076580, 0x7FFF); // Please select your unmade bed position + + AddPage(1); + + AddButton(19, 49, 0x845, 0x846, 1); + AddHtmlLocalized(44, 47, 213, 20, 1075386, 0x7FFF); // South + AddButton(19, 73, 0x845, 0x846, 2); + AddHtmlLocalized(44, 71, 213, 20, 1075387, 0x7FFF); // East } - // Misspelled in cliloc - public override int SelectionNumber => 1076580; // Pleae select your unmade bed position + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (_deed?.Deleted != false || info.ButtonID == 0) + { + return; + } + + _deed._east = info.ButtonID != 1; + _deed.SendTarget(sender.Mobile); + } } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/Vanity.cs b/Projects/UOContent/Items/Special/Heritage Items/Vanity.cs index e001ecd31..6dfcc3fff 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/Vanity.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/Vanity.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Gumps; +using Server.Network; namespace Server.Items; @@ -26,14 +27,14 @@ public partial class VanityAddon : BaseAddonContainer } [SerializationGenerator(0)] -public partial class VanityDeed : BaseAddonContainerDeed, IDirectionAddonDeed +public partial class VanityDeed : BaseAddonContainerDeed { - public bool East { get; set; } + private bool m_East; [Constructible] public VanityDeed() => LootType = LootType.Blessed; - public override BaseAddonContainer Addon => new VanityAddon(East); + public override BaseAddonContainer Addon => new VanityAddon(m_East); public override int LabelNumber => 1074027; // Vanity public override void OnDoubleClick(Mobile from) @@ -48,17 +49,49 @@ public partial class VanityDeed : BaseAddonContainerDeed, IDirectionAddonDeed } } - public void SendTarget(Mobile m) + private void SendTarget(Mobile m) { base.OnDoubleClick(m); } - private class InternalGump : SelectAddonDirectionGump + private class InternalGump : Gump { - public InternalGump(IDirectionAddonDeed deed) : base(deed) + private readonly VanityDeed _deed; + + public override bool Singleton => true; + + public InternalGump(VanityDeed deed) : base(60, 36) { + _deed = deed; + + AddPage(0); + + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 273, 20, 1076744, 0x7FFF); // Please select your vanity position. + + AddPage(1); + + AddButton(19, 49, 0x845, 0x846, 1); + AddHtmlLocalized(44, 47, 213, 20, 1075386, 0x7FFF); // South + AddButton(19, 73, 0x845, 0x846, 2); + AddHtmlLocalized(44, 71, 213, 20, 1075387, 0x7FFF); // East } - public override int SelectionNumber => 1076744; // Please select your vanity position. + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (_deed?.Deleted != false || info.ButtonID == 0) + { + return; + } + + _deed.m_East = info.ButtonID != 1; + _deed.SendTarget(sender.Mobile); + } } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/WallTorch.cs b/Projects/UOContent/Items/Special/Heritage Items/WallTorch.cs index 0fd529525..15f844459 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/WallTorch.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/WallTorch.cs @@ -14,22 +14,23 @@ public partial class WallTorchComponent : AddonComponent public override void OnDoubleClick(Mobile from) { - if (!from.InRange(Location, 2)) + if (from.InRange(Location, 2)) + { + ItemID = ItemID switch + { + 0x3D98 => 0x3D9B, + 0x3D9B => 0x3D98, + 0x3D94 => 0x3D97, + 0x3D97 => 0x3D94, + _ => ItemID + }; + + Effects.PlaySound(Location, Map, 0x3BE); + } + else { from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; } - - ItemID = ItemID switch - { - 0x3D98 => 0x3D9B, - 0x3D9B => 0x3D98, - 0x3D94 => 0x3D97, - 0x3D97 => 0x3D94, - _ => ItemID - }; - - Effects.PlaySound(Location, Map, 0x3BE); } } diff --git a/Projects/UOContent/Items/Special/Heritage Items/WoodenCoffin.cs b/Projects/UOContent/Items/Special/Heritage Items/WoodenCoffin.cs index cfbf8129e..b6cf3be6b 100644 --- a/Projects/UOContent/Items/Special/Heritage Items/WoodenCoffin.cs +++ b/Projects/UOContent/Items/Special/Heritage Items/WoodenCoffin.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Gumps; +using Server.Network; namespace Server.Items; @@ -37,14 +38,14 @@ public partial class WoodenCoffinAddon : BaseAddon } [SerializationGenerator(0)] -public partial class WoodenCoffinDeed : BaseAddonDeed, IDirectionAddonDeed +public partial class WoodenCoffinDeed : BaseAddonDeed { - public bool East { get; set; } + private bool m_East; [Constructible] public WoodenCoffinDeed() => LootType = LootType.Blessed; - public override BaseAddon Addon => new WoodenCoffinAddon(East); + public override BaseAddon Addon => new WoodenCoffinAddon(m_East); public override int LabelNumber => 1076274; // Coffin public override void OnDoubleClick(Mobile from) @@ -59,17 +60,49 @@ public partial class WoodenCoffinDeed : BaseAddonDeed, IDirectionAddonDeed } } - public void SendTarget(Mobile m) + private void SendTarget(Mobile m) { base.OnDoubleClick(m); } - private class InternalGump : SelectAddonDirectionGump + private class InternalGump : Gump { - public InternalGump(IDirectionAddonDeed deed) : base(deed) + private readonly WoodenCoffinDeed _deed; + + public override bool Singleton => true; + + public InternalGump(WoodenCoffinDeed deed) : base(60, 36) { + _deed = deed; + + AddPage(0); + + AddBackground(0, 0, 273, 324, 0x13BE); + AddImageTiled(10, 10, 253, 20, 0xA40); + AddImageTiled(10, 40, 253, 244, 0xA40); + AddImageTiled(10, 294, 253, 20, 0xA40); + AddAlphaRegion(10, 10, 253, 304); + AddButton(10, 294, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 296, 450, 20, 1060051, 0x7FFF); // CANCEL + AddHtmlLocalized(14, 12, 273, 20, 1076748, 0x7FFF); // Please select your coffin position + + AddPage(1); + + AddButton(19, 49, 0x845, 0x846, 1); + AddHtmlLocalized(44, 47, 213, 20, 1075386, 0x7FFF); // South + AddButton(19, 73, 0x845, 0x846, 2); + AddHtmlLocalized(44, 71, 213, 20, 1075387, 0x7FFF); // East } - public override int SelectionNumber => 1076748; // Please select your coffin position + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (_deed?.Deleted != false || info.ButtonID == 0) + { + return; + } + + _deed.m_East = info.ButtonID != 1; + _deed.SendTarget(sender.Mobile); + } } } diff --git a/Projects/UOContent/Items/Special/HeritageToken.cs b/Projects/UOContent/Items/Special/HeritageToken.cs index 8d92278c3..eb5aae65a 100644 --- a/Projects/UOContent/Items/Special/HeritageToken.cs +++ b/Projects/UOContent/Items/Special/HeritageToken.cs @@ -7,16 +7,19 @@ namespace Server.Items; public partial class HeritageToken : Item { [Constructible] - public HeritageToken() : base(0x367A) => LootType = LootType.Blessed; + public HeritageToken() : base(0x367A) + { + LootType = LootType.Blessed; + Weight = 5.0; + } - public override double DefaultWeight => 5.0; public override int LabelNumber => 1076596; // A Heritage Token public override void OnDoubleClick(Mobile from) { if (IsChildOf(from.Backpack)) { - HeritageTokenGump.DisplayTo(from, this); + from.SendGump(new HeritageTokenGump(this), true); } else { diff --git a/Projects/UOContent/Items/Special/Holiday/GiftBox.cs b/Projects/UOContent/Items/Special/Holiday/GiftBox.cs index a45702c03..d7081d1e9 100644 --- a/Projects/UOContent/Items/Special/Holiday/GiftBox.cs +++ b/Projects/UOContent/Items/Special/Holiday/GiftBox.cs @@ -13,7 +13,9 @@ public partial class GiftBox : BaseContainer } [Constructible] - public GiftBox(int hue) : base(Utility.Random(0x232A, 2)) => Hue = hue; - - public override double DefaultWeight => 2.0; + public GiftBox(int hue) : base(Utility.Random(0x232A, 2)) + { + Weight = 2.0; + Hue = hue; + } } diff --git a/Projects/UOContent/Items/Special/Holiday/GingerBreadHouseDeed.cs b/Projects/UOContent/Items/Special/Holiday/GingerBreadHouseDeed.cs index 4150d8d16..2121800fd 100644 --- a/Projects/UOContent/Items/Special/Holiday/GingerBreadHouseDeed.cs +++ b/Projects/UOContent/Items/Special/Holiday/GingerBreadHouseDeed.cs @@ -24,11 +24,10 @@ public partial class GingerBreadHouseDeed : BaseAddonDeed [Constructible] public GingerBreadHouseDeed() { + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1077394; // a Gingerbread House Deed public override BaseAddon Addon => new GingerBreadHouseAddon(); } diff --git a/Projects/UOContent/Items/Special/Holiday/HolidayPottedPlant.cs b/Projects/UOContent/Items/Special/Holiday/HolidayPottedPlant.cs index 33d63ed47..475c56491 100644 --- a/Projects/UOContent/Items/Special/Holiday/HolidayPottedPlant.cs +++ b/Projects/UOContent/Items/Special/Holiday/HolidayPottedPlant.cs @@ -33,7 +33,7 @@ public partial class PottedPlantDeed : Item { if (IsChildOf(from.Backpack)) { - HolidayPottedPlantGump.DisplayTo(from, this); + from.SendGump(new InternalGump(this)); } else { @@ -41,49 +41,41 @@ public partial class PottedPlantDeed : Item } } - private class HolidayPottedPlantGump : StaticGump + private class InternalGump : Gump { private readonly PottedPlantDeed _deed; public override bool Singleton => true; - private HolidayPottedPlantGump(PottedPlantDeed deed) : base(100, 200) + public InternalGump(PottedPlantDeed deed) : base(100, 200) { _deed = deed; - } - public static void DisplayTo(Mobile from, PottedPlantDeed deed) - { - if (from?.NetState == null || deed?.Deleted != false) - { - return; - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - from.SendGump(new HolidayPottedPlantGump(deed)); - } + AddPage(0); + AddBackground(0, 0, 360, 195, 0xA28); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 360, 195, 0xA28); + AddPage(1); + AddLabel(45, 15, 0, "Choose a Potted Plant:"); - builder.AddPage(1); - builder.AddLabel(45, 15, 0, "Choose a Potted Plant:"); + AddItem(45, 75, 0x11C8); + AddButton(55, 50, 0x845, 0x846, 1); - builder.AddItem(45, 75, 0x11C8); - builder.AddButton(55, 50, 0x845, 0x846, 1); + AddItem(100, 75, 0x11C9); + AddButton(115, 50, 0x845, 0x846, 2); - builder.AddItem(100, 75, 0x11C9); - builder.AddButton(115, 50, 0x845, 0x846, 2); + AddItem(160, 75, 0x11CA); + AddButton(175, 50, 0x845, 0x846, 3); - builder.AddItem(160, 75, 0x11CA); - builder.AddButton(175, 50, 0x845, 0x846, 3); + AddItem(225, 75, 0x11CB); + AddButton(235, 50, 0x845, 0x846, 4); - builder.AddItem(225, 75, 0x11CB); - builder.AddButton(235, 50, 0x845, 0x846, 4); - - builder.AddItem(280, 75, 0x11CC); - builder.AddButton(295, 50, 0x845, 0x846, 5); + AddItem(280, 75, 0x11CC); + AddButton(295, 50, 0x845, 0x846, 5); } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Items/Special/Holiday/HolidayTimepiece.cs b/Projects/UOContent/Items/Special/Holiday/HolidayTimepiece.cs index 085e60e94..e7b6d76b3 100644 --- a/Projects/UOContent/Items/Special/Holiday/HolidayTimepiece.cs +++ b/Projects/UOContent/Items/Special/Holiday/HolidayTimepiece.cs @@ -8,6 +8,7 @@ public partial class HolidayTimepiece : Clock [Constructible] public HolidayTimepiece() : base(0x1086) { + Weight = DefaultWeight; LootType = LootType.Blessed; Layer = Layer.Bracelet; } diff --git a/Projects/UOContent/Items/Special/Holiday/Poinsettias.cs b/Projects/UOContent/Items/Special/Holiday/Poinsettias.cs index 756b0a297..013814d79 100644 --- a/Projects/UOContent/Items/Special/Holiday/Poinsettias.cs +++ b/Projects/UOContent/Items/Special/Holiday/Poinsettias.cs @@ -8,10 +8,9 @@ public partial class RedPoinsettia : Item [Constructible] public RedPoinsettia() : base(0x2330) { + Weight = 1.0; LootType = LootType.Blessed; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -20,8 +19,7 @@ public partial class WhitePoinsettia : Item [Constructible] public WhitePoinsettia() : base(0x2331) { + Weight = 1.0; LootType = LootType.Blessed; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Special/Holiday/SnowStatue.cs b/Projects/UOContent/Items/Special/Holiday/SnowStatue.cs index ba80561d6..5ae65c20a 100644 --- a/Projects/UOContent/Items/Special/Holiday/SnowStatue.cs +++ b/Projects/UOContent/Items/Special/Holiday/SnowStatue.cs @@ -65,7 +65,7 @@ public partial class SnowStatueDeed : Item { if (IsChildOf(from.Backpack)) { - SnowStatueGump.DisplayTo(from, this); + from.SendGump(new InternalGump(from, this)); } else { @@ -73,46 +73,38 @@ public partial class SnowStatueDeed : Item } } - private class SnowStatueGump : StaticGump + private class InternalGump : Gump { private readonly SnowStatueDeed _deed; public override bool Singleton => true; - private SnowStatueGump(SnowStatueDeed deed) : base(100, 200) + public InternalGump(Mobile from, SnowStatueDeed deed) : base(100, 200) { _deed = deed; - } - public static void DisplayTo(Mobile from, SnowStatueDeed deed) - { - if (from?.NetState == null || deed?.Deleted != false) - { - return; - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - from.SendGump(new SnowStatueGump(deed)); - } + AddPage(0); + AddBackground(0, 0, 360, 225, 0xA28); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 360, 225, 0xA28); + AddPage(1); + AddHtmlLocalized(45, 15, 200, 20, 1156487); // Select One: - builder.AddPage(1); - builder.AddHtmlLocalized(45, 15, 200, 20, 1156487); // Select One: + AddItem(35, 75, 0x456E); + AddButton(65, 50, 0x845, 0x846, 1); - builder.AddItem(35, 75, 0x456E); - builder.AddButton(65, 50, 0x845, 0x846, 1); + AddItem(120, 75, 0x4578); + AddButton(135, 50, 0x845, 0x846, 2); - builder.AddItem(120, 75, 0x4578); - builder.AddButton(135, 50, 0x845, 0x846, 2); + AddItem(190, 75, 0x457A); + AddButton(205, 50, 0x845, 0x846, 3); - builder.AddItem(190, 75, 0x457A); - builder.AddButton(205, 50, 0x845, 0x846, 3); - - builder.AddItem(250, 75, 0x457C); - builder.AddButton(275, 50, 0x845, 0x846, 4); + AddItem(250, 75, 0x457C); + AddButton(275, 50, 0x845, 0x846, 4); } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Items/Special/Holiday/Snowflakes.cs b/Projects/UOContent/Items/Special/Holiday/Snowflakes.cs index f6f897b40..e08fcdf7e 100644 --- a/Projects/UOContent/Items/Special/Holiday/Snowflakes.cs +++ b/Projects/UOContent/Items/Special/Holiday/Snowflakes.cs @@ -8,10 +8,9 @@ public partial class BlueSnowflake : Item [Constructible] public BlueSnowflake() : base(0x232E) { + Weight = 1.0; LootType = LootType.Blessed; } - - public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -20,8 +19,7 @@ public partial class WhiteSnowflake : Item [Constructible] public WhiteSnowflake() : base(0x232F) { + Weight = 1.0; LootType = LootType.Blessed; } - - public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Special/Holiday/Snowman.cs b/Projects/UOContent/Items/Special/Holiday/Snowman.cs index 0f6d26871..ded511569 100644 --- a/Projects/UOContent/Items/Special/Holiday/Snowman.cs +++ b/Projects/UOContent/Items/Special/Holiday/Snowman.cs @@ -87,14 +87,13 @@ public partial class Snowman : Item, IDyable [Constructible] public Snowman(int hue, string title) : base(Utility.Random(0x2328, 2)) { + Weight = 10.0; Hue = hue; LootType = LootType.Blessed; _title = title; } - public override double DefaultWeight => 10.0; - public bool Dye(Mobile from, DyeTub sender) { if (Deleted) diff --git a/Projects/UOContent/Items/Special/Holiday/Wreath.cs b/Projects/UOContent/Items/Special/Holiday/Wreath.cs index f2513db13..f5de38f7c 100644 --- a/Projects/UOContent/Items/Special/Holiday/Wreath.cs +++ b/Projects/UOContent/Items/Special/Holiday/Wreath.cs @@ -56,6 +56,7 @@ public partial class WreathAddon : Item, IDyable, IAddon } from.SendLocalizedMessage(500295); // You are too far away to do that. + return false; } return false; @@ -91,69 +92,60 @@ public partial class WreathAddon : Item, IDyable, IAddon { var house = BaseHouse.FindHouseAt(this); - if (house?.IsCoOwner(from) != true) + if (house?.IsCoOwner(from) == true) { - return; - } - - if (from.InRange(GetWorldLocation(), 3)) - { - WreathAddonGump.DisplayTo(from, this); - } - else - { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + if (from.InRange(GetWorldLocation(), 3)) + { + from.SendGump(new WreathAddonGump(from, this)); + } + else + { + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + } } } - private class WreathAddonGump : StaticGump + private class WreathAddonGump : Gump { private readonly WreathAddon _addon; + private readonly Mobile _from; public override bool Singleton => true; - private WreathAddonGump(WreathAddon addon) : base(150, 50) => _addon = addon; - - public static void DisplayTo(Mobile from, WreathAddon addon) + public WreathAddonGump(Mobile from, WreathAddon addon) : base(150, 50) { - if (from?.NetState == null || addon?.Deleted != false) - { - return; - } + _from = from; + _addon = addon; - from.SendGump(new WreathAddonGump(addon)); - } + AddPage(0); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 220, 170, 0x13BE); - builder.AddBackground(10, 10, 200, 150, 0xBB8); - builder.AddHtmlLocalized(20, 30, 180, 60, 1062839); // Do you wish to re-deed this decoration? - builder.AddHtmlLocalized(55, 100, 160, 25, 1011011); // CONTINUE - builder.AddButton(20, 100, 0xFA5, 0xFA7, 1); - builder.AddHtmlLocalized(55, 125, 160, 25, 1011012); // CANCEL - builder.AddButton(20, 125, 0xFA5, 0xFA7, 0); + AddBackground(0, 0, 220, 170, 0x13BE); + AddBackground(10, 10, 200, 150, 0xBB8); + AddHtmlLocalized(20, 30, 180, 60, 1062839); // Do you wish to re-deed this decoration? + AddHtmlLocalized(55, 100, 160, 25, 1011011); // CONTINUE + AddButton(20, 100, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(55, 125, 160, 25, 1011012); // CANCEL + AddButton(20, 125, 0xFA5, 0xFA7, 0); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_addon.Deleted || info.ButtonID != 1) + if (_addon.Deleted) { return; } - var from = sender.Mobile; - - if (from.InRange(_addon.GetWorldLocation(), 3)) + if (info.ButtonID == 1) { - from.AddToBackpack(_addon.Deed); - _addon.Delete(); - } - else - { - from.SendLocalizedMessage(500295); // You are too far away to do that. + if (_from.InRange(_addon.GetWorldLocation(), 3)) + { + _from.AddToBackpack(_addon.Deed); + _addon.Delete(); + } + else + { + _from.SendLocalizedMessage(500295); // You are too far away to do that. + } } } } @@ -171,12 +163,11 @@ public partial class WreathDeed : Item [Constructible] public WreathDeed(int hue) : base(0x14F0) { + Weight = 1.0; Hue = hue; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1062837; // holiday wreath deed public override void OnDoubleClick(Mobile from) @@ -219,7 +210,7 @@ public partial class WreathDeed : Item if (northWall && westWall) { - from.SendGump(new WreathDeedGump(loc, this)); + from.SendGump(new WreathDeedGump(from, loc, this)); } else { @@ -264,56 +255,57 @@ public partial class WreathDeed : Item if (itemID > 0) { - var addon = new WreathAddon(Hue) - { - ItemID = itemID - }; + Item addon = new WreathAddon(Hue); + addon.ItemID = itemID; addon.MoveToWorld(loc, from.Map); + house.Addons.Add(addon); Delete(); } } - private class WreathDeedGump : StaticGump + private class WreathDeedGump : Gump { private readonly WreathDeed _deed; + private readonly Mobile _from; private readonly Point3D _loc; - public WreathDeedGump(Point3D loc, WreathDeed deed) : base(150, 50) + public WreathDeedGump(Mobile from, Point3D loc, WreathDeed deed) : base(150, 50) { + _from = from; _loc = loc; _deed = deed; - } - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddBackground(0, 0, 300, 150, 0xA28); + AddBackground(0, 0, 300, 150, 0xA28); - builder.AddPage(); + AddPage(0); - builder.AddItem(90, 30, 0x232D); - builder.AddItem(180, 30, 0x232C); - builder.AddButton(50, 35, 0x868, 0x869, 1); - builder.AddButton(145, 35, 0x868, 0x869, 2); + AddItem(90, 30, 0x232D); + AddItem(180, 30, 0x232C); + AddButton(50, 35, 0x868, 0x869, 1); + AddButton(145, 35, 0x868, 0x869, 2); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_deed.Deleted || info.ButtonID == 0) + if (_deed.Deleted) { return; } - var from = sender.Mobile; - - if (info.ButtonID == 1) + switch (info.ButtonID) { - _deed.PlaceAddon(from, _loc, false, true); - } - else - { - _deed.PlaceAddon(from, _loc, true, false); + case 1: + { + _deed.PlaceAddon(_from, _loc, false, true); + break; + } + case 2: + { + _deed.PlaceAddon(_from, _loc, true, false); + break; + } } } } diff --git a/Projects/UOContent/Items/Special/Holiday/WristWatch.cs b/Projects/UOContent/Items/Special/Holiday/WristWatch.cs index 6df024187..62dc9e1d0 100644 --- a/Projects/UOContent/Items/Special/Holiday/WristWatch.cs +++ b/Projects/UOContent/Items/Special/Holiday/WristWatch.cs @@ -8,6 +8,7 @@ public partial class WristWatch : Clock [Constructible] public WristWatch() : base(0x1086) { + Weight = DefaultWeight; LootType = LootType.Blessed; Layer = Layer.Bracelet; } diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleDeed.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleDeed.cs index 73e7531cb..0d7f1f9bf 100644 --- a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleDeed.cs +++ b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleDeed.cs @@ -84,7 +84,7 @@ public partial class HouseRaffleDeed : Item if (IsChildOf(from.Backpack)) { - WritOfLeaseGump.DisplayTo(from, this); + from.SendGump(new WritOfLeaseGump(this)); } else { @@ -92,49 +92,32 @@ public partial class HouseRaffleDeed : Item } } - private class WritOfLeaseGump : DynamicGump + private class WritOfLeaseGump : Gump { - private readonly HouseRaffleDeed _deed; - public override bool Singleton => true; - private WritOfLeaseGump(HouseRaffleDeed deed) : base(150, 50) + public WritOfLeaseGump(HouseRaffleDeed deed) : base(150, 50) { - _deed = deed; - } + AddPage(0); - public static void DisplayTo(Mobile from, HouseRaffleDeed deed) - { - if (from?.NetState == null || deed?.Deleted != false) - { - return; - } + AddImage(0, 0, 9380); + AddImage(114, 0, 9381); + AddImage(171, 0, 9382); + AddImage(0, 140, 9383); + AddImage(114, 140, 9384); + AddImage(171, 140, 9385); + AddImage(0, 182, 9383); + AddImage(114, 182, 9384); + AddImage(171, 182, 9385); + AddImage(0, 224, 9383); + AddImage(114, 224, 9384); + AddImage(171, 224, 9385); + AddImage(0, 266, 9386); + AddImage(114, 266, 9387); + AddImage(171, 266, 9388); - from.SendGump(new WritOfLeaseGump(deed)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddImage(0, 0, 9380); - builder.AddImage(114, 0, 9381); - builder.AddImage(171, 0, 9382); - builder.AddImage(0, 140, 9383); - builder.AddImage(114, 140, 9384); - builder.AddImage(171, 140, 9385); - builder.AddImage(0, 182, 9383); - builder.AddImage(114, 182, 9384); - builder.AddImage(171, 182, 9385); - builder.AddImage(0, 224, 9383); - builder.AddImage(114, 224, 9384); - builder.AddImage(171, 224, 9385); - builder.AddImage(0, 266, 9386); - builder.AddImage(114, 266, 9387); - builder.AddImage(171, 266, 9388); - - builder.AddHtmlLocalized(30, 48, 229, 20, 1150484, 200); // WRIT OF LEASE - builder.AddHtml(28, 75, 231, 280, FormatDescription(_deed), background: false, scrollbar: true); + AddHtmlLocalized(30, 48, 229, 20, 1150484, 200); // WRIT OF LEASE + AddHtml(28, 75, 231, 280, FormatDescription(deed), false, true); } private static string FormatDescription(HouseRaffleDeed deed) diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleManagementGump.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleManagementGump.cs index 751f92d6c..dd39c9ba7 100644 --- a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleManagementGump.cs +++ b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleManagementGump.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using Server.Accounting; using Server.Items; @@ -6,7 +5,7 @@ using Server.Network; namespace Server.Gumps; -public class HouseRaffleManagementGump : DynamicGump +public class HouseRaffleManagementGump : Gump { public enum SortMethod { @@ -18,167 +17,111 @@ public class HouseRaffleManagementGump : DynamicGump public const int LabelColor = 0xFFFFFF; public const int HighlightColor = 0x11EE11; - - private enum Buttons - { - Close, - PrevPage, - NextPage, - SortByName, - SortByAccount, - SortByAddress, - Refresh, - Delete // per-row delete buttons start here: (int)Buttons.Delete + row index - } - - private const int RowsPerPage = 10; + private readonly List _list; + private readonly SortMethod _sort; private readonly HouseRaffleStone _stone; - // Cached, sorted snapshot of _stone.Entries. Page navigation, sort, and delete operate on - // this snapshot so paginated indices stay stable even while other staff or players add or - // remove entries on the live stone. Staff hits the Refresh button (or reopens the gump) to - // resync. _count is the logical length; _entries backing array is reused across responses. - private RaffleEntry[] _entries; - private int _count; - private SortMethod _sort; private int _page; - public override bool Singleton => true; - - private HouseRaffleManagementGump(HouseRaffleStone stone, SortMethod sort = SortMethod.Default, int page = 0) + public HouseRaffleManagementGump(HouseRaffleStone stone, SortMethod sort = SortMethod.Default, int page = 0) : base(40, 40) { _stone = stone; _page = page; + + _list = new List(_stone.Entries); _sort = sort; - _entries = _stone.Entries.ToArray(); - _count = _entries.Length; - SortSnapshot(); - } - - public static void DisplayTo(Mobile from, HouseRaffleStone stone, SortMethod sort = SortMethod.Default, int page = 0) - { - if (from?.NetState != null && stone != null && !stone.Deleted) + switch (_sort) { - from.SendGump(new HouseRaffleManagementGump(stone, sort, page)); - } - } + case SortMethod.Name: + { + _list.Sort(NameComparer.Instance); - private void SortSnapshot() - { - if (_sort == SortMethod.Default || _count <= 1) - { - return; + break; + } + case SortMethod.Account: + { + _list.Sort(AccountComparer.Instance); + + break; + } + case SortMethod.Address: + { + _list.Sort(AddressComparer.Instance); + + break; + } } - var sortMethod = _sort switch + AddPage(0); + + AddBackground(0, 0, 618, 354, 9270); + AddAlphaRegion(10, 10, 598, 334); + + AddHtml(10, 10, 598, 20, "Raffle Management".Center(LabelColor)); + + AddHtml(45, 35, 100, 20, "Location:".Color(LabelColor)); + AddHtml(145, 35, 250, 20, HouseRaffleStone.FormatLocation(stone.PlotBounds, stone.GetPlotCenter(), stone.PlotFacet).Color(LabelColor)); + + AddHtml(45, 55, 100, 20, "Ticket Price:".Color(LabelColor)); + AddHtml(145, 55, 250, 20, HouseRaffleStone.FormatPrice(stone.TicketPrice).Color(LabelColor)); + + AddHtml(45, 75, 100, 20, "Total Entries:".Color(LabelColor)); + AddHtml(145, 75, 250, 20, _stone.Entries.Count.ToString().Color(LabelColor)); + + AddButton(440, 33, 0xFA5, 0xFA7, 3); + AddHtml(474, 35, 120, 20, "Sort by name".Color(LabelColor)); + + AddButton(440, 53, 0xFA5, 0xFA7, 4); + AddHtml(474, 55, 120, 20, "Sort by account".Color(LabelColor)); + + AddButton(440, 73, 0xFA5, 0xFA7, 5); + AddHtml(474, 75, 120, 20, "Sort by address".Color(LabelColor)); + + AddImageTiled(13, 99, 592, 242, 9264); + AddImageTiled(14, 100, 590, 240, 9274); + AddAlphaRegion(14, 100, 590, 240); + + AddHtml(14, 100, 590, 20, "Entries".Center(LabelColor)); + + if (page > 0) { - SortMethod.Name => NameComparer.Instance, - SortMethod.Account => AccountComparer.Instance, - SortMethod.Address => AddressComparer.Instance - }; - - Array.Sort(_entries, 0, _count, sortMethod); - } - - private void RefreshSnapshot() - { - var live = _stone.Entries; - var liveCount = live.Count; - - if (_entries.Length < liveCount) - { - // Grow with doubling so subsequent inserts can fit without re-allocating each time. - Array.Resize(ref _entries, Math.Max(_entries.Length * 2, liveCount)); - } - - live.CopyTo(_entries, 0); - - // Clear any references in trailing slots so we don't pin deleted entries. - if (_count > liveCount) - { - Array.Clear(_entries, liveCount, _count - liveCount); - } - - _count = liveCount; - SortSnapshot(); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 618, 354, 9270); - builder.AddAlphaRegion(10, 10, 598, 334); - - builder.AddHtml(10, 10, 598, 20, "Raffle Management".Center(LabelColor)); - - builder.AddHtml(45, 35, 100, 20, "Location:".Color(LabelColor)); - builder.AddHtml(145, 35, 250, 20, HouseRaffleStone.FormatLocation(_stone.PlotBounds, _stone.GetPlotCenter(), _stone.PlotFacet).Color(LabelColor)); - - builder.AddHtml(45, 55, 100, 20, "Ticket Price:".Color(LabelColor)); - builder.AddHtml(145, 55, 250, 20, HouseRaffleStone.FormatPrice(_stone.TicketPrice).Color(LabelColor)); - - builder.AddHtml(45, 75, 100, 20, "Total Entries:".Color(LabelColor)); - builder.AddHtml(145, 75, 250, 20, Html.Color($"{_stone.Entries.Count}", LabelColor)); - - builder.AddButton(440, 33, 0xFA5, 0xFA7, (int)Buttons.SortByName); - builder.AddHtml(474, 35, 120, 20, "Sort by name".Color(LabelColor)); - - builder.AddButton(440, 53, 0xFA5, 0xFA7, (int)Buttons.SortByAccount); - builder.AddHtml(474, 55, 120, 20, "Sort by account".Color(LabelColor)); - - builder.AddButton(440, 73, 0xFA5, 0xFA7, (int)Buttons.SortByAddress); - builder.AddHtml(474, 75, 120, 20, "Sort by address".Color(LabelColor)); - - builder.AddImageTiled(13, 99, 592, 242, 9264); - builder.AddImageTiled(14, 100, 590, 240, 9274); - builder.AddAlphaRegion(14, 100, 590, 240); - - builder.AddHtml(14, 100, 590, 20, "Entries".Center(LabelColor)); - - builder.AddButton(545, 104, 0x845, 0x846, (int)Buttons.Refresh); - - if (_page > 0) - { - builder.AddButton(567, 104, 0x15E3, 0x15E7, (int)Buttons.PrevPage); + AddButton(567, 104, 0x15E3, 0x15E7, 1); } else { - builder.AddImage(567, 104, 0x25EA); + AddImage(567, 104, 0x25EA); } - if ((_page + 1) * RowsPerPage < _count) + if ((page + 1) * 10 < _list.Count) { - builder.AddButton(584, 104, 0x15E1, 0x15E5, (int)Buttons.NextPage); + AddButton(584, 104, 0x15E1, 0x15E5, 2); } else { - builder.AddImage(584, 104, 0x25E6); + AddImage(584, 104, 0x25E6); } - builder.AddHtml(14, 120, 30, 20, "DEL".Center(LabelColor)); - builder.AddHtml(47, 120, 250, 20, "Name".Color(LabelColor)); - builder.AddHtml(295, 120, 100, 20, "Address".Center(LabelColor)); - builder.AddHtml(395, 120, 150, 20, "Date".Center(LabelColor)); - builder.AddHtml(545, 120, 60, 20, "Num".Center(LabelColor)); + AddHtml(14, 120, 30, 20, "DEL".Center(LabelColor)); + AddHtml(47, 120, 250, 20, "Name".Color(LabelColor)); + AddHtml(295, 120, 100, 20, "Address".Center(LabelColor)); + AddHtml(395, 120, 150, 20, "Date".Center(LabelColor)); + AddHtml(545, 120, 60, 20, "Num".Center(LabelColor)); var idx = 0; var winner = _stone.Winner; - var pageStart = _page * RowsPerPage; - var pageEnd = Math.Min(pageStart + RowsPerPage, _count); - for (var i = pageStart; i < pageEnd; ++i, ++idx) + for (var i = page * 10; i >= 0 && i < _list.Count && i < (page + 1) * 10; ++i, ++idx) { - var entry = _entries[i]; + var entry = _list[i]; if (entry == null) { continue; } - builder.AddButton(13, 138 + idx * 20, 4002, 4004, (int)Buttons.Delete + i); + AddButton(13, 138 + idx * 20, 4002, 4004, 6 + i); var x = 45; var color = winner != null && entry.From == winner ? HighlightColor : LabelColor; @@ -187,11 +130,11 @@ public class HouseRaffleManagementGump : DynamicGump { if (entry.From.Account is Account acc) { - builder.AddHtml(x + 2, 140 + idx * 20, 250, 20, Html.Color($"{entry.From.RawName} ({acc})", color)); + AddHtml(x + 2, 140 + idx * 20, 250, 20, $"{entry.From.RawName} ({acc})".Color(color)); } else { - builder.AddHtml(x + 2, 140 + idx * 20, 250, 20, entry.From.RawName.Color(color)); + AddHtml(x + 2, 140 + idx * 20, 250, 20, entry.From.RawName.Color(color)); } } @@ -199,119 +142,84 @@ public class HouseRaffleManagementGump : DynamicGump if (entry.Address != null) { - builder.AddHtml(x, 140 + idx * 20, 100, 20, Html.Center($"{entry.Address}", color)); + AddHtml(x, 140 + idx * 20, 100, 20, entry.Address.ToString().Center(color)); } x += 100; - builder.AddHtml(x, 140 + idx * 20, 150, 20, Html.Center($"{entry.Date}", color)); + AddHtml(x, 140 + idx * 20, 150, 20, entry.Date.ToString().Center(color)); x += 150; - builder.AddHtml(x, 140 + idx * 20, 60, 20, "1".Center(color)); + AddHtml(x, 140 + idx * 20, 60, 20, "1".Center(color)); } } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_stone.Deleted) - { - return; - } + var from = sender.Mobile; + var buttonId = info.ButtonID; - switch ((Buttons)info.ButtonID) + switch (buttonId) { - case Buttons.Close: - { - return; - } - case Buttons.PrevPage: + case 1: // Previous { if (_page > 0) { _page--; } + from.SendGump(new HouseRaffleManagementGump(_stone, _sort, _page)); + break; } - case Buttons.NextPage: + case 2: // Next { - if ((_page + 1) * RowsPerPage < _count) + if ((_page + 1) * 10 < _stone.Entries.Count) { _page++; } - break; - } - case Buttons.SortByName: - { - _sort = SortMethod.Name; - _page = 0; - SortSnapshot(); + from.SendGump(new HouseRaffleManagementGump(_stone, _sort, _page)); break; } - case Buttons.SortByAccount: + case 3: // Sort by name { - _sort = SortMethod.Account; - _page = 0; - SortSnapshot(); + from.SendGump(new HouseRaffleManagementGump(_stone, SortMethod.Name)); break; } - case Buttons.SortByAddress: + case 4: // Sort by account { - _sort = SortMethod.Address; - _page = 0; - SortSnapshot(); + from.SendGump(new HouseRaffleManagementGump(_stone, SortMethod.Account)); break; } - case Buttons.Refresh: + case 5: // Sort by address { - RefreshSnapshot(); - - var maxPage = _count == 0 ? 0 : (_count - 1) / RowsPerPage; - if (_page > maxPage) - { - _page = maxPage; - } + from.SendGump(new HouseRaffleManagementGump(_stone, SortMethod.Address)); break; } - default: // Per-row delete + default: // Delete { - var deleteIndex = info.ButtonID - (int)Buttons.Delete; + buttonId -= 6; - if (deleteIndex < 0 || deleteIndex >= _count) + if (buttonId >= 0 && buttonId < _list.Count) { - return; - } + _stone.Entries.Remove(_list[buttonId]); - var target = _entries[deleteIndex]; + if (_page > 0 && _page * 10 >= _list.Count - 1) + { + _page--; + } - if (target != null) - { - _stone.Entries.Remove(target); - } - - // Compact the snapshot in place — array stays sorted, no reallocation. - var tail = _count - deleteIndex - 1; - if (tail > 0) - { - Array.Copy(_entries, deleteIndex + 1, _entries, deleteIndex, tail); - } - _entries[--_count] = null; - - if (_page > 0 && _page * RowsPerPage >= _count) - { - _page--; + from.SendGump(new HouseRaffleManagementGump(_stone, _sort, _page)); } break; } } - - sender.Mobile.SendGump(this); } private class NameComparer : IComparer @@ -338,7 +246,7 @@ public class HouseRaffleManagementGump : DynamicGump return 1; } - var result = x.From.RawName.InsensitiveCompare(y.From.RawName); + var result = x.From.Name.InsensitiveCompare(y.From.Name); return result == 0 ? x.Date.CompareTo(y.Date) : result; } diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs index 59e2b8277..628d09457 100644 --- a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs +++ b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs @@ -146,24 +146,34 @@ public partial class HouseRaffleStone : Item } } - [SerializableField(3, fieldChanged: nameof(OnPlotBoundsChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] - [InvalidateProperties] - private Rectangle2D _plotBounds; - - private void OnPlotBoundsChanged(Rectangle2D oldValue, Rectangle2D newValue) + [SerializableProperty(3)] + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] + public Rectangle2D PlotBounds { - InvalidateRegion(); + get => _plotBounds; + set + { + _plotBounds = value; + + InvalidateRegion(); + InvalidateProperties(); + this.MarkDirty(); + } } - [SerializableField(4, fieldChanged: nameof(OnPlotFacetChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] - [InvalidateProperties] - private Map _plotFacet; - - private void OnPlotFacetChanged(Map oldValue, Map newValue) + [SerializableProperty(4)] + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] + public Map PlotFacet { - InvalidateRegion(); + get => _plotFacet; + set + { + _plotFacet = value; + + InvalidateRegion(); + InvalidateProperties(); + this.MarkDirty(); + } } [CommandProperty(AccessLevel.GameMaster)] @@ -180,22 +190,24 @@ public partial class HouseRaffleStone : Item } } - [SerializableField(6, allowFieldChange: nameof(AllowTicketPriceChange))] - [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] - [InvalidateProperties] - private int _ticketPrice; - - private bool AllowTicketPriceChange(ref int value) + [SerializableProperty(6)] + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Seer)] + public int TicketPrice { - value = Math.Max(0, value); - return true; + get => _ticketPrice; + set + { + _ticketPrice = Math.Max(0, value); + InvalidateProperties(); + this.MarkDirty(); + } } public override string DefaultName => "a house raffle stone"; public override bool DisplayWeight => false; - private static void CheckEnd_OnTick() + public static void CheckEnd_OnTick() { foreach (var stone in _allStones) { @@ -205,11 +217,12 @@ public partial class HouseRaffleStone : Item private static void AddRaffleStone(HouseRaffleStone stone) { - _allStones ??= []; + _allStones ??= new HashSet(); + _allStones.Add(stone); - if (_allStones.Add(stone) && _allStones.Count == 1) + if (_allStones.Count == 1) { - _allStonesTimer = Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), CheckEnd_OnTick); + Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0), CheckEnd_OnTick); } } @@ -292,11 +305,11 @@ public partial class HouseRaffleStone : Item public static string FormatLocation(Point3D loc, Map map, bool displayMap) { - using var result = new ValueStringBuilder(stackalloc char[48]); + using var result = ValueStringBuilder.Create(); var xLong = 0; var yLat = 0; - var xMins = 0; + int xMins = 0; var yMins = 0; bool xEast = false, ySouth = false; @@ -625,7 +638,7 @@ public partial class HouseRaffleStone : Item { if (from.AccessLevel >= AccessLevel.Seer && target is HouseRaffleStone { Deleted: false } stone) { - HouseRaffleManagementGump.DisplayTo(from, stone); + from.SendGump(new HouseRaffleManagementGump(stone)); } } } @@ -634,9 +647,10 @@ public partial class HouseRaffleStone : Item { public override int Header => 1150470; // CONFIRM TICKET PURCHASE public override int HeaderColor => 0x7F00; - public override string ContentColor => "#FFFFFF"; + public override int ContentColor => 0xFFFFFF; public override int Width => 420; public override int Height => 280; + public override string Content { get; } public ConfirmTicketPurchaseGump(Rectangle2D bounds, Point3D center, Map map, int price, Action callback) : base(callback) diff --git a/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs b/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs index 32ed9eda0..171ef0649 100644 --- a/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs +++ b/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class GrizzledMareStatuette : BaseImprisonedMobile { [Constructible] - public GrizzledMareStatuette() : base(0x2617) - { - } - - public override double DefaultWeight => 1.0; + public GrizzledMareStatuette() : base(0x2617) => Weight = 1.0; public override int LabelNumber => 1074475; // Grizzled Mare Statuette public override BaseCreature Summon => new GrizzledMare(); diff --git a/Projects/UOContent/Items/Special/ML/MinotaurHedge.cs b/Projects/UOContent/Items/Special/ML/MinotaurHedge.cs index fb25317f0..593a6586d 100644 --- a/Projects/UOContent/Items/Special/ML/MinotaurHedge.cs +++ b/Projects/UOContent/Items/Special/ML/MinotaurHedge.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class MinotaurHedge : Item { [Constructible] - public MinotaurHedge() : base(Utility.Random(3215, 4)) - { - } - - public override double DefaultWeight => 1.0; + public MinotaurHedge() : base(Utility.Random(3215, 4)) => Weight = 1.0; public override string DefaultName => "minotaur hedge"; } diff --git a/Projects/UOContent/Items/Special/ML/TormentedChains.cs b/Projects/UOContent/Items/Special/ML/TormentedChains.cs index 784da5d2e..a8d8a5f00 100644 --- a/Projects/UOContent/Items/Special/ML/TormentedChains.cs +++ b/Projects/UOContent/Items/Special/ML/TormentedChains.cs @@ -6,9 +6,5 @@ namespace Server.Items; public partial class TormentedChains : Item { [Constructible] - public TormentedChains() : base(Utility.Random(6663, 2)) - { - } - - public override double DefaultWeight => 1.0; + public TormentedChains() : base(Utility.Random(6663, 2)) => Weight = 1.0; } diff --git a/Projects/UOContent/Items/Special/MiniHouses.cs b/Projects/UOContent/Items/Special/MiniHouses.cs index 48ed9470c..5104dad33 100644 --- a/Projects/UOContent/Items/Special/MiniHouses.cs +++ b/Projects/UOContent/Items/Special/MiniHouses.cs @@ -74,11 +74,11 @@ public partial class MiniHouseDeed : BaseAddonDeed public MiniHouseDeed(MiniHouseType type = MiniHouseType.StoneAndPlaster) { _type = type; + + Weight = 1.0; LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override BaseAddon Addon => new MiniHouseAddon(_type); public override int LabelNumber => 1062096; // a mini house deed diff --git a/Projects/UOContent/Items/Special/MonsterStatuette.cs b/Projects/UOContent/Items/Special/MonsterStatuette.cs index fd26a0060..8d49dd0e4 100644 --- a/Projects/UOContent/Items/Special/MonsterStatuette.cs +++ b/Projects/UOContent/Items/Special/MonsterStatuette.cs @@ -162,15 +162,21 @@ public partial class MonsterStatuette : Item, IRewardItem, IGumpToggleItem _ => fallback }; - [SerializableField(0, fieldChanged: nameof(OnTypeChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private MonsterStatuetteType _type; - - private void OnTypeChanged(MonsterStatuetteType oldValue, MonsterStatuetteType newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public MonsterStatuetteType Type { - ItemID = MonsterStatuetteInfo.GetInfo(_type).ItemID; - Hue = GetStatuetteHue(_type, Hue); + get => _type; + set + { + _type = value; + ItemID = MonsterStatuetteInfo.GetInfo(_type).ItemID; + + Hue = GetStatuetteHue(_type, Hue); + + InvalidateProperties(); + this.MarkDirty(); + } } public override int LabelNumber => MonsterStatuetteInfo.GetInfo(_type).LabelNumber; diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastBackpack.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastBackpack.cs index 5aeeefa2c..f9681165c 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastBackpack.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastBackpack.cs @@ -125,7 +125,7 @@ public partial class PlagueBeastBackpack : BaseContainer return false; } - var ir = ItemBounds.Bounds[item.ItemID]; + var ir = ItemBounds.Table[item.ItemID]; int x, y; var cx = p.X + ir.X + ir.Width / 2; var cy = p.Y + ir.Y + ir.Height / 2; @@ -134,7 +134,7 @@ public partial class PlagueBeastBackpack : BaseContainer { if (Items[i] is PlagueBeastComponent innard) { - var r = ItemBounds.Bounds[innard.ItemID]; + var r = ItemBounds.Table[innard.ItemID]; x = innard.X + r.X; y = innard.Y + r.Y; diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastGland.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastGland.cs index a5817fbfc..ab81044db 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastGland.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastGland.cs @@ -6,9 +6,11 @@ namespace Server.Items; public partial class PlagueBeastGland : Item { [Constructible] - public PlagueBeastGland() : base(0x1CEF) => Hue = 0x6; - - public override double DefaultWeight => 1.0; + public PlagueBeastGland() : base(0x1CEF) + { + Weight = 1.0; + Hue = 0x6; + } public override int LabelNumber => 1153759; // a healthy gland } diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastInnard.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastInnard.cs index e40497416..bead7bdd0 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastInnard.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastInnard.cs @@ -10,10 +10,9 @@ public partial class PlagueBeastInnard : Item, IScissorable, ICarvable { Hue = hue; Movable = false; + Weight = 1.0; } - public override double DefaultWeight => 1.0; - public PlagueBeastLord Owner => RootParent as PlagueBeastLord; public override string DefaultName => "plague beast innards"; diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs index 4865f804b..4149b6131 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs @@ -15,11 +15,10 @@ public partial class PlagueBeastMutationCore : Item, IScissorable public PlagueBeastMutationCore() : base(0x1CF0) { _cut = true; + Weight = 1.0; Hue = 0x480; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1153760; // a plague beast mutation core public virtual bool Scissor(Mobile from, Scissors scissors) diff --git a/Projects/UOContent/Items/Special/RewardCake.cs b/Projects/UOContent/Items/Special/RewardCake.cs index 94dbe81a5..88d7f3f95 100644 --- a/Projects/UOContent/Items/Special/RewardCake.cs +++ b/Projects/UOContent/Items/Special/RewardCake.cs @@ -9,6 +9,7 @@ public partial class RewardCake : Item public RewardCake() : base(0x9e9) { Stackable = false; + Weight = 1.0; Hue = Utility.RandomList( 0x135, 0xcd, @@ -30,8 +31,6 @@ public partial class RewardCake : Item LootType = LootType.Blessed; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1049786; // Happy Birthday! ... public override bool DisplayLootType => false; diff --git a/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs b/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs index 9441fd5c7..a57c2d282 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs @@ -5,7 +5,6 @@ using Server.ContextMenus; using Server.Engines.Quests; using Server.Regions; using Server.Spells; -using Server.Mobiles; using Server.Targeting; namespace Server.Items; @@ -28,49 +27,58 @@ public partial class BagOfSending : Item, TranslocationItem [Constructible] public BagOfSending(BagOfSendingHue hue) : base(0xE76) { + Weight = 2.0; + BagOfSendingHue = hue; _charges = Utility.RandomMinMax(3, 9); } - public override double DefaultWeight => 2.0; - public override int LabelNumber => 1054104; // a bag of sending - [SerializableField(0, fieldChanged: nameof(OnBagOfSendingHueChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private BagOfSendingHue _bagOfSendingHue; - - private void OnBagOfSendingHueChanged(BagOfSendingHue oldValue, BagOfSendingHue newValue) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public BagOfSendingHue BagOfSendingHue { - Hue = newValue switch + get => _bagOfSendingHue; + set { - BagOfSendingHue.Yellow => 0x8A5, - BagOfSendingHue.Blue => 0x8AD, - BagOfSendingHue.Red => 0x89B, - _ => Hue - }; + _bagOfSendingHue = value; + + Hue = value switch + { + BagOfSendingHue.Yellow => 0x8A5, + BagOfSendingHue.Blue => 0x8AD, + BagOfSendingHue.Red => 0x89B, + _ => Hue + }; + this.MarkDirty(); + } } - [SerializableField(1, allowFieldChange: nameof(AllowChargesChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _charges; - - private bool AllowChargesChange(ref int value) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public int Charges { - value = Math.Clamp(value, 0, MaxCharges); - return true; + get => _charges; + set + { + _charges = Math.Clamp(value, 0, MaxCharges); + InvalidateProperties(); + this.MarkDirty(); + } } - [SerializableField(2, allowFieldChange: nameof(AllowRechargesChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _recharges; - - private bool AllowRechargesChange(ref int value) + [SerializableProperty(2)] + [CommandProperty(AccessLevel.GameMaster)] + public int Recharges { - value = Math.Clamp(value, 0, MaxRecharges); - return true; + get => _recharges; + set + { + _recharges = Math.Clamp(value, 0, MaxRecharges); + InvalidateProperties(); + this.MarkDirty(); + } } [CommandProperty(AccessLevel.GameMaster)] @@ -225,35 +233,16 @@ public partial class BagOfSending : Item, TranslocationItem { from.SendLocalizedMessage(1079932); // You don't have enough charges to send that much weight } + else if (!from.BankBox.TryDropItem(from, item, false)) + { + _bag.SendLocalizedMessageTo(from, 1054110, 0x59); // Your bank box is full. + } else { - bool sent; + _bag.Charges -= Core.ML ? reqCharges : 1; - if (item is Gold or BankCheck) - { - var amount = (item as Gold)?.Amount ?? ((BankCheck)item).Worth; - sent = Banker.Deposit(from, amount); - if (sent) - { - item.Delete(); - } - } - else - { - sent = from.BankBox.TryDropItem(from, item, false); - } - - if (sent) - { - _bag.Charges -= Core.ML ? reqCharges : 1; - - // The item was placed in your bank box. - _bag.SendLocalizedMessageTo(from, 1054150, 0x59); - } - else - { - _bag.SendLocalizedMessageTo(from, 1054110, 0x59); // Your bank box is full. - } + // The item was placed in your bank box. + _bag.SendLocalizedMessageTo(from, 1054150, 0x59); } } } diff --git a/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs b/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs index 070a802b1..a9db9ec33 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs @@ -21,34 +21,37 @@ public partial class BallOfSummoning : Item, TranslocationItem [Constructible] public BallOfSummoning() : base(0xE2E) { + Weight = 10.0; Light = LightType.Circle150; _charges = Utility.RandomMinMax(3, 9); _petName = null; } - public override double DefaultWeight => 10.0; - - [SerializableField(0, allowFieldChange: nameof(AllowRechargesChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _recharges; - - private bool AllowRechargesChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int Recharges { - value = Math.Clamp(value, 0, MaxRecharges); - return true; + get => _recharges; + set + { + _recharges = Math.Clamp(value, 0, MaxRecharges); + InvalidateProperties(); + this.MarkDirty(); + } } - [SerializableField(1, allowFieldChange: nameof(AllowChargesChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _charges; - - private bool AllowChargesChange(ref int value) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public int Charges { - value = Math.Clamp(value, 0, MaxCharges); - return true; + get => _charges; + set + { + _charges = Math.Clamp(value, 0, MaxCharges); + InvalidateProperties(); + this.MarkDirty(); + } } [SerializableProperty(2)] diff --git a/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs b/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs index 1d508c4ac..8a8ce5d32 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs @@ -23,30 +23,36 @@ public partial class BraceletOfBinding : BaseBracelet, TranslocationItem private TransportTimer _timer; [Constructible] - public BraceletOfBinding() : base(0x1086) => Hue = 0x489; - - public override double DefaultWeight => 1.0; - - [SerializableField(0, allowFieldChange: nameof(AllowRechargesChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _recharges; - - private bool AllowRechargesChange(ref int value) + public BraceletOfBinding() : base(0x1086) { - value = Math.Clamp(value, 0, MaxRecharges); - return true; + Hue = 0x489; + Weight = 1.0; } - [SerializableField(1, allowFieldChange: nameof(AllowChargesChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _charges; - - private bool AllowChargesChange(ref int value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public int Recharges { - value = Math.Clamp(value, 0, MaxCharges); - return true; + get => _recharges; + set + { + _recharges = Math.Clamp(value, 0, MaxRecharges); + InvalidateProperties(); + this.MarkDirty(); + } + } + + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public int Charges + { + get => _charges; + set + { + _charges = Math.Clamp(value, 0, MaxCharges); + InvalidateProperties(); + this.MarkDirty(); + } } [SerializableProperty(3)] @@ -222,7 +228,7 @@ public partial class BraceletOfBinding : BaseBracelet, TranslocationItem return false; } - if (from.Murderer && boundRoot.Map != Map.Felucca) + if (from.Kills >= 5 && boundRoot.Map != Map.Felucca) { from.SendLocalizedMessage(1019004); // You are not allowed to travel there. return false; diff --git a/Projects/UOContent/Items/Special/Solen Items/PowderOfTranslocation.cs b/Projects/UOContent/Items/Special/Solen Items/PowderOfTranslocation.cs index 1a4e86786..47618e53d 100644 --- a/Projects/UOContent/Items/Special/Solen Items/PowderOfTranslocation.cs +++ b/Projects/UOContent/Items/Special/Solen Items/PowderOfTranslocation.cs @@ -19,11 +19,10 @@ public partial class PowderOfTranslocation : Item public PowderOfTranslocation(int amount = 1) : base(0x26B8) { Stackable = true; + Weight = 0.1; Amount = amount; } - public override double DefaultWeight => 0.1; - public override void OnDoubleClick(Mobile from) { if (from.InRange(GetWorldLocation(), 2)) @@ -88,11 +87,11 @@ public partial class PowderOfTranslocation : Item // The ~1_translocationItem~ glows with green energy and absorbs magical power from the powder. if (_transItemName.Number > 0) { - item.SendLocalizedMessageTo(from, 1054139, 0x43, $"#{_transItemName.Number}"); + item.SendLocalizedMessageTo(from, 1054139, $"#{_transItemName.Number}", 0x43); } else if (_transItemName.String != null) { - item.SendLocalizedMessageTo(from, 1054139, 0x43, _transItemName.String); + item.SendLocalizedMessageTo(from, 1054139, _transItemName.String, 0x43); } } } diff --git a/Projects/UOContent/Items/Special/Solen Items/ZoogiFungus.cs b/Projects/UOContent/Items/Special/Solen Items/ZoogiFungus.cs index 33465c040..d01c22198 100644 --- a/Projects/UOContent/Items/Special/Solen Items/ZoogiFungus.cs +++ b/Projects/UOContent/Items/Special/Solen Items/ZoogiFungus.cs @@ -9,11 +9,10 @@ public partial class ZoogiFungus : Item, ICommodity public ZoogiFungus(int amount = 1) : base(0x26B7) { Stackable = true; + Weight = 0.1; Amount = amount; } - public override double DefaultWeight => 0.1; - int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => Core.ML; } diff --git a/Projects/UOContent/Items/Special/SoulStone.cs b/Projects/UOContent/Items/Special/SoulStone.cs index d26f6792c..8cb6aa19c 100644 --- a/Projects/UOContent/Items/Special/SoulStone.cs +++ b/Projects/UOContent/Items/Special/SoulStone.cs @@ -83,14 +83,20 @@ public partial class SoulStone : Item, ISecurable } } - [SerializableField(6, fieldChanged: nameof(OnSkillValueChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private double _skillValue; - - private void OnSkillValueChanged(double oldValue, double newValue) + [SerializableProperty(6)] + [CommandProperty(AccessLevel.GameMaster)] + public double SkillValue { - ItemID = IsEmpty ? _inactiveItemID : _activeItemID; + get => _skillValue; + set + { + _skillValue = value; + + ItemID = IsEmpty ? _inactiveItemID : _activeItemID; + + InvalidateProperties(); + this.MarkDirty(); + } } [CommandProperty(AccessLevel.GameMaster)] @@ -230,11 +236,11 @@ public partial class SoulStone : Item, ISecurable if (IsEmpty) { - SelectSkillGump.DisplayTo(from, this); + gumps.Send(new SelectSkillGump(this, from)); } else { - ConfirmTransferGump.DisplayTo(from, this); + gumps.Send(new ConfirmTransferGump(this, from)); } } @@ -251,50 +257,32 @@ public partial class SoulStone : Item, ISecurable _skillValue = reader.ReadDouble(); } - private class SelectSkillGump : DynamicGump + private class SelectSkillGump : Gump { private readonly SoulStone _stone; - private readonly Mobile _from; - public override bool Singleton => true; - - private SelectSkillGump(SoulStone stone, Mobile from) : base(50, 50) + public SelectSkillGump(SoulStone stone, Mobile from) : base(50, 50) { _stone = stone; - _from = from; - } - public static void DisplayTo(Mobile from, SoulStone stone) - { - if (from?.NetState == null || stone == null || stone.Deleted || !stone.IsEmpty) + AddPage(0); + + AddBackground(0, 0, 520, 440, 0x13BE); + + AddImageTiled(10, 10, 500, 20, 0xA40); + AddImageTiled(10, 40, 500, 360, 0xA40); + AddImageTiled(10, 410, 500, 20, 0xA40); + + AddAlphaRegion(10, 10, 500, 420); + + AddHtmlLocalized(10, 12, 500, 20, 1061087, 0x7FFF); // Which skill do you wish to transfer to the Soulstone? + + AddButton(10, 410, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 412, 450, 20, 1060051, 0x7FFF); // CANCEL + + for (int i = 0, n = 0; i < from.Skills.Length; i++) { - return; - } - - from.SendGump(new SelectSkillGump(stone, from)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 520, 440, 0x13BE); - - builder.AddImageTiled(10, 10, 500, 20, 0xA40); - builder.AddImageTiled(10, 40, 500, 360, 0xA40); - builder.AddImageTiled(10, 410, 500, 20, 0xA40); - - builder.AddAlphaRegion(10, 10, 500, 420); - - // Which skill do you wish to transfer to the Soulstone? - builder.AddHtmlLocalized(10, 12, 500, 20, 1061087, 0x7FFF); - - builder.AddButton(10, 410, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(45, 412, 450, 20, 1060051, 0x7FFF); // CANCEL - - for (int i = 0, n = 0; i < _from.Skills.Length; i++) - { - var skill = _from.Skills[i]; + var skill = from.Skills[i]; if (skill.Base > 0.0) { @@ -306,24 +294,24 @@ public partial class SoulStone : Item, ISecurable if (page > 0) { - builder.AddButton(260, 380, 0xFA5, 0xFA6, 0, GumpButtonType.Page, page + 1); - builder.AddHtmlLocalized(305, 382, 200, 20, 1011066, 0x7FFF); // Next page + AddButton(260, 380, 0xFA5, 0xFA6, 0, GumpButtonType.Page, page + 1); + AddHtmlLocalized(305, 382, 200, 20, 1011066, 0x7FFF); // Next page } - builder.AddPage(page + 1); + AddPage(page + 1); if (page > 0) { - builder.AddButton(10, 380, 0xFAE, 0xFAF, 0, GumpButtonType.Page, page); - builder.AddHtmlLocalized(55, 382, 200, 20, 1011067, 0x7FFF); // Previous page + AddButton(10, 380, 0xFAE, 0xFAF, 0, GumpButtonType.Page, page); + AddHtmlLocalized(55, 382, 200, 20, 1011067, 0x7FFF); // Previous page } } var x = p % 2 == 0 ? 10 : 260; var y = p / 2 * 20 + 40; - builder.AddButton(x, y, 0xFA5, 0xFA6, i + 1); - builder.AddHtmlLocalized(x + 45, y + 2, 200, 20, AosSkillBonuses.GetLabel(skill.SkillName), 0x7FFF); + AddButton(x, y, 0xFA5, 0xFA6, i + 1); + AddHtmlLocalized(x + 45, y + 2, 200, 20, AosSkillBonuses.GetLabel(skill.SkillName), 0x7FFF); n++; } @@ -346,52 +334,39 @@ public partial class SoulStone : Item, ISecurable } var skill = from.Skills[iSkill]; - if (skill.Base <= 0.0 || !_stone.CheckUse(from)) + if (skill.Base <= 0.0) { return; } - ConfirmSkillGump.DisplayTo(from, _stone, skill); + if (!_stone.CheckUse(from)) + { + return; + } + + from.SendGump(new ConfirmSkillGump(_stone, skill)); } } - private class ConfirmSkillGump : DynamicGump + private class ConfirmSkillGump : Gump { private readonly Skill _skill; private readonly SoulStone _stone; - public override bool Singleton => true; - - private ConfirmSkillGump(SoulStone stone, Skill skill) : base(50, 50) + public ConfirmSkillGump(SoulStone stone, Skill skill) : base(50, 50) { _stone = stone; _skill = skill; - } - public static void DisplayTo(Mobile from, SoulStone stone, Skill skill) - { - if (from?.NetState == null || stone == null || stone.Deleted || !stone.IsEmpty || skill == null) - { - return; - } + AddBackground(0, 0, 520, 440, 0x13BE); - from.SendGump(new ConfirmSkillGump(stone, skill)); - } + AddImageTiled(10, 10, 500, 20, 0xA40); + AddImageTiled(10, 40, 500, 360, 0xA40); + AddImageTiled(10, 410, 500, 20, 0xA40); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddAlphaRegion(10, 10, 500, 420); - builder.AddBackground(0, 0, 520, 440, 0x13BE); - - builder.AddImageTiled(10, 10, 500, 20, 0xA40); - builder.AddImageTiled(10, 40, 500, 360, 0xA40); - builder.AddImageTiled(10, 410, 500, 20, 0xA40); - - builder.AddAlphaRegion(10, 10, 500, 420); - - //
Confirm Soulstone Transfer
- builder.AddHtmlLocalized(10, 12, 500, 20, 1070709, 0x7FFF); + AddHtmlLocalized(10, 12, 500, 20, 1070709, 0x7FFF); //
Confirm Soulstone Transfer
/*
Soulstone

* You are using a Soulstone. This powerful artifact allows you to remove skill points @@ -408,30 +383,30 @@ public partial class SoulStone : Item, ISecurable * This is an Account Bound Soulstone. Skill pointsstored inside can be retrieved by any * character on the same account as the character who placed them into the stone. */ - builder.AddHtmlLocalized(10, 42, 500, 110, 1061067, 0x7FFF, false, true); + AddHtmlLocalized(10, 42, 500, 110, 1061067, 0x7FFF, false, true); - builder.AddHtmlLocalized(10, 200, 390, 20, 1062297, 0x7FFF); // Skill Chosen: - builder.AddHtmlLocalized(210, 200, 390, 20, AosSkillBonuses.GetLabel(_skill.SkillName), 0x7FFF); + AddHtmlLocalized(10, 200, 390, 20, 1062297, 0x7FFF); // Skill Chosen: + AddHtmlLocalized(210, 200, 390, 20, AosSkillBonuses.GetLabel(skill.SkillName), 0x7FFF); - builder.AddHtmlLocalized(10, 220, 390, 20, 1062298, 0x7FFF); // Current Value: - builder.AddLabel(210, 220, 0x481, $"{_skill.Base:F1}"); + AddHtmlLocalized(10, 220, 390, 20, 1062298, 0x7FFF); // Current Value: + AddLabel(210, 220, 0x481, skill.Base.ToString("F1")); - builder.AddHtmlLocalized(10, 240, 390, 20, 1062299, 0x7FFF); // Current Cap: - builder.AddLabel(210, 240, 0x481, $"{_skill.Cap:F1}"); + AddHtmlLocalized(10, 240, 390, 20, 1062299, 0x7FFF); // Current Cap: + AddLabel(210, 240, 0x481, skill.Cap.ToString("F1")); - builder.AddHtmlLocalized(10, 260, 390, 20, 1062300, 0x7FFF); // New Value: - builder.AddLabel(210, 260, 0x481, "0.0"); + AddHtmlLocalized(10, 260, 390, 20, 1062300, 0x7FFF); // New Value: + AddLabel(210, 260, 0x481, "0.0"); - builder.AddButton(10, 360, 0xFA5, 0xFA6, 2); + AddButton(10, 360, 0xFA5, 0xFA6, 2); // Activate the stone. I am ready to transfer the skill points to it. - builder.AddHtmlLocalized(45, 362, 450, 20, 1070720, 0x7FFF); + AddHtmlLocalized(45, 362, 450, 20, 1070720, 0x7FFF); - builder.AddButton(10, 380, 0xFA5, 0xFA6, 1); - builder.AddHtmlLocalized(45, 382, 450, 20, 1062279, 0x7FFF); // No, let me make another selection. + AddButton(10, 380, 0xFA5, 0xFA6, 1); + AddHtmlLocalized(45, 382, 450, 20, 1062279, 0x7FFF); // No, let me make another selection. - builder.AddButton(10, 410, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(45, 412, 450, 20, 1060051, 0x7FFF); // CANCEL + AddButton(10, 410, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 412, 450, 20, 1060051, 0x7FFF); // CANCEL } public override void OnResponse(NetState sender, in RelayInfo info) @@ -450,7 +425,7 @@ public partial class SoulStone : Item, ISecurable if (info.ButtonID == 1) // Is asking for another selection { - SelectSkillGump.DisplayTo(from, _stone); + from.SendGump(new SelectSkillGump(_stone, from)); return; } @@ -470,7 +445,7 @@ public partial class SoulStone : Item, ISecurable * the selected skill at this time, click "Cancel". */ - ErrorGump.DisplayTo(from, _stone, 1070710, 1070711); + from.SendGump(new ErrorGump(_stone, 1070710, 1070711)); return; } @@ -517,43 +492,23 @@ public partial class SoulStone : Item, ISecurable } } - private class ConfirmTransferGump : DynamicGump + private class ConfirmTransferGump : Gump { private readonly SoulStone _stone; - private readonly Mobile _from; - public override bool Singleton => true; - - private ConfirmTransferGump(SoulStone stone, Mobile from) : base(50, 50) + public ConfirmTransferGump(SoulStone stone, Mobile from) : base(50, 50) { _stone = stone; - _from = from; - } - public static void DisplayTo(Mobile from, SoulStone stone) - { - if (from?.NetState == null || stone == null || stone.Deleted || stone.IsEmpty) - { - return; - } + AddBackground(0, 0, 520, 440, 0x13BE); - from.SendGump(new ConfirmTransferGump(stone, from)); - } + AddImageTiled(10, 10, 500, 20, 0xA40); + AddImageTiled(10, 40, 500, 360, 0xA40); + AddImageTiled(10, 410, 500, 20, 0xA40); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddAlphaRegion(10, 10, 500, 420); - builder.AddBackground(0, 0, 520, 440, 0x13BE); - - builder.AddImageTiled(10, 10, 500, 20, 0xA40); - builder.AddImageTiled(10, 40, 500, 360, 0xA40); - builder.AddImageTiled(10, 410, 500, 20, 0xA40); - - builder.AddAlphaRegion(10, 10, 500, 420); - - //
Confirm Soulstone Transfer
- builder.AddHtmlLocalized(10, 12, 500, 20, 1070709, 0x7FFF); + AddHtmlLocalized(10, 12, 500, 20, 1070709, 0x7FFF); //
Confirm Soulstone Transfer
/*
Soulstone

* You are using a Soulstone. This powerful artifact allows you to remove skill points @@ -570,34 +525,34 @@ public partial class SoulStone : Item, ISecurable * This is an Account Bound Soulstone. Skill pointsstored inside can be retrieved by any * character on the same account as the character who placed them into the stone. */ - builder.AddHtmlLocalized(10, 42, 500, 110, 1061067, 0x7FFF, false, true); + AddHtmlLocalized(10, 42, 500, 110, 1061067, 0x7FFF, false, true); - builder.AddHtmlLocalized(10, 200, 390, 20, 1070718, 0x7FFF); // Skill Stored: - builder.AddHtmlLocalized(210, 200, 390, 20, AosSkillBonuses.GetLabel(_stone.Skill), 0x7FFF); + AddHtmlLocalized(10, 200, 390, 20, 1070718, 0x7FFF); // Skill Stored: + AddHtmlLocalized(210, 200, 390, 20, AosSkillBonuses.GetLabel(stone.Skill), 0x7FFF); - var fromSkill = _from.Skills[_stone.Skill]; + var fromSkill = from.Skills[stone.Skill]; - builder.AddHtmlLocalized(10, 220, 390, 20, 1062298, 0x7FFF); // Current Value: - builder.AddLabel(210, 220, 0x481, $"{fromSkill.Base:F1}"); + AddHtmlLocalized(10, 220, 390, 20, 1062298, 0x7FFF); // Current Value: + AddLabel(210, 220, 0x481, fromSkill.Base.ToString("F1")); - builder.AddHtmlLocalized(10, 240, 390, 20, 1062299, 0x7FFF); // Current Cap: - builder.AddLabel(210, 240, 0x481, $"{fromSkill.Cap:F1}"); + AddHtmlLocalized(10, 240, 390, 20, 1062299, 0x7FFF); // Current Cap: + AddLabel(210, 240, 0x481, fromSkill.Cap.ToString("F1")); - builder.AddHtmlLocalized(10, 260, 390, 20, 1062300, 0x7FFF); // New Value: - builder.AddLabel(210, 260, 0x481, $"{_stone.SkillValue:F1}"); + AddHtmlLocalized(10, 260, 390, 20, 1062300, 0x7FFF); // New Value: + AddLabel(210, 260, 0x481, stone.SkillValue.ToString("F1")); - builder.AddButton(10, 360, 0xFA5, 0xFA6, 2); + AddButton(10, 360, 0xFA5, 0xFA6, 2); // Activate the stone. I am ready to retrieve the skill points from it. - builder.AddHtmlLocalized(45, 362, 450, 20, 1070719, 0x7FFF); + AddHtmlLocalized(45, 362, 450, 20, 1070719, 0x7FFF); - builder.AddButton(10, 380, 0xFA5, 0xFA6, 1); + AddButton(10, 380, 0xFA5, 0xFA6, 1); // Remove all skill points from this stone and DO NOT absorb them. - builder.AddHtmlLocalized(45, 382, 450, 20, 1070723, 0x7FFF); + AddHtmlLocalized(45, 382, 450, 20, 1070723, 0x7FFF); - builder.AddButton(10, 410, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(45, 412, 450, 20, 1060051, 0x7FFF); // CANCEL + AddButton(10, 410, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 412, 450, 20, 1060051, 0x7FFF); // CANCEL } public override void OnResponse(NetState sender, in RelayInfo info) @@ -616,7 +571,7 @@ public partial class SoulStone : Item, ISecurable if (info.ButtonID == 1) // Remove skill points { - ConfirmRemovalGump.DisplayTo(from, _stone); + from.SendGump(new ConfirmRemovalGump(_stone)); return; } @@ -667,7 +622,7 @@ public partial class SoulStone : Item, ISecurable * time, click "Cancel". */ - ErrorGump.DisplayTo(from, _stone, 1070717, 1070716); + from.SendGump(new ErrorGump(_stone, 1070717, 1070716)); return; } @@ -681,7 +636,7 @@ public partial class SoulStone : Item, ISecurable * skill cap. You cannot currently retrieve the skill points stored in this stone. */ - ErrorGump.DisplayTo(from, _stone, 1070717, 1070715); + from.SendGump(new ErrorGump(_stone, 1070717, 1070715)); return; } @@ -695,7 +650,7 @@ public partial class SoulStone : Item, ISecurable // Wrong message?! - ErrorGump.DisplayTo(from, _stone, 1070717, 1070802); + from.SendGump(new ErrorGump(_stone, 1070717, 1070802)); return; } @@ -707,7 +662,7 @@ public partial class SoulStone : Item, ISecurable // Wrong message?! - ErrorGump.DisplayTo(from, _stone, 1070717, 1078115); + from.SendGump(new ErrorGump(_stone, 1070717, 1078115)); return; } @@ -781,41 +736,23 @@ public partial class SoulStone : Item, ISecurable } } - private class ConfirmRemovalGump : StaticGump + private class ConfirmRemovalGump : Gump { private readonly SoulStone _stone; - public override bool Singleton => true; - - private ConfirmRemovalGump(SoulStone stone) : base(50, 50) + public ConfirmRemovalGump(SoulStone stone) : base(50, 50) { _stone = stone; - } - public static void DisplayTo(Mobile from, SoulStone stone) - { - if (from?.NetState == null || stone == null || stone.Deleted || stone.IsEmpty) - { - return; - } + AddBackground(0, 0, 520, 440, 0x13BE); - from.SendGump(new ConfirmRemovalGump(stone)); - } + AddImageTiled(10, 10, 500, 20, 0xA40); + AddImageTiled(10, 40, 500, 360, 0xA40); + AddImageTiled(10, 410, 500, 20, 0xA40); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddAlphaRegion(10, 10, 500, 420); - builder.AddBackground(0, 0, 520, 440, 0x13BE); - - builder.AddImageTiled(10, 10, 500, 20, 0xA40); - builder.AddImageTiled(10, 40, 500, 360, 0xA40); - builder.AddImageTiled(10, 410, 500, 20, 0xA40); - - builder.AddAlphaRegion(10, 10, 500, 420); - - //
Confirm Soulstone Skill Removal
- builder.AddHtmlLocalized(10, 12, 500, 20, 1070725, 0x7FFF); + AddHtmlLocalized(10, 12, 500, 20, 1070725, 0x7FFF); //
Confirm Soulstone Skill Removal
/* WARNING!

* @@ -824,13 +761,13 @@ public partial class SoulStone : Item, ISecurable * * Are you sure you wish to do this? If not, press the Cancel button. */ - builder.AddHtmlLocalized(10, 42, 500, 110, 1070724, 0x7FFF, false, true); + AddHtmlLocalized(10, 42, 500, 110, 1070724, 0x7FFF, false, true); - builder.AddButton(10, 380, 0xFA5, 0xFA6, 1); - builder.AddHtmlLocalized(45, 382, 450, 20, 1052072, 0x7FFF); // Continue + AddButton(10, 380, 0xFA5, 0xFA6, 1); + AddHtmlLocalized(45, 382, 450, 20, 1052072, 0x7FFF); // Continue - builder.AddButton(10, 410, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(45, 412, 450, 20, 1060051, 0x7FFF); // CANCEL + AddButton(10, 410, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 412, 450, 20, 1060051, 0x7FFF); // CANCEL } public override void OnResponse(NetState sender, in RelayInfo info) @@ -852,52 +789,31 @@ public partial class SoulStone : Item, ISecurable } } - private class ErrorGump : DynamicGump + private class ErrorGump : Gump { private readonly SoulStone _stone; - private readonly int _title; - private readonly int _message; - public override bool Singleton => true; - - private ErrorGump(SoulStone stone, int title, int message) : base(50, 50) + public ErrorGump(SoulStone stone, int title, int message) : base(50, 50) { _stone = stone; - _title = title; - _message = message; - } - public static void DisplayTo(Mobile from, SoulStone stone, int title, int message) - { - if (from?.NetState == null || stone == null || stone.Deleted) - { - return; - } + AddBackground(0, 0, 520, 440, 0x13BE); - from.SendGump(new ErrorGump(stone, title, message)); - } + AddImageTiled(10, 10, 500, 20, 0xA40); + AddImageTiled(10, 40, 500, 360, 0xA40); + AddImageTiled(10, 410, 500, 20, 0xA40); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddAlphaRegion(10, 10, 500, 420); - builder.AddBackground(0, 0, 520, 440, 0x13BE); + AddHtmlLocalized(10, 12, 500, 20, title, 0x7FFF); - builder.AddImageTiled(10, 10, 500, 20, 0xA40); - builder.AddImageTiled(10, 40, 500, 360, 0xA40); - builder.AddImageTiled(10, 410, 500, 20, 0xA40); + AddHtmlLocalized(10, 42, 500, 110, message, 0x7FFF, false, true); - builder.AddAlphaRegion(10, 10, 500, 420); + AddButton(10, 380, 0xFA5, 0xFA6, 1); + AddHtmlLocalized(45, 382, 450, 20, 1052072, 0x7FFF); // Continue - builder.AddHtmlLocalized(10, 12, 500, 20, _title, 0x7FFF); - - builder.AddHtmlLocalized(10, 42, 500, 110, _message, 0x7FFF, false, true); - - builder.AddButton(10, 380, 0xFA5, 0xFA6, 1); - builder.AddHtmlLocalized(45, 382, 450, 20, 1052072, 0x7FFF); // Continue - - builder.AddButton(10, 410, 0xFB1, 0xFB2, 0); - builder.AddHtmlLocalized(45, 412, 450, 20, 1060051, 0x7FFF); // CANCEL + AddButton(10, 410, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(45, 412, 450, 20, 1060051, 0x7FFF); // CANCEL } public override void OnResponse(NetState sender, in RelayInfo info) @@ -916,11 +832,11 @@ public partial class SoulStone : Item, ISecurable if (_stone.IsEmpty) { - SelectSkillGump.DisplayTo(from, _stone); + from.SendGump(new SelectSkillGump(_stone, from)); } else { - ConfirmTransferGump.DisplayTo(from, _stone); + from.SendGump(new ConfirmTransferGump(_stone, from)); } } } diff --git a/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs b/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs index 9b5a288b7..267f8e971 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs @@ -110,7 +110,7 @@ public partial class PowerScroll : SpecialScroll } } - public override string DefaultTitle => Html.Color($"Power Scroll ({Math.Floor(Value * 10) / 10:0.#} Skill):", 0xFFFFFF); + public override string DefaultTitle => $"Power Scroll ({Math.Floor(Value * 10) / 10:0.#} Skill):".Color(0xFFFFFF); public static SkillName[] Skills { @@ -136,7 +136,7 @@ public partial class PowerScroll : SpecialScroll _skills = new SkillName[totalSkills]; Array.Copy(_preAosSkills, 0, _skills, 0, _preAosSkills.Length); - var pos = 0; + int pos = 0; if (Core.AOS) { diff --git a/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs b/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs index 6f74356e9..7ad9b72fd 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs @@ -25,7 +25,7 @@ public partial class ScrollofTranscendence : SpecialScroll public override int Message => 1094933; public override string DefaultTitle => - Html.Color( $"Scroll of Transcendence ({Math.Floor(Value * 10) / 10:0.#} Skill):", 0xFFFFFF); + $"Scroll of Transcendence ({Math.Floor(Value * 10) / 10:0.#} Skill):".Color(0xFFFFFF); public static ScrollofTranscendence CreateRandom(int min, int max) => new(SkillsInfo.RandomSkill(), Utility.RandomMinMax(min, max) / 10.0); diff --git a/Projects/UOContent/Items/Special/Special Scrolls/SpecialScroll.cs b/Projects/UOContent/Items/Special/Special Scrolls/SpecialScroll.cs index e72786d90..3bb4ca068 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/SpecialScroll.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/SpecialScroll.cs @@ -20,13 +20,12 @@ public abstract partial class SpecialScroll : Item public SpecialScroll(SkillName skill, double value) : base(0x14F0) { LootType = LootType.Cursed; + Weight = 1.0; _skill = skill; _value = value; } - public override double DefaultWeight => 1.0; - public abstract int Message { get; } public virtual int Title => 0; public abstract string DefaultTitle { get; } @@ -73,60 +72,52 @@ public abstract partial class SpecialScroll : Item return; } - SpecialScrollGump.DisplayTo(from, this); + from.SendGump(new InternalGump(from, this)); } - public class SpecialScrollGump : DynamicGump + public class InternalGump : Gump { + private readonly Mobile _mobile; private readonly SpecialScroll _scroll; public override bool Singleton => true; - private SpecialScrollGump(SpecialScroll scroll) : base(25, 50) => _scroll = scroll; - - public static void DisplayTo(Mobile from, SpecialScroll scroll) + public InternalGump(Mobile mobile, SpecialScroll scroll) : base(25, 50) { - if (from?.NetState == null || scroll?.Deleted != false) - { - return; - } + _mobile = mobile; + _scroll = scroll; - from.SendGump(new SpecialScrollGump(scroll)); - } + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddBackground(25, 10, 420, 200, 5054); - builder.AddBackground(25, 10, 420, 200, 5054); + AddImageTiled(33, 20, 401, 181, 2624); + AddAlphaRegion(33, 20, 401, 181); - builder.AddImageTiled(33, 20, 401, 181, 2624); - builder.AddAlphaRegion(33, 20, 401, 181); + AddHtmlLocalized(40, 48, 387, 100, _scroll.Message, true, true); - builder.AddHtmlLocalized(40, 48, 387, 100, _scroll.Message, true, true); + AddHtmlLocalized(125, 148, 200, 20, 1049478, 0x7FFF); // Do you wish to use this scroll? - builder.AddHtmlLocalized(125, 148, 200, 20, 1049478, 0x7FFF); // Do you wish to use this scroll? + AddButton(100, 172, 4005, 4007, 1); + AddHtmlLocalized(135, 172, 120, 20, 1046362, 0x7FFF); // Yes - builder.AddButton(100, 172, 4005, 4007, 1); - builder.AddHtmlLocalized(135, 172, 120, 20, 1046362, 0x7FFF); // Yes - - builder.AddButton(275, 172, 4005, 4007, 0); - builder.AddHtmlLocalized(310, 172, 120, 20, 1046363, 0x7FFF); // No + AddButton(275, 172, 4005, 4007, 0); + AddHtmlLocalized(310, 172, 120, 20, 1046363, 0x7FFF); // No if (_scroll.Title != 0) { - builder.AddHtmlLocalized(40, 20, 260, 20, _scroll.Title, 0x7FFF); + AddHtmlLocalized(40, 20, 260, 20, _scroll.Title, 0x7FFF); } else { - builder.AddHtml(40, 20, 260, 20, _scroll.DefaultTitle); + AddHtml(40, 20, 260, 20, _scroll.DefaultTitle); } var skillLabel = _scroll.SkillLabel; if (skillLabel > 0) { - builder.AddHtmlLocalized(310, 20, 120, 20, skillLabel, 0x7FFF); + AddHtmlLocalized(310, 20, 120, 20, skillLabel, 0x7FFF); } } @@ -134,7 +125,7 @@ public abstract partial class SpecialScroll : Item { if (info.ButtonID == 1) { - _scroll.Use(state.Mobile); + _scroll.Use(_mobile); } } } diff --git a/Projects/UOContent/Items/Special/Special Scrolls/StatCapScroll.cs b/Projects/UOContent/Items/Special/Special Scrolls/StatCapScroll.cs index 92dcd54de..f4e1a7d71 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/StatCapScroll.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/StatCapScroll.cs @@ -42,7 +42,7 @@ public partial class StatCapScroll : SpecialScroll } public override string DefaultTitle => - Html.Color($"Power Scroll ({((int)Value - 225 >= 0 ? "+" : "")}{(int)Value - 225} Maximum Stats):", 0xFFFFFF); + $"Power Scroll ({((int)Value - 225 >= 0 ? "+" : "")}{(int)Value - 225} Maximum Stats):".Color(0xFFFFFF); public override void AddNameProperty(IPropertyList list) { diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/AnkhOfSacrifice.cs b/Projects/UOContent/Items/Special/Veteran Rewards/AnkhOfSacrifice.cs index 320038491..49950b13c 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/AnkhOfSacrifice.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/AnkhOfSacrifice.cs @@ -23,7 +23,7 @@ public partial class AnkhOfSacrificeComponent : AddonComponent { base.GetContextMenuEntries(from, ref list); - if (Core.UOTD && !Core.AOS && from is PlayerMobile mobile) + if (from is PlayerMobile mobile) { list.Add(new Ankhs.LockKarmaEntry(mobile.KarmaLocked)); } @@ -214,7 +214,7 @@ public partial class AnkhOfSacrificeDeed : BaseAddonDeed, IRewardItem, IRewardOp if (IsChildOf(from.Backpack)) { - RewardOptionGump.DisplayTo(from, this); + from.SendGump(new RewardOptionGump(this)); } else { diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs b/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs index 370ab9855..823ce8604 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs @@ -71,22 +71,23 @@ public partial class Banner : Item, IAddon, IDyable, IRewardItem public override void OnDoubleClick(Mobile from) { - if (!from.InRange(Location, 2)) + if (from.InRange(Location, 2)) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } + var house = BaseHouse.FindHouseAt(this); - var house = BaseHouse.FindHouseAt(this); - - if (house?.IsOwner(from) == true) - { - RewardDemolitionGump.DisplayTo(from, this, 1018318); // Do you wish to re-deed this banner? + if (house?.IsOwner(from) == true) + { + from.SendGump(new RewardDemolitionGump(this, 1018318)); // Do you wish to re-deed this banner? + } + else + { + // You can only re-deed a banner if you placed it or you are the owner of the house. + from.SendLocalizedMessage(1018330); + } } else { - // You can only re-deed a banner if you placed it or you are the owner of the house. - from.SendLocalizedMessage(1018330); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } } @@ -100,9 +101,12 @@ public partial class BannerDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public BannerDeed() : base(0x14F0) => LootType = LootType.Blessed; + public BannerDeed() : base(0x14F0) + { + LootType = LootType.Blessed; + Weight = 1.0; + } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1041007; // a banner deed public override void GetProperties(IPropertyList list) @@ -128,7 +132,7 @@ public partial class BannerDeed : Item, IRewardItem if (house?.IsOwner(from) == true) { - BannerGump.DisplayTo(from, this); + from.SendGump(new InternalGump(this)); } else { @@ -141,7 +145,7 @@ public partial class BannerDeed : Item, IRewardItem } } - private class BannerGump : StaticGump + private class InternalGump : Gump { public const int Start = 0x15AE; public const int End = 0x15F4; @@ -150,46 +154,41 @@ public partial class BannerDeed : Item, IRewardItem public override bool Singleton => true; - private BannerGump(BannerDeed banner) : base(100, 200) => _banner = banner; - - public static void DisplayTo(Mobile from, BannerDeed banner) + public InternalGump(BannerDeed banner) : base(100, 200) { - if (from?.NetState == null || banner?.Deleted != false) - { - return; - } + _banner = banner; - from.SendGump(new BannerGump(banner)); - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(25, 0, 520, 230, 0xA28); + AddBackground(25, 0, 520, 230, 0xA28); // TODO: Use 1152360 -
Choose a banner:
- builder.AddLabel(70, 12, 0x3E3, "Choose a Banner:"); + AddLabel(70, 12, 0x3E3, "Choose a Banner:"); var itemID = Start; for (var i = 1; i <= 4; i++) { - builder.AddPage(i); + AddPage(i); for (var j = 0; j < 8; j++, itemID += 2) { - builder.AddItem(50 + 60 * j, 70, itemID); - builder.AddButton(50 + 60 * j, 50, 0x845, 0x846, itemID); + AddItem(50 + 60 * j, 70, itemID); + AddButton(50 + 60 * j, 50, 0x845, 0x846, itemID); } if (i > 1) { - builder.AddButton(75, 198, 0x8AF, 0x8AF, 0, GumpButtonType.Page, i - 1); + AddButton(75, 198, 0x8AF, 0x8AF, 0, GumpButtonType.Page, i - 1); } if (i < 4) { - builder.AddButton(475, 198, 0x8B0, 0x8B0, 0, GumpButtonType.Page, i + 1); + AddButton(475, 198, 0x8B0, 0x8B0, 0, GumpButtonType.Page, i + 1); } } } @@ -294,52 +293,65 @@ public partial class BannerDeed : Item, IRewardItem } } - private class FacingGump : DynamicGump + private class FacingGump : Gump { - private readonly BannerDeed _banner; - private readonly BaseHouse _house; - private readonly int _itemId; - private readonly Point3D _location; + private readonly BannerDeed m_Banner; + private readonly BaseHouse m_House; + private readonly int m_ItemID; + private readonly Point3D m_Location; public override bool Singleton => true; public FacingGump(BannerDeed banner, int itemID, Point3D location, BaseHouse house) : base(150, 50) { - _banner = banner; - _itemId = itemID; - _location = location; - _house = house; - } + m_Banner = banner; + m_ItemID = itemID; + m_Location = location; + m_House = house; - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoResize(); + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - builder.AddPage(); + AddPage(0); - builder.AddBackground(0, 0, 300, 150, 0xA28); + AddBackground(0, 0, 300, 150, 0xA28); - builder.AddItem(90, 30, _itemId + 1); - builder.AddItem(180, 30, _itemId); + AddItem(90, 30, itemID + 1); + AddItem(180, 30, itemID); - builder.AddButton(50, 35, 0x868, 0x869, (int)Buttons.East); - builder.AddButton(145, 35, 0x868, 0x869, (int)Buttons.South); + AddButton(50, 35, 0x868, 0x869, (int)Buttons.East); + AddButton(145, 35, 0x868, 0x869, (int)Buttons.South); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_banner?.Deleted != false || _house == null) + if (m_Banner?.Deleted != false || m_House == null) { return; } - var banner = new Banner(_itemId + (info.ButtonID == (int)Buttons.East ? 1 : 0)); - _house.Addons.Add(banner); + Banner banner = null; - banner.IsRewardItem = _banner.IsRewardItem; - banner.MoveToWorld(_location, sender.Mobile.Map); + if (info.ButtonID == (int)Buttons.East) + { + banner = new Banner(m_ItemID + 1); + } + else if (info.ButtonID == (int)Buttons.South) + { + banner = new Banner(m_ItemID); + } - _banner.Delete(); + if (banner != null) + { + m_House.Addons.Add(banner); + + banner.IsRewardItem = m_Banner.IsRewardItem; + banner.MoveToWorld(m_Location, sender.Mobile.Map); + + m_Banner.Delete(); + } } private enum Buttons diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs b/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs index fb9a32c84..437a8de63 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs @@ -25,9 +25,11 @@ public partial class RewardBrazier : Item, IRewardItem } [Constructible] - public RewardBrazier(int itemID) : base(itemID) => LootType = LootType.Blessed; - - public override double DefaultWeight => 10.0; + public RewardBrazier(int itemID) : base(itemID) + { + LootType = LootType.Blessed; + Weight = 10.0; + } public override bool ForceShowProperties => ObjectPropertyList.Enabled; @@ -113,9 +115,11 @@ public partial class RewardBrazierDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public RewardBrazierDeed() : base(0x14F0) => LootType = LootType.Blessed; - - public override double DefaultWeight => 1.0; + public RewardBrazierDeed() : base(0x14F0) + { + LootType = LootType.Blessed; + Weight = 1.0; + } public override int LabelNumber => 1080527; // Brazier Deed @@ -132,7 +136,7 @@ public partial class RewardBrazierDeed : Item, IRewardItem return; } - BrazierGump.DisplayTo(from, this); + from.SendGump(new InternalGump(this)); } public override void GetProperties(IPropertyList list) @@ -145,37 +149,32 @@ public partial class RewardBrazierDeed : Item, IRewardItem } } - private class BrazierGump : StaticGump + private class InternalGump : Gump { private readonly RewardBrazierDeed _brazier; public override bool Singleton => true; - private BrazierGump(RewardBrazierDeed brazier) : base(100, 200) => _brazier = brazier; - - public static void DisplayTo(Mobile from, RewardBrazierDeed brazier) + public InternalGump(RewardBrazierDeed brazier) : base(100, 200) { - if (from?.NetState == null || brazier?.Deleted != false) - { - return; - } + _brazier = brazier; - from.SendGump(new BrazierGump(brazier)); - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 200, 200, 2600); + AddPage(0); + AddBackground(0, 0, 200, 200, 2600); - builder.AddPage(1); - builder.AddLabel(45, 15, 0, "Choose a Brazier:"); + AddPage(1); + AddLabel(45, 15, 0, "Choose a Brazier:"); - builder.AddItem(40, 75, 0x19AA); - builder.AddButton(55, 50, 0x845, 0x846, 0x19AA); + AddItem(40, 75, 0x19AA); + AddButton(55, 50, 0x845, 0x846, 0x19AA); - builder.AddItem(100, 75, 0x19BB); - builder.AddButton(115, 50, 0x845, 0x846, 0x19BB); + AddItem(100, 75, 0x19BB); + AddButton(115, 50, 0x845, 0x846, 0x19BB); } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/Cannon.cs b/Projects/UOContent/Items/Special/Veteran Rewards/Cannon.cs index 961506961..9d7c7d691 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/Cannon.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/Cannon.cs @@ -142,7 +142,7 @@ public partial class CannonAddon : BaseAddon if (Validate(keg) > 0) { - CannonGump.DisplayTo(from, this, keg); + from.SendGump(new InternalGump(this, keg)); } else { @@ -198,7 +198,7 @@ public partial class CannonAddon : BaseAddon for (var count = Utility.Random(3); count > 0; count--) { - var location = new Point3D( + Point3D location = new Point3D( target.X + Utility.RandomMinMax(-1, 1), target.Y + Utility.RandomMinMax(-1, 1), target.Z @@ -309,44 +309,34 @@ public partial class CannonAddon : BaseAddon } } - private class CannonGump : DynamicGump + private class InternalGump : Gump { private readonly CannonAddon _cannon; private readonly PotionKeg _keg; - public override bool Singleton => true; - - private CannonGump(CannonAddon cannon, PotionKeg keg) : base(50, 50) + public InternalGump(CannonAddon cannon, PotionKeg keg) : base(50, 50) { _cannon = cannon; _keg = keg; - } - public static void DisplayTo(Mobile from, CannonAddon cannon, PotionKeg keg) - { - if (from?.NetState == null || cannon?.Deleted != false || keg?.Deleted != false) - { - return; - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - from.SendGump(new CannonGump(cannon, keg)); - } + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 291, 133, 0x13BE); - builder.AddImageTiled(5, 5, 280, 100, 0xA40); + AddBackground(0, 0, 291, 133, 0x13BE); + AddImageTiled(5, 5, 280, 100, 0xA40); // You will need a full keg of explosion potions to recharge the cannon. Your keg will provide ~1_CHARGES~ charges. - builder.AddHtmlLocalized(9, 9, 272, 100, 1076196, $"{_cannon.Validate(_keg)}", 0x7FFF); + AddHtmlLocalized(9, 9, 272, 100, 1076196, cannon.Validate(keg).ToString(), 0x7FFF); - builder.AddButton(5, 107, 0xFB1, 0xFB2, (int)Buttons.Cancel); - builder.AddHtmlLocalized(40, 109, 100, 20, 1060051, 0x7FFF); // CANCEL + AddButton(5, 107, 0xFB1, 0xFB2, (int)Buttons.Cancel); + AddHtmlLocalized(40, 109, 100, 20, 1060051, 0x7FFF); // CANCEL - builder.AddButton(160, 107, 0xFB7, 0xFB8, (int)Buttons.Recharge); - builder.AddHtmlLocalized(195, 109, 120, 20, 1076197, 0x7FFF); // Recharge + AddButton(160, 107, 0xFB7, 0xFB8, (int)Buttons.Recharge); + AddHtmlLocalized(195, 109, 120, 20, 1076197, 0x7FFF); // Recharge } public override void OnResponse(NetState state, in RelayInfo info) @@ -430,7 +420,7 @@ public partial class CannonDeed : BaseAddonDeed, IRewardItem, IRewardOption if (IsChildOf(from.Backpack)) { - RewardOptionGump.DisplayTo(from, this); + from.SendGump(new RewardOptionGump(this)); } else { diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs b/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs index ac29c2cca..78292c651 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs @@ -13,9 +13,12 @@ public partial class CommodityDeedBox : BaseContainer, IRewardItem private bool _isRewardItem; [Constructible] - public CommodityDeedBox() : base(0x9AA) => Hue = 0x47; + public CommodityDeedBox() : base(0x9AA) + { + Hue = 0x47; + Weight = 4.0; + } - public override double DefaultWeight => 4.0; public override int LabelNumber => 1080523; // Commodity Deed Box public override int DefaultGumpID => 0x43; diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs b/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs index 2a732dfc3..26838a7ee 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs @@ -55,22 +55,23 @@ public partial class DecorativeShield : Item, IAddon, IRewardItem public override void OnDoubleClick(Mobile from) { - if (!from.InRange(Location, 2)) + if (from.InRange(Location, 2)) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } + var house = BaseHouse.FindHouseAt(this); - var house = BaseHouse.FindHouseAt(this); - - if (house?.IsOwner(from) == true) - { - RewardDemolitionGump.DisplayTo(from, this, 1049783); // Do you wish to re-deed this decoration? + if (house?.IsOwner(from) == true) + { + from.SendGump(new RewardDemolitionGump(this, 1049783)); // Do you wish to re-deed this decoration? + } + else + { + // You can only re-deed this decoration if you are the house owner or originally placed the decoration. + from.SendLocalizedMessage(1049784); + } } else { - // You can only re-deed this decoration if you are the house owner or originally placed the decoration. - from.SendLocalizedMessage(1049784); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } } @@ -84,9 +85,12 @@ public partial class DecorativeShieldDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public DecorativeShieldDeed() : base(0x14F0) => LootType = LootType.Blessed; + public DecorativeShieldDeed() : base(0x14F0) + { + LootType = LootType.Blessed; + Weight = 1.0; + } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1049771; // deed for a decorative shield wall hanging public override void GetProperties(IPropertyList list) @@ -108,7 +112,7 @@ public partial class DecorativeShieldDeed : Item, IRewardItem if (IsChildOf(from.Backpack)) { - DecorativeShieldGump.DisplayTo(from, this); + from.SendGump(new InternalGump(this)); } else { @@ -128,7 +132,7 @@ public partial class DecorativeShieldDeed : Item, IRewardItem }; } - private class DecorativeShieldGump : StaticGump + private class InternalGump : Gump { public const int Start = 0x156C; public const int End = 0x1585; @@ -137,34 +141,29 @@ public partial class DecorativeShieldDeed : Item, IRewardItem public override bool Singleton => true; - private DecorativeShieldGump(DecorativeShieldDeed shield) : base(150, 50) => _shield = shield; - - public static void DisplayTo(Mobile from, DecorativeShieldDeed shield) + public InternalGump(DecorativeShieldDeed shield) : base(150, 50) { - if (from?.NetState == null || shield?.Deleted != false) - { - return; - } + _shield = shield; - from.SendGump(new DecorativeShieldGump(shield)); - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(25, 0, 500, 230, 0xA28); + AddBackground(25, 0, 500, 230, 0xA28); var itemID = Start; for (var i = 1; i <= 2; i++) { - builder.AddPage(i); + AddPage(i); for (var j = 0; j < 9 - i; j++) { - builder.AddItem(40 + j * 60, 70, itemID); - builder.AddButton(60 + j * 60, 50, 0x845, 0x846, itemID); + AddItem(40 + j * 60, 70, itemID); + AddButton(60 + j * 60, 50, 0x845, 0x846, itemID); if (itemID < 0x1582) { @@ -180,12 +179,12 @@ public partial class DecorativeShieldDeed : Item, IRewardItem { case 1: { - builder.AddButton(455, 198, 0x8B0, 0x8B0, 0, GumpButtonType.Page, 2); + AddButton(455, 198, 0x8B0, 0x8B0, 0, GumpButtonType.Page, 2); break; } case 2: { - builder.AddButton(70, 198, 0x8AF, 0x8AF, 0, GumpButtonType.Page, 1); + AddButton(70, 198, 0x8AF, 0x8AF, 0, GumpButtonType.Page, 1); break; } } @@ -268,7 +267,7 @@ public partial class DecorativeShieldDeed : Item, IRewardItem if (north && west) { - DecorativeShieldFacingGump.DisplayTo(from, _shield, _itemID, p3d, house); + from.SendGump(new FacingGump(_shield, _itemID, p3d, house)); } else if (north || west) { @@ -287,7 +286,7 @@ public partial class DecorativeShieldDeed : Item, IRewardItem } } - private class DecorativeShieldFacingGump : DynamicGump + private class FacingGump : Gump { private readonly BaseHouse _house; private readonly int _itemID; @@ -296,34 +295,26 @@ public partial class DecorativeShieldDeed : Item, IRewardItem public override bool Singleton => true; - private DecorativeShieldFacingGump(DecorativeShieldDeed shield, int itemID, Point3D location, BaseHouse house) : base(150, 50) + public FacingGump(DecorativeShieldDeed shield, int itemID, Point3D location, BaseHouse house) : base(150, 50) { _shield = shield; _itemID = itemID; _location = location; _house = house; - } - public static void DisplayTo(Mobile from, DecorativeShieldDeed shield, int itemID, Point3D location, BaseHouse house) - { - if (from?.NetState == null || shield?.Deleted != false || house == null) - { - return; - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - from.SendGump(new DecorativeShieldFacingGump(shield, itemID, location, house)); - } + AddPage(0); + AddBackground(0, 0, 300, 150, 0xA28); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 300, 150, 0xA28); + AddItem(90, 30, GetWestItemID(itemID)); + AddItem(180, 30, itemID); - builder.AddItem(90, 30, GetWestItemID(_itemID)); - builder.AddItem(180, 30, _itemID); - - builder.AddButton(50, 35, 0x867, 0x869, (int)Buttons.East); - builder.AddButton(145, 35, 0x867, 0x869, (int)Buttons.South); + AddButton(50, 35, 0x867, 0x869, (int)Buttons.East); + AddButton(145, 35, 0x867, 0x869, (int)Buttons.South); } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs b/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs index 63d2c19c1..198628300 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs @@ -69,22 +69,23 @@ public partial class FlamingHead : StoneFaceTrapNoDamage, IAddon, IRewardItem public override void OnDoubleClick(Mobile from) { - if (!from.InRange(Location, 2)) + if (from.InRange(Location, 2)) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } + var house = BaseHouse.FindHouseAt(this); - var house = BaseHouse.FindHouseAt(this); - - if (house?.IsOwner(from) == true) - { - RewardDemolitionGump.DisplayTo(from, this, 1018329); // Do you wish to re-deed this skull? + if (house?.IsOwner(from) == true) + { + from.SendGump(new RewardDemolitionGump(this, 1018329)); // Do you wish to re-deed this skull? + } + else + { + // You can only re-deed a skull if you placed it or you are the owner of the house. + from.SendLocalizedMessage(1018328); + } } else { - // You can only re-deed a skull if you placed it or you are the owner of the house. - from.SendLocalizedMessage(1018328); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } } @@ -98,9 +99,12 @@ public partial class FlamingHeadDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public FlamingHeadDeed() : base(0x14F0) => LootType = LootType.Blessed; + public FlamingHeadDeed() : base(0x14F0) + { + LootType = LootType.Blessed; + Weight = 1.0; + } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1041050; // a flaming head deed public override void GetProperties(IPropertyList list) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs b/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs index 83f9c352f..728a35d7d 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs @@ -59,22 +59,23 @@ public partial class HangingSkeleton : Item, IAddon, IRewardItem public override void OnDoubleClick(Mobile from) { - if (!from.InRange(Location, 3)) + if (from.InRange(Location, 3)) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } + var house = BaseHouse.FindHouseAt(this); - var house = BaseHouse.FindHouseAt(this); - - if (house?.IsOwner(from) == true) - { - RewardDemolitionGump.DisplayTo(from, this, 1049783); // Do you wish to re-deed this decoration? + if (house?.IsOwner(from) == true) + { + from.SendGump(new RewardDemolitionGump(this, 1049783)); // Do you wish to re-deed this decoration? + } + else + { + // You can only re-deed this decoration if you are the house owner or originally placed the decoration. + from.SendLocalizedMessage(1049784); + } } else { - // You can only re-deed this decoration if you are the house owner or originally placed the decoration. - from.SendLocalizedMessage(1049784); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } } @@ -88,9 +89,12 @@ public partial class HangingSkeletonDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public HangingSkeletonDeed() : base(0x14F0) => LootType = LootType.Blessed; + public HangingSkeletonDeed() : base(0x14F0) + { + LootType = LootType.Blessed; + Weight = 1.0; + } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1049772; // deed for a hanging skeleton decoration public override void GetProperties(IPropertyList list) @@ -124,7 +128,7 @@ public partial class HangingSkeletonDeed : Item, IRewardItem return; } - HangingSkeletonGump.DisplayTo(from, this); + from.SendGump(new InternalGump(this)); } public static int GetWestItemID(int south) @@ -137,46 +141,41 @@ public partial class HangingSkeletonDeed : Item, IRewardItem }; } - private class HangingSkeletonGump : StaticGump + private class InternalGump : Gump { private readonly HangingSkeletonDeed _deed; public override bool Singleton => true; - private HangingSkeletonGump(HangingSkeletonDeed skeleton) : base(100, 200) => _deed = skeleton; - - public static void DisplayTo(Mobile from, HangingSkeletonDeed deed) + public InternalGump(HangingSkeletonDeed skeleton) : base(100, 200) { - if (from?.NetState == null || deed?.Deleted != false) - { - return; - } + _deed = skeleton; - from.SendGump(new HangingSkeletonGump(deed)); - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(25, 0, 500, 230, 0xA28); + AddBackground(25, 0, 500, 230, 0xA28); - builder.AddPage(1); + AddPage(1); - builder.AddItem(130, 70, 0x1A03); - builder.AddButton(150, 50, 0x845, 0x846, 0x1A03); + AddItem(130, 70, 0x1A03); + AddButton(150, 50, 0x845, 0x846, 0x1A03); - builder.AddItem(190, 70, 0x1A05); - builder.AddButton(210, 50, 0x845, 0x846, 0x1A05); + AddItem(190, 70, 0x1A05); + AddButton(210, 50, 0x845, 0x846, 0x1A05); - builder.AddItem(250, 70, 0x1A09); - builder.AddButton(270, 50, 0x845, 0x846, 0x1A09); + AddItem(250, 70, 0x1A09); + AddButton(270, 50, 0x845, 0x846, 0x1A09); - builder.AddItem(310, 70, 0x1B1E); - builder.AddButton(330, 50, 0x845, 0x846, 0x1B1E); + AddItem(310, 70, 0x1B1E); + AddButton(330, 50, 0x845, 0x846, 0x1B1E); - builder.AddItem(370, 70, 0x1B7F); - builder.AddButton(390, 50, 0x845, 0x846, 0x1B7F); + AddItem(370, 70, 0x1B7F); + AddButton(390, 50, 0x845, 0x846, 0x1B7F); } public override void OnResponse(NetState sender, in RelayInfo info) @@ -273,7 +272,7 @@ public partial class HangingSkeletonDeed : Item, IRewardItem } } - private class FacingGump : DynamicGump + private class FacingGump : Gump { private readonly BaseHouse _house; private readonly int _itemID; @@ -288,26 +287,26 @@ public partial class HangingSkeletonDeed : Item, IRewardItem _itemID = itemID; _location = location; _house = house; - } - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoResize(); + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - builder.AddPage(); + AddPage(0); - builder.AddBackground(0, 0, 300, 150, 0xA28); + AddBackground(0, 0, 300, 150, 0xA28); - builder.AddItem(90, 30, GetWestItemID(_itemID)); - builder.AddItem(180, 30, _itemID); + AddItem(90, 30, GetWestItemID(itemID)); + AddItem(180, 30, itemID); - builder.AddButton(50, 35, 0x868, 0x869, (int)Buttons.East); - builder.AddButton(145, 35, 0x868, 0x869, (int)Buttons.South); + AddButton(50, 35, 0x868, 0x869, (int)Buttons.East); + AddButton(145, 35, 0x868, 0x869, (int)Buttons.South); } public override void OnResponse(NetState sender, in RelayInfo info) { - if (_skeleton?.Deleted != false || _house == null || info.ButtonID == (int)Buttons.Cancel) + if (_skeleton?.Deleted != false || _house == null) { return; } @@ -318,7 +317,8 @@ public partial class HangingSkeletonDeed : Item, IRewardItem { banner = new HangingSkeleton(GetWestItemID(_itemID)); } - else if (info.ButtonID == (int)Buttons.South) + + if (info.ButtonID == (int)Buttons.South) { banner = new HangingSkeleton(_itemID); } diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs b/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs index 5e5c513b8..f677de888 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs @@ -368,7 +368,7 @@ public partial class MiningCartDeed : BaseAddonDeed, IRewardItem, IRewardOption if (IsChildOf(from.Backpack)) { - RewardOptionGump.DisplayTo(from, this); + from.SendGump(new RewardOptionGump(this)); } else { diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/MinotaurStatue.cs b/Projects/UOContent/Items/Special/Veteran Rewards/MinotaurStatue.cs index bca4ecad7..3c4a9a130 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/MinotaurStatue.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/MinotaurStatue.cs @@ -110,7 +110,7 @@ public partial class MinotaurStatueDeed : BaseAddonDeed, IRewardItem, IRewardOpt if (IsChildOf(from.Backpack)) { - RewardOptionGump.DisplayTo(from, this); + from.SendGump(new RewardOptionGump(this)); } else { diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs b/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs index 2f185fa1c..cbafbc7b0 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs @@ -19,11 +19,7 @@ public partial class RewardPottedCactus : Item, IRewardItem } [Constructible] - public RewardPottedCactus(int itemID) : base(itemID) - { - } - - public override double DefaultWeight => 5.0; + public RewardPottedCactus(int itemID) : base(itemID) => Weight = 5.0; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -37,9 +33,12 @@ public partial class PottedCactusDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public PottedCactusDeed() : base(0x14F0) => LootType = LootType.Blessed; + public PottedCactusDeed() : base(0x14F0) + { + LootType = LootType.Blessed; + Weight = 1.0; + } - public override double DefaultWeight => 1.0; public override int LabelNumber => 1080407; // Potted Cactus Deed public override void OnDoubleClick(Mobile from) @@ -51,7 +50,7 @@ public partial class PottedCactusDeed : Item, IRewardItem if (IsChildOf(from.Backpack)) { - PottedCactusGump.DisplayTo(from, this); + from.SendGump(new InternalGump(this)); } else { @@ -69,49 +68,44 @@ public partial class PottedCactusDeed : Item, IRewardItem } } - private class PottedCactusGump : StaticGump + private class InternalGump : Gump { private readonly PottedCactusDeed _deed; public override bool Singleton => true; - private PottedCactusGump(PottedCactusDeed cactus) : base(100, 200) => _deed = cactus; - - public static void DisplayTo(Mobile from, PottedCactusDeed deed) + public InternalGump(PottedCactusDeed cactus) : base(100, 200) { - if (from?.NetState == null || deed?.Deleted != false) - { - return; - } + _deed = cactus; - from.SendGump(new PottedCactusGump(deed)); - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 425, 250, 0xA28); + AddPage(0); + AddBackground(0, 0, 425, 250, 0xA28); - builder.AddPage(1); - builder.AddLabel(45, 15, 0, "Choose a Potted Cactus:"); + AddPage(1); + AddLabel(45, 15, 0, "Choose a Potted Cactus:"); - builder.AddItem(45, 75, 0x1E0F); - builder.AddButton(55, 50, 0x845, 0x846, 0x1E0F); + AddItem(45, 75, 0x1E0F); + AddButton(55, 50, 0x845, 0x846, 0x1E0F); - builder.AddItem(105, 75, 0x1E10); - builder.AddButton(115, 50, 0x845, 0x846, 0x1E10); + AddItem(105, 75, 0x1E10); + AddButton(115, 50, 0x845, 0x846, 0x1E10); - builder.AddItem(160, 75, 0x1E14); - builder.AddButton(175, 50, 0x845, 0x846, 0x1E14); + AddItem(160, 75, 0x1E14); + AddButton(175, 50, 0x845, 0x846, 0x1E14); - builder.AddItem(220, 75, 0x1E11); - builder.AddButton(235, 50, 0x845, 0x846, 0x1E11); + AddItem(220, 75, 0x1E11); + AddButton(235, 50, 0x845, 0x846, 0x1E11); - builder.AddItem(280, 75, 0x1E12); - builder.AddButton(295, 50, 0x845, 0x846, 0x1E12); + AddItem(280, 75, 0x1E12); + AddButton(295, 50, 0x845, 0x846, 0x1E12); - builder.AddItem(340, 75, 0x1E13); - builder.AddButton(355, 50, 0x845, 0x846, 0x1E13); + AddItem(340, 75, 0x1E13); + AddButton(355, 50, 0x845, 0x846, 0x1E13); } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs b/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs index 002c77d3f..b520563cc 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs @@ -9,11 +9,7 @@ namespace Server.Items; [SerializationGenerator(0)] public partial class StoneAnkhComponent : AddonComponent { - public StoneAnkhComponent(int itemID) : base(itemID) - { - } - - public override double DefaultWeight => 1.0; + public StoneAnkhComponent(int itemID) : base(itemID) => Weight = 1.0; public override bool ForceShowProperties => ObjectPropertyList.Enabled; @@ -74,22 +70,23 @@ public partial class StoneAnkh : BaseAddon, IRewardItem public override void OnComponentUsed(AddonComponent c, Mobile from) { - if (!from.InRange(Location, 2)) + if (from.InRange(Location, 2)) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } + var house = BaseHouse.FindHouseAt(this); - var house = BaseHouse.FindHouseAt(this); - - if (house?.IsOwner(from) == true) - { - RewardDemolitionGump.DisplayTo(from, this, 1049783); // Do you wish to re-deed this decoration? + if (house?.IsOwner(from) == true) + { + from.SendGump(new RewardDemolitionGump(this, 1049783)); // Do you wish to re-deed this decoration? + } + else + { + // You can only re-deed this decoration if you are the house owner or originally placed the decoration. + from.SendLocalizedMessage(1049784); + } } else { - // You can only re-deed this decoration if you are the house owner or originally placed the decoration. - from.SendLocalizedMessage(1049784); + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } } } @@ -124,7 +121,7 @@ public partial class StoneAnkhDeed : BaseAddonDeed, IRewardItem if (IsChildOf(from.Backpack)) { - StoneAnkhGump.DisplayTo(from, this); + from.SendGump(new InternalGump(this)); } else { @@ -147,37 +144,32 @@ public partial class StoneAnkhDeed : BaseAddonDeed, IRewardItem } } - private class StoneAnkhGump : StaticGump + private class InternalGump : Gump { private readonly StoneAnkhDeed _deed; public override bool Singleton => true; - private StoneAnkhGump(StoneAnkhDeed deed) : base(150, 50) => _deed = deed; - - public static void DisplayTo(Mobile from, StoneAnkhDeed deed) + public InternalGump(StoneAnkhDeed deed) : base(150, 50) { - if (from?.NetState == null || deed?.Deleted != false) - { - return; - } + _deed = deed; - from.SendGump(new StoneAnkhGump(deed)); - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(0, 0, 300, 150, 0xA28); + AddBackground(0, 0, 300, 150, 0xA28); - builder.AddItem(90, 30, 0x4); - builder.AddItem(112, 30, 0x5); - builder.AddButton(50, 35, 0x867, 0x869, (int)Buttons.South); // South + AddItem(90, 30, 0x4); + AddItem(112, 30, 0x5); + AddButton(50, 35, 0x867, 0x869, (int)Buttons.South); // South - builder.AddItem(170, 30, 0x2); - builder.AddItem(192, 30, 0x3); - builder.AddButton(145, 35, 0x867, 0x869, (int)Buttons.East); // East + AddItem(170, 30, 0x2); + AddItem(192, 30, 0x3); + AddButton(145, 35, 0x867, 0x869, (int)Buttons.East); // East } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs b/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs index 5b338a391..5f4fa7cd1 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs @@ -203,7 +203,7 @@ public partial class TreeStumpDeed : BaseAddonDeed, IRewardItem, IRewardOption if (IsChildOf(from.Backpack)) { - RewardOptionGump.DisplayTo(from, this); + from.SendGump(new RewardOptionGump(this)); } else { diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/WallBanner.cs b/Projects/UOContent/Items/Special/Veteran Rewards/WallBanner.cs index 9c8e7ca1b..14a1b6e07 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/WallBanner.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/WallBanner.cs @@ -304,7 +304,7 @@ public partial class WallBannerDeed : BaseAddonDeed, IRewardItem if (IsChildOf(from.Backpack)) { - WallBannerGump.DisplayTo(from, this); + from.SendGump(new InternalGump(this)); } else { @@ -319,157 +319,152 @@ public partial class WallBannerDeed : BaseAddonDeed, IRewardItem base.OnDoubleClick(m); } - private class WallBannerGump : StaticGump + private class InternalGump : Gump { private readonly WallBannerDeed _wallBanner; public override bool Singleton => true; - private WallBannerGump(WallBannerDeed wallBanner) : base(150, 50) => _wallBanner = wallBanner; - - public static void DisplayTo(Mobile from, WallBannerDeed wallBanner) + public InternalGump(WallBannerDeed wallBanner) : base(150, 50) { - if (from?.NetState == null || wallBanner?.Deleted != false) - { - return; - } + _wallBanner = wallBanner; - from.SendGump(new WallBannerGump(wallBanner)); - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddBackground(25, 0, 500, 265, 0xA28); - builder.AddLabel(70, 12, 0x3E3, "Choose a Wall Banner:"); + AddBackground(25, 0, 500, 265, 0xA28); + AddLabel(70, 12, 0x3E3, "Choose a Wall Banner:"); - builder.AddPage(1); + AddPage(1); - builder.AddItem(55, 110, 0x161D); - builder.AddItem(75, 90, 0x161E); - builder.AddItem(95, 70, 0x161F); - builder.AddButton(70, 50, 0x845, 0x846, 1); - builder.AddItem(105, 70, 0x1586); - builder.AddItem(125, 90, 0x1587); - builder.AddItem(145, 110, 0x1588); - builder.AddButton(145, 50, 0x845, 0x846, 2); - builder.AddItem(200, 110, 0x1620); - builder.AddItem(220, 90, 0x1621); - builder.AddItem(240, 70, 0x1622); - builder.AddButton(220, 50, 0x845, 0x846, 3); - builder.AddItem(250, 70, 0x1589); - builder.AddItem(270, 90, 0x158A); - builder.AddItem(290, 110, 0x158B); - builder.AddButton(300, 50, 0x845, 0x846, 4); - builder.AddItem(350, 110, 0x1623); - builder.AddItem(370, 90, 0x1624); - builder.AddItem(390, 70, 0x1625); - builder.AddButton(365, 50, 0x845, 0x846, 5); - builder.AddItem(400, 70, 0x158C); - builder.AddItem(420, 90, 0x158D); - builder.AddItem(440, 110, 0x158E); - builder.AddButton(445, 50, 0x845, 0x846, 6); - builder.AddButton(455, 205, 0x8B0, 0x8B0, 0, GumpButtonType.Page, 2); + AddItem(55, 110, 0x161D); + AddItem(75, 90, 0x161E); + AddItem(95, 70, 0x161F); + AddButton(70, 50, 0x845, 0x846, 1); + AddItem(105, 70, 0x1586); + AddItem(125, 90, 0x1587); + AddItem(145, 110, 0x1588); + AddButton(145, 50, 0x845, 0x846, 2); + AddItem(200, 110, 0x1620); + AddItem(220, 90, 0x1621); + AddItem(240, 70, 0x1622); + AddButton(220, 50, 0x845, 0x846, 3); + AddItem(250, 70, 0x1589); + AddItem(270, 90, 0x158A); + AddItem(290, 110, 0x158B); + AddButton(300, 50, 0x845, 0x846, 4); + AddItem(350, 110, 0x1623); + AddItem(370, 90, 0x1624); + AddItem(390, 70, 0x1625); + AddButton(365, 50, 0x845, 0x846, 5); + AddItem(400, 70, 0x158C); + AddItem(420, 90, 0x158D); + AddItem(440, 110, 0x158E); + AddButton(445, 50, 0x845, 0x846, 6); + AddButton(455, 205, 0x8B0, 0x8B0, 0, GumpButtonType.Page, 2); - builder.AddPage(2); + AddPage(2); - builder.AddItem(52, 110, 0x1626); - builder.AddItem(72, 90, 0x1627); - builder.AddItem(95, 70, 0x1628); - builder.AddButton(70, 50, 0x845, 0x846, 7); - builder.AddItem(105, 70, 0x1590); - builder.AddItem(125, 90, 0x1591); - builder.AddItem(145, 110, 0x158F); - builder.AddButton(145, 50, 0x845, 0x846, 8); - builder.AddItem(197, 110, 0x1626); - builder.AddItem(217, 90, 0x1629); - builder.AddItem(240, 70, 0x162A); - builder.AddButton(220, 50, 0x845, 0x846, 9); - builder.AddItem(250, 70, 0x1592); - builder.AddItem(270, 90, 0x1593); - builder.AddItem(290, 110, 0x158F); - builder.AddButton(300, 50, 0x845, 0x846, 10); - builder.AddItem(340, 110, 0x162B); - builder.AddItem(363, 90, 0x162C); - builder.AddItem(385, 70, 0x162D); - builder.AddButton(365, 50, 0x845, 0x846, 11); - builder.AddItem(395, 70, 0x1594); - builder.AddItem(417, 90, 0x1595); - builder.AddItem(439, 111, 0x1596); - builder.AddButton(445, 50, 0x845, 0x846, 12); - builder.AddButton(70, 205, 0x8AF, 0x8AF, 0, GumpButtonType.Page, 1); - builder.AddButton(455, 205, 0x8B0, 0x8B0, 0, GumpButtonType.Page, 3); + AddItem(52, 110, 0x1626); + AddItem(72, 90, 0x1627); + AddItem(95, 70, 0x1628); + AddButton(70, 50, 0x845, 0x846, 7); + AddItem(105, 70, 0x1590); + AddItem(125, 90, 0x1591); + AddItem(145, 110, 0x158F); + AddButton(145, 50, 0x845, 0x846, 8); + AddItem(197, 110, 0x1626); + AddItem(217, 90, 0x1629); + AddItem(240, 70, 0x162A); + AddButton(220, 50, 0x845, 0x846, 9); + AddItem(250, 70, 0x1592); + AddItem(270, 90, 0x1593); + AddItem(290, 110, 0x158F); + AddButton(300, 50, 0x845, 0x846, 10); + AddItem(340, 110, 0x162B); + AddItem(363, 90, 0x162C); + AddItem(385, 70, 0x162D); + AddButton(365, 50, 0x845, 0x846, 11); + AddItem(395, 70, 0x1594); + AddItem(417, 90, 0x1595); + AddItem(439, 111, 0x1596); + AddButton(445, 50, 0x845, 0x846, 12); + AddButton(70, 205, 0x8AF, 0x8AF, 0, GumpButtonType.Page, 1); + AddButton(455, 205, 0x8B0, 0x8B0, 0, GumpButtonType.Page, 3); - builder.AddPage(3); + AddPage(3); - builder.AddItem(55, 110, 0x162E); - builder.AddItem(75, 93, 0x1631); - builder.AddItem(95, 70, 0x1632); - builder.AddButton(70, 50, 0x845, 0x846, 13); - builder.AddItem(118, 70, 0x1598); - builder.AddItem(138, 94, 0x159B); - builder.AddItem(159, 113, 0x159C); - builder.AddButton(160, 50, 0x845, 0x846, 14); - builder.AddItem(219, 111, 0x162F); - builder.AddItem(238, 94, 0x1630); - builder.AddItem(258, 70, 0x1633); - builder.AddButton(240, 50, 0x845, 0x846, 15); - builder.AddItem(279, 70, 0x1599); - builder.AddItem(298, 93, 0x159A); - builder.AddItem(319, 113, 0x159D); - builder.AddButton(320, 50, 0x845, 0x846, 16); - builder.AddItem(380, 90, 0x160F); - builder.AddItem(400, 70, 0x1610); - builder.AddButton(390, 50, 0x845, 0x846, 17); - builder.AddItem(420, 70, 0x15A0); - builder.AddItem(440, 90, 0x15A1); - builder.AddButton(455, 50, 0x845, 0x846, 18); - builder.AddButton(70, 205, 0x8AF, 0x8AF, 0, GumpButtonType.Page, 2); - builder.AddButton(455, 205, 0x8B0, 0x8B0, 0, GumpButtonType.Page, 4); + AddItem(55, 110, 0x162E); + AddItem(75, 93, 0x1631); + AddItem(95, 70, 0x1632); + AddButton(70, 50, 0x845, 0x846, 13); + AddItem(118, 70, 0x1598); + AddItem(138, 94, 0x159B); + AddItem(159, 113, 0x159C); + AddButton(160, 50, 0x845, 0x846, 14); + AddItem(219, 111, 0x162F); + AddItem(238, 94, 0x1630); + AddItem(258, 70, 0x1633); + AddButton(240, 50, 0x845, 0x846, 15); + AddItem(279, 70, 0x1599); + AddItem(298, 93, 0x159A); + AddItem(319, 113, 0x159D); + AddButton(320, 50, 0x845, 0x846, 16); + AddItem(380, 90, 0x160F); + AddItem(400, 70, 0x1610); + AddButton(390, 50, 0x845, 0x846, 17); + AddItem(420, 70, 0x15A0); + AddItem(440, 90, 0x15A1); + AddButton(455, 50, 0x845, 0x846, 18); + AddButton(70, 205, 0x8AF, 0x8AF, 0, GumpButtonType.Page, 2); + AddButton(455, 205, 0x8B0, 0x8B0, 0, GumpButtonType.Page, 4); - builder.AddPage(4); + AddPage(4); - builder.AddItem(55, 90, 0x1611); - builder.AddItem(75, 70, 0x1612); - builder.AddButton(70, 50, 0x845, 0x846, 19); - builder.AddItem(105, 70, 0x15A2); - builder.AddItem(125, 90, 0x15A3); - builder.AddButton(145, 50, 0x845, 0x846, 20); - builder.AddItem(200, 84, 0x1613); - builder.AddItem(220, 70, 0x1614); - builder.AddButton(215, 50, 0x845, 0x846, 21); - builder.AddItem(250, 70, 0x15A4); - builder.AddItem(270, 84, 0x15A5); - builder.AddButton(290, 50, 0x845, 0x846, 22); - builder.AddItem(350, 90, 0x1615); - builder.AddItem(370, 70, 0x1616); - builder.AddButton(365, 50, 0x845, 0x846, 23); - builder.AddItem(400, 70, 0x15A6); - builder.AddItem(420, 90, 0x15A7); - builder.AddButton(445, 50, 0x845, 0x846, 24); - builder.AddButton(70, 205, 0x8AF, 0x8AF, 0, GumpButtonType.Page, 3); - builder.AddButton(455, 205, 0x8B0, 0x8B0, 0, GumpButtonType.Page, 5); + AddItem(55, 90, 0x1611); + AddItem(75, 70, 0x1612); + AddButton(70, 50, 0x845, 0x846, 19); + AddItem(105, 70, 0x15A2); + AddItem(125, 90, 0x15A3); + AddButton(145, 50, 0x845, 0x846, 20); + AddItem(200, 84, 0x1613); + AddItem(220, 70, 0x1614); + AddButton(215, 50, 0x845, 0x846, 21); + AddItem(250, 70, 0x15A4); + AddItem(270, 84, 0x15A5); + AddButton(290, 50, 0x845, 0x846, 22); + AddItem(350, 90, 0x1615); + AddItem(370, 70, 0x1616); + AddButton(365, 50, 0x845, 0x846, 23); + AddItem(400, 70, 0x15A6); + AddItem(420, 90, 0x15A7); + AddButton(445, 50, 0x845, 0x846, 24); + AddButton(70, 205, 0x8AF, 0x8AF, 0, GumpButtonType.Page, 3); + AddButton(455, 205, 0x8B0, 0x8B0, 0, GumpButtonType.Page, 5); - builder.AddPage(5); + AddPage(5); - builder.AddItem(55, 90, 0x1617); - builder.AddItem(77, 70, 0x1618); - builder.AddButton(70, 50, 0x845, 0x846, 25); - builder.AddItem(105, 70, 0x15A8); - builder.AddItem(127, 90, 0x15A9); - builder.AddButton(145, 50, 0x845, 0x846, 26); - builder.AddItem(200, 90, 0x1619); - builder.AddItem(222, 70, 0x161A); - builder.AddButton(220, 50, 0x845, 0x846, 27); - builder.AddItem(250, 70, 0x15AA); - builder.AddItem(272, 90, 0x15AB); - builder.AddButton(300, 50, 0x845, 0x846, 28); - builder.AddItem(350, 90, 0x161B); - builder.AddItem(372, 70, 0x161C); - builder.AddButton(365, 50, 0x845, 0x846, 29); - builder.AddItem(400, 70, 0x15AC); - builder.AddItem(422, 90, 0x15AD); - builder.AddButton(445, 50, 0x845, 0x846, 30); - builder.AddButton(70, 205, 0x8AF, 0x8AF, 0, GumpButtonType.Page, 4); + AddItem(55, 90, 0x1617); + AddItem(77, 70, 0x1618); + AddButton(70, 50, 0x845, 0x846, 25); + AddItem(105, 70, 0x15A8); + AddItem(127, 90, 0x15A9); + AddButton(145, 50, 0x845, 0x846, 26); + AddItem(200, 90, 0x1619); + AddItem(222, 70, 0x161A); + AddButton(220, 50, 0x845, 0x846, 27); + AddItem(250, 70, 0x15AA); + AddItem(272, 90, 0x15AB); + AddButton(300, 50, 0x845, 0x846, 28); + AddItem(350, 90, 0x161B); + AddItem(372, 70, 0x161C); + AddButton(365, 50, 0x845, 0x846, 29); + AddItem(400, 70, 0x15AC); + AddItem(422, 90, 0x15AD); + AddButton(445, 50, 0x845, 0x846, 30); + AddButton(70, 205, 0x8AF, 0x8AF, 0, GumpButtonType.Page, 4); } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs b/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs index b0c6bcd3c..95af438d8 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs @@ -24,11 +24,11 @@ public partial class WeaponEngravingTool : Item, IUsesRemaining, IRewardItem public WeaponEngravingTool(int uses = 10) : base(0x32F8) { LootType = LootType.Blessed; + Weight = 1.0; + _usesRemaining = uses; } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1076158; // Weapon Engraving Tool public bool ShowUsesRemaining @@ -73,7 +73,7 @@ public partial class WeaponEngravingTool : Item, IUsesRemaining, IRewardItem } from.SendLocalizedMessage(1076163); // There are no charges left on this engraving tool. - WeaponEngravingToolConfirmGump.DisplayTo(from, this, null); + from.SendGump(new ConfirmGump(this, null)); } public override void GetProperties(IPropertyList list) @@ -174,7 +174,7 @@ public partial class WeaponEngravingTool : Item, IUsesRemaining, IRewardItem if (targeted is BaseWeapon item) { - WeaponEngravingToolGump.DisplayTo(from, _tool, item); + from.SendGump(new InternalGump(_tool, item)); } else { @@ -183,47 +183,39 @@ public partial class WeaponEngravingTool : Item, IUsesRemaining, IRewardItem } } - private class WeaponEngravingToolGump : StaticGump + private class InternalGump : Gump { private readonly BaseWeapon _target; private readonly WeaponEngravingTool _tool; public override bool Singleton => true; - private WeaponEngravingToolGump(WeaponEngravingTool tool, BaseWeapon target) : base(0, 0) + public InternalGump(WeaponEngravingTool tool, BaseWeapon target) : base(0, 0) { _tool = tool; _target = target; - } - public static void DisplayTo(Mobile from, WeaponEngravingTool tool, BaseWeapon target) - { - if (from?.NetState == null || tool?.Deleted != false || target?.Deleted != false) - { - return; - } + Closable = true; + Disposable = true; + Draggable = true; + Resizable = false; - from.SendGump(new WeaponEngravingToolGump(tool, target)); - } + AddBackground(50, 50, 400, 300, 0xA28); - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(50, 50, 400, 300, 0xA28); - - builder.AddHtmlLocalized(50, 70, 400, 20, 1072359, 0x0); //
Engraving Tool
+ AddHtmlLocalized(50, 70, 400, 20, 1072359, 0x0); //
Engraving Tool
// Please enter the text to add to the selected object. Leave the text area blank to remove any existing text. Removing text does not use a charge. - builder.AddHtmlLocalized(75, 95, 350, 145, 1076229, 0x0, true, true); - builder.AddButton(125, 300, 0x81A, 0x81B, (int)Buttons.Okay); - builder.AddButton(320, 300, 0x819, 0x818, (int)Buttons.Cancel); - builder.AddImageTiled(75, 245, 350, 40, 0xDB0); - builder.AddImageTiled(76, 245, 350, 2, 0x23C5); - builder.AddImageTiled(75, 245, 2, 40, 0x23C3); - builder.AddImageTiled(75, 285, 350, 2, 0x23C5); - builder.AddImageTiled(425, 245, 2, 42, 0x23C3); + AddHtmlLocalized(75, 95, 350, 145, 1076229, 0x0, true, true); + AddButton(125, 300, 0x81A, 0x81B, (int)Buttons.Okay); + AddButton(320, 300, 0x819, 0x818, (int)Buttons.Cancel); + AddImageTiled(75, 245, 350, 40, 0xDB0); + AddImageTiled(76, 245, 350, 2, 0x23C5); + AddImageTiled(75, 245, 2, 40, 0x23C3); + AddImageTiled(75, 285, 350, 2, 0x23C5); + AddImageTiled(425, 245, 2, 42, 0x23C3); - builder.AddTextEntry(75, 245, 350, 40, 0x0, (int)Buttons.Text, ""); + AddTextEntry(75, 245, 350, 40, 0x0, (int)Buttons.Text, ""); } public override void OnResponse(NetState state, in RelayInfo info) @@ -270,54 +262,42 @@ public partial class WeaponEngravingTool : Item, IUsesRemaining, IRewardItem } } - public class WeaponEngravingToolConfirmGump : DynamicGump + public class ConfirmGump : Gump { private readonly WeaponEngravingTool _engraver; private readonly Mobile _guildmaster; - public override bool Singleton => true; - - private WeaponEngravingToolConfirmGump(WeaponEngravingTool engraver, Mobile guildmaster) : base(200, 200) + public ConfirmGump(WeaponEngravingTool engraver, Mobile guildmaster) : base(200, 200) { _engraver = engraver; _guildmaster = guildmaster; - } - public static void DisplayTo(Mobile from, WeaponEngravingTool engraver, Mobile guildmaster) - { - if (from?.NetState == null || engraver?.Deleted != false) - { - return; - } + Closable = false; + Disposable = true; + Draggable = true; + Resizable = false; - from.SendGump(new WeaponEngravingToolConfirmGump(engraver, guildmaster)); - } + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); + AddBackground(0, 0, 291, 133, 0x13BE); + AddImageTiled(5, 5, 280, 100, 0xA40); - builder.AddPage(); - - builder.AddBackground(0, 0, 291, 133, 0x13BE); - builder.AddImageTiled(5, 5, 280, 100, 0xA40); - - if (_guildmaster != null) + if (guildmaster != null) { // It will cost you 100,000 gold and a blue diamond to recharge your weapon engraver with 10 charges. - builder.AddHtmlLocalized(9, 9, 272, 100, 1076169, 0x7FFF); - builder.AddHtmlLocalized(195, 109, 120, 20, 1076172, 0x7FFF); // Recharge it + AddHtmlLocalized(9, 9, 272, 100, 1076169, 0x7FFF); + AddHtmlLocalized(195, 109, 120, 20, 1076172, 0x7FFF); // Recharge it } else { // You will need a blue diamond to repair the tip of the engraver. A successful repair will give the engraver 10 charges. - builder.AddHtmlLocalized(9, 9, 272, 100, 1076176, 0x7FFF); - builder.AddHtmlLocalized(195, 109, 120, 20, 1076177, 0x7FFF); // Replace the tip. + AddHtmlLocalized(9, 9, 272, 100, 1076176, 0x7FFF); + AddHtmlLocalized(195, 109, 120, 20, 1076177, 0x7FFF); // Replace the tip. } - builder.AddButton(160, 107, 0xFB7, 0xFB8, (int)Buttons.Confirm); - builder.AddButton(5, 107, 0xFB1, 0xFB2, (int)Buttons.Cancel); - builder.AddHtmlLocalized(40, 109, 100, 20, 1060051, 0x7FFF); // CANCEL + AddButton(160, 107, 0xFB7, 0xFB8, (int)Buttons.Confirm); + AddButton(5, 107, 0xFB1, 0xFB2, (int)Buttons.Cancel); + AddHtmlLocalized(40, 109, 100, 20, 1060051, 0x7FFF); // CANCEL } public override void OnResponse(NetState state, in RelayInfo info) diff --git a/Projects/UOContent/Items/Suits/BaseSuit.cs b/Projects/UOContent/Items/Suits/BaseSuit.cs index dd1eb00f6..ee7bf3897 100644 --- a/Projects/UOContent/Items/Suits/BaseSuit.cs +++ b/Projects/UOContent/Items/Suits/BaseSuit.cs @@ -8,6 +8,7 @@ public abstract partial class BaseSuit : Item public BaseSuit(AccessLevel level, int hue, int itemID) : base(itemID) { Hue = hue; + Weight = 1.0; Movable = false; LootType = LootType.Newbied; Layer = Layer.OuterTorso; @@ -15,11 +16,20 @@ public abstract partial class BaseSuit : Item _accessLevel = level; } - public override double DefaultWeight => 1.0; + [SerializableProperty(0)] + public AccessLevel AccessLevel + { + get => _accessLevel; + set + { + var oldAccessLevel = _accessLevel; + _accessLevel = value; + InvalidateProperties(); + this.MarkDirty(); - [SerializableField(0, fieldChanged: nameof(OnAccessLevelChanged))] - [InvalidateProperties] - private AccessLevel _accessLevel; + OnAccessLevelChanged(oldAccessLevel, _accessLevel); + } + } public virtual void OnAccessLevelChanged(AccessLevel oldAccessLevel, AccessLevel accessLevel) { diff --git a/Projects/UOContent/Items/Talismans/BaseTalisman.cs b/Projects/UOContent/Items/Talismans/BaseTalisman.cs index b3f0c2ed4..c96e70f68 100644 --- a/Projects/UOContent/Items/Talismans/BaseTalisman.cs +++ b/Projects/UOContent/Items/Talismans/BaseTalisman.cs @@ -1,6 +1,5 @@ using System; using ModernUO.Serialization; -using Server.Engines.BuffIcons; using Server.Mobiles; using Server.Spells.Fifth; using Server.Spells.First; @@ -128,134 +127,140 @@ public partial class BaseTalisman : Item, IAosItem [SerializedIgnoreDupe] [SerializableField(0, setter: "private")] - [SaveFlag(nameof(ShouldSerializeAttributes), nameof(AttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosAttributes _attributes; + [SerializableFieldSaveFlag(0)] public bool ShouldSerializeAttributes() => !_attributes.IsEmpty; + [SerializableFieldDefault(0)] private AosAttributes AttributesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(1, setter: "private")] - [SaveFlag(nameof(ShouldSerializeSkillBonuses), nameof(SkillBonusesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosSkillBonuses _skillBonuses; + [SerializableFieldSaveFlag(1)] public bool ShouldSerializeSkillBonuses() => !_skillBonuses.IsEmpty; + [SerializableFieldDefault(1)] private AosSkillBonuses SkillBonusesDefaultValue() => new(this); [SerializedIgnoreDupe] [InvalidateProperties] [SerializableField(2)] - [SaveFlag(nameof(ShouldSerializeProtection), nameof(ProtectionDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private TalismanAttribute _protection; + [SerializableFieldSaveFlag(2)] public bool ShouldSerializeProtection() => !_protection.IsEmpty; + [SerializableFieldDefault(2)] private TalismanAttribute ProtectionDefaultValue() => new(); [SerializedIgnoreDupe] [InvalidateProperties] [SerializableField(3)] - [SaveFlag(nameof(ShouldSerializeKiller), nameof(KillerDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private TalismanAttribute _killer; + [SerializableFieldSaveFlag(3)] public bool ShouldSerializeKiller() => !_killer.IsEmpty; + [SerializableFieldDefault(3)] private TalismanAttribute KillerDefaultValue() => new(); [SerializedIgnoreDupe] [InvalidateProperties] [SerializableField(4)] - [SaveFlag(nameof(ShouldSerializeSummoner), nameof(SummonerDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private TalismanAttribute _summoner; + [SerializableFieldSaveFlag(4)] public bool ShouldSerializeSummoner() => !_summoner.IsEmpty; + [SerializableFieldDefault(4)] private TalismanAttribute SummonerDefaultValue() => new(); [InvalidateProperties] [SerializableField(5)] - [SaveFlag(nameof(ShouldSerializeRemoval))] [SerializedCommandProperty(AccessLevel.GameMaster)] private TalismanRemoval _removal; + [SerializableFieldSaveFlag(5)] public bool ShouldSerializeRemoval() => _removal != TalismanRemoval.None; [InvalidateProperties] [SerializableField(6)] - [SaveFlag(nameof(ShouldSerializeSkill))] [SerializedCommandProperty(AccessLevel.GameMaster)] private SkillName _skill; + [SerializableFieldSaveFlag(6)] public bool ShouldSerializeSkill() => (int)_skill != 0; [EncodedInt] [InvalidateProperties] [SerializableField(7)] - [SaveFlag(nameof(ShouldSerializeSuccessBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _successBonus; + [SerializableFieldSaveFlag(7)] public bool ShouldSerializeSuccessBonus() => _successBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(8)] - [SaveFlag(nameof(ShouldSerializeExceptionalBonus))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _exceptionalBonus; + [SerializableFieldSaveFlag(8)] public bool ShouldSerializeExceptionalBonus() => _exceptionalBonus != 0; [EncodedInt] [InvalidateProperties] [SerializableField(9)] - [SaveFlag(nameof(ShouldSerializeMaxCharges))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxCharges; + [SerializableFieldSaveFlag(9)] public bool ShouldSerializeMaxCharges() => _maxCharges != 0; [EncodedInt] [InvalidateProperties] [SerializableField(11)] - [SaveFlag(nameof(ShouldSerializeMaxChargeTime))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxChargeTime; + [SerializableFieldSaveFlag(11)] public bool ShouldSerializeMaxChargeTime() => _maxChargeTime != 0; [EncodedInt] [InvalidateProperties] [SerializableField(12)] - [SaveFlag(nameof(ShouldSerializeChargeTime))] private int _chargeTime; + [SerializableFieldSaveFlag(12)] public bool ShouldSerializeChargeTime() => _chargeTime != 0; [InvalidateProperties] [SerializableField(13)] - [SaveFlag(nameof(ShouldSerializeBlessed))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _blessed; + [SerializableFieldSaveFlag(13)] public bool ShouldSerializeBlessed() => _blessed; [InvalidateProperties] [SerializableField(14)] - [SaveFlag(nameof(ShouldSerializeSlayer))] [SerializedCommandProperty(AccessLevel.GameMaster)] private TalismanSlayerName _slayer; + [SerializableFieldSaveFlag(14)] public bool ShouldSerializeSlayer() => _slayer != TalismanSlayerName.None; - private BaseCreature _creature; + private Mobile _creature; + private TimerExecutionToken _timerToken; @@ -266,6 +271,7 @@ public partial class BaseTalisman : Item, IAosItem public BaseTalisman(int itemID) : base(itemID) { Layer = Layer.Talisman; + Weight = 1.0; _protection = new TalismanAttribute(); _killer = new TalismanAttribute(); @@ -274,25 +280,29 @@ public partial class BaseTalisman : Item, IAosItem SkillBonuses = new AosSkillBonuses(this); } - public override double DefaultWeight => 1.0; - public override int LabelNumber => 1071023; // Talisman public virtual bool ForceShowName => false; // used to override default summoner/removal name - [SerializableField(10, fieldChanged: nameof(OnChargesChanged))] - [SaveFlag(nameof(ShouldSerializeCharges))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _charges; - - private void OnChargesChanged(int oldValue, int newValue) + [SerializableProperty(10)] + [CommandProperty(AccessLevel.GameMaster)] + public int Charges { - if (_chargeTime > 0) + get => _charges; + set { - StartTimer(); + _charges = value; + + if (_chargeTime > 0) + { + StartTimer(); + } + + InvalidateProperties(); + this.MarkDirty(); } } + [SerializableFieldSaveFlag(10)] public bool ShouldSerializeCharges() => _charges != 0; public static void Configure() @@ -606,7 +616,127 @@ public partial class BaseTalisman : Item, IAosItem SkillBonuses.GetProperties(list); - Attributes.GetProperties(list); + int prop; + + if ((prop = Attributes.WeaponDamage) != 0) + { + list.Add(1060401, prop); // damage increase ~1_val~% + } + + if ((prop = Attributes.DefendChance) != 0) + { + list.Add(1060408, prop); // defense chance increase ~1_val~% + } + + if ((prop = Attributes.BonusDex) != 0) + { + list.Add(1060409, prop); // dexterity bonus ~1_val~ + } + + if ((prop = Attributes.EnhancePotions) != 0) + { + list.Add(1060411, prop); // enhance potions ~1_val~% + } + + if ((prop = Attributes.CastRecovery) != 0) + { + list.Add(1060412, prop); // faster cast recovery ~1_val~ + } + + if ((prop = Attributes.CastSpeed) != 0) + { + list.Add(1060413, prop); // faster casting ~1_val~ + } + + if ((prop = Attributes.AttackChance) != 0) + { + list.Add(1060415, prop); // hit chance increase ~1_val~% + } + + if ((prop = Attributes.BonusHits) != 0) + { + list.Add(1060431, prop); // hit point increase ~1_val~ + } + + if ((prop = Attributes.BonusInt) != 0) + { + list.Add(1060432, prop); // intelligence bonus ~1_val~ + } + + if ((prop = Attributes.LowerManaCost) != 0) + { + list.Add(1060433, prop); // lower mana cost ~1_val~% + } + + if ((prop = Attributes.LowerRegCost) != 0) + { + list.Add(1060434, prop); // lower reagent cost ~1_val~% + } + + if ((prop = Attributes.Luck) != 0) + { + list.Add(1060436, prop); // luck ~1_val~ + } + + if ((prop = Attributes.BonusMana) != 0) + { + list.Add(1060439, prop); // mana increase ~1_val~ + } + + if ((prop = Attributes.RegenMana) != 0) + { + list.Add(1060440, prop); // mana regeneration ~1_val~ + } + + if (Attributes.NightSight != 0) + { + list.Add(1060441); // night sight + } + + if ((prop = Attributes.ReflectPhysical) != 0) + { + list.Add(1060442, prop); // reflect physical damage ~1_val~% + } + + if ((prop = Attributes.RegenStam) != 0) + { + list.Add(1060443, prop); // stamina regeneration ~1_val~ + } + + if ((prop = Attributes.RegenHits) != 0) + { + list.Add(1060444, prop); // hit point regeneration ~1_val~ + } + + if (Attributes.SpellChanneling != 0) + { + list.Add(1060482); // spell channeling + } + + if ((prop = Attributes.SpellDamage) != 0) + { + list.Add(1060483, prop); // spell damage increase ~1_val~% + } + + if ((prop = Attributes.BonusStam) != 0) + { + list.Add(1060484, prop); // stamina increase ~1_val~ + } + + if ((prop = Attributes.BonusStr) != 0) + { + list.Add(1060485, prop); // strength bonus ~1_val~ + } + + if ((prop = Attributes.WeaponSpeed) != 0) + { + list.Add(1060486, prop); // swing speed increase ~1_val~% + } + + if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) + { + list.Add(1075210, prop); // Increased Karma Loss ~1val~% + } if (_maxCharges > 0) { @@ -843,140 +973,126 @@ public partial class BaseTalisman : Item, IAosItem switch (m_Talisman.Removal) { case TalismanRemoval.Curse: + target.PlaySound(0xF6); + target.PlaySound(0x1F7); + target.FixedParticles(0x3709, 1, 30, 9963, 13, 3, EffectLayer.Head); + + IEntity mfrom = new Entity( + Serial.Zero, + new Point3D(target.X, target.Y, target.Z - 10), + from.Map + ); + IEntity mto = new Entity(Serial.Zero, new Point3D(target.X, target.Y, target.Z + 50), from.Map); + Effects.SendMovingParticles( + mfrom, + mto, + 0x2255, + 1, + 0, + false, + false, + 13, + 3, + 9501, + 1, + 0, + EffectLayer.Head, + 0x100 + ); + + var mod = target.GetStatMod("[Magic] Str Curse"); + if (mod?.Offset < 0) { - target.PlaySound(0xF6); - target.PlaySound(0x1F7); - target.FixedParticles(0x3709, 1, 30, 9963, 13, 3, EffectLayer.Head); - - IEntity mfrom = new Entity( - Serial.Zero, - new Point3D(target.X, target.Y, target.Z - 10), - from.Map - ); - IEntity mto = new Entity(Serial.Zero, new Point3D(target.X, target.Y, target.Z + 50), from.Map); - Effects.SendMovingParticles( - mfrom, - mto, - 0x2255, - 1, - 0, - false, - false, - 13, - 3, - 9501, - 1, - 0, - EffectLayer.Head, - 0x100 - ); - - var mod = target.GetStatMod("[Magic] Str Curse"); - if (mod?.Offset < 0) - { - target.RemoveStatMod("[Magic] Str Curse"); - } - - mod = target.GetStatMod("[Magic] Dex Curse"); - if (mod?.Offset < 0) - { - target.RemoveStatMod("[Magic] Dex Curse"); - } - - mod = target.GetStatMod("[Magic] Int Curse"); - if (mod?.Offset < 0) - { - target.RemoveStatMod("[Magic] Int Curse"); - } - - target.Paralyzed = false; - - EvilOmenSpell.EndEffect(target); - StrangleSpell.RemoveCurse(target); - CorpseSkinSpell.RemoveCurse(target); - CurseSpell.RemoveEffect(target); - - if (target is PlayerMobile pm) - { - pm.RemoveBuff(BuffIcon.Clumsy); - pm.RemoveBuff(BuffIcon.FeebleMind); - pm.RemoveBuff(BuffIcon.Weaken); - pm.RemoveBuff(BuffIcon.MassCurse); - } - - target.SendLocalizedMessage(1072408); // Any curses on you have been lifted - - if (target != from) - { - from.SendLocalizedMessage(1072409); // Your targets curses have been lifted - } - - break; + target.RemoveStatMod("[Magic] Str Curse"); } + + mod = target.GetStatMod("[Magic] Dex Curse"); + if (mod?.Offset < 0) + { + target.RemoveStatMod("[Magic] Dex Curse"); + } + + mod = target.GetStatMod("[Magic] Int Curse"); + if (mod?.Offset < 0) + { + target.RemoveStatMod("[Magic] Int Curse"); + } + + target.Paralyzed = false; + + EvilOmenSpell.EndEffect(target); + StrangleSpell.RemoveCurse(target); + CorpseSkinSpell.RemoveCurse(target); + CurseSpell.RemoveEffect(target); + + BuffInfo.RemoveBuff(target, BuffIcon.Clumsy); + BuffInfo.RemoveBuff(target, BuffIcon.FeebleMind); + BuffInfo.RemoveBuff(target, BuffIcon.Weaken); + BuffInfo.RemoveBuff(target, BuffIcon.MassCurse); + + target.SendLocalizedMessage(1072408); // Any curses on you have been lifted + + if (target != from) + { + from.SendLocalizedMessage(1072409); // Your targets curses have been lifted + } + + break; case TalismanRemoval.Damage: + target.PlaySound(0x201); + Effects.SendLocationParticles( + EffectItem.Create(target.Location, target.Map, EffectItem.DefaultDuration), + 0x3728, + 1, + 13, + 0x834, + 0, + 0x13B2, + 0 + ); + + BleedAttack.EndBleed(target, true); + MortalStrike.EndWound(target); + + BuffInfo.RemoveBuff(target, BuffIcon.Bleed); + BuffInfo.RemoveBuff(target, BuffIcon.MortalStrike); + + target.SendLocalizedMessage(1072405); // Your lasting damage effects have been removed! + + if (target != from) { - target.PlaySound(0x201); - Effects.SendLocationParticles( - EffectItem.Create(target.Location, target.Map, EffectItem.DefaultDuration), - 0x3728, - 1, - 13, - 0x834, - 0, - 0x13B2, - 0 - ); - - BleedAttack.EndBleed(target, true); - MortalStrike.EndWound(target); - - if (target is PlayerMobile pm) - { - pm.RemoveBuff(BuffIcon.Bleed); - pm.RemoveBuff(BuffIcon.MortalStrike); - } - - target.SendLocalizedMessage(1072405); // Your lasting damage effects have been removed! - - if (target != from) - { - from.SendLocalizedMessage(1072406); // Your Targets lasting damage effects have been removed! - } - - break; + from.SendLocalizedMessage(1072406); // Your Targets lasting damage effects have been removed! } + + break; case TalismanRemoval.Ward: + target.PlaySound(0x201); + Effects.SendLocationParticles( + EffectItem.Create(target.Location, target.Map, EffectItem.DefaultDuration), + 0x3728, + 1, + 13, + 0x834, + 0, + 0x13B2, + 0 + ); + + MagicReflectSpell.EndReflect(target); + ReactiveArmorSpell.EndArmor(target); + ProtectionSpell.EndProtection(target); + + target.SendLocalizedMessage(1072402); // Your wards have been removed! + + if (target != from) { - target.PlaySound(0x201); - Effects.SendLocationParticles( - EffectItem.Create(target.Location, target.Map, EffectItem.DefaultDuration), - 0x3728, - 1, - 13, - 0x834, - 0, - 0x13B2, - 0 - ); - - MagicReflectSpell.EndReflect(target); - ReactiveArmorSpell.EndArmor(target); - ProtectionSpell.EndProtection(target); - - target.SendLocalizedMessage(1072402); // Your wards have been removed! - - if (target != from) - { - from.SendLocalizedMessage(1072403); // Your target's wards have been removed! - } - - break; + from.SendLocalizedMessage(1072403); // Your target's wards have been removed! } + + break; case TalismanRemoval.Wildfire: - { - // TODO - break; - } + // TODO + break; } m_Talisman.OnAfterUse(from); diff --git a/Projects/UOContent/Items/Talismans/Items/EnchantedSwitch.cs b/Projects/UOContent/Items/Talismans/Items/EnchantedSwitch.cs index b7ed6829b..0258a5feb 100644 --- a/Projects/UOContent/Items/Talismans/Items/EnchantedSwitch.cs +++ b/Projects/UOContent/Items/Talismans/Items/EnchantedSwitch.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class EnchantedSwitch : Item { [Constructible] - public EnchantedSwitch() : base(0x2F5C) - { - } - - public override double DefaultWeight => 1.0; + public EnchantedSwitch() : base(0x2F5C) => Weight = 1.0; public override int LabelNumber => 1072893; // enchanted switch } diff --git a/Projects/UOContent/Items/Talismans/Items/HollowPrism.cs b/Projects/UOContent/Items/Talismans/Items/HollowPrism.cs index 723afa349..7f58dcfa5 100644 --- a/Projects/UOContent/Items/Talismans/Items/HollowPrism.cs +++ b/Projects/UOContent/Items/Talismans/Items/HollowPrism.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class HollowPrism : Item { [Constructible] - public HollowPrism() : base(0x2F5D) - { - } - - public override double DefaultWeight => 1.0; + public HollowPrism() : base(0x2F5D) => Weight = 1.0; public override int LabelNumber => 1072895; // hollow prism } diff --git a/Projects/UOContent/Items/Talismans/Items/JeweledFiligree.cs b/Projects/UOContent/Items/Talismans/Items/JeweledFiligree.cs index 042ffa032..8164dc22e 100644 --- a/Projects/UOContent/Items/Talismans/Items/JeweledFiligree.cs +++ b/Projects/UOContent/Items/Talismans/Items/JeweledFiligree.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class JeweledFiligree : Item { [Constructible] - public JeweledFiligree() : base(0x2F5E) - { - } - - public override double DefaultWeight => 1.0; + public JeweledFiligree() : base(0x2F5E) => Weight = 1.0; public override int LabelNumber => 1072894; // jeweled filigree } diff --git a/Projects/UOContent/Items/Talismans/Items/RunedPrism.cs b/Projects/UOContent/Items/Talismans/Items/RunedPrism.cs index dc8791966..af7429967 100644 --- a/Projects/UOContent/Items/Talismans/Items/RunedPrism.cs +++ b/Projects/UOContent/Items/Talismans/Items/RunedPrism.cs @@ -6,11 +6,7 @@ namespace Server.Items; public partial class RunedPrism : Item { [Constructible] - public RunedPrism() : base(0x2F57) - { - } - - public override double DefaultWeight => 1.0; + public RunedPrism() : base(0x2F57) => Weight = 1.0; public override int LabelNumber => 1073465; // runed prism } diff --git a/Projects/UOContent/Items/Talismans/Items/RunedSwitch.cs b/Projects/UOContent/Items/Talismans/Items/RunedSwitch.cs index 0c4bdec4d..bc1c741c4 100644 --- a/Projects/UOContent/Items/Talismans/Items/RunedSwitch.cs +++ b/Projects/UOContent/Items/Talismans/Items/RunedSwitch.cs @@ -7,11 +7,7 @@ namespace Server.Items; public partial class RunedSwitch : Item { [Constructible] - public RunedSwitch() : base(0x2F61) - { - } - - public override double DefaultWeight => 1.0; + public RunedSwitch() : base(0x2F61) => Weight = 1.0; public RunedSwitch(Serial serial) : base(serial) { diff --git a/Projects/UOContent/Items/Talismans/RandomTalisman.cs b/Projects/UOContent/Items/Talismans/RandomTalisman.cs index 5235c6f20..95e613363 100644 --- a/Projects/UOContent/Items/Talismans/RandomTalisman.cs +++ b/Projects/UOContent/Items/Talismans/RandomTalisman.cs @@ -1,9 +1,6 @@ -using ModernUO.Serialization; - namespace Server.Items; -[SerializationGenerator(0, false)] -public partial class RandomTalisman : BaseTalisman +public class RandomTalisman : BaseTalisman { [Constructible] public RandomTalisman() : base(GetRandomItemID()) @@ -43,4 +40,22 @@ public partial class RandomTalisman : BaseTalisman SuccessBonus = GetRandomSuccessful(); Charges = MaxCharges; } -} + + public RandomTalisman(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } +} \ No newline at end of file diff --git a/Projects/UOContent/Items/Traps/FlameSpurtTrap.cs b/Projects/UOContent/Items/Traps/FlameSpurtTrap.cs index 61e30046c..31bff1636 100644 --- a/Projects/UOContent/Items/Traps/FlameSpurtTrap.cs +++ b/Projects/UOContent/Items/Traps/FlameSpurtTrap.cs @@ -8,8 +8,7 @@ namespace Server.Items; public partial class FlameSpurtTrap : BaseTrap { [SerializableField(0)] - private Static _spurt; - + private Item _spurt; private TimerExecutionToken _timerToken; [Constructible] diff --git a/Projects/UOContent/Items/Traps/SawTrap.cs b/Projects/UOContent/Items/Traps/SawTrap.cs index 4a72a9560..d4ab21077 100644 --- a/Projects/UOContent/Items/Traps/SawTrap.cs +++ b/Projects/UOContent/Items/Traps/SawTrap.cs @@ -1,5 +1,4 @@ using System; -using ModernUO.Serialization; using Server.Spells; namespace Server.Items; @@ -12,25 +11,31 @@ public enum SawTrapType NorthFloor } -[SerializationGenerator(0, false)] -public partial class SawTrap : BaseTrap +public class SawTrap : BaseTrap { [Constructible] public SawTrap(SawTrapType type = SawTrapType.NorthFloor) : base(GetBaseID(type)) { } + public SawTrap(Serial serial) : base(serial) + { + } + [CommandProperty(AccessLevel.GameMaster)] public SawTrapType Type { - get => ItemID switch + get { - 0x1103 => SawTrapType.NorthWall, - 0x1116 => SawTrapType.WestWall, - 0x11AC => SawTrapType.NorthFloor, - 0x11B1 => SawTrapType.WestFloor, - _ => SawTrapType.NorthWall - }; + return ItemID switch + { + 0x1103 => SawTrapType.NorthWall, + 0x1116 => SawTrapType.WestWall, + 0x11AC => SawTrapType.NorthFloor, + 0x11B1 => SawTrapType.WestFloor, + _ => SawTrapType.NorthWall + }; + } set => ItemID = GetBaseID(value); } @@ -39,8 +44,9 @@ public partial class SawTrap : BaseTrap public override int PassiveTriggerRange => 0; public override TimeSpan ResetDelay => TimeSpan.FromSeconds(0.0); - public static int GetBaseID(SawTrapType type) => - type switch + public static int GetBaseID(SawTrapType type) + { + return type switch { SawTrapType.NorthWall => 0x1103, SawTrapType.WestWall => 0x1116, @@ -48,6 +54,7 @@ public partial class SawTrap : BaseTrap SawTrapType.WestFloor => 0x11B1, _ => 0 }; + } public override void OnTrigger(Mobile from) { @@ -63,4 +70,18 @@ public partial class SawTrap : BaseTrap from.LocalOverheadMessage(MessageType.Regular, 0x22, 500853); // You stepped onto a blade trap! } -} + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } +} \ No newline at end of file diff --git a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel1.cs b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel1.cs index 929903171..61dfd853d 100644 --- a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel1.cs +++ b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel1.cs @@ -72,10 +72,7 @@ public partial class TreasureChestLevel1 : LockableContainer public override bool IsDecoContainer => false; - // Rolled once: DecayScheduler reads ScheduledDecayTime repeatedly and needs a stable value. - private readonly TimeSpan _decayTime = TimeSpan.FromMinutes(Utility.Random(15, 60)); - - public override TimeSpan DecayTime => _decayTime; + public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); public override int DefaultGumpID => 0x44; diff --git a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel2.cs b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel2.cs index f212d3e71..bc3fe877c 100644 --- a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel2.cs +++ b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel2.cs @@ -69,10 +69,7 @@ public partial class TreasureChestLevel2 : LockableContainer public override bool IsDecoContainer => false; - // Rolled once: DecayScheduler reads ScheduledDecayTime repeatedly and needs a stable value. - private readonly TimeSpan _decayTime = TimeSpan.FromMinutes(Utility.Random(15, 60)); - - public override TimeSpan DecayTime => _decayTime; + public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); public override int DefaultGumpID => 0x42; diff --git a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel3.cs b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel3.cs index 04b4577c9..9ec26c7bb 100644 --- a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel3.cs +++ b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel3.cs @@ -114,10 +114,7 @@ public partial class TreasureChestLevel3 : LockableContainer public override bool IsDecoContainer => false; - // Rolled once: DecayScheduler reads ScheduledDecayTime repeatedly and needs a stable value. - private readonly TimeSpan _decayTime = TimeSpan.FromMinutes(Utility.Random(15, 60)); - - public override TimeSpan DecayTime => _decayTime; + public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); public override int DefaultGumpID => 0x42; diff --git a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel4.cs b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel4.cs index 12639638d..a4bbd17bc 100644 --- a/Projects/UOContent/Items/TreasureChests/TreasureChestLevel4.cs +++ b/Projects/UOContent/Items/TreasureChests/TreasureChestLevel4.cs @@ -117,10 +117,7 @@ public partial class TreasureChestLevel4 : LockableContainer public override bool IsDecoContainer => false; - // Rolled once: DecayScheduler reads ScheduledDecayTime repeatedly and needs a stable value. - private readonly TimeSpan _decayTime = TimeSpan.FromMinutes(Utility.Random(15, 60)); - - public override TimeSpan DecayTime => _decayTime; + public override TimeSpan DecayTime => TimeSpan.FromMinutes(Utility.Random(15, 60)); public override int DefaultGumpID => 0x42; diff --git a/Projects/UOContent/Items/Wands/BaseWand.cs b/Projects/UOContent/Items/Wands/BaseWand.cs index 8b0098275..4a0127192 100644 --- a/Projects/UOContent/Items/Wands/BaseWand.cs +++ b/Projects/UOContent/Items/Wands/BaseWand.cs @@ -38,6 +38,7 @@ public abstract partial class BaseWand : BaseBashing public BaseWand(WandEffect effect, int minCharges, int maxCharges) : base(0xDF2 + Utility.Random(4)) { + Weight = 1.0; _wandEffect = effect; _charges = Utility.RandomMinMax(minCharges, maxCharges); Attributes.SpellChanneling = 1; @@ -46,8 +47,6 @@ public abstract partial class BaseWand : BaseBashing Resource = CraftResource.None; } - public override double DefaultWeight => 1.0; - public override WeaponAbility PrimaryAbility => WeaponAbility.Dismount; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs b/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs index 96b4a79d1..7aa4149c5 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/Disarm.cs @@ -1,6 +1,4 @@ using System; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Items { @@ -71,7 +69,7 @@ namespace Server.Items pack.DropItem(toDisarm); - (defender as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.NoRearm, 1075637, BlockEquipDuration)); + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.NoRearm, 1075637, BlockEquipDuration, defender)); BaseWeapon.BlockEquip(defender, BlockEquipDuration); } diff --git a/Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs b/Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs index 144b35f37..c846b3f60 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ForceArrow.cs @@ -1,8 +1,6 @@ using Server.Spells; using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Items { @@ -30,7 +28,7 @@ namespace Server.Items defender.Warmode = false; } - var info = GetInfo(attacker, defender); + ForceArrowInfo info = GetInfo(attacker, defender); if (info == null) { @@ -40,9 +38,8 @@ namespace Server.Items { info.Timer.IncreaseExpiration(); - (defender as PlayerMobile)?.AddBuff( - new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, args: info.DefenseChanceMalus.ToString()) - ); + BuffInfo.RemoveBuff(defender, BuffIcon.ForceArrow); + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, info.DefenseChanceMalus.ToString())); } if (defender.Spell is Spell spell && spell.IsCasting) @@ -55,7 +52,7 @@ namespace Server.Items public static void BeginForceArrow(Mobile attacker, Mobile defender) { - var info = new ForceArrowInfo(attacker, defender); + ForceArrowInfo info = new ForceArrowInfo(attacker, defender); info.Timer = new ForceArrowTimer(info); if (_table.TryGetValue(attacker, out var list)) @@ -67,9 +64,7 @@ namespace Server.Items _table.Add(attacker, new List { info }); } - (defender as PlayerMobile)?.AddBuff( - new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, args: info.DefenseChanceMalus.ToString()) - ); + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.ForceArrow, 1151285, 1151286, info.DefenseChanceMalus.ToString())); } public static void EndForceArrow(ForceArrowInfo info) @@ -79,21 +74,21 @@ namespace Server.Items return; } - var attacker = info.Attacker; + Mobile attacker = info.Attacker; if (_table.TryGetValue(attacker, out var list) && list.Remove(info) && list.Count == 0) { _table.Remove(attacker); } - (info.Defender as PlayerMobile)?.RemoveBuff(BuffIcon.ForceArrow); + BuffInfo.RemoveBuff(info.Defender, BuffIcon.ForceArrow); } public static bool HasForceArrow(Mobile attacker, Mobile defender) { if (_table.TryGetValue(attacker, out var list)) { - foreach (var info in list) + foreach (ForceArrowInfo info in list) { if (info.Defender == defender) { @@ -109,7 +104,7 @@ namespace Server.Items { if (_table.TryGetValue(attacker, out var list)) { - foreach (var info in list) + foreach (ForceArrowInfo info in list) { if (info.Defender == defender) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs b/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs index 1600342cd..f38a6fef1 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/ForceOfNature.cs @@ -73,7 +73,7 @@ public class ForceOfNature : WeaponAbility if (t.Hits % 12 == 0) { - var duration = target.Skills[SkillName.MagicResist].Value >= 90.0 ? 1 : 2; + int duration = target.Skills[SkillName.MagicResist].Value >= 90.0 ? 1 : 2; target.Paralyze(TimeSpan.FromSeconds(duration)); target.FixedEffect(0x376A, 9, 32); diff --git a/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs b/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs index 0ab3e130f..9750f5f02 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/FrenziedWhirlwind.cs @@ -37,7 +37,7 @@ namespace Server.Items attacker.Skills.Ninjitsu.Value ) - 50.0) / 70.0 + 5)); - var didEffect = false; + bool didEffect = false; foreach (var m in attacker.GetMobilesInRange(1)) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs b/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs index a5196b6ec..6e9b7df2b 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/InfectiousStrike.cs @@ -46,30 +46,25 @@ namespace Server.Items --weapon.PoisonCharges; - var poisoningSkill = attacker.Skills.Poisoning.Value; - // Infectious strike special move now uses poisoning skill to help determine potency - var family = p.Family; - - int maxLevel = family switch - { - PoisonFamily.Darkglow => Math.Min((int)(poisoningSkill / 33.3), 3), - PoisonFamily.Parasitic => Math.Min((int)(poisoningSkill / 25), 4), - _ => Math.Max((int)(poisoningSkill / 20), 0) - }; - + var maxLevel = Math.Max((int)(attacker.Skills.Poisoning.Value / 20), 0); if (p.Level > maxLevel) { - p = Poison.GetPoisonByFamilyAndLevel(family, maxLevel); + p = Poison.GetPoison(maxLevel); } - // Check if the poison can be increased. - var newPoison = Poison.IncreaseLevel(p); - if (newPoison != p && poisoningSkill / 100.0 > Utility.RandomDouble()) + if (attacker.Skills.Poisoning.Value / 100.0 > Utility.RandomDouble()) { - p = newPoison; - attacker.SendLocalizedMessage(1060080); // Your precise strike has increased the level of the poison by 1 - defender.SendLocalizedMessage(1060081); // The poison seems extra effective! + var level = p.Level + 1; + var newPoison = Poison.GetPoison(level); + + if (newPoison != null) + { + p = newPoison; + + attacker.SendLocalizedMessage(1060080); // Your precise strike has increased the level of the poison by 1 + defender.SendLocalizedMessage(1060081); // The poison seems extra effective! + } } defender.PlaySound(0xDD); diff --git a/Projects/UOContent/Items/Weapons/Abilities/InfusedThrow.cs b/Projects/UOContent/Items/Weapons/Abilities/InfusedThrow.cs index 05d9d48ae..b5c7aa60a 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/InfusedThrow.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/InfusedThrow.cs @@ -26,7 +26,7 @@ public class InfusedThrow : WeaponAbility AOS.Damage(defender, attacker, 15, false, 0, 0, 0, 0, 0, 100); - var mount = defender.Mount; + IMount mount = defender.Mount; if ((defender.Mounted || defender.Flying || AnimalForm.UnderTransformation(defender)) && !attacker.Mounted && !attacker.Flying && defender is not ChaosDragoon && defender is not ChaosDragoonElite) diff --git a/Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs b/Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs index 0a88bc9a9..5805fb406 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/LightningArrow.cs @@ -17,7 +17,7 @@ namespace Server.Items ClearCurrentAbility(attacker); - var map = attacker.Map; + Map map = attacker.Map; if (map == null || attacker.Weapon is not BaseWeapon weapon || !CheckMana(attacker, true)) { @@ -25,7 +25,7 @@ namespace Server.Items } using var list = PooledRefList.Create(); - foreach (var m in worldLocation.Map.GetMobilesInRange(worldLocation.Location, 5)) + foreach (Mobile m in worldLocation.Map.GetMobilesInRange(worldLocation.Location, 5)) { if (m != defender && m != attacker && SpellHelper.ValidIndirectTarget(attacker, m) && m is { Deleted: false, Alive: true } && attacker.CanSee(m) && attacker.CanBeHarmful(m) && diff --git a/Projects/UOContent/Items/Weapons/Abilities/MysticArc.cs b/Projects/UOContent/Items/Weapons/Abilities/MysticArc.cs index d5be5a7d6..560bafff2 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/MysticArc.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/MysticArc.cs @@ -20,7 +20,7 @@ public class MysticArc : WeaponAbility return; } - var weapon = attacker.Weapon as BaseThrown; + BaseThrown weapon = attacker.Weapon as BaseThrown; if (weapon == null) { @@ -28,7 +28,7 @@ public class MysticArc : WeaponAbility } using var queue = PooledRefQueue.Create(); - foreach (var m in attacker.GetMobilesInRange(weapon.MaxRange)) + foreach (Mobile m in attacker.GetMobilesInRange(weapon.MaxRange)) { if (m == defender) { diff --git a/Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs b/Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs index 7443fdecd..c6e084062 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/PsychicAttack.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Items { @@ -40,14 +38,10 @@ namespace Server.Items Registry.Add(defender, timer); } - (defender as PlayerMobile)?.AddBuff( - new BuffInfo( - BuffIcon.PsychicAttack, - 1151296, - 1151297, - args: $"{timer.SpellDamageMalus}\t{timer.ManaCostMalus}" - ) - ); + BuffInfo.RemoveBuff(defender, BuffIcon.PsychicAttack); + + string args = $"{timer.SpellDamageMalus}\t{timer.ManaCostMalus}"; + BuffInfo.AddBuff(defender, new BuffInfo(BuffIcon.PsychicAttack, 1151296, 1151297, args)); } public static void RemoveEffects(Mobile defender) @@ -57,7 +51,7 @@ namespace Server.Items return; } - (defender as PlayerMobile)?.RemoveBuff(BuffIcon.PsychicAttack); + BuffInfo.RemoveBuff(defender, BuffIcon.PsychicAttack); Registry.Remove(defender); diff --git a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs index 4508a16f5..819b6543b 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs @@ -240,11 +240,11 @@ public abstract class WeaponAbility } var reqSecondarySkill = GetRequiredSecondarySkill(from); - var secondarySkill = GetSecondarySkillName(from); + SkillName secondarySkill = GetSecondarySkillName(from); if (RequiresSecondarySkill(from) && from.Skills[secondarySkill].Base < reqSecondarySkill) { - var loc = GetSkillLocalization(secondarySkill); + int loc = GetSkillLocalization(secondarySkill); if (loc == 1060184) { @@ -359,7 +359,7 @@ public abstract class WeaponAbility return false; } - var option = this switch + string option = this switch { ArmorIgnore _ => "Armor Ignore", ArmorPierce _ => "Armor Pierce", diff --git a/Projects/UOContent/Items/Weapons/Axes/Axe.cs b/Projects/UOContent/Items/Weapons/Axes/Axe.cs index 84b5f0366..71f47b3b5 100644 --- a/Projects/UOContent/Items/Weapons/Axes/Axe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/Axe.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Axe : BaseAxe { [Constructible] - public Axe() : base(0xF49) - { - } - - public override double DefaultWeight => 4.0; + public Axe() : base(0xF49) => Weight = 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; diff --git a/Projects/UOContent/Items/Weapons/Axes/BaseAxe.cs b/Projects/UOContent/Items/Weapons/Axes/BaseAxe.cs index 13c41d0c4..3fb291bed 100644 --- a/Projects/UOContent/Items/Weapons/Axes/BaseAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/BaseAxe.cs @@ -73,15 +73,12 @@ namespace Server.Items return; } - if (!IsChildOf(from)) - { - var loc = GetWorldLocation(); + var loc = GetWorldLocation(); - if (!from.InLOS(loc) || !from.InRange(loc, 2)) - { - from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 1019045); // I can't reach that - return; - } + if (!from.InLOS(loc) || !from.InRange(loc, 2)) + { + from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 1019045); // I can't reach that + return; } if (!IsAccessibleTo(from)) @@ -112,7 +109,7 @@ namespace Server.Items { base.OnHit(attacker, defender, damageBonus); - if (!Core.AOS && Core.UOR && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && + if (!Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && attacker.Skills.Anatomy.Value >= 80 && attacker.Skills.Anatomy.Value / 400.0 >= Utility.RandomDouble() && DuelContext.AllowSpecialAbility(attacker, "Concussion Blow", false)) diff --git a/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs b/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs index 0fa0b7ba9..00352dbd3 100644 --- a/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class BattleAxe : BaseAxe { [Constructible] - public BattleAxe() : base(0xF47) => Layer = Layer.TwoHanded; + public BattleAxe() : base(0xF47) + { + Weight = 4.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs b/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs index 2c9ba8a9f..fea645c8e 100644 --- a/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class DoubleAxe : BaseAxe { [Constructible] - public DoubleAxe() : base(0xF4B) - { - } - - public override double DefaultWeight => 8.0; + public DoubleAxe() : base(0xF4B) => Weight = 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.WhirlwindAttack; diff --git a/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs b/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs index bfee386c1..6302a26c0 100644 --- a/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class ExecutionersAxe : BaseAxe { [Constructible] - public ExecutionersAxe() : base(0xF45) - { - } - - public override double DefaultWeight => 8.0; + public ExecutionersAxe() : base(0xF45) => Weight = 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs b/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs index 4509e28e1..924a54891 100644 --- a/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs +++ b/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Hatchet : BaseAxe { [Constructible] - public Hatchet() : base(0xF43) - { - } - - public override double DefaultWeight => 4.0; + public Hatchet() : base(0xF43) => Weight = 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs b/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs index 613af57cc..37f6668f6 100644 --- a/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class LargeBattleAxe : BaseAxe { [Constructible] - public LargeBattleAxe() : base(0x13FB) - { - } - - public override double DefaultWeight => 6.0; + public LargeBattleAxe() : base(0x13FB) => Weight = 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.BleedAttack; diff --git a/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs b/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs index b78851d78..07cac1b08 100644 --- a/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs @@ -1,6 +1,4 @@ -using System; using ModernUO.Serialization; -using Server.Engines.Craft; using Server.Engines.Harvest; namespace Server.Items @@ -12,11 +10,11 @@ namespace Server.Items [Constructible] public Pickaxe() : base(0xE86) { + Weight = 11.0; UsesRemaining = 50; ShowUsesRemaining = true; } - public override double DefaultWeight => 11.0; public override HarvestSystem HarvestSystem => Mining.System; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; @@ -37,27 +35,5 @@ namespace Server.Items public override int InitMaxHits => 60; public override WeaponAnimation DefAnimation => WeaponAnimation.Slash1H; - - public override int OnCraft( - int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, - CraftItem craftItem, int resHue - ) - { - var result = base.OnCraft(quality, makersMark, from, craftSystem, typeRes, tool, craftItem, resHue); - - if (Core.UOR) - { - if (Quality == WeaponQuality.Exceptional) - { - UsesRemaining += 50; - } - } - else if (Quality != WeaponQuality.Regular) - { - UsesRemaining += (int)(UsesRemaining * ((int)Quality - 1) * 0.2); - } - - return result; - } } } diff --git a/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs b/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs index b1ea91e5e..1a87091b2 100644 --- a/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class TwoHandedAxe : BaseAxe { [Constructible] - public TwoHandedAxe() : base(0x1443) - { - } - - public override double DefaultWeight => 8.0; + public TwoHandedAxe() : base(0x1443) => Weight = 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ShadowStrike; diff --git a/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs b/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs index 5c006da82..b11db81e0 100644 --- a/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs @@ -8,11 +8,7 @@ namespace Server.Items public partial class WarAxe : BaseAxe { [Constructible] - public WarAxe() : base(0x13B0) - { - } - - public override double DefaultWeight => 8.0; + public WarAxe() : base(0x13B0) => Weight = 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.BleedAttack; diff --git a/Projects/UOContent/Items/Weapons/BaseMeleeWeapon.cs b/Projects/UOContent/Items/Weapons/BaseMeleeWeapon.cs index 4be1c5035..4df9ca877 100644 --- a/Projects/UOContent/Items/Weapons/BaseMeleeWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseMeleeWeapon.cs @@ -1,4 +1,3 @@ -using Server.Spells.First; using Server.Spells.Spellweaving; namespace Server.Items @@ -24,7 +23,34 @@ namespace Server.Items return damage; } - ReactiveArmorSpell.HandleMeleeHit(attacker, defender, ref damage); + var absorb = defender.MeleeDamageAbsorb; + + if (absorb > 0) + { + if (absorb > damage) + { + var react = damage / 5; + + if (react <= 0) + { + react = 1; + } + + defender.MeleeDamageAbsorb -= damage; + damage = 0; + + attacker.Damage(react, defender); + + attacker.PlaySound(0x1F1); + attacker.FixedEffect(0x374A, 10, 16); + } + else + { + defender.MeleeDamageAbsorb = 0; + defender.SendLocalizedMessage(1005556); // Your reactive armor spell has been nullified. + DefensiveSpell.Nullify(defender); + } + } return damage; } diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.Migrations.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.Migrations.cs deleted file mode 100644 index 3385598d9..000000000 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.Migrations.cs +++ /dev/null @@ -1,311 +0,0 @@ -using System; -using Server.Engines.Craft; - -namespace Server.Items; - -public partial class BaseWeapon -{ - // PlayerConstructed moved onto Item - private void MigrateFrom(V10Content content) - { - _damageLevel = content.DamageLevel ?? WeaponDamageLevel.Regular; - _accuracyLevel = content.AccuracyLevel ?? WeaponAccuracyLevel.Regular; - _durabilityLevel = content.DurabilityLevel ?? WeaponDurabilityLevel.Regular; - _quality = content.Quality ?? WeaponQuality.Regular; - _hitPoints = content.HitPoints ?? 0; - _maxHitPoints = content.MaxHitPoints ?? 0; - _slayer = content.Slayer ?? SlayerName.None; - _poison = content.Poison; - _poisonCharges = content.PoisonCharges ?? 0; - _crafter = content.Crafter; - _identified = content.Identified; - _strRequirement = content.StrRequirement ?? -1; - _dexRequirement = content.DexRequirement ?? -1; - _intRequirement = content.IntRequirement ?? -1; - _minDamage = content.MinDamage ?? -1; - _maxDamage = content.MaxDamage ?? -1; - _hitSound = content.HitSound ?? -1; - _missSound = content.MissSound ?? -1; - _speed = content.Speed ?? -1; - _maxRange = content.MaxRange ?? -1; - _skill = content.Skill ?? (SkillName)(-1); - _type = content.Type ?? (WeaponType)(-1); - _animation = content.Animation ?? (WeaponAnimation)(-1); - _resource = content.Resource ?? CraftResource.Iron; - _attributes = content.Attributes ?? AttributesDefaultValue(); - _weaponAttributes = content.WeaponAttributes ?? WeaponAttributesDefaultValue(); - PlayerConstructed = content.PlayerConstructed; - _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); - _slayer2 = content.Slayer2 ?? SlayerName.None; - _aosElementDamages = content.AosElementDamages ?? AosElementAttributesDefaultValue(); - _engravedText = content.EngravedText; - } - - // Version 9 (pre-codegen) - private void Deserialize(IGenericReader reader, int version) - { - var flags = (OldSaveFlag)reader.ReadInt(); - - if (GetSaveFlag(flags, OldSaveFlag.DamageLevel)) - { - _damageLevel = (WeaponDamageLevel)reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.AccuracyLevel)) - { - _accuracyLevel = (WeaponAccuracyLevel)reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.DurabilityLevel)) - { - _durabilityLevel = (WeaponDurabilityLevel)reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Quality)) - { - _quality = (WeaponQuality)reader.ReadInt(); - } - else - { - _quality = WeaponQuality.Regular; - } - - if (GetSaveFlag(flags, OldSaveFlag.Hits)) - { - _hitPoints = reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.MaxHits)) - { - _maxHitPoints = reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Slayer)) - { - _slayer = (SlayerName)reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Poison)) - { - _poison = reader.ReadPoison(); - } - - if (GetSaveFlag(flags, OldSaveFlag.PoisonCharges)) - { - _poisonCharges = reader.ReadInt(); - } - - if (GetSaveFlag(flags, OldSaveFlag.Crafter)) - { - Timer.DelayCall(crafter => _crafter = crafter?.RawName, reader.ReadEntity()); - } - - if (GetSaveFlag(flags, OldSaveFlag.Identified)) - { - _identified = version >= 6 || reader.ReadBool(); - } - - if (GetSaveFlag(flags, OldSaveFlag.StrReq)) - { - _strRequirement = reader.ReadInt(); - } - else - { - _strRequirement = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.DexReq)) - { - _dexRequirement = reader.ReadInt(); - } - else - { - _dexRequirement = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.IntReq)) - { - _intRequirement = reader.ReadInt(); - } - else - { - _intRequirement = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.MinDamage)) - { - _minDamage = reader.ReadInt(); - } - else - { - _minDamage = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.MaxDamage)) - { - _maxDamage = reader.ReadInt(); - } - else - { - _maxDamage = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.HitSound)) - { - _hitSound = reader.ReadInt(); - } - else - { - _hitSound = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.MissSound)) - { - _missSound = reader.ReadInt(); - } - else - { - _missSound = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.Speed)) - { - if (version < 9) - { - _speed = reader.ReadInt(); - } - else - { - _speed = reader.ReadFloat(); - } - } - else - { - _speed = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.MaxRange)) - { - _maxRange = reader.ReadInt(); - } - else - { - _maxRange = -1; - } - - if (GetSaveFlag(flags, OldSaveFlag.Skill)) - { - _skill = (SkillName)reader.ReadInt(); - } - else - { - _skill = (SkillName)(-1); - } - - if (GetSaveFlag(flags, OldSaveFlag.Type)) - { - _type = (WeaponType)reader.ReadInt(); - } - else - { - _type = (WeaponType)(-1); - } - - if (GetSaveFlag(flags, OldSaveFlag.Animation)) - { - _animation = (WeaponAnimation)reader.ReadInt(); - } - else - { - _animation = (WeaponAnimation)(-1); - } - - if (GetSaveFlag(flags, OldSaveFlag.Resource)) - { - _resource = (CraftResource)reader.ReadInt(); - } - else - { - _resource = CraftResource.Iron; - } - - Attributes = new AosAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.Attributes)) - { - Attributes.Deserialize(reader); - } - - WeaponAttributes = new AosWeaponAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.WeaponAttributes)) - { - WeaponAttributes.Deserialize(reader); - } - - PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); - - SkillBonuses = new AosSkillBonuses(this); - - if (GetSaveFlag(flags, OldSaveFlag.SkillBonuses)) - { - SkillBonuses.Deserialize(reader); - } - - if (GetSaveFlag(flags, OldSaveFlag.Slayer2)) - { - _slayer2 = (SlayerName)reader.ReadInt(); - } - - AosElementDamages = new AosElementAttributes(this); - - if (GetSaveFlag(flags, OldSaveFlag.ElementalDamages)) - { - AosElementDamages.Deserialize(reader); - } - - if (GetSaveFlag(flags, OldSaveFlag.EngravedText)) - { - _engravedText = reader.ReadString(); - } - } - - private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; - - [Flags] - private enum OldSaveFlag - { - None = 0x00000000, - DamageLevel = 0x00000001, - AccuracyLevel = 0x00000002, - DurabilityLevel = 0x00000004, - Quality = 0x00000008, - Hits = 0x00000010, - MaxHits = 0x00000020, - Slayer = 0x00000040, - Poison = 0x00000080, - PoisonCharges = 0x00000100, - Crafter = 0x00000200, - Identified = 0x00000400, - StrReq = 0x00000800, - DexReq = 0x00001000, - IntReq = 0x00002000, - MinDamage = 0x00004000, - MaxDamage = 0x00008000, - HitSound = 0x00010000, - MissSound = 0x00020000, - Speed = 0x00040000, - MaxRange = 0x00080000, - Skill = 0x00100000, - Type = 0x00200000, - Animation = 0x00400000, - Resource = 0x00800000, - Attributes = 0x01000000, - WeaponAttributes = 0x02000000, - PlayerConstructed = 0x04000000, - SkillBonuses = 0x08000000, - Slayer2 = 0x10000000, - ElementalDamages = 0x20000000, - EngravedText = 0x40000000 - } -} diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index d48824670..66b54cb30 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -27,7 +27,7 @@ public interface ISlayer SlayerName Slayer2 { get; set; } } -[SerializationGenerator(11, false)] +[SerializationGenerator(10, false)] public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftable, ISlayer, IDurability, IAosItem, IIdentifiable { @@ -56,128 +56,140 @@ public abstract partial class BaseWeapon [InvalidateProperties] [SerializableField(0)] - [SaveFlag(nameof(ShouldSerializeDamageLevel))] [SerializedCommandProperty(AccessLevel.GameMaster)] private WeaponDamageLevel _damageLevel; + [SerializableFieldSaveFlag(0)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeDamageLevel() => _damageLevel != WeaponDamageLevel.Regular; [InvalidateProperties] [SerializableField(5)] - [SaveFlag(nameof(ShouldSerializeMaxHitPoints))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _maxHitPoints; + [SerializableFieldSaveFlag(5)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeMaxHitPoints() => _maxHitPoints != 0; [InvalidateProperties] [SerializableField(6)] - [SaveFlag(nameof(ShouldSerializeSlayer))] [SerializedCommandProperty(AccessLevel.GameMaster)] private SlayerName _slayer; + [SerializableFieldSaveFlag(6)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeSlayer() => _slayer != SlayerName.None; [InvalidateProperties] [SerializableField(7)] - [SaveFlag(nameof(ShouldSerializePoison))] [SerializedCommandProperty(AccessLevel.GameMaster)] private Poison _poison; + [SerializableFieldSaveFlag(7)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool ShouldSerializePoison() => _poison != null; + private bool ShouldSerializePoison() => _poison?.Level > 0; [InvalidateProperties] [SerializableField(8)] - [SaveFlag(nameof(ShouldSerializePoisonCharges))] [SerializedCommandProperty(AccessLevel.GameMaster)] private int _poisonCharges; + [SerializableFieldSaveFlag(8)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializePoisonCharges() => _poisonCharges > 0; [InvalidateProperties] [SerializableField(9)] - [SaveFlag(nameof(ShouldSerializeCrafter))] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _crafter; + [SerializableFieldSaveFlag(9)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeCrafter() => !string.IsNullOrEmpty(_crafter); [InvalidateProperties] [SerializableField(10)] - [SaveFlag(nameof(ShouldSerializeIdentified))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _identified; + [SerializableFieldSaveFlag(10)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeIdentified() => _identified; [SerializedIgnoreDupe] [SerializableField(24, setter: "private")] - [SaveFlag(nameof(ShouldSerializeAttributes), nameof(AttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosAttributes _attributes; + [SerializableFieldSaveFlag(24)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeAttributes() => !_attributes.IsEmpty; + [SerializableFieldDefault(24)] private AosAttributes AttributesDefaultValue() => new(this); [SerializedIgnoreDupe] [SerializableField(25, setter: "private")] - [SaveFlag(nameof(ShouldSerializeWeaponAttributes), nameof(WeaponAttributesDefaultValue))] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosWeaponAttributes _weaponAttributes; + [SerializableFieldSaveFlag(25)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeWeaponAttributes() => !_weaponAttributes.IsEmpty; + [SerializableFieldDefault(25)] private AosWeaponAttributes WeaponAttributesDefaultValue() => new(this); + [SerializableField(26)] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private bool _playerConstructed; + + [SerializableFieldSaveFlag(26)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool ShouldSerializePlayerConstructed() => _playerConstructed; + [SerializedIgnoreDupe] - [SerializableField(26, setter: "private")] - [SaveFlag(nameof(ShouldSerializeSkillBonuses), nameof(SkillBonusesDefaultValue))] + [SerializableField(27, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosSkillBonuses _skillBonuses; + [SerializableFieldSaveFlag(27)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeSkillBonuses() => !_skillBonuses.IsEmpty; + [SerializableFieldDefault(27)] private AosSkillBonuses SkillBonusesDefaultValue() => new(this); [InvalidateProperties] - [SerializableField(27)] - [SaveFlag(nameof(ShouldSerializeSlayer2))] + [SerializableField(28)] [SerializedCommandProperty(AccessLevel.GameMaster)] private SlayerName _slayer2; + [SerializableFieldSaveFlag(28)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeSlayer2() => _slayer2 != SlayerName.None; [SerializedIgnoreDupe] - [SerializableField(28, setter: "private")] - [SaveFlag(nameof(ShouldSerializeElementAttributes), nameof(AosElementAttributesDefaultValue))] + [SerializableField(29, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] private AosElementAttributes _aosElementDamages; + [SerializableFieldSaveFlag(29)] [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeElementAttributes() => !_aosElementDamages.IsEmpty; + [SerializableFieldDefault(29)] private AosElementAttributes AosElementAttributesDefaultValue() => new(this); [InvalidateProperties] - [SerializableField(29)] - [SaveFlag(nameof(ShouldSerializeEngravedText))] + [SerializableField(30)] [SerializedCommandProperty(AccessLevel.GameMaster)] private string _engravedText; + [SerializableFieldSaveFlag(30)] [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool ShouldSerializeEngravedText() => !string.IsNullOrEmpty(_engravedText); + private bool ShouldSerializeEngravedText() => string.IsNullOrEmpty(_engravedText); private FactionItem m_FactionState; private SkillMod m_SkillMod, m_MageMod; @@ -282,7 +294,6 @@ public abstract partial class BaseWeapon public bool Consecrated { get; set; } [SerializableProperty(1)] - [SaveFlag(nameof(ShouldSerializeWeaponAccuracy))] [CommandProperty(AccessLevel.GameMaster)] public WeaponAccuracyLevel AccuracyLevel { @@ -318,10 +329,10 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(1)] private bool ShouldSerializeWeaponAccuracy() => _accuracyLevel != WeaponAccuracyLevel.Regular; [SerializableProperty(2)] - [SaveFlag(nameof(ShouldSerializeDurabilityLevel))] [CommandProperty(AccessLevel.GameMaster)] public WeaponDurabilityLevel DurabilityLevel { @@ -336,10 +347,10 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(2)] private bool ShouldSerializeDurabilityLevel() => _durabilityLevel != WeaponDurabilityLevel.Regular; [SerializableProperty(3)] - [SaveFlag(nameof(ShouldSerializeQuality), nameof(QualityDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public WeaponQuality Quality { @@ -354,12 +365,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(3)] private bool ShouldSerializeQuality() => _quality != WeaponQuality.Regular; + [SerializableFieldDefault(3)] private WeaponQuality QualityDefaultValue() => WeaponQuality.Regular; [SerializableProperty(4)] - [SaveFlag(nameof(ShouldSerializeHitPoints))] [CommandProperty(AccessLevel.GameMaster)] public int HitPoints { @@ -383,10 +395,10 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(4)] private bool ShouldSerializeHitPoints() => _hitPoints > 0; [SerializableProperty(11)] - [SaveFlag(nameof(ShouldSerializeStrReq), nameof(StrReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int StrRequirement { @@ -399,12 +411,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(11)] private bool ShouldSerializeStrReq() => _strRequirement != -1; + [SerializableFieldDefault(11)] private int StrReqDefaultValue() => -1; [SerializableProperty(12)] - [SaveFlag(nameof(ShouldSerializeDexReq), nameof(DexReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int DexRequirement { @@ -417,12 +430,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(12)] private bool ShouldSerializeDexReq() => _dexRequirement != -1; + [SerializableFieldDefault(12)] private int DexReqDefaultValue() => -1; [SerializableProperty(13)] - [SaveFlag(nameof(ShouldSerializeIntReq), nameof(IntReqDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int IntRequirement { @@ -435,12 +449,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(13)] private bool ShouldSerializeIntReq() => _intRequirement != -1; + [SerializableFieldDefault(13)] private int IntReqDefaultValue() => -1; [SerializableProperty(14)] - [SaveFlag(nameof(ShouldSerializeMinDamage), nameof(MinDamageDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int MinDamage { @@ -453,12 +468,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(14)] private bool ShouldSerializeMinDamage() => _minDamage != -1; + [SerializableFieldDefault(14)] private int MinDamageDefaultValue() => -1; [SerializableProperty(15)] - [SaveFlag(nameof(ShouldSerializeMaxDamage), nameof(MaxDamageDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int MaxDamage { @@ -471,12 +487,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(15)] private bool ShouldSerializeMaxDamage() => _maxDamage != -1; + [SerializableFieldDefault(15)] private int MaxDamageDefaultValue() => -1; [SerializableProperty(16)] - [SaveFlag(nameof(ShouldSerializeHitSound), nameof(HitSoundDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int HitSound { @@ -488,12 +505,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(16)] private bool ShouldSerializeHitSound() => _hitSound != -1; + [SerializableFieldDefault(16)] private int HitSoundDefaultValue() => -1; [SerializableProperty(17)] - [SaveFlag(nameof(ShouldSerializeMissSound), nameof(MissSoundDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int MissSound { @@ -505,12 +523,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(17)] private bool ShouldSerializeMissSound() => _missSound != -1; + [SerializableFieldDefault(17)] private int MissSoundDefaultValue() => -1; [SerializableProperty(18)] - [SaveFlag(nameof(ShouldSerializeSpeed), nameof(SpeedDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public float Speed { @@ -541,12 +560,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(18)] private bool ShouldSerializeSpeed() => _speed != -1; + [SerializableFieldDefault(18)] private float SpeedDefaultValue() => -1; [SerializableProperty(19)] - [SaveFlag(nameof(ShouldSerializeMaxRange), nameof(MaxRangeDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public int MaxRange { @@ -559,12 +579,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(19)] private bool ShouldSerializeMaxRange() => _maxRange != -1; + [SerializableFieldDefault(19)] private int MaxRangeDefaultValue() => -1; [SerializableProperty(20)] - [SaveFlag(nameof(ShouldSerializeSkill), nameof(SkillNameDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public SkillName Skill { @@ -577,12 +598,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(20)] private bool ShouldSerializeSkill() => _skill != (SkillName)(-1); + [SerializableFieldDefault(20)] private SkillName SkillNameDefaultValue() => (SkillName)(-1); [SerializableProperty(21)] - [SaveFlag(nameof(ShouldSerializeType), nameof(TypeDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public WeaponType Type { @@ -594,12 +616,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(21)] private bool ShouldSerializeType() => _type != (WeaponType)(-1); + [SerializableFieldDefault(21)] private WeaponType TypeDefaultValue() => (WeaponType)(-1); [SerializableProperty(22)] - [SaveFlag(nameof(ShouldSerializeAnimation), nameof(AnimationDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public WeaponAnimation Animation { @@ -611,12 +634,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(22)] private bool ShouldSerializeAnimation() => _animation != (WeaponAnimation)(-1); + [SerializableFieldDefault(22)] private WeaponAnimation AnimationDefaultValue() => (WeaponAnimation)(-1); [SerializableProperty(23)] - [SaveFlag(nameof(ShouldSerializeResource), nameof(ResourceDefaultValue))] [CommandProperty(AccessLevel.GameMaster)] public CraftResource Resource { @@ -632,11 +656,13 @@ public abstract partial class BaseWeapon } } + [SerializableFieldSaveFlag(23)] private bool ShouldSerializeResource() => _resource != CraftResource.Iron; + [SerializableFieldDefault(23)] private CraftResource ResourceDefaultValue() => CraftResource.Iron; - public virtual int OnCraft( + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) @@ -648,6 +674,7 @@ public abstract partial class BaseWeapon Crafter = from.RawName; } + PlayerConstructed = true; Identified = true; var resourceType = typeRes ?? craftItem.Resources[0].ItemType; @@ -863,11 +890,6 @@ public abstract partial class BaseWeapon if (attacker is BaseCreature bc) { - if (bc.TriggerAbility(MonsterAbilityTrigger.CombatAction, defender)) - { - return GetDelay(attacker); - } - // Only change direction if they are not a player. attacker.Direction = attacker.GetDirectionTo(defender); var ab = bc.GetWeaponAbility(); @@ -919,12 +941,9 @@ public abstract partial class BaseWeapon public int GetDurabilityBonus() { - if (!Core.UOR) - { - return (int)_durabilityLevel * 5 + ((int)_quality - 1) * 10; - } + var bonus = _quality == WeaponQuality.Exceptional ? 20 : 0; - var bonus = _durabilityLevel switch + bonus += _durabilityLevel switch { WeaponDurabilityLevel.Durable => 20, WeaponDurabilityLevel.Substantial => 50, @@ -936,11 +955,23 @@ public abstract partial class BaseWeapon if (Core.AOS) { + bonus += WeaponAttributes.DurabilityBonus; + var resInfo = CraftResources.GetInfo(_resource); - bonus += WeaponAttributes.DurabilityBonus + (resInfo?.AttributeInfo?.WeaponDurability ?? 0); + CraftAttributeInfo attrInfo = null; + + if (resInfo != null) + { + attrInfo = resInfo.AttributeInfo; + } + + if (attrInfo != null) + { + bonus += attrInfo.WeaponDurability; + } } - return _quality == WeaponQuality.Exceptional ? bonus + 20 : bonus; + return bonus; } public int GetLowerStatReq() @@ -1004,11 +1035,6 @@ public abstract partial class BaseWeapon public override bool CanEquip(Mobile from) { - if (!from.Player || from.AccessLevel >= AccessLevel.GameMaster) - { - return from.CanBeginAction() && base.CanEquip(from); - } - if (!Ethic.CheckEquip(from, this)) { return false; @@ -1330,8 +1356,6 @@ public abstract partial class BaseWeapon var chance = ourValue / (theirValue * 2.0) * 1.0 + (double)bonus / 100; - chance = ModifyHitChance(attacker, defender, chance); - if (Core.AOS && chance < 0.02) { chance = 0.02; @@ -1340,19 +1364,6 @@ public abstract partial class BaseWeapon return attacker.CheckSkill(atkSkill.SkillName, chance); } - /// - /// Allows subclasses to modify the final hit chance before the dice roll. - /// Called after all standard AOS bonuses are applied but before the 2% floor. - /// Supports both additive adjustments (chance -= 0.12) and multiplicative ones (chance *= scalar). - /// - protected virtual double ModifyHitChance(Mobile attacker, Mobile defender, double chance) => chance; - - /// - /// Allows subclasses to modify the final post-bonus damage (e.g. range-based penalties) - /// before defender mitigation is applied. Mirrors . - /// - protected virtual int ModifyDamage(Mobile attacker, Mobile defender, int damage) => damage; - public virtual TimeSpan GetDelay(Mobile m) { double speed = Speed; @@ -1621,7 +1632,7 @@ public abstract partial class BaseWeapon { var positionChance = Utility.RandomDouble(); - var armorItem = positionChance switch + Item armorItem = positionChance switch { < 0.07 => defender.NeckArmor, < 0.14 => defender.HandArmor, @@ -1654,7 +1665,7 @@ public abstract partial class BaseWeapon var chance = Utility.RandomDouble(); - var armorItem = chance switch + Item armorItem = chance switch { < 0.07 => defender.NeckArmor, < 0.14 => defender.HandArmor, @@ -1673,7 +1684,7 @@ public abstract partial class BaseWeapon if (virtualArmor > 0) { - var scalar = chance switch + double scalar = chance switch { < 0.14 => 0.07, < 0.28 => 0.14, @@ -1862,7 +1873,6 @@ public abstract partial class BaseWeapon percentageBonus = Math.Min(percentageBonus, 300); damage = AOS.Scale(damage, 100 + percentageBonus); - damage = ModifyDamage(attacker, defender, damage); var defLoc = new WorldLocation(defender); var bcAtt = attacker as BaseCreature; @@ -2474,15 +2484,7 @@ public abstract partial class BaseWeapon */ if (_damageLevel != WeaponDamageLevel.Regular) { - // Toward the end of T2A, calculations were changed - if (!Core.T2A) - { - damage += (int)_damageLevel; - } - else - { - damage += 2 * (int)_damageLevel - 1; - } + damage += 2 * (int)_damageLevel - 1; } return damage; @@ -2604,7 +2606,7 @@ public abstract partial class BaseWeapon // Passively check Anatomy for gain attacker.CheckSkill(SkillName.Anatomy, 0.0, attacker.Skills.Anatomy.Cap); - if (Core.UOR && Type == WeaponType.Axe) + if (Type == WeaponType.Axe) { // Passively check Lumberjacking for gain attacker.CheckSkill(SkillName.Lumberjacking, 0.0, 100.0); @@ -2625,27 +2627,27 @@ public abstract partial class BaseWeapon /* Compute anatomy modifier * : 1% bonus for every 5 points of anatomy - * : +10% bonus at Grandmaster or higher (UOTD+) + * : +10% bonus at Grandmaster or higher */ var anatomyValue = attacker.Skills.Anatomy.Value; modifiers += anatomyValue / 5.0 / 100.0; - if (Core.UOTD && anatomyValue >= 100.0) + if (anatomyValue >= 100.0) { modifiers += 0.1; } - /* Compute lumberjacking bonus (UOR+) + /* Compute lumberjacking bonus * : 1% bonus for every 5 points of lumberjacking - * : +10% bonus at Grandmaster or higher (UOTD+) + * : +10% bonus at Grandmaster or higher */ - if (Core.UOR && Type == WeaponType.Axe) + if (Type == WeaponType.Axe) { var lumberValue = attacker.Skills.Lumberjacking.Value; modifiers += lumberValue / 5.0 / 100.0; - if (Core.UOTD && lumberValue >= 100.0) + if (lumberValue >= 100.0) { modifiers += 0.1; } @@ -2987,7 +2989,115 @@ public abstract partial class BaseWeapon list.Add(1072792); // Balanced } - WeaponAttributes.GetProperties(list); + if (WeaponAttributes.UseBestSkill != 0) + { + list.Add(1060400); // use best weapon skill + } + + if ((prop = GetDamageBonus() + Attributes.WeaponDamage) != 0) + { + list.Add(1060401, prop); // damage increase ~1_val~% + } + + if ((prop = Attributes.DefendChance) != 0) + { + list.Add(1060408, prop); // defense chance increase ~1_val~% + } + + if ((prop = Attributes.EnhancePotions) != 0) + { + list.Add(1060411, prop); // enhance potions ~1_val~% + } + + if ((prop = Attributes.CastRecovery) != 0) + { + list.Add(1060412, prop); // faster cast recovery ~1_val~ + } + + if ((prop = Attributes.CastSpeed) != 0) + { + list.Add(1060413, prop); // faster casting ~1_val~ + } + + if ((prop = GetHitChanceBonus() + Attributes.AttackChance) != 0) + { + list.Add(1060415, prop); // hit chance increase ~1_val~% + } + + if ((prop = WeaponAttributes.HitColdArea) != 0) + { + list.Add(1060416, prop); // hit cold area ~1_val~% + } + + if ((prop = WeaponAttributes.HitDispel) != 0) + { + list.Add(1060417, prop); // hit dispel ~1_val~% + } + + if ((prop = WeaponAttributes.HitEnergyArea) != 0) + { + list.Add(1060418, prop); // hit energy area ~1_val~% + } + + if ((prop = WeaponAttributes.HitFireArea) != 0) + { + list.Add(1060419, prop); // hit fire area ~1_val~% + } + + if ((prop = WeaponAttributes.HitFireball) != 0) + { + list.Add(1060420, prop); // hit fireball ~1_val~% + } + + if ((prop = WeaponAttributes.HitHarm) != 0) + { + list.Add(1060421, prop); // hit harm ~1_val~% + } + + if ((prop = WeaponAttributes.HitLeechHits) != 0) + { + list.Add(1060422, prop); // hit life leech ~1_val~% + } + + if ((prop = WeaponAttributes.HitLightning) != 0) + { + list.Add(1060423, prop); // hit lightning ~1_val~% + } + + if ((prop = WeaponAttributes.HitLowerAttack) != 0) + { + list.Add(1060424, prop); // hit lower attack ~1_val~% + } + + if ((prop = WeaponAttributes.HitLowerDefend) != 0) + { + list.Add(1060425, prop); // hit lower defense ~1_val~% + } + + if ((prop = WeaponAttributes.HitMagicArrow) != 0) + { + list.Add(1060426, prop); // hit magic arrow ~1_val~% + } + + if ((prop = WeaponAttributes.HitLeechMana) != 0) + { + list.Add(1060427, prop); // hit mana leech ~1_val~% + } + + if ((prop = WeaponAttributes.HitPhysicalArea) != 0) + { + list.Add(1060428, prop); // hit physical area ~1_val~% + } + + if ((prop = WeaponAttributes.HitPoisonArea) != 0) + { + list.Add(1060429, prop); // hit poison area ~1_val~% + } + + if ((prop = WeaponAttributes.HitLeechStam) != 0) + { + list.Add(1060430, prop); // hit stamina leech ~1_val~% + } if (ImmolatingWeaponSpell.IsImmolating(this)) { @@ -2999,17 +3109,109 @@ public abstract partial class BaseWeapon list.Add(1072793, prop); // Velocity ~1_val~% } - Attributes.GetProperties( - list, - damageBonus: GetDamageBonus(), - hitChanceBonus: GetHitChanceBonus(), - luckBonus: GetLuckBonus() - ); - - var lowerStatReq = GetLowerStatReq(); - if (lowerStatReq != 0) + if ((prop = Attributes.BonusDex) != 0) { - list.Add(1060435, lowerStatReq); // lower requirements ~1_val~% + list.Add(1060409, prop); // dexterity bonus ~1_val~ + } + + if ((prop = Attributes.BonusHits) != 0) + { + list.Add(1060431, prop); // hit point increase ~1_val~ + } + + if ((prop = Attributes.BonusInt) != 0) + { + list.Add(1060432, prop); // intelligence bonus ~1_val~ + } + + if ((prop = Attributes.LowerManaCost) != 0) + { + list.Add(1060433, prop); // lower mana cost ~1_val~% + } + + if ((prop = Attributes.LowerRegCost) != 0) + { + list.Add(1060434, prop); // lower reagent cost ~1_val~% + } + + if ((prop = GetLowerStatReq()) != 0) + { + list.Add(1060435, prop); // lower requirements ~1_val~% + } + + if ((prop = GetLuckBonus() + Attributes.Luck) != 0) + { + list.Add(1060436, prop); // luck ~1_val~ + } + + if ((prop = WeaponAttributes.MageWeapon) != 0) + { + list.Add(1060438, 30 - prop); // mage weapon -~1_val~ skill + } + + if ((prop = Attributes.BonusMana) != 0) + { + list.Add(1060439, prop); // mana increase ~1_val~ + } + + if ((prop = Attributes.RegenMana) != 0) + { + list.Add(1060440, prop); // mana regeneration ~1_val~ + } + + if (Attributes.NightSight != 0) + { + list.Add(1060441); // night sight + } + + if ((prop = Attributes.ReflectPhysical) != 0) + { + list.Add(1060442, prop); // reflect physical damage ~1_val~% + } + + if ((prop = Attributes.RegenStam) != 0) + { + list.Add(1060443, prop); // stamina regeneration ~1_val~ + } + + if ((prop = Attributes.RegenHits) != 0) + { + list.Add(1060444, prop); // hit point regeneration ~1_val~ + } + + if ((prop = WeaponAttributes.SelfRepair) != 0) + { + list.Add(1060450, prop); // self repair ~1_val~ + } + + if (Attributes.SpellChanneling != 0) + { + list.Add(1060482); // spell channeling + } + + if ((prop = Attributes.SpellDamage) != 0) + { + list.Add(1060483, prop); // spell damage increase ~1_val~% + } + + if ((prop = Attributes.BonusStam) != 0) + { + list.Add(1060484, prop); // stamina increase ~1_val~ + } + + if ((prop = Attributes.BonusStr) != 0) + { + list.Add(1060485, prop); // strength bonus ~1_val~ + } + + if ((prop = Attributes.WeaponSpeed) != 0) + { + list.Add(1060486, prop); // swing speed increase ~1_val~% + } + + if (Core.ML && (prop = Attributes.IncreasedKarmaLoss) != 0) + { + list.Add(1075210, prop); // Increased Karma Loss ~1val~% } GetDamageTypes( @@ -3028,16 +3230,16 @@ public abstract partial class BaseWeapon list.Add(1060403, phys); // physical damage ~1_val~% } - if (cold != 0) - { - list.Add(1060404, cold); // cold damage ~1_val~% - } - if (fire != 0) { list.Add(1060405, fire); // fire damage ~1_val~% } + if (cold != 0) + { + list.Add(1060404, cold); // cold damage ~1_val~% + } + if (pois != 0) { list.Add(1060406, pois); // poison damage ~1_val~% @@ -3114,11 +3316,6 @@ public abstract partial class BaseWeapon list.Add(1061175); // skill required: archery break; } - case SkillName.Throwing: - { - list.Add(1112075); // skill required: throwing - break; - } } } @@ -3245,7 +3442,7 @@ public abstract partial class BaseWeapon if (isMagicItem) { - var builder = new ValueStringBuilder(stackalloc char[160]); + var builder = ValueStringBuilder.Create(128); var durabilityText = DurabilityText(out var articleAnDurability); if (durabilityText != null) @@ -3461,7 +3658,7 @@ public abstract partial class BaseWeapon attacker.DoHarmful(defender); - var sp = new DispelSpell(attacker); + MagerySpell sp = new DispelSpell(attacker); if (sp.CheckResisted(defender)) { @@ -3553,6 +3750,236 @@ public abstract partial class BaseWeapon } } + private static bool GetSaveFlag(OldSaveFlag flags, OldSaveFlag toGet) => (flags & toGet) != 0; + + private void Deserialize(IGenericReader reader, int version) + { + var flags = (OldSaveFlag)reader.ReadInt(); + + if (GetSaveFlag(flags, OldSaveFlag.DamageLevel)) + { + _damageLevel = (WeaponDamageLevel)reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.AccuracyLevel)) + { + _accuracyLevel = (WeaponAccuracyLevel)reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.DurabilityLevel)) + { + _durabilityLevel = (WeaponDurabilityLevel)reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Quality)) + { + _quality = (WeaponQuality)reader.ReadInt(); + } + else + { + _quality = WeaponQuality.Regular; + } + + if (GetSaveFlag(flags, OldSaveFlag.Hits)) + { + _hitPoints = reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.MaxHits)) + { + _maxHitPoints = reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Slayer)) + { + _slayer = (SlayerName)reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Poison)) + { + _poison = reader.ReadPoison(); + } + + if (GetSaveFlag(flags, OldSaveFlag.PoisonCharges)) + { + _poisonCharges = reader.ReadInt(); + } + + if (GetSaveFlag(flags, OldSaveFlag.Crafter)) + { + Timer.DelayCall(crafter => _crafter = crafter?.RawName, reader.ReadEntity()); + } + + if (GetSaveFlag(flags, OldSaveFlag.Identified)) + { + _identified = version >= 6 || reader.ReadBool(); + } + + if (GetSaveFlag(flags, OldSaveFlag.StrReq)) + { + _strRequirement = reader.ReadInt(); + } + else + { + _strRequirement = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.DexReq)) + { + _dexRequirement = reader.ReadInt(); + } + else + { + _dexRequirement = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.IntReq)) + { + _intRequirement = reader.ReadInt(); + } + else + { + _intRequirement = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.MinDamage)) + { + _minDamage = reader.ReadInt(); + } + else + { + _minDamage = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.MaxDamage)) + { + _maxDamage = reader.ReadInt(); + } + else + { + _maxDamage = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.HitSound)) + { + _hitSound = reader.ReadInt(); + } + else + { + _hitSound = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.MissSound)) + { + _missSound = reader.ReadInt(); + } + else + { + _missSound = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.Speed)) + { + if (version < 9) + { + _speed = reader.ReadInt(); + } + else + { + _speed = reader.ReadFloat(); + } + } + else + { + _speed = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.MaxRange)) + { + _maxRange = reader.ReadInt(); + } + else + { + _maxRange = -1; + } + + if (GetSaveFlag(flags, OldSaveFlag.Skill)) + { + _skill = (SkillName)reader.ReadInt(); + } + else + { + _skill = (SkillName)(-1); + } + + if (GetSaveFlag(flags, OldSaveFlag.Type)) + { + _type = (WeaponType)reader.ReadInt(); + } + else + { + _type = (WeaponType)(-1); + } + + if (GetSaveFlag(flags, OldSaveFlag.Animation)) + { + _animation = (WeaponAnimation)reader.ReadInt(); + } + else + { + _animation = (WeaponAnimation)(-1); + } + + if (GetSaveFlag(flags, OldSaveFlag.Resource)) + { + _resource = (CraftResource)reader.ReadInt(); + } + else + { + _resource = CraftResource.Iron; + } + + Attributes = new AosAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.Attributes)) + { + Attributes.Deserialize(reader); + } + + WeaponAttributes = new AosWeaponAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.WeaponAttributes)) + { + WeaponAttributes.Deserialize(reader); + } + + PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); + + SkillBonuses = new AosSkillBonuses(this); + + if (GetSaveFlag(flags, OldSaveFlag.SkillBonuses)) + { + SkillBonuses.Deserialize(reader); + } + + if (GetSaveFlag(flags, OldSaveFlag.Slayer2)) + { + _slayer2 = (SlayerName)reader.ReadInt(); + } + + AosElementDamages = new AosElementAttributes(this); + + if (GetSaveFlag(flags, OldSaveFlag.ElementalDamages)) + { + AosElementDamages.Deserialize(reader); + } + + if (GetSaveFlag(flags, OldSaveFlag.EngravedText)) + { + _engravedText = reader.ReadString(); + } + } + [AfterDeserialization] private void AfterDeserialization() { @@ -3618,6 +4045,42 @@ public abstract partial class BaseWeapon } } + [Flags] + private enum OldSaveFlag + { + None = 0x00000000, + DamageLevel = 0x00000001, + AccuracyLevel = 0x00000002, + DurabilityLevel = 0x00000004, + Quality = 0x00000008, + Hits = 0x00000010, + MaxHits = 0x00000020, + Slayer = 0x00000040, + Poison = 0x00000080, + PoisonCharges = 0x00000100, + Crafter = 0x00000200, + Identified = 0x00000400, + StrReq = 0x00000800, + DexReq = 0x00001000, + IntReq = 0x00002000, + MinDamage = 0x00004000, + MaxDamage = 0x00008000, + HitSound = 0x00010000, + MissSound = 0x00020000, + Speed = 0x00040000, + MaxRange = 0x00080000, + Skill = 0x00100000, + Type = 0x00200000, + Animation = 0x00400000, + Resource = 0x00800000, + Attributes = 0x01000000, + WeaponAttributes = 0x02000000, + PlayerConstructed = 0x04000000, + SkillBonuses = 0x08000000, + Slayer2 = 0x10000000, + ElementalDamages = 0x20000000, + EngravedText = 0x40000000 + } } public enum CheckSlayerResult diff --git a/Projects/UOContent/Items/Weapons/Fists.cs b/Projects/UOContent/Items/Weapons/Fists.cs index ba73fb665..69f265356 100644 --- a/Projects/UOContent/Items/Weapons/Fists.cs +++ b/Projects/UOContent/Items/Weapons/Fists.cs @@ -14,8 +14,6 @@ namespace Server.Items Quality = WeaponQuality.Regular; } - public override bool SkipSerialization => true; - public override WeaponAbility PrimaryAbility => WeaponAbility.Disarm; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; @@ -45,12 +43,6 @@ namespace Server.Items public override double GetDefendSkillValue(Mobile attacker, Mobile defender) { var wresValue = defender.Skills.Wrestling.Value; - - if (!Core.LBR) - { - return wresValue; - } - var anatValue = defender.Skills.Anatomy.Value; var evalValue = defender.Skills.EvalInt.Value; var incrValue = Math.Min((anatValue + evalValue + 20.0) * 0.5, 120.0); @@ -58,9 +50,9 @@ namespace Server.Items return wresValue > incrValue ? wresValue : incrValue; } - private static void CheckPreAOSMoves(Mobile attacker, Mobile defender) + private void CheckPreAOSMoves(Mobile attacker, Mobile defender) { - if (!Core.UOR || !attacker.CanBeginAction()) + if (!attacker.CanBeginAction()) { return; } @@ -98,67 +90,60 @@ namespace Server.Items attacker.SendLocalizedMessage(1004010); // You failed in your attempt to stun. defender.SendLocalizedMessage(1004011); // Your opponent tried to stun you and failed. } - - return; } - - if (!attacker.DisarmReady) + else if (attacker.DisarmReady) { - return; - } + if (!defender.Player && !defender.Body.IsHuman) + { + attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. + return; + } - if (!defender.Player && !defender.Body.IsHuman) - { - attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. - return; - } + if (attacker.Skills.ArmsLore.Value < 80.0 || attacker.Skills.Wrestling.Value < 80.0) + { + attacker.SendLocalizedMessage(1004002); // You are not skilled enough to disarm your opponent. + attacker.DisarmReady = false; + return; + } - if (attacker.Skills.ArmsLore.Value < 80.0 || attacker.Skills.Wrestling.Value < 80.0) - { - attacker.SendLocalizedMessage(1004002); // You are not skilled enough to disarm your opponent. - attacker.DisarmReady = false; - return; - } + if (attacker.Stam < 15) + { + attacker.SendLocalizedMessage(1004003); // You are too fatigued to attempt anything. + return; + } - if (attacker.Stam < 15) - { - attacker.SendLocalizedMessage(1004003); // You are too fatigued to attempt anything. - return; - } + var toDisarm = defender.FindItemOnLayer(Layer.OneHanded); - var toDisarm = defender.FindItemOnLayer(Layer.OneHanded); + if (toDisarm?.Movable == false) + { + toDisarm = defender.FindItemOnLayer(Layer.TwoHanded); + } - if (toDisarm?.Movable != true) - { - toDisarm = defender.FindItemOnLayer(Layer.TwoHanded); - } + var pack = defender.Backpack; - var pack = defender.Backpack; + if (pack == null || toDisarm?.Movable == false) + { + attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. + } + else if (CheckMove(attacker, SkillName.ArmsLore)) + { + StartMoveDelay(attacker); - if (pack == null || toDisarm?.Movable != true) - { - attacker.SendLocalizedMessage(1004001); // You cannot disarm your opponent. - return; - } + attacker.Stam -= 15; + attacker.DisarmReady = false; - if (CheckMove(attacker, SkillName.ArmsLore)) - { - StartMoveDelay(attacker); + attacker.SendLocalizedMessage(1004006); // You successfully disarm your opponent! + defender.SendLocalizedMessage(1004007); // You have been disarmed! - attacker.Stam -= 15; - attacker.DisarmReady = false; + pack.DropItem(toDisarm); + } + else + { + attacker.Stam -= 15; - attacker.SendLocalizedMessage(1004006); // You successfully disarm your opponent! - defender.SendLocalizedMessage(1004007); // You have been disarmed! - - pack.DropItem(toDisarm); - } - else - { - attacker.Stam -= 15; - - attacker.SendLocalizedMessage(1004004); // You failed in your attempt to disarm. - defender.SendLocalizedMessage(1004005); // Your opponent tried to disarm you but failed. + attacker.SendLocalizedMessage(1004004); // You failed in your attempt to disarm. + defender.SendLocalizedMessage(1004005); // Your opponent tried to disarm you but failed. + } } } @@ -177,6 +162,12 @@ namespace Server.Items base.PlaySwingAnimation( attacker ); }*/ + [AfterDeserialization(false)] + private void OnAfterDeserialization() + { + Delete(); + } + /* Wrestling moves */ private static bool CheckMove(Mobile m, SkillName other) @@ -203,7 +194,7 @@ namespace Server.Items public static void DisarmRequest(Mobile m) { - if (Core.AOS || !Core.UOR) + if (Core.AOS) { return; } @@ -236,7 +227,7 @@ namespace Server.Items public static void StunRequest(Mobile m) { - if (Core.AOS || !Core.UOR || !DuelContext.AllowSpecialAbility(m, "Stun", true)) + if (Core.AOS || !DuelContext.AllowSpecialAbility(m, "Stun", true)) { return; } diff --git a/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs b/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs index 4cfb3fe40..19743996b 100644 --- a/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs +++ b/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class ButcherKnife : BaseKnife { [Constructible] - public ButcherKnife() : base(0x13F6) - { - } - - public override double DefaultWeight => 1.0; + public ButcherKnife() : base(0x13F6) => Weight = 1.0; public override WeaponAbility PrimaryAbility => WeaponAbility.InfectiousStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs b/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs index 5c70fc6ec..1738cf81c 100644 --- a/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs +++ b/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Cleaver : BaseKnife { [Constructible] - public Cleaver() : base(0xEC3) - { - } - - public override double DefaultWeight => 2.0; + public Cleaver() : base(0xEC3) => Weight = 2.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.InfectiousStrike; diff --git a/Projects/UOContent/Items/Weapons/Knives/Dagger.cs b/Projects/UOContent/Items/Weapons/Knives/Dagger.cs index baa4e87b8..fadcb7daf 100644 --- a/Projects/UOContent/Items/Weapons/Knives/Dagger.cs +++ b/Projects/UOContent/Items/Weapons/Knives/Dagger.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Dagger : BaseKnife { [Constructible] - public Dagger() : base(0xF52) - { - } - - public override double DefaultWeight => 1.0; + public Dagger() : base(0xF52) => Weight = 1.0; public override WeaponAbility PrimaryAbility => WeaponAbility.InfectiousStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ShadowStrike; diff --git a/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs b/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs index 5d72aca1d..dedd24265 100644 --- a/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs +++ b/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class SkinningKnife : BaseKnife { [Constructible] - public SkinningKnife() : base(0xEC4) - { - } - - public override double DefaultWeight => 1.0; + public SkinningKnife() : base(0xEC4) => Weight = 1.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ShadowStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs b/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs index 3e618435e..f6a00ddbc 100644 --- a/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs +++ b/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs @@ -10,9 +10,11 @@ namespace Server.Items; public partial class ThrowingDagger : Item { [Constructible] - public ThrowingDagger() : base(0xF52) => Layer = Layer.OneHanded; - - public override double DefaultWeight => 1.0; + public ThrowingDagger() : base(0xF52) + { + Weight = 1.0; + Layer = Layer.OneHanded; + } public override string DefaultName => "a throwing dagger"; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs b/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs index 6d6ecfdff..f0560945e 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class AssassinSpike : BaseKnife { [Constructible] - public AssassinSpike() : base(0x2D21) - { - } - - public override double DefaultWeight => 4.0; + public AssassinSpike() : base(0x2D21) => Weight = 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.InfectiousStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ShadowStrike; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs b/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs index 038682c33..453e67e19 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class DiamondMace : BaseBashing { [Constructible] - public DiamondMace() : base(0x2D24) - { - } - - public override double DefaultWeight => 10.0; + public DiamondMace() : base(0x2D24) => Weight = 10.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ConcussionBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.CrushingBlow; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs index a0d866129..43a972d92 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs @@ -8,9 +8,11 @@ namespace Server.Items public partial class ElvenCompositeLongbow : BaseRanged { [Constructible] - public ElvenCompositeLongbow() : base(0x2D1E) => Resource = CraftResource.RegularWood; - - public override double DefaultWeight => 8.0; + public ElvenCompositeLongbow() : base(0x2D1E) + { + Weight = 8.0; + Resource = CraftResource.RegularWood; + } public override int EffectID => 0xF42; public override Type AmmoType => typeof(Arrow); diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs index 7132d8d92..459c76a66 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class ElvenMachete : BaseSword { [Constructible] - public ElvenMachete() : base(0x2D35) - { - } - - public override double DefaultWeight => 6.0; + public ElvenMachete() : base(0x2D35) => Weight = 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DefenseMastery; public override WeaponAbility SecondaryAbility => WeaponAbility.Bladeweave; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs index be8bb7a58..ebe89b1a5 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class ElvenSpellblade : BaseKnife { [Constructible] - public ElvenSpellblade() : base(0x2D20) => Layer = Layer.TwoHanded; + public ElvenSpellblade() : base(0x2D20) + { + Weight = 5.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.PsychicAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.BleedAttack; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs index 8562eab28..79fe3730f 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Leafblade : BaseKnife { [Constructible] - public Leafblade() : base(0x2D22) - { - } - - public override double DefaultWeight => 8.0; + public Leafblade() : base(0x2D22) => Weight = 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Feint; public override WeaponAbility SecondaryAbility => WeaponAbility.ArmorIgnore; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs b/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs index 449dbc3a7..5a078290f 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs @@ -8,9 +8,12 @@ namespace Server.Items public partial class MagicalShortbow : BaseRanged { [Constructible] - public MagicalShortbow() : base(0x2D2B) => Resource = CraftResource.RegularWood; + public MagicalShortbow() : base(0x2D2B) + { + Weight = 6.0; + Resource = CraftResource.RegularWood; + } - public override double DefaultWeight => 6.0; public override int EffectID => 0xF42; public override Type AmmoType => typeof(Arrow); public override Item Ammo => new Arrow(); diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs b/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs index c871908d6..1c4f89602 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class OrnateAxe : BaseAxe { [Constructible] - public OrnateAxe() : base(0x2D28) => Layer = Layer.TwoHanded; + public OrnateAxe() : base(0x2D28) + { + Weight = 12.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 12.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Disarm; public override WeaponAbility SecondaryAbility => WeaponAbility.CrushingBlow; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs b/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs index a89915a89..6783f36d3 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class RadiantScimitar : BaseSword { [Constructible] - public RadiantScimitar() : base(0x2D33) - { - } - - public override double DefaultWeight => 9.0; + public RadiantScimitar() : base(0x2D33) => Weight = 9.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.Bladeweave; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs index 905a349dc..1adda77d6 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class RuneBlade : BaseSword { [Constructible] - public RuneBlade() : base(0x2D32) => Layer = Layer.TwoHanded; + public RuneBlade() : base(0x2D32) + { + Weight = 7.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Disarm; public override WeaponAbility SecondaryAbility => WeaponAbility.Bladeweave; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs b/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs index 7b96ea256..153bf26b7 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class WarCleaver : BaseKnife { [Constructible] - public WarCleaver() : base(0x2D2F) - { - } - - public override double DefaultWeight => 10.0; + public WarCleaver() : base(0x2D2F) => Weight = 10.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Disarm; public override WeaponAbility SecondaryAbility => WeaponAbility.Bladeweave; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs b/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs index 7689cad06..e253665f1 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class WildStaff : BaseStaff { [Constructible] - public WildStaff() : base(0x2D25) => Resource = CraftResource.RegularWood; + public WildStaff() : base(0x2D25) + { + Weight = 8.0; + Resource = CraftResource.RegularWood; + } - public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Block; public override WeaponAbility SecondaryAbility => WeaponAbility.ForceOfNature; diff --git a/Projects/UOContent/Items/Weapons/Maces/BaseBashing.cs b/Projects/UOContent/Items/Weapons/Maces/BaseBashing.cs index 40ab2c919..b9870332b 100644 --- a/Projects/UOContent/Items/Weapons/Maces/BaseBashing.cs +++ b/Projects/UOContent/Items/Weapons/Maces/BaseBashing.cs @@ -28,7 +28,7 @@ namespace Server.Items { var damage = base.GetBaseDamage(attacker); - if (!Core.AOS && Core.UOR && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && + if (!Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && attacker.Skills.Anatomy.Value >= 80 && attacker.Skills.Anatomy.Value / 400.0 >= Utility.RandomDouble() && DuelContext.AllowSpecialAbility(attacker, "Crushing Blow", false)) diff --git a/Projects/UOContent/Items/Weapons/Maces/Club.cs b/Projects/UOContent/Items/Weapons/Maces/Club.cs index e7619c44c..a4b3333e3 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Club.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Club.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Club : BaseBashing { [Constructible] - public Club() : base(0x13B4) - { - } - - public override double DefaultWeight => 9.0; + public Club() : base(0x13B4) => Weight = 9.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ShadowStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; diff --git a/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs b/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs index 3b27eb897..88a74b2aa 100644 --- a/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs +++ b/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class HammerPick : BaseBashing { [Constructible] - public HammerPick() : base(0x143D) => Layer = Layer.OneHanded; + public HammerPick() : base(0x143D) + { + Weight = 9.0; + Layer = Layer.OneHanded; + } - public override double DefaultWeight => 9.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Maces/Mace.cs b/Projects/UOContent/Items/Weapons/Maces/Mace.cs index 530ad4c07..b362f04fe 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Mace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Mace.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Mace : BaseBashing { [Constructible] - public Mace() : base(0xF5C) - { - } - - public override double DefaultWeight => 14.0; + public Mace() : base(0xF5C) => Weight = 14.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ConcussionBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs b/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs index c88baa93f..31e92e79f 100644 --- a/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs +++ b/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs @@ -6,11 +6,7 @@ namespace Server.Items public partial class MagicWand : BaseBashing { [Constructible] - public MagicWand() : base(0xDF2) - { - } - - public override double DefaultWeight => 1.0; + public MagicWand() : base(0xDF2) => Weight = 1.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Dismount; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Maces/Maul.cs b/Projects/UOContent/Items/Weapons/Maces/Maul.cs index 69e44f833..ee3054c76 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Maul.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Maul.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Maul : BaseBashing { [Constructible] - public Maul() : base(0x143B) - { - } - - public override double DefaultWeight => 10.0; + public Maul() : base(0x143B) => Weight = 10.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/Maces/Scepter.cs b/Projects/UOContent/Items/Weapons/Maces/Scepter.cs index 6b33fd211..d48793e15 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Scepter.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Scepter.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Scepter : BaseBashing { [Constructible] - public Scepter() : base(0x26BC) - { - } - - public override double DefaultWeight => 8.0; + public Scepter() : base(0x26BC) => Weight = 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs b/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs index b3944be74..dd5ab6944 100644 --- a/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs +++ b/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class WarHammer : BaseBashing { [Constructible] - public WarHammer() : base(0x1439) => Layer = Layer.TwoHanded; + public WarHammer() : base(0x1439) + { + Weight = 10.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 10.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.CrushingBlow; diff --git a/Projects/UOContent/Items/Weapons/Maces/WarMace.cs b/Projects/UOContent/Items/Weapons/Maces/WarMace.cs index cd6a18651..0ed10402c 100644 --- a/Projects/UOContent/Items/Weapons/Maces/WarMace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/WarMace.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class WarMace : BaseBashing { [Constructible] - public WarMace() : base(0x1407) - { - } - - public override double DefaultWeight => 17.0; + public WarMace() : base(0x1407) => Weight = 17.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs b/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs index 25d99ccd1..a19bc33c5 100644 --- a/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs +++ b/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Bardiche : BasePoleArm { [Constructible] - public Bardiche() : base(0xF4D) - { - } - - public override double DefaultWeight => 7.0; + public Bardiche() : base(0xF4D) => Weight = 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ParalyzingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; diff --git a/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs b/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs index 6b41a685c..9356842a5 100644 --- a/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs +++ b/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Halberd : BasePoleArm { [Constructible] - public Halberd() : base(0x143E) - { - } - - public override double DefaultWeight => 16.0; + public Halberd() : base(0x143E) => Weight = 16.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs b/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs index 031de09f0..62038c4d4 100644 --- a/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs +++ b/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs @@ -8,11 +8,7 @@ namespace Server.Items public partial class Scythe : BasePoleArm { [Constructible] - public Scythe() : base(0x26BA) - { - } - - public override double DefaultWeight => 5.0; + public Scythe() : base(0x26BA) => Weight = 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/Ranged/AmmoRecovery.cs b/Projects/UOContent/Items/Weapons/Ranged/AmmoRecovery.cs deleted file mode 100644 index 680ef9bdf..000000000 --- a/Projects/UOContent/Items/Weapons/Ranged/AmmoRecovery.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Mobiles; - -namespace Server.Items -{ - /// - /// SE-era archery ammo recovery. When a player misses a shot there is a chance the spent ammo - /// can be gathered back up afterwards. The banked ammo is held off of - /// (in this side table) so the vast majority of players who never miss with a bow carry no extra - /// state. A single repeating timer per tracked player gathers the ammo, but only once the archer - /// has disengaged: out of warmode and not running (standing still or walking is fine). - /// - public static class AmmoRecovery - { - // How often we re-check whether the archer has settled enough to gather their ammo. - private static readonly TimeSpan RecoveryInterval = TimeSpan.FromSeconds(10); - - // A running step is only "current" if one landed this recently. Comfortably above the running - // step cadence (run-foot ~200ms, run-mount ~100ms) so an actively running archer always reads as - // running, while a stopped runner ages out within ~half a second. - private const long RunningWindowMillis = 500; - - // Only players who have banked recoverable ammo appear here, so this stays tiny. - private static readonly Dictionary _states = new(); - - private sealed class RecoveryState - { - public readonly Dictionary Ammo = new(); - public RecoveryTimer Timer; - } - - private sealed class RecoveryTimer : Timer - { - private readonly PlayerMobile _player; - - // delay == interval, count 0 => first fire after the interval, then repeats until Stop(). - public RecoveryTimer(PlayerMobile player) : base(RecoveryInterval, 0) => _player = player; - - protected override void OnTick() => OnRecoveryTick(_player); - } - - // Called from BaseRanged.OnMiss when a player misses and the shot is recoverable. - public static void Bank(PlayerMobile player, Type ammoType) - { - if (!Core.SE || ammoType == null) - { - return; - } - - if (!_states.TryGetValue(player, out var state)) - { - state = new RecoveryState(); - state.Timer = new RecoveryTimer(player); - state.Timer.Start(); - _states[player] = state; - } - - state.Ammo.TryGetValue(ammoType, out var count); - state.Ammo[ammoType] = count + 1; - } - - // Stops tracking a player and cancels their pending recovery (e.g. on delete). - public static void Forget(PlayerMobile player) - { - if (_states.Remove(player, out var state)) - { - state.Timer?.Stop(); - } - } - - private static void OnRecoveryTick(PlayerMobile player) - { - if (!_states.TryGetValue(player, out var state)) - { - return; - } - - if (player.Deleted || state.Ammo.Count == 0) - { - Forget(player); - return; - } - - // Gather the scattered ammo only once the archer has disengaged: out of warmode and not running. - if (!player.Alive || player.Warmode || IsRunning(player)) - { - return; // Not settled yet; try again on the next tick. - } - - Recover(player, state); - Forget(player); - } - - // The Direction.Running bit is left set after the player stops, so it alone can't tell us whether - // the archer is still running. We pair it with movement recency: a running step is only "current" - // if one landed within roughly two run-step intervals. A stopped runner ages out of that window, - // and a walker never sets the bit at all -- both are treated as "not running", which is what we want. - private static bool IsRunning(Mobile m) => - (m.Direction & Direction.Running) != 0 && - Core.TickCount - m.LastMoveTime < RunningWindowMillis; - - private static void Recover(PlayerMobile player, RecoveryState state) - { - foreach (var (type, amount) in state.Ammo) - { - if (amount <= 0) - { - continue; - } - - Item ammo = null; - - try - { - ammo = type.CreateInstance(); - } - catch - { - // ignored - } - - if (ammo == null) - { - continue; - } - - ammo.Amount = amount; - - var name = ammo.Name; - if (name == null) - { - var label = ammo.LabelNumber; - - // Arrow (1023903/1023904) and bolt (1027163/1027164) name clilocs keep their plural form - // two entries above the singular, so bump the label when recovering more than one. - if (ammo.Amount != 1 && label is 1023903 or 1023904 or 1027163 or 1027164) - { - label += 2; - } - - name = $"#{label}"; - } - - player.PlaceInBackpack(ammo); - player.SendLocalizedMessage(1073504, $"{ammo.Amount}\t{name}"); // You recover ~1_NUM~ ~2_AMMO~. - } - - state.Ammo.Clear(); - } - } -} diff --git a/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs b/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs index 4fd786809..bbe80daf6 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs @@ -1,9 +1,9 @@ using System; +using System.Collections.Generic; using ModernUO.Serialization; using Server.Mobiles; using Server.Network; using Server.Spells; -using Server.Engines.Craft; namespace Server.Items { @@ -20,6 +20,8 @@ namespace Server.Items [SerializedCommandProperty(AccessLevel.GameMaster)] private int _velocity; + private TimerExecutionToken _recoveryTimerToken; + public BaseRanged(int itemID) : base(itemID) { } @@ -68,11 +70,6 @@ namespace Server.Items attacker.DisruptiveAction(); attacker.NetState.SendSwing(attacker.Serial, defender.Serial); - if (attacker is BaseCreature bc && bc.TriggerAbility(MonsterAbilityTrigger.CombatAction, defender)) - { - return GetDelay(attacker); - } - if (OnFired(attacker, defender)) { if (CheckHit(attacker, defender)) @@ -87,7 +84,7 @@ namespace Server.Items else if (WeaponAbility.GetCurrentAbility(attacker) is LightningArrow lightningArrow) { // Lightning Arrow doesn't require ammunition - attacker.MovingParticles(defender, EffectID, 18, 1, false, false, 0, 0, 0, 0, 0, EffectLayer.RightHand, 0); + attacker.MovingEffect(defender, EffectID, 18, 1, false, false); lightningArrow.OnHit(attacker, defender, 0, new WorldLocation(defender)); } } @@ -104,10 +101,11 @@ namespace Server.Items public override void OnHit(Mobile attacker, Mobile defender, double damageBonus = 1) { - if (attacker.Player && !defender.Player && (defender.Body.IsAnimal || defender.Body.IsMonster) && - Utility.RandomDouble() < 0.4 && Ammo is { } ammo && !defender.AddToBackpack(ammo)) + var ammo = Ammo; + if (ammo != null && attacker.Player && !defender.Player && (defender.Body.IsAnimal || defender.Body.IsMonster) && + Utility.RandomDouble() < 0.4) { - ammo.Delete(); + defender.AddToBackpack(ammo); } if (Core.ML && _velocity > 0) @@ -141,8 +139,29 @@ namespace Server.Items { if (attacker is PlayerMobile pm) { - // Bank the spent ammo; it is gathered back up once the archer disengages. - AmmoRecovery.Bank(pm, AmmoType); + var ammo = AmmoType; + + if (ammo != null) + { + pm.RecoverableAmmo ??= new Dictionary(); + pm.RecoverableAmmo.TryGetValue(ammo, out var result); + pm.RecoverableAmmo[ammo] = result + 1; + } + + if (!pm.Warmode) + { + if (!_recoveryTimerToken.Running) + { + Timer.StartTimer(TimeSpan.FromSeconds(10), + () => + { + _recoveryTimerToken.Cancel(); + pm.RecoverAmmo(); + }, + out _recoveryTimerToken + ); + } + } } } else @@ -187,111 +206,8 @@ namespace Server.Items } } - attacker.MovingParticles(defender, EffectID, 18, 1, false, false, 0, 0, 0, 0, 0, EffectLayer.RightHand, 0); - + attacker.MovingEffect(defender, EffectID, 18, 1, false, false); return true; } - - public override int OnCraft( - int quality, - bool makersMark, - Mobile from, - CraftSystem craftSystem, - Type typeRes, - BaseTool tool, - CraftItem craftItem, - int resHue - ) - { - var result = base.OnCraft(quality, makersMark, from, craftSystem, typeRes, tool, craftItem, resHue); - if (Core.ML) - { - ApplyWoodBonuses(typeRes); - } - - return result; - } - - private void ApplyWoodBonuses(Type resource) - { - var craftResource = CraftResources.GetFromType(resource); - - switch (craftResource) - { - case CraftResource.OakWood: - { - Attributes.Luck += 40; - Attributes.WeaponDamage += 5; - break; - } - case CraftResource.AshWood: - { - Attributes.WeaponSpeed += 10; - WeaponAttributes.LowerStatReq += 20; - break; - } - case CraftResource.YewWood: - { - Attributes.AttackChance += 5; - Attributes.WeaponDamage += 10; - break; - } - case CraftResource.Bloodwood: - { - Attributes.RegenHits += 2; - WeaponAttributes.HitLeechHits += 16; - break; - } - case CraftResource.Heartwood: - { - ApplyHeartwoodBonus(); - break; - } - case CraftResource.Frostwood: - { - AosElementDamages.Physical = 60; - AosElementDamages.Cold = 40; - Attributes.WeaponDamage += 12; - break; - } - } - } - - private void ApplyHeartwoodBonus() - { - switch (Utility.Random(6)) - { - case 0: - { - Attributes.Luck += 40; - break; - } - case 1: - { - WeaponAttributes.DurabilityBonus += 50; - break; - } - case 2: - { - WeaponAttributes.LowerStatReq += 20; - break; - } - case 3: - { - Attributes.WeaponSpeed += 10; - break; - } - case 4: - { - Attributes.AttackChance += 5; - break; - } - case 5: - { - WeaponAttributes.HitLeechHits += 10; - break; - } - } - } } } diff --git a/Projects/UOContent/Items/Weapons/Ranged/Bow.cs b/Projects/UOContent/Items/Weapons/Ranged/Bow.cs index f873c6c42..0953163d2 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/Bow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/Bow.cs @@ -10,11 +10,11 @@ namespace Server.Items [Constructible] public Bow() : base(0x13B2) { + Weight = 6.0; Layer = Layer.TwoHanded; Resource = CraftResource.RegularWood; } - public override double DefaultWeight => 6.0; public override int EffectID => 0xF42; public override Type AmmoType => typeof(Arrow); public override Item Ammo => new Arrow(); diff --git a/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs b/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs index 875eb005f..bc1e26185 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs @@ -8,9 +8,12 @@ namespace Server.Items public partial class CompositeBow : BaseRanged { [Constructible] - public CompositeBow() : base(0x26C2) => Resource = CraftResource.RegularWood; + public CompositeBow() : base(0x26C2) + { + Weight = 5.0; + Resource = CraftResource.RegularWood; + } - public override double DefaultWeight => 5.0; public override int EffectID => 0xF42; public override Type AmmoType => typeof(Arrow); public override Item Ammo => new Arrow(); diff --git a/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs b/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs index 6abd968fa..12c823bba 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs @@ -10,11 +10,11 @@ namespace Server.Items [Constructible] public Crossbow() : base(0xF50) { + Weight = 7.0; Layer = Layer.TwoHanded; Resource = CraftResource.RegularWood; } - public override double DefaultWeight => 7.0; public override int EffectID => 0x1BFE; public override Type AmmoType => typeof(Bolt); public override Item Ammo => new Bolt(); diff --git a/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs b/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs index 62c2d5251..947762bd5 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs @@ -10,11 +10,11 @@ namespace Server.Items [Constructible] public HeavyCrossbow() : base(0x13FD) { + Weight = 9.0; Layer = Layer.TwoHanded; Resource = CraftResource.RegularWood; } - public override double DefaultWeight => 9.0; public override int EffectID => 0x1BFE; public override Type AmmoType => typeof(Bolt); public override Item Ammo => new Bolt(); diff --git a/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs b/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs index 14c38ed0c..92af83a9c 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs @@ -8,9 +8,12 @@ namespace Server.Items public partial class RepeatingCrossbow : BaseRanged { [Constructible] - public RepeatingCrossbow() : base(0x26C3) => Resource = CraftResource.RegularWood; + public RepeatingCrossbow() : base(0x26C3) + { + Weight = 6.0; + Resource = CraftResource.RegularWood; + } - public override double DefaultWeight => 6.0; public override int EffectID => 0x1BFE; public override Type AmmoType => typeof(Bolt); public override Item Ammo => new Bolt(); diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs index c975bd3ce..1f3f9db30 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Bokuto : BaseSword { [Constructible] - public Bokuto() : base(0x27A8) - { - } - - public override double DefaultWeight => 7.0; + public Bokuto() : base(0x27A8) => Weight = 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Feint; public override WeaponAbility SecondaryAbility => WeaponAbility.NerveStrike; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs index 4ce963ff5..136897fdf 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class Daisho : BaseSword { [Constructible] - public Daisho() : base(0x27A9) => Layer = Layer.TwoHanded; + public Daisho() : base(0x27A9) + { + Weight = 8.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Feint; public override WeaponAbility SecondaryAbility => WeaponAbility.DoubleStrike; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs index 4bbd1b090..23d054bb6 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class Kama : BaseKnife { [Constructible] - public Kama() : base(0x27AD) => Layer = Layer.TwoHanded; + public Kama() : base(0x27AD) + { + Weight = 7.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.DefenseMastery; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs index f28b67961..963b1219f 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class Lajatang : BaseKnife { [Constructible] - public Lajatang() : base(0x27A7) => Layer = Layer.TwoHanded; + public Lajatang() : base(0x27A7) + { + Weight = 12.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 12.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DefenseMastery; public override WeaponAbility SecondaryAbility => WeaponAbility.FrenziedWhirlwind; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs index 64e6a6a92..7d61ea419 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class NoDachi : BaseSword { [Constructible] - public NoDachi() : base(0x27A2) => Layer = Layer.TwoHanded; + public NoDachi() : base(0x27A2) + { + Weight = 10.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 10.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.RidingSwipe; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs index 990f86df6..2e5bf689c 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Nunchaku : BaseBashing { [Constructible] - public Nunchaku() : base(0x27AE) - { - } - - public override double DefaultWeight => 5.0; + public Nunchaku() : base(0x27AE) => Weight = 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Block; public override WeaponAbility SecondaryAbility => WeaponAbility.Feint; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs index bfd9e9146..df60344a2 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs @@ -7,9 +7,11 @@ namespace Server.Items public partial class Sai : BaseKnife { [Constructible] - public Sai() : base(0x27AF) => Layer = Layer.TwoHanded; - - public override double DefaultWeight => 7.0; + public Sai() : base(0x27AF) + { + Weight = 7.0; + Layer = Layer.TwoHanded; + } public override WeaponAbility PrimaryAbility => WeaponAbility.Block; public override WeaponAbility SecondaryAbility => WeaponAbility.ArmorPierce; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs index 9fc3ed171..388186818 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class Tekagi : BaseKnife { [Constructible] - public Tekagi() : base(0x27AB) => Layer = Layer.TwoHanded; + public Tekagi() : base(0x27AB) + { + Weight = 5.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DualWield; public override WeaponAbility SecondaryAbility => WeaponAbility.TalonStrike; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs index ba4de594f..442ae9e55 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs @@ -7,9 +7,11 @@ namespace Server.Items public partial class Tessen : BaseBashing { [Constructible] - public Tessen() : base(0x27A3) => Layer = Layer.TwoHanded; - - public override double DefaultWeight => 6.0; + public Tessen() : base(0x27A3) + { + Weight = 6.0; + Layer = Layer.TwoHanded; + } public override WeaponAbility PrimaryAbility => WeaponAbility.Feint; public override WeaponAbility SecondaryAbility => WeaponAbility.Block; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs index 7ca5402b3..ab32c41a4 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class Tetsubo : BaseBashing { [Constructible] - public Tetsubo() : base(0x27A6) => Layer = Layer.TwoHanded; + public Tetsubo() : base(0x27A6) + { + Weight = 8.0; + Layer = Layer.TwoHanded; + } - public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.FrenziedWhirlwind; public override WeaponAbility SecondaryAbility => WeaponAbility.CrushingBlow; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs index 3f3d06ca2..5bb6b0fd4 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class Wakizashi : BaseSword { [Constructible] - public Wakizashi() : base(0x27A4) => Layer = Layer.OneHanded; + public Wakizashi() : base(0x27A4) + { + Weight = 5.0; + Layer = Layer.OneHanded; + } - public override double DefaultWeight => 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.FrenziedWhirlwind; public override WeaponAbility SecondaryAbility => WeaponAbility.DoubleStrike; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs index 83cef50ad..024f51657 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs @@ -10,11 +10,11 @@ namespace Server.Items [Constructible] public Yumi() : base(0x27A5) { + Weight = 9.0; Layer = Layer.TwoHanded; Resource = CraftResource.RegularWood; } - public override double DefaultWeight => 9.0; public override int EffectID => 0xF42; public override Type AmmoType => typeof(Arrow); public override Item Ammo => new Arrow(); diff --git a/Projects/UOContent/Items/Weapons/SlayerGroup.cs b/Projects/UOContent/Items/Weapons/SlayerGroup.cs index 87380978a..b358431d0 100644 --- a/Projects/UOContent/Items/Weapons/SlayerGroup.cs +++ b/Projects/UOContent/Items/Weapons/SlayerGroup.cs @@ -342,7 +342,6 @@ namespace Server.Items typeof(OphidianMage), typeof(OphidianMatriarch), typeof(OphidianWarrior), - typeof(Raptor), typeof(Reptalon), typeof(SeaSerpent), typeof(Serado), @@ -351,9 +350,6 @@ namespace Server.Items typeof(SilverSerpent), typeof(SkeletalDragon), typeof(Snake), - typeof(Slith), - typeof(StoneSlith), - typeof(ToxicSlith), typeof(SwampDragon), typeof(WhiteWyrm), typeof(Wyvern), diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/BaseSpear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/BaseSpear.cs index 0cedb68db..612ce7033 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/BaseSpear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/BaseSpear.cs @@ -22,7 +22,7 @@ namespace Server.Items { base.OnHit(attacker, defender, damageBonus); - if (!Core.AOS && Core.UOR && Layer == Layer.TwoHanded && + if (!Core.AOS && Layer == Layer.TwoHanded && attacker.Skills.Anatomy.Value / 400.0 >= Utility.RandomDouble() && DuelContext.AllowSpecialAbility(attacker, "Paralyzing Blow", false)) { diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs index 0116c525f..102ceb361 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class BladedStaff : BaseSpear { [Constructible] - public BladedStaff() : base(0x26BD) - { - } - - public override double DefaultWeight => 4.0; + public BladedStaff() : base(0x26BD) => Weight = 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs index b0b758afb..7d7bfc05a 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class DoubleBladedStaff : BaseSpear { [Constructible] - public DoubleBladedStaff() : base(0x26BF) - { - } - - public override double DefaultWeight => 2.0; + public DoubleBladedStaff() : base(0x26BF) => Weight = 2.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.InfectiousStrike; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs index 06289fdeb..6c8b724c9 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Pike : BaseSpear { [Constructible] - public Pike() : base(0x26BE) - { - } - - public override double DefaultWeight => 8.0; + public Pike() : base(0x26BE) => Weight = 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ParalyzingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.InfectiousStrike; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs index 77267916e..fc3ac6a21 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Pitchfork : BaseSpear { [Constructible] - public Pitchfork() : base(0xE87) - { - } - - public override double DefaultWeight => 11.0; + public Pitchfork() : base(0xE87) => Weight = 11.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs index 4bb826dee..32b566a0b 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class ShortSpear : BaseSpear { [Constructible] - public ShortSpear() : base(0x1403) - { - } - - public override double DefaultWeight => 4.0; + public ShortSpear() : base(0x1403) => Weight = 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ShadowStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs index 6a72f030b..26d3e2636 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Spear : BaseSpear { [Constructible] - public Spear() : base(0xF62) - { - } - - public override double DefaultWeight => 7.0; + public Spear() : base(0xF62) => Weight = 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs index 7aaac31b5..3f3f17ecc 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs @@ -7,9 +7,11 @@ namespace Server.Items public partial class TribalSpear : BaseSpear { [Constructible] - public TribalSpear() : base(0xF62) => Hue = 837; - - public override double DefaultWeight => 7.0; + public TribalSpear() : base(0xF62) + { + Weight = 7.0; + Hue = 837; + } public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs index fa28136cd..94b672b63 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class WarFork : BaseSpear { [Constructible] - public WarFork() : base(0x1405) - { - } - - public override double DefaultWeight => 9.0; + public WarFork() : base(0x1405) => Weight = 9.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs b/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs index a8525393e..a4508680c 100644 --- a/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class BlackStaff : BaseStaff { [Constructible] - public BlackStaff() : base(0xDF0) - { - } - - public override double DefaultWeight => 6.0; + public BlackStaff() : base(0xDF0) => Weight = 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs b/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs index fc852bdc9..22719d53e 100644 --- a/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class GlassStaff : BaseStaff { [Constructible] - public GlassStaff() : base(0x905) => Resource = CraftResource.None; + public GlassStaff() : base(0x905) + { + Weight = 4.0; + Resource = CraftResource.None; + } - public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; public override int AosStrengthReq => 20; diff --git a/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs b/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs index 05cf5bd2a..cc05d6a12 100644 --- a/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class GnarledStaff : BaseStaff { [Constructible] - public GnarledStaff() : base(0x13F8) => Resource = CraftResource.RegularWood; + public GnarledStaff() : base(0x13F8) + { + Weight = 3.0; + Resource = CraftResource.RegularWood; + } - public override double DefaultWeight => 3.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ConcussionBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.ForceOfNature; diff --git a/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs b/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs index b05648f2f..a6ee04d97 100644 --- a/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs @@ -7,9 +7,12 @@ namespace Server.Items public partial class QuarterStaff : BaseStaff { [Constructible] - public QuarterStaff() : base(0xE89) => Resource = CraftResource.RegularWood; + public QuarterStaff() : base(0xE89) + { + Weight = 4.0; + Resource = CraftResource.RegularWood; + } - public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs b/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs index cd47ac8f0..3378f14b6 100644 --- a/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs +++ b/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs @@ -11,9 +11,12 @@ namespace Server.Items public partial class ShepherdsCrook : BaseStaff { [Constructible] - public ShepherdsCrook() : base(0xE81) => Resource = CraftResource.RegularWood; + public ShepherdsCrook() : base(0xE81) + { + Weight = 4.0; + Resource = CraftResource.RegularWood; + } - public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs b/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs index a08813ad6..8c3d9fbd2 100644 --- a/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs +++ b/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class BoneHarvester : BaseSword { [Constructible] - public BoneHarvester() : base(0x26BB) - { - } - - public override double DefaultWeight => 3.0; + public BoneHarvester() : base(0x26BB) => Weight = 3.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ParalyzingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs b/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs index e4b42f359..e980b11ad 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Broadsword : BaseSword { [Constructible] - public Broadsword() : base(0xF5E) - { - } - - public override double DefaultWeight => 6.0; + public Broadsword() : base(0xF5E) => Weight = 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.ArmorIgnore; diff --git a/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs b/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs index 2a12d7c9b..7bb7f1470 100644 --- a/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class CrescentBlade : BaseSword { [Constructible] - public CrescentBlade() : base(0x26C1) - { - } - - public override double DefaultWeight => 1.0; + public CrescentBlade() : base(0x26C1) => Weight = 1.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs b/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs index 5892dbfb7..922c1fd04 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Cutlass : BaseSword { [Constructible] - public Cutlass() : base(0x1441) - { - } - - public override double DefaultWeight => 8.0; + public Cutlass() : base(0x1441) => Weight = 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.ShadowStrike; diff --git a/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs b/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs index b277ab125..e821588ab 100644 --- a/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class GlassSword : BaseSword { [Constructible] - public GlassSword() : base(0x90C) - { - } - - public override double DefaultWeight => 6.0; + public GlassSword() : base(0x90C) => Weight = 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Swords/Katana.cs b/Projects/UOContent/Items/Weapons/Swords/Katana.cs index f59ae28ed..34e4110eb 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Katana.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Katana.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Katana : BaseSword { [Constructible] - public Katana() : base(0x13FF) - { - } - - public override double DefaultWeight => 6.0; + public Katana() : base(0x13FF) => Weight = 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ArmorIgnore; diff --git a/Projects/UOContent/Items/Weapons/Swords/Kryss.cs b/Projects/UOContent/Items/Weapons/Swords/Kryss.cs index 00dfa36de..e6ae7a663 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Kryss.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Kryss.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Kryss : BaseSword { [Constructible] - public Kryss() : base(0x1401) - { - } - - public override double DefaultWeight => 2.0; + public Kryss() : base(0x1401) => Weight = 2.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.InfectiousStrike; diff --git a/Projects/UOContent/Items/Weapons/Swords/Lance.cs b/Projects/UOContent/Items/Weapons/Swords/Lance.cs index 215e43ca4..3520855ca 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Lance.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Lance.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Lance : BaseSword { [Constructible] - public Lance() : base(0x26C0) - { - } - - public override double DefaultWeight => 12.0; + public Lance() : base(0x26C0) => Weight = 12.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Dismount; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/Swords/Longsword.cs b/Projects/UOContent/Items/Weapons/Swords/Longsword.cs index fbff01e81..df3310606 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Longsword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Longsword.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Longsword : BaseSword { [Constructible] - public Longsword() : base(0xF61) - { - } - - public override double DefaultWeight => 7.0; + public Longsword() : base(0xF61) => Weight = 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs b/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs index c8f8cc012..d49112d38 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class Scimitar : BaseSword { [Constructible] - public Scimitar() : base(0x13B6) - { - } - - public override double DefaultWeight => 5.0; + public Scimitar() : base(0x13B6) => Weight = 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs b/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs index b7338ccc9..0e17985ca 100644 --- a/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class ThinLongsword : BaseSword { [Constructible] - public ThinLongsword() : base(0x13B8) - { - } - - public override double DefaultWeight => 1.0; + public ThinLongsword() : base(0x13B8) => Weight = 1.0; public override int AosStrengthReq => 35; public override int AosMinDamage => 15; diff --git a/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs b/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs index 81b7c39b6..83c6f4de9 100644 --- a/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs @@ -7,11 +7,7 @@ namespace Server.Items public partial class VikingSword : BaseSword { [Constructible] - public VikingSword() : base(0x13B9) - { - } - - public override double DefaultWeight => 6.0; + public VikingSword() : base(0x13B9) => Weight = 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/Throwing/AbyssReaver.cs b/Projects/UOContent/Items/Weapons/Throwing/AbyssReaver.cs deleted file mode 100644 index 1b88d6898..000000000 --- a/Projects/UOContent/Items/Weapons/Throwing/AbyssReaver.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class AbyssReaver : Cyclone -{ - [Constructible] - public AbyssReaver() - { - SkillBonuses.SetValues(0, SkillName.Throwing, Utility.RandomMinMax(5, 10)); - Attributes.WeaponDamage = Utility.RandomMinMax(25, 35); - Slayer = SlayerName.Exorcism; - } - - public override int LabelNumber => 1112694; // Abyss Reaver -} diff --git a/Projects/UOContent/Items/Weapons/Throwing/BansheesCall.cs b/Projects/UOContent/Items/Weapons/Throwing/BansheesCall.cs deleted file mode 100644 index 00a23084f..000000000 --- a/Projects/UOContent/Items/Weapons/Throwing/BansheesCall.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class BansheesCall : Cyclone -{ - [Constructible] - public BansheesCall() - { - Hue = 1266; - WeaponAttributes.HitHarm = 40; - WeaponAttributes.HitLeechHits = 45; - Attributes.BonusStr = 5; - Attributes.WeaponSpeed = 30; - Attributes.WeaponDamage = 50; - Velocity = 35; - AosElementDamages.Cold = 100; - } - - public override int LabelNumber => 1113529; // Banshee's Call - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; -} diff --git a/Projects/UOContent/Items/Weapons/Throwing/BaseThrown.cs b/Projects/UOContent/Items/Weapons/Throwing/BaseThrown.cs index 3905aea14..1c99668ad 100644 --- a/Projects/UOContent/Items/Weapons/Throwing/BaseThrown.cs +++ b/Projects/UOContent/Items/Weapons/Throwing/BaseThrown.cs @@ -14,92 +14,31 @@ public abstract partial class BaseThrown : BaseRanged public virtual int MaxThrowRange => MinThrowRange + 3; - // Dynamic max range scaled by attacker Strength, clamped to the weapon's throw band. - // At StrReq the effective range equals MinThrowRange; at 140 Str it reaches MaxThrowRange. public override int DefMaxRange { get { - if (Parent is not Mobile attacker) - { - return MaxThrowRange; - } + int baseRange = MaxThrowRange; - var divisor = (140 - AosStrengthReq) / 3; - if (divisor <= 0) - { - return MaxThrowRange; - } - - // Scale up from MinThrowRange so the base matches the clamp floor even if a - // subclass overrides MaxThrowRange to something other than MinThrowRange + 3. - var scaled = MinThrowRange + (attacker.Str - AosStrengthReq) / divisor; - return Math.Clamp(scaled, MinThrowRange, MaxThrowRange); + return Parent is Mobile attacker + ? baseRange - 3 + (attacker.Str - AosStrengthReq) / ((140 - AosStrengthReq) / 3) + : baseRange; } } public override int EffectID => ItemID; - // Throwing weapons require no ammo — the weapon itself is the projectile. public override Type AmmoType => null; + public override Item Ammo => null; public override int DefHitSound => 0x5D3; public override int DefMissSound => 0x5D4; public override SkillName DefSkill => SkillName.Throwing; - public override SkillName AccuracySkill => SkillName.Throwing; public override WeaponAnimation DefAnimation => WeaponAnimation.Throwing; - // Throwing-specific hit chance modifiers (applied via BaseWeapon.ModifyHitChance hook). - protected override double ModifyHitChance(Mobile attacker, Mobile defender, double chance) - { - // Use Chebyshev distance — consistent with all UO range mechanics. - var distance = Math.Max( - Math.Abs(attacker.X - defender.X), - Math.Abs(attacker.Y - defender.Y) - ); - - if (distance <= 1) - { - // Close-quarters penalty: up to -12%, mitigated by (Throwing + RawDex) / 20. - // At 240 combined (120 skill + 120 RawDex) the penalty is fully mitigated. - var throwSkill = attacker.Skills[SkillName.Throwing].Value; - var mitigation = Math.Min(12.0, (throwSkill + attacker.RawDex) / 20.0); - chance -= (12.0 - mitigation) / 100.0; - } - else if (distance < MinThrowRange) - { - // Below minimum throw range (but not melee): flat -12% hit chance. - chance -= 0.12; - } - - // Shield penalty: equipping a shield while throwing reduces hit chance. - // Penalty = 1200 / Parry (capped at 90%), applied multiplicatively. - // High Parrying skill reduces the penalty significantly. - if (attacker.FindItemOnLayer(Layer.TwoHanded) != null) - { - var parry = attacker.Skills[SkillName.Parry].Value; - var penalty = parry > 0.0 ? 1200.0 / parry : 90.0; - chance *= 1.0 - Math.Min(90.0, penalty) / 100.0; - } - - return chance; - } - - // Overthrow: a throw that reaches the edge of its (STR-scaled) range lands with 47% less - // damage, applied on top of all offensive bonuses. MaxRange is the dynamic DefMaxRange. - protected override int ModifyDamage(Mobile attacker, Mobile defender, int damage) - { - if (!attacker.InRange(defender.Location, MaxRange - 1)) - { - damage = damage * 53 / 100; - } - - return damage; - } - public override bool OnFired(Mobile attacker, Mobile defender) { if (!attacker.InRange(defender, 1)) @@ -134,12 +73,12 @@ public abstract partial class BaseThrown : BaseRanged public virtual void Return(Mobile thrower, Mobile target, WorldLocation worldLocation) { - if (thrower?.Deleted != false || thrower.Map == null || thrower.Map == Map.Internal) + if (thrower == null) { return; } - if (target?.Deleted == false) + if (target != null) { target.MovingEffect(thrower, EffectID, 18, 1, false, false, Hue, 0); } @@ -163,10 +102,4 @@ public abstract partial class BaseThrown : BaseRanged ); } } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - list.Add(1149791, MinThrowRange); // Min Throw Range: ~1_val~ - } } diff --git a/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs b/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs index 34d802cf7..67650361b 100644 --- a/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs +++ b/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs @@ -6,9 +6,12 @@ namespace Server.Items; public partial class Boomerang : BaseThrown { [Constructible] - public Boomerang() : base(0x8FF) => Layer = Layer.OneHanded; + public Boomerang() : base(0x8FF) + { + Weight = 4.0; + Layer = Layer.OneHanded; + } - public override double DefaultWeight => 4.0; public override int MinThrowRange => 4; public override WeaponAbility PrimaryAbility => WeaponAbility.MysticArc; @@ -19,6 +22,4 @@ public partial class Boomerang : BaseThrown public override float MlSpeed => 2.75f; public override int InitMinHits => 31; public override int InitMaxHits => 60; - - public override int RequiredRaces => Race.AllowGargoylesOnly; } diff --git a/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs b/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs index db9a14ec1..889d1e1fb 100644 --- a/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs +++ b/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs @@ -6,9 +6,12 @@ namespace Server.Items; public partial class Cyclone : BaseThrown { [Constructible] - public Cyclone() : base(0x901) => Layer = Layer.OneHanded; + public Cyclone() : base(0x901) + { + Weight = 6.0; + Layer = Layer.OneHanded; + } - public override double DefaultWeight => 6.0; public override int MinThrowRange => 6; public override WeaponAbility PrimaryAbility => WeaponAbility.MovingShot; @@ -20,5 +23,5 @@ public partial class Cyclone : BaseThrown public override int InitMinHits => 31; public override int InitMaxHits => 60; - public override int RequiredRaces => Race.AllowGargoylesOnly; + } diff --git a/Projects/UOContent/Items/Weapons/Throwing/RaptorClaw.cs b/Projects/UOContent/Items/Weapons/Throwing/RaptorClaw.cs deleted file mode 100644 index 9b3ed6a10..000000000 --- a/Projects/UOContent/Items/Weapons/Throwing/RaptorClaw.cs +++ /dev/null @@ -1,23 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class RaptorClaw : Boomerang -{ - [Constructible] - public RaptorClaw() - { - Hue = 53; - Attributes.AttackChance = 12; - Attributes.WeaponSpeed = 30; - Attributes.WeaponDamage = 35; - Slayer = SlayerName.Silver; - WeaponAttributes.HitLeechStam = 40; - } - - public override int LabelNumber => 1112394; // Raptor Claw - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; -} diff --git a/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs b/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs index 6a14de3c8..724053a39 100644 --- a/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs +++ b/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs @@ -6,9 +6,12 @@ namespace Server.Items; public partial class SoulGlaive : BaseThrown { [Constructible] - public SoulGlaive() : base(0x090A) => Layer = Layer.OneHanded; + public SoulGlaive() : base(0x090A) + { + Weight = 8.0; + Layer = Layer.OneHanded; + } - public override double DefaultWeight => 8.0; public override int MinThrowRange => 8; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; @@ -20,6 +23,4 @@ public partial class SoulGlaive : BaseThrown public override int InitMinHits => 31; public override int InitMaxHits => 65; - - public override int RequiredRaces => Race.AllowGargoylesOnly; } diff --git a/Projects/UOContent/Items/Weapons/Throwing/StoneSlithClaw.cs b/Projects/UOContent/Items/Weapons/Throwing/StoneSlithClaw.cs deleted file mode 100644 index 028253e0a..000000000 --- a/Projects/UOContent/Items/Weapons/Throwing/StoneSlithClaw.cs +++ /dev/null @@ -1,23 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class StoneSlithClaw : Cyclone -{ - [Constructible] - public StoneSlithClaw() - { - Hue = 1150; - Attributes.WeaponSpeed = 25; - Attributes.WeaponDamage = 45; - Slayer = SlayerName.DaemonDismissal; - WeaponAttributes.HitHarm = 40; - WeaponAttributes.HitLowerDefend = 40; - } - - public override int LabelNumber => 1112393; // Stone Slith Claw - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; -} diff --git a/Projects/UOContent/Items/Weapons/Throwing/StormCaller.cs b/Projects/UOContent/Items/Weapons/Throwing/StormCaller.cs deleted file mode 100644 index afb568058..000000000 --- a/Projects/UOContent/Items/Weapons/Throwing/StormCaller.cs +++ /dev/null @@ -1,30 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class StormCaller : Boomerang -{ - [Constructible] - public StormCaller() - { - Hue = 456; - //TODO Implement BattleLust - //WeaponAttributes.BattleLust = 1; - WeaponAttributes.HitLightning = 40; - WeaponAttributes.HitLowerDefend = 30; - Attributes.BonusStr = 5; - Attributes.WeaponSpeed = 30; - Attributes.WeaponDamage = 40; - AosElementDamages.Physical = 20; - AosElementDamages.Fire = 20; - AosElementDamages.Cold = 20; - AosElementDamages.Poison = 20; - AosElementDamages.Energy = 20; - } - - public override int LabelNumber => 1113530; // Storm Caller - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; -} diff --git a/Projects/UOContent/Items/Weapons/Throwing/ValkyriesGlaive.cs b/Projects/UOContent/Items/Weapons/Throwing/ValkyriesGlaive.cs deleted file mode 100644 index beded2540..000000000 --- a/Projects/UOContent/Items/Weapons/Throwing/ValkyriesGlaive.cs +++ /dev/null @@ -1,26 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class ValkyriesGlaive : SoulGlaive -{ - [Constructible] - public ValkyriesGlaive() - { - Hue = 1651; - Attributes.SpellChanneling = 1; - Attributes.BonusStr = 5; - Attributes.WeaponSpeed = 20; - Attributes.WeaponDamage = 20; - Slayer = SlayerName.Silver; - WeaponAttributes.HitFireball = 40; - } - - public override int LabelNumber => 1113531; // Valkyrie's Glaive - - public override int ArtifactRarity => 5; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; -} diff --git a/Projects/UOContent/Items/Weapons/Throwing/WindOfCorruption.cs b/Projects/UOContent/Items/Weapons/Throwing/WindOfCorruption.cs deleted file mode 100644 index 7971920cc..000000000 --- a/Projects/UOContent/Items/Weapons/Throwing/WindOfCorruption.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class WindOfCorruption : Cyclone -{ - [Constructible] - public WindOfCorruption() - { - Hue = 1171; - WeaponAttributes.HitLeechStam = 40; - WeaponAttributes.HitLowerDefend = 40; - Attributes.WeaponSpeed = 30; - Attributes.WeaponDamage = 50; - Slayer = SlayerName.Fey; - AosElementDamages.Chaos = 100; - } - - public override int LabelNumber => 1150358; // Wind of Corruption - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; -} diff --git a/Projects/UOContent/Migrations/Server.Engines.BulkOrders.SmallBOD.v0.json b/Projects/UOContent/Migrations/Server.Engines.BulkOrders.SmallBOD.v0.json deleted file mode 100644 index d2703db60..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.BulkOrders.SmallBOD.v0.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.BulkOrders.SmallBOD", - "properties": [ - { - "name": "AmountCur", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Type", - "type": "System.Type", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "Number", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Graphic", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.CannedEvil.ChampionSpawn.v11.json b/Projects/UOContent/Migrations/Server.Engines.CannedEvil.ChampionSpawn.v11.json deleted file mode 100644 index defcbe100..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.CannedEvil.ChampionSpawn.v11.json +++ /dev/null @@ -1,189 +0,0 @@ -{ - "version": 11, - "type": "Server.Engines.CannedEvil.ChampionSpawn", - "properties": [ - { - "name": "Level", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "ActivatedByProximity", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "NextProximityTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "MaxLevel", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "ActivatedByValor", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "DamageEntries", - "type": "System.Collections.Generic.Dictionary\u003CServer.Mobile, int\u003E", - "rule": "DictionaryMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule", - "0", - "int", - "PrimitiveTypeMigrationRule", - "1", - "" - ] - }, - { - "name": "ConfinedRoaming", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Idol", - "type": "Server.Engines.CannedEvil.IdolOfTheChampion", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "HasBeenAdvanced", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "SpawnArea", - "type": "Server.Rectangle2D", - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "Rect2D" - ] - }, - { - "name": "RandomizeType", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Kills", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Active", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Type", - "type": "Server.Engines.CannedEvil.ChampionSpawnType", - "rule": "EnumMigrationRule" - }, - { - "name": "Creatures", - "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "@Tidy", - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "RedSkulls", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "@Tidy", - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "WhiteSkulls", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "@Tidy", - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Platform", - "type": "Server.Engines.CannedEvil.ChampionPlatform", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Altar", - "type": "Server.Engines.CannedEvil.ChampionAltar", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "ExpireDelay", - "type": "System.TimeSpan", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "ExpireTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "Champion", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "RestartDelay", - "type": "System.TimeSpan", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "RestartTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.ArenasMoongate.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.ArenasMoongate.v0.json deleted file mode 100644 index 890daeeed..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.ArenasMoongate.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.ArenasMoongate" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.BRBoard.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.BRBoard.v0.json deleted file mode 100644 index a8ed00ad6..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.BRBoard.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.BRBoard" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.BRBomb.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.BRBomb.v0.json deleted file mode 100644 index 4b7ad8d8c..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.BRBomb.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.BRBomb" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.BRGoal.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.BRGoal.v0.json deleted file mode 100644 index 734d81f04..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.BRGoal.v0.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.BRGoal", - "properties": [ - { - "name": "North", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.CTFBoard.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.CTFBoard.v0.json deleted file mode 100644 index e12470e8c..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.CTFBoard.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.CTFBoard" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.CTFFlag.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.CTFFlag.v0.json deleted file mode 100644 index e2bc9a93a..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.CTFFlag.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.CTFFlag" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DDBoard.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.DDBoard.v0.json deleted file mode 100644 index 33146ad57..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DDBoard.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.DDBoard" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DDWayPoint.DDStep.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.DDWayPoint.DDStep.v0.json deleted file mode 100644 index b5dd5737d..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DDWayPoint.DDStep.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.DDWayPoint.DDStep" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DDWayPoint.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.DDWayPoint.v0.json deleted file mode 100644 index 05fcfd6c9..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DDWayPoint.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.DDWayPoint" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DuelContext.ArenaMoongate.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.DuelContext.ArenaMoongate.v0.json deleted file mode 100644 index edb4336ba..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DuelContext.ArenaMoongate.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.DuelContext.ArenaMoongate" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DuelContext.InternalWall.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.DuelContext.InternalWall.v0.json deleted file mode 100644 index 90b2a1629..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DuelContext.InternalWall.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.DuelContext.InternalWall" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DuelTeleporterAddon.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.DuelTeleporterAddon.v0.json deleted file mode 100644 index e87eaeecb..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.DuelTeleporterAddon.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.DuelTeleporterAddon" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.EventController.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.EventController.v0.json deleted file mode 100644 index 763603e25..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.EventController.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.EventController" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.HillOfTheKing.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.HillOfTheKing.v0.json deleted file mode 100644 index bfccd2eb6..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.HillOfTheKing.v0.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.HillOfTheKing", - "properties": [ - { - "name": "ScoreInterval", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.KHBoard.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.KHBoard.v0.json deleted file mode 100644 index 246348a67..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.KHBoard.v0.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.KHBoard", - "properties": [ - { - "name": "Controller", - "type": "Server.Engines.ConPVP.KHController", - "rule": "SerializableInterfaceMigrationRule" - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.LadderItem.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.LadderItem.v0.json deleted file mode 100644 index 58fb7cfba..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.LadderItem.v0.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.LadderItem", - "properties": [ - { - "name": "Ladder", - "type": "Server.Engines.ConPVP.LadderController", - "rule": "SerializableInterfaceMigrationRule" - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.TournamentBracketItem.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.TournamentBracketItem.v0.json deleted file mode 100644 index 93d812ff0..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.TournamentBracketItem.v0.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.TournamentBracketItem", - "properties": [ - { - "name": "Tournament", - "type": "Server.Engines.ConPVP.TournamentController", - "rule": "SerializableInterfaceMigrationRule" - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.TournamentRegistrar.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.TournamentRegistrar.v0.json deleted file mode 100644 index a1f89eb40..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.TournamentRegistrar.v0.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.TournamentRegistrar", - "properties": [ - { - "name": "Tournament", - "type": "Server.Engines.ConPVP.TournamentController", - "rule": "SerializableInterfaceMigrationRule" - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.ConPVP.TournamentSignupItem.v0.json b/Projects/UOContent/Migrations/Server.Engines.ConPVP.TournamentSignupItem.v0.json deleted file mode 100644 index efa167ea5..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.ConPVP.TournamentSignupItem.v0.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.ConPVP.TournamentSignupItem", - "properties": [ - { - "name": "Tournament", - "type": "Server.Engines.ConPVP.TournamentController", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Registrar", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.Aelorn.v0.json b/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.Aelorn.v0.json deleted file mode 100644 index 3b64d081f..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.Aelorn.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.MLQuests.Definitions.Aelorn" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.BatteredBucket.v0.json b/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.BatteredBucket.v0.json deleted file mode 100644 index ccc163311..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.BatteredBucket.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.MLQuests.Definitions.BatteredBucket" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.Lissbet.v0.json b/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.Lissbet.v0.json deleted file mode 100644 index 60c8b530d..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.Lissbet.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.MLQuests.Definitions.Lissbet" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.MaulTheBear.v0.json b/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.MaulTheBear.v0.json deleted file mode 100644 index cb0be40e9..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.MaulTheBear.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.MLQuests.Definitions.MaulTheBear" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.Strongroot.v0.json b/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.Strongroot.v0.json deleted file mode 100644 index 488b93334..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Definitions.Strongroot.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.MLQuests.Definitions.Strongroot" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Mobiles.SirHelper.v0.json b/Projects/UOContent/Migrations/Server.Engines.MLQuests.Mobiles.SirHelper.v0.json deleted file mode 100644 index b5fe3f488..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.MLQuests.Mobiles.SirHelper.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.MLQuests.Mobiles.SirHelper" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.PlayerMurderSystem.MurderContext.v1.json b/Projects/UOContent/Migrations/Server.Engines.PlayerMurderSystem.MurderContext.v1.json deleted file mode 100644 index 2dee75847..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.PlayerMurderSystem.MurderContext.v1.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "version": 1, - "type": "Server.Engines.PlayerMurderSystem.MurderContext", - "properties": [ - { - "name": "ShortTermElapse", - "type": "System.TimeSpan", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "LongTermElapse", - "type": "System.TimeSpan", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "ShortTermMurders", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "PingPongs", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.PlayerMurderSystem.MurderContext.v2.json b/Projects/UOContent/Migrations/Server.Engines.PlayerMurderSystem.MurderContext.v2.json deleted file mode 100644 index fee2646e5..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.PlayerMurderSystem.MurderContext.v2.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "version": 2, - "type": "Server.Engines.PlayerMurderSystem.MurderContext", - "properties": [ - { - "name": "ShortTermElapse", - "type": "System.TimeSpan", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "LongTermElapse", - "type": "System.TimeSpan", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "ShortTermMurders", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "PingPongs", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Bounty", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "LastMurderTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Spawners.BaseSpawner.v11.json b/Projects/UOContent/Migrations/Server.Engines.Spawners.BaseSpawner.v11.json deleted file mode 100644 index b259d381f..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.Spawners.BaseSpawner.v11.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "version": 11, - "type": "Server.Engines.Spawners.BaseSpawner", - "properties": [ - { - "name": "Guid", - "type": "System.Guid", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "ReturnOnDeactivate", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Entries", - "type": "System.Collections.Generic.List\u003CServer.Engines.Spawners.SpawnerEntry\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Engines.Spawners.SpawnerEntry", - "RawSerializableMigrationRule", - "DeserializationRequiresParent" - ] - }, - { - "name": "WalkingRange", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "WayPoint", - "type": "Server.Items.WayPoint", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Group", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "MinDelay", - "type": "System.TimeSpan", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "MaxDelay", - "type": "System.TimeSpan", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "Count", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Team", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "SpawnBounds", - "type": "Server.Rectangle3D", - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "Rect3D" - ] - }, - { - "name": "Running", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "SpawnLocationIsHome", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "End", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Spawners.BaseSpawner.v12.json b/Projects/UOContent/Migrations/Server.Engines.Spawners.BaseSpawner.v12.json deleted file mode 100644 index 13fcec9c5..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.Spawners.BaseSpawner.v12.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "version": 12, - "type": "Server.Engines.Spawners.BaseSpawner", - "properties": [ - { - "name": "Guid", - "type": "System.Guid", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "ReturnOnDeactivate", - "type": "bool", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Entries", - "type": "System.Collections.Generic.List\u003CServer.Engines.Spawners.SpawnerEntry\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Engines.Spawners.SpawnerEntry", - "RawSerializableMigrationRule", - "DeserializationRequiresParent" - ] - }, - { - "name": "WalkingRange", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "WayPoint", - "type": "Server.Items.WayPoint", - "usesSaveFlag": true, - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Group", - "type": "bool", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "MinDelay", - "type": "System.TimeSpan", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "MaxDelay", - "type": "System.TimeSpan", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "Count", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Team", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Running", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "SpawnLocationIsHome", - "type": "bool", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "End", - "type": "System.DateTime", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "SpawnPositionMode", - "type": "Server.Engines.Spawners.SpawnPositionMode", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "MaxSpawnAttempts", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Spawners.Spawner.v1.json b/Projects/UOContent/Migrations/Server.Engines.Spawners.Spawner.v1.json deleted file mode 100644 index 1e7b3a83f..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.Spawners.Spawner.v1.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "version": 1, - "type": "Server.Engines.Spawners.Spawner", - "properties": [ - { - "name": "UseSpiralScan", - "type": "bool", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "SpawnBounds", - "type": "Server.Rectangle3D", - "usesSaveFlag": true, - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "Rect3D" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Spawners.SpawnerBorder.v0.json b/Projects/UOContent/Migrations/Server.Engines.Spawners.SpawnerBorder.v0.json deleted file mode 100644 index 1a85c0821..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.Spawners.SpawnerBorder.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Engines.Spawners.SpawnerBorder" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Engines.Virtues.VirtueContext.v1.json b/Projects/UOContent/Migrations/Server.Engines.Virtues.VirtueContext.v1.json deleted file mode 100644 index 956de91db..000000000 --- a/Projects/UOContent/Migrations/Server.Engines.Virtues.VirtueContext.v1.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "version": 1, - "type": "Server.Engines.Virtues.VirtueContext", - "properties": [ - { - "name": "LastSacrificeGain", - "type": "System.DateTime", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "LastSacrificeLoss", - "type": "System.DateTime", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "AvailableResurrects", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "LastJusticeLoss", - "type": "System.DateTime", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "LastCompassionLoss", - "type": "System.DateTime", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "NextCompassionDay", - "type": "System.DateTime", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "CompassionGains", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "LastValorLoss", - "type": "System.DateTime", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "LastHonorUse", - "type": "System.DateTime", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "HonorActive", - "type": "bool", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "JusticeProtection", - "type": "Server.Mobiles.PlayerMobile", - "usesSaveFlag": true, - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "JusticeStatus", - "type": "Server.Engines.Virtues.JusticeProtectorStatus", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "Values", - "type": "int[]", - "usesSaveFlag": true, - "rule": "ArrayMigrationRule", - "ruleArguments": [ - "int", - "PrimitiveTypeMigrationRule", - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Ethics.Player.v2.json b/Projects/UOContent/Migrations/Server.Ethics.Player.v2.json deleted file mode 100644 index 04f4a0199..000000000 --- a/Projects/UOContent/Migrations/Server.Ethics.Player.v2.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "version": 2, - "type": "Server.Ethics.Player", - "properties": [ - { - "name": "Mobile", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Power", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "History", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Steed", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Familiar", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Shield", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "Ethic", - "type": "Server.Ethics.Ethic", - "rule": "SerializableInterfaceMigrationRule" - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.AbyssReaver.v0.json b/Projects/UOContent/Migrations/Server.Items.AbyssReaver.v0.json deleted file mode 100644 index 7af2fdda6..000000000 --- a/Projects/UOContent/Migrations/Server.Items.AbyssReaver.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.AbyssReaver" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BansheesCall.v0.json b/Projects/UOContent/Migrations/Server.Items.BansheesCall.v0.json deleted file mode 100644 index 1a3d9c627..000000000 --- a/Projects/UOContent/Migrations/Server.Items.BansheesCall.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.BansheesCall" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseAddonContainerDeed.v2.json b/Projects/UOContent/Migrations/Server.Items.BaseAddonContainerDeed.v2.json deleted file mode 100644 index ee957b7bd..000000000 --- a/Projects/UOContent/Migrations/Server.Items.BaseAddonContainerDeed.v2.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 2, - "type": "Server.Items.BaseAddonContainerDeed", - "properties": [ - { - "name": "Resource", - "type": "Server.Items.CraftResource", - "rule": "EnumMigrationRule" - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseArmor.v10.json b/Projects/UOContent/Migrations/Server.Items.BaseArmor.v10.json deleted file mode 100644 index d7844e116..000000000 --- a/Projects/UOContent/Migrations/Server.Items.BaseArmor.v10.json +++ /dev/null @@ -1,207 +0,0 @@ -{ - "version": 10, - "type": "Server.Items.BaseArmor", - "properties": [ - { - "name": "Attributes", - "type": "Server.AosAttributes", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "ArmorAttributes", - "type": "Server.AosArmorAttributes", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "PhysicalBonus", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "FireBonus", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "ColdBonus", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "PoisonBonus", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "EnergyBonus", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "Identified", - "type": "bool", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "MaxHitPoints", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "HitPoints", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "Crafter", - "type": "string", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Quality", - "type": "Server.Items.ArmorQuality", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "Durability", - "type": "Server.Items.ArmorDurabilityLevel", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "ProtectionLevel", - "type": "Server.Items.ArmorProtectionLevel", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "Resource", - "type": "Server.Items.CraftResource", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "BaseArmorRating", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "StrBonus", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "DexBonus", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "IntBonus", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "StrRequirement", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "DexRequirement", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "IntRequirement", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "MeditationAllowance", - "type": "Server.Items.ArmorMeditationAllowance", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "SkillBonuses", - "type": "Server.AosSkillBonuses", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseClothing.v8.json b/Projects/UOContent/Migrations/Server.Items.BaseClothing.v8.json deleted file mode 100644 index 09d5e07be..000000000 --- a/Projects/UOContent/Migrations/Server.Items.BaseClothing.v8.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "version": 8, - "type": "Server.Items.BaseClothing", - "properties": [ - { - "name": "Resource", - "type": "Server.Items.CraftResource", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "Attributes", - "type": "Server.AosAttributes", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "ClothingAttributes", - "type": "Server.AosArmorAttributes", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "SkillBonuses", - "type": "Server.AosSkillBonuses", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "Resistances", - "type": "Server.AosElementAttributes", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "MaxHitPoints", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "HitPoints", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "Crafter", - "type": "string", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Quality", - "type": "Server.Items.ClothingQuality", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "StrRequirement", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseJewel.v5.json b/Projects/UOContent/Migrations/Server.Items.BaseJewel.v5.json deleted file mode 100644 index f26e43f46..000000000 --- a/Projects/UOContent/Migrations/Server.Items.BaseJewel.v5.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "version": 5, - "type": "Server.Items.BaseJewel", - "properties": [ - { - "name": "MaxHitPoints", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "HitPoints", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - }, - { - "name": "Resource", - "type": "Server.Items.CraftResource", - "rule": "EnumMigrationRule" - }, - { - "name": "GemType", - "type": "Server.Items.GemType", - "rule": "EnumMigrationRule" - }, - { - "name": "Attributes", - "type": "Server.AosAttributes", - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "Resistances", - "type": "Server.AosElementAttributes", - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "SkillBonuses", - "type": "Server.AosSkillBonuses", - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "GemCount", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "EncodedInt" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseLight.v2.json b/Projects/UOContent/Migrations/Server.Items.BaseLight.v2.json deleted file mode 100644 index c04a03f3b..000000000 --- a/Projects/UOContent/Migrations/Server.Items.BaseLight.v2.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "version": 2, - "type": "Server.Items.BaseLight", - "properties": [ - { - "name": "BurntOut", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Burning", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Duration", - "type": "System.TimeSpan", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "Protected", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "BurnTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@AnchoredTimer" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseWeapon.v11.json b/Projects/UOContent/Migrations/Server.Items.BaseWeapon.v11.json deleted file mode 100644 index 009666433..000000000 --- a/Projects/UOContent/Migrations/Server.Items.BaseWeapon.v11.json +++ /dev/null @@ -1,246 +0,0 @@ -{ - "version": 11, - "type": "Server.Items.BaseWeapon", - "properties": [ - { - "name": "DamageLevel", - "type": "Server.Items.WeaponDamageLevel", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "AccuracyLevel", - "type": "Server.Items.WeaponAccuracyLevel", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "DurabilityLevel", - "type": "Server.Items.WeaponDurabilityLevel", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "Quality", - "type": "Server.Items.WeaponQuality", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "HitPoints", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "MaxHitPoints", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Slayer", - "type": "Server.Items.SlayerName", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "Poison", - "type": "Server.Poison", - "usesSaveFlag": true, - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "Poison" - ] - }, - { - "name": "PoisonCharges", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Crafter", - "type": "string", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Identified", - "type": "bool", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "StrRequirement", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "DexRequirement", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "IntRequirement", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "MinDamage", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "MaxDamage", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "HitSound", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "MissSound", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Speed", - "type": "float", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "MaxRange", - "type": "int", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Skill", - "type": "Server.SkillName", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "Type", - "type": "Server.Items.WeaponType", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "Animation", - "type": "Server.Items.WeaponAnimation", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "Resource", - "type": "Server.Items.CraftResource", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "Attributes", - "type": "Server.AosAttributes", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "WeaponAttributes", - "type": "Server.AosWeaponAttributes", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "SkillBonuses", - "type": "Server.AosSkillBonuses", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "Slayer2", - "type": "Server.Items.SlayerName", - "usesSaveFlag": true, - "rule": "EnumMigrationRule" - }, - { - "name": "AosElementDamages", - "type": "Server.AosElementAttributes", - "usesSaveFlag": true, - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "DeserializationRequiresParent" - ] - }, - { - "name": "EngravedText", - "type": "string", - "usesSaveFlag": true, - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BountyBoard.v0.json b/Projects/UOContent/Migrations/Server.Items.BountyBoard.v0.json deleted file mode 100644 index 0f6c9ba87..000000000 --- a/Projects/UOContent/Migrations/Server.Items.BountyBoard.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.BountyBoard" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BronzeStatueMaker.v0.json b/Projects/UOContent/Migrations/Server.Items.BronzeStatueMaker.v0.json deleted file mode 100644 index 4267f98b2..000000000 --- a/Projects/UOContent/Migrations/Server.Items.BronzeStatueMaker.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.BronzeStatueMaker" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Corpse.v14.json b/Projects/UOContent/Migrations/Server.Items.Corpse.v14.json index 8ecb490ef..f10f8c563 100644 --- a/Projects/UOContent/Migrations/Server.Items.Corpse.v14.json +++ b/Projects/UOContent/Migrations/Server.Items.Corpse.v14.json @@ -111,7 +111,7 @@ }, { "name": "Hair", - "type": "Server.Items.LegacyHairInfo", + "type": "Server.VirtualHairInfo", "rule": "RawSerializableMigrationRule", "ruleArguments": [ "", @@ -120,7 +120,7 @@ }, { "name": "FacialHair", - "type": "Server.Items.LegacyHairInfo", + "type": "Server.VirtualHairInfo", "rule": "RawSerializableMigrationRule", "ruleArguments": [ "", diff --git a/Projects/UOContent/Migrations/Server.Items.Corpse.v15.json b/Projects/UOContent/Migrations/Server.Items.Corpse.v15.json deleted file mode 100644 index 7b8195999..000000000 --- a/Projects/UOContent/Migrations/Server.Items.Corpse.v15.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "version": 15, - "type": "Server.Items.Corpse", - "properties": [ - { - "name": "RestoreEquip", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Flags", - "type": "Server.Items.CorpseFlag", - "rule": "EnumMigrationRule" - }, - { - "name": "TimeOfDeath", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "DeltaTime" - ] - }, - { - "name": "RestoreTable", - "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Point3D\u003E", - "rule": "DictionaryMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule", - "0", - "Server.Point3D", - "PrimitiveUOTypeMigrationRule", - "1", - "Point3D" - ] - }, - { - "name": "DecayTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@TimerDrift" - ] - }, - { - "name": "Looters", - "type": "System.Collections.Generic.HashSet\u003CServer.Mobile\u003E", - "rule": "HashSetMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Killer", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Aggressors", - "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "CorpseName", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "AccessLevel", - "type": "Server.AccessLevel", - "rule": "EnumMigrationRule" - }, - { - "name": "Guild", - "type": "Server.Guilds.Guild", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Murderer", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "EquipItems", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Hair", - "type": "Server.Items.LegacyHairInfo", - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "", - "@CanBeNull" - ] - }, - { - "name": "FacialHair", - "type": "Server.Items.LegacyHairInfo", - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "", - "@CanBeNull" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Corpse.v16.json b/Projects/UOContent/Migrations/Server.Items.Corpse.v16.json deleted file mode 100644 index 25aa6b747..000000000 --- a/Projects/UOContent/Migrations/Server.Items.Corpse.v16.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "version": 16, - "type": "Server.Items.Corpse", - "properties": [ - { - "name": "RestoreEquip", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Flags", - "type": "Server.Items.CorpseFlag", - "rule": "EnumMigrationRule" - }, - { - "name": "TimeOfDeath", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "DeltaTime" - ] - }, - { - "name": "RestoreTable", - "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Point3D\u003E", - "rule": "DictionaryMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule", - "0", - "Server.Point3D", - "PrimitiveUOTypeMigrationRule", - "1", - "Point3D" - ] - }, - { - "name": "DecayTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@TimerDrift" - ] - }, - { - "name": "Looters", - "type": "System.Collections.Generic.HashSet\u003CServer.Mobile\u003E", - "rule": "HashSetMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Killer", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Aggressors", - "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "CorpseName", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "AccessLevel", - "type": "Server.AccessLevel", - "rule": "EnumMigrationRule" - }, - { - "name": "Guild", - "type": "Server.Guilds.Guild", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "EquipItems", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Hair", - "type": "Server.Items.LegacyHairInfo", - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "", - "@CanBeNull" - ] - }, - { - "name": "FacialHair", - "type": "Server.Items.LegacyHairInfo", - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "", - "@CanBeNull" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Corpse.v17.json b/Projects/UOContent/Migrations/Server.Items.Corpse.v17.json deleted file mode 100644 index 5dab0cd37..000000000 --- a/Projects/UOContent/Migrations/Server.Items.Corpse.v17.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "version": 17, - "type": "Server.Items.Corpse", - "properties": [ - { - "name": "RestoreEquip", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Flags", - "type": "Server.Items.CorpseFlag", - "rule": "EnumMigrationRule" - }, - { - "name": "TimeOfDeath", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "DeltaTime" - ] - }, - { - "name": "RestoreTable", - "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Point3D\u003E", - "rule": "DictionaryMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule", - "0", - "Server.Point3D", - "PrimitiveUOTypeMigrationRule", - "1", - "Point3D" - ] - }, - { - "name": "DecayTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@TimerDrift" - ] - }, - { - "name": "Looters", - "type": "System.Collections.Generic.HashSet\u003CServer.Mobile\u003E", - "rule": "HashSetMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Killer", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Aggressors", - "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "CorpseName", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "AccessLevel", - "type": "Server.AccessLevel", - "rule": "EnumMigrationRule" - }, - { - "name": "Guild", - "type": "Server.Guilds.Guild", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "EquipItems", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "HairItemId", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "HairHue", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "FacialHairItemId", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "FacialHairHue", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Corpse.v18.json b/Projects/UOContent/Migrations/Server.Items.Corpse.v18.json deleted file mode 100644 index 452e3ba7c..000000000 --- a/Projects/UOContent/Migrations/Server.Items.Corpse.v18.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "version": 18, - "type": "Server.Items.Corpse", - "properties": [ - { - "name": "RestoreEquip", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Flags", - "type": "Server.Items.CorpseFlag", - "rule": "EnumMigrationRule" - }, - { - "name": "TimeOfDeath", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "DeltaTime" - ] - }, - { - "name": "RestoreTable", - "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Point3D\u003E", - "rule": "DictionaryMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule", - "0", - "Server.Point3D", - "PrimitiveUOTypeMigrationRule", - "1", - "Point3D" - ] - }, - { - "name": "DecayTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@AnchoredTimer" - ] - }, - { - "name": "Looters", - "type": "System.Collections.Generic.HashSet\u003CServer.Mobile\u003E", - "rule": "HashSetMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Killer", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Aggressors", - "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "CorpseName", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "AccessLevel", - "type": "Server.AccessLevel", - "rule": "EnumMigrationRule" - }, - { - "name": "Guild", - "type": "Server.Guilds.Guild", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "EquipItems", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "HairItemId", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "HairHue", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "FacialHairItemId", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "FacialHairHue", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Corpse.v19.json b/Projects/UOContent/Migrations/Server.Items.Corpse.v19.json deleted file mode 100644 index 2cccb267a..000000000 --- a/Projects/UOContent/Migrations/Server.Items.Corpse.v19.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "version": 19, - "type": "Server.Items.Corpse", - "properties": [ - { - "name": "RestoreEquip", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Flags", - "type": "Server.Items.CorpseFlag", - "rule": "EnumMigrationRule" - }, - { - "name": "TimeOfDeath", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "RestoreTable", - "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Point3D\u003E", - "rule": "DictionaryMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule", - "0", - "Server.Point3D", - "PrimitiveUOTypeMigrationRule", - "1", - "Point3D" - ] - }, - { - "name": "DecayTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@AnchoredTimer" - ] - }, - { - "name": "Looters", - "type": "System.Collections.Generic.HashSet\u003CServer.Mobile\u003E", - "rule": "HashSetMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Killer", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Aggressors", - "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "CorpseName", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "AccessLevel", - "type": "Server.AccessLevel", - "rule": "EnumMigrationRule" - }, - { - "name": "Guild", - "type": "Server.Guilds.Guild", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "EquipItems", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "HairItemId", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "HairHue", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "FacialHairItemId", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "FacialHairHue", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DeathRobe.v4.json b/Projects/UOContent/Migrations/Server.Items.DeathRobe.v4.json deleted file mode 100644 index ef81d79cd..000000000 --- a/Projects/UOContent/Migrations/Server.Items.DeathRobe.v4.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 4, - "type": "Server.Items.DeathRobe", - "properties": [ - { - "name": "DecayTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@AnchoredTimer" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DecayedCorpse.v3.json b/Projects/UOContent/Migrations/Server.Items.DecayedCorpse.v3.json deleted file mode 100644 index adcf02ff6..000000000 --- a/Projects/UOContent/Migrations/Server.Items.DecayedCorpse.v3.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 3, - "type": "Server.Items.DecayedCorpse", - "properties": [ - { - "name": "DecayTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@AnchoredTimer" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.DisappearingRaiseSwitch.v0.json b/Projects/UOContent/Migrations/Server.Items.DisappearingRaiseSwitch.v0.json deleted file mode 100644 index 7042e50ef..000000000 --- a/Projects/UOContent/Migrations/Server.Items.DisappearingRaiseSwitch.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.DisappearingRaiseSwitch" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EndlessDecanter.v0.json b/Projects/UOContent/Migrations/Server.Items.EndlessDecanter.v0.json deleted file mode 100644 index aef743a99..000000000 --- a/Projects/UOContent/Migrations/Server.Items.EndlessDecanter.v0.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.EndlessDecanter", - "properties": [ - { - "name": "Linked", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "LinkLocation", - "type": "Server.Point3D", - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "Point3D" - ] - }, - { - "name": "LinkMap", - "type": "Server.Map", - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "Map" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FillableContainer.v3.json b/Projects/UOContent/Migrations/Server.Items.FillableContainer.v3.json deleted file mode 100644 index 2384676a3..000000000 --- a/Projects/UOContent/Migrations/Server.Items.FillableContainer.v3.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": 3, - "type": "Server.Items.FillableContainer", - "properties": [ - { - "name": "ContentType", - "type": "Server.Items.FillableContentType", - "rule": "EnumMigrationRule" - }, - { - "name": "RespawnTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@AnchoredTimer" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.FlameSpurtTrap.v0.json b/Projects/UOContent/Migrations/Server.Items.FlameSpurtTrap.v0.json index 70c2b17fd..1e92a8ec5 100644 --- a/Projects/UOContent/Migrations/Server.Items.FlameSpurtTrap.v0.json +++ b/Projects/UOContent/Migrations/Server.Items.FlameSpurtTrap.v0.json @@ -4,7 +4,7 @@ "properties": [ { "name": "Spurt", - "type": "Server.Items.Static", + "type": "Server.Item", "rule": "SerializableInterfaceMigrationRule" } ] diff --git a/Projects/UOContent/Migrations/Server.Items.Head.v2.json b/Projects/UOContent/Migrations/Server.Items.Head.v2.json deleted file mode 100644 index 0aaf53c22..000000000 --- a/Projects/UOContent/Migrations/Server.Items.Head.v2.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "version": 2, - "type": "Server.Items.Head", - "properties": [ - { - "name": "PlayerName", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "HeadType", - "type": "Server.Items.HeadType", - "rule": "EnumMigrationRule" - }, - { - "name": "BountyTarget", - "type": "Server.Mobiles.PlayerMobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "CarvedTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.InteractionTeleporter.v0.json b/Projects/UOContent/Migrations/Server.Items.InteractionTeleporter.v0.json deleted file mode 100644 index debf928fc..000000000 --- a/Projects/UOContent/Migrations/Server.Items.InteractionTeleporter.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.InteractionTeleporter" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.JadeStatueMaker.v0.json b/Projects/UOContent/Migrations/Server.Items.JadeStatueMaker.v0.json deleted file mode 100644 index 4a61b3ebf..000000000 --- a/Projects/UOContent/Migrations/Server.Items.JadeStatueMaker.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.JadeStatueMaker" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook1.v0.json b/Projects/UOContent/Migrations/Server.Items.LysanderNotebook1.v0.json deleted file mode 100644 index 1d3503958..000000000 --- a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook1.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.LysanderNotebook1" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook11.v0.json b/Projects/UOContent/Migrations/Server.Items.LysanderNotebook11.v0.json deleted file mode 100644 index c5cf16df7..000000000 --- a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook11.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.LysanderNotebook11" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook2.v0.json b/Projects/UOContent/Migrations/Server.Items.LysanderNotebook2.v0.json deleted file mode 100644 index 6b41bfad0..000000000 --- a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook2.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.LysanderNotebook2" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook3.v0.json b/Projects/UOContent/Migrations/Server.Items.LysanderNotebook3.v0.json deleted file mode 100644 index a6a00ebc5..000000000 --- a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook3.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.LysanderNotebook3" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook7.v0.json b/Projects/UOContent/Migrations/Server.Items.LysanderNotebook7.v0.json deleted file mode 100644 index 6faa170dc..000000000 --- a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook7.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.LysanderNotebook7" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook8.v0.json b/Projects/UOContent/Migrations/Server.Items.LysanderNotebook8.v0.json deleted file mode 100644 index 8f9a4a8f8..000000000 --- a/Projects/UOContent/Migrations/Server.Items.LysanderNotebook8.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.LysanderNotebook8" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MarbleStatueMaker.v0.json b/Projects/UOContent/Migrations/Server.Items.MarbleStatueMaker.v0.json deleted file mode 100644 index efb173e7c..000000000 --- a/Projects/UOContent/Migrations/Server.Items.MarbleStatueMaker.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.MarbleStatueMaker" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MarkContainer.v1.json b/Projects/UOContent/Migrations/Server.Items.MarkContainer.v1.json deleted file mode 100644 index 267ca4709..000000000 --- a/Projects/UOContent/Migrations/Server.Items.MarkContainer.v1.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "version": 1, - "type": "Server.Items.MarkContainer", - "properties": [ - { - "name": "AutoLock", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "RelockTimer", - "type": "Server.Items.MarkContainer.InternalTimer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@AnchoredTimer" - ] - }, - { - "name": "TargetMap", - "type": "Server.Map", - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "Map" - ] - }, - { - "name": "Target", - "type": "Server.Point3D", - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "Point3D" - ] - }, - { - "name": "Description", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MetalGoldenPuzzleChest.v0.json b/Projects/UOContent/Migrations/Server.Items.MetalGoldenPuzzleChest.v0.json deleted file mode 100644 index c6b99473b..000000000 --- a/Projects/UOContent/Migrations/Server.Items.MetalGoldenPuzzleChest.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.MetalGoldenPuzzleChest" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ProjectedItem.v0.json b/Projects/UOContent/Migrations/Server.Items.ProjectedItem.v0.json deleted file mode 100644 index 9742c8e52..000000000 --- a/Projects/UOContent/Migrations/Server.Items.ProjectedItem.v0.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.ProjectedItem", - "properties": [ - { - "name": "EffectItemId", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "MinimumVisible", - "type": "Server.AccessLevel", - "rule": "EnumMigrationRule" - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PuzzleChest.v1.json b/Projects/UOContent/Migrations/Server.Items.PuzzleChest.v1.json deleted file mode 100644 index 508755f69..000000000 --- a/Projects/UOContent/Migrations/Server.Items.PuzzleChest.v1.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "version": 1, - "type": "Server.Items.PuzzleChest", - "properties": [ - { - "name": "Solution", - "type": "Server.Items.PuzzleChestSolution", - "rule": "RawSerializableMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Hints", - "type": "Server.Items.PuzzleChestCylinder[]", - "rule": "ArrayMigrationRule", - "ruleArguments": [ - "Server.Items.PuzzleChestCylinder", - "EnumMigrationRule" - ] - }, - { - "name": "Guesses", - "type": "System.Collections.Generic.Dictionary\u003CServer.Mobile, Server.Items.PuzzleChestSolutionAndTime\u003E", - "rule": "DictionaryMigrationRule", - "ruleArguments": [ - "@Tidy", - "@CanBeNull", - "Server.Mobile", - "SerializableInterfaceMigrationRule", - "0", - "Server.Items.PuzzleChestSolutionAndTime", - "RawSerializableMigrationRule", - "2", - "", - "@CanBeNull" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolution.v1.json b/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolution.v1.json deleted file mode 100644 index 71453f13a..000000000 --- a/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolution.v1.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": 1, - "type": "Server.Items.PuzzleChestSolution", - "properties": [ - { - "name": "Cylinders", - "type": "Server.Items.PuzzleChestCylinder[]", - "rule": "ArrayMigrationRule", - "ruleArguments": [ - "Server.Items.PuzzleChestCylinder", - "EnumMigrationRule" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v0.json b/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v0.json deleted file mode 100644 index 50e207abb..000000000 --- a/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v0.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.PuzzleChestSolutionAndTime", - "properties": [ - { - "name": "When", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "DeltaTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v1.json b/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v1.json deleted file mode 100644 index 8424276f6..000000000 --- a/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v1.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 1, - "type": "Server.Items.PuzzleChestSolutionAndTime", - "properties": [ - { - "name": "When", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RandomTalisman.v0.json b/Projects/UOContent/Migrations/Server.Items.RandomTalisman.v0.json deleted file mode 100644 index 74d2c09ed..000000000 --- a/Projects/UOContent/Migrations/Server.Items.RandomTalisman.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.RandomTalisman" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RaptorClaw.v0.json b/Projects/UOContent/Migrations/Server.Items.RaptorClaw.v0.json deleted file mode 100644 index 8165371d4..000000000 --- a/Projects/UOContent/Migrations/Server.Items.RaptorClaw.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.RaptorClaw" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SawTrap.v0.json b/Projects/UOContent/Migrations/Server.Items.SawTrap.v0.json deleted file mode 100644 index e1ed348b1..000000000 --- a/Projects/UOContent/Migrations/Server.Items.SawTrap.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.SawTrap" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SpiritualityHelm.v0.json b/Projects/UOContent/Migrations/Server.Items.SpiritualityHelm.v0.json deleted file mode 100644 index 877224f61..000000000 --- a/Projects/UOContent/Migrations/Server.Items.SpiritualityHelm.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.SpiritualityHelm" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StarRoomGate.v2.json b/Projects/UOContent/Migrations/Server.Items.StarRoomGate.v2.json deleted file mode 100644 index 0a61f7bfe..000000000 --- a/Projects/UOContent/Migrations/Server.Items.StarRoomGate.v2.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 2, - "type": "Server.Items.StarRoomGate", - "properties": [ - { - "name": "Decays", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "DecayTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StoneSlithClaw.v0.json b/Projects/UOContent/Migrations/Server.Items.StoneSlithClaw.v0.json deleted file mode 100644 index f01d64da8..000000000 --- a/Projects/UOContent/Migrations/Server.Items.StoneSlithClaw.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.StoneSlithClaw" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StormCaller.v0.json b/Projects/UOContent/Migrations/Server.Items.StormCaller.v0.json deleted file mode 100644 index d69235cd9..000000000 --- a/Projects/UOContent/Migrations/Server.Items.StormCaller.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.StormCaller" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.StrongBoxPuzzle.v0.json b/Projects/UOContent/Migrations/Server.Items.StrongBoxPuzzle.v0.json deleted file mode 100644 index 5a369ab63..000000000 --- a/Projects/UOContent/Migrations/Server.Items.StrongBoxPuzzle.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.StrongBoxPuzzle" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal1.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal1.v0.json deleted file mode 100644 index 379d77c60..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal1.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal1" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal11.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal11.v0.json deleted file mode 100644 index f363919d0..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal11.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal11" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal14.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal14.v0.json deleted file mode 100644 index b4aad02fb..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal14.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal14" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal16.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal16.v0.json deleted file mode 100644 index 8050f04cb..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal16.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal16" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal16b.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal16b.v0.json deleted file mode 100644 index 11bc78d0b..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal16b.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal16b" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal17.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal17.v0.json deleted file mode 100644 index fa14a6594..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal17.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal17" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal19.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal19.v0.json deleted file mode 100644 index 315295943..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal19.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal19" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal2.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal2.v0.json deleted file mode 100644 index d96c68f0f..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal2.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal2" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal3.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal3.v0.json deleted file mode 100644 index 761553671..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal3.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal3" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal6.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal6.v0.json deleted file mode 100644 index 978faa37e..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal6.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal6" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal7.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal7.v0.json deleted file mode 100644 index 9b08669d0..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal7.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal7" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal8.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal8.v0.json deleted file mode 100644 index 1b3222608..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal8.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal8" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TavarasJournal9.v0.json b/Projects/UOContent/Migrations/Server.Items.TavarasJournal9.v0.json deleted file mode 100644 index 5eb786887..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TavarasJournal9.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.TavarasJournal9" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TransientItem.v2.json b/Projects/UOContent/Migrations/Server.Items.TransientItem.v2.json deleted file mode 100644 index 3745c4428..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TransientItem.v2.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 2, - "type": "Server.Items.TransientItem", - "properties": [ - { - "name": "Expiration", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json index e44d7e19a..6510e836b 100644 --- a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json +++ b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json @@ -7,7 +7,6 @@ "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", "rule": "ListMigrationRule", "ruleArguments": [ - "@Tidy", "Server.Mobile", "SerializableInterfaceMigrationRule" ] diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v3.json b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v3.json deleted file mode 100644 index 805bbb6d1..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v3.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "version": 3, - "type": "Server.Items.TreasureMapChest", - "properties": [ - { - "name": "Guardians", - "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "@Tidy", - "@CanBeNull", - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Temporary", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Level", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "ExpireTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@TimerDrift" - ] - }, - { - "name": "Lifted", - "type": "System.Collections.Generic.HashSet\u003CServer.Item\u003E", - "rule": "HashSetMigrationRule", - "ruleArguments": [ - "@Tidy", - "@CanBeNull", - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v4.json b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v4.json deleted file mode 100644 index b4d1f85d4..000000000 --- a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v4.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "version": 4, - "type": "Server.Items.TreasureMapChest", - "properties": [ - { - "name": "Guardians", - "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "@Tidy", - "@CanBeNull", - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Temporary", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Level", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "ExpireTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@AnchoredTimer" - ] - }, - { - "name": "Lifted", - "type": "System.Collections.Generic.HashSet\u003CServer.Item\u003E", - "rule": "HashSetMigrationRule", - "ruleArguments": [ - "@Tidy", - "@CanBeNull", - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ValkyriesGlaive.v0.json b/Projects/UOContent/Migrations/Server.Items.ValkyriesGlaive.v0.json deleted file mode 100644 index 227fe419a..000000000 --- a/Projects/UOContent/Migrations/Server.Items.ValkyriesGlaive.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.ValkyriesGlaive" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.ValorGauntlets.v0.json b/Projects/UOContent/Migrations/Server.Items.ValorGauntlets.v0.json deleted file mode 100644 index 34d671492..000000000 --- a/Projects/UOContent/Migrations/Server.Items.ValorGauntlets.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.ValorGauntlets" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WeddingRing.v0.json b/Projects/UOContent/Migrations/Server.Items.WeddingRing.v0.json deleted file mode 100644 index ed31f38f1..000000000 --- a/Projects/UOContent/Migrations/Server.Items.WeddingRing.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.WeddingRing" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WindOfCorruption.v0.json b/Projects/UOContent/Migrations/Server.Items.WindOfCorruption.v0.json deleted file mode 100644 index 37df005be..000000000 --- a/Projects/UOContent/Migrations/Server.Items.WindOfCorruption.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Items.WindOfCorruption" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Misc.WeatherMap.v0.json b/Projects/UOContent/Migrations/Server.Misc.WeatherMap.v0.json deleted file mode 100644 index eb0f8f98f..000000000 --- a/Projects/UOContent/Migrations/Server.Misc.WeatherMap.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Misc.WeatherMap" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.BaseEscortable.v3.json b/Projects/UOContent/Migrations/Server.Mobiles.BaseEscortable.v3.json deleted file mode 100644 index 5149f4347..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.BaseEscortable.v3.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "version": 3, - "type": "Server.Mobiles.BaseEscortable", - "properties": [ - { - "name": "DestinationString", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "DeleteTimer", - "type": "Server.Timer", - "rule": "TimerMigrationRule", - "ruleArguments": [ - "@AnchoredTimer" - ] - }, - { - "name": "MlQuestType", - "type": "System.Type", - "rule": "PrimitiveTypeMigrationRule" - }, - { - "name": "MlQuestDestinationMessage", - "type": "Server.TextDefinition", - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "TextDefinition" - ] - }, - { - "name": "MlQuestPaymentMessage", - "type": "Server.TextDefinition", - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "TextDefinition" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.GenericBuyInfo.DisplayCache.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.GenericBuyInfo.DisplayCache.v0.json deleted file mode 100644 index c7a823b76..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.GenericBuyInfo.DisplayCache.v0.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": 0, - "type": "Server.Mobiles.GenericBuyInfo.DisplayCache", - "properties": [ - { - "name": "Mobiles", - "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json index 3c76d4e00..0866656e3 100644 --- a/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json +++ b/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json @@ -4,7 +4,7 @@ "properties": [ { "name": "Harrower", - "type": "Server.Mobiles.Harrower", + "type": "Server.Mobile", "rule": "SerializableInterfaceMigrationRule" } ] diff --git a/Projects/UOContent/Migrations/Server.Mobiles.LordOaks.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.LordOaks.v0.json index d210e74a1..435aa4121 100644 --- a/Projects/UOContent/Migrations/Server.Mobiles.LordOaks.v0.json +++ b/Projects/UOContent/Migrations/Server.Mobiles.LordOaks.v0.json @@ -4,7 +4,7 @@ "properties": [ { "name": "Queen", - "type": "Server.Mobiles.Silvani", + "type": "Server.Mobiles.BaseCreature", "rule": "SerializableInterfaceMigrationRule" }, { diff --git a/Projects/UOContent/Migrations/Server.Mobiles.PlayerBarkeeper.v2.json b/Projects/UOContent/Migrations/Server.Mobiles.PlayerBarkeeper.v2.json deleted file mode 100644 index eeaf3634b..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.PlayerBarkeeper.v2.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": 2, - "type": "Server.Mobiles.PlayerBarkeeper", - "properties": [ - { - "name": "House", - "type": "Server.Multis.BaseHouse", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Rumors", - "type": "Server.Mobiles.BarkeeperRumor[]", - "rule": "ArrayMigrationRule", - "ruleArguments": [ - "Server.Mobiles.BarkeeperRumor", - "RawSerializableMigrationRule", - "DeserializationRequiresParent" - ] - }, - { - "name": "TipMessage", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.PlayerVendor.v4.json b/Projects/UOContent/Migrations/Server.Mobiles.PlayerVendor.v4.json deleted file mode 100644 index 6b9c9eb1c..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.PlayerVendor.v4.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "version": 4, - "type": "Server.Mobiles.PlayerVendor", - "properties": [ - { - "name": "ShopName", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "NextPayTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "House", - "type": "Server.Multis.BaseHouse", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "BankAccount", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "HoldGold", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "SellItems", - "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Mobiles.VendorItem\u003E", - "rule": "DictionaryMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule", - "0", - "Server.Mobiles.VendorItem", - "RawSerializableMigrationRule", - "1", - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Raptor.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.Raptor.v0.json deleted file mode 100644 index 4e2bcf4ab..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.Raptor.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Mobiles.Raptor" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v1.json b/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v1.json deleted file mode 100644 index b42fa33eb..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v1.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "version": 1, - "type": "Server.Mobiles.RentedVendor", - "properties": [ - { - "name": "RentalDurationId", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "RentalPrice", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "LandlordRenew", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "RenterRenew", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "RenewalPrice", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "RentalGold", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "RentalExpireTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Sheep.v1.json b/Projects/UOContent/Migrations/Server.Mobiles.Sheep.v1.json deleted file mode 100644 index bd224f77f..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.Sheep.v1.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 1, - "type": "Server.Mobiles.Sheep", - "properties": [ - { - "name": "NextWoolTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Slith.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.Slith.v0.json deleted file mode 100644 index 1254f919a..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.Slith.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Mobiles.Slith" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Spellbinder.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.Spellbinder.v0.json deleted file mode 100644 index a072037a3..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.Spellbinder.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Mobiles.Spellbinder" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.StoneSlith.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.StoneSlith.v0.json deleted file mode 100644 index 58b5c4b1c..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.StoneSlith.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Mobiles.StoneSlith" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.ToxicSlith.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.ToxicSlith.v0.json deleted file mode 100644 index 1184eca95..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.ToxicSlith.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Mobiles.ToxicSlith" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.TransferItem.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.TransferItem.v0.json deleted file mode 100644 index 569bc4899..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.TransferItem.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Mobiles.TransferItem" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Mobiles.WaterElemental.v1.json b/Projects/UOContent/Migrations/Server.Mobiles.WaterElemental.v1.json deleted file mode 100644 index 264a05cc8..000000000 --- a/Projects/UOContent/Migrations/Server.Mobiles.WaterElemental.v1.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 1, - "type": "Server.Mobiles.WaterElemental", - "properties": [ - { - "name": "HasDecanter", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.BaseBoat.v5.json b/Projects/UOContent/Migrations/Server.Multis.BaseBoat.v5.json deleted file mode 100644 index 140d3dd64..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.BaseBoat.v5.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "version": 5, - "type": "Server.Multis.BaseBoat", - "properties": [ - { - "name": "MapItem", - "type": "Server.Items.MapItem", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "NextNavPoint", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Facing", - "type": "Server.Direction", - "rule": "EnumMigrationRule" - }, - { - "name": "TimeOfDecay", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - }, - { - "name": "Owner", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "PPlank", - "type": "Server.Items.Plank", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "SPlank", - "type": "Server.Items.Plank", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "TillerMan", - "type": "Server.Items.TillerMan", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Hold", - "type": "Server.Items.Hold", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "Anchored", - "type": "bool", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "ShipName", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.BaseCamp.v2.json b/Projects/UOContent/Migrations/Server.Multis.BaseCamp.v2.json deleted file mode 100644 index 471c645f1..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.BaseCamp.v2.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": 2, - "type": "Server.Multis.BaseCamp", - "properties": [ - { - "name": "Items", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "@Tidy", - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "Mobiles", - "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "@Tidy", - "Server.Mobile", - "SerializableInterfaceMigrationRule" - ] - }, - { - "name": "DecayTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.BaseContestHouse.v1.json b/Projects/UOContent/Migrations/Server.Multis.BaseContestHouse.v1.json deleted file mode 100644 index 82392981f..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.BaseContestHouse.v1.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 1, - "type": "Server.Multis.BaseContestHouse", - "properties": [ - { - "name": "HouseType", - "type": "Server.Multis.ContestHouseType", - "rule": "EnumMigrationRule" - }, - { - "name": "Fixtures", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "@Tidy", - "@CanBeNull", - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.BaseHouse.TransferItem.v0.json b/Projects/UOContent/Migrations/Server.Multis.BaseHouse.TransferItem.v0.json deleted file mode 100644 index c83d7a6b3..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.BaseHouse.TransferItem.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.BaseHouse.TransferItem" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Camelot.v0.json b/Projects/UOContent/Migrations/Server.Multis.Camelot.v0.json deleted file mode 100644 index 0d816c134..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Camelot.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Camelot" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.CasaMoga.v0.json b/Projects/UOContent/Migrations/Server.Multis.CasaMoga.v0.json deleted file mode 100644 index 4ac22589a..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.CasaMoga.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.CasaMoga" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Castle.v0.json b/Projects/UOContent/Migrations/Server.Multis.Castle.v0.json deleted file mode 100644 index 38aa9652b..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Castle.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Castle" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.CastleOfOceania.v0.json b/Projects/UOContent/Migrations/Server.Multis.CastleOfOceania.v0.json deleted file mode 100644 index 13e124411..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.CastleOfOceania.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.CastleOfOceania" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.DarkthornKeep.v0.json b/Projects/UOContent/Migrations/Server.Multis.DarkthornKeep.v0.json deleted file mode 100644 index bae25c481..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.DarkthornKeep.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.DarkthornKeep" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.BrickHouseDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.BrickHouseDeed.v0.json deleted file mode 100644 index b752267f0..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.BrickHouseDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.BrickHouseDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.CastleDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.CastleDeed.v0.json deleted file mode 100644 index 675fc2494..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.CastleDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.CastleDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.FieldStoneHouseDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.FieldStoneHouseDeed.v0.json deleted file mode 100644 index d47027a29..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.FieldStoneHouseDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.FieldStoneHouseDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.HouseDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.HouseDeed.v0.json deleted file mode 100644 index 0d692d5c0..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.HouseDeed.v0.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.HouseDeed", - "properties": [ - { - "name": "Offset", - "type": "Server.Point3D", - "rule": "PrimitiveUOTypeMigrationRule", - "ruleArguments": [ - "Point3D" - ] - }, - { - "name": "MultiID", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.KeepDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.KeepDeed.v0.json deleted file mode 100644 index 562fc3f58..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.KeepDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.KeepDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.LargeMarbleDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.LargeMarbleDeed.v0.json deleted file mode 100644 index 674e20a0b..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.LargeMarbleDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.LargeMarbleDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.LargePatioDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.LargePatioDeed.v0.json deleted file mode 100644 index 632d3b875..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.LargePatioDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.LargePatioDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.LogCabinDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.LogCabinDeed.v0.json deleted file mode 100644 index 37d59be6c..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.LogCabinDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.LogCabinDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.MarbleWorkshopDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.MarbleWorkshopDeed.v0.json deleted file mode 100644 index 255dcf522..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.MarbleWorkshopDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.MarbleWorkshopDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.SandstonePatioDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.SandstonePatioDeed.v0.json deleted file mode 100644 index eed571e7e..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.SandstonePatioDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.SandstonePatioDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.SmallBrickHouseDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.SmallBrickHouseDeed.v0.json deleted file mode 100644 index 6b6577017..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.SmallBrickHouseDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.SmallBrickHouseDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.SmallTowerDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.SmallTowerDeed.v0.json deleted file mode 100644 index 867f91a07..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.SmallTowerDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.SmallTowerDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.StonePlasterHouseDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.StonePlasterHouseDeed.v0.json deleted file mode 100644 index a44fa9e9e..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.StonePlasterHouseDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.StonePlasterHouseDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.StoneWorkshopDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.StoneWorkshopDeed.v0.json deleted file mode 100644 index b40b2da0d..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.StoneWorkshopDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.StoneWorkshopDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.ThatchedRoofCottageDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.ThatchedRoofCottageDeed.v0.json deleted file mode 100644 index 2da3acf20..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.ThatchedRoofCottageDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.ThatchedRoofCottageDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.TowerDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.TowerDeed.v0.json deleted file mode 100644 index e71196b88..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.TowerDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.TowerDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.TwoStoryStonePlasterHouseDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.TwoStoryStonePlasterHouseDeed.v0.json deleted file mode 100644 index 6e6ee3522..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.TwoStoryStonePlasterHouseDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.TwoStoryStonePlasterHouseDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.TwoStoryWoodPlasterHouseDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.TwoStoryWoodPlasterHouseDeed.v0.json deleted file mode 100644 index a9d1ddea6..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.TwoStoryWoodPlasterHouseDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.TwoStoryWoodPlasterHouseDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.VillaDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.VillaDeed.v0.json deleted file mode 100644 index 5b776d43a..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.VillaDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.VillaDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.WoodHouseDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.WoodHouseDeed.v0.json deleted file mode 100644 index c4a64fba2..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.WoodHouseDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.WoodHouseDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Deeds.WoodPlasterHouseDeed.v0.json b/Projects/UOContent/Migrations/Server.Multis.Deeds.WoodPlasterHouseDeed.v0.json deleted file mode 100644 index 450dc6602..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Deeds.WoodPlasterHouseDeed.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Deeds.WoodPlasterHouseDeed" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.ElsaCastle.v0.json b/Projects/UOContent/Migrations/Server.Multis.ElsaCastle.v0.json deleted file mode 100644 index 54b830550..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.ElsaCastle.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.ElsaCastle" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.FeudalCastle.v0.json b/Projects/UOContent/Migrations/Server.Multis.FeudalCastle.v0.json deleted file mode 100644 index 37b754e18..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.FeudalCastle.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.FeudalCastle" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.GothicRoseCastle.v0.json b/Projects/UOContent/Migrations/Server.Multis.GothicRoseCastle.v0.json deleted file mode 100644 index 251388607..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.GothicRoseCastle.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.GothicRoseCastle" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.GrimswindSisters.v0.json b/Projects/UOContent/Migrations/Server.Multis.GrimswindSisters.v0.json deleted file mode 100644 index 021ab5233..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.GrimswindSisters.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.GrimswindSisters" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.GuildHouse.v0.json b/Projects/UOContent/Migrations/Server.Multis.GuildHouse.v0.json deleted file mode 100644 index 75e1bd270..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.GuildHouse.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.GuildHouse" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Keep.v0.json b/Projects/UOContent/Migrations/Server.Multis.Keep.v0.json deleted file mode 100644 index 5fe2ffc07..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Keep.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Keep" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.LacrimaeInCaelo.v0.json b/Projects/UOContent/Migrations/Server.Multis.LacrimaeInCaelo.v0.json deleted file mode 100644 index 638d78a36..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.LacrimaeInCaelo.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.LacrimaeInCaelo" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.LargeMarbleHouse.v0.json b/Projects/UOContent/Migrations/Server.Multis.LargeMarbleHouse.v0.json deleted file mode 100644 index 6bf98fab1..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.LargeMarbleHouse.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.LargeMarbleHouse" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.LargePatioHouse.v0.json b/Projects/UOContent/Migrations/Server.Multis.LargePatioHouse.v0.json deleted file mode 100644 index d38686cd9..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.LargePatioHouse.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.LargePatioHouse" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.LogCabin.v0.json b/Projects/UOContent/Migrations/Server.Multis.LogCabin.v0.json deleted file mode 100644 index bd07bacec..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.LogCabin.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.LogCabin" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.MovingCrate.v0.json b/Projects/UOContent/Migrations/Server.Multis.MovingCrate.v0.json deleted file mode 100644 index 1e0e10e1f..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.MovingCrate.v0.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.MovingCrate", - "properties": [ - { - "name": "House", - "type": "Server.Multis.BaseHouse", - "rule": "SerializableInterfaceMigrationRule" - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.OkinawaSweetDreamCastle.v0.json b/Projects/UOContent/Migrations/Server.Multis.OkinawaSweetDreamCastle.v0.json deleted file mode 100644 index c867d3d66..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.OkinawaSweetDreamCastle.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.OkinawaSweetDreamCastle" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.PackingBox.v0.json b/Projects/UOContent/Migrations/Server.Multis.PackingBox.v0.json deleted file mode 100644 index 0b62ec435..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.PackingBox.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.PackingBox" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.PreviewHouse.v0.json b/Projects/UOContent/Migrations/Server.Multis.PreviewHouse.v0.json deleted file mode 100644 index cdbd1b23d..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.PreviewHouse.v0.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.PreviewHouse", - "properties": [ - { - "name": "PreviewComponents", - "type": "System.Collections.Generic.List\u003CServer.Item\u003E", - "rule": "ListMigrationRule", - "ruleArguments": [ - "Server.Item", - "SerializableInterfaceMigrationRule" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.RobinsNest.v0.json b/Projects/UOContent/Migrations/Server.Multis.RobinsNest.v0.json deleted file mode 100644 index 8b8fb7664..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.RobinsNest.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.RobinsNest" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.RobinsRoost.v0.json b/Projects/UOContent/Migrations/Server.Multis.RobinsRoost.v0.json deleted file mode 100644 index 2712d28d2..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.RobinsRoost.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.RobinsRoost" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.SandStonePatio.v0.json b/Projects/UOContent/Migrations/Server.Multis.SandStonePatio.v0.json deleted file mode 100644 index dae92b501..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.SandStonePatio.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.SandStonePatio" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.SandalwoodKeep.v0.json b/Projects/UOContent/Migrations/Server.Multis.SandalwoodKeep.v0.json deleted file mode 100644 index d21ff554c..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.SandalwoodKeep.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.SandalwoodKeep" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.SmallOldHouse.v0.json b/Projects/UOContent/Migrations/Server.Multis.SmallOldHouse.v0.json deleted file mode 100644 index 428f378d9..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.SmallOldHouse.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.SmallOldHouse" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.SmallShop.v0.json b/Projects/UOContent/Migrations/Server.Multis.SmallShop.v0.json deleted file mode 100644 index c0445ff51..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.SmallShop.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.SmallShop" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.SmallTower.v0.json b/Projects/UOContent/Migrations/Server.Multis.SmallTower.v0.json deleted file mode 100644 index 0328c6408..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.SmallTower.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.SmallTower" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Spires.v0.json b/Projects/UOContent/Migrations/Server.Multis.Spires.v0.json deleted file mode 100644 index 8f2a5f820..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Spires.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Spires" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.TheSandstoneCastle.v0.json b/Projects/UOContent/Migrations/Server.Multis.TheSandstoneCastle.v0.json deleted file mode 100644 index cd0d66970..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.TheSandstoneCastle.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.TheSandstoneCastle" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.Tower.v0.json b/Projects/UOContent/Migrations/Server.Multis.Tower.v0.json deleted file mode 100644 index dee425eeb..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.Tower.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.Tower" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.TraditionalKeep.v0.json b/Projects/UOContent/Migrations/Server.Multis.TraditionalKeep.v0.json deleted file mode 100644 index 4a87f2b5c..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.TraditionalKeep.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.TraditionalKeep" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.TrinsicKeep.v0.json b/Projects/UOContent/Migrations/Server.Multis.TrinsicKeep.v0.json deleted file mode 100644 index bd90abb03..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.TrinsicKeep.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.TrinsicKeep" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.TwoStoryHouse.v0.json b/Projects/UOContent/Migrations/Server.Multis.TwoStoryHouse.v0.json deleted file mode 100644 index 51d321a24..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.TwoStoryHouse.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.TwoStoryHouse" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.TwoStoryVilla.v0.json b/Projects/UOContent/Migrations/Server.Multis.TwoStoryVilla.v0.json deleted file mode 100644 index 56ff6b46b..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.TwoStoryVilla.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.TwoStoryVilla" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Multis.VillaCrowley.v0.json b/Projects/UOContent/Migrations/Server.Multis.VillaCrowley.v0.json deleted file mode 100644 index a7e53ff34..000000000 --- a/Projects/UOContent/Migrations/Server.Multis.VillaCrowley.v0.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": 0, - "type": "Server.Multis.VillaCrowley" -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Spells.Fifth.PoisonField.v1.json b/Projects/UOContent/Migrations/Server.Spells.Fifth.PoisonField.v1.json deleted file mode 100644 index 1f16a4d7f..000000000 --- a/Projects/UOContent/Migrations/Server.Spells.Fifth.PoisonField.v1.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": 1, - "type": "Server.Spells.Fifth.PoisonField", - "properties": [ - { - "name": "Caster", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "End", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Spells.Fourth.FireFieldItem.v1.json b/Projects/UOContent/Migrations/Server.Spells.Fourth.FireFieldItem.v1.json deleted file mode 100644 index 3e91c7a4d..000000000 --- a/Projects/UOContent/Migrations/Server.Spells.Fourth.FireFieldItem.v1.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "version": 1, - "type": "Server.Spells.Fourth.FireFieldItem", - "properties": [ - { - "name": "Damage", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "Caster", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "End", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Spells.Seventh.EnergyField.v2.json b/Projects/UOContent/Migrations/Server.Spells.Seventh.EnergyField.v2.json deleted file mode 100644 index dd202da03..000000000 --- a/Projects/UOContent/Migrations/Server.Spells.Seventh.EnergyField.v2.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": 2, - "type": "Server.Spells.Seventh.EnergyField", - "properties": [ - { - "name": "Caster", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "End", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Spells.Sixth.ParalyzeField.v1.json b/Projects/UOContent/Migrations/Server.Spells.Sixth.ParalyzeField.v1.json deleted file mode 100644 index 3e834a6b1..000000000 --- a/Projects/UOContent/Migrations/Server.Spells.Sixth.ParalyzeField.v1.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": 1, - "type": "Server.Spells.Sixth.ParalyzeField", - "properties": [ - { - "name": "Caster", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "End", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Spells.Third.WallOfStone.v1.json b/Projects/UOContent/Migrations/Server.Spells.Third.WallOfStone.v1.json deleted file mode 100644 index eafcc5945..000000000 --- a/Projects/UOContent/Migrations/Server.Spells.Third.WallOfStone.v1.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": 1, - "type": "Server.Spells.Third.WallOfStone", - "properties": [ - { - "name": "Caster", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - }, - { - "name": "End", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "AnchoredTime" - ] - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Systems.JailSystem.JailRecord.v0.json b/Projects/UOContent/Migrations/Server.Systems.JailSystem.JailRecord.v0.json deleted file mode 100644 index acbdc3297..000000000 --- a/Projects/UOContent/Migrations/Server.Systems.JailSystem.JailRecord.v0.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "version": 0, - "type": "Server.Systems.JailSystem.JailRecord", - "properties": [ - { - "name": "JailCount", - "type": "int", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "LastJailed", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "JailEndTime", - "type": "System.DateTime", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "LastJailReason", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": [ - "" - ] - }, - { - "name": "JailedBy", - "type": "Server.Mobile", - "rule": "SerializableInterfaceMigrationRule" - } - ] -} \ No newline at end of file diff --git a/Projects/UOContent/Misc/AOS.cs b/Projects/UOContent/Misc/AOS.cs index 6a9ec278c..ec8c09b84 100644 --- a/Projects/UOContent/Misc/AOS.cs +++ b/Projects/UOContent/Misc/AOS.cs @@ -493,131 +493,6 @@ namespace Server set => this[AosAttribute.IncreasedKarmaLoss] = value; } - public void GetProperties(IPropertyList list, int damageBonus = 0, int hitChanceBonus = 0, int luckBonus = 0) - { - int prop; - - if ((prop = WeaponDamage + damageBonus) != 0) - { - list.Add(1060401, prop); // damage increase ~1_val~% - } - - if ((prop = DefendChance) != 0) - { - list.Add(1060408, prop); // defense chance increase ~1_val~% - } - - if ((prop = BonusDex) != 0) - { - list.Add(1060409, prop); // dexterity bonus ~1_val~ - } - - if ((prop = EnhancePotions) != 0) - { - list.Add(1060411, prop); // enhance potions ~1_val~% - } - - if ((prop = CastRecovery) != 0) - { - list.Add(1060412, prop); // faster cast recovery ~1_val~ - } - - if ((prop = CastSpeed) != 0) - { - list.Add(1060413, prop); // faster casting ~1_val~ - } - - if ((prop = AttackChance + hitChanceBonus) != 0) - { - list.Add(1060415, prop); // hit chance increase ~1_val~% - } - - if ((prop = BonusHits) != 0) - { - list.Add(1060431, prop); // hit point increase ~1_val~ - } - - if ((prop = BonusInt) != 0) - { - list.Add(1060432, prop); // intelligence bonus ~1_val~ - } - - if ((prop = LowerManaCost) != 0) - { - list.Add(1060433, prop); // lower mana cost ~1_val~% - } - - if ((prop = LowerRegCost) != 0) - { - list.Add(1060434, prop); // lower reagent cost ~1_val~% - } - - if ((prop = Luck + luckBonus) != 0) - { - list.Add(1060436, prop); // luck ~1_val~ - } - - if ((prop = BonusMana) != 0) - { - list.Add(1060439, prop); // mana increase ~1_val~ - } - - if ((prop = RegenMana) != 0) - { - list.Add(1060440, prop); // mana regeneration ~1_val~ - } - - if (NightSight != 0) - { - list.Add(1060441); // night sight - } - - if ((prop = ReflectPhysical) != 0) - { - list.Add(1060442, prop); // reflect physical damage ~1_val~% - } - - if ((prop = RegenStam) != 0) - { - list.Add(1060443, prop); // stamina regeneration ~1_val~ - } - - if ((prop = RegenHits) != 0) - { - list.Add(1060444, prop); // hit point regeneration ~1_val~ - } - - if (SpellChanneling != 0) - { - list.Add(1060482); // spell channeling - } - - if ((prop = SpellDamage) != 0) - { - list.Add(1060483, prop); // spell damage increase ~1_val~% - } - - if ((prop = BonusStam) != 0) - { - list.Add(1060484, prop); // stamina increase ~1_val~ - } - - if ((prop = BonusStr) != 0) - { - list.Add(1060485, prop); // strength bonus ~1_val~ - } - - if ((prop = WeaponSpeed) != 0) - { - list.Add(1060486, prop); // swing speed increase ~1_val~% - } - - if (Core.ML && (prop = IncreasedKarmaLoss) != 0) - { - list.Add(1075210, prop); // Increased Karma Loss ~1val~% - } - } - public static int GetValue(Mobile m, AosAttribute attribute) { if (!Core.AOS) @@ -1010,101 +885,6 @@ namespace Server return value; } - public void GetProperties(IPropertyList list) - { - int prop; - - if (UseBestSkill != 0) - { - list.Add(1060400); // use best weapon skill - } - - if ((prop = HitColdArea) != 0) - { - list.Add(1060416, prop); // hit cold area ~1_val~% - } - - if ((prop = HitDispel) != 0) - { - list.Add(1060417, prop); // hit dispel ~1_val~% - } - - if ((prop = HitEnergyArea) != 0) - { - list.Add(1060418, prop); // hit energy area ~1_val~% - } - - if ((prop = HitFireArea) != 0) - { - list.Add(1060419, prop); // hit fire area ~1_val~% - } - - if ((prop = HitFireball) != 0) - { - list.Add(1060420, prop); // hit fireball ~1_val~% - } - - if ((prop = HitHarm) != 0) - { - list.Add(1060421, prop); // hit harm ~1_val~% - } - - if ((prop = HitLeechHits) != 0) - { - list.Add(1060422, prop); // hit life leech ~1_val~% - } - - if ((prop = HitLightning) != 0) - { - list.Add(1060423, prop); // hit lightning ~1_val~% - } - - if ((prop = HitLowerAttack) != 0) - { - list.Add(1060424, prop); // hit lower attack ~1_val~% - } - - if ((prop = HitLowerDefend) != 0) - { - list.Add(1060425, prop); // hit lower defense ~1_val~% - } - - if ((prop = HitMagicArrow) != 0) - { - list.Add(1060426, prop); // hit magic arrow ~1_val~% - } - - if ((prop = HitLeechMana) != 0) - { - list.Add(1060427, prop); // hit mana leech ~1_val~% - } - - if ((prop = HitPhysicalArea) != 0) - { - list.Add(1060428, prop); // hit physical area ~1_val~% - } - - if ((prop = HitPoisonArea) != 0) - { - list.Add(1060429, prop); // hit poison area ~1_val~% - } - - if ((prop = HitLeechStam) != 0) - { - list.Add(1060430, prop); // hit stamina leech ~1_val~% - } - - if ((prop = MageWeapon) != 0) - { - list.Add(1060438, 30 - prop); // mage weapon -~1_val~ skill - } - - if ((prop = SelfRepair) != 0) - { - list.Add(1060450, prop); // self repair ~1_val~ - } - } - public override string ToString() => "..."; } @@ -1198,21 +978,6 @@ namespace Server return value; } - public void GetProperties(IPropertyList list) - { - int prop; - - if (MageArmor != 0) - { - list.Add(1060437); // mage armor - } - - if ((prop = SelfRepair) != 0) - { - list.Add(1060450, prop); // self repair ~1_val~ - } - } - public override string ToString() => "..."; } diff --git a/Projects/UOContent/Misc/AccountPrompt.cs b/Projects/UOContent/Misc/AccountPrompt.cs index 032c55526..fc283892b 100644 --- a/Projects/UOContent/Misc/AccountPrompt.cs +++ b/Projects/UOContent/Misc/AccountPrompt.cs @@ -1,3 +1,4 @@ +using System; using Server.Accounting; using Server.Logging; @@ -11,16 +12,18 @@ public static class AccountPrompt { if (Accounts.Count == 0) { - logger.Warning("This server has no accounts."); - logger.Information("Do you want to create the owner account now? (y/n):"); + Console.WriteLine("This server has no accounts."); + Console.Write("Do you want to create the owner account now? (y/n): "); var answer = ConsoleInputHandler.ReadLine(); if (answer.InsensitiveEquals("y")) { - logger.Information("Input Username:"); + Console.WriteLine(); + + Console.Write("Username: "); var username = ConsoleInputHandler.ReadLine(); - logger.Information("Input Password:"); + Console.Write("Password: "); var password = ConsoleInputHandler.ReadLine(); var a = new Account(username, password) diff --git a/Projects/UOContent/Misc/AdminFirewall.cs b/Projects/UOContent/Misc/AdminFirewall.cs new file mode 100644 index 000000000..684546fba --- /dev/null +++ b/Projects/UOContent/Misc/AdminFirewall.cs @@ -0,0 +1,136 @@ +using System; +using System.Buffers; +using System.IO; +using System.Net; +using System.Runtime.CompilerServices; +using Server.Logging; +using Server.Network; + +namespace Server; + +public static class AdminFirewall +{ + private static readonly ILogger logger = LogFactory.GetLogger(typeof(AdminFirewall)); + + private const string firewallConfigPath = "firewall.cfg"; + + public static void Configure() + { + if (File.Exists(firewallConfigPath)) + { + var searchValues = SearchValues.Create("*Xx?"); + + using var ip = new StreamReader(firewallConfigPath); + + while (ip.ReadLine() is { } line) + { + line = line.Trim(); + + if (line.Length == 0) + { + continue; + } + + if (line.AsSpan().ContainsAny(searchValues)) + { + logger.Warning("Legacy firewall entry \"{Entry}\" ignored", line); + continue; + } + + Add(ToFirewallEntry(line), false); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IFirewallEntry ToFirewallEntry(object entry) + { + return entry switch + { + IFirewallEntry firewallEntry => firewallEntry, + IPAddress address => new SingleIpFirewallEntry(address), + string s => ToFirewallEntry(s), + _ => null + }; + } + + public static IFirewallEntry ToFirewallEntry(string entry) + { + if (entry == null) + { + return null; + } + + try + { + var rangeSeparator = entry.IndexOf('-'); + if (rangeSeparator > -1) + { + return new CidrFirewallEntry( + IPAddress.Parse(entry.AsSpan(0, rangeSeparator)), + IPAddress.Parse(entry.AsSpan(rangeSeparator + 1)) + ); + } + + // CIDR notation + if (entry.IndexOf('/') > -1) + { + return new CidrFirewallEntry(entry); + } + + return new SingleIpFirewallEntry(entry); + } + catch + { + return null; + } + } + + public static bool Remove(object obj, bool save = true) + { + var entry = ToFirewallEntry(obj); + + if (entry == null) + { + return false; + } + + if (!Firewall.Remove(entry)) + { + return false; + } + + if (save) + { + Save(); + } + + return true; + } + + public static void Add(object obj) => Add(ToFirewallEntry(obj)); + + public static bool Add(IFirewallEntry entry, bool save = true) + { + if (!Firewall.Add(entry)) + { + return false; + } + + if (save) + { + Save(); + } + + return true; + } + + public static void Save() + { + using var op = new StreamWriter(firewallConfigPath); + foreach (var entry in Firewall.FirewallSet) + { + op.WriteLine(entry); + } + } +} diff --git a/Projects/UOContent/Misc/AttackMessage.cs b/Projects/UOContent/Misc/AttackMessage.cs index ed4690ca7..04d4697d1 100644 --- a/Projects/UOContent/Misc/AttackMessage.cs +++ b/Projects/UOContent/Misc/AttackMessage.cs @@ -4,6 +4,8 @@ namespace Server.Misc { public static class AttackMessage { + private const string AggressorFormat = "You are attacking {0}!"; + private const string AggressedFormat = "{0} is attacking you!"; private const int Hue = 0x22; private static readonly TimeSpan Delay = TimeSpan.FromMinutes(1.0); @@ -29,13 +31,13 @@ namespace Server.Misc MessageType.Regular, Hue, true, - $"You are attacking {aggressed.Name}!" + string.Format(AggressorFormat, aggressed.Name) ); aggressed.LocalOverheadMessage( MessageType.Regular, Hue, true, - $"{aggressor.Name} is attacking you!" + string.Format(AggressedFormat, aggressor.Name) ); } } diff --git a/Projects/UOContent/Misc/BuffIcons.cs b/Projects/UOContent/Misc/BuffIcons.cs new file mode 100644 index 000000000..46ab80109 --- /dev/null +++ b/Projects/UOContent/Misc/BuffIcons.cs @@ -0,0 +1,330 @@ +using System; +using ModernUO.CodeGeneratedEvents; +using Server.Mobiles; +using Server.Network; + +namespace Server +{ + public class BuffInfo + { + private TimerExecutionToken _timerToken; + + public BuffInfo(BuffIcon iconID, int titleCliloc) : this(iconID, titleCliloc, titleCliloc + 1) + { + } + + public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc) + { + ID = iconID; + TitleCliloc = titleCliloc; + SecondaryCliloc = secondaryCliloc; + } + + public BuffInfo(BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m) + : this(iconID, titleCliloc, titleCliloc + 1, length, m) + { + } + + // Only the timed one needs the Mobile to know when to automagically remove it. + public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m) + : this(iconID, titleCliloc, secondaryCliloc) + { + TimeLength = length; + TimeStart = Core.TickCount; + + Timer.StartTimer(length, () => RemoveBuff(m, this), out _timerToken); + } + + public BuffInfo(BuffIcon iconID, int titleCliloc, TextDefinition args) + : this(iconID, titleCliloc, titleCliloc + 1, args) + { + } + + public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args) + : this(iconID, titleCliloc, secondaryCliloc) => + Args = args; + + public BuffInfo(BuffIcon iconID, int titleCliloc, bool retainThroughDeath) + : this(iconID, titleCliloc, titleCliloc + 1, retainThroughDeath) + { + } + + public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, bool retainThroughDeath) + : this(iconID, titleCliloc, secondaryCliloc) => + RetainThroughDeath = retainThroughDeath; + + public BuffInfo(BuffIcon iconID, int titleCliloc, TextDefinition args, bool retainThroughDeath) + : this(iconID, titleCliloc, titleCliloc + 1, args, retainThroughDeath) + { + } + + public BuffInfo(BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args, bool retainThroughDeath) + : this(iconID, titleCliloc, secondaryCliloc, args) => + RetainThroughDeath = retainThroughDeath; + + public BuffInfo(BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m, TextDefinition args) + : this(iconID, titleCliloc, titleCliloc + 1, length, m, args) + { + } + + public BuffInfo( + BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m, + TextDefinition args + ) : this(iconID, titleCliloc, secondaryCliloc, length, m) => Args = args; + + public BuffInfo( + BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m, TextDefinition args, + bool retainThroughDeath + ) : this(iconID, titleCliloc, titleCliloc + 1, length, m, args, retainThroughDeath) + { + } + + public BuffInfo( + BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m, + TextDefinition args, bool retainThroughDeath + ) : this(iconID, titleCliloc, secondaryCliloc, length, m) + { + Args = args; + RetainThroughDeath = retainThroughDeath; + } + + public static bool Enabled { get; private set; } + + public BuffIcon ID { get; } + + public int TitleCliloc { get; } + + public int SecondaryCliloc { get; } + + public TimeSpan TimeLength { get; } + + public long TimeStart { get; } + + public bool RetainThroughDeath { get; } + + public TextDefinition Args { get; } + + public static void Configure() + { + Enabled = ServerConfiguration.GetOrUpdateSetting("buffIcons.enable", Core.ML); + } + + [OnEvent(nameof(PlayerMobile.PlayerLoginEvent))] + public static void OnLogin(PlayerMobile pm) + { + if (!Enabled) + { + return; + } + + pm.ResendBuffs(); + } + + public static void AddBuff(Mobile m, BuffInfo b) + { + (m as PlayerMobile)?.AddBuff(b); + } + + public static void RemoveBuff(Mobile m, BuffInfo b) + { + b._timerToken.Cancel(); + (m as PlayerMobile)?.RemoveBuff(b); + } + + public static void RemoveBuff(Mobile m, BuffIcon b) + { + (m as PlayerMobile)?.RemoveBuff(b); + } + } + + public enum BuffIcon : short + { + DismountPrevention = 0x3E9, + NoRearm = 0x3EA, + //Currently, no 0x3EB or 0x3EC + NightSight = 0x3ED, //* + DeathStrike, + EvilOmen, + HonoredDebuff, + AchievePerfection, + DivineFury, //* + EnemyOfOne, //* + HidingAndOrStealth, //* + ActiveMeditation, //* + BloodOathCaster, //* + BloodOathCurse, //* + CorpseSkin, //* + Mindrot, //* + PainSpike, //* + Strangle, + GiftOfRenewal, //* + AttuneWeapon, //* + Thunderstorm, //* + EssenceOfWind, //* + EtherealVoyage, //* + GiftOfLife, //* + ArcaneEmpowerment, //* + MortalStrike, + ReactiveArmor, //* + Protection, //* + ArchProtection, + MagicReflection, //* + Incognito, //* + Disguised, + AnimalForm, + Polymorph, + Invisibility, //* + Paralyze, //* + Poison, + Bleed, + Clumsy, //* + FeebleMind, //* + Weaken, //* + Curse, //* + MassCurse, + Agility, //* + Cunning, //* + Strength, //* + Bless, //* + Sleep, + StoneForm, + SpellPlague, + Berserk, + MassSleep, + Fly, + Inspire, + Invigorate, + Resilience, + Perseverance, + TribulationTarget, + DespairTarget, + FishPie = 0x426, + HitLowerAttack, + HitLowerDefense, + DualWield, + Block, + DefenseMastery, + DespairCaster, + Healing, + SpellFocusingBuff, + SpellFocusingDebuff, + RageFocusingDebuff, + RageFocusingBuff, + Warding, + TribulationCaster, + ForceArrow, + Disarm, + Surge, + Feint, + TalonStrike, + PsychicAttack, + ConsecrateWeapon, + GrapesOfWrath, + EnemyOfOneDebuff, + HorrificBeast, + LichForm, + VampiricEmbrace, + CurseWeapon, + ReaperForm, + ImmolatingWeapon, + Enchant, + HonorableExecution, + Confidence, + Evasion, + CounterAttack, + LightningStrike, + MomentumStrike, + OrangePetals, + RoseOfTrinsic, + PoisonImmunity, + Veterinary, + Perfection, + Honored, + ManaPhase, + FanDancerFanFire, + Rage, + Webbing, + MedusaStone, + TrueFear, + AuraOfNausea, + HowlOfCacophony, + GazeDespair, + HiryuPhysicalResistance, + RuneBeetleCorruption, + BloodwormAnemia, + RotwormBloodDisease, + SkillUseDelay, + FactionStatLoss, + HeatOfBattleStatus, + CriminalStatus, + ArmorPierce, + SplinteringEffect, + SwingSpeedDebuff, + WraithForm, + CityTradeDeal = 0x466, + HumilityDebuff = 0x467, + Spirituality, + Humility, + // Skill Masteries + Rampage, + Stagger, // Debuff + Toughness, + Thrust, + Pierce, // Debuff + PlayingTheOdds, + FocusedEye, + Onslaught, // Debuff + ElementalFury, + ElementalFuryDebuff, // Debuff + CalledShot, + Knockout, + SavingThrow, + Conduit, + EtherealBurst, + MysticWeapon, + ManaShield, + AnticipateHit, + Warcry, + Shadow, + WhiteTigerForm, + Bodyguard, + HeightenedSenses, + Tolerance, + DeathRay, + DeathRayDebuff, + Intuition, + EnchantedSummoning, + ShieldBash, + Whispering, + CombatTraining, + InjectedStrikeDebuff, + InjectedStrike, + UnknownTomato, + PlayingTheOddsDebuff, + DragonTurtleDebuff, + Boarding, + Potency, + ThrustDebuff, + FistsOfFury, // 1169 + BarrabHemolymphConcentrate, + JukariBurnPoiltice, + KurakAmbushersEssence, + BarakoDraftOfMight, + UraliTranceTonic, + SakkhraProphylaxis, // 1175 + Sparks, + Swarm, + BoneBreaker, + Unknown2, + SwarmImmune, + BoneBreakerImmune, + UnknownGoblin, + UnknownRedDrop, + UnknownStar, + FeintDebuff, + CaddelliteInfused, + PotionGloriousFortune, + MysticalPolymorphTotem, + UnknownDebuff, + } +} diff --git a/Projects/UOContent/Misc/ClientVerification.cs b/Projects/UOContent/Misc/ClientVerification.cs index 0c02c7585..27aac5f1f 100644 --- a/Projects/UOContent/Misc/ClientVerification.cs +++ b/Projects/UOContent/Misc/ClientVerification.cs @@ -27,12 +27,14 @@ namespace Server.Misc public static bool AllowKR => (AllowedClientTypes & ClientType.KR) != 0; public static bool AllowSA => (AllowedClientTypes & ClientType.SA) != 0; + public static ClientVersion MinRequired { get; private set; } + public static ClientVersion MaxRequired { get; private set; } public static TimeSpan KickDelay { get; private set; } public static void Configure() { - UOClient.MinRequired = ServerConfiguration.GetSetting("clientVerification.minRequired", (ClientVersion)null); - UOClient.MaxRequired = ServerConfiguration.GetSetting("clientVerification.maxRequired", (ClientVersion)null); + MinRequired = ServerConfiguration.GetSetting("clientVerification.minRequired", (ClientVersion)null); + MaxRequired = ServerConfiguration.GetSetting("clientVerification.maxRequired", (ClientVersion)null); _enable = ServerConfiguration.GetOrUpdateSetting("clientVerification.enable", true); _invalidClientResponse = @@ -49,12 +51,12 @@ namespace Server.Misc public static void Initialize() { - if (UOClient.MinRequired == null && UOClient.MaxRequired == null) + if (MinRequired == null && MaxRequired == null) { - UOClient.MinRequired = UOClient.ServerClientVersion; + MinRequired = UOClient.ServerClientVersion; } - if (UOClient.MinRequired != null || UOClient.MaxRequired != null) + if (MinRequired != null || MaxRequired != null) { logger.Information( "Restricting client version to {ClientVersion}. Action to be taken: {Action}", @@ -68,17 +70,17 @@ namespace Server.Misc { if (_versionExpression == null) { - if (UOClient.MinRequired != null && UOClient.MaxRequired != null) + if (MinRequired != null && MaxRequired != null) { - _versionExpression = $"{UOClient.MinRequired}-{UOClient.MaxRequired}"; + _versionExpression = $"{MinRequired}-{MaxRequired}"; } - else if (UOClient.MinRequired != null) + else if (MinRequired != null) { - _versionExpression = $"{UOClient.MinRequired} or newer"; + _versionExpression = $"{MinRequired} or newer"; } else { - _versionExpression = $"{UOClient.MaxRequired} or older"; + _versionExpression = $"{MaxRequired} or older"; } } @@ -94,7 +96,7 @@ namespace Server.Misc return "There are no clients supported at this time."; } - using var builder = new ValueStringBuilder(stackalloc char[192]); + using var builder = ValueStringBuilder.Create(); builder.Append("Please connect with a "); uint flags = 0; var i = 0; @@ -115,7 +117,7 @@ namespace Server.Misc public static void ClientVersionReceived(NetState state, ClientVersion version) { - using var sb = ValueStringBuilder.Create(); + var sb = ValueStringBuilder.Create(); if (!_enable || state.Mobile?.AccessLevel != AccessLevel.Player) { @@ -128,17 +130,17 @@ namespace Server.Misc state.Mobile is PlayerMobile mobile && mobile.GameTime > _gameTimeLeniency; - var shouldKick = false; - var isKRClient = version.Type == ClientType.KR; + bool shouldKick = false; + bool isKRClient = version.Type == ClientType.KR; - if (!isKRClient && UOClient.MinRequired != null && version < UOClient.MinRequired) + if (!isKRClient && MinRequired != null && version < MinRequired) { - sb.Append($"This server doesn't support clients older than {UOClient.MinRequired}."); + sb.Append($"This server doesn't support clients older than {MinRequired}."); shouldKick = strictRequirement; } - else if (!isKRClient && UOClient.MaxRequired != null && version > UOClient.MaxRequired) + else if (!isKRClient && MaxRequired != null && version > MaxRequired) { - sb.Append($"This server doesn't support clients newer than {UOClient.MaxRequired}."); + sb.Append($"This server doesn't support clients newer than {MaxRequired}."); shouldKick = strictRequirement; } else @@ -202,6 +204,8 @@ namespace Server.Misc } } } + + sb.Dispose(); } private static void OnKick(NetState ns) diff --git a/Projects/UOContent/Misc/CrashGuard.cs b/Projects/UOContent/Misc/CrashGuard.cs index 1a6e12cc1..b8c291bc8 100644 --- a/Projects/UOContent/Misc/CrashGuard.cs +++ b/Projects/UOContent/Misc/CrashGuard.cs @@ -65,19 +65,19 @@ namespace Server.Misc private static void DirectoryCopy(string sourceDirName, string destDirName) { // Get the subdirectories for the specified directory. - var dir = new DirectoryInfo(sourceDirName); - var dirs = dir.GetDirectories(); + DirectoryInfo dir = new DirectoryInfo(sourceDirName); + DirectoryInfo[] dirs = dir.GetDirectories(); - var files = dir.GetFiles(); - foreach (var file in files) + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) { - var tempPath = Path.Combine(destDirName, file.Name); + string tempPath = Path.Combine(destDirName, file.Name); file.CopyTo(tempPath, false); } - foreach (var subdir in dirs) + foreach (DirectoryInfo subdir in dirs) { - var tempPath = Path.Combine(destDirName, subdir.Name); + string tempPath = Path.Combine(destDirName, subdir.Name); DirectoryCopy(subdir.FullName, tempPath); } } diff --git a/Projects/UOContent/Misc/Guild.cs b/Projects/UOContent/Misc/Guild.cs index 88ec44773..3c9707160 100644 --- a/Projects/UOContent/Misc/Guild.cs +++ b/Projects/UOContent/Misc/Guild.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; using Server.Commands.Generic; using Server.Gumps; using Server.Items; @@ -191,7 +190,7 @@ namespace Server.Guilds public void TurnToMember(Guild g) { - if (g.Alliance != this || m_Members.Contains(g) || !m_PendingMembers.Remove(g)) + if (g.Alliance != this || !m_PendingMembers.Contains(g) || m_Members.Contains(g)) { return; } @@ -199,16 +198,21 @@ namespace Server.Guilds g.GuildMessage(1070760, Name); // Your Guild has joined the ~1_ALLIANCENAME~ Alliance. AllianceMessage(1070761, g.Name); // A new Guild has joined your Alliance: ~1_GUILDNAME~ + m_PendingMembers.Remove(g); m_Members.Add(g); g.Alliance.InvalidateMemberProperties(); } public void RemoveGuild(Guild g) { - m_PendingMembers.Remove(g); - - if (m_Members.Remove(g)) // Sanity, just incase someone with a custom script adds a character to BOTH arrays + if (m_PendingMembers.Contains(g)) { + m_PendingMembers.Remove(g); + } + + if (m_Members.Contains(g)) // Sanity, just incase someone with a custom script adds a character to BOTH arrays + { + m_Members.Remove(g); g.InvalidateMemberProperties(); g.GuildMessage(1070763, Name); // Your Guild has been removed from the ~1_ALLIANCENAME~ Alliance. @@ -306,7 +310,7 @@ namespace Server.Guilds public void AllianceChat(Mobile from, int hue, string text) { - var buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); for (var i = 0; i < m_Members.Count; i++) { @@ -346,6 +350,8 @@ namespace Server.Guilds public class AllianceRosterGump : GuildDiplomacyGump { + private readonly AllianceInfo m_Alliance; + public AllianceRosterGump(PlayerMobile pm, Guild g, AllianceInfo alliance) : base( pm, g, @@ -354,12 +360,32 @@ namespace Server.Guilds 0, alliance.m_Members, alliance.Name - ) - { - } + ) => + m_Alliance = alliance; + + public AllianceRosterGump( + PlayerMobile pm, Guild g, AllianceInfo alliance, IComparer currentComparer, + bool ascending, string filter, int startNumber + ) : base( + pm, + g, + currentComparer, + ascending, + filter, + startNumber, + alliance.m_Members, + alliance.Name + ) => + m_Alliance = alliance; protected override bool AllowAdvancedSearch => false; + public override Gump GetResentGump( + PlayerMobile pm, Guild g, IComparer comparer, bool ascending, + string filter, int startNumber + ) => + new AllianceRosterGump(pm, g, m_Alliance, comparer, ascending, filter, startNumber); + public override void OnResponse(NetState sender, in RelayInfo info) { if (info.ButtonID != 8) // So that they can't get to the AdvancedSearch button @@ -583,7 +609,7 @@ namespace Server.Guilds { get { - if (Disbanded) + if (Disbanded || m_Leader.Guild != this) { CalculateGuildmaster(); } @@ -751,7 +777,7 @@ namespace Server.Guilds public void InvalidateMemberProperties(bool onlyOPL = false) { - for (var i = 0; i < Members.Count; i++) + for (var i = 0; i < Members?.Count; i++) { var m = Members[i]; m.InvalidateProperties(); @@ -765,7 +791,7 @@ namespace Server.Guilds public void InvalidateMemberNotoriety() { - for (var i = 0; i < Members.Count; i++) + for (var i = 0; i < Members?.Count; i++) { Members[i].Delta(MobileDelta.Noto); } @@ -1088,7 +1114,7 @@ namespace Server.Guilds public static void Tidy(List list) { - for (var i = list.Count - 1; i >= 0; i--) + for (int i = list.Count - 1; i >= 0; i--) { var entry = list[i]; if (entry?.Disbanded != false) @@ -1194,14 +1220,14 @@ namespace Server.Guilds { var count = reader.ReadInt(); - PendingWars = new List(count); + PendingWars = new List(); for (var i = 0; i < count; i++) { PendingWars.Add(new WarDeclaration(reader)); } count = reader.ReadInt(); - AcceptedWars = new List(count); + AcceptedWars = new List(); for (var i = 0; i < count; i++) { AcceptedWars.Add(new WarDeclaration(reader)); @@ -1249,6 +1275,11 @@ namespace Server.Guilds { m_Leader = reader.ReadEntity(); + if (m_Leader is PlayerMobile mobile) + { + mobile.GuildRank = RankDefinition.Leader; + } + m_Name = reader.ReadString(); m_Abbreviation = reader.ReadString(); @@ -1303,70 +1334,66 @@ namespace Server.Guilds public void AddMember(Mobile m) { - if (Members.Contains(m)) + if (!Members.Contains(m)) { - return; + if (m.Guild != null && m.Guild != this) + { + ((Guild)m.Guild).RemoveMember(m); + } + + Members.Add(m); + m.Guild = this; + + m.GuildFealty = !NewGuildSystem ? m_Leader : null; + + if (m is PlayerMobile mobile) + { + mobile.GuildRank = RankDefinition.Lowest; + } + + ((Guild)m.Guild).InvalidateWarNotoriety(); } - - var oldGuild = m.Guild as Guild; - - if (oldGuild != this) - { - oldGuild?.RemoveMember(m); - } - - Members.Add(m); - m.Guild = this; - - m.GuildFealty = !NewGuildSystem ? m_Leader : null; - - if (m is PlayerMobile pm) - { - pm.GuildRank = RankDefinition.Lowest; - } - - oldGuild?.InvalidateWarNotoriety(); - InvalidateWarNotoriety(); } public void RemoveMember(Mobile m, int message = 1018028) // You have been dismissed from your guild. { - if (!Members.Remove(m)) + if (Members.Contains(m)) { - return; - } + Members.Remove(m); - var oldGuild = m.Guild as Guild; - m.Guild = null; + var guild = m.Guild as Guild; - if (m is PlayerMobile pm) - { - pm.GuildRank = RankDefinition.Lowest; - } + m.Guild = null; - if (message > 0) - { - m.SendLocalizedMessage(message); - } + if (m is PlayerMobile mobile) + { + mobile.GuildRank = RankDefinition.Lowest; + } - if (m == m_Leader) - { - CalculateGuildmaster(); + if (message > 0) + { + m.SendLocalizedMessage(message); + } - if (m_Leader == null) + if (m == m_Leader) + { + CalculateGuildmaster(); + + if (m_Leader == null) + { + Disband(); + } + } + + if (Members.Count == 0) { Disband(); } + + guild?.InvalidateWarNotoriety(); + + m.Delta(MobileDelta.Noto); } - - if (Members.Count == 0) - { - Disband(); - } - - oldGuild?.InvalidateWarNotoriety(); - - m.Delta(MobileDelta.Noto); } public void AddAlly(Guild g) @@ -1381,8 +1408,10 @@ namespace Server.Guilds public void RemoveAlly(Guild g) { - if (Allies.Remove(g)) + if (Allies.Contains(g)) { + Allies.Remove(g); + g.RemoveAlly(this); } } @@ -1399,8 +1428,10 @@ namespace Server.Guilds public void RemoveEnemy(Guild g) { - if (Enemies.Remove(g)) + if (Enemies.Contains(g)) { + Enemies.Remove(g); + g.RemoveEnemy(this); } } @@ -1441,7 +1472,7 @@ namespace Server.Guilds public void GuildChat(Mobile from, int hue, string text) { - var buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); for (var i = 0; i < Members.Count; i++) { @@ -1476,12 +1507,9 @@ namespace Server.Guilds { var votes = new Dictionary(); - // When the leader resigns, this will be false - var disbanded = Disbanded; - var hasLeader = !disbanded && m_Leader?.Guild == this; var votingMembers = 0; - for (var i = 0; i < Members.Count; ++i) + for (var i = 0; i < Members?.Count; ++i) { var memb = Members[i]; @@ -1494,7 +1522,14 @@ namespace Server.Guilds if (!CanBeVotedFor(m)) { - m = hasLeader ? m_Leader : memb; + if (!Disbanded && m_Leader.Guild == this) + { + m = m_Leader; + } + else + { + m = memb; + } } if (m == null) @@ -1502,16 +1537,18 @@ namespace Server.Guilds continue; } - ref var voteCount = ref CollectionsMarshal.GetValueRefOrAddDefault(votes, m, out _); - voteCount++; + votes[m] = 1 + (votes.TryGetValue(m, out var v) ? v : 0); votingMembers++; } Mobile winner = null; var highVotes = 0; - foreach (var (m, val) in votes) + foreach (var kvp in votes) { + var m = kvp.Key; + var val = kvp.Value; + if (winner == null || val > highVotes) { winner = m; @@ -1519,34 +1556,13 @@ namespace Server.Guilds } } - if (hasLeader && (winner == null || - NewGuildSystem && highVotes * 100 / Math.Max(votingMembers, 1) < MajorityPercentage)) + if (NewGuildSystem && highVotes * 100 / Math.Max(votingMembers, 1) < MajorityPercentage && !Disbanded && + winner != m_Leader && m_Leader.Guild == this) { winner = m_Leader; } - if (winner == null) - { - if (votes.Count > 0) - { - var randomNumber = Utility.Random(votes.Count); - var index = 0; - foreach (var m in votes.Keys) - { - if (index++ == randomNumber) - { - winner = m; - break; - } - } - } - else - { - winner = Members.RandomElement(); - } - } - - if (winner != null && m_Leader != winner) + if (m_Leader != winner && winner != null) { Leader = winner; GuildMessage(1018015, true, winner.RawName); // Guild Message: Guildmaster changed to: diff --git a/Projects/UOContent/Misc/InhumanSpeech.cs b/Projects/UOContent/Misc/InhumanSpeech.cs index 4aafad2f8..41f0e51b7 100644 --- a/Projects/UOContent/Misc/InhumanSpeech.cs +++ b/Projects/UOContent/Misc/InhumanSpeech.cs @@ -360,10 +360,10 @@ namespace Server.Misc return sentence.ToString(); } - public void SayRandomTranslate(Mobile mob, params ReadOnlySpan sentencesInEnglish) + public void SayRandomTranslate(Mobile mob, params string[] sentancesInEnglish) { SaySentance(mob, Utility.RandomMinMax(2, 3)); - mob.Say(sentencesInEnglish.RandomElement()); + mob.Say(sentancesInEnglish.RandomElement()); } private string GetRandomResponseWord(List keywordsFound) diff --git a/Projects/UOContent/Misc/Keywords.cs b/Projects/UOContent/Misc/Keywords.cs index b0b897048..541cf9e0c 100644 --- a/Projects/UOContent/Misc/Keywords.cs +++ b/Projects/UOContent/Misc/Keywords.cs @@ -1,4 +1,3 @@ -using Server.Engines.PlayerMurderSystem; using Server.Guilds; using Server.Gumps; using Server.Mobiles; @@ -32,7 +31,15 @@ namespace Server.Misc { if (from is PlayerMobile player) { - PlayerMurderSystem.ReportKillsToSelf(player); + if (!Core.SE) + { + from.SendMessage($"Short Term Murders : {player.ShortTermMurders}"); + from.SendMessage($"Long Term Murders : {from.Kills}"); + } + else + { + from.SendLocalizedMessage(1114370, $"{player.ShortTermMurders}\t{from.Kills}"); + } } break; diff --git a/Projects/UOContent/Misc/LightCycle.cs b/Projects/UOContent/Misc/LightCycle.cs index 3c25b696a..441fefeb2 100644 --- a/Projects/UOContent/Misc/LightCycle.cs +++ b/Projects/UOContent/Misc/LightCycle.cs @@ -1,6 +1,5 @@ using System; using ModernUO.CodeGeneratedEvents; -using Server.Engines.BuffIcons; using Server.Items; using Server.Mobiles; using Server.Network; @@ -121,7 +120,7 @@ namespace Server { m_Owner.EndAction(); m_Owner.LightLevel = 0; - (m_Owner as PlayerMobile)?.RemoveBuff(BuffIcon.NightSight); + BuffInfo.RemoveBuff(m_Owner, BuffIcon.NightSight); } } } diff --git a/Projects/UOContent/Misc/Loot.cs b/Projects/UOContent/Misc/Loot.cs index 2477c25b5..4c597586c 100644 --- a/Projects/UOContent/Misc/Loot.cs +++ b/Projects/UOContent/Misc/Loot.cs @@ -6,17 +6,6 @@ namespace Server { public static class Loot { - public static Type[] SAWeaponTypes { get; } = - { - typeof(DiscMace), typeof(GargishTalwar), typeof(DualPointedSpear), - typeof(GlassStaff), typeof(DualShortAxes), typeof(GlassSword) - }; - - public static Type[] SARangedWeaponTypes { get; } = - { - typeof(Boomerang), typeof(Cyclone), typeof(SoulGlaive) - }; - public static Type[] MLWeaponTypes { get; } = { typeof(AssassinSpike), typeof(DiamondMace), typeof(ElvenMachete), @@ -385,18 +374,12 @@ namespace Server return Construct(ClothingTypes); } - private static readonly Type[][] _saRangedWeaponTypes = [SARangedWeaponTypes, AosRangedWeaponTypes, RangedWeaponTypes]; private static readonly Type[][] _mlRangedWeaponTypes = [MLRangedWeaponTypes, AosRangedWeaponTypes, RangedWeaponTypes]; private static readonly Type[][] _seRangedWeaponTypes = [SERangedWeaponTypes, AosRangedWeaponTypes, RangedWeaponTypes]; private static readonly Type[][] _aosRangedWeaponTypes = [AosRangedWeaponTypes, RangedWeaponTypes]; - public static BaseWeapon RandomRangedWeapon(bool inTokuno = false, bool isMondain = false, bool isStygian = false) + public static BaseWeapon RandomRangedWeapon(bool inTokuno = false, bool isMondain = false) { - if (Core.SA && isStygian) - { - return Construct(_saRangedWeaponTypes); - } - if (Core.ML && isMondain) { return Construct(_mlRangedWeaponTypes); @@ -415,18 +398,12 @@ namespace Server return Construct(RangedWeaponTypes); } - private static readonly Type[][] _saWeaponTypes = [SAWeaponTypes, AosWeaponTypes, WeaponTypes]; private static readonly Type[][] _mlWeaponTypes = [MLWeaponTypes, AosWeaponTypes, WeaponTypes]; private static readonly Type[][] _seWeaponTypes = [SEWeaponTypes, AosWeaponTypes, WeaponTypes]; private static readonly Type[][] _aosWeaponTypes = [AosWeaponTypes, WeaponTypes]; - public static BaseWeapon RandomWeapon(bool inTokuno = false, bool isMondain = false, bool isStygian = false) + public static BaseWeapon RandomWeapon(bool inTokuno = false, bool isMondain = false) { - if (Core.SA && isStygian) - { - return Construct(_saWeaponTypes); - } - if (Core.ML && isMondain) { return Construct(_mlWeaponTypes); @@ -570,33 +547,33 @@ namespace Server return Construct(_oldArmorOrShieldTypes); } - private static readonly Type[][] _mlArmorOrHatOrShieldOrJewelryTypes = + private static readonly Type[][] _mlArmorOrHatOrShieldOrJeweleyTypes = [MLArmorTypes, ArmorTypes, AosHatTypes, HatTypes, AosShieldTypes, ShieldTypes, JewelryTypes]; - private static readonly Type[][] _seArmorOrHatOrShieldOrJewelryTypes = + private static readonly Type[][] _seArmorOrHatOrShieldOrJeweleyTypes = [SEArmorTypes, ArmorTypes, SEHatTypes, AosHatTypes, HatTypes, AosShieldTypes, ShieldTypes, JewelryTypes]; - private static readonly Type[][] _aosArmorOrHatOrShieldOrJewelryTypes = + private static readonly Type[][] _aosArmorOrHatOrShieldOrJeweleyTypes = [ArmorTypes, AosHatTypes, HatTypes, AosShieldTypes, ShieldTypes, JewelryTypes]; - private static readonly Type[][] _oldArmorOrHatOrShieldOrJewelryTypes = + private static readonly Type[][] _oldArmorOrHatOrShieldOrJeweleyTypes = [ArmorTypes, HatTypes, ShieldTypes, JewelryTypes]; public static Item RandomArmorOrShieldOrJewelry(bool inTokuno = false, bool isMondain = false) { if (Core.ML && isMondain) { - return Construct(_mlArmorOrHatOrShieldOrJewelryTypes); + return Construct(_mlArmorOrHatOrShieldOrJeweleyTypes); } if (Core.SE && inTokuno) { - return Construct(_seArmorOrHatOrShieldOrJewelryTypes); + return Construct(_seArmorOrHatOrShieldOrJeweleyTypes); } if (Core.AOS) { - return Construct(_aosArmorOrHatOrShieldOrJewelryTypes); + return Construct(_aosArmorOrHatOrShieldOrJeweleyTypes); } - return Construct(_oldArmorOrHatOrShieldOrJewelryTypes); + return Construct(_oldArmorOrHatOrShieldOrJeweleyTypes); } private static readonly Type[][] _mlWeaponOrRangedOrArmorOrHatOrShieldTypes = @@ -789,15 +766,15 @@ namespace Server } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Item Construct(params ReadOnlySpan types) => Construct(types.RandomElement()); + public static Item Construct(params Type[] types) => Construct(types.RandomElement()); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Construct(params ReadOnlySpan types) where T : Item => Construct(types.RandomElement()); + public static T Construct(params Type[] types) where T : Item => Construct(types.RandomElement()); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Item Construct(ReadOnlySpan types, int index) => Construct(types, index); + public static Item Construct(Type[] types, int index) => Construct(types, index); - public static T Construct(ReadOnlySpan types, int index) where T : Item + public static T Construct(Type[] types, int index) where T : Item { if (index >= 0 && index < types.Length) { diff --git a/Projects/UOContent/Misc/LootPack.cs b/Projects/UOContent/Misc/LootPack.cs index 92cb06eb2..6f86f17d0 100644 --- a/Projects/UOContent/Misc/LootPack.cs +++ b/Projects/UOContent/Misc/LootPack.cs @@ -619,9 +619,6 @@ namespace Server private static bool IsMondain(Mobile m) => MondainsLegacy.IsMLRegion(m.Region); - // Stygian Abyss loot flavor: creatures in Ter Mur draw from the SA gear pools. - private static bool IsStygian(Mobile m) => m.Map == Map.TerMur; - public Item Construct(Mobile from, int luckChance, bool spawning) { if (m_AtSpawnTime != spawning) @@ -644,7 +641,7 @@ namespace Server if (rnd < item.Chance) { - return Mutate(from, luckChance, item.Construct(IsInTokuno(from), IsMondain(from), IsStygian(from))); + return Mutate(from, luckChance, item.Construct(IsInTokuno(from), IsMondain(from))); } rnd -= item.Chance; @@ -767,7 +764,8 @@ namespace Server { weapon.Slayer = SlayerName.Silver; } - else if (from != null && weapon.AccuracyLevel == 0 && weapon.DamageLevel == 0 && + + if (from != null && weapon.AccuracyLevel == 0 && weapon.DamageLevel == 0 && weapon.DurabilityLevel == 0 && weapon.Slayer == SlayerName.None && Utility.Random(100) < 5) { weapon.Slayer = SlayerGroup.GetLootSlayerType(from.GetType()); @@ -973,18 +971,18 @@ namespace Server return Loot.RandomScroll(minCircle * 8, maxCircle * 8 + 7, SpellbookType.Regular); } - public Item Construct(bool inTokuno, bool isMondain, bool isStygian) + public Item Construct(bool inTokuno, bool isMondain) { try { if (Type == typeof(BaseRanged)) { - return Loot.RandomRangedWeapon(inTokuno, isMondain, isStygian); + return Loot.RandomRangedWeapon(inTokuno, isMondain); } if (Type == typeof(BaseWeapon)) { - return Loot.RandomWeapon(inTokuno, isMondain, isStygian); + return Loot.RandomWeapon(inTokuno, isMondain); } if (Type == typeof(BaseArmor)) diff --git a/Projects/UOContent/Misc/MessageHelper.cs b/Projects/UOContent/Misc/MessageHelper.cs new file mode 100644 index 000000000..82eabc37f --- /dev/null +++ b/Projects/UOContent/Misc/MessageHelper.cs @@ -0,0 +1,21 @@ +using Server.Network; + +namespace Server; + +public static class MessageHelper +{ + public static void SendLocalizedMessageTo(this Item from, Mobile to, int number, int hue) + { + SendLocalizedMessageTo(from, to, number, "", hue); + } + + public static void SendLocalizedMessageTo(this Item from, Mobile to, int number, string args, int hue) + { + to?.NetState.SendMessageLocalized(from.Serial, from.ItemID, MessageType.Regular, hue, 3, number, "", args); + } + + public static void SendMessageTo(this Item from, Mobile to, string text, int hue) + { + to?.NetState.SendMessage(from.Serial, from.ItemID, MessageType.Regular, hue, 3, false, "ENU", "", text); + } +} diff --git a/Projects/UOContent/Misc/NameVerification.cs b/Projects/UOContent/Misc/NameVerification.cs index d35c2b883..e64c41bf9 100644 --- a/Projects/UOContent/Misc/NameVerification.cs +++ b/Projects/UOContent/Misc/NameVerification.cs @@ -1,295 +1,247 @@ using System; -using System.Buffers; -using System.Runtime.CompilerServices; -namespace Server.Misc; - -public static class NameVerification +namespace Server.Misc { - public static readonly SearchValues AlphaNumeric = SearchValues.Create( - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', - 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', - 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', - 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', - 'u', 'v', 'w', 'x', 'y', 'z' - ); + public static class NameVerification + { + public static readonly char[] SpaceDashPeriodQuote = + { + ' ', '-', '.', '\'' + }; - public static readonly SearchValues Alphabetic = SearchValues.Create( - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', - 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', - 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', - 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', - 'u', 'v', 'w', 'x', 'y', 'z' - ); + public static readonly char[] Empty = Array.Empty(); - public static readonly SearchValues Numeric = SearchValues.Create( - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' - ); - - public static readonly SearchValues SpaceDashPeriodQuote = SearchValues.Create(' ', '-', '.', '\''); - - public static readonly SearchValues StartDisallowed = SearchValues.Create( - [ + public static string[] StartDisallowed { get; } = + { "seer", "counselor", "gm", "admin", "lady", "lord" - ], - StringComparison.OrdinalIgnoreCase - ); + }; - public static readonly string[] Disallowed = - [ - ..ProfanityProtection.Disallowed, - "tailor", - "smith", - "scholar", - "rogue", - "novice", - "neophyte", - "merchant", - "medium", - "master", - "mage", - "lb", - "journeyman", - "grandmaster", - "fisherman", - "expert", - "chef", - "carpenter", - "british", - "blackthorne", - "blackthorn", - "beggar", - "archer", - "apprentice", - "adept", - "gamemaster", - "frozen", - "squelched", - "invulnerable", - "osi", - "origin" - ]; - - public static readonly SearchValues DisallowedSearchValues = SearchValues.Create( - Disallowed, - StringComparison.OrdinalIgnoreCase - ); - - public static void Configure() - { - CommandSystem.Register("ValidateName", AccessLevel.Administrator, ValidateName_OnCommand); - } - - [Usage("ValidateName"), Description("Checks the result of NameValidation on the specified name.")] - public static void ValidateName_OnCommand(CommandEventArgs e) - { - if (Validate(e.ArgString, 2, 16, true, false, true, 1, SpaceDashPeriodQuote)) + public static string[] Disallowed { get; } = { - e.Mobile.SendMessage(0x59, "That name is considered valid."); - } - else + "jigaboo", + "chigaboo", + "wop", + "kyke", + "kike", + "tit", + "spic", + "prick", + "piss", + "lezbo", + "lesbo", + "felatio", + "dyke", + "dildo", + "chinc", + "chink", + "cunnilingus", + "cum", + "cocksucker", + "cock", + "clitoris", + "clit", + "ass", + "hitler", + "penis", + "nigga", + "nigger", + "klit", + "kunt", + "jiz", + "jism", + "jerkoff", + "jackoff", + "goddamn", + "fag", + "blowjob", + "bitch", + "asshole", + "dick", + "pussy", + "snatch", + "cunt", + "twat", + "shit", + "fuck", + "tailor", + "smith", + "scholar", + "rogue", + "novice", + "neophyte", + "merchant", + "medium", + "master", + "mage", + "lb", + "journeyman", + "grandmaster", + "fisherman", + "expert", + "chef", + "carpenter", + "british", + "blackthorne", + "blackthorn", + "beggar", + "archer", + "apprentice", + "adept", + "gamemaster", + "frozen", + "squelched", + "invulnerable", + "osi", + "origin" + }; + + public static void Configure() { - e.Mobile.SendMessage(0x22, "That name is considered invalid."); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ValidatePlayerName(ReadOnlySpan name) => - Validate(name, 2, 16, true, false, true, 1, SpaceDashPeriodQuote); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ValidatePetName(ReadOnlySpan name) => Validate(name, 1, 16, true, false, exceptions: null); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ValidateVendorName(ReadOnlySpan name) => Validate(name, 1, 20, true, true); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Validate( - ReadOnlySpan name, int minLength, int maxLength, bool allowLetters, bool allowDigits, - bool noExceptionsAtStart = true, int maxExceptions = 0, SearchValues exceptions = null - ) => Validate( - name, - minLength, - maxLength, - allowLetters, - allowDigits, - noExceptionsAtStart, - maxExceptions, - exceptions, - Disallowed, - DisallowedSearchValues, - StartDisallowed - ); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Validate( - ReadOnlySpan name, int minLength, int maxLength, bool allowLetters, bool allowDigits, - bool noExceptionsAtStart, int maxExceptions, SearchValues exceptions, ReadOnlySpan disallowed, - SearchValues disallowedSV - ) => Validate( - name, - minLength, - maxLength, - allowLetters, - allowDigits, - noExceptionsAtStart, - maxExceptions, - exceptions, - disallowed, - disallowedSV, - null - ); - - public static bool Validate( - ReadOnlySpan name, int minLength, int maxLength, bool allowLetters, bool allowDigits, - bool noExceptionsAtStart, int maxExceptions, SearchValues exceptions, ReadOnlySpan disallowed, - SearchValues disallowedSV, SearchValues startDisallowedSV - ) - { - if (name.Length == 0 || name.Length < minLength || name.Length > maxLength) - { - return false; + CommandSystem.Register("ValidateName", AccessLevel.Administrator, ValidateName_OnCommand); } - if (exceptions == null) + [Usage("ValidateName"), Description("Checks the result of NameValidation on the specified name.")] + public static void ValidateName_OnCommand(CommandEventArgs e) { - // We don't have exceptions, so we might be limited to letters or numbers - var allowed = allowLetters switch + if (Validate(e.ArgString, 2, 16, true, false, true, 1, SpaceDashPeriodQuote)) { - // If we don't allow exceptions, then non-alphanumeric is not allowed - true when allowDigits && maxExceptions == 0 => AlphaNumeric, - true when !allowDigits => Alphabetic, - false when allowDigits => Numeric, - // Everything has been allowed! Use `Utility.FixHtml()` to stop weird behavior - _ => null - }; - - if (allowed != null && name.ContainsAnyExcept(allowed)) - { - return false; - } - } - else - { - // We have exceptions, and at least one of the letters/digits flag is false: - var notAllowed = allowLetters switch - { - true when !allowDigits => Numeric, - false when allowDigits => Alphabetic, - _ => null - }; - - if (notAllowed != null && name.ContainsAny(notAllowed)) - { - return false; - } - - if (ContainsExceptions(name, exceptions, noExceptionsAtStart, maxExceptions)) - { - return false; - } - } - - if (disallowedSV != null && disallowed.Length > 0 && ContainsDisallowedWord(name, disallowed, disallowedSV)) - { - return false; - } - - return startDisallowedSV == null || name.IndexOfAny(startDisallowedSV) != 0; - } - - public static bool ContainsExceptions( - ReadOnlySpan name, SearchValues exceptions, bool noExceptionsAtStart, int maxExceptions - ) - { - if (!noExceptionsAtStart && maxExceptions is <= -1 or >= int.MaxValue) - { - return false; - } - - var index = name.IndexOfAny(exceptions); - var exceptionCount = 0; - - while (index != -1) - { - if (noExceptionsAtStart) - { - if (index == 0) - { - return true; - } - - noExceptionsAtStart = false; - } - - if (exceptionCount++ >= maxExceptions) - { - return true; - } - - if (index + 1 < name.Length) - { - name = name[(index + 1)..]; - index = name.IndexOfAny(exceptions); - if (index != 0) - { - exceptionCount = 0; - } + e.Mobile.SendMessage(0x59, "That name is considered valid."); } else { - index = -1; + e.Mobile.SendMessage(0x22, "That name is considered invalid."); } } - return false; - } + public static bool Validate( + string name, int minLength, int maxLength, bool allowLetters, bool allowDigits, + bool noExceptionsAtStart, int maxExceptions, char[] exceptions + ) => + Validate( + name, + minLength, + maxLength, + allowLetters, + allowDigits, + noExceptionsAtStart, + maxExceptions, + exceptions, + Disallowed, + StartDisallowed + ); - public static bool ContainsDisallowedWord(ReadOnlySpan name, ReadOnlySpan disallowed, SearchValues disallowedSV) - { - var index = name.IndexOfAny(disallowedSV); - - while (index != -1) + public static bool Validate( + string name, int minLength, int maxLength, bool allowLetters, bool allowDigits, + bool noExceptionsAtStart, int maxExceptions, char[] exceptions, string[] disallowed, string[] startDisallowed + ) { - var isStartBoundary = index == 0 || !char.IsLetterOrDigit(name[index - 1]); - - if (isStartBoundary) + if (name == null || name.Length < minLength || name.Length > maxLength) { - for (var i = 0; i < disallowed.Length; i++) - { - var word = disallowed[i].AsSpan(); - if (index + word.Length > name.Length || !name.Slice(index, word.Length).InsensitiveEquals(word)) - { - continue; - } + return false; + } - // End boundary - if (index + word.Length == name.Length || !char.IsLetterOrDigit(name[index + word.Length])) + var exceptCount = 0; + + name = name.ToLower(); + + if (!allowLetters || !allowDigits || + exceptions.Length > 0 && (noExceptionsAtStart || maxExceptions < int.MaxValue)) + { + for (var i = 0; i < name.Length; ++i) + { + var c = name[i]; + + if (c >= 'a' && c <= 'z') { - return true; + if (!allowLetters) + { + return false; + } + + exceptCount = 0; + } + else if (c >= '0' && c <= '9') + { + if (!allowDigits) + { + return false; + } + + exceptCount = 0; + } + else + { + var except = false; + + for (var j = 0; !except && j < exceptions.Length; ++j) + { + if (c == exceptions[j]) + { + except = true; + } + } + + if (!except || i == 0 && noExceptionsAtStart) + { + return false; + } + + if (exceptCount++ == maxExceptions) + { + return false; + } } } } - if (index + 1 < name.Length) + for (var i = 0; i < disallowed.Length; ++i) { - name = name[(index + 1)..]; - index = name.IndexOfAny(disallowedSV); - } - else - { - index = -1; - } - } + var indexOf = name.IndexOfOrdinal(disallowed[i]); - return false; + if (indexOf == -1) + { + continue; + } + + var badPrefix = indexOf == 0; + + for (var j = 0; !badPrefix && j < exceptions.Length; ++j) + { + badPrefix = name[indexOf - 1] == exceptions[j]; + } + + if (!badPrefix) + { + continue; + } + + var badSuffix = indexOf + disallowed[i].Length >= name.Length; + + for (var j = 0; !badSuffix && j < exceptions.Length; ++j) + { + badSuffix = name[indexOf + disallowed[i].Length] == exceptions[j]; + } + + if (badSuffix) + { + return false; + } + } + + for (var i = 0; i < startDisallowed.Length; ++i) + { + if (name.StartsWithOrdinal(startDisallowed[i])) + { + return false; + } + } + + return true; + } } } diff --git a/Projects/UOContent/Misc/Notoriety.cs b/Projects/UOContent/Misc/Notoriety.cs index cb9b1ddcb..e86c81ecc 100644 --- a/Projects/UOContent/Misc/Notoriety.cs +++ b/Projects/UOContent/Misc/Notoriety.cs @@ -44,8 +44,15 @@ namespace Server.Misc return GuildStatus.Waring; } - private static bool CheckBeneficialStatus(GuildStatus from, GuildStatus target) => - from != GuildStatus.Waring && target != GuildStatus.Waring; + private static bool CheckBeneficialStatus(GuildStatus from, GuildStatus target) + { + if (from == GuildStatus.Waring || target == GuildStatus.Waring) + { + return false; + } + + return true; + } /*private static bool CheckHarmfulStatus( GuildStatus from, GuildStatus target ) { @@ -63,16 +70,20 @@ namespace Server.Misc return true; } - if (from.Region.IsPartOf() || target.Region.IsPartOf()) - { - return false; - } - var bcFrom = from as BaseCreature; var bcTarg = target as BaseCreature; + var pmFrom = from as PlayerMobile; + var pmTarg = target as PlayerMobile; - var pmFrom = (bcFrom?.GetMaster() ?? from) as PlayerMobile; - var pmTarg = (bcTarg?.GetMaster() ?? target) as PlayerMobile; + if (pmFrom == null && bcFrom?.Summoned == true) + { + pmFrom = bcFrom.SummonMaster as PlayerMobile; + } + + if (pmTarg == null && bcTarg?.Summoned == true) + { + pmTarg = bcTarg.SummonMaster as PlayerMobile; + } if (pmFrom != null && pmTarg != null) { @@ -113,6 +124,11 @@ namespace Server.Misc return false; } + if (from.Region.IsPartOf() || target.Region.IsPartOf()) + { + return false; + } + var map = from.Map; var targetFaction = Faction.Find(target, true); @@ -127,7 +143,7 @@ namespace Server.Misc return true; // In felucca, anything goes } - if (!from.Player && pmFrom?.AccessLevel != AccessLevel.Player) + if (!from.Player) { return true; // NPCs have no restrictions } @@ -137,9 +153,9 @@ namespace Server.Misc return false; // Players cannot heal uncontrolled mobiles } - if (pmFrom?.Young == true && pmTarg?.Young == false) + if (pmFrom?.Young == true && pmTarg?.Young != true) { - return false; // Young players cannot perform beneficial actions towards non-young players or pets + return false; // Young players cannot perform beneficial actions towards older players } if (from.Guild is Guild fromGuild && target.Guild is Guild targetGuild && @@ -159,16 +175,20 @@ namespace Server.Misc return true; } - if (from.Region.IsPartOf() || target.Region.IsPartOf()) - { - return false; - } - var bcFrom = from as BaseCreature; + var pmFrom = from as PlayerMobile; + var pmTarg = target as PlayerMobile; var bcTarg = target as BaseCreature; - var pmFrom = (bcFrom?.GetMaster() ?? from) as PlayerMobile; - var pmTarg = (bcTarg?.GetMaster() ?? target) as PlayerMobile; + if (pmFrom == null && bcFrom?.Summoned == true) + { + pmFrom = bcFrom.SummonMaster as PlayerMobile; + } + + if (pmTarg == null && bcTarg?.Summoned == true) + { + pmTarg = bcTarg.SummonMaster as PlayerMobile; + } if (pmFrom != null && pmTarg != null) { @@ -204,6 +224,11 @@ namespace Server.Misc return false; } + if (from.Region.IsPartOf() || target.Region.IsPartOf()) + { + return false; + } + var map = from.Map; if ((map?.Rules & MapRules.HarmfulRestrictions) == 0) @@ -211,11 +236,16 @@ namespace Server.Misc return true; // In felucca, anything goes } - if (!from.Player && pmFrom?.AccessLevel != AccessLevel.Player) + if (!from.Player && !(from is BaseCreature bc && bc.GetMaster() != null && + bc.GetMaster().AccessLevel == AccessLevel.Player)) { - // Uncontrolled NPCs are only restricted by the young system - return CheckAggressor(from.Aggressors, target) || CheckAggressed(from.Aggressed, target) || - (target as PlayerMobile)?.CheckYoungProtection(from) != true; + if (!CheckAggressor(from.Aggressors, target) && !CheckAggressed(from.Aggressed, target) && + pmTarg?.CheckYoungProtection(from) == true) + { + return false; + } + + return true; // Uncontrolled NPCs are only restricted by the young system } var fromGuild = GetGuildFor(from.Guild as Guild, from); @@ -228,7 +258,7 @@ namespace Server.Misc } if (bcTarg?.Controlled == true - || bcTarg?.Summoned == true && bcTarg.SummonMaster != from && bcTarg.SummonMaster.Player) + || (bcTarg?.Summoned == true && bcTarg.SummonMaster != from && bcTarg.SummonMaster.Player)) { return false; // Cannot harm other controlled mobiles from players } @@ -248,24 +278,23 @@ namespace Server.Misc public static Guild GetGuildFor(Guild def, Mobile m) { - if (m is not BaseCreature c || !c.Controlled || c.ControlMaster == null) + var g = def; + + if (m is BaseCreature c && c.Controlled && c.ControlMaster != null) { - return def; + c.DisplayGuildTitle = false; + + if (c.Map != Map.Internal && (Core.AOS || Guild.NewGuildSystem || c.ControlOrder is OrderType.Attack or OrderType.Guard)) + { + g = (Guild)(c.Guild = c.ControlMaster.Guild); + } + else if (c.Map == Map.Internal || c.ControlMaster.Guild == null) + { + g = (Guild)(c.Guild = null); + } } - c.DisplayGuildTitle = false; - - if (c.Map != Map.Internal && (Core.AOS || Guild.NewGuildSystem || c.ControlOrder is OrderType.Attack or OrderType.Guard)) - { - return (Guild)(c.Guild = c.ControlMaster.Guild); - } - - if (c.Map == Map.Internal || c.ControlMaster.Guild == null) - { - return (Guild)(c.Guild = null); - } - - return def; + return g; } public static int CorpseNotoriety(Mobile source, Corpse target) @@ -297,25 +326,22 @@ namespace Server.Misc } } - // BaseCreatures are deleted on death, so target.Owner is null after a server restart. - // The OwnerWasBaseCreature flag is the persisted snapshot that survives the live mobile. - var creature = target.Owner as BaseCreature; - - if (target.OwnerWasBaseCreature || creature != null) + if (target.Owner is BaseCreature creature) { if (srcFaction != null && trgFaction != null && srcFaction != trgFaction && source.Map == Faction.Facet) { return Notoriety.Enemy; } - if (creature != null && CheckHouseFlag(source, creature, target.Location, target.Map)) + if (CheckHouseFlag(source, creature, target.Location, target.Map)) { return Notoriety.CanBeAttacked; } var actual = Notoriety.CanBeAttacked; - if (target.Murderer || body.IsMonster && target.OwnerWasSummoned || target.OwnerWasAnimatedDead) + if (target.Kills >= 5 || body.IsMonster && IsSummoned(creature) || creature.AlwaysMurderer || + creature.IsAnimatedDead) { actual = Notoriety.Murderer; } @@ -338,7 +364,7 @@ namespace Server.Misc return Notoriety.Innocent; } - if (target.Murderer || body.IsMonster) + if (target.Kills >= 5 || body.IsMonster) { return Notoriety.Murderer; } @@ -390,12 +416,6 @@ namespace Server.Misc return Notoriety.Invulnerable; } - // Moved above AccessLevel check so staff summons are red - if (bcTarg?.AlwaysMurderer == true) - { - return Notoriety.Murderer; - } - var pmFrom = source as PlayerMobile; var pmTarg = target as PlayerMobile; @@ -441,9 +461,9 @@ namespace Server.Misc } } - if (target.Murderer || + if (target.Kills >= 5 || target.Body.IsMonster && IsSummoned(bcTarg) && target is not BaseFamiliar && target is not ArcaneFey && - target is not Golem || bcTarg?.IsAnimatedDead == true) + target is not Golem || bcTarg?.AlwaysMurderer == true || bcTarg?.IsAnimatedDead == true) { return Notoriety.Murderer; } @@ -477,8 +497,17 @@ namespace Server.Misc return Notoriety.Enemy; } - if (Stealing.ClassicMode && pmTarg?.PermaFlags.Contains(source) == true || - bcTarg?.AlwaysAttackable == true || CheckHouseFlag(source, target, target.Location, target.Map)) + if (Stealing.ClassicMode && pmTarg?.PermaFlags.Contains(source) == true) + { + return Notoriety.CanBeAttacked; + } + + if (bcTarg?.AlwaysAttackable == true) + { + return Notoriety.CanBeAttacked; + } + + if (CheckHouseFlag(source, target, target.Location, target.Map)) { return Notoriety.CanBeAttacked; } diff --git a/Projects/UOContent/Misc/PacketThrottles.cs b/Projects/UOContent/Misc/PacketThrottles.cs index fe0af280a..9bd76ee75 100644 --- a/Projects/UOContent/Misc/PacketThrottles.cs +++ b/Projects/UOContent/Misc/PacketThrottles.cs @@ -45,7 +45,7 @@ public static class PacketThrottles Delays[0x75] = 500; // Rename request } - for (var i = 0; i < 0x100; i++) + for (int i = 0; i < 0x100; i++) { if (Delays[i] > 0) { @@ -66,7 +66,7 @@ public static class PacketThrottles return; } - var packetID = e.GetInt32(0); + int packetID = e.GetInt32(0); if (packetID is < 0 or > 0x100) { @@ -87,8 +87,8 @@ public static class PacketThrottles return; } - var packetID = e.GetInt32(0); - var delay = e.GetInt32(1); + int packetID = e.GetInt32(0); + int delay = e.GetInt32(1); if (packetID is < 0 or > 0x100) { diff --git a/Projects/UOContent/Misc/Poison.cs b/Projects/UOContent/Misc/Poison.cs index 4147fbad7..e9d11fc72 100644 --- a/Projects/UOContent/Misc/Poison.cs +++ b/Projects/UOContent/Misc/Poison.cs @@ -6,156 +6,157 @@ using Server.Spells; using Server.Spells.Necromancy; using Server.Spells.Ninjitsu; -namespace Server; - -public class PoisonImpl : Poison +namespace Server { - private readonly int _count; - private readonly TimeSpan _delay; - private readonly TimeSpan _interval; - private readonly int _maximum; - private readonly int _messageInterval; - private readonly int _minimum; - private readonly double _scalar; - - public PoisonImpl( - string name, int index, int level, int min, int max, double percent, double delay, double interval, int count, - int messageInterval, PoisonFamily family = PoisonFamily.Standard - ) : base(index) + public class PoisonImpl : Poison { - Name = name; - Level = level; - Family = family; - _minimum = min; - _maximum = max; - _scalar = percent * 0.01; - _delay = TimeSpan.FromSeconds(delay); - _interval = TimeSpan.FromSeconds(interval); - _count = count; - _messageInterval = messageInterval; - } + private readonly int m_Count; - public override string Name { get; } + // Timers + private readonly TimeSpan m_Delay; + private readonly TimeSpan m_Interval; + private readonly int m_Maximum; + private readonly int m_MessageInterval; - public override int Level { get; } + // Info - public override PoisonFamily Family { get; } + // Damage + private readonly int m_Minimum; + private readonly double m_Scalar; - public override Timer ConstructTimer(Mobile m) => new PoisonTimer(m, this); - - public class PoisonTimer : Timer - { - private readonly Mobile _mobile; - private readonly PoisonImpl _poison; - private int _index; - private int _lastDamage; - - public PoisonTimer(Mobile m, PoisonImpl p) : base(p._delay, p._interval) + public PoisonImpl( + string name, int level, int min, int max, double percent, double delay, double interval, int count, + int messageInterval + ) { - From = m; - _mobile = m; - _poison = p; + Name = name; + Level = level; + m_Minimum = min; + m_Maximum = max; + m_Scalar = percent * 0.01; + m_Delay = TimeSpan.FromSeconds(delay); + m_Interval = TimeSpan.FromSeconds(interval); + m_Count = count; + m_MessageInterval = messageInterval; } - public Mobile From{ get; set; } + public override string Name { get; } - protected override void OnTick() + public override int Level { get; } + + [CallPriority(10)] + public static void Configure() { - if ((Core.AOS && _poison.Level < 4 && - TransformationSpellHelper.UnderTransformation(_mobile, typeof(VampiricEmbraceSpell)) || - _poison.Level < 3 && OrangePetals.UnderEffect(_mobile) || - AnimalForm.UnderTransformation(_mobile, typeof(Unicorn))) && _mobile.CurePoison(_mobile)) + if (Core.AOS) { - if (Core.SA) - { - // * You feel yourself resisting the effects of the poison * - _mobile.LocalOverheadMessage(MessageType.Emote, 0x3F, 1114441); - } - else - { - _mobile.LocalOverheadMessage( - MessageType.Emote, - 0x3F, - true, - "* You feel yourself resisting the effects of the poison *" - ); - } - - if (Core.SA) - { - // * ~1_NAME~ seems resistant to the poison * - _mobile.NonlocalOverheadMessage(MessageType.Emote, 0x3F, 1114442, _mobile.Name); - } - else - { - _mobile.LocalOverheadMessage( - MessageType.Emote, - 0x3F, - true, - $"* {_mobile.Name} seems resistant to the poison *" - ); - } - - Stop(); - return; - } - - if (_index++ == _poison._count) - { - _mobile.SendLocalizedMessage(502136); // The poison seems to have worn off. - _mobile.Poison = null; - - Stop(); - return; - } - - int damage; - - if (!Core.AOS && _lastDamage != 0 && Utility.RandomBool()) - { - damage = _lastDamage; + Register(new PoisonImpl("Lesser", 0, 4, 16, 7.5, 3.0, 2.25, 10, 4)); + Register(new PoisonImpl("Regular", 1, 8, 18, 10.0, 3.0, 3.25, 10, 3)); + Register(new PoisonImpl("Greater", 2, 12, 20, 15.0, 3.0, 4.25, 10, 2)); + Register(new PoisonImpl("Deadly", 3, 16, 30, 30.0, 3.0, 5.25, 15, 2)); + Register(new PoisonImpl("Lethal", 4, 20, 50, 35.0, 3.0, 5.25, 20, 2)); } else { - damage = 1 + (int)(_mobile.Hits * _poison._scalar); - damage = Math.Clamp(damage, _poison._minimum, _poison._maximum); + Register(new PoisonImpl("Lesser", 0, 4, 26, 2.500, 3.5, 3.0, 10, 2)); + Register(new PoisonImpl("Regular", 1, 5, 26, 3.125, 3.5, 3.0, 10, 2)); + Register(new PoisonImpl("Greater", 2, 6, 26, 6.250, 3.5, 3.0, 10, 2)); + Register(new PoisonImpl("Deadly", 3, 7, 26, 12.500, 3.5, 4.0, 10, 2)); + Register(new PoisonImpl("Lethal", 4, 9, 26, 25.000, 3.5, 5.0, 10, 2)); + } + } - _lastDamage = damage; + public static Poison IncreaseLevel(Poison oldPoison) + { + var newPoison = oldPoison == null ? null : GetPoison(oldPoison.Level + 1); + + return newPoison ?? oldPoison; + } + + public override Timer ConstructTimer(Mobile m) => new PoisonTimer(m, this); + + public class PoisonTimer : Timer + { + private readonly Mobile m_Mobile; + private readonly PoisonImpl m_Poison; + private int m_Index; + private int m_LastDamage; + + public PoisonTimer(Mobile m, PoisonImpl p) : base(p.m_Delay, p.m_Interval) + { + From = m; + m_Mobile = m; + m_Poison = p; } - // Darkglow: 10% damage boost when attacker is more than 1 tile away - if (_poison.Family == PoisonFamily.Darkglow && From != null && From.Map == _mobile.Map && - !From.InRange(_mobile, 1)) + public Mobile From { get; set; } + + protected override void OnTick() { - damage = (int)(damage * 1.1); - // Darkglow poison increases your damage! - From.SendLocalizedMessage(1072850); - } + if (Core.AOS && m_Poison.Level < 4 && + TransformationSpellHelper.UnderTransformation(m_Mobile, typeof(VampiricEmbraceSpell)) || + m_Poison.Level < 3 && OrangePetals.UnderEffect(m_Mobile) || + AnimalForm.UnderTransformation(m_Mobile, typeof(Unicorn))) + { + if (m_Mobile.CurePoison(m_Mobile)) + { + // * You feel yourself resisting the effects of the poison * + m_Mobile.LocalOverheadMessage(MessageType.Emote, 0x3F, 1114441); - From?.DoHarmful(_mobile, true); + // * ~1_NAME~ seems resistant to the poison * + m_Mobile.NonlocalOverheadMessage(MessageType.Emote, 0x3F, 1114442, m_Mobile.Name); - (_mobile as IHonorTarget)?.ReceivedHonorContext?.OnTargetPoisoned(); + Stop(); + return; + } + } - AOS.Damage(_mobile, From, damage, 0, 0, 0, 100, 0); + if (m_Index++ == m_Poison.m_Count) + { + m_Mobile.SendLocalizedMessage(502136); // The poison seems to have worn off. + m_Mobile.Poison = null; - // Parasitic: heals attacker for damage dealt when within 1 tile - if (_poison.Family == PoisonFamily.Parasitic && From != null && From.Map == _mobile.Map && - From.InRange(_mobile, 1)) - { - From.Heal(damage); - // You have had ~1_HEALED_AMOUNT~ hit points healed. - From.SendLocalizedMessage(1060203, damage.ToString()); - } + Stop(); + return; + } - // OSI: randomly revealed between first and third damage tick, guessing 60% chance - if (Utility.RandomDouble() < 0.40) - { - _mobile.RevealingAction(); - } + int damage; - if (_index % _poison._messageInterval == 0) - { - _mobile.OnPoisoned(From, _poison, _poison); + if (!Core.AOS && m_LastDamage != 0 && Utility.RandomBool()) + { + damage = m_LastDamage; + } + else + { + damage = 1 + (int)(m_Mobile.Hits * m_Poison.m_Scalar); + + if (damage < m_Poison.m_Minimum) + { + damage = m_Poison.m_Minimum; + } + else if (damage > m_Poison.m_Maximum) + { + damage = m_Poison.m_Maximum; + } + + m_LastDamage = damage; + } + + From?.DoHarmful(m_Mobile, true); + + (m_Mobile as IHonorTarget)?.ReceivedHonorContext?.OnTargetPoisoned(); + + AOS.Damage(m_Mobile, From, damage, 0, 0, 0, 100, 0); + + // OSI: randomly revealed between first and third damage tick, guessing 60% chance + if (Utility.RandomDouble() < 0.40) + { + m_Mobile.RevealingAction(); + } + + if (m_Index % m_Poison.m_MessageInterval == 0) + { + m_Mobile.OnPoisoned(From, m_Poison, m_Poison); + } } } } diff --git a/Projects/UOContent/Misc/PoisonKinds.cs b/Projects/UOContent/Misc/PoisonKinds.cs deleted file mode 100644 index 45d707d41..000000000 --- a/Projects/UOContent/Misc/PoisonKinds.cs +++ /dev/null @@ -1,109 +0,0 @@ -namespace Server; - -public static class PoisonKinds -{ - private static Poison _lesser; - private static Poison _regular; - private static Poison _greater; - private static Poison _deadly; - private static Poison _lethal; - - private static Poison _lesserDarkglow; - private static Poison _regularDarkglow; - private static Poison _greaterDarkglow; - private static Poison _deadlyDarkglow; - - private static Poison _lesserParasitic; - private static Poison _regularParasitic; - private static Poison _greaterParasitic; - private static Poison _deadlyParasitic; - private static Poison _lethalParasitic; - - extension(Poison poison) - { - public static Poison Lesser => _lesser ??= Poison.GetPoison("Lesser"); - public static Poison Regular => _regular ??= Poison.GetPoison("Regular"); - public static Poison Greater => _greater ??= Poison.GetPoison("Greater"); - public static Poison Deadly => _deadly ??= Poison.GetPoison("Deadly"); - public static Poison Lethal => _lethal ??= Poison.GetPoison("Lethal"); - - public static Poison LesserDarkglow => _lesserDarkglow ??= Poison.GetPoison("LesserDarkglow"); - public static Poison RegularDarkglow => _regularDarkglow ??= Poison.GetPoison("RegularDarkglow"); - public static Poison GreaterDarkglow => _greaterDarkglow ??= Poison.GetPoison("GreaterDarkglow"); - public static Poison DeadlyDarkglow => _deadlyDarkglow ??= Poison.GetPoison("DeadlyDarkglow"); - - public static Poison LesserParasitic => _lesserParasitic ??= Poison.GetPoison("LesserParasitic"); - public static Poison RegularParasitic => _regularParasitic ??= Poison.GetPoison("RegularParasitic"); - public static Poison GreaterParasitic => _greaterParasitic ??= Poison.GetPoison("GreaterParasitic"); - public static Poison DeadlyParasitic => _deadlyParasitic ??= Poison.GetPoison("DeadlyParasitic"); - public static Poison LethalParasitic => _lethalParasitic ??= Poison.GetPoison("LethalParasitic"); - - public bool IsDarkglow => poison.Family == PoisonFamily.Darkglow; - public bool IsParasitic => poison.Family == PoisonFamily.Parasitic; - - public static Poison GetPoison(int level) - { - for (var i = 0; i < Poison.Poisons.Count; ++i) - { - var p = Poison.Poisons[i]; - - if (p.Family == PoisonFamily.Standard && p.Level == level) - { - return p; - } - } - - return null; - } - - public static Poison GetPoisonByFamilyAndLevel(PoisonFamily family, int level) - { - for (var i = 0; i < Poison.Poisons.Count; ++i) - { - var p = Poison.Poisons[i]; - - if (p.Family == family && p.Level == level) - { - return p; - } - } - - return null; - } - } - - [CallPriority(10)] - public static void Configure() - { - if (Core.AOS) - { - Poison.Register(new PoisonImpl("Lesser", 0, 0, 4, 16, 7.5, 3.0, 2.25, 10, 4)); - Poison.Register(new PoisonImpl("Regular", 1, 1, 8, 18, 10.0, 3.0, 3.25, 10, 3)); - Poison.Register(new PoisonImpl("Greater", 2, 2, 12, 20, 15.0, 3.0, 4.25, 10, 2)); - Poison.Register(new PoisonImpl("Deadly", 3, 3, 16, 30, 30.0, 3.0, 5.25, 15, 2)); - Poison.Register(new PoisonImpl("Lethal", 4, 4, 20, 50, 35.0, 3.0, 5.25, 20, 2)); - } - else - { - Poison.Register(new PoisonImpl("Lesser", 0, 0, 4, 26, 2.5, 3.5, 3.0, 10, 2)); - Poison.Register(new PoisonImpl("Regular", 1, 1, 5, 26, 3.125, 3.5, 3.0, 10, 2)); - Poison.Register(new PoisonImpl("Greater", 2, 2, 6, 26, 6.25, 3.5, 3.0, 10, 2)); - Poison.Register(new PoisonImpl("Deadly", 3, 3, 7, 26, 12.5, 3.5, 4.0, 10, 2)); - Poison.Register(new PoisonImpl("Lethal", 4, 4, 9 , 26, 25.0, 3.5, 5.0, 10, 2)); - } - - if (Core.ML) - { - Poison.Register(new PoisonImpl("LesserDarkglow", 10, 0, 4, 16, 7.5, 3.0, 2.25, 10, 4, PoisonFamily.Darkglow)); - Poison.Register(new PoisonImpl("RegularDarkglow", 11, 1, 8, 18, 10.0, 3.0, 3.25, 10, 3, PoisonFamily.Darkglow)); - Poison.Register(new PoisonImpl("GreaterDarkglow", 12, 2, 12, 20, 15.0, 3.0, 4.25, 10, 2, PoisonFamily.Darkglow)); - Poison.Register(new PoisonImpl("DeadlyDarkglow", 13, 3, 16, 30, 30.0, 3.0, 5.25, 15, 2, PoisonFamily.Darkglow)); - - Poison.Register(new PoisonImpl("LesserParasitic", 20, 0, 4, 16, 7.5, 3.0, 2.25, 10, 4, PoisonFamily.Parasitic)); - Poison.Register(new PoisonImpl("RegularParasitic", 21, 1, 8, 18, 10.0, 3.0, 3.25, 10, 3, PoisonFamily.Parasitic)); - Poison.Register(new PoisonImpl("GreaterParasitic", 22, 2, 12, 20, 15.0, 3.0, 4.25, 10, 2, PoisonFamily.Parasitic)); - Poison.Register(new PoisonImpl("DeadlyParasitic", 23, 3, 16, 30, 30.0, 3.0, 5.25, 15, 2, PoisonFamily.Parasitic)); - Poison.Register(new PoisonImpl("LethalParasitic", 24, 4, 20, 50, 35.0, 3.0, 5.25, 20, 2, PoisonFamily.Parasitic)); - } - } -} diff --git a/Projects/UOContent/Misc/ProfanityProtection.cs b/Projects/UOContent/Misc/ProfanityProtection.cs index 9aa0280d4..cf917938e 100644 --- a/Projects/UOContent/Misc/ProfanityProtection.cs +++ b/Projects/UOContent/Misc/ProfanityProtection.cs @@ -1,147 +1,140 @@ using System; -using System.Buffers; -namespace Server.Misc; - -public enum ProfanityAction +namespace Server.Misc { - None, // no action taken - Disallow, // speech is not displayed - Criminal, // makes the player criminal, not killable by guards - CriminalAction, // makes the player criminal, can be killed by guards - Disconnect, // player is kicked - Other // some other implementation -} - -public static class ProfanityProtection -{ - private static bool Enabled; - private static ProfanityAction Action; - - public static void Configure() + public enum ProfanityAction { - Enabled = ServerConfiguration.GetSetting("profanityProtection.enabled", false); - Action = ServerConfiguration.GetSetting("profanityProtection.action", ProfanityAction.Disallow); - - if (Enabled) - { - EventSink.Speech += EventSink_Speech; - } + None, // no action taken + Disallow, // speech is not displayed + Criminal, // makes the player criminal, not killable by guards + CriminalAction, // makes the player criminal, can be killed by guards + Disconnect, // player is kicked + Other // some other implementation } - // Used by the guild system - public static readonly SearchValues Exceptions = SearchValues.Create( - ' ', '-', '.', '\'', '"', ',', '_', '+', '=', '~', '`', '!', '^', '*', '\\', '/', ';', ':', '<', '>', '[', ']', - '{', '}', '?', '|', '(', ')', '%', '$', '&', '#', '@' - ); - - public static readonly string[] Disallowed = - [ - "jigaboo", - "chigaboo", - "wop", - "kyke", - "kike", - "tit", - "spic", - "prick", - "piss", - "lezbo", - "lesbo", - "felatio", - "dyke", - "dildo", - "chinc", - "chink", - "cunnilingus", - "cum", - "cocksucker", - "cock", - "clitoris", - "clit", - "ass", - "hitler", - "penis", - "nigga", - "nigger", - "klit", - "kunt", - "jiz", - "jism", - "jerkoff", - "jackoff", - "goddamn", - "fag", - "blowjob", - "bitch", - "asshole", - "dick", - "pussy", - "snatch", - "cunt", - "twat", - "shit", - "fuck", - ]; - - public static readonly SearchValues DisallowedSearchValues = SearchValues.Create( - Disallowed, - StringComparison.OrdinalIgnoreCase - ); - - private static bool OnProfanityDetected(Mobile from, string speech) + public static class ProfanityProtection { - switch (Action) - { - case ProfanityAction.None: return true; - case ProfanityAction.Disallow: return false; - case ProfanityAction.Criminal: - from.Criminal = true; - return true; - case ProfanityAction.CriminalAction: - from.CriminalAction(false); - return true; - case ProfanityAction.Disconnect: - { - from.NetState?.Disconnect("Using profanity."); + // TODO: Move this to configuration + private static readonly bool Enabled = false; - return false; - } - default: - case ProfanityAction.Other: // TODO: Provide custom implementation if this is chosen - { + private static readonly ProfanityAction + Action = ProfanityAction.Disallow; // change here what to do when profanity is detected + + public static char[] Exceptions { get; } = + { + ' ', '-', '.', '\'', '"', ',', '_', '+', '=', '~', '`', '!', '^', '*', '\\', '/', ';', ':', '<', '>', '[', ']', + '{', '}', '?', '|', '(', ')', '%', '$', '&', '#', '@' + }; + + public static string[] StartDisallowed { get; } = Array.Empty(); + + public static string[] Disallowed { get; } = + { + "jigaboo", + "chigaboo", + "wop", + "kyke", + "kike", + "tit", + "spic", + "prick", + "piss", + "lezbo", + "lesbo", + "felatio", + "dyke", + "dildo", + "chinc", + "chink", + "cunnilingus", + "cum", + "cocksucker", + "cock", + "clitoris", + "clit", + "ass", + "hitler", + "penis", + "nigga", + "nigger", + "klit", + "kunt", + "jiz", + "jism", + "jerkoff", + "jackoff", + "goddamn", + "fag", + "blowjob", + "bitch", + "asshole", + "dick", + "pussy", + "snatch", + "cunt", + "twat", + "shit", + "fuck" + }; + + public static void Initialize() + { + if (Enabled) + { + EventSink.Speech += EventSink_Speech; + } + } + + private static bool OnProfanityDetected(Mobile from, string speech) + { + switch (Action) + { + case ProfanityAction.None: return true; + case ProfanityAction.Disallow: return false; + case ProfanityAction.Criminal: + from.Criminal = true; return true; - } - } - } + case ProfanityAction.CriminalAction: + from.CriminalAction(false); + return true; + case ProfanityAction.Disconnect: + { + from.NetState?.Disconnect("Using profanity."); - public static bool ContainsProfanity(ReadOnlySpan speech) => - speech.Length > 0 && - !NameVerification.Validate( - speech, - 1, - int.MaxValue, - true, - true, - true, - int.MaxValue, // allow all non-alphanumeric characters - null, - Disallowed, - DisallowedSearchValues - ); - - private static void EventSink_Speech(SpeechEventArgs e) - { - var from = e.Mobile; - - if (from.AccessLevel > AccessLevel.Player) - { - return; + return false; + } + default: + case ProfanityAction.Other: // TODO: Provide custom implementation if this is chosen + { + return true; + } + } } - if (ContainsProfanity(e.Speech)) + private static void EventSink_Speech(SpeechEventArgs e) { - e.Blocked = !OnProfanityDetected(from, e.Speech); + var from = e.Mobile; + + if (from.AccessLevel > AccessLevel.Player) + { + return; + } + + if (!NameVerification.Validate( + e.Speech, + 0, + int.MaxValue, + true, + true, + false, + int.MaxValue, + Exceptions, + Disallowed, + StartDisallowed + )) + { + e.Blocked = !OnProfanityDetected(from, e.Speech); + } } } } diff --git a/Projects/UOContent/Misc/RenameRequests.cs b/Projects/UOContent/Misc/RenameRequests.cs index 8abf648d7..ffaece37e 100644 --- a/Projects/UOContent/Misc/RenameRequests.cs +++ b/Projects/UOContent/Misc/RenameRequests.cs @@ -1,31 +1,54 @@ using System; -namespace Server.Misc; - -public static class RenameRequests +namespace Server.Misc { - public static void RenameRequest(Mobile from, Mobile targ, string name) + public static class RenameRequests { - if (!from.CanSee(targ) || !from.InRange(targ, 12) || !targ.CanBeRenamedBy(from)) + public static void RenameRequest(Mobile from, Mobile targ, string name) { - return; - } + if (from.CanSee(targ) && from.InRange(targ, 12) && targ.CanBeRenamedBy(from)) + { + name = name.Trim(); - var span = name.AsSpan().Trim(); + if (NameVerification.Validate( + name, + 1, + 16, + true, + false, + true, + 0, + NameVerification.Empty, + NameVerification.StartDisallowed, + Core.ML ? NameVerification.Disallowed : Array.Empty() + )) + { + if (Core.ML) + { + var disallowed = ProfanityProtection.Disallowed; - if (NameVerification.ValidatePetName(span)) - { - // Pet ~1_OLDPETNAME~ renamed to ~2_NEWPETNAME~. - from.SendLocalizedMessage(1072623, $"{targ.Name}\t{span}"); - targ.Name = span.ToString(); - } - else if (span.IndexOfAny(ProfanityProtection.DisallowedSearchValues) != -1) - { - from.SendLocalizedMessage(1072622); // That name isn't very polite. - } - else - { - from.SendMessage("That name is unacceptable."); + for (var i = 0; i < disallowed.Length; i++) + { + if (name.IndexOfOrdinal(disallowed[i]) != -1) + { + from.SendLocalizedMessage(1072622); // That name isn't very polite. + return; + } + } + + from.SendLocalizedMessage( + 1072623, + $"{targ.Name}\t{name}" + ); // Pet ~1_OLDPETNAME~ renamed to ~2_NEWPETNAME~. + } + + targ.Name = name; + } + else + { + from.SendMessage("That name is unacceptable."); + } + } } } } diff --git a/Projects/UOContent/Misc/ServerList.cs b/Projects/UOContent/Misc/ServerList.cs index c8a05a3bf..56cbaad2a 100644 --- a/Projects/UOContent/Misc/ServerList.cs +++ b/Projects/UOContent/Misc/ServerList.cs @@ -79,23 +79,24 @@ namespace Server.Misc { var ns = e.State; - var localEndPoint = ns.LocalEndPoint; - if (localEndPoint == null) + var ipep = (IPEndPoint)ns.Connection?.LocalEndPoint; + if (ipep == null) { return; } - var localAddress = localEndPoint.Address; - var localPort = localEndPoint.Port; + var localAddress = ipep.Address; + var localPort = ipep.Port; if (_useServerListingAddressConfig) { localAddress = _publicAddress; } - else if (localAddress.IsPrivateNetwork()) + else if (IsPrivateNetwork(localAddress)) { - // Check if client is from a public network - if (!ns.Address.IsPrivateNetwork() && _publicAddress != null) + ipep = (IPEndPoint)ns.Connection.RemoteEndPoint; + + if (ipep == null || !IsPrivateNetwork(ipep.Address) && _publicAddress != null) { localAddress = _publicAddress; } @@ -157,7 +158,7 @@ namespace Server.Misc { var ip = unicast.Address; if (!IPAddress.IsLoopback(ip) && ip.AddressFamily != AddressFamily.InterNetworkV6 && - !ip.IsPrivateNetwork()) + !IsPrivateNetwork(ip)) { return true; } @@ -167,6 +168,40 @@ namespace Server.Misc return false; } + private static bool IsPrivateNetwork(IPAddress ip) => + ip.AddressFamily switch + { + AddressFamily.InterNetwork => IsPrivateNetworkV4(ip), + AddressFamily.InterNetworkV6 => IsPrivateNetworkV6(ip), + _ => false + }; + + private static readonly IFirewallEntry[] _privateNetworkV4 = + [ + new CidrFirewallEntry("192.168.0.0/16"), + new CidrFirewallEntry("10.0.0.0/8"), + new CidrFirewallEntry("172.16.0.0/12"), + new CidrFirewallEntry("169.254.0.0/16"), + new CidrFirewallEntry("100.64.0.0/10") + ]; + + private static readonly IFirewallEntry[] _privateNetworkV6 = + [ + new CidrFirewallEntry("fc00::/7"), + new CidrFirewallEntry("fe80::/10") + ]; + + private static bool IsPrivateNetworkV4(IPAddress ip) => + _privateNetworkV4[0].IsBlocked(ip) || + _privateNetworkV4[1].IsBlocked(ip) || + _privateNetworkV4[2].IsBlocked(ip) || + _privateNetworkV4[3].IsBlocked(ip) || + _privateNetworkV4[4].IsBlocked(ip); + + private static bool IsPrivateNetworkV6(IPAddress ip) => + _privateNetworkV6[0].IsBlocked(ip) || + _privateNetworkV6[1].IsBlocked(ip); + private const string _ipifyUrl = "https://api.ipify.org"; private static IPAddress FindPublicAddress() @@ -177,7 +212,7 @@ namespace Server.Misc try { // This isn't called often so we don't need to optimize - using var hc = new HttpClient(); + using HttpClient hc = new HttpClient(); hc.Timeout = TimeSpan.FromSeconds(1); // Only wait 1 second var ipAddress = hc.GetStringAsync(_ipifyUrl).Result; return IPAddress.Parse(ipAddress); diff --git a/Projects/UOContent/Misc/ShardPoller.cs b/Projects/UOContent/Misc/ShardPoller.cs index 8516e6678..682003c58 100644 --- a/Projects/UOContent/Misc/ShardPoller.cs +++ b/Projects/UOContent/Misc/ShardPoller.cs @@ -36,14 +36,16 @@ public partial class ShardPoller : Item Movable = false; } - [SerializableField(0, allowFieldChange: nameof(AllowTitleChange))] - [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] - private string _title; - - private bool AllowTitleChange(ref string value) + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] + public string Title { - value = ShardPollPrompt.UrlToHref(value); - return true; + get => _title; + set + { + _title = ShardPollPrompt.UrlToHref(value); + this.MarkDirty(); + } } [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] @@ -52,20 +54,31 @@ public partial class ShardPoller : Item ? TimeSpan.Zero : Utility.Max(StartTime + Duration - Core.Now, TimeSpan.Zero); - [SerializableField(3, fieldChanged: nameof(OnActiveChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] - private bool _active; - - private void OnActiveChanged(bool oldValue, bool newValue) + [SerializableProperty(3)] + [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] + public bool Active { - if (_active) + get => _active; + set { - StartTime = Core.Now; - _activePollers.Add(this); - } - else - { - _activePollers.Remove(this); + if (_active == value) + { + return; + } + + _active = value; + + if (_active) + { + StartTime = Core.Now; + _activePollers.Add(this); + } + else + { + _activePollers.Remove(this); + } + + this.MarkDirty(); } } @@ -154,7 +167,8 @@ public partial class ShardPoller : Item if (spg == null) { - spg = ShardPollGump.DisplayTo(from, poller, false, null); + spg = new ShardPollGump(from, poller, false, null); + from.SendGump(spg); } else { @@ -172,7 +186,7 @@ public partial class ShardPoller : Item { if (from.AccessLevel >= AccessLevel.Administrator) { - ShardPollGump.DisplayTo(from, this, true, null); + from.SendGump(new ShardPollGump(from, this, true, null)); } } @@ -237,12 +251,15 @@ public partial class ShardPollOption } } - [SerializableField(0, fieldChanged: nameof(OnTitleChanged))] - private string _title; - - private void OnTitleChanged(string oldValue, string newValue) + [SerializableProperty(0)] + public string Title { - _lineBreaks = -1; + get => _title; + set + { + _title = value; + _lineBreaks = -1; + } } public int Votes => Voters.Length; @@ -323,51 +340,23 @@ public partial class ShardPollOption } } -public class ShardPollGump : DynamicGump +public class ShardPollGump : Gump { private const int LabelColor32 = 0xFFFFFF; private readonly Mobile _from; private readonly ShardPoller _poller; private Queue _polls; - public override bool Singleton => true; - - private ShardPollGump(Mobile from, ShardPoller poller, bool editing, Queue polls) : base(50, 50) + public ShardPollGump(Mobile from, ShardPoller poller, bool editing, Queue polls) : base(50, 50) { _from = from; _poller = poller; Editing = editing; _polls = polls; - } - public static ShardPollGump DisplayTo(Mobile from, ShardPoller poller, bool editing, Queue polls) - { - if (from?.NetState == null || poller == null || poller.Deleted) - { - return null; - } + Closable = false; - var gump = new ShardPollGump(from, poller, editing, polls); - from.SendGump(gump); - return gump; - } - - public bool Editing { get; } - - public void QueuePoll(ShardPoller poller) - { - _polls ??= new Queue(4); - _polls.Enqueue(poller); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var poller = _poller; - var editing = Editing; - - builder.SetNoClose(); - - builder.AddPage(); + AddPage(0); var totalVotes = 0; var totalOptionHeight = 0; @@ -388,10 +377,10 @@ public class ShardPollGump : DynamicGump var height = 115 + totalOptionHeight; - builder.AddBackground(1, 1, 398, height - 2, 3600); - builder.AddAlphaRegion(16, 15, 369, height - 31); + AddBackground(1, 1, 398, height - 2, 3600); + AddAlphaRegion(16, 15, 369, height - 31); - builder.AddItem(308, 30, 0x1E5E); + AddItem(308, 30, 0x1E5E); string title; @@ -404,18 +393,18 @@ public class ShardPollGump : DynamicGump title = "Shard Poll"; } - builder.AddHtml(22, 22, 294, 20, title.Center(LabelColor32)); + AddHtml(22, 22, 294, 20, title.Center(LabelColor32)); if (editing) { - builder.AddHtml(22, 22, 294, 20, Html.Color($"{totalVotes} total", LabelColor32)); - builder.AddButton(287, 23, 0x2622, 0x2623, 2); + AddHtml(22, 22, 294, 20, $"{totalVotes} total".Color(LabelColor32)); + AddButton(287, 23, 0x2622, 0x2623, 2); } - builder.AddHtml(22, 50, 294, 40, poller.Title.Color(0x99CC66)); + AddHtml(22, 50, 294, 40, poller.Title.Color(0x99CC66)); - builder.AddImageTiled(32, 88, 264, 1, 9107); - builder.AddImageTiled(42, 90, 264, 1, 9157); + AddImageTiled(32, 88, 264, 1, 9107); + AddImageTiled(42, 90, 264, 1, 9157); var y = 100; @@ -437,16 +426,16 @@ public class ShardPollGump : DynamicGump if (isViewingResults) { - builder.AddImage(24, y - 15, 0x25FE); + AddImage(24, y - 15, 0x25FE); } else { - builder.AddRadio(24, y - 15, 0x25F9, 0x25FC, false, 1 + i); + AddRadio(24, y - 15, 0x25F9, 0x25FC, false, 1 + i); } var lineBreaks = option.GetBreaks(); - builder.AddHtml(60, y - 9 * lineBreaks, 250, 18 * lineBreaks, text.Color(LabelColor32)); + AddHtml(60, y - 9 * lineBreaks, 250, 18 * lineBreaks, text.Color(LabelColor32)); y += optHeight / 2; y += 5; @@ -454,12 +443,20 @@ public class ShardPollGump : DynamicGump if (editing && !isViewingResults) { - builder.AddRadio(24, y + 15 - 15, 0x25F9, 0x25FC, false, 1 + poller.Options.Length); - builder.AddHtml(60, y + 15 - 9, 250, 18, "Create new option.".Color(0x99CC66)); + AddRadio(24, y + 15 - 15, 0x25F9, 0x25FC, false, 1 + poller.Options.Length); + AddHtml(60, y + 15 - 9, 250, 18, "Create new option.".Color(0x99CC66)); } - builder.AddButton(314, height - 73, 247, 248, 1); - builder.AddButton(314, height - 47, 242, 241, 0); + AddButton(314, height - 73, 247, 248, 1); + AddButton(314, height - 47, 242, 241, 0); + } + + public bool Editing { get; } + + public void QueuePoll(ShardPoller poller) + { + _polls ??= new Queue(4); + _polls.Enqueue(poller); } public override void OnResponse(NetState sender, in RelayInfo info) @@ -470,11 +467,9 @@ public class ShardPollGump : DynamicGump if (shardPoller != null) { - var from = _from; - var queuedPolls = _polls; Timer.StartTimer( TimeSpan.FromSeconds(1.0), - () => DisplayTo(from, shardPoller, false, queuedPolls) + () => _from.SendGump(new ShardPollGump(_from, shardPoller, false, _polls)) ); } } @@ -519,7 +514,7 @@ public class ShardPollGump : DynamicGump else { _from.SendMessage("You may not edit an active poll. Deactivate it first."); - _from.SendGump(this); + _from.SendGump(new ShardPollGump(_from, _poller, Editing, _polls)); } } else @@ -540,7 +535,7 @@ public class ShardPollGump : DynamicGump } else if (info.ButtonID == 2 && Editing) { - _from.SendGump(this); + _from.SendGump(new ShardPollGump(_from, _poller, Editing, _polls)); _from.SendGump(new PropertiesGump(_from, _poller)); } } @@ -559,7 +554,7 @@ public partial class ShardPollPrompt : Prompt public override void OnCancel(Mobile from) { - ShardPollGump.DisplayTo(from, _poller, true, null); + from.SendGump(new ShardPollGump(from, _poller, true, null)); } private static string UrlRegex_Match(Match m) @@ -608,7 +603,7 @@ public partial class ShardPollPrompt : Prompt } } - ShardPollGump.DisplayTo(from, _poller, true, null); + from.SendGump(new ShardPollGump(from, _poller, true, null)); } [GeneratedRegex(@"\[url(?:=(.*?))?\](.*?)\[/url\]", RegexOptions.IgnoreCase | RegexOptions.Compiled)] diff --git a/Projects/UOContent/Misc/ShrinkTable.cs b/Projects/UOContent/Misc/ShrinkTable.cs index 956be85de..2c4f71093 100644 --- a/Projects/UOContent/Misc/ShrinkTable.cs +++ b/Projects/UOContent/Misc/ShrinkTable.cs @@ -37,7 +37,7 @@ namespace Server throw new JsonException($"Failed to deserialize {path}."); } - var length = 0; + int length = 0; foreach (var key in table.Keys) { diff --git a/Projects/UOContent/Misc/StaminaSystem.cs b/Projects/UOContent/Misc/StaminaSystem.cs index fcd5d46b0..e4704e310 100644 --- a/Projects/UOContent/Misc/StaminaSystem.cs +++ b/Projects/UOContent/Misc/StaminaSystem.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using ModernUO.CodeGeneratedEvents; +using Server.Collections; using Server.Logging; using Server.Mobiles; using Server.Spells.Ninjitsu; @@ -29,8 +30,7 @@ public static class StaminaSystem public static DFAlgorithm DFA { get; set; } public static int StonesOverweightAllowance { get; set; } - public static bool CannotRunWhenFatigued { get; set; } - public static bool CannotWalkWhenFatigued { get; set; } + public static bool CannotMoveWhenFatigued { get; set; } public static int StonesPerOverweightLoss { get; set; } public static int BaseOverweightLoss { get; set; } public static double AdditionalLossWhenBelow { get; set; } @@ -42,8 +42,7 @@ public static class StaminaSystem public static void Configure() { - CannotRunWhenFatigued = ServerConfiguration.GetOrUpdateSetting("stamina.cannotRunWhenFatigued", !Core.AOS); - CannotWalkWhenFatigued = ServerConfiguration.GetOrUpdateSetting("stamina.cannotWalkWhenFatigued", false); + CannotMoveWhenFatigued = ServerConfiguration.GetOrUpdateSetting("stamina.cannotMoveWhenFatigued", !Core.AOS); StonesPerOverweightLoss = ServerConfiguration.GetOrUpdateSetting("stamina.stonesPerOverweightLoss", 25); StonesOverweightAllowance = ServerConfiguration.GetOrUpdateSetting("stamina.stonesOverweightAllowance", 4); BaseOverweightLoss = ServerConfiguration.GetOrUpdateSetting("stamina.baseOverweightLoss", 5); @@ -59,16 +58,22 @@ public static class StaminaSystem EventSink.Logout += Logout; // Credit idle time + using var queue = PooledRefQueue.Create(); foreach (var m in _stepsTaken.Keys) { - // Keeps the ref valid for the check below. + // We cannot remove since we are iterating. ref var stepsTaken = ref RegenSteps(m, out var exists, removeOnInvalidation: false); if (exists && stepsTaken.Steps <= 0) { - _stepsTaken.Remove(m); + queue.Enqueue(m); } } + + while (queue.Count > 0) + { + _stepsTaken.Remove(queue.Dequeue()); + } } [OnEvent(nameof(PlayerMobile.PlayerDeletedEvent))] @@ -319,13 +324,6 @@ public static class StaminaSystem var from = e.Mobile; var running = (e.Direction & Direction.Running) != 0; - if (CannotWalkWhenFatigued && from.Stam <= 0) - { - from.SendLocalizedMessage(500110); // You are too fatigued to move. - e.Blocked = true; - return; - } - if (overweight > 0) { var stamLoss = GetStamLoss(from, overweight, running); @@ -351,7 +349,7 @@ public static class StaminaSystem --from.Stam; } - if (CannotRunWhenFatigued && from.Stam <= 0) + if (CannotMoveWhenFatigued && from.Stam <= 0) { from.SendLocalizedMessage(500110); // You are too fatigued to move. e.Blocked = true; @@ -360,7 +358,7 @@ public static class StaminaSystem if (from is PlayerMobile pm) { - ref var stepsTaken = ref GetOrCreateStepsTaken(pm, out var created); + ref StepsTaken stepsTaken = ref GetOrCreateStepsTaken(pm, out var created); if (!created) { RegenSteps(ref stepsTaken, removeOnInvalidation: false); @@ -474,13 +472,27 @@ public static class StaminaSystem if (_resetHash.Count > 0) { - ref var stepsTaken = ref Unsafe.NullRef(); + using var queue = PooledRefQueue.Create(); + + ref StepsTaken stepsTaken = ref Unsafe.NullRef(); foreach (var m in _resetHash) { stepsTaken = ref GetStepsTaken(m, out var exists); if (!exists || Core.Now >= stepsTaken.IdleStartTime + ResetDuration) { - _resetHash.Remove(m); + queue.Enqueue(m); + } + } + + if (_resetHash.Count == queue.Count) + { + _resetHash.Clear(); + } + else + { + while (queue.Count > 0) + { + _resetHash.Remove(queue.Dequeue()); } } } diff --git a/Projects/UOContent/Misc/Titles.cs b/Projects/UOContent/Misc/Titles.cs index c5f9f2a18..9595665b5 100644 --- a/Projects/UOContent/Misc/Titles.cs +++ b/Projects/UOContent/Misc/Titles.cs @@ -116,20 +116,6 @@ namespace Server.Misc ) }; - public static (int Fame, int Karma) ComputeKillAwards(Mobile killed, Map map) - { - var totalFame = killed.Fame / 100; - var totalKarma = -killed.Karma / 100; - - if (Core.LBR && map == Map.Felucca) - { - totalFame += totalFame / 10 * 3; - totalKarma += totalKarma / 10 * 3; - } - - return (totalFame, totalKarma); - } - public static void AwardFame(Mobile m, int offset, bool message) { if (offset > 0) @@ -197,29 +183,13 @@ namespace Server.Misc } } - public static void SetKarma(Mobile m, int newKarma, bool message) - { - newKarma = Math.Clamp(newKarma, MinKarma, MaxKarma); - var offset = newKarma - m.Karma; - var wasPositiveKarma = m.Karma >= 0; - - m.Karma = newKarma; - - if (message) - { - SendKarmaMessage(m, offset); - } - - CheckKarmaLock(m, wasPositiveKarma); - } - public static void AwardKarma(Mobile m, int offset, bool message) { var pm = m as PlayerMobile; if (offset > 0) { - if (Core.UOTD && !Core.AOS && pm?.KarmaLocked == true) + if (pm?.KarmaLocked == true) { return; } @@ -254,32 +224,41 @@ namespace Server.Misc if (message) { - SendKarmaMessage(m, offset); + if (offset > 40) + { + m.SendLocalizedMessage(1019062); // You have gained a lot of karma. + } + else if (offset > 20) + { + m.SendLocalizedMessage(1019061); // You have gained a good amount of karma. + } + else if (offset > 10) + { + m.SendLocalizedMessage(1019060); // You have gained some karma. + } + else if (offset > 0) + { + m.SendLocalizedMessage(1019059); // You have gained a little karma. + } + else if (offset < -40) + { + m.SendLocalizedMessage(1019066); // You have lost a lot of karma. + } + else if (offset < -20) + { + m.SendLocalizedMessage(1019065); // You have lost a good amount of karma. + } + else if (offset < -10) + { + m.SendLocalizedMessage(1019064); // You have lost some karma. + } + else if (offset < 0) + { + m.SendLocalizedMessage(1019063); // You have lost a little karma. + } } - CheckKarmaLock(m, wasPositiveKarma); - } - - private static void SendKarmaMessage(Mobile m, int offset) - { - var message = offset switch - { - > 40 => 1019062, // You have gained a lot of karma. - > 20 => 1019061, // You have gained a good amount of karma. - > 10 => 1019060, // You have gained some karma. - > 0 => 1019059, // You have gained a little karma. - < -40 => 1019066, // You have lost a lot of karma. - < -20 => 1019065, // You have lost a good amount of karma. - < -10 => 1019064, // You have lost some karma. - _ => 1019063, // You have lost a little karma. - }; - - m.SendLocalizedMessage(message); - } - - private static void CheckKarmaLock(Mobile m, bool wasPositiveKarma) - { - if (Core.UOTD && !Core.AOS && wasPositiveKarma && m.Karma < 0 && m is PlayerMobile { KarmaLocked: false } pm) + if (!Core.AOS && wasPositiveKarma && m.Karma < 0 && pm?.KarmaLocked == false) { pm.KarmaLocked = true; // Karma is locked. A mantra spoken at a shrine will unlock it again. diff --git a/Projects/UOContent/Misc/TreasureMapProtection.cs b/Projects/UOContent/Misc/TreasureMapProtection.cs index 6741edd62..0fc5d1283 100644 --- a/Projects/UOContent/Misc/TreasureMapProtection.cs +++ b/Projects/UOContent/Misc/TreasureMapProtection.cs @@ -23,7 +23,7 @@ namespace Server public static void Initialize() { var filePath = Path.Combine(Core.BaseDirectory, "Data/treasure.cfg"); - var i = 0; + int i = 0; if (File.Exists(filePath)) { diff --git a/Projects/UOContent/Misc/Weather.cs b/Projects/UOContent/Misc/Weather.cs index 3a8a919ea..a29b7e985 100644 --- a/Projects/UOContent/Misc/Weather.cs +++ b/Projects/UOContent/Misc/Weather.cs @@ -1,135 +1,245 @@ using System; using System.Collections.Generic; -using ModernUO.Serialization; using Server.Items; using Server.Network; -namespace Server.Misc; - -public partial class Weather +namespace Server.Misc { - private static Map[] m_Facets; - private static readonly Dictionary> m_WeatherByFacet = new(); - private bool m_Active; - private bool m_ExtremeTemperature; - - private int m_Stage; - - public Weather( - Map facet, Rectangle2D[] area, int temperature, int chanceOfPrecipitation, int chanceOfExtremeTemperature, - TimeSpan interval - ) + public class Weather { - Facet = facet; - Area = area; - Temperature = temperature; - ChanceOfPrecipitation = chanceOfPrecipitation; - ChanceOfExtremeTemperature = chanceOfExtremeTemperature; + private static Map[] m_Facets; + private static readonly Dictionary> m_WeatherByFacet = new(); + private bool m_Active; + private bool m_ExtremeTemperature; - var list = GetWeatherList(facet); + private int m_Stage; - list?.Add(this); - - Timer.DelayCall( - TimeSpan.FromSeconds((0.2 + Utility.RandomDouble() * 0.8) * interval.TotalSeconds), - interval, - OnTick - ); - } - - public Map Facet { get; } - - public Rectangle2D[] Area { get; set; } - - public int Temperature { get; set; } - - public int ChanceOfPrecipitation { get; set; } - - public int ChanceOfExtremeTemperature { get; set; } - - // For dynamic weather: - - public Rectangle2D Bounds { get; set; } - - public int MoveSpeed { get; set; } - - public int MoveAngleX { get; set; } - - public int MoveAngleY { get; set; } - - public static void Initialize() - { - m_Facets = [Map.Felucca, Map.Trammel]; - - /* Static weather: - * - * Format: - * AddWeather( temperature, chanceOfPrecipitation, chanceOfExtremeTemperature, ); - */ - - // ice island - AddWeather( - -15, - 100, - 5, - new Rectangle2D(3850, 160, 390, 320), - new Rectangle2D(3900, 480, 380, 180), - new Rectangle2D(4160, 660, 150, 110) - ); - - // covetous entrance, around vesper and minoc - AddWeather(+15, 50, 5, new Rectangle2D(2425, 725, 250, 250)); - - // despise entrance, north of britain - AddWeather(+15, 50, 5, new Rectangle2D(1245, 1045, 250, 250)); - - /* Dynamic weather: - * - * Format: - * AddDynamicWeather( temperature, chanceOfPrecipitation, chanceOfExtremeTemperature, moveSpeed, width, height, bounds ); - */ - - for (var i = 0; i < 15; ++i) + public Weather( + Map facet, Rectangle2D[] area, int temperature, int chanceOfPercipitation, int chanceOfExtremeTemperature, + TimeSpan interval + ) { - AddDynamicWeather(+15, 100, 5, 8, 400, 400, new Rectangle2D(0, 0, 5120, 4096)); - } - } + Facet = facet; + Area = area; + Temperature = temperature; + ChanceOfPercipitation = chanceOfPercipitation; + ChanceOfExtremeTemperature = chanceOfExtremeTemperature; - public static List GetWeatherList(Map facet) - { - if (facet == null) - { - return null; + var list = GetWeatherList(facet); + + list?.Add(this); + + Timer.DelayCall( + TimeSpan.FromSeconds((0.2 + Utility.RandomDouble() * 0.8) * interval.TotalSeconds), + interval, + OnTick + ); } - if (!m_WeatherByFacet.TryGetValue(facet, out var list)) + public Map Facet { get; } + + public Rectangle2D[] Area { get; set; } + + public int Temperature { get; set; } + + public int ChanceOfPercipitation { get; set; } + + public int ChanceOfExtremeTemperature { get; set; } + + // For dynamic weather: + + public Rectangle2D Bounds { get; set; } + + public int MoveSpeed { get; set; } + + public int MoveAngleX { get; set; } + + public int MoveAngleY { get; set; } + + public static void Initialize() { - m_WeatherByFacet[facet] = list = []; + m_Facets = new[] { Map.Felucca, Map.Trammel }; + + /* Static weather: + * + * Format: + * AddWeather( temperature, chanceOfPercipitation, chanceOfExtremeTemperature, ); + */ + + // ice island + AddWeather( + -15, + 100, + 5, + new Rectangle2D(3850, 160, 390, 320), + new Rectangle2D(3900, 480, 380, 180), + new Rectangle2D(4160, 660, 150, 110) + ); + + // covetous entrance, around vesper and minoc + AddWeather(+15, 50, 5, new Rectangle2D(2425, 725, 250, 250)); + + // despise entrance, north of britain + AddWeather(+15, 50, 5, new Rectangle2D(1245, 1045, 250, 250)); + + /* Dynamic weather: + * + * Format: + * AddDynamicWeather( temperature, chanceOfPercipitation, chanceOfExtremeTemperature, moveSpeed, width, height, bounds ); + */ + + for (var i = 0; i < 15; ++i) + { + AddDynamicWeather(+15, 100, 5, 8, 400, 400, new Rectangle2D(0, 0, 5120, 4096)); + } } - return list; - } - - public static void AddDynamicWeather( - int temperature, int chanceOfPrecipitation, int chanceOfExtremeTemperature, int moveSpeed, int width, int height, - Rectangle2D bounds - ) - { - for (var i = 0; i < m_Facets.Length; ++i) + public static List GetWeatherList(Map facet) { + if (facet == null) + { + return null; + } + + if (!m_WeatherByFacet.TryGetValue(facet, out var list)) + { + m_WeatherByFacet[facet] = list = new List(); + } + + return list; + } + + public static void AddDynamicWeather( + int temperature, int chanceOfPercipitation, int chanceOfExtremeTemperature, int moveSpeed, int width, int height, + Rectangle2D bounds + ) + { + for (var i = 0; i < m_Facets.Length; ++i) + { + var area = new Rectangle2D(); + var isValid = false; + + for (var j = 0; j < 10; ++j) + { + area = new Rectangle2D( + bounds.X + Utility.Random(bounds.Width - width), + bounds.Y + Utility.Random(bounds.Height - height), + width, + height + ); + + if (!CheckWeatherConflict(m_Facets[i], null, area)) + { + isValid = true; + } + + if (isValid) + { + break; + } + } + + if (!isValid) + { + continue; + } + + new Weather( + m_Facets[i], + new[] { area }, + temperature, + chanceOfPercipitation, + chanceOfExtremeTemperature, + TimeSpan.FromSeconds(30.0) + ) + { Bounds = bounds, MoveSpeed = moveSpeed }; + } + } + + public static void AddWeather( + int temperature, int chanceOfPercipitation, int chanceOfExtremeTemperature, params Rectangle2D[] area + ) + { + for (var i = 0; i < m_Facets.Length; ++i) + { + new Weather( + m_Facets[i], + area, + temperature, + chanceOfPercipitation, + chanceOfExtremeTemperature, + TimeSpan.FromSeconds(30.0) + ); + } + } + + public static bool CheckWeatherConflict(Map facet, Weather exclude, Rectangle2D area) + { + var list = GetWeatherList(facet); + + if (list == null) + { + return false; + } + + for (var i = 0; i < list.Count; ++i) + { + var w = list[i]; + + if (w != exclude && w.IntersectsWith(area)) + { + return true; + } + } + + return false; + } + + public static bool CheckIntersection(Rectangle2D r1, Rectangle2D r2) => r1.X < r2.X + r2.Width && + r2.X < r1.X + r1.Width && + r1.Y < r2.Y + r2.Height && + r2.Y < r1.Y + r1.Height; + + public static bool CheckContains(Rectangle2D big, Rectangle2D small) => + small.X >= big.X && small.Y >= big.Y && small.X + small.Width <= big.X + big.Width + && small.Y + small.Height <= big.Y + big.Height; + + public virtual bool IntersectsWith(Rectangle2D area) + { + for (var i = 0; i < Area.Length; ++i) + { + if (CheckIntersection(area, Area[i])) + { + return true; + } + } + + return false; + } + + public virtual void Reposition() + { + if (Area.Length == 0) + { + return; + } + + var width = Area[0].Width; + var height = Area[0].Height; + var area = new Rectangle2D(); var isValid = false; for (var j = 0; j < 10; ++j) { area = new Rectangle2D( - bounds.X + Utility.Random(bounds.Width - width), - bounds.Y + Utility.Random(bounds.Height - height), + Bounds.X + Utility.Random(Bounds.Width - width), + Bounds.Y + Utility.Random(Bounds.Height - height), width, height ); - if (!CheckWeatherConflict(m_Facets[i], null, area)) + if (!CheckWeatherConflict(Facet, this, area)) { isValid = true; } @@ -142,271 +252,181 @@ public partial class Weather if (!isValid) { - continue; + return; } - new Weather( - m_Facets[i], - [area], - temperature, - chanceOfPrecipitation, - chanceOfExtremeTemperature, - TimeSpan.FromSeconds(30.0) - ) - { Bounds = bounds, MoveSpeed = moveSpeed }; - } - } - - public static void AddWeather( - int temperature, int chanceOfPrecipitation, int chanceOfExtremeTemperature, params Rectangle2D[] area - ) - { - for (var i = 0; i < m_Facets.Length; ++i) - { - new Weather( - m_Facets[i], - area, - temperature, - chanceOfPrecipitation, - chanceOfExtremeTemperature, - TimeSpan.FromSeconds(30.0) - ); - } - } - - public static bool CheckWeatherConflict(Map facet, Weather exclude, Rectangle2D area) - { - var list = GetWeatherList(facet); - - if (list == null) - { - return false; + Area[0] = area; } - for (var i = 0; i < list.Count; ++i) + public virtual void RecalculateMovementAngle() { - var w = list[i]; + var angle = Utility.RandomDouble() * Math.PI * 2.0; - if (w != exclude && w.IntersectsWith(area)) + var cos = Math.Cos(angle); + var sin = Math.Sin(angle); + + MoveAngleX = (int)(100 * cos); + MoveAngleY = (int)(100 * sin); + } + + public virtual void MoveForward() + { + if (Area.Length == 0) { - return true; - } - } - - return false; - } - - public static bool CheckIntersection(Rectangle2D r1, Rectangle2D r2) => r1.X < r2.X + r2.Width && - r2.X < r1.X + r1.Width && - r1.Y < r2.Y + r2.Height && - r2.Y < r1.Y + r1.Height; - - public static bool CheckContains(Rectangle2D big, Rectangle2D small) => - small.X >= big.X && small.Y >= big.Y && small.X + small.Width <= big.X + big.Width - && small.Y + small.Height <= big.Y + big.Height; - - public virtual bool IntersectsWith(Rectangle2D area) - { - for (var i = 0; i < Area.Length; ++i) - { - if (CheckIntersection(area, Area[i])) - { - return true; - } - } - - return false; - } - - public virtual void Reposition() - { - if (Area.Length == 0) - { - return; - } - - var width = Area[0].Width; - var height = Area[0].Height; - - var area = new Rectangle2D(); - var isValid = false; - - for (var j = 0; j < 10; ++j) - { - area = new Rectangle2D( - Bounds.X + Utility.Random(Bounds.Width - width), - Bounds.Y + Utility.Random(Bounds.Height - height), - width, - height - ); - - if (!CheckWeatherConflict(Facet, this, area)) - { - isValid = true; + return; } - if (isValid) + for (var i = 0; i < 5; ++i) // try 5 times to find a valid spot { - break; - } - } + var xOffset = MoveSpeed * MoveAngleX / 100; + var yOffset = MoveSpeed * MoveAngleY / 100; - if (!isValid) - { - return; - } + var oldArea = Area[0]; + var newArea = new Rectangle2D(oldArea.X + xOffset, oldArea.Y + yOffset, oldArea.Width, oldArea.Height); - Area[0] = area; - } + if (!CheckWeatherConflict(Facet, this, newArea) && CheckContains(Bounds, newArea)) + { + Area[0] = newArea; + break; + } - public virtual void RecalculateMovementAngle() - { - var angle = Utility.RandomDouble() * Math.PI * 2.0; - - var cos = Math.Cos(angle); - var sin = Math.Sin(angle); - - MoveAngleX = (int)(100 * cos); - MoveAngleY = (int)(100 * sin); - } - - public virtual void MoveForward() - { - if (Area.Length == 0) - { - return; - } - - for (var i = 0; i < 5; ++i) // try 5 times to find a valid spot - { - var xOffset = MoveSpeed * MoveAngleX / 100; - var yOffset = MoveSpeed * MoveAngleY / 100; - - var oldArea = Area[0]; - var newArea = new Rectangle2D(oldArea.X + xOffset, oldArea.Y + yOffset, oldArea.Width, oldArea.Height); - - if (!CheckWeatherConflict(Facet, this, newArea) && CheckContains(Bounds, newArea)) - { - Area[0] = newArea; - break; - } - - RecalculateMovementAngle(); - } - } - - public virtual void OnTick() - { - if (m_Stage == 0) - { - m_Active = ChanceOfPrecipitation > Utility.Random(100); - m_ExtremeTemperature = ChanceOfExtremeTemperature > Utility.Random(100); - - if (MoveSpeed > 0) - { - Reposition(); RecalculateMovementAngle(); } } - if (m_Active) + public virtual void OnTick() { - if (m_Stage > 0 && MoveSpeed > 0) + if (m_Stage == 0) { - MoveForward(); - } + m_Active = ChanceOfPercipitation > Utility.Random(100); + m_ExtremeTemperature = ChanceOfExtremeTemperature > Utility.Random(100); - int type, density; - var temperature = Temperature; - - if (m_ExtremeTemperature) - { - temperature *= -1; - } - - if (m_Stage < 15) - { - density = m_Stage * 5; - } - else - { - density = Math.Clamp(150 - m_Stage * 5, 10, 70); - } - - if (density == 0) - { - type = 0xFE; - } - else if (temperature > 0) - { - type = 0; - } - else - { - type = 2; - } - - foreach (var ns in NetState.Instances) - { - var mob = ns.Mobile; - - if (mob == null || mob.Map != Facet) + if (MoveSpeed > 0) { - continue; + Reposition(); + RecalculateMovementAngle(); + } + } + + if (m_Active) + { + if (m_Stage > 0 && MoveSpeed > 0) + { + MoveForward(); } - var contains = Area.Length == 0; + int type, density; + var temperature = Temperature; - for (var j = 0; !contains && j < Area.Length; ++j) + if (m_ExtremeTemperature) { - contains = Area[j].Contains(mob.Location); + temperature *= -1; } - if (!contains) + if (m_Stage < 15) { - continue; + density = m_Stage * 5; + } + else + { + density = Math.Clamp(150 - m_Stage * 5, 10, 70); } - ns.SendWeather((byte)type, (byte)density, (byte)temperature); + if (density == 0) + { + type = 0xFE; + } + else if (temperature > 0) + { + type = 0; + } + else + { + type = 2; + } + + foreach (var ns in NetState.Instances) + { + var mob = ns.Mobile; + + if (mob == null || mob.Map != Facet) + { + continue; + } + + var contains = Area.Length == 0; + + for (var j = 0; !contains && j < Area.Length; ++j) + { + contains = Area[j].Contains(mob.Location); + } + + if (!contains) + { + continue; + } + + ns.SendWeather((byte)type, (byte)density, (byte)temperature); + } } + + m_Stage++; + m_Stage %= 30; } - - m_Stage++; - m_Stage %= 30; } -} -[SerializationGenerator(0)] -public partial class WeatherMap : MapItem -{ - [Constructible] - public WeatherMap() => SetDisplay(0, 0, 5119, 4095, 400, 400); - - public override string DefaultName => "weather map"; - - public override void OnDoubleClick(Mobile from) + public class WeatherMap : MapItem { - var facet = from.Map; - - if (facet == null) + [Constructible] + public WeatherMap() { - return; + SetDisplay(0, 0, 5119, 4095, 400, 400); } - var list = Weather.GetWeatherList(facet); - - ClearPins(); - - for (var i = 0; i < list.Count; ++i) + public WeatherMap(Serial serial) : base(serial) { - var w = list[i]; + } - for (var j = 0; j < w.Area.Length; ++j) + public override string DefaultName => "weather map"; + + public override void OnDoubleClick(Mobile from) + { + var facet = from.Map; + + if (facet == null) { - AddWorldPin(w.Area[j].X + w.Area[j].Width / 2, w.Area[j].Y + w.Area[j].Height / 2); + return; } + + var list = Weather.GetWeatherList(facet); + + ClearPins(); + + for (var i = 0; i < list.Count; ++i) + { + var w = list[i]; + + for (var j = 0; j < w.Area.Length; ++j) + { + AddWorldPin(w.Area[j].X + w.Area[j].Width / 2, w.Area[j].Y + w.Area[j].Height / 2); + } + } + + base.OnDoubleClick(from); } - base.OnDoubleClick(from); + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } } diff --git a/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs b/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs index f14b46792..063fc5221 100644 --- a/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs +++ b/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs @@ -6,7 +6,7 @@ namespace Server.Targets; public class AIControlMobileTarget : Target { - private readonly List _list; + private readonly List m_List; public AIControlMobileTarget(BaseAI ai, OrderType order) : base( -1, @@ -14,17 +14,19 @@ public class AIControlMobileTarget : Target order == OrderType.Attack ? TargetFlags.Harmful : TargetFlags.None ) { - _list = [ai]; + m_List = new List(); Order = order; + + AddAI(ai); } public OrderType Order { get; } public void AddAI(BaseAI ai) { - if (!_list.Contains(ai)) + if (!m_List.Contains(ai)) { - _list.Add(ai); + m_List.Add(ai); } } @@ -32,10 +34,10 @@ public class AIControlMobileTarget : Target { if (o is Mobile m) { - for (var i = 0; i < _list.Count; ++i) + for (var i = 0; i < m_List.Count; ++i) { - _list[i].EndPickTarget(from, m, Order); + m_List[i].EndPickTarget(from, m, Order); } } } -} +} \ No newline at end of file diff --git a/Projects/UOContent/Mobiles/AI/AnimalAI.cs b/Projects/UOContent/Mobiles/AI/AnimalAI.cs index a9d410483..8e3da75df 100644 --- a/Projects/UOContent/Mobiles/AI/AnimalAI.cs +++ b/Projects/UOContent/Mobiles/AI/AnimalAI.cs @@ -1,3 +1,12 @@ +// Ideas +// When you run on animals the panic +// When if (distance < 8 && Utility.RandomDouble() * Math.Sqrt( (8 - distance) / 6 ) >= incoming.Skills.AnimalTaming.Value) +// More your close, the more it can panic +/* + * AnimalHunterAI, AnimalHidingAI, AnimalDomesticAI... + * + */ + namespace Server.Mobiles; public class AnimalAI : BaseAI @@ -6,16 +15,29 @@ public class AnimalAI : BaseAI { } - public override double FleeHealthThreshold => 0.1; // 10% is default - public override double FleeChance => 0.1; // 10% is default - public override double BackoffChance => 0.5; // 50% is default - public override bool DoActionWander() { - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + // New, only flee @ 10% + + var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + + if (!m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 && m_Mobile.CanFlee) // Less than 10% health { - this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); - Mobile.Combatant = Mobile.FocusMob; + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am low on health!"); + } + + Action = ActionType.Flee; + } + else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + } + + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } else @@ -28,34 +50,98 @@ public class AnimalAI : BaseAI public override bool DoActionCombat() { - var combatant = Mobile.Combatant; + var combatant = m_Mobile.Combatant; - if (!IsValidCombatant(combatant)) + if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || + combatant.IsDeadBondedPet) { - DebugSay("My combatant is gone!"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is gone!"); + } Action = ActionType.Wander; return true; } - if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) { - if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) + if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) { - this.DebugSayFormatted($"I cannot find {combatant.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I cannot find {combatant.Name}"); + } Action = ActionType.Wander; return true; } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); + } } - else if (Core.TickCount - Mobile.LastMoveTime > 400) + else if (Core.TickCount - m_Mobile.LastMoveTime > 400) { - Mobile.Direction = Mobile.GetDirectionTo(combatant); + m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); } - if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee) { - this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); + var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + + if (hitPercent <= 0.1) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am low on health!"); + } + + Action = ActionType.Flee; + return true; + } + } + + if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); + } + } + + return true; + } + + public override bool DoActionBackoff() + { + var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + + if (!m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 && m_Mobile.CanFlee) // Less than 10% health + { + Action = ActionType.Flee; + } + else if (AcquireFocusMob(m_Mobile.RangePerception * 2, FightMode.Closest, true, false, true)) + { + if (WalkMobileRange(m_Mobile.FocusMob, 1, false, m_Mobile.RangePerception, m_Mobile.RangePerception * 2)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Well, here I am safe"); + } + + Action = ActionType.Wander; + } + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost my focus, lets relax"); + } + + Action = ActionType.Wander; } return true; @@ -63,9 +149,9 @@ public class AnimalAI : BaseAI public override bool DoActionFlee() { - AcquireFocusMob(Mobile.RangePerception * 2, Mobile.FightMode, true, false, true); + AcquireFocusMob(m_Mobile.RangePerception * 2, m_Mobile.FightMode, true, false, true); - Mobile.FocusMob ??= Mobile.Combatant; + m_Mobile.FocusMob ??= m_Mobile.Combatant; return base.DoActionFlee(); } diff --git a/Projects/UOContent/Mobiles/AI/ArcherAI.cs b/Projects/UOContent/Mobiles/AI/ArcherAI.cs index dedd914c9..ad6788952 100644 --- a/Projects/UOContent/Mobiles/AI/ArcherAI.cs +++ b/Projects/UOContent/Mobiles/AI/ArcherAI.cs @@ -1,3 +1,4 @@ +using System; using Server.Items; namespace Server.Mobiles; @@ -8,18 +9,21 @@ public class ArcherAI : BaseAI { } - public override double FleeHealthThreshold => 0.2; // 20% is default - public override double FleeChance => 0.1; // 10% is default - public override bool DoActionWander() { - DebugSay("I have no combatant"); - - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (m_Mobile.Debug) { - this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name} and I will attack"); + m_Mobile.DebugSay("I have no combatant"); + } - Mobile.Combatant = Mobile.FocusMob; + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name} and I will attack"); + } + + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } else @@ -32,44 +36,62 @@ public class ArcherAI : BaseAI public override bool DoActionCombat() { - var combatant = Mobile.Combatant; + var combatant = m_Mobile.Combatant; - if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || + if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - DebugSay("My combatant is gone, so my guard is up"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + } Action = ActionType.Guard; return true; } - if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.Weapon.MaxRange)) + if (Core.TickCount - m_Mobile.LastMoveTime > 1000 && !WalkMobileRange( + combatant, + 1, + true, + m_Mobile.RangeFight, + m_Mobile.Weapon.MaxRange + )) { - this.DebugSayFormatted($"I am still not in range of {combatant.Name}"); - - if (!Mobile.InRange(combatant, Mobile.ChaseLeashRange)) + if (m_Mobile.Debug) { - this.DebugSayFormatted($"I have lost {combatant.Name}"); + m_Mobile.DebugSay($"I am still not in range of {combatant.Name}"); + } - Mobile.Combatant = null; + if ((int)m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have lost {combatant.Name}"); + } + + m_Mobile.Combatant = null; Action = ActionType.Guard; return true; } } - else if (Core.TickCount - Mobile.LastMoveTime > 400) + else if (Core.TickCount - m_Mobile.LastMoveTime > 400) { - Mobile.Direction = Mobile.GetDirectionTo(combatant); + m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); } - if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); + } return true; } // When we have no ammo, we flee - var pack = Mobile.Backpack; + var pack = m_Mobile.Backpack; if (pack?.FindItemByType() == null) { @@ -77,16 +99,31 @@ public class ArcherAI : BaseAI return true; } + // At 20% we should check if we must leave + if (m_Mobile.Combatant != null && m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100 && m_Mobile.CanFlee) + { + // 10% to flee + the diff of hits + var fleeChance = 10 + Math.Max(0, m_Mobile.Combatant.Hits - m_Mobile.Hits); + + if (Utility.Random(0, 100) > fleeChance) + { + Action = ActionType.Flee; + } + } + return true; } public override bool DoActionGuard() { - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + } - Mobile.Combatant = Mobile.FocusMob; + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } else diff --git a/Projects/UOContent/Mobiles/AI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI.cs new file mode 100644 index 000000000..5ce7ba5a7 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI.cs @@ -0,0 +1,3322 @@ +using System; +using System.Collections.Generic; +using Server.Collections; +using Server.ContextMenus; +using Server.Engines.Quests.Necro; +using Server.Engines.Spawners; +using Server.Engines.Virtues; +using Server.Factions; +using Server.Gumps; +using Server.Items; +using Server.Network; +using Server.Spells; +using Server.Spells.Spellweaving; +using Server.Targets; +using MoveImpl = Server.Movement.MovementImpl; + +namespace Server.Mobiles; + +public enum AIType +{ + AI_Use_Default, + AI_Melee, + AI_Animal, + AI_Archer, + AI_Healer, + AI_Vendor, + AI_Mage, + AI_Berserk, + AI_Predator, + AI_Thief +} + +public enum ActionType +{ + Wander, + Combat, + Guard, + Flee, + Backoff, + Interact +} + +public abstract class BaseAI +{ + // How many milliseconds until our next move can we consider it ok to move without deferring/blocking. + private const int FuzzyTimeUntilNextMove = 24; + private static readonly SkillName[] m_KeywordTable = + { + SkillName.Parry, + SkillName.Healing, + SkillName.Hiding, + SkillName.Stealing, + SkillName.Alchemy, + SkillName.AnimalLore, + SkillName.ItemID, + SkillName.ArmsLore, + SkillName.Begging, + SkillName.Blacksmith, + SkillName.Fletching, + SkillName.Peacemaking, + SkillName.Camping, + SkillName.Carpentry, + SkillName.Cartography, + SkillName.Cooking, + SkillName.DetectHidden, + SkillName.Discordance, // ?? + SkillName.EvalInt, + SkillName.Fishing, + SkillName.Provocation, + SkillName.Lockpicking, + SkillName.Magery, + SkillName.MagicResist, + SkillName.Tactics, + SkillName.Snooping, + SkillName.RemoveTrap, + SkillName.Musicianship, + SkillName.Poisoning, + SkillName.Archery, + SkillName.SpiritSpeak, + SkillName.Tailoring, + SkillName.AnimalTaming, + SkillName.TasteID, + SkillName.Tinkering, + SkillName.Veterinary, + SkillName.Forensics, + SkillName.Herding, + SkillName.Tracking, + SkillName.Stealth, + SkillName.Inscribe, + SkillName.Swords, + SkillName.Macing, + SkillName.Fencing, + SkillName.Wrestling, + SkillName.Lumberjacking, + SkillName.Mining, + SkillName.Meditation + }; + + protected ActionType m_Action; + + public readonly BaseCreature m_Mobile; + + private long m_NextDetectHidden; + private long m_NextStopGuard; + + protected PathFollower m_Path; + public Timer m_Timer; + + public BaseAI(BaseCreature m) + { + m_Mobile = m; + + m_Timer = new AITimer(this); + + bool activate; + + if (!m.PlayerRangeSensitive) + { + activate = true; + } + else if (World.Loading) + { + activate = false; + } + else if (m.Map == null || m.Map == Map.Internal || !m.Map.GetSector(m.Location).Active) + { + activate = false; + } + else + { + activate = true; + } + + if (activate) + { + m_Timer.Start(); + } + + Action = ActionType.Wander; + } + + public ActionType Action + { + get => m_Action; + set + { + m_Action = value; + OnActionChanged(); + } + } + + public long NextMove { get; set; } + + public virtual bool CanDetectHidden => m_Mobile.Skills.DetectHidden.Value > 0; + + public virtual bool WasNamed(string speech) + { + var name = m_Mobile.Name; + + return name != null && speech.InsensitiveStartsWith(name); + } + + public virtual void GetContextMenuEntries(Mobile from, ref PooledRefList list) + { + if (!from.Alive || !m_Mobile.Controlled || !from.InRange(m_Mobile, 14)) + { + return; + } + + var isDeadPet = m_Mobile.IsDeadPet; + + if (from == m_Mobile.ControlMaster) + { + list.Add(new InternalEntry(6107, 14, OrderType.Guard, !isDeadPet)); // Command: Guard + list.Add(new InternalEntry(6108, 14, OrderType.Follow, true)); // Command: Follow + + if (m_Mobile.CanDrop) + { + list.Add(new InternalEntry(6109, 14, OrderType.Drop, !isDeadPet)); // Command: Drop + } + + list.Add(new InternalEntry(6111, 14, OrderType.Attack, !isDeadPet)); // Command: Kill + + list.Add(new InternalEntry(6112, 14, OrderType.Stop, true)); // Command: Stop + list.Add(new InternalEntry(6114, 14, OrderType.Stay, true)); // Command: Stay + + if (!m_Mobile.Summoned && m_Mobile is not GrizzledMare) + { + list.Add(new InternalEntry(6110, 14, OrderType.Friend, true)); // Add Friend + list.Add(new InternalEntry(6099, 14, OrderType.Unfriend, true)); // Remove Friend + list.Add(new InternalEntry(6113, 14, OrderType.Transfer, !isDeadPet)); // Transfer + } + + list.Add(new InternalEntry(6118, 14, OrderType.Release, true)); // Release + } + else if (m_Mobile.IsPetFriend(from)) + { + list.Add(new InternalEntry(6108, 14, OrderType.Follow, true)); // Command: Follow + list.Add(new InternalEntry(6112, 14, OrderType.Stop, !isDeadPet)); // Command: Stop + list.Add(new InternalEntry(6114, 14, OrderType.Stay, true)); // Command: Stay + } + } + + public virtual void BeginPickTarget(Mobile from, OrderType order) + { + if (m_Mobile.Deleted || !m_Mobile.Controlled || !from.InRange(m_Mobile, 14) || from.Map != m_Mobile.Map) + { + return; + } + + var isOwner = from == m_Mobile.ControlMaster; + var isFriend = !isOwner && m_Mobile.IsPetFriend(from); + + if (!isOwner && !isFriend) + { + return; + } + + if (isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop) + { + return; + } + + if (from.Target == null) + { + if (order == OrderType.Transfer) + { + from.SendLocalizedMessage(502038); // Click on the person to transfer ownership to. + } + else if (order == OrderType.Friend) + { + from.SendLocalizedMessage(502020); // Click on the player whom you wish to make a co-owner. + } + else if (order == OrderType.Unfriend) + { + from.SendLocalizedMessage(1070948); // Click on the player whom you wish to remove as a co-owner. + } + + from.Target = new AIControlMobileTarget(this, order); + } + else if (from.Target is AIControlMobileTarget t) + { + if (t.Order == order) + { + t.AddAI(this); + } + } + } + + public virtual void OnAggressiveAction(Mobile aggressor) + { + var currentCombat = m_Mobile.Combatant; + + if (currentCombat != null && !aggressor.Hidden && currentCombat != aggressor && + m_Mobile.GetDistanceToSqrt(currentCombat) > m_Mobile.GetDistanceToSqrt(aggressor)) + { + m_Mobile.Combatant = aggressor; + } + } + + public virtual void EndPickTarget(Mobile from, Mobile target, OrderType order) + { + if (m_Mobile.Deleted || !m_Mobile.Controlled || !from.InRange(m_Mobile, 14) || from.Map != m_Mobile.Map || + !from.CheckAlive()) + { + return; + } + + var isOwner = from == m_Mobile.ControlMaster; + var isFriend = !isOwner && m_Mobile.IsPetFriend(from); + + if (!isOwner && !isFriend) + { + return; + } + + if (isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop) + { + return; + } + + if (order == OrderType.Attack) + { + if (target is BaseCreature creature && creature.IsScaryToPets && m_Mobile.IsScaredOfScaryThings) + { + m_Mobile.SayTo(from, "Your pet refuses to attack this creature!"); + return; + } + + if (SolenHelper.CheckRedFriendship(from) && + target is RedSolenInfiltratorQueen or RedSolenInfiltratorWarrior or RedSolenQueen or RedSolenWarrior or RedSolenWorker + || SolenHelper.CheckBlackFriendship(from) && + target is BlackSolenInfiltratorQueen or BlackSolenInfiltratorWarrior or BlackSolenQueen or BlackSolenWarrior or BlackSolenWorker) + { + from.SendAsciiMessage("You can not force your pet to attack a creature you are protected from."); + return; + } + + if (target is BaseFactionGuard) + { + m_Mobile.SayTo(from, "Your pet refuses to attack the guard."); + return; + } + } + + if (m_Mobile.CheckControlChance(from)) + { + m_Mobile.ControlTarget = target; + m_Mobile.ControlOrder = order; + } + } + + public virtual bool HandlesOnSpeech(Mobile from) + { + if (from.AccessLevel >= AccessLevel.GameMaster) + { + return true; + } + + if (from.Alive && m_Mobile.Controlled && m_Mobile.Commandable && + (from == m_Mobile.ControlMaster || m_Mobile.IsPetFriend(from))) + { + return true; + } + + return from.Alive && from.InRange(m_Mobile.Location, 3) && m_Mobile.IsHumanInTown(); + } + + public virtual void OnSpeech(SpeechEventArgs e) + { + if (e.Mobile.Alive && e.Mobile.InRange(m_Mobile.Location, 3) && m_Mobile.IsHumanInTown()) + { + if (e.HasKeyword(0x9D) && WasNamed(e.Speech)) // *move* + { + if (m_Mobile.Combatant != null) + { + // I am too busy fighting to deal with thee! + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); + } + else + { + // Excuse me? + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501516); + WalkRandomInHome(2, 2, 1); + } + } + else if (e.HasKeyword(0x9E) && WasNamed(e.Speech)) // *time* + { + if (m_Mobile.Combatant != null) + { + // I am too busy fighting to deal with thee! + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); + } + else + { + Clock.GetTime(m_Mobile, out var generalNumber, out _); + + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, generalNumber); + } + } + else if (e.HasKeyword(0x6C) && WasNamed(e.Speech)) // *train + { + if (m_Mobile.Combatant != null) + { + // I am too busy fighting to deal with thee! + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); + } + else + { + var foundSomething = false; + + var ourSkills = m_Mobile.Skills; + var theirSkills = e.Mobile.Skills; + + for (var i = 0; i < ourSkills.Length && i < theirSkills.Length; ++i) + { + var skill = ourSkills[i]; + var theirSkill = theirSkills[i]; + + if (skill?.Base >= 60.0 && m_Mobile.CheckTeach(skill.SkillName, e.Mobile)) + { + var toTeach = skill.Base / 3.0; + + if (toTeach > 42.0) + { + toTeach = 42.0; + } + + if (toTeach > theirSkill.Base) + { + var number = 1043059 + i; + + if (number > 1043107) + { + continue; + } + + if (!foundSomething) + { + m_Mobile.Say(1043058); // I can train the following: + } + + m_Mobile.Say(number); + + foundSomething = true; + } + } + } + + if (!foundSomething) + { + m_Mobile.Say(501505); // Alas, I cannot teach thee anything. + } + } + } + else + { + var toTrain = (SkillName)(-1); + + for (var i = 0; toTrain == (SkillName)(-1) && i < e.Keywords.Length; ++i) + { + var keyword = e.Keywords[i]; + + if (keyword == 0x154) + { + toTrain = SkillName.Anatomy; + } + else if (keyword >= 0x6D && keyword <= 0x9C) + { + var index = keyword - 0x6D; + + if (index >= 0 && index < m_KeywordTable.Length) + { + toTrain = m_KeywordTable[index]; + } + } + } + + if (toTrain != (SkillName)(-1) && WasNamed(e.Speech)) + { + if (m_Mobile.Combatant != null) + { + // I am too busy fighting to deal with thee! + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); + } + else + { + var skills = m_Mobile.Skills; + var skill = skills[toTrain]; + + if (skill == null || skill.Base < 60.0 || !m_Mobile.CheckTeach(toTrain, e.Mobile)) + { + m_Mobile.Say(501507); // 'Tis not something I can teach thee of. + } + else + { + m_Mobile.Teach(toTrain, e.Mobile, 0, false); + } + } + } + } + } + + if (m_Mobile.Controlled && m_Mobile.Commandable) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Listening..."); + } + + var isOwner = e.Mobile == m_Mobile.ControlMaster; + var isFriend = !isOwner && m_Mobile.IsPetFriend(e.Mobile); + + if (e.Mobile.Alive && (isOwner || isFriend)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("It's from my master"); + } + + var keywords = e.Keywords; + var speech = e.Speech; + + // First, check the all* + for (var i = 0; i < keywords.Length; ++i) + { + var keyword = keywords[i]; + + switch (keyword) + { + case 0x164: // all come + { + if (!isOwner) + { + break; + } + + if (m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Come; + } + + return; + } + case 0x165: // all follow + { + BeginPickTarget(e.Mobile, OrderType.Follow); + return; + } + case 0x166: // all guard + case 0x16B: // all guard me + { + if (!isOwner) + { + break; + } + + if (m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Guard; + } + + return; + } + case 0x167: // all stop + { + if (m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stop; + } + + return; + } + case 0x168: // all kill + case 0x169: // all attack + { + if (!isOwner) + { + break; + } + + BeginPickTarget(e.Mobile, OrderType.Attack); + return; + } + case 0x16C: // all follow me + { + if (m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = e.Mobile; + m_Mobile.ControlOrder = OrderType.Follow; + } + + return; + } + case 0x170: // all stay + { + if (m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stay; + } + + return; + } + } + } + + // No all*, so check *command + for (var i = 0; i < keywords.Length; ++i) + { + var keyword = keywords[i]; + + switch (keyword) + { + case 0x155: // *come + { + if (!isOwner) + { + break; + } + + if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Come; + } + + return; + } + case 0x156: // *drop + { + if (!isOwner) + { + break; + } + + if (!m_Mobile.IsDeadPet && !m_Mobile.Summoned && WasNamed(speech) && + m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Drop; + } + + return; + } + case 0x15A: // *follow + { + if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + BeginPickTarget(e.Mobile, OrderType.Follow); + } + + return; + } + case 0x15B: // *friend + { + if (!isOwner) + { + break; + } + + if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + if (m_Mobile.Summoned || m_Mobile is GrizzledMare) + { + e.Mobile.SendLocalizedMessage( + 1005481 + ); // Summoned creatures are loyal only to their summoners. + } + else if (e.Mobile.HasTrade) + { + e.Mobile.SendLocalizedMessage( + 1070947 + ); // You cannot friend a pet with a trade pending + } + else + { + BeginPickTarget(e.Mobile, OrderType.Friend); + } + } + + return; + } + case 0x15C: // *guard + { + if (!isOwner) + { + break; + } + + if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Guard; + } + + return; + } + case 0x15D: // *kill + case 0x15E: // *attack + { + if (!isOwner) + { + break; + } + + if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + BeginPickTarget(e.Mobile, OrderType.Attack); + } + + return; + } + case 0x15F: // *patrol + { + if (!isOwner) + { + break; + } + + if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Patrol; + } + + return; + } + case 0x161: // *stop + { + if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stop; + } + + return; + } + case 0x163: // *follow me + { + if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = e.Mobile; + m_Mobile.ControlOrder = OrderType.Follow; + } + + return; + } + case 0x16D: // *release + { + if (!isOwner) + { + break; + } + + if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + if (!m_Mobile.Summoned) + { + e.Mobile.SendGump(new ConfirmReleaseGump(e.Mobile, m_Mobile)); + } + else + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Release; + } + } + + return; + } + case 0x16E: // *transfer + { + if (!isOwner) + { + break; + } + + if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + if (m_Mobile.Summoned || m_Mobile is GrizzledMare) + { + e.Mobile.SendLocalizedMessage( + 1005487 + ); // You cannot transfer ownership of a summoned creature. + } + else if (e.Mobile.HasTrade) + { + e.Mobile.SendLocalizedMessage( + 1010507 + ); // You cannot transfer a pet with a trade pending + } + else + { + BeginPickTarget(e.Mobile, OrderType.Transfer); + } + } + + return; + } + case 0x16F: // *stay + { + if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stay; + } + + return; + } + } + } + } + } + else + { + if (e.Mobile.AccessLevel >= AccessLevel.GameMaster) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("It's from a GM"); + } + + if (m_Mobile.FindMyName(e.Speech, true)) + { + var str = e.Speech.Split(' '); + int i; + + for (i = 0; i < str.Length; i++) + { + var word = str[i]; + + if (word.InsensitiveEquals("obey")) + { + m_Mobile.SetControlMaster(e.Mobile); + + if (m_Mobile.Summoned) + { + m_Mobile.SummonMaster = e.Mobile; + } + + return; + } + } + } + } + } + } + + public virtual bool Think() + { + if (m_Mobile.Deleted) + { + return false; + } + + if (CheckFlee()) + { + return true; + } + + switch (Action) + { + case ActionType.Wander: + { + m_Mobile.OnActionWander(); + return DoActionWander(); + } + + case ActionType.Combat: + { + m_Mobile.OnActionCombat(); + return DoActionCombat(); + } + + case ActionType.Guard: + { + m_Mobile.OnActionGuard(); + return DoActionGuard(); + } + + case ActionType.Flee: + { + m_Mobile.OnActionFlee(); + return DoActionFlee(); + } + + case ActionType.Interact: + { + m_Mobile.OnActionInteract(); + return DoActionInteract(); + } + + case ActionType.Backoff: + { + m_Mobile.OnActionBackoff(); + return DoActionBackoff(); + } + + default: + { + return false; + } + } + } + + public virtual void OnActionChanged() + { + switch (Action) + { + case ActionType.Wander: + { + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + m_Mobile.FocusMob = null; + m_Mobile.SetCurrentSpeedToPassive(); + break; + } + + case ActionType.Combat: + { + m_Mobile.Warmode = true; + m_Mobile.FocusMob = null; + m_Mobile.SetCurrentSpeedToActive(); + break; + } + + case ActionType.Guard: + { + m_Mobile.Warmode = true; + m_Mobile.FocusMob = null; + m_Mobile.Combatant = null; + m_NextStopGuard = Core.TickCount + (int)TimeSpan.FromSeconds(10).TotalMilliseconds; + m_Mobile.SetCurrentSpeedToActive(); + break; + } + + case ActionType.Flee: + { + m_Mobile.Warmode = true; + m_Mobile.FocusMob = null; + m_Mobile.SetCurrentSpeedToActive(); + break; + } + + case ActionType.Interact: + { + m_Mobile.Warmode = false; + m_Mobile.SetCurrentSpeedToPassive(); + break; + } + + case ActionType.Backoff: + { + m_Mobile.Warmode = false; + m_Mobile.SetCurrentSpeedToPassive(); + break; + } + } + } + + public virtual bool OnAtWayPoint() => true; + + public virtual bool DoActionWander() + { + if (CheckHerding()) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Praise the shepherd!"); + } + } + else if (m_Mobile.CurrentWayPoint != null) + { + var point = m_Mobile.CurrentWayPoint; + if ((point.X != m_Mobile.Location.X || point.Y != m_Mobile.Location.Y) && point.Map == m_Mobile.Map && + point.Parent == null && !point.Deleted) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I will move towards my waypoint."); + } + + DoMove(m_Mobile.GetDirectionTo(m_Mobile.CurrentWayPoint)); + } + else if (OnAtWayPoint()) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I will go to the next waypoint"); + } + + m_Mobile.CurrentWayPoint = point.NextPoint; + if (point.NextPoint?.Deleted == true) + { + m_Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; + } + } + } + else if (m_Mobile.IsAnimatedDead) + { + // animated dead follow their master + var master = m_Mobile.SummonMaster; + + if (master != null && master.Map == m_Mobile.Map && master.InRange(m_Mobile, m_Mobile.RangePerception)) + { + MoveTo(master, false, 1); + } + else + { + WalkRandomInHome(2, 2, 1); + } + } + else if (CheckMove() && CanMoveNow(out _) && !m_Mobile.CheckIdle()) + { + WalkRandomInHome(2, 2, 1); + } + + if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && + !m_Mobile.Combatant.IsDeadBondedPet) + { + m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); + } + + return true; + } + + public virtual bool DoActionCombat() + { + if (Core.AOS && CheckHerding()) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Praise the shepherd!"); + } + + return true; + } + + var combatant = m_Mobile.Combatant; + + if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || + combatant.IsDeadBondedPet) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is gone!"); + } + + Action = ActionType.Wander; + return true; + } + + m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); + if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); + } + } + + return true; + } + + public virtual bool DoActionGuard() + { + if (Core.AOS && CheckHerding()) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Praise the shepherd!"); + } + } + else if (Core.TickCount - m_NextStopGuard < 0) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am on guard"); + } + // m_Mobile.Turn( Utility.Random(0, 2) - 1 ); + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I stopped being on guard"); + } + + Action = ActionType.Wander; + } + + return true; + } + + public virtual bool DoActionFlee() + { + var from = m_Mobile.FocusMob; + + if (from?.Deleted != false || from.Map != m_Mobile.Map) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost him"); + } + + Action = ActionType.Guard; + return true; + } + + if (WalkMobileRange(from, 1, true, m_Mobile.RangePerception * 2, m_Mobile.RangePerception * 3)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have fled"); + } + + Action = ActionType.Guard; + return true; + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am fleeing!"); + } + + return true; + } + + public virtual bool DoActionInteract() => true; + + public virtual bool DoActionBackoff() => true; + + public virtual bool Obey() => + !m_Mobile.Deleted && m_Mobile.ControlOrder switch + { + OrderType.None => DoOrderNone(), + OrderType.Come => DoOrderCome(), + OrderType.Drop => DoOrderDrop(), + OrderType.Friend => DoOrderFriend(), + OrderType.Unfriend => DoOrderUnfriend(), + OrderType.Guard => DoOrderGuard(), + OrderType.Attack => DoOrderAttack(), + OrderType.Patrol => DoOrderPatrol(), + OrderType.Release => DoOrderRelease(), + OrderType.Stay => DoOrderStay(), + OrderType.Stop => DoOrderStop(), + OrderType.Follow => DoOrderFollow(), + OrderType.Transfer => DoOrderTransfer(), + _ => false + }; + + public virtual void OnCurrentOrderChanged() + { + if (m_Mobile.Deleted || m_Mobile.ControlMaster?.Deleted != false) + { + return; + } + + switch (m_Mobile.ControlOrder) + { + case OrderType.None: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.Home = m_Mobile.Location; + m_Mobile.SetCurrentSpeedToPassive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + } + + case OrderType.Come: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.SetCurrentSpeedToActive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + } + + case OrderType.Drop: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.SetCurrentSpeedToPassive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + } + + case OrderType.Friend: + case OrderType.Unfriend: + { + m_Mobile.ControlMaster.RevealingAction(); + break; + } + + case OrderType.Guard: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.SetCurrentSpeedToActive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + m_Mobile.Warmode = true; + m_Mobile.Combatant = null; + m_Mobile.ControlMaster.SendLocalizedMessage(1049671, m_Mobile.Name); // ~1_PETNAME~ is now guarding you. + break; + } + + case OrderType.Attack: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.SetCurrentSpeedToActive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + + m_Mobile.Warmode = true; + m_Mobile.Combatant = null; + break; + } + + case OrderType.Patrol: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.SetCurrentSpeedToActive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + } + + case OrderType.Release: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.SetCurrentSpeedToPassive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + } + + case OrderType.Stay: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.SetCurrentSpeedToPassive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + } + + case OrderType.Stop: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.Home = m_Mobile.Location; + m_Mobile.SetCurrentSpeedToPassive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + } + + case OrderType.Follow: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.SetCurrentSpeedToActive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + } + + case OrderType.Transfer: + { + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.SetCurrentSpeedToPassive(); + m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + } + } + } + + public virtual bool DoOrderNone() + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have no order"); + } + + WalkRandomInHome(3, 2, 1); + + if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && + !m_Mobile.Combatant.IsDeadBondedPet) + { + m_Mobile.Warmode = true; + m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); + } + else + { + m_Mobile.Warmode = false; + } + + return true; + } + + public virtual bool DoOrderCome() + { + if (m_Mobile.ControlMaster?.Deleted != false) + { + return true; + } + + var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlMaster); + + if (iCurrDist > m_Mobile.RangePerception) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost my master. I stay here"); + } + + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My master told me come"); + } + + // Not exactly OSI style, but better than nothing. + var bRun = iCurrDist > 5; + + if (WalkMobileRange(m_Mobile.ControlMaster, 1, bRun, 0, 1)) + { + if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && + !m_Mobile.Combatant.IsDeadBondedPet) + { + m_Mobile.Warmode = true; + // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun); + } + else + { + m_Mobile.Warmode = false; + } + } + } + + return true; + } + + public virtual bool DoOrderDrop() + { + if (m_Mobile.IsDeadPet || !m_Mobile.CanDrop) + { + return true; + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I drop my stuff for my master"); + } + + var pack = m_Mobile.Backpack; + + if (pack != null) + { + var list = pack.Items; + + for (var i = list.Count - 1; i >= 0; --i) + { + if (i < list.Count) + { + list[i].MoveToWorld(m_Mobile.Location, m_Mobile.Map); + } + } + } + + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; + + return true; + } + + public virtual bool CheckHerding() + { + var target = m_Mobile.TargetLocation; + + if (target == null) + { + return false; // Creature is not being herded + } + + var distance = m_Mobile.GetDistanceToSqrt(target); + + if (distance is not (< 1 or > 15)) + { + DoMove(m_Mobile.GetDirectionTo(target)); + return true; + } + + if (distance < 1 && target.X == 1076 && target.Y == 450 && m_Mobile is HordeMinionFamiliar) + { + if (m_Mobile.ControlMaster is PlayerMobile pm) + { + var qs = pm.Quest; + + if (qs is DarkTidesQuest) + { + var obj = qs.FindObjective(); + + if (obj?.Completed == false) + { + m_Mobile.AddToBackpack(new ScrollOfAbraxus()); + obj.Complete(); + } + } + } + } + + m_Mobile.TargetLocation = null; + return false; // At the target or too far away + } + + public virtual bool DoOrderFollow() + { + if (CheckHerding()) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Praise the shepherd!"); + } + } + else if (m_Mobile.ControlTarget?.Deleted == false && m_Mobile.ControlTarget != m_Mobile) + { + var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlTarget); + + if (iCurrDist > m_Mobile.RangePerception) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost the one to follow. I stay here"); + } + + if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && + !m_Mobile.Combatant.IsDeadBondedPet) + { + m_Mobile.Warmode = true; + m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); + } + else + { + m_Mobile.Warmode = false; + } + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"My master told me to follow: {m_Mobile.ControlTarget.Name}"); + } + + // Not exactly OSI style, but better than nothing. + var bRun = iCurrDist > 5; + + if (WalkMobileRange(m_Mobile.ControlTarget, 1, bRun, 0, 1)) + { + if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && + !m_Mobile.Combatant.IsDeadBondedPet) + { + m_Mobile.Warmode = true; + // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun); + } + else + { + m_Mobile.Warmode = false; + if (Core.AOS) + { + m_Mobile.CurrentSpeed = 0.1; + } + } + } + } + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have nobody to follow"); + } + + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; + } + + return true; + } + + public virtual bool DoOrderFriend() + { + var from = m_Mobile.ControlMaster; + var to = m_Mobile.ControlTarget; + + if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) + { + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* + } + else + { + var youngFrom = from is PlayerMobile mobile && mobile.Young; + var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; + + if (youngFrom && !youngTo) + { + from.SendLocalizedMessage(502040); // As a young player, you may not friend pets to older players. + } + else if (!youngFrom && youngTo) + { + from.SendLocalizedMessage(502041); // As an older player, you may not friend pets to young players. + } + else if (from.CanBeBeneficial(to, true)) + { + NetState fromState = from.NetState, toState = to.NetState; + + if (fromState != null && toState != null) + { + if (from.HasTrade) + { + from.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending + } + else if (to.HasTrade) + { + to.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending + } + else if (m_Mobile.IsPetFriend(to)) + { + from.SendLocalizedMessage(1049691); // That person is already a friend. + } + else if (!m_Mobile.AllowNewPetFriend) + { + from.SendLocalizedMessage( + 1005482 + ); // Your pet does not seem to be interested in making new friends right now. + } + else + { + // ~1_NAME~ will now accept movement commands from ~2_NAME~. + from.SendLocalizedMessage(1049676, $"{m_Mobile.Name}\t{to.Name}"); + + /* ~1_NAME~ has granted you the ability to give orders to their pet ~2_PET_NAME~. + * This creature will now consider you as a friend. + */ + to.SendLocalizedMessage(1043246, $"{from.Name}\t{m_Mobile.Name}"); + + m_Mobile.AddPetFriend(to); + + m_Mobile.ControlTarget = to; + m_Mobile.ControlOrder = OrderType.Follow; + + return true; + } + } + } + } + + m_Mobile.ControlTarget = from; + m_Mobile.ControlOrder = OrderType.Follow; + + return true; + } + + public virtual bool DoOrderUnfriend() + { + var from = m_Mobile.ControlMaster; + var to = m_Mobile.ControlTarget; + + if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) + { + m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* + } + else if (!m_Mobile.IsPetFriend(to)) + { + from.SendLocalizedMessage(1070953); // That person is not a friend. + } + else + { + // ~1_NAME~ will no longer accept movement commands from ~2_NAME~. + from.SendLocalizedMessage(1070951, $"{m_Mobile.Name}\t{to.Name}"); + + /* ~1_NAME~ has no longer granted you the ability to give orders to their pet ~2_PET_NAME~. + * This creature will no longer consider you as a friend. + */ + to.SendLocalizedMessage(1070952, $"{from.Name}\t{m_Mobile.Name}"); + + m_Mobile.RemovePetFriend(to); + } + + m_Mobile.ControlTarget = from; + m_Mobile.ControlOrder = OrderType.Follow; + + return true; + } + + public virtual bool DoOrderGuard() + { + if (m_Mobile.IsDeadPet) + { + return true; + } + + var controlMaster = m_Mobile.ControlMaster; + + if (controlMaster?.Deleted != false) + { + return true; + } + + var combatant = m_Mobile.Combatant; + + var aggressors = controlMaster.Aggressors; + + if (aggressors.Count > 0) + { + for (var i = 0; i < aggressors.Count; ++i) + { + var info = aggressors[i]; + var attacker = info.Attacker; + + if (attacker?.Deleted == false && + attacker.GetDistanceToSqrt(m_Mobile) <= m_Mobile.RangePerception) + { + if (combatant == null || attacker.GetDistanceToSqrt(controlMaster) < + combatant.GetDistanceToSqrt(controlMaster)) + { + combatant = attacker; + } + } + } + + if (combatant != null && m_Mobile.Debug) + { + m_Mobile.DebugSay("Crap, my master has been attacked! I will attack one of those bastards!"); + } + } + + if (combatant?.Deleted == false && combatant != m_Mobile && combatant != m_Mobile.ControlMaster && + combatant.Alive && !combatant.IsDeadBondedPet && m_Mobile.CanSee(combatant) && + m_Mobile.CanBeHarmful(combatant, false) && combatant.Map == m_Mobile.Map) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Guarding from target..."); + } + + m_Mobile.Combatant = combatant; + m_Mobile.FocusMob = combatant; + Action = ActionType.Combat; + + /* + * We need to call Think() here or spell casting monsters will not use + * spells when guarding because their target is never processed. + */ + Think(); + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Nothing to guard from"); + } + + m_Mobile.Warmode = false; + if (Core.AOS) + { + m_Mobile.CurrentSpeed = 0.1; + } + + WalkMobileRange(controlMaster, 1, false, 0, 1); + } + + return true; + } + + public virtual bool DoOrderAttack() + { + if (m_Mobile.IsDeadPet) + { + return true; + } + + if (m_Mobile.ControlTarget?.Deleted != false || m_Mobile.ControlTarget.Map != m_Mobile.Map || + !m_Mobile.ControlTarget.Alive || m_Mobile.ControlTarget.IsDeadBondedPet) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay( + "I think he might be dead. He's not anywhere around here at least. That's cool. I'm glad he's dead." + ); + } + + if (Core.AOS) + { + m_Mobile.ControlTarget = m_Mobile.ControlMaster; + m_Mobile.ControlOrder = OrderType.Follow; + } + else + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; + } + + if (m_Mobile.FightMode is FightMode.Closest or FightMode.Aggressor) + { + Mobile newCombatant = null; + var newScore = 0.0; + + foreach (var aggr in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) + { + if (!m_Mobile.CanSee(aggr) || aggr.Combatant != m_Mobile) + { + continue; + } + + if (aggr.IsDeadBondedPet || !aggr.Alive) + { + continue; + } + + var aggrScore = m_Mobile.GetFightModeRanking(aggr, FightMode.Closest, false); + + if ((newCombatant == null || aggrScore > newScore) && m_Mobile.InLOS(aggr)) + { + newCombatant = aggr; + newScore = aggrScore; + } + } + + if (newCombatant != null) + { + m_Mobile.ControlTarget = newCombatant; + m_Mobile.ControlOrder = OrderType.Attack; + m_Mobile.Combatant = newCombatant; + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("But -that- is not dead. Here we go again..."); + } + + Think(); + } + } + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Attacking target..."); + } + + Think(); + } + + return true; + } + + public virtual bool DoOrderPatrol() + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("This order is not yet coded"); + } + + return true; + } + + public virtual bool DoOrderRelease() + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have been released"); + } + + m_Mobile.PlaySound(m_Mobile.GetAngerSound()); + + m_Mobile.SetControlMaster(null); + m_Mobile.SummonMaster = null; + + m_Mobile.BondingBegin = DateTime.MinValue; + m_Mobile.OwnerAbandonTime = DateTime.MinValue; + m_Mobile.IsBonded = false; + + var spawner = m_Mobile.Spawner; + + if (spawner != null && spawner.HomeLocation != Point3D.Zero) + { + m_Mobile.Home = spawner.HomeLocation; + m_Mobile.RangeHome = spawner.HomeRange; + } + + if (m_Mobile.DeleteOnRelease || m_Mobile.IsDeadPet) + { + m_Mobile.Delete(); + } + + m_Mobile.BeginDeleteTimer(); + + if (m_Mobile.CanDrop) + { + m_Mobile.DropBackpack(); + } + + return true; + } + + public virtual bool DoOrderStay() + { + if (m_Mobile.Debug) + { + if (CheckHerding()) + { + m_Mobile.DebugSay("Praise the shepherd!"); + } + else + { + m_Mobile.DebugSay("My master told me to stay"); + } + } + + return true; + } + + public virtual bool DoOrderStop() + { + if (m_Mobile.ControlMaster?.Deleted != false) + { + return true; + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My master told me to stop."); + } + + m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.ControlMaster); + m_Mobile.Home = m_Mobile.Location; + + m_Mobile.ControlTarget = null; + + if (Core.ML) + { + WalkRandomInHome(3, 2, 1); + } + else + { + m_Mobile.ControlOrder = OrderType.None; + } + + return true; + } + + public virtual bool DoOrderTransfer() + { + if (m_Mobile.IsDeadPet) + { + return true; + } + + var from = m_Mobile.ControlMaster; + var to = m_Mobile.ControlTarget; + + if (from?.Deleted == false && to?.Deleted == false && from != to && to.Player) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"Begin transfer with {to.Name}"); + } + + var youngFrom = from is PlayerMobile mobile && mobile.Young; + var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; + + if (youngFrom && !youngTo) + { + from.SendLocalizedMessage(502051); // As a young player, you may not transfer pets to older players. + } + else if (!youngFrom && youngTo) + { + from.SendLocalizedMessage(502052); // As an older player, you may not transfer pets to young players. + } + else if (!m_Mobile.CanBeControlledBy(to)) + { + var args = $"{to.Name}\t{from.Name}\t "; + + from.SendLocalizedMessage( + 1043248, + args + ); // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ + to.SendLocalizedMessage( + 1043249, + args + ); // The pet will not accept you as a master because it does not trust you.~3_BLANK~ + } + else if (!m_Mobile.CanBeControlledBy(from)) + { + var args = $"{to.Name}\t{from.Name}\t "; + + from.SendLocalizedMessage( + 1043250, + args + ); // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ + to.SendLocalizedMessage( + 1043251, + args + ); // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ + } + else if (TransferItem.IsInCombat(m_Mobile)) + { + from.SendMessage("You may not transfer a pet that has recently been in combat."); + to.SendMessage("The pet may not be transferred to you because it has recently been in combat."); + } + else + { + NetState fromState = from.NetState, toState = to.NetState; + + if (fromState != null && toState != null) + { + if (from.HasTrade) + { + from.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending + } + else if (to.HasTrade) + { + to.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending + } + else + { + Container c = fromState.AddTrade(toState); + c.DropItem(new TransferItem(m_Mobile)); + } + } + } + } + + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stay; + + return true; + } + + public virtual bool DoBardPacified() + { + if (Core.Now < m_Mobile.BardEndTime) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am pacified, I wait"); + } + + m_Mobile.Combatant = null; + m_Mobile.Warmode = false; + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I'm not pacified any longer"); + } + + m_Mobile.BardPacified = false; + } + + return true; + } + + public virtual bool DoBardProvoked() + { + if (Core.Now >= m_Mobile.BardEndTime && + (m_Mobile.BardMaster?.Deleted != false || + m_Mobile.BardMaster.Map != m_Mobile.Map || m_Mobile.GetDistanceToSqrt(m_Mobile.BardMaster) > + m_Mobile.RangePerception)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost my provoker"); + } + + m_Mobile.BardProvoked = false; + m_Mobile.BardMaster = null; + m_Mobile.BardTarget = null; + + m_Mobile.Combatant = null; + m_Mobile.Warmode = false; + } + else if (m_Mobile.BardTarget?.Deleted != false || m_Mobile.BardTarget.Map != m_Mobile.Map || + m_Mobile.GetDistanceToSqrt(m_Mobile.BardTarget) > m_Mobile.RangePerception) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost my provoke target"); + } + + m_Mobile.BardProvoked = false; + m_Mobile.BardMaster = null; + m_Mobile.BardTarget = null; + + m_Mobile.Combatant = null; + m_Mobile.Warmode = false; + } + else + { + m_Mobile.Combatant = m_Mobile.BardTarget; + m_Action = ActionType.Combat; + + m_Mobile.OnThink(); + Think(); + } + + return true; + } + + public virtual void WalkRandom(int iChanceToNotMove, int iChanceToDir, int iSteps) + { + if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) + { + return; + } + + if (iChanceToNotMove <= 0) + { + return; + } + + for (var i = 0; i < iSteps; i++) + { + if (Utility.Random(1 + iChanceToNotMove) == 0) + { + // iChanceToDir = 2, total weight is 18 + // 7/26 chance of other direction + var iRndMove = Utility.Random(8 * (iChanceToDir + 1)); + Direction direction = iRndMove < 8 ? (Direction)iRndMove : m_Mobile.Direction; + DoMove(direction); + } + } + } + + public static double TransformMoveDelay(BaseCreature bc, bool isPassive = false) + { + if (bc == null) + { + return 0.1; + } + + if (bc.MoveSpeedMod > 0) + { + return bc.MoveSpeedMod; + } + + var moveSpeed = bc.CurrentSpeed; + var isControlled = bc.Controlled || bc.Summoned; + + /* + * Movement Speed Table based on RunUO + * <0.075 -> 0.0375 + * 0.075 -> 0.05 + * 0.1 -> 0.125 + * 0.2 -> 0.3 + * 0.25 -> 0.45 + * 0.3 -> 0.6 + * 0.4 -> 0.85 + * 0.5 -> 1.05 + * 0.6 -> 1.2, + * 0.8 -> 1.5 + * 1.0 -> 1.8 + * Above 1.0 is linear + */ + + // Linear interpolated + var movementSpeed = moveSpeed switch + { + >= 1.0 => 1.8 * moveSpeed, + >= 0.5 => 1.05 + (moveSpeed - 0.5) * 1.5, + >= 0.4 => 0.85 + (moveSpeed - 0.4) * 2, + >= 0.3 => 0.6 + (moveSpeed - 0.3) * 2.5, + >= 0.2 => 0.3 + (moveSpeed - 0.2) * 3, + >= 0.1 => 0.125 + (moveSpeed - 0.1) * 1.75, + >= 0.075 => 0.05 + (moveSpeed - 0.075) * 3, + _ => 0.0375 // 30 ticks, 37.5ms + }; + + if (isPassive) + { + movementSpeed += 0.2; + } + + if (!isControlled) + { + movementSpeed += 0.1; + } + else if (!bc.Summoned) + { + if (bc.ControlOrder == OrderType.Follow && bc.ControlTarget == bc.ControlMaster) + { + movementSpeed *= 0.5; + } + + movementSpeed -= 0.075; + } + + if (!bc.IsDeadPet && (bc.ReduceSpeedWithDamage || bc.IsSubdued)) + { + int stats, statsMax; + if (Core.HS) + { + stats = bc.Stam; + statsMax = bc.StamMax; + } + else + { + stats = bc.Hits; + statsMax = bc.HitsMax; + } + + var offset = statsMax <= 0 ? 1.0 : Math.Max(0, stats) / (double)statsMax; + + movementSpeed += (1.0 - offset) * 0.8; + } + + return movementSpeed; + } + + public bool CanMoveNow(out long delay) => (delay = NextMove - Core.TickCount) <= FuzzyTimeUntilNextMove; + + public virtual bool CheckMove() => true; + + public virtual bool DoMove(Direction d, bool badStateOk = false) + { + var res = DoMoveImpl(d, badStateOk); + + return res is MoveResult.Success or MoveResult.SuccessAutoTurn || badStateOk && res == MoveResult.BadState; + } + + public virtual MoveResult DoMoveImpl(Direction d, bool badStateOk) + { + if (m_Mobile == null || m_Mobile.Deleted || m_Mobile.Frozen || m_Mobile.Paralyzed || + m_Mobile.Spell?.IsCasting == true || m_Mobile.DisallowAllMoves) + { + return MoveResult.BadState; + } + + if (!CheckMove()) + { + return MoveResult.BadState; + } + + var delay = (int)(TransformMoveDelay(m_Mobile) * 1000); + + if (!CanMoveNow(out var timeUntilMove)) + { + // When bad state is ok, we can still move if the time until move is less than the fuzzy time + if (badStateOk) + { + AIMovementTimerPool.GetTimer(TimeSpan.FromMilliseconds(timeUntilMove), this, d).Start(); + } + + return MoveResult.BadState; + } + + // This makes them always move one step, never any direction changes + // TODO: This is firing off deltas which aren't needed. Look into replacing/removing this + m_Mobile.Direction = d; + NextMove += delay; + + if (Core.TickCount - NextMove > 0) + { + NextMove = Core.TickCount; + } + + m_Mobile.Pushing = false; + var mobDirection = m_Mobile.Direction; + + // Do the actual move + MoveImpl.IgnoreMovableImpassables = m_Mobile.CanMoveOverObstacles && !m_Mobile.CanDestroyObstacles; + var moveResult = m_Mobile.Move(d); + MoveImpl.IgnoreMovableImpassables = false; + + if (moveResult) + { + // If we don't delay combat, then a direction change will happen and cause a glitchy sliding effect. + if (m_Mobile.Warmode && m_Mobile.Combatant != null) + { + var remaining = m_Mobile.NextCombatTime - Core.TickCount; + var maxWait = Math.Min(delay, 400); + if (remaining < maxWait) + { + m_Mobile.NextCombatTime = Core.TickCount + maxWait; + } + } + return MoveResult.Success; + } + + if ((mobDirection & Direction.Mask) != (d & Direction.Mask)) + { + return MoveResult.Blocked; + } + + var wasPushing = m_Mobile.Pushing; + + var blocked = true; + + var canOpenDoors = m_Mobile.CanOpenDoors; + var canDestroyObstacles = m_Mobile.CanDestroyObstacles; + + if (canOpenDoors || canDestroyObstacles) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles."); + } + + var map = m_Mobile.Map; + + if (map != null) + { + var x = m_Mobile.X; + var y = m_Mobile.Y; + Movement.Movement.Offset(d, ref x, ref y); + + using var queue = PooledRefQueue.Create(); + var destroyables = 0; + foreach (var item in map.GetItemsInRange(new Point2D(x, y), 1)) + { + if (canOpenDoors && item is BaseDoor door && door.Z + door.ItemData.Height > m_Mobile.Z && + m_Mobile.Z + 16 > door.Z) + { + if (door.X != x || door.Y != y) + { + continue; + } + + if (!door.Locked || !door.UseLocks()) + { + queue.Enqueue(door); + } + + if (!canDestroyObstacles) + { + break; + } + } + else if (canDestroyObstacles && item.Movable && item.ItemData.Impassable && + item.Z + item.ItemData.Height > m_Mobile.Z && m_Mobile.Z + 16 > item.Z) + { + if (!m_Mobile.InRange(item.GetWorldLocation(), 1)) + { + continue; + } + + queue.Enqueue(item); + ++destroyables; + } + } + + if (destroyables > 0) + { + Effects.PlaySound(new Point3D(x, y, m_Mobile.Z), m_Mobile.Map, 0x3B3); + } + + if (queue.Count > 0) + { + blocked = false; // retry movement + } + + while (queue.Count > 0) + { + var item = queue.Dequeue(); + + if (item is BaseDoor door) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay( + "Little do they expect, I've learned how to open doors. Didn't they read the script??" + ); + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("*twist*"); + } + + door.Use(m_Mobile); + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay( + $"Ugabooga. I'm so big and tough I can destroy it: {item.GetType().Name}" + ); + } + + if (item is Container cont) + { + for (var i = 0; i < cont.Items.Count; ++i) + { + var check = cont.Items[i]; + + if (check.Movable && check.ItemData.Impassable && + cont.Z + check.ItemData.Height > m_Mobile.Z) + { + queue.Enqueue(check); + } + } + + cont.Destroy(); + } + else + { + item.Delete(); + } + } + } + + if (!blocked) + { + blocked = !m_Mobile.Move(d); + } + } + } + + if (blocked) + { + var offset = Utility.RandomDouble() < 0.4 ? 1 : -1; + + for (var i = 0; i < 2; ++i) + { + m_Mobile.TurnInternal(offset); + + if (m_Mobile.Move(m_Mobile.Direction)) + { + return MoveResult.SuccessAutoTurn; + } + } + + return wasPushing ? MoveResult.BadState : MoveResult.Blocked; + } + + return MoveResult.Success; + } + + public virtual void WalkRandomInHome(int iChanceToNotMove, int iChanceToDir, int iSteps) + { + if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) + { + return; + } + + if (m_Mobile.Home == Point3D.Zero) + { + if (m_Mobile.Spawner is RegionSpawner rs) + { + Region region = rs.SpawnRegion; + + if (m_Mobile.Region.AcceptsSpawnsFrom(region)) + { + m_Mobile.WalkRegion = region; + WalkRandom(iChanceToNotMove, iChanceToDir, iSteps); + m_Mobile.WalkRegion = null; + } + else + { + if (region.GoLocation != Point3D.Zero && Utility.RandomBool()) + { + DoMove(m_Mobile.GetDirectionTo(region.GoLocation)); + } + else + { + WalkRandom(iChanceToNotMove, iChanceToDir, 1); + } + } + } + else + { + WalkRandom(iChanceToNotMove, iChanceToDir, iSteps); + } + } + else + { + for (var i = 0; i < iSteps; i++) + { + if (m_Mobile.RangeHome != 0) + { + var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.Home); + + if (iCurrDist < m_Mobile.RangeHome * 2 / 3) + { + WalkRandom(iChanceToNotMove, iChanceToDir, 1); + } + else if (iCurrDist > m_Mobile.RangeHome) + { + DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); + } + else if (Utility.Random(10) > 5) + { + DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); + } + else + { + WalkRandom(iChanceToNotMove, iChanceToDir, 1); + } + } + else + { + if (m_Mobile.Location != m_Mobile.Home) + { + DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); + } + } + } + } + } + + public virtual bool CheckFlee() + { + if (m_Mobile.CheckFlee()) + { + var combatant = m_Mobile.Combatant; + + if (combatant == null) + { + WalkRandom(1, 2, 1); + } + else + { + var d = combatant.GetDirectionTo(m_Mobile); + + d = (Direction)((int)d + Utility.RandomMinMax(-1, +1)); + + m_Mobile.Direction = d; + m_Mobile.Move(d); + } + + return true; + } + + return false; + } + + public virtual void OnTeleported() + { + if (m_Path != null) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Teleported; repathing"); + } + + m_Path.ForceRepath(); + } + } + + public virtual bool MoveTo(Mobile m, bool run, int range) + { + if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves || m?.Deleted != false) + { + return false; + } + + if (m_Mobile.InRange(m, range)) + { + m_Path = null; + return true; + } + + if (m_Path?.Goal == m) + { + if (m_Path.Follow(run, 1)) + { + m_Path = null; + return true; + } + } + else if (!DoMove(m_Mobile.GetDirectionTo(m), true)) + { + m_Path = new PathFollower(m_Mobile, m) { Mover = DoMoveImpl }; + + if (m_Path.Follow(run, 1)) + { + m_Path = null; + return true; + } + } + else + { + m_Path = null; + return true; + } + + return false; + } + + /* + * Walk at range distance from mobile + * + * iSteps : Number of steps + * bRun : Do we run + * iWantDistMin : The minimum distance we want to be + * iWantDistMax : The maximum distance we want to be + * + */ + public virtual bool WalkMobileRange(Mobile m, int iSteps, bool run, int iWantDistMin, int iWantDistMax) + { + if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) + { + return false; + } + + if (m == null) + { + return false; + } + + for (var i = 0; i < iSteps; i++) + { + // Get the current distance + var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m); + + if (iCurrDist < iWantDistMin || iCurrDist > iWantDistMax) + { + var needCloser = iCurrDist > iWantDistMax; + + if (needCloser && m_Path != null && m_Path.Goal == m) + { + if (m_Path.Follow(run, 1)) + { + m_Path = null; + } + } + else + { + var dirTo = iCurrDist > iWantDistMax ? + m_Mobile.GetDirectionTo(m, run) : m.GetDirectionTo(m_Mobile, run); + + if (!DoMove(dirTo, true) && needCloser) + { + m_Path = new PathFollower(m_Mobile, m) { Mover = DoMoveImpl }; + + if (m_Path.Follow(run, 1)) + { + m_Path = null; + } + } + else + { + m_Path = null; + } + } + } + else + { + return true; + } + } + + // Get the current distance + var iNewDist = (int)m_Mobile.GetDistanceToSqrt(m); + + return iNewDist >= iWantDistMin && iNewDist <= iWantDistMax; + } + + /* + * Here we check to acquire a target from our surrounding + * + * iRange : The range + * acqType : A type of acquire we want (closest, strongest, etc) + * bPlayerOnly : Don't bother with other creatures or NPCs, want a player + * bFacFriend : Check people in my faction + * bFacFoe : Check people in other factions + * + */ + public virtual bool AcquireFocusMob(int iRange, FightMode acqType, bool bPlayerOnly, bool bFacFriend, bool bFacFoe) + { + if (m_Mobile.Deleted) + { + return false; + } + + if (m_Mobile.BardProvoked) + { + if (m_Mobile.BardTarget?.Deleted != false) + { + m_Mobile.FocusMob = null; + return false; + } + + m_Mobile.FocusMob = m_Mobile.BardTarget; + return m_Mobile.FocusMob != null; + } + + if (m_Mobile.Controlled) + { + if (m_Mobile.ControlTarget?.Deleted != false || m_Mobile.ControlTarget.Hidden || + !m_Mobile.ControlTarget.Alive || m_Mobile.ControlTarget.IsDeadBondedPet || + !m_Mobile.InRange(m_Mobile.ControlTarget, m_Mobile.RangePerception * 2)) + { + if (m_Mobile.ControlTarget != null && m_Mobile.ControlTarget != m_Mobile.ControlMaster) + { + m_Mobile.ControlTarget = null; + } + + m_Mobile.FocusMob = null; + return false; + } + + m_Mobile.FocusMob = m_Mobile.ControlTarget; + return m_Mobile.FocusMob != null; + } + + if (m_Mobile.ConstantFocus != null) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Acquired my constant focus"); + } + + m_Mobile.FocusMob = m_Mobile.ConstantFocus; + return true; + } + + if (acqType == FightMode.None) + { + m_Mobile.FocusMob = null; + return false; + } + + if (acqType == FightMode.Aggressor && m_Mobile.Aggressors.Count == 0 && m_Mobile.Aggressed.Count == 0 && + m_Mobile.FactionAllegiance == null && m_Mobile.EthicAllegiance == null) + { + m_Mobile.FocusMob = null; + return false; + } + + if (Core.TickCount - m_Mobile.NextReacquireTime < 0) + { + m_Mobile.FocusMob = null; + return false; + } + + m_Mobile.NextReacquireTime = Core.TickCount + (int)m_Mobile.ReacquireDelay.TotalMilliseconds; + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Acquiring..."); + } + + var map = m_Mobile.Map; + + if (map == null) + { + // TODO: Is this correct? Maybe it should return false? + return m_Mobile.FocusMob != null; + } + + Mobile newFocusMob = null; + var val = double.MinValue; + Mobile enemySummonMob = null; + var enemySummonVal = double.MinValue; + + foreach (var m in map.GetMobilesInRange(m_Mobile.Location, iRange)) + { + if (m.Deleted || m.Blessed) + { + continue; + } + + // Let's not target ourselves... + if (m == m_Mobile || m is BaseFamiliar) + { + continue; + } + + // Dead targets are invalid. + if (!m.Alive || m.IsDeadBondedPet) + { + continue; + } + + // Staff members cannot be targeted. + if (m.AccessLevel > AccessLevel.Player) + { + continue; + } + + // Does it have to be a player? + if (bPlayerOnly && !m.Player) + { + continue; + } + + // Can't acquire a target we can't see. + if (!m_Mobile.CanSee(m)) + { + continue; + } + + var bc = m as BaseCreature; + var pm = m as PlayerMobile; + + // Monster don't attack it's own summon or the summon of another monster + if (Core.AOS && bc != null && bc.Summoned && (bc.SummonMaster == m_Mobile || (!bc.SummonMaster.Player && IsHostile(bc.SummonMaster)))) + { + continue; + } + + if (m_Mobile.Summoned && m_Mobile.SummonMaster != null) + { + // If this is a summon, it can't target its controller. + if (m == m_Mobile.SummonMaster) + { + continue; + } + + // It also must abide by harmful spell rules. + if (!SpellHelper.ValidIndirectTarget(m_Mobile.SummonMaster, m)) + { + continue; + } + + // Animated creatures cannot attack players directly. + if (pm != null && m_Mobile.IsAnimatedDead) + { + continue; + } + + // Animated creatures cannot attack other animated creatures + if (m_Mobile.IsAnimatedDead && bc?.IsAnimatedDead == true) + { + continue; + } + + // Animated creatures cannot attack pets of other players + if (m_Mobile.IsAnimatedDead && bc?.Controlled == true) + { + continue; + } + } + + // If we only want faction friends, make sure it's one. + if (bFacFriend && !m_Mobile.IsFriend(m)) + { + continue; + } + + // Ignore anyone under EtherealVoyage + if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell))) + { + continue; + } + + // Ignore players with activated honor + if (m_Mobile.Combatant != m && VirtueSystem.GetVirtues(pm)?.HonorActive == true) + { + continue; + } + + if (acqType is FightMode.Aggressor or FightMode.Evil) + { + var bValid = IsHostile(m); + + if (!bValid) + { + bValid = m_Mobile.GetFactionAllegiance(m) == BaseCreature.Allegiance.Enemy || + m_Mobile.GetEthicAllegiance(m) == BaseCreature.Allegiance.Enemy; + } + + if (acqType == FightMode.Evil && !bValid) + { + if (bc?.Controlled == true && bc?.ControlMaster != null) + { + bValid = bc.ControlMaster.Karma < 0; + } + else + { + bValid = m.Karma < 0; + } + } + + if (!bValid) + { + continue; + } + } + else + { + // Same goes for faction enemies. + if (bFacFoe && !m_Mobile.IsEnemy(m)) + { + continue; + } + + // If it's an enemy factioned mobile, make sure we can be harmful to it. + if (bFacFoe && !bFacFriend && !m_Mobile.CanBeHarmful(m, false)) + { + continue; + } + } + + var theirVal = m_Mobile.GetFightModeRanking(m, acqType, bPlayerOnly); + if (theirVal > val && m_Mobile.InLOS(m)) + { + newFocusMob = m; + val = theirVal; + } + // The summon is targeted when nothing else around. Otherwise this monster enters idle mode. + // Do a check for this edge case so players cannot abuse by casting EVs offscreen to kill an idle monster. + else if (Core.AOS && theirVal > enemySummonVal && m_Mobile.InLOS(m) && bc?.Summoned == true && bc?.Controlled != true) + { + enemySummonMob = m; + enemySummonVal = theirVal; + } + } + + m_Mobile.FocusMob = newFocusMob ?? enemySummonMob; + return m_Mobile.FocusMob != null; + } + + private bool IsHostile(Mobile from) + { + if (m_Mobile.Combatant == from || from.Combatant == m_Mobile) + { + return true; + } + + var count = Math.Max(m_Mobile.Aggressors.Count, m_Mobile.Aggressed.Count); + + for (var a = 0; a < count; ++a) + { + if (a < m_Mobile.Aggressed.Count && m_Mobile.Aggressed[a].Attacker == from) + { + return true; + } + + if (a < m_Mobile.Aggressors.Count && m_Mobile.Aggressors[a].Defender == from) + { + return true; + } + } + + return false; + } + + public virtual void DetectHidden() + { + if (m_Mobile.Deleted || m_Mobile.Map == null) + { + return; + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Checking for hidden players"); + } + + var srcSkill = m_Mobile.Skills.DetectHidden.Value; + + if (srcSkill <= 0) + { + return; + } + + foreach (var trg in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) + { + if (trg != m_Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player && + m_Mobile.InLOS(trg)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"Trying to detect {trg.Name}"); + } + + var trgHiding = trg.Skills.Hiding.Value / 2.9; + var trgStealth = trg.Skills.Stealth.Value / 1.8; + + var chance = srcSkill / 1.2 - Math.Min(trgHiding, trgStealth); + + if (chance < srcSkill / 10) + { + chance = srcSkill / 10; + } + + chance /= 100; + + if (chance > Utility.RandomDouble()) + { + trg.RevealingAction(); + trg.SendLocalizedMessage(500814); // You have been revealed! + } + } + } + } + + public virtual void Deactivate() + { + if (m_Mobile.PlayerRangeSensitive) + { + m_Timer.Stop(); + + var spawner = m_Mobile.Spawner; + + if (spawner?.ReturnOnDeactivate == true && !m_Mobile.Controlled && ( + spawner.HomeLocation == Point3D.Zero && !m_Mobile.Region.AcceptsSpawnsFrom(spawner.Region) || + !m_Mobile.InRange(spawner.HomeLocation, spawner.HomeRange) + )) + { + Timer.StartTimer(ReturnToHome); + } + } + } + + private void ReturnToHome() + { + if (m_Mobile.Spawner != null) + { + var loc = m_Mobile.Spawner.GetSpawnPosition(m_Mobile, m_Mobile.Spawner.Map); + + if (loc != Point3D.Zero) + { + m_Mobile.MoveToWorld(loc, m_Mobile.Spawner.Map); + } + } + } + + public virtual void Activate() + { + if (!m_Timer.Running) + { + // We want to randomize the time at which the AI activates. + // This triggers when a mob is first created since it moves from the internal map to it's added location + // If we spawn lots of mobs, we don't want their AI synchronized exactly. + m_Timer.Delay = TimeSpan.FromMilliseconds(Utility.Random(48) * 8); + m_Timer.Start(); + } + } + + public virtual void OnCurrentSpeedChanged() + { + m_Timer.Interval = TimeSpan.FromSeconds(Math.Max(0.008, m_Mobile.CurrentSpeed)); + } + + private class InternalEntry : ContextMenuEntry + { + private readonly OrderType _order; + + public InternalEntry(int number, int range, OrderType order, bool enabled) : base(number, range) + { + _order = order; + Enabled = enabled; + } + + public override void OnClick(Mobile from, IEntity target) + { + if (!from.CheckAlive() || target is not BaseCreature { Deleted: not true, Controlled: true } bc) + { + return; + } + + // Just in case + if (bc.IsDeadPet && _order is OrderType.Guard or OrderType.Attack or OrderType.Transfer or OrderType.Drop) + { + return; + } + + var isOwner = from == bc.ControlMaster; + var isFriend = !isOwner && bc.IsPetFriend(from); + + if (!isOwner && !isFriend) + { + return; + } + + if (isFriend && _order != OrderType.Follow && _order != OrderType.Stay && _order != OrderType.Stop) + { + return; + } + + switch (_order) + { + case OrderType.Follow: + case OrderType.Attack: + case OrderType.Transfer: + case OrderType.Friend: + case OrderType.Unfriend: + { + if (_order == OrderType.Transfer && from.HasTrade) + { + from.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending + } + else if (_order == OrderType.Friend && from.HasTrade) + { + from.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending + } + else + { + bc.AIObject.BeginPickTarget(from, _order); + } + + break; + } + case OrderType.Release: + { + if (bc.Summoned) + { + goto default; + } + + from.SendGump(new ConfirmReleaseGump(from, bc)); + + break; + } + default: + { + if (bc.CheckControlChance(from)) + { + bc.ControlOrder = _order; + } + + break; + } + } + } + } + + private class TransferItem : Item + { + private readonly BaseCreature m_Creature; + + public TransferItem(BaseCreature creature) + : base(ShrinkTable.Lookup(creature)) + { + m_Creature = creature; + + Movable = false; + + if (!Core.AOS) + { + Name = creature.Name; + } + else if (ItemID == ShrinkTable.DefaultItemID || + creature.GetType().IsDefined(typeof(FriendlyNameAttribute), false) || creature is Reptalon) + { + Name = FriendlyNameAttribute.GetFriendlyNameFor(creature.GetType()).ToString(); + } + + // (As Per OSI)No name. Normally, set by the ItemID of the Shrink Item unless we either explicitly set it with an Attribute, or, no lookup found + + Hue = creature.Hue & 0x0FFF; + } + + public TransferItem(Serial serial) + : base(serial) + { + } + + public static bool IsInCombat(BaseCreature creature) => + creature != null && (creature.Aggressors.Count > 0 || creature.Aggressed.Count > 0); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + Delete(); + } + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + + list.Add(1041603); // This item represents a pet currently in consideration for trade + list.Add(1041601, m_Creature.Name); // Pet Name: ~1_val~ + + if (m_Creature.ControlMaster != null) + { + list.Add(1041602, m_Creature.ControlMaster.Name); // Owner: ~1_val~ + } + } + + public override bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) + { + if (!base.AllowSecureTrade(from, to, newOwner, accepted)) + { + return false; + } + + if (Deleted || m_Creature?.Deleted != false || m_Creature.ControlMaster != from || + !from.CheckAlive() || !to.CheckAlive()) + { + return false; + } + + if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) + { + return false; + } + + var youngFrom = from is PlayerMobile mobile && mobile.Young; + var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; + + if (accepted && youngFrom && !youngTo) + { + from.SendLocalizedMessage(502051); // As a young player, you may not transfer pets to older players. + } + else if (accepted && !youngFrom && youngTo) + { + from.SendLocalizedMessage(502052); // As an older player, you may not transfer pets to young players. + } + else if (accepted && !m_Creature.CanBeControlledBy(to)) + { + var args = $"{to.Name}\t{from.Name}\t "; + + // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ + from.SendLocalizedMessage(1043248, args); + // The pet will not accept you as a master because it does not trust you.~3_BLANK~ + to.SendLocalizedMessage(1043249, args); + + return false; + } + else if (accepted && !m_Creature.CanBeControlledBy(from)) + { + var args = $"{to.Name}\t{from.Name}\t "; + + // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ + from.SendLocalizedMessage(1043250, args); + // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ + to.SendLocalizedMessage(1043251, args); + } + else if (accepted && to.Followers + m_Creature.ControlSlots > to.FollowersMax) + { + to.SendLocalizedMessage(1049607); // You have too many followers to control that creature. + + return false; + } + else if (accepted && IsInCombat(m_Creature)) + { + from.SendMessage("You may not transfer a pet that has recently been in combat."); + to.SendMessage("The pet may not be transferred to you because it has recently been in combat."); + + return false; + } + + return true; + } + + public override void OnSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) + { + if (Deleted) + { + return; + } + + Delete(); + + if (m_Creature?.Deleted != false || m_Creature.ControlMaster != from || !from.CheckAlive() || + !to.CheckAlive()) + { + return; + } + + if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) + { + return; + } + + if (accepted && m_Creature.SetControlMaster(to)) + { + if (m_Creature.Summoned) + { + m_Creature.SummonMaster = to; + } + + m_Creature.ControlTarget = to; + m_Creature.ControlOrder = OrderType.Follow; + + m_Creature.BondingBegin = DateTime.MinValue; + m_Creature.OwnerAbandonTime = DateTime.MinValue; + m_Creature.IsBonded = false; + + m_Creature.PlaySound(m_Creature.GetIdleSound()); + + var args = $"{from.Name}\t{m_Creature.Name}\t{to.Name}"; + + from.SendLocalizedMessage(1043253, args); // You have transferred your pet to ~3_GETTER~. + // ~1_NAME~ has transferred the allegiance of ~2_PET_NAME~ to you. + to.SendLocalizedMessage(1043252, args); + } + } + } + + /* + * The Timer object + */ + private class AITimer : Timer + { + private readonly BaseAI m_Owner; + + public AITimer(BaseAI owner) : base( + TimeSpan.FromMilliseconds(Utility.Random(96) * 8), + TimeSpan.FromSeconds(Math.Max(0.0, owner.m_Mobile.CurrentSpeed)) + ) + { + m_Owner = owner; + m_Owner.m_NextDetectHidden = Core.TickCount; + } + + protected override void OnTick() + { + if (m_Owner.m_Mobile.Deleted) + { + Stop(); + return; + } + + if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) + { + m_Owner.Deactivate(); + return; + } + + if (m_Owner.m_Mobile.PlayerRangeSensitive) // have to check this in the timer.... + { + var sect = m_Owner.m_Mobile.Map.GetSector(m_Owner.m_Mobile.Location); + if (!sect.Active) + { + m_Owner.Deactivate(); + return; + } + } + + m_Owner.m_Mobile.OnThink(); + + if (m_Owner.m_Mobile.Deleted) + { + Stop(); + return; + } + + if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) + { + m_Owner.Deactivate(); + return; + } + + if (m_Owner.m_Mobile.BardPacified) + { + m_Owner.DoBardPacified(); + } + else if (m_Owner.m_Mobile.BardProvoked) + { + m_Owner.DoBardProvoked(); + } + else if (!m_Owner.m_Mobile.Controlled) + { + if (!m_Owner.Think()) + { + Stop(); + return; + } + } + else if (!m_Owner.Obey()) + { + Stop(); + return; + } + + if (m_Owner.CanDetectHidden && Core.TickCount - m_Owner.m_NextDetectHidden >= 0) + { + m_Owner.DetectHidden(); + + // Not exactly OSI style, approximation. + var delay = Math.Min(15000 / m_Owner.m_Mobile.Int, 60); + + var min = delay * 900; // 13s at 1000 int, 33s at 400 int, 54s at <250 int + var max = delay * 1100; // 16s at 1000 int, 41s at 400 int, 66s at <250 int + + m_Owner.m_NextDetectHidden = Core.TickCount + Utility.RandomMinMax(min, max); + } + } + } + + public static class AIMovementTimerPool + { + private const int _poolSize = 1024; + private static readonly Queue _pool = new (_poolSize); + + public static void Configure() + { + var i = 0; + while (i++ < _poolSize) + { + _pool.Enqueue(new AIMovementTimer()); + } + } + + public static AIMovementTimer GetTimer(TimeSpan delay, BaseAI ai, Direction direction) + { + AIMovementTimer timer; + if (_pool.Count > 0) + { + timer = _pool.Dequeue(); + } + else + { + timer = new AIMovementTimer(); + } + + timer.Set(delay, ai, direction); + return timer; + } + + public class AIMovementTimer : Timer + { + public BaseAI AI { get; private set; } + public Direction Direction { get; private set; } + + public AIMovementTimer() : base(TimeSpan.Zero) + { + } + + public void Set(TimeSpan delay, BaseAI ai, Direction direction) + { + if (Running) + { + return; + } + + Delay = delay; + AI = ai; + Direction = direction; + } + + protected override void OnTick() + { + AI?.DoMove(Direction); + AI = null; + _pool.Enqueue(this); + } + } + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs deleted file mode 100644 index 548fe0d54..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs +++ /dev/null @@ -1,224 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: AIGroupMovement.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Server.Collections; - -namespace Server.Mobiles; - -public abstract partial class BaseAI -{ - private static readonly Dictionary _reservedPositions = new(); - private static long _lastGroupUpdateTime; - - private static void CleanupReservedPositions() - { - foreach (var (m, p) in _reservedPositions) - { - if (m?.Deleted != false || m.GetDistanceToSqrt(p) < 1) - { - _reservedPositions.Remove(m); - } - } - } - - /// - /// Crowding refinement for the final approach: engages only near the target when allies - /// contest the ring, so creatures spread instead of stacking. Chasing any real distance - /// always uses the pathfinding approach primitive. - /// - private bool UseGroupMovement(Mobile target, int range) => - Mobile.Combatant == target - && !Mobile.Controlled - && Mobile.InRange(target, range + 2) - && CountCrowdingAllies(target, range) > 0; - - private int CountCrowdingAllies(Mobile target, int range) - { - var crowding = 0; - - foreach (var m in target.GetMobilesInRange(range + 1)) - { - if (m != Mobile && m.Combatant == target && m is BaseCreature { Controlled: false } bc - && bc.Team == Mobile.Team) - { - crowding++; - } - } - - return crowding; - } - - public static bool MoveToWithGroup(BaseAI ai, Mobile target, bool run, int range) - { - if (Core.TickCount - _lastGroupUpdateTime > 1000) - { - CleanupReservedPositions(); - _lastGroupUpdateTime = Core.TickCount; - } - - var mobile = ai.Mobile; - var allies = ai.GetNearbyAllies(target); - var optimalPosition = ai.CalculateOptimalPosition(target, ref allies, range); - try - { - if (optimalPosition == Point3D.Zero) - { - - return ai.MoveToWithCollisionAvoidance(target, run, range); - } - - _reservedPositions[mobile] = optimalPosition; - - var direction = mobile.GetDirectionTo(optimalPosition); - - if (Utility.Random(3) == 0) - { - direction = GetAdjustedDirection(direction); - } - - var res = ai.DoMoveImpl(direction, true); - - if (res is MoveResult.Success or MoveResult.BadState) - { - return true; - } - - // A blocked or wall-slid step is not progress — route around the obstacle. - return ai.ApproachTarget(target, run, range); - } - finally - { - allies.Dispose(); - } - } - - private PooledRefList GetNearbyAllies(Mobile target) - { - var allies = PooledRefList.Create(); - - foreach (var m in Mobile.GetMobilesInRange(8)) - { - if (m != Mobile && m.Combatant == target && m is BaseCreature { Controlled: false } bc - && bc.Team == Mobile.Team) - { - allies.Add(bc); - } - } - - return allies; - } - - private Point3D CalculateOptimalPosition(Mobile target, ref PooledRefList allies, int range) - { - var targetLoc = target.Location; - var bestPosition = Point3D.Zero; - var bestScore = -1.0; - - for (var x = -range; x <= range; x++) - { - for (var y = -range; y <= range; y++) - { - if (Math.Abs(x) + Math.Abs(y) != range) - { - continue; - } - - var testLoc = new Point3D(targetLoc.X + x, targetLoc.Y + y, targetLoc.Z); - var distance = Mobile.GetDistanceToSqrt(testLoc); - - if (distance < range || - distance > range + 3 || !CanMoveTo(testLoc)) - { - continue; - } - - var score = ScorePosition(testLoc, distance, target, ref allies); - - if (score > bestScore) - { - bestScore = score; - bestPosition = testLoc; - - if (score > 20) - { - break; - } - } - } - } - - return bestPosition; - } - - private double ScorePosition(Point3D position, double currentDistance, Mobile target, ref PooledRefList allies) - { - var score = -(currentDistance * 2); - - for (var i = 0; i < allies.Count; i++) - { - var ally = allies[i]; - var allyDistance = ally.GetDistanceToSqrt(position); - - if (allyDistance < 2) - { - score -= 50; - } - else if (allyDistance < 3) - { - score -= 20; - } - } - - foreach (var (m, p) in _reservedPositions) - { - if (m != Mobile && p.GetDistanceToSqrt(position) < 2) - { - score -= 30; - } - } - - if (Mobile.Map != null && Mobile.Map.LineOfSight(position, target.Location)) - { - score += 10; - } - - return score + Utility.RandomDouble() * 5; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool CanMoveTo(Point3D location) => - Mobile.Map?.CanFit(location.X, location.Y, location.Z, 16, false, false) == true; - - private static Direction GetAdjustedDirection(Direction original) - { - var adjustment = Utility.Random(3) - 1; - var newDir = (int)original + adjustment; - - if (newDir < 0) - { - return (Direction)(newDir + 8); - } - - if (newDir >= 8) - { - return (Direction)(newDir - 8); - } - - return (Direction)newDir; - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs deleted file mode 100644 index ccd1c72b6..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs +++ /dev/null @@ -1,689 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: AIMovement.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using Server.Collections; -using Server.Items; -using MoveImpl = Server.Movement.MovementImpl; - -namespace Server.Mobiles; - -public abstract partial class BaseAI -{ - // --- Centralized progress-based approach state (see ApproachTarget) --------------- - // Consecutive move-eligible ticks a creature may fail to improve its best distance to a - // STATIONARY goal before it gives up and idles. A moving goal (an active chase) never - // triggers give-up. Must exceed the longest no-improvement stretch of a valid detour - // (the Britain Inn detour's is ~13 ticks), with margin; this also bounds the largest - // concave detour a creature will navigate before idling on a stationary goal. - private const int ApproachGiveUpTicks = 40; - - private Mobile _approachGoal; - private Point3D _approachGoalLoc; - private double _approachBestDist; - private int _approachStallTicks; - private bool _approachGaveUp; - private Point3D _approachGaveUpGoalLoc; - - // --- Move intent (see ContinueMove) ------------------------------------------------ - // Durable movement goal renewed by en-route ApproachTarget/MoveToPoint calls; while - // live, the AITimer wakes at NextMove between think ticks to advance the step. - private Mobile _moveIntentTarget; - private IPoint3D _moveIntentPoint; - private bool _moveIntentRun; - private int _moveIntentRange; - private long _moveIntentExpire; - - // Inflates a step delay while badly hurt; computed from the passed base so it cannot - // compound across steps. Damage slows steps, never decisions. - public static double BadlyHurtMoveDelay(BaseCreature bc, double delay) - { - var statMin = Core.HS ? bc.Stam : bc.Hits; - var statMax = Core.HS ? bc.StamMax : bc.HitsMax; - - if (!bc.IsDeadPet && (bc.ReduceSpeedWithDamage || bc.IsSubdued) - && statMax > 0 && statMin < statMax * 0.3) - { - var stat = (double)statMin / statMax; - - if (stat < 0.1) { return delay + 0.15; } - if (stat < 0.2) { return delay + 0.1; } - - return delay + 0.05; - } - - return delay; - } - - public bool CanMoveNow(out double delay) - { - delay = 0.0; - return Core.TickCount - NextMove >= 0; - } - - // Accumulative full-step budget: long-run pacing averages CurrentMoveSpeed exactly - // regardless of timer-grid jitter; snap-to-now caps stall catch-up at one step. - private void ConsumeMoveBudget() - { - var stepDelay = Mobile.CurrentMoveSpeed; - - if (!(Core.AOS && IsFollowingMaster())) - { - stepDelay = BadlyHurtMoveDelay(Mobile, stepDelay); - } - - NextMove += Math.Max(50, (long)(stepDelay * 1000)); - - if (Core.TickCount - NextMove > 0) - { - NextMove = Core.TickCount; - } - } - - public virtual bool CheckMove() => !(Mobile.Deleted || Mobile.DisallowAllMoves); - - public virtual bool DoMove(Direction d, bool badStateOk = false) => IsMoveSuccessful(DoMoveImpl(d, badStateOk), badStateOk); - - private static bool IsMoveSuccessful(MoveResult res, bool badStateOk) => - res is MoveResult.Success or MoveResult.SuccessAutoTurn - || badStateOk && res == MoveResult.BadState; - - public virtual MoveResult DoMoveImpl(Direction d, bool badStateOk) - { - if (IsInBadState() || !CanMoveNow(out _)) - { - return MoveResult.BadState; - } - - if ((Mobile.Direction & Direction.Mask) != (d & Direction.Mask)) - { - Mobile.Direction = d; - } - - Mobile.Pushing = false; - var mobDirection = Mobile.Direction; - - if (TryMove(d)) - { - // Writes the think clock only; hurt slowdown applies in ConsumeMoveBudget. - if (Core.AOS && IsFollowingMaster()) - { - Mobile.CurrentSpeed = 0.1; - } - else if (Mobile.Warmode || Mobile.Combatant != null) - { - Mobile.SetCurrentSpeedToActive(); - } - else - { - Mobile.SetCurrentSpeedToPassive(); - } - - ConsumeMoveBudget(); - - return MoveResult.Success; - } - - if ((mobDirection & Direction.Mask) != (d & Direction.Mask)) - { - Mobile.Direction = d; - return MoveResult.SuccessAutoTurn; - } - - return HandleBlockedMovement(d); - } - - private bool TryMove(Direction d) - { - MoveImpl.IgnoreMovableImpassables = Mobile.CanMoveOverObstacles && !Mobile.CanDestroyObstacles; - - var result = Mobile.Move(d); - - MoveImpl.IgnoreMovableImpassables = false; - return result; - } - - private bool IsInBadState() => - Mobile == null || Mobile.Deleted || Mobile.Frozen || Mobile.Paralyzed || - Mobile.Spell?.IsCasting == true || Mobile.DisallowAllMoves; - - private MoveResult HandleBlockedMovement(Direction d) - { - var wasPushing = Mobile.Pushing; - - if ((Mobile.CanOpenDoors || Mobile.CanDestroyObstacles) && !TryClearObstacles(d)) - { - return MoveResult.Success; - } - - return TryAlternateMovement(wasPushing); - } - - private MoveResult TryAlternateMovement(bool wasPushing) - { - var offset = Utility.Random(2) == 0 ? 1 : -1; - - for (var i = 0; i < 2; ++i) - { - Mobile.TurnInternal(offset); - - if (Mobile.Move(Mobile.Direction)) - { - ConsumeMoveBudget(); - return MoveResult.SuccessAutoTurn; - } - } - - return wasPushing ? MoveResult.BadState : MoveResult.Blocked; - } - - private bool TryClearObstacles(Direction d) - { - DebugSay("My movement is blocked. Trying to push through."); - - var map = Mobile.Map; - - if (map == null) { return true; } - - var (x, y) = GetOffsetLocation(d); - - var queue = GatherObstacles(x, y, out var destroyables); - - if (destroyables > 0) - { - Effects.PlaySound(new Point3D(x, y, Mobile.Z), Mobile.Map, 0x3B3); - } - - try - { - return ProcessObstacles(ref queue, d); - } - finally - { - queue.Dispose(); - } - } - - private (int x, int y) GetOffsetLocation(Direction d) - { - var x = Mobile.X; - var y = Mobile.Y; - Movement.Movement.Offset(d, ref x, ref y); - return (x, y); - } - - private PooledRefQueue GatherObstacles(int x, int y, out int destroyables) - { - var queue = PooledRefQueue.Create(); - destroyables = 0; - - foreach (var item in Mobile.Map.GetItemsInRange(new Point2D(x, y), 1)) - { - if (IsValidDoor(item, x, y) || IsValidDestroyableItem(item)) - { - queue.Enqueue(item); - if (item is not BaseDoor) - { - destroyables++; - } - } - } - - return queue; - } - - private bool IsValidDoor(Item item, int x, int y) - { - if (!Mobile.CanOpenDoors || item is not BaseDoor door) - { - return false; - } - - if (door.Z + door.ItemData.Height <= Mobile.Z || Mobile.Z + 16 <= door.Z) - { - return false; - } - - if (door.X != x || door.Y != y) - { - return false; - } - - return !door.Locked || !door.UseLocks(); - } - - private bool IsValidDestroyableItem(Item item) - { - if (!Mobile.CanDestroyObstacles || !item.Movable || !item.ItemData.Impassable) - { - return false; - } - - if (item.Z + item.ItemData.Height <= Mobile.Z || Mobile.Z + 16 <= item.Z) - { - return false; - } - - return Mobile.InRange(item.GetWorldLocation(), 1); - } - - private bool ProcessObstacles(ref PooledRefQueue queue, Direction d) - { - if (queue.Count == 0) { return true; } - - while (queue.Count > 0) - { - ProcessObstacle(queue.Dequeue(), ref queue); - } - - return !Mobile.Move(d); - } - - private void ProcessObstacle(Item item, ref PooledRefQueue queue) - { - if (item is BaseDoor door) - { - DebugSay("Opening the door."); - door.Use(Mobile); - } - else - { - this.DebugSayFormatted($"Destroying item: {item.GetType().Name}"); - - if (item is Container cont) - { - ProcessContainer(cont, ref queue); - cont.Destroy(); - } - else - { - item.Delete(); - } - } - } - - private void ProcessContainer(Container cont, ref PooledRefQueue queue) - { - foreach (var check in cont.Items) - { - if (check.Movable && check.ItemData.Impassable && cont.Z + check.ItemData.Height > Mobile.Z) - { - queue.Enqueue(check); - } - } - } - - /// - /// Centralized "move toward until within - /// " decision shared by MoveTo and WalkMobileRange. A greedy - /// step is taken only when it actually gets the creature closer; a blocked step or an - /// auto-turn sidestep that made no progress falls through to a persistent PathFollower - /// that routes around the obstacle and is never discarded by a greedy step. A - /// best-distance stall counter idles the creature if an in-range goal is genuinely - /// unreachable, without ever abandoning a real chase or detour. - /// - protected bool ApproachTarget(Mobile target, bool run, int range) - { - if (Mobile.Deleted || Mobile.DisallowAllMoves || target?.Deleted != false) - { - ClearMoveIntent(); - return false; - } - - if (Mobile.InRange(target, range)) - { - ResetApproach(); - ClearMoveIntent(); - return true; - } - - // Already gave up on this exact (unreachable) goal: idle until it moves. - if (_approachGaveUp && _approachGoal == target) - { - if (target.Location == _approachGaveUpGoalLoc) - { - ClearMoveIntent(); - return false; - } - - ResetApproach(); // target moved — try again fresh - } - - RenewMoveIntent(target, null, run, range); - - // FAST PATH: greedy step toward the target, counted as success ONLY when the move - // fully succeeded (not an auto-turn sidestep) and actually got us closer. An - // auto-turn sidestep can reduce Euclidean distance while moving in the wrong - // direction (e.g., east when the true route requires going south-first around a - // concave obstacle); treating it as progress would discard a PathFollower that is - // the only way to navigate. A blocked step or a non-Success result falls through to - // the planner immediately. - if (Path == null && Mobile.InLOS(target)) - { - var distBefore = Mobile.GetDistanceToSqrt(target); - var res = DoMoveImpl(Mobile.GetDirectionTo(target, run), true); - - if (res == MoveResult.BadState) - { - return true; // not allowed to move this tick (frozen/casting/throttled); not a failure - } - - if (res == MoveResult.Success && Mobile.GetDistanceToSqrt(target) < distBefore) - { - - ResetApproach(); - return true; // healthy en-route progress - } - - // else: fall through; let the PathFollower route around the obstacle. - } - - // PLANNING PATH: a persistent PathFollower, never discarded by a greedy step. - if (Path == null || Path.Goal != target) - { - - Path = new PathFollower(Mobile, target) { Mover = DoMoveImpl }; - } - - // Sample move-eligibility BEFORE the attempt: a successful step consumes the move - // budget, which would mask stall accounting and the progress signal. - var couldMove = CanMoveNow(out _) && !IsInBadState(); - var locBefore = Mobile.Location; - - if (Path.Follow(run, range)) - { - ResetApproach(); - return true; - } - - TrackApproachProgress(target, couldMove); - - // En-route progress is success; failure only when a move-eligible tick took no step - // (no working path), or the approach has given up. - var progressed = !_approachGaveUp && (Mobile.Location != locBefore || !couldMove); - - return progressed; - } - - /// - /// Walks toward a fixed point (e.g. a target's last-known position), pathfinding around - /// obstacles. Returns false on arrival or when genuinely unable to make progress. - /// - public bool MoveToPoint(IPoint3D goal, bool run) - { - if (Mobile.Deleted || Mobile.DisallowAllMoves || goal == null) - { - ClearMoveIntent(); - return false; - } - - if (Path?.Goal != goal) - { - Path = new PathFollower(Mobile, goal) { Mover = DoMoveImpl }; - } - - RenewMoveIntent(null, goal, run, 1); - - var couldMove = CanMoveNow(out _) && !IsInBadState(); - var locBefore = Mobile.Location; - - if (Path.Follow(run, 1)) - { - Path = null; - ClearMoveIntent(); - return false; // arrived - } - - var progressed = Mobile.Location != locBefore || !couldMove; - - if (!progressed) - { - ClearMoveIntent(); - } - - return progressed; - } - - /// - /// Best-distance stuck detection. A creature making real headway keeps lowering its - /// closest-ever distance to the goal (a detour's outbound leg pauses that, but it - /// resumes once the creature rounds the obstacle). A creature that cannot reach a - /// STATIONARY goal never lowers it and, after ticks, - /// gives up and idles. A MOVING goal (an active chase) resets the baseline every tick, - /// so chases never give up even when the gap holds constant. - /// - private void TrackApproachProgress(Mobile target, bool couldMove) - { - if (!couldMove) - { - return; // a tick that was never allowed to move (stun, stall) is not a stall - } - - var dist = Mobile.GetDistanceToSqrt(target); - var goalLoc = target.Location; - - // New goal, or the goal moved (active chase): reset the stall baseline. Clearing the - // give-up flag here prevents a prior goal's give-up state from leaking onto a new one. - if (_approachGoal != target || goalLoc != _approachGoalLoc) - { - _approachGoal = target; - _approachGoalLoc = goalLoc; - _approachBestDist = dist; - _approachStallTicks = 0; - _approachGaveUp = false; - return; - } - - // Stationary goal: getting closer than ever resets the stall. - if (dist < _approachBestDist) - { - _approachBestDist = dist; - _approachStallTicks = 0; - return; - } - - if (++_approachStallTicks >= ApproachGiveUpTicks) - { - _approachGaveUp = true; - _approachGaveUpGoalLoc = goalLoc; - Path = null; - ClearMoveIntent(); - } - } - - /// Clears all approach state (called on arrival, real greedy progress, or when - /// a given-up goal moves). - private void ResetApproach() - { - Path = null; - _approachGoal = null; - _approachGoalLoc = Point3D.Zero; - _approachBestDist = 0; - _approachStallTicks = 0; - _approachGaveUp = false; - } - - private void RenewMoveIntent(Mobile target, IPoint3D point, bool run, int range) - { - _moveIntentTarget = target; - _moveIntentPoint = point; - _moveIntentRun = run; - _moveIntentRange = range; - - // A live pursuit renews every think tick; unrenewed intent dies on its own. - _moveIntentExpire = Core.TickCount + (long)(Mobile.CurrentSpeed * 2000) + 250; - } - - public void ClearMoveIntent() - { - _moveIntentTarget = null; - _moveIntentPoint = null; - } - - /// - /// True while a durable movement goal is live; is the tick - /// the movement budget elapses. - /// - public bool TryGetMoveWake(out long nextMove) - { - nextMove = NextMove; - - return (_moveIntentTarget != null || _moveIntentPoint != null) && - Core.TickCount - _moveIntentExpire < 0; - } - - /// - /// Advances the current pursuit/investigation by one step on a movement-clock wake; - /// no decisions run. - /// - public void ContinueMove() - { - if (!TryGetMoveWake(out var nextMove) || Core.TickCount - nextMove < 0) - { - return; - } - - if (_moveIntentTarget != null) - { - ApproachTarget(_moveIntentTarget, _moveIntentRun, _moveIntentRange); - } - else - { - MoveToPoint(_moveIntentPoint, _moveIntentRun); - } - } - - public virtual bool MoveTo(Mobile m, bool run, int range) - { - if (Mobile.Deleted || Mobile.DisallowAllMoves || m?.Deleted != false) - { - return false; - } - - var distance = (int)Mobile.GetDistanceToSqrt(m); - var distanceThreshold = Core.AOS && IsFollowingMaster() ? 1 : 5; - - var shouldRun = run && distance > distanceThreshold; - - if (Mobile.InRange(m, range)) - { - ResetApproach(); - return true; - } - - if (UseGroupMovement(m, range)) - { - return MoveToWithGroup(this, m, shouldRun, range); - } - - return ApproachTarget(m, shouldRun, range); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsFollowingMaster() => - Mobile.Controlled && - Mobile.ControlOrder == OrderType.Follow && - Mobile.ControlTarget == Mobile.ControlMaster && - Mobile.Combatant == null; - - private bool MoveToWithCollisionAvoidance(Mobile target, bool run, int range) - { - var distance = (int)Mobile.GetDistanceToSqrt(target); - - var shouldRun = run && distance > 5; - - var direction = Mobile.GetDirectionTo(target); - - // Wall-slide auto-turns must not count as progress, or a creature pinned on - // geometry reports success forever. - var res = DoMoveImpl(direction, true); - - if (res is MoveResult.Success or MoveResult.BadState) - { - return true; - } - - for (var i = 1; i <= 3; i++) - { - var clockwise = (Direction)(((int)direction + i) % 8); - - if (DoMoveImpl(clockwise, true) == MoveResult.Success) - { - return true; - } - - var counterclockwise = (Direction)(((int)direction - i + 8) % 8); - - if (DoMoveImpl(counterclockwise, true) == MoveResult.Success) - { - return true; - } - } - - // Tactical sidesteps exhausted — route around the obstacle via the centralized - // approach primitive (persistent PathFollower, no oscillation). - return ApproachTarget(target, shouldRun, range); - } - - public virtual bool WalkMobileRange(Mobile m, int iSteps, bool run, int iWantDistMin, int iWantDistMax) - { - if (Mobile.Deleted || Mobile.DisallowAllMoves || m == null) - { - return false; - } - - for (var i = 0; i < iSteps; i++) - { - var iCurrDist = (int)Mobile.GetDistanceToSqrt(m); - - var shouldRun = run && iCurrDist > 5; - - if (iCurrDist >= iWantDistMin && iCurrDist <= iWantDistMax) - { - return true; - } - - if (!MoveTowardsOrAwayFrom(m, shouldRun, iCurrDist, iWantDistMax)) - { - return false; - } - } - - var dist = Mobile.GetDistanceToSqrt(m); - - return dist >= iWantDistMin && dist <= iWantDistMax; - } - - private bool MoveTowardsOrAwayFrom(Mobile m, bool run, int iCurrDist, int iWantDistMax) - { - var shouldRun = run && iCurrDist > 5; - - if (iCurrDist > iWantDistMax) - { - // Too far: approach via the centralized progress-based primitive. - return ApproachTarget(m, shouldRun, iWantDistMax); - } - - // Too close: back away. Retreat keeps the simple greedy behavior (out of scope). - if (DoMove(m.GetDirectionTo(Mobile, shouldRun), true)) - { - Path = null; - return true; - } - - return false; - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs deleted file mode 100644 index fe24e5f6d..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs +++ /dev/null @@ -1,168 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: AITimer.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using System; - -namespace Server.Mobiles; - -/// -/// Drives an AI on two clocks: decisions at , plus -/// move-only wakes at while a pursuit is live. Each tick -/// schedules the earlier of the two deadlines. -/// -public sealed class AITimer : Timer -{ - private readonly BaseAI _owner; - private long _nextThink; - private int _detectHiddenMinDelay; - private int _detectHiddenMaxDelay; - - public AITimer(BaseAI owner) : base(TimeSpan.FromMilliseconds(Utility.Random(3000)), - TimeSpan.FromSeconds(owner.Mobile.CurrentSpeed)) - { - _owner = owner; - _owner._nextDetectHidden = Core.TickCount; - _nextThink = Core.TickCount; - } - - public void Activate() - { - _nextThink = Core.TickCount; - Interval = TimeSpan.FromSeconds(_owner.Mobile.CurrentSpeed); - Start(); - } - - // A speed-up must not wait out a stale, longer think deadline. - public void OnSpeedChanged() - { - var candidate = Core.TickCount + (long)(_owner.Mobile.CurrentSpeed * 1000); - - if (candidate - _nextThink < 0) - { - _nextThink = candidate; - } - - Interval = TimeSpan.FromSeconds(_owner.Mobile.CurrentSpeed); - } - - protected override void OnTick() - { - if (ShouldStop()) - { - Stop(); - return; - } - - if (Core.TickCount - _nextThink >= 0) - { - _owner.Mobile.OnThink(); - - if (ShouldStop()) - { - Stop(); - return; - } - - HandleBardEffects(); - - if (_owner.Mobile.Controlled ? _owner.Obey() : _owner.Think()) - { - HandleDetectHidden(); - } - - // Cadence from the post-decision speed (decisions may flip active/passive). - _nextThink = Core.TickCount + (long)(_owner.Mobile.CurrentSpeed * 1000); - } - else - { - _owner.ContinueMove(); - } - - ScheduleNext(); - } - - private void ScheduleNext() - { - var now = Core.TickCount; - var delay = _nextThink - now; - - if (_owner.TryGetMoveWake(out var nextMove)) - { - var moveDelay = nextMove - now; - - // Only a future budget is a wake — a blocked creature must not spin the timer. - if (moveDelay > 0 && moveDelay < delay) - { - delay = moveDelay; - } - } - - // The wheel rounds up to its 8ms resolution; a non-positive delay becomes one turn. - Interval = TimeSpan.FromMilliseconds(delay); - } - - private bool ShouldStop() - { - if (_owner.Mobile.Deleted) - { - return true; - } - - if (_owner.Mobile.Map == null || _owner.Mobile.Map == Map.Internal || _owner.Mobile.PlayerRangeSensitive && - !_owner.Mobile.Controlled && !_owner.Mobile.Map.GetSector(_owner.Mobile.Location).Active) - { - _owner.Deactivate(); - return true; - } - - return false; - } - - private void HandleBardEffects() - { - if (_owner.Mobile.BardPacified) - { - _owner.DoBardPacified(); - } - else if (_owner.Mobile.BardProvoked) - { - _owner.DoBardProvoked(); - } - } - - private void CacheDetectHiddenDelays() - { - var delay = Math.Min(30000 / _owner.Mobile.Int, 120); - _detectHiddenMinDelay = delay * 900; // 26s to 108s - _detectHiddenMaxDelay = delay * 1100; // 32s to 132s - } - - private void HandleDetectHidden() - { - if (!_owner.CanDetectHidden || Core.TickCount - _owner._nextDetectHidden < 0) - { - return; - } - - _owner.DetectHidden(); - - if (_detectHiddenMinDelay == 0 || _detectHiddenMaxDelay == 0) - { - CacheDetectHiddenDelays(); - } - - _owner._nextDetectHidden = Core.TickCount + Utility.RandomMinMax(_detectHiddenMinDelay, _detectHiddenMaxDelay); - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AIType.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AIType.cs deleted file mode 100644 index 9f6e2088f..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/AIType.cs +++ /dev/null @@ -1,30 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: AIType.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -namespace Server.Mobiles; - -public enum AIType -{ - AI_Use_Default, - AI_Melee, - AI_Animal, - AI_Archer, - AI_Healer, - AI_Vendor, - AI_Mage, - AI_Berserk, - AI_Predator, - AI_Thief -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/ActionType.cs b/Projects/UOContent/Mobiles/AI/BaseAI/ActionType.cs deleted file mode 100644 index 9db19cb35..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/ActionType.cs +++ /dev/null @@ -1,26 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ActionType.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -namespace Server.Mobiles; - -public enum ActionType -{ - Wander, - Combat, - Guard, - Flee, - Backoff, - Interact -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs deleted file mode 100644 index cf64cae17..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs +++ /dev/null @@ -1,1151 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BaseAI.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using System; -using Server.Engines.Quests.Necro; -using Server.Engines.Spawners; -using Server.Engines.Virtues; -using Server.Factions; -using Server.Spells; -using Server.Spells.Spellweaving; -using Server.Targets; - -namespace Server.Mobiles; - -public abstract partial class BaseAI -{ - // Last-known-position tracking: recorded while the combatant is in LOS; drives the - // guard-time investigation and the instant re-engage. - private const int GuardGraceDuration = 10_000; - private const int LkpFreshDuration = 30_000; - private const int InvestigateDuration = 15_000; - - private ActionType _action; - public long _nextDetectHidden; - public DateTime _lastOrder = DateTime.MinValue; - public Mobile _commandIssuer; - - private Mobile _lkpTarget; - private Point3D _lkpLocation; - private IPoint3D _lkpGoal; // boxed _lkpLocation handed to the PathFollower - private IPoint3D _herdGoal; // boxed herding goal handed to the PathFollower - private long _lkpExpireTick; - private long _guardStopTick; - private long _investigateStopTick; - private bool _investigating; - - public PathFollower Path { get; protected set; } - public AITimer AITimer { get; } - public long NextMove { get; set; } - - public BaseCreature Mobile { get; } - - public long NextDebugMessage { get; set; } - - public virtual bool CanDetectHidden => Mobile.Skills.DetectHidden.Value > 0; - - public BaseAI(BaseCreature m) - { - Mobile = m; - NextMove = Core.TickCount; - AITimer = new AITimer(this); - - if (!m.PlayerRangeSensitive || !World.Loading && m.Map != null && m.Map != Map.Internal && m.Map.GetSector(m.Location).Active) - { - AITimer.Start(); - } - - if (Action != ActionType.Wander) - { - Action = ActionType.Wander; - } - } - - public ActionType Action - { - get => _action; - set - { - if (_action != value) - { - _action = value; - OnActionChanged(); - } - } - } - - public virtual bool WasNamed(string speech) => !string.IsNullOrEmpty(Mobile.Name) && speech.InsensitiveStartsWith(Mobile.Name); - - public virtual void BeginPickTarget(Mobile from, OrderType order) - { - if (!IsValidTarget(from, order)) - { - return; - } - - if (from.Target == null) - { - SendOrderMessage(from, order); - from.Target = new AIControlMobileTarget(this, order); - } - else if (from.Target is AIControlMobileTarget t && t.Order == order) - { - t.AddAI(this); - } - } - - private static void SendOrderMessage(Mobile from, OrderType order) - { - switch (order) - { - case OrderType.Transfer: - { - from.SendLocalizedMessage(502038); - // Click on the person to transfer ownership to. - break; - } - case OrderType.Friend: - { - from.SendLocalizedMessage(502020); - // Click on the player whom you wish to make a co-owner. - break; - } - case OrderType.Unfriend: - { - from.SendLocalizedMessage(1070948); - // Click on the player whom you wish to remove as a co-owner. - break; - } - } - } - - public virtual void OnAggressiveAction(Mobile aggressor) - { - if (aggressor.Hidden) - { - return; - } - - var currentCombat = Mobile.Combatant; - - if (currentCombat == null || currentCombat == aggressor) - { - return; - } - - if (Mobile.GetDistanceToSqrt(aggressor) < Mobile.GetDistanceToSqrt(currentCombat)) - { - Mobile.Combatant = aggressor; - } - } - - public virtual void EndPickTarget(Mobile from, Mobile target, OrderType order) - { - if (!IsValidTarget(from, order) || order == OrderType.Attack && !CanAttackTarget(from, target)) - { - return; - } - - if (Mobile.CheckControlChance(from)) - { - Mobile.ControlTarget = target; - Mobile.ControlOrder = order; - - if (order == OrderType.Attack) - { - Mobile.FocusMob = target; - Mobile.Combatant = target; - Action = ActionType.Combat; - } - } - } - - private bool IsValidTarget(Mobile from, OrderType order) - { - if (Mobile.Deleted || !Mobile.Controlled || !from.InRange(Mobile, 14) - || from.Map != Mobile.Map || !from.CheckAlive()) - { - return false; - } - - var isOwner = from == Mobile.ControlMaster; - var isFriend = !isOwner && Mobile.IsPetFriend(from); - - if (!isOwner && !isFriend) - { - return false; - } - - if (isFriend && order is not (OrderType.Follow or OrderType.Stay or OrderType.Stop)) - { - return false; - } - - return true; - } - - private bool CanAttackTarget(Mobile from, Mobile target) - { - if (target.Hidden) - { - return false; - } - - if (target is BaseCreature creature && creature.IsScaryToPets && Mobile.IsScaredOfScaryThings) - { - Mobile.SayTo(from, "Your pet refuses to attack this creature!"); - return false; - } - - if (SolenHelper.CheckRedFriendship(from) && - target is RedSolenInfiltratorQueen or RedSolenInfiltratorWarrior or RedSolenQueen or RedSolenWarrior or RedSolenWorker || - SolenHelper.CheckBlackFriendship(from) && - target is BlackSolenInfiltratorQueen or BlackSolenInfiltratorWarrior or BlackSolenQueen or BlackSolenWarrior or BlackSolenWorker) - { - from.SendLocalizedMessage(1063106); - // You can not force your pet to attack a creature you are protected from. - return false; - } - - if (target is BaseFactionGuard) - { - Mobile.SayTo(from, "Your pet refuses to attack the guard."); - return false; - } - - return true; - } - - public void DebugSay(string message, int cooldownMs = 5000) - { - if (Mobile.Debug && NextDebugMessage - Core.TickCount <= 0) - { - Mobile.PublicOverheadMessage(MessageType.Regular, 41, false, message); - NextDebugMessage = Core.TickCount + cooldownMs; - } - } - - public virtual bool Think() - { - if (Mobile.Deleted || Mobile.Map == null) - { - return false; - } - - if (CheckFlee()) - { - return true; - } - - if (_action == ActionType.Combat) - { - UpdateLastKnownLocation(); - } - else if (_action is ActionType.Wander or ActionType.Guard) - { - TryReengageLastKnown(); - } - - switch (Action) - { - case ActionType.Wander: - { - Mobile.OnActionWander(); - return DoActionWander(); - } - case ActionType.Combat: - { - Mobile.OnActionCombat(); - return DoActionCombat(); - } - case ActionType.Guard: - { - Mobile.OnActionGuard(); - return DoActionGuard(); - } - case ActionType.Flee: - { - Mobile.OnActionFlee(); - return DoActionFlee(); - } - case ActionType.Interact: - { - Mobile.OnActionInteract(); - return DoActionInteract(); - } - case ActionType.Backoff: - { - Mobile.OnActionBackoff(); - return DoActionBackoff(); - } - } - - return false; - } - - public virtual void OnActionChanged() - { - // A change of course invalidates between-think movement continuation. - ClearMoveIntent(); - - switch (Action) - { - case ActionType.Wander: - { - HandleWanderAction(); - break; - } - case ActionType.Combat: - { - HandleCombatAction(); - break; - } - case ActionType.Guard: - { - HandleGuardAction(); - break; - } - case ActionType.Flee: - { - HandleFleeAction(); - break; - } - case ActionType.Interact: - { - HandleInteractAction(); - break; - } - case ActionType.Backoff: - { - HandleBackoffAction(); - break; - } - } - } - - private void HandleWanderAction() - { - Mobile.FocusMob = null; - Mobile.Warmode = false; - Mobile.Combatant = null; - } - - private void HandleCombatAction() - { - Mobile.Warmode = true; - } - - private void HandleGuardAction() - { - Mobile.Warmode = true; - Mobile.Combatant = null; - - // Investigate a fresh last-seen position that is not already in view; the guard - // grace period begins once the investigation ends. - _investigating = _lkpTarget != null && Core.TickCount - _lkpExpireTick < 0 && - !(Mobile.InRange(_lkpLocation, 1) || - Mobile.InLOS(_lkpLocation) && Mobile.InRange(_lkpLocation, Mobile.RangePerception)); - _investigateStopTick = Core.TickCount + InvestigateDuration; - _guardStopTick = Core.TickCount + GuardGraceDuration; - _lkpGoal = null; - } - - private void HandleFleeAction() - { - Mobile.FocusMob = null; - Mobile.Warmode = true; - } - - private void HandleInteractAction() - { - Mobile.Warmode = false; - } - - private void HandleBackoffAction() - { - Mobile.Warmode = false; - } - - public virtual bool DoActionWander() - { - if (CheckHerding()) - { - this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); - } - else if (ShouldBackoff()) - { - DebugSay("Detected threat, backing off!"); - - Action = ActionType.Backoff; - return true; - } - else if (Mobile.CurrentWayPoint != null) - { - HandleWayPoint(); - } - else if (Mobile.IsAnimatedDead) - { - FollowMaster(); - - if (CheckMove() && CanMoveNow(out _) && !Mobile.CheckIdle()) - { - WalkRandomInHome(3, 2, 1); - } - } - else if (CheckMove() && CanMoveNow(out _) && !Mobile.CheckIdle()) - { - WalkRandomInHome(3, 2, 1); - } - - return true; - } - - public virtual bool OnAtWayPoint() => true; - - private void HandleWayPoint() - { - var point = Mobile.CurrentWayPoint; - - if ((point.X != Mobile.Location.X || point.Y != Mobile.Location.Y) - && point.Map == Mobile.Map && point.Parent == null && !point.Deleted) - { - this.DebugSayFormatted($"Moving towards waypoint {point.X}, {point.Y}."); - - DoMove(Mobile.GetDirectionTo(point)); - } - else if (OnAtWayPoint()) - { - this.DebugSayFormatted($"I have reached waypoint {point.X}, {point.Y}."); - - Mobile.CurrentWayPoint = point.NextPoint; - if (point.NextPoint?.Deleted == true) - { - Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; - } - } - } - - private void FollowMaster() - { - var master = Mobile.SummonMaster; - if (master != null && master.Map == Mobile.Map && master.InRange(Mobile, Mobile.RangePerception)) - { - MoveTo(master, false, 1); - } - } - - public virtual bool DoActionCombat() - { - if (Core.AOS && CheckHerding()) - { - this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); - return true; - } - - var combatant = Mobile.Combatant; - if (!IsValidCombatant(combatant)) - { - DebugSay("My combatant is missing. Wandering..."); - - Mobile.FocusMob = null; - Mobile.Warmode = false; - Mobile.Combatant = null; - - Action = ActionType.Wander; - return true; - } - - if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) - { - this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); - } - - return true; - } - - public bool IsValidCombatant(Mobile combatant) => IsValidFocusMob(combatant) - && (Mobile.InLOS(combatant) || IsHostile(combatant)); - - public bool IsValidFocusMob(Mobile focusMob) => - focusMob != null - && !focusMob.Deleted - && focusMob.Map == Mobile.Map - && focusMob.Alive - && (focusMob is not BaseCreature bc || !bc.IsDeadPet) - && focusMob.AccessLevel == AccessLevel.Player - && Mobile.CanSee(focusMob) - && Mobile.InRange(focusMob, Mobile.RangePerception); - - public virtual bool DoActionGuard() - { - if (_investigating) - { - if (InvestigateLastKnown()) - { - return true; - } - - _investigating = false; - _guardStopTick = Core.TickCount + GuardGraceDuration; - } - - if (Core.TickCount - _guardStopTick < 0) - { - DebugSay("I am on guard."); - - if (Utility.Random(8) == 0) - { - Mobile.Direction = (Direction)Utility.Random(8); - } - - return true; - } - - DebugSay("I stopped being on guard. Going home..."); - Action = ActionType.Wander; - - return true; - } - - /// - /// Records the combatant's position while it is visible and in line of sight. - /// - private void UpdateLastKnownLocation() - { - var combatant = Mobile.Combatant; - - if (combatant?.Deleted == false && combatant.Map == Mobile.Map && - Mobile.CanSee(combatant) && Mobile.InLOS(combatant)) - { - _lkpTarget = combatant; - _lkpLocation = combatant.Location; - _lkpExpireTick = Core.TickCount + LkpFreshDuration; - } - } - - /// - /// Re-engages the last-seen target when it returns to view within perception range, - /// bypassing the reacquire throttle. - /// - private bool TryReengageLastKnown() - { - var target = _lkpTarget; - - if (target == null) - { - return false; - } - - if (target.Deleted || !target.Alive || target.Map != Mobile.Map || - target is BaseCreature { IsDeadPet: true } || Core.TickCount - _lkpExpireTick >= 0) - { - ClearLastKnown(); - return false; - } - - if (Mobile.Controlled || Mobile.BardPacified || Mobile.BardProvoked || Mobile.FightMode == FightMode.None) - { - return false; - } - - if (!Mobile.InRange(target, Mobile.RangePerception) || !Mobile.CanSee(target) || - !Mobile.InLOS(target) || !Mobile.CanBeHarmful(target, false)) - { - return false; - } - - DebugSay("There you are!"); - Mobile.Combatant = target; - Mobile.FocusMob = null; - Action = ActionType.Combat; - return true; - } - - /// - /// Walks toward the last-seen position until it is in view, reached, timed out, or - /// unreachable. Returns false when the investigation is finished. - /// - private bool InvestigateLastKnown() - { - if (_lkpTarget == null || Core.TickCount - _investigateStopTick >= 0) - { - return false; - } - - if (Mobile.InRange(_lkpLocation, 1) || - Mobile.InLOS(_lkpLocation) && Mobile.InRange(_lkpLocation, Mobile.RangePerception)) - { - DebugSay("They truly disappeared..."); - return false; - } - - _lkpGoal ??= _lkpLocation; - return MoveToPoint(_lkpGoal, false); - } - - private void ClearLastKnown() - { - _lkpTarget = null; - _lkpGoal = null; - _investigating = false; - } - - public virtual bool DoActionFlee() - { - var from = Mobile.Combatant; - - if (!IsValidCombatant(from)) - { - DebugSay("Combatant is missing."); - - WalkRandomInHome(3, 2, 1); - return true; - } - - DebugSay("I am fleeing!"); - - DoMove(from.GetDirectionTo(Mobile)); - return true; - } - - public virtual bool DoActionInteract() => true; - - public virtual bool CheckHerding() - { - var target = Mobile.TargetLocation; - - if (target == null) - { - _herdGoal = null; - return false; - } - - var distance = Mobile.GetDistanceToSqrt(target); - - if (distance >= 1 && distance <= 15) - { - // A cached boxed goal keeps the PathFollower persistent across ticks; walking - // through MoveToPoint paces herding on the movement clock and paths around - // obstacles. - if (_herdGoal == null || _herdGoal.X != target.X || _herdGoal.Y != target.Y) - { - _herdGoal = new Point3D(target.X, target.Y, Mobile.Map?.GetAverageZ(target.X, target.Y) ?? Mobile.Z); - } - - MoveToPoint(_herdGoal, false); - return true; - } - - if (distance < 1 && IsSpecialHerdingCase(target)) - { - HandleSpecialHerdingCase(); - } - - Mobile.TargetLocation = null; - _herdGoal = null; - return false; - } - - private bool IsSpecialHerdingCase(IPoint2D target) => target.X == 1076 && target.Y == 450 && Mobile is HordeMinionFamiliar; - - private void HandleSpecialHerdingCase() - { - if (Mobile.ControlMaster is PlayerMobile pm && pm.Quest is DarkTidesQuest qs) - { - var obj = qs.FindObjective(); - - if (obj?.Completed == false) - { - Mobile.AddToBackpack(new ScrollOfAbraxus()); - obj.Complete(); - } - } - } - - public virtual void DoBardPacified() - { - if (Core.Now < Mobile.BardEndTime) - { - DebugSay("I am pacified. Can not fight."); - - Mobile.Warmode = false; - Mobile.Combatant = null; - } - else - { - DebugSay("I am free from pacification."); - - Mobile.BardPacified = false; - } - } - - public virtual void DoBardProvoked() - { - if (Core.Now >= Mobile.BardEndTime && IsProvokerLost()) - { - DebugSay("Provoker missing."); - - Mobile.BardProvoked = false; - Mobile.BardMaster = null; - Mobile.BardTarget = null; - Mobile.Warmode = false; - Mobile.Combatant = null; - } - else if (IsProvokeTargetLost()) - { - DebugSay("Provoke target missing."); - - Mobile.BardProvoked = false; - Mobile.BardMaster = null; - Mobile.BardTarget = null; - Mobile.Warmode = false; - Mobile.Combatant = null; - } - else - { - Mobile.Combatant = Mobile.BardTarget; - Action = ActionType.Combat; - } - } - - private bool IsProvokerLost() => - Mobile.BardMaster?.Deleted != false - || Mobile.BardMaster.Map != Mobile.Map - || Mobile.GetDistanceToSqrt(Mobile.BardMaster) > Mobile.RangePerception; - - private bool IsProvokeTargetLost() => - Mobile.BardTarget?.Deleted != false - || Mobile.BardTarget.Map != Mobile.Map - || Mobile.GetDistanceToSqrt(Mobile.BardTarget) > Mobile.RangePerception; - - public virtual double FleeHealthThreshold => 0.0; - public virtual double FleeChance => 0.0; - - public virtual bool CheckFlee() - { - if (Mobile.CheckFlee()) - { - if (Mobile.Combatant == null) - { - Action = ActionType.Wander; - return false; - } - - Action = ActionType.Flee; - return true; - } - - if (StartFlee()) - { - var duration = TimeSpan.FromSeconds(Utility.Random(10, 30)); - Mobile.BeginFlee(duration); - - Action = ActionType.Flee; - - return true; - } - - return false; - } - - protected virtual bool StartFlee() - { - if (!Mobile.CanFlee) - { - return false; - } - - var hitPercent = (double)Mobile.Hits / Mobile.HitsMax; - - if (hitPercent > FleeHealthThreshold) - { - return false; - } - - return Utility.RandomDouble() < FleeChance; - } - - public virtual double BackoffChance => 0.0; - - protected virtual bool ShouldBackoff() - { - if (Mobile.Combatant != null || BackoffChance <= 0.0) - { - return false; - } - - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) - { - return Utility.RandomDouble() < BackoffChance; - } - - return false; - } - - public virtual bool DoActionBackoff() - { - if (AcquireFocusMob(Mobile.RangePerception * 2, FightMode.Closest, true, false, true)) - { - if (WalkMobileRange(Mobile.FocusMob, 1, false, Mobile.RangePerception, Mobile.RangePerception * 2)) - { - DebugSay("I backed off to safety. Wandering..."); - - Action = ActionType.Wander; - } - } - else - { - DebugSay("Focus target missing. Wandering..."); - - Action = ActionType.Wander; - } - - return true; - } - - public virtual void OnTeleported() - { - DebugSay("Teleported; recalculating path..."); - - Path?.ForceRepath(); - } - - public virtual bool AcquireFocusMob(int iRange, FightMode acqType, bool bPlayerOnly, bool bFacFriend, bool bFacFoe) - { - if (Mobile.Deleted || Mobile.Map == null) - { - return false; - } - - if (Mobile.BardPacified) - { - return false; - } - - if (HandleBardProvoked() || HandleControlled() || HandleConstantFocus()) - { - return true; - } - - if (acqType == FightMode.None) - { - Mobile.FocusMob = null; - return false; - } - - if (HandleAggressor(acqType)) - { - return false; - } - - if (Core.TickCount - Mobile.NextReacquireTime < 0) - { - Mobile.FocusMob = null; - return false; - } - - Mobile.NextReacquireTime = Core.TickCount + (int)Mobile.ReacquireDelay.TotalMilliseconds; - - DebugSay("Acquiring new target..."); - - if (Mobile.Map == null) - { - return Mobile.FocusMob != null; - } - - return AcquireNewFocusMob(Mobile.Map, iRange, acqType, bPlayerOnly, bFacFriend, bFacFoe); - } - - private bool HandleBardProvoked() - { - if (!Mobile.BardProvoked) - { - return false; - } - - if (Mobile.BardTarget?.Deleted != false) - { - Mobile.FocusMob = null; - return false; - } - - Mobile.FocusMob = Mobile.BardTarget; - return true; - } - - private bool HandleControlled() - { - if (!Mobile.Controlled) - { - return false; - } - - if (Mobile.ControlTarget?.Deleted == false && - Mobile.ControlTarget?.Hidden != true && - Mobile.ControlTarget?.Alive == true && - Mobile.ControlTarget?.IsDeadBondedPet != true && - Mobile.InRange(Mobile.ControlTarget, Mobile.RangePerception * 2)) - { - Mobile.FocusMob = Mobile.ControlTarget; - return true; - } - - if (Mobile.ControlTarget != null && Mobile.ControlTarget != Mobile.ControlMaster) - { - Mobile.ControlTarget = null; - } - - Mobile.FocusMob = null; - return false; - } - - private bool HandleConstantFocus() - { - if (Mobile.ConstantFocus == null) - { - return false; - } - - this.DebugSayFormatted($"Acquired focused target: {Mobile.ConstantFocus.Name}."); - - Mobile.FocusMob = Mobile.ConstantFocus; - return true; - } - - private bool HandleAggressor(FightMode acqType) - { - if (acqType != FightMode.Aggressor || - Mobile.Aggressors.Count > 0 || - Mobile.Aggressed.Count > 0 || - Mobile.FactionAllegiance != null || - Mobile.EthicAllegiance != null) - { - return false; - } - - Mobile.FocusMob = null; - return true; - } - - private bool AcquireNewFocusMob(Map map, int iRange, FightMode acqType, bool bPlayerOnly, bool bFacFriend, bool bFacFoe) - { - Mobile newFocusMob = null, enemySummonMob = null; - double val = double.MinValue, enemySummonVal = double.MinValue; - - foreach (var m in map.GetMobilesInRange(Mobile.Location, iRange)) - { - if (IsInvalidTarget(m, bPlayerOnly)) - { - continue; - } - - var bc = m as BaseCreature; - var pm = m as PlayerMobile; - - if (IsInvalidSummonTarget(m, bc, pm) || IsInvalidFactionTarget(m, bFacFriend, bFacFoe) - || IsInvalidFightModeTarget(m, acqType, bc)) - { - continue; - } - - var theirVal = Mobile.GetFightModeRanking(m, acqType, bPlayerOnly); - - if (theirVal > val && Mobile.InLOS(m)) - { - newFocusMob = m; - val = theirVal; - } - else if (Core.AOS && theirVal > enemySummonVal - && Mobile.InLOS(m) && bc?.Summoned == true && bc.Controlled != true) - { - enemySummonMob = m; - enemySummonVal = theirVal; - } - } - - Mobile.FocusMob = newFocusMob ?? enemySummonMob; - return Mobile.FocusMob != null; - } - - private bool IsInvalidTarget(Mobile m, bool bPlayerOnly) => - m.Deleted || m.Blessed || m == Mobile || m is BaseFamiliar || !m.Alive || m.IsDeadBondedPet || - m.AccessLevel > AccessLevel.Player || bPlayerOnly && !m.Player || !Mobile.CanSee(m); - - private bool IsInvalidSummonTarget(Mobile m, BaseCreature bc, PlayerMobile pm) - { - if (Core.AOS && bc?.Summoned == true && - (bc.SummonMaster == Mobile || !bc.SummonMaster.Player && IsHostile(bc.SummonMaster))) - { - return true; - } - - if (!Mobile.Summoned || Mobile.SummonMaster == null) - { - return false; - } - - return m == Mobile.SummonMaster || !SpellHelper.ValidIndirectTarget(Mobile.SummonMaster, m) || - Mobile.IsAnimatedDead && (pm != null || bc?.IsAnimatedDead == true || bc?.Controlled == true); - } - - private bool IsInvalidFactionTarget(Mobile m, bool bFacFriend, bool bFacFoe) - { - if (bFacFriend && !Mobile.IsFriend(m)) - { - return true; - } - - if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell)) || - Mobile.Combatant != m && VirtueSystem.GetVirtues(m as PlayerMobile)?.HonorActive == true) - { - return true; - } - - return bFacFoe && (!Mobile.IsEnemy(m) || !bFacFriend && !Mobile.CanBeHarmful(m, false)); - } - - private bool IsInvalidFightModeTarget(Mobile m, FightMode acqType, BaseCreature bc) - { - if (acqType is not (FightMode.Aggressor or FightMode.Evil)) - { - return false; - } - - var valid = IsHostile(m) || Mobile.GetFactionAllegiance(m) == BaseCreature.Allegiance.Enemy - || Mobile.GetEthicAllegiance(m) == BaseCreature.Allegiance.Enemy; - - // Valid if FightMode is Evil and the target's karma is negative - return !valid && (acqType != FightMode.Evil || (bc?.GetMaster()?.Karma ?? m.Karma) >= 0); - } - - private bool IsHostile(Mobile from) => - Mobile.Combatant == from || from.Combatant == Mobile || IsAggressor(from) || IsAggressed(from); - - private bool IsAggressor(Mobile from) - { - foreach (var aggressor in Mobile.Aggressors) - { - if (aggressor.Defender == from) - { - return true; - } - } - return false; - } - - private bool IsAggressed(Mobile from) - { - foreach (var aggressed in Mobile.Aggressed) - { - if (aggressed.Attacker == from) - { - return true; - } - } - - return false; - } - - public virtual void DetectHidden() - { - if (Mobile.Deleted || Mobile.Map == null || !CanDetectHidden) - { - return; - } - - DebugSay("Checking for hidden entities..."); - - var srcSkill = Mobile.Skills.DetectHidden.Value; - - if (srcSkill <= 0) - { - return; - } - - foreach (var trg in Mobile.GetMobilesInRange(Mobile.RangePerception)) - { - if (IsValidTargetCombatTarget(trg)) - { - TryDetectHidden(trg, srcSkill); - } - } - } - - private bool IsValidTargetCombatTarget(Mobile trg) => trg != Mobile && trg.Player && trg.Alive && trg.Hidden && - trg.AccessLevel == AccessLevel.Player && Mobile.InLOS(trg); - - private void TryDetectHidden(Mobile trg, double srcSkill) - { - this.DebugSayFormatted($"Trying to detect: {trg.Name}"); - - var trgHiding = trg.Skills.Hiding.Value / 2.9; - var trgStealth = trg.Skills.Stealth.Value / 1.8; - var chance = Math.Max(srcSkill / 10, srcSkill / 1.2 - Math.Min(trgHiding, trgStealth)) / 100; - - if (chance > Utility.RandomDouble()) - { - trg.RevealingAction(); - trg.SendLocalizedMessage(500814); - // You have been revealed! - } - } - - public virtual void Deactivate() - { - if (Mobile.Map == Map.Internal || !Mobile.Controlled && !Mobile.Map.GetSector(Mobile.Location).Active) - { - AITimer.Stop(); - } - - if (ShouldReturnToHome(Mobile.Spawner)) - { - Timer.StartTimer(ReturnToHome); - } - } - - private bool ShouldReturnToHome(ISpawner spawner) => - spawner?.ReturnOnDeactivate == true && !Mobile.Controlled && - !spawner.IsInSpawnBounds(Mobile.Location); - - private void ReturnToHome() - { - if (Mobile.Spawner is not Spawner spawner) - { - return; - } - - var loc = spawner.GetSpawnPosition(Mobile, spawner.Map); - - if (loc != Point3D.Zero) - { - Mobile.MoveToWorld(loc, spawner.Map); - } - - Activate(); - } - - public virtual void Activate() - { - if (!AITimer.Running) - { - AITimer.Activate(); - } - } - - public virtual void OnCurrentSpeedChanged() - { - AITimer.OnSpeedChanged(); - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAIExtensions.cs b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAIExtensions.cs deleted file mode 100644 index e46181c00..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAIExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Runtime.CompilerServices; -using Server.Mobiles.AI.BaseAI; - -namespace Server.Mobiles; - -public static class BaseAIExtensions -{ - public static void DebugSayFormatted(this BaseAI ai, - [InterpolatedStringHandlerArgument("ai")] ref DebugInterpolatedStringHandler handler, int cooldownMs = 5000) - { - var message = handler.Text; - if (message.Length > 0) - { - ai.Mobile.PublicOverheadMessage(MessageType.Regular, 41, false, message.ToString()); - ai.NextDebugMessage = Core.TickCount + cooldownMs; - } - - handler.Clear(); - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/ContextMenu.cs b/Projects/UOContent/Mobiles/AI/BaseAI/ContextMenu.cs deleted file mode 100644 index cd6188046..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/ContextMenu.cs +++ /dev/null @@ -1,75 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ContextMenu.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using Server.Collections; -using Server.ContextMenus; - -namespace Server.Mobiles; - -public abstract partial class BaseAI -{ - public virtual void GetContextMenuEntries(Mobile from, ref PooledRefList list) - { - if (!from.Alive || !Mobile.Controlled || !from.InRange(Mobile, 16)) - { - return; - } - - if (from == Mobile.ControlMaster) - { - AddControlMasterEntries(ref list); - } - else if (Mobile.IsPetFriend(from)) - { - AddPetFriendEntries(ref list); - } - } - - private void AddControlMasterEntries(ref PooledRefList list) - { - var isDeadPet = Mobile.IsDeadPet; - - list.Add(new InternalEntry(3006111, 14, OrderType.Attack, !isDeadPet)); // Command: Kill - list.Add(new InternalEntry(3006108, 14, OrderType.Follow, true)); // Command: Follow - list.Add(new InternalEntry(3006107, 14, OrderType.Guard, !isDeadPet)); // Command: Guard - list.Add(new InternalEntry(3006112, 14, OrderType.Stop, true)); // Command: Stop - list.Add(new InternalEntry(3006114, 14, OrderType.Stay, true)); // Command: Stay - - if (Mobile.CanDrop) - { - list.Add(new InternalEntry(3006109, 14, OrderType.Drop, !isDeadPet)); // Command: Drop - } - - list.Add(new InternalEntry(3006098, 14, OrderType.Rename, true)); // Rename - - if (!Mobile.Summoned && Mobile is not GrizzledMare) - { - list.Add(new InternalEntry(3006110, 14, OrderType.Friend, true)); // Add Friend - list.Add(new InternalEntry(3006099, 14, OrderType.Unfriend, true)); // Remove Friend - list.Add(new InternalEntry(3006113, 14, OrderType.Transfer, !isDeadPet)); // Transfer - } - - list.Add(new InternalEntry(3006118, 14, OrderType.Release, true)); // Release - } - - private void AddPetFriendEntries(ref PooledRefList list) - { - var isDeadPet = Mobile.IsDeadPet; - - list.Add(new InternalEntry(3006108, 14, OrderType.Follow, true)); // Command: Follow - list.Add(new InternalEntry(3006112, 14, OrderType.Stop, !isDeadPet)); // Command: Stop - list.Add(new InternalEntry(3006114, 14, OrderType.Stay, true)); // Command: Stay - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs b/Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs deleted file mode 100644 index 05630d2f8..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs +++ /dev/null @@ -1,663 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.Runtime.CompilerServices; -using Server.Buffers; - -namespace Server.Mobiles.AI.BaseAI; - -[InterpolatedStringHandler] -public ref struct DebugInterpolatedStringHandler -{ - // Implementation note: - // As this type lives in CompilerServices and is only intended to be targeted by the compiler, - // public APIs eschew argument validation logic in a variety of places, e.g. allowing a null input - // when one isn't expected to produce a NullReferenceException rather than an ArgumentNullException. - - /// Expected average length of formatted data used for an individual interpolation expression result. - /// - /// This is inherited from string.Format, and could be changed based on further data. - /// string.Format actually uses `format.Length + args.Length * 8`, but format.Length - /// includes the format items themselves, e.g. "{0}", and since it's rare to have double-digit - /// numbers of items, we bump the 8 up to 11 to account for the three extra characters in "{d}", - /// since the compiler-provided base length won't include the equivalent character count. - /// - private const int GuessedLengthPerHole = 11; - /// Minimum size array to rent from the pool. - /// Same as stack-allocation size used today by string.Format. - private const int MinimumArrayPoolLength = 256; - - /// Optional provider to pass to IFormattable.ToString or ISpanFormattable.TryFormat calls. - private readonly IFormatProvider? _provider; - /// Array rented from the array pool and used to back . - private char[]? _arrayToReturnToPool; - /// The span to write into. - private Span _chars; - /// Position at which to write the next character. - private int _pos; - /// Whether provides an ICustomFormatter. - /// - /// Custom formatters are very rare. We want to support them, but it's ok if we make them more expensive - /// in order to make them as pay-for-play as possible. So, we avoid adding another reference type field - /// to reduce the size of the handler and to reduce required zero'ing, by only storing whether the provider - /// provides a formatter, rather than actually storing the formatter. This in turn means, if there is a - /// formatter, we pay for the extra interface call on each AppendFormatted that needs it. - /// - private readonly bool _hasCustomFormatter; - - private readonly bool _debugActive; - - /// Creates a handler used to translate an interpolated string into a . - /// The number of constant characters outside of interpolation expressions in the interpolated string. - /// The number of interpolation expressions in the interpolated string. - /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. - public DebugInterpolatedStringHandler(int literalLength, int formattedCount, Mobiles.BaseAI ai) - { - _debugActive = ai.Mobile?.Debug == true && Core.TickCount >= ai.NextDebugMessage; - - if (_debugActive) - { - _chars = _arrayToReturnToPool = STArrayPool.Shared.Rent(GetDefaultLength(literalLength, formattedCount)); - } - else - { - _chars = _arrayToReturnToPool = null; - } - - _provider = null; - _pos = 0; - _hasCustomFormatter = false; - } - - /// Creates a handler used to translate an interpolated string into a . - /// The number of constant characters outside of interpolation expressions in the interpolated string. - /// The number of interpolation expressions in the interpolated string. - /// An object that supplies culture-specific formatting information. - /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. - public DebugInterpolatedStringHandler(int literalLength, int formattedCount, IFormatProvider? provider, Mobiles.BaseAI ai) - { - _debugActive = ai.Mobile?.Debug == true && Core.TickCount >= ai.NextDebugMessage; - - if (_debugActive) - { - _chars = _arrayToReturnToPool = STArrayPool.Shared.Rent(GetDefaultLength(literalLength, formattedCount)); - } - else - { - _chars = _arrayToReturnToPool = null; - } - - _provider = provider; - _pos = 0; - _hasCustomFormatter = provider is not null && HasCustomFormatter(provider); - } - - /// Derives a default length with which to seed the handler. - /// The number of constant characters outside of interpolation expressions in the interpolated string. - /// The number of interpolation expressions in the interpolated string. - [MethodImpl(MethodImplOptions.AggressiveInlining)] // becomes a constant when inputs are constant - internal static int GetDefaultLength(int literalLength, int formattedCount) => - Math.Max(MinimumArrayPoolLength, literalLength + formattedCount * GuessedLengthPerHole); - - /// Clears the handler, returning any rented array to the pool. - [MethodImpl(MethodImplOptions.AggressiveInlining)] // used only on a few hot paths - public void Clear() - { - var toReturn = _arrayToReturnToPool; - this = default; // defensive clear - if (toReturn is not null) - { - STArrayPool.Shared.Return(toReturn); - } - } - - /// Gets a span of the written characters thus far. - public ReadOnlySpan Text => _chars[.._pos]; - - /// Writes the specified string to the handler. - /// The string to write. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AppendLiteral(string value) - { - if (!_debugActive) - { - return; - } - - if (value.Length == 1) - { - var chars = _chars; - var pos = _pos; - if ((uint)pos < (uint)chars.Length) - { - chars[pos] = value[0]; - _pos = pos + 1; - } - else - { - GrowThenCopyString(value); - } - return; - } - - AppendStringDirect(value); - } - - /// Writes the specified string to the handler. - /// The string to write. - private void AppendStringDirect(string value) - { - if (value.TryCopyTo(_chars[_pos..])) - { - _pos += value.Length; - } - else - { - GrowThenCopyString(value); - } - } - - #region AppendFormatted - // Design note: - // The compiler requires a AppendFormatted overload for anything that might be within an interpolation expression; - // if it can't find an appropriate overload, for handlers in general it'll simply fail to compile. - // (For target-typing to string where it uses DefaultInterpolatedStringHandler implicitly, it'll instead fall back to - // its other mechanisms, e.g. using string.Format. This fallback has the benefit that if we miss a case, - // interpolated strings will still work, but it has the downside that a developer generally won't know - // if the fallback is happening and they're paying more.) - // - // At a minimum, then, we would need an overload that accepts: - // (object value, int alignment = 0, string? format = null) - // Such an overload would provide the same expressiveness as string.Format. However, this has several - // shortcomings: - // - Every value type in an interpolation expression would be boxed. - // - ReadOnlySpan could not be used in interpolation expressions. - // - Every AppendFormatted call would have three arguments at the call site, bloating the IL further. - // - Every invocation would be more expensive, due to lack of specialization, every call needing to account - // for alignment and format, etc. - // - // To address that, we could just have overloads for T and ReadOnlySpan: - // (T) - // (T, int alignment) - // (T, string? format) - // (T, int alignment, string? format) - // (ReadOnlySpan) - // (ReadOnlySpan, int alignment) - // (ReadOnlySpan, string? format) - // (ReadOnlySpan, int alignment, string? format) - // but this also has shortcomings: - // - Some expressions that would have worked with an object overload will now force a fallback to string.Format - // (or fail to compile if the handler is used in places where the fallback isn't provided), because the compiler - // can't always target type to T, e.g. `b switch { true => 1, false => null }` where `b` is a bool can successfully - // be passed as an argument of type `object` but not of type `T`. - // - Reference types get no benefit from going through the generic code paths, and actually incur some overheads - // from doing so. - // - Nullable value types also pay a heavy price, in particular around interface checks that would generally evaporate - // at compile time for value types but don't (currently) if the Nullable goes through the same code paths - // (see https://github.com/dotnet/runtime/issues/50915). - // - // We could try to take a more elaborate approach for DefaultInterpolatedStringHandler, since it is the most common handler - // and we want to minimize overheads both at runtime and in IL size, e.g. have a complete set of overloads for each of: - // (T, ...) where T : struct - // (T?, ...) where T : struct - // (object, ...) - // (ReadOnlySpan, ...) - // (string, ...) - // but this also has shortcomings, most importantly: - // - If you have an unconstrained T that happens to be a value type, it'll now end up getting boxed to use the object overload. - // This also necessitates the T? overload, since nullable value types don't meet a T : struct constraint, so without those - // they'd all map to the object overloads as well. - // - Any reference type with an implicit cast to ROS will fail to compile due to ambiguities between the overloads. string - // is one such type, hence needing dedicated overloads for it that can be bound to more tightly. - // - // A middle ground we've settled on, which is likely to be the right approach for most other handlers as well, would be the set: - // (T, ...) with no constraint - // (ReadOnlySpan) and (ReadOnlySpan, int) - // (object, int alignment = 0, string? format = null) - // (string) and (string, int) - // This would address most of the concerns, at the expense of: - // - Most reference types going through the generic code paths and so being a bit more expensive. - // - Nullable types being more expensive until https://github.com/dotnet/runtime/issues/50915 is addressed. - // We could choose to add a T? where T : struct set of overloads if necessary. - // Strings don't require their own overloads here, but as they're expected to be very common and as we can - // optimize them in several ways (can copy the contents directly, don't need to do any interface checks, don't - // need to pay the shared generic overheads, etc.) we can add overloads specifically to optimize for them. - // - // Hole values are formatted according to the following policy: - // 1. If an IFormatProvider was supplied and it provides an ICustomFormatter, use ICustomFormatter.Format (even if the value is null). - // 2. If the type implements ISpanFormattable, use ISpanFormattable.TryFormat. - // 3. If the type implements IFormattable, use IFormattable.ToString. - // 4. Otherwise, use object.ToString. - // This matches the behavior of string.Format, StringBuilder.AppendFormat, etc. The only overloads for which this doesn't - // apply is ReadOnlySpan, which isn't supported by either string.Format nor StringBuilder.AppendFormat, but more - // importantly which can't be boxed to be passed to ICustomFormatter.Format. - - #region AppendFormatted T - /// Writes the specified value to the handler. - /// The value to write. - public void AppendFormatted(T value) - { - if (!_debugActive) - { - return; - } - - // This method could delegate to AppendFormatted with a null format, but explicitly passing - // default as the format to TryFormat helps to improve code quality in some cases when TryFormat is inlined, - // e.g. for Int32 it enables the JIT to eliminate code in the inlined method based on a length check on the format. - - // If there's a custom formatter, always use it. - if (_hasCustomFormatter) - { - AppendCustomFormatter(value, format: null); - return; - } - - // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter - // requires the former. For value types, it won't matter as the type checks devolve into - // JIT-time constants. For reference types, they're more likely to implement IFormattable - // than they are to implement ISpanFormattable: if they don't implement either, we save an - // interface check over first checking for ISpanFormattable and then for IFormattable, and - // if it only implements IFormattable, we come out even: only if it implements both do we - // end up paying for an extra interface check. - string? s; - if (value is IFormattable) - { - // If the value can format itself directly into our buffer, do so. - if (value is ISpanFormattable) - { - int charsWritten; - while (!((ISpanFormattable)value).TryFormat(_chars[_pos..], out charsWritten, default, _provider)) // constrained call avoiding boxing for value types - { - Grow(); - } - - _pos += charsWritten; - return; - } - - s = ((IFormattable)value).ToString(format: null, _provider); // constrained call avoiding boxing for value types - } - else - { - s = value?.ToString(); - } - - if (s is not null) - { - AppendStringDirect(s); - } - } - /// Writes the specified value to the handler. - /// The value to write. - /// The format string. - public void AppendFormatted(T value, string? format) - { - if (!_debugActive) - { - return; - } - - // If there's a custom formatter, always use it. - if (_hasCustomFormatter) - { - AppendCustomFormatter(value, format); - return; - } - - // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter - // requires the former. For value types, it won't matter as the type checks devolve into - // JIT-time constants. For reference types, they're more likely to implement IFormattable - // than they are to implement ISpanFormattable: if they don't implement either, we save an - // interface check over first checking for ISpanFormattable and then for IFormattable, and - // if it only implements IFormattable, we come out even: only if it implements both do we - // end up paying for an extra interface check. - string? s; - if (value is IFormattable) - { - // If the value can format itself directly into our buffer, do so. - if (value is ISpanFormattable) - { - int charsWritten; - while (!((ISpanFormattable)value).TryFormat(_chars[_pos..], out charsWritten, format, _provider)) // constrained call avoiding boxing for value types - { - Grow(); - } - - _pos += charsWritten; - return; - } - - s = ((IFormattable)value).ToString(format, _provider); // constrained call avoiding boxing for value types - } - else - { - s = value?.ToString(); - } - - if (s is not null) - { - AppendStringDirect(s); - } - } - - /// Writes the specified value to the handler. - /// The value to write. - /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. - public void AppendFormatted(T value, int alignment) - { - if (!_debugActive) - { - return; - } - - var startingPos = _pos; - AppendFormatted(value); - if (alignment != 0) - { - AppendOrInsertAlignmentIfNeeded(startingPos, alignment); - } - } - - /// Writes the specified value to the handler. - /// The value to write. - /// The format string. - /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. - public void AppendFormatted(T value, int alignment, string? format) - { - if (!_debugActive) - { - return; - } - - var startingPos = _pos; - AppendFormatted(value, format); - if (alignment != 0) - { - AppendOrInsertAlignmentIfNeeded(startingPos, alignment); - } - } - #endregion - - #region AppendFormatted ReadOnlySpan - /// Writes the specified character span to the handler. - /// The span to write. - public void AppendFormatted(ReadOnlySpan value) - { - if (!_debugActive) - { - return; - } - - // Fast path for when the value fits in the current buffer - if (value.TryCopyTo(_chars[_pos..])) - { - _pos += value.Length; - } - else - { - GrowThenCopySpan(value); - } - } - - /// Writes the specified string of chars to the handler. - /// The span to write. - /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. - /// The format string. - public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) - { - if (!_debugActive) - { - return; - } - - var leftAlign = false; - if (alignment < 0) - { - leftAlign = true; - alignment = -alignment; - } - - var paddingRequired = alignment - value.Length; - if (paddingRequired <= 0) - { - // The value is as large or larger than the required amount of padding, - // so just write the value. - AppendFormatted(value); - return; - } - - // Write the value along with the appropriate padding. - EnsureCapacityForAdditionalChars(value.Length + paddingRequired); - if (leftAlign) - { - value.CopyTo(_chars[_pos..]); - _pos += value.Length; - _chars.Slice(_pos, paddingRequired).Fill(' '); - _pos += paddingRequired; - } - else - { - _chars.Slice(_pos, paddingRequired).Fill(' '); - _pos += paddingRequired; - value.CopyTo(_chars[_pos..]); - _pos += value.Length; - } - } - #endregion - - #region AppendFormatted string - /// Writes the specified value to the handler. - /// The value to write. - public void AppendFormatted(string? value) - { - if (!_debugActive) - { - return; - } - - // Fast-path for no custom formatter and a non-null string that fits in the current destination buffer. - if (!_hasCustomFormatter && value?.TryCopyTo(_chars[_pos..]) == true) - { - _pos += value.Length; - } - else - { - AppendFormattedSlow(value); - } - } - - /// Writes the specified value to the handler. - /// The value to write. - /// - /// Slow path to handle a custom formatter, potentially null value, - /// or a string that doesn't fit in the current buffer. - /// - [MethodImpl(MethodImplOptions.NoInlining)] - private void AppendFormattedSlow(string? value) - { - if (_hasCustomFormatter) - { - AppendCustomFormatter(value, format: null); - } - else if (value is not null) - { - EnsureCapacityForAdditionalChars(value.Length); - value.CopyTo(_chars[_pos..]); - _pos += value.Length; - } - } - - /// Writes the specified value to the handler. - /// The value to write. - /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. - /// The format string. - public void AppendFormatted(string? value, int alignment = 0, string? format = null) => - // Format is meaningless for strings and doesn't make sense for someone to specify. We have the overload - // simply to disambiguate between ROS and object, just in case someone does specify a format, as - // string is implicitly convertible to both. Just delegate to the T-based implementation. - AppendFormatted(value, alignment, format); - #endregion - - #region AppendFormatted object - /// Writes the specified value to the handler. - /// The value to write. - /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. - /// The format string. - public void AppendFormatted(object? value, int alignment = 0, string? format = null) => - // This overload is expected to be used rarely, only if either a) something strongly typed as object is - // formatted with both an alignment and a format, or b) the compiler is unable to target type to T. It - // exists purely to help make cases from (b) compile. Just delegate to the T-based implementation. - AppendFormatted(value, alignment, format); - #endregion - #endregion - - /// Gets whether the provider provides a custom formatter. - [MethodImpl(MethodImplOptions.AggressiveInlining)] // only used in a few hot path call sites - internal static bool HasCustomFormatter(IFormatProvider provider) - { - Debug.Assert(provider is not null); - Debug.Assert(provider is not CultureInfo || provider.GetFormat(typeof(ICustomFormatter)) is null, "Expected CultureInfo to not provide a custom formatter"); - return - provider.GetType() != typeof(CultureInfo) && // optimization to avoid GetFormat in the majority case - provider.GetFormat(typeof(ICustomFormatter)) != null; - } - - /// Formats the value using the custom formatter from the provider. - /// The value to write. - /// The format string. - [MethodImpl(MethodImplOptions.NoInlining)] - private void AppendCustomFormatter(T value, string? format) - { - // This case is very rare, but we need to handle it prior to the other checks in case - // a provider was used that supplied an ICustomFormatter which wanted to intercept the particular value. - // We do the cast here rather than in the ctor, even though this could be executed multiple times per - // formatting, to make the cast pay for play. - Debug.Assert(_hasCustomFormatter); - Debug.Assert(_provider != null); - - var formatter = (ICustomFormatter?)_provider.GetFormat(typeof(ICustomFormatter)); - Debug.Assert(formatter != null, "An incorrectly written provider said it implemented ICustomFormatter, and then didn't"); - - if (formatter?.Format(format, value, _provider) is string customFormatted) - { - AppendStringDirect(customFormatted); - } - } - - /// Handles adding any padding required for aligning a formatted value in an interpolation expression. - /// The position at which the written value started. - /// Non-zero minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. - private void AppendOrInsertAlignmentIfNeeded(int startingPos, int alignment) - { - Debug.Assert(startingPos >= 0 && startingPos <= _pos); - Debug.Assert(alignment != 0); - - var charsWritten = _pos - startingPos; - - var leftAlign = false; - if (alignment < 0) - { - leftAlign = true; - alignment = -alignment; - } - - var paddingNeeded = alignment - charsWritten; - if (paddingNeeded > 0) - { - EnsureCapacityForAdditionalChars(paddingNeeded); - - if (leftAlign) - { - _chars.Slice(_pos, paddingNeeded).Fill(' '); - } - else - { - _chars.Slice(startingPos, charsWritten).CopyTo(_chars[(startingPos + paddingNeeded)..]); - _chars.Slice(startingPos, paddingNeeded).Fill(' '); - } - - _pos += paddingNeeded; - } - } - - /// Ensures has the capacity to store beyond . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void EnsureCapacityForAdditionalChars(int additionalChars) - { - if (_chars.Length - _pos < additionalChars) - { - Grow(additionalChars); - } - } - - /// Fallback for fast path in when there's not enough space in the destination. - /// The string to write. - [MethodImpl(MethodImplOptions.NoInlining)] - private void GrowThenCopyString(string value) - { - Grow(value.Length); - value.CopyTo(_chars[_pos..]); - _pos += value.Length; - } - - /// Fallback for for when not enough space exists in the current buffer. - /// The span to write. - [MethodImpl(MethodImplOptions.NoInlining)] - private void GrowThenCopySpan(ReadOnlySpan value) - { - Grow(value.Length); - value.CopyTo(_chars[_pos..]); - _pos += value.Length; - } - - /// Grows to have the capacity to store at least beyond . - [MethodImpl(MethodImplOptions.NoInlining)] // keep consumers as streamlined as possible - private void Grow(int additionalChars) - { - // This method is called when the remaining space (_chars.Length - _pos) is - // insufficient to store a specific number of additional characters. Thus, we - // need to grow to at least that new total. GrowCore will handle growing by more - // than that if possible. - Debug.Assert(additionalChars > _chars.Length - _pos); - GrowCore((uint)_pos + (uint)additionalChars); - } - - /// Grows the size of . - [MethodImpl(MethodImplOptions.NoInlining)] // keep consumers as streamlined as possible - private void Grow() - { - // This method is called when the remaining space in _chars isn't sufficient to continue - // the operation. Thus, we need at least one character beyond _chars.Length. GrowCore - // will handle growing by more than that if possible. - GrowCore((uint)_chars.Length + 1); - } - - /// Grow the size of to at least the specified . - [MethodImpl(MethodImplOptions.AggressiveInlining)] // but reuse this grow logic directly in both of the above grow routines - private void GrowCore(uint requiredMinCapacity) - { - // We want the max of how much space we actually required and doubling our capacity (without going beyond the max allowed length). We - // also want to avoid asking for small arrays, to reduce the number of times we need to grow, and since we're working with unsigned - // ints that could technically overflow if someone tried to, for example, append a huge string to a huge string, we also clamp to int.MaxValue. - // Even if the array creation fails in such a case, we may later fail in ToStringAndClear. - - var newCapacity = Math.Max(requiredMinCapacity, Math.Min((uint)_chars.Length * 2, 0x3FFFFFDF)); - var arraySize = (int)Math.Clamp(newCapacity, MinimumArrayPoolLength, int.MaxValue); - - var newArray = STArrayPool.Shared.Rent(arraySize); - _chars[.._pos].CopyTo(newArray); - - var toReturn = _arrayToReturnToPool; - _chars = _arrayToReturnToPool = newArray; - - if (toReturn is not null) - { - STArrayPool.Shared.Return(toReturn); - } - } -} - diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/InternalEntry.cs b/Projects/UOContent/Mobiles/AI/BaseAI/InternalEntry.cs deleted file mode 100644 index 9bee2edff..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/InternalEntry.cs +++ /dev/null @@ -1,119 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: InternalEntry.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using Server.ContextMenus; -using Server.Gumps; - -namespace Server.Mobiles; - -internal sealed class InternalEntry : ContextMenuEntry -{ - private readonly OrderType _order; - - public InternalEntry(int number, int range, OrderType order, bool enabled) : base(number, range) - { - _order = order; - Enabled = enabled; - } - - public override void OnClick(Mobile from, IEntity target) - { - if (!IsValidClick(from, target, out var bc) || - IsInvalidOrderForDeadPet(bc) || - !IsOwnerOrFriend(from, bc, out var isFriend) || - IsInvalidOrderForFriend(isFriend)) - { - return; - } - - HandleOrder(from, bc); - } - - private static bool IsValidClick(Mobile from, IEntity target, out BaseCreature bc) - { - bc = target as BaseCreature; - return from.CheckAlive() && bc != null && !bc.Deleted && bc.Controlled; - } - - private bool IsInvalidOrderForDeadPet(BaseCreature bc) => bc.IsDeadPet && _order is OrderType.Guard or OrderType.Attack or OrderType.Transfer or OrderType.Drop; - - private static bool IsOwnerOrFriend(Mobile from, BaseCreature bc, out bool isFriend) - { - var isOwner = from == bc.ControlMaster; - isFriend = !isOwner && bc.IsPetFriend(from); - return isOwner || isFriend; - } - - private bool IsInvalidOrderForFriend(bool isFriend) => isFriend && _order is not (OrderType.Follow or OrderType.Stay or OrderType.Stop); - - private void HandleOrder(Mobile from, BaseCreature bc) - { - switch (_order) - { - case OrderType.Follow: - case OrderType.Attack: - case OrderType.Transfer: - case OrderType.Friend: - case OrderType.Unfriend: - { - HandleTargetOrder(from, bc); - break; - } - case OrderType.Release: - { - HandleReleaseOrder(from, bc); - break; - } - default: - { - HandleDefaultOrder(from, bc); - break; - } - } - } - - private void HandleTargetOrder(Mobile from, BaseCreature bc) - { - if (_order is OrderType.Transfer or OrderType.Friend && from.HasTrade) - { - from.SendLocalizedMessage(_order == OrderType.Transfer ? 1010507 : 1070947); - // 1010507: You cannot transfer a pet with a trade pending - // 1070947: You cannot friend a pet with a trade pending - return; - } - - bc.AIObject.BeginPickTarget(from, _order); - } - - private void HandleReleaseOrder(Mobile from, BaseCreature bc) - { - if (bc.Summoned) - { - HandleDefaultOrder(from, bc); - return; - } - - from.SendGump(new ConfirmReleaseGump(from, bc)); - } - - private void HandleDefaultOrder(Mobile from, BaseCreature bc) - { - if (bc.CheckControlChance(from)) - { - bc.ControlTarget = null; - bc.ControlOrder = _order; - } - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs b/Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs deleted file mode 100644 index 8ffd39b42..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs +++ /dev/null @@ -1,457 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: OnSpeech.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using System; -using Server.Items; -using Server.Gumps; - -namespace Server.Mobiles; - -public abstract partial class BaseAI -{ - public virtual bool HandlesOnSpeech(Mobile from) - { - if (from.AccessLevel >= AccessLevel.GameMaster) - { - return true; - } - - if (from.Alive && Mobile.Controlled && Mobile.Commandable && - (from == Mobile.ControlMaster || Mobile.IsPetFriend(from))) - { - return true; - } - - return from.Alive && from.InRange(Mobile.Location, 3) && Mobile.IsHumanInTown(); - } - - public virtual void OnSpeech(SpeechEventArgs e) - { - if (WasNamed(e.Speech) && e.Mobile.Alive && - e.Mobile.InRange(Mobile.Location, 3) && Mobile.IsHumanInTown()) - { - if (HandleMoveCommand(e) || HandleTimeCommand(e) || HandleTrainCommand(e)) - { - return; - } - } - - if (Mobile.Controlled && Mobile.Commandable) - { - AllOnSpeechPet(e); - NamedOnSpeechPet(e); - return; - } - - if (e.Mobile.AccessLevel >= AccessLevel.GameMaster) - { - HandleGMCommands(e); - } - } - - private bool HandleMoveCommand(SpeechEventArgs e) - { - if (!e.HasKeyword(0x9D)) // *move* - { - return false; - } - - if ((Core.Now - _lastOrder).TotalSeconds < 5) - { - return true; - } - - _lastOrder = Core.Now; - - var map = Mobile.Map; - var currentLoc = Mobile.Location; - - var newX = currentLoc.X + Utility.RandomMinMax(-1, 1); - var newY = currentLoc.Y + Utility.RandomMinMax(-1, 1); - var newZ = currentLoc.Z; - - if (map != null && map.CanFit(newX, newY, newZ, 16, false, false)) - { - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501516); // Excuse me? - Mobile.Location = new Point3D(newX, newY, newZ); - } - else - { - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501487); - // You're standing too close, go away. - } - - return true; - } - - private bool HandleTimeCommand(SpeechEventArgs e) - { - if (!e.HasKeyword(0x9E)) // *time* - { - return false; - } - - if ((Core.Now - _lastOrder).TotalSeconds < 5) - { - return true; - } - - _lastOrder = Core.Now; - - Clock.GetTime(Mobile, out var generalNumber, out _); - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, generalNumber); - - return true; - } - - private bool HandleTrainCommand(SpeechEventArgs e) - { - if (!e.HasKeyword(0x6C)) // *train* - { - return false; - } - - HandleTraining(e.Mobile); - return true; - } - - public virtual void AllOnSpeechPet(SpeechEventArgs e) - { - if (!e.Mobile.InRange(Mobile.Location, 14)) - { - return; - } - - var isOwner = e.Mobile == Mobile.ControlMaster; - var isPetFriend = !isOwner && Mobile.IsPetFriend(e.Mobile); - - if (!isOwner && !isPetFriend) - { - return; - } - - var keyword = e.GetFirstKeyword( - 0x164, // all come - 0x165, // all follow - 0x166, // all guard - 0x167, // all stop - 0x168, // all kill - 0x169, // all attack - 0x16B, // all guard me - 0x16C, // all follow me - 0x170 // all stay - ); - - switch (keyword) - { - case 0x164: // all come - { - HandleComeCommand(e.Mobile, true); - break; - } - case 0x165: // all follow - { - BeginPickTarget(e.Mobile, OrderType.Follow); - break; - } - case 0x166: // all guard - case 0x16B: // all guard me - { - HandleGuardCommand(e.Mobile, true); - break; - } - case 0x167: // all stop - { - HandleStayStopFollowCommand(e.Mobile, OrderType.Stop); - break; - } - case 0x168: // all kill - case 0x169: // all attack - { - HandleAttackCommand(e.Mobile, true); - break; - } - case 0x16C: // all follow me - { - HandleStayStopFollowCommand(e.Mobile, OrderType.Follow, e.Mobile); - break; - } - case 0x170: // all stay - { - HandleStayStopFollowCommand(e.Mobile, OrderType.Stay); - break; - } - } - } - - public virtual void NamedOnSpeechPet(SpeechEventArgs e) - { - if (!e.Mobile.InRange(Mobile.Location, 14)) - { - return; - } - - var isOwner = e.Mobile == Mobile.ControlMaster; - var isPetFriend = !isOwner && Mobile.IsPetFriend(e.Mobile); - - if (!isOwner && !isPetFriend) - { - return; - } - - var keyword = e.GetFirstKeyword( - 0x155, // *come - 0x156, // *drop - 0x15A, // *follow - 0x15B, // *friend - 0x15C, // *guard - 0x15D, // *kill - 0x15E, // *attack - 0x161, // *stop - 0x163, // *follow me - 0x16D, // *release - 0x16E, // *transfer - 0x16F // *stay - ); - - switch (keyword) - { - case 0x155: // *come - { - HandleComeCommand(e.Mobile, true); - break; - } - case 0x156: // *drop - { - HandleDropCommand(e.Mobile, true, e.Speech); - break; - } - case 0x15A: // *follow - { - BeginPickTarget(e.Mobile, OrderType.Follow); - break; - } - case 0x15B: // *friend - { - HandleFriendCommand(e.Mobile, true, e.Speech); - break; - } - case 0x15C: // *guard - { - HandleGuardCommand(e.Mobile, true); - break; - } - case 0x15D: // *kill - case 0x15E: // *attack - { - HandleAttackCommand(e.Mobile, true); - break; - } - case 0x161: // *stop - { - HandleStayStopFollowCommand(e.Mobile, OrderType.Stop); - break; - } - case 0x163: // *follow me - { - HandleStayStopFollowCommand(e.Mobile, OrderType.Follow, e.Mobile); - break; - } - case 0x16D: // *release - { - HandleReleaseCommand(e.Mobile, true, e.Speech); - break; - } - case 0x16E: // *transfer - { - HandleTransferCommand(e.Mobile, true, e.Speech); - break; - } - case 0x16F: // *stay - { - HandleStayStopFollowCommand(e.Mobile, OrderType.Stay); - break; - } - } - } - - private void HandleTraining(Mobile from) - { - var foundSomething = false; - - foreach (var skill in Mobile.Skills) - { - if (skill.Base < 60.0 || !Mobile.CheckTeach(skill.SkillName, from)) - { - continue; - } - - var toTeach = Math.Min(skill.Base / 3.0, 42.0); - - if (toTeach <= from.Skills[skill.SkillName].Base) - { - continue; - } - - var number = 1043059 + (int)skill.SkillName; // alchemy - if (number > 1043107) // disarming traps - { - continue; - } - - if (!foundSomething) - { - Mobile.Say(1043058); // I can train the following: - foundSomething = true; - } - - Mobile.Say(number); - } - - if (!foundSomething) - { - Mobile.Say(501505); // Alas, I cannot teach thee anything. - } - } - - private void HandleComeCommand(Mobile from, bool isOwner) - { - if (isOwner && Mobile.CheckControlChance(from)) - { - _commandIssuer = from; - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Come; - } - } - - private void HandleGuardCommand(Mobile from, bool isOwner) - { - if (isOwner && Mobile.CheckControlChance(from)) - { - _commandIssuer = from; - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Guard; - } - } - - private void HandleStayStopFollowCommand(Mobile from, OrderType order, Mobile target = null) - { - if (Mobile.CheckControlChance(from)) - { - _commandIssuer = from; - Mobile.ControlTarget = target; - Mobile.ControlOrder = order; - } - } - - private void HandleAttackCommand(Mobile from, bool isOwner) - { - if (isOwner) - { - _commandIssuer = from; - BeginPickTarget(from, OrderType.Attack); - } - } - - private void HandleDropCommand(Mobile from, bool isOwner, string speech) - { - if (isOwner && !Mobile.IsDeadPet && !Mobile.Summoned && WasNamed(speech) - && Mobile.CheckControlChance(from)) - { - _commandIssuer = from; - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Drop; - } - } - - private void HandleFriendCommand(Mobile from, bool isOwner, string speech) - { - if (isOwner && WasNamed(speech) && Mobile.CheckControlChance(from)) - { - if (Mobile.Summoned || Mobile is GrizzledMare) - { - from.SendLocalizedMessage(1005481); - // Summoned creatures are loyal only to their summoners. - return; - } - - if (from.HasTrade) - { - from.SendLocalizedMessage(1070947); - // You cannot friend a pet with a trade pending - return; - } - - BeginPickTarget(from, OrderType.Friend); - } - } - - private void HandleReleaseCommand(Mobile from, bool isOwner, string speech) - { - if (!isOwner) - { - return; - } - - if (WasNamed(speech) && Mobile.CheckControlChance(from)) - { - if (!Mobile.Summoned) - { - from.SendGump(new ConfirmReleaseGump(from, Mobile)); - } - else - { - Mobile.ControlOrder = OrderType.Release; - } - } - } - - private void HandleTransferCommand(Mobile from, bool isOwner, string speech) - { - if (isOwner && !Mobile.IsDeadPet && WasNamed(speech) && Mobile.CheckControlChance(from)) - { - if (Mobile.Summoned || Mobile is GrizzledMare) - { - from.SendLocalizedMessage(1005487); - // You cannot transfer ownership of a summoned creature. - return; - } - - if (from.HasTrade) - { - from.SendLocalizedMessage(1010507); - // You cannot transfer a pet with a trade pending - return; - } - - BeginPickTarget(from, OrderType.Transfer); - } - } - - private void HandleGMCommands(SpeechEventArgs e) - { - this.DebugSayFormatted($"Command is from GM: {e.Mobile.Name}, Target: {Mobile.ControlTarget?.Name ?? "None or Unknown"}"); - - if (Mobile.FindMyName(e.Speech, true) && e.Speech.InsensitiveContains("obey")) - { - Mobile.SetControlMaster(e.Mobile); - - if (Mobile.Summoned) - { - Mobile.SummonMaster = e.Mobile; - } - } - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/PetLogin.cs b/Projects/UOContent/Mobiles/AI/BaseAI/PetLogin.cs deleted file mode 100644 index 446b25b21..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/PetLogin.cs +++ /dev/null @@ -1,50 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: PetLogin.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using ModernUO.CodeGeneratedEvents; - -namespace Server.Mobiles; - -public static class PetLoginHandler -{ - // Within this many tiles of the master we assume the pet was following; otherwise staying. - private const int FollowRange = 12; - - [OnEvent(nameof(PlayerMobile.PlayerLoginEvent))] - public static void OnLogin(PlayerMobile pm) => DeriveFollowerOrders(pm); - - // The persistent command is runtime-only and reset to None on load. When the master logs - // in we give each controlled pet that still has no standing command a sane one, inferred - // from proximity: near master -> Follow, otherwise Stay. - public static void DeriveFollowerOrders(PlayerMobile master) - { - if (master?.AllFollowers == null) - { - return; - } - - foreach (var follower in master.AllFollowers) - { - if (follower is BaseCreature { Controlled: true, Deleted: false } bc - && bc.ControlMaster == master - && bc.AIObject is { } ai - && ai.PersistentOrder == OrderType.None) - { - var near = bc.Map == master.Map && bc.GetDistanceToSqrt(master) <= FollowRange; - ai.SetPersistentOrder(near ? OrderType.Follow : OrderType.Stay); - } - } - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs b/Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs deleted file mode 100644 index ecb456d2e..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs +++ /dev/null @@ -1,271 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: PetOrderHandlers.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using System; - -namespace Server.Mobiles; - -public abstract partial class BaseAI -{ - public virtual void OnCurrentOrderChanged(OrderType previous) - { - if (Mobile.Deleted || Mobile.ControlMaster?.Deleted != false) - { - return; - } - - Activate(); - - switch (Mobile.ControlOrder) - { - case OrderType.None: - { - HandleNoOrder(); - break; - } - case OrderType.Come: - case OrderType.Drop: - case OrderType.Friend: - case OrderType.Unfriend: - { - break; - } - case OrderType.Release: - { - HandleReleaseOrder(); - break; - } - case OrderType.Stop: - { - // Stop is resolved into another order; it never rests as the active order. - ResolveStop(previous); - return; - } - case OrderType.Transfer: - { - HandleTransferOrder(); - break; - } - case OrderType.Stay: - { - HandleStayOrder(); - break; - } - case OrderType.Guard: - { - HandleGuardOrder(); - break; - } - case OrderType.Attack: - { - HandleAttackOrder(); - break; - } - case OrderType.Follow: - { - HandleFollowOrder(); - break; - } - case OrderType.Rename: - { - HandleRenameOrder(); - break; - } - } - - // A freshly issued standing command becomes the persistent fallback and (re)anchors - // Home. Skipped while resuming a fallback so a resume never re-anchors. See - // ResumePersistentOrder. - if (!_resolvingOrder && Mobile.ControlOrder is OrderType.Stay or OrderType.Follow or OrderType.Guard) - { - SetPersistentOrder(Mobile.ControlOrder); - } - } - - // "Stop" cancels the active order, mapping to a resting order based on what the pet was - // doing: Attack/Come/etc. -> resume the persistent command; Follow/Guard -> cancel to idle - // (None) where it stands; Stay -> remain staying at its post. - private void ResolveStop(OrderType previous) - { - _commandIssuer?.RevealingAction(); - _commandIssuer = null; - Mobile.ControlTarget = null; - - switch (previous) - { - case OrderType.Stay: - { - _resolvingOrder = true; - Mobile.ControlOrder = OrderType.Stay; // remain staying; anchor untouched - _resolvingOrder = false; - break; - } - case OrderType.Follow: - case OrderType.Guard: - { - SetPersistentOrder(OrderType.None); // cancel standing order; anchor = current - _resolvingOrder = true; - Mobile.ControlOrder = OrderType.None; // idle - _resolvingOrder = false; - break; - } - default: // Attack / Come / Drop / None / etc. -> resume the standing order - { - ResumePersistentOrder(); - break; - } - } - } - - private void HandleNoOrder() - { - Mobile.ControlTarget = null; - Mobile.FocusMob = null; - Mobile.Warmode = false; - Mobile.Combatant = null; - } - - private void HandleTransferOrder() - { - if (Mobile.ControlMaster?.Alive != true) - { - return; - } - - _commandIssuer?.RevealingAction(); - Mobile.FocusMob = null; - Mobile.Warmode = false; - Mobile.Combatant = null; - Mobile.PlaySound(Mobile.GetIdleSound()); - _commandIssuer = null; - } - - private void HandleGuardOrder() - { - if (Mobile.ControlMaster?.Alive != true) - { - return; - } - - _commandIssuer?.RevealingAction(); - Mobile.FocusMob = null; - Mobile.Warmode = true; - Mobile.PlaySound(Mobile.GetAttackSound()); - Mobile.ControlMaster?.SendLocalizedMessage(1049671, Mobile.Name); - // ~1_NAME~ is now guarding you. - _commandIssuer = null; - } - - private void HandleAttackOrder() - { - if (Mobile.ControlMaster?.Alive != true) - { - return; - } - - _commandIssuer?.RevealingAction(); - - if (Mobile.ControlTarget != null && - !Mobile.ControlTarget.Deleted && - Mobile.ControlTarget.Alive) - { - Mobile.FocusMob = Mobile.ControlTarget; - Mobile.Combatant = Mobile.ControlTarget; - } - else - { - Mobile.FocusMob = null; - Mobile.Combatant = null; - } - - Mobile.Warmode = true; - Mobile.PlaySound(Mobile.GetAttackSound()); - _commandIssuer = null; - } - - private void HandleFollowOrder() - { - if (Mobile.ControlMaster?.Alive != true) - { - return; - } - - _commandIssuer?.RevealingAction(); - Mobile.FocusMob = null; - Mobile.Warmode = false; - Mobile.Combatant = null; - Mobile.PlaySound(Mobile.GetIdleSound()); - _commandIssuer = null; - } - - private void HandleStayOrder() - { - if (Mobile.ControlMaster?.Alive != true) - { - return; - } - - _commandIssuer?.RevealingAction(); - Mobile.FocusMob = null; - Mobile.Warmode = false; - Mobile.Combatant = null; - Mobile.PlaySound(Mobile.GetIdleSound()); - _commandIssuer = null; - // Home (the stay anchor) is owned by SetPersistentOrder, not this handler. - } - - private void HandleReleaseOrder() - { - if (Mobile.ControlMaster?.Alive != true) - { - return; - } - - if (Mobile.Summoned) - { - Mobile.Kill(); - return; - } - - if (!string.IsNullOrEmpty(Mobile.Name)) - { - Mobile.Name = null; - } - - _commandIssuer?.RevealingAction(); - Mobile.ControlTarget = null; - Mobile.FocusMob = null; - Mobile.Warmode = false; - Mobile.Combatant = null; - Mobile.PlaySound(Mobile.GetIdleSound()); - Mobile.BondingBegin = DateTime.MinValue; - Mobile.OwnerAbandonTime = DateTime.MinValue; - Mobile.IsBonded = false; - Mobile.SetControlMaster(null); - _commandIssuer = null; - } - - public virtual void HandleRenameOrder() - { - if (Mobile.Summoned) - { - Mobile.ControlMaster?.SendMessage("You cannot rename a summoned creature."); - } - else - { - Mobile.ControlMaster?.SendMessage("Change name on pet health bar."); - } - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs b/Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs deleted file mode 100644 index 1b7139a96..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs +++ /dev/null @@ -1,580 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: PetOrders.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -namespace Server.Mobiles; - -public abstract partial class BaseAI -{ - // The standing command a pet falls back to when a transient order (Attack/Come/Drop) - // completes: None, Stay, Follow, or Guard. Runtime-only (not serialized); reset to None - // on load and derived from master proximity on login. See PetLoginHandler. - internal OrderType PersistentOrder { get; private set; } = OrderType.None; - - // Guards anchor/persistent derivation while we resume a fallback order, so a resume - // never re-derives the persistent command or re-anchors Home. See OnCurrentOrderChanged. - private bool _resolvingOrder; - - // The controlled-pet wander anchor (Home) is a pure function of the persistent command. - internal void SetPersistentOrder(OrderType order) - { - PersistentOrder = order; - Mobile.Home = order is OrderType.Follow or OrderType.Guard ? Point3D.Zero : Mobile.Location; - } - - // Resume the persistent command without re-deriving the persistent order or anchor. - private void ResumePersistentOrder() - { - _resolvingOrder = true; - Mobile.ControlOrder = PersistentOrder; - _resolvingOrder = false; - } - - public virtual bool Obey() => - !Mobile.Deleted && Mobile.ControlOrder switch - { - OrderType.None => DoOrderNone(), - OrderType.Come => DoOrderCome(), - OrderType.Drop => DoOrderDrop(), - OrderType.Friend => DoOrderFriend(), - OrderType.Unfriend => DoOrderUnfriend(), - OrderType.Guard => DoOrderGuard(), - OrderType.Attack => DoOrderAttack(), - OrderType.Release => DoOrderRelease(), - OrderType.Stay => DoOrderStay(), - OrderType.Stop => DoOrderStop(), - OrderType.Follow => DoOrderFollow(), - OrderType.Transfer => DoOrderTransfer(), - _ => false - }; - - public virtual bool DoOrderNone() - { - DebugSay("I currently have no orders."); - - Mobile.Warmode = IsValidCombatant(Mobile.Combatant); - - // Pure idle: gently wander near the anchor, with CheckIdle rest periods. Pets resume - // a standing order via ResumePersistentOrder, not by re-deriving it here. - WalkRandomIdle(); - return true; - } - - public virtual bool DoOrderCome() - { - if (CheckHerding()) - { - this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); - return true; - } - - if (Mobile.ControlMaster?.Deleted != false) - { - return true; - } - - WalkMobileRange(Mobile.ControlMaster, 1, false, 1, 2); - - if (Mobile.GetDistanceToSqrt(Mobile.ControlMaster) <= 2) - { - Mobile.ControlOrder = OrderType.Stay; - } - - return true; - } - - public virtual bool DoOrderFollow() - { - if (CheckHerding()) - { - this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); - return true; - } - - if (Mobile.ControlTarget?.Deleted == false && Mobile.ControlTarget != Mobile) - { - FollowTarget(); - } - else - { - DebugSay("I have no one to follow."); - - Mobile.ControlOrder = OrderType.None; - } - - return true; - } - - private void FollowTarget() - { - var currentDistance = (int)Mobile.GetDistanceToSqrt(Mobile.ControlTarget); - - if (currentDistance > Mobile.RangePerception) - { - this.DebugSayFormatted($"Master {Mobile.ControlMaster?.Name ?? "Unknown"} is missing. Staying put."); - return; - } - - this.DebugSayFormatted($"I am ordered to follow {Mobile.ControlTarget?.Name}."); - - if (currentDistance > 1) - { - WalkMobileRange(Mobile.ControlTarget, 1, currentDistance > 2, 1, 2); - } - } - - public virtual bool DoOrderDrop() - { - if (Mobile.IsDeadPet || !Mobile.CanDrop) - { - return true; - } - - this.DebugSayFormatted($"I am ordered to drop my items by {Mobile.ControlMaster?.Name ?? "Unknown"}."); - - DropItems(); - ResumePersistentOrder(); - return true; - } - - private void DropItems() - { - var pack = Mobile.Backpack; - - if (pack == null) - { - return; - } - - var items = pack.Items; - - for (var i = items.Count - 1; i >= 0; --i) - { - if (i < items.Count) - { - items[i].MoveToWorld(Mobile.Location, Mobile.Map); - } - } - } - - public virtual bool DoOrderFriend() - { - var from = Mobile.ControlMaster; - var to = Mobile.ControlTarget; - - HandleFriendRequest(from, to); - return true; - } - - private void HandleFriendRequest(Mobile from, Mobile to) - { - var youngFrom = from is PlayerMobile mobile && mobile.Young; - var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; - - if (youngFrom && !youngTo) - { - from.SendLocalizedMessage(502040); - // As a young player, you may not friend pets to older players. - return; - } - - if (!youngFrom && youngTo) - { - from.SendLocalizedMessage(502041); - // As an older player, you may not friend pets to young players. - return; - } - - if (!from.CanBeBeneficial(to, true)) - { - return; - } - - if (to?.Deleted != false || from == to || !to.Player) - { - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); - // *looks confused* - return; - } - - if (from.HasTrade || to.HasTrade) - { - (from.HasTrade ? from : to).SendLocalizedMessage(1070947); - // You cannot friend a pet with a trade pending - return; - } - - if (Mobile.IsPetFriend(to)) - { - from.SendLocalizedMessage(1049691); - // That person is already a friend. - ResumePersistentOrder(); - return; - } - - if (!Mobile.AllowNewPetFriend) - { - from.SendLocalizedMessage(1005482); - // Your pet does not seem to be interested in making new friends right now. - return; - } - - from.SendLocalizedMessage(1049676, $"{Mobile.Name}\t{to.Name}"); - // ~1_NAME~ will now accept movement commands from ~2_NAME~. - - to.SendLocalizedMessage(1043246, $"{from.Name}\t{Mobile.Name}"); - // ~1_NAME~ has granted you the ability to give orders to their pet ~2_PET_NAME~. - // This creature will now consider you as a friend. - - Mobile.AddPetFriend(to); - - Mobile.ControlTarget = to; - Mobile.ControlOrder = OrderType.Follow; - } - - public virtual bool DoOrderUnfriend() - { - var from = Mobile.ControlMaster; - var to = Mobile.ControlTarget; - - HandleUnfriendRequest(from, to); - return true; - } - - private void HandleUnfriendRequest(Mobile from, Mobile to) - { - if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) - { - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); - // *looks confused* - return; - } - - if (!Mobile.IsPetFriend(to)) - { - from.SendLocalizedMessage(1070953); - // That person is not a friend. - ResumePersistentOrder(); - return; - } - - from.SendLocalizedMessage(1070951, $"{Mobile.Name}\t{to.Name}"); - // ~1_NAME~ will no longer accept movement commands from ~2_NAME~. - - to.SendLocalizedMessage(1070952, $"{from.Name}\t{Mobile.Name}"); - // ~1_NAME~ has no longer granted you the ability to give orders to their pet ~2_PET_NAME~. - // This creature will no longer consider you as a friend. - - Mobile.RemovePetFriend(to); - - Mobile.ControlTarget = from; - Mobile.ControlOrder = OrderType.Follow; - } - - public virtual bool DoOrderGuard() - { - var controlMaster = Mobile.ControlMaster; - - if (Mobile.IsDeadPet || controlMaster?.Deleted != false) - { - return true; - } - - FindCombatant(); - - if (IsValidCombatant(Mobile.Combatant)) - { - var combatant = Mobile.Combatant; - - this.DebugSayFormatted($"Attacking target: {combatant.Name}"); - - Mobile.Combatant = combatant; - Mobile.FocusMob = combatant; - Action = ActionType.Combat; - - Think(); - } - else - { - this.DebugSayFormatted($"Guarding my master, {controlMaster.Name}."); - - var guardLocation = controlMaster.Location; - - var distance = (int)Mobile.GetDistanceToSqrt(guardLocation); - - if (distance > 3) - { - DoMove(Mobile.GetDirectionTo(guardLocation)); - } - else - { - WalkRandom(3, 1, 1); - } - } - - return true; - } - - public virtual bool DoOrderAttack() - { - if (Mobile.IsDeadPet) - { - return true; - } - - if (IsInvalidControlTarget(Mobile.ControlTarget)) - { - HandleInvalidControlTarget(); - } - else - { - Mobile.Combatant = Mobile.ControlTarget; - - this.DebugSayFormatted($"Attacking target: {Mobile.ControlTarget?.Name}"); - - Think(); - } - - return true; - } - - private bool IsInvalidControlTarget(Mobile target) => target?.Deleted != false || target.Map != Mobile.Map - || !target.Alive || target.IsDeadBondedPet || target.Hidden; - - private void HandleInvalidControlTarget() - { - DebugSay("Target is either dead, hidden, or out of range."); - - Mobile.ControlTarget = Mobile.ControlMaster; - ResumePersistentOrder(); - - if (Mobile.FightMode is FightMode.Closest or FightMode.Aggressor) - { - FindCombatant(); - } - } - - private void FindCombatant() - { - var controlMaster = Mobile.ControlMaster; - - foreach (var aggr in Mobile.GetMobilesInRange(Mobile.RangePerception)) - { - if (!Mobile.CanSee(aggr) || aggr.IsDeadBondedPet || !aggr.Alive) - { - continue; - } - - var isAttackingPet = aggr.Combatant == Mobile; - var isAttackingMaster = controlMaster != null && aggr.Combatant == controlMaster; - - if (isAttackingPet || isAttackingMaster) - { - if (Mobile.InLOS(aggr)) - { - Mobile.ControlTarget = aggr; - Mobile.ControlOrder = OrderType.Attack; - Mobile.Combatant = aggr; - - var target = isAttackingMaster ? "master" : "me"; - this.DebugSayFormatted($"{aggr.Name} is attacking my {target}! Engaging..."); - - Think(); - return; - } - } - } - - if (controlMaster?.Aggressors != null) - { - for (var i = 0; i < controlMaster.Aggressors.Count; i++) - { - var aggressor = controlMaster.Aggressors[i].Attacker; - - if (aggressor?.Deleted != false || !aggressor.Alive || aggressor.IsDeadBondedPet) - { - continue; - } - - if (Mobile.InRange(aggressor, Mobile.RangePerception) && Mobile.CanSee(aggressor) && Mobile.InLOS(aggressor)) - { - Mobile.ControlTarget = aggressor; - Mobile.ControlOrder = OrderType.Attack; - Mobile.Combatant = aggressor; - - this.DebugSayFormatted($"{aggressor.Name} recently attacked my master! Retaliating..."); - - Think(); - return; - } - } - } - } - - public virtual bool DoOrderRelease() - { - DebugSay("I have been released to the wild."); - - var spawner = Mobile.Spawner; - - if (spawner != null) - { - Mobile.Home = spawner.GetSpawnPosition(Mobile, spawner.Map); - Mobile.RangeHome = spawner.WalkingRange; - } - else - { - // No spawner to return to: anchor where it stands so it idle-wanders here - // instead of pathing toward a stale (e.g. former stay) anchor. - Mobile.Home = Mobile.Location; - Action = ActionType.Wander; - } - - if (Mobile.DeleteOnRelease || Mobile.IsDeadPet) - { - Mobile.Delete(); - } - else - { - Mobile.BeginDeleteTimer(); - - if (Mobile.CanDrop) - { - Mobile.DropBackpack(); - } - } - - return true; - } - - public virtual bool DoOrderStay() - { - if (CheckHerding()) - { - this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); - } - else - { - this.DebugSayFormatted($"I have been ordered to stay by {Mobile.ControlMaster?.Name ?? "Unknown"}."); - } - - // Hold position at the post (Home). Stand still when there; only walk back if displaced - // (e.g. after chasing a kill). No idle shuffle. - if (Mobile.Home != Point3D.Zero && Mobile.Location != Mobile.Home) - { - DoMove(Mobile.GetDirectionTo(Mobile.Home)); - } - - return true; - } - - // Stop is resolved into another order in OnCurrentOrderChanged and never rests as the - // active order; this is a defensive no-op. - public virtual bool DoOrderStop() => true; - - public virtual bool DoOrderTransfer() - { - if (Mobile.IsDeadPet) - { - return true; - } - - var from = Mobile.ControlMaster; - var to = Mobile.ControlTarget; - - if (from?.Deleted == false && to?.Deleted == false && from != to && to.Player) - { - this.DebugSayFormatted($"Beginning transfer with {to.Name}"); - - var youngFrom = from is PlayerMobile mobile && mobile.Young; - var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; - - if (youngFrom && !youngTo) - { - from.SendLocalizedMessage(502040); - // As a young player, you may not friend pets to older players. - ResumePersistentOrder(); - return true; - } - - if (!youngFrom && youngTo) - { - from.SendLocalizedMessage(502041); - // As an older player, you may not friend pets to young players. - ResumePersistentOrder(); - return true; - } - - if (!Mobile.CanBeControlledBy(to)) - { - SendTransferRefusalMessages(from, to, 1043248, 1043249); - // 1043248: The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ - // 1043249: The pet will not accept you as a master because it does not trust you.~3_BLANK~ - ResumePersistentOrder(); - return true; - } - - if (!Mobile.CanBeControlledBy(from)) - { - SendTransferRefusalMessages(from, to, 1043250, 1043251); - // 1043250: The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ - // 1043251: The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ - ResumePersistentOrder(); - return true; - } - - if (Mobile.Combatant != null || Mobile.Aggressors.Count > 0 || - Mobile.Aggressed.Count > 0 || Core.TickCount < Mobile.NextCombatTime) - { - from.SendMessage("You can not transfer a pet while in combat."); - to.SendMessage("You can not transfer a pet while in combat."); - ResumePersistentOrder(); - return true; - } - - var fromState = from.NetState; - var toState = to.NetState; - - if (fromState == null || toState == null) - { - ResumePersistentOrder(); - return true; - } - - if (from.HasTrade || to.HasTrade) - { - from.SendLocalizedMessage(1010507); - // You cannot transfer a pet with a trade pending - to.SendLocalizedMessage(1010507); - // You cannot transfer a pet with a trade pending - ResumePersistentOrder(); - return true; - } - - var container = fromState.AddTrade(toState); - container.DropItem(new TransferItem(Mobile)); - } - - Mobile.ControlOrder = OrderType.Stay; - return true; - } - - private static void SendTransferRefusalMessages(Mobile from, Mobile to, int fromMessage, int toMessage) - { - var args = $"{to.Name}\t{from.Name}\t "; - - from.SendLocalizedMessage(fromMessage, args); - to.SendLocalizedMessage(toMessage, args); - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/SpeechEventArgsExt.cs b/Projects/UOContent/Mobiles/AI/BaseAI/SpeechEventArgsExt.cs deleted file mode 100644 index 35850d9bc..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/SpeechEventArgsExt.cs +++ /dev/null @@ -1,35 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SpeechEventArgsExt.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using System; - -namespace Server; - -public static class SpeechEventArgsExt -{ - public static int GetFirstKeyword(this SpeechEventArgs e, params ReadOnlySpan keywords) - { - for (var i = 0; i < keywords.Length; i++) - { - var keyword = keywords[i]; - if (e.HasKeyword(keyword)) - { - return keyword; - } - } - - return 0; - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs b/Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs deleted file mode 100644 index 9efad5a5e..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs +++ /dev/null @@ -1,177 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: TransferItem.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using ModernUO.Serialization; - -namespace Server.Mobiles; - -[SerializationGenerator(0, false)] -internal sealed partial class TransferItem : Item -{ - private readonly BaseCreature _creature; - - public override string DefaultName => _creature.GetType().Name; - - public TransferItem(BaseCreature creature) : base(ShrinkTable.Lookup(creature)) - { - _creature = creature; - Movable = false; - - Hue = creature.Hue & 0x0FFF; - } - - public override bool SkipSerialization => true; - - public static bool IsInCombat(BaseCreature creature) => creature?.Aggressors.Count > 0 || creature?.Aggressed.Count > 0; - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - list.Add(1041603); // This item represents a pet currently in consideration for trade - list.Add(1041601, _creature.Name); // Pet Name: ~1_val~ - - if (_creature.ControlMaster != null) - { - list.Add(1041602, _creature.ControlMaster.Name); // Owner: ~1_val~ - } - } - - public override bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) - { - if (!base.AllowSecureTrade(from, to, newOwner, accepted) || IsInvalidTrade(from, to)) - { - return false; - } - - return !accepted || HandleAcceptedTrade(from, to); - } - - private bool IsInvalidTrade(Mobile from, Mobile to) => - Deleted - || _creature?.Deleted != false - || _creature.ControlMaster != from - || !from.CheckAlive() - || !to.CheckAlive() - || from.Map != _creature.Map - || !from.InRange(_creature, 14); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool HandleAcceptedTrade(Mobile from, Mobile to) => - ValidateYoungStatus(from, to) && ValidateControlStatus(from, to) && ValidateFollowerLimit(to) && - !IsInCombat(_creature); - - private bool ValidateFollowerLimit(Mobile to) - { - if (to.Followers + _creature.ControlSlots > to.FollowersMax) - { - to.SendLocalizedMessage(1049607); - // You have too many followers to control that creature. - return false; - } - - return true; - } - - private static bool ValidateYoungStatus(Mobile from, Mobile to) - { - var youngFrom = from is PlayerMobile mobile && mobile.Young; - var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; - - if (youngFrom && !youngTo) - { - from.SendLocalizedMessage(502051); - // As a young player, you may not transfer pets to older players. - return false; - } - - if (!youngFrom && youngTo) - { - from.SendLocalizedMessage(502052); - // As an older player, you may not transfer pets to young players. - return false; - } - - return true; - } - - private bool ValidateControlStatus(Mobile from, Mobile to) - { - if (!_creature.CanBeControlledBy(to)) - { - SendTransferRefusalMessages(from, to, 1043248, 1043249); - // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ - // The pet will not accept you as a master because it does not trust you.~3_BLANK~ - return false; - } - - if (!_creature.CanBeControlledBy(from)) - { - SendTransferRefusalMessages(from, to, 1043250, 1043251); - // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ - // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ - return false; - } - - return true; - } - - private static void SendTransferRefusalMessages(Mobile from, Mobile to, int fromMessage, int toMessage) - { - var args = $"{to.Name}\t{from.Name}\t "; - from.SendLocalizedMessage(fromMessage, args); - to.SendLocalizedMessage(toMessage, args); - } - - public override void OnSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) - { - if (Deleted || IsInvalidTrade(from, to)) - { - Delete(); - return; - } - - Delete(); - - if (!accepted || !_creature.SetControlMaster(to)) - { - return; - } - - TransferPetOwnership(from, to); - } - - private void TransferPetOwnership(Mobile from, Mobile to) - { - if (_creature.Summoned) - { - _creature.SummonMaster = to; - } - - _creature.ControlTarget = to; - _creature.ControlOrder = OrderType.Follow; - _creature.BondingBegin = DateTime.MinValue; - _creature.OwnerAbandonTime = DateTime.MinValue; - _creature.IsBonded = false; - _creature.PlaySound(_creature.GetIdleSound()); - - var args = $"{from.Name}\t{_creature.Name}\t{to.Name}"; - from.SendLocalizedMessage(1043253, args); - // You have transferred your pet to ~3_GETTER~. - to.SendLocalizedMessage(1043252, args); - // ~1_NAME~ has transferred the allegiance of ~2_PET_NAME~ to you. - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/WalkRandomLogic.cs b/Projects/UOContent/Mobiles/AI/BaseAI/WalkRandomLogic.cs deleted file mode 100644 index 9ac53b96d..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI/WalkRandomLogic.cs +++ /dev/null @@ -1,140 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: WalkRandomLogic.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ************************************************************************/ - -using System; -using Server.Engines.Spawners; - -namespace Server.Mobiles; - -public abstract partial class BaseAI -{ - public virtual void WalkRandom(int chanceToNotMove, int chanceToDir, int steps) - { - if (Mobile.Deleted || Mobile.DisallowAllMoves || chanceToNotMove <= 0) - { - return; - } - - var maxSteps = Math.Min(steps, 3); - - for (var i = 0; i < maxSteps; i++) - { - if (Utility.Random(1 + chanceToNotMove) == 0) - { - DoMove(GetRandomDirection(chanceToDir)); - } - } - } - - // Idle wander for controlled pets. Routes through the same CheckMove/CanMoveNow/CheckIdle - // gate that non-controlled DoActionWander uses, so idling pets take the gentle 15-25s - // CheckIdle rest periods instead of shuffling every AI tick. - public void WalkRandomIdle() - { - if (CheckMove() && CanMoveNow(out _) && !Mobile.CheckIdle()) - { - WalkRandomInHome(3, 2, 1); - } - } - - public virtual void WalkRandomInHome(int chanceToNotMove, int chanceToDir, int steps) - { - if (Mobile.Deleted || Mobile.DisallowAllMoves) - { - return; - } - - if (Mobile.Home == Point3D.Zero) - { - WalkRandomNoHome(chanceToNotMove, chanceToDir, steps); - } - else - { - WalkRandomWithHome(chanceToNotMove, chanceToDir, steps); - } - } - - private void WalkRandomNoHome(int chanceToNotMove, int chanceToDir, int steps) - { - if (Mobile.Spawner is RegionSpawner rs) - { - var region = rs.SpawnRegion; - - if (Mobile.Region.AcceptsSpawnsFrom(region)) - { - Mobile.WalkRegion = region; - - WalkRandom(chanceToNotMove, chanceToDir, steps); - - Mobile.WalkRegion = null; - } - else if (region.GoLocation != Point3D.Zero && Utility.RandomBool()) - { - DoMove(Mobile.GetDirectionTo(region.GoLocation)); - } - else - { - WalkRandom(chanceToNotMove, chanceToDir, 1); - } - } - else - { - WalkRandom(chanceToNotMove, chanceToDir, steps); - } - } - - private void WalkRandomWithHome(int chanceToNotMove, int chanceToDir, int steps) - { - if (Mobile.RangeHome == 0) - { - if (Mobile.Location != Mobile.Home) - { - DoMove(Mobile.GetDirectionTo(Mobile.Home)); - } - - return; - } - - for (var i = 0; i < steps; i++) - { - var currDist = (int)Mobile.GetDistanceToSqrt(Mobile.Home); - - if (currDist > Mobile.RangeHome) - { - DoMove(Mobile.GetDirectionTo(Mobile.Home)); - } - else if (currDist < Mobile.RangeHome * 2 / 3 || Utility.Random(10) <= 5) - { - WalkRandom(chanceToNotMove, chanceToDir, 1); - } - else - { - DoMove(Mobile.GetDirectionTo(Mobile.Home)); - } - } - } - - private Direction GetRandomDirection(int chanceToDir) - { - var randomMove = Utility.Random(8 * (chanceToDir + 1)); - - if (randomMove < 8) - { - return (Direction)randomMove; - } - - return Mobile.Direction; - } -} diff --git a/Projects/UOContent/Mobiles/AI/BerserkAI.cs b/Projects/UOContent/Mobiles/AI/BerserkAI.cs index 4663ae8d0..db7a994ba 100644 --- a/Projects/UOContent/Mobiles/AI/BerserkAI.cs +++ b/Projects/UOContent/Mobiles/AI/BerserkAI.cs @@ -8,13 +8,19 @@ public class BerserkAI : BaseAI public override bool DoActionWander() { - DebugSay("I have no combatant"); - - if (AcquireFocusMob(Mobile.RangePerception, FightMode.Closest, false, true, true)) + if (m_Mobile.Debug) { - this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name} and I will attack"); + m_Mobile.DebugSay("I have no combatant"); + } - Mobile.Combatant = Mobile.FocusMob; + if (AcquireFocusMob(m_Mobile.RangePerception, FightMode.Closest, false, true, true)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name} and I will attack"); + } + + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } else @@ -27,37 +33,49 @@ public class BerserkAI : BaseAI public override bool DoActionCombat() { - var combatant = Mobile.Combatant; + var combatant = m_Mobile.Combatant; - if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || + if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - DebugSay("My combatant is gone, so my guard is up"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + } Action = ActionType.Guard; return true; } - if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) { - this.DebugSayFormatted($"I am still not in range of {combatant.Name}"); - - if ((int)Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) + if (m_Mobile.Debug) { - this.DebugSayFormatted($"I have lost {combatant.Name}"); + m_Mobile.DebugSay($"I am still not in range of {combatant.Name}"); + } + + if ((int)m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have lost {combatant.Name}"); + } Action = ActionType.Guard; return true; } } - else if (Core.TickCount - Mobile.LastMoveTime > 400) + else if (Core.TickCount - m_Mobile.LastMoveTime > 400) { - Mobile.Direction = Mobile.GetDirectionTo(combatant); + m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); } - if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); + } } return true; @@ -65,11 +83,14 @@ public class BerserkAI : BaseAI public override bool DoActionGuard() { - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, true, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, true, true)) { - this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + } - Mobile.Combatant = Mobile.FocusMob; + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } else diff --git a/Projects/UOContent/Mobiles/AI/HealerAI.cs b/Projects/UOContent/Mobiles/AI/HealerAI.cs index 2a1127b43..9a08703ea 100644 --- a/Projects/UOContent/Mobiles/AI/HealerAI.cs +++ b/Projects/UOContent/Mobiles/AI/HealerAI.cs @@ -1,4 +1,3 @@ -using System; using Server.Spells; using Server.Spells.First; using Server.Spells.Fourth; @@ -9,10 +8,13 @@ namespace Server.Mobiles; public class HealerAI : BaseAI { - private static readonly NeedDelegate[] Cure = [NeedCure]; - private static readonly NeedDelegate[] GHeal = [NeedGHeal]; - private static readonly NeedDelegate[] LHeal = [NeedLHeal]; - private static readonly NeedDelegate[] AllHeal = [NeedCure, NeedGHeal, NeedLHeal]; + private static readonly NeedDelegate m_Cure = NeedCure; + private static readonly NeedDelegate m_GHeal = NeedGHeal; + private static readonly NeedDelegate m_LHeal = NeedLHeal; + private static readonly NeedDelegate[] m_ACure = { m_Cure }; + private static readonly NeedDelegate[] m_AGHeal = { m_GHeal }; + private static readonly NeedDelegate[] m_ALHeal = { m_LHeal }; + private static readonly NeedDelegate[] m_All = { m_Cure, m_GHeal, m_LHeal }; public HealerAI(BaseCreature m) : base(m) { @@ -20,73 +22,93 @@ public class HealerAI : BaseAI public override bool Think() { - if (Mobile.Deleted) + if (m_Mobile.Deleted) { return false; } - var targ = Mobile.Target; + var targ = m_Mobile.Target; if (targ != null) { var spellTarg = targ as ISpellTarget; - var funcs = spellTarg?.Spell switch + if (spellTarg?.Spell is CureSpell) { - CureSpell => Cure, - GreaterHealSpell => GHeal, - HealSpell => LHeal, - _ => null - }; + ProcessTarget(targ, m_ACure); + } + else if (spellTarg?.Spell is GreaterHealSpell) + { + ProcessTarget(targ, m_AGHeal); + } + else if (spellTarg?.Spell is HealSpell) + { + ProcessTarget(targ, m_ALHeal); + } + else + { + targ.Cancel(m_Mobile, TargetCancelType.Canceled); + } - ProcessTarget(targ, funcs); return true; } - var toHelp = Find(AllHeal); + var toHelp = Find(m_All); if (toHelp != null) { if (NeedCure(toHelp)) { - this.DebugSayFormatted($"{toHelp.Name} needs a cure"); - - if (!new CureSpell(Mobile).Cast()) + if (m_Mobile.Debug) { - new CureSpell(Mobile).Cast(); + m_Mobile.DebugSay($"{toHelp.Name} needs a cure"); + } + + if (!new CureSpell(m_Mobile).Cast()) + { + new CureSpell(m_Mobile).Cast(); } } else if (NeedGHeal(toHelp)) { - this.DebugSayFormatted($"{toHelp.Name} needs a greater heal"); - - if (!new GreaterHealSpell(Mobile).Cast()) + if (m_Mobile.Debug) { - new HealSpell(Mobile).Cast(); + m_Mobile.DebugSay($"{toHelp.Name} needs a greater heal"); + } + + if (!new GreaterHealSpell(m_Mobile).Cast()) + { + new HealSpell(m_Mobile).Cast(); } } else if (NeedLHeal(toHelp)) { - this.DebugSayFormatted($"{toHelp.Name} needs a lesser heal"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"{toHelp.Name} needs a lesser heal"); + } - new HealSpell(Mobile).Cast(); + new HealSpell(m_Mobile).Cast(); } return true; } - if (!AcquireFocusMob(Mobile.RangePerception, FightMode.Weakest, false, true, false)) + if (!AcquireFocusMob(m_Mobile.RangePerception, FightMode.Weakest, false, true, false)) { WalkRandomInHome(3, 2, 1); return true; } - WalkMobileRange(Mobile.FocusMob, 1, false, 4, 7); + WalkMobileRange(m_Mobile.FocusMob, 1, false, 4, 7); // TODO: Should it be able to do this? - if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, Mobile.Combatant)) + if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, m_Mobile.Combatant)) { - this.DebugSayFormatted($"I used my abilities on {Mobile.Combatant.Name}!"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I used my abilities on {m_Mobile.Combatant.Name}!"); + } } return true; @@ -94,36 +116,30 @@ public class HealerAI : BaseAI private void ProcessTarget(Target targ, NeedDelegate[] func) { - if (func == null || func.Length == 0) - { - targ.Cancel(Mobile, TargetCancelType.Canceled); - return; - } - var toHelp = Find(func); if (toHelp == null) { - targ.Cancel(Mobile, TargetCancelType.Canceled); + targ.Cancel(m_Mobile, TargetCancelType.Canceled); } - else if (targ.Range != -1 && !Mobile.InRange(toHelp, targ.Range)) + else if (targ.Range != -1 && !m_Mobile.InRange(toHelp, targ.Range)) { - DoMove(Mobile.GetDirectionTo(toHelp) | Direction.Running); + DoMove(m_Mobile.GetDirectionTo(toHelp) | Direction.Running); } else { - targ.Invoke(Mobile, toHelp); + targ.Invoke(m_Mobile, toHelp); } } - private Mobile Find(params ReadOnlySpan funcs) + private Mobile Find(params NeedDelegate[] funcs) { - if (Mobile.Deleted) + if (m_Mobile.Deleted) { return null; } - var map = Mobile.Map; + var map = m_Mobile.Map; if (map == null) { @@ -133,9 +149,9 @@ public class HealerAI : BaseAI var prio = 0.0; Mobile found = null; - foreach (var m in Mobile.GetMobilesInRange(Mobile.RangePerception)) + foreach (var m in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) { - if (!Mobile.CanSee(m) || m is not BaseCreature bc || bc.Team != Mobile.Team) + if (!m_Mobile.CanSee(m) || m is not BaseCreature bc || bc.Team != m_Mobile.Team) { continue; } @@ -144,7 +160,7 @@ public class HealerAI : BaseAI { if (funcs[i](bc)) { - var val = -Mobile.GetDistanceToSqrt(bc); + var val = -m_Mobile.GetDistanceToSqrt(bc); if (found == null || val > prio) { diff --git a/Projects/UOContent/Mobiles/AI/MageAI.cs b/Projects/UOContent/Mobiles/AI/MageAI.cs index 61be59a1f..9a323f812 100644 --- a/Projects/UOContent/Mobiles/AI/MageAI.cs +++ b/Projects/UOContent/Mobiles/AI/MageAI.cs @@ -20,11 +20,8 @@ public class MageAI : BaseAI private const double DispelChance = 0.75; // 75% chance to dispel at gm magery private const double InvisChance = 0.50; // 50% chance to invis at gm magery - public override double FleeHealthThreshold => 0.2; // 20% is default - public override double FleeChance => 0.1; // 10% is default - - private static readonly int[] Offsets = - [ + private static readonly int[] m_Offsets = + { -1, -1, -1, 0, -1, 1, @@ -50,28 +47,29 @@ public class MageAI : BaseAI 2, 0, 2, 1, 2, 2 - ]; + }; - protected int _combo = -1; + protected int m_Combo = -1; - private Mobile _lastTarget; - private Point3D _lastTargetLoc; - private long _nextCastTime; - private long _nextHealTime; + private Mobile m_LastTarget; + private Point3D m_LastTargetLoc; + private long m_NextCastTime; + private long m_NextHealTime; - private LandTarget _revealTarget; + private LandTarget m_RevealTarget; - public MageAI(BaseCreature m) : base(m) + public MageAI(BaseCreature m) + : base(m) { } - public virtual bool SmartAI => Mobile is BaseVendor or BaseEscortable or Changeling; + public virtual bool SmartAI => m_Mobile is BaseVendor or BaseEscortable or Changeling; - public virtual bool IsNecromancer => Core.AOS && Mobile.Skills.Necromancy.Value > 50; + public virtual bool IsNecromancer => Core.AOS && m_Mobile.Skills.Necromancy.Value > 50; public override bool Think() { - if (Mobile.Deleted) + if (m_Mobile.Deleted) { return false; } @@ -79,29 +77,38 @@ public class MageAI : BaseAI return ProcessTarget() || base.Think(); } - public virtual double ScaleBySkill(double v, SkillName skill) => v * Mobile.Skills[skill].Value / 100; + public virtual double ScaleBySkill(double v, SkillName skill) => v * m_Mobile.Skills[skill].Value / 100; public override bool DoActionWander() { - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - this.DebugSayFormatted($"I am going to attack {Mobile.FocusMob.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to attack {m_Mobile.FocusMob.Name}"); + } - Mobile.Combatant = Mobile.FocusMob; + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; - _nextCastTime = Core.TickCount; + m_NextCastTime = Core.TickCount; } - else if (SmartAI && Mobile.Mana < Mobile.ManaMax && !Mobile.Meditating) + else if (SmartAI && m_Mobile.Mana < m_Mobile.ManaMax && !m_Mobile.Meditating) { - DebugSay("I am going to meditate"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am going to meditate"); + } - Mobile.UseSkill(SkillName.Meditation); + m_Mobile.UseSkill(SkillName.Meditation); } else { - DebugSay("I am wandering"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am wandering"); + } - Mobile.Warmode = false; + m_Mobile.Warmode = false; base.DoActionWander(); @@ -119,13 +126,13 @@ public class MageAI : BaseAI private Spell CheckCastHealingSpell() { // If I'm poisoned, always attempt to cure. - if (Mobile.Poisoned) + if (m_Mobile.Poisoned) { - return new CureSpell(Mobile); + return new CureSpell(m_Mobile); } // Summoned creatures never heal themselves. - if (Mobile.Summoned || Mobile.Controlled && Core.TickCount - _nextHealTime < 0) + if (m_Mobile.Summoned || m_Mobile.Controlled && Core.TickCount - m_NextHealTime < 0) { return null; } @@ -137,32 +144,32 @@ public class MageAI : BaseAI return null; } } - else if (Utility.Random(0, 4 + (Mobile.Hits == 0 ? Mobile.HitsMax : Mobile.HitsMax / Mobile.Hits)) < 3) + else if (Utility.Random(0, 4 + (m_Mobile.Hits == 0 ? m_Mobile.HitsMax : m_Mobile.HitsMax / m_Mobile.Hits)) < 3) { return null; } Spell spell = null; - if (Mobile.Hits < Mobile.HitsMax - 50) + if (m_Mobile.Hits < m_Mobile.HitsMax - 50) { if (UseNecromancy()) { - Mobile.UseSkill(SkillName.SpiritSpeak); + m_Mobile.UseSkill(SkillName.SpiritSpeak); } else { - spell = new GreaterHealSpell(Mobile); + spell = new GreaterHealSpell(m_Mobile); } } - else if (Mobile.Hits < Mobile.HitsMax - 10) + else if (m_Mobile.Hits < m_Mobile.HitsMax - 10) { - spell = new HealSpell(Mobile); + spell = new HealSpell(m_Mobile); } - var delay = Mobile.Int >= 500 ? Utility.RandomMinMax(7, 10) : Math.Sqrt(600 - Mobile.Int); + var delay = m_Mobile.Int >= 500 ? Utility.RandomMinMax(7, 10) : Math.Sqrt(600 - m_Mobile.Int); - _nextHealTime = Core.TickCount + (int)TimeSpan.FromSeconds(delay).TotalMilliseconds; + m_NextHealTime = Core.TickCount + (int)TimeSpan.FromSeconds(delay).TotalMilliseconds; return spell; } @@ -171,7 +178,7 @@ public class MageAI : BaseAI { if (!SmartAI) { - if (!MoveTo(m, false, Mobile.RangeFight)) + if (!MoveTo(m, true, m_Mobile.RangeFight)) { OnFailedMove(); } @@ -181,23 +188,23 @@ public class MageAI : BaseAI if (m.Paralyzed || m.Frozen) { - if (Mobile.InRange(m, 1)) + if (m_Mobile.InRange(m, 1)) { RunFrom(m); } - else if (!Mobile.InRange(m, Math.Max(Mobile.RangeFight, 2)) && !MoveTo(m, false, 1)) + else if (!m_Mobile.InRange(m, Math.Max(m_Mobile.RangeFight, 2)) && !MoveTo(m, true, 1)) { OnFailedMove(); } } - else if (!Mobile.InRange(m, Mobile.RangeFight)) + else if (!m_Mobile.InRange(m, m_Mobile.RangeFight)) { - if (!MoveTo(m, false, 1)) + if (!MoveTo(m, true, 1)) { OnFailedMove(); } } - else if (Mobile.InRange(m, Mobile.RangeFight - 1)) + else if (m_Mobile.InRange(m, m_Mobile.RangeFight - 1)) { RunFrom(m); } @@ -205,45 +212,51 @@ public class MageAI : BaseAI public void RunFrom(Mobile m) { - Run((Mobile.GetDirectionTo(m) - 4) & Direction.Mask); + Run((m_Mobile.GetDirectionTo(m) - 4) & Direction.Mask); } public void OnFailedMove() { - if (!Mobile.DisallowAllMoves && (SmartAI + if (!m_Mobile.DisallowAllMoves && (SmartAI ? Utility.Random(4) == 0 : TeleportChance > 0 && ScaleBySkill(TeleportChance, SkillName.Magery) > Utility.RandomDouble())) { - Mobile.Target?.Cancel(Mobile, TargetCancelType.Canceled); + m_Mobile.Target?.Cancel(m_Mobile, TargetCancelType.Canceled); - new TeleportSpell(Mobile).Cast(); + new TeleportSpell(m_Mobile).Cast(); - DebugSay("I am stuck, I'm going to try teleporting away"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am stuck, I'm going to try teleporting away"); + } } - else if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - this.DebugSayFormatted($"My move is blocked, so I am going to attack {Mobile.FocusMob.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"My move is blocked, so I am going to attack {m_Mobile.FocusMob.Name}"); + } - Mobile.Combatant = Mobile.FocusMob; + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } - else + else if (m_Mobile.Debug) { - DebugSay("I am stuck"); + m_Mobile.DebugSay("I am stuck"); } } public void Run(Direction d) { - if (Mobile.Spell?.IsCasting == true || Mobile.Paralyzed || Mobile.Frozen || - Mobile.DisallowAllMoves) + if (m_Mobile.Spell?.IsCasting == true || m_Mobile.Paralyzed || m_Mobile.Frozen || + m_Mobile.DisallowAllMoves) { return; } - Mobile.Direction = d | Direction.Running; + m_Mobile.Direction = d | Direction.Running; - if (!DoMove(Mobile.Direction, true)) + if (!DoMove(m_Mobile.Direction, true)) { OnFailedMove(); } @@ -251,8 +264,8 @@ public class MageAI : BaseAI public virtual bool UseNecromancy() { - var magery = Mobile.Skills.Magery.BaseFixedPoint; - var necro = Mobile.Skills.Necromancy.BaseFixedPoint; + var magery = m_Mobile.Skills.Magery.BaseFixedPoint; + var necro = m_Mobile.Skills.Necromancy.BaseFixedPoint; return IsNecromancer && Utility.Random(magery + necro) >= magery; } @@ -261,37 +274,37 @@ public class MageAI : BaseAI public virtual Spell GetRandomDamageSpellNecro() { - var bound = Mobile.Skills.Necromancy.Value >= 100 ? 5 : 3; + var bound = m_Mobile.Skills.Necromancy.Value >= 100 ? 5 : 3; return Utility.Random(bound) switch { - 0 => new PainSpikeSpell(Mobile), - 1 => new PoisonStrikeSpell(Mobile), - 2 => new StrangleSpell(Mobile), - 3 => new WitherSpell(Mobile), - _ => new VengefulSpiritSpell(Mobile) + 0 => new PainSpikeSpell(m_Mobile), + 1 => new PoisonStrikeSpell(m_Mobile), + 2 => new StrangleSpell(m_Mobile), + 3 => new WitherSpell(m_Mobile), + _ => new VengefulSpiritSpell(m_Mobile) }; } public virtual Spell GetRandomDamageSpellMage() { - var maxCircle = Math.Clamp((int)((Mobile.Skills.Magery.Value + 20.0) / (100.0 / 7.0)), 1, 8); + var maxCircle = Math.Clamp((int)((m_Mobile.Skills.Magery.Value + 20.0) / (100.0 / 7.0)), 1, 8); return Utility.Random(maxCircle * 2) switch { - 0 => new MagicArrowSpell(Mobile), - 1 => new MagicArrowSpell(Mobile), - 2 => new HarmSpell(Mobile), - 3 => new HarmSpell(Mobile), - 4 => new FireballSpell(Mobile), - 5 => new FireballSpell(Mobile), - 6 => new LightningSpell(Mobile), - 7 => new LightningSpell(Mobile), - 8 => new MindBlastSpell(Mobile), - 9 => new MindBlastSpell(Mobile), - 10 => new EnergyBoltSpell(Mobile), - 11 => new ExplosionSpell(Mobile), - _ => new FlameStrikeSpell(Mobile) + 0 => new MagicArrowSpell(m_Mobile), + 1 => new MagicArrowSpell(m_Mobile), + 2 => new HarmSpell(m_Mobile), + 3 => new HarmSpell(m_Mobile), + 4 => new FireballSpell(m_Mobile), + 5 => new FireballSpell(m_Mobile), + 6 => new LightningSpell(m_Mobile), + 7 => new LightningSpell(m_Mobile), + 8 => new MindBlastSpell(m_Mobile), + 9 => new MindBlastSpell(m_Mobile), + 10 => new EnergyBoltSpell(m_Mobile), + 11 => new ExplosionSpell(m_Mobile), + _ => new FlameStrikeSpell(m_Mobile) }; } @@ -302,36 +315,36 @@ public class MageAI : BaseAI { return Utility.Random(4) switch { - 0 => new BloodOathSpell(Mobile), - 1 => new CorpseSkinSpell(Mobile), - 2 => new EvilOmenSpell(Mobile), - _ => new MindRotSpell(Mobile) + 0 => new BloodOathSpell(m_Mobile), + 1 => new CorpseSkinSpell(m_Mobile), + 2 => new EvilOmenSpell(m_Mobile), + _ => new MindRotSpell(m_Mobile) }; } public virtual Spell GetRandomCurseSpellMage() { - if (Mobile.Skills.Magery.Value >= 40.0 && Utility.Random(4) == 0) + if (m_Mobile.Skills.Magery.Value >= 40.0 && Utility.Random(4) == 0) { - return new CurseSpell(Mobile); + return new CurseSpell(m_Mobile); } return Utility.Random(3) switch { - 0 => new WeakenSpell(Mobile), - 1 => new ClumsySpell(Mobile), - _ => new FeeblemindSpell(Mobile) + 0 => new WeakenSpell(m_Mobile), + 1 => new ClumsySpell(m_Mobile), + _ => new FeeblemindSpell(m_Mobile) }; } public virtual Spell GetRandomManaDrainSpell() { - if (Mobile.Skills.Magery.Value >= 80.0 && Utility.RandomBool()) + if (m_Mobile.Skills.Magery.Value >= 80.0 && Utility.RandomBool()) { - return new ManaVampireSpell(Mobile); + return new ManaVampireSpell(m_Mobile); } - return new ManaDrainSpell(Mobile); + return new ManaDrainSpell(m_Mobile); } public virtual Spell DoDispel(Mobile toDispel) @@ -340,7 +353,7 @@ public class MageAI : BaseAI { if (DispelChance > 0 && ScaleBySkill(DispelChance, SkillName.Magery) > Utility.RandomDouble()) { - return new DispelSpell(Mobile); + return new DispelSpell(m_Mobile); } return null; @@ -353,18 +366,18 @@ public class MageAI : BaseAI return spell; } - var distance = (int)Mobile.GetDistanceToSqrt(toDispel); - if (!Mobile.DisallowAllMoves && distance > 0 && Utility.Random(distance) == 0) + var distance = (int)m_Mobile.GetDistanceToSqrt(toDispel); + if (!m_Mobile.DisallowAllMoves && distance > 0 && Utility.Random(distance) == 0) { - return new TeleportSpell(Mobile); + return new TeleportSpell(m_Mobile); } - if (Utility.Random(3) == 0 && !Mobile.InRange(toDispel, 3) && !toDispel.Paralyzed && !toDispel.Frozen) + if (Utility.Random(3) == 0 && !m_Mobile.InRange(toDispel, 3) && !toDispel.Paralyzed && !toDispel.Frozen) { - return new ParalyzeSpell(Mobile); + return new ParalyzeSpell(m_Mobile); } - return new DispelSpell(Mobile); + return new DispelSpell(m_Mobile); } public virtual Spell ChooseSpell(Mobile c) @@ -383,12 +396,12 @@ public class MageAI : BaseAI if (IsNecromancer) { var psDamage = - (Mobile.Skills.SpiritSpeak.Value - c.Skills.MagicResist.Value) / 10 + + (m_Mobile.Skills.SpiritSpeak.Value - c.Skills.MagicResist.Value) / 10 + (c.Player ? 18 : 30); if (psDamage > c.Hits) { - return new PainSpikeSpell(Mobile); + return new PainSpikeSpell(m_Mobile); } } @@ -402,41 +415,56 @@ public class MageAI : BaseAI goto default; } - DebugSay("Attempting to poison"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Attempting to poison"); + } - spell = new PoisonSpell(Mobile); + spell = new PoisonSpell(m_Mobile); break; } case 2: // Bless ourselves { - DebugSay("Blessing myself"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Blessing myself"); + } - spell = new BlessSpell(Mobile); + spell = new BlessSpell(m_Mobile); break; } case 3: case 4: // Curse them { - DebugSay("Attempting to curse"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Attempting to curse"); + } spell = GetRandomCurseSpell(); break; } case 5: // Paralyze them { - if (c.Paralyzed || Mobile.Skills.Magery.Value <= 50.0) + if (c.Paralyzed || m_Mobile.Skills.Magery.Value <= 50.0) { goto default; } - DebugSay("Attempting to paralyze"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Attempting to paralyze"); + } - spell = new ParalyzeSpell(Mobile); + spell = new ParalyzeSpell(m_Mobile); break; } case 6: // Drain mana { - DebugSay("Attempting to drain mana"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Attempting to drain mana"); + } spell = GetRandomManaDrainSpell(); break; @@ -448,14 +476,20 @@ public class MageAI : BaseAI goto default; } - DebugSay("Attempting to invis myself"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Attempting to invis myself"); + } - spell = new InvisibilitySpell(Mobile); + spell = new InvisibilitySpell(m_Mobile); break; } default: // Damage them { - DebugSay("Just doing damage"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Just doing damage"); + } spell = GetRandomDamageSpell(); break; @@ -477,7 +511,7 @@ public class MageAI : BaseAI goto case 1; } - spell = new PoisonSpell(Mobile); + spell = new PoisonSpell(m_Mobile); break; } case 1: // Deal some damage @@ -487,30 +521,33 @@ public class MageAI : BaseAI } default: // Set up a combo { - if (Mobile.Mana is > 15 and < 40) + if (m_Mobile.Mana is > 15 and < 40) { - if (c.Paralyzed && !c.Poisoned && !Mobile.Meditating) + if (c.Paralyzed && !c.Poisoned && !m_Mobile.Meditating) { - DebugSay("I am going to meditate"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am going to meditate"); + } - Mobile.UseSkill(SkillName.Meditation); + m_Mobile.UseSkill(SkillName.Meditation); } else if (!c.Poisoned) { - spell = new ParalyzeSpell(Mobile); + spell = new ParalyzeSpell(m_Mobile); } } - else if (Mobile.Mana > 60) + else if (m_Mobile.Mana > 60) { if (Utility.RandomBool() && !c.Paralyzed && !c.Frozen && !c.Poisoned) { - _combo = 0; - spell = new ParalyzeSpell(Mobile); + m_Combo = 0; + spell = new ParalyzeSpell(m_Mobile); } else { - _combo = 1; - spell = new ExplosionSpell(Mobile); + m_Combo = 1; + spell = new ExplosionSpell(m_Mobile); } } @@ -520,13 +557,16 @@ public class MageAI : BaseAI } } - if (spell != null) + if (m_Mobile.Debug) { - this.DebugSayFormatted($"Casting {spell.Name}"); - } - else - { - DebugSay("I don't have a spell to use!"); + if (spell != null) + { + m_Mobile.DebugSay($"Casting {spell.Name}"); + } + else + { + m_Mobile.DebugSay("I don't have a spell to use!"); + } } return spell; @@ -536,31 +576,31 @@ public class MageAI : BaseAI { Spell spell = null; - if (_combo == 0) + if (m_Combo == 0) { - spell = new ExplosionSpell(Mobile); - ++_combo; // Move to next spell + spell = new ExplosionSpell(m_Mobile); + ++m_Combo; // Move to next spell } - else if (_combo == 1) + else if (m_Combo == 1) { - spell = new WeakenSpell(Mobile); - ++_combo; // Move to next spell + spell = new WeakenSpell(m_Mobile); + ++m_Combo; // Move to next spell } - else if (_combo == 2) + else if (m_Combo == 2) { if (!c.Poisoned) { - spell = new PoisonSpell(Mobile); + spell = new PoisonSpell(m_Mobile); } else if (IsNecromancer) { - spell = new StrangleSpell(Mobile); + spell = new StrangleSpell(m_Mobile); } - ++_combo; // Move to next spell + ++m_Combo; // Move to next spell } - if (_combo == 3 && spell == null) + if (m_Combo == 3 && spell == null) { switch (Utility.Random(IsNecromancer ? 4 : 3)) { @@ -568,41 +608,41 @@ public class MageAI : BaseAI { if (c.Int < c.Dex) { - spell = new FeeblemindSpell(Mobile); + spell = new FeeblemindSpell(m_Mobile); } else { - spell = new ClumsySpell(Mobile); + spell = new ClumsySpell(m_Mobile); } - ++_combo; // Move to next spell + ++m_Combo; // Move to next spell break; } case 1: { - spell = new EnergyBoltSpell(Mobile); - _combo = -1; // Reset combo state + spell = new EnergyBoltSpell(m_Mobile); + m_Combo = -1; // Reset combo state break; } case 2: { - spell = new FlameStrikeSpell(Mobile); - _combo = -1; // Reset combo state + spell = new FlameStrikeSpell(m_Mobile); + m_Combo = -1; // Reset combo state break; } default: { - spell = new PainSpikeSpell(Mobile); - _combo = -1; // Reset combo state + spell = new PainSpikeSpell(m_Mobile); + m_Combo = -1; // Reset combo state break; } } } - else if (_combo == 4 && spell == null) + else if (m_Combo == 4 && spell == null) { - spell = new MindBlastSpell(Mobile); - _combo = -1; + spell = new MindBlastSpell(m_Mobile); + m_Combo = -1; } return spell; @@ -612,7 +652,7 @@ public class MageAI : BaseAI { if (SmartAI || spell is DispelSpell) { - return TimeSpan.FromSeconds(Mobile.ActiveSpeed); + return TimeSpan.FromSeconds(m_Mobile.ActiveSpeed); } var del = ScaleBySkill(3.0, SkillName.Magery); @@ -624,121 +664,140 @@ public class MageAI : BaseAI public override bool DoActionCombat() { - var c = Mobile.Combatant; - Mobile.Warmode = true; + var c = m_Mobile.Combatant; + m_Mobile.Warmode = true; - if (c?.Deleted != false || !c.Alive || c.IsDeadBondedPet || !Mobile.CanSee(c) || - !Mobile.CanBeHarmful(c, false) || c.Map != Mobile.Map) + if (c?.Deleted != false || !c.Alive || c.IsDeadBondedPet || !m_Mobile.CanSee(c) || + !m_Mobile.CanBeHarmful(c, false) || c.Map != m_Mobile.Map) { // Our combatant is deleted, dead, hidden, or we cannot hurt them // Try to find another combatant - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - Mobile.Combatant = c = Mobile.FocusMob!; + m_Mobile.Combatant = c = m_Mobile.FocusMob!; - this.DebugSayFormatted($"Something happened to my combatant, so I am going to fight {c.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay( + $"Something happened to my combatant, so I am going to fight {c.Name}" + ); + } - Mobile.FocusMob = null; + m_Mobile.FocusMob = null; } else { - DebugSay("Something happened to my combatant, and nothing is around. I am on guard."); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Something happened to my combatant, and nothing is around. I am on guard."); + } Action = ActionType.Guard; return true; } } - if (!Mobile.InLOS(c)) + if (!m_Mobile.InLOS(c)) { - DebugSay("I can't see my target"); - - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (m_Mobile.Debug) { - Mobile.Combatant = c = Mobile.FocusMob!; + m_Mobile.DebugSay("I can't see my target"); + } - this.DebugSayFormatted($"I will switch to {c.Name}"); + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + m_Mobile.Combatant = c = m_Mobile.FocusMob!; - Mobile.FocusMob = null; + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I will switch to {c.Name}"); + } + + m_Mobile.FocusMob = null; } } - if (!Core.AOS && SmartAI && !Mobile.StunReady && Mobile.Skills.Wrestling.Value >= 80.0 && - Mobile.Skills.Anatomy.Value >= 80.0) + if (!Core.AOS && SmartAI && !m_Mobile.StunReady && m_Mobile.Skills.Wrestling.Value >= 80.0 && + m_Mobile.Skills.Anatomy.Value >= 80.0) { - Fists.StunRequest(Mobile); + Fists.StunRequest(m_Mobile); } - if (!Mobile.InRange(c, Mobile.RangePerception)) + if (!m_Mobile.InRange(c, m_Mobile.RangePerception)) { // They are somewhat far away, can we find something else? - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - Mobile.Combatant = Mobile.FocusMob; - Mobile.FocusMob = null; + m_Mobile.Combatant = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; } - else if (!Mobile.InRange(c, Mobile.ChaseLeashRange)) + else if (!m_Mobile.InRange(c, m_Mobile.RangePerception * 3)) { - Mobile.Combatant = null; + m_Mobile.Combatant = null; } - c = Mobile.Combatant; + c = m_Mobile.Combatant; if (c == null) { - DebugSay("My combatant has fled, so I am on guard"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant has fled, so I am on guard"); + } Action = ActionType.Guard; return true; } } - // Geometry (not hiding — CanSee passed above) is blocking the shot: close in until - // line of sight returns. Poisoned mages still fall through to cure. - if (!Mobile.Poisoned && Mobile.Spell?.IsCasting != true && !Mobile.InLOS(c)) + if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee && m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100) { - DebugSay("I cannot see my target, moving to regain line of sight"); + // We are low on health, should we flee? + // (10 + diff)% chance to flee + var fleeChance = 10 + Math.Max(0, c.Hits - m_Mobile.Hits); - if (!MoveTo(c, false, 1)) + if (Utility.Random(0, 100) > fleeChance) { - OnFailedMove(); - } + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to flee from {c.Name}"); + } - _lastTarget = c; - _lastTargetLoc = c.Location; - - return true; - } - - if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, c)) - { - DebugSay("I used my abilities!"); - } - else if (Mobile.Spell == null && Core.TickCount - _nextCastTime >= 0) - { - if (Mobile.Controlled && c == Mobile) - { - DebugSay("I should not attack myself!"); - Mobile.Combatant = null; - Action = ActionType.Guard; + Action = ActionType.Flee; return true; } + } + if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, c)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I used my abilities!"); + } + } + else if (m_Mobile.Spell == null && Core.TickCount - m_NextCastTime >= 0) + { // We are ready to cast a spell Spell spell; var toDispel = FindDispelTarget(true); - if (Mobile.Poisoned) // Top cast priority is cure + if (m_Mobile.Poisoned) // Top cast priority is cure { - DebugSay("I am going to cure myself"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am going to cure myself"); + } - spell = new CureSpell(Mobile); + spell = new CureSpell(m_Mobile); } else if (toDispel != null) // Something dispellable is attacking us { - this.DebugSayFormatted($"I am going to dispel {toDispel}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to dispel {toDispel}"); + } spell = DoDispel(toDispel); // May return null if dumb AI and doesn't have enough skill } @@ -750,90 +809,96 @@ public class MageAI : BaseAI spell ??= SmartAI switch { // We are doing a spell combo - true when _combo != -1 => DoCombo(c), + true when m_Combo != -1 => DoCombo(c), // They have a heal spell out - true when !c.Poisoned && c.Spell is HealSpell or GreaterHealSpell => new PoisonSpell(Mobile), + true when !c.Poisoned && c.Spell is HealSpell or GreaterHealSpell => new PoisonSpell(m_Mobile), _ => ChooseSpell(c) }; // Now we have a spell picked - var range = (spell as IRangedSpell)?.TargetRange ?? Mobile.RangePerception; + var range = (spell as IRangedSpell)?.TargetRange ?? m_Mobile.RangePerception; // Move first before casting if (!SmartAI || toDispel == null) { RunTo(c); } - else if (Mobile.InRange(toDispel, Math.Min(10, range))) + else if (m_Mobile.InRange(toDispel, Math.Min(10, range))) { RunFrom(toDispel); } - else if (!Mobile.InRange(toDispel, range)) + else if (!m_Mobile.InRange(toDispel, range)) { RunTo(toDispel); } // After running, make sure we are still in range - if (spell == null || Mobile.InRange(c, range)) + if (spell == null || m_Mobile.InRange(c, range)) { spell?.Cast(); // Even if we don't have a spell, delay the next potential cast - _nextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; + m_NextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; } } - else if (Mobile.Spell?.IsCasting != true) + else if (m_Mobile.Spell?.IsCasting != true) { RunTo(c); } - if (Mobile.InRange(c, 1) || Mobile.Spell?.IsCasting == true || Core.TickCount - Mobile.LastMoveTime > 400) + if (m_Mobile.Spell != null || !m_Mobile.InRange(c, 1) || Core.TickCount - m_Mobile.LastMoveTime > 800) { - Mobile.Direction = Mobile.GetDirectionTo(c); + m_Mobile.Direction = m_Mobile.GetDirectionTo(c); } - _lastTarget = c; - _lastTargetLoc = c.Location; + m_LastTarget = c; + m_LastTargetLoc = c.Location; return true; } public override bool DoActionGuard() { - if (_lastTarget?.Hidden == true) + if (m_LastTarget?.Hidden == true) { - var map = Mobile.Map; + var map = m_Mobile.Map; - if (map == null || !Mobile.InRange(_lastTargetLoc, Mobile.RangePerception)) + if (map == null || !m_Mobile.InRange(m_LastTargetLoc, m_Mobile.RangePerception)) { - _lastTarget = null; + m_LastTarget = null; } - else if (Mobile.Spell == null && Core.TickCount - _nextCastTime >= 0) + else if (m_Mobile.Spell == null && Core.TickCount - m_NextCastTime >= 0) { - DebugSay("I am going to reveal my last target"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am going to reveal my last target"); + } - _revealTarget = new LandTarget(_lastTargetLoc, map); - Spell spell = new RevealSpell(Mobile); + m_RevealTarget = new LandTarget(m_LastTargetLoc, map); + Spell spell = new RevealSpell(m_Mobile); if (spell.Cast()) { - _lastTarget = null; // only do it once + m_LastTarget = null; // only do it once } - _nextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; + m_NextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; } } - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - this.DebugSayFormatted($"I am going to attack {Mobile.FocusMob.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to attack {m_Mobile.FocusMob.Name}"); + } - Mobile.Combatant = Mobile.FocusMob; + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } else { - if (!Mobile.Controlled) + if (!m_Mobile.Controlled) { ProcessTarget(); @@ -848,28 +913,70 @@ public class MageAI : BaseAI return true; } + public override bool DoActionFlee() + { + // Mobile c = m_Mobile.Combatant; + + if ((m_Mobile.Mana > 20 || m_Mobile.Mana == m_Mobile.ManaMax) && m_Mobile.Hits > m_Mobile.HitsMax / 2) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am stronger now, my guard is up"); + } + + Action = ActionType.Guard; + } + else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am scared of {m_Mobile.FocusMob.Name}"); + } + + RunFrom(m_Mobile.FocusMob); + m_Mobile.FocusMob = null; + + if (m_Mobile.Poisoned && Utility.Random(0, 5) == 0) + { + new CureSpell(m_Mobile).Cast(); + } + } + else + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Area seems clear, but my guard is up"); + } + + Action = ActionType.Guard; + m_Mobile.Warmode = true; + } + + return true; + } + public Mobile FindDispelTarget(bool activeOnly) { - if (Mobile.Deleted || Mobile.Int < 95 || CanDispel(Mobile) || Mobile.AutoDispel) + if (m_Mobile.Deleted || m_Mobile.Int < 95 || CanDispel(m_Mobile) || m_Mobile.AutoDispel) { return null; } if (activeOnly) { - var aggressed = Mobile.Aggressed; - var aggressors = Mobile.Aggressors; + var aggressed = m_Mobile.Aggressed; + var aggressors = m_Mobile.Aggressors; Mobile active = null; var activePrio = 0.0; - var comb = Mobile.Combatant; + var comb = m_Mobile.Combatant; if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && - Mobile.InRange(comb, Mobile.RangePerception) && CanDispel(comb)) + m_Mobile.InRange(comb, m_Mobile.RangePerception) && CanDispel(comb)) { active = comb; - activePrio = Mobile.GetDistanceToSqrt(comb); + activePrio = m_Mobile.GetDistanceToSqrt(comb); if (activePrio <= 2) { @@ -882,9 +989,9 @@ public class MageAI : BaseAI var info = aggressed[i]; var m = info.Defender; - if (m != comb && m.Combatant == Mobile && Mobile.InRange(m, Mobile.RangePerception) && CanDispel(m)) + if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, m_Mobile.RangePerception) && CanDispel(m)) { - var prio = Mobile.GetDistanceToSqrt(m); + var prio = m_Mobile.GetDistanceToSqrt(m); if (active == null || prio < activePrio) { @@ -904,9 +1011,9 @@ public class MageAI : BaseAI var info = aggressors[i]; var m = info.Attacker; - if (m != comb && m.Combatant == Mobile && Mobile.InRange(m, Mobile.RangePerception) && CanDispel(m)) + if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, m_Mobile.RangePerception) && CanDispel(m)) { - var prio = Mobile.GetDistanceToSqrt(m); + var prio = m_Mobile.GetDistanceToSqrt(m); if (active == null || prio < activePrio) { @@ -924,26 +1031,26 @@ public class MageAI : BaseAI return active; } - var map = Mobile.Map; + var map = m_Mobile.Map; if (map != null) { Mobile active = null, inactive = null; double actPrio = 0.0, inactPrio = 0.0; - var comb = Mobile.Combatant; + var comb = m_Mobile.Combatant; if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && CanDispel(comb)) { active = inactive = comb; - actPrio = inactPrio = Mobile.GetDistanceToSqrt(comb); + actPrio = inactPrio = m_Mobile.GetDistanceToSqrt(comb); } - foreach (var m in Mobile.GetMobilesInRange(Mobile.RangePerception)) + foreach (var m in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) { - if (m != Mobile && CanDispel(m)) + if (m != m_Mobile && CanDispel(m)) { - var prio = Mobile.GetDistanceToSqrt(m); + var prio = m_Mobile.GetDistanceToSqrt(m); if (inactive == null || prio < inactPrio) { @@ -951,7 +1058,7 @@ public class MageAI : BaseAI inactPrio = prio; } - if ((Mobile.Combatant == m || m.Combatant == Mobile) && (active == null || prio < actPrio)) + if ((m_Mobile.Combatant == m || m.Combatant == m_Mobile) && (active == null || prio < actPrio)) { active = m; actPrio = prio; @@ -966,12 +1073,12 @@ public class MageAI : BaseAI } public bool CanDispel(Mobile m) => - m is BaseCreature creature && creature.Summoned && creature.SummonMaster != Mobile && - Mobile.CanBeHarmful(creature, false) && !creature.IsAnimatedDead; + m is BaseCreature creature && creature.Summoned && creature.SummonMaster != m_Mobile && + m_Mobile.CanBeHarmful(creature, false) && !creature.IsAnimatedDead; private bool ProcessTarget() { - var targ = Mobile.Target; + var targ = m_Mobile.Target; if (targ == null) { @@ -991,7 +1098,7 @@ public class MageAI : BaseAI if (isInvisible) { - toTarget = Mobile; + toTarget = m_Mobile; } else if (isDispel) { @@ -1001,7 +1108,7 @@ public class MageAI : BaseAI { RunTo(toTarget); } - else if (toTarget != null && Mobile.InRange(toTarget, 10)) + else if (toTarget != null && m_Mobile.InRange(toTarget, 10)) { RunFrom(toTarget); } @@ -1012,14 +1119,14 @@ public class MageAI : BaseAI if (toTarget == null) { - toTarget = Mobile.Combatant; + toTarget = m_Mobile.Combatant; if (toTarget != null) { RunTo(toTarget); } } - else if (Mobile.InRange(toTarget, 10)) + else if (m_Mobile.InRange(toTarget, 10)) { RunFrom(toTarget); teleportAway = true; @@ -1031,46 +1138,37 @@ public class MageAI : BaseAI } else { - toTarget = Mobile.Combatant; + toTarget = m_Mobile.Combatant; if (toTarget != null) { - // Without line of sight the stand-off is pointless — close in so the held - // target can be invoked. - if (!Mobile.InLOS(toTarget)) - { - MoveTo(toTarget, true, 1); - } - else - { - RunTo(toTarget); - } + RunTo(toTarget); } } if ((targ.Flags & TargetFlags.Harmful) != 0 && toTarget != null) { - if ((targ.Range == -1 || Mobile.InRange(toTarget, targ.Range)) && Mobile.CanSee(toTarget) && - Mobile.InLOS(toTarget)) + if ((targ.Range == -1 || m_Mobile.InRange(toTarget, targ.Range)) && m_Mobile.CanSee(toTarget) && + m_Mobile.InLOS(toTarget)) { - targ.Invoke(Mobile, toTarget); + targ.Invoke(m_Mobile, toTarget); } else if (isDispel) { - targ.Cancel(Mobile, TargetCancelType.Canceled); + targ.Cancel(m_Mobile, TargetCancelType.Canceled); } } else if ((targ.Flags & TargetFlags.Beneficial) != 0) { - targ.Invoke(Mobile, Mobile); + targ.Invoke(m_Mobile, m_Mobile); } - else if (isReveal && _revealTarget != null) + else if (isReveal && m_RevealTarget != null) { - targ.Invoke(Mobile, _revealTarget); + targ.Invoke(m_Mobile, m_RevealTarget); } else { - var map = Mobile.Map; + var map = m_Mobile.Map; if (map != null && isTeleport && toTarget != null) { @@ -1081,10 +1179,10 @@ public class MageAI : BaseAI if (teleportAway) { - var rx = Mobile.X - toTarget.X; - var ry = Mobile.Y - toTarget.Y; + var rx = m_Mobile.X - toTarget.X; + var ry = m_Mobile.Y - toTarget.Y; - var d = Mobile.GetDistanceToSqrt(toTarget); + var d = m_Mobile.GetDistanceToSqrt(toTarget); px = toTarget.X + (int)(rx * (10 / d)); py = toTarget.Y + (int)(ry * (10 / d)); @@ -1095,18 +1193,18 @@ public class MageAI : BaseAI py = toTarget.Y; } - for (var i = 0; i < Offsets.Length; i += 2) + for (var i = 0; i < m_Offsets.Length; i += 2) { - int x = Offsets[i], y = Offsets[i + 1]; + int x = m_Offsets[i], y = m_Offsets[i + 1]; var p = new Point3D(px + x, py + y, 0); var lt = new LandTarget(p, map); - if ((targ.Range == -1 || Mobile.InRange(p, targ.Range)) && Mobile.InLOS(lt) && + if ((targ.Range == -1 || m_Mobile.InRange(p, targ.Range)) && m_Mobile.InLOS(lt) && map.CanSpawnMobile(px + x, py + y, lt.Z) && !SpellHelper.CheckMulti(p, map)) { - targ.Invoke(Mobile, lt); + targ.Invoke(m_Mobile, lt); return true; } } @@ -1114,23 +1212,23 @@ public class MageAI : BaseAI for (var i = 0; i < 10; ++i) { var randomPoint = new Point3D( - Mobile.X - teleRange + Utility.Random(teleRange * 2 + 1), - Mobile.Y - teleRange + Utility.Random(teleRange * 2 + 1), + m_Mobile.X - teleRange + Utility.Random(teleRange * 2 + 1), + m_Mobile.Y - teleRange + Utility.Random(teleRange * 2 + 1), 0 ); var lt = new LandTarget(randomPoint, map); - if (Mobile.InLOS(lt) && map.CanSpawnMobile(lt.X, lt.Y, lt.Z) && + if (m_Mobile.InLOS(lt) && map.CanSpawnMobile(lt.X, lt.Y, lt.Z) && !SpellHelper.CheckMulti(randomPoint, map)) { - targ.Invoke(Mobile, new LandTarget(randomPoint, map)); + targ.Invoke(m_Mobile, new LandTarget(randomPoint, map)); return true; } } } - targ.Cancel(Mobile, TargetCancelType.Canceled); + targ.Cancel(m_Mobile, TargetCancelType.Canceled); } return true; diff --git a/Projects/UOContent/Mobiles/AI/MeleeAI.cs b/Projects/UOContent/Mobiles/AI/MeleeAI.cs index 544770069..5bb697a1b 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -1,3 +1,5 @@ +using System; + namespace Server.Mobiles; public class MeleeAI : BaseAI @@ -6,21 +8,27 @@ public class MeleeAI : BaseAI { } - public override double FleeHealthThreshold => 0.2; // 20% is default - public override double FleeChance => 0.1; // 10% is default - public override bool DoActionWander() { - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); - Mobile.Combatant = Mobile.FocusMob; + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + } + + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } else { - DebugSay("I am wandering"); - Mobile.Warmode = false; + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am wandering"); + } + + m_Mobile.Warmode = false; + base.DoActionWander(); } @@ -29,106 +37,125 @@ public class MeleeAI : BaseAI public override bool DoActionCombat() { - var combatant = Mobile.Combatant; + var combatant = m_Mobile.Combatant; - if (!IsValidCombatant(combatant)) + if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || + combatant.IsDeadBondedPet) { - DebugSay("My combatant is gone, so my guard is up"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + } + Action = ActionType.Guard; return true; } - if (!Mobile.InRange(combatant, Mobile.RangePerception)) + if (!m_Mobile.InRange(combatant, m_Mobile.RangePerception)) { - if (!HandleOutOfRangeCombatant(combatant)) + // They are somewhat far away, can we find something else? + + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { + m_Mobile.Combatant = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; + } + else if (!m_Mobile.InRange(combatant, m_Mobile.RangePerception * 3)) + { + m_Mobile.Combatant = null; + } + + combatant = m_Mobile.Combatant; + + if (combatant == null) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant has fled, so I am on guard"); + } + + Action = ActionType.Guard; return true; } - combatant = Mobile.Combatant; } - if (!AttemptMoveToCombatant(combatant)) + if (!MoveTo(combatant, true, m_Mobile.RangeFight)) { - return true; - } + m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"My move is blocked, so I am going to attack {m_Mobile.FocusMob!.Name}"); + } - if (Core.TickCount - Mobile.LastMoveTime > 400) + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + return true; + } + + if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I cannot find {combatant.Name}, so my guard is up"); + } + + Action = ActionType.Guard; + return true; + } + + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I cannot find {combatant.Name}, so my guard is up"); + } + } + else if (Core.TickCount - m_Mobile.LastMoveTime > 400) { - Mobile.Direction = Mobile.GetDirectionTo(combatant); + m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); } - if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee) { - DebugSay("I used my abilities!"); + if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100) + { + // We are low on health, should we flee? + + var fleeChance = 10 + Math.Max(0, combatant.Hits - m_Mobile.Hits); // (10 + diff)% chance to flee; + if (Utility.Random(0, 100) < fleeChance) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); + } + + Action = ActionType.Flee; + return true; + } + } } - return true; - } - - private bool IsValidCombatant(Mobile combatant) - { - return combatant?.Deleted == false - && combatant.Map == Mobile.Map - && combatant.Alive - && !combatant.IsDeadBondedPet; - } - - private bool HandleOutOfRangeCombatant(Mobile combatant) - { - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - Mobile.Combatant = Mobile.FocusMob; - Mobile.FocusMob = null; - return true; + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I used my abilities!"); + } } - if (!Mobile.InRange(combatant, Mobile.ChaseLeashRange)) - { - Mobile.Combatant = null; - } - - if (Mobile.Combatant == null) - { - DebugSay("My combatant has fled, so I am on guard."); - Action = ActionType.Guard; - return false; - } - - return true; - } - - private bool AttemptMoveToCombatant(Mobile combatant) - { - if (MoveTo(combatant, false, Mobile.RangeFight)) - { - return true; - } - - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) - { - this.DebugSayFormatted($"My move is blocked, so I am going to attack {Mobile.FocusMob.Name}."); - Mobile.Combatant = Mobile.FocusMob; - Action = ActionType.Combat; - return true; - } - - if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) - { - this.DebugSayFormatted($"I cannot find {combatant.Name}, so my guard is up."); - Action = ActionType.Guard; - return false; - } - - this.DebugSayFormatted($"I cannot reach {combatant.Name} but continuing to try."); return true; } public override bool DoActionGuard() { - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking."); - Mobile.Combatant = Mobile.FocusMob; + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + } + + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } else @@ -141,14 +168,18 @@ public class MeleeAI : BaseAI public override bool DoActionFlee() { - if (Mobile.Hits > Mobile.HitsMax * FleeHealthThreshold) + if (m_Mobile.Hits > m_Mobile.HitsMax / 2) { - DebugSay("I am stronger now, so I will continue fighting."); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am stronger now, so I will continue fighting"); + } + Action = ActionType.Combat; } else { - Mobile.FocusMob = Mobile.Combatant; + m_Mobile.FocusMob = m_Mobile.Combatant; base.DoActionFlee(); } diff --git a/Projects/UOContent/Mobiles/AI/OppositionGroup.cs b/Projects/UOContent/Mobiles/AI/OppositionGroup.cs index 344b65eba..9c7dafb97 100644 --- a/Projects/UOContent/Mobiles/AI/OppositionGroup.cs +++ b/Projects/UOContent/Mobiles/AI/OppositionGroup.cs @@ -1,125 +1,135 @@ using System; using Server.Mobiles; -namespace Server; - -public class OppositionGroup +namespace Server { - private readonly Type[][] _types; - - public OppositionGroup(Type[][] types) => _types = types; - - public static OppositionGroup TerathansAndOphidians { get; } = new( - [ - [ - typeof(TerathanAvenger), - typeof(TerathanDrone), - typeof(TerathanMatriarch), - typeof(TerathanWarrior) - ], - [ - typeof(OphidianArchmage), - typeof(OphidianKnight), - typeof(OphidianMage), - typeof(OphidianMatriarch), - typeof(OphidianWarrior) - ] - ] - ); - - public static OppositionGroup SavagesAndOrcs { get; } = new( - [ - [ - typeof(Orc), - typeof(OrcBomber), - typeof(OrcBrute), - typeof(OrcCaptain), - typeof(OrcishLord), - typeof(OrcishMage), - typeof(SpawnedOrcishLord) - ], - [ - typeof(Savage), - typeof(SavageRider), - typeof(SavageRidgeback), - typeof(SavageShaman) - ] - ] - ); - - public static OppositionGroup FeyAndUndead { get; } = new( - [ - [ - typeof(Centaur), - typeof(EtherealWarrior), - typeof(Kirin), - typeof(LordOaks), - typeof(Pixie), - typeof(Silvani), - typeof(Unicorn), - typeof(Wisp), - typeof(Treefellow), - typeof(MLDryad), - typeof(Satyr) - ], - [ - typeof(AncientLich), - typeof(Bogle), - typeof(LichLord), - typeof(Shade), - typeof(Spectre), - typeof(Wraith), - typeof(BoneKnight), - typeof(Ghoul), - typeof(Mummy), - typeof(SkeletalKnight), - typeof(Skeleton), - typeof(Zombie), - typeof(ShadowKnight), - typeof(DarknightCreeper), - typeof(RevenantLion), - typeof(LadyOfTheSnow), - typeof(RottingCorpse), - typeof(SkeletalDragon), - typeof(Lich) - ] - ] - ); - - public bool IsEnemy(object from, object target) + public class OppositionGroup { - var fromGroup = IndexOf(from); - var targGroup = IndexOf(target); + private readonly Type[][] m_Types; - return fromGroup != -1 && targGroup != -1 && fromGroup != targGroup; - } + public OppositionGroup(Type[][] types) => m_Types = types; - public int IndexOf(object obj) - { - if (obj == null) + public static OppositionGroup TerathansAndOphidians { get; } = new( + new[] + { + new[] + { + typeof(TerathanAvenger), + typeof(TerathanDrone), + typeof(TerathanMatriarch), + typeof(TerathanWarrior) + }, + new[] + { + typeof(OphidianArchmage), + typeof(OphidianKnight), + typeof(OphidianMage), + typeof(OphidianMatriarch), + typeof(OphidianWarrior) + } + } + ); + + public static OppositionGroup SavagesAndOrcs { get; } = new( + new[] + { + new[] + { + typeof(Orc), + typeof(OrcBomber), + typeof(OrcBrute), + typeof(OrcCaptain), + typeof(OrcishLord), + typeof(OrcishMage), + typeof(SpawnedOrcishLord) + }, + new[] + { + typeof(Savage), + typeof(SavageRider), + typeof(SavageRidgeback), + typeof(SavageShaman) + } + } + ); + + public static OppositionGroup FeyAndUndead { get; } = new( + new[] + { + new[] + { + typeof(Centaur), + typeof(EtherealWarrior), + typeof(Kirin), + typeof(LordOaks), + typeof(Pixie), + typeof(Silvani), + typeof(Unicorn), + typeof(Wisp), + typeof(Treefellow), + typeof(MLDryad), + typeof(Satyr) + }, + new[] + { + typeof(AncientLich), + typeof(Bogle), + typeof(LichLord), + typeof(Shade), + typeof(Spectre), + typeof(Wraith), + typeof(BoneKnight), + typeof(Ghoul), + typeof(Mummy), + typeof(SkeletalKnight), + typeof(Skeleton), + typeof(Zombie), + typeof(ShadowKnight), + typeof(DarknightCreeper), + typeof(RevenantLion), + typeof(LadyOfTheSnow), + typeof(RottingCorpse), + typeof(SkeletalDragon), + typeof(Lich) + } + } + ); + + public bool IsEnemy(object from, object target) { + var fromGroup = IndexOf(from); + var targGroup = IndexOf(target); + + return fromGroup != -1 && targGroup != -1 && fromGroup != targGroup; + } + + public int IndexOf(object obj) + { + if (obj == null) + { + return -1; + } + + var type = obj.GetType(); + + for (var i = 0; i < m_Types.Length; ++i) + { + var group = m_Types[i]; + + var contains = false; + + for (var j = 0; !contains && j < group.Length; ++j) + { + contains = group[j].IsAssignableFrom(type); + } + + if (contains) + { + return i; + } + } + return -1; } - - var type = obj.GetType(); - - for (var i = 0; i < _types.Length; ++i) - { - var group = _types[i]; - - var contains = false; - - for (var j = 0; !contains && j < group.Length; ++j) - { - contains = group[j].IsAssignableFrom(type); - } - - if (contains) - { - return i; - } - } - - return -1; } } diff --git a/Projects/UOContent/Mobiles/AI/PredatorAI.cs b/Projects/UOContent/Mobiles/AI/PredatorAI.cs index 5e0e01520..7532595df 100644 --- a/Projects/UOContent/Mobiles/AI/PredatorAI.cs +++ b/Projects/UOContent/Mobiles/AI/PredatorAI.cs @@ -8,15 +8,21 @@ public class PredatorAI : BaseAI public override bool DoActionWander() { - if (Mobile.Combatant != null) + if (m_Mobile.Combatant != null) { - DebugSay("I am hurt or being attacked, I kill him"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am hurt or being attacked, I kill him"); + } Action = ActionType.Combat; } - else if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, true, false, true)) + else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, true, false, true)) { - DebugSay("There is something near, I go away"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("There is something near, I go away"); + } Action = ActionType.Backoff; } @@ -30,33 +36,45 @@ public class PredatorAI : BaseAI public override bool DoActionCombat() { - var combatant = Mobile.Combatant; + var combatant = m_Mobile.Combatant; - if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || + if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - DebugSay("My combatant is gone, so my guard is up"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + } Action = ActionType.Guard; return true; } - if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) { - if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) + if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) { - this.DebugSayFormatted($"I cannot find {combatant.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I cannot find {combatant.Name}"); + } Action = ActionType.Wander; return true; } - this.DebugSayFormatted($"I should be closer to {combatant.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); + } } - if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); + } } return true; @@ -64,22 +82,28 @@ public class PredatorAI : BaseAI public override bool DoActionBackoff() { - if (Mobile.IsHurt() || Mobile.Combatant != null) + if (m_Mobile.IsHurt() || m_Mobile.Combatant != null) { Action = ActionType.Combat; } - else if (AcquireFocusMob(Mobile.RangePerception * 2, FightMode.Closest, true, false, true)) + else if (AcquireFocusMob(m_Mobile.RangePerception * 2, FightMode.Closest, true, false, true)) { - if (WalkMobileRange(Mobile.FocusMob, 1, false, Mobile.RangePerception, Mobile.RangePerception * 2)) + if (WalkMobileRange(m_Mobile.FocusMob, 1, false, m_Mobile.RangePerception, m_Mobile.RangePerception * 2)) { - DebugSay("Well, here I am safe"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Well, here I am safe"); + } Action = ActionType.Wander; } } else { - DebugSay("I have lost my focus, lets relax"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I have lost my focus, lets relax"); + } Action = ActionType.Wander; } diff --git a/Projects/UOContent/Mobiles/AI/ThiefAI.cs b/Projects/UOContent/Mobiles/AI/ThiefAI.cs index 0fa37bbc9..c81e5be6e 100644 --- a/Projects/UOContent/Mobiles/AI/ThiefAI.cs +++ b/Projects/UOContent/Mobiles/AI/ThiefAI.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles; public class ThiefAI : BaseAI { - private Item _toDisarm; + private Item m_toDisarm; public ThiefAI(BaseCreature m) : base(m) { @@ -13,13 +13,19 @@ public class ThiefAI : BaseAI public override bool DoActionWander() { - DebugSay("I have no combatant"); - - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (m_Mobile.Debug) { - this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); + m_Mobile.DebugSay("I have no combatant"); + } - Mobile.Combatant = Mobile.FocusMob; + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + } + + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } else @@ -32,55 +38,70 @@ public class ThiefAI : BaseAI public override bool DoActionCombat() { - var combatant = Mobile.Combatant; + var combatant = m_Mobile.Combatant; - if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || + if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - DebugSay("My combatant is gone, so my guard is up"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + } Action = ActionType.Guard; return true; } - if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) { - this.DebugSayFormatted($"I should be closer to {combatant.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); + } } else { - if (_toDisarm?.IsChildOf(Mobile.Backpack) != false) + if (m_toDisarm?.IsChildOf(m_Mobile.Backpack) != false) { - _toDisarm = combatant.FindItemOnLayer(Layer.OneHanded) ?? combatant.FindItemOnLayer(Layer.TwoHanded); + m_toDisarm = combatant.FindItemOnLayer(Layer.OneHanded) ?? combatant.FindItemOnLayer(Layer.TwoHanded); } - if (!Core.AOS && !Mobile.DisarmReady && Mobile.Skills.Wrestling.Value >= 80.0 && - Mobile.Skills.ArmsLore.Value >= 80.0 && _toDisarm != null) + if (!Core.AOS && !m_Mobile.DisarmReady && m_Mobile.Skills.Wrestling.Value >= 80.0 && + m_Mobile.Skills.ArmsLore.Value >= 80.0 && m_toDisarm != null) { - Fists.DisarmRequest(Mobile); + Fists.DisarmRequest(m_Mobile); } - if (_toDisarm?.IsChildOf(combatant.Backpack) == true && - Core.TickCount - Mobile.NextSkillTime >= 0 && _toDisarm.LootType != LootType.Blessed && - _toDisarm.LootType != LootType.Newbied) + if (m_toDisarm?.IsChildOf(combatant.Backpack) == true && + Core.TickCount - m_Mobile.NextSkillTime >= 0 && m_toDisarm.LootType != LootType.Blessed && + m_toDisarm.LootType != LootType.Newbied) { - DebugSay("Trying to steal from combatant."); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("Trying to steal from combatant."); + } - Mobile.UseSkill(SkillName.Stealing); - Mobile.Target?.Invoke(Mobile, _toDisarm); + m_Mobile.UseSkill(SkillName.Stealing); + m_Mobile.Target?.Invoke(m_Mobile, m_toDisarm); } - else if (_toDisarm == null && Core.TickCount - Mobile.NextSkillTime >= 0) + else if (m_toDisarm == null && Core.TickCount - m_Mobile.NextSkillTime >= 0) { - this.DebugSayFormatted($"Trying to steal from {combatant.Name}."); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"Trying to steal from {combatant.Name}."); + } - var didSteal = TryStealFrom(combatant); + bool didSteal = TryStealFrom(combatant); didSteal = TryStealFrom(combatant) || didSteal; didSteal = TryStealFrom(combatant) || didSteal; didSteal = TryStealFrom(combatant) || didSteal; if (!didSteal) { - this.DebugSayFormatted($"I am going to flee from {combatant.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); + } Action = ActionType.Flee; return true; @@ -90,13 +111,16 @@ public class ThiefAI : BaseAI // We are low on health, should we flee? // (10 + diff)% chance to flee - if (Mobile.Hits < Mobile.HitsMax * 20 / 100 && Mobile.CanFlee) + if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100 && m_Mobile.CanFlee) { - var fleeChance = 10 + Math.Max(0, combatant.Hits - Mobile.Hits); + var fleeChance = 10 + Math.Max(0, combatant.Hits - m_Mobile.Hits); if (Utility.Random(0, 100) > fleeChance) { - this.DebugSayFormatted($"I am going to flee from {combatant.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); + } Action = ActionType.Flee; } @@ -104,9 +128,12 @@ public class ThiefAI : BaseAI return true; } - if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, Mobile.Combatant)) + if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, m_Mobile.Combatant)) { - this.DebugSayFormatted($"I used my abilities on {Mobile.Combatant.Name}!"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I used my abilities on {m_Mobile.Combatant.Name}!"); + } } return true; } @@ -116,8 +143,8 @@ public class ThiefAI : BaseAI Item steal = combatant.Backpack?.FindItemByType(); if (steal != null) { - Mobile.UseSkill(SkillName.Stealing); - Mobile.Target?.Invoke(Mobile, steal); + m_Mobile.UseSkill(SkillName.Stealing); + m_Mobile.Target?.Invoke(m_Mobile, steal); return true; } @@ -126,11 +153,14 @@ public class ThiefAI : BaseAI public override bool DoActionGuard() { - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { - this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + } - Mobile.Combatant = Mobile.FocusMob; + m_Mobile.Combatant = m_Mobile.FocusMob; Action = ActionType.Combat; } else @@ -143,15 +173,18 @@ public class ThiefAI : BaseAI public override bool DoActionFlee() { - if (Mobile.Hits > Mobile.HitsMax / 2) + if (m_Mobile.Hits > m_Mobile.HitsMax / 2) { - DebugSay("I am stronger now, so I will continue fighting"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("I am stronger now, so I will continue fighting"); + } Action = ActionType.Combat; } else { - Mobile.FocusMob = Mobile.Combatant; + m_Mobile.FocusMob = m_Mobile.Combatant; base.DoActionFlee(); } diff --git a/Projects/UOContent/Mobiles/AI/VendorAI.cs b/Projects/UOContent/Mobiles/AI/VendorAI.cs index a835ef61f..3540655b6 100644 --- a/Projects/UOContent/Mobiles/AI/VendorAI.cs +++ b/Projects/UOContent/Mobiles/AI/VendorAI.cs @@ -15,26 +15,38 @@ public class VendorAI : BaseAI public override bool DoActionWander() { - DebugSay("I'm fine"); - - if (Mobile.Combatant != null) + if (m_Mobile.Debug) { - this.DebugSayFormatted($"{Mobile.Combatant.Name} is attacking me"); - - Mobile.Say(GetRandomGuardMessage()); - Action = ActionType.Flee; + m_Mobile.DebugSay("I'm fine"); } - else if (Mobile.FocusMob != null) - { - this.DebugSayFormatted($"{Mobile.FocusMob.Name} has talked to me"); - Action = ActionType.Interact; + if (m_Mobile.Combatant != null) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"{m_Mobile.Combatant.Name} is attacking me"); + } + + m_Mobile.Say(GetRandomGuardMessage()); + Action = ActionType.Flee; } else { - Mobile.Warmode = false; + if (m_Mobile.FocusMob != null) + { + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"{m_Mobile.FocusMob.Name} has talked to me"); + } - base.DoActionWander(); + Action = ActionType.Interact; + } + else + { + m_Mobile.Warmode = false; + + base.DoActionWander(); + } } return true; @@ -42,38 +54,50 @@ public class VendorAI : BaseAI public override bool DoActionInteract() { - var customer = Mobile.FocusMob; + var customer = m_Mobile.FocusMob; - if (Mobile.Combatant != null) + if (m_Mobile.Combatant != null) { - this.DebugSayFormatted($"{Mobile.Combatant.Name} is attacking me"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"{m_Mobile.Combatant.Name} is attacking me"); + } - Mobile.Say(GetRandomGuardMessage()); + m_Mobile.Say(GetRandomGuardMessage()); Action = ActionType.Flee; return true; } - if (customer?.Deleted != false || customer.Map != Mobile.Map) + if (customer?.Deleted != false || customer.Map != m_Mobile.Map) { - DebugSay("My customer has disappeared"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay("My customer have disapeared"); + } - Mobile.FocusMob = null; + m_Mobile.FocusMob = null; Action = ActionType.Wander; } - else if (customer.InRange(Mobile, Mobile.RangeFight)) + else if (customer.InRange(m_Mobile, m_Mobile.RangeFight)) { - this.DebugSayFormatted($"I am with {customer.Name}"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"I am with {customer.Name}"); + } - Mobile.Direction = Mobile.GetDirectionTo(customer); + m_Mobile.Direction = m_Mobile.GetDirectionTo(customer); } else { - this.DebugSayFormatted($"{customer.Name} is gone"); + if (m_Mobile.Debug) + { + m_Mobile.DebugSay($"{customer.Name} is gone"); + } - Mobile.FocusMob = null; + m_Mobile.FocusMob = null; Action = ActionType.Wander; } @@ -82,13 +106,13 @@ public class VendorAI : BaseAI public override bool DoActionGuard() { - Mobile.FocusMob = Mobile.Combatant; + m_Mobile.FocusMob = m_Mobile.Combatant; return base.DoActionGuard(); } public override bool HandlesOnSpeech(Mobile from) { - if (from.InRange(Mobile, 4)) + if (from.InRange(m_Mobile, 4)) { return true; } @@ -103,7 +127,7 @@ public class VendorAI : BaseAI var from = e.Mobile; - if (Mobile is BaseVendor vendor && from.InRange(Mobile, Core.AOS ? 1 : 4) && !e.Handled) + if (m_Mobile is BaseVendor vendor && from.InRange(m_Mobile, Core.AOS ? 1 : 4) && !e.Handled) { if (e.HasKeyword(0x14D)) // *vendor sell* { diff --git a/Projects/UOContent/Mobiles/Abilities/AreaEffectMonsterAbility.cs b/Projects/UOContent/Mobiles/Abilities/AreaEffectMonsterAbility.cs index f7f83af8f..444681627 100644 --- a/Projects/UOContent/Mobiles/Abilities/AreaEffectMonsterAbility.cs +++ b/Projects/UOContent/Mobiles/Abilities/AreaEffectMonsterAbility.cs @@ -27,7 +27,7 @@ public abstract class AreaEffectMonsterAbility : MonsterAbility protected virtual bool CanEffectTarget(BaseCreature source, Mobile defender) => source != defender && defender.Alive && source.CanBeHarmful(defender) - && (defender.Player || defender is BaseCreature bc && (bc.Team != source.Team || bc.Controlled || bc.Summoned)) + && (defender.Player || defender is BaseCreature bc && (bc.Team == source.Team || bc.Controlled || bc.Summoned)) && (!Core.AOS || source.InLOS(defender)); protected abstract void DoEffectTarget(BaseCreature source, Mobile defender); diff --git a/Projects/UOContent/Mobiles/Abilities/DeathExplosion.cs b/Projects/UOContent/Mobiles/Abilities/DeathExplosion.cs index 7712e43ef..9a43e0f22 100644 --- a/Projects/UOContent/Mobiles/Abilities/DeathExplosion.cs +++ b/Projects/UOContent/Mobiles/Abilities/DeathExplosion.cs @@ -1,10 +1,11 @@ -using System; +using System; +using System.Collections.Generic; namespace Server.Mobiles; public class DeathExplosion : AreaEffectMonsterAbility { - private static int _chainDepth; + private static HashSet _exploding = new(); public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.Death; public override MonsterAbilityType AbilityType => MonsterAbilityType.DeathExplosion; @@ -17,25 +18,17 @@ public class DeathExplosion : AreaEffectMonsterAbility public override void Trigger(MonsterAbilityTrigger trigger, BaseCreature source, Mobile target) { - if (trigger != MonsterAbilityTrigger.Death || _chainDepth > 0) - { - return; - } - + _exploding.Add(source); source.FixedParticles(0x36BD, 20, 10, 5044, EffectLayer.Head); source.PlaySound(0x307); - _chainDepth++; - try - { - base.Trigger(trigger, source, target); - } - finally - { - _chainDepth--; - } + base.Trigger(trigger, source, target); + _exploding.Remove(source); } + protected override bool CanEffectTarget(BaseCreature source, Mobile defender) => + !_exploding.Contains(defender) && base.CanEffectTarget(source, defender); + protected override void DoEffectTarget(BaseCreature source, Mobile defender) { source.DoHarmful(defender); diff --git a/Projects/UOContent/Mobiles/Abilities/DestroyEquipment.cs b/Projects/UOContent/Mobiles/Abilities/DestroyEquipment.cs index ff7937ded..c74698d7c 100644 --- a/Projects/UOContent/Mobiles/Abilities/DestroyEquipment.cs +++ b/Projects/UOContent/Mobiles/Abilities/DestroyEquipment.cs @@ -1,5 +1,4 @@ using Server.Collections; -using Server.Engines.Insurance; using Server.Items; namespace Server.Mobiles; @@ -24,7 +23,7 @@ public class DestroyEquipment : MonsterAbilitySingleTarget continue; } - if (Insurance.Enabled && item.Insured) + if (Mobile.InsuranceEnabled && item.Insured) { continue; } diff --git a/Projects/UOContent/Mobiles/Abilities/FanningFire.cs b/Projects/UOContent/Mobiles/Abilities/FanningFire.cs index 99e65f5c6..a06bb561f 100644 --- a/Projects/UOContent/Mobiles/Abilities/FanningFire.cs +++ b/Projects/UOContent/Mobiles/Abilities/FanningFire.cs @@ -5,23 +5,8 @@ namespace Server.Mobiles; public class FanningFire : MonsterAbilitySingleTargetDoT { public override MonsterAbilityType AbilityType => MonsterAbilityType.FanningFire; - public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.CombatAction; - - public FanningFire(double chanceToTrigger, int fireResistMod, int minDamage, int maxDamage) - { - ChanceToTrigger = chanceToTrigger; - FireResistMod = fireResistMod; - MinDamage = minDamage; - MaxDamage = maxDamage; - } - - public sealed override double ChanceToTrigger { get; } - - public int FireResistMod { get; } - - public int MinDamage { get; } - - public int MaxDamage { get; } + public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.GiveDamage; + public override double ChanceToTrigger => 0.05; public const string Name = "FanningFire"; @@ -67,12 +52,16 @@ public class FanningFire : MonsterAbilitySingleTargetDoT */ source.DoHarmful(defender); - defender.AddResistanceMod(new ResistanceMod(ResistanceType.Fire, Name, FireResistMod)); + var effect = -(defender.FireResistance / 10); - defender.FixedParticles(0x3709, 10, 30, 0x34, EffectLayer.RightFoot); + var mod = new ResistanceMod(ResistanceType.Fire, Name, effect); + defender.AddResistanceMod(mod); + + defender.FixedParticles(0x37B9, 10, 30, 0x34, EffectLayer.RightFoot); defender.PlaySound(0x208); - AOS.Damage(defender, source, Utility.RandomMinMax(MinDamage, MaxDamage), 0, 100, 0, 0, 0); + // TODO: Trigger replaces a normal attack. + AOS.Damage(defender, source, Utility.RandomMinMax(35, 45), 0, 100, 0, 0, 0); } protected override void EffectTick(BaseCreature source, Mobile defender, ref TimeSpan nextDelay) diff --git a/Projects/UOContent/Mobiles/Abilities/MagicalBarrier.cs b/Projects/UOContent/Mobiles/Abilities/MagicalBarrier.cs index 2069448be..585a7695b 100644 --- a/Projects/UOContent/Mobiles/Abilities/MagicalBarrier.cs +++ b/Projects/UOContent/Mobiles/Abilities/MagicalBarrier.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; namespace Server.Mobiles; @@ -8,7 +7,7 @@ public class MagicalBarrier : MonsterAbility { private HashSet _inactiveField; - public bool HasField(Mobile source) => _inactiveField?.Contains(source) != true; + public bool HasField(Mobile source) => !_inactiveField.Contains(source); public override MonsterAbilityType AbilityType => MonsterAbilityType.MagicalBarrier; @@ -18,20 +17,17 @@ public class MagicalBarrier : MonsterAbility public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(10.0); public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(10.0); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool CanUseField(BaseCreature source) => source.Hits >= source.HitsMax * 9 / 10; - // Regeneration is subject to the cooldown, the rest are not. public override bool CanTrigger(BaseCreature source, MonsterAbilityTrigger trigger) => trigger != MonsterAbilityTrigger.Think || base.CanTrigger(source, trigger); public override void Trigger(MonsterAbilityTrigger trigger, BaseCreature source, Mobile target) { - if (trigger == MonsterAbilityTrigger.Think) + if (trigger == MonsterAbilityTrigger.Think && !source.IsHurt()) { - if (CanUseField(source) && _inactiveField?.Remove(source) == true) + var fieldUp = _inactiveField?.Remove(source) == true; + if (fieldUp) { - // Field going up! source.FixedParticles(0, 10, 0, 0x2530, EffectLayer.Waist); if (_inactiveField?.Count == 0) @@ -40,15 +36,6 @@ public class MagicalBarrier : MonsterAbility } } } - else if (trigger is MonsterAbilityTrigger.TakeSpellDamage && !CanUseField(source)) - { - _inactiveField ??= []; - if (_inactiveField.Add(source)) - { - // TODO: message and effect when field turns down; cannot be verified on OSI due to a bug - source.FixedParticles(0x3735, 1, 30, 0x251F, EffectLayer.Waist); - } - } base.Trigger(trigger, source, target); } @@ -66,13 +53,22 @@ public class MagicalBarrier : MonsterAbility public override void AlterSpellDamageFrom(BaseCreature source, Mobile target, ref int damage) { - if (!HasField(source)) + var canUseField = source.Hits >= source.HitsMax * 9 / 10; + // If we cannot use the field, deactivate it. + var fieldActive = canUseField ? HasField(source) : _inactiveField.Add(source); + + if (!fieldActive) { damage = 0; // no spell damage when the field is down // should there be an effect when spells nullifying is on? source.FixedParticles(0, 10, 0, 0x2522, EffectLayer.Waist); target.SendLocalizedMessage(1114359); // Your attack has no effect on the creature's armor. } + else if (!canUseField) + { + // TODO: message and effect when field turns down; cannot be verified on OSI due to a bug + source.FixedParticles(0x3735, 1, 30, 0x251F, EffectLayer.Waist); + } } public override void Move(BaseCreature source, Direction d) diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbilities.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbilities.cs index 0afdf05de..9df7ffd0a 100644 --- a/Projects/UOContent/Mobiles/Abilities/MonsterAbilities.cs +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbilities.cs @@ -13,9 +13,8 @@ public static class MonsterAbilities // Resistance Debuffs public static GraspingClaw GraspingClaw => new(); - public static TailSwipe TailSwipe => new(); public static RuneCorruption RuneCorruption => new(); - public static FanningFire FanningFire => new(0.05, -10, 35, 45); + public static FanningFire FanningFire => new(); // Summon Undead public static SummonSkeletonsCounter SummonSkeletonsCounter => new(); diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbility.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbility.cs index fc227507e..72fb61ec3 100644 --- a/Projects/UOContent/Mobiles/Abilities/MonsterAbility.cs +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbility.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -using ModernUO.CodeGeneratedEvents; namespace Server.Mobiles; /// /// Abstract class used to build singletons for managing a specific monster ability. /// -public abstract class MonsterAbility +public abstract partial class MonsterAbility { private Dictionary _nextTriggerTicks; @@ -19,34 +18,39 @@ public abstract class MonsterAbility public virtual TimeSpan MinTriggerCooldown => TimeSpan.Zero; public virtual TimeSpan MaxTriggerCooldown => TimeSpan.Zero; + public bool WillTrigger(MonsterAbilityTrigger trigger) => (AbilityTrigger & trigger) != 0; + /// /// Returns true if ability is not on cooldown, and the chance to trigger succeeds. /// /// Boolean indicating the ability can trigger. public virtual bool CanTrigger(BaseCreature source, MonsterAbilityTrigger trigger) { - if ((AbilityTrigger & trigger) == 0 || source is not { Alive: true, Deleted: false }) + if (source is not { Alive: true, Deleted: false }) { return false; } - if (_nextTriggerTicks?.TryGetValue(source, out var nextTrigger) == true) + if (_nextTriggerTicks?.TryGetValue(source, out var nextTrigger) == true && nextTrigger - Core.TickCount > 0) { - if (nextTrigger - Core.TickCount > 0) - { - return false; - } - - _nextTriggerTicks.Remove(source); - if (_nextTriggerTicks.Count == 0) - { - _nextTriggerTicks = null; - } + return false; } var c = ChanceToTrigger; - return c >= 1 || c > 0 && c > Utility.RandomDouble(); + if (c >= 1) + { + return true; + } + + if (c <= 0) + { + return false; + } + + var rnd = Utility.RandomDouble(); + + return c > rnd; } /// @@ -95,23 +99,7 @@ public abstract class MonsterAbility { } - public virtual void Move(BaseCreature source, Direction d) + public virtual void Move(BaseCreature creature, Direction d) { } - - [OnEvent(nameof(BaseCreature.CreatureDeathEvent))] - [OnEvent(nameof(BaseCreature.CreatureDeletedEvent))] - public static void InvalidateNextAbilityTriggers(BaseCreature source) - { - var abilities = source.GetMonsterAbilities(); - if (abilities == null || abilities.Length == 0) - { - return; - } - - for (var i = 0; i < abilities.Length; i++) - { - abilities[i]._nextTriggerTicks?.Remove(source); - } - } } diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbilityGroup.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityGroup.cs index 6555a3682..c431d4d35 100644 --- a/Projects/UOContent/Mobiles/Abilities/MonsterAbilityGroup.cs +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityGroup.cs @@ -1,14 +1,15 @@ using System; +using Server.Random; using WeightedMonsterAbility = Server.Random.WeightedValue; namespace Server.Mobiles; public class MonsterAbilityGroup : MonsterAbility { - private readonly WeightedMonsterAbility[] _weightedAbilities; - private readonly WeightedMonsterAbility[] _availableToTrigger; - private readonly MonsterAbilityTrigger _triggers; + private WeightedMonsterAbility[] _weightedAbilities; + private WeightedMonsterAbility[] _availableToTrigger; private int _availableToTriggerCount; + private MonsterAbilityTrigger _triggers; public MonsterAbilityGroup(params WeightedMonsterAbility[] weightedAbilities) { @@ -17,7 +18,8 @@ public class MonsterAbilityGroup : MonsterAbility for (var i = 0; i < _weightedAbilities.Length; i++) { - _triggers |= _weightedAbilities[i].Value.AbilityTrigger; + var weightedAbility = _weightedAbilities[i]; + _triggers |= weightedAbility.Value.AbilityTrigger; } } @@ -64,7 +66,7 @@ public class MonsterAbilityGroup : MonsterAbility for (var i = 0; i < _weightedAbilities.Length; i++) { var weightedAbility = _weightedAbilities[i]; - if (weightedAbility.Value.CanTrigger(source, trigger)) + if (weightedAbility.Value.WillTrigger(trigger) && weightedAbility.Value.CanTrigger(source, trigger)) { _availableToTrigger[_availableToTriggerCount++] = weightedAbility; } @@ -81,7 +83,7 @@ public class MonsterAbilityGroup : MonsterAbility return; } - var slice = new ReadOnlySpan(_availableToTrigger, 0, _availableToTriggerCount); + var slice = new ReadOnlySpan>(_availableToTrigger, 0, _availableToTriggerCount); var chosenAbility = slice.RandomWeightedElement().Value; // Just in case? diff --git a/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs index fe0fe46f4..9f188243d 100644 --- a/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs +++ b/Projects/UOContent/Mobiles/Abilities/MonsterAbilityType.cs @@ -20,6 +20,5 @@ public enum MonsterAbilityType FanningFire, // Fire debuff RuneCorruption, FanThrow, - BloodBath, - TailSwipe + BloodBath } diff --git a/Projects/UOContent/Mobiles/Abilities/ReflectPhysicalDamage.cs b/Projects/UOContent/Mobiles/Abilities/ReflectPhysicalDamage.cs index 3aa48960f..ae66734ba 100644 --- a/Projects/UOContent/Mobiles/Abilities/ReflectPhysicalDamage.cs +++ b/Projects/UOContent/Mobiles/Abilities/ReflectPhysicalDamage.cs @@ -8,8 +8,4 @@ public class ReflectPhysicalDamage : MonsterAbility public override MonsterAbilityType AbilityType => MonsterAbilityType.ReflectPhysicalDamage; public virtual int PercentReflected => 10; - - public override void Trigger(MonsterAbilityTrigger trigger, BaseCreature source, Mobile target) - { - } } diff --git a/Projects/UOContent/Mobiles/Abilities/Summon Undead/SummonUndeadCounter.cs b/Projects/UOContent/Mobiles/Abilities/Summon Undead/SummonUndeadCounter.cs index 3666d21ef..0e634dcca 100644 --- a/Projects/UOContent/Mobiles/Abilities/Summon Undead/SummonUndeadCounter.cs +++ b/Projects/UOContent/Mobiles/Abilities/Summon Undead/SummonUndeadCounter.cs @@ -27,7 +27,7 @@ public abstract class SummonUndeadCounter : MonsterAbility var amount = AmountToSummon - source.Followers; var distance = SummonRange; - var willTransform = !source.Paralyzed && ChanceToPolymorph > 0 && ChanceToPolymorph > Utility.RandomDouble() + int willTransform = !source.Paralyzed && ChanceToPolymorph > 0 && ChanceToPolymorph > Utility.RandomDouble() ? Utility.Random(amount) : -1; diff --git a/Projects/UOContent/Mobiles/Abilities/TailSwipe.cs b/Projects/UOContent/Mobiles/Abilities/TailSwipe.cs deleted file mode 100644 index 9982355b2..000000000 --- a/Projects/UOContent/Mobiles/Abilities/TailSwipe.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; - -namespace Server.Mobiles; - -public class TailSwipe : MonsterAbilitySingleTarget -{ - public override MonsterAbilityType AbilityType => MonsterAbilityType.TailSwipe; - public override MonsterAbilityTrigger AbilityTrigger => MonsterAbilityTrigger.GiveMeleeDamage; - public override double ChanceToTrigger => 0.3; - - // ServUO's SpecialAbility base cooldown; TailSwipe does not override it. - public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(30); - public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(30); - - protected override void OnTarget(MonsterAbilityTrigger trigger, BaseCreature source, Mobile defender) - { - source.DoHarmful(defender); - - defender.PlaySound(0x204); - defender.FixedEffect(0x376A, 6, 1); - - // OSI/Stratics reports roughly a 90% stun / 10% confuse split (ServUO's even 50/50 is incorrect). - if (Utility.RandomDouble() < 0.10) - { - defender.SendLocalizedMessage(1112555); // You're left confused as the creature's tail catches you right in the face! - defender.AddStatMod(new StatMod(StatType.Dex, "[TailSwipe] Dex", -20, TimeSpan.FromSeconds(5.0))); - defender.AddStatMod(new StatMod(StatType.Int, "[TailSwipe] Int", -20, TimeSpan.FromSeconds(5.0))); - } - else - { - defender.SendLocalizedMessage(1112554); // You're stunned as the creature's tail knocks the wind out of you. - defender.Paralyze(TimeSpan.FromSeconds(3.0)); - } - } -} diff --git a/Projects/UOContent/Mobiles/Animals/Misc/PackHorse.cs b/Projects/UOContent/Mobiles/Animals/Misc/PackHorse.cs index ca2d4e3dd..ab111f166 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/PackHorse.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/PackHorse.cs @@ -155,7 +155,7 @@ namespace Server.Mobiles if (pack != null) { - var newPack = new Backpack(); + Container newPack = new Backpack(); for (var i = pack.Items.Count - 1; i >= 0; --i) { diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs b/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs index 741093110..b43c34272 100644 --- a/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs +++ b/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs @@ -5,14 +5,9 @@ using System.Runtime.CompilerServices; namespace Server.Mobiles { - [SerializationGenerator(1, false)] + [SerializationGenerator(0, false)] public partial class Sheep : BaseCreature, ICarvable { - private void MigrateFrom(V0Content content) - { - _nextWoolTime = content.NextWoolTime; - } - [Constructible] public Sheep() : base(AIType.AI_Animal, FightMode.Aggressor) { @@ -48,14 +43,18 @@ namespace Server.Mobiles public override string CorpseName => "a sheep corpse"; - [SerializableField(0, fieldChanged: nameof(OnNextWoolTimeChanged))] - [AnchoredDateTime] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private DateTime _nextWoolTime; - - private void OnNextWoolTimeChanged(DateTime oldValue, DateTime newValue) + [DeltaDateTime] + [SerializableProperty(0)] + [CommandProperty(AccessLevel.GameMaster)] + public DateTime NextWoolTime { - SheepBody(); + get => _nextWoolTime; + set + { + _nextWoolTime = value; + SheepBody(); + this.MarkDirty(); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/BaseMount.cs b/Projects/UOContent/Mobiles/Animals/Mounts/BaseMount.cs index 956f13034..7b6389854 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/BaseMount.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/BaseMount.cs @@ -15,7 +15,7 @@ public abstract partial class BaseMount : BaseCreature, IMount int itemID, AIType aiType, FightMode fightMode = FightMode.Closest, - int rangePerception = DefaultRangePerception, + int rangePerception = 10, int rangeFight = 1 ) : base(aiType, fightMode, rangePerception, rangeFight) { diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs b/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs index f6dc13b40..f13cebcbe 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/Ethereals.cs @@ -11,19 +11,19 @@ namespace Server.Mobiles public partial class EtherealMount : Item, IMount, IMountItem, IRewardItem { [SerializableField(0)] - [SaveFlag(nameof(ShouldSerializeIsDonationItem))] [SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)] public bool _isDonationItem; [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SerializableFieldSaveFlag(0)] public bool ShouldSerializeIsDonationItem() => _isDonationItem; [SerializableField(1)] - [SaveFlag(nameof(ShouldSerializeIsRewardItem))] [SerializedCommandProperty(AccessLevel.GameMaster)] public bool _isRewardItem; [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SerializableFieldSaveFlag(1)] public bool ShouldSerializeIsRewardItem() => _isRewardItem; [Constructible] @@ -40,27 +40,43 @@ namespace Server.Mobiles public override double DefaultWeight => 1.0; - [SerializableField(2, fieldChanged: nameof(OnMountedIDChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _mountedID; - - private void OnMountedIDChanged(int oldValue, int newValue) + [SerializableProperty(2)] + [CommandProperty(AccessLevel.GameMaster)] + public int MountedID { - if (_rider != null) + get => _mountedID; + set { - ItemID = newValue; + if (_mountedID != value) + { + _mountedID = value; + + if (_rider != null) + { + ItemID = value; + } + this.MarkDirty(); + } } } - [SerializableField(3, fieldChanged: nameof(OnRegularIDChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _regularID; - - private void OnRegularIDChanged(int oldValue, int newValue) + [SerializableProperty(3)] + [CommandProperty(AccessLevel.GameMaster)] + public int RegularID { - if (_rider == null) + get => _regularID; + set { - ItemID = newValue; + if (_regularID != value) + { + _regularID = value; + + if (_rider == null) + { + ItemID = value; + } + this.MarkDirty(); + } } } @@ -71,7 +87,6 @@ namespace Server.Mobiles public virtual int EtherealHue => 0x4001; [SerializableProperty(4)] - [SaveFlag(nameof(ShouldSerializeRider))] [CommandProperty(AccessLevel.GameMaster)] public Mobile Rider { @@ -109,19 +124,22 @@ namespace Server.Mobiles } } + [SerializableFieldSaveFlag(4)] private bool ShouldSerializeRider() => _rider != null; - [SerializableField(5, allowFieldChange: nameof(AllowStepsChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [SaveFlag(nameof(ShouldSerializeSteps))] - private int _steps; - - private bool AllowStepsChange(ref int value) + [CommandProperty(AccessLevel.GameMaster)] + [SerializableProperty(5)] + public int Steps { - value = Math.Clamp(value, 0, StepsMax); - return true; + get => _steps; + set + { + _steps = Math.Clamp(value, 0, StepsMax); + this.MarkDirty(); + } } + [SerializableFieldSaveFlag(5)] private bool ShouldSerializeSteps() => _steps != StepsMax; public virtual int StepsMax => 3840; // Should be same as horse diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs b/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs index 19a061af2..69bd9ce56 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/SwampDragon.cs @@ -62,37 +62,48 @@ namespace Server.Mobiles [SerializableField(3)] private int _bardingHP; - [SerializableField(2, fieldChanged: nameof(OnHasBardingChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private bool _hasBarding; - - private void OnHasBardingChanged(bool oldValue, bool newValue) + [CommandProperty(AccessLevel.GameMaster)] + [SerializableProperty(2)] + public bool HasBarding { - if (_hasBarding) + get => _hasBarding; + set { - Hue = CraftResources.GetHue(_bardingResource); - Body = 0x31F; - ItemID = 0x3EBE; - } - else - { - Hue = 0x851; - Body = 0x31A; - ItemID = 0x3EBD; + _hasBarding = value; + + if (_hasBarding) + { + Hue = CraftResources.GetHue(_bardingResource); + Body = 0x31F; + ItemID = 0x3EBE; + } + else + { + Hue = 0x851; + Body = 0x31A; + ItemID = 0x3EBD; + } + InvalidateProperties(); + this.MarkDirty(); } } - [SerializableField(4, fieldChanged: nameof(OnBardingResourceChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private CraftResource _bardingResource; - - private void OnBardingResourceChanged(CraftResource oldValue, CraftResource newValue) + [CommandProperty(AccessLevel.GameMaster)] + [SerializableProperty(4)] + public CraftResource BardingResource { - if (_hasBarding) + get => _bardingResource; + set { - Hue = CraftResources.GetHue(newValue); + _bardingResource = value; + + if (_hasBarding) + { + Hue = CraftResources.GetHue(value); + } + + InvalidateProperties(); + this.MarkDirty(); } } diff --git a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs index 4d0751456..8a439a98c 100644 --- a/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs +++ b/Projects/UOContent/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs @@ -8,12 +8,8 @@ namespace Server.Mobiles public override string DefaultName => "a war horse"; public BaseWarHorse( - int bodyID, - int itemID, - AIType aiType = AIType.AI_Melee, - FightMode fightMode = FightMode.Aggressor, - int rangePerception = DefaultRangePerception, - int rangeFight = 1 + int bodyID, int itemID, AIType aiType = AIType.AI_Melee, FightMode fightMode = FightMode.Aggressor, + int rangePerception = 10, int rangeFight = 1 ) : base( bodyID, itemID, diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index f64a289f4..dc7e91ceb 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -56,12 +56,11 @@ namespace Server.Mobiles Attack, // "(All/Name) kill", // "(All/Name) attack" All or the specified pet(s) currently under your control attack the target. - Patrol, // "(Name) patrol" Roves between two or more guarded targets. - Release, // "(Name) release" Releases pet back into the wild (removes "tame" status). - Stay, // "(All/Name) stay" All or the specified pet(s) will stop and stay in current spot. - Stop, // "(All/Name) stop Cancels any current orders to attack, guard or follow. - Transfer, // "(Name) transfer" Transfers complete ownership to targeted player. - Rename // "(Name) rename" Changes the name of the pet. + Patrol, // "(Name) patrol" Roves between two or more guarded targets. + Release, // "(Name) release" Releases pet back into the wild (removes "tame" status). + Stay, // "(All/Name) stay" All or the specified pet(s) will stop and stay in current spot. + Stop, // "(All/Name) stop Cancels any current orders to attack, guard or follow. + Transfer // "(Name) transfer" Transfers complete ownership to targeted player. } [Flags] @@ -133,6 +132,30 @@ namespace Server.Mobiles public int CompareTo(DamageStore ds) => (ds?.m_Damage ?? 0).CompareTo(m_Damage); } + [AttributeUsage(AttributeTargets.Class)] + public class FriendlyNameAttribute : Attribute + { + public FriendlyNameAttribute(TextDefinition friendlyName) => FriendlyName = friendlyName; + // future use: Talisman 'Protection/Bonus vs. Specific Creature + + public TextDefinition FriendlyName { get; } + + public static TextDefinition GetFriendlyNameFor(Type t) + { + if (t.IsDefined(typeof(FriendlyNameAttribute), false)) + { + var objs = t.GetCustomAttributes(typeof(FriendlyNameAttribute), false); + + if (objs.Length > 0) + { + return (objs[0] as FriendlyNameAttribute)?.FriendlyName ?? ""; + } + } + + return t.Name; + } + } + public abstract partial class BaseCreature : Mobile, IHonorTarget, IQuestGiver { public enum Allegiance @@ -265,12 +288,8 @@ namespace Server.Mobiles private double _passiveSpeed; private double _currentSpeed; - // Movement clock (seconds per step); 0 = inherit the matching think value. - private double _activeMoveSpeed; - private double _passiveMoveSpeed; - - // Herding - forces the mob to walk to a specific location, paced by the movement - // clock at HerdingMoveSpeed. Thinking is unaffected. + // Herding - Overrides the AI to force the mob to move to a specific location + // Thinking: 0.3s, Movement: 0.6s. private IPoint2D _targetLocation; private int m_DamageMax = -1; @@ -345,12 +364,7 @@ namespace Server.Mobiles FightMode = mode; - GetSpeeds(out var activeSpeed, out var passiveSpeed); - GetMoveSpeeds(out _activeMoveSpeed, out _passiveMoveSpeed); - - ActiveSpeed = activeSpeed; - PassiveSpeed = passiveSpeed; - CurrentSpeed = passiveSpeed; + ResetSpeeds(); m_Team = 0; @@ -434,8 +448,6 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster)] public bool IsPrisoner { get; set; } - public virtual bool FollowsAcquireRules => true; - protected DateTime SummonEnd { get; set; } public virtual Faction FactionAllegiance => null; @@ -562,8 +574,6 @@ namespace Server.Mobiles public virtual bool AlwaysMurderer => false; - public override bool Murderer => AlwaysMurderer || base.Murderer; - public virtual bool AlwaysAttackable => false; [CommandProperty(AccessLevel.GameMaster)] @@ -665,20 +675,12 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster)] public int RangePerception { get; set; } - /// - /// How far a chase may stretch before the creature gives up its combatant. Between - /// RangePerception and this leash it keeps chasing but may switch to closer targets. - /// - [CommandProperty(AccessLevel.GameMaster)] - public virtual int ChaseLeashRange => RangePerception * 2; - [CommandProperty(AccessLevel.GameMaster)] public int RangeFight { get; set; } [CommandProperty(AccessLevel.GameMaster)] public int RangeHome { get; set; } = 10; - /// Seconds per AI decision while engaged; see for movement pace. [CommandProperty(AccessLevel.GameMaster)] public virtual double ActiveSpeed { @@ -692,7 +694,6 @@ namespace Server.Mobiles } } - /// Seconds per AI decision while idle; see for movement pace. [CommandProperty(AccessLevel.GameMaster)] public virtual double PassiveSpeed { @@ -707,37 +708,21 @@ namespace Server.Mobiles } } - /// Seconds per step while engaged. Inherits ; set 0 to re-inherit. - [CommandProperty(AccessLevel.GameMaster)] - public virtual double ActiveMoveSpeed - { - get => _activeMoveSpeed > 0 ? _activeMoveSpeed : _activeSpeed; - set => _activeMoveSpeed = value > 0 ? value : 0; - } - - /// Seconds per step while idle. Inherits ; set 0 to re-inherit. - [CommandProperty(AccessLevel.GameMaster)] - public virtual double PassiveMoveSpeed - { - get => _passiveMoveSpeed > 0 ? _passiveMoveSpeed : _passiveSpeed; - set => _passiveMoveSpeed = value > 0 ? value : 0; - } - - // Herded creatures walk at a fixed standard pace regardless of their own speed - // (RunUO's forced 0.3, without its TransformMoveDelay inflation to 0.6). - private const double HerdingMoveSpeed = 0.3; - [CommandProperty(AccessLevel.GameMaster)] public IPoint2D TargetLocation { get => _targetLocation; - set => _targetLocation = value; + set + { + _targetLocation = value; + AIObject?.OnCurrentSpeedChanged(); + } } [CommandProperty(AccessLevel.GameMaster)] public double CurrentSpeed { - get => _currentSpeed; + get => _targetLocation != null ? 0.3 : _currentSpeed; set { if (Math.Abs(_currentSpeed - value) > 0.0001) @@ -748,26 +733,8 @@ namespace Server.Mobiles } } - /// - /// Resolved seconds per step: a verbatim active/passive - /// maps to the matching movement value; a bespoke pace stays fused to both clocks. - /// A herded creature is always driven at . - /// [CommandProperty(AccessLevel.GameMaster)] - public double CurrentMoveSpeed - { - get - { - if (_targetLocation != null) - { - return HerdingMoveSpeed; - } - - return _currentSpeed == _activeSpeed ? ActiveMoveSpeed - : _currentSpeed == _passiveSpeed ? PassiveMoveSpeed - : _currentSpeed; - } - } + public double MoveSpeedMod { get; set; } [CommandProperty(AccessLevel.GameMaster)] public Point3D Home @@ -851,10 +818,9 @@ namespace Server.Mobiles get => m_ControlOrder; set { - var previous = m_ControlOrder; m_ControlOrder = value; - AIObject?.OnCurrentOrderChanged(previous); + AIObject?.OnCurrentOrderChanged(); InvalidateProperties(); @@ -958,6 +924,8 @@ namespace Server.Mobiles public virtual bool ReturnsToHome => SeeksHome && Home != Point3D.Zero && !m_ReturnQueued && !Controlled && !Summoned; + public virtual bool ScaleSpeedByDex => NPCSpeeds.ScaleSpeedByDex && !IsMonster; + // used for deleting untamed creatures [in houses] [CommandProperty(AccessLevel.GameMaster)] public bool RemoveIfUntamed { get; set; } @@ -1179,8 +1147,6 @@ namespace Server.Mobiles public virtual MonsterAbility[] GetMonsterAbilities() => null; - private MonsterAbilityTrigger _activeTriggers; - public virtual MonsterAbility GetAbility(MonsterAbilityType type) { var abilities = GetMonsterAbilities(); @@ -1237,11 +1203,6 @@ namespace Server.Mobiles public virtual bool TriggerAbility(MonsterAbilityTrigger trigger, Mobile defender) { - if ((_activeTriggers & trigger) != 0) - { - return false; - } - var abilities = GetMonsterAbilities(); if (abilities == null) @@ -1249,26 +1210,18 @@ namespace Server.Mobiles return false; } - _activeTriggers |= trigger; - try + var triggered = false; + for (var i = 0; i < abilities.Length; i++) { - var triggered = false; - for (var i = 0; i < abilities.Length; i++) + var ability = abilities[i]; + if (ability.WillTrigger(trigger) && ability.CanTrigger(this, trigger)) { - var ability = abilities[i]; - if (ability.CanTrigger(this, trigger)) - { - ability.Trigger(trigger, this, defender); - triggered = true; - } + ability.Trigger(trigger, this, defender); + triggered = true; } + } - return triggered; - } - finally - { - _activeTriggers &= ~trigger; - } + return triggered; } public virtual void TriggerAbilityMove(MonsterAbilityTrigger trigger, Mobile defender, Direction d) @@ -1283,7 +1236,7 @@ namespace Server.Mobiles for (var i = 0; i < abilities.Length; i++) { var ability = abilities[i]; - if (ability.CanTrigger(this, trigger)) + if (ability.WillTrigger(trigger) && ability.CanTrigger(this, trigger)) { ability.Move(this, d); } @@ -1302,7 +1255,7 @@ namespace Server.Mobiles for (var i = 0; i < abilities.Length; i++) { var ability = abilities[i]; - if (ability.CanTrigger(this, trigger)) + if (ability.WillTrigger(trigger) && ability.CanTrigger(this, trigger)) { if ((trigger & MonsterAbilityTrigger.GiveMeleeDamage) != 0) { @@ -1343,7 +1296,7 @@ namespace Server.Mobiles for (var i = 0; i < abilities.Length; i++) { var ability = abilities[i]; - if (ability.CanTrigger(this, trigger)) + if (ability.WillTrigger(trigger) && ability.CanTrigger(this, trigger)) { if ((trigger & MonsterAbilityTrigger.GiveSpellDamage) != 0) { @@ -1527,10 +1480,6 @@ namespace Server.Mobiles { var oldHits = Hits; - // Blood oath reflects the original damage the attacker dealt, before other modifiers. - var hasBloodOath = from != null && BloodOathSpell.GetBloodOath(from) == this; - var reflectedDamage = hasBloodOath ? amount : 0; - if (Core.AOS && !Summoned && Controlled && Utility.RandomDouble() < 0.2) { amount = (int)(amount * BonusPetDamageScalar); @@ -1541,24 +1490,14 @@ namespace Server.Mobiles amount = (int)(amount * 1.25); } - if (hasBloodOath) + if (from != null && BloodOathSpell.GetBloodOath(from) == this) { - amount = (int)(amount * 1.2); + amount = (int)(amount * 1.1); + from.Damage(amount, from); } base.Damage(amount, from, informMount); - // If the blood oath caster will die then damage is not reflected back to the attacker. - if (hasBloodOath && Alive && !Deleted && !IsDeadBondedPet) - { - // Reflect the original damage back to the attacker, attributed to the caster. - // The caster is a creature, so the Publish 48 (SA+) resist mitigation applies. - from.Damage( - BloodOathSpell.ComputeReflectedDamage(reflectedDamage, from.Skills.MagicResist.Value, Core.SA), - this - ); - } - if (SubdueBeforeTame && !Controlled && oldHits > HitsMax / 10 && Hits <= HitsMax / 10) { // * The creature has been beaten into subjugation! * @@ -1576,6 +1515,15 @@ namespace Server.Mobiles } } + public override void OnRawDexChange(int oldValue) + { + // This only really happens for pets or when a GM modifies a mob. + if (oldValue != RawDex && ScaleSpeedByDex) + { + ResetSpeeds(); + } + } + public override void OnBeforeSpawn(Point3D location, Map m) { if (Paragon.CheckConvert(this, location, m)) @@ -1671,9 +1619,16 @@ namespace Server.Mobiles ReceivedHonorContext?.OnTargetDamaged(from, amount); - if (!willKill && CanBeDistracted && ControlOrder == OrderType.Follow) + if (!willKill) { - CheckDistracted(from); + if (CanBeDistracted && ControlOrder == OrderType.Follow) + { + CheckDistracted(from); + } + } + else if (from is PlayerMobile mobile) + { + Timer.StartTimer(TimeSpan.FromSeconds(10), mobile.RecoverAmmo); } base.OnDamage(amount, from, willKill); @@ -1891,7 +1846,7 @@ namespace Server.Mobiles { base.Serialize(writer); - writer.Write(22); // version + writer.Write(20); // version writer.Write((int)m_CurrentAI); writer.Write((int)m_DefaultAI); @@ -1928,7 +1883,7 @@ namespace Server.Mobiles if (_summoned) { - writer.WriteAnchoredTime(SummonEnd); + writer.WriteDeltaTime(SummonEnd); } writer.Write(ControlSlots); @@ -2011,10 +1966,6 @@ namespace Server.Mobiles // Version 19 writer.Write(HomeMap); - - // Version 22 (0 = inherit the matching think value) - writer.Write(_activeMoveSpeed); - writer.Write(_passiveMoveSpeed); } public override void Deserialize(IGenericReader reader) @@ -2087,7 +2038,7 @@ namespace Server.Mobiles if (_summoned) { - SummonEnd = version >= 21 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime(); + SummonEnd = reader.ReadDeltaTime(); new UnsummonTimer(this, SummonEnd - Core.Now).Start(); } @@ -2218,16 +2169,6 @@ namespace Server.Mobiles HomeMap = reader.ReadMap(); } - if (version >= 22) - { - _activeMoveSpeed = reader.ReadDouble(); - _passiveMoveSpeed = reader.ReadDouble(); - } - else - { - MigrateMoveSpeeds(); - } - if (version <= 14 && m_Paragon && Hue == 0x31) { Hue = Paragon.Hue; // Paragon hue fixed, should now be 0x501. @@ -2315,7 +2256,7 @@ namespace Server.Mobiles public void ChangeAIType(AIType newAI) { - AIObject?.AITimer.Stop(); + AIObject?.m_Timer.Stop(); if (ForcedAI != null) { @@ -2438,7 +2379,7 @@ namespace Server.Mobiles { if (AIObject != null) { - AIObject.AITimer?.Stop(); + AIObject.m_Timer?.Stop(); AIObject = null; } @@ -2471,6 +2412,13 @@ namespace Server.Mobiles base.OnAfterDelete(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DebugSay(string text) + { + // Moved the debug check to implementation layer so we can avoid string formatting when we do not need it + PublicOverheadMessage(MessageType.Regular, 41, false, text); + } + /* * This function can be overridden.. so a "Strongest" mobile, can have a different definition depending * on who check for value @@ -2489,7 +2437,7 @@ namespace Server.Mobiles { FightMode.Strongest => m.Skills.Tactics.Value + m.Str, // returns strongest mobile FightMode.Weakest => -m.Hits, // returns weakest mobile - _ => -this.GetDistanceToSqrt(m) + _ => -GetDistanceToSqrt(m) }; } @@ -2511,7 +2459,7 @@ namespace Server.Mobiles public bool IsHurt() => Hits != HitsMax; - public double GetHomeDistance() => this.GetDistanceToSqrt(m_Home); + public double GetHomeDistance() => GetDistanceToSqrt(m_Home); public virtual int GetTeamSize(int iRange) { @@ -2548,7 +2496,7 @@ namespace Server.Mobiles } else { - AIObject.DebugSay("I'm being attacked but my master told me not to fight."); + DebugSay("I'm being attacked but my master told me not to fight."); Warmode = false; return; } @@ -2713,10 +2661,13 @@ namespace Server.Mobiles if (ai.Defender == target) { - var master = GetMaster(); - if (master?.Player == true && master.CanBeHarmful(target, false)) + if (m_ControlMaster?.Player == true && m_ControlMaster.CanBeHarmful(target, false)) { - master.DoHarmful(target, true); + m_ControlMaster.DoHarmful(target, true); + } + else if (m_SummonMaster?.Player == true && m_SummonMaster.CanBeHarmful(target, false)) + { + m_SummonMaster.DoHarmful(target, true); } return; @@ -2763,7 +2714,19 @@ namespace Server.Mobiles if (Body.IsHuman) { - CheckedAnimate(Utility.RandomBool() ? 5 : 6, 5, 1, true, false, 1); + switch (Utility.Random(2)) + { + case 0: + { + CheckedAnimate(5, 5, 1, true, true, 1); + break; + } + case 1: + { + CheckedAnimate(6, 5, 1, true, false, 1); + break; + } + } } else if (Body.IsAnimal) { @@ -2788,7 +2751,19 @@ namespace Server.Mobiles } else if (Body.IsMonster) { - CheckedAnimate(Utility.RandomBool() ? 17 : 18, 5, 1, true, false, 1); + switch (Utility.Random(2)) + { + case 0: + { + CheckedAnimate(17, 5, 1, true, false, 1); + break; + } + case 1: + { + CheckedAnimate(18, 5, 1, true, false, 1); + break; + } + } } PlaySound(GetIdleSound()); @@ -2852,7 +2827,7 @@ namespace Server.Mobiles public override void OnMovement(Mobile m, Point3D oldLocation) { - if (AcquireOnApproach && !Controlled && !Summoned && !BardPacified && FightMode != FightMode.Aggressor) + if (AcquireOnApproach && !Controlled && !Summoned && FightMode != FightMode.Aggressor) { if (InRange(m.Location, AcquireOnApproachRange) && !InRange(oldLocation, AcquireOnApproachRange) && CanBeHarmful(m) && IsEnemy(m)) @@ -2867,11 +2842,13 @@ namespace Server.Mobiles ForceReacquire(); } - SpeechType?.OnMovement(this, m, oldLocation); + var speechType = SpeechType; + + speechType?.OnMovement(this, m, oldLocation); /* Begin notice sound */ if ((!m.Hidden || m.AccessLevel == AccessLevel.Player) && m.Player && FightMode != FightMode.Aggressor && - FightMode != FightMode.None && Combatant == null && !Controlled && !Summoned && !BardPacified && + FightMode != FightMode.None && Combatant == null && !Controlled && !Summoned && InRange(m.Location, 18) && !InRange(oldLocation, 18)) { if (Body.IsMonster) @@ -2893,7 +2870,7 @@ namespace Server.Mobiles return; } - if (!Body.IsHuman || Murderer || AlwaysAttackable || !m.Murderer || + if (!Body.IsHuman || Kills >= 5 || AlwaysMurderer || AlwaysAttackable || m.Kills < 5 || !m.InRange(Location, 12) || !m.Alive) { return; @@ -3246,7 +3223,7 @@ namespace Server.Mobiles var topDamage = rights[0].m_Damage; - var minDamage = hitsMax switch + int minDamage = hitsMax switch { >= 3000 => topDamage / 16, >= 1000 => topDamage / 8, @@ -3284,7 +3261,7 @@ namespace Server.Mobiles } [GeneratedEvent(nameof(CreatureDeathEvent))] - public static partial void CreatureDeathEvent(BaseCreature bc); + public static partial void CreatureDeathEvent(Mobile m); public override void OnDeath(Container c) { @@ -3356,12 +3333,19 @@ namespace Server.Mobiles if (!Summoned && !NoKillAwards) { - var (totalFame, totalKarma) = Titles.ComputeKillAwards(this, Map); + var totalFame = Fame / 100; + var totalKarma = -Karma / 100; + + if (Map == Map.Felucca) + { + totalFame += totalFame / 10 * 3; + totalKarma += totalKarma / 10 * 3; + } var list = GetLootingRights(DamageEntries, HitsMax); - using var titles = PooledRefList.Create(); - var fame = PooledRefList.Create(); - var karma = PooledRefList.Create(); + var titles = new List(); + var fame = new List(); + var karma = new List(); var givenQuestKill = false; var givenFactionKill = false; @@ -3376,19 +3360,9 @@ namespace Server.Mobiles continue; } - if (!Core.UOR) - { - var killer = LastKiller is BaseCreature bc ? bc.GetDamageMaster(this) : LastKiller; + var party = Engines.PartySystem.Party.Get(ds.m_Mobile); - if (ds.m_Mobile == killer) - { - // If the titles system gets feature flagged, it will be supported - titles.Add(ds.m_Mobile); - fame.Add(totalFame); - karma.Add(totalKarma); - } - } - else if (Engines.PartySystem.Party.Get(ds.m_Mobile) is { } party) + if (party != null) { var divedFame = totalFame / party.Members.Count; var divedKarma = totalKarma / party.Members.Count; @@ -3466,9 +3440,6 @@ namespace Server.Mobiles Titles.AwardFame(titles[i], fame[i], true); Titles.AwardKarma(titles[i], karma[i], true); } - - fame.Dispose(); - karma.Dispose(); } base.OnDeath(c); @@ -3481,13 +3452,8 @@ namespace Server.Mobiles CreatureDeathEvent(this); } - [GeneratedEvent(nameof(CreatureDeletedEvent))] - public static partial void CreatureDeletedEvent(BaseCreature bc); - public override void OnDelete() { - CreatureDeletedEvent(this); - var m = m_ControlMaster; SetControlMaster(null); @@ -3570,6 +3536,7 @@ namespace Server.Mobiles } Guild = null; + ResetSpeeds(); Delta(MobileDelta.Noto); @@ -3701,7 +3668,7 @@ namespace Server.Mobiles if (ReturnsToHome && IsSpawnerBound() && !InRange(Home, RangeHome)) { - if (Combatant == null && !Warmode && Utility.RandomDouble() < .10) /* some throttling */ + if (Combatant == null && Warmode == false && Utility.RandomDouble() < .10) /* some throttling */ { m_FailedReturnHome = !Move(GetDirectionTo(Home.X, Home.Y)) ? m_FailedReturnHome + 1 : 0; @@ -3931,7 +3898,7 @@ namespace Server.Mobiles IsDeadPet = false; - var buffer = stackalloc byte[OutgoingMobilePackets.BondedStatusPacketLength].InitializePacket(); + Span buffer = stackalloc byte[OutgoingMobilePackets.BondedStatusPacketLength].InitializePacket(); OutgoingMobilePackets.CreateBondedStatus(buffer, Serial, false); Effects.SendPacket(Location, Map, buffer); @@ -3939,8 +3906,6 @@ namespace Server.Mobiles OnAfterResurrect(); - AIObject?.Activate(); - var owner = ControlMaster; if (owner?.Deleted == false && owner.Map == Map && owner.InRange(this, 12) && CanSee(owner) && InLOS(owner)) @@ -3992,7 +3957,7 @@ namespace Server.Mobiles { SetLocation(Home, true); - if (PlayerRangeSensitive && !Map.GetSector(X, Y).Active) + if (!Map.GetSector(X, Y).Active) { AIObject?.Deactivate(); } @@ -4326,7 +4291,7 @@ namespace Server.Mobiles if (amount > 0) { - var stamGain = dropped switch + int stamGain = dropped switch { Gold => amount - 50, _ => amount * 15 - 50 @@ -4347,7 +4312,7 @@ namespace Server.Mobiles else if (m_Loyalty < MaxLoyalty) { // Calculate the loyalty increase - var loyaltyIncrease = Utility.CoinFlips(amount, MaxLoyaltyIncrease) * 10; + int loyaltyIncrease = Utility.CoinFlips(amount, MaxLoyaltyIncrease) * 10; if (loyaltyIncrease > 0) // Only update if there's an actual increase { @@ -4641,78 +4606,13 @@ namespace Server.Mobiles return false; } - /// - /// Sets the think clock and clears movement overrides (legacy one-clock semantics); - /// use for an independent movement pace. - /// public void SetSpeed(double active, double passive, bool isPassive = true) { ActiveSpeed = active; PassiveSpeed = passive; - ClearMoveSpeed(); CurrentSpeed = isPassive ? PassiveSpeed : ActiveSpeed; } - /// Sets only the movement clock (seconds per step). - public void SetMoveSpeed(double active, double passive) - { - ActiveMoveSpeed = active; - PassiveMoveSpeed = passive; - } - - /// Clears movement overrides; steps pace off the think clock again. - public void ClearMoveSpeed() - { - _activeMoveSpeed = 0; - _passiveMoveSpeed = 0; - } - - /// - /// Scales movement overrides (paragon and similar buffs). Inheriting values stay - /// inheriting — they already follow the scaled think clock. - /// - public void ScaleMoveSpeed(double scalar) - { - if (_activeMoveSpeed > 0) - { - _activeMoveSpeed *= scalar; - } - - if (_passiveMoveSpeed > 0) - { - _passiveMoveSpeed *= scalar; - } - } - - /// - /// Snaps speeds within rounding distance of the creature's table values back to - /// exact. A scaling buff that divides then multiplies can drift by an ulp (e.g. - /// 0.9 and 0.45 through 1.2), which would read as hand-tuned; call after undoing - /// such a buff. Genuinely tuned speeds are nowhere near the epsilon and keep. - /// - public void SnapSpeedsToTable() - { - GetSpeeds(out var activeSpeed, out var passiveSpeed); - - if (Math.Abs(_activeSpeed - activeSpeed) < 0.0001 && Math.Abs(_passiveSpeed - passiveSpeed) < 0.0001) - { - _activeSpeed = activeSpeed; - _passiveSpeed = passiveSpeed; - } - - GetMoveSpeeds(out var activeMoveSpeed, out var passiveMoveSpeed); - - if (activeMoveSpeed > 0 && Math.Abs(_activeMoveSpeed - activeMoveSpeed) < 0.0001) - { - _activeMoveSpeed = activeMoveSpeed; - } - - if (passiveMoveSpeed > 0 && Math.Abs(_passiveMoveSpeed - passiveMoveSpeed) < 0.0001) - { - _passiveMoveSpeed = passiveMoveSpeed; - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetCurrentSpeedToActive() => CurrentSpeed = ActiveSpeed; @@ -5026,49 +4926,36 @@ namespace Server.Mobiles NPCSpeeds.GetSpeeds(this, out activeSpeed, out passiveSpeed); } - public virtual void GetMoveSpeeds(out double activeMoveSpeed, out double passiveMoveSpeed) - { - NPCSpeeds.GetMoveSpeeds(this, out activeMoveSpeed, out passiveMoveSpeed); - } - - // Pre-v22 saves carry no movement clock. A creature whose serialized think speeds - // still match what it would spawn with today was never hand-tuned: adopt today's - // move values so existing worlds (and pets) pick up npc-speeds pacing without a - // respawn. Tuned creatures keep movement inheriting their think clock. - internal void MigrateMoveSpeeds() + public void ResetSpeeds(bool currentUseActive = false) { GetSpeeds(out var activeSpeed, out var passiveSpeed); - if (_activeSpeed == activeSpeed && _passiveSpeed == passiveSpeed) - { - GetMoveSpeeds(out _activeMoveSpeed, out _passiveMoveSpeed); - } + ActiveSpeed = activeSpeed; + PassiveSpeed = passiveSpeed; + CurrentSpeed = currentUseActive ? activeSpeed : passiveSpeed; } public virtual void DropBackpack() { - var backpack = Backpack; - if (!(backpack?.Items.Count > 0)) + if (Backpack?.Items.Count > 0) { - return; - } + Backpack b = new CreatureBackpack(Name); - var b = new CreatureBackpack(Name); - using var queue = backpack.EnumerateItems(); + var list = new List(Backpack.Items); + foreach (var item in list) + { + b.DropItem(item); + } - while (queue.Count > 0) - { - b.DropItem(queue.Dequeue()); - } - - var house = BaseHouse.FindHouseAt(this); - if (house != null) - { - b.MoveToWorld(house.BanLocation, house.Map); - } - else - { - b.MoveToWorld(Location, Map); + var house = BaseHouse.FindHouseAt(this); + if (house != null) + { + b.MoveToWorld(house.BanLocation, house.Map); + } + else + { + b.MoveToWorld(Location, Map); + } } } @@ -5479,8 +5366,6 @@ namespace Server.Mobiles public virtual void HealStart(Mobile patient) { - StopHeal(); - var onSelf = patient == this; // DoBeneficial( patient ); @@ -5507,13 +5392,12 @@ namespace Server.Mobiles return; } - if (!InRange(patient, HealStartRange)) - { - return; - } - var onSelf = patient == this; - if (patient.Poisoned) + + if (!patient.Alive) + { + } + else if (patient.Poisoned) { var poisonLevel = patient.Poison.Level; diff --git a/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs b/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs index d3b119523..3997a3856 100644 --- a/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs +++ b/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using ModernUO.Serialization; using Server.Collections; using Server.ContextMenus; @@ -92,7 +93,7 @@ public abstract partial class BaseFamiliar : BaseCreature Hidden = m_LastHidden = master.Hidden; } - if (AIObject?.WalkMobileRange(master, 5, false, 1, 1) == true) + if (AIObject?.WalkMobileRange(master, 5, true, 1, 1) == true) { Warmode = master.Warmode; Combatant = master.Combatant; @@ -150,15 +151,14 @@ public abstract partial class BaseFamiliar : BaseCreature var map = Map; var pack = Backpack; - if (map == null || map == Map.Internal || pack == null) + if (map != null && map != Map.Internal && pack != null) { - return; - } + var list = new List(pack.Items); - using var queue = pack.EnumerateItems(); - while (queue.Count > 0) - { - queue.Dequeue().MoveToWorld(Location, map); + for (var i = 0; i < list.Count; ++i) + { + list[i].MoveToWorld(Location, map); + } } } diff --git a/Projects/UOContent/Mobiles/Guards/ArcherGuard.cs b/Projects/UOContent/Mobiles/Guards/ArcherGuard.cs index 895e0724f..e1719b94b 100644 --- a/Projects/UOContent/Mobiles/Guards/ArcherGuard.cs +++ b/Projects/UOContent/Mobiles/Guards/ArcherGuard.cs @@ -1,3 +1,4 @@ +using System; using ModernUO.Serialization; using Server.Items; @@ -6,13 +7,17 @@ namespace Server.Mobiles; [SerializationGenerator(0, false)] public partial class ArcherGuard : BaseGuard { - private bool _shooting; + private Timer _attackTimer; + private Timer _idleTimer; [Constructible] public ArcherGuard(Mobile target = null) : base(target) { InitStats(100, 125, 25); + Title = "the guard"; + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); if (Female = Utility.RandomBool()) @@ -44,15 +49,15 @@ public partial class ArcherGuard : BaseGuard AddItem(bow); - var pack = new Backpack - { - Movable = false - }; + Container pack = new Backpack(); - pack.DropItem(new Arrow(250) - { - LootType = LootType.Newbied - }); + pack.Movable = false; + + var arrows = new Arrow(250); + + arrows.LootType = LootType.Newbied; + + pack.DropItem(arrows); pack.DropItem(new Gold(10, 25)); AddItem(pack); @@ -102,22 +107,34 @@ public partial class ArcherGuard : BaseGuard Say(500131); // Thou wilt regret thine actions, swine! } - AttackTimer = null; - IdleTimer = null; + if (_attackTimer != null) + { + _attackTimer.Stop(); + _attackTimer = null; + } + + if (_idleTimer != null) + { + _idleTimer.Stop(); + _idleTimer = null; + } if (_focus != null) { - AttackTimer = new GuardAttackTimer(this); - AttackTimer.Start(); + _attackTimer = new AttackTimer(this); + _attackTimer.Start(); + ((AttackTimer)_attackTimer).DoOnTick(); } else { - IdleTimer = new GuardIdleTimer(this); + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); } } - else if (_focus == null && IdleTimer == null && Spawner != null) + else if (_focus == null && _idleTimer == null) { - IdleTimer = new GuardIdleTimer(this); + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); } this.MarkDirty(); @@ -128,72 +145,245 @@ public partial class ArcherGuard : BaseGuard { if (_focus?.Alive == true) { - new GuardAvengeTimer(_focus).Start(); // If a guard dies, three more guards will spawn + new AvengeTimer(_focus).Start(); // If a guard dies, three more guards will spawn } return base.OnBeforeDeath(); } - public override void NonLethalAttack(Mobile target) + [AfterDeserialization] + private void AfterDeserialization() { - if (!InRange(target, 20)) + if (_focus != null) { - _shooting = false; - Focus = null; - return; + _attackTimer = new AttackTimer(this); + _attackTimer.Start(); } - - if (!InLOS(target)) + else { - _shooting = false; - TeleportTo(this, target.Location); - return; - } - - if (!CanSee(target)) - { - _shooting = false; - - if (InRange(target, 2)) - { - if (UseSkill(SkillName.DetectHidden)) - { - Say("Reveal!"); - } - } - else if (!Move(GetDirectionTo(target) | Direction.Running) && OutOfMaxDistance(target)) - { - TeleportTo(this, target.Location); - } - - return; - } - - if (_shooting) - { - if (TimeToSpare() || OutOfMaxDistance(target)) - { - _shooting = false; - } - - return; - } - - if (InRange(target, 1) && !Move(GetDirectionTo(target) - 4 | Direction.Running) && OutOfMaxDistance(target)) - { - TeleportTo(this, target.Location); - return; - } - - if (InMinDistance(target)) - { - _shooting = true; + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); } } - private bool TimeToSpare() => NextCombatTime - Core.TickCount > 1000; + public override void OnAfterDelete() + { + if (_attackTimer != null) + { + _attackTimer.Stop(); + _attackTimer = null; + } - private bool OutOfMaxDistance(IPoint2D target) => !InRange(target, Weapon.MaxRange); + if (_idleTimer != null) + { + _idleTimer.Stop(); + _idleTimer = null; + } - private bool InMinDistance(IPoint2D target) => InRange(target, 4); + base.OnAfterDelete(); + } + + private class AvengeTimer : Timer + { + private readonly Mobile m_Focus; + + // After 2.5 seconds, one guard will spawn every 1.0 second, three times + public AvengeTimer(Mobile focus) : base(TimeSpan.FromSeconds(2.5), TimeSpan.FromSeconds(1.0), 3) => + m_Focus = focus; + + protected override void OnTick() + { + Spawn(m_Focus, m_Focus, 1, true); + } + } + + private class AttackTimer : Timer + { + private readonly ArcherGuard m_Owner; + // private bool m_Shooting; + + public AttackTimer(ArcherGuard owner) : base(TimeSpan.FromSeconds(0.25), TimeSpan.FromSeconds(0.1)) => + m_Owner = owner; + + public void DoOnTick() + { + OnTick(); + } + + protected override void OnTick() + { + if (m_Owner.Deleted) + { + Stop(); + return; + } + + m_Owner.Criminal = false; + m_Owner.Kills = 0; + m_Owner.Stam = m_Owner.StamMax; + + var target = m_Owner.Focus; + + if (target != null && (target.Deleted || !target.Alive || !m_Owner.CanBeHarmful(target))) + { + m_Owner.Focus = null; + Stop(); + return; + } + + if (m_Owner.Weapon is Fists) + { + m_Owner.Kill(); + Stop(); + return; + } + + if (target != null && m_Owner.Combatant != target) + { + m_Owner.Combatant = target; + } + + if (target == null) + { + Stop(); + } + else + { + // + TeleportTo(target); + target.BoltEffect(0); + + if (target is BaseCreature creature) + { + creature.NoKillAwards = true; + } + + target.Damage(target.HitsMax, m_Owner); + target.Kill(); // just in case, maybe Damage is overridden on some shard + + if (target.Corpse != null && !target.Player) + { + target.Corpse.Delete(); + } + + m_Owner.Focus = null; + Stop(); + } // + + /*else if (!m_Owner.InRange( target, 20 )) + { + m_Shooting = false; + m_Owner.Focus = null; + } + else if (!m_Owner.InLOS( target )) + { + m_Shooting = false; + TeleportTo( target ); + } + else if (!m_Owner.CanSee( target )) + { + m_Shooting = false; + + if (!m_Owner.InRange( target, 2 )) + { + if (!m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running ) && OutOfMaxDistance( target )) + TeleportTo( target ); + } + else + { + if (!m_Owner.UseSkill( SkillName.DetectHidden ) && Utility.Random( 50 ) == 0) + m_Owner.Say( "Reveal!" ); + } + } + else + { + if (m_Shooting && (TimeToSpare() || OutOfMaxDistance( target ))) + m_Shooting = false; + else if (!m_Shooting && InMinDistance( target )) + m_Shooting = true; + + if (!m_Shooting) + { + if (m_Owner.InRange( target, 1 )) + { + if (!m_Owner.Move( (Direction)(m_Owner.GetDirectionTo( target ) - 4) | Direction.Running ) && OutOfMaxDistance( target )) + TeleportTo( target ); // Too close, move away + } + else if (!m_Owner.InRange( target, 2 )) + { + if (!m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running ) && OutOfMaxDistance( target )) + TeleportTo( target ); + } + } + }*/ + } + + private bool TimeToSpare() => m_Owner.NextCombatTime - Core.TickCount > 1000; + + private bool OutOfMaxDistance(IPoint2D target) => !m_Owner.InRange(target, m_Owner.Weapon.MaxRange); + + private bool InMinDistance(IPoint2D target) => m_Owner.InRange(target, 4); + + private void TeleportTo(IEntity target) + { + var from = m_Owner.Location; + var to = target.Location; + + m_Owner.Location = to; + + Effects.SendLocationParticles( + EffectItem.Create(from, m_Owner.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 2023 + ); + Effects.SendLocationParticles( + EffectItem.Create(to, m_Owner.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 5023 + ); + + m_Owner.PlaySound(0x1FE); + } + } + + private class IdleTimer : Timer + { + private readonly ArcherGuard m_Owner; + private int m_Stage; + + public IdleTimer(ArcherGuard owner) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.5)) => + m_Owner = owner; + + protected override void OnTick() + { + if (m_Owner.Deleted) + { + Stop(); + return; + } + + if (m_Stage++ % 4 == 0 || !m_Owner.Move(m_Owner.Direction)) + { + m_Owner.Direction = (Direction)Utility.Random(8); + } + + if (m_Stage > 16) + { + Effects.SendLocationParticles( + EffectItem.Create(m_Owner.Location, m_Owner.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 2023 + ); + m_Owner.PlaySound(0x1FE); + + m_Owner.Delete(); + } + } + } } diff --git a/Projects/UOContent/Mobiles/Guards/BaseGuard.cs b/Projects/UOContent/Mobiles/Guards/BaseGuard.cs index 1fe8c89ac..25d302394 100644 --- a/Projects/UOContent/Mobiles/Guards/BaseGuard.cs +++ b/Projects/UOContent/Mobiles/Guards/BaseGuard.cs @@ -1,83 +1,13 @@ -using System; -using System.Runtime.CompilerServices; using ModernUO.Serialization; -using Server.Engines.PlayerMurderSystem; using Server.Items; -using Server.Misc; namespace Server.Mobiles; [SerializationGenerator(0)] public abstract partial class BaseGuard : Mobile { - public static bool GuardsInstantKill { get; private set; } - public static void Configure() - { - GuardsInstantKill = ServerConfiguration.GetSetting("guards.instantKill", true); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Spawn(Mobile caller, Mobile target, int amount = 1, bool onlyAdditional = false) => - Spawn(caller.Region, target, amount, onlyAdditional); - - public static void Spawn(Region region, Mobile target, int amount = 1, bool onlyAdditional = false) - { - if (target?.Deleted != false) - { - return; - } - - foreach (var g in target.GetMobilesInRange(15)) - { - if (g.Focus == null) // idling - { - g.Focus = target; - - --amount; - } - else if (g.Focus == target && !onlyAdditional) - { - --amount; - } - } - - while (amount-- > 0) - { - region.MakeGuard(target); - } - } - - public static void TeleportTo(Mobile source, Point3D to) - { - Effects.SendLocationParticles( - EffectItem.Create(source.Location, source.Map, EffectItem.DefaultDuration), - 0x3728, - 10, - 10, - 2023 - ); - - source.Location = to; - - Effects.SendLocationParticles( - EffectItem.Create(to, source.Map, EffectItem.DefaultDuration), - 0x3728, - 10, - 10, - 5023 - ); - - source.PlaySound(0x1FE); - } - - - private GuardIdleTimer _idleTimer; - private GuardAttackTimer _attackTimer; - public BaseGuard(Mobile target) { - Title = "the guard"; - if (target != null) { Location = target.Location; @@ -90,40 +20,39 @@ public abstract partial class BaseGuard : Mobile 10, 5023 ); - - Focus = target; - } - } - - protected GuardAttackTimer AttackTimer - { - get => _attackTimer; - set - { - _attackTimer?.Stop(); - _attackTimer = value; - _attackTimer?.Start(); - } - } - - protected GuardIdleTimer IdleTimer - { - get => _idleTimer; - set - { - _idleTimer?.Stop(); - _idleTimer = value; - _idleTimer?.Start(); } } public abstract Mobile Focus { get; set; } - public override void OnAfterDelete() + public static void Spawn(Mobile caller, Mobile target, int amount = 1, bool onlyAdditional = false) { - AttackTimer = null; - IdleTimer = null; - base.OnAfterDelete(); + if (target?.Deleted != false) + { + return; + } + + foreach (var m in target.GetMobilesInRange(15)) + { + if (m is BaseGuard g) + { + if (g.Focus == null) // idling + { + g.Focus = target; + + --amount; + } + else if (g.Focus == target && !onlyAdditional) + { + --amount; + } + } + } + + while (amount-- > 0) + { + caller.Region.MakeGuard(target); + } } public override bool OnBeforeDeath() @@ -137,200 +66,9 @@ public abstract partial class BaseGuard : Mobile ); PlaySound(0x1FE); + Delete(); + return false; } - - public abstract void NonLethalAttack(Mobile target); - - public override bool OnDragDrop(Mobile from, Item dropped) - { - if (PlayerMurderSystem.BountiesEnabled && dropped is Head head && head.PlayerName != null) - { - var target = head.BountyTarget; - - if (target == null || Core.Now - head.CarvedTime > TimeSpan.FromHours(24)) - { - SayNonBountyHeadResponse(); - head.Delete(); - return true; - } - - Say(500670); // Ah, a head! Let me check to see if there is a bounty on this. - head.Delete(); - ClaimBounty(from, target); - return true; - } - - return base.OnDragDrop(from, dropped); - } - - private void ClaimBounty(Mobile from, PlayerMobile target) - { - var bounty = PlayerMurderSystem.GetBounty(target); - - if (bounty > 0) - { - PlayerMurderSystem.ClearBounty(target); - Banker.Deposit(from, bounty); - Titles.AwardKarma(from, 20, true); - - Say(1042855, $"{target.Name}\t{bounty}"); // The bounty on ~1_PLAYER_NAME~ was ~2_AMOUNT~ gold, and has been credited to your account. - } - else - { - Say(1042854, target.Name); // There was no bounty on ~1_PLAYER_NAME~. - } - } - - private void SayNonBountyHeadResponse() - { - if (Utility.Random(5) == 0) - { - Say(500661 + Utility.Random(9)); // 500661–500669: silly guard responses - } - else - { - Say(500654 + Utility.Random(7)); // 500654–500660: normal guard responses - } - } - - [AfterDeserialization] - private void AfterDeserialization() - { - if (Focus != null) - { - AttackTimer = new GuardAttackTimer(this); - } - else - { - IdleTimer = new GuardIdleTimer(this); - } - } -} - -public class GuardAvengeTimer : Timer -{ - private readonly Mobile _focus; - - public GuardAvengeTimer(Mobile focus) : base(TimeSpan.FromSeconds(2.5), TimeSpan.FromSeconds(1.0), 3) => - _focus = focus; - - protected override void OnTick() => BaseGuard.Spawn(_focus, _focus, 1, true); -} - -public class GuardIdleTimer : Timer -{ - private readonly BaseGuard _owner; - private int m_Stage; - - public GuardIdleTimer(BaseGuard owner) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.5)) => - _owner = owner; - - protected override void OnTick() - { - if (_owner.Deleted) - { - Stop(); - return; - } - - if (m_Stage++ % 4 == 0 || !_owner.Move(_owner.Direction)) - { - _owner.Direction = (Direction)Utility.Random(8); - } - - if (m_Stage > 16) - { - Effects.SendLocationParticles( - EffectItem.Create(_owner.Location, _owner.Map, EffectItem.DefaultDuration), - 0x3728, - 10, - 10, - 2023 - ); - _owner.PlaySound(0x1FE); - - if (_owner.Spawner == null) - { - _owner.Delete(); - } - else - { - BaseGuard.TeleportTo(_owner, _owner.Spawner.Location); - } - - Stop(); - } - } -} - -public class GuardAttackTimer : Timer -{ - private readonly BaseGuard _owner; - - public GuardAttackTimer(BaseGuard owner) : base(TimeSpan.FromSeconds(0.25), TimeSpan.FromSeconds(0.1)) => - _owner = owner; - - public void DoOnTick() - { - OnTick(); - } - - protected override void OnTick() - { - if (_owner.Deleted) - { - Stop(); - return; - } - - _owner.Criminal = false; - _owner.Kills = 0; - _owner.Stam = _owner.StamMax; - - var target = _owner.Focus; - - if (target != null && (target.Deleted || !target.Alive || !_owner.CanBeHarmful(target))) - { - _owner.Focus = null; - Stop(); - return; - } - - if (target != null && _owner.Combatant != target) - { - _owner.Combatant = target; - } - - if (target == null) - { - Stop(); - } - else if (BaseGuard.GuardsInstantKill) - { - BaseGuard.TeleportTo(_owner, target.Location); - target.BoltEffect(0); - - if (target is BaseCreature creature) - { - creature.NoKillAwards = true; - } - - target.Damage(target.HitsMax, _owner); - target.Kill(); // just in case, maybe Damage is overridden on some shard - - if (target.Corpse != null && !target.Player) - { - target.Corpse.Delete(); - } - - _owner.Focus = null; - Stop(); - } - else - { - _owner.NonLethalAttack(target); - } - } } diff --git a/Projects/UOContent/Mobiles/Guards/WarriorGuard.cs b/Projects/UOContent/Mobiles/Guards/WarriorGuard.cs index c402980b4..c818fa0aa 100644 --- a/Projects/UOContent/Mobiles/Guards/WarriorGuard.cs +++ b/Projects/UOContent/Mobiles/Guards/WarriorGuard.cs @@ -1,3 +1,4 @@ +using System; using ModernUO.Serialization; using Server.Items; @@ -6,11 +7,17 @@ namespace Server.Mobiles; [SerializationGenerator(0, false)] public partial class WarriorGuard : BaseGuard { + private Timer _attackTimer; + private Timer _idleTimer; + [Constructible] public WarriorGuard(Mobile target = null) : base(target) { - InitStats(100, 125, 25); + InitStats(1000, 1000, 1000); + Title = "the guard"; + SpeechHue = Utility.RandomDyedHue(); + Hue = Race.Human.RandomSkinHue(); if (Female = Utility.RandomBool()) @@ -66,12 +73,12 @@ public partial class WarriorGuard : BaseGuard AddItem(weapon); - var pack = new Backpack - { - Movable = false - }; + Container pack = new Backpack(); + + pack.Movable = false; pack.DropItem(new Gold(10, 25)); + AddItem(pack); Skills.Anatomy.Base = 120.0; @@ -81,6 +88,7 @@ public partial class WarriorGuard : BaseGuard Skills.DetectHidden.Base = 100.0; NextCombatTime = Core.TickCount + 500; + Focus = target; } [SerializableProperty(0)] @@ -118,22 +126,34 @@ public partial class WarriorGuard : BaseGuard Say(500131); // Thou wilt regret thine actions, swine! } - AttackTimer = null; - IdleTimer = null; + if (_attackTimer != null) + { + _attackTimer.Stop(); + _attackTimer = null; + } + + if (_idleTimer != null) + { + _idleTimer.Stop(); + _idleTimer = null; + } if (_focus != null) { - AttackTimer = new GuardAttackTimer(this); - AttackTimer.Start(); + _attackTimer = new AttackTimer(this); + _attackTimer.Start(); + ((AttackTimer)_attackTimer).DoOnTick(); } else { - IdleTimer = new GuardIdleTimer(this); + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); } } - else if (_focus == null && IdleTimer == null && Spawner != null) + else if (_focus == null && _idleTimer == null) { - IdleTimer = new GuardIdleTimer(this); + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); } this.MarkDirty(); @@ -144,32 +164,209 @@ public partial class WarriorGuard : BaseGuard { if (_focus?.Alive == true) { - new GuardAvengeTimer(_focus).Start(); // If a guard dies, three more guards will spawn + new AvengeTimer(_focus).Start(); // If a guard dies, three more guards will spawn } return base.OnBeforeDeath(); } - public override void NonLethalAttack(Mobile target) + [AfterDeserialization] + private void AfterDeserialization() { - if (!InRange(target, 20)) + if (_focus != null) { - Focus = null; + _attackTimer = new AttackTimer(this); + _attackTimer.Start(); } - else if (!InRange(target, 10) || !InLOS(target)) + else { - TeleportTo(this, target.Location); + _idleTimer = new IdleTimer(this); + _idleTimer.Start(); } - else if (!InRange(target, 1)) + } + + public override void OnAfterDelete() + { + if (_attackTimer != null) { - if (!Move(GetDirectionTo(target) | Direction.Running)) + _attackTimer.Stop(); + _attackTimer = null; + } + + if (_idleTimer != null) + { + _idleTimer.Stop(); + _idleTimer = null; + } + + base.OnAfterDelete(); + } + + private class AvengeTimer : Timer + { + private readonly Mobile m_Focus; + + public AvengeTimer(Mobile focus) : base(TimeSpan.FromSeconds(2.5), TimeSpan.FromSeconds(1.0), 3) => + m_Focus = focus; + + protected override void OnTick() + { + Spawn(m_Focus, m_Focus, 1, true); + } + } + + private class AttackTimer : Timer + { + private readonly WarriorGuard m_Owner; + + public AttackTimer(WarriorGuard owner) : base(TimeSpan.FromSeconds(0.25), TimeSpan.FromSeconds(0.1)) => + m_Owner = owner; + + public void DoOnTick() + { + OnTick(); + } + + protected override void OnTick() + { + if (m_Owner.Deleted) { - TeleportTo(this, target.Location); + Stop(); + return; } + + m_Owner.Criminal = false; + m_Owner.Kills = 0; + m_Owner.Stam = m_Owner.StamMax; + + var target = m_Owner.Focus; + + if (target != null && (target.Deleted || !target.Alive || !m_Owner.CanBeHarmful(target))) + { + m_Owner.Focus = null; + Stop(); + return; + } + + if (m_Owner.Weapon is Fists) + { + m_Owner.Kill(); + Stop(); + return; + } + + if (target != null && m_Owner.Combatant != target) + { + m_Owner.Combatant = target; + } + + if (target == null) + { + Stop(); + } + else + { + // + TeleportTo(target); + target.BoltEffect(0); + + if (target is BaseCreature creature) + { + creature.NoKillAwards = true; + } + + target.Damage(target.HitsMax, m_Owner); + target.Kill(); // just in case, maybe Damage is overridden on some shard + + if (target.Corpse != null && !target.Player) + { + target.Corpse.Delete(); + } + + m_Owner.Focus = null; + Stop(); + } // + + /*else if (!m_Owner.InRange( target, 20 )) + { + m_Owner.Focus = null; + } + else if (!m_Owner.InRange( target, 10 ) || !m_Owner.InLOS( target )) + { + TeleportTo( target ); + } + else if (!m_Owner.InRange( target, 1 )) + { + if (!m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running )) + TeleportTo( target ); + } + else if (!m_Owner.CanSee( target )) + { + if (!m_Owner.UseSkill( SkillName.DetectHidden ) && Utility.Random( 50 ) == 0) + m_Owner.Say( "Reveal!" ); + }*/ } - else if (!CanSee(target) && UseSkill(SkillName.DetectHidden)) + + private void TeleportTo(Mobile target) { - Say("Reveal!"); + var from = m_Owner.Location; + var to = target.Location; + + m_Owner.Location = to; + + Effects.SendLocationParticles( + EffectItem.Create(from, m_Owner.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 2023 + ); + Effects.SendLocationParticles( + EffectItem.Create(to, m_Owner.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 5023 + ); + + m_Owner.PlaySound(0x1FE); + } + } + + private class IdleTimer : Timer + { + private readonly WarriorGuard m_Owner; + private int m_Stage; + + public IdleTimer(WarriorGuard owner) : base(TimeSpan.FromSeconds(2.0), TimeSpan.FromSeconds(2.5)) => + m_Owner = owner; + + protected override void OnTick() + { + if (m_Owner.Deleted) + { + Stop(); + return; + } + + if (m_Stage++ % 4 == 0 || !m_Owner.Move(m_Owner.Direction)) + { + m_Owner.Direction = (Direction)Utility.Random(8); + } + + if (m_Stage > 16) + { + Effects.SendLocationParticles( + EffectItem.Create(m_Owner.Location, m_Owner.Map, EffectItem.DefaultDuration), + 0x3728, + 10, + 10, + 2023 + ); + m_Owner.PlaySound(0x1FE); + + m_Owner.Delete(); + } } } } diff --git a/Projects/UOContent/Mobiles/Healers/Healer.cs b/Projects/UOContent/Mobiles/Healers/Healer.cs index 9e5c50e21..3d01a66a3 100644 --- a/Projects/UOContent/Mobiles/Healers/Healer.cs +++ b/Projects/UOContent/Mobiles/Healers/Healer.cs @@ -48,7 +48,7 @@ public partial class Healer : BaseHealer return false; } - if (m.Murderer) + if (m.Kills >= 5) { Say(501223); // Thou'rt not a decent and good person. I shall not resurrect thee. return false; diff --git a/Projects/UOContent/Mobiles/Healers/WanderingHealer.cs b/Projects/UOContent/Mobiles/Healers/WanderingHealer.cs index bc46d51d2..75ccb74be 100644 --- a/Projects/UOContent/Mobiles/Healers/WanderingHealer.cs +++ b/Projects/UOContent/Mobiles/Healers/WanderingHealer.cs @@ -40,7 +40,7 @@ public partial class WanderingHealer : BaseHealer return false; } - if (m.Murderer) + if (m.Kills >= 5) { Say(501223); // Thou'rt not a decent and good person. I shall not resurrect thee. return false; diff --git a/Projects/UOContent/Mobiles/Hireables/BaseHire.cs b/Projects/UOContent/Mobiles/Hireables/BaseHire.cs index 3f96e5d36..06c569e25 100644 --- a/Projects/UOContent/Mobiles/Hireables/BaseHire.cs +++ b/Projects/UOContent/Mobiles/Hireables/BaseHire.cs @@ -23,14 +23,19 @@ public partial class BaseHire : BaseCreature public int GoldOnDeath { get; set; } - [SerializableField(1, fieldChanged: nameof(OnIsHiredChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private bool _isHired; - - private void OnIsHiredChanged(bool oldValue, bool newValue) + [SerializableProperty(1)] + [CommandProperty(AccessLevel.GameMaster)] + public bool IsHired { - Delta(MobileDelta.Noto); + get => _isHired; + set + { + _isHired = value; + + Delta(MobileDelta.Noto); + InvalidateProperties(); + this.MarkDirty(); + } } public BaseHire(AIType AI) : base(AI, FightMode.Aggressor) @@ -106,7 +111,7 @@ public partial class BaseHire : BaseCreature public virtual bool AddHire(Mobile m) { - var owner = GetOwner(); + Mobile owner = GetOwner(); if (owner != null) { @@ -223,6 +228,11 @@ public partial class BaseHire : BaseCreature if (!Controlled) { + if (CanPaperdollBeOpenedBy(from)) + { + list.Add(new PaperdollEntry()); + } + list.Add(new HireEntry()); } else @@ -254,7 +264,7 @@ public partial class BaseHire : BaseCreature hire.NextPay = Core.Now + GetInterval(); - var pay = hire.Pay; + int pay = hire.Pay; if (hire.HoldGold <= pay) { diff --git a/Projects/UOContent/Mobiles/Monsters/AOS/Revenant.cs b/Projects/UOContent/Mobiles/Monsters/AOS/Revenant.cs index ae0829753..44105e9e5 100644 --- a/Projects/UOContent/Mobiles/Monsters/AOS/Revenant.cs +++ b/Projects/UOContent/Mobiles/Monsters/AOS/Revenant.cs @@ -57,8 +57,6 @@ namespace Server.Mobiles AddItem(new Halberd { Hue = 1, Movable = false }); } - public override bool SkipSerialization => true; - public override Mobile ConstantFocus => m_Target; public override bool NoHouseRestrictions => true; @@ -174,5 +172,11 @@ namespace Server.Mobiles Delete(); return false; } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } } diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AirElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AirElemental.cs index 04b1791d7..c50564901 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AirElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/AirElemental.cs @@ -40,9 +40,13 @@ namespace Server.Mobiles Karma = -4500; VirtualArmor = 40; + ControlSlots = 2; } public override string CorpseName => "an air elemental corpse"; + public override double DispelDifficulty => 117.5; + public override double DispelFocus => 45.0; + public override string DefaultName => "an air elemental"; public override bool BleedImmune => true; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/FireElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/FireElemental.cs index 7065a06b6..affa29a5a 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/FireElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/FireElemental.cs @@ -39,6 +39,7 @@ namespace Server.Mobiles Karma = -4500; VirtualArmor = 40; + ControlSlots = 4; PackItem(new SulfurousAsh(3)); @@ -46,10 +47,12 @@ namespace Server.Mobiles } public override string CorpseName => "a fire elemental corpse"; + public override double DispelDifficulty => 117.5; + public override double DispelFocus => 45.0; + public override string DefaultName => "a fire elemental"; public override bool BleedImmune => true; - public override int TreasureMapLevel => 2; public override void GenerateLoot() diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs index b86d775b9..cb20ad9e8 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs @@ -3,17 +3,12 @@ using Server.Items; namespace Server.Mobiles { - [SerializationGenerator(1, false)] + [SerializationGenerator(0, false)] public partial class WaterElemental : BaseCreature { - [SerializableField(0)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private bool _hasDecanter; - [Constructible] public WaterElemental() : base(AIType.AI_Mage) { - _hasDecanter = true; Body = 16; BaseSoundID = 278; @@ -43,13 +38,16 @@ namespace Server.Mobiles Karma = -4500; VirtualArmor = 40; - + ControlSlots = 3; CanSwim = true; PackItem(new BlackPearl(3)); } public override string CorpseName => "a water elemental corpse"; + public override double DispelDifficulty => 117.5; + public override double DispelFocus => 45.0; + public override string DefaultName => "a water elemental"; public override bool BleedImmune => true; @@ -61,9 +59,5 @@ namespace Server.Mobiles AddLoot(LootPack.Meager); AddLoot(LootPack.Potions); } - - private void MigrateFrom(V0Content content) - { - } } } diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs index 35cf273af..57ae60315 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs @@ -36,17 +36,20 @@ namespace Server.Mobiles Karma = -3500; VirtualArmor = 34; + ControlSlots = 2; PackItem(new FertileDirt(Utility.RandomMinMax(1, 4))); PackItem(new MandrakeRoot()); - PackItem(new IronOre(5) - { - ItemID = 0x19B7 - }); + Item ore = new IronOre(5); + ore.ItemID = 0x19B7; + PackItem(ore); } public override string CorpseName => "an earth elemental corpse"; + public override double DispelDifficulty => 117.5; + public override double DispelFocus => 45.0; + public override string DefaultName => "an earth elemental"; public override bool BleedImmune => true; diff --git a/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs b/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs index 3884a6c03..60005a74c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs @@ -39,10 +39,9 @@ namespace Server.Mobiles VirtualArmor = 50; PackItem(new BlackPearl(3)); - PackItem(new IronOre(3) - { - ItemID = 0x19B8 - }); + Item ore = new IronOre(3); + ore.ItemID = 0x19B8; + PackItem(ore); } public override string CorpseName => "a snow elemental corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Daemon.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Daemon.cs index 5117c15b1..2fab099b4 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Daemon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Daemon.cs @@ -40,21 +40,20 @@ namespace Server.Mobiles Karma = -15000; VirtualArmor = 58; + ControlSlots = Core.SE ? 4 : 5; } public override string CorpseName => "a daemon corpse"; + public override double DispelDifficulty => 125.0; + public override double DispelFocus => 45.0; public override Faction FactionAllegiance => Shadowlords.Instance; public override Ethic EthicAllegiance => Ethic.Evil; public override bool CanRummageCorpses => true; - public override Poison PoisonImmune => Poison.Regular; - public override int TreasureMapLevel => 4; - public override int Meat => 1; - public override bool CanFly => true; public override void GenerateLoot() diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs index 8fbfc9a15..d1994f82d 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs @@ -228,14 +228,14 @@ namespace Server.Mobiles var total = Skills.Magery.Value + Skills.Poisoning.Value; - var dist = this.GetDistanceToSqrt(m); + var dist = GetDistanceToSqrt(m); if (dist >= 3.0) { total -= (dist - 3.0) * 10.0; } - var level = total switch + int level = total switch { >= 200.0 => Utility.Random(10) == 0 ? 3 : 2, > 170.0 => 2, diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Spellbinder.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Spellbinder.cs deleted file mode 100644 index 3bde9178b..000000000 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/Spellbinder.cs +++ /dev/null @@ -1,65 +0,0 @@ -using ModernUO.Serialization; -using Server.Spells; - - -namespace Server.Mobiles; - -[SerializationGenerator(0)] -public partial class Spellbinder : BaseCreature -{ - [Constructible] - public Spellbinder() : base(AIType.AI_Mage, FightMode.Aggressor) - { - Body = Utility.RandomList(26, 50, 56); - BaseSoundID = 0x482; - - SetStr(46, 70); - SetDex(47, 65); - SetInt(187, 210); - - SetHits(36, 50); - - SetDamage(3, 6); - - SetDamageType(ResistanceType.Physical, 50); - SetDamageType(ResistanceType.Cold, 50); - - SetResistance(ResistanceType.Physical, 25, 30); - SetResistance(ResistanceType.Cold, 15, 25); - SetResistance(ResistanceType.Poison, 10, 20); - - SetSkill(SkillName.MagicResist, 35.1, 50.0); - SetSkill(SkillName.Tactics, 35.1, 50.0); - SetSkill(SkillName.Wrestling, 35.1, 45.0); - - Fame = 2500; - Karma = -2500; - - VirtualArmor = 28; - } - - protected override BaseAI ForcedAI => new SpellbinderAI(this); - - public override string CorpseName => "a ghostly corpse"; - public override string DefaultName => "a spectral spellbinder"; - - public override bool BleedImmune => true; - - public override OppositionGroup OppositionGroup => OppositionGroup.FeyAndUndead; - - public override Poison PoisonImmune => Poison.Regular; - - public override void GenerateLoot() - { - AddLoot(LootPack.Meager); - PackItem(Loot.RandomWeapon()); - } - - public class SpellbinderAI : MageAI{ - public SpellbinderAI(BaseCreature m) : base(m) - { - } - - public override Spell GetRandomDamageSpell() => GetRandomCurseSpell(); - } -} diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs index 850d3275a..6c76299dd 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs @@ -1,5 +1,4 @@ using ModernUO.Serialization; -using Server.Items; namespace Server.Mobiles { @@ -33,18 +32,6 @@ namespace Server.Mobiles Karma = -450; VirtualArmor = 18; - - Item bodyPart = Utility.Random(6) switch - { - 0 => new Head(), - 1 => new Torso(), - 2 => new RightArm(), - 3 => new LeftArm(), - 4 => new RightLeg(), - _ => new LeftLeg() - }; - - AddItem(bodyPart); } public override string CorpseName => "a headless corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs index 269f244fa..f8e3ea8a7 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs @@ -56,8 +56,6 @@ namespace Server.Mobiles AddItem(weapon); } - public override bool SkipSerialization => true; - public override bool DeleteCorpseOnDeath => true; public override Mobile ConstantFocus => m_Target; @@ -151,5 +149,11 @@ namespace Server.Mobiles base.OnDelete(); } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } } diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs index 317df7658..45541153c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs @@ -38,10 +38,9 @@ namespace Server.Mobiles VirtualArmor = 50; - PackItem(new ShadowIronOre(25) - { - ItemID = 0x19B9 - }); + Item ore = new ShadowIronOre(25); + ore.ItemID = 0x19B9; + PackItem(ore); PackItem(new IronIngot(10)); if (Utility.RandomDouble() < 0.05) diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaLord.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaLord.cs index 4529489d8..6685790ba 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaLord.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaLord.cs @@ -40,7 +40,7 @@ namespace Server.Mobiles VirtualArmor = 28; - var pack = new Backpack(); + Container pack = new Backpack(); pack.DropItem(new Arrow(Utility.RandomMinMax(25, 35))); pack.DropItem(new Arrow(Utility.RandomMinMax(25, 35))); @@ -74,29 +74,15 @@ namespace Server.Mobiles { if (!willKill && amount > 5 && from?.Player == true && Utility.Random(100) < 5) { - switch (Utility.Random(4)) + string[] toSay = { - case 0: - { - Say(true, $"{from.Name}!! You will have to do better than that!"); - break; - } - case 1: - { - Say(true, $"{from.Name}!! Prepare to meet your doom!"); - break; - } - case 2: - { - Say(true, $"{from.Name}!! My armies will crush you!"); - break; - } - default: - { - Say(true, $"{from.Name}!! You will pay for that!"); - break; - } - } + "{0}!! You will have to do better than that!", + "{0}!! Prepare to meet your doom!", + "{0}!! My armies will crush you!", + "{0}!! You will pay for that!" + }; + + Say(true, string.Format(toSay.RandomElement(), from.Name)); } base.OnDamage(amount, from, willKill); diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs index a0d486535..abf2c39dc 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Jukas/JukaMage.cs @@ -45,7 +45,7 @@ namespace Server.Mobiles VirtualArmor = 16; - var bag = new Bag(); + Container bag = new Bag(); var count = Utility.RandomMinMax(10, 20); diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs index d4b8eaf82..3af690d48 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs @@ -42,7 +42,7 @@ namespace Server.Mobiles VirtualArmor = 28; - var pack = new Backpack(); + Container pack = new Backpack(); pack.DropItem(new Bolt(Utility.RandomMinMax(10, 20))); pack.DropItem(new Bolt(Utility.RandomMinMax(10, 20))); @@ -59,7 +59,7 @@ namespace Server.Mobiles } ); - var bag = new Bag(); + Container bag = new Bag(); var count = Utility.RandomMinMax(10, 20); diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs index e9a878c95..89f7f3d8b 100644 --- a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs +++ b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs @@ -96,7 +96,7 @@ namespace Server.Mobiles return; } - var scalar = queue.Count switch + double scalar = queue.Count switch { 1 => 0.75, 2 => 0.50, diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs index f09b345ca..6b7a89005 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Bedlam/LadyJennifyr.cs @@ -1,71 +1,122 @@ using ModernUO.Serialization; +using System; +using System.Collections.Generic; -namespace Server.Mobiles; - -[SerializationGenerator(0, false)] -public partial class LadyJennifyr : SkeletalKnight +namespace Server.Mobiles { - [Constructible] - public LadyJennifyr() + [SerializationGenerator(0, false)] + public partial class LadyJennifyr : SkeletalKnight { - IsParagon = true; + private static readonly Dictionary m_Table = new(); - Hue = 0x76D; + [Constructible] + public LadyJennifyr() + { + IsParagon = true; - SetStr(208, 309); - SetDex(91, 118); - SetInt(44, 101); + Hue = 0x76D; - SetHits(1113, 1285); + SetStr(208, 309); + SetDex(91, 118); + SetInt(44, 101); - SetDamage(15, 25); + SetHits(1113, 1285); - SetDamageType(ResistanceType.Physical, 40); - SetDamageType(ResistanceType.Cold, 60); + SetDamage(15, 25); - SetResistance(ResistanceType.Physical, 56, 65); - SetResistance(ResistanceType.Fire, 41, 49); - SetResistance(ResistanceType.Cold, 71, 80); - SetResistance(ResistanceType.Poison, 41, 50); - SetResistance(ResistanceType.Energy, 50, 58); + SetDamageType(ResistanceType.Physical, 40); + SetDamageType(ResistanceType.Cold, 60); - SetSkill(SkillName.Wrestling, 127.9, 137.1); - SetSkill(SkillName.Tactics, 128.4, 141.9); - SetSkill(SkillName.MagicResist, 102.1, 119.5); - SetSkill(SkillName.Anatomy, 129.0, 137.5); + SetResistance(ResistanceType.Physical, 56, 65); + SetResistance(ResistanceType.Fire, 41, 49); + SetResistance(ResistanceType.Cold, 71, 80); + SetResistance(ResistanceType.Poison, 41, 50); + SetResistance(ResistanceType.Energy, 50, 58); - Fame = 18000; - Karma = -18000; + SetSkill(SkillName.Wrestling, 127.9, 137.1); + SetSkill(SkillName.Tactics, 128.4, 141.9); + SetSkill(SkillName.MagicResist, 102.1, 119.5); + SetSkill(SkillName.Anatomy, 129.0, 137.5); + + Fame = 18000; + Karma = -18000; + } + + public override string CorpseName => "a Lady Jennifyr corpse"; + public override string DefaultName => "Lady Jennifyr"; + + /* + // TODO: Uncomment once added + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + if (Utility.RandomDouble() < 0.15) + c.DropItem( new DisintegratingThesisNotes() ); + + if (Utility.RandomDouble() < 0.1) + c.DropItem( new ParrotItem() ); + } + */ + + public override bool GivesMLMinorArtifact => true; + + public override void GenerateLoot() + { + AddLoot(LootPack.UltraRich, 3); + } + + public override void OnGaveMeleeAttack(Mobile defender, int damage) + { + base.OnGaveMeleeAttack(defender, damage); + + if (Utility.RandomDouble() < 0.9) + { + return; + } + + if (m_Table.Remove(defender, out var timer)) + { + timer.DoExpire(); + } + + defender.FixedParticles(0x3709, 10, 30, 5052, EffectLayer.LeftFoot); + defender.PlaySound(0x208); + // The creature fans you with fire, reducing your resistance to fire attacks. + defender.SendLocalizedMessage(1070833); + + var mod = new ResistanceMod(ResistanceType.Fire, "FireResistFanningFire", -10); + defender.AddResistanceMod(mod); + + m_Table[defender] = timer = new ExpireTimer(defender, mod); + timer.Start(); + } + + private class ExpireTimer : Timer + { + private readonly Mobile m_Mobile; + private readonly ResistanceMod m_Mod; + + public ExpireTimer(Mobile m, ResistanceMod mod) + : base(TimeSpan.FromSeconds(10)) + { + m_Mobile = m; + m_Mod = mod; + } + + public void DoExpire() + { + m_Mobile.RemoveResistanceMod(m_Mod); + + Stop(); + } + + protected override void OnTick() + { + m_Mobile.SendLocalizedMessage(1070834); // Your resistance to fire attacks has returned. + DoExpire(); + m_Table.Remove(m_Mobile); + } + } } - - public override string CorpseName => "a Lady Jennifyr corpse"; - public override string DefaultName => "Lady Jennifyr"; - - /* - // TODO: Uncomment once added - public override void OnDeath( Container c ) - { - base.OnDeath( c ); - - if (Utility.RandomDouble() < 0.15) - c.DropItem( new DisintegratingThesisNotes() ); - - if (Utility.RandomDouble() < 0.1) - c.DropItem( new ParrotItem() ); - } - */ - - public override bool GivesMLMinorArtifact => true; - - public override void GenerateLoot() - { - AddLoot(LootPack.UltraRich, 3); - } - - private static readonly MonsterAbility[] _abilities = - [ - new FanningFire(0.10, -10, 35, 45) - ]; - - public override MonsterAbility[] GetMonsterAbilities() => _abilities; } diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/Protector.cs b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/Protector.cs index 4fbe38291..e0e11287f 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/Protector.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Prism of Light/Protector.cs @@ -42,17 +42,17 @@ namespace Server.Mobiles Fame = 10000; Karma = -10000; - AddItem(new ThighBoots - { - Movable = false, - Hue = Utility.Random(2) - }); - AddItem(new Item(0x204E) - { - Layer = Layer.OuterTorso, - Movable = false, - Hue = Utility.Random(2) - }); + Item boots = new ThighBoots(); + boots.Movable = false; + boots.Hue = Utility.Random(2); + + var shroud = new Item(0x204E); + shroud.Layer = Layer.OuterTorso; + shroud.Movable = false; + shroud.Hue = Utility.Random(2); + + AddItem(boots); + AddItem(shroud); } public override string CorpseName => "a human corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs b/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs index 732a232e3..b6adac231 100644 --- a/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs +++ b/Projects/UOContent/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs @@ -102,7 +102,11 @@ namespace Server.Mobiles public override string DefaultName => "a mysterious rabbit hole"; - public override bool SkipSerialization => true; + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } } } diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs index f156ec05f..64b81daf4 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs @@ -70,7 +70,7 @@ namespace Server.Mobiles InLOS(from)) { m_NextResurrect = Core.Now + ResurrectDelay; - if (!from.Criminal && !from.Murderer && from.Karma > 0) + if (!from.Criminal && from.Kills < 5 && from.Karma > 0) { if (from.Map?.CanFit(from.Location, 16, false, false) == true) { diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs index 6edf6927b..9f486c679 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs @@ -86,7 +86,7 @@ namespace Server.Mobiles public override Poison PoisonImmune => Poison.Lethal; public override double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly) => - m.Str / Math.Max(this.GetDistanceToSqrt(m), 1.0); + m.Str / Math.Max(GetDistanceToSqrt(m), 1.0); public override int GetAngerSound() => 0x23A; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs index a0966eaf4..03f4adf9f 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs @@ -44,21 +44,19 @@ namespace Server.Mobiles } public override string CorpseName => "a blade spirit corpse"; - public override string DefaultName => "a blade spirit"; - public override bool DeleteCorpseOnDeath => Core.AOS; public override bool IsHouseSummonable => true; public override double DispelDifficulty => 0.0; public override double DispelFocus => 20.0; + public override string DefaultName => "a blade spirit"; + public override bool BleedImmune => true; public override Poison PoisonImmune => Poison.Lethal; - public override bool FollowsAcquireRules => Core.AOS || !Summoned || SummonMaster?.Player != true || Map != Map.Felucca; - public override double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly) => - (m.Str + m.Skills.Tactics.Value) / Math.Max(this.GetDistanceToSqrt(m), 1.0); + (m.Str + m.Skills.Tactics.Value) / Math.Max(GetDistanceToSqrt(m), 1.0); public override int GetAngerSound() => 0x23A; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs index 3bb2e69fa..a6ab88261 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs @@ -63,10 +63,8 @@ namespace Server.Mobiles public override bool BleedImmune => true; public override Poison PoisonImmune => Poison.Lethal; - public override bool FollowsAcquireRules => Core.AOS || !Summoned || SummonMaster?.Player != true || Map != Map.Felucca; - public override double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly) => - (m.Int + m.Skills.Magery.Value) / Math.Max(this.GetDistanceToSqrt(m), 1.0); + (m.Int + m.Skills.Magery.Value) / Math.Max(GetDistanceToSqrt(m), 1.0); public override int GetAngerSound() => 0x15; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs index 8c7984360..cb469a55a 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs @@ -124,11 +124,9 @@ namespace Server.Mobiles { if (Map != null && caster != this && Utility.RandomDouble() < 0.25) { - var spawn = new PlagueSpawn(this) - { - Team = Team - }; + BaseCreature spawn = new PlagueSpawn(this); + spawn.Team = Team; spawn.MoveToWorld(Location, Map); spawn.Combatant = caster; @@ -142,11 +140,9 @@ namespace Server.Mobiles { if (Map != null && attacker != this && Utility.RandomDouble() < 0.25) { - var spawn = new PlagueSpawn(this) - { - Team = Team - }; + BaseCreature spawn = new PlagueSpawn(this); + spawn.Team = Team; spawn.MoveToWorld(Location, Map); spawn.Combatant = attacker; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs index 60db2e920..bdf45058f 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs @@ -37,10 +37,9 @@ namespace Server.Mobiles VirtualArmor = 32; - PackItem(new AgapiteOre(oreAmount) - { - ItemID = 0x19B9 - }); + Item ore = new AgapiteOre(oreAmount); + ore.ItemID = 0x19B9; + PackItem(ore); } public override string CorpseName => "an ore elemental corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs index ecd9df804..e90e95bb7 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs @@ -38,10 +38,9 @@ namespace Server.Mobiles VirtualArmor = 29; - PackItem(new BronzeOre(oreAmount) - { - ItemID = 0x19B9 - }); + Item ore = new BronzeOre(oreAmount); + ore.ItemID = 0x19B9; + PackItem(ore); } public override string CorpseName => "an ore elemental corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs index d2388302f..0adc81b6e 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/CopperElemental.cs @@ -37,10 +37,9 @@ namespace Server.Mobiles VirtualArmor = 26; - PackItem(new CopperOre(oreAmount) - { - ItemID = 0x19B9 - }); + Item ore = new CopperOre(oreAmount); + ore.ItemID = 0x19B9; + PackItem(ore); } public override string CorpseName => "an ore elemental corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs index c2fd84e75..e8f94d462 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs @@ -37,10 +37,9 @@ namespace Server.Mobiles VirtualArmor = 20; - PackItem(new DullCopperOre(oreAmount) - { - ItemID = 0x19B9 - }); + Item ore = new DullCopperOre(oreAmount); + ore.ItemID = 0x19B9; + PackItem(ore); } public override string CorpseName => "an ore elemental corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs index 845a1f1ca..35903c3fe 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs @@ -37,10 +37,9 @@ namespace Server.Mobiles VirtualArmor = 60; - PackItem(new GoldOre(oreAmount) - { - ItemID = 0x19B9 - }); + Item ore = new GoldOre(oreAmount); + ore.ItemID = 0x19B9; + PackItem(ore); } public override string CorpseName => "an ore elemental corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs index e68c6f4f5..edfeffeb8 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs @@ -37,10 +37,9 @@ namespace Server.Mobiles VirtualArmor = 23; - PackItem(new ShadowIronOre(oreAmount) - { - ItemID = 0x19B9 - }); + Item ore = new ShadowIronOre(oreAmount); + ore.ItemID = 0x19B9; + PackItem(ore); } public override string CorpseName => "an ore elemental corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs index 2480d3a9c..64c099969 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs @@ -40,10 +40,9 @@ namespace Server.Mobiles VirtualArmor = 38; - PackItem(new ValoriteOre(oreAmount) - { - ItemID = 0x19B9 - }); + Item ore = new ValoriteOre(oreAmount); + ore.ItemID = 0x19B9; + PackItem(ore); } public override string CorpseName => "an ore elemental corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs index 9b1a1e547..306c07d06 100644 --- a/Projects/UOContent/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs @@ -38,10 +38,9 @@ namespace Server.Mobiles VirtualArmor = 35; - PackItem(new VeriteOre(oreAmount) - { - ItemID = 0x19B9 - }); + Item ore = new VeriteOre(oreAmount); + ore.ItemID = 0x19B9; + PackItem(ore); } public override string CorpseName => "an ore elemental corpse"; diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Raptor.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Raptor.cs deleted file mode 100644 index 36c3088bb..000000000 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Raptor.cs +++ /dev/null @@ -1,76 +0,0 @@ -using ModernUO.Serialization; -using Server.Items; - -namespace Server.Mobiles; - -[SerializationGenerator(0)] -public partial class Raptor : BaseCreature -{ - [Constructible] - public Raptor() : base(AIType.AI_Melee) - { - Body = 730; - - SetStr(404, 471); - SetDex(132, 155); - SetInt(105, 145); - - SetHits(343, 400); - - SetDamage(11, 17); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 45, 50); - SetResistance(ResistanceType.Fire, 50, 60); - SetResistance(ResistanceType.Cold, 40, 50); - SetResistance(ResistanceType.Poison, 20, 30); - SetResistance(ResistanceType.Energy, 30, 40); - - SetSkill(SkillName.MagicResist, 75.1, 90.0); - SetSkill(SkillName.Tactics, 75.1, 100.0); - SetSkill(SkillName.Wrestling, 70.1, 95.1); - - Fame = 7500; - Karma = -7500; - - Tamable = true; - MinTameSkill = 107.1; - ControlSlots = 2; - - // TODO(SA-creatures): ServUO Raptors summon up to 2 "friend" raptors on entering combat - // (InternalTimer/CheckFriends). Omitted pending a ModernUO timer/MonsterAbility adaptation. - // Also unported vs ServUO: the 25% AncientPotteryFragments corpse drop (item class not yet in ModernUO). - } - - public override string CorpseName => "a raptor corpse"; - public override string DefaultName => "a raptor"; - - public override int TreasureMapLevel => 3; - public override int Meat => 7; - public override int Hides => 11; - public override HideType HideType => HideType.Horned; - public override PackInstinct PackInstinct => PackInstinct.Ostard; - - public override WeaponAbility GetWeaponAbility() => WeaponAbility.BleedAttack; - - public override int GetIdleSound() => 1573; - public override int GetAngerSound() => 1570; - public override int GetHurtSound() => 1572; - public override int GetDeathSound() => 1571; - - public override void GenerateLoot() - { - AddLoot(LootPack.Rich, 2); - } - - public override void OnDeath(Container c) - { - base.OnDeath(c); - - if (!Controlled && Utility.RandomDouble() <= 0.005) - { - c.DropItem(new RaptorClaw()); - } - } -} diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/Slith.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/Slith.cs deleted file mode 100644 index 90178101e..000000000 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/Slith.cs +++ /dev/null @@ -1,56 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Mobiles; - -[SerializationGenerator(0)] -public partial class Slith : BaseCreature -{ - // ServUO SpecialAbility.DragonBreath -> closest ModernUO monster ability. - private static readonly MonsterAbility[] _abilities = [MonsterAbilities.FireBreath]; - - [Constructible] - public Slith() : base(AIType.AI_Melee) - { - Body = 734; - - SetStr(129, 136); - SetDex(72, 75); - SetInt(12, 13); - - SetHits(84, 85); - - SetDamage(6, 24); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 35, 40); - SetResistance(ResistanceType.Fire, 35, 45); - SetResistance(ResistanceType.Poison, 25, 35); - SetResistance(ResistanceType.Energy, 25, 30); - - SetSkill(SkillName.MagicResist, 59.1, 63.5); - SetSkill(SkillName.Tactics, 74.6, 76.4); - SetSkill(SkillName.Wrestling, 62.0, 77.1); - - Tamable = true; - ControlSlots = 1; - MinTameSkill = 80.7; - - // TODO(SA-creatures): unported vs ServUO — DragonBlood carving and the minor drops - // SlithEye / AncientPotteryFragments / TatteredAncientScroll (item classes not yet in ModernUO). - } - - public override string CorpseName => "a slith corpse"; - public override string DefaultName => "a slith"; - - public override int TreasureMapLevel => 2; - public override int Meat => 6; - public override int Hides => 10; - - public override MonsterAbility[] GetMonsterAbilities() => _abilities; - - public override void GenerateLoot() - { - AddLoot(LootPack.Average, 2); - } -} diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/StoneSlith.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/StoneSlith.cs deleted file mode 100644 index c7a3d7a45..000000000 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/StoneSlith.cs +++ /dev/null @@ -1,72 +0,0 @@ -using ModernUO.Serialization; -using Server.Items; - -namespace Server.Mobiles; - -[SerializationGenerator(0)] -public partial class StoneSlith : BaseCreature -{ - private static readonly MonsterAbility[] _abilities = [MonsterAbilities.GraspingClaw, MonsterAbilities.TailSwipe]; - - [Constructible] - public StoneSlith() : base(AIType.AI_Melee) - { - Body = 734; - - SetStr(250, 300); - SetDex(76, 90); - SetInt(34, 69); - - SetHits(154, 166); - SetStam(76, 90); - SetMana(34, 69); - - SetDamage(6, 24); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 50, 55); - SetResistance(ResistanceType.Fire, 20, 30); - SetResistance(ResistanceType.Cold, 10, 20); - SetResistance(ResistanceType.Poison, 30, 40); - SetResistance(ResistanceType.Energy, 30, 40); - - SetSkill(SkillName.MagicResist, 86.8, 95.1); - SetSkill(SkillName.Tactics, 82.6, 88.6); - SetSkill(SkillName.Wrestling, 75.8, 87.4); - SetSkill(SkillName.Anatomy, 0.0, 2.9); - - Tamable = true; - MinTameSkill = 65.1; - ControlSlots = 2; - - // TODO(SA-creatures): unported vs ServUO — DragonBlood carving, and the minor drops - // SlithEye / TatteredAncientScroll / AncientPotteryFragments (item classes not yet in ModernUO). - } - - public override string CorpseName => "a slith corpse"; - public override string DefaultName => "a stone slith"; - - public override int Meat => 1; - public override int Hides => 12; - public override HideType HideType => HideType.Spined; - - public override WeaponAbility GetWeaponAbility() => WeaponAbility.BleedAttack; - - public override MonsterAbility[] GetMonsterAbilities() => _abilities; - - public override void GenerateLoot() - { - AddLoot(LootPack.Average, 2); - } - - public override void OnDeath(Container c) - { - base.OnDeath(c); - - if (!Controlled && Utility.RandomDouble() <= 0.005) - { - c.DropItem(new StoneSlithClaw()); - } - } -} diff --git a/Projects/UOContent/Mobiles/Monsters/Reptile/ToxicSlith.cs b/Projects/UOContent/Mobiles/Monsters/Reptile/ToxicSlith.cs deleted file mode 100644 index fa33e4cb2..000000000 --- a/Projects/UOContent/Mobiles/Monsters/Reptile/ToxicSlith.cs +++ /dev/null @@ -1,56 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Mobiles; - -[SerializationGenerator(0)] -public partial class ToxicSlith : BaseCreature -{ - // ServUO SpecialAbility.DragonBreath -> closest ModernUO monster ability. - private static readonly MonsterAbility[] _abilities = [MonsterAbilities.FireBreath]; - - [Constructible] - public ToxicSlith() : base(AIType.AI_Melee) - { - Body = 734; - Hue = 476; - - SetStr(223, 306); - SetDex(231, 258); - SetInt(30, 35); - - SetHits(197, 215); - SetStam(231, 258); - - SetDamage(6, 24); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 35, 45); - SetResistance(ResistanceType.Fire, 0, 9); - SetResistance(ResistanceType.Cold, 5, 10); - SetResistance(ResistanceType.Poison, 100, 100); - SetResistance(ResistanceType.Energy, 5, 7); - - SetSkill(SkillName.MagicResist, 95.4, 98.3); - SetSkill(SkillName.Tactics, 85.5, 90.9); - SetSkill(SkillName.Wrestling, 90.4, 95.1); - SetSkill(SkillName.Poisoning, 90.0, 110.0); - - // TODO(SA-creatures): unported vs ServUO — DragonBlood carving and the minor drops - // ToxicVenomSac / SlithEye / AncientPotteryFragments / TatteredAncientScroll (not yet in ModernUO). - } - - public override string CorpseName => "a slith corpse"; - public override string DefaultName => "a toxic slith"; - - public override int Meat => 6; - public override int Hides => 11; - public override HideType HideType => HideType.Horned; - - public override MonsterAbility[] GetMonsterAbilities() => _abilities; - - public override void GenerateLoot() - { - AddLoot(LootPack.Average, 2); - } -} diff --git a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedAirElemental.cs b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedAirElemental.cs index e6cc90190..467304a1c 100644 --- a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedAirElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedAirElemental.cs @@ -12,73 +12,39 @@ namespace Server.Mobiles Hue = 0x4001; BaseSoundID = 655; - if (Core.AOS) - { - SetStr(200); - SetDex(200); - SetInt(100); + SetStr(200); + SetDex(200); + SetInt(100); - SetHits(150); - SetStam(50); + SetHits(150); + SetStam(50); - SetDamage(6, 9); + SetDamage(6, 9); - SetDamageType(ResistanceType.Physical, 50); - SetDamageType(ResistanceType.Energy, 50); + SetDamageType(ResistanceType.Physical, 50); + SetDamageType(ResistanceType.Energy, 50); - SetResistance(ResistanceType.Physical, 40, 50); - SetResistance(ResistanceType.Fire, 30, 40); - SetResistance(ResistanceType.Cold, 35, 45); - SetResistance(ResistanceType.Poison, 50, 60); - SetResistance(ResistanceType.Energy, 70, 80); + SetResistance(ResistanceType.Physical, 40, 50); + SetResistance(ResistanceType.Fire, 30, 40); + SetResistance(ResistanceType.Cold, 35, 45); + SetResistance(ResistanceType.Poison, 50, 60); + SetResistance(ResistanceType.Energy, 70, 80); - SetSkill(SkillName.Meditation, 90.0); - SetSkill(SkillName.EvalInt, 70.0); - SetSkill(SkillName.Magery, 70.0); - SetSkill(SkillName.MagicResist, 60.0); - SetSkill(SkillName.Tactics, 100.0); - SetSkill(SkillName.Wrestling, 80.0); - } - else - { - SetStr(126, 155); - SetDex(166, 185); - SetInt(101, 125); - - SetHits(76, 93); - - SetDamage(8, 10); - - SetDamageType(ResistanceType.Physical, 20); - SetDamageType(ResistanceType.Cold, 40); - SetDamageType(ResistanceType.Energy, 40); - - SetResistance(ResistanceType.Physical, 35, 45); - SetResistance(ResistanceType.Fire, 15, 25); - SetResistance(ResistanceType.Cold, 10, 20); - SetResistance(ResistanceType.Poison, 10, 20); - SetResistance(ResistanceType.Energy, 25, 35); - - SetSkill(SkillName.EvalInt, 60.1, 75.0); - SetSkill(SkillName.Magery, 60.1, 75.0); - SetSkill(SkillName.MagicResist, 60.1, 75.0); - SetSkill(SkillName.Tactics, 60.1, 80.0); - SetSkill(SkillName.Wrestling, 60.1, 80.0); - } + SetSkill(SkillName.Meditation, 90.0); + SetSkill(SkillName.EvalInt, 70.0); + SetSkill(SkillName.Magery, 70.0); + SetSkill(SkillName.MagicResist, 60.0); + SetSkill(SkillName.Tactics, 100.0); + SetSkill(SkillName.Wrestling, 80.0); VirtualArmor = 40; ControlSlots = 2; } - public override bool DeleteCorpseOnDeath => Summoned; - public override string CorpseName => "an air elemental corpse"; - public override string DefaultName => "an air elemental"; - - public override bool BleedImmune => true; - public override double DispelDifficulty => 117.5; public override double DispelFocus => 45.0; + public override string DefaultName => "an air elemental"; [AfterDeserialization] private void AfterDeserialization() diff --git a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedDaemon.cs b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedDaemon.cs index 6486e10c5..74220f53d 100644 --- a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedDaemon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedDaemon.cs @@ -12,67 +12,37 @@ namespace Server.Mobiles Body = Core.AOS ? 10 : 9; BaseSoundID = 357; - if (Core.AOS) - { - SetStr(200); - SetDex(110); - SetInt(150); + SetStr(200); + SetDex(110); + SetInt(150); - SetDamage(14, 21); + SetDamage(14, 21); - SetDamageType(ResistanceType.Physical, 0); - SetDamageType(ResistanceType.Poison, 100); + SetDamageType(ResistanceType.Physical, 0); + SetDamageType(ResistanceType.Poison, 100); - SetResistance(ResistanceType.Physical, 45, 55); - SetResistance(ResistanceType.Fire, 50, 60); - SetResistance(ResistanceType.Cold, 20, 30); - SetResistance(ResistanceType.Poison, 70, 80); - SetResistance(ResistanceType.Energy, 40, 50); + SetResistance(ResistanceType.Physical, 45, 55); + SetResistance(ResistanceType.Fire, 50, 60); + SetResistance(ResistanceType.Cold, 20, 30); + SetResistance(ResistanceType.Poison, 70, 80); + SetResistance(ResistanceType.Energy, 40, 50); - SetSkill(SkillName.EvalInt, 90.1, 100.0); - SetSkill(SkillName.Meditation, 90.1, 100.0); - SetSkill(SkillName.Magery, 90.1, 100.0); - SetSkill(SkillName.MagicResist, 90.1, 100.0); - SetSkill(SkillName.Tactics, 100.0); - SetSkill(SkillName.Wrestling, 98.1, 99.0); - } - else - { - SetStr(476, 505); - SetDex(76, 95); - SetInt(301, 325); - - SetHits(286, 303); - - SetDamage(7, 14); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 45, 60); - SetResistance(ResistanceType.Fire, 50, 60); - SetResistance(ResistanceType.Cold, 30, 40); - SetResistance(ResistanceType.Poison, 20, 30); - SetResistance(ResistanceType.Energy, 30, 40); - - SetSkill(SkillName.EvalInt, 70.1, 80.0); - SetSkill(SkillName.Magery, 70.1, 80.0); - SetSkill(SkillName.MagicResist, 85.1, 95.0); - SetSkill(SkillName.Tactics, 70.1, 80.0); - SetSkill(SkillName.Wrestling, 60.1, 80.0); - } + SetSkill(SkillName.EvalInt, 90.1, 100.0); + SetSkill(SkillName.Meditation, 90.1, 100.0); + SetSkill(SkillName.Magery, 90.1, 100.0); + SetSkill(SkillName.MagicResist, 90.1, 100.0); + SetSkill(SkillName.Tactics, 100.0); + SetSkill(SkillName.Wrestling, 98.1, 99.0); VirtualArmor = 58; - ControlSlots = Core.SE ? 4 : 5; } - - public override bool DeleteCorpseOnDeath => Summoned; + public override string CorpseName => "a daemon corpse"; - public override double DispelDifficulty => 125.0; public override double DispelFocus => 45.0; - public override Poison PoisonImmune => Poison.Regular; + public override Poison PoisonImmune => Poison.Regular; // TODO: Immune to poison? public override bool CanFly => true; } } diff --git a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedEarthElemental.cs b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedEarthElemental.cs index 60bb387c8..5a4d0ff80 100644 --- a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedEarthElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedEarthElemental.cs @@ -11,63 +11,33 @@ namespace Server.Mobiles Body = 14; BaseSoundID = 268; - if (Core.AOS) - { - SetStr(200); - SetDex(70); - SetInt(70); + SetStr(200); + SetDex(70); + SetInt(70); - SetHits(180); + SetHits(180); - SetDamage(14, 21); + SetDamage(14, 21); - SetDamageType(ResistanceType.Physical, 100); + SetDamageType(ResistanceType.Physical, 100); - SetResistance(ResistanceType.Physical, 65, 75); - SetResistance(ResistanceType.Fire, 40, 50); - SetResistance(ResistanceType.Cold, 40, 50); - SetResistance(ResistanceType.Poison, 40, 50); - SetResistance(ResistanceType.Energy, 40, 50); + SetResistance(ResistanceType.Physical, 65, 75); + SetResistance(ResistanceType.Fire, 40, 50); + SetResistance(ResistanceType.Cold, 40, 50); + SetResistance(ResistanceType.Poison, 40, 50); + SetResistance(ResistanceType.Energy, 40, 50); - SetSkill(SkillName.MagicResist, 65.0); - SetSkill(SkillName.Tactics, 100.0); - SetSkill(SkillName.Wrestling, 90.0); - } - else - { - SetStr(126, 155); - SetDex(66, 85); - SetInt(71, 92); - - SetHits(76, 93); - - SetDamage(9, 16); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 30, 35); - SetResistance(ResistanceType.Fire, 10, 20); - SetResistance(ResistanceType.Cold, 10, 20); - SetResistance(ResistanceType.Poison, 15, 25); - SetResistance(ResistanceType.Energy, 15, 25); - - SetSkill(SkillName.MagicResist, 50.1, 95.0); - SetSkill(SkillName.Tactics, 60.1, 100.0); - SetSkill(SkillName.Wrestling, 60.1, 100.0); - } + SetSkill(SkillName.MagicResist, 65.0); + SetSkill(SkillName.Tactics, 100.0); + SetSkill(SkillName.Wrestling, 90.0); VirtualArmor = 34; ControlSlots = 2; } - public override bool DeleteCorpseOnDeath => Summoned; - public override string CorpseName => "an earth elemental corpse"; - public override string DefaultName => "an earth elemental"; - - public override bool BleedImmune => true; - public override double DispelDifficulty => 117.5; public override double DispelFocus => 45.0; + public override string DefaultName => "an earth elemental"; } } diff --git a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedFireElemental.cs b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedFireElemental.cs index e89b391b6..e4647dc81 100644 --- a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedFireElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedFireElemental.cs @@ -12,54 +12,26 @@ namespace Server.Mobiles Body = 15; BaseSoundID = 838; - if (Core.AOS) - { - SetStr(200); - SetDex(200); - SetInt(100); + SetStr(200); + SetDex(200); + SetInt(100); - SetDamage(9, 14); + SetDamage(9, 14); - SetDamageType(ResistanceType.Physical, 0); - SetDamageType(ResistanceType.Fire, 100); + SetDamageType(ResistanceType.Physical, 0); + SetDamageType(ResistanceType.Fire, 100); - SetResistance(ResistanceType.Physical, 50, 60); - SetResistance(ResistanceType.Fire, 70, 80); - SetResistance(ResistanceType.Cold, 0, 10); - SetResistance(ResistanceType.Poison, 50, 60); - SetResistance(ResistanceType.Energy, 50, 60); + SetResistance(ResistanceType.Physical, 50, 60); + SetResistance(ResistanceType.Fire, 70, 80); + SetResistance(ResistanceType.Cold, 0, 10); + SetResistance(ResistanceType.Poison, 50, 60); + SetResistance(ResistanceType.Energy, 50, 60); - SetSkill(SkillName.EvalInt, 90.0); - SetSkill(SkillName.Magery, 90.0); - SetSkill(SkillName.MagicResist, 85.0); - SetSkill(SkillName.Tactics, 100.0); - SetSkill(SkillName.Wrestling, 92.0); - } - else - { - SetStr(126, 155); - SetDex(166, 185); - SetInt(101, 125); - - SetHits(76, 93); - - SetDamage(7, 9); - - SetDamageType(ResistanceType.Physical, 25); - SetDamageType(ResistanceType.Fire, 75); - - SetResistance(ResistanceType.Physical, 35, 45); - SetResistance(ResistanceType.Fire, 60, 80); - SetResistance(ResistanceType.Cold, 5, 10); - SetResistance(ResistanceType.Poison, 30, 40); - SetResistance(ResistanceType.Energy, 30, 40); - - SetSkill(SkillName.EvalInt, 60.1, 75.0); - SetSkill(SkillName.Magery, 60.1, 75.0); - SetSkill(SkillName.MagicResist, 75.2, 105.0); - SetSkill(SkillName.Tactics, 80.1, 100.0); - SetSkill(SkillName.Wrestling, 70.1, 100.0); - } + SetSkill(SkillName.EvalInt, 90.0); + SetSkill(SkillName.Magery, 90.0); + SetSkill(SkillName.MagicResist, 85.0); + SetSkill(SkillName.Tactics, 100.0); + SetSkill(SkillName.Wrestling, 92.0); VirtualArmor = 40; ControlSlots = 4; @@ -67,14 +39,9 @@ namespace Server.Mobiles AddItem(new LightSource()); } - public override bool DeleteCorpseOnDeath => Summoned; - public override string CorpseName => "a fire elemental corpse"; - public override string DefaultName => "a fire elemental"; - - public override bool BleedImmune => true; - public override double DispelDifficulty => 117.5; public override double DispelFocus => 45.0; + public override string DefaultName => "a fire elemental"; } } diff --git a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedWaterElemental.cs b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedWaterElemental.cs index fa6742360..d6c146a95 100644 --- a/Projects/UOContent/Mobiles/Monsters/Summons/SummonedWaterElemental.cs +++ b/Projects/UOContent/Mobiles/Monsters/Summons/SummonedWaterElemental.cs @@ -11,70 +11,38 @@ namespace Server.Mobiles Body = 16; BaseSoundID = 278; - if (Core.AOS) - { - SetStr(200); - SetDex(70); - SetInt(100); + SetStr(200); + SetDex(70); + SetInt(100); - SetHits(165); + SetHits(165); - SetDamage(12, 16); + SetDamage(12, 16); - SetDamageType(ResistanceType.Physical, 0); - SetDamageType(ResistanceType.Cold, 100); + SetDamageType(ResistanceType.Physical, 0); + SetDamageType(ResistanceType.Cold, 100); - SetResistance(ResistanceType.Physical, 50, 60); - SetResistance(ResistanceType.Fire, 20, 30); - SetResistance(ResistanceType.Cold, 70, 80); - SetResistance(ResistanceType.Poison, 45, 55); - SetResistance(ResistanceType.Energy, 40, 50); + SetResistance(ResistanceType.Physical, 50, 60); + SetResistance(ResistanceType.Fire, 20, 30); + SetResistance(ResistanceType.Cold, 70, 80); + SetResistance(ResistanceType.Poison, 45, 55); + SetResistance(ResistanceType.Energy, 40, 50); - SetSkill(SkillName.Meditation, 90.0); - SetSkill(SkillName.EvalInt, 80.0); - SetSkill(SkillName.Magery, 80.0); - SetSkill(SkillName.MagicResist, 75.0); - SetSkill(SkillName.Tactics, 100.0); - SetSkill(SkillName.Wrestling, 85.0); - } - else - { - SetStr(126, 155); - SetDex(66, 85); - SetInt(101, 125); - - SetHits(76, 93); - - SetDamage(7, 9); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 35, 45); - SetResistance(ResistanceType.Fire, 10, 25); - SetResistance(ResistanceType.Cold, 10, 25); - SetResistance(ResistanceType.Poison, 60, 70); - SetResistance(ResistanceType.Energy, 5, 10); - - SetSkill(SkillName.EvalInt, 60.1, 75.0); - SetSkill(SkillName.Magery, 60.1, 75.0); - SetSkill(SkillName.MagicResist, 100.1, 115.0); - SetSkill(SkillName.Tactics, 50.1, 70.0); - SetSkill(SkillName.Wrestling, 50.1, 70.0); - } + SetSkill(SkillName.Meditation, 90.0); + SetSkill(SkillName.EvalInt, 80.0); + SetSkill(SkillName.Magery, 80.0); + SetSkill(SkillName.MagicResist, 75.0); + SetSkill(SkillName.Tactics, 100.0); + SetSkill(SkillName.Wrestling, 85.0); VirtualArmor = 40; ControlSlots = 3; CanSwim = true; } - public override bool DeleteCorpseOnDeath => Summoned; - public override string CorpseName => "a water elemental corpse"; - public override string DefaultName => "a water elemental"; - - public override bool BleedImmune => true; - public override double DispelDifficulty => 117.5; public override double DispelFocus => 45.0; + public override string DefaultName => "a water elemental"; } } diff --git a/Projects/UOContent/Mobiles/NPCSpeeds.cs b/Projects/UOContent/Mobiles/NPCSpeeds.cs index 8f5e908bb..3a2bc201e 100644 --- a/Projects/UOContent/Mobiles/NPCSpeeds.cs +++ b/Projects/UOContent/Mobiles/NPCSpeeds.cs @@ -22,12 +22,32 @@ public static class NPCSpeeds private static readonly Dictionary _speedsByType = new(); private static readonly Dictionary _speedsByLevel = new(); + // Enabled for pets on HS+ + public static bool ScaleSpeedByDex { get; private set; } + public static double MinDelay { get; private set; } + public static double MaxDelay { get; private set; } + public static int MinDex { get; private set; } + public static int MaxDex { get; private set; } + // Time period to lock NPCs into idling public static int MinIdleSeconds { get; private set; } public static int MaxIdleSeconds { get; private set; } public static void GetSpeeds(BaseCreature bc, out double activeSpeed, out double passiveSpeed) { + // Used for scaling pet's speed by dex in HS+ + if (bc.ScaleSpeedByDex) + { + var maxDex = MaxDex; + double min = MinDelay; + double max = MaxDelay; + var dex = Math.Clamp(bc.Dex, MinDex, maxDex); + + activeSpeed = Math.Max(max - (max - min) * ((double)dex / maxDex), min); + passiveSpeed = activeSpeed * 2; + return; + } + if ((bc.SpeedClass == SpeedLevel.None || !_speedsByLevel.TryGetValue(bc.SpeedClass, out var sp)) && !_speedsByType.TryGetValue(bc.GetType(), out sp)) { @@ -38,22 +58,6 @@ public static class NPCSpeeds passiveSpeed = sp.PassiveSpeed; } - // Move speeds are optional (0 = inherit), so this tolerates a missing entry or table. - public static void GetMoveSpeeds(BaseCreature bc, out double activeMoveSpeed, out double passiveMoveSpeed) - { - if ((bc.SpeedClass == SpeedLevel.None || !_speedsByLevel.TryGetValue(bc.SpeedClass, out var sp)) && - !_speedsByType.TryGetValue(bc.GetType(), out sp) && - !_speedsByLevel.TryGetValue(SpeedLevel.Medium, out sp)) - { - activeMoveSpeed = 0; - passiveMoveSpeed = 0; - return; - } - - activeMoveSpeed = sp.ActiveMoveSpeed; - passiveMoveSpeed = sp.PassiveMoveSpeed; - } - public static void RegisterSpeed(SpeedClassEntry entry) { _speedsByLevel[entry.Level] = entry; @@ -66,6 +70,11 @@ public static class NPCSpeeds public static void Configure() { + ScaleSpeedByDex = ServerConfiguration.GetSetting("movement.delay.scaleSpeedByDex", Core.HS); + MinDelay = ServerConfiguration.GetSetting("movement.delay.npcMinDelay", 0.1); + MaxDelay = ServerConfiguration.GetSetting("movement.delay.npcMaxDelay", 0.4); + MaxDex = ServerConfiguration.GetSetting("movement.delay.npcMinDex", 50); + MaxDex = ServerConfiguration.GetSetting("movement.delay.npcMaxDex", 200); MinIdleSeconds = ServerConfiguration.GetSetting("movement.delay.npcMinIdle", 15); MaxIdleSeconds = ServerConfiguration.GetSetting("movement.delay.npcMaxIdle", 25); @@ -94,13 +103,6 @@ public static class NPCSpeeds [JsonPropertyName("passive")] public double PassiveSpeed { get; init; } - // Movement clock (seconds per step); absent/0 = inherit the matching think value. - [JsonPropertyName("activeMove")] - public double ActiveMoveSpeed { get; init; } - - [JsonPropertyName("passiveMove")] - public double PassiveMoveSpeed { get; init; } - [JsonPropertyName("types")] public HashSet Types { get; init; } } diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index 83c0e4cca..abf2d5c67 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -1,17 +1,14 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using ModernUO.CodeGeneratedEvents; using Server.Accounting; using Server.Collections; using Server.ContextMenus; -using Server.Engines.BuffIcons; using Server.Engines.BulkOrders; using Server.Engines.CannedEvil; using Server.Engines.ConPVP; using Server.Engines.Craft; using Server.Engines.Help; -using Server.Engines.Insurance; using Server.Engines.MLQuests; using Server.Engines.MLQuests.Gumps; using Server.Engines.PartySystem; @@ -36,9 +33,8 @@ using Server.Spells.Necromancy; using Server.Spells.Ninjitsu; using Server.Spells.Sixth; using Server.Spells.Spellweaving; -using Server.Systems.FeatureFlags; using Server.Targeting; -using BaseMLQuestGump = Server.Engines.MLQuests.Gumps.BaseMLQuestGump; +using BaseQuestGump = Server.Engines.MLQuests.Gumps.BaseQuestGump; using CalcMoves = Server.Movement.Movement; using QuestOfferGump = Server.Engines.MLQuests.Gumps.QuestOfferGump; using RankDefinition = Server.Guilds.RankDefinition; @@ -162,7 +158,7 @@ namespace Server.Mobiles /* * a value of zero means, that the mobile is not executing the spell. Otherwise, * the value should match the BaseMana required - */ + */ private RankDefinition m_GuildRank; @@ -170,6 +166,9 @@ namespace Server.Mobiles public DateTime _honorTime; + private Mobile m_InsuranceAward; + private int m_InsuranceBonus; + private int m_LastGlobalLight = -1, m_LastPersonalLight = -1; private bool m_LastProtectedMessage; @@ -188,6 +187,9 @@ namespace Server.Mobiles private bool m_NoDeltaRecursion; + // number of items that could not be automatically reinsured because gold in bank was not enough + private int m_NonAutoreinsuredItems; + private DateTime m_SavagePaintExpiration; private DateTime[] m_StuckMenuUses; @@ -349,10 +351,6 @@ namespace Server.Mobiles public override bool NewGuildDisplay => Guilds.Guild.NewGuildSystem; - public override bool Murderer => - Core.T2A && !Core.LBR && PlayerMurderSystem.GetMurderContext(this, out var context) - && context.PingPongs >= 5 || base.Murderer; - public bool BedrollLogout { get; set; } [CommandProperty(AccessLevel.GameMaster)] @@ -531,6 +529,8 @@ namespace Server.Mobiles set => SetFlag(PlayerFlag.RefuseTrades, value); } + public Dictionary RecoverableAmmo { get; set; } + [CommandProperty(AccessLevel.GameMaster)] public DateTime AcceleratedStart { get; set; } @@ -677,7 +677,7 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster)] public bool Young { - get => ContentFeatureFlags.YoungPlayerSystem && GetFlag(PlayerFlag.Young); + get => GetFlag(PlayerFlag.Young); set { SetFlag(PlayerFlag.Young, value); @@ -697,13 +697,6 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster, canModify: true)] public ChampionTitleContext ChampionTitles => ChampionTitleSystem.GetOrCreateChampionTitleContext(this); - [CommandProperty(AccessLevel.GameMaster)] - public int PingPongs - { - get => PlayerMurderSystem.GetMurderContext(this, out var context) ? context.PingPongs : 0; - set => PlayerMurderSystem.ManuallySetPingPong(this, value); - } - [CommandProperty(AccessLevel.GameMaster)] public int ShortTermMurders { @@ -759,7 +752,7 @@ namespace Server.Mobiles Freeze(TimeSpan.FromSeconds(1)); Animate(61, 10, 1, true, false, 0); Flying = false; - RemoveBuff(BuffIcon.Fly); + BuffInfo.RemoveBuff(this, BuffIcon.Fly); SendMessage("You have landed."); BaseMount.Dismount(this); @@ -817,7 +810,7 @@ namespace Server.Mobiles else { Flying = false; - RemoveBuff(BuffIcon.Fly); + BuffInfo.RemoveBuff(this, BuffIcon.Fly); } } } @@ -861,7 +854,7 @@ namespace Server.Mobiles if (Core.AOS) { - foreach (var m in Map.GetMobilesAt(location)) + foreach (Mobile m in Map.GetMobilesAt(location)) { if (m.Z >= location.Z && m.Z < location.Z + 16 && (!m.Hidden || m.AccessLevel == AccessLevel.Player)) { @@ -903,17 +896,17 @@ namespace Server.Mobiles { Direction.North => itemIDs[0], Direction.South => itemIDs[0], - Direction.East => itemIDs[1], - Direction.West => itemIDs[1], - _ => item.ItemID + Direction.East => itemIDs[1], + Direction.West => itemIDs[1], + _ => item.ItemID }, 4 => dir switch { Direction.South => itemIDs[0], - Direction.East => itemIDs[1], + Direction.East => itemIDs[1], Direction.North => itemIDs[2], - Direction.West => itemIDs[3], - _ => item.ItemID + Direction.West => itemIDs[3], + _ => item.ItemID }, _ => item.ItemID }; @@ -984,7 +977,7 @@ namespace Server.Mobiles } if (skillId == 35) - // AnimalTaming.DeferredTarget = true; + // AnimalTaming.DeferredTarget = true; { AnimalTaming.DisableMessage = false; } @@ -992,71 +985,67 @@ namespace Server.Mobiles from.TargetLocked = false; } - public static void EquipMacro(Mobile m, ref PooledRefList list) + public static void EquipMacro(Mobile m, List list) { - if (m is not PlayerMobile { Alive: true } pm || pm.Backpack == null) + if (m is PlayerMobile { Alive: true } pm && pm.Backpack != null) { - return; - } + var pack = pm.Backpack; - var pack = pm.Backpack; - - foreach (var serial in list) - { - Item item = null; - foreach (var i in pack.Items) + foreach (var serial in list) { - if (i.Serial == serial) + Item item = null; + foreach (var i in pack.Items) { - item = i; - break; + if (i.Serial == serial) + { + item = i; + break; + } } - } - if (item == null) - { - continue; - } - - var toMove = pm.FindItemOnLayer(item.Layer); - - if (toMove != null) - { - // pack.DropItem(toMove); - toMove.Internalize(); - - if (!pm.EquipItem(item)) + if (item == null) { - pm.EquipItem(toMove); + continue; + } + + var toMove = pm.FindItemOnLayer(item.Layer); + + if (toMove != null) + { + // pack.DropItem(toMove); + toMove.Internalize(); + + if (!pm.EquipItem(item)) + { + pm.EquipItem(toMove); + } + else + { + pack.DropItem(toMove); + } } else { - pack.DropItem(toMove); + pm.EquipItem(item); } } - else - { - pm.EquipItem(item); - } } } - public static void UnequipMacro(Mobile m, ref PooledRefList layers) + public static void UnequipMacro(Mobile m, List layers) { - if (m is not PlayerMobile { Alive: true } pm || pm.Backpack == null) + if (m is PlayerMobile { Alive: true } pm && pm.Backpack != null) { - return; - } + var pack = pm.Backpack; + var eq = m.Items; - var pack = pm.Backpack; - var eq = m.Items; - - for (var i = eq.Count - 1; i >= 0; i--) - { - var item = eq[i]; - if (layers.Contains(item.Layer)) + for (var i = eq.Count - 1; i >= 0; i--) { - pack.TryDropItem(pm, item, false); + var item = eq[i]; + if (layers.Contains(item.Layer)) + { + pack.TryDropItem(pm, item, false); + } } } } @@ -1227,7 +1216,7 @@ namespace Server.Mobiles if (!Meditating) { - RemoveBuff(BuffIcon.ActiveMeditation); + BuffInfo.RemoveBuff(this, BuffIcon.ActiveMeditation); } } @@ -1275,7 +1264,6 @@ namespace Server.Mobiles VirtueSystem.CheckAtrophies(from); from.ClaimAutoStabledPets(); AnimalForm.GetContext(from)?.Timer.Start(); - from.ResendBuffs(); } private class ServerLockdownNoticeGump : StaticNoticeGump @@ -1557,17 +1545,13 @@ namespace Server.Mobiles // Eject all from house from.RevealingAction(); - var list = context.Foundation.GetItems(); - for (var i = 0; i < list.Count; i++) + foreach (var item in context.Foundation.GetItems()) { - var item = list[i]; item.Location = context.Foundation.BanLocation; } - using var mobiles = context.Foundation.GetMobilesPooled(); - for (var i = 0; i < mobiles.Count; i++) + foreach (var mobile in context.Foundation.GetMobiles()) { - var mobile = mobiles[i]; mobile.Location = context.Foundation.BanLocation; } @@ -1607,8 +1591,10 @@ namespace Server.Mobiles { base.OnHiddenChanged(); - // Always remove, default to the hiding icon EXCEPT in the invis spell where it's explicitly set - RemoveBuff(BuffIcon.Invisibility); + RemoveBuff( + BuffIcon + .Invisibility + ); // Always remove, default to the hiding icon EXCEPT in the invis spell where it's explicitly set if (!Hidden) { @@ -1616,8 +1602,10 @@ namespace Server.Mobiles } else // if (!InvisibilitySpell.HasTimer( this )) { - // Hidden/Stealthing & You Are Hidden - AddBuff(new BuffInfo(BuffIcon.HidingAndOrStealth, 1075655)); + BuffInfo.AddBuff( + this, + new BuffInfo(BuffIcon.HidingAndOrStealth, 1075655) + ); // Hidden/Stealthing & You Are Hidden } } @@ -1770,25 +1758,16 @@ namespace Server.Mobiles public override bool AllowItemUse(Item item) { - if (AccessLevel < FeatureFlagSettings.RequiredAccessLevel && - FeatureFlagManager.IsItemUseBlocked(item.GetType(), out var reason)) + if (DuelContext?.AllowItemUse(this, item) == false) { - SendMessage(0x22, reason); return false; } - return DuelContext?.AllowItemUse(this, item) != false && DesignContext.Check(this); + return DesignContext.Check(this); } public override bool AllowSkillUse(SkillName skill) { - if (AccessLevel < FeatureFlagSettings.RequiredAccessLevel - && FeatureFlagManager.IsSkillBlocked(skill, out var reason)) - { - SendMessage(0x22, reason); - return false; - } - if (AnimalForm.UnderTransformation(this)) { for (var i = 0; i < AnimalFormRestrictedSkills.Length; i++) @@ -1840,7 +1819,7 @@ namespace Server.Mobiles // moving, not teleporting var zDrop = Location.Z - loc.Z; - if (zDrop > 20) // we fell more than one story + if (zDrop > 20) // we fell more than one story { Hits -= zDrop / 20 * 10 - 5; // deal some damage; does not kill, disrupt, etc } @@ -1869,7 +1848,7 @@ namespace Server.Mobiles if (Alive) { - if (Insurance.Enabled) + if (InsuranceEnabled) { if (Core.SA) { @@ -1997,9 +1976,9 @@ namespace Server.Mobiles { var house = BaseHouse.FindHouseAt(this); - if (CheckAlive() && house?.IsOwner(this) == true && house.InternalizedVendors.Count > 0 && NetState != null) + if (CheckAlive() && house?.IsOwner(this) == true && house.InternalizedVendors.Count > 0 && NetState is NetState { } ns) { - ReclaimVendorGump.DisplayTo(this, house); + ns.SendGump(new ReclaimVendorGump(house)); } } @@ -2048,13 +2027,6 @@ namespace Server.Mobiles public override bool CheckEquip(Item item) { - if (AccessLevel < FeatureFlagSettings.RequiredAccessLevel - && FeatureFlagManager.IsItemEquipBlocked(item.GetType(), out var reason)) - { - SendMessage(0x22, reason); - return false; - } - if (!base.CheckEquip(item)) { return false; @@ -2305,8 +2277,7 @@ namespace Server.Mobiles pm.DuelPlayer.Eliminated) || base.OnMoveOver(m); public override bool CheckShove(Mobile shoved) => - IgnoreMobiles || shoved.IgnoreMobiles || - TransformationSpellHelper.UnderTransformation(shoved, typeof(WraithFormSpell)) || + IgnoreMobiles || shoved.IgnoreMobiles || TransformationSpellHelper.UnderTransformation(shoved, typeof(WraithFormSpell)) || base.CheckShove(shoved); protected override void OnMapChange(Map oldMap) @@ -2375,6 +2346,11 @@ namespace Server.Mobiles ReceivedHonorContext?.OnTargetDamaged(from, amount); SentHonorContext?.OnSourceDamaged(from, amount); + if (willKill && from is PlayerMobile mobile) + { + Timer.StartTimer(TimeSpan.FromSeconds(10), mobile.RecoverAmmo); + } + base.OnDamage(amount, from, willKill); } @@ -2386,7 +2362,7 @@ namespace Server.Mobiles if (Alive && !wasAlive) { - var deathRobe = new DeathRobe(); + Item deathRobe = new DeathRobe(); if (!EquipItem(deathRobe)) { @@ -2395,10 +2371,17 @@ namespace Server.Mobiles } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void OnWarmodeChanged() + { + if (!Warmode) + { + Timer.StartTimer(TimeSpan.FromSeconds(10), RecoverAmmo); + } + } + private bool FindItems_Callback(Item item) => - !item.Deleted && Backpack != item.Parent && - (item.LootType == LootType.Blessed || ServerFeatureFlags.InsuranceEnabled && item.Insured); + !item.Deleted && (item.LootType == LootType.Blessed || item.Insured) && + Backpack != item.Parent; public override bool OnBeforeDeath() { @@ -2412,22 +2395,95 @@ namespace Server.Mobiles // This fixes a "bug" where players put blessed items in nested bags and they were dropped on death if (Core.AOS && Backpack?.Deleted == false) { - using var queue = Backpack.EnumerateItems(true, FindItems_Callback); - foreach (var item in queue) + foreach (var item in Backpack.EnumerateItems(true, FindItems_Callback)) { Backpack.AddItem(item); } } - EquipSnapshot = [..Items]; - Insurance.CheckInsuranceBeforeDeath(this); + EquipSnapshot = new List(Items); + + m_NonAutoreinsuredItems = 0; + m_InsuranceAward = FindMostRecentDamager(false); + + if (m_InsuranceAward is BaseCreature creature) + { + var master = creature.GetMaster(); + + if (master != null) + { + m_InsuranceAward = master; + } + } + + if (m_InsuranceAward != null && (!m_InsuranceAward.Player || m_InsuranceAward == this)) + { + m_InsuranceAward = null; + } + + if (m_InsuranceAward is PlayerMobile mobile) + { + mobile.m_InsuranceBonus = 0; + } ReceivedHonorContext?.OnTargetKilled(); SentHonorContext?.OnSourceKilled(); + RecoverAmmo(); + return base.OnBeforeDeath(); } + private bool CheckInsuranceOnDeath(Item item) + { + if (!InsuranceEnabled || !item.Insured) + { + return false; + } + + if (DuelContext?.Registered == true && DuelContext.Started && + m_DuelPlayer?.Eliminated != true) + { + return true; + } + + if (AutoRenewInsurance) + { + var cost = GetInsuranceCost(item); + + if (m_InsuranceAward != null) + { + cost /= 2; + } + + if (Banker.Withdraw(this, cost)) + { + item.PaidInsurance = true; + // ~1_AMOUNT~ gold has been withdrawn from your bank box. + SendLocalizedMessage(1060398, cost.ToString()); + } + else + { + SendLocalizedMessage(1061079, "", 0x23); // You lack the funds to purchase the insurance + item.PaidInsurance = false; + item.Insured = false; + m_NonAutoreinsuredItems++; + } + } + else + { + item.PaidInsurance = false; + item.Insured = false; + } + + if (m_InsuranceAward is PlayerMobile insurancePm && Banker.Deposit(m_InsuranceAward, 300)) + { + insurancePm.m_InsuranceBonus += 300; + } + + return true; + } + public override DeathMoveResult GetParentMoveResultFor(Item item) { // It seems all items are unmarked on death, even blessed/insured ones @@ -2436,7 +2492,7 @@ namespace Server.Mobiles item.QuestItem = false; } - if (Insurance.CheckItemInsuranceOnDeath(this, item)) + if (CheckInsuranceOnDeath(item)) { return DeathMoveResult.MoveToBackpack; } @@ -2459,7 +2515,7 @@ namespace Server.Mobiles item.QuestItem = false; } - if (Insurance.CheckItemInsuranceOnDeath(this, item)) + if (CheckInsuranceOnDeath(item)) { return DeathMoveResult.MoveToBackpack; } @@ -2479,6 +2535,11 @@ namespace Server.Mobiles public override void OnDeath(Container c) { + if (m_NonAutoreinsuredItems > 0) + { + SendLocalizedMessage(1061115); + } + base.OnDeath(c); EquipSnapshot = null; @@ -2491,7 +2552,7 @@ namespace Server.Mobiles if (Flying) { Flying = false; - RemoveBuff(BuffIcon.Fly); + BuffInfo.RemoveBuff(this, BuffIcon.Fly); } if (PermaFlags.Count > 0) @@ -2509,7 +2570,7 @@ namespace Server.Mobiles } } - if (Murderer && Core.Now >= m_NextJustAward) + if (Kills >= 5 && Core.Now >= m_NextJustAward) { var m = FindMostRecentDamager(false); @@ -2547,7 +2608,11 @@ namespace Server.Mobiles } } - Insurance.CheckInsuranceOnDeath(this); + if (m_InsuranceAward is PlayerMobile insurancePm && insurancePm.m_InsuranceBonus > 0) + { + // ~1_AMOUNT~ gold has been deposited into your bank box. + insurancePm.SendLocalizedMessage(1060397, insurancePm.m_InsuranceBonus.ToString()); + } var killer = FindMostRecentDamager(true); @@ -2560,9 +2625,12 @@ namespace Server.Mobiles } } - if (Young && DuelContext == null && YoungDeathTeleport()) + if (Young && DuelContext == null) { - Timer.StartTimer(TimeSpan.FromSeconds(2.5), SendYoungDeathNotice); + if (YoungDeathTeleport()) + { + Timer.StartTimer(TimeSpan.FromSeconds(2.5), SendYoungDeathNotice); + } } if (DuelContext?.Registered != true || !DuelContext.Started || m_DuelPlayer?.Eliminated != false) @@ -2574,13 +2642,13 @@ namespace Server.Mobiles if (m_BuffTable != null) { - using var queue = PooledRefQueue.Create(); + using var queue = PooledRefQueue.Create(); foreach (var buff in m_BuffTable.Values) { if (!buff.RetainThroughDeath) { - queue.Enqueue(buff.ID); + queue.Enqueue(buff); } } @@ -2658,9 +2726,9 @@ namespace Server.Mobiles } } - private static void SendToStaffMessage(PlayerMobile from, string text) + private static void SendToStaffMessage(Mobile from, string text) { - var buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); + Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); foreach (var ns in from.GetClientsInRange(8)) { @@ -2723,10 +2791,13 @@ namespace Server.Mobiles // If the blood oath caster will die then damage is not reflected back to the attacker if (hasBloodOath && Alive && !Deleted && !IsDeadBondedPet) { - // Reflect the attacker's original damage back to them, attributed to the caster. - // The caster is a player, so the Publish 48 resist mitigation does not apply - // (it only reduces reflected damage from creature casters). - from.Damage(BloodOathSpell.ComputeReflectedDamage(amount, 0, applyResistMitigation: false), this); + // In some expansions resisting spells reduces reflect dmg from monster blood oath + var resistReflectedDamage = !from.Player && Core.ML && !Core.HS + ? (from.Skills.MagicResist.Value * 0.5 + 10) / 100 + : 0; + + // Reflect damage to the attacker + from.Damage((int)(amount * (1.0 - resistReflectedDamage)), this); } } @@ -2767,7 +2838,7 @@ namespace Server.Mobiles base.Deserialize(reader); var version = reader.ReadInt(); - var virtues = version < 32 ? Virtues : null; + VirtueContext virtues = version < 32 ? Virtues : null; switch (version) { @@ -2827,8 +2898,7 @@ namespace Server.Mobiles for (var i = 0; i < recipeCount; i++) { var r = reader.ReadInt(); - // Don't add in recipes which we haven't gotten or have been removed - if (version > 33 || reader.ReadBool()) + if (version > 33 || reader.ReadBool()) // Don't add in recipes which we haven't gotten or have been removed { _acquiredRecipes.Add(r); } @@ -2843,7 +2913,6 @@ namespace Server.Mobiles { reader.ReadDeltaTime(); // LastHonorLoss - Not even used } - goto case 23; } case 23: @@ -2939,7 +3008,6 @@ namespace Server.Mobiles { virtues.LastCompassionLoss = reader.ReadDeltaTime(); } - goto case 14; } case 14: @@ -3444,26 +3512,6 @@ namespace Server.Mobiles return true; } - public override void OnMovement(Mobile m, Point3D oldLocation) - { - base.OnMovement(m, oldLocation); - - // Passive detect hidden: either party moving within range can trigger detection - if (m is PlayerMobile && Utility.InRange(Location, m.Location, 4)) - { - if (m.Hidden && AccessLevel == AccessLevel.Player) - { - // A hidden mobile (stealther) moved near us — we try to detect them - DetectHidden.TryDetectStealther(this, m); - } - else if (Hidden && m.AccessLevel == AccessLevel.Player) - { - // We're hidden and a potential detector moved near us — they try to detect us - DetectHidden.TryDetectStealther(m, this); - } - } - } - public void AddFollower(Mobile m) { _allFollowers ??= new HashSet(); @@ -3629,13 +3677,269 @@ namespace Server.Mobiles AutoStabled = null; } - private void ToggleItemInsurance() => Insurance.ToggleItemInsurance(this); + public void RecoverAmmo() + { + if (!Core.SE || !Alive || RecoverableAmmo == null) + { + return; + } - private void OpenItemInsuranceMenu() => Insurance.OpenItemInsuranceMenu(this); + foreach (var kvp in RecoverableAmmo) + { + if (kvp.Value > 0) + { + Item ammo = null; - private void CancelRenewInventoryInsurance() => Insurance.CancelRenewInventoryInsurance(this); + try + { + ammo = kvp.Key.CreateInstance(); + } + catch + { + // ignored + } - private void AutoRenewInventoryInsurance() => Insurance.AutoRenewInventoryInsurance(this); + if (ammo == null) + { + continue; + } + + ammo.Amount = kvp.Value; + + var name = ammo.Name ?? ammo switch + { + Arrow _ => $"arrow{(ammo.Amount != 1 ? "s" : "")}", + Bolt _ => $"bolt{(ammo.Amount != 1 ? "s" : "")}", + _ => $"#{ammo.LabelNumber}" + }; + + PlaceInBackpack(ammo); + SendLocalizedMessage(1073504, $"{ammo.Amount}\t{name}"); // You recover ~1_NUM~ ~2_AMMO~. + } + } + + RecoverableAmmo.Clear(); + } + + private static int GetInsuranceCost(Item item) => 600; + + private void ToggleItemInsurance() + { + if (!CheckAlive()) + { + return; + } + + BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance_Callback); + SendLocalizedMessage(1060868); // Target the item you wish to toggle insurance status on to cancel + } + + private bool CanInsure(Item item) + { + if (item is Container && item is not BaseQuiver || item is BagOfSending or KeyRing or PotionKeg or Sigil) + { + return false; + } + + if (item.Stackable) + { + return false; + } + + if (item.LootType == LootType.Cursed) + { + return false; + } + + if (item.ItemID == 0x204E) // death shroud + { + return false; + } + + if (item.Layer == Layer.Mount) + { + return false; + } + + return item.LootType != LootType.Blessed && item.LootType != LootType.Newbied && item.BlessedFor != this; + } + + private void ToggleItemInsurance_Callback(Mobile from, object obj) + { + if (!CheckAlive()) + { + return; + } + + ToggleItemInsurance_Callback(from, obj as Item, true); + } + + private void ToggleItemInsurance_Callback(Mobile from, Item item, bool target) + { + if (item?.IsChildOf(this) != true) + { + if (target) + { + BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance_Callback); + } + + SendLocalizedMessage( + 1060871, + "", + 0x23 + ); // You can only insure items that you have equipped or that are in your backpack + } + else if (item.Insured) + { + item.Insured = false; + + SendLocalizedMessage(1060874, "", 0x35); // You cancel the insurance on the item + + if (target) + { + BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance_Callback); + SendLocalizedMessage( + 1060868, + "", + 0x23 + ); // Target the item you wish to toggle insurance status on to cancel + } + } + else if (!CanInsure(item)) + { + if (target) + { + BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance_Callback); + } + + SendLocalizedMessage(1060869, "", 0x23); // You cannot insure that + } + else + { + if (!item.PaidInsurance) + { + var cost = GetInsuranceCost(item); + + if (Banker.Withdraw(from, cost)) + { + SendLocalizedMessage( + 1060398, + cost.ToString() + ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + item.PaidInsurance = true; + } + else + { + SendLocalizedMessage(1061079, "", 0x23); // You lack the funds to purchase the insurance + return; + } + } + + item.Insured = true; + + SendLocalizedMessage(1060873, "", 0x23); // You have insured the item + + if (target) + { + BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance_Callback); + SendLocalizedMessage( + 1060868, + "", + 0x23 + ); // Target the item you wish to toggle insurance status on to cancel + } + } + } + + private void AutoRenewInventoryInsurance() + { + if (!CheckAlive()) + { + return; + } + + SendLocalizedMessage( + 1060881, + "", + 0x23 + ); // You have selected to automatically reinsure all insured items upon death + AutoRenewInsurance = true; + } + + private void CancelRenewInventoryInsurance() + { + if (!CheckAlive()) + { + return; + } + + if (Core.SE && NetState is { } ns) + { + if (!ns.HasGump()) + { + ns.SendGump(new CancelRenewInventoryInsuranceGump(this, null)); + } + } + else + { + SendLocalizedMessage( + 1061075, + "", + 0x23 + ); // You have cancelled automatically reinsuring all insured items upon death + AutoRenewInsurance = false; + } + } + + private void OpenItemInsuranceMenu() + { + if (!CheckAlive()) + { + return; + } + + using var queue = PooledRefQueue.Create(128); + + foreach (var item in Items) + { + if (DisplayInItemInsuranceGump(item)) + { + queue.Enqueue(item); + } + } + + var pack = Backpack; + + if (pack != null) + { + foreach (var item in pack.FindItems()) + { + if (DisplayInItemInsuranceGump(item)) + { + queue.Enqueue(item); + } + } + } + + // TODO: Investigate item sorting + + if (NetState is { } ns) + { + ns.CloseGump(); + + if (queue.Count == 0) + { + SendLocalizedMessage(1114915, "", 0x35); // None of your current items meet the requirements for insurance. + } + else + { + ns.SendGump(new ItemInsuranceMenuGump(this, queue.ToArray())); + } + } + } + + private bool DisplayInItemInsuranceGump(Item item) => (item.Visible || AccessLevel >= AccessLevel.GameMaster) && + (item.Insured || CanInsure(item)); private void ToggleQuestItem() { @@ -3651,7 +3955,7 @@ namespace Server.Mobiles { if (NetState != null) { - BaseMLQuestGump.CloseOtherGumps(this); + BaseQuestGump.CloseOtherGumps(this); var gumps = this.GetGumps(); gumps.Close(); gumps.Close(); @@ -3741,7 +4045,7 @@ namespace Server.Mobiles if (EvilOmenSpell.EndEffect(this)) { - poison = Poison.IncreaseLevel(poison); + poison = PoisonImpl.IncreaseLevel(poison); } var result = base.ApplyPoison(from, poison); @@ -3830,8 +4134,6 @@ namespace Server.Mobiles ReceivedHonorContext?.Cancel(); SentHonorContext?.Cancel(); - AmmoRecovery.Forget(this); - if (Stabled != null) { foreach (var stabled in Stabled) @@ -4181,36 +4483,26 @@ namespace Server.Mobiles return; } - var duration = Utility.Max(buffInfo.Duration - (Core.Now - buffInfo.StartTime), TimeSpan.Zero); - if (duration == TimeSpan.Zero) + // Synchronize the buff icon as close to _on the second_ as we can. + var msecs = buffInfo.TimeLength.Milliseconds; + if (msecs >= 8) { - SendAddBuffPacket(buffInfo, 0); - return; - } - - var roundedSeconds = Math.Round(duration.TotalSeconds); - var offset = duration.TotalMilliseconds - roundedSeconds * TimeSpan.MillisecondsPerSecond; - if (offset > 0) - { - Timer.DelayCall( - TimeSpan.FromMilliseconds(offset), - () => + Timer.DelayCall(TimeSpan.FromMilliseconds(msecs), () => + { + // They are still online, we still have the buff icon in the table, and it is the same buff icon + if (NetState != null && m_BuffTable?.GetValueOrDefault(buffInfo.ID) == buffInfo) { - // They are still online, we still have the buff icon in the table, and it is the same buff icon - if (NetState != null && m_BuffTable?.GetValueOrDefault(buffInfo.ID) == buffInfo) - { - SendAddBuffPacket(buffInfo, (long)roundedSeconds); - } + SendAddBuffPacket(buffInfo, (long)buffInfo.TimeLength.TotalMilliseconds - msecs); } - ); + }); } - else // Round up, will be removed a little bit early by the server + else { - SendAddBuffPacket(buffInfo, (long)roundedSeconds); + SendAddBuffPacket(buffInfo, (long)buffInfo.TimeLength.TotalMilliseconds); } } - private void SendAddBuffPacket(BuffInfo buffInfo, long seconds) + private void SendAddBuffPacket(BuffInfo buffInfo, long ticks) { NetState.SendAddBuffPacket( Serial, @@ -4218,7 +4510,7 @@ namespace Server.Mobiles buffInfo.TitleCliloc, buffInfo.SecondaryCliloc, buffInfo.Args, - seconds + ticks ); } @@ -4240,8 +4532,7 @@ namespace Server.Mobiles return; } - RemoveBuff(b.ID); // Check, stop old timer, & subsequently remove the old one. - b.StartTimer(this); + RemoveBuff(b); // Check & subsequently remove the old one. m_BuffTable ??= new Dictionary(); m_BuffTable.Add(b.ID, b); @@ -4249,14 +4540,22 @@ namespace Server.Mobiles SendAddBuffPacket(b); } - public void RemoveBuff(BuffIcon b) + public void RemoveBuff(BuffInfo b) { - if (m_BuffTable?.Remove(b, out var buffInfo) != true) + if (b == null) { return; } - buffInfo.StopTimer(); + RemoveBuff(b.ID); + } + + public void RemoveBuff(BuffIcon b) + { + if (m_BuffTable?.Remove(b) != true) + { + return; + } if (NetState?.BuffIcon == true) { @@ -4319,5 +4618,313 @@ namespace Server.Mobiles m_Callback?.Invoke(); } } + + private class CancelRenewInventoryInsuranceGump : Gump + { + private readonly ItemInsuranceMenuGump m_InsuranceGump; + private readonly PlayerMobile m_Player; + + public CancelRenewInventoryInsuranceGump(PlayerMobile player, ItemInsuranceMenuGump insuranceGump) : base( + 250, + 200 + ) + { + m_Player = player; + m_InsuranceGump = insuranceGump; + + AddBackground(0, 0, 240, 142, 0x13BE); + AddImageTiled(6, 6, 228, 100, 0xA40); + AddImageTiled(6, 116, 228, 20, 0xA40); + AddAlphaRegion(6, 6, 228, 142); + + AddHtmlLocalized( + 8, + 8, + 228, + 100, + 1071021, + 0x7FFF + ); // You are about to disable inventory insurance auto-renewal. + + AddButton(6, 116, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(40, 118, 450, 20, 1060051, 0x7FFF); // CANCEL + + AddButton(114, 116, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(148, 118, 450, 20, 1071022, 0x7FFF); // DISABLE IT! + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (!m_Player.CheckAlive()) + { + return; + } + + if (info.ButtonID == 1) + { + // You have cancelled automatically reinsuring all insured items upon death + m_Player.SendLocalizedMessage(1061075, "", 0x23); + m_Player.AutoRenewInsurance = false; + } + else + { + m_Player.SendLocalizedMessage(1042021); // Cancelled. + } + + if (m_InsuranceGump != null) + { + m_Player.SendGump(m_InsuranceGump.NewInstance()); + } + } + } + + private class ItemInsuranceMenuGump : Gump + { + private readonly PlayerMobile m_From; + private readonly bool[] m_Insure; + private readonly Item[] m_Items; + private readonly int m_Page; + + public ItemInsuranceMenuGump(PlayerMobile from, Item[] items, bool[] insure = null, int page = 0) + : base(25, 50) + { + m_From = from; + m_Items = items; + + if (insure == null) + { + insure = new bool[items.Length]; + + for (var i = 0; i < items.Length; ++i) + { + insure[i] = items[i].Insured; + } + } + + m_Insure = insure; + m_Page = page; + + AddPage(0); + + AddBackground(0, 0, 520, 510, 0x13BE); + AddImageTiled(10, 10, 500, 30, 0xA40); + AddImageTiled(10, 50, 500, 355, 0xA40); + AddImageTiled(10, 415, 500, 80, 0xA40); + AddAlphaRegion(10, 10, 500, 485); + + AddButton(15, 470, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(50, 472, 80, 20, 1011012, 0x7FFF); // CANCEL + + if (from.AutoRenewInsurance) + { + AddButton(360, 10, 9723, 9724, 1); + } + else + { + AddButton(360, 10, 9720, 9722, 1); + } + + AddHtmlLocalized(395, 14, 105, 20, 1114122, 0x7FFF); // AUTO REINSURE + + AddButton(395, 470, 0xFA5, 0xFA6, 2); + AddHtmlLocalized(430, 472, 50, 20, 1006044, 0x7FFF); // OK + + AddHtmlLocalized(10, 14, 150, 20, 1114121, 0x7FFF); //
ITEM INSURANCE MENU
+ + AddHtmlLocalized(45, 54, 70, 20, 1062214, 0x7FFF); // Item + AddHtmlLocalized(250, 54, 70, 20, 1061038, 0x7FFF); // Cost + AddHtmlLocalized(400, 54, 70, 20, 1114311, 0x7FFF); // Insured + + var balance = Banker.GetBalance(from); + var cost = 0; + + for (var i = 0; i < items.Length; ++i) + { + if (insure[i]) + { + cost += GetInsuranceCost(items[i]); + } + } + + AddHtmlLocalized(15, 420, 300, 20, 1114310, 0x7FFF); // GOLD AVAILABLE: + AddLabel(215, 420, 0x481, balance.ToString()); + AddHtmlLocalized(15, 435, 300, 20, 1114123, 0x7FFF); // TOTAL COST OF INSURANCE: + AddLabel(215, 435, 0x481, cost.ToString()); + + if (cost != 0) + { + AddHtmlLocalized(15, 450, 300, 20, 1114125, 0x7FFF); // NUMBER OF DEATHS PAYABLE: + AddLabel(215, 450, 0x481, (balance / cost).ToString()); + } + + for (int i = page * 4, y = 72; i < (page + 1) * 4 && i < items.Length; ++i, y += 75) + { + var item = items[i]; + var b = ItemBounds.Table[item.ItemID]; + + AddImageTiledButton( + 40, + y, + 0x918, + 0x918, + 0, + GumpButtonType.Page, + 0, + item.ItemID, + item.Hue, + 40 - b.Width / 2 - b.X, + 30 - b.Height / 2 - b.Y + ); + AddItemProperty(item.Serial); + + if (insure[i]) + { + AddButton(400, y, 9723, 9724, 100 + i); + AddLabel(250, y, 0x481, GetInsuranceCost(item).ToString()); + } + else + { + AddButton(400, y, 9720, 9722, 100 + i); + AddLabel(250, y, 0x66C, GetInsuranceCost(item).ToString()); + } + } + + if (page >= 1) + { + AddButton(15, 380, 0xFAE, 0xFAF, 3); + AddHtmlLocalized(50, 380, 450, 20, 1044044, 0x7FFF); // PREV PAGE + } + + if ((page + 1) * 4 < items.Length) + { + AddButton(400, 380, 0xFA5, 0xFA7, 4); + AddHtmlLocalized(435, 380, 70, 20, 1044045, 0x7FFF); // NEXT PAGE + } + } + + public ItemInsuranceMenuGump NewInstance() => new(m_From, m_Items, m_Insure, m_Page); + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (info.ButtonID == 0 || !m_From.CheckAlive()) + { + return; + } + + switch (info.ButtonID) + { + case 1: // Auto Reinsure + { + if (m_From.AutoRenewInsurance) + { + if (!m_From.HasGump()) + { + m_From.SendGump(new CancelRenewInventoryInsuranceGump(m_From, this)); + } + } + else + { + m_From.AutoRenewInventoryInsurance(); + m_From.SendGump(new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page)); + } + + break; + } + case 2: // OK + { + m_From.SendGump(new ItemInsuranceMenuConfirmGump(m_From, m_Items, m_Insure, m_Page)); + + break; + } + case 3: // Prev + { + if (m_Page >= 1) + { + m_From.SendGump(new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page - 1)); + } + + break; + } + case 4: // Next + { + if ((m_Page + 1) * 4 < m_Items.Length) + { + m_From.SendGump(new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page + 1)); + } + + break; + } + default: + { + var idx = info.ButtonID - 100; + + if (idx >= 0 && idx < m_Items.Length) + { + m_Insure[idx] = !m_Insure[idx]; + } + + m_From.SendGump(new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page)); + + break; + } + } + } + } + + private class ItemInsuranceMenuConfirmGump : Gump + { + private readonly PlayerMobile m_From; + private readonly bool[] m_Insure; + private readonly Item[] m_Items; + private readonly int m_Page; + + public ItemInsuranceMenuConfirmGump(PlayerMobile from, Item[] items, bool[] insure, int page) + : base(250, 200) + { + m_From = from; + m_Items = items; + m_Insure = insure; + m_Page = page; + + AddBackground(0, 0, 240, 142, 0x13BE); + AddImageTiled(6, 6, 228, 100, 0xA40); + AddImageTiled(6, 116, 228, 20, 0xA40); + AddAlphaRegion(6, 6, 228, 142); + + AddHtmlLocalized(8, 8, 228, 100, 1114300, 0x7FFF); // Do you wish to insure all newly selected items? + + AddButton(6, 116, 0xFB1, 0xFB2, 0); + AddHtmlLocalized(40, 118, 450, 20, 1060051, 0x7FFF); // CANCEL + + AddButton(114, 116, 0xFA5, 0xFA7, 1); + AddHtmlLocalized(148, 118, 450, 20, 1073996, 0x7FFF); // ACCEPT + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (!m_From.CheckAlive()) + { + return; + } + + if (info.ButtonID == 1) + { + for (var i = 0; i < m_Items.Length; ++i) + { + var item = m_Items[i]; + + if (item.Insured != m_Insure[i]) + { + m_From.ToggleItemInsurance_Callback(m_From, item, false); + } + } + } + else + { + m_From.SendLocalizedMessage(1042021); // Cancelled. + m_From.SendGump(new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page)); + } + } + } } } diff --git a/Projects/UOContent/Mobiles/Special/BaseChampion.cs b/Projects/UOContent/Mobiles/Special/BaseChampion.cs index bacc79430..57ad2b290 100644 --- a/Projects/UOContent/Mobiles/Special/BaseChampion.cs +++ b/Projects/UOContent/Mobiles/Special/BaseChampion.cs @@ -165,7 +165,7 @@ public abstract partial class BaseChampion : BaseCreature var prot = JusticeVirtue.GetProtector(pm); - if (prot == null || prot.Map != pm.Map || prot.Murderer || prot.Criminal || + if (prot == null || prot.Map != pm.Map || prot.Kills >= 5 || prot.Criminal || !JusticeVirtue.CheckMapRegion(pm, prot)) { return; diff --git a/Projects/UOContent/Mobiles/Special/Harrower.cs b/Projects/UOContent/Mobiles/Special/Harrower.cs index ee3fa1873..9b69aa3c7 100644 --- a/Projects/UOContent/Mobiles/Special/Harrower.cs +++ b/Projects/UOContent/Mobiles/Special/Harrower.cs @@ -279,7 +279,7 @@ public partial class Harrower : BaseCreature var prot = JusticeVirtue.GetProtector(pm); - if (prot == null || prot.Map != pm.Map || prot.Murderer || prot.Criminal || + if (prot == null || prot.Map != pm.Map || prot.Kills >= 5 || prot.Criminal || !JusticeVirtue.CheckMapRegion(pm, prot)) { continue; @@ -343,7 +343,7 @@ public partial class Harrower : BaseCreature for (var i = 0; i < _tentacles.Count; ++i) { - var m = _tentacles[i]; + Mobile m = _tentacles[i]; if (!m.Deleted) { diff --git a/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs b/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs index 04a8b79d7..20877aa6a 100644 --- a/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs +++ b/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs @@ -11,10 +11,10 @@ public partial class HarrowerTentacles : BaseCreature [SerializableField(0)] [SerializedCommandProperty(AccessLevel.GameMaster)] - private Harrower _harrower; + private Mobile _harrower; [Constructible] - public HarrowerTentacles(Harrower harrower = null) : base(AIType.AI_Melee) + public HarrowerTentacles(Mobile harrower = null) : base(AIType.AI_Melee) { _harrower = harrower; Body = 129; @@ -101,7 +101,6 @@ public partial class HarrowerTentacles : BaseCreature _timer = null; base.OnAfterDelete(); - Harrower?.RemoveFromTentacles(this); } private class DrainTimer : Timer diff --git a/Projects/UOContent/Mobiles/Special/LordOaks.cs b/Projects/UOContent/Mobiles/Special/LordOaks.cs index f8fb6d444..0387d6353 100644 --- a/Projects/UOContent/Mobiles/Special/LordOaks.cs +++ b/Projects/UOContent/Mobiles/Special/LordOaks.cs @@ -9,7 +9,7 @@ namespace Server.Mobiles; public partial class LordOaks : BaseChampion { [SerializableField(0)] - private Silvani _queen; + private BaseCreature _queen; [SerializableField(1)] private bool _spawnedQueen; diff --git a/Projects/UOContent/Mobiles/Special/Paragon.cs b/Projects/UOContent/Mobiles/Special/Paragon.cs index cbd0a30c7..825609e22 100644 --- a/Projects/UOContent/Mobiles/Special/Paragon.cs +++ b/Projects/UOContent/Mobiles/Special/Paragon.cs @@ -79,7 +79,6 @@ public static class Paragon bc.PassiveSpeed /= SpeedBuff; bc.ActiveSpeed /= SpeedBuff; - bc.ScaleMoveSpeed(1.0 / SpeedBuff); bc.CurrentSpeed = bc.PassiveSpeed; bc.DamageMin += DamageBuff; @@ -144,8 +143,6 @@ public static class Paragon bc.PassiveSpeed *= SpeedBuff; bc.ActiveSpeed *= SpeedBuff; - bc.ScaleMoveSpeed(SpeedBuff); - bc.SnapSpeedsToTable(); // an ulp of scaling drift must not read as hand-tuned bc.CurrentSpeed = bc.PassiveSpeed; bc.DamageMin -= DamageBuff; diff --git a/Projects/UOContent/Mobiles/Townfolk/Actor.cs b/Projects/UOContent/Mobiles/Townfolk/Actor.cs index 375ec8116..c13b8b087 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Actor.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Actor.cs @@ -35,12 +35,12 @@ public partial class Actor : BaseCreature Utility.AssignRandomHair(this); - var pack = new Backpack - { - Movable = false - }; + Container pack = new Backpack(); pack.DropItem(new Gold(250, 300)); + + pack.Movable = false; + AddItem(pack); } diff --git a/Projects/UOContent/Mobiles/Townfolk/Artist.cs b/Projects/UOContent/Mobiles/Townfolk/Artist.cs index 271026e63..9e39817e5 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Artist.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Artist.cs @@ -35,12 +35,12 @@ public partial class Artist : BaseCreature Utility.AssignRandomHair(this); - var pack = new Backpack - { - Movable = false - }; + Container pack = new Backpack(); pack.DropItem(new Gold(250, 300)); + + pack.Movable = false; + AddItem(pack); } diff --git a/Projects/UOContent/Mobiles/Townfolk/Banker.cs b/Projects/UOContent/Mobiles/Townfolk/Banker.cs index e93ddcf4f..d79cc7055 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Banker.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Banker.cs @@ -12,7 +12,7 @@ namespace Server.Mobiles; [SerializationGenerator(0, false)] public partial class Banker : BaseVendor { - private readonly List m_SBInfos = []; + private readonly List m_SBInfos = new(); [Constructible] public Banker() : base("the banker") @@ -41,7 +41,7 @@ public partial class Banker : BaseVendor } } - var bank = m.FindBankNoCreate(); + Container bank = m.FindBankNoCreate(); if (bank != null) { @@ -80,11 +80,11 @@ public partial class Banker : BaseVendor } } - var bank = m.FindBankNoCreate(); + Container bank = m.FindBankNoCreate(); if (bank != null) { - gold = []; + gold = new List(); foreach (var g in bank.FindItemsByType()) { @@ -97,7 +97,7 @@ public partial class Banker : BaseVendor return int.MaxValue; } - checks = []; + checks = new List(); foreach (var bc in bank.FindItemsByType()) { @@ -111,7 +111,7 @@ public partial class Banker : BaseVendor private static bool HasRequiredBalance(int requiredBalance, Mobile m, out PooledRefList gold, out PooledRefList checks) { - var bank = m.FindBankNoCreate(); + Container bank = m.FindBankNoCreate(); if (bank == null) { @@ -206,7 +206,7 @@ public partial class Banker : BaseVendor return true; } - public static bool Deposit(Mobile from, int amount, bool useChecks = true) + public static bool Deposit(Mobile from, int amount) { // If for whatever reason the TOL checks fail, we should still try old methods for depositing currency. if (amount <= 0 || AccountGold.Enabled && from.Account?.DepositGold(amount) == true) @@ -221,99 +221,47 @@ public partial class Banker : BaseVendor return false; } - using var items = box.ListItemsByType([typeof(Gold), typeof(BankCheck)], false); + using var items = PooledRefQueue.Create(); - // Pre-calculate available capacity without modifying anything - var remaining = amount; - - for (var i = 0; i < items.Count && remaining > 0; i++) + while (amount > 0) { - var item = items[i]; - if (item is Gold g) + Item item; + if (amount < 5000) { - remaining -= Math.Min(60000 - g.Amount, remaining); + item = new Gold(amount); + amount = 0; } - else if (useChecks && item is BankCheck c) + else if (amount <= 1000000) { - remaining -= Math.Min(1000000 - c.Worth, remaining); - } - } - - // Calculate new item slots needed for the remainder - if (remaining > 0) - { - var slotsNeeded = 0; - var r = remaining; - while (r > 0) - { - if (!useChecks || r < 5000) - { - r -= Math.Min(r, 60000); - } - else if (r <= 1000000) - { - r = 0; - } - else - { - r -= 1000000; - } - - slotsNeeded++; - } - - var maxItems = box.MaxItems; - if (maxItems != 0 && slotsNeeded > maxItems - box.TotalItems) - { - return false; - } - } - - // Capacity verified — execute deposit - remaining = amount; - - for (var i = 0; i < items.Count && remaining > 0; i++) - { - var item = items[i]; - if (item is Gold g && g.Amount < 60000) - { - var add = Math.Min(60000 - g.Amount, remaining); - g.Amount += add; - remaining -= add; - } - else if (useChecks && item is BankCheck c && c.Worth < 1000000) - { - var add = Math.Min(1000000 - c.Worth, remaining); - c.Worth += add; - remaining -= add; - } - } - - // Create new items for the remainder - while (remaining > 0) - { - if (!useChecks || remaining < 5000) - { - var pile = Math.Min(remaining, 60000); - box.DropItem(new Gold(pile)); - remaining -= pile; - } - else if (remaining <= 1000000) - { - box.DropItem(new BankCheck(remaining)); - remaining = 0; + item = new BankCheck(amount); + amount = 0; } else { - box.DropItem(new BankCheck(1000000)); - remaining -= 1000000; + item = new BankCheck(1000000); + amount -= 1000000; + } + + if (box.TryDropItem(from, item, false)) + { + items.Enqueue(item); + } + else + { + item.Delete(); + while (items.Count > 0) + { + items.Dequeue().Delete(); + } + + return false; } } return true; } - public static int DepositUpTo(Mobile from, int amount, bool useChecks = true, int reserveSlots = 0) + public static int DepositUpTo(Mobile from, int amount) { // If for whatever reason the TOL checks fail, we should still try old methods for depositing currency. if (AccountGold.Enabled && from.Account?.DepositGold(amount) == true) @@ -328,97 +276,40 @@ public partial class Banker : BaseVendor return 0; } - var remaining = amount; - - // Top off existing gold piles and checks - using var items = box.ListItemsByType([typeof(Gold), typeof(BankCheck)], false); - - for (var i = 0; i < items.Count && remaining > 0; i++) + var amountLeft = amount; + while (amountLeft > 0) { - var item = items[i]; - if (item is Gold g && g.Amount < 60000) + Item item; + int amountGiven; + + if (amountLeft < 5000) { - var add = Math.Min(60000 - g.Amount, remaining); - g.Amount += add; - remaining -= add; + item = new Gold(amountLeft); + amountGiven = amountLeft; } - else if (useChecks && item is BankCheck c && c.Worth < 1000000) + else if (amountLeft <= 1000000) { - var add = Math.Min(1000000 - c.Worth, remaining); - c.Worth += add; - remaining -= add; - } - } - - if (remaining == 0) - { - return amount; - } - - // Calculate how many new item slots can be filled - int slotsToFill; - var maxItems = box.MaxItems; - if (maxItems == 0 || from.AccessLevel >= AccessLevel.GameMaster) - { - slotsToFill = int.MaxValue; - } - else - { - slotsToFill = maxItems - box.TotalItems; - if (reserveSlots > 0) - { - // Count slots needed to determine if the full amount fits. - // Only reserve if it doesn't — the caller will delete the reserved item on full deposit. - var slots = slotsToFill; - var r = remaining; - while (r > 0) - { - if (!useChecks || r < 5000) - { - r -= Math.Min(r, 60000); - } - else if (r <= 1000000) - { - r = 0; - } - else - { - r -= 1000000; - } - - if (--slots < 0) - { - slotsToFill -= reserveSlots; - break; - } - } - } - } - - // Create new items for the remainder - while (remaining > 0 && slotsToFill > 0) - { - if (!useChecks || remaining < 5000) - { - var pile = Math.Min(remaining, 60000); - box.DropItem(new Gold(pile)); - remaining -= pile; - } - else if (remaining <= 1000000) - { - box.DropItem(new BankCheck(remaining)); - remaining = 0; + item = new BankCheck(amountLeft); + amountGiven = amountLeft; } else { - box.DropItem(new BankCheck(1000000)); - remaining -= 1000000; + item = new BankCheck(1000000); + amountGiven = 1000000; } - slotsToFill--; + if (box.TryDropItem(from, item, false)) + { + amountLeft -= amountGiven; + } + else + { + item.Delete(); + break; + } } - return amount - remaining; + return amount - amountLeft; } public static void Deposit(Container cont, int amount) @@ -491,7 +382,9 @@ public partial class Banker : BaseVendor } else if (amount > 0) { - if (!Withdraw(e.Mobile, amount)) + var box = e.Mobile.FindBankNoCreate(); + + if (box == null || !Withdraw(e.Mobile, amount)) { Say(500384); // Ah, art thou trying to fool me? Thou hast not so much gold! } diff --git a/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs b/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs index cf439a76c..04c6e9311 100644 --- a/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs +++ b/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using ModernUO.Serialization; using Server.Buffers; @@ -17,7 +18,7 @@ using EDI = Server.Mobiles.EscortDestinationInfo; namespace Server.Mobiles; -[SerializationGenerator(3, false)] +[SerializationGenerator(2, false)] public partial class BaseEscortable : BaseCreature { private static readonly ILogger logger = LogFactory.GetLogger(typeof(BaseEscortable)); @@ -158,22 +159,16 @@ public partial class BaseEscortable : BaseCreature [SerializableField(0, setter: "private")] private string _destinationString; + [TimerDrift] [SerializableField(1)] - [DeserializeTimer(nameof(DeserializeDeleteTimer))] private Timer _deleteTimer; - private void DeserializeDeleteTimer(TimeSpan delay) => Timer.DelayCall(delay, Delete); - - private void MigrateFrom(V2Content content) + [DeserializeTimerField(1)] + private void DeserializeDeleteTimer(TimeSpan delay) { - _destinationString = content.DestinationString; - _mlQuestType = content.MlQuestType; - _mlQuestDestinationMessage = content.MlQuestDestinationMessage; - _mlQuestPaymentMessage = content.MlQuestPaymentMessage; - - if (content.DeleteTimerDelay != TimeSpan.MinValue) + if (delay >= TimeSpan.Zero) { - DeserializeDeleteTimer(content.DeleteTimerDelay); + Timer.DelayCall(delay, Delete); } } @@ -854,7 +849,7 @@ public class EscortDestinationInfo public static void Initialize() { - var list = Map.Felucca.Regions.Values; + ICollection list = Map.Felucca.Regions.Values; if (list.Count == 0) { @@ -863,7 +858,7 @@ public class EscortDestinationInfo _table = new Dictionary(); - foreach (var r in list) + foreach (Region r in list) { if (r is DungeonRegion or TownRegion && r.Name != null) { diff --git a/Projects/UOContent/Mobiles/Townfolk/Gypsy.cs b/Projects/UOContent/Mobiles/Townfolk/Gypsy.cs index a645b8d83..ab08cf4a9 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Gypsy.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Gypsy.cs @@ -42,12 +42,12 @@ public partial class Gypsy : BaseCreature Utility.AssignRandomHair(this); - var pack = new Backpack - { - Movable = false - }; + Container pack = new Backpack(); pack.DropItem(new Gold(250, 300)); + + pack.Movable = false; + AddItem(pack); } diff --git a/Projects/UOContent/Mobiles/Townfolk/HarborMaster.cs b/Projects/UOContent/Mobiles/Townfolk/HarborMaster.cs index 84e1f1d3a..25c35513e 100644 --- a/Projects/UOContent/Mobiles/Townfolk/HarborMaster.cs +++ b/Projects/UOContent/Mobiles/Townfolk/HarborMaster.cs @@ -37,12 +37,12 @@ public partial class HarborMaster : BaseCreature Utility.AssignRandomHair(this); - var pack = new Backpack - { - Movable = false - }; + Container pack = new Backpack(); pack.DropItem(new Gold(250, 300)); + + pack.Movable = false; + AddItem(pack); } diff --git a/Projects/UOContent/Mobiles/Townfolk/Sculptor.cs b/Projects/UOContent/Mobiles/Townfolk/Sculptor.cs index c669822b7..00f23353c 100644 --- a/Projects/UOContent/Mobiles/Townfolk/Sculptor.cs +++ b/Projects/UOContent/Mobiles/Townfolk/Sculptor.cs @@ -34,12 +34,12 @@ public partial class Sculptor : BaseCreature Utility.AssignRandomHair(this); - var pack = new Backpack - { - Movable = false - }; + Container pack = new Backpack(); pack.DropItem(new Gold(250, 300)); + + pack.Movable = false; + AddItem(pack); } diff --git a/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs b/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs index 8343a0667..a2dd13d14 100644 --- a/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs +++ b/Projects/UOContent/Mobiles/Townfolk/TownCrier.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using System.Text; using ModernUO.Serialization; -using Server.Text; using Server.Gumps; using Server.Items; using Server.Network; @@ -88,7 +88,7 @@ public class GlobalTownCrierEntryList : ITownCrierEntryList [Usage("TownCriers"), Description("Manages the global town crier list.")] public static void TownCriers_OnCommand(CommandEventArgs e) { - TownCrierGump.DisplayTo(e.Mobile, Instance); + e.Mobile.SendGump(new TownCrierGump(e.Mobile, Instance)); } } @@ -118,8 +118,8 @@ public class TownCrierDurationPrompt : Prompt { if (!TimeSpan.TryParse(text, out var ts)) { - from.SendMessage("Value was not properly formatted. Use: "); - TownCrierGump.DisplayTo(from, m_Owner); + from.SendMessage("Value was not properly formatted. Use: "); + from.SendGump(new TownCrierGump(from, m_Owner)); return; } @@ -137,7 +137,7 @@ public class TownCrierDurationPrompt : Prompt public override void OnCancel(Mobile from) { from.SendLocalizedMessage(502980); // Message entry cancelled. - TownCrierGump.DisplayTo(from, m_Owner); + from.SendGump(new TownCrierGump(from, m_Owner)); } } @@ -188,104 +188,96 @@ public class TownCrierLinesPrompt : Prompt } } - TownCrierGump.DisplayTo(from, m_Owner); + from.SendGump(new TownCrierGump(from, m_Owner)); } } -public class TownCrierGump : DynamicGump +public class TownCrierGump : Gump { - private readonly ITownCrierEntryList _owner; + private readonly Mobile m_From; + private readonly ITownCrierEntryList m_Owner; public override bool Singleton => true; - private TownCrierGump(ITownCrierEntryList owner) : base(50, 50) => _owner = owner; - - public static void DisplayTo(Mobile from, ITownCrierEntryList owner) + public TownCrierGump(Mobile from, ITownCrierEntryList owner) : base(50, 50) { - if (from?.NetState == null || owner == null) - { - return; - } + m_From = from; + m_Owner = owner; - from.SendGump(new TownCrierGump(owner)); - } + AddPage(0); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + var entries = owner.Entries; - _owner.GetRandomEntry(); // force expiration checks + owner.GetRandomEntry(); // force expiration checks - var entries = _owner.Entries; var count = entries?.Count ?? 0; - builder.AddImageTiled(0, 0, 300, 38 + (count == 0 ? 20 : count * 85), 0xA40); - builder.AddAlphaRegion(1, 1, 298, 36 + (count == 0 ? 20 : count * 85)); + AddImageTiled(0, 0, 300, 38 + (count == 0 ? 20 : count * 85), 0xA40); + AddAlphaRegion(1, 1, 298, 36 + (count == 0 ? 20 : count * 85)); - builder.AddHtml(8, 8, 300 - 8 - 30, 20, "
TOWN CRIER MESSAGES
"); + AddHtml(8, 8, 300 - 8 - 30, 20, "
TOWN CRIER MESSAGES
"); - builder.AddButton(300 - 8 - 30, 8, 0xFAB, 0xFAD, 1); + AddButton(300 - 8 - 30, 8, 0xFAB, 0xFAD, 1); if (count == 0) { - builder.AddHtml(8, 30, 284, 20, "The crier has no news."); - return; + AddHtml(8, 30, 284, 20, "The crier has no news."); } - - for (var i = 0; i < entries!.Count; ++i) + else { - var tce = entries[i]; - - var toExpire = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero); - - using var sb = ValueStringBuilder.Create(512); - - sb.Append("[Expires: "); - - if (toExpire.TotalHours >= 1) + for (var i = 0; i < entries!.Count; ++i) { - sb.Append((int)toExpire.TotalHours); - sb.Append(':'); - sb.Append(toExpire.Minutes.ToString("D2")); - } - else - { - sb.Append(toExpire.Minutes); - } + var tce = entries[i]; - sb.Append(':'); - sb.Append(toExpire.Seconds.ToString("D2")); + var toExpire = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero); - sb.Append("] "); + var sb = new StringBuilder(); - for (var j = 0; j < tce.Lines.Length; ++j) - { - if (j > 0) + sb.Append("[Expires: "); + + if (toExpire.TotalHours >= 1) { - sb.Append("
"); + sb.Append((int)toExpire.TotalHours); + sb.Append(':'); + sb.Append(toExpire.Minutes.ToString("D2")); + } + else + { + sb.Append(toExpire.Minutes); } - sb.Append(tce.Lines[j]); + sb.Append(':'); + sb.Append(toExpire.Seconds.ToString("D2")); + + sb.Append("] "); + + for (var j = 0; j < tce.Lines.Length; ++j) + { + if (j > 0) + { + sb.Append("
"); + } + + sb.Append(tce.Lines[j]); + } + + AddHtml(8, 35 + i * 85, 254, 80, sb.ToString(), true, true); + + AddButton(300 - 8 - 26, 35 + i * 85, 0x15E1, 0x15E5, 2 + i); } - - builder.AddHtml(8, 35 + i * 85, 254, 80, sb.ToString(), background: true, scrollbar: true); - - builder.AddButton(300 - 8 - 26, 35 + i * 85, 0x15E1, 0x15E5, 2 + i); } } public override void OnResponse(NetState sender, in RelayInfo info) { - var from = sender.Mobile; - if (info.ButtonID == 1) { - from.SendMessage("Enter the duration for the new message. Format: "); - from.Prompt = new TownCrierDurationPrompt(_owner); + m_From.SendMessage("Enter the duration for the new message. Format: "); + m_From.Prompt = new TownCrierDurationPrompt(m_Owner); } else if (info.ButtonID > 1) { - var entries = _owner.Entries; + var entries = m_Owner.Entries; var index = info.ButtonID - 2; if (index < entries?.Count) @@ -293,9 +285,9 @@ public class TownCrierGump : DynamicGump var tce = entries[index]; var ts = Utility.Max(tce.ExpireTime - Core.Now, TimeSpan.Zero); - from.SendMessage($"Editing entry #{index + 1}."); - from.SendMessage("Enter the first line to shout:"); - from.Prompt = new TownCrierLinesPrompt(_owner, tce, new List(), ts); + m_From.SendMessage($"Editing entry #{index + 1}."); + m_From.SendMessage("Enter the first line to shout:"); + m_From.Prompt = new TownCrierLinesPrompt(m_Owner, tce, new List(), ts); } } } @@ -360,6 +352,11 @@ public partial class TownCrier : Mobile, ITownCrierEntryList Utility.AssignRandomHair(this); } + public TownCrier(Serial serial) : base(serial) + { + Instances.Add(this); + } + public static List Instances { get; } = new(); public List Entries { get; private set; } @@ -436,7 +433,7 @@ public partial class TownCrier : Mobile, ITownCrierEntryList if (tce == null) { - _autoShoutTimer?.Stop(); + _autoShoutTimer.Stop(); _autoShoutTimer = null; } else if (_newsTimer == null) @@ -457,7 +454,7 @@ public partial class TownCrier : Mobile, ITownCrierEntryList var index = _newsTimer.Index; if (index >= tce.Lines.Length) { - _newsTimer?.Stop(); + _newsTimer.Stop(); _newsTimer = null; } else @@ -470,7 +467,7 @@ public partial class TownCrier : Mobile, ITownCrierEntryList { if (from.AccessLevel >= AccessLevel.GameMaster) { - TownCrierGump.DisplayTo(from, this); + from.SendGump(new TownCrierGump(from, this)); } else { @@ -513,10 +510,4 @@ public partial class TownCrier : Mobile, ITownCrierEntryList Instances.Remove(this); base.OnDelete(); } - - [AfterDeserialization] - private void AfterDeserialization() - { - Instances.Add(this); - } } diff --git a/Projects/UOContent/Mobiles/Vendors/Barkeeper/BarkeeperGump.cs b/Projects/UOContent/Mobiles/Vendors/Barkeeper/BarkeeperGump.cs deleted file mode 100644 index ddac8e89e..000000000 --- a/Projects/UOContent/Mobiles/Vendors/Barkeeper/BarkeeperGump.cs +++ /dev/null @@ -1,351 +0,0 @@ -using System; -using Server.Mobiles; -using Server.Network; - -namespace Server.Gumps; - -public static class BarkeeperGump -{ - public static void DisplayTo(Mobile from, PlayerBarkeeper barkeeper) - { - from.CloseGump(); - from.CloseGump(); - from.CloseGump(); - - if ((int)barkeeper.Body is not 0x340 and not 0x402) - { - from.SendGump(new BarkeeperHumanGump(barkeeper)); - } - else - { - from.SendGump(new BarkeeperNonHumanGump(barkeeper)); - } - } -} - -file class BarkeeperHumanGump(PlayerBarkeeper barkeeper) : BarkeeperGump(barkeeper) -{ - protected override bool ModifyAppearance => true; -} - -file class BarkeeperNonHumanGump(PlayerBarkeeper barkeeper) : BarkeeperGump(barkeeper) -{ - protected override bool ModifyAppearance => false; -} - -abstract file class BarkeeperGump : StaticGump where T : BarkeeperGump -{ - private readonly PlayerBarkeeper _barkeeper; - - public override bool Singleton => true; - - protected abstract bool ModifyAppearance { get; } - - protected BarkeeperGump(PlayerBarkeeper barkeeper) : base(0, 0) => _barkeeper = barkeeper; - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - RenderBackground(ref builder); - RenderCategories(ref builder); - RenderMessageManagement(ref builder); - RenderDismissConfirmation(ref builder); - RenderMessageManagement_Message_AddOrChange(ref builder); - RenderMessageManagement_Message_Remove(ref builder); - RenderMessageManagement_Tip_AddOrChange(ref builder); - RenderMessageManagement_Tip_Remove(ref builder); - RenderAppearanceCategories(ref builder); - } - - private static void RenderBackground(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(30, 40, 585, 410, 5054); - - builder.AddImage(30, 40, 9251); - builder.AddImage(180, 40, 9251); - builder.AddImage(30, 40, 9253); - builder.AddImage(30, 130, 9253); - builder.AddImage(598, 40, 9255); - builder.AddImage(598, 130, 9255); - builder.AddImage(30, 433, 9257); - builder.AddImage(180, 433, 9257); - builder.AddImage(30, 40, 9250); - builder.AddImage(598, 40, 9252); - builder.AddImage(598, 433, 9258); - builder.AddImage(30, 433, 9256); - - builder.AddItem(30, 40, 6816); - builder.AddItem(30, 125, 6817); - builder.AddItem(30, 233, 6817); - builder.AddItem(30, 341, 6817); - builder.AddItem(580, 40, 6814); - builder.AddItem(588, 125, 6815); - builder.AddItem(588, 233, 6815); - builder.AddItem(588, 341, 6815); - - builder.AddBackground(183, 25, 280, 30, 5054); - - builder.AddImage(180, 25, 10460); - builder.AddImage(434, 25, 10460); - builder.AddImage(560, 20, 1417); - - builder.AddHtmlLocalized(223, 32, 200, 40, 1078366); // BARKEEP CUSTOMIZATION MENU - builder.AddBackground(243, 433, 150, 30, 5054); - - builder.AddImage(240, 433, 10460); - builder.AddImage(375, 433, 10460); - } - - private static void RenderCategories(ref StaticGumpBuilder builder) - { - builder.AddPage(1); - - builder.AddButton(130, 120, 4005, 4007, 0, GumpButtonType.Page, 2); - builder.AddHtmlLocalized(170, 120, 200, 40, 1078352); // Message Control - - builder.AddButton(130, 200, 4005, 4007, 0, GumpButtonType.Page, 8); - builder.AddHtmlLocalized(170, 200, 200, 40, 1078353); // Customize your barkeep - - builder.AddButton(130, 280, 4005, 4007, 0, GumpButtonType.Page, 3); - builder.AddHtmlLocalized(170, 280, 200, 40, 1078354); // Dismiss your barkeep - - builder.AddButton(338, 437, 4014, 4016, 0); - builder.AddHtmlLocalized(290, 440, 35, 40, 1005007); // Back - - builder.AddItem(574, 43, 5360); - } - - private static void RenderMessageManagement(ref StaticGumpBuilder builder) - { - builder.AddPage(2); - - builder.AddButton(130, 120, 4005, 4007, 0, GumpButtonType.Page, 4); - builder.AddHtmlLocalized(170, 120, 380, 20, 1078355); // Add or change a message and keyword - - builder.AddButton(130, 200, 4005, 4007, 0, GumpButtonType.Page, 5); - builder.AddHtmlLocalized(170, 200, 380, 20, 1078356); // Remove a message and keyword from your barkeep - - builder.AddButton(130, 280, 4005, 4007, 0, GumpButtonType.Page, 6); - builder.AddHtmlLocalized(170, 280, 380, 20, 1078357); // Add or change your barkeeper's tip message - - builder.AddButton(130, 360, 4005, 4007, 0, GumpButtonType.Page, 7); - builder.AddHtmlLocalized(170, 360, 380, 20, 1078358); // Delete your barkeepers tip message - - builder.AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 1); - builder.AddHtmlLocalized(290, 440, 35, 40, 1005007); // Back - - builder.AddItem(580, 46, 4030); - } - - private static void RenderDismissConfirmation(ref StaticGumpBuilder builder) - { - builder.AddPage(3); - - builder.AddHtmlLocalized(170, 160, 380, 20, 1078359); // Are you sure you want to dismiss your barkeeper? - - builder.AddButton(205, 280, 4005, 4007, GetButtonID(0, 0)); - builder.AddHtmlLocalized(240, 280, 100, 20, 1046362); // Yes - - builder.AddButton(395, 280, 4005, 4007, 0); - builder.AddHtmlLocalized(430, 280, 100, 20, 1046363); // No - - builder.AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 1); - builder.AddHtmlLocalized(290, 440, 35, 40, 1005007); // Back - - builder.AddItem(574, 43, 5360); - builder.AddItem(584, 34, 6579); - } - - private static void RenderMessageManagement_Message_AddOrChange(ref StaticGumpBuilder builder) - { - builder.AddPage(4); - - builder.AddHtmlLocalized(250, 60, 500, 25, 1078360); // Add or change a message - - for (var i = 0; i < PlayerBarkeeper.RumorCount; ++i) - { - builder.AddHtml(100, 70 + i * 120, 50, 20, "Message"); - builder.AddHtmlPlaceholder(100, 90 + i * 120, 450, 40, $"r{i}msg", background: true); - builder.AddHtmlLocalized(100, 130 + i * 120, 50, 20, 1078361); // Keyword - builder.AddHtmlPlaceholder(100, 150 + i * 120, 450, 40, $"r{i}key", background: true); - - builder.AddButton(60, 90 + i * 120, 4005, 4007, GetButtonID(1, i)); - } - - builder.AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 2); - builder.AddHtmlLocalized(290, 440, 35, 40, 1005007); // Back - - builder.AddItem(580, 46, 4030); - } - - private static void RenderMessageManagement_Message_Remove(ref StaticGumpBuilder builder) - { - builder.AddPage(5); - - builder.AddHtmlLocalized(190, 60, 500, 25, 1078362); // Choose the message you would like to remove - - for (var i = 0; i < PlayerBarkeeper.RumorCount; ++i) - { - builder.AddHtml(100, 70 + i * 120, 50, 20, "Message"); - builder.AddHtmlPlaceholder(100, 90 + i * 120, 450, 40, $"r{i}msg", background: true); - builder.AddHtmlLocalized(100, 130 + i * 120, 50, 20, 1078361); // Keyword - builder.AddHtmlPlaceholder(100, 150 + i * 120, 450, 40, $"r{i}key", background: true); - - builder.AddButton(60, 90 + i * 120, 4005, 4007, GetButtonID(2, i)); - } - - builder.AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 2); - builder.AddHtmlLocalized(290, 440, 35, 40, 1005007); // Back - - builder.AddItem(580, 46, 4030); - } - - private static int GetButtonID(int type, int index) => 1 + index * 6 + type; - - private static void RenderMessageManagement_Tip_AddOrChange(ref StaticGumpBuilder builder) - { - builder.AddPage(6); - - builder.AddHtmlLocalized(250, 95, 500, 20, 1078363); // Change this tip message - builder.AddHtml(100, 190, 50, 20, "Message"); - builder.AddHtmlPlaceholder(100, 210, 450, 40, "tipMsg", background: true); - - builder.AddButton(60, 210, 4005, 4007, GetButtonID(3, 0)); - - builder.AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 2); - builder.AddHtmlLocalized(290, 440, 35, 40, 1005007); // Back - - builder.AddItem(580, 46, 4030); - } - - private static void RenderMessageManagement_Tip_Remove(ref StaticGumpBuilder builder) - { - builder.AddPage(7); - - builder.AddHtmlLocalized(250, 95, 500, 20, 1078364); // Remove this tip message - builder.AddHtml(100, 190, 50, 20, "Message"); - builder.AddHtmlPlaceholder(100, 210, 450, 40, "tipMsg", background: true); - - builder.AddButton(60, 210, 4005, 4007, GetButtonID(4, 0)); - - builder.AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 2); - builder.AddHtmlLocalized(290, 440, 35, 40, 1005007); // Back - - builder.AddItem(580, 46, 4030); - } - - private void RenderAppearanceCategories(ref StaticGumpBuilder builder) - { - builder.AddPage(8); - - builder.AddButton(130, 120, 4005, 4007, GetButtonID(5, 0)); - builder.AddHtml(170, 120, 120, 20, "Title"); - - if (ModifyAppearance) - { - builder.AddButton(130, 200, 4005, 4007, GetButtonID(5, 1)); - builder.AddHtmlLocalized(170, 200, 120, 20, 1077829); // Appearance - - builder.AddButton(130, 280, 4005, 4007, GetButtonID(5, 2)); - builder.AddHtmlLocalized(170, 280, 120, 20, 1078365); // Male / Female - } - - builder.AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 1); - builder.AddHtmlLocalized(290, 440, 35, 40, 1005007); // Back - - builder.AddItem(580, 44, 4033); - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - var rumors = _barkeeper.Rumors ?? PlayerBarkeeper.EmptyRumors; - - for (var i = 0; i < rumors.Length; ++i) - { - var rumor = rumors[i]; - builder.SetStringSlot($"r{i}msg", rumor?.Message ?? "No current message"); - builder.SetStringSlot($"r{i}key", rumor?.Keyword ?? "None"); - } - - builder.SetStringSlot("tipMsg", _barkeeper.TipMessage ?? "No current tip message"); - } - - public override void OnResponse(NetState state, in RelayInfo info) - { - var from = state.Mobile; - if (!_barkeeper.IsOwner(from)) - { - return; - } - - var index = info.ButtonID - 1; - - if (index < 0) - { - return; - } - - index = Math.DivRem(index, 6, out var type); - - switch (type) - { - case 0: // Controls - { - switch (index) - { - case 0: // Dismiss - { - _barkeeper.Dismiss(); - break; - } - } - - break; - } - case 1: // Change message - { - _barkeeper.BeginChangeRumor(from, index); - break; - } - case 2: // Remove message - { - _barkeeper.RemoveRumor(index); - break; - } - case 3: // Change tip - { - _barkeeper.BeginChangeTip(from); - break; - } - case 4: // Remove tip - { - _barkeeper.RemoveTip(); - break; - } - case 5: // Appearance category selection - { - switch (index) - { - case 0: - { - _barkeeper.BeginChangeTitle(from); - break; - } - case 1: - { - _barkeeper.BeginChangeAppearance(from); - break; - } - case 2: - { - _barkeeper.ChangeGender(); - break; - } - } - - break; - } - } - } -} diff --git a/Projects/UOContent/Mobiles/Vendors/Barkeeper/BarkeeperTitleGump.cs b/Projects/UOContent/Mobiles/Vendors/Barkeeper/BarkeeperTitleGump.cs deleted file mode 100644 index f1c0bdca8..000000000 --- a/Projects/UOContent/Mobiles/Vendors/Barkeeper/BarkeeperTitleGump.cs +++ /dev/null @@ -1,233 +0,0 @@ -using System; -using Server.Mobiles; -using Server.Network; - -namespace Server.Gumps; - -public class BarkeeperTitleGump : StaticGump -{ - private static readonly Entry[] _entries = - [ - new(1076083, "the alchemist"), - new(1077244, "the animal tamer"), - new(1078440, "the apothecary"), - new(1078441, "the artist"), - new(1078442, "the baker", true), - new(1073298, "the bard"), - new(1076102, "the barkeep", true), - new(1078443, "the beggar"), - new(1073297, "the blacksmith"), - new(1078444, "the bounty hunter"), - new(1078446, "the brigand"), - new(1078447, "the butler"), - new(1060774, "the carpenter"), - new(1078448, "the chef", true), - new(1078449, "the commander"), - new(1078450, "the curator"), - new(1078451, "the drunkard"), - new(1078452, "the farmer"), - new(1078453, "the fisherman"), - new(1078454, "the gambler"), - new(1078455, "the gypsy"), - new(1075996, "the herald"), - new(1076107, "the herbalist"), - new(1078465, "the hermit"), - new(1078466, "the innkeeper", true), - new(1078467, "the jailor"), - new(1078468, "the jester"), - new(1078469, "the librarian"), - new(1073292, "the mage"), - new(1078470, "the mercenary"), - new(1060775, "the merchant"), - new(1078472, "the messenger"), - new(1076093, "the miner"), - new(1078475, "the monk"), - new(1078476, "the noble"), - new(1073290, "the paladin"), - new(1078479, "the peasant"), - new(1078480, "the pirate"), - new(1078481, "the prisoner"), - new(1078482, "the prophet"), - new(1078484, "the ranger"), - new(1078487, "the sage"), - new(1078488, "the sailor"), - new(1078489, "the scholar"), - new(1060773, "the scribe"), - new(1078490, "the sentry"), - new(1060795, "the servant"), - new(1078491, "the shepherd"), - new(1078492, "the soothsayer"), - new(1078493, "the stoic"), - new(1078494, "the storyteller"), - new(1076134, "the tailor"), - new(1076096, "the thief"), - new(1076137, "the tinker"), - new(1076097, "the town crier"), - new(1073291, "the treasure hunter"), - new(1076112, "the waiter", true), - new(1077242, "the warrior"), - new(1078496, "the watchman"), - new(1078495, null) // No Title - ]; - - private static readonly int _pageCount = (_entries.Length + 19) / 20; - - private readonly PlayerBarkeeper _barkeeper; - - public override bool Singleton => true; - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - RenderBackground(ref builder); - - for (var i = 0; i < _pageCount; ++i) - { - RenderPage(ref builder, i); - } - } - - public BarkeeperTitleGump(PlayerBarkeeper barkeeper) : base(0, 0) => _barkeeper = barkeeper; - - private static void RenderBackground(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(30, 40, 585, 410, 5054); - - builder.AddImage(30, 40, 9251); - builder.AddImage(180, 40, 9251); - builder.AddImage(30, 40, 9253); - builder.AddImage(30, 130, 9253); - builder.AddImage(598, 40, 9255); - builder.AddImage(598, 130, 9255); - builder.AddImage(30, 433, 9257); - builder.AddImage(180, 433, 9257); - builder.AddImage(30, 40, 9250); - builder.AddImage(598, 40, 9252); - builder.AddImage(598, 433, 9258); - builder.AddImage(30, 433, 9256); - - builder.AddItem(30, 40, 6816); - builder.AddItem(30, 125, 6817); - builder.AddItem(30, 233, 6817); - builder.AddItem(30, 341, 6817); - builder.AddItem(580, 40, 6814); - builder.AddItem(588, 125, 6815); - builder.AddItem(588, 233, 6815); - builder.AddItem(588, 341, 6815); - - builder.AddImage(560, 20, 1417); - builder.AddItem(580, 44, 4033); - - builder.AddBackground(183, 25, 280, 30, 5054); - - builder.AddImage(180, 25, 10460); - builder.AddImage(434, 25, 10460); - - builder.AddHtmlLocalized(223, 32, 200, 40, 1078366); // BARKEEP CUSTOMIZATION MENU - builder.AddBackground(243, 433, 150, 30, 5054); - - builder.AddImage(240, 433, 10460); - builder.AddImage(375, 433, 10460); - - builder.AddImage(80, 398, 2151); - builder.AddItem(72, 406, 2543); - - builder.AddHtmlLocalized(110, 412, 180, 25, 1078445); // sells food and drink - } - - private static void RenderPage(ref StaticGumpBuilder builder, int page) - { - var currentPage = page + 1; - builder.AddPage(currentPage); - - if (_pageCount == 3 && currentPage is >= 1 and <= 3) - { - var pageCliloc = currentPage switch - { - 1 => 1078439, // Page 1 of 3 - 2 => 1078464, // Page 2 of 3 - 3 => 1078483, // Page 3 of 3 - }; - - builder.AddHtmlLocalized(430, 70, 180, 25, pageCliloc); - } - else - { - // Page ~1_CUR~ of ~2_MAX~ - builder.AddHtmlLocalized(430, 70, 180, 25, 1153561, $"{currentPage}\t{_pageCount}", 0x7FFF); - } - - for (int count = 0, i = page * 20; count < 20 && i < _entries.Length; ++count, ++i) - { - var entry = _entries[i]; - - var xOffset = Math.DivRem(count, 10, out var yOffset); - - builder.AddButton(80 + xOffset * 260, 100 + yOffset * 30, 4005, 4007, 2 + i); - builder.AddHtmlLocalized( - 120 + xOffset * 260, - 100 + yOffset * 30, - entry.Vendor ? 148 : 180, - 25, - entry.Description, - true - ); - - if (entry.Vendor) - { - builder.AddImage(270 + xOffset * 260, 98 + yOffset * 30, 2151); - builder.AddItem(262 + xOffset * 260, 106 + yOffset * 30, 2543); - } - } - - builder.AddButton(340, 400, 4005, 4007, 0, GumpButtonType.Page, currentPage + 1 % _pageCount); - builder.AddHtmlLocalized(380, 400, 180, 25, 1078456); // More Job Titles - - builder.AddButton(338, 437, 4014, 4016, 1); - builder.AddHtmlLocalized(290, 440, 35, 40, 1005007); // Back - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (_barkeeper.Deleted) - { - return; - } - - var buttonID = info.ButtonID; - - if (buttonID-- <= 0) - { - return; - } - - var from = sender.Mobile; - - if (buttonID-- <= 0) - { - _barkeeper.CancelChangeTitle(from); - return; - } - - if (buttonID < _entries.Length) - { - var entry = _entries[buttonID]; - _barkeeper.EndChangeTitle(entry.Title); - } - } - - private class Entry - { - public readonly int Description; - public readonly string Title; - public readonly bool Vendor; - - public Entry(int desc, string title, bool vendor = false) - { - Description = desc; - Title = title; - Vendor = vendor; - } - } -} diff --git a/Projects/UOContent/Mobiles/Vendors/Barkeeper/PlayerBarkeeper.cs b/Projects/UOContent/Mobiles/Vendors/Barkeeper/PlayerBarkeeper.cs deleted file mode 100644 index bbbc3ff6f..000000000 --- a/Projects/UOContent/Mobiles/Vendors/Barkeeper/PlayerBarkeeper.cs +++ /dev/null @@ -1,527 +0,0 @@ -using System; -using System.Collections.Generic; -using ModernUO.Serialization; -using Server.Collections; -using Server.ContextMenus; -using Server.Gumps; -using Server.Items; -using Server.Multis; -using Server.Prompts; - -namespace Server.Mobiles; - -public class ChangeRumorMessagePrompt : Prompt -{ - private readonly PlayerBarkeeper _barkeeper; - private readonly int _rumorIndex; - - public ChangeRumorMessagePrompt(PlayerBarkeeper barkeeper, int rumorIndex) - { - _barkeeper = barkeeper; - _rumorIndex = rumorIndex; - } - - public override void OnCancel(Mobile from) - { - OnResponse(from, ""); - } - - public override void OnResponse(Mobile from, string text) - { - if (text.Length > 130) - { - text = text[..130]; - } - - _barkeeper.EndChangeRumor(from, _rumorIndex, text); - } -} - -public class ChangeRumorKeywordPrompt : Prompt -{ - private readonly PlayerBarkeeper _barkeeper; - private readonly int _rumorIndex; - - public ChangeRumorKeywordPrompt(PlayerBarkeeper barkeeper, int rumorIndex) - { - _barkeeper = barkeeper; - _rumorIndex = rumorIndex; - } - - public override void OnCancel(Mobile from) - { - OnResponse(from, ""); - } - - public override void OnResponse(Mobile from, string text) - { - if (text.Length > 130) - { - text = text[..130]; - } - - _barkeeper.EndChangeKeyword(from, _rumorIndex, text); - } -} - -public class ChangeTipMessagePrompt : Prompt -{ - private readonly PlayerBarkeeper _barkeeper; - - public ChangeTipMessagePrompt(PlayerBarkeeper barkeeper) => _barkeeper = barkeeper; - - public override void OnCancel(Mobile from) - { - OnResponse(from, ""); - } - - public override void OnResponse(Mobile from, string text) - { - if (text.Length > 130) - { - text = text[..130]; - } - - _barkeeper.EndChangeTip(from, text); - } -} - -[SerializationGenerator(0)] -public partial class BarkeeperRumor -{ - [DirtyTrackingEntity] - private PlayerBarkeeper _barkeeper; - - [SerializableField(0)] - private string _message; - - [SerializableField(1)] - private string _keyword; - - public BarkeeperRumor(PlayerBarkeeper barkeeper, string message, string keyword) - { - _message = message; - _keyword = keyword; - _barkeeper = barkeeper; - } - - public BarkeeperRumor(PlayerBarkeeper barkeeper) => _barkeeper = barkeeper; -} - -public class ManageBarkeeperEntry : ContextMenuEntry -{ - public ManageBarkeeperEntry() : base(6151, 12) - { - } - - public override void OnClick(Mobile from, IEntity target) - { - (target as PlayerBarkeeper)?.BeginManagement(from); - } -} - -[SerializationGenerator(2, false)] -public partial class PlayerBarkeeper : BaseVendor -{ - public static readonly BarkeeperRumor[] EmptyRumors = new BarkeeperRumor[RumorCount]; - public const int RumorCount = 3; - private readonly List _sbInfos = []; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Mobile _owner; - - [SerializableField(2)] - private BarkeeperRumor[] _rumors; - - [SerializableField(3)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private string _tipMessage; - - private Timer _newsTimer; - - public PlayerBarkeeper(Mobile owner, BaseHouse house) : base("the barkeeper") - { - Owner = owner; - House = house; - - LoadSBInfo(); - } - - [SerializableField(0, fieldChanged: nameof(OnHouseChanged))] - private BaseHouse _house; - - private void OnHouseChanged(BaseHouse oldValue, BaseHouse newValue) - { - oldValue?.PlayerBarkeepers.Remove(this); - newValue?.PlayerBarkeepers.Add(this); - } - - public override bool IsActiveBuyer => false; - public override bool IsActiveSeller => _sbInfos.Count > 0; - - public override bool DisallowAllMoves => true; - public override bool NoHouseRestrictions => true; - - public override VendorShoeType ShoeType => Utility.RandomBool() ? VendorShoeType.ThighBoots : VendorShoeType.Boots; - protected override List SBInfos => _sbInfos; - - public override bool GetGender() => false; - - public override void InitOutfit() - { - base.InitOutfit(); - - AddItem(new HalfApron(Utility.RandomBrightHue())); - - var pack = Backpack; - - pack?.Delete(); - } - - public override void InitBody() - { - base.InitBody(); - - Hue = Body == 0x340 || Body == 0x402 ? 0 : 0x83F4; // hue is not random - Backpack?.Delete(); - } - - public override bool HandlesOnSpeech(Mobile from) => InRange(from, 3) || base.HandlesOnSpeech(from); - - private void ShoutNews_Callback(TownCrierEntry tce) - { - var index = _newsTimer.Index; - if (index >= tce.Lines.Length) - { - _newsTimer.Stop(); - _newsTimer = null; - } - else - { - PublicOverheadMessage(MessageType.Regular, 0x3B2, false, tce.Lines[index]); - } - } - - public override void OnAfterDelete() - { - base.OnAfterDelete(); - - House = null; - } - - public override bool OnBeforeDeath() - { - if (!base.OnBeforeDeath()) - { - return false; - } - - var shoes = FindItemOnLayer(Layer.Shoes); - - if (shoes is Sandals) - { - shoes.Hue = 0; - } - - return true; - } - - public override void OnSpeech(SpeechEventArgs e) - { - base.OnSpeech(e); - - if (e.Handled || !InRange(e.Mobile, 3)) - { - return; - } - - if (_newsTimer == null && e.HasKeyword(0x30)) // *news* - { - var tce = GlobalTownCrierEntryList.Instance.GetRandomEntry(); - - if (tce == null) - { - PublicOverheadMessage(MessageType.Regular, 0x3B2, 1005643); // I have no news at this time. - } - else - { - _newsTimer = Timer.DelayCall( - TimeSpan.FromSeconds(1.0), - TimeSpan.FromSeconds(3.0), - tce.Lines.Length, - () => ShoutNews_Callback(tce) - ); - - PublicOverheadMessage(MessageType.Regular, 0x3B2, 502978); // Some of the latest news! - } - } - - if (_rumors == null) - { - return; - } - - for (var i = 0; i < _rumors.Length; ++i) - { - var rumor = _rumors[i]; - - var keyword = rumor?.Keyword; - - if (keyword == null || (keyword = keyword.Trim()).Length == 0) - { - continue; - } - - if (keyword.InsensitiveEquals(e.Speech)) - { - var message = rumor.Message; - - if (message == null || (message = message.Trim()).Length == 0) - { - continue; - } - - PublicOverheadMessage(MessageType.Regular, 0x3B2, false, message); - } - } - } - - public override bool CheckGold(Mobile from, Item dropped) - { - if (dropped is not Gold g) - { - return false; - } - - if (g.Amount > 50) - { - PrivateOverheadMessage( - MessageType.Regular, - 0x3B2, - false, - "I cannot accept so large a tip!", - from.NetState - ); - } - else - { - var tip = TipMessage; - - if (tip == null || (tip = tip.Trim()).Length == 0) - { - PrivateOverheadMessage( - MessageType.Regular, - 0x3B2, - false, - "It would not be fair of me to take your money and not offer you information in return.", - from.NetState - ); - } - else - { - Direction = GetDirectionTo(from); - PrivateOverheadMessage(MessageType.Regular, 0x3B2, false, tip, from.NetState); - - g.Delete(); - return true; - } - } - - return false; - } - - public bool IsOwner(Mobile from) => - from?.Deleted == false && !Deleted && (from.AccessLevel > AccessLevel.GameMaster || Owner == from); - - public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) - { - base.GetContextMenuEntries(from, ref list); - - if (IsOwner(from) && from.InLOS(this)) - { - list.Add(new ManageBarkeeperEntry()); - } - } - - public void BeginManagement(Mobile from) - { - if (!IsOwner(from)) - { - return; - } - - BarkeeperGump.DisplayTo(from, this); - } - - public void Dismiss() - { - Delete(); - } - - public void BeginChangeRumor(Mobile from, int index) - { - if (index is < 0 or >= RumorCount) - { - return; - } - - from.Prompt = new ChangeRumorMessagePrompt(this, index); - PrivateOverheadMessage( - MessageType.Regular, - 0x3B2, - false, - "Say what news you would like me to tell our guests.", - from.NetState - ); - } - - public void EndChangeRumor(Mobile from, int index, string text) - { - if (index is < 0 or >= RumorCount) - { - return; - } - - Rumors ??= new BarkeeperRumor[RumorCount]; - - if (Rumors[index] == null) - { - Rumors[index] = new BarkeeperRumor(this, text, null); - } - else - { - Rumors[index].Message = text; - } - - this.MarkDirty(); - - from.Prompt = new ChangeRumorKeywordPrompt(this, index); - - // What keyword should a guest say to me to get this news? - PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1079267, from.NetState); - } - - public void EndChangeKeyword(Mobile from, int index, string text) - { - if (index is < 0 or >= RumorCount) - { - return; - } - - Rumors ??= new BarkeeperRumor[RumorCount]; - - if (Rumors[index] == null) - { - Rumors[index] = new BarkeeperRumor(this, null, text); - } - else - { - Rumors[index].Keyword = text; - } - - this.MarkDirty(); - - PrivateOverheadMessage(MessageType.Regular, 0x3B2, false, "I'll pass on the message.", from.NetState); - } - - public void RemoveRumor(int index) - { - if (index is >= 0 and < RumorCount && _rumors != null) - { - _rumors[index] = null; - this.MarkDirty(); - } - } - - public void BeginChangeTip(Mobile from) - { - from.Prompt = new ChangeTipMessagePrompt(this); - - // Say what you want me to tell guests when they give me a good tip. - PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1079268, from.NetState); - } - - public void EndChangeTip(Mobile from, string text) - { - TipMessage = text; - PrivateOverheadMessage( - MessageType.Regular, - 0x3B2, - false, - "I'll say that to anyone who gives me a good tip.", - from.NetState - ); - } - - public void RemoveTip() => TipMessage = null; - - public void BeginChangeTitle(Mobile from) => from.SendGump(new BarkeeperTitleGump(this)); - - public void EndChangeTitle(string title) - { - Title = title; - LoadSBInfo(); - } - - public void CancelChangeTitle(Mobile from) => BarkeeperGump.DisplayTo(from, this); - - public void BeginChangeAppearance(Mobile from) - { - PlayerVendorCustomizeGump.DisplayTo(from, this); - } - - public void ChangeGender() - { - Female = !Female; - - if (Female) - { - Body = 401; - Name = NameList.RandomName("female"); - - FacialHairItemID = 0; - } - else - { - Body = 400; - Name = NameList.RandomName("male"); - } - } - - public override void InitSBInfo() - { - if (Title is not ("the waiter" or "the barkeeper" or "the baker" or "the innkeeper" or "the chef")) - { - _sbInfos.Clear(); - return; - } - - if (_sbInfos.Count == 0) - { - _sbInfos.Add(new SBPlayerBarkeeper()); - } - } - - private void Deserialize(IGenericReader reader, int version) - { - _house = (BaseHouse)reader.ReadEntity(); - _owner = reader.ReadEntity(); - - _rumors = new BarkeeperRumor[reader.ReadEncodedInt()]; - - for (var i = 0; i < _rumors.Length; ++i) - { - if (reader.ReadBool()) - { - _rumors[i] = new BarkeeperRumor(this) - { - Message = reader.ReadString(), - Keyword = reader.ReadString() - }; - } - } - - _tipMessage = reader.ReadString(); - } -} diff --git a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs index 485d56c1d..88878d791 100644 --- a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs @@ -11,7 +11,6 @@ using Server.Mobiles; using Server.Network; using Server.Regions; using Server.Logging; -using Server.Systems.FeatureFlags; namespace Server.Mobiles { @@ -45,7 +44,7 @@ namespace Server.Mobiles // See SBAnimalTrainer for an example _enableVendorBuyOPL = ServerConfiguration.GetSetting("opl.enableForVendorBuy", true); - _vendorInvulnerable = ServerConfiguration.GetSetting("vendor.isInvulnerable", Core.LBR); + _vendorInvulnerable = ServerConfiguration.GetSetting("vendor.isInvulnerable", false); } public static void Initialize() @@ -97,7 +96,7 @@ namespace Server.Mobiles public virtual NpcGuild NpcGuild => NpcGuild.None; - public override bool IsInvulnerable => _vendorInvulnerable; + public override bool IsInvulnerable => Core.LBR || _vendorInvulnerable; public virtual DateTime NextTrickOrTreat { get; set; } @@ -547,7 +546,7 @@ namespace Server.Mobiles seller.PlaySound(0x0037); // Gold dropping sound - if (ContentFeatureFlags.BulkOrders && SupportsBulkOrders(seller)) + if (SupportsBulkOrders(seller)) { var bulkOrder = CreateBulkOrder(seller, false); @@ -561,6 +560,8 @@ namespace Server.Mobiles } } } + // no cliloc for this? + // SayTo( seller, true, "Thank you! I bought {0} item{1}. Here is your {2}gp.", Sold, (Sold > 1 ? "s" : ""), GiveGold ); return true; } @@ -842,14 +843,13 @@ namespace Server.Mobiles public virtual void VendorBuy(Mobile from) { - if (!IsActiveSeller || !from.CheckAlive()) + if (!IsActiveSeller) { return; } - if (!ContentFeatureFlags.VendorPurchase) + if (!from.CheckAlive()) { - from.SendMessage(0x22, "Vendor purchases are temporarily disabled."); return; } @@ -1014,14 +1014,13 @@ namespace Server.Mobiles public virtual void VendorSell(Mobile from) { - if (!IsActiveBuyer || !from.CheckAlive()) + if (!IsActiveBuyer) { return; } - if (!ContentFeatureFlags.VendorSell) + if (!from.CheckAlive()) { - from.SendMessage(0x22, "Vendor sales are temporarily disabled."); return; } @@ -1082,7 +1081,7 @@ namespace Server.Mobiles var smallBod = dropped as SmallBOD; var largeBod = dropped as LargeBOD; - if (!ContentFeatureFlags.BulkOrders || !(smallBod != null || largeBod != null)) + if (!(smallBod != null || largeBod != null)) { return base.OnDragDrop(from, dropped); } @@ -1166,7 +1165,7 @@ namespace Server.Mobiles } private static bool ProcessSinglePurchase( - BuyItemResponse buy, GenericBuyInfo bii, List validBuy, + BuyItemResponse buy, IBuyItemInfo bii, List validBuy, ref int controlSlots, ref bool fullPurchase, ref int totalCost ) { @@ -1194,7 +1193,7 @@ namespace Server.Mobiles return false; } - var totalCostLong = (long)totalCost + bii.Price * amount; + long totalCostLong = (long)totalCost + bii.Price * amount; if (totalCostLong > int.MaxValue) { @@ -1214,7 +1213,7 @@ namespace Server.Mobiles return true; } - private static void ProcessValidPurchase(int amount, GenericBuyInfo bii, Mobile buyer, Container cont) + private static void ProcessValidPurchase(int amount, IBuyItemInfo bii, Mobile buyer, Container cont) { if (amount > bii.Amount) { @@ -1404,17 +1403,17 @@ namespace Server.Mobiles { if (from.Alive && IsActiveVendor) { - if (ContentFeatureFlags.BulkOrders && SupportsBulkOrders(from)) + if (SupportsBulkOrders(from)) { list.Add(new BulkOrderInfoEntry()); } - if (ContentFeatureFlags.VendorPurchase && IsActiveSeller) + if (IsActiveSeller) { list.Add(new VendorBuyEntry(CheckVendorAccess(from))); } - if (ContentFeatureFlags.VendorSell && IsActiveBuyer) + if (IsActiveBuyer) { list.Add(new VendorSellEntry(CheckVendorAccess(from))); } @@ -1447,7 +1446,7 @@ namespace Server.Mobiles public override void OnClick(Mobile from, IEntity target) { - if (!ContentFeatureFlags.BulkOrders || target is not BaseVendor vendor || !vendor.SupportsBulkOrders(from)) + if (target is not BaseVendor vendor || !vendor.SupportsBulkOrders(from)) { return; } diff --git a/Projects/UOContent/Mobiles/Vendors/GenericBuy.cs b/Projects/UOContent/Mobiles/Vendors/GenericBuy.cs index 776f1020e..3be1f35d0 100644 --- a/Projects/UOContent/Mobiles/Vendors/GenericBuy.cs +++ b/Projects/UOContent/Mobiles/Vendors/GenericBuy.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; -using ModernUO.Serialization; using Server.Items; namespace Server.Mobiles { - public partial class GenericBuyInfo : IBuyItemInfo + public class GenericBuyInfo : IBuyItemInfo { private int m_Amount; private IEntity m_DisplayEntity; @@ -165,32 +164,33 @@ namespace Server.Mobiles return m_DisplayEntity; } - [SerializationGenerator(0, false)] - private partial class DisplayCache : Container + private class DisplayCache : Container { - private static DisplayCache _cache; - - [SerializableField(0)] - private List _mobiles; + private static DisplayCache m_Cache; + private List m_Mobiles; private Dictionary m_Table; private DisplayCache() : base(0) { m_Table = new Dictionary(); - _mobiles = new List(); + m_Mobiles = new List(); + } + + public DisplayCache(Serial serial) : base(serial) + { } public static DisplayCache Cache { get { - if (_cache?.Deleted != false) + if (m_Cache?.Deleted != false) { - _cache = new DisplayCache(); + m_Cache = new DisplayCache(); } - return _cache; + return m_Cache; } } @@ -213,18 +213,20 @@ namespace Server.Mobiles } else if (obj is Mobile mobile) { - _mobiles.Add(mobile); + m_Mobiles.Add(mobile); } } - private void ClearContents() + public override void OnAfterDelete() { - for (var i = 0; i < _mobiles.Count; ++i) + base.OnAfterDelete(); + + for (var i = 0; i < m_Mobiles.Count; ++i) { - _mobiles[i].Delete(); + m_Mobiles[i].Delete(); } - _mobiles.Clear(); + m_Mobiles.Clear(); for (var i = Items.Count - 1; i >= 0; --i) { @@ -233,36 +235,44 @@ namespace Server.Mobiles Items[i].Delete(); } } - } - public override void OnAfterDelete() - { - base.OnAfterDelete(); - - ClearContents(); - - if (_cache == this) + if (m_Cache == this) { - _cache = null; + m_Cache = null; } } - [AfterDeserialization] - private void AfterDeserialization() + public override void Serialize(IGenericWriter writer) { - if (_cache == this && Items.Count <= 0 && _mobiles.Count <= 0) - { - return; - } + base.Serialize(writer); + + writer.Write(0); // version + + writer.Write(m_Mobiles); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + List entities = [..Items]; + entities.AddRange(reader.ReadEntityList()); + + m_Mobiles = new List(); // This cannot be null in case it is referenced before disposing + m_Table = new Dictionary(); Timer.StartTimer(() => { - ClearContents(); - - if (_cache == null) + foreach (var entity in entities) { - _cache = this; - m_Table = []; + entity.Delete(); + } + + if (m_Cache == null) + { + m_Cache = this; } else { diff --git a/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs b/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs index 847209869..467ea007b 100644 --- a/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs +++ b/Projects/UOContent/Mobiles/Vendors/NPC/AnimalTrainer.cs @@ -142,7 +142,7 @@ namespace Server.Mobiles if (list.Count > 0) { - ClaimListGump.DisplayTo(this, from, list); + from.SendGump(new ClaimListGump(this, from, list)); } else { @@ -152,8 +152,7 @@ namespace Server.Mobiles public void EndClaimList(Mobile from, BaseCreature pet) { - if (pet?.Deleted != false || from.Map != Map || from is not PlayerMobile pm - || pm.Stabled?.Contains(pet) != true || !from.CheckAlive()) + if (pet?.Deleted != false || from.Map != Map || from is not PlayerMobile pm || pm.Stabled?.Contains(pet) != true || !from.CheckAlive()) { return; } @@ -422,50 +421,38 @@ namespace Server.Mobiles } } - private class ClaimListGump : DynamicGump + private class ClaimListGump : Gump { - private readonly List _list; - private readonly AnimalTrainer _trainer; + private readonly Mobile m_From; + private readonly List m_List; + private readonly AnimalTrainer m_Trainer; public override bool Singleton => true; - private ClaimListGump(AnimalTrainer trainer, List list) : base(50, 50) + public ClaimListGump(AnimalTrainer trainer, Mobile from, List list) : base(50, 50) { - _trainer = trainer; - _list = list; - } + m_Trainer = trainer; + m_From = from; + m_List = list; - public static void DisplayTo(AnimalTrainer trainer, Mobile from, List list) - { - if (from?.NetState == null || trainer == null || list == null || list.Count == 0) + AddPage(0); + + AddBackground(0, 0, 325, 50 + list.Count * 20, 9250); + AddAlphaRegion(5, 5, 315, 40 + list.Count * 20); + + AddHtml(15, 15, 275, 20, "Select a pet to retrieve from the stables:"); + + for (var i = 0; i < list.Count; ++i) { - return; - } - - from.SendGump(new ClaimListGump(trainer, list)); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 325, 50 + _list.Count * 20, 9250); - builder.AddAlphaRegion(5, 5, 315, 40 + _list.Count * 20); - - // Select a pet to retrieve from the stables: - builder.AddHtmlLocalized(15, 15, 275, 20, 1080333, 0x7FFF); - - for (var i = 0; i < _list.Count; ++i) - { - var pet = _list[i]; + var pet = list[i]; if (pet?.Deleted != false) { continue; } - builder.AddButton(15, 39 + i * 20, 10006, 10006, i + 1); - builder.AddHtml(32, 35 + i * 20, 275, 18, pet.Name.Color(0xC0C0EE)); + AddButton(15, 39 + i * 20, 10006, 10006, i + 1); + AddHtml(32, 35 + i * 20, 275, 18, pet.Name.Color(0xC0C0EE)); } } @@ -473,9 +460,9 @@ namespace Server.Mobiles { var index = info.ButtonID - 1; - if (index >= 0 && index < _list.Count) + if (index >= 0 && index < m_List.Count) { - _trainer.EndClaimList(sender.Mobile, _list[index]); + m_Trainer.EndClaimList(m_From, m_List[index]); } } } diff --git a/Projects/UOContent/Mobiles/Vendors/NPC/Blacksmith.cs b/Projects/UOContent/Mobiles/Vendors/NPC/Blacksmith.cs index 5d9b39cbd..033bd30e0 100644 --- a/Projects/UOContent/Mobiles/Vendors/NPC/Blacksmith.cs +++ b/Projects/UOContent/Mobiles/Vendors/NPC/Blacksmith.cs @@ -32,19 +32,19 @@ namespace Server.Mobiles public override void InitSBInfo() { /*m_SBInfos.Add( new SBSmithTools() ); - + m_SBInfos.Add( new SBMetalShields() ); m_SBInfos.Add( new SBWoodenShields() ); - + m_SBInfos.Add( new SBPlateArmor() ); - + m_SBInfos.Add( new SBHelmetArmor() ); m_SBInfos.Add( new SBChainmailArmor() ); m_SBInfos.Add( new SBRingmailArmor() ); m_SBInfos.Add( new SBAxeWeapon() ); m_SBInfos.Add( new SBPoleArmWeapon() ); m_SBInfos.Add( new SBRangedWeapon() ); - + m_SBInfos.Add( new SBKnifeWeapon() ); m_SBInfos.Add( new SBMaceWeapon() ); m_SBInfos.Add( new SBSpearForkWeapon() ); @@ -62,11 +62,17 @@ namespace Server.Mobiles { base.InitOutfit(); - Item item = Utility.RandomBool() ? new FullApron() : new RingmailChest(); + Item item = Utility.RandomBool() ? null : new RingmailChest(); - if (!EquipItem(item)) + if (item != null && !EquipItem(item)) { item.Delete(); + item = null; + } + + if (item == null) + { + AddItem(new FullApron()); } AddItem(new Bascinet()); diff --git a/Projects/UOContent/Mobiles/Vendors/NPC/CustomHairstylist.cs b/Projects/UOContent/Mobiles/Vendors/NPC/CustomHairstylist.cs index ae45ad6d1..819224578 100644 --- a/Projects/UOContent/Mobiles/Vendors/NPC/CustomHairstylist.cs +++ b/Projects/UOContent/Mobiles/Vendors/NPC/CustomHairstylist.cs @@ -5,536 +5,680 @@ using Server.Gumps; using Server.Items; using Server.Network; -namespace Server.Mobiles; - -[SerializationGenerator(0, false)] -public partial class CustomHairstylist : BaseVendor +namespace Server.Mobiles { - private static readonly HairstylistBuyInfo[] _sellList = - [ - new( - 1018357, // New Hair (50000 gold) - 50000, - false, - (from, vendor, price) => - new ChangeHairstyleGump(from, vendor, price, false, ChangeHairstyleEntry.HairEntries) - ), - new( - 1018358, // New Beard (50000 gold) - 50000, - true, - (from, vendor, price) => - new ChangeHairstyleGump(from, vendor, price, true, ChangeHairstyleEntry.BeardEntries) - ), - new( - 1018359, // Normal Hair Dye (50 gold) - 50, - false, - (_, vendor, price) => - new ChangeHairHueGump(vendor, price, true, true, ChangeHairHueEntry.RegularEntries) - ), - new( - 1018360, // Bright Hair Dye (500000 gold) - 500000, - false, - (_, vendor, price) => - new ChangeHairHueGump(vendor, price, true, true, ChangeHairHueEntry.BrightEntries) - ), - new( - 1018361, // Hair Only Dye (30000 gold) - 30000, - false, - (_, vendor, price) => - new ChangeHairHueGump(vendor, price, true, false, ChangeHairHueEntry.RegularEntries) - ), - new( - 1018362, // Beard Only Dye (30000 gold) - 30000, - true, - (_, vendor, price) => - new ChangeHairHueGump(vendor, price, false, true, ChangeHairHueEntry.RegularEntries) - ), - new( - 1018363, // Bright Hair Only Dye (500000 gold) - 500000, - false, - (_, vendor, price) => - new ChangeHairHueGump(vendor, price, true, false, ChangeHairHueEntry.BrightEntries) - ), - new( - 1018364, // Bright Beard Only Dye (500000 gold) - 500000, - true, - (_, vendor, price) => - new ChangeHairHueGump(vendor, price, false, true, ChangeHairHueEntry.BrightEntries) - ) - ]; - - [Constructible] - public CustomHairstylist() : base("the hairstylist") + [SerializationGenerator(0, false)] + public partial class CustomHairstylist : BaseVendor { - } + public static readonly object From = new(); + public static readonly object Vendor = new(); + public static readonly object Price = new(); - protected override List SBInfos { get; } = []; - - public override bool ClickTitle => false; - public override bool IsActiveBuyer => false; - public override bool IsActiveSeller => true; - - public override VendorShoeType ShoeType => Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals; - - public override bool OnBuyItems(Mobile buyer, List list) => false; - - public override void VendorBuy(Mobile from) => from.SendGump(new HairstylistBuyGump(from, this, _sellList)); - - public override int GetHairHue() => Utility.RandomBrightHue(); - - public override void InitOutfit() - { - base.InitOutfit(); - - AddItem(new Robe(Utility.RandomPinkHue())); - } - - public override void InitSBInfo() - { - } -} - -public class HairstylistBuyInfo -{ - public HairstylistBuyInfo( - TextDefinition title, int price, bool facialHair, Func gumpFunction - ) - { - Title = title; - Price = price; - FacialHair = facialHair; - GumpFactoryFn = gumpFunction; - } - - public TextDefinition Title { get; } - - public int Price { get; } - - public bool FacialHair { get; } - - public Func GumpFactoryFn { get; } -} - -public class HairstylistBuyGump : DynamicGump -{ - private readonly Mobile _from; - private readonly HairstylistBuyInfo[] _sellList; - private readonly Mobile _vendor; - public override bool Singleton => true; - - public HairstylistBuyGump(Mobile from, Mobile vendor, HairstylistBuyInfo[] sellList) : base(50, 50) - { - _from = from; - _vendor = vendor; - _sellList = sellList; - - var gumps = from.GetGumps(); - - gumps.Close(); - gumps.Close(); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var isFemale = _from.Female || _from.Body.IsFemale; - - var balance = Banker.GetBalance(_from); - var canAfford = 0; - - for (var i = 0; i < _sellList.Length; ++i) + private static readonly HairstylistBuyInfo[] m_SellList = { - var buyInfo = _sellList[i]; - if (balance >= buyInfo.Price && (!buyInfo.FacialHair || !isFemale)) + new( + 1018357, + 50000, + false, + typeof(ChangeHairstyleGump), + new[] + { From, Vendor, Price, false, ChangeHairstyleEntry.HairEntries } + ), + new( + 1018358, + 50000, + true, + typeof(ChangeHairstyleGump), + new[] + { From, Vendor, Price, true, ChangeHairstyleEntry.BeardEntries } + ), + new( + 1018359, + 50, + false, + typeof(ChangeHairHueGump), + new[] + { From, Vendor, Price, true, true, ChangeHairHueEntry.RegularEntries } + ), + new( + 1018360, + 500000, + false, + typeof(ChangeHairHueGump), + new[] + { From, Vendor, Price, true, true, ChangeHairHueEntry.BrightEntries } + ), + new( + 1018361, + 30000, + false, + typeof(ChangeHairHueGump), + new[] + { From, Vendor, Price, true, false, ChangeHairHueEntry.RegularEntries } + ), + new( + 1018362, + 30000, + true, + typeof(ChangeHairHueGump), + new[] + { From, Vendor, Price, false, true, ChangeHairHueEntry.RegularEntries } + ), + new( + 1018363, + 500000, + false, + typeof(ChangeHairHueGump), + new[] + { From, Vendor, Price, true, false, ChangeHairHueEntry.BrightEntries } + ), + new( + 1018364, + 500000, + true, + typeof(ChangeHairHueGump), + new[] + { From, Vendor, Price, false, true, ChangeHairHueEntry.BrightEntries } + ) + }; + + [Constructible] + public CustomHairstylist() : base("the hairstylist") + { + } + + protected override List SBInfos { get; } = new(); + + public override bool ClickTitle => false; + + public override bool IsActiveBuyer => false; + public override bool IsActiveSeller => true; + + public override VendorShoeType ShoeType => Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals; + + public override bool OnBuyItems(Mobile buyer, List list) => false; + + public override void VendorBuy(Mobile from) + { + from.SendGump(new HairstylistBuyGump(from, this, m_SellList)); + } + + public override int GetHairHue() => Utility.RandomBrightHue(); + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem(new Robe(Utility.RandomPinkHue())); + } + + public override void InitSBInfo() + { + } + } + + public class HairstylistBuyInfo + { + public HairstylistBuyInfo(int title, int price, bool facialHair, Type gumpType, object[] args) + { + Title = title; + Price = price; + FacialHair = facialHair; + GumpType = gumpType; + GumpArgs = args; + } + + public HairstylistBuyInfo(string title, int price, bool facialHair, Type gumpType, object[] args) + { + TitleString = title; + Price = price; + FacialHair = facialHair; + GumpType = gumpType; + GumpArgs = args; + } + + public int Title { get; } + + public string TitleString { get; } + + public int Price { get; } + + public bool FacialHair { get; } + + public Type GumpType { get; } + + public object[] GumpArgs { get; } + } + + public class HairstylistBuyGump : Gump + { + private readonly Mobile m_From; + private readonly HairstylistBuyInfo[] m_SellList; + private readonly Mobile m_Vendor; + public override bool Singleton => true; + public HairstylistBuyGump(Mobile from, Mobile vendor, HairstylistBuyInfo[] sellList) : base(50, 50) + { + m_From = from; + m_Vendor = vendor; + m_SellList = sellList; + + var gumps = from.GetGumps(); + + gumps.Close(); + gumps.Close(); + + var isFemale = from.Female || from.Body.IsFemale; + + var balance = Banker.GetBalance(from); + var canAfford = 0; + + for (var i = 0; i < sellList.Length; ++i) { - ++canAfford; + if (balance >= sellList[i].Price && (!sellList[i].FacialHair || !isFemale)) + { + ++canAfford; + } + } + + AddPage(0); + + AddBackground(50, 10, 450, 100 + canAfford * 25, 2600); + + AddHtmlLocalized(100, 40, 350, 20, 1018356); // Choose your hairstyle change: + + var index = 0; + + for (var i = 0; i < sellList.Length; ++i) + { + if (balance >= sellList[i].Price && (!sellList[i].FacialHair || !isFemale)) + { + if (sellList[i].TitleString != null) + { + AddHtml(140, 75 + index * 25, 300, 20, sellList[i].TitleString); + } + else + { + AddHtmlLocalized(140, 75 + index * 25, 300, 20, sellList[i].Title); + } + + AddButton(100, 75 + index++ * 25, 4005, 4007, 1 + i); + } } } - builder.AddPage(); - builder.AddBackground(50, 10, 450, 100 + canAfford * 25, 2600); - builder.AddHtmlLocalized(100, 40, 350, 20, 1018356); // Choose your hairstyle change: - - var index = 0; - - for (var i = 0; i < _sellList.Length; ++i) + public override void OnResponse(NetState sender, in RelayInfo info) { - var buyInfo = _sellList[i]; - if (balance >= buyInfo.Price && (!buyInfo.FacialHair || !isFemale)) + var index = info.ButtonID - 1; + + if (index >= 0 && index < m_SellList.Length) { - buyInfo.Title.AddHtmlText(ref builder, 140, 75 + index * 25, 300, 20); - builder.AddButton(100, 75 + index++ * 25, 4005, 4007, 1 + i); + var buyInfo = m_SellList[index]; + + var balance = Banker.GetBalance(m_From); + + var isFemale = m_From.Female || m_From.Body.IsFemale; + + if (buyInfo.FacialHair && isFemale) + { + m_Vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1010639, m_From.NetState); + } + else if (balance >= buyInfo.Price) + { + try + { + var origArgs = buyInfo.GumpArgs; + var args = new object[origArgs.Length]; + + for (var i = 0; i < args.Length; ++i) + { + if (origArgs[i] == CustomHairstylist.Price) + { + args[i] = m_SellList[index].Price; + } + else if (origArgs[i] == CustomHairstylist.From) + { + args[i] = m_From; + } + else if (origArgs[i] == CustomHairstylist.Vendor) + { + args[i] = m_Vendor; + } + else + { + args[i] = origArgs[i]; + } + } + + var g = buyInfo.GumpType.CreateInstance(args); + + m_From.SendGump(g); + } + catch + { + // ignored + } + } + else + { + m_Vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1042293, m_From.NetState); + } } } } - public override void OnResponse(NetState sender, in RelayInfo info) + public class ChangeHairHueEntry { - var index = info.ButtonID - 1; - - if (index < 0 || index >= _sellList.Length) + public static readonly ChangeHairHueEntry[] BrightEntries = { - return; + new("*****", 12, 10), + new("*****", 32, 5), + new("*****", 38, 8), + new("*****", 54, 3), + new("*****", 62, 10), + new("*****", 81, 2), + new("*****", 89, 2), + new("*****", 1153, 2) + }; + + public static readonly ChangeHairHueEntry[] RegularEntries = + { + new("*****", 1602, 26), + new("*****", 1628, 27), + new("*****", 1502, 32), + new("*****", 1302, 32), + new("*****", 1402, 32), + new("*****", 1202, 24), + new("*****", 2402, 29), + new("*****", 2213, 6), + new("*****", 1102, 8), + new("*****", 1110, 8), + new("*****", 1118, 16), + new("*****", 1134, 16) + }; + + public ChangeHairHueEntry(string name, int[] hues) + { + Name = name; + Hues = hues; } - var balance = Banker.GetBalance(_from); - var isFemale = _from.Female || _from.Body.IsFemale; - - var buyInfo = _sellList[index]; - if (buyInfo.FacialHair && isFemale) + public ChangeHairHueEntry(string name, int start, int count) { - _vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1010639, _from.NetState); - } - else if (balance >= buyInfo.Price) - { - _from.SendGump(buyInfo.GumpFactoryFn(_from, _vendor, buyInfo.Price)); - } - else - { - _vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1042293, _from.NetState); - } - } -} + Name = name; -public class ChangeHairHueEntry -{ - public static readonly ChangeHairHueEntry[] BrightEntries = - [ - new("*****", 12, 10), - new("*****", 32, 5), - new("*****", 38, 8), - new("*****", 54, 3), - new("*****", 62, 10), - new("*****", 81, 2), - new("*****", 89, 2), - new("*****", 1153, 2) - ]; + Hues = new int[count]; - public static readonly ChangeHairHueEntry[] RegularEntries = - [ - new("*****", 1602, 26), - new("*****", 1628, 27), - new("*****", 1502, 32), - new("*****", 1302, 32), - new("*****", 1402, 32), - new("*****", 1202, 24), - new("*****", 2402, 29), - new("*****", 2213, 6), - new("*****", 1102, 8), - new("*****", 1110, 8), - new("*****", 1118, 16), - new("*****", 1134, 16) - ]; - - public ChangeHairHueEntry(string name, int[] hues) - { - Name = name; - Hues = hues; - } - - public ChangeHairHueEntry(string name, int start, int count) - { - Name = name; - - Hues = new int[count]; - - for (var i = 0; i < count; ++i) - { - Hues[i] = start + i; - } - } - - public string Name { get; } - - public int[] Hues { get; } -} - -public class ChangeHairHueGump : DynamicGump -{ - private readonly ChangeHairHueEntry[] _entries; - private readonly bool _facialHair; - private readonly bool _hair; - private readonly int _price; - private readonly Mobile _vendor; - - public override bool Singleton => true; - - public ChangeHairHueGump( - Mobile vendor, int price, bool hair, bool facialHair, ChangeHairHueEntry[] entries - ) : base(50, 50) - { - _vendor = vendor; - _price = price; - _hair = hair; - _facialHair = facialHair; - _entries = entries; - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(100, 10, 350, 370, 2600); - builder.AddBackground(120, 54, 110, 270, 5100); - - builder.AddHtmlLocalized(155, 25, 240, 30, 1011013); //
Hair Color Selection Menu
- - builder.AddHtmlLocalized(150, 330, 220, 35, 1011014); // Dye my hair this color! - builder.AddButton(380, 330, 4005, 4007, 1); - - for (var i = 0; i < _entries.Length; ++i) - { - var entry = _entries[i]; - - builder.AddLabel(130, 59 + i * 22, entry.Hues[0] - 1, entry.Name); - builder.AddButton(207, 60 + i * 22, 5224, 5224, 0, GumpButtonType.Page, 1 + i); - } - - for (var i = 0; i < _entries.Length; ++i) - { - var entry = _entries[i]; - var hues = entry.Hues; - var name = entry.Name; - - builder.AddPage(1 + i); - - for (var j = 0; j < hues.Length; ++j) + for (var i = 0; i < count; ++i) { - var page = Math.DivRem(j, 16, out var index); - builder.AddLabel(278 + page * 80, 52 + index * 17, hues[j] - 1, name); - builder.AddRadio(260 + page * 80, 52 + index * 17, 210, 211, false, j * _entries.Length + i); + Hues[i] = start + i; } } + + public string Name { get; } + + public int[] Hues { get; } } - public override void OnResponse(NetState sender, in RelayInfo info) + public class ChangeHairHueGump : Gump { - var from = sender.Mobile; + private readonly ChangeHairHueEntry[] m_Entries; + private readonly bool m_FacialHair; + private readonly Mobile m_From; + private readonly bool m_Hair; + private readonly int m_Price; + private readonly Mobile m_Vendor; - var switches = info.Switches; - if (info.ButtonID != 1 || switches.Length <= 0) + public override bool Singleton => true; + + public ChangeHairHueGump( + Mobile from, Mobile vendor, int price, bool hair, bool facialHair, + ChangeHairHueEntry[] entries + ) : base(50, 50) { - // You decide not to change your hairstyle. - _vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1013009, from.NetState); - return; - } + m_From = from; + m_Vendor = vendor; + m_Price = price; + m_Hair = hair; + m_FacialHair = facialHair; + m_Entries = entries; - var offset = Math.DivRem(switches[0], _entries.Length, out var index); + AddPage(0); - if (index < 0 || index >= _entries.Length || offset < 0 || offset >= _entries[index].Hues.Length) - { - return; - } + AddBackground(100, 10, 350, 370, 2600); + AddBackground(120, 54, 110, 270, 5100); - if ((!_hair || from.HairItemID <= 0) && (!_facialHair || from.FacialHairItemID <= 0)) - { - // You have no hair to dye and you cannot use this. - _vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 502623, sender); - return; - } + AddHtmlLocalized(155, 25, 240, 30, 1011013); //
Hair Color Selection Menu
- if (!Banker.Withdraw(from, _price)) - { - // You cannot afford my services for that style. - _vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1042293, sender); - return; - } + AddHtmlLocalized(150, 330, 220, 35, 1011014); // Dye my hair this color! + AddButton(380, 330, 4005, 4007, 1); - var hue = _entries[index].Hues[offset]; - - if (_hair) - { - from.HairHue = hue; - } - - if (_facialHair) - { - from.FacialHairHue = hue; - } - } -} - -public class ChangeHairstyleEntry -{ - public static readonly ChangeHairstyleEntry[] HairEntries = - [ - new(50700, 70 - 137, 20 - 60, 0x203B), - new(60710, 193 - 260, 18 - 60, 0x2045), - new(50703, 316 - 383, 25 - 60, 0x2044), - new(60708, 70 - 137, 75 - 125, 0x203C), - new(60900, 193 - 260, 85 - 125, 0x2047), - new(60713, 320 - 383, 85 - 125, 0x204A), - new(60702, 70 - 137, 140 - 190, 0x203D), - new(60707, 193 - 260, 140 - 190, 0x2049), - new(60901, 315 - 383, 150 - 190, 0x2048), - new(0, 0, 0, 0) - ]; - - public static readonly ChangeHairstyleEntry[] BeardEntries = - [ - new(50800, 120 - 187, 30 - 80, 0x2040), - new(50904, 243 - 310, 33 - 80, 0x204B), - new(50906, 120 - 187, 100 - 150, 0x204D), - new(50801, 243 - 310, 95 - 150, 0x203E), - new(50802, 120 - 187, 173 - 220, 0x203F), - new(50905, 243 - 310, 165 - 220, 0x204C), - new(50808, 120 - 187, 242 - 290, 0x2041), - new(0, 0, 0, 0) - ]; - - public ChangeHairstyleEntry(int gumpID, int x, int y, int itemID) - { - GumpID = gumpID; - X = x; - Y = y; - ItemID = itemID; - } - - public int ItemID { get; } - - public int GumpID { get; } - - public int X { get; } - - public int Y { get; } -} - -public class ChangeHairstyleGump : DynamicGump -{ - private readonly ChangeHairstyleEntry[] _entries; - private readonly bool _facialHair; - private readonly Mobile _from; - private readonly int _price; - private readonly Mobile _vendor; - - public override bool Singleton => true; - - public ChangeHairstyleGump(Mobile from, Mobile vendor, int price, bool facialHair, ChangeHairstyleEntry[] entries) - : base(50, 50) - { - _from = from; - _vendor = vendor; - _price = price; - _facialHair = facialHair; - _entries = entries; - - var gumps = from.GetGumps(); - - gumps.Close(); - gumps.Close(); - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var tableWidth = _facialHair ? 2 : 3; - var tableHeight = (_entries.Length + tableWidth - (_facialHair ? 1 : 2)) / tableWidth; - const int offsetWidth = 123; - var offsetHeight = _facialHair ? 70 : 65; - var tableWidthOffset = 81 + tableWidth * offsetWidth; - var tableHeightOffset = 45 + tableHeight * offsetHeight; - - builder.AddPage(); - - builder.AddBackground(0, 0, tableWidthOffset, 60 + tableHeightOffset, 2600); - - builder.AddButton(45, tableHeightOffset, 4005, 4007, 1); - builder.AddHtmlLocalized(77, tableHeightOffset, 90, 35, 1006044); // Ok - - builder.AddButton(tableWidthOffset - 180, tableHeightOffset, 4005, 4007, 0); - // Cancel - builder.AddHtmlLocalized(tableWidthOffset - 148, tableHeightOffset, 90, 35, 1006045); - - if (!_facialHair) - { - builder.AddHtmlLocalized(50, 15, 350, 20, 1018353); //
New Hairstyle
- } - else - { - builder.AddHtmlLocalized(55, 15, 200, 20, 1018354); //
New Beard
- } - - for (var i = 0; i < _entries.Length; ++i) - { - var yTable = Math.DivRem(i, tableWidth, out var xTable); - var xOffset = xTable * offsetWidth; - var yOffset = yTable * offsetHeight; - var entry = _entries[i]; - - if (entry.GumpID != 0) + for (var i = 0; i < entries.Length; ++i) { - builder.AddRadio(40 + xOffset, 70 + yOffset, 208, 209, false, i); - builder.AddBackground(87 + xOffset, 50 + yOffset, 50, 50, 2620); - builder.AddImage(87 + xOffset + entry.X, 50 + yOffset + entry.Y, entry.GumpID); + var entry = entries[i]; + + AddLabel(130, 59 + i * 22, entry.Hues[0] - 1, entry.Name); + AddButton(207, 60 + i * 22, 5224, 5224, 0, GumpButtonType.Page, 1 + i); } - else if (!_facialHair) + + for (var i = 0; i < entries.Length; ++i) { - builder.AddRadio(40 + (xTable + 1) * offsetWidth, 240, 208, 209, false, i); - builder. AddHtmlLocalized(60 + (xTable + 1) * offsetWidth, 240, 85, 35, 1011064); // Bald + var entry = entries[i]; + var hues = entry.Hues; + var name = entry.Name; + + AddPage(1 + i); + + for (var j = 0; j < hues.Length; ++j) + { + AddLabel(278 + j / 16 * 80, 52 + j % 16 * 17, hues[j] - 1, name); + AddRadio(260 + j / 16 * 80, 52 + j % 16 * 17, 210, 211, false, j * entries.Length + i); + } + } + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (info.ButtonID == 1) + { + var switches = info.Switches; + + if (switches.Length > 0) + { + var index = switches[0] % m_Entries.Length; + var offset = switches[0] / m_Entries.Length; + + if (index >= 0 && index < m_Entries.Length) + { + if (offset >= 0 && offset < m_Entries[index].Hues.Length) + { + if (m_Hair && m_From.HairItemID > 0 || m_FacialHair && m_From.FacialHairItemID > 0) + { + if (!Banker.Withdraw(m_From, m_Price)) + { + m_Vendor.PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + 1042293, // You cannot afford my services for that style. + m_From.NetState + ); + return; + } + + var hue = m_Entries[index].Hues[offset]; + + if (m_Hair) + { + m_From.HairHue = hue; + } + + if (m_FacialHair) + { + m_From.FacialHairHue = hue; + } + } + else + { + m_Vendor.PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + 502623, // You have no hair to dye and you cannot use this. + m_From.NetState + ); + } + } + } + } + else + { + // You decide not to change your hairstyle. + m_Vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1013009, m_From.NetState); + } } else { - builder.AddRadio(40 + xOffset, 70 + yOffset, 208, 209, false, i); - builder.AddHtmlLocalized(60 + xOffset, 70 + yOffset, 85, 35, 1011064); // Bald + // You decide not to change your hairstyle. + m_Vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1013009, m_From.NetState); } } } - public override void OnResponse(NetState sender, in RelayInfo info) + public class ChangeHairstyleEntry { - if (_facialHair && (_from.Female || _from.Body.IsFemale)) + public static readonly ChangeHairstyleEntry[] HairEntries = { - return; + new(50700, 70 - 137, 20 - 60, 0x203B), + new(60710, 193 - 260, 18 - 60, 0x2045), + new(50703, 316 - 383, 25 - 60, 0x2044), + new(60708, 70 - 137, 75 - 125, 0x203C), + new(60900, 193 - 260, 85 - 125, 0x2047), + new(60713, 320 - 383, 85 - 125, 0x204A), + new(60702, 70 - 137, 140 - 190, 0x203D), + new(60707, 193 - 260, 140 - 190, 0x2049), + new(60901, 315 - 383, 150 - 190, 0x2048), + new(0, 0, 0, 0) + }; + + public static readonly ChangeHairstyleEntry[] BeardEntries = + { + new(50800, 120 - 187, 30 - 80, 0x2040), + new(50904, 243 - 310, 33 - 80, 0x204B), + new(50906, 120 - 187, 100 - 150, 0x204D), + new(50801, 243 - 310, 95 - 150, 0x203E), + new(50802, 120 - 187, 173 - 220, 0x203F), + new(50905, 243 - 310, 165 - 220, 0x204C), + new(50808, 120 - 187, 242 - 290, 0x2041), + new(0, 0, 0, 0) + }; + + public ChangeHairstyleEntry(int gumpID, int x, int y, int itemID) + { + GumpID = gumpID; + X = x; + Y = y; + ItemID = itemID; } - if (_from.Race == Race.Elf) + public int ItemID { get; } + + public int GumpID { get; } + + public int X { get; } + + public int Y { get; } + } + + public class ChangeHairstyleGump : Gump + { + private readonly ChangeHairstyleEntry[] m_Entries; + private readonly bool m_FacialHair; + private readonly Mobile m_From; + private readonly int m_Price; + private readonly Mobile m_Vendor; + + public override bool Singleton => true; + + public ChangeHairstyleGump( + Mobile from, Mobile vendor, int price, bool facialHair, ChangeHairstyleEntry[] entries + ) : base(50, 50) { - _from.SendMessage("This isn't implemented for elves yet. Sorry!"); - return; + m_From = from; + m_Vendor = vendor; + m_Price = price; + m_FacialHair = facialHair; + m_Entries = entries; + + var gumps = from.GetGumps(); + + gumps.Close(); + gumps.Close(); + + var tableWidth = m_FacialHair ? 2 : 3; + var tableHeight = (entries.Length + tableWidth - (m_FacialHair ? 1 : 2)) / tableWidth; + var offsetWidth = 123; + var offsetHeight = m_FacialHair ? 70 : 65; + + AddPage(0); + + AddBackground(0, 0, 81 + tableWidth * offsetWidth, 105 + tableHeight * offsetHeight, 2600); + + AddButton(45, 45 + tableHeight * offsetHeight, 4005, 4007, 1); + AddHtmlLocalized(77, 45 + tableHeight * offsetHeight, 90, 35, 1006044); // Ok + + AddButton(81 + tableWidth * offsetWidth - 180, 45 + tableHeight * offsetHeight, 4005, 4007, 0); + AddHtmlLocalized( + 81 + tableWidth * offsetWidth - 148, + 45 + tableHeight * offsetHeight, + 90, + 35, + 1006045 + ); // Cancel + + if (!facialHair) + { + AddHtmlLocalized(50, 15, 350, 20, 1018353); //
New Hairstyle
+ } + else + { + AddHtmlLocalized(55, 15, 200, 20, 1018354); //
New Beard
+ } + + for (var i = 0; i < entries.Length; ++i) + { + var xTable = i % tableWidth; + var yTable = i / tableWidth; + + if (entries[i].GumpID != 0) + { + AddRadio(40 + xTable * offsetWidth, 70 + yTable * offsetHeight, 208, 209, false, i); + AddBackground(87 + xTable * offsetWidth, 50 + yTable * offsetHeight, 50, 50, 2620); + AddImage( + 87 + xTable * offsetWidth + entries[i].X, + 50 + yTable * offsetHeight + entries[i].Y, + entries[i].GumpID + ); + } + else if (!facialHair) + { + AddRadio(40 + (xTable + 1) * offsetWidth, 240, 208, 209, false, i); + AddHtmlLocalized(60 + (xTable + 1) * offsetWidth, 240, 85, 35, 1011064); // Bald + } + else + { + AddRadio(40 + xTable * offsetWidth, 70 + yTable * offsetHeight, 208, 209, false, i); + AddHtmlLocalized(60 + xTable * offsetWidth, 70 + yTable * offsetHeight, 85, 35, 1011064); // Bald + } + } } - var switches = info.Switches; - - if (info.ButtonID != 1 || switches.Length <= 0) + public override void OnResponse(NetState sender, in RelayInfo info) { - // You decide not to change your hairstyle. - _vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1013009, _from.NetState); - return; - } + if (m_FacialHair && (m_From.Female || m_From.Body.IsFemale)) + { + return; + } - var index = switches[0]; + if (m_From.Race == Race.Elf) + { + m_From.SendMessage("This isn't implemented for elves yet. Sorry!"); + return; + } - if (index < 0 || index >= _entries.Length) - { - return; - } + if (info.ButtonID == 1) + { + var switches = info.Switches; - var entry = _entries[index]; + if (switches.Length > 0) + { + var index = switches[0]; - (_from as PlayerMobile)?.SetHairMods(-1, -1); + if (index >= 0 && index < m_Entries.Length) + { + var entry = m_Entries[index]; - if ((_facialHair ? _from.FacialHairItemID : _from.HairItemID) == entry.ItemID) - { - return; - } + (m_From as PlayerMobile)?.SetHairMods(-1, -1); - if (!Banker.Withdraw(_from, _price)) - { - // You cannot afford my services for that style. - _vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1042293, _from.NetState); - } - else if (_facialHair) - { - _from.FacialHairItemID = entry.ItemID; - } - else - { - _from.HairItemID = entry.ItemID; + var hairID = m_From.HairItemID; + var facialHairID = m_From.FacialHairItemID; + + if (entry.ItemID == 0) + { + if (m_FacialHair ? facialHairID == 0 : hairID == 0) + { + return; + } + + if (Banker.Withdraw(m_From, m_Price)) + { + if (m_FacialHair) + { + m_From.FacialHairItemID = 0; + } + else + { + m_From.HairItemID = 0; + } + } + else + { + m_Vendor.PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + 1042293, // You cannot afford my services for that style. + m_From.NetState + ); + } + } + else + { + if (m_FacialHair) + { + if (facialHairID > 0 && facialHairID == entry.ItemID) + { + return; + } + } + else + { + if (hairID > 0 && hairID == entry.ItemID) + { + return; + } + } + + if (Banker.Withdraw(m_From, m_Price)) + { + if (m_FacialHair) + { + m_From.FacialHairItemID = entry.ItemID; + } + else + { + m_From.HairItemID = entry.ItemID; + } + } + else + { + m_Vendor.PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + 1042293, + m_From.NetState + ); // You cannot afford my services for that style. + } + } + } + } + else + { + // You decide not to change your hairstyle. + m_Vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1013009, m_From.NetState); + } + } + else + { + // You decide not to change your hairstyle. + m_Vendor.PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1013009, m_From.NetState); + } } } } diff --git a/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/BlacksmithGuildmaster.cs b/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/BlacksmithGuildmaster.cs index 2bc52b4c7..cfc6baf8c 100644 --- a/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/BlacksmithGuildmaster.cs +++ b/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/BlacksmithGuildmaster.cs @@ -32,11 +32,17 @@ namespace Server.Mobiles { base.InitOutfit(); - Item item = Utility.RandomBool() ? new FullApron() : new RingmailChest(); + Item item = Utility.RandomBool() ? null : new RingmailChest(); - if (!EquipItem(item)) + if (item != null && !EquipItem(item)) { item.Delete(); + item = null; + } + + if (item == null) + { + AddItem(new FullApron()); } AddItem(new Bascinet()); diff --git a/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/TinkerGuildmaster.cs b/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/TinkerGuildmaster.cs index 789ce4824..9e9dbad67 100644 --- a/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/TinkerGuildmaster.cs +++ b/Projects/UOContent/Mobiles/Vendors/NPC/Guildmasters/TinkerGuildmaster.cs @@ -1,6 +1,7 @@ using ModernUO.Serialization; using Server.Collections; using Server.ContextMenus; +using Server.Gumps; using Server.Items; namespace Server.Mobiles @@ -59,7 +60,7 @@ namespace Server.Mobiles if (Banker.GetBalance(from) >= 100000) { - WeaponEngravingTool.WeaponEngravingToolConfirmGump.DisplayTo(from, tool, vendor); + from.SendGump(new WeaponEngravingTool.ConfirmGump(tool, vendor)); } else { diff --git a/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs b/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs new file mode 100644 index 000000000..c658e0111 --- /dev/null +++ b/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs @@ -0,0 +1,1152 @@ +using System; +using System.Collections.Generic; +using Server.Collections; +using Server.ContextMenus; +using Server.Gumps; +using Server.Items; +using Server.Multis; +using Server.Network; +using Server.Prompts; + +namespace Server.Mobiles +{ + public class ChangeRumorMessagePrompt : Prompt + { + private readonly PlayerBarkeeper m_Barkeeper; + private readonly int m_RumorIndex; + + public ChangeRumorMessagePrompt(PlayerBarkeeper barkeeper, int rumorIndex) + { + m_Barkeeper = barkeeper; + m_RumorIndex = rumorIndex; + } + + public override void OnCancel(Mobile from) + { + OnResponse(from, ""); + } + + public override void OnResponse(Mobile from, string text) + { + if (text.Length > 130) + { + text = text[..130]; + } + + m_Barkeeper.EndChangeRumor(from, m_RumorIndex, text); + } + } + + public class ChangeRumorKeywordPrompt : Prompt + { + private readonly PlayerBarkeeper m_Barkeeper; + private readonly int m_RumorIndex; + + public ChangeRumorKeywordPrompt(PlayerBarkeeper barkeeper, int rumorIndex) + { + m_Barkeeper = barkeeper; + m_RumorIndex = rumorIndex; + } + + public override void OnCancel(Mobile from) + { + OnResponse(from, ""); + } + + public override void OnResponse(Mobile from, string text) + { + if (text.Length > 130) + { + text = text[..130]; + } + + m_Barkeeper.EndChangeKeyword(from, m_RumorIndex, text); + } + } + + public class ChangeTipMessagePrompt : Prompt + { + private readonly PlayerBarkeeper m_Barkeeper; + + public ChangeTipMessagePrompt(PlayerBarkeeper barkeeper) => m_Barkeeper = barkeeper; + + public override void OnCancel(Mobile from) + { + OnResponse(from, ""); + } + + public override void OnResponse(Mobile from, string text) + { + if (text.Length > 130) + { + text = text[..130]; + } + + m_Barkeeper.EndChangeTip(from, text); + } + } + + public class BarkeeperRumor + { + public BarkeeperRumor(string message, string keyword) + { + Message = message; + Keyword = keyword; + } + + public string Message { get; set; } + + public string Keyword { get; set; } + + public static BarkeeperRumor Deserialize(IGenericReader reader) + { + if (!reader.ReadBool()) + { + return null; + } + + return new BarkeeperRumor(reader.ReadString(), reader.ReadString()); + } + + public static void Serialize(IGenericWriter writer, BarkeeperRumor rumor) + { + if (rumor == null) + { + writer.Write(false); + } + else + { + writer.Write(true); + writer.Write(rumor.Message); + writer.Write(rumor.Keyword); + } + } + } + + public class ManageBarkeeperEntry : ContextMenuEntry + { + public ManageBarkeeperEntry() : base(6151, 12) + { + } + + public override void OnClick(Mobile from, IEntity target) + { + (target as PlayerBarkeeper)?.BeginManagement(from); + } + } + + public class PlayerBarkeeper : BaseVendor + { + private readonly List m_SBInfos = new(); + private BaseHouse m_House; + + private Timer _newsTimer; + + public PlayerBarkeeper(Mobile owner, BaseHouse house) : base("the barkeeper") + { + Owner = owner; + House = house; + Rumors = new BarkeeperRumor[3]; + + LoadSBInfo(); + } + + public PlayerBarkeeper(Serial serial) : base(serial) + { + } + + [CommandProperty(AccessLevel.GameMaster)] + public Mobile Owner { get; set; } + + public BaseHouse House + { + get => m_House; + set + { + m_House?.PlayerBarkeepers.Remove(this); + + value?.PlayerBarkeepers.Add(this); + + m_House = value; + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public string TipMessage { get; set; } + + public override bool IsActiveBuyer => false; + public override bool IsActiveSeller => m_SBInfos.Count > 0; + + public override bool DisallowAllMoves => true; + public override bool NoHouseRestrictions => true; + + public BarkeeperRumor[] Rumors { get; private set; } + + public override VendorShoeType ShoeType => Utility.RandomBool() ? VendorShoeType.ThighBoots : VendorShoeType.Boots; + protected override List SBInfos => m_SBInfos; + + public override bool GetGender() => false; + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem(new HalfApron(Utility.RandomBrightHue())); + + var pack = Backpack; + + pack?.Delete(); + } + + public override void InitBody() + { + base.InitBody(); + + if (Body == 0x340 || Body == 0x402) + { + Hue = 0; + } + else + { + Hue = 0x83F4; // hue is not random + } + + var pack = Backpack; + + pack?.Delete(); + } + + public override bool HandlesOnSpeech(Mobile from) => InRange(from, 3) || base.HandlesOnSpeech(from); + + private void ShoutNews_Callback(TownCrierEntry tce) + { + var index = _newsTimer.Index; + if (index >= tce.Lines.Length) + { + _newsTimer.Stop(); + _newsTimer = null; + } + else + { + PublicOverheadMessage(MessageType.Regular, 0x3B2, false, tce.Lines[index]); + } + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + House = null; + } + + public override bool OnBeforeDeath() + { + if (!base.OnBeforeDeath()) + { + return false; + } + + var shoes = FindItemOnLayer(Layer.Shoes); + + if (shoes is Sandals) + { + shoes.Hue = 0; + } + + return true; + } + + public override void OnSpeech(SpeechEventArgs e) + { + base.OnSpeech(e); + + if (!e.Handled && InRange(e.Mobile, 3)) + { + if (_newsTimer == null && e.HasKeyword(0x30)) // *news* + { + var tce = GlobalTownCrierEntryList.Instance.GetRandomEntry(); + + if (tce == null) + { + PublicOverheadMessage(MessageType.Regular, 0x3B2, 1005643); // I have no news at this time. + } + else + { + _newsTimer = Timer.DelayCall( + TimeSpan.FromSeconds(1.0), + TimeSpan.FromSeconds(3.0), + tce.Lines.Length, + () => ShoutNews_Callback(tce) + ); + + PublicOverheadMessage(MessageType.Regular, 0x3B2, 502978); // Some of the latest news! + } + } + + for (var i = 0; i < Rumors.Length; ++i) + { + var rumor = Rumors[i]; + + var keyword = rumor?.Keyword; + + if (keyword == null || (keyword = keyword.Trim()).Length == 0) + { + continue; + } + + if (keyword.InsensitiveEquals(e.Speech)) + { + var message = rumor.Message; + + if (message == null || (message = message.Trim()).Length == 0) + { + continue; + } + + PublicOverheadMessage(MessageType.Regular, 0x3B2, false, message); + } + } + } + } + + public override bool CheckGold(Mobile from, Item dropped) + { + if (dropped is not Gold g) + { + return false; + } + + if (g.Amount > 50) + { + PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + false, + "I cannot accept so large a tip!", + from.NetState + ); + } + else + { + var tip = TipMessage; + + if (tip == null || (tip = tip.Trim()).Length == 0) + { + PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + false, + "It would not be fair of me to take your money and not offer you information in return.", + from.NetState + ); + } + else + { + Direction = GetDirectionTo(from); + PrivateOverheadMessage(MessageType.Regular, 0x3B2, false, tip, from.NetState); + + g.Delete(); + return true; + } + } + + return false; + } + + public bool IsOwner(Mobile from) + { + if (from?.Deleted != false || Deleted) + { + return false; + } + + if (from.AccessLevel > AccessLevel.GameMaster) + { + return true; + } + + return Owner == from; + } + + public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) + { + base.GetContextMenuEntries(from, ref list); + + if (IsOwner(from) && from.InLOS(this)) + { + list.Add(new ManageBarkeeperEntry()); + } + } + + public void BeginManagement(Mobile from) + { + if (!IsOwner(from)) + { + return; + } + + from.SendGump(new BarkeeperGump(from, this)); + } + + public void Dismiss() + { + Delete(); + } + + public void BeginChangeRumor(Mobile from, int index) + { + if (index < 0 || index >= Rumors.Length) + { + return; + } + + from.Prompt = new ChangeRumorMessagePrompt(this, index); + PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + false, + "Say what news you would like me to tell our guests.", + from.NetState + ); + } + + public void EndChangeRumor(Mobile from, int index, string text) + { + if (index < 0 || index >= Rumors.Length) + { + return; + } + + if (Rumors[index] == null) + { + Rumors[index] = new BarkeeperRumor(text, null); + } + else + { + Rumors[index].Message = text; + } + + from.Prompt = new ChangeRumorKeywordPrompt(this, index); + PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + false, + "What keyword should a guest say to me to get this news?", + from.NetState + ); + } + + public void EndChangeKeyword(Mobile from, int index, string text) + { + if (index < 0 || index >= Rumors.Length) + { + return; + } + + if (Rumors[index] == null) + { + Rumors[index] = new BarkeeperRumor(null, text); + } + else + { + Rumors[index].Keyword = text; + } + + PrivateOverheadMessage(MessageType.Regular, 0x3B2, false, "I'll pass on the message.", from.NetState); + } + + public void RemoveRumor(Mobile from, int index) + { + if (index < 0 || index >= Rumors.Length) + { + return; + } + + Rumors[index] = null; + } + + public void BeginChangeTip(Mobile from) + { + from.Prompt = new ChangeTipMessagePrompt(this); + PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + false, + "Say what you want me to tell guests when they give me a good tip.", + from.NetState + ); + } + + public void EndChangeTip(Mobile from, string text) + { + TipMessage = text; + PrivateOverheadMessage( + MessageType.Regular, + 0x3B2, + false, + "I'll say that to anyone who gives me a good tip.", + from.NetState + ); + } + + public void RemoveTip(Mobile from) + { + TipMessage = null; + } + + public void BeginChangeTitle(Mobile from) + { + from.SendGump(new BarkeeperTitleGump(from, this)); + } + + public void EndChangeTitle(Mobile from, string title, bool vendor) + { + Title = title; + + LoadSBInfo(); + } + + public void CancelChangeTitle(Mobile from) + { + from.SendGump(new BarkeeperGump(from, this)); + } + + public void BeginChangeAppearance(Mobile from) + { + from.SendGump(new PlayerVendorCustomizeGump(this, from)); + } + + public void ChangeGender(Mobile from) + { + Female = !Female; + + if (Female) + { + Body = 401; + Name = NameList.RandomName("female"); + + FacialHairItemID = 0; + } + else + { + Body = 400; + Name = NameList.RandomName("male"); + } + } + + public override void InitSBInfo() + { + if (Title is "the waiter" or "the barkeeper" or "the baker" or "the innkeeper" or "the chef") + { + if (m_SBInfos.Count == 0) + { + m_SBInfos.Add(new SBPlayerBarkeeper()); + } + } + else + { + m_SBInfos.Clear(); + } + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(1); // version; + + writer.Write(m_House); + + writer.Write(Owner); + + writer.WriteEncodedInt(Rumors.Length); + + for (var i = 0; i < Rumors.Length; ++i) + { + BarkeeperRumor.Serialize(writer, Rumors[i]); + } + + writer.Write(TipMessage); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 1: + { + House = (BaseHouse)reader.ReadEntity(); + + goto case 0; + } + case 0: + { + Owner = reader.ReadEntity(); + + Rumors = new BarkeeperRumor[reader.ReadEncodedInt()]; + + for (var i = 0; i < Rumors.Length; ++i) + { + Rumors[i] = BarkeeperRumor.Deserialize(reader); + } + + TipMessage = reader.ReadString(); + + break; + } + } + + if (version < 1) + { + Timer.StartTimer(UpgradeFromVersion0); + } + } + + private void UpgradeFromVersion0() + { + House = BaseHouse.FindHouseAt(this); + } + } + + public class BarkeeperTitleGump : StaticGump + { + private static readonly Entry[] _entries = + { + new(1076083, "the alchemist"), + new(1077244, "the animal tamer"), + new(1078440, "the apothecary"), + new(1078441, "the artist"), + new(1078442, "the baker", true), + new(1073298, "the bard"), + new(1076102, "the barkeep", true), + new(1078443, "the beggar"), + new(1073297, "the blacksmith"), + new(1078444, "the bounty hunter"), + new(1078446, "the brigand"), + new(1078447, "the butler"), + new(1060774, "the carpenter"), + new(1078448, "the chef", true), + new(1078449, "the commander"), + new(1078450, "the curator"), + new(1078451, "the drunkard"), + new(1078452, "the farmer"), + new(1078453, "the fisherman"), + new(1078454, "the gambler"), + new(1078455, "the gypsy"), + new(1075996, "the herald"), + new(1076107, "the herbalist"), + new(1078465, "the hermit"), + new(1078466, "the innkeeper", true), + new(1078467, "the jailor"), + new(1078468, "the jester"), + new(1078469, "the librarian"), + new(1073292, "the mage"), + new(1078470, "the mercenary"), + new(1060775, "the merchant"), + new(1078472, "the messenger"), + new(1076093, "the miner"), + new(1078475, "the monk"), + new(1078476, "the noble"), + new(1073290, "the paladin"), + new(1078479, "the peasant"), + new(1078480, "the pirate"), + new(1078481, "the prisoner"), + new(1078482, "the prophet"), + new(1078484, "the ranger"), + new(1078487, "the sage"), + new(1078488, "the sailor"), + new(1078489, "the scholar"), + new(1060773, "the scribe"), + new(1078490, "the sentry"), + new(1060795, "the servant"), + new(1078491, "the shepherd"), + new(1078492, "the soothsayer"), + new(1078493, "the stoic"), + new(1078494, "the storyteller"), + new(1076134, "the tailor"), + new(1076096, "the thief"), + new(1076137, "the tinker"), + new(1076097, "the town crier"), + new(1073291, "the treasure hunter"), + new(1076112, "the waiter", true), + new(1077242, "the warrior"), + new(1078496, "the watchman"), + new(1078495, null) // No Title + }; + + private static int _pageCount = (_entries.Length + 19) / 20; + + private readonly PlayerBarkeeper _barkeeper; + private readonly Mobile _from; + + public override bool Singleton => true; + + protected override void BuildLayout(ref StaticGumpBuilder builder) + { + RenderBackground(ref builder); + + for (var i = 0; i < _pageCount; ++i) + { + RenderPage(ref builder, i); + } + } + + public BarkeeperTitleGump(Mobile from, PlayerBarkeeper barkeeper) : base(0, 0) + { + _from = from; + _barkeeper = barkeeper; + } + + private static void RenderBackground(ref StaticGumpBuilder builder) + { + builder.AddPage(); + + builder.AddBackground(30, 40, 585, 410, 5054); + + builder.AddImage(30, 40, 9251); + builder.AddImage(180, 40, 9251); + builder.AddImage(30, 40, 9253); + builder.AddImage(30, 130, 9253); + builder.AddImage(598, 40, 9255); + builder.AddImage(598, 130, 9255); + builder.AddImage(30, 433, 9257); + builder.AddImage(180, 433, 9257); + builder.AddImage(30, 40, 9250); + builder.AddImage(598, 40, 9252); + builder.AddImage(598, 433, 9258); + builder.AddImage(30, 433, 9256); + + builder.AddItem(30, 40, 6816); + builder.AddItem(30, 125, 6817); + builder.AddItem(30, 233, 6817); + builder.AddItem(30, 341, 6817); + builder.AddItem(580, 40, 6814); + builder.AddItem(588, 125, 6815); + builder.AddItem(588, 233, 6815); + builder.AddItem(588, 341, 6815); + + builder.AddImage(560, 20, 1417); + builder.AddItem(580, 44, 4033); + + builder.AddBackground(183, 25, 280, 30, 5054); + + builder.AddImage(180, 25, 10460); + builder.AddImage(434, 25, 10460); + + builder.AddHtmlLocalized(223, 32, 200, 40, 1078366); // BARKEEP CUSTOMIZATION MENU + builder.AddBackground(243, 433, 150, 30, 5054); + + builder.AddImage(240, 433, 10460); + builder.AddImage(375, 433, 10460); + + builder.AddImage(80, 398, 2151); + builder.AddItem(72, 406, 2543); + + builder.AddHtmlLocalized(110, 412, 180, 25, 1078445); // sells food and drink + } + + private static void RenderPage(ref StaticGumpBuilder builder, int page) + { + var currentPage = page + 1; + builder.AddPage(currentPage); + + if (_pageCount == 3 && currentPage is >= 1 and <= 3) + { + var pageCliloc = currentPage switch + { + 1 => 1078439, // Page 1 of 3 + 2 => 1078464, // Page 2 of 3 + 3 => 1078483, // Page 3 of 3 + }; + + builder.AddHtmlLocalized(430, 70, 180, 25, pageCliloc); + } + else + { + // Page ~1_CUR~ of ~2_MAX~ + builder.AddHtmlLocalized(430, 70, 180, 25, 1153561, $"{currentPage}\t{_pageCount}", 0x7FFF); + } + + for (int count = 0, i = page * 20; count < 20 && i < _entries.Length; ++count, ++i) + { + var entry = _entries[i]; + + var xOffset = Math.DivRem(count, 10, out var yOffset); + + builder.AddButton(80 + xOffset * 260, 100 + yOffset * 30, 4005, 4007, 2 + i); + builder.AddHtmlLocalized( + 120 + xOffset * 260, + 100 + yOffset * 30, + entry.Vendor ? 148 : 180, + 25, + entry.Description, + true + ); + + if (entry.Vendor) + { + builder.AddImage(270 + xOffset * 260, 98 + yOffset * 30, 2151); + builder.AddItem(262 + xOffset * 260, 106 + yOffset * 30, 2543); + } + } + + builder.AddButton(340, 400, 4005, 4007, 0, GumpButtonType.Page, currentPage + 1 % _pageCount); + builder.AddHtmlLocalized(380, 400, 180, 25, 1078456); // More Job Titles + + builder.AddButton(338, 437, 4014, 4016, 1); + builder.AddHtmlLocalized(290, 440, 35, 40, 1005007); // Back + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (_barkeeper.Deleted) + { + return; + } + + var buttonID = info.ButtonID; + + if (buttonID-- <= 0) + { + return; + } + + if (buttonID-- <= 0) + { + _barkeeper.CancelChangeTitle(_from); + return; + } + + if (buttonID < _entries.Length) + { + var entry = _entries[buttonID]; + _barkeeper.EndChangeTitle(_from, entry.Title, entry.Vendor); + } + } + + private class Entry + { + public readonly int Description; + public readonly string Title; + public readonly bool Vendor; + + public Entry(int desc, string title, bool vendor = false) + { + Description = desc; + Title = title; + Vendor = vendor; + } + } + } + + public class BarkeeperGump : Gump + { + private readonly PlayerBarkeeper m_Barkeeper; + private readonly Mobile m_From; + + public override bool Singleton => true; + + public BarkeeperGump(Mobile from, PlayerBarkeeper barkeeper) : base(0, 0) + { + m_From = from; + m_Barkeeper = barkeeper; + + from.CloseGump(); + + RenderBackground(); + RenderCategories(); + RenderMessageManagement(); + RenderDismissConfirmation(); + RenderMessageManagement_Message_AddOrChange(); + RenderMessageManagement_Message_Remove(); + RenderMessageManagement_Tip_AddOrChange(); + RenderMessageManagement_Tip_Remove(); + RenderAppearanceCategories(); + } + + public void RenderBackground() + { + AddPage(0); + + AddBackground(30, 40, 585, 410, 5054); + + AddImage(30, 40, 9251); + AddImage(180, 40, 9251); + AddImage(30, 40, 9253); + AddImage(30, 130, 9253); + AddImage(598, 40, 9255); + AddImage(598, 130, 9255); + AddImage(30, 433, 9257); + AddImage(180, 433, 9257); + AddImage(30, 40, 9250); + AddImage(598, 40, 9252); + AddImage(598, 433, 9258); + AddImage(30, 433, 9256); + + AddItem(30, 40, 6816); + AddItem(30, 125, 6817); + AddItem(30, 233, 6817); + AddItem(30, 341, 6817); + AddItem(580, 40, 6814); + AddItem(588, 125, 6815); + AddItem(588, 233, 6815); + AddItem(588, 341, 6815); + + AddBackground(183, 25, 280, 30, 5054); + + AddImage(180, 25, 10460); + AddImage(434, 25, 10460); + AddImage(560, 20, 1417); + + AddHtml(223, 32, 200, 40, "BARKEEP CUSTOMIZATION MENU"); + AddBackground(243, 433, 150, 30, 5054); + + AddImage(240, 433, 10460); + AddImage(375, 433, 10460); + } + + public void RenderCategories() + { + AddPage(1); + + AddButton(130, 120, 4005, 4007, 0, GumpButtonType.Page, 2); + AddHtml(170, 120, 200, 40, "Message Control"); + + AddButton(130, 200, 4005, 4007, 0, GumpButtonType.Page, 8); + AddHtml(170, 200, 200, 40, "Customize your barkeep"); + + AddButton(130, 280, 4005, 4007, 0, GumpButtonType.Page, 3); + AddHtml(170, 280, 200, 40, "Dismiss your barkeep"); + + AddButton(338, 437, 4014, 4016, 0); + AddHtml(290, 440, 35, 40, "Back"); + + AddItem(574, 43, 5360); + } + + public void RenderMessageManagement() + { + AddPage(2); + + AddButton(130, 120, 4005, 4007, 0, GumpButtonType.Page, 4); + AddHtml(170, 120, 380, 20, "Add or change a message and keyword"); + + AddButton(130, 200, 4005, 4007, 0, GumpButtonType.Page, 5); + AddHtml(170, 200, 380, 20, "Remove a message and keyword from your barkeep"); + + AddButton(130, 280, 4005, 4007, 0, GumpButtonType.Page, 6); + AddHtml(170, 280, 380, 20, "Add or change your barkeeper's tip message"); + + AddButton(130, 360, 4005, 4007, 0, GumpButtonType.Page, 7); + AddHtml(170, 360, 380, 20, "Delete your barkeepers tip message"); + + AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 1); + AddHtml(290, 440, 35, 40, "Back"); + + AddItem(580, 46, 4030); + } + + public void RenderDismissConfirmation() + { + AddPage(3); + + AddHtml(170, 160, 380, 20, "Are you sure you want to dismiss your barkeeper?"); + + AddButton(205, 280, 4005, 4007, GetButtonID(0, 0)); + AddHtml(240, 280, 100, 20, @"Yes"); + + AddButton(395, 280, 4005, 4007, 0); + AddHtml(430, 280, 100, 20, "No"); + + AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 1); + AddHtml(290, 440, 35, 40, "Back"); + + AddItem(574, 43, 5360); + AddItem(584, 34, 6579); + } + + public void RenderMessageManagement_Message_AddOrChange() + { + AddPage(4); + + AddHtml(250, 60, 500, 25, "Add or change a message"); + + var rumors = m_Barkeeper.Rumors; + + for (var i = 0; i < rumors.Length; ++i) + { + var rumor = rumors[i]; + + AddHtml(100, 70 + i * 120, 50, 20, "Message"); + AddHtml(100, 90 + i * 120, 450, 40, rumor == null ? "No current message" : rumor.Message, true); + AddHtml(100, 130 + i * 120, 50, 20, "Keyword"); + AddHtml(100, 150 + i * 120, 450, 40, rumor == null ? "None" : rumor.Keyword, true); + + AddButton(60, 90 + i * 120, 4005, 4007, GetButtonID(1, i)); + } + + AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 2); + AddHtml(290, 440, 35, 40, "Back"); + + AddItem(580, 46, 4030); + } + + public void RenderMessageManagement_Message_Remove() + { + AddPage(5); + + AddHtml(190, 60, 500, 25, "Choose the message you would like to remove"); + + var rumors = m_Barkeeper.Rumors; + + for (var i = 0; i < rumors.Length; ++i) + { + var rumor = rumors[i]; + + AddHtml(100, 70 + i * 120, 50, 20, "Message"); + AddHtml(100, 90 + i * 120, 450, 40, rumor == null ? "No current message" : rumor.Message, true); + AddHtml(100, 130 + i * 120, 50, 20, "Keyword"); + AddHtml(100, 150 + i * 120, 450, 40, rumor == null ? "None" : rumor.Keyword, true); + + AddButton(60, 90 + i * 120, 4005, 4007, GetButtonID(2, i)); + } + + AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 2); + AddHtml(290, 440, 35, 40, "Back"); + + AddItem(580, 46, 4030); + } + + private int GetButtonID(int type, int index) => 1 + index * 6 + type; + + private void RenderMessageManagement_Tip_AddOrChange() + { + AddPage(6); + + AddHtml(250, 95, 500, 20, "Change this tip message"); + AddHtml(100, 190, 50, 20, "Message"); + AddHtml(100, 210, 450, 40, m_Barkeeper.TipMessage ?? "No current message", true); + + AddButton(60, 210, 4005, 4007, GetButtonID(3, 0)); + + AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 2); + AddHtml(290, 440, 35, 40, "Back"); + + AddItem(580, 46, 4030); + } + + private void RenderMessageManagement_Tip_Remove() + { + AddPage(7); + + AddHtml(250, 95, 500, 20, "Remove this tip message"); + AddHtml(100, 190, 50, 20, "Message"); + AddHtml(100, 210, 450, 40, m_Barkeeper.TipMessage ?? "No current message", true); + + AddButton(60, 210, 4005, 4007, GetButtonID(4, 0)); + + AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 2); + AddHtml(290, 440, 35, 40, "Back"); + + AddItem(580, 46, 4030); + } + + private void RenderAppearanceCategories() + { + AddPage(8); + + AddButton(130, 120, 4005, 4007, GetButtonID(5, 0)); + AddHtml(170, 120, 120, 20, "Title"); + + if (m_Barkeeper.Body != 0x340 && m_Barkeeper.Body != 0x402) + { + AddButton(130, 200, 4005, 4007, GetButtonID(5, 1)); + AddHtml(170, 200, 120, 20, "Appearance"); + + AddButton(130, 280, 4005, 4007, GetButtonID(5, 2)); + AddHtml(170, 280, 120, 20, "Male / Female"); + + AddButton(338, 437, 4014, 4016, 0, GumpButtonType.Page, 1); + AddHtml(290, 440, 35, 40, "Back"); + } + + AddItem(580, 44, 4033); + } + + public override void OnResponse(NetState state, in RelayInfo info) + { + if (!m_Barkeeper.IsOwner(m_From)) + { + return; + } + + var index = info.ButtonID - 1; + + if (index < 0) + { + return; + } + + var type = index % 6; + index /= 6; + + switch (type) + { + case 0: // Controls + { + switch (index) + { + case 0: // Dismiss + { + m_Barkeeper.Dismiss(); + break; + } + } + + break; + } + case 1: // Change message + { + m_Barkeeper.BeginChangeRumor(m_From, index); + break; + } + case 2: // Remove message + { + m_Barkeeper.RemoveRumor(m_From, index); + break; + } + case 3: // Change tip + { + m_Barkeeper.BeginChangeTip(m_From); + break; + } + case 4: // Remove tip + { + m_Barkeeper.RemoveTip(m_From); + break; + } + case 5: // Appearance category selection + { + switch (index) + { + case 0: + m_Barkeeper.BeginChangeTitle(m_From); + break; + case 1: + m_Barkeeper.BeginChangeAppearance(m_From); + break; + case 2: + m_Barkeeper.ChangeGender(m_From); + break; + } + + break; + } + } + } + } +} diff --git a/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs b/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs index 9b8f5a2f3..2721fadbd 100644 --- a/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs @@ -9,7 +9,6 @@ using Server.Items; using Server.Misc; using Server.Multis; using Server.Prompts; -using Server.Systems.FeatureFlags; using Server.Targeting; namespace Server.Mobiles; @@ -22,20 +21,9 @@ public class PlayerVendorTargetAttribute : Attribute; * Next, uncomment the MigrateFrom function and change the `V3Content` type to match the serialization version * before it was bumped. Then run publish.cmd to generate the migration file. */ -[SerializationGenerator(4, false)] +[SerializationGenerator(3, false)] public partial class PlayerVendor : Mobile { - private void MigrateFrom(V3Content content) - { - _shopName = content.ShopName; - _nextPayTime = content.NextPayTime; - _house = content.House; - _owner = content.Owner; - _bankAccount = content.BankAccount; - _holdGold = content.HoldGold; - _sellItems = content.SellItems; - } - private Timer _payTimer; [InvalidateProperties] @@ -43,7 +31,7 @@ public partial class PlayerVendor : Mobile [SerializedCommandProperty(AccessLevel.GameMaster)] private string _shopName; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(1, setter: "private")] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _nextPayTime; @@ -105,13 +93,18 @@ public partial class PlayerVendor : Mobile public PlayerVendorPlaceholder Placeholder { get; set; } - [SerializableField(2, fieldChanged: nameof(OnHouseChanged))] - private BaseHouse _house; - - private void OnHouseChanged(BaseHouse oldValue, BaseHouse newValue) + [SerializableProperty(2)] + public BaseHouse House { - oldValue?.PlayerVendors.Remove(this); - newValue?.PlayerVendors.Add(this); + get => _house; + set + { + _house?.PlayerVendors.Remove(this); + value?.PlayerVendors.Add(this); + + _house = value; + this.MarkDirty(); + } } public int ChargePerDay @@ -178,7 +171,7 @@ public partial class PlayerVendor : Mobile reader.ReadBool(); // New vendor system? _shopName = reader.ReadString(); _nextPayTime = reader.ReadDeltaTime(); - House = reader.ReadEntity(); + _house = reader.ReadEntity(); _owner = reader.ReadEntity(); _bankAccount = reader.ReadInt(); _holdGold = reader.ReadInt(); @@ -208,34 +201,6 @@ public partial class PlayerVendor : Mobile { NameHue = -1; } - - // Do we have a vendor that may have been orphaned? Let's try to recover them and attach them to their house. - if (_house == null) - { - if (_owner == null) - { - Timer.DelayCall(Delete); // Don't try to dismiss, no owner. - } - Timer.DelayCall(() => - { - var house = BaseHouse.FindHouseAt(this); - - if (house != null) - { - House = house; - } - else if (_owner.AccessLevel == AccessLevel.Player) - { - // If we can't find a house, dismiss the vendor. - Dismiss(_owner); - } - } - ); - } - else - { - _house.PlayerVendors.Add(this); - } } public void InitBody() @@ -262,10 +227,9 @@ public partial class PlayerVendor : Mobile public virtual void InitOutfit() { - AddItem(new FancyShirt(Utility.RandomNeutralHue()) - { - Layer = Layer.InnerTorso - }); + Item item = new FancyShirt(Utility.RandomNeutralHue()); + item.Layer = Layer.InnerTorso; + AddItem(item); AddItem(new LongPants(Utility.RandomNeutralHue())); AddItem(new BodySash(Utility.RandomNeutralHue())); AddItem(new Boots(Utility.RandomNeutralHue())); @@ -710,13 +674,13 @@ public partial class PlayerVendor : Mobile { gumps.Close(); gumps.Close(); - NewPlayerVendorOwnerGump.DisplayTo(to, this); + gumps.Send(new NewPlayerVendorOwnerGump(this)); } else { gumps.Close(); gumps.Close(); - PlayerVendorOwnerGump.DisplayTo(to, this); + gumps.Send(new PlayerVendorOwnerGump(this)); } } @@ -737,12 +701,6 @@ public partial class PlayerVendor : Mobile return; } - if (!ContentFeatureFlags.PlayerVendors && from.AccessLevel < AccessLevel.Administrator) - { - from.SendMessage(0x22, "Player vendor transactions are temporarily disabled."); - return; - } - if (vendor.IsOwner(from)) { vendor.SayTo(from, 503212); // You own this shop, just take what you want. @@ -765,7 +723,7 @@ public partial class PlayerVendor : Mobile } else { - PlayerVendorBuyGump.DisplayTo(from, vendor, vi); + from.SendGump(new PlayerVendorBuyGump(vendor, vi)); } } @@ -1274,9 +1232,9 @@ public partial class PlayerVendor : Mobile return; } - var name = text.AsSpan().Trim(); + var name = text.Trim(); - if (!NameVerification.ValidateVendorName(name)) + if (!NameVerification.Validate(name, 1, 20, true, true, true, 0, NameVerification.Empty)) { m_Vendor.SayTo(from, "That name is unacceptable."); return; @@ -1286,7 +1244,7 @@ public partial class PlayerVendor : Mobile from.SendLocalizedMessage(1062496); // Your vendor has been renamed. - NewPlayerVendorOwnerGump.DisplayTo(from, m_Vendor); + from.SendGump(new NewPlayerVendorOwnerGump(m_Vendor)); } } @@ -1303,9 +1261,9 @@ public partial class PlayerVendor : Mobile return; } - var name = text.AsSpan().Trim(); + var name = text.Trim(); - if (!NameVerification.ValidateVendorName(name)) + if (!NameVerification.Validate(name, 1, 20, true, true, true, 0, NameVerification.Empty)) { m_Vendor.SayTo(from, "That name is unacceptable."); return; @@ -1313,7 +1271,7 @@ public partial class PlayerVendor : Mobile m_Vendor.ShopName = name.FixHtml(); - NewPlayerVendorOwnerGump.DisplayTo(from, m_Vendor); + from.SendGump(new NewPlayerVendorOwnerGump(m_Vendor)); } } } diff --git a/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs b/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs index d7d0a3ab6..64977022a 100644 --- a/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs @@ -46,20 +46,9 @@ public class VendorRentalDuration } } -[SerializationGenerator(1)] +[SerializationGenerator(0)] public partial class RentedVendor : PlayerVendor { - private void MigrateFrom(V0Content content) - { - _rentalDurationId = content.RentalDurationId; - _rentalPrice = content.RentalPrice; - _landlordRenew = content.LandlordRenew; - _renterRenew = content.RenterRenew; - _renewalPrice = content.RenewalPrice; - _rentalGold = content.RentalGold; - _rentalExpireTime = content.RentalExpireTime; - } - private Timer _rentalExpireTimer; public RentedVendor( @@ -104,7 +93,7 @@ public partial class RentedVendor : PlayerVendor [SerializedCommandProperty(AccessLevel.GameMaster)] private int _rentalGold; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(6)] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _rentalExpireTime; @@ -214,13 +203,13 @@ public partial class RentedVendor : PlayerVendor if (vendor.IsOwner(from)) { - RenterVendorRentalGump.DisplayTo(from, vendor); + from.SendGump(new RenterVendorRentalGump(vendor), true); vendor.SendRentalExpireMessage(from); } else if (vendor.IsLandlord(from)) { - LandlordVendorRentalGump.DisplayTo(from, vendor); + from.SendGump(new LandlordVendorRentalGump(vendor), true); vendor.SendRentalExpireMessage(from); } @@ -322,7 +311,7 @@ public partial class RentedVendor : PlayerVendor { from.SendLocalizedMessage(1062504); // Please wait while the renter considers your offer. - VendorRentalRefundGump.DisplayTo(owner, m_Vendor, from, amount); + owner.SendGump(new VendorRentalRefundGump(m_Vendor, from, amount)); } } } diff --git a/Projects/UOContent/Mobiles/Vendors/VendorBackpack.cs b/Projects/UOContent/Mobiles/Vendors/VendorBackpack.cs index 06f0e6b62..267fbcd1a 100644 --- a/Projects/UOContent/Mobiles/Vendors/VendorBackpack.cs +++ b/Projects/UOContent/Mobiles/Vendors/VendorBackpack.cs @@ -12,9 +12,12 @@ namespace Server.Mobiles; [SerializationGenerator(0, false)] public partial class VendorBackpack : Backpack { - public VendorBackpack() => Layer = Layer.Backpack; + public VendorBackpack() + { + Layer = Layer.Backpack; + Weight = 1.0; + } - public override double DefaultWeight => 1.0; public override int DefaultMaxWeight => 0; public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) diff --git a/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs b/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs index 8f0a1fba8..d4d5ce4bd 100644 --- a/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs +++ b/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs @@ -37,7 +37,7 @@ namespace Server.Mobiles Items = reader.ReadEntityList(); Gold = reader.ReadInt(); - ExpireTime = version >= 1 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime(); + ExpireTime = reader.ReadDeltaTime(); if (Items.Count == 0 && Gold == 0) { @@ -88,7 +88,7 @@ namespace Server.Mobiles public void Serialize(IGenericWriter writer) { - writer.WriteEncodedInt(1); // version + writer.WriteEncodedInt(0); // version writer.Write(Owner); writer.Write(VendorName); @@ -98,7 +98,7 @@ namespace Server.Mobiles writer.Write(Items); writer.Write(Gold); - writer.WriteAnchoredTime(ExpireTime); + writer.WriteDeltaTime(ExpireTime); } private class ExpireTimer : Timer diff --git a/Projects/UOContent/Mobiles/Vendors/VendorItem.cs b/Projects/UOContent/Mobiles/Vendors/VendorItem.cs index 99d1446e6..14af3656a 100644 --- a/Projects/UOContent/Mobiles/Vendors/VendorItem.cs +++ b/Projects/UOContent/Mobiles/Vendors/VendorItem.cs @@ -32,20 +32,18 @@ public partial class VendorItem public string FormattedPrice => Core.ML ? Price.ToString("N0", CultureInfo.GetCultureInfo("en-US")) : Price.ToString(); - [SerializableField(2, fieldChanged: nameof(OnDescriptionChanged), allowFieldChange: nameof(AllowDescriptionChange))] - private string _description; - - private bool AllowDescriptionChange(ref string value) + [SerializableProperty(2)] + public string Description { - value = value ?? ""; - return true; - } - - private void OnDescriptionChanged(string oldValue, string newValue) - { - if (Valid) + get => _description; + set { - Item.InvalidateProperties(); + _description = value ?? ""; + + if (Valid) + { + Item.InvalidateProperties(); + } } } diff --git a/Projects/UOContent/Multis/Boats/BaseBoat.cs b/Projects/UOContent/Multis/Boats/BaseBoat.cs index ed618b91a..9e367b455 100644 --- a/Projects/UOContent/Multis/Boats/BaseBoat.cs +++ b/Projects/UOContent/Multis/Boats/BaseBoat.cs @@ -19,24 +19,9 @@ namespace Server.Multis Single } - [SerializationGenerator(5, false)] + [SerializationGenerator(4, false)] public abstract partial class BaseBoat : BaseMulti { - private void MigrateFrom(V4Content content) - { - _mapItem = content.MapItem; - _nextNavPoint = content.NextNavPoint; - _facing = content.Facing; - _timeOfDecay = content.TimeOfDecay; - _owner = content.Owner; - _pPlank = content.PPlank; - _sPlank = content.SPlank; - _tillerMan = content.TillerMan; - _hold = content.Hold; - _anchored = content.Anchored; - _shipName = content.ShipName; - } - public enum DryDockResult { Valid, @@ -151,23 +136,31 @@ namespace Server.Multis } } - [SerializableField(3, fieldChanged: nameof(OnTimeOfDecayChanged))] - [AnchoredDateTime] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private DateTime _timeOfDecay; - - private void OnTimeOfDecayChanged(DateTime oldValue, DateTime newValue) + [DeltaDateTime] + [SerializableProperty(3)] + [CommandProperty(AccessLevel.GameMaster)] + public DateTime TimeOfDecay { - TillerMan?.InvalidateProperties(); + get => _timeOfDecay; + set + { + _timeOfDecay = value; + TillerMan?.InvalidateProperties(); + this.MarkDirty(); + } } - [SerializableField(10, fieldChanged: nameof(OnShipNameChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private string _shipName; - - private void OnShipNameChanged(string oldValue, string newValue) + [SerializableProperty(10)] + [CommandProperty(AccessLevel.GameMaster)] + public string ShipName { - TillerMan?.InvalidateProperties(); + get => _shipName; + set + { + _shipName = value; + TillerMan?.InvalidateProperties(); + this.MarkDirty(); + } } [CommandProperty(AccessLevel.GameMaster)] @@ -182,16 +175,40 @@ namespace Server.Multis [CommandProperty(AccessLevel.GameMaster)] public BoatOrder Order { get; set; } - public int Status => - (Core.Now - (TimeOfDecay - BoatDecayDelay)) switch + public int Status + { + get { - var start when start < TimeSpan.FromHours(1) => 1043010, // This structure is like new. - var start when start < TimeSpan.FromDays(2) => 1043011, // This structure is slightly worn. - var start when start < TimeSpan.FromDays(3) => 1043012, // This structure is somewhat worn. - var start when start < TimeSpan.FromDays(4) => 1043013, // This structure is fairly worn. - var start when start < TimeSpan.FromDays(5) => 1043014, // This structure is greatly worn. - _ => 1043015 // This structure is in danger of collapsing. - }; + var start = Core.Now - TimeOfDecay - BoatDecayDelay; + + if (start < TimeSpan.FromHours(1.0)) + { + return 1043010; // This structure is like new. + } + + if (start < TimeSpan.FromDays(2.0)) + { + return 1043011; // This structure is slightly worn. + } + + if (start < TimeSpan.FromDays(3.0)) + { + return 1043012; // This structure is somewhat worn. + } + + if (start < TimeSpan.FromDays(4.0)) + { + return 1043013; // This structure is fairly worn. + } + + if (start < TimeSpan.FromDays(5.0)) + { + return 1043014; // This structure is greatly worn. + } + + return 1043015; // This structure is in danger of collapsing. + } + } public virtual int NorthID => 0; public virtual int EastID => 0; @@ -378,27 +395,52 @@ namespace Server.Multis public override void OnLocationChange(Point3D old) { - base.OnLocationChange(old); + if (TillerMan != null) + { + TillerMan.Location = new Point3D( + X + (TillerMan.X - old.X), + Y + (TillerMan.Y - old.Y), + Z + (TillerMan.Z - old.Z) + ); + } - TillerMan?.Location = new Point3D( - X + (TillerMan.X - old.X), - Y + (TillerMan.Y - old.Y), - Z + (TillerMan.Z - old.Z) - ); + if (Hold != null) + { + Hold.Location = new Point3D(X + (Hold.X - old.X), Y + (Hold.Y - old.Y), Z + (Hold.Z - old.Z)); + } - Hold?.Location = new Point3D(X + (Hold.X - old.X), Y + (Hold.Y - old.Y), Z + (Hold.Z - old.Z)); - PPlank?.Location = new Point3D(X + (PPlank.X - old.X), Y + (PPlank.Y - old.Y), Z + (PPlank.Z - old.Z)); - SPlank?.Location = new Point3D(X + (SPlank.X - old.X), Y + (SPlank.Y - old.Y), Z + (SPlank.Z - old.Z)); + if (PPlank != null) + { + PPlank.Location = new Point3D(X + (PPlank.X - old.X), Y + (PPlank.Y - old.Y), Z + (PPlank.Z - old.Z)); + } + + if (SPlank != null) + { + SPlank.Location = new Point3D(X + (SPlank.X - old.X), Y + (SPlank.Y - old.Y), Z + (SPlank.Z - old.Z)); + } } public override void OnMapChange() { - base.OnMapChange(); + if (TillerMan != null) + { + TillerMan.Map = Map; + } - TillerMan?.Map = Map; - Hold?.Map = Map; - PPlank?.Map = Map; - SPlank?.Map = Map; + if (Hold != null) + { + Hold.Map = Map; + } + + if (PPlank != null) + { + PPlank.Map = Map; + } + + if (SPlank != null) + { + SPlank.Map = Map; + } } public bool CanCommand(Mobile m) => true; @@ -1354,7 +1396,7 @@ namespace Server.Multis } // Changing the interval or flipping between commands: "Forward" and "Forward One" - var delay = TimeSpan.Zero; + TimeSpan delay = TimeSpan.Zero; if (interval.TotalMilliseconds > Math.Abs(moverTimerNext)) { var addedDelay = (int)(interval.TotalMilliseconds - Math.Abs(moverTimerNext)); @@ -1771,7 +1813,7 @@ namespace Server.Multis list.Add(e); } - var moveBoatPacket = stackalloc byte[BoatPackets.GetMoveBoatHSPacketLength(list.Count)] + Span moveBoatPacket = stackalloc byte[BoatPackets.GetMoveBoatHSPacketLength(list.Count)] .InitializePacket(); // Packet must be sent before actual locations are changed diff --git a/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs b/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs index 961b4898c..6838ec755 100644 --- a/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs +++ b/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs @@ -1,7 +1,6 @@ using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Regions; -using Server.Systems.FeatureFlags; using Server.Targeting; namespace Server.Multis; @@ -19,6 +18,8 @@ public abstract partial class BaseBoatDeed : Item public BaseBoatDeed(int id, Point3D offset) : base(0x14F2) { + Weight = 1.0; + if (!Core.AOS) { LootType = LootType.Newbied; @@ -28,8 +29,6 @@ public abstract partial class BaseBoatDeed : Item Offset = offset; } - public override double DefaultWeight => 1.0; - public abstract BaseBoat Boat { get; } public override void OnDoubleClick(Mobile from) @@ -67,68 +66,64 @@ public abstract partial class BaseBoatDeed : Item if (!IsChildOf(from.Backpack)) { from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. - return; - } - - var map = from.Map; - - if (map == null) - { - return; - } - - if (!ContentFeatureFlags.BoatPlacement && from.AccessLevel < FeatureFlagSettings.RequiredAccessLevel) - { - from.SendMessage(0x22, "Boat placement is temporarily disabled."); - } - - if (from.AccessLevel < AccessLevel.GameMaster && (map == Map.Ilshenar || map == Map.Malas)) - { - from.SendLocalizedMessage(1043284); // A ship can not be created here. - return; - } - - if (from.Region.IsPartOf() || BaseBoat.FindBoatAt(from.Location, from.Map) != null) - { - // You may not place a ship while on another ship or inside a house. - from.SendLocalizedMessage(1010568, null, 0x25); - return; - } - - var boat = Boat; - - if (boat == null) - { - return; - } - - p = new Point3D(p.X - Offset.X, p.Y - Offset.Y, p.Z - Offset.Z); - - if (BaseBoat.IsValidLocation(p, map) && boat.CanFit(p, map, boat.ItemID)) - { - Delete(); - - boat.Owner = from; - boat.Anchored = true; - - var keyValue = boat.CreateKeys(from); - - if (boat.PPlank != null) - { - boat.PPlank.KeyValue = keyValue; - } - - if (boat.SPlank != null) - { - boat.SPlank.KeyValue = keyValue; - } - - boat.MoveToWorld(p, map); } else { - boat.Delete(); - from.SendLocalizedMessage(1043284); // A ship can not be created here. + var map = from.Map; + + if (map == null) + { + return; + } + + if (from.AccessLevel < AccessLevel.GameMaster && (map == Map.Ilshenar || map == Map.Malas)) + { + from.SendLocalizedMessage(1043284); // A ship can not be created here. + return; + } + + if (from.Region.IsPartOf() || BaseBoat.FindBoatAt(from.Location, from.Map) != null) + { + // You may not place a ship while on another ship or inside a house. + from.SendLocalizedMessage(1010568, null, 0x25); + return; + } + + var boat = Boat; + + if (boat == null) + { + return; + } + + p = new Point3D(p.X - Offset.X, p.Y - Offset.Y, p.Z - Offset.Z); + + if (BaseBoat.IsValidLocation(p, map) && boat.CanFit(p, map, boat.ItemID)) + { + Delete(); + + boat.Owner = from; + boat.Anchored = true; + + var keyValue = boat.CreateKeys(from); + + if (boat.PPlank != null) + { + boat.PPlank.KeyValue = keyValue; + } + + if (boat.SPlank != null) + { + boat.SPlank.KeyValue = keyValue; + } + + boat.MoveToWorld(p, map); + } + else + { + boat.Delete(); + from.SendLocalizedMessage(1043284); // A ship can not be created here. + } } } diff --git a/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs b/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs index 07d1c7ac5..7b638801a 100644 --- a/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs +++ b/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs @@ -1,7 +1,6 @@ using ModernUO.Serialization; using Server.Engines.CannedEvil; using Server.Regions; -using Server.Systems.FeatureFlags; using Server.Targeting; namespace Server.Multis; @@ -24,6 +23,7 @@ public abstract partial class BaseDockedBoat : Item public BaseDockedBoat(int id, Point3D offset, BaseBoat boat) : base(0x14F4) { + Weight = 1.0; LootType = LootType.Blessed; _multiId = id; @@ -32,8 +32,6 @@ public abstract partial class BaseDockedBoat : Item _shipName = boat.ShipName; } - public override double DefaultWeight => 1.0; - public abstract BaseBoat Boat { get; } public override void OnDoubleClick(Mobile from) @@ -84,58 +82,53 @@ public abstract partial class BaseDockedBoat : Item if (!IsChildOf(from.Backpack)) { from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. - return; - } - - var map = from.Map; - - if (map == null) - { - return; - } - - var boat = Boat; - - if (boat == null) - { - return; - } - - if (!ContentFeatureFlags.BoatPlacement && from.AccessLevel < FeatureFlagSettings.RequiredAccessLevel) - { - from.SendMessage(0x22, "Boat placement is temporarily disabled."); - return; - } - - p = new Point3D(p.X - Offset.X, p.Y - Offset.Y, p.Z - Offset.Z); - - if (BaseBoat.IsValidLocation(p, map) && boat.CanFit(p, map, boat.ItemID) && map != Map.Ilshenar && - map != Map.Malas) - { - Delete(); - - boat.Owner = from; - boat.Anchored = true; - boat.ShipName = _shipName; - - var keyValue = boat.CreateKeys(from); - - if (boat.PPlank != null) - { - boat.PPlank.KeyValue = keyValue; - } - - if (boat.SPlank != null) - { - boat.SPlank.KeyValue = keyValue; - } - - boat.MoveToWorld(p, map); } else { - boat.Delete(); - from.SendLocalizedMessage(1043284); // A ship can not be created here. + var map = from.Map; + + if (map == null) + { + return; + } + + var boat = Boat; + + if (boat == null) + { + return; + } + + p = new Point3D(p.X - Offset.X, p.Y - Offset.Y, p.Z - Offset.Z); + + if (BaseBoat.IsValidLocation(p, map) && boat.CanFit(p, map, boat.ItemID) && map != Map.Ilshenar && + map != Map.Malas) + { + Delete(); + + boat.Owner = from; + boat.Anchored = true; + boat.ShipName = _shipName; + + var keyValue = boat.CreateKeys(from); + + if (boat.PPlank != null) + { + boat.PPlank.KeyValue = keyValue; + } + + if (boat.SPlank != null) + { + boat.SPlank.KeyValue = keyValue; + } + + boat.MoveToWorld(p, map); + } + else + { + boat.Delete(); + from.SendLocalizedMessage(1043284); // A ship can not be created here. + } } } @@ -152,7 +145,7 @@ public abstract partial class BaseDockedBoat : Item return; } - var p = ip switch + Point3D p = ip switch { Item item => item.GetWorldTop(), Mobile m => m.Location, diff --git a/Projects/UOContent/Multis/Boats/BoatPackets.cs b/Projects/UOContent/Multis/Boats/BoatPackets.cs index d75f32375..c5512f225 100644 --- a/Projects/UOContent/Multis/Boats/BoatPackets.cs +++ b/Projects/UOContent/Multis/Boats/BoatPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: BoatPackets.cs * * * @@ -74,7 +74,7 @@ public static class BoatPackets return; } - var moveBoatPacket = stackalloc byte[GetMoveBoatHSPacketLength(entities.Count)] + Span moveBoatPacket = stackalloc byte[GetMoveBoatHSPacketLength(entities.Count)] .InitializePacket(); CreateMoveBoatHS(moveBoatPacket, boat, entities, d, speed, xOffset, yOffset); @@ -113,7 +113,7 @@ public static class BoatPackets continue; } - var buffer = builder.GetSpan(OutgoingEntityPackets.MaxWorldEntityPacketLength).InitializePacket(); + Span buffer = builder.GetSpan(OutgoingEntityPackets.MaxWorldEntityPacketLength).InitializePacket(); var bytesWritten = OutgoingEntityPackets.CreateWorldEntity(buffer, entity, true); builder.Advance(bytesWritten); } diff --git a/Projects/UOContent/Multis/Camps/BaseCamp.cs b/Projects/UOContent/Multis/Camps/BaseCamp.cs index e60e3e46e..cd0354859 100644 --- a/Projects/UOContent/Multis/Camps/BaseCamp.cs +++ b/Projects/UOContent/Multis/Camps/BaseCamp.cs @@ -6,16 +6,9 @@ using Server.Mobiles; namespace Server.Multis; -[SerializationGenerator(2, false)] +[SerializationGenerator(1, false)] public abstract partial class BaseCamp : BaseMulti { - private void MigrateFrom(V1Content content) - { - _items = content.Items; - _mobiles = content.Mobiles; - _decayTime = content.DecayTime; - } - [Tidy] [SerializableField(0, setter: "private")] private List _items; @@ -24,13 +17,12 @@ public abstract partial class BaseCamp : BaseMulti [SerializableField(1, setter: "private")] private List _mobiles; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(2, setter: "private")] private DateTime _decayTime; private TimeSpan _decayDelay; private Timer _decayTimer; - private Timer _initTimer; public BaseCamp(int multiID) : base(multiID) { @@ -39,7 +31,7 @@ public abstract partial class BaseCamp : BaseMulti _decayDelay = TimeSpan.FromMinutes(30.0); RefreshDecay(true); - _initTimer = Timer.DelayCall(TimeSpan.Zero, CheckAddComponents); + Timer.StartTimer(CheckAddComponents); } public virtual int EventRange => 10; @@ -58,8 +50,6 @@ public abstract partial class BaseCamp : BaseMulti public void CheckAddComponents() { - _initTimer = null; - if (Deleted) { return; @@ -148,16 +138,16 @@ public abstract partial class BaseCamp : BaseMulti for (var i = 0; i < _items.Count; ++i) { - _items[i]?.Delete(); + _items[i].Delete(); } for (var i = 0; i < _mobiles.Count; ++i) { var mob = _mobiles[i]; - if (mob != null && (mob.CantWalk || (mob as BaseCreature)?.IsPrisoner == false)) + if (mob.CantWalk || (mob as BaseCreature)?.IsPrisoner == false) { - mob.Delete(); + _mobiles[i].Delete(); } } @@ -166,9 +156,6 @@ public abstract partial class BaseCamp : BaseMulti _decayTimer?.Stop(); _decayTimer = null; - - _initTimer?.Stop(); - _initTimer = null; } private void Deserialize(IGenericReader reader, int version) @@ -181,20 +168,7 @@ public abstract partial class BaseCamp : BaseMulti [AfterDeserialization] private void AfterDeserialization() { - var remaining = _decayTime - Core.Now; - - if (remaining > TimeSpan.Zero) - { - _decayDelay = remaining; - RefreshDecay(false); - } - else - { - Timer.DelayCall(TimeSpan.Zero, Delete); - return; - } - - _initTimer = Timer.DelayCall(TimeSpan.Zero, CheckAddComponents); + RefreshDecay(false); } } @@ -202,9 +176,5 @@ public abstract partial class BaseCamp : BaseMulti public partial class LockableBarrel : LockableContainer { [Constructible] - public LockableBarrel() : base(0xE77) - { - } - - public override double DefaultWeight => 1.0; + public LockableBarrel() : base(0xE77) => Weight = 1.0; } diff --git a/Projects/UOContent/Multis/ComponentVerification.cs b/Projects/UOContent/Multis/ComponentVerification.cs index dda094aa4..f0fd40d08 100644 --- a/Projects/UOContent/Multis/ComponentVerification.cs +++ b/Projects/UOContent/Multis/ComponentVerification.cs @@ -1,470 +1,315 @@ using System; -using System.Buffers; using System.Collections.Generic; using System.IO; -using System.IO.Compression; -using Server.Compression; -using Server.Logging; -namespace Server.Multis; - -public static class ComponentVerification +namespace Server.Multis { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ComponentVerification)); - - private static int[] _itemTable; - private static int[] _multiTable; - private static bool _loaded; - - // housing.bin stores each component's feature mask in the client's feature-flag bit space, which - // tags pre-AOS base pieces (e.g. sandstone) with low bits - notably T2A (0x1) - that HousingFlags - // does not model. CheckValidity validates against HousingFlags, so strip everything except the - // housing-tier bits when loading: base pieces collapse to 0 (always valid, exactly as walls.txt - // encodes them) while AOS/SE/ML/... line up unchanged. - private const int HousingTierMask = (int)HousingFlags.HousingEJ; - - // Table sentinels. Slots start as NotAComponent, which CheckValidity rejects, so a piece that - // never gets registered can never be placed. NoFeatureRequired is a piece with no expansion - // requirement: walls.txt encodes pre-AOS base pieces as 0, housing.bin collapses to 0 under - // HousingTierMask. - private const int NotAComponent = -1; - private const int NoFeatureRequired = 0; - - private const string FeatureMaskColumn = "FeatureMask"; - - public static bool IsItemValid(int itemID) + public class ComponentVerification { - EnsureLoaded(); - return itemID > 0 && itemID < _itemTable.Length && CheckValidity(_itemTable[itemID]); - } + private readonly int[] m_ItemTable; + private readonly int[] m_MultiTable; - public static bool IsMultiValid(int multiID) - { - EnsureLoaded(); - return multiID > 0 && multiID < _multiTable.Length && CheckValidity(_multiTable[multiID]); - } - - private static bool CheckValidity(int val) => - val != NotAComponent && - (val == NoFeatureRequired || ((int)ExpansionInfo.CoreExpansion.HousingFlags & val) != 0); - - private static void EnsureLoaded() - { - if (_loaded) + public ComponentVerification() { - return; + m_ItemTable = CreateTable(TileData.MaxItemValue); + m_MultiTable = CreateTable(0x4000); + + LoadItems( + "Data/Components/walls.txt", + "South1", + "South2", + "South3", + "Corner", + "East1", + "East2", + "East3", + "Post", + "WindowS", + "AltWindowS", + "WindowE", + "AltWindowE", + "SecondAltWindowS", + "SecondAltWindowE" + ); + LoadItems( + "Data/Components/teleprts.txt", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "F16" + ); + LoadItems( + "Data/Components/stairs.txt", + "Block", + "North", + "East", + "South", + "West", + "Squared1", + "Squared2", + "Rounded1", + "Rounded2" + ); + LoadItems( + "Data/Components/roof.txt", + "North", + "East", + "South", + "West", + "NSCrosspiece", + "EWCrosspiece", + "NDent", + "EDent", + "SDent", + "WDent", + "NTPiece", + "ETPiece", + "STPiece", + "WTPiece", + "XPiece", + "Extra Piece" + ); + LoadItems( + "Data/Components/floors.txt", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "F16" + ); + LoadItems( + "Data/Components/misc.txt", + "Piece1", + "Piece2", + "Piece3", + "Piece4", + "Piece5", + "Piece6", + "Piece7", + "Piece8" + ); + LoadItems( + "Data/Components/doors.txt", + "Piece1", + "Piece2", + "Piece3", + "Piece4", + "Piece5", + "Piece6", + "Piece7", + "Piece8" + ); + + LoadMultis("Data/Components/stairs.txt", "MultiNorth", "MultiEast", "MultiSouth", "MultiWest"); } - // Set before loading: this runs from the design packet handler, so a bad file must not - // re-read every sheet on each later placement attempt. Sheets below fail independently. - _loaded = true; + public bool IsItemValid(int itemID) => + itemID > 0 && itemID < m_ItemTable.Length && CheckValidity(m_ItemTable[itemID]); - _itemTable = CreateTable(TileData.MaxItemValue); - _multiTable = CreateTable(0x4000); + public bool IsMultiValid(int multiID) => + multiID > 0 && multiID < m_MultiTable.Length && CheckValidity(m_MultiTable[multiID]); - var housingPath = MultiData.HousingUOPPath; - if (housingPath != null && TryLoadFromHousingBin(housingPath)) + public bool CheckValidity(int val) => + val != -1 && (val == 0 || ((int)ExpansionInfo.CoreExpansion.HousingFlags & val) != 0); + + private int[] CreateTable(int length) { - return; - } + var table = new int[length]; - LoadFromTxtFiles(); - } - - private static bool TryLoadFromHousingBin(string path) - { - try - { - var data = ReadUOPEntry(path, MultiData.HousingEntry); - if (data != null) + for (var i = 0; i < table.Length; ++i) { - LoadFromHousingBin(data); - return true; + table[i] = -1; } - logger.Warning( - "Could not decompress housing.bin from {Path}. Falling back to the component sheets", - path - ); - } - catch (Exception ex) - { - logger.Warning( - ex, - "Failed to read housing.bin from {Path}. Falling back to the component sheets", - path - ); + return table; } - return false; - } - - private static byte[] ReadUOPEntry(string path, UOPEntry entry) - { - using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); - stream.Seek(entry.Offset, SeekOrigin.Begin); - - if (entry.Compressed) + private void LoadItems(string path, params string[] itemColumns) { - var compressedData = new byte[entry.CompressedSize]; - stream.ReadExactly(compressedData); - var decompressedData = new byte[entry.Size]; + LoadSpreadsheet(m_ItemTable, path, itemColumns); + } - if (Deflate.Standard.Unpack(decompressedData, compressedData, out var bytesDecompressed) != LibDeflateResult.Success - || entry.Size != bytesDecompressed) + private void LoadMultis(string path, params string[] multiColumns) + { + LoadSpreadsheet(m_MultiTable, path, multiColumns); + } + + private void LoadSpreadsheet(int[] table, string path, params string[] tileColumns) + { + var ss = new Spreadsheet(path); + + var tileCIDs = new int[tileColumns.Length]; + + for (var i = 0; i < tileColumns.Length; ++i) { - return null; + tileCIDs[i] = ss.GetColumnID(tileColumns[i]); } - return decompressedData; - } + var featureCID = ss.GetColumnID("FeatureMask"); - var data = new byte[entry.Size]; - stream.ReadExactly(data); - return data; - } - - private static void LoadFromHousingBin(byte[] data) - { - var reader = new SpanReader(data); - - var fileTypesCount = (int)reader.ReadUInt32LE(); - - for (var f = 0; f < fileTypesCount; f++) - { - var fileType = (int)reader.ReadUInt32LE(); - var entriesCount = (int)reader.ReadUInt32LE(); - var isWalls = fileType == 5; - var isStairs = fileType == 1; - - for (var e = 0; e < entriesCount; e++) + for (var i = 0; i < ss.Records.Length; ++i) { - reader.ReadUInt32LE(); // category_id - reader.ReadUInt32LE(); // subcategory_id - var featureMask = (int)reader.ReadUInt32LE() & HousingTierMask; - reader.ReadUInt32LE(); // cliloc_id + var record = ss.Records[i]; - // fields_1 - var fieldsCount1 = (int)reader.ReadUInt32LE(); - for (var i = 0; i < fieldsCount1; i++) + var fid = record.GetInt32(featureCID); + + for (var j = 0; j < tileCIDs.Length; ++j) { - reader.ReadUInt32LE(); // direction - var staticId = (int)reader.ReadUInt32LE(); + var itemID = record.GetInt32(tileCIDs[j]); - if (staticId > 0 && staticId < _itemTable.Length) + if (itemID <= 0 || itemID >= table.Length) { - _itemTable[staticId] = featureMask; + continue; } - } - // unknown1 (only for walls, between fields_1 and fields_count_2) - if (isWalls) - { - reader.ReadUInt32LE(); - } - - // fields_2 - var fieldsCount2 = (int)reader.ReadUInt32LE(); - for (var i = 0; i < fieldsCount2; i++) - { - reader.ReadUInt32LE(); // direction - var staticId = (int)reader.ReadUInt32LE(); - - if (isStairs) - { - if (staticId > 0 && staticId < _multiTable.Length) - { - _multiTable[staticId] = featureMask; - } - } - else if (staticId > 0 && staticId < _itemTable.Length) - { - _itemTable[staticId] = featureMask; - } - } - - // unknown2 (only for non-walls, after fields_2) - if (!isWalls) - { - reader.ReadUInt32LE(); + table[itemID] = fid; } } } } - private static void LoadFromTxtFiles() + public class Spreadsheet { - LoadItems( - "walls.txt", - "South1", "South2", "South3", "Corner", - "East1", "East2", "East3", "Post", - "WindowS", "AltWindowS", "WindowE", "AltWindowE", - "SecondAltWindowS", "SecondAltWindowE" - ); - LoadItems( - "teleprts.txt", - "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", - "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16" - ); - LoadItems( - "stairs.txt", - "Block", "North", "East", "South", "West", - "Squared1", "Squared2", "Rounded1", "Rounded2" - ); - LoadItems( - "roof.txt", - "North", "East", "South", "West", - "NSCrosspiece", "EWCrosspiece", - "NDent", "EDent", "SDent", "WDent", - "NTPiece", "ETPiece", "STPiece", "WTPiece", - "XPiece", "Extra Piece" - ); - LoadItems( - "floors.txt", - "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", - "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16" - ); - LoadItems( - "misc.txt", - "Piece1", "Piece2", "Piece3", "Piece4", - "Piece5", "Piece6", "Piece7", "Piece8" - ); - LoadItems( - "doors.txt", - "Piece1", "Piece2", "Piece3", "Piece4", - "Piece5", "Piece6", "Piece7", "Piece8" - ); + private readonly ColumnInfo[] m_Columns; - LoadMultis("stairs.txt", "MultiNorth", "MultiEast", "MultiSouth", "MultiWest"); - } - - private static string ResolveTxtPath(string filename) - { - var clientPath = Core.FindDataFile(filename, false); - if (clientPath != null) + public Spreadsheet(string path) { - return clientPath; - } + using var ip = new StreamReader(path); + var types = ReadLine(ip); + var names = ReadLine(ip); - var bundledPath = Path.Combine("Data", "Components", filename); - return File.Exists(bundledPath) ? bundledPath : null; - } - - private static void LoadItems(string filename, params ReadOnlySpan itemColumns) => - LoadSpreadsheet(_itemTable, filename, itemColumns); - - private static void LoadMultis(string filename, params ReadOnlySpan multiColumns) => - LoadSpreadsheet(_multiTable, filename, multiColumns); - - private static void LoadSpreadsheet(int[] table, string filename, params ReadOnlySpan tileColumns) - { - var path = ResolveTxtPath(filename); - if (path == null) - { - return; - } - - Spreadsheet ss; - - try - { - ss = new Spreadsheet(path); - } - catch (Exception ex) - { - // One unreadable sheet must not take the others down with it. - logger.Error(ex, "Could not read house components from {Path}", path); - return; - } - - // GetInt32 on a missing column yields NoFeatureRequired, which would register every piece in - // the sheet as unconditionally placeable. Refuse the sheet instead. - var featureCID = ss.GetColumnID(FeatureMaskColumn); - if (featureCID < 0) - { - logger.Error( - "House component sheet {Path} has no {Column} column. Its pieces will not be registered", - path, - FeatureMaskColumn - ); - return; - } - - // An individual missing column is expected - older sheets predate walls.txt's - // SecondAltWindowS/E - but a sheet matching none of them is not the sheet we expect. - var tileCIDs = new int[tileColumns.Length]; - var matchedColumns = 0; - - for (var i = 0; i < tileColumns.Length; ++i) - { - tileCIDs[i] = ss.GetColumnID(tileColumns[i]); - - if (tileCIDs[i] >= 0) - { - matchedColumns++; - } - } - - if (matchedColumns == 0) - { - logger.Error( - "House component sheet {Path} has none of its expected tile columns. Its pieces will not be registered", - path - ); - return; - } - - for (var i = 0; i < ss.Records.Length; ++i) - { - var record = ss.Records[i]; - - var fid = record.GetInt32(featureCID); - - for (var j = 0; j < tileCIDs.Length; ++j) - { - var itemID = record.GetInt32(tileCIDs[j]); - - if (itemID <= 0 || itemID >= table.Length) - { - continue; - } - - table[itemID] = fid; - } - } - } - - private static int[] CreateTable(int length) - { - var table = new int[length]; - - for (var i = 0; i < table.Length; ++i) - { - table[i] = NotAComponent; - } - - return table; - } -} - -public class Spreadsheet -{ - private readonly ColumnInfo[] m_Columns; - - public Spreadsheet(string path) - { - using var ip = new StreamReader(path); - var types = ReadLine(ip); - var names = ReadLine(ip); - - if (types == null || names == null) - { - throw new InvalidDataException($"House component sheet '{path}' is missing its header rows."); - } - - m_Columns = new ColumnInfo[types.Length]; - - for (var i = 0; i < m_Columns.Length; ++i) - { - // A names row shorter than the types row leaves the extras unnamed, so nothing resolves - // to them. - m_Columns[i] = new ColumnInfo(i, types[i], i < names.Length ? names[i] : ""); - } - - var records = new List(); - - while (ReadLine(ip) is { } values) - { - var data = new object[m_Columns.Length]; + m_Columns = new ColumnInfo[types.Length]; for (var i = 0; i < m_Columns.Length; ++i) { - var ci = m_Columns[i]; + m_Columns[i] = new ColumnInfo(i, types[i], names[i]); + } - // Client sheets write an empty trailing Comment as a plain newline, leaving the row - // one field short. The client only requires the columns up to FeatureMask, so treat - // the missing field as empty rather than dropping a row that lists real pieces. - var value = ci.m_DataIndex < values.Length ? values[ci.m_DataIndex] : null; + var records = new List(); - data[i] = ci.m_Type switch + string[] values; + + while ((values = ReadLine(ip)) != null) + { + var data = new object[m_Columns.Length]; + + for (var i = 0; i < m_Columns.Length; ++i) { - "int" => Utility.ToInt32(value), - "string" => value, - _ => data[i] - }; + var ci = m_Columns[i]; + + switch (ci.m_Type) + { + case "int": + { + data[i] = Utility.ToInt32(values[ci.m_DataIndex]); + break; + } + case "string": + { + data[i] = values[ci.m_DataIndex]; + break; + } + } + } + + records.Add(new DataRecord(this, data)); } - records.Add(new DataRecord(this, data)); + Records = records.ToArray(); } - Records = records.ToArray(); - } + public DataRecord[] Records { get; } - public DataRecord[] Records { get; } - - public int GetColumnID(string name) - { - for (var i = 0; i < m_Columns.Length; ++i) + public int GetColumnID(string name) { - if (m_Columns[i].m_Name == name) + for (var i = 0; i < m_Columns.Length; ++i) { - return i; + if (m_Columns[i].m_Name == name) + { + return i; + } } + + return -1; } - return -1; - } - - private static string[] ReadLine(StreamReader ip) - { - while (ip.ReadLine() is { } line) + private string[] ReadLine(StreamReader ip) { - // Whitespace-only, not merely empty: the retail client's doors.txt separates its header - // rows with lines of bare tabs, and accepting one as the names row leaves every column - // unnamed, so no door resolves. The client skips them the same way. - if (!string.IsNullOrWhiteSpace(line)) + string line; + + while ((line = ip.ReadLine()) != null) { - return line.Split('\t'); + if (line.Length > 0) + { + return line.Split('\t'); + } + } + + return null; + } + + private class ColumnInfo + { + public readonly int m_DataIndex; + public readonly string m_Name; + + public readonly string m_Type; + + public ColumnInfo(int dataIndex, string type, string name) + { + m_DataIndex = dataIndex; + + m_Type = type; + m_Name = name; } } - - return null; } - private class ColumnInfo + public class DataRecord { - public readonly int m_DataIndex; - public readonly string m_Name; - - public readonly string m_Type; - - public ColumnInfo(int dataIndex, string type, string name) + public DataRecord(Spreadsheet ss, object[] data) { - m_DataIndex = dataIndex; - - m_Type = type; - m_Name = name; + Spreadsheet = ss; + Data = data; } + + public Spreadsheet Spreadsheet { get; } + + public object[] Data { get; } + + public object this[string name] => this[Spreadsheet.GetColumnID(name)]; + + public object this[int id] => id < 0 ? null : Data[id]; + + public int GetInt32(string name) => GetInt32(this[name]); + + public int GetInt32(int id) => GetInt32(this[id]); + + public int GetInt32(object obj) => Convert.ToInt32(obj); + + public string GetString(string name) => this[name] as string; } } - -public class DataRecord -{ - public DataRecord(Spreadsheet ss, object[] data) - { - Spreadsheet = ss; - Data = data; - } - - public Spreadsheet Spreadsheet { get; } - - public object[] Data { get; } - - public object this[string name] => this[Spreadsheet.GetColumnID(name)]; - - public object this[int id] => id < 0 ? null : Data[id]; - - public int GetInt32(int id) => GetInt32(this[id]); - - public int GetInt32(object obj) => Convert.ToInt32(obj); -} diff --git a/Projects/UOContent/Multis/Deeds.cs b/Projects/UOContent/Multis/Deeds.cs index 3b420d337..b537cec5e 100644 --- a/Projects/UOContent/Multis/Deeds.cs +++ b/Projects/UOContent/Multis/Deeds.cs @@ -1,283 +1,839 @@ -using ModernUO.Serialization; +using Server.Regions; +using Server.Targeting; -namespace Server.Multis.Deeds; - -[SerializationGenerator(0, false)] -public partial class StonePlasterHouseDeed : HouseDeed +namespace Server.Multis.Deeds { - [Constructible] - public StonePlasterHouseDeed() : base(0x64, new Point3D(0, 4, 0)) + public class HousePlacementTarget : MultiTarget { + private readonly HouseDeed m_Deed; + + public HousePlacementTarget(HouseDeed deed) : base(deed.MultiID, deed.Offset) => m_Deed = deed; + + protected override void OnTarget(Mobile from, object o) + { + if (o is not IPoint3D ip) + { + return; + } + + Point3D p = ip switch + { + Item item => item.GetWorldTop(), + Mobile m => m.Location, + _ => new Point3D(ip) + }; + + var reg = Region.Find(p, from.Map); + + if (from.AccessLevel >= AccessLevel.GameMaster || reg.AllowHousing(from, p)) + { + m_Deed.OnPlacement(from, p); + } + else if (reg.IsPartOf()) + { + // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. + from.SendLocalizedMessage(501270); + } + else if (reg.IsPartOf() || reg.IsPartOf()) + { + // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. + from.SendLocalizedMessage(1043287); + } + else if (reg.IsPartOf()) + { + from.SendLocalizedMessage(1150493); // You must have a deed for this plot of land in order to build here. + } + else + { + from.SendLocalizedMessage(501265); // Housing can not be created in this area. + } + } } - public override int LabelNumber => 1041211; - public override Rectangle2D[] Area => SmallOldHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x64); -} - -[SerializationGenerator(0, false)] -public partial class FieldStoneHouseDeed : HouseDeed -{ - [Constructible] - public FieldStoneHouseDeed() : base(0x66, new Point3D(0, 4, 0)) + public abstract class HouseDeed : Item { + public HouseDeed(int id, Point3D offset) : base(0x14F0) + { + Weight = 1.0; + LootType = LootType.Newbied; + + MultiID = id; + Offset = offset; + } + + public HouseDeed(Serial serial) : base(serial) + { + } + + [CommandProperty(AccessLevel.GameMaster)] + public int MultiID { get; set; } + + [CommandProperty(AccessLevel.GameMaster)] + public Point3D Offset { get; set; } + + public abstract Rectangle2D[] Area { get; } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(1); // version + + writer.Write(Offset); + + writer.Write(MultiID); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 1: + { + Offset = reader.ReadPoint3D(); + + goto case 0; + } + case 0: + { + MultiID = reader.ReadInt(); + + break; + } + } + + if (Weight == 0.0) + { + Weight = 1.0; + } + } + + public override void OnDoubleClick(Mobile from) + { + if (!IsChildOf(from.Backpack)) + { + from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. + } + else if (from.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse(from)) + { + from.SendLocalizedMessage(501271); // You already own a house, you may not place another! + } + else + { + /* House placement cancellation could result in a + * 60 second delay in the return of your deed. + */ + from.SendLocalizedMessage(1010433); + + from.Target = new HousePlacementTarget(this); + } + } + + public abstract BaseHouse GetHouse(Mobile owner); + + public void OnPlacement(Mobile from, Point3D p) + { + if (Deleted) + { + return; + } + + if (!IsChildOf(from.Backpack)) + { + from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. + } + else if (from.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse(from)) + { + from.SendLocalizedMessage(501271); // You already own a house, you may not place another! + } + else + { + var center = new Point3D(p.X - Offset.X, p.Y - Offset.Y, p.Z - Offset.Z); + var res = HousePlacement.Check(from, MultiID, center, out var toMove); + + switch (res) + { + case HousePlacementResult.Valid: + { + var house = GetHouse(from); + house.MoveToWorld(center, from.Map); + Delete(); + + for (var i = 0; i < toMove.Count; ++i) + { + object o = toMove[i]; + + if (o is Mobile mobile) + { + mobile.Location = house.BanLocation; + } + else if (o is Item item) + { + item.Location = house.BanLocation; + } + } + + break; + } + case HousePlacementResult.BadItem: + case HousePlacementResult.BadLand: + case HousePlacementResult.BadStatic: + case HousePlacementResult.BadRegionHidden: + { + // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. + from.SendLocalizedMessage(1043287); + break; + } + case HousePlacementResult.NoSurface: + { + from.SendMessage( + "The house could not be created here. Part of the foundation would not be on any surface." + ); + break; + } + case HousePlacementResult.BadRegion: + { + from.SendLocalizedMessage(501265); // Housing cannot be created in this area. + break; + } + case HousePlacementResult.BadRegionTemp: + { + // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. + from.SendLocalizedMessage(501270); + break; + } + case HousePlacementResult.BadRegionRaffle: + { + // You must have a deed for this plot of land in order to build here. + from.SendLocalizedMessage(1150493); + break; + } + } + } + } } - public override int LabelNumber => 1041212; - public override Rectangle2D[] Area => SmallOldHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x66); -} - -[SerializationGenerator(0, false)] -public partial class SmallBrickHouseDeed : HouseDeed -{ - [Constructible] - public SmallBrickHouseDeed() : base(0x68, new Point3D(0, 4, 0)) + public class StonePlasterHouseDeed : HouseDeed { + [Constructible] + public StonePlasterHouseDeed() : base(0x64, new Point3D(0, 4, 0)) + { + } + + public StonePlasterHouseDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041211; + public override Rectangle2D[] Area => SmallOldHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x64); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041213; - public override Rectangle2D[] Area => SmallOldHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x68); -} - -[SerializationGenerator(0, false)] -public partial class WoodHouseDeed : HouseDeed -{ - [Constructible] - public WoodHouseDeed() : base(0x6A, new Point3D(0, 4, 0)) + public class FieldStoneHouseDeed : HouseDeed { + [Constructible] + public FieldStoneHouseDeed() : base(0x66, new Point3D(0, 4, 0)) + { + } + + public FieldStoneHouseDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041212; + public override Rectangle2D[] Area => SmallOldHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x66); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041214; - public override Rectangle2D[] Area => SmallOldHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x6A); -} - -[SerializationGenerator(0, false)] -public partial class WoodPlasterHouseDeed : HouseDeed -{ - [Constructible] - public WoodPlasterHouseDeed() : base(0x6C, new Point3D(0, 4, 0)) + public class SmallBrickHouseDeed : HouseDeed { + [Constructible] + public SmallBrickHouseDeed() : base(0x68, new Point3D(0, 4, 0)) + { + } + + public SmallBrickHouseDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041213; + public override Rectangle2D[] Area => SmallOldHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x68); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041215; - public override Rectangle2D[] Area => SmallOldHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x6C); -} - -[SerializationGenerator(0, false)] -public partial class ThatchedRoofCottageDeed : HouseDeed -{ - [Constructible] - public ThatchedRoofCottageDeed() : base(0x6E, new Point3D(0, 4, 0)) + public class WoodHouseDeed : HouseDeed { + [Constructible] + public WoodHouseDeed() : base(0x6A, new Point3D(0, 4, 0)) + { + } + + public WoodHouseDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041214; + public override Rectangle2D[] Area => SmallOldHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x6A); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041216; - public override Rectangle2D[] Area => SmallOldHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x6E); -} - -[SerializationGenerator(0, false)] -public partial class BrickHouseDeed : HouseDeed -{ - [Constructible] - public BrickHouseDeed() : base(0x74, new Point3D(-1, 7, 0)) + public class WoodPlasterHouseDeed : HouseDeed { + [Constructible] + public WoodPlasterHouseDeed() : base(0x6C, new Point3D(0, 4, 0)) + { + } + + public WoodPlasterHouseDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041215; + public override Rectangle2D[] Area => SmallOldHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x6C); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041219; - public override Rectangle2D[] Area => GuildHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new GuildHouse(owner); -} - -[SerializationGenerator(0, false)] -public partial class TwoStoryWoodPlasterHouseDeed : HouseDeed -{ - [Constructible] - public TwoStoryWoodPlasterHouseDeed() : base(0x76, new Point3D(-3, 7, 0)) + public class ThatchedRoofCottageDeed : HouseDeed { + [Constructible] + public ThatchedRoofCottageDeed() : base(0x6E, new Point3D(0, 4, 0)) + { + } + + public ThatchedRoofCottageDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041216; + public override Rectangle2D[] Area => SmallOldHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new SmallOldHouse(owner, 0x6E); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041220; - public override Rectangle2D[] Area => TwoStoryHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new TwoStoryHouse(owner, 0x76); -} - -[SerializationGenerator(0, false)] -public partial class TwoStoryStonePlasterHouseDeed : HouseDeed -{ - [Constructible] - public TwoStoryStonePlasterHouseDeed() : base(0x78, new Point3D(-3, 7, 0)) + public class BrickHouseDeed : HouseDeed { + [Constructible] + public BrickHouseDeed() : base(0x74, new Point3D(-1, 7, 0)) + { + } + + public BrickHouseDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041219; + public override Rectangle2D[] Area => GuildHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new GuildHouse(owner); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041221; - public override Rectangle2D[] Area => TwoStoryHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new TwoStoryHouse(owner, 0x78); -} - -[SerializationGenerator(0, false)] -public partial class TowerDeed : HouseDeed -{ - [Constructible] - public TowerDeed() : base(0x7A, new Point3D(0, 7, 0)) + public class TwoStoryWoodPlasterHouseDeed : HouseDeed { + [Constructible] + public TwoStoryWoodPlasterHouseDeed() : base(0x76, new Point3D(-3, 7, 0)) + { + } + + public TwoStoryWoodPlasterHouseDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041220; + public override Rectangle2D[] Area => TwoStoryHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new TwoStoryHouse(owner, 0x76); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041222; - public override Rectangle2D[] Area => Tower.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new Tower(owner); -} - -[SerializationGenerator(0, false)] -public partial class KeepDeed : HouseDeed -{ - [Constructible] - public KeepDeed() : base(0x7C, new Point3D(0, 11, 0)) + public class TwoStoryStonePlasterHouseDeed : HouseDeed { + [Constructible] + public TwoStoryStonePlasterHouseDeed() : base(0x78, new Point3D(-3, 7, 0)) + { + } + + public TwoStoryStonePlasterHouseDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041221; + public override Rectangle2D[] Area => TwoStoryHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new TwoStoryHouse(owner, 0x78); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041223; - public override Rectangle2D[] Area => Keep.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new Keep(owner); -} - -[SerializationGenerator(0, false)] -public partial class CastleDeed : HouseDeed -{ - [Constructible] - public CastleDeed() : base(0x7E, new Point3D(0, 16, 0)) + public class TowerDeed : HouseDeed { + [Constructible] + public TowerDeed() : base(0x7A, new Point3D(0, 7, 0)) + { + } + + public TowerDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041222; + public override Rectangle2D[] Area => Tower.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new Tower(owner); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041224; - public override Rectangle2D[] Area => Castle.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new Castle(owner); -} - -[SerializationGenerator(0, false)] -public partial class LargePatioDeed : HouseDeed -{ - [Constructible] - public LargePatioDeed() : base(0x8C, new Point3D(-4, 7, 0)) + public class KeepDeed : HouseDeed { + [Constructible] + public KeepDeed() : base(0x7C, new Point3D(0, 11, 0)) + { + } + + public KeepDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041223; + public override Rectangle2D[] Area => Keep.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new Keep(owner); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041231; - public override Rectangle2D[] Area => LargePatioHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new LargePatioHouse(owner); -} - -[SerializationGenerator(0, false)] -public partial class LargeMarbleDeed : HouseDeed -{ - [Constructible] - public LargeMarbleDeed() : base(0x96, new Point3D(-4, 7, 0)) + public class CastleDeed : HouseDeed { + [Constructible] + public CastleDeed() : base(0x7E, new Point3D(0, 16, 0)) + { + } + + public CastleDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041224; + public override Rectangle2D[] Area => Castle.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new Castle(owner); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041236; - public override Rectangle2D[] Area => LargeMarbleHouse.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new LargeMarbleHouse(owner); -} - -[SerializationGenerator(0, false)] -public partial class SmallTowerDeed : HouseDeed -{ - [Constructible] - public SmallTowerDeed() : base(0x98, new Point3D(3, 4, 0)) + public class LargePatioDeed : HouseDeed { + [Constructible] + public LargePatioDeed() : base(0x8C, new Point3D(-4, 7, 0)) + { + } + + public LargePatioDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041231; + public override Rectangle2D[] Area => LargePatioHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new LargePatioHouse(owner); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041237; - public override Rectangle2D[] Area => SmallTower.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new SmallTower(owner); -} - -[SerializationGenerator(0, false)] -public partial class LogCabinDeed : HouseDeed -{ - [Constructible] - public LogCabinDeed() : base(0x9A, new Point3D(1, 6, 0)) + public class LargeMarbleDeed : HouseDeed { + [Constructible] + public LargeMarbleDeed() : base(0x96, new Point3D(-4, 7, 0)) + { + } + + public LargeMarbleDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041236; + public override Rectangle2D[] Area => LargeMarbleHouse.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new LargeMarbleHouse(owner); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041238; - public override Rectangle2D[] Area => LogCabin.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new LogCabin(owner); -} - -[SerializationGenerator(0, false)] -public partial class SandstonePatioDeed : HouseDeed -{ - [Constructible] - public SandstonePatioDeed() : base(0x9C, new Point3D(-1, 4, 0)) + public class SmallTowerDeed : HouseDeed { + [Constructible] + public SmallTowerDeed() : base(0x98, new Point3D(3, 4, 0)) + { + } + + public SmallTowerDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041237; + public override Rectangle2D[] Area => SmallTower.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new SmallTower(owner); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041239; - public override Rectangle2D[] Area => SandStonePatio.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new SandStonePatio(owner); -} - -[SerializationGenerator(0, false)] -public partial class VillaDeed : HouseDeed -{ - [Constructible] - public VillaDeed() : base(0x9E, new Point3D(3, 6, 0)) + public class LogCabinDeed : HouseDeed { + [Constructible] + public LogCabinDeed() : base(0x9A, new Point3D(1, 6, 0)) + { + } + + public LogCabinDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041238; + public override Rectangle2D[] Area => LogCabin.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new LogCabin(owner); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041240; - public override Rectangle2D[] Area => TwoStoryVilla.AreaArray; - - public override BaseHouse GetHouse(Mobile owner) => new TwoStoryVilla(owner); -} - -[SerializationGenerator(0, false)] -public partial class StoneWorkshopDeed : HouseDeed -{ - [Constructible] - public StoneWorkshopDeed() : base(0xA0, new Point3D(-1, 4, 0)) + public class SandstonePatioDeed : HouseDeed { + [Constructible] + public SandstonePatioDeed() : base(0x9C, new Point3D(-1, 4, 0)) + { + } + + public SandstonePatioDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041239; + public override Rectangle2D[] Area => SandStonePatio.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new SandStonePatio(owner); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041241; - public override Rectangle2D[] Area => SmallShop.AreaArray2; - - public override BaseHouse GetHouse(Mobile owner) => new SmallShop(owner, 0xA0); -} - -[SerializationGenerator(0, false)] -public partial class MarbleWorkshopDeed : HouseDeed -{ - [Constructible] - public MarbleWorkshopDeed() : base(0xA2, new Point3D(-1, 4, 0)) + public class VillaDeed : HouseDeed { + [Constructible] + public VillaDeed() : base(0x9E, new Point3D(3, 6, 0)) + { + } + + public VillaDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041240; + public override Rectangle2D[] Area => TwoStoryVilla.AreaArray; + + public override BaseHouse GetHouse(Mobile owner) => new TwoStoryVilla(owner); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } } - public override int LabelNumber => 1041242; - public override Rectangle2D[] Area => SmallShop.AreaArray1; + public class StoneWorkshopDeed : HouseDeed + { + [Constructible] + public StoneWorkshopDeed() : base(0xA0, new Point3D(-1, 4, 0)) + { + } - public override BaseHouse GetHouse(Mobile owner) => new SmallShop(owner, 0xA2); + public StoneWorkshopDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041241; + public override Rectangle2D[] Area => SmallShop.AreaArray2; + + public override BaseHouse GetHouse(Mobile owner) => new SmallShop(owner, 0xA0); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } + } + + public class MarbleWorkshopDeed : HouseDeed + { + [Constructible] + public MarbleWorkshopDeed() : base(0xA2, new Point3D(-1, 4, 0)) + { + } + + public MarbleWorkshopDeed(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1041242; + public override Rectangle2D[] Area => SmallShop.AreaArray1; + + public override BaseHouse GetHouse(Mobile owner) => new SmallShop(owner, 0xA2); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + } + } } diff --git a/Projects/UOContent/Multis/HouseDeed.cs b/Projects/UOContent/Multis/HouseDeed.cs deleted file mode 100644 index 01ca55c12..000000000 --- a/Projects/UOContent/Multis/HouseDeed.cs +++ /dev/null @@ -1,133 +0,0 @@ -using ModernUO.Serialization; - -namespace Server.Multis.Deeds; - -[SerializationGenerator(0, false)] -public abstract partial class HouseDeed : Item -{ - [SerializableField(0)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Point3D _offset; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _multiID; - - public HouseDeed(int id, Point3D offset) : base(0x14F0) - { - LootType = LootType.Newbied; - - _multiID = id; - _offset = offset; - } - - public virtual Direction HouseDirection => Direction.South; - - public abstract Rectangle2D[] Area { get; } - - public override double DefaultWeight => 1.0; - - public override void OnDoubleClick(Mobile from) - { - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. - } - else if (from.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse(from)) - { - from.SendLocalizedMessage(501271); // You already own a house, you may not place another! - } - else - { - /* House placement cancellation could result in a - * 60 second delay in the return of your deed. - */ - from.SendLocalizedMessage(1010433); - - from.Target = new HousePlacementTarget(this); - } - } - - public abstract BaseHouse GetHouse(Mobile owner); - - public void OnPlacement(Mobile from, Point3D p) - { - if (Deleted) - { - return; - } - - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. - } - else if (from.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse(from)) - { - from.SendLocalizedMessage(501271); // You already own a house, you may not place another! - } - else - { - var center = new Point3D(p.X - Offset.X, p.Y - Offset.Y, p.Z - Offset.Z); - var res = HousePlacement.Check(from, MultiID, center, out var toMove, HouseDirection); - - switch (res) - { - case HousePlacementResult.Valid: - { - var house = GetHouse(from); - house.MoveToWorld(center, from.Map); - Delete(); - - for (var i = 0; i < toMove.Count; ++i) - { - var o = toMove[i]; - - if (o is Mobile mobile) - { - mobile.Location = house.BanLocation; - } - else if (o is Item item) - { - item.Location = house.BanLocation; - } - } - - break; - } - case HousePlacementResult.BadItem: - case HousePlacementResult.BadLand: - case HousePlacementResult.BadStatic: - case HousePlacementResult.BadRegionHidden: - { - // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. - from.SendLocalizedMessage(1043287); - break; - } - case HousePlacementResult.NoSurface: - { - from.SendMessage( - "The house could not be created here. Part of the foundation would not be on any surface." - ); - break; - } - case HousePlacementResult.BadRegion: - { - from.SendLocalizedMessage(501265); // Housing cannot be created in this area. - break; - } - case HousePlacementResult.BadRegionTemp: - { - // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. - from.SendLocalizedMessage(501270); - break; - } - case HousePlacementResult.BadRegionRaffle: - { - // You must have a deed for this plot of land in order to build here. - from.SendLocalizedMessage(1150493); - break; - } - } - } - } -} diff --git a/Projects/UOContent/Multis/HousePlacementTarget.cs b/Projects/UOContent/Multis/HousePlacementTarget.cs deleted file mode 100644 index 98b1653bb..000000000 --- a/Projects/UOContent/Multis/HousePlacementTarget.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Server.Regions; -using Server.Targeting; - -namespace Server.Multis.Deeds; - -public class HousePlacementTarget : MultiTarget -{ - private readonly HouseDeed _deed; - - public HousePlacementTarget(HouseDeed deed) : base(deed.MultiID, deed.Offset) => _deed = deed; - - protected override void OnTarget(Mobile from, object o) - { - if (o is not IPoint3D ip) - { - return; - } - - var p = ip switch - { - Item item => item.GetWorldTop(), - Mobile m => m.Location, - _ => new Point3D(ip) - }; - - var reg = Region.Find(p, from.Map); - - if (from.AccessLevel >= AccessLevel.GameMaster || reg.AllowHousing(from, p)) - { - _deed.OnPlacement(from, p); - } - else if (reg.IsPartOf()) - { - // Lord British has decreed a 'no build' period, thus you cannot build this house at this time. - from.SendLocalizedMessage(501270); - } - else if (reg.IsPartOf() || reg.IsPartOf()) - { - // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. - from.SendLocalizedMessage(1043287); - } - else if (reg.IsPartOf()) - { - from.SendLocalizedMessage(1150493); // You must have a deed for this plot of land in order to build here. - } - else - { - from.SendLocalizedMessage(501265); // Housing can not be created in this area. - } - } -} diff --git a/Projects/UOContent/Multis/Houses/BaseHouse.cs b/Projects/UOContent/Multis/Houses/BaseHouse.cs index 19e9c020c..095f95569 100644 --- a/Projects/UOContent/Multis/Houses/BaseHouse.cs +++ b/Projects/UOContent/Multis/Houses/BaseHouse.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; -using ModernUO.Serialization; using Server.Accounting; using Server.Collections; using Server.ContextMenus; -using Server.Engines.Spawners; using Server.Ethics; using Server.Guilds; using Server.Gumps; @@ -18,7 +16,7 @@ using Server.Targeting; namespace Server.Multis { - public abstract partial class BaseHouse : BaseMulti + public abstract class BaseHouse : BaseMulti { public static bool DecayEnabled { get; set; } @@ -102,8 +100,6 @@ namespace Server.Multis [CommandProperty(AccessLevel.GameMaster)] public bool RestrictDecay { get; set; } - public virtual Direction HouseDirection => Direction.South; - public virtual TimeSpan DecayPeriod => TimeSpan.FromDays(5.0); public virtual DecayType DecayType @@ -795,8 +791,8 @@ namespace Server.Multis public List AvailableVendorsFor(Mobile m) { - var list = new List(); - foreach (var vendor in PlayerVendors) + List list = new List(); + foreach (PlayerVendor vendor in PlayerVendors) { if (vendor.CanInteractWith(m, false)) { @@ -1237,24 +1233,24 @@ namespace Server.Multis return list; } - public PooledRefList GetMobilesPooled() + public List GetMobiles() { if (Map == null || Map == Map.Internal) { - return PooledRefList.Create(0); + return new List(); } - var mobileList = Region.GetMobilesPooled(); - for (var i = mobileList.Count - 1; i >= 0; i--) + var list = new List(); + + foreach (var mobile in Region.GetMobiles()) { - var mobile = mobileList[i]; - if (!IsInside(mobile)) + if (IsInside(mobile)) { - mobileList.Remove(mobile); + list.Add(mobile); } } - return mobileList; + return list; } public virtual bool CheckAosLockdowns(int need) => GetAosCurLockdowns() + need <= GetAosMaxLockdowns(); @@ -1556,8 +1552,6 @@ namespace Server.Multis public override void OnMapChange() { - base.OnMapChange(); - if (LockDowns == null) { return; @@ -1565,19 +1559,6 @@ namespace Server.Multis UpdateRegion(); - // Invalidate spawn position cache for affected sectors - if (Map != null && Map != Map.Internal) - { - var mcl = Components; - var bounds = new Rectangle2D( - X + mcl.Min.X, - Y + mcl.Min.Y, - mcl.Width, - mcl.Height - ); - SectorSpawnCacheManager.InvalidateSectors(Map, bounds); - } - if (Sign?.Deleted == false) { Sign.Map = Map; @@ -1629,8 +1610,6 @@ namespace Server.Multis public override void OnLocationChange(Point3D oldLocation) { - base.OnLocationChange(oldLocation); - if (LockDowns == null) { return; @@ -2355,7 +2334,9 @@ namespace Server.Multis } else { - fromState.AddTrade(toState).DropItem(new TransferItem(this)); + Container c = fromState.AddTrade(toState); + + c.DropItem(new TransferItem(this)); } } } @@ -2642,8 +2623,10 @@ namespace Server.Multis return; } - if (Access.Remove(targ)) + if (Access.Contains(targ)) { + Access.Remove(targ); + if (!HasAccess(targ) && IsInside(targ)) { targ.Location = BanLocation; @@ -2798,8 +2781,10 @@ namespace Server.Multis return; } - if (CoOwners.Remove(targ)) + if (CoOwners.Contains(targ)) { + CoOwners.Remove(targ); + targ.Delta(MobileDelta.Noto); from.SendLocalizedMessage(501299); // Co-owner removed from list. @@ -2868,8 +2853,10 @@ namespace Server.Multis return; } - if (Friends.Remove(targ)) + if (Friends.Contains(targ)) { + Friends.Remove(targ); + targ.Delta(MobileDelta.Noto); from.SendLocalizedMessage(501298); // Friend removed from list. @@ -3342,19 +3329,6 @@ namespace Server.Multis { RestoreRelocatedEntities(); - // Invalidate spawn position cache for affected sectors before deletion - if (Map != null && Map != Map.Internal) - { - var mcl = Components; - var bounds = new Rectangle2D( - X + mcl.Min.X, - Y + mcl.Min.Y, - mcl.Width, - mcl.Height - ); - SectorSpawnCacheManager.InvalidateSectors(Map, bounds); - } - new FixColumnTimer(this).Start(); base.OnDelete(); @@ -3383,7 +3357,9 @@ namespace Server.Multis { for (var i = 0; i < Doors.Count; ++i) { - Doors[i]?.Delete(); + Item item = Doors[i]; + + item?.Delete(); } Doors.Clear(); @@ -3724,20 +3700,21 @@ namespace Server.Multis } } - [SerializationGenerator(0)] - private partial class TransferItem : Item + private class TransferItem : Item { - private readonly BaseHouse _house; + private readonly BaseHouse m_House; public TransferItem(BaseHouse house) : base(0x14F0) { - _house = house; + m_House = house; Hue = 0x480; Movable = false; } - public override bool SkipSerialization => true; + public TransferItem(Serial serial) : base(serial) + { + } public override string DefaultName => "a house transfer contract"; @@ -3745,15 +3722,15 @@ namespace Server.Multis { base.GetProperties(list); - var houseName = _house == null ? "an unnamed house" : _house.Sign.GetName(); - var owner = _house?.Owner?.Name ?? "nobody"; + var houseName = m_House == null ? "an unnamed house" : m_House.Sign.GetName(); + var owner = m_House?.Owner?.Name ?? "nobody"; int xLong = 0, yLat = 0, xMins = 0, yMins = 0; bool xEast = false, ySouth = false; - var valid = _house != null && Sextant.Format( - _house.Location, - _house.Map, + var valid = m_House != null && Sextant.Format( + m_House.Location, + m_House.Map, ref xLong, ref yLat, ref xMins, @@ -3775,6 +3752,22 @@ namespace Server.Multis } } + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadInt(); + + Timer.DelayCall(Delete); + } + public override bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) { if (!base.AllowSecureTrade(from, to, newOwner, accepted)) @@ -3787,7 +3780,7 @@ namespace Server.Multis return true; } - if (Deleted || _house?.Deleted != false || !_house.IsOwner(from) || !from.CheckAlive() || + if (Deleted || m_House?.Deleted != false || !m_House.IsOwner(from) || !from.CheckAlive() || !to.CheckAlive()) { return false; @@ -3799,7 +3792,7 @@ namespace Server.Multis return false; } - return _house.CheckTransferPosition(from, to); + return m_House.CheckTransferPosition(from, to); } public override void OnSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) @@ -3811,7 +3804,7 @@ namespace Server.Multis Delete(); - if (_house?.Deleted != false || !_house.IsOwner(from) || !from.CheckAlive() || !to.CheckAlive()) + if (m_House?.Deleted != false || !m_House.IsOwner(from) || !from.CheckAlive() || !to.CheckAlive()) { return; } @@ -3829,13 +3822,13 @@ namespace Server.Multis */ to.SendLocalizedMessage(501339); - _house.RemoveKeys(from); - _house.Owner = to; - _house.Bans.Clear(); - _house.Friends.Clear(); - _house.CoOwners.Clear(); - _house.ChangeLocks(to); - _house.LastTraded = Core.Now; + m_House.RemoveKeys(from); + m_House.Owner = to; + m_House.Bans.Clear(); + m_House.Friends.Clear(); + m_House.CoOwners.Clear(); + m_House.ChangeLocks(to); + m_House.LastTraded = Core.Now; } } diff --git a/Projects/UOContent/Multis/Houses/ContestHouses.cs b/Projects/UOContent/Multis/Houses/ContestHouses.cs index b37a8f62f..06382205e 100644 --- a/Projects/UOContent/Multis/Houses/ContestHouses.cs +++ b/Projects/UOContent/Multis/Houses/ContestHouses.cs @@ -1,490 +1,778 @@ using System.Collections.Generic; using System.Linq; -using ModernUO.Serialization; using Server.Items; using Server.Logging; -namespace Server.Multis; - -public enum ContestHouseType +namespace Server.Multis { - Keep, - Castle, - Other -} - -[SerializationGenerator(1, false)] -public partial class BaseContestHouse : BaseHouse -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(BaseContestHouse)); - - [SerializableField(0)] - private ContestHouseType _houseType; - - [Tidy] - [CanBeNull] - [SerializableField(1, setter: "private")] - private List _fixtures; - - public BaseContestHouse(ContestHouseType type, int multiID, Mobile owner, int maxLockDown, int maxSecure) - : base(multiID, owner, maxLockDown, maxSecure) + public enum ContestHouseType { - _houseType = type; - - AutoAddFixtures(); + Keep, + Castle, + Other } - public virtual int SignPostID => 9; - - public override Point3D BaseBanLocation => new( - Components.Min.X, - Components.Height - 1 - Components.Center.Y, - 0 - ); - - public override Rectangle2D[] Area + public class BaseContestHouse : BaseHouse { - get + private static readonly ILogger logger = LogFactory.GetLogger(typeof(BaseContestHouse)); + + public BaseContestHouse(ContestHouseType type, int multiID, Mobile owner, int maxLockDown, int maxSecure) + : base(multiID, owner, maxLockDown, maxSecure) { - var mcl = Components; - return new[] { new Rectangle2D(mcl.Min.X, mcl.Min.Y, mcl.Width, mcl.Height) }; - } - } + HouseType = type; - protected void SetSign(int xOffset, int yOffset, int zOffset, bool post) - { - SetSign(xOffset, yOffset, zOffset); - - var hanger = new Static(0xB9E); - hanger.MoveToWorld(new Point3D(X + xOffset, Y + yOffset, Z + zOffset), Map); - - AddFixture(hanger); - - if (post) - { - var signPost = new Static(SignPostID); - signPost.MoveToWorld(new Point3D(X + xOffset, Y + yOffset - 1, Z + zOffset), Map); - - AddFixture(signPost); - } - } - - public override void OnAfterDelete() - { - base.OnAfterDelete(); - - if (Fixtures == null) - { - return; + AutoAddFixtures(); } - foreach (var item in Fixtures) + public BaseContestHouse(Serial serial) : base(serial) { - item.Delete(); - } - } - - public override void OnLocationChange(Point3D oldLocation) - { - base.OnLocationChange(oldLocation); - - var x = Location.X - oldLocation.X; - var y = Location.Y - oldLocation.Y; - var z = Location.Z - oldLocation.Z; - - if (Fixtures == null) - { - return; } - foreach (var item in Fixtures) - { - item.Location = new Point3D(item.X + x, item.Y + y, item.Z + z); - } - } + public ContestHouseType HouseType { get; set; } + public List Fixtures { get; private set; } - public override void OnMapChange() - { - base.OnMapChange(); + public virtual int SignPostID => 9; - if (Fixtures == null) + public override Point3D BaseBanLocation => new( + Components.Min.X, + Components.Height - 1 - Components.Center.Y, + 0 + ); + + public override Rectangle2D[] Area { - return; + get + { + var mcl = Components; + return new[] { new Rectangle2D(mcl.Min.X, mcl.Min.Y, mcl.Width, mcl.Height) }; + } } - foreach (var item in Fixtures) + protected void SetSign(int xOffset, int yOffset, int zOffset, bool post) { - item.Map = Map; - } - } + SetSign(xOffset, yOffset, zOffset); - public void AddTeleporters(int id, Point3D offset1, Point3D offset2) - { - var tele1 = new HouseTeleporter(id); - var tele2 = new HouseTeleporter(id); + var hanger = new Static(0xB9E); + hanger.MoveToWorld(new Point3D(X + xOffset, Y + yOffset, Z + zOffset), Map); - tele1.Target = tele2; - tele2.Target = tele1; + AddFixture(hanger); - tele1.MoveToWorld(new Point3D(X + offset1.X, Y + offset1.Y, offset1.Z), Map); - tele2.MoveToWorld(new Point3D(X + offset2.X, Y + offset2.Y, offset2.Z), Map); + if (post) + { + var signPost = new Static(SignPostID); + signPost.MoveToWorld(new Point3D(X + xOffset, Y + yOffset - 1, Z + zOffset), Map); - AddFixture(tele1); - AddFixture(tele2); - } - - public void AddFixture(Item item) - { - Fixtures ??= []; - Fixtures.Add(item); - } - - public override bool IsInside(Point3D p, int height) - { - if (base.IsInside(p, height)) - { - return true; + AddFixture(signPost); + } } - if (Fixtures == null) + public override void OnAfterDelete() { - return false; + base.OnAfterDelete(); + + if (Fixtures == null) + { + return; + } + + foreach (var item in Fixtures) + { + item.Delete(); + } } - for (var i = 0; i < Fixtures.Count; i++) + public override void OnLocationChange(Point3D oldLocation) { - var fixture = Fixtures[i]; - if (fixture is HouseTeleporter fix && fix.Location == p) + base.OnLocationChange(oldLocation); + + var x = Location.X - oldLocation.X; + var y = Location.Y - oldLocation.Y; + var z = Location.Z - oldLocation.Z; + + if (Fixtures == null) + { + return; + } + + foreach (var item in Fixtures) + { + item.Location = new Point3D(item.X + x, item.Y + y, item.Z + z); + } + } + + public override void OnMapChange() + { + base.OnMapChange(); + + if (Fixtures == null) + { + return; + } + + foreach (var item in Fixtures) + { + item.Map = Map; + } + } + + public void AddTeleporters(int id, Point3D offset1, Point3D offset2) + { + var tele1 = new HouseTeleporter(id); + var tele2 = new HouseTeleporter(id); + + tele1.Target = tele2; + tele2.Target = tele1; + + tele1.MoveToWorld(new Point3D(X + offset1.X, Y + offset1.Y, offset1.Z), Map); + tele2.MoveToWorld(new Point3D(X + offset2.X, Y + offset2.Y, offset2.Z), Map); + + AddFixture(tele1); + AddFixture(tele2); + } + + public void AddFixture(Item item) + { + Fixtures ??= new List(); + + Fixtures.Add(item); + } + + public override bool IsInside(Point3D p, int height) + { + if (base.IsInside(p, height)) { return true; } + + if (Fixtures == null) + { + return false; + } + + foreach (Item fixture in Fixtures) + { + if (fixture is HouseTeleporter fix && fix.Location == p) + { + return true; + } + } + + return false; } - return false; - } - - public virtual void AutoAddFixtures() - { - var components = MultiData.GetComponents(ItemID); - - var teleporters = new Dictionary>(); - - foreach (var entry in components.List.Where(e => e.Flags == 0)) - // Teleporters + public virtual void AutoAddFixtures() { - if (entry.ItemId >= 0x181D && entry.ItemId <= 0x1828) + var components = MultiData.GetComponents(ItemID); + + var teleporters = new Dictionary>(); + + foreach (var entry in components.List.Where(e => e.Flags == 0)) + // Teleporters { - if (teleporters.TryGetValue(entry.ItemId, out var result)) + if (entry.ItemId >= 0x181D && entry.ItemId <= 0x1828) { - result.Add(entry); + if (teleporters.TryGetValue(entry.ItemId, out var result)) + { + result.Add(entry); + } + else + { + teleporters.Add(entry.ItemId, new List { entry }); + } } else { - teleporters.Add(entry.ItemId, new List { entry }); + var data = TileData.ItemTable[entry.ItemId & TileData.MaxItemValue]; + + // door + if (data.Door) + { + AddDoor(entry.ItemId, entry.OffsetX, entry.OffsetY, entry.OffsetZ); + } + else + { + Item st = new Static(entry.ItemId); + + st.MoveToWorld(new Point3D(X + entry.OffsetX, Y + entry.OffsetY, entry.OffsetZ), Map); + AddFixture(st); + } } } + + foreach (var door in Doors) + { + foreach (var check in Doors.Where(d => d != door)) + { + if (door.InRange(check.Location, 1)) + { + door.Link = check; + check.Link = door; + } + } + } + + foreach (var (key, value) in teleporters) + { + if (value.Count > 2) + { + logger.Warning("More than 2 teleporters detected for {ItemId:X}!", key); + } + else if (value.Count <= 1) + { + logger.Warning("1 or less teleporters detected for {ItemId:X}!", key); + + continue; + } + + AddTeleporters( + key, + new Point3D(value[0].OffsetX, value[0].OffsetY, value[0].OffsetZ), + new Point3D(value[1].OffsetX, value[1].OffsetY, value[1].OffsetZ) + ); + } + + teleporters.Clear(); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + + writer.Write((int)HouseType); + + if (Fixtures != null) + { + Fixtures.Tidy(); + writer.Write(Fixtures); + } else { - var data = TileData.ItemTable[entry.ItemId & TileData.MaxItemValue]; - - // door - if (data.Door) - { - AddDoor(entry.ItemId, entry.OffsetX, entry.OffsetY, entry.OffsetZ); - } - else - { - Item st = new Static(entry.ItemId); - - st.MoveToWorld(new Point3D(X + entry.OffsetX, Y + entry.OffsetY, entry.OffsetZ), Map); - AddFixture(st); - } + writer.Write(0); } } - for (var i = 0; i < Doors.Count; i++) + public override void Deserialize(IGenericReader reader) { - var door = Doors[i]; - foreach (var check in Doors.Where(d => d != door)) + base.Deserialize(reader); + var version = reader.ReadInt(); + + HouseType = (ContestHouseType)reader.ReadInt(); + + var count = reader.ReadInt(); + + for (var i = 0; i < count; i++) { - if (door.InRange(check.Location, 1)) - { - door.Link = check; - check.Link = door; - } + AddFixture(reader.ReadEntity()); } } + } - foreach (var (key, value) in teleporters) + public class TrinsicKeep : BaseContestHouse + { + public static Rectangle2D[] AreaArray = { - if (value.Count > 2) - { - logger.Warning("More than 2 teleporters detected for {ItemId:X}!", key); - } - else if (value.Count <= 1) - { - logger.Warning("1 or less teleporters detected for {ItemId:X}!", key); + new(-11, -11, 23, 23), new(-10, 13, 6, 1), + new(-2, 13, 6, 1), new(6, 13, 7, 1) + }; - continue; - } - - AddTeleporters( - key, - new Point3D(value[0].OffsetX, value[0].OffsetY, value[0].OffsetZ), - new Point3D(value[1].OffsetX, value[1].OffsetY, value[1].OffsetZ) - ); - } - - teleporters.Clear(); - } - - private void Deserialize(IGenericReader reader, int version) - { - HouseType = (ContestHouseType)reader.ReadInt(); - - var count = reader.ReadInt(); - - for (var i = 0; i < count; i++) + public TrinsicKeep(Mobile owner) + : base(ContestHouseType.Keep, 0x147E, owner, 2113, 18) { - AddFixture(reader.ReadEntity()); + SetSign(-11, 13, 7, false); + } + + public TrinsicKeep(Serial serial) : base(serial) + { + } + + public override Rectangle2D[] Area => AreaArray; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class GothicRoseCastle : BaseContestHouse + { + public static Rectangle2D[] AreaArray = + { + new(-15, -15, 31, 31), + new(-14, 16, 11, 1), + new(-2, 16, 6, 1), + new(5, 16, 11, 1) + }; + + public GothicRoseCastle(Mobile owner) + : base(ContestHouseType.Castle, 0x147F, owner, 3281, 28) + { + SetSign(-15, 16, 7, false); + } + + public GothicRoseCastle(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class ElsaCastle : BaseContestHouse + { + public ElsaCastle(Mobile owner) + : base(ContestHouseType.Castle, 0x1480, owner, 3281, 28) + { + SetSign(-15, 16, 7, false); + } + + public ElsaCastle(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class Spires : BaseContestHouse + { + public Spires(Mobile owner) + : base(ContestHouseType.Castle, 0x1481, owner, 3281, 28) + { + SetSign(-15, 16, 7, false); + } + + public Spires(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class CastleOfOceania : BaseContestHouse + { + public CastleOfOceania(Mobile owner) + : base(ContestHouseType.Castle, 0x1482, owner, 3281, 28) + { + SetSign(-15, 16, 7, false); + } + + public CastleOfOceania(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class FeudalCastle : BaseContestHouse + { + public static Rectangle2D[] AreaArray = + { + new(-15, -15, 31, 31), + new(5, 16, 1, 1), + new(7, 16, 4, 1), + new(12, 16, 1, 1) + }; + + public FeudalCastle(Mobile owner) + : base(ContestHouseType.Castle, 0x1483, owner, 3281, 28) + { + SetSign(-15, 16, 7, true); + } + + public FeudalCastle(Serial serial) : base(serial) + { + } + + public override Rectangle2D[] Area => AreaArray; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class RobinsNest : BaseContestHouse + { + public RobinsNest(Mobile owner) + : base(ContestHouseType.Keep, 0x1484, owner, 2113, 18) + { + SetSign(-11, 13, 7, false); + } + + public RobinsNest(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class TraditionalKeep : BaseContestHouse + { + public static Rectangle2D[] AreaArray = + { + new(-11, -11, 23, 23), + new(-10, 13, 6, 1), + new(-2, 13, 6, 1), + new(6, 13, 7, 1) + }; + + public TraditionalKeep(Mobile owner) + : base(ContestHouseType.Keep, 0x1485, owner, 2113, 18) + { + SetSign(-11, 13, 7, false); + } + + public TraditionalKeep(Serial serial) : base(serial) + { + } + + public override Rectangle2D[] Area => AreaArray; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class VillaCrowley : BaseContestHouse + { + public VillaCrowley(Mobile owner) + : base(ContestHouseType.Keep, 0x1486, owner, 2113, 18) + { + SetSign(-11, 13, 7, true); + } + + public VillaCrowley(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class DarkthornKeep : BaseContestHouse + { + public DarkthornKeep(Mobile owner) + : base(ContestHouseType.Keep, 0x1487, owner, 2113, 18) + { + SetSign(-11, 13, 7, false); + } + + public DarkthornKeep(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class SandalwoodKeep : BaseContestHouse + { + public SandalwoodKeep(Mobile owner) + : base(ContestHouseType.Keep, 0x1488, owner, 2113, 18) + { + SetSign(-11, 13, 7, true); + } + + public SandalwoodKeep(Serial serial) : base(serial) + { + } + + public override int SignPostID => 353; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class CasaMoga : BaseContestHouse + { + public CasaMoga(Mobile owner) + : base(ContestHouseType.Keep, 0x1489, owner, 2113, 18) + { + SetSign(-11, 13, 7, false); + } + + public CasaMoga(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class RobinsRoost : BaseContestHouse + { + public RobinsRoost(Mobile owner) + : base(ContestHouseType.Castle, 0x148A, owner, 3281, 28) + { + SetSign(-15, 16, 7, true); + } + + public RobinsRoost(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class Camelot : BaseContestHouse + { + public Camelot(Mobile owner) + : base(ContestHouseType.Castle, 0x148B, owner, 3281, 28) + { + SetSign(-15, 16, 7, false); + } + + public Camelot(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class LacrimaeInCaelo : BaseContestHouse + { + public LacrimaeInCaelo(Mobile owner) + : base(ContestHouseType.Castle, 0x148C, owner, 3281, 28) + { + SetSign(-15, 16, 7, false); + } + + public LacrimaeInCaelo(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class OkinawaSweetDreamCastle : BaseContestHouse + { + public static Rectangle2D[] AreaArray = + { + new(-15, -15, 31, 31), + new(-14, 16, 6, 1), + new(-7, 16, 8, 1), + new(10, 16, 5, 1) + }; + + public OkinawaSweetDreamCastle(Mobile owner) + : base(ContestHouseType.Castle, 0x148D, owner, 3281, 28) + { + SetSign(-15, 16, 7, true); + } + + public OkinawaSweetDreamCastle(Serial serial) : base(serial) + { + } + + public override Rectangle2D[] Area => AreaArray; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class TheSandstoneCastle : BaseContestHouse + { + public TheSandstoneCastle(Mobile owner) + : base(ContestHouseType.Castle, 0x148E, owner, 3281, 28) + { + SetSign(-15, 16, 7, true); + } + + public TheSandstoneCastle(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class GrimswindSisters : BaseContestHouse + { + public static Rectangle2D[] AreaArray = + { + new(-15, -15, 31, 31), + new(-14, 16, 9, 1), + new(-3, 16, 8, 1), + new(7, 16, 9, 1) + }; + + public GrimswindSisters(Mobile owner) + : base(ContestHouseType.Castle, 0x148F, owner, 3281, 28) + { + SetSign(-15, 16, 7, false); + } + + public GrimswindSisters(Serial serial) : base(serial) + { + } + + public override Rectangle2D[] Area => AreaArray; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); } } } - -[SerializationGenerator(0, false)] -public partial class TrinsicKeep : BaseContestHouse -{ - public static readonly Rectangle2D[] AreaArray = - { - new(-11, -11, 23, 23), new(-10, 13, 6, 1), - new(-2, 13, 6, 1), new(6, 13, 7, 1) - }; - - public TrinsicKeep(Mobile owner) - : base(ContestHouseType.Keep, 0x147E, owner, 2113, 18) - { - SetSign(-11, 13, 7, false); - } - - public override Rectangle2D[] Area => AreaArray; -} - -[SerializationGenerator(0, false)] -public partial class GothicRoseCastle : BaseContestHouse -{ - public static readonly Rectangle2D[] AreaArray = - { - new(-15, -15, 31, 31), - new(-14, 16, 11, 1), - new(-2, 16, 6, 1), - new(5, 16, 11, 1) - }; - - public GothicRoseCastle(Mobile owner) - : base(ContestHouseType.Castle, 0x147F, owner, 3281, 28) - { - SetSign(-15, 16, 7, false); - } - - public override Rectangle2D[] Area => AreaArray; -} - -[SerializationGenerator(0, false)] -public partial class ElsaCastle : BaseContestHouse -{ - public ElsaCastle(Mobile owner) - : base(ContestHouseType.Castle, 0x1480, owner, 3281, 28) - { - SetSign(-15, 16, 7, false); - } -} - -[SerializationGenerator(0, false)] -public partial class Spires : BaseContestHouse -{ - public Spires(Mobile owner) - : base(ContestHouseType.Castle, 0x1481, owner, 3281, 28) - { - SetSign(-15, 16, 7, false); - } -} - -[SerializationGenerator(0, false)] -public partial class CastleOfOceania : BaseContestHouse -{ - public CastleOfOceania(Mobile owner) - : base(ContestHouseType.Castle, 0x1482, owner, 3281, 28) - { - SetSign(-15, 16, 7, false); - } -} - -[SerializationGenerator(0, false)] -public partial class FeudalCastle : BaseContestHouse -{ - public static readonly Rectangle2D[] AreaArray = - { - new(-15, -15, 31, 31), - new(5, 16, 1, 1), - new(7, 16, 4, 1), - new(12, 16, 1, 1) - }; - - public FeudalCastle(Mobile owner) - : base(ContestHouseType.Castle, 0x1483, owner, 3281, 28) - { - SetSign(-15, 16, 7, true); - } - - public override Rectangle2D[] Area => AreaArray; -} - -[SerializationGenerator(0, false)] -public partial class RobinsNest : BaseContestHouse -{ - public RobinsNest(Mobile owner) - : base(ContestHouseType.Keep, 0x1484, owner, 2113, 18) - { - SetSign(-11, 13, 7, false); - } -} - -[SerializationGenerator(0, false)] -public partial class TraditionalKeep : BaseContestHouse -{ - public static readonly Rectangle2D[] AreaArray = - { - new(-11, -11, 23, 23), - new(-10, 13, 6, 1), - new(-2, 13, 6, 1), - new(6, 13, 7, 1) - }; - - public TraditionalKeep(Mobile owner) - : base(ContestHouseType.Keep, 0x1485, owner, 2113, 18) - { - SetSign(-11, 13, 7, false); - } - - public override Rectangle2D[] Area => AreaArray; -} - -[SerializationGenerator(0, false)] -public partial class VillaCrowley : BaseContestHouse -{ - public VillaCrowley(Mobile owner) - : base(ContestHouseType.Keep, 0x1486, owner, 2113, 18) - { - SetSign(-11, 13, 7, true); - } -} - -[SerializationGenerator(0, false)] -public partial class DarkthornKeep : BaseContestHouse -{ - public DarkthornKeep(Mobile owner) - : base(ContestHouseType.Keep, 0x1487, owner, 2113, 18) - { - SetSign(-11, 13, 7, false); - } -} - -[SerializationGenerator(0, false)] -public partial class SandalwoodKeep : BaseContestHouse -{ - public SandalwoodKeep(Mobile owner) - : base(ContestHouseType.Keep, 0x1488, owner, 2113, 18) - { - SetSign(-11, 13, 7, true); - } - - public override int SignPostID => 353; -} - -[SerializationGenerator(0, false)] -public partial class CasaMoga : BaseContestHouse -{ - public CasaMoga(Mobile owner) - : base(ContestHouseType.Keep, 0x1489, owner, 2113, 18) - { - SetSign(-11, 13, 7, false); - } -} - -[SerializationGenerator(0, false)] -public partial class RobinsRoost : BaseContestHouse -{ - public RobinsRoost(Mobile owner) - : base(ContestHouseType.Castle, 0x148A, owner, 3281, 28) - { - SetSign(-15, 16, 7, true); - } -} - -[SerializationGenerator(0, false)] -public partial class Camelot : BaseContestHouse -{ - public Camelot(Mobile owner) - : base(ContestHouseType.Castle, 0x148B, owner, 3281, 28) - { - SetSign(-15, 16, 7, false); - } -} - -[SerializationGenerator(0, false)] -public partial class LacrimaeInCaelo : BaseContestHouse -{ - public LacrimaeInCaelo(Mobile owner) - : base(ContestHouseType.Castle, 0x148C, owner, 3281, 28) - { - SetSign(-15, 16, 7, false); - } -} - -[SerializationGenerator(0, false)] -public partial class OkinawaSweetDreamCastle : BaseContestHouse -{ - public static readonly Rectangle2D[] AreaArray = - { - new(-15, -15, 31, 31), - new(-14, 16, 6, 1), - new(-7, 16, 8, 1), - new(10, 16, 5, 1) - }; - - public OkinawaSweetDreamCastle(Mobile owner) - : base(ContestHouseType.Castle, 0x148D, owner, 3281, 28) - { - SetSign(-15, 16, 7, true); - } -} - -[SerializationGenerator(0, false)] -public partial class TheSandstoneCastle : BaseContestHouse -{ - public TheSandstoneCastle(Mobile owner) - : base(ContestHouseType.Castle, 0x148E, owner, 3281, 28) - { - SetSign(-15, 16, 7, true); - } -} - -[SerializationGenerator(0, false)] -public partial class GrimswindSisters : BaseContestHouse -{ - public static readonly Rectangle2D[] AreaArray = - { - new(-15, -15, 31, 31), - new(-14, 16, 9, 1), - new(-3, 16, 8, 1), - new(7, 16, 9, 1) - }; - - public GrimswindSisters(Mobile owner) - : base(ContestHouseType.Castle, 0x148F, owner, 3281, 28) - { - SetSign(-15, 16, 7, false); - } - - public override Rectangle2D[] Area => AreaArray; -} diff --git a/Projects/UOContent/Multis/Houses/HouseFoundation.cs b/Projects/UOContent/Multis/Houses/HouseFoundation.cs index 11a387934..74ad5e4e7 100644 --- a/Projects/UOContent/Multis/Houses/HouseFoundation.cs +++ b/Projects/UOContent/Multis/Houses/HouseFoundation.cs @@ -25,6 +25,8 @@ namespace Server.Multis public class HouseFoundation : BaseHouse { + private static ComponentVerification m_Verification; + public static readonly bool AllowStairSectioning = true; /* Stair block IDs @@ -199,6 +201,8 @@ namespace Server.Multis } } + public static ComponentVerification Verification => m_Verification ??= new ComponentVerification(); + public bool IsFixture(Item item) => Fixtures.Contains(item); public override int GetMaxUpdateRange() => 24; @@ -897,10 +901,8 @@ namespace Server.Multis item.Location = BanLocation; } - using var mobiles = GetMobilesPooled(); - for (var i = 0; i < mobiles.Count; i++) + foreach (var mobile in GetMobiles()) { - var mobile = mobiles[i]; if (mobile != m) { mobile.Location = BanLocation; @@ -1292,18 +1294,13 @@ namespace Server.Multis // Eject all from house from.RevealingAction(); - var items = GetItems(); - for (var i = 0; i < items.Count; i++) + foreach (var item in GetItems()) { - var item = items[i]; item.Location = BanLocation; } - using var mobiles = GetMobilesPooled(); - var list = GetMobilesPooled(); - for (var i = 0; i < list.Count; i++) + foreach (var mobile in GetMobiles()) { - var mobile = list[i]; mobile.Location = BanLocation; } @@ -1354,7 +1351,7 @@ namespace Server.Multis public static bool ValidPiece(int itemID, bool roof = false) { itemID &= TileData.MaxItemValue; - return roof == TileData.ItemTable[itemID].Roof && ComponentVerification.IsItemValid(itemID); + return roof == TileData.ItemTable[itemID].Roof && Verification.IsItemValid(itemID); } public static bool IsStairBlock(int id) @@ -1634,7 +1631,7 @@ namespace Server.Multis // Validate stair multi ID var design = context.Foundation.DesignState; - if (!ComponentVerification.IsMultiValid(itemID)) + if (!Verification.IsMultiValid(itemID)) { /* Specified multi ID is not a stair * - Resend design state @@ -1763,17 +1760,13 @@ namespace Server.Multis // Eject all from house from.RevealingAction(); - var list = context.Foundation.GetItems(); - for (var i = 0; i < list.Count; i++) + foreach (var item in context.Foundation.GetItems()) { - var item = list[i]; item.Location = context.Foundation.BanLocation; } - using var mobiles = context.Foundation.GetMobilesPooled(); - for (var i = 0; i < mobiles.Count; i++) + foreach (var mobile in context.Foundation.GetMobiles()) { - var mobile = mobiles[i]; mobile.Location = context.Foundation.BanLocation; } @@ -2104,62 +2097,53 @@ namespace Server.Multis }; } - public class ConfirmCommitGump : DynamicGump + public class ConfirmCommitGump : Gump { private readonly HouseFoundation m_Foundation; - private readonly int _bankBalance; - private readonly int _oldPrice; - private readonly int _newPrice; public ConfirmCommitGump(HouseFoundation foundation, int bankBalance, int oldPrice, int newPrice) : base(50, 50) { m_Foundation = foundation; - _bankBalance = bankBalance; - _oldPrice = oldPrice; - _newPrice = newPrice; - } - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(0, 0, 320, 320, 5054); + AddBackground(0, 0, 320, 320, 5054); - builder.AddImageTiled(10, 10, 300, 20, 2624); - builder.AddImageTiled(10, 40, 300, 240, 2624); - builder.AddImageTiled(10, 290, 300, 20, 2624); + AddImageTiled(10, 10, 300, 20, 2624); + AddImageTiled(10, 40, 300, 240, 2624); + AddImageTiled(10, 290, 300, 20, 2624); - builder.AddAlphaRegion(10, 10, 300, 300); + AddAlphaRegion(10, 10, 300, 300); - builder.AddHtmlLocalized(10, 10, 300, 20, 1062060, 32736); //
COMMIT DESIGN
+ AddHtmlLocalized(10, 10, 300, 20, 1062060, 32736); //
COMMIT DESIGN
- builder.AddHtmlLocalized(10, 40, 300, 140, _newPrice - _oldPrice <= _bankBalance ? 1061898 : 1061903, 1023, false, true); + AddHtmlLocalized(10, 40, 300, 140, newPrice - oldPrice <= bankBalance ? 1061898 : 1061903, 1023, false, true); - builder.AddHtmlLocalized(10, 190, 150, 20, 1061902, 32736); // Bank Balance: - builder.AddLabel(170, 190, 55, _bankBalance.ToString()); + AddHtmlLocalized(10, 190, 150, 20, 1061902, 32736); // Bank Balance: + AddLabel(170, 190, 55, bankBalance.ToString()); - builder.AddHtmlLocalized(10, 215, 150, 20, 1061899, 1023); // Old Value: - builder.AddLabel(170, 215, 90, _oldPrice.ToString()); + AddHtmlLocalized(10, 215, 150, 20, 1061899, 1023); // Old Value: + AddLabel(170, 215, 90, oldPrice.ToString()); - builder.AddHtmlLocalized(10, 235, 150, 20, 1061900, 1023); // Cost To Commit: - builder.AddLabel(170, 235, 90, _newPrice.ToString()); + AddHtmlLocalized(10, 235, 150, 20, 1061900, 1023); // Cost To Commit: + AddLabel(170, 235, 90, newPrice.ToString()); - if (_newPrice - _oldPrice < 0) + if (newPrice - oldPrice < 0) { - builder.AddHtmlLocalized(10, 260, 150, 20, 1062059, 992); // Your Refund: - builder.AddLabel(170, 260, 70, (_oldPrice - _newPrice).ToString()); + AddHtmlLocalized(10, 260, 150, 20, 1062059, 992); // Your Refund: + AddLabel(170, 260, 70, (oldPrice - newPrice).ToString()); } else { - builder.AddHtmlLocalized(10, 260, 150, 20, 1061901, 31744); // Your Cost: - builder.AddLabel(170, 260, 40, (_newPrice - _oldPrice).ToString()); + AddHtmlLocalized(10, 260, 150, 20, 1061901, 31744); // Your Cost: + AddLabel(170, 260, 40, (newPrice - oldPrice).ToString()); } - builder.AddButton(10, 290, 4005, 4007, 1); - builder.AddHtmlLocalized(45, 290, 55, 20, 1011036, 32767); // OKAY + AddButton(10, 290, 4005, 4007, 1); + AddHtmlLocalized(45, 290, 55, 20, 1011036, 32767); // OKAY - builder.AddButton(170, 290, 4005, 4007, 0); - builder.AddHtmlLocalized(195, 290, 55, 20, 1011012, 32767); // CANCEL + AddButton(170, 290, 4005, 4007, 0); + AddHtmlLocalized(195, 290, 55, 20, 1011012, 32767); // CANCEL } public override void OnResponse(NetState sender, in RelayInfo info) diff --git a/Projects/UOContent/Multis/Houses/HousePackets.cs b/Projects/UOContent/Multis/Houses/HousePackets.cs index b01befb59..7f8308c0d 100644 --- a/Projects/UOContent/Multis/Houses/HousePackets.cs +++ b/Projects/UOContent/Multis/Houses/HousePackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: HousePackets.cs * * * @@ -119,7 +119,7 @@ public static class HousePackets int z = mte.OffsetZ; var floor = TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Height <= 0; - var plane = z switch + int plane = z switch { 0 => 0, 7 => 1, @@ -195,7 +195,7 @@ public static class HousePackets continue; } - var size = i switch + int size = i switch { 0 => planeLength, < 5 => (width - 1) * (height - 2) * 2, @@ -208,7 +208,7 @@ public static class HousePackets writer.Write((byte)(0x20 | i)); writer.Write((byte)size); - WritePacked(source, ref writer, out var destLength); + WritePacked(source, ref writer, out int destLength); totalLength += 4 + destLength; } @@ -224,7 +224,7 @@ public static class HousePackets writer.Write((byte)(9 + index++)); writer.Write((byte)size); - WritePacked(source, ref writer, out var destLength); + WritePacked(source, ref writer, out int destLength); totalLength += 4 + destLength; totalPlanes++; } diff --git a/Projects/UOContent/Multis/Houses/HousePlacement.cs b/Projects/UOContent/Multis/Houses/HousePlacement.cs index 86a364048..d97f734ab 100644 --- a/Projects/UOContent/Multis/Houses/HousePlacement.cs +++ b/Projects/UOContent/Multis/Houses/HousePlacement.cs @@ -1,9 +1,7 @@ -using System; using System.Collections.Generic; using Server.Collections; using Server.Regions; using Server.Spells; -using Server.Systems.FeatureFlags; namespace Server.Multis { @@ -39,9 +37,7 @@ namespace Server.Multis 0x0150, 0x015C // Furrows }; - public static HousePlacementResult Check( - Mobile from, int multiID, Point3D center, out List toMove, Direction houseFacing = Direction.South - ) + public static HousePlacementResult Check(Mobile from, int multiID, Point3D center, out List toMove) { // If this spot is considered valid, every item and mobile in this list will be moved under the house sign toMove = new List(); @@ -53,11 +49,6 @@ namespace Server.Multis return HousePlacementResult.BadLand; // A house cannot go here } - if (!ContentFeatureFlags.HousePlacement && from.AccessLevel < FeatureFlagSettings.RequiredAccessLevel) - { - return HousePlacementResult.BadRegionTemp; - } - if (from.AccessLevel >= AccessLevel.GameMaster) { return HousePlacementResult.Valid; // Staff can place anywhere @@ -86,7 +77,7 @@ namespace Server.Multis HouseFoundation.AddStairsTo(ref mcl); // this is a AOS house, add the stairs } - // Location of the northwest-most corner of the house + // Location of the nortwest-most corner of the house var start = new Point3D(center.X + mcl.Min.X, center.Y + mcl.Min.Y, center.Z); // These are storage lists. They hold items and mobiles found in the map for further processing @@ -94,7 +85,7 @@ namespace Server.Multis var mobiles = new List(); // These are also storage lists. They hold location values indicating the yard and border locations. - List borders = []; + List yard = new(), borders = new(); /* RULES: * @@ -130,7 +121,7 @@ namespace Server.Multis return HousePlacementResult.BadRegionTemp; } - if (reg.IsPartOf()) + if (reg.IsPartOf() || reg.IsPartOf()) { return HousePlacementResult.BadRegionHidden; } @@ -227,18 +218,16 @@ namespace Server.Multis { var id = item.ItemData; - if (addTileTop <= item.Z || item.Z + id.CalcHeight <= addTileZ) + if (addTileTop > item.Z && item.Z + id.CalcHeight > addTileZ) { - continue; - } - - if (item.Movable) - { - toMove.Add(item); - } - else if (id.Impassable || id.Surface && !id.Background) - { - return HousePlacementResult.BadItem; // Broke rule #2 + if (item.Movable) + { + toMove.Add(item); + } + else if (id.Impassable || id.Surface && !id.Background) + { + return HousePlacementResult.BadItem; // Broke rule #2 + } } } @@ -268,9 +257,17 @@ namespace Server.Multis if (hasFoundation) { - if (!CheckYard(map, tileX, tileY, YardSize, houseFacing)) + for (var xOffset = -1; xOffset <= 1; ++xOffset) { - return HousePlacementResult.BadStatic; // Broke rule #3 + for (var yOffset = -YardSize; yOffset <= YardSize; ++yOffset) + { + var yardPoint = new Point2D(tileX + xOffset, tileY + yOffset); + + if (!yard.Contains(yardPoint)) + { + yard.Add(yardPoint); + } + } } for (var xOffset = -1; xOffset <= 1; ++xOffset) @@ -367,73 +364,37 @@ namespace Server.Multis } } - return HousePlacementResult.Valid; - } - - private static bool CheckYard(Map map, int tileX, int tileY, int yardSize, Direction houseFacing) - { - var isSouthFacing = (houseFacing & Direction.South) != 0; - var isEastFacing = (houseFacing & Direction.East) != 0; - - for (var xOffset = -yardSize; xOffset <= yardSize; ++xOffset) + for (var i = 0; i < yard.Count; i++) { - var absXOffset = Math.Abs(xOffset); - for (var yOffset = -yardSize; yOffset <= yardSize; ++yOffset) + var yardPoint = yard[i]; + + foreach (var house in map.GetMultisInSector(yardPoint)) { - var absYOffset = Math.Abs(yOffset); - var yardPoint = new Point2D(tileX + xOffset, tileY + yOffset); - - var inSouthYard = yOffset > 0 && yOffset <= yardSize && absXOffset <= 1; - var inEastYard = xOffset > 0 && xOffset <= yardSize && absYOffset <= 1; - var inNorthYard = yOffset < 0 && yOffset >= -yardSize && absXOffset <= 1; - var inWestYard = xOffset < 0 && xOffset >= -yardSize && absYOffset <= 1; - - // Check each house at this point - foreach (var house in map.GetMultisInSector(yardPoint)) + if (house.Contains(yard[i])) { - if (!house.Contains(yardPoint)) - { - continue; - } - - var existingHouseFacing = house.HouseDirection; - var existingHouseIsSouthFacing = (existingHouseFacing & Direction.South) != 0; - var existingHouseIsEastFacing = (existingHouseFacing & Direction.East) != 0; - - // Sub-Rule 1: No houses within immediate proximity (1 tile radius) - if (absXOffset <= 1 && absYOffset <= 1) - { - return false; - } - - // Sub-Rule 2: If we're south facing, protect our south yard - if (isSouthFacing && inSouthYard) - { - return false; - } - - // Sub-Rule 3: If we're east facing, protect our east yard - if (isEastFacing && inEastYard) - { - return false; - } - - // Sub-Rule 4: If there's a south-facing house to our north, respect its yard - if (inNorthYard && existingHouseIsSouthFacing) - { - return false; - } - - // Sub-Rule 5: If there's an east-facing house to our west, respect its yard - if (inWestYard && existingHouseIsEastFacing) - { - return false; - } + return HousePlacementResult.BadStatic; // Broke rule #3 } } } - return true; + // TODO: Should we check for MultiTilesAt each yard point? + // for (var i = 0; i < yard.Count; i++) + // { + // var yardPoint = yard[i]; + // + // foreach (var tiles in map.GetMultiTilesAt(yardPoint)) + // { + // for (int j = 0; j < tiles.Length; ++j) + // { + // if ((TileData.ItemTable[tiles[j].ID & TileData.MaxItemValue].Flags & (TileFlag.Impassable | TileFlag.Surface)) != 0) + // { + // return HousePlacementResult.BadStatic; // Broke rule #3 + // } + // } + // } + // } + + return HousePlacementResult.Valid; } } } diff --git a/Projects/UOContent/Multis/Houses/HousePlacementTool.cs b/Projects/UOContent/Multis/Houses/HousePlacementTool.cs index 4ccf68593..18f455f3c 100644 --- a/Projects/UOContent/Multis/Houses/HousePlacementTool.cs +++ b/Projects/UOContent/Multis/Houses/HousePlacementTool.cs @@ -14,61 +14,64 @@ namespace Server.Items; public partial class HousePlacementTool : Item { [Constructible] - public HousePlacementTool() : base(0x14F6) => LootType = LootType.Blessed; + public HousePlacementTool() : base(0x14F6) + { + Weight = 3.0; + LootType = LootType.Blessed; + } - public override double DefaultWeight => 3.0; public override int LabelNumber => 1060651; // a house placement tool public override void OnDoubleClick(Mobile from) { - if (!IsChildOf(from.Backpack)) + if (IsChildOf(from.Backpack)) + { + from.SendGump(new HousePlacementCategoryGump(from)); + } + else { from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. - return; } - - from.SendGump(new HousePlacementCategoryGump()); } } -public class HousePlacementCategoryGump : StaticGump +public class HousePlacementCategoryGump : Gump { private const int LabelColor = 0x7FFF; + private const int LabelColorDisabled = 0x4210; + private readonly Mobile _from; public override bool Singleton => true; - public HousePlacementCategoryGump() : base(50, 50) + public HousePlacementCategoryGump(Mobile from) : base(50, 50) { - } + _from = from; - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(0, 0, 270, 145, 5054); + AddBackground(0, 0, 270, 145, 5054); - builder.AddImageTiled(10, 10, 250, 125, 2624); - builder.AddAlphaRegion(10, 10, 250, 125); + AddImageTiled(10, 10, 250, 125, 2624); + AddAlphaRegion(10, 10, 250, 125); - builder.AddHtmlLocalized(10, 10, 250, 20, 1060239, LabelColor); //
HOUSE PLACEMENT TOOL
+ AddHtmlLocalized(10, 10, 250, 20, 1060239, LabelColor); //
HOUSE PLACEMENT TOOL
- builder.AddButton(10, 110, 4017, 4019, 0); - builder.AddHtmlLocalized(45, 110, 150, 20, 3000363, LabelColor); // Close + AddButton(10, 110, 4017, 4019, 0); + AddHtmlLocalized(45, 110, 150, 20, 3000363, LabelColor); // Close - builder.AddButton(10, 40, 4005, 4007, 1); - builder.AddHtmlLocalized(45, 40, 200, 20, 1060390, LabelColor); // Classic Houses + AddButton(10, 40, 4005, 4007, 1); + AddHtmlLocalized(45, 40, 200, 20, 1060390, LabelColor); // Classic Houses - builder.AddButton(10, 60, 4005, 4007, 2); - builder.AddHtmlLocalized(45, 60, 200, 20, 1060391, LabelColor); // 2-Story Customizable Houses + AddButton(10, 60, 4005, 4007, 2); + AddHtmlLocalized(45, 60, 200, 20, 1060391, LabelColor); // 2-Story Customizable Houses - builder.AddButton(10, 80, 4005, 4007, 3); - builder.AddHtmlLocalized(45, 80, 200, 20, 1060392, LabelColor); // 3-Story Customizable Houses + AddButton(10, 80, 4005, 4007, 3); + AddHtmlLocalized(45, 80, 200, 20, 1060392, LabelColor); // 3-Story Customizable Houses } public override void OnResponse(NetState sender, in RelayInfo info) { - var from = sender.Mobile; - if (!from.CheckAlive() || from.Backpack?.FindItemByType() == null) + if (!_from.CheckAlive() || _from.Backpack?.FindItemByType() == null) { return; } @@ -78,31 +81,30 @@ public class HousePlacementCategoryGump : StaticGump case 1: // Classic Houses { var entry = Core.EJ ? HousePlacementEntry.HousesEJ : HousePlacementEntry.ClassicHouses; - from.SendGump(new HousePlacementListGump(from, entry)); + _from.SendGump(new HousePlacementListGump(_from, entry)); break; } case 2: // 2-Story Customizable Houses { - from.SendGump(new HousePlacementListGump(from, HousePlacementEntry.TwoStoryFoundations)); + _from.SendGump(new HousePlacementListGump(_from, HousePlacementEntry.TwoStoryFoundations)); break; } case 3: // 3-Story Customizable Houses { - from.SendGump(new HousePlacementListGump(from, HousePlacementEntry.ThreeStoryFoundations)); + _from.SendGump(new HousePlacementListGump(_from, HousePlacementEntry.ThreeStoryFoundations)); break; } } } } -public class HousePlacementListGump : DynamicGump +public class HousePlacementListGump : Gump { private const int LabelColor = 0x7FFF; private const int LabelHue = 0x480; - - private readonly Mobile _from; private readonly HousePlacementEntry[] _entries; + private readonly Mobile _from; public override bool Singleton => true; @@ -110,72 +112,70 @@ public class HousePlacementListGump : DynamicGump { _from = from; _entries = entries; - } - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(0, 0, 520, 420, 5054); + AddBackground(0, 0, 520, 420, 5054); - builder.AddImageTiled(10, 10, 500, 20, 2624); - builder.AddAlphaRegion(10, 10, 500, 20); + AddImageTiled(10, 10, 500, 20, 2624); + AddAlphaRegion(10, 10, 500, 20); - builder.AddHtmlLocalized(10, 10, 500, 20, 1060239, LabelColor); //
HOUSE PLACEMENT TOOL
+ AddHtmlLocalized(10, 10, 500, 20, 1060239, LabelColor); //
HOUSE PLACEMENT TOOL
- builder.AddImageTiled(10, 40, 500, 20, 2624); - builder.AddAlphaRegion(10, 40, 500, 20); + AddImageTiled(10, 40, 500, 20, 2624); + AddAlphaRegion(10, 40, 500, 20); - builder.AddHtmlLocalized(50, 40, 225, 20, 1060235, LabelColor); // House Description - builder.AddHtmlLocalized(275, 40, 75, 20, 1060236, LabelColor); // Storage - builder.AddHtmlLocalized(350, 40, 75, 20, 1060237, LabelColor); // Lockdowns - builder.AddHtmlLocalized(425, 40, 75, 20, 1060034, LabelColor); // Cost + AddHtmlLocalized(50, 40, 225, 20, 1060235, LabelColor); // House Description + AddHtmlLocalized(275, 40, 75, 20, 1060236, LabelColor); // Storage + AddHtmlLocalized(350, 40, 75, 20, 1060237, LabelColor); // Lockdowns + AddHtmlLocalized(425, 40, 75, 20, 1060034, LabelColor); // Cost - builder.AddImageTiled(10, 70, 500, 280, 2624); - builder.AddAlphaRegion(10, 70, 500, 280); + AddImageTiled(10, 70, 500, 280, 2624); + AddAlphaRegion(10, 70, 500, 280); - builder.AddImageTiled(10, 360, 500, 20, 2624); - builder.AddAlphaRegion(10, 360, 500, 20); + AddImageTiled(10, 360, 500, 20, 2624); + AddAlphaRegion(10, 360, 500, 20); - builder.AddHtmlLocalized(10, 360, 250, 20, 1060645, LabelColor); // Bank Balance: - builder.AddLabel(250, 360, LabelHue, Banker.GetBalance(_from).ToString()); + AddHtmlLocalized(10, 360, 250, 20, 1060645, LabelColor); // Bank Balance: + AddLabel(250, 360, LabelHue, Banker.GetBalance(from).ToString()); - builder.AddImageTiled(10, 390, 500, 20, 2624); - builder.AddAlphaRegion(10, 390, 500, 20); + AddImageTiled(10, 390, 500, 20, 2624); + AddAlphaRegion(10, 390, 500, 20); - builder.AddButton(10, 390, 4017, 4019, 0); - builder.AddHtmlLocalized(50, 390, 100, 20, 3000363, LabelColor); // Close + AddButton(10, 390, 4017, 4019, 0); + AddHtmlLocalized(50, 390, 100, 20, 3000363, LabelColor); // Close - for (var i = 0; i < _entries.Length; ++i) + for (var i = 0; i < entries.Length; ++i) { - var page = Math.DivRem(i, 14, out var index) + 1; + var page = 1 + i / 14; + var index = i % 14; if (index == 0) { if (page > 1) { - builder.AddButton(450, 390, 4005, 4007, 0, GumpButtonType.Page, page); - builder.AddHtmlLocalized(400, 390, 100, 20, 3000406, LabelColor); // Next + AddButton(450, 390, 4005, 4007, 0, GumpButtonType.Page, page); + AddHtmlLocalized(400, 390, 100, 20, 3000406, LabelColor); // Next } - builder.AddPage(page); + AddPage(page); if (page > 1) { - builder.AddButton(200, 390, 4014, 4016, 0, GumpButtonType.Page, page - 1); - builder.AddHtmlLocalized(250, 390, 100, 20, 3000405, LabelColor); // Previous + AddButton(200, 390, 4014, 4016, 0, GumpButtonType.Page, page - 1); + AddHtmlLocalized(250, 390, 100, 20, 3000405, LabelColor); // Previous } } - var entry = _entries[i]; + var entry = entries[i]; var y = 70 + index * 20; - builder.AddButton(10, y, 4005, 4007, 1 + i); - builder.AddHtmlLocalized(50, y, 225, 20, entry.Description, LabelColor); - builder.AddLabel(275, y, LabelHue, entry.Storage.ToString()); - builder.AddLabel(350, y, LabelHue, entry.Lockdowns.ToString()); - builder.AddLabel(425, y, LabelHue, entry.Cost.ToString()); + AddButton(10, y, 4005, 4007, 1 + i); + AddHtmlLocalized(50, y, 225, 20, entry.Description, LabelColor); + AddLabel(275, y, LabelHue, entry.Storage.ToString()); + AddLabel(350, y, LabelHue, entry.Lockdowns.ToString()); + AddLabel(425, y, LabelHue, entry.Cost.ToString()); } } @@ -188,17 +188,20 @@ public class HousePlacementListGump : DynamicGump var index = info.ButtonID - 1; - if (index < 0 || index >= _entries.Length) + if (index >= 0 && index < _entries.Length) { - _from.SendGump(new HousePlacementCategoryGump()); - } - else if (_from.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse(_from)) - { - _from.SendLocalizedMessage(501271); // You already own a house, you may not place another! + if (_from.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse(_from)) + { + _from.SendLocalizedMessage(501271); // You already own a house, you may not place another! + } + else + { + _from.Target = new NewHousePlacementTarget(_entries, _entries[index]); + } } else { - _from.Target = new NewHousePlacementTarget(_entries, _entries[index]); + _from.SendGump(new HousePlacementCategoryGump(_from)); } } } @@ -233,7 +236,7 @@ public class NewHousePlacementTarget : MultiTarget return; } - var p = ip switch + Point3D p = ip switch { Item item => item.GetWorldTop(), Mobile m => m.Location, @@ -299,7 +302,7 @@ public class HousePlacementEntry public HousePlacementEntry( Type type, int description, int storage, int lockdowns, int newStorage, int newLockdowns, - int vendors, int cost, int xOffset, int yOffset, int zOffset, int multiID, Direction direction = Direction.South + int vendors, int cost, int xOffset, int yOffset, int zOffset, int multiID ) { Type = type; @@ -314,7 +317,6 @@ public class HousePlacementEntry Offset = new Point3D(xOffset, yOffset, zOffset); MultiID = multiID; - HouseDirection = direction; } public Type Type { get; } @@ -331,8 +333,6 @@ public class HousePlacementEntry public Point3D Offset { get; } - public Direction HouseDirection { get; } - public static HousePlacementEntry[] ClassicHouses { get; } = { new(typeof(SmallOldHouse), 1011303, 425, 212, 489, 244, 10, 37000, 0, 4, 0, 0x0064), @@ -1980,7 +1980,7 @@ public class HousePlacementEntry prevHouse.Delete(); - var res = HousePlacement.Check(from, MultiID, center, out var toMove, HouseDirection); + var res = HousePlacement.Check(from, MultiID, center, out var toMove); switch (res) { @@ -2007,18 +2007,21 @@ public class HousePlacementEntry $"{Cost} gold would have been withdrawn from your bank if you were not a GM." ); } - else if (Banker.Withdraw(from, Cost)) - { - // ~1_AMOUNT~ gold has been withdrawn from your bank box. - from.SendLocalizedMessage(1060398, Cost.ToString()); - } else { - house.RemoveKeys(from); - house.Delete(); - // You do not have the funds available in your bank box to purchase this house. Try placing a smaller house, or adding gold or checks to your bank box. - from.SendLocalizedMessage(1060646); - return; + if (Banker.Withdraw(from, Cost)) + { + // ~1_AMOUNT~ gold has been withdrawn from your bank box. + from.SendLocalizedMessage(1060398, Cost.ToString()); + } + else + { + house.RemoveKeys(from); + house.Delete(); + // You do not have the funds available in your bank box to purchase this house. Try placing a smaller house, or adding gold or checks to your bank box. + from.SendLocalizedMessage(1060646); + return; + } } house.MoveToWorld(center, from.Map); @@ -2083,7 +2086,7 @@ public class HousePlacementEntry } var center = new Point3D(p.X - Offset.X, p.Y - Offset.Y, p.Z - Offset.Z); - var res = HousePlacement.Check(from, MultiID, center, out var toMove, HouseDirection); + var res = HousePlacement.Check(from, MultiID, center, out var toMove); switch (res) { diff --git a/Projects/UOContent/Multis/Houses/HouseSign.cs b/Projects/UOContent/Multis/Houses/HouseSign.cs index 750651dbd..255077418 100644 --- a/Projects/UOContent/Multis/Houses/HouseSign.cs +++ b/Projects/UOContent/Multis/Houses/HouseSign.cs @@ -128,11 +128,11 @@ public partial class HouseSign : Item if (Owner.IsAosRules) { - HouseGumpAOS.DisplayTo(m, Owner, HouseGumpPageAOS.Information); + m.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Information, m, Owner)); } else { - HouseGump.DisplayTo(m, Owner); + m.SendGump(new HouseGump(m, Owner)); } } @@ -237,7 +237,7 @@ public partial class HouseSign : Item } else { - HouseGumpAOS.DisplayTo(from, sign.Owner, HouseGumpPageAOS.Vendors); + from.SendGump(new HouseGumpAOS(HouseGumpPageAOS.Vendors, from, sign.Owner)); } } } @@ -263,7 +263,7 @@ public partial class HouseSign : Item } else { - VendorInventoryGump.DisplayTo(from, sign.Owner); + from.SendGump(new VendorInventoryGump(sign.Owner, from)); } } } diff --git a/Projects/UOContent/Multis/Houses/Houses.cs b/Projects/UOContent/Multis/Houses/Houses.cs index 909ce13fb..b8f5c9a94 100644 --- a/Projects/UOContent/Multis/Houses/Houses.cs +++ b/Projects/UOContent/Multis/Houses/Houses.cs @@ -1,390 +1,586 @@ -using ModernUO.Serialization; using Server.Items; using Server.Multis.Deeds; -namespace Server.Multis; - -[SerializationGenerator(0, false)] -public partial class SmallOldHouse : BaseHouse +namespace Server.Multis { - public static readonly Rectangle2D[] AreaArray = [new(-3, -3, 7, 7), new(-1, 4, 3, 1)]; - - public SmallOldHouse(Mobile owner, int id) : base(id, owner, 425, 3) + public class SmallOldHouse : BaseHouse { - var keyValue = CreateKeys(owner); + public static Rectangle2D[] AreaArray = { new(-3, -3, 7, 7), new(-1, 4, 3, 1) }; - AddSouthDoor(0, 3, 7, keyValue); - - SetSign(2, 4, 5); - } - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(2, 4, 0); - - public override int DefaultPrice => 43800; - - public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[0]; - - public override HouseDeed GetDeed() - { - return ItemID switch + public SmallOldHouse(Mobile owner, int id) : base(id, owner, 425, 3) { - 0x64 => new StonePlasterHouseDeed(), - 0x66 => new FieldStoneHouseDeed(), - 0x68 => new SmallBrickHouseDeed(), - 0x6A => new WoodHouseDeed(), - 0x6C => new WoodPlasterHouseDeed(), - _ => new ThatchedRoofCottageDeed() - }; - } -} + var keyValue = CreateKeys(owner); -[SerializationGenerator(0, false)] -public partial class GuildHouse : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray = [new(-7, -7, 14, 14), new(-2, 7, 4, 1)]; + AddSouthDoor(0, 3, 7, keyValue); - public GuildHouse(Mobile owner) : base(0x74, owner, 1100, 8) - { - var keyValue = CreateKeys(owner); - - AddSouthDoors(-1, 6, 7, keyValue); - - SetSign(4, 8, 16); - - AddSouthDoor(-3, -1, 7); - AddSouthDoor(3, -1, 7); - } - - public override int DefaultPrice => 144500; - - public override HousePlacementEntry ConvertEntry => HousePlacementEntry.ThreeStoryFoundations[20]; - public override int ConvertOffsetX => -1; - public override int ConvertOffsetY => -1; - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(4, 8, 0); - - public override HouseDeed GetDeed() => new BrickHouseDeed(); -} - -[SerializationGenerator(0, false)] -public partial class TwoStoryHouse : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray = - [new(-7, 0, 14, 7), new(-7, -7, 9, 7), new(-4, 7, 4, 1)]; - - public TwoStoryHouse(Mobile owner, int id) : base(id, owner, 1370, 10) - { - var keyValue = CreateKeys(owner); - - AddSouthDoors(-3, 6, 7, keyValue); - - SetSign(2, 8, 16); - - AddSouthDoor(-3, 0, 7); - AddSouthDoor(id == 0x76 ? -2 : -3, 0, 27); - } - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(2, 8, 0); - - public override int DefaultPrice => 192400; - - public override HouseDeed GetDeed() - { - return ItemID switch - { - 0x76 => new TwoStoryWoodPlasterHouseDeed(), - _ => new TwoStoryStonePlasterHouseDeed() - }; - } -} - -[SerializationGenerator(0, false)] -public partial class Tower : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray = - [ - new(-7, -7, 16, 14), new(-1, 7, 4, 2), new(-11, 0, 4, 7), - new(9, 0, 4, 7) - ]; - - public Tower(Mobile owner) : base(0x7A, owner, 2119, 15) - { - var keyValue = CreateKeys(owner); - - AddSouthDoors(false, 0, 6, 6, keyValue); - - SetSign(5, 8, 16); - - AddSouthDoor(false, 3, -2, 6); - AddEastDoor(false, 1, 4, 26); - AddEastDoor(false, 1, 4, 46); - } - - public override int DefaultPrice => 433200; - - public override HousePlacementEntry ConvertEntry => HousePlacementEntry.ThreeStoryFoundations[37]; - public override int ConvertOffsetY => -1; - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(5, 8, 0); - - public override HouseDeed GetDeed() => new TowerDeed(); -} - -[SerializationGenerator(0, false)] -public partial class Keep : BaseHouse // warning: ODD shape! -{ - public static readonly Rectangle2D[] AreaArray = - [ - new(-11, -11, 7, 8), new(-11, 5, 7, 8), new(6, -11, 7, 8), - new(6, 5, 7, 8), new(-9, -3, 5, 8), new(6, -3, 5, 8), - new(-4, -9, 10, 20), new(-1, 11, 4, 1) - ]; - - public Keep(Mobile owner) : base(0x7C, owner, 2625, 18) - { - var keyValue = CreateKeys(owner); - - AddSouthDoors(false, 0, 10, 6, keyValue); - - SetSign(5, 12, 16); - } - - public override int DefaultPrice => 665200; - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(5, 13, 0); - - public override HouseDeed GetDeed() => new KeepDeed(); -} - -[SerializationGenerator(0, false)] -public partial class Castle : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray = [new(-15, -15, 31, 31), new(-1, 16, 4, 1)]; - - public Castle(Mobile owner) : base(0x7E, owner, 4076, 28) - { - var keyValue = CreateKeys(owner); - - AddSouthDoors(false, 0, 15, 6, keyValue); - - SetSign(5, 17, 16); - - AddSouthDoors(false, 0, 11, 6, true); - AddSouthDoors(false, 0, 5, 6, false); - AddSouthDoors(false, -1, -11, 6, false); - } - - public override int DefaultPrice => 1022800; - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(5, 17, 0); - - public override HouseDeed GetDeed() => new CastleDeed(); -} - -[SerializationGenerator(0, false)] -public partial class LargePatioHouse : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray = [new(-7, -7, 15, 14), new(-5, 7, 4, 1)]; - - public LargePatioHouse(Mobile owner) : base(0x8C, owner, 1100, 8) - { - var keyValue = CreateKeys(owner); - - AddSouthDoors(-4, 6, 7, keyValue); - - SetSign(1, 8, 16); - - AddEastDoor(1, 4, 7); - - // Patio doors are external to prevent res-looting - AddEastDoor(1, -4, 7, keyValue); - AddSouthDoor(4, -1, 7, keyValue); - } - - public override int DefaultPrice => 152800; - - public override HousePlacementEntry ConvertEntry => HousePlacementEntry.ThreeStoryFoundations[29]; - public override int ConvertOffsetY => -1; - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(1, 8, 0); - - public override HouseDeed GetDeed() => new LargePatioDeed(); -} - -[SerializationGenerator(0, false)] -public partial class LargeMarbleHouse : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray = [new(-7, -7, 15, 14), new(-6, 7, 6, 1)]; - - public LargeMarbleHouse(Mobile owner) : base(0x96, owner, 1370, 10) - { - var keyValue = CreateKeys(owner); - - AddSouthDoors(false, -4, 3, 4, keyValue); - - SetSign(1, 8, 11); - } - - public override int DefaultPrice => 192000; - - public override HousePlacementEntry ConvertEntry => HousePlacementEntry.ThreeStoryFoundations[29]; - public override int ConvertOffsetY => -1; - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(1, 8, 0); - - public override HouseDeed GetDeed() => new LargeMarbleDeed(); -} - -[SerializationGenerator(0, false)] -public partial class SmallTower : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray = [new(-3, -3, 8, 7), new(2, 4, 3, 1)]; - - public SmallTower(Mobile owner) : base(0x98, owner, 580, 4) - { - var keyValue = CreateKeys(owner); - - AddSouthDoor(false, 3, 3, 6, keyValue); - - SetSign(1, 4, 5); - } - - public override int DefaultPrice => 88500; - - public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[6]; - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(1, 4, 0); - - public override HouseDeed GetDeed() => new SmallTowerDeed(); -} - -[SerializationGenerator(0, false)] -public partial class LogCabin : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray = [new(-3, -6, 8, 13)]; - - public LogCabin(Mobile owner) : base(0x9A, owner, 1100, 8) - { - var keyValue = CreateKeys(owner); - - AddSouthDoor(1, 4, 8, keyValue); - - SetSign(5, 8, 20); - - // Balcony door is external to prevent res-looting - AddSouthDoor(1, 0, 29, keyValue); - } - - public override int DefaultPrice => 97800; - - public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[12]; - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(5, 8, 0); - - public override HouseDeed GetDeed() => new LogCabinDeed(); -} - -[SerializationGenerator(0, false)] -public partial class SandStonePatio : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray = [new(-5, -4, 12, 8), new(-2, 4, 3, 1)]; - - public SandStonePatio(Mobile owner) : base(0x9C, owner, 850, 6) - { - var keyValue = CreateKeys(owner); - - AddSouthDoor(-1, 3, 6, keyValue); - - SetSign(4, 6, 24); - } - - public override int DefaultPrice => 90900; - - public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[35]; - public override int ConvertOffsetY => -1; - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(4, 6, 0); - - public override HouseDeed GetDeed() => new SandstonePatioDeed(); -} - -[SerializationGenerator(0, false)] -public partial class TwoStoryVilla : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray = [new(-5, -5, 11, 11), new(2, 6, 4, 1)]; - - public TwoStoryVilla(Mobile owner) : base(0x9E, owner, 1100, 8) - { - var keyValue = CreateKeys(owner); - - AddSouthDoors(3, 1, 5, keyValue); - - SetSign(3, 8, 24); - - // Balcony door is external to prevent res-looting - AddEastDoor(1, 0, 25, keyValue); - AddSouthDoor(-3, -1, 25); - } - - public override int DefaultPrice => 136500; - - public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[31]; - - public override Rectangle2D[] Area => AreaArray; - public override Point3D BaseBanLocation => new(3, 8, 0); - - public override HouseDeed GetDeed() => new VillaDeed(); -} - -[SerializationGenerator(0, false)] -public partial class SmallShop : BaseHouse -{ - public static readonly Rectangle2D[] AreaArray1 = [new(-3, -3, 7, 7), new(-1, 4, 4, 1)]; - public static readonly Rectangle2D[] AreaArray2 = [new(-3, -3, 7, 7), new(-2, 4, 3, 1)]; - - public SmallShop(Mobile owner, int id) : base(id, owner, 425, 3) - { - var keyValue = CreateKeys(owner); - - var door = MakeDoor(false, DoorFacing.EastCW); - - door.Locked = true; - door.KeyValue = keyValue; - - if (door is BaseHouseDoor houseDoor) - { - houseDoor.Facing = DoorFacing.EastCCW; + SetSign(2, 4, 5); } - AddDoor(door, -2, 0, id == 0xA2 ? 24 : 27); + public SmallOldHouse(Serial serial) : base(serial) + { + } - SetSign(3, 4, 7 - (id == 0xA2 ? 2 : 0)); + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(2, 4, 0); + + public override int DefaultPrice => 43800; + + public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[0]; + + public override HouseDeed GetDeed() + { + return ItemID switch + { + 0x64 => new StonePlasterHouseDeed(), + 0x66 => new FieldStoneHouseDeed(), + 0x68 => new SmallBrickHouseDeed(), + 0x6A => new WoodHouseDeed(), + 0x6C => new WoodPlasterHouseDeed(), + 0x6E => new ThatchedRoofCottageDeed(), + _ => new ThatchedRoofCottageDeed() + }; + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } } - public override Rectangle2D[] Area => ItemID == 0x40A2 ? AreaArray1 : AreaArray2; - public override Point3D BaseBanLocation => new(3, 4, 0); - - public override int DefaultPrice => 63000; - - public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[0]; - - public override HouseDeed GetDeed() + public class GuildHouse : BaseHouse { - return ItemID switch + public static Rectangle2D[] AreaArray = { new(-7, -7, 14, 14), new(-2, 7, 4, 1) }; + + public GuildHouse(Mobile owner) : base(0x74, owner, 1100, 8) { - 0xA0 => new StoneWorkshopDeed(), - _ => new MarbleWorkshopDeed() + var keyValue = CreateKeys(owner); + + AddSouthDoors(-1, 6, 7, keyValue); + + SetSign(4, 8, 16); + + AddSouthDoor(-3, -1, 7); + AddSouthDoor(3, -1, 7); + } + + public GuildHouse(Serial serial) : base(serial) + { + } + + public override int DefaultPrice => 144500; + + public override HousePlacementEntry ConvertEntry => HousePlacementEntry.ThreeStoryFoundations[20]; + public override int ConvertOffsetX => -1; + public override int ConvertOffsetY => -1; + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(4, 8, 0); + + public override HouseDeed GetDeed() => new BrickHouseDeed(); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class TwoStoryHouse : BaseHouse + { + public static Rectangle2D[] AreaArray = + { new(-7, 0, 14, 7), new(-7, -7, 9, 7), new(-4, 7, 4, 1) }; + + public TwoStoryHouse(Mobile owner, int id) : base(id, owner, 1370, 10) + { + var keyValue = CreateKeys(owner); + + AddSouthDoors(-3, 6, 7, keyValue); + + SetSign(2, 8, 16); + + AddSouthDoor(-3, 0, 7); + AddSouthDoor(id == 0x76 ? -2 : -3, 0, 27); + } + + public TwoStoryHouse(Serial serial) : base(serial) + { + } + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(2, 8, 0); + + public override int DefaultPrice => 192400; + + public override HouseDeed GetDeed() + { + return ItemID switch + { + 0x76 => new TwoStoryWoodPlasterHouseDeed(), + 0x78 => new TwoStoryStonePlasterHouseDeed(), + _ => new TwoStoryStonePlasterHouseDeed() + }; + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class Tower : BaseHouse + { + public static Rectangle2D[] AreaArray = + { + new(-7, -7, 16, 14), new(-1, 7, 4, 2), new(-11, 0, 4, 7), + new(9, 0, 4, 7) }; + + public Tower(Mobile owner) : base(0x7A, owner, 2119, 15) + { + var keyValue = CreateKeys(owner); + + AddSouthDoors(false, 0, 6, 6, keyValue); + + SetSign(5, 8, 16); + + AddSouthDoor(false, 3, -2, 6); + AddEastDoor(false, 1, 4, 26); + AddEastDoor(false, 1, 4, 46); + } + + public Tower(Serial serial) : base(serial) + { + } + + public override int DefaultPrice => 433200; + + public override HousePlacementEntry ConvertEntry => HousePlacementEntry.ThreeStoryFoundations[37]; + public override int ConvertOffsetY => -1; + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(5, 8, 0); + + public override HouseDeed GetDeed() => new TowerDeed(); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class Keep : BaseHouse // warning: ODD shape! + { + public static Rectangle2D[] AreaArray = + { + new(-11, -11, 7, 8), new(-11, 5, 7, 8), new(6, -11, 7, 8), + new(6, 5, 7, 8), new(-9, -3, 5, 8), new(6, -3, 5, 8), + new(-4, -9, 10, 20), new(-1, 11, 4, 1) + }; + + public Keep(Mobile owner) : base(0x7C, owner, 2625, 18) + { + var keyValue = CreateKeys(owner); + + AddSouthDoors(false, 0, 10, 6, keyValue); + + SetSign(5, 12, 16); + } + + public Keep(Serial serial) : base(serial) + { + } + + public override int DefaultPrice => 665200; + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(5, 13, 0); + + public override HouseDeed GetDeed() => new KeepDeed(); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class Castle : BaseHouse + { + public static Rectangle2D[] AreaArray = { new(-15, -15, 31, 31), new(-1, 16, 4, 1) }; + + public Castle(Mobile owner) : base(0x7E, owner, 4076, 28) + { + var keyValue = CreateKeys(owner); + + AddSouthDoors(false, 0, 15, 6, keyValue); + + SetSign(5, 17, 16); + + AddSouthDoors(false, 0, 11, 6, true); + AddSouthDoors(false, 0, 5, 6, false); + AddSouthDoors(false, -1, -11, 6, false); + } + + public Castle(Serial serial) : base(serial) + { + } + + public override int DefaultPrice => 1022800; + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(5, 17, 0); + + public override HouseDeed GetDeed() => new CastleDeed(); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class LargePatioHouse : BaseHouse + { + public static Rectangle2D[] AreaArray = { new(-7, -7, 15, 14), new(-5, 7, 4, 1) }; + + public LargePatioHouse(Mobile owner) : base(0x8C, owner, 1100, 8) + { + var keyValue = CreateKeys(owner); + + AddSouthDoors(-4, 6, 7, keyValue); + + SetSign(1, 8, 16); + + AddEastDoor(1, 4, 7); + AddEastDoor(1, -4, 7); + AddSouthDoor(4, -1, 7); + } + + public LargePatioHouse(Serial serial) : base(serial) + { + } + + public override int DefaultPrice => 152800; + + public override HousePlacementEntry ConvertEntry => HousePlacementEntry.ThreeStoryFoundations[29]; + public override int ConvertOffsetY => -1; + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(1, 8, 0); + + public override HouseDeed GetDeed() => new LargePatioDeed(); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class LargeMarbleHouse : BaseHouse + { + public static Rectangle2D[] AreaArray = { new(-7, -7, 15, 14), new(-6, 7, 6, 1) }; + + public LargeMarbleHouse(Mobile owner) : base(0x96, owner, 1370, 10) + { + var keyValue = CreateKeys(owner); + + AddSouthDoors(false, -4, 3, 4, keyValue); + + SetSign(1, 8, 11); + } + + public LargeMarbleHouse(Serial serial) : base(serial) + { + } + + public override int DefaultPrice => 192000; + + public override HousePlacementEntry ConvertEntry => HousePlacementEntry.ThreeStoryFoundations[29]; + public override int ConvertOffsetY => -1; + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(1, 8, 0); + + public override HouseDeed GetDeed() => new LargeMarbleDeed(); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class SmallTower : BaseHouse + { + public static Rectangle2D[] AreaArray = { new(-3, -3, 8, 7), new(2, 4, 3, 1) }; + + public SmallTower(Mobile owner) : base(0x98, owner, 580, 4) + { + var keyValue = CreateKeys(owner); + + AddSouthDoor(false, 3, 3, 6, keyValue); + + SetSign(1, 4, 5); + } + + public SmallTower(Serial serial) : base(serial) + { + } + + public override int DefaultPrice => 88500; + + public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[6]; + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(1, 4, 0); + + public override HouseDeed GetDeed() => new SmallTowerDeed(); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class LogCabin : BaseHouse + { + public static Rectangle2D[] AreaArray = { new(-3, -6, 8, 13) }; + + public LogCabin(Mobile owner) : base(0x9A, owner, 1100, 8) + { + var keyValue = CreateKeys(owner); + + AddSouthDoor(1, 4, 8, keyValue); + + SetSign(5, 8, 20); + + AddSouthDoor(1, 0, 29); + } + + public LogCabin(Serial serial) : base(serial) + { + } + + public override int DefaultPrice => 97800; + + public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[12]; + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(5, 8, 0); + + public override HouseDeed GetDeed() => new LogCabinDeed(); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class SandStonePatio : BaseHouse + { + public static Rectangle2D[] AreaArray = { new(-5, -4, 12, 8), new(-2, 4, 3, 1) }; + + public SandStonePatio(Mobile owner) : base(0x9C, owner, 850, 6) + { + var keyValue = CreateKeys(owner); + + AddSouthDoor(-1, 3, 6, keyValue); + + SetSign(4, 6, 24); + } + + public SandStonePatio(Serial serial) : base(serial) + { + } + + public override int DefaultPrice => 90900; + + public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[35]; + public override int ConvertOffsetY => -1; + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(4, 6, 0); + + public override HouseDeed GetDeed() => new SandstonePatioDeed(); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class TwoStoryVilla : BaseHouse + { + public static Rectangle2D[] AreaArray = { new(-5, -5, 11, 11), new(2, 6, 4, 1) }; + + public TwoStoryVilla(Mobile owner) : base(0x9E, owner, 1100, 8) + { + var keyValue = CreateKeys(owner); + + AddSouthDoors(3, 1, 5, keyValue); + + SetSign(3, 8, 24); + + AddEastDoor(1, 0, 25); + AddSouthDoor(-3, -1, 25); + } + + public TwoStoryVilla(Serial serial) : base(serial) + { + } + + public override int DefaultPrice => 136500; + + public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[31]; + + public override Rectangle2D[] Area => AreaArray; + public override Point3D BaseBanLocation => new(3, 8, 0); + + public override HouseDeed GetDeed() => new VillaDeed(); + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } + } + + public class SmallShop : BaseHouse + { + public static Rectangle2D[] AreaArray1 = { new(-3, -3, 7, 7), new(-1, 4, 4, 1) }; + public static Rectangle2D[] AreaArray2 = { new(-3, -3, 7, 7), new(-2, 4, 3, 1) }; + + public SmallShop(Mobile owner, int id) : base(id, owner, 425, 3) + { + var keyValue = CreateKeys(owner); + + var door = MakeDoor(false, DoorFacing.EastCW); + + door.Locked = true; + door.KeyValue = keyValue; + + if (door is BaseHouseDoor houseDoor) + { + houseDoor.Facing = DoorFacing.EastCCW; + } + + AddDoor(door, -2, 0, id == 0xA2 ? 24 : 27); + + // AddSouthDoor( false, -2, 0, 27 - (id == 0xA2 ? 3 : 0), keyValue ); + + SetSign(3, 4, 7 - (id == 0xA2 ? 2 : 0)); + } + + public SmallShop(Serial serial) : base(serial) + { + } + + public override Rectangle2D[] Area => ItemID == 0x40A2 ? AreaArray1 : AreaArray2; + public override Point3D BaseBanLocation => new(3, 4, 0); + + public override int DefaultPrice => 63000; + + public override HousePlacementEntry ConvertEntry => HousePlacementEntry.TwoStoryFoundations[0]; + + public override HouseDeed GetDeed() + { + return ItemID switch + { + 0xA0 => new StoneWorkshopDeed(), + 0xA2 => new MarbleWorkshopDeed(), + _ => new MarbleWorkshopDeed() + }; + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + var version = reader.ReadInt(); + } } } diff --git a/Projects/UOContent/Multis/Houses/MovingCrate.cs b/Projects/UOContent/Multis/Houses/MovingCrate.cs index 6e12b0035..7388323fd 100644 --- a/Projects/UOContent/Multis/Houses/MovingCrate.cs +++ b/Projects/UOContent/Multis/Houses/MovingCrate.cs @@ -1,294 +1,347 @@ using System; -using ModernUO.Serialization; +using System.Collections.Generic; using Server.Items; using Server.Network; -namespace Server.Multis; - -[SerializationGenerator(0)] -public partial class MovingCrate : Container +namespace Server.Multis { - private const int MaxItemsPerSubcontainer = 20; - private const int Rows = 3; - private const int Columns = 5; - private const int HorizontalSpacing = 25; - private const int VerticalSpacing = 25; - - private Timer _internalizeTimer; - - [SerializableField(0)] - private BaseHouse _house; - - public MovingCrate(BaseHouse house) : base(0xE3D) + public class MovingCrate : Container { - Hue = 0x8A5; - Movable = false; + public static readonly int MaxItemsPerSubcontainer = 20; + public static readonly int Rows = 3; + public static readonly int Columns = 5; + public static readonly int HorizontalSpacing = 25; + public static readonly int VerticalSpacing = 25; - House = house; - } + private Timer m_InternalizeTimer; - public override int LabelNumber => 1061690; // Packing Crate - - public override int DefaultMaxItems => 0; - public override int DefaultMaxWeight => 0; - - public override bool IsDecoContainer => false; - - /* - public override void AddNameProperties( ObjectPropertyList list ) - { - base.AddNameProperties( list ); - - if (House != null && House.InternalizedVendors.Count > 0) - list.Add( 1061833, House.InternalizedVendors.Count.ToString() ); // This packing crate contains ~1_COUNT~ vendors/barkeepers. - } - */ - - public override void DropItem(Item dropped) - { - // 1. Try to stack the item - foreach (var item in Items) + public MovingCrate(BaseHouse house) : base(0xE3D) { - if (item is PackingBox) + Hue = 0x8A5; + Movable = false; + + House = house; + } + + public MovingCrate(Serial serial) : base(serial) + { + } + + public override int LabelNumber => 1061690; // Packing Crate + + public BaseHouse House { get; set; } + + public override int DefaultMaxItems => 0; + public override int DefaultMaxWeight => 0; + + public override bool IsDecoContainer => false; + + /* + public override void AddNameProperties( ObjectPropertyList list ) + { + base.AddNameProperties( list ); + + if (House != null && House.InternalizedVendors.Count > 0) + list.Add( 1061833, House.InternalizedVendors.Count.ToString() ); // This packing crate contains ~1_COUNT~ vendors/barkeepers. + } + */ + + public override void DropItem(Item dropped) + { + // 1. Try to stack the item + foreach (var item in Items) { - var subItems = item.Items; - - for (var i = 0; i < subItems.Count; i++) + if (item is PackingBox) { - var subItem = subItems[i]; + var subItems = item.Items; - if (subItem is not Container && subItem.StackWith(null, dropped, false)) + for (var i = 0; i < subItems.Count; i++) { + var subItem = subItems[i]; + + if (subItem is not Container && subItem.StackWith(null, dropped, false)) + { + return; + } + } + } + } + + // 2. Try to drop the item into an existing container + foreach (var item in Items) + { + if (item is PackingBox packingBox) + { + Container box = packingBox; + var subItems = box.Items; + + if (subItems.Count < MaxItemsPerSubcontainer) + { + box.DropItem(dropped); return; } } } - } - // 2. Try to drop the item into an existing container - foreach (var item in Items) - { - if (item is PackingBox packingBox) + // 3. Drop the item into a new container + Container subContainer = new PackingBox(); + subContainer.DropItem(dropped); + + var location = GetFreeLocation(); + if (location != Point3D.Zero) { - var subItems = packingBox.Items; - - if (subItems.Count < MaxItemsPerSubcontainer) - { - packingBox.DropItem(dropped); - return; - } + AddItem(subContainer); + subContainer.Location = location; + } + else + { + base.DropItem(subContainer); } } - // 3. Drop the item into a new container - var subContainer = new PackingBox(); - subContainer.DropItem(dropped); - - var location = GetFreeLocation(); - if (location != Point3D.Zero) + private Point3D GetFreeLocation() { - AddItem(subContainer); - subContainer.Location = location; - } - else - { - base.DropItem(subContainer); - } - } + var positions = new bool[Rows, Columns]; - private Point3D GetFreeLocation() - { - var positions = new bool[Rows, Columns]; - - foreach (var item in Items) - { - if (item is PackingBox) + foreach (var item in Items) { - var i = (item.Y - Bounds.Y) / VerticalSpacing; - if (i < 0) + if (item is PackingBox) { - i = 0; - } - else if (i >= Rows) - { - i = Rows - 1; - } + var i = (item.Y - Bounds.Y) / VerticalSpacing; + if (i < 0) + { + i = 0; + } + else if (i >= Rows) + { + i = Rows - 1; + } - var j = (item.X - Bounds.X) / HorizontalSpacing; - if (j < 0) - { - j = 0; - } - else if (j >= Columns) - { - j = Columns - 1; - } + var j = (item.X - Bounds.X) / HorizontalSpacing; + if (j < 0) + { + j = 0; + } + else if (j >= Columns) + { + j = Columns - 1; + } - positions[i, j] = true; + positions[i, j] = true; + } + } + + for (var i = 0; i < Rows; i++) + { + for (var j = 0; j < Columns; j++) + { + if (!positions[i, j]) + { + var x = Bounds.X + j * HorizontalSpacing; + var y = Bounds.Y + i * VerticalSpacing; + + return new Point3D(x, y, 0); + } + } + } + + return Point3D.Zero; + } + + public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) + { + if (m.AccessLevel < AccessLevel.GameMaster) + { + m.SendLocalizedMessage(1061145); // You cannot place items into a house moving crate. + return false; + } + + return base.CheckHold(m, item, message, checkItems, plusItems, plusWeight); + } + + public override bool CheckLift(Mobile from, Item item, ref LRReason reject) => House?.Deleted == false && + base.CheckLift(from, item, ref reject) && House.IsOwner(from); + + public override bool CheckItemUse(Mobile from, Item item) => + House?.Deleted == false && base.CheckItemUse(from, item) && House.IsOwner(from); + + public override void OnItemRemoved(Item item) + { + base.OnItemRemoved(item); + + if (TotalItems == 0) + { + Delete(); } } - for (var i = 0; i < Rows; i++) + public void RestartTimer() { - for (var j = 0; j < Columns; j++) + if (m_InternalizeTimer == null) { - if (!positions[i, j]) - { - var x = Bounds.X + j * HorizontalSpacing; - var y = Bounds.Y + i * VerticalSpacing; - - return new Point3D(x, y, 0); - } + m_InternalizeTimer = new InternalizeTimer(this); + m_InternalizeTimer.Start(); + } + else + { + m_InternalizeTimer.Stop(); + m_InternalizeTimer.Start(); } } - return Point3D.Zero; - } - - public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) - { - if (m.AccessLevel < AccessLevel.GameMaster) + public void Hide() { - m.SendLocalizedMessage(1061145); // You cannot place items into a house moving crate. - return false; + if (m_InternalizeTimer != null) + { + m_InternalizeTimer.Stop(); + m_InternalizeTimer = null; + } + + var toRemove = new List(); + foreach (var item in Items) + { + if (item is PackingBox && item.Items.Count == 0) + { + toRemove.Add(item); + } + } + + foreach (var item in toRemove) + { + item.Delete(); + } + + if (TotalItems == 0) + { + Delete(); + } + else + { + Internalize(); + } } - return base.CheckHold(m, item, message, checkItems, plusItems, plusWeight); - } - - public override bool CheckLift(Mobile from, Item item, ref LRReason reject) => House?.Deleted == false && - base.CheckLift(from, item, ref reject) && House.IsOwner(from); - - public override bool CheckItemUse(Mobile from, Item item) => - House?.Deleted == false && base.CheckItemUse(from, item) && House.IsOwner(from); - - public override void OnItemRemoved(Item item) - { - base.OnItemRemoved(item); - - if (TotalItems == 0) + public override void OnAfterDelete() { - Delete(); + base.OnAfterDelete(); + + if (House?.MovingCrate == this) + { + House.MovingCrate = null; + } + + m_InternalizeTimer?.Stop(); + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(1); + + writer.Write(House); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + + House = reader.ReadEntity(); + + if (House != null) + { + House.MovingCrate = this; + Timer.StartTimer(Hide); + } + else + { + Timer.StartTimer(Delete); + } + + if (version == 0) + { + MaxItems = -1; // reset to default + } + } + + public class InternalizeTimer : Timer + { + private readonly MovingCrate m_Crate; + + public InternalizeTimer(MovingCrate crate) : base(TimeSpan.FromMinutes(5.0)) + { + m_Crate = crate; + } + + protected override void OnTick() + { + m_Crate.Hide(); + } } } - public void RestartTimer() + public class PackingBox : BaseContainer { - if (_internalizeTimer == null) - { - _internalizeTimer = new InternalizeTimer(this); - _internalizeTimer.Start(); - } - else - { - _internalizeTimer.Stop(); - _internalizeTimer.Start(); - } - } + public PackingBox() : base(0x9A8) => Movable = false; - public void Hide() - { - if (_internalizeTimer != null) + public PackingBox(Serial serial) : base(serial) { - _internalizeTimer.Stop(); - _internalizeTimer = null; } - using var queue = EnumerateItems(predicate: item => item is PackingBox && item.Items.Count == 0); - while (queue.Count > 0) + public override int LabelNumber => 1061690; // Packing Crate + + public override int DefaultGumpID => 0x4B; + public override int DefaultDropSound => 0x42; + + public override Rectangle2D Bounds => new(16, 51, 168, 73); + + public override int DefaultMaxItems => 0; + public override int DefaultMaxWeight => 0; + + public override void SendCantStoreMessage(Mobile to, Item item) { - queue.Dequeue().Delete(); + to.SendLocalizedMessage(1061145); // You cannot place items into a house moving crate. } - if (TotalItems == 0) + public override void OnItemRemoved(Item item) { - Delete(); - } - else - { - Internalize(); - } - } + base.OnItemRemoved(item); - public override void OnAfterDelete() - { - base.OnAfterDelete(); - - if (House?.MovingCrate == this) - { - House.MovingCrate = null; + if (item.GetBounce() == null && TotalItems == 0) + { + Delete(); + } } - _internalizeTimer?.Stop(); - } - - [AfterDeserialization] - private void AfterDeserialization() - { - if (House != null) + public override void OnItemBounceCleared(Item item) { - House.MovingCrate = this; - Timer.StartTimer(Hide); - } - else - { - Timer.StartTimer(Delete); - } - } + base.OnItemBounceCleared(item); - public class InternalizeTimer : Timer - { - private readonly MovingCrate m_Crate; - - public InternalizeTimer(MovingCrate crate) : base(TimeSpan.FromMinutes(5.0)) - { - m_Crate = crate; + if (TotalItems == 0) + { + Delete(); + } } - protected override void OnTick() + public override void Serialize(IGenericWriter writer) { - m_Crate.Hide(); - } - } -} - -[SerializationGenerator(0)] -public partial class PackingBox : BaseContainer -{ - public PackingBox() : base(0x9A8) => Movable = false; - - public override int LabelNumber => 1061690; // Packing Crate - - public override int DefaultGumpID => 0x4B; - public override int DefaultDropSound => 0x42; - - public override Rectangle2D Bounds => new(16, 51, 168, 73); - - public override int DefaultMaxItems => 0; - public override int DefaultMaxWeight => 0; - - public override void SendCantStoreMessage(Mobile to, Item item) - { - to.SendLocalizedMessage(1061145); // You cannot place items into a house moving crate. - } - - public override void OnItemRemoved(Item item) - { - base.OnItemRemoved(item); - - if (item.GetBounce() == null && TotalItems == 0) - { - Delete(); - } - } - - public override void OnItemBounceCleared(Item item) - { - base.OnItemBounceCleared(item); - - if (TotalItems == 0) - { - Delete(); + base.Serialize(writer); + + writer.WriteEncodedInt(1); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + + if (version == 0) + { + MaxItems = -1; // reset to default + } } } } diff --git a/Projects/UOContent/Multis/Houses/PreviewHouse.cs b/Projects/UOContent/Multis/Houses/PreviewHouse.cs index 5043709df..3db6b4d72 100644 --- a/Projects/UOContent/Multis/Houses/PreviewHouse.cs +++ b/Projects/UOContent/Multis/Houses/PreviewHouse.cs @@ -1,113 +1,147 @@ using System; using System.Collections.Generic; -using ModernUO.Serialization; using Server.Items; -namespace Server.Multis; - -[SerializationGenerator(0, false)] -public partial class PreviewHouse : BaseMulti +namespace Server.Multis { - [SerializableField(0)] - private List _previewComponents; - - private Timer _timer; - - public PreviewHouse(int multiID) : base(multiID) + public class PreviewHouse : BaseMulti { - var mcl = Components; + private List m_Components; + private Timer m_Timer; - for (var i = 1; i < mcl.List.Length; ++i) + public PreviewHouse(int multiID) : base(multiID) { - var entry = mcl.List[i]; + m_Components = new List(); - if (entry.Flags == 0) + var mcl = Components; + + for (var i = 1; i < mcl.List.Length; ++i) { - Item item = new Static(entry.ItemId); - item.MoveToWorld(new Point3D(X + entry.OffsetX, Y + entry.OffsetY, Z + entry.OffsetZ), Map); + var entry = mcl.List[i]; - _previewComponents ??= []; - _previewComponents.Add(item); + if (entry.Flags == 0) + { + Item item = new Static(entry.ItemId); + + item.MoveToWorld(new Point3D(X + entry.OffsetX, Y + entry.OffsetY, Z + entry.OffsetZ), Map); + + m_Components.Add(item); + } + } + + m_Timer = new DecayTimer(this); + m_Timer.Start(); + } + + public PreviewHouse(Serial serial) : base(serial) + { + } + + public override void OnLocationChange(Point3D oldLocation) + { + base.OnLocationChange(oldLocation); + + if (m_Components == null) + { + return; + } + + var xOffset = X - oldLocation.X; + var yOffset = Y - oldLocation.Y; + var zOffset = Z - oldLocation.Z; + + for (var i = 0; i < m_Components.Count; ++i) + { + var item = m_Components[i]; + + item.MoveToWorld(new Point3D(item.X + xOffset, item.Y + yOffset, item.Z + zOffset), Map); } } - _timer = new DecayTimer(this); - _timer.Start(); - } - - public override bool SkipSerialization => true; - - public override void OnLocationChange(Point3D oldLocation) - { - base.OnLocationChange(oldLocation); - - if (_previewComponents == null) + public override void OnMapChange() { - return; + base.OnMapChange(); + + if (m_Components == null) + { + return; + } + + for (var i = 0; i < m_Components.Count; ++i) + { + var item = m_Components[i]; + + item.Map = Map; + } } - var xOffset = X - oldLocation.X; - var yOffset = Y - oldLocation.Y; - var zOffset = Z - oldLocation.Z; - - for (var i = 0; i < _previewComponents.Count; ++i) + public override void OnDelete() { - var item = _previewComponents[i]; + base.OnDelete(); - item.MoveToWorld(new Point3D(item.X + xOffset, item.Y + yOffset, item.Z + zOffset), Map); - } - } + if (m_Components == null) + { + return; + } - public override void OnMapChange() - { - base.OnMapChange(); + for (var i = 0; i < m_Components.Count; ++i) + { + var item = m_Components[i]; - if (_previewComponents == null) - { - return; + item.Delete(); + } } - for (var i = 0; i < _previewComponents.Count; ++i) + public override void OnAfterDelete() { - _previewComponents[i].Map = Map; - } - } + m_Timer?.Stop(); - public override void OnDelete() - { - base.OnDelete(); + m_Timer = null; - if (_previewComponents == null) - { - return; + base.OnAfterDelete(); } - for (var i = 0; i < _previewComponents.Count; ++i) + public override void Serialize(IGenericWriter writer) { - _previewComponents[i]?.Delete(); - } - } + base.Serialize(writer); - public override void OnAfterDelete() - { - _timer?.Stop(); - _timer = null; + writer.Write(0); // version - base.OnAfterDelete(); - } - - private class DecayTimer : Timer - { - private readonly Item _item; - - public DecayTimer(Item item) : base(TimeSpan.FromSeconds(20.0)) - { - _item = item; + writer.Write(m_Components); } - protected override void OnTick() + public override void Deserialize(IGenericReader reader) { - _item.Delete(); + base.Deserialize(reader); + + var version = reader.ReadInt(); + + switch (version) + { + case 0: + { + m_Components = reader.ReadEntityList(); + + break; + } + } + + Timer.StartTimer(Delete); + } + + private class DecayTimer : Timer + { + private readonly Item m_Item; + + public DecayTimer(Item item) : base(TimeSpan.FromSeconds(20.0)) + { + m_Item = item; + } + + protected override void OnTick() + { + m_Item.Delete(); + } } } } diff --git a/Projects/UOContent/Network/AutoDenylist/AutoDenylist.cs b/Projects/UOContent/Network/AutoDenylist/AutoDenylist.cs deleted file mode 100644 index 7b3c44ae4..000000000 --- a/Projects/UOContent/Network/AutoDenylist/AutoDenylist.cs +++ /dev/null @@ -1,343 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: AutoDenylist.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; -using Server.Logging; -using Server.Network.Bans; - -namespace Server.Network; - -/// -/// A short-lived, in-memory denylist of addresses the shard itself just caught misbehaving. -/// -/// -/// The local half of promotion. Contributing to CrowdSec only helps once an OS bouncer reacts; until then -/// every reconnect costs a socket, a buffer and a NetState slot — and the verdicts that matter most -/// are reachable only after reading bytes, like a zero seed. It is also the whole defense on a shard running -/// no bouncer, which is the default. Not persisted, by design: a holding pen that survives restarts is a ban -/// without a ban's review. Only verdicts are held. -/// -/// -/// A hold is never refreshed, so every expiry is insertion + duration and the ring is sorted by -/// construction. Retiring lapsed entries is therefore the number expiring rather than the number held, which -/// is what lets the cap be sized for the flood instead of for a scan. -/// -public static class AutoDenylist -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(AutoDenylist)); - - // Membership only (normalized v6 bits). Loop-only. The expiry lives beside the key in the ring, so - // there is exactly one copy of it and the two cannot disagree. - private static readonly HashSet _held = []; - - // The same keys in expiry order. Parallel arrays rather than an array of structs: UInt128 forces - // 16-byte alignment, so a packed (key, expiry) struct costs 32 bytes where these cost 24 -- and the - // drain reads only the long[], 8 sequential bytes per entry. - private static UInt128[] _ringKeys = []; - private static long[] _ringExpiry = []; - private static int _ringHead; - private static int _ringCount; - - private static bool _enabled; - private static long _durationMs; - private static int _maxEntries; - private static bool _warnedFull; - - public static int Count => _held.Count; - - // Test seam: the ring and the set hold the same entries, and nothing else may assume it. - internal static int RingCount => _ringCount; - - public static void Configure() - { - AutoDenylistConfiguration.Load(); - var s = AutoDenylistConfiguration.Settings; - - _enabled = s.Enabled && s.Duration > TimeSpan.Zero && s.MaxEntries > 0; - if (!_enabled) - { - return; - } - - _durationMs = (long)s.Duration.TotalMilliseconds; - _maxEntries = s.MaxEntries; - - ConnectionFilters.Register(new AutoDenylistFilter()); - BanChannel.Register(new AutoDenylistReporter()); - } - - /// - /// Holds an address for the configured duration. Ignores non-behavioural verdicts and refuses to grow - /// past the cap: the flood this exists for must not become a memory leak. - /// - public static void Hold(IPAddress address, string reason) => Hold(address, reason, Core.TickCount); - - internal static bool Hold(IPAddress address, string reason, long nowTicks) - { - if (!_enabled || address == null || !BanReasons.IsBehavioral(reason)) - { - return false; - } - - Drain(nowTicks); - - var key = address.ToUInt128(); - - // Deliberately not refreshed: the first detection sets the expiry and later ones leave it alone. - // That keeps insertion order equal to expiry order, which is why the drain can stop at the first - // live record. A flooder whose hold lapses trips the rate limiter on its next attempt -- which - // runs ahead of the connection filters -- and is held again. - if (!_held.Add(key)) - { - return true; - } - - // Drain already reclaimed everything reclaimable, so being over now means genuinely full. - if (_held.Count > _maxEntries) - { - _held.Remove(key); - - if (!_warnedFull) - { - _warnedFull = true; - logger.Warning( - "Auto-denylist is full at {Max} addresses; further detections are disconnected but not held", - _maxEntries - ); - } - - return false; - } - - Push(key, nowTicks + _durationMs); - return true; - } - - public static bool IsDenied(IPAddress address) => IsDenied(address, Core.TickCount); - - /// - /// The accept-path decision, split out so the policy can be tested without a clock. Drains first: the - /// expiry lives in the ring, not beside the membership, so a lapsed hold has to be retired here rather - /// than expired on read. One array read when nothing has lapsed. - /// - internal static bool IsDenied(IPAddress address, long nowTicks) - { - if (!_enabled || address == null) - { - return false; - } - - Drain(nowTicks); - return _held.Contains(address.ToUInt128()); - } - - /// Releases an address early, e.g. when an operator retracts a ban. - public static void Release(IPAddress address) - { - if (!_enabled || address == null) - { - return; - } - - var key = address.ToUInt128(); - if (_held.Remove(key)) - { - // The ring record has to go too. Nothing records that this key was released, so if it were - // detected again before the old record lapsed, that record would retire the new hold early. - // O(n), but this is an operator retraction, not the accept path. - PurgeRing(key); - } - } - - /// - /// Retires everything that has lapsed. Expiries only ever increase along the ring, so the first live - /// record ends the scan and the cost is the number actually expiring, not the number held. - /// - internal static void Drain(long nowTicks) - { - var before = _ringCount; - - // Subtraction, never a direct compare: tick counts wrap. See dev-docs/tick-counts.md. - while (_ringCount > 0 && _ringExpiry[_ringHead] - nowTicks <= 0) - { - _held.Remove(_ringKeys[_ringHead]); - _ringHead = _ringHead + 1 == _ringKeys.Length ? 0 : _ringHead + 1; - _ringCount--; - } - - if (_ringCount != before) - { - _warnedFull = false; - } - } - - private static void Push(UInt128 key, long expiry) - { - if (_ringCount == _ringKeys.Length) - { - Grow(); - } - - var tail = _ringHead + _ringCount; - if (tail >= _ringKeys.Length) - { - tail -= _ringKeys.Length; - } - - _ringKeys[tail] = key; - _ringExpiry[tail] = expiry; - _ringCount++; - } - - private static void Grow() - { - // Capped at the entry cap: Push only runs below it, so the ring never needs more, and doubling - // past it would reserve roughly twice the slots it can ever use. - var size = Math.Min(Math.Max(64, _ringKeys.Length * 2), _maxEntries); - var keys = new UInt128[size]; - var expiry = new long[size]; - - for (var i = 0; i < _ringCount; i++) - { - var from = _ringHead + i; - if (from >= _ringKeys.Length) - { - from -= _ringKeys.Length; - } - - keys[i] = _ringKeys[from]; - expiry[i] = _ringExpiry[from]; - } - - _ringKeys = keys; - _ringExpiry = expiry; - _ringHead = 0; - } - - private static void PurgeRing(UInt128 key) - { - var capacity = _ringKeys.Length; - - for (var i = 0; i < _ringCount; i++) - { - var at = _ringHead + i; - if (at >= capacity) - { - at -= capacity; - } - - if (_ringKeys[at] != key) - { - continue; - } - - // Close the gap so the ring stays contiguous and expiry-ordered. - for (var j = i; j < _ringCount - 1; j++) - { - var to = _ringHead + j; - if (to >= capacity) - { - to -= capacity; - } - - var from = to + 1 == capacity ? 0 : to + 1; - _ringKeys[to] = _ringKeys[from]; - _ringExpiry[to] = _ringExpiry[from]; - } - - _ringCount--; - return; - } - } - - internal static void LoadForTesting(bool enabled, long durationMs, int maxEntries) - { - _held.Clear(); - _ringKeys = []; - _ringExpiry = []; - _ringHead = 0; - _ringCount = 0; - _enabled = enabled; - _durationMs = durationMs; - _maxEntries = maxEntries; - _warnedFull = false; - } -} - -/// Accept-path gate for . -public sealed class AutoDenylistFilter : IConnectionFilter -{ - private Timer _sweepTimer; - - public string Name => "auto-denylist"; - - public void Register() - { - } - - public void Start(CancellationToken token) - { - // Only reclaims memory: Hold and IsDenied both drain, so this matters on a shard that has gone - // quiet after a flood and would otherwise hold the ring until someone next connects. - _sweepTimer = Timer.DelayCall( - TimeSpan.FromMinutes(1), - TimeSpan.FromMinutes(1), - () => AutoDenylist.Drain(Core.TickCount) - ); - } - - public void Stop() - { - // Recurring, so an uncancelled sweep survives Stop and the next Start adds a second one. - _sweepTimer?.Stop(); - _sweepTimer = null; - } - - public bool ShouldDeny(IPAddress address) => AutoDenylist.IsDenied(address); -} - -/// -/// Feeds from the ban channel. A reporter rather than a direct call, because the -/// detection sites live in the engine and must not reach into content. -/// -public sealed class AutoDenylistReporter : IBanReporter -{ - public string Name => "auto-denylist"; - - public bool CanRetract => true; - - public void Register() - { - } - - public void Start(CancellationToken token) - { - } - - public void Stop() - { - } - - /// - /// The contributed is ignored: how long a bouncer should ban an address is a - /// different question from how long this shard holds it at accept. - /// - public void Report(IPAddress address, TimeSpan ttl, string reason) => AutoDenylist.Hold(address, reason); - - public void Retract(IPAddress address) => AutoDenylist.Release(address); -} diff --git a/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs b/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs deleted file mode 100644 index fd13ef003..000000000 --- a/Projects/UOContent/Network/AutoDenylist/AutoDenylistConfiguration.cs +++ /dev/null @@ -1,88 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: AutoDenylistConfiguration.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.IO; -using System.Text.Json.Serialization; -using Server.Json; - -namespace Server.Network; - -/// -/// Loads the from Configuration/auto-denylist.json. Loaded once; -/// a missing file writes a template so operators have something to edit. -/// -public static class AutoDenylistConfiguration -{ - private const string _path = "Configuration/auto-denylist.json"; - - public static AutoDenylistSettings Settings { get; private set; } - - public static void Load() - { - var path = Path.Join(Core.BaseDirectory, _path); - - if (File.Exists(path)) - { - Settings = JsonConfig.Deserialize(path); - } - else - { - Settings = new AutoDenylistSettings(); - Save(); - } - } - - private static void Save() - { - JsonConfig.Serialize(Path.Join(Core.BaseDirectory, _path), Settings); - } -} - -/// Bound configuration for . -public record AutoDenylistSettings -{ - /// Whether behavioural detections are held locally. Disabled makes the filter inert. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } = true; - - /// - /// How long an address is denied at accept after the shard catches it misbehaving. Deliberately - /// independent of the duration reported to external bouncers: this is a local holding pen, not a ban. - /// - /// - /// Short on purpose: it covers the gap before an OS bouncer reacts, and blunts a flood on shards running - /// none. An address still attacking is simply re-detected and re-added, so the list sustains itself while - /// a mistake clears on its own. - /// - [JsonPropertyName("duration")] - public TimeSpan Duration { get; set; } = TimeSpan.FromMinutes(15); - - /// - /// Hard cap on tracked addresses: a distinct-source flood is the case this exists for, so the cap is what - /// stops it becoming the exhaustion it prevents. At the cap new addresses are not tracked, but are still - /// disconnected by whichever gate detected them. - /// - /// - /// A HashSet capacity, not a round number. Grown from empty it steps 36,353 → 75,431 → 156,437 - /// → 324,449, so this fills one exactly instead of stranding slots: 65,536 sat just past a resize and - /// left 9,895 of them unusable. Sized to cover the 50k–250k distinct-source floods seen in practice, - /// for ~19 MB — 36 bytes a set slot plus 24 for the ring record. Raising it is bounded by memory - /// rather than by a scan, since holds are retired from the ring in expiry order; a flood past it wants - /// upstream scrubbing rather than a larger cap. - /// - [JsonPropertyName("maxEntries")] - public int MaxEntries { get; set; } = 324_449; -} diff --git a/Projects/UOContent/Network/BanExemptions.cs b/Projects/UOContent/Network/BanExemptions.cs deleted file mode 100644 index c5f6b726a..000000000 --- a/Projects/UOContent/Network/BanExemptions.cs +++ /dev/null @@ -1,61 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BanExemptions.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Net; -using Server.Network.Bans; - -namespace Server.Network; - -/// -/// Combines and into the one answer -/// asks for, so neither source has to know about the other. -/// -public static class BanExemptions -{ - public static void Configure() - { - BanChannel.IsExempt = IsExempt; - } - - public static bool IsExempt(IPAddress address, string reason) => - IsExempt(address, reason, LoginAllowlist.IsExemptFromEscalation); - - /// - /// Split for testing. is stateful — calling it spends a strike — so it - /// must not be invoked once the answer is already decided. - /// - internal static bool IsExempt(IPAddress address, string reason, Func loginAllowlist) - { - if (address == null) - { - return false; - } - - // Never suppress an operator's explicit ban. Checked first so it also costs no strike. - if (!BanReasons.IsBehavioral(reason)) - { - return false; - } - - // Deliberate and unconditional, so it wins and must not spend the earned list's strikes. - if (ManualAllowlist.Contains(address)) - { - return true; - } - - return loginAllowlist(address, reason); - } -} diff --git a/Projects/UOContent/Network/Blocklist/BlocklistConfiguration.cs b/Projects/UOContent/Network/Blocklist/BlocklistConfiguration.cs deleted file mode 100644 index 19541636f..000000000 --- a/Projects/UOContent/Network/Blocklist/BlocklistConfiguration.cs +++ /dev/null @@ -1,96 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BlocklistConfiguration.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.IO; -using System.Text.Json.Serialization; -using Server.Json; - -namespace Server.Network.Bans; - -/// -/// Loads the from Configuration/blocklist.json. Loaded once; a -/// missing file writes a template so operators have something to edit. -/// -public static class BlocklistConfiguration -{ - private const string _path = "Configuration/blocklist.json"; - - public static BlocklistSettings Settings { get; private set; } - - public static void Load() - { - var path = Path.Join(Core.BaseDirectory, _path); - - if (File.Exists(path)) - { - Settings = JsonConfig.Deserialize(path); - } - else - { - Settings = new BlocklistSettings(); - Save(); - } - } - - private static void Save() - { - JsonConfig.Serialize(Path.Join(Core.BaseDirectory, _path), Settings); - } -} - -/// -/// Bound configuration for . The filter is inert unless -/// is set and points at a list that exists, so a shard that never runs the generator -/// pays nothing for the defaults. -/// -public record BlocklistSettings -{ - /// - /// Whether the accept-path gate runs at all. Off by default: the reload poll runs for the whole - /// uptime, which no shard should pay before an operator has chosen to run a blocklist. - /// - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } - - /// - /// Path to the blocklist. A relative path resolves against ; an - /// absolute path is used as-is (handy when several shards share one generated list). Set to - /// "" to disable the gate entirely. Produce the file with tools/Export-IpBlocklist.ps1. - /// - [JsonPropertyName("file")] - public string File { get; set; } = "Configuration/ip-blocklist.txt"; - - /// How often the file is checked for changes. Reloads only happen when it actually changed. - [JsonPropertyName("reloadInterval")] - public TimeSpan ReloadInterval { get; set; } = TimeSpan.FromSeconds(60); - - /// Whether blocklist hits are contributed to the ban channel (the demand-paging promotion). - [JsonPropertyName("reportHits")] - public bool ReportHits { get; set; } = true; - - /// Duration reported for a blocklist-matched ban. - [JsonPropertyName("banDuration")] - public TimeSpan BanDuration { get; set; } = TimeSpan.FromHours(6); - - /// - /// How long the accept-path guard suppresses re-reporting a promoted address. This only needs to - /// bridge the gap until the OS bouncer picks up the promotion (seconds); after that the kernel drops - /// repeat traffic. Decoupled from so the guard doesn't have to remember - /// hours' worth of distinct addresses. - /// - [JsonPropertyName("promoteSuppression")] - public TimeSpan PromoteSuppression { get; set; } = TimeSpan.FromSeconds(60); -} diff --git a/Projects/UOContent/Network/Blocklist/BlocklistFile.cs b/Projects/UOContent/Network/Blocklist/BlocklistFile.cs deleted file mode 100644 index a593a7035..000000000 --- a/Projects/UOContent/Network/Blocklist/BlocklistFile.cs +++ /dev/null @@ -1,86 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BlocklistFile.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.IO; - -namespace Server.Network.Bans; - -public readonly record struct BlocklistHeader(string Generated, int Count, bool Present); - -/// Reads the versioned blocklist file: cheap header probe + full snapshot load. -public static class BlocklistFile -{ - public static bool TryReadHeader(string path, out BlocklistHeader header) - { - header = new BlocklistHeader(null, 0, false); - try - { - if (!File.Exists(path)) - { - return false; - } - using var reader = new StreamReader(path); - var first = reader.ReadLine(); - if (first == null) - { - header = new BlocklistHeader(null, 0, true); - return true; - } - string generated = null; - var count = 0; - if (first.StartsWith('#')) - { - var tokens = first.Split(' ', StringSplitOptions.RemoveEmptyEntries); - for (var i = 0; i < tokens.Length; i++) - { - var tok = tokens[i]; - if (tok.StartsWith("generated=", StringComparison.Ordinal)) - { - generated = tok["generated=".Length..]; - } - else if (tok.StartsWith("count=", StringComparison.Ordinal)) - { - int.TryParse(tok["count=".Length..], out count); - } - } - } - header = new BlocklistHeader(generated, count, true); - return true; - } - catch - { - return false; // treat as absent; caller keeps last-good / empty - } - } - - public static BlocklistSnapshot Load(string path, out int parsed, out int skipped) - { - parsed = 0; - skipped = 0; - try - { - if (!File.Exists(path)) - { - return BlocklistSnapshot.Empty; - } - return BlocklistSnapshot.Build(File.ReadAllBytes(path), out parsed, out skipped); - } - catch - { - return BlocklistSnapshot.Empty; - } - } -} diff --git a/Projects/UOContent/Network/Blocklist/BlocklistFilter.cs b/Projects/UOContent/Network/Blocklist/BlocklistFilter.cs deleted file mode 100644 index 030997803..000000000 --- a/Projects/UOContent/Network/Blocklist/BlocklistFilter.cs +++ /dev/null @@ -1,316 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BlocklistFilter.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Server.Logging; - -namespace Server.Network.Bans; - -/// -/// Accept-path gate for a large, file-sourced IP blocklist, hydrated from the file a generator -/// (tools/Export-IpBlocklist.ps1) writes on a schedule. Holds an immutable snapshot swapped -/// atomically by an off-loop reload poll, so accept-path reads are lock-free. Opt-in via -/// blocklist.json's enabled; inert when off, or when no file is configured or present. -/// -/// -/// This is the demand-paging half of the design: an OS firewall cannot hold millions of entries on -/// Windows, so the millions live here and only addresses that actually connect are promoted to CrowdSec -/// (and from there to the OS firewall) through . -/// keeps a flood of repeat connections from re-reporting the same address before the bouncer picks it up. -/// -public sealed class BlocklistFilter : IConnectionFilter -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(BlocklistFilter)); - - // Written by the reload poll (off-loop), read by the accept path (game loop). One volatile reference - // swap is the whole synchronization story: readers see the old or the new snapshot, whole. - private volatile BlocklistSnapshot _snapshot = BlocklistSnapshot.Empty; - - private readonly PromotedGuard _guard = new(); - - private bool _enabled; - private string _path; - private TimeSpan _interval; - private bool _reportHits; - private TimeSpan _banDuration; - private long _suppressionMs; - private string _lastGenerated; - private DateTime _lastWriteUtc; - private CancellationTokenSource _cts; - private Timer _sweepTimer; - - public string Name => "blocklist"; - - public int Count => _snapshot.Count; - - public static void Configure() - { - ConnectionFilters.Register(new BlocklistFilter()); - } - - public void Register() - { - BlocklistConfiguration.Load(); - var s = BlocklistConfiguration.Settings; - - _path = ResolvePath(s.File); - _enabled = s.Enabled && _path != null; - _interval = s.ReloadInterval <= TimeSpan.Zero ? TimeSpan.FromSeconds(60) : s.ReloadInterval; - _reportHits = s.ReportHits; - _banDuration = s.BanDuration; - _suppressionMs = (long)s.PromoteSuppression.TotalMilliseconds; - } - - /// - /// Resolves the configured path once: a relative path is anchored to - /// (never the process working directory, which differs when the shard is launched from elsewhere), an - /// absolute path is taken as-is so several shards can share one generated list. - /// - private static string ResolvePath(string configured) - { - if (string.IsNullOrWhiteSpace(configured)) - { - return null; - } - - return Path.IsPathRooted(configured) ? configured : Path.Join(Core.BaseDirectory, configured); - } - - public void Start(CancellationToken token) - { - if (!_enabled) - { - LogWhyDisabled(); - return; - } - - // The operator's override on this gate, opted into separately. Without it only the generator's - // subtraction covers carve-outs, and that does not cover ban contributions. - if (!ManualAllowlist.Enabled) - { - logger.Warning( - "Blocklist is on but the manual allowlist is not; set \"enabled\" in ip-allowlist.json so a " + - "carve-out also suppresses ban contributions" - ); - } - - _cts = CancellationTokenSource.CreateLinkedTokenSource(token); - - // A missing file is not an error: the gate stays inert until the poll picks up whatever the - // generator first writes. No restart needed. - if (File.Exists(_path)) - { - Reload(); // synchronous prime; empty on failure (fail-open) - } - else - { - logger.Information("Blocklist inert: no list at \"{Path}\"; polling every {Interval}", _path, _interval); - } - - // Sweep the promote-guard so a distinct-IP flood cannot grow it unbounded. Only marked when hits - // are reported, so there is nothing to sweep otherwise. - if (_reportHits) - { - _sweepTimer = Timer.DelayCall(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), SweepGuard); - } - - _ = Task.Run(() => PollLoop(_cts.Token), _cts.Token); - } - - private void LogWhyDisabled() - { - if (_path == null) - { - logger.Information("Blocklist disabled (\"file\" empty in blocklist.json)"); - } - else if (File.Exists(_path)) - { - // An upgraded shard has a list on disk but no "enabled" key, so say so rather than silently - // dropping a gate it was relying on. - logger.Warning( - "Blocklist is off (\"enabled\" false in blocklist.json) but a list is present at \"{Path}\"; " + - "no addresses will be denied", - _path - ); - } - else - { - logger.Information("Blocklist disabled (\"enabled\" false in blocklist.json)"); - } - } - - public void Stop() - { - _cts?.Cancel(); - _cts?.Dispose(); - _cts = null; - - // Recurring, so an uncancelled sweep survives Stop and the next Start adds a second one. - _sweepTimer?.Stop(); - _sweepTimer = null; - } - - public bool ShouldDeny(IPAddress address) - { - if (!Evaluate(address, Core.TickCount, out var shouldReport)) - { - return false; - } - - if (shouldReport) - { - // Demand-page this address up to the OS-level bouncer. Enqueue-only; never blocks the loop. - BanChannel.Report(address, _banDuration, BanReasons.Blocklist); - } - - return true; - } - - /// - /// The pure decision, split out so the accept-path policy can be tested without a clock or a ban - /// channel. is true at most once per suppression window. - /// - internal bool Evaluate(IPAddress address, long nowTicks, out bool shouldReport) - { - shouldReport = false; - - if (!_snapshot.IsBanned(address)) - { - return false; - } - - // Both are asked only once the list has matched, so they cost the common accept nothing. The file - // list is usually redundant because the generator subtracts it — except right after an operator - // adds an entry without regenerating, which is when someone is waiting to get back in. - if (ManualAllowlist.Contains(address)) - { - return false; - } - - // A feed listing an address a real player logged in from recently is far more often a false positive - // than a compromise. - if (LoginAllowlist.IsAllowed(address)) - { - return false; - } - - if (_reportHits) - { - shouldReport = _guard.TryMark(address.ToUInt128(), nowTicks, _suppressionMs); - } - - return true; - } - - private void SweepGuard() => _guard.Sweep(Core.TickCount); - - // Test hook: inject a snapshot and policy without file I/O. - internal void LoadForTesting(BlocklistSnapshot snapshot, bool reportHits = true, long suppressionMs = 60000) - { - _snapshot = snapshot; - _reportHits = reportHits; - _suppressionMs = suppressionMs; - } - - private async ValueTask PollLoop(CancellationToken token) - { - while (!token.IsCancellationRequested) - { - try - { - await Task.Delay(_interval, token); - } - catch (OperationCanceledException) - { - return; - } - - try - { - if (ChangedSinceLastLoad()) - { - // Parsing millions of lines competes with a save for CPU and page cache, so yield until - // the world is written out. See the threading policy in CLAUDE.md (rules #3 and #10). - while (World.Saving || World.WorldState == WorldState.PendingSave) - { - await Task.Delay(TimeSpan.FromSeconds(1), token); - } - - Reload(); - } - } - catch (OperationCanceledException) - { - return; - } - catch (Exception e) - { - logger.Warning(e, "Blocklist reload check failed; keeping last snapshot ({Count})", Count); - } - } - } - - private bool ChangedSinceLastLoad() - { - try - { - var info = new FileInfo(_path); - if (!info.Exists) - { - return false; - } - - if (info.LastWriteTimeUtc == _lastWriteUtc) - { - return false; // cheapest guard - } - } - catch - { - return false; - } - - return !BlocklistFile.TryReadHeader(_path, out var h) || h.Generated != _lastGenerated; - } - - private void Reload() - { - // Capture the mtime/header BEFORE Load() so they describe the version being parsed. Capturing - // after could skip a version the producer swapped in mid-parse; stale markers only cost a reload. - var writeUtc = default(DateTime); - try - { - writeUtc = new FileInfo(_path).LastWriteTimeUtc; - } - catch - { - /* keep default */ - } - - BlocklistFile.TryReadHeader(_path, out var h); - - var next = BlocklistFile.Load(_path, out var parsed, out var skipped); - _snapshot = next; // single volatile swap; readers see old or new whole - _lastGenerated = h.Generated; - _lastWriteUtc = writeUtc; - - logger.Information("Blocklist loaded {Parsed} entr(ies) ({Count} ranges, {Skipped} skipped) gen={Gen}", - parsed, next.Count, skipped, h.Generated); - } -} diff --git a/Projects/UOContent/Network/Blocklist/BlocklistSnapshot.cs b/Projects/UOContent/Network/Blocklist/BlocklistSnapshot.cs deleted file mode 100644 index d62df0752..000000000 --- a/Projects/UOContent/Network/Blocklist/BlocklistSnapshot.cs +++ /dev/null @@ -1,210 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BlocklistSnapshot.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Buffers.Text; -using System.Net; -using System.Net.Sockets; -using System.Text; -using Server.Collections; - -namespace Server.Network.Bans; - -/// -/// Immutable dual-stack blocklist. Singles and CIDRs are folded into a single sorted, coalesced -/// interval index per family: IPv4 as ranges (lean for the millions-strong common -/// case), IPv6 as ranges (empty unless the feed carries v6). Immutable → lock-free reads. -/// -public sealed class BlocklistSnapshot -{ - public static readonly BlocklistSnapshot Empty = new(SortedRangeIndex.Empty, SortedRangeIndex.Empty); - - private readonly SortedRangeIndex _v4; - private readonly SortedRangeIndex _v6; - - public int Count => _v4.Count + _v6.Count; - - private BlocklistSnapshot(SortedRangeIndex v4, SortedRangeIndex v6) - { - _v4 = v4; - _v6 = v6; - } - - /// - /// Parses a blocklist directly from its UTF-8/ASCII file bytes — one line at a time, splitting on - /// '\n' with no per-line string allocation. IPv4 singles and CIDRs are parsed straight from the - /// byte span; IPv6 (the rare path) decodes the single address token and defers to the framework parser. - /// Malformed lines increment and never throw. - /// - public static BlocklistSnapshot Build(ReadOnlySpan data, out int parsed, out int skipped) - { - parsed = 0; - skipped = 0; - - // Only the two final index arrays (allocated inside SortedRangeIndex.Build) hit the heap; every - // build-time buffer here is a pooled ref list. mt: true is required — this runs off the game loop. - using var v4 = PooledRefList.Range>.Create(mt: true); - using var v6 = PooledRefList.Range>.Create(mt: true); - - var rest = data; - while (!rest.IsEmpty) - { - ReadOnlySpan line; - var nl = rest.IndexOf((byte)'\n'); - if (nl >= 0) - { - line = rest[..nl]; - rest = rest[(nl + 1)..]; - } - else - { - line = rest; - rest = default; - } - - line = line[Ascii.Trim(line)]; - if (line.IsEmpty || line[0] == (byte)'#' || line[0] == (byte)';') - { - continue; - } - - var slash = line.IndexOf((byte)'/'); - var addr = slash >= 0 ? line[..slash] : line; - var bitsToken = slash >= 0 ? line[(slash + 1)..] : default; - - if (addr.IndexOf((byte)':') < 0) - { - // IPv4 single or CIDR — parsed straight from the byte span. - if (slash >= 0) - { - if (IPAddressUtility.TryParseV4(addr, out var ip) && - TryParseBits(bitsToken, out var bits) && bits is >= 0 and <= 32) - { - var size = bits == 0 ? 0xFFFFFFFFu : (1u << (32 - bits)) - 1; - var b = ip & ~size; - v4.Add(new SortedRangeIndex.Range(b, b + size)); - parsed++; - } - else - { - skipped++; - } - } - else if (IPAddressUtility.TryParseV4(addr, out var ip)) - { - v4.Add(new SortedRangeIndex.Range(ip, ip)); - parsed++; - } - else - { - skipped++; - } - } - else if (TryDecodeV6(addr, out var v)) - { - // IPv6 is rare in these feeds; the single token was decoded and framework-parsed above. - if (slash >= 0) - { - if (TryParseBits(bitsToken, out var bits) && bits is >= 0 and <= 128) - { - var mask = bits == 0 ? UInt128.Zero : ~((UInt128.One << (128 - bits)) - 1); - var b = v & mask; - v6.Add(new SortedRangeIndex.Range(b, b | ~mask)); - parsed++; - } - else - { - skipped++; - } - } - else - { - v6.Add(new SortedRangeIndex.Range(v, v)); - parsed++; - } - } - else - { - skipped++; - } - } - - v4.Sort(SortedRangeIndex.ByMin); - v6.Sort(SortedRangeIndex.ByMin); - return new BlocklistSnapshot(SortedRangeIndex.Build(v4.AsSpan()), SortedRangeIndex.Build(v6.AsSpan())); - } - - // Decodes a single IPv6 address token from ASCII bytes and validates it via the framework parser. - private static bool TryDecodeV6(ReadOnlySpan addr, out UInt128 v) - { - v = UInt128.Zero; - if (addr.Length > 45) - { - return false; - } - - Span chars = stackalloc char[addr.Length]; - for (var i = 0; i < addr.Length; i++) - { - chars[i] = (char)addr[i]; - } - - if (!IPAddress.TryParse(chars, out var a) || a.AddressFamily != AddressFamily.InterNetworkV6) - { - return false; - } - - v = a.ToUInt128(); - return true; - } - - private static bool TryParseBits(ReadOnlySpan token, out int bits) - { - if (Utf8Parser.TryParse(token, out bits, out var consumed) && consumed == token.Length) - { - return true; - } - - bits = 0; - return false; - } - - /// - /// Plain set membership, for callers whose set is an ALLOWlist (see ) and for - /// whom would read backwards. The interval machinery is direction-agnostic. - /// - public bool Contains(IPAddress ip) => IsBanned(ip); - - public bool IsBanned(IPAddress ip) - { - if (ip.IsIPv4MappedToIPv6) - { - // v6-encoded v4 must not dodge the v4 set; extract the embedded v4 uint directly. - return IPAddressUtility.TryMappedV4(ip, out var mv) && _v4.Contains(mv); - } - - if (ip.AddressFamily == AddressFamily.InterNetwork) - { - return IPAddressUtility.TryV4(ip, out var v) && _v4.Contains(v); - } - - if (ip.AddressFamily == AddressFamily.InterNetworkV6) - { - return _v6.Contains(ip.ToUInt128()); - } - - return false; - } -} diff --git a/Projects/UOContent/Network/Blocklist/PromotedGuard.cs b/Projects/UOContent/Network/Blocklist/PromotedGuard.cs deleted file mode 100644 index bba8e3ee5..000000000 --- a/Projects/UOContent/Network/Blocklist/PromotedGuard.cs +++ /dev/null @@ -1,51 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: PromotedGuard.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; - -namespace Server.Network.Bans; - -/// Suppresses re-reporting the same IP within a TTL. Accept-path thread only. -public sealed class PromotedGuard -{ - private readonly Dictionary _expiry = []; - - public bool TryMark(UInt128 ip, long nowTicks, long ttlMs) - { - if (_expiry.TryGetValue(ip, out var exp) && exp - nowTicks > 0) - { - return false; - } - _expiry[ip] = nowTicks + ttlMs; - return true; - } - - public void Sweep(long nowTicks) - { - if (_expiry.Count == 0) - { - return; - } - - foreach (var (ip, exp) in _expiry) - { - if (exp - nowTicks <= 0) - { - _expiry.Remove(ip); - } - } - } -} diff --git a/Projects/UOContent/Engines/BuffIcons/BuffIconPackets.cs b/Projects/UOContent/Network/BuffIconPackets.cs similarity index 84% rename from Projects/UOContent/Engines/BuffIcons/BuffIconPackets.cs rename to Projects/UOContent/Network/BuffIconPackets.cs index 42a8bd080..fc615ee5f 100644 --- a/Projects/UOContent/Engines/BuffIcons/BuffIconPackets.cs +++ b/Projects/UOContent/Network/BuffIconPackets.cs @@ -1,18 +1,11 @@ using System.Buffers; -using Server.Network; -namespace Server.Engines.BuffIcons; +namespace Server.Network; public static class BuffIconPackets { public static void SendAddBuffPacket( - this NetState ns, - Serial mob, - BuffIcon iconID, - int titleCliloc, - int secondaryCliloc, - TextDefinition args, - long seconds + this NetState ns, Serial mob, BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args, long ticks ) { if (ns.CannotSendPackets()) @@ -34,7 +27,8 @@ public static class BuffIconPackets writer.Write((short)0x1); // command (0 = remove, 1 = add, 2 = data) writer.Write(0); - writer.Write((short)seconds); + // Truncate to whole seconds - The packet should be delayed by the partial seconds and then sent "on the second" + writer.Write((short)(ticks / 1000)); writer.Clear(3); writer.Write(titleCliloc); writer.Write(secondaryCliloc); diff --git a/Projects/UOContent/Network/ContainerGridPacketHandler.cs b/Projects/UOContent/Network/ContainerGridPacketHandler.cs index 70659124c..20f37d695 100644 --- a/Projects/UOContent/Network/ContainerGridPacketHandler.cs +++ b/Projects/UOContent/Network/ContainerGridPacketHandler.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ContainerGridPacketHandler.cs * * * diff --git a/Projects/UOContent/Network/CrowdSec/CrowdSecAlert.cs b/Projects/UOContent/Network/CrowdSec/CrowdSecAlert.cs deleted file mode 100644 index 773cf50ec..000000000 --- a/Projects/UOContent/Network/CrowdSec/CrowdSecAlert.cs +++ /dev/null @@ -1,78 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: CrowdSecAlert.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Server.Network.Bans.CrowdSec; - -/// One CrowdSec alert (POST /v1/alerts takes an array of these). -public sealed class CrowdSecAlert -{ - [JsonPropertyName("scenario")] public string Scenario { get; set; } - - /// - /// Required by LAPI. It dereferences the field unconditionally when persisting the alert, so - /// omitting it answers 500 rather than a validation error. Empty is the accepted value for a - /// watcher that isn't shipping a hub scenario. - /// - [JsonPropertyName("scenario_hash")] public string ScenarioHash { get; set; } = ""; - - /// Required alongside ; same 500-on-missing behavior. - [JsonPropertyName("scenario_version")] public string ScenarioVersion { get; set; } = "1.0"; - - [JsonPropertyName("message")] public string Message { get; set; } - [JsonPropertyName("events_count")] public int EventsCount { get; set; } = 1; - [JsonPropertyName("start_at")] public string StartAt { get; set; } - [JsonPropertyName("stop_at")] public string StopAt { get; set; } - - /// Bucket capacity. One decision per alert, so 1 — a leaky-bucket capacity of 0 is nonsense. - [JsonPropertyName("capacity")] public int Capacity { get; set; } = 1; - [JsonPropertyName("leakspeed")] public string LeakSpeed { get; set; } = "0s"; - [JsonPropertyName("simulated")] public bool Simulated { get; set; } - [JsonPropertyName("events")] public object[] Events { get; set; } = []; - [JsonPropertyName("remediation")] public bool Remediation { get; set; } = true; - [JsonPropertyName("source")] public CrowdSecSource Source { get; set; } - [JsonPropertyName("decisions")] public CrowdSecDecisionDto[] Decisions { get; set; } -} - -public sealed class CrowdSecSource -{ - [JsonPropertyName("scope")] public string Scope { get; set; } = "Ip"; - [JsonPropertyName("value")] public string Value { get; set; } -} - -public sealed class CrowdSecDecisionDto -{ - [JsonPropertyName("origin")] public string Origin { get; set; } - [JsonPropertyName("type")] public string Type { get; set; } = "ban"; - [JsonPropertyName("scope")] public string Scope { get; set; } = "Ip"; - [JsonPropertyName("value")] public string Value { get; set; } - [JsonPropertyName("duration")] public string Duration { get; set; } - [JsonPropertyName("scenario")] public string Scenario { get; set; } -} - -/// Watcher login request/response for POST /v1/watchers/login. -public sealed class CrowdSecLoginRequest -{ - [JsonPropertyName("machine_id")] public string MachineId { get; set; } - [JsonPropertyName("password")] public string Password { get; set; } -} - -public sealed class CrowdSecLoginResponse -{ - [JsonPropertyName("code")] public int Code { get; set; } - [JsonPropertyName("token")] public string Token { get; set; } - [JsonPropertyName("expire")] public string Expire { get; set; } -} diff --git a/Projects/UOContent/Network/CrowdSec/CrowdSecAlertClient.cs b/Projects/UOContent/Network/CrowdSec/CrowdSecAlertClient.cs deleted file mode 100644 index b91d0ebe5..000000000 --- a/Projects/UOContent/Network/CrowdSec/CrowdSecAlertClient.cs +++ /dev/null @@ -1,152 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: CrowdSecAlertClient.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Net.Http.Json; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; - -namespace Server.Network.Bans.CrowdSec; - -/// Reporter-side LAPI operations, mockable for tests. -public interface ICrowdSecAlertClient : IDisposable -{ - ValueTask PostAlertsAsync(IReadOnlyList alerts, CancellationToken token); - ValueTask DeleteDecisionsAsync(string origin, IPAddress ip, CancellationToken token); -} - -/// -/// CrowdSec LAPI watcher client: authenticates with machine credentials and posts/deletes decisions. -/// Holds a JWT refreshed on expiry or a 401. -/// -public sealed class CrowdSecAlertClient : ICrowdSecAlertClient -{ - private static readonly JsonSerializerOptions _jsonOptions = new() { PropertyNameCaseInsensitive = true }; - - /// - /// The crowdsec/ prefix is load-bearing: LAPI's default watcher profile matches on it and - /// answers 401 for anything else, so this cannot be a plain product string. - /// - internal const string UserAgent = "crowdsec/ModernUO-watcher-1.0"; - - private readonly HttpClient _http; - private readonly string _machineId; - private readonly string _password; - - private string _token; - private DateTime _tokenExpiresUtc = DateTime.MinValue; - - public CrowdSecAlertClient(CrowdSecSettings settings) - { - var baseUri = new Uri(settings.LapiUrl, UriKind.Absolute); // fails loud on malformed url - _http = new HttpClient { BaseAddress = baseUri, Timeout = TimeSpan.FromSeconds(30) }; - _http.DefaultRequestHeaders.Add("User-Agent", UserAgent); - _machineId = settings.MachineId; - _password = settings.Password; - } - - private async Task EnsureAuthAsync(CancellationToken token) - { - if (_token != null && DateTime.UtcNow < _tokenExpiresUtc - TimeSpan.FromMinutes(1)) - { - return; - } - - var request = new CrowdSecLoginRequest { MachineId = _machineId, Password = _password }; - using var response = await _http.PostAsJsonAsync("/v1/watchers/login", request, _jsonOptions, token) - .ConfigureAwait(false); - response.EnsureSuccessStatusCode(); - - var login = await response.Content.ReadFromJsonAsync(_jsonOptions, token) - .ConfigureAwait(false); - _token = login?.Token ?? throw new InvalidOperationException("CrowdSec login returned no token."); - - // LAPI returns an RFC3339 expiry. Parse it invariantly for the same reason we format invariantly: - // the current culture must not decide whether a machine-readable timestamp is understood. - _tokenExpiresUtc = DateTime.TryParse( - login.Expire, - CultureInfo.InvariantCulture, - DateTimeStyles.AdjustToUniversal, - out var exp - ) ? exp : DateTime.UtcNow.AddHours(1); - } - - private void Authorize(HttpRequestMessage message) => - message.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token); - - public async ValueTask PostAlertsAsync(IReadOnlyList alerts, CancellationToken token) - { - if (alerts.Count == 0) - { - return; - } - - await SendWithRetryAsync(() => - { - var message = new HttpRequestMessage(HttpMethod.Post, "/v1/alerts") - { - Content = JsonContent.Create(alerts, options: _jsonOptions) - }; - Authorize(message); - return message; - }, token).ConfigureAwait(false); - } - - public async ValueTask DeleteDecisionsAsync(string origin, IPAddress ip, CancellationToken token) - { - var query = BuildDeleteQuery(origin, ip); - await SendWithRetryAsync(() => - { - var message = new HttpRequestMessage(HttpMethod.Delete, query); - Authorize(message); - return message; - }, token).ConfigureAwait(false); - } - - /// - /// Builds the decisions-delete query string. is operator-controlled config - /// (crowdsec.json), so it must be escaped like any other untrusted value going into a URL. - /// - internal static string BuildDeleteQuery(string origin, IPAddress ip) => - $"/v1/decisions?origin={Uri.EscapeDataString(origin)}&ip={Uri.EscapeDataString(ip.ToString())}"; - - private async ValueTask SendWithRetryAsync(Func build, CancellationToken token) - { - await EnsureAuthAsync(token).ConfigureAwait(false); - - using var first = build(); - using var response = await _http.SendAsync(first, token).ConfigureAwait(false); - if (response.StatusCode != HttpStatusCode.Unauthorized) - { - response.EnsureSuccessStatusCode(); - return; - } - - // Token rejected mid-flight: force a re-login and retry once. - _token = null; - await EnsureAuthAsync(token).ConfigureAwait(false); - using var retry = build(); - using var retryResponse = await _http.SendAsync(retry, token).ConfigureAwait(false); - retryResponse.EnsureSuccessStatusCode(); - } - - public void Dispose() => _http.Dispose(); -} diff --git a/Projects/UOContent/Network/CrowdSec/CrowdSecConfiguration.cs b/Projects/UOContent/Network/CrowdSec/CrowdSecConfiguration.cs deleted file mode 100644 index 5855633f9..000000000 --- a/Projects/UOContent/Network/CrowdSec/CrowdSecConfiguration.cs +++ /dev/null @@ -1,100 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: CrowdSecConfiguration.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.IO; -using System.Text.Json.Serialization; -using Server.Json; - -namespace Server.Network.Bans.CrowdSec; - -/// -/// Loads the from Configuration/crowdsec.json. Loaded once; a -/// missing file writes a disabled-by-default template so operators have something to edit. -/// -public static class CrowdSecConfiguration -{ - private const string _path = "Configuration/crowdsec.json"; - - public static CrowdSecSettings Settings { get; private set; } - - public static void Load() - { - var path = Path.Join(Core.BaseDirectory, _path); - - if (File.Exists(path)) - { - Settings = JsonConfig.Deserialize(path); - } - else - { - Settings = new CrowdSecSettings - { - LapiUrl = "http://127.0.0.1:8080", - MachineId = "", - Password = "", - Origin = "modernuo", - ManualBanDuration = TimeSpan.FromHours(168), - FlushInterval = TimeSpan.FromSeconds(1), - MaxQueue = 10000 - }; - - Save(); - } - } - - private static void Save() - { - JsonConfig.Serialize(Path.Join(Core.BaseDirectory, _path), Settings); - } -} - -/// -/// Bound configuration for . Read once at Configure(). -/// The reporter is inert unless is true. -/// -public record CrowdSecSettings -{ - /// LAPI endpoint. Default http://127.0.0.1:8080. - [JsonPropertyName("lapiUrl")] - public string LapiUrl { get; set; } = "http://127.0.0.1:8080"; - - /// Watcher machine id from cscli machines add. Empty disables reporting. - [JsonPropertyName("machineId")] - public string MachineId { get; set; } = ""; - - /// Watcher password paired with . - [JsonPropertyName("password")] - public string Password { get; set; } = ""; - - /// Decision origin stamped on our contributions. Default modernuo. - [JsonPropertyName("origin")] - public string Origin { get; set; } = "modernuo"; - - /// Duration for manual admin bans pushed to CrowdSec. Default 168h (renewable). Finite so a missed retract self-heals. - [JsonPropertyName("manualBanDuration")] - public TimeSpan ManualBanDuration { get; set; } = TimeSpan.FromHours(168); - - /// Max time the drain coalesces before flushing a batch. Default 1s. - [JsonPropertyName("flushInterval")] - public TimeSpan FlushInterval { get; set; } = TimeSpan.FromSeconds(1); - - /// Bounded contribution queue capacity; overflow is dropped (counted). Default 10000. - [JsonPropertyName("maxQueue")] - public int MaxQueue { get; set; } = 10000; - - [JsonIgnore] - public bool ReportingEnabled => !string.IsNullOrWhiteSpace(MachineId) && !string.IsNullOrWhiteSpace(Password); -} diff --git a/Projects/UOContent/Network/CrowdSec/CrowdSecReporter.cs b/Projects/UOContent/Network/CrowdSec/CrowdSecReporter.cs deleted file mode 100644 index c900eb90f..000000000 --- a/Projects/UOContent/Network/CrowdSec/CrowdSecReporter.cs +++ /dev/null @@ -1,409 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: CrowdSecReporter.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Net; -using System.Threading; -using System.Threading.Channels; -using System.Threading.Tasks; -using Server.Logging; - -namespace Server.Network.Bans.CrowdSec; - -/// -/// Contributes locally-decided bans to CrowdSec via the LAPI alerts API. Non-blocking on the accept -/// path: enqueues onto a bounded, drop-on-overflow channel drained by a single -/// background task that coalesces by IP and POSTs batched alerts. -/// -public sealed class CrowdSecReporter : IBanReporter -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(CrowdSecReporter)); - - internal readonly record struct ReportItem(IPAddress Ip, TimeSpan Ttl, string Reason, bool Retract); - - // Bounded retry for transient LAPI failures during a drain send (network blips, 5xx). Distinct from - // CrowdSecAlertClient.SendWithRetryAsync's single 401-relogin retry, which is an auth concern. - private static readonly TimeSpan[] _retryDelays = [TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)]; - - private ICrowdSecAlertClient _client; - private CrowdSecSettings _settings; - private Channel _queue; - private CancellationTokenSource _cts; - private Task _drainTask; - private int _dropped; - private int _sendFailures; - - public CrowdSecReporter() - { - } - - // Test/embedding ctor with an injected client + settings. - internal CrowdSecReporter(ICrowdSecAlertClient client, CrowdSecSettings settings) - { - _client = client; - _settings = settings; - _queue = CreateQueue(settings.MaxQueue); - } - - public string Name => "crowdsec"; - public bool CanRetract => true; - public int DroppedCount => _dropped; - - /// - /// Batches ultimately dropped after the bounded transient-retry in gave up. - /// Distinct from (queue-overflow drops on the accept path): this counts - /// sustained LAPI outages so operators can see contribution loss instead of it being silent. - /// - public int SendFailureCount => _sendFailures; - - /// The drain loop's task, so tests can assert it stays alive until the loop exits. - internal Task DrainTaskForTesting => _drainTask; - - public static void Configure() - { - BanChannel.Register(new CrowdSecReporter()); - } - - public void Register() - { - CrowdSecConfiguration.Load(); - _settings ??= CrowdSecConfiguration.Settings; - } - - public void Start(CancellationToken token) - { - if (!_settings.ReportingEnabled) - { - logger.Information("CrowdSec reporter disabled (machineId/password empty in crowdsec.json)"); - return; - } - - _client ??= new CrowdSecAlertClient(_settings); - _queue ??= CreateQueue(_settings.MaxQueue); - _cts = CancellationTokenSource.CreateLinkedTokenSource(token); - _drainTask = Task.Run(() => DrainLoop(_cts.Token), _cts.Token); - } - - public void Stop() - { - _cts?.Cancel(); - - // The flush below reads a SingleReader channel, so wait for the drain to actually exit first. - var drainExited = true; - try - { - // Wait(timeout) is false only on timeout; a throw means faulted/cancelled, which is still exited. - drainExited = _drainTask == null || _drainTask.Wait(TimeSpan.FromSeconds(2)); - } - catch - { - // Ignored: a faulted wait means the drain has completed and released the channel. - } - - _cts?.Dispose(); - _cts = null; - - if (drainExited) - { - FlushRemainingOnStop(); - } - - _client?.Dispose(); - _drainTask = null; - } - - /// - /// Best-effort bounded flush of whatever is still queued at shutdown. Blocking is correct here — the - /// loop has stopped ticking — but must not happen on the loop thread: runs where - /// SynchronizationContext.Current is the EventLoopContext, and a captured continuation - /// would be posted to a queue nothing pumps any more. keeps the - /// chain on the pool; the bounded wait caps a wedged send at a few seconds of shutdown. - /// - private void FlushRemainingOnStop() - { - if (_queue == null || _client == null) - { - return; - } - - _queue.Writer.TryComplete(); - - List reports = []; - List retracts = []; - while (_queue.Reader.TryRead(out var item)) - { - (item.Retract ? retracts : reports).Add(item); - } - - if (reports.Count == 0 && retracts.Count == 0) - { - return; - } - - try - { - if (!Task.Run(() => FlushRemainingOnStopAsync(reports, retracts)).Wait(TimeSpan.FromSeconds(4))) - { - logger.Warning( - "CrowdSec flush-on-stop timed out; {Count} item(s) not contributed", - reports.Count + retracts.Count - ); - } - } - catch (Exception e) - { - logger.Warning(e, "CrowdSec flush-on-stop failed"); - } - } - - /// - /// Uses a fresh token, not the drain loop's already-cancelled one, which would fail every send - /// immediately. Reports go as one deduped batch; retracts go as individual DELETEs so an admin's - /// unban propagates on a clean shutdown. Leftovers self-heal via - /// . - /// - private async Task FlushRemainingOnStopAsync(List reports, List retracts) - { - using var flushCts = new CancellationTokenSource(TimeSpan.FromSeconds(3)); - - if (reports.Count > 0) - { - var alerts = BuildAlerts(reports, _settings, DateTime.UtcNow); - try - { - await _client.PostAlertsAsync(alerts, flushCts.Token).ConfigureAwait(false); - } - catch (Exception e) - { - logger.Warning(e, "CrowdSec flush-on-stop reports failed"); - RecordSendFailure(alerts.Count); - } - } - - HashSet seen = []; - for (var i = 0; i < retracts.Count; i++) - { - if (flushCts.IsCancellationRequested) - { - break; // out of budget; the rest self-heal via ManualBanDuration - } - - var ip = retracts[i].Ip; - if (!seen.Add(ip.ToString())) - { - continue; - } - - try - { - await _client.DeleteDecisionsAsync(_settings.Origin, ip, flushCts.Token).ConfigureAwait(false); - } - catch (Exception e) - { - logger.Warning(e, "CrowdSec flush-on-stop retract failed for {Address}", ip); - RecordSendFailure(1); - } - } - } - - public void Report(IPAddress address, TimeSpan ttl, string reason) => - Enqueue(new ReportItem(address, ttl, reason, false)); - - public void Retract(IPAddress address) => - Enqueue(new ReportItem(address, TimeSpan.Zero, "retract", true)); - - private void Enqueue(ReportItem item) - { - if (_queue == null || !_queue.Writer.TryWrite(item)) - { - Interlocked.Increment(ref _dropped); - } - } - - // FullMode.Wait (the default) makes TryWrite return false immediately when the channel is full - // instead of blocking the caller — exactly the non-blocking drop-on-overflow behavior the accept - // path requires. DropWrite would silently discard the new item and always report success, which - // would make overflow undetectable. - private static Channel CreateQueue(int capacity) => - Channel.CreateBounded(new BoundedChannelOptions(Math.Max(1, capacity)) - { - FullMode = BoundedChannelFullMode.Wait, - SingleReader = true - }); - - // Must return Task: Start() passes this to Task.Run, which has no Func overload, so a - // ValueTask would bind to Task.Run and yield a Task that completes at the first - // await rather than when the loop exits. - private async Task DrainLoop(CancellationToken token) - { - var reader = _queue.Reader; - - while (!token.IsCancellationRequested) - { - try - { - if (!await reader.WaitToReadAsync(token).ConfigureAwait(false)) - { - return; - } - - // Coalesce a burst before flushing. - await Task.Delay(_settings.FlushInterval, token).ConfigureAwait(false); - - List reports = []; - List retracts = []; - while (reader.TryRead(out var item)) - { - (item.Retract ? retracts : reports).Add(item); - } - - if (reports.Count > 0) - { - var alerts = BuildAlerts(reports, _settings, DateTime.UtcNow); - if (!await SendWithBoundedRetryAsync(() => _client.PostAlertsAsync(alerts, token), token) - .ConfigureAwait(false)) - { - RecordSendFailure(alerts.Count); - } - } - - for (var i = 0; i < retracts.Count; i++) - { - var ip = retracts[i].Ip; - if (!await SendWithBoundedRetryAsync(() => _client.DeleteDecisionsAsync(_settings.Origin, ip, token), token) - .ConfigureAwait(false)) - { - RecordSendFailure(1); - } - } - } - catch (OperationCanceledException) - { - return; - } - catch (Exception e) - { - // Contribution is auxiliary: log and keep draining. Never crash the shard. - logger.Warning(e, "CrowdSec contribution flush failed; dropped this batch"); - } - } - } - - /// - /// Up to 3 attempts (1 initial + 2 retries) backing off 1s then 2s, for transient LAPI failures - /// (network blips, 5xx). Backoff uses so it never blocks the thread, and a - /// cancellation during it propagates so the drain loop exits cleanly. Returns false rather than - /// throwing once attempts are exhausted, so the caller counts the drop and keeps draining. - /// - private static async ValueTask SendWithBoundedRetryAsync(Func send, CancellationToken token) - { - for (var attempt = 0; ; attempt++) - { - try - { - await send().ConfigureAwait(false); - return true; - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception e) - { - if (attempt >= _retryDelays.Length) - { - logger.Warning(e, "CrowdSec send failed after {Attempts} attempt(s); giving up", attempt + 1); - return false; - } - - var delay = _retryDelays[attempt]; - logger.Warning(e, "CrowdSec send failed (attempt {Attempt}); retrying in {Delay}", attempt + 1, delay); - await Task.Delay(delay, token).ConfigureAwait(false); - } - } - } - - private void RecordSendFailure(int itemCount) - { - var total = Interlocked.Increment(ref _sendFailures); - logger.Warning( - "CrowdSec contribution batch dropped after retries ({Items} item(s)); total dropped batches: {Total}", - itemCount, - total - ); - } - - /// Coalesces items by IP (last write wins) and builds one alert per unique address. - internal static List BuildAlerts(IEnumerable items, CrowdSecSettings settings, DateTime nowUtc) - { - Dictionary byIp = []; - foreach (var item in items) - { - byIp[item.Ip.ToString()] = item; - } - - var timestamp = FormatTimestamp(nowUtc); - var alerts = new List(byIp.Count); - - foreach (var (value, item) in byIp) - { - var ttl = item.Reason == BanReasons.Manual || item.Ttl <= TimeSpan.Zero ? settings.ManualBanDuration : item.Ttl; - var scenario = $"{settings.Origin}/{item.Reason}"; - - alerts.Add(new CrowdSecAlert - { - Scenario = scenario, - Message = $"ModernUO {item.Reason} ban for {value}", - StartAt = timestamp, - StopAt = timestamp, - Source = new CrowdSecSource { Scope = "Ip", Value = value }, - Decisions = - [ - new CrowdSecDecisionDto - { - Origin = settings.Origin, - Type = "ban", - Scope = "Ip", - Value = value, - Duration = FormatDuration(ttl), - Scenario = scenario - } - ] - }); - } - - return alerts; - } - - /// - /// ISO8601/RFC3339 UTC timestamp for start_at/stop_at. LAPI parses these with Go's - /// time.RFC3339 and answers 500 when the parse fails, so the format must be culture-independent: - /// ':' is the *time separator* specifier in a custom .NET format string, and a shard running under a - /// culture like fi-FI would otherwise emit "T12.34.56.789Z". InvariantCulture also pins the Gregorian - /// calendar, which non-Gregorian cultures (th-TH, ar-SA) would otherwise shift the year for. - /// - internal static string FormatTimestamp(DateTime time) => - (time.Kind == DateTimeKind.Utc ? time : time.ToUniversalTime()) - .ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture); - - /// CrowdSec accepts Go durations; whole seconds are unambiguous and sufficient. - internal static string FormatDuration(TimeSpan ttl) - { - var seconds = (long)ttl.TotalSeconds; - return $"{Math.Max(1, seconds)}s"; - } -} diff --git a/Projects/UOContent/Network/EntityPackets.cs b/Projects/UOContent/Network/EntityPackets.cs index 190de9c4d..190026e65 100644 --- a/Projects/UOContent/Network/EntityPackets.cs +++ b/Projects/UOContent/Network/EntityPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: EntityPackets.cs * * * @@ -13,6 +13,7 @@ * along with this program. If not, see . * *************************************************************************/ +using System; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -37,7 +38,7 @@ namespace Server.Network using var builder = new PacketContainerBuilder(stackalloc byte[minLength]); - var buffer = builder.GetSpan(OutgoingEntityPackets.MaxWorldEntityPacketLength); + Span buffer = builder.GetSpan(OutgoingEntityPackets.MaxWorldEntityPacketLength); foreach (var entity in entities) { diff --git a/Projects/UOContent/Network/Firewall/Firewall.cs b/Projects/UOContent/Network/Firewall/Firewall.cs deleted file mode 100644 index 36c11bbf9..000000000 --- a/Projects/UOContent/Network/Firewall/Firewall.cs +++ /dev/null @@ -1,413 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Firewall.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Buffers; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Runtime.CompilerServices; -using Server.Collections; -using Server.Json; -using Server.Logging; - -namespace Server.Network; - -public static class Firewall -{ - // Single-threaded: the accept path, admin gump/command, TTL expiry timer, and boot load all run on - // the main game loop, so no locks, caches, or version counters are needed. _entries is the - // authoritative store; _index is a derived, rebuild-on-demand SortedRangeIndex used only for the - // accept-path IsBlocked lookup, over the same primitive the blocklist uses (see BlocklistSnapshot). - // See dev-docs/ip-bans-and-allowlists.md. - private static readonly List _entries = []; - - // Entries with a TTL: entry -> absolute expiry tick (Core.TickCount). Permanent entries are absent. - private static readonly Dictionary _expiring = []; - - private static SortedRangeIndex _index = SortedRangeIndex.Empty; - private static bool _indexDirty; - - private static readonly ILogger logger = LogFactory.GetLogger(typeof(Firewall)); - private const string _path = "Configuration/firewall.json"; - private const string _legacyPath = "firewall.cfg"; - private static bool _dirty; - private static bool _configured; - - public static int FirewallSetCount => _entries.Count; - - public static void ReadFirewallSet(Action> callback) => callback(_entries); - - public static bool IsBlocked(IPAddress address) - { - if (_entries.Count == 0) - { - return false; - } - - EnsureIndex(); - return _index.Contains(address.ToUInt128()); - } - - // Rebuilds the derived lookup index from the authoritative _entries list, but only when entries have - // changed since the last build. Runs on the main game loop, so the pooled build buffer is single-threaded - // (mt: false); only the two final SortedRangeIndex arrays are heap-allocated. - private static void EnsureIndex() - { - if (!_indexDirty) - { - return; - } - - using var ranges = PooledRefList.Range>.Create(_entries.Count, mt: false); - for (var i = 0; i < _entries.Count; i++) - { - var entry = _entries[i]; - ranges.Add(new SortedRangeIndex.Range(entry.MinIpAddress, entry.MaxIpAddress)); - } - - ranges.Sort(SortedRangeIndex.ByMin); - _index = SortedRangeIndex.Build(ranges.AsSpan()); - _indexDirty = false; - } - - public static bool Add(IFirewallEntry firewallEntry) => Add(firewallEntry, TimeSpan.Zero); - - /// - /// Adds an entry. <= means permanent. Returns false - /// if the entry was already present. - /// - // Indexed scan (no closure allocation); firewall lists are small, so O(n) is negligible and this - // stays off the hot path (Add/Remove are admin/boot actions, not the accept path). - private static int IndexOfEntry(IFirewallEntry entry) - { - for (var i = 0; i < _entries.Count; i++) - { - if (_entries[i].CompareTo(entry) == 0) - { - return i; - } - } - - return -1; - } - - public static bool Add(IFirewallEntry firewallEntry, TimeSpan ttl, bool persist = true) - { - if (firewallEntry == null || IndexOfEntry(firewallEntry) >= 0) - { - return false; - } - - _entries.Add(firewallEntry); - - if (ttl > TimeSpan.Zero) - { - _expiring[firewallEntry] = Core.TickCount + (long)ttl.TotalMilliseconds; - } - - _indexDirty = true; - - if (persist) - { - MarkDirty(); - } - - return true; - } - - public static bool Remove(IFirewallEntry entry) - { - if (entry == null) - { - return false; - } - - var index = IndexOfEntry(entry); - if (index < 0) - { - return false; - } - - // Remove the stored instance from _expiring (not the passed reference), so a value-equal - // entry created elsewhere still clears the TTL bookkeeping. - var stored = _entries[index]; - _entries.RemoveAt(index); - _expiring.Remove(stored); - _indexDirty = true; - MarkDirty(); - return true; - } - - /// - /// Removes every entry whose TTL has elapsed. Called from the main-thread maintenance timer. - /// - internal static void ExpireEntries(long nowTicks) - { - if (_expiring.Count == 0) - { - return; - } - - List expired = null; - foreach (var (entry, expiresAt) in _expiring) - { - if (expiresAt - nowTicks <= 0) - { - (expired ??= []).Add(entry); - } - } - - if (expired == null) - { - return; - } - - for (var i = 0; i < expired.Count; i++) - { - var entry = expired[i]; - _entries.Remove(entry); - _expiring.Remove(entry); - } - - _indexDirty = true; - MarkDirty(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IFirewallEntry ToFirewallEntry(object entry) => - entry switch - { - IFirewallEntry firewallEntry => firewallEntry, - IPAddress address => new SingleIpFirewallEntry(address), - string s => ToFirewallEntry(s), - _ => null - }; - - public static IFirewallEntry ToFirewallEntry(string entry) - { - if (entry == null) - { - return null; - } - - try - { - var rangeSeparator = entry.IndexOf('-'); - if (rangeSeparator > -1) - { - return new CidrFirewallEntry( - IPAddress.Parse(entry.AsSpan(0, rangeSeparator)), - IPAddress.Parse(entry.AsSpan(rangeSeparator + 1)) - ); - } - - if (entry.IndexOf('/') > -1) - { - return new CidrFirewallEntry(entry); - } - - return new SingleIpFirewallEntry(entry); - } - catch - { - return null; - } - } - - public static void Configure() - { - if (_configured) - { - return; - } - _configured = true; - - var path = Path.Join(Core.BaseDirectory, _path); - - if (File.Exists(path)) - { - LoadFrom(JsonConfig.Deserialize(path)); - } - else - { - var legacyPath = ResolveLegacyCfgPath(); - if (legacyPath != null) - { - MigrateLegacyCfg(legacyPath); - Save(); // materialize firewall.json; the .cfg is no longer read after this - TryMarkLegacyCfgMigrated(legacyPath); - } - } - - // Main-thread maintenance: expire TTLs and flush pending writes. No background thread. - Timer.DelayCall(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30), Maintenance); - - // Expose the set to the accept path. Everything else (gump, commands, persistence) keeps using - // the Firewall API directly; only the per-connection question goes through the filter registry. - ConnectionFilters.Register(FirewallConnectionFilter.Instance); - } - - private static void Maintenance() - { - ExpireEntries(Core.TickCount); - - if (_dirty) - { - Save(); - } - } - - private static void MarkDirty() => _dirty = true; - - internal static void LoadFrom(FirewallSettings settings) - { - if (settings?.Entries == null) - { - return; - } - - // Core.Now: this runs on the game loop, via the Configure sweep. - var now = Core.Now; - var records = settings.Entries; - for (var i = 0; i < records.Length; i++) - { - var record = records[i]; - var entry = ToFirewallEntry(record.Value); - if (entry == null) - { - logger.Warning("Ignoring unparseable firewall entry \"{Entry}\"", record.Value); - continue; - } - - var ttl = TimeSpan.Zero; - if (record.Expires is { } expires) - { - ttl = expires - now; - if (ttl <= TimeSpan.Zero) - { - continue; // already expired - } - } - - Add(entry, ttl, persist: false); - } - } - - internal static FirewallSettings ToSettings() - { - // expires is derived below as now + (expiresAtTick - nowTicks), so both operands must come from - // the same instant. Core.Now and Core.TickCount are refreshed together each loop iteration; a - // fresh DateTime.UtcNow here would bake the loop's lag into every persisted expiry. - var now = Core.Now; - var nowTicks = Core.TickCount; - var list = new List(_entries.Count); - - for (var i = 0; i < _entries.Count; i++) - { - var entry = _entries[i]; - DateTime? expires = null; - if (_expiring.TryGetValue(entry, out var expiresAtTick)) - { - expires = now.AddMilliseconds(expiresAtTick - nowTicks); - } - - list.Add(new FirewallEntryRecord { Value = entry.ToString(), Expires = expires }); - } - - return new FirewallSettings { Entries = list.ToArray() }; - } - - public static void Save() - { - _dirty = false; - var path = Path.Join(Core.BaseDirectory, _path); - var tmp = $"{path}.tmp"; - JsonConfig.Serialize(tmp, ToSettings()); - File.Move(tmp, path, overwrite: true); // atomic swap - } - - /// - /// Locates the legacy firewall.cfg to migrate. The modern convention is , - /// checked first; the pre-collapse AdminFirewall used a bare relative path (resolved against the - /// process's current working directory), which may differ from when the - /// shard is launched from elsewhere, so that's checked as a fallback. Returns null if neither exists. - /// - private static string ResolveLegacyCfgPath() - { - var underBaseDirectory = Path.Join(Core.BaseDirectory, _legacyPath); - if (File.Exists(underBaseDirectory)) - { - return underBaseDirectory; - } - - return File.Exists(_legacyPath) ? _legacyPath : null; - } - - private static void MigrateLegacyCfg(string legacyPath) - { - var searchValues = SearchValues.Create("*Xx?"); - - using var reader = new StreamReader(legacyPath); - while (reader.ReadLine() is { } line) - { - line = line.Trim(); - if (line.Length == 0) - { - continue; - } - - if (line.AsSpan().ContainsAny(searchValues)) - { - logger.Warning("Legacy firewall entry \"{Entry}\" ignored during migration", line); - continue; - } - - var entry = ToFirewallEntry(line); - if (entry != null) - { - Add(entry, TimeSpan.Zero, persist: false); - } - } - - logger.Information("Migrated {Count} entr(ies) from legacy firewall.cfg to firewall.json", _entries.Count); - } - - /// - /// Renames the migrated .cfg to firewall.cfg.migrated so it isn't re-scanned on the next - /// boot and operators can see it was already migrated. Best-effort: a locked/read-only file must not - /// fail startup, since the migration itself (firewall.json) already succeeded. - /// - private static void TryMarkLegacyCfgMigrated(string legacyPath) - { - try - { - File.Move(legacyPath, $"{legacyPath}.migrated", overwrite: true); - } - catch (Exception e) - { - logger.Warning(e, "Could not rename migrated legacy firewall file \"{Path}\"", legacyPath); - } - } - - internal static void ResetForTesting() - { - _entries.Clear(); - _expiring.Clear(); - _index = SortedRangeIndex.Empty; - _indexDirty = false; - _configured = false; - } -} diff --git a/Projects/UOContent/Network/Firewall/FirewallConnectionFilter.cs b/Projects/UOContent/Network/Firewall/FirewallConnectionFilter.cs deleted file mode 100644 index 570e33f19..000000000 --- a/Projects/UOContent/Network/Firewall/FirewallConnectionFilter.cs +++ /dev/null @@ -1,52 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: FirewallConnectionFilter.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Net; -using System.Threading; - -namespace Server.Network; - -/// -/// Exposes the admin-curated set to the accept path as an -/// . The firewall keeps its own API (the admin gump and commands mutate -/// it directly); this is only the accept-path adapter, since a static class cannot implement an -/// interface. It is the cheapest gate to consult — an empty set costs one length compare — so -/// registers it first. -/// -internal sealed class FirewallConnectionFilter : IConnectionFilter -{ - public static readonly FirewallConnectionFilter Instance = new(); - - private FirewallConnectionFilter() - { - } - - public string Name => "firewall"; - - // Firewall.Configure() owns loading and registration. - public void Register() - { - } - - // Nothing to hydrate: the set loads at Configure and is maintained by a main-loop timer. - public void Start(CancellationToken token) - { - } - - /// Flushes pending writes on the way down so a TTL expiry or late admin edit is not lost. - public void Stop() => Firewall.Save(); - - public bool ShouldDeny(IPAddress address) => Firewall.IsBlocked(address); -} diff --git a/Projects/UOContent/Network/Firewall/FirewallSettings.cs b/Projects/UOContent/Network/Firewall/FirewallSettings.cs deleted file mode 100644 index dd0281c92..000000000 --- a/Projects/UOContent/Network/Firewall/FirewallSettings.cs +++ /dev/null @@ -1,42 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: FirewallSettings.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Text.Json.Serialization; - -namespace Server.Network; - -/// -/// Persisted local firewall entries (manual admin bans). Stored at Configuration/firewall.json. -/// Auto-detected rate-limit trips are never persisted — they are contributed to CrowdSec, not stored here. -/// -public record FirewallSettings -{ - [JsonPropertyName("entries")] - public FirewallEntryRecord[] Entries { get; set; } = []; -} - -/// -/// One persisted entry. is a single IP, a min-max range, or CIDR. -/// is UTC wall-clock; null means permanent. -/// -public record FirewallEntryRecord -{ - [JsonPropertyName("value")] - public string Value { get; set; } - - [JsonPropertyName("expires")] - public DateTime? Expires { get; set; } -} diff --git a/Projects/UOContent/Network/FreeshardProtocol.cs b/Projects/UOContent/Network/FreeshardProtocol.cs index a827e2106..ada158275 100644 --- a/Projects/UOContent/Network/FreeshardProtocol.cs +++ b/Projects/UOContent/Network/FreeshardProtocol.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: FreeshardProtocol.cs * * * diff --git a/Projects/UOContent/Network/GameServer.cs b/Projects/UOContent/Network/GameServer.cs index b9de09515..9fc170d69 100644 --- a/Projects/UOContent/Network/GameServer.cs +++ b/Projects/UOContent/Network/GameServer.cs @@ -6,21 +6,13 @@ public static partial class GameServer { public class GameLoginEventArgs { - public GameLoginEventArgs(NetState state, string un, string pw, bool preAuthenticated) + public GameLoginEventArgs(NetState state, string un, string pw) { State = state; Username = un; Password = pw; - PreAuthenticated = preAuthenticated; } - /// - /// The auth id presented on this game login was issued to this account, from this address, - /// after the account login packet verified the password. Read-only so a subscriber cannot - /// grant itself the skip. - /// - public bool PreAuthenticated { get; } - public NetState State { get; } public string Username { get; } diff --git a/Projects/UOContent/Network/LoginAllowlist/LoginAllowlist.cs b/Projects/UOContent/Network/LoginAllowlist/LoginAllowlist.cs deleted file mode 100644 index 23075f7ff..000000000 --- a/Projects/UOContent/Network/LoginAllowlist/LoginAllowlist.cs +++ /dev/null @@ -1,440 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: LoginAllowlist.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Server.Logging; -using Server.Network.Bans; - -namespace Server.Network; - -/// -/// An allowlist addresses earn by logging in successfully, so a reputation feed cannot get a known player -/// blocked and a flaky connection cannot get one globally banned. -/// -/// -/// Consulted only after the blocklist has already matched, so a normal accept pays nothing for it. An entry -/// is evidence rather than a licence: enough strikes inside the window revokes it. It cannot bootstrap, so -/// it does not replace . Both dictionaries are game-loop state; only the file -/// write runs off-loop, over a snapshot taken on the loop. -/// See dev-docs/ip-bans-and-allowlists.md. -/// -public static class LoginAllowlist -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(LoginAllowlist)); - - // Address (normalized v6 bits) -> unix seconds of its last successful login. Loop-only. - private static readonly Dictionary _allowed = []; - - // Suppressed contributions in the current window. Only holds allowlisted addresses, so it is bounded by - // _allowed and cannot be grown by an attacker. - private static readonly Dictionary _strikes = []; - - // Reused: past ~5,300 entries a fresh UInt128[] is an LOH allocation, once per flush. Grown - // geometrically, never shrunk. - private static UInt128[] _addressBuffer = []; - private static long[] _stampBuffer = []; - - private static bool _enabled; - private static string _path; - private static long _ttlSeconds; - private static int _escalateAfterStrikes; - private static long _strikeWindowSeconds; - private static bool _dirty; - - // Loop-only. The writer owns the buffers until it posts completion back, so a flush landing mid-write - // waits rather than overwriting them. - private static bool _writing; - - public static int Count => _allowed.Count; - - private struct Strike - { - public int Count; - public long WindowStart; // unix seconds; 0 means "no window open" - } - - public static void Configure() - { - LoginAllowlistConfiguration.Load(); - var s = LoginAllowlistConfiguration.Settings; - - _enabled = s.Enabled && !string.IsNullOrWhiteSpace(s.File) && s.Ttl > TimeSpan.Zero; - if (!_enabled) - { - return; - } - - _path = Path.IsPathRooted(s.File) ? s.File : Path.Join(Core.BaseDirectory, s.File); - _ttlSeconds = (long)s.Ttl.TotalSeconds; - _escalateAfterStrikes = s.EscalateAfterStrikes; - _strikeWindowSeconds = (long)s.StrikeWindow.TotalSeconds; - } - - public static void Initialize() - { - if (!_enabled) - { - logger.Information("Login allowlist disabled"); - return; - } - - Load(); - - var interval = LoginAllowlistConfiguration.Settings.FlushInterval; - if (interval <= TimeSpan.Zero) - { - interval = TimeSpan.FromHours(1); - } - - Timer.DelayCall(interval, interval, Flush); - - // HandleClosed skips InvokeShutdown when the server crashed, so the crash path needs its own. - EventSink.Shutdown += OnShutdown; - EventSink.ServerCrashed += OnCrashed; - } - - /// - /// Records a successful authentication. Private addresses are skipped: a LAN or loopback login says - /// nothing about the public internet. - /// - public static void RecordLogin(IPAddress address) => RecordLogin(address, ToUnixSeconds(Core.Now)); - - /// The pure write, split out so policy can be tested without a clock. - internal static void RecordLogin(IPAddress address, long nowUnix) - { - if (!_enabled || address == null || address.IsPrivateNetwork()) - { - return; - } - - var key = address.ToUInt128(); - _allowed[key] = nowUnix; - - // A fresh login clears the tally: someone just proved they hold an account. - _strikes.Remove(key); - _dirty = true; - } - - /// - /// True when this address logged in within the TTL. Expiry is decided on read, so a stale entry left for - /// the next flush can never allow anything. - /// - public static bool IsAllowed(IPAddress address) => IsAllowed(address, ToUnixSeconds(Core.Now)); - - /// The pure decision, split out so the TTL policy can be tested without a clock. - internal static bool IsAllowed(IPAddress address, long nowUnix) - { - if (!_enabled || address == null) - { - return false; - } - - return _allowed.TryGetValue(address.ToUInt128(), out var stamp) && nowUnix - stamp <= _ttlSeconds; - } - - public static bool IsExemptFromEscalation(IPAddress address, string reason) => - IsExemptFromEscalation(address, reason, ToUnixSeconds(Core.Now)); - - /// - /// Whether this contribution should be dropped instead of escalated, counting a strike if so. Not a pure - /// read — calling it is what spends the address's allowance. - /// - internal static bool IsExemptFromEscalation(IPAddress address, string reason, long nowUnix) - { - // An operator's explicit ban, or a reason nobody opted in, escalates untouched. - if (!BanReasons.IsBehavioral(reason) || !IsAllowed(address, nowUnix)) - { - return false; - } - - if (_escalateAfterStrikes <= 0) - { - return true; // revocation disabled: an entry is unconditional - } - - var key = address.ToUInt128(); - _strikes.TryGetValue(key, out var strike); - - if (strike.WindowStart == 0 || nowUnix - strike.WindowStart > _strikeWindowSeconds) - { - strike = new Strike { WindowStart = nowUnix }; - } - - strike.Count++; - - if (strike.Count < _escalateAfterStrikes) - { - _strikes[key] = strike; - return true; - } - - // Allowance spent: drop the entry so this and all after it escalate. Earned back by logging in. - _allowed.Remove(key); - _strikes.Remove(key); - _dirty = true; - - logger.Information( - "{Address} revoked from the login allowlist after {Count} suppressed contribution(s); last was '{Reason}'", - address, - strike.Count, - reason - ); - - return false; - } - - internal static void LoadForTesting(bool enabled, long ttlSeconds, int escalateAfterStrikes = 0, long strikeWindowSeconds = 3600) - { - _allowed.Clear(); - _strikes.Clear(); - _writing = false; - _dirty = false; - _enabled = enabled; - _ttlSeconds = ttlSeconds; - _escalateAfterStrikes = escalateAfterStrikes; - _strikeWindowSeconds = strikeWindowSeconds; - _path = null; - } - - private static long ToUnixSeconds(DateTime utc) => (long)(utc - DateTime.UnixEpoch).TotalSeconds; - - private static void Flush() - { - // A save owns the disk and nothing here is urgent. _dirty stays set, so skipping loses nothing. - // See the threading policy in CLAUDE.md (rules #3 and #10). - if (!_enabled || !_dirty || _writing || World.Saving || World.WorldState == WorldState.PendingSave) - { - return; - } - - var count = Snapshot(out var dropped); - var addresses = _addressBuffer; - var stamps = _stampBuffer; - var path = _path; - - _dirty = false; - _writing = true; - - _ = Task.Run( - () => - { - var written = Write(path, addresses, stamps, count, dropped); - - // _writing and _dirty are loop state, so the writer hands the release back. Rule #10. - Core.LoopContext.Post( - () => - { - _writing = false; - if (!written) - { - _dirty = true; // nothing reached disk; the next flush retries - } - } - ); - } - ); - } - - /// - /// A crash is the case the flush interval cannot cover, so write on the way down. Runs on whichever - /// thread faulted, and the dictionaries are loop state, so it only writes when that is the loop. - /// - private static void OnCrashed(ServerCrashedEventArgs e) - { - if (Thread.CurrentThread == Core.Thread) - { - OnShutdown(); - } - } - - /// Synchronous: nothing schedules after this, so a handed-off write would reach no disk. - private static void OnShutdown() - { - // A write already in flight holds the buffers and has all but the last moments of the list. - if (!_enabled || !_dirty || _writing) - { - return; - } - - var count = Snapshot(out var dropped); - _dirty = false; - - Write(_path, _addressBuffer, _stampBuffer, count, dropped); - } - - /// - /// Prunes expired entries and copies what survives into the shared buffers. Returns the live count; the - /// buffers run longer and everything past it is stale. - /// - private static int Snapshot(out int dropped) - { - var nowUnix = ToUnixSeconds(Core.Now); - var cutoff = nowUnix - _ttlSeconds; - - if (_addressBuffer.Length < _allowed.Count) - { - // Geometric so a shard adding addresses one at a time does not reallocate every flush. - var size = Math.Max(_allowed.Count, Math.Max(64, _addressBuffer.Length * 2)); - _addressBuffer = new UInt128[size]; - _stampBuffer = new long[size]; - } - - var count = 0; - dropped = 0; - - foreach (var (address, stamp) in _allowed) - { - if (stamp < cutoff) - { - _allowed.Remove(address); - _strikes.Remove(address); - dropped++; - continue; - } - - _addressBuffer[count] = address; - _stampBuffer[count] = stamp; - count++; - } - - PruneStaleStrikes(nowUnix); - return count; - } - - /// Drops tallies whose window has closed. - private static void PruneStaleStrikes(long nowUnix) - { - if (_strikes.Count == 0) - { - return; - } - - foreach (var (address, strike) in _strikes) - { - if (nowUnix - strike.WindowStart > _strikeWindowSeconds) - { - _strikes.Remove(address); - } - } - } - - /// Writes the list out. Returns false when nothing reached disk, so the caller can retry. - private static bool Write(string path, UInt128[] addresses, long[] stamps, int count, int dropped) - { - try - { - var dir = Path.GetDirectoryName(path); - if (!string.IsNullOrEmpty(dir)) - { - Directory.CreateDirectory(dir); - } - - // Sibling + swap, so a reader never sees a half-written list. - var tmp = path + ".tmp"; - - using (var writer = new StreamWriter(tmp, false, new UTF8Encoding(false), 1 << 16)) - { - writer.Write("# modernuo-login-allowlist generated="); - writer.Write(DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)); - writer.Write(" count="); - writer.Write(count); - writer.Write('\n'); - - for (var i = 0; i < count; i++) - { - writer.Write(addresses[i].ToIpAddress().ToString()); - writer.Write(' '); - writer.Write(stamps[i]); - writer.Write('\n'); - } - } - - File.Move(tmp, path, true); - - if (dropped > 0) - { - logger.Information("Login allowlist wrote {Count} entr(ies), dropped {Dropped} past TTL", count, dropped); - } - - return true; - } - catch (Exception e) - { - // Recoverable: entries are still in memory and the next flush retries. - logger.Warning(e, "Could not write the login allowlist to \"{Path}\"", path); - return false; - } - } - - private static void Load() - { - if (!File.Exists(_path)) - { - logger.Information("Login allowlist empty: no file at \"{Path}\"", _path); - return; - } - - var cutoff = ToUnixSeconds(Core.Now) - _ttlSeconds; - var loaded = 0; - var skipped = 0; - - try - { - foreach (var line in File.ReadLines(_path)) - { - var span = line.AsSpan().Trim(); - if (span.Length == 0 || span[0] == '#' || span[0] == ';') - { - continue; - } - - var sep = span.IndexOf(' '); - if (sep <= 0 || - !IPAddress.TryParse(span[..sep], out var address) || - !long.TryParse(span[(sep + 1)..].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var stamp)) - { - skipped++; - continue; - } - - // Expired on disk: do not carry a stranger into memory. - if (stamp < cutoff) - { - skipped++; - _dirty = true; // the file is now out of date; the next flush rewrites it - continue; - } - - _allowed[address.ToUInt128()] = stamp; - loaded++; - } - } - catch (Exception e) - { - // Fail open: an unreadable list allows nobody, which beats refusing to boot. - logger.Warning(e, "Could not read the login allowlist at \"{Path}\"; continuing with {Count}", _path, _allowed.Count); - return; - } - - logger.Information("Login allowlist loaded {Loaded} entr(ies) ({Skipped} expired or malformed)", loaded, skipped); - } -} diff --git a/Projects/UOContent/Network/LoginAllowlist/LoginAllowlistConfiguration.cs b/Projects/UOContent/Network/LoginAllowlist/LoginAllowlistConfiguration.cs deleted file mode 100644 index 5c673eb10..000000000 --- a/Projects/UOContent/Network/LoginAllowlist/LoginAllowlistConfiguration.cs +++ /dev/null @@ -1,106 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: LoginAllowlistConfiguration.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.IO; -using System.Text.Json.Serialization; -using Server.Json; - -namespace Server.Network; - -/// -/// Loads the from Configuration/login-allowlist.json. Loaded -/// once; a missing file writes a template so operators have something to edit. -/// -public static class LoginAllowlistConfiguration -{ - private const string _path = "Configuration/login-allowlist.json"; - - public static LoginAllowlistSettings Settings { get; private set; } - - public static void Load() - { - var path = Path.Join(Core.BaseDirectory, _path); - - if (File.Exists(path)) - { - Settings = JsonConfig.Deserialize(path); - } - else - { - Settings = new LoginAllowlistSettings(); - Save(); - } - } - - private static void Save() - { - JsonConfig.Serialize(Path.Join(Core.BaseDirectory, _path), Settings); - } -} - -/// -/// Bound configuration for : which addresses have recently proven they carry a -/// real player, how long that proof counts, and how much misbehaviour revokes it. -/// -/// -/// The recency window is the point. Consumer addresses are reassigned constantly, and on CGNAT the same -/// address fronts a different subscriber week to week, so a list without a TTL becomes a list of strangers. -/// -public record LoginAllowlistSettings -{ - /// Whether successful logins are recorded and consulted at all. Disabled makes the list inert. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } = true; - - /// - /// Where the list is persisted. A relative path resolves against . - /// Plain text, one address unix-seconds pair per line, so it can be read and edited by hand. - /// Set to "" to disable. - /// - [JsonPropertyName("file")] - public string File { get; set; } = "Configuration/login-allowlist.txt"; - - /// - /// How long a successful login allowlists its address; dropped on the next flush after that. 90 days - /// covers a player who takes a season off without carrying a reassigned address indefinitely. - /// - [JsonPropertyName("ttl")] - public TimeSpan Ttl { get; set; } = TimeSpan.FromDays(90); - - /// - /// How often a changed list is written out. A clean shutdown always writes, so this only bounds what a - /// crash loses — and an entry is re-earned by the next login. Hourly against a 90-day TTL, because each - /// flush walks the whole list on the game loop. - /// - [JsonPropertyName("flushInterval")] - public TimeSpan FlushInterval { get; set; } = TimeSpan.FromHours(1); - - /// - /// How many suppressed contributions inside revoke an address's entry. Past - /// this it escalates like anything else until it earns a new entry by logging in again. - /// - /// - /// Generous on purpose: local defenses never stop applying, so a high threshold only delays the external - /// ban. A bad line might trip a gate a few times an hour; a host being used to flood burns through this - /// in seconds. Set to 0 to never revoke. - /// - [JsonPropertyName("escalateAfterStrikes")] - public int EscalateAfterStrikes { get; set; } = 10; - - /// Rolling window the strike count is measured over. A quiet hour clears the tally. - [JsonPropertyName("strikeWindow")] - public TimeSpan StrikeWindow { get; set; } = TimeSpan.FromHours(1); -} diff --git a/Projects/UOContent/Network/ManualAllowlist/ManualAllowlist.cs b/Projects/UOContent/Network/ManualAllowlist/ManualAllowlist.cs deleted file mode 100644 index 20e351457..000000000 --- a/Projects/UOContent/Network/ManualAllowlist/ManualAllowlist.cs +++ /dev/null @@ -1,340 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ManualAllowlist.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Server.Logging; - -namespace Server.Network.Bans; - -/// -/// The operator's own "leave this address alone" list, read from the same files -/// tools/Export-IpBlocklist.ps1 subtracts at generation time. -/// -/// -/// The generator already subtracts these from the blocklist, but that only covers being BLOCKED. -/// Behavioural detections never consult the blocklist, so without reading the files here a carve-out is -/// quietly routed around: one scanner behind a shared CGNAT address is enough to get the whole address -/// contributed and firewalled. Reading them also means an entry applies on the next reload rather than the -/// next regeneration. Opt-in via ip-allowlist.json's enabled, since the poll runs for the -/// whole uptime; no shield against a manual ban either — see . -/// -public static class ManualAllowlist -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ManualAllowlist)); - - // Written by the reload poll (off-loop), read by the accept path (game loop). One volatile reference - // swap is the whole synchronization story: readers see the old or the new snapshot, whole. - private static volatile BlocklistSnapshot _snapshot = BlocklistSnapshot.Empty; - - private static string[] _patterns = []; - private static TimeSpan _interval = TimeSpan.FromSeconds(60); - private static long _lastStamp; - private static CancellationTokenSource _cts; - - public static int Count => _snapshot.Count; - - /// True when an operator listed this address. Safe before . - public static bool Contains(IPAddress address) => address != null && _snapshot.Contains(address); - - /// True when the shard is reading allowlist files. Safe before . - public static bool Enabled { get; private set; } - - public static void Initialize() - { - ManualAllowlistConfiguration.Load(); - var settings = ManualAllowlistConfiguration.Settings; - if (settings == null) - { - return; - } - - _patterns = ResolvePaths(settings.Files); - _interval = settings.ReloadInterval <= TimeSpan.Zero ? TimeSpan.FromSeconds(60) : settings.ReloadInterval; - - if (!settings.Enabled) - { - // The generator still subtracts these files, so a carve-out an operator already wrote looks - // like it works right up until a behavioural detection contributes the address anyway. - var present = ExpandPaths().Length; - _patterns = []; - - if (present > 0) - { - logger.Warning( - "Manual allowlist is off (\"enabled\" false in ip-allowlist.json) but {Count} allowlist file(s) " + - "are present; those carve-outs will not suppress ban contributions", - present - ); - } - else - { - logger.Information("Manual allowlist disabled (\"enabled\" false in ip-allowlist.json)"); - } - - return; - } - - if (_patterns.Length == 0) - { - logger.Information("Manual allowlist disabled (\"files\" empty in ip-allowlist.json)"); - return; - } - - Enabled = true; - Reload(); - - _cts = CancellationTokenSource.CreateLinkedTokenSource(Core.ClosingTokenSource.Token); - _ = Task.Run(() => PollLoop(_cts.Token), _cts.Token); - } - - public static void Stop() - { - _cts?.Cancel(); - _cts?.Dispose(); - _cts = null; - } - - private static string[] ResolvePaths(string[] configured) - { - if (configured == null) - { - return []; - } - - var resolved = new string[configured.Length]; - var count = 0; - - for (var i = 0; i < configured.Length; i++) - { - var path = configured[i]; - if (string.IsNullOrWhiteSpace(path)) - { - continue; - } - - // Relative resolves against BaseDirectory, never the working directory, which differs when the - // shard is launched from elsewhere. Absolute is as-is, so shards can share a list. - resolved[count++] = Path.IsPathRooted(path) ? path : Path.Join(Core.BaseDirectory, path); - } - - Array.Resize(ref resolved, count); - return resolved; - } - - /// - /// Expands the configured patterns to actual files. Done per poll rather than once, so a carve-out an - /// admin adds is picked up without a restart. - /// - private static string[] ExpandPaths() - { - var files = new List(); - - for (var i = 0; i < _patterns.Length; i++) - { - var pattern = _patterns[i]; - var name = Path.GetFileName(pattern); - - if (name.IndexOf('*') < 0 && name.IndexOf('?') < 0) - { - if (File.Exists(pattern)) - { - files.Add(pattern); - } - - continue; - } - - try - { - var dir = Path.GetDirectoryName(pattern); - if (string.IsNullOrEmpty(dir) || !Directory.Exists(dir)) - { - continue; - } - - var matches = Directory.GetFiles(dir, name); - Array.Sort(matches, StringComparer.Ordinal); - - for (var j = 0; j < matches.Length; j++) - { - // Windows wildcard matching still honours legacy short names, so ".txt" can pull in the - // generator's ".txt.tmp" mid-swap. Check the real extension. - if (matches[j].EndsWith(".txt", StringComparison.OrdinalIgnoreCase)) - { - files.Add(matches[j]); - } - } - } - catch - { - // Unreadable directory; the next poll retries. - } - } - - return files.ToArray(); - } - - private static async ValueTask PollLoop(CancellationToken token) - { - while (!token.IsCancellationRequested) - { - try - { - await Task.Delay(_interval, token); - } - catch (OperationCanceledException) - { - return; - } - - try - { - if (Stamp() != _lastStamp) - { - // A save owns the disk and nothing here is urgent. - // See the threading policy in CLAUDE.md (rules #3 and #10). - while (World.Saving || World.WorldState == WorldState.PendingSave) - { - await Task.Delay(TimeSpan.FromSeconds(1), token); - } - - Reload(); - } - } - catch (OperationCanceledException) - { - return; - } - catch (Exception e) - { - logger.Warning(e, "Manual allowlist reload check failed; keeping last snapshot ({Count})", Count); - } - } - } - - /// - /// Change fingerprint across every configured file. A missing file contributes nothing, so creating or - /// deleting one also registers as a change. - /// - private static long Stamp() - { - var stamp = 0L; - var paths = ExpandPaths(); - - for (var i = 0; i < paths.Length; i++) - { - try - { - var info = new FileInfo(paths[i]); - if (info.Exists) - { - stamp = stamp * 31 + info.LastWriteTimeUtc.Ticks + info.Length; - } - } - catch - { - // Mid-swap by the generator; the next poll picks it up. - } - } - - return stamp; - } - - private static void Reload() - { - // Fingerprint BEFORE parsing, so it describes the version being read. Capturing after could skip a - // version; a stale fingerprint only costs an extra reload. - var stamp = Stamp(); - var combined = ReadAll(out var files); - - // Reuses the blocklist parser and interval index: an address set is direction-agnostic. - var next = combined.Length == 0 - ? BlocklistSnapshot.Empty - : BlocklistSnapshot.Build(combined, out _, out _); - - _snapshot = next; // single volatile swap; readers see old or new whole - _lastStamp = stamp; - - logger.Information( - "Manual allowlist loaded {Count} range(s) from {Files} file(s)", - next.Count, - files - ); - } - - /// - /// Concatenates every configured file into one buffer. The parser is line-based, so a newline join is - /// enough, and membership stays a single lookup. - /// - private static byte[] ReadAll(out int files) - { - files = 0; - - var paths = ExpandPaths(); - var chunks = new byte[paths.Length][]; - var total = 0; - - for (var i = 0; i < paths.Length; i++) - { - try - { - if (!File.Exists(paths[i])) - { - continue; - } - - var bytes = File.ReadAllBytes(paths[i]); - chunks[i] = bytes; - total += bytes.Length + 1; // + newline separator - files++; - } - catch (Exception e) - { - // Fail open per file: losing one entry beats refusing to load the rest. - logger.Warning(e, "Could not read allowlist \"{Path}\"", paths[i]); - } - } - - if (total == 0) - { - return []; - } - - var combined = new byte[total]; - var offset = 0; - - for (var i = 0; i < chunks.Length; i++) - { - var chunk = chunks[i]; - if (chunk == null) - { - continue; - } - - Buffer.BlockCopy(chunk, 0, combined, offset, chunk.Length); - offset += chunk.Length; - combined[offset++] = (byte)'\n'; - } - - return combined; - } - - internal static void LoadForTesting(BlocklistSnapshot snapshot) => _snapshot = snapshot ?? BlocklistSnapshot.Empty; -} diff --git a/Projects/UOContent/Network/ManualAllowlist/ManualAllowlistConfiguration.cs b/Projects/UOContent/Network/ManualAllowlist/ManualAllowlistConfiguration.cs deleted file mode 100644 index dd21fe97c..000000000 --- a/Projects/UOContent/Network/ManualAllowlist/ManualAllowlistConfiguration.cs +++ /dev/null @@ -1,83 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ManualAllowlistConfiguration.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.IO; -using System.Text.Json.Serialization; -using Server.Json; - -namespace Server.Network.Bans; - -/// -/// Loads the from Configuration/ip-allowlist.json. Loaded once; -/// a missing file writes a template so operators have something to edit. -/// -public static class ManualAllowlistConfiguration -{ - private const string _path = "Configuration/ip-allowlist.json"; - - public static ManualAllowlistSettings Settings { get; private set; } - - public static void Load() - { - var path = Path.Join(Core.BaseDirectory, _path); - - if (File.Exists(path)) - { - Settings = JsonConfig.Deserialize(path); - } - else - { - Settings = new ManualAllowlistSettings(); - Save(); - } - } - - private static void Save() - { - JsonConfig.Serialize(Path.Join(Core.BaseDirectory, _path), Settings); - } -} - -/// -/// Bound configuration for . Its own file rather than a corner of -/// blocklist.json: the blocklist is only one of two consumers, and the other -/// () works on a shard that runs no blocklist at all. -/// -public record ManualAllowlistSettings -{ - /// - /// Whether the shard reads at all. Off by default: reading them costs a poll for - /// the whole uptime, which no shard should pay before an operator has written a carve-out. - /// - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } - - /// - /// Addresses that must never be blocked and never escalated, in the blocklist's own format. The same - /// files tools/Export-IpBlocklist.ps1 subtracts at generation time; the shard reads them so an - /// entry also suppresses ban contributions, which the generator alone cannot do. - /// - /// - /// The filename may contain wildcards, which is how the default picks up a carve-out an admin adds - /// without anyone editing this file. - /// - [JsonPropertyName("files")] - public string[] Files { get; set; } = ["Configuration/ip-allowlist*.txt"]; - - /// How often the files are checked for changes. Reloads only happen when one actually changed. - [JsonPropertyName("reloadInterval")] - public TimeSpan ReloadInterval { get; set; } = TimeSpan.FromSeconds(60); -} diff --git a/Projects/UOContent/Network/MapUO.cs b/Projects/UOContent/Network/MapUO.cs index d751cb021..695d9fd01 100644 --- a/Projects/UOContent/Network/MapUO.cs +++ b/Projects/UOContent/Network/MapUO.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: MapUO.cs * * * @@ -31,14 +31,14 @@ public static class MapUO public static void QueryGuildMemberLocations(NetState state, SpanReader reader) { - var from = state.Mobile; + Mobile from = state.Mobile; state.SendGuildMemberLocations(from, from.Guild as Guild, reader.ReadBoolean()); } public static void QueryPartyMemberLocations(NetState state, SpanReader reader) { - var from = state.Mobile; + Mobile from = state.Mobile; var party = Party.Get(from); if (party != null) @@ -62,7 +62,7 @@ public static class MapUO writer.Write((byte)0x02); // Command writer.Write(count > 0 && sendLocations); - var sendPacket = false; + bool sendPacket = false; for (var i = 0; i < count; i++) { var m = guild!.Members[i]; @@ -121,11 +121,11 @@ public static class MapUO writer.Seek(2, SeekOrigin.Current); writer.Write((byte)0x01); // Command - var sendPacket = false; + bool sendPacket = false; for (var i = 0; i < count; i++) { var pmi = party!.Members[i]; - var mob = pmi?.Mobile; + Mobile mob = pmi?.Mobile; if (mob?.NetState == null || mob == from) { diff --git a/Projects/UOContent/Network/Packets/IncomingAccountPackets.cs b/Projects/UOContent/Network/Packets/IncomingAccountPackets.cs index 792ed880b..3a658effe 100644 --- a/Projects/UOContent/Network/Packets/IncomingAccountPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingAccountPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingAccountPackets.cs * * * @@ -17,9 +17,6 @@ using System; using System.Buffers; using System.Collections.Generic; using System.IO; -using System.Net; -using System.Security.Cryptography; -using Server.Accounting; using Server.Engines.CharacterCreation; using Server.Misc; using Server.Mobiles; @@ -28,16 +25,7 @@ namespace Server.Network; public static class IncomingAccountPackets { - // Initial capacity and the point at which issuing sweeps expired ids. Not a cap; the window - // grows rather than evicting a live id. private const int _authIDWindowSize = 128; - - private static int _authIdPurgeThreshold = _authIDWindowSize; - - // The gap between PlayServerAck and the game login is seconds. Bounds how long a stolen id - // stays usable. - private static readonly TimeSpan _authIDLifetime = TimeSpan.FromMinutes(2.0); - private static readonly Dictionary _authIDWindow = new(_authIDWindowSize); @@ -46,33 +34,13 @@ public static class IncomingAccountPackets public DateTime Age; public readonly ClientVersion Version; - // GameLogin skips its password verify when both match, so the id is a bearer token and has - // to be bound to whatever earned it. - public readonly IAccount Account; - public readonly IPAddress Address; - - public AuthIDPersistence(ClientVersion v, IAccount account, IPAddress address) + public AuthIDPersistence(ClientVersion v) { Age = Core.Now; Version = v; - Account = account; - Address = Utility.Intern(address); } } - internal enum AuthIdResult - { - // No such id, or it was issued for a different account or address. - Rejected, - - // Right account and address, too old to stand in for the verify. Idling on the server list - // is normal, so this falls back to the password check rather than becoming a lockout. - Expired, - - // Issued to this account, from this address, recently. Stands in for the password verify. - Vouched - } - public static unsafe void Configure() { IncomingPackets.Register(0x00, &CreateCharacter, 104, outgameOnly: true); @@ -95,7 +63,7 @@ public static class IncomingAccountPackets var unk2 = reader.ReadInt32(); int unk3 = reader.ReadByte(); */ - var name = reader.ReadLatin1Safe(30); + var name = reader.ReadAscii(30); reader.Seek(2, SeekOrigin.Current); var flags = reader.ReadInt32(); @@ -148,7 +116,7 @@ public static class IncomingAccountPackets var female = genderRace % 2 != 0; var raceID = state.StygianAbyss ? (byte)(genderRace < 4 ? 0 : genderRace / 2 - 1) : (byte)(genderRace / 2); - var race = Race.Races[raceID] ?? Race.DefaultRace; + Race race = Race.Races[raceID] ?? Race.DefaultRace; var info = state.CityInfo; var a = state.Account; @@ -226,9 +194,6 @@ public static class IncomingAccountPackets { var version = state.Version = new ClientVersion(reader.ReadAscii()); - // Record RTT if this is a response to our probe - state.RecordRttMeasurement(); - ClientVerification.ClientVersionReceived(state, version); } @@ -239,9 +204,6 @@ public static class IncomingAccountPackets int type = reader.ReadUInt16(); var version = state.Version = new ClientVersion(reader.ReadAscii()); - // Record RTT if this is a response to our probe - state.RecordRttMeasurement(); - ClientVerification.ClientVersionReceived(state, version); } @@ -308,7 +270,7 @@ public static class IncomingAccountPackets state.SendSupportedFeature(); - state.ResetMovementState(); + state.Sequence = 0; state.SendMobileUpdate(m); state.SendMobileUpdate(m); @@ -344,92 +306,42 @@ public static class IncomingAccountPackets } } - private static int GenerateAuthID(this NetState state) => - EnsureAuthId(state.AuthId, state.Account, state.Address, state.Version); - - /// - /// One id per connection, by construction. Choosing a server queues a disconnect that is not - /// drained until the next slice, so a client pipelining another select into the same buffer - /// arrives here again; handing back the id it already holds cannot orphan one. - /// - internal static int EnsureAuthId(int existingAuthId, IAccount account, IPAddress address, ClientVersion version) - => existingAuthId != 0 ? existingAuthId : RegisterAuthId(account, address, version); - - internal static int RegisterAuthId(IAccount account, IPAddress address, ClientVersion version) + private static int GenerateAuthID(this NetState state) { - // Sweep the ids left behind by clients that picked a server and never arrived, but never - // evict a live one to make room -- the client holding it is on its way to redeem it. If all - // are live the window grows, which is a login rush, not a backlog. Each entry costs a - // successful password verify, so the size is self-limiting. - if (_authIDWindow.Count >= _authIdPurgeThreshold) + if (_authIDWindow.Count == _authIDWindowSize) { - PurgeExpiredAuthIds(); - _authIdPurgeThreshold = Math.Max(_authIDWindowSize, _authIDWindow.Count * 2); + var oldestID = 0; + var oldest = DateTime.MaxValue; + + foreach (var (key, authId) in _authIDWindow) + { + if (authId.Age < oldest) + { + oldestID = key; + oldest = authId.Age; + } + } + + _authIDWindow.Remove(oldestID); } int authID; - // The id stands in for a password verify, so it has to be unguessable. Zero is reserved: - // GameLogin reads state.AuthId == 0 as "no auth id was issued". do { - authID = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue); - } while (authID == 0 || _authIDWindow.ContainsKey(authID)); + authID = Utility.Random(1, int.MaxValue - 1); - _authIDWindow[authID] = new AuthIDPersistence(version, account, address); + if (Utility.RandomBool()) + { + authID |= 1 << 31; + } + } while (_authIDWindow.ContainsKey(authID)); + + _authIDWindow[authID] = new AuthIDPersistence(state.Version); return authID; } - /// - /// Spends an auth id, but only for the account and address it was issued to. An address - /// mismatch is rather than a fallback: network switching - /// mid-login is not supported. - /// - internal static AuthIdResult ConsumeAuthId(int authId, string username, IPAddress address, out AuthIDPersistence entry) - { - if (!_authIDWindow.TryGetValue(authId, out entry)) - { - return AuthIdResult.Rejected; - } - - // Look, then take: removing before ownership is proven would let anyone landing on a live id - // burn it, leaving its owner to log in again. Address before username, so a remote guesser - // never learns whether a username matched. - if (!Utility.Intern(address).Equals(entry.Address) - || entry.Account == null || !username.InsensitiveEquals(entry.Account.Username)) - { - entry = default; - return AuthIdResult.Rejected; - } - - // Theirs, so spend it. Expired counts as spent; it has done all it is ever going to do. - _authIDWindow.Remove(authId); - - return Core.Now - entry.Age > _authIDLifetime ? AuthIdResult.Expired : AuthIdResult.Vouched; - } - - private static void PurgeExpiredAuthIds() - { - var now = Core.Now; - - foreach (var (key, entry) in _authIDWindow) - { - if (now - entry.Age > _authIDLifetime) - { - _authIDWindow.Remove(key); - } - } - } - - internal static void ClearAuthIdWindow() - { - _authIDWindow.Clear(); - _authIdPurgeThreshold = _authIDWindowSize; - } - - internal static int AuthIdWindowCount => _authIDWindow.Count; - public static void GameLogin(NetState state, SpanReader reader) { if (state.SentFirstPacket) @@ -442,6 +354,12 @@ public static class IncomingAccountPackets var authId = reader.ReadInt32(); + if (!_authIDWindow.TryGetValue(authId, out var ap)) + { + state.LogInfo("Invalid client detected, disconnecting..."); + state.Disconnect("Unable to find auth id."); + } + if (state.AuthId != 0 && authId != state.AuthId || state.AuthId == 0 && authId != state.Seed) { state.LogInfo("Invalid client detected, disconnecting..."); @@ -449,28 +367,14 @@ public static class IncomingAccountPackets return; } - var username = reader.ReadLatin1Safe(30); - var password = reader.ReadLatin1Safe(30); - - var authResult = ConsumeAuthId(authId, username, state.Address, out var ap); - - if (authResult == AuthIdResult.Rejected) - { - state.LogInfo("Invalid client detected, disconnecting..."); - state.Disconnect("Unable to find auth id."); - return; - } - + _authIDWindow.Remove(authId); state.Version = ap.Version; state.Seeded = true; - // Expired carries a usable entry; only the password verify skip is withheld. - var e = new GameServer.GameLoginEventArgs( - state, - username, - password, - authResult == AuthIdResult.Vouched - ); + var username = reader.ReadAscii(30); + var password = reader.ReadAscii(30); + + var e = new GameServer.GameLoginEventArgs(state, username, password); GameServer.GameServerLoginEvent(e); @@ -480,6 +384,7 @@ public static class IncomingAccountPackets // Comment out these lines to turn off huffman compression state.CompressionEnabled = true; + state.PacketEncoder ??= NetworkCompression.Compress; state.SendSupportedFeature(); state.SendCharacterList(); @@ -492,14 +397,6 @@ public static class IncomingAccountPackets public static void PlayServer(NetState state, SpanReader reader) { - // A server is picked once per connection. Picking again hands back an id this connection may - // already have spent on a game login, which the client could never redeem. - if (state.AuthId != 0) - { - state.Disconnect("Duplicate play server packet sent."); - return; - } - int index = reader.ReadInt16(); var info = state.ServerInfo; var a = state.Account; @@ -512,7 +409,7 @@ public static class IncomingAccountPackets { var si = info[index]; - state.AuthId = state.GenerateAuthID(); + state.AuthId = GenerateAuthID(state); state.SentFirstPacket = false; state.SendPlayServerAck(si, state.AuthId); @@ -521,21 +418,13 @@ public static class IncomingAccountPackets public static void LoginServerSeed(NetState state, SpanReader reader) { - // Seeding happens once per connection. A second one restarts a handshake this connection - // already completed, which no real client does. - if (state.Seeded) - { - state.Disconnect("Duplicate login server seed packet sent."); - return; - } - state.Seed = reader.ReadInt32(); state.Seeded = true; if (state.Seed == 0) { state.LogInfo("Invalid client detected, disconnecting"); - state.Disconnect("Invalid client detected"); + state.Disconnect("Duplicate seed sent."); return; } @@ -557,29 +446,14 @@ public static class IncomingAccountPackets state.SentFirstPacket = true; - var username = reader.ReadLatin1Safe(30); - var password = reader.ReadLatin1Safe(30); + var username = reader.ReadAscii(30); + var password = reader.ReadAscii(30); var accountLoginEventArgs = new AccountLoginEventArgs(state, username, password); EventSink.InvokeAccountLogin(accountLoginEventArgs); - // The password check moved off the loop; whoever took it replies when the verdict lands. - if (accountLoginEventArgs.Deferred) - { - return; - } - - CompleteAccountLogin(state, accountLoginEventArgs.Accepted, accountLoginEventArgs.RejectReason); - } - - /// - /// Replies to an account login. Split out so a verdict produced off the loop reaches the client - /// through exactly the same path as one produced inline. - /// - internal static void CompleteAccountLogin(NetState state, bool accepted, ALRReason rejectReason) - { - if (accepted) + if (accountLoginEventArgs.Accepted) { var serverListEventArgs = new GatewayServer.ServerListEventArgs(state, state.Account); @@ -599,7 +473,7 @@ public static class IncomingAccountPackets else { state.Account = null; - AccountLogin_ReplyRej(state, rejectReason); + AccountLogin_ReplyRej(state, accountLoginEventArgs.RejectReason); } } diff --git a/Projects/UOContent/Network/Packets/IncomingEntityPackets.cs b/Projects/UOContent/Network/Packets/IncomingEntityPackets.cs index b19aaaa6f..2ad4bb7db 100644 --- a/Projects/UOContent/Network/Packets/IncomingEntityPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingEntityPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingEntityPackets.cs * * * @@ -72,7 +72,7 @@ public static class IncomingEntityPackets } else { - var s = (Serial)value; + Serial s = (Serial)value; if (s.IsMobile) { @@ -106,7 +106,7 @@ public static class IncomingEntityPackets { var from = state.Mobile; - var s = (Serial)reader.ReadUInt32(); + Serial s = (Serial)reader.ReadUInt32(); if (s.IsMobile) { @@ -169,7 +169,7 @@ public static class IncomingEntityPackets while (reader.Remaining > 0) { - var s = (Serial)reader.ReadUInt32(); + Serial s = (Serial)reader.ReadUInt32(); if (s.IsMobile) { diff --git a/Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs b/Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs index da1521942..c593a56a2 100644 --- a/Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingExtendedCommandPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingExtendedCommandPackets.cs * * * @@ -263,7 +263,7 @@ public static partial class IncomingExtendedCommandPackets return; } - var spellbook = reader.ReadInt16() == 1 ? World.FindItem((Serial)reader.ReadUInt32()) : null; + Item spellbook = reader.ReadInt16() == 1 ? World.FindItem((Serial)reader.ReadUInt32()) : null; var spellID = reader.ReadInt16() - 1; Spellbook.CastSpellRequest(from, spellID, spellbook); @@ -361,7 +361,7 @@ public static partial class IncomingExtendedCommandPackets var from = state.Mobile; - var s = (Serial)reader.ReadUInt32(); + Serial s = (Serial)reader.ReadUInt32(); if (s.IsMobile) { diff --git a/Projects/UOContent/Network/Packets/IncomingHousePackets.cs b/Projects/UOContent/Network/Packets/IncomingHousePackets.cs index ab2d68b75..b16d562cb 100644 --- a/Projects/UOContent/Network/Packets/IncomingHousePackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingHousePackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingHousePackets.cs * * * diff --git a/Projects/UOContent/Network/Packets/IncomingItemPackets.cs b/Projects/UOContent/Network/Packets/IncomingItemPackets.cs index 2daa6d5f7..f2ad7fd8f 100644 --- a/Projects/UOContent/Network/Packets/IncomingItemPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingItemPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingItemPackets.cs * * * @@ -14,8 +14,8 @@ *************************************************************************/ using System.Buffers; +using System.Collections.Generic; using System.IO; -using Server.Collections; using Server.Items; using Server.Mobiles; @@ -78,7 +78,7 @@ public static class IncomingItemPackets reader.ReadByte(); // Grid Location? } - var dest = (Serial)reader.ReadUInt32(); + Serial dest = (Serial)reader.ReadUInt32(); var loc = new Point3D(x, y, z); @@ -112,25 +112,24 @@ public static class IncomingItemPackets public static void EquipMacro(NetState state, SpanReader reader) { int count = reader.ReadByte(); - var serialList = PooledRefList.Create(count); + var serialList = new List(count); for (var i = 0; i < count; ++i) { serialList.Add((Serial)reader.ReadUInt32()); } - PlayerMobile.EquipMacro(state.Mobile, ref serialList); - serialList.Dispose(); + PlayerMobile.EquipMacro(state.Mobile, serialList); } public static void UnequipMacro(NetState state, SpanReader reader) { int count = reader.ReadByte(); - var layers = PooledRefList.Create(count); + var layers = new List(count); for (var i = 0; i < count; ++i) { layers.Add((Layer)reader.ReadUInt16()); } - PlayerMobile.UnequipMacro(state.Mobile, ref layers); + PlayerMobile.UnequipMacro(state.Mobile, layers); } } diff --git a/Projects/UOContent/Network/Packets/IncomingMessagePackets.cs b/Projects/UOContent/Network/Packets/IncomingMessagePackets.cs index fa4d33323..1ce975082 100644 --- a/Projects/UOContent/Network/Packets/IncomingMessagePackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingMessagePackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingMessagePackets.cs * * * @@ -40,7 +40,7 @@ public static class IncomingMessagePackets var type = (MessageType)reader.ReadByte(); int hue = reader.ReadInt16(); reader.ReadInt16(); // font - var text = reader.ReadLatin1Safe().Trim(); + var text = reader.ReadAsciiSafe().Trim(); if (text.Length is <= 0 or > 128) { diff --git a/Projects/UOContent/Network/Packets/IncomingMobilePackets.cs b/Projects/UOContent/Network/Packets/IncomingMobilePackets.cs index 9fc907f38..994a2590a 100644 --- a/Projects/UOContent/Network/Packets/IncomingMobilePackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingMobilePackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingMobilePackets.cs * * * @@ -36,7 +36,7 @@ public static class IncomingMobilePackets if (targ != null) { - RenameRequests.RenameRequest(from, targ, reader.ReadLatin1Safe()); + RenameRequests.RenameRequest(from, targ, reader.ReadAsciiSafe()); } } @@ -158,8 +158,6 @@ public static class IncomingMobilePackets trade.To.Plat = plat; trade.UpdateToCurrency(); } - - cont.ClearChecks(); } } } diff --git a/Projects/UOContent/Network/Packets/IncomingMovementPackets.cs b/Projects/UOContent/Network/Packets/IncomingMovementPackets.cs index 380be80d6..7f4884e07 100644 --- a/Projects/UOContent/Network/Packets/IncomingMovementPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingMovementPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingMovementPackets.cs * * * @@ -37,7 +37,7 @@ public static class IncomingMovementPackets } var steps = reader.ReadByte(); - for (var i = 0; i < steps; i++) + for (int i = 0; i < steps; i++) { var t1 = reader.ReadUInt64(); // start time? var t2 = reader.ReadUInt64(); // end time? @@ -91,9 +91,23 @@ public static class IncomingMovementPackets var dir = (Direction)reader.ReadByte(); int seq = reader.ReadByte(); - var key = reader.ReadUInt32(); // FastWalkStack key - not used (not on EA servers) + var key = reader.ReadUInt32(); - // Delegate to MovementThrottle which has full context for timing validation - MovementThrottle.ValidateAndQueueMovement(state, from, dir, seq); + if (state.Sequence == 0 && seq != 0 || !from.Move(dir)) + { + state.SendMovementRej(seq, from); + state.Sequence = 0; + } + else + { + ++seq; + + if (seq == 256) + { + seq = 1; + } + + state.Sequence = seq; + } } } diff --git a/Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs b/Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs index a6297837d..fe803caa7 100644 --- a/Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingPlayerPackets.cs * * * @@ -223,7 +223,7 @@ public static class IncomingPlayerPackets var serial = reader.ReadUInt32(); var prompt = reader.ReadInt32(); var type = reader.ReadInt32(); - var text = reader.ReadLatin1Safe(); + var text = reader.ReadAsciiSafe(); if (text.Length > 128) { @@ -287,32 +287,30 @@ public static class IncomingPlayerPackets public static void MenuResponse(NetState state, SpanReader reader) { var serial = reader.ReadUInt32(); - int menuID = reader.ReadInt16(); + int menuID = reader.ReadInt16(); // unused in our implementation int index = reader.ReadInt16(); int itemID = reader.ReadInt16(); int hue = reader.ReadInt16(); index -= 1; // convert from 1-based to 0-based - for (var i = 0; i < state.Menus.Count; i++) + foreach (var menu in state.Menus) { - var menu = state.Menus[i]; - if ((uint)menu.Serial != serial) + if (menu.Serial == serial) { - continue; - } + state.RemoveMenu(menu); - state.RemoveMenu(menu); + if (index >= 0 && index < menu.EntryLength) + { + menu.OnResponse(state, index); + } + else + { + menu.OnCancel(state); + } - if (index >= 0 && index < menu.EntryLength) - { - menu.OnResponse(state, index); + break; } - else - { - menu.OnCancel(state); - } - break; } } @@ -362,7 +360,7 @@ public static class IncomingPlayerPackets from.SendEverything(); - state.ResetMovementState(); + state.Sequence = 0; } public static void PingReq(NetState state, SpanReader reader) diff --git a/Projects/UOContent/Network/Packets/IncomingTargetingPackets.cs b/Projects/UOContent/Network/Packets/IncomingTargetingPackets.cs index 82a6f8ee2..9d68bccf8 100644 --- a/Projects/UOContent/Network/Packets/IncomingTargetingPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingTargetingPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingTargetingPackets.cs * * * @@ -86,7 +86,7 @@ public static class IncomingTargetingPackets } } - var hue = 0; + int hue = 0; var valid = false; var eable = t.DisallowMultis diff --git a/Projects/UOContent/Network/Packets/IncomingVendorPackets.cs b/Projects/UOContent/Network/Packets/IncomingVendorPackets.cs index 9a0c19ec3..062bd8a7c 100644 --- a/Projects/UOContent/Network/Packets/IncomingVendorPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingVendorPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IncomingVendorPackets.cs * * * diff --git a/Projects/UOContent/Network/ProtocolExtensions.cs b/Projects/UOContent/Network/ProtocolExtensions.cs index 08c1548a7..abbec8b46 100644 --- a/Projects/UOContent/Network/ProtocolExtensions.cs +++ b/Projects/UOContent/Network/ProtocolExtensions.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: ProtocolExtension.cs * * * @@ -39,7 +39,7 @@ namespace Server.Network { int cmd = reader.ReadByte(); - var ph = packetHandlers[cmd]; + PacketHandler ph = packetHandlers[cmd]; if (ph == null) { diff --git a/Projects/UOContent/Network/UOGateway.cs b/Projects/UOContent/Network/UOGateway.cs index 14cded105..8d0c5e7e7 100644 --- a/Projects/UOContent/Network/UOGateway.cs +++ b/Projects/UOContent/Network/UOGateway.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: UOGateway.cs * * * diff --git a/Projects/UOContent/Regions/BaseRegion.cs b/Projects/UOContent/Regions/BaseRegion.cs index 776ec269d..dbe924594 100644 --- a/Projects/UOContent/Regions/BaseRegion.cs +++ b/Projects/UOContent/Regions/BaseRegion.cs @@ -12,7 +12,7 @@ public class BaseRegion : Region private static readonly List m_RectBuffer1 = new(); private static readonly List m_RectBuffer2 = new(); - public BaseRegion(string name, Map map, int priority, params ReadOnlySpan area) : base(name, map, priority, area) + public BaseRegion(string name, Map map, int priority, params Rectangle2D[] area) : base(name, map, priority, area) { } @@ -26,7 +26,7 @@ public class BaseRegion : Region { } - public BaseRegion(string name, Map map, Region parent, params ReadOnlySpan area) : base(name, map, parent, area) + public BaseRegion(string name, Map map, Region parent, params Rectangle2D[] area) : base(name, map, parent, area) { } @@ -113,7 +113,7 @@ public class BaseRegion : Region m_RectBuffer2.RemoveAt(k); var sz = rect.Start.Z; - var ez = rect.End.Z; + var ez = rect.End.X; if (l1 < l2) { diff --git a/Projects/UOContent/Regions/GuardedRegion.cs b/Projects/UOContent/Regions/GuardedRegion.cs index 3d9e883f2..28050a515 100644 --- a/Projects/UOContent/Regions/GuardedRegion.cs +++ b/Projects/UOContent/Regions/GuardedRegion.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using Server.Collections; using Server.Mobiles; namespace Server.Regions; @@ -135,7 +134,7 @@ public class GuardedRegion : BaseRegion } public virtual bool CheckVendorAccess(BaseVendor vendor, Mobile from) => - from.AccessLevel >= AccessLevel.GameMaster || IsDisabled() || !from.Murderer; + from.AccessLevel >= AccessLevel.GameMaster || IsDisabled() || from.Kills < 5; public override bool OnBeginSpellCast(Mobile m, ISpell s) { @@ -152,15 +151,32 @@ public class GuardedRegion : BaseRegion public override void MakeGuard(Mobile focus) { - m_GuardParams[0] = focus; - - try + BaseGuard useGuard = null; + foreach (var m in focus.GetMobilesInRange(8)) { - GuardType.CreateInstance(m_GuardParams); + if (m.Focus == null) + { + useGuard = m; + break; + } } - catch + + if (useGuard == null) { - // ignored + m_GuardParams[0] = focus; + + try + { + GuardType.CreateInstance(m_GuardParams); + } + catch + { + // ignored + } + } + else + { + useGuard.Focus = focus; } } @@ -171,7 +187,7 @@ public class GuardedRegion : BaseRegion return; } - if (!AllowReds && m.Murderer) + if (!AllowReds && m.Kills >= 5) { CheckGuardCandidate(m); } @@ -235,7 +251,7 @@ public class GuardedRegion : BaseRegion public void CheckGuardCandidate(Mobile m) { - if (!IsGuardCandidate(m)) + if (IsDisabled() || !IsGuardCandidate(m)) { return; } @@ -260,18 +276,16 @@ public class GuardedRegion : BaseRegion foreach (var v in m.GetMobilesInRange(8)) { - if (v.Player || v == m || IsGuardCandidate(v) || - !((v as BaseCreature)?.IsHumanInTown() ?? v.Body.IsHuman && v.Region.IsPartOf(this))) + if (!v.Player && v != m && !IsGuardCandidate(v) && + ((v as BaseCreature)?.IsHumanInTown() ?? v.Body.IsHuman && v.Region.IsPartOf(this))) { - continue; - } + var dist = m.GetDistanceToSqrt(v); - var dist = m.GetDistanceToSqrt(v); - - if (fakeCall == null || dist < prio) - { - fakeCall = v; - prio = dist; + if (fakeCall == null || dist < prio) + { + fakeCall = v; + prio = dist; + } } } @@ -279,14 +293,10 @@ public class GuardedRegion : BaseRegion { fakeCall.Say(Utility.Random(1013037, 16)); - BaseGuard.Spawn(fakeCall, m); + MakeGuard(m); timer.Stop(); m_GuardCandidates.Remove(m); - - if (!IsAlwaysGuardCandidate(m)) - { - m.SendLocalizedMessage(502276); // Guards can no longer be called on you. - } + m.SendLocalizedMessage(502276); // Guards can no longer be called on you. } } else @@ -298,51 +308,32 @@ public class GuardedRegion : BaseRegion public void CallGuards(Point3D p) { - using var queue = PooledRefQueue.Create(); + if (IsDisabled()) + { + return; + } foreach (var m in Map.GetMobilesInRange(p, 14)) { - if (!IsGuardCandidate(m)) - { - continue; - } - - // Dictionary members can be targeted regardless of region (guards reach outside). - // Non-dictionary permanent candidates (reds) must be in the region. - if (m_GuardCandidates.ContainsKey(m) || m.Region.IsPartOf(this) && IsAlwaysGuardCandidate(m)) + if (IsGuardCandidate(m) && + (!AllowReds && m.Kills >= 5 && m.Region.IsPartOf(this) || m_GuardCandidates.ContainsKey(m))) { if (m_GuardCandidates.Remove(m, out var timer)) { timer.Stop(); } - queue.Enqueue(m); - } - } - - while (queue.Count > 0) - { - var m = queue.Dequeue(); - BaseGuard.Spawn(this, m); - - if (!IsAlwaysGuardCandidate(m)) - { + MakeGuard(m); m.SendLocalizedMessage(502276); // Guards can no longer be called on you. + break; } } } - private bool IsAlwaysGuardCandidate(Mobile m) => !AllowReds && m.Murderer; - - public bool IsGuardCandidate(Mobile m) - { - if (m is BaseGuard || !m.Alive || m.AccessLevel > AccessLevel.Player || m.Blessed) - { - return false; - } - - return (m as BaseCreature)?.IsInvulnerable != true && (IsAlwaysGuardCandidate(m) || m.Criminal); - } + public bool IsGuardCandidate(Mobile m) => + m is not BaseGuard && m.Alive && m.AccessLevel <= AccessLevel.Player && !m.Blessed && + (m is not BaseCreature creature || !creature.IsInvulnerable) && !IsDisabled() && + (!AllowReds && m.Kills >= 5 || m.Criminal); private class GuardTimer : Timer { diff --git a/Projects/UOContent/Systems/JailSystem/JailRegion.cs b/Projects/UOContent/Regions/JailRegion.cs similarity index 100% rename from Projects/UOContent/Systems/JailSystem/JailRegion.cs rename to Projects/UOContent/Regions/JailRegion.cs diff --git a/Projects/UOContent/Skills/AnimalLore.cs b/Projects/UOContent/Skills/AnimalLore.cs index a02bec8a4..e53220844 100644 --- a/Projects/UOContent/Skills/AnimalLore.cs +++ b/Projects/UOContent/Skills/AnimalLore.cs @@ -62,76 +62,60 @@ namespace Server.SkillHandlers } else { - AnimalLoreGump.DisplayTo(from, c); + from.SendGump(new AnimalLoreGump(c)); } } } } - public class AnimalLoreGump : DynamicGump + public class AnimalLoreGump : Gump { private const int LabelColor = 0x24E5; - private readonly BaseCreature _creature; - public override bool Singleton => true; - private AnimalLoreGump(BaseCreature c) : base(250, 50) => _creature = c; - - public static void DisplayTo(Mobile from, BaseCreature c) + public AnimalLoreGump(BaseCreature c) : base(250, 50) { - if (from?.NetState == null || c == null || c.Deleted) - { - return; - } + AddPage(0); - from.SendGump(new AnimalLoreGump(c)); - } + AddImage(100, 100, 2080); + AddImage(118, 137, 2081); + AddImage(118, 207, 2081); + AddImage(118, 277, 2081); + AddImage(118, 347, 2083); - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var c = _creature; + AddHtml(147, 108, 210, 18, $"
{c.Name}
"); - builder.AddPage(0); + AddButton(240, 77, 2093, 2093, 2); - builder.AddImage(100, 100, 2080); - builder.AddImage(118, 137, 2081); - builder.AddImage(118, 207, 2081); - builder.AddImage(118, 277, 2081); - builder.AddImage(118, 347, 2083); - - builder.AddHtml(147, 108, 210, 18, $"
{c.Name}
"); - - builder.AddButton(240, 77, 2093, 2093, 2); - - builder.AddImage(140, 138, 2091); - builder.AddImage(140, 335, 2091); + AddImage(140, 138, 2091); + AddImage(140, 335, 2091); var pages = Core.AOS ? 5 : 3; var page = 0; - builder.AddPage(++page); + AddPage(++page); - builder.AddImage(128, 152, 2086); - builder.AddHtmlLocalized(147, 150, 160, 18, 1049593, 200); // Attributes + AddImage(128, 152, 2086); + AddHtmlLocalized(147, 150, 160, 18, 1049593, 200); // Attributes - builder.AddHtmlLocalized(153, 168, 160, 18, 1049578, LabelColor); // Hits - builder.AddHtml(280, 168, 75, 18, FormatAttributes(c.Hits, c.HitsMax)); + AddHtmlLocalized(153, 168, 160, 18, 1049578, LabelColor); // Hits + AddHtml(280, 168, 75, 18, FormatAttributes(c.Hits, c.HitsMax)); - builder.AddHtmlLocalized(153, 186, 160, 18, 1049579, LabelColor); // Stamina - builder.AddHtml(280, 186, 75, 18, FormatAttributes(c.Stam, c.StamMax)); + AddHtmlLocalized(153, 186, 160, 18, 1049579, LabelColor); // Stamina + AddHtml(280, 186, 75, 18, FormatAttributes(c.Stam, c.StamMax)); - builder.AddHtmlLocalized(153, 204, 160, 18, 1049580, LabelColor); // Mana - builder.AddHtml(280, 204, 75, 18, FormatAttributes(c.Mana, c.ManaMax)); + AddHtmlLocalized(153, 204, 160, 18, 1049580, LabelColor); // Mana + AddHtml(280, 204, 75, 18, FormatAttributes(c.Mana, c.ManaMax)); - builder.AddHtmlLocalized(153, 222, 160, 18, 1028335, LabelColor); // Strength - builder.AddHtml(320, 222, 35, 18, FormatStat(c.Str)); + AddHtmlLocalized(153, 222, 160, 18, 1028335, LabelColor); // Strength + AddHtml(320, 222, 35, 18, FormatStat(c.Str)); - builder.AddHtmlLocalized(153, 240, 160, 18, 3000113, LabelColor); // Dexterity - builder.AddHtml(320, 240, 35, 18, FormatStat(c.Dex)); + AddHtmlLocalized(153, 240, 160, 18, 3000113, LabelColor); // Dexterity + AddHtml(320, 240, 35, 18, FormatStat(c.Dex)); - builder.AddHtmlLocalized(153, 258, 160, 18, 3000112, LabelColor); // Intelligence - builder.AddHtml(320, 258, 35, 18, FormatStat(c.Int)); + AddHtmlLocalized(153, 258, 160, 18, 3000112, LabelColor); // Intelligence + AddHtml(320, 258, 35, 18, FormatStat(c.Int)); if (Core.AOS) { @@ -145,17 +129,17 @@ namespace Server.SkillHandlers bd = 0; } - builder.AddHtmlLocalized(153, 276, 160, 18, 1070793, LabelColor); // Barding Difficulty - builder.AddHtml(320, y, 35, 18, FormatDouble(bd)); + AddHtmlLocalized(153, 276, 160, 18, 1070793, LabelColor); // Barding Difficulty + AddHtml(320, y, 35, 18, FormatDouble(bd)); y += 18; } - builder.AddImage(128, y + 2, 2086); - builder.AddHtmlLocalized(147, y, 160, 18, 1049594, 200); // Loyalty Rating + AddImage(128, y + 2, 2086); + AddHtmlLocalized(147, y, 160, 18, 1049594, 200); // Loyalty Rating y += 18; - builder.AddHtmlLocalized( + AddHtmlLocalized( 153, y, 160, @@ -166,122 +150,122 @@ namespace Server.SkillHandlers } else { - builder.AddImage(128, 278, 2086); - builder.AddHtmlLocalized(147, 276, 160, 18, 3001016, 200); // Miscellaneous + AddImage(128, 278, 2086); + AddHtmlLocalized(147, 276, 160, 18, 3001016, 200); // Miscellaneous - builder.AddHtmlLocalized(153, 294, 160, 18, 1049581, LabelColor); // Armor Rating - builder.AddHtml(320, 294, 35, 18, FormatStat(c.VirtualArmor)); + AddHtmlLocalized(153, 294, 160, 18, 1049581, LabelColor); // Armor Rating + AddHtml(320, 294, 35, 18, FormatStat(c.VirtualArmor)); } - builder.AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); - builder.AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, pages); + AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); + AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, pages); if (Core.AOS) { - builder.AddPage(++page); + AddPage(++page); - builder.AddImage(128, 152, 2086); - builder.AddHtmlLocalized(147, 150, 160, 18, 1061645, 200); // Resistances + AddImage(128, 152, 2086); + AddHtmlLocalized(147, 150, 160, 18, 1061645, 200); // Resistances - builder.AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical - builder.AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalResistance)); + AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical + AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalResistance)); - builder.AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire - builder.AddHtml(320, 186, 35, 18, FormatElement(c.FireResistance, "#FF0000")); + AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire + AddHtml(320, 186, 35, 18, FormatElement(c.FireResistance, "#FF0000")); - builder.AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold - builder.AddHtml(320, 204, 35, 18, FormatElement(c.ColdResistance, "#000080")); + AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold + AddHtml(320, 204, 35, 18, FormatElement(c.ColdResistance, "#000080")); - builder.AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison - builder.AddHtml(320, 222, 35, 18, FormatElement(c.PoisonResistance, "#008000")); + AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison + AddHtml(320, 222, 35, 18, FormatElement(c.PoisonResistance, "#008000")); - builder.AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy - builder.AddHtml(320, 240, 35, 18, FormatElement(c.EnergyResistance, "#BF80FF")); + AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy + AddHtml(320, 240, 35, 18, FormatElement(c.EnergyResistance, "#BF80FF")); - builder.AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); - builder.AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); + AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); + AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); - builder.AddPage(++page); + AddPage(++page); - builder.AddImage(128, 152, 2086); - builder.AddHtmlLocalized(147, 150, 160, 18, 1017319, 200); // Damage + AddImage(128, 152, 2086); + AddHtmlLocalized(147, 150, 160, 18, 1017319, 200); // Damage - builder.AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical - builder.AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalDamage)); + AddHtmlLocalized(153, 168, 160, 18, 1061646, LabelColor); // Physical + AddHtml(320, 168, 35, 18, FormatElement(c.PhysicalDamage)); - builder.AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire - builder.AddHtml(320, 186, 35, 18, FormatElement(c.FireDamage, "#FF0000")); + AddHtmlLocalized(153, 186, 160, 18, 1061647, LabelColor); // Fire + AddHtml(320, 186, 35, 18, FormatElement(c.FireDamage, "#FF0000")); - builder.AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold - builder.AddHtml(320, 204, 35, 18, FormatElement(c.ColdDamage, "#000080")); + AddHtmlLocalized(153, 204, 160, 18, 1061648, LabelColor); // Cold + AddHtml(320, 204, 35, 18, FormatElement(c.ColdDamage, "#000080")); - builder.AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison - builder.AddHtml(320, 222, 35, 18, FormatElement(c.PoisonDamage, "#008000")); + AddHtmlLocalized(153, 222, 160, 18, 1061649, LabelColor); // Poison + AddHtml(320, 222, 35, 18, FormatElement(c.PoisonDamage, "#008000")); - builder.AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy - builder.AddHtml(320, 240, 35, 18, FormatElement(c.EnergyDamage, "#BF80FF")); + AddHtmlLocalized(153, 240, 160, 18, 1061650, LabelColor); // Energy + AddHtml(320, 240, 35, 18, FormatElement(c.EnergyDamage, "#BF80FF")); if (Core.ML) { - builder.AddHtmlLocalized(153, 258, 160, 18, 1076750, LabelColor); // Base Damage - builder.AddHtml(300, 258, 55, 18, FormatDamage(c.DamageMin, c.DamageMax)); + AddHtmlLocalized(153, 258, 160, 18, 1076750, LabelColor); // Base Damage + AddHtml(300, 258, 55, 18, FormatDamage(c.DamageMin, c.DamageMax)); } - builder.AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); - builder.AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); + AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); + AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); } - builder.AddPage(++page); + AddPage(++page); - builder.AddImage(128, 152, 2086); - builder.AddHtmlLocalized(147, 150, 160, 18, 3001030, 200); // Combat Ratings + AddImage(128, 152, 2086); + AddHtmlLocalized(147, 150, 160, 18, 3001030, 200); // Combat Ratings - builder.AddHtmlLocalized(153, 168, 160, 18, 1044103, LabelColor); // Wrestling - builder.AddHtml(320, 168, 35, 18, FormatSkill(c, SkillName.Wrestling)); + AddHtmlLocalized(153, 168, 160, 18, 1044103, LabelColor); // Wrestling + AddHtml(320, 168, 35, 18, FormatSkill(c, SkillName.Wrestling)); - builder.AddHtmlLocalized(153, 186, 160, 18, 1044087, LabelColor); // Tactics - builder.AddHtml(320, 186, 35, 18, FormatSkill(c, SkillName.Tactics)); + AddHtmlLocalized(153, 186, 160, 18, 1044087, LabelColor); // Tactics + AddHtml(320, 186, 35, 18, FormatSkill(c, SkillName.Tactics)); - builder.AddHtmlLocalized(153, 204, 160, 18, 1044086, LabelColor); // Magic Resistance - builder.AddHtml(320, 204, 35, 18, FormatSkill(c, SkillName.MagicResist)); + AddHtmlLocalized(153, 204, 160, 18, 1044086, LabelColor); // Magic Resistance + AddHtml(320, 204, 35, 18, FormatSkill(c, SkillName.MagicResist)); - builder.AddHtmlLocalized(153, 222, 160, 18, 1044061, LabelColor); // Anatomy - builder.AddHtml(320, 222, 35, 18, FormatSkill(c, SkillName.Anatomy)); + AddHtmlLocalized(153, 222, 160, 18, 1044061, LabelColor); // Anatomy + AddHtml(320, 222, 35, 18, FormatSkill(c, SkillName.Anatomy)); if (c is CuSidhe) { - builder.AddHtmlLocalized(153, 240, 160, 18, 1044077, LabelColor); // Healing - builder.AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Healing)); + AddHtmlLocalized(153, 240, 160, 18, 1044077, LabelColor); // Healing + AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Healing)); } else { - builder.AddHtmlLocalized(153, 240, 160, 18, 1044090, LabelColor); // Poisoning - builder.AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Poisoning)); + AddHtmlLocalized(153, 240, 160, 18, 1044090, LabelColor); // Poisoning + AddHtml(320, 240, 35, 18, FormatSkill(c, SkillName.Poisoning)); } // TODO: Add remaining combat skills - builder.AddImage(128, 260, 2086); - builder.AddHtmlLocalized(147, 258, 160, 18, 3001032, 200); // Lore & Knowledge + AddImage(128, 260, 2086); + AddHtmlLocalized(147, 258, 160, 18, 3001032, 200); // Lore & Knowledge - builder.AddHtmlLocalized(153, 276, 160, 18, 1044085, LabelColor); // Magery - builder.AddHtml(320, 276, 35, 18, FormatSkill(c, SkillName.Magery)); + AddHtmlLocalized(153, 276, 160, 18, 1044085, LabelColor); // Magery + AddHtml(320, 276, 35, 18, FormatSkill(c, SkillName.Magery)); - builder.AddHtmlLocalized(153, 294, 160, 18, 1044076, LabelColor); // Evaluating Intelligence - builder.AddHtml(320, 294, 35, 18, FormatSkill(c, SkillName.EvalInt)); + AddHtmlLocalized(153, 294, 160, 18, 1044076, LabelColor); // Evaluating Intelligence + AddHtml(320, 294, 35, 18, FormatSkill(c, SkillName.EvalInt)); - builder.AddHtmlLocalized(153, 312, 160, 18, 1044106, LabelColor); // Meditation - builder.AddHtml(320, 312, 35, 18, FormatSkill(c, SkillName.Meditation)); + AddHtmlLocalized(153, 312, 160, 18, 1044106, LabelColor); // Meditation + AddHtml(320, 312, 35, 18, FormatSkill(c, SkillName.Meditation)); // TODO: Add remaining skills - builder.AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); - builder.AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); + AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1); + AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); - builder.AddPage(++page); + AddPage(++page); - builder.AddImage(128, 152, 2086); - builder.AddHtmlLocalized(147, 150, 160, 18, 1049563, 200); // Preferred Foods + AddImage(128, 152, 2086); + AddHtmlLocalized(147, 150, 160, 18, 1049563, 200); // Preferred Foods var foodPref = 3000340; @@ -312,10 +296,10 @@ namespace Server.SkillHandlers // TODO: Add 1115752 "Blackrock Stew" as a food type - builder.AddHtmlLocalized(153, 168, 160, 18, foodPref, LabelColor); + AddHtmlLocalized(153, 168, 160, 18, foodPref, LabelColor); - builder.AddImage(128, 188, 2086); - builder.AddHtmlLocalized(147, 186, 160, 18, 1049569, 200); // Pack Instincts + AddImage(128, 188, 2086); + AddHtmlLocalized(147, 186, 160, 18, 1049569, 200); // Pack Instincts var packInstinct = 3000340; @@ -352,7 +336,7 @@ namespace Server.SkillHandlers packInstinct = 1049577; // Bull } - builder.AddHtmlLocalized(153, 204, 160, 18, packInstinct, LabelColor); + AddHtmlLocalized(153, 204, 160, 18, packInstinct, LabelColor); // TODO: Add Pet Slots @@ -360,10 +344,10 @@ namespace Server.SkillHandlers if (!Core.AOS) { - builder.AddImage(128, 224, 2086); - builder.AddHtmlLocalized(147, 222, 160, 18, 1049594, 200); // Loyalty Rating + AddImage(128, 224, 2086); + AddHtmlLocalized(147, 222, 160, 18, 1049594, 200); // Loyalty Rating - builder.AddHtmlLocalized( + AddHtmlLocalized( 153, 240, 160, @@ -373,8 +357,8 @@ namespace Server.SkillHandlers ); } - builder.AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, 1); - builder.AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); + AddButton(340, 358, 5601, 5605, 0, GumpButtonType.Page, 1); + AddButton(317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1); } private static string FormatSkill(BaseCreature c, SkillName name) diff --git a/Projects/UOContent/Skills/AnimalTaming.cs b/Projects/UOContent/Skills/AnimalTaming.cs index b34017fb7..7e661f692 100644 --- a/Projects/UOContent/Skills/AnimalTaming.cs +++ b/Projects/UOContent/Skills/AnimalTaming.cs @@ -443,8 +443,6 @@ namespace Server.SkillHandlers m_Creature.SetControlMaster(m_Tamer); m_Creature.IsBonded = false; - m_Creature.ControlTarget = m_Tamer; - m_Creature.ControlOrder = OrderType.Follow; } else { diff --git a/Projects/UOContent/Skills/AntiMacroSystem.cs b/Projects/UOContent/Skills/AntiMacroSystem.cs index e37431ddd..ffc4e0bc0 100644 --- a/Projects/UOContent/Skills/AntiMacroSystem.cs +++ b/Projects/UOContent/Skills/AntiMacroSystem.cs @@ -120,17 +120,23 @@ public static class AntiMacroSystem var now = Core.Now; + using var toRemove = PooledRefQueue.Create(); foreach (var (m, antiMacro) in _antiMacroTable) { if (antiMacro._lastExpiration <= now) { - _antiMacroTable.Remove(m); + toRemove.Enqueue(m); } else { antiMacro.CleanExpired(); } } + + while (toRemove.Count > 0) + { + _antiMacroTable.Remove(toRemove.Dequeue()); + } } [OnEvent(nameof(PlayerMobile.PlayerLoginEvent))] @@ -197,7 +203,7 @@ public static class AntiMacroSystem _antiMacroTable ??= new Dictionary(); // Hot path so use optimized code - ref var antiMacro = ref CollectionsMarshal.GetValueRefOrAddDefault(_antiMacroTable, pm, out var exists); + ref PlayerAntiMacro antiMacro = ref CollectionsMarshal.GetValueRefOrAddDefault(_antiMacroTable, pm, out var exists); if (!exists) { antiMacro = new PlayerAntiMacro(); @@ -235,8 +241,8 @@ public static class AntiMacroSystem var now = Core.Now; // Potential hot path, so use optimized code - ref var _countTimeStamp = - ref CollectionsMarshal.GetValueRefOrAddDefault(_antiMacroTracking, (skill, obj), out var exists); + ref CountAndTimeStamp _countTimeStamp = + ref CollectionsMarshal.GetValueRefOrAddDefault(_antiMacroTracking, (skill, obj), out bool exists); _countTimeStamp._count++; @@ -253,13 +259,20 @@ public static class AntiMacroSystem { var now = Core.Now; + using var toRemove = PooledRefQueue<(Skill, object)>.Create(); + foreach (var (key, countAndTimeStamp) in _antiMacroTracking) { if (countAndTimeStamp._count <= 0 || countAndTimeStamp._expiration <= now) { - _antiMacroTracking.Remove(key); + toRemove.Enqueue(key); } } + + while (toRemove.Count > 0) + { + _antiMacroTracking.Remove(toRemove.Dequeue()); + } } } diff --git a/Projects/UOContent/Skills/Begging.cs b/Projects/UOContent/Skills/Begging.cs index 83180baca..0267aa682 100644 --- a/Projects/UOContent/Skills/Begging.cs +++ b/Projects/UOContent/Skills/Begging.cs @@ -21,97 +21,120 @@ namespace Server.SkillHandlers m.SendLocalizedMessage(500397); // To whom do you wish to grovel? - return TimeSpan.FromSeconds(30.0); + return TimeSpan.FromHours(6.0); } private class InternalTarget : Target { + private bool m_SetSkillTime = true; + public InternalTarget() : base(12, false, TargetFlags.None) { } - protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) + protected override void OnTargetFinish(Mobile from) { - from.NextSkillTime = Core.TickCount; + if (m_SetSkillTime) + { + from.NextSkillTime = Core.TickCount; + } } protected override void OnTarget(Mobile from, object targeted) { - if (targeted is not Mobile targ) + from.RevealingAction(); + + var number = -1; + + if (targeted is Mobile targ) { - from.SendLocalizedMessage(500399); // There is little chance of getting money from that! - } - else if (targ.Player) // We can't beg from players - { - from.SendLocalizedMessage(500398); // Perhaps just asking would work better. - } - else if (!targ.Body.IsHuman) // Make sure the NPC is human - { - from.SendLocalizedMessage(500399); // There is little chance of getting money from that! - } - else if (!from.InRange(targ, 2)) - { - if (!targ.Female) + if (targ.Player) // We can't beg from players { - from.SendLocalizedMessage(500401); // You are too far away to beg from him. + number = 500398; // Perhaps just asking would work better. + } + else if (!targ.Body.IsHuman) // Make sure the NPC is human + { + number = 500399; // There is little chance of getting money from that! + } + else if (!from.InRange(targ, 2)) + { + if (!targ.Female) + { + number = 500401; // You are too far away to beg from him. + } + else + { + number = 500402; // You are too far away to beg from her. + } + } + // If we're on a mount, who would give us money? TODO: guessed it's removed since ML + else if (!Core.ML && from.Mounted) + { + number = 500404; // They seem unwilling to give you any money. } else { - from.SendLocalizedMessage(500402); // You are too far away to beg from her. + // Face each other + from.Direction = from.GetDirectionTo(targ); + targ.Direction = targ.GetDirectionTo(from); + + from.Animate(32, 5, 1, true, false, 0); // Bow + + new InternalTimer(from, targ).Start(); + + m_SetSkillTime = false; } } - // If we're on a mount, who would give us money? TODO: guessed it's removed since ML - else if (!Core.ML && from.Mounted) + else // Not a Mobile { - from.SendLocalizedMessage(500404); // They seem unwilling to give you any money. - } - else - { - from.RevealingAction(); - - // Face each other - from.Direction = from.GetDirectionTo(targ); - targ.Direction = targ.GetDirectionTo(from); - - from.Animate(32, 5, 1, true, false, 0); // Bow - - new InternalTimer(from, targ).Start(); - return; + number = 500399; // There is little chance of getting money from that! } - from.NextSkillTime = Core.TickCount; + if (number != -1) + { + from.SendLocalizedMessage(number); + } } private class InternalTimer : Timer { - private readonly Mobile _from; - private readonly Mobile _target; + private readonly Mobile m_From; + private readonly Mobile m_Target; public InternalTimer(Mobile from, Mobile target) : base(TimeSpan.FromSeconds(2.0)) { - _from = from; - _target = target; + m_From = from; + m_Target = target; } protected override void OnTick() { - var theirPack = _target.Backpack; + var theirPack = m_Target.Backpack; - var badKarmaChance = 0.5 - (double)_from.Karma / 8570; + var badKarmaChance = 0.5 - (double)m_From.Karma / 8570; if (theirPack == null) { - _from.SendLocalizedMessage(500404); // They seem unwilling to give you any money. + m_From.SendLocalizedMessage(500404); // They seem unwilling to give you any money. } - else if (_from.Karma < 0 && badKarmaChance > Utility.RandomDouble()) + else if (m_From.Karma < 0 && badKarmaChance > Utility.RandomDouble()) { // Thou dost not look trustworthy... no gold for thee today! - _target.PublicOverheadMessage(MessageType.Regular, _target.SpeechHue, 500406); + m_Target.PublicOverheadMessage(MessageType.Regular, m_Target.SpeechHue, 500406); } - else if (_from.CheckTargetSkill(SkillName.Begging, _target, 0.0, 100.0)) + else if (m_From.CheckTargetSkill(SkillName.Begging, m_Target, 0.0, 100.0)) { var toConsume = theirPack.GetAmount(typeof(Gold)) / 10; - var max = Math.Clamp(10 + _from.Fame / 2500, 10, 14); + var max = 10 + m_From.Fame / 2500; + + if (max > 14) + { + max = 14; + } + else if (max < 10) + { + max = 10; + } if (toConsume > max) { @@ -125,49 +148,43 @@ namespace Server.SkillHandlers if (consumed > 0) { // I feel sorry for thee... - _target.PublicOverheadMessage(MessageType.Regular, _target.SpeechHue, 500405); + m_Target.PublicOverheadMessage(MessageType.Regular, m_Target.SpeechHue, 500405); var gold = new Gold(consumed); - _from.AddToBackpack(gold); - _from.PlaySound(gold.GetDropSound()); + m_From.AddToBackpack(gold); + m_From.PlaySound(gold.GetDropSound()); - if (_from.Karma > -3000) + if (m_From.Karma > -3000) { - var toLose = _from.Karma + 3000; + var toLose = m_From.Karma + 3000; if (toLose > 40) { toLose = 40; } - Titles.AwardKarma(_from, -toLose, true); + Titles.AwardKarma(m_From, -toLose, true); } } else { // I have not enough money to give thee any! - _target.PublicOverheadMessage(MessageType.Regular, _target.SpeechHue, 500407); + m_Target.PublicOverheadMessage(MessageType.Regular, m_Target.SpeechHue, 500407); } } else { // I have not enough money to give thee any! - _target.PublicOverheadMessage(MessageType.Regular, _target.SpeechHue, 500407); + m_Target.PublicOverheadMessage(MessageType.Regular, m_Target.SpeechHue, 500407); } } else { - _target.SendLocalizedMessage(500404); // They seem unwilling to give you any money. + m_Target.SendLocalizedMessage(500404); // They seem unwilling to give you any money. } - const int TargeterCooldown = 30000; // 30s - const int SkillCooldown = 10000; // 10s - - // Calculate how much time has passed since the targeter was opened - var ticksSinceTargeter = (int)(Core.TickCount - (_from.NextSkillTime - TargeterCooldown)); - var remainingCooldown = Math.Max(0, SkillCooldown - ticksSinceTargeter); - _from.NextSkillTime = Core.TickCount + remainingCooldown; + m_From.NextSkillTime = Core.TickCount + 10000; } } } diff --git a/Projects/UOContent/Skills/Cartography.cs b/Projects/UOContent/Skills/Cartography.cs deleted file mode 100644 index e6911590c..000000000 --- a/Projects/UOContent/Skills/Cartography.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using Server.Engines.Craft; -using Server.Engines.Craft.T2A; - -namespace Server.SkillHandlers; - -public static class Cartography -{ - public static void Initialize() - { - SkillInfo.Table[(int)SkillName.Cartography].Callback = OnUse; - } - - public static TimeSpan OnUse(Mobile m) - { - if (!T2ACraftSystem.Enabled) - { - m.SendLocalizedMessage(1046444); // Use a mapmaker's pen to draw maps. - return TimeSpan.Zero; - } - - T2ACraftSystem.ShowMenu(m, DefCartography.CraftSystem, null); - return TimeSpan.FromSeconds(1.0); - } -} diff --git a/Projects/UOContent/Skills/DetectHidden.cs b/Projects/UOContent/Skills/DetectHidden.cs index 487eda693..a0fcecafd 100644 --- a/Projects/UOContent/Skills/DetectHidden.cs +++ b/Projects/UOContent/Skills/DetectHidden.cs @@ -1,184 +1,54 @@ using System; -using System.Collections.Generic; -using Server.Collections; -using Server.Engines.PartySystem; using Server.Factions; -using Server.Guilds; -using Server.Items; using Server.Mobiles; using Server.Multis; -using Server.Network; -using Server.Systems.FeatureFlags; using Server.Targeting; -namespace Server.SkillHandlers; - -public static class DetectHidden +namespace Server.SkillHandlers { - // Debounce tracking: (stealther, detector) -> last detection time - private static readonly Dictionary<(Mobile, Mobile), long> PassiveDetectDebounce = []; - - private const int PassiveDetectDebounceMs = 3000; // 3 seconds - private const int DebounceCleanupIntervalMs = 10000; // Run cleanup every 10 seconds - private const int DebounceExpiryMs = 10000; // Remove entries older than 10 seconds - private static long _lastCleanupTime; - - public static void Initialize() + public static class DetectHidden { - SkillInfo.Table[(int)SkillName.DetectHidden].Callback = OnUse; - } - - public static TimeSpan OnUse(Mobile src) - { - src.SendLocalizedMessage(500819); // Where will you search? - src.Target = new InternalTarget(); - - return TimeSpan.FromSeconds(30.0); - } - - // Clean up old debounce entries to prevent memory bloat - private static void CleanupDebounceCache(long now) - { - foreach (var entry in PassiveDetectDebounce) + public static void Initialize() { - if (now - entry.Value > DebounceExpiryMs) + SkillInfo.Table[(int)SkillName.DetectHidden].Callback = OnUse; + } + + public static TimeSpan OnUse(Mobile src) + { + src.SendLocalizedMessage(500819); // Where will you search? + src.Target = new InternalTarget(); + + return TimeSpan.FromSeconds(6.0); + } + + private class InternalTarget : Target + { + public InternalTarget() : base(12, true, TargetFlags.None) { - PassiveDetectDebounce.Remove(entry.Key); } - } - } - // For testing: clear the debounce cache to prevent cross-test contamination - internal static void ClearDebounceCache() - { - PassiveDetectDebounce.Clear(); - } - - // Passive detection: check if a detector can passively detect a stealther. - // Called via OnMovement when either party moves within range. - // Returns true if detection was successful (and the stealther was revealed). - // NOTE: OSI uncertain - The exact chance calculation and distance dropoff is unknown. - // We use a ±10 variance on both skills, matching active detection mechanics. - public static bool TryDetectStealther(Mobile detector, Mobile stealther) - { - if (!ContentFeatureFlags.PassiveDetectHidden || stealther == detector || !stealther.Hidden || !detector.Alive) - { - return false; - } - - // Felucca PvP only - if (stealther.Map != Map.Felucca) - { - return false; - } - - var detectSkill = detector.Skills.DetectHidden.Value; - if (detectSkill <= 0) - { - return false; - } - - var now = Core.TickCount; - - // Debounce: skip pairs already checked recently (cheap dictionary lookup before expensive checks) - var key = (stealther, detector); - if (PassiveDetectDebounce.TryGetValue(key, out var lastDetect) && now - lastDetect < PassiveDetectDebounceMs) - { - return false; - } - - // Periodic cleanup (amortized, not every call) - if (now - _lastCleanupTime > DebounceCleanupIntervalMs) - { - _lastCleanupTime = now; - CleanupDebounceCache(now); - } - - // Excludes blessed, dead, bonded pets, and region-based PvP rules - if (stealther.AccessLevel > AccessLevel.Player || !detector.CanBeHarmful(stealther, false)) - { - return false; - } - - // Exclude party members - var stealtherParty = Party.Get(stealther); - var detectorParty = Party.Get(detector); - if (stealtherParty != null && stealtherParty == detectorParty) - { - return false; - } - - // Exclude guild members and allies - if (stealther.Guild is Guild sg && detector.Guild is Guild dg && (sg == dg || sg.IsAlly(dg))) - { - return false; - } - - var ss = detectSkill + Utility.Random(21) - 10; - var ts = stealther.Skills.Hiding.Value + Utility.Random(21) - 10; - - if (ss >= ts) - { - stealther.RevealingAction(); - stealther.SendLocalizedMessage(500814); // You have been revealed! - PassiveDetectDebounce[key] = now; - return true; - } - - return false; - } - - private class InternalTarget : Target - { - public InternalTarget() : base(12, true, TargetFlags.None) - { - } - - protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) - { - from.NextSkillTime = Core.TickCount; - } - - protected override void OnTarget(Mobile src, object targ) - { - var foundAnyone = false; - var srcSkill = src.Skills.DetectHidden.Value; - var range = (int)(srcSkill / 10.0); - - if (targ is TrappableContainer container && container.TrapType != TrapType.None) + protected override void OnTarget(Mobile src, object targ) { - // Direct container targeting: show [trapped] if within detection range and skill check passes - if (src.InRange(container.GetWorldLocation(), range) && - src.CheckSkill(SkillName.DetectHidden, 0.0, 100.0)) - { - src.NetState.SendMessageLocalized( - container.Serial, - container.ItemID, - MessageType.Regular, - 0x3B2, - 3, - 500813 // [trapped] - ); + var foundAnyone = false; - foundAnyone = true; - } - } - else if (targ is not Item && (targ is not Mobile m || m == src)) - { - // Area scan only when targeting self or the ground - var p = targ switch + Point3D p = targ switch { Mobile mobile => mobile.Location, + Item item => item.Location, IPoint3D d => new Point3D(d), _ => src.Location }; + var srcSkill = src.Skills.DetectHidden.Value; + var range = (int)(srcSkill / 10.0); + if (!src.CheckSkill(SkillName.DetectHidden, 0.0, 100.0)) { range /= 2; } var house = BaseHouse.FindHouseAt(p, src.Map, 16); + var inHouse = house?.IsFriend(src) == true; if (inHouse) @@ -213,57 +83,32 @@ public static class DetectHidden foundAnyone = true; } - foreach (var item in src.Map.GetItemsInRange(p, range)) + if (Faction.Find(src) != null) { - if (item is BaseFactionTrap factionTrap) + foreach (var trap in src.Map.GetItemsInRange(p, range)) { - if (Faction.Find(src) != null && - src.CheckTargetSkill(SkillName.DetectHidden, factionTrap, 80.0, 100.0)) + if (src.CheckTargetSkill(SkillName.DetectHidden, trap, 80.0, 100.0)) { src.SendLocalizedMessage( 1042712, // You reveal a trap placed by a faction: true, - $" {(factionTrap.Faction == null ? "" : factionTrap.Faction.Definition.FriendlyName)}" + $" {(trap.Faction == null ? "" : trap.Faction.Definition.FriendlyName)}" ); - factionTrap.Visible = true; - factionTrap.BeginConceal(); + trap.Visible = true; + trap.BeginConceal(); foundAnyone = true; } } - else if (item is BaseTrap { Visible: false } trap) - { - // High Seas (Publish 79): Requires 75 Detect Hidden to detect dungeon traps - if (Core.HS && srcSkill < 75.0) - { - continue; - } - - trap.Visible = true; - Timer.StartTimer(TimeSpan.FromSeconds(10.0), () => - { - if (!trap.Deleted) - { - trap.Visible = false; - } - }); - - foundAnyone = true; - } } } - } - if (!foundAnyone) - { - src.SendLocalizedMessage(500817); // You can see nothing hidden there. + if (!foundAnyone) + { + src.SendLocalizedMessage(500817); // You can see nothing hidden there. + } } - - // Calculate how much time has passed since the targeter was opened - var ticksSinceTargeter = (int)(Core.TickCount - (src.NextSkillTime - 30000)); - var remainingCooldown = Math.Max(0, 10000 - ticksSinceTargeter); - src.NextSkillTime = Core.TickCount + remainingCooldown; } } } diff --git a/Projects/UOContent/Skills/ForensicEval.cs b/Projects/UOContent/Skills/ForensicEval.cs index 622b4941a..b2fd41939 100644 --- a/Projects/UOContent/Skills/ForensicEval.cs +++ b/Projects/UOContent/Skills/ForensicEval.cs @@ -71,8 +71,8 @@ namespace Server.SkillHandlers if (c.Looters.Count > 0) { - using var sb = new ValueStringBuilder(stackalloc char[128]); - var i = 0; + var sb = ValueStringBuilder.Create(128); + int i = 0; foreach (var looter in c.Looters) { if (i == c.Looters.Count - 1) @@ -93,6 +93,7 @@ namespace Server.SkillHandlers // This body has been disturbed by ~1_PLAYER_NAMES~ from.SendLocalizedMessage(1042752, sb.ToString()); + sb.Dispose(); } else { diff --git a/Projects/UOContent/Skills/Inscribe.cs b/Projects/UOContent/Skills/Inscribe.cs index d2d90f156..84c47de5f 100644 --- a/Projects/UOContent/Skills/Inscribe.cs +++ b/Projects/UOContent/Skills/Inscribe.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.Craft; -using Server.Engines.Craft.T2A; using Server.Items; using Server.Targeting; @@ -18,19 +16,10 @@ namespace Server.SkillHandlers public static TimeSpan OnUse(Mobile m) { - if (T2ACraftSystem.Enabled) - { - var target = new T2AInscribeTarget(); - m.Target = target; - m.SendAsciiMessage("Target the book you wish to copy or scroll you want to use."); - target.BeginTimeout(m, 60000); // 1 minute - return TimeSpan.FromSeconds(1.0); - } - - Target uotdTarget = new InternalTargetSrc(); - m.Target = uotdTarget; + Target target = new InternalTargetSrc(); + m.Target = target; m.SendLocalizedMessage(1046295); // Target the book you wish to copy. - uotdTarget.BeginTimeout(m, 60000); // 1 minute + target.BeginTimeout(m, 60000); // 1 minute return TimeSpan.FromSeconds(1.0); } @@ -53,12 +42,10 @@ namespace Server.SkillHandlers public static bool IsEmpty(BaseBook book) { - for (var i = 0; i < book.Pages.Length; i++) + foreach (var page in book.Pages) { - var page = book.Pages[i]; - for (var j = 0; j < page.Lines.Length; j++) + foreach (var line in page.Lines) { - var line = page.Lines[j]; if (!string.IsNullOrEmpty(line)) { return false; @@ -91,76 +78,6 @@ namespace Server.SkillHandlers } } - private class T2AInscribeTarget : Target - { - public T2AInscribeTarget() : base(3, false, TargetFlags.None) - { - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is BlankScroll scroll) - { - if (!scroll.IsChildOf(from.Backpack)) - { - from.SendAsciiMessage("That must be in your pack for you to use it."); - } - else - { - T2ACraftSystem.ShowMenu(from, DefInscription.CraftSystem, null); - } - } - else if (targeted is RecallRune) - { - var system = DefInscription.CraftSystem; - var itemDef = system.CraftItems.SearchFor(typeof(Runebook)); - if (itemDef != null) - { - var num = system.CanCraft(from, null, itemDef.ItemType); - if (num > 0) - { - from.SendLocalizedMessage(num); - } - else - { - system.CreateItem(from, itemDef.ItemType, null, null, itemDef); - } - } - } - else if (targeted is BaseBook book) - { - if (IsEmpty(book)) - { - from.SendLocalizedMessage(501611); // Can't copy an empty book. - } - else if (GetUser(book) != null) - { - from.SendLocalizedMessage(501621); // Someone else is inscribing that item. - } - else - { - Target target = new InternalTargetDst(book); - from.Target = target; - from.SendLocalizedMessage(501612); // Select a book to copy this to. - target.BeginTimeout(from, 60000); - SetUser(book, from); - } - } - else - { - from.SendLocalizedMessage(1046296); // That is not a book - } - } - - protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) - { - if (cancelType == TargetCancelType.Timeout) - { - from.SendLocalizedMessage(501619); - } - } - } - private class InternalTargetSrc : Target { public InternalTargetSrc() : base(3, false, TargetFlags.None) @@ -234,16 +151,19 @@ namespace Server.SkillHandlers { from.SendLocalizedMessage(501621); // Someone else is inscribing that item. } - else if (from.CheckTargetSkill(SkillName.Inscribe, bookDst, 0, 50)) - { - Copy(m_BookSrc, bookDst); - - from.SendLocalizedMessage(501618); // You make a copy of the book. - from.PlaySound(0x249); - } else { - from.SendLocalizedMessage(501617); // You fail to make a copy of the book. + if (from.CheckTargetSkill(SkillName.Inscribe, bookDst, 0, 50)) + { + Copy(m_BookSrc, bookDst); + + from.SendLocalizedMessage(501618); // You make a copy of the book. + from.PlaySound(0x249); + } + else + { + from.SendLocalizedMessage(501617); // You fail to make a copy of the book. + } } } diff --git a/Projects/UOContent/Skills/ItemIdentification.cs b/Projects/UOContent/Skills/ItemIdentification.cs index 19ddf585d..9d09208fb 100644 --- a/Projects/UOContent/Skills/ItemIdentification.cs +++ b/Projects/UOContent/Skills/ItemIdentification.cs @@ -37,7 +37,7 @@ public static class ItemIdentification return; } - var identified = false; + bool identified = false; if (o is Item item) { diff --git a/Projects/UOContent/Skills/Meditation.cs b/Projects/UOContent/Skills/Meditation.cs index d382c7c3a..b30f63a91 100644 --- a/Projects/UOContent/Skills/Meditation.cs +++ b/Projects/UOContent/Skills/Meditation.cs @@ -1,8 +1,6 @@ using System; -using Server.Engines.BuffIcons; using Server.Items; using Server.Misc; -using Server.Mobiles; namespace Server.SkillHandlers { @@ -81,7 +79,7 @@ namespace Server.SkillHandlers m.SendLocalizedMessage(501851); // You enter a meditative trance. m.Meditating = true; - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.ActiveMeditation, 1075657)); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.ActiveMeditation, 1075657)); if (m.Player || m.Body.IsHuman) { diff --git a/Projects/UOContent/Skills/Peacemaking.cs b/Projects/UOContent/Skills/Peacemaking.cs index 437249bba..c963395d5 100644 --- a/Projects/UOContent/Skills/Peacemaking.cs +++ b/Projects/UOContent/Skills/Peacemaking.cs @@ -27,22 +27,27 @@ namespace Server.SkillHandlers from.RevealingAction(); from.SendLocalizedMessage(1049525); // Whom do you wish to calm? from.Target = new InternalTarget(from, instrument); - from.NextSkillTime = Core.TickCount + 30000; // 30s timeout on the targeter + from.NextSkillTime = Core.TickCount + 21600000; } private class InternalTarget : Target { private readonly BaseInstrument m_Instrument; + private bool m_SetSkillTime = true; public InternalTarget(Mobile from, BaseInstrument instrument) : base( BaseInstrument.GetBardRange(from, SkillName.Peacemaking), false, TargetFlags.None - ) => m_Instrument = instrument; + ) => + m_Instrument = instrument; - protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) + protected override void OnTargetFinish(Mobile from) { - from.NextSkillTime = Core.TickCount; + if (m_SetSkillTime) + { + from.NextSkillTime = Core.TickCount; + } } protected override void OnTarget(Mobile from, object targeted) @@ -55,19 +60,21 @@ namespace Server.SkillHandlers } else if (from.Region.IsPartOf()) { - from.SendMessage("You may not use peacemaking in this area."); + from.SendMessage("You may not peacemake in this area."); } else if (targ.Region.IsPartOf()) { - from.SendMessage("You may not use peacemaking there."); + from.SendMessage("You may not peacemake there."); } else if (!m_Instrument.IsChildOf(from.Backpack)) { - // The instrument you are trying to play is no longer in your backpack! - from.SendLocalizedMessage(1062488); + from.SendLocalizedMessage( + 1062488 + ); // The instrument you are trying to play is no longer in your backpack! } else { + m_SetSkillTime = false; from.NextSkillTime = Core.TickCount + 10000; if (targeted == from) @@ -142,14 +149,17 @@ namespace Server.SkillHandlers if (!from.CanBeHarmful(targ, false)) { from.SendLocalizedMessage(1049528); + m_SetSkillTime = true; } else if (bc?.Uncalmable == true) { from.SendLocalizedMessage(1049526); // You have no chance of calming that creature. + m_SetSkillTime = true; } else if (bc?.BardPacified == true) { from.SendLocalizedMessage(1049527); // That creature is already being calmed. + m_SetSkillTime = true; } else if (!BaseInstrument.CheckMusicianship(from)) { @@ -183,10 +193,10 @@ namespace Server.SkillHandlers targ.Combatant = null; targ.Warmode = false; - from.SendLocalizedMessage(1049532); // You play hypnotic music, calming your target. if (bc != null) { - // You play hypnotic music, calming your target. + from.SendLocalizedMessage(1049532); // You play hypnotic music, calming your target. + var seconds = 100 - diff / 1.5; if (seconds > 120) @@ -202,7 +212,8 @@ namespace Server.SkillHandlers } else { - // You play hypnotic music, calming your target. + from.SendLocalizedMessage(1049532); // You play hypnotic music, calming your target. + // You hear lovely music, and forget to continue battling! targ.SendLocalizedMessage(500616); } diff --git a/Projects/UOContent/Skills/Provocation.cs b/Projects/UOContent/Skills/Provocation.cs index 9215a7f61..0cd329333 100644 --- a/Projects/UOContent/Skills/Provocation.cs +++ b/Projects/UOContent/Skills/Provocation.cs @@ -1,6 +1,5 @@ using System; using Server.Items; -using Server.Misc; using Server.Mobiles; using Server.Targeting; @@ -52,13 +51,6 @@ namespace Server.SkillHandlers 1062488 ); // The instrument you are trying to play is no longer in your backpack! } - else if (Notoriety.Compute(from, creature) == Notoriety.Innocent) - { - // Think not that I fail to see what thou art doing! - creature.Say(501591); - - Titles.AwardKarma(from, -75, true); - } else if (creature.Controlled) { from.SendLocalizedMessage(501590); // They are too loyal to their master to be provoked. diff --git a/Projects/UOContent/Skills/Stealing.cs b/Projects/UOContent/Skills/Stealing.cs index 99c92a4c9..c0459a621 100644 --- a/Projects/UOContent/Skills/Stealing.cs +++ b/Projects/UOContent/Skills/Stealing.cs @@ -5,7 +5,6 @@ using Server.Engines.ConPVP; using Server.Engines.Stealables; using Server.Factions; using Server.Items; -using Server.Misc; using Server.Mobiles; using Server.Spells; using Server.Spells.Fifth; @@ -13,512 +12,500 @@ using Server.Spells.Ninjitsu; using Server.Spells.Seventh; using Server.Targeting; -namespace Server.SkillHandlers; - -public static class Stealing +namespace Server.SkillHandlers { - public static bool ClassicMode { get; private set; } - - public static bool SuspendOnMurder { get; private set; } - - public static int MaxWeightToSteal { get; private set; } - - public static bool CanStealContainers { get; private set; } - - public static void Configure() + public static class Stealing { - ClassicMode = ServerConfiguration.GetSetting("stealing.classicMode", !Core.AOS); - SuspendOnMurder = ServerConfiguration.GetSetting("stealing.suspendOnMurder", !Core.AOS); - CanStealContainers = ServerConfiguration.GetSetting("stealing.canStealContainers", !Core.AOS); - MaxWeightToSteal = ServerConfiguration.GetSetting("stealing.maxWeightToSteal", 10); - } + public static readonly bool ClassicMode = false; + public static readonly bool SuspendOnMurder = false; - public static void Initialize() - { - SkillInfo.Table[33].Callback = OnUse; - } - - public static bool IsInGuild(Mobile m) => m is PlayerMobile mobile && mobile.NpcGuild == NpcGuild.ThievesGuild; - - public static bool IsInnocentTo(Mobile from, Mobile to) => Notoriety.Compute(from, to) == Notoriety.Innocent; - - public static bool IsEmptyHanded(Mobile from) => - from.FindItemOnLayer(Layer.OneHanded) == null && from.FindItemOnLayer(Layer.TwoHanded) == null; - - public static TimeSpan OnUse(Mobile m) - { - if (!IsEmptyHanded(m)) + public static void Initialize() { - m.SendLocalizedMessage(1005584); // Both hands must be free to steal. - } - else if (m.Region.IsPartOf()) - { - m.SendMessage("You may not steal in this area."); - } - else - { - m.Target = new StealingTarget(m); - m.RevealingAction(); - - m.SendLocalizedMessage(502698); // Which item do you want to steal? + SkillInfo.Table[33].Callback = OnUse; } - return TimeSpan.FromSeconds(30.0); - } + public static bool IsInGuild(Mobile m) => m is PlayerMobile mobile && mobile.NpcGuild == NpcGuild.ThievesGuild; - private class StealingTarget : Target - { - private readonly Mobile _thief; + public static bool IsInnocentTo(Mobile from, Mobile to) => Notoriety.Compute(from, to) == Notoriety.Innocent; - public StealingTarget(Mobile thief) : base(1, false, TargetFlags.None) + public static bool IsEmptyHanded(Mobile from) { - _thief = thief; - AllowNonlocal = true; + if (from.FindItemOnLayer(Layer.OneHanded) != null) + { + return false; + } + + if (from.FindItemOnLayer(Layer.TwoHanded) != null) + { + return false; + } + + return true; } - protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) + public static TimeSpan OnUse(Mobile m) { - from.NextSkillTime = Core.TickCount; - } - - private Item TryStealItem(Item toSteal, ref bool caught) - { - Item stolen = null; - - var root = toSteal.RootParent; - var mobRoot = root as Mobile; - var rootIsPlayer = mobRoot?.Player == true; - - var si = toSteal.Parent == null || !toSteal.Movable - ? StealableArtifacts.GetStealableInstance(toSteal) - : null; - - if (!IsEmptyHanded(_thief)) + if (!IsEmptyHanded(m)) { - _thief.SendLocalizedMessage(1005584); // Both hands must be free to steal. + m.SendLocalizedMessage(1005584); // Both hands must be free to steal. } - else if (_thief.Region.IsPartOf()) + else if (m.Region.IsPartOf()) { - _thief.SendMessage("You may not steal in this area."); - } - else if ((_thief as PlayerMobile)?.Young == true && (rootIsPlayer || mobRoot is BaseCreature)) - { - _thief.SendLocalizedMessage(502700); // You cannot steal from people or monsters right now. Practice on chests and barrels. - } - else if (rootIsPlayer && !IsInGuild(_thief)) - { - _thief.SendLocalizedMessage(1005596); // You must be in the thieves guild to steal from other players. - } - else if (SuspendOnMurder && rootIsPlayer && IsInGuild(_thief) && _thief.Kills > 0) - { - _thief.SendLocalizedMessage(502706); // You are currently suspended from the thieves guild. - } - else if ((mobRoot as PlayerMobile)?.Young == true) - { - _thief.SendLocalizedMessage(502699); // You cannot steal from the Young. - } - else if ((root as BaseVendor)?.IsInvulnerable == true) - { - _thief.SendLocalizedMessage(1005598); // You can't steal from shopkeepers. - } - else if (root is PlayerVendor) - { - _thief.SendLocalizedMessage(502709); // You can't steal from vendors. - } - else if (!_thief.CanSee(toSteal)) - { - _thief.SendLocalizedMessage(500237); // Target can not be seen. - } - else if (toSteal is Sigil sig) - { - var pl = PlayerState.Find(_thief); - var faction = pl?.Faction; - - if (!_thief.InRange(sig.GetWorldLocation(), 1)) - { - _thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it. - } - else if (root != null) // not on the ground - { - _thief.SendLocalizedMessage(502710); // You can't steal that! - } - else if (faction == null) - { - _thief.SendLocalizedMessage(1005588); // You must join a faction to do that - } - else if (!_thief.CanBeginAction()) - { - _thief.SendLocalizedMessage(1010581); // You cannot steal the sigil when you are incognito - } - else if (DisguisePersistence.IsDisguised(_thief)) - { - _thief.SendLocalizedMessage(1010583); // You cannot steal the sigil while disguised - } - else if (!_thief.CanBeginAction()) - { - _thief.SendLocalizedMessage(1010582); // You cannot steal the sigil while polymorphed - } - else if (TransformationSpellHelper.UnderTransformation(_thief)) - { - _thief.SendLocalizedMessage(1061622); // You cannot steal the sigil while in that form. - } - else if (AnimalForm.UnderTransformation(_thief)) - { - _thief.SendLocalizedMessage(1063222); // You cannot steal the sigil while mimicking an animal. - } - else if (pl.IsLeaving) - { - // You are currently quitting a faction and cannot steal the town sigil - _thief.SendLocalizedMessage(1005589); - } - else if (sig.IsBeingCorrupted && sig.LastMonolith.Faction == faction) - { - _thief.SendLocalizedMessage(1005590); // You cannot steal your own sigil - } - else if (sig.IsPurifying) - { - _thief.SendLocalizedMessage(1005592); // You cannot steal this sigil until it has been purified - } - else if (!_thief.CheckTargetSkill(SkillName.Stealing, toSteal, 80.0, 80.0)) - { - _thief.SendLocalizedMessage(1005594); // You do not have enough skill to steal the sigil - } - else if (Sigil.ExistsOn(_thief)) - { - // The sigil has gone back to its home location because you already have a sigil. - _thief.SendLocalizedMessage(1010258); - } - else if (_thief.Backpack?.CheckHold(_thief, sig, false, true) != true) - { - // The sigil has gone home because your backpack is full - _thief.SendLocalizedMessage(1010259); - } - else - { - if (sig.IsBeingCorrupted) - { - sig.GraceStart = Core.Now; // begin grace period - } - - _thief.SendLocalizedMessage(1010586); // YOU STOLE THE SIGIL!!! (woah, calm down now) - - if (sig.LastMonolith?.Sigil != null) - { - sig.LastMonolith.Sigil = null; - } - - sig.LastStolen = Core.Now; - - return sig; - } - } - else if (_thief.Backpack?.CheckHold(_thief, toSteal, false, true) != true) - { - _thief.SendLocalizedMessage(1048147); // Your backpack can't hold anything else. - } - else if (si == null && (toSteal.Parent == null || !toSteal.Movable) || toSteal.LootType == LootType.Newbied || - toSteal.CheckBlessed(mobRoot) || !CanStealContainers && si == null && toSteal is Container) - { - _thief.SendLocalizedMessage(502710); // You can't steal that! - } - else if (!_thief.InRange(toSteal.GetWorldLocation(), 1)) - { - _thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it. - } - else if (si != null && _thief.Skills.Stealing.Value < 100.0) - { - // You're not skilled enough to attempt the theft of this item. - _thief.SendLocalizedMessage(1060025, "", 0x66D); - } - else if (toSteal.Parent is Mobile) - { - _thief.SendLocalizedMessage(1005585); // You cannot steal items which are equipped. - } - else if (root == _thief) - { - _thief.SendLocalizedMessage(502704); // You catch yourself red-handed. - } - else if (mobRoot?.AccessLevel > AccessLevel.Player) - { - _thief.SendLocalizedMessage(502710); // You can't steal that! - } - else if (mobRoot != null && !_thief.CanBeHarmful(mobRoot)) - { - } - else if (root is Corpse) - { - _thief.SendLocalizedMessage(502710); // You can't steal that! + m.SendMessage("You may not steal in this area."); } else { - var w = toSteal.Weight + toSteal.TotalWeight; + m.Target = new StealingTarget(m); + m.RevealingAction(); - if (w > MaxWeightToSteal) + m.SendLocalizedMessage(502698); // Which item do you want to steal? + } + + return TimeSpan.FromSeconds(10.0); + } + + private class StealingTarget : Target + { + private readonly Mobile m_Thief; + + public StealingTarget(Mobile thief) : base(1, false, TargetFlags.None) + { + m_Thief = thief; + AllowNonlocal = true; + } + + private Item TryStealItem(Item toSteal, ref bool caught) + { + Item stolen = null; + + var root = toSteal.RootParent; + var mobRoot = root as Mobile; + + StealableArtifacts.StealableInstance si = toSteal.Parent == null || !toSteal.Movable + ? StealableArtifacts.GetStealableInstance(toSteal) + : null; + + if (!IsEmptyHanded(m_Thief)) { - // This item is too heavy to steal from someone's backpack. - _thief.SendLocalizedMessage(502722); + m_Thief.SendLocalizedMessage(1005584); // Both hands must be free to steal. } - else + else if (m_Thief.Region.IsPartOf()) { - if (toSteal.Stackable && toSteal.Amount > 1) + m_Thief.SendMessage("You may not steal in this area."); + } + else if (mobRoot?.Player == true && !IsInGuild(m_Thief)) + { + m_Thief.SendLocalizedMessage(1005596); // You must be in the thieves guild to steal from other players. + } + else if (SuspendOnMurder && mobRoot?.Player == true && IsInGuild(m_Thief) && + m_Thief.Kills > 0) + { + m_Thief.SendLocalizedMessage(502706); // You are currently suspended from the thieves guild. + } + else if (root is BaseVendor vendor && vendor.IsInvulnerable) + { + m_Thief.SendLocalizedMessage(1005598); // You can't steal from shopkeepers. + } + else if (root is PlayerVendor) + { + m_Thief.SendLocalizedMessage(502709); // You can't steal from vendors. + } + else if (!m_Thief.CanSee(toSteal)) + { + m_Thief.SendLocalizedMessage(500237); // Target can not be seen. + } + else if (m_Thief.Backpack?.CheckHold(m_Thief, toSteal, false, true) != true) + { + m_Thief.SendLocalizedMessage(1048147); // Your backpack can't hold anything else. + } + else if (toSteal is Sigil sig) + { + var pl = PlayerState.Find(m_Thief); + var faction = pl?.Faction; + + if (!m_Thief.InRange(sig.GetWorldLocation(), 1)) { - var maxAmount = Math.Clamp( - (int)(_thief.Skills.Stealing.Value / 10.0 / toSteal.Weight), - 1, - toSteal.Amount - ); - - var amount = Utility.RandomMinMax(1, maxAmount); - - if (amount >= toSteal.Amount) + m_Thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it. + } + else if (root != null) // not on the ground + { + m_Thief.SendLocalizedMessage(502710); // You can't steal that! + } + else if (faction != null) + { + if (!m_Thief.CanBeginAction()) { - var pileWeight = (int)Math.Ceiling(toSteal.Weight * toSteal.Amount); - pileWeight *= 10; - - if (_thief.CheckTargetSkill( - SkillName.Stealing, - toSteal, - pileWeight - 22.5, - pileWeight + 27.5 - )) + m_Thief.SendLocalizedMessage(1010581); // You cannot steal the sigil when you are incognito + } + else if (DisguisePersistence.IsDisguised(m_Thief)) + { + m_Thief.SendLocalizedMessage(1010583); // You cannot steal the sigil while disguised + } + else if (!m_Thief.CanBeginAction()) + { + m_Thief.SendLocalizedMessage(1010582); // You cannot steal the sigil while polymorphed + } + else if (TransformationSpellHelper.UnderTransformation(m_Thief)) + { + m_Thief.SendLocalizedMessage(1061622); // You cannot steal the sigil while in that form. + } + else if (AnimalForm.UnderTransformation(m_Thief)) + { + m_Thief.SendLocalizedMessage(1063222); // You cannot steal the sigil while mimicking an animal. + } + else if (pl.IsLeaving) + { + // You are currently quitting a faction and cannot steal the town sigil + m_Thief.SendLocalizedMessage(1005589); + } + else if (sig.IsBeingCorrupted && sig.LastMonolith.Faction == faction) + { + m_Thief.SendLocalizedMessage(1005590); // You cannot steal your own sigil + } + else if (sig.IsPurifying) + { + m_Thief.SendLocalizedMessage(1005592); // You cannot steal this sigil until it has been purified + } + else if (m_Thief.CheckTargetSkill(SkillName.Stealing, toSteal, 80.0, 80.0)) + { + if (Sigil.ExistsOn(m_Thief)) { - stolen = toSteal; + // The sigil has gone back to its home location because you already have a sigil. + m_Thief.SendLocalizedMessage(1010258); + } + else if (m_Thief.Backpack?.CheckHold(m_Thief, sig, false, true) != true) + { + // The sigil has gone home because your backpack is full + m_Thief.SendLocalizedMessage(1010259); + } + else + { + if (sig.IsBeingCorrupted) + { + sig.GraceStart = Core.Now; // begin grace period + } + + m_Thief.SendLocalizedMessage(1010586); // YOU STOLE THE SIGIL!!! (woah, calm down now) + + if (sig.LastMonolith?.Sigil != null) + { + sig.LastMonolith.Sigil = null; + sig.LastStolen = Core.Now; + } + + return sig; } } else { - var pileWeight = (int)Math.Ceiling(toSteal.Weight * amount); - pileWeight *= 10; + m_Thief.SendLocalizedMessage(1005594); // You do not have enough skill to steal the sigil + } + } + else + { + m_Thief.SendLocalizedMessage(1005588); // You must join a faction to do that + } + } + else if (si == null && (toSteal.Parent == null || !toSteal.Movable)) + { + m_Thief.SendLocalizedMessage(502710); // You can't steal that! + } + else if (toSteal.LootType == LootType.Newbied || toSteal.CheckBlessed(mobRoot)) + { + m_Thief.SendLocalizedMessage(502710); // You can't steal that! + } + else if (Core.AOS && si == null && toSteal is Container) + { + m_Thief.SendLocalizedMessage(502710); // You can't steal that! + } + else if (!m_Thief.InRange(toSteal.GetWorldLocation(), 1)) + { + m_Thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it. + } + else if (si != null && m_Thief.Skills.Stealing.Value < 100.0) + { + // You're not skilled enough to attempt the theft of this item. + m_Thief.SendLocalizedMessage(1060025, "", 0x66D); + } + else if (toSteal.Parent is Mobile) + { + m_Thief.SendLocalizedMessage(1005585); // You cannot steal items which are equipped. + } + else if (root == m_Thief) + { + m_Thief.SendLocalizedMessage(502704); // You catch yourself red-handed. + } + else if (mobRoot?.AccessLevel > AccessLevel.Player) + { + m_Thief.SendLocalizedMessage(502710); // You can't steal that! + } + else if (mobRoot != null && !m_Thief.CanBeHarmful(mobRoot)) + { + } + else if (root is Corpse) + { + m_Thief.SendLocalizedMessage(502710); // You can't steal that! + } + else + { + var w = toSteal.Weight + toSteal.TotalWeight; - if (_thief.CheckTargetSkill( + if (w > 10) + { + m_Thief.SendMessage("That is too heavy to steal."); + } + else + { + if (toSteal.Stackable && toSteal.Amount > 1) + { + var maxAmount = Math.Clamp( + (int)(m_Thief.Skills.Stealing.Value / 10.0 / toSteal.Weight), + 1, + toSteal.Amount + ); + + var amount = Utility.RandomMinMax(1, maxAmount); + + if (amount >= toSteal.Amount) + { + var pileWeight = (int)Math.Ceiling(toSteal.Weight * toSteal.Amount); + pileWeight *= 10; + + if (m_Thief.CheckTargetSkill( SkillName.Stealing, toSteal, pileWeight - 22.5, pileWeight + 27.5 )) + { + stolen = toSteal; + } + } + else { - stolen = Mobile.LiftItemDupe(toSteal, toSteal.Amount - amount) ?? toSteal; + var pileWeight = (int)Math.Ceiling(toSteal.Weight * amount); + pileWeight *= 10; + + if (m_Thief.CheckTargetSkill( + SkillName.Stealing, + toSteal, + pileWeight - 22.5, + pileWeight + 27.5 + )) + { + stolen = Mobile.LiftItemDupe(toSteal, toSteal.Amount - amount) ?? toSteal; + } + } + } + else + { + var iw = (int)Math.Ceiling(w); + iw *= 10; + + if (m_Thief.CheckTargetSkill(SkillName.Stealing, toSteal, iw - 22.5, iw + 27.5)) + { + stolen = toSteal; + } + } + + if (stolen != null) + { + m_Thief.SendLocalizedMessage(502724); // You successfully steal the item. + + if (si != null) + { + toSteal.Movable = true; + si.Item = null; + } + } + else + { + m_Thief.SendLocalizedMessage(502723); // You fail to steal the item. + } + + caught = m_Thief.Skills.Stealing.Value < Utility.Random(150); + } + } + + return stolen; + } + + protected override void OnTarget(Mobile from, object target) + { + from.RevealingAction(); + + Item stolen = null; + IEntity root = null; + var caught = false; + + if (target is Item item) + { + root = item.RootParent; + stolen = TryStealItem(item, ref caught); + } + else if (target is Mobile mobile) + { + var pack = mobile.Backpack; + + if (pack?.Items.Count > 0) + { + root = mobile; + stolen = TryStealItem(pack.Items.RandomElement(), ref caught); + } + } + else + { + m_Thief.SendLocalizedMessage(502710); // You can't steal that! + } + + var mobRoot = root as Mobile; + + if (stolen != null) + { + from.AddToBackpack(stolen); + + if (!(stolen is Container || stolen.Stackable)) + { + StolenItem.Add(stolen, m_Thief, mobRoot); + } + } + + var corpse = root as Corpse; + + if (caught) + { + if (root == null || corpse?.IsCriminalAction(m_Thief) == true) + { + m_Thief.CriminalAction(false); + } + else if (mobRoot != null) + { + if (!IsInGuild(mobRoot) && IsInnocentTo(m_Thief, mobRoot)) + { + m_Thief.CriminalAction(false); + } + + var message = $"You notice {m_Thief.Name} trying to steal from {mobRoot.Name}."; + + foreach (var ns in m_Thief.GetClientsInRange(8)) + { + if (ns.Mobile != m_Thief) + { + ns.Mobile.SendMessage(message); } } } - else + } + else if (corpse?.IsCriminalAction(m_Thief) == true) + { + m_Thief.CriminalAction(false); + } + + if (mobRoot?.Player == true && m_Thief is PlayerMobile pm && + IsInnocentTo(pm, mobRoot) && !IsInGuild(mobRoot)) + { + pm.PermaFlags.Add(mobRoot); + pm.Delta(MobileDelta.Noto); + } + } + } + } + + public class StolenItem + { + public static readonly TimeSpan StealTime = TimeSpan.FromMinutes(2.0); + + private static readonly Queue m_Queue = new(); + + public StolenItem(Item stolen, Mobile thief, Mobile victim) + { + Stolen = stolen; + Thief = thief; + Victim = victim; + + Expires = Core.Now + StealTime; + } + + public Item Stolen { get; } + + public Mobile Thief { get; } + + public Mobile Victim { get; } + + public DateTime Expires { get; private set; } + + public bool IsExpired => Core.Now >= Expires; + + public static void Add(Item item, Mobile thief, Mobile victim) + { + Clean(); + + m_Queue.Enqueue(new StolenItem(item, thief, victim)); + } + + public static bool IsStolen(Item item) + { + Mobile victim = null; + + return IsStolen(item, ref victim); + } + + public static bool IsStolen(Item item, ref Mobile victim) + { + Clean(); + + foreach (var si in m_Queue) + { + if (si.Stolen == item && !si.IsExpired) + { + victim = si.Victim; + return true; + } + } + + return false; + } + + [OnEvent(nameof(PlayerMobile.PlayerDeathEvent))] + public static void ReturnOnDeath(Mobile killed) + { + Clean(); + + var corpse = killed.Corpse; + + foreach (var si in m_Queue) + { + if (si.Stolen.RootParent == corpse && si.Victim != null && !si.IsExpired) + { + if (si.Victim.AddToBackpack(si.Stolen)) { - var iw = (int)Math.Ceiling(w); - iw *= 10; - - if (_thief.CheckTargetSkill(SkillName.Stealing, toSteal, iw - 22.5, iw + 27.5)) - { - stolen = toSteal; - } - } - - if (stolen != null) - { - _thief.SendLocalizedMessage(502724); // You successfully steal the item. - - if (si != null) - { - toSteal.Movable = true; - si.Item = null; - } + si.Victim.SendLocalizedMessage(1010464); // the item that was stolen is returned to you. } else { - _thief.SendLocalizedMessage(502723); // You fail to steal the item. + si.Victim.SendLocalizedMessage(1010463); // the item that was stolen from you falls to the ground. } - caught = _thief.Skills.Stealing.Value < Utility.Random(150); + si.Expires = Core.Now; // such a hack } } - - return stolen; } - protected override void OnTarget(Mobile from, object target) + public static void Clean() { - from.RevealingAction(); - - Item stolen = null; - IEntity root = null; - var caught = false; - - if (target is Item item) + while (m_Queue.Count > 0) { - root = item.RootParent; - stolen = TryStealItem(item, ref caught); - } - else if (target is Mobile mobile) - { - var pack = mobile.Backpack; + var si = m_Queue.Peek(); - if (pack?.Items.Count > 0) + if (si.IsExpired) { - root = mobile; - stolen = TryStealItem(pack.Items.RandomElement(), ref caught); + m_Queue.Dequeue(); + } + else + { + break; } } - else - { - _thief.SendLocalizedMessage(502710); // You can't steal that! - } - - Titles.AwardKarma(from, -50, true); - - var mobRoot = root as Mobile; - - if (stolen != null) - { - from.AddToBackpack(stolen); - - if (!(stolen is Container || stolen.Stackable)) - { - StolenItem.Add(stolen, _thief, mobRoot); - } - } - - var corpse = root as Corpse; - - if (caught) - { - if (root == null || corpse?.IsCriminalAction(_thief) == true) - { - _thief.CriminalAction(false); - } - else if (mobRoot != null) - { - if (!IsInGuild(mobRoot) && IsInnocentTo(_thief, mobRoot)) - { - _thief.CriminalAction(false); - } - - var message = $"You notice {_thief.Name} trying to steal from {mobRoot.Name}."; - - foreach (var ns in _thief.GetClientsInRange(8)) - { - if (ns.Mobile != _thief) - { - ns.Mobile.SendMessage(message); - } - } - } - } - else if (corpse?.IsCriminalAction(_thief) == true) - { - _thief.CriminalAction(false); - } - - if (mobRoot?.Player == true && _thief is PlayerMobile pm && - IsInnocentTo(pm, mobRoot) && !IsInGuild(mobRoot)) - { - pm.PermaFlags.Add(mobRoot); - pm.Delta(MobileDelta.Noto); - } - - const int TargeterCooldown = 30000; // 30s - const int SkillCooldown = 10000; // 10s - - // Calculate how much time has passed since the targeter was opened - var ticksSinceTargeter = (int)(Core.TickCount - (from.NextSkillTime - TargeterCooldown)); - var remainingCooldown = Math.Max(0, SkillCooldown - ticksSinceTargeter); - from.NextSkillTime = Core.TickCount + remainingCooldown; - } - } -} - -public class StolenItem -{ - public static readonly TimeSpan StealTime = TimeSpan.FromMinutes(2.0); - - private static readonly Queue _queue = []; - - public StolenItem(Item stolen, Mobile thief, Mobile victim) - { - Stolen = stolen; - Thief = thief; - Victim = victim; - - Expires = Core.Now + StealTime; - } - - public Item Stolen { get; } - - public Mobile Thief { get; } - - public Mobile Victim { get; } - - public DateTime Expires { get; private set; } - - public bool IsExpired => Core.Now >= Expires; - - public static void Add(Item item, Mobile thief, Mobile victim) - { - Clean(); - - _queue.Enqueue(new StolenItem(item, thief, victim)); - } - - public static bool IsStolen(Item item) - { - Mobile victim = null; - - return IsStolen(item, ref victim); - } - - public static bool IsStolen(Item item, ref Mobile victim) - { - Clean(); - - foreach (var si in _queue) - { - if (si.Stolen == item && !si.IsExpired) - { - victim = si.Victim; - return true; - } - } - - return false; - } - - [OnEvent(nameof(PlayerMobile.PlayerDeathEvent))] - public static void ReturnOnDeath(Mobile killed) - { - Clean(); - - var corpse = killed.Corpse; - - foreach (var si in _queue) - { - if (si.Stolen.RootParent != corpse || si.Victim == null || si.IsExpired) - { - continue; - } - - if (si.Victim.AddToBackpack(si.Stolen)) - { - si.Victim.SendLocalizedMessage(1010464); // the item that was stolen is returned to you. - } - else - { - si.Victim.SendLocalizedMessage(1010463); // the item that was stolen from you falls to the ground. - } - - si.Expires = Core.Now; // such a hack - } - } - - public static void Clean() - { - while (_queue.Count > 0) - { - var si = _queue.Peek(); - - if (!si.IsExpired) - { - break; - } - - _queue.Dequeue(); } } } diff --git a/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs b/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs index cf8829a21..f55db47a1 100644 --- a/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs +++ b/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: OutgoingArrowPackets.cs * * * diff --git a/Projects/UOContent/Skills/Tracking/Tracking.cs b/Projects/UOContent/Skills/Tracking/Tracking.cs index 4825f8bc4..f59f3462a 100644 --- a/Projects/UOContent/Skills/Tracking/Tracking.cs +++ b/Projects/UOContent/Skills/Tracking/Tracking.cs @@ -7,375 +7,350 @@ using Server.Network; using Server.Spells; using Server.Spells.Necromancy; -namespace Server.SkillHandlers; - -public static class Tracking +namespace Server.SkillHandlers { - private static readonly Dictionary _table = new(); - - public static unsafe void Configure() + public static class Tracking { - IncomingExtendedCommandPackets.RegisterExtended(0x07, true, &QuestArrow); - } + private static readonly Dictionary _table = new(); - public static void Initialize() - { - SkillInfo.Table[(int)SkillName.Tracking].Callback = OnUse; - } - - public static void QuestArrow(NetState state, SpanReader reader) - { - if (state.Mobile is PlayerMobile from) + public static unsafe void Configure() { - var rightClick = reader.ReadBoolean(); - - from.QuestArrow?.OnClick(rightClick); - } - } - - public static TimeSpan OnUse(Mobile m) - { - if (m is PlayerMobile pm) - { - m.SendLocalizedMessage(1011350); // What do you wish to track? - - var gumps = pm.GetGumps(); - - gumps.Close(); - gumps.Close(); - gumps.Send(new TrackWhatGump()); + IncomingExtendedCommandPackets.RegisterExtended(0x07, true, &QuestArrow); } - return TimeSpan.FromSeconds(10.0); // 10 second delay before being able to re-use a skill - } - - public static void AddInfo(Mobile tracker, Mobile target) - { - var info = new TrackingInfo(target); - _table[tracker] = info; - } - - public static double GetStalkingBonus(Mobile tracker, Mobile target) - { - if (!_table.Remove(tracker, out var info) || info._target != target || info._map != target.Map) + public static void Initialize() { - return 0.0; + SkillInfo.Table[(int)SkillName.Tracking].Callback = OnUse; } - var xDelta = info._location.X - target.X; - var yDelta = info._location.Y - target.Y; - - var bonus = Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - - return Core.ML ? Math.Min(bonus, 10 + tracker.Skills.Tracking.Value / 10) : bonus; - } - - public static void ClearTrackingInfo(Mobile tracker) - { - _table.Remove(tracker); - } - - private class TrackingInfo - { - public Point2D _location; - public readonly Map _map; - public readonly Mobile _target; - - public TrackingInfo(Mobile target) + public static void QuestArrow(NetState state, SpanReader reader) { - _target = target; - _location = new Point2D(target); - _map = target.Map; - } - } -} - -public class TrackWhatGump : StaticGump -{ - public TrackWhatGump() : base(20, 30) - { - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 440, 135, 5054); - - builder.AddBackground(10, 10, 420, 75, 2620); - builder.AddBackground(10, 85, 420, 25, 3000); - - builder.AddItem(20, 20, 9682); - builder.AddButton(20, 110, 4005, 4007, 1); - builder.AddHtmlLocalized(20, 90, 100, 20, 1018087); // Animals - - builder.AddItem(120, 20, 9607); - builder.AddButton(120, 110, 4005, 4007, 2); - builder.AddHtmlLocalized(120, 90, 100, 20, 1018088); // Monsters - - builder.AddItem(220, 20, 8454); - builder.AddButton(220, 110, 4005, 4007, 3); - builder.AddHtmlLocalized(220, 90, 100, 20, 1018089); // Human NPCs - - builder.AddItem(320, 20, 8455); - builder.AddButton(320, 110, 4005, 4007, 4); - builder.AddHtmlLocalized(320, 90, 100, 20, 1018090); // Players - } - - public override void OnResponse(NetState state, in RelayInfo info) - { - if (info.ButtonID is >= 1 and <= 4 && state.Mobile is PlayerMobile pm) - { - var success = pm.CheckSkill(SkillName.Tracking, 0.0, 21.1); - TrackWhoGump.DisplayTo(success, pm, info.ButtonID - 1); - } - } -} - -public class TrackWhoGump : DynamicGump -{ - private const int MaxClosest = 12; - - private readonly Mobile[] _targets; - private readonly int _range; - - private TrackWhoGump(Mobile[] targets, int range) : base(20, 30) - { - _targets = targets; - _range = range; - } - - public static void DisplayTo(bool success, PlayerMobile from, int type) - { - if (!success) - { - from.SendLocalizedMessage(1018092); // You see no evidence of those in the area. - return; - } - - var map = from.Map; - - if (map == null) - { - return; - } - - from.CheckSkill(SkillName.Tracking, 21.1, 100.0); // Passive gain - - // 10 tiles base + 10 tiles per 10 skill - var range = 10 + (int)from.Skills.Tracking.Value / 10 * 10; - - var mobs = GetClosestMobs(from, range, type); - - if (mobs.Length > 0) - { - from.SendGump(new TrackWhoGump(mobs, range)); - from.SendLocalizedMessage(1018093); // Select the one you would like to track. - } - else if (type == 0) - { - from.SendLocalizedMessage(502991); // You see no evidence of animals in the area. - } - else if (type == 1) - { - from.SendLocalizedMessage(502993); // You see no evidence of creatures in the area. - } - else - { - from.SendLocalizedMessage(502995); // You see no evidence of people in the area. - } - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - - builder.AddBackground(0, 0, 440, 155, 5054); - - builder.AddBackground(10, 10, 420, 75, 2620); - builder.AddBackground(10, 85, 420, 45, 3000); - - if (_targets.Length > 4) - { - builder.AddBackground(0, 155, 440, 155, 5054); - - builder.AddBackground(10, 165, 420, 75, 2620); - builder.AddBackground(10, 240, 420, 45, 3000); - - if (_targets.Length > 8) + if (state.Mobile is PlayerMobile from) { - builder.AddBackground(0, 310, 440, 155, 5054); + var rightClick = reader.ReadBoolean(); - builder.AddBackground(10, 320, 420, 75, 2620); - builder.AddBackground(10, 395, 420, 45, 3000); + from.QuestArrow?.OnClick(rightClick); } } - for (var i = 0; i < _targets.Length; ++i) + public static TimeSpan OnUse(Mobile m) { - var m = _targets[i]; - - builder.AddItem(20 + i % 4 * 100, 20 + i / 4 * 155, ShrinkTable.Lookup(m)); - builder.AddButton(20 + i % 4 * 100, 130 + i / 4 * 155, 4005, 4007, i + 1); - - if (m.Name != null) + if (m is PlayerMobile pm) { - builder.AddHtml(20 + i % 4 * 100, 90 + i / 4 * 155, 90, 40, m.Name); + m.SendLocalizedMessage(1011350); // What do you wish to track? + + var gumps = pm.GetGumps(); + + gumps.Close(); + gumps.Close(); + gumps.Send(new TrackWhatGump(pm)); + } + + return TimeSpan.FromSeconds(10.0); // 10 second delay before being able to re-use a skill + } + + public static void AddInfo(Mobile tracker, Mobile target) + { + var info = new TrackingInfo(tracker, target); + _table[tracker] = info; + } + + public static double GetStalkingBonus(Mobile tracker, Mobile target) + { + if (!_table.Remove(tracker, out var info) || info._target != target || info._map != target.Map) + { + return 0.0; + } + + var xDelta = info._location.X - target.X; + var yDelta = info._location.Y - target.Y; + + var bonus = Math.Sqrt(xDelta * xDelta + yDelta * yDelta); + + return Core.ML ? Math.Min(bonus, 10 + tracker.Skills.Tracking.Value / 10) : bonus; + } + + public static void ClearTrackingInfo(Mobile tracker) + { + _table.Remove(tracker); + } + + private class TrackingInfo + { + public Point2D _location; + public readonly Map _map; + public readonly Mobile _target; + public Mobile _tracker; + + public TrackingInfo(Mobile tracker, Mobile target) + { + _tracker = tracker; + _target = target; + _location = new Point2D(target); + _map = target.Map; } } } - private static Mobile[] GetClosestMobs(Mobile from, int range, int type) + public class TrackWhatGump : Gump { - var loc = from.Location; + private readonly PlayerMobile _from; + private readonly bool _success; - // We only track the closest 12 - // Using a simple array with count is sufficient since we're single-threaded - var mobs = new Mobile[MaxClosest]; - Span distances = stackalloc double[MaxClosest]; - var maxDistance = double.MaxValue; - var count = 0; - - foreach (var (m, minDistance) in from.GetMobilesInRangeByDistance(range)) + public TrackWhatGump(PlayerMobile from) : base(20, 30) { - if (count == MaxClosest && minDistance > maxDistance) + _from = from; + _success = from.CheckSkill(SkillName.Tracking, 0.0, 21.1); + + AddPage(0); + + AddBackground(0, 0, 440, 135, 5054); + + AddBackground(10, 10, 420, 75, 2620); + AddBackground(10, 85, 420, 25, 3000); + + AddItem(20, 20, 9682); + AddButton(20, 110, 4005, 4007, 1); + AddHtmlLocalized(20, 90, 100, 20, 1018087); // Animals + + AddItem(120, 20, 9607); + AddButton(120, 110, 4005, 4007, 2); + AddHtmlLocalized(120, 90, 100, 20, 1018088); // Monsters + + AddItem(220, 20, 8454); + AddButton(220, 110, 4005, 4007, 3); + AddHtmlLocalized(220, 90, 100, 20, 1018089); // Human NPCs + + AddItem(320, 20, 8455); + AddButton(320, 110, 4005, 4007, 4); + AddHtmlLocalized(320, 90, 100, 20, 1018090); // Players + } + + public override void OnResponse(NetState state, in RelayInfo info) + { + if (info.ButtonID >= 1 && info.ButtonID <= 4) { - break; + TrackWhoGump.DisplayTo(_success, _from, info.ButtonID - 1); } + } + } - if (m == from || Core.AOS && !m.Alive || - m.Hidden && m.AccessLevel != AccessLevel.Player && from.AccessLevel <= m.AccessLevel || - !IsValidMobileType(m, type) || !CheckDifficulty(from, m)) + public class TrackWhoGump : Gump + { + private const int MaxClosest = 12; + + private readonly PlayerMobile _from; + private readonly Mobile[] _targets; + private readonly int _range; + + private TrackWhoGump(PlayerMobile from, Mobile[] targets, int range) : base(20, 30) + { + _from = from; + _targets = targets; + _range = range; + + AddPage(0); + + AddBackground(0, 0, 440, 155, 5054); + + AddBackground(10, 10, 420, 75, 2620); + AddBackground(10, 85, 420, 45, 3000); + + if (targets.Length > 4) { - continue; - } + AddBackground(0, 155, 440, 155, 5054); - var distance = m.GetDistanceToSqrt(loc); + AddBackground(10, 165, 420, 75, 2620); + AddBackground(10, 240, 420, 45, 3000); - if (count >= MaxClosest && distance >= maxDistance) - { - continue; - } - - var searchLimit = Math.Min(count, MaxClosest - 1); - var insertIndex = searchLimit; - - for (var i = 0; i < searchLimit; i++) - { - if (distance < distances[i]) + if (targets.Length > 8) { - insertIndex = i; - break; + AddBackground(0, 310, 440, 155, 5054); + + AddBackground(10, 320, 420, 75, 2620); + AddBackground(10, 395, 420, 45, 3000); } } - // Shift elements to make room for insertion - if (insertIndex < searchLimit) + for (var i = 0; i < targets.Length; ++i) { - var shiftCount = searchLimit - insertIndex; - Array.Copy(mobs, insertIndex, mobs, insertIndex + 1, shiftCount); - distances.Slice(insertIndex, shiftCount).CopyTo(distances[(insertIndex + 1)..]); - } + var m = targets[i]; - mobs[insertIndex] = m; - distances[insertIndex] = distance; + AddItem(20 + i % 4 * 100, 20 + i / 4 * 155, ShrinkTable.Lookup(m)); + AddButton(20 + i % 4 * 100, 130 + i / 4 * 155, 4005, 4007, i + 1); - if (count < MaxClosest) - { - count++; - } - - if (count == MaxClosest) - { - maxDistance = distances[MaxClosest - 1]; + if (m.Name != null) + { + AddHtml(20 + i % 4 * 100, 90 + i / 4 * 155, 90, 40, m.Name); + } } } - // Only resize if we found fewer than MaxClosest - if (count < MaxClosest) + public static void DisplayTo(bool success, PlayerMobile from, int type) { - Array.Resize(ref mobs, count); - } - - return mobs; - } - - // Tracking players uses tracking and detect hidden vs. hiding and stealth - private static bool CheckDifficulty(Mobile from, Mobile m) - { - if (!Core.AOS || !m.Player) - { - return true; - } - - var tracking = from.Skills.Tracking.Fixed; - var detectHidden = from.Skills.DetectHidden.Fixed; - - if (Core.ML && m.Race == Race.Elf) - { - tracking /= 2; // The 'Guide' says that it requires twice as Much tracking SKILL to track an elf. Not the total difficulty to track. - } - - var hiding = m.Skills.Hiding.Fixed; - var stealth = m.Skills.Stealth.Fixed; - var divisor = hiding + stealth; - - // Necromancy forms affect tracking difficulty - if (TransformationSpellHelper.UnderTransformation(m, typeof(HorrificBeastSpell))) - { - divisor -= 200; - } - else if (TransformationSpellHelper.UnderTransformation(m, typeof(VampiricEmbraceSpell)) && divisor < 500) - { - divisor = 500; - } - else if (TransformationSpellHelper.UnderTransformation(m, typeof(WraithFormSpell)) && divisor <= 2000) - { - divisor += 200; - } - - int chance; - if (divisor > 0) - { - if (Core.SE) + if (!success) { - chance = 50 * (tracking * 2 + detectHidden) / divisor; + from.SendLocalizedMessage(1018092); // You see no evidence of those in the area. + return; + } + + var map = from.Map; + + if (map == null) + { + return; + } + + from.CheckSkill(SkillName.Tracking, 21.1, 100.0); // Passive gain + + var range = 10 + (int)(from.Skills.Tracking.Value / 10); + + var mobs = GetClosestMobs(from, range, type); + + if (mobs.Length > 0) + { + from.SendGump(new TrackWhoGump(from, mobs, range)); + from.SendLocalizedMessage(1018093); // Select the one you would like to track. + } + else if (type == 0) + { + from.SendLocalizedMessage(502991); // You see no evidence of animals in the area. + } + else if (type == 1) + { + from.SendLocalizedMessage(502993); // You see no evidence of creatures in the area. } else { - chance = 50 * (tracking + detectHidden + 10 * Utility.RandomMinMax(1, 20)) / divisor; + from.SendLocalizedMessage(502995); // You see no evidence of people in the area. } } - else + + private static Mobile[] GetClosestMobs(Mobile from, int range, int type) { - chance = 100; + var loc = from.Location; + + // We only track the closest 12 + var mobs = new Mobile[MaxClosest]; + Span distances = stackalloc double[MaxClosest]; + distances.Fill(double.MaxValue); // Fill with max values + var total = 0; + + foreach (var m in from.GetMobilesInRange(range)) + { + if (m == from || Core.AOS && !m.Alive || + m.Hidden && m.AccessLevel != AccessLevel.Player && from.AccessLevel <= m.AccessLevel || + !IsValidMobileType(m, type) || !CheckDifficulty(from, m)) + { + continue; + } + + total++; + + var distance = m.GetDistanceToSqrt(loc); + for (var i = 0; i < MaxClosest; i++) + { + if (distance < distances[i]) + { + // Shift down the rest + for (int j = MaxClosest - 1; j > i; j--) + { + mobs[j] = mobs[j - 1]; + distances[j] = distances[j - 1]; + } + + mobs[i] = m; + distances[i] = distance; + break; + } + } + } + + if (total < MaxClosest) + { + Array.Resize(ref mobs, total); + } + + return mobs; } - return chance >= 100 || chance > Utility.Random(100); - } - - private static bool IsValidMobileType(Mobile m, int type) => - type switch + // Tracking players uses tracking and detect hidden vs. hiding and stealth + private static bool CheckDifficulty(Mobile from, Mobile m) { - 0 => !m.Player && m.Body.IsAnimal, - 1 => !m.Player && m.Body.IsMonster, - 2 => !m.Player && m.Body.IsHuman, - _ => m.Player - }; - - public override void OnResponse(NetState state, in RelayInfo info) - { - var index = info.ButtonID - 1; - - if (index >= 0 && index < _targets.Length && index < 12 && state.Mobile is PlayerMobile pm) - { - var m = _targets[index]; - - pm.QuestArrow = new TrackArrow(pm, m, _range * 2); - - if (Core.SE) + if (!Core.AOS || !m.Player) { - Tracking.AddInfo(pm, m); + return true; + } + + var tracking = from.Skills.Tracking.Fixed; + var detectHidden = from.Skills.DetectHidden.Fixed; + + if (Core.ML && m.Race == Race.Elf) + { + tracking /= 2; // The 'Guide' says that it requires twice as Much tracking SKILL to track an elf. Not the total difficulty to track. + } + + var hiding = m.Skills.Hiding.Fixed; + var stealth = m.Skills.Stealth.Fixed; + var divisor = hiding + stealth; + + // Necromancy forms affect tracking difficulty + if (TransformationSpellHelper.UnderTransformation(m, typeof(HorrificBeastSpell))) + { + divisor -= 200; + } + else if (TransformationSpellHelper.UnderTransformation(m, typeof(VampiricEmbraceSpell)) && divisor < 500) + { + divisor = 500; + } + else if (TransformationSpellHelper.UnderTransformation(m, typeof(WraithFormSpell)) && divisor <= 2000) + { + divisor += 200; + } + + int chance; + if (divisor > 0) + { + if (Core.SE) + { + chance = 50 * (tracking * 2 + detectHidden) / divisor; + } + else + { + chance = 50 * (tracking + detectHidden + 10 * Utility.RandomMinMax(1, 20)) / divisor; + } + } + else + { + chance = 100; + } + + return chance >= 100 || chance > Utility.Random(100); + } + + private static bool IsValidMobileType(Mobile m, int type) => + type switch + { + 0 => !m.Player && m.Body.IsAnimal, + 1 => !m.Player && m.Body.IsMonster, + 2 => !m.Player && m.Body.IsHuman, + _ => m.Player + }; + + public override void OnResponse(NetState state, in RelayInfo info) + { + var index = info.ButtonID - 1; + + if (index >= 0 && index < _targets.Length && index < 12) + { + var m = _targets[index]; + + _from.QuestArrow = new TrackArrow(_from, m, _range * 2); + + if (Core.SE) + { + Tracking.AddInfo(_from, m); + } } } } diff --git a/Projects/UOContent/Special Systems/Engines/PreventInaccess.cs b/Projects/UOContent/Special Systems/Engines/PreventInaccess.cs index cea6a65ad..8e45c53a7 100644 --- a/Projects/UOContent/Special Systems/Engines/PreventInaccess.cs +++ b/Projects/UOContent/Special Systems/Engines/PreventInaccess.cs @@ -69,7 +69,7 @@ namespace Server.Misc } } - private static bool HasDisconnected(Mobile m) => m.NetState is not { IsConnected: true }; + private static bool HasDisconnected(Mobile m) => m.NetState?.Connection == null; private static LocationInfo GetRandomDestination() => m_Destinations.RandomElement(); diff --git a/Projects/UOContent/Special Systems/Engines/TestCenter.cs b/Projects/UOContent/Special Systems/Engines/TestCenter.cs index e7105575d..23480d38b 100644 --- a/Projects/UOContent/Special Systems/Engines/TestCenter.cs +++ b/Projects/UOContent/Special Systems/Engines/TestCenter.cs @@ -1,4 +1,5 @@ using System; +using System.Text; using CommunityToolkit.HighPerformance; using Server.Commands; using Server.Factions; @@ -6,7 +7,6 @@ using Server.Gumps; using Server.Items; using Server.Mobiles; using Server.Network; -using Server.Text; namespace Server.Misc { @@ -703,7 +703,7 @@ namespace Server.Misc Array.Sort(strings); - using var sb = ValueStringBuilder.Create(256); + var sb = new StringBuilder(); if (strings.Length > 0) { @@ -728,7 +728,7 @@ namespace Server.Misc sb.ToString() ); - sb.Reset(); + sb = new StringBuilder(); sb.Append(v); } else diff --git a/Projects/UOContent/Special Systems/Items/Stones/GamblingStone.cs b/Projects/UOContent/Special Systems/Items/Stones/GamblingStone.cs index c949b0cbc..dc3898e39 100644 --- a/Projects/UOContent/Special Systems/Items/Stones/GamblingStone.cs +++ b/Projects/UOContent/Special Systems/Items/Stones/GamblingStone.cs @@ -82,7 +82,7 @@ public partial class GamblingStone : Item } else { - from.SendMessage(0x22, "You need at least 250gp in your backpack to use this."); + from.SendMessage(0x22, $"You need at least 250gp in your backpack to use this."); } } } diff --git a/Projects/UOContent/Spells/Base/SpecialMove.cs b/Projects/UOContent/Spells/Base/SpecialMove.cs index cd77acb88..c7dc2092e 100644 --- a/Projects/UOContent/Spells/Base/SpecialMove.cs +++ b/Projects/UOContent/Spells/Base/SpecialMove.cs @@ -153,7 +153,7 @@ namespace Server.Spells return false; } - var option = this switch + string option = this switch { Backstab => "Backstab", DeathStrike => "Death Strike", diff --git a/Projects/UOContent/Spells/Base/Spell.cs b/Projects/UOContent/Spells/Base/Spell.cs index d3f666530..ec04e9158 100644 --- a/Projects/UOContent/Spells/Base/Spell.cs +++ b/Projects/UOContent/Spells/Base/Spell.cs @@ -9,7 +9,6 @@ using Server.Spells.Necromancy; using Server.Spells.Ninjitsu; using Server.Spells.Second; using Server.Spells.Spellweaving; -using Server.Systems.FeatureFlags; using Server.Targeting; namespace Server.Spells @@ -164,7 +163,7 @@ namespace Server.Spells { _contextTable[type] = context = new DelayedDamageContextWrapper(); - for (var i = 0; i < damageStacking.Length; i++) + for (int i = 0; i < damageStacking.Length; i++) { _contextTable.Add(damageStacking[i], context); } @@ -509,12 +508,6 @@ namespace Server.Spells else if ((Caster as PlayerMobile)?.DuelContext?.AllowSpellCast(Caster, this) == false) { } - else if (Caster is PlayerMobile { AccessLevel: < AccessLevel.Administrator } && - FeatureFlagManager.IsSpellBlocked(GetType())) - { - var entry = FeatureFlagManager.GetSpellBlockEntry(GetType()); - Caster.SendMessage(0x22, entry?.Reason ?? "This spell is temporarily disabled."); - } else { var requiredMana = ScaleMana(GetMana()); @@ -838,21 +831,9 @@ namespace Server.Spells return false; } - if ((Caster as PlayerMobile)?.Young == true && (target as PlayerMobile)?.Young == false) - { - Caster.SendLocalizedMessage(500278); // As a young player, you may not cast beneficial spells onto older players. - return false; - } - if (Caster.CanBeBeneficial(target, true, allowDead) && CheckSequence()) { Caster.DoBeneficial(target); - - if (Caster != target) - { - Titles.AwardKarma(Caster, target.Karma / 5, true); - } - return true; } diff --git a/Projects/UOContent/Spells/Base/SpellHelper.cs b/Projects/UOContent/Spells/Base/SpellHelper.cs index dc7e22f80..396e64fdb 100644 --- a/Projects/UOContent/Spells/Base/SpellHelper.cs +++ b/Projects/UOContent/Spells/Base/SpellHelper.cs @@ -178,26 +178,21 @@ namespace Server.Spells public static void Turn(Mobile from, IPoint3D to) { - if (from == null || to == null || from.Equals(to)) + if (from == null) { return; } - if (to is Item item) + var root = (to as Item)?.RootParent; + if (from != root) { - var root = item.RootParent; - if (root != null) - { - if (from == root) - { - return; - } - - to = root; - } + to = root; } - from.Direction = from.GetDirectionTo(to); + if (!from.Equals(to)) + { + from.Direction = from.GetDirectionTo(to); + } } public static bool CheckCombat(Mobile m) @@ -502,7 +497,7 @@ namespace Server.Spells } return bcTarg?.Controlled == false && bcTarg.InitialInnocent || - Notoriety.Compute(from, to) != Notoriety.Innocent || from.Murderer; + Notoriety.Compute(from, to) != Notoriety.Innocent || from.Kills >= 5; } public static void Summon( @@ -893,25 +888,16 @@ namespace Server.Spells if (target.MagicDamageAbsorb > 0) { - if (!Core.UOR) + ++circle; + + target.MagicDamageAbsorb -= circle; + + // This order isn't very intuitive, but you have to nullify reflect before target gets switched + reflect = target.MagicDamageAbsorb >= 0; + if (target.MagicDamageAbsorb <= 0) { - // T2A: single-use reflection, consumed immediately target.MagicDamageAbsorb = 0; - reflect = true; - } - else - { - ++circle; - - target.MagicDamageAbsorb -= circle; - - // This order isn't very intuitive, but you have to nullify reflect before target gets switched - reflect = target.MagicDamageAbsorb >= 0; - if (target.MagicDamageAbsorb <= 0) - { - target.MagicDamageAbsorb = 0; - DefensiveSpell.Nullify(target); - } + DefensiveSpell.Nullify(target); } } @@ -1026,7 +1012,7 @@ namespace Server.Spells bcTarget?.AlterSpellDamageFrom(from, ref dmg); - if (Feint.GetDamageReduction(from, target, out var feintReduction)) + if (Feint.GetDamageReduction(from, target, out int feintReduction)) { // example: 35 damage * 50 / 100 = 17 damage dmg -= dmg * feintReduction / 100; diff --git a/Projects/UOContent/Spells/Base/SpellRegistry.cs b/Projects/UOContent/Spells/Base/SpellRegistry.cs index 1f3060d76..3d7847d27 100644 --- a/Projects/UOContent/Spells/Base/SpellRegistry.cs +++ b/Projects/UOContent/Spells/Base/SpellRegistry.cs @@ -66,7 +66,7 @@ namespace Server.Spells public static int GetRegistryNumber(SpecialMove s) => GetRegistryNumber(s.GetType()); - public static int GetRegistryNumber(Type type) => m_IDsFromTypes.GetValueOrDefault(type, -1); + public static int GetRegistryNumber(Type type) => m_IDsFromTypes.TryGetValue(type, out var value) ? value : -1; public static void Register(int spellID, Type type) { diff --git a/Projects/UOContent/Spells/Bushido/Confidence.cs b/Projects/UOContent/Spells/Bushido/Confidence.cs index 2dd21bc0e..fb9757904 100644 --- a/Projects/UOContent/Spells/Bushido/Confidence.cs +++ b/Projects/UOContent/Spells/Bushido/Confidence.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Spells.Bushido; @@ -71,19 +69,13 @@ public class Confidence : SamuraiSpell if (Core.HS) { var bushido = m.Skills.Bushido.Fixed; - (m as PlayerMobile)?.AddBuff( - new BuffInfo( - BuffIcon.Confidence, - 1060596, - 1153809, - TimeSpan.FromSeconds(30), - $"{bushido / 120}\t{bushido / 50}\t{"100"}" - ) - ); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Confidence, 1060596, 1153809, TimeSpan.FromSeconds(30), m, + $"{bushido / 120}\t{bushido / 50}\t{"100"}" + )); } else { - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Confidence, 1060596, TimeSpan.FromSeconds(30))); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Confidence, 1060596, TimeSpan.FromSeconds(30), m)); } } @@ -105,7 +97,7 @@ public class Confidence : SamuraiSpell if (StopConfidenceTimer(m)) { OnEffectEnd(m, typeof(Confidence)); - (m as PlayerMobile)?.RemoveBuff(BuffIcon.Confidence); + BuffInfo.RemoveBuff(m, BuffIcon.Confidence); } } diff --git a/Projects/UOContent/Spells/Bushido/HonorableExecution.cs b/Projects/UOContent/Spells/Bushido/HonorableExecution.cs index c9a3759e1..0cb0cd3e2 100644 --- a/Projects/UOContent/Spells/Bushido/HonorableExecution.cs +++ b/Projects/UOContent/Spells/Bushido/HonorableExecution.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Spells.Bushido; @@ -42,51 +40,59 @@ public class HonorableExecution : SamuraiMove var swingBonus = Math.Max(1, (int)(bushido / 720.0)); - timer = new HonorableExecutionTimer(TimeSpan.FromSeconds(20.0), attacker, swingBonus: swingBonus); + timer = new HonorableExecutionTimer(attacker, swingBonus); - (attacker as PlayerMobile)?.AddBuff( + BuffInfo.AddBuff( + attacker, new BuffInfo( BuffIcon.HonorableExecution, 1060595, 1153807, TimeSpan.FromSeconds(20.0), + attacker, $"{swingBonus}" ) ); } else { - attacker.AddResistanceMod(new ResistanceMod(ResistanceType.Physical, "PhysicalResistHonorableExecution", -40)); - attacker.AddResistanceMod(new ResistanceMod(ResistanceType.Fire, "FireResistHonorableExecution", -40)); - attacker.AddResistanceMod(new ResistanceMod(ResistanceType.Cold, "ColdResistHonorableExecution", -40)); - attacker.AddResistanceMod(new ResistanceMod(ResistanceType.Poison, "PoisonResistHonorableExecution", -40)); - attacker.AddResistanceMod(new ResistanceMod(ResistanceType.Energy, "EnergyResistHonorableExecution", -40)); + var mods = new List + { + new ResistanceMod(ResistanceType.Physical, "PhysicalResistHonorableExecution", -40), + new ResistanceMod(ResistanceType.Fire, "FireResistHonorableExecution", -40), + new ResistanceMod(ResistanceType.Cold, "ColdResistHonorableExecution", -40), + new ResistanceMod(ResistanceType.Poison, "PoisonResistHonorableExecution", -40), + new ResistanceMod(ResistanceType.Energy, "EnergyResistHonorableExecution", -40) + }; var resSpells = attacker.Skills.MagicResist.Value; if (resSpells > 0.0) { - attacker.AddSkillMod(new DefaultSkillMod(SkillName.MagicResist, "MagicResistHonorableExecution", true, -resSpells)); + mods.Add(new DefaultSkillMod(SkillName.MagicResist, "MagicResistHonorableExecution", true, -resSpells)); } - timer = new HonorableExecutionTimer(TimeSpan.FromSeconds(7.0), attacker, penalty: true); + timer = new HonorableExecutionTimer(attacker, mods); if (Core.HS) { - (attacker as PlayerMobile)?.AddBuff( + BuffInfo.AddBuff( + attacker, new BuffInfo( BuffIcon.HonorableExecution, 1060595, 1153808, TimeSpan.FromSeconds(7.0), + attacker, $"{resSpells}\t{40}\t{40}\t{40}\t{40}\t{40}" ) ); } else { - (attacker as PlayerMobile)?.AddBuff( - new BuffInfo(BuffIcon.HonorableExecution, 1060595, TimeSpan.FromSeconds(7.0)) + BuffInfo.AddBuff( + attacker, + new BuffInfo(BuffIcon.HonorableExecution, 1060595, TimeSpan.FromSeconds(7.0), attacker) ); } } @@ -98,9 +104,9 @@ public class HonorableExecution : SamuraiMove CheckGain(attacker); } - public static int GetSwingBonus(Mobile target) => _table.TryGetValue(target, out var info) ? info._swingBonus : 0; + public static int GetSwingBonus(Mobile target) => _table.TryGetValue(target, out var info) ? info.m_SwingBonus : 0; - public static bool IsUnderPenalty(Mobile target) => _table.TryGetValue(target, out var info) && info._penalty; + public static bool IsUnderPenalty(Mobile target) => _table.TryGetValue(target, out var info) && info.m_Penalty; public static void RemovePenalty(Mobile target) { @@ -112,33 +118,81 @@ public class HonorableExecution : SamuraiMove private class HonorableExecutionTimer : Timer { - public readonly Mobile _from; - public readonly bool _penalty; - public readonly int _swingBonus; + public readonly Mobile m_Mobile; + public readonly List m_Mods; + public readonly bool m_Penalty; + public readonly int m_SwingBonus; - public HonorableExecutionTimer(TimeSpan duration, Mobile from, int swingBonus = 0, bool penalty = false) : base(duration) + public HonorableExecutionTimer(Mobile from, List mods) : this(TimeSpan.FromSeconds(7.0), from, 0, mods, mods != null) { - _from = from; - _swingBonus = swingBonus; - _penalty = penalty; + } + + public HonorableExecutionTimer(Mobile from, int swingBonus) : this(TimeSpan.FromSeconds(20.0), from, swingBonus) + { + } + + public HonorableExecutionTimer( + TimeSpan duration, Mobile from, int swingBonus, List mods = null, bool penalty = false + ) : base(duration) + { + m_Mobile = from; + m_SwingBonus = swingBonus; + m_Mods = mods; + m_Penalty = penalty; + + Apply(); } protected override void OnTick() { - _from?.Delta(MobileDelta.WeaponDamage); - RemovePenalty(_from); + m_Mobile?.Delta(MobileDelta.WeaponDamage); + RemovePenalty(m_Mobile); + } + + public void Apply() + { + if (m_Mods == null) + { + return; + } + + for (var i = 0; i < m_Mods.Count; ++i) + { + var mod = m_Mods[i]; + + if (mod is ResistanceMod resistanceMod) + { + m_Mobile.AddResistanceMod(resistanceMod); + } + else if (mod is SkillMod skillMod) + { + m_Mobile.AddSkillMod(skillMod); + } + } } public void Clear() { Stop(); - _from.RemoveResistanceMod("PhysicalResistHonorableExecution"); - _from.RemoveResistanceMod("FireResistHonorableExecution"); - _from.RemoveResistanceMod("ColdResistHonorableExecution"); - _from.RemoveResistanceMod("PoisonResistHonorableExecution"); - _from.RemoveResistanceMod("EnergyResistHonorableExecution"); - _from.RemoveSkillMod("MagicResistHonorableExecution"); + if (m_Mods == null) + { + return; + } + + for (var i = 0; i < m_Mods.Count; ++i) + { + var mod = m_Mods[i]; + + if (mod is ResistanceMod resistanceMod) + { + m_Mobile?.RemoveResistanceMod(resistanceMod); + } + else if (mod is SkillMod skillMod) + { + m_Mobile?.RemoveSkillMod(skillMod); + } + } } } } diff --git a/Projects/UOContent/Spells/Bushido/MomentumStrike.cs b/Projects/UOContent/Spells/Bushido/MomentumStrike.cs index d6164d8e7..9a0883d71 100644 --- a/Projects/UOContent/Spells/Bushido/MomentumStrike.cs +++ b/Projects/UOContent/Spells/Bushido/MomentumStrike.cs @@ -41,7 +41,7 @@ namespace Server.Spells.Bushido return; } - var target = queue.PeekRandom(); + Mobile target = queue.PeekRandom(); var damageBonus = attacker.Skills.Bushido.Value / 100.0; diff --git a/Projects/UOContent/Spells/Chivalry/DivineFury.cs b/Projects/UOContent/Spells/Chivalry/DivineFury.cs index 1429c9da2..c2afcfa36 100644 --- a/Projects/UOContent/Spells/Chivalry/DivineFury.cs +++ b/Projects/UOContent/Spells/Chivalry/DivineFury.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Spells.Chivalry; @@ -54,19 +52,24 @@ public class DivineFurySpell : PaladinSpell if (Core.HS) // Publish 71 with boost { - (Caster as PlayerMobile)?.AddBuff( + BuffInfo.AddBuff( + Caster, new BuffInfo( BuffIcon.DivineFury, 1060589, 1150218, delay, + Caster, $"{timer.AttackBonus}\t{timer.DamageBonus}\t{timer.WeaponSpeed}\t{timer.DefendMalus}" ) ); } else { - (Caster as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.DivineFury, 1060589, 1075634, delay)); + BuffInfo.AddBuff( + Caster, + new BuffInfo(BuffIcon.DivineFury, 1060589, 1075634, delay, Caster) + ); } } @@ -113,7 +116,7 @@ public class DivineFurySpell : PaladinSpell RemoveTimer(m); m.Delta(MobileDelta.WeaponDamage); m.PlaySound(0xF8); - (m as PlayerMobile)?.RemoveBuff(BuffIcon.DivineFury); + BuffInfo.RemoveBuff(m, BuffIcon.DivineFury); } public static bool UnderEffect(Mobile m) => _table.ContainsKey(m); diff --git a/Projects/UOContent/Spells/Chivalry/EnemyOfOne.cs b/Projects/UOContent/Spells/Chivalry/EnemyOfOne.cs index 0956bfc69..5014eab26 100644 --- a/Projects/UOContent/Spells/Chivalry/EnemyOfOne.cs +++ b/Projects/UOContent/Spells/Chivalry/EnemyOfOne.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; using Server.Mobiles; namespace Server.Spells.Chivalry @@ -50,8 +49,9 @@ namespace Server.Spells.Chivalry mobile.EnemyOfOneType = null; mobile.WaitingForEnemy = true; - mobile.AddBuff( - new BuffInfo(BuffIcon.EnemyOfOne, 1075653, 1044111, TimeSpan.FromMinutes(delay)) + BuffInfo.AddBuff( + mobile, + new BuffInfo(BuffIcon.EnemyOfOne, 1075653, 1044111, TimeSpan.FromMinutes(delay), mobile) ); } } diff --git a/Projects/UOContent/Spells/Chivalry/NobleSacrifice.cs b/Projects/UOContent/Spells/Chivalry/NobleSacrifice.cs index c34207646..a63a9fe38 100644 --- a/Projects/UOContent/Spells/Chivalry/NobleSacrifice.cs +++ b/Projects/UOContent/Spells/Chivalry/NobleSacrifice.cs @@ -1,6 +1,5 @@ using System; using Server.Collections; -using Server.Engines.BuffIcons; using Server.Gumps; using Server.Items; using Server.Mobiles; @@ -81,7 +80,7 @@ namespace Server.Spells.Chivalry } else { - var sendEffect = false; + bool sendEffect = false; if (m.Poisoned && m.CurePoison(Caster)) { @@ -143,17 +142,14 @@ namespace Server.Spells.Chivalry sendEffect = SpellPlagueSpell.RemoveEffect(m) || sendEffect; // TODO: Move these into their respective end effect methods - if (m is PlayerMobile pm) - { - pm.RemoveBuff(BuffIcon.Clumsy); - pm.RemoveBuff(BuffIcon.FeebleMind); - pm.RemoveBuff(BuffIcon.Weaken); - pm.RemoveBuff(BuffIcon.Curse); - pm.RemoveBuff(BuffIcon.MassCurse); - pm.RemoveBuff(BuffIcon.MortalStrike); - pm.RemoveBuff(BuffIcon.Strangle); - pm.RemoveBuff(BuffIcon.EvilOmen); - } + BuffInfo.RemoveBuff(m, BuffIcon.Clumsy); + BuffInfo.RemoveBuff(m, BuffIcon.FeebleMind); + BuffInfo.RemoveBuff(m, BuffIcon.Weaken); + BuffInfo.RemoveBuff(m, BuffIcon.Curse); + BuffInfo.RemoveBuff(m, BuffIcon.MassCurse); + BuffInfo.RemoveBuff(m, BuffIcon.MortalStrike); + BuffInfo.RemoveBuff(m, BuffIcon.Strangle); + BuffInfo.RemoveBuff(m, BuffIcon.EvilOmen); if (sendEffect) { diff --git a/Projects/UOContent/Spells/Chivalry/RemoveCurse.cs b/Projects/UOContent/Spells/Chivalry/RemoveCurse.cs index 0913c0a27..6f89d8562 100644 --- a/Projects/UOContent/Spells/Chivalry/RemoveCurse.cs +++ b/Projects/UOContent/Spells/Chivalry/RemoveCurse.cs @@ -1,8 +1,6 @@ using System; -using Server.Engines.BuffIcons; using Server.Engines.ConPVP; using Server.Items; -using Server.Mobiles; using Server.Spells.Fourth; using Server.Spells.Mysticism; using Server.Spells.Necromancy; @@ -49,7 +47,7 @@ namespace Server.Spells.Chivalry * Chance of removing curse is affected by Caster's Karma. */ - var chance = Caster.Karma switch + int chance = Caster.Karma switch { < -5000 => 0, < 0 => (int)Math.Sqrt(20000 + Caster.Karma) - 122, @@ -111,17 +109,15 @@ namespace Server.Spells.Chivalry BloodOathSpell.RemoveCurse(m); SpellPlagueSpell.RemoveEffect(m); - if (m is PlayerMobile pm) - { - pm.RemoveBuff(BuffIcon.Clumsy); - pm.RemoveBuff(BuffIcon.FeebleMind); - pm.RemoveBuff(BuffIcon.Weaken); - pm.RemoveBuff(BuffIcon.Curse); - pm.RemoveBuff(BuffIcon.MassCurse); - pm.RemoveBuff(BuffIcon.MortalStrike); - pm.RemoveBuff(BuffIcon.Strangle); - pm.RemoveBuff(BuffIcon.EvilOmen); - } + // TODO: Move these into their respective end effect methods + BuffInfo.RemoveBuff(m, BuffIcon.Clumsy); + BuffInfo.RemoveBuff(m, BuffIcon.FeebleMind); + BuffInfo.RemoveBuff(m, BuffIcon.Weaken); + BuffInfo.RemoveBuff(m, BuffIcon.Curse); + BuffInfo.RemoveBuff(m, BuffIcon.MassCurse); + BuffInfo.RemoveBuff(m, BuffIcon.MortalStrike); + BuffInfo.RemoveBuff(m, BuffIcon.Strangle); + BuffInfo.RemoveBuff(m, BuffIcon.EvilOmen); } else { diff --git a/Projects/UOContent/Spells/Chivalry/SacredJourney.cs b/Projects/UOContent/Spells/Chivalry/SacredJourney.cs index 396321dac..38dd6ff2c 100644 --- a/Projects/UOContent/Spells/Chivalry/SacredJourney.cs +++ b/Projects/UOContent/Spells/Chivalry/SacredJourney.cs @@ -60,7 +60,7 @@ namespace Server.Spells.Chivalry { mobile.SendLocalizedMessage(1049543); // You decide against traveling to Felucca while you are still young. } - else if (Caster.Murderer && map != Map.Felucca) + else if (Caster.Kills >= 5 && map != Map.Felucca) { Caster.SendLocalizedMessage(1019004); // You are not allowed to travel there. } diff --git a/Projects/UOContent/Spells/Eighth/AirElemental.cs b/Projects/UOContent/Spells/Eighth/AirElemental.cs index 58cea35a9..e88c35fff 100644 --- a/Projects/UOContent/Spells/Eighth/AirElemental.cs +++ b/Projects/UOContent/Spells/Eighth/AirElemental.cs @@ -49,7 +49,14 @@ namespace Server.Spells.Eighth _ => TimeSpan.FromSeconds(4 * Math.Max(5, Caster.Skills.Magery.Value)), }; - SpellHelper.Summon(new SummonedAirElemental(), Caster, 0x217, duration, false, false); + if (Core.AOS) + { + SpellHelper.Summon(new SummonedAirElemental(), Caster, 0x217, duration, false, false); + } + else + { + SpellHelper.Summon(new AirElemental(), Caster, 0x217, duration, false, false); + } } FinishSequence(); diff --git a/Projects/UOContent/Spells/Eighth/EarthElemental.cs b/Projects/UOContent/Spells/Eighth/EarthElemental.cs index 662a04d29..6298728b5 100644 --- a/Projects/UOContent/Spells/Eighth/EarthElemental.cs +++ b/Projects/UOContent/Spells/Eighth/EarthElemental.cs @@ -49,7 +49,14 @@ namespace Server.Spells.Eighth _ => TimeSpan.FromSeconds(4 * Math.Max(5, Caster.Skills.Magery.Value)), }; - SpellHelper.Summon(new SummonedEarthElemental(), Caster, 0x217, duration, false, false); + if (Core.AOS) + { + SpellHelper.Summon(new SummonedEarthElemental(), Caster, 0x217, duration, false, false); + } + else + { + SpellHelper.Summon(new EarthElemental(), Caster, 0x217, duration, false, false); + } } FinishSequence(); diff --git a/Projects/UOContent/Spells/Eighth/FireElemental.cs b/Projects/UOContent/Spells/Eighth/FireElemental.cs index b94d7fcab..718c547a7 100644 --- a/Projects/UOContent/Spells/Eighth/FireElemental.cs +++ b/Projects/UOContent/Spells/Eighth/FireElemental.cs @@ -50,7 +50,14 @@ namespace Server.Spells.Eighth _ => TimeSpan.FromSeconds(4 * Math.Max(5, Caster.Skills.Magery.Value)), }; - SpellHelper.Summon(new SummonedFireElemental(), Caster, 0x217, duration, false, false); + if (Core.AOS) + { + SpellHelper.Summon(new SummonedFireElemental(), Caster, 0x217, duration, false, false); + } + else + { + SpellHelper.Summon(new FireElemental(), Caster, 0x217, duration, false, false); + } } FinishSequence(); diff --git a/Projects/UOContent/Spells/Eighth/Resurrection.cs b/Projects/UOContent/Spells/Eighth/Resurrection.cs index 689ddc3d5..f4c6b686e 100644 --- a/Projects/UOContent/Spells/Eighth/Resurrection.cs +++ b/Projects/UOContent/Spells/Eighth/Resurrection.cs @@ -1,7 +1,6 @@ using Server.Engines.ConPVP; using Server.Gumps; using Server.Targeting; -using Server.Mobiles; namespace Server.Spells.Eighth { @@ -31,22 +30,22 @@ namespace Server.Spells.Eighth { Caster.SendLocalizedMessage(501039); // Thou can not resurrect thyself. } - else if (m is BaseCreature { IsDeadBondedPet: true }) + else if (!Caster.Alive) { - Caster.SendMessage("Target can not be revived this way."); - } - else if (!m.Player) - { - Caster.SendLocalizedMessage(501043); // Target is not a being. + Caster.SendLocalizedMessage(501040); // The resurrecter must be alive. } else if (m.Alive) { Caster.SendLocalizedMessage(501041); // Target is not dead. } - else if (!Caster.InRange(m, TargetRange)) + else if (!Caster.InRange(m, 1)) { Caster.SendLocalizedMessage(501042); // Target is not close enough. } + else if (!m.Player) + { + Caster.SendLocalizedMessage(501043); // Target is not a being. + } else if (m.Map?.CanFit(m.Location, 16, false, false) != true) { Caster.SendLocalizedMessage(501042); // Target can not be resurrected at that location. diff --git a/Projects/UOContent/Spells/Eighth/SummonDaemon.cs b/Projects/UOContent/Spells/Eighth/SummonDaemon.cs index 022796a11..430114d01 100644 --- a/Projects/UOContent/Spells/Eighth/SummonDaemon.cs +++ b/Projects/UOContent/Spells/Eighth/SummonDaemon.cs @@ -1,5 +1,4 @@ using System; -using Server.Misc; using Server.Mobiles; namespace Server.Spells.Eighth @@ -51,9 +50,16 @@ namespace Server.Spells.Eighth _ => TimeSpan.FromSeconds(4 * Math.Max(5, Caster.Skills.Magery.Value)), }; - SpellHelper.Summon(new SummonedDaemon(), Caster, 0x216, duration, false, false); - - Titles.AwardKarma(Caster, -70, true); + if (Core.AOS) /* Why two diff daemons? TODO: solve this */ + { + BaseCreature m_Daemon = new SummonedDaemon(); + SpellHelper.Summon(m_Daemon, Caster, 0x216, duration, false, false); + m_Daemon.FixedParticles(0x3728, 8, 20, 5042, EffectLayer.Head); + } + else + { + SpellHelper.Summon(new Daemon(), Caster, 0x216, duration, false, false); + } } FinishSequence(); diff --git a/Projects/UOContent/Spells/Eighth/WaterElemental.cs b/Projects/UOContent/Spells/Eighth/WaterElemental.cs index 82d5b99f6..12c6325bc 100644 --- a/Projects/UOContent/Spells/Eighth/WaterElemental.cs +++ b/Projects/UOContent/Spells/Eighth/WaterElemental.cs @@ -49,7 +49,14 @@ namespace Server.Spells.Eighth _ => TimeSpan.FromSeconds(4 * Math.Max(5, Caster.Skills.Magery.Value)), }; - SpellHelper.Summon(new SummonedWaterElemental(), Caster, 0x217, duration, false, false); + if (Core.AOS) + { + SpellHelper.Summon(new SummonedWaterElemental(), Caster, 0x217, duration, false, false); + } + else + { + SpellHelper.Summon(new WaterElemental(), Caster, 0x217, duration, false, false); + } } FinishSequence(); diff --git a/Projects/UOContent/Spells/Fifth/DispelField.cs b/Projects/UOContent/Spells/Fifth/DispelField.cs index 909ea4377..9e805bf10 100644 --- a/Projects/UOContent/Spells/Fifth/DispelField.cs +++ b/Projects/UOContent/Spells/Fifth/DispelField.cs @@ -22,8 +22,6 @@ namespace Server.Spells.Fifth public override SpellCircle Circle => SpellCircle.Fifth; - public int TargetRange => Core.T2A ? 15 : 18; - public void Target(Item item) { if (!item.GetType().IsDefined(typeof(DispellableFieldAttribute), false)) diff --git a/Projects/UOContent/Spells/Fifth/Incognito.cs b/Projects/UOContent/Spells/Fifth/Incognito.cs index 77d50c703..a1092a307 100644 --- a/Projects/UOContent/Spells/Fifth/Incognito.cs +++ b/Projects/UOContent/Spells/Fifth/Incognito.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; -using Server.Engines.BuffIcons; using Server.Factions; using Server.Items; using Server.Mobiles; @@ -115,7 +114,7 @@ namespace Server.Spells.Fifth Timer.StartTimer(duration, () => EndIncognito(Caster), out var timerToken); _table[Caster] = timerToken; - pm?.AddBuff(new BuffInfo(BuffIcon.Incognito, 1075819, duration)); + BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.Incognito, 1075819, duration, Caster)); } else { @@ -140,7 +139,7 @@ namespace Server.Spells.Fifth timerToken.Cancel(); } - (m as PlayerMobile)?.RemoveBuff(BuffIcon.Incognito); + BuffInfo.RemoveBuff(m, BuffIcon.Incognito); } public static void EndIncognito(Mobile m) diff --git a/Projects/UOContent/Spells/Fifth/MagicReflect.cs b/Projects/UOContent/Spells/Fifth/MagicReflect.cs index 576094e88..d6a85e91c 100644 --- a/Projects/UOContent/Spells/Fifth/MagicReflect.cs +++ b/Projects/UOContent/Spells/Fifth/MagicReflect.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; -using Server.Engines.BuffIcons; using Server.Mobiles; namespace Server.Spells.Fifth @@ -38,11 +37,6 @@ namespace Server.Spells.Fifth return false; } - if (!Core.UOR) - { - return true; - } - if (!Caster.CanBeginAction()) { Caster.SendLocalizedMessage(1005385); // The spell will not adhere to you at this time. @@ -76,7 +70,7 @@ namespace Server.Spells.Fifth Caster.RemoveResistanceMod(mods[i]); } - (Caster as PlayerMobile)?.RemoveBuff(BuffIcon.MagicReflection); + BuffInfo.RemoveBuff(Caster, BuffIcon.MagicReflection); } else { @@ -104,26 +98,10 @@ namespace Server.Spells.Fifth var buffFormat = $"{physiMod}\t+{otherMod}\t+{otherMod}\t+{otherMod}\t+{otherMod}"; - (Caster as PlayerMobile)?.AddBuff( - new BuffInfo(BuffIcon.MagicReflection, 1075817, args: buffFormat, retainThroughDeath: true) - ); + BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.MagicReflection, 1075817, buffFormat, true)); } } } - else if (!Core.UOR) - { - if (Caster.MagicDamageAbsorb > 0) - { - Caster.SendLocalizedMessage(1005559); // This spell is already in effect. - } - else if (CheckSequence()) - { - Caster.MagicDamageAbsorb = 1; - - Caster.FixedParticles(0x375A, 10, 15, 5037, EffectLayer.Waist); - Caster.PlaySound(0x1E9); - } - } else { if (Caster.MagicDamageAbsorb > 0) @@ -169,7 +147,7 @@ namespace Server.Spells.Fifth m.RemoveResistanceMod(mods[i]); } - (m as PlayerMobile)?.RemoveBuff(BuffIcon.MagicReflection); + BuffInfo.RemoveBuff(m, BuffIcon.MagicReflection); } } } diff --git a/Projects/UOContent/Spells/Fifth/Paralyze.cs b/Projects/UOContent/Spells/Fifth/Paralyze.cs index 9140d55e9..7f3a8a0de 100644 --- a/Projects/UOContent/Spells/Fifth/Paralyze.cs +++ b/Projects/UOContent/Spells/Fifth/Paralyze.cs @@ -39,7 +39,7 @@ namespace Server.Spells.Fifth if (Core.AOS) { - var secs = GetDamageSkill(Caster) / 10 - GetResistSkill(m) / 10; + var secs = (GetDamageSkill(Caster) / 10 - GetResistSkill(m) / 10); if (!Core.AOS) { diff --git a/Projects/UOContent/Spells/Fifth/PoisonField.cs b/Projects/UOContent/Spells/Fifth/PoisonField.cs index 92a797f19..7f38c677f 100644 --- a/Projects/UOContent/Spells/Fifth/PoisonField.cs +++ b/Projects/UOContent/Spells/Fifth/PoisonField.cs @@ -26,8 +26,6 @@ public class PoisonFieldSpell : MagerySpell, ITargetingSpell public override SpellCircle Circle => SpellCircle.Fifth; - public int TargetRange => Core.T2A ? 15 : 18; - public void Target(IPoint3D p) { if (SpellHelper.CheckTown(p, Caster) && CheckSequence()) @@ -64,19 +62,13 @@ public class PoisonFieldSpell : MagerySpell, ITargetingSpell } [DispellableField] -[SerializationGenerator(1, false)] +[SerializationGenerator(0, false)] public partial class PoisonField : Item { - private void MigrateFrom(V0Content content) - { - _caster = content.Caster; - _end = content.End; - } - [SerializableField(0)] private Mobile _caster; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(1)] private DateTime _end; diff --git a/Projects/UOContent/Spells/First/Clumsy.cs b/Projects/UOContent/Spells/First/Clumsy.cs index 539473bb3..5d942991b 100644 --- a/Projects/UOContent/Spells/First/Clumsy.cs +++ b/Projects/UOContent/Spells/First/Clumsy.cs @@ -1,5 +1,3 @@ -using Server.Engines.BuffIcons; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.First @@ -41,7 +39,7 @@ namespace Server.Spells.First var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, true) * 100); - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Clumsy, 1075831, length, percentage.ToString())); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Clumsy, 1075831, length, m, percentage.ToString())); HarmfulSpell(m); } diff --git a/Projects/UOContent/Spells/First/Feeblemind.cs b/Projects/UOContent/Spells/First/Feeblemind.cs index 93cbb32da..e762d5684 100644 --- a/Projects/UOContent/Spells/First/Feeblemind.cs +++ b/Projects/UOContent/Spells/First/Feeblemind.cs @@ -1,5 +1,3 @@ -using Server.Engines.BuffIcons; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.First @@ -43,7 +41,7 @@ namespace Server.Spells.First var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, true) * 100); - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.FeebleMind, 1075833, length, percentage.ToString())); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.FeebleMind, 1075833, length, m, percentage.ToString())); HarmfulSpell(m); } diff --git a/Projects/UOContent/Spells/First/MagicArrow.cs b/Projects/UOContent/Spells/First/MagicArrow.cs index ad22a5d10..7816d8805 100644 --- a/Projects/UOContent/Spells/First/MagicArrow.cs +++ b/Projects/UOContent/Spells/First/MagicArrow.cs @@ -59,7 +59,7 @@ namespace Server.Spells.First damage *= GetDamageScalar(m); } - source.MovingParticles(m, 0x36E4, 5, 0, false, false, 0, 0, 3006, 0, 0, EffectLayer.RightHand, 0); + source.MovingParticles(m, 0x36E4, 5, 0, false, false, 3006, 0, 0); source.PlaySound(0x1E5); SpellHelper.Damage(this, m, damage, 0, 100, 0, 0, 0); diff --git a/Projects/UOContent/Spells/First/NightSight.cs b/Projects/UOContent/Spells/First/NightSight.cs index 01aeb2af1..6c23a13f7 100644 --- a/Projects/UOContent/Spells/First/NightSight.cs +++ b/Projects/UOContent/Spells/First/NightSight.cs @@ -1,6 +1,4 @@ using System; -using Server.Engines.BuffIcons; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.First; @@ -48,7 +46,7 @@ public class NightSightSpell : MagerySpell, ITargetingSpell m.PlaySound(0x1E3); // Night Sight/You ignore lighting effects - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.NightSight, 1075643)); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.NightSight, 1075643)); } else if (m == Caster) { diff --git a/Projects/UOContent/Spells/First/ReactiveArmor.cs b/Projects/UOContent/Spells/First/ReactiveArmor.cs index 6f88490db..887f1228a 100644 --- a/Projects/UOContent/Spells/First/ReactiveArmor.cs +++ b/Projects/UOContent/Spells/First/ReactiveArmor.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; -using Server.Engines.BuffIcons; using Server.Mobiles; -using Server.Targeting; namespace Server.Spells.First { - public class ReactiveArmorSpell : MagerySpell, ITargetingSpell + public class ReactiveArmorSpell : MagerySpell { private static readonly SpellInfo _info = new( "Reactive Armor", @@ -19,8 +17,7 @@ namespace Server.Spells.First Reagent.SulfurousAsh ); - private static Dictionary _table; - private static Dictionary _t2aTable; + private static readonly Dictionary _table = new(); public ReactiveArmorSpell(Mobile caster, Item scroll = null) : base(caster, scroll, _info) { @@ -28,81 +25,9 @@ namespace Server.Spells.First public override SpellCircle Circle => SpellCircle.First; - public static bool HasEffect(Mobile m) => _t2aTable?.ContainsKey(m) == true; - - public static void RemoveEffect(Mobile m) - { - if (_t2aTable?.Remove(m, out var token) == true) - { - token.Cancel(); - } - } - - public static void HandleMeleeHit(Mobile attacker, Mobile defender, ref int damage) - { - if (!Core.UOR) - { - // T2A: percentage-based melee reflection - if (!HasEffect(defender)) - { - return; - } - - // Only reflect adjacent melee hits; guards are exempt - if (!defender.InRange(attacker, 1) || attacker is BaseGuard) - { - return; - } - - var magery = defender.Skills.Magery.Value; - var reflectPct = (int)(10 + magery / 4); // 10–35% - var reflected = damage * reflectPct / 100; - - if (reflected > 0) - { - damage -= reflected; - - // Sourceless damage — does not trigger attacker's own combat events - attacker.Damage(reflected); - } - - attacker.PlaySound(0x1F1); - attacker.FixedEffect(0x374A, 10, 16); - } - else - { - // UOR: damage absorption pool - var absorb = defender.MeleeDamageAbsorb; - - if (absorb <= 0) - { - return; - } - - if (absorb > damage) - { - var react = Math.Max(damage / 5, 1); - - defender.MeleeDamageAbsorb -= damage; - damage = 0; - - attacker.Damage(react, defender); - - attacker.PlaySound(0x1F1); - attacker.FixedEffect(0x374A, 10, 16); - } - else - { - defender.MeleeDamageAbsorb = 0; - defender.SendLocalizedMessage(1005556); // Your reactive armor spell has been nullified. - DefensiveSpell.Nullify(defender); - } - } - } - public override bool CheckCast() { - if (Core.AOS || !Core.UOR) + if (Core.AOS) { return true; } @@ -122,38 +47,6 @@ namespace Server.Spells.First return true; } - public void Target(Mobile m) - { - if (!Caster.CanBeBeneficial(m)) - { - return; - } - - if (HasEffect(m)) - { - // This target already has Reactive Armor - Caster.SendLocalizedMessage(1005559); // This spell is already in effect. - return; - } - - if (CheckBSequence(m)) - { - Caster.DoBeneficial(m); - SpellHelper.Turn(Caster, m); - - m.FixedParticles(0x376A, 9, 32, 5008, EffectLayer.Waist); - m.PlaySound(0x1F2); - - var duration = TimeSpan.FromSeconds(25 + Caster.Skills.Magery.Value / 2); // 25–75s - - Timer.StartTimer(duration, () => ExpireT2AEffect(m), out var token); - _t2aTable ??= []; - _t2aTable[m] = token; - } - } - - private static void ExpireT2AEffect(Mobile m) => _t2aTable?.Remove(m); - public override void OnCast() { if (Core.AOS) @@ -169,7 +62,7 @@ namespace Server.Spells.First if (CheckSequence()) { - if (_table?.Remove(Caster, out var mods) == true) + if (_table.Remove(Caster, out var mods)) { Caster.PlaySound(0x1ED); Caster.FixedParticles(0x376A, 9, 32, 5008, EffectLayer.Waist); @@ -179,7 +72,7 @@ namespace Server.Spells.First Caster.RemoveResistanceMod(mods[i]); } - (Caster as PlayerMobile)?.RemoveBuff(BuffIcon.ReactiveArmor); + BuffInfo.RemoveBuff(Caster, BuffIcon.ReactiveArmor); } else { @@ -199,7 +92,6 @@ namespace Server.Spells.First new ResistanceMod(ResistanceType.Energy, "EnergyResistReactiveArmorSpell", -5) ]; - _table ??= []; _table[Caster] = mods; for (var i = 0; i < mods.Length; ++i) @@ -207,18 +99,15 @@ namespace Server.Spells.First Caster.AddResistanceMod(mods[i]); } - var args = $"{15 + (int)(Caster.Skills.Inscribe.Value / 20)}\t{5}\t{5}\t{5}\t{5}"; + var physresist = 15 + (int)(Caster.Skills.Inscribe.Value / 20); + var args = $"{physresist}\t{5}\t{5}\t{5}\t{5}"; - (Caster as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.ReactiveArmor, 1075812, 1075813, args: args)); + BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.ReactiveArmor, 1075812, 1075813, args)); } } FinishSequence(); } - else if (!Core.UOR) - { - Caster.Target = new SpellTarget(this, TargetFlags.Beneficial); - } else { if (Caster.MeleeDamageAbsorb > 0) @@ -258,9 +147,7 @@ namespace Server.Spells.First [OnEvent(nameof(PlayerMobile.PlayerDeletedEvent))] public static void EndArmor(Mobile m) { - RemoveEffect(m); - - if (_table?.Remove(m, out var mods) != true) + if (!_table.Remove(m, out var mods)) { return; } @@ -270,7 +157,7 @@ namespace Server.Spells.First m.RemoveResistanceMod(mods[i]); } - (m as PlayerMobile)?.RemoveBuff(BuffIcon.ReactiveArmor); + BuffInfo.RemoveBuff(m, BuffIcon.ReactiveArmor); } } } diff --git a/Projects/UOContent/Spells/First/Weaken.cs b/Projects/UOContent/Spells/First/Weaken.cs index eee9f5036..082027ad0 100644 --- a/Projects/UOContent/Spells/First/Weaken.cs +++ b/Projects/UOContent/Spells/First/Weaken.cs @@ -1,5 +1,3 @@ -using Server.Engines.BuffIcons; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.First @@ -41,7 +39,7 @@ namespace Server.Spells.First var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, true) * 100); - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Weaken, 1075837, length, percentage.ToString())); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Weaken, 1075837, length, m, percentage.ToString())); HarmfulSpell(m); } diff --git a/Projects/UOContent/Spells/Fourth/ArchCure.cs b/Projects/UOContent/Spells/Fourth/ArchCure.cs index aca85af40..ed5307df3 100644 --- a/Projects/UOContent/Spells/Fourth/ArchCure.cs +++ b/Projects/UOContent/Spells/Fourth/ArchCure.cs @@ -70,8 +70,8 @@ namespace Server.Spells.Fourth if (poison != null) { - var poisonLevel = Poison.IncreaseLevel(poison).Level; - var chanceToCure = 10000 + (int)(Caster.Skills.Magery.Value * 75) - poisonLevel * 1750; + var chanceToCure = 10000 + (int)(Caster.Skills.Magery.Value * 75) - + (poison.Level + 1) * 1750; chanceToCure /= 100; chanceToCure -= 1; diff --git a/Projects/UOContent/Spells/Fourth/ArchProtection.cs b/Projects/UOContent/Spells/Fourth/ArchProtection.cs index cb44a7b25..18e2ab58b 100644 --- a/Projects/UOContent/Spells/Fourth/ArchProtection.cs +++ b/Projects/UOContent/Spells/Fourth/ArchProtection.cs @@ -69,19 +69,6 @@ namespace Server.Spells.Fourth } } } - else if (!Core.UOR) - { - while (targets.Count > 0) - { - var m = targets.Dequeue(); - if (!ProtectionSpell.HasT2AProtection(m)) - { - Caster.DoBeneficial(m); - ProtectionSpell.ApplyT2AProtection(Caster, m); - m.PlaySound(0x1F7); - } - } - } else { var val = (int)(Caster.Skills.Magery.Value / 10.0 + 1); diff --git a/Projects/UOContent/Spells/Fourth/Curse.cs b/Projects/UOContent/Spells/Fourth/Curse.cs index b85337e96..0f01b0a3c 100644 --- a/Projects/UOContent/Spells/Fourth/Curse.cs +++ b/Projects/UOContent/Spells/Fourth/Curse.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Fourth @@ -54,7 +52,7 @@ namespace Server.Spells.Fourth var oldDexOffset = SpellHelper.GetCurse(caster, m, StatType.Dex); var oldIntOffset = SpellHelper.GetCurse(caster, m, StatType.Int); - if (oldStrOffset > newStrOffset && oldDexOffset > newDexOffset && oldIntOffset > newIntOffset) + if (oldStrOffset <= newStrOffset && oldDexOffset <= newDexOffset && oldIntOffset <= newIntOffset) { return false; } @@ -82,7 +80,7 @@ namespace Server.Spells.Fourth var percentage = (int)(SpellHelper.GetOffsetScalar(caster, m, true) * 100); var args = $"{percentage}\t{percentage}\t{percentage}\t{10}\t{10}\t{10}\t{10}"; - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Curse, 1075835, 1075836, duration, args)); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Curse, 1075835, 1075836, duration, m, args)); return true; } diff --git a/Projects/UOContent/Spells/Fourth/FireField.cs b/Projects/UOContent/Spells/Fourth/FireField.cs index 8f20f995b..004a43b57 100644 --- a/Projects/UOContent/Spells/Fourth/FireField.cs +++ b/Projects/UOContent/Spells/Fourth/FireField.cs @@ -26,8 +26,6 @@ public class FireFieldSpell : MagerySpell, ITargetingSpell public override SpellCircle Circle => SpellCircle.Fourth; - public int TargetRange => Core.T2A ? 15 : 18; - public void Target(IPoint3D p) { if (SpellHelper.CheckTown(p, Caster) && CheckSequence()) @@ -67,23 +65,16 @@ public class FireFieldSpell : MagerySpell, ITargetingSpell } [DispellableField] -[SerializationGenerator(1, false)] +[SerializationGenerator(0, false)] public partial class FireFieldItem : Item { - private void MigrateFrom(V0Content content) - { - _damage = content.Damage; - _caster = content.Caster; - _end = content.End; - } - [SerializableField(0)] private int _damage; [SerializableField(1)] private Mobile _caster; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(2)] private DateTime _end; private Timer _timer; diff --git a/Projects/UOContent/Spells/Fourth/Recall.cs b/Projects/UOContent/Spells/Fourth/Recall.cs index fd40bcfee..5bd59340a 100644 --- a/Projects/UOContent/Spells/Fourth/Recall.cs +++ b/Projects/UOContent/Spells/Fourth/Recall.cs @@ -60,7 +60,7 @@ namespace Server.Spells.Fourth { mobile.SendLocalizedMessage(1049543); // You decide against traveling to Felucca while you are still young. } - else if (Caster.Murderer && map != Map.Felucca) + else if (Caster.Kills >= 5 && map != Map.Felucca) { Caster.SendLocalizedMessage(1019004); // You are not allowed to travel there. } diff --git a/Projects/UOContent/Spells/Gargoyle/SpellDefinitions/FlySpell.cs b/Projects/UOContent/Spells/Gargoyle/SpellDefinitions/FlySpell.cs index db7106524..6339be747 100644 --- a/Projects/UOContent/Spells/Gargoyle/SpellDefinitions/FlySpell.cs +++ b/Projects/UOContent/Spells/Gargoyle/SpellDefinitions/FlySpell.cs @@ -1,6 +1,4 @@ using System; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Spells { @@ -57,10 +55,11 @@ namespace Server.Spells public override void OnCast() { Caster.Flying = false; + BuffInfo.RemoveBuff(Caster, BuffIcon.Fly); Caster.Animate(60, 10, 1, true, false, 0); Caster.SendLocalizedMessage(1112567); // You are flying. Caster.Flying = true; - (Caster as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Fly, 1112567)); + BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.Fly, 1112567)); FinishSequence(); } } diff --git a/Projects/UOContent/Spells/Mysticism/BombardSpell.cs b/Projects/UOContent/Spells/Mysticism/BombardSpell.cs index baaea9c0c..1847ac421 100644 --- a/Projects/UOContent/Spells/Mysticism/BombardSpell.cs +++ b/Projects/UOContent/Spells/Mysticism/BombardSpell.cs @@ -67,7 +67,7 @@ public class BombardSpell : MysticSpell, ITargetingSpell var damageSkill = GetDamageSkill(Caster); var resist = GetResistSkill(m); - var secs = Math.Max(0, (int)(damageSkill / 10 - resist / 10)); + int secs = Math.Max(0, (int)(damageSkill / 10 - resist / 10)); if (secs > 0) { diff --git a/Projects/UOContent/Spells/Mysticism/CleansingWindsSpell.cs b/Projects/UOContent/Spells/Mysticism/CleansingWindsSpell.cs index 744dce453..c871dd9b8 100644 --- a/Projects/UOContent/Spells/Mysticism/CleansingWindsSpell.cs +++ b/Projects/UOContent/Spells/Mysticism/CleansingWindsSpell.cs @@ -4,8 +4,6 @@ using Server.Spells.Fourth; using Server.Spells.Necromancy; using Server.Targeting; using Server.Collections; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Spells.Mysticism; @@ -53,7 +51,7 @@ public class CleansingWindsSpell : MysticSpell, ITargetingSpell var casterParty = Party.Get(Caster); if (casterParty != null) { - foreach (var mob in Caster.Map.GetMobilesInRange(m.Location, 2)) + foreach (Mobile mob in Caster.Map.GetMobilesInRange(m.Location, 2)) { if (mob != m && casterParty.Contains(mob) && Caster.CanBeBeneficial(mob, false)) { @@ -99,7 +97,7 @@ public class CleansingWindsSpell : MysticSpell, ITargetingSpell if (target.Poisoned) { - var poisonLevel = Poison.IncreaseLevel(target.Poison).Level; + var poisonLevel = target.Poison.Level + 1; var chanceToCure = cureChance - poisonLevel * 1750; if (chanceToCure > 10000 || chanceToCure > Utility.Random(10000) && target.CurePoison(Caster)) @@ -200,18 +198,15 @@ public class CleansingWindsSpell : MysticSpell, ITargetingSpell curseLevel += 2; } - if (m is PlayerMobile pm) - { - pm.RemoveBuff(BuffIcon.Clumsy); - pm.RemoveBuff(BuffIcon.FeebleMind); - pm.RemoveBuff(BuffIcon.Weaken); - pm.RemoveBuff(BuffIcon.Curse); - pm.RemoveBuff(BuffIcon.MassCurse); - pm.RemoveBuff(BuffIcon.MortalStrike); - pm.RemoveBuff(BuffIcon.CorpseSkin); - pm.RemoveBuff(BuffIcon.Strangle); - pm.RemoveBuff(BuffIcon.EvilOmen); - } + BuffInfo.RemoveBuff(m, BuffIcon.Clumsy); + BuffInfo.RemoveBuff(m, BuffIcon.FeebleMind); + BuffInfo.RemoveBuff(m, BuffIcon.Weaken); + BuffInfo.RemoveBuff(m, BuffIcon.Curse); + BuffInfo.RemoveBuff(m, BuffIcon.MassCurse); + BuffInfo.RemoveBuff(m, BuffIcon.MortalStrike); + BuffInfo.RemoveBuff(m, BuffIcon.CorpseSkin); + BuffInfo.RemoveBuff(m, BuffIcon.Strangle); + BuffInfo.RemoveBuff(m, BuffIcon.EvilOmen); return curseLevel; } diff --git a/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs b/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs index 266b2f6cc..f1ed5d675 100644 --- a/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs +++ b/Projects/UOContent/Spells/Mysticism/SpellPlagueSpell.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; -using Server.Engines.BuffIcons; using Server.Mobiles; using Server.Targeting; @@ -79,7 +78,7 @@ public class SpellPlagueSpell : MysticSpell, ITargetingSpell if (_table.Remove(m, out var timer)) { timer.Stop(); - (m as PlayerMobile)?.RemoveBuff(BuffIcon.SpellPlague); + BuffInfo.RemoveBuff(m, BuffIcon.SpellPlague); return true; } @@ -130,8 +129,9 @@ public class SpellPlagueSpell : MysticSpell, ITargetingSpell public void StartPlague() { - (_target as PlayerMobile)?.AddBuff( - new BuffInfo(BuffIcon.SpellPlague, 1031690, 1080167, TimeSpan.FromSeconds(8.5)) + BuffInfo.AddBuff( + _target, + new BuffInfo(BuffIcon.SpellPlague, 1031690, 1080167, TimeSpan.FromSeconds(8.5), _target) ); _nextExplosion = Core.Now + TimeSpan.FromSeconds(1); @@ -184,7 +184,7 @@ public class SpellPlagueSpell : MysticSpell, ITargetingSpell else { _table.Remove(_target); - (_target as PlayerMobile)?.RemoveBuff(BuffIcon.SpellPlague); + BuffInfo.RemoveBuff(_target, BuffIcon.SpellPlague); } _next = null; diff --git a/Projects/UOContent/Spells/Mysticism/StoneFormSpell.cs b/Projects/UOContent/Spells/Mysticism/StoneFormSpell.cs index eca4ba1e9..60ad336c5 100644 --- a/Projects/UOContent/Spells/Mysticism/StoneFormSpell.cs +++ b/Projects/UOContent/Spells/Mysticism/StoneFormSpell.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; -using Server.Engines.BuffIcons; using Server.Factions; using Server.Mobiles; using Server.Spells.Fifth; @@ -119,12 +118,14 @@ namespace Server.Spells.Mysticism var damageBonus = (int)((GetBaseSkill(Caster) + GetDamageSkill(Caster)) / 12.0); var resistCap = (int)((GetBaseSkill(Caster) + GetDamageSkill(Caster)) / 48.0); - (Caster as PlayerMobile)?.AddBuff( + BuffInfo.AddBuff( + Caster, new BuffInfo( BuffIcon.StoneForm, 1080145, 1080146, - args: $"-10\t-2\t{offset}\t{resistCap}\t{damageBonus}" + $"-10\t-2\t{offset}\t{resistCap}\t{damageBonus}", + false ) ); } @@ -150,7 +151,7 @@ namespace Server.Spells.Mysticism m.BodyMod = 0; m.HueMod = -1; - (m as PlayerMobile)?.RemoveBuff(BuffIcon.StoneForm); + BuffInfo.RemoveBuff(m, BuffIcon.StoneForm); } } } diff --git a/Projects/UOContent/Spells/Necromancy/AnimateDeadSpell.cs b/Projects/UOContent/Spells/Necromancy/AnimateDeadSpell.cs index 6aceab235..8ebc42ab8 100644 --- a/Projects/UOContent/Spells/Necromancy/AnimateDeadSpell.cs +++ b/Projects/UOContent/Spells/Necromancy/AnimateDeadSpell.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; +using Server.Engines.Quests; using Server.Engines.Quests.Necro; using Server.Items; using Server.Mobiles; @@ -114,9 +115,13 @@ public class AnimateDeadSpell : NecromancerSpell, ITargetingSpell if (comp?.Addon is MaabusCoffin addon) { - if (Caster is PlayerMobile { Quest : DarkTidesQuest quest }) + var pm = Caster as PlayerMobile; + + var qs = pm?.Quest; + + if (qs is DarkTidesQuest) { - var objective = quest.FindObjective(); + QuestObjective objective = qs.FindObjective(); if (objective?.Completed == false) { @@ -134,7 +139,7 @@ public class AnimateDeadSpell : NecromancerSpell, ITargetingSpell } else { - var type = c.Owner?.GetType(); + Type type = c.Owner?.GetType(); if (c.ItemID != 0x2006 || c.Animated || type == typeof(PlayerMobile) || type == null || c.Owner?.Fame < 100 || diff --git a/Projects/UOContent/Spells/Necromancy/BloodOathSpell.cs b/Projects/UOContent/Spells/Necromancy/BloodOathSpell.cs index c0f3c3ed0..bc3903dd3 100644 --- a/Projects/UOContent/Spells/Necromancy/BloodOathSpell.cs +++ b/Projects/UOContent/Spells/Necromancy/BloodOathSpell.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using ModernUO.CodeGeneratedEvents; -using Server.Engines.BuffIcons; using Server.Mobiles; using Server.Targeting; @@ -17,8 +15,7 @@ public class BloodOathSpell : NecromancerSpell, ITargetingSpell Reagent.DaemonBlood ); - // Keyed by BOTH participants (caster and target) -> shared timer, so the oath resolves and - // removes from either side. Required so the death/delete events can break it from either mobile. + private static readonly Dictionary _oathTable = new(); private static readonly Dictionary _table = new(); public BloodOathSpell(Mobile caster, Item scroll = null) : base(caster, scroll, _info) @@ -41,11 +38,11 @@ public class BloodOathSpell : NecromancerSpell, ITargetingSpell { Caster.SendLocalizedMessage(1060508); // You can't curse that. } - else if (_table.ContainsKey(Caster)) + else if (_oathTable.ContainsKey(Caster)) { Caster.SendLocalizedMessage(1061607); // You are already bonded in a Blood Oath. } - else if (_table.ContainsKey(m)) + else if (_oathTable.ContainsKey(m)) { if (m.Player) { @@ -62,12 +59,17 @@ public class BloodOathSpell : NecromancerSpell, ITargetingSpell /* Temporarily creates a dark pact between the caster and the target. * Any damage dealt by the target to the caster is increased, but the target receives the same amount of damage. - * The effect lasts for ((Spirit Speak skill level - target's Resist Magic skill level) / 8) + 8 seconds. + * The effect lasts for ((Spirit Speak skill level - target's Resist Magic skill level) / 80 ) + 8 seconds. * - * NOTE: The in-game tooltip (and UOGuide) display /80 due to a fixed-point bug. - * The actual OSI formula is /8, matching RunUO/ServUO. + * NOTE: The above algorithm must be fixed point, it should be: + * ((ss-rm)/8)+8 */ + RemoveCurse(m); + + _oathTable[Caster] = Caster; + _oathTable[m] = Caster; + m.Spell?.OnCasterHurt(); Caster.PlaySound(0x175); @@ -78,11 +80,16 @@ public class BloodOathSpell : NecromancerSpell, ITargetingSpell m.FixedParticles(0x375A, 1, 17, 9919, 33, 7, EffectLayer.Waist); m.FixedParticles(0x3728, 1, 13, 9502, 33, 7, (EffectLayer)255); - var duration = TimeSpan.FromSeconds(GetDurationSeconds(GetDamageSkill(Caster), GetResistSkill(m))); + var duration = TimeSpan.FromSeconds((GetDamageSkill(Caster) - GetResistSkill(m)) / 80 + 8); m.CheckSkill(SkillName.MagicResist, 0.0, 120.0); // Skill check for gain - RegisterOath(Caster, m, duration); + var timer = new ExpireTimer(Caster, m, duration); + timer.Start(); + BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.BloodOathCaster, 1075659, duration, Caster, m.Name)); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.BloodOathCurse, 1075661, duration, m, Caster.Name)); + + _table[m] = timer; HarmfulSpell(m); } } @@ -92,82 +99,58 @@ public class BloodOathSpell : NecromancerSpell, ITargetingSpell Caster.Target = new SpellTarget(this, TargetFlags.Harmful); } - // ((Spirit Speak - Resisting Spells) / 8) + 8 seconds. - internal static double GetDurationSeconds(double damageSkill, double resistSkill) => - (damageSkill - resistSkill) / 8 + 8; - - // The attacker takes the original (un-bonused) damage reflected back. Publish 48 lets the - // attacker's Resisting Spells reduce the reflected damage, but only against creature casters. - internal static int ComputeReflectedDamage(int originalDamage, double attackerMagicResist, bool applyResistMitigation) + public static bool RemoveCurse(Mobile target) { - if (!applyResistMitigation) - { - return originalDamage; - } - - // ((Resisting Spells * 10) / 20) + 10 = percentage of damage resisted - var resisted = (attackerMagicResist * 0.5 + 10) / 100; - return (int)(originalDamage * (1.0 - resisted)); - } - - internal static void RegisterOath(Mobile caster, Mobile target, TimeSpan duration) - { - var timer = new ExpireTimer(caster, target, duration); - _table[caster] = timer; - _table[target] = timer; - timer.Start(); - - (caster as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.BloodOathCaster, 1075659, duration, target.Name)); - (target as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.BloodOathCurse, 1075661, duration, caster.Name)); - } - - public static bool RemoveCurse(Mobile m) - { - if (m == null || !_table.TryGetValue(m, out var timer)) + if (!_table.Remove(target, out var timer)) { return false; } var caster = timer.Caster; - var target = timer.Target; + if (_oathTable.Remove(caster)) + { + caster.SendLocalizedMessage(1061620); // Your Blood Oath has been broken. + } + + if (_oathTable.Remove(target)) + { + target.SendLocalizedMessage(1061620); // Your Blood Oath has been broken. + } timer.Stop(); - _table.Remove(caster); - _table.Remove(target); - caster.SendLocalizedMessage(1061620); // Your Blood Oath has been broken. - target.SendLocalizedMessage(1061620); // Your Blood Oath has been broken. - - (caster as PlayerMobile)?.RemoveBuff(BuffIcon.BloodOathCaster); - (target as PlayerMobile)?.RemoveBuff(BuffIcon.BloodOathCurse); + BuffInfo.RemoveBuff(caster, BuffIcon.BloodOathCaster); + BuffInfo.RemoveBuff(target, BuffIcon.BloodOathCurse); return true; } public static Mobile GetBloodOath(Mobile m) => - m != null && _table.TryGetValue(m, out var timer) && timer.Target == m ? timer.Caster : null; - - // Death or deletion of either participant breaks the oath immediately. RemoveCurse resolves the - // shared timer from either the caster or the target key, so a single call per mobile is enough. - [OnEvent(nameof(PlayerMobile.PlayerDeathEvent))] - [OnEvent(nameof(PlayerMobile.PlayerDeletedEvent))] - [OnEvent(nameof(BaseCreature.CreatureDeathEvent))] - [OnEvent(nameof(BaseCreature.CreatureDeletedEvent))] - public static void OnCurseEnds(Mobile m) => RemoveCurse(m); + m == null || _oathTable.TryGetValue(m, out var oath) && oath == m ? null : oath; private class ExpireTimer : Timer { - public Mobile Caster { get; } - public Mobile Target { get; } + private readonly Mobile _target; + private readonly DateTime _end; - // Single-shot: fire once when the oath expires. Death or deletion of either party is - // handled separately by the OnCurseEnds event handler. - public ExpireTimer(Mobile caster, Mobile target, TimeSpan delay) : base(delay) + public Mobile Caster { get; } + + public ExpireTimer(Mobile caster, Mobile target, TimeSpan delay) : base( + TimeSpan.FromSeconds(1.0), + TimeSpan.FromSeconds(1.0) + ) { Caster = caster; - Target = target; + _target = target; + _end = Core.Now + delay; } - protected override void OnTick() => RemoveCurse(Target); + protected override void OnTick() + { + if (Caster.Deleted || _target.Deleted || !Caster.Alive || !_target.Alive || Core.Now >= _end) + { + RemoveCurse(_target); + } + } } } diff --git a/Projects/UOContent/Spells/Necromancy/CorpseSkin.cs b/Projects/UOContent/Spells/Necromancy/CorpseSkin.cs index 5f9ad6ba7..74f81eb1c 100644 --- a/Projects/UOContent/Spells/Necromancy/CorpseSkin.cs +++ b/Projects/UOContent/Spells/Necromancy/CorpseSkin.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Necromancy; @@ -82,7 +80,7 @@ public class CorpseSkinSpell : NecromancerSpell, ITargetingSpell timer = new ExpireTimer(m, mods, duration); timer.Start(); - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.CorpseSkin, 1075663, duration)); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.CorpseSkin, 1075663, duration, m)); _table[m] = timer; @@ -131,7 +129,7 @@ public class CorpseSkinSpell : NecromancerSpell, ITargetingSpell } Stop(); - (_mobile as PlayerMobile)?.RemoveBuff(BuffIcon.CorpseSkin); + BuffInfo.RemoveBuff(_mobile, BuffIcon.CorpseSkin); _table.Remove(_mobile); } diff --git a/Projects/UOContent/Spells/Necromancy/EvilOmen.cs b/Projects/UOContent/Spells/Necromancy/EvilOmen.cs index f87db36e4..b515fc736 100644 --- a/Projects/UOContent/Spells/Necromancy/EvilOmen.cs +++ b/Projects/UOContent/Spells/Necromancy/EvilOmen.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; using Server.Mobiles; using Server.Targeting; @@ -65,7 +64,7 @@ public class EvilOmenSpell : NecromancerSpell, ITargetingSpell HarmfulSpell(m); - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.EvilOmen, 1075647, 1075648, duration)); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.EvilOmen, 1075647, 1075648, duration, m)); } } diff --git a/Projects/UOContent/Spells/Necromancy/MindRot.cs b/Projects/UOContent/Spells/Necromancy/MindRot.cs index 8ec5758a8..9d32af258 100644 --- a/Projects/UOContent/Spells/Necromancy/MindRot.cs +++ b/Projects/UOContent/Spells/Necromancy/MindRot.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Necromancy; @@ -74,7 +72,7 @@ public class MindRotSpell : NecromancerSpell, ITargetingSpell { timer.Stop(); m.SendLocalizedMessage(1060872); // Your mind feels normal again. - (m as PlayerMobile)?.RemoveBuff(BuffIcon.Mindrot); + BuffInfo.RemoveBuff(m, BuffIcon.Mindrot); return true; } @@ -103,7 +101,7 @@ public class MindRotSpell : NecromancerSpell, ITargetingSpell timer.Start(); _table[target] = timer; - (target as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Mindrot, 1075665, duration)); + BuffInfo.AddBuff(target, new BuffInfo(BuffIcon.Mindrot, 1075665, duration, target)); target.SendLocalizedMessage(1074384); } } diff --git a/Projects/UOContent/Spells/Necromancy/PainSpike.cs b/Projects/UOContent/Spells/Necromancy/PainSpike.cs index eca3bd275..b3623ce48 100644 --- a/Projects/UOContent/Spells/Necromancy/PainSpike.cs +++ b/Projects/UOContent/Spells/Necromancy/PainSpike.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; using Server.Misc; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Necromancy; @@ -72,7 +70,7 @@ public class PainSpikeSpell : NecromancerSpell, ITargetingSpell buffTime = timer.Next - Core.Now; } - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.PainSpike, 1075667, buffTime, Convert.ToString((int)damage))); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.PainSpike, 1075667, buffTime, m, Convert.ToString((int)damage))); // TODO: Find a better way to do this StaminaSystem.DFA = DFAlgorithm.PainSpike; @@ -111,7 +109,7 @@ public class PainSpikeSpell : NecromancerSpell, ITargetingSpell _mobile.Hits += _toRestore; } - (_mobile as PlayerMobile)?.RemoveBuff(BuffIcon.PainSpike); + BuffInfo.RemoveBuff(_mobile, BuffIcon.PainSpike); } } } diff --git a/Projects/UOContent/Spells/Necromancy/PoisonStrike.cs b/Projects/UOContent/Spells/Necromancy/PoisonStrike.cs index 9b68c83b5..ec358c8b1 100644 --- a/Projects/UOContent/Spells/Necromancy/PoisonStrike.cs +++ b/Projects/UOContent/Spells/Necromancy/PoisonStrike.cs @@ -80,7 +80,7 @@ public class PoisonStrikeSpell : NecromancerSpell, ITargetingSpell var cbc = Caster as BaseCreature; var isMonster = cbc?.Controlled == false && (cbc.IsAnimatedDead || !cbc.Summoned); - foreach (var targ in m.GetMobilesInRange(2)) + foreach (Mobile targ in m.GetMobilesInRange(2)) { if (targ == Caster || m == targ || !SpellHelper.ValidIndirectTarget(Caster, targ) || !Caster.CanBeHarmful(targ, false)) diff --git a/Projects/UOContent/Spells/Necromancy/Strangle.cs b/Projects/UOContent/Spells/Necromancy/Strangle.cs index 1b4a0465d..d887505bb 100644 --- a/Projects/UOContent/Spells/Necromancy/Strangle.cs +++ b/Projects/UOContent/Spells/Necromancy/Strangle.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Necromancy; @@ -63,7 +61,7 @@ public class StrangleSpell : NecromancerSpell, ITargetingSpell m.FixedParticles(0x374A, 1, 17, 9502, 1108, 4, (EffectLayer)255); // According to testing on OSI, it is refreshed. - if (_table.Remove(m, out var timer)) + if (_table.TryGetValue(m, out var timer)) { timer.Stop(); } @@ -73,33 +71,42 @@ public class StrangleSpell : NecromancerSpell, ITargetingSpell timer.Start(); HarmfulSpell(m); - - /* Note: OSI has a bug where the last "tick" is at 0 seconds and never happens. - * Example: 100SS -> 39s, 9 ticks, buff icon disappears with 3s left. - * 5.5, 5, 5, 5, 4, 3.5, 3, 3, 2 -> off. - * - * On ModernUO: 100SS -> 34s, 10 ticks - * We opt for the last tick at 1 second, so the duration is 1 second longer than it needs to be. - */ - - // Calculations for the buff bar - var power = Math.Max(4, Caster.Skills.SpiritSpeak.Value / 10); - - // Closed formula based on loop: - // MaxCount = 4 -> 5 + (4 + 3 + 2 + 1) - // MaxCount > 4 -> 5 + (for each count, Ceiling((1.0 + 5 * count) / power) not to exceed 5) - var totalLength = power % 5 == 0 - ? 3 * power + 5 - : 3 * power + 3; - - var duration = TimeSpan.FromSeconds(totalLength); - - const int minDamage = 4; - var maxDamage = ((int)power + 1) * 3; - var args = $"{minDamage}\t{maxDamage}"; - - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Strangle, 1075794, 1075795, duration, args)); } + + // Calculations for the buff bar + var spiritlevel = Math.Max(4, Caster.Skills.SpiritSpeak.Value / 10); + + const int minDamage = 4; + var maxDamage = ((int)spiritlevel + 1) * 3; + var args = $"{minDamage}\t{maxDamage}"; + + var count = (int)spiritlevel; + var maxCount = count; + var hitDelay = 5; + var length = hitDelay; + + while (count >= 1) + { + --count; + if (hitDelay > 1) + { + if (maxCount < 5) + { + --hitDelay; + } + else + { + var delay = (int)Math.Ceiling((1.0 + 5 * count) / maxCount); + + hitDelay = delay <= 5 ? delay : 5; + } + } + + length += hitDelay; + } + + var t_Duration = TimeSpan.FromSeconds(length); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Strangle, 1075794, 1075795, t_Duration, m, args)); } public override void OnCast() @@ -136,15 +143,15 @@ public class StrangleSpell : NecromancerSpell, ITargetingSpell _target = target; _from = from; - var power = from.Skills.SpiritSpeak.Value / 10; + var spiritLevel = from.Skills.SpiritSpeak.Value / 10; - _minBaseDamage = power - 2; - _maxBaseDamage = power + 1; + _minBaseDamage = spiritLevel - 2; + _maxBaseDamage = spiritLevel + 1; _hitDelay = 5; _nextHit = Core.Now + TimeSpan.FromSeconds(_hitDelay); - _maxCount = _count = Math.Max(4, (int)power); + _maxCount = _count = Math.Max(4, (int)spiritLevel); } protected override void OnTick() @@ -153,21 +160,14 @@ public class StrangleSpell : NecromancerSpell, ITargetingSpell { _table.Remove(_target); Stop(); - return; } - if (Core.Now < _nextHit) + if (!_target.Alive || Core.Now < _nextHit) { return; } - if (--_count == 0) - { - _target.SendLocalizedMessage(1061687); // You can breathe normally again. - _table.Remove(_target); - Stop(); - return; - } + --_count; if (_hitDelay > 1) { @@ -177,32 +177,50 @@ public class StrangleSpell : NecromancerSpell, ITargetingSpell } else { - _hitDelay = (int)Math.Min(Math.Ceiling((1.0 + 5 * _count) / _maxCount), 5); + var delay = (int)Math.Ceiling((1.0 + 5 * _count) / _maxCount); + + if (delay <= 5) + { + _hitDelay = delay; + } + else + { + _hitDelay = 5; + } } } - _nextHit = Core.Now + TimeSpan.FromSeconds(_hitDelay); - - var damage = Utility.RandomMinMax(_minBaseDamage, _maxBaseDamage); - - damage *= 3 - (double)_target.Stam / _target.StamMax * 2; - - if (damage < 1) + if (_count == 0) { - damage = 1; + _target.SendLocalizedMessage(1061687); // You can breath normally again. + _table.Remove(_target); + Stop(); } - - if (!_target.Player) + else { - damage *= 1.75; - } + _nextHit = Core.Now + TimeSpan.FromSeconds(_hitDelay); - AOS.Damage(_target, _from, (int)damage, 0, 0, 0, 100, 0); + var damage = _minBaseDamage + Utility.RandomDouble() * (_maxBaseDamage - _minBaseDamage); - // OSI: randomly revealed between first and third damage tick, guessing 60% chance - if (Utility.RandomDouble() < 0.40) - { - _target.RevealingAction(); + damage *= 3 - (double)_target.Stam / _target.StamMax * 2; + + if (damage < 1) + { + damage = 1; + } + + if (!_target.Player) + { + damage *= 1.75; + } + + AOS.Damage(_target, _from, (int)damage, 0, 0, 0, 100, 0); + + // OSI: randomly revealed between first and third damage tick, guessing 60% chance + if (Utility.RandomDouble() < 0.40) + { + _target.RevealingAction(); + } } } } diff --git a/Projects/UOContent/Spells/Necromancy/SummonFamiliar.cs b/Projects/UOContent/Spells/Necromancy/SummonFamiliar.cs index b9050af35..900d7e8cb 100644 --- a/Projects/UOContent/Spells/Necromancy/SummonFamiliar.cs +++ b/Projects/UOContent/Spells/Necromancy/SummonFamiliar.cs @@ -80,7 +80,7 @@ public class SummonFamiliarSpell : NecromancerSpell public class SummonFamiliarEntry { - public SummonFamiliarEntry(Type type, TextDefinition name, double reqNecromancy, double reqSpiritSpeak) + public SummonFamiliarEntry(Type type, object name, double reqNecromancy, double reqSpiritSpeak) { Type = type; Name = name; @@ -90,14 +90,14 @@ public class SummonFamiliarEntry public Type Type { get; } - public TextDefinition Name { get; } + public object Name { get; } public double ReqNecromancy { get; } public double ReqSpiritSpeak { get; } } -public class SummonFamiliarGump : DynamicGump +public class SummonFamiliarGump : Gump { private const int EnabledColor16 = 0x0F20; private const int DisabledColor16 = 0x262A; @@ -105,8 +105,8 @@ public class SummonFamiliarGump : DynamicGump private const int EnabledColor32 = 0x18CD00; private const int DisabledColor32 = 0x4A8B52; - private readonly Mobile _from; private readonly SummonFamiliarEntry[] _entries; + private readonly Mobile _from; private readonly SummonFamiliarSpell _spell; @@ -117,47 +117,49 @@ public class SummonFamiliarGump : DynamicGump _from = from; _entries = entries; _spell = spell; - } - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); + AddPage(0); - builder.AddBackground(10, 10, 250, 178, 9270); - builder.AddAlphaRegion(20, 20, 230, 158); + AddBackground(10, 10, 250, 178, 9270); + AddAlphaRegion(20, 20, 230, 158); - builder.AddImage(220, 20, 10464); - builder.AddImage(220, 72, 10464); - builder.AddImage(220, 124, 10464); + AddImage(220, 20, 10464); + AddImage(220, 72, 10464); + AddImage(220, 124, 10464); - builder.AddItem(188, 16, 6883); - builder.AddItem(198, 168, 6881); - builder.AddItem(8, 15, 6882); - builder.AddItem(2, 168, 6880); + AddItem(188, 16, 6883); + AddItem(198, 168, 6881); + AddItem(8, 15, 6882); + AddItem(2, 168, 6880); - builder.AddHtmlLocalized(30, 26, 200, 20, 1060147, EnabledColor16); // Chose thy familiar... + AddHtmlLocalized(30, 26, 200, 20, 1060147, EnabledColor16); // Chose thy familiar... - var necro = _from.Skills.Necromancy.Value; - var spirit = _from.Skills.SpiritSpeak.Value; + var necro = from.Skills.Necromancy.Value; + var spirit = from.Skills.SpiritSpeak.Value; - for (var i = 0; i < _entries.Length; ++i) + for (var i = 0; i < entries.Length; ++i) { - var entry = _entries[i]; + var entry = entries[i]; var name = entry.Name; var enabled = necro >= entry.ReqNecromancy && spirit >= entry.ReqSpiritSpeak; - builder.AddButton(27, 53 + i * 21, 9702, 9703, i + 1); + AddButton(27, 53 + i * 21, 9702, 9703, i + 1); - name.AddHtmlText( - ref builder, - 50, - 51 + i * 21, - 150, - 20, - numberColor: enabled ? EnabledColor16 : DisabledColor16, - stringColor: enabled ? EnabledColor32 : DisabledColor32 - ); + if (name is int intName) + { + AddHtmlLocalized(50, 51 + i * 21, 150, 20, intName, enabled ? EnabledColor16 : DisabledColor16); + } + else if (name is string strName) + { + AddHtml( + 50, + 51 + i * 21, + 150, + 20, + strName.Color(enabled ? EnabledColor32 : DisabledColor32) + ); + } } } @@ -188,12 +190,13 @@ public class SummonFamiliarGump : DynamicGump // That familiar requires ~1_NECROMANCY~ Necromancy and ~2_SPIRIT~ Spirit Speak. _from.SendLocalizedMessage(1061606, $"{entry.ReqNecromancy:F1}\t{entry.ReqSpiritSpeak:F1}"); - _from.SendGump(this); + _from.SendGump(new SummonFamiliarGump(_from, SummonFamiliarSpell.Entries, _spell)); } else if (entry.Type == null) { _from.SendMessage("That familiar has not yet been defined."); - _from.SendGump(this); + + _from.SendGump(new SummonFamiliarGump(_from, SummonFamiliarSpell.Entries, _spell)); } else { diff --git a/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs b/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs index 9e924d1a3..abe0847d9 100644 --- a/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs +++ b/Projects/UOContent/Spells/Ninjitsu/AnimalForm.cs @@ -140,25 +140,22 @@ public class AnimalForm : NinjaSpell RemoveContext(Caster, context); Caster.Mana -= mana; } - // On OSI, casting while standing still always opens the selection menu; casting while - // moving quick-transforms into the last selected form without the menu. - else if (Caster is PlayerMobile && !_wasMoving && !CasterIsMoving()) - { - Caster.SendGump(new AnimalFormGump(Caster, Entries, this)); - } else { - var result = Morph(Caster, GetLastAnimalForm(Caster)); - if (result == MorphResult.Success) + var lastAnimalForm = GetLastAnimalForm(Caster); + if (Caster is PlayerMobile && lastAnimalForm == -1 && !_wasMoving && !CasterIsMoving()) + { + Caster.SendGump(new AnimalFormGump(Caster, Entries, this)); + } + else if (Morph(Caster, lastAnimalForm) == MorphResult.Fail) + { + DoFizzle(); + } + else { Caster.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist); Caster.Mana -= mana; } - else if (result == MorphResult.Fail) - { - DoFizzle(); - } - // MorphResult.NoSkill: Morph already messaged the requirement; consume no mana. } } @@ -167,11 +164,6 @@ public class AnimalForm : NinjaSpell public static int GetLastAnimalForm(Mobile m) => _lastAnimalForms.GetValueOrDefault(m, -1); - // Whether the caster currently meets the requirements (Ninjitsu skill + talisman) to select this form. - // Note: ReqSkill is on the 0-100 scale, so this must compare against Skill.Value, not Skill.Fixed (value x10). - public static bool CanSelectEntry(Mobile m, AnimalFormEntry entry) => - m.Skills.Ninjitsu.Value >= entry.ReqSkill && BaseFormTalisman.EntryEnabled(m, entry.Type); - public static MorphResult Morph(Mobile m, int entryID) { if (entryID < 0 || entryID >= Entries.Length) @@ -394,14 +386,15 @@ public class AnimalForm : NinjaSpell builder.AddButton(10, 374, 0xFB1, 0xFB2, 0); builder.AddHtmlLocalized(45, 376, 450, 20, 1011012, 0x7FFF); // CANCEL - var current = 0; + int ninjitsu = _caster.Skills[SkillName.Ninjitsu].Fixed; + int current = 0; - for (var i = 0; i < _entries.Length; ++i) + for (int i = 0; i < _entries.Length; ++i) { - var enabled = CanSelectEntry(_caster, _entries[i]); + bool enabled = ninjitsu >= _entries[i].ReqSkill && BaseFormTalisman.EntryEnabled(_caster, _entries[i].Type); - var page = current / 10 + 1; - var pos = current % 10; + int page = current / 10 + 1; + int pos = current % 10; if (pos == 0) { @@ -425,11 +418,11 @@ public class AnimalForm : NinjaSpell continue; } - var entry = _entries[i]; + AnimalFormEntry entry = _entries[i]; - var y = Math.DivRem(pos, 2, out var rem) * 64 + 44; - var x = rem == 0 ? 14 : 264; - var b = ItemBounds.Bounds[entry.ItemID]; + int y = Math.DivRem(pos, 2, out var rem) * 64 + 44; + int x = rem == 0 ? 14 : 264; + Rectangle2D b = ItemBounds.Table[entry.ItemID]; builder.AddImageTiledButton(x, y, 0x918, 0x919, i + 1, GumpButtonType.Reply, 0, entry.ItemID, entry.Hue, 40 - b.Width / 2 - b.X, 30 - b.Height / 2 - b.Y, entry.Tooltip); @@ -468,21 +461,16 @@ public class AnimalForm : NinjaSpell { _caster.SendLocalizedMessage(1063108); // You cannot use this ability right now. } + else if (Morph(_caster, entryID) == MorphResult.Fail) + { + _caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 502632); // The spell fizzles. + _caster.FixedParticles(0x3735, 1, 30, 9503, EffectLayer.Waist); + _caster.PlaySound(0x5C); + } else { - var result = Morph(_caster, entryID); - if (result == MorphResult.Success) - { - _caster.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist); - _caster.Mana -= mana; - } - else if (result == MorphResult.Fail) - { - _caster.LocalOverheadMessage(MessageType.Regular, 0x3B2, 502632); // The spell fizzles. - _caster.FixedParticles(0x3735, 1, 30, 9503, EffectLayer.Waist); - _caster.PlaySound(0x5C); - } - // MorphResult.NoSkill: Morph already messaged the requirement; consume no mana. + _caster.FixedParticles(0x3728, 10, 13, 2023, EffectLayer.Waist); + _caster.Mana -= mana; } } } diff --git a/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs b/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs index 6d85b9d73..d5a088123 100644 --- a/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs +++ b/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs @@ -234,11 +234,11 @@ namespace Server.Mobiles public override bool Think() { // Clones only follow their owners - var master = Mobile.SummonMaster; + var master = m_Mobile.SummonMaster; - if (master?.Map == Mobile.Map && master?.InRange(Mobile, Mobile.RangePerception) == true) + if (master?.Map == m_Mobile.Map && master?.InRange(m_Mobile, m_Mobile.RangePerception) == true) { - var iCurrDist = (int)Mobile.GetDistanceToSqrt(master); + var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(master); var bRun = iCurrDist > 5; WalkMobileRange(master, 2, bRun, 0, 1); diff --git a/Projects/UOContent/Spells/Second/Agility.cs b/Projects/UOContent/Spells/Second/Agility.cs index ed0c7ef6c..73ef9c554 100644 --- a/Projects/UOContent/Spells/Second/Agility.cs +++ b/Projects/UOContent/Spells/Second/Agility.cs @@ -1,6 +1,4 @@ -using Server.Engines.BuffIcons; using Server.Engines.ConPVP; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Second @@ -36,7 +34,7 @@ namespace Server.Spells.Second var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, false) * 100); - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Agility, 1075841, length, percentage.ToString())); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Agility, 1075841, length, m, percentage.ToString())); } } diff --git a/Projects/UOContent/Spells/Second/Cunning.cs b/Projects/UOContent/Spells/Second/Cunning.cs index fefc40a0e..d44881640 100644 --- a/Projects/UOContent/Spells/Second/Cunning.cs +++ b/Projects/UOContent/Spells/Second/Cunning.cs @@ -1,6 +1,4 @@ -using Server.Engines.BuffIcons; using Server.Engines.ConPVP; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Second @@ -36,7 +34,7 @@ namespace Server.Spells.Second var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, false) * 100); - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Cunning, 1075843, length, percentage.ToString())); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Cunning, 1075843, length, m, percentage.ToString())); } } diff --git a/Projects/UOContent/Spells/Second/Harm.cs b/Projects/UOContent/Spells/Second/Harm.cs index 46eaf9856..e6abad766 100644 --- a/Projects/UOContent/Spells/Second/Harm.cs +++ b/Projects/UOContent/Spells/Second/Harm.cs @@ -49,16 +49,13 @@ namespace Server.Spells.Second damage *= GetDamageScalar(m); } - if (Core.UOTD) + if (!m.InRange(Caster, 2)) { - if (!m.InRange(Caster, 2)) - { - damage *= 0.25; // 1/4 damage at > 2 tile range - } - else if (!m.InRange(Caster, 1)) - { - damage *= 0.50; // 1/2 damage at 2 tile range - } + damage *= 0.25; // 1/4 damage at > 2 tile range + } + else if (!m.InRange(Caster, 1)) + { + damage *= 0.50; // 1/2 damage at 2 tile range } if (Core.AOS) diff --git a/Projects/UOContent/Spells/Second/Protection.cs b/Projects/UOContent/Spells/Second/Protection.cs index dcbeb998d..460990bc8 100644 --- a/Projects/UOContent/Spells/Second/Protection.cs +++ b/Projects/UOContent/Spells/Second/Protection.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; -using Server.Engines.BuffIcons; using Server.Mobiles; -using Server.Targeting; namespace Server.Spells.Second { - public class ProtectionSpell : MagerySpell, ITargetingSpell + public class ProtectionSpell : MagerySpell { private static readonly SpellInfo _info = new( "Protection", @@ -19,10 +17,8 @@ namespace Server.Spells.Second Reagent.SulfurousAsh ); - private static Dictionary> _table; - - // T2A: stores the AR bonus per mobile (shared with ArchProtection) - private static Dictionary _t2aTable; + // TODO: Cleanup periodically if players have logged out for a while + private static readonly Dictionary> _table = new(); public ProtectionSpell(Mobile caster, Item scroll = null) : base(caster, scroll, _info) { @@ -32,39 +28,9 @@ namespace Server.Spells.Second public override SpellCircle Circle => SpellCircle.Second; - public static bool HasT2AProtection(Mobile m) => _t2aTable?.ContainsKey(m) ?? false; - - public static void RemoveT2AProtection(Mobile m) - { - if (_t2aTable?.Remove(m, out var bonus) == true) - { - m.VirtualArmorMod -= Math.Min(bonus, m.VirtualArmorMod); - } - } - - public static void ApplyT2AProtection(Mobile caster, Mobile target) - { - if (HasT2AProtection(target)) - { - return; - } - - var magery = caster.Skills.Magery.Value; - var bonus = (int)(magery / 10); - var duration = TimeSpan.FromSeconds(6 * magery / 5); - - _t2aTable ??= []; - _t2aTable[target] = bonus; - target.VirtualArmorMod += bonus; - - target.FixedParticles(0x375A, 9, 20, 5016, EffectLayer.Waist); - - new InternalTimer(target, duration).Start(); - } - public override bool CheckCast() { - if (Core.AOS || !Core.UOR) + if (Core.AOS) { return true; } @@ -84,28 +50,6 @@ namespace Server.Spells.Second return false; } - public void Target(Mobile m) - { - if (!Caster.CanBeBeneficial(m)) - { - return; - } - - if (HasT2AProtection(m)) - { - Caster.SendLocalizedMessage(1005559); // This spell is already in effect. - return; - } - - if (CheckBSequence(m)) - { - Caster.DoBeneficial(m); - SpellHelper.Turn(Caster, m); - ApplyT2AProtection(Caster, m); - m.PlaySound(0x1ED); - } - } - // AOS+ only public static void Toggle(Mobile caster, Mobile target) { @@ -118,7 +62,7 @@ namespace Server.Spells.Second * even after dying, until you turn them off by casting them again. */ - if (_table?.Remove(target, out var mods) == true) + if (_table.Remove(target, out var mods)) { target.PlaySound(0x1ED); target.FixedParticles(0x375A, 9, 20, 5016, EffectLayer.Waist); @@ -128,7 +72,7 @@ namespace Server.Spells.Second target.RemoveResistanceMod(mods.Item1); target.RemoveSkillMod(mods.Item2); - (target as PlayerMobile)?.RemoveBuff(BuffIcon.Protection); + BuffInfo.RemoveBuff(target, BuffIcon.Protection); } else { @@ -140,7 +84,6 @@ namespace Server.Spells.Second var physMod = new ResistanceMod(ResistanceType.Physical, "PhysicalResistProtectionSpell", physLoss); var resistMod = new DefaultSkillMod(SkillName.MagicResist, "MagicResistProtectionSpell", true, resistLoss); - _table ??= []; _table[target] = Tuple.Create(physMod, resistMod); Registry[target] = 1000; // 100.0% protection from disruption @@ -148,16 +91,14 @@ namespace Server.Spells.Second target.AddSkillMod(resistMod); var args = $"{physLoss}\t{resistLoss}"; - (target as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Protection, 1075814, 1075815, args: args)); + BuffInfo.AddBuff(target, new BuffInfo(BuffIcon.Protection, 1075814, 1075815, args)); } } [OnEvent(nameof(PlayerMobile.PlayerDeletedEvent))] public static void EndProtection(Mobile m) { - RemoveT2AProtection(m); - - if (_table?.Remove(m, out var mods) != true) + if (!_table.Remove(m, out var mods)) { return; } @@ -167,7 +108,7 @@ namespace Server.Spells.Second m.RemoveResistanceMod(mods.Item1); m.RemoveSkillMod(mods.Item2); - (m as PlayerMobile)?.RemoveBuff(BuffIcon.Protection); + BuffInfo.RemoveBuff(m, BuffIcon.Protection); } public override void OnCast() @@ -181,10 +122,6 @@ namespace Server.Spells.Second FinishSequence(); } - else if (!Core.UOR) - { - Caster.Target = new SpellTarget(this, TargetFlags.Beneficial); - } else { if (Registry.ContainsKey(Caster)) @@ -199,13 +136,12 @@ namespace Server.Spells.Second { if (Caster.BeginAction()) { - var value = (Caster.Skills.EvalInt.Value + - Caster.Skills.Meditation.Value + - Caster.Skills.Inscribe.Value) * 10 / 4; + double value = (Caster.Skills.EvalInt.Value + + Caster.Skills.Meditation.Value + + Caster.Skills.Inscribe.Value) * 10 / 4; Registry.Add(Caster, Math.Clamp((int)value, 0, 750)); // 75.0% protection from disruption - var duration = TimeSpan.FromSeconds(Math.Clamp(Caster.Skills.Magery.Value * 2.0, 15, 240)); - new InternalTimer(Caster, duration).Start(); + new InternalTimer(Caster).Start(); Caster.FixedParticles(0x375A, 9, 20, 5016, EffectLayer.Waist); Caster.PlaySound(0x1ED); @@ -222,21 +158,20 @@ namespace Server.Spells.Second private class InternalTimer : Timer { - private readonly Mobile _mobile; + private readonly Mobile m_Caster; - public InternalTimer(Mobile mobile, TimeSpan duration) : base(duration) => _mobile = mobile; + public InternalTimer(Mobile caster) : base(TimeSpan.FromSeconds(0)) + { + var val = Math.Clamp(caster.Skills.Magery.Value * 2.0, 15, 240); + + m_Caster = caster; + Delay = TimeSpan.FromSeconds(val); + } protected override void OnTick() { - if (!Core.UOR) - { - RemoveT2AProtection(_mobile); - } - else - { - Registry.Remove(_mobile); - DefensiveSpell.Nullify(_mobile); - } + Registry.Remove(m_Caster); + DefensiveSpell.Nullify(m_Caster); } } } diff --git a/Projects/UOContent/Spells/Second/Strength.cs b/Projects/UOContent/Spells/Second/Strength.cs index 3f0f978ae..82d5dff13 100644 --- a/Projects/UOContent/Spells/Second/Strength.cs +++ b/Projects/UOContent/Spells/Second/Strength.cs @@ -1,6 +1,4 @@ -using Server.Engines.BuffIcons; using Server.Engines.ConPVP; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Second @@ -36,7 +34,7 @@ namespace Server.Spells.Second var percentage = (int)(SpellHelper.GetOffsetScalar(Caster, m, false) * 100); - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Strength, 1075845, length, percentage.ToString())); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Strength, 1075845, length, m, percentage.ToString())); } } diff --git a/Projects/UOContent/Spells/Seventh/EnergyField.cs b/Projects/UOContent/Spells/Seventh/EnergyField.cs index dbfece36d..87dedeaf7 100644 --- a/Projects/UOContent/Spells/Seventh/EnergyField.cs +++ b/Projects/UOContent/Spells/Seventh/EnergyField.cs @@ -26,8 +26,6 @@ public class EnergyFieldSpell : MagerySpell, ITargetingSpell public override SpellCircle Circle => SpellCircle.Seventh; - public int TargetRange => Core.T2A ? 15 : 18; - public void Target(IPoint3D p) { if (SpellHelper.CheckTown(p, Caster) && CheckSequence()) @@ -41,7 +39,7 @@ public class EnergyFieldSpell : MagerySpell, ITargetingSpell Effects.PlaySound(loc, Caster.Map, 0x20B); - var duration = Core.AOS + TimeSpan duration = Core.AOS ? TimeSpan.FromSeconds((15 + Caster.Skills.Magery.Value * 2) / 7.0) : TimeSpan.FromSeconds(Caster.Skills.Magery.Value * 0.28 + 2.0); @@ -57,7 +55,8 @@ public class EnergyFieldSpell : MagerySpell, ITargetingSpell continue; } - new EnergyField(targetLoc, Caster.Map, duration, itemID, Caster).ProcessDelta(); + Item item = new EnergyField(targetLoc, Caster.Map, duration, itemID, Caster); + item.ProcessDelta(); Effects.SendLocationParticles( EffectItem.Create(targetLoc, Caster.Map, EffectItem.DefaultDuration), @@ -77,19 +76,13 @@ public class EnergyFieldSpell : MagerySpell, ITargetingSpell } [DispellableField] -[SerializationGenerator(2, false)] +[SerializationGenerator(1, false)] public partial class EnergyField : Item { - private void MigrateFrom(V1Content content) - { - _caster = content.Caster; - _end = content.End; - } - [SerializableField(0)] private Mobile _caster; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(1)] private DateTime _end; diff --git a/Projects/UOContent/Spells/Seventh/GateTravel.cs b/Projects/UOContent/Spells/Seventh/GateTravel.cs index 3e87a721f..c0cd6b4a8 100644 --- a/Projects/UOContent/Spells/Seventh/GateTravel.cs +++ b/Projects/UOContent/Spells/Seventh/GateTravel.cs @@ -52,7 +52,7 @@ public class GateTravelSpell : MagerySpell, IRecallSpell { mobile.SendLocalizedMessage(1049543); // You decide against traveling to Felucca while you are still young. } - else if (Caster.Murderer && map != Map.Felucca) + else if (Caster.Kills >= 5 && map != Map.Felucca) { Caster.SendLocalizedMessage(1019004); // You are not allowed to travel there. } @@ -170,8 +170,6 @@ public partial class GateTravelMoongate : Moongate Timer.StartTimer(TimeSpan.FromSeconds(Core.T2A ? 30.0 : 10.0), Delete); } - public override bool SkipSerialization => true; - [CommandProperty(AccessLevel.GameMaster)] public Moongate LinkedGate { get; set; } @@ -202,4 +200,10 @@ public partial class GateTravelMoongate : Moongate base.UseGate(m); } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } diff --git a/Projects/UOContent/Spells/Seventh/MeteorSwarm.cs b/Projects/UOContent/Spells/Seventh/MeteorSwarm.cs index f73fb3111..adb35a827 100644 --- a/Projects/UOContent/Spells/Seventh/MeteorSwarm.cs +++ b/Projects/UOContent/Spells/Seventh/MeteorSwarm.cs @@ -63,7 +63,7 @@ namespace Server.Spells.Seventh ? GetNewAosDamage(51, 1, 5, playerVsPlayer) : Utility.Random(27, 22); - var count = queue.Count; + int count = queue.Count; if (count > 0) { diff --git a/Projects/UOContent/Spells/Sixth/EnergyBolt.cs b/Projects/UOContent/Spells/Sixth/EnergyBolt.cs index d72aaf55f..a253cd1bb 100644 --- a/Projects/UOContent/Spells/Sixth/EnergyBolt.cs +++ b/Projects/UOContent/Spells/Sixth/EnergyBolt.cs @@ -52,9 +52,11 @@ namespace Server.Spells.Sixth damage *= GetDamageScalar(m); } - source.MovingParticles(m, 0x379F, 7, 0, false, true, 0, 0, 3043, 4043, 0x211, EffectLayer.RightHand, 0); + // Do the effects + source.MovingParticles(m, 0x379F, 7, 0, false, true, 3043, 4043, 0x211); source.PlaySound(0x20A); + // Deal the damage SpellHelper.Damage(this, m, damage, 0, 0, 0, 0, 100); } } diff --git a/Projects/UOContent/Spells/Sixth/Explosion.cs b/Projects/UOContent/Spells/Sixth/Explosion.cs index 9e101f03e..f6f49ce0e 100644 --- a/Projects/UOContent/Spells/Sixth/Explosion.cs +++ b/Projects/UOContent/Spells/Sixth/Explosion.cs @@ -34,7 +34,7 @@ namespace Server.Spells.Sixth if (Caster.CanBeHarmful(m) && CheckSequence()) { - var defender = m; + Mobile defender = m; SpellHelper.Turn(Caster, m); SpellHelper.CheckReflect((int)Circle, Caster, ref m); diff --git a/Projects/UOContent/Spells/Sixth/Invisibility.cs b/Projects/UOContent/Spells/Sixth/Invisibility.cs index 21ca2d302..916707bcb 100644 --- a/Projects/UOContent/Spells/Sixth/Invisibility.cs +++ b/Projects/UOContent/Spells/Sixth/Invisibility.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; using Server.Engines.ConPVP; using Server.Items; using Server.Mobiles; @@ -54,11 +53,8 @@ namespace Server.Spells.Sixth var duration = TimeSpan.FromSeconds(1.2 * Caster.Skills.Magery.Value); - if (m is PlayerMobile pm) - { - pm.RemoveBuff(BuffIcon.HidingAndOrStealth); - pm.AddBuff(new BuffInfo(BuffIcon.Invisibility, 1075825, duration)); // Invisibility/Invisible - } + BuffInfo.RemoveBuff(m, BuffIcon.HidingAndOrStealth); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Invisibility, 1075825, duration, m)); // Invisibility/Invisible Timer.StartTimer(duration, () => diff --git a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs index 301cee507..ce2ec48f0 100644 --- a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs +++ b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs @@ -25,8 +25,6 @@ public class ParalyzeFieldSpell : MagerySpell, ITargetingSpell public override SpellCircle Circle => SpellCircle.Sixth; - public int TargetRange => Core.T2A ? 15 : 18; - public void Target(IPoint3D p) { if (SpellHelper.CheckTown(p, Caster) && CheckSequence()) @@ -57,7 +55,8 @@ public class ParalyzeFieldSpell : MagerySpell, ITargetingSpell continue; } - new ParalyzeField(Caster, itemID, targetLoc, Caster.Map, duration).ProcessDelta(); + Item item = new ParalyzeField(Caster, itemID, targetLoc, Caster.Map, duration); + item.ProcessDelta(); Effects.SendLocationParticles( EffectItem.Create(targetLoc, Caster.Map, EffectItem.DefaultDuration), @@ -77,19 +76,13 @@ public class ParalyzeFieldSpell : MagerySpell, ITargetingSpell } [DispellableField] -[SerializationGenerator(1, false)] +[SerializationGenerator(0, false)] public partial class ParalyzeField : Item { - private void MigrateFrom(V0Content content) - { - _caster = content.Caster; - _end = content.End; - } - [SerializableField(0)] private Mobile _caster; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(1)] private DateTime _end; diff --git a/Projects/UOContent/Spells/Spellweaving/AttuneWeapon.cs b/Projects/UOContent/Spells/Spellweaving/AttuneWeapon.cs index 64dd6e705..aa898843c 100644 --- a/Projects/UOContent/Spells/Spellweaving/AttuneWeapon.cs +++ b/Projects/UOContent/Spells/Spellweaving/AttuneWeapon.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Spells.Spellweaving { @@ -64,8 +62,9 @@ namespace Server.Spells.Spellweaving Caster.BeginAction(); - (Caster as PlayerMobile)?.AddBuff( - new BuffInfo(BuffIcon.AttuneWeapon, 1075798, duration, damageAbsorb.ToString()) + BuffInfo.AddBuff( + Caster, + new BuffInfo(BuffIcon.AttuneWeapon, 1075798, duration, Caster, damageAbsorb.ToString()) ); } @@ -131,7 +130,7 @@ namespace Server.Spells.Spellweaving _table.Remove(m_Mobile); StartTimer(TimeSpan.FromSeconds(120), m_Mobile.EndAction); - (m_Mobile as PlayerMobile)?.RemoveBuff(BuffIcon.AttuneWeapon); + BuffInfo.RemoveBuff(m_Mobile, BuffIcon.AttuneWeapon); } } } diff --git a/Projects/UOContent/Spells/Spellweaving/EssenceOfWind.cs b/Projects/UOContent/Spells/Spellweaving/EssenceOfWind.cs index 6a85c317c..75debc119 100644 --- a/Projects/UOContent/Spells/Spellweaving/EssenceOfWind.cs +++ b/Projects/UOContent/Spells/Spellweaving/EssenceOfWind.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using Server.Collections; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Spells.Spellweaving { @@ -65,8 +63,9 @@ namespace Server.Spells.Spellweaving _table[m] = t; - (m as PlayerMobile)?.AddBuff( - new BuffInfo(BuffIcon.EssenceOfWind, 1075802, duration, $"{fcMalus}\t{ssiMalus}") + BuffInfo.AddBuff( + m, + new BuffInfo(BuffIcon.EssenceOfWind, 1075802, duration, m, $"{fcMalus}\t{ssiMalus}") ); } } @@ -111,7 +110,7 @@ namespace Server.Spells.Spellweaving Stop(); _table.Remove(_defender); - (_defender as PlayerMobile)?.RemoveBuff(BuffIcon.EssenceOfWind); + BuffInfo.RemoveBuff(_defender, BuffIcon.EssenceOfWind); } } } diff --git a/Projects/UOContent/Spells/Spellweaving/EtherealVoyage.cs b/Projects/UOContent/Spells/Spellweaving/EtherealVoyage.cs index 5a033b14d..6d2aa6bee 100644 --- a/Projects/UOContent/Spells/Spellweaving/EtherealVoyage.cs +++ b/Projects/UOContent/Spells/Spellweaving/EtherealVoyage.cs @@ -1,6 +1,4 @@ using System; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Spells.Spellweaving { @@ -73,7 +71,7 @@ namespace Server.Spells.Spellweaving // Cannot cast this spell for another 5 minutes(300sec) after effect removed. Caster.BeginAction(); - (Caster as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.EtherealVoyage, 1031613, 1075805, duration)); + BuffInfo.AddBuff(Caster, new BuffInfo(BuffIcon.EtherealVoyage, 1031613, 1075805, duration, Caster)); } public override void RemoveEffect(Mobile m) @@ -84,7 +82,7 @@ namespace Server.Spells.Spellweaving Timer.StartTimer(TimeSpan.FromMinutes(5), m.EndAction); - (m as PlayerMobile)?.RemoveBuff(BuffIcon.EtherealVoyage); + BuffInfo.RemoveBuff(m, BuffIcon.EtherealVoyage); } } } diff --git a/Projects/UOContent/Spells/Spellweaving/GiftOfLife.cs b/Projects/UOContent/Spells/Spellweaving/GiftOfLife.cs index 585d37284..7118854b7 100644 --- a/Projects/UOContent/Spells/Spellweaving/GiftOfLife.cs +++ b/Projects/UOContent/Spells/Spellweaving/GiftOfLife.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using ModernUO.CodeGeneratedEvents; -using Server.Engines.BuffIcons; using Server.Gumps; using Server.Mobiles; using Server.Targeting; @@ -68,9 +67,7 @@ namespace Server.Spells.Spellweaving _table[m] = t; - (m as PlayerMobile)?.AddBuff( - new BuffInfo(BuffIcon.GiftOfLife, 1031615, 1075807, duration, retainThroughDeath: true) - ); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.GiftOfLife, 1031615, 1075807, duration, m, null, true)); } } @@ -155,7 +152,7 @@ namespace Server.Spells.Spellweaving _mobile.SendLocalizedMessage(1074776); // You are no longer protected with Gift of Life. _table.Remove(_mobile); - (_mobile as PlayerMobile)?.RemoveBuff(BuffIcon.GiftOfLife); + BuffInfo.RemoveBuff(_mobile, BuffIcon.GiftOfLife); } } } diff --git a/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs b/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs index 11f21993c..557367e71 100644 --- a/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs +++ b/Projects/UOContent/Spells/Spellweaving/GiftOfRenewal.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Server.Engines.BuffIcons; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Spellweaving @@ -61,14 +59,9 @@ namespace Server.Spells.Spellweaving Caster.BeginAction(); - (m as PlayerMobile)?.AddBuff( - new BuffInfo( - BuffIcon.GiftOfRenewal, - 1031602, - 1075797, - TimeSpan.FromSeconds(duration), - hitsPerRound.ToString() - ) + BuffInfo.AddBuff( + m, + new BuffInfo(BuffIcon.GiftOfRenewal, 1031602, 1075797, TimeSpan.FromSeconds(duration), m, hitsPerRound.ToString()) ); } } @@ -81,7 +74,7 @@ namespace Server.Spells.Spellweaving public static bool StopEffect(Mobile m) { - (m as PlayerMobile)?.RemoveBuff(BuffIcon.GiftOfRenewal); + BuffInfo.RemoveBuff(m, BuffIcon.GiftOfRenewal); if (_table.Remove(m, out var timer)) { diff --git a/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs b/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs index 49894cc95..54ac52030 100644 --- a/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs +++ b/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs @@ -3,17 +3,12 @@ using ModernUO.Serialization; namespace Server.Items; -[SerializationGenerator(2, false)] +[SerializationGenerator(1, false)] public partial class TransientItem : Item { - private void MigrateFrom(V1Content content) - { - _expiration = content.Expiration; - } - private TimerExecutionToken _timerToken; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(0)] [SerializedCommandProperty(AccessLevel.GameMaster)] private DateTime _expiration; diff --git a/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs b/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs index e1d3316b1..a1deba44f 100644 --- a/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs +++ b/Projects/UOContent/Spells/Spellweaving/Mobiles/NatureFury.cs @@ -36,8 +36,6 @@ public partial class NatureFury : BaseCreature ControlSlots = 1; } - public override bool SkipSerialization => true; - public override bool DeleteCorpseOnDeath => Core.AOS; public override bool IsHouseSummonable => true; @@ -67,4 +65,10 @@ public partial class NatureFury : BaseCreature Timer.StartTimer(TimeSpan.FromSeconds(7.0), DoEffects); } } + + [AfterDeserialization(false)] + private void AfterDeserialization() + { + Delete(); + } } diff --git a/Projects/UOContent/Spells/Spellweaving/Thunderstorm.cs b/Projects/UOContent/Spells/Spellweaving/Thunderstorm.cs index 0096d3dbf..14de37d65 100644 --- a/Projects/UOContent/Spells/Spellweaving/Thunderstorm.cs +++ b/Projects/UOContent/Spells/Spellweaving/Thunderstorm.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using Server.Collections; -using Server.Engines.BuffIcons; -using Server.Mobiles; namespace Server.Spells.Spellweaving { @@ -74,8 +72,9 @@ namespace Server.Spells.Spellweaving Timer.StartTimer(duration, () => DoExpire(m), out var timerToken); _table[m] = timerToken; - (m as PlayerMobile)?.AddBuff( - new BuffInfo(BuffIcon.Thunderstorm, 1075800, duration, GetCastRecoveryMalus(m)) + BuffInfo.AddBuff( + m, + new BuffInfo(BuffIcon.Thunderstorm, 1075800, duration, m, GetCastRecoveryMalus(m)) ); } } @@ -96,7 +95,7 @@ namespace Server.Spells.Spellweaving public static void DoExpire(Mobile m) { StopTimer(m); - (m as PlayerMobile)?.RemoveBuff(BuffIcon.Thunderstorm); + BuffInfo.RemoveBuff(m, BuffIcon.Thunderstorm); } } } diff --git a/Projects/UOContent/Spells/Targeting/ITargetingSpell.cs b/Projects/UOContent/Spells/Targeting/ITargetingSpell.cs index 3c23dcbd8..f2671e1a5 100644 --- a/Projects/UOContent/Spells/Targeting/ITargetingSpell.cs +++ b/Projects/UOContent/Spells/Targeting/ITargetingSpell.cs @@ -4,8 +4,7 @@ public interface IRangedSpell { Mobile Caster { get; } - // https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/1999-2/1999-06-14th-april/ - int TargetRange => Core.T2A ? 10 : 12; + int TargetRange => Core.ML ? 10 : 12; void FinishSequence(); } diff --git a/Projects/UOContent/Spells/Third/Bless.cs b/Projects/UOContent/Spells/Third/Bless.cs index de23ad93e..9cb2f16aa 100644 --- a/Projects/UOContent/Spells/Third/Bless.cs +++ b/Projects/UOContent/Spells/Third/Bless.cs @@ -1,6 +1,4 @@ -using Server.Engines.BuffIcons; using Server.Engines.ConPVP; -using Server.Mobiles; using Server.Targeting; namespace Server.Spells.Third @@ -40,7 +38,7 @@ namespace Server.Spells.Third var args = $"{percentage}\t{percentage}\t{percentage}"; - (m as PlayerMobile)?.AddBuff(new BuffInfo(BuffIcon.Bless, 1075847, 1075848, length, args)); + BuffInfo.AddBuff(m, new BuffInfo(BuffIcon.Bless, 1075847, 1075848, length, m, args)); } } diff --git a/Projects/UOContent/Spells/Third/Fireball.cs b/Projects/UOContent/Spells/Third/Fireball.cs index 84d8d5b82..2ec73fecb 100644 --- a/Projects/UOContent/Spells/Third/Fireball.cs +++ b/Projects/UOContent/Spells/Third/Fireball.cs @@ -50,7 +50,7 @@ namespace Server.Spells.Third damage *= GetDamageScalar(m); } - source.MovingParticles(m, 0x36D4, 7, 0, false, true, 0, 0, 9502, 4019, 0x160, EffectLayer.RightHand, 0); + source.MovingParticles(m, 0x36D4, 7, 0, false, true, 9502, 4019, 0x160); source.PlaySound(Core.AOS ? 0x15E : 0x44B); SpellHelper.Damage(this, m, damage, 0, 100, 0, 0, 0); diff --git a/Projects/UOContent/Spells/Third/WallOfStone.cs b/Projects/UOContent/Spells/Third/WallOfStone.cs index 1b1dd04d3..f1c94c6fd 100644 --- a/Projects/UOContent/Spells/Third/WallOfStone.cs +++ b/Projects/UOContent/Spells/Third/WallOfStone.cs @@ -23,8 +23,6 @@ public class WallOfStoneSpell : MagerySpell, ITargetingSpell public override SpellCircle Circle => SpellCircle.Third; - public int TargetRange => Core.T2A ? 15 : 18; - public void Target(IPoint3D p) { if (SpellHelper.CheckTown(p, Caster) && CheckSequence()) @@ -63,19 +61,13 @@ public class WallOfStoneSpell : MagerySpell, ITargetingSpell } [DispellableField] -[SerializationGenerator(1, false)] +[SerializationGenerator(0, false)] public partial class WallOfStone : Item { - private void MigrateFrom(V0Content content) - { - _caster = content.Caster; - _end = content.End; - } - [SerializableField(0)] private Mobile _caster; - [AnchoredDateTime] + [DeltaDateTime] [SerializableField(1)] private DateTime _end; diff --git a/Projects/UOContent/Spells/UnsummonTimer.cs b/Projects/UOContent/Spells/UnsummonTimer.cs index 77dd3eccd..169517411 100644 --- a/Projects/UOContent/Spells/UnsummonTimer.cs +++ b/Projects/UOContent/Spells/UnsummonTimer.cs @@ -25,7 +25,7 @@ public class UnsummonTimer : Timer _onUnsummon = onUnsummon; _creature = creature; - ref var timer = ref CollectionsMarshal.GetValueRefOrAddDefault(_timers, creature, out var exists); + ref var timer = ref CollectionsMarshal.GetValueRefOrAddDefault(_timers, creature, out bool exists); if (exists) { timer.Stop(); diff --git a/Projects/UOContent/Systems/JailSystem/JailRecord.cs b/Projects/UOContent/Systems/JailSystem/JailRecord.cs deleted file mode 100644 index c57ef8622..000000000 --- a/Projects/UOContent/Systems/JailSystem/JailRecord.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using ModernUO.Serialization; - -namespace Server.Systems.JailSystem; - -[SerializationGenerator(0)] -public partial class JailRecord -{ - [SerializableField(0)] - private int _jailCount; - - [SerializableField(1)] - private DateTime _lastJailed; - - [SerializableField(2)] - private DateTime _jailEndTime; - - [SerializableField(3)] - private string _lastJailReason; - - [SerializableField(4)] - private Mobile _jailedBy; - - public bool IsCurrentlyJailed => JailEndTime > Core.Now; -} diff --git a/Projects/UOContent/Systems/JailSystem/JailRecordGump.cs b/Projects/UOContent/Systems/JailSystem/JailRecordGump.cs deleted file mode 100644 index 31312382a..000000000 --- a/Projects/UOContent/Systems/JailSystem/JailRecordGump.cs +++ /dev/null @@ -1,90 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: JailRecordGump.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using Server.Gumps; -using Server.Mobiles; - -namespace Server.Systems.JailSystem; - -public class JailRecordGump : StaticGump -{ - private readonly PlayerMobile _player; - private readonly JailRecord _record; - - public JailRecordGump(PlayerMobile player, JailRecord record) : base(0, 0) - { - _player = player; - _record = record; - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.SetNoResize(); - - builder.AddPage(); - builder.AddBackground(0, 0, 330, 300, 9200); - builder.AddAlphaRegion(10, 10, 310, 280); - - builder.AddHtml(20, 20, 360, 30, "JAIL RECORD"); - builder.AddHtmlPlaceholder(20, 60, 360, 25, "playerName"); - builder.AddHtmlPlaceholder(20, 80, 360, 25, "jailCount"); - builder.AddHtmlPlaceholder(20, 100, 360, 25, "lastJailed"); - builder.AddHtmlPlaceholder(20, 120, 290, 25, "jailReason"); - - builder.AddHtmlPlaceholder(20, 150, 360, 25, "jailStatus"); - builder.AddHtmlPlaceholder(20, 170, 360, 25, "jailTime"); - - builder.AddHtml(20, 200, 360, 25, "If you believe you were jailed in error,"); - builder.AddHtml(20, 220, 360, 25, "please contact staff through normal channels."); - builder.AddHtml(20, 240, 360, 25, "Each time you are jailed, the wait increases."); - builder.AddHtml(20, 260, 360, 25, "Follow all shard rules to avoid future jail time."); - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - builder.SetHtmlText("playerName", $"Player: {_player.Name}", "#FFFF00", 5); - builder.SetHtmlText("jailCount", $"Jail Count: {_record.JailCount}", "#FFFFFF", 5); - - if (_record.LastJailed > DateTime.MinValue) - { - builder.SetHtmlText("lastJailed", $"Last Jailed: {_record.LastJailed:yyyy/MM/dd HH:mm}", "#FFFFFF", 5); - } - else - { - builder.SetHtmlText("lastJailed", "Last Jailed: Never", "#FFFFFF", 5); - } - - if (string.IsNullOrWhiteSpace(_record.LastJailReason)) - { - builder.SetHtmlText("jailReason", "Jail Reason: None given.", "#FFFFFF", 4); - } - else - { - builder.SetHtmlText("jailReason", $"Jail Reason: {_record.LastJailReason}", "#FFFFFF", 4); - } - - if (_record.IsCurrentlyJailed) - { - builder.SetHtmlText("jailStatus", "Status: Currently Jailed", "#FF6666", 5); - builder.SetHtmlText("jailTime", $"Jail Time: {(_record.JailEndTime - Core.Now).FormatTimeCompact()}", "#FF6666", 5); - } - else - { - builder.SetHtmlText("jailStatus", "Status: Not Jailed", "#00FF00", 5); - builder.SetStringSlot("jailTime", ""); - } - } -} diff --git a/Projects/UOContent/Systems/JailSystem/JailSystem.cs b/Projects/UOContent/Systems/JailSystem/JailSystem.cs deleted file mode 100644 index c79c44303..000000000 --- a/Projects/UOContent/Systems/JailSystem/JailSystem.cs +++ /dev/null @@ -1,427 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: JailSystem.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using Server.Mobiles; -using Server.Network; -using Server.Commands; -using Server.Gumps; - -namespace Server.Systems.JailSystem; - -public class JailSystem : GenericPersistence -{ - // Jail locations (modify if using custom maps) - // felucca void - private static readonly Point3D[] JailLocations = - [ - new(5276, 1164, 0), - new(5286, 1164, 0), - new(5296, 1164, 0), - new(5306, 1164, 0), - new(5276, 1174, 0), - new(5286, 1174, 0), - new(5296, 1174, 0), - new(5306, 1174, 0), - new(5283, 1184, 0), - new(5304, 1184, 0) - ]; - - // Jail map, change this for custom maps - public static readonly Map JailMap = Map.Felucca; - private static readonly JailRecord EmptyRecord = new(); - - private static readonly HashSet CurrentlyBeingJailed = []; - private static readonly Dictionary PlayerJailRecords = []; - private static readonly Dictionary JailTimers = []; - - // Jail time scales from 5 minutes to 12 hours based on the number of offenses - private static readonly TimeSpan MinJailTime = TimeSpan.FromMinutes(5); - private static readonly TimeSpan MaxJailTime = TimeSpan.FromHours(12); - - // Release location, change this for custom maps - private static readonly Point3D ReleaseLocation = new(1444, 1697, 10); // Britain Bank - public static readonly Map ReleaseMap = Map.Felucca; - - private static JailSystem Instance; - - // [jail [reason] - Jail with time escalation per offense (GM only) - // [unjail - Manual release from jail regardless of time (GM only) - // [jailinfo - Check jail status and history (GM only) - // [jailrecord - Checks their own jail record stats (player access) - - public static void Configure() - { - Instance = new JailSystem(); - CommandSystem.Register("Jail", AccessLevel.GameMaster, Jail_OnCommand); - CommandSystem.Register("Unjail", AccessLevel.GameMaster, Unjail_OnCommand); - CommandSystem.Register("JailInfo", AccessLevel.Counselor, JailInfo_OnCommand); - CommandSystem.Register("JailRecord", AccessLevel.Player, MyJailInfo_OnCommand); - } - - public JailSystem() : base("Jail", 3) - { - } - - // Can be used by other systems to check player jail status - public static bool IsPlayerJailed(PlayerMobile player) => - PlayerJailRecords.GetValueOrDefault(player)?.IsCurrentlyJailed == true; - - private static TimeSpan CalculateJailTime(int jailCount) - { - var totalMinutes = MinJailTime + (jailCount - 1) * (MaxJailTime - MinJailTime) / 9.0; - - return totalMinutes.Clamp(MinJailTime, MaxJailTime); - } - - public static void JailPlayer(Mobile from, PlayerMobile player, string reason = "") - { - if (!CurrentlyBeingJailed.Add(player)) - { - return; - } - - if (!PlayerJailRecords.TryGetValue(player, out var record)) - { - PlayerJailRecords[player] = record = new JailRecord(); - } - - record.JailCount++; - record.LastJailed = Core.Now; - record.LastJailReason = reason; - record.JailedBy = from; - - var jailTime = CalculateJailTime(record.JailCount); - record.JailEndTime = Core.Now + jailTime; - - player.Frozen = true; - player.SendMessage(0x35, "You are being sent to jail!"); - player.PlaySound(0x204); - - CommandLogging.WriteLine(from, $"Player {player.Name} jailed for: {reason} (Offense #{record.JailCount}, {jailTime.TotalMinutes} minutes)"); - - foreach (var ns in NetState.Instances) - { - if (ns.Mobile is PlayerMobile staff && staff.AccessLevel >= AccessLevel.Counselor) - { - staff.SendMessage(0x35, $"Player {player.Name} has been jailed for {jailTime.TotalMinutes} minutes. Reason: {reason} (Offense #{record.JailCount})"); - } - } - - Timer.DelayCall(TimeSpan.FromSeconds(2.0), DismountPlayer, from, player, jailTime); - } - - private static void DismountPlayer(Mobile from, PlayerMobile player, TimeSpan jailTime) - { - if (player.Mount != null) - { - var mount = player.Mount; - mount.Rider = null; - player.SendMessage(0x35, "You have been dismounted."); - if (mount is BaseCreature bc) - { - if (bc.Summoned) - { - bc.Dispel(bc); - } - else // Stable the pet - { - bc.ControlTarget = null; - bc.ControlOrder = OrderType.Stay; - bc.Internalize(); - - bc.SetControlMaster(null); - - bc.IsStabled = true; - bc.StabledBy = from; - player.AddStabled(bc); - } - } - } - - CommandLogging.WriteLine(from, $"Player {player.Name} dismounted before jail teleport"); - - Timer.DelayCall(TimeSpan.FromSeconds(3.0), TeleportToJail, from, player, jailTime); - } - - private static void TeleportToJail(Mobile from, PlayerMobile player, TimeSpan jailTime) - { - var jailLocation = JailLocations.RandomElement(); - - player.MoveToWorld(jailLocation, JailMap); - - player.SendMessage(0x35, "Use [jailrecord to pull up your record."); - player.SendMessage(0x35, "Please contact staff if you believe this was a mistake."); - - CommandLogging.WriteLine(from, $"Player {player.Name} teleported to jail at {jailLocation}"); - - Timer.DelayCall(TimeSpan.FromSeconds(5.0), UnfreezePlayer, from, player, jailTime); - } - - private static void UnfreezePlayer(Mobile from, PlayerMobile player, TimeSpan jailTime) - { - player.Frozen = false; - - CommandLogging.WriteLine(from, $"Player {player.Name} unfrozen in jail"); - - CurrentlyBeingJailed.Remove(player); - - var releaseTimer = Timer.DelayCall(jailTime, ReleasePlayer, from, player); - JailTimers[player] = releaseTimer; - } - - private static void ReleasePlayer(Mobile from, PlayerMobile player) - { - if (PlayerJailRecords.TryGetValue(player, out var record)) - { - record.JailEndTime = Core.Now; - } - - JailTimers.Remove(player); - - // Freeze player for release sequence - player.Frozen = true; - player.SendMessage(0x35, "You have been released from jail!"); - player.PlaySound(0x1FF); - - if (from != null) - { - CommandLogging.WriteLine(from, $"Player {player.Name} released from jail, starting teleport sequence"); - } - - foreach (var ns in NetState.Instances) - { - if (ns.Mobile is PlayerMobile staff && staff.AccessLevel >= AccessLevel.Counselor) - { - staff.SendMessage(0x35, $"Player {player.Name} has been released from jail."); - } - } - - Timer.DelayCall(TimeSpan.FromSeconds(5.0), TeleportFromJail, from, player); - } - - private static void TeleportFromJail(Mobile from, PlayerMobile player) - { - player.MoveToWorld(ReleaseLocation, ReleaseMap); - - if (from != null) - { - CommandLogging.WriteLine(player, $"Player {player.Name} teleported from jail to {ReleaseLocation}"); - } - - Timer.DelayCall(TimeSpan.FromSeconds(5.0), UnfreezeFromRelease, from, player); - } - - private static void UnfreezeFromRelease(Mobile from, PlayerMobile player) - { - player.Frozen = false; - player.SendMessage(0x35, "Welcome back!"); - player.SendMessage(0x35, "Please follow the shard rules."); - player.SendMessage(0x35, "Have a nice day!"); - - if (from != null) - { - CommandLogging.WriteLine(from, $"Player {player.Name} unfrozen after jail release"); - } - } - - [Usage("Jail [reason]")] - [Description("Jails a player with freeze/teleport/unfreeze sequence.")] - private static void Jail_OnCommand(CommandEventArgs e) - { - if (e.Length < 1) - { - e.Mobile.SendMessage(0x35, "Usage: [jail [reason]"); - return; - } - - var reason = e.GetString(1); - var playerName = e.GetString(0); - var playerSerial = Serial.TryParse(playerName, null, out var serial) ? serial : Serial.MinusOne; - - PlayerMobile player = null; - - foreach (var m in World.Mobiles.Values) - { - if (m is PlayerMobile pm && (pm.Serial == playerSerial || m.RawName.InsensitiveEquals(playerName))) - { - player = pm; - break; - } - } - - if (player == null) - { - e.Mobile.SendMessage(0x35, $"Player '{playerName}' not found."); - return; - } - - if (player.AccessLevel > AccessLevel.Player) - { - e.Mobile.SendMessage(0x35, "You cannot jail staff members."); - return; - } - - if (IsPlayerJailed(player)) - { - e.Mobile.SendMessage(0x35, $"Player {player.Name} is already jailed."); - return; - } - - JailPlayer(e.Mobile, player, reason); - e.Mobile.SendMessage(0x35, $"Player {player.Name} is being jailed. Reason: {reason}"); - } - - [Usage("Unjail ")] - [Description("Manually releases a player from jail.")] - private static void Unjail_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - if (e.Length < 1) - { - from.SendMessage(0x35, "Usage: [unjail "); - return; - } - - var playerName = e.GetString(0); - var playerSerial = Serial.TryParse(playerName, null, out var serial) ? serial : Serial.MinusOne; - - PlayerMobile player = null; - - foreach (var m in World.Mobiles.Values) - { - if (m is PlayerMobile pm && (pm.Serial == playerSerial || m.RawName.InsensitiveEquals(playerName))) - { - player = pm; - break; - } - } - - if (player == null) - { - from.SendMessage(0x35, $"Player '{playerName}' not found."); - return; - } - - if (!IsPlayerJailed(player)) - { - from.SendMessage(0x35, $"Player {player.Name} is not currently jailed."); - return; - } - - if (JailTimers.Remove(player, out var value)) - { - value.Stop(); - } - - ReleasePlayer(from, player); - from.SendMessage(0x35, $"Player {player.Name} has been manually released from jail."); - } - - [Usage("JailInfo ")] - [Description("Shows jail information for a player.")] - private static void JailInfo_OnCommand(CommandEventArgs e) - { - if (e.Length < 1) - { - e.Mobile.SendMessage(0x35, "Usage: [jailinfo "); - return; - } - - var playerName = e.GetString(0); - var playerSerial = Serial.TryParse(playerName, null, out var serial) ? serial : Serial.MinusOne; - - PlayerMobile player = null; - - foreach (var m in World.Mobiles.Values) - { - if (m is PlayerMobile pm && (pm.Serial == playerSerial || m.RawName.InsensitiveEquals(playerName))) - { - player = pm; - break; - } - } - - if (player == null) - { - e.Mobile.SendMessage(0x35, $"Player '{playerName}' not found."); - return; - } - - e.Mobile.SendGump(new JailRecordGump(player, PlayerJailRecords.GetValueOrDefault(player, EmptyRecord))); - } - - private static readonly Dictionary JailRecordCooldowns = new(); - private static readonly TimeSpan JailRecordCooldown = TimeSpan.FromSeconds(30); - - [Usage("JailRecord")] - [Description("Shows your own jail information.")] - private static void MyJailInfo_OnCommand(CommandEventArgs e) - { - var player = (PlayerMobile)e.Mobile; - - if (JailRecordCooldowns.TryGetValue(player, out var cooldown)) - { - var timeSinceLastUse = Core.Now - cooldown; - - if (timeSinceLastUse < JailRecordCooldown) - { - var remaining = JailRecordCooldown - timeSinceLastUse; - e.Mobile.SendMessage(0x35, $"You must wait {remaining.TotalSeconds:F0} seconds."); - return; - } - } - - JailRecordCooldowns[player] = Core.Now; - - player.SendGump(new JailRecordGump(player, PlayerJailRecords.GetValueOrDefault(player, EmptyRecord))); - } - - public override void Serialize(IGenericWriter writer) - { - writer.WriteEncodedInt(0); // version - writer.WriteEncodedInt(PlayerJailRecords.Count); - foreach (var (m, record) in PlayerJailRecords) - { - writer.Write(m); - record.Serialize(writer); - } - } - - public override void Deserialize(IGenericReader reader) - { - var version = reader.ReadEncodedInt(); - - var count = reader.ReadEncodedInt(); - for (var i = 0; i < count; i++) - { - var player = reader.ReadEntity(); - var record = new JailRecord(); - record.Deserialize(reader); - - if (player != null) - { - PlayerJailRecords[player] = record; - if (record.IsCurrentlyJailed) - { - CurrentlyBeingJailed.Add(player); - var jailTime = record.JailEndTime - Core.Now; - JailTimers[player] = Timer.DelayCall(jailTime, ReleasePlayer, record.JailedBy, player); - } - } - } - } -} diff --git a/Projects/UOContent/Targets/BladedItemTarget.cs b/Projects/UOContent/Targets/BladedItemTarget.cs index c1d7b4c87..acb2e0101 100644 --- a/Projects/UOContent/Targets/BladedItemTarget.cs +++ b/Projects/UOContent/Targets/BladedItemTarget.cs @@ -71,43 +71,47 @@ namespace Server.Targets } } - var system = Lumberjacking.System; + HarvestSystem system = Lumberjacking.System; var def = system.GetDefinition(); - if (!system.GetHarvestDetails(from, m_Item, targeted, out var tileID, out var map, out var loc, out var isLand) - || !def.Validate(tileID, isLand)) + if (!system.GetHarvestDetails(from, m_Item, targeted, out var tileID, out var map, out var loc, out var isLand)) { from.SendLocalizedMessage(500494); // You can't use a bladed item on that! - return; } - - var bank = def.GetBank(map, loc.X, loc.Y); - - if (bank == null) + else if (!def.Validate(tileID, isLand)) { - return; - } - - if (bank.Current < 5) - { - from.SendLocalizedMessage(500493); // There's not enough wood here to harvest. + from.SendLocalizedMessage(500494); // You can't use a bladed item on that! } else { - bank.Consume(5, from); + var bank = def.GetBank(map, loc.X, loc.Y); - var item = new Kindling(); - - if (from.PlaceInBackpack(item)) + if (bank == null) { - from.SendLocalizedMessage(500491); // You put some kindling into your backpack. - from.SendLocalizedMessage(500492); // An axe would probably get you more wood. + return; + } + + if (bank.Current < 5) + { + from.SendLocalizedMessage(500493); // There's not enough wood here to harvest. } else { - from.SendLocalizedMessage(500490); // You can't place any kindling into your backpack! + bank.Consume(5, from); - item.Delete(); + Item item = new Kindling(); + + if (from.PlaceInBackpack(item)) + { + from.SendLocalizedMessage(500491); // You put some kindling into your backpack. + from.SendLocalizedMessage(500492); // An axe would probably get you more wood. + } + else + { + from.SendLocalizedMessage(500490); // You can't place any kindling into your backpack! + + item.Delete(); + } } } } diff --git a/Projects/UOContent/Targets/MoveTarget.cs b/Projects/UOContent/Targets/MoveTarget.cs index 53c2f8fc3..8b344552a 100644 --- a/Projects/UOContent/Targets/MoveTarget.cs +++ b/Projects/UOContent/Targets/MoveTarget.cs @@ -23,7 +23,7 @@ namespace Server.Targets return; } - var p = ip switch + Point3D p = ip switch { Item i => i.GetWorldTop(), Mobile m => m.Location, diff --git a/Projects/UOContent/Text/TextDefinitionExtensions.cs b/Projects/UOContent/Text/TextDefinitionExtensions.cs index bdbdaeaa1..7e463b78a 100644 --- a/Projects/UOContent/Text/TextDefinitionExtensions.cs +++ b/Projects/UOContent/Text/TextDefinitionExtensions.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * + * Copyright 2019-2023 - ModernUO Development Team * * Email: hi@modernuo.com * * File: TextDefinitionExtensions.cs * * * @@ -32,7 +32,7 @@ public static class TextDefinitionExtensions int stringColor = -1 ) { - if (def?.IsEmpty != false) + if (def == null) { return; } @@ -48,28 +48,16 @@ public static class TextDefinitionExtensions builder.AddHtmlLocalized(x, y, width, height, def.Number, back, scroll); } } - else if (stringColor != -1) + else if (def.String != null) { builder.AddHtml( x, y, width, height, - $"{def.String}", - background: back, - scrollbar: scroll - ); - } - else - { - builder.AddHtml( - x, - y, - width, - height, - def.String, - background: back, - scrollbar: scroll + stringColor >= 0 ? def.String.Color(stringColor) : def.String, // 8 bits per RGB component (24 bit RGB) + back, + scroll ); } } @@ -87,7 +75,7 @@ public static class TextDefinitionExtensions int stringColor = -1 ) { - if (def?.IsEmpty != false) + if (def == null) { return; } @@ -103,28 +91,16 @@ public static class TextDefinitionExtensions builder.AddHtmlLocalized(x, y, width, height, def.Number, back, scroll); } } - else if (stringColor != -1) + else if (def.String != null) { builder.AddHtml( x, y, width, height, - $"{def.String}", - background: back, - scrollbar: scroll - ); - } - else - { - builder.AddHtml( - x, - y, - width, - height, - def.String, - background: back, - scrollbar: scroll + stringColor >= 0 ? def.String.Color(stringColor) : def.String, // 8 bits per RGB component (24 bit RGB) + back, + scroll ); } } @@ -142,12 +118,23 @@ public static class TextDefinitionExtensions int stringColor = -1 ) { - if (def?.IsEmpty != false) + if (def == null) { return; } - if (def.Number <= 0) + if (def.Number > 0) + { + if (numberColor >= 0) // 5 bits per RGB component (15 bit RGB) + { + g.AddHtmlLocalized(x, y, width, height, def.Number, numberColor, back, scroll); + } + else + { + g.AddHtmlLocalized(x, y, width, height, def.Number, back, scroll); + } + } + else if (def.String != null) { g.AddHtml( x, @@ -159,14 +146,6 @@ public static class TextDefinitionExtensions scroll ); } - else if (numberColor >= 0) // 5 bits per RGB component (15 bit RGB) - { - g.AddHtmlLocalized(x, y, width, height, def.Number, numberColor, back, scroll); - } - else - { - g.AddHtmlLocalized(x, y, width, height, def.Number, back, scroll); - } } public static void AddHtmlText( @@ -183,7 +162,7 @@ public static class TextDefinitionExtensions int stringColor = -1 ) { - if (def?.IsEmpty != false) + if (def == null) { return; } @@ -193,7 +172,7 @@ public static class TextDefinitionExtensions // 5 bits per RGB component (15 bit RGB) g.AddHtmlLocalized(x, y, width, height, def.Number, args, numberColor >= 0 ? numberColor : 0x7FFF, back, scroll); } - else + else if (def.String != null) { g.AddHtml( x, diff --git a/Projects/UOContent/UOContent.csproj b/Projects/UOContent/UOContent.csproj index 60f28ac6f..f6b8b40d6 100644 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -29,34 +29,28 @@ - - + + - - false - - - - - - + + + + + - + - - + + - - <_Parameter1>UOContent.Tests - diff --git a/Projects/UOContent/Utilities/Types.cs b/Projects/UOContent/Utilities/Types.cs index ca5b9422d..4860f2269 100644 --- a/Projects/UOContent/Utilities/Types.cs +++ b/Projects/UOContent/Utilities/Types.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Concurrent; +using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; @@ -10,8 +10,7 @@ namespace Server { public static readonly Type[] ParseStringParamTypes = { typeof(string), typeof(IFormatProvider) }; public static readonly Type[] ParseStringNumericParamTypes = { typeof(string), typeof(NumberStyles) }; - // Legacy RunUO signature: a static Parse(string) that predates IParsable (e.g. Faction, Town). - public static readonly Type[] ParseStringSingleParamTypes = { typeof(string) }; + private static object[] _parseParams = { null, null }; public static readonly Type OfByte = typeof(byte); public static readonly Type OfSByte = typeof(sbyte); @@ -76,9 +75,7 @@ namespace Server OfULong }; - // Thread-safe: parse metadata is read from parallel callers (e.g. the Advanced Search workers), - // not just the single-threaded command path. - private static readonly ConcurrentDictionary _isParsable = new(); + private static Dictionary _isParsable; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsType(Type type, Type check) => check.IsAssignableFrom(type); @@ -92,19 +89,25 @@ namespace Server [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsText(Type t) => IsType(t, OfText); - public static bool IsParsable(Type t) => - _isParsable.GetOrAdd(t, static type => + public static bool IsParsable(Type t) + { + _isParsable ??= new(); + if (_isParsable.TryGetValue(t, out var isParsable)) { - foreach (var x in type.GetInterfaces()) - { - if (x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IParsable<>)) - { - return true; - } - } + return isParsable; + } - return false; - }); + foreach (var x in t.GetInterfaces()) + { + if (x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IParsable<>)) + { + isParsable = true; + break; + } + } + + return _isParsable[t] = isParsable; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsDecimal(Type t) => Array.IndexOf(DecimalTypes, t) >= 0; @@ -115,81 +118,18 @@ namespace Server [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEntity(Type t) => OfEntity.IsAssignableFrom(t); - private static readonly ConcurrentDictionary _parseMethods = new(); - - // A static string Parse method: the modern IParsable Parse(string, IFormatProvider), or a - // legacy RunUO Parse(string). Cached per type; null if the type has neither. (Span-based Parse - // can't be reflection-invoked — a ReadOnlySpan can't be boxed into the args array — so the - // string overloads are what we bind to.) - public static MethodInfo GetParseMethod(Type t) => - _parseMethods.GetOrAdd( - t, - static type => type.GetMethod("Parse", ParseStringParamTypes) - ?? type.GetMethod("Parse", ParseStringSingleParamTypes) - ); + private static Dictionary _parseMethods; public static object Parse(Type t, string value) { - var method = GetParseMethod(t); - if (method == null) + _parseMethods ??= new(); + if (!_parseMethods.TryGetValue(t, out var method)) { - return null; + _parseMethods[t] = method = t.GetMethod("Parse", ParseStringParamTypes); } - // Fresh args array per call — a shared static array would race across concurrent callers. - // Arg shape depends on which overload we bound to (IParsable 2-arg vs legacy 1-arg). - var args = method.GetParameters().Length == 2 ? new object[] { value, null } : new object[] { value }; - return method.Invoke(null, args); - } - - // Parses directly into the concrete numeric type via INumber.TryParse (the Type-dispatched - // equivalent of a generic TryParse). Returns the boxed value; false if the text doesn't fit - // the type's range/format so the caller can fall through. - private static bool TryParseNumeric(Type type, ReadOnlySpan span, NumberStyles style, out object result) - { - if (type == OfInt && int.TryParse(span, style, null, out var i)) - { - result = i; - return true; - } - if (type == OfUInt && uint.TryParse(span, style, null, out var ui)) - { - result = ui; - return true; - } - if (type == OfLong && long.TryParse(span, style, null, out var l)) - { - result = l; - return true; - } - if (type == OfULong && ulong.TryParse(span, style, null, out var ul)) - { - result = ul; - return true; - } - if (type == OfShort && short.TryParse(span, style, null, out var s)) - { - result = s; - return true; - } - if (type == OfUShort && ushort.TryParse(span, style, null, out var us)) - { - result = us; - return true; - } - if (type == OfByte && byte.TryParse(span, style, null, out var b)) - { - result = b; - return true; - } - if (type == OfSByte && sbyte.TryParse(span, style, null, out var sb)) - { - result = sb; - return true; - } - - result = null; - return false; + _parseParams[0] = value; + return method?.Invoke(null, _parseParams); } // Do not use this in "Parse" methods, it may cause a stack overflow @@ -250,7 +190,7 @@ namespace Server if (IsType(type, OfBool)) { - if (bool.TryParse(value, out var parsed)) + if (bool.TryParse(value, out bool parsed)) { constructed = parsed; return null; @@ -261,38 +201,35 @@ namespace Server if (IsNumeric(type)) { - var span = value.AsSpan(); - var style = NumberStyles.Integer; - if (span.StartsWithOrdinal("0x")) + try { - span = span[2..]; - style = NumberStyles.HexNumber; - } - - if (isEntity || isSerial) - { - // Serial/entity properties were mutated to int above; a Serial is a uint, so parse - // the full 32-bit range as ulong and resolve. - if (ulong.TryParse(span, style, null, out var num)) + var isHex = value.StartsWithOrdinal("0x"); + var index = isHex ? 2 : 0; + if (ulong.TryParse(value.AsSpan(index), isHex ? NumberStyles.HexNumber : NumberStyles.Integer, null, out var num)) { - constructed = isEntity ? World.FindEntity((Serial)num) : (Serial)num; + if (isEntity) + { + constructed = World.FindEntity((Serial)num); + } + else if (isSerial) + { + constructed = (Serial)num; + } + else + { + constructed = Convert.ChangeType(num, type); + } + return null; } } - else if (TryParseNumeric(type, span, style, out constructed)) + catch { - // Parse the string directly into the target type via INumber.TryParse — no - // Convert.ChangeType, and (unlike parse-as-ulong) signed and per-type ranges are honored. - return null; + return "That is not properly formatted."; } - - // On parse failure, fall through to the Parse-method / Convert.ChangeType fallbacks below. } - // IParsable (Parse(string, IFormatProvider)) or a legacy RunUO Parse(string). Gating on - // the discovered method rather than the IParsable interface keeps pre-IParsable types - // (Faction, Town, ...) parseable for backwards compatibility. - if (GetParseMethod(type) != null) + if (IsParsable(type)) { try { diff --git a/Projects/UOContent/World Saves/ArchiveDestinationRegistry.cs b/Projects/UOContent/World Saves/ArchiveDestinationRegistry.cs deleted file mode 100644 index 74219ffea..000000000 --- a/Projects/UOContent/World Saves/ArchiveDestinationRegistry.cs +++ /dev/null @@ -1,34 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ArchiveDestinationRegistry.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; - -namespace Server.Saves; - -/// -/// Registration point for archive destinations. Register during Configure() phase. -/// No locks needed — registration is single-threaded, reading happens on one -/// ThreadPool thread at a time (guarded by the archive concurrency flag). -/// -public static class ArchiveDestinationRegistry -{ - private static readonly List _destinations = []; - - public static IReadOnlyList Destinations => _destinations; - - public static void Register(IArchiveDestination destination) => _destinations.Add(destination); - - public static void Unregister(IArchiveDestination destination) => _destinations.Remove(destination); -} diff --git a/Projects/UOContent/World Saves/ArchiveEventArgs.cs b/Projects/UOContent/World Saves/ArchiveEventArgs.cs deleted file mode 100644 index b1f8db63b..000000000 --- a/Projects/UOContent/World Saves/ArchiveEventArgs.cs +++ /dev/null @@ -1,40 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ArchiveEventArgs.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Saves; - -public class ArchiveCompletedEventArgs( - string archiveFilePath, - ArchivePeriod period, - DateTime rangeStart, - long fileSizeBytes, - double elapsedSeconds -) -{ - public string ArchiveFilePath { get; } = archiveFilePath; - public ArchivePeriod Period { get; } = period; - public DateTime RangeStart { get; } = rangeStart; - public long FileSizeBytes { get; } = fileSizeBytes; - public double ElapsedSeconds { get; } = elapsedSeconds; -} - -public class ArchiveFailedEventArgs(ArchivePeriod period, DateTime rangeStart, Exception exception) -{ - public ArchivePeriod Period { get; } = period; - public DateTime RangeStart { get; } = rangeStart; - public Exception Exception { get; } = exception; -} diff --git a/Projects/UOContent/World Saves/ArchiveJournal.cs b/Projects/UOContent/World Saves/ArchiveJournal.cs deleted file mode 100644 index 0ec7f0e28..000000000 --- a/Projects/UOContent/World Saves/ArchiveJournal.cs +++ /dev/null @@ -1,269 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: ArchiveJournal.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Collections.Generic; -using System.IO; -using Server.Json; -using Server.Logging; - -namespace Server.Saves; - -public enum ArchiveOperationState -{ - Started, - Archived, - Distributed, - Completed, - Failed -} - -public class ArchiveJournalEntry -{ - public string Id { get; set; } - public ArchivePeriod Period { get; set; } - public DateTime RangeStart { get; set; } - public ArchiveOperationState State { get; set; } - public string ArchiveFile { get; set; } - public string TempFile { get; set; } - public List SourceDirectories { get; set; } = []; - public int EntryCount { get; set; } - public long TotalSourceBytes { get; set; } - public long ArchiveBytes { get; set; } - public DateTime StartedAt { get; set; } - public DateTime CompletedAt { get; set; } = DateTime.MaxValue; - public string FailureReason { get; set; } - public Dictionary DestinationResults { get; set; } = new(); -} - -public class ArchiveJournalData -{ - public List Operations { get; set; } = []; -} - -public static class ArchiveJournal -{ - private const int MaxJournalEntries = 100; - - private static readonly ILogger logger = LogFactory.GetLogger(typeof(ArchiveJournal)); - - private static string _journalPath; - private static ArchiveJournalData _data = new(); - - public static IReadOnlyList Operations => _data.Operations; - - public static void Configure(string archivePath) - { - _journalPath = Path.Combine(archivePath, ".archive-journal.json"); - _data = new ArchiveJournalData(); - Load(); - } - - private static void Load() - { - try - { - if (File.Exists(_journalPath)) - { - _data = JsonConfig.Deserialize(_journalPath) ?? new ArchiveJournalData(); - } - } - catch (Exception ex) - { - logger.Error(ex, "Failed to load archive journal, starting fresh"); - _data = new ArchiveJournalData(); - } - } - - private static void Save() - { - try - { - var tempPath = _journalPath + ".tmp"; - JsonConfig.Serialize(tempPath, _data); - - if (File.Exists(_journalPath)) - { - File.Delete(_journalPath); - } - - File.Move(tempPath, _journalPath); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to save archive journal"); - } - } - - private static void Prune() - { - if (_data.Operations.Count > MaxJournalEntries) - { - // Remove oldest completed/failed entries beyond the limit - var toRemove = _data.Operations.Count - MaxJournalEntries; - for (var i = _data.Operations.Count - 1; i >= 0 && toRemove > 0; i--) - { - if (_data.Operations[i].State is ArchiveOperationState.Completed or ArchiveOperationState.Failed) - { - _data.Operations.RemoveAt(i); - toRemove--; - } - } - } - } - - public static ArchiveJournalEntry BeginOperation( - ArchivePeriod period, - DateTime rangeStart, - string tempFile, - string archiveFile, - List sourceDirectories) - { - var entry = new ArchiveJournalEntry - { - Id = $"{rangeStart:O}-{period.ToString().ToLowerInvariant()}", - Period = period, - RangeStart = rangeStart, - State = ArchiveOperationState.Started, - TempFile = tempFile, - ArchiveFile = archiveFile, - SourceDirectories = sourceDirectories, - StartedAt = Core.Now - }; - - _data.Operations.Add(entry); - Prune(); - Save(); - return entry; - } - - public static void RecordArchived(ArchiveJournalEntry entry, int entryCount, long archiveBytes) - { - entry.State = ArchiveOperationState.Archived; - entry.EntryCount = entryCount; - entry.ArchiveBytes = archiveBytes; - entry.TempFile = null; // Temp file has been renamed to final - Save(); - } - - public static void RecordDistributed(ArchiveJournalEntry entry, Dictionary results) - { - entry.State = ArchiveOperationState.Distributed; - entry.DestinationResults = results; - Save(); - } - - public static void RecordCompleted(ArchiveJournalEntry entry) - { - entry.State = ArchiveOperationState.Completed; - entry.CompletedAt = Core.Now; - Save(); - } - - public static void RecordFailure(ArchiveJournalEntry entry, string reason) - { - entry.State = ArchiveOperationState.Failed; - entry.FailureReason = reason; - entry.CompletedAt = Core.Now; - Save(); - } - - /// - /// Recovers from interrupted operations on startup. - /// Handles missing files/directories gracefully (e.g. admin cleaned up archives folder). - /// - public static void RecoverInterrupted() - { - var recovered = false; - - foreach (var entry in _data.Operations) - { - switch (entry.State) - { - case ArchiveOperationState.Started: - { - // Archive creation was interrupted. Clean up temp file if it exists. - if (entry.TempFile != null) - { - try - { - if (File.Exists(entry.TempFile)) - { - File.Delete(entry.TempFile); - } - } - catch (Exception ex) - { - logger.Warning(ex, "Failed to clean up temp file {File}", entry.TempFile); - } - } - - logger.Warning( - "Recovered interrupted archive operation {Id} (was in Started state)", - entry.Id - ); - entry.State = ArchiveOperationState.Failed; - entry.FailureReason = "Interrupted during archive creation"; - entry.CompletedAt = Core.Now; - recovered = true; - break; - } - case ArchiveOperationState.Archived: - { - // Archive was created but not distributed. - // If the archive file was deleted (admin cleanup), mark as failed. - if (entry.ArchiveFile != null && !File.Exists(entry.ArchiveFile)) - { - logger.Warning( - "Archive file for operation {Id} no longer exists (manually deleted?). Marking failed.", - entry.Id - ); - entry.State = ArchiveOperationState.Failed; - entry.FailureReason = "Archive file missing on recovery"; - entry.CompletedAt = Core.Now; - recovered = true; - } - else - { - logger.Warning( - "Found unfinished archive operation {Id} (Archived but not distributed). " + - "Will re-attempt distribution on next cycle.", - entry.Id - ); - } - - break; - } - case ArchiveOperationState.Distributed: - { - // Sources should have been pruned but weren't. - logger.Information( - "Completing interrupted operation {Id} (was Distributed, pruning sources)", - entry.Id - ); - entry.State = ArchiveOperationState.Completed; - entry.CompletedAt = Core.Now; - recovered = true; - break; - } - } - } - - if (recovered) - { - Save(); - } - } -} diff --git a/Projects/UOContent/World Saves/AutoArchive.cs b/Projects/UOContent/World Saves/AutoArchive.cs index aca0f75a9..43579cfe9 100755 --- a/Projects/UOContent/World Saves/AutoArchive.cs +++ b/Projects/UOContent/World Saves/AutoArchive.cs @@ -1,18 +1,3 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: AutoArchive.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - using System; using System.Collections.Generic; using System.Diagnostics; @@ -22,833 +7,336 @@ using System.Threading; using CommunityToolkit.HighPerformance; using Server.Compression; using Server.Logging; -using Server.Text; -namespace Server.Saves; - -public static class AutoArchive +namespace Server.Saves { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(AutoArchive)); - - private static string _tempArchivePath; - private static int _isArchiving; - private static DateTime _nextHourlyArchive; - private static DateTime _nextDailyArchive; - private static DateTime _nextMonthlyArchive; - private static bool _enablePruning; - private static int _compressionLevel; - private static bool _verifyArchives; - private static int _retryCount; - private static int _retryDelayMs; - private static int _backupMaxAgeDays; - - public static event Action ArchiveCompleted; - public static event Action ArchiveFailed; - - public static Action Archive { get; set; } - public static Action Prune { get; set; } - public static string ArchivePath { get; private set; } - public static string BackupPath { get; private set; } - public static string AutomaticBackupPath { get; private set; } - - public static DateTime NextHourlyArchive => _nextHourlyArchive; - public static DateTime NextDailyArchive => _nextDailyArchive; - public static DateTime NextMonthlyArchive => _nextMonthlyArchive; - - public static void Configure() + public enum ArchivePeriod { - var tempArchivePath = ServerConfiguration.GetSetting("autoArchive.tempArchivePath", "temp"); - _tempArchivePath = PathUtility.GetFullPath(tempArchivePath); - - var backupPath = ServerConfiguration.GetOrUpdateSetting("autoArchive.backupPath", "Backups"); - BackupPath = PathUtility.GetFullPath(backupPath); - AutomaticBackupPath = Path.Combine(BackupPath, "Automatic"); - - var archivePath = ServerConfiguration.GetOrUpdateSetting("autoArchive.archivePath", "Archives"); - ArchivePath = PathUtility.GetFullPath(archivePath); - - var useLocalArchives = ServerConfiguration.GetOrUpdateSetting("autoArchive.archiveLocally", true); - _enablePruning = ServerConfiguration.GetOrUpdateSetting("autoArchive.enableArchivePruning", true); - - _compressionLevel = ServerConfiguration.GetOrUpdateSetting("autoArchive.compressionLevel", 3); - _verifyArchives = ServerConfiguration.GetOrUpdateSetting("autoArchive.verifyArchives", true); - _retryCount = ServerConfiguration.GetOrUpdateSetting("autoArchive.retryCount", 3); - _retryDelayMs = ServerConfiguration.GetOrUpdateSetting("autoArchive.retryDelayMs", 500); - _backupMaxAgeDays = ServerConfiguration.GetOrUpdateSetting("autoArchive.backupMaxAge", 30); - - // Configurable retention - var hourlyRetention = ServerConfiguration.GetOrUpdateSetting("autoArchive.hourlyRetention", 24); - var dailyRetention = ServerConfiguration.GetOrUpdateSetting("autoArchive.dailyRetention", 30); - var monthlyRetention = ServerConfiguration.GetOrUpdateSetting("autoArchive.monthlyRetention", 12); - - // Register local filesystem destination - if (useLocalArchives) - { - ArchiveDestinationRegistry.Register( - new LocalArchiveDestination(hourlyRetention, dailyRetention, monthlyRetention) - ); - Archive = AutoArchiveLocally; - } - - if (_enablePruning) - { - Prune = PruneBackups; - } - - // Initialize journal and recover interrupted operations - ArchiveJournal.Configure(ArchivePath); - ArchiveJournal.RecoverInterrupted(); - - // Restores an archive file placed in the Saves folder - RestoreFromArchive(); - - // If no valid save data exists, offer to restore from backups/archives - if (!HasValidSaveData()) - { - PromptRestoreFromBackup(); - } - - DateTimeOffset now = Core.Now; - var date = now.Date; - _nextHourlyArchive = date.AddHours(now.Hour); - _nextDailyArchive = date; - _nextMonthlyArchive = date.AddDays(1 - now.Day); - - // Do a local archive rollup & prune - AutoArchiveLocally(); + Hourly, + Daily, + Monthly } - public static void Initialize() + public enum CompressionFormat { - EventSink.WorldSavePostSnapshot += Backup; + None, + Zip, + GZip, + Zstd, } - private const string BackupCompleteMarker = ".backup-complete"; - - private static void Backup(WorldSavePostSnapshotEventArgs args) + public static class AutoArchive { - if (!Directory.Exists(args.OldSavePath)) + private static readonly ILogger logger = LogFactory.GetLogger(typeof(AutoArchive)); + + private static string _tempArchivePath; + private static int _isArchiving; + private static DateTime _nextHourlyArchive; + private static DateTime _nextDailyArchive; + private static DateTime _nextMonthlyArchive; + private static CompressionFormat _compressionFormat; + private static bool _enablePruning; + + public static Action Archive { get; set; } + public static Action Prune { get; set; } + public static string ArchivePath { get; private set; } + public static string BackupPath { get; private set; } + public static string AutomaticBackupPath { get; private set; } + + public static void Configure() { - return; - } + var tempArchivePath = ServerConfiguration.GetSetting("autoArchive.tempArchivePath", "temp"); + _tempArchivePath = PathUtility.GetFullPath(tempArchivePath); - Directory.CreateDirectory(AutomaticBackupPath); - var backupPath = Path.Combine(AutomaticBackupPath, Utility.GetTimeStamp()); - PathUtility.MoveDirectoryContents(args.OldSavePath, backupPath); + var backupPath = ServerConfiguration.GetOrUpdateSetting("autoArchive.backupPath", "Backups"); + BackupPath = PathUtility.GetFullPath(backupPath); + AutomaticBackupPath = Path.Combine(BackupPath, "Automatic"); - // Write a marker file so Rollup knows this backup is complete and not mid-write. - // This prevents archiving a partially-moved directory if Rollup runs concurrently. - File.WriteAllBytes(Path.Combine(backupPath, BackupCompleteMarker), []); + var archivePath = ServerConfiguration.GetOrUpdateSetting("autoArchive.archivePath", "Archives"); + ArchivePath = PathUtility.GetFullPath(archivePath); - logger.Information("Created backup at {Path}", backupPath); + var useLocalArchives = ServerConfiguration.GetOrUpdateSetting("autoArchive.archiveLocally", true); + _enablePruning = ServerConfiguration.GetOrUpdateSetting("autoArchive.enableArchivePruning", true); - Archive?.Invoke(); - } + _compressionFormat = ServerConfiguration.GetOrUpdateSetting("autoArchive.compressionFormat", CompressionFormat.Zstd); - private static void RestoreFromArchive() - { - var savePath = Path.Combine(Core.BaseDirectory, ServerConfiguration.GetSetting("world.savePath", "Saves")); - if (!Directory.Exists(savePath)) - { - return; - } - - foreach (var file in PathsByTimestampName(savePath, true)) - { - if (RestoreFromFile(file, savePath)) + if (useLocalArchives) { - File.Delete(file); - break; + Archive = AutoArchiveLocally; + } + + if (_enablePruning) + { + Prune = PruneBackups; + } + + // Restores an archive file placed in the Saves folder. Supports all compression formats. + RestoreFromArchive(); + + DateTimeOffset now = Core.Now; + var date = now.Date; + _nextHourlyArchive = date.AddHours(now.Hour); + _nextDailyArchive = date; + _nextMonthlyArchive = date.AddDays(1 - now.Day); + + // Do a local archive rollup & prune + AutoArchiveLocally(); + } + + public static void Initialize() + { + EventSink.WorldSavePostSnapshot += Backup; + } + + private static void Backup(WorldSavePostSnapshotEventArgs args) + { + if (!Directory.Exists(args.OldSavePath)) + { + return; + } + + Directory.CreateDirectory(AutomaticBackupPath); + var backupPath = Path.Combine(AutomaticBackupPath, Utility.GetTimeStamp()); + PathUtility.MoveDirectoryContents(args.OldSavePath, backupPath); + + logger.Information("Created backup at {Path}", backupPath); + + Archive?.Invoke(); + } + + private static void RestoreFromArchive() + { + var savePath = Path.Combine(Core.BaseDirectory, ServerConfiguration.GetSetting("world.savePath", "Saves")); + if (!Directory.Exists(savePath)) + { + return; + } + + foreach (var file in PathsByTimestampName(savePath, true)) + { + if (RestoreFromFile(file, savePath)) + { + File.Delete(file); + break; + } } } - } - private static bool RestoreFromFile(string file, string savePath) - { - var fi = new FileInfo(file); - var fileName = fi.Name; - - if (!TryGetDate(fileName[..fileName.IndexOfOrdinal(".")], out _)) + private static bool RestoreFromFile(string file, string savePath) { - return false; - } + var fi = new FileInfo(file); + var fileName = fi.Name; - logger.Information("Restoring world save from archive {File}", fileName); - - var tempPath = PathUtility.EnsureRandomPath(_tempArchivePath); - try - { - if (!ExtractArchive(fi.FullName, tempPath)) + if (!TryGetDate(fileName[..fileName.IndexOfOrdinal(".")], out _)) { return false; } - return PromptAndRestoreFromExtractedArchive(tempPath, savePath); - } - finally - { - CleanupTempDirectory(tempPath); - } - } + logger.Information("Restoring latest world save from archive {File}", fileName); - /// - /// Checks if the Saves directory contains valid world save data. - /// - private static bool HasValidSaveData() - { - var savePath = Path.Combine(Core.BaseDirectory, ServerConfiguration.GetSetting("world.savePath", "Saves")); - if (!Directory.Exists(savePath)) - { - return false; - } + var tempPath = PathUtility.EnsureRandomPath(_tempArchivePath); + var successful = fileName.EndsWithOrdinal(".tar.zst") + ? ZstdArchive.ExtractToDirectory(fi.FullName, tempPath) + : TarArchive.ExtractToDirectory(fi.FullName, tempPath); + + if (!successful) + { + logger.Information("Failed to extract {File}", fi.Name); + return false; + } + + foreach (var folder in PathsByTimestampName(tempPath)) + { + Directory.Delete(savePath, true); + var dirInfo = new DirectoryInfo(folder); + logger.Information("Restoring backup {Directory}", dirInfo.Name); + PathUtility.MoveDirectoryContents(folder, savePath); + break; + } + + Directory.Delete(tempPath, true); - // A valid save has SerializedTypes.db or at least one .bin/.idx file - if (File.Exists(Path.Combine(savePath, "SerializedTypes.db"))) - { return true; } - foreach (var dir in Directory.EnumerateDirectories(savePath)) + public static void PruneBackups() { - foreach (var file in Directory.EnumerateFiles(dir)) + if (Directory.Exists(ArchivePath)) { - if (file.EndsWithOrdinal(".bin") || file.EndsWithOrdinal(".idx")) - { - return true; - } + // Maintain a total of 66 of the most recent archives + PruneLocalArchives(ArchivePeriod.Hourly, 24); + PruneLocalArchives(ArchivePeriod.Daily, 30); + PruneLocalArchives(ArchivePeriod.Monthly, 12); } - } - return false; - } - - /// - /// Presents an interactive console menu to restore from a backup directory or archive file. - /// Only called during Configure() when no valid save data exists. - /// At this point, Console.ReadLine() works directly (ConsoleInputHandler not yet initialized). - /// - private static void PromptRestoreFromBackup() - { - // Collect available restore points: backup directories and archive files - var restorePoints = new SortedDictionary( - new DescendingComparer() - ); - - // Scan backup directories - if (Directory.Exists(AutomaticBackupPath)) - { - foreach (var dir in Directory.EnumerateDirectories(AutomaticBackupPath)) + if (!Directory.Exists(AutomaticBackupPath)) { - if (!File.Exists(Path.Combine(dir, BackupCompleteMarker))) + return; + } + + var allFolders = Directory.EnumerateDirectories(AutomaticBackupPath); + var threshold = Core.Now.AddMonths(-1); + + foreach (var folder in allFolders) + { + var dirName = new DirectoryInfo(folder).Name; + + if (!TryGetDate(dirName, out var date)) { continue; } - var dirName = new DirectoryInfo(dir).Name; - if (TryGetDate(dirName, out var date)) + if (date < threshold) { - restorePoints[date] = (dir, $"Backup: {dirName}", false); + logger.Information("Pruning old backup {Directory}", folder); + Directory.Delete(folder, true); } } } - // Scan archive files (hourly, daily, monthly) - if (Directory.Exists(ArchivePath)) + private static void PruneLocalArchives(ArchivePeriod period, int minRetained) { - foreach (var periodDir in Directory.EnumerateDirectories(ArchivePath)) + var periodStr = period.ToString(); + var archivePath = Path.Combine(ArchivePath, periodStr); + if (!Directory.Exists(archivePath)) { - var periodName = new DirectoryInfo(periodDir).Name; - foreach (var file in Directory.EnumerateFiles(periodDir)) - { - var fi = new FileInfo(file); - var nameWithoutExt = fi.Name; - var dotIndex = nameWithoutExt.IndexOfOrdinal('.'); - if (dotIndex > 0) - { - nameWithoutExt = nameWithoutExt[..dotIndex]; - } - - if (TryGetDate(nameWithoutExt, out var date)) - { - restorePoints[date] = (file, $"{periodName} archive: {fi.Name}", true); - } - } - } - } - - if (restorePoints.Count == 0) - { - return; - } - - // Build flat list from sorted dictionary - var entries = new List<(string path, string label, bool isArchive, DateTime date)>(); - foreach (var (date, (path, label, isArchive)) in restorePoints) - { - entries.Add((path, label, isArchive, date)); - } - - // Interactive paginated menu - const int pageSize = 20; - var page = 0; - var totalPages = (entries.Count + pageSize - 1) / pageSize; - using var sb = ValueStringBuilder.Create(); - - while (true) - { - Console.WriteLine(); - Utility.PushColor(ConsoleColor.Yellow); - Console.WriteLine("No world save data found."); - Utility.PopColor(); - Console.Write($"Available backups and archives ({entries.Count} total"); - if (totalPages > 1) - { - Console.Write($", page {page + 1}/{totalPages}"); - } - - Console.WriteLine("):"); - Console.WriteLine(); - - var start = page * pageSize; - var end = Math.Min(start + pageSize, entries.Count); - - for (var i = start; i < end; i++) - { - var (_, label, _, date) = entries[i]; - Console.Write($" {i + 1,3}) "); - Utility.PushColor(ConsoleColor.Cyan); - Console.Write($"{date:yyyy-MM-dd HH:mm:ss}"); - Utility.PopColor(); - Console.WriteLine($" {label}"); - } - - Console.WriteLine(); - sb.Reset(); - sb.Append("Enter number to restore"); - if (page < totalPages - 1) - { - sb.Append(", [n]ext page"); - } - - if (page > 0) - { - sb.Append(", [p]rev page"); - } - - sb.Append(", or Enter to start fresh: "); - Console.Write(sb.ToString()); - - var input = Console.ReadLine(); - - if (string.IsNullOrWhiteSpace(input)) - { - Console.WriteLine("Starting with no world save data."); return; } - if (input.InsensitiveEquals("n") && page < totalPages - 1) + var periodLowerStr = periodStr.ToLowerInvariant(); + foreach (var archive in PathsByTimestampName(archivePath, true)) { - page++; - continue; - } - - if (input.InsensitiveEquals("p") && page > 0) - { - page--; - continue; - } - - if (int.TryParse(input, out var choice) && choice >= 1 && choice <= entries.Count) - { - var selected = entries[choice - 1]; - Console.Write("Restoring "); - Utility.PushColor(ConsoleColor.Green); - Console.Write(selected.label); - Utility.PopColor(); - Console.WriteLine("..."); - - var savePath = Path.Combine( - Core.BaseDirectory, - ServerConfiguration.GetSetting("world.savePath", "Saves") - ); - - if (selected.isArchive) + if (minRetained > 0) { - RestoreFromArchiveFile(selected.path, savePath); - } - else - { - RestoreFromBackupDirectory(selected.path, savePath); + minRetained--; + continue; } + var fi = new FileInfo(archive); + logger.Information("Pruning {Period} archive {File}", periodLowerStr, fi.Name); + File.Delete(archive); + } + } + + public static void AutoArchiveLocally() + { + var date = Core.Now; + + if (date < _nextHourlyArchive && date < _nextDailyArchive && date < _nextMonthlyArchive) + { return; } - Console.WriteLine("Invalid selection. Try again."); - } - } - - private static void RestoreFromArchiveFile(string archiveFile, string savePath) - { - var tempPath = PathUtility.EnsureRandomPath(_tempArchivePath); - try - { - if (!ExtractArchive(archiveFile, tempPath)) + if (Interlocked.CompareExchange(ref _isArchiving, 0, 1) == 1) { - Utility.PushColor(ConsoleColor.Red); - Console.WriteLine($"Failed to extract {new FileInfo(archiveFile).Name}."); - Utility.PopColor(); return; } - PromptAndRestoreFromExtractedArchive(tempPath, savePath); - } - finally - { - CleanupTempDirectory(tempPath); - } - } - - private static void RestoreFromBackupDirectory(string backupDir, string savePath) - { - if (Directory.Exists(savePath)) - { - Directory.Delete(savePath, true); - } - - Directory.CreateDirectory(savePath); - PathUtility.CopyDirectoryContents(backupDir, savePath); - logger.Information("Restored world save from backup {Directory}", new DirectoryInfo(backupDir).Name); - } - - /// - /// Extracts an archive file (.tar.zst, .tar.gz, .tar) to a directory. - /// - private static bool ExtractArchive(string archiveFile, string outputDirectory) - { - var fileName = new FileInfo(archiveFile).Name; - - if (fileName.EndsWithOrdinal(".tar.zst")) - { - return ManagedArchive.ExtractTarZstd(archiveFile, outputDirectory); - } - - if (fileName.EndsWithOrdinal(".tar.gz")) - { - return ManagedArchive.ExtractTarGz(archiveFile, outputDirectory); - } - - if (fileName.EndsWithOrdinal(".tar")) - { - return ManagedArchive.ExtractTar(archiveFile, outputDirectory); - } - - logger.Warning("Unsupported archive format: {File}", fileName); - return false; - } - - /// - /// Lists backup directories inside an extracted archive and lets the user choose which to restore. - /// If only one backup exists, restores it directly. - /// - private static bool PromptAndRestoreFromExtractedArchive(string extractedPath, string savePath) - { - var backups = new List<(DateTime date, string path, string name)>(); - foreach (var dir in Directory.EnumerateDirectories(extractedPath)) - { - var dirName = new DirectoryInfo(dir).Name; - if (TryGetDate(dirName, out var date)) - { - backups.Add((date, dir, dirName)); - } - } - - if (backups.Count == 0) - { - Utility.PushColor(ConsoleColor.Red); - Console.WriteLine("No valid backup directories found inside the archive."); - Utility.PopColor(); - return false; - } - - // Sort newest first - backups.Sort((a, b) => b.date.CompareTo(a.date)); - - string selectedPath; - - if (backups.Count == 1) - { - selectedPath = backups[0].path; - logger.Information("Restoring backup {Directory}", backups[0].name); - } - else - { - // Multiple backups — let the user choose - Console.WriteLine(); - Console.WriteLine($"Archive contains {backups.Count} backups:"); - Console.WriteLine(); - - for (var i = 0; i < backups.Count; i++) - { - Console.Write($" {i + 1,3}) "); - Utility.PushColor(ConsoleColor.Cyan); - Console.Write($"{backups[i].date:yyyy-MM-dd HH:mm:ss}"); - Utility.PopColor(); - Console.WriteLine($" {backups[i].name}"); - } - - Console.WriteLine(); - Console.Write("[1]> "); - var input = Console.ReadLine(); - - int choice; - if (string.IsNullOrWhiteSpace(input)) - { - choice = 1; // Default to most recent - } - else if (!int.TryParse(input, out choice) || choice < 1 || choice > backups.Count) - { - Console.WriteLine("Invalid selection, using most recent."); - choice = 1; - } - - selectedPath = backups[choice - 1].path; - logger.Information("Restoring backup {Directory}", backups[choice - 1].name); - } - - if (Directory.Exists(savePath)) - { - Directory.Delete(savePath, true); - } - - Directory.CreateDirectory(savePath); - PathUtility.MoveDirectoryContents(selectedPath, savePath); - return true; - } - - private static void CleanupTempDirectory(string tempPath) - { - try - { - if (Directory.Exists(tempPath)) - { - Directory.Delete(tempPath, true); - } - } - catch (Exception ex) - { - logger.Warning(ex, "Failed to clean up temp directory {Path}", tempPath); - } - } - - public static void PruneBackups() - { - if (Directory.Exists(ArchivePath)) - { - // Get retention counts from registered destinations - var hourlyRetention = GetMaxRetention(ArchivePeriod.Hourly); - var dailyRetention = GetMaxRetention(ArchivePeriod.Daily); - var monthlyRetention = GetMaxRetention(ArchivePeriod.Monthly); - - PruneLocalArchives(ArchivePeriod.Hourly, hourlyRetention); - PruneLocalArchives(ArchivePeriod.Daily, dailyRetention); - PruneLocalArchives(ArchivePeriod.Monthly, monthlyRetention); - } - - if (!Directory.Exists(AutomaticBackupPath)) - { - return; - } - - var allFolders = Directory.EnumerateDirectories(AutomaticBackupPath); - var threshold = Core.Now.AddDays(-_backupMaxAgeDays); - - foreach (var folder in allFolders) - { - // Skip backup directories that are still being written - if (!File.Exists(Path.Combine(folder, BackupCompleteMarker))) - { - continue; - } - - var dirName = new DirectoryInfo(folder).Name; - - if (!TryGetDate(dirName, out var date)) - { - continue; - } - - if (date < threshold) - { - logger.Information("Pruning old backup {Directory}", folder); - RetryFileOperation(() => Directory.Delete(folder, true)); - } - } - } - - private static int GetMaxRetention(ArchivePeriod period) - { - var max = 0; - for (var i = 0; i < ArchiveDestinationRegistry.Destinations.Count; i++) - { - var dest = ArchiveDestinationRegistry.Destinations[i]; - var count = dest.GetRetentionCount(period); - if (count > max) - { - max = count; - } - } - - return max; - } - - private static void PruneLocalArchives(ArchivePeriod period, int minRetained) - { - var periodStr = period.ToString(); - var archivePath = Path.Combine(ArchivePath, periodStr); - if (!Directory.Exists(archivePath)) - { - return; - } - - var periodLowerStr = periodStr.ToLowerInvariant(); - foreach (var archive in PathsByTimestampName(archivePath, true)) - { - if (minRetained > 0) - { - minRetained--; - continue; - } - - var fi = new FileInfo(archive); - logger.Information("Pruning {Period} archive {File}", periodLowerStr, fi.Name); - RetryFileOperation(() => File.Delete(archive)); - } - } - - public static void AutoArchiveLocally() - { - var date = Core.Now; - - if (date < _nextHourlyArchive && date < _nextDailyArchive && date < _nextMonthlyArchive) - { - return; - } - - // Fixed: was CompareExchange(ref _isArchiving, 0, 1) which never guarded - if (Interlocked.CompareExchange(ref _isArchiving, 1, 0) == 1) - { - logger.Debug("Archive operation already in progress, skipping this cycle"); - return; - } - - ThreadPool.QueueUserWorkItem( - _ => - { - try + ThreadPool.QueueUserWorkItem( + now => { - // Use current time for schedule checks (not the captured time from the caller) - // to avoid re-processing periods that elapsed during a long archive operation. - var now = Core.Now; - if (now >= _nextHourlyArchive) { Rollup(ArchivePeriod.Hourly); - AdvanceSchedule(ref _nextHourlyArchive, now, static dt => dt.AddHours(1)); + _nextHourlyArchive = _nextHourlyArchive.AddHours(1); } if (now >= _nextDailyArchive) { Rollup(ArchivePeriod.Daily); - AdvanceSchedule(ref _nextDailyArchive, now, static dt => dt.AddDays(1)); + _nextDailyArchive = _nextDailyArchive.AddDays(1); } if (now >= _nextMonthlyArchive) { Rollup(ArchivePeriod.Monthly); - AdvanceSchedule(ref _nextMonthlyArchive, now, static dt => dt.AddMonths(1)); + _nextMonthlyArchive = _nextMonthlyArchive.AddMonths(1); } Prune?.Invoke(); - } - catch (Exception ex) - { - logger.Error(ex, "Archive operation failed"); - } - finally - { _isArchiving = 0; - } - } - ); - } - - /// - /// Forces an immediate rollup regardless of schedule. - /// - public static void ForceRollup() - { - if (Interlocked.CompareExchange(ref _isArchiving, 1, 0) == 1) - { - logger.Warning("Cannot force rollup — an archive operation is already in progress"); - return; - } - - ThreadPool.QueueUserWorkItem( - _ => - { - try - { - Rollup(ArchivePeriod.Hourly); - Rollup(ArchivePeriod.Daily); - Rollup(ArchivePeriod.Monthly); - Prune?.Invoke(); - } - catch (Exception ex) - { - logger.Error(ex, "Forced archive operation failed"); - } - finally - { - _isArchiving = 0; - } - } - ); - } - - /// - /// Advances a schedule marker past the current time to prevent redundant catchup cycles - /// after a long-running archive operation. - /// - private static void AdvanceSchedule(ref DateTime schedule, DateTime now, Func advance) - { - while (schedule <= now) - { - schedule = advance(schedule); - } - } - - private static void Rollup(ArchivePeriod archivePeriod) - { - if (!Directory.Exists(AutomaticBackupPath)) - { - return; - } - - var currentRangeStart = Core.Now.ArchivePeriodStart(archivePeriod); - - var items = new Dictionary>(); - foreach (var path in Directory.EnumerateDirectories(AutomaticBackupPath)) - { - // Skip backup directories that are still being written (no completion marker) - if (!File.Exists(Path.Combine(path, BackupCompleteMarker))) - { - continue; - } - - var dirName = new DirectoryInfo(path).Name; - - if (!TryGetDate(dirName, out var date)) - { - continue; - } - - var rangeStart = date.ArchivePeriodStart(archivePeriod); - - // Only archive the past - if (rangeStart >= currentRangeStart) - { - continue; - } - - if (items.TryGetValue(rangeStart, out var value)) - { - value.Add(date, path); - } - else - { - value = new SortedDictionary(new DescendingComparer()) - { - { date, path } - }; - - items.Add(rangeStart, value); - } - } - - var archivePeriodStr = archivePeriod.ToString(); - var archivePath = PathUtility.EnsureDirectory(Path.Combine(ArchivePath, archivePeriodStr)); - var archivePeriodStrLower = archivePeriodStr.ToLowerInvariant(); - - // Leave behind 1 hourly for daily, 1 daily for monthly. - var minimum = archivePeriod != ArchivePeriod.Monthly ? 1 : 0; - - foreach (var (rangeStart, sortedBackups) in items) - { - var backups = sortedBackups.Values; - if (backups.Count <= minimum) - { - continue; - } - - var stopWatch = Stopwatch.StartNew(); - - var fileName = $"{rangeStart.ToTimeStamp(archivePeriod)}.tar.zst"; - var archiveFilePath = Path.Combine(archivePath, fileName); - - // Journal: record start - var sourceList = new List(backups); - var journalEntry = ArchiveJournal.BeginOperation( - archivePeriod, rangeStart, archiveFilePath + ".tmp", archiveFilePath, sourceList + }, + date, + false ); + } - // Create archive using managed streaming compression - var entryCount = ManagedArchive.CreateTarZstd( - backups, archiveFilePath, AutomaticBackupPath, _compressionLevel - ); - - if (entryCount >= 0) + private static void Rollup(ArchivePeriod archivePeriod) + { + if (!Directory.Exists(AutomaticBackupPath)) { - // Verify if enabled - if (_verifyArchives) + return; + } + + var currentRangeStart = Core.Now.ArchivePeriodStart(archivePeriod); + var allFolders = Directory.EnumerateDirectories(AutomaticBackupPath); + + var items = new Dictionary>(); + foreach (var path in allFolders) + { + var dirName = new DirectoryInfo(path).Name; + + if (!TryGetDate(dirName, out var date)) { - var verifiedCount = ManagedArchive.CountEntries(archiveFilePath); - if (verifiedCount != entryCount) + continue; + } + + var rangeStart = date.ArchivePeriodStart(archivePeriod); + + // Only archive the past + if (rangeStart >= currentRangeStart) + { + continue; + } + + if (items.TryGetValue(rangeStart, out var value)) + { + value.Add(date, path); + } + else + { + value = new SortedDictionary(new DescendingComparer()) { - logger.Warning( - "Archive verification failed for {Path}: expected {Expected} entries, got {Actual}", - archiveFilePath, entryCount, verifiedCount - ); - ArchiveJournal.RecordFailure(journalEntry, $"Verification failed: expected {entryCount}, got {verifiedCount}"); + { date, path } + }; - ArchiveFailed?.Invoke(new ArchiveFailedEventArgs( - archivePeriod, rangeStart, new InvalidOperationException("Archive verification failed") - )); - continue; - } + items.Add(rangeStart, value); + } + } + + var archivePeriodStr = archivePeriod.ToString(); + var archivePath = PathUtility.EnsureDirectory(Path.Combine(ArchivePath, archivePeriodStr)); + var archivePeriodStrLower = archivePeriodStr.ToLowerInvariant(); + var extension = _compressionFormat.GetFileExtension(); + + // Leave behind 1 hourly for daily, 1 daily for monthly. + var minimum = archivePeriod != ArchivePeriod.Monthly ? 1 : 0; + + foreach (var (rangeStart, sortedBackups) in items) + { + var backups = sortedBackups.Values; + if (backups.Count <= minimum) + { + continue; } - var archiveSize = new FileInfo(archiveFilePath).Length; - ArchiveJournal.RecordArchived(journalEntry, entryCount, archiveSize); + var stopWatch = new Stopwatch(); + stopWatch.Start(); - stopWatch.Stop(); - logger.Information( - "Created {Period} archive at {Path} ({EntryCount} entries, {Size:F1} MB, {Elapsed:F2}s)", - archivePeriodStrLower, - archiveFilePath, - entryCount, - archiveSize / 1048576.0, - stopWatch.Elapsed.TotalSeconds - ); + var fileName = $"{rangeStart.ToTimeStamp(archivePeriod)}{extension}"; + var archiveFilePath = Path.Combine(archivePath, fileName); - // Distribute to registered destinations - var allDestinationsSucceeded = DistributeToDestinations( - archiveFilePath, archivePeriod, rangeStart, journalEntry - ); + var archiveCreated = CreateArchive(archiveFilePath, AutomaticBackupPath, backups); - // Only delete source backups if all destinations with retention succeeded - if (allDestinationsSucceeded) + if (archiveCreated) { + logger.Information( + "Created {Period} archive at {Path} ({Elapsed:F2} seconds)", + archivePeriodStrLower, + archiveFilePath, + stopWatch.Elapsed.TotalSeconds + ); + var i = minimum; foreach (var backup in backups) { @@ -858,196 +346,120 @@ public static class AutoArchive continue; } - RetryFileOperation(() => Directory.Delete(backup, true)); + Directory.Delete(backup, true); } - - ArchiveJournal.RecordCompleted(journalEntry); } else { - logger.Warning( - "Not pruning source backups for {Period} archive — some destinations failed", - archivePeriodStrLower - ); + logger.Warning($"Failed to create {archivePeriodStrLower} archive"); } - ArchiveCompleted?.Invoke(new ArchiveCompletedEventArgs( - archiveFilePath, archivePeriod, rangeStart, archiveSize, stopWatch.Elapsed.TotalSeconds - )); - } - else - { stopWatch.Stop(); - logger.Warning("Failed to create {Period} archive", archivePeriodStrLower); - ArchiveJournal.RecordFailure(journalEntry, "Archive creation failed"); - - ArchiveFailed?.Invoke(new ArchiveFailedEventArgs( - archivePeriod, rangeStart, new InvalidOperationException("Archive creation failed") - )); } } - } - private static bool DistributeToDestinations( - string archiveFilePath, - ArchivePeriod period, - DateTime rangeStart, - ArchiveJournalEntry journalEntry) - { - var destinations = ArchiveDestinationRegistry.Destinations; - if (destinations.Count == 0) - { - ArchiveJournal.RecordDistributed(journalEntry, new Dictionary()); - return true; - } + private static bool CreateArchive(string archiveFilePath, string relativeTo, IEnumerable backups) => + _compressionFormat == CompressionFormat.Zstd + ? ZstdArchive.CreateFromPaths(backups, archiveFilePath, relativeTo) + : TarArchive.CreateFromPaths(backups, archiveFilePath, relativeTo); - var results = new Dictionary(destinations.Count); - var allSucceeded = true; - - foreach (var dest in destinations) - { - try + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static string ToTimeStamp(this DateTime date, ArchivePeriod archivePeriod) => + archivePeriod switch { - var success = dest.SendArchive(archiveFilePath, period, rangeStart); - results[dest.Name] = success; + ArchivePeriod.Monthly => date.ToString("yyyy-MM"), + ArchivePeriod.Daily => date.ToString("yyyy-MM-dd"), + ArchivePeriod.Hourly => date.ToString("yyyy-MM-dd-HH"), + _ => date.ToTimeStamp() + }; - if (success) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static DateTime ArchivePeriodStart(this DateTime date, ArchivePeriod archivePeriod) => + archivePeriod switch + { + ArchivePeriod.Monthly => date.Date.AddDays(-(date.Day - 1)), + ArchivePeriod.Daily => date.Date, + ArchivePeriod.Hourly => date.Date.AddHours(date.Hour), + _ => date + }; + + private static IEnumerable PathsByTimestampName(string path, bool files = false) + { + var allItems = files ? Directory.EnumerateFiles(path) : Directory.GetDirectories(path); + var items = new SortedDictionary(new DescendingComparer()); + foreach (var item in allItems) + { + string name; + if (files) { - logger.Debug("Archive sent to destination {Destination}", dest.Name); + var fileName = new FileInfo(item).Name; + name = fileName[..fileName.IndexOfOrdinal('.')]; } else { - logger.Warning("Destination {Destination} rejected archive", dest.Name); - if (dest.GetRetentionCount(period) > 0) - { - allSucceeded = false; - } + name = new DirectoryInfo(item).Name; } - } - catch (Exception ex) - { - logger.Error(ex, "Failed to send archive to destination {Destination}", dest.Name); - results[dest.Name] = false; - if (dest.GetRetentionCount(period) > 0) + + if (TryGetDate(name, out var date)) { - allSucceeded = false; + // Might give wrong results if there is a file that matches: + // Example: 2021-09-01, and 2021-09-01-00 + items[date] = item; } } + + return items.Values; } - ArchiveJournal.RecordDistributed(journalEntry, results); - return allSucceeded; - } - - private static void RetryFileOperation(Action operation) - { - for (var attempt = 0; attempt < _retryCount; attempt++) + private static bool TryGetDate(string value, out DateTime date) { + Span parts = stackalloc int[] { 0, 1, 1, 0, 0, 0 }; + try { - operation(); - return; - } - catch (Exception ex) when (attempt < _retryCount - 1 && ex is IOException or UnauthorizedAccessException) - { - logger.Debug( - ex, - "File operation failed on attempt {Attempt}/{Max}, retrying", - attempt + 1, - _retryCount - ); - Thread.Sleep(_retryDelayMs * (attempt + 1)); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string ToTimeStamp(this DateTime date, ArchivePeriod archivePeriod) => - archivePeriod switch - { - ArchivePeriod.Monthly => date.ToString("yyyy-MM"), - ArchivePeriod.Daily => date.ToString("yyyy-MM-dd"), - ArchivePeriod.Hourly => date.ToString("yyyy-MM-dd-HH"), - _ => date.ToTimeStamp() - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static DateTime ArchivePeriodStart(this DateTime date, ArchivePeriod archivePeriod) => - archivePeriod switch - { - ArchivePeriod.Monthly => date.Date.AddDays(-(date.Day - 1)), - ArchivePeriod.Daily => date.Date, - ArchivePeriod.Hourly => date.Date.AddHours(date.Hour), - _ => date - }; - - internal static SortedDictionary.ValueCollection PathsByTimestampName( - string path, bool files = false) - { - var allItems = files ? Directory.EnumerateFiles(path) : Directory.GetDirectories(path); - var items = new SortedDictionary(new DescendingComparer()); - foreach (var item in allItems) - { - string name; - if (files) - { - var fileName = new FileInfo(item).Name; - name = fileName[..fileName.IndexOfOrdinal('.')]; - } - else - { - name = new DirectoryInfo(item).Name; - } - - if (TryGetDate(name, out var date)) - { - // Might give wrong results if there is a file that matches: - // Example: 2021-09-01, and 2021-09-01-00 - items[date] = item; - } - } - - return items.Values; - } - - private static bool TryGetDate(string value, out DateTime date) - { - Span parts = [0, 1, 1, 0, 0, 0]; - - try - { - var i = 0; - foreach (var part in value.Tokenize('-')) - { - if (!int.TryParse(part, out var partValue)) + var i = 0; + foreach (var part in value.Tokenize('-')) { - break; + if (!int.TryParse(part, out var partValue)) + { + break; + } + + parts[i++] = partValue; } - parts[i++] = partValue; - } + if (i == 0) + { + date = DateTime.MinValue; + return false; + } - if (i == 0) + date = new DateTime( + parts[0], + parts[1], + parts[2], + parts[3], + parts[4], + parts[5], + DateTimeKind.Utc + ); + return true; + } + catch { date = DateTime.MinValue; return false; } + } - date = new DateTime( - parts[0], - parts[1], - parts[2], - parts[3], - parts[4], - parts[5], - DateTimeKind.Utc - ); - return true; - } - catch - { - date = DateTime.MinValue; - return false; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static string GetFileExtension(this CompressionFormat compressionFormat) => + compressionFormat switch + { + CompressionFormat.Zip => ".zip", + CompressionFormat.GZip => ".tar.gz", + CompressionFormat.Zstd => ".tar.zst", + _ => ".tar" + }; } } diff --git a/Projects/UOContent/World Saves/IArchiveDestination.cs b/Projects/UOContent/World Saves/IArchiveDestination.cs deleted file mode 100644 index 9cd3b0b37..000000000 --- a/Projects/UOContent/World Saves/IArchiveDestination.cs +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: IArchiveDestination.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Saves; - -/// -/// Represents a destination for completed archive files. -/// Implementations run on a background thread (ThreadPool). -/// -public interface IArchiveDestination -{ - /// - /// A human-readable name for logging (e.g., "Local Filesystem", "S3 us-east-1"). - /// - string Name { get; } - - /// - /// Called after a local archive file is successfully created and verified. - /// Must be safe to call from a ThreadPool thread. - /// - /// Full path to the verified .tar.zst archive. - /// The archive period (Hourly, Daily, Monthly). - /// The start of the time range this archive covers. - /// True if the destination successfully received the archive. - bool SendArchive(string archiveFilePath, ArchivePeriod period, DateTime rangeStart); - - /// - /// Returns the number of local archives to retain for this period. - /// Destinations that keep their own copies may return 0. - /// - int GetRetentionCount(ArchivePeriod period); -} diff --git a/Projects/UOContent/World Saves/LocalArchiveDestination.cs b/Projects/UOContent/World Saves/LocalArchiveDestination.cs deleted file mode 100644 index 8348cde48..000000000 --- a/Projects/UOContent/World Saves/LocalArchiveDestination.cs +++ /dev/null @@ -1,50 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2026 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: LocalArchiveDestination.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Saves; - -/// -/// Built-in archive destination representing the local filesystem. -/// The archive is already on local disk, so SendArchive is a no-op. -/// Participates in the retention/pruning protocol via configurable counts. -/// -public class LocalArchiveDestination : IArchiveDestination -{ - public string Name => "Local Filesystem"; - - private readonly int _hourlyRetention; - private readonly int _dailyRetention; - private readonly int _monthlyRetention; - - public LocalArchiveDestination(int hourlyRetention, int dailyRetention, int monthlyRetention) - { - _hourlyRetention = hourlyRetention; - _dailyRetention = dailyRetention; - _monthlyRetention = monthlyRetention; - } - - // Archive is already on local filesystem — nothing to do. - public bool SendArchive(string archiveFilePath, ArchivePeriod period, DateTime rangeStart) => true; - - public int GetRetentionCount(ArchivePeriod period) => period switch - { - ArchivePeriod.Hourly => _hourlyRetention, - ArchivePeriod.Daily => _dailyRetention, - ArchivePeriod.Monthly => _monthlyRetention, - _ => 0 - }; -} diff --git a/Projects/UOContent/World Saves/SaveCommands.cs b/Projects/UOContent/World Saves/SaveCommands.cs index 76f919556..21da36a1f 100644 --- a/Projects/UOContent/World Saves/SaveCommands.cs +++ b/Projects/UOContent/World Saves/SaveCommands.cs @@ -1,102 +1,68 @@ using Server.Saves; -namespace Server.Commands; - -public static class SaveCommands +namespace Server.Commands { - public static void Configure() + public static class SaveCommands { - CommandSystem.Register("Save", AccessLevel.Administrator, Save_OnCommand); - CommandSystem.Register("SetSaves", AccessLevel.Administrator, SetAutoSaves_OnCommand); - CommandSystem.Register("AutoSave", AccessLevel.Administrator, SetAutoSaves_OnCommand); - CommandSystem.Register("SaveFrequency", AccessLevel.Administrator, SetSaveFrequency_OnCommand); - CommandSystem.Register("PruneArchives", AccessLevel.Administrator, PruneArchives_OnCommand); - CommandSystem.Register("ArchiveStatus", AccessLevel.Administrator, ArchiveStatus_OnCommand); - CommandSystem.Register("ArchiveNow", AccessLevel.Administrator, ArchiveNow_OnCommand); - } - - [Usage("PruneArchives"), Description("Prunes archives folder.")] - private static void PruneArchives_OnCommand(CommandEventArgs e) => AutoArchive.PruneBackups(); - - [Usage("Save"), Description("Saves the world.")] - private static void Save_OnCommand(CommandEventArgs e) => AutoSave.Save(); - - [Usage("AutoSave "), Description("Enables or disables automatic shard saving.")] - public static void SetAutoSaves_OnCommand(CommandEventArgs e) - { - if (e.Length == 1) + public static void Configure() { - var enabled = AutoSave.SavesEnabled = e.GetBoolean(0); - - e.Mobile.SendMessage(enabled ? "Saves have been enabled." : "Saves have been disabled."); - } - else - { - e.Mobile.SendMessage("Format: AutoSave "); - } - } - - [Usage("SaveFrequency [warning duration]"), Description("Sets the save frequency starting at midnight local to the server.")] - public static void SetSaveFrequency_OnCommand(CommandEventArgs e) - { - if (e.Length < 1) - { - e.Mobile.SendMessage("Format: SaveFrequency [warning duration]"); - return; + CommandSystem.Register("Save", AccessLevel.Administrator, Save_OnCommand); + CommandSystem.Register("SetSaves", AccessLevel.Administrator, SetAutoSaves_OnCommand); + CommandSystem.Register("AutoSave", AccessLevel.Administrator, SetAutoSaves_OnCommand); + CommandSystem.Register("SaveFrequency", AccessLevel.Administrator, SetSaveFrequency_OnCommand); + CommandSystem.Register("PruneArchives", AccessLevel.Administrator, PruneArchives_OnCommand); } - var saveDelay = e.GetTimeSpan(0); - var warningDelay = e.Length >= 2 ? e.GetTimeSpan(1) : AutoSave.Warning; - - AutoSave.ResetAutoSave(saveDelay, warningDelay); - - e.Mobile.SendMessage("Save frequency has been updated."); - } - - [Usage("ArchiveStatus")] - [Description("Shows archive system status, journal state, and registered destinations.")] - private static void ArchiveStatus_OnCommand(CommandEventArgs e) - { - var mobile = e.Mobile; - - mobile.SendMessage("--- Archive Status ---"); - mobile.SendMessage($"Next hourly: {AutoArchive.NextHourlyArchive:yyyy-MM-dd HH:mm}"); - mobile.SendMessage($"Next daily: {AutoArchive.NextDailyArchive:yyyy-MM-dd}"); - mobile.SendMessage($"Next monthly: {AutoArchive.NextMonthlyArchive:yyyy-MM}"); - - var destinations = ArchiveDestinationRegistry.Destinations; - mobile.SendMessage($"Registered destinations: {destinations.Count}"); - foreach (var dest in destinations) + [Usage("PruneArchives"), Description("Prunes archives folder.")] + private static void PruneArchives_OnCommand(CommandEventArgs e) { - mobile.SendMessage( - $" - {dest.Name} (retention: {dest.GetRetentionCount(ArchivePeriod.Hourly)}h/{dest.GetRetentionCount(ArchivePeriod.Daily)}d/{dest.GetRetentionCount(ArchivePeriod.Monthly)}m)" - ); + AutoArchive.PruneBackups(); } - var operations = ArchiveJournal.Operations; - var pending = 0; - var failed = 0; - for (var i = 0; i < operations.Count; i++) + [Usage("Save"), Description("Saves the world.")] + private static void Save_OnCommand(CommandEventArgs e) { - var op = operations[i]; - if (op.State is ArchiveOperationState.Started or ArchiveOperationState.Archived - or ArchiveOperationState.Distributed) + AutoSave.Save(); + } + + [Usage("AutoSave "), Description("Enables or disables automatic shard saving.")] + public static void SetAutoSaves_OnCommand(CommandEventArgs e) + { + if (e.Length == 1) { - pending++; + + var enabled = AutoSave.SavesEnabled = e.GetBoolean(0); + + if (enabled) + { + e.Mobile.SendMessage($"Saves have been enabled."); + } + else + { + e.Mobile.SendMessage($"Saves have been disabled."); + } } - else if (op.State == ArchiveOperationState.Failed) + else { - failed++; + e.Mobile.SendMessage("Format: AutoSave "); } } - mobile.SendMessage($"Journal entries: {operations.Count} ({pending} pending, {failed} failed)"); - } + [Usage("SaveFrequency [warning duration]"), Description("Sets the save frequency starting at midnight local to the server.")] + public static void SetSaveFrequency_OnCommand(CommandEventArgs e) + { + if (e.Length < 1) + { + e.Mobile.SendMessage("Format: SaveFrequency [warning duration]"); + return; + } - [Usage("ArchiveNow"), Description("Forces an immediate archive rollup regardless of schedule.")] - private static void ArchiveNow_OnCommand(CommandEventArgs e) - { - e.Mobile.SendMessage("Forcing immediate archive rollup..."); - AutoArchive.ForceRollup(); + var saveDelay = e.GetTimeSpan(0); + var warningDelay = e.Length >= 2 ? e.GetTimeSpan(1) : AutoSave.Warning; + + AutoSave.ResetAutoSave(saveDelay, warningDelay); + + e.Mobile.SendMessage("Save frequency has been updated."); + } } } diff --git a/README.md b/README.md index 01a368804..c04c17279 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- ModernUO - Ultima Online Server Emulator for the modern era! +

ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=discord&style=social)](https://muo.gg/discord) [![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/modernuo?style=social&label=/r/modernuo)](https://muo.gg/reddit/) [![Twitter Follow](https://img.shields.io/twitter/follow/modernuo?label=@modernuo&style=social)](https://muo.gg/twitter) @@ -9,57 +9,48 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc [![GitHub license](https://img.shields.io/github/license/modernuo/ModernUO?color=blue)](https://github.com/modernuo/ModernUO/blob/master/LICENSE) [![GitHub stars](https://img.shields.io/github/stars/modernuo/ModernUO?logo=github&style=flat)](https://github.com/modernuo/ModernUO/stargazers) [![GitHub issues](https://img.shields.io/github/issues/modernuo/ModernUO?logo=github)](https://github.com/modernuo/ModernUO/issues) -
+
[![GitHub build](https://img.shields.io/github/actions/workflow/status/modernuo/ModernUO/build-test.yml?branch=main&logo=github)](https://github.com/modernuo/ModernUO/actions) [![Azure Pipelines build](https://dev.azure.com/modernuo/modernuo/_apis/build/status/Build?branchName=main)](https://dev.azure.com/modernuo/modernuo/_build/latest?definitionId=1&branchName=main) ## Requirements #### Supported Operating Systems -[![Windows 10/11/2012 R2/2016/2019/2022/2025](https://img.shields.io/badge/-server%202025-3c78d5?labelColor=222222&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHJvbGU9ImltZyIgdmlld0JveD0iMCAwIDI0IDI0Ij48dGl0bGU+V2luZG93czwvdGl0bGU+PHBhdGggZD0iTTAsMEgxMS4zNzdWMTEuMzcySDBaTTEyLjYyMywwSDI0VjExLjM3MkgxMi42MjNaTTAsMTIuNjIzSDExLjM3N1YyNEgwWm0xMi42MjMsMEgyNFYyNEgxMi42MjMiIGZpbGw9IiMzYzc4ZDUiLz48L3N2Zz4=)](https://www.microsoft.com/en-US/evalcenter/evaluate-windows-server-2022) -![MacOS 14+](https://img.shields.io/badge/-sonoma-222222?logo=apple&logoColor=white&labelColor=222222) -[![Debian 12+](https://img.shields.io/badge/-trixie-A81D33?logo=debian&logoColor=A81D33&labelColor=222222)](https://www.debian.org/distrib/) -[![Ubuntu 22+ LTS](https://img.shields.io/badge/-26LTS-E95420?logo=ubuntu&logoColor=E95420&labelColor=222222)](https://ubuntu.com/download/server) -
-[![Alpine 3.22+](https://img.shields.io/badge/-3.22-0D597F?logo=alpinelinux&logoColor=0D597F&labelColor=222222)](https://alpinelinux.org/downloads/) -[![Fedora 42+](https://img.shields.io/badge/-44-51a2da?logo=fedora&logoColor=51a2da&labelColor=222222)](https://getfedora.org/en/server/download/) -[![RedHat 9+](https://img.shields.io/badge/-10-BE0000?logo=redhat&logoColor=BE0000&labelColor=222222)](https://access.redhat.com/downloads) -[![CentOS Stream 9+](https://img.shields.io/badge/-stream_10-262577?logo=centos&logoColor=white&labelColor=222222)](https://www.centos.org/download/) -[![AlmaLinux 10](https://img.shields.io/badge/-10-0F4266?logo=almalinux&logoColor=white&labelColor=222222)](https://almalinux.org/get-almalinux/) -[![openSUSE 15.6+](https://img.shields.io/badge/-15.6-73BA25?logo=openSUSE&logoColor=73BA25&labelColor=222222)](https://get.opensuse.org/) -[![SUSE Enterprise 15 SP6](https://img.shields.io/badge/-15%20SP6-0C322C?logo=suse&logoColor=30BA78&labelColor=222222)](https://www.suse.com/download/sles/) -[![Linux Mint 21+](https://img.shields.io/badge/-21-87CF3E?logo=linux%20mint&logoColor=87CF3E&labelColor=222222)](https://linuxmint.com/download.php) +[![Windows 10/11/2019/2022](https://img.shields.io/badge/-server%202022-3c78d5?labelColor=222222&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHJvbGU9ImltZyIgdmlld0JveD0iMCAwIDI0IDI0Ij48dGl0bGU+V2luZG93czwvdGl0bGU+PHBhdGggZD0iTTAsMEgxMS4zNzdWMTEuMzcySDBaTTEyLjYyMywwSDI0VjExLjM3MkgxMi42MjNaTTAsMTIuNjIzSDExLjM3N1YyNEgwWm0xMi42MjMsMEgyNFYyNEgxMi42MjMiIGZpbGw9IiMzYzc4ZDUiLz48L3N2Zz4=)](https://www.microsoft.com/en-US/evalcenter/evaluate-windows-server-2022) +![MacOS 13+](https://img.shields.io/badge/-sonoma-222222?logo=apple&logoColor=white&labelColor=222222) +[![Debian 11+](https://img.shields.io/badge/-bookworm-A81D33?logo=debian&logoColor=A81D33&labelColor=222222)](https://www.debian.org/distrib/) +[![Ubuntu 20+ LTS](https://img.shields.io/badge/-22LTS-E95420?logo=ubuntu&logoColor=E95420&labelColor=222222)](https://ubuntu.com/download/server) +
+[![Alpine 3.18+](https://img.shields.io/badge/-3.18-0D597F?logo=alpinelinux&logoColor=0D597F&labelColor=222222)](https://alpinelinux.org/downloads/) +[![Fedora 40+](https://img.shields.io/badge/-40-51a2da?logo=fedora&logoColor=51a2da&labelColor=222222)](https://getfedora.org/en/server/download/) +[![RedHat 8+](https://img.shields.io/badge/-8-BE0000?logo=redhat&logoColor=BE0000&labelColor=222222)](https://access.redhat.com/downloads) +[![CentOS 9](https://img.shields.io/badge/-9-262577?logo=centos&logoColor=white&labelColor=222222)](https://www.centos.org/download/) +[![openSUSE 15+](https://img.shields.io/badge/-15-73BA25?logo=openSUSE&logoColor=73BA25&labelColor=222222)](https://get.opensuse.org/) +[![SUSE Enterprise 12 SP2+](https://img.shields.io/badge/-12%20SP2-0C322C?logo=suse&logoColor=30BA78&labelColor=222222)](https://www.suse.com/download/sles/) +[![Linux Mint 17+](https://img.shields.io/badge/-20-87CF3E?logo=linux%20mint&logoColor=87CF3E&labelColor=222222)](https://linuxmint.com/download.php) [![Arch](https://img.shields.io/badge/-Arch-1793D1?logo=archlinux&logoColor=1793D1&labelColor=222222)](https://archlinux.org/download/) #### Required Frameworks ##### All Operating Systems -[![.NET](https://img.shields.io/badge/-10.0.0-5C2D91?logo=.NET&logoColor=white&labelColor=222222)](https://dotnet.microsoft.com/download/dotnet/10.0) +[![.NET](https://img.shields.io/badge/-9.0.0-5C2D91?logo=.NET&logoColor=white&labelColor=222222)](https://dotnet.microsoft.com/download/dotnet/9.0) ##### Windows -[![VC++ Redistributable v14](https://img.shields.io/badge/-Redist%20v14-00599C?logo=cplusplus&logoColor=white&labelColor=222222)](https://aka.ms/vc14/vc_redist.x64.exe) - -#### Hardware - -| Use | vCPU | RAM | Storage | -|---|---|---|---| -| Development / test | 2 **dedicated** | 2 GB | SSD | -| Small live shard (< 50 concurrent) | 4 dedicated | 4 GB | NVMe | -| Medium (50–200) | 4–8 | 8 GB | NVMe | -| Large (200+) | 8+, high clock | 16 GB+ | NVMe | - -See [dev-docs/server-requirements.md](dev-docs/server-requirements.md) for more information. +[![VC++ Redistributable 17](https://img.shields.io/badge/-Redist%2017-00599C?logo=cplusplus&logoColor=white&labelColor=222222)](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2015-2017-2019-and-2022) #### Development [![git](https://img.shields.io/badge/-git-F05032?logo=git&logoColor=F05032&labelColor=222222)](https://git-scm.com/downloads) -[![.NET](https://img.shields.io/badge/-%2010.0.100%20SDK-5C2D91?logo=.NET&logoColor=white&labelColor=222222)](https://dotnet.microsoft.com/download/dotnet/10.0) +[![.NET](https://img.shields.io/badge/-%209.0.101%20SDK-5C2D91?logo=.NET&logoColor=white&labelColor=222222)](https://dotnet.microsoft.com/download/dotnet/9.0) #### Supported IDEs -

- Jetbrains Rider 2025.3+ - space - VSCode - space - Visual Studio 2026 -

+ +

Jetbrains Rider 2024.3+spaceVSCodespaceVisual Studio 2022 v17.12+

## Getting Started - Install prerequisite [requirements](https://github.com/modernuo/ModernUO#requirements) @@ -68,68 +59,38 @@ See [dev-docs/server-requirements.md](dev-docs/server-requirements.md) for more - Open `ModernUO.sln` to start developing ## Building/Publishing - -#### Interactive Mode (Recommended for new users) -Run `./publish.cmd` (Windows) or `./publish.sh` (Linux/macOS) with no arguments to launch the guided build tool. It will: -- Check prerequisites (.NET SDK, native libraries) -- Walk you through configuration and platform selection -- Build and publish the server to the `Distribution` directory -- Show deployment instructions for cross-compiled builds - -#### Command Line -```shell -./publish.cmd [release|debug] [os] [arch] -``` -- `os` - [Supported operating systems](https://github.com/dotnet/core/blob/main/release-notes/10.0/supported-os.md) - - `win` - [Windows](https://learn.microsoft.com/en-us/dotnet/core/install/windows) - - `osx` - [macOS](https://learn.microsoft.com/en-us/dotnet/core/install/macos) - - `linux` - [Linux](https://learn.microsoft.com/en-us/dotnet/core/install/linux) -- `arch` - - `x64` - Intel/AMD 64-bit - - `arm64` - ARM 64-bit +- Run `./publish.cmd [release|debug (default: release)] [os] [arch (default: x64)]` + - `os` - [Supported operating systems](https://github.com/dotnet/core/blob/main/release-notes/7.0/supported-os.md) + - `win` - Windows 10/11/2019/2022 + - `osx` - MacOS 12/13/14 (Sonoma, Big Sur, Monterey) + - `linux` - Linux + - `arch` + - `x64` - Intel 64-bit + - `arm64` - ARM 64-bit (Windows Arm64 not supported) ## Linux Prerequisites -### Fedora, CentOS, RHEL, AlmaLinux, Rocky, etc +### Fedora, CentOS, RHEL, etc ```shell dnf upgrade --refresh -y -# RHEL-family distributions (CentOS Stream, AlmaLinux, Rocky, RHEL) need CRB and EPEL enabled. -# Fedora skips this block. -dnf install -y dnf-plugins-core -dnf config-manager --set-enabled crb -dnf install -y epel-release -# Prerequisites -dnf install -y findutils libicu libdeflate libargon2 tzdata +# CentOS does not come with EPEL enabled +dnf install -y epel-release epel-next-release +dnf install -y findutils libicu libdeflate-devel zstd libargon2-devel ``` ### Ubuntu, Debian, etc ```shell apt-get update -y -# The ICU runtime package carries the ABI version in its name (libicu74, libicu76, …) and has no -# stable alias, so match it by pattern rather than pinning a release-specific name. -apt-get install -y '^libicu[0-9]+$' libdeflate0 libargon2-1 tzdata +apt-get install -y libicu-dev libdeflate-dev zstd libargon2-dev ``` -Only the runtime libraries are needed — the `-dev`/`-devel` packages are not. Run -`./build-tool --check-prereqs` to check the current machine and print the exact packages your -release needs. - -`zstd` is not listed because ZstdNet bundles `libzstd` for every platform, and `liburing` is not -listed because IORingGroup issues `io_uring` syscalls directly. - -If the shard's configured time zone is a legacy alias such as `US/Eastern`, Debian 12 and Ubuntu -24.04 also need `tzdata-legacy`. See [Platform Prerequisites](dev-docs/platform-prerequisites.md) -for what each dependency is for and what breaks without it. - ## OSX Requirements ```shell -brew install icu4c libdeflate argon2 +brew install icu4c libdeflate zstd argon2 ``` ## Running the Server -- Follow the [publish](https://github.com/modernuo/ModernUO#buildingpublishing) instructions -- The `Distribution` directory is portable — copy it to your production server for deployment -- Run `ModernUO.exe` or `dotnet ModernUO.dll` from the `Distribution` directory -- On first run, the server will prompt you to configure game data file locations +- Follow the [publish](https://github.com/modernuo/ModernUO#publishing-builds) instructions +- Run `ModernUO.exe` or `dotnet ModernUO.dll` from the `Distribution` directory on the ## Troubleshooting / FAQ - See [FAQ](./FAQ.md) @@ -146,16 +107,7 @@ Thank you for supporting us! You can find out how by visiting the [sponsors](./S - [Voxpire](https://github.com/Voxpire), the ServUO Team & Community - [Karasho](https://github.com/andreakarasho), [Jaedan](https://github.com/jaedan) and the ClassicUO Community -

-

Development Tools & Plugins provided with ♥ by

JetBrains
-Material Theme

- -## Code Signing Policy - -Free code signing provided by [SignPath.io](https://about.signpath.io), certificate by [SignPath Foundation](https://signpath.org). - -This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it - -### Teams & Roles -Approvers & Committers: [Development Team](https://github.com/orgs/modernuo/teams/development-team) - +

+

Development Tools & Plugins provided with ♥ by
JetBrains +Material Theme +

diff --git a/RUNUO_TO_MODERNUO.md b/RUNUO_TO_MODERNUO.md new file mode 100644 index 000000000..6c553f563 --- /dev/null +++ b/RUNUO_TO_MODERNUO.md @@ -0,0 +1,121 @@ +# From RunUO to ModernUO +RunUO was built using C# from .NET 1.1 in 2002. There have been massive changes to technology in the past 20+ years. +We believe it is time for Ultima Online to take advantage of this technology so a server can provide a richer experience with never before seen scale. + +While it is possible (and many have done it) to migrate an _active server_ from RunUO to ModernUO, it is a daunting task. +Please ask for help in our discord! + +## Technology +| | RunUO | ModernUO | +|:-------------|:------------------------------------------------------------------------------------------------------|:------------------------------------------------------------| +| Language | C# 4 | C# 11 (.NET 8) | +| Supported OS | 32 & 64bit Windows or Mono | 64bit Windows, MacOS & Linux | +| IDEs | [VS](https://visualstudio.microsoft.com/downloads/), [VSCode](https://code.visualstudio.com/download) | VS 2022+ or [Rider 2023+](https://www.jetbrains.com/rider/) | + +## Code API Changes +* **ModernUO can use source generators to [serialization/deserialization](https://github.com/modernuo/SerializationGenerator#basic-usage) automatically.** + * This is the biggest change to the API! While intimidating and different, it unlocks the ability for ModernUO to only serialize _data that has changed_. + * We estimate a world save with 10mill objects on a busy server will take less than 1 second. + * This feature is _optional and will remain optional indefinitely_. +* `Serialize(GenericWriter writer)` and equiv deserialize was changed to `Serialize(IGenericWriter writer)`. +* The following now use generics, e.g. `BeginAction(typeof(X))` is now `BeginAction`. + * CanBeginAction, BeginAction and EndAction + * FindRegion and IsPartOf + * FindGump, HasGump, and CloseGump +* Functions such as `OnAdded(object)` for both Items/Mobiles are now `OnAdded(IEntity)`. +* Most `delegate` have been changed to `Action`. + * Example: `EventSink.PlayerDeath += new PlayerDeathEventHandler(EventSink_PlayerDeath);` is now `EventSink.PlayerDeath += EventSink_PlayerDeath;`. +* `ObjectPropertyList` is now `IPropertyList`, + * Example: `GetProperties(ObjectPropertyList list)` is now `GetProperties(IPropertyList list)`. +* `[Constructable]` attribute is now `[Constructible]`. + +### Object Property List API Changes +ObjectPropertyList has been drastically optimized, which means the API has been modernized: + +❌ _Not valid_ +```cs +list.Add(1061837, "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges); +``` +✅ +```cs +list.Add(1061837, $"{_curArcaneCharges}\t{_maxArcaneCharges}"); +``` + +Clilocs that use arguments: + +❌ _Not valid_ +```cs +list.Add(1060659, "Level\t{1}", m_Level); +``` +✅ - _Note the string as an argument, this is mandatory!_ +```cs +list.Add(1060659, $"{"Level"}\t{Level}"); // ~1_val~: ~2_val~ +``` + +Clilocs that use other clilocs as an argument: + +❌ _Do not prepend #_ +```cs +list.Add(1060830, $"#{dirt.ToString()}"); +``` +✅ _Use the new custom cliloc argument formatter_ +```cs +list.Add(1060830, $"{dirt:#}"); +``` + +## Core Changes +* ModernUO is not inherently thread safe. Overall infrastructure improved with CPU and minimizing memory garbage in mind. +* Timer system improved drastically by using [Timer Wheels](http://www.cs.columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf). +* Networking is 5-10x faster by using fixed [Circular Buffers](https://en.wikipedia.org/wiki/Circular_buffer). +* Network packets are no longer objects and write directly to the network buffer, improving performance by 10x. +* World saves are 30x faster by saving to memory and flushing to disk in the background. +* Improved RNG accuracy and performance by 5x using [Xoshiro256++](https://prng.di.unimi.it/) +* Converted quite a bit of configuration to JSON with a central settings file. +* Logging changed from Console.WriteLine to Serilog (still work in progress). +* Eliminated calling `DateTime.Now` which had a huge performance penalty. +* Accounts are now saved to a binary file (will eventually change to a databse) to improve world save performance by eliminating XML. +* Strings are now built using the new interpolated string syntax, and highly performant string builders. + +## New Features +* IPv6 support. +* Generic serialization that is automatically done in parallel with the world save. + * The [faction system](https://github.com/modernuo/ModernUO/blob/7adf52ef48df7ae2b034c27e67b0c332b37fb053/Projects/UOContent/Engines/Factions/Core/FactionSystem.cs#L15) is no longer powered by a single item and instead uses generic persistence. +* Timezone support for custom scripts that might need it. +* Hourly/daily/weekly/monthly backups & archiving using [Z-Standard](https://facebook.github.io/zstd). +* Client version detection (including CUO) for easy configuration. +* Localization (Cliloc) support. +* Better encryption for passwords using [Argon2](https://en.wikipedia.org/wiki/Argon2). +* Packet throttling that is configurable per packet and per connection. +* Enable packet logging per connection. +* Owner accounts can be protected from being locked out. The first account created is automatically added to this list. +* Use optional arguments from constructors for Add command. +* Captures Razor version and displays it in client gump +* Spawners can be exported/imported using commands and use a GUID for replacement. + +## Major Feature Changes +* By default, world saves are now every 5th minute of a real world hour. + * E.g. 5:00, 5:05, 5:10, regardless of when the server is booted. +* Some items have their constructor arguments rearranged. + +## Changes to UO Mechanics +* Min/max skill requirements for magery adjusted to be accurate to OSI +* Buffs/Curses now apply appropriately + +## Removed Features +* Reporting +* Remote Admin +* My RunUO +* Event Log +* DocsGen command + +## New Development Features & Changes +* Added a shared list/queue for temporary processing such as accumulating players to damage/kill. + * [PooledRefList](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/PooledRefList.cs) + * [PooledRefQueue](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/PooledRefQueue.cs) +* Adds HashSet that is ordered by insertion time + * [OrderedHashSet](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/OrderedHashSet.cs) + * [PooledOrderedHashSet](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/PooledOrderedHashSet.cs) +* Adds [StringBuilder](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Buffers/ValueStringBuilder.cs) that is fast and does not impose garbage collection. +* Adds performant [JSON](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Json/JsonConfig.cs) support with simple API. +* Adds performant, thread _unsafe_, [ArrayPool](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Buffers/STArrayPool.cs) +* Adds highly performant and easy to use converters for [HexString](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Text/HexStringConverter.cs) representation of data diff --git a/SPONSORS.md b/SPONSORS.md index f832b6a72..315169097 100644 --- a/SPONSORS.md +++ b/SPONSORS.md @@ -3,16 +3,14 @@ Thank you to all of our generous sponsors that make ModernUO possible. **A special thank you to the following sponsors for their considerable contributions:** -* [Age of Shadows](https://ageofshadows.gg) * [UO Outlands](https://uooutlands.com) -* [UO Sagas](https://uosagas.com) * Prayer ([MagnUm-Opus](https://discord.gg/CzDEq3vv2N)) ### Looking to sponsor ModernUO? We greatly appreciate the support! Use one of the following platforms below: #### GitHub -[Github Sponsors | ModernUO](https://github.com/sponsors/modernuo) +[Github Sponsors | ModernUO](https://github.com/sponsors/modernuo) #### Patreon [Patreon | ModernUO](https://muo.gg/patreon) @@ -32,5 +30,6 @@ xch1vzk93t538m4xukg955v3ltjtgz6rapev3evnmnxdzf4hjj5y7cuq7tkf6z #### Bitcoin 37QmRWTCjVoNWycMpo1t5JnYVDmJTGSJ9W + #### Ethereum 0x7A9D76F497d4Ee150Ada0ea0455fF3f6e8F3b6b8 diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index 0b2b2f9f5..a9a7cc38f 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -85,24 +85,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Attribution notice for RunUO-Encryption +License notice for wepoll --------------------------- -The client encryption support in ModernUO (Projects/Server/Network/Encryption/) -is based on protocol research and reverse engineering originally performed by -Sebastian Hartte (DarkStorm) for the RunUO-Encryption project. +wepoll - epoll for Windows +https://github.com/piscisaureus/wepoll -His work identified: -- The login packet XOR cipher algorithm used by the Ultima Online client -- The Twofish + MD5 game packet encryption scheme -- The login key values for client versions prior to 6.0.5 +Copyright 2012-2020, Bert Belder +All rights reserved. -The Twofish block cipher implementation used in RunUO-Encryption was a C# port -by Shaun Wilde, originally published at: -http://www.codetools.com/csharp/twofish_csharp.asp +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -ModernUO's implementation is a clean rewrite using modern C# (Span, SIMD, -BitOperations) but the underlying protocol knowledge originates from -Sebastian Hartte's reverse engineering effort. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. -RunUO-Encryption was licensed under the GNU General Public License v2: -https://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a16bb8fcc..798907fc1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,37 +1,24 @@ name: 'Build' trigger: - branches: - include: - - main - paths: - include: - - .config/dotnet-tools.json - - Projects/** - - Directory.Build.props - - global.json - - version.json - - '*.slnx' - - '*.sln' +- main variables: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true jobs: - job: BuildWindows - displayName: 'Windows Server 2025' + displayName: 'Windows Server 2022' pool: vmImage: 'windows-latest' steps: - - checkout: self - fetchDepth: 0 # avoid shallow clone so nbgv can do its work. - task: UseDotNet@2 - displayName: 'Install .NET' + displayName: 'Install .NET 9' inputs: useGlobalJson: true - task: NuGetAuthenticate@1 - - script: dotnet run --project Projects\BuildTool -- --config Release --skip-prereqs + - script: ./publish.cmd Release displayName: 'Build' - powershell: ./.github/porcelain.ps1 displayName: Migration Changes diff --git a/branding/build-tool.svg b/branding/build-tool.svg deleted file mode 100644 index 2da3c9937..000000000 --- a/branding/build-tool.svg +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/branding/gears.svg b/branding/gears.svg deleted file mode 100644 index c923911d9..000000000 --- a/branding/gears.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/branding/muo.svg b/branding/muo.svg deleted file mode 100644 index 133bb2fa1..000000000 --- a/branding/muo.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/dev-docs/claude-skills/migrate-from-runuo/migrate-commands-events.md b/dev-docs/claude-skills/migrate-from-runuo/migrate-commands-events.md deleted file mode 100644 index 21d8cdc69..000000000 --- a/dev-docs/claude-skills/migrate-from-runuo/migrate-commands-events.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: migrate-commands-events -description: > - Trigger: when converting RunUO command registration, EventSink handlers, or event delegate patterns. - Covers: event name changes, delegate removal, Configure vs Initialize. ---- - -# RunUO -> ModernUO Commands & Events Migration - -## When This Activates -- Converting `EventSink` subscriptions -- Converting event handler signatures -- Moving from `Initialize()` to `Configure()` -- Converting WorldSave/WorldLoad events to GenericPersistence - -## Conversion Steps -1. Change `Initialize()` to `Configure()` for event registration -2. Remove delegate constructors: `new LoginEventHandler(OnLogin)` -> `OnLogin` -3. Rename events: `Login` -> `Connected`, `Logout` -> `Disconnected` -4. Update signatures: `OnLogin(LoginEventArgs e)` -> `OnConnected(Mobile m)` -5. WorldSave persistence -> convert to `GenericPersistence` (see migrate-persistence skill) - -## Event Mapping -| RunUO | ModernUO | -|---|---| -| `EventSink.Login` | `EventSink.Connected` (Action) | -| `EventSink.Logout` | `EventSink.Disconnected` (Action) | -| `EventSink.WorldSave` | `EventSink.WorldSave` (Action -- no args) | -| `EventSink.Crashed` | `EventSink.ServerCrashed` | -| `new XXXEventHandler(method)` | `method` (direct reference) | - -## Commands (Minimal Changes) -Commands use the same `CommandSystem.Register()` API. Main change: use `Configure()` for registration. - -## See Also -- `dev-docs/runuo-migration-docs/07-commands-events.md` -- detailed migration reference -- `dev-docs/events.md` -- complete ModernUO event system -- `dev-docs/claude-skills/modernuo-events.md` -- ModernUO events skill -- `dev-docs/claude-skills/modernuo-commands-targeting.md` -- ModernUO commands skill diff --git a/dev-docs/claude-skills/migrate-from-runuo/migrate-foundation.md b/dev-docs/claude-skills/migrate-from-runuo/migrate-foundation.md deleted file mode 100644 index 630d8eb45..000000000 --- a/dev-docs/claude-skills/migrate-from-runuo/migrate-foundation.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -name: migrate-foundation -description: > - Trigger: when migrating ANY RunUO code to ModernUO. Always load this skill first. - Covers: namespace changes, naming conventions, attribute renames, logging, threading, performance. ---- - -# RunUO -> ModernUO Foundation Migration - -## When This Activates -- Converting ANY RunUO 2.7 script to ModernUO -- Always apply these changes FIRST before system-specific migration - -## Universal Changes Checklist -1. File-scoped namespace: `namespace X { ... }` -> `namespace X;` -2. `using ModernUO.Serialization;` -- add for any serializable type -3. Rename fields: `m_FieldName` -> `_fieldName` -4. `[Constructable]` -> `[Constructible]` -5. `Console.WriteLine` -> `LogFactory.GetLogger(typeof(X))` -> `logger.Information(...)` -6. `DateTime.UtcNow` -> `Core.Now` -7. `World.Mobiles`/`World.Items` iteration -> spatial queries (`map.GetMobilesInRange()`) -8. Remove `lock`, `volatile`, `ConcurrentDictionary`, `Mutex` -- server is single-threaded -9. Remove `Task.Run`, `new Thread` -- use `Timer.StartTimer()` instead -10. `ArrayPool.Shared` -> `STArrayPool.Shared` -11. `new List()` on hot paths -> `PooledRefList.Create()` -12. Modernize property syntax: `{ get { return x; } }` -> `{ get => x; }` -13. Delete `MyType(Serial serial) : base(serial)` constructor -- auto-generated -14. `Name = "text"` in constructor -> `public override string DefaultName => "text";` - -## Quick Reference -| RunUO | ModernUO | -|---|---| -| `[Constructable]` | `[Constructible]` | -| `m_Field` | `_field` | -| `Console.WriteLine(...)` | `logger.Information(...)` | -| `DateTime.UtcNow` | `Core.Now` | -| `MyItem(Serial serial) : base(serial)` | DELETE | -| `lock (_obj) { }` | Remove entirely | -| `ConcurrentDictionary` | `Dictionary` | -| `ArrayPool.Shared` | `STArrayPool.Shared` | - -## Anti-Patterns -- Don't rename existing `m_` fields in code you're not otherwise migrating -- Don't add threading constructs -- everything is single-threaded -- Don't use allocating LINQ (`.ToList()`, `.GroupBy()`, etc.) on hot paths - -## See Also -- `dev-docs/runuo-migration-docs/01-foundation-changes.md` -- complete foundation changes reference -- `dev-docs/code-standards.md` -- ModernUO coding standards and LINQ tiers -- `dev-docs/threading-model.md` -- Why single-threaded, what's allowed diff --git a/dev-docs/claude-skills/migrate-from-runuo/migrate-gumps.md b/dev-docs/claude-skills/migrate-from-runuo/migrate-gumps.md deleted file mode 100644 index acb7452ae..000000000 --- a/dev-docs/claude-skills/migrate-from-runuo/migrate-gumps.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: migrate-gumps -description: > - Trigger: when converting RunUO Gump classes, OnResponse handlers, or gump UI code to ModernUO DynamicGump/StaticGump. - Covers: builder pattern, DisplayTo, response handling, empty gump rule. ---- - -# RunUO -> ModernUO Gump Migration - -## When This Activates -- Converting `Gump` subclasses -- Converting `OnResponse(NetState, RelayInfo)` handlers -- Updating gump sending/closing patterns - -## Conversion Steps -1. Choose type: `DynamicGump` (variable layout) or `StaticGump` (fixed layout) -2. Change class declaration: `class X : Gump` -> `class X : DynamicGump` -3. Add `public override bool Singleton => true;` if only one per player -4. Make constructor private, add static `DisplayTo()` method -5. Move all `AddXxx()` calls from constructor to `BuildLayout(ref DynamicGumpBuilder builder)` -6. Prefix each call with `builder.`: `AddLabel(...)` -> `builder.AddLabel(...)` -7. Convert properties: `Closable = false` -> `builder.SetNoClose()` -8. Update OnResponse: `OnResponse(NetState, RelayInfo)` -> `OnResponse(NetState, in RelayInfo)` -9. Update text entries: `info.TextEntries[i].Text` -> `info.GetTextEntry(id)` -10. For StaticGump: extract variable text into placeholders + `BuildStrings` - -## Quick Mapping -| RunUO | ModernUO | -|---|---| -| `class X : Gump` | `class X : DynamicGump` or `StaticGump` | -| `AddPage(0)` | `builder.AddPage()` | -| `Closable = false` | `builder.SetNoClose()` | -| `Dragable = false` | `builder.SetNoMove()` | -| `OnResponse(NetState, RelayInfo)` | `OnResponse(NetState, in RelayInfo)` | -| `info.TextEntries[i].Text` | `info.GetTextEntry(id)` | -| `from.SendGump(new X(...))` | `X.DisplayTo(from, ...)` | -| `from.CloseGump(typeof(X))` | `from.CloseGump()` | - -## Critical: Empty Gump Rule -Never create a gump with no visual elements. Use the `DisplayTo()` pattern -- validate before constructing. - -## See Also -- `dev-docs/runuo-migration-docs/04-gumps.md` -- detailed migration reference -- `dev-docs/gump-system.md` -- complete ModernUO gump system -- `dev-docs/claude-skills/modernuo-gump-system.md` -- ModernUO gump skill diff --git a/dev-docs/claude-skills/migrate-from-runuo/migrate-items-mobiles.md b/dev-docs/claude-skills/migrate-from-runuo/migrate-items-mobiles.md deleted file mode 100644 index 8edf70a72..000000000 --- a/dev-docs/claude-skills/migrate-from-runuo/migrate-items-mobiles.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -name: migrate-items-mobiles -description: > - Trigger: when converting RunUO Item, Mobile, or BaseCreature subclasses to ModernUO. Most common migration task. - Covers: complete item/creature conversion combining serialization, timers, properties, naming. ---- - -# RunUO -> ModernUO Item/Mobile/Creature Migration - -## When This Activates -- Converting any `Item` subclass from RunUO -- Converting any `Mobile`/`BaseCreature` subclass -- This is the most common migration task -- combines all other systems - -## Item Conversion Checklist -1. [ ] Foundation: file-scoped namespace, `using ModernUO.Serialization;` -2. [ ] Class: add `[SerializationGenerator(N, false)]` (N = old version + 1, `false` if old `Deserialize` used `ReadInt()`), add `partial` -3. [ ] Fields: `m_X` -> `[SerializableField(N)] _x` with `[SerializedCommandProperty]` -4. [ ] Add `[InvalidateProperties]` where RunUO setter called `InvalidateProperties()` -5. [ ] Delete: Serial constructor, Serialize, Deserialize -6. [ ] `[Constructable]` -> `[Constructible]` -7. [ ] `Name = "text"` -> `public override string DefaultName => "text";` -8. [ ] Timers: nested class -> `Timer.StartTimer()` + `TimerExecutionToken` + `[AfterDeserialization]` + `OnAfterDelete` -9. [ ] Properties: `GetProperties(ObjectPropertyList)` -> `GetProperties(IPropertyList)`, apply string hole rule -10. [ ] Context menus: `List` -> `ref PooledRefList` - -## Creature-Specific Changes -- `[CorpseName("...")]` attribute -> `public override string CorpseName => "...";` -- `BaseCreature(AI, Fight, 10, 1, 0.2, 0.4)` -> `BaseCreature(AI, Fight)` (extra params default) -- `Name = "text"` -> `public override string DefaultName => "text";` -- Expression-bodied overrides: `public override int Meat { get { return 1; } }` -> `public override int Meat => 1;` - -## Anti-Patterns -- Using `_field--` instead of `Property--` (bypasses MarkDirty tracking) -- Forgetting `[AfterDeserialization]` for timer restoration -- Forgetting `OnAfterDelete()` for timer cancellation - -## See Also -- `dev-docs/runuo-migration-docs/09-items-mobiles-creatures.md` -- detailed migration with before/after examples -- `dev-docs/content-patterns.md` -- ModernUO content templates -- `dev-docs/claude-skills/modernuo-content-patterns.md` -- ModernUO content skill -- `dev-docs/claude-skills/modernuo-serialization.md` -- serialization patterns -- `dev-docs/claude-skills/modernuo-timers.md` -- timer patterns diff --git a/dev-docs/claude-skills/migrate-from-runuo/migrate-packets.md b/dev-docs/claude-skills/migrate-from-runuo/migrate-packets.md deleted file mode 100644 index c686908ff..000000000 --- a/dev-docs/claude-skills/migrate-from-runuo/migrate-packets.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: migrate-packets -description: > - Trigger: when converting RunUO Packet subclasses, PacketWriter/PacketReader, or packet handler registration to ModernUO span-based packets. - Covers: outgoing packet conversion, incoming handler conversion, SpanWriter/SpanReader. ---- - -# RunUO -> ModernUO Packet Migration - -## When This Activates -- Converting `Packet` subclasses to static create methods -- Converting `PacketHandlers.Register()` to `IncomingPackets.Register()` -- Converting `PacketWriter`/`PacketReader` to `SpanWriter`/`SpanReader` - -## Conversion Steps (Outgoing) -1. Create static class: `public static class OutgoingMyPackets` -2. Define length constant -3. Convert constructor to `static void CreateXxx(Span buffer, ...)` -4. Replace `m_Stream.Write(x)` with `writer.Write(x)` -5. Create `SendXxx(this NetState ns, ...)` extension method -6. Check `ns.CannotSendPackets()` at start -7. Use `stackalloc byte[length].InitializePacket()` for buffer -8. Replace `ns.Send(new Packet(...))` with `ns.SendXxx(...)` - -## Conversion Steps (Incoming) -1. Change `PacketHandlers.Register(id, len, ingame, new OnPacketReceive(H))` to `IncomingPackets.Register(id, len, ingame, &H)` in `unsafe Configure()` -2. Change handler: `void H(NetState, PacketReader)` -> `void H(NetState, SpanReader)` -3. Replace read calls: `pvSrc.ReadString()` -> `reader.ReadAsciiSafe()` - -## Quick Mapping -| RunUO | ModernUO | -|---|---| -| `class X : Packet` | Static `CreateX(Span)` method | -| `m_Stream.Write(val)` | `writer.Write(val)` (SpanWriter) | -| `PacketWriter` | `SpanWriter` | -| `PacketReader` / `pvSrc` | `SpanReader` / `reader` | -| `ns.Send(new X(...))` | `ns.SendX(...)` extension | -| `PacketHandlers.Register(...)` | `IncomingPackets.Register(... &handler)` | -| `new OnPacketReceive(H)` | `&H` (function pointer) | - -## See Also -- `dev-docs/runuo-migration-docs/05-packets-networking.md` -- detailed migration reference -- `dev-docs/networking-packets.md` -- complete ModernUO networking system -- `dev-docs/claude-skills/modernuo-networking.md` -- ModernUO networking skill diff --git a/dev-docs/claude-skills/migrate-from-runuo/migrate-persistence.md b/dev-docs/claude-skills/migrate-from-runuo/migrate-persistence.md deleted file mode 100644 index 0fbe257a6..000000000 --- a/dev-docs/claude-skills/migrate-from-runuo/migrate-persistence.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -name: migrate-persistence -description: > - Trigger: when converting RunUO EventSink.WorldSave/WorldLoad manual binary persistence to ModernUO GenericPersistence. - Covers: GenericPersistence subclassing, IGenericWriter/IGenericReader, MarkDirty pattern. ---- - -# RunUO -> ModernUO Persistence Migration - -## When This Activates -- Converting `EventSink.WorldSave`/`EventSink.WorldLoad` patterns -- Converting manual `BinaryFileWriter`/`BinaryFileReader` persistence -- Systems that save custom data outside of Item/Mobile serialization - -## Conversion Steps -1. Create class inheriting `GenericPersistence`: `class MySystem : GenericPersistence` -2. Add static instance + `Configure()`: `_instance = new MySystem();` -3. Call `base("SaveName", 10)` in constructor -4. Move save logic to `override Serialize(IGenericWriter writer)` -5. Move load logic to `override Deserialize(IGenericReader reader)` -6. Remove `EventSink.WorldSave`/`WorldLoad` subscriptions -7. Remove all file management code (Directory.Create, File.Exists, FileStream) -8. Add `_instance.MarkDirty()` wherever data changes -9. Replace `writer.Write(mobile.Serial.Value)` -> `writer.Write(mobile)` -10. Replace `World.FindMobile(reader.ReadInt32())` -> `reader.ReadEntity()` - -## Template -```csharp -public class MySystem : GenericPersistence -{ - private static MySystem _instance; - public static void Configure() => _instance = new MySystem(); - public MySystem() : base("MySystem", 10) { } - public override void Serialize(IGenericWriter writer) { /* save */ } - public override void Deserialize(IGenericReader reader) { /* load */ } -} -``` - -## See Also -- `dev-docs/runuo-migration-docs/08-persistence.md` -- detailed migration reference with before/after -- `dev-docs/serialization.md` -- ModernUO serialization system (IGenericWriter/IGenericReader) -- `dev-docs/claude-skills/modernuo-serialization.md` -- ModernUO serialization skill diff --git a/dev-docs/claude-skills/migrate-from-runuo/migrate-property-lists.md b/dev-docs/claude-skills/migrate-from-runuo/migrate-property-lists.md deleted file mode 100644 index 02cc7dae7..000000000 --- a/dev-docs/claude-skills/migrate-from-runuo/migrate-property-lists.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -name: migrate-property-lists -description: > - Trigger: when converting RunUO GetProperties(ObjectPropertyList) to ModernUO GetProperties(IPropertyList). Critical string literal rule. - Covers: IPropertyList interface, string hole rule, cliloc arguments. ---- - -# RunUO -> ModernUO Property List Migration - -## When This Activates -- Converting `GetProperties(ObjectPropertyList list)` overrides -- Updating tooltip/property list code - -## Conversion Steps -1. Change signature: `GetProperties(ObjectPropertyList list)` -> `GetProperties(IPropertyList list)` -2. Convert string args to interpolation: `list.Add(num, val.ToString())` -> `list.Add(num, $"{val}")` -3. Apply string hole rule: `$"Text\t{val}"` -> `$"{"Text"}\t{val}"` -4. Tab-separated: `string.Format("{0}\t{1}", a, b)` -> `$"{a}\t{b}"` -5. Cliloc as argument: `"#" + cliloc` -> `$"{cliloc:#}"` - -## Critical Rule: String Literals Must Be Holes -Only `\t` should be a bare literal. All text must be inside `{}` holes: -```csharp -// BAD: "Map" is a delimiter -list.Add(1060658, $"Map\t{value}"); -// GOOD: "Map" is an argument -list.Add(1060658, $"{"Map"}\t{value}"); -``` - -## See Also -- `dev-docs/runuo-migration-docs/06-property-lists.md` -- detailed migration reference -- `dev-docs/property-lists.md` -- complete ModernUO property list system -- `dev-docs/claude-skills/modernuo-property-lists.md` -- ModernUO property list skill diff --git a/dev-docs/claude-skills/migrate-from-runuo/migrate-serialization.md b/dev-docs/claude-skills/migrate-from-runuo/migrate-serialization.md deleted file mode 100644 index aa7f8d38c..000000000 --- a/dev-docs/claude-skills/migrate-from-runuo/migrate-serialization.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -name: migrate-serialization -description: > - Trigger: when converting RunUO Serialize/Deserialize methods, adding [SerializableField], converting [Constructable] to [Constructible], or migrating manual serialization code. - Covers: source-generated serialization, field conversion, version handling, TypeAlias. ---- - -# RunUO -> ModernUO Serialization Migration - -## When This Activates -- Converting `Serialize(GenericWriter)`/`Deserialize(GenericReader)` overrides -- Converting `[Constructable]` to `[Constructible]` -- Adding `[SerializableField]` attributes -- Handling save compatibility with `[TypeAlias]` - -## Conversion Steps -1. Add `using ModernUO.Serialization;` -2. Read the old `Serialize()` to find the version number it writes. Bump it by 1 for `[SerializationGenerator]` -3. If old `Deserialize()` used `reader.ReadInt()` (not `ReadEncodedInt()`), pass `false` as second parameter: `[SerializationGenerator(N, false)]` -4. Add `partial` to class declaration -5. Convert each serialized field: `private int m_X` -> `[SerializableField(N)] private int _x` -6. Add `[SerializedCommandProperty(AccessLevel.X)]` if RunUO had `[CommandProperty]` -7. Add `[InvalidateProperties]` if setter called `InvalidateProperties()` -8. DELETE the `Serial` constructor -9. DELETE the `Serialize()` override -10. Convert `Deserialize()` to `private void Deserialize(IGenericReader reader, int version)` to handle pre-codegen saves (remove `override`, `base.Deserialize()`, and version read line). Delete entirely if no existing saves. -11. Change `[Constructable]` to `[Constructible]` -12. Timer fields: leave unserialized, add `[AfterDeserialization]` method - -## Quick Mapping -| RunUO | ModernUO | -|---|---| -| `public class Foo : Item` | `[SerializationGenerator(N, false)] public partial class Foo : Item` (N = old version + 1) | -| `private int m_X` + manual Serialize | `[SerializableField(0)] private int _x` | -| `[CommandProperty(GM)]` on property | `[SerializedCommandProperty(GM)]` on field | -| `Foo(Serial serial) : base(serial)` | DELETE | -| `Serialize(GenericWriter)` | DELETE -- auto-generated | -| `Deserialize(GenericReader)` | Convert to `private void Deserialize(IGenericReader reader, int version)` for old saves | -| Custom setter with InvalidateProperties() | `[InvalidateProperties]` attribute | -| Custom setter logic | `[SerializableProperty(N)]` with `this.MarkDirty()` | -| `reader.ReadMobile()` | `reader.ReadEntity()` | -| `reader.ReadItem()` | `reader.ReadEntity()` | - -## Anti-Patterns -- Missing `partial` keyword -> build error -- Serializing `TimerExecutionToken` -> build error -- Missing `this.MarkDirty()` in `[SerializableProperty]` setter -> changes not saved -- Wrong field prefix (`m_` instead of `_`) - -## See Also -- `dev-docs/runuo-migration-docs/02-serialization.md` -- detailed migration reference with before/after -- `dev-docs/serialization.md` -- complete ModernUO serialization system -- `dev-docs/claude-skills/modernuo-serialization.md` -- ModernUO serialization skill (patterns, attributes, examples) diff --git a/dev-docs/claude-skills/migrate-from-runuo/migrate-systems.md b/dev-docs/claude-skills/migrate-from-runuo/migrate-systems.md deleted file mode 100644 index ad55466d5..000000000 --- a/dev-docs/claude-skills/migrate-from-runuo/migrate-systems.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: migrate-systems -description: > - Trigger: when converting multi-file RunUO engines or systems (crafting, spawners, economy, quests) to ModernUO. - Covers: system mapping, conversion order, file organization, cross-reference handling. ---- - -# RunUO -> ModernUO Multi-File System Migration - -## When This Activates -- Converting RunUO systems with multiple interdependent files -- Converting custom engines (crafting, spawners, economy, quests) -- Organizing RunUO `Scripts/Custom/` code into ModernUO structure - -## Conversion Order -1. **Data types / enums** -- Just naming and namespace changes -2. **Persistence classes** -- `EventSink.WorldSave` -> `GenericPersistence` -3. **Core entities (Items/Mobiles)** -- Full serialization migration -4. **Gumps** -- Convert to `DynamicGump`/`StaticGump` -5. **Commands** -- Usually minimal changes -6. **Packets** -- Convert to `SpanWriter` if custom packets exist -7. **Entry point** -- Update Configure/Initialize registration - -## File Organization -| RunUO | ModernUO | -|---|---| -| `Scripts/Custom/MySystem/` | `Projects/UOContent/Engines/MySystem/` or `Projects/UOContent/Systems/MySystem/` | -| `Scripts/Items/X.cs` | `Projects/UOContent/Items/{Category}/X.cs` | -| `Scripts/Mobiles/X.cs` | `Projects/UOContent/Mobiles/{Category}/X.cs` | -| `Scripts/Gumps/X.cs` | `Projects/UOContent/Gumps/X.cs` | - -## Configuration Migration -| RunUO | ModernUO | -|---|---| -| XML config files | `ServerConfiguration.GetOrUpdateSetting()` or `JsonConfig` | -| Custom .cfg parsing | `ServerConfiguration` for simple, `JsonConfig` for complex | - -## Testing -After converting: `dotnet build`, fix errors, test [add for items, verify gumps, check persistence across save/restart. - -## See Also -- `dev-docs/runuo-migration-docs/10-systems-engines.md` -- detailed system migration patterns -- `dev-docs/configuration.md` -- ModernUO configuration system -- `dev-docs/claude-skills/modernuo-configuration.md` -- ModernUO configuration skill -- All other migrate-* skills for system-specific guidance diff --git a/dev-docs/claude-skills/migrate-from-runuo/migrate-timers.md b/dev-docs/claude-skills/migrate-from-runuo/migrate-timers.md deleted file mode 100644 index ed1b715fa..000000000 --- a/dev-docs/claude-skills/migrate-from-runuo/migrate-timers.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: migrate-timers -description: > - Trigger: when converting RunUO Timer subclasses, Timer.DelayCall patterns, or TimerPriority usage to ModernUO fire-and-forget timers. - Covers: Timer subclass elimination, TimerExecutionToken, callback patterns, timer restoration. ---- - -# RunUO -> ModernUO Timer Migration - -## When This Activates -- Converting nested `Timer` subclasses with `OnTick()` -- Converting `Timer.DelayCall()` patterns -- Removing `TimerPriority` usage -- Restoring timers after deserialization - -## Conversion Steps -1. Move `OnTick()` logic to a method on the parent class -2. Replace `new InternalTimer(this).Start()` with `Timer.StartTimer(..., callback, out _token)` -3. Add `private TimerExecutionToken _token;` (NOT serialized) -4. Cancel in `OnAfterDelete()`: `_token.Cancel();` -5. Restore in `[AfterDeserialization]`: re-call `Timer.StartTimer(...)` -6. Delete the nested Timer class entirely -7. Remove all `TimerPriority` references - -## Quick Mapping -| RunUO | ModernUO | -|---|---| -| `new Timer(delay).Start()` | `Timer.StartTimer(delay, callback)` | -| `new Timer(delay, interval).Start()` | `Timer.StartTimer(delay, interval, callback, out token)` | -| `timer.Stop()` | `token.Cancel()` | -| `Timer.DelayCall(delay, callback)` | `Timer.StartTimer(delay, callback)` | -| `Timer.DelayCall(delay, stateCallback, state)` | `Timer.DelayCall(delay, callback, state)` | -| `TimerPriority.XXX` | Remove -- timer wheel auto-schedules | -| Timer started in `Deserialize()` | `[AfterDeserialization]` method | - -## Anti-Patterns -- Serializing `TimerExecutionToken` -- it's a struct tracking a pooled timer -- Starting timers in `Deserialize()` -- world isn't loaded yet, use `[AfterDeserialization]` -- Lambda closures on hot paths -- use `Timer.DelayCall` with state parameters instead - -## See Also -- `dev-docs/runuo-migration-docs/03-timers.md` -- detailed migration reference -- `dev-docs/timers.md` -- complete ModernUO timer system -- `dev-docs/claude-skills/modernuo-timers.md` -- ModernUO timer skill diff --git a/dev-docs/claude-skills/modernuo-code-audit.md b/dev-docs/claude-skills/modernuo-code-audit.md deleted file mode 100644 index d805b0578..000000000 --- a/dev-docs/claude-skills/modernuo-code-audit.md +++ /dev/null @@ -1,243 +0,0 @@ ---- -name: modernuo-code-audit -description: > - Auto-trigger whenever writing or modifying .cs files under Projects/. Audits code for ModernUO convention violations. Warnings only - flag issues and ask before fixing. ---- - -# ModernUO Code Audit - -## When This Activates -- Any time you write, edit, or modify a `.cs` file under `Projects/` -- After generating code snippets for the user -- During code review - -## Audit Rules (Warnings Only) - -Flag these issues but do NOT auto-fix. Ask the user before making changes. - -### 1. LINQ: Know What's Optimized (.NET 10) -Not all LINQ is banned. .NET 10 JIT/PGO eliminates overhead for specific patterns. Anything not listed below is still forbidden on hot paths. - -**Tier 1 — Zero-cost (use freely on hot paths):** -- `foreach` over `IEnumerable` backed by `T[]`, `List`, `Stack`, `Queue` — PGO devirtualizes the enumerator, zero heap allocation -- `.Contains()` after a preceding LINQ operator (`.Distinct()`, `.OrderBy()`, `.Reverse()`, `.Union()`, `.Intersect()`, `.Except()`, `.Concat()`, `.SelectMany()`, `.Where().Select()`, `.Skip()`, `.Take()`, `.OfType()`, `.Cast()`, `.Shuffle()`) — LINQ has ~30 specialized overrides that skip the intermediate work (no sort, no HashSet, no buffering) -- `.Count()` on sized collections (`ICollection`, or after `Range`/`Repeat`/`Skip`/`Take`/`Append`) — O(1) property access, no enumeration -- `.OrderBy().First()` / `.OrderByDescending().First()` / `.OrderBy().Last()` — O(N) min/max scan, no sort performed -- `.Shuffle().Take(n)` — reservoir sampling, single pass, O(n) memory -- `Enumerable.Range()` / `Enumerable.Sequence()` followed by `.Count()`, `.Contains()`, `.ToArray()`, `.ToList()`, `.ElementAt()`, `.Last()` — arithmetic, not enumeration - -**Tier 2 — Low overhead (acceptable on warm paths, benchmark if critical):** -- `.Skip(n).Take(m).ToArray()` on `T[]`/`List` — vectorized `Span.CopyTo` (still allocates output) -- `.LeftJoin()` / `.RightJoin()` — ~2x faster than manual `GroupJoin`+`SelectMany`+`DefaultIfEmpty` -- `.Where(predicate)` on `T[]`/`List` — `WhereIterator` still heap-allocates, but enumeration is PGO-optimized. Manual `foreach`+`if` is still faster for true hot paths. - -**Tier 3 — Still forbidden on hot paths (write manual code):** -- `.Select(f).Where(p)` (this order — each intermediate iterator allocates) -- `.GroupBy()`, `.ToDictionary()`, `.ToHashSet()`, `.ToLookup()` (always allocate internal structures) -- `.Aggregate()` (delegate overhead per element) -- `.Sum()` / `.Min()` / `.Max()` on `float`/`double` (no SIMD in LINQ on ARM) -- `.SelectMany()` when iterating results (not `.Contains()`) — multiple enumerator allocations -- `.Zip()` when iterating — enumerator allocations -- Any LINQ over `IAsyncEnumerable` — no PGO/escape analysis -- Long chains like `.Where().Select().OrderBy().Take()` — each step allocates an iterator - -**Prerequisites**: .NET 10, tiered compilation + Dynamic PGO enabled (default). Tier 1 optimizations require ~30+ calls for JIT warmup. - -**Quick decision**: If the exact pattern is in Tier 1 → use it. If it's in Tier 2 → acceptable unless profiling shows it's a bottleneck. If it's anything else → manual `for`/`foreach` + `PooledRefList`. - -### 2. No Console.WriteLine -**Bad**: `Console.WriteLine(...)`, `Console.Write(...)` -**Good**: `private static readonly ILogger logger = LogFactory.GetLogger(typeof(MyClass));` then `logger.Information(...)`, `logger.Warning(...)`, `logger.Error(...)` -**Requires**: `using Server.Logging;` - -### 3. No Concurrency Primitives in Game Code -**Bad**: `ConcurrentDictionary`, `ConcurrentQueue`, `ConcurrentBag`, `volatile`, `lock(...)`, `Mutex`, `Semaphore`, `Monitor`, `Interlocked`, `ReaderWriterLock` -**Why**: Server is single-threaded. These add overhead for no benefit. -**Instead**: Use regular `Dictionary`, `List`, plain fields. - -### 4. Never Iterate World.Mobiles or World.Items Directly -**Bad**: `foreach (var m in World.Mobiles.Values)`, `World.Items.Values.Where(...)` -**Good**: `map.GetMobilesInBounds(bounds)`, `map.GetMobilesInRange(point, range)`, `map.GetItemsInRange(point, range)` -**Why**: Full world iteration is O(n) over all entities. Spatial queries use sector indexing. - -### 5. Clean Up References in OnDelete/OnAfterDelete -**Check**: Classes with `Item` or `Mobile` references should clean them in `OnDelete()` or `OnAfterDelete()`. -**Pattern**: -```csharp -public override void OnAfterDelete() -{ - _someReference = null; - base.OnAfterDelete(); -} -``` - -### 6. Cancel Timers in OnDelete/OnAfterDelete -**Check**: Any class with `TimerExecutionToken` or `Timer` fields must cancel them on deletion. -**Pattern**: -```csharp -public override void OnAfterDelete() -{ - _timerToken.Cancel(); // For TimerExecutionToken - _timer?.Stop(); // For Timer references - _timer = null; - base.OnAfterDelete(); -} -``` - -### 7. Use STArrayPool, Not ArrayPool -**Bad**: `ArrayPool.Shared.Rent(...)` in game logic -**Good**: `STArrayPool.Shared.Rent(...)` in game logic -**Why**: STArrayPool is single-threaded optimized (no locks). Use ArrayPool only in explicitly multi-threaded code. -**Also**: Always return rented arrays in a `finally` block. - -### 8. No new List in Hot Paths -**Bad**: `var list = new List();` in frequently-called methods -**Good**: `using var list = PooledRefList.Create();` -**Why**: PooledRefList uses pooled arrays, zero GC pressure. It's a ref struct (stack-allocated). - -### 9. Serialization Class Requirements -**Check**: Classes with `[SerializationGenerator]` MUST be `partial`. -**Check**: `[Constructible]` on parameterless constructors for items/mobiles. -**Check**: `TimerExecutionToken` fields must NOT have `[SerializableField]`. -**Check**: Use `using ModernUO.Serialization;` when using serialization attributes. - -### 10. No Task.Run or new Thread -**Bad**: `Task.Run(...)`, `new Thread(...)`, `ThreadPool.QueueUserWorkItem(...)` in game code -**Why**: Game logic runs on the single-threaded event loop. Background threads cause race conditions. -**Exception**: Server infrastructure code (Projects/Server/Main.cs, World saves) may use threading. - -### 11. Never Assume Era -**Check**: If code uses era-conditional logic (`Core.AOS`, `Core.SE`, etc.) and the user hasn't specified a target era, ASK which expansion to target. -**Why**: Different eras have dramatically different mechanics. - -### 12. Naming Conventions -**Check**: `_camelCase` for private fields, `PascalCase` for properties/methods/classes. -**Note**: Legacy code may use `m_` prefix -- don't flag existing `m_` fields but use `_` for new code. - -### 13. No Empty Gumps -**Check**: Any gump (legacy `Gump` constructor, or `BuildLayout`) must not have a code path that produces zero visual elements (no `AddBackground`, no `AddPage` with content, etc.). -**Why**: The client has no way to close an empty gump — no close button, no right-click dismiss. This leaks a gump slot on both client and server until relog. -**Common cause**: Early `return` in a constructor or `BuildLayout` when prerequisites aren't met. -**Fix**: Use a static `DisplayTo(Mobile from)` method that validates prerequisites **before** constructing the gump. Make the constructor `private`. See `Projects/UOContent/Gumps/Go/GoGump.cs` for the canonical pattern. - -### 14. PropertyList String Literals Must Be Holes -**Check**: In any `IPropertyList.Add()` interpolated string, string constants must be wrapped as holes `{"text"}`, not bare literals. -**Bad**: `list.Add(1060658, $"Chances\t{_charges}");` — "Chances" becomes a delimiter, not an argument. -**Good**: `list.Add(1060658, $"{"Chances"}\t{_charges}");` — "Chances" is an argument. -**Why**: The handler treats bare text as delimiters and `{}` contents as arguments. The property list system is used beyond the game client (e.g., web rendering) which must distinguish arguments from delimiters. Only `\t` should be a bare literal. -**Also**: If you don't know the text for a cliloc number, see `Projects/Server/Localization/Localization.cs` `LoadClilocs()` to learn the binary format, and ask the user where their `cliloc.enu` file is. - -### 15. Braces Required on All Control Flow -**Check**: ALL `if`, `else`, `for`, `foreach`, `while`, `do`, `switch` statements must have braces, even for single-line bodies. -**Bad**: -```csharp -if (condition) - DoSomething(); -``` -**Good**: -```csharp -if (condition) -{ - DoSomething(); -} -``` -**Why**: Reduces merge conflicts and diff sizes. - -### 16. Prefer Switch Expressions and Switch-When Patterns -**Check**: Where a chain of `if`/`else if` maps inputs to outputs, prefer a switch expression. Where pattern matching with guards improves clarity, prefer `switch`-`when`. -**Bad**: -```csharp -if (type == GemType.StarSapphire) return "star sapphire"; -else if (type == GemType.Emerald) return "emerald"; -else return "gem"; -``` -**Good**: -```csharp -return type switch -{ - GemType.StarSapphire => "star sapphire", - GemType.Emerald => "emerald", - _ => "gem" -}; -``` -**Why**: Switch expressions enable JIT/PGO optimization and improve readability. -**Exception**: Skip if the switch would be unreadable or the code is on a cold path. - -### 17. Interpolation Anti-Patterns (handler-aware APIs) - -**Context**: Many ModernUO APIs accept `ref RawInterpolatedStringHandler` (`Mobile.SendMessage`/`Say`/`Emote`/etc., `Item.Public/Local/NonlocalOverheadMessage`/`SendLocalizedMessageTo`/`SendMessageTo`, `IPropertyList.Add`, `SpanWriter.WriteAscii`/`WriteLatin1`, gump `AddLabel`/`AddHtml`/`AddHtmlLocalized`, `Html.Center`/`Color`/`Right`). The handler overload renders the interpolation directly into a pooled buffer with **zero `string` allocation** — but only when the call-site argument is a `$"..."` literal directly in the parameter slot. - -**Check**: Flag any of the following patterns when the call target is one of those handler-aware APIs. The handler overload is silently bypassed and a `string` is allocated per call. - -| Pattern | Fix | -|---|---| -| `Send(cond ? $"a" : $"b")` | `if/else` with two calls | -| `Send(thing switch { 1 => $"a", _ => $"b" })` | `switch` statement, call per arm | -| `var s = $"foo {x}"; Send(s);` (single-use) | Inline at call site | -| `Send($"x {value.ToString()}")` | Drop `.ToString()` — handler formats directly | -| `Send($"x {td.String()}")` | Drop `.String()` — pass `td` directly | -| `Send($"x {a + b}")` (string concat) | Multiple holes: `Send($"x {a}{b}")` | -| `Send(string.Format("x {0}", v))` | `Send($"x {v}")` | -| `Send($"x {items.Aggregate(...)}")` | Build via `ValueStringBuilder`, pass span | - -**For lowercase output**, use the `:L` format specifier instead of `value.ToString().ToLowerInvariant()`: -```csharp -mob.SendMessage($"You earned a {rank:L} trophy!"); // "gold" not "Gold" -``` - -**Why**: These methods are called constantly during gameplay (every chat line, every system message, every gump label, every tooltip). The handler overload exists specifically to eliminate per-call `string` allocation. Each anti-pattern leaks one or more strings per call. - -**Severity**: WARNING. Flag and ask before fixing — some patterns (e.g., reused locals across multiple call sites) are intentional and shouldn't be inlined. - -**See**: `dev-docs/string-handling.md` § "Interpolation Anti-Patterns" for the full reference with detailed before/after examples. - -### 19. No InvalidateProperties From Inside GetProperties -**Check**: Any property read by a `GetProperties` override — including through helpers — must be a pure read. Flag getters that call `InvalidateProperties()` (or a wrapper like `Invalidate()`) as a side effect. -**Bad**: a `Rank` getter that lazily recomputes and then calls `Invalidate()`; reading it from `GetProperties` re-enters the build. -**Good**: invalidate in the setter that actually changes the value, or defer with `Timer.DelayCall(InvalidateProperties)`. -**Why**: `InvalidateProperties()` rebuilds the list in place (`Reset()` + rebuild). `Reset()` returns the pooled interpolation buffer — which the compiler rents for the whole `$"..."` expression, so every hole is evaluated while it is live — and rewinds the packet cursor. Re-entering mid-build throws `ArgumentNullException` (parameter `"array"`) out of `GetProperties` from a line unrelated to the offending getter, or silently corrupts the tooltip. The engine refuses and logs an error, and `DEBUG` throws, so this shows up as a crash in development. -**Note**: Lazy recomputation inside a getter is fine. It is the notification that must not happen there. - -**See**: `dev-docs/property-lists.md` § "Never Invalidate From Inside `GetProperties`". - -### 20. Tick-Count Math Must Be Wraparound-Safe -**Check**: Every comparison between `Core.TickCount` / `Core.GetTimestamp()` values (or fields -derived from them — names like `*Until`, `*At`, `*Next*`, `deadline`) must be in subtraction form. -Flag direct comparisons, zero/sign sentinels, and deadline fields left at their zero default. -**Bad**: `if (Core.TickCount < _deadline)`; `if (_lastEventAt > 0)` as "has happened"; -`private static long _deadline;` compared before being seeded from a real tick. -**Good**: `if (Core.TickCount - _deadline < 0)`; a separate `bool` for "has happened"; seeding -deadline fields from the first observed timestamp. -**Why**: On some hypervisors — Google Cloud specifically — the VM receives a pass-through of the -host's never-resetting counter. Tick counts are NOT zero at process start, NOT zero at OS boot, -can be enormous from the first read, and can wrap negative. Direct comparisons and sign sentinels -then fail only on those hosts, after long host uptimes — the least reproducible bug class there -is. Windows has not shown this in testing; Linux has, in production. Subtraction of two ticks -wraps correctly in two's complement. -**Note**: `DateTime`/`DateTimeOffset` comparisons are unaffected; this applies only to the -monotonic tick domain. - -**See**: `dev-docs/tick-counts.md` for the full rules and review checklist. - -## Severity Levels -- **ERROR**: Rules 3, 9, 10, 13, 19, 20 (will cause bugs, build failures, or client-side leaks) -- **WARNING**: Rules 1 (Tier 3 LINQ), 2, 4, 5, 6, 7, 8, 12, 14, 15, 17 (performance/convention issues) -- **INFO**: Rules 1 (Tier 2 LINQ on warm paths — note it but don't flag as violation), 16 (switch patterns — suggest but don't flag) -- **ASK**: Rule 11 (need user input) - -## How to Report -When you find violations, report them as: -``` -[AUDIT] {SEVERITY}: {Description} - File: {path}:{line} - Suggestion: {fix} -``` - -Do NOT silently fix issues. Always flag and ask. - -## See Also -- `dev-docs/code-standards.md` - Full coding standards documentation -- `dev-docs/claude-skills/modernuo-serialization.md` - Serialization rules -- `dev-docs/claude-skills/modernuo-timers.md` - Timer cleanup rules -- `dev-docs/claude-skills/modernuo-threading.md` - Threading model details -- `dev-docs/claude-skills/modernuo-property-lists.md` - PropertyList interpolation rules diff --git a/dev-docs/claude-skills/modernuo-commands-targeting.md b/dev-docs/claude-skills/modernuo-commands-targeting.md deleted file mode 100644 index 25c9e171b..000000000 --- a/dev-docs/claude-skills/modernuo-commands-targeting.md +++ /dev/null @@ -1,223 +0,0 @@ ---- -name: modernuo-commands-targeting -description: > - Trigger when creating in-game commands, targeting mechanics, or working with CommandSystem/Target. When implementing [commands or player interactions. ---- - -# ModernUO Commands & Targeting - -## When This Activates -- Creating new in-game `[` commands -- Implementing targeting mechanics -- Working with `CommandSystem.Register()` or `Target` class -- Adding GM/admin tools - -## Key Rules - -1. **Register commands in `Configure()`** static method (called at startup) -2. **Use `[Usage]` and `[Description]`** attributes on handler methods -3. **Prefix is `[`** by default (e.g., `[mycommand`) -4. **Target inherits from `Target`** class, override `OnTarget()` -5. **Always validate inputs** in command handlers - -## Command Registration - -```csharp -using Server.Commands; - -namespace Server.Custom; - -public static class MyCommands -{ - public static void Configure() - { - CommandSystem.Register("MyCommand", AccessLevel.GameMaster, MyCommand_OnCommand); - CommandSystem.Register("MyOtherCmd", AccessLevel.Player, MyOtherCmd_OnCommand); - } - - [Usage("MyCommand [count]")] - [Description("Does something with a name and optional count")] - public static void MyCommand_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - - if (e.Length < 1) - { - from.SendMessage("Usage: [MyCommand [count]"); - return; - } - - var name = e.GetString(0); - var count = e.Length > 1 ? e.GetInt32(1) : 1; - - from.SendMessage($"Processing {name} x{count}"); - } -} -``` - -## CommandEventArgs - -```csharp -e.Mobile // Mobile who issued the command -e.Command // Command name string -e.ArgString // Raw argument string -e.Arguments // string[] split arguments -e.Length // Number of arguments - -// Typed argument accessors: -e.GetString(0) // Get string at index (empty string if missing) -e.GetInt32(1) // Get int at index (0 if missing/invalid) -e.GetUInt32(0) // Get uint at index -e.GetBoolean(0) // Get bool at index -e.GetDouble(0) // Get double at index -e.GetTimeSpan(0) // Get TimeSpan at index -``` - -## Access Levels - -```csharp -AccessLevel.Player // Regular players -AccessLevel.Counselor // Support staff -AccessLevel.GameMaster // GMs -AccessLevel.Seer // Event coordinators -AccessLevel.Administrator // Server admins -AccessLevel.Developer // Developers -AccessLevel.Owner // Server owner -``` - -## Target System - -### Basic Target -```csharp -using Server.Targeting; - -private class MyTarget : Target -{ - public MyTarget() : base( - 12, // Range (-1 for unlimited) - false, // AllowGround - TargetFlags.None // None, Harmful, or Beneficial - ) - { - // Optional settings: - // CheckLOS = false; // Skip line-of-sight check - // DisallowMultis = true; // Don't allow targeting multi objects - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is Mobile m) - { - from.SendMessage($"You targeted {m.Name}"); - } - else if (targeted is Item item) - { - from.SendMessage($"You targeted item {item.Name}"); - } - else if (targeted is LandTarget land) - { - from.SendMessage($"You targeted land at {land.Location}"); - } - else if (targeted is StaticTarget st) - { - from.SendMessage($"You targeted static {st.ItemID}"); - } - } - - protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) - { - from.SendMessage("Targeting cancelled."); - } - - protected override void OnTargetFinish(Mobile from) - { - // Always called after targeting completes (success or cancel) - } -} - -// Usage: -from.Target = new MyTarget(); -``` - -### TargetFlags -```csharp -TargetFlags.None // Neutral targeting -TargetFlags.Harmful // Criminal check, combat targeting -TargetFlags.Beneficial // Healing, buffing -``` - -### Target Object Types -- `Mobile` -- a player or creature -- `Item` -- an item in the world or container -- `LandTarget` -- ground tile (`Location`, `TileID`, `Name`) -- `StaticTarget` -- static map object (`Location`, `ItemID`, `Name`, `Hue`) - -### Command + Target Pattern -```csharp -public static void Configure() -{ - CommandSystem.Register("Tame", AccessLevel.GameMaster, Tame_OnCommand); -} - -[Usage("Tame")] -[Description("Force-tames a creature")] -public static void Tame_OnCommand(CommandEventArgs e) -{ - e.Mobile.SendMessage("Select a creature to tame."); - e.Mobile.Target = new TameTarget(); -} - -private class TameTarget : Target -{ - public TameTarget() : base(-1, false, TargetFlags.None) { } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is BaseCreature { Tamable: true } creature) - { - creature.SetControlMaster(from); - from.SendMessage($"You have tamed {creature.Name}."); - } - else - { - from.SendMessage("That cannot be tamed."); - } - } -} -``` - -### Target Validation Overrides -```csharp -// Override these for custom validation: -protected override bool CanTarget(Mobile from, Mobile mobile, ref Point3D loc, ref Map map) -protected override bool CanTarget(Mobile from, Item item, ref Point3D loc, ref Map map) -protected override bool CanTarget(Mobile from, LandTarget land, ref Point3D loc, ref Map map) -protected override bool CanTarget(Mobile from, StaticTarget st, ref Point3D loc, ref Map map) - -// Error handlers: -protected override void OnTargetOutOfRange(Mobile from, object targeted) -protected override void OnTargetOutOfLOS(Mobile from, object targeted) -protected override void OnTargetNotAccessible(Mobile from, object targeted) -protected override void OnTargetDeleted(Mobile from, object targeted) -protected override void OnTargetUntargetable(Mobile from, object targeted) -``` - -## Anti-Patterns - -- **Registering commands outside `Configure()`**: Won't be called during startup -- **Missing access level validation**: Always set appropriate `AccessLevel` -- **Not checking `e.Length`**: Accessing missing arguments returns defaults silently -- **Hardcoded range in targeting**: Use -1 for unlimited, appropriate range for game mechanics - -## Real Examples -- Command registration: `Projects/UOContent/Commands/StaffAccess.cs` -- Target from item: `Projects/UOContent/Items/Misc/InteriorDecorator.cs` -- Spell targeting: `Projects/UOContent/Spells/First/MagicArrow.cs` -- Command system: `Projects/Server/Commands.cs` -- Target base: `Projects/Server/Targeting/Target.cs` -- Attributes: `Projects/Server/Attributes.cs` (Usage, Description, Aliases) - -## See Also -- `dev-docs/commands-targeting.md` - Complete documentation -- `dev-docs/claude-skills/modernuo-gump-system.md` - Commands that open gumps -- `dev-docs/claude-skills/modernuo-content-patterns.md` - Content creation diff --git a/dev-docs/claude-skills/modernuo-configuration.md b/dev-docs/claude-skills/modernuo-configuration.md deleted file mode 100644 index fe6a6a32c..000000000 --- a/dev-docs/claude-skills/modernuo-configuration.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -name: modernuo-configuration -description: > - Trigger when adding server settings, reading config values, or working with modernuo.json or JsonConfig. ---- - -# ModernUO Configuration System - -## When This Activates -- Adding new server settings -- Reading config values with `ServerConfiguration` -- Using `JsonConfig.Serialize/Deserialize` -- Working with `modernuo.json` or custom config files - -## Key Rules - -1. **Read settings in `Configure()`** static method -2. **Use `GetOrUpdateSetting()`** when you want the setting created with a default if missing -3. **Use `GetSetting()`** for read-only access (won't write default to file) -4. **Custom config files use `JsonConfig.Serialize/Deserialize()`** -5. **Config path base**: `Distribution/Configuration/` - -## ServerConfiguration - -### GetOrUpdateSetting (Creates Default If Missing) -```csharp -// In Configure() method: -var maxAccounts = ServerConfiguration.GetOrUpdateSetting("accountHandler.maxAccountsPerIP", 1); -var saveDelay = ServerConfiguration.GetOrUpdateSetting("autosave.saveDelay", TimeSpan.FromMinutes(5)); -var enabled = ServerConfiguration.GetOrUpdateSetting("mySystem.enabled", true); -``` - -If the key doesn't exist in `modernuo.json`, it writes the default value and returns it. - -### GetSetting (Read-Only) -```csharp -var statMax = ServerConfiguration.GetSetting("stats.statMax", 100); -var usePub45 = ServerConfiguration.GetSetting("stats.usePub45StatGain", false); -``` - -Returns the default if key is missing but does NOT write to the config file. - -### Supported Types -```csharp -ServerConfiguration.GetSetting(string key, int defaultValue) -ServerConfiguration.GetSetting(string key, bool defaultValue) -ServerConfiguration.GetSetting(string key, double defaultValue) -ServerConfiguration.GetSetting(string key, TimeSpan defaultValue) -ServerConfiguration.GetSetting(string key, T defaultValue) where T : struct, Enum -``` - -### SetSetting -```csharp -ServerConfiguration.SetSetting("mySystem.customValue", "42"); -// Immediately persisted to modernuo.json -``` - -## Configuration Pattern - -```csharp -namespace Server.Custom; - -public static class MySystem -{ - private static bool _enabled; - private static int _maxItems; - private static TimeSpan _cooldown; - - public static void Configure() - { - _enabled = ServerConfiguration.GetOrUpdateSetting("mySystem.enabled", true); - _maxItems = ServerConfiguration.GetOrUpdateSetting("mySystem.maxItems", 100); - _cooldown = ServerConfiguration.GetOrUpdateSetting("mySystem.cooldown", TimeSpan.FromMinutes(5)); - } - - // System logic uses _enabled, _maxItems, _cooldown... -} -``` - -## Custom Config Files with JsonConfig - -For complex configuration that doesn't fit in `modernuo.json`: - -```csharp -using Server.Json; - -public static class MyComplexSystem -{ - private static MyConfig _config; - private static readonly string ConfigPath = - Path.Combine(Core.BaseDirectory, "Configuration/MySystem/config.json"); - - public static void Configure() - { - _config = JsonConfig.Deserialize(ConfigPath) - ?? new MyConfig(); - } - - public static void SaveConfig() - { - JsonConfig.Serialize(ConfigPath, _config); - } -} - -public class MyConfig -{ - public bool Enabled { get; set; } = true; - public int MaxItems { get; set; } = 100; - public List BlockedNames { get; set; } = new(); - public Dictionary Scores { get; set; } = new(); -} -``` - -### JsonConfig Options -- Pretty-printed (WriteIndented = true) -- Comments allowed (ReadCommentHandling = Skip) -- Trailing commas allowed -- Null values omitted (WhenWritingNull) -- Enums serialized as strings (JsonStringEnumConverter) -- Built-in converters for: `ClientVersion`, `Guid`, `Map`, `Point3D`, `Rectangle3D`, `TimeSpan`, `IPEndPoint`, `Type`, `WorldLocation`, `TextDefinition` - -## modernuo.json Structure - -Location: `Distribution/Configuration/modernuo.json` - -```json -{ - "assemblyDirectories": ["./Assemblies"], - "dataDirectories": ["C:\\Ultima Online Classic"], - "listeners": ["0.0.0.0:2593"], - "settings": { - "accountHandler.enableAutoAccountCreation": "True", - "accountHandler.maxAccountsPerIP": "1", - "autosave.enabled": "True", - "autosave.saveDelay": "00:05:00", - "world.savePath": "Saves", - "stats.statMax": "100", - "timer.initialPoolCapacity": "1024", - "mySystem.enabled": "True" - } -} -``` - -All settings are stored as strings in the `settings` dictionary. - -## Naming Convention for Keys - -Use dot-separated hierarchical keys: -``` -systemName.settingName -systemName.subSystem.settingName -``` - -Examples: -- `accountHandler.maxAccountsPerIP` -- `stats.statMax` -- `movement.delay.walkFoot` -- `autosave.saveDelay` - -## Anti-Patterns - -- **Reading config in constructors**: Use `Configure()` static method instead -- **Hardcoding values**: Use `ServerConfiguration.GetOrUpdateSetting()` for tunable values -- **Complex objects in modernuo.json**: Use `JsonConfig` with separate files instead -- **Not providing defaults**: Always pass a sensible default value - -## Real Examples -- ServerConfiguration: `Projects/Server/Configuration/ServerConfiguration.cs` -- JsonConfig: `Projects/Server/Json/JsonConfig.cs` -- Config usage: `Projects/UOContent/Skills/SkillCheck.cs` (Configure method) -- Timer pool config: `Projects/Server/Timer/Timer.Pool.cs` -- Main config: `Distribution/Configuration/modernuo.json` - -## See Also -- `dev-docs/configuration.md` - Complete configuration documentation -- `dev-docs/claude-skills/modernuo-era-expansion.md` - Expansion configuration -- `dev-docs/claude-skills/modernuo-events.md` - Configure() pattern diff --git a/dev-docs/claude-skills/modernuo-content-patterns.md b/dev-docs/claude-skills/modernuo-content-patterns.md deleted file mode 100644 index ce7f18acf..000000000 --- a/dev-docs/claude-skills/modernuo-content-patterns.md +++ /dev/null @@ -1,376 +0,0 @@ ---- -name: modernuo-content-patterns -description: > - Trigger when creating new items, mobiles, creatures, spells, skills, loot tables, or any game content under Projects/UOContent/. This is the hub skill that connects to all other ModernUO skills. ---- - -# ModernUO Content Patterns (Hub Skill) - -## When This Activates -- Creating new items, weapons, armor, clothing, containers -- Creating new creatures, NPCs, vendors -- Creating new spells -- Implementing skill handlers -- Adding loot tables -- Adding context menus -- Any new game content under `Projects/UOContent/` - -## Key Rules - -1. **Always ask target era** if the user hasn't specified (see `modernuo-era-expansion.md`) -2. **All serializable classes must be `partial`** with `[SerializationGenerator]` -3. **All Item/Mobile constructors need `[Constructible]`** -4. **Clean up timers and references in `OnDelete()`/`OnAfterDelete()`** -5. **No LINQ** in game logic -- use loops and `PooledRefList` -6. **File placement** matters -- follow the directory conventions below -7. **Creature speeds are delays in seconds, on two clocks** -- think - (`ActiveSpeed`/`PassiveSpeed`, seconds per AI decision) and move - (`ActiveMoveSpeed`/`PassiveMoveSpeed`, seconds per step; inherits think until - overridden). Prefer `npc-speeds.json` buckets (`SpeedClass`); `SetSpeed()` sets think - AND clears move overrides, `SetMoveSpeed()` sets move only -- see - `dev-docs/content-patterns.md` § Creature Speeds - -## New Item Template - -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class MyItem : Item -{ - [Constructible] - public MyItem() : base(0x1234) // itemID from art - { - Weight = 1.0; - // Stackable = true; // if stackable - // Amount = 1; // if stackable - } - - public override string DefaultName => "a my item"; - // OR: public override int LabelNumber => 1234567; // cliloc number - - public override void OnDoubleClick(Mobile from) - { - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); // Must be in backpack - return; - } - - // Item use logic - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - // list.Add(1060741, $"{_charges}"); // charges: ~1_val~ - } -} -``` - -## New Creature Template - -```csharp -using ModernUO.Serialization; -using Server.Items; - -namespace Server.Mobiles; - -[SerializationGenerator(0)] -public partial class MyCreature : BaseCreature -{ - [Constructible] - public MyCreature() : base(AIType.AI_Melee, FightMode.Closest) - { - Body = 0; // Body graphic ID - BaseSoundID = 0; // Base sound ID - - SetStr(100, 150); // Strength min/max - SetDex(80, 100); // Dexterity min/max - SetInt(30, 50); // Intelligence min/max - - SetHits(80, 120); - SetMana(0); - - SetDamage(8, 14); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 30, 40); - SetResistance(ResistanceType.Fire, 10, 20); - SetResistance(ResistanceType.Cold, 10, 20); - SetResistance(ResistanceType.Poison, 15, 25); - SetResistance(ResistanceType.Energy, 10, 20); - - SetSkill(SkillName.MagicResist, 30.0, 50.0); - SetSkill(SkillName.Tactics, 50.0, 70.0); - SetSkill(SkillName.Wrestling, 50.0, 70.0); - - Fame = 1000; - Karma = -1000; // Negative = evil, positive = good, 0 = neutral - - VirtualArmor = 30; - } - - public override string CorpseName => "a creature corpse"; - public override string DefaultName => "a creature"; - - // Optional overrides: - // public override int Meat => 1; - // public override int Hides => 8; - // public override HideType HideType => HideType.Regular; - // public override FoodType FavoriteFood => FoodType.Meat; - // public override PackInstinct PackInstinct => PackInstinct.Canine; - // public override bool CanRummageCorpses => true; - // public override Poison PoisonImmune => Poison.Lesser; - // public override Poison HitPoison => Poison.Regular; - - public override void GenerateLoot() - { - AddLoot(LootPack.Average); - AddLoot(LootPack.Gems, 1); - // PackItem(new SpecificItem()); - // PackGold(50, 100); - } -} -``` - -### Tameable Creature Additions -```csharp -// In constructor: -Tamable = true; -ControlSlots = 1; // 1-5, how many pet slots it uses -MinTameSkill = 35.1; // Required Animal Taming skill -``` - -### AI Types -| AIType | Behavior | -|---|---| -| `AI_Melee` | Charges into melee combat | -| `AI_Mage` | Casts spells, keeps distance | -| `AI_Archer` | Uses ranged attacks | -| `AI_Animal` | Passive, flees or fights back | -| `AI_Predator` | Hunts other creatures | -| `AI_Healer` | Heals allies | -| `AI_Vendor` | NPC vendor behavior | -| `AI_Berserk` | Aggressive, attacks everything | -| `AI_Thief` | Steals from players | - -### Fight Modes -| FightMode | Target Selection | -|---|---| -| `None` | Never attacks | -| `Aggressor` | Only attacks those who attack first | -| `Strongest` | Targets highest stats | -| `Weakest` | Targets lowest stats | -| `Closest` | Targets nearest entity | -| `Evil` | Attacks aggressors or negative-karma entities | - -## New Spell Template - -```csharp -using System; -using Server.Targeting; - -namespace Server.Spells.First; - -public class MySpell : MagerySpell, ITargetingSpell -{ - private static readonly SpellInfo _info = new( - "Spell Name", // Display name - "In Vas Ort", // Power words (mantra) - 212, // Cast animation action - 9041, // Cast sound - Reagent.Bloodmoss, // Required reagents - Reagent.MandrakeRoot - ); - - public MySpell(Mobile caster, Item scroll = null) : base(caster, scroll, _info) - { - } - - public override SpellCircle Circle => SpellCircle.First; - - public void Target(Mobile m) - { - if (CheckHSequence(m)) // Harmful spell check - { - SpellHelper.Turn(Caster, m); - - double damage = GetNewAosDamage(10, 1, 4, m); - - SpellHelper.Damage(this, m, damage, 0, 100, 0, 0, 0); - // Damage types: phys, fire, cold, poison, energy (must sum to 100) - } - } - - public override void OnCast() - { - Caster.Target = new SpellTarget(this, TargetFlags.Harmful); - } -} -``` - -### Spell Circles (Magery) -| Circle | Mana Cost | Min Skill | -|---|---|---| -| First | 4 | -50.0 (Pre-ML) / -46.0 (ML+) | -| Second | 6 | -30.0 / -32.0 | -| Third | 9 | 0.0 / -18.0 | -| Fourth | 11 | 10.0 / -4.0 | -| Fifth | 14 | 20.0 / 10.0 | -| Sixth | 20 | 30.0 / 24.0 | -| Seventh | 40 | 40.0 / 38.0 | -| Eighth | 50 | 50.0 / 52.0 | - -## Skill Implementation - -```csharp -using Server.Targeting; - -namespace Server.Skills; - -public static class MySkillHandler -{ - public static void Initialize() - { - // Register handler delegate - SkillInfo.Table[(int)SkillName.Alchemy].Callback = OnUse; - } - - public static TimeSpan OnUse(Mobile from) - { - from.SendMessage("You begin working..."); - from.Target = new InternalTarget(); - return TimeSpan.FromSeconds(1.0); // Delay before next use - } - - private class InternalTarget : Target - { - public InternalTarget() : base(2, false, TargetFlags.None) - { - } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is Item item) - { - // from.CheckSkill(SkillName.Alchemy, minSkill, maxSkill) - if (from.CheckSkill(SkillName.Alchemy, 0.0, 100.0)) - { - from.SendMessage("Success!"); - } - else - { - from.SendMessage("You fail."); - } - } - } - } -} -``` - -## Loot Packs - -Use predefined packs -- they auto-select era-appropriate loot: -```csharp -public override void GenerateLoot() -{ - AddLoot(LootPack.Poor); // ~50 gold equivalent - AddLoot(LootPack.Meager); // ~100 gold equivalent - AddLoot(LootPack.Average); // ~250 gold equivalent - AddLoot(LootPack.Rich); // ~500 gold equivalent - AddLoot(LootPack.FilthyRich); // ~1000 gold equivalent - AddLoot(LootPack.UltraRich); // ~2000 gold equivalent - AddLoot(LootPack.SuperBoss); // Boss-level loot - - AddLoot(LootPack.Gems, 2); // 2 random gems - AddLoot(LootPack.Potions); // Random potion - - // Specific items - PackItem(new Arrow(Utility.RandomMinMax(20, 40))); - PackGold(100, 200); -} -``` - -## Context Menus - -```csharp -public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) -{ - base.GetContextMenuEntries(from, ref list); - - if (from.Alive && from.InRange(this, 2)) - { - list.Add(new MyContextMenuEntry(this)); - } -} - -private class MyContextMenuEntry : ContextMenuEntry -{ - private readonly Item _item; - - public MyContextMenuEntry(Item item) : base(6100) // Cliloc number - { - _item = item; - } - - public override void OnClick(Mobile from, IEntity target) - { - // Handle click - } -} -``` - -## Two-Phase Deletion - -```csharp -public override void OnDelete() -{ - _timerToken.Cancel(); // Cancel timers FIRST - base.OnDelete(); -} - -public override void OnAfterDelete() -{ - _timer?.Stop(); // Stop Timer references - _timer = null; - _owner = null; // Clear Mobile/Item references - base.OnAfterDelete(); -} -``` - -## File Placement - -| Content Type | Directory | -|---|---| -| Items | `Projects/UOContent/Items/{Category}/` | -| Weapons | `Projects/UOContent/Items/Weapons/{Type}/` | -| Armor | `Projects/UOContent/Items/Armor/{Type}/` | -| Creatures | `Projects/UOContent/Mobiles/{Type}/` | -| Animals | `Projects/UOContent/Mobiles/Animals/{Species}/` | -| Monsters | `Projects/UOContent/Mobiles/Monsters/{Era}/` | -| Spells | `Projects/UOContent/Spells/{School}/` | -| Skills | `Projects/UOContent/Skills/` | -| Engines | `Projects/UOContent/Engines/{SystemName}/` | -| Gumps | `Projects/UOContent/Gumps/` | - -## Real Examples -- Simple creature: `Projects/UOContent/Mobiles/Animals/Bears/BlackBear.cs` -- Boss creature: `Projects/UOContent/Mobiles/Special/Barracoon.cs` -- SE creature: `Projects/UOContent/Mobiles/Monsters/SE/RaiJu.cs` -- Spell: `Projects/UOContent/Spells/First/MagicArrow.cs` -- Skill check: `Projects/UOContent/Skills/SkillCheck.cs` -- Loot packs: `Projects/UOContent/Misc/LootPack.cs` - -## See Also -- `dev-docs/claude-skills/modernuo-serialization.md` - Serialization details -- `dev-docs/claude-skills/modernuo-era-expansion.md` - Era-conditional code -- `dev-docs/claude-skills/modernuo-timers.md` - Timer patterns -- `dev-docs/claude-skills/modernuo-property-lists.md` - Item tooltips -- `dev-docs/claude-skills/modernuo-gump-system.md` - UI dialogs -- `dev-docs/claude-skills/modernuo-commands-targeting.md` - Commands and targeting -- `dev-docs/claude-skills/modernuo-events.md` - Event system -- `dev-docs/content-patterns.md` - Full content documentation diff --git a/dev-docs/claude-skills/modernuo-era-expansion.md b/dev-docs/claude-skills/modernuo-era-expansion.md deleted file mode 100644 index f8a9b18e7..000000000 --- a/dev-docs/claude-skills/modernuo-era-expansion.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -name: modernuo-era-expansion -description: > - Trigger when writing era-conditional code, using Core.AOS/SE/ML etc., or when user hasn't specified target era. Always ask which expansion to target if not specified. ---- - -# ModernUO Era & Expansion Support - -## When This Activates -- Writing code that depends on game era (damage formulas, skill caps, mechanics) -- Using `Core.AOS`, `Core.SE`, `Core.ML`, etc. -- User asks for a feature without specifying era -- Implementing mechanics that changed across expansions - -## CRITICAL RULE -**Never assume era.** If the user hasn't specified which expansion to target, ASK before writing era-dependent code. - -## Expansion Enum - -```csharp -public enum Expansion -{ - None, // 0 - Pre-T2A - T2A, // 1 - The Second Age - UOR, // 2 - Renaissance - UOTD, // 3 - Third Dawn - LBR, // 4 - Blackthorn's Revenge - AOS, // 5 - Age of Shadows (major overhaul) - SE, // 6 - Samurai Empire - ML, // 7 - Mondain's Legacy - SA, // 8 - Stygian Abyss - HS, // 9 - High Seas - TOL, // 10 - Time of Legends - EJ // 11 - Endless Journey -} -``` - -## Era Check Properties - -Each returns `true` when `Core.Expansion >= that era`: - -```csharp -Core.T2A // >= The Second Age -Core.UOR // >= Renaissance -Core.UOTD // >= Third Dawn -Core.LBR // >= Blackthorn's Revenge -Core.AOS // >= Age of Shadows -Core.SE // >= Samurai Empire -Core.ML // >= Mondain's Legacy -Core.SA // >= Stygian Abyss -Core.HS // >= High Seas -Core.TOL // >= Time of Legends -Core.EJ // >= Endless Journey -``` - -For exact expansion: `Core.Expansion == Expansion.AOS` - -## Key Era Boundaries - -### AOS (Age of Shadows) -- Most Significant Change -- Complete combat overhaul: resistance-based damage system -- Property-based item system (magic properties) -- New damage formula: `GetNewAosDamage()` vs flat `Utility.Random()` -- Luck system for loot -- Insurance system - -### SE (Samurai Empire) -- Bushido / Ninjitsu skills -- Samurai/Ninja classes -- Loot pack adjustments - -### ML (Mondain's Legacy) -- Spellweaving -- Adjusted skill gain chances -- Container weight display changes - -## Pattern: Era-Conditional Code - -```csharp -// Ternary for simple values -var delay = Core.SE ? 250 : Core.AOS ? 500 : 1000; - -// If/else for logic branches -if (Core.AOS) -{ - damage = GetNewAosDamage(10, 1, 4, target); -} -else -{ - damage = Utility.Random(4, 4); - if (CheckResisted(target)) - damage *= 0.75; -} - -// Property display varies by era -if (Core.ML) -{ - list.Add(1072241, $"{TotalItems}\t{MaxItems}\t{TotalWeight}\t{MaxWeight}"); -} -else -{ - list.Add(1050044, $"{TotalItems}\t{TotalWeight}"); -} -``` - -## Pattern: Era-Dependent Loot - -`LootPack` properties auto-select based on expansion: -```csharp -// These automatically pick the right era variant -LootPack.Poor // OldPoor / AosPoor / SePoor -LootPack.Average // OldAverage / AosAverage / SeAverage -LootPack.Rich // OldRich / AosRich / SeRich -LootPack.FilthyRich // OldFilthyRich / AosFilthyRich / SeFilthyRich -LootPack.UltraRich // OldUltraRich / AosUltraRich / SeUltraRich -``` - -## Anti-Patterns - -- **Hardcoding mechanics for one era** without conditional checks -- **Assuming AOS** when the user might want pre-AOS -- **Using era-specific APIs** without checking (e.g., `GetNewAosDamage()` pre-AOS) - -## Real Examples -- Era-conditional damage: `Projects/UOContent/Spells/First/MagicArrow.cs` -- Era-conditional properties: `Projects/Server/Items/Container.cs` (`GetProperties`) -- Era-conditional values: `Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs` -- Expansion enum: `Projects/Server/ExpansionInfo.cs` -- Stat config by era: `Projects/UOContent/Skills/SkillCheck.cs` - -## See Also -- `dev-docs/era-expansion.md` - Complete expansion documentation -- `dev-docs/claude-skills/modernuo-content-patterns.md` - Content templates -- `dev-docs/claude-skills/modernuo-configuration.md` - Configuration system diff --git a/dev-docs/claude-skills/modernuo-event-scheduler.md b/dev-docs/claude-skills/modernuo-event-scheduler.md deleted file mode 100644 index 63ccdb83e..000000000 --- a/dev-docs/claude-skills/modernuo-event-scheduler.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -name: modernuo-event-scheduler -description: > - Trigger when creating holiday events, seasonal content, scheduled maintenance, daily/weekly resets, or any wall-clock/calendar-based scheduling. When using EventScheduler, ScheduledEvent, YearlyScheduledEvent, or IRecurrencePattern. ---- - -# ModernUO EventScheduler (Wall-Clock / Calendar Scheduling) - -## When This Activates -- Creating holiday or seasonal events (Halloween, Christmas, etc.) -- Scheduling daily/weekly/monthly resets or activities -- Any event that must fire at a real-world time or date -- Working with `EventScheduler`, `ScheduledEvent`, `YearlyScheduledEvent`, `CallbackScheduledEvent` -- Working with `IRecurrencePattern`, `AllowedDays`, `AllowedMonths`, `MonthDay` - -## Key Rules - -1. **EventScheduler for wall-clock/calendar, Timer for game-tick delays** — if the event is "at 9 AM every Monday," use EventScheduler; if it's "5 seconds from now," use Timer -2. **Always specify timezone** for local-time events — omitting defaults to UTC -3. **Prefer `CallbackScheduledEvent` (via static methods)** for simple recurring actions -4. **Use `YearlyScheduledEvent`** for seasonal windows (e.g., Oct 15 - Nov 1 each year) -5. **Cancel events on cleanup** — call `Cancel()` when disabling or shutting down -6. **1-second granularity** — EventScheduler ticks every second, not suitable for sub-second precision - -## Timer vs EventScheduler Decision - -| Need | Use | -|---|---| -| "Every 5 seconds" | `Timer.StartTimer` | -| "At 6:00 AM daily" | `EventScheduler.DailyAt` | -| "Delete after 10 seconds" | `Timer.StartTimer` | -| "Every Monday at noon" | `EventScheduler.WeeklyAt` | -| "Combat tick every 250ms" | `Timer.StartTimer` | -| "Oct 15 - Nov 1 each year" | `YearlyScheduledEvent` | -| "First Tuesday of each month" | `MonthlyOrdinalRecurrencePattern` | - -## Quick Patterns - -### Daily Event at a Specific Time -```csharp -var eastern = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); -var startOn = new DateTime(2025, 1, 1, 6, 0, 0); // 6:00 AM -EventScheduler.DailyAt(startOn, ResetDailyQuests, eastern); -``` - -### Weekly Event -```csharp -var startOn = new DateTime(2025, 1, 6, 18, 0, 0); // Monday 6:00 PM -EventScheduler.WeeklyAt(startOn, StartWeeklyTournament, eastern); -``` - -### Monthly Event on a Specific Day -```csharp -var startOn = new DateTime(2025, 1, 15, 12, 0, 0); // 15th at noon -EventScheduler.MonthlyAt(startOn, MonthlyRewards, eastern); -``` - -### Yearly Seasonal Event (with Window) -```csharp -var halloween = new YearlyCallbackScheduledEvent( - new TimeOnly(0, 0), - new MonthDay(2025, 10, 15), // Start: Oct 15 - new MonthDay(2025, 11, 1), // End: Nov 1 - SpawnHalloweenContent, - EventScheduler.Daily -); -halloween.Schedule(DateTime.UtcNow, eastern); -``` - -### Filtered Weekly (Specific Days/Months) -```csharp -var pattern = new WeeklyRecurrencePattern( - intervalWeeks: 1, - allowedMonths: AllowedMonths.June | AllowedMonths.July | AllowedMonths.August, - allowedDays: AllowedDays.Friday | AllowedDays.Saturday -); -var evt = new CallbackScheduledEvent(new TimeOnly(20, 0), SummerWeekendEvent, pattern); -evt.Schedule(DateTime.UtcNow, eastern); -``` - -### Ordinal Monthly (e.g., "Second Tuesday") -```csharp -var pattern = new MonthlyOrdinalRecurrencePattern( - OrdinalDayOccurrence.Second, - DayOfWeek.Tuesday -); -var evt = new CallbackScheduledEvent(new TimeOnly(12, 0), MonthlyMeeting, pattern); -evt.Schedule(DateTime.UtcNow, eastern); -``` - -## Custom Event Class Template - -```csharp -using System; -using Server.Engines.Events; - -public class MyScheduledEvent : ScheduledEvent -{ - public MyScheduledEvent(TimeOnly time, IRecurrencePattern recurrence) - : base(time, recurrence) - { - } - - public override void OnEvent() - { - // Your event logic here - } -} - -// Schedule it: -var evt = new MyScheduledEvent(new TimeOnly(9, 0), EventScheduler.Daily); -evt.Schedule(DateTime.UtcNow, timeZone); - -// Cancel it: -evt.Cancel(); -``` - -### Custom Yearly Seasonal Event Template - -```csharp -public class MySeasonalEvent : YearlyScheduledEvent -{ - protected MySeasonalEvent( - TimeOnly time, - MonthDay yearlyStart, - MonthDay yearlyEnd, - IRecurrencePattern recurrence - ) : base(time, yearlyStart, yearlyEnd, recurrence) - { - } - - public override void OnEvent() - { - // Only fires when date is within [yearlyStart, yearlyEnd] - } -} -``` - -## Anti-Patterns - -- **Using `Timer.StartTimer` for calendar events**: Timers drift across restarts and have no timezone support — use EventScheduler -- **Forgetting to specify timezone**: Event fires at UTC instead of expected local time — always pass `TimeZoneInfo` -- **Not cancelling events on cleanup**: Scheduled events keep firing after the system is disabled — call `Cancel()` -- **Using EventScheduler for sub-second timing**: 1-second granularity is too coarse — use `Timer.StartTimer` -- **Constructing `MonthDay` with invalid day**: Throws `ArgumentOutOfRangeException` — validate against `DateTime.DaysInMonth` - -## See Also -- `dev-docs/event-scheduler.md` — Complete EventScheduler documentation -- `dev-docs/timers.md` — Game-tick timer system (Timer.StartTimer, TimerExecutionToken) -- `dev-docs/claude-skills/modernuo-timers.md` — Timer skill for game-tick delays diff --git a/dev-docs/claude-skills/modernuo-events.md b/dev-docs/claude-skills/modernuo-events.md deleted file mode 100644 index 6febbbfa0..000000000 --- a/dev-docs/claude-skills/modernuo-events.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -name: modernuo-events -description: > - Trigger when subscribing to or creating game events, working with EventSink or generated events. When hooking into player login, death, speech, or other game events. ---- - -# ModernUO Events System - -## When This Activates -- Subscribing to game events (login, logout, death, speech) -- Creating new events -- Working with `EventSink` static events -- Using `[GeneratedEvent]` / `[OnEvent]` attributes - -## Key Rules - -1. **Subscribe to events in `Configure()`** static method -2. **EventSink events are `static event Action`** -- subscribe with `+=` -3. **Event handlers must match the delegate signature** -4. **Unsubscribe if your system can be disabled** (prevent leaks) - -## EventSink Events - -Subscribe in `Configure()`: -```csharp -public static void Configure() -{ - EventSink.Connected += OnPlayerConnected; - EventSink.Disconnected += OnPlayerDisconnected; - EventSink.Logout += OnLogout; - EventSink.ServerStarted += OnServerStarted; -} -``` - -### Available Events - -#### Core Lifecycle -```csharp -EventSink.ServerStarted // Action -- Server fully started -EventSink.Shutdown // Action -- Server shutting down -EventSink.WorldLoad // Action -- World loaded from saves -EventSink.WorldSave // Action -- World save triggered -EventSink.WorldSavePostSnapshot // Action -``` - -#### Player Connection -```csharp -EventSink.Connected // Action -- Player connected -EventSink.BeforeDisconnected // Action -- About to disconnect -EventSink.Disconnected // Action -- Player disconnected -EventSink.Logout // Action -- Player logged out -``` - -#### Account -```csharp -EventSink.AccountLogin // Action -// AccountLoginEventArgs: .State (NetState), .Username, .Password, .Accepted (set), .RejectReason (set) -``` - -#### Communication -```csharp -EventSink.Speech // Action -// SpeechEventArgs: .Mobile, .Speech, .Type, .Hue, .Keywords, .Handled (set), .Blocked (set) -``` - -#### Combat -```csharp -EventSink.AggressiveAction // Action -// AggressiveActionEventArgs: .Aggressed, .Aggressor, .Criminal -``` - -#### Movement -```csharp -EventSink.Movement // Action -// MovementEventArgs: .Mobile, .Direction, .Blocked (set) -``` - -#### Network -```csharp -EventSink.SocketConnect // Action -// SocketConnectEventArgs: .Address, .AllowConnection (set) - -EventSink.ServerCrashed // Action -// ServerCrashedEventArgs: .Exception, .Close (set) -``` - -#### UI -```csharp -EventSink.PaperdollRequest // Action -- (beholder, beheld) -``` - -## Event Handler Pattern - -```csharp -public static class MyEventSystem -{ - public static void Configure() - { - EventSink.Connected += OnConnected; - EventSink.Speech += OnSpeech; - } - - private static void OnConnected(Mobile m) - { - if (m is PlayerMobile pm) - { - pm.SendMessage("Welcome back!"); - } - } - - private static void OnSpeech(SpeechEventArgs e) - { - if (e.Speech.InsensitiveContains("help")) - { - e.Mobile.SendMessage("How can I help you?"); - e.Handled = true; // Prevent further processing - } - } -} -``` - -## Generated Events (Code-Generated) - -For custom events on game entities, use the CodeGeneratedEvents package: - -### Defining Events -```csharp -// On the class that fires the event: -[GeneratedEvent(nameof(PlayerLoginEvent))] -public static partial void PlayerLoginEvent(PlayerMobile player); -``` - -### Subscribing to Events -```csharp -// On the handler class: -[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))] -public static void HandleLogin(PlayerMobile player) -{ - // Handle the event -} -``` - -### Known Generated Events -- `PlayerMobile.PlayerLoginEvent` -- `PlayerMobile.PlayerDeathEvent` -- `BaseCreature.CreatureDeathEvent` - -External reference: https://github.com/modernuo/CodeGeneratedEvents - -## EventArgs Pool Pattern - -Some EventArgs use object pooling to avoid allocation: -```csharp -// Movement uses pooling: -var args = MovementEventArgs.Create(mobile, dir); -EventSink.InvokeMovement(args); -args.Free(); // Return to pool - -// AggressiveAction uses pooling: -var args = AggressiveActionEventArgs.Create(aggressed, aggressor, criminal); -EventSink.InvokeAggressiveAction(args); -args.Free(); -``` - -## Anti-Patterns - -- **Subscribing outside `Configure()`**: Won't be called during startup -- **Not checking player type**: `EventSink.Connected` fires for all mobiles, cast to `PlayerMobile` if needed -- **Blocking in event handlers**: Event handlers run on the game loop -- keep them fast -- **Not unsubscribing**: If system can be disabled, unsubscribe to prevent leaks - -## Real Examples -- EventSink core: `Projects/Server/Events/EventSink.cs` -- Speech events: `Projects/Server/Events/SpeechEvent.cs` -- Movement events: `Projects/Server/Events/MovementEvent.cs` -- Account events: `Projects/Server/Events/AccountLoginEvent.cs` -- World events: `Projects/Server/Events/EventSink.cs` (WorldLoad, WorldSave, ServerStarted, Shutdown) -- Connection events: `Projects/Server/Events/SocketConnectionEvent.cs` -- GumpSystem subscription: `Projects/UOContent/Gumps/Base/GumpSystem.cs` - -## See Also -- `dev-docs/events.md` - Complete events documentation -- `dev-docs/claude-skills/modernuo-content-patterns.md` - Content hooks -- `dev-docs/claude-skills/modernuo-configuration.md` - Configure() pattern diff --git a/dev-docs/claude-skills/modernuo-gump-system.md b/dev-docs/claude-skills/modernuo-gump-system.md deleted file mode 100644 index ad00db114..000000000 --- a/dev-docs/claude-skills/modernuo-gump-system.md +++ /dev/null @@ -1,359 +0,0 @@ ---- -name: modernuo-gump-system -description: > - Trigger when creating or modifying gumps (UI dialogs). When working with BaseGump, StaticGump, DynamicGump, or GumpSystem. ---- - -# ModernUO Gump System - -## When This Activates -- Creating new gumps (UI dialogs/windows) -- Modifying existing gump layouts -- Handling gump button responses -- Using `mobile.SendGump()`, `HasGump()`, `CloseGump()` - -## Key Rules - -1. **Use `StaticGump`** when layout is fixed (cached, better performance) -2. **Use `DynamicGump`** when layout depends on instance data (rebuilt each time) -3. **Use `mobile.SendGump(gump)`** to send -- requires `using Server.Gumps;` -4. **Override `Singleton => true`** if only one instance should be open per player -5. **Never forget `using Server.Gumps;`** -- extension methods won't resolve without it - -## Hierarchy - -``` -BaseGump (abstract) -├── StaticGump -- Cached layout, use for menus/dialogs -└── DynamicGump -- Rebuilt layout, use for dynamic content -``` - -## StaticGump Pattern (Preferred for Fixed Layouts) - -```csharp -using Server.Gumps; - -namespace Server.Gumps; - -public class MyGump : StaticGump -{ - private readonly Mobile _player; - private readonly string _message; - - public override bool Singleton => true; // One per player - - public MyGump(Mobile player, string message) : base(50, 50) - { - _player = player; - _message = message; - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 400, 300, 5054); - - // Static text (cached) - builder.AddHtmlLocalized(10, 10, 380, 20, 1060635, 0x7800); // "Warning" - - // Dynamic text placeholder (filled per-instance) - builder.AddHtmlPlaceholder(10, 40, 380, 220, "content", false, true); - - // Buttons - builder.AddButton(150, 265, 4005, 4007, 1); // OK button (buttonID=1) - builder.AddHtmlLocalized(185, 267, 100, 20, 1011036); // "OK" - - builder.AddButton(250, 265, 4017, 4019, 0); // Cancel (buttonID=0 = close) - builder.AddHtmlLocalized(285, 267, 100, 20, 1011012); // "Cancel" - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - // Fill dynamic placeholders - builder.SetHtmlText("content", _message, "#FFC000"); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID == 1) - { - _player.SendMessage("You clicked OK!"); - } - } -} -``` - -## DynamicGump Pattern (For Variable Layouts) - -```csharp -using Server.Gumps; - -namespace Server.Gumps; - -public class InventoryGump : DynamicGump -{ - private readonly Mobile _player; - private readonly List _items; - - public override bool Singleton => true; - - public InventoryGump(Mobile player, List items) : base(50, 50) - { - _player = player; - _items = items; - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 400, 40 + _items.Count * 30, 5054); - builder.AddHtml(10, 10, 380, 20, "Inventory"); - - for (var i = 0; i < _items.Count; i++) - { - var y = 40 + i * 30; - builder.AddLabel(10, y, 0, _items[i].Name ?? "Unknown"); - builder.AddButton(350, y, 4005, 4007, i + 1); // Button per item - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID > 0 && info.ButtonID <= _items.Count) - { - var item = _items[info.ButtonID - 1]; - _player.SendMessage($"Selected: {item.Name}"); - } - } -} -``` - -## Sending and Managing Gumps - -```csharp -using Server.Gumps; - -// Send gump -mobile.SendGump(new MyGump(mobile, "Hello!")); - -// Check if gump is open -if (mobile.HasGump()) -{ - // Already open -} - -// Find existing gump -var gump = mobile.FindGump(); - -// Close gump -mobile.CloseGump(); -``` - -## Builder Methods Reference - -### Layout Elements -```csharp -builder.AddPage(int page = 0); -builder.AddBackground(int x, int y, int width, int height, int gumpID); -builder.AddAlphaRegion(int x, int y, int width, int height); -builder.AddImageTiled(int x, int y, int width, int height, int gumpID); -builder.AddImage(int x, int y, int gumpID, int hue = 0); -builder.AddItem(int x, int y, int itemID, int hue = 0); -``` - -### Text -```csharp -builder.AddLabel(int x, int y, int hue, ReadOnlySpan text); -builder.AddHtml(int x, int y, int w, int h, ReadOnlySpan text, ...); -builder.AddHtmlLocalized(int x, int y, int w, int h, int number, ...); -builder.AddLabelPlaceholder(int x, int y, int hue, ReadOnlySpan slotKey); -builder.AddHtmlPlaceholder(int x, int y, int w, int h, ReadOnlySpan slotKey, ...); -``` - -Text-accepting builders take `ROS` and have `ref RawInterpolatedStringHandler` overloads — `$"..."` literals at the call site are zero-allocation. Same for `Html.Center`/`Html.Color`/`Html.Right` helpers used for HTML markup wrapping. - -```csharp -builder.AddHtml(20, 20, 200, 100, $"
{Title}: {Score:N0}
"); -builder.AddLabel(20, 40, hue, $"You have {gold} gold"); -``` - -Avoid the interpolation anti-patterns (ternaries with `$"..."` branches, `.ToString()` inside holes, pre-built `var msg = $"..."` locals, `string.Format`, etc.) — they silently fall back to string-allocating overloads. See `dev-docs/claude-skills/modernuo-string-handling.md` § "Interpolation Anti-Patterns" for the full list. - -### Interactive -```csharp -builder.AddButton(int x, int y, int normalID, int pressedID, int buttonID, ...); -builder.AddCheckbox(int x, int y, int inactiveID, int activeID, bool selected, int switchID); -builder.AddRadio(int x, int y, int inactiveID, int activeID, bool selected, int switchID); -builder.AddTextEntry(int x, int y, int w, int h, int hue, int entryID, ...); -``` - -### Modifiers -```csharp -builder.SetNoClose(); // Cannot close with right-click -builder.SetNoMove(); // Cannot move the gump -builder.SetNoResize(); // Cannot resize -builder.SetNoDispose(); // Cannot dispose -builder.AddTooltip(int number); // Hover tooltip -``` - -## Response Handling - -```csharp -public override void OnResponse(NetState sender, in RelayInfo info) -{ - var buttonID = info.ButtonID; // 0 = close, 1+ = button clicks - var isChecked = info.IsSwitched(0); // Checkbox/radio state by switchID - var text = info.GetTextEntry(0); // Text entry value by entryID -} -``` - -## Static vs Dynamic: When to Use Which - -| Use StaticGump | Use DynamicGump | -|---|---| -| Confirmation dialogs | Lists of variable length | -| Static menus | Player-specific content | -| Warning prompts | Crafting interfaces | -| Settings panels | Search results | -| Help pages | Dynamic data display | - -## Important Properties - -### Singleton -```csharp -public override bool Singleton => true; -``` -Automatically closes any existing instance of this gump type for the player before sending a new one. **Always set this for gumps that shouldn't stack.** Without it, repeated sends create duplicates the player must close individually. - -### Cached (StaticGump only) -```csharp -protected virtual bool Cached => true; // default -``` -Controls whether `StaticGump` caches its compiled layout bytes. Override to `false` **during development only** to force layout rebuild each send — useful for iterating on layout without restarting the server: -```csharp -protected override bool Cached => false; // TEMPORARY — remove before commit -``` - -## Empty Gump Rule (CRITICAL AUDIT RULE) - -**NEVER send a gump with no visual components.** An empty gump cannot be closed by the client — no close button, no right-click dismiss. This causes a **gump leak** on both client and server. - -Empty gumps happen when short-circuiting inside a constructor or `BuildLayout`: -```csharp -// BAD — early return in constructor leaves gump empty but it still gets sent -public MyGump(Mobile from) : base(50, 50) -{ - if (!from.Alive) return; // Empty gump — LEAK! - // ...layout... -} -``` - -### Fix: Static DisplayTo Pattern -Validate prerequisites **before** constructing the gump. Make the constructor `private`: -```csharp -public class MyGump : DynamicGump -{ - public override bool Singleton => true; - - private MyGump(Mobile from, SomeData data) : base(50, 50) { /* store fields */ } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 400, 300, 5054); - // Always has visual content — DisplayTo guarantees valid state - } - - public static void DisplayTo(Mobile from) - { - if (!from.Alive || from.NetState == null) return; // No gump created - var data = GetData(from); - if (data == null) return; // No gump created - from.SendGump(new MyGump(from, data)); - } -} -``` -Reference: `Projects/UOContent/Gumps/Go/GoGump.cs` - -## Converting Legacy Gump to DynamicGump / StaticGump - -### Choose target type -- Layout is fixed structure → `StaticGump` (cached, best performance) -- Layout varies per instance (loops, conditionals) → `DynamicGump` -- When in doubt → `DynamicGump` (simpler, still much better than legacy) - -### Conversion checklist -1. Change base class: `Gump` → `DynamicGump` or `StaticGump` -2. Move layout code from constructor into `BuildLayout(ref DynamicGumpBuilder builder)` or `BuildLayout(ref StaticGumpBuilder builder)` -3. Store any state the constructor used as fields (constructor now just stores state, doesn't build layout) -4. Replace property flags with builder methods: - - `Closable = false` → `builder.SetNoClose()` - - `Draggable = false` → `builder.SetNoMove()` - - `Resizable = false` → `builder.SetNoResize()` - - `Disposable = false` → `builder.SetNoDispose()` -5. `AddPage(0)` → `builder.AddPage()` (0 is default) -6. For `StaticGump`: extract dynamic text into placeholders (`AddLabelPlaceholder` / `AddHtmlPlaceholder`) and fill in `BuildStrings(ref GumpStringsBuilder builder)` -7. Update `OnResponse` signature: `RelayInfo info` → `in RelayInfo info` -8. Add `public override bool Singleton => true;` if appropriate -9. Make constructor `private`, add `public static void DisplayTo(Mobile from)` method -10. Move all validation/short-circuit logic into `DisplayTo` (never leave `BuildLayout` empty) - -### Example: legacy → modern -```csharp -// BEFORE (legacy Gump) -public class OldGump : Gump -{ - public OldGump(Mobile from) : base(50, 50) - { - Closable = false; - AddPage(0); - AddBackground(0, 0, 400, 300, 5054); - AddLabel(20, 20, 0x480, from.Name); - AddButton(20, 260, 4005, 4007, 1); - } - public override void OnResponse(NetState sender, RelayInfo info) { } -} - -// AFTER (DynamicGump) -public class NewGump : DynamicGump -{ - private readonly Mobile _from; - public override bool Singleton => true; - - private NewGump(Mobile from) : base(50, 50) => _from = from; - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.SetNoClose(); - builder.AddPage(); - builder.AddBackground(0, 0, 400, 300, 5054); - builder.AddLabel(20, 20, 0x480, _from.Name); - builder.AddButton(20, 260, 4005, 4007, 1); - } - - public override void OnResponse(NetState sender, in RelayInfo info) { } - - public static void DisplayTo(Mobile from) => from.SendGump(new NewGump(from)); -} -``` - -## Anti-Patterns - -- **Missing `using Server.Gumps;`**: `SendGump()`, `HasGump()` won't resolve -- **Creating DynamicGump for static layouts**: Wastes CPU rebuilding every time -- **Not setting `Singleton`**: Multiple copies of same gump stack up -- **ButtonID 0 for actions**: 0 means "close" -- use 1+ for action buttons -- **Empty gumps**: Short-circuiting in constructor/BuildLayout causes gump leaks — use `DisplayTo` pattern -- **Leaving `Cached => false` in production**: Defeats the purpose of StaticGump — only disable during development - -## Real Examples -- StaticGump warning: `Projects/UOContent/Gumps/StaticWarningGump.cs` -- DynamicGump craft: `Projects/UOContent/Engines/Craft/Core/CraftGump.cs` -- GumpSystem extensions: `Projects/UOContent/Gumps/Base/GumpSystem.cs` -- StaticGumpBuilder: `Projects/UOContent/Gumps/Base/StaticGumpBuilder.cs` -- DynamicGumpBuilder: `Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs` -- BaseGump: `Projects/UOContent/Gumps/Base/BaseGump.cs` - -## See Also -- `dev-docs/gump-system.md` - Complete gump documentation -- `dev-docs/claude-skills/modernuo-commands-targeting.md` - Commands that open gumps diff --git a/dev-docs/claude-skills/modernuo-networking.md b/dev-docs/claude-skills/modernuo-networking.md deleted file mode 100644 index 9b049bc89..000000000 --- a/dev-docs/claude-skills/modernuo-networking.md +++ /dev/null @@ -1,316 +0,0 @@ ---- -name: modernuo-networking -description: > - Trigger when creating or modifying packets, working with NetState, SpanWriter, SpanReader, or implementing network protocol handlers. ---- - -# ModernUO Networking & Packets - -## When This Activates -- Creating new packets (outgoing or incoming) -- Modifying existing packet handlers -- Working with `NetState`, `SpanWriter`, `SpanReader` -- Implementing network protocol features - -## Key Rules - -1. **Outgoing packets**: Static `Create*` method fills `Span`, extension `Send*` method on `NetState` -2. **Incoming packets**: Register with function pointers in `Configure()` -3. **Use `stackalloc`** for small fixed-size outgoing packets -4. **Always check `ns.CannotSendPackets()`** before sending -5. **Big-endian by default** -- use `WriteLE()` for little-endian - -## Outgoing Packet Pattern - -### Step 1: Create Extension Method on NetState -```csharp -public static class OutgoingMyPackets -{ - public const int MyPacketLength = 12; - - public static void SendMyPacket(this NetState ns, Serial target, int value) - { - if (ns.CannotSendPackets()) - return; - - var buffer = stackalloc byte[MyPacketLength].InitializePacket(); - CreateMyPacket(buffer, target, value); - ns.Send(buffer); - } - - public static void CreateMyPacket(Span buffer, Serial target, int value) - { - if (buffer[0] != 0) // Already initialized check - return; - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)12); // Length - writer.Write((ushort)0x99); // Sub-command - writer.Write(target); // Serial (4 bytes) - writer.Write((short)value); // Value (2 bytes) - } -} -``` - -### Step 2: Call from Game Code -```csharp -mobile.NetState.SendMyPacket(target.Serial, 42); - -// Or for all nearby players: -foreach (var ns in mobile.GetClientsInRange(18)) -{ - ns.SendMyPacket(target.Serial, 42); -} -``` - -### Variable-Length Outgoing Packets -```csharp -public static void SendMyDynamicPacket(this NetState ns, string name) -{ - if (ns.CannotSendPackets()) - return; - - var writer = new SpanWriter(stackalloc byte[64]); // Or use pooled buffer for large packets - writer.Write((byte)0x99); // Packet ID - writer.Write((ushort)0); // Placeholder for length - writer.WriteBigUniNull(name); // Unicode string with null terminator - writer.WritePacketLength(); // Fill in actual length - - ns.Send(writer.Span); -} -``` - -## Incoming Packet Pattern - -### Step 1: Register Handler in Configure() -```csharp -public static class IncomingMyPackets -{ - public static unsafe void Configure() - { - IncomingPackets.Register(0x99, 12, true, &MyPacketHandler); - // ^ID ^len ^inGameOnly ^handler - // len=0 for variable-length packets - } - - public static void MyPacketHandler(NetState state, SpanReader reader) - { - var from = state.Mobile; - if (from == null) - return; - - var targetSerial = (Serial)reader.ReadUInt32(); - var value = reader.ReadInt16(); - - var target = World.FindMobile(targetSerial); - if (target != null) - { - // Process packet - } - } -} -``` - -### Encoded Packet Registration -```csharp -public static unsafe void Configure() -{ - IncomingPackets.RegisterEncoded(0x28, true, &GuildGumpRequest); - // ^subID ^inGame ^handler -} - -public static void GuildGumpRequest(NetState state, IEntity target, EncodedReader reader) -{ - // Handle encoded packet -} -``` - -## SpanWriter Reference - -```csharp -// Constructors -var writer = new SpanWriter(Span buffer); -var writer = new SpanWriter(stackalloc byte[64]); -var writer = new SpanWriter(int capacity, bool resize = false); - -// Integer writes (big-endian by default) -writer.Write(bool value); -writer.Write(byte value); -writer.Write(sbyte value); -writer.Write(short value); // Big-endian -writer.Write(ushort value); // Big-endian -writer.Write(int value); // Big-endian -writer.Write(uint value); // Big-endian -writer.Write(long value); -writer.Write(Serial serial); // 4 bytes, big-endian - -// Little-endian variants -writer.WriteLE(short value); -writer.WriteLE(ushort value); -writer.WriteLE(int value); -writer.WriteLE(uint value); - -// String writes -writer.WriteAscii(string value); -writer.WriteAsciiNull(string value); // Null-terminated -writer.WriteAscii(string value, int fixedLength); -writer.WriteLatin1(string value); -writer.WriteLatin1Null(string value); -writer.WriteBigUni(string value); // UTF-16 big-endian -writer.WriteBigUniNull(string value); -writer.WriteLittleUni(string value); // UTF-16 little-endian -writer.WriteLittleUniNull(string value); -writer.WriteUTF8(string value); -writer.WriteUTF8Null(string value); - -// Utilities -writer.Write(ReadOnlySpan data); -writer.Clear(int count); // Write zeros -writer.Seek(int offset, SeekOrigin origin); -writer.WritePacketLength(); // Fill in length at position 1-2 -writer.EnsureCapacity(int capacity); -writer.Dispose(); // Return pooled buffer if any - -// Properties -writer.Position; // Current write position -writer.Capacity; // Buffer size -writer.Span; // ReadOnlySpan of written data -``` - -## SpanReader Reference - -```csharp -// Constructor -var reader = new SpanReader(ReadOnlySpan data); - -// Integer reads (big-endian by default) -reader.ReadByte(); -reader.ReadBoolean(); // byte > 0 -reader.ReadSByte(); -reader.ReadInt16(); // Big-endian -reader.ReadUInt16(); // Big-endian -reader.ReadInt32(); // Big-endian -reader.ReadUInt32(); // Big-endian -reader.ReadInt64(); -reader.ReadUInt64(); - -// Little-endian variants -reader.ReadInt16LE(); -reader.ReadUInt16LE(); -reader.ReadUInt32LE(); - -// String reads -reader.ReadAscii(int fixedLength = -1); -reader.ReadAsciiSafe(int fixedLength = -1); // Filters control chars -reader.ReadLatin1(int fixedLength = -1); -reader.ReadLatin1Safe(int fixedLength = -1); -reader.ReadBigUni(int fixedLength = -1); -reader.ReadBigUniSafe(int fixedLength = -1); -reader.ReadLittleUni(int fixedLength = -1); -reader.ReadUTF8(int fixedLength = -1); - -// Utilities -reader.Seek(int offset, SeekOrigin origin); -reader.Read(Span destination); - -// Properties -reader.Position; // Current read position -reader.Length; // Total data length -reader.Remaining; // Bytes remaining -``` - -## Player-Facing Message APIs - -For chat / system messages / overhead text, use the convenience methods on `Mobile` and `Item` rather than building packets manually. They handle stackalloc sizing, spatial queries, and visibility filtering, and each has a `ref RawInterpolatedStringHandler` overload for zero-allocation interpolation. - -### Mobile - -```csharp -// Self-message -mob.SendMessage(int hue, ROS text); -mob.SendLocalizedMessage(int number, ROS args = default, int hue = 0x3B2); -mob.SendAsciiMessage(int hue, ROS text); - -// Speech (broadcast in range) -mob.Say(ROS text); // SpeechHue -mob.Emote(ROS text); // EmoteHue -mob.Whisper(ROS text); // WhisperHue, short range -mob.Yell(ROS text); // YellHue, long range - -// Overhead (3 visibility variants × text/localized × affix) -mob.PublicOverheadMessage(MessageType, int hue, bool ascii, ROS text, ...); -mob.PrivateOverheadMessage(MessageType, int hue, int number, ROS args, NetState); -mob.LocalOverheadMessage(MessageType, int hue, bool ascii, ROS text); -mob.NonlocalOverheadMessage(MessageType, int hue, int number, ROS args = default); -``` - -### Item - -```csharp -item.PublicOverheadMessage(MessageType, int hue, bool ascii, ROS text); -item.PublicOverheadMessage(MessageType, int hue, int number, ROS args = default); -item.SendLocalizedMessageTo(Mobile to, int number, ROS args = default); -item.SendLocalizedMessageTo(Mobile to, int number, int hue, ROS args = default); -item.SendMessageTo(Mobile to, ROS text, int hue = 0x3B2); -``` - -### Zero-allocation interpolation - -```csharp -// Compiler picks the handler overload — no string allocated -mob.SendMessage($"You have {gold:N0} gold"); -mob.Say($"Hello, {target.Name}!"); -item.SendLocalizedMessageTo(player, cliloc, $"{a}\t{b}"); -``` - -Several call-site shapes (ternaries with interpolated branches, `.ToString()` inside holes, pre-built `var msg = $"..."` locals, etc.) silently defeat handler binding and allocate a `string`. See `dev-docs/string-handling.md` § "Interpolation Anti-Patterns" for the list and fixes. - -For lowercase output, use `:L`: -```csharp -mob.SendMessage($"You earned a {rank:L} trophy!"); // "gold" not "Gold" -``` - -### Implementation note - -The 7 spatial-broadcast variants (`Public/NonlocalOverheadMessage` × text/localized/affix) route through generic `OutgoingMessagePackets.Broadcast*` helpers parameterized over a `private readonly struct` filter that encapsulates the visibility predicate. The `where TFilter : struct, IBroadcastFilter` constraint specializes per filter type and keeps dispatch zero-alloc. If you add a new spatial-message API, add a filter struct and call the existing helper rather than duplicating the loop. - ---- - -## Common Packet Patterns - -### Sound Effect -```csharp -ns.SendSoundEffect(0x1E5, target); -``` - -### Mobile Animation -```csharp -ns.SendMobileAnimation(mobile.Serial, action, frameCount, repeatCount, forward, repeat, delay); -``` - -### Damage -```csharp -ns.SendDamage(target.Serial, amount); -``` - -## Anti-Patterns - -- **Not checking `CannotSendPackets()`**: Player may have disconnected -- **Using `new byte[]` for packets**: Use `stackalloc` for fixed-size, `SpanWriter` for variable -- **Wrong endianness**: UO protocol is big-endian; only use `WriteLE` when spec requires it -- **Not calling `InitializePacket()`**: Required for `Create*` pattern with static buffer reuse - -## Real Examples -- Mobile packets: `Projects/Server/Network/Packets/OutgoingMobilePackets.cs` -- Item packets: `Projects/Server/Network/Packets/OutgoingItemPackets.cs` -- Damage packets: `Projects/Server/Network/Packets/OutgoingDamagePackets.cs` -- Effect packets: `Projects/Server/Network/Packets/OutgoingEffectPackets.cs` -- Incoming registration: `Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs` -- Movement handler: `Projects/UOContent/Network/Packets/IncomingMovementPackets.cs` -- SpanWriter: `Projects/Server/Buffers/SpanWriter.cs` -- SpanReader: `Projects/Server/Buffers/SpanReader.cs` - -## See Also -- `dev-docs/networking-packets.md` - Complete networking documentation -- `dev-docs/claude-skills/modernuo-threading.md` - Network I/O threading context diff --git a/dev-docs/claude-skills/modernuo-property-lists.md b/dev-docs/claude-skills/modernuo-property-lists.md deleted file mode 100644 index 51e47ff35..000000000 --- a/dev-docs/claude-skills/modernuo-property-lists.md +++ /dev/null @@ -1,256 +0,0 @@ ---- -name: modernuo-property-lists -description: > - Trigger when implementing GetProperties(), working with IPropertyList/ObjectPropertyList, or customizing item tooltips. ---- - -# ModernUO Property Lists (Tooltips) - -## When This Activates -- Implementing `GetProperties()` override -- Working with `IPropertyList` or `ObjectPropertyList` -- Customizing item or mobile tooltips -- Using `[InvalidateProperties]` attribute -- Adding cliloc-based text to items - -## Key Rules - -1. **Always call `base.GetProperties(list)` first** in overrides -2. **Use cliloc numbers** when possible (int IDs that map to localized strings) -3. **String interpolation** works with `IPropertyList` -- use `$"..."` syntax -4. **`[InvalidateProperties]`** on `[SerializableField]` auto-refreshes tooltip on change -5. **Call `InvalidateProperties()`** manually when non-serialized state changes tooltip -6. **Never invalidate from inside `GetProperties`** -- every property a `GetProperties` override - reads must be a pure read. `InvalidateProperties()` rebuilds in place (`Reset()` + rebuild), so a - getter with that side effect tears down the list mid-build: it returns the pooled interpolation - buffer under an in-flight `$"..."` handler (`ArgumentNullException`, parameter `"array"`) and - rewinds the packet cursor. The engine refuses and logs an error; `DEBUG` throws. Defer instead: - `Timer.DelayCall(InvalidateProperties)` - -## IPropertyList Interface - -```csharp -public interface IPropertyList -{ - void Add(int number); // Cliloc number only - void Add(int number, string argument); // Cliloc with ~1_val~ arg - void Add(ReadOnlySpan argument); // Raw text, no string alloc (uses passthrough cliloc) - void Add(int number, ReadOnlySpan argument); - void AddChunked(ReadOnlySpan text); // Newline-joined text, split across properties - OplTextBlock TextBlock(); // Builder that flushes via AddChunked on dispose - void Add(int number, int value); // Cliloc with int arg - void AddLocalized(int value); // Cliloc number as value - void AddLocalized(int number, int value); // Cliloc wrapper for cliloc - - // String interpolation overloads - void Add(ref InterpolatedStringHandler handler); - void Add(int number, ref InterpolatedStringHandler handler); -} -``` - -> No `Add(string)` overload — pass a span (`text.AsSpan()`) or, preferably, an interpolated `$"..."` -> literal so the handler formats straight into the pooled buffer. - -## Patterns - -### Basic GetProperties Override -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); // ALWAYS call base first - - list.Add(1060741, $"{_charges}"); // "charges: ~1_val~" - list.Add($"{"Quality: "}{_quality}"); // Raw string - list.Add(1060637, $"{_uses}\t{_maxUses}"); // "~1_val~ / ~2_val~" -} -``` - -### Cliloc Arguments Format -Cliloc strings use `~1_val~`, `~2_val~`, etc. as placeholders. Arguments are tab-separated: - -```csharp -// Cliloc 1060637 = "~1_val~ / ~2_val~" -list.Add(1060637, $"{current}\t{max}"); - -// Cliloc 1072241 = "Contents: ~1_ITEMS~/~2_MAXITEMS~ items, ~3_WEIGHT~/~4_MAXWEIGHT~ stones" -list.Add(1072241, $"{TotalItems}\t{MaxItems}\t{TotalWeight}\t{MaxWeight}"); - -// Cliloc 1042971 = "~1_val~" (generic single argument) -list.Add(1042971, $"{"Custom text here"}"); -``` - -### String Literals Must Be Holes (CRITICAL) - -The interpolated string handler distinguishes **literals** (bare text between `{}` holes) from **holes** (values inside `{}`). Literals are delimiters. Holes are arguments. This matters because the property list system is also used for web rendering, which must tell arguments apart from delimiters. - -**String constants must always be wrapped as holes: `{"..."}`** - -```csharp -// BAD — "Chances" becomes a literal/delimiter, not an argument -list.Add(1060658, $"Chances\t{_charges}"); - -// GOOD — "Chances" is a hole → argument ~1_val~ -list.Add(1060658, $"{"Chances"}\t{_charges}"); -``` - -Real examples (`Teleporter.cs`): -```csharp -list.Add(1060658, $"{"Map"}\t{_mapDest}"); // "~1_val~: ~2_val~" -list.Add(1060659, $"{"Coords"}\t{_pointDest}"); -list.Add(1060661, $"{"Range"}\t{_range}"); -``` - -**Rule**: Only `\t` (argument separator) should be bare literal text. Everything else — including string constants — must be inside `{}` holes. - -### No `.ToString()` Inside Holes - -`IPropertyList`'s handler formats values directly via `ISpanFormattable.TryFormat` — no intermediate `string` allocation per hole. An explicit `.ToString()` defeats this: - -```csharp -// BAD — .ToString() allocates a string the handler then re-buffers -list.Add(1060658, $"{"Charges"}\t{_charges.ToString()}"); - -// GOOD — handler formats _charges directly with no intermediate string -list.Add(1060658, $"{"Charges"}\t{_charges}"); -``` - -Same applies to `.String()` (TextDefinition), `.GetValue()`, etc. The full list of interpolation anti-patterns (ternaries, switch expressions, pre-built locals, `string.Format`, concat in hole, LINQ in hole) applies equally to `IPropertyList.Add($"...")`. See `dev-docs/string-handling.md` § "Interpolation Anti-Patterns" or `dev-docs/claude-skills/modernuo-string-handling.md`. - -### Cliloc as Argument (Use `:#` Format Specifier) - -When an argument is itself a cliloc number, use the `:#` format specifier — **not** a `"#number"` string: - -```csharp -// BAD — "#1060000" is a string, web renderers will display it literally -list.Add(1050039, $"{m_Amount}\t{"#1060000"}"); - -// GOOD — :# tells the handler this is a cliloc number to resolve -list.Add(1050039, $"{m_Amount}\t{1060000:#}"); -``` - -The `:#` format lets the handler (and other consumers like web renderers) know the value is a cliloc reference to resolve, not a raw number. Also available via `list.AddLocalized(number, clilocValue)`. - -### Looking Up Cliloc Text - -If you don't know what arguments a cliloc number expects, you can read the `cliloc.enu` binary file. Loading logic is in `Projects/Server/Localization/Localization.cs` → `LoadClilocs(string lang, string file)`. Ask the user where their `cliloc.enu` file is (typically in the UO client data directory). - -### Auto-Refresh with [InvalidateProperties] -```csharp -[SerializableField(0)] -[InvalidateProperties] // Auto-calls InvalidateProperties() when Charges changes -[SerializedCommandProperty(AccessLevel.GameMaster)] -private int _charges; -``` - -### Manual Refresh -```csharp -public void UseCharge() -{ - _charges--; - InvalidateProperties(); // Manually trigger tooltip refresh - this.MarkDirty(); -} -``` - -### Conditional Properties -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); - - if (_charges > 0) - list.Add(1060741, $"{_charges}"); - - if (_owner != null) - list.Add($"{"Owned by: "}{_owner.Name}"); - - if (Core.AOS) // Era-conditional properties - list.Add(1061170, $"{_imbueLevel}"); // "animal " ~1_val~ -} -``` - -### Mobile Properties -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); - - if (Core.AOS && Faction != null) - { - list.Add(1060776, $"{Rank.Title}\t{Faction.Definition.PropName}"); - } - - if (DisplayChampionTitle) - { - var titleLabel = ChampionTitleSystem.GetChampionTitleLabel(this); - if (titleLabel > 0) - list.Add(titleLabel); - } -} -``` - -### Multi-Line Free Text (`AddChunked` / `OplTextBlock`) - -For variable-length, free-form (non-cliloc) tooltip text — e.g. a consolidated attribute dump or -staged ID text — **never emit it as one property**. The legacy 2D client copies each OPL property into -a fixed ~512-char buffer; a longer property smashes the client heap and crashes it. -`ObjectPropertyList.MaxArgumentLength` (504) is the safe per-property cap. - -Two safe APIs split `\n`-joined text across multiple passthrough-cliloc properties (each ≤ cap): - -```csharp -// Already have a '\n'-joined string? Use the IPropertyList primitive directly: -list.AddChunked(_description); - -// Building lines conditionally? Use the OplTextBlock builder (flushes via AddChunked on dispose): -using var block = list.TextBlock(); // IPropertyList.TextBlock() -block.Add($"Lower Parry Cap {cap}%"); // zero-alloc interpolated overload -block.Add("Cannot be repaired".AsSpan()); // plain span, no string alloc -``` - -- Lines join with `\n`; empty lines are skipped; no lines → nothing emitted. -- `block.Add($"...")` is a real `[InterpolatedStringHandler]` — same hole anti-patterns apply (no `.ToString()`, ternaries, concat). -- `OplTextBlock` is a `ref struct` for the single-threaded build pass — always `using`, never store/await across it. - -## Common Cliloc Numbers - -| Number | Text | Usage | -|---|---|---| -| 1042971 | `~1_val~` | Generic single argument | -| 1060741 | `charges: ~1_val~` | Charge count | -| 1060637 | `~1_val~ / ~2_val~` | Current/max values | -| 1060658 | `~1_val~: ~2_val~` | Key: value pair | -| 1050044 | `~1_ITEMS~ items, ~2_WEIGHT~ stones` | Container contents | -| 1072241 | `Contents: ~1~/~2~ items, ~3~/~4~ stones` | ML container | -| 1060776 | `~1_val~, ~2_val~` | Two comma-separated values | -| 1061170 | `animal lore ~1_val~` | Taming info | -| 1053099 | `damage ~1_val~ - ~2_val~` | Damage range | - -## ObjectPropertyList Internals - -- Packet ID: 0xD6 -- Hash-based change detection -- only sends if content actually changed -- `InvalidateProperties()` rebuilds the list and compares hash -- Uses `STArrayPool` for string building (zero GC) -- Global toggle: `ObjectPropertyList.Enabled` - -## Anti-Patterns - -- **Forgetting `base.GetProperties(list)`**: Loses default name/weight display -- **Not using cliloc**: Raw strings don't get localized -- **Excessive rebuilds**: Don't call `InvalidateProperties()` in tight loops -- **Assuming tooltip support**: Check `ObjectPropertyList.Enabled` if needed -- **One giant `Add()` for multi-line text**: A property over ~512 chars crashes the legacy 2D client. Use `AddChunked`/`OplTextBlock` for variable-length free text -- **Side-effecting property getters**: A getter reached from `GetProperties` that calls `InvalidateProperties()` (directly or via a helper like `Invalidate()`) re-enters the build and is refused — error logged, `DEBUG` throws. Lazy recomputation in a getter is fine; the *notification* is not. Invalidate where the value changes, or `Timer.DelayCall(InvalidateProperties)` - -## Real Examples -- Item properties: `Projects/Server/Items/Item.cs` (AddNameProperties, GetProperties) -- Mobile properties: `Projects/UOContent/Mobiles/PlayerMobile.cs` (GetProperties) -- Container properties: `Projects/Server/Items/Container.cs` (era-conditional display) -- Interface: `Projects/Server/PropertyList/IPropertyList.cs` -- Implementation: `Projects/Server/PropertyList/ObjectPropertyList.cs` - -## See Also -- `dev-docs/property-lists.md` - Complete property list documentation -- `dev-docs/claude-skills/modernuo-serialization.md` - [InvalidateProperties] on fields -- `dev-docs/claude-skills/modernuo-era-expansion.md` - Era-conditional properties diff --git a/dev-docs/claude-skills/modernuo-regions.md b/dev-docs/claude-skills/modernuo-regions.md deleted file mode 100644 index bd0c2d8f5..000000000 --- a/dev-docs/claude-skills/modernuo-regions.md +++ /dev/null @@ -1,235 +0,0 @@ ---- -name: modernuo-regions -description: > - Trigger when creating custom regions, dynamic item-controlled areas, dungeon sub-zones, travel restrictions, housing blocks, spawn control, or any spatial gameplay rule. When working with Region, BaseRegion, GuardedRegion, DungeonRegion, HouseRegion, or CheckTravel. ---- - -# ModernUO Regions - -## When This Activates -- Creating a custom region (dungeon zone, boss arena, restricted area) -- Creating a dynamic region tied to an item (chest effect zone, spawn area) -- Adding travel spell restrictions, housing blocks, or spawn control -- Working with `Region`, `BaseRegion`, `GuardedRegion`, `DungeonRegion`, `HouseRegion` -- Overriding `CheckTravel`, `AllowHousing`, `OnEnter`, `OnBeginSpellCast`, etc. -- Modifying region JSON or `RegionJsonRegistration` - -## Key Rules - -1. **Inherit from the right base** — `BaseRegion` for general, `DungeonRegion` for dungeons, `GuardedRegion` for towns, `NoTravelSpellsAllowedRegion` for no-travel dungeons -2. **Dynamic regions must Register/Unregister** — call `Register()` after creation, `Unregister()` before recreation or deletion -3. **Use `Region.Find(location, map)` as parent** for dynamic child regions — this inherits the existing region's behavior -4. **Defer registration in `[AfterDeserialization]`** — use `Timer.StartTimer(TimeSpan.Zero, UpdateRegion)` since map/parents may not be loaded yet -5. **Always override `AllowHousing` returning false** if your region should block housing -6. **Staff bypass** — travel/spell checks should allow `AccessLevel > Player` -7. **Virtual hooks delegate to Parent** — you only need to override what you change - -## Choosing a Base Class - -| You Need | Inherit From | -|---|---| -| Custom overworld area | `BaseRegion` | -| Dungeon area (dark, no housing) | `DungeonRegion` | -| Dungeon + no travel spells | `NoTravelSpellsAllowedRegion` | -| Town with guards | `GuardedRegion` or `TownRegion` | -| No housing only | `NoHousingRegion` | -| Item-controlled dynamic area | `BaseRegion` (parent = `Region.Find(...)`) | - -## Quick Patterns - -### Static Region (JSON-Defined) - -Add to `Distribution/Data/regions.json`: -```json -{ - "$type": "NoTravelSpellsAllowedRegion", - "Name": "My Dungeon", - "Map": "Felucca", - "Parent": { "Name": "Felucca", "Map": "Felucca" }, - "Area": [{ "x1": 100, "y1": 200, "x2": 300, "y2": 400 }], - "GoLocation": { "x": 150, "y": 250, "z": 0 }, - "Music": "Dungeon9" -} -``` - -Register the type (if new) in `RegionJsonRegistration.Configure()`: -```csharp -RegionJsonSerializer.Register(); -``` - -### Custom Region Class - -```csharp -public class MyCustomRegion : BaseRegion -{ - public MyCustomRegion(string name, Map map, Region parent, params Rectangle3D[] area) - : base(name, map, parent, area) - { - } - - public override bool AllowHousing(Mobile from, Point3D p) => false; - - public override bool CheckTravel( - Mobile m, Point3D newLocation, TravelCheckType travelType, out TextDefinition message) - { - message = null; - return m.AccessLevel > AccessLevel.Player; - } -} -``` - -### Dynamic Item-Tracked Region - -Full pattern for an item that creates a region around itself: - -```csharp -public partial class MySpecialItem : Item -{ - private MyItemRegion _region; - - public void UpdateRegion() - { - _region?.Unregister(); - - if (!Deleted && Map != Map.Internal) - { - _region = new MyItemRegion(this); - _region.Register(); - } - } - - public override void OnLocationChange(Point3D oldLoc) - { - base.OnLocationChange(oldLoc); - UpdateRegion(); - } - - public override void OnMapChange() - { - base.OnMapChange(); - UpdateRegion(); - } - - public override void OnAfterDelete() - { - base.OnAfterDelete(); - UpdateRegion(); // Unregisters because Deleted == true - } - - [AfterDeserialization(false)] - private void AfterDeserialization() - { - Timer.StartTimer(TimeSpan.Zero, UpdateRegion); // Defer to next tick - } -} - -public class MyItemRegion : BaseRegion -{ - public MySpecialItem Item { get; } - - public MyItemRegion(MySpecialItem item) - : base(null, item.Map, - Region.Find(item.Location, item.Map), // Parent = existing region - new Rectangle2D(item.X - 5, item.Y - 5, 11, 11)) - { - Item = item; - } - - public override void OnEnter(Mobile m) - { - base.OnEnter(m); - // Custom enter logic - } -} -``` - -### Child Region Inside a Dungeon - -When you want effects that don't break dungeon rules (inherits lighting, no housing, etc.): - -```csharp -public class BossArenaRegion : BaseRegion -{ - public BossArenaRegion(Item source) - : base(null, source.Map, - Region.Find(source.Location, source.Map), // Parent = dungeon - new Rectangle2D(source.X - 10, source.Y - 10, 21, 21)) - { - } - - // Inherits DungeonRegion behavior from parent automatically - // Only add what's different: - - public override bool CheckTravel( - Mobile m, Point3D newLocation, TravelCheckType travelType, out TextDefinition message) - { - message = null; - return m.AccessLevel > AccessLevel.Player; // No escape during boss fight - } - - public override void SpellDamageScalar(Mobile caster, Mobile target, ref double damage) - { - base.SpellDamageScalar(caster, target, ref damage); - damage *= 1.25; // Bonus damage in arena - } -} -``` - -### Blocking Specific Spells - -```csharp -public override bool OnBeginSpellCast(Mobile m, ISpell s) -{ - if (m.AccessLevel == AccessLevel.Player && s is MarkSpell) - { - m.SendLocalizedMessage(501802); // Thy spell doth not appear to work... - return false; - } - return base.OnBeginSpellCast(m, s); -} -``` - -## Region Hierarchy & IsPartOf - -Virtual hooks delegate to `Parent` — child regions inherit all parent behavior: - -```csharp -// A child of DungeonRegion automatically has: -// - Dungeon lighting -// - No housing -// - YoungProtected = false - -// Check hierarchy: -region.IsPartOf() // true if this or any ancestor is DungeonRegion -region.GetRegion() // returns first GuardedRegion in ancestor chain -``` - -## Existing Region Types Reference - -| Type | Key Behavior | -|---|---| -| `BaseRegion` | CheckTravel, spawn weights, RuneName | -| `GuardedRegion` | NPC guards, no housing, town spell restrictions | -| `TownRegion` | Guard behavior + Entrance property | -| `DungeonRegion` | Dungeon lighting, no housing, young not protected | -| `NoTravelSpellsAllowedRegion` | Blocks all travel spells (extends DungeonRegion) | -| `NoHousingRegion` | Blocks housing placement | -| `NoHousingGuardedRegion` | Guards + housing block | -| `GreenAcresRegion` | No housing, no travel, no Mark | -| `JailRegion` | Full lockdown: no skills, spells, combat, travel | -| `HouseRegion` | Dynamic per-house; access, bans, lockdowns | -| `MondainRegion` | Mondain's Legacy dungeon (no travel) | - -## Anti-Patterns - -- **Registering regions in deserialization constructors**: Map/parents may not be loaded — use `[AfterDeserialization]` with `Timer.StartTimer(TimeSpan.Zero, ...)` -- **Forgetting `Unregister()` on item delete**: Ghost region stays on map — always unregister in `OnAfterDelete()` -- **Not using parent for dynamic regions**: Loses inherited behavior (lighting, guards, etc.) — pass `Region.Find(location, map)` as parent -- **Using `Region.Find(string, Map)` in hot paths**: Linear O(n) scan — use `Region.Find(Point3D, Map)` (sector-indexed) -- **Overriding a hook without calling `base`**: Breaks parent delegation chain — always call `base.Method()` unless intentionally blocking - -## See Also -- `dev-docs/regions.md` — Complete region system documentation -- `dev-docs/timers.md` — Timer system (for deferred registration) -- `dev-docs/claude-skills/modernuo-content-patterns.md` — Item/Mobile lifecycle (OnAfterDelete, AfterDeserialization) -- `dev-docs/claude-skills/modernuo-serialization.md` — Serialization and AfterDeserialization hooks diff --git a/dev-docs/claude-skills/modernuo-serialization.md b/dev-docs/claude-skills/modernuo-serialization.md deleted file mode 100644 index 91e3d5e95..000000000 --- a/dev-docs/claude-skills/modernuo-serialization.md +++ /dev/null @@ -1,388 +0,0 @@ ---- -name: modernuo-serialization -description: > - Trigger when creating or modifying classes inheriting Item, Mobile, BaseCreature, or any type with [SerializationGenerator]. When adding serialized fields. When discussing migration or version bumps. ---- - -# ModernUO Serialization System - -## When This Activates -- Creating/modifying classes that inherit `Item`, `Mobile`, `BaseCreature`, or any serializable type -- Adding `[SerializableField]` or `[SerializableProperty]` attributes -- Bumping serialization versions -- Working with migration schemas -- Discussing save/load behavior - -## Key Rules - -1. **Always use `partial` class** when applying `[SerializationGenerator]` -2. **Always add `[Constructible]`** on parameterless constructors for Items/Mobiles -3. **Never serialize `TimerExecutionToken`** -- restore timers in `[AfterDeserialization]` -4. **Call `this.MarkDirty()`** in custom property setters that modify serialized state -5. **Use `using ModernUO.Serialization;`** for serialization attributes -6. **Field order matters** -- `[SerializableField(N)]` index determines serialization order -7. **Increment version** when adding, removing, or reordering fields - -## Core Attributes - -### [SerializationGenerator(version, encoded)] -Applied to class. Generates Serialize/Deserialize methods. -- `version`: Current serialization version (0+) -- `encoded`: Omit for new classes. When migrating from pre-codegen Serialize/Deserialize, pass `false` if old code used `reader.ReadInt()` (not `ReadEncodedInt()`) - -```csharp -// New class — omit encoded -[SerializationGenerator(0)] -public partial class MyItem : Item { } - -// Migration from pre-codegen — old version was 2, used ReadInt() -[SerializationGenerator(3, false)] -public partial class MigratedItem : Item { } -``` - -### [SerializableField(index, getter, setter, isVirtual, fieldChanged, allowFieldChange)] -Applied to `_camelCase` private fields. Generates `PascalCase` property. -- `index`: Serialization order (0+) -- `getter`/`setter`: Access level -- `"private"`, `"internal"`, or omit for public -- `isVirtual`: Generate a virtual property -- `fieldChanged`: `nameof` of `void Method(T oldValue, T newValue)`, invoked by the generated setter after assignment -- `allowFieldChange`: `nameof` of `bool Method(ref T value)`, invoked before assignment -- coerce through the `ref` parameter or return `false` to reject - -Generated setter pipeline: equality check → `allowFieldChange` → assignment → `MarkDirty` → `InvalidateProperties` (if declared) → `fieldChanged`. The field still holds the old value while the gate runs. Hooks require a generated setter (SG3018 on readonly/setterless fields); a missing or wrong-shaped named method is SG3015. - -```csharp -[SerializableField(0, allowFieldChange: nameof(AllowChargesChange))] -[SerializedCommandProperty(AccessLevel.GameMaster)] -[InvalidateProperties] -private int _charges; - -private bool AllowChargesChange(ref int value) -{ - value = Math.Clamp(value, 0, MaxCharges); - return true; -} -``` - -### [SerializableProperty(index, useField)] -Applied to properties with **custom getters** (fallback defaults, lazy/self-healing reads) or setter semantics the field hooks cannot express. For setters that only coerce, veto, or run post-change side effects, use `[SerializableField]` with `allowFieldChange`/`fieldChanged` instead. -- `index`: Serialization order -- `useField`: Backing field name if auto-detection fails - -```csharp -[SerializableProperty(0)] -[CommandProperty(AccessLevel.GameMaster)] -public int MaxItems -{ - get => _maxItems == -1 ? DefaultMaxItems : _maxItems; // custom getter: the reason this is a property - set - { - _maxItems = value; - InvalidateProperties(); - this.MarkDirty(); // REQUIRED in custom setters - } -} -``` - -### [InvalidateProperties] -On serialized fields -- auto-calls `InvalidateProperties()` when field changes (refreshes client tooltip). - -```csharp -[SerializableField(0)] -[InvalidateProperties] -[SerializedCommandProperty(AccessLevel.GameMaster)] -private bool _balanced; -``` - -### [SerializedCommandProperty(accessLevel)] -Exposes field to `[Props` gump for in-game editing. - -### [EncodedInt] -Variable-length int encoding (saves space for small values). - -### [AnchoredDateTime] -Stores the absolute UTC instant; shifted by downtime at load so remaining time is preserved. Byte-stable across idle saves. Prefer for deadlines/elapsed-while-running values. - -### [DeltaDateTime] -Stores DateTime as offset from current time (handles server restarts). Legacy: rewrites bytes every save; prefer `[AnchoredDateTime]` for new fields. Converting between the two changes the wire format (version bump). - -### [InternString] -Interns strings to reduce memory for repeated values. - -### [Tidy] -Auto-removes null/deleted entries from collections after deserialization. - -### [CanBeNull] -Marks field as nullable during deserialization. - -### [AfterDeserialization(synchronous)] -Method called after fields are deserialized. The `synchronous` parameter controls timing: -- `true` (default): runs immediately after this entity's deserialization -- `false`: runs after ALL entities in the world are deserialized - -Use `true` (default) for: restarting timers, setting up derived values from own fields. -Use `false` for: logic that calls `Delete()`, depends on other entities, or affects game state. - -```csharp -// Sync (default) — only touches own fields -[AfterDeserialization] -private void AfterDeserialization() -{ - Timer.StartTimer(TimeSpan.FromSeconds(5), CheckExpiry, out _timerToken); -} - -// Deferred — calls Delete() which affects game state -[AfterDeserialization(false)] -private void AfterDeserialization() -{ - if (_expireTimer == null) - { - Delete(); - } -} -``` - -### [DeserializeTimer(nameof(Method), wallClock)] -Required on every serializable `Timer` member (SG3008 otherwise). By default the next tick is stored as **anchored time** (downtime does not consume the remaining delay; idle saves byte-stable); `wallClock: true` stores an absolute deadline instead (delay negative if it passed during downtime). The method -- `void Method(TimeSpan delay)` -- is invoked **only when a timer was running at save**; there is no sentinel to check. - -```csharp -[SerializableField(0, setter: "private")] -[DeserializeTimer(nameof(DeserializeEvaluateTimer), wallClock: true)] -private Timer _evaluateTimer; - -private void DeserializeEvaluateTimer(TimeSpan delay) -{ - _evaluateTimer = Timer.DelayCall(delay, EvaluationInterval, Evaluate); -} -``` - -Switching a timer between drifting and `wallClock` changes the wire format: bump the class version and add `MigrateFrom` -- the old content struct exposes `XxxNext` (`DateTime`) and `XxxDelay` (`TimeSpan`, `TimeSpan.MinValue` when no timer ran). - -### [SaveFlag(nameof(ShouldSerializeMethod), nameof(DefaultValueMethod))] -On the serializable field/property itself. Conditional serialization -- skip fields with default values. Second method optional; when omitted, the field keeps its default at load. - -```csharp -[SerializableField(0)] -[SaveFlag(nameof(ShouldSerializeMaxItems), nameof(MaxItemsDefaultValue))] -private int _maxItems; - -private bool ShouldSerializeMaxItems() => _maxItems != -1; - -private int MaxItemsDefaultValue() => -1; -``` - -### [TypeAlias(aliases)] -Maps old type names for backward-compatible deserialization. - -```csharp -[TypeAlias("Server.Mobiles.Bear")] -[SerializationGenerator(0)] -public partial class BlackBear : BaseCreature { } -``` - -## Patterns - -### Minimal Item (Version 0, No Custom Fields) -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class MyItem : Item -{ - [Constructible] - public MyItem() : base(0x1234) - { - Weight = 1.0; - } - - public override string DefaultName => "a my item"; -} -``` - -### Item with Fields -```csharp -[SerializationGenerator(0)] -public partial class ChargedItem : Item -{ - [SerializableField(0)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _charges; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Mobile _owner; - - private TimerExecutionToken _timerToken; // NOT serialized - - [Constructible] - public ChargedItem() : base(0x1234) => _charges = 10; - - [AfterDeserialization] - private void AfterDeserialization() - { - Timer.StartTimer(TimeSpan.FromSeconds(5), CheckExpiry, out _timerToken); - } - - public override void OnAfterDelete() - { - _timerToken.Cancel(); - base.OnAfterDelete(); - } -} -``` - -### Item with Setter Hooks (coerce + side effects) -```csharp -[SerializationGenerator(2)] -public partial class BagOfSending : Item -{ - [SerializableField(0, fieldChanged: nameof(OnBagOfSendingHueChanged))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private BagOfSendingHue _bagOfSendingHue; - - private void OnBagOfSendingHueChanged(BagOfSendingHue oldValue, BagOfSendingHue newValue) - { - Hue = newValue switch - { - BagOfSendingHue.Yellow => 0x8A5, - BagOfSendingHue.Blue => 0x8AD, - BagOfSendingHue.Red => 0x89B, - _ => Hue - }; - } - - [SerializableField(1, allowFieldChange: nameof(AllowChargesChange))] - [SerializedCommandProperty(AccessLevel.GameMaster)] - [InvalidateProperties] - private int _charges; - - private bool AllowChargesChange(ref int value) - { - value = Math.Clamp(value, 0, MaxCharges); - return true; - } -} -``` - -## Custom Serialize/Deserialize (Purity Rules) - -When writing custom `Serialize(IGenericWriter)` or `Deserialize(IGenericReader)` methods (e.g. for `GenericPersistence` subclasses), the following rules apply: - -### Serialize() MUST remain pure -`Serialize()` is called from **background serialization threads** during world saves (see `SerializationThreadWorker`). Multiple entities are serialized in parallel across threads. This means `Serialize()` must NOT: - -- **Create or destroy Items/Mobiles** -- mutates shared world state -- **Move, equip, or unequip Items/Mobiles** -- mutates shared world state -- **Start or stop timers** (`Timer.StartTimer`, `Timer.DelayCall`, `_token.Cancel()`) -- timers are NOT thread-safe -- **Send packets or modify NetState** -- networking is game-thread-only -- **Access or modify other entities' mutable state** -- data race -- **Call `Delete()`** on anything -- triggers deletion cascades on wrong thread - -`Serialize()` should ONLY read fields and write them to the `IGenericWriter`. Treat it as a read-only snapshot. - -```csharp -// CORRECT -- pure reads and writes only -public override void Serialize(IGenericWriter writer) -{ - writer.WriteEncodedInt(0); // version - writer.WriteEncodedInt(_records.Count); - foreach (var (key, value) in _records) - { - writer.Write(key); - writer.Write(value); - } -} - -// WRONG -- side effects in Serialize -public override void Serialize(IGenericWriter writer) -{ - CleanupExpiredEntries(); // BAD: mutates state - Timer.StartTimer(Recheck); // BAD: not thread-safe - writer.Write(_data); -} -``` - -### Deserialize() runs on the game thread -`Deserialize()` runs during world load on the main thread, so it CAN create entities and start timers. However, prefer `[AfterDeserialization]` for timer setup to keep deserialization clean. - -## MigrateFrom Pattern - -When bumping the `[SerializationGenerator]` version, you **must** add a `MigrateFrom` method: - -```csharp -// Version bumped from 0 to 1 (added _quality field) -[SerializationGenerator(1)] -public partial class MagicGem : Item -{ - [SerializableField(0)] - private int _charges; - - [SerializableField(1)] // New in v1 - private GemQuality _quality; -} - -// In MagicGem.Migrations.cs: -public partial class MagicGem -{ - private void MigrateFrom(V0Content content) - { - _charges = content.Charges; - // _quality defaults to GemQuality.Rough (default enum value) - } -} -``` - -- Signature: `private void MigrateFrom(VXContent content)` where X is the **previous** version -- `VXContent` is auto-generated with PascalCase properties matching the old fields -- New fields not in the old version get their default values -- Use `.Migrations.cs` partial files for organization - -## Anti-Patterns - -- **Missing `partial`**: `[SerializationGenerator]` requires `partial class` -- **Serializing timers**: `TimerExecutionToken` cannot be serialized -- **Side effects in `Serialize()`**: Serialize runs on background threads -- must be pure (no creating/destroying entities, no timer start/stop, no packets) -- **Missing `MarkDirty()`**: Custom property setters must call `this.MarkDirty()` -- **Wrong field prefix**: Use `_camelCase`, not `m_camelCase` for new fields -- **Forgetting `[Constructible]`**: Items/Mobiles need this for `[add` command -- **Modifying `Deserialize(reader, version)` for version bumps**: `Deserialize` exists ONLY for pre-codegen legacy saves. Use `MigrateFrom(VXContent)` for all post-codegen version transitions. - -## Real Examples -- Simple creature: `Projects/UOContent/Mobiles/Animals/Bears/BlackBear.cs` -- Serialized fields + timer: `Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs` -- Setter hooks (allowFieldChange + fieldChanged): `Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs` -- Custom getters (era fallbacks, the [SerializableProperty] use case): `Projects/UOContent/Items/Weapons/BaseWeapon.cs` -- Complex with AfterDeserialization: `Projects/UOContent/Accounting/Account.cs` -- Timer deserialization (wall-clock): `Projects/UOContent/Items/Aquarium/Aquarium.cs` -- Timer deserialization (drifting/anchored + timer MigrateFrom): `Projects/UOContent/Items/Lights/BaseLight.cs` -- Tidy + DeltaDateTime: `Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs` -- Conditional serialization ([SaveFlag]): `Projects/Server/Items/Container.cs` - -## Version Migration -Migration schemas are JSON files in `Projects/Server/Migrations/` and `Projects/UOContent/Migrations/`: -- Format: `TypeName.vN.json` -- Read by the serialization generator at compile time to produce `VXContent` types for `MigrateFrom` -- Used for reading old save formats - -### Schema generator must be run after every version bump - -The `dotnet build` does **not** emit migration JSON files. After bumping `[SerializationGenerator(N)]` to `N+1`, run the schema generator tool to produce `TypeName.v{N+1}.json`. Commit the new JSON alongside the code change. - -```sh -dotnet tool restore -dotnet tool run ModernUOSchemaGenerator -- ModernUO.slnx -``` - -Verify the new `TypeName.v{N+1}.json` was created in the appropriate `Migrations/` folder. If the JSON is missing, future version bumps that need to migrate from this version will fail to compile (the generator can't build `VXContent` for a version with no schema on disk). - -Also available via the build tool: `dotnet run --project Projects/BuildTool -- --action migrate`. - -External reference: https://github.com/modernuo/SerializationGenerator - -## See Also -- `dev-docs/serialization.md` - Complete serialization documentation -- `dev-docs/claude-skills/modernuo-timers.md` - Timer token patterns -- `dev-docs/claude-skills/modernuo-content-patterns.md` - Item/Mobile templates -- `dev-docs/claude-skills/modernuo-property-lists.md` - [InvalidateProperties] usage diff --git a/dev-docs/claude-skills/modernuo-string-handling.md b/dev-docs/claude-skills/modernuo-string-handling.md deleted file mode 100644 index 76e00454b..000000000 --- a/dev-docs/claude-skills/modernuo-string-handling.md +++ /dev/null @@ -1,157 +0,0 @@ -# ModernUO String Handling Skill - -## When This Skill Applies -- Any code that builds strings dynamically (concatenation, formatting, interpolation) -- Converting `System.Text.StringBuilder` to `ValueStringBuilder` -- Packet string construction -- Gump/message text building - -## Core Rule -**Never use `System.Text.StringBuilder`**. Use `Server.Text.ValueStringBuilder` everywhere. - -## Quick Reference - -### Construction -```csharp -// Bounded output (preferred): zero heap allocation -using var sb = new ValueStringBuilder(stackalloc char[128]); - -// Unbounded output: rents from STArrayPool -using var sb = ValueStringBuilder.Create(256); -using var sb = ValueStringBuilder.Create(); // default 64 chars -``` - -### String Interpolation -Works with stackalloc — writes directly into the builder's buffer: -```csharp -using var sb = new ValueStringBuilder(stackalloc char[64]); -sb.Append($"Player {name} has {kills} kills"); -``` - -### Reuse via Reset -Use `Reset()` instead of creating a new builder: -```csharp -using var sb = new ValueStringBuilder(stackalloc char[128]); -foreach (var item in items) -{ - sb.Reset(); - sb.Append($"{item.Name}: {item.Value}"); - Process(sb.ToString()); -} -``` - -### Reading Results -- `sb.ToString()` — when you need a string (allocates) -- `sb.AsSpan()` — when consumer accepts `ReadOnlySpan` (zero-alloc) - -## Common Mistakes - -### 1. Using StringBuilder -```csharp -// BAD -var sb = new StringBuilder(); -sb.Append(name); -return sb.ToString(); - -// GOOD -using var sb = new ValueStringBuilder(stackalloc char[64]); -sb.Append(name); -return sb.ToString(); -``` - -### 2. Forgetting `using var` -```csharp -// BAD: pooled array may leak if Grow() happened -var sb = ValueStringBuilder.Create(); -return sb.ToString(); // never disposed! - -// GOOD -using var sb = ValueStringBuilder.Create(); -return sb.ToString(); -``` - -### 3. Chaining Append calls -```csharp -// BAD: VSB Append returns void, not this -sb.Append("a").Append("b"); - -// GOOD -sb.Append("a"); -sb.Append("b"); - -// BETTER: use interpolation -sb.Append($"a{value}b"); -``` - -### 4. Reassigning a using variable -```csharp -// BAD: can't reassign using var -using var sb = ValueStringBuilder.Create(); -sb = ValueStringBuilder.Create(); // CS1656! - -// GOOD: use Reset() -using var sb = ValueStringBuilder.Create(); -sb.Reset(); -``` - -### 5. `using var` with `ref` extension methods -```csharp -// BAD: CS1657 — using var can't be passed by ref -using var sb = new ValueStringBuilder(stackalloc char[64]); -sb.AppendSpaceWithArticle(text, articleAn); // takes ref VSB - -// GOOD: manual Dispose -var sb = new ValueStringBuilder(stackalloc char[64]); -sb.AppendSpaceWithArticle(text, articleAn); -var result = sb.ToString(); -sb.Dispose(); -``` - -### 6. No AppendFormat — use `$"..."` interpolation -```csharp -// BAD: AppendFormat doesn't exist on VSB (no object[] params equivalent) -sb.AppendFormat("{0:N0} points, {1:N0} kills", score, kills); - -// GOOD: use interpolation with format specifiers (zero boxing, zero intermediate strings) -sb.Append($"{score:N0} points, {kills:N0} kills"); -``` - -## Interpolation Anti-Patterns (handler-aware APIs) - -Many APIs accept `ref RawInterpolatedStringHandler` (messages on `Mobile`/`Item`, `IPropertyList.Add`, `SpanWriter.WriteAscii`/`WriteLatin1`, gump `AddLabel`/`AddHtml`, `Html.Center`/`Color`/`Right`, etc.). The handler overload renders the interpolation directly into a pooled buffer with zero `string` allocation — but **only when the call-site argument is a `$"..."` literal directly in position**. These patterns silently defeat that selection. Flag any of them in messaging/gump/OPL code. - -| Pattern | Why bad | Fix | -|---|---|---| -| `Send(cond ? $"a" : $"b")` | Ternary unifies branches as `string` | `if/else` with two calls | -| `Send(thing switch { 1 => $"a", _ => $"b" })` | Switch expr unifies as `string` | `switch` statement, call per arm | -| `var s = $"foo {x}"; Send(s);` | Local typed `string`; ROS overload picked | Inline at call site | -| `Send($"x {value.ToString()}")` | `.ToString()` allocates a `string` per call | Drop `.ToString()` — handler formats directly | -| `Send($"x {td.String()}")` | `TextDefinition.String` allocates | Drop `.String()` (or pass `td` directly if API supports it) | -| `Send($"x {a + b}")` | `string + string` allocates | Multiple holes: `Send($"x {a}{b}")` | -| `Send(string.Format("x {0}", v))` | Format allocates | `Send($"x {v}")` | -| `Send($"x {items.Aggregate(...)}")` | LINQ string ops allocate | `ValueStringBuilder` + pass span | - -For lowercase output, use the `:L` format specifier instead of `value.ToString().ToLowerInvariant()`: - -```csharp -mob.SendMessage($"You earned a {rank:L} trophy!"); // "gold" not "Gold" -``` - -`:L` is recognized by `RawInterpolatedStringHandler.AppendFormatted(T, string?)` and the `(ROS, int, string?)` overload. Case-sensitive — use uppercase `:L`. - -## Capacity Sizing Guide - -| Content | Recommended | -|---|---| -| Version strings, coordinates | `stackalloc char[32-48]` | -| Player names, short messages | `stackalloc char[64]` | -| Item descriptions, titles | `stackalloc char[128]` | -| Paragraph text, HTML snippets | `stackalloc char[256]` | -| Large HTML, gump content | `Create(512)` or `Create()` | -| Unbounded (logs, file paths) | `Create()` | - -## Related Docs -- `dev-docs/string-handling.md` — full reference (incl. interpolation anti-patterns + `:L` spec) -- `dev-docs/code-standards.md` — memory management rules -- `dev-docs/property-lists.md` — IPropertyList string interpolation (different handler) -- `dev-docs/networking-packets.md` — player-facing message APIs and their handler overloads diff --git a/dev-docs/claude-skills/modernuo-threading.md b/dev-docs/claude-skills/modernuo-threading.md deleted file mode 100644 index 3b6507af2..000000000 --- a/dev-docs/claude-skills/modernuo-threading.md +++ /dev/null @@ -1,247 +0,0 @@ ---- -name: modernuo-threading -description: > - Trigger when discussing async patterns, world saves, game loop, or reviewing code for threading issues. When using await, Task, or any concurrency-related code in game logic. ---- - -# ModernUO Threading & Event Loop - -## When This Activates -- Reviewing code for threading issues -- Discussing async/await patterns -- Working with world saves -- Any mention of `Task.Run`, `Thread`, `lock`, `ConcurrentDictionary` -- Understanding the game loop - -## CRITICAL RULE: Single-Threaded Game Logic - -ModernUO uses a **single-threaded game loop**. All game logic runs on one thread. There are NO exceptions for game code. - -## Forbidden in Game Code - -```csharp -// ALL of these are WRONG in Projects/UOContent/ code: -Task.Run(() => ProcessItems()); // Background thread -new Thread(BackgroundWork).Start(); // Manual thread -ThreadPool.QueueUserWorkItem(Work); // Thread pool -lock (_syncObj) { ... } // Locking -Monitor.Enter(obj); // Monitor -volatile int _counter; // Volatile -ConcurrentDictionary _items; // Concurrent collections -ConcurrentQueue _queue; // Concurrent collections -Interlocked.Increment(ref _count); // Atomics -Mutex mutex; // OS mutex -Semaphore sem; // Semaphore -ReaderWriterLockSlim rwl; // RW lock -``` - -**Why**: The game loop is single-threaded. Concurrency primitives add overhead for no benefit, and background threads would cause data races with game state. - -## Why await Is Safe - -`EventLoopContext` implements `SynchronizationContext` and routes all `await` continuations back to the main thread: - -```csharp -// This is SAFE in game code: -await Timer.Pause(TimeSpan.FromMilliseconds(100)); -// Continuation runs on the game thread, not a thread pool thread -``` - -The flow: -1. `await` captures `EventLoopContext` as the synchronization context -2. When the awaited task completes, the continuation is posted to `EventLoopContext._queue` -3. `LoopContext.ExecuteTasks()` runs those continuations on the main thread during the next game loop tick - -## Game Loop Structure - -```csharp -// Simplified from Projects/Server/Main.cs -while (!Closing) -{ - _tickCount = GetTimestamp(); - _now = DateTime.UtcNow; - - Mobile.ProcessDeltaQueue(); // Send mobile state changes to clients - Item.ProcessDeltaQueue(); // Send item state changes to clients - Timer.Slice(_tickCount); // Execute due timers - NetState.Slice(); // Process network I/O - LoopContext.ExecuteTasks(); // Run async continuations (Timer.Pause, etc.) - Timer.CheckTimerPool(); // Refill timer pool if needed -} -``` - -## EventLoopContext Details - -```csharp -public sealed class EventLoopContext : SynchronizationContext -{ - private readonly ConcurrentQueue _queue; // Normal tasks - private readonly ConcurrentQueue _priorityQueue; // High priority - private readonly int _maxPerFrame; // Default: 128 - - // Posts run on next ExecuteTasks() call - public void Post(Action d, Priority priority = Priority.Normal); - - // Send blocks if called from another thread, immediate if on game thread - public override void Send(SendOrPostCallback d, object state); - - // Called once per game loop tick - public void ExecuteTasks(); -} -``` - -## Memory: STArrayPool vs ArrayPool - -In game code, use `STArrayPool.Shared` (single-threaded, no locks): -```csharp -// GOOD - no locking overhead -var buffer = STArrayPool.Shared.Rent(1024); -try { /* use buffer */ } -finally { STArrayPool.Shared.Return(buffer); } -``` - -`ArrayPool.Shared` uses locks for thread safety -- unnecessary overhead in single-threaded context. - -## Memory: PooledRefList - -```csharp -// Stack-allocated list using pooled arrays -using var list = PooledRefList.Create(); -list.Add(mobile); -// Automatically returns array to pool on Dispose - -// For multi-threaded contexts (rare): -using var list = PooledRefList.CreateMT(); -``` - -## World Saves - -World saves use **parallel serialization threads**. This is critical to understand: - -1. **Preserialize**: Allocates heaps and wakes serialization thread workers (background) -2. **Snapshot**: Main thread calls `Persistence.SerializeAll()` which pushes entities into `SerializationThreadWorker` queues (round-robin). Workers call `Serialize(writer)` on **their own background threads** in parallel. -3. **Write snapshot**: Disk I/O on background threads after serialization completes - -```csharp -// From World.cs -- save flow: -World.Save(); -→ Preserialize() on thread pool (allocate heaps, wake serialization workers) -→ Snapshot() on main thread (queues entities to workers, workers serialize in parallel) - → SerializationThreadWorker.Execute() calls e.Serialize(writer) on background thread -→ PauseSerializationThreads() (wait for workers to finish) -→ WriteSnapshot() on thread pool (disk I/O only) -``` - -### Serialize() runs on background threads -Because `SerializationThreadWorker` calls `Serialize()` on its own thread, **`Serialize()` must be pure**: -- **NO** creating/destroying Items or Mobiles -- **NO** starting/stopping timers (not thread-safe) -- **NO** sending packets or modifying NetState -- **NO** mutating shared game state -- **ONLY** read fields and write to `IGenericWriter` - -See `modernuo-serialization.md` for full purity rules. - -## Exceptions: Server Infrastructure - -These files MAY use threading (they're server infrastructure, not game logic): -- `Projects/Server/Main.cs` - Event loop, thread setup -- `Projects/Server/World/World.cs` - World save I/O -- `Projects/Server/Network/` - Network I/O -- `Projects/Server/Timer/Timer.Pool.cs` - Pool refill - -## Exceptions: Vetted Workers in UOContent - -**A background thread is a last resort.** The forbidden list is about game logic, which is never -threaded. A dedicated worker touching no game state is the sanctioned way off the loop, and -necessarily uses `new Thread`, `ConcurrentQueue`, `Interlocked`, `AutoResetEvent` and -`volatile` **at the thread boundary only**. - -### Prove the need first - -- Measure **on-loop time**, not wall-clock. Frozen world is the cost; player latency is not. -- Off-loading creates no CPU. On 1-2 cores there is no spare core — gate on `ProcessorCount`. -- Count what stays: dispatch, continuation, and the loop slowing while the worker evicts shared L3. -- Record the measurement, or nobody can re-justify the worker later. - -### Game logic stays on the loop — chunk it - -Work needing game state cannot be threaded at any core count. Too slow for one tick? Split across -ticks, bounded by count or elapsed time — never "until done". - -```csharp -Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMilliseconds(50), () => -{ - var budget = 0; - while (_cursor < _items.Count && budget++ < 100) { Process(_items[_cursor++]); } -}); -``` - -### Vetted workers - -| Worker | Justification | -|---|---| -| `Accounting/Security/PasswordWorker.cs` | 8.9 ms/login on-loop at Argon2; 3.5-8.9 ms measured saving | -| `Engines/Advanced Search/AdvancedSearchGump.cs` | Admin-triggered full-world scan, saves disabled | - -### The six rules - -1. No game state read or written off-thread; dispatch immutable values captured on the loop. -2. Resolve policy (algorithm, salt, era branch) at dispatch — the worker holds none. -3. Park on a kernel wait, never spin. Spinning burns a core on shared hosts. -4. Run only while `WorldState is Running or WritingSave`. **Not** `World.Saving` — that misses - `PendingSave`, where serialization threads are already spinning. -5. Bounded queue, or a bound upstream named in a comment. -6. Everything the worker calls must itself be thread-safe. A singleton is not automatically safe — - `HashAlgorithm.ComputeHash` carries state, `Utility`'s RNG is a shared `System.Random` and game - state. Prefer static one-shot APIs (`SHA256.HashData`, `RandomNumberGenerator.Fill`). - -### Crossing the boundary - -Dispatch captures what the continuation will need to re-validate: - -```csharp -var job = new Job { Target = state, Expected = account.Password, Input = DerivePhrase(...) }; -if (!Worker.TryEnqueue(job)) { /* reject — never fall back to running it inline */ } -``` - -Hand back one of two ways, and no other: - -```csharp -Core.LoopContext.Post(() => Apply(job, result)); // a result for a specific caller -Volatile.Write(ref _snapshot, newTable); // a shared table rebuilt periodically -``` - -The continuation re-validates, because time passed: - -```csharp -if (job.Target?.Running != true) { return; } // gone -if (account.Password != job.Expected) { return; } // changed underneath -``` - -Always post a result, including on failure — a worker that throws silently leaves its caller -waiting forever. Use `ConfigureAwait(false)` on every await inside off-loop work. - -## Anti-Patterns - -| Pattern | Problem | Solution | -|---|---|---| -| `Task.Run(...)` | Runs on thread pool, races with game state | Use `Timer.StartTimer()` | -| `new Thread(...)` | Same as above | Use `Timer.StartTimer()` | -| `lock(obj)` | Unnecessary overhead, no contention exists | Remove lock, use plain code | -| `ConcurrentDictionary` | Lock-free but still overhead | Use `Dictionary` | -| `volatile` | Memory barriers not needed on single thread | Use plain field | -| `Thread.Sleep()` | Blocks entire game loop | Use `await Timer.Pause()` | -| `ArrayPool.Shared` | Uses locks | Use `STArrayPool.Shared` | - -## Real Examples -- Game loop: `Projects/Server/Main.cs` (RunEventLoop) -- EventLoopContext: `Projects/Server/EventLoopTasks.cs` -- STArrayPool: `Projects/Server/Buffers/STArrayPool.cs` -- PooledRefList: `Projects/Server/Collections/PooledRefList.cs` -- World save: `Projects/Server/World/World.cs` - -## See Also -- `dev-docs/threading-model.md` - Complete threading documentation -- `dev-docs/claude-skills/modernuo-code-audit.md` - Threading audit rules -- `dev-docs/claude-skills/modernuo-timers.md` - Timer-based scheduling diff --git a/dev-docs/claude-skills/modernuo-timers.md b/dev-docs/claude-skills/modernuo-timers.md deleted file mode 100644 index f366b3569..000000000 --- a/dev-docs/claude-skills/modernuo-timers.md +++ /dev/null @@ -1,230 +0,0 @@ ---- -name: modernuo-timers -description: > - Trigger when creating delayed actions, recurring timers, or any time-based behavior. When using Timer.StartTimer, Timer.DelayCall, or TimerExecutionToken. ---- - -# ModernUO Timers & Scheduling - -## When This Activates -- Creating delayed actions or recurring timers -- Working with `Timer`, `TimerExecutionToken`, `DelayCallTimer` -- Implementing decay, expiration, or periodic behavior -- Restoring timers after deserialization - -## Key Rules - -1. **Prefer `Timer.StartTimer` with token** for cancellable timers -2. **Prefer `Timer.StartTimer` without token** for fire-and-forget -3. **Never serialize `TimerExecutionToken`** -- restore in `[AfterDeserialization]` -4. **Always cancel timers in `OnDelete()`/`OnAfterDelete()`** -5. **8ms minimum precision** -- timer wheel uses 8ms tick rate -6. **Timers are NOT thread-safe** -- never Start/Stop timers or call `Timer.DelayCall`/`Timer.StartTimer` from any thread other than the game thread. This includes `Serialize()` which runs on background serialization threads during world saves. - -## Preferred APIs (In Order) - -### 1. Timer.StartTimer with Token (Cancellable Fire-and-Forget) -```csharp -private TimerExecutionToken _timerToken; - -// One-shot -Timer.StartTimer(TimeSpan.FromSeconds(5), DoSomething, out _timerToken); - -// Repeating -Timer.StartTimer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), CheckExpiry, out _timerToken); - -// Repeating with count limit -Timer.StartTimer(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), 10, Tick, out _timerToken); - -// Cancel -_timerToken.Cancel(); // Safe to call multiple times - -// Check state -if (_timerToken.Running) { } -``` - -### 2. Timer.StartTimer without Token (Fire-and-Forget, No Cancel) -```csharp -Timer.StartTimer(Delete); // Immediate -Timer.StartTimer(TimeSpan.FromSeconds(5), Delete); // Delayed -Timer.StartTimer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), Tick); // Repeating -``` - -### 3. Timer.DelayCall (Returns Timer Object) -```csharp -var timer = Timer.DelayCall(TimeSpan.FromSeconds(5), DoSomething); -timer.Stop(); // Cancel later if needed -``` - -### 4. Timer.DelayCall with State (Avoids Closures) -```csharp -// Passes state without lambda allocation -Timer.DelayCall(TimeSpan.FromSeconds(2), ProcessTarget, from, target); -// Calls: ProcessTarget(Mobile from, Mobile target) after 2s - -// Up to 5 parameters supported -Timer.DelayCall(TimeSpan.FromSeconds(1), DoWork, arg1, arg2, arg3); -``` - -### 5. Timer.Pause (Awaitable) -```csharp -await Timer.Pause(TimeSpan.FromMilliseconds(100)); -await Timer.Pause(500); // 500ms overload -``` - -## TimerExecutionToken Properties - -```csharp -_timerToken.Running // bool: is timer still active? -_timerToken.Index // int: how many times OnTick has fired -_timerToken.RemainingCount // int: ticks remaining (int.MaxValue if infinite) -_timerToken.Next // DateTime: when next tick fires -_timerToken.Cancel() // Stop and return to pool (safe to call multiple times) -``` - -## Timer Wheel Architecture - -3-layer hierarchical wheel with 4096 slots per layer: -- Layer 0: 8ms resolution, ~33 second range -- Layer 1: ~33s resolution, ~22 minute range -- Layer 2: ~22m resolution, ~16 day range - -All delays rounded up to nearest 8ms boundary. - -## Patterns - -### Cleanup in Deletion -```csharp -public override void OnDelete() -{ - _timerToken.Cancel(); // TimerExecutionToken - base.OnDelete(); -} - -public override void OnAfterDelete() -{ - _timer?.Stop(); // Timer reference - _timer = null; - base.OnAfterDelete(); -} -``` - -### Timer Restoration After Deserialization -```csharp -[SerializationGenerator(0)] -public partial class DecayingItem : Item -{ - private TimerExecutionToken _decayTimer; // NOT serialized - - [SerializableField(0)] - [DeltaDateTime] - private DateTime _expireTime; - - [Constructible] - public DecayingItem() : base(0x1234) - { - _expireTime = Core.Now + TimeSpan.FromHours(1); - Timer.StartTimer(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), CheckDecay, out _decayTimer); - } - - [AfterDeserialization] - private void AfterDeserialization() - { - Timer.StartTimer(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), CheckDecay, out _decayTimer); - } - - public override void OnAfterDelete() - { - _decayTimer.Cancel(); - base.OnAfterDelete(); - } - - private void CheckDecay() - { - if (Core.Now >= _expireTime) - Delete(); - } -} -``` - -### [DeserializeTimer] Pattern (for Timer fields) -Required on every serializable `Timer` member. Drifting by default: the next tick is stored -as anchored time, so server downtime does not consume the remaining delay. Use -`wallClock: true` for absolute deadlines (delay is negative if it passed during downtime). -The method is invoked **only when a timer was running at save** — no sentinel to check. - -```csharp -[SerializableField(0, setter: "private")] -[DeserializeTimer(nameof(DeserializeEvaluateTimer), wallClock: true)] -private Timer _evaluateTimer; - -private void DeserializeEvaluateTimer(TimeSpan delay) -{ - _evaluateTimer = Timer.DelayCall(delay, EvaluationInterval, Evaluate); -} -``` - -Switching an existing timer between drifting and `wallClock` changes the wire format — bump -the class's `[SerializationGenerator]` version and add a `MigrateFrom` (the old content -struct exposes `XxxDelay`, `TimeSpan.MinValue` when no timer ran). - -### Custom Timer Class (When You Need Complex Logic) -```csharp -private class DecayTimer : Timer -{ - private readonly Corpse _corpse; - - public DecayTimer(Corpse c, TimeSpan delay) : base(delay) - { - _corpse = c; - } - - protected override void OnTick() - { - if (!_corpse.GetFlag(CorpseFlag.NoBones)) - _corpse.TurnToBones(); - else - _corpse.Delete(); - } -} - -// Usage: -_decayTimer = new DecayTimer(this, delay); -_decayTimer.Start(); - -// Cleanup: -_decayTimer?.Stop(); -_decayTimer = null; -``` - -## Anti-Patterns - -- **Serializing `TimerExecutionToken`**: It's a struct with internal Timer reference -- not serializable -- **Forgetting cleanup**: Timers keep running if not cancelled on deletion -- **Using `Thread.Sleep`**: Blocks the game loop. Use `Timer.StartTimer` or `await Timer.Pause` instead -- **Creating timers in constructors called during deserialization**: Use `[AfterDeserialization]` instead -- **Starting/stopping timers in `Serialize()`**: `Serialize()` runs on background threads during world saves -- timer APIs are game-thread-only and will corrupt state or crash - -## Real Examples -- Token cleanup: `Projects/UOContent/Spells/Third/WallOfStone.cs` -- Repeating timer: `Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs` -- Custom timer class: `Projects/UOContent/Items/Misc/Corpses/Corpse.cs` -- State-carrying delay: Various files using `Timer.DelayCall(delay, callback, arg1, arg2)` -- Timer deserialization: `Projects/UOContent/Items/Aquarium/Aquarium.cs` -- Timer pool config: `Projects/Server/Timer/Timer.Pool.cs` - -## Timer Files -- `Projects/Server/Timer/Timer.cs` - Base class -- `Projects/Server/Timer/Timer.DelayCall.cs` - DelayCall + StartTimer -- `Projects/Server/Timer/Timer.TimerWheel.cs` - Scheduler -- `Projects/Server/Timer/Timer.Pool.cs` - Pool management -- `Projects/Server/Timer/Timer.DelayStateCall.cs` - Generic state timers -- `Projects/Server/Timer/TimerExecutionToken.cs` - Token struct - -## See Also -- `dev-docs/timers.md` - Complete timer documentation -- `dev-docs/event-scheduler.md` - Wall-clock/calendar scheduling (EventScheduler) — use for daily resets, weekly events, holiday seasons instead of Timer -- `dev-docs/claude-skills/modernuo-event-scheduler.md` - EventScheduler skill for calendar-based events -- `dev-docs/claude-skills/modernuo-serialization.md` - Timer fields not serialized -- `dev-docs/claude-skills/modernuo-content-patterns.md` - Deletion patterns -- `dev-docs/claude-skills/modernuo-threading.md` - Single-threaded model diff --git a/dev-docs/code-standards.md b/dev-docs/code-standards.md deleted file mode 100644 index 6a5540047..000000000 --- a/dev-docs/code-standards.md +++ /dev/null @@ -1,617 +0,0 @@ -# ModernUO Coding Standards - -This document defines the coding conventions and standards for ModernUO content development. All code under `Projects/UOContent/` and `Projects/Server/` must follow these guidelines. - -## Table of Contents -1. [Naming Conventions](#naming-conventions) -2. [Performance Rules](#performance-rules) -3. [Serialization Requirements](#serialization-requirements) -4. [Logging](#logging) -5. [Threading Model](#threading-model) -6. [Memory Management](#memory-management) -7. [Entity Lifecycle](#entity-lifecycle) -8. [Era-Conditional Code](#era-conditional-code) -9. [File Organization](#file-organization) - ---- - -## Naming Conventions - -### Fields and Properties -- **Private fields**: `_camelCase` prefix with underscore - ```csharp - private int _charges; - private Mobile _owner; - private TimerExecutionToken _timerToken; - ``` -- **Properties**: `PascalCase` - ```csharp - public int Charges { get; set; } - public Mobile Owner => _owner; - ``` -- **Methods**: `PascalCase` - ```csharp - public void OnDoubleClick(Mobile from) { } - private void CheckExpiry() { } - ``` -- **Constants**: `PascalCase` - ```csharp - public const int MaxCharges = 20; - ``` -- **Local variables**: `camelCase` - ```csharp - var damage = Utility.RandomMinMax(10, 20); - ``` - -### Brace Style -ALL control flow statements (`if`, `else`, `for`, `foreach`, `while`, `do`, `switch`) **must** have braces, even for single-line bodies. This reduces merge conflicts and diff sizes. - -```csharp -// BAD -if (condition) - DoSomething(); - -// GOOD -if (condition) -{ - DoSomething(); -} -``` - -### Switch Patterns -Prefer switch expressions and switch-when pattern matching where they improve readability and enable JIT/PGO optimization. Skip if code becomes unreadable or is on a cold path. - -```csharp -// Prefer switch expression for value mapping -private static string GetName(GemType type) => type switch -{ - GemType.StarSapphire => "star sapphire", - GemType.Emerald => "emerald", - _ => "gem" -}; - -// Prefer switch-when for performance (compiler hints to JIT/PGO) -switch (item) -{ - case Sword { Quality: >= ItemQuality.Exceptional } when Core.AOS: - { - bonus = 10; - break; - } -} -``` - -### Legacy Code -Older code uses `m_` prefix for private fields (e.g., `m_Amount`). Do not change existing `m_` fields, but always use `_` prefix for new code. - -### Access Levels -```csharp -public enum AccessLevel -{ - Player, // Regular players - Counselor, // Support staff - GameMaster, // GMs - Seer, // Event coordinators - Administrator,// Server admins - Developer, // Developers - Owner // Server owner -} -``` -Reference: `Projects/Server/Mobiles/Mobile.cs` - ---- - -## Performance Rules - -### LINQ: Know What's Optimized (.NET 10) - -.NET 10's JIT and Dynamic PGO can now eliminate abstraction overhead for specific LINQ patterns. Not all LINQ is banned — but most still is. This section defines exactly what's allowed and what isn't. - -**Prerequisites**: All optimizations below require .NET 10 with tiered compilation and Dynamic PGO enabled (both on by default — don't disable them). Tier 1 optimizations need ~30+ calls for the JIT to recompile at Tier 1 with PGO data. - -#### Tier 1 — Zero-Cost Abstractions (use freely on hot paths) - -These patterns produce **zero heap allocations** after JIT warmup, performing as well as hand-written code. - -**`foreach` over `IEnumerable` backed by known collection types:** - -PGO profiles the concrete type. Guarded devirtualization (GDV) emits a specialized path. The enumerator is devirtualized, inlined, and stack-allocated. - -Optimized backing types: `T[]`, `List`, `Stack`, `Queue`, `ConcurrentDictionary`, `PriorityQueue`. - -```csharp -// ✅ ALLOWED — JIT eliminates all abstraction overhead -int Sum(IEnumerable values) // caller passes int[] or List -{ - int sum = 0; - foreach (int v in values) sum += v; - return sum; -} -``` - -Falls back to normal virtual dispatch + heap-allocated enumerator for unknown/uncommon collection types or before JIT warmup. - -**`.Contains()` after a preceding LINQ operator:** - -LINQ has ~30 specialized `Contains` overrides that bypass intermediate processing entirely. No sort, no HashSet, no buffering — the source is searched directly. - -| Preceding operator | What `.Contains()` does | Speedup vs .NET 9 | -|---|---|---| -| `.Distinct()` | Searches source directly — no HashSet built | ~363x | -| `.Union(other)` | Searches both sources — no HashSet | ~302x | -| `.OrderBy()` / `.OrderByDescending()` | Searches source directly — no sort | ~258x | -| `.ThenBy()` / `.ThenByDescending()` | Same — no sort | ~258x | -| `.Append()` / `.Prepend()` / `.Concat()` | Searches sequentially | ~56x | -| `.SelectMany(f)` | Searches each sub-source | ~49x | -| `.Reverse()` | Searches source directly — no buffering | ~9x | -| `.Where(p).Select(f)` | Applies predicate+projection inline | ~7x | -| `.Select(f)` | Applies projection inline | moderate | -| `.Skip(n)` / `.Take(n)` | Searches within bounds | moderate | -| `.OfType()` / `.Cast()` | Filters/casts and searches | moderate | -| `.Intersect(other)` / `.Except(other)` | Searches appropriately | large | -| `.Shuffle()` | Searches source directly — no shuffle | large | -| `.Shuffle().Take(n)` | Hypergeometric probability — near O(1) math | massive | - -```csharp -// ✅ ALLOWED — no sort performed, source searched directly -bool exists = source.OrderBy(x => x.Name).Contains(target); - -// ✅ ALLOWED — no HashSet built -bool exists = source.Distinct().Contains(target); - -// ✅ ALLOWED — no buffering/reversing -bool exists = source.Reverse().Contains(target); -``` - -Falls back to normal enumeration for custom `IEnumerable` implementations that LINQ doesn't recognize. - -**`.Count()` on sized collections:** - -Returns `.Count` property directly when source implements `ICollection` or is a known LINQ iterator with tracked count (after `Range`, `Repeat`, `Skip`, `Take`, `Append`, etc.). O(1), no enumeration. - -```csharp -// ✅ ALLOWED — O(1) property access -int count = myList.Count(); -int count = Enumerable.Range(0, 1000).Skip(10).Take(50).Count(); -``` - -**`.OrderBy().First()` / `.OrderByDescending().First()` / `.OrderBy().Last()`:** - -LINQ performs O(N) min/max scan instead of O(N log N) sort. No sort buffer allocated. - -```csharp -// ✅ ALLOWED — O(N) scan, no sort -var cheapest = products.OrderBy(p => p.Price).First(); -var newest = events.OrderByDescending(e => e.Timestamp).First(); -``` - -**`.Shuffle().Take(n)`:** - -Uses reservoir sampling — single pass over source, O(n) memory. Does NOT shuffle the entire collection. - -```csharp -// ✅ ALLOWED — reservoir sampling, not full shuffle -var sample = population.Shuffle().Take(10).ToArray(); -``` - -**`Enumerable.Range()` / `Enumerable.Sequence()` terminal operations:** - -When followed by `.Count()`, `.Contains()`, `.ToArray()`, `.ToList()`, `.Skip()`, `.Take()`, `.ElementAt()`, `.Last()`. Specialized iterators compute results from arithmetic, not enumeration. - -```csharp -// ✅ ALLOWED — range check, no enumeration -bool has = Enumerable.Range(0, 1000).Contains(500); - -// ✅ ALLOWED — single allocation, span fill -int[] arr = Enumerable.Range(0, 100).ToArray(); -``` - -#### Tier 2 — Low Overhead (acceptable on warm paths, benchmark if critical) - -These patterns have some overhead but are significantly optimized in .NET 10. - -**`.Skip(n).Take(m).ToArray()` / `.ToList()` on `T[]` or `List`:** - -Uses vectorized `Span.CopyTo` (~5x faster than .NET 9). Still allocates the output array/list. - -```csharp -// ✅ Acceptable on warm paths — vectorized copy -var page = items.Skip(offset).Take(pageSize).ToArray(); -``` - -**`.LeftJoin()` / `.RightJoin()` (new in .NET 10):** - -~2x faster and ~2x less memory than the manual `GroupJoin`+`SelectMany`+`DefaultIfEmpty` pattern. - -```csharp -// ✅ Prefer over manual GroupJoin chain -var results = orders.LeftJoin(customers, o => o.CustomerId, c => c.Id, - (order, customer) => new { order, customer }); -``` - -**`.Where(predicate)` on `T[]` or `List`:** - -The `WhereIterator` still heap-allocates, but enumerating it is cheaper due to PGO. For true hot paths, manual `foreach`+`if` is still faster. - -```csharp -// ⚠️ Acceptable but not zero-cost — WhereIterator allocates -foreach (var item in items.Where(x => x.IsActive)) - Process(item); - -// 🏆 Faster manual alternative for true hot paths: -foreach (var item in items) - if (item.IsActive) Process(item); -``` - -#### Tier 3 — Still Forbidden on Hot Paths - -These patterns still carry meaningful abstraction overhead. Use manual code. - -| Pattern | Why it's still slow | Manual alternative | -|---|---|---| -| `.Select(f).Where(p)` (this order) | Each intermediate iterator allocates | `foreach` + `if` + inline transform | -| `.GroupBy(k)` | Builds dictionary internally | Manual dictionary loop | -| `.ToDictionary()` / `.ToHashSet()` | Always allocates the collection | Pre-size and fill manually | -| `.ToLookup()` | Always builds grouping structure | Manual dictionary of lists | -| `.Aggregate(f)` | Delegate overhead per element | Manual accumulator loop | -| `.Sum()` / `.Min()` / `.Max()` on `float`/`double` | No SIMD vectorization in LINQ (ARM) | `TensorPrimitives.Sum()` etc. | -| `.SelectMany(f)` (iterating results, not `.Contains()`) | Multiple enumerator allocations | Nested manual loops | -| `.Zip()` iterating | Enumerator allocations | Dual-index `for` loop | -| Any LINQ over `IAsyncEnumerable` | No PGO/escape analysis for async | Manual `await foreach` | -| Long chains: `.Where().Select().OrderBy().Take()` | Each step allocates an iterator | Manual loop with sort | - -```csharp -// ❌ STILL FORBIDDEN — allocates iterator + delegate per step -var targets = nearbyMobiles.Where(m => m.Alive).ToList(); -var count = items.Count(i => i.Stackable); // Count with predicate is NOT .Count() -var first = mobiles.FirstOrDefault(m => m is PlayerMobile); - -// ✅ CORRECT — zero allocations -using var targets = PooledRefList.Create(); -foreach (var m in nearbyMobiles) -{ - if (m.Alive) - targets.Add(m); -} - -// ✅ CORRECT — manual count -var count = 0; -foreach (var i in items) -{ - if (i.Stackable) - count++; -} -``` - -#### Quick Decision Flowchart - -``` -Is it .Contains() after another LINQ operator? - YES → ✅ Use it (see Tier 1 table) - -Is it foreach over IEnumerable backed by T[]/List/Stack/Queue? - YES → ✅ Use it (zero-alloc with PGO) - -Is it .OrderBy().First() or .OrderBy().Last()? - YES → ✅ Use it (O(N) not O(N log N)) - -Is it .Shuffle().Take(n)? - YES → ✅ Use it (reservoir sampling) - -Is it .Count() on a sized collection or Range? - YES → ✅ Use it (O(1)) - -Is it .Skip().Take().ToArray() on T[]/List? - YES → ✅ Acceptable (vectorized copy) - -Is it anything else on a hot path? - → ❌ Write manual code -``` - -*Reference: Stephen Toub, "Performance Improvements in .NET 10", September 2025. dotnet/runtime PRs: #112684, #108153, #111473, #116978, #112173, #118425.* - -### Array Pooling -Use `STArrayPool.Shared` instead of `ArrayPool.Shared` in game logic. STArrayPool is optimized for single-threaded access (no locks). - -```csharp -var buffer = STArrayPool.Shared.Rent(1024); -try -{ - // Use buffer... -} -finally -{ - STArrayPool.Shared.Return(buffer); -} -``` - -Reference: `Projects/Server/Buffers/STArrayPool.cs` - -### PooledRefList -For temporary lists in methods, use `PooledRefList` instead of `new List()`: - -```csharp -using var list = PooledRefList.Create(); -// list is stack-allocated, uses pooled backing array -list.Add(mobile); -// Automatically returns array to pool on Dispose -``` - -Reference: `Projects/Server/Collections/PooledRefList.cs` - -### Spatial Queries -Never iterate `World.Mobiles` or `World.Items` directly. Use map-based spatial queries: - -```csharp -// BAD - O(n) over ALL mobiles in the world -foreach (var m in World.Mobiles.Values) -{ - if (m.InRange(location, 10)) - DoSomething(m); -} - -// GOOD - O(1) sector lookup -foreach (var m in map.GetMobilesInRange(location, 10)) -{ - DoSomething(m); -} -``` - -Available spatial queries (on `Map`): -- `GetMobilesAt(Point3D p)` - exact location -- `GetMobilesInRange(Point3D p, int range)` - within range -- `GetMobilesInBounds(Rectangle2D bounds)` - within rectangle -- Same patterns for `GetItemsAt`, `GetItemsInRange`, `GetItemsInBounds` - ---- - -## Serialization Requirements - -### Partial Classes -Any class with `[SerializationGenerator]` **must** be declared `partial`: -```csharp -[SerializationGenerator(0)] -public partial class MyItem : Item // MUST be partial -{ -} -``` - -### Constructible Attribute -Items and Mobiles must have `[Constructible]` on their parameterless constructor: -```csharp -[Constructible] -public MyItem() : base(0x1234) -{ -} -``` - -### Timer Fields Are Not Serialized -`TimerExecutionToken` fields must NOT have `[SerializableField]`: -```csharp -// CORRECT -private TimerExecutionToken _timerToken; // No serialization attribute - -// WRONG -[SerializableField(2)] -private TimerExecutionToken _timerToken; // Will cause errors -``` - -Timers are restored in `[AfterDeserialization]` methods. - -See: `dev-docs/serialization.md` for complete serialization guide. - ---- - -## Logging - -Use structured logging via `ILogger`, never `Console.WriteLine`. - -### Setup -```csharp -using Server.Logging; - -public class MySystem -{ - private static readonly ILogger logger = LogFactory.GetLogger(typeof(MySystem)); -} -``` - -### Usage -```csharp -logger.Debug("Processing {Count} items for {Player}", items.Count, player.Name); -logger.Information("Player {Name} logged in from {IP}", name, ip); -logger.Warning("Unexpected state in {System}: {Details}", "Combat", details); -logger.Error(exception, "Failed to process {Action}", action); -logger.Fatal(exception, "Unrecoverable error in {System}", system); -``` - -### Levels -- `Debug` - Detailed diagnostic information -- `Information` - General operational events -- `Warning` - Unexpected but recoverable situations -- `Error` - Failures that affect specific operations -- `Fatal` - Unrecoverable errors - -Reference: `Projects/Logger/ILogger.cs` - ---- - -## Threading Model - -ModernUO uses a **single-threaded game loop**. All game logic runs on one thread. - -### Forbidden in Game Code -```csharp -// ALL of these are WRONG in game code: -Task.Run(() => ProcessItems()); -new Thread(BackgroundWork).Start(); -ThreadPool.QueueUserWorkItem(Work); -lock (_syncObj) { } -volatile int _counter; -ConcurrentDictionary _items; -``` - -### Why It Works -- `EventLoopContext` (SynchronizationContext) routes all `await` continuations to the main thread -- `await` is safe because it always resumes on the game thread -- No data races possible in single-threaded code - -### Exceptions -Only server infrastructure code may use threading: -- `Projects/Server/Main.cs` - Event loop setup -- World save disk I/O (serialization on main thread, writes may be background) -- Network I/O - -See: `dev-docs/threading-model.md` for complete threading documentation. - ---- - -## Memory Management - -### Array Returns -Always return pooled arrays: -```csharp -var arr = STArrayPool.Shared.Rent(size); -try -{ - // Use arr -} -finally -{ - STArrayPool.Shared.Return(arr); -} -``` - -### Avoid Allocations in Hot Paths -- Use `PooledRefList` instead of `new List()` -- Use `stackalloc` for small fixed-size buffers -- Use `STArrayPool` for larger buffers -- Use `ValueStringBuilder` with `stackalloc` for string building (never `System.Text.StringBuilder`) → `dev-docs/string-handling.md` - ---- - -## Entity Lifecycle - -### Two-Phase Deletion -Items and Mobiles use two deletion hooks: - -1. **`OnDelete()`** - Called first. Cancel timers, remove from tracking systems. - ```csharp - public override void OnDelete() - { - _timerToken.Cancel(); - base.OnDelete(); - } - ``` - -2. **`OnAfterDelete()`** - Called after entity is removed from world. Clean up references. - ```csharp - public override void OnAfterDelete() - { - _timer?.Stop(); - _timer = null; - _owner = null; - base.OnAfterDelete(); - } - ``` - -### Reference Cleanup -Any field holding an `Item` or `Mobile` reference should be nulled in deletion: -```csharp -public override void OnAfterDelete() -{ - _target = null; - _owner = null; - base.OnAfterDelete(); -} -``` - ---- - -## Era-Conditional Code - -### Always Ask for Target Era -If the user hasn't specified which expansion to target, **always ask**. Different eras have dramatically different mechanics. - -### Pattern -```csharp -if (Core.AOS) // Age of Shadows or later -{ - damage = GetNewAosDamage(10, 1, 4, target); -} -else // Pre-AOS -{ - damage = Utility.Random(4, 4); -} -``` - -### Available Checks -```csharp -Core.T2A // >= The Second Age -Core.UOR // >= Renaissance -Core.UOTD // >= Third Dawn -Core.LBR // >= Blackthorn's Revenge -Core.AOS // >= Age of Shadows -Core.SE // >= Samurai Empire -Core.ML // >= Mondain's Legacy -Core.SA // >= Stygian Abyss -Core.HS // >= High Seas -Core.TOL // >= Time of Legends -Core.EJ // >= Endless Journey -``` - -See: `dev-docs/era-expansion.md` for complete expansion guide. - ---- - -## File Organization - -### Directory Structure -``` -Projects/UOContent/ -├── Items/ -│ ├── Weapons/ # BaseWeapon, Swords/, Maces/, etc. -│ ├── Armor/ # BaseArmor, Plate/, Chain/, etc. -│ ├── Clothing/ # Shirts, hats, etc. -│ ├── Misc/ # General items -│ └── Special/ # Unique/quest items -├── Mobiles/ -│ ├── Animals/ # Bears/, Birds/, etc. -│ ├── Monsters/ # AOS/, SE/, ML/ by era -│ ├── Special/ # Champions, bosses -│ └── Vendors/ # NPC vendors -├── Spells/ -│ ├── Base/ # Spell base classes -│ ├── First/ - Eighth/ # Magery circles -│ ├── Necromancy/ # Necro spells -│ └── Spellweaving/ # Spellweaving -├── Skills/ # Skill implementations -├── Gumps/ # UI dialogs -│ └── Base/ # Gump base classes -└── Engines/ # Complex systems -``` - -### File Naming -- One class per file (generally) -- File name matches class name -- Group related items in subdirectories - ---- - -## Quick Reference: Common Anti-Patterns - -| Anti-Pattern | Correct Pattern | -|---|---| -| `list.Where(x => x.Alive)` | `foreach` + `if` (Tier 2 — acceptable on warm paths) | -| `.GroupBy()` / `.ToDictionary()` / `.ToHashSet()` | Manual dictionary loop (Tier 3 — still forbidden) | -| `.Select(f).Where(p)` chain | `foreach` + `if` + inline transform (Tier 3) | -| `.OrderBy().First()` | ✅ Allowed — O(N) scan, no sort (Tier 1) | -| `.Distinct().Contains()` | ✅ Allowed — no HashSet built (Tier 1) | -| `Console.WriteLine(msg)` | `logger.Information(msg)` | -| `new List()` in hot path | `PooledRefList.Create()` | -| `ArrayPool.Shared` | `STArrayPool.Shared` | -| `ConcurrentDictionary` | `Dictionary` | -| `Task.Run(...)` | Don't. Use timers. | -| `World.Mobiles.Values` iteration | `map.GetMobilesInRange()` | -| Missing `partial` on serialized class | Add `partial` keyword | -| Serializing `TimerExecutionToken` | Leave unserialized, restore in `[AfterDeserialization]` | diff --git a/dev-docs/commands-targeting.md b/dev-docs/commands-targeting.md deleted file mode 100644 index d4e96c793..000000000 --- a/dev-docs/commands-targeting.md +++ /dev/null @@ -1,341 +0,0 @@ -# ModernUO Commands & Targeting - -This document covers ModernUO's command system for in-game `[` commands and the targeting system for player interactions. - -## Command System - -### Overview - -Commands are prefixed with `[` by default (e.g., `[MyCommand`). They are registered in static `Configure()` methods and associated with an access level. - -### Registration - -```csharp -using Server.Commands; - -namespace Server.Custom; - -public static class MyCommands -{ - public static void Configure() - { - CommandSystem.Register("MyCommand", AccessLevel.GameMaster, MyCommand_OnCommand); - } - - [Usage("MyCommand [count]")] - [Description("Does something with a name and optional count")] - [Aliases("mc", "mycmd")] - public static void MyCommand_OnCommand(CommandEventArgs e) - { - var from = e.Mobile; - - if (e.Length < 1) - { - from.SendMessage("Usage: [MyCommand [count]"); - return; - } - - var name = e.GetString(0); - var count = e.Length > 1 ? e.GetInt32(1) : 1; - - from.SendMessage($"Processing {name} x{count}"); - } -} -``` - -### CommandSystem API - -```csharp -public static class CommandSystem -{ - public static string Prefix { get; set; } = "["; - public static Dictionary Entries { get; } - - public static void Register(string command, AccessLevel access, CommandEventHandler handler); - public static bool Handle(Mobile from, string text, MessageType type = MessageType.Regular); - public static string[] Split(string value); -} -``` - -### CommandEventArgs - -```csharp -public class CommandEventArgs -{ - public Mobile Mobile { get; } // Who issued the command - public string Command { get; } // Command name - public string ArgString { get; } // Raw argument string - public string[] Arguments { get; } // Split arguments - public int Length { get; } // Argument count - - // Typed accessors (return default if index out of range) - public string GetString(int index); // "" if missing - public int GetInt32(int index); // 0 if missing - public uint GetUInt32(int index); // 0 if missing - public bool GetBoolean(int index); // false if missing - public double GetDouble(int index); // 0.0 if missing - public TimeSpan GetTimeSpan(int index); // TimeSpan.Zero if missing -} -``` - -### Access Levels - -```csharp -public enum AccessLevel -{ - Player, // 0 - Regular players - Counselor, // 1 - Support staff - GameMaster, // 2 - Game Masters - Seer, // 3 - Event coordinators - Administrator, // 4 - Server administrators - Developer, // 5 - Developers - Owner // 6 - Server owner -} -``` - -### Command Attributes - -```csharp -[Usage("CommandName [optional]")] -// Documents command syntax. Displayed in help listings. - -[Description("What this command does")] -// Documents command purpose. Displayed in help listings. - -[Aliases("alias1", "alias2")] -// Alternative names for the command. -``` - -Defined in `Projects/Server/Attributes.cs`. - -### Command + Targeting Pattern - -A common pattern: command starts targeting, target handler performs the action. - -```csharp -public static class HealCommand -{ - public static void Configure() - { - CommandSystem.Register("Heal", AccessLevel.GameMaster, Heal_OnCommand); - } - - [Usage("Heal")] - [Description("Fully heals a targeted mobile")] - public static void Heal_OnCommand(CommandEventArgs e) - { - e.Mobile.SendMessage("Select a mobile to heal."); - e.Mobile.Target = new HealTarget(); - } - - private class HealTarget : Target - { - public HealTarget() : base(-1, false, TargetFlags.Beneficial) { } - - protected override void OnTarget(Mobile from, object targeted) - { - if (targeted is Mobile m) - { - m.Hits = m.HitsMax; - m.Mana = m.ManaMax; - m.Stam = m.StamMax; - m.Poison = null; - from.SendMessage($"You have healed {m.Name}."); - } - else - { - from.SendMessage("That is not a mobile."); - } - } - } -} -``` - ---- - -## Targeting System - -### Overview - -The targeting system allows players to select objects in the game world. When a target is set on a mobile, the client shows a targeting cursor. The player clicks on something, and the server processes the selection. - -### Target Base Class - -```csharp -public abstract class Target -{ - // Constructor - protected Target( - int range, // Max range (-1 for unlimited) - bool allowGround, // Can target ground tiles - TargetFlags flags // None, Harmful, Beneficial - ); - - // Properties - public int Range { get; set; } - public bool AllowGround { get; set; } - public TargetFlags Flags { get; set; } - public bool CheckLOS { get; set; } // Default: true - public bool DisallowMultis { get; set; } // Default: false - public bool AllowNonlocal { get; set; } // Default: false - public int TargetID { get; } - - // Override for main handling - protected virtual void OnTarget(Mobile from, object targeted); - - // Override for error handling - protected virtual void OnTargetCancel(Mobile from, TargetCancelType cancelType); - protected virtual void OnTargetFinish(Mobile from); - protected virtual void OnTargetOutOfRange(Mobile from, object targeted); - protected virtual void OnTargetOutOfLOS(Mobile from, object targeted); - protected virtual void OnTargetNotAccessible(Mobile from, object targeted); - protected virtual void OnTargetDeleted(Mobile from, object targeted); - protected virtual void OnTargetUntargetable(Mobile from, object targeted); - protected virtual void OnNonlocalTarget(Mobile from, object targeted); - protected virtual void OnCantSeeTarget(Mobile from, object targeted); - protected virtual void OnTargetInSecureTrade(Mobile from, object targeted); - - // Validation overrides - protected virtual bool CanTarget(Mobile from, Mobile mobile, ref Point3D loc, ref Map map); - protected virtual bool CanTarget(Mobile from, Item item, ref Point3D loc, ref Map map); - protected virtual bool CanTarget(Mobile from, LandTarget land, ref Point3D loc, ref Map map); - protected virtual bool CanTarget(Mobile from, StaticTarget st, ref Point3D loc, ref Map map); - - // Timeout - public void BeginTimeout(Mobile from, long delay); - public void CancelTimeout(); -} -``` - -### TargetFlags - -```csharp -[Flags] -public enum TargetFlags : byte -{ - None = 0x00, // Neutral targeting - Harmful = 0x01, // Triggers criminal check, PvP flag - Beneficial = 0x02 // Healing, buffing -} -``` - -### TargetCancelType - -```csharp -public enum TargetCancelType -{ - Overridden, // New target replaced this one - Canceled, // Player pressed Escape - Disconnected, // Player disconnected - Timeout // Target timed out -} -``` - -### Target Object Types - -When `OnTarget` is called, the `targeted` parameter can be: - -| Type | Description | Key Properties | -|---|---|---| -| `Mobile` | A player or creature | `.Name`, `.Hits`, `.Location` | -| `Item` | An item | `.Name`, `.ItemID`, `.Location` | -| `LandTarget` | Ground tile | `.Location`, `.TileID`, `.Name` | -| `StaticTarget` | Static map object | `.Location`, `.ItemID`, `.Hue` | - -### Setting a Target - -```csharp -// Set target on mobile (shows targeting cursor) -mobile.Target = new MyTarget(); - -// Cancel current target -Target.Cancel(mobile); -``` - -### Basic Target Implementation - -```csharp -private class IdentifyTarget : Target -{ - public IdentifyTarget() : base(12, false, TargetFlags.None) - { - // CheckLOS = false; // Uncomment to skip line-of-sight - } - - protected override void OnTarget(Mobile from, object targeted) - { - switch (targeted) - { - case Mobile m: - from.SendMessage($"Mobile: {m.Name}, Hits: {m.Hits}/{m.HitsMax}"); - break; - case Item item: - from.SendMessage($"Item: {item.Name ?? item.DefaultName}, ID: 0x{item.ItemID:X}"); - break; - case LandTarget land: - from.SendMessage($"Land at {land.Location}, Tile: {land.TileID}"); - break; - case StaticTarget st: - from.SendMessage($"Static at {st.Location}, ID: 0x{st.ItemID:X}"); - break; - } - } - - protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType) - { - if (cancelType == TargetCancelType.Canceled) - from.SendMessage("Targeting cancelled."); - } - - protected override void OnTargetFinish(Mobile from) - { - // Always called after success or cancel - } -} -``` - -### Target Validation Flow - -1. Client sends target response -2. Server validates: same map, within range, line of sight -3. Calls `CanTarget()` override for type-specific validation -4. If valid: calls `OnTarget()` -5. If invalid: calls appropriate error handler -6. Always calls `OnTargetFinish()` at the end - -### SpellTarget - -For spells, use the built-in `SpellTarget`: - -```csharp -public override void OnCast() -{ - Caster.Target = new SpellTarget(this, TargetFlags.Harmful); -} - -// The spell's Target(Mobile m) method is called when the player targets -``` - ---- - -## Best Practices - -1. **Register commands in `Configure()`** -- it's called automatically during startup -2. **Validate argument count** before accessing -- `GetInt32()` returns 0 for missing args (not an error) -3. **Use appropriate access levels** -- don't give players GM commands -4. **Use `TargetFlags.Harmful`** for offensive actions (triggers criminal flagging) -5. **Use `TargetFlags.Beneficial`** for healing/buffing -6. **Handle `OnTargetCancel`** to provide feedback when player cancels -7. **Clean up in `OnTargetFinish`** if you have state to release - -## Key File References - -| File | Description | -|---|---| -| `Projects/Server/Commands.cs` | CommandSystem, CommandEventArgs | -| `Projects/Server/Attributes.cs` | Usage, Description, Aliases | -| `Projects/Server/Targeting/Target.cs` | Target base class | -| `Projects/Server/Targeting/TargetFlags.cs` | TargetFlags enum | -| `Projects/Server/Targeting/TargetCancelType.cs` | Cancel types | -| `Projects/Server/Targeting/LandTarget.cs` | Land target | -| `Projects/Server/Targeting/StaticTarget.cs` | Static target | diff --git a/dev-docs/configuration.md b/dev-docs/configuration.md deleted file mode 100644 index b69349451..000000000 --- a/dev-docs/configuration.md +++ /dev/null @@ -1,283 +0,0 @@ -# ModernUO Configuration System - -This document covers ModernUO's configuration system, including ServerConfiguration for global settings, JsonConfig for custom config files, and best practices. - -## Overview - -ModernUO has two configuration mechanisms: -1. **ServerConfiguration**: Global key-value settings stored in `modernuo.json` -2. **JsonConfig**: Custom JSON configuration files for complex data structures - -## ServerConfiguration - -Defined in `Projects/Server/Configuration/ServerConfiguration.cs`. - -### Reading Settings - -#### GetSetting (Read-Only) - -Returns the configured value or the default. Does NOT write the default to the config file. - -```csharp -int statMax = ServerConfiguration.GetSetting("stats.statMax", 100); -bool enabled = ServerConfiguration.GetSetting("mySystem.enabled", true); -TimeSpan delay = ServerConfiguration.GetSetting("autosave.saveDelay", TimeSpan.FromMinutes(5)); -double rate = ServerConfiguration.GetSetting("stats.gainChanceMultiplier", 1.0); -Expansion exp = ServerConfiguration.GetSetting("core.expansion", Expansion.ML); -``` - -Supported types: -- `int` -- `bool` -- `double` -- `TimeSpan` -- `T where T : struct, Enum` - -#### GetOrUpdateSetting (Read-Write) - -Returns the configured value. If the key doesn't exist, writes the default to the config file and returns it. - -```csharp -int maxAccounts = ServerConfiguration.GetOrUpdateSetting("accountHandler.maxAccountsPerIP", 1); -bool autoCreate = ServerConfiguration.GetOrUpdateSetting("accountHandler.enableAutoAccountCreation", true); -int poolSize = ServerConfiguration.GetOrUpdateSetting("timer.initialPoolCapacity", 1024); -``` - -Use this when you want new settings to appear in `modernuo.json` automatically with sensible defaults. - -#### SetSetting - -Directly sets a value and immediately persists to disk: - -```csharp -ServerConfiguration.SetSetting("mySystem.enabled", "true"); -ServerConfiguration.SetSetting("mySystem.maxItems", "100"); -``` - -### Configuration Pattern - -Read settings in your `Configure()` static method: - -```csharp -namespace Server.Custom; - -public static class MySystem -{ - private static bool _enabled; - private static int _maxItems; - private static TimeSpan _cooldown; - - public static void Configure() - { - _enabled = ServerConfiguration.GetOrUpdateSetting("mySystem.enabled", true); - _maxItems = ServerConfiguration.GetOrUpdateSetting("mySystem.maxItems", 100); - _cooldown = ServerConfiguration.GetOrUpdateSetting("mySystem.cooldown", TimeSpan.FromMinutes(5)); - } - - public static void Initialize() - { - if (!_enabled) - return; - - // System initialization that depends on config values - } -} -``` - -### Key Naming Convention - -Use dot-separated hierarchical keys: - -``` -systemName.settingName -systemName.subSystem.settingName -``` - -Examples from the codebase: -``` -accountHandler.enableAutoAccountCreation -accountHandler.enablePlayerPasswordCommand -accountHandler.maxAccountsPerIP -accountSecurity.encryptionAlgorithm -autosave.enabled -autosave.saveDelay -world.savePath -world.useMultithreadedSaves -movement.delay.walkFoot -movement.delay.runFoot -stats.statMax -stats.gainChanceMultiplier -stats.primaryStatGainChance -stats.gainDelay -stats.petGainDelay -stats.usePub45StatGain -timer.initialPoolCapacity -timer.maxPoolCapacity -core.enableIdleCPU -``` - -### modernuo.json Structure - -Located at `Distribution/Configuration/modernuo.json`: - -```json -{ - "assemblyDirectories": ["./Assemblies"], - "dataDirectories": ["C:\\Ultima Online Classic"], - "listeners": ["0.0.0.0:2593"], - "settings": { - "accountHandler.enableAutoAccountCreation": "True", - "accountHandler.maxAccountsPerIP": "1", - "autosave.enabled": "True", - "autosave.saveDelay": "00:05:00", - "world.savePath": "Saves", - "stats.statMax": "100" - } -} -``` - -Key points: -- All settings are stored as strings in the `settings` dictionary -- Top-level fields (`assemblyDirectories`, `dataDirectories`, `listeners`) are structural -- `GetOrUpdateSetting` adds new entries to `settings` automatically - ---- - -## JsonConfig - -For complex configuration that doesn't fit in flat key-value pairs, use `JsonConfig`. - -Defined in `Projects/Server/Json/JsonConfig.cs`. - -### API - -```csharp -// Deserialize from file (returns default if file doesn't exist) -T config = JsonConfig.Deserialize(filePath); -T config = JsonConfig.Deserialize(filePath, customOptions); - -// Serialize to file (creates directory if needed) -JsonConfig.Serialize(filePath, config); -JsonConfig.Serialize(filePath, config, customOptions); - -// Default options (available for customization) -JsonSerializerOptions options = JsonConfig.DefaultOptions; -``` - -### Default JSON Options - -```csharp -WriteIndented = true // Pretty-printed -AllowTrailingCommas = true // Forgiving parser -ReadCommentHandling = JsonCommentHandling.Skip // Comments allowed -DefaultIgnoreCondition = WhenWritingNull // Null values omitted -Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping -``` - -### Built-in Converters - -JsonConfig includes converters for ModernUO types: -- `ClientVersion` -- `Guid` -- `Map` -- `Point3D` -- `Rectangle3D` -- `TimeSpan` -- `IPEndPoint` -- `Type` -- `WorldLocation` -- `TextDefinition` -- All enums (as strings via `JsonStringEnumConverter`) - -### Custom Config File Pattern - -```csharp -using Server.Json; - -namespace Server.Custom; - -public class MySystemConfig -{ - public bool Enabled { get; set; } = true; - public int MaxItems { get; set; } = 100; - public TimeSpan Cooldown { get; set; } = TimeSpan.FromMinutes(5); - public List BlockedNames { get; set; } = new(); - public Dictionary Scores { get; set; } = new(); -} - -public static class MySystem -{ - private static MySystemConfig _config; - private static readonly string ConfigPath = - Path.Combine(Core.BaseDirectory, "Configuration/MySystem/config.json"); - - public static void Configure() - { - _config = JsonConfig.Deserialize(ConfigPath); - - if (_config == null) - { - _config = new MySystemConfig(); - JsonConfig.Serialize(ConfigPath, _config); - } - } - - public static void SaveConfig() - { - JsonConfig.Serialize(ConfigPath, _config); - } -} -``` - -This creates a config file like: -```json -{ - "Enabled": true, - "MaxItems": 100, - "Cooldown": "00:05:00", - "BlockedNames": [], - "Scores": {} -} -``` - -### Custom Converters - -Add custom converters via `JsonConfig.GetOptions()`: - -```csharp -var options = JsonConfig.GetOptions(new MyCustomConverterFactory()); -var data = JsonConfig.Deserialize(path, options); -``` - ---- - -## Configuration File Locations - -| File | Purpose | -|---|---| -| `Distribution/Configuration/modernuo.json` | Main server settings | -| `Distribution/Configuration/expansion.json` | Target expansion | -| `Distribution/Data/expansions.json` | Expansion metadata | -| `Distribution/Configuration/` | Custom config directory | - -Custom config files should be placed under `Distribution/Configuration/` in a subdirectory named after your system. - -## Best Practices - -1. **Read in `Configure()`** -- called before `Initialize()`, ensures settings are available early -2. **Use `GetOrUpdateSetting`** for new features -- ensures defaults appear in config file -3. **Use `GetSetting`** for optional/advanced settings -- doesn't clutter config file -4. **Use JsonConfig for complex data** -- lists, dictionaries, nested objects -5. **Provide sensible defaults** -- system should work without manual configuration -6. **Use era-aware defaults** -- `Core.LBR ? 125 : 100` for values that vary by expansion -7. **Document key names** -- use clear hierarchical naming - -## Key File References - -| File | Description | -|---|---| -| `Projects/Server/Configuration/ServerConfiguration.cs` | ServerConfiguration class | -| `Projects/Server/Json/JsonConfig.cs` | JsonConfig utility | -| `Distribution/Configuration/modernuo.json` | Main config file | -| `Projects/UOContent/Skills/SkillCheck.cs` | Config usage example | -| `Projects/Server/Timer/Timer.Pool.cs` | Config usage example | diff --git a/dev-docs/content-patterns.md b/dev-docs/content-patterns.md deleted file mode 100644 index ec56e1bb3..000000000 --- a/dev-docs/content-patterns.md +++ /dev/null @@ -1,586 +0,0 @@ -# ModernUO Content Creation Patterns - -This document covers the patterns and templates for creating game content in ModernUO: items, creatures, spells, skills, loot, context menus, and file organization. - -## Table of Contents -1. [New Item](#new-item) -2. [New Creature](#new-creature) -3. [New Spell](#new-spell) -4. [Skill Implementation](#skill-implementation) -5. [Loot System](#loot-system) -6. [Context Menus](#context-menus) -7. [Entity Lifecycle](#entity-lifecycle) -8. [File Organization](#file-organization) - ---- - -## New Item - -### Minimal Item -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class SimpleItem : Item -{ - [Constructible] - public SimpleItem() : base(0x1234) // itemID from UO art - { - Weight = 1.0; - } - - public override string DefaultName => "a simple item"; - // OR: public override int LabelNumber => 1234567; // cliloc number -} -``` - -### Item with Properties and Behavior -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class MagicLantern : Item -{ - [SerializableField(0)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _charges; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Mobile _owner; - - private TimerExecutionToken _glowTimer; - - [Constructible] - public MagicLantern() : base(0xA25) - { - _charges = Utility.RandomMinMax(5, 15); - Weight = 2.0; - Light = LightType.Circle300; - StartGlow(); - } - - public override string DefaultName => "a magic lantern"; - - private void StartGlow() - { - Timer.StartTimer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), Glow, out _glowTimer); - } - - [AfterDeserialization] - private void AfterDeserialization() => StartGlow(); - - public override void OnAfterDelete() - { - _glowTimer.Cancel(); - base.OnAfterDelete(); - } - - private void Glow() - { - if (_charges > 0) - Effects.SendLocationParticles(this, 0x376A, 9, 10, 5042); - } - - public override void OnDoubleClick(Mobile from) - { - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); // Must be in your backpack - return; - } - - if (_charges <= 0) - { - from.SendMessage("The lantern is depleted."); - return; - } - - Charges--; - from.SendMessage("The lantern flares brightly!"); - from.FixedParticles(0x376A, 9, 32, 5042, EffectLayer.Waist); - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - list.Add(1060741, $"{_charges}"); // "charges: ~1_val~" - } -} -``` - -### Common Item Base Classes -| Base Class | Use For | -|---|---| -| `Item` | Generic items | -| `BaseWeapon` | Melee weapons | -| `BaseRanged` | Ranged weapons (bows, crossbows) | -| `BaseArmor` | Armor pieces | -| `BaseShield` | Shields | -| `BaseClothing` | Wearable clothing | -| `BaseJewel` | Rings, bracelets, necklaces | -| `BaseContainer` | Containers (bags, boxes) | -| `BasePotion` | Potions | -| `BaseReagent` | Spell reagents | -| `Food` | Edible items | -| `SpellScroll` | Spell scrolls | - -### Key Item Properties -```csharp -Weight = 1.0; // Item weight in stones -Stackable = true; // Can stack with same type -Amount = 1; // Stack amount -Movable = true; // Can be picked up -Visible = true; // Visible to players -Hue = 0; // Color hue (0 = default) -Light = LightType.Circle300; // Light emission -LootType = LootType.Regular; // Regular, Newbied, Blessed, Cursed -Layer = Layer.OneHanded; // Equipment layer -``` - ---- - -## New Creature - -### Basic Creature -```csharp -using ModernUO.Serialization; -using Server.Items; - -namespace Server.Mobiles; - -[SerializationGenerator(0)] -public partial class ForestWolf : BaseCreature -{ - [Constructible] - public ForestWolf() : base(AIType.AI_Melee, FightMode.Closest) - { - Body = 225; // Wolf body graphic - BaseSoundID = 0xE5; // Base sound ID - - SetStr(80, 120); - SetDex(90, 110); - SetInt(20, 40); - - SetHits(60, 80); - SetMana(0); - - SetDamage(8, 14); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 25, 35); - SetResistance(ResistanceType.Fire, 5, 10); - SetResistance(ResistanceType.Cold, 15, 25); - SetResistance(ResistanceType.Poison, 10, 15); - SetResistance(ResistanceType.Energy, 5, 10); - - SetSkill(SkillName.MagicResist, 30.0, 50.0); - SetSkill(SkillName.Tactics, 50.0, 70.0); - SetSkill(SkillName.Wrestling, 50.0, 70.0); - - Fame = 600; - Karma = 0; - - VirtualArmor = 28; - - Tamable = true; - ControlSlots = 1; - MinTameSkill = 50.1; - } - - public override string CorpseName => "a wolf corpse"; - public override string DefaultName => "a forest wolf"; - public override int Meat => 1; - public override int Hides => 6; - public override HideType HideType => HideType.Regular; - public override FoodType FavoriteFood => FoodType.Meat; - public override PackInstinct PackInstinct => PackInstinct.Canine; - - public override void GenerateLoot() - { - AddLoot(LootPack.Meager); - } -} -``` - -### AI Types -| AIType | Use For | -|---|---| -| `AI_Melee` | Warriors, melee fighters | -| `AI_Mage` | Spellcasters | -| `AI_Archer` | Ranged attackers | -| `AI_Animal` | Passive animals (flee/fight back) | -| `AI_Predator` | Hunting animals | -| `AI_Healer` | Healing NPCs | -| `AI_Vendor` | Shop NPCs | -| `AI_Berserk` | Mindless aggressors | -| `AI_Thief` | Pickpockets | - -### Fight Modes -| FightMode | Behavior | -|---|---| -| `None` | Never attacks | -| `Aggressor` | Only retaliates | -| `Strongest` | Targets highest-stat enemy | -| `Weakest` | Targets lowest-stat enemy | -| `Closest` | Targets nearest enemy | -| `Evil` | Attacks aggressors or evil-karma targets | - -### Creature Stats Guide -| Creature Level | Str | Dex | Int | Hits | Damage | Fame | -|---|---|---|---|---|---|---| -| Weak | 30-60 | 30-50 | 10-20 | 20-40 | 2-6 | 100-300 | -| Average | 80-120 | 60-90 | 20-40 | 60-100 | 6-14 | 500-1500 | -| Strong | 150-250 | 80-120 | 50-100 | 120-200 | 12-22 | 2000-5000 | -| Elite | 300-500 | 100-150 | 100-200 | 250-500 | 18-30 | 5000-15000 | -| Boss | 500-1000 | 150-250 | 200-400 | 500-2000 | 25-40 | 15000+ | - -### Optional Creature Overrides -```csharp -public override Poison PoisonImmune => Poison.Regular; // Poison immunity -public override Poison HitPoison => Poison.Lesser; // Melee poison -public override double HitPoisonChance => 0.2; // 20% poison chance -public override bool CanRummageCorpses => true; // Loots corpses -public override bool BardImmune => true; // Cannot be provoked/peaced -public override bool Unprovokable => true; // Cannot be provoked -public override bool CanFly => true; // Can fly -public override int TreasureMapLevel => 3; // Drops treasure map -public override double WeaponAbilityChance => 0.4; // Weapon ability chance -``` - -### Creature Speeds (think vs move clocks) - -All "speed" values are **delays in seconds** (smaller = faster). A creature runs two clocks: - -- **Think clock** — `ActiveSpeed`/`PassiveSpeed`/`CurrentSpeed`: seconds per AI decision - (combat decisions, target acquisition, spell timing). -- **Move clock** — `ActiveMoveSpeed`/`PassiveMoveSpeed`/`CurrentMoveSpeed`: seconds per - step. Inherits the matching think value until overridden, so a creature configured with - only think speeds behaves as one clock. Any value is legal — steps are scheduled - independently of think ticks, so the two need not divide evenly. - -Speeds normally come from `Distribution/Data/npc-speeds.json` (via `SpeedClass` or type -lists); `activeMove`/`passiveMove` are optional per bucket. Prefer data over code: - -```csharp -public override SpeedLevel SpeedClass => SpeedLevel.Slow; // bucket in npc-speeds.json -``` - -Code-level overrides for special cases: - -```csharp -SetSpeed(0.5, 2.0); // think clock; ALSO clears move overrides (one-clock legacy semantics) -SetMoveSpeed(0.45, 0.9); // move clock only — call after SetSpeed if both are wanted -ClearMoveSpeed(); // back to inheriting the think clock -``` - -All four are `[props`-tunable per instance (move values: set `0` to re-inherit); per-instance -move overrides serialize. Being badly hurt slows steps, never decisions (RunUO parity). - ---- - -## New Spell - -### Targeted Damage Spell (Magery) -```csharp -using System; -using Server.Targeting; - -namespace Server.Spells.Third; - -public class FireballSpellCustom : MagerySpell, ITargetingSpell -{ - private static readonly SpellInfo _info = new( - "Fireball", // Name - "Vas Flam", // Mantra - 212, // Cast animation - 9041, // Cast sound - Reagent.BlackPearl // Reagents (comma-separated) - ); - - public FireballSpellCustom(Mobile caster, Item scroll = null) : base(caster, scroll, _info) { } - - public override SpellCircle Circle => SpellCircle.Third; - public override bool DelayedDamage => true; - - public void Target(Mobile m) - { - if (CheckHSequence(m)) // Harmful spell sequence check - { - var source = Caster; - SpellHelper.Turn(source, m); - SpellHelper.CheckReflect((int)Circle, ref source, ref m); - - double damage; - if (Core.AOS) - { - damage = GetNewAosDamage(19, 1, 5, m); - } - else - { - damage = Utility.Random(10, 7); - if (CheckResisted(m)) - { - damage *= 0.75; - m.SendLocalizedMessage(501783); // You resist - } - damage *= GetDamageScalar(m); - } - - source.MovingParticles(m, 0x36D4, 7, 0, false, true, 9502, 4019, 0x160); - source.PlaySound(0x15E); - - // Damage types must sum to 100 - SpellHelper.Damage(this, m, damage, 0, 100, 0, 0, 0); - // phys fire cold pois energy - } - } - - public override void OnCast() - { - Caster.Target = new SpellTarget(this, TargetFlags.Harmful); - } -} -``` - -### Spell Helper Methods -```csharp -SpellHelper.Turn(caster, target); // Face target -SpellHelper.CheckReflect(circle, ref source, ref target); // Magic reflect -SpellHelper.Damage(spell, target, damage, phys, fire, cold, poison, energy); -SpellHelper.AddStatCurse(caster, target, stat); -SpellHelper.AddStatBonus(caster, target, stat); -SpellHelper.CanRevealCaster(spell); - -CheckHSequence(target); // Harmful spell checks (LOS, range, criminal) -CheckBSequence(target); // Beneficial spell checks -CheckResisted(target); // Resistance check -GetNewAosDamage(bonus, dice, sides, target); // AOS damage formula -GetDamageScalar(target); // Pre-AOS damage multiplier -``` - -### Spell Circles (Magery) -| Circle | Mana | Base Delay | -|---|---|---| -| First | 4 | 0.25s + circle | -| Second | 6 | 0.50s + circle | -| Third | 9 | 0.75s + circle | -| Fourth | 11 | 1.00s + circle | -| Fifth | 14 | 1.25s + circle | -| Sixth | 20 | 1.50s + circle | -| Seventh | 40 | 1.75s + circle | -| Eighth | 50 | 2.00s + circle | - ---- - -## Skill Implementation - -### Registering a Skill Handler -```csharp -namespace Server.SkillHandlers; - -public static class MySkillHandler -{ - public static void Initialize() - { - SkillInfo.Table[(int)SkillName.Tracking].Callback = OnUse; - } - - public static TimeSpan OnUse(Mobile from) - { - from.SendMessage("You begin tracking..."); - from.Target = new TrackingTarget(); - return TimeSpan.FromSeconds(10.0); // Cooldown - } -} -``` - -### Skill Check -```csharp -// Difficulty-based check (with skill gain chance) -if (from.CheckSkill(SkillName.Mining, 0.0, 100.0)) -{ - // Success -} - -// Direct chance check -if (from.CheckSkill(SkillName.Hiding, minSkill: 25.0, maxSkill: 75.0)) -{ - // Success -} -``` - -### SkillName Enum (58 skills) -Key skills: `Alchemy`, `Anatomy`, `AnimalLore`, `AnimalTaming`, `Archery`, `ArmsLore`, `Begging`, `Blacksmith`, `Bushido`, `Camping`, `Carpentry`, `Cartography`, `Chivalry`, `Cooking`, `DetectHidden`, `Discordance`, `EvalInt`, `Fencing`, `Fishing`, `Fletching`, `Focus`, `Forensics`, `Healing`, `Herding`, `Hiding`, `Inscribe`, `ItemID`, `Lockpicking`, `Lumberjacking`, `Macing`, `Magery`, `MagicResist`, `Meditation`, `Mining`, `Musicianship`, `Necromancy`, `Ninjitsu`, `Parry`, `Peacemaking`, `Poisoning`, `Provocation`, `RemoveTrap`, `Snooping`, `Spellweaving`, `SpiritSpeak`, `Stealing`, `Stealth`, `Swords`, `Tactics`, `Tailoring`, `TasteID`, `Tinkering`, `Tracking`, `Veterinary`, `Wrestling` - ---- - -## Loot System - -### Using Predefined Packs -```csharp -public override void GenerateLoot() -{ - AddLoot(LootPack.Poor); // ~50g equivalent - AddLoot(LootPack.Meager); // ~100g equivalent - AddLoot(LootPack.Average); // ~250g equivalent - AddLoot(LootPack.Rich); // ~500g equivalent - AddLoot(LootPack.FilthyRich); // ~1000g equivalent - AddLoot(LootPack.UltraRich); // ~2000g equivalent - AddLoot(LootPack.SuperBoss); // Boss-level loot - - // Auxiliary packs - AddLoot(LootPack.Gems, 2); // 2 random gems - AddLoot(LootPack.Potions); // Random potion - AddLoot(LootPack.LowScrolls); // Low circle scroll - AddLoot(LootPack.MedScrolls); // Med circle scroll - AddLoot(LootPack.HighScrolls); // High circle scroll -} -``` - -Packs auto-select era-appropriate loot (Pre-AOS, AOS, SE variants). - -### Specific Items -```csharp -PackItem(new Arrow(Utility.RandomMinMax(20, 40))); -PackGold(100, 200); -PackItem(new Bandage(Utility.RandomMinMax(5, 10))); -``` - ---- - -## Context Menus - -```csharp -public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) -{ - base.GetContextMenuEntries(from, ref list); - - if (from.Alive && from.InRange(this, 2)) - { - list.Add(new RepairEntry(this)); - } -} - -private class RepairEntry : ContextMenuEntry -{ - private readonly Item _item; - - public RepairEntry(Item item) : base(6100) // Cliloc number - { - _item = item; - Enabled = item is { Deleted: false }; - } - - public override void OnClick(Mobile from, IEntity target) - { - if (_item.Deleted || !from.InRange(_item, 2)) - return; - - from.SendMessage("You repair the item."); - } -} -``` - ---- - -## Entity Lifecycle - -### Two-Phase Deletion -```csharp -// Phase 1: Pre-removal cleanup -public override void OnDelete() -{ - _timerToken.Cancel(); // Cancel managed timers - // Remove from tracking systems - base.OnDelete(); -} - -// Phase 2: Post-removal cleanup -public override void OnAfterDelete() -{ - _timer?.Stop(); // Stop Timer references - _timer = null; - _owner = null; // Null Item/Mobile refs - base.OnAfterDelete(); -} -``` - -### OnDoubleClick Validation -```csharp -public override void OnDoubleClick(Mobile from) -{ - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); // Must be in backpack - return; - } - - if (!from.InRange(GetWorldLocation(), 2)) - { - from.SendLocalizedMessage(500446); // Too far away - return; - } - - // Item logic here -} -``` - ---- - -## File Organization - -``` -Projects/UOContent/ -├── Items/ -│ ├── Weapons/Swords/ # Swords -│ ├── Weapons/Maces/ # Maces -│ ├── Weapons/Ranged/ # Bows, crossbows -│ ├── Armor/Plate/ # Plate armor -│ ├── Armor/Chain/ # Chain armor -│ ├── Armor/Leather/ # Leather armor -│ ├── Clothing/ # Wearable clothing -│ ├── Containers/ # Bags, boxes -│ ├── Misc/ # General items -│ ├── Special/ # Unique/quest items -│ └── Resources/ # Crafting materials -├── Mobiles/ -│ ├── Animals/Bears/ # Bears (BlackBear, GrizzlyBear) -│ ├── Animals/Birds/ # Birds -│ ├── Monsters/AOS/ # AOS-era monsters -│ ├── Monsters/SE/ # SE-era monsters -│ ├── Monsters/ML/ # ML-era monsters -│ ├── Special/ # Champions, bosses -│ ├── Vendors/ # NPC vendors -│ └── Townfolk/ # NPCs -├── Spells/ -│ ├── Base/ # Spell base classes -│ ├── First/ - Eighth/ # Magery circles -│ ├── Necromancy/ # Necromancer spells -│ ├── Chivalry/ # Paladin spells -│ ├── Bushido/ # Samurai abilities -│ ├── Ninjitsu/ # Ninja abilities -│ └── Spellweaving/ # Spellweaving -├── Skills/ # Skill handlers -├── Gumps/ # UI dialogs -│ └── Base/ # Gump base classes -├── Engines/ # Complex systems -│ ├── Craft/ # Crafting system -│ ├── CannedEvil/ # Champion spawns -│ ├── Factions/ # Faction system -│ └── Quests/ # Quest system -└── Misc/ # Miscellaneous - └── LootPack.cs # Loot tables -``` - -### Naming Rules -- File name = class name -- One primary class per file -- Group related items in subdirectories -- Era-specific content goes in era-named subdirectories diff --git a/dev-docs/debugging-event-loop.md b/dev-docs/debugging-event-loop.md deleted file mode 100644 index c38a0e6f7..000000000 --- a/dev-docs/debugging-event-loop.md +++ /dev/null @@ -1,123 +0,0 @@ -# Debugging Event Loop Performance - -How to diagnose "the server feels slow" — written for both humans and AI assistants. Follow the -funnel in order; most incidents resolve before the last step. Do not start with dotnet-trace. - -## The model - -Every second of the main thread's wall time goes to exactly one of four places: - -1. **Work** — the loop's phases: mobile deltas, item deltas, timer callbacks (`Timer.Slice`), - network processing (`NetState.Slice`), posted tasks (`LoopContext`), world snapshots - (`WorldSnapshot` — the on-loop portion of a save). -2. **Sleep** — idle blocking in `NetState.WaitForCompletion`, bounded by the next timer tick and - `server.eventLoopIdleWaitMs`. -3. **GC pauses** — land inside whichever phase (or sleep) was running. -4. **Stolen** — the host ran something else: hypervisor scheduling, noisy neighbors, CPU credit - throttling. - -A sleep is bounded by the time to the next wheel turn, so **a correctly honoured sleep can never -cost a deadline**. The only way sleeping harms the game is the wait *returning late* — that is -stolen time, and the server measures it directly on every sleep. - -## Step 0 — Read what production already tells you - -No build changes needed. Three signals exist, all actionable: - -| Signal | Meaning | Action | -|---|---|---| -| Startup error: *host cannot honour short waits* | No high-resolution timer and `timeBeginPeriod` failed. Very old or unusual Windows. | Nothing is wrong with the server; it spins and uses a full core. Upgrade the OS or accept the core. | -| Warning: *host returned a Nms idle wait late … for the Nth time running* | The OS did not reschedule the process promptly after a 1–2ms wait, through several escalating backoffs. Shared/burstable vCPU signature. | Move to dedicated CPU, or set `server.eventLoopIdleWaitMs=0` to spin permanently. This is a **host** problem — no amount of server-side change fixes it. | -| Error: *keeps returning idle waits late and sleeping has backed off N times* | The escalation hit its 120s ceiling. The host is not going to recover. | As above, but stop waiting for it to settle. Logged once per degradation, re-armed after a clean minute. | -| Admin gump → Performance → *Event Loop* | `Healthy` / `Sleep suspended (host)` / `Spinning (configured)` / `Spinning - host cannot honor short waits` | Same as above; the last verdict is the startup error's state, not a config choice. | - -The first two backoffs of any episode log at **Debug**, not Warning: a single suspension is -recoverable and not something an operator can act on. Raise the log level if you are chasing a -marginal host and want to see them. Late wakes that coincide with a gen1-or-higher GC are not -counted at all — the GC deliberately collects during idle sleeps, so its pauses land there by -design and are not the host's fault. - -If none of these fired and the shard still feels laggy, the cause is work, GC, or something a -boot-time signal cannot see. Continue. - -## Step 1 — Flip the profiling build - -``` -dotnet build -p:EventLoopProfiling=true -``` - -This compiles in `EventLoopProfiler` (Server) and the `[LoopStats` command (UOContent). Without -the flag every hook call site is removed by the compiler (`[Conditional]`), so there is nothing to -"turn off" in normal builds and no cost to leave the hooks in the code. The profiling build's own -overhead is a handful of timestamp reads per iteration — small enough to run for days while -hunting an intermittent problem. - -**Capture a baseline first.** Run `[LoopStats` while the shard feels *fine* and keep the CSV. The -profiler also keeps ~15 minutes of history in memory, so if the problem is episodic you can wait -for an episode and the good minutes on either side are already recorded. Numbers without a -baseline are how RunUO's profiler became useless — always compare bad minutes to good minutes on -the same box, build, and world. - -## Step 2 — Read the decomposition - -`[LoopStats` prints the last minute and writes the full history CSV (one row per second). Match -the shape against these signatures: - -| Signature | Diagnosis | Next step | -|---|---|---| -| One phase consistently hot (e.g. `TimerSlice` 40%/s) | Deep processing in that subsystem | Step 3 — find the culprit in that phase | -| All phases near zero, `stolen` high, `lateWakes` > 0 | Host is stealing CPU | Host problem; see step 0 actions | -| `gcPauseMs` high, gen2 counts rising | GC pressure — something is allocating heavily | Step 3 on the allocating phase, or dotnet-counters for alloc rate | -| Iterations ≫ sleeps while shard is idle | The loop is not sleeping: a queue never drains or a wake storm | Check `IsIdle` inputs; a stuck signal in the ring is the historical example | -| Sleeps ≈ iterations, each sleep ~0ms | Spurious wake storm | Ring backend issue; count `wakesIssued` vs actual cross-thread posts | -| Everything normal, complaint persists | Not the event loop | Look at the network path, client, or DB/save timing | - -**Wheel lag vs player lag:** `wheelLagMaxMs` is how late timer callbacks fired. Receives are -handled the moment they arrive (they wake the loop), so player-felt lag with a clean wheel points -away from the loop entirely. - -## Step 3 — Find the culprit inside a hot phase - -Add a temporary culprit hook rather than reaching for a tracer. The pattern: same -`[Conditional("EVENT_LOOP_PROFILING")]` attribute, own file or the profiler file, record only the -worst offender per second (identity + duration), never a per-event log. Examples: - -- `TimerSlice` hot → time each timer callback, keep the max and its `timer.ToString()`. -- `NetworkSlice` hot → time packet handlers by packet id, keep the max. -- GC pressure → `dotnet-counters monitor --counters System.Runtime` for alloc rate first; it is - cheap and often names the culprit generation without a trace. - -Keep the hook after the hunt if it earns its cost in the profiling build; delete it otherwise. - -## Step 4 — dotnet-trace, last and targeted - -Only when a hot phase resists the culprit hook. Know the costs: EventPipe visibly slows the -process (worst exactly when things are already bad) and adds artifacts to the trace — on small -vCPU hosts the tracer's own threads appear as hotspots and Rider/PerfView hotspot views can -mislead. Mitigate by being narrow: - -- Trace the specific minutes the decomposition flagged, not "a while". -- `dotnet-trace collect --profile cpu-sampling --duration 00:00:30` is usually enough. -- Compare against a trace of a good minute (same rule as step 1: no baseline, no conclusions). - -## The RAM / GC misconception (read before declaring a leak) - -ModernUO allocates very little, and the GC collects opportunistically — mostly during idle sleeps -and world saves. Under a spinning loop (`eventLoopIdleWaitMs=0`, or the pre-2026 default) the GC -may find **no** natural pause point: memory climbs to a large fraction of physical RAM, a forced -collection eventually drops part of it, and fragmentation keeps the baseline permanently above -where it started. Task manager shows alarming numbers; the in-game numbers do not. **Performance -is unaffected — this is lazy collection working as designed, not a leak.** Idle sleeping largely -removes the effect because every sleep is a natural GC opportunity. Before investigating "a leak": -check `gen0/1/2` and `gcPauseMs` in the decomposition, and compare working set *after a world -save*, which forces the collection the spin loop never allowed. - -## Rules of thumb - -- Never trade always-on profiling for the numbers. Production carries one timestamp per sleep and - nothing else; everything heavier lives behind the build flag or on the `measure/event-loop` - branch (full harness, A/B scripts, vendored ring experiments). -- One decomposition chart beats a thousand log lines. Resist adding warnings the reader cannot - act on; the three production signals are deliberate. -- When filing or reporting: attach the baseline CSV and the episode CSV. Relative statements - ("TimerSlice went from 4% to 61% during the episode") are the useful form. diff --git a/dev-docs/era-expansion.md b/dev-docs/era-expansion.md deleted file mode 100644 index 4b52fe81e..000000000 --- a/dev-docs/era-expansion.md +++ /dev/null @@ -1,188 +0,0 @@ -# ModernUO Era & Expansion System - -This document covers ModernUO's expansion system, era checks, and how to write era-conditional code. - -## Overview - -ModernUO supports all Ultima Online expansions from the original game through Endless Journey. The server's target expansion is configured at startup and affects gameplay mechanics, damage formulas, loot tables, skill systems, and more. - -## Expansion Enum - -Defined in `Projects/Server/ExpansionInfo.cs`: - -```csharp -public enum Expansion -{ - None, // 0 - Original UO (pre-T2A) - T2A, // 1 - The Second Age (October 1998) - UOR, // 2 - Renaissance (May 2000) - UOTD, // 3 - Third Dawn (March 2001) - LBR, // 4 - Lord Blackthorn's Revenge (February 2002) - AOS, // 5 - Age of Shadows (February 2003) - SE, // 6 - Samurai Empire (November 2004) - ML, // 7 - Mondain's Legacy (August 2005) - SA, // 8 - Stygian Abyss (September 2009) - HS, // 9 - High Seas (October 2010) - TOL, // 10 - Time of Legends (October 2015) - EJ // 11 - Endless Journey (March 2018) -} -``` - -## Era Check Properties - -Each property returns `true` when `Core.Expansion >= that expansion`: - -```csharp -Core.Expansion // Exact expansion (Expansion enum value) -Core.T2A // bool: >= The Second Age -Core.UOR // bool: >= Renaissance -Core.UOTD // bool: >= Third Dawn -Core.LBR // bool: >= Blackthorn's Revenge -Core.AOS // bool: >= Age of Shadows -Core.SE // bool: >= Samurai Empire -Core.ML // bool: >= Mondain's Legacy -Core.SA // bool: >= Stygian Abyss -Core.HS // bool: >= High Seas -Core.TOL // bool: >= Time of Legends -Core.EJ // bool: >= Endless Journey -``` - -## Major Era Boundaries - -### Pre-AOS (None through LBR) -- Simple damage model: flat random damage -- Skill-based resistance -- No item properties system -- Simple loot tables - -### AOS (Age of Shadows) -- The Big Divide -AOS fundamentally changed UO's combat and item systems: -- **Resistance system**: 5 damage types (Physical, Fire, Cold, Poison, Energy) each with resistances -- **Item properties**: Magic items with bonus properties (hit chance, damage increase, etc.) -- **New damage formula**: `GetNewAosDamage()` replaces flat random -- **Luck system**: Affects magic item quality from loot -- **Insurance**: Players can insure items against loss -- **Necromancy**: New spell school -- **Chivalry**: New spell school (Paladin) - -### SE (Samurai Empire) -- **Bushido/Ninjitsu**: Two new skill/spell schools -- **Adjusted loot packs**: `SePoor`, `SeMeager`, `SeAverage`, etc. -- **Reduced hit delay**: `Core.SE ? 250 : Core.AOS ? 500 : 1000` - -### ML (Mondain's Legacy) -- **Spellweaving**: New magic school -- **Adjusted skill requirements**: Different min skill values for spells -- **Container display**: Shows weight limits in tooltips -- **Stat gain changes**: Faster stat gain option - -### SA (Stygian Abyss) -- **Gargoyle race**: New playable race -- **Mysticism/Throwing**: New skills -- **Extended mobile status**: Additional stats in status bar - -### HS (High Seas) -- **Ship combat**: Naval warfare system -- **Extended status bar**: More stats visible - -## Writing Era-Conditional Code - -### Simple Value Selection -```csharp -// Ternary chain (most common pattern) -var delay = Core.SE ? 250 : Core.AOS ? 500 : 1000; -var statMax = Core.LBR ? 125 : 100; -``` - -### Logic Branching -```csharp -if (Core.AOS) -{ - // AOS+ damage formula - damage = GetNewAosDamage(10, 1, 4, target); -} -else -{ - // Pre-AOS damage formula - damage = Utility.Random(4, 4); - if (CheckResisted(target)) - { - damage *= 0.75; - target.SendLocalizedMessage(501783); - } - damage *= GetDamageScalar(target); -} -``` - -### Display Branching -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); - - if (Core.ML) - { - // ML+ shows full container info - list.Add(1072241, $"{TotalItems}\t{MaxItems}\t{TotalWeight}\t{MaxWeight}"); - } - else - { - // Pre-ML shows basic info - list.Add(1050044, $"{TotalItems}\t{TotalWeight}"); - } -} -``` - -### Skill Requirements by Era -```csharp -// From MagerySpell.cs - spell difficulty varies by era -private static readonly double[] _requiredSkill = Core.ML - ? new[] { -46.0, -32.0, -18.0, -4.0, 10.0, 24.0, 38.0, 52.0, 66.0, 80.0 } - : new[] { -50.0, -30.0, 0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0 }; -``` - -### Loot by Era -LootPack properties auto-select era-appropriate variants: -```csharp -LootPack.Poor // Selects: OldPoor / AosPoor / SePoor -LootPack.Meager // Selects: OldMeager / AosMeager / SeMeager -LootPack.Average // etc. -LootPack.Rich -LootPack.FilthyRich -LootPack.UltraRich -LootPack.SuperBoss -``` - -## Configuration - -Expansion is set in `Distribution/Configuration/expansion.json`: -```json -{ - "expansion": "ML" -} -``` - -Full expansion metadata is in `Distribution/Data/expansions.json`, containing: -- Required client version -- Supported feature flags -- Character list flags -- Housing flags -- Mobile status version -- Map selection flags - -## Best Practices - -1. **Always ask the user** which expansion to target if not specified -2. **Test both branches** when writing era-conditional code -3. **Use `Core.XYZ` properties** (not `Core.Expansion >= Expansion.XYZ`) -4. **Chain ternaries** from newest to oldest for value selection -5. **Document era requirements** in comments when the logic is complex -6. **Use era-aware LootPack** properties instead of hardcoding specific era packs - -## Key File References -- Expansion enum: `Projects/Server/ExpansionInfo.cs` -- Core properties: `Projects/Server/Core.cs` -- Expansion data: `Distribution/Data/expansions.json` -- Loot packs: `Projects/UOContent/Misc/LootPack.cs` -- Spell circles: `Projects/UOContent/Spells/Base/MagerySpell.cs` -- Skill check: `Projects/UOContent/Skills/SkillCheck.cs` diff --git a/dev-docs/event-scheduler.md b/dev-docs/event-scheduler.md deleted file mode 100644 index f1e070e82..000000000 --- a/dev-docs/event-scheduler.md +++ /dev/null @@ -1,347 +0,0 @@ -# ModernUO EventScheduler System - -This document covers ModernUO's wall-clock and calendar-based event scheduling system. For game-tick timers (sub-second precision, combat ticks, decay, etc.), see `dev-docs/timers.md`. - -## Overview - -The `EventScheduler` provides **wall-clock scheduling** — firing events at real-world times, dates, and calendar patterns. It runs as a 1-second `Timer` on the game loop and uses a `PriorityQueue` ordered by next UTC occurrence. - -### Timer vs EventScheduler - -| Aspect | `Timer.StartTimer` | `EventScheduler` | -|---|---|---| -| Clock basis | Game-tick (8ms wheel) | Wall-clock (1s poll) | -| Precision | 8ms | 1 second | -| Use case | Combat ticks, decay, delays | Holidays, daily resets, scheduled maintenance | -| Recurrence | Fixed interval | Calendar patterns (hourly, daily, weekly, monthly, yearly) | -| Timezone | N/A | Full `TimeZoneInfo` + DST handling | -| Seasonal windows | No | Yes (`YearlyScheduledEvent`) | - -**Rule of thumb**: If the event must happen "at 9:00 AM EST every Monday" or "during October through November each year," use `EventScheduler`. If it must happen "5 seconds from now" or "every 2 seconds," use `Timer`. - -## Architecture - -`EventScheduler` is a singleton `Timer` that ticks every second. Internally it holds a `PriorityQueue` sorted by `NextOccurrence` (UTC). Each tick it dequeues and fires all events whose time has passed, then each event self-re-enqueues for its next occurrence. - -``` -EventScheduler (Timer, 1s tick) - └─ PriorityQueue - ├─ CallbackScheduledEvent (fires Action) - ├─ YearlyCallbackScheduledEvent (fires Action within seasonal window) - └─ Your custom subclass -``` - -## Class Hierarchy - -``` -BaseScheduledEvent (abstract: Schedule, Cancel, Advance, OnEvent) - └─ ScheduledEvent (adds IRecurrencePattern, TimeOnly, EndDate) - ├─ CallbackScheduledEvent (sealed: fires an Action callback) - └─ YearlyScheduledEvent (abstract: adds MonthDay start/end seasonal window) - └─ YearlyCallbackScheduledEvent (fires an Action within seasonal window) -``` - -### BaseScheduledEvent - -The abstract root. Key members: - -| Member | Description | -|---|---| -| `TimeZone` | `TimeZoneInfo` — defaults to UTC | -| `NextOccurrence` | `DateTime` (UTC) of the next fire time | -| `Cancelled` | `bool` — set by `Cancel()` | -| `Scheduler` | Back-reference to the owning `EventScheduler` | -| `Schedule(startAfter, timeZone?)` | Schedule on the shared instance | -| `Schedule(scheduler, startAfter, timeZone?)` | Schedule on a specific scheduler | -| `Cancel()` | Cancel and unschedule the event | -| `Advance()` | Called by the scheduler — fires `OnEvent()`, then re-schedules | -| `OnEvent()` | Abstract — your event logic goes here | - -### ScheduledEvent - -Extends `BaseScheduledEvent` with recurrence support: - -| Member | Description | -|---|---| -| `Recurrence` | `IRecurrencePattern` — determines when the event recurs | -| `Time` | `TimeOnly` — the time-of-day component for recurrence calculation | -| `EndDate` | `DateTime` — stop recurring after this date (default: never) | - -### CallbackScheduledEvent - -Sealed concrete class. Wraps an `Action` callback: - -```csharp -var evt = new CallbackScheduledEvent(new TimeOnly(9, 0), myAction, EventScheduler.Daily); -evt.Schedule(DateTime.UtcNow); -``` - -Most users should use the static convenience methods on `EventScheduler` instead of constructing directly. - -### YearlyScheduledEvent - -Abstract. Adds a seasonal window defined by `MonthDay` start and end: - -| Member | Description | -|---|---| -| `YearlyStart` | `MonthDay` — first day of the active window | -| `YearlyEnd` | `MonthDay` — last day of the active window | - -The event only fires when the next occurrence falls within the `[YearlyStart, YearlyEnd]` range. If it falls outside, the scheduler fast-forwards to the next year's window start. Supports year-boundary wrapping (e.g., Nov 15 through Feb 15). - -### YearlyCallbackScheduledEvent - -Concrete version of `YearlyScheduledEvent` that fires an `Action`: - -```csharp -var halloween = new YearlyCallbackScheduledEvent( - new TimeOnly(0, 0), - new MonthDay(2025, 10, 1), // Oct 1 - new MonthDay(2025, 11, 1), // Nov 1 - SpawnHalloweenContent, - EventScheduler.Daily -); -halloween.Schedule(DateTime.UtcNow, easternTimeZone); -``` - -## Recurrence Patterns - -All patterns implement `IRecurrencePattern`: - -```csharp -public interface IRecurrencePattern -{ - DateTime GetNextOccurrence(DateTime afterUtc, TimeOnly time, TimeZoneInfo timeZone); -} -``` - -### Built-In Patterns - -| Pattern | Static accessor | Behavior | -|---|---|---| -| `HourlyRecurrencePattern` | `EventScheduler.Hourly` | Every N hours (default 1) at the same minute | -| `DailyRecurrencePattern` | `EventScheduler.Daily` | Every N days (default 1) at the specified time | -| `WeeklyRecurrencePattern` | `EventScheduler.Weekly` | Every N weeks (default 1), with optional `AllowedDays` and `AllowedMonths` filters | -| `WeeklyRecurrencePattern(2)` | `EventScheduler.Biweekly` | Every 2 weeks | -| `MonthlyRecurrencePattern` | `EventScheduler.Monthly` | Every N months (default 1) on a specific day-of-month | -| `MonthlyRecurrencePattern(-1, 12)` | `EventScheduler.Yearly` | Every 12 months (yearly) | -| `MonthlyOrdinalRecurrencePattern` | (construct directly) | E.g., "second Tuesday of every month" or "last Friday" | - -### MonthlyOrdinalRecurrencePattern - -For patterns like "the third Wednesday of every month": - -```csharp -// Second Tuesday of every month -var pattern = new MonthlyOrdinalRecurrencePattern( - OrdinalDayOccurrence.Second, - DayOfWeek.Tuesday -); - -// Last Friday of every month -var pattern = new MonthlyOrdinalRecurrencePattern( - OrdinalDayOccurrence.Last, - DayOfWeek.Friday -); -``` - -The `OrdinalDayOccurrence` enum: - -| Value | Meaning | -|---|---| -| `Last` (-1) | Last occurrence in the month | -| `First` (0) | First occurrence | -| `Second` (1) | Second occurrence | -| `Third` (2) | Third occurrence | -| `Fourth` (3) | Fourth occurrence | -| `Fifth` (4) | Fifth occurrence (skipped if doesn't exist) | - -### WeeklyRecurrencePattern with Filters - -```csharp -// Every week on Monday and Wednesday, only in January through March -var pattern = new WeeklyRecurrencePattern( - intervalWeeks: 1, - allowedMonths: AllowedMonths.January | AllowedMonths.February | AllowedMonths.March, - allowedDays: AllowedDays.Monday | AllowedDays.Wednesday -); -``` - -## Supporting Types - -### AllowedDays (Flags Enum) - -```csharp -[Flags] -public enum AllowedDays : byte -{ - None, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, All -} -``` - -Extension: `DayOfWeek.Monday.ToDaysOfWeek()` → `AllowedDays.Monday` - -### AllowedMonths (Flags Enum) - -```csharp -[Flags] -public enum AllowedMonths -{ - None, January, February, ..., December, All -} -``` - -### MonthDay (Record Struct) - -Represents a month/day pair without a year. Used for seasonal window boundaries: - -```csharp -var oct1 = new MonthDay(2025, 10, 1); // Year is used only for day-count validation -var nov1 = new MonthDay(2025, 11, 1); -``` - -Extension method `DateTime.IsBetween(MonthDay start, MonthDay end)` handles year-boundary wrapping: -- `IsBetween(Oct 1, Nov 1)` — standard range within one year -- `IsBetween(Nov 15, Feb 15)` — wraps across year boundary - -## Static Convenience Methods - -`EventScheduler` provides static methods that create, schedule, and return a `ScheduledEvent`: - -```csharp -// All methods accept: DateTime startOn, Action action, TimeZoneInfo timeZone = null - -EventScheduler.HourlyAt(startOn, action, timeZone); -EventScheduler.DailyAt(startOn, action, timeZone); -EventScheduler.WeeklyAt(startOn, action, timeZone); -EventScheduler.BiweeklyAt(startOn, action, timeZone); -EventScheduler.MonthlyAt(startOn, action, timeZone); -EventScheduler.YearlyAt(startOn, action, timeZone); -``` - -The `startOn` parameter determines: -1. The `TimeOnly` component (hour/minute for recurrence) -2. The starting reference date -3. For `MonthlyAt`/`YearlyAt`, the day-of-month - -Example: -```csharp -// Fire at 6:00 AM Eastern every day, starting tomorrow -var eastern = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); -var tomorrow6am = new DateTime(2025, 6, 15, 6, 0, 0); -EventScheduler.DailyAt(tomorrow6am, ResetDailyQuests, eastern); -``` - -## Timezone & DST Handling - -The system fully supports timezones via `TimeZoneInfo`: - -- **Default**: UTC if no timezone specified -- **DST-safe**: Uses `LocalToUtc()` extension which handles: - - **Ambiguous times** (fall-back): Uses the later offset (standard time) - - **Invalid times** (spring-forward): Recurrence patterns skip invalid candidates -- **Conversion**: All internal scheduling uses UTC; local time is only for calculating the next occurrence - -```csharp -// Extension method in Server namespace (Utility.cs) -public static DateTime LocalToUtc(this DateTime local, TimeZoneInfo tz) -``` - -**Always specify a timezone** when your event needs to fire at a local time. Omitting it defaults to UTC. - -## Custom Event Classes - -For complex logic, inherit from `ScheduledEvent` or `YearlyScheduledEvent`: - -```csharp -public class WeekendBonusEvent : ScheduledEvent -{ - public WeekendBonusEvent() - : base( - new TimeOnly(18, 0), // 6:00 PM - new WeeklyRecurrencePattern( - intervalWeeks: 1, - allowedDays: AllowedDays.Friday | AllowedDays.Saturday - )) - { - } - - public override void OnEvent() - { - // Enable weekend bonus XP - BonusSystem.ActivateWeekendBonus(); - } -} - -// Usage: -var evt = new WeekendBonusEvent(); -evt.Schedule(DateTime.UtcNow, easternTimeZone); -``` - -### Yearly Seasonal Custom Event - -```csharp -public class HalloweenSpawnEvent : YearlyScheduledEvent -{ - protected HalloweenSpawnEvent() - : base( - new TimeOnly(0, 0), - new MonthDay(2025, 10, 15), // Oct 15 - new MonthDay(2025, 11, 1), // Nov 1 - EventScheduler.Daily) - { - } - - public override void OnEvent() - { - // Spawn Halloween creatures daily during the window - HalloweenSystem.SpawnCreatures(); - } -} -``` - -## Cancellation - -Call `Cancel()` on any `BaseScheduledEvent` to remove it from the scheduler: - -```csharp -private BaseScheduledEvent _dailyReset; - -public void StartDailyResets() -{ - var tomorrow = new DateTime(2025, 6, 15, 0, 0, 0); - _dailyReset = EventScheduler.DailyAt(tomorrow, ResetDaily, easternTz); -} - -public void StopDailyResets() -{ - _dailyReset?.Cancel(); - _dailyReset = null; -} -``` - -## Common Mistakes - -| Mistake | Problem | Fix | -|---|---|---| -| Using `Timer` for calendar events | Drifts with server restarts, no timezone support | Use `EventScheduler` | -| Forgetting timezone | Event fires at UTC instead of local time | Always pass `TimeZoneInfo` for local-time events | -| Not cancelling on cleanup | Event keeps firing after system disabled | Call `Cancel()` in cleanup/shutdown | -| Using `EventScheduler` for sub-second timing | 1-second granularity too coarse | Use `Timer.StartTimer` instead | -| Constructing `MonthDay` with invalid days | Throws `ArgumentOutOfRangeException` | Check `DateTime.DaysInMonth` for the given month | - -## Key File References - -| File | Description | -|---|---| -| `Projects/UOContent/Engines/Events/EventScheduler.cs` | Singleton scheduler, PriorityQueue, static factory methods | -| `Projects/UOContent/Engines/Events/BaseScheduledEvent.cs` | Abstract base: Schedule, Cancel, Advance, OnEvent | -| `Projects/UOContent/Engines/Events/ScheduledEvent.cs` | Adds IRecurrencePattern, TimeOnly, EndDate | -| `Projects/UOContent/Engines/Events/CallbackScheduledEvent.cs` | Sealed Action-callback concrete class | -| `Projects/UOContent/Engines/Events/YearlyScheduledEvent.cs` | Seasonal window with MonthDay start/end | -| `Projects/UOContent/Engines/Events/YearlyCallbackScheduledEvent.cs` | Yearly seasonal + Action callback | -| `Projects/UOContent/Engines/Events/CommonRecurrencePatterns.cs` | All IRecurrencePattern implementations | -| `Projects/UOContent/Engines/Events/AllowedDays.cs` | Flags enum for day-of-week filtering | -| `Projects/UOContent/Engines/Events/AllowedMonths.cs` | Flags enum for month filtering | -| `Projects/UOContent/Engines/Events/MonthDay.cs` | Record struct + IsBetween extension | -| `Projects/Server/Utilities/Utility.cs` | `LocalToUtc()` extension method | diff --git a/dev-docs/events.md b/dev-docs/events.md deleted file mode 100644 index 2fcbeda68..000000000 --- a/dev-docs/events.md +++ /dev/null @@ -1,281 +0,0 @@ -# ModernUO Event System - -This document covers ModernUO's event system, including EventSink static events and the CodeGeneratedEvents system for custom entity events. - -## Overview - -ModernUO provides two event mechanisms: -1. **EventSink**: Static events for core game lifecycle (login, logout, death, speech, etc.) -2. **CodeGeneratedEvents**: Attribute-based events on game entities (player login, creature death, etc.) - -## EventSink - -### Architecture - -`EventSink` is a `static partial class` spread across multiple files in `Projects/Server/Events/`. Each event is defined as a `public static event Action` with a corresponding `InvokeXxx()` method. - -### Subscribing to Events - -Subscribe in your `Configure()` static method: - -```csharp -public static class MySystem -{ - public static void Configure() - { - EventSink.Connected += OnPlayerConnected; - EventSink.Disconnected += OnPlayerDisconnected; - EventSink.Speech += OnSpeech; - EventSink.ServerStarted += OnServerStarted; - } - - private static void OnPlayerConnected(Mobile m) - { - if (m is PlayerMobile pm) - pm.SendMessage("Welcome to the server!"); - } - - private static void OnPlayerDisconnected(Mobile m) - { - // Cleanup player state - } - - private static void OnSpeech(SpeechEventArgs e) - { - if (e.Speech.InsensitiveContains("help")) - { - e.Mobile.SendMessage("Type [help for commands."); - e.Handled = true; - } - } - - private static void OnServerStarted() - { - // Initialize after all systems loaded - } -} -``` - -### Available Events - -#### Server Lifecycle -| Event | Signature | When | -|---|---|---| -| `ServerStarted` | `Action` | Server fully initialized | -| `Shutdown` | `Action` | Server shutting down | -| `WorldLoad` | `Action` | World loaded from saves | -| `WorldSave` | `Action` | World save triggered | -| `WorldSavePostSnapshot` | `Action` | After save snapshot | -| `ServerCrashed` | `Action` | Unhandled exception | - -#### Player Connection -| Event | Signature | When | -|---|---|---| -| `Connected` | `Action` | Player connected to server | -| `BeforeDisconnected` | `Action` | About to disconnect | -| `Disconnected` | `Action` | Player disconnected | -| `Logout` | `Action` | Player logged out | - -#### Account -| Event | Signature | When | -|---|---|---| -| `AccountLogin` | `Action` | Account login attempt | - -#### Communication -| Event | Signature | When | -|---|---|---| -| `Speech` | `Action` | Player speaks | -| `PaperdollRequest` | `Action` | Paperdoll opened (beholder, beheld) | - -#### Combat -| Event | Signature | When | -|---|---|---| -| `AggressiveAction` | `Action` | Aggressive action taken | - -#### Movement -| Event | Signature | When | -|---|---|---| -| `Movement` | `Action` | Player moves | - -#### Network -| Event | Signature | When | -|---|---|---| -| `SocketConnect` | `Action` | New socket connection | - -### EventArgs Classes - -#### SpeechEventArgs -```csharp -public class SpeechEventArgs -{ - public Mobile Mobile { get; } - public string Speech { get; set; } // Can modify speech text - public MessageType Type { get; } - public int Hue { get; } - public int[] Keywords { get; } - public bool Handled { get; set; } // Set true to consume - public bool Blocked { get; set; } // Set true to block - public bool HasKeyword(int keyword); -} -``` - -#### AccountLoginEventArgs -```csharp -public class AccountLoginEventArgs -{ - public NetState State { get; } - public string Username { get; } - public string Password { get; } - public bool Accepted { get; set; } // Set false to reject - public ALRReason RejectReason { get; set; } // Reason for rejection -} -``` - -#### MovementEventArgs (Pooled) -```csharp -public class MovementEventArgs -{ - public Mobile Mobile { get; } - public Direction Direction { get; } - public bool Blocked { get; set; } // Set true to block movement - - // Object pooling - public static MovementEventArgs Create(Mobile m, Direction dir); - public void Free(); // Return to pool -} -``` - -#### AggressiveActionEventArgs (Pooled) -```csharp -public class AggressiveActionEventArgs -{ - public Mobile Aggressed { get; } - public Mobile Aggressor { get; } - public bool Criminal { get; } - - public static AggressiveActionEventArgs Create(Mobile aggressed, Mobile aggressor, bool criminal); - public void Free(); -} -``` - -#### WorldSavePostSnapshotEventArgs -```csharp -public class WorldSavePostSnapshotEventArgs -{ - public string OldSavePath { get; } - public string NewSavePath { get; } -} -``` - -#### ServerCrashedEventArgs -```csharp -public class ServerCrashedEventArgs -{ - public Exception Exception { get; } - public bool Close { get; set; } // Set false to continue running -} -``` - -#### SocketConnectEventArgs -```csharp -public class SocketConnectEventArgs -{ - public IPAddress Address { get; } - public bool AllowConnection { get; set; } // Set false to reject -} -``` - -### Creating Custom EventSink Events - -Add to EventSink as a partial class: - -```csharp -// Projects/Server/Events/MyCustomEvent.cs -namespace Server; - -public static partial class EventSink -{ - public static event Action ItemCrafted; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InvokeItemCrafted(Mobile crafter, Item item) => - ItemCrafted?.Invoke(crafter, item); -} -``` - -Then invoke from game code: -```csharp -EventSink.InvokeItemCrafted(crafter, craftedItem); -``` - ---- - -## CodeGeneratedEvents - -For events on specific game entities, ModernUO uses source-generated events via the `CodeGeneratedEvents` package. - -External reference: https://github.com/modernuo/CodeGeneratedEvents - -### Defining Generated Events - -On the class that fires the event: -```csharp -[GeneratedEvent(nameof(PlayerLoginEvent))] -public static partial void PlayerLoginEvent(PlayerMobile player); -``` - -### Subscribing to Generated Events - -On any class that handles the event: -```csharp -[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))] -public static void HandlePlayerLogin(PlayerMobile player) -{ - // Handle the event -} -``` - -### Known Generated Events -- `PlayerMobile.PlayerLoginEvent` -- Player logs in -- `PlayerMobile.PlayerDeathEvent` -- Player dies -- `BaseCreature.CreatureDeathEvent` -- Creature dies - ---- - -## Event Args Pooling Pattern - -Some EventArgs use object pooling to avoid allocation in hot paths: - -```csharp -// System that fires the event: -var args = MovementEventArgs.Create(mobile, direction); -EventSink.InvokeMovement(args); -// Check args.Blocked after invocation -args.Free(); // Return to pool -``` - -This pattern is used for high-frequency events (movement, combat) to minimize GC pressure. - ---- - -## Best Practices - -1. **Subscribe in `Configure()`** -- called automatically during startup -2. **Check player type** -- `Connected` fires for all mobiles; cast to `PlayerMobile` if needed -3. **Keep handlers fast** -- they run on the game loop thread -4. **Use `Handled`/`Blocked`** -- on SpeechEventArgs to consume/block messages -5. **Unsubscribe on disable** -- if your system can be turned off, unsubscribe (`-=`) to prevent leaks -6. **Don't throw exceptions** -- unhandled exceptions in event handlers can crash the server - -## Key File References - -| File | Description | -|---|---| -| `Projects/Server/Events/EventSink.cs` | Core EventSink (partial) | -| `Projects/Server/Events/SpeechEvent.cs` | Speech event | -| `Projects/Server/Events/MovementEvent.cs` | Movement event (pooled) | -| `Projects/Server/Events/AggressiveActionEvent.cs` | Combat event (pooled) | -| `Projects/Server/Events/AccountLoginEvent.cs` | Account login | -| `Projects/Server/Events/EventSink.cs` | World save/load (WorldLoad, WorldSave, ServerStarted, Shutdown) | -| `Projects/Server/Events/SocketConnectionEvent.cs` | Socket connections | -| `Projects/Server/Events/ServerCrashedEvent.cs` | Crash handling | diff --git a/dev-docs/gump-system.md b/dev-docs/gump-system.md deleted file mode 100644 index 90b1f7622..000000000 --- a/dev-docs/gump-system.md +++ /dev/null @@ -1,519 +0,0 @@ -# ModernUO Gump System - -This document covers ModernUO's gump (UI dialog) system, including BaseGump, StaticGump, DynamicGump, builders, response handling, and best practices. - -## Overview - -Gumps are custom UI dialogs displayed to players. ModernUO provides a modern gump system with two main types: -- **StaticGump**: Layout is cached and reused across instances (better performance) -- **DynamicGump**: Layout is rebuilt each time (for variable content) - -## Class Hierarchy - -``` -BaseGump (abstract) -├── StaticGump -- Cached layout, for fixed-structure gumps -└── DynamicGump -- Rebuilt layout, for dynamic-structure gumps -``` - -All gump classes are in `Projects/UOContent/Gumps/Base/`. - -## Sending Gumps - -```csharp -using Server.Gumps; // REQUIRED for extension methods - -// Send a gump -mobile.SendGump(new MyGump(mobile)); - -// Check if gump is open -if (mobile.HasGump()) { } - -// Find an open gump -var gump = mobile.FindGump(); - -// Close a gump -mobile.CloseGump(); -``` - -**Important**: `using Server.Gumps;` is required. Without it, `SendGump()`, `HasGump()`, `FindGump()`, and `CloseGump()` extension methods won't resolve. - -These methods are also available on `NetState`: -```csharp -mobile.NetState.SendGump(gump); -mobile.NetState.HasGump(); -``` - -## StaticGump - -Use for gumps where the layout structure is the same for all instances. The layout is compiled and cached on first use, then reused. - -### Template -```csharp -using Server.Gumps; - -namespace Server.Gumps; - -public class MyStaticGump : StaticGump -{ - private readonly Mobile _player; - private readonly string _data; - - public override bool Singleton => true; // Only one per player - - public MyStaticGump(Mobile player, string data) : base(50, 50) - { - _player = player; - _data = data; - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 400, 300, 5054); - builder.AddAlphaRegion(10, 10, 380, 280); - - // Static text (baked into cached layout) - builder.AddHtmlLocalized(15, 15, 370, 20, 1060635, 0x7800); // "Warning" - - // Dynamic text (placeholder filled per-instance via BuildStrings) - builder.AddHtmlPlaceholder(15, 45, 370, 200, "content", false, true); - - // Buttons - builder.AddButton(100, 265, 4005, 4007, 1); // OK (buttonID=1) - builder.AddHtmlLocalized(135, 267, 100, 20, 1011036); // "OK" - - builder.AddButton(250, 265, 4017, 4019, 0); // Cancel (buttonID=0 = close) - builder.AddHtmlLocalized(285, 267, 100, 20, 1011012); // "Cancel" - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - builder.SetHtmlText("content", _data, "#FFC000", 4); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID == 1) - { - _player.SendMessage("Confirmed!"); - } - } -} -``` - -### How Caching Works -1. First instance calls `BuildLayout()` -- the layout bytes are compiled and cached -2. Subsequent instances reuse the cached layout bytes -3. `BuildStrings()` is called per-instance to fill dynamic text placeholders -4. Use `AddLabelPlaceholder`/`AddHtmlPlaceholder` for text that changes per instance -5. Use `AddLabel`/`AddHtml`/`AddHtmlLocalized` for text baked into the cache - -### Placeholders -```csharp -// In BuildLayout: -builder.AddLabelPlaceholder(x, y, hue, "slotKey"); -builder.AddHtmlPlaceholder(x, y, w, h, "slotKey", background, scrollbar); -builder.AddTextEntryPlaceholder(x, y, w, h, hue, entryId, "slotKey"); - -// In BuildStrings: -builder.SetStringSlot("slotKey", "text value"); -builder.SetHtmlText("slotKey", "html content", "#color", fontSize); -``` - -## DynamicGump - -Use for gumps where the layout structure varies per instance (e.g., lists of items, search results). - -### Template -```csharp -using Server.Gumps; - -namespace Server.Gumps; - -public class MyDynamicGump : DynamicGump -{ - private readonly Mobile _player; - private readonly List _items; - - public override bool Singleton => true; - - public MyDynamicGump(Mobile player, List items) : base(50, 50) - { - _player = player; - _items = items; - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var height = 60 + _items.Count * 30; - builder.AddPage(); - builder.AddBackground(0, 0, 400, height, 5054); - builder.AddAlphaRegion(10, 10, 380, height - 20); - builder.AddHtml(15, 15, 370, 20, "Select an item:"); - - for (var i = 0; i < _items.Count; i++) - { - var y = 45 + i * 30; - var item = _items[i]; - builder.AddLabel(20, y, 0x480, item.Name ?? "Unknown"); - builder.AddButton(350, y, 4005, 4007, i + 1); - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID > 0 && info.ButtonID <= _items.Count) - { - var selected = _items[info.ButtonID - 1]; - _player.SendMessage($"You selected: {selected.Name}"); - } - } -} -``` - -## Builder Methods Reference - -### Layout Structure -```csharp -builder.AddPage(int page = 0); // Add page (0 = all pages) -builder.AddBackground(x, y, w, h, gumpID); -builder.AddAlphaRegion(x, y, w, h); // Transparent background -builder.AddImageTiled(x, y, w, h, gumpID); // Tiled background image -builder.AddGroup(int groupId); // Radio button group -``` - -### Images -```csharp -builder.AddImage(x, y, gumpID, hue); // Gump art image -builder.AddItem(x, y, itemID, hue); // Item graphic -builder.AddImageTiledButton(x, y, normalID, pressedID, buttonID, type, param, itemID, hue, w, h); -``` - -### Text -```csharp -builder.AddLabel(x, y, hue, text); // Single-line text -builder.AddLabelCropped(x, y, w, h, hue, text); // Cropped text -builder.AddHtml(x, y, w, h, text, bg, scrollbar); // HTML text -builder.AddHtml(x, y, w, h, text, color, size, fontStyle, align, bg, scrollbar); -builder.AddHtmlLocalized(x, y, w, h, clilocNumber); // Localized text -builder.AddHtmlLocalized(x, y, w, h, clilocNumber, color); -``` - -#### Interpolation in text - -Most text-accepting builders take a `ReadOnlySpan` and have a `ref RawInterpolatedStringHandler` overload, so `$"..."` literals at the call site are zero-allocation. The same applies to `Html.Center`, `Html.Color`, `Html.Right` helpers used when wrapping text in HTML markup: - -```csharp -// Zero allocation — interpolation handler renders directly into a pooled buffer -builder.AddHtml(20, 20, 200, 100, $"
{Title}: {Score:N0}
"); -builder.AddLabel(20, 40, hue, $"You have {gold} gold"); -``` - -Several call-site shapes silently defeat the handler overload selection (ternaries with interpolated branches, `.ToString()` inside holes, pre-built `var msg = $"..."` locals, etc.). See [`dev-docs/string-handling.md`](string-handling.md#interpolation-anti-patterns) for the full list and fixes — they apply equally inside `BuildLayout`. - -### Interactive Elements -```csharp -builder.AddButton(x, y, normalID, pressedID, buttonID); -builder.AddButton(x, y, normalID, pressedID, buttonID, GumpButtonType.Page, pageNum); -builder.AddCheckbox(x, y, inactiveID, activeID, selected, switchID); -builder.AddRadio(x, y, inactiveID, activeID, selected, switchID); -builder.AddTextEntry(x, y, w, h, hue, entryID, initialText); -builder.AddTextEntryLimited(x, y, w, h, hue, entryID, initialText, maxLength); -``` - -### Modifiers -```csharp -builder.SetNoClose(); // Disable right-click close -builder.SetNoMove(); // Disable dragging -builder.SetNoResize(); // Disable resizing -builder.SetNoDispose(); // Disable dispose -builder.AddTooltip(num); // Tooltip on hover -builder.AddItemProperty(serial); // Item property tooltip -``` - -## Response Handling - -```csharp -public override void OnResponse(NetState sender, in RelayInfo info) -{ - var mobile = sender.Mobile; - - // Button ID (0 = close/cancel, 1+ = custom buttons) - switch (info.ButtonID) - { - case 0: return; // Closed - case 1: - // Handle button 1 - break; - } - - // Check checkbox/radio state - bool isChecked = info.IsSwitched(switchID); - - // Get text entry value - string text = info.GetTextEntry(entryID); -} -``` - -### Button ID Convention -- `0` = Close/Cancel (default when player closes gump) -- `1+` = Custom action buttons -- Use `GumpButtonType.Page` for page navigation buttons (don't trigger OnResponse) - -## BaseGump Properties - -```csharp -public int X { get; set; } // Gump X position -public int Y { get; set; } // Gump Y position -public virtual bool Singleton => false; // Only one instance per player -public int TypeID { get; } // Unique type identifier -public Serial Serial { get; } // Gump serial -``` - -## Common Gump IDs (Background Art) - -| ID | Description | -|---|---| -| 5054 | Dark stone background | -| 9200 | Scroll background | -| 9250 | Light parchment | -| 3600 | Brown wood panel | -| 5120 | Gray stone border | -| 2620 | Ornate gold frame | - -## Common Button IDs (Art) - -| Normal/Pressed | Description | -|---|---| -| 4005/4007 | Small right arrow (green) | -| 4017/4019 | Small X (red) | -| 4023/4025 | Small left arrow | -| 4020/4022 | Small checkmark | -| 4029/4031 | Large right arrow | -| 247/248 | Large green gem | -| 241/242 | Large red gem | - -## Important Properties - -### Singleton -```csharp -public override bool Singleton => true; -``` -When `true`, the gump system automatically closes any existing instance of this gump type for the player before sending a new one. **Always set this for gumps that shouldn't stack.** Without it, repeated sends create duplicate gumps the player must close individually. - -### Cached (StaticGump only) -```csharp -protected virtual bool Cached => true; // default -``` -Controls whether `StaticGump` caches its compiled layout. The layout is compiled once on first send, then reused for all subsequent instances. - -**Set to `false` during development** to force the layout to rebuild every send — useful for hot-reload iteration and debugging layout changes without restarting the server: -```csharp -// Temporary: disable caching while iterating on layout -protected override bool Cached => false; -``` -**Remove the override (or set back to `true`) before committing.** Leaving it `false` in production wastes CPU recompiling identical layouts. - -## Empty Gump Rule (CRITICAL) - -**NEVER send a gump with no visual components.** An empty gump (no background, no buttons, no content) has no close button and no right-click dismiss — the client cannot close it. This causes a **gump leak** on both the client and server: the gump stays in the tracking list forever, the client renders an invisible undismissable element, and the slot is consumed until the player relogs. - -Empty gumps typically happen when a developer short-circuits inside the constructor or `BuildLayout`: -```csharp -// BAD: Short-circuit in constructor creates an empty gump -public MyGump(Mobile from) : base(50, 50) -{ - if (!from.Alive) - return; // Gump is already constructed — it's empty but will still be sent! - - AddPage(0); - AddBackground(0, 0, 400, 300, 5054); - // ... -} -``` - -### The Fix: Static DisplayTo Pattern - -Use a static entry-point method that validates prerequisites **before** constructing the gump. The constructor is private — the only way to create the gump is through `DisplayTo`, which guarantees the gump is never empty. See `GoGump.cs` for the canonical example: - -```csharp -public class MyGump : DynamicGump // or StaticGump, or Gump -{ - public override bool Singleton => true; - - // Private constructor — can only be called from DisplayTo - private MyGump(Mobile from, SomeData data) : base(50, 50) - { - // Safe to build layout — prerequisites already validated - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - // Always produces visual content — DisplayTo guarantees valid state - builder.AddPage(); - builder.AddBackground(0, 0, 400, 300, 5054); - // ... - } - - // Static entry point — validates before constructing - public static void DisplayTo(Mobile from) - { - if (!from.Alive || from.NetState == null) - return; // No gump created at all - - var data = GetData(from); - if (data == null) - return; // No gump created at all - - from.SendGump(new MyGump(from, data)); - } -} -``` - -**Key points:** -- Constructor is `private` — enforces that `DisplayTo` is the only entry point -- All validation/short-circuiting happens in `DisplayTo` before `new MyGump(...)` is called -- If prerequisites fail, no gump is constructed or sent -- The constructor and `BuildLayout` can assume valid state and always produce visual output -- Reference implementation: `Projects/UOContent/Gumps/Go/GoGump.cs` - -## Converting Legacy Gump to DynamicGump / StaticGump - -The legacy `Gump` class (in `Gumps/Base/Legacy/Gump.cs`) builds layouts by appending `GumpEntry` objects to a list. The modern `DynamicGump` and `StaticGump` use ref struct builders that write directly to buffers — fewer allocations, better performance. - -### Step-by-Step Conversion - -#### 1. Choose the target type - -| If the layout... | Convert to | -|---|---| -| Is the same structure every time (fixed elements, maybe some dynamic text) | `StaticGump` | -| Changes shape based on instance data (loops, conditionals that add/remove elements) | `DynamicGump` | - -When in doubt, use `DynamicGump` — it's simpler and still much better than legacy `Gump`. - -#### 2. Change the class declaration - -```csharp -// Legacy -public class MyGump : Gump - -// Modern — pick one: -public class MyGump : DynamicGump -public class MyGump : StaticGump -``` - -#### 3. Move layout code into BuildLayout - -Legacy gumps build their layout in the constructor. Modern gumps build it in `BuildLayout`: - -```csharp -// Legacy — layout in constructor -public class OldGump : Gump -{ - public OldGump(Mobile from) : base(50, 50) - { - AddPage(0); - AddBackground(0, 0, 400, 300, 5054); - AddLabel(20, 20, 0x480, "Hello"); - AddButton(20, 260, 4005, 4007, 1); - } -} - -// Modern DynamicGump — layout in BuildLayout -public class NewGump : DynamicGump -{ - private readonly Mobile _from; - - public override bool Singleton => true; - - private NewGump(Mobile from) : base(50, 50) - { - _from = from; - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 400, 300, 5054); - builder.AddLabel(20, 20, 0x480, "Hello"); - builder.AddButton(20, 260, 4005, 4007, 1); - } - - public static void DisplayTo(Mobile from) - { - from.SendGump(new NewGump(from)); - } -} -``` - -#### 4. Key API differences - -| Legacy `Gump` | Modern builder | -|---|---| -| `AddPage(0)` | `builder.AddPage()` (0 is the default) | -| `AddHtml(x, y, w, h, text, bg, scroll)` | `builder.AddHtml(x, y, w, h, text, background: bg, scrollbar: scroll)` | -| `Closable = false` | `builder.SetNoClose()` | -| `Draggable = false` | `builder.SetNoMove()` | -| `Resizable = false` | `builder.SetNoResize()` | -| `Disposable = false` | `builder.SetNoDispose()` | -| `AddLabel(x, y, hue, string)` | `builder.AddLabel(x, y, hue, ReadOnlySpan)` | -| `Intern(string)` / string list | Not needed — builder handles strings internally | - -#### 5. For StaticGump: extract dynamic text into placeholders - -If converting to `StaticGump` and some text varies per instance, replace those `AddLabel`/`AddHtml` calls with placeholder versions and fill them in `BuildStrings`: - -```csharp -// In BuildLayout: -builder.AddLabelPlaceholder(20, 20, 0x480, "playerName"); -builder.AddHtmlPlaceholder(20, 50, 360, 200, "description", false, true); - -// In BuildStrings: -protected override void BuildStrings(ref GumpStringsBuilder builder) -{ - builder.SetStringSlot("playerName", _from.Name); - builder.SetHtmlText("description", _description, "#FFC000", 4); -} -``` - -#### 6. Update OnResponse signature - -```csharp -// Legacy -public override void OnResponse(NetState sender, RelayInfo info) - -// Modern (RelayInfo is passed by ref) -public override void OnResponse(NetState sender, in RelayInfo info) -``` - -#### 7. Add DisplayTo and make constructor private - -Always add a static `DisplayTo` method and make the constructor private to prevent empty gumps (see Empty Gump Rule above). - -## When to Use Which - -| Scenario | Type | Reason | -|---|---|---| -| Confirmation dialog | StaticGump | Fixed layout, shown frequently | -| Warning prompt | StaticGump | Fixed layout | -| Settings menu | StaticGump | Fixed structure | -| Item list (variable length) | DynamicGump | Layout depends on data | -| Craft menu | DynamicGump | Player-specific recipes | -| Search results | DynamicGump | Variable result count | -| Vendor inventory | DynamicGump | Different items per vendor | - -## Key File References - -| File | Description | -|---|---| -| `Projects/UOContent/Gumps/Base/BaseGump.cs` | Abstract base class | -| `Projects/UOContent/Gumps/Base/StaticGump.cs` | Cached static gump | -| `Projects/UOContent/Gumps/Base/DynamicGump.cs` | Dynamic gump | -| `Projects/UOContent/Gumps/Base/StaticGumpBuilder.cs` | Static layout builder | -| `Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs` | Dynamic layout builder | -| `Projects/UOContent/Gumps/Base/GumpStringsBuilder.cs` | String slot builder | -| `Projects/UOContent/Gumps/Base/GumpLayoutBuilder.cs` | Shared layout methods | -| `Projects/UOContent/Gumps/Base/GumpSystem.cs` | Extension methods | -| `Projects/UOContent/Gumps/StaticWarningGump.cs` | Example static gump | diff --git a/dev-docs/ip-bans-and-allowlists.md b/dev-docs/ip-bans-and-allowlists.md deleted file mode 100644 index 40320efe5..000000000 --- a/dev-docs/ip-bans-and-allowlists.md +++ /dev/null @@ -1,236 +0,0 @@ -# IP Bans, Blocklists and Allowlists - -How a shard decides to refuse a connection, how it contributes bans to an external bouncer, and how an -operator exempts someone who was caught by mistake. - -If you are here because **a player cannot connect**, skip to [Unblocking a player](#unblocking-a-player). - -## The shape of it - -Two independent questions, deliberately separated: - -| Question | Answered by | Effect | -|---|---|---| -| Refuse this connection? | `IConnectionFilter` implementations, at accept | The socket is dropped | -| Tell the outside world about it? | `BanChannel` → `IBanReporter` implementations | CrowdSec, and from there an OS bouncer | - -`BanChannel` **never enforces** and filters **never report on each other's behalf**. Enforcement that -outlives the process belongs to the OS bouncer; the shard only contributes. - -### Refusing a connection - -Filters are consulted in registration order, first denial wins: - -| Filter | Source | Scope | -|---|---|---| -| `firewall` | `Configuration/firewall.json`, mutable in-game | Admin-curated, permanent | -| `blocklist` | `Configuration/ip-blocklist.txt` (millions of entries, opt-in) | Reputation feeds | -| `auto-denylist` | In-memory, 15 min | What this shard just caught misbehaving | - -### Contributing a ban - -`BanChannel.Report(address, ttl, reason)` → `BanExemptions.IsExempt` → if not exempt, fan out to every -reporter (`crowdsec`, `auto-denylist`). - -### Allowlists - -Two, with different authority: - -| List | Source | Revocable? | Covers | -|---|---|---|---| -| `ManualAllowlist` | every `ip-allowlist*.txt` (opt-in) | No — an operator said so | Blocking **and** escalation | -| `LoginAllowlist` | Earned by authenticating, 90-day TTL | Yes — 10 strikes/hour | Blocking **and** escalation | - -Both are consulted **only after the blocklist has already matched**, so a normal accept — the one an -attacker is trying to flood — pays nothing for them. The accept gate itself is deliberately allowlist-free: -a whitelist there could only turn a deny into an allow at the cost of a lookup on every accept, the -attacker's included. - -## Unblocking a player - -### 1. Find out what is actually blocking them - -```bash -# In the reputation blocklist? -grep -x "203.0.113.42" Distribution/Configuration/ip-blocklist.txt - -# A live external decision? (this is what survives a restart) -cscli decisions list --ip 203.0.113.42 - -# Admin-curated? -grep 203.0.113.42 Distribution/Configuration/firewall.json -``` - -If none of those match, they may be inside a **CIDR** in the blocklist, or held by the in-memory -`auto-denylist` — that one is not queryable and expires on its own within 15 minutes. - -### 2. Add them to the allowlist - -Set `"enabled": true` in `Configuration/ip-allowlist.json` first — it is off by default, so a shard that -has never written a carve-out does not poll for one. The shard logs a warning at startup if allowlist files -are present while the flag is off. - -Then one entry per line in `Distribution/Configuration/ip-allowlist.txt` — a bare address or a CIDR. This -file is yours; the generator creates it once and never rewrites it. - -``` -203.0.113.42 # shard owner, listed via a shared upstream address -198.51.100.0/24 # a whole range if the ISP rotates within it -``` - -With the flag on, the shard reloads within `reloadInterval` (60s default). **No restart, and no need to -re-run the generator.** From that point the address is neither blocked nor contributed. With the flag off -the generator still subtracts the file at generation time, so the address stops being *blocked* — but a -behavioural detection can still contribute it, which is the case the flag exists to cover. - -### 3. Clear any ban that already exists - -A config change cannot retract a ban that has already left the building: - -```bash -cscli decisions delete -i 203.0.113.42 -``` - -### 4. If it keeps coming back - -The shard is no longer contributing them, so a recurring ban is coming from CrowdSec's own sources (the -community blocklist, another watcher). Allowlist it there too: - -```bash -cscli allowlists create shard-staff -d "Known-good player addresses" -cscli allowlists add shard-staff 203.0.113.42 -``` - -### What will NOT work - -- **Deleting the CrowdSec decision alone.** If the address is still in `ip-blocklist.txt` and not - allowlisted, the next connection re-reports it within `promoteSuppression` (60s). -- **Editing `ip-blocklist.txt` by hand.** The next generator run rewrites the whole file. -- **`cscli allowlists` alone.** That is the enforcement layer. The shard's own accept gate sits upstream of - it and will still refuse the connection. - -## Why entries appear that should not - -Reputation feeds list shared consumer address space constantly. On CGNAT one public address fronts many -subscribers **at the same time**, so a single abusive customer gets the address listed and everyone else -behind it is blocked with them — and where leases rotate, a listing says little about whoever holds the -address now. This is near-universal on mobile carriers, satellite (Starlink) and WISPs, and common on -fixed-line broadband outside North America. - -A **carve-out** exempts a whole network. **None ship with ModernUO** — which providers to exempt depends on -where your players actually are, and a carve-out names a real network, so you build the ones you need: - -```powershell -# Exempt a CGNAT provider whose players keep getting listed -.\Export-IpBlocklist.ps1 -AddCarveout starlink -Asn 14593 - -# Later: bring every carve-out up to date with what those networks currently announce -.\Export-IpBlocklist.ps1 -RefreshCarveouts -``` - -That writes `ip-allowlist-starlink.txt` beside the blocklist, and every `ip-allowlist*.txt` there is -subtracted by the generator with no config edit. For the shard to read them too — which is what also stops -a carve-out address being *contributed* by a behavioural detection — set `enabled` in `ip-allowlist.json`; -it is off by default so no shard polls for files it never wrote. Starlink costs about 0.1% of the list. -Blank a file (keep the file) to reputation-block that network again; delete it to drop the carve-out. - -Carve-out files carry an `asn=` marker in their header, which is how `-RefreshCarveouts` finds them. A -hand-written allowlist has no marker and is never rewritten. - -**Do you need one?** If players report being blocked and they are on satellite, mobile, or an ISP short on -IPv4, probably yes. Find the ASN by looking up an address the network hands out on any public BGP lookup. - -Prefixes come from **announcements, not ownership records**. Registry data disagrees with what is actually -routed and silently caps result sets: ARIN whois returns at most 256 rows and gives per-customer /24s, and -`206.83.96.0/19` reads as APNIC in RDAP even though `206.83.96/21` is announced by Starlink. - -## Behavioural detection - -Verdicts the shard reaches by watching a connection, rather than by consulting a list: - -| Reason | Trigger | -|---|---| -| `rate-limit` | Too many connection attempts in the limiter's window | -| `silent-connect` | Reaped after `ConnectingSocketIdleLimit` (5s) having sent **zero bytes** | -| `invalid-seed` | Opened with a zero seed, which no real client sends | -| `foreign-protocol` | Positively identified as HTTP, TLS or SSH | - -`BanReasons.IsBehavioral` gates two things: only these may be exempted, and only these enter the -`auto-denylist`. It is an **opt-in list**, not "everything except `manual`" — a reason added later escalates -normally rather than silently inheriting an exemption. `manual` is never exempt and never auto-denied. - -Escalation is **immediate**, on the first detection: a 15-minute local hold plus a `badConnectDuration` -(4h) contribution. There is no N-connection threshold; the strike counter governs only revoking a -`LoginAllowlist` entry. - -The local hold runs 15 minutes from the **first** detection and is never extended by later ones, so an -address that keeps trying is released on schedule rather than held indefinitely. It does not get a free -run: the rate limiter sits *ahead* of the connection filters, so a flooder is re-reported and re-held on -its next attempt. Not refreshing is what keeps the holds in expiry order, which is what makes retiring -lapsed ones cost the number expiring rather than the number held. - -### What is deliberately NOT detected - -**Do not add rules based on arrival framing.** TCP has no message boundaries, so the network, the OS or a -middlebox can split the opening bytes anywhere regardless of what the client sent. A rule of the form -"these bytes must arrive together" is broken by construction and drops real players on poor links. This has -been tried and reverted before. - -**Do not treat unreadable payloads as hostile.** A legitimate client with encryption enabled when the shard -expects none sends a structurally perfect connection whose payload is noise — `LoginEncryption.ClientDecrypt` -is a byte-for-byte stream XOR, so it preserves length exactly while destroying content. This is why -detection asks "is this positively some *other* protocol?" rather than "is this a good UO client?": however -misconfigured a UO client is, it never sends `GET / HTTP/1.1`. - -**Timeouts are keyed on bytes-received, not elapsed time.** A connection that sent *something* and ran out -of time is far more likely a slow link than an attack. Banning those produces the worst failure mode -available: the player retries, trips the rate limiter, and compounds a bad connection into hours of being -firewalled off. Shortening the 5s handshake window has been tried and broke real players. - -## Known limits - -- **An allowlist cannot bootstrap.** A `LoginAllowlist` entry is only earned by getting in, so it can never - repair an existing false positive, and it is weakest on rotating CGNAT — a player whose lease moved is a - stranger again. `ManualAllowlist` is the fix for that, which is why it is manual — and opt-in, via - `ip-allowlist.json`. -- **A never-logged-in player on a shared address can still be caught**, for up to `badConnectDuration`, if a - co-tenant misbehaves. Accepted: it is 4h and self-healing. The cheapest lever is `badConnectDuration`. -- **`MaxConnections` (4096) is a hard ceiling.** The accept gate runs *after* the kernel completed the TCP - handshake, so a blocklist match saves the socket setup and the `NetState` slot but never the connection - itself. Only an upstream L4 proxy or edge scrubbing moves that cost off the shard. -- **The `auto-denylist` stops tracking at `maxEntries`.** Past it a detection still disconnects the - connection, but the address is not held, so it pays full detection cost on every reconnect instead of a - cheap accept-gate deny. The default is sized for the 50k–250k distinct-source floods seen in practice; a - flood past it wants upstream scrubbing rather than a larger cap, which only buys a longer on-loop scan. - -## Configuration - -| File | Controls | -|---|---| -| `bans.json` | `reportRateLimitTrips`, `autoBanDuration`, `reportBadConnects`, `badConnectDuration` | -| `blocklist.json` | `enabled` (default `false`), `file`, `reloadInterval`, `reportHits`, `banDuration`, `promoteSuppression` | -| `ip-allowlist.json` | `enabled` (default `false`), `files` (wildcards allowed), `reloadInterval` | -| `login-allowlist.json` | `enabled`, `file`, `ttl`, `flushInterval`, `escalateAfterStrikes`, `strikeWindow` | -| `auto-denylist.json` | `enabled`, `duration`, `maxEntries` (default `324,449` — sized for the floods seen in practice; see the remark on the setting before raising it) | -| `crowdsec.json` | `lapiUrl`, `machineId`, `password`, `origin`, `manualBanDuration`, `flushInterval`, `maxQueue` | -| `firewall.json` | Admin-curated entries | - -A shard fronted by an upstream proxy can disable all of it and register nothing. - -## Key files - -| File | Role | -|---|---| -| `Projects/Server/Network/IConnectionFilter.cs` | Accept-path gate contract | -| `Projects/Server/Network/ConnectionFilters.cs` | Filter registry + lifecycle | -| `Projects/Server/Network/ForeignProtocol.cs` | Positive identification of non-UO traffic | -| `Projects/Server/Network/Bans/BanChannel.cs` | Contribution fan-out + `IsExempt` seam | -| `Projects/Server/Network/Bans/BanReasons.cs` | Reason slugs + the behavioural opt-in set | -| `Projects/UOContent/Network/BanExemptions.cs` | Combines both allowlists into one answer | -| `Projects/UOContent/Network/Blocklist/BlocklistFilter.cs` | File-sourced blocklist filter | -| `Projects/UOContent/Network/Blocklist/ManualAllowlist.cs` | Operator carve-outs, read from the allowlist files | -| `Projects/UOContent/Network/LoginAllowlist/LoginAllowlist.cs` | Allowlist earned by authenticating | -| `Projects/UOContent/Network/AutoDenylist/AutoDenylist.cs` | Short-lived local hold | -| `Projects/UOContent/Network/CrowdSec/CrowdSecReporter.cs` | LAPI contribution sink | -| `Projects/UOContent/Network/Firewall/Firewall.cs` | Admin-curated firewall set | -| `tools/Export-IpBlocklist.ps1` | Blocklist generator + allowlist subtraction | diff --git a/dev-docs/networking-packets.md b/dev-docs/networking-packets.md deleted file mode 100644 index 639963cfd..000000000 --- a/dev-docs/networking-packets.md +++ /dev/null @@ -1,567 +0,0 @@ -# ModernUO Networking & Packets - -This document covers ModernUO's networking system, including outgoing and incoming packet patterns, SpanWriter/SpanReader, and NetState extensions. - -## Overview - -ModernUO uses a binary packet protocol for client-server communication. The system is built around: -- **Outgoing packets**: Static `Create*` methods + `Send*` extension methods on `NetState` -- **Incoming packets**: Function pointer handlers registered in `Configure()` -- **SpanWriter/SpanReader**: High-performance binary I/O using `Span` - -## Outgoing Packet Pattern - -### Step 1: Define Constants and Create Method - -```csharp -public static class OutgoingMyPackets -{ - public const int MyPacketLength = 12; // Fixed-size packet - - public static void CreateMyPacket(Span buffer, Serial target, int value) - { - if (buffer[0] != 0) // Already initialized guard - return; - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)12); // Packet length - writer.Write((ushort)0x99); // Sub-command - writer.Write(target); // Serial (4 bytes) - writer.Write((short)value); // Value (2 bytes) - } -} -``` - -### Step 2: Define Send Extension Method - -```csharp -public static void SendMyPacket(this NetState ns, Serial target, int value) -{ - if (ns.CannotSendPackets()) - return; - - var buffer = stackalloc byte[MyPacketLength].InitializePacket(); - CreateMyPacket(buffer, target, value); - ns.Send(buffer); -} -``` - -### Step 3: Call from Game Code - -```csharp -// Send to one player -mobile.NetState.SendMyPacket(target.Serial, 42); - -// Send to nearby players -foreach (var ns in mobile.GetClientsInRange(18)) -{ - ns.SendMyPacket(target.Serial, 42); -} -``` - -### Variable-Length Outgoing Packets - -```csharp -public static void SendMyDynamicPacket(this NetState ns, string name, int[] values) -{ - if (ns.CannotSendPackets()) - return; - - var length = 7 + name.Length * 2 + values.Length * 4; - var writer = new SpanWriter(stackalloc byte[length]); - - writer.Write((byte)0x99); // Packet ID - writer.Write((ushort)0); // Length placeholder - writer.WriteBigUniNull(name); // Unicode string - writer.Write((ushort)values.Length); - - foreach (var val in values) - writer.Write(val); - - writer.WritePacketLength(); // Fill in actual length at position 1-2 - ns.Send(writer.Span); -} -``` - -### Shared Buffer Pattern (Multiple Recipients) - -When sending the same packet to multiple players, create the buffer once: - -```csharp -public static void SendToNearby(Mobile source, int effectId) -{ - Span buffer = stackalloc byte[EffectPacketLength]; - buffer.InitializePacket(); - - foreach (var ns in source.GetClientsInRange(18)) - { - // CreateXxx checks buffer[0] != 0 to avoid re-initializing - CreateEffectPacket(buffer, source.Serial, effectId); - ns.Send(buffer); - } -} -``` - ---- - -## Incoming Packet Pattern - -### Step 1: Register Handler in Configure() - -```csharp -public static class IncomingMyPackets -{ - public static unsafe void Configure() - { - // Fixed-length packet (12 bytes, in-game only) - IncomingPackets.Register(0x99, 12, true, &MyHandler); - - // Variable-length packet (0 = variable) - IncomingPackets.Register(0x9A, 0, true, &MyDynamicHandler); - - // Out-of-game packet - IncomingPackets.Register(0x9B, 10, false, &LoginHandler); - - // Encoded packet (sub-command) - IncomingPackets.RegisterEncoded(0x28, true, &EncodedHandler); - } -} -``` - -### Step 2: Implement Handler - -```csharp -public static void MyHandler(NetState state, SpanReader reader) -{ - var from = state.Mobile; - if (from == null) - return; - - var targetSerial = (Serial)reader.ReadUInt32(); - var value = reader.ReadInt16(); - - var target = World.FindMobile(targetSerial); - if (target == null) - return; - - // Process packet... -} - -public static void MyDynamicHandler(NetState state, SpanReader reader) -{ - var from = state.Mobile; - if (from == null) - return; - - var name = reader.ReadBigUniSafe(); - var count = reader.ReadUInt16(); - - for (var i = 0; i < count; i++) - { - var val = reader.ReadInt32(); - // Process each value... - } -} -``` - -### Encoded Packet Handler - -```csharp -public static void EncodedHandler(NetState state, IEntity target, EncodedReader reader) -{ - // Encoded packets have a different signature - var from = state.Mobile; - if (from == null) - return; - - // Process... -} -``` - -### Registration Parameters - -```csharp -IncomingPackets.Register( - int packetID, // Packet identifier (0x00-0xFF) - int length, // Fixed length, or 0 for variable-length - bool inGameOnly, // Requires authenticated player - delegate* handler // Function pointer -); -``` - ---- - -## SpanWriter Reference - -High-performance ref struct for writing binary data. Defined in `Projects/Server/Buffers/SpanWriter.cs`. - -### Constructors - -```csharp -var writer = new SpanWriter(Span buffer); // Fixed buffer -var writer = new SpanWriter(stackalloc byte[64]); // Stack buffer -var writer = new SpanWriter(int capacity, bool resize = false); // Pooled buffer -``` - -### Integer Writes (Big-Endian by Default) - -```csharp -writer.Write(bool value); // 1 byte -writer.Write(byte value); // 1 byte -writer.Write(sbyte value); // 1 byte -writer.Write(short value); // 2 bytes, big-endian -writer.Write(ushort value); // 2 bytes, big-endian -writer.Write(int value); // 4 bytes, big-endian -writer.Write(uint value); // 4 bytes, big-endian -writer.Write(long value); // 8 bytes, big-endian -writer.Write(ulong value); // 8 bytes, big-endian -writer.Write(Serial serial); // 4 bytes (writes serial.Value) -``` - -### Little-Endian Variants - -```csharp -writer.WriteLE(short value); -writer.WriteLE(ushort value); -writer.WriteLE(int value); -writer.WriteLE(uint value); -``` - -### String Writes - -```csharp -// ASCII (1 byte per char) -writer.WriteAscii(string value); -writer.WriteAsciiNull(string value); // Null-terminated -writer.WriteAscii(string value, int fixedLength); - -// Latin-1 (1 byte per char, extended ASCII) -writer.WriteLatin1(string value); -writer.WriteLatin1Null(string value); -writer.WriteLatin1(string value, int fixedLength); - -// UTF-16 Big-Endian (UO standard for Unicode) -writer.WriteBigUni(string value); -writer.WriteBigUniNull(string value); -writer.WriteBigUni(string value, int fixedLength); - -// UTF-16 Little-Endian -writer.WriteLittleUni(string value); -writer.WriteLittleUniNull(string value); -writer.WriteLittleUni(string value, int fixedLength); - -// UTF-8 -writer.WriteUTF8(string value); -writer.WriteUTF8Null(string value); -``` - -### Utilities - -```csharp -writer.Write(ReadOnlySpan data); // Raw bytes -writer.Clear(int count); // Write zeros -writer.Seek(int offset, SeekOrigin origin); // Move position -writer.WritePacketLength(); // Fill length at position 1-2 -writer.EnsureCapacity(int capacity); // Grow buffer if needed -writer.Dispose(); // Return pooled buffer - -// Properties -writer.Position; // Current write position -writer.Capacity; // Buffer size -writer.Span; // ReadOnlySpan of written data -writer.RawBuffer; // Mutable Span of full buffer -``` - ---- - -## SpanReader Reference - -High-performance ref struct for reading binary data. Defined in `Projects/Server/Buffers/SpanReader.cs`. - -### Constructor - -```csharp -var reader = new SpanReader(ReadOnlySpan data); -``` - -### Integer Reads (Big-Endian by Default) - -```csharp -reader.ReadByte(); // 1 byte -reader.ReadBoolean(); // 1 byte (> 0 = true) -reader.ReadSByte(); // 1 byte signed -reader.ReadInt16(); // 2 bytes, big-endian -reader.ReadUInt16(); // 2 bytes, big-endian -reader.ReadInt32(); // 4 bytes, big-endian -reader.ReadUInt32(); // 4 bytes, big-endian -reader.ReadInt64(); // 8 bytes, big-endian -reader.ReadUInt64(); // 8 bytes, big-endian -``` - -### Little-Endian Variants - -```csharp -reader.ReadInt16LE(); -reader.ReadUInt16LE(); -reader.ReadUInt32LE(); -``` - -### String Reads - -```csharp -// Each has a "Safe" variant that filters control characters -reader.ReadAscii(int fixedLength = -1); -reader.ReadAsciiSafe(int fixedLength = -1); -reader.ReadLatin1(int fixedLength = -1); -reader.ReadLatin1Safe(int fixedLength = -1); -reader.ReadBigUni(int fixedLength = -1); -reader.ReadBigUniSafe(int fixedLength = -1); -reader.ReadLittleUni(int fixedLength = -1); -reader.ReadLittleUniSafe(int fixedLength = -1); -reader.ReadUTF8(int fixedLength = -1); -reader.ReadUTF8Safe(int fixedLength = -1); -``` - -### Utilities - -```csharp -reader.Seek(int offset, SeekOrigin origin); -reader.Read(Span destination); - -// Properties -reader.Position; // Current read position -reader.Length; // Total data length -reader.Remaining; // Bytes remaining -reader.Buffer; // ReadOnlySpan of full data -``` - ---- - -## Player-Facing Message APIs - -For chat, system messages, and overhead text, prefer the high-level convenience methods on `Mobile` and `Item` — they handle stackalloc sizing, packet buffer initialization, spatial queries, and visibility filtering for you. The underlying packets all live in `OutgoingMessagePackets`. - -### On `Mobile` - -```csharp -// Self-message (sent only to this mobile's NetState) -mob.SendMessage(int hue, ReadOnlySpan text); -mob.SendAsciiMessage(int hue, ReadOnlySpan text); -mob.SendLocalizedMessage(int number, ReadOnlySpan args = default, int hue = 0x3B2); -mob.SendLocalizedMessage(int number, bool append, ReadOnlySpan affix, ReadOnlySpan args = default, int hue = 0x3B2); - -// Speech variants (overhead text from this mobile, broadcast in range) -mob.Say(ReadOnlySpan text); // SpeechHue -mob.Say(int number, ReadOnlySpan args = default); -mob.Emote(ReadOnlySpan text); // EmoteHue -mob.Whisper(ReadOnlySpan text); // WhisperHue, short range -mob.Yell(ReadOnlySpan text); // YellHue, long range - -// Targeted overhead messages -mob.PublicOverheadMessage(MessageType type, int hue, bool ascii, ReadOnlySpan text, bool noLineOfSight = true, AccessLevel accessLevel = AccessLevel.Player); -mob.PublicOverheadMessage(MessageType type, int hue, int number, ReadOnlySpan args = default, bool noLineOfSight = true); -mob.PrivateOverheadMessage(MessageType type, int hue, int number, ReadOnlySpan args, NetState state); -mob.LocalOverheadMessage(MessageType type, int hue, bool ascii, ReadOnlySpan text); -mob.NonlocalOverheadMessage(MessageType type, int hue, int number, ReadOnlySpan args = default); -``` - -### On `Item` - -```csharp -item.PublicOverheadMessage(MessageType type, int hue, bool ascii, ReadOnlySpan text); -item.PublicOverheadMessage(MessageType type, int hue, int number, ReadOnlySpan args = default); -item.SendLocalizedMessageTo(Mobile to, int number, ReadOnlySpan args = default); -item.SendLocalizedMessageTo(Mobile to, int number, int hue, ReadOnlySpan args = default); -item.SendMessageTo(Mobile to, ReadOnlySpan text, int hue = 0x3B2); -``` - -### Direct `NetState` extensions - -When you have a `NetState` and need full control (custom serial, body, font, language): - -```csharp -ns.SendMessage(Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, string lang, ReadOnlySpan name, ReadOnlySpan text); -ns.SendMessageLocalized(Serial serial, int graphic, MessageType type, int hue, int font, int number, ReadOnlySpan name = default, ReadOnlySpan args = default); -ns.SendMessageLocalizedAffix(Serial serial, int graphic, MessageType type, int hue, int font, int number, ReadOnlySpan name, AffixType affixType, ReadOnlySpan affix = default, ReadOnlySpan args = default); -``` - -### Zero-allocation interpolation overloads - -Every method above has a `ref RawInterpolatedStringHandler` overload for the text/args parameter. When the call-site argument is a `$"..."` literal, the compiler picks the handler overload and the message text is rendered directly into a pooled `char[]` — no `string` allocation: - -```csharp -mob.SendMessage($"You have {gold:N0} gold and {bounty:N0} bounty"); -mob.Say($"Hello, {target.Name}!"); -item.SendLocalizedMessageTo(player, cliloc, $"{a}\t{b}"); -mob.PublicOverheadMessage(MessageType.Regular, hue, false, $"I am {mob.Name}"); -``` - -When the argument is a pre-built `string` or `ReadOnlySpan` variable, the `ROS` overload is selected via implicit conversion — also fine, just doesn't get the zero-alloc benefit. - -For methods with two text parameters (`SendLocalizedMessageTo` with affix, `SendLocalizedMessage` with append), only `args` has a handler overload — `affix` stays `ROS` because it's typically a short literal. - -**Critical caveat:** call-site shapes like ternaries, switch expressions, pre-built locals, and `.ToString()` inside the hole silently defeat the handler overload selection. See the [Interpolation Anti-Patterns](string-handling.md#interpolation-anti-patterns) section in the string-handling doc for the full list and the fixes. - -### Lowercase format specifier - -`RawInterpolatedStringHandler` recognizes `:L` to lowercase a value's output (using `MemoryExtensions.ToLowerInvariant`). Useful for enum names in player-facing text: - -```csharp -mob.SendMessage($"You earned a {rank:L} trophy!"); // "gold" not "Gold" -``` - -See [`dev-docs/string-handling.md`](string-handling.md#rawinterpolatedstringhandler) for full coverage. - -### Implementation notes - -- `Mobile.PublicOverheadMessage` and `Item.PublicOverheadMessage` route through generic `OutgoingMessagePackets.BroadcastMessage*` helpers (in `OutgoingMessagePackets.Broadcast.cs`) parameterized over a `private readonly struct` filter that encapsulates the per-method visibility predicate (`CanSee`, `InLOS`, `AccessLevel`, `!= self`). The `where TFilter : struct, IBroadcastFilter` constraint specializes per filter type and keeps the dispatch zero-allocation (no boxing, no virtual call — JIT inlines the predicate). -- The convenience methods themselves live in `Mobile.Messages.cs` and `Item.Messages.cs` partial files for organization. - ---- - -## Common Existing Send Methods - -### Effects and Sounds -```csharp -ns.SendSoundEffect(int soundID, IPoint3D target); -ns.SendMobileAnimation(Serial mobile, int action, int frames, int repeat, bool forward, bool loop, int delay); -ns.SendNewMobileAnimation(Serial mobile, int action, int frames, int delay); -``` - -### Mobile Status -```csharp -ns.SendMobileHits(Mobile m, bool normalize = false); -ns.SendMobileMana(Mobile m, bool normalize = false); -ns.SendMobileStam(Mobile m, bool normalize = false); -ns.SendMobileAttributes(Mobile m, bool normalize = false); -ns.SendMobileStatus(Mobile m); -ns.SendMobileName(Mobile m); -ns.SendMobileMoving(Mobile source, Mobile target); -ns.SendBondedStatus(Serial serial, bool bonded); -ns.SendDeathAnimation(Serial killed, Serial corpse); -``` - -### Damage -```csharp -ns.SendDamage(Serial serial, int amount); -``` - -### Targeting -```csharp -ns.SendTargetReq(Target target); -ns.SendMovementRej(int sequence, Mobile m); -``` - ---- - -## Protocol Notes - -- **Endianness**: UO protocol is big-endian by default -- **Packet ID**: First byte identifies the packet type (0x00-0xFF) -- **Length**: For variable-length packets, bytes 1-2 are the total length (big-endian ushort) -- **Serials**: 4-byte identifiers for items (0x40000000+) and mobiles (0x00000001+) -- **Clilocs**: 4-byte localized string IDs - -## Best Practices - -1. **Always check `ns.CannotSendPackets()`** before sending -2. **Use `stackalloc`** for fixed-size packets (avoids heap allocation) -3. **Use `InitializePacket()`** extension on stackalloc spans -4. **Use `ReadAsciiSafe`/`ReadBigUniSafe`** for incoming strings (filters control chars) -5. **Use `WritePacketLength()`** for variable-length packets -6. **Big-endian by default** -- only use `WriteLE`/`ReadLE` when the protocol requires it -7. **Function pointers** (`&Handler`) for incoming packet registration (no delegate allocation) - -## Connection Filtering (Accept Path) - -Every inbound socket is checked before it becomes a `NetState`. The check runs on the game loop once -per accepted connection -- this is the path that has to survive a DDoS -- so it must be allocation-free -and non-blocking. - -Gates plug in through `IConnectionFilter`, registered with `ConnectionFilters.Register()` during the -Configure sweep: - -```csharp -public sealed class MyFilter : IConnectionFilter -{ - public string Name => "my-filter"; - public void Configure() { /* read config, no I/O */ } - public void Start(CancellationToken token) { /* background hydration */ } - public void Stop() { } - public bool ShouldDeny(IPAddress address) => /* allocation-free membership test */; -} - -// In a static Configure() so the sweep finds it: -ConnectionFilters.Register(new MyFilter()); -``` - -Rules: - -- `ShouldDeny` must be **allocation-free**, O(log n) at worst, no I/O, no blocking. Anything expensive - (parsing, reloading, contributing to an external service) belongs off the loop or behind a bounded, - non-blocking enqueue. -- Side effects a hit implies (reporting to `BanChannel`, promoting to an OS firewall, suppressing - duplicate reports) are the **filter's** business, not the accept path's. -- Filters are consulted in registration order and the first denial short-circuits, so register the - cheapest and most selective first. Order affects only how quickly a denial is reached, never whether - one happens. -- A filter that throws is **unregistered** and the connection fails open. A filter that faults once - faults for every connection, so leaving it registered would mean an exception per accept. - -Core owns the question; **every implementation lives in UOContent**. The three that ship are `firewall` -(admin-curated, mutable at runtime, persisted to `Configuration/firewall.json`), `blocklist` (file-sourced, -millions of entries, demand-pages hits to CrowdSec, **opt-in**) and `auto-denylist` (in-memory, -short-lived, fed by the shard's own behavioural detections). A shard that fronts its server with an -upstream proxy or edge scrubbing can drop all of them and register nothing. - -The allowlists, ban contribution, behavioural detection and the operator process for exempting a -false-positive address are covered separately in -[`dev-docs/ip-bans-and-allowlists.md`](ip-bans-and-allowlists.md). - -Do **not** route this kind of check through `EventSink.InvokeSocketConnect` -- that fires later and -allocates a `SocketConnectEventArgs` per connection, which is exactly what the accept path avoids for -rejected traffic. - -### IP Address Normalization (`IPAddressUtility`) - -Addresses are normalized to `UInt128` in **IPv6 form** so a single comparison/index works for both -families. An IPv4 address becomes its v4-mapped-v6 value (`::ffff:a.b.c.d`), which is why round-tripping -matters: `IPv4 -> UInt128 -> IPAddress` can come back as `InterNetworkV6` with `IsIPv4MappedToIPv6` -set, even though it is "really" a v4 address. Code that switches on `AddressFamily` alone will mis-handle -those, so the helpers check both. - -> **Known wart / follow-up:** `ToUInt128` guards with `AddressFamily == InterNetwork && !IsIPv4MappedToIPv6`. -> Per BCL semantics `IsIPv4MappedToIPv6` is only ever true for `InterNetworkV6`, so the second clause -> reads as redundant -- it is really defending the round-trip described above. The normalization would be -> clearer as an explicit "to canonical v6 bits" step that never needs the family check at all. Deliberately -> left as-is; to be revisited in a follow-up PR rather than churned mid-feature. - -## Key File References - -| File | Description | -|---|---| -| `Projects/Server/Buffers/SpanWriter.cs` | SpanWriter ref struct | -| `Projects/Server/Buffers/SpanReader.cs` | SpanReader ref struct | -| `Projects/Server/Network/Packets/IncomingPackets.cs` | Packet registration | -| `Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs` | Player packet handlers | -| `Projects/UOContent/Network/Packets/IncomingMovementPackets.cs` | Movement handlers | -| `Projects/UOContent/Network/Packets/IncomingMessagePackets.cs` | Speech handlers | -| `Projects/UOContent/Network/Packets/IncomingItemPackets.cs` | Item handlers | -| `Projects/UOContent/Network/Packets/IncomingTargetingPackets.cs` | Targeting handlers | -| `Projects/Server/Network/Packets/OutgoingMobilePackets.cs` | Mobile packets | -| `Projects/Server/Network/Packets/OutgoingItemPackets.cs` | Item packets | -| `Projects/Server/Network/Packets/OutgoingDamagePackets.cs` | Damage packets | -| `Projects/Server/Network/Packets/OutgoingEffectPackets.cs` | Effect/sound packets | -| `Projects/Server/Network/Packets/OutgoingAccountPackets.cs` | Account packets | -| `Projects/Server/Network/Packets/OutgoingContainerPackets.cs` | Container packets | -| `Projects/Server/Network/PacketHandler.cs` | PacketHandler class | -| `Projects/Server/Network/IConnectionFilter.cs` | Accept-path gate contract | -| `Projects/Server/Network/ConnectionFilters.cs` | Filter registry + lifecycle | -| `Projects/UOContent/Network/Firewall/Firewall.cs` | Admin-curated firewall set | -| `Projects/Server/Utilities/IPAddressUtility.cs` | IPAddress <-> UInt128 normalization, CIDR parsing | -| `Projects/UOContent/Network/Blocklist/BlocklistFilter.cs` | File-sourced blocklist filter | -| `Projects/UOContent/Network/LoginAllowlist/LoginAllowlist.cs` | Allowlist earned by a recent successful login | -| `Projects/UOContent/Network/AutoDenylist/AutoDenylist.cs` | Short-lived local hold on behavioural detections | -| `Projects/Server/Network/Bans/BanReasons.cs` | Ban reason slugs + the behavioural opt-in set | -| `Projects/Server/Network/ForeignProtocol.cs` | Positive identification of non-UO traffic (HTTP/TLS/SSH) | diff --git a/dev-docs/pathfinding.md b/dev-docs/pathfinding.md deleted file mode 100644 index 861a41d75..000000000 --- a/dev-docs/pathfinding.md +++ /dev/null @@ -1,304 +0,0 @@ -# Pathfinding architecture, configuration, and tuning levers - -How creatures navigate in ModernUO: the algorithm, the static-walkability cache, the AI -integration, the configuration knobs, and what to tell shard owners on different hardware. -Written so a future contributor (human or AI) can reason about the system without re-deriving -it from the code. - -## The stack (top to bottom) - -``` -BaseAI movement (AIMovement.cs: MoveTo / WalkMobileRange → ApproachTarget) - │ builds/keeps a - ▼ -PathFollower (Engines/Pathing/PathFollower.cs) — owns a MovementPath, walks it, repaths - │ constructs a - ▼ -MovementPath (Engines/Pathing/MovementPath.cs) — thin wrapper; calls the algorithm once - │ calls - ▼ -BitmapAStarAlgorithm.Find (Engines/Pathing/BitmapAStarAlgorithm.cs) — windowed A* - │ per cell expansion asks - ▼ -StepCache (Engines/Pathing/Cache/) — static per-chunk walkability bitmaps (+ optional .swb) - │ on cache miss / non-default walker, falls through to - ▼ -MovementImpl.CheckMovement (the per-direction "slow path") -``` - -There is **one** pathfinding algorithm now: `BitmapAStarAlgorithm`. The old `FastAStarAlgorithm` -was removed. Its behavior survives as the **slow path** inside BitmapAStar -(`GetSuccessorsSlowPath` → `CheckMovement`), which is what runs on a cache miss or when the -cache is disabled (see Levers). So "no cache" ≈ "old FastAStar", not a missing capability. - -## AI integration: `BaseAI.ApproachTarget` - -All creature goal-seeking funnels through one primitive (`AIMovement.cs`). `MoveTo` (combat -chase, AOS follow) and `WalkMobileRange` → `MoveTowardsOrAwayFrom` (pet come/follow, most -combat AIs) both delegate to `ApproachTarget(target, run, range)`. - -Per think-tick decision: - -1. **Greedy fast path** — if no path is active and the target is in LOS, take one direct step. - It only counts as progress when the move *fully succeeds* (`MoveResult.Success`) **and** - actually gets closer. A blocked step or an auto-turn *sidestep* (`SuccessAutoTurn`) that - didn't reduce distance does **not** count — it falls through to the planner. (This is the - fix for the old "pace back and forth at a concave obstacle" bug: a sidestep used to be - mistaken for progress and discard the path.) -2. **Planner** — commit to a persistent `PathFollower` (kept across ticks, never discarded by a - greedy step) and follow it around the obstacle. -3. **Give-up / idle** — a best-distance stall counter (`ApproachGiveUpTicks = 40`, - `AIMovement.cs`) idles a creature that can't lower its closest-ever distance to a - **stationary** goal, so it stops shuffling on a genuinely unreachable target. A **moving** - goal (active chase) resets the baseline every tick and never gives up. - -Open terrain stays on the greedy fast path and never builds a `PathFollower` — pathfinding only -engages when greedy movement stalls. - -## The algorithm: windowed A* with hard limits - -`BitmapAStarAlgorithm` (`Find`) is a **bounded local** pathfinder, not a global one. Key -constants: - -| Constant | Value | Meaning | -|---|---|---| -| `AreaSize` | 38 | Search is a 38×38 box **centered on the midpoint of start+goal**. Every path cell must lie within ~19 tiles of that midpoint. | -| `MaxSearchNodes` | 1000 (settable) | Max node expansions per `Find` before bailing — a per-pathfind CPU bound on the single game thread. | -| `PlaneCount`/`PlaneHeight`/`PlaneOffset` | 13 / 20 / 128 | Z handling: 13 planes × 20 height, offset 128 → multi-Z (stairs) representable when it fits the window. | - -**Consequences (these are by-design limits, not bugs):** - -- The window is sized by the **straight-line** start↔goal span, *not* the route. When start and - goal are close but separated by a big obstacle (around a building, or one floor up via remote - stairs), the real detour can fall outside the 38-box and is unfindable at any budget. - Verified example: pet at `(1443,1568,30)` → owner upstairs `(1444,1566,50)` Trammel returns - `null` at every budget (300→3000). That class needs higher-level handling (waypoints, or pets - teleporting to master when they can't path — classic UO behavior), not a bigger A*. -- `MaxSearchNodes` only bounds **long / failed** searches; successful open paths terminate on - goal-found and never approach it. Sizing data (Release, BDN, `MaxSearchNodesBenchmarks`): - open path ~2 µs at every budget; a ~33-step indoor detour needs ≥ ~500 expansions (NULL - below); an unreachable search's cost rises then plateaus when it exhausts the window - (~1500–1700 nodes). **1000 is near-optimal**: above the ~500 needed for indoor detours, below - the window-exhaustion ceiling, so a failed search at 1000 costs ~79 µs (Release) and bails - before the ~185 µs full-exhaustion cost. Raising it past ~1500 buys nothing. - -Internal A* working buffers (`_nodes` ~18,772 entries, `_nodeStates`, `_path`, `_successors`, -`_openQueue`) are `static` singletons reused every call — **zero allocation**. The only -intentional per-call allocation is the returned `Direction[]` path (handed off to the -`PathFollower` and read across ticks until repath/arrival). - -## The StepCache (static walkability) - -`StepCache` stores, per map chunk, a precomputed bitmap of which of the 8 directions are -walkable from each cell plus the destination Z — so a default walker's cell expansion is one -`TryGetMask` lookup instead of 8 `CheckMovement` calls. Cells the cache can't model (multi-Z -fallthroughs, non-default walkers: fliers, non-GM players, swim/door/clip capabilities) fall -through to the slow path for that one cell. - -- **Warming is on-demand and second-touch gated.** A chunk is built when a *second* distinct - pathfind touches it (avoids building chunks a one-off search will never reuse). Until built, - touches fall through to the slow path. -- **Memory is bounded, not unbounded.** Resident chunks are LRU-capped at - `pathfinding.maxResidentChunks` (default 8192 ≈ ~40 MB). It ramps to the cap and *plateaus*; - it does not grow forever. -- **Steady-state win:** once warm, BitmapAStar is **2–5× faster than the old FastAStar** at zero - allocation (BDN `PathfindBenchmarks`, `LazyWarm`/`WarmNoFile` providers). -- **Cold cost:** with the cache *on* but never warm (every search a first-touch), it is ~1.15– - 1.25× *slower* than FastAStar (the per-cell probe overhead without payoff). This is the - transient first-pathfind-per-region case, not steady state. - -### `.swb` baked files (optional) - -`[PathBake` / `[PathCacheSave` write a `.swb` per map; `[PathCacheLoad` (and startup -auto-load) open them as **lazy** backing stores — only the header + chunk-offset index is read -up front (~16 B/chunk); individual chunks are fetched on demand and remain LRU-capped, so -**RAM stays bounded by `maxResidentChunks` regardless of file size.** The only thing a baked -file buys is **zero first-pathfind-after-boot latency** for a region (chunks reload from file -instead of being rebuilt by the runtime baker). - -**Disk cost (current, format v8):** the size-reduction roadmap (§ `.swb` size reduction) took -Trammel from ~565 MB to **17.9 MB**; the other facets are smaller, so all six together are on the -order of **tens of MB** — not the ~1.5–2 GB the uncompressed v2 format cost. Baking is now cheap -enough to **offer by prompt at first boot** (below) rather than being a heavy opt-in-only step. -Still don't *ship* prebaked `.swb` files (they're tile-data-version-specific and regenerate from -the client files anyway). - -### First-boot pre-bake prompt - -On the **first boot** (right after map selection) an interactive prompt offers to pre-bake the -`.swb` cache for the selected maps. The answer is stored in `modernuo.json` as -**`pathfinding.prebakeMaps`** (default **false**), so it is asked exactly once; headless/CI boots -(redirected input) skip the prompt and default to off — operators can set the flag directly. - -When the flag is set, `PathCacheCommands.Initialize()` bakes, at startup, any map whose `.swb` is -missing or **stale** (its tile-data fingerprint no longer matches — e.g. after a client/map -update). A fresh cache is a no-op, so only the first boot (or a post-update boot) pays the -several-minutes cost. Wiring: - -- The prompt runs in a dedicated `AssemblyHandler.Invoke("ConfigurePrompts")` startup phase — - after assemblies load (so content can register prompts) but **before Serilog starts**, so the - console prompt is not interleaved with the async console sink. Any class can participate by - defining `public static void ConfigurePrompts()` and self-gating on first-boot state. -- Both the reader open and the bake run in `Invoke("Initialize")`, after the tile matrix and world - load. Neither belongs in `Configure`, which runs *before* both: the fingerprint hashes the map - files, so opening a `.swb` there would force the lazy `Map.Tiles` property and build every - `TileMatrix` ahead of `TileMatrixLoader` — possibly before `TileMatrix.Configure()` settles - `Pre6000ClientSupport`, since both sit at the default call priority and the phase sort is - unstable. `PathCacheCommands.Configure` is limited to settings and command registration. -- Staleness is decided by the `.swb` fingerprint, which `StepCacheFile.OpenForLazy` validates at - open time (hash of `tiledata.mul` + the per-map `.mul`/`.uop` files — never the in-memory - `TileData` tables, which the server patches at runtime). `Initialize` opens a reader for every - up-to-date file, then skips any map where `StepCache.HasLazyReader` is already true, so the - fingerprint is computed once per boot. Each newly baked map reopens only itself. - -## Configuration levers - -| Lever | Where | Default | Effect | -|---|---|---|---| -| `pathfinding.enable` | `PathFollower.Configure` | `true` | Master switch for `PathFollower` pathfinding. Off → greedy/auto-turn only, no A* at all. | -| `bitmap_pathfinding_cache` feature flag (`ContentFeatureFlags.BitmapPathfindingCache`, `Server.Systems.FeatureFlags`) | `FeatureFlagManager` | `true` | Off → `BitmapAStar` routes straight to the slow path with **no cache probe and no warming memory**. ≈ old FastAStar at ~1×. | -| `pathfinding.maxResidentChunks` | `PathCacheCommands.Configure` | 8192 (~40 MB) | LRU cap on resident chunks = the warming-memory ceiling. Lower it (e.g. 512–1024 ≈ 2.5–5 MB) on small shards. | -| `pathfinding.maxSearchNodes` | `BitmapAStarAlgorithm.Configure` → `BitmapAStarAlgorithm.MaxSearchNodes` | 1000 | A* per-Find node-expansion budget. See limits above; ~1000 is the sweet spot. | -| `pathfinding.prebakeMaps` | `PathCacheCommands` (first-boot prompt + `Initialize`) | `false` | When set, bakes any missing/stale `.swb` for the selected maps at startup (fingerprint-gated, so a fresh cache is a no-op). Set interactively by the first-boot prompt. | -| `PathFollower` `RepathDelay` | `PathFollower.cs` (const) | 2 s | Throttle: a moving goal re-`Find`s at most ~once per 2 s; a stationary reachable goal is pathed once and reused until arrival. Not a setting (compile-time). | - -### Default configuration (recommended) - -Cache **on**, warm-on-demand, **no `.swb`**. Most shards get the 2–5× steady-state win for -free, with warming memory plateau-capped at ~40 MB. `.swb` baking stays opt-in for large shards -that care about first-pathfind-after-boot latency and can spend ~1.5–2 GB of disk. - -### Small / crappy-hardware shards — the spectrum - -| Config | Perf | Memory | Disk | -|---|---|---|---| -| `bitmap_pathfinding_cache = false` | ≈1× (old FastAStar) | **0** cache RAM | 0 | -| Cache on, `maxResidentChunks` low (~512) | ~2–5× on hot regions | ~few MB | 0 | -| Cache on, default cap (8192) | 2–5× warm | ~40 MB plateau | 0 | -| Cache on + baked `.swb` | + zero first-pathfind-after-boot latency | ~40 MB + index | **~tens of MB** (v8) | - -The key point for RAM-starved boxes: **disabling the cache is not a regression** — it's the old -FastAStar behavior at ~1× with zero warming memory (the slow path does the same per-cell work, -and with the flag off there's no probe overhead). The `CacheOffBenchmarks` in the benchmark repo -exists to prove this (ratio ≈ 1.0 vs the vendored FastAStar baseline). - -## Diagnostics & tooling - -- **`[PathCacheStats`** — resident-chunk count + hit/miss/eviction telemetry. Watch - `evictions(lruCap)` on a live shard to see if the working set exceeds the cap. -- **`[PathCacheClear`** — drop residents + zero counters. -- **`[PathBake [mapId]` / `[PathCacheSave` / `[PathCacheLoad`** — produce / persist / lazy-open - `.swb` files (see disk cost above). -- **`[PathRecord on|off|flush`** — capture every `Find` as JSONL (replay / benchmark corpus). -- **`MapDump`** (`ModernUO-Tools/TerrainAnalyzer/`, see its `MapDump.md`) — offline UO - client-file inspector (`dump`/`scan`/`floors` modes) for diagnosing whether a route exists and - whether it fits the 38-tile window. Read raw geometry, then confirm with a real `Find` in a - test. -- **Benchmarks** (`ModernUO-Benchmarks/Benchmarks/PathfindInGame/`, BenchmarkDotNet, Release): - `PathfindBenchmarks` (BitmapAStar vs FastAStar × 4 cache providers × corpus), - `MaxSearchNodesBenchmarks` (budget sweep), `CacheOffBenchmarks` (flag-off ≈ FastAStar). Use - these to validate any change to the defaults above against the recorded corpus. - -## Testing notes - -- Pathfinding tests live in `Projects/UOContent.Tests/Tests/Engines/Pathing/` and - `.../Mobiles/AI/ApproachTargetTests.cs`, in the `Sequential Pathfinding Tests` collection - (the A* statics are not reentrant). They run against the live Trammel `TileMatrix`. -- Run in **Debug or Release** — both work since the `PathfindingTestFixture` data-copy was fixed - to use a project-relative path (`UOContent.Tests.csproj`); historically Release threw an NRE - because `Data/skills.json` wasn't copied to the Release bin. - -## Future work - -### Cache / bake (deferred follow-ups) - -- **Background-thread bake.** Build chunks off the game thread so even *promoted* chunks don't - pay the ~700 µs build cost on the main thread. Rule 10 (no `Task.Run`/`new Thread()` in game - code) applies — the bake itself is a pure data transform, but the main-thread synchronization - on chunk-state transitions (resident map insert, LRU bookkeeping, generation gate) has to be - threaded through carefully. Needs its own design; defer. -- **Long-traverse BDN scenario.** A multi-`Find` benchmark simulating ~50 pet repaths across - chunk transitions, to exercise the gate under sustained cross-chunk movement. Requires - restructuring the bench harness (it's currently one `Find` per scenario); the existing - corpus + `Cold` provider already covers the single-Find gate, so this is lower priority. -- **Swim `SourceZ` bake.** The corpus sea-serpent scenario shows ~56 B allocation on warm paths - because the cache's `SourceZ` is computed under default-walker rules, so swim creatures fall - through to the slow path. Baking swim-aware source Z (or a swim stratum) would let them hit - the cache. Independent of the size-reduction work below. -### `.swb` size reduction (the ~565 MB → tens of MB roadmap) - -The v2 format stores every 16×16 chunk as a flat ~5,393-byte record, uncompressed, with no -uniform-region elision — so Trammel's ~114,688 chunks × 5.4 KB ≈ ~565 MB, and ocean / Green -Acres / void cost the same as dense dungeon. Three **independent, separately-shippable** wins, -all of which preserve seekable random reads (compress at chunk/sector granularity, never -whole-file) and bounded RAM (only touched chunks materialize, LRU-capped): - -1. **Uniform-chunk elision** (biggest ratio, lowest risk). A chunk whose 256 cells share one - mask + Z is stored as a few bytes (flag + mask + Z) instead of 5,393. Pairs with a two-level - **sector index** so a uniform super-sector (open ocean) collapses to a single entry — UO's - 16-tile sectors nest cleanly inside. -2. **Predictive (lossless) Z residuals** — *shipped as format v6.* Kills the bulk of the 16 - base directional Z arrays in Full chunks. Each array is stored as a **masked residual** - against the cell's **own `SourceZ`**: predict `mask bit ? SourceZ : 0` (the mask term matches - the baker, which leaves non-walkable directional slots at `0`), residual `= Z − predict` via - unchecked two's-complement (byte-exact for all inputs). A new u16 `ZArrayMask` flags which of - the 16 arrays differ from their prediction; an array that matches everywhere (flat terrain — - including partial-walkability coastlines with nonzero `SourceZ`) is **omitted entirely** and - synthesized from `mask + SourceZ` at read. Reconstruct `Z = predict + residual` → byte-identical - in-memory `StepChunk`, so **no runtime recompute and no risk of diverging from `MovementImpl`**. - Self-prediction (not a neighbor) keeps chunk independence. The residual *subtraction* itself - saves ~0 bytes (a residual is still 1 byte/cell); the win is the per-array elision, and leaving - non-elided arrays in residual form makes #3 a pure codec add (no further transform/format bump). - Swim-layer + strata trailers stay absolute, deferred to #3. -3. **Per-chunk block compression + index compaction.** - - **#3a — per-chunk compression: *shipped as format v7.*** Each record is libdeflate-compressed - independently (random access preserved; the reader inflates one chunk on read into a reused - buffer) behind a `u32 UncompressedLen` frame; tiny Uniform records that do not shrink are - stored raw (detected as on-disk payload length == UncompressedLen). Codec chosen by full-Trammel - spike: **libdeflate VeryHigh** beat zstd L19/22 (17.4 MB) and tied managed Brotli q11 (16.4 MB) - at **16.5 MB of compressed records**, with the *fastest* decompress (**1.83 µs/chunk**, vs zstd - 2.21) — and it is already the repo's packet codec (`LibDeflate.Bindings`), so no new dependency. - zstd's large-window advantage doesn't apply at 256-cell record granularity. Compression runs at - bake time only (offline); decompression is one-time per chunk (LRU-cached after). **Calibrated: - v6 124.7 MB → v7 19.2 MB (−85%); −97% vs the original 565 MB.** - - **#3b — index compaction: *shipped as format v8.*** The v7 file's residual was the - uncompacted index (20 B/chunk × 114 K ≈ 2.3 MB). The trailer now stores only - `{ u32 packedKey = (ChunkX << 16) | ChunkY, u32 recordLength }` per chunk (8 B), in record - write order; the per-chunk file offset is dropped and reconstructed by cumulative `recordLength` - from `HeaderSize` at open. No record reordering, no varint — a fixed-stride, low-risk change to - the load-bearing index. **Calibrated: v7 19.2 MB → v8 17.9 MB.** (A varint/implicit-key scheme - could shave the index toward ~0.34 MB for another ~0.6 MB, at the cost of variable-stride - parsing and record reordering — not worth it on an already −97% file.) - -**Do first:** a uniformity audit over a baked facet (how many chunks are fully uniform / have -all-zero Z residuals?) to size the #1/#2 win before writing any format code. Each technique is a -clean v3 format bump; `MinSupportedVersion` already silently rejects + overwrites older files. -Validate size **and** read-latency vs the corpus in the benchmark repo after each. - -**Measured headroom (Trammel).** Two measurements, both 2026-06-06: - -- *Pre-swim audit (v2 spike, indicative):* directional-Z is **95.2% zero-residual for WalkZ**, - **38.1% for SwimZ** vs `SourceZ` (→ #2; swim wants its own predictor or leans on #3). The - spike's combined size projection double-counted and is superseded by the calibration below. -- *Calibrated on the real v4/v5 format (`SaveToFile`-measured):* 114,688 chunks; **62.7% fully - uniform** (swim-aware → #1 elides these), **8.9% carry a swim layer** and **1.8% strata** - (both stay Full). **#1 alone: 592.2 MB → 231.9 MB (−61%), actual on-disk.** The residual is - ~150 MB of non-uniform land Z-blocks (→ #2 predictive-Z) + ~81 MB of swim-layer trailers - (→ #2/#3). Confirms build order **#1 → #2 → #3**, with #1 the dominant, lowest-risk, - no-algorithm-change win (shipped as format v5). -- *Calibrated v6 (`BakeMap`-measured, full Trammel, `MaxResidentChunks` raised above 114,688):* - **#2 predictive-Z: 231.9 MB → 124.7 MB (−46%, −107 MB; −78% vs the original 565 MB).** Of the - 42,775 Full chunks (71,913 are Uniform), **walk directional-Z arrays elide 47.7%** and **swim - arrays elide 87.6%** (per-array, not per-cell — one slope cell keeps a 256-byte array, which is - why the per-array rate trails the 95.2% per-cell zero-residual). Remaining v6 bytes are dominated - by present walk-residual blocks (~46 MB, mostly ±1..3 + zeros → highly compressible), the - per-Full-chunk mask/SourceZ base (~33 MB), and absolute swim-layer trailers (~26 MB) — all prime - targets for #3 per-chunk compression. -- *Calibrated v7 (`BakeMap`-measured, full Trammel):* **#3a per-chunk libdeflate VeryHigh: 124.7 MB - → 19.2 MB (−85%); −97% vs the original 565 MB.** Codec spike (per-chunk over the 122.4 MB of v6 - records): libdeflate VeryHigh **16.5 MB** vs Brotli q11 16.4 / zstd L19–22 17.4; decompress - **1.83 µs/chunk** (libdeflate) vs 2.21 (zstd). The remaining ~2.3 MB is the uncompacted index - (→ #3b). -- *Calibrated v8 (`BakeMap`-measured, full Trammel):* **#3b index compaction (20 → 8 B/chunk): - 19.2 MB → 17.9 MB.** Roadmap end-to-end: **565 MB → 17.9 MB (−96.8%)** across #1 uniform elision - (v5) → #2 predictive-Z (v6) → #3a per-chunk libdeflate (v7) → #3b compact index (v8). diff --git a/dev-docs/platform-prerequisites.md b/dev-docs/platform-prerequisites.md deleted file mode 100644 index 3e76d3b47..000000000 --- a/dev-docs/platform-prerequisites.md +++ /dev/null @@ -1,159 +0,0 @@ -# Platform Prerequisites - -OS-level dependencies ModernUO needs at runtime, why each one is required, and what breaks without -it. This page is about software packages, not hardware sizing. - -Run `./build-tool --check-prereqs` from the repository root to check the current machine. It prints -the exact install command for the detected distribution. - -## What is required - -| Dependency | Platform | Why | -|---|---|---| -| .NET 10 Runtime | all | — | -| ICU (`libicuuc`, `libicui18n`) | Linux, macOS | The runtime refuses to start without it; see below | -| tzdata | Linux | Time zone lookups; see below | -| `libdeflate` | all | `LibDeflate.Bindings` | -| `libargon2` | all | `Argon2.Bindings` (password hashing) | -| VC++ Redistributable v14 | Windows | Native bindings | - -Not required, despite appearances: - -- **zstd** — `ZstdNet` bundles `libzstd` for every RID. -- **liburing** — `IORingGroup` issues `io_uring` syscalls directly. It imports only `libc`, - `libSystem.dylib`, `kernel32.dll`, `kernelbase.dll` and `ws2_32.dll`. -- **`-dev` / `-devel` packages** — see "Runtime packages only" below. - -## Install - -```sh -# Debian / Ubuntu (ICU has no stable package alias, so match it by pattern) -sudo apt-get install -y '^libicu[0-9]+$' libdeflate0 libargon2-1 tzdata - -# Fedora / RHEL -sudo dnf install -y libdeflate libargon2 libicu tzdata - -# Alpine -apk add --no-cache libdeflate argon2-libs icu-libs tzdata - -# macOS -brew install icu4c libdeflate argon2 -``` - -CentOS additionally needs EPEL and CRB: - -```sh -sudo dnf install -y epel-release epel-next-release && sudo dnf config-manager --set-enabled crb -``` - -## Runtime packages only - -Only the runtime packages are needed. The `-dev`/`-devel` packages are **not** required. - -They used to be, because .NET's `DllImport` probing looks for the unversioned `libfoo.so`, and on -Linux that bare symlink ships only in the development package. The runtime package ships the -versioned SONAME (`libdeflate.so.0`, `libargon2.so.1`). The binding packages now probe the versioned -names as well, so the runtime package is sufficient. - -Anything still documenting `libicu-dev` or `libdeflate-dev` as a requirement is out of date. - -## ICU - -`Directory.Build.props` sets `InvariantGlobalization=false`, so ICU is mandatory. Without it the -runtime does **not** throw — it `FailFast`s: - -``` -Couldn't find a valid ICU package installed on the system. Please install libicu (or icu-libs) -using your package manager and try again. -``` - -That is `SIGABRT` (exit 134) and it cannot be caught. Note the process **starts cleanly and aborts -later**, at whatever line first touches a culture, so the crash rarely points at the cause. - -### Why invariant mode is not an option - -`InvariantGlobalization=true` would remove the ICU dependency, but it changes behaviour in ways that -corrupt data silently. Measured on .NET 10 with the repository's settings: - -| Behaviour | With ICU | Invariant mode | -|---|---|---| -| `new CultureInfo("de-DE")` | real culture | succeeds, returns invariant data | -| de-DE decimal separator | `,` | `.` | -| `1234.5` as de-DE | `1.234,5` | `1,234.5` | -| `string.Compare("a", "B", InvariantCulture)` | `-1` (linguistic) | `31` (ordinal) | -| sort `[b, A, a, B]` | `a, A, b, B` | `A, B, a, b` | -| `FindSystemTimeZoneById("Eastern Standard Time")` on Linux | resolves | `TimeZoneNotFoundException` | -| UTF-8 round-trip of non-ASCII | unaffected | unaffected | - -The dangerous row is the first. Because `Directory.Build.props` also sets -`PredefinedCulturesOnly=false`, constructing a culture in invariant mode **succeeds** instead of -throwing `CultureNotFoundException`, and hands back an object populated with invariant data. Number -parsing and formatting then produce wrong values with no error, and culture-sensitive sort order -silently becomes ordinal. - -Encoding is not affected — UTF-8 round-trips correctly in both modes. - -### Version floor - -The runtime accepts `libicuuc.so.60` and above (`MinICUVersion` in `pal_icushim.c`). The prerequisite -checker enforces the same floor, so a host carrying only an older ICU is reported missing rather than -passing and then aborting at startup. RHEL/CentOS 7 ships ICU 50 and is affected. - -ICU tracks its own release train, so the SONAME digit varies widely by distribution — `.so.74` on -Ubuntu 24.04, `.so.76` on Alpine, `.so.77` on Fedora, `.so.78` on openSUSE. There is no stable -package alias on Debian and Ubuntu, which is why the checker resolves the name via `apt-cache` -instead of hardcoding one. - -Only `libicuuc` and `libicui18n` are used; those are the two names -`libSystem.Globalization.Native.so` loads. `libicudata` arrives as a dependency of `libicuuc`, and -`libicuio`/`libicutu`/`libicutest` are never referenced. Every distribution ships all of them in a -single package, so installing ICU at all satisfies both. - -## tzdata - -The event scheduler resolves configured zone IDs through `TimeZoneInfo`, which reads -`/usr/share/zoneinfo` on Linux. This is separate from ICU: it is data, not a library, so no loader -probe finds it, and slim container images routinely omit it. - -Without tzdata every lookup except `UTC` throws: - -``` -TimeZoneNotFoundException: The time zone ID 'America/New_York' was not found on the local computer. -``` - -`TimeZoneInfo.GetSystemTimeZones()` returns 1 entry instead of ~419, and `TimeZoneInfo.Local` falls -back to UTC. - -### There is no per-zone subset - -Distributions do not package individual zones — it is one `tzdata` package, about 2 MB installed for -the full set. Subsetting is not worth pursuing. - -The one split that does exist is **`tzdata-legacy`** on Debian 12 and Ubuntu 24.04, which carries the -deprecated aliases. With plain `tzdata` alone: - -| Zone ID | `tzdata` | `tzdata-legacy` | -|---|---|---| -| `America/New_York` | present | — | -| `Europe/Kyiv` | present | — | -| `EST5EDT` | present | — | -| `US/Eastern` | **missing** | present | -| `Asia/Calcutta` | **missing** | present | - -So a shard configured with a legacy alias such as `US/Eastern` throws on a current Debian or Ubuntu -even though tzdata is installed. Either install `tzdata-legacy` or switch the configured value to the -canonical ID (`America/New_York`, `Asia/Kolkata`). - -`TZDIR` is honoured if the data lives somewhere non-standard. - -## How the check works - -`--check-prereqs` asks the loader directly — `NativeLibrary.TryLoad` on the unversioned name, then -`libfoo.so.N` descending through the accepted range. - -It deliberately does not consult a package database or `ldconfig -p`. Both answer a different -question than "will `dlopen` succeed": - -- Package queries need a hardcoded name, which does not exist for ICU. -- `ldconfig`'s cache can be stale, omits `LD_LIBRARY_PATH`, and carries no version information to - enforce the ICU floor against. On musl it exits successfully while producing nothing usable. diff --git a/dev-docs/property-lists.md b/dev-docs/property-lists.md deleted file mode 100644 index 929429bcd..000000000 --- a/dev-docs/property-lists.md +++ /dev/null @@ -1,455 +0,0 @@ -# ModernUO Property Lists (Tooltips) - -This document covers ModernUO's property list system for item and mobile tooltips, including the IPropertyList interface, ObjectPropertyList internals, and patterns for customizing tooltips. - -## Overview - -Property lists (also called Object Property Lists or OPL) are the tooltip popups that appear when a player hovers over items and mobiles. They display the item name, stats, charges, and other relevant information. - -The system uses cliloc numbers (localized string IDs) with argument substitution to support multiple languages. - -## IPropertyList Interface - -Defined in `Projects/Server/PropertyList/IPropertyList.cs`: - -```csharp -public interface IPropertyList : ISelfInterpolatedStringHandler -{ - void Reset(); - void Terminate(); - - void Add(int number); // Cliloc number only - void Add(int number, string argument); // Cliloc with string argument - void Add(ReadOnlySpan argument); // Raw text, no string allocation - void Add(int number, ReadOnlySpan argument); // Cliloc with span argument - void AddChunked(ReadOnlySpan text); // Newline-joined text split across properties - OplTextBlock TextBlock(); // Builder that flushes via AddChunked on dispose - void Add(int number, int value); // Cliloc with int argument - void AddLocalized(int value); // Cliloc number as argument value - void AddLocalized(int number, int value); // Cliloc with localized argument - - // String interpolation support - void Add(ref InterpolatedStringHandler handler); - void Add(int number, ref InterpolatedStringHandler handler); -} -``` - -> There is no `Add(string)` overload. Pass raw text as a span (`Add(text.AsSpan())`) or, ideally, -> as an interpolated `$"..."` literal so the handler formats straight into the pooled buffer. - -## GetProperties Override - -Override `GetProperties()` to add custom tooltip lines: - -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); // ALWAYS call base first - - // Add cliloc with value - list.Add(1060741, $"{_charges}"); // "charges: ~1_val~" - - // Add raw string - list.Add($"{"Quality: "}{_quality}"); - - // Add cliloc with multiple tab-separated arguments - list.Add(1060637, $"{_current}\t{_max}"); // "~1_val~ / ~2_val~" - - // Add cliloc number only (no arguments) - list.Add(1049644); // "Crafted by a Grandmaster" - - // Add int argument - list.Add(1060741, _charges); // "charges: ~1_val~" -} -``` - -## Cliloc Argument Format - -Cliloc strings contain placeholders like `~1_val~`, `~2_val~`, etc. Multiple arguments are separated by tab characters (`\t`): - -```csharp -// Cliloc 1060637 = "~1_val~ / ~2_val~" -list.Add(1060637, $"{current}\t{max}"); - -// Cliloc 1072241 = "Contents: ~1_ITEMS~/~2_MAXITEMS~ items, ~3_WEIGHT~/~4_MAXWEIGHT~ stones" -list.Add(1072241, $"{TotalItems}\t{MaxItems}\t{TotalWeight}\t{MaxWeight}"); -``` - -### String Literals Must Be Holes (CRITICAL) - -The `IPropertyList` interpolated string handler distinguishes between **literals** (text between `{}` holes) and **holes** (values inside `{}`). Literals are treated as delimiters (like `\t`). Holes are treated as arguments. The property list system is used beyond just the game client — for example, web rendering — which must be able to tell arguments apart from delimiters. - -**This means string constants must always be wrapped as holes using `{"..."}` syntax:** - -```csharp -// BAD — "Chances" becomes a literal/delimiter, not an argument -list.Add(1060658, $"Chances\t{_charges}"); - -// GOOD — "Chances" is a hole, so it's treated as argument ~1_val~ -list.Add(1060658, $"{"Chances"}\t{_charges}"); -``` - -Real examples from the codebase (`Teleporter.cs`): -```csharp -// Cliloc 1060658 = "~1_val~: ~2_val~" -list.Add(1060658, $"{"Map"}\t{_mapDest}"); -list.Add(1060659, $"{"Coords"}\t{_pointDest}"); -list.Add(1060660, $"{"Creatures"}\t{(Creatures ? "Yes" : "No")}"); -list.Add(1060661, $"{"Range"}\t{_range}"); -``` - -The compiler generates different calls for each: -- `$"Map\t{value}"` → `AppendLiteral("Map\t")` then `AppendFormatted(value)` — "Map\t" is a delimiter, only `value` is an argument -- `$"{"Map"}\t{value}"` → `AppendFormatted("Map")` then `AppendLiteral("\t")` then `AppendFormatted(value)` — both "Map" and `value` are arguments, `\t` is the delimiter - -**Rule of thumb**: The only text that should appear as bare literals in the interpolated string is `\t` (the argument separator). Everything else — including string constants — must be inside `{}` holes. - -### No `.ToString()` Inside Holes - -`IPropertyList`'s interpolated string handler formats values directly into a pooled buffer via `ISpanFormattable.TryFormat` — no intermediate `string` allocation per hole. An explicit `.ToString()` defeats this: - -```csharp -// BAD — .ToString() allocates a string, then the handler copies its chars -list.Add(1060658, $"{"Charges"}\t{_charges.ToString()}"); - -// GOOD — the handler formats _charges directly with no intermediate string -list.Add(1060658, $"{"Charges"}\t{_charges}"); -``` - -Same applies to `.String()` on `TextDefinition`, `.GetValue()`, and any method that returns a freshly allocated `string` — drop the call and let the handler format the underlying value directly. - -The full list of interpolation anti-patterns (ternaries, switch expressions, pre-built locals, `string.Format`, concat, LINQ in holes) applies equally to `IPropertyList.Add($"...")`. See [`dev-docs/string-handling.md`](string-handling.md#interpolation-anti-patterns) for the full reference. - -### Cliloc as Argument (Use `:#` Format Specifier) - -When a cliloc argument is itself another cliloc number (i.e., the argument should resolve to localized text), use the `:#` format specifier on the integer — **not** a `"#number"` string: - -```csharp -// BAD — "#1060000" is a string, not a cliloc reference. -// Other systems (web rendering) will render it as the literal text "#1060000" -list.Add(1050039, $"{m_Amount}\t{"#1060000"}"); - -// GOOD — 1060000:# tells the handler this argument is a cliloc number to resolve -list.Add(1050039, $"{m_Amount}\t{1060000:#}"); -``` - -The `:#` format specifier is a hint that the value is a cliloc number. The handler calls `AppendFormatted(int value, string? format)` which, when `format == "#"`, resolves the cliloc and appends the localized text. Other consumers of the property list data (like web renderers) can see the `:#` format and know to look up the cliloc text rather than displaying a raw number. - -This also works with the `AddLocalized` convenience methods: -```csharp -// These use :# internally -list.AddLocalized(clilocNumber); // Single cliloc value as argument -list.AddLocalized(1050039, clilocNumber); // Cliloc with cliloc argument -``` - -### Looking Up Cliloc Text - -If you don't know what text a cliloc number maps to (and therefore what arguments it expects), you can read the `cliloc.enu` binary file. The loading logic is in `Projects/Server/Localization/Localization.cs`, method `LoadClilocs(string lang, string file)`: - -- File format: 6-byte header, then repeating entries of `int number` + `byte flag` + `ushort length` + UTF-8 text -- Placeholders in the text look like `~1_val~`, `~2_AMOUNT~`, etc. -- Ask the user where their `cliloc.enu` file is located (typically in the UO client data directory) - -## Multi-Line Free Text: `AddChunked` and `OplTextBlock` - -Some tooltips need a block of **free-form text** (not cliloc lookups) whose length is variable and -potentially large — e.g. a consolidated dump of every AOS attribute on an item, or staged -identification text that grows as the item is identified. Emitting that as a single property is -dangerous: - -> **The legacy 2D client copies each OPL property's text into a fixed ~512-char (1024-byte) buffer.** -> A single property longer than that smashes an adjacent world object's vtable on the client heap and -> crashes the client. `ObjectPropertyList.MaxArgumentLength = 504` is the safe per-property cap (a -> multiple of 8, comfortably under the empirically confirmed ~510-char ceiling). - -Two APIs handle this safely. Both split the text at `\n` boundaries into as many OPL properties as -needed, so no single property ever exceeds `MaxArgumentLength`. Each chunk is emitted through the -cycling **passthrough clilocs** (`1042971`, `1070722`, `1114057`, `1114778`, `1114779` — each -localized to a single `~1_val~`/`~1_NOTHING~` argument), which render the raw string verbatim. - -### `AddChunked` — the interface primitive - -`AddChunked(ReadOnlySpan)` is on `IPropertyList`, so it works from any `GetProperties(IPropertyList list)` -override. Give it `\n`-joined text; it breaks **only** at newlines: - -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); - - // _description may be hundreds of chars and contains embedded '\n's. - list.AddChunked(_description); -} -``` - -Splitting only at `\n` means each line stays intact across the chunk boundary — a chunk is flushed at -the last newline that keeps it under the cap. (A single line longer than `MaxArgumentLength` is the -one case that still gets clamped; the engine logs a warning naming the offending entity and cliloc.) - -### `OplTextBlock` — the ergonomic builder - -`OplTextBlock` is a `ref struct` builder that accumulates `\n`-joined lines and calls `AddChunked` for -you on dispose. Obtain it from `IPropertyList.TextBlock()` (so it works in any `GetProperties` override) -and always scope it with `using` so the flush happens: - -```csharp -using var block = list.TextBlock(); - -// Zero-alloc interpolated overload — formats directly into the pooled buffer: -block.Add($"Luck Bonus: +{luck}%"); -block.Add($"Damage: {min} - {max}"); - -// Plain text as a span (no string allocation): -block.Add("Cannot be repaired".AsSpan()); -``` - -Behavior worth knowing: - -- **Lines join with `\n`**; the trailing separator is stripped before the single `AddChunked` flush on `Dispose()`. -- **Empty lines are skipped** (`block.Add(ReadOnlySpan.Empty)` is a no-op). -- **No lines added → nothing is emitted** (no empty property, no wasted passthrough cliloc). -- The `Add($"...")` overload is a dedicated `[InterpolatedStringHandler]`, so interpolation allocates no - intermediate strings — the same anti-patterns as `IPropertyList.Add($"...")` apply (no `.ToString()` - in holes, no ternaries/`string.Format`/concat — see [`string-handling.md`](string-handling.md#interpolation-anti-patterns)). -- It is a `ref struct` tied to the single-threaded OPL build pass — never store it, capture it in a - closure, or use it across an `await`. - -### When to use which - -| Situation | Use | -|---|---| -| You already have a `\n`-joined string (e.g. a serialized description) | `list.AddChunked(text)` | -| You're building several free-text lines conditionally | `using var block = list.TextBlock();` then `block.Add(...)` | -| The content is a single short cliloc-backed property | Plain `list.Add(number, $"...")` — chunking is unnecessary | - -Real-world pattern (consolidated attribute lines, condensed from UOEvolution's `BaseWeapon`): - -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); - - using var block = list.TextBlock(); - - if (_attrs.HitLowerParryCap > 0) - { - block.Add($"Lower Parry Cap {_attrs.HitLowerParryCap}%"); - } - - if (_attrs.ParryBonusDamage > 0) - { - block.Add($"Parry Damage {_attrs.ParryBonusDamage}%"); - } - // ...any number of optional lines; the block flushes safely on dispose. -} -``` - -## Common Cliloc Numbers - -| Number | Text | Usage | -|---|---|---| -| 1042971 | `~1_val~` | Generic single value | -| 1060741 | `charges: ~1_val~` | Charge count | -| 1060637 | `~1_val~ / ~2_val~` | Current/max values | -| 1060658 | `~1_val~: ~2_val~` | Key: value pair | -| 1050044 | `~1_ITEMS~ items, ~2_WEIGHT~ stones` | Container contents (pre-ML) | -| 1072241 | `Contents: ~1~/~2~ items, ~3~/~4~ stones` | Container contents (ML+) | -| 1060776 | `~1_val~, ~2_val~` | Two comma-separated values | -| 1053099 | `damage ~1_val~ - ~2_val~` | Damage range | -| 1061170 | `animal lore ~1_val~` | Taming info | -| 1049644 | `Crafted by a Grandmaster` | Crafting quality | -| 1042001 | `That must be in your pack...` | Backpack requirement message | -| 1011036 | `OK` | OK button text | -| 1011012 | `CANCEL` | Cancel button text | -| 1060635 | `Warning` | Warning header | - -## Auto-Refresh with [InvalidateProperties] - -When using `[SerializableField]`, adding `[InvalidateProperties]` automatically calls `InvalidateProperties()` whenever the generated property setter is invoked: - -```csharp -[SerializableField(0)] -[InvalidateProperties] // Auto-refreshes tooltip when Charges changes -[SerializedCommandProperty(AccessLevel.GameMaster)] -private int _charges; -``` - -The generated setter becomes: -```csharp -public int Charges -{ - get => _charges; - set - { - _charges = value; - InvalidateProperties(); // Added by [InvalidateProperties] - this.MarkDirty(); - } -} -``` - -## Manual Refresh - -Call `InvalidateProperties()` when non-serialized state changes affect the tooltip: - -```csharp -public void UseCharge() -{ - _charges--; - InvalidateProperties(); // Force tooltip rebuild - this.MarkDirty(); -} -``` - -### Never Invalidate From Inside `GetProperties` (CRITICAL) - -`InvalidateProperties()` rebuilds the list **in place** — `Reset()`, then `GetProperties()` again on -the same instance. Calling it from a property getter that the build itself reaches is therefore -re-entrant, and `Reset()` does two destructive things to the build in flight: - -1. It returns the pooled interpolation scratch buffer. The compiler rents that buffer in the - interpolated-string handler's constructor and returns it in the closing `Add`, so **every hole is - evaluated while the buffer is live**. Pulling it out mid-append makes the next `Append*` span a - null array — `ArgumentNullException: Value cannot be null. (Parameter 'array')` thrown out of - `GetProperties`, from a line that looks unrelated to the getter that caused it. -2. It rewinds the packet cursor, so properties already written are overwritten by the nested pass. - -This is always a defect in the property getter, so the engine refuses rather than trying to recover: -a nested call logs an error with a stack trace, throws in `DEBUG`, and in `RELEASE` returns without -touching the list — leaving a possibly stale tooltip, but never a crash, a corrupted packet, or a -leaked pool buffer. Retrying the build would only hide the bug. - -```csharp -// BAD -- a getter with a side effect. Reading it from GetProperties re-enters the build. -public RankDefinition Rank -{ - get - { - if (_invalidateRank) - { - _rank = Recompute(); - _invalidateRank = false; - Invalidate(); // -> InvalidateProperties() -> Reset() on the list being built - } - - return _rank; - } -} - -// GOOD -- getters stay side-effect free; invalidate where the value actually changes. -public int RankIndex -{ - get => _rankIndex; - set - { - if (_rankIndex != value) - { - _rankIndex = value; - _invalidateRank = true; - Invalidate(); - } - } -} -``` - -Lazy recomputation inside a getter is fine — it is the *notification* that must not happen there. If -something genuinely must invalidate in response to a read, defer it off the build: - -```csharp -Timer.DelayCall(InvalidateProperties); -``` - -**Check when writing a `GetProperties` override**: every property it reads must be a pure read. - -## ObjectPropertyList Internals - -Defined in `Projects/Server/PropertyList/ObjectPropertyList.cs`: - -- **Packet ID**: 0xD6 -- **Hash-based updates**: Each property list has a hash. When `InvalidateProperties()` is called, the list is rebuilt and compared. Only if the hash changed is the new list sent to clients. -- **String building**: Uses `STArrayPool` for zero-GC string construction -- **Global toggle**: `ObjectPropertyList.Enabled` can disable the entire system -- **Lazy initialization**: Property lists are built on first access -- **Per-property cap**: `MaxArgumentLength` (504) bounds each property's text so the legacy 2D client's fixed tooltip buffer can't overflow. `AddChunked`/`OplTextBlock` keep multi-line content under it; anything that slips through is clamped with a logged warning - -### Update Flow -1. `InvalidateProperties()` is called -2. If map is valid and world isn't loading: - a. Save old hash - b. Reset and rebuild property list via `GetProperties()` - c. Compare new hash with old hash - d. If changed, queue delta update to clients via `Delta(ItemDelta.Properties)` - -## Era-Conditional Properties - -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); - - if (Core.ML) - { - if (ParentsContain()) - list.Add(1073841, $"{TotalItems}\t{MaxItems}\t{TotalWeight}"); - else - list.Add(1072241, $"{TotalItems}\t{MaxItems}\t{TotalWeight}\t{MaxWeight}"); - } - else - { - list.Add(1050044, $"{TotalItems}\t{TotalWeight}"); - } -} -``` - -## Mobile Properties - -Mobiles can also have property lists: - -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); - - if (Core.AOS && Faction != null) - { - list.Add(1060776, $"{Rank.Title}\t{Faction.Definition.PropName}"); - } - - if (_guildTitle != null) - list.Add($"[{_guildTitle}]"); -} -``` - -## Item Built-in Property Methods - -`Item` provides several helper methods called during property list building: - -```csharp -public virtual void AddNameProperty(IPropertyList list) // Item name -public virtual void AddLootTypeProperty(IPropertyList list) // Blessed/Cursed/etc. -public virtual void AddResistanceProperties(IPropertyList list) // Resistance values -public virtual void AddWeightProperty(IPropertyList list) // Weight display -public virtual void AddQuestItemProperty(IPropertyList list) // Quest item marker -public virtual void AddSecureProperty(IPropertyList list) // Secure container marker -``` - -## Best Practices - -1. **Always call `base.GetProperties(list)` first** -- it adds the item name and standard properties -2. **Use cliloc numbers** over raw strings when possible for localization support -3. **Use string interpolation** with `$""` for clean argument formatting -4. **Use tab (`\t`) to separate** multiple arguments in a single cliloc -5. **Don't call `InvalidateProperties()` in tight loops** -- it triggers hash computation and potential network sends -6. **Use `[InvalidateProperties]`** on serialized fields to automate refresh -7. **Check era** when properties differ between expansions - -## Key File References - -| File | Description | -|---|---| -| `Projects/Server/PropertyList/IPropertyList.cs` | Interface definition | -| `Projects/Server/PropertyList/ObjectPropertyList.cs` | Implementation | -| `Projects/Server/PropertyList/IObjectPropertyListEntity.cs` | Entity interface | -| `Projects/Server/Items/Item.cs` | Item.GetProperties, InvalidateProperties | -| `Projects/UOContent/Mobiles/PlayerMobile.cs` | Mobile property example | -| `Projects/Server/Items/Container.cs` | Era-conditional properties | diff --git a/dev-docs/regions.md b/dev-docs/regions.md deleted file mode 100644 index cf76dad00..000000000 --- a/dev-docs/regions.md +++ /dev/null @@ -1,567 +0,0 @@ -# ModernUO Region System - -This document covers ModernUO's region system: spatial areas on the map that control gameplay rules, spawning, spell restrictions, housing, combat, lighting, and more. - -## Overview - -Regions are named, polygonal volumes on a map. When a mobile enters a region, the server calls that region's virtual hooks to control behavior. Regions are hierarchical — a child region inherits its parent's behavior and can selectively override it. - -Regions are loaded from `Data/regions.json` at startup via polymorphic JSON deserialization, but they can also be created **dynamically** at runtime (e.g., house regions, champion spawn areas). - -## Class Hierarchy - -``` -Region (Server) ← Engine-level base, 30+ virtual hooks - └─ BaseRegion (UOContent) ← Game-level base: CheckTravel, spawn weights, RuneName - ├─ GuardedRegion ← NPC guards, vendor access, spell restrictions in town - │ ├─ NoHousingGuardedRegion ← GuardedRegion that also blocks housing overlap checks - │ └─ TownRegion ← Type marker for towns (adds Entrance property) - ├─ DungeonRegion ← Dungeon lighting, no housing, young not protected - │ └─ NoTravelSpellsAllowedRegion ← Blocks all travel spells for players - │ └─ MondainRegion ← Mondain's Legacy dungeon base - │ ├─ CrystalFieldRegion, IcyRiverRegion, ... (damage boost regions) - ├─ NoHousingRegion ← Blocks housing placement (manual overlap check) - ├─ GreenAcresRegion ← No housing, no travel, no Mark spell - ├─ JailRegion ← Full lockdown: no skills, spells, travel, combat - ├─ HouseRegion ← Dynamic: one per house, access/ban/lockdown control - ├─ ChampionSpawnRegion ← Dynamic: champion spawn area effects - └─ (many more specialized regions) -``` - -## Region (Server Engine) - -`Server.Region` is the engine-level base class in `Projects/Server/Regions/Region.cs`. It provides: - -### Key Properties - -| Property | Type | Description | -|---|---|---| -| `Name` | `string` | Region identifier (unique per map for JSON regions) | -| `Map` | `Map` | The map this region belongs to | -| `Parent` | `Region` | Parent region (null for top-level) | -| `Children` | `List` | Child regions nested inside this one | -| `Area` | `Rectangle3D[]` | Spatial bounds (one or more 3D rectangles) | -| `Priority` | `int` | Sort priority (default 50); higher = checked first | -| `ChildLevel` | `int` | Nesting depth (0 for top-level) | -| `Dynamic` | `bool` | True if created via constructor (not from JSON) | -| `Registered` | `bool` | Whether the region is active on the map | -| `GoLocation` | `Point3D` | Recall/bind target location | -| `Music` | `MusicName` | Music played for players in this region | - -### Sort Order - -Regions in each map sector are sorted by: -1. **Dynamic regions first** — runtime regions take precedence over JSON-defined ones -2. **Higher priority first** — `DefaultPriority` is 50 -3. **Higher child level first** — more deeply nested regions win - -This means a child region always takes precedence over its parent, and dynamic regions (like `HouseRegion`) override static JSON regions. - -### Static Lookup Methods - -```csharp -// Find the most specific region at a world location (fast — uses sector index) -Region region = Region.Find(point3D, map); - -// Find a named region on a map (slow — linear scan, use sparingly) -Region region = Region.Find("Britain", Map.Felucca); -``` - -### Hierarchy Traversal - -```csharp -// Walk up the parent chain looking for a specific type -var dungeon = region.GetRegion(); // null if not in a dungeon -var guarded = region.GetRegion(); - -// Check if this region is a child of (or equal to) a specific region/type -bool inDungeon = region.IsPartOf(); -bool inBritain = region.IsPartOf("Britain"); - -// Check two types at once (avoids double traversal) -bool inDungeonOrGuarded = region.IsPartOf(); -``` - -### Registration / Unregistration - -```csharp -region.Register(); // Adds to map sectors, Region.Regions list, parent's Children -region.Unregister(); // Removes from all of the above -``` - -Both are **idempotent** — calling `Register()` on an already-registered region is a no-op. - -`Register()` does: -1. Calls `OnRegister()` virtual -2. Adds self to `Parent.Children` (if parent exists) -3. Adds self to `Region.Regions` global list -4. Adds self to relevant `Map.Sector` lists -5. Stores sector references in `Sectors` array - -`Unregister()` does the reverse. **Warning**: unregistering a region that still has children logs a warning — unregister children first. - -### Querying Region Contents - -```csharp -// Get all players in this region (pooled — zero-alloc) -using var players = region.GetPlayersPooled(); - -// Get all mobiles in this region (pooled) -using var mobiles = region.GetMobilesPooled(); - -// Get count without allocating -int count = region.GetPlayerCount(); -``` - -## Virtual Hooks - -Region provides 30+ virtual methods. Each default implementation **delegates to Parent** (or returns a default if no parent). This means child regions automatically inherit parent behavior — you only override what you need. - -### Lifecycle - -| Method | Called when | -|---|---| -| `OnRegister()` | Region is registered on the map | -| `OnUnregister()` | Region is unregistered | -| `OnChildAdded(Region)` | A child region registers under this parent | -| `OnChildRemoved(Region)` | A child region unregisters | - -### Movement & Entry - -| Method | Called when | -|---|---| -| `OnMoveInto(Mobile, Direction, newLoc, oldLoc)` | Mobile attempts to enter; return false to block | -| `OnEnter(Mobile)` | Mobile enters this region | -| `OnExit(Mobile)` | Mobile leaves this region | -| `OnLocationChanged(Mobile, oldLoc)` | Mobile moves within the region | - -### Combat & Interaction - -| Method | Called when | -|---|---| -| `AllowHarmful(Mobile from, Mobile target)` | PvP/PvM harm attempt | -| `AllowBeneficial(Mobile from, Mobile target)` | Healing/buffing attempt | -| `OnAggressed(Mobile aggressor, Mobile aggressed, bool criminal)` | Aggression committed | -| `OnDidHarmful(Mobile harmer, Mobile harmed)` | Harmful action completed | -| `OnCriminalAction(Mobile, bool message)` | Criminal act committed | -| `OnCombatantChange(Mobile, old, new)` | Target change; return false to block | -| `SpellDamageScalar(Mobile caster, Mobile target, ref double damage)` | Modify spell damage | - -### Spells & Skills - -| Method | Called when | -|---|---| -| `OnBeginSpellCast(Mobile, ISpell)` | Spell cast attempt; return false to block | -| `OnSpellCast(Mobile, ISpell)` | Spell successfully cast | -| `OnSkillUse(Mobile, int skill)` | Skill use attempt; return false to block | -| `AllowGain(Mobile, Skill, object)` | Skill gain attempt; return false to block | - -### World Rules - -| Method | Called when | -|---|---| -| `AllowHousing(Mobile, Point3D)` | Housing placement check; return false to block | -| `AllowSpawn()` | Creature spawn check | -| `AcceptsSpawnsFrom(Region)` | Whether spawns from another region are accepted | -| `OnDecay(Item)` | Item decay; return false to prevent | -| `CheckAccessibility(Item, Mobile)` | Item access check; return false to deny | -| `GetResource(Type)` | Alter resource type for mining/harvesting | -| `MakeGuard(Mobile focus)` | Spawn a guard at a location | - -### Environment - -| Method | Called when | -|---|---| -| `AlterLightLevel(Mobile, ref int global, ref int personal)` | Modify light levels | -| `GetLogoutDelay(Mobile)` | Get logout timer duration | -| `CanUseStuckMenu(Mobile)` | Whether stuck menu is available | -| `OnSpeech(SpeechEventArgs)` | Speech in region (used for guard calls, house commands) | -| `OnResurrect(Mobile)` | Resurrection attempt; return false to block | -| `OnBeforeDeath(Mobile)` / `OnDeath(Mobile)` | Death handling | - -## BaseRegion (UOContent) - -`Server.Regions.BaseRegion` in `Projects/UOContent/Regions/BaseRegion.cs` extends `Region` with game-specific features: - -### Additional Properties - -| Property | Type | Default | Description | -|---|---|---|---| -| `YoungProtected` | `virtual bool` | `true` | Young players get warning gump on entry | -| `YoungMayEnter` | `virtual bool` | `true` | Young players allowed to enter | -| `MountsAllowed` | `virtual bool` | `true` | Mounts allowed in region | -| `DeadMayEnter` | `virtual bool` | `true` | Dead players can enter | -| `ResurrectionAllowed` | `virtual bool` | `true` | Resurrection permitted | -| `LogoutAllowed` | `virtual bool` | `true` | Logout permitted | -| `ExcludeFromParentSpawns` | `bool` | `false` | Blocks parent region spawners from placing here | -| `RuneName` | `string` | `null` | Name shown on recall runes | -| `NoLogoutDelay` | `bool` | `false` | Zero logout delay if not in combat | - -### CheckTravel - -`BaseRegion.CheckTravel` is the key hook for spell travel restrictions: - -```csharp -public virtual bool CheckTravel( - Mobile m, - Point3D newLocation, - TravelCheckType travelType, - out TextDefinition message) -``` - -`TravelCheckType` values: `RecallFrom`, `RecallTo`, `GateFrom`, `GateTo`, `Mark`, `TeleportFrom`, `TeleportTo`. - -Both the **current** region and the **destination** region are checked by `SpellHelper.CheckTravel`. If either returns false, the spell is blocked. - -### Spawn Distribution - -`BaseRegion` provides `InitRectangles()` which breaks overlapping `Area` rectangles into non-overlapping pieces and calculates weights for uniform spawn distribution. Used by `RegionSpawner`. - -## JSON Region Loading - -Regions are defined in `Distribution/Data/regions.json` and loaded at startup by `RegionJsonSerializer.LoadRegions()`. - -### JSON Schema - -```json -{ - "$type": "DungeonRegion", - "Name": "Shame", - "Map": "Felucca", - "Priority": 50, - "Parent": { "Name": "Felucca", "Map": "Felucca" }, - "Area": [ - { "x1": 5369, "y1": 1, "x2": 5627, "y2": 127 } - ], - "GoLocation": { "x": 511, "y": 1565, "z": 0 }, - "Music": "Dungeon9", - "Entrance": { "x": 511, "y": 1565, "z": 0 }, - "MinExpansion": "None", - "MaxExpansion": "EJ" -} -``` - -Key fields: -- `$type` — Polymorphic type discriminator (must be a registered type name) -- `Parent` — Resolved by `RegionByNameConverter` via `Region.Find(name, map)` -- `MinExpansion` / `MaxExpansion` — Region only loads if server expansion is within range -- `Area` — Array of 2D or 3D rectangles - -### Type Registration - -All region types that appear in JSON must be registered in `RegionJsonRegistration.Configure()`: - -```csharp -RegionJsonSerializer.Register(); -RegionJsonSerializer.Register(); -RegionJsonSerializer.Register(); -RegionJsonSerializer.Register(); -// ... 30+ more types -``` - -On deserialization, each region's `Register()` is called automatically if the server expansion is in range. - -## Child Regions - -Child regions are regions with a `Parent`. They form a hierarchy: - -``` -Felucca (default region) - └─ Britain (TownRegion, guarded) - └─ Britain Bank (BaseRegion, NoLogoutDelay) -``` - -### How Children Work - -1. **Behavior inheritance**: Every virtual hook delegates to `Parent` by default. A child only needs to override what it changes. -2. **Priority**: Children have higher `ChildLevel` than parents, so they're checked first in sector lookups. -3. **Entry/exit events**: When a mobile moves from parent to child (or vice versa), `OnExit` is called on regions being left and `OnEnter` on regions being entered, walking the hierarchy. -4. **`IsPartOf()`**: Walks up the parent chain. A region inside "Britain Bank" returns true for `IsPartOf()` because `TownRegion` (Britain) is a `GuardedRegion`. - -### When to Use Child Regions - -Use a child region when you need to **modify behavior within an existing region** without replacing it: - -- **Dungeon sub-areas**: A treasure room inside a dungeon that has different light or spawn rules -- **Town districts**: A bank area with no logout delay inside a guarded town -- **Boss arenas**: A champion spawn area inside a dungeon that adds lighting effects and player ejection -- **Restricted zones**: A no-spell zone within a larger dungeon - -**Example — dungeon chest with a localized effect zone**: - -If you need a dungeon chest that creates a "cursed area" around it (e.g., damage over time, spell restrictions), create a child region of the dungeon: - -```csharp -public class CursedChestRegion : BaseRegion -{ - private readonly CursedChest _chest; - - public CursedChestRegion(CursedChest chest) - : base(null, chest.Map, Region.Find(chest.Location, chest.Map), // parent = dungeon - new Rectangle2D(chest.X - 5, chest.Y - 5, 11, 11)) - { - _chest = chest; - } - - // Inherits DungeonRegion behavior (lighting, no housing, etc.) - // Only adds curse-specific effects - - public override void OnEnter(Mobile m) - { - base.OnEnter(m); - if (m.Player) - m.SendMessage("You feel a dark presence..."); - } - - public override void SpellDamageScalar(Mobile caster, Mobile target, ref double damage) - { - base.SpellDamageScalar(caster, target, ref damage); - damage *= 1.25; // 25% more spell damage in cursed area - } -} -``` - -Because this is a **child** of the dungeon region, it inherits all dungeon rules (lighting, no housing, young protection = false) and only adds the curse effect on top. - -## Dynamic Regions - -Dynamic regions are created at runtime by game code rather than loaded from JSON. They're identified by `Dynamic = true` (set automatically in the constructor) and sort before static regions. - -### Pattern 1: Item-Tracked Region (Register/Unregister) - -The most common pattern — an item creates a region around itself and manages its lifecycle: - -```csharp -public partial class ChampionSpawn : Item -{ - private ChampionSpawnRegion m_Region; - - public void UpdateRegion() - { - m_Region?.Unregister(); // Remove old region - - if (!Deleted && Map != Map.Internal) - { - m_Region = GetRegion(); // Create new region - m_Region.Register(); // Add to map - } - } - - public override void OnLocationChange(Point3D oldLoc) - { - // ... update spawn area coordinates ... - UpdateRegion(); // Re-register at new location - } - - public override void OnMapChange() - { - // ... update child items' maps ... - UpdateRegion(); // Re-register on new map - } - - public override void OnAfterDelete() - { - base.OnAfterDelete(); - // ... cleanup child items ... - UpdateRegion(); // Unregisters (Deleted == true) - } - - [AfterDeserialization(false)] - private void AfterDeserialization() - { - // Defer registration to next tick (world may not be fully loaded) - Timer.StartTimer(TimeSpan.Zero, UpdateRegion); - } -} -``` - -**Key points:** -- Call `UpdateRegion()` on `OnLocationChange`, `OnMapChange`, and `OnAfterDelete` -- In `AfterDeserialization`, defer registration with `Timer.StartTimer(TimeSpan.Zero, ...)` — regions rely on the map and parent regions being loaded first -- The `UpdateRegion()` method is idempotent: unregisters old, creates new if not deleted - -### Pattern 2: House Region - -Houses create a region with priority `DefaultPriority + 1` (higher than normal regions): - -```csharp -public virtual void UpdateRegion() -{ - m_Region?.Unregister(); - - if (Map != null) - { - m_Region = new HouseRegion(this); - m_Region.Register(); - } - else - { - m_Region = null; - } -} -``` - -### Pattern 3: Parent-Aware Dynamic Child - -When creating a dynamic region that should be a child of whatever region exists at a location: - -```csharp -// Find the existing region at the item's location and use it as parent -var parent = Region.Find(item.Location, item.Map); -var childRegion = new MyCustomRegion(item, parent); -childRegion.Register(); -``` - -The `ChampionSpawnRegion` constructor does exactly this: - -```csharp -public ChampionSpawnRegion(ChampionSpawn spawn) - : base(null, spawn.Map, Region.Find(spawn.Location, spawn.Map), spawn.SpawnArea) -``` - -This makes the champion spawn region a child of whatever region it's placed in (dungeon, wilderness, etc.), inheriting that region's rules. - -## Existing Region Types Quick Reference - -### Base Types (Use for Most Tasks) - -| Type | Inherits | Key Behavior | -|---|---|---| -| `BaseRegion` | `Region` | Game-level base; CheckTravel, RuneName, spawn weights | -| `GuardedRegion` | `BaseRegion` | NPC guards, no housing, town spell restrictions | -| `TownRegion` | `GuardedRegion` | Town marker (adds Entrance); inherits guard behavior | -| `DungeonRegion` | `BaseRegion` | Dungeon lighting, no housing, young not protected, no stuck menu on Felucca | -| `NoHousingRegion` | `BaseRegion` | Blocks housing placement (manual overlap check) | -| `NoHousingGuardedRegion` | `GuardedRegion` | Guards + no housing overlap check | -| `NoTravelSpellsAllowedRegion` | `DungeonRegion` | Blocks all travel spells for players | -| `GreenAcresRegion` | `BaseRegion` | No housing, no travel, no Mark | -| `JailRegion` | `BaseRegion` | Full lockdown: no skills, spells, combat, travel, or gain | -| `HouseRegion` | `BaseRegion` | Dynamic; per-house access control, lockdown, secure items | - -### Choosing a Base for Your Region - -| You Need | Inherit From | -|---|---| -| Standard overworld area with custom rules | `BaseRegion` | -| Town/city with guards | `TownRegion` or `GuardedRegion` | -| Dungeon area (dark, no housing) | `DungeonRegion` | -| Dungeon + no travel spells | `NoTravelSpellsAllowedRegion` | -| No housing only | `NoHousingRegion` | -| Full spell/skill lockdown | `JailRegion` (or custom `BaseRegion`) | -| Item-controlled dynamic area | `BaseRegion` (with parent = `Region.Find(...)`) | - -### Specialized Regions (Registered for JSON) - -These are used in `regions.json` for specific areas and can serve as references for custom regions: - -| Type | Purpose | -|---|---| -| `MondainRegion` | Mondain's Legacy dungeon base (no travel) | -| `CrystalFieldRegion` | Cold damage boost zone | -| `IcyRiverRegion` | Cold damage boost zone | -| `AcidRiverRegion` | Poison damage boost zone | -| `PoisonedTreeRegion` | Poison damage boost zone | -| `PoisonedCemeteryRegion` | Poison damage boost zone | -| `LostCityEntranceRegion` | Special dungeon entrance | -| `BlackthornDungeonRegion` | Blackthorn-specific rules | -| `ExodusDungeonRegion` | Exodus dungeon rules | -| `DoomGuardianRegion` | Doom gauntlet area | -| `UnderwaterRegion` | Underwater mechanics | -| `ApprenticeRegion` | Apprentice quest zone | -| `SeaMarketRegion` | Sea market mechanics | -| `BattleRegion` | Myrmidex battle zone | -| `NewMaginciaRegion` | New Magincia rules | -| `TokunoDocksRegion` | Tokuno docks mechanics | -| `TombOfKingsRegion` / `ToKBridgeRegion` | Tomb of Kings areas | -| `WrongLevel3Region` / `WrongJailRegion` | Wrong dungeon jail | -| `CousteauPerronHouseRegion` | Special house region | - -## Common Patterns - -### Blocking Travel Spells - -```csharp -public override bool CheckTravel( - Mobile m, Point3D newLocation, TravelCheckType travelType, out TextDefinition message) -{ - message = null; // null = use default "Thy spell doth not appear to work" - return m.AccessLevel > AccessLevel.Player; // Staff can always travel -} -``` - -### Blocking Specific Spells (e.g., Mark) - -```csharp -public override bool OnBeginSpellCast(Mobile m, ISpell s) -{ - if (m.AccessLevel == AccessLevel.Player && s is MarkSpell) - { - m.SendLocalizedMessage(501802); // Thy spell doth not appear to work... - return false; - } - return base.OnBeginSpellCast(m, s); -} -``` - -### Blocking Housing - -```csharp -public override bool AllowHousing(Mobile from, Point3D p) => false; -``` - -### Custom Light Level - -```csharp -public override void AlterLightLevel(Mobile m, ref int global, ref int personal) -{ - global = LightCycle.DungeonLevel; // Dungeon darkness -} -``` - -### No Logout Delay (Safe Zone) - -Set `NoLogoutDelay = true` on a `BaseRegion`. The delay is zero only if the mobile has no aggressors and is not criminal. - -### Damage Modification - -```csharp -public override void SpellDamageScalar(Mobile caster, Mobile target, ref double damage) -{ - base.SpellDamageScalar(caster, target, ref damage); - damage *= 1.5; // 50% more spell damage -} -``` - -## Common Mistakes - -| Mistake | Problem | Fix | -|---|---|---| -| Not calling `Unregister()` before `Register()` | Duplicate region entries | Always unregister old before creating new | -| Registering in deserialization constructor | Map/parent may not exist yet | Use `[AfterDeserialization]` with `Timer.StartTimer(TimeSpan.Zero, ...)` | -| Forgetting to unregister on item delete | Ghost region remains on map | Call `UpdateRegion()` or `Unregister()` in `OnAfterDelete()` | -| Creating a child without finding the parent | Region has no parent hierarchy benefits | Use `Region.Find(location, map)` as parent | -| Not registering type for JSON | Deserialization fails silently | Add `RegionJsonSerializer.Register()` in `RegionJsonRegistration.Configure()` | -| Using `Region.Find(string, Map)` in hot paths | Linear scan, O(n) | Use `Region.Find(Point3D, Map)` which uses sector index | -| Unregistering parent before children | Warning logged, orphaned children | Unregister children first | - -## Key File References - -| File | Description | -|---|---| -| `Projects/Server/Regions/Region.cs` | Engine-level base class (30+ virtual hooks) | -| `Projects/Server/Regions/RegionJsonSerializer.cs` | JSON loading, type registration | -| `Projects/Server/Json/Converters/RegionByNameConverter.cs` | Parent resolution from JSON | -| `Projects/UOContent/Regions/BaseRegion.cs` | Game-level base: CheckTravel, spawn weights | -| `Projects/UOContent/Regions/GuardedRegion.cs` | NPC guards, town mechanics | -| `Projects/UOContent/Regions/DungeonRegion.cs` | Dungeon lighting and rules | -| `Projects/UOContent/Regions/TownRegion.cs` | Town type marker | -| `Projects/UOContent/Regions/NoHousingRegion.cs` | Housing block | -| `Projects/UOContent/Regions/NoHousingGuardedRegion.cs` | Guarded + housing block | -| `Projects/UOContent/Regions/NoTravelSpellsAllowedRegion.cs` | Travel spell block | -| `Projects/UOContent/Regions/GreenAcresRegion.cs` | Multi-restriction region | -| `Projects/UOContent/Regions/HouseRegion.cs` | Dynamic per-house region | -| `Projects/UOContent/Regions/RegionJsonRegistration.cs` | All registered JSON types | -| `Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs` | Dynamic region lifecycle example | -| `Projects/UOContent/Multis/Houses/BaseHouse.cs` | House region lifecycle example | -| `Projects/UOContent/Spells/Base/SpellHelper.cs` | Travel check dispatch | -| `Distribution/Data/regions.json` | Static region definitions | diff --git a/dev-docs/runuo-migration-docs/00-overview.md b/dev-docs/runuo-migration-docs/00-overview.md deleted file mode 100644 index 0ef5216be..000000000 --- a/dev-docs/runuo-migration-docs/00-overview.md +++ /dev/null @@ -1,68 +0,0 @@ -# Migrating from RunUO to ModernUO — Overview - -## Why Migration Is Needed - -RunUO 2.7 targets .NET Framework (Windows-only, end-of-life). ModernUO targets .NET 10 (cross-platform, actively supported). Beyond the runtime, ModernUO has rewritten or replaced nearly every major subsystem for performance, safety, and maintainability. - -## High-Level Architecture Differences - -| Aspect | RunUO 2.7 | ModernUO | -|---|---|---| -| Runtime | .NET Framework 4.x (Windows) | .NET 10 (cross-platform) | -| Project structure | `Server/` + `Scripts/` (dynamic compilation) | `Projects/Server/` + `Projects/UOContent/` (compiled project) | -| Serialization | Manual `Serialize(GenericWriter)`/`Deserialize(GenericReader)` overrides | Source-generated via `[SerializationGenerator]` + `[SerializableField]` | -| Timers | `Timer` subclass instances with `Start()`/`Stop()` | Fire-and-forget `Timer.StartTimer()` + `TimerExecutionToken` | -| Packets | `Packet` class hierarchy with `PacketWriter` | Static `Create*Packet(Span)` methods + `SpanWriter` | -| Gumps | `Gump` with entry lists in constructor | `StaticGump`/`DynamicGump` with builder pattern | -| Property lists | `ObjectPropertyList` directly | `IPropertyList` interface | -| Persistence | `EventSink.WorldSave`/`WorldLoad` with manual binary files | `GenericPersistence`/`GenericEntityPersistence` | -| Events | `EventSink` with delegate types (`WorldSaveEventHandler`) | `EventSink` with `Action` delegates | -| Configuration | XML/cfg files | JSON (`modernuo.json`, `JsonConfig`) | -| Commands | `CommandSystem.Register` | Same, but handler conventions updated | -| Naming | `m_` private fields | `_camelCase` private fields | -| Logging | `Console.WriteLine` | `LogFactory.GetLogger()` → `logger.Information()` | -| Threading | Locks, volatile, concurrent collections | Single-threaded game loop (forbidden) | -| Collections | `new List()` everywhere | `PooledRefList`, `STArrayPool` | - -## Approach to Migration - -1. **Foundation changes first** — namespace style, naming, usings, logging, attributes -2. **Serialization** — the biggest change; convert `Serialize`/`Deserialize` to source-generated -3. **System-specific changes** — timers, gumps, packets, events, persistence -4. **Content fixes** — item/mobile/creature adjustments -5. **Testing** — compile, load world, verify in-game behavior - -## How These Docs Are Organized - -| Doc | Content | -|---|---| -| `01-foundation-changes.md` | Universal changes for ALL scripts | -| `02-serialization.md` | Source-generated serialization system | -| `03-timers.md` | Timer.StartTimer + TimerExecutionToken | -| `04-gumps.md` | StaticGump/DynamicGump builder pattern | -| `05-packets-networking.md` | SpanWriter/SpanReader packet system | -| `06-property-lists.md` | IPropertyList + string hole rule | -| `07-commands-events.md` | Command registration + EventSink changes | -| `08-persistence.md` | GenericPersistence system | -| `09-items-mobiles-creatures.md` | Content migration patterns | -| `10-systems-engines.md` | Multi-file system migration | -| `11-api-reference.md` | Comprehensive API mapping table | - -## Key Terminology Changes - -| RunUO Term | ModernUO Term | -|---|---| -| `Scripts/` directory | `Projects/UOContent/` directory | -| `[Constructable]` | `[Constructible]` | -| `GenericWriter` / `GenericReader` | `IGenericWriter` / `IGenericReader` | -| `PacketWriter` / `PacketReader` | `SpanWriter` / `SpanReader` | -| `ObjectPropertyList` (parameter) | `IPropertyList` (parameter) | -| `BinaryFileWriter` | `GenericPersistence` base class | -| `EventSink.WorldSave += new WorldSaveEventHandler(Save)` | `GenericPersistence` subclass | -| `m_FieldName` | `_fieldName` | - -## See Also - -- ModernUO wiki: [Migrating from RunUO](https://github.com/modernuo/ModernUO/wiki/4.-Migrating-From-RunUO) -- ModernUO dev-docs: `dev-docs/` (system-specific documentation) -- SerializationGenerator: https://github.com/modernuo/SerializationGenerator diff --git a/dev-docs/runuo-migration-docs/01-foundation-changes.md b/dev-docs/runuo-migration-docs/01-foundation-changes.md deleted file mode 100644 index 668701bea..000000000 --- a/dev-docs/runuo-migration-docs/01-foundation-changes.md +++ /dev/null @@ -1,368 +0,0 @@ -# Foundation Changes (All Scripts) - -## Overview - -These changes apply to every RunUO script being migrated. Apply them first before tackling system-specific changes (serialization, timers, gumps, etc.). - -## 1. File-Scoped Namespaces - -RunUO uses block-scoped namespaces. ModernUO uses file-scoped (C# 10+): - -```csharp -// RunUO -namespace Server.Items -{ - public class MyItem : Item - { - // ... - } -} - -// ModernUO -namespace Server.Items; - -public class MyItem : Item -{ - // ... -} -``` - -## 2. Naming Conventions - -### Private Fields -RunUO uses `m_` prefix. ModernUO uses `_camelCase`: - -```csharp -// RunUO -private int m_Charges; -private Mobile m_Owner; -private string m_Name; - -// ModernUO -private int _charges; -private Mobile _owner; -private string _name; -``` - -**Note**: Don't rename existing `m_` fields in legacy code you're not otherwise modifying. Only use `_` for new code and code you're actively migrating. - -### Properties -Both use `PascalCase`. RunUO often has verbose syntax: - -```csharp -// RunUO -public int Charges { get { return m_Charges; } set { m_Charges = value; } } - -// ModernUO (expression-bodied or auto-property) -public int Charges { get => _charges; set => _charges = value; } -// Or if serialized, [SerializableField] generates it automatically -``` - -## 3. [Constructable] → [Constructible] - -Spelling change for the attribute on parameterless constructors: - -```csharp -// RunUO -[Constructable] -public MyItem() : base(0x1234) { } - -// ModernUO -[Constructible] -public MyItem() : base(0x1234) { } -``` - -The `using` also changes: -```csharp -// RunUO — no using needed, it's in Server namespace -// ModernUO — still in Server namespace, but ensure you have: -using ModernUO.Serialization; // for [SerializationGenerator], [SerializableField] -``` - -## 4. Logging - -`Console.WriteLine` is never used in ModernUO. Use structured logging: - -```csharp -// RunUO -Console.WriteLine("Player {0} logged in from {1}", name, ip); - -// ModernUO -using Server.Logging; - -private static readonly ILogger logger = LogFactory.GetLogger(typeof(MyClass)); - -logger.Information("Player {Name} logged in from {IP}", name, ip); -``` - -Log levels: `Debug`, `Information`, `Warning`, `Error`, `Fatal` - -## 5. DateTime.UtcNow → Core.Now - -```csharp -// RunUO -DateTime.UtcNow - -// ModernUO -Core.Now -``` - -`Core.Now` is the server's authoritative time source, consistent within each game tick. - -## 6. World Iteration → Spatial Queries - -Never iterate `World.Mobiles` or `World.Items`. Use map-based spatial queries: - -```csharp -// RunUO -foreach (Mobile m in World.Mobiles.Values) -{ - if (m.InRange(location, 10)) - DoSomething(m); -} - -// ModernUO -foreach (var m in map.GetMobilesInRange(location, 10)) -{ - DoSomething(m); -} -``` - -Available spatial queries (on `Map`): -- `GetMobilesAt(Point3D)` — exact location -- `GetMobilesInRange(Point3D, int range)` — within range -- `GetMobilesInBounds(Rectangle2D)` — within rectangle -- Same for `GetItemsAt`, `GetItemsInRange`, `GetItemsInBounds` - -## 7. Remove Concurrency Primitives - -ModernUO's game loop is single-threaded. Remove all threading constructs: - -```csharp -// RunUO (remove ALL of these) -lock (_syncObj) { } -volatile int _counter; -ConcurrentDictionary _items; -Mutex m = new Mutex(); -Semaphore s = new Semaphore(1, 1); - -// ModernUO (single-threaded replacements) -// lock → remove entirely -// volatile → remove keyword -// ConcurrentDictionary → Dictionary -// Mutex/Semaphore → remove entirely -``` - -## 8. No Task.Run / new Thread - -Game code must not spawn threads: - -```csharp -// RunUO (FORBIDDEN in ModernUO) -Task.Run(() => ProcessItems()); -new Thread(BackgroundWork).Start(); -ThreadPool.QueueUserWorkItem(Work); - -// ModernUO — use timers or await -Timer.StartTimer(TimeSpan.FromSeconds(1), ProcessItems); -await Timer.Pause(1000); // for async/await patterns -``` - -## 9. ArrayPool → STArrayPool - -```csharp -// RunUO -var buffer = ArrayPool.Shared.Rent(1024); -// ... -ArrayPool.Shared.Return(buffer); - -// ModernUO (single-threaded, no locks) -var buffer = STArrayPool.Shared.Rent(1024); -// ... -STArrayPool.Shared.Return(buffer); -``` - -## 10. new List() on Hot Paths → PooledRefList - -```csharp -// RunUO -var list = new List(); -foreach (var m in nearbyMobiles) -{ - if (m.Alive) - list.Add(m); -} -// list goes to GC - -// ModernUO (zero-alloc) -using var list = PooledRefList.Create(); -foreach (var m in nearbyMobiles) -{ - if (m.Alive) - list.Add(m); -} -// Automatically returns array to pool on Dispose -``` - -## 11. LINQ Restrictions - -ModernUO has tiered LINQ rules. On hot paths: -- **Tier 1 (allowed)**: `foreach` over `IEnumerable`, `.Contains()` after LINQ operators, `.OrderBy().First()`, `.Count()` on sized collections -- **Tier 2 (acceptable on warm paths)**: `.Skip().Take().ToArray()`, `.Where()` on arrays -- **Tier 3 (forbidden on hot paths)**: `.Select().Where()` chains, `.GroupBy()`, `.ToDictionary()`, `.Aggregate()`, `.Sum()`/`.Min()`/`.Max()` - -```csharp -// RunUO (common LINQ patterns — FORBIDDEN on hot paths in ModernUO) -var targets = nearbyMobiles.Where(m => m.Alive).ToList(); -var count = items.Count(i => i.Stackable); - -// ModernUO (manual loops) -using var targets = PooledRefList.Create(); -foreach (var m in nearbyMobiles) -{ - if (m.Alive) - targets.Add(m); -} - -var count = 0; -foreach (var i in items) -{ - if (i.Stackable) - count++; -} -``` - -See `dev-docs/code-standards.md` for full LINQ tier details. - -## 12. Property Syntax Modernization - -```csharp -// RunUO (verbose) -public int Charges -{ - get { return m_Charges; } - set { m_Charges = value; } -} - -public override string Name -{ - get { return "An Item"; } -} - -// ModernUO (modern C#) -public int Charges { get => _charges; set => _charges = value; } - -public override string DefaultName => "an item"; -``` - -## 13. Using Directives - -Common new usings in ModernUO: -```csharp -using ModernUO.Serialization; // [SerializationGenerator], [SerializableField], etc. -using Server.Logging; // ILogger, LogFactory -using Server.Gumps; // SendGump, HasGump, etc. extension methods -using Server.Collections; // PooledRefList -``` - -Removed/changed usings: -```csharp -// RunUO (no longer exists/changed) -using Server.Network; // Packet classes removed — use extension methods -``` - -## 14. Serial Constructor Removal - -RunUO items have a deserialization constructor `MyItem(Serial serial) : base(serial)`. In ModernUO with `[SerializationGenerator]`, this constructor is generated automatically. **Remove it.** - -```csharp -// RunUO -public MyItem(Serial serial) : base(serial) { } - -// ModernUO — DELETE THIS CONSTRUCTOR. The source generator creates it. -``` - -## 15. Static `Parse(string)` → `IParsable` / `ISpanParsable` - -RunUO predates `IParsable`/`ISpanParsable` (C# 11 / .NET 7 static-abstract interface -members), so RunUO types that convert from a string expose a bare `public static T Parse(string value)`. -**ModernUO expects any such type to implement `IParsable` (string) and, where practical, -`ISpanParsable` (span; it extends `IParsable`, so implement span and you get both).** - -This matters because the engine's string→value converter, `Server.Types.TryParse` — used by `[set`, -`[props`, spawner property assignment, the conditional-command compiler (`[where`), and Advanced -Search — binds to the `Parse(string, IFormatProvider)` signature. A type with **only** a legacy -`Parse(string)` is discovered by `Types` through a reflection fallback, but that fallback is a safety -net, not the intended path: a bare `Parse(string)` is easy to miss, doesn't participate in the -span-based fast paths, and (if it returns `null` instead of throwing) makes `[set` silently assign -`null` on bad input. Convert it. - -The `Parse` overloads throw `FormatException` on failure; `TryParse` returns `false`. Delegate the -string overloads to a span core (see `Race`, `Poison`, `Point3D` for the established pattern): - -```csharp -// RunUO -public abstract class Faction : IComparable -{ - public static Faction Parse(string name) // returns null on no-match — wrong contract, not IParsable - { - // ... linear search by name ... - return null; - } -} - -// ModernUO -public abstract class Faction : IComparable, ISpanParsable -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Faction Parse(string s) => Parse(s, null); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Faction Parse(string s, IFormatProvider provider) => Parse(s.AsSpan(), provider); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryParse(string s, IFormatProvider provider, out Faction result) => - TryParse(s.AsSpan(), provider, out result); - - public static Faction Parse(ReadOnlySpan s, IFormatProvider provider) => - TryParse(s, provider, out var result) - ? result - : throw new FormatException($"The input string '{s}' was not in a correct format."); - - public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, out Faction result) - { - // ... linear search by name using s.InsensitiveEquals(...) ... - result = null; - return false; - } -} -``` - -To find un-migrated types: search for `public static [A-Za-z0-9_<>]+ Parse\(string ` and check whether -the declaring type lists `IParsable`/`ISpanParsable`. - -## Quick Checklist - -When migrating any RunUO script, apply these changes in order: - -1. [ ] Change to file-scoped namespace -2. [ ] Add `using ModernUO.Serialization;` -3. [ ] Rename `m_` fields to `_camelCase` -4. [ ] Change `[Constructable]` to `[Constructible]` -5. [ ] Replace `Console.WriteLine` with structured logging -6. [ ] Replace `DateTime.UtcNow` with `Core.Now` -7. [ ] Replace `World.Mobiles`/`World.Items` iteration with spatial queries -8. [ ] Remove concurrency primitives -9. [ ] Remove threading code -10. [ ] Replace `ArrayPool` with `STArrayPool` -11. [ ] Replace `new List()` on hot paths with `PooledRefList` -12. [ ] Modernize property syntax -13. [ ] Remove `Serial` constructor (handled by serialization generator) -14. [ ] Update usings -15. [ ] Convert bare static `Parse(string)` to `IParsable`/`ISpanParsable` - -## See Also - -- `dev-docs/code-standards.md` — Full coding standards and LINQ tiers -- `dev-docs/threading-model.md` — Threading model details -- `02-serialization.md` — Next step: converting serialization diff --git a/dev-docs/runuo-migration-docs/02-serialization.md b/dev-docs/runuo-migration-docs/02-serialization.md deleted file mode 100644 index 666ebfdb4..000000000 --- a/dev-docs/runuo-migration-docs/02-serialization.md +++ /dev/null @@ -1,507 +0,0 @@ -# Serialization Migration - -## Overview - -This is the most impactful migration change. RunUO uses manual `Serialize(GenericWriter)`/`Deserialize(GenericReader)` overrides. ModernUO uses a source generator that automatically produces serialization code from attribute-decorated fields. - -## RunUO Pattern - -```csharp -using Server; - -namespace Server.Items -{ - public class ChargedGem : Item - { - private int m_Charges; - private Mobile m_Owner; - - [CommandProperty(AccessLevel.GameMaster)] - public int Charges { get { return m_Charges; } set { m_Charges = value; InvalidateProperties(); } } - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Owner { get { return m_Owner; } set { m_Owner = value; } } - - [Constructable] - public ChargedGem() : base(0x1EA7) - { - m_Charges = 10; - Weight = 1.0; - } - - public ChargedGem(Serial serial) : base(serial) { } - - public override void Serialize(GenericWriter writer) - { - base.Serialize(writer); - writer.Write((int)1); // version - - // Version 1 - writer.Write(m_Owner); - - // Version 0 - writer.Write(m_Charges); - } - - public override void Deserialize(GenericReader reader) - { - base.Deserialize(reader); - int version = reader.ReadInt(); - - switch (version) - { - case 1: - { - m_Owner = reader.ReadMobile(); - goto case 0; - } - case 0: - { - m_Charges = reader.ReadInt(); - break; - } - } - } - - public override void GetProperties(ObjectPropertyList list) - { - base.GetProperties(list); - list.Add(1060741, m_Charges.ToString()); // charges: ~1_val~ - } - } -} -``` - -## ModernUO Equivalent - -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(2, false)] // Old version was 1 → bump to 2; false because old saves used ReadInt() -public partial class ChargedGem : Item -{ - [SerializableField(0)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _charges; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Mobile _owner; - - [Constructible] - public ChargedGem() : base(0x1EA7) - { - _charges = 10; - Weight = 1.0; - } - - // Handles loading saves from BEFORE the SerializationGenerator conversion - private void Deserialize(IGenericReader reader, int version) - { - switch (version) - { - case 1: - { - _owner = reader.ReadEntity(); - goto case 0; - } - case 0: - { - _charges = reader.ReadInt(); - break; - } - } - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - list.Add(1060741, $"{_charges}"); // charges: ~1_val~ - } -} -``` - -## Migration Mapping Table - -| RunUO | ModernUO | Notes | -|---|---|---| -| `public class Foo : Item` | `public partial class Foo : Item` | Must add `partial` | -| `[Constructable]` | `[Constructible]` | Spelling change | -| `Foo(Serial serial) : base(serial)` | DELETE | Generated automatically | -| `Serialize(GenericWriter writer)` | DELETE | Generated from `[SerializableField]` attributes | -| `Deserialize(GenericReader reader)` | DELETE | Generated from attributes | -| `writer.Write((int)version)` | `[SerializationGenerator(version, false)]` | Version in attribute | -| `private int m_Charges` | `[SerializableField(0)] private int _charges` | Attribute + rename | -| `[CommandProperty(AccessLevel.GM)]` on property | `[SerializedCommandProperty(AccessLevel.GM)]` on field | Moves to field | -| `InvalidateProperties()` in setter | `[InvalidateProperties]` on field | Attribute replaces manual call | -| `GenericWriter` | `IGenericWriter` | Interface now | -| `GenericReader` | `IGenericReader` | Interface now | -| `reader.ReadInt()` | `reader.ReadInt()` | Same for manual cases | -| `reader.ReadMobile()` | `reader.ReadEntity()` | Generic method | -| `reader.ReadItem()` | `reader.ReadEntity()` | Generic method | -| `writer.Write((int)0)` version | `[SerializationGenerator(N, false)]` | Version bumped +1; `false` because old saves used `ReadInt()` | - -## Step-by-Step Conversion - -### Step 1: Add Required Using -```csharp -using ModernUO.Serialization; -``` - -### Step 2: Add Class Attributes and `partial` - -Read the old `Serialize()` to find the version number it writes. Bump it by 1 for the `[SerializationGenerator]` first parameter. If the old `Deserialize()` used `reader.ReadInt()` (not `ReadEncodedInt()`), pass `false` as the second parameter. - -```csharp -// Change: -public class MyItem : Item -// To (old Serialize wrote version 0, old Deserialize used ReadInt()): -[SerializationGenerator(1, false)] -public partial class MyItem : Item -``` - -### Step 3: Delete Serial Constructor -Remove `public MyItem(Serial serial) : base(serial) { }` entirely. - -### Step 4: Convert Fields -For each field that was serialized in `Serialize()`: - -```csharp -// RunUO -private int m_Charges; -[CommandProperty(AccessLevel.GameMaster)] -public int Charges { get { return m_Charges; } set { m_Charges = value; } } - -// ModernUO -[SerializableField(0)] // Index = serialization order -[SerializedCommandProperty(AccessLevel.GameMaster)] -private int _charges; -// Property is auto-generated: public int Charges { get; set; } -``` - -Add `[InvalidateProperties]` if the RunUO setter called `InvalidateProperties()`: -```csharp -[SerializableField(0)] -[InvalidateProperties] -[SerializedCommandProperty(AccessLevel.GameMaster)] -private int _charges; -``` - -### Step 5: Migrate Serialize and Deserialize Methods - -Delete the `Serialize()` override entirely — the source generator creates it. - -For `Deserialize()`: if there are existing saves to support, convert it to `private void Deserialize(IGenericReader reader, int version)` (remove the `override`, change the signature). This method handles loading saves from before the SerializationGenerator conversion. Remove the `base.Deserialize(reader)` call and the version reading line — the generator handles those. - -```csharp -// Old RunUO: -public override void Deserialize(GenericReader reader) -{ - base.Deserialize(reader); - int version = reader.ReadInt(); - m_Charges = reader.ReadInt(); -} - -// Converted — keeps backward compat with old saves: -private void Deserialize(IGenericReader reader, int version) -{ - _charges = reader.ReadInt(); -} -``` - -If there are no existing saves to worry about (fresh world), you can delete `Deserialize()` entirely. - -### Step 6: Change [Constructable] to [Constructible] -```csharp -[Constructible] -public MyItem() : base(0x1234) { } -``` - -### Step 7: Handle Timer Fields -`TimerExecutionToken` MUST NOT have `[SerializableField]`. Restore timers in `[AfterDeserialization]`: - -```csharp -private TimerExecutionToken _timerToken; // NOT serialized - -[AfterDeserialization] -private void AfterDeserialization() -{ - Timer.StartTimer(TimeSpan.FromSeconds(5), CheckExpiry, out _timerToken); -} - -public override void OnAfterDelete() -{ - _timerToken.Cancel(); - base.OnAfterDelete(); -} -``` - -### Step 8: Handle Custom Property Logic -If a property has non-trivial getter/setter logic, use `[SerializableProperty]` instead: - -```csharp -[SerializableProperty(0)] -[CommandProperty(AccessLevel.GameMaster)] -public int MaxItems -{ - get => _maxItems == -1 ? DefaultMaxItems : _maxItems; - set - { - _maxItems = value; - InvalidateProperties(); - this.MarkDirty(); // REQUIRED in custom setters - } -} -``` - -### Step 9: Update GetProperties -Change `ObjectPropertyList` to `IPropertyList`: -```csharp -// RunUO -public override void GetProperties(ObjectPropertyList list) - -// ModernUO -public override void GetProperties(IPropertyList list) -``` - -## Before/After Examples - -### Simple Item (No Custom Fields) - -**RunUO:** -```csharp -namespace Server.Items -{ - public class SimpleGem : Item - { - [Constructable] - public SimpleGem() : base(0x1EA7) - { - Weight = 1.0; - } - - public SimpleGem(Serial serial) : base(serial) { } - - public override void Serialize(GenericWriter writer) - { - base.Serialize(writer); - writer.Write((int)0); - } - - public override void Deserialize(GenericReader reader) - { - base.Deserialize(reader); - int version = reader.ReadInt(); - } - } -} -``` - -**ModernUO:** -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(1, false)] // Old version was 0 → bump to 1; false because old saves used ReadInt() -public partial class SimpleGem : Item -{ - [Constructible] - public SimpleGem() : base(0x1EA7) - { - Weight = 1.0; - } - - public override string DefaultName => "a simple gem"; - - // Handles loading saves from BEFORE the SerializationGenerator conversion - private void Deserialize(IGenericReader reader, int version) - { - // Version 0 had no custom fields — nothing to read - } -} -``` - -### Versioned Item - -**RunUO (version 2 — added Owner in v1, Quality in v2):** -```csharp -namespace Server.Items -{ - public class MagicGem : Item - { - private int m_Charges; - private Mobile m_Owner; - private GemQuality m_Quality; - - [Constructable] - public MagicGem() : base(0x1EA7) - { - m_Charges = 10; - m_Quality = GemQuality.Rough; - } - - public MagicGem(Serial serial) : base(serial) { } - - public override void Serialize(GenericWriter writer) - { - base.Serialize(writer); - writer.Write((int)2); - writer.Write((int)m_Quality); - writer.Write(m_Owner); - writer.Write(m_Charges); - } - - public override void Deserialize(GenericReader reader) - { - base.Deserialize(reader); - int version = reader.ReadInt(); - - switch (version) - { - case 2: - m_Quality = (GemQuality)reader.ReadInt(); - goto case 1; - case 1: - m_Owner = reader.ReadMobile(); - goto case 0; - case 0: - m_Charges = reader.ReadInt(); - break; - } - } - } -} -``` - -**ModernUO:** -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(3, false)] // Old version was 2 → bump to 3; false because old saves used ReadInt() -public partial class MagicGem : Item -{ - [SerializableField(0)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _charges; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Mobile _owner; - - [SerializableField(2)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private GemQuality _quality; - - [Constructible] - public MagicGem() : base(0x1EA7) - { - _charges = 10; - _quality = GemQuality.Rough; - } - - // Handles loading saves from BEFORE the SerializationGenerator conversion - private void Deserialize(IGenericReader reader, int version) - { - switch (version) - { - case 2: - { - _quality = (GemQuality)reader.ReadInt(); - goto case 1; - } - case 1: - { - _owner = reader.ReadEntity(); - goto case 0; - } - case 0: - { - _charges = reader.ReadInt(); - break; - } - } - } -} -``` - -**Important**: When migrating RunUO code: -- Read the old `Serialize()` to find the version it writes, then bump it by 1 for the `[SerializationGenerator]` first parameter -- Pass `false` as the second parameter if the old `Deserialize()` used `reader.ReadInt()` (not `ReadEncodedInt()`) — this tells the generator how old saves encoded the version number -- Keep the old deserialization logic as `private void Deserialize(IGenericReader reader, int version)` to handle loading pre-codegen saves -- This `Deserialize` method is called automatically for old saves; the generator handles new saves - -## Edge Cases & Gotchas - -### 1. Save Compatibility -ModernUO's serialization format is completely different from RunUO's. You CANNOT load RunUO saves directly into ModernUO with source-generated serialization. Options: -- Use `[TypeAlias("Old.Namespace.ClassName")]` to map old type names -- Start with a fresh world -- Write a one-time migration tool - -### 2. MarkDirty() in Custom Setters -If you use `[SerializableProperty]` with a custom setter, you MUST call `this.MarkDirty()`: -```csharp -set -{ - _value = value; - this.MarkDirty(); // Required! -} -``` -Without this, changes won't be saved. - -Most RunUO custom setters only clamp the value or run side effects after assignment. Those -convert to a plain `[SerializableField]` with the `allowFieldChange`/`fieldChanged` hooks, -which handle the equality check and `MarkDirty()` for you -- reserve `[SerializableProperty]` -for custom getters (see `dev-docs/serialization.md`). - -### 3. Field Ordering -The `[SerializableField(N)]` index determines serialization order. Choose a logical order and don't change it after the first save — or increment the version. - -### 4. Conditional Serialization -Use `[SaveFlag]` on the serializable field to skip default values (the second method is -optional -- omit it and the field keeps its default at load): -```csharp -[SerializableField(0)] -[SaveFlag(nameof(ShouldSerializeMaxItems), nameof(MaxItemsDefaultValue))] -private int _maxItems; - -private bool ShouldSerializeMaxItems() => _maxItems != -1; - -private int MaxItemsDefaultValue() => -1; -``` - -### 5. Collection Fields -Use `[Tidy]` to auto-clean null/deleted entries on deserialization: -```csharp -[Tidy] -[SerializableField(0)] -private List _followers; -``` - -### 6. DateTime Fields -Use `[AnchoredDateTime]` to survive server restarts -- the value is shifted by downtime at -load, so the remaining time is preserved and idle saves stay byte-stable: -```csharp -[AnchoredDateTime] -[SerializableField(0)] -private DateTime _expireTime; -``` -(`[DeltaDateTime]` is the legacy equivalent; it rewrites bytes on every save. Converting an -existing field between the two changes the wire format and requires a version bump.) - -### 7. Keeping Manual Serialization (Rare) -Some edge cases still need manual serialization. If a type has complex conditional logic that can't be expressed with attributes, you can implement `ISerializable` manually. But this is rare — try attributes first. - -## See Also - -- `dev-docs/serialization.md` — Complete ModernUO serialization reference -- `01-foundation-changes.md` — Foundation changes to apply first -- `03-timers.md` — Timer migration (often coupled with serialization) diff --git a/dev-docs/runuo-migration-docs/03-timers.md b/dev-docs/runuo-migration-docs/03-timers.md deleted file mode 100644 index 13596a079..000000000 --- a/dev-docs/runuo-migration-docs/03-timers.md +++ /dev/null @@ -1,327 +0,0 @@ -# Timer Migration - -## Overview - -RunUO uses `Timer` subclass instances that you construct, start, and stop. ModernUO replaces most of this with fire-and-forget `Timer.StartTimer()` calls and lightweight `TimerExecutionToken` structs for cancellation. The `TimerPriority` enum is removed — ModernUO's timer wheel handles scheduling automatically with 8ms precision. - -## RunUO Pattern - -```csharp -// RunUO — Timer subclass pattern -public class MyItem : Item -{ - private InternalTimer m_Timer; - - [Constructable] - public MyItem() : base(0x1234) - { - m_Timer = new InternalTimer(this); - m_Timer.Start(); - } - - public MyItem(Serial serial) : base(serial) { } - - public override void OnDelete() - { - if (m_Timer != null) - m_Timer.Stop(); - } - - public override void Serialize(GenericWriter writer) - { - base.Serialize(writer); - writer.Write((int)0); - } - - public override void Deserialize(GenericReader reader) - { - base.Deserialize(reader); - int version = reader.ReadInt(); - - m_Timer = new InternalTimer(this); - m_Timer.Start(); - } - - private void DoWork() - { - // Timer callback logic - } - - private class InternalTimer : Timer - { - private MyItem m_Item; - - public InternalTimer(MyItem item) : base(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)) - { - m_Item = item; - Priority = TimerPriority.OneSecond; - } - - protected override void OnTick() - { - m_Item.DoWork(); - } - } -} -``` - -### RunUO Timer.DelayCall -```csharp -// One-shot delay -Timer.DelayCall(TimeSpan.FromSeconds(5), new TimerCallback(DoWork)); -Timer.DelayCall(TimeSpan.FromSeconds(5), new TimerStateCallback(DoWork), target); - -// Repeating -Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromSeconds(1), new TimerCallback(DoWork)); -``` - -## ModernUO Equivalent - -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class MyItem : Item -{ - private TimerExecutionToken _timerToken; - - [Constructible] - public MyItem() : base(0x1234) - { - StartTimer(); - } - - private void StartTimer() - { - Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), DoWork, out _timerToken); - } - - [AfterDeserialization] - private void AfterDeserialization() => StartTimer(); - - public override void OnAfterDelete() - { - _timerToken.Cancel(); - base.OnAfterDelete(); - } - - private void DoWork() - { - // Timer callback logic - } -} -``` - -## Migration Mapping Table - -| RunUO | ModernUO | Notes | -|---|---|---| -| `new InternalTimer().Start()` | `Timer.StartTimer(..., callback, out token)` | Fire-and-forget | -| `Timer` subclass with `OnTick()` | Static callback method | No class needed | -| `timer.Stop()` | `_token.Cancel()` | Lightweight struct | -| `timer.Running` | `_token.Running` | Same concept | -| `TimerPriority.OneSecond` | (removed) | Timer wheel handles scheduling | -| `TimerPriority.FiveSeconds` | (removed) | Timer wheel handles scheduling | -| `Timer.DelayCall(delay, callback)` | `Timer.StartTimer(delay, callback)` | Similar API | -| `Timer.DelayCall(delay, callback, state)` | `Timer.DelayCall(delay, callback, state)` | State-carrying version still exists | -| `new TimerCallback(Method)` | `Method` | Direct method reference | -| `new TimerStateCallback(Method)` | Use state-carrying overload | `Timer.DelayCall(delay, Method, arg1, arg2)` | -| Timer started in `Deserialize()` | `[AfterDeserialization]` method | Never start timers in deserialization | -| `m_Timer != null` check | `_token.Running` check | Token is a value type, always valid | - -## Step-by-Step Conversion - -### Step 1: Identify the Timer Pattern -Look for: -- Nested `Timer` subclass with `OnTick()` override -- `Timer.DelayCall()` calls -- `TimerPriority` usage - -### Step 2: Extract the Callback -Move the `OnTick()` logic to a regular method on the parent class: - -```csharp -// RunUO — nested class -private class InternalTimer : Timer -{ - private MyItem m_Item; - public InternalTimer(MyItem item) : base(TimeSpan.FromSeconds(5)) { m_Item = item; } - protected override void OnTick() { m_Item.DoWork(); } -} - -// ModernUO — just the method -private void DoWork() -{ - // Same logic, directly on the item -} -``` - -### Step 3: Replace Construction with Timer.StartTimer -```csharp -// RunUO -m_Timer = new InternalTimer(this); -m_Timer.Start(); - -// ModernUO (one-shot) -Timer.StartTimer(TimeSpan.FromSeconds(5), DoWork); - -// ModernUO (repeating, need cancellation) -Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), DoWork, out _timerToken); - -// ModernUO (repeating with count limit) -Timer.StartTimer(TimeSpan.Zero, TimeSpan.FromSeconds(1), 10, DoWork, out _timerToken); -``` - -### Step 4: Add TimerExecutionToken Field (if cancellable) -```csharp -private TimerExecutionToken _timerToken; // NOT serialized — no [SerializableField] -``` - -### Step 5: Cancel in OnAfterDelete -```csharp -public override void OnAfterDelete() -{ - _timerToken.Cancel(); // Safe to call multiple times - base.OnAfterDelete(); -} -``` - -### Step 6: Restore in [AfterDeserialization] -```csharp -[AfterDeserialization] -private void AfterDeserialization() -{ - Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), DoWork, out _timerToken); -} -``` - -### Step 7: Delete the Nested Timer Class -Remove the entire `private class InternalTimer : Timer { ... }` block. - -### Step 8: Remove TimerPriority -Delete any `Priority = TimerPriority.xxx` lines. The timer wheel handles scheduling. - -## Before/After Examples - -### Simple One-Shot Timer - -**RunUO:** -```csharp -Timer.DelayCall(TimeSpan.FromSeconds(10), new TimerCallback(Delete)); -``` - -**ModernUO:** -```csharp -Timer.StartTimer(TimeSpan.FromSeconds(10), Delete); -``` - -### Repeating Timer with State - -**RunUO:** -```csharp -private class HealTimer : Timer -{ - private Mobile m_Target; - - public HealTimer(Mobile target) : base(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2)) - { - m_Target = target; - Priority = TimerPriority.TwoFiftyMS; - } - - protected override void OnTick() - { - if (m_Target.Alive && m_Target.Hits < m_Target.HitsMax) - m_Target.Hits += 5; - else - Stop(); - } -} -``` - -**ModernUO:** -```csharp -private TimerExecutionToken _healTimer; - -private void StartHeal(Mobile target) -{ - Timer.StartTimer(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2), () => HealTick(target), out _healTimer); -} - -private void HealTick(Mobile target) -{ - if (target.Alive && target.Hits < target.HitsMax) - target.Hits += 5; - else - _healTimer.Cancel(); -} -``` - -Or for zero-allocation, use the state-carrying `Timer.DelayCall`: -```csharp -Timer.DelayCall(TimeSpan.FromSeconds(2), HealTick, target); -``` - -### Timer.DelayCall with State - -**RunUO:** -```csharp -Timer.DelayCall(TimeSpan.FromSeconds(2), new TimerStateCallback(ProcessTarget), target); - -private static void ProcessTarget(object state) -{ - Mobile target = (Mobile)state; - // ... -} -``` - -**ModernUO:** -```csharp -Timer.DelayCall(TimeSpan.FromSeconds(2), ProcessTarget, target); - -private static void ProcessTarget(Mobile target) -{ - // Type-safe — no casting needed -} -``` - -ModernUO supports up to 5 typed state parameters: -```csharp -Timer.DelayCall(TimeSpan.FromSeconds(2), ProcessTarget, mobile, item); -Timer.DelayCall(TimeSpan.FromSeconds(2), DoWork, arg1, arg2, arg3); -``` - -## Edge Cases & Gotchas - -### 1. TimerExecutionToken Is NOT Serializable -Never add `[SerializableField]` to a `TimerExecutionToken`. It's a struct that tracks a pooled timer — it can't survive serialization. Always restore timers in `[AfterDeserialization]`. - -### 2. Don't Start Timers in Deserialization -In RunUO, timers are commonly started in `Deserialize()`. In ModernUO, use `[AfterDeserialization]` — this runs after the world is fully loaded. - -### 3. Cancel() Is Always Safe -`_token.Cancel()` can be called on a default token, a stopped token, or an already-cancelled token. No null checks needed. - -### 4. Timer.DelayCall Still Exists -`Timer.DelayCall()` is still available and returns a `Timer` object. Use it when you need the `Timer` reference (e.g., for a serialized timer field with `[DeserializeTimer]`) or state-carrying overloads. - -### 5. Custom Timer Classes Are Still Possible -For complex timer logic (e.g., `Corpse.DecayTimer`), you can still subclass `Timer` with `OnTick()`. But prefer the fire-and-forget pattern for simple cases. - -### 6. Avoid Lambda on Hot Paths -Lambdas allocate closures. For hot-path timers, use state-carrying `Timer.DelayCall` or direct method references: -```csharp -// Allocates closure: -Timer.StartTimer(TimeSpan.FromSeconds(2), () => ProcessTarget(from, target)); - -// No allocation: -Timer.DelayCall(TimeSpan.FromSeconds(2), ProcessTarget, from, target); -``` - -## See Also - -- `dev-docs/timers.md` — Complete ModernUO timer reference -- `02-serialization.md` — Serialization (timer fields, [AfterDeserialization]) -- `01-foundation-changes.md` — Foundation changes diff --git a/dev-docs/runuo-migration-docs/04-gumps.md b/dev-docs/runuo-migration-docs/04-gumps.md deleted file mode 100644 index ab44914a8..000000000 --- a/dev-docs/runuo-migration-docs/04-gumps.md +++ /dev/null @@ -1,343 +0,0 @@ -# Gump Migration - -## Overview - -RunUO uses a `Gump` base class where layout is built in the constructor using `AddXxx()` entry methods. ModernUO provides `StaticGump` (cached layout) and `DynamicGump` (rebuilt per-instance) with ref struct builders, and enforces the empty gump rule. - -## RunUO Pattern - -```csharp -using Server.Gumps; -using Server.Network; - -namespace Server.Gumps -{ - public class ConfirmGump : Gump - { - private Mobile m_From; - private string m_Message; - - public ConfirmGump(Mobile from, string message) : base(50, 50) - { - m_From = from; - m_Message = message; - - Closable = true; - Disposable = true; - Dragable = true; - Resizable = false; - - AddPage(0); - AddBackground(0, 0, 400, 300, 5054); - AddAlphaRegion(10, 10, 380, 280); - - AddHtml(20, 20, 360, 200, message, true, true); - - AddButton(100, 260, 4005, 4007, 1, GumpButtonType.Reply, 0); - AddHtmlLocalized(135, 262, 100, 20, 1011036, false, false); // OK - - AddButton(250, 260, 4017, 4019, 0, GumpButtonType.Reply, 0); - AddHtmlLocalized(285, 262, 100, 20, 1011012, false, false); // Cancel - } - - public override void OnResponse(NetState sender, RelayInfo info) - { - if (info.ButtonID == 1) - { - m_From.SendMessage("Confirmed!"); - } - } - } -} - -// Usage: -from.SendGump(new ConfirmGump(from, "Are you sure?")); -``` - -## ModernUO Equivalent (DynamicGump) - -```csharp -using Server.Gumps; - -namespace Server.Gumps; - -public class ConfirmGump : DynamicGump -{ - private readonly Mobile _from; - private readonly string _message; - - public override bool Singleton => true; - - private ConfirmGump(Mobile from, string message) : base(50, 50) - { - _from = from; - _message = message; - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 400, 300, 5054); - builder.AddAlphaRegion(10, 10, 380, 280); - - builder.AddHtml(20, 20, 360, 200, _message, background: true, scrollbar: true); - - builder.AddButton(100, 260, 4005, 4007, 1); - builder.AddHtmlLocalized(135, 262, 100, 20, 1011036); // OK - - builder.AddButton(250, 260, 4017, 4019, 0); - builder.AddHtmlLocalized(285, 262, 100, 20, 1011012); // Cancel - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID == 1) - { - _from.SendMessage("Confirmed!"); - } - } - - public static void DisplayTo(Mobile from, string message) - { - if (from?.NetState == null) - return; - - from.SendGump(new ConfirmGump(from, message)); - } -} - -// Usage: -ConfirmGump.DisplayTo(from, "Are you sure?"); -``` - -## ModernUO Equivalent (StaticGump — for fixed layouts) - -```csharp -using Server.Gumps; - -namespace Server.Gumps; - -public class ConfirmGump : StaticGump -{ - private readonly Mobile _from; - private readonly string _message; - - public override bool Singleton => true; - - private ConfirmGump(Mobile from, string message) : base(50, 50) - { - _from = from; - _message = message; - } - - protected override void BuildLayout(ref StaticGumpBuilder builder) - { - builder.AddPage(); - builder.AddBackground(0, 0, 400, 300, 5054); - builder.AddAlphaRegion(10, 10, 380, 280); - - builder.AddHtmlPlaceholder(20, 20, 360, 200, "message", true, true); - - builder.AddButton(100, 260, 4005, 4007, 1); - builder.AddHtmlLocalized(135, 262, 100, 20, 1011036); - - builder.AddButton(250, 260, 4017, 4019, 0); - builder.AddHtmlLocalized(285, 262, 100, 20, 1011012); - } - - protected override void BuildStrings(ref GumpStringsBuilder builder) - { - builder.SetStringSlot("message", _message); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID == 1) - _from.SendMessage("Confirmed!"); - } - - public static void DisplayTo(Mobile from, string message) - { - if (from?.NetState == null) - return; - from.SendGump(new ConfirmGump(from, message)); - } -} -``` - -## Migration Mapping Table - -| RunUO | ModernUO | Notes | -|---|---|---| -| `class Foo : Gump` | `class Foo : DynamicGump` or `class Foo : StaticGump` | Choose based on layout | -| Layout in constructor | Layout in `BuildLayout(ref builder)` | Move all AddXxx calls | -| `AddPage(0)` | `builder.AddPage()` | 0 is default | -| `AddBackground(...)` | `builder.AddBackground(...)` | Same args, on builder | -| `AddButton(x, y, n, p, id, GumpButtonType.Reply, 0)` | `builder.AddButton(x, y, n, p, id)` | Simplified — Reply is default | -| `AddButton(x, y, n, p, id, GumpButtonType.Page, p)` | `builder.AddButton(x, y, n, p, id, GumpButtonType.Page, p)` | Same for page nav | -| `AddHtml(x, y, w, h, text, bg, scroll)` | `builder.AddHtml(x, y, w, h, text, background: bg, scrollbar: scroll)` | Named params | -| `AddHtmlLocalized(x, y, w, h, num, bg, scroll)` | `builder.AddHtmlLocalized(x, y, w, h, num)` | Simplified | -| `AddLabel(x, y, hue, text)` | `builder.AddLabel(x, y, hue, text)` | Same | -| `AddTextEntry(x, y, w, h, hue, id, text)` | `builder.AddTextEntry(x, y, w, h, hue, id, text)` | Same | -| `AddCheck(x, y, off, on, state, id)` | `builder.AddCheckbox(x, y, off, on, state, id)` | Renamed | -| `AddRadio(x, y, off, on, state, id)` | `builder.AddRadio(x, y, off, on, state, id)` | Same | -| `Closable = false` | `builder.SetNoClose()` | Property → method | -| `Dragable = false` | `builder.SetNoMove()` | Property → method (note: Dragable → NoMove) | -| `Resizable = false` | `builder.SetNoResize()` | Property → method | -| `Disposable = false` | `builder.SetNoDispose()` | Property → method | -| `OnResponse(NetState, RelayInfo)` | `OnResponse(NetState, in RelayInfo)` | `in` keyword added | -| `info.TextEntries[i].Text` | `info.GetTextEntry(id)` | Direct lookup by ID | -| `info.Switches` contains check | `info.IsSwitched(switchID)` | Direct boolean check | -| `from.SendGump(new MyGump(...))` | `MyGump.DisplayTo(from, ...)` | Static entry point | -| `from.CloseGump(typeof(MyGump))` | `from.CloseGump()` | Generic method | -| `from.HasGump(typeof(MyGump))` | `from.HasGump()` | Generic method | - -## Step-by-Step Conversion - -### Step 1: Choose Target Type -| If the layout... | Convert to | -|---|---| -| Is the same structure every time | `StaticGump` (best performance) | -| Changes based on data (loops, conditionals) | `DynamicGump` | -| You're unsure | `DynamicGump` (simpler, still much better than legacy `Gump`) | - -### Step 2: Change Class Declaration -```csharp -// RunUO -public class MyGump : Gump - -// ModernUO -public class MyGump : DynamicGump -// OR -public class MyGump : StaticGump -``` - -### Step 3: Add Singleton If Needed -```csharp -public override bool Singleton => true; // Only one instance per player -``` - -### Step 4: Make Constructor Private, Add DisplayTo -```csharp -private MyGump(Mobile from) : base(50, 50) { _from = from; } - -public static void DisplayTo(Mobile from) -{ - if (from?.NetState == null) - return; - from.SendGump(new MyGump(from)); -} -``` - -### Step 5: Move Layout to BuildLayout -Move all `AddXxx()` calls from the constructor to `BuildLayout(ref DynamicGumpBuilder builder)`, prefixing each with `builder.`. - -### Step 6: Convert Properties to Builder Methods -```csharp -// RunUO properties in constructor -Closable = false; -Dragable = false; - -// ModernUO methods in BuildLayout -builder.SetNoClose(); -builder.SetNoMove(); -``` - -### Step 7: Update OnResponse Signature -```csharp -// RunUO -public override void OnResponse(NetState sender, RelayInfo info) - -// ModernUO -public override void OnResponse(NetState sender, in RelayInfo info) -``` - -### Step 8: Update Text Entry and Switch Access -```csharp -// RunUO -TextRelay relay = info.GetTextEntry(0); -string text = relay != null ? relay.Text : ""; - -// ModernUO -string text = info.GetTextEntry(0); -``` - -```csharp -// RunUO -bool isChecked = info.IsSwitched(switchID); -// ModernUO — same -bool isChecked = info.IsSwitched(switchID); -``` - -### Step 9: For StaticGump — Extract Dynamic Text -Replace variable text with placeholders: -```csharp -// In BuildLayout: -builder.AddLabelPlaceholder(x, y, hue, "playerName"); -builder.AddHtmlPlaceholder(x, y, w, h, "description", false, true); - -// In BuildStrings: -protected override void BuildStrings(ref GumpStringsBuilder builder) -{ - builder.SetStringSlot("playerName", _from.Name); - builder.SetHtmlText("description", _text, "#FFC000", 4); -} -``` - -### Step 10: Update Callers -```csharp -// RunUO -from.SendGump(new MyGump(from)); -from.CloseGump(typeof(MyGump)); - -// ModernUO -MyGump.DisplayTo(from); -from.CloseGump(); -``` - -## Empty Gump Rule (CRITICAL) - -**NEVER send a gump with no visual components.** An empty gump has no close button and cannot be dismissed — it leaks on both client and server. - -This typically happens when constructor logic short-circuits: -```csharp -// BAD — gump is created empty, then sent -public MyGump(Mobile from) : base(50, 50) -{ - if (!from.Alive) - return; // Empty gump created! - AddPage(0); - // ... -} -``` - -**Fix**: Use the static `DisplayTo()` pattern. Validate before constructing: -```csharp -private MyGump(Mobile from) : base(50, 50) { /* always builds layout */ } - -public static void DisplayTo(Mobile from) -{ - if (!from.Alive || from.NetState == null) - return; // No gump created at all - from.SendGump(new MyGump(from)); -} -``` - -## Edge Cases & Gotchas - -### 1. using Server.Gumps Is Required -The extension methods `SendGump()`, `HasGump()`, `FindGump()`, `CloseGump()` are in the `Server.Gumps` namespace. Without the using, they won't resolve. - -### 2. StaticGump Caching -StaticGump caches layout bytes on first use. During development, override `Cached => false` to rebuild every time, but remove before committing. - -### 3. Button ID 0 = Close -Button ID 0 is reserved for close/cancel. Use 1+ for action buttons. - -### 4. GumpButtonType.Reply Is Default -In ModernUO, `AddButton(x, y, n, p, id)` defaults to Reply type. Only specify `GumpButtonType.Page` for page navigation buttons. - -## See Also - -- `dev-docs/gump-system.md` — Complete ModernUO gump reference -- `01-foundation-changes.md` — Foundation changes diff --git a/dev-docs/runuo-migration-docs/05-packets-networking.md b/dev-docs/runuo-migration-docs/05-packets-networking.md deleted file mode 100644 index a722825bd..000000000 --- a/dev-docs/runuo-migration-docs/05-packets-networking.md +++ /dev/null @@ -1,309 +0,0 @@ -# Packets & Networking Migration - -## Overview - -RunUO uses a `Packet` class hierarchy where outgoing packets are objects with `PacketWriter`, and incoming packets use `PacketHandler` with `PacketReader`. ModernUO completely removes the `Packet` class hierarchy. Outgoing packets are static methods using `SpanWriter` with stack-allocated buffers. Incoming packets are registered function pointers using `SpanReader`. - -## RunUO Outgoing Packet Pattern - -```csharp -// RunUO — Packet subclass -public sealed class MyPacket : Packet -{ - public MyPacket(Serial target, int value) : base(0xBF, 12) - { - m_Stream.Write((ushort)12); - m_Stream.Write((ushort)0x99); - m_Stream.Write((int)target); - m_Stream.Write((short)value); - } -} - -// Usage: -ns.Send(new MyPacket(target.Serial, 42)); -``` - -## ModernUO Outgoing Packet Pattern - -```csharp -// ModernUO — Static create method + extension method -public static class OutgoingMyPackets -{ - public const int MyPacketLength = 12; - - public static void CreateMyPacket(Span buffer, Serial target, int value) - { - if (buffer[0] != 0) - return; - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xBF); - writer.Write((ushort)12); - writer.Write((ushort)0x99); - writer.Write(target); - writer.Write((short)value); - } -} - -public static class MyPacketExtensions -{ - public static void SendMyPacket(this NetState ns, Serial target, int value) - { - if (ns.CannotSendPackets()) - return; - - var buffer = stackalloc byte[OutgoingMyPackets.MyPacketLength].InitializePacket(); - OutgoingMyPackets.CreateMyPacket(buffer, target, value); - ns.Send(buffer); - } -} - -// Usage: -mobile.NetState.SendMyPacket(target.Serial, 42); -``` - -## RunUO Incoming Packet Pattern - -```csharp -// RunUO — PacketHandler registration -public class MyPacketHandlers -{ - public static void Initialize() - { - PacketHandlers.Register(0x99, 12, true, new OnPacketReceive(MyHandler)); - } - - public static void MyHandler(NetState state, PacketReader pvSrc) - { - Serial target = pvSrc.ReadInt32(); - int value = pvSrc.ReadInt16(); - // Process... - } -} -``` - -## ModernUO Incoming Packet Pattern - -```csharp -// ModernUO — Function pointer registration -public static class IncomingMyPackets -{ - public static unsafe void Configure() - { - IncomingPackets.Register(0x99, 12, true, &MyHandler); - } - - public static void MyHandler(NetState state, SpanReader reader) - { - var target = (Serial)reader.ReadUInt32(); - var value = reader.ReadInt16(); - // Process... - } -} -``` - -## Migration Mapping Table - -| RunUO | ModernUO | Notes | -|---|---|---| -| `class MyPacket : Packet` | Static `CreateXxx(Span)` method | No class hierarchy | -| `Packet(packetId, length)` constructor | `new SpanWriter(buffer)` | Stack-allocated buffer | -| `m_Stream.Write(value)` | `writer.Write(value)` | Same method names | -| `PacketWriter` | `SpanWriter` | Ref struct, stack-allocated | -| `PacketReader` | `SpanReader` | Ref struct | -| `pvSrc.ReadInt32()` | `reader.ReadInt32()` | Same names | -| `pvSrc.ReadString()` | `reader.ReadAsciiSafe()` or `reader.ReadBigUniSafe()` | Explicit encoding | -| `pvSrc.ReadUnicodeStringSafe()` | `reader.ReadBigUniSafe()` | Explicit name | -| `ns.Send(new MyPacket(...))` | `ns.SendMyPacket(...)` | Extension method | -| `PacketHandlers.Register(id, len, ingame, handler)` | `IncomingPackets.Register(id, len, ingame, &handler)` | Function pointer | -| `new OnPacketReceive(handler)` | `&handler` | Function pointer, no delegate | -| `Packet.Compile()` / `Packet.SetStatic()` | Not needed | Buffer is stack-allocated | -| `Packet.Acquire()` / `Packet.Release()` | Not needed | No pooling, stack memory | -| Variable-length: `this.EnsureCapacity(len)` | `writer.WritePacketLength()` at end | Fill length at position 1-2 | - -## Step-by-Step Conversion - -### Outgoing Packets - -#### Step 1: Create Static Class -```csharp -public static class OutgoingMySystemPackets -{ - // Constants and Create methods go here -} -``` - -#### Step 2: Convert Packet Constructor to Create Method -```csharp -// Determine packet length from RunUO constructor: base(0xBF, 12) -public const int MyPacketLength = 12; - -public static void CreateMyPacket(Span buffer, Serial target, int value) -{ - if (buffer[0] != 0) // Already initialized guard - return; - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)12); // Length - writer.Write((ushort)0x99); // Sub-command - writer.Write(target); // Serial - writer.Write((short)value); // Value -} -``` - -#### Step 3: Create Send Extension Method -```csharp -public static void SendMyPacket(this NetState ns, Serial target, int value) -{ - if (ns.CannotSendPackets()) - return; - - var buffer = stackalloc byte[MyPacketLength].InitializePacket(); - CreateMyPacket(buffer, target, value); - ns.Send(buffer); -} -``` - -#### Step 4: For Variable-Length Packets -```csharp -public static void SendDynamicPacket(this NetState ns, string name, int[] values) -{ - if (ns.CannotSendPackets()) - return; - - var length = 7 + name.Length * 2 + values.Length * 4; - var writer = new SpanWriter(stackalloc byte[length]); - - writer.Write((byte)0x99); - writer.Write((ushort)0); // Length placeholder - writer.WriteBigUniNull(name); - writer.Write((ushort)values.Length); - - foreach (var val in values) - writer.Write(val); - - writer.WritePacketLength(); // Fill in actual length - ns.Send(writer.Span); -} -``` - -### Incoming Packets - -#### Step 1: Change Registration -```csharp -// RunUO (Initialize) -PacketHandlers.Register(0x99, 12, true, new OnPacketReceive(MyHandler)); - -// ModernUO (Configure, unsafe) -public static unsafe void Configure() -{ - IncomingPackets.Register(0x99, 12, true, &MyHandler); -} -``` - -#### Step 2: Update Handler Signature -```csharp -// RunUO -public static void MyHandler(NetState state, PacketReader pvSrc) - -// ModernUO -public static void MyHandler(NetState state, SpanReader reader) -``` - -#### Step 3: Update Read Methods -```csharp -// RunUO → ModernUO -pvSrc.ReadInt32() → reader.ReadInt32() -pvSrc.ReadInt16() → reader.ReadInt16() -pvSrc.ReadByte() → reader.ReadByte() -pvSrc.ReadBoolean() → reader.ReadBoolean() -pvSrc.ReadString() → reader.ReadAsciiSafe() -pvSrc.ReadUnicodeStringSafe() → reader.ReadBigUniSafe() -pvSrc.ReadUnicodeString() → reader.ReadBigUni() -pvSrc.Seek(offset, origin) → reader.Seek(offset, origin) -``` - -## SpanWriter Quick Reference - -```csharp -writer.Write(bool); // 1 byte -writer.Write(byte); // 1 byte -writer.Write(short); // 2 bytes big-endian -writer.Write(ushort); // 2 bytes big-endian -writer.Write(int); // 4 bytes big-endian -writer.Write(uint); // 4 bytes big-endian -writer.Write(Serial); // 4 bytes -writer.WriteAsciiNull(str); // ASCII null-terminated -writer.WriteBigUniNull(str); // UTF-16 BE null-terminated -writer.WriteLE(int); // 4 bytes little-endian -writer.WritePacketLength(); // Fill length at pos 1-2 -``` - -## SpanReader Quick Reference - -```csharp -reader.ReadByte(); // 1 byte -reader.ReadBoolean(); // 1 byte -reader.ReadInt16(); // 2 bytes big-endian -reader.ReadUInt16(); // 2 bytes big-endian -reader.ReadInt32(); // 4 bytes big-endian -reader.ReadUInt32(); // 4 bytes big-endian -reader.ReadAsciiSafe(); // ASCII, filtered -reader.ReadBigUniSafe(); // UTF-16 BE, filtered -reader.Seek(offset, origin); // Position -reader.Remaining; // Bytes left -``` - -## Shared Buffer Pattern - -When sending the same packet to multiple players: -```csharp -public static void SendToNearby(Mobile source, int effectId) -{ - Span buffer = stackalloc byte[EffectPacketLength]; - buffer.InitializePacket(); - - foreach (var ns in source.GetClientsInRange(18)) - { - CreateEffectPacket(buffer, source.Serial, effectId); - ns.Send(buffer); - } -} -``` - -The `buffer[0] != 0` guard in `Create` methods prevents re-initialization, so the buffer is built once and reused. - -## Edge Cases & Gotchas - -### 1. Always Check CannotSendPackets() -```csharp -if (ns.CannotSendPackets()) - return; -``` - -### 2. Use InitializePacket() on stackalloc -```csharp -var buffer = stackalloc byte[length].InitializePacket(); -``` - -### 3. Big-Endian by Default -UO protocol is big-endian. Only use `WriteLE`/`ReadLE` when specifically needed. - -### 4. Use Safe String Reads -Always use `ReadAsciiSafe()`/`ReadBigUniSafe()` for incoming strings to filter control characters. - -### 5. Function Pointers Require unsafe -The `Configure()` method must be marked `unsafe` for function pointer syntax `&handler`. - -### 6. Many Common Packets Already Exist -Before writing custom packet code, check if ModernUO already has a `Send*` extension method in: -- `OutgoingMobilePackets` — Mobile status, animation, movement -- `OutgoingItemPackets` — Item display, updates -- `OutgoingEffectPackets` — Effects, sounds -- `OutgoingContainerPackets` — Container contents - -## See Also - -- `dev-docs/networking-packets.md` — Complete ModernUO networking reference -- `01-foundation-changes.md` — Foundation changes diff --git a/dev-docs/runuo-migration-docs/06-property-lists.md b/dev-docs/runuo-migration-docs/06-property-lists.md deleted file mode 100644 index 2df7f408f..000000000 --- a/dev-docs/runuo-migration-docs/06-property-lists.md +++ /dev/null @@ -1,194 +0,0 @@ -# Property Lists (Tooltips) Migration - -## Overview - -RunUO uses `ObjectPropertyList` as both the interface and implementation for item/mobile tooltips. ModernUO introduces an `IPropertyList` interface and has a critical rule: string literals in interpolated strings must be wrapped as holes (`{"text"}` not `text`). - -## RunUO Pattern - -```csharp -public override void GetProperties(ObjectPropertyList list) -{ - base.GetProperties(list); - list.Add(1060741, m_Charges.ToString()); // "charges: ~1_val~" - list.Add(1060658, "Map\t" + m_MapDest); // "~1_val~: ~2_val~" - list.Add(1060637, "{0}\t{1}", m_Current, m_Max); // "~1_val~ / ~2_val~" - list.Add("Custom text line"); -} -``` - -## ModernUO Equivalent - -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); - list.Add(1060741, $"{_charges}"); // "charges: ~1_val~" - list.Add(1060658, $"{"Map"}\t{_mapDest}"); // "~1_val~: ~2_val~" — "Map" is a hole! - list.Add(1060637, $"{_current}\t{_max}"); // "~1_val~ / ~2_val~" - list.Add($"{"Custom text line"}"); // Raw string — still a hole -} -``` - -## Migration Mapping Table - -| RunUO | ModernUO | Notes | -|---|---|---| -| `GetProperties(ObjectPropertyList list)` | `GetProperties(IPropertyList list)` | Interface instead of class | -| `list.Add(number, string.Format(...))` | `list.Add(number, $"...")` | Interpolated string | -| `list.Add(number, value.ToString())` | `list.Add(number, $"{value}")` | Interpolated | -| `list.Add(number, "text\t" + value)` | `list.Add(number, $"{"text"}\t{value}")` | Text must be hole | -| `list.Add(number, string.Format("{0}\t{1}", a, b))` | `list.Add(number, $"{a}\t{b}")` | Tab-separated args | -| `list.Add("raw string")` | `list.Add($"{"raw string"}")` | String literal as hole | -| `list.Add(number)` | `list.Add(number)` | Same — cliloc only | -| `list.Add(number, "#" + cliloc)` | `list.Add(number, $"{cliloc:#}")` | Cliloc as argument | - -## The String Literal Rule (CRITICAL) - -The `IPropertyList` interpolated string handler distinguishes between **literals** (text between `{}` holes) and **holes** (values inside `{}`). Literals are treated as delimiters (like `\t`). Holes are treated as cliloc arguments. - -**Rule**: The only bare literal text should be `\t` (argument separator). All other text must be inside `{}` holes. - -```csharp -// BAD — "Map" is a literal, treated as a delimiter -list.Add(1060658, $"Map\t{_mapDest}"); - -// GOOD — "Map" is a hole, treated as argument ~1_val~ -list.Add(1060658, $"{"Map"}\t{_mapDest}"); -``` - -Why this matters: The property list data is consumed beyond just the game client (e.g., web renderers). The system must distinguish arguments from delimiters to correctly format tooltips for all consumers. - -### Cliloc Number as Argument - -When a cliloc argument is itself a cliloc number to resolve: - -```csharp -// BAD — string "#1060000" is not properly handled by all consumers -list.Add(1050039, $"{_amount}\t{"#1060000"}"); - -// GOOD — :# format specifier marks it as a cliloc reference -list.Add(1050039, $"{_amount}\t{1060000:#}"); -``` - -Or use the convenience methods: -```csharp -list.AddLocalized(clilocNumber); // Single cliloc value -list.AddLocalized(1050039, clilocNumber); // Cliloc with cliloc argument -``` - -## Step-by-Step Conversion - -### Step 1: Change Method Signature -```csharp -// RunUO -public override void GetProperties(ObjectPropertyList list) -// ModernUO -public override void GetProperties(IPropertyList list) -``` - -### Step 2: Always Call Base First -```csharp -base.GetProperties(list); // Unchanged -``` - -### Step 3: Convert Each list.Add() Call - -**Simple value:** -```csharp -// RunUO -list.Add(1060741, m_Charges.ToString()); -// ModernUO -list.Add(1060741, $"{_charges}"); -``` - -**Multiple tab-separated arguments:** -```csharp -// RunUO -list.Add(1060637, string.Format("{0}\t{1}", m_Current, m_Max)); -// ModernUO -list.Add(1060637, $"{_current}\t{_max}"); -``` - -**String literal arguments:** -```csharp -// RunUO -list.Add(1060658, "Coords\t" + m_Location.ToString()); -// ModernUO -list.Add(1060658, $"{"Coords"}\t{_location}"); -``` - -**Raw text:** -```csharp -// RunUO -list.Add("Soulbound"); -// ModernUO -list.Add($"{"Soulbound"}"); -``` - -### Step 4: Add [InvalidateProperties] to Serialized Fields -If the RunUO property setter called `InvalidateProperties()`, add the attribute: - -```csharp -[SerializableField(0)] -[InvalidateProperties] // Auto-refreshes tooltip -[SerializedCommandProperty(AccessLevel.GameMaster)] -private int _charges; -``` - -## Before/After Example - -**RunUO:** -```csharp -public override void GetProperties(ObjectPropertyList list) -{ - base.GetProperties(list); - list.Add(1060741, m_Charges.ToString()); - list.Add(1060658, "Map\t" + m_MapDest); - list.Add(1060659, "Coords\t" + m_PointDest); - list.Add(1060660, "Creatures\t" + (m_Creatures ? "Yes" : "No")); - list.Add(1060661, "Range\t" + m_Range); - - if (m_Active) - list.Add(1060742); // "active" -} -``` - -**ModernUO:** -```csharp -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); - list.Add(1060741, $"{_charges}"); - list.Add(1060658, $"{"Map"}\t{_mapDest}"); - list.Add(1060659, $"{"Coords"}\t{_pointDest}"); - list.Add(1060660, $"{"Creatures"}\t{(Creatures ? "Yes" : "No")}"); - list.Add(1060661, $"{"Range"}\t{_range}"); - - if (_active) - list.Add(1060742); -} -``` - -## Edge Cases & Gotchas - -### 1. Integer Overload vs String Interpolation -`list.Add(number, int)` exists and is different from `list.Add(number, $"{int}")`. The integer overload passes the raw int; the interpolation formats it as a string. Use whichever matches the cliloc expectation. - -### 2. Era-Conditional Properties -Check era when properties differ between expansions: -```csharp -if (Core.ML) - list.Add(1072241, $"{TotalItems}\t{MaxItems}\t{TotalWeight}\t{MaxWeight}"); -else - list.Add(1050044, $"{TotalItems}\t{TotalWeight}"); -``` - -### 3. Don't Call InvalidateProperties() in Loops -It triggers hash computation and potential network sends. Batch changes first. - -## See Also - -- `dev-docs/property-lists.md` — Complete ModernUO property list reference -- `02-serialization.md` — [InvalidateProperties] on serialized fields -- `01-foundation-changes.md` — Foundation changes diff --git a/dev-docs/runuo-migration-docs/07-commands-events.md b/dev-docs/runuo-migration-docs/07-commands-events.md deleted file mode 100644 index a3a261bfb..000000000 --- a/dev-docs/runuo-migration-docs/07-commands-events.md +++ /dev/null @@ -1,211 +0,0 @@ -# Commands & Events Migration - -## Overview - -Commands are largely similar between RunUO and ModernUO — the `CommandSystem.Register()` API is the same. The main changes are handler attribute conventions and that events now use `Action` delegates instead of custom delegate types. - -## Command Changes - -### Registration (Same Pattern) -```csharp -// Both RunUO and ModernUO -public static void Configure() // ModernUO uses Configure(), RunUO may use Initialize() -{ - CommandSystem.Register("MyCommand", AccessLevel.GameMaster, MyCommand_OnCommand); -} -``` - -**Key difference**: ModernUO prefers `Configure()` for registration. RunUO often uses `Initialize()`. Both work, but `Configure()` runs earlier in the startup sequence and is the convention. - -### Handler Attributes -```csharp -// RunUO -[Usage("MyCommand ")] -[Description("Does something")] - -// ModernUO — same attributes, plus optional Aliases -[Usage("MyCommand ")] -[Description("Does something")] -[Aliases("mc", "mycmd")] -``` - -### CommandEventArgs (Same) -```csharp -public static void MyCommand_OnCommand(CommandEventArgs e) -{ - var from = e.Mobile; - var name = e.GetString(0); - var count = e.Length > 1 ? e.GetInt32(1) : 1; -} -``` - -No changes needed for command handlers themselves. - -## Event System Migration - -### RunUO EventSink Pattern -```csharp -// RunUO — custom delegate types -public static void Initialize() -{ - EventSink.WorldSave += new WorldSaveEventHandler(OnWorldSave); - EventSink.WorldLoad += new WorldLoadEventHandler(OnWorldLoad); - EventSink.Login += new LoginEventHandler(OnLogin); - EventSink.Logout += new LogoutEventHandler(OnLogout); - EventSink.Speech += new SpeechEventHandler(OnSpeech); - EventSink.Movement += new MovementEventHandler(OnMovement); - EventSink.ServerStarted += new ServerStartedEventHandler(OnServerStarted); - EventSink.Crashed += new CrashedEventHandler(OnCrashed); -} - -private static void OnWorldSave(WorldSaveEventArgs e) -{ - // Save data to file -} - -private static void OnLogin(LoginEventArgs e) -{ - Mobile m = e.Mobile; - m.SendMessage("Welcome!"); -} -``` - -### ModernUO EventSink Pattern -```csharp -// ModernUO — Action delegates, changed event names -public static void Configure() -{ - EventSink.WorldSave += OnWorldSave; // Action (no args) - EventSink.WorldLoad += OnWorldLoad; // Action (no args) - EventSink.Connected += OnConnected; // Action — was Login - EventSink.Disconnected += OnDisconnected; // Action — was Logout - EventSink.Speech += OnSpeech; // Action - EventSink.Movement += OnMovement; // Action - EventSink.ServerStarted += OnServerStarted; // Action (no args) - EventSink.ServerCrashed += OnCrashed; // Action -} - -private static void OnWorldSave() -{ - // For persistence, use GenericPersistence instead (see 08-persistence.md) -} - -private static void OnConnected(Mobile m) -{ - m.SendMessage("Welcome!"); -} -``` - -## Event Migration Mapping - -| RunUO Event | ModernUO Event | Signature Change | -|---|---|---| -| `EventSink.WorldSave` | `EventSink.WorldSave` | `WorldSaveEventArgs` → `Action` (no args) | -| `EventSink.WorldLoad` | `EventSink.WorldLoad` | `WorldLoadEventArgs` → `Action` (no args) | -| `EventSink.Login` | `EventSink.Connected` | `LoginEventArgs` → `Action` | -| `EventSink.Logout` | `EventSink.Disconnected` | `LogoutEventArgs` → `Action` | -| `EventSink.Speech` | `EventSink.Speech` | `SpeechEventArgs` (mostly same) | -| `EventSink.Movement` | `EventSink.Movement` | `MovementEventArgs` (mostly same) | -| `EventSink.ServerStarted` | `EventSink.ServerStarted` | `Action` (no args) | -| `EventSink.Crashed` | `EventSink.ServerCrashed` | Renamed | -| `EventSink.AggressiveAction` | `EventSink.AggressiveAction` | `AggressiveActionEventArgs` | -| `EventSink.AccountLogin` | `EventSink.AccountLogin` | `AccountLoginEventArgs` | -| `EventSink.SocketConnect` | `EventSink.SocketConnect` | `SocketConnectEventArgs` | -| `EventSink.BeforeWorldSave` | Removed | Use `WorldSave` event directly | -| `EventSink.Shutdown` | `EventSink.Shutdown` | `Action` | -| `EventSink.CharacterCreated` | Removed/Restructured | Check current source | -| `EventSink.OpenDoorMacroUsed` | Removed | Handle in movement/speech | -| `EventSink.PlayerDeath` | Use CodeGeneratedEvents | `PlayerMobile.PlayerDeathEvent` | -| `EventSink.CreatureDeath` | Use CodeGeneratedEvents | `BaseCreature.CreatureDeathEvent` | - -## Step-by-Step Conversion - -### Step 1: Change Initialize() to Configure() -```csharp -// RunUO -public static void Initialize() - -// ModernUO -public static void Configure() -``` - -### Step 2: Remove Delegate Type Constructors -```csharp -// RunUO -EventSink.Login += new LoginEventHandler(OnLogin); - -// ModernUO -EventSink.Connected += OnConnected; -``` - -### Step 3: Update Event Names -Rename `Login` to `Connected`, `Logout` to `Disconnected`, etc. (see mapping table). - -### Step 4: Update Handler Signatures -```csharp -// RunUO -private static void OnLogin(LoginEventArgs e) -{ - Mobile m = e.Mobile; -} - -// ModernUO -private static void OnConnected(Mobile m) -{ - // Mobile is passed directly -} -``` - -### Step 5: WorldSave/WorldLoad → GenericPersistence -If the event handler was saving/loading data to binary files, convert to `GenericPersistence` instead. See `08-persistence.md`. - -```csharp -// RunUO — manual file persistence via EventSink -EventSink.WorldSave += new WorldSaveEventHandler(Save); -EventSink.WorldLoad += new WorldLoadEventHandler(Load); - -// ModernUO — use GenericPersistence class (see 08-persistence.md) -// Don't use EventSink.WorldSave for custom persistence -``` - -## CodeGeneratedEvents - -For entity-specific events, ModernUO uses source-generated events: - -```csharp -// Subscribing to a generated event -[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))] -public static void HandlePlayerLogin(PlayerMobile player) -{ - // Handle player login -} -``` - -Known generated events: -- `PlayerMobile.PlayerLoginEvent` -- `PlayerMobile.PlayerDeathEvent` -- `BaseCreature.CreatureDeathEvent` - -## Edge Cases & Gotchas - -### 1. WorldSave No Longer Has EventArgs -RunUO's `WorldSaveEventArgs` contained the save path. In ModernUO, use `WorldSavePostSnapshot` event if you need paths, or better yet, use `GenericPersistence`. - -### 2. Login/Logout → Connected/Disconnected -The names changed AND the signatures changed. `LoginEventArgs.Mobile` → direct `Mobile` parameter. - -### 3. Subscribe in Configure(), Not Initialize() -`Configure()` runs before `Initialize()`. Event subscriptions should happen early. - -### 4. Pooled EventArgs -Some events (Movement, AggressiveAction) use pooled args. Don't store references to them — they get recycled. - -### 5. Handled/Blocked Properties -`SpeechEventArgs.Handled` and `.Blocked` work the same. Set `Handled = true` to consume, `Blocked = true` to block. - -## See Also - -- `dev-docs/events.md` — Complete ModernUO event system reference -- `dev-docs/commands-targeting.md` — Complete command/targeting reference -- `08-persistence.md` — Converting WorldSave persistence -- `01-foundation-changes.md` — Foundation changes diff --git a/dev-docs/runuo-migration-docs/08-persistence.md b/dev-docs/runuo-migration-docs/08-persistence.md deleted file mode 100644 index dd30668f2..000000000 --- a/dev-docs/runuo-migration-docs/08-persistence.md +++ /dev/null @@ -1,398 +0,0 @@ -# Persistence Migration - -## Overview - -RunUO systems that persist custom data use `EventSink.WorldSave`/`EventSink.WorldLoad` with manual `BinaryFileWriter`/`BinaryFileReader`. ModernUO replaces this with `GenericPersistence` (for system state) and `GenericEntityPersistence` (for custom entity collections). The persistence framework handles save/load lifecycle, file management, and integration with ModernUO's multi-threaded save pipeline automatically. - -## RunUO Pattern - -```csharp -using System; -using System.IO; -using Server; - -namespace Server.Custom -{ - public class JailSystem - { - private static Dictionary m_Records = new Dictionary(); - - public static void Configure() - { - EventSink.WorldLoad += new WorldLoadEventHandler(Load); - EventSink.WorldSave += new WorldSaveEventHandler(Save); - } - - private static void Load() - { - string filePath = Path.Combine("Saves/Custom", "JailSystem.bin"); - - if (!File.Exists(filePath)) - return; - - using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - BinaryReader reader = new BinaryReader(fs); - int version = reader.ReadInt32(); - - int count = reader.ReadInt32(); - for (int i = 0; i < count; i++) - { - Mobile m = World.FindMobile(reader.ReadInt32()); - string reason = reader.ReadString(); - DateTime releaseDate = new DateTime(reader.ReadInt64()); - - if (m != null) - m_Records[m] = new JailRecord(reason, releaseDate); - } - } - } - - private static void Save(WorldSaveEventArgs e) - { - string dirPath = "Saves/Custom"; - if (!Directory.Exists(dirPath)) - Directory.CreateDirectory(dirPath); - - string filePath = Path.Combine(dirPath, "JailSystem.bin"); - - using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - { - BinaryWriter writer = new BinaryWriter(fs); - writer.Write((int)0); // version - writer.Write(m_Records.Count); - - foreach (var kvp in m_Records) - { - writer.Write(kvp.Key.Serial.Value); - writer.Write(kvp.Value.Reason); - writer.Write(kvp.Value.ReleaseDate.Ticks); - } - } - } - } -} -``` - -## ModernUO Equivalent (GenericPersistence) - -```csharp -using Server; -using Server.Serialization; - -namespace Server.Custom; - -public class JailSystem : GenericPersistence -{ - private static JailSystem _instance; - private static Dictionary _records = new(); - - public static void Configure() - { - _instance = new JailSystem(); - } - - public JailSystem() : base("JailSystem", 10) { } - - public override void Serialize(IGenericWriter writer) - { - writer.WriteEncodedInt(0); // version - writer.WriteEncodedInt(_records.Count); - - foreach (var (mobile, record) in _records) - { - writer.Write(mobile); - writer.Write(record.Reason); - writer.Write(record.ReleaseDate); - } - } - - public override void Deserialize(IGenericReader reader) - { - var version = reader.ReadEncodedInt(); - var count = reader.ReadEncodedInt(); - - for (var i = 0; i < count; i++) - { - var mobile = reader.ReadEntity(); - var reason = reader.ReadString(); - var releaseDate = reader.ReadDateTime(); - - if (mobile != null) - _records[mobile] = new JailRecord(reason, releaseDate); - } - } - - // Public API for the system - public static void JailPlayer(Mobile m, string reason, DateTime releaseDate) - { - _records[m] = new JailRecord(reason, releaseDate); - _instance.MarkDirty(); - } - - public static bool IsJailed(Mobile m) => _records.ContainsKey(m); - - public static void Release(Mobile m) - { - _records.Remove(m); - _instance.MarkDirty(); - } -} -``` - -## When to Use Which - -| Scenario | Use | -|---|---| -| System state (jail records, virtue data, faction data, scores) | `GenericPersistence` | -| Custom entity collections with their own serial ranges | `GenericEntityPersistence` | -| Item/Mobile subclasses (normal serialization) | `[SerializationGenerator]` — not persistence | - -Most RunUO `EventSink.WorldSave` patterns should convert to `GenericPersistence`. - -## Migration Mapping Table - -| RunUO | ModernUO | Notes | -|---|---|---| -| `EventSink.WorldSave += Save` | `class MySystem : GenericPersistence` | Subclass instead | -| `EventSink.WorldLoad += Load` | `override Deserialize(IGenericReader)` | Method on class | -| Manual `Save(WorldSaveEventArgs)` | `override Serialize(IGenericWriter)` | Method on class | -| Manual `Load()` | `override Deserialize(IGenericReader)` | Method on class | -| `new BinaryFileWriter(path, true)` | Handled by framework | No file management | -| `new BinaryFileReader(new FileStream(...))` | Handled by framework | No file management | -| `writer.Write((int)0)` version | `writer.WriteEncodedInt(0)` | Encoded preferred | -| `reader.ReadInt()` count | `reader.ReadEncodedInt()` | Encoded preferred | -| `writer.Write(mobile.Serial.Value)` | `writer.Write(mobile)` | Write entity directly | -| `World.FindMobile(reader.ReadInt32())` | `reader.ReadEntity()` | Generic method | -| `World.FindItem(reader.ReadInt32())` | `reader.ReadEntity()` | Generic method | -| `Directory.CreateDirectory(...)` | Handled by framework | Automatic | -| `File.Exists(path)` check | Handled by framework | Automatic | - -## Step-by-Step Conversion - -### Step 1: Create Persistence Class -```csharp -public class MySystem : GenericPersistence -{ - private static MySystem _instance; - - public static void Configure() - { - _instance = new MySystem(); - } - - public MySystem() : base("MySystem", 10) { } - // "MySystem" = save file name - // 10 = priority (lower = saved first) -} -``` - -### Step 2: Move Save Logic to Serialize -```csharp -public override void Serialize(IGenericWriter writer) -{ - writer.WriteEncodedInt(0); // version - - // Convert BinaryWriter calls to IGenericWriter calls - writer.WriteEncodedInt(_data.Count); - foreach (var (key, value) in _data) - { - writer.Write(key); // Can write Mobile/Item directly - writer.Write(value); - } -} -``` - -### Step 3: Move Load Logic to Deserialize -```csharp -public override void Deserialize(IGenericReader reader) -{ - var version = reader.ReadEncodedInt(); - var count = reader.ReadEncodedInt(); - - for (var i = 0; i < count; i++) - { - var key = reader.ReadEntity(); // Not World.FindMobile() - var value = reader.ReadInt(); - - if (key != null) - _data[key] = value; - } -} -``` - -### Step 4: Remove EventSink Subscriptions -Delete the `EventSink.WorldSave += ...` and `EventSink.WorldLoad += ...` lines. - -### Step 5: Add MarkDirty() Calls -Whenever data changes, call `_instance.MarkDirty()` to flag the system for saving: -```csharp -public static void AddRecord(Mobile m, string data) -{ - _records[m] = data; - _instance.MarkDirty(); // Required! -} -``` - -### Step 6: Remove File Management Code -Delete all `Directory.CreateDirectory`, `File.Exists`, `FileStream`, path construction. The framework handles this. - -## IGenericWriter vs BinaryWriter - -| BinaryWriter (RunUO) | IGenericWriter (ModernUO) | -|---|---| -| `writer.Write((int)value)` | `writer.Write(value)` or `writer.WriteEncodedInt(value)` | -| `writer.Write((string)value)` | `writer.Write(value)` | -| `writer.Write((bool)value)` | `writer.Write(value)` | -| `writer.Write(mobile.Serial.Value)` | `writer.Write(mobile)` | -| `writer.Write(item.Serial.Value)` | `writer.Write(item)` | -| `writer.Write(dateTime.Ticks)` | `writer.Write(dateTime)` | -| No encoded int | `writer.WriteEncodedInt(value)` — variable-length, saves space | - -## IGenericReader vs BinaryReader - -| BinaryReader (RunUO) | IGenericReader (ModernUO) | -|---|---| -| `reader.ReadInt32()` | `reader.ReadInt()` or `reader.ReadEncodedInt()` | -| `reader.ReadString()` | `reader.ReadString()` | -| `reader.ReadBoolean()` | `reader.ReadBool()` | -| `World.FindMobile(reader.ReadInt32())` | `reader.ReadEntity()` | -| `World.FindItem(reader.ReadInt32())` | `reader.ReadEntity()` | -| `new DateTime(reader.ReadInt64())` | `reader.ReadDateTime()` | - -## Before/After: Complete System - -**RunUO:** -```csharp -namespace Server.Custom -{ - public class VirtueSystem - { - private static Dictionary m_Points = new Dictionary(); - - public static void Configure() - { - EventSink.WorldLoad += new WorldLoadEventHandler(Load); - EventSink.WorldSave += new WorldSaveEventHandler(Save); - } - - private static void Load() - { - string path = Path.Combine("Saves/Custom", "Virtue.bin"); - if (!File.Exists(path)) return; - - using var fs = new FileStream(path, FileMode.Open); - var reader = new BinaryReader(fs); - - int version = reader.ReadInt32(); - int count = reader.ReadInt32(); - - for (int i = 0; i < count; i++) - { - Mobile m = World.FindMobile(reader.ReadInt32()); - int pts = reader.ReadInt32(); - if (m != null) - m_Points[m] = pts; - } - } - - private static void Save(WorldSaveEventArgs e) - { - string dir = "Saves/Custom"; - if (!Directory.Exists(dir)) - Directory.CreateDirectory(dir); - - using var fs = new FileStream(Path.Combine(dir, "Virtue.bin"), FileMode.Create); - var writer = new BinaryWriter(fs); - - writer.Write(0); // version - writer.Write(m_Points.Count); - foreach (var kvp in m_Points) - { - writer.Write(kvp.Key.Serial.Value); - writer.Write(kvp.Value); - } - } - - public static void AddPoints(Mobile m, int points) - { - if (!m_Points.ContainsKey(m)) - m_Points[m] = 0; - m_Points[m] += points; - } - } -} -``` - -**ModernUO:** -```csharp -namespace Server.Custom; - -public class VirtueSystem : GenericPersistence -{ - private static VirtueSystem _instance; - private static readonly Dictionary _points = new(); - - public static void Configure() - { - _instance = new VirtueSystem(); - } - - public VirtueSystem() : base("VirtueSystem", 10) { } - - public override void Serialize(IGenericWriter writer) - { - writer.WriteEncodedInt(0); // version - writer.WriteEncodedInt(_points.Count); - foreach (var (mobile, pts) in _points) - { - writer.Write(mobile); - writer.Write(pts); - } - } - - public override void Deserialize(IGenericReader reader) - { - var version = reader.ReadEncodedInt(); - var count = reader.ReadEncodedInt(); - - for (var i = 0; i < count; i++) - { - var mobile = reader.ReadEntity(); - var pts = reader.ReadInt(); - if (mobile != null) - _points[mobile] = pts; - } - } - - public static void AddPoints(Mobile m, int points) - { - _points.TryGetValue(m, out var current); - _points[m] = current + points; - _instance.MarkDirty(); - } -} -``` - -## Edge Cases & Gotchas - -### 1. MarkDirty() Is Required -Without `MarkDirty()`, changes won't be saved. Call it whenever your persisted data changes. - -### 2. GenericPersistence Constructor Name -The first argument to the base constructor is the save file name. It must be unique across all `GenericPersistence` instances. - -### 3. Priority Argument -The second argument is save priority. Lower numbers save first. Use 10 for most systems. - -### 4. Don't Mix EventSink.WorldSave with GenericPersistence -Don't subscribe to `EventSink.WorldSave` for data that `GenericPersistence` manages. The framework handles the lifecycle. - -### 5. ReadEntity() Returns Null for Deleted Entities -Unlike `World.FindMobile()`, `ReadEntity()` will return null if the entity was deleted. Always null-check. - -## See Also - -- `dev-docs/serialization.md` — Serialization system overview -- `07-commands-events.md` — EventSink migration -- `01-foundation-changes.md` — Foundation changes diff --git a/dev-docs/runuo-migration-docs/09-items-mobiles-creatures.md b/dev-docs/runuo-migration-docs/09-items-mobiles-creatures.md deleted file mode 100644 index c5029131c..000000000 --- a/dev-docs/runuo-migration-docs/09-items-mobiles-creatures.md +++ /dev/null @@ -1,566 +0,0 @@ -# Items, Mobiles & Creatures Migration - -## Overview - -Most RunUO migration work involves converting Item, Mobile, and BaseCreature subclasses. This doc combines all prior system changes (serialization, timers, property lists, naming) into complete step-by-step conversion guides for the most common content types. - -## Item Migration Step-by-Step - -### 1. Apply Foundation Changes -- File-scoped namespace -- `using ModernUO.Serialization;` -- Rename `m_` fields to `_camelCase` -- `[Constructable]` → `[Constructible]` -- Replace `Console.WriteLine` with logging -- Replace `DateTime.UtcNow` with `Core.Now` - -### 2. Add Serialization Attributes -```csharp -[SerializationGenerator(N, false)] // N = old version + 1; false if old Deserialize used ReadInt() -public partial class MyItem : Item // Add partial -``` - -### 3. Convert Fields to [SerializableField] -```csharp -// RunUO -private int m_Charges; -[CommandProperty(AccessLevel.GameMaster)] -public int Charges { get { return m_Charges; } set { m_Charges = value; InvalidateProperties(); } } - -// ModernUO -[SerializableField(0)] -[InvalidateProperties] -[SerializedCommandProperty(AccessLevel.GameMaster)] -private int _charges; -// Property auto-generated with InvalidateProperties -``` - -### 4. Delete Boilerplate -- Delete `public MyItem(Serial serial) : base(serial) { }` -- Delete `public override void Serialize(GenericWriter writer) { ... }` -- Delete `public override void Deserialize(GenericReader reader) { ... }` - -### 5. Convert Timer Fields -```csharp -// RunUO -private InternalTimer m_Timer; -// + nested Timer class - -// ModernUO -private TimerExecutionToken _timerToken; -// + direct Timer.StartTimer() calls -// + [AfterDeserialization] for timer restoration -// + OnAfterDelete() for timer cancellation -``` - -### 6. Convert GetProperties -```csharp -// RunUO -public override void GetProperties(ObjectPropertyList list) -{ - base.GetProperties(list); - list.Add(1060741, m_Charges.ToString()); -} - -// ModernUO -public override void GetProperties(IPropertyList list) -{ - base.GetProperties(list); - list.Add(1060741, $"{_charges}"); -} -``` - -### 7. Convert Context Menus (if present) -```csharp -// RunUO -public override void GetContextMenuEntries(Mobile from, List list) -{ - base.GetContextMenuEntries(from, list); - list.Add(new MyEntry(this)); -} - -// ModernUO -public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) -{ - base.GetContextMenuEntries(from, ref list); - list.Add(new MyEntry(this)); -} -``` - -## Complete Before/After: Item - -**RunUO:** -```csharp -using System; -using Server; -using Server.Network; - -namespace Server.Items -{ - public class MagicLantern : Item - { - private int m_Charges; - private Mobile m_Owner; - private InternalTimer m_Timer; - - [CommandProperty(AccessLevel.GameMaster)] - public int Charges - { - get { return m_Charges; } - set { m_Charges = value; InvalidateProperties(); } - } - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Owner - { - get { return m_Owner; } - set { m_Owner = value; } - } - - [Constructable] - public MagicLantern() : base(0xA25) - { - m_Charges = Utility.RandomMinMax(5, 15); - Weight = 2.0; - Light = LightType.Circle300; - Name = "a magic lantern"; - - m_Timer = new InternalTimer(this); - m_Timer.Start(); - } - - public MagicLantern(Serial serial) : base(serial) { } - - public override void OnDelete() - { - if (m_Timer != null) - m_Timer.Stop(); - - base.OnDelete(); - } - - public override void GetProperties(ObjectPropertyList list) - { - base.GetProperties(list); - list.Add(1060741, m_Charges.ToString()); - } - - public override void OnDoubleClick(Mobile from) - { - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); - return; - } - - if (m_Charges <= 0) - { - from.SendMessage("The lantern is depleted."); - return; - } - - m_Charges--; - InvalidateProperties(); - from.SendMessage("The lantern flares brightly!"); - } - - public override void Serialize(GenericWriter writer) - { - base.Serialize(writer); - writer.Write((int)1); // version - - writer.Write(m_Owner); - writer.Write(m_Charges); - } - - public override void Deserialize(GenericReader reader) - { - base.Deserialize(reader); - int version = reader.ReadInt(); - - switch (version) - { - case 1: - m_Owner = reader.ReadMobile(); - goto case 0; - case 0: - m_Charges = reader.ReadInt(); - break; - } - - m_Timer = new InternalTimer(this); - m_Timer.Start(); - } - - private void Glow() - { - if (m_Charges > 0) - Effects.SendLocationParticles(this, 0x376A, 9, 10, 5042); - } - - private class InternalTimer : Timer - { - private MagicLantern m_Lantern; - - public InternalTimer(MagicLantern lantern) : base(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)) - { - m_Lantern = lantern; - Priority = TimerPriority.OneSecond; - } - - protected override void OnTick() - { - m_Lantern.Glow(); - } - } - } -} -``` - -**ModernUO:** -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class MagicLantern : Item -{ - [SerializableField(0)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _charges; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Mobile _owner; - - private TimerExecutionToken _glowTimer; - - [Constructible] - public MagicLantern() : base(0xA25) - { - _charges = Utility.RandomMinMax(5, 15); - Weight = 2.0; - Light = LightType.Circle300; - StartGlow(); - } - - public override string DefaultName => "a magic lantern"; - - private void StartGlow() - { - Timer.StartTimer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), Glow, out _glowTimer); - } - - [AfterDeserialization] - private void AfterDeserialization() => StartGlow(); - - public override void OnAfterDelete() - { - _glowTimer.Cancel(); - base.OnAfterDelete(); - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - list.Add(1060741, $"{_charges}"); - } - - public override void OnDoubleClick(Mobile from) - { - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); - return; - } - - if (_charges <= 0) - { - from.SendMessage("The lantern is depleted."); - return; - } - - Charges--; - from.SendMessage("The lantern flares brightly!"); - } - - private void Glow() - { - if (_charges > 0) - Effects.SendLocationParticles(this, 0x376A, 9, 10, 5042); - } -} -``` - -**What changed:** -- File-scoped namespace -- `partial class` + `[SerializationGenerator(0)]` (omit `encoded` parameter) -- `[Constructable]` → `[Constructible]` -- `m_Charges`/`m_Owner` → `_charges`/`_owner` with `[SerializableField]` -- Manual properties → auto-generated with `[SerializedCommandProperty]` -- `InvalidateProperties()` in setter → `[InvalidateProperties]` attribute -- `Name = "..."` → `DefaultName =>` property override -- Serial constructor deleted -- Serialize/Deserialize deleted -- Nested InternalTimer class → `Timer.StartTimer()` + `TimerExecutionToken` -- Timer in Deserialize → `[AfterDeserialization]` -- `OnDelete()` timer stop → `OnAfterDelete()` + `_token.Cancel()` -- `ObjectPropertyList` → `IPropertyList` -- `GetProperties` uses string interpolation with holes - -## BaseCreature Migration - -BaseCreature subclasses follow the same pattern as items but have additional considerations. - -### Key Differences from Items -1. Constructor calls `base(AIType, FightMode)` instead of `base(itemID)` -2. Stats set with `SetStr()`, `SetDex()`, `SetInt()`, etc. -3. Damage/resistance types set explicitly -4. `GenerateLoot()` override for loot tables -5. Many property overrides (CorpseName, Meat, Hides, etc.) - -### Before/After: Simple Creature - -**RunUO:** -```csharp -namespace Server.Mobiles -{ - [CorpseName("a wolf corpse")] - public class ForestWolf : BaseCreature - { - [Constructable] - public ForestWolf() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) - { - Name = "a forest wolf"; - Body = 225; - BaseSoundID = 0xE5; - - SetStr(80, 120); - SetDex(90, 110); - SetInt(20, 40); - - SetHits(60, 80); - - SetDamage(8, 14); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 25, 35); - - SetSkill(SkillName.MagicResist, 30.0, 50.0); - SetSkill(SkillName.Tactics, 50.0, 70.0); - SetSkill(SkillName.Wrestling, 50.0, 70.0); - - Fame = 600; - Karma = 0; - - VirtualArmor = 28; - - Tamable = true; - ControlSlots = 1; - MinTameSkill = 50.1; - } - - public ForestWolf(Serial serial) : base(serial) { } - - public override int Meat { get { return 1; } } - public override int Hides { get { return 6; } } - public override FoodType FavoriteFood { get { return FoodType.Meat; } } - public override PackInstinct PackInstinct { get { return PackInstinct.Canine; } } - - public override void GenerateLoot() - { - AddLoot(LootPack.Meager); - } - - public override void Serialize(GenericWriter writer) - { - base.Serialize(writer); - writer.Write((int)0); - } - - public override void Deserialize(GenericReader reader) - { - base.Deserialize(reader); - int version = reader.ReadInt(); - } - } -} -``` - -**ModernUO:** -```csharp -using ModernUO.Serialization; - -namespace Server.Mobiles; - -[SerializationGenerator(0)] -public partial class ForestWolf : BaseCreature -{ - [Constructible] - public ForestWolf() : base(AIType.AI_Melee, FightMode.Closest) - { - Body = 225; - BaseSoundID = 0xE5; - - SetStr(80, 120); - SetDex(90, 110); - SetInt(20, 40); - - SetHits(60, 80); - - SetDamage(8, 14); - - SetDamageType(ResistanceType.Physical, 100); - - SetResistance(ResistanceType.Physical, 25, 35); - - SetSkill(SkillName.MagicResist, 30.0, 50.0); - SetSkill(SkillName.Tactics, 50.0, 70.0); - SetSkill(SkillName.Wrestling, 50.0, 70.0); - - Fame = 600; - Karma = 0; - - VirtualArmor = 28; - - Tamable = true; - ControlSlots = 1; - MinTameSkill = 50.1; - } - - public override string CorpseName => "a wolf corpse"; - public override string DefaultName => "a forest wolf"; - public override int Meat => 1; - public override int Hides => 6; - public override FoodType FavoriteFood => FoodType.Meat; - public override PackInstinct PackInstinct => PackInstinct.Canine; - - public override void GenerateLoot() - { - AddLoot(LootPack.Meager); - } -} -``` - -**What changed:** -- `[CorpseName("...")]` attribute → `CorpseName` property override -- `Name = "..."` → `DefaultName` property override -- `BaseCreature(AI, Fight, 10, 1, 0.2, 0.4)` → `BaseCreature(AI, Fight)` (extra params have defaults) -- Expression-bodied property overrides -- Serialization boilerplate removed -- Serial constructor removed - -## Key Creature Constructor Differences - -```csharp -// RunUO — many parameters -public ForestWolf() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4) -// 10 = RangePerception, 1 = RangeFight, 0.2 = ActiveSpeed, 0.4 = PassiveSpeed - -// ModernUO — simplified (defaults built in) -public ForestWolf() : base(AIType.AI_Melee, FightMode.Closest) -``` - -The extra parameters (RangePerception, RangeFight, ActiveSpeed, PassiveSpeed) have sensible defaults. Only specify them if they differ from defaults. - -## Common Creature Attribute Changes - -| RunUO | ModernUO | -|---|---| -| `[CorpseName("a corpse")]` attribute | `public override string CorpseName => "a corpse";` | -| `Name = "a creature"` in constructor | `public override string DefaultName => "a creature";` | -| `get { return value; }` | `=> value;` expression-bodied | - -## Item Name Changes - -```csharp -// RunUO -Name = "a magic gem"; // Set in constructor - -// ModernUO — prefer property overrides -public override string DefaultName => "a magic gem"; -// OR for cliloc: -public override int LabelNumber => 1234567; -``` - -## Equipment/Weapon/Armor Migration - -Weapons and armor follow the same item pattern but inherit from specialized base classes: - -```csharp -// ModernUO weapon example -[SerializationGenerator(0)] -public partial class MySpecialSword : BaseSword -{ - [Constructible] - public MySpecialSword() : base(0x13FF) // Katana graphic - { - Weight = 6.0; - Layer = Layer.TwoHanded; - } - - public override string DefaultName => "a special sword"; - public override int AosStrengthReq => 25; - public override int AosMinDamage => 11; - public override int AosMaxDamage => 13; - public override int AosSpeed => 44; - public override float MlSpeed => 2.50f; -} -``` - -## Common Base Classes - -| RunUO | ModernUO | Notes | -|---|---|---| -| `BaseWeapon` | `BaseWeapon` | Same, add `partial` | -| `BaseSword` / `BaseMace` / etc. | Same | Same, add `partial` | -| `BaseArmor` | `BaseArmor` | Same, add `partial` | -| `BaseClothing` | `BaseClothing` | Same, add `partial` | -| `BaseJewel` | `BaseJewel` | Same, add `partial` | -| `BaseContainer` | `BaseContainer` | Same, add `partial` | -| `Food` | `Food` | Same, add `partial` | -| `BasePotion` | `BasePotion` | Same, add `partial` | - -## Edge Cases & Gotchas - -### 1. [TypeAlias] for Save Compatibility -If a class changed namespace or name, use `[TypeAlias]`: -```csharp -[TypeAlias("Server.Items.OldName")] -[SerializationGenerator(0)] -public partial class NewName : Item { } -``` - -### 2. OnDoubleClick Validation -ModernUO patterns prefer: -```csharp -if (!IsChildOf(from.Backpack)) -{ - from.SendLocalizedMessage(1042001); - return; -} -``` - -### 3. CorpseName as Property Override -RunUO uses `[CorpseName]` attribute. ModernUO uses a property override instead. - -### 4. SetMana(0) for Non-Casters -Always call `SetMana(0)` for creatures that shouldn't have mana. - -### 5. Decrement via Generated Property -Use the generated property name (PascalCase) when decrementing to trigger dirty tracking: -```csharp -Charges--; // Uses generated property — triggers MarkDirty + InvalidateProperties -// NOT: _charges--; // Bypasses tracking -``` - -## See Also - -- `dev-docs/content-patterns.md` — ModernUO content creation patterns -- `dev-docs/serialization.md` — Serialization system -- `02-serialization.md` — Serialization migration details -- `03-timers.md` — Timer migration -- `06-property-lists.md` — Property list migration diff --git a/dev-docs/runuo-migration-docs/10-systems-engines.md b/dev-docs/runuo-migration-docs/10-systems-engines.md deleted file mode 100644 index 44f811dca..000000000 --- a/dev-docs/runuo-migration-docs/10-systems-engines.md +++ /dev/null @@ -1,354 +0,0 @@ -# Systems & Engines Migration - -## Overview - -RunUO community scripts often include multi-file systems: custom crafting extensions, spawner systems, economy engines, housing addons, quest systems. Migrating these requires applying all prior changes (serialization, timers, events, persistence) systematically across multiple interdependent files. - -## Approach - -### 1. Map the System -Before changing code, understand the system's structure: -- List all files in the system -- Identify entry points (Configure/Initialize methods) -- Map class dependencies (what references what) -- Identify serialized types (anything with Serialize/Deserialize) -- Identify persistence (EventSink.WorldSave handlers) -- Identify timers (Timer subclasses, DelayCall) -- Identify gumps (Gump subclasses) -- Identify packets (Packet subclasses) - -### 2. Conversion Order -Convert files in dependency order: -1. **Data types / enums** — No serialization changes needed, just naming/namespace -2. **Persistence classes** — Convert EventSink.WorldSave to GenericPersistence -3. **Core entities (Items/Mobiles)** — Serialization, timers, properties -4. **Gumps** — Convert to DynamicGump/StaticGump -5. **Commands** — Usually minimal changes -6. **Packets** — Convert to SpanWriter if custom packets exist -7. **Entry point / registration** — Update Configure/Initialize - -### 3. File Organization -RunUO scripts are in `Scripts/` with arbitrary structure. ModernUO expects files in `Projects/UOContent/`: - -| RunUO Location | ModernUO Location | -|---|---| -| `Scripts/Custom/MySystem/` | `Projects/UOContent/Engines/MySystem/` or `Projects/UOContent/Systems/MySystem/` | -| `Scripts/Items/MyItem.cs` | `Projects/UOContent/Items/{Category}/MyItem.cs` | -| `Scripts/Mobiles/MyMobile.cs` | `Projects/UOContent/Mobiles/{Category}/MyMobile.cs` | -| `Scripts/Gumps/MyGump.cs` | `Projects/UOContent/Gumps/MyGump.cs` | -| `Scripts/Commands/MyCommand.cs` | Keep with parent system or `Projects/UOContent/Commands/` | - -## Pattern: Multi-File System Migration - -### Example: A Custom Bounty System - -**RunUO structure:** -``` -Scripts/Custom/BountySystem/ -├── BountySystem.cs (EventSink.WorldSave/Load, core logic) -├── BountyBoard.cs (Item - the board in town) -├── BountyBoardGump.cs (Gump - UI) -├── BountyEntry.cs (Data class - not serialized as entity) -├── BountyCommands.cs (Commands) -└── BountyTimer.cs (Timer for bounty expiry) -``` - -**Migration steps:** - -#### File 1: BountyEntry.cs (Data class — simplest) -```csharp -// RunUO -namespace Server.Custom.BountySystem -{ - public class BountyEntry - { - private Mobile m_Target; - private Mobile m_Poster; - private int m_Amount; - private DateTime m_Expiry; - - public Mobile Target { get { return m_Target; } } - public Mobile Poster { get { return m_Poster; } } - public int Amount { get { return m_Amount; } set { m_Amount = value; } } - public DateTime Expiry { get { return m_Expiry; } } - - public BountyEntry(Mobile target, Mobile poster, int amount, DateTime expiry) - { - m_Target = target; - m_Poster = poster; - m_Amount = amount; - m_Expiry = expiry; - } - } -} - -// ModernUO -namespace Server.Custom.BountySystem; - -public class BountyEntry -{ - public Mobile Target { get; } - public Mobile Poster { get; } - public int Amount { get; set; } - public DateTime Expiry { get; } - - public BountyEntry(Mobile target, Mobile poster, int amount, DateTime expiry) - { - Target = target; - Poster = poster; - Amount = amount; - Expiry = expiry; - } -} -``` - -#### File 2: BountySystem.cs (Persistence — convert to GenericPersistence) -```csharp -// ModernUO -namespace Server.Custom.BountySystem; - -public class BountySystem : GenericPersistence -{ - private static BountySystem _instance; - private static readonly List _bounties = new(); - - public static IReadOnlyList Bounties => _bounties; - - public static void Configure() - { - _instance = new BountySystem(); - } - - public BountySystem() : base("BountySystem", 10) { } - - public override void Serialize(IGenericWriter writer) - { - writer.WriteEncodedInt(0); - writer.WriteEncodedInt(_bounties.Count); - foreach (var b in _bounties) - { - writer.Write(b.Target); - writer.Write(b.Poster); - writer.Write(b.Amount); - writer.Write(b.Expiry); - } - } - - public override void Deserialize(IGenericReader reader) - { - var version = reader.ReadEncodedInt(); - var count = reader.ReadEncodedInt(); - for (var i = 0; i < count; i++) - { - var target = reader.ReadEntity(); - var poster = reader.ReadEntity(); - var amount = reader.ReadInt(); - var expiry = reader.ReadDateTime(); - if (target != null && poster != null) - _bounties.Add(new BountyEntry(target, poster, amount, expiry)); - } - } - - public static void AddBounty(BountyEntry entry) - { - _bounties.Add(entry); - _instance.MarkDirty(); - } - - public static void RemoveBounty(BountyEntry entry) - { - _bounties.Remove(entry); - _instance.MarkDirty(); - } -} -``` - -#### File 3: BountyBoard.cs (Item — serialization + timer) -```csharp -// ModernUO -using ModernUO.Serialization; -using Server.Gumps; - -namespace Server.Custom.BountySystem; - -[SerializationGenerator(0)] -public partial class BountyBoard : Item -{ - [Constructible] - public BountyBoard() : base(0x1E5E) - { - Movable = false; - } - - public override string DefaultName => "a bounty board"; - - public override void OnDoubleClick(Mobile from) - { - if (!from.InRange(GetWorldLocation(), 3)) - { - from.SendLocalizedMessage(500446); - return; - } - - BountyBoardGump.DisplayTo(from); - } -} -``` - -#### File 4: BountyBoardGump.cs (Gump — convert to DynamicGump) -```csharp -// ModernUO -using Server.Gumps; - -namespace Server.Custom.BountySystem; - -public class BountyBoardGump : DynamicGump -{ - private readonly Mobile _from; - - public override bool Singleton => true; - - private BountyBoardGump(Mobile from) : base(50, 50) - { - _from = from; - } - - protected override void BuildLayout(ref DynamicGumpBuilder builder) - { - var bounties = BountySystem.Bounties; - var height = 80 + bounties.Count * 30; - - builder.AddPage(); - builder.AddBackground(0, 0, 400, height, 5054); - builder.AddAlphaRegion(10, 10, 380, height - 20); - builder.AddLabel(20, 15, 0x480, "Active Bounties"); - - for (var i = 0; i < bounties.Count; i++) - { - var b = bounties[i]; - var y = 45 + i * 30; - builder.AddLabel(20, y, 0x480, b.Target.Name ?? "Unknown"); - builder.AddLabel(200, y, 0x480, $"{b.Amount} gold"); - builder.AddButton(350, y, 4005, 4007, i + 1); - } - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - if (info.ButtonID > 0) - { - var index = info.ButtonID - 1; - var bounties = BountySystem.Bounties; - if (index < bounties.Count) - _from.SendMessage($"Bounty on {bounties[index].Target.Name}: {bounties[index].Amount} gold"); - } - } - - public static void DisplayTo(Mobile from) - { - if (from?.NetState == null) - return; - from.SendGump(new BountyBoardGump(from)); - } -} -``` - -#### File 5: BountyCommands.cs (Commands — minimal changes) -```csharp -// ModernUO -using Server.Commands; - -namespace Server.Custom.BountySystem; - -public static class BountyCommands -{ - public static void Configure() - { - CommandSystem.Register("Bounty", AccessLevel.Player, Bounty_OnCommand); - } - - [Usage("Bounty ")] - [Description("Place a bounty on a targeted player")] - public static void Bounty_OnCommand(CommandEventArgs e) - { - if (e.Length < 1) - { - e.Mobile.SendMessage("Usage: [Bounty "); - return; - } - - var amount = e.GetInt32(0); - e.Mobile.SendMessage("Target the player to place a bounty on."); - e.Mobile.Target = new BountyTarget(amount); - } -} -``` - -## Cross-Reference Handling - -When files reference each other: -1. Convert shared data types first -2. Convert the persistence/core system next -3. Convert consumers (gumps, commands) last -4. Keep all files in the same namespace - -## Common Multi-File Patterns - -### Crafting Extension -| RunUO File | ModernUO Conversion | -|---|---| -| CraftItem subclass | `[SerializationGenerator]`, `partial` | -| CraftSystem registration | `Configure()` method | -| Resource definition | Data class, auto-properties | -| Custom tools (Item) | Standard item migration | - -### Custom Spawner -| RunUO File | ModernUO Conversion | -|---|---| -| Spawner Item | `[SerializationGenerator]`, `[AfterDeserialization]` for timers | -| Spawn timer | `TimerExecutionToken`, fire-and-forget | -| Config file | `JsonConfig` or `ServerConfiguration` | -| Admin gump | `DynamicGump` conversion | - -### Economy/Banking System -| RunUO File | ModernUO Conversion | -|---|---| -| Data persistence | `GenericPersistence` | -| Account data | Serialized fields or `GenericPersistence` | -| Transaction log | `GenericPersistence` | -| Player-facing gump | `DynamicGump` or `StaticGump` | - -## Configuration Migration - -RunUO systems often use XML or custom config files: -```csharp -// RunUO -XmlDocument doc = new XmlDocument(); -doc.Load("Data/MyConfig.xml"); - -// ModernUO — use ServerConfiguration for simple settings -var enabled = ServerConfiguration.GetOrUpdateSetting("mySystem.enabled", true); -var maxItems = ServerConfiguration.GetOrUpdateSetting("mySystem.maxItems", 100); - -// ModernUO — use JsonConfig for complex settings -var config = JsonConfig.Deserialize(configPath); -``` - -## Testing the Migration - -After converting all files: -1. Compile the project: `dotnet build` -2. Fix any compilation errors -3. Start the server and check for runtime errors -4. Test each feature: - - Items can be `[add`ed - - Gumps display correctly - - Commands work - - Data persists across saves/restarts - - Timers fire correctly - -## See Also - -- `dev-docs/content-patterns.md` — File organization patterns -- `dev-docs/configuration.md` — Configuration system reference -- All prior migration docs (01 through 09) diff --git a/dev-docs/runuo-migration-docs/11-api-reference.md b/dev-docs/runuo-migration-docs/11-api-reference.md deleted file mode 100644 index 215dd3b06..000000000 --- a/dev-docs/runuo-migration-docs/11-api-reference.md +++ /dev/null @@ -1,200 +0,0 @@ -# API Reference — RunUO to ModernUO Mapping - -## Quick Lookup - -Alphabetical by RunUO API name. Use Ctrl+F / Cmd+F to search. - -## Attributes - -| RunUO | ModernUO | Notes | -|---|---|---| -| `[Constructable]` | `[Constructible]` | Spelling change | -| `[CommandProperty(AccessLevel)]` | `[SerializedCommandProperty(AccessLevel)]` | On `[SerializableField]` fields | -| `[CommandProperty(AccessLevel)]` | `[CommandProperty(AccessLevel)]` | On `[SerializableProperty]` properties (unchanged) | -| `[CorpseName("name")]` | `public override string CorpseName => "name";` | Attribute → property override | -| `[Constructable]` | `[Constructible]` | Must use `using ModernUO.Serialization;` | -| `[Usage("...")]` | `[Usage("...")]` | Unchanged | -| `[Description("...")]` | `[Description("...")]` | Unchanged | -| N/A | `[Aliases("a1", "a2")]` | New in ModernUO | -| N/A | `[SerializationGenerator(ver, enc)]` | New — replaces manual Serialize/Deserialize | -| N/A | `[SerializableField(idx)]` | New — auto-generates property + serialization | -| N/A | `[SerializableProperty(idx)]` | New — custom property serialization | -| N/A | `[InvalidateProperties]` | New — auto-calls InvalidateProperties on set | -| N/A | `[AfterDeserialization]` | New — post-deserialize hook | -| N/A | `[DeltaDateTime]` | New — relative DateTime storage | -| N/A | `[EncodedInt]` | New — variable-length int encoding | -| N/A | `[InternString]` | New — string interning on load | -| N/A | `[Tidy]` | New — auto-clean null entries in collections | -| N/A | `[CanBeNull]` | New — nullable reference field | -| N/A | `[TypeAlias("old.name")]` | New — backward-compatible type mapping | - -## Classes - -| RunUO | ModernUO | Notes | -|---|---|---| -| `BinaryFileWriter` | `IGenericWriter` (via GenericPersistence) | No manual file writing | -| `BinaryFileReader` | `IGenericReader` (via GenericPersistence) | No manual file reading | -| `GenericWriter` | `IGenericWriter` | Interface now | -| `GenericReader` | `IGenericReader` | Interface now | -| `Gump` | `DynamicGump` or `StaticGump` | See 04-gumps.md | -| `ObjectPropertyList` (parameter) | `IPropertyList` | Interface for GetProperties | -| `Packet` (base class) | Removed — use static methods | See 05-packets.md | -| `PacketWriter` | `SpanWriter` | Ref struct | -| `PacketReader` | `SpanReader` | Ref struct | -| `Timer` (subclass pattern) | `Timer.StartTimer()` + `TimerExecutionToken` | See 03-timers.md | -| `TextRelay` | Removed — use `info.GetTextEntry(id)` | Returns string directly | -| `RelayInfo` | `RelayInfo` (passed with `in`) | `in RelayInfo` in OnResponse | - -## Constructors - -| RunUO | ModernUO | Notes | -|---|---|---| -| `MyItem(Serial serial) : base(serial)` | DELETE | Auto-generated | -| `BaseCreature(AI, Fight, 10, 1, 0.2, 0.4)` | `BaseCreature(AI, Fight)` | Extra params have defaults | - -## Events (EventSink) - -| RunUO | ModernUO | Signature | -|---|---|---| -| `EventSink.Login` | `EventSink.Connected` | `LoginEventArgs` → `Action` | -| `EventSink.Logout` | `EventSink.Disconnected` | `LogoutEventArgs` → `Action` | -| `EventSink.WorldSave` | `EventSink.WorldSave` | `WorldSaveEventArgs` → `Action` | -| `EventSink.WorldLoad` | `EventSink.WorldLoad` | `Action` | -| `EventSink.ServerStarted` | `EventSink.ServerStarted` | `Action` | -| `EventSink.Crashed` | `EventSink.ServerCrashed` | Renamed | -| `EventSink.Speech` | `EventSink.Speech` | `Action` | -| `EventSink.Movement` | `EventSink.Movement` | `Action` | -| `EventSink.AggressiveAction` | `EventSink.AggressiveAction` | `Action` | -| `EventSink.AccountLogin` | `EventSink.AccountLogin` | `Action` | -| `EventSink.SocketConnect` | `EventSink.SocketConnect` | `Action` | -| `EventSink.Shutdown` | `EventSink.Shutdown` | `Action` | -| `EventSink.BeforeWorldSave` | Removed | Use `WorldSave` | -| `EventSink.PlayerDeath` | `PlayerMobile.PlayerDeathEvent` | CodeGeneratedEvent | -| `EventSink.CreatureDeath` | `BaseCreature.CreatureDeathEvent` | CodeGeneratedEvent | - -## Event Delegates - -| RunUO | ModernUO | -|---|---| -| `new WorldSaveEventHandler(Save)` | `Save` (direct method reference) | -| `new WorldLoadEventHandler(Load)` | `Load` | -| `new LoginEventHandler(OnLogin)` | `OnConnected` | -| `new LogoutEventHandler(OnLogout)` | `OnDisconnected` | -| `new SpeechEventHandler(OnSpeech)` | `OnSpeech` | -| `new TimerCallback(Method)` | `Method` | -| `new TimerStateCallback(Method)` | `Method` (typed overloads) | -| `new OnPacketReceive(Handler)` | `&Handler` (function pointer) | - -## Gump Methods - -| RunUO | ModernUO | Notes | -|---|---|---| -| `AddPage(0)` | `builder.AddPage()` | 0 is default | -| `AddBackground(...)` | `builder.AddBackground(...)` | On builder | -| `AddAlphaRegion(...)` | `builder.AddAlphaRegion(...)` | On builder | -| `AddLabel(x, y, hue, text)` | `builder.AddLabel(x, y, hue, text)` | On builder | -| `AddHtml(x, y, w, h, text, bg, scroll)` | `builder.AddHtml(x, y, w, h, text, background: bg, scrollbar: scroll)` | Named params | -| `AddHtmlLocalized(...)` | `builder.AddHtmlLocalized(...)` | On builder | -| `AddButton(x, y, n, p, id, Reply, 0)` | `builder.AddButton(x, y, n, p, id)` | Reply is default | -| `AddTextEntry(...)` | `builder.AddTextEntry(...)` | On builder | -| `AddCheck(x, y, off, on, state, id)` | `builder.AddCheckbox(x, y, off, on, state, id)` | Renamed | -| `Closable = false` | `builder.SetNoClose()` | Property → method | -| `Dragable = false` | `builder.SetNoMove()` | Renamed | -| `Resizable = false` | `builder.SetNoResize()` | Property → method | -| `Disposable = false` | `builder.SetNoDispose()` | Property → method | -| `from.SendGump(new G(...))` | `G.DisplayTo(from, ...)` | Static entry point | -| `from.CloseGump(typeof(G))` | `from.CloseGump()` | Generic | -| `from.HasGump(typeof(G))` | `from.HasGump()` | Generic | - -## Gump Response - -| RunUO | ModernUO | -|---|---| -| `OnResponse(NetState, RelayInfo)` | `OnResponse(NetState, in RelayInfo)` | -| `info.TextEntries[i].Text` | `info.GetTextEntry(id)` | -| `info.IsSwitched(id)` | `info.IsSwitched(id)` (same) | -| `info.ButtonID` | `info.ButtonID` (same) | - -## Methods - -| RunUO | ModernUO | Notes | -|---|---|---| -| `DateTime.UtcNow` | `Core.Now` | Server time source | -| `Console.WriteLine(...)` | `logger.Information(...)` | Structured logging | -| `World.FindMobile(serial)` | `reader.ReadEntity()` | In deserialization | -| `World.FindItem(serial)` | `reader.ReadEntity()` | In deserialization | -| `reader.ReadMobile()` | `reader.ReadEntity()` | Generic method | -| `reader.ReadItem()` | `reader.ReadEntity()` | Generic method | -| `reader.ReadInt()` | `reader.ReadInt()` | Same | -| `writer.Write((int)value)` | `writer.Write(value)` | No cast needed | -| `writer.WriteEncodedInt(value)` | `writer.WriteEncodedInt(value)` | Same | -| `InvalidateProperties()` | `InvalidateProperties()` | Same, or use `[InvalidateProperties]` | -| `this.MarkDirty()` | `this.MarkDirty()` | NEW — required in custom setters | - -## Networking - -| RunUO | ModernUO | Notes | -|---|---|---| -| `ns.Send(new MyPacket(...))` | `ns.SendMyPacket(...)` | Extension method | -| `Packet.Compile()` | Not needed | Stack-allocated | -| `Packet.SetStatic()` | Not needed | Reuse via buffer[0] != 0 guard | -| `PacketHandlers.Register(...)` | `IncomingPackets.Register(...)` | Function pointers | -| `m_Stream.Write(value)` | `writer.Write(value)` | SpanWriter | -| `pvSrc.ReadInt32()` | `reader.ReadInt32()` | SpanReader | -| `pvSrc.ReadString()` | `reader.ReadAsciiSafe()` | Explicit encoding | -| `pvSrc.ReadUnicodeStringSafe()` | `reader.ReadBigUniSafe()` | Explicit name | - -## Persistence - -| RunUO | ModernUO | Notes | -|---|---|---| -| `EventSink.WorldSave += Save` | `class X : GenericPersistence` | Subclass | -| `EventSink.WorldLoad += Load` | `override Deserialize(IGenericReader)` | Method | -| `new BinaryFileWriter(path, true)` | Handled by framework | Automatic | -| `BinaryFileReader` / `FileStream` | Handled by framework | Automatic | -| `Directory.CreateDirectory(...)` | Handled by framework | Automatic | - -## Properties - -| RunUO | ModernUO | Notes | -|---|---|---| -| `Name = "text"` (in constructor) | `public override string DefaultName => "text";` | Property override | -| `Name` (custom text) | `DefaultName` (property override) | For fixed names | -| `int Prop { get { return m_X; } }` | `int Prop => _x;` | Expression-bodied | -| `int Prop { get { return m_X; } set { m_X = value; } }` | Auto-generated by `[SerializableField]` | Delete manual property | - -## Timer - -| RunUO | ModernUO | Notes | -|---|---|---| -| `new InternalTimer().Start()` | `Timer.StartTimer(..., out token)` | Fire-and-forget | -| `Timer.DelayCall(delay, callback)` | `Timer.StartTimer(delay, callback)` | Similar | -| `Timer.DelayCall(delay, stateCallback, state)` | `Timer.DelayCall(delay, callback, state)` | Typed state | -| `timer.Stop()` | `token.Cancel()` | Struct, safe to call multiple times | -| `timer.Running` | `token.Running` | Same concept | -| `TimerPriority.XXX` | Removed | Timer wheel auto-schedules | -| Timer in `Deserialize()` | `[AfterDeserialization]` | Post-load hook | -| `timer != null` check | `token.Running` check | Value type, always valid | - -## Threading / Performance - -| RunUO | ModernUO | Notes | -|---|---|---| -| `lock (_obj)` | Remove | Single-threaded | -| `volatile` | Remove keyword | Single-threaded | -| `ConcurrentDictionary` | `Dictionary` | Single-threaded | -| `Task.Run(...)` | Use `Timer.StartTimer()` | Game loop only | -| `new Thread(...)` | Forbidden | Game loop only | -| `ArrayPool.Shared` | `STArrayPool.Shared` | No-lock pool | -| `new List()` (hot path) | `PooledRefList.Create()` | Zero-alloc | -| `World.Mobiles.Values` iteration | `map.GetMobilesInRange()` | Spatial query | -| `World.Items.Values` iteration | `map.GetItemsInRange()` | Spatial query | - -## Usings - -| RunUO | ModernUO | Notes | -|---|---|---| -| (implicit) | `using ModernUO.Serialization;` | For serialization attributes | -| (implicit) | `using Server.Logging;` | For logging | -| (implicit) | `using Server.Gumps;` | For gump extension methods | -| (implicit) | `using Server.Collections;` | For PooledRefList | diff --git a/dev-docs/serialization.md b/dev-docs/serialization.md deleted file mode 100644 index 059fbff69..000000000 --- a/dev-docs/serialization.md +++ /dev/null @@ -1,707 +0,0 @@ -# ModernUO Serialization System - -ModernUO uses a source generator-based serialization system that automatically generates `Serialize()` and `Deserialize()` methods from attribute-decorated fields and properties. - -## Overview - -The serialization system is provided by two NuGet packages: -- `ModernUO.Serialization.Annotations` - Defines attributes -- `ModernUO.Serialization.Generator` - C# source generator that produces serialization code - -Source: https://github.com/modernuo/SerializationGenerator - -## Quick Start - -### Minimal Serializable Item -```csharp -using ModernUO.Serialization; - -namespace Server.Items; - -[SerializationGenerator(0)] -public partial class SimpleItem : Item -{ - [Constructible] - public SimpleItem() : base(0x1234) - { - Weight = 1.0; - } - - public override string DefaultName => "a simple item"; -} -``` - -Key requirements: -1. `using ModernUO.Serialization;` for the attributes -2. `[SerializationGenerator(0)]` on the class -3. `partial` class declaration -4. `[Constructible]` on the parameterless constructor - -### Item with Serialized Fields -```csharp -[SerializationGenerator(0)] -public partial class ChargedGem : Item -{ - [SerializableField(0)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _charges; - - [SerializableField(1)] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private Mobile _owner; - - private TimerExecutionToken _glowTimer; // NOT serialized - - [Constructible] - public ChargedGem() : base(0x1EA7) - { - _charges = Utility.RandomMinMax(5, 15); - Light = LightType.Circle150; - Timer.StartTimer(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2), Glow, out _glowTimer); - } - - [AfterDeserialization] - private void AfterDeserialization() - { - Timer.StartTimer(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2), Glow, out _glowTimer); - } - - public override void OnAfterDelete() - { - _glowTimer.Cancel(); - base.OnAfterDelete(); - } - - private void Glow() - { - if (_charges > 0) - Effects.SendLocationParticles(this, 0x376A, 9, 10, 5042); - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - list.Add(1060741, $"{_charges}"); // "charges: ~1_val~" - } -} -``` - ---- - -## Attribute Reference - -### [SerializationGenerator(version, encoded)] - -**Target**: Class declaration -**Required**: Yes, for any serializable type - -| Parameter | Type | Default | Description | -|---|---|---|---| -| `version` | `int` | Required | Current serialization version number | -| `encoded` | `bool` | `true` | How the version was written in old saves. Only needed when migrating from pre-codegen Serialize/Deserialize — pass `false` if old code used `reader.ReadInt()` | - -The version number determines which `Deserialize` overload is called. When you add, remove, or reorder fields, increment the version. - -For **new classes**, omit the `encoded` parameter: -```csharp -[SerializationGenerator(0)] // New class — omit encoded -public partial class MyItem : Item { } -``` - -When **migrating from RunUO/pre-codegen** classes that used `reader.ReadInt()` for version, pass `false` and bump the version: -```csharp -[SerializationGenerator(3, false)] // Old version was 2 → bump to 3; false because old saves used ReadInt() -public partial class MyItem : Item { } -``` - -See `dev-docs/runuo-migration-docs/02-serialization.md` for complete migration guidance. - -### [SerializableField(index, getter, setter, isVirtual, fieldChanged, allowFieldChange)] - -**Target**: Private field (`_camelCase`) -**Generates**: Public `PascalCase` property with get/set - -| Parameter | Type | Default | Description | -|---|---|---|---| -| `index` | `int` | Required | Serialization order (0-based) | -| `getter` | `string` | `"public"` | Getter accessibility | -| `setter` | `string` | `"public"` | `"private"` or `"internal"` to restrict setter | -| `isVirtual` | `bool` | `false` | Generate a `virtual` property | -| `fieldChanged` | `string` | `null` | `nameof` of a `void Method(T oldValue, T newValue)` invoked by the generated setter after assignment | -| `allowFieldChange` | `string` | `null` | `nameof` of a `bool Method(ref T value)` invoked before assignment; coerce the value through the `ref` parameter, or return `false` to reject the change | - -```csharp -[SerializableField(0)] // Public property -private int _charges; - -[SerializableField(1, setter: "private")] // Private setter -private string _name; - -[SerializableField(2, setter: "internal")] // Internal setter -private DateTime _created; -``` - -The generated property for `_charges` would be: -```csharp -public int Charges -{ - get => _charges; - set - { - if (value != _charges) - { - _charges = value; - this.MarkDirty(); - } - } -} -``` - -**Setter hooks** replace most hand-written `[SerializableProperty]` setters. The generated -pipeline is: equality check → `allowFieldChange` (coerce/veto) → assignment → `MarkDirty` → -`InvalidateProperties` (if declared) → `fieldChanged`. The gate runs before assignment, so -the field itself still holds the old value inside it. - -```csharp -[SerializableField(0, allowFieldChange: nameof(AllowChargesChange))] -[SerializedCommandProperty(AccessLevel.GameMaster)] -[InvalidateProperties] -private int _charges; - -private bool AllowChargesChange(ref int value) -{ - value = Math.Clamp(value, 0, MaxCharges); // coerce, or return false to veto - return true; -} - -[SerializableField(1, fieldChanged: nameof(OnOwnerChanged))] -private Mobile _owner; - -// oldValue makes unsubscribe/resubscribe patterns trivial -private void OnOwnerChanged(Mobile oldValue, Mobile newValue) -{ - oldValue?.Followers.Remove(this); - newValue?.Followers.Add(this); -} -``` - -Both hooks require a generated setter — declaring one on a `readonly` field or with -`setter: null` is a compile-time error (SG3018), and a named method that is missing or has -the wrong signature is too (SG3015). - -### [SerializableProperty(index, useField)] - -**Target**: Property with custom get/set logic -**Use when**: You need a **custom getter** (fallback defaults, lazy or self-healing reads) -or setter semantics the field hooks cannot express (work that must run on *equal* -assignment, pre-assignment state capture). For setters that only coerce, veto, or run -post-change side effects, prefer `[SerializableField]` with `allowFieldChange`/`fieldChanged` -instead — the generated setter handles equality, `MarkDirty`, and ordering for you. - -| Parameter | Type | Default | Description | -|---|---|---|---| -| `index` | `int` | Required | Serialization order | -| `useField` | `string` | `null` | Explicit backing field name | - -```csharp -[SerializableProperty(0)] -[CommandProperty(AccessLevel.GameMaster)] -public int MaxItems -{ - get => _maxItems == -1 ? DefaultMaxItems : _maxItems; // custom getter: the reason this is a property - set - { - _maxItems = value; - InvalidateProperties(); - this.MarkDirty(); // REQUIRED in custom setters - } -} -``` - -Note: the `fieldChanged`/`allowFieldChange` hooks are `[SerializableField]` arguments and -cannot be declared on a `[SerializableProperty]` — its setter is your own code, so call your -methods from the setter directly. - -### [InvalidateProperties] - -**Target**: `[SerializableField]`-decorated field -**Effect**: Calls `InvalidateProperties()` when the property setter is invoked, refreshing the client tooltip. - -```csharp -[SerializableField(0)] -[InvalidateProperties] -private int _charges; -// Generated setter calls InvalidateProperties() automatically -``` - -### [SerializedCommandProperty(accessLevel)] - -**Target**: `[SerializableField]`-decorated field -**Effect**: Exposes the generated property to the `[Props` gump for in-game editing. - -```csharp -[SerializableField(0)] -[SerializedCommandProperty(AccessLevel.GameMaster)] -private int _charges; -// GMs can view/edit via [Props command -``` - -Overloads: -- `[SerializedCommandProperty(AccessLevel.GameMaster)]` - Same read/write level -- `[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]` - Different read/write levels - -### [EncodedInt] - -**Target**: `int` field or property -**Effect**: Uses variable-length encoding. 1 byte for 0-127, 2 bytes for 128-16383, etc. - -Best for fields that are usually small values (counts, IDs, indexes). - -### [AnchoredDateTime] - -**Target**: `DateTime` field -**Effect**: Stores the absolute UTC instant; at load it is shifted forward by the downtime -between the save and the load (using the save-start anchor in the save's index file), so -server downtime does not consume the remaining time. `DateTime.MinValue`/`MaxValue` -sentinels pass through unshifted. - -Prefer this for deadlines and "elapsed while running" values. Unlike `[DeltaDateTime]`, the -stored bytes do not change on every save when the value is unchanged, keeping idle saves -byte-stable. - -```csharp -[AnchoredDateTime] -[SerializableField(0)] -private DateTime _expireTime; -``` - -### [DeltaDateTime] - -**Target**: `DateTime` field -**Effect**: Stores as offset from current time rather than absolute timestamp. - -Legacy encoding for surviving restarts: it rewrites the bytes on every save even when the -value has not changed. Prefer `[AnchoredDateTime]` for new fields; converting an existing -field between the two changes the wire format and requires a version bump. - -```csharp -[DeltaDateTime] -[SerializableField(0)] -private DateTime _expireTime; -``` - -### [InternString] - -**Target**: `string` field -**Effect**: Calls `string.Intern()` on deserialization to deduplicate identical strings in memory. - -Best for frequently repeated strings (usernames, template names). - -### [Tidy] - -**Target**: Collection field (`List`, `Dictionary`, etc.) -**Effect**: Removes null and deleted entries from the collection after deserialization. - -```csharp -[Tidy] -[SerializableField(0)] -private List _followers; -// After loading, any deleted/null mobiles are removed -``` - -### [CanBeNull] - -**Target**: Any reference-type field -**Effect**: Allows the field to be null during deserialization without error. - -```csharp -[CanBeNull] -[SerializableField(0)] -private Mobile _target; -``` - -### [AfterDeserialization(synchronous)] - -**Target**: Parameterless private method -**Effect**: Called after fields are deserialized. The `synchronous` parameter controls execution timing. - -| Parameter | Type | Default | Description | -|---|---|---|---| -| `synchronous` | `bool` | `true` | `true` = runs immediately after this entity's deserialization. `false` = runs after ALL entities in the world are deserialized. | - -**When to use `true` (default):** -- Restarting timers for this entity -- Setting up derived values from this entity's own fields -- Cleaning up this entity's own collections - -**When to use `false` (deferred):** -- Logic that adds or deletes entities (e.g., `Delete()`) -- Logic that depends on other entities being fully loaded (cross-entity references) -- Logic that affects game state - -```csharp -// Sync (default) — only touches own fields -[AfterDeserialization] -private void AfterDeserialization() -{ - Timer.StartTimer(TimeSpan.FromSeconds(5), CheckExpiry, out _timerToken); -} - -// Deferred — calls Delete() which affects game state -[AfterDeserialization(false)] -private void AfterDeserialization() -{ - if (_expireTimer == null) - { - Delete(); - } -} -``` - -### [DeserializeTimer(nameof(Method), wallClock)] - -**Target**: `Timer`-typed `[SerializableField]` or `[SerializableProperty]` member -**Effect**: Declares how the timer is stored and restored. Required on every serializable -timer (SG3008 otherwise). - -By default the timer's next tick is stored as **anchored time**: server downtime does not -consume the remaining delay, and idle saves are byte-stable. Pass `wallClock: true` to store -an absolute deadline instead (the delay is then negative when the deadline passed during -downtime). - -The named method — `void Method(TimeSpan delay)` — is invoked **only when a timer was -actually running at save**, with the remaining delay. There is no sentinel value to check. - -```csharp -[SerializableField(0, setter: "private")] -[DeserializeTimer(nameof(DeserializeDecayTimer))] -private Timer _decayTimer; - -private void DeserializeDecayTimer(TimeSpan delay) => _decayTimer = Timer.DelayCall(delay, Delete); -``` - -Switching an existing timer between drifting and `wallClock` changes the wire format — bump -the class version and add a `MigrateFrom`. The old-version content struct exposes the -timer's `XxxNext` (`DateTime`) and `XxxDelay` (`TimeSpan`, `TimeSpan.MinValue` when no timer -was running): - -```csharp -private void MigrateFrom(V3Content content) -{ - if (content.DecayTimerDelay != TimeSpan.MinValue) - { - DeserializeDecayTimer(content.DecayTimerDelay); - } -} -``` - -### [SaveFlag(nameof(ShouldSerializeMethod), nameof(DefaultValueMethod))] - -**Target**: the serializable field or property itself -**Conditional serialization** -- skip fields that have their default value. - -The first method (`bool Method()`) decides whether the value is written. The optional second -method (returning the field's type, no parameters) supplies the value at load when it was -not written; when omitted, the field keeps its default value. - -```csharp -[SerializableField(0)] -[SaveFlag(nameof(ShouldSerializeCharges), nameof(ChargesDefaultValue))] -private int _charges; - -private bool ShouldSerializeCharges() => _charges != -1; - -private int ChargesDefaultValue() => -1; -``` - -Works on `[SerializableProperty]` members the same way: - -```csharp -[EncodedInt] -[SerializableProperty(0)] -[SaveFlag(nameof(ShouldSerializeMaxItems), nameof(MaxItemsDefaultValue))] -public int MaxItems -{ - get => _maxItems == -1 ? DefaultMaxItems : _maxItems; - set { _maxItems = value; this.MarkDirty(); } -} - -private bool ShouldSerializeMaxItems() => _maxItems != -1; - -private int MaxItemsDefaultValue() => -1; -``` - -### [TypeAlias(params string[] aliases)] - -**Target**: Class declaration -**Effect**: Maps old type names to this class for deserialization of old saves. - -```csharp -[TypeAlias("Server.Mobiles.Bear")] -[SerializationGenerator(0)] -public partial class BlackBear : BaseCreature { } -``` - -### [Constructible(accessLevel)] - -**Target**: Constructor -**Effect**: Marks constructor as available for the `[add` command. - -```csharp -[Constructible] // Any player can [add -public MyItem() : base(0x1234) { } - -[Constructible(AccessLevel.Administrator)] // Only admins can [add -public SpecialItem() : base(0x5678) { } -``` - ---- - -## Version Migration - -### When to Increment Version - -Increment the version number when you: -- Add a new serialized field -- Remove a serialized field -- Reorder fields (change indexes) -- Change a field's type - -### Migration Schema Files - -Located in `Projects/Server/Migrations/` and `Projects/UOContent/Migrations/`. -Format: `Namespace.TypeName.vN.json` - -These JSONs are read by the source generator at compile time to build the `VXContent` types referenced by `MigrateFrom`. They are **not** emitted by `dotnet build` — you must run the schema generator tool after every version bump to produce the new `vN.json`: - -```sh -dotnet tool restore -dotnet tool run ModernUOSchemaGenerator -- ModernUO.slnx -``` - -Verify `Namespace.TypeName.v{N+1}.json` appears in the appropriate `Migrations/` folder, then commit it with the code change. Without the new JSON, the next version bump won't be able to construct `V{N+1}Content` and will fail to compile. Equivalent shortcut via the build tool: `dotnet run --project Projects/BuildTool -- --action migrate`. - -Example: `Server.Accounting.Account.v6.json` -```json -{ - "version": 6, - "type": "Server.Accounting.Account", - "properties": [ - { - "name": "Username", - "type": "string", - "rule": "PrimitiveTypeMigrationRule", - "ruleArguments": ["InternString"] - }, - { - "name": "Mobiles", - "type": "Server.Mobile[]", - "rule": "ArrayMigrationRule", - "ruleArguments": ["Server.Mobile", "SerializableInterfaceMigrationRule"] - } - ] -} -``` - -### Migration Rule Types -| Rule | Description | -|---|---| -| `PrimitiveTypeMigrationRule` | Basic types: int, string, bool, DateTime, etc. | -| `EnumMigrationRule` | Enum values | -| `ListMigrationRule` | `List` | -| `ArrayMigrationRule` | `T[]` | -| `DictionaryMigrationRule` | `Dictionary` | -| `SerializableInterfaceMigrationRule` | Objects implementing ISerializable | -| `SerializationMethodSignatureMigrationRule` | Objects with custom Deserialize methods | - -### MigrateFrom Pattern - -When you bump the `[SerializationGenerator]` version number, you **must** add a `MigrateFrom` method to handle the transition from the previous version. The serialization generator creates a `VXContent` struct (where X is the previous version number) containing PascalCase properties matching the old serialized fields. - -```csharp -// Version bumped from 0 to 1 (added _quality field) -[SerializationGenerator(1)] -public partial class MagicGem : Item -{ - [SerializableField(0)] - private int _charges; - - [SerializableField(1)] // New in v1 - private GemQuality _quality; -} - -// In MagicGem.Migrations.cs: -public partial class MagicGem -{ - private void MigrateFrom(V0Content content) - { - _charges = content.Charges; - // _quality defaults to GemQuality.Rough (default enum value) - } -} -``` - -**Key rules:** -- The method signature is `private void MigrateFrom(VXContent content)` where X is the **previous** version -- `VXContent` is auto-generated with PascalCase properties matching the old fields -- New fields not present in the old version get their default values -- Organize migrations in a separate `.Migrations.cs` partial file for clarity - -### Deserialize vs MigrateFrom — Know the Difference - -> **WARNING**: Do NOT modify `Deserialize(IGenericReader reader, int version)` when bumping SerializationGenerator versions. - -The two mechanisms serve different purposes: - -| Method | When It Runs | Purpose | -|---|---|---| -| `Deserialize(IGenericReader reader, int version)` | Loading saves from **before** the class was converted to the SerializationGenerator | One-time legacy migration only | -| `MigrateFrom(VXContent content)` | Transitioning between SerializationGenerator versions | All post-codegen version bumps | - -The `Deserialize` function exists **only** to handle deserialization of entities saved before they used `[SerializationGenerator]`. Once a class uses the generator, all future version transitions must use `MigrateFrom`. - ---- - -## Extension Methods - -From `Projects/Server/Serialization/ISerializableExtensions.cs`: - -```csharp -// Mark entity as dirty (must be called in custom property setters) -entity.MarkDirty(); - -// Collection operations (auto-mark dirty) -entity.Add(list, value); -entity.Add(dict, key, value); -entity.Remove(list, value); -entity.Clear(list); - -// Timer operations (auto-mark dirty) -entity.Stop(timer); -entity.Start(timer); -entity.Restart(timer, delay, interval); -entity.Stop(ref timer); // Stops and nulls the reference -``` - ---- - -## Complete Example: Versioned Item - -```csharp -using ModernUO.Serialization; -using Server.Targeting; - -namespace Server.Items; - -public enum GemQuality -{ - Rough, - Cut, - Flawless -} - -[SerializationGenerator(1)] // Version 1 (added Quality in v1) -public partial class MagicGem : Item -{ - [SerializableField(0)] - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private int _charges; - - [SerializableField(1)] // Added in version 1 - [InvalidateProperties] - [SerializedCommandProperty(AccessLevel.GameMaster)] - private GemQuality _quality; - - private TimerExecutionToken _pulseTimer; - - [Constructible] - public MagicGem() : base(0x1EA7) - { - _charges = Utility.RandomMinMax(5, 15); - _quality = GemQuality.Rough; - Weight = 1.0; - Light = LightType.Circle150; - StartPulse(); - } - - public override string DefaultName => "a magic gem"; - - private void StartPulse() - { - Timer.StartTimer( - TimeSpan.FromSeconds(3), - TimeSpan.FromSeconds(3), - Pulse, - out _pulseTimer - ); - } - - [AfterDeserialization] - private void AfterDeserialization() - { - StartPulse(); - } - - public override void OnAfterDelete() - { - _pulseTimer.Cancel(); - base.OnAfterDelete(); - } - - private void Pulse() - { - if (_charges <= 0) - { - _pulseTimer.Cancel(); - return; - } - - Effects.SendLocationParticles(this, 0x376A, 9, 10, 5042); - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - list.Add(1060741, $"{_charges}"); // charges: ~1_val~ - list.Add($"{"Quality: "}{_quality}"); - } - - public override void OnDoubleClick(Mobile from) - { - if (!IsChildOf(from.Backpack)) - { - from.SendLocalizedMessage(1042001); // Must be in backpack - return; - } - - if (_charges <= 0) - { - from.SendMessage("The gem is depleted."); - return; - } - - _charges--; - InvalidateProperties(); - this.MarkDirty(); - from.SendMessage("The gem pulses with energy!"); - } -} -``` - ---- - -## Key File Locations - -| File | Description | -|---|---| -| `Projects/Server/Serialization/ISerializableExtensions.cs` | MarkDirty(), collection helpers | -| `Projects/Server/Migrations/*.v*.json` | Server migration schemas | -| `Projects/UOContent/Migrations/*.v*.json` | Content migration schemas | -| `Projects/UOContent/Mobiles/Animals/Bears/BlackBear.cs` | Simple creature example | -| `Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs` | Fields + timer token | -| `Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs` | Custom properties | -| `Projects/UOContent/Accounting/Account.cs` | Complex versioned type | -| `Projects/UOContent/Items/Aquarium/Aquarium.cs` | Timer deserialization | -| `Projects/Server/Items/Container.cs` | Conditional serialization | diff --git a/dev-docs/server-lifecycle.md b/dev-docs/server-lifecycle.md deleted file mode 100644 index 69056427c..000000000 --- a/dev-docs/server-lifecycle.md +++ /dev/null @@ -1,127 +0,0 @@ -# Server Lifecycle & Bootstrap Phases - -How a ModernUO server starts, the reflection-discovered lifecycle hooks (`ConfigurePrompts`, -`Configure`, `Initialize`), the runtime `EventSink` events, and **which hook to use for what**. - -The startup orchestration lives in `Projects/Server/Main.cs` (`Core` entry point). The named -phases are dispatched by `AssemblyHandler.Invoke("")`, which finds every -`public static void ()` (parameterless) across `Core.Assembly` **and** all loaded -content assemblies and calls them — no registration required. - -## Startup sequence (in order) - -> Don't hardcode line numbers when reasoning about this — refer to the phase/method names; the -> ordering is what's stable. - -1. **Console banner + setup** — direct synchronous `Console.*` writes (no logging yet). -2. **`ServerConfiguration.Load()`** — reads/creates `modernuo.json`. On **first boot** (file - absent) it runs the engine's own interactive console prompts: data directories, listeners, - server name, expansion + map selection. **Pre-Serilog** — nothing has logged yet, so the - console is clean for prompts. (`Load(mocked: true)` skips all prompts; that's what tests use.) -3. **`AssemblyHandler.LoadAssemblies(...)`** — loads `UOContent.dll` (and friends) from - `AssemblyDirectories` (default `./Assemblies`). Note: this depends on `AssemblyDirectories`, - **not** `DataDirectories`, so it does not need the data-dir prompt to have run. -4. **`AssemblyHandler.Invoke("ConfigurePrompts")`** — first-boot interactive prompts contributed - by *any* assembly (engine or content). Runs **after** assemblies load (so content can - participate) but **before the first Serilog line** (so prompts aren't interleaved with the - async console sink). Each handler self-gates on first-boot state. -5. **First `logger.Information(...)`** — Serilog goes live. From here on, log via the logger; - the console sink is async, so anything you write with `Console.*` after this can interleave - with log output. -6. **`VerifySerialization()`** → **`Timer.Init(...)`**. -7. **`AssemblyHandler.Invoke("Configure")`** — the main configuration phase. World is **not** - loaded yet (no entities), but maps are registered. -8. **`TileMatrixLoader.LoadTileMatrix()`** → **`RegionJsonSerializer.LoadRegions()`**. -9. **`World.Load()`** — deserializes all items/mobiles; fires `EventSink.WorldLoad`. -10. **`AssemblyHandler.Invoke("Initialize")`** — post-world phase. World entities **and** the - tile matrix are available. -11. **`NetState.Start()`** / **`PingServer.Start()`** → **`EventSink.InvokeServerStarted()`** → - **`RunEventLoop()`** (the single-threaded game loop begins). - -## The three reflection phases — which to use - -| Phase | Runs | Use it for | Don't | -|---|---|---|---| -| **`ConfigurePrompts()`** | after assemblies load, **before logging** | one-time **first-boot interactive prompts**; persist the answer to `modernuo.json`; self-gate so it asks once; skip when input is redirected | log (Serilog isn't live — use `Console`); touch World/maps/tile data (not ready) | -| **`Configure()`** | post-logging, **pre-World** | command registration, reading settings (`GetOrUpdateSetting`), `EventSink` subscriptions, wiring systems | anything needing loaded **World entities** or the tile matrix | -| **`Initialize()`** | **post-World**, post-tile-matrix | work needing a loaded world / tile data: decoration/generation, validation, pre-baking caches | first-boot prompts (too late, and it would clobber logs) | - -All three are `public static void ()`, parameterless, discovered across every loaded -assembly. Within a phase, order is controlled by **`[CallPriority(n)]`** (lower runs first; -default `50`). **Same-priority order is unspecified**, so never rely on one class's `Configure` -running before another's at the same priority — use `EventSink`/explicit calls for ordering. - -## Pre-Serilog vs post-Serilog — why `ConfigurePrompts` exists - -Logging uses an **async** Serilog console sink (`Serilog.Sinks.Async` → `LogFactory`). Once the -first `logger.*` call fires (right after the `ConfigurePrompts` phase), log lines are pumped to -the console from a background thread and will **interleave** with anything written via -`Console.*`. Interactive prompts therefore have to run *before* that point. `ConfigurePrompts` -is the **only** reflection phase that runs pre-logging — that is its entire reason to exist. -Inside it: use `Console`, never the logger; and guard with `Console.IsInputRedirected` so -headless/CI boots don't block on `Console.ReadLine`. - -## Runtime lifecycle events (`EventSink`) - -Subscribe to these from `Configure`/`Initialize` (`EventSink. += handler`): - -- **`ServerStarted`** — after world load and listeners are up, at loop start. -- **`WorldLoad`** / **`WorldSave`** — around persistence (see `WorldEvents`). -- **`Shutdown`** — during shutdown. - -## Recipe: add a first-boot prompt - -```csharp -public static void ConfigurePrompts() -{ - // Ask once, and only when a human is at the console. The answer persists in modernuo.json. - if (ServerConfiguration.GetSetting("my.feature", (string)null) != null || Console.IsInputRedirected) - { - return; - } - - Console.Write("Enable my feature? [y/N] "); - var yes = Console.ReadLine()?.Trim().StartsWith("y", StringComparison.OrdinalIgnoreCase) == true; - ServerConfiguration.SetSetting("my.feature", yes); -} -``` - -If acting on the answer needs a loaded world / tile data, do that in `Initialize()` (read the -setting there), not in `ConfigurePrompts`. - -### Canonical example — pathfinding pre-bake - -`Projects/UOContent/Engines/Pathing/PathCacheCommands.cs` is the reference pairing: - -- `ConfigurePrompts()` — first-boot `[y/N]`, stores `pathfinding.prebakeMaps`. -- `Initialize()` — when set, bakes any missing/stale `.swb` (needs the tile matrix, so it must - be `Initialize`, not `Configure`). - -## Testing note - -Tests do **not** go through `Main`. The test fixtures (`Server.Tests`/`UOContent.Tests` -`TestServerInitializer`) call a curated subset of phase methods directly with -`ServerConfiguration.Load(mocked: true)`, so console prompts are skipped. Consequence: changes -to the **startup ordering in `Main.cs`** (including the prompt phases) are **not** covered by the -test suite and need first-boot runtime verification. - -## Unified: the engine's first-boot prompts run through `ConfigurePrompts` - -The engine's own first-boot prompts (data directories, listeners, server name, expansion + map -selection) live in **`ServerConfiguration.ConfigurePrompts()`** (`[CallPriority(0)]`) and are -discovered by the same `Invoke("ConfigurePrompts")` phase as content prompts — one sequence and -one wiring for all first-boot prompting. `ServerConfiguration.Load` now only reads/creates the -config file. What made this safe: - -- Assembly loading uses `AssemblyDirectories` (default `./Assemblies`), **not** `DataDirectories`, - so assemblies load fine before the (now-later) data-dir prompt. -- `UOClient.Load()` (client-file discovery via `Core.FindDataFile`) needs `DataDirectories`, so it - moved *with* the data-dir prompt into `ConfigurePrompts`. -- `Core.Expansion` is now assigned in `ConfigurePrompts` (every non-mocked boot). Nothing between - assembly-load and that phase reads it — type initializers run lazily on first use, not during - `LoadAssemblies`. -- `[CallPriority(0)]` keeps the engine prompts (including map selection) ahead of content prompts - such as the pathfinding pre-bake (default priority 50), preserving "after map selection". - -Since `Main.cs` startup ordering isn't covered by the fixture-based suite (see Testing note), this -path is validated by a first-boot runtime check rather than tests. diff --git a/dev-docs/server-requirements.md b/dev-docs/server-requirements.md deleted file mode 100644 index 0b2c50569..000000000 --- a/dev-docs/server-requirements.md +++ /dev/null @@ -1,121 +0,0 @@ -# Server Requirements - -Hardware guidance for running a ModernUO shard. - -## Tiers - -| Use | vCPU | RAM | Storage | -|---|---|---|---| -| Development / test | 2 **dedicated** | 2 GB | SSD | -| Small live shard (< 50 concurrent) | 4 dedicated | 4 GB | NVMe | -| Medium (50–200) | 4–8 | 8 GB | NVMe | -| Large (200+) | 8+, high clock | 16 GB+ | NVMe | - -These are starting points. Save size drives RAM more than player count does, and single-thread -clock speed drives tick latency more than core count does. Both are explained below. - -## Dedicated vCPU, not burstable - -This matters more than any other line on this page. - -Budget VPS plans sold as "2 vCPU" are frequently shared or burstable: you get a CPU credit balance -or a cgroup quota, and once it is exhausted the hypervisor throttles you. Throttling shows up in -game as periodic freezes that correlate with nothing in your logs, and it is the single most common -cause of "ModernUO is laggy on my $3/month VPS". - -Symptoms worth checking before blaming the server: - -- Steal time above ~1% (`top`, the `%st` column on Linux) -- Lag that disappears when you move to a larger plan with the same core count -- Tick lag spikes with no matching CPU spike in the process itself - -## Cores - -Game logic is **single-threaded**. Every mobile, item, timer, and packet handler runs on one -thread, so a shard's headroom is bounded by how fast one core is. Two fast cores beat four slow -ones. - -Cores beyond the first are used by: - -- **World saves.** `world.useMultithreadedSaves` (default on) spins up `ProcessorCount - 1` - serialization workers plus one inline on the main thread. On a 2-core box that is one worker; on - a 2-core box with a large world, consider setting it to `false` so saves do not contend with the - loop. -- **The .NET runtime.** Tiered JIT compilation (heaviest in the first minutes after boot) and - background GC. -- **Everything else on the machine**, including your OS and, on Windows, antivirus. - -Since ModernUO 2026 the loop sleeps when idle, so an empty shard costs roughly 1% of a core rather -than spinning. That change disproportionately helps small hosts. - -## Memory - -Three things dominate, and only one of them scales with players. - -**World size.** A world of ~190,000 items and ~33,000 mobiles loads in about a second and is not -itself large. Items and mobiles are the cheap part. - -**Saves.** Each serialization worker pre-allocates a heap sized to its share of the last save, at -roughly 1.25× total save size, and those buffers are retained afterwards. A 400 MB save therefore -implies about 500 MB of resident serialization heap on top of the live world. **This is the reason -1 GB hosts are not viable for a real shard**, even though an empty one boots fine. - -**Map residency.** `TileMatrix` reads map blocks from disk on demand and caches them permanently — -there is no eviction. Memory climbs toward full-facet residency as players explore. Felucca's land -tiles alone are around 117 MB, and statics are larger. - -Optional systems can add substantially more. The pathfinding prebake -(`pathfinding.prebakeMaps`) peaks above 1 GB of heap while baking. Budget for it or leave it off on -small hosts. - -Network buffers are minor by comparison: 64 KB receive plus a configurable 256 KB send -(`network.sendBufferSize`) per connection, so 100 players is roughly 32 MB. - -ModernUO runs **Workstation GC**, which is the right default for small hosts. Do not switch to -Server GC on a 2-core box. - -## Storage - -Saves are write-heavy bursts. Cheap network-attached storage with throttled IOPS will stall the -save path, and `World.WaitForWriteCompletion` blocks the loop at shutdown. Use local NVMe or SSD. - -Budget disk for: the world save, plus archives and backups if `autoArchive` is enabled (retention -defaults keep 24 hourly, 30 daily, and 12 monthly copies), plus the pathfinding cache if enabled. - -## Operating systems - -See the README for the full supported list. Two things are worth calling out: - -- **Windows Server 2012 R2 and 2016 sleep via a raised timer resolution.** Sleeping for a couple - of milliseconds prefers a high-resolution waitable timer, which requires Windows 10 1803 / - Server 2019. On older versions the ring falls back to `timeBeginPeriod(1)`, which raises the - system timer resolution to 1 ms so the plain wait timeout is accurate enough. The trade-off is a - higher interrupt rate (system-wide on those versions) — an acceptable price on a dedicated game - server, and the reason the high-resolution timer is preferred where it exists. - - Only if *both* mechanisms fail does the server detect it at startup, log it, and spin instead — - the same behaviour as setting `server.eventLoopIdleWaitMs` to 0: a full core at idle, and zero - missed deadlines. A host that claims short waits but cannot deliver them is caught at runtime by - the adaptive backoff. -- **Linux kernel 6.1** or newer (Debian 12 and equivalents). io_uring is used where available, with - automatic epoll fallback. - -## Tuning for a small host - -| Setting | Default | Why change it | -|---|---|---| -| `server.eventLoopIdleWaitMs` | `2` | `0` never sleeps: ~98% of one core, but zero skipped timer slots and zero lag. The choice for a large shard on dedicated CPU that would rather spend a core than risk a late wake. Above `2` the wheel starts losing slots. | -| `server.lateWakeThreshold` | `1` | Floor for the backoff: idle waits the host may return a full tick late, per second, before the rate test below applies at all. Raise on a jittery host; set very high to disable the backoff. | -| `server.lateWakePercent` | `10` | Share of a second's idle waits that must come back late before idle sleeping backs off. An idle loop sleeps hundreds of times a second, so a bare count cannot tell a few tail outliers from a host that never schedules the process — a genuinely bad host misses *most* of its waits. `0` leaves `lateWakeThreshold` in sole charge. | -| `world.useMultithreadedSaves` | `true` | Set `false` on 2-core hosts so saves do not contend with the game loop. | -| `pathfinding.prebakeMaps` | varies | Leave off on memory-constrained hosts; it peaks above 1 GB while baking. | -| `network.sendBufferSize` | 256 KB | Lower it if you are memory-bound with many connections. | -| `autoArchive.*` retention | 24h/30d/12m | Reduce if disk is tight. | - -## Am I undersized? - -Watch the log. The server warns when the host returns idle waits late and suspends idle sleeping, -and says so at startup if the host cannot honour short waits at all. Those warnings mean the host -is not scheduling the process promptly — typical of burstable or shared vCPU plans — and no -server-side change fixes that. For anything deeper, see -[debugging-event-loop.md](debugging-event-loop.md). diff --git a/dev-docs/string-handling.md b/dev-docs/string-handling.md deleted file mode 100644 index 6c915c6c9..000000000 --- a/dev-docs/string-handling.md +++ /dev/null @@ -1,402 +0,0 @@ -# String Handling in ModernUO - -This document covers the string building utilities in `Projects/Server/Text/` and `Projects/Server/Buffers/`, when to use each, and how to avoid common allocation pitfalls. - -## Table of Contents -1. [ValueStringBuilder](#valuestringbuilder) -2. [RawInterpolatedStringHandler](#rawinterpolatedstringhandler) -3. [Interpolation Anti-Patterns](#interpolation-anti-patterns) -4. [StringHelpers](#stringhelpers) -5. [TextEncoding](#textencoding) -6. [Decision Guide](#decision-guide) - ---- - -## ValueStringBuilder - -**Location**: `Projects/Server/Buffers/ValueStringBuilder.cs` -**Namespace**: `Server.Text` - -A `ref struct` string builder that avoids heap allocations entirely when backed by `stackalloc`. This is the **preferred** string builder for all ModernUO code — do not use `System.Text.StringBuilder`. - -### Construction Patterns - -**Stackalloc (preferred for bounded output)**: -```csharp -// Best: zero heap allocation, zero pool overhead -using var sb = new ValueStringBuilder(stackalloc char[128]); -sb.Append($"Hello {name}, score: {score}"); -return sb.ToString(); -``` - -**Pooled (for unbounded or large output)**: -```csharp -// Rents from STArrayPool — returned on Dispose -using var sb = ValueStringBuilder.Create(256); -// or with default capacity (64): -using var sb = ValueStringBuilder.Create(); -``` - -### Choosing Capacity - -| Output size | Pattern | -|---|---| -| Known, <=256 chars | `new ValueStringBuilder(stackalloc char[N])` | -| Known, >256 chars | `ValueStringBuilder.Create(N)` | -| Unbounded/unknown | `ValueStringBuilder.Create()` (grows automatically) | - -If the stackalloc buffer is too small, the builder automatically grows to a pooled array. This is safe but costs a pool rent — size the stackalloc to fit the expected output. - -### String Interpolation (`$"..."`) - -`ValueStringBuilder` supports `$"..."` syntax via a copy-and-reconcile `InterpolationHandler`. This writes directly into the builder's buffer — **no intermediate allocation**, even with stackalloc. - -```csharp -// Works with stackalloc — zero allocation -using var sb = new ValueStringBuilder(stackalloc char[64]); -sb.Append($"Player {name} has {kills} kills"); -sb.Append($" and {bounty} gold bounty"); -``` - -**How it works**: The compiler passes a value copy of the builder to the handler. The copy shares the same underlying buffer (Span points to the same memory), so writes go to the original buffer. `Append()` reconciles by copying the handler's updated state back to the original. If the handler triggers a `Grow()`, the reconciliation updates the buffer reference. - -### Reusing a Builder - -Use `Reset()` to clear the builder for reuse instead of creating a new one: - -```csharp -using var sb = new ValueStringBuilder(stackalloc char[128]); - -foreach (var item in items) -{ - sb.Reset(); // clear for next iteration - sb.Append($"{item.Name}: {item.Value}"); - Process(sb.ToString()); -} -``` - -### Reading the Result - -| Method | Use when | -|---|---| -| `sb.ToString()` | You need a `string` (allocates) | -| `sb.AsSpan()` | You can consume a `ReadOnlySpan` (zero-alloc) | -| `sb.AsSpan(terminate: true)` | You need a null-terminated span | - -### Disposal - -Always use `using var` for automatic disposal: -```csharp -using var sb = new ValueStringBuilder(stackalloc char[64]); -``` - -If `using var` is not possible (e.g., the builder is passed by `ref` to extension methods, or `goto case` in switch blocks), use manual `Dispose()`: -```csharp -var sb = new ValueStringBuilder(stackalloc char[64]); -sb.AppendSpaceWithArticle(text, articleAn); // takes ref ValueStringBuilder -var result = sb.ToString(); -sb.Dispose(); -``` - -For stackalloc-only builders that never grow, `Dispose()` is a no-op. But always call it defensively — if a future change triggers growth, the pooled array needs returning. - -### Limitations - -- **No `AppendFormat`**: Use `$"..."` interpolation instead — it's more readable and zero-allocation: - ```csharp - // StringBuilder (old): - sb.AppendFormat("{0:N0} points, {1:N0} kills", score, kills); - - // ValueStringBuilder: - sb.Append($"{score:N0} points, {kills:N0} kills"); - ``` - There is no `object[] params` equivalent for format strings. All formatting goes through `$"..."` interpolation which uses `ISpanFormattable.TryFormat` directly — zero boxing, zero intermediate strings. - -- **No chained Append**: `Append()` returns `void`, not `this`. Write `sb.Append(a); sb.Append(b);` instead of `sb.Append(a).Append(b)`. -- **Ref struct constraints**: Cannot be stored in fields, captured by lambdas, or used in `async` methods. Scoped to the declaring method. -- **`using var` + `ref` conflict**: A `using` variable cannot be passed by `ref`. If extension methods take `ref ValueStringBuilder`, use manual `Dispose()` instead. - ---- - -## RawInterpolatedStringHandler - -**Location**: `Projects/Server/Buffers/RawInterpolatedStringHandler.cs` -**Namespace**: `Server.Buffers` - -A `[InterpolatedStringHandler]` ref struct that renders an interpolated string directly into a `char[]` rented from `STArrayPool.Shared`. Used as a parameter type to make zero-allocation interpolation overloads possible — when the caller writes `$"..."`, the compiler synthesizes the handler, fills it with the formatted chars, and the receiving method passes `handler.Text` to its underlying span path. - -**You normally don't construct this directly**: it's used as a parameter type. Most ModernUO APIs that accept formatted text already provide a `ref RawInterpolatedStringHandler` overload alongside the `string` / `ReadOnlySpan` overload. The compiler picks the handler overload automatically when the argument is a `$"..."` literal. - -### APIs that accept `ref RawInterpolatedStringHandler` - -- `SpanWriter.WriteAscii`, `WriteLatin1`, `Write(Encoding, …)` — packet building -- `Mobile.SendMessage`, `SendLocalizedMessage`, `SendAsciiMessage`, `Public/Local/Nonlocal/PrivateOverheadMessage`, `Say`, `Emote`, `Whisper`, `Yell` — player-facing chat -- `Item.PublicOverheadMessage`, `SendLocalizedMessageTo`, `SendMessageTo` — item-attributed messages -- `OutgoingMessagePackets.SendMessageLocalized`, `SendMessageLocalizedAffix`, `SendMessage` — direct NetState extensions -- `Html.Center`, `Html.Color`, `Html.Right` — gump HTML helpers - -When in doubt, just write `$"..."` at the call site — if a handler overload exists, the compiler picks it. - -### `:L` Lowercase Format Specifier - -`RawInterpolatedStringHandler` recognizes `:L` as a custom format specifier that lowercases the value's output in-place using `char.ToLowerInvariant`. Handles surrogate pairs correctly via the BCL's vectorized `MemoryExtensions.ToLowerInvariant`. - -```csharp -mob.SendMessage($"You earned a {rank:L} trophy!"); // "gold" instead of "Gold" -mob.SendMessage($"Welcome, {playerName:L}"); // lowercased name -mob.SendMessage($"{count:L} kills"); // ints unchanged ("42") -``` - -This eliminates the `value.ToString().ToLowerInvariant()` two-allocation idiom. Works for any type that goes through the handler (enums, strings, anything `ISpanFormattable`). - -The format string is case-sensitive — `:l` is not recognized. Match the convention of standard format specifiers (`:N0`, `:F2`, etc.) and use uppercase `:L`. - -### Pooled buffer lifecycle - -`RawInterpolatedStringHandler` rents a `char[]` from `STArrayPool.Shared` on construction (sized by the literal length + an estimate of formatted chars per hole). Methods that take `ref RawInterpolatedStringHandler` are responsible for calling `handler.Clear()` after consuming `handler.Text`, which returns the buffer to the pool. The rent is single-threaded and lock-free (~tens of nanoseconds), so the cost is negligible compared to the `string` allocation it replaces. - ---- - -## Interpolation Anti-Patterns - -ModernUO has many APIs with `ref RawInterpolatedStringHandler` overloads (messages, gumps, packets, OPL — see the list above). The handler overload is **only selected when the call-site argument is a `$"..."` literal directly in position**. Several patterns silently defeat handler binding and fall back to a `string`-allocating path. Each pattern below has a "before" and "after" — apply the "after" form when writing or reviewing code that interpolates into any handler-aware API. - -### 1. Ternary with interpolated branches - -```csharp -// BAD — the ternary unifies branches as `string`; handler overload not selected -mob.SendMessage(cond ? $"a {x}" : $"b {y}"); -``` -```csharp -// GOOD — each branch is a separate call, each binds to the handler overload -if (cond) -{ - mob.SendMessage($"a {x}"); -} -else -{ - mob.SendMessage($"b {y}"); -} -``` - -`RawInterpolatedStringHandler` is a `ref struct` and cannot appear in a conditional expression result type — the C# compiler unifies the ternary branches to `string`, and the call binds to the `string` / `ROS` overload, allocating the message text per call. - -### 2. Switch expression with interpolated arms - -```csharp -// BAD — switch expression branches unify as `string` -mob.SendMessage(thing switch -{ - 1 => $"a {x}", - _ => $"b" -}); -``` -```csharp -// GOOD — switch statement, each arm calls the handler-aware API directly -switch (thing) -{ - case 1: - mob.SendMessage($"a {x}"); - break; - default: - mob.SendMessage($"b"); - break; -} -``` - -Same root cause as the ternary case. - -### 3. Pre-built local typed as `string` - -```csharp -// BAD — `msg` is a `string`; ROS overload picked, not the handler -var msg = $"foo {x}"; -mob.SendMessage(msg); -``` -```csharp -// GOOD — inline at the call site so the compiler sees the literal -mob.SendMessage($"foo {x}"); -``` - -If the local is reused (multiple calls, multiple branches), keep the local — pre-building avoids re-interpolating per send. Inline only when the local is single-use. - -### 4. `.ToString()` (or any string-returning method) inside a hole - -```csharp -// BAD — .ToString() allocates a string before the handler copies the chars -mob.SendMessage($"You are now {accessLevel.ToString()}."); -mob.SendMessage($"Your guild is {td.String()}."); -``` -```csharp -// GOOD — drop the .ToString() and let the handler format the value directly -mob.SendMessage($"You are now {accessLevel}."); -mob.SendMessage($"Your guild is {td}."); -``` - -The handler's `AppendFormatted` calls `ISpanFormattable.TryFormat` on the value directly, with zero intermediate `string`. An explicit `.ToString()` defeats this. The same applies to `.String()` (TextDefinition), `.GetValue()`, `.AsHexString()`, and any other method that returns a freshly allocated `string`. - -For values that don't implement `ISpanFormattable`, the handler falls back to `value.ToString()` internally — same allocation as the explicit call, but at least the call site is consistent. - -For lowercase output, use the `:L` format specifier instead of `.ToString().ToLowerInvariant()` (see [RawInterpolatedStringHandler](#rawinterpolatedstringhandler)). - -### 5. String concatenation inside a hole - -```csharp -// BAD — `+` on strings allocates an intermediate string -mob.SendMessage($"Total: {a + b}"); -mob.SendMessage($"Title: {string.Concat(prefix, name)}"); -``` -```csharp -// GOOD — multiple holes, each formatted directly into the buffer -mob.SendMessage($"Total: {a}{b}"); -mob.SendMessage($"Title: {prefix}{name}"); -``` - -Note: `int + int` inside a hole is arithmetic, not concatenation — that's fine. The anti-pattern is `string + string` or `string + value`. - -### 6. `string.Format` feeding a handler-aware API - -```csharp -// BAD — string.Format allocates a string the handler then re-buffers -mob.SendMessage(string.Format("You earned {0:N0} gold", amount)); -``` -```csharp -// GOOD — the handler formats `amount` directly into its buffer -mob.SendMessage($"You earned {amount:N0} gold"); -``` - -### 7. LINQ-built strings inside a hole - -```csharp -// BAD — Select/Aggregate/Join on strings allocates a chain of intermediates -mob.SendMessage($"Allies: {names.Aggregate((a, b) => $"{a}, {b}")}"); -``` -```csharp -// GOOD — build via ValueStringBuilder, pass the span -using var sb = new ValueStringBuilder(stackalloc char[256]); -sb.Append("Allies: "); -for (var i = 0; i < names.Count; i++) -{ - if (i > 0) - { - sb.Append(", "); - } - sb.Append(names[i]); -} -mob.SendMessage(sb.AsSpan()); -``` - -For unbounded inputs, use `ValueStringBuilder.Create()` and call `mob.SendMessage(sb.ToString())` if the consumer needs a `string` (one allocation, vs LINQ's many). - -### 8. Pre-built concat var - -```csharp -// BAD — concatenation allocates, then the local picks the ROS overload -var s = obj.Name + " says hi"; -mob.SendMessage(s); -``` -```csharp -// GOOD — interpolation literal at the call site -mob.SendMessage($"{obj.Name} says hi"); -``` - -### Why these matter - -These patterns aren't bugs — they produce correct output. But they each leak a `string` per call, and message/gump/OPL APIs are called constantly during gameplay. The handler overload exists specifically to eliminate that allocation, but only when the call-site argument is a direct `$"..."` literal. - -When in doubt, ask: "is the handler overload selected here?" — and if the argument is anything other than a top-level `$"..."` literal in the parameter slot, the answer is no. - ---- - -## StringHelpers - -**Location**: `Projects/Server/Text/StringHelpers.cs` -**Namespace**: `Server.Text` - -Extension methods for common string operations: - -| Method | Description | -|---|---| -| `Wrap(string, int perLine, int maxLines)` | Word-wrap text into lines | -| `AppendSpaceWithArticle(ref ValueStringBuilder, string, bool)` | Append with "a"/"an" article prefix | -| `Remove(ReadOnlySpan, ...)` | Filter substrings from spans | -| `Capitalize(string)` | Title-case with "the" handling | -| `TrimMultiline(string)` | Trim each line in multiline text | - ---- - -## TextEncoding - -**Location**: `Projects/Server/Text/TextEncoding.cs` -**Namespace**: `Server.Text` - -UTF-8/Unicode encoding utilities used by the networking layer: - -| Method | Description | -|---|---| -| `GetBytesUtf8(string, Span)` | Encode string to UTF-8 in buffer | -| `GetBytesUtf8(ReadOnlySpan, Span)` | Encode char span to UTF-8 | -| `GetStringUtf8(Span)` | Decode UTF-8 bytes to string (with filtering) | - ---- - -## Decision Guide - -### When to use what - -``` -Need to build a string? -├── In a hot path (packets, ticks, spatial queries)? -│ └── ValueStringBuilder with stackalloc -├── In game content (gumps, messages, commands)? -│ └── ValueStringBuilder with stackalloc (bounded) or Create() (unbounded) -├── Building an ObjectPropertyList tooltip? -│ └── Use IPropertyList.Add($"...") — has its own handler -├── In async/multi-threaded code (rare)? -│ └── ValueStringBuilder.CreateMT() or System.Text.StringBuilder -└── Never → System.Text.StringBuilder -``` - -### Do NOT use `System.Text.StringBuilder` - -`ValueStringBuilder` replaces `StringBuilder` in all game code. It avoids: -- GC pressure from `StringBuilder`'s internal `char[]` allocations -- The `StringBuilder` object allocation itself (24+ bytes on heap) -- Thread-safe overhead in `ArrayPool.Shared` (VSB uses lock-free `STArrayPool`) - -### Common patterns - -**Instead of string concatenation**: -```csharp -// BAD: allocates intermediate strings -var msg = "Player " + name + " has " + kills + " kills"; - -// GOOD: zero allocation with stackalloc -using var sb = new ValueStringBuilder(stackalloc char[64]); -sb.Append($"Player {name} has {kills} kills"); -var msg = sb.ToString(); // single allocation for the final string -``` - -**Instead of StringBuilder**: -```csharp -// BAD: StringBuilder allocates on heap -var sb = new StringBuilder(); -sb.Append(name); -sb.Append(": "); -sb.Append(value); -return sb.ToString(); - -// GOOD: ValueStringBuilder with stackalloc -using var sb = new ValueStringBuilder(stackalloc char[64]); -sb.Append($"{name}: {value}"); -return sb.ToString(); -``` - -**For packet string construction** (hot path): -```csharp -// Use AsSpan() to avoid ToString() allocation when the consumer accepts spans -using var sb = new ValueStringBuilder(stackalloc char[32]); -sb.Append(bounty); -sb.Append(" gold"); -writer.WriteString(sb.AsSpan(), textBuffer); // zero-copy -``` diff --git a/dev-docs/t2a-crafting.md b/dev-docs/t2a-crafting.md deleted file mode 100644 index 27b619264..000000000 --- a/dev-docs/t2a-crafting.md +++ /dev/null @@ -1,141 +0,0 @@ -# T2A Packet-Based Crafting Menus - -This document covers ModernUO's **T2A-era crafting menus** — the pre-UO:Third-Dawn, packet-based item-list crafting UI that replaces the modern gump crafting interface when enabled. It is the developer/AI reference for how the system is wired, how to extend it, and how it deviates from authentic T2A behavior. - -## Overview - -In the T2A era (≈1998–2001, before Publish 14 on 2001-11-30), UO crafting did not use gumps. The server sent the generic `0x7C` "Open Dialog" menu packet and the client replied with the 13-byte `0x7D` response. Double-clicking a crafting tool opened a **skill-and-material-filtered item-list menu**; the player picked a category/item and targeted a resource, and the item was made. - -ModernUO reproduces this behind a single startup-read toggle. When `T2ACraftSystem.Enabled` is `false`, crafting uses the normal `CraftGump`. When `true`, the same `CraftSystem`/`CraftItem` definitions are presented through packet menus instead. The value is read once at startup from the `t2aCraftMenus` server setting (default `!Core.UOTD`), so a pre-UO:TD shard gets the T2A menus automatically and a UO:TD-or-later shard gets gumps — with no runtime/admin toggle. - -The wire-level menu packets (`0x7C`/`0x7D`) already exist in the engine (`Projects/Server/Network/Packets/OutgoingMenuPackets.cs`, `Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs`) and in `Server.Menus.ItemLists.ItemListMenu` / `Server.Menus.Questions.QuestionMenu`. The T2A feature is a *consumer* of that existing infrastructure, not a new protocol. - -## Activation - -Toggle: `T2ACraftSystem.Enabled` (static). It is set once in `ExpansionConfiguration.Configure()` from `ServerConfiguration.GetSetting("t2aCraftMenus", !Core.UOTD)` — a read-only setting (the default is **not** written back to the config file). It is intentionally **not** a runtime feature flag and cannot be flipped in-game by admins; change it via the `t2aCraftMenus` server setting and restart. - -**Intended deployment:** because the default is `!Core.UOTD`, a pre-UO:TD shard gets T2A menus and the matching era mechanics automatically. The toggle controls the *UI system*; the expansion/era (`Core.UOTD`) controls *mechanics* (see [Gating model](#gating-model-toggle-vs-era)). Since the default tracks the era and there is no runtime override, the two cannot drift into an incoherent combination. - -## Architecture - -All T2A-specific code lives under `Projects/UOContent/Engines/Craft/T2A/`. - -| Type | File | Responsibility | -|---|---|---| -| `T2ACraftSystem` (static) | `T2ACraftSystem.cs` | Central router. `ShowMenu(from, craftSystem, tool, preTarget)` dispatches per craft system to the right resource-selection / menu flow. Hosts shared filtering helpers. | -| `T2ACraftToolTarget` (Target) | `T2ACraftToolTarget.cs` | The first target after double-clicking a tool: target the **tool** → make-last; target **anything else** → begin crafting with that item as `preTarget`. | -| `*Menu : ItemListMenu` | `AlchemyMenu.cs`, `BlacksmithMenu.cs`, `BowFletchingMenu.cs`, `CarpentryMenu.cs`, `CartographyMenu.cs`, `InscriptionMenu.cs`, `TailoringMenu.cs`, `TinkeringMenu.cs` | One menu per skill. Builds filtered entries, drives category submenus, and on response either descends a category or crafts. | - -**Separation of concerns:** -- Each `*Menu` owns only its category tree and entry formatting. -- `T2ACraftSystem.CanCraftItem` / `FilterEntries` / `AnyCraftableInCategory` own *craft-eligibility* (skill gate + material count, sub-resource aware, with resource-equivalence: `Log↔Board`, `Cloth↔UncutCloth`, `Leather↔Hides`). -- `CraftItem` / `CraftSystem` own *consumption and item creation*. - -### Control flow - -``` -BaseTool.OnDoubleClick - └─ if T2ACraftSystem.Enabled: - from.Target = new T2ACraftToolTarget(tool, system) - "Target this tool to make last item, or any other target to begin crafting." - ├─ target == tool → make-last (repeat context.LastMade; jewelry re-prompts the gem) - └─ target == item/null → T2ACraftSystem.ShowMenu(from, system, tool, preTarget) - ├─ resource selected/targeted (per skill) - ├─ build filtered menu; empty → "You lack the skill and materials…" - └─ ItemListMenu sent (0x7C) → player picks → 0x7D → OnResponse - ├─ category → open submenu - └─ leaf → CraftItem.Craft(...) -``` - -Tool-less skills (Inscription, Cartography) enter `ShowMenu` from their **skill handler** (`Skills/Inscribe.cs`, `Skills/Cartography.cs`) instead of a tool double-click — see [Tool-less skills](#tool-less-skills). - -### How a selection maps back to a craftable - -`ItemListEntry` carries a `CraftIndex` (a 4th constructor arg added for this feature) — an index into the menu's parallel `Type[]`. When the `0x7D` response arrives, `OnResponse(state, index)` uses the entry's `CraftIndex` to resolve the chosen category or `CraftItem` type. Menus build their entries through `T2ACraftSystem.FilterEntries(from, staticEntries, types, system, selectedResourceType)` so only craftable rows appear. - -## Key mechanics - -### Resource pre-selection -Tool skills select the working resource **before** the menu opens. `T2ACraftToolTarget` passes whatever the player targeted as `preTarget`; `T2ACraftSystem.ShowMenu` validates it per skill (e.g. ingots for smithing, cloth/leather for tailoring, wood for carpentry/fletching, blank map for cartography, blank scroll/reagent/rune for inscription) and otherwise prompts for a valid resource. The selected sub-resource index is stored via `T2ACraftSystem.SetLastResourceIndex` (`context.LastResourceIndex` / `LastResourceIndex2`) for make-last. - -### Make-last (QoL — see deviations) -`T2ACraftToolTarget`: targeting the tool repeats `context.LastMade` with the remembered resource (and hue). Jewelry re-prompts for a gem target (you cannot silently re-consume gems). **Not historically part of T2A packet menus** ("Make Last" was a Publish 14 gump feature) — kept as a quality-of-life convenience. - -### Hue-aware tailoring -Targeting hued cloth/leather makes the craft consume **only matching-hue** material for the primary resource. Implemented by the `CraftItem.Craft(..., resHue)` overload and `CheckHuedRes`/`ConsumeHuedRes`/`GetHuedAmount`/`ConsumeHuedAmount`; the hue rides the `InternalTimer` (`m_ResHue`) into the hue-aware `CompleteCraft` overload, which sets `context.LastHue`. Secondary resources (e.g. ingots in mixed items) are consumed normally. This affects **consumption only**. - -### How crafted items get their color -A crafted item's color comes from its **`CraftResource`**, not from the consumed item's (dyed) hue: `OnCraft` sets `Resource = CraftResources.GetFromType(resourceType)` and armor/clothing then take `Hue = CraftResources.GetHue(Resource)`. So dyeing raw leather/cloth does **not** tint the crafted piece (plain `Leather` maps to `RegularLeather`, hue 0) — **leather, cloth, and wood never produce colored items in T2A**. The only color-bearing resource in T2A is **colored ingots/ore** (→ colored metal armor and shields). Colored/special leather, hides, and scales that convey color via `CraftResource` are an **AOS+** addition and don't exist in the T2A era. - -Era gating differs by item: -- **`BaseArmor`** / **`BaseClothing`**: set `Resource` (and thus color) in **all eras** — colored-ore armor is colored even in T2A (authentic). -- **`BaseWeapon`**: sets `Resource`/color **only when `Core.AOS`** — pre-AOS weapons are uncolored (and unnamed by resource). This is intended; weapons did not retain resource color until AOS/runic. - -### Stacked-gem jewelry -Tinkered jewelry consumes ingots + a targeted gem **stack**. The player targets a stack of N gems; `TinkeringMenu.GemSelectTarget` captures `gemItem.Amount` into `context.PendingGemCount` and the gem type into `context.PendingGemType`. `BaseJewel.OnCraft` consumes the **entire** stack (`ConsumeTotal(gemItemType, PendingGemCount)`) and names the piece by count ("a 1000 diamond ring"). The count persists via `_gemCount` (`[SerializableField(7)]`, jewel serialization **v5**) and is shown in `OnSingleClickPreUOTD`. If the gems are unavailable at craft time the piece is left plain and the player is messaged. - -### Half-resources on failure (era mechanic) -`CraftItem.ConsumeRes` reduces each resource by half on a failed craft when `!Core.UOTD` (`amounts[i] -= amounts[i] / 2`). Note integer division: amount-1 resources (e.g. each inscription reagent + the single blank scroll) are fully consumed, matching the confirmed scroll-scribing rule; multi-unit resources (e.g. runebook's 8 blank scrolls) lose half. - -### Tool-less skills -`DefInscription` and `DefCartography` override `RequiresTool => !T2ACraftSystem.Enabled`, and `CanCraft` wraps tool validation in `if (RequiresTool)`. Inscription is invoked from the skill list (`Inscribe.cs` → `T2AInscribeTarget`: blank scroll opens the menu, recall rune crafts a runebook, a book enters the copy flow); cartography from `Cartography.cs`. `CraftItem` tool-null guards prevent `UsesRemaining` decrement when there is no tool. - -### Maker's mark -Under T2A the system **always prompts** for the maker's mark (no auto/never toggle): `CompleteCraft` gates on `makersMark && (T2ACraftMenus || context.MarkOption == PromptForMark)`, using the shared `QueryMakersMarkGump` (the old `QueryMakersMarkMenu` was removed). Exceptional + mark are tied to GM/near-GM skill, as in the era. - -## Gating model (toggle vs era) - -| Switch | Meaning | Governs | -|---|---|---| -| `T2ACraftSystem.Enabled` (from `t2aCraftMenus` setting, default `!Core.UOTD`) | "Use packet menus instead of gumps." | Menu routing, `ShowCraftMenu` (message vs gump), tool-less inscription/cartography, jewelry gem-targeting flow, always-prompt maker's mark, `BlankMap`/`BlankScroll` equivalence suppression. | -| `Core.UOTD` (expansion/era) | T2A↔UO:TD era boundary (`false` = T2A or earlier). | Era mechanics: half-on-failure, tinkering metal-color suppression, pre-AOS recipe availability. | - -Because the toggle's default **is** `!Core.UOTD` and there is no runtime override, the two move together by construction — a pre-UO:TD shard gets both the menus and the era mechanics, and there is no incoherent "menus on / UO:TD era" combination to guard against. An operator can still force the setting explicitly (e.g. menus on a later era) via `t2aCraftMenus`, but that is a deliberate, restart-time choice. - -## Extending: add a craftable to a T2A menu - -1. Ensure the item has a `CraftItem` in the relevant `Def*.cs` (`AddCraft(...)`), as for gump crafting — the T2A menus read the same `CraftSystem.CraftItems`. -2. Add the item's `Type` to the appropriate category `Type[]` in the skill's `*Menu.cs` and a matching static `ItemListEntry` (name + `ItemID` + `CraftIndex`). Entries are filtered at build time by `T2ACraftSystem`, so you don't repeat skill/material checks. -3. For a new **category**, add a `Category` enum value, a `GetQuestion` arm, a static entries array, and the navigation case in `OnResponse`. `BlacksmithMenu.cs` is the canonical template. -4. Jewelry: gem-bearing pieces flow through `TinkeringMenu.GemSelectTarget` and `BaseJewel.OnCraft`; ensure `BaseJewel.GetGemType`/`GetGemItemType` cover any new gem. - -## Gotchas - -- **Resource equivalence is era-gated.** `CraftItem.InitTypesTable()` only treats `BlankMap`/`BlankScroll` as interchangeable when `!T2ACraftMenus` (the gump clilocs reference both). Under T2A they are distinct, so cartography consumes blank *maps*, not scroll s. -- **Transient context fields are not serialized.** `CraftContext.PendingGemType`, `PendingGemCount`, and `LastHue` are plain properties (no `[SerializableField]`) — they exist only during a craft. -- **`BaseJewel` is at serialization v5.** Bumping it again requires `MigrateFrom(V5Content)` per the serialization rules. -- **Menu entry creation uses reflection in one spot.** `T2ACraftSystem.ShowMenuDirect` uses `Activator.CreateInstance` (once per tool double-click). Fine for now; convert to a compiled factory if it ever shows up hot. - -## Files - -- T2A UI: `Projects/UOContent/Engines/Craft/T2A/*.cs` -- Engine glue: `Projects/UOContent/Engines/Craft/Core/{CraftItem,CraftContext,CraftSystem,Enhance,Repair,Resmelt,CraftGumpItem,QueryMakersMarkGump}.cs` -- Defs: `Projects/UOContent/Engines/Craft/Def{Alchemy,Cartography,Inscription,Tailoring,Tinkering}.cs` -- Skills: `Projects/UOContent/Skills/{Inscribe,Cartography}.cs` -- Items: `Projects/UOContent/Items/Jewels/{BaseJewel,Ring}.cs` (+ `Migrations/Server.Items.BaseJewel.v5.json`) -- Tool entry: `Projects/UOContent/Items/Skill Items/Tools/BaseTool.cs` -- Toggle: `T2ACraftSystem.Enabled` (in `Engines/Craft/T2A/T2ACraftSystem.cs`), set from `Projects/UOContent/Configuration/ExpansionConfiguration.cs` via `ServerConfiguration.GetSetting("t2aCraftMenus", !Core.UOTD)` -- Engine menus (additive): `Projects/Server/Menus/{BaseMenu,ItemListMenu,QuestionMenu}.cs`; response: `Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs` -- Tests: `Projects/UOContent.Tests/Tests/Items/Jewels/T2AJewelGemCraftTests.cs` - -## Testing - -`BaseJewel.OnCraft`'s gem block is unit-testable directly (it keys off `CraftContext.PendingGem*`, not the flag): see `T2AJewelGemCraftTests.cs`. The packet-menu UX (double-click → window → target → craft) requires a running shard + T2A client and is covered by the manual checklist in the design spec (§12.1). - -## Deviations from authentic T2A (summary) - -Decided in the design spec §4; faithful to Jack's research except where shard authority overrode: -- **Make-last** — kept as QoL though it post-dates the T2A packet menus. -- **Half-on-failure** for non-scroll crafts — best-known reconstruction, not OSI-confirmed. -- **Hue-aware tailoring** — matching-hue *consumption* only (does **not** color the product); reconstruction, unverified by primary sources. -- **Stacked-gem jewelry** — the full targeted stack is consumed and named by count (shard-authoritative; overrides both the "single gem" reconstruction and Jack's deliberate "consume 1, name by stack"). -- **Cooking** — out of scope (no T2A crafting menu existed for it). - -## Related docs - -| Topic | File | -|---|---| -| Serialization | `dev-docs/serialization.md` | -| Networking & packets | `dev-docs/networking-packets.md` | -| Era & expansion handling | `dev-docs/era-expansion.md` | -| Gumps (the non-T2A path) | `dev-docs/gump-system.md` | diff --git a/dev-docs/threading-model.md b/dev-docs/threading-model.md deleted file mode 100644 index d89bc1c61..000000000 --- a/dev-docs/threading-model.md +++ /dev/null @@ -1,370 +0,0 @@ -# ModernUO Threading Model - -This document covers ModernUO's single-threaded game loop architecture, the EventLoopContext synchronization context, memory pooling, and rules for safe concurrent code. - -## Core Principle: Single-Threaded Game Logic - -All game logic in ModernUO runs on a single thread. There are no exceptions for code under `Projects/UOContent/`. - -This means: -- No locks, mutexes, or synchronization primitives needed -- No concurrent collections needed -- No volatile fields needed -- No race conditions possible in game code -- `await` is safe because continuations route through EventLoopContext - -## Game Loop - -The game loop in `Projects/Server/Main.cs` runs continuously: - -```csharp -public static void RunEventLoop() -{ - while (!Closing) - { - _tickCount = GetTimestamp(); - _now = DateTime.UtcNow; - - Mobile.ProcessDeltaQueue(); // Send mobile state changes to clients - Item.ProcessDeltaQueue(); // Send item state changes to clients - Timer.Slice(_tickCount); // Execute due timers - NetState.Slice(); // Process network I/O - LoopContext.ExecuteTasks(); // Run async continuations - Timer.CheckTimerPool(); // Refill timer pool if needed - - // World save handling - if (_performSnapshot) - { - World.Snapshot(_snapshotPath); - _performSnapshot = false; - } - } -} -``` - -Each iteration: -1. Updates timestamp -2. Sends pending mobile/item updates to clients -3. Fires due timers -4. Processes incoming network packets -5. Runs async continuations (from `await`) -6. Checks timer pool health -7. Handles world save snapshots if requested - -## EventLoopContext - -`EventLoopContext` implements `SynchronizationContext` to ensure all `await` continuations run on the game thread. - -Defined in `Projects/Server/EventLoopTasks.cs`: - -```csharp -public sealed class EventLoopContext : SynchronizationContext -{ - public enum Priority { Normal, High } - - private readonly ConcurrentQueue _queue; - private readonly ConcurrentQueue _priorityQueue; - private readonly Thread _mainThread; - private readonly int _maxPerFrame; // Default: 128 - - // Post: queues action for next ExecuteTasks() call - public void Post(Action d, Priority priority = Priority.Normal); - - // SynchronizationContext.Post: used by await - public override void Post(SendOrPostCallback d, object state); - - // Send: immediate if on main thread, blocks if on other thread - public override void Send(SendOrPostCallback d, object state); - - // Called once per game loop tick - public void ExecuteTasks(); -} -``` - -### How await Works - -```csharp -// Safe in game code: -await Timer.Pause(TimeSpan.FromMilliseconds(100)); -// After the pause, execution continues on the game thread -``` - -Flow: -1. `await` captures `EventLoopContext` as the current `SynchronizationContext` -2. When the awaited task completes, the continuation is posted to `_queue` -3. `LoopContext.ExecuteTasks()` runs the continuation on the main thread -4. Game state is safely accessible - -### Task Limits -- Maximum 128 tasks per frame by default (configurable) -- High-priority tasks (`_priorityQueue`) are always processed first -- Normal tasks are processed up to the per-frame limit - -## Forbidden Patterns - -### In Game Code (Projects/UOContent/) - -| Pattern | Problem | Alternative | -|---|---|---| -| `Task.Run(...)` | Runs on thread pool, races with game state | `Timer.StartTimer()` | -| `new Thread(...)` | Manual thread, races with game state | `Timer.StartTimer()` | -| `ThreadPool.QueueUserWorkItem(...)` | Thread pool, same issue | `Timer.StartTimer()` | -| `lock(obj) { ... }` | Unnecessary overhead, no contention | Remove lock | -| `Monitor.Enter(obj)` | Same as lock | Remove | -| `volatile int _field` | Memory barriers not needed | Plain field | -| `ConcurrentDictionary` | Lock-free overhead, unnecessary | `Dictionary` | -| `ConcurrentQueue` | Same | `Queue` or `List` | -| `ConcurrentBag` | Same | `List` | -| `Interlocked.Increment(...)` | Atomic operations unnecessary | `_field++` | -| `Mutex` / `Semaphore` | OS-level sync, unnecessary | Remove | -| `ReaderWriterLockSlim` | Lock overhead, unnecessary | Remove | -| `Thread.Sleep(ms)` | Blocks entire game loop | `await Timer.Pause(ms)` | - -### Exceptions: Server Infrastructure - -These files in `Projects/Server/` MAY use threading because they handle I/O outside the game loop: - -- `Main.cs` -- Event loop setup, thread configuration -- `World/World.cs` -- World save disk I/O (serialization on main thread, writes on background) -- `Network/` -- Network I/O processing -- `Timer/Timer.Pool.cs` -- Async pool refill -- `EventLoopTasks.cs` -- The synchronization context itself - -### Exceptions: Vetted Workers in `Projects/UOContent/` - -**Take great care here. A background thread is a last resort, not a tool of first choice.** - -The table above is about **game logic**, which is never threaded. A dedicated worker that touches -no game state is the sanctioned way to move CPU-heavy or I/O work off the loop, and it necessarily -uses primitives the table forbids -- `new Thread`, `ConcurrentQueue`, `Interlocked`, -`AutoResetEvent`, `volatile`. Those are legitimate **at the thread boundary**, and nowhere else. - -#### First: prove the need - -Do not add a worker because something "looks slow". Measure, and measure the right thing: - -- **Measure on-loop time, not wall-clock.** How long a player waits does not matter; how long the - world is frozen does. A change that improves latency but not loop time buys nothing. -- **Off-loading does not create CPU.** It converts "the loop is blocked for N ms" into "the loop - competes for cores for N ms". On a 1--2 core host there is no spare core and it buys nothing at - all -- gate on `Environment.ProcessorCount`. -- **Account for what stays behind.** Dispatch, the continuation, and the loop's own work slowing - down while the worker evicts shared L3. That last one is real and is usually the largest. -- **Write the benchmark down.** A worker with no recorded measurement cannot be re-justified later, - and will be removed by someone who cannot tell whether it earns its complexity. - -#### Game logic stays on the loop -- chunk it instead - -Work that **needs** game state cannot be threaded at any core count. If it is too slow for one -tick, split it across ticks rather than across threads: - -```csharp -// Bound the work per tick, resume where it left off. -Timer.DelayCall(TimeSpan.Zero, TimeSpan.FromMilliseconds(50), () => -{ - var budget = 0; - while (_cursor < _items.Count && budget++ < 100) - { - Process(_items[_cursor++]); - } -}); -``` - -Bound by count or elapsed time, never by "until done". Threading game state is not a faster -version of this -- it is a correctness bug. - -#### Vetted workers - -| Worker | Off-loop work | Justification | -|---|---|---| -| `Accounting/Security/PasswordWorker.cs` | Password verification and hashing | `docs/handoffs/2026-08-07-off-loop-argon2-hashing.md` -- 8.9 ms/login on-loop at Argon2, measured 3.5--8.9 ms saved | -| `Engines/Advanced Search/AdvancedSearchGump.cs` | Parallel entity search | Admin-triggered full-world scan; saves disabled for its duration | - -Adding to this table needs the same bar: a measurement, and all five rules below. - -#### The six rules - -1. **No game state off-thread, read or written.** Hand the worker immutable values (strings, - structs) captured on the loop. Carrying a reference is fine only if the worker just passes it - back untouched. -2. **Decide policy on the loop, compute on the worker.** Anything rule-dependent -- which algorithm, - which salt, which era branch -- is resolved at dispatch, so the worker holds no policy it could - apply inconsistently. -3. **Park on a kernel wait; never spin.** `AutoResetEvent.WaitOne()` costs nothing while idle. - `SerializationThreadWorker` does spin, but only to await a producer mid-drain; absent that race, - spinning is a bug that burns a core on shared hosts. -4. **Yield to world saves.** Run only while `WorldState is Running or WritingSave`. `World.Saving` - is *not* the right check -- it covers only the freeze and misses `PendingSave`, where the - serialization threads are already awake and spinning on an empty queue. -5. **Bound the queue**, or rely on a bound upstream and say which one in a comment. -6. **Everything the worker calls must itself be safe off-thread.** A process-wide singleton is not - automatically safe -- look for instance state. `HashAlgorithm.ComputeHash` carries the running - digest across `HashCore`/`HashFinal`, so two threads sharing one corrupt each other. `Utility`'s - RNG is a shared `System.Random`, which is both thread-unsafe and game state. Prefer the static - one-shot forms (`SHA256.HashData`, `RandomNumberGenerator.Fill`), and if a dependency cannot be - made safe, fix it at the source rather than narrowing the worker around it. - -#### Handing work across the boundary - -**Loop → worker (dispatch).** Snapshot everything needed into immutable values. Capture any value -you intend to overwrite later, so the continuation can tell whether it changed: - -```csharp -var job = new Job -{ - Target = state, // carried, never dereferenced off-thread - Expected = account.Password, // captured so the continuation can detect a change - Input = DerivePhrase(...) // policy resolved here, on the loop -}; - -if (!Worker.TryEnqueue(job)) -{ - // Full. Reject -- do not fall back to running it inline, or a flood steers the work - // straight back onto the loop. -} -``` - -**Worker → loop (hand back).** Two sanctioned routes, and no others: - -```csharp -// 1. Marshal the apply step. Preferred when a specific result belongs to a specific caller. -Core.LoopContext.Post(() => Apply(job, result)); - -// 2. Publish an immutable snapshot behind a single volatile reference, read lock-free by the loop. -// Preferred for a shared lookup table rebuilt periodically. -Volatile.Write(ref _snapshot, newTable); -``` - -**The continuation must re-validate.** Time passed, and the loop kept running: - -```csharp -private static void Apply(Job job, Result result) -{ - // Gone? Never revive a dead NetState or a deleted entity. - if (job.Target?.Running != true) - { - return; - } - - // Changed? Do not overwrite a newer value with one derived from an older one. - if (!string.Equals(account.Password, job.Expected, StringComparison.Ordinal)) - { - return; - } - - account.Apply(result); -} -``` - -**Always post a result, including on failure.** A worker that throws and posts nothing leaves -whatever awaited it waiting forever. Catch, log, and post a failure verdict. - -**Never** call into game state from the worker, and never `await` on the loop in a way that lets a -continuation resume heavy work there -- `ConfigureAwait(false)` on every await inside off-loop work. - -## Memory Pooling - -### STArrayPool - -Single-threaded array pool optimized for game code (no locks): - -```csharp -// Defined in Projects/Server/Buffers/STArrayPool.cs -public class STArrayPool : ArrayPool -{ - public static new STArrayPool Shared { get; } - - public override T[] Rent(int minimumLength); - public override void Return(T[]? array, bool clearArray = false); -} -``` - -Usage: -```csharp -var buffer = STArrayPool.Shared.Rent(1024); -try -{ - // Use buffer (may be larger than requested) -} -finally -{ - STArrayPool.Shared.Return(buffer); -} -``` - -Architecture: -- 27 buckets covering sizes 16 to 1GB+ -- Per-bucket cache (1 array) + stack storage (32 arrays) -- Trim callbacks on Gen2 GC to reduce memory pressure -- Formula: bucket index = `Log2(size - 1 | 15) - 3` - -**Use `STArrayPool.Shared`** in game code, **not** `ArrayPool.Shared` (which uses locks). - -### PooledRefList - -Stack-allocated list using pooled arrays: - -```csharp -// Defined in Projects/Server/Collections/PooledRefList.cs -public ref struct PooledRefList -{ - public static PooledRefList Create(int capacity = 32, bool mt = false); - public static PooledRefList CreateMT(int capacity = 32); // Multi-threaded - - public void Add(T item); - public bool Remove(T item); - public void Clear(); - public int Count { get; } - public T this[int index] { get; set; } - public void Dispose(); // Returns array to pool -} -``` - -Usage: -```csharp -using var list = PooledRefList.Create(); -list.Add(mobile); -// list is stack-allocated, zero GC pressure -// Dispose() returns backing array to STArrayPool -``` - -Key properties: -- `ref struct` -- stack-allocated, cannot escape to heap -- Uses `STArrayPool` by default, `ArrayPool.Shared` with `CreateMT()` -- Auto-grows when capacity exceeded -- Must be disposed (use `using` pattern) - -## World Save Threading - -World saves involve both threads: - -1. **`World.Save()`** -- Called on main thread, queues preserialize to thread pool -2. **`Preserialize()`** -- Thread pool: allocates serialization heaps, wakes workers -3. **`Snapshot()`** -- Main thread: serializes all game state (safe access), blocks game loop briefly -4. **`WriteFiles()`** -- Thread pool: writes serialized data to disk (no game state access) - -``` -Main Thread: Save() → ... → Snapshot() → ... → continue loop -Thread Pool: Preserialize() → ... → WriteFiles() -``` - -The main thread blocks during `Snapshot()` to ensure consistent state, then the disk I/O happens asynchronously. - -## Best Practices - -1. **Never use concurrency primitives in game code** -- they add overhead for no benefit -2. **Use `STArrayPool.Shared`** instead of `ArrayPool.Shared` -3. **Use `PooledRefList`** instead of `new List()` in hot paths -4. **Use `await Timer.Pause()`** instead of `Thread.Sleep()` -5. **Use `Timer.StartTimer()`** instead of `Task.Run()` for delayed work -6. **Trust single-threaded invariants** -- no need to protect shared state - -## Key File References - -| File | Description | -|---|---| -| `Projects/Server/Main.cs` | Game loop (RunEventLoop) | -| `Projects/Server/EventLoopTasks.cs` | EventLoopContext | -| `Projects/Server/Buffers/STArrayPool.cs` | Single-threaded array pool | -| `Projects/Server/Collections/PooledRefList.cs` | Pooled ref list | -| `Projects/Server/World/World.cs` | World save system | diff --git a/dev-docs/tick-counts.md b/dev-docs/tick-counts.md deleted file mode 100644 index f53e223d4..000000000 --- a/dev-docs/tick-counts.md +++ /dev/null @@ -1,55 +0,0 @@ -# Tick Counts: Overflow and Huge Starting Values - -Rules for any code that compares `Core.TickCount` / `Core.GetTimestamp()` values. Getting this -wrong produces bugs that only appear on specific cloud hosts after long host uptimes — the worst -kind to reproduce. - -## Why this matters (the Linux/cloud problem) - -`Core.GetTimestamp()` is built on `Stopwatch.GetTimestamp()`, which on Linux reads the kernel's -monotonic clock — and on some hypervisors, notably **Google Cloud**, the VM receives a -**pass-through of the host's never-resetting counter**. The tick count is *not* zero when the -process starts and *not* zero when the operating system booted; it is however long the physical -host has been up, which can be months or years. We have been burned by this in production. - -Consequences: - -- Raw values are enormous from the first read. Arithmetic that would "never overflow in 292 - years" of process uptime can overflow immediately (`Core.GetTimestamp()`'s `UInt128` - conversion path exists precisely because `raw * 1000` does not fit in 64 bits for large raws). -- Wrapped values can be **negative**. Nothing may assume a tick count is positive. -- **Windows is not affected** in our testing so far, which is exactly why this class of bug - ships: it works on every dev machine and fails on a customer's GCP instance. - -## The rules - -1. **Compare by subtraction, never directly.** Subtraction of two ticks wraps correctly in two's - complement; direct comparison does not. - - ```csharp - // WRONG: fails when ticks wrap or start huge - if (Core.TickCount < deadline) - - // RIGHT: wraparound-safe - if (Core.TickCount - deadline < 0) - ``` - -2. **Durations are always subtractions of two readings** (`elapsed = end - start`). Never derive - a duration from a single absolute value. - -3. **No zero or sign sentinels.** `if (_lastEventAt > 0)` as "has this happened yet" breaks when - ticks are negative. Track "has happened" with a separate `bool` or an existing counter. - -4. **Seed deadline fields from a real tick, not from field initialization.** A `long _deadline;` - left at 0 compares wrong against a huge or negative tick. Initialize relative to the first - observed timestamp (see the schedule-state seeding in `Core.Setup`). - -5. **Store deadlines as `start + interval` only if every comparison follows rule 1.** The - addition may wrap; the subtraction comparison handles it. - -## Reviewing for it - -Grep the diff for `TickCount <`, `TickCount >`, `GetTimestamp() <`, and comparisons against any -field whose name suggests a deadline (`*Until`, `*At`, `*Next*`). Each hit must be in subtraction -form. `DateTime`/`DateTimeOffset` comparisons are unaffected; this applies only to the monotonic -tick domain. diff --git a/dev-docs/timers.md b/dev-docs/timers.md deleted file mode 100644 index 2c051cca6..000000000 --- a/dev-docs/timers.md +++ /dev/null @@ -1,291 +0,0 @@ -# ModernUO Timer System - -This document covers ModernUO's timer system: the timer wheel scheduler, delay calls, timer execution tokens, and patterns for timer usage in game content. - -## Overview - -ModernUO uses a 3-layer hierarchical timer wheel for scheduling delayed and recurring actions. The system is single-threaded and processes timers during each game loop tick. - -> **Timer vs EventScheduler**: The timer wheel is for **game-tick** delays and repeats (8ms precision, sub-second to ~16 days). For **wall-clock/calendar** scheduling — daily resets, weekly events, holiday seasons — use `EventScheduler` instead (1-second granularity, timezone-aware, calendar recurrence patterns). See `dev-docs/event-scheduler.md`. - -## Architecture - -### Timer Wheel -3-layer wheel with 4096 slots per layer: - -| Layer | Resolution | Range | -|---|---|---| -| 0 | 8ms | ~32.8 seconds | -| 1 | ~32.8s | ~22 minutes | -| 2 | ~22m | ~16 days | - -- Tick rate: 8ms (minimum precision) -- All delays are rounded up to nearest 8ms boundary -- O(1) insert and remove operations - -### Execution Flow -1. `Timer.Slice(tickCount)` called each game loop iteration -2. Wheel rotates to current slot -3. All timers in the slot are executed via `OnTick()` -4. Repeating timers are re-inserted at next interval -5. Finished timers call `OnDetach()` for cleanup - -## API Reference - -### Timer.StartTimer (Fire-and-Forget with Pooling) - -Preferred for most use cases. Timers are automatically pooled for reuse. - -```csharp -// Immediate execution -Timer.StartTimer(callback); - -// Delayed execution -Timer.StartTimer(TimeSpan.FromSeconds(5), callback); - -// Repeating -Timer.StartTimer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), callback); - -// Repeating with count limit -Timer.StartTimer(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), 10, callback); - -// Delayed start, then repeating -Timer.StartTimer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(1), callback); -``` - -### Timer.StartTimer with Token (Cancellable) - -Use when you need to cancel the timer later. - -```csharp -private TimerExecutionToken _token; - -// Start with token -Timer.StartTimer(TimeSpan.FromSeconds(5), DoWork, out _token); - -// Check state -if (_token.Running) { } -var remaining = _token.RemainingCount; // int.MaxValue if infinite -var next = _token.Next; // DateTime of next tick -var index = _token.Index; // Times ticked so far - -// Cancel (safe to call multiple times) -_token.Cancel(); -``` - -### Timer.DelayCall (Returns Timer Object) - -Legacy-style API that returns the timer object directly. - -```csharp -var timer = Timer.DelayCall(TimeSpan.FromSeconds(5), DoWork); -timer.Stop(); // Cancel - -// With state parameters (avoids lambda allocation) -Timer.DelayCall(TimeSpan.FromSeconds(2), ProcessTarget, mobile, item); -Timer.DelayCall(TimeSpan.FromSeconds(1), DoWork, arg1, arg2, arg3); -// Supports up to 5 state parameters -``` - -### Timer.Pause (Awaitable) - -For async/await patterns: - -```csharp -await Timer.Pause(TimeSpan.FromMilliseconds(100)); -await Timer.Pause(500); // Milliseconds overload -``` - -Safe because `EventLoopContext` routes continuations to the main thread. - -## TimerExecutionToken - -Lightweight struct for tracking fire-and-forget timers: - -```csharp -public struct TimerExecutionToken -{ - public bool Running { get; } // Is timer still active? - public int Index { get; } // How many times OnTick fired - public int RemainingCount { get; } // Ticks remaining (int.MaxValue if infinite) - public DateTime Next { get; } // When next tick fires - public void Cancel(); // Stop and return to pool -} -``` - -Key behaviors: -- `Cancel()` is safe to call multiple times -- Default value (`default(TimerExecutionToken)`) has `Running = false` -- NOT serializable -- restore in `[AfterDeserialization]` - -## Patterns - -### Pattern 1: Simple Delayed Action -```csharp -// Delete item after 10 seconds -Timer.StartTimer(TimeSpan.FromSeconds(10), Delete); -``` - -### Pattern 2: Cancellable Recurring Timer -```csharp -private TimerExecutionToken _checkTimer; - -public MyItem() : base(0x1234) -{ - Timer.StartTimer( - TimeSpan.FromSeconds(5), // Initial delay - TimeSpan.FromSeconds(5), // Repeat interval - CheckExpiry, // Callback - out _checkTimer // Token for cancellation - ); -} - -public override void OnAfterDelete() -{ - _checkTimer.Cancel(); - base.OnAfterDelete(); -} - -private void CheckExpiry() -{ - if (Core.Now >= _expireTime) - Delete(); -} -``` - -### Pattern 3: Timer Restoration After Deserialization -```csharp -[SerializationGenerator(0)] -public partial class TimedItem : Item -{ - private TimerExecutionToken _timer; // NOT serialized - - [SerializableField(0)] - [DeltaDateTime] - private DateTime _expireTime; - - [Constructible] - public TimedItem() : base(0x1234) - { - _expireTime = Core.Now + TimeSpan.FromHours(1); - StartTimer(); - } - - private void StartTimer() - { - Timer.StartTimer(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), Check, out _timer); - } - - [AfterDeserialization] - private void AfterDeserialization() => StartTimer(); - - public override void OnAfterDelete() - { - _timer.Cancel(); - base.OnAfterDelete(); - } -} -``` - -### Pattern 4: Serializable Timer Field -Every serializable `Timer` member declares `[DeserializeTimer(nameof(Method))]` on the -field. By default the next tick is stored as anchored time (downtime does not consume the -remaining delay); pass `wallClock: true` for absolute deadlines. The method runs **only when -a timer was running at save**, with the remaining delay. - -```csharp -[SerializableField(0, setter: "private")] -[DeserializeTimer(nameof(DeserializeDecayTimer))] -private Timer _decayTimer; - -private void DeserializeDecayTimer(TimeSpan delay) -{ - _decayTimer = Timer.DelayCall(delay, Delete); -} - -public void BeginDecay(TimeSpan delay) -{ - _decayTimer?.Stop(); - _decayTimer = new InternalTimer(this, delay); - _decayTimer.Start(); -} - -public override void OnAfterDelete() -{ - _decayTimer?.Stop(); - _decayTimer = null; - base.OnAfterDelete(); -} -``` - -### Pattern 5: Custom Timer Class -```csharp -private class DecayTimer : Timer -{ - private readonly Corpse _corpse; - - public DecayTimer(Corpse c, TimeSpan delay) : base(delay) - { - _corpse = c; - } - - protected override void OnTick() - { - if (!_corpse.GetFlag(CorpseFlag.NoBones)) - _corpse.TurnToBones(); - else - _corpse.Delete(); - } -} -``` - -### Pattern 6: State-Carrying Delay (No Lambda) -```csharp -// Instead of lambda (allocates closure): -Timer.StartTimer(TimeSpan.FromSeconds(2), () => ProcessTarget(from, target)); - -// Use state parameters (no allocation): -Timer.DelayCall(TimeSpan.FromSeconds(2), ProcessTarget, from, target); - -private static void ProcessTarget(Mobile from, Mobile target) -{ - // Process... -} -``` - -## Timer Pool - -Timers created via `Timer.StartTimer()` are pooled for reuse: -- Initial pool: 1024 timers (configurable: `timer.initialPoolCapacity`) -- Max pool: 16x initial (configurable: `timer.maxPoolCapacity`) -- Pool refills asynchronously when depleted -- `Timer.CheckTimerPool()` called each game loop to monitor - -## ISerializableExtensions for Timers - -```csharp -// Extension methods on ISerializable: -entity.Stop(timer); // Stop + MarkDirty -entity.Start(timer); // Start + MarkDirty -entity.Restart(timer, delay, interval); // Stop + reconfigure + Start + MarkDirty -entity.Stop(ref timer); // Stop + null + MarkDirty -``` - -## Common Mistakes - -| Mistake | Problem | Fix | -|---|---|---| -| Serializing `TimerExecutionToken` | Build error / data corruption | Leave unserialized, use `[AfterDeserialization]` | -| Not cancelling on delete | Timer fires on deleted entity | Cancel in `OnDelete()`/`OnAfterDelete()` | -| Using `Thread.Sleep` | Blocks game loop | Use `await Timer.Pause()` | -| Creating timer in deserialization | Timer starts before world is ready | Use `[AfterDeserialization]` | -| Lambda in hot-path timer | Allocates closure every time | Use state parameters | - -## Key File References -- Timer base class: `Projects/Server/Timer/Timer.cs` -- DelayCall + StartTimer: `Projects/Server/Timer/Timer.DelayCall.cs` -- Timer wheel: `Projects/Server/Timer/Timer.TimerWheel.cs` -- Pool management: `Projects/Server/Timer/Timer.Pool.cs` -- State timers: `Projects/Server/Timer/Timer.DelayStateCall.cs` -- Token: `Projects/Server/Timer/TimerExecutionToken.cs` -- Serializable extensions: `Projects/Server/Serialization/ISerializableExtensions.cs` diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 000000000..9716c910a --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +modernuo.com diff --git a/docs/assets/stylesheets/extra.css b/docs/assets/stylesheets/extra.css new file mode 100644 index 000000000..dfe2e4d38 --- /dev/null +++ b/docs/assets/stylesheets/extra.css @@ -0,0 +1,112 @@ +:root { + --muo-gold: #d5bf74; + --muo-gold-compl: #748ad5; + --muo-gold-analog: #bf74d5; + --muo-green: #77d574; + --muo-gold-a-link: #947a40; + + /* default */ + --md-default-fg-color: #212121; + --md-default-bg-color: #ffffff; + + /* primary */ + --md-primary-fg-color: #212121; + --md-primary-bg-color: #ffffff; + --md-primary-fg-color--dark: #212121; + + /* accent */ + --md-accent-fg-color: var(--muo-gold); +} + +:root > * { + --md-code-bg-color: #212121; + --md-code-fg-color: rgba(255, 255, 255, 0.7); + + --md-typeset-a-color: var(--muo-gold-a-link); + + --md-footer-fg-color: #ffffff; + --md-footer-bg-color: #212121; + --md-footer-bg-color--dark: #212121; + + --md-code-hl-color: var(--muo-gold); + + + --md-code-hl-number-color: var(--muo-gold-analog); + --md-code-hl-special-color: var(--md-default-bg-color--lighter); + --md-code-hl-function-color: var(--muo-gold-compl); + --md-code-hl-constant-color: rgba(255, 255, 255, 0.7); + --md-code-hl-keyword-color: var(--muo-gold); + --md-code-hl-string-color: var(--muo-green); + --md-code-hl-name-color: rgba(255, 255, 255, 0.7); + + --md-code-hl-operator-color: var(--muo-gold-a-link); + --md-code-hl-punctuation-color: var(--muo-gold-a-link); + --md-code-hl-comment-color: var(--md-default-bg-color--lighter); + --md-code-hl-generic-color: var(--muo-gold-compl); + --md-code-hl-variable-color: var(--muo-gold-compl); +} + +.md-tabs__link--active, +.md-tabs__link:focus, +.md-tabs__link:hover, +.md-footer-nav__link:focus, +.md-footer-nav__link:hover, +.md-header-nav__button:focus, +.md-header-nav__button:hover, +.md-source:hover, +html .md-footer-meta.md-typeset a:focus, +html .md-footer-meta.md-typeset a:hover { + color: var(--muo-gold); + opacity: 1; +} + +@media screen and (max-width: 76.1875em) { + .md-nav--primary .md-nav__title[for=__drawer] .md-nav__button { + left: 3.75rem; + } + + .md-nav--primary .md-nav__title { + padding: 3rem 3.75rem 0.2rem + } +} + +.windows { + color: #00adef; +} + +.apple { + color: #000000; +} + +.ubuntu { + color: #e95420; +} + +.linuxmint { + color: #87cF3e; +} + +.debian { + color: #d70a53; +} + +.fedora { + color: #3c6eb4; +} + +.centos { + color: #212078; +} + +.redhat { + color: #be0000; +} + +.codehilitetable .linenodiv pre, .highlighttable .linenodiv pre { + color: var(--md-default-bg-color); +} + +.codehilitetable .linenodiv, .highlighttable .linenodiv { + box-shadow: -0.05rem 0 var(--md-default-bg-color) inset; + opacity: 0.7; +} diff --git a/docs/assets/stylesheets/home_overrides.css b/docs/assets/stylesheets/home_overrides.css new file mode 100644 index 000000000..713c49eef --- /dev/null +++ b/docs/assets/stylesheets/home_overrides.css @@ -0,0 +1,101 @@ +/* Remove spacing, as we cannot hide it completely */ +.md-main__inner { + margin: 0; +} + +/* Hide main content for now */ +.md-content { + display: none; +} + +/* Hide table of contents */ +@media screen and (min-width: 60em) { + .md-sidebar--secondary { + display: none; + } +} + +/* Hide navigation */ +@media screen and (min-width: 76.25em) { + .md-sidebar--primary { + display: none; + } +} + +.md-main { + background: var(--md-default-fg-color); +} + +.md-typeset h1 { + color: var(--md-default-bg-color--light); +} + +.tx-container { + background: var(--md-default-fg-color); + padding-top: 1rem; +} + +.tx-hero { + display: flex; + align-content: center; + justify-content: center; + flex-direction: column; + min-height: 400px; + margin: 0 0.8rem; +} + +.tx-hero div { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.tx-hero__image { + text-align: center; +} + +.tx-hero__content { + text-align: center; +} + +.tx-hero__content .md-button { + background-color: transparent; + color: var(--md-primary-bg-color--light); + border-color: var(--md-primary-bg-color--light); +} + +.tx-hero__content .md-button--primary { + background-color: var(--muo-gold); + color: var(--md-primary-fg-color); + border-color: transparent; +} + +.tx-hero__content .md-button:hover { + border-color: transparent; + color: var(--md-primary-fg-color); + background-color: var(--md-primary-bg-color); +} + +.tx-hero__content .md-button--primary:hover { + border-color: var(--md-primary-bg-color--light); +} + +/*@media screen and (max-width: 60em)*/ +/*{*/ +/* .tx-hero__image img {*/ +/* max-width: 64px;*/ +/* }*/ +/*}*/ + +@media screen and (max-width: 60em) +{ + .tx-hero__image img { + max-width: 128px; + } +} + +@media screen and (min-width: 60em) +{ + .tx-hero__image img { + max-width: 200px; + } +} diff --git a/docs/branding/android-chrome-192x192.png b/docs/branding/android-chrome-192x192.png new file mode 100644 index 000000000..177604a9e Binary files /dev/null and b/docs/branding/android-chrome-192x192.png differ diff --git a/docs/branding/android-chrome-512x512.png b/docs/branding/android-chrome-512x512.png new file mode 100644 index 000000000..0fda32a43 Binary files /dev/null and b/docs/branding/android-chrome-512x512.png differ diff --git a/docs/branding/apple-touch-icon.png b/docs/branding/apple-touch-icon.png new file mode 100644 index 000000000..c6ce40fcd Binary files /dev/null and b/docs/branding/apple-touch-icon.png differ diff --git a/docs/branding/favicon-16x16.png b/docs/branding/favicon-16x16.png new file mode 100644 index 000000000..45b6741aa Binary files /dev/null and b/docs/branding/favicon-16x16.png differ diff --git a/docs/branding/favicon-32x32.png b/docs/branding/favicon-32x32.png new file mode 100644 index 000000000..023b48d9a Binary files /dev/null and b/docs/branding/favicon-32x32.png differ diff --git a/docs/branding/favicon.ico b/docs/branding/favicon.ico new file mode 100644 index 000000000..10c47ee1c Binary files /dev/null and b/docs/branding/favicon.ico differ diff --git a/docs/branding/favicon.png b/docs/branding/favicon.png new file mode 100644 index 000000000..023b48d9a Binary files /dev/null and b/docs/branding/favicon.png differ diff --git a/docs/branding/logo.svg b/docs/branding/logo.svg new file mode 100644 index 000000000..5583999ad --- /dev/null +++ b/docs/branding/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/branding/mstile-144x144.png b/docs/branding/mstile-144x144.png new file mode 100644 index 000000000..a03597d2e Binary files /dev/null and b/docs/branding/mstile-144x144.png differ diff --git a/docs/branding/mstile-150x150.png b/docs/branding/mstile-150x150.png new file mode 100644 index 000000000..f5ccc900e Binary files /dev/null and b/docs/branding/mstile-150x150.png differ diff --git a/docs/branding/mstile-310x150.png b/docs/branding/mstile-310x150.png new file mode 100644 index 000000000..6196557f9 Binary files /dev/null and b/docs/branding/mstile-310x150.png differ diff --git a/docs/branding/mstile-310x310.png b/docs/branding/mstile-310x310.png new file mode 100644 index 000000000..fcab2046c Binary files /dev/null and b/docs/branding/mstile-310x310.png differ diff --git a/docs/branding/mstile-70x70.png b/docs/branding/mstile-70x70.png new file mode 100644 index 000000000..2416cf18d Binary files /dev/null and b/docs/branding/mstile-70x70.png differ diff --git a/docs/branding/safari-pinned-tab.svg b/docs/branding/safari-pinned-tab.svg new file mode 100644 index 000000000..55d1c1fc7 --- /dev/null +++ b/docs/branding/safari-pinned-tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/building-server.md b/docs/building-server.md new file mode 100644 index 000000000..bd793c978 --- /dev/null +++ b/docs/building-server.md @@ -0,0 +1,33 @@ +--- +title: Creating a build +--- + +# Creating a build + +The server software must be built before it can be run. + +=== "Windows" + Using _windows terminal_, _git bash_, or _powershell_, run: + ```bash + ./publish.cmd + ``` + +=== "OSX/Linux" + Using _terminal_, run: + ```bash + ./publish.sh + ``` +

+`os` (_Optional_)
+The operating system to build the server against. If not specified then the server will be built for the same operating system. + +:fontawesome-brands-windows:{: .windows } `win`
+:fontawesome-brands-apple:{: .apple } `osx`
+:fontawesome-brands-linux:{: .linux } `linux`
+ +

+`arch` (_Optional_)
+The architecture to build the server against. If not specified then the server will be built for x64. + +`x64`
+`arm64`
diff --git a/docs/commands/commands.7z b/docs/commands/commands.7z new file mode 100644 index 000000000..d0d6c0e9b Binary files /dev/null and b/docs/commands/commands.7z differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..487ccfec7 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,4 @@ +--- +title: Home +template: home.html +--- diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 000000000..ff6d90c8b --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,54 @@ +--- +title: Installation +--- + +# Installation + +=== "Windows" + ### Prerequisites + 1. Download and install the latest [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) + 1. Download and install from [here](https://git-scm.com/download/win) + + !!! Tip + Use [Windows Terminal](https://learn.microsoft.com/en-us/windows/terminal/install) as your command prompt. + + ### Install ModernUO + 1. Navigate to the folder where you want to install ModernUO. + 1. Using _Windows Terminal_ run: + ```bash + git clone https://github.com/modernuo/modernuo + cd modernuo + ``` + +=== "OSX" + ### Prerequisites + 1. Download and install the latest [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0). + 1. Using _terminal_, install [homebrew](https://brew.sh) and git: + ```bash + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" + brew install git + ``` + ### Install ModernUO + 1. Using _terminal_, navigate to the folder where you want to install ModernUO and run: + ```bash + git clone https://github.com/modernuo/modernuo + cd modernuo + ``` + +=== "Linux" + ### Prerequisites + 1. Download and install the latest [.NET 8 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux). + 1. Using _bash_, install git: + ```bash + sudo apt update && sudo apt install git + ``` + + !!! Note + The command to install git might be different for your flavor of linux. Consult your local Google search for answers. + + ### Install ModernUO + 1. Using _bash_, navigate to the folder where you want to install ModernUO and run: + ```bash + git clone https://github.com/modernuo/modernuo + cd modernuo + ``` diff --git a/docs/overrides/.icons/modernuo/logo.svg b/docs/overrides/.icons/modernuo/logo.svg new file mode 100644 index 000000000..5583999ad --- /dev/null +++ b/docs/overrides/.icons/modernuo/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/overrides/home.html b/docs/overrides/home.html new file mode 100644 index 000000000..64153b7bf --- /dev/null +++ b/docs/overrides/home.html @@ -0,0 +1,55 @@ +{% extends "main.html" %} +{% block extrahead %} + + +{% endblock %} + + +{% block tabs %} +{{ super() }} + + +
+
+
+
+ +
+ + +
+

Ultima Online Server Emulator for the modern era!

+ + Get started + + + Go to GitHub + +
+
+
+
+{% endblock %} + + +{% block content %}{% endblock %} + + +{% block footer %} +{% include "partials/footer.html" %} +{% endblock %} diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 000000000..e49ce3a10 --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,43 @@ +{% extends "base.html" %} + + +{% block extrahead %} + + +{% set title = config.site_name %} +{% if page and page.title and not page.is_homepage %} +{% set title = config.site_name ~ " - " ~ page.title | striptags %} +{% endif %} + + +{% set image = config.site_url ~ '/branding/android-chrome-512x512.png' %} + + + + + + + + + + + + + + + + + + + +{% endblock %} + + +{% block content %} +{{ super() }} + + +
+ +
+{% endblock %} diff --git a/docs/overrides/partials/footer.html b/docs/overrides/partials/footer.html new file mode 100644 index 000000000..d160b2f2c --- /dev/null +++ b/docs/overrides/partials/footer.html @@ -0,0 +1,52 @@ +{#- +This file was automatically generated - do not edit +-#} +{% import "partials/language.html" as lang with context %} + diff --git a/docs/overrides/partials/nav.html b/docs/overrides/partials/nav.html new file mode 100644 index 000000000..8924593d6 --- /dev/null +++ b/docs/overrides/partials/nav.html @@ -0,0 +1,69 @@ + + + + diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 000000000..fcc0f5972 --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,10 @@ +# ModernUO Website + +## Development + +1. Install Python's `pip` package manager +1. Install MkDocs: `pip install mkdocs` +1. Install Required Extensions + * `pip install pymdown-extensions` + * `pip install mkdocs-material` +1. Launch development server: `mkdocs` diff --git a/docs/scripting-guide/serialization.md b/docs/scripting-guide/serialization.md new file mode 100644 index 000000000..ccca4ae62 --- /dev/null +++ b/docs/scripting-guide/serialization.md @@ -0,0 +1,198 @@ +--- +title: Serialization +--- + +# Serialization / Savings + +=== "Generic persistence" + ```Persistence.Serialize``` and ```Persistence.Deserialize``` is replaced with GenericPersistence class + + Example of how to persist a custom system. + + ```cs + namespace Server.ExampleSystem + { + public static class ExampleSerialization + { + public static void Configure() + { + GenericPersistence.Register("ExampleSystem", Serialize, Deserialize); + } + + public static void Serialize(IGenericWriter writer) + { + // Do serialization here + writer.WriteEncodedInt(0); // version + } + + public static void Deserialize(IGenericReader reader) + { + // Do deserialization here + var version = reader.ReadEncodedInt(); + } + } + } + ``` + +=== "Codegen" + ### Basic info + ModernUO can programatically generate migrations. This feature is based on internal C# Source generators [More info](https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/) + + Old way of serializing objects: + ```cs + public class ExampleItem : Item + { + private string _exampleText; + + [CommandProperty(AccessLevel.GameMaster)] + public string ExampleText + { + get => _exampleText; + set + { + if (value != _exampleText) + { + _exampleText = value; + this.MarkDirty(); + } + } + } + + [Constructible] + public ExampleItem(string text) : base(0) + { + Example = text; + } + + public ExampleItem(Serial serial) : base(serial) + { + } + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + + writer.WriteEncodedInt(0); //Version + writer.Write(_exampleText); + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + + var version = reader.ReadEncodedInt(); + + // version 0 + _exampleText = reader.ReadString(); + } + } + ``` + + Same class serialized with codegen + ```cs + [SerializationGenerator(0)] + public partial class ExampleItem : Item + { + [SerializableField(0)] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private string _exampleText; + + [Constructible] + public ExampleItem(string text) : base(0) + { + _exampleText = text; + } + } + ``` + + ### Step by step + 1. Add ```SerializationGenerator(versionNumber)``` to your class and make the class ```partial``` + ```cs + [SerializationGenerator(0)] + public partial class ExampleItem : Item + ``` + 1. Delete the constructor with ```Serial serial``` + 1. Delete the ```Serialize``` and ```Deserialize``` methods. + 1. Add ```SerializableField(fieldOrder)``` attribute to all field you want to serialize. + ```cs + [SerializableField(0)] + private string _exampleText; + ``` + 1. Run `publish.cmd`. + + ModernUO will create migration files for you. In this case "Server.Items.ExampleItem.v0.json" and "Server.Items.ExampleItem.Serialization.cs". + These files contains all information and classes needed for ModernUO to serialize/deserialize your objects. + + ### Migrations + When new field is added to the serialization, you need to increment `versionNumber` and run `publish.cmd` again to generate a migration file for the new version. + Here is little example. + + New class code will look like this: + ```cs + [SerializationGenerator(1)] + public partial class ExampleItem : Item + { + [SerializableField(0)] + private string _exampleText; + + [SerializableField(1)] + [SerializedCommandProperty(AccessLevel.GameMaster)] + private string _addedExampleTest; + + [Constructible] + public ExampleItem(string text, string addedText) : base(0) + { + _exampleText = text; + _addedExampleTest = addedText; + } + } + ``` + + Your IDE (Visual Studio, Rider, or VSCode), will show errors and the ModernUO will not compile. + This happens because the code generator builds a migration from V0 to V1 and a new struct `V0Content` with all of the V0 fields is generated. + ModernUO is expecting the developer to create a migration from the old version to the new. + Now create a `MigrateFrom` method for each of the older versions, in this case V0, to the new version. + + ```cs + private void MigrateFrom(V0Content content) + { + _exampleText = content.ExampleText; + } + ``` + + !!! Tip + Since the class is `partial`, you can create a standalone file for migrations to keep them organized. For example "ExampleItem.Migrations.cs" + + Your migration is now complete. + + ### Migrating from pre-codegen + To migrate from pre-codegen serialization, change the old Deserialize method to this: + ```cs + private void Deserialize(IGenericReader reader, int version) + ``` + + This method will be automatically called when the serialization generator doesn't have a migration for for that older version. + In this method you can make old fashioned deserializations that are mostly compatible with RunUO. + + ### After Deserialization + To execute code after the deserialization, add the `AfterDeserialization()` attribute to a method. + ```cs + [AfterDeserialization] + private void AfterDeserialization() + { + // Some code here + } + ``` + + !!! Tip + By default, `AfterDeserialization` is executed synchronously right after the actual deserialization. Passing `false` to the attribute will make it execute after all deserializations. + + ### Serializing Non-Entity Classes + Sometimes you might need to serialize a nested object that is not an `ISerializable`. The syntax is largely the same except you must also mark the *parent* object for dirty tracking by using the `DirtyTrackingEntity` attribute. + A good example of this is ["AquariumState"](https://github.com/modernuo/ModernUO/blob/main/Projects/UOContent/Items/Aquarium/AquariumState.cs). + + + !!! Note + Non-entity classes cannot be serialized by reference since that would require a reference Serial or ID. This means if the object is a serializable property on multiple objects, it will be serialized + multiple times and will effectively get duplicated on world load. Consider either making it an `ISerializable` type and building world load/save mechanisms, or do not serialize the nested object directly. + Instead opt for a global lookup, serialize with generic persistence, and then reattach to the objects using the `WorldLoad` event sink. diff --git a/docs/scripting-guide/timers.md b/docs/scripting-guide/timers.md new file mode 100644 index 000000000..443291e14 --- /dev/null +++ b/docs/scripting-guide/timers.md @@ -0,0 +1,8 @@ +--- +title: Timers +--- + +# Timers + +ModernUO completely changed the timer system to use an optimized data structure called a timer wheel. This will allow shards to add thousands of timers without slowing down the server. Traditionally the timer system used a thread and locked to add/remove/process timers. All of this is gone. +With the new timer system there is no TimerPriority. This can be deleted entirely from your scripts. diff --git a/docs/starting-server.md b/docs/starting-server.md new file mode 100644 index 000000000..e0da2a6c2 --- /dev/null +++ b/docs/starting-server.md @@ -0,0 +1,30 @@ +--- +title: Starting the server +--- + +# Starting the server + +Now that the software has been built, all you need to do is run it! +Everything is run from the _Distribution_ folder. + +=== "Windows" + Using _windows terminal_, _git bash_, or _powershell_, run: + ```bash + cd Distribution + ModernUO.exe + ``` + +=== "OSX/Linux" + Using _terminal_, run: + ```bash + cd Distribution + dotnet ModernUO.dll + ``` + +=== "Game Files" + !!! Note + Game files are required to initiate a server. Upon the initial launch, you will encounter a prompt asking for the location of your game files or ClassicUO installation. ClassicUO installations should be configured to reference game files directory within settings.json file. + + + !!! Tip + Game files are not directly distributed in ModernUO, find the latest version to download these at [client download](https://uo.com/Client-Download/) diff --git a/global.json b/global.json index fa8e68b5f..1739913f7 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.201", + "version": "9.0.101", "rollForward": "latestMajor", "allowPrerelease": false } diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..a535a64a6 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,78 @@ + +# Project +site_name: ModernUO +site_author: Kamron Batman +site_url: https://modernuo.com +edit_uri: edit/main/docs/ + +# Repository +repo_name: modernuo/modernuo +repo_url: https://github.com/modernuo/modernuo +site_description: The Ultima Online Server Emulator for the modern era! +copyright: Copyright 2019-2023 ModernUO Development Team +theme: + name: material + favicon: branding/favicon.png + icon: + logo: modernuo/logo + custom_dir: docs/overrides/ + features: + - tabs + - instant +extra_css: + - assets/stylesheets/extra.css +extra: + social: + - icon: fontawesome/brands/github + link: https://muo.gg/github + name: ModernUO on Github + - icon: fontawesome/brands/discord + link: https://muo.gg/discord + name: ModernUO on Discord + - icon: fontawesome/brands/patreon + link: https://muo.gg/patreon + name: Sponsor ModernUO on Patreon + - icon: fontawesome/brands/reddit + link: https://muo.gg/reddit + name: /r/modernuo + - icon: fontawesome/brands/twitter + link: https://muo.gg/twitter + name: "@modernuo" +markdown_extensions: + - admonition + - footnotes + - meta + - toc: + toc_depth: "1-1" + permalink: true + - attr_list + - pymdownx.highlight + - pymdownx.inlinehilite + - pymdownx.superfences + - pymdownx.snippets + - pymdownx.tabbed: + alternate_style: true + - pymdownx.caret + - pymdownx.mark + - pymdownx.tilde + - pymdownx.critic: + mode: view + - pymdownx.betterem: + smart_enable: all + - pymdownx.emoji: + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg + options: + custom_icons: + - docs/overrides/.icons + +# Pages +nav: + - Home: 'index.md' + - 'Get Started': + - 'installation.md' + - 'building-server.md' + - 'starting-server.md' + - 'Scripting guide': + - 'scripting-guide/timers.md' + - 'scripting-guide/serialization.md' diff --git a/publish.cmd b/publish.cmd index a15991ab0..f8e490426 100755 --- a/publish.cmd +++ b/publish.cmd @@ -1,9 +1,46 @@ :<<"::SHELLSCRIPT" @ECHO OFF -powershell -ExecutionPolicy Bypass -NoProfile -File "%~dp0publish.ps1" %* -exit /b %ERRORLEVEL% +GOTO :CMDSCRIPT ::SHELLSCRIPT -# If run from bash (e.g., CI on Linux/macOS), dispatch to publish.sh path=$(dirname "$0") -exec "$path/publish.sh" "$@" +cd $path +./publish.sh +exit $? + +:CMDSCRIPT +IF "%~1" == "" ( + SET config=-c Release +) ELSE ( + IF "%~1" == "release" ( + SET config=-c Release + ) ELSE ( + SET config=-c Debug + ) +) + +IF "%~2" == "" ( + SET os=-r win +) ELSE ( + SET os=-r %~2 +) + +IF "%~3" == "" ( + SET arch=x64 +) ELSE ( + SET arch=%~3 +) + +echo dotnet tool restore +dotnet tool restore + +echo dotnet clean --verbosity quiet +dotnet clean --verbosity quiet +echo dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json +dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json + +echo dotnet publish %config% %os%-%arch% --no-restore --self-contained=false +dotnet publish %config% %os%-%arch% --no-restore --self-contained=false + +echo Generating serialization migration schema... +dotnet tool run ModernUOSchemaGenerator -- ModernUO.sln diff --git a/publish.ps1 b/publish.ps1 deleted file mode 100644 index ef2a400ee..000000000 --- a/publish.ps1 +++ /dev/null @@ -1,114 +0,0 @@ -$ErrorActionPreference = 'Stop' -$repoRoot = Split-Path -Parent $MyInvocation.MyCommand.Path - -$toolBinary = Join-Path (Join-Path $repoRoot 'tools') 'build-tool.exe' -$stampFile = Join-Path (Join-Path $repoRoot 'tools') '.build-tool-commit' -$buildToolProject = Join-Path (Join-Path (Join-Path $repoRoot 'Projects') 'BuildTool') 'BuildTool.csproj' - -function Get-BuildToolCommit { - try { - $hash = & git -C $repoRoot log -1 --format=%H -- Projects/BuildTool/ 2>$null - if ($LASTEXITCODE -eq 0 -and $hash) { return $hash.Trim() } - } - catch { } - return $null -} - -function Get-BuildToolFromRelease { - try { - # Determine platform - $arch = if ([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture -eq 'Arm64') { 'arm64' } else { 'x64' } - $assetName = "build-tool-win-$arch.exe" - - Write-Host "Downloading build tool..." -ForegroundColor Blue - - $releaseUrl = 'https://api.github.com/repos/modernuo/ModernUO/releases/tags/build-tool-latest' - $headers = @{ 'User-Agent' = 'ModernUO-BuildTool' } - $release = Invoke-RestMethod -Uri $releaseUrl -Headers $headers -TimeoutSec 10 - - $asset = $release.assets | Where-Object { $_.name -eq $assetName } | Select-Object -First 1 - if (-not $asset) { - return $false - } - - $toolsDir = Join-Path $repoRoot 'tools' - if (-not (Test-Path $toolsDir)) { - New-Item -ItemType Directory -Path $toolsDir -Force | Out-Null - } - - Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $toolBinary -TimeoutSec 60 - - $commitHash = Get-BuildToolCommit - if ($null -ne $commitHash) { - Set-Content -Path $stampFile -Value $commitHash -NoNewline - } - - return $true - } - catch { - return $false - } -} - -function Test-DotNetAvailable { - try { - $null = & dotnet --version 2>$null - return $LASTEXITCODE -eq 0 - } - catch { - return $false - } -} - -# Try native binary first (with staleness check) -if (Test-Path $toolBinary) { - $currentCommit = Get-BuildToolCommit - if ($null -ne $currentCommit -and (Test-Path $stampFile)) { - $storedCommit = (Get-Content $stampFile -Raw).Trim() - if ($currentCommit -eq $storedCommit) { - & $toolBinary @args - exit $LASTEXITCODE - } - else { - Write-Host "Build tool source has changed, updating..." -ForegroundColor Yellow - Remove-Item $toolBinary -Force - } - } - elseif ($null -eq $currentCommit) { - # Not in a git repo — use binary as-is - & $toolBinary @args - exit $LASTEXITCODE - } - else { - # Binary exists but no stamp — re-download to establish tracking - Remove-Item $toolBinary -Force - } -} - -# Try to download native binary -if (Get-BuildToolFromRelease) { - & $toolBinary @args - exit $LASTEXITCODE -} - -# Fall back to dotnet run -if (Test-DotNetAvailable) { - if (Test-Path $buildToolProject) { - Write-Host "Using dotnet run fallback..." -ForegroundColor Yellow - & dotnet run --project $buildToolProject -- @args - exit $LASTEXITCODE - } - else { - Write-Error "BuildTool project not found at: $buildToolProject" - exit 1 - } -} - -# Nothing works -Write-Host "" -Write-Host "Error: Could not run the build tool." -ForegroundColor Red -Write-Host "" -Write-Host "The .NET 10 SDK is required. Download it from:" -ForegroundColor Yellow -Write-Host " https://dotnet.microsoft.com/download/dotnet/10.0" -ForegroundColor Cyan -Write-Host "" -exit 1 diff --git a/publish.sh b/publish.sh index 30d80c4f2..8b6f76f81 100755 --- a/publish.sh +++ b/publish.sh @@ -1,121 +1,45 @@ #!/bin/bash -set -e +config=$1 +os=$2 +arch=${3:-$(uname -m)} -REPO_ROOT="$(cd "$(dirname "$0")" && pwd)" - -# Determine platform for native binary -detect_platform() { - local os arch - case "$(uname -s)" in - Darwin) os="osx" ;; - *) os="linux" ;; - esac - - case "$(uname -m)" in - aarch64|arm64) arch="arm64" ;; - *) arch="x64" ;; - esac - - echo "${os}-${arch}" -} - -PLATFORM="$(detect_platform)" -TOOL_BINARY="$REPO_ROOT/tools/build-tool" -STAMP_FILE="$REPO_ROOT/tools/.build-tool-commit" -BUILD_TOOL_PROJECT="$REPO_ROOT/Projects/BuildTool/BuildTool.csproj" - -# Get the commit hash of the latest change to BuildTool source -get_buildtool_commit() { - git -C "$REPO_ROOT" log -1 --format=%H -- Projects/BuildTool/ 2>/dev/null -} - -# Try to download native binary from GitHub Release -download_build_tool() { - local asset_name="build-tool-${PLATFORM}" - local tools_dir="$REPO_ROOT/tools" - - echo -e "\033[34mDownloading build tool...\033[0m" - - # Get latest release asset URL - local release_url="https://api.github.com/repos/modernuo/ModernUO/releases/tags/build-tool-latest" - local release_json - release_json=$(curl -fsSL --connect-timeout 10 -H "User-Agent: ModernUO-BuildTool" "$release_url" 2>/dev/null) || return 1 - - local download_url - download_url=$(echo "$release_json" | grep -o "\"browser_download_url\"[[:space:]]*:[[:space:]]*\"[^\"]*${asset_name}[^\"]*\"" | head -1 | grep -o 'https://[^"]*') || return 1 - - if [ -z "$download_url" ]; then - return 1 - fi - - mkdir -p "$tools_dir" - curl -fsSL --connect-timeout 10 -o "$TOOL_BINARY" "$download_url" || return 1 - chmod +x "$TOOL_BINARY" - - local current_commit - current_commit="$(get_buildtool_commit)" - if [ -n "$current_commit" ]; then - echo -n "$current_commit" > "$STAMP_FILE" - fi - - return 0 -} - -has_dotnet() { - command -v dotnet >/dev/null 2>&1 -} - -# CentOS/RHEL globalization workaround -if [ -f /etc/os-release ]; then - . /etc/os-release - case "$ID" in - centos|rhel) - export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 - ;; - esac +if [[ -n $os ]]; then + os="-r $os" +elif [[ $(uname) = "Darwin" ]]; then + os="-r osx" +else + os="-r linux" fi -# Try native binary first (with staleness check) -if [ -x "$TOOL_BINARY" ]; then - current_commit="$(get_buildtool_commit)" - if [ -n "$current_commit" ] && [ -f "$STAMP_FILE" ]; then - stored_commit="$(cat "$STAMP_FILE")" - if [ "$current_commit" = "$stored_commit" ]; then - exec "$TOOL_BINARY" "$@" - else - echo -e "\033[33mBuild tool source has changed, updating...\033[0m" - rm -f "$TOOL_BINARY" - fi - elif [ -z "$current_commit" ]; then - # Not in a git repo — use binary as-is - exec "$TOOL_BINARY" "$@" - else - # Binary exists but no stamp file — re-download to establish tracking - rm -f "$TOOL_BINARY" - fi +if [[ $config ]]; then + config="$(tr '[:lower:]' '[:upper:]' <<< ${1:0:1})${1:1}" + config="-c $config" +else + config="-c Release" fi -# Try to download native binary -if download_build_tool 2>/dev/null; then - exec "$TOOL_BINARY" "$@" +if [[ $arch == *'aarch'* || $arch == *'arm'* ]]; then + arch="arm64" +else + arch="x64" fi -# Fall back to dotnet run -if has_dotnet; then - if [ -f "$BUILD_TOOL_PROJECT" ]; then - echo -e "\033[33mUsing dotnet run fallback...\033[0m" - exec dotnet run --project "$BUILD_TOOL_PROJECT" -- "$@" - else - echo "Error: BuildTool project not found at: $BUILD_TOOL_PROJECT" >&2 - exit 1 - fi +if [[ $os == *'centos'* || $os == *'rhel'* ]]; then + export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 fi -# Nothing works -echo "" -echo -e "\033[31mError: Could not run the build tool.\033[0m" -echo "" -echo -e "\033[33mThe .NET 10 SDK is required. Download it from:\033[0m" -echo -e "\033[36m https://dotnet.microsoft.com/download/dotnet/10.0\033[0m" -echo "" -exit 1 +echo dotnet tool restore +dotnet tool restore + +echo dotnet clean --verbosity quiet +dotnet clean --verbosity quiet +echo dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json +dotnet restore --force-evaluate --source https://api.nuget.org/v3/index.json + +echo dotnet publish ${config} ${os}-${arch} --no-restore --self-contained=false +dotnet publish ${config} ${os}-${arch} --no-restore --self-contained=false + +echo Generating serialization migration schema... +dotnet tool run ModernUOSchemaGenerator -- ModernUO.sln + +exit $? diff --git a/tools/Export-IpBlocklist.ps1 b/tools/Export-IpBlocklist.ps1 deleted file mode 100644 index 0e9fd781d..000000000 --- a/tools/Export-IpBlocklist.ps1 +++ /dev/null @@ -1,1082 +0,0 @@ -#requires -Version 7.0 -<# -.SYNOPSIS - Downloads a small, non-overlapping set of public IP threat feeds and writes them to a single - ModernUO blocklist file -- merged, de-duplicated and bogon-filtered. - -.DESCRIPTION - This is the producer half of ModernUO's in-app blocklist gate. It fetches a deliberately THIN feed - set, merges every source into one global set, drops duplicates and reserved/bogon addresses, then - writes the result to a plain text file that the shard reads via `file` in - Configuration/blocklist.json. Nothing is installed and no credentials are needed -- the output is just - a text file, so this can run on any machine that can reach the shard's Distribution folder. - - It writes the file the shard's `BlocklistFilter` demand-pages against. IPs that actually connect are - promoted to CrowdSec / the OS firewall by the shard; the OS firewall never has to hold millions of - entries, which is exactly the scale it cannot handle on Windows. - - Inclusion principle: any category of IP used in OTHER attacks that could plausibly be turned against a - game server should be blocked -- compromised hosts, botnets, scanners, spam / DDoS-as-a-service bots, - open proxies and Tor relays. That whole surface is already covered by the anchor feed `bitwire-it`, - which is itself a 91-source aggregator (it folds in spamhaus, ipsum, firehol-level2, blocklist-de, - dshield, emergingthreats, binarydefense, cins-army, bruteforceblocker, greensnow, vxvault, ThreatFox, - StopForumSpam/sblam, Tor, open-proxy and C2 lists). So the inclusive posture lives in the base layer, - and every one of those standalone feeds is dropped as pure redundancy. Only the feeds bitwire does NOT - already carry are kept on top of it: - - bitwire-it 2h-refreshed 91-source aggregate (compromised hosts, botnets, scanners, spam - bots, Tor/open-proxy abuse relays, ThreatFox C2) -- the broad base layer. - romainmarcoux ~130k fresh attacker IPs bitwire's snapshot lags on (high-churn feed). - sentinel-turris ~800 unique honeypot probers (Turris greylist) not in bitwire. - firehol-level1 hijacked/reputation NETBLOCKS (spamhaus DROP-style) -- bogon-filtered. - - The only category deliberately held back is commercial VPN exit endpoints, which could block a legit - player -- and those are barely present here anyway (bitwire is ~5% of VPN-tunnel lists). If you ever want - to protect VPN/Tor players, pass -ExcludeAnonymizers to subtract Tor/open-proxy/VPN IPs from the output. - - ALLOWLIST - Aggregators inevitably list shared consumer address space. A CGNAT public IP fronts many subscribers, so - one abusive customer gets the address listed and every other subscriber behind it is blocked with them. - Entries in the allowlist file (-AllowlistFile) are SUBTRACTED from the merged set before it is written, so - the exemption costs nothing on the shard's accept path -- which is deliberately allowlist-free, because a - whitelist there could only ever turn a deny into an allow at the price of a lookup on every accept, the - attacker's included. Allowlisting belongs here (at generation) and at the enforcement layer - (`cscli allowlists`), never at the gate. - - Subtraction is range-correct: an allowlisted address that falls inside a blocked CIDR splits that CIDR - around the hole instead of being silently ignored, so an exemption always takes effect no matter which - shape the feed happened to publish. - - Every `ip-allowlist*.txt` beside the output is subtracted, so allowlists are split by owner rather than - kept in one file: `ip-allowlist.txt` holds the operator's own exemptions and is never rewritten, while - network carve-outs live in `ip-allowlist-.txt`. Keeping them apart means a carve-out can be - regenerated, diffed or copied to another shard without disturbing hand-written entries. - - NO CARVE-OUT IS SHIPPED. Which providers to exempt is a policy call that depends on where a shard's - players actually are, and a carve-out names a real network, so this script builds them on request rather - than publishing anyone's. A shard whose players are on CGNAT -- satellite, mobile, or an ISP short on - IPv4 -- will usually want one: - - .\Export-IpBlocklist.ps1 -AddCarveout starlink -Asn 14593 - - That costs roughly 0.1% of the list. Abusive hosts inside a carved-out network are still caught on - BEHAVIOR by the rate limiter and promoted to CrowdSec, which is the gate that actually observes them. - - OUTPUT FORMAT (must stay in sync with UOContent/Network/Blocklist/BlocklistFile.cs): - Line 1 is a header comment carrying the version markers, e.g. - # modernuo-blocklist generated=2026-07-25T18:03:11Z count=3914022 ipv4=3901188 cidr=12834 - The shard polls `reloadInterval` and reloads when the file mtime AND `generated=` change, - so the header is REQUIRED -- without it the shard loads once and never picks up a new file. - Every following line is one entry: a bare IPv4/IPv6 address or a CIDR (`1.2.3.0/24`). Blank lines - and lines starting with `#` or `;` are ignored. Order does not matter; the shard sorts and - coalesces on load. The feeds used here are IPv4-only, but the shard parses IPv6 lines too. - - The file is written to a `.tmp` sibling and swapped into place atomically, so the shard never reads a - half-written list -- it either sees the previous version or the new one, whole. - - Performance: bitwire alone is ~4M lines. Parsing/validating/bogon-filtering that in interpreted - PowerShell is the slow part (minutes), so the hot loop is compiled once via Add-Type (C#) -- it runs in - ~1s. Downloads stream with a live Write-Progress bar; every phase prints its own elapsed time so you can - see exactly where the wall-clock goes. - - Requires PowerShell 7 (pwsh), which runs on Windows, Linux and macOS -- Windows PowerShell 5.1 is - not supported and the script refuses to run there. Schedule it with Task Scheduler, cron, or a - systemd timer. - - Every run rewrites the whole file, so an IP that drops off the feeds stops being blocked on the next - run -- there is no TTL to tune. Calling it is idempotent: if the list on disk is younger than - -MinInterval the script exits without downloading anything, so an over-eager trigger costs nothing - upstream. -Force overrides that. - -.PARAMETER DistributionPath - Path to the shard's Distribution folder. The blocklist is written to the Configuration/ip-blocklist.txt - beneath it, which is the default `file` in blocklist.json. Not needed when the script is run from its - place in the repo (tools/), or when -OutFile is given. - -.PARAMETER OutFile - Explicit output path, overriding -DistributionPath. Use this if you relocated the blocklist and - changed `file` in blocklist.json to match. - -.PARAMETER MinInterval - Refuse to re-run while the existing blocklist is younger than this (default 2h), so a misbehaving - scheduler, a login script or a stuck retry loop cannot hammer the upstream feeds. The age comes from - the `generated=` header of the file already on disk (falling back to its mtime), so it survives across - machines and reboots -- there is no separate state file. Nothing is downloaded when the check trips. - Accepts `90s`, `45m`, `2h`, `2.5h`, `1d`, or a bare number of hours. Use `0` to disable the check. - Match this to how often you actually want fresh data: the anchor feed only refreshes every 2h, so - running more often than that costs bandwidth and gains nothing. - -.PARAMETER Force - Run regardless of how recently the blocklist was generated (bypasses -MinInterval). - -.PARAMETER Feeds - Which feeds to include (by Name). Default: all of them. - -.PARAMETER AllowlistFile - One or more lists of addresses that must NEVER be blocked; every entry is subtracted from the merged set - before the output is written. Same format as the blocklist: one bare IPv4 or CIDR per line, `#`/`;` - comments ignored. - - Defaults to every `ip-allowlist*.txt` beside the output, merged into one allow set: - ip-allowlist.txt operator exemptions -- created on first run, never rewritten - ip-allowlist-.txt a network carve-out -- generated data, safe to regenerate or copy - Discovered rather than configured, so a carve-out you add is picked up with no further edits. Blank a - file (keep the file) to disable its contents; delete it to drop it entirely. - - Passing this parameter replaces the defaults entirely; an explicitly-named file that does not exist is a - warning rather than a silent template write, so a typo cannot look like it worked. Pass `''` to disable - subtraction altogether. - - Editing any allowlist also bypasses -MinInterval on the next run: an exemption you just added would - otherwise sit unapplied for up to the cooldown, which reads exactly like the allowlist not working. - -.PARAMETER AddCarveout - Build a carve-out for a network and start subtracting it. Takes a short name for the file and -Asn for - the network, fetches that ASN's current routing announcements, collapses them, and writes - `ip-allowlist-.txt` beside the output. Implies -Force. - - .\Export-IpBlocklist.ps1 -AddCarveout starlink -Asn 14593 - - No carve-out ships with this script: naming a network to exempt is a policy call for the shard, so they - are built on request rather than published here. Re-running with the same name rebuilds the file. - -.PARAMETER Asn - Autonomous system number for -AddCarveout, e.g. 14593 for Starlink. Look one up by querying an address - the network hands out, or on any public BGP lookup. - -.PARAMETER RefreshCarveouts - Re-fetch every carve-out beside the output and rewrite it from current routing announcements. Implies - -Force. Carve-outs are recognised by the `asn=` marker in their header, so a hand-written allowlist is - left alone, and one whose fetch fails keeps the data it already had. - - Announcements rather than ownership records on purpose: registry data disagrees with what is actually - routed, and registry queries silently cap their result sets. - -.PARAMETER ExcludeAnonymizers - Also download Tor-exit / open-proxy / VPN-tunnel lists and SUBTRACT those IPs from the output. Off by - default -- for a game server, Tor/open-proxy relays are attack infrastructure you want to block. Turn - this on only if you need to keep VPN/Tor players reachable. - -.PARAMETER DryRun - Download + parse + merge + count only. Writes nothing. - -.EXAMPLE - .\Export-IpBlocklist.ps1 -DryRun - -.EXAMPLE - # Safe to call as often as you like -- it no-ops unless the list is older than 2h. - .\Export-IpBlocklist.ps1 -DistributionPath 'C:\Shard\Distribution' - -.EXAMPLE - .\Export-IpBlocklist.ps1 -OutFile 'D:\shared\ip-blocklist.txt' -ExcludeAnonymizers - -.EXAMPLE - # Regenerate right now, ignoring the cooldown. - .\Export-IpBlocklist.ps1 -DistributionPath 'C:\Shard\Distribution' -Force - -.EXAMPLE - # Unblock a player caught by a shared-IP listing: add the address, then regenerate. Editing the - # allowlist bypasses the cooldown, so no -Force is needed. - Add-Content 'C:\Shard\Distribution\Configuration\ip-allowlist.txt' '203.0.113.42' - .\Export-IpBlocklist.ps1 -DistributionPath 'C:\Shard\Distribution' - -.EXAMPLE - # Check what an allowlist would cost before committing to it. - .\Export-IpBlocklist.ps1 -AllowlistFile 'D:\shared\allow.txt' -DryRun - -.EXAMPLE - # Exempt a CGNAT provider whose players keep getting listed, then keep it current. - .\Export-IpBlocklist.ps1 -AddCarveout starlink -Asn 14593 - .\Export-IpBlocklist.ps1 -RefreshCarveouts - -.EXAMPLE - # Linux/macOS, e.g. from cron: - pwsh -File /opt/modernuo/Export-IpBlocklist.ps1 -DistributionPath /opt/modernuo/Distribution - -.NOTES - Feeds are aggressive-but-low-FP for a game server (attacker / botnet / compromised / abuse-relay SOURCE - IPs). Reserved/bogon space (0/8, 10/8, 127/8, RFC1918, multicast, etc.) is always filtered out -- this - matters because firehol-level1 ships bogon netblocks that would otherwise block private/reserved ranges. -#> -[CmdletBinding()] -param( - [string] $DistributionPath, - [string] $OutFile, - [string] $MinInterval = '2h', - [string[]] $AllowlistFile, - [string] $AddCarveout, - [int] $Asn, - [switch] $RefreshCarveouts, - [string[]] $Feeds, - [switch] $ExcludeAnonymizers, - [switch] $Force, - [switch] $DryRun -) - -$ErrorActionPreference = 'Stop' - -# Fail before any download rather than after 60MB of feeds. -if ($AddCarveout -and $Asn -le 0) { - throw "-AddCarveout needs -Asn, e.g. -AddCarveout starlink -Asn 14593" -} -$UA = 'ModernUO-Blocklist-Export' -$totalSw = [System.Diagnostics.Stopwatch]::StartNew() - -# Default location under the Distribution folder. Keep in sync with BlocklistSettings.File. -# Kept as separate segments (never a literal 'a\b') so Join-Path picks the right separator per OS. -$DefaultPathSegments = @('Configuration', 'ip-blocklist.txt') - -# --------------------------------------------------------------------------------------------------------- -# Resolve the output path. Explicit -OutFile wins; then -DistributionPath; then the in-repo layout -# (tools\ sits next to Distribution\) so a checkout works with no arguments at all. The script is meant to -# be copied onto the shard host, and there it needs -DistributionPath (or -OutFile). -# --------------------------------------------------------------------------------------------------------- -if (-not $OutFile) { - if (-not $DistributionPath -and $PSScriptRoot) { - $inRepo = Join-Path (Split-Path -Parent $PSScriptRoot) 'Distribution' - if (Test-Path -LiteralPath $inRepo -PathType Container) { $DistributionPath = $inRepo } - } - if (-not $DistributionPath) { - throw "Could not locate the shard's Distribution folder. Pass -DistributionPath 'C:\path\to\Distribution' (or -OutFile)." - } - if (-not (Test-Path -LiteralPath $DistributionPath -PathType Container)) { - throw "DistributionPath '$DistributionPath' does not exist." - } - $OutFile = Join-Path $DistributionPath @DefaultPathSegments -} - -# --------------------------------------------------------------------------------------------------------- -# Network carve-outs are DATA, not code: this script ships none. Which providers a shard exempts is a policy -# call that depends on where its players actually are, so the carve-outs live in files an admin creates with -# -AddCarveout, and every ip-allowlist*.txt beside the output is subtracted. -# -# A shard whose players are on CGNAT -- satellite, mobile, or an ISP short on IPv4 -- will usually want one: -# .\Export-IpBlocklist.ps1 -AddCarveout starlink -Asn 14593 -# --------------------------------------------------------------------------------------------------------- - -# --------------------------------------------------------------------------------------------------------- -# Resolve the allowlists. They sit beside the output by default so relocating the blocklist keeps the set -# together, and they are split by owner: `ip-allowlist.txt` is the operator's -- hand-edited, never rewritten -# -- while each carve-out file is generated data that can be regenerated, diffed or copied between shards -# without touching anyone's local exemptions. -# -# Carve-outs are discovered rather than listed, so a file an admin drops in is picked up with no config edit -# and no code change. An EXPLICIT -AllowlistFile replaces the whole set and is never templated: if the -# operator names a file, a missing one is a typo worth hearing about. -# --------------------------------------------------------------------------------------------------------- -$AllowGlob = 'ip-allowlist*.txt' -$ConfigDir = Split-Path -Parent $OutFile - -$allowExplicit = $PSBoundParameters.ContainsKey('AllowlistFile') - -if ($allowExplicit) { - $AllowPaths = @($AllowlistFile | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) -} -else { - $AllowPaths = @(Get-ChildItem -Path $ConfigDir -Filter $AllowGlob -File -ErrorAction SilentlyContinue | - Sort-Object Name | ForEach-Object { $_.FullName }) -} - -function Write-AllowlistFile { - param([string]$Path, [string[]]$Lines) - - $dir = Split-Path -Parent $Path - if ($dir -and -not (Test-Path -LiteralPath $dir -PathType Container)) { - New-Item -ItemType Directory -Path $dir -Force | Out-Null - } - - # Same atomic write the blocklist gets: a half-written allowlist would silently under-subtract. - $tmp = $Path + '.tmp' - [IO.File]::WriteAllLines($tmp, $Lines, [System.Text.UTF8Encoding]::new($false)) - [IO.File]::Move($tmp, $Path, $true) -} - -$OperatorTemplate = @' -# ModernUO blocklist allowlist -- every entry here is SUBTRACTED from the generated blocklist. -# -# This file is yours. Export-IpBlocklist.ps1 creates it once and never rewrites it, so anything you add -# survives every regeneration. -# -# One entry per line: a bare IPv4 address (1.2.3.4) or a CIDR (1.2.3.0/24). Lines starting with '#' or ';' -# are comments. Order does not matter. Re-run Export-IpBlocklist.ps1 to apply changes -- editing this file -# bypasses the -MinInterval cooldown, so no -Force is needed. -# -# Removal is range-correct: an address listed here is removed even when a feed published it as part of a -# larger CIDR -- that CIDR is split around the hole rather than dropped wholesale or silently ignored. -# -# Network carve-outs live in their own ip-allowlist-.txt beside this one (see -AddCarveout), so they -# can be regenerated or copied between shards without touching anything you put here. -# -# Put player/staff exemptions below, one per line, e.g.: -# 203.0.113.42 # shard owner, listed via a shared upstream address -'@ - -# Carve-out files carry their own `asn=` marker, so -RefreshCarveouts can rebuild whatever an admin created -# without this script keeping a list of anyone's networks. -function Get-CarveoutHeader { - param([string]$Name, [int]$CarveoutAsn) - - @( - ("# {0} carve-out (asn={1}) -- subtracted from the generated blocklist." -f $Name, $CarveoutAsn) - "#" - "# Reputation feeds list shared consumer address space constantly, so a hit inside a CGNAT network" - "# says little about the player currently behind it. Abusive hosts here are still caught on BEHAVIOR." - "#" - "# GENERATED DATA -- safe to regenerate, diff, or copy to another shard. Blank the file (keep the" - "# file) to reputation-block this network again; delete it to stop carving it out entirely." - "#" - "# Refresh with: .\Export-IpBlocklist.ps1 -RefreshCarveouts" - ) -} - -# Fetches a network's currently ANNOUNCED prefixes and collapses them. Routing data, not a registry: -# ownership records disagree with what is actually announced, and registry queries cap their result sets. -function Get-CarveoutPrefixes { - param([int]$CarveoutAsn) - - $url = "https://stat.ripe.net/data/announced-prefixes/data.json?resource=AS$CarveoutAsn" - $json = Get-Url -Url $url -Label ("AS{0} prefixes" -f $CarveoutAsn) | ConvertFrom-Json - - $v4 = @($json.data.prefixes.prefix | Where-Object { $_ -and $_ -notmatch ':' }) - if (-not $v4) { throw "AS$CarveoutAsn announced no IPv4 prefixes -- refusing to overwrite the carve-out." } - - [BlocklistExporter]::CollapsePrefixes(($v4 -join "`n")) -} - -# Reads the `asn=` marker back out of a carve-out file. Anything without one is a hand-written allowlist and -# is left alone by -RefreshCarveouts. -function Get-CarveoutAsn { - param([string]$Path) - - foreach ($line in (Get-Content -LiteralPath $Path -TotalCount 5 -ErrorAction SilentlyContinue)) { - if ($line -match 'asn=(\d+)') { return [int]$Matches[1] } - } - - return 0 -} - -# The operator's own list is the one file this script will create unprompted; carve-outs are opt-in. -if (-not $allowExplicit) { - $operatorPath = Join-Path $ConfigDir 'ip-allowlist.txt' - if (-not (Test-Path -LiteralPath $operatorPath -PathType Leaf)) { - Write-AllowlistFile -Path $operatorPath -Lines @($OperatorTemplate) - Write-Host ("Created allowlist at {0} (add player/staff exemptions here)." -f $operatorPath) - $AllowPaths = @($operatorPath) + $AllowPaths - } -} -else { - $AllowPaths = @($AllowPaths | ForEach-Object { - if (Test-Path -LiteralPath $_ -PathType Leaf) { return $_ } - Write-Warning ("Allowlist '{0}' does not exist -- nothing will be subtracted from it. Check the path." -f $_) - }) -} - -# --------------------------------------------------------------------------------------------------------- -# Cooldown gate. Runs BEFORE anything is downloaded: the whole point is that a misconfigured scheduler or a -# retry loop cannot spam the upstream feeds. State lives in the output file itself (`generated=` header, -# mtime as fallback), so it is correct across reboots, machines and hand-runs with no sidecar state file. -# --------------------------------------------------------------------------------------------------------- -function ConvertTo-Duration { - param([string]$Text) - if ([string]::IsNullOrWhiteSpace($Text)) { return [TimeSpan]::Zero } - $t = $Text.Trim().ToLowerInvariant() - $unit = $t[$t.Length - 1] - $numText = if ($unit -match '[0-9.]') { $t } else { $t.Substring(0, $t.Length - 1) } - $n = 0.0 - # InvariantCulture is not optional here: under a comma-decimal locale (de-DE, fr-FR, ...) the - # current-culture parse reads '2.5' as 25 -- it treats '.' as a group separator and SUCCEEDS, so - # `-MinInterval 2.5h` would silently become a 25 hour cooldown instead of failing loudly. - if (-not [double]::TryParse($numText, [Globalization.NumberStyles]::Float, - [Globalization.CultureInfo]::InvariantCulture, [ref]$n)) { - throw "Could not parse duration '$Text' (try 90s, 45m, 2h, 2.5h, 1d)." - } - switch ($unit) { - 's' { return [TimeSpan]::FromSeconds($n) } - 'm' { return [TimeSpan]::FromMinutes($n) } - 'h' { return [TimeSpan]::FromHours($n) } - 'd' { return [TimeSpan]::FromDays($n) } - default { return [TimeSpan]::FromHours($n) } # bare number == hours - } -} - -# Age of the list already on disk, or $null when there is nothing usable to age. -function Get-BlocklistAge { - param([string]$Path) - if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return $null } - - # Prefer the header we wrote: it describes the data, not the file, so copying/restoring the file - # cannot make a stale list look fresh (or a fresh one look stale). - try { - $first = Get-Content -LiteralPath $Path -TotalCount 1 -ErrorAction Stop - if ($first -and $first.StartsWith('#')) { - foreach ($tok in $first.Split(' ', [StringSplitOptions]::RemoveEmptyEntries)) { - if ($tok.StartsWith('generated=', [StringComparison]::Ordinal)) { - $stamp = [DateTime]::MinValue - $styles = [Globalization.DateTimeStyles]::AdjustToUniversal -bor [Globalization.DateTimeStyles]::AssumeUniversal - if ([DateTime]::TryParse($tok.Substring(10), [Globalization.CultureInfo]::InvariantCulture, $styles, [ref]$stamp)) { - return @{ Age = ([DateTime]::UtcNow - $stamp); Stamp = $tok.Substring(10); Source = 'header' } - } - } - } - } - } - catch { } - - # Hand-maintained or truncated file: fall back to the filesystem timestamp. - try { - $w = (Get-Item -LiteralPath $Path -ErrorAction Stop).LastWriteTimeUtc - return @{ Age = ([DateTime]::UtcNow - $w) - Stamp = $w.ToString('yyyy-MM-ddTHH:mm:ssZ', [Globalization.CultureInfo]::InvariantCulture) - Source = 'mtime' } - } - catch { return $null } -} - -$minAge = ConvertTo-Duration $MinInterval -# Asking for carve-out data implies -Force: waiting out the cooldown and leaving the old data in place would -# be the wrong answer. -if (-not $Force -and -not $RefreshCarveouts -and -not $AddCarveout -and $minAge -gt [TimeSpan]::Zero) { - $existing = Get-BlocklistAge -Path $OutFile - if ($existing) { - # A negative age means the stamp is in the future (clock skew, or a file from another host). Treat it - # as fresh: refusing to run is the recoverable failure, hammering the feeds on every tick is not. - if ($existing.Age -lt $minAge) { - # An allowlist edited since the list was built is the one case where waiting out the cooldown is - # the wrong answer: the operator is unblocking someone, and "nothing happened" is indistinguishable - # from the allowlist not working. Cheap to honour -- it can only ever shrink the output. - $changedAllow = $null - $builtAt = [DateTime]::UtcNow - $existing.Age - foreach ($p in $AllowPaths) { - try { - if ((Get-Item -LiteralPath $p -ErrorAction Stop).LastWriteTimeUtc -gt $builtAt) { - $changedAllow = $p - break - } - } - catch { } - } - - if ($changedAllow) { - Write-Host ("Allowlist {0} changed since the blocklist was built; regenerating despite -MinInterval {1}." -f ` - $changedAllow, $MinInterval) - } - else { - $agoText = if ($existing.Age -lt [TimeSpan]::Zero) { 'in the future -- check the clock' } else { ("{0:N1}h ago" -f $existing.Age.TotalHours) } - Write-Host ("Blocklist at {0} was generated {1} ({2}={3}); newer than -MinInterval {4}." -f ` - $OutFile, $agoText, $existing.Source, $existing.Stamp, $MinInterval) - Write-Host "Nothing downloaded. Pass -Force to regenerate now, or lower -MinInterval." - return - } - } - } -} - -# --------------------------------------------------------------------------------------------------------- -# Compiled hot loop. Interpreted PowerShell chokes on bitwire's ~4M lines; this parses + validates + bogon- -# filters + de-dupes in one compiled pass, and writes the final file directly (no 4M-element PS pipelines). -# Deliberately plain C#: no LINQ, no generics beyond HashSet, nothing that would slow the hot loop. -# --------------------------------------------------------------------------------------------------------- -Add-Type -TypeDefinition @' -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -public static class BlocklistExporter -{ - static bool TryParseIPv4(string s, int start, int len, out uint val) - { - val = 0; - uint acc = 0; int octet = 0, dots = 0, digits = 0, end = start + len; - for (int i = start; i < end; i++) - { - char c = s[i]; - if (c == '.') - { - if (digits == 0 || octet > 255) return false; - acc = (acc << 8) | (uint)octet; dots++; octet = 0; digits = 0; - } - else if (c >= '0' && c <= '9') - { - octet = octet * 10 + (c - '0'); if (++digits > 3) return false; - } - else return false; - } - if (dots != 3 || digits == 0 || octet > 255) return false; - val = (acc << 8) | (uint)octet; - return true; - } - - static bool IsBogon(uint start, uint end, uint[] bs, uint[] be) - { - for (int i = 0; i < bs.Length; i++) - if (start <= be[i] && end >= bs[i]) return true; - return false; - } - - // Parse one feed's text; add bare IPs to `singles`, CIDRs to `cidrs`. Returns count newly added. - public static int AddContent(string content, HashSet singles, HashSet cidrs, uint[] bs, uint[] be) - { - int added = 0, n = content.Length, i = 0; - while (i < n) - { - int eol = content.IndexOf('\n', i); - int lineEnd = (eol < 0) ? n : eol; - int a = i, b = lineEnd; - while (a < b && (content[a] == ' ' || content[a] == '\t' || content[a] == '\r')) a++; - while (b > a && (content[b - 1] == ' ' || content[b - 1] == '\t' || content[b - 1] == '\r')) b--; - i = (eol < 0) ? n : eol + 1; - if (a >= b) continue; - char first = content[a]; - if (first == '#' || first == ';') continue; - - // Feeds vary: some are bare IPs, some are CSV/whitespace records with the IP first. - int t = a; - while (t < b) - { - char c = content[t]; - if (c == ' ' || c == '\t' || c == ',' || c == ';') break; - t++; - } - int slash = -1; - for (int k = a; k < t; k++) { if (content[k] == '/') { slash = k; break; } } - - if (slash >= 0) - { - uint ip; - if (!TryParseIPv4(content, a, slash - a, out ip)) continue; - int bits = 0, bd = 0; - for (int k = slash + 1; k < t; k++) - { - char c = content[k]; - if (c < '0' || c > '9') { bd = -1; break; } - bits = bits * 10 + (c - '0'); bd++; - } - if (bd <= 0 || bits > 32) continue; - ulong size = (bits == 0) ? 0xFFFFFFFFUL : ((1UL << (32 - bits)) - 1UL); - ulong endAddr = (ulong)ip + size; if (endAddr > 0xFFFFFFFFUL) endAddr = 0xFFFFFFFFUL; - if (IsBogon(ip, (uint)endAddr, bs, be)) continue; - if (cidrs.Add(content.Substring(a, t - a))) added++; - } - else - { - uint ip; - if (!TryParseIPv4(content, a, t - a, out ip)) continue; - if (IsBogon(ip, ip, bs, be)) continue; - if (singles.Add(ip)) added++; - } - } - return added; - } - - // --------------------------------------------------------------------------------------------------- - // Allowlist subtraction. Allow entries become sorted, merged [start,end] ranges once; the blocklist is - // then filtered against them. The interesting case is a blocked CIDR that only PARTIALLY overlaps an - // allow range -- dropping it whole would unblock far more than asked, keeping it whole would ignore the - // exemption, so it is split into the surviving pieces and re-emitted as minimal CIDRs. - // --------------------------------------------------------------------------------------------------- - public sealed class RangeSet - { - public uint[] Start; - public uint[] End; - public int Count; - } - - public sealed class AllowResult - { - public int SinglesRemoved; - public int CidrsDropped; - public int CidrsSplit; - public int EntriesAdded; - } - - // Parses "a.b.c.d/p" to an inclusive range. The base is masked to the prefix, so a sloppy 1.2.3.5/24 - // means the whole 1.2.3.0/24 -- the standard reading, and the safe direction for an exemption. - static bool TryCidrRange(string c, out ulong lo, out ulong hi) - { - lo = 0; hi = 0; - int slash = c.IndexOf('/'); - if (slash <= 0) return false; - uint ip; - if (!TryParseIPv4(c, 0, slash, out ip)) return false; - int bits = 0, bd = 0; - for (int i = slash + 1; i < c.Length; i++) - { - char ch = c[i]; - if (ch < '0' || ch > '9') { bd = -1; break; } - bits = bits * 10 + (ch - '0'); bd++; - } - if (bd <= 0 || bits > 32) return false; - // 1u << 32 is undefined in C# (the shift count is masked to 5 bits), so /0 is special-cased. - uint mask = (bits == 0) ? 0u : ~((uint)((1UL << (32 - bits)) - 1UL)); - ulong size = (bits == 0) ? 0x100000000UL : (1UL << (32 - bits)); - lo = ip & mask; - hi = lo + size - 1UL; - return true; - } - - public static RangeSet BuildRanges(HashSet singles, HashSet cidrs) - { - uint[] s = new uint[singles.Count + cidrs.Count]; - uint[] e = new uint[s.Length]; - int k = 0; - - foreach (uint v in singles) { s[k] = v; e[k] = v; k++; } - foreach (string c in cidrs) - { - ulong lo, hi; - if (!TryCidrRange(c, out lo, out hi)) continue; - s[k] = (uint)lo; - e[k] = (uint)(hi > 0xFFFFFFFFUL ? 0xFFFFFFFFUL : hi); - k++; - } - - Array.Resize(ref s, k); - Array.Resize(ref e, k); - Array.Sort(s, e); - - // Coalesce overlapping AND adjacent ranges so the lookups below can assume disjoint, ordered spans. - int w = 0; - for (int i = 0; i < k; i++) - { - if (w > 0 && (ulong)s[i] <= (ulong)e[w - 1] + 1UL) - { - if (e[i] > e[w - 1]) e[w - 1] = e[i]; - } - else - { - s[w] = s[i]; e[w] = e[i]; w++; - } - } - - return new RangeSet { Start = s, End = e, Count = w }; - } - - // Index of the first range whose End >= v (ranges are disjoint and sorted, so End is sorted too). - static int FirstEndAtLeast(uint[] re, int n, uint v) - { - int lo = 0, hi = n; - while (lo < hi) - { - int mid = (int)(((uint)lo + (uint)hi) >> 1); - if (re[mid] < v) lo = mid + 1; else hi = mid; - } - return lo; - } - - static bool Covered(uint[] rs, uint[] re, int n, uint v) - { - int i = FirstEndAtLeast(re, n, v); - return i < n && rs[i] <= v; - } - - static string FormatCidr(uint ip, int bits) - { - char[] buf = new char[19]; - int p = 0; - p = WriteOctet(buf, p, (ip >> 24) & 255); buf[p++] = '.'; - p = WriteOctet(buf, p, (ip >> 16) & 255); buf[p++] = '.'; - p = WriteOctet(buf, p, (ip >> 8) & 255); buf[p++] = '.'; - p = WriteOctet(buf, p, ip & 255); buf[p++] = '/'; - p = WriteOctet(buf, p, (uint)bits); - return new string(buf, 0, p); - } - - // Writes [lo,hi] as the minimal set of aligned CIDR blocks. A /32 goes back to the singles set so the - // output keeps the file's convention of bare addresses for single hosts. - static void Emit(ulong lo, ulong hi, HashSet singles, HashSet cidrs, AllowResult r) - { - while (lo <= hi) - { - int bits = 32; - while (bits > 0) - { - ulong size = 1UL << (32 - (bits - 1)); - if ((lo % size) != 0UL) break; - if (lo + size - 1UL > hi) break; - bits--; - } - - if (bits == 32) - { - if (singles.Add((uint)lo)) r.EntriesAdded++; - } - else if (cidrs.Add(FormatCidr((uint)lo, bits))) - { - r.EntriesAdded++; - } - - lo += 1UL << (32 - bits); - } - } - - public static AllowResult ApplyAllowlist(HashSet singles, HashSet cidrs, RangeSet allow) - { - var r = new AllowResult(); - if (allow == null || allow.Count == 0) return r; - - uint[] rs = allow.Start, re = allow.End; - int n = allow.Count; - - // Singles first: the CIDR pass below can add new singles, and those are outside the allow ranges by - // construction, so re-testing them would be wasted work. - uint[] sarr = new uint[singles.Count]; - singles.CopyTo(sarr); - for (int i = 0; i < sarr.Length; i++) - { - if (Covered(rs, re, n, sarr[i]) && singles.Remove(sarr[i])) r.SinglesRemoved++; - } - - string[] carr = new string[cidrs.Count]; - cidrs.CopyTo(carr); - cidrs.Clear(); - - for (int i = 0; i < carr.Length; i++) - { - string c = carr[i]; - ulong lo, hi; - - // Unparseable entries are kept verbatim rather than dropped: this pass exists to subtract, and - // silently discarding something it could not read would weaken the list. - if (!TryCidrRange(c, out lo, out hi)) { cidrs.Add(c); continue; } - if (hi > 0xFFFFFFFFUL) hi = 0xFFFFFFFFUL; - - int idx = FirstEndAtLeast(re, n, (uint)lo); - if (idx >= n || (ulong)rs[idx] > hi) { cidrs.Add(c); continue; } // no overlap: the common case - - ulong cursor = lo; - int before = r.EntriesAdded; - for (int j = idx; j < n && (ulong)rs[j] <= hi; j++) - { - if ((ulong)rs[j] > cursor) Emit(cursor, (ulong)rs[j] - 1UL, singles, cidrs, r); - ulong next = (ulong)re[j] + 1UL; - if (next > cursor) cursor = next; - if (cursor > hi) break; - } - if (cursor <= hi) Emit(cursor, hi, singles, cidrs, r); - - if (r.EntriesAdded == before) r.CidrsDropped++; else r.CidrsSplit++; - } - - return r; - } - - static string FormatIp(uint ip) - { - char[] buf = new char[16]; - int p = 0; - p = WriteOctet(buf, p, (ip >> 24) & 255); buf[p++] = '.'; - p = WriteOctet(buf, p, (ip >> 16) & 255); buf[p++] = '.'; - p = WriteOctet(buf, p, (ip >> 8) & 255); buf[p++] = '.'; - p = WriteOctet(buf, p, ip & 255); - return new string(buf, 0, p); - } - - // Collapses a prefix list into the minimal equivalent set, in ascending order. Used by - // -RefreshCarveouts: routing data publishes thousands of overlapping announcements. - public static string[] CollapsePrefixes(string content) - { - uint[] noBogon = new uint[0]; - var singles = new HashSet(); - var cidrs = new HashSet(); - AddContent(content, singles, cidrs, noBogon, noBogon); - - var ranges = BuildRanges(singles, cidrs); - var result = new List(); - - for (int i = 0; i < ranges.Count; i++) - { - ulong lo = ranges.Start[i], hi = ranges.End[i]; - - while (lo <= hi) - { - int bits = 32; - while (bits > 0) - { - ulong size = 1UL << (32 - (bits - 1)); - if ((lo % size) != 0UL) break; - if (lo + size - 1UL > hi) break; - bits--; - } - - result.Add(bits == 32 ? FormatIp((uint)lo) : FormatCidr((uint)lo, bits)); - lo += 1UL << (32 - bits); - } - } - - return result.ToArray(); - } - - static int WriteOctet(char[] buf, int pos, uint v) - { - if (v >= 100) { buf[pos++] = (char)('0' + v / 100); buf[pos++] = (char)('0' + (v / 10) % 10); } - else if (v >= 10) { buf[pos++] = (char)('0' + v / 10); } - buf[pos++] = (char)('0' + v % 10); - return pos; - } - - // Writes the whole blocklist in one streamed pass so we never materialize millions of strings. - // Header first (the shard's reload detector requires it), then singles, then CIDRs. LF line endings. - public static void Write(string path, string header, HashSet singles, HashSet cidrs) - { - using (var w = new StreamWriter(path, false, new UTF8Encoding(false), 1 << 20)) - { - w.Write(header); w.Write('\n'); - - char[] buf = new char[20]; - foreach (uint v in singles) - { - int p = 0; - p = WriteOctet(buf, p, (v >> 24) & 255); buf[p++] = '.'; - p = WriteOctet(buf, p, (v >> 16) & 255); buf[p++] = '.'; - p = WriteOctet(buf, p, (v >> 8) & 255); buf[p++] = '.'; - p = WriteOctet(buf, p, v & 255); buf[p++] = '\n'; - w.Write(buf, 0, p); - } - - foreach (string c in cidrs) { w.Write(c); w.Write('\n'); } - } - } -} -'@ - -# --------------------------------------------------------------------------------------------------------- -# Feed set -- thin, non-overlapping. See .DESCRIPTION for why each is kept and what was dropped as redundant. -# romainmarcoux's "full" set is sharded; only aa..ad carry data today (ae.. are empty placeholders). -# A 404/empty shard is skipped, so extend this list if upstream grows the shard count. -# --------------------------------------------------------------------------------------------------------- -$rmBase = 'https://raw.githubusercontent.com/romainmarcoux/malicious-ip/main/full-300k-' -$rmShards = @('aa','ab','ac','ad') | ForEach-Object { $rmBase + $_ + '.txt' } - -$AllFeeds = @( - [pscustomobject]@{ Name = 'bitwire-it'; Urls = @('https://raw.githubusercontent.com/bitwire-it/ipblocklist/main/inbound.txt') } - [pscustomobject]@{ Name = 'romainmarcoux'; Urls = $rmShards } - [pscustomobject]@{ Name = 'sentinel-turris';Urls = @('https://view.sentinel.turris.cz/greylist-data/greylist-latest.csv') } - [pscustomobject]@{ Name = 'firehol-level1'; Urls = @('https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset') } -) - -# Anonymizer / relay lists subtracted only when -ExcludeAnonymizers is set (Tor exits, open proxies, VPN tunnels). -$AnonFeeds = @( - 'https://raw.githubusercontent.com/borestad/firehol-mirror/refs/heads/main/tor_exits.ipset' - 'https://raw.githubusercontent.com/borestad/firehol-mirror/refs/heads/main/sslproxies_7d.ipset' - 'https://raw.githubusercontent.com/borestad/firehol-mirror/refs/heads/main/socks_proxy_7d.ipset' - 'https://raw.githubusercontent.com/ShadowWhisperer/IPs/master/Lists/Tunnels' -) - -if ($Feeds) { - $AllFeeds = $AllFeeds | Where-Object { $Feeds -contains $_.Name } - if (-not $AllFeeds) { throw "No feeds matched -Feeds." } -} - -# --------------------------------------------------------------------------------------------------------- -# Reserved / bogon ranges -- never valid attacker SOURCE IPs; always filtered. Built once as uint32 arrays. -# --------------------------------------------------------------------------------------------------------- -function ConvertTo-IPv4UInt { - param([string]$s) - $a = $s.Split('.') - if ($a.Length -ne 4) { return $null } - $v = [uint32]0 - foreach ($o in $a) { - $n = 0 - if (-not [int]::TryParse($o, [ref]$n) -or $n -lt 0 -or $n -gt 255) { return $null } - $v = ($v -shl 8) -bor [uint32]$n - } - return $v -} - -$bogonCidrs = '0.0.0.0/8','10.0.0.0/8','100.64.0.0/10','127.0.0.0/8','169.254.0.0/16','172.16.0.0/12', - '192.0.0.0/24','192.0.2.0/24','192.168.0.0/16','198.18.0.0/15','198.51.100.0/24', - '203.0.113.0/24','224.0.0.0/3' # 224/3 covers multicast + reserved + 255.255.255.255 -$bogStart = [System.Collections.Generic.List[uint32]]::new() -$bogEnd = [System.Collections.Generic.List[uint32]]::new() -foreach ($c in $bogonCidrs) { - $p = $c.Split('/'); $base = ConvertTo-IPv4UInt $p[0]; $bits = [int]$p[1] - $size = [uint32]([Math]::Pow(2, 32 - $bits)) - $bogStart.Add($base); $bogEnd.Add([uint32]($base + $size - 1)) -} -$bogStart = $bogStart.ToArray(); $bogEnd = $bogEnd.ToArray() - -# --------------------------------------------------------------------------------------------------------- -# Streaming download with a live progress bar (Write-Progress) so large feeds show real byte progress. -# --------------------------------------------------------------------------------------------------------- -function Get-Url { - param([string]$Url, [string]$Label) - $req = [System.Net.HttpWebRequest]::Create($Url) - $req.UserAgent = $UA; $req.Timeout = 120000; $req.ReadWriteTimeout = 120000 - $resp = $req.GetResponse() - try { - $total = $resp.ContentLength - $stream = $resp.GetResponseStream() - $ms = New-Object System.IO.MemoryStream - $buf = New-Object byte[] (1MB) - $read = 0; $lastReport = 0 - while (($n = $stream.Read($buf, 0, $buf.Length)) -gt 0) { - $ms.Write($buf, 0, $n); $read += $n - if ($read - $lastReport -ge 2MB) { - $lastReport = $read - if ($total -gt 0) { - Write-Progress -Activity ("Downloading {0}" -f $Label) -PercentComplete ([int](100 * $read / $total)) ` - -Status ("{0:N1} / {1:N1} MB" -f ($read / 1MB), ($total / 1MB)) - } else { - Write-Progress -Activity ("Downloading {0}" -f $Label) -Status ("{0:N1} MB" -f ($read / 1MB)) - } - } - } - Write-Progress -Activity ("Downloading {0}" -f $Label) -Completed - return [System.Text.Encoding]::UTF8.GetString($ms.ToArray()) - } - finally { $resp.Close() } -} - -# --------------------------------------------------------------------------------------------------------- -# Refresh carve-out data from routing announcements. Runs here because it needs Get-Url and the compiled -# collapser. A failure leaves the existing file alone rather than truncating a working carve-out. -# --------------------------------------------------------------------------------------------------------- -if ($AddCarveout) { - $path = Join-Path $ConfigDir ("ip-allowlist-{0}.txt" -f $AddCarveout) - $prefixes = Get-CarveoutPrefixes -CarveoutAsn $Asn - - Write-AllowlistFile -Path $path -Lines (@(Get-CarveoutHeader -Name $AddCarveout -CarveoutAsn $Asn) + $prefixes) - Write-Host ("Created {0} carve-out from AS{1}: {2} prefixes -> {3}" -f $AddCarveout, $Asn, @($prefixes).Count, $path) - - if ($AllowPaths -notcontains $path) { $AllowPaths += $path } -} - -if ($RefreshCarveouts) { - foreach ($path in $AllowPaths) { - $carveoutAsn = Get-CarveoutAsn -Path $path - if ($carveoutAsn -le 0) { continue } # hand-written allowlist, not ours to rewrite - - try { - $name = [IO.Path]::GetFileNameWithoutExtension($path) -replace '^ip-allowlist-', '' - $prefixes = Get-CarveoutPrefixes -CarveoutAsn $carveoutAsn - Write-AllowlistFile -Path $path -Lines (@(Get-CarveoutHeader -Name $name -CarveoutAsn $carveoutAsn) + $prefixes) - Write-Host ("Refreshed {0} carve-out from AS{1}: {2} prefixes" -f $name, $carveoutAsn, @($prefixes).Count) - } - catch { - Write-Warning ("AS{0}: refresh failed ({1}) -- keeping the existing carve-out" -f $carveoutAsn, $_.Exception.Message) - } - } -} - -# --------------------------------------------------------------------------------------------------------- -# Collect every kept feed into ONE global set, timing each phase. -# --------------------------------------------------------------------------------------------------------- -$singles = [System.Collections.Generic.HashSet[uint32]]::new() -$cidrs = [System.Collections.Generic.HashSet[string]]::new() -$feedCount = 0 - -foreach ($feed in $AllFeeds) { - $before = $singles.Count + $cidrs.Count - $ok = $false - foreach ($url in $feed.Urls) { - try { - $dlSw = [System.Diagnostics.Stopwatch]::StartNew() - $content = Get-Url -Url $url -Label $feed.Name - $dlSw.Stop() - $mb = [Math]::Round($content.Length / 1MB, 1) - - $pSw = [System.Diagnostics.Stopwatch]::StartNew() - [void][BlocklistExporter]::AddContent($content, $singles, $cidrs, $bogStart, $bogEnd) - $pSw.Stop() - Write-Host (" [dl {0,6:N1}s / parse {1,5:N1}s] {2}" -f $dlSw.Elapsed.TotalSeconds, $pSw.Elapsed.TotalSeconds, ("{0} ({1} MB)" -f $feed.Name, $mb)) - $ok = $true - } - catch { Write-Warning ("{0}: {1} -- skipping shard ({2})" -f $feed.Name, $url, $_.Exception.Message) } - } - if ($ok) { - $feedCount++ - $delta = ($singles.Count + $cidrs.Count) - $before - Write-Host ("{0,-16} +{1,8} new (running total {2} ip / {3} cidr)`n" -f $feed.Name, $delta, $singles.Count, $cidrs.Count) - } - else { Write-Warning ("{0}: all sources failed -- skipping" -f $feed.Name) } -} - -# --------------------------------------------------------------------------------------------------------- -# Subtraction pass: the allowlist file, plus the anonymizer feeds when -ExcludeAnonymizers is set. Both are -# "never block these", so they share one set and one range-correct removal -- which is also the fix for the -# old anonymizer path, where CIDR entries were parsed and then never actually subtracted. -# -# Bogon filtering is deliberately NOT applied here: it exists to keep junk OUT of the blocklist, and running -# it over subtractive input would quietly discard exemptions instead (e.g. a shard exempting its own LAN). -# --------------------------------------------------------------------------------------------------------- -$allowSingles = [System.Collections.Generic.HashSet[uint32]]::new() -$allowCidrs = [System.Collections.Generic.HashSet[string]]::new() -$noBogon = [uint32[]]::new(0) - -foreach ($p in $AllowPaths) { - try { - $allowText = Get-Content -LiteralPath $p -Raw -ErrorAction Stop - if (-not $allowText) { continue } - - $before = $allowSingles.Count + $allowCidrs.Count - [void][BlocklistExporter]::AddContent($allowText, $allowSingles, $allowCidrs, $noBogon, $noBogon) - Write-Host (" allowlist {0,-28} +{1} entr(ies)" -f (Split-Path -Leaf $p), (($allowSingles.Count + $allowCidrs.Count) - $before)) - } - catch { Write-Warning ("Could not read allowlist {0}: {1}" -f $p, $_.Exception.Message) } -} - -if ($ExcludeAnonymizers) { - foreach ($url in $AnonFeeds) { - try { [void][BlocklistExporter]::AddContent((Get-Url -Url $url -Label 'anonymizers'), $allowSingles, $allowCidrs, $noBogon, $noBogon) } - catch { Write-Warning ("anonymizer list {0}: {1}" -f $url, $_.Exception.Message) } - } -} - -$allowCount = $allowSingles.Count + $allowCidrs.Count -if ($allowCount -gt 0) { - $aSw = [System.Diagnostics.Stopwatch]::StartNew() - $ranges = [BlocklistExporter]::BuildRanges($allowSingles, $allowCidrs) - $res = [BlocklistExporter]::ApplyAllowlist($singles, $cidrs, $ranges) - $aSw.Stop() - - Write-Host ("Allowlist: {0} entr(ies) -> {1} ranges; removed {2} IPs, dropped {3} CIDRs, split {4} into {5} ({6:N1}s)" -f ` - $allowCount, $ranges.Count, $res.SinglesRemoved, $res.CidrsDropped, $res.CidrsSplit, $res.EntriesAdded, $aSw.Elapsed.TotalSeconds) -} - -$total = $singles.Count + $cidrs.Count -Write-Host ("Merged {0} feed(s): {1} unique single IPs + {2} unique CIDRs (bogon-filtered) in {3:N1}s." -f ` - $feedCount, $singles.Count, $cidrs.Count, $totalSw.Elapsed.TotalSeconds) - -if ($DryRun) { - Write-Host ("DRY RUN: nothing written (would have written {0} entries to {1})." -f $total, $OutFile) - return -} - -# A partial feed outage must not silently shrink the shard's blocklist to nothing; keep the last good file. -if ($total -eq 0) { throw "No entries parsed -- refusing to overwrite '$OutFile' with an empty list." } - -# --------------------------------------------------------------------------------------------------------- -# Write to a .tmp sibling and swap it into place, so the shard (which reads the whole file on a change) -# never observes a half-written list. One rename does it whether or not a list is already there. -# --------------------------------------------------------------------------------------------------------- -$outDir = Split-Path -Parent $OutFile -if ($outDir -and -not (Test-Path -LiteralPath $outDir -PathType Container)) { - New-Item -ItemType Directory -Path $outDir -Force | Out-Null -} - -# InvariantCulture: ':' is the culture-defined time separator in a custom format string, and the -# header is a machine-read marker the shard compares verbatim. -$generated = [DateTime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ', [Globalization.CultureInfo]::InvariantCulture) -# The shard's header reader is token-based and ignores tokens it does not know, so `allow=` is additive -- -# it is here so an operator can tell from the file alone whether a carve-out was in effect when it was built. -$header = "# modernuo-blocklist generated=$generated count=$total ipv4=$($singles.Count) cidr=$($cidrs.Count) feeds=$feedCount allow=$allowCount" - -$tmp = $OutFile + '.tmp' -$wSw = [System.Diagnostics.Stopwatch]::StartNew() -try { - [BlocklistExporter]::Write($tmp, $header, $singles, $cidrs) - # One atomic rename over the destination on every platform: MoveFileEx REPLACE_EXISTING on - # Windows, rename(2) on Linux and macOS. - [IO.File]::Move($tmp, $OutFile, $true) -} -finally { - # Never leave a partial .tmp next to a live blocklist for the next run to trip over. - if (Test-Path -LiteralPath $tmp -PathType Leaf) { Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue } -} -$wSw.Stop() - -$sizeMb = [Math]::Round((Get-Item -LiteralPath $OutFile).Length / 1MB, 1) -Write-Host ("`nWrote {0} entries ({1} MB) to {2} in {3:N1}s (total {4:N1}s). generated={5}" -f ` - $total, $sizeMb, $OutFile, $wSw.Elapsed.TotalSeconds, $totalSw.Elapsed.TotalSeconds, $generated) -Write-Host "The shard picks this up on its next reloadInterval poll; no restart needed." diff --git a/version.json b/version.json index 30cc19627..3bb3f304b 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "0.15.6" + "version": "0.14.0" }